- New dm/gfx/tools/unityfs.py: UnityFS bundles (LZ4 / LZMA blocks), serialized files (formats 19..22), objects read by their type trees (Unity's common string table comes from the game's own main), and a Shader object's per-platform compressed subprograms. The player's own files have no type trees and borrow the bundles' Shader tree of the same type hash. - shader_split.py reads Unity Shader objects in a second pass: each Switch subprogram is one NVN program (0x60-byte header of per-stage offset / control size / code size, then control + code per stage); a program's vertex / pixel stages form a pair. Engage: 9,269 shaders, 4,634 pairs. Mario's tables and package are byte-identical. - Engage profile: shader groups engine / assets, measured NVN layout (texture handles 0x168 + 8 r, 128 slots; separate samplers 0x568), regenerated tables, chain a295a8035b7b. 9,268 of 9,269 compile (one geometry shader; Metal has none). - dmsh_build.py: the translator's built-in bases only matter with per-slot facts, so the guard checks that; c2 reads count the sampler table and 16-byte rows overlapping a table as fillable. - game.py: DM_GAME_NVN_SAMP_BASE. NSP script survey note updated. - Mario chain is now 271056163ea2 (Mac and iOS libraries rebuilt, 16,266 of 16,268 as before; c1_full regenerated).
4.7 KiB
4.7 KiB
Game profiles
Each game the port knows has a folder dm/games/<id>/. Mario's is smbw. The folder holds:
game.tsv: the game's constants, onekey<TAB>valuerow each, with comments on their own lines.- The shader tables that
shader_split.pywrites:shader_map.tsv(+.conflicts.tsv),shader_facts.tsv,shader_pairs.tsv. c1_full.tsv: regenerated byc1_guard.pyafter each shader build.pso_manifest.tsv: the prewarm seed list.bf_want.tsv.dmsh_chain.h: the chain version, written bydmsh_chain.py --header.
Everything game-specific belongs here. Code reads the profile; it does not hard-code a game.
| Who | Picks the profile with | Reads |
|---|---|---|
Shader tools (shader_split.py, dmsh_chain.py, dmsh_build.py, dmsh_facts.py) |
DM_GAME=<id> |
tables, nvn_*, shader_* keys |
Host (make -C dm/host, dm/gfx/tests) |
GAME=<id> |
gen/<id>/game_profile.h, generated from game.tsv by game.py header |
dm/ios/tools/build_ipa.sh |
GAME=<id> |
app name, bundle ID, IPA name, modules, RomFS marker, tables |
dm/ios/tools/build_ipa_from_nsp.py |
the NSPs' title ID | everything above. With no matching profile it only writes a survey |
All of them default to smbw. To query a profile:
python3 dm/games/game.py listlists the profiles.python3 dm/games/game.py <id> <key>prints one key.
Keys
| Key | Meaning |
|---|---|
title_id, program_id |
Identity. program_id is what svcGetInfo(ProgramId) reports. The game's name is not stored: the NSP script reads it from the NSP's control data (NACP) |
app_name, bundle_id, ipa, app_category |
iOS app. app_name is only the fallback: the NSP script uses its APP_NAME setting, else the game name from the NSP. A manual build_ipa.sh run and the Mac window title use app_name |
build <ver> <display version> <module>=<build ID>… |
One row per known version. The NSP script rejects other builds. The host reports the display version by main's build ID |
package |
Version packed into the IPA by default |
shader_versions |
Versions whose union the shader tables are (base game first). All of them are needed to build |
modules |
Code modules rewritten and loaded, in load order. rtld is replaced by the host |
romfs_marker |
A path that must exist in the RomFS |
zip_store |
Extensions the IPA stores without compressing |
mem_reserve, mem_alias, mem_stack, app_pool |
Guest address-space partition and application pool (hle_svc.c) |
nvn_tex_base, nvn_tex_slots, nvn_samp_base, nvn_img_base, nvn_img_slots, nvn_ssbo_bases |
NVN driver constant segments, measured over all shaders |
shader_groups, shader_note |
shader_split.py source groups and the map header note (Engage: engine for data.unity3d and Resources/, assets for the Addressables bundles) |
The translator (dmsh_xlate/Program.cs) still has Mario's texture / sampler bases and the depth convention built in. It uses the bases only to look up per-slot texture / image types in shader_facts.tsv; dmsh_build.py decodes the bindings from the profile. So a profile with other bases (Engage: 0x168 / 0x568) builds as long as its facts table has no per-slot texture / image entries; otherwise dmsh_build.py stops, and the translator must take the bases from the profile first.
Adding a game
- Survey. Run
dm/ios/tools/build_ipa_from_nsp.pyon the game's NSP folder. Without a profile it extracts only the code modules and a RomFS list, then writes<NSP folder>/<title ID>_survey/survey.md. The survey covers modules and build IDs, SDK, engine and graphics API markers, RomFS containers, the rewrite-site census, and imports compared with the host hooks. - Profile. Copy
smbw/game.tsvtodm/games/<id>/game.tsvand fill in the identity,buildrows andmodules. Leave the memory and NVN values as Mario's until the bring-up measures them (docs/agents). - Shader tables. Run
DM_GAME=<id> shader_split.py … dm/games/<id>/shader_map.tsv dm/games/<id>/shader_facts.tsv dm/games/<id>/shader_pairs.tsv. Add an emptyshader_map.tsv.conflicts.tsv,bf_want.tsvandpso_manifest.tsv(copy Mario's headers). - Chain.
DM_GAME=<id> dmsh_chain.py --headerDM_GAME=<id> [IOSOUT=…] dmsh_build.py <pkg> <cache>c1_guard.py <cache>/b-<V> dm/games/<id>/c1_full.tsv
- Host bring-up. Run
make -C dm/host GAME=<id>, then work through the bring-up loop in docs/agents. Code that only fits one game is gated by its build ID (dm_main_build()), never left to run on every game. - IPA. Use
GAME=<id> dm/ios/tools/build_ipa.sh, or the NSP script, which now finds the profile.