Files

718 lines
48 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Interface language
The desktop app defaults to English. Its English/Español selector updates labels,
status messages and app-owned folder-picker prompts immediately, and persists the
choice in local UserDefaults (`interfaceLanguage`). Invalid saved values fall back
to English. Game language, the in-game touch editor and raw build/tool/OS diagnostics
are independent and are not translated by this selector. Gameplay compatibility is
reported by upstream game notes, not detected from a build or device by this app.
# Desktop interface
For daily use, open `~/Applications/nxnative Easy.app`, choose iPhone/iPad or Mac,
select your own game and key folders, and press Prepare game. Cards use existing
repository screenshots. The interface shows progress, cancellation and the output
steps appropriate to the selected target.
On another Mac, run the root `START-HERE.command` or the README's single-command
installer. It keeps an independent checkout and prepared tools in Application
Support, builds the launcher locally with ad-hoc signing and opens it. Full Xcode
must already be installed and opened once. No Apple account is used for these
builds. Source ZIPs can be deleted after setup; games and keys remain external.
Cancellation signals the entire build process group.
# Personal iOS builds
This branch retains nxnative's MIT license and attribution. It starts from
upstream `ecbeae164c0b419d1ba1031248fe1402050a3eb0`, includes the shared iOS touch
editor and the tested #277 extraction-space fix. Release 1.5 integrates upstream
main through the SHA in [upstream-sync.json](upstream-sync.json); the previous
working game apps/caches remain untouched. The new chain has a separate validated
Dread shader build. See [the integration review](personal-pr-review.md) and the
[standing agent procedure](agents/07-upstream-maintenance.md).
## One command, including lightweight export
Double-click `scripts/Build-iPhone.command` in Finder, choose the folder containing
one game's base NSP/XCI and updates, and select the external folder containing your own `prod.keys` when asked.
Alternatively, from this checkout:
```sh
bash scripts/prepare_ios.sh "/Volumes/Games/MyGame"
```
Full Xcode and Homebrew must be installed first. The script checks iOS SDK 26+,
installs missing command-line dependencies, and keeps its Python environment next
to the game. It builds using THIS checkout, including the editor and any local
changes. It never clones an older revision over it, resets it, or pulls upstream.
For subsequent games, run the same command with a different external game folder.
It supports the upstream builder's known games; unsupported titles may require
bring-up even when a provisional IPA can be generated.
The default result is an unsigned full IPA plus a separate output folder with a
lightweight unsigned IPA, `romfs/`, and installation instructions. Each run uses
new names; previous IPAs, game packages, extraction and shader caches are retained.
Lightweight ZIP validation rejects traversal, duplicate/colliding paths, symlinks,
keys and unsupported compression. All exported data are streamed with CRC checks.
Large uncompressed app resources may still exceed the non-ZIP64 format limit;
the script stops rather than silently emit a Sideloadly-incompatible archive.
Per-game `nxnative-ios.json` records the source revision, app identity, language,
version and outputs; it contains no key values. Later runs retain its bundle ID.
The default language is `es-419`; the upstream builder handles supported-language
selection. The default pipeline archive target is `none`, as in the working Dread
build; choose a known compatible target explicitly with `--gpu` when desired.
Game versions are selected by upstream (newest present unless `--package` is set).
An update may change RomFS, so only reuse phone data when that data are identical.
## Developer CLI: rebuild the app using existing caches
The desktop interface always uses the normal pipeline, which validates and reuses
unchanged stages automatically. There is no cache-mode toggle for users to choose.
The app-only option below remains a developer CLI tool, outside the desktop flow.
For editor/app-only changes on an already prepared game:
```sh
bash scripts/prepare_ios.sh "/Volumes/Games/MyGame" --app-only
```
This does not need keys or re-extraction. It requires exactly one existing
`<title>_build` folder in the game folder, valid rewritten modules, extracted
RomFS and iOS shaders in the builder's standard locations. It selects the newest
cached `patched_v*` unless `--package` is supplied. On the first app-only run,
identity is read from the newest root-level unsigned IPA, then persisted. Keep
only the intended app's IPAs in that game's folder. Shader stamp verification in
`build_ipa.sh` remains enabled; stale caches fail explicitly rather than being used.
Changes to a profile/compiler/shader chain require a normal full pipeline run.
App-only and normal runs both re-export RomFS into a new output folder; they do
not delete or synchronize the existing phone's Documents.
## Options and diagnostics
```sh
bash scripts/prepare_ios.sh --setup-only "/Volumes/Games/MyGame"
python3 scripts/build_ios.py "/Volumes/Games/MyGame" --dry-run
python3 scripts/split_ipa.py "/Volumes/Games/MyGame/full-unsigned.ipa"
python3 scripts/review_upstream.py --output /Volumes/Games/new-pr-review
```
The direct Python build invocation needs the prepared venv. For example use
`<game>/nxnative-tools/venv/bin/python` instead of `python3`. Dry-run reads the
folder/configuration and source revision without opening keys or running a build;
the setup shell still checks/installs dependencies before invoking Python.
`--full-only` omits RomFS export. `--keys-folder` (or an explicit `--keys` file path), `--bundle-id`, `--app-name`, `--language`,
`--package` and `--gpu` provide explicit overrides. The wrapper rejects simultaneous builds from this checkout because generated files
and caches are shared. Avoid launching the original upstream builder alongside it.
## Install and preserve saves
The scripts do not sign or install, use Apple accounts, or choose developer teams.
The placeholder `0000000000` is only for unsigned packaging. Sign with Sideloadly
using your personal account. Updating the installed app requires the same personal
Apple account and the same effective bundle ID Sideloadly previously used. Keep
any Sideloadly custom bundle-ID settings. The source IPA identifier alone cannot
prove the identifier installed on the phone. Do not uninstall: this deletes saves
and `Documents/romfs`. First installation needs the exported `romfs` folder copied
whole to app Documents through Finder. Later app-only updates can reuse identical
RomFS already there. Updates to game data require the matching new export.
## Touch editor
Tap **Edit controls** above the game. Select a control from the menu or touch near
its center; drag outside the tools to move it. **Show / hide tools** exposes areas
behind the panel. Adjust **Size** (50–200%) and **Opacity** (0–100%). At 0% its hit
area remains active; editing keeps an invisible control findable. **Save** commits,
**Cancel** reloads saved preferences, **Reset control** and **Reset all** restore
original geometry and need **Save** to confirm. Each game now stores four named
profiles under `DmPadProfiles.v2.<Title ID>` in UserDefaults; every profile has
independent 2D/3D maps and the selected slot survives relaunch. The first profile
migrates both legacy `DmPadControls.v1.*` maps without deleting them. Names can be
changed from the profile menu; **Save as…** copies both maps plus the visible
draft into another slot after confirmation, preserving the source. Switching
profiles releases touches and asks before discarding a dirty draft. Cancel reloads
the current saved profile; reset affects only its current layout after Save.
Profiles are local to the device, not uploaded as game progress. The game keeps running during editing. Controller connection changes
cancel unsaved edits. Actual iPhone multitouch and controller behavior still need
user validation after signing/installing.
## Verify changes before committing
```sh
python3 -m unittest discover -s scripts/tests
python3 -m unittest dm.tools.tests.test_nsp_space
python3 -m unittest discover -s dm/ios/tests
GAME=010093801237C000 python3 dm/ios/tools/compile_check.py
```
Keep game packages, keys, saves, IPAs, signing material and extracted resources
outside this source repository. `.gitignore` guards common output types; it is
not a substitute for reviewing staged files. Original downloaded scripts were
preserved on the Mac. These maintained scripts generalize that preparation and
splitting workflow without the Metroid-only profile/bundle assumptions.
## Accounts and keys
The desktop build runs swiftc and explicitly ad-hoc signs with `codesign --sign -`.
No Apple identity, certificate or development team is selected. The iPhone package
continues to use CODE_SIGNING_ALLOWED=NO; Sideloadly signing is a separate user step.
The GUI remembers only a user-selected keys-folder path in local UserDefaults.
It passes that explicit folder to the builder; keys are not copied, bundled,
downloaded or stored in repository configuration. The CLI always prompts for a
folder when no explicit key option is supplied, even if prod.keys is beside a game.
The game app's key file is never packed into an IPA. Tests use disposable synthetic
bytes, not Nintendo key material.
## Game catalog
The home carousel lists all local game profiles as a support reference, with an explicit reminder that users must supply their own game files. **View all / Ver todos** at the top right of the carousel opens the searchable catalog. The native catalog reads every
16-digit title directory containing `dm/games/<id>/game.tsv` from the current checkout
when opened. It shows the profile name, registered build display versions and title ID,
with a link to the corresponding repository game note. Search ignores case and accents
and matches name, version or title ID; whitespace-separated terms must all match.
No game artwork or network request is needed to populate/search the list. Profile
presence is not interpreted as verified iPhone gameplay. The current checkout has 70
profiles, including regional variants.
## Simplified player flow
The home screen contains the catalog, game-folder selection, explicit keys-folder
selection and one prepare action. Empty-state status copy, technical cache choices,
permanent legal/compatibility footers and the standalone installation-guide link
were removed. There is one progress indicator while running. Only failures expose
raw diagnostics; common missing-tool failures receive direct recovery instructions.
The catalog keeps version search but does not show raw title IDs in every row.
When an existing or newly built IPA is available, the app shows the two remaining
steps: send the prepared file to Sideloadly, then copy RomFS through Finder for a
first installation. Sending a file opens it in the installed Sideloadly application;
it does not click Start, select an account, sign or install on a phone. If Sideloadly
is missing, the action is labelled Get Sideloadly and opens its official website.
The normal build workflow still requires Xcode/Homebrew first and installs other
missing dependencies. No Apple identity is selected by the desktop compiler.
## One-command setup and destination selection
`install.sh` installs missing Homebrew/build tools, prepares an isolated Python
runtime, compiles the desktop app locally and opens it. Full Xcode must already be
installed and opened once; the script checks this before downloading anything.
The install uses an independent source checkout and never resets local changes.
`--check` reports prerequisite status without installations or network access.
Users must explicitly choose iPhone/iPad or Mac before preparing a game. The iOS
route preserves the lightweight IPA workflow and `nxnative-ios.json`. The Mac route
uses the upstream `--platform mac-app` packer with explicit ad-hoc signing, a unique
output path per build and a separate `nxnative-mac.json` output record. It uses the
upstream per-title save location without replacing iOS state. Apple silicon is
required. Successful packaging is not a new claim of gameplay verification.
## Controller readiness and one launcher
`desktop/NxNativeEasy/ControllerMonitor.swift` enumerates `GCController.controllers`
at launch and refreshes on connect, disconnect and foreground notifications. Only
an extended gamepad profile is marked ready. The entire controller panel is displayed only for Mac builds. This is local Mac
input readiness, not a claim of a phone connection or a successful game boot. It does not install
input handlers or change pairing, so it cannot take input away from a game.
Notification tests use Apple's snapshot controllers, including unsupported profiles;
they do not claim testing physical Xbox or PlayStation hardware.
The launcher has one SwiftUI Window scene and activates an existing process with
the same bundle ID rather than keeping a duplicate process. The installer and
local opening command now use `~/Applications/nxnative Easy.app` by default.
## Live controller test
The Mac-only Test controller sheet samples `GCExtendedGamepad` values at 60 Hz
while visible and foreground. It draws actual button/D-pad presses, both stick
positions, thumbstick clicks, menu buttons and analog trigger pressure. The success
label requires input, not a connection alone. PlayStation gamepad profiles use
PlayStation face-button symbols. Disconnecting or changing the selected controller
clears stale input; switching to another app clears the live drawing. Sampling
never replaces input handlers or enables background controller capture.
Snapshot-controller tests check pressure, axes, releases, disconnect and selecting
a second device. They are not physical-hardware or game compatibility validation.
## In-app source updates (1.4+)
`desktop/release.json` is the public release version, monotonically increasing
build number and minimum macOS version. The app checks GitHub's public main commit,
then reads the manifest pinned to that SHA. It checks at launch, every six hours
while open, and from the app menu. Offline automatic checks stay quiet.
The bundled `update_app.py` helper downloads only the fixed repository at that SHA,
rejects unsafe ZIP paths, symlinks, game packages and credential filenames, and
extracts to a new versioned directory under Application Support. It never pulls or
resets the existing checkout. It compiles the app in a staging directory on the
installation volume and verifies its build, version, bundle identifier and ad-hoc
signature before asking the old process to quit. After that process exits, it swaps
in the new bundle, retains a hidden backup and relaunches. A failed swap rolls back;
a failure before verification leaves the running app intact. The helper serializes
updates with a per-installation file lock. It does not sign or install iPhone games.
`--prepare-only` downloads and compiles without closing, replacing or launching an
installed app, for release validation. Pass an external `--cache` directory in tests.
Older apps must run the setup command once to receive the updater itself. Existing
prepared games require a normal rebuild to incorporate newer engine/profile code.
Profile artwork is mapped by title ID in `desktop/NxNativeEasy/game-artwork.json`.
All 70 current profiles use existing repository screenshots, including the shared
13 Sentinels image for its Asia profile; no network image search is performed.
Validation on 2026-10-26 completed an actual in-app update to public commit
`86793e23d67cdb035405996d3fa6d79acc9586a8`: download, staged compilation,
signature verification, backup, replacement and automatic relaunch all passed.
The resulting app reported version 1.4/build 5 and only one launcher process
remained. The manual check then reported it was up to date. The 24 script tests
and four documentation checks passed. No game preparation or gameplay was part
of this updater validation.
## Clean-download installation validation (1.4.1)
On 2026-10-26, a public GitHub main ZIP at `a5ed1e8` was downloaded and
extracted outside the development checkout. Its `START-HERE.command` was run
through Bash with new installation, tools and application directories. Automatic
opening was suppressed only so the installed bundle could be opened explicitly
through the UI. Setup cloned an independent checkout, created a new Python
environment, installed its packages, compiled and signed version 1.4/build 5.
After moving the downloaded folder away, the installed app still opened and
its catalog displayed all 70 profiles; searching Metroid returned its one profile.
That unmodified release version detected the genuinely newer public release
1.4.1/build 6 at `d151d54`. Clicking Update now downloaded, compiled, verified,
replaced and automatically reopened the app. Its version, pinned source revision,
ad-hoc signature and retained previous bundle were verified; one launcher process
remained, and the manual check reported it was up to date. Only the validation
cache location was overridden, keeping test downloads in the separate validation
directory outside the checkout.
The original installer checkout remained clean and unchanged.
A second new installation used the current published raw `install.sh` with
separate source, tools and app directories. All four setup stages completed and
produced version 1.4.1/build 6; opening it through Finder displayed the home
window with its profiles and game/key folder selectors. Launcher rebuilds now
stage and verify before
replacement, keep backups and roll back on replacement failure. Regression tests
cover compile failure, replacement failure and preserving an unrelated app.
All 27 script tests and four documentation checks passed.
This validates clean app installations on an existing Apple silicon Mac, not a
fresh macOS installation or another physical Mac. Xcode 27.1, Homebrew, Metal
tools and system build dependencies were already installed; initial installation
of those system components, browser quarantine prompts, a fresh macOS user
account and game compilation/gameplay were not exercised by this check. No
game packages, keys or saves were used or modified.
The clean-install check above did not start game preparation. A later user report
exposed a Foundation worker-launch failure, fixed and reproduced in 1.4.2; see
[Prepare game fails at os.setsid](reference/packing.md#prepare-game-fails-at-ossetsid).
The regression suite now exercises the actual Foundation launch mechanism.
## Cloud-save investigation and API foundation
The separate [save API prototype](../dm/cloud/README.md) records the current save
layout, transaction/case-alias constraints, portable snapshot envelope, identity
provider requirements and remaining native restore work. It stores immutable
versions with optimistic concurrency and authenticated, expiring/revocable copy
sharing; it does not access game files or modify game saves. No native cloud
controls are exposed before login, export and journaled restore are integrated.
On 2026-10-26, upstream main was checked and still matched our integrated
`40e6baede556acd454169ac75b33d1686063f528`. No open PR or new engine/shader change
was imported for the control-profile/API work. The shared profile storage's native
Foundation tests and unsigned production iOS compile/link passed; actual touch
menus on physical iPhone/iPad still need device validation after sideloading.
The focused profile/geometry/API checks passed (9 tests, including real RSA JWT
validation and loopback HTTP); the script suite passed 50 tests (one optional
JWT test skipped without the API dependency), and Python lint had no new
findings. A final iOS compile check compiled 178 sources with zero failures.
The gameplay regression harness was invoked but found no registered Mac build
for the local Dread folder (its `tools/dmhost` is absent); it did not run a
save/gameplay comparison. Existing game packages and save folders were untouched.
## Upstream Metal front-end update after the profile work
A final remote check found a newly published main commit, `8334fdfb50a9900d8a5fc950d099fde46d85135f`.
It was reviewed and merged in a detached candidate, retaining the fork and all
four-profile/API changes. It changes Mac/iOS Metal front-end optimization to
`-O1` and the coherent shader chain to `d374e783beec`; no profiles were added,
removed or changed. The catalog gate still covers 78 profiles/artwork mappings.
Validation passed: the candidate's 451 Python cases across 63 suites (optional
checks retain their documented skips), the tool suite (301 cases, 16 skipped),
native Metal graphics suite, Dread Mac host build and unsigned production iOS
compile/link with synthetic game modules. A full separate Dread shader rebuild
produced 1,010 successful shaders plus 359 pulling variants for Mac and iOS with
no compile failures, stamped with game inputs `d2ce2b7a20f2`. The existing
`own_compilers.py` flow explicitly reported this Mac's spirv-cross fingerprint
`242d2c694be3ec91`, differing from upstream's tested `d9dc197f061b42a5`; the
locked translator matched and no tool lock or chain was falsified.
All shader/build outputs are in the external `upstream-o1-validation-20261026`
folder. The gameplay regression harness was invoked again but could not discover
a registered Dread Mac build (`tools/dmhost` absent); no real gameplay/image or
physical iPhone/iPad comparison was performed. Existing installed games, working
caches, keys and saves were not changed. Preparing a game again builds against
the new chain and includes the control profiles; sideload over the same bundle
identity rather than deleting the game to retain its local data.
## Live preparation logs and game updates
Version 1.7/build 11 detects prior target-specific preparation records and explains
Mac versus iPhone update handling. Mac output remains a fresh complete app;
its previous identity and Title ID are checked before recording success, and
external saves are untouched. iPhone retains its existing identity checks;
content updates require the new RomFS, preserving the app and both save roots.
The shared worker retains cancellable process groups, persists console output,
streams bounded changed compiler-log tails and leaves original logs complete.
The native parser handles split UTF-8/JSON records, partial final lines and
unknown counts. The UI exposes live logs, compiler-log folders and elapsed time;
it never invents a percentage or interprets silence as a dead process.
The separate [REA flow](game-analysis.md) exports original NSO to ELF and offers
optional local analysis. Synthetic converter tests exercise the actual NSO/ELF
tool, source integrity, destination isolation, tool pins and preserved Evidence
on provider failure. Published REA 6.8.0 was downloaded with isolated Node
24.22.1 and provider discovery ran successfully; no native decompiler provider
was configured on the validation Mac, so full Ghidra/Hopper/IDA analysis is not
claimed. No real game module, game installation, key or save was changed.
Validation for this release: 58 script cases passed (one optional API-identity
test skipped without its separate dependency); focused native progress/log/
language cases passed, including byte-split UTF-8, real counts, final log replay,
private retained logs, bounded tails and Foundation cancellation. The production
SwiftUI bundle compiled and its ad-hoc signature verified. The game catalog
remains 78 profiles and documentation checks passed. Python lint retained 414
baseline findings and introduced none. `scripts/regress.py` was invoked, but no
registered Dread Mac build was discoverable (`tools/dmhost` absent); no gameplay
comparison or physical Mac/iPhone update was performed. The shader chain and
runtime were not changed by this release.
## Native installation and save-location guidance (1.8)
The [native sideloading implementation](sideloading.md) uses an ARM64 helper with
isideload/idevice and an exact-identifier patch. REA read-only inspection of the
installed Sideloadly 0.71.1 found an x86_64-only executable; it did not recover
source. No Sideloadly proprietary resources are distributed. Helper/build source,
checksums, licenses, Keychain/stdio policy and runtime validation limits are
recorded in the linked guide. Existing game outputs remain unsigned and intact.
The player-facing [save guide](saves.md) and [Spanish guide](saves-es.md) distinguish
Mac Application Support, iOS Documents, account/device saves, transient journals,
RomFS, app preferences and the limits of manual copies. A Your saves sheet links
those guides and can reveal the default Mac folder by Title ID. Cloud sync is
still inactive. No real Apple login, game signing or device installation was
performed during development; physical validation is required before promoting
the integrated installer over the retained Sideloadly route.
Validation: all 10 Rust unit tests passed, including exact app/extension identity,
restricted RomFS access and fail-closed idle detection. All 62 script tests passed
with one optional API dependency skip; the Swift client tests use a simulated
helper, not Apple services. The ARM64 production bundle compiled and verified
with an ad-hoc signature; its helper links only system libraries. Catalog and
documentation checks passed, with no new Python lint findings. The gameplay
harness could not discover a registered Mac Dread build (`tools/dmhost` absent),
so no gameplay comparison is claimed. Runtime and shader chain are unchanged.
Real account authentication, signing, installation, AFC recovery and scheduled
renewal still require physical-device validation.
## Device/game installation library (1.8.1)
The main window now opens My iPhones and iPads independently of preparation.
Recorded installations expose actual expiry, per-game schedule controls, manual
renewal, Keychain session reuse and metadata-only removal. Manual renewal retains
the original source hash and identity, does not recopy RomFS or enable a paused
schedule, and refuses a different account. Both player READMEs describe the native
flow first and retain the Sideloadly fallback and physical-validation boundary.
The expanded compiled Swift IPC fixture passed with synthetic login/2FA/restore
and installation events, multi-game metadata persistence, manual-refresh protocol
assertions, account rejection and non-destructive record removal. The production
ARM64 bundle compiled and its signature verified; localization, documentation and
78-profile catalog checks passed. No runtime, shader or Rust helper change was
needed. No real account login, signing or physical-device install was performed.
## Guided device workspace and complete iOS packaging (1.9)
The device workspace now exposes metadata, transport/readiness, installed app
search, update selection, Apple-account access and separate team registration
queries. USB/Wi-Fi duplicates merge by UDID with USB preferred. After preparing
an iOS game the installer opens, but signing/installing still needs explicit
approval in the UI. Lightweight mode remains default; full-only mode includes
RomFS and skips separate transfer. The new `DMPreferBundledRomFS` runtime flag
prevents an older Documents/romfs from shadowing a complete native installation;
unflagged apps retain legacy overrides. No saves or external directories are
removed. Installation activity is bounded, timestamped, copyable and privately
retained, without raw protocol/account credentials.
Validation: 11 Rust cases passed; the expanded Swift fixture covers duplicate
connections, metadata, installed-target continuity, explicit team-account device
queries, full/light output selection, legacy full-record refusal and private log
permissions/redaction. All 64 script cases completed (two optional dependency/
compiled-helper checks skipped in that suite); documentation/localization and
78-profile catalog gates passed. The unsigned iOS compile check compiled 178
files with zero failures. Read-only real-device probing returned one unique
connected iPhone with model/OS and 184 installed user apps. No Apple-account
login, registration query, signing or installation was performed. The Mac
regression harness was invoked but found no registered Mac Dread build; no
physical gameplay or full/light migration comparison is claimed. The shader
chain is unchanged. Cloud work remains paused; saves remain local/manual.
## Personal library and frontend progress (1.10)
Reviewed NX2Apple's library/task organization at 0313148688ec and upstream's
frontend contract at ceaf6d6ee21b; no engine revision was integrated by this
frontend change. The [library guide](frontend-library.md) records references,
own implementation, data boundaries and deferred settings/runtime-event work.
The launcher now starts at a personal folder library, with accepted local artwork,
search, separate Mac/iOS output readiness and open/install/update actions.
Preparation remains one live model across navigation. The dock and timeline use
structured counts, dynamic stage totals, source ETA and process exit/cancellation;
log-tail replay retains known stages. Library removal never deletes game files.
Validation: all 65 script cases completed with one optional identity-dependency
skip. Compiled Foundation fixtures exercise persistence, deduplication, both
platform outputs, unavailable/missing paths, malformed-index preservation and
non-destructive removal, plus dynamic progress/ETA/exit/tail cases. The production
SwiftUI bundle compiled and its ad-hoc signature verified. English/Spanish
offscreen renders used synthetic metadata, not live game builds; both READMEs
include current, explicitly labelled examples. Documentation/catalog gates passed
and Python lint introduced no new findings. The game regression harness was
invoked but found no registered Mac Dread build, so no new gameplay result is
claimed. No host, shader-chain, game, key or save data was changed; native signing
and cloud-sync validation limits remain unchanged.
## Inline devices and known-game discovery (1.10.1)
Devices now routes inside the main workspace, with no navigation sheet or Close
button. Preparation remains mounted, and choosing update files switches pages
without dismissing the main window. App discovery now accepts only an explicit
local preparation/installation scope, tries exact BundleIDs first and requests
only an ID index when finding a known standard game's legacy team suffix. It
never matches app names or queries arbitrary app containers; the UI independently
rejects unrelated rows and older helpers cannot issue unscoped discovery.
Read-only validation with the existing Metroid preparation returned only Metroid
Dread on the connected phone. The old 184-app inventory is superseded. Fourteen
Rust cases passed, including identifier boundaries and empty-scope/no-connection;
compiled Swift fixtures rejected unrelated/same-name app rows, and the compiled
helper returned an empty result for a nonexistent device when scope was empty.
No account login, install, uninstall, game-file access or save modification was
performed. Existing signing/device-validation limitations are unchanged.
Final checks: 66 script cases completed with one optional identity-dependency skip;
scoped compiled-helper and Swift client checks passed, documentation passed,
and Python lint retained 414 baseline findings with none added. Offscreen
English/Spanish workspace renders asserted that device routing consumes the
navigation request without attaching a sheet. The production ARM64 bundle
compiled; signing/install execution on real devices was not part of this fix.
The regression harness was invoked again but found no registered Mac Dread
build; this release adds no gameplay validation claim.
## Manual device saves and separate game content (1.11.0)
Devices resolves game artwork by the prepared TitleID and offers a read-only
committed-save ZIP export. Export requires a closed game, reads only save and
save_device, verifies a stable inventory and a second content hash pass, and
never replaces an existing local backup. It rejects aliases requiring guest
metadata that AFC cannot preserve. Cloud synchronization and automatic restore
remain deferred. A read-only physical iPhone export, with the process guard
verified rather than manually overridden, produced four files (38,362 bytes);
ZIP integrity and all manifest hashes passed. The private ZIP is outside Git.
Copy game data now invokes the existing RomFS staging/recovery transfer without
reinstalling the app or touching saves. The prepared and installed game versions
must agree. An explicit app-only update keeps existing RomFS for controls/runtime
changes at the same game version; changed game/DLC assets still require new data.
No physical content transfer, Apple account login, signing or installation was
performed over the user's working game in this release. Physical transfer
interruption/recovery and native signing validation remain outstanding.
Validation: 18 Rust tests passed, including actual ZIP pipeline/manifest/hash/
permissions roundtrip and changing-source rejection. All 66 script cases completed
with one optional JWT dependency skip; compiled Swift client and production
SwiftUI/helper builds passed. Documentation and the 78-profile catalog passed;
Python lint retained 414 baseline findings with no additions. The gameplay
harness was invoked with the local game root, but no registered Mac Dread build
was available; this release claims no new gameplay test. Host and shaders did
not change.
The explicitly approved obsolete backup was permanently removed after checking
for protected data, freeing approximately 35.7 GB before final build-cache
cleanup. Original XCI/update NSP, the lightweight unsigned IPA, RomFS, source
checkout and exported saves remain outside that deletion. A private cleanup
receipt records paths, available space and the IPA hash beside the game tools.
## Global Apple account placement (1.11.1)
Devices now has a persistent account header above the device list, showing the
signed-in email or an explicit signed-out state. Its account window does not
show device selection or installation actions. Installation records still bind
the original account/team to each device and app; authentication semantics have
not changed. The header explains in English and Spanish that exporting saves
and copying RomFS do not require Apple sign-in. These actions retain their
existing pairing, known-game scope and closed-game requirements.
Validation: compiled Swift client/localization fixtures and whole-app Swift
type checking passed. This change is UI-only; the existing native helper is
reused byte-for-byte in the local bundle. No real Apple login, signing, device
installation or game/save mutation was performed.
## Application account and collection detail (1.12.0)
Apple Account now lives in the persistent application sidebar and opens an
inline account-only page from every workspace section. Devices no longer owns
an account card. The sidebar displays the authenticated email or signed-out
state; session storage, account/team/device bindings and credential handling
are unchanged. Device copy/export explicitly remain available without Apple
login. The collection now selects a game from a searchable list and shows its
existing artwork, prepared platforms and separate play/install/update actions.
No library schema, original path, installed game or save data was migrated.
The expanded pinned NX2Apple review is in docs/nx2apple-review.md. It records
native/Qt/core integration boundaries and candidates without claiming a whole
engine audit or importing unsupported save/archive/runtime changes. English and
Spanish README screenshots were regenerated from our actual SwiftUI views with
synthetic metadata. Compiled library/localization/sideload fixtures, SwiftUI
compilation, documentation and catalog checks passed. The unchanged native
helper is reused byte-for-byte in the local application build. No real account
login, signing, installation, game launch or save modification was performed.
## Guided preparation and explanatory library (1.13.0)
The platform-first flow separates destination, source selection/drop, external
keys, review and compilation. Starting a new game clears only form selection;
existing collection entries, files and saves stay in place. Known device updates
prefill only their matching prepared folder. Choosing Mac cancels an outstanding
iOS update intent. Existing outputs can be used explicitly, without recompilation.
The installer uses device/account/review/activity steps and preserves explicit
confirmation and original identity guards. Registered Apple devices are shown
as records, never as an invented remaining free-account quota.
The library now displays scoped device observations separately from local output
readiness. Read-only physical discovery found one matched Metroid installation
on the connected iPhone and recorded private local presence metadata. No Apple
login, app install, game-container read, content copy or save write occurred.
Cached observations after reopening/disconnection are labelled last-seen, not
current verification. Scope filtering and malformed-cache preservation remain.
Per-platform build provenance adds informational advice only for relevant runtime,
build-tool or own-profile changes; missing history is explicit. Global/build-log
diagnostics redact home paths, key-value material, email and authentication/device
lines without altering original logs. Background completion notices request OS
permission at a real build and can be disabled; cancellation and foreground
builds are silent. Real OS notification delivery was not exercised during tests.
Validation: all 68 script cases completed with one optional JWT dependency skip.
New compiled Swift fixtures exercise actual Git history comparisons, missing
revisions, platform-specific metadata persistence, diagnostics redaction, drop
ambiguity, source/repository symlinks, busy guards and unchanged synthetic inputs.
Existing client fixtures now test private scoped presence persistence, historical
versus verified status and unrelated-app exclusion. Affected checks passed again
after flow refinements. Full SwiftUI compilation and synthetic English/Spanish
screenshots cover library, platform selection/review and build activity. Python
lint retained 414 baseline findings, with none added. The game regression harness
was invoked with the local root but found no registered Mac Dread build. No new
gameplay or real-account signing validation is claimed; host/shaders are unchanged.
## New-device registration review and free-team estimates (1.14.0)
Team membership metadata now distinguishes an explicit free membership from the
ambiguous Individual type. Only known free teams show a policy-based, labelled
slot estimate; missing/paid/ambiguous metadata remains unknown. Source policy and
limitations are documented in sideloading.md. Account device names use connected
metadata when possible and omit unexplained raw status letters.
Installation review refreshes registrations, reports whether the selected device
already exists and requires acceptance for a new registration. The helper checks
again before adding any device and refuses new registrations during automatic
renewal. Older helpers cannot bypass the new consent capability. Confirmation is
bound to the chosen team/device; registration success updates local counts even
if a later signing/install step fails. Existing game files and saves are untouched
by review and lookup. No real-account login/registration/install was performed.
Validation uses compiled synthetic client fixtures for case-insensitive matches,
team mismatch, stale snapshots, distinct registration counts, unknown limits,
zero clamping and explicit approval transport. Rust fixtures cover unknown/paid/
free/ambiguous membership and the manual/automatic registration-consent matrix.
The existing original-identity, RomFS, save-export and device-scope checks remain.
Final checks: 20 Rust cases passed. All 68 script cases completed with one
optional JWT dependency skip; compiled consent/capacity client checks, complete
SwiftUI compilation and synthetic new/existing registration renders passed.
Documentation and the 78-profile catalog passed. The production native helper
was rebuilt with the pinned toolchain; real-account registration/signing remains
unexecuted. No games or save data were accessed or modified by these tests.
## Settings organization and independent controller test (1.14.1)
Replaced the cramped sidebar account/language/version stack with one Settings
entry. General hosts language, notifications, diagnostics and version; Apple
Account retains the shared model and full account/registration interface;
Controller embeds the existing live tester inside the page. Command-comma opens
Settings. Controller UI was removed from preparation rather than duplicated.
No account/token storage, mapping, game pipeline, native helper, save or game
file changed. Existing launcher instances were not restarted.
Validation: compiled controller lifecycle/input fixtures and localization passed,
including live buttons, D-pad, sticks, triggers, release and disconnection. Whole
SwiftUI compilation and offscreen Settings/library/controller renders validate
the layout; they do not claim new physical hardware/gameplay tests. The native
helper is reused byte-for-byte for this UI-only patch.
## In-window compatibility page (1.14.2)
Removed the catalog sheet and routed all catalog entry points through workspace
navigation. The sidebar shows Compatibility selected; search, profile counts,
artwork and note links retain the existing data model. Flexible sizing replaces
the modal frame and Close button. No helper, game or save behavior changed.
Offscreen navigation validates that the open-catalog intent is consumed without
an attached sheet; catalog fixtures/checks and the production SwiftUI build
validate the change. No gameplay/device actions were performed.
## Automatic remembered account recovery (1.14.3)
The observed sign-in form was caused by missing normal-startup session recovery:
only a manual action or a due app refresh invoked the existing Keychain route.
This fix queues one authentication-only recovery when the remembered-account
marker exists, waits for the shared helper to be idle and prevents duplicate
attempts/retry loops. Failure preserves saved data and offers retry/password
choices without asserting that the token expired. A per-account previous team
is restored only against fresh team membership and never over an active refresh.
Explicit logout/cancel prevents queued recovery; cancelled remember-choice intent
cannot overwrite a prior saved preference during a later recovery.
The native helper, Keychain service/key/format, game files and saves are unchanged.
No real Keychain value was read, credential printed, Apple login performed or
running launcher restarted. All 68 script cases completed with one optional JWT
dependency skip; expanded fake-helper recovery/cancellation fixtures passed again
after state cleanup. These test restoration orchestration, not the user's actual
session validity. Production compilation/signature validation and documentation
checks validate the delivered UI/client patch.
### Guided preparation inspection (1.15.0)
Each milestone has a distinct native vector composition. The file step lists top-level NSP/XCI/NSZ/XCZ regular files, their size and readability; it does not infer a title/version from folder names or decrypt packages. Identity and key compatibility are confirmed by preparation. Unreadable or empty packages block continuation. Drop stays on the file step so the user can inspect before continuing. The keys picker accepts the `prod.keys` file directly (or drag/drop), rejects empty/unreadable files and resolved repository paths, and remembers only its parent path for the existing build CLI. No key contents are read by the picker. Existing folder preferences remain compatible. Synthetic workflow tests cover inspection, direct key selection and source preservation.
### Internal game identity before compilation (1.16.0)
The file step now invokes `scripts/inspect_game.py` with the chosen local `prod.keys`. Its metadata-only `nx_extract --inspect` mode reads CNMT identities, package types and numeric versions plus NACP names/display versions. It writes no game extraction or app. The highest internal update version is selected regardless of filename tags; base-only folders are explicit. Multiple game identities, missing bases, partial reads, missing reader tools and unreadable keys have distinct states. Unknown display versions are shown as internal numeric versions, never invented. Compressed archives work when their metadata/control NCAs remain readable; otherwise the UI reports failure rather than silently ignoring them. The reader is built once per source digest into an external user cache, with optional explicit .NET setup.
Both iOS and Mac build wrappers receive the inspection manifest and use its actual title IDs and numeric package versions rather than filename guesses. Input filenames/sizes/mtime are rechecked before the normal builder runs; changes require re-identification. The manifest contains no key contents and is removed after the preparation exits. Existing CLI users without a manifest keep the original scanning behavior. Verified read-only against the local Metroid base XCI (1.0.0) plus update NSP (2.1.0, internal 327680), without extracting RomFS or invoking a game. Runtime/graphics paths are unchanged; no gameplay or device install was used to validate this metadata feature.
### Unified installation and account design (1.17.0)
Successful iOS preparation configures the shared installer inline in the existing workspace; it does not sign/install automatically. Later library/device install actions retain the modal variant. Settings embeds the same account content. Shared design tokens, cards, fields, milestone artwork and button styles live in `desktop/NxNativeEasy/DesignSystem.swift`; the interaction and validation contract is [the design system](design-system.md). Review keeps a single final action in a safe-area footer and describes unmet requirements. Logs remain available but start collapsed. Account login, remembered-session recovery, 2FA, team selection, membership estimates, registration approval and renewal retain the native helper's guards. The success screen requires a validated installation event, rather than idle status or 100% progress. Native signing/helper code was unchanged.
Validation used synthetic IPC/device/account fixtures, with rendered English/Spanish forms, recovery, 2FA, slots, device selection, new registration review, progress, failure and verified success. Long input text was checked at the minimum 760-point content width. README previews are explicitly fictitious. No personal credentials, real signing/install or game/save changes were used for this UI release.
### Installation presence and dismissible completion (1.17.1)
Root cause: validated `installed` IPC events saved signing jobs, but did not update/persist `deviceGames`; the library therefore continued to show a local artifact and no installation until another device query. A successful event now updates the exact device/bundle observation and installed version immediately. Custom effective IDs are matched through their exact signing-job identity. Older signing receipts without presence history appear as historical records with unknown observation date/version, without pretending the phone was checked. Fresh negative scoped queries suppress these fallbacks. Restarted observations remain historical until checked again. Existing JSON dates decode through the now-optional observation date.
Library rows distinguish local Mac apps, local IPA files, verified installations and historical records; rebuild advice remains separate. Detail uses separate prepared and installed versions. The completed build dock can be dismissed without discarding activity/transcripts, and the next build restores it. Current successful install text supersedes the preparation-only dock caption. Synthetic client tests cover immediate presence, offline reload, legacy receipt fallback, unrelated IDs and a negative query after installation. No real device install/signing or game/save mutation was performed.