From e2f2b432ffac8a19c704267846c21c730cb3bef0 Mon Sep 17 00:00:00 2001 From: bryanthaboi Date: Wed, 22 Jul 2026 15:42:19 -0400 Subject: [PATCH] bazinga moment 2 --- conf.lua | 17 ++++++++--- mobile/android/gradle.properties | 4 ++- mobile/ios/overlays/love-ios.plist | 5 ++++ scripts/build_android.sh | 2 +- scripts/build_ios.sh | 2 +- src/import/CacheFs.lua | 46 +++++++++++++++++++++--------- 6 files changed, 55 insertions(+), 21 deletions(-) diff --git a/conf.lua b/conf.lua index c3e5b849..0016b486 100644 --- a/conf.lua +++ b/conf.lua @@ -37,10 +37,19 @@ function love.conf(t) local osName = love._os local mobile = osName == "Android" or osName == "iOS" if mobile then - -- On Android/iOS, width/height aspect picks portrait vs landscape - -- (fullscreen alone is not enough). Use a tall portrait size; the - -- OS then resizes to the real display. highdpi is required for - -- Retina iOS (Android always behaves as highdpi). + -- resizable is what unlocks orientation. SDL's Android backend, given no + -- SDL_HINT_ORIENTATIONS (LÖVE sets none), calls setRequestedOrientation + -- at window creation -- FULL_SENSOR when the window is resizable (rotates + -- freely to portrait or landscape), otherwise locked to the window's w/h + -- aspect. So a non-resizable tall window forced portrait; resizable lets + -- the game follow the device. The renderer letterboxes the 160x144 + -- viewport into whatever size results, and touch input is gesture-based, + -- so both orientations just work. iOS follows the Info.plist orientations + -- (see mobile/ios/overlays/love-ios.plist, now portrait + landscape). + t.window.resizable = true + -- Starting size is a tall portrait hint; the OS resizes to the real + -- display and rotations resize again. highdpi is required for Retina iOS + -- (Android always behaves as highdpi). t.window.width = 1080 t.window.height = 1920 t.window.fullscreen = true diff --git a/mobile/android/gradle.properties b/mobile/android/gradle.properties index 61db6e16..a3c7a5f7 100644 --- a/mobile/android/gradle.properties +++ b/mobile/android/gradle.properties @@ -6,7 +6,9 @@ #app.name=LÖVE for Android app.application_id=com.theboisclub.pokemonred -app.orientation=portrait +# fullUser: allow every orientation the player's device permits (portrait and +# landscape), honouring their auto-rotate lock. Was "portrait" (locked). +app.orientation=fullUser app.version_code=32 app.version_name=11.5a diff --git a/mobile/ios/overlays/love-ios.plist b/mobile/ios/overlays/love-ios.plist index 98fc630f..5305530a 100644 --- a/mobile/ios/overlays/love-ios.plist +++ b/mobile/ios/overlays/love-ios.plist @@ -62,10 +62,15 @@ UISupportedInterfaceOrientations UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight UTExportedTypeDeclarations diff --git a/scripts/build_android.sh b/scripts/build_android.sh index b9ee09d6..cfd7e0e4 100755 --- a/scripts/build_android.sh +++ b/scripts/build_android.sh @@ -101,7 +101,7 @@ def set_prop(text, key, value): text = re.sub(r"(?m)^app\.name_byte_array=.*\n?", "", text) text = set_prop(text, "app.name", name) text = set_prop(text, "app.application_id", app_id) -text = set_prop(text, "app.orientation", "portrait") +text = set_prop(text, "app.orientation", "fullUser") if version: text = set_prop(text, "app.version_name", version) text = set_prop(text, "app.version_code", version_code) diff --git a/scripts/build_ios.sh b/scripts/build_ios.sh index b58cca72..c8a1c046 100755 --- a/scripts/build_ios.sh +++ b/scripts/build_ios.sh @@ -155,7 +155,7 @@ require_ios_libraries apply_ios_branding() { [ -f "$OVERLAY_PLIST" ] || fail "missing overlay plist: $OVERLAY_PLIST" local dest="$XCODE_DIR/ios/love-ios.plist" - say "applying iOS branding (portrait-only Info.plist, display name)" + say "applying iOS branding (portrait + landscape Info.plist, display name)" cp "$OVERLAY_PLIST" "$dest" } diff --git a/src/import/CacheFs.lua b/src/import/CacheFs.lua index a4081fe4..533c5a06 100644 --- a/src/import/CacheFs.lua +++ b/src/import/CacheFs.lua @@ -63,26 +63,44 @@ end -- Mount an external directory onto the physfs read path (appended, so the -- game's own source always wins a name clash). Returns true on success. --- Guarded and lazily bound like the mkdir helper; PHYSFS_mount is exported --- from love's framework, so ffi.C resolves it in the running process. +-- +-- PHYSFS_mount is exported by love's own binary. How ffi finds it differs +-- per platform: on macOS/Linux the symbol is in the default namespace, so +-- ffi.C resolves it; on Windows it lives in love.dll, which ffi.C does NOT +-- search, so love.dll is loaded explicitly with ffi.load("love"). Try the +-- default first, then love. local physfsMountFn = nil -local function mountReadable(dir) - if physfsMountFn == nil then - physfsMountFn = false - local ok, ffi = pcall(require, "ffi") - if ok then - pcall(ffi.cdef, - "int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath);") - if pcall(function() return ffi.C.PHYSFS_mount end) then +local function resolveMount() + if physfsMountFn ~= nil then return physfsMountFn end + physfsMountFn = false + local ok, ffi = pcall(require, "ffi") + if not ok then return physfsMountFn end + pcall(ffi.cdef, + "int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath);") + local libs = { + function() return ffi.C end, + function() return ffi.load("love") end, + } + for _, getlib in ipairs(libs) do + local okl, lib = pcall(getlib) + if okl and lib then + local oks, fn = pcall(function() return lib.PHYSFS_mount end) + if oks and fn then physfsMountFn = function(d) - local okc, ret = pcall(ffi.C.PHYSFS_mount, d, "", 1) - return okc and ret ~= 0 + local okr, ret = pcall(fn, d, "", 1) + return okr and ret ~= 0 end + break end end end - if not physfsMountFn then return false end - return physfsMountFn(dir) + return physfsMountFn +end + +local function mountReadable(dir) + local fn = resolveMount() + if not fn then return false end + return fn(dir) end -- The portable game folder when the cache should live there, else nil.