Apply PR #262's device-buffer fix for clicks caused by audio underruns. Reconfigure on both output-device and hardware sample-rate changes, respect the device's frame range, and read back the actual buffer size. Avoid rewriting unchanged sizes when buffer changes notify the format listener. Remove listeners before teardown, including failed initialization, so they cannot configure a device that the AudioUnit has already released. Add hardware-independent regression coverage for switching, rate changes, scope filtering, limits, synchronous notifications, diagnostic overrides, absent devices, and listener cleanup. Record the original gameplay evidence and the additional silent Core Audio validation in the audio reference. Validation: full host tests, Mac host build, docs checks, and the iOS source compile check (178 files) passed. Silent USB/speaker switching and speaker rates of 44.1/96/48 kHz verified 221/480/240 frames. HDMI switching and gameplay listening after these listener changes remain unvalidated. Co-authored-by: parsifal295 <130349765+parsifal295@users.noreply.github.com>
Unicorn Overlord (010069401ADB8000)
Update 1.05 (v262144) over the base game, SDK 16.2.4. Vanillaware's engine, as in 13 Sentinels: Aegis Rim (01003FC01670C000), on NVN. Its ExeFS holds only main and sdk (no movie module); the sdk build is the one Brain Memory Training Academy ships (0100F5D01DA3A000). Playable on the iPhone at 60 fps; on the Mac brought up through the logos, the title, a new game and the opening story scene with no red line.
What it needs
| Setting | Where | Why |
|---|---|---|
alloc_big_align on |
game.tsv |
without it a thread's ring heap kept base 0 and the game wrote to 0x268 right after the title screen; see Memory |
mem_used 0x5c00000 |
game.tsv |
the rewritten modules are 92 MB, larger than the default 64 MB of used memory |
4 GB layout: mem_reserve 0x100000000, mem_alias 0x10000000, mem_stack 0x10000000, app_pool 0xCD500000 |
game.tsv |
256 MB in front + 256 MB alias region + 256 MB stack region + 3.25 GB heap region, which holds the 3.12 GB heap |
| Node area cut 1 GB → 62 MB | dm/tools/sdk_patches.py, sdk build 12f16103… (sdk+0x134778 / +0x13480c) |
registered for Brain Memory Training Academy, which has virtual address memory. This game has none and never allocates an address region, so it never needed the cut; it runs the same with it |
No patches.py, no pad_layout (the iPhone pad starts in the 2D layout).
Memory
- The NPDM declares no system resource (META +0x14 = 0): the game uses
SetHeapSizewith pool − used, as 13 Sentinels does, a 3.12 GB heap.main's bss is 30 MB; the rewritten modules are 92 MB (mem_used). - Resident: about 2.9 GB in the opening scene on the Mac (development notes;
game.tsvsays about 3.0 GB in the opening story scene). On an iPhone (A20 Pro): a 2.88 GB footprint with at least 3.2 GB of headroom. - Resolved: a write to
0x268right after the title screen (main+0x5d6708, a constructor whosethisis0x200).operator new(main+0x5a55d4) takes the thread's ring heap, a bump allocator whose cursor starts at its base. One ring heap kept base 0, as in 13 Sentinels: the engine cuts ring heaps out of a memory pool at 64 KB GPU alignment, and the host's pool storage from malloc is only 4 KB aligned. The first allocation returned 0, whichoperator new's null check skipped, and the next returned0x200. Withalloc_big_align(allocations of 64 KB or more aligned to 64 KB) the game plays from the title screen into its opening story scene.
Frame rate
- 60 fps. The game sets present interval 1; on the iPhone the frame time is 16.7 ms at the median.
Audio and services
Each found by one boot (Hardware Opus decoders built on the SDK's software decoders, Cryptographically random bytes):
- Resolved: abort in
sm::Initializeundernn::codec::GetHardwareOpusMultiStreamDecoderWorkBufferSize(int, int, int, int, int)(atnnMain, CRI's Opus streams). The host now has the C interface of the hardware multi-stream decoder, including the SDK 12+ overload with an option argument (Age of Calamity only needed the class and the four-argument size). Each call goes to thesdk's softwareOpusMultiStreamDecoderC interface, with a host-held state per game object as for the single-stream decoder (hle_codec.c).GetHardwareOpusMultiStreamDecoderProcessingTimereports 0. - Resolved: abort in
sm::Initializeundernn::crypto::GenerateCryptographicallyRandomBytes(std::random_device→ thesdk's owngetentropy→InitializeCsrng). Thesdkdefinesgetentropy, so the host'sgetentropyhook never sees the call, but the call intonn::cryptogoes through thesdk's PLT. AnHLE_FORCEon it fills from the host's entropy stream (hle_os.c).
Files and shaders
- All data is in 7 CRI CPK archives (
Unicorn.CPK, the profile'sromfs_marker,UnicornPatch.CPKand one per language). Audio is CRI ADX2 with Opus streams (nn::codec's hardware multi-stream decoder, replaced by the SDK's software one). - Both DLC NSPs (indices 1 and 2) are licences only: their RomFS holds just the SDK's placeholder file.
- Shaders: not in RomFS. 2,188 GLSLC compiler outputs (magic
0x19866891) compiled intomain's read-only segment, 1,725 distinct (614 vertex, 1,111 pixel), read byshader_split.pyglslc_shaders. Single-stage programs, combined per draw. Every binding decodes.
Input
-
The game reads a USB mouse and no touch screen, as OCTOPATH TRAVELER 0 and Tears of the Kingdom do; a rule that turns touches into a mouse would need testing on it (ROMANCE OF THE THREE KINGDOMS 8 REMAKE).
-
Rumble:
mainsends each player's vibration every frame (twohid::SendVibrationValuecalls per player; Asian releasemain9c3116f0…at+0x5ead8c) from fields of its pad object that only the pad reset writes (amplitude 0, 160 / 320 Hz), so that path never vibrates. CRI's audio-driven vibration (hid::VibrationConverter, behind the sound renderer type 6 that CRI registers at startup,+0x790b60) never opened in the opening scene nor in a battle on the Mac. The game has no rumble: on the Switch it does not vibrate either. With a DualSense its (zero) values reach the controller since Vibration handles before a controller is reported.
Mac routes
DM_KEYS=f2400:A*20/300goes from the title screen (about frame 2,500) through a new game into the opening story scene (about frame 4,900).
Found on this game
General host changes first needed here; each applies to every game:
alloc_big_alignwas first needed on 13 Sentinels (its note, the profile keys).- The hardware multi-stream Opus decoder's C interface forwarded to the SDK's software decoder (
hle_codec.c) andGenerateCryptographicallyRandomBytesunder thesdk's owngetentropy(HLE_FORCE,hle_os.c): Hardware Opus decoders built on the SDK's software decoders, Cryptographically random bytes. - A 5 ms device buffer on the Mac (
hle_audio.c), maintained across output-device and sample-rate changes: The Mac's default I/O buffer records the underrun measurements, host validation and remaining checks. - Vibration handles answered before a controller's haptics are routed (
hle_hid.c): the game asks for them once at startup, before GameController reports the pad, and every value went to handle 0 (seen in the log; the game has no rumble, so nothing changed in play) (Vibration handles before a controller is reported).
Open
- Resident memory in the opening scene: about 2.9 GB in the development notes, about 3.0 GB in
game.tsv. game.tsvstill describes the game as "In bring-up" (the Mac into the opening story scene), while AGENTS.md lists it as playable on the iPhone.game.tsv: only the merged 1.05 copy is extracted; the base game's own build IDs are added when needed.