Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -472,13 +472,13 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Android")
# deps don't bloat the SDL-bound game library.
list(FILTER SSB64_SRC_PORT EXCLUDE REGEX "port/android_torch_bridge\\.cpp$")
# Drop features we explicitly disable on Android (no curl-driven
# self-updater, no discord-rpc link, no libretro shader downloader,
# no hires-pack runtime). The corresponding menu entries are gated
# with #if !defined(__ANDROID__) in port/gui/PortMenu.cpp so nothing
# references the removed symbols.
# self-updater, no discord-rpc link, no libretro shader downloader).
# The corresponding menu entries are gated with #if !defined(__ANDROID__)
# in port/gui/PortMenu.cpp so nothing references the removed symbols.
# (port/hires/ is NOT dropped here — it compiles on Android but runs
# opt-in with a reduced LRU budget; see the PORT_HIRES_ENABLED block.)
list(FILTER SSB64_SRC_PORT EXCLUDE REGEX
"port/enhancements/(Updater|DiscordRichPresence|ShaderDownloader)\\.cpp$")
list(FILTER SSB64_SRC_PORT EXCLUDE REGEX "port/hires/")
list(FILTER SSB64_SRC_PORT EXCLUDE REGEX "port/port_window_icon\\.cpp$")
endif()
# Treat the generated icon header as a source so CMake schedules it ahead
Expand Down Expand Up @@ -621,17 +621,19 @@ if(SSB64_STAGE_CYCLE_DEMO)
list(APPEND SSB64_COMPILE_DEFS PORT_STAGE_CYCLE_DEMO=1)
endif()

# Hi-res texture pack support is US-only on desktop and dropped entirely
# on Android (mobile memory budget can't carry the 512 MB LRU cache,
# and the dump-tooling menu items add no value on touch UI).
# Hi-res texture pack support is US-only (desktop AND Android). On Android
# it compiles but defaults to OFF and runs with a smaller decoded-RGBA8 LRU
# budget (see port/hires/HiResPack.h: kHiResEnabledDefault / kDefaultLruBudgetMB)
# plus a per-texture upscale cap, so a pack can't blow the mobile memory
# budget and trip the low-memory killer; the dev dump-tooling menu items are
# still gated off touch UI in PortMenu.cpp.
# Pack PNGs are addressed by CRC32-IEEE of the decoded RGBA8 buffer that
# came out of the US ROM fast-path; JP rendering hits different hash inputs
# (different glyph art for VS-records, JP-only menus, plus palette/sprite
# nuances on shared assets), so a US-built pack would silently miss on JP
# and no JP-only pack has been authored. Drop port/hires/ from the JP and
# Android builds entirely and gate the port.cpp / PortMenu.cpp integration
# on PORT_HIRES_ENABLED.
if(SSB64_VERSION STREQUAL "us" AND NOT CMAKE_SYSTEM_NAME STREQUAL "Android")
# and no JP-only pack has been authored. Drop port/hires/ from the JP build
# entirely and gate the port.cpp / PortMenu.cpp integration on PORT_HIRES_ENABLED.
if(SSB64_VERSION STREQUAL "us")
list(APPEND SSB64_COMPILE_DEFS PORT_HIRES_ENABLED=1)
else()
list(FILTER SSB64_SRC_PORT EXCLUDE REGEX "port/hires/")
Expand Down
9 changes: 8 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,14 @@
android:exported="true"
android:resizeableActivity="true"
android:maxAspectRatio="2.4"
android:screenOrientation="landscape">
android:screenOrientation="landscape"
android:configChanges="layoutDirection|locale|orientation|density|fontScale|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation">
<!-- configChanges: BootActivity runs a multi-second background
import/extract. Without this, a config change in that window
(multi-window resize, dark mode, font scale) recreates the
Activity, and the worker thread posts its result to the dead
instance — leaving the app stuck on the picker. The plain UI
needs no recreation to handle these, so we absorb them. -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand Down
110 changes: 110 additions & 0 deletions android/app/src/main/java/com/jrickey/battleship/BootActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.activity.ComponentActivity;
import androidx.activity.result.ActivityResultLauncher;
Expand Down Expand Up @@ -48,6 +49,13 @@ public class BootActivity extends ComponentActivity {
new ActivityResultContracts.OpenDocument(),
this::onRomPicked);

/** Result handler for the hi-res-pack SAF picker (import-pack flow).
* Registered eagerly so it survives Activity recreation mid-pick. */
private final ActivityResultLauncher<String[]> mPickPack
= registerForActivityResult(
new ActivityResultContracts.OpenDocument(),
this::onPackPicked);

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Expand Down Expand Up @@ -80,6 +88,34 @@ protected void onCreate(Bundle savedInstanceState) {
*/
private static final String EXTRA_REPICK = "ssb64.repick";

/**
* If true on the launching Intent, BootActivity shows a SAF picker for a
* hi-res texture pack (.zip) after assets are ready, copies it into the
* app-owned mods/ folder ({@link PackImporter}), then continues to the
* normal route. Surfaced via the "Import texture pack" launcher shortcut
* (res/xml/shortcuts.xml); also drivable for dev/testing:
*
* adb shell am start -n com.jrickey.battleship.debug/.BootActivity \
* --ez ssb64.import_pack true
*
* Import runs BEFORE the SDL game starts, so HiResPack's scan sees the new
* pack on a single thread at boot — no rescan race against the renderer.
*/
private static final String EXTRA_IMPORT_PACK = "ssb64.import_pack";

private boolean isImportPackRequest() {
return getIntent() != null
&& getIntent().getBooleanExtra(EXTRA_IMPORT_PACK, false);
}

/**
* Dev/test: import a pack from an absolute path the app can read, skipping
* the SAF picker. Mirrors {@link #EXTRA_DEV_ROM}.
* adb shell am start -n com.jrickey.battleship.debug/.BootActivity \
* --es ssb64.dev_pack /sdcard/Download/pack.zip
*/
private static final String EXTRA_DEV_PACK = "ssb64.dev_pack";

private void buildUi() {
mStatus = new TextView(this);
mStatus.setGravity(Gravity.CENTER);
Expand Down Expand Up @@ -129,6 +165,19 @@ private void startAssetExtraction() {
showError("Asset extraction failed:\n\n" + err);
return;
}
// Debug-only: BootActivity is the exported launcher entry, so a
// release build must not let another app drive an arbitrary
// absolute-path import through this extra (confused deputy).
String devPack = (BuildConfig.DEBUG && getIntent() != null)
? getIntent().getStringExtra(EXTRA_DEV_PACK) : null;
if (devPack != null && !devPack.isEmpty()) {
importDevPack(new File(devPack));
return;
}
if (isImportPackRequest()) {
showPackPicker();
return;
}
routeAfterAssets();
});
}, "ssb64-boot-extract").start();
Expand Down Expand Up @@ -248,6 +297,67 @@ private void onRomPicked(Uri romUri) {
}, "ssb64-rom-stage").start();
}

/* ===================================================================== */
/* Optional: hi-res texture pack import (ssb64.import_pack) */
/* ===================================================================== */

private void showPackPicker() {
mStatus.setText(
"Import a hi-res texture pack.\n\n" +
"Pick the pack's .zip file. It's copied into the app's mods folder " +
"and applied the next time the game loads textures."
);
mPickButton.setText("Choose texture pack (.zip)");
mPickButton.setOnClickListener(v -> launchPackPicker());
mPickButton.setVisibility(View.VISIBLE);
}

private void launchPackPicker() {
// Bias toward zips but fall back to "*/*" (some providers report a
// pack as octet-stream); PackImporter validates by actually opening it.
mPickPack.launch(new String[] { "application/zip", "*/*" });
}

private void importDevPack(File packFile) {
mPickButton.setVisibility(View.GONE);
mStatus.setText("Importing texture pack (dev)…");
new Thread(() -> {
File pack = PackImporter.importPackFromFile(getApplicationContext(), packFile);
runOnUi(() -> {
Toast.makeText(BootActivity.this,
pack != null
? "Texture pack imported (dev)."
: "Dev pack import failed — see logcat tag ssb64.pack.",
Toast.LENGTH_LONG).show();
routeAfterAssets();
});
}, "ssb64-pack-import-dev").start();
}

private void onPackPicked(Uri packUri) {
if (packUri == null) {
// Cancelled — go on to the game/ROM flow without a new pack.
routeAfterAssets();
return;
}
mPickButton.setVisibility(View.GONE);
mStatus.setText("Importing texture pack…");

new Thread(() -> {
File pack = PackImporter.importPack(getApplicationContext(), packUri);
runOnUi(() -> {
// Toast survives the hand-off to the game Activity, so the
// result is visible even though we route on immediately.
Toast.makeText(BootActivity.this,
pack != null
? "Texture pack imported."
: "Couldn't import that file — is it a valid .zip pack?",
Toast.LENGTH_LONG).show();
routeAfterAssets();
});
}, "ssb64-pack-import").start();
}

/* ===================================================================== */
/* Stage 3: hand off to the SDL game Activity */
/* ===================================================================== */
Expand Down
Loading
Loading