From fd93fdc3088d8b320e453a2f63f9d058aaeaa238 Mon Sep 17 00:00:00 2001 From: Erick Sanchez <7268597+nalancer08@users.noreply.github.com> Date: Mon, 26 Oct 2026 12:58:52 -0600 Subject: [PATCH] Require complete Mac game bundles and clarify iPhone-only splitting --- README-es.md | 4 ++-- README.md | 4 ++-- desktop/NxNativeEasy/Localization.swift | 3 ++- desktop/NxNativeEasy/main.swift | 4 +++- scripts/build_mac.py | 5 +++-- scripts/tests/test_mac_pipeline.py | 15 +++++++++++++++ 6 files changed, 27 insertions(+), 8 deletions(-) diff --git a/README-es.md b/README-es.md index 2ea0dac..1c9ddc1 100644 --- a/README-es.md +++ b/README-es.md @@ -26,7 +26,7 @@ Cuando abra la app: **Las siguientes veces:** abre **nxnative Easy.app** en la carpeta **Aplicaciones de tu usuario**. La preparación conserva su propia copia del código; puedes borrar el ZIP y la carpeta que descargaste. -**Mac:** crea una `.app` del juego; pulsa **Abrir juego**. No necesitas Sideloadly ni un iPhone. Las partidas quedan en `~/Library/Application Support/nxnative//`. +**Mac:** crea una sola `.app` completa, con todos los datos del juego dentro; pulsa **Abrir juego**. No necesitas copiar una carpeta RomFS aparte. No necesitas Sideloadly ni un iPhone. Las partidas quedan en `~/Library/Application Support/nxnative/<title ID>/`. **iPhone / iPad:** crea una IPA ligera sin firmar. Instálala con Sideloadly y copia los datos del juego como te indica la app. @@ -40,7 +40,7 @@ Partimos de [seedds/nxnative](https://github.com/seedds/nxnative): el motor, los - **App para Mac:** elegir juego, avance, cancelación y acceso a IPA, datos y Sideloadly. - **Controles editables en iPhone:** posición, tamaño y opacidad, guardados por juego y layout. -- **IPA ligera automática:** los datos `romfs` van aparte; se conservan salidas y cachés válidas. +- **IPA ligera sólo para iPhone/iPad:** los datos `romfs` van aparte; se conservan salidas y cachés válidas. - **Llaves del usuario:** debes seleccionar la carpeta de tus propias `prod.keys`. No incluimos, descargamos ni distribuimos llaves o juegos. - **Preparación local:** sin cuenta Apple, sin certificados de empresa y sin servicio que reciba tus archivos. diff --git a/README.md b/README.md index 4082723..3f35ec1 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ When the app opens: **After setup:** open **nxnative Easy.app** in your home **Applications** folder. The setup keeps its source checkout separately, so you can delete the downloaded ZIP and folder. -**Mac:** creates a game `.app`; click **Open game**. No Sideloadly or iPhone needed. Saves stay in `~/Library/Application Support/nxnative/<title ID>/`. +**Mac:** creates one complete game `.app`, with its game data included inside; click **Open game**. No separate RomFS folder to copy. No Sideloadly or iPhone needed. Saves stay in `~/Library/Application Support/nxnative/<title ID>/`. **iPhone / iPad:** creates an unsigned lightweight IPA; install it with Sideloadly, then copy the game data as shown in the app. @@ -40,7 +40,7 @@ Built on [seedds/nxnative](https://github.com/seedds/nxnative). The original pro - **Native Mac app:** game-folder selection, progress, cancellation, and buttons for the IPA, RomFS data and Sideloadly. - **Editable iPhone touch controls:** position, size and opacity, saved per game and 2D/3D layout. -- **Automatic lightweight IPA export:** RomFS data live separately; existing outputs and valid caches are preserved. +- **Lightweight IPA export for iPhone/iPad only:** RomFS data live separately; existing outputs and valid caches are preserved. - **Your own keys:** explicitly select the external folder containing your `prod.keys`. No games or keys are included, downloaded or distributed. - **Local preparation:** no Apple account or company certificate is used to build the desktop app. diff --git a/desktop/NxNativeEasy/Localization.swift b/desktop/NxNativeEasy/Localization.swift index 977ebcf..9d89c4c 100644 --- a/desktop/NxNativeEasy/Localization.swift +++ b/desktop/NxNativeEasy/Localization.swift @@ -15,7 +15,8 @@ struct EasyStrings { "¿Dónde quieres jugar?": "Where do you want to play?", "Elige un dispositivo": "Choose a device", "Elige dónde quieres jugar y añade tus archivos.": "Choose where to play, then add your own files.", - "Creamos una app para jugar en tu Mac.": "We create an app to play on your Mac.", + "Creamos una app completa para jugar en tu Mac.": "We create a complete app to play on your Mac.", + "Todo el juego va dentro de la app. No necesitas copiar carpetas.": "The entire game is inside the app. No folders to copy.", "Terminó, pero no encontramos la app.": "Finished, but the app could not be found.", "Tu juego está listo para Mac.": "Your game is ready for Mac.", "Listo. Juega en tu Mac.": "Ready. Play on your Mac.", diff --git a/desktop/NxNativeEasy/main.swift b/desktop/NxNativeEasy/main.swift index d7bae35..68c88c6 100644 --- a/desktop/NxNativeEasy/main.swift +++ b/desktop/NxNativeEasy/main.swift @@ -274,7 +274,7 @@ struct ContentView: View { } VStack(alignment:.leading,spacing:6) { Text(model.text("Prepara tu juego.")).font(.system(size:32,weight:.bold,design:.rounded)).foregroundStyle(ink) - Text(model.text(model.isMac ? "Creamos una app para jugar en tu Mac." : "Elige dónde quieres jugar y añade tus archivos.")) + Text(model.text(model.isMac ? "Creamos una app completa para jugar en tu Mac." : "Elige dónde quieres jugar y añade tus archivos.")) .font(.system(size:14)).foregroundStyle(.secondary) } VStack(alignment:.leading,spacing:10) { @@ -352,6 +352,8 @@ struct ContentView: View { .buttonStyle(.borderedProminent).tint(lime).foregroundStyle(.black) Button(model.text("Mostrar app")){model.revealIPA()} } + Text(model.text("Todo el juego va dentro de la app. No necesitas copiar carpetas.")) + .font(.system(size:12)).foregroundStyle(.secondary) Text(model.text("Tus partidas se guardan en este Mac y se conservan al crear otra versión.")) .font(.system(size:12)).foregroundStyle(.secondary) } else { diff --git a/scripts/build_mac.py b/scripts/build_mac.py index 9d7e8c8..a1e773d 100644 --- a/scripts/build_mac.py +++ b/scripts/build_mac.py @@ -36,8 +36,9 @@ def main(argv=None): subprocess.run([sys.executable,'-m','pip','install','nsz'],check=True) code=load_builder().cli(command+['-k',str(keys)]) if code: return code - if not (output/'Contents/MacOS/dmhost').is_file(): - raise ValueError('Build finished without a Mac game app.') + romfs=output/'Contents/Resources/romfs' + if not (output/'Contents/MacOS/dmhost').is_file() or not romfs.is_dir() or romfs.is_symlink(): + raise ValueError('Build finished without a complete Mac game app (embedded game data required).') record={'schema':1,'app_path':str(output)} state=game/'nxnative-mac.json' temporary=state.with_suffix('.json.tmp') diff --git a/scripts/tests/test_mac_pipeline.py b/scripts/tests/test_mac_pipeline.py index 32458b0..277dfd7 100644 --- a/scripts/tests/test_mac_pipeline.py +++ b/scripts/tests/test_mac_pipeline.py @@ -20,6 +20,7 @@ class MacPipeline(unittest.TestCase): calls.append(args) host=Path(args[args.index('-o')+1])/'Contents/MacOS/dmhost' host.parent.mkdir(parents=True);host.write_bytes(b'synthetic') + (host.parents[1]/'Resources/romfs').mkdir(parents=True) return 0 with patch.object(build_mac,'load_builder',return_value=Builder()),patch.object(build_mac.platform,'machine',return_value='arm64'): self.assertEqual(build_mac.main([temp,'--keys-folder',temp]),0) @@ -37,3 +38,17 @@ class MacPipeline(unittest.TestCase): with patch.object(build_mac,'load_builder',return_value=Builder()),patch.object(build_mac.platform,'machine',return_value='arm64'): self.assertEqual(build_mac.main([temp,'--keys-folder',temp]),1) self.assertEqual((root/'nxnative-mac.json').read_text(),'{"app_path":"old"}') + + def test_incomplete_app_does_not_replace_previous_record(self): + with tempfile.TemporaryDirectory() as temp: + root=Path(temp);(root/'base.nsp').write_bytes(b'synthetic');(root/'prod.keys').write_bytes(b'synthetic') + (root/'nxnative-mac.json').write_text('{"app_path":"old"}') + class Builder: + def cli(self,args): + host=Path(args[args.index('-o')+1])/'Contents/MacOS/dmhost' + host.parent.mkdir(parents=True);host.write_bytes(b'synthetic') + return 0 + with patch.object(build_mac,'load_builder',return_value=Builder()),patch.object(build_mac.platform,'machine',return_value='arm64'): + with self.assertRaisesRegex(ValueError,'embedded game data required'): + build_mac.main([temp,'--keys-folder',temp]) + self.assertEqual((root/'nxnative-mac.json').read_text(),'{"app_path":"old"}')