fixed dll paths for vr

This commit is contained in:
DramaticShape
2026-08-02 12:29:14 -04:00
parent 1fa29a831f
commit 395f51d268
5 changed files with 96 additions and 8 deletions
+10
View File
@@ -4,6 +4,16 @@
### Added
- **VR works from an installed release.** The OpenXR loader used to be
looked for only against the working directory and the game's source --
right for the dev tree, wrong for a release install, where importing
the mod lands it in the game's SAVE DIRECTORY (or keeps it zipped) and
the VR row silently failed to start. The search now also asks the
mount that actually holds the mod and the save directory itself; and
if nothing on disk answers -- the mod imported as an archive -- the
DLL is copied once out of the mod into the save directory and loaded
from there, so every install shape reaches the headset.
- **SELECT walks the VOXEL ladder.** In free roam, the pad's SELECT
button makes exactly the step hotkey 3 makes -- OFF through the angle
rungs to 1ST and round, stepping over FULL, clearing TILT and GBC FX
+21 -1
View File
@@ -44,4 +44,24 @@ alongside.
| right stick left / right | *1ST only* — snap-turn 45° |
| grip squeeze + raise / lower that hand | *diorama only* — drag the table's height |
| head | *1ST and battles* — look; FreeMove walks where you look |
| left hand | *1ST and battles* — the Pokédex: menus, dialogs and the 2D battle screen on its screen |
| left hand | *1ST and battles* — the Pokédex: menus, dialogs and the 2D battle screen on its screen |
## Licenses
This mod redistributes one third-party binary:
- **`assets/vr/openxr_loader.dll`** — the Khronos OpenXR loader
(version 1.0.10.2, x64, unmodified), © The Khronos Group Inc.,
licensed under the **Apache License 2.0**. The full license text ships
alongside the DLL at
[`assets/vr/LICENSE-openxr_loader.txt`](assets/vr/LICENSE-openxr_loader.txt),
as the license requires; keep the two files together if you
redistribute this mod. Source:
[KhronosGroup/OpenXR-SDK](https://github.com/KhronosGroup/OpenXR-SDK).
Everything else in this mod is original to it, except that the voxel
geometry and shape profiles are derived from the tile and sprite data of
the original game, as documented by the
[pret/pokered](https://github.com/pret/pokered) disassembly. No ROM
data, artwork or audio is included; the mod reads the assets the host
game already has.
+53 -6
View File
@@ -361,18 +361,58 @@ local function check(r, what)
end
-- Where the loader DLL might really be on disk. mod.path is
-- PHYSFS-relative; ffi.load needs an OS path, so try it against the
-- process's working directory and against the game's source directory.
-- PHYSFS-relative; ffi.load needs an OS path. The dev tree answers to
-- the working directory or the source directory -- but an INSTALLED
-- release is neither: importing a mod there lands it in the game's SAVE
-- DIRECTORY, so the mount that actually holds the mod
-- (getRealDirectory) and the save directory itself are both tried too.
local DLL_REL = "assets/vr/openxr_loader.dll"
local function loaderCandidates()
local rel = (V.path or "mods/DramaticShapeVoxelMod")
.. "/assets/vr/openxr_loader.dll"
local rel = (V.path or "mods/DramaticShapeVoxelMod") .. "/" .. DLL_REL
local list = { rel }
local okR, real = pcall(function()
return love.filesystem.getRealDirectory(rel)
end)
if okR and type(real) == "string" then
list[#list + 1] = real .. "/" .. rel
end
local ok, src = pcall(function() return love.filesystem.getSource() end)
if ok and type(src) == "string" then list[#list + 1] = src .. "/" .. rel end
local okS, save = pcall(function()
return love.filesystem.getSaveDirectory()
end)
if okS and type(save) == "string" then list[#list + 1] = save .. "/" .. rel end
list[#list + 1] = "openxr_loader" -- last resort: the system search path
return list
end
VRXR._loaderCandidates = loaderCandidates -- named for the suite
-- The escape hatch for a mod imported as an ARCHIVE (or mounted anywhere
-- else ffi.load cannot see): copy the DLL out of the mount into the save
-- directory -- a real folder on every platform -- and load it from
-- there. Written once and reused; rewritten only if the size changed (a
-- mod update shipping a new loader).
local EXTRACTED = "dramatic_shape_openxr_loader.dll"
local function extractLoader()
local okB, bytes = pcall(function()
return V.mod and V.mod.read and V.mod:read(DLL_REL)
end)
if not (okB and type(bytes) == "string" and #bytes > 0) then return nil end
local ok, path = pcall(function()
local have = love.filesystem.getInfo and love.filesystem.getInfo(EXTRACTED)
if not (have and have.size == #bytes) then
if not love.filesystem.write(EXTRACTED, bytes) then return nil end
end
local save = love.filesystem.getSaveDirectory()
if type(save) ~= "string" then return nil end
return save .. "/" .. EXTRACTED
end)
return ok and path or nil
end
local function loadLoader()
ffi = require("ffi")
pcall(ffi.cdef, CDEF)
@@ -383,8 +423,15 @@ local function loadLoader()
if ok then return lib end
errs[#errs + 1] = tostring(lib)
end
error("openxr_loader.dll not loadable (is the mod running from a real "
.. "folder, not an archive?): " .. table.concat(errs, " | "), 0)
-- nothing loadable in place: extract a copy to the save directory
local extracted = extractLoader()
if extracted then
local ok, lib = pcall(ffi.load, extracted)
if ok then return lib end
errs[#errs + 1] = tostring(lib)
end
error("openxr_loader.dll not loadable from the mod, the save directory "
.. "or the system: " .. table.concat(errs, " | "), 0)
end
local function makeSwapchain(w, h, format)
+1 -1
View File
@@ -47,7 +47,7 @@ return {
"1ST needs the 3D pass like every rung; without it the level still persists but the world stays 2D and the grid walk stays in charge",
"in 1ST, scripted walks, ledge hops and spinner slides play out as the grid moves they are, with the camera riding along; free control resumes when they land",
"rooms have no ceilings, so a first-person look over an interior wall shows the void the diorama always had behind it",
"VR is Windows x64 only (the shipped loader and the Win32 GL binding): on any other platform -- mobile above all -- the VR row is absent from the OPTIONS menu and the manager's page both, and a stored vr=true carried over in a save is ignored. On Windows it renders the scene once per eye (heavy with WATER FULL or AA up) and cannot start from a mounted .love archive -- the FFI needs the loader DLL on a real disk path; pad/keyboard/mouse keep working alongside the XR controllers",
"VR is Windows x64 only (the shipped loader and the Win32 GL binding): on any other platform -- mobile above all -- the VR row is absent from the OPTIONS menu and the manager's page both, and a stored vr=true carried over in a save is ignored. On Windows it renders the scene once per eye (heavy with WATER FULL or AA up); pad/keyboard/mouse keep working alongside the XR controllers. The loader DLL is found wherever the mod was put -- the dev tree, an installed release's save directory, or an imported archive, from which it is copied once into the save directory so the FFI has a real disk path",
"VR on and off are both the VR row's job (options menu or manager); no controller button does either",
"the Pokedex needs a TRACKED left controller; without one there is no device in hand and the floating panel carries the UI as before",
"while the VR row is ON, 3D-BTL is held ON and BACK SPRITES held OFF (the headset's battle staging assumes both), and both rows leave the OPTIONS menu until VR goes off -- their stored values come back with them",
+11
View File
@@ -3998,6 +3998,17 @@ T.eq(type(VRMod.supported), "function",
"the platform gate exists -- off Windows the row is not offered at all")
T.eq(VRMod.supported(), true,
"and a headless run (no love.system) counts as supported, harmlessly")
-- the loader search covers every install shape: the mod-relative path
-- first, the system name last, and (with a filesystem to ask) the real
-- mount and the save directory in between
local VRXR_ = run.loader.exports.DRAMATIC_SHAPE.lib.require("VRXR")
local cands = VRXR_._loaderCandidates()
T.check(#cands >= 2, "the loader has candidates to try")
T.check(cands[1]:find("assets/vr/openxr_loader%.dll") ~= nil,
"the first is the mod's own path")
T.eq(cands[#cands], "openxr_loader",
"and the system search path is the last resort")
T.eq(type(VRMod.leave), "function",
"VR.leave stays as the programmatic door out -- no controller button "
.. "is wired to it")