am-stubs-legal (2026-11-02, batch r4): title legal pages, am stub rows made real, offline network-connection applet¶
Branch claude/am-stubs-legal-2026-11-02-r4 (Mac session, base orchestrate/2026-11-02-r4 = 90d3e3c).
1. LegalInformation extraction (crates/napple-build)¶
html.rs now handles document contents generically (read_document / write_document): HtmlDocument (CNMT content type 4) still goes to
<version dir>/html/; LegalInformation (content type 5) goes to <version dir>/legal/ (newest version that carries one, written into every
version directory, like the manual). The runtime's offline web applet already served ApplicationLegalInformation documents from <data-dir>/legal
(r3), so nothing changes there.
- Tests: unit write_legal_goes_under_legal; e2e html_document_written_next_to_romfs_map builds a LegalInformation NCA next to the manual and checks
legal/legalinfo.xml, legal/ipnotices.htdocs/index.html, a nested image, and that a title without one gets no legal/.
cargo test --release -p napple-build: all suites ok.
- Real title (built privately into $TMPDIR, the shared NappleWork/games untouched): LAS's legal/ holds legalinfo.xml, ipnotices.htdocs/,
support.htdocs/ (per-region support pages), important.htdocs/.
2. A title's legal page in the web sheet (verified)¶
LAS from that private data directory, windowed, NAPPLE_OPEN_WEB_DOC=20:2:0:ipnotices.htdocs/index.html NAPPLE_SHEET_AUTO=confirm:5000:
applet: web document: shown, exit reason 0, last page file:///…/legal/ipnotices.htdocs/index.html. The web sheet shows LAS's own notice page
(©1993-2019 Nintendo, "Nintendo and GREZZO are the authors of this software…", the licence notices). Screenshot in the batch's shared notes folder.
3. am stub rows made real (13)¶
| Interface / cmd | Before | Now |
|---|---|---|
| IApplicationFunctions 70 RequestToShutdown, 71 RequestToReboot, 310 RequestExitApplicationAndTryPopMessage | no-op | the application is told to leave (AppletMessage ExitRequested, 4) and gets a grace period (3 s, NAPPLE_EXIT_GRACE_MS) to save and exit itself; then the host stops cleanly (summary partial "guest requested |
| IApplicationFunctions 180 GetLaunchRequiredVersion, 181 UpgradeLaunchRequiredVersion | 0 / no-op | kept per title in <save-root>/<title id>/launch_required_version, only ever raised, read back across runs |
| IApplicationFunctions 110 QueryApplicationPlayStatistics, 111 …ByUid | 0 entries | one PdmApplicationPlayStatistics {id, play time ns, launches} per requested id that has been played: the running title (play time since its first appletOE session, 1 launch); others unplayed; duplicates once |
| IApplicationFunctions 34 SelectApplicationLicense, 36 GetLimitedApplicationLicense | stub | the documented answers for a full license (nothing to select; no limited license) |
| ISelfController 120 SaveCurrentScreenshot | no-op | the current screen is saved to the caps album as a JPEG screenshot (refused with capture's not-permitted result when the guest disabled screenshots, not-found when no frame exists yet) |
| IDisplayController 28 TakeScreenShotOfOwnLayerEx | no-op | the same capture as TakeScreenShotOfOwnLayer (8) into the application slot |
| ICommonStateGetter 1004 GetPanelPhysicalSizeForSpecificTitle, 1005 GetPanelResolutionForSpecificTitle | stub | the built-in panel's real values (137 x 77 mm, 1280 x 720) |
New: the capture slots have a real frame source. applet_capture_host.c registers the graphics back end's last completed frame
(napple_gfx_screenshot_last, decoded with ImageIO and scaled to 1280x720) for IDisplayController's capture images. Until now no source was ever
registered, so every capture image read as black.
Kept as state only (still IPC_STUB, listed): ISelfController 45 SetManagedDisplayLayerSeparationMode, 46 SetRecordingLayerCompositionEnabled, 130
SetRecordVolumeMuted (no game play recording or separable layers on the host).
Still stubs (rest): ICommonStateGetter 52, 62, 80, 100, 130, 1003; IDisplayController 21; IApplicationFunctions 10, 12, 72, 120, 121, 122, 124, 131,
170, 190, 200, 220, 1000, 1001; the applet-side 50, 80, 81, 160. Reasons per row in the integrator file.
Debug aid: NAPPLE_DEBUG_SAVE_SCREENSHOT=<seconds>[:<file.jpg>] runs SaveCurrentScreenshot's path from a host thread (no test title calls it).
Verified with it on LAS (headless, 12 s into the opening movie): SaveCurrentScreenshot: saved to the album (115030-byte JPEG). The album JPEG
is the storm scene the game was showing, so the real frame source works end to end: gfx readback, then the capture slot, then the caps album.
4. Network-connection library applet (NetConnect, 0x0f)¶
A title launches it after nifm reports a failed network request (IRequest::GetAppletInfo). It is now answered through the handler table: it finishes
normally (GetResult success) and its output storage {u32 result; u32 pad} carries the "not connected" result, the same result nifm's
GetResult gives offline here (HZ_R_NIFM_NETWORK_COMMUNICATION_DISABLED, 2110-1111). So HandleNetworkRequestResult keeps the request failed and
the game stays offline, as on a console that cannot connect. Before, the applet was "not implemented" and terminated with 0x1480. Layout and code are
[unverified] against a console. MK8D's Online Play reaches this applet only once nifm's GetAppletInfo (offline.c, ipc-reference-audit) hands out
the applet info; verification is planned at integration.
Tests¶
test_svc_applet_am (new, 67 checks): license / panel answers; launch-required version (raise, never lower, persisted); play statistics; the
screenshot paths with a test frame source (album JPEG decodes back to the frame, permission refusal, no frame); NetConnect through the IPC accessor
(Start, completed, GetResult 0, 8-byte output with the not-connected result); the three exit requests each in a forked child (ExitRequested
message once, process exits 0 within the grace period). make -C runtime test test-services rc 0 (ipc_reference 111 fixtures, missing 0; every
applet suite green).
Regression¶
SMO, MK8D, ACNH, LAS 60 s headless with their golden inputs (this runtime, private saves): exit 0, no crash. None of them calls a command changed here in that window.