Skip to content
NX Native

Absolum

.NET (FNA, MonoGame)0100A83022A22000
Absolum feature illustration: original artwork for the nxnative notes
Title ID
0100A83022A22000
Tested build
1.1.2
Nintendo SDK
18.3.2
Engine
FNA / .NET NativeAOT (FNA3D's NVN driver)
Graphics API
NVN

Build this game

Keep [0100A83022A22000] in your file names, put the base game, update and DLC in one folder and run the script. Follow the build guide, then the sideloading guide. Needs an iPhone 15 Pro or newer, or an M1 iPad or later.

Technical notes

The project's own record of bringing this game up: what its profile needs and why, what was found and what is still open.

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'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
game_env DOTNET_GCRegionRange=60000000 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).

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).
  • 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). 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) 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).

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:

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).

These notes are republished from the nxnative repository under its MIT license (license and attribution). The original, which may be newer, is the game's notes on GitHub. This site hosts no game files.

More .NET (FNA, MonoGame) games