Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ Under a game, "60 fps with 1.2.0" means it runs at 60 fps (30 on the console) wi
<tr>
<td align="center" valign="top" width="33%"><img src="docs/images/kirby-star-allies.jpg" width="256" alt="Kirby Star Allies"><br><small><b><a href="dm/games/01007E3006DDA000/README.md">Kirby Star Allies</a></b></small></td>
<td align="center" valign="top" width="33%"><img src="docs/images/dave-the-diver.jpg" width="256" alt="DAVE THE DIVER"><br><small><b><a href="dm/games/010097F018538000/README.md">DAVE THE DIVER</a></b></small></td>
<td></td>
<td align="center" valign="top" width="33%"><img src="docs/images/hello-kitty-party-land.jpg" width="256" alt="Hello Kitty Party Land"><br><small><b><a href="dm/games/01001340271EC000/README.md">Hello Kitty Party Land</a></b><br>60 fps with 1.0.2</small></td>
</tr>
</table>
<!-- end of gallery -->
Expand Down
46 changes: 46 additions & 0 deletions dm/games/01001340271EC000/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Hello Kitty Party Land (`01001340271EC000`)

Update 1.0.2 (v131072), SDK 21.4.4 (its own `sdk` build). Unity 6000.3.11f1 IL2CPP with CRIWARE movies (USM), Addressables and Pia, on NVN. Modules `main`, `subsdk0` (the SDK's movie library) and `sdk`. Plays on the Mac and on the iPhone at 60 fps, where the Switch runs gameplay at 30 (IPA 2.7 GB, 2,936,235,769 bytes).

On the Mac (M3 Pro, macOS 27.0, 2026-11-01): about an hour of play, the menus, the tutorial and minigames, with sound (six-channel AudioOut, no underruns). 60 fps in menus and gameplay (frame time p50 16.66 ms, p95 17.2 ms; GPU under 2 ms a frame), the minigames at their intended speed. About 2 GB resident and 0.5 GB of Metal allocations.

On the iPhone (iPhone 15 Pro Max, iOS 27.0.1, 2026-11-01): about 30 minutes, a few minigames, 60 fps with the cut, on par with the Mac.

It needs two tool changes, each in a PR of its own: the rewriter's split of two patch areas within the back area's reach (`main` has 151.8 MB of text; [PR #856](https://github.com/seedds/nxnative/pull/856)) and Unity 6000.3's `ComputeShader` layout in `unityfs.py` (the GPU skinning kernels; [PR #857](https://github.com/seedds/nxnative/pull/857)). See [Found on this game](#found-on-this-game).

## What it needs

| Setting | Where | Why |
|---|---|---|
| 60 fps: the game's 30 fps mode → its 60 fps mode | [`patches.py`](patches.py), `main` build `ca82f3ab…`, `main+0x863b318` / `+0x863b31c` / `+0x863b4ec` / `+0x863b4f8` | gameplay switches to 30 fps; see [Frame rate](#frame-rate) |
| `sys_resource 0x1000000` | [`game.tsv`](game.tsv) | the NPDM declares 16 MB of system resource, so `IsVirtualAddressMemoryEnabled` is true |
| 5 GB block: `mem_reserve 0x140000000`, `mem_alias 0x118000000`, `mem_stack 0x10000000`, `app_pool 0xCD500000` | `game.tsv` | the block of the other Unity games with virtual address memory (4.375 GB alias region) |

## Frame rate

- The game's frame-rate class has `SetMode(int)` at `main+0x863b2a0`: 1 calls `QualitySettings.set_vSyncCount(1)` and sets a mode field (its statics +0) to 1, 0 calls `set_vSyncCount(2)` and sets it to 0, and 2 does as 1 with one more call (`main+0x863b580`). Each gameplay section calls `SetMode(0)` when it starts and `SetMode(1)` when it returns to the menus (nine pairs), so on the Switch the menus run at 60 fps and gameplay at 30, as an emulator shows.
- **Current: 60 fps everywhere.** `patches.py` makes the 30 fps paths of `SetMode(0)` and of the setter at `main+0x863b4a0` the 60 fps ones: `vSyncCount` 1 and the field 1, so the field stays consistent with the rate. Nothing in `main` calls the field's getter (`main+0x863b6a0`) directly. Over about an hour on the Mac and 30 minutes on the iPhone the minigames kept their intended speed at 60: they step by elapsed time.
- Unity's `Application.set_targetFrameRate` is called only from engine code, not the game's; no engine limit holds gameplay at 30 once the count is 1.

## Memory

- As BALL x PIT ([010086A022444000](../010086A022444000/README.md)): 16 MB of system resource, no heap (`main` never sets one), Unity 6 reserving and committing its memory itself in the alias region, and the 5 GB block.
- **Current: no SDK cut.** The SDK-wide node-area cut ([The SDK's address-region node area](../../../docs/reference/memory.md#the-sdks-address-region-node-area)) and the 2 MB alignment rule match no function in this SDK 21.4.4 `sdk` build (`148394139d9d26ca8afd5c9ba5d30601`; `sdk.match.tsv`), and neither platform needs them: the Mac and the iPhone 15 Pro Max ran the menus, the tutorial and minigames without them.
- Measured on the Mac (menus, the tutorial and minigames): the alias region's high-water mark +2.75 GB of 4.375 GB, 1.26 GB mapped.

## Rendering

- The game draws into 1920x1080 window textures and crops the window (`nvnWindowSetCrop`) to 1280x720 at boot and 1920x1080 after.
- **Open:** `nvnTextureViewCompare` is unimplemented (`[RED] NVN unimplemented function called`, a few times a frame at most; 42 lines in one run). No wrong picture has been traced to it.

## Files and shaders

- 12,739 shaders (6,321 vertex, 6,325 pixel, 92 compute, one geometry), 11 of them the player's own GLSLC outputs in `main`; everything is under `Data/` (`shader_groups /Data/=data`). The Addressables bundles are in `StreamingAssets/aa/Switch`, the movies are CRIWARE USM files.
- The loose serialized files (`globalgamemanagers.assets`, `resources.assets`, `Resources/unity default resources`) have no type trees. Their `Shader` objects borrow the tree the bundles carry ([Unity 6 Shader objects](../../../docs/reference/shaders.md#unity-6-shader-objects)); their `ComputeShader` objects, Unity's `Internal-Skinning` and `Internal-BlendShape` among them, need `unityfs.py`'s Unity 6000.3 layout. Without it the characters drew as huge stretched triangles.
- `romfs_marker` is the opening movie, `Data/StreamingAssets/aa/Switch/movieassets_assets_assets/app/movieresources/opening.usm_data`.
- No prewarm list is recorded yet (`pso_manifest.tsv` is empty); the pipeline prebuild covers first sights.

## Found on this game

- `main`'s 151.8 MB of text and 98 MB of ro, data and bss after it: the split of the [two patch areas](../../../docs/reference/rewriter.md#two-patch-areas-for-a-text-longer-than-a-branch-reaches) has to stay within the back area's reach ([PR #856](https://github.com/seedds/nxnative/pull/856)).
- Stripped Unity 6000.3 `ComputeShader` objects, hash `43a0948a…`: [Unity ComputeShader objects](../../../docs/reference/shaders.md#unity-computeshader-objects) ([PR #857](https://github.com/seedds/nxnative/pull/857)).
1 change: 1 addition & 0 deletions dm/games/01001340271EC000/bf_want.tsv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# generated
41 changes: 41 additions & 0 deletions dm/games/01001340271EC000/game.tsv
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Game profile of Hello Kitty Party Land (Switch, update 1.0.2 = v131072). Plays on the Mac and on the iPhone at 60 fps
# Format: see dm/games/README.md
# Unity 6000.3.11f1 (IL2CPP) with CRIWARE movies (USM) and Addressables, SDK 21.4.4; subsdk0 is the movie module. The player's serialized files and built-in resources have no type trees; the
# Shader objects borrow the tree the Addressables bundles carry, and the ComputeShader objects (Unity's GPU skinning among them) take unityfs.py's written-out 6000.3 layout (COMPUTE_6000_3).
# The game runs its menus at 60 fps and switches gameplay to 30; patches.py keeps its own 60 fps mode everywhere (README.md, Frame rate)

# ---- Identity ----
title_id 01001340271EC000
program_id 01001340271EC000

# ---- App ----
# Game name: only a label for profile listings and the Mac window title; the iOS app name is the game name in the base NSP, and the bundle ID comes from the base game title ID (build_ipa.sh / NSP script)
name Hello Kitty Party Land
ipa HelloKittyPartyLand.ipa
app_category public.app-category.games

# ---- Versions ----
build v131072 1.0.2 main=ca82f3aba950e98060aa8b6aa8f24217 subsdk0=fc537f0b21c880e89d6f247d22a150ca sdk=148394139d9d26ca8afd5c9ba5d30601
package v131072
shader_versions v131072

# ---- Files ----
# load order as on Horizon: main, subsdk*, sdk (subsdk0 = NintendoSDK_movie)
modules main subsdk0 sdk
romfs_marker Data/StreamingAssets/aa/Switch/movieassets_assets_assets/app/movieresources/opening.usm_data
zip_store .assets:.bin:.bundle:.dat:.ress:.usm_data:.metallib

# ---- Memory ----
# The NPDM declares 16 MB of system resource, so IsVirtualAddressMemoryEnabled is true, and main never sets a heap: Unity 6 takes its memory itself in the alias region, as in BALL x PIT and
# Hollow Knight: Silksong. The 5 GB block of the other Unity games with virtual address memory. Measured on the Mac (2026-11-01, menus, the tutorial and minigames): the alias region's
# high-water mark +2.75 GB of 4.375 GB, 1.26 GB mapped, about 2 GB resident and 0.5 GB of GPU memory
sys_resource 0x1000000
mem_reserve 0x140000000
mem_alias 0x118000000
mem_stack 0x10000000
app_pool 0xCD500000

# ---- Shader split (shader_split.py; Unity Shader and ComputeShader objects, read by unityfs.py) ----
# everything is under Data/: the player's serialized files and the StreamingAssets bundles (12,739 shaders, 92 of them compute, 11 GLSLC outputs of Unity's player in main)
shader_groups /Data/=data
shader_note Hello Kitty Party Land shaders
23 changes: 23 additions & 0 deletions dm/games/01001340271EC000/patches.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Hello Kitty Party Land (01001340271EC000): patches of the game's own builds (dm/tools/patch_nso.py "Registered patches"; README.md, Frame rate)
CUTS = {
# 1.0.2 (v131072) main: run the game's own 60 fps mode everywhere.
# The game's frame-rate class (statics at [[main+0xcdba088]+0xb8], an int at +0 = 1 in 60 fps mode, 0 in 30) has SetMode(int) at main+0x863b2a0:
# 1 → QualitySettings.set_vSyncCount(1) and the field 1; 0 → set_vSyncCount(2) and the field 0; 2 → as 1 and one more call (main+0x863b580).
# Each gameplay section calls SetMode(0) when it starts and SetMode(1) when it returns to the menus (nine pairs, main+0x35e5a3c / +0x35e5ca4 and so on), so on the Switch the menus run at
# 60 fps and gameplay at 30; main+0x863b4a0 is a 30 fps setter of the same shape. The cuts make the 30 fps paths the 60 fps ones: vSyncCount 1 and the field 1 (str w0, the 1 just
# loaded), so the field stays consistent with the rate. The minigames step by elapsed time: played at 60 they keep their speed (README.md, Frame rate)
'ca82f3aba950e98060aa8b6aa8f24217': [
(0x863b318, '40008052', '20008052', 'frame rate SetMode(0): vSyncCount mov w0,#2 → mov w0,#1 (the game\'s own 60 fps mode)'),
(0x863b31c, '1f0100b9', '000100b9', 'frame rate SetMode(0): 60 fps field str wzr → str w0 (1, as SetMode(1))'),
(0x863b4ec, '40008052', '20008052', '30 fps setter: vSyncCount mov w0,#2 → mov w0,#1'),
(0x863b4f8, '1f0100b9', '000100b9', '30 fps setter: 60 fps field str wzr → str w0 (1)'),
],
}

# How general each patch is and could be (dm/tools/docs_patches.py "Binding", listed in docs/agents/game-patches.md): what, its binding now, the loosest it could have (goal),
# purpose, why; blocked or kept: why the gap stays open (cannot close yet, or left open on purpose); checked: what was checked on game files (else a judgement)
BINDING = [
{'what': "the game's 30 fps mode → its 60 fps mode (vSyncCount 1 and the mode field)", 'now': 'build', 'goal': 'game', 'purpose': 'frame rate',
'why': "the game's own frame-rate class; a function-matched rule could carry it over the game's updates",
'checked': 'about an hour on the Mac and 30 minutes on an iPhone 15 Pro Max at 60 fps, the minigames at their intended speed, 2026-11-01'},
]
2 changes: 2 additions & 0 deletions dm/games/01001340271EC000/pso_manifest.tsv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Prewarm seed list (shipped in the app, next to shader_map.tsv). Rows: S<TAB>shader key<TAB>stage / R<TAB>render pipeline key (pso_skey hex) / C<TAB>compute shader key.
# Empty: no prewarm list recorded yet (record one on the Mac with DM_PSO_MANIFEST=off DM_PSO_RECORD); the pipeline prebuild covers first sights meanwhile
1 change: 1 addition & 0 deletions dm/games/01001340271EC000/pso_pairs.tsv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# kind: evidence supplemental vertex / pixel shader pairs (BAHS program variations and legacy Unity pass-input covers) for vertex output padding and the pipeline prebuild (generated by dm/gfx/tools/shader_split.py, do not edit): vertex key <TAB> pixel key
93 changes: 93 additions & 0 deletions dm/games/01001340271EC000/shader_facts.tsv
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Answer table for translation queries (generated by dm/gfx/tools/shader_split.py, do not edit): key <TAB> fact JSON. No PC shader pairing supplied: only the thread group / shared memory of compute shaders (taken from the control section); other queries get the translator defaults
0137b15eb96d88cc {"local":[64,1,1],"shared":0}
021680c607de6d77 {"local":[1,1,1],"shared":0}
043742cf107c2ae4 {"local":[1,1,1],"shared":0}
076cfff073227ed5 {"local":[64,1,1],"shared":0}
094c1f21532a637a {"local":[8,8,1],"shared":16}
0993a4a427fa32ab {"local":[64,1,1],"shared":0}
0c3a218fde082016 {"local":[64,1,1],"shared":0}
150b34ba6b862409 {"local":[64,1,1],"shared":0}
1561400099e711cf {"local":[8,8,1],"shared":16}
17023907798443de {"local":[64,1,1],"shared":0}
1edc41ef93a96d4f {"local":[32,32,1],"shared":0}
202facb85c6b8e2b {"local":[8,8,1],"shared":16}
24a62a1aaca0a179 {"local":[32,32,1],"shared":0}
2505a4c0d03b675b {"local":[16,16,1],"shared":16}
27ac68eeab8452ae {"local":[1,1,1],"shared":0}
28162ccd96a83b8e {"local":[32,32,1],"shared":0}
2c3fdd248a99c8af {"local":[64,1,1],"shared":0}
2e94e7adcb72c4fe {"local":[32,32,1],"shared":0}
2eb638f5eed80511 {"local":[64,1,1],"shared":0}
34c32aa0b1e33aed {"local":[16,16,1],"shared":16}
34c560817c9902d0 {"local":[16,16,1],"shared":16}
34f79fd22c8e40ba {"local":[1,1,1],"shared":0}
3594acfbbc9c3c57 {"local":[1,1,1],"shared":0}
375828ea6b4eddee {"local":[64,1,1],"shared":0}
40a8a617f5cdc5fd {"local":[1,1,1],"shared":0}
4152ea0c56a99ebc {"local":[64,1,1],"shared":0}
49b409a3167cf73a {"local":[64,1,1],"shared":0}
4e6d3a9c6054eb0a {"local":[16,16,1],"shared":16}
503605940205cea5 {"local":[64,1,1],"shared":0}
51c4c81b7e577d28 {"local":[64,1,1],"shared":0}
5369de42f375bf95 {"local":[64,1,1],"shared":0}
55102da0710a6cd4 {"local":[32,32,1],"shared":0}
55293e530b74de0c {"local":[64,1,1],"shared":0}
552bbc8ffc2459e4 {"local":[4,4,4],"shared":0}
583df74da349cd6a {"local":[1,1,1],"shared":0}
5ac210f5b9183452 {"local":[64,1,1],"shared":0}
5aca55706f57edb5 {"local":[64,1,1],"shared":0}
5ec91b98293453b4 {"local":[32,32,1],"shared":0}
5f1c3a9b64c0334c {"local":[64,1,1],"shared":0}
5f6b194d690d2240 {"local":[32,32,1],"shared":0}
607ab2ef3bd6e9c7 {"local":[32,32,1],"shared":0}
661d530f301b3fa1 {"local":[256,1,1],"shared":0}
673c7272f21576d5 {"local":[1,1,1],"shared":0}
6e31c2f9da6ff9ca {"local":[64,1,1],"shared":0}
6f30ea556045a5d3 {"local":[64,1,1],"shared":0}
7002ed042baa5d8f {"local":[64,1,1],"shared":0}
7148dd4658918ef2 {"local":[32,32,1],"shared":0}
755df78e9dc661a1 {"local":[64,1,1],"shared":0}
7c61811d452a82c7 {"local":[64,1,1],"shared":0}
7e40da5681d25599 {"local":[64,1,1],"shared":0}
7e70c5f671c31efd {"local":[64,1,1],"shared":0}
7f7c0f217793270c {"local":[64,1,1],"shared":0}
82c061fd25e06f59 {"local":[32,32,1],"shared":0}
8a0e8d25588958cd {"local":[64,1,1],"shared":0}
903ce14654bbbe15 {"local":[1,1,1],"shared":0}
92f31c6154b8a604 {"local":[32,32,1],"shared":0}
97cfcd54e66a2e54 {"local":[32,32,1],"shared":16}
99e02ba44eebadc2 {"local":[64,1,1],"shared":0}
9c45ea2d1455a973 {"local":[64,1,1],"shared":0}
9ca5e33ae7eb4ca7 {"local":[32,32,1],"shared":16}
a041a90d13fe1ff5 {"local":[64,1,1],"shared":0}
a446c3032b2c2e55 {"local":[1,1,1],"shared":0}
aa8c350395fa7723 {"local":[1,1,1],"shared":0}
b280a5456c66ec51 {"local":[64,1,1],"shared":0}
bb0839b759814ff5 {"local":[32,32,1],"shared":16}
bb385550d4f89d8d {"local":[32,32,1],"shared":0}
bc6a21db94115b17 {"local":[32,32,1],"shared":0}
bd91c670a1ac323c {"local":[64,1,1],"shared":0}
be194834cbc54b9b {"local":[64,1,1],"shared":0}
be4bd97249bc53c7 {"local":[1,1,1],"shared":0}
bfaa742468d916a7 {"local":[1,1,1],"shared":0}
c1c7afb17eea8f81 {"local":[64,1,1],"shared":0}
c92ab2e4a275a97f {"local":[64,1,1],"shared":0}
c92ee6c7f9299400 {"local":[32,32,1],"shared":0}
cdd48ce4bafe7df2 {"local":[8,8,1],"shared":16}
cf17ee394ba80f72 {"local":[64,1,1],"shared":0}
d59beda3de6b23cf {"local":[64,1,1],"shared":0}
d9933c869ccc50a8 {"local":[64,1,1],"shared":0}
dafef65bce13e73e {"local":[32,32,1],"shared":0}
dcf38c7d830e6687 {"local":[64,1,1],"shared":0}
e1e605535807d9ca {"local":[32,32,1],"shared":0}
e3a88b04e106cc51 {"local":[64,1,1],"shared":0}
e76887d9269100f4 {"local":[64,1,1],"shared":0}
f1ef86326dcbda86 {"local":[64,1,1],"shared":0}
f2521f573c4b437b {"local":[64,1,1],"shared":0}
f2c34b6d6851c9da {"local":[64,1,1],"shared":0}
f351f4a871b45479 {"local":[64,1,1],"shared":0}
f371bbb517735be3 {"local":[64,1,1],"shared":0}
f68c82b81ee389f0 {"local":[64,1,1],"shared":0}
f7fdbeb495810cf0 {"local":[64,1,1],"shared":0}
fa9115e142cdeed8 {"local":[32,32,1],"shared":16}
fc9a81babc7fc8df {"local":[64,1,1],"shared":0}
Loading
Loading