Files
nxnative-easy/dm/games/010069401ADB8000
seeddsandparsifal295 40b643bbe6 Keep Mac audio buffers near 5 ms across output changes
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>
2026-10-27 00:41:13 +08:00
..

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 SetHeapSize with 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.tsv says 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 0x268 right after the title screen (main+0x5d6708, a constructor whose this is 0x200). 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, which operator new's null check skipped, and the next returned 0x200. With alloc_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::Initialize under nn::codec::GetHardwareOpusMultiStreamDecoderWorkBufferSize(int, int, int, int, int) (at nnMain, 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 the sdk's software OpusMultiStreamDecoder C interface, with a host-held state per game object as for the single-stream decoder (hle_codec.c). GetHardwareOpusMultiStreamDecoderProcessingTime reports 0.
  • Resolved: abort in sm::Initialize under nn::crypto::GenerateCryptographicallyRandomBytes (std::random_device → the sdk's own getentropy → InitializeCsrng). The sdk defines getentropy, so the host's getentropy hook never sees the call, but the call into nn::crypto goes through the sdk's PLT. An HLE_FORCE on 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's romfs_marker, UnicornPatch.CPK and 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 into main's read-only segment, 1,725 distinct (614 vertex, 1,111 pixel), read by shader_split.py glslc_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: main sends each player's vibration every frame (two hid::SendVibrationValue calls per player; Asian release main 9c3116f0… 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/300 goes 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:

Open

  • Resident memory in the opening scene: about 2.9 GB in the development notes, about 3.0 GB in game.tsv.
  • game.tsv still 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.