Files
seedds f4f0a85d6e patch_nso.py split into the general rewriter and per-build patch files (GitHub issue #63): every game's bring-up edited the one file, so PRs for unrelated games conflicted. dm/tools/patch_nso.py (1,711 → about 1,100 lines) keeps the general passes and the driver; the instruction encoders move unchanged to dm/tools/nso_asm.py; a game's own patches (CUTS, OBS and the frame-step stubs, now FRAME_STEP = {build ID: stub(ctx)}) move verbatim with their comments to dm/games/<id>/patches.py (16 profiles: Tears of the Kingdom, Breath of the Wild, Age of Calamity and Paper Mario: The Thousand-Year Door with their stubs), and the cuts of Nintendo's sdk module builds, 6 of the 15 shipped by 2 to 4 games, to dm/tools/sdk_patches.py. The rewriter loads every patch file, whatever the game, and applies those of a module's build ID at the points it did before (cuts before the scan, gates after the rewrite, stubs after the gates); a build ID claimed twice stops it, and its command line needs no DM_GAME. build_src.sha256 lists the rewriter's files and every patch file (the same for every module). NSP script: the rewrite step's reuse key counts nso_asm.py and only the patch files that name one of the game's builds (patch_files), profile_files leaves patches.py out (only the rewrite step reads it), and a derived profile gets none. dm/tools/tests/test_patches.py: no build claimed twice, the entries' forms, a game's file names only its own profile's non-sdk builds, sdk_patches.py only sdk builds some profile ships, and the reuse key's patch files. Checked byte-identical: every extracted module on the game drive (131 builds) rewritten with the previous and the new rewriter gives the same outputs but build_src.sha256, 28 of the 30 patched builds among them (all four frame-step mains), and 7 fail alike (rtld, and New Super Mario Bros. U Deluxe's, which no profile rewrites); the other two (Super Mario Bros. Wonder's mains and Absolum's, no game files here) by their CUTS / OBS data, equal to the previous tables. AGENTS.md, docs, dm/games/README.md, the profiles' comments and the host comments that named a game's cut in patch_nso.py now name its patches.py or sdk_patches.py
2026-10-23 23:36:21 +08:00

3.0 KiB

1# OCTOPATH TRAVELER 0: Square Enix, Unreal Engine 5 (the paged global distance field shaders and the LowLevelTasks task system; mobile renderer, SF_GLSL_SWITCH_MOBILE shaders), SDK 19.3.13, NVN, CRIWARE sound and movies; update 1.0.5.
2# Started from the NSP script's provisional profile (dm/games/profile_guess.py) and brought up on the Mac (docs/agents)
3# ---- Identity ----
4title_id01005270232F2000
5program_id01005270232F2000
6# ---- App ----
7nameOCTOPATH TRAVELER 0
8ipaOctopathTraveler0.ipa
9app_categorypublic.app-category.games
10# ---- Versions ----
11buildv3276801.0.5main=9fe60a212cff9b9a3656ab63d4f882e5subsdk0=ca7f78a09c547c54beac3a33ed6bf364sdk=7bba185a35ee9b4d0af66a4df910a823
12packagev327680
13shader_versionsv327680
14# ---- Files ----
15# subsdk0 is NintendoSDK_movie (main imports from it)
16modulesmain subsdk0 sdk
17romfs_markerOctopath_Traveler0/Content/Paks/pakchunk0-Switch.pak
18zip_store.awb:.pak:.ttf:.txt:.usm:.metallib
19# ---- Graphics ----
20# 60 fps (30 on the Switch): the game's config caps Unreal at 30 (t.MaxFPS 30, FrameRateLimit 30, and rhi.SyncInterval 2, the present interval 2 it asks for); the host writes t.MaxFPS 60 and
21# rhi.SyncInterval 1 into its console variables (hle_fpslock.c), so the game asks for present interval 1 itself. Unreal steps the game by the time that really passed.
22# DM_FPS_TARGET=30 (Documents/env.txt on the iPhone) keeps the game's 30 and saves power
23fps_target60
24# ---- Memory ----
25# Unreal Engine: nninitStartup (main+0x12d74) needs virtual address memory and reserves one address region for the engine (-SwitchVirtualAddressSpaceSize= in MB, game_args below; 62 GB by default).
26# Its allocator, an FMallocBinned3 (37 GB of address space), is replaced by the host's (patches.py cut, hle_uemalloc.c), and the SDK's node area is cut to 62 MB (dm/tools/sdk_patches.py), so the region holds what the engine maps
27# itself (709 MB at character creation, spread over 1 GB). Alias region = node area + up to 1 GB of alignment (regions of 1 GB and more are 1 GB aligned) + the 2 GB region; 256 MB stacks, 128 MB heap
28game_args-SwitchVirtualAddressSpaceSize=2048
29mem_reserve0xF0000000
30mem_alias0xC8000000
31mem_stack0x10000000
32app_pool0xCD500000
33# ---- NVN ----
34# 61 vertex shaders pick one of several c2 table slots at run time (separate texture slots 0-10, samplers 0-4), which the translator cannot trace to one slot: slot indices in c2, the bound slots in its arrays
35tex_dyn_slotson
36# ---- Shader split ----
37# The decoded shader code archives: the engine's global shaders and the game's material shaders
38shader_groupsShaderArchive-Global-=global ShaderArchive-=game
39shader_noteOCTOPATH TRAVELER 0 shaders
40# The shaders are in the shader code archives of the pak (ShaderArchive-*.ushaderbytecode), each one Oodle-compressed; the NSP script decodes them on the Mac with the game's own Oodle (uepak.py, DM_OODLE_JOB),
41# as for any game with Unreal paks. Unreal combines vertex and pixel shaders per draw from its pipeline state, so every vertex shader is made to link with any pixel shader (shader_split.py separable_pairs)