# Absolum (`0100A83022A22000`) Update 1.1.2 (v458752) over the base game (v0), SDK 18.3.2. FNA (XNA 4 reimplemented in C#) compiled with .NET NativeAOT (NativeAOT-NX; its own GC, regions enabled), SDL3, FNA3D's NVN driver (`FNA3D_Driver_NVN.cpp`), Wwise. Modules `main`, `subsdk2` (the movie module, for FNA3D's video player: H.264 logos) and `sdk`; NVIDIA's Vulkan and OpenGL drivers (`subsdk0`, `subsdk1`) are not loaded. Epic Online Services comes as an NRO the host does not load; the game plays offline without it. Playable on an iPhone. Status: brought up on 1.1.2 on the Mac: the logo movie, the licence agreement, the main menu, the campfire character select, the `WOF_Intro` intro movie and into the first level (the story's intro blurbs past, no error to frame 17,400 of a 300 s scripted route), at 60 fps with real sound and saves; about 2.5 GB resident (2531 MB at frame 14,700). ## What it needs | Setting | Where | Why | |---|---|---| | malloc's address space 40 GB → 2.5 GB | the host's StandardAllocator cap (`hle_svc.c`), every build, with [`game.tsv`](game.tsv)'s `stdalloc_cap 0xa0000000` | malloc's StandardAllocator takes 40 GB of address space; 2.19 GB is used into the first level, and the GC's 1.5 GB region comes after it; see [Memory](#memory) | | `game_env DOTNET_GCRegionRange=60000000` | [`game.tsv`](game.tsv), `hle_os.c` | the .NET GC's region range 1.5 GB instead of 5 × its hard limit (3.75 GB) | | Node area cut 1 GB → 62 MB (`sdk+0x13be4c` / `+0x13bee0`) and 2 MB alignment for regions of 4 MB and more, as SDK 20 (`sdk+0x13c034`, `+0x13c03c`, `+0x13c040`) | `dm/tools/sdk_patches.py` SDK-wide rules, `sdk` build `f118ae1c…` | the 1 GB alignment lost up to 1 GB before each of the two large regions | | Engage's 5 GB block: `mem_reserve 0x140000000`, `mem_alias 0x118000000`, `mem_stack 0x10000000`, `app_pool 0xCD500000` | `game.tsv` | 256 MB in front + 4.375 GB alias region + 256 MB stack region + 128 MB heap region (never used) | No `pad_layout` key: the iPhone gamepad starts in the 2D layout ([README](../../../README.md)). ## Memory - The NPDM declares 16 MB of system resource (virtual address memory), and `main` never calls `SetMemoryHeapSize`: everything lives in address regions of the alias region. `profile_guess.py` gives every game with an NPDM system resource the 5 GB block, and this game came out right with it ([Unity 6 takes its memory with no StandardAllocator and no heap](../../../docs/reference/memory.md#unity-6-takes-its-memory-with-no-standardallocator-and-no-heap)). - **Resolved: `nn::diag` abort at `sdk+0x12dad0` in `nninitStartup`, called from `main+0x756310` (1.1.1: `+0x7562d0`).** That is malloc's `StandardAllocator::Initialize(nullptr, 0xa00000000)`: 40 GB of address space (`main+0x28c084`; 1.1.1 `main+0x28bff4`). `nninitStartup` is at `main+0x1b54d0` in both builds. - How the needs were measured: one boot on the Mac with a 64 GB alias region (`mem_reserve 0x1030000000`, `mem_alias 0x1008000000`), probing `VammManager::AllocateAddressRegion`'s success path (`sdk+0x13bf8c`: x21 size, x22 address) with `DM_SVC=1`'s map log. The regions, from boot into the first level: - malloc's: 2.19 GB used, 1.75 GB of it FNA3D's GPU heap, allocated whole at `FNA3D_CreateDevice` (`main+0xc03660` → `main+0x563370`); its blocks reach 332 MB from the region's start and 1.82 GB from its end, and no more after the title. The cut gives it 2.5 GB, 300 MB to spare. - the .NET GC's region range: 5 × its hard limit (768 MB = 75% of the 1 GB its `GetPhysicalMemoryLimit`, `main+0x28c08c` in 1.1.2, `main+0x28bffc` in 1.1.1, reports), 3.75 GB. The GC reads `DOTNET_GCRegionRange` through `getenv` first; the host answers `getenv` from the profile key `game_env` (`hle_os.c`; every other name goes to the SDK's own `getenv`, an empty environment), which sets 1.5 GB. The GC then uses its range from both ends, 756 MB from the start and 748 MB from the end (718 MB mapped into the first level), where it used only the first 1.14 GB of 3.75. - a few small regions (bookkeeping, thread-local blocks), under 5 MB. - **Resolved: the regions did not fit even then.** SDK 18's `AllocateAddressRegion` (`sdk+0x13bfd0`) aligns a region by its size: 64 KB under 2 MB, 2 MB under 4 MB, 4 MB under 32 MB, 32 MB under 1 GB, else 1 GB in absolute address (`+0x13c03c`), and places it first-fit after the regions already there. The two large regions, with a 4 KB one between them, lost up to 1 GB each to alignment, depending on where `mmap` put the guest block (measured with a 64 GB alias region: 0.6 GB before malloc's at that run's alias base, 1 GB before the GC's). A cut on that build aligns them to 32 MB as well (what the SDK gives regions from 32 MB up; nothing in the SDK or the game reads the region's alignment), beside the node-area cut. The `sdk`'s AddressRegionManager is at the offsets of Super Mario Galaxy's SDK 18.3.5 build. - **Current: the host's StandardAllocator cap with `stdalloc_cap 0xa0000000`, for every build; the `main` cut at `main+0x28bff4` and the profile's `patches.py` are gone** ([StandardAllocators over 12 to 62 GB of address space](../../../docs/reference/memory.md#standardallocators-over-12-to-62-gb-of-address-space)). 1.1.2 has the same `sdk` build; the contributor tested it on an iPhone 15 Pro Max with a 2.5 GB `main` cut, which the cap now gives the SDK; a 1.1.2 gameplay run with only the cap is open (no 1.1.2 files on this Mac). Checked on 1.1.1 with the route into the first level, walking and striking there (320 s, frame 18,600), headless from a fresh save with the same keys, against the same build with the cut: the same frames reached, `[RED]` kinds and alias region use, screenshots of the same scenes (only animation and fade timing differ). Before, with `main` rewritten without its cut (another build ID): the default cap gave malloc 4.06 GB and the GC's region then had no room (`nn::diag` abort at `sdk+0x13a684`); with the key malloc got 2.5 GB, the GC its 1.5 GB at alias +2.59 GB, and the game ran 75 s headless to frame 3,900. The NSP script's region check first computed `0xae000000` (2.72 GB) from a 64 GB boot, leaving 64 MB for regions made later; that value plays too: the same route on 1.1.1 with `main` uncut, one host built with each value from the same source (2026-10-28, 320 s headless): both reach frame 18,600 with no `[RED]` line, the same screenshots and 2.73 GB mapped; with 2.72 GB the GC's region is at alias +2.81 GB and the regions reach +4.31 GB of the 4.38 GB alias region (+4.09 GB with 2.5 GB), so it leaves less room for regions made later in play. The check now leaves the host's 256 MB for those and sets the value in a provisional or derived profile: a provisional pack of 1.1.1 (2026-10-28) set `0xa2000000` (2.53 GB), between the two, once its profile had this one's `game_env` (without it the GC's region is 3.75 GB and the check reports that `malloc`'s region has no room). This profile keeps 2.5 GB, the size the iPhone run had. (The earlier runs used an extraction made from the NSPs in the work folder; the NSP script's own was pruned then.) - **Current:** the regions end 4.13 GB into the 4.375 GB alias region of Engage's 5 GB block. The mapped total peaks at 2.9 GB, under the hardware pool. ## Rendering The effects (`.xnb`) are XNA's D3D9 effects (MOJOSHADER is linked in and parses them for the parameters). Every binding decodes with the usual layout. - **Resolved: the whole picture upside down** (the title logo and "Press Any Button" mirrored top to bottom). The game keeps NVN's lower-left window origin (handled as for Link's Awakening, [01006BB00C6F0000](../01006BB00C6F0000/README.md)) and, twice a frame, sets viewport 0's swizzle with `nvnCommandBufferSetViewportSwizzles` to (x, −y, z, w), which negates clip-space y: how a renderer with OpenGL heritage flips its picture. The host had it as a red-reporting stub. It is recorded now (`OP_VP_SWIZZLE`) and applied at the draw as a viewport flipped about its centre, the same mapping. Facing is decided after the swizzle on the GPU as in Metal, so the winding stays. Other swizzles (axis exchanges, negated x / z / w) give a red line and are drawn unswizzled. - **Resolved: the logo movie as a strip of small grey copies at the top of the screen** (FNA3D's `nx_video_player.cpp`: type 1, output 3, mode 2). The texture held NV12 bytes: in RGBA8, 405 rows of 1920 pixels are exactly a 1920×1080 NV12 frame, rows 0–270 the luma at video black (16) and rows 270–405 the chroma (128). This game's MP4 demuxer hands the parameter sets to `Configure` (`csd-0` / `csd-1`), which made the H.264 decoder there without switching it to RGBA, and the first packet then found it made. Both paths now go through `md_avc_open` (`hle_movie.c`). ## Files and shaders - The shaders the GPU runs are 163 GLSLC outputs in `rom:/shaders.nx.bin` (a u32 count, then per entry a u32 hash, a u32 size and one whole output), which `shader_split.py` reads as a loose file no other reader takes: 162 unique shaders, 83 pixel and 79 vertex, every vertex shader paired with the single-stage cover. Groups `fna` and `data` (`shader_groups`). - `romfs_marker`: `data/textures`. ## Code - **Resolved: `RuntimeError: 788 ldr x18, #0xd1eb0` in `patch_nso.py`.** The text segment starts with 1.7 MB of data (`main+0x0`–`+0x1b5000`, high-entropy, no FDE covers it, and no `adrp` or relocation points into it): 62% of its words do not decode, and 6,858 of the rest read as instructions that use `x18`, plus three "`svc`" words with numbers such as `#0x91e7`. The linear sweep rewrote them all. `dm/tools/nso_code.py` finds such data blocks (a stretch of at least 4 KB between the `.eh_frame` FDE ranges in which more than a quarter of the words do not decode); the sweep skips them (`DATA_BLOCKS`, logged as `data blocks left alone`), and so does `insn_census.py`, whose counts then match the rewriter's again (11,744 `x18` sites in this `main`). Code without unwind information is still swept: it decodes (zlib at `main+0x75261c`, libunwind's register restore at `main+0x28dbd4`). - Padding halfword jump tables only to their largest entry (`half_count`) changed one to six tables in this `main`, as in a few other games' ([Halfword jump table landing pads sized to the largest entry](../../../docs/reference/rewriter.md#halfword-jump-table-landing-pads-sized-to-the-largest-entry)). ## Services - **Resolved: SIGSEGV in `vi::CreateLayer(Layer**, Display*, int, int)`** (`sdk+0x3f9bc4`, from SDL3's Switch video driver at `main+0x239304`): the overload with the layer's size; the host had only the two-argument one. It returns the static layer (`hle_vi.c`). The survey's import table had listed every vi call as unhooked: `nso_syms.py host_hooks` read only `dm/host`, and the vi hooks are in `dm/gfx` (it reads `dm/gfx` and `dm/gfx/nvn` now). - **AudioIn** (SDL3's audio driver lists the input devices when its audio starts): the host has no microphone. `ListAudioIns` reports none, both opens fail, and the calls that need an open AudioIn answer without touching the game's object (`hle_audio.c`). - **Epic Online Services** in an NRO (`rom:/nro/EOSSDK-Switch-Shipping.nro`, loaded with `ro::LoadModule`; `main` imports the `EOS_*` functions). Before the host loaded NROs, the imports stayed `UNIMPL` stubs and `EOS_Platform_Create` handed back a garbage handle; the game checks it (`[EOS] Failed to create platform`) and plays offline. **Current:** the host never loads an online SDK: an NRO that exports `EOS_Platform_Create` is left out of the pack (`dm/tools/nro_collect.py` `ONLINE_SDKS`, a `refused` row in `nro.tsv`) and `ro::LoadModule` refuses it with one log line (`refused …: an online SDK (Epic Online Services), not loaded`). Offline, local saves. - **The modules:** `main` imports EGL (SDL3's GL support, the same 13 EGL functions as Digimon Story Cyber Sleuth) and `vkGetInstanceProcAddr` (its Vulkan support), but FNA3D renders with NVN and names no GL function. NVIDIA's OpenGL and Vulkan drivers program the GPU through NvRm, which the host does not have, so they are never loaded (`profile_guess.py` `GPU_DRIVERS`); the EGL imports stay unbound stubs that nothing calls. ## Mac routes - A fresh save reaches the first level at about frame 9,000 with `DM_KEYS="f1500:A,f1900:A,f2300:A,f2700:A,f3000:A,f3300:A,f3600:A,f4000:A,f4400:A,f4800:A,f5400:A*30/150"` (any button, accept the licence, New game, the character select, then A through the intro and the first dialogue); `LRight` walks, Y strikes and X uses the skill. ## Found on this game General host changes first needed here; each applies to every game: - **Data blocks at the start of the text left alone** (`nso_code.py`, `DATA_BLOCKS`): [Data blocks at the start of the text](../../../docs/reference/rewriter.md#data-blocks-at-the-start-of-the-text). - **`getenv` answered from the profile** (`game_env`, `hle_os.c`) and **the 32 MB alignment cut for large regions on SDK 18**, later taken by Paper Mario: The Thousand-Year Door's and Celeste's SDK 15.3.2 build: [.NET NativeAOT malloc and the GC region range](../../../docs/reference/memory.md#net-nativeaot-malloc-and-the-gc-region-range), [1 GB alignment of regions of 1 GB and more](../../../docs/reference/memory.md#1-gb-alignment-of-regions-of-1-gb-and-more). - **`vi::CreateLayer` with a size**, **AudioIn absent**, **online SDK NROs refused** (`ONLINE_SDKS`), **`nso_syms.py host_hooks` reading `dm/gfx`**, **NVIDIA's GPU drivers never loaded** (`GPU_DRIVERS`): [`vi::CreateLayer` with the layer's size](../../../docs/reference/services.md#vicreatelayer-with-the-layers-size), [No audio input devices](../../../docs/reference/audio.md#no-audio-input-devices), [Online SDKs in NROs are never loaded](../../../docs/reference/services.md#online-sdks-in-nros-are-never-loaded), [Listing a game's unhooked imports before they are met](../../../docs/reference/services.md#listing-a-games-unhooked-imports-before-they-are-met), [GPU driver modules that are never loaded](../../../docs/reference/services.md#gpu-driver-modules-that-are-never-loaded). - **H.264 decoders made at `Configure` switched to RGBA** (`md_avc_open`): [Parameter sets in `Configure` made the H.264 decoder without RGBA](../../../docs/reference/movies.md#parameter-sets-in-configure-made-the-h264-decoder-without-rgba). - **Viewport swizzles** (`OP_VP_SWIZZLE`): [Viewport swizzles applied as a flipped viewport](../../../docs/reference/rendering.md#viewport-swizzles-applied-as-a-flipped-viewport). - **GLSLC outputs in loose RomFS files**: [GLSLC outputs in main and in loose files](../../../docs/reference/shaders.md#glslc-outputs-in-main-and-in-loose-files). - **The host's StandardAllocator cap and the `stdalloc_cap` key** (`hle_svc.c`; the region check's advice): [StandardAllocators over 12 to 62 GB of address space](../../../docs/reference/memory.md#standardallocators-over-12-to-62-gb-of-address-space). ## Open - Shader counts differ: development.md says 162 GLSLC outputs in `shaders.nx.bin`; 04 and `dm/games/README.md` say 163 outputs (162 unique). - Loading online SDKs: always load, refuse them (as now), or decide per profile is an open question for the maintainer ([Online SDKs in NROs are never loaded](../../../docs/reference/services.md#online-sdks-in-nros-are-never-loaded)).