Require complete Mac game bundles and clarify iPhone-only splitting

This commit is contained in:
Erick Sanchez committed 2026-10-26 12:58:52 -06:00
1 parent 1b5ecaabb5
commit fd93fdc308
6 files changed
+27 -8

No files matched your search

+2 -2
View File
@@ -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/<title ID>/`.
**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.
+2 -2
View File
@@ -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.
+2 -1
View File
@@ -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.",
+3 -1
View File
@@ -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 {
+3 -2
View File
@@ -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')
+15
View File
@@ -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"}')