• Welcome to Smashboards, the world's largest Super Smash Brothers community! Over 250,000 Smash Bros. fans from around the world have come to discuss these great games in over 19 million posts!

    You are currently viewing our boards as a visitor. Click here to sign up right now and start on your path in the Smash community!

Completed Heap Logger Tools

Punkline

Dr. Frankenstack
Joined
May 15, 2015
Messages
423
This code contains a query tool that can be used to check if a given address is part of any actively allocated fragments in the game’s OS memory heap. It can be used to identify the base address of allocations, like loaded files, HSD Objects, GObj data tables; basically anything you see that the game instantiates in RAM. It’s like a much more broadly applicable version of my DAT File Offset Finder code, and should be very useful when trying to learn more about unknown data structures encountered in Melee.




The code comes with a few layers of optional tools that you may install through injection codes, and use from either from a programmable logger code or quick memory edits done in Debug Dolphin. The code includes several functions that help make it easy to program your own logger codes, and comes with 2 example plugin loggers in addition to a set of core loggers. Some of the functions in these modules can be used without installing any injections -- making them generally available to any other code in your MCM library.


The core loggers are installed by the 'Heap Logger' code, and display the following information when triggered:
  • Memory Editor Query Interface Logger - prints a message every time a user input is discovered in the global input field created at static RAM address 804DAA84.
  • Arena Logger - prints messages every time the OS Arena functions are used to read/write to arena high/low. These are usually limited to just the beginning of the game, during boot.
  • OSHeap Logger - prints messages every time a Heap is created or destroyed using one of the four available (by default) heap descriptors.
  • Scene Logger - prints messages every time a scene transition happens, storing metadata that can be used to make sub-frame accurate timestamps that are oriented with the starting frame of each new minor scene.

The example plugin loggers can be installed with main code as a prerequisite, and are programmed to display the following information about files that are loaded:
  • PathToEntrynum Logger - prints messages every time the game tries to check if a file of a specific name is on disk through a commonly used DVD system implemented in many HSD library functions.
  • Archive Object Logger - prints messages about loaded DAT or USD (‘Archive’) formats loaded from disk or copied from somewhere else in RAM. These can be seen each time a DAT file is loaded, or when something like a fighter animation is copied on action state changes.


---

The DOL Mod comes with both ‘Codes’ and ‘Function Modules’.

The codes are what you install/uninstall to your game, and the modules just have to be somewhere in your Mods Library to grant usable functions to the codes.
I recommend putting them in different text files, to prevent clutter from the modules.

Install both ‘Code’ and ‘Modules’ somewhere in your MCM Mods Library. Only the 'Codes' then need to be installed to a DOL in order to get the loggers working:

Code:

Code:
    -==-


Heap Logger
Installs injections that help log extra information about heap allocations
Includes "Core Utilities" and "Standalone Functions" modules, enabling default loggers

# See "Core Utilities" module for a list of features installed by this Core
# See "Standalone Functions" module for a list of features that can be used with or without Core

# --- MEMORY EDITOR QUERY:

804DAA84 = INPUT FIELD -- write a query address here to trigger the output next frame

Inputs will be read once per VIRetrace frame.
Use pointer at -0x4F60(rtoc) (804DAA80) to reach Memory Editor Query output data:
OUTPUT TABLE :
  0x00 = (copy of last input)
  0x04 = offset of input from allocation start
  0x08 = POINT to fragment metadata header
  0x0C = POINT to data start
  0x10 = POINT to alloc caller  -- the instruction responsible for making this
  0x14 = POINT to heap descriptor
  0x1A = Byte: Heap ID
  0x1B = Byte: Fragment type : 0=free, 1=allocated, 2=not dynamic
  0x1C = Word: Size of allocation
[Punkline]
Revision ---- DOL Offset ---- Hex to Replace ---------- ASM Code -
NTSC 1.02 --- 0x801A891C ---- C882B0A0 -> C88280A0
------------- 0x801A87AC ---- C862B0A0 -> C86280A0
------------- 0x801A863C ---- C842B0A0 -> C84280A0
------------- 0x804DAA80 ---- 43300000 -> .long <<isFromHeap.output.data>>
------------- 0x804DAA84 ---- 80000000 -> 00000000
# Mytoc Block_177
# -0x4F60(rtoc)  - use this to reference in codes
# 804DAA80       - use this to reference when memory editing
# 0x0 = point to output data
# 0x4 = input field
------------- 0x801A41D0 ---- 987F0003 -> Branch

987F0003
bl <timestamp.scene>
394A0001 B14C0044 90AC0040 992C0046 990C0047
lis r3, <<isFromHeap.loggerSettings>>@h
ori r3, r3, <<isFromHeap.loggerSettings>>@l
88630002 2C030000 4182008C 48000009 48000060 4E800021 202D2D20 5343454E 45205452 414E5349 54494F4E 20255820 3A205649 4672616D 65202523 20782E25 30347820 3A204D61 6A6F7220 25303278 202D3E20 25303278 2C204D69 6E6F7220 25303278 202D3E20 25303278 00000000 7C6802A6 7C862378 7D445378 88EB0000 890B0001 892B0004 894B0003
bl <printf>
bl <printf.newline>
00000000

------------- 0x80344118 ---- 7C072050 -> Branch

7C0802A6 9421FFC0 90010044 39810010 7C6CA5AA
lis r12, <<isFromHeap.globals>> + 0x10 @h
ori r11, r12, <<isFromHeap.globals>> + 0x10 @l
54601838 7CEB016E 810DBD80 910B0004
lis r12, <<isFromHeap.loggerSettings>>@h
ori r12, r12, <<isFromHeap.loggerSettings>>@l
898C0001 2C0C0000 41820084 48000009 48000060 4E800021 202D2D20 4F534865 61705B25 785D203A 20686173 20626565 6E20414C 4C4F4341 54454420 6F6E2056 49467261 6D652025 2320783A 20252320 78206279 74657320 636C6169 6D656420 61742025 78202E2E 2E202578 00000000 7D054378 7CC72050 7C882378 7C641B78 7C6802A6
bl <printf>
bl <printf.newline>
39810010 7C6CA4AA 80010044 38210040 7C0803A6 7C072050 00000000

------------- 0x80344508 ---- 900DA570 -> Branch

814DA570 900DA570 7C090378 39800001 39600000 7CC33378
b <OSAnnounceArena.inj>
00000000

------------- 0x8034452C ---- 906DBCD0 -> Branch

814DBCD0 7C691B78 39800001 39600001 906DBCD0
b <OSAnnounceArena.inj>
00000000

------------- 0x803444E0 ---- 906DA570 -> Branch

814DA570 7C691B78 39800001 39600000 906DA570
b <OSAnnounceArena.inj>
00000000

------------- 0x803444D8 ---- 906DBCD0 -> Branch

814DBCD0 7C691B78 39800001 39600001 906DBCD0
b <OSAnnounceArena.inj>
00000000

------------- 0x803444D0 ---- 806DA570 -> Branch

39800000 39600000 806DA570
b <OSAnnounceArena.inj>
00000000

------------- 0x803444C8 ---- 806DBCD0 -> Branch

39800000 39600001 806DBCD0
b <OSAnnounceArena.inj>
00000000

<OSAnnounceArena.inj> NTSC 1.02
7C0802A6 9421FFC0 90010044 2C0C0000 39010010 7C6885AA 91810020 7C681B78
lis r7, <<isFromHeap.globals>>@h
ori r7, r7, <<isFromHeap.globals>>@l
2C8B0000 41820014 41860008 91270030 40860008 91270008 48000009 48000098 4E800021 202D2D20 4F534172 656E6125 73203A20 68617320 6265656E 20777269 7474656E 20746F20 62792063 616C6C65 72202578 20206D6F 76696E67 2066726F 6D202578 20746F20 25783B20 25232078 20627974 65730020 2D2D204F 53417265 6E612573 203A2068 61732062 65656E20 72656164 20287233 203D2025 78292062 79206361 6C6C6572 20257800 4869004C 6F000000 30A0FFFC 7C6802A6 38830088 7D465378 40820010 38630053 7CA62B78 7D054378 7D274B78 7D0A4850 40860008 38840003
lis r12, <<isFromHeap.loggerSettings>>@h
ori r12, r12, <<isFromHeap.loggerSettings>>@l
898C0000 2C0C0000 41820018
bl <printf>
81810020 2C0C0000 40A10008
bl <printf.newline>
39010010 7C6884AA 80010044 38210040 7C0803A6 4E800020

------------- 0x80344154 ---- 1C03000C -> Branch

7C0802A6 9421FFC0 90010044 90610010
lis r12, <<isFromHeap.loggerSettings>>@h
ori r12, r12, <<isFromHeap.loggerSettings>>@l
898C0001 2C0C0000 418200A4 48000009 48000060 4E800021 202D2D20 4F534865 61705B25 785D203A 20686173 20626565 6E204445 5354524F 59454420 61667465 72202523 20782056 49467261 6D65733A 20252320 78206279 74657320 66726565 64206174 20202578 202E2E2E 20257800
lis r12, <<isFromHeap.globals>> + 0x10 @h
ori r12, r12, <<isFromHeap.globals>> + 0x10 @l
54601838 7CEC006E 1C03000C 80AC0004 7C641B78 806DBCC0 7CC3002E 7D073214 806DBD80 7CA51850 7C6802A6
bl <printf>
bl <printf.newline>
81810044 7D8803A6 80610010 38210040 1C03000C 00000000

------------- 0x80343F24 ---- 28060000 -> Branch

7C0802A6 9421FFC0 90010044 39810010 7C6C05AA
lis r0, <<isFromHeap.callerFilter>>@h
ori r3, r0, <<isFromHeap.callerFilter>>@l
bl <callerFilter>
80C1001C 90660014
bl <timestamp.scene>
7CA62850 80C1001C 90A60018 9086001C 38000000 9006000C 90060010 39810010 7C6C04AA 80010044 38210040 7C0803A6 28060000 00000000

------------- 0x8034EA28 ---- 38610018 -> Branch

lis r12, <<isFromHeap.loggerSettings>>@h
ori r12, r12, <<isFromHeap.loggerSettings>>@l
898C0003 2C0C0000 4182000C 8062B0A4
bl <isFromHeap.printf>
38000000 9002B0A4
bl <timestamp.scene>
908C0048 7C6C42E6 906C004C 38610018 00000000


    -==-


Heap Logger - PathToEntrynum Logger
Prints the argument string, the string location, and the caller of this function; every call
- uses the HSD filename buffer function as a means of capuring static string arguments when possible
Edit the <PathToEntrynum.callerFilter> table to skip over sampling callers from known functions
Edit the <PathToEntrynum.loggerSettings> table to mute PathToEntrynum logger features
[Punkline]
Revision ---- DOL Offset ---- Hex to Replace ---------- ASM Code -
<PathToEntrynum.loggerSettings> NTSC 1.02
# '00' bytes are disabled;  else = enabled
01  # --- PATHTOENTRYNUM LOGGER - prints a message every time a file name entrynumber is looked up
01  # --- SAMPLE BUFFER ARGUMENTS - logger uses memory of 0x80432058 buffer arg for path string addr
0000     # - padding
00000000 # - (space reserved for buffer)

<PathToEntrynum.callerFilter> NTSC 1.02
800186d0
800181b8 # unknown DAT index HSD heap funcs
00000000

NTSC 1.02 --- 0x80016220 ---- 3C80803C -> Branch

lis r0, <<PathToEntrynum.loggerSettings>>@h
ori r4, r0, <<PathToEntrynum.loggerSettings>>@l
93440004 3C80803C 00000000

------------- 0x80337C4C ---- BA810018 -> Branch

lis r0, <<PathToEntrynum.loggerSettings>>@h
ori r29, r0, <<PathToEntrynum.loggerSettings>>@l
7C7E1B78
lis r0, <<PathToEntrynum.callerFilter>>@h
ori r3, r0, <<PathToEntrynum.callerFilter>>@l
bl <timestamp.scene>
7E862850 7C952378 7CF63B78
bl <callerFilter>
48000009 48000058 4E800021 20506174 68546F45 6E747279 6E756D3A 206F6E20 4672616D 65202523 20782E25 30347820 5363656E 65202530 3578203A 20257820 67657473 2072333D 20252320 30367820 3C2D2025 78202D20 25730000 881D0000 2C000000 41820048 7E84A378 7EA5AB78 7EC6B378 7C671B78 7C6802A6 7FC8F378 7F29CB78 3C008043 60002058 7F2ACB78 7C004800 40820014 881D0001 2C000000 41820008 813D0004
bl <printf>
7FC3F378 BA810018 00000000


    -==-


Heap Logger - Archive Object Logger
Invokes <isFromHeap.printf> each time a DAT file is loaded
Requires Heap Fragment Offset Finder
Edit the <ArchiveObject.loggerSettings> table to mute PathToEntrynum logger features
[Punkline]
Revision ---- DOL Offset ---- Hex to Replace ---------- ASM Code -
<ArchiveObject.loggerSettings> NTSC 1.02
# Archive Logger flags can be edited below  using False=0,  True=1
# flags are in nibbles on little end, to fit discretely in CR:
0000 # padding
1 # --- ARCHIVE OBJECT LOGGER    - logs information about archives (dat files) as they are loaded
1 # --- VERBOSE HEADER LOG       - logs details extracted from the archive header and heap fragment
1 # --- LOG COPIED ARCHIVES ONCE - logs copied archives (like action state animations) only once
# - prevents archive copies from flooding the logger by only announcing it once per allocation
#   - this will prevent the logger from seeing things like action state changes, beyond the first
1 # --- VERBOSE HEADER ONCE      - prevents serial verbose header logs from the same allocation
# - prevents verbose copies from flooding the logger with multiple lines every action state
#   - if logging all archive copy parses, then each copy only shows verbose lines on the first use

<ArchiveObject.callerFilter> NTSC 1.02
80069d14
80069cf4
80085dec # these are related to action state changes
00000000


# --- Injection code:

NTSC 1.02 --- 0x8038033C ---- 8001002C -> Branch

7C000026 90610018 90010014
lis r0, <<ArchiveObject.loggerSettings>>@h
ori r12, r0, <<ArchiveObject.loggerSettings>>@l
818C0000 7D80F120 4093037C 7FE3FB78
bl <isFromHeap>
7CFF3B78 2D040001 38000000 41890010 A01F000C 39200DA7 B13F000C 2D800DA7 4D8ED902 4FFF7202 4DB7FA02 815E0000 394A003F 39600000 41890008 817F0008 55400034 7E0B0000 4D8C9102
lis r0, <<ArchiveObject.callerFilter>>@h
ori r3, r0, <<ArchiveObject.callerFilter>>@l
bl <callerFilter>
38000000 90610010
bl <timestamp.scene>
4800000D 7FA802A6 480001C8 4E800021 20415243 48495645 2046494C 45202020 20203A20 25387820 3A202573 2C202523 20782062 79746573 20617420 2578203A 20467261 6D652025 2320782E 25303478 206F6620 5363656E 65202530 35780020 2D204152 43484956 45204441 54412020 203A2025 3878203A 20252320 78206279 74657300 202D2041 52434849 56452052 454C4F43 53203A20 25387820 3A202564 20706F69 6E746572 28732900 202D2041 52434849 5645204E 4F444553 20203A20 25387820 3A202564 2073796D 626F6C28 7329206E 6F646573 00202D20 41524348 49564520 45585445 524E203A 20253878 203A2025 64206578 7465726E 616C2073 796D626F 6C287329 00202D20 41524348 49564520 53594D53 5452203A 20253878 203A2025 23207820 62797465 20666F6F 74657200 20415243 48495645 20434F50 59202020 20203A20 25387820 3A202523 20782062 79746573 206F6620 25782028 616C6C6F 63202578 202B2025 7829203A 20467261 6D652025 2320782E 25303478 00202D20 50415253 45204341 4C4C4552 2020203A 20253878 203A2072 65636965 76657320 41726368 69766520 25782025 73002870 61727420 6F662074 68652072 756E7469 6D652073 7461636B 29000000 40B2002C 7D062850 7C892378 7CEA3B78 387D0000 3C008043 60052058 80FE0040 7FC4F378 80DE0000
bl <printf>
408C005C 7D262850 7C8A2378 387D0124 80DE0040 38FF0020 7FC4F378 80BE0000 40A90034 38E00000 39000008 3508FFFF 41800024 55001838 7C0C002E 7C060040 4180FFEC 7C063840 4180FFE4 7C070378 4BFFFFDC 7D073050
bl <printf>
418D00B4 387D004F 80BE0004 809E0020
bl <printf>
387D0074 80BE0008 2C050000 809E0024 40810008
bl <printf>
387D009C 80BE000C 2C050000 809E0028 40810008
bl <printf>
387D00C9 80BE0010 2C050000 809E002C 40810008
bl <printf>
387D00F9 801E0040 80BE0000 809E0030 7CA50214 7CA42850 2C050000 40810008
bl <printf>
387D0171 80810010 7FC5F378 3C00804F 38DD01BD 7C050000 40800010 7C050800 40810008 38DD01A2
bl <printf>
807E0040
bl <isFromHeap.printf>
80010014 80610018 7C0FF120 8001002C 00000000
#

Function Modules:

Code:
    -==-


Heap Logger - Standalone Functions
Standalone utilities used by Heap Logger that do not require injections:


# --- HEAP TOOLS:

<alloc> creates a heap allocation in the topmost heap (while preserving volatile registers)
Arguments:   rSize = 3
Returns:     rAllocation = 3; # r4...r12 are restored -- for easy use inside of injections

<alloc.r12> is a variation of alloc that uses r12 as its argument/return register
# May be useful for creating allocations while r3 is in use by injection context

<alloc.free> is a shortcut to HSD_Free
Arguments:   rAlloc = 3

<isFromHeap> checks if a given RAM address belongs to a any heap fragments defined by OS
Arguments:   rQuery = 3
Returns:     rQuery=3; rType=4; rOffset=5; rAlloc=6; rMeta=7; rDesc=8; rDescID=9
# Type = memory type
#  - 0 = Free;   1 = Allocated;   2 = (not currently in an active heap)
# Offset = offset of given query from allocation start
# Meta = 0x20 bytes of header metadata for all heap fragments
#  - 0x8(rMeta) = alloc + meta size
# Desc = 0xC bytes of header information about this heap (of IDs 0...3)
#  - 0x0(rDesc) = heap size;   0x4 = Free Fragment;   0x8 = Allocated


# --- TIME SAMPLER:

<timestamp> returns a 64-bit timestamp for you to use for logging purposes
Returns:     rTimeHi = 3; rTimeLo = 4
# TimeLo increments every 20 nanoseconds
# TimeHi increments every 85.8993 seconds


# --- CALLSTACK SAMPLER:

<callerFilter> is a leaf that samples the caller of the current stack frame
Arguments:   rFilter = 3  # use a value of 0 to ignore filter
Returns:     rCaller = 3  # the address of the instruction that called this frame
# - filter is a null-terminated array of 4-byte address pointers
#   - each instruction is of a call, like bl or blrl; and will be skipped in sample


# --- PRINTF SHORTCUTS:

<printf> is a shortcut for invoking the NTSC 1.02 printf function, for writing to logger
Arguments: rFormat = 3; bUseFloatArgs = cr1.eq; # varargs = r4 ... r10, f0 ... f8
# search for 'printf' online for details on syntax of format string
# - format strings should start with a space, to properly print in dolphin
# - format strings are null terminated, and are printed on individual lines

<printf.newline> is a variation of printf that takes no args, and prints a blank string
[Punkline]
<alloc> NTSC 1.02
# Allocate from top-most heap, and back up volatile registers

# --- Arguments:
# r3 = alloc size

# --- Returns:
# r3 = allocation;  all other registers are preserved
7C0802A6 9421FF80 90010084 91810010 39810014 7C8C05AA 7C641B78 806DA760
bl 0x80343ef0
39810014 7C8C04AA 81810010 80010084 38210080 7C0803A6 4E800020

<alloc.r12> NTSC 1.02
# Variant of <alloc> uses r12 as argument/return instead of r3
7C0802A6 9421FF80 90010084 90610010 38610014 7C8305AA 7D846378 806DA760
bl 0x80343ef0
38610014 7C8304AA 80610010 80010084 38210080 7C0803A6 4E800020

<alloc.free> NTSC 1.02
# --- Arguments:
# r3 = allocation to free
b 0x8037f1b0  # HSD_Free -- picks topmost heap and invokes OSFreeToHeap


<isFromHeap> NTSC 1.02
# check if query comes from any dynamic heap fragments

# --- Arguments:
# r3 = Query Address

# --- Returns:
# r3 = argument query
# r4 = type    -- 0 = Free;  1 = Allocated;  2 = Not_Dynamic (not in heap sub-arenas)
# r5 = offset  -- offset of argument query from start of fragment allocation
# r6 = alloc   -- the base address of an allocated space for storing data
# r7 = meta    -- the base address of a header for an allocation or free fragment
# r8 = desc    -- the heap descriptor responsible for pointing to this heap
# r9 = descID  -- the index of this heap descriptor
810DBCC0 38000004 38800001 7C0903A6 39080030 8408FFF4 2C000000 41A00044 38A00001 80E80008 7C071800 80070008 7C070214 7C801800 4C450902 41820044 7CE03B78 80E70004 2C070000 4180FFDC 38800000 80E80004 34A5FFFF 4182FFCC 4200FFB4 38800002 38A00000 38C00000 38E00000 39000000 3920FFFF 4E800020 38C70020 7D2902A6 7CA61850 21290003 4E800020

<timestamp> NTSC 1.02
# leaf takes no args, and returns the following identifying timestamp data:
# --- Retrns:
# r3 = TBU: Upper 32-bits of timestamp
# r4 = TBL: Lower 32-bits of timestamp
7C6D42E6 7C8C42E6 7C0D42E6 7C001800 40A2FFF0 4E800020

<callerFilter> NTSC 1.02
# Fetches callers, optionally filtering out known caller instructions
# --- Arguments:
# r3 = filter list  -  null terminated list of exceptions to skip
# - each exception must be the address of the CALLER, not the return address or the function start

# --- Returns:
# r3 = caller
2C830000 3083FFFC 80A10000 7C862378 4800001C 84040004 2C000000 4C800020 7C001800 4082FFF0 80A50000 80650004 3863FFFC 7CC43378 4184FFDC 4E800020

<printf.newline> NTSC 1.02
# no args -- as though calling printf with r3 = point to null
lis r3, <<printf.newline.data>>@h
ori r3, r3, <<printf.newline.data>>@l
b <printf>

<printf.newline.data> NTSC 1.02
00000000

<printf> NTSC 1.02
# Can be used to format a print string for stdout, and displaying in the Dolphin Logger
# --- Arguments:
# r3 = format string, r4...r10 = varargs
# if cr1.eq is true, f0...f8 also = varargs
# - format string must start with a space in order to display in Dolphin Logger properly
b 0x80323eb4


    -==-


Heap Logger - Core Utilities
Installs injections that help log extra information about heap allocations


# --- METADATA:

Injections write extra metadata to OSHeap fragment headers :
  0xC  = 8 bytes are cleared for implementation by users
  0x14 = address of allocation caller (filtered by callerFilter)
  0x18 = VI Retrace frame that allocation was made
  0x1C = tbl timestamp


# --- LOGGERS:

Enables the log announcer for:
- on OSArenaHi/Lo reads/writes
- on OSHeap[n] creation/destruction
- on Scene Transitions
- on Memory Editor Query Inputs

Edit the <isFromHeap.loggerSettings> table to selectively mute core logger features


# --- MEMORY EDITOR QUERY:

Use 804DAA80 or -0x4F60(rtoc) to reach Memory Editor Query IO :
804DAA80 = POINT TO OUTPUT TABLE -- this will display info about your query address
804DAA84 = INPUT FIELD -- write a query address here to trigger the output next frame

OUTPUT TABLE :
  0x00 = (copy of last input)
  0x04 = offset of input from allocation start
  0x08 = POINT to fragment metadata header
  0x0C = POINT to data start
  0x10 = POINT to alloc caller  -- the instruction responsible for making this
  0x14 = POINT to heap descriptor
  0x1A = Byte: Heap ID
  0x1B = Byte: Fragment type : 0=free, 1=allocated, 2=not dynamic
  0x1C = Word: Size of allocation

Edit the <isFromHeap.callerFilter> table to specify callers to ignore when sampling a callstack


# --- HEAP OFFSET FINDER EXTENSIONS:

<isFromHeap.output> is a version of <isFromHeap> that also samples extra metadata
# samples are written to global output table <isFromHeap.output.data>

<isFromHeap.printf> is a log announcer that calls and uses <isFromHeap.output> for logging


# --- TIMESTAMP EXTENSIONS:

<timestamp.scene> is a verbose timestamp that also samples Melee scene and VIRetrace frames
Returns:   rTimeHi=3;rFrameFrac=4;rFrame=5;rSceneFrame=6;rCompID=7;rMinor=8;rMajor=9;rCTR=10;rScene=11;rGlob=12
[Punkline]
<isFromHeap.loggerSettings> NTSC 1.02
# You can select which loggers you want to enable when the core module is installed:
# '00' bytes are disabled;  else = enabled
01  # --- ARENA LOGGER - triggered when the OS Arena functions are invoked
01  # --- HEAP LOGGER  - triggered when an OS Heap is created or destroyed
01  # --- SCENE LOGGER - triggered when the scene function writes a new minor ID
01  # --- MEMORY EDITOR QUERY INTERFACE LOGGER - triggered when a user input is found at 804DAA84

<isFromHeap.callerFilter> NTSC 1.02
# You can filter out unwanted caller samples by adding them here
# - when sampled on allocation, the callstack will reach one extra step when finding these
8037f20c # HSD_MemAlloc call
8037aa38 # HSD_ObjAllocAddFree
8037acbc # HSD_ObjAlloc ... something with IDs

80015c40 # HSD_ ... something with loading files

# <- add additional instruction addresses here


# - add the address of instructions that call OSAllocFromHeap (80343ef0)
#   - you can also add instructions that call functions that call OSAllocFromHeap
#   - each matching address will be skipped in the callstack parse, in favor of the next one
00000000  # leave a null terminator, for the parser



# --- Reserved Data:

<isFromHeap.output.data> NTSC 1.02
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
<isFromHeap.globals> NTSC 1.02
80000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 81800000 00000000 00000000 00000000 00000000 00000000

# --- Functions:``
<isFromHeap.output> NTSC 1.02
# invokes <isFromHeap> to record 0x20 bytes of output data in <isFromHeap.output>
7C0802A6 9421FFE0 90010024 8182B0A0 816C0000 2C030000 40800048
bl <isFromHeap>
2C040001 39400000 39600000 41810010 81470014 81670008 396BFFE0 906C0000 992C001A 90AC0004 90EC0008 914C0010 90CC000C 910C0014 988C001B 916C001C 80010024 38210020 7C0803A6 4E800020
<isFromHeap.printf> NTSC 1.02
# invokes <isFromHeap.output> and prints out the resulting data into dolphin logger
7C0802A6 9421FFC0 90010044 BF610010 2C030000 40A00090
bl <isFromHeap.output>
7C9C2378 7CFD3B78 2C1C0002 7D9F6378 4800008D 7F6802A6 7F63DB78 809F0000 40A2000C
bl <printf>
4800005C 387B0049 2C1C0001 40820034 3BDB009D 7CC73378 7D665B78 7D284B78
bl <printf>
bl <timestamp.scene>
7FC3F378 80BD0018 809D0014 80DD001C
bl <printf>
48000020 387B00F5 2C1C0000 40820018 7FA5EB78 7D264B78 7D675B78
bl <printf>
bl <printf.newline>
BB610010 80010044 38210040 7C0803A6 4E800020 4E800021 202D204E 4F545F44 594E414D 49432020 20203A20 2558203A 20697320 6E6F7420 61207061 7274206F 6620616E 79206163 74697665 204F5348 65617020 66726167 6D656E74 732E2E2E 00202D20 414C4C4F 43415445 44202020 2020203A 20255820 3A206F66 66736574 20252320 78206F66 20612025 2320782D 62797465 20616C6C 6F636174 696F6E20 61742025 7820696E 204F5348 6561705B 2531785D 00202020 20202020 20202020 2D20616C 6C6F6320 77617320 63616C6C 65642062 79207468 6520696E 73747275 6374696F 6E206174 20257820 6F6E2046 72616D65 20252320 782E2530 3478206F 66205363 656E6520 25303578 00202D20 46524545 20465241 474D454E 5420203A 20255820 3A206973 20696E20 66726167 6D656E74 20257820 696E204F 53486561 705B2564 5D2C2061 6E642068 61732025 23207820 72656D61 696E696E 67206279 74657300
<timestamp.scene> NTSC 1.02
# leaf takes no args, and returns the following identifying timestamp data:
# --- returns:
# r3 = TBU: Upper 32-bits of timestamp
# r4 = 16-bit fractional scene frame -- measured from TBL
# r5 = VIRetrace Frame
# r6 = VIRetrace Frame of last Minor Scene Transition
# r7 = 32-bit Scene ID (16-bit r10, 8-bit r8, 8-bit r9)
# r8 = Minor Scene
# r9 = Major Scene
# r10 = Scene Reload Counter -- counts up each time a new minor scene of the same r9 is loaded
# r11 = scene controller base address
# r12 = isFromHeap.globals
lis r0, <<isFromHeap.globals>>@h
ori r12, r0, <<isFromHeap.globals>>@l
3C008047 80ADBD80 600B9D30 80CC0040 88EB0000 890B0003 5107442E A14C0044 5147801E 7C6D42E6 7C8C42E6 7C0D42E6 7C001800 40A2FFF0 800C004C 7C802050 1C841421 5484843E 4E800020
#
Code:

Rich (BB code):
-==- 
  
Heap Logger 
Installs injections that help log extra information about heap allocations 
Includes "Core Utilities" and "Standalone Functions" modules, enabling default loggers 
  
# See "Core Utilities" for a list of features installed by this Core 
# See "Standalone Functions" for a list of features that can be used with or without Core 
  
# --- MEMORY EDITOR QUERY: 
  
804DAA84 = INPUT FIELD -- write a query address here to trigger the output next frame 
Inputs will be read once per VIRetrace frame. 
Use pointer at -0x4F60(rtoc) (804DAA80) to reach Memory Editor Query output data: 
OUTPUT TABLE : 
  0x00 = (copy of last input) 
  0x04 = offset of input from allocation start 
  0x08 = POINT to fragment metadata header 
  0x0C = POINT to data start 
  0x10 = POINT to alloc caller  -- the instruction responsible for making this 
  0x14 = POINT to heap descriptor 
  0x1A = Byte: Heap ID 
  0x1B = Byte: Fragment type : 0=free, 1=allocated, 2=not dynamic 
  0x1C = Word: Size of allocation 
[Punkline] 
  
  
# --- Injections and Overwrites: 
  
NTSC 1.02 ----- 0x801A891C --- C882B0A0 -> C88280A0 
NTSC 1.02 ----- 0x801A87AC --- C862B0A0 -> C86280A0 
NTSC 1.02 ----- 0x801A863C --- C842B0A0 -> C84280A0 
NTSC 1.02 ----- 0x804DAA80 --- 43300000 -> .long <<isFromHeap.output.data>> 
NTSC 1.02 ----- 0x804DAA84 --- 80000000 -> 00000000 
# Mytoc Block_177 
# -0x4F60(rtoc)  - use this to reference in codes 
# 804DAA80       - use this to reference when memory editing 
# 0x0 = point to output data 
# 0x4 = input field 
  
  
NTSC 1.02 --- 801a41d0 ---- 987f0003 -> Branch 
# On Scene Transitions 
  
isFromHeap.xStaticLo       = 0x00 
isFromHeap.xStaticLoFrame  = 0x04 
isFromHeap.xArenaLo        = 0x08 
isFromHeap.xArenaLoFrame   = 0x0C 
isFromHeap.xHeap0          = 0x10 
isFromHeap.xHeap0Frame     = 0x14 
isFromHeap.xHeap1          = 0x18 
isFromHeap.xHeap1Frame     = 0x1C 
isFromHeap.xHeap2          = 0x20 
isFromHeap.xHeap2Frame     = 0x24 
isFromHeap.xHeap3          = 0x28 
isFromHeap.xHeap3Frame     = 0x2C 
isFromHeap.xArenaHi        = 0x30 
isFromHeap.xArenaHiFrame   = 0x34 
isFromHeap.xStaticLo       = 0x38 
isFromHeap.xStaticLoFrame  = 0x3C 
  
isFromHeap.xLastSceneFrame = 0x40 
isFromHeap.xLastSceneCount = 0x44 
isFromHeap.xLastSceneMajor = 0x46 
isFromHeap.xLastSceneMinor = 0x47 
scene.xMajor     = 0x0 
scene.xNextMajor = 0x1 
scene.xLastMajor = 0x2 
scene.xMinor     = 0x3 
scene.xLastMinor = 0x4 
scene.xNextMinor = 0x5 
  
stb r3, 0x03(r31)# original instruction 
bl <timestamp.scene> 
rTimeHi=3;rFrameFrac=4;rFrame=5;rSceneFrame=6;rCompID=7;rMinor=8;rMajor=9;rCTR=10;rScene=11;rGlob=12 
addi rCTR, rCTR, 1 
sth rCTR, isFromHeap.xLastSceneCount(rGlob) 
stw rFrame, isFromHeap.xLastSceneFrame(rGlob) 
stb rMajor, isFromHeap.xLastSceneMajor(rGlob) 
stb rMinor, isFromHeap.xLastSceneMinor(rGlob) 
lis r3, <<isFromHeap.loggerSettings>>@h 
ori r3, r3, <<isFromHeap.loggerSettings>>@l 
lbz r3, 0x2(r3) 
cmpwi r3, 0 
beq- _return # ignore logger if disabled in logger settings 
bl _data 
b _announce_new_scene 
  _data: blrl 
  0: .asciz " -- SCENE TRANSITION %X : VIFrame %\x23\ x.%04x : Major %02x -> %02x, Minor %02x -> %02x" 
  .align 2 
   
_announce_new_scene: 
mflr r3 
mr r6, rFrameFrac 
mr r4, rCTR 
lbz r7,  scene.xMajor(rScene) 
lbz r8,  scene.xNextMajor(rScene) 
lbz r9,  scene.xLastMinor(rScene) 
lbz r10, scene.xMinor(rScene) 
bl <printf> 
bl <printf.newline> 
  
_return: 
.long 0 
  
NTSC 1.02 --- 80344118 ---- 7c072050 -> Branch 
# on OSCreateHeap 
globals.xRetraceFrame = -0x4280 
mflr r0 
stwu sp, -0x40(sp) 
stw  r0,  0x40+4(sp) 
addi r12, sp, 0x10 
stswi r3, r12, 0x14 
  
lis r12, <<isFromHeap.globals>> + 0x10 @h 
ori r11, r12, <<isFromHeap.globals>> + 0x10 @l 
slwi r0, r3, 3 
stwux r7, r11, r0 
lwz r8, globals.xRetraceFrame(r13) 
stw r8, 0x4(r11) 
  
  
lis r12, <<isFromHeap.loggerSettings>>@h 
ori r12, r12, <<isFromHeap.loggerSettings>>@l 
lbz r12, 0x1(r12) 
cmpwi r12, 0 
beq- _return # ignore logger if disabled in logger settings 
  
bl _data 
b _announce_OSCreateHeap 
  _data: blrl 
  0: .asciz " -- OSHeap[%x] : has been ALLOCATED on VIFrame %\x23\ x: %\x23\ x bytes claimed at %x ... %x" 
  .align 2 
   
_announce_OSCreateHeap: 
mr r5, r8 
sub r6, r4, r7 
mr r8, r4 
mr r4, r3 
mflr r3 
bl <printf> 
bl <printf.newline> 
  
_return: 
addi r12, sp, 0x10 
lswi r3, r12, 0x14 
lwz  r0,  0x40+4(sp) 
addi sp, sp, 0x40 
mtlr r0 
sub r0, r4, r7 
.long 0 
  
NTSC 1.02 --- 80344508 ---- 900da570 -> Branch 
# on OSAllocFromArenaLo 
rRW = 12; rHL = 11; rBefore = 10; rAfter = 9 
lwz rBefore, -0x5A90(r13) 
stw r0, -0x5A90(r13) 
mr rAfter, r0 
li rRW, 1 
li rHL, 0 
mr r3, r6 
b <OSAnnounceArena.inj> 
.long 0 
NTSC 1.02 --- 8034452c ---- 906dbcd0 -> Branch 
# on OSAllocFromArenaHi 
rRW = 12; rHL = 11; rBefore = 10; rAfter = 9 
lwz rBefore, -0x4330(r13) 
mr rAfter, r3 
li rRW, 1 
li rHL, 1 
stw r3, -0x4330(r13) 
b <OSAnnounceArena.inj> 
.long 0 
NTSC 1.02 --- 803444e0 ---- 906da570 -> Branch 
# On OSSetArenaLo 
rRW = 12; rHL = 11; rBefore = 10; rAfter = 9 
lwz rBefore, -0x5A90(r13) 
mr rAfter, r3 
li rRW, 1 
li rHL, 0 
stw r3, -0x5A90(r13) 
b <OSAnnounceArena.inj> 
.long 0 
NTSC 1.02 --- 803444d8 ---- 906dbcd0 -> Branch 
# On OSSetArenaHi 
rRW = 12; rHL = 11; rBefore = 10; rAfter = 9 
lwz rBefore, -0x4330(r13) 
mr rAfter, r3 
li rRW, 1 
li rHL, 1 
stw r3, -0x4330(r13) 
b <OSAnnounceArena.inj> 
.long 0 
NTSC 1.02 --- 803444d0 ---- 806da570 -> Branch 
# On OSGetArenaLo 
rRW = 12; rHL = 11; rBefore = 10; rAfter = 9 
li rRW, 0 
li rHL, 0 
lwz r3, -0x5A90(r13) 
b <OSAnnounceArena.inj> 
.long 0 
NTSC 1.02 --- 803444c8 ---- 806dbcd0 -> Branch 
# On OSGetArenaHi 
rRW = 12; rHL = 11; rBefore = 10; rAfter = 9 
li rRW, 0 
li rHL, 1 
lwz r3, -0x4330 (r13) 
b <OSAnnounceArena.inj> 
.long 0 
  
  
<OSAnnounceArena.inj> NTSC 1.02 
# r12 = 0 if read, else write 
# r11 = 0 if low, else high 
rRW = 12; rHL = 11; rBefore = 10; rAfter = 9; rMem = 8 
globals.xRetraceFrame = -0x4280 
  
isFromHeap.xStaticLo       = 0x00 
isFromHeap.xStaticLoFrame  = 0x04 
isFromHeap.xArenaLo        = 0x08 
isFromHeap.xArenaLoFrame   = 0x0C 
isFromHeap.xHeap0          = 0x10 
isFromHeap.xHeap0Frame     = 0x14 
isFromHeap.xHeap1          = 0x18 
isFromHeap.xHeap1Frame     = 0x1C 
isFromHeap.xHeap2          = 0x20 
isFromHeap.xHeap2Frame     = 0x24 
isFromHeap.xHeap3          = 0x28 
isFromHeap.xHeap3Frame     = 0x2C 
isFromHeap.xArenaHi        = 0x30 
isFromHeap.xArenaHiFrame   = 0x34 
isFromHeap.xStaticLo       = 0x38 
isFromHeap.xStaticLoFrame  = 0x3C 
  
isFromHeap.xLastSceneFrame = 0x40 
isFromHeap.xLastSceneCount = 0x44 
isFromHeap.xLastSceneMajor = 0x46 
isFromHeap.xLastSceneMinor = 0x47 
  
mflr r0 
stwu sp, -0x40(sp) 
stw  r0,  0x40+4(sp) 
cmpwi rRW, 0 
addi rMem, sp, 0x10 
stswi r3, rMem, 0x10 
stw rRW, 0x20(sp) 
mr rMem, r3 
# backup r3...r6 and rRW 
  
lis r7, <<isFromHeap.globals>>@h 
ori r7, r7, <<isFromHeap.globals>>@l 
cmpwi cr1, rHL, 0 
beq- 0f 
  beq- cr1, 1f 
    stw rAfter, isFromHeap.xArenaHi(r7) 
  1: bne- cr1, 0f 
    stw rAfter, isFromHeap.xArenaLo(r7) 
0: 
  
bl _data 
b _announce_arena 
  _data: blrl 
  0: .asciz " -- OSArena%s : has been written to by caller %x  moving from %x to %x; %\x23\ x bytes" 
  1: .asciz " -- OSArena%s : has been read (r3 = %x) by caller %x" 
  2: .asciz "Hi" 
  3: .asciz "Lo" 
  .align 2 
   
_announce_arena: 
addic r5, r0, -4 
mflr r3 
addi r4, r3, 2b-0b 
mr r6, rBefore 
bne- 9f;  addi r3, r3, 1b-0b; mr r6, r5; mr r5, rMem; 9: 
mr r7, rAfter 
sub r8, r9, r10 
bne- cr1, 0f;  addi r4, r4, 3b-2b; 0: 
lis r12, <<isFromHeap.loggerSettings>>@h 
ori r12, r12, <<isFromHeap.loggerSettings>>@l 
lbz r12, 0x0(r12) 
cmpwi r12, 0 
beq- _return # ignore logger if disabled in logger settings 
  bl <printf> 
  lwz rRW, 0x20(sp) 
  cmpwi rRW, 0 
  ble+ _return 
      bl <printf.newline> 
       
_return: 
addi rMem, sp, 0x10 
lswi r3, rMem, 0x10 
lwz  r0,  0x40+4(sp) 
addi sp, sp, 0x40 
mtlr r0 
blr 
  
NTSC 1.02 --- 80344154 ---- 80344154 -> Branch 
# on OSDestroyHeap 
globals.xHeapDescs = -0x4340 
globals.xRetraceFrame = -0x4280 
mflr r0 
stwu sp, -0x40(sp) 
stw  r0,  0x40+4(sp) 
stw r3, 0x10(sp) 
  
  
lis r12, <<isFromHeap.loggerSettings>>@h 
ori r12, r12, <<isFromHeap.loggerSettings>>@l 
lbz r12, 0x1(r12) 
cmpwi r12, 0 
beq- _return # ignore logger if disabled in logger settings 
  
bl _data 
b _announce_OSDestroyHeap 
  _data: blrl 
  0: .asciz " -- OSHeap[%x] : has been DESTROYED after %\x23\ x VIFrames: %\x23\ x bytes freed at  %x ... %x" 
  .align 2 
   
_announce_OSDestroyHeap: 
lis r12, <<isFromHeap.globals>> + 0x10 @h 
ori r12, r12, <<isFromHeap.globals>> + 0x10 @l 
slwi r0, r3, 3 
lwzux r7, r12, r0 
mulli r0, r3, 0xC 
lwz r5, 0x4(r12) 
mr r4, r3 
lwz r3, globals.xHeapDescs(r13) 
lwzx r6, r3, r0 
add r8, r7, r6 
lwz r3, globals.xRetraceFrame(r13) 
sub r5, r3, r5 
mflr r3 
# r3 = format string 
# r4 = descID 
# r5 = frame - timestamp 
# r6 = size from desc 
# r7 = beginning 
# r8 = ending 
bl <printf> 
bl <printf.newline> 
  
_return: 
lwz  r12,  0x40+4(sp) 
mtlr r12 
lwz  r3, 0x10(sp) 
addi sp, sp, 0x40 
mulli r0, r3, 0xC # original instruction 
.long 0 
  
  
NTSC 1.02 --- 0x80343f24 ---- 28060000 -> Branch 
# when a free fragment has been found for this allocation... 
  
xTotal    = 0x00; xFree   = 0x04; xAlloc = 0x08 # heap descriptor 
xPrev     = 0x00; xNext   = 0x04; xSize  = 0x08 # heap fragment metadata header 
xParams   = 0x0C; xCaller = 0x14; xFrame = 0x18; xTime = 0x1C# extended metadata 
globals.xHeapDescs = -0x4340 
globals.xRetraceFrame = -0x4280 
rSize = 3; rCaller = 4; rDesc = 5; rThis = 6; rPoint = 7; rFrame = 8 
  
  
mflr r0 
stwu sp, -0x40(sp) 
stw  r0,  0x40+4(sp) 
addi r12, sp, 0x10 
stswi r3, r12, 0x20 
  
  
lis r0, <<isFromHeap.callerFilter>>@h 
ori r3, r0, <<isFromHeap.callerFilter>>@l 
bl <callerFilter> 
lwz rThis, 0x1C(sp) 
stw r3, xCaller(rThis) 
# --- the address of the instruction that called this allocation is now in fragment metadata 
  
bl <timestamp.scene> 
rTimeHi=3;rFrameFrac=4;rFrame=5;rSceneFrame=6;rCompID=7;rMinor=8;rMajor=9;rCTR=10;rScene=11;rGlob=12 
  
sub rFrame, rFrame, rSceneFrame 
lwz rThis, 0x1C(sp) 
stw rFrame, xFrame(rThis) 
stw rFrameFrac, xTime(rThis) 
# --- the current draw frame is now in fragment metadata 
  
li r0, 0 
stw r0, 0xC(rThis) 
stw r0, 0x10(rThis) 
# --- offset 0xC...0x18 of metadata is made blank on allocation 
  
_return: 
  
addi r12, sp, 0x10 
lswi r3, r12, 0x20 
lwz  r0,  0x40+4(sp) 
addi sp, sp, 0x40 
mtlr r0 
  
cmplwi  r6, 0 # original istruction 
.long 0 
  
NTSC 1.02 --- 0x8034ea28 ---- 38610018 -> Branch 
# when updating the VIWaitForRetrace frame... 
# - basically a 'for each frame' injection... 
  
xMytoc  = -0x4F60 
mytoc.xOutput = 0x0 
mytoc.xInput  = 0x4 
isFromHeap.xLastFrame     = 0x48 
isFromHeap.xLastFrameTime = 0x4C 
# these offsets are globally available because of our mytoc overwrites 
  
lis r12, <<isFromHeap.loggerSettings>>@h 
ori r12, r12, <<isFromHeap.loggerSettings>>@l 
lbz r12, 0x3(r12) 
cmpwi r12, 0 
beq- _return # ignore logger if disabled in logger settings 
  
lwz r3, xMytoc + mytoc.xInput(rtoc) 
bl <isFromHeap.printf> 
  
  
_return: 
li r0, 0 
stw r0, xMytoc + mytoc.xInput(rtoc) 
bl <timestamp.scene> 
stw r4, isFromHeap.xLastFrame(r12) 
mftbl r3 
stw r3, isFromHeap.xLastFrameTime(r12) 
addi  r3, sp, 24 
.long 0 
  
  
-==- 
  
Heap Logger - PathToEntrynum Logger 
  
Prints the argument string, the string location, and the caller of this function; every call 
- uses the HSD filename buffer function as a means of capuring static string arguments when possible 
Edit the <PathToEntrynum.callerFilter> table to skip over sampling callers from known functions 
Edit the <PathToEntrynum.loggerSettings> table to mute PathToEntrynum logger features 
[Punkline] 
  
<PathToEntrynum.loggerSettings> NTSC 1.02 
# '00' bytes are disabled;  else = enabled 
01  # --- PATHTOENTRYNUM LOGGER - prints a message every time a file name entrynumber is looked up 
01  # --- SAMPLE BUFFER ARGUMENTS - logger uses memory of 0x80432058 buffer arg for path string addr 
0000     # - padding 
00000000 # - (space reserved for buffer) 
  
<PathToEntrynum.callerFilter> NTSC 1.02 
800186d0 
800181b8 # unknown DAT index HSD heap funcs 
00000000 
  
<PathToEntrynum.data> NTSC 1.02 
  
NTSC 1.02 --- 80016220 ---- 3c80803c -> Branch 
lis r0, <<PathToEntrynum.loggerSettings>>@h 
ori r4, r0, <<PathToEntrynum.loggerSettings>>@l 
stw r26, 0x4(r4) 
  
_return: 
lis r4, 0x803C 
.long 0 
  
NTSC 1.02 --- 80337c4c ---- ba810018 -> Branch 
buffer = 0x80432058 
lis r0, <<PathToEntrynum.loggerSettings>>@h 
ori r29, r0, <<PathToEntrynum.loggerSettings>>@l 
mr r30, r3 
lis r0, <<PathToEntrynum.callerFilter>>@h 
ori r3, r0, <<PathToEntrynum.callerFilter>>@l 
bl <timestamp.scene> 
rFrameFrac=4;rFrame=5;rSceneFrame=6;rCompID=7 
sub r20, rFrame, rSceneFrame 
mr r21, rFrameFrac 
mr r22, rCompID 
bl <callerFilter> 
bl _data 
b _announce_path 
  _data: blrl 
  0: .asciz " PathToEntrynum: on Frame %\x23\ x.%04x Scene %05x : %x gets r3= %\x23\ 06x <- %x - %s" 
  .align 2 
   
_announce_path: 
lbz r0, 0(r29) 
cmpwi r0, 0 
beq- _return 
  mr r4, r20 
  mr r5, r21 
  mr r6, r22 
  mr r7, r3 
  mflr r3 
  mr r8, r30 
  mr r9, r25 
  lis r0, buffer@h 
  ori r0, r0, buffer@l 
  mr r10, r25 
  cmpw r0, r9 
  bne- 0f 
    lbz r0, 0x1(r29) 
    cmpwi r0, 0 
    beq- 0f 
      lwz r9, 0x4(r29) 
  0: 
  bl <printf> 
   
_return: 
mr r3, r30 
lmw r20, 0x0018 (sp) 
.long 0 
  
  
-==- 
  
Heap Logger - Archive Object Logger 
Invokes <isFromHeap.printf> each time a DAT file is loaded 
Requires Heap Fragment Offset Finder 
Edit the <ArchiveObject.loggerSettings> table to mute PathToEntrynum logger features 
[Punkline] 
  
# --- Configurable Data: 
  
<ArchiveObject.loggerSettings> NTSC 1.02 
# Archive Logger flags can be edited below  using False=0,  True=1 
# flags are in nibbles on little end, to fit discretely in CR: 
0000 # padding 
1 # --- ARCHIVE OBJECT LOGGER    - logs information about archives (dat files) as they are loaded 
1 # --- VERBOSE HEADER LOG       - logs details extracted from the archive header and heap fragment 
1 # --- LOG COPIED ARCHIVES ONCE - logs copied archives (like action state animations) only once 
# - prevents archive copies from flooding the logger by only announcing it once per allocation 
#   - this will prevent the logger from seeing things like action state changes, beyond the first 
1 # --- VERBOSE HEADER ONCE      - prevents serial verbose header logs from the same allocation 
# - prevents verbose copies from flooding the logger with multiple lines every action state 
#   - if logging all archive copy parses, then each copy only shows verbose lines on the first use 
  
<ArchiveObject.callerFilter> NTSC 1.02 
80069d14 
80069cf4 
80085dec # these are related to action state changes 
00000000 
  
  
# --- Injection code: 
  
NTSC 1.02 --- 8038033c ---- 8001002c -> Branch 
rData=29;rArch=30; rMeta=31; rSize = 10 
.include "punkpc/xem.s" 
meta.size  = 0x20 
meta.xPrev = 0x00; 
meta.xNext = 0x04; 
meta.xSize = 0x08 
# heap fragment metadata header 
  
meta.param     = 0xDA7  # a special hardcoded tag 
meta.xParam    = 0x0C 
meta.xFrame    = 0x18 
meta.xCaller   = 0x1C 
# extended metadata 
  
scene.frame = 0x80479D60 
# scene controller frame counter 
  
sp.xBackup = 0x18 
sp.xCR     = 0x14 
sp.xCaller = 0x10 
# stack frame padding 
  
arch.xSize  = 0x00 
arch.xData  = 0x20 
arch.xReloc = 0x24 
arch.xNodes = 0x28 
arch.xExtr  = 0x2C 
arch.xStr   = 0x30 
arch.xHead  = 0x40 
# Archive object offsets 
  
entrynum.buffer = 0x80432058 
  
mfcr r0 
stw r3, sp.xBackup(sp) # r3 may be used in host function's return value 
stw r0, sp.xCR(sp)  # we can use all of CR this way 
  
lis r0, <<ArchiveObject.loggerSettings>>@h 
ori r12, r0, <<ArchiveObject.loggerSettings>>@l 
lwz r12, 0x0(r12) 
mtcrf 0x0F, r12; bLogging = 19; bVerbose = 23; bCopyOnce = 27; bVerboseOnce = 31 
bf- bLogging, _return 
# skip logging if ARCHIVE OBJECT LOGGER is false 
  
mr r3, r31 
bl <isFromHeap> 
mr rMeta, r7 
# get information about this allocation 
  
rQuery = 3; rType = 4; rOffset = 5; rAlloc = 6; rDesc = 8; rDescID = 9 
cmpwi cr2, rType, 1; bFree = cr2.lt;  bNotDynamic = cr2.gt; bAlloc = cr2.eq 
# cr2 checks type 
  
li r0, 0 
bt- bNotDynamic, 0f 
  lhz r0, meta.xParam(rMeta) 
  li  r9, meta.param 
  sth r9, meta.xParam(rMeta) 
0: cmpwi cr3, r0, meta.param; bAnnounced = cr3.eq; bCopying = cr3.lt 
crandc bCopying, bAnnounced, bCopyOnce 
crand bVerboseOnce, bVerboseOnce, bAnnounced 
crand cr3.gt, bVerbose, bVerboseOnce;  bVerbose = cr3.gt # move 'bVerbose' to a saved index 
# cr3 checks if our special param exists, and update meta with tag 
  
lwz rSize, arch.xSize(rArch) 
addi r10, rSize, 63 
li r11, 0 
bt- bNotDynamic, 0f 
  lwz r11, meta.xSize(rMeta) 
0: rlwinm r0, r10, 0, ~31 
cmpw cr4, r11, r0; bFullAlloc = cr4.eq 
crandc bCopying, bCopying, bFullAlloc 
# cr4 checks if this allocation is entirely dedicated to this archive (like a loaded file) 
  
lis r0, <<ArchiveObject.callerFilter>>@h 
ori r3, r0, <<ArchiveObject.callerFilter>>@l 
bl <callerFilter> 
li r0, 0 
stw r3, sp.xCaller(sp) 
  
bl <timestamp.scene> 
rTimeHi=3;rFrameFrac=4;rFrame=5;rSceneFrame=6;rCompID=7;rScene=11;rGlob=12 
bl _data 
mflr rData 
b _announce_archive 
  _data: blrl 
  0: .asciz " ARCHIVE FILE     : %8x : %s, %\x23\ x bytes at %x : Frame %\x23 x.%04x of Scene %05x" 
  1: .asciz " - ARCHIVE DATA   : %8x : %\x23\ x bytes" 
  2: .asciz " - ARCHIVE RELOCS : %8x : %d pointer(s)" 
  3: .asciz " - ARCHIVE NODES  : %8x : %d symbol(s) nodes" 
  4: .asciz " - ARCHIVE EXTERN : %8x : %d external symbol(s)" 
  5: .asciz " - ARCHIVE SYMSTR : %8x : %\x23\ x byte footer" 
  6: .asciz " ARCHIVE COPY     : %8x : %\x23\ x bytes of %x (alloc %x + %x) : Frame %\x23\ x.%04x" 
  7: .asciz " - PARSE CALLER   : %8x : recieves Archive %x %s" 
  8: .ascii "(part of the runtime stack)" 
  9: .byte 0 
  .align 2;  .macro getstr, r, n; addi \r, rData, \n\()b-0b; .endm 
   
_announce_archive: 
bf+ bFullAlloc, 100f 
   
  # --- if archiving an allocation == size of archive - assume this is a loaded file 
  sub r8, rFrame, rSceneFrame 
  mr r9, rFrameFrac 
  mr r10, rCompID 
  getstr r3, 0 
  lis r0, entrynum.buffer@h 
  ori r5, r0, entrynum.buffer@l 
  lwz r7, arch.xHead(rArch) 
  mr r4, rArch 
  lwz r6, arch.xSize(rArch) 
  bl <printf> 
   
100: bf- bCopying, 100f 
  _copying_archive: 
  # --- if archiving an allocation != size of archive - assume it's some copied archive 
   
  sub r9, rFrame, rSceneFrame 
  mr r10, rFrameFrac 
  getstr r3, 6 
  lwz r6, arch.xHead(rArch) 
  addi r7, rMeta, meta.size 
  mr r4, rArch 
  lwz r5, arch.xSize(rArch) 
  bf+ bNotDynamic, 99f 
    li r7, 0 
    li r8, 8 
    _for_8_memory_regions: 
      subic. r8, r8, 1 
      blt 99f 
      slwi r0, r8, 3 
      lwzx r0, rGlob, r0 
      cmplw r6, r0 
      blt _for_8_memory_regions 
        cmplw r6, r7 
          blt _for_8_memory_regions 
            mr r7, r0 
            b _for_8_memory_regions 
  99: subf r8, r7, r6 
  bl <printf> 
   
# at this point, the archive object has been annouced -- now we print details 
100: bt- bVerbose, 100f 
  getstr r3, 1 
  lwz r5, 0x4(rArch) 
  lwz r4, arch.xData(rArch) 
  bl <printf> 
  getstr r3, 2 
  lwz r5, 0x8(rArch) 
  cmpwi r5, 0 
  lwz r4, arch.xReloc(rArch) 
  ble- 99f 
  bl <printf> 
  99: getstr r3, 3 
  lwz r5, 0xC(rArch) 
  cmpwi r5, 0 
  lwz r4, arch.xNodes(rArch) 
  ble- 99f 
  bl <printf> 
  99: getstr r3, 4 
  lwz r5, 0x10(rArch) 
  cmpwi r5, 0 
  lwz r4, arch.xExtr(rArch) 
  ble- 99f 
  bl <printf> 
  99: getstr r3, 5 
  lwz r0, arch.xHead(rArch) 
  lwz r5, arch.xSize(rArch) 
  lwz r4, arch.xStr(rArch) 
  add r5, r5, r0 
  sub r5, r5, r4 
  cmpwi r5, 0 
  ble- 99f 
  bl <printf> 
  99: getstr r3, 7 
  lwz r4, sp.xCaller(sp) 
  mr r5, rArch 
  lis r0, 0x804F 
  getstr r6, 9 
  cmpw r5, r0 
  bge- 98f 
    cmpw r5, sp 
    ble- 98f 
      getstr r6, 8 
  98: 
  bl <printf> 
  99: lwz r3, arch.xHead(rArch) 
  bl <isFromHeap.printf> 
   
   
100: 
_return: 
lwz r0, sp.xCR(sp) 
lwz r3, sp.xBackup(sp) 
mtcr r0 
lwz r0, 0x002C(sp) 
.long 0 

Function Modules:

Rich (BB code):
-==- 
  
Heap Logger - Standalone Functions 
Standalone utilities used by Heap Logger that do not require injections: 
  
  
# --- HEAP TOOLS: 
  
<alloc> creates a heap allocation in the topmost heap (while preserving volatile registers) 
Arguments:   rSize = 3 
Returns:     rAllocation = 3; # r4...r12 are restored -- for easy use inside of injections 
  
<alloc.r12> is a variation of alloc that uses r12 as its argument/return register 
# May be useful for creating allocations while r3 is in use by injection context 
  
<alloc.free> is a shortcut to HSD_Free 
Arguments:   rAlloc = 3 
  
<isFromHeap> checks if a given RAM address belongs to a any heap fragments defined by OS 
Arguments:   rQuery = 3 
Returns:     rQuery=3; rType=4; rOffset=5; rAlloc=6; rMeta=7; rDesc=8; rDescID=9 
# Type = memory type 
#  - 0 = Free;   1 = Allocated;   2 = (not currently in an active heap) 
# Offset = offset of given query from allocation start 
# Meta = 0x20 bytes of header metadata for all heap fragments 
#  - 0x8(rMeta) = alloc + meta size 
# Desc = 0xC bytes of header information about this heap (of IDs 0...3) 
#  - 0x0(rDesc) = heap size;   0x4 = Free Fragment;   0x8 = Allocated 
  
  
# --- TIME SAMPLER: 
  
<timestamp> returns a 64-bit timestamp for you to use for logging purposes 
Returns:     rTimeHi = 3; rTimeLo = 4 
# TimeLo increments every 20 nanoseconds 
# TimeHi increments every 85.8993 seconds 
  
  
# --- CALLSTACK SAMPLER: 
  
<callerFilter> is a leaf that samples the caller of the current stack frame 
Arguments:   rFilter = 3  # use a value of 0 to ignore filter 
Returns:     rCaller = 3  # the address of the instruction that called this frame 
# - filter is a null-terminated array of 4-byte address pointers 
#   - each instruction is of a call, like bl or blrl; and will be skipped in sample 
  
  
# --- PRINTF SHORTCUTS: 
  
<printf> is a shortcut for invoking the NTSC 1.02 printf function, for writing to logger 
Arguments: rFormat = 3; bUseFloatArgs = cr1.eq; # varargs = r4 ... r10, f0 ... f8 
# search for 'printf' online for details on syntax of format string 
# - format strings should start with a space, to properly print in dolphin 
# - format strings are null terminated, and are printed on individual lines 
  
<printf.newline> is a variation of printf that takes no args, and prints a blank string 
[Punkline] 
  
  
# --- Independently Usable Functions: 
# You can use these without installing Heap Logger: 
  
<alloc> NTSC 1.02 
# Allocate from top-most heap, and back up volatile registers 
  
# --- Arguments: 
# r3 = alloc size 
  
# --- Returns: 
# r3 = allocation;  all other registers are preserved 
  
mflr r0 
stwu sp, -0x80(sp) 
stw  r0,  0x80+4(sp) 
stw  r12, 0x10(sp) 
addi r12, sp, 0x14 
stswi r4, r12, 0x20 
mr r4, r3 
lwz r3, -0x58A0(r13) # HSD : Topmost Heap ID 
bl 0x80343ef0 # OSAllocFromHeap 
addi r12, sp, 0x14 
lswi r4, r12, 0x20 
lwz r12, 0x10(sp) 
lwz  r0,  0x80+4(sp) 
addi sp, sp, 0x80 
mtlr r0 
blr 
  
<alloc.r12> NTSC 1.02 
# Variant of <alloc> uses r12 as argument/return instead of r3 
  
mflr r0 
stwu sp, -0x80(sp) 
stw  r0,  0x80+4(sp) 
stw  r3, 0x10(sp) 
addi r3, sp, 0x14 
stswi r4, r3, 0x20 
mr r4, r12 
lwz r3, -0x58A0(r13) # HSD : Topmost Heap ID 
bl 0x80343ef0 # OSAllocFromHeap 
addi r3, sp, 0x14 
lswi r4, r3, 0x20 
lwz r3, 0x10(sp) 
lwz  r0,  0x80+4(sp) 
addi sp, sp, 0x80 
mtlr r0 
blr 
  
  
  
<alloc.free> NTSC 1.02 
# --- Arguments: 
# r3 = allocation to free 
b 0x8037f1b0  # HSD_Free -- picks topmost heap and invokes OSFreeToHeap 
  
  
  
<isFromHeap> NTSC 1.02 
# check if query comes from any dynamic heap fragments 
  
# --- Arguments: 
# r3 = Query Address 
  
# --- Returns: 
# r3 = argument query 
# r4 = type    -- 0 = Free;  1 = Allocated;  2 = Not_Dynamic (not in heap sub-arenas) 
# r5 = offset  -- offset of argument query from start of fragment allocation 
# r6 = alloc   -- the base address of an allocated space for storing data 
# r7 = meta    -- the base address of a header for an allocation or free fragment 
# r8 = desc    -- the heap descriptor responsible for pointing to this heap 
# r9 = descID  -- the index of this heap descriptor 
  
.include "punkpc/xem.s"  # cr register names plz 
  
desc.size = 0xC 
desc.xTotal = 0x00 
desc.xFree  = 0x04 
desc.xAlloc = 0x08 
# heap descriptor 
  
meta.size  = 0x20 
meta.xPrev = 0x00; 
meta.xNext = 0x04; 
meta.xSize = 0x08 
# heap fragment metadata header 
  
meta.xParams   = 0x0C 
meta.xCaller   = 0x14 
meta.xFrame    = 0x18 
meta.xTime     = 0x1C 
# extended metadata 
  
globals.xHeapDescs = -0x4340 
globals.xRetraceFrame = -0x4280 
# globals 
  
type.Not_Dynamic = 2 
type.Alloc = 1 
type.Free  = 0 
  
rQuery = 3; rType = 4; rPass = 5; rAlloc = 6; rMeta = 7; rDesc = 8; rDescID = 9 
lwz rDesc, globals.xHeapDescs(r13) 
li r0, 4 
li rType, type.Alloc 
mtctr r0 
addi rDesc, rDesc, 4 * desc.size 
# check allocations first, in descending heap stack order 
  
_for_4_descs: 
  lwzu r0, desc.xTotal-desc.size(rDesc) 
  cmpwi r0, 0 
  blt+ _iter 
  # if total byte size in descriptor is negative, then the heap has been destroyed 
     
    # else, check the allocation chain for matches 
    li rPass, 1 
    lwz  rMeta, desc.xAlloc(rDesc) 
     
    _for_each_fragment: 
      cmpw cr0, rMeta, rQuery 
      lwz r0, meta.xSize(rMeta) 
      add r0, rMeta, r0 
      cmpw cr1, r0, rQuery 
      crandc eq, cr1.gt, cr0.gt 
      beq- _found_match 
      mr r0, rMeta 
      lwz rMeta, meta.xNext(rMeta) 
      cmpwi rMeta, 0 
      blt+ _for_each_fragment 
      # check each allocated fragment... 
       
    li rType, type.Free 
    lwz rMeta, desc.xFree(rDesc) 
    subic. rPass, rPass, 1 
    beq+ _for_each_fragment 
    # do 2 loop passes, one for allocated fragments, the other for free fragments 
    # else, iterate to next heap 
     
  _iter: 
  bdnz+ _for_4_descs 
   
rOffset = rPass 
_no_match: 
li rType, type.Not_Dynamic 
li rOffset, 0 
li rAlloc, 0 
li rMeta, 0 
li rDesc, 0 
li rDescID, -1 
blr 
   
  _found_match: 
  addi rAlloc, rMeta, meta.size 
  mfctr rDescID 
  sub rOffset, rQuery, rAlloc 
  subfic rDescID, rDescID, 3 
  blr 
   
   
   
<timestamp> NTSC 1.02 
# leaf takes no args, and returns the following identifying timestamp data: 
# --- Retrns: 
# r3 = TBU: Upper 32-bits of timestamp 
# r4 = TBL: Lower 32-bits of timestamp 
0: mftbu r3 
mftbl r4 
mftbu r0 
cmpw r0, r3 
bne- 0b 
blr 
  
  
  
<callerFilter> NTSC 1.02 
# Fetches callers, optionally filtering out known caller instructions 
# --- Arguments: 
# r3 = filter list  -  null terminated list of exceptions to skip 
# - each exception must be the address of the CALLER, not the return address or the function start 
  
# --- Returns: 
# r3 = caller 
  
cmpwi cr1, r3, 0 
subic r4, r3, 4 
lwz r5, 0(sp) 
mr r6, r4 
b 1f 
_for_each_caller_filter: 
  lwzu r0, 0x4(r4) 
  cmpwi r0, 0 
  bgelr- 
  cmpw r0, r3 
  bne+ _for_each_caller_filter 
    lwz r5, 0(r5) 
    1: lwz r3, 0x4(r5) 
    subi r3, r3, 4 
    mr r4, r6 # try again with deeper frame if a match was found 
    blt+ cr1, _for_each_caller_filter 
      blr # return immediately if no filter was given 
       
       
       
<printf.newline> NTSC 1.02 
# no args -- as though calling printf with r3 = point to null 
lis r3, <<printf.newline.data>>@h 
ori r3, r3, <<printf.newline.data>>@l 
b <printf> 
  
<printf.newline.data> NTSC 1.02 
00000000 
  
  
  
<printf> NTSC 1.02 
# Can be used to format a print string for stdout, and displaying in the Dolphin Logger 
# --- Arguments: 
# r3 = format string, r4...r10 = varargs 
# if cr1.eq is true, f0...f8 also = varargs 
# - format string must start with a space in order to display in Dolphin Logger properly 
b 0x80323eb4 
  
  
  
-==- 
  
Heap Logger - Core Utilities 
  
  
Installs injections that help log extra information about heap allocations 
  
  
# --- METADATA: 
  
Injections write extra metadata to OSHeap fragment headers : 
  0xC  = 8 bytes are cleared for implementation by users 
  0x14 = address of allocation caller (filtered by callerFilter) 
  0x18 = VI Retrace frame that allocation was made 
  0x1C = tbl timestamp 
   
   
# --- LOGGERS: 
  
Enables the log announcer for: 
- on OSArenaHi/Lo reads/writes 
- on OSHeap[n] creation/destruction 
- on Scene Transitions 
- on Memory Editor Query Inputs 
  
Edit the <isFromHeap.loggerSettings> table to selectively mute core logger features 
  
  
# --- MEMORY EDITOR QUERY: 
  
Use 804DAA80 or -0x4F60(rtoc) to reach Memory Editor Query IO : 
804DAA80 = POINT TO OUTPUT TABLE -- this will display info about your query address 
804DAA84 = INPUT FIELD -- write a query address here to trigger the output next frame 
  
OUTPUT TABLE : 
  0x00 = (copy of last input) 
  0x04 = offset of input from allocation start 
  0x08 = POINT to fragment metadata header 
  0x0C = POINT to data start 
  0x10 = POINT to alloc caller  -- the instruction responsible for making this 
  0x14 = POINT to heap descriptor 
  0x1A = Byte: Heap ID 
  0x1B = Byte: Fragment type : 0=free, 1=allocated, 2=not dynamic 
  0x1C = Word: Size of allocation 
   
Edit the <isFromHeap.callerFilter> table to specify callers to ignore when sampling a callstack 
  
  
# --- HEAP OFFSET FINDER EXTENSIONS: 
  
<isFromHeap.output> is a version of <isFromHeap> that also samples extra metadata 
# samples are written to global output table <isFromHeap.output.data> 
  
<isFromHeap.printf> is a log announcer that calls and uses <isFromHeap.output> for logging 
  
  
# --- TIMESTAMP EXTENSIONS: 
  
<timestamp.scene> is a verbose timestamp that also samples Melee scene and VIRetrace frames 
Returns:   rTimeHi=3;rFrameFrac=4;rFrame=5;rSceneFrame=6;rCompID=7;rMinor=8;rMajor=9;rCTR=10;rScene=11;rGlob=12 
[Punkline] 
  
  
# --- Editable Data: 
<isFromHeap.loggerSettings> NTSC 1.02 
# You can select which loggers you want to enable when the core module is installed: 
# '00' bytes are disabled;  else = enabled 
01  # --- ARENA LOGGER - triggered when the OS Arena functions are invoked 
01  # --- HEAP LOGGER  - triggered when an OS Heap is created or destroyed 
01  # --- SCENE LOGGER - triggered when the scene function writes a new minor ID 
01  # --- MEMORY EDITOR QUERY INTERFACE LOGGER - triggered when a user input is found at 804DAA84 
  
<isFromHeap.callerFilter> NTSC 1.02 
# You can filter out unwanted caller samples by adding them here 
# - when sampled on allocation, the callstack will reach one extra step when finding these 
8037f20c # HSD_MemAlloc call 
8037aa38 # HSD_ObjAllocAddFree 
8037acbc # HSD_ObjAlloc ... something with IDs 
  
80015c40 # HSD_ ... something with loading files 
  
# <- add additional instruction addresses here 
  
  
# - add the address of instructions that call OSAllocFromHeap (80343ef0) 
#   - you can also add instructions that call functions that call OSAllocFromHeap 
#   - each matching address will be skipped in the callstack parse, in favor of the next one 
00000000  # leave a null terminator, for the parser 
  
  
  
# --- Reserved Data: 
  
<isFromHeap.output.data> NTSC 1.02 
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
  
  
<isFromHeap.globals> NTSC 1.02 
isFromHeap.xStaticLo       = 0x00 
isFromHeap.xStaticLoFrame  = 0x04 
isFromHeap.xArenaLo        = 0x08 
isFromHeap.xArenaLoFrame   = 0x0C 
isFromHeap.xHeap0          = 0x10 
isFromHeap.xHeap0Frame     = 0x14 
isFromHeap.xHeap1          = 0x18 
isFromHeap.xHeap1Frame     = 0x1C 
isFromHeap.xHeap2          = 0x20 
isFromHeap.xHeap2Frame     = 0x24 
isFromHeap.xHeap3          = 0x28 
isFromHeap.xHeap3Frame     = 0x2C 
isFromHeap.xArenaHi        = 0x30 
isFromHeap.xArenaHiFrame   = 0x34 
isFromHeap.xStaticLo       = 0x38 
isFromHeap.xStaticLoFrame  = 0x3C 
  
isFromHeap.xLastSceneFrame = 0x40 
isFromHeap.xLastSceneCount = 0x44 
isFromHeap.xLastSceneMajor = 0x46 
isFromHeap.xLastSceneMinor = 0x47 
isFromHeap.xLastFrameTime  = 0x48 
  
.long 0x80000000, 0 
.zero 0x30 
.long 0x81800000, 0 
.zero 0x10 
  
  
  
# --- Functions: 
# These will only function correctly if they are used with the injection mutations in this code 
  
<isFromHeap.output> NTSC 1.02 
# invokes <isFromHeap> to record 0x20 bytes of output data in <isFromHeap.output> 
  
desc.xTotal = 0x00 
desc.xFree  = 0x04 
desc.xAlloc = 0x08 
# heap descriptor 
  
meta.size  = 0x20 
meta.xPrev = 0x00; 
meta.xNext = 0x04; 
meta.xSize = 0x08 
# heap fragment metadata header 
  
meta.xParams   = 0x0C 
meta.xCaller   = 0x14 
meta.xFrame    = 0x18 
meta.xTime     = 0x1C 
# extended metadata 
  
globals.xHeapDescs = -0x4340 
globals.xRetraceFrame = -0x4280 
# globals 
  
xMytoc  = -0x4F60 
mytoc.xOutput = 0x0 
mytoc.xInput  = 0x4 
# these offsets are globally available because of our mytoc overwrites 
  
out.xLastInput  = 0x00 # (copy of last input) 
out.xOffset     = 0x04 # offset of input from allocation start 
out.xHeader     = 0x08 # POINT to fragment metadata header 
out.xAllocation = 0x0C # POINT to data start 
out.xCaller     = 0x10 # POINT to alloc caller (or alloc freer) 
out.xDesc       = 0x14 # POINT to heap descriptor 
out.xDescID     = 0x1A # Byte: Heap ID 
out.xFragType   = 0x1B # Byte: Fragment type : 0=free, 1=allocated, 2=not dynamic 
out.xSize       = 0x1C # Word: size of this fragment 
  
mflr r0 
stwu sp, -0x20(sp) 
stw  r0,  0x20+4(sp) 
  
rOut = 12; rThis = 11; rIn = 3 
lwz rOut, xMytoc + mytoc.xOutput (rtoc) 
lwz rThis, out.xLastInput(rOut) 
   
  cmpwi rIn, 0 
  bge- _return 
   
  # if input is negative (sign is true, like in an address input) ... 
  bl <isFromHeap> 
  rQuery=3;rType=4;rOffset=5;rAlloc=6;rMeta=7;rDesc=8;rDescID=9;rCaller=10;rSize=11 
  cmpwi rType, 1 
  li rCaller, 0 
  li rSize, 0 
  bgt- 0f 
    lwz rCaller, meta.xCaller(rMeta) 
    lwz rSize,   meta.xSize(rMeta) 
    subi rSize, rSize, meta.size 
     
  0: 
  stw rQuery,  out.xLastInput(rOut) 
  stb rDescID, out.xDescID(rOut) 
  stw rOffset, out.xOffset(rOut) 
  stw rMeta,   out.xHeader(rOut) 
  stw rCaller, out.xCaller(rOut) 
  stw rAlloc,  out.xAllocation(rOut) 
  stw rDesc,   out.xDesc(rOut) 
  stb rType,   out.xFragType(rOut) 
  stw rSize,   out.xSize(rOut) 
  # ... then populate output with new information based on input query 
   
_return: 
lwz  r0,  0x20+4(sp) 
addi sp, sp, 0x20 
mtlr r0 
blr 
  
  
  
<isFromHeap.printf> NTSC 1.02 
# invokes <isFromHeap.output> and prints out the resulting data into dolphin logger 
  
xMytoc  = -0x4F60 
mytoc.xOutput = 0x0 
mytoc.xInput  = 0x4 
# these offsets are globally available because of our mytoc overwrites 
  
meta.xParams   = 0x0C 
meta.xCaller   = 0x14 
meta.xFrame    = 0x18 
meta.xTime     = 0x1C 
# extended metadata 
  
scene.data = 0x80479D30 
scene.xMajor = 0x0 
scene.xMinor = 0x2 
  
out.xLastInput  = 0x00 # (copy of last input) 
out.xOffset     = 0x04 # offset of input from allocation start 
out.xHeader     = 0x08 # POINT to fragment metadata header 
out.xAllocation = 0x0C # POINT to data start 
out.xCaller     = 0x10 # POINT to alloc caller (or alloc freer) 
out.xDesc       = 0x14 # POINT to heap descriptor 
out.xDescID     = 0x1A # Byte: Heap ID 
out.xFragType   = 0x1B # Byte: Fragment type : 0=free, 1=allocated, 2=not dynamic 
out.xSize       = 0x1C # Word: size of this fragment 
type.Not_Dynamic = 2 
type.Alloc = 1 
type.Free  = 0 
  
rString=3;rOffset=5;rAlloc=6;rDesc=8;rDescID=9;rCaller=10;rSize=11;rFrame=12 
rOut=31;rNext=30;rMeta=29;rType=28;rData=27 
  
mflr r0 
stwu sp, -0x40(sp) 
stw  r0,  0x40+4(sp) 
stmw r27, 0x10(sp) 
cmpwi r3, 0 
bge+ _return 
   
  bl <isFromHeap.output> 
  mr rType, r4 
  mr rMeta, r7 
  cmpwi rType, type.Not_Dynamic 
  mr rOut, r12 
  bl _data 
  mflr rData 
  mr rString, rData 
  lwz r4, out.xLastInput(rOut) 
  # r3 = format string 
  # r4 = this input 
   
  bne+ _check_alloc 
     
    _print_NA: 
    bl <printf> 
    b _newline 
     
  _check_alloc: 
  addi rString, rData, 1f-0f 
  cmpwi rType, type.Alloc 
  bne- _check_free 
     
    _print_alloc: 
    addi rNext, rData, 2f-0f 
    mr r7, rAlloc 
    mr r6, rSize 
    mr r8, rDescID 
    bl <printf> 
    # printf 
     
    bl <timestamp.scene> 
    # r6 = VIRetrace Frame of last Minor Scene Transition 
    # r7 = 32-bit Scene ID (16-bit r10, 8-bit r8, 8-bit r9) 
    mr r3, rNext 
    lwz r5, meta.xFrame(rMeta) 
    lwz r4, meta.xCaller(rMeta) 
    lwz r6, meta.xTime(rMeta) 
    bl <printf> 
    b _newline 
     
  _check_free: 
  addi rString, rData, 3f-0f 
  cmpwi rType, type.Free 
  bne- _return 
     
    _print_free: 
    mr r5, rMeta 
    mr r6, rDescID 
    mr r7, rSize 
    bl <printf> 
    # printf 
     
  _newline: 
  bl <printf.newline> 
   
_return: 
lmw r27, 0x10(sp) 
lwz  r0,  0x40+4(sp) 
addi sp, sp, 0x40 
mtlr r0 
blr 
  
_data: blrl 
0: .asciz " - NOT_DYNAMIC    : %X : is not a part of any active OSHeap fragments..." 
1: .asciz " - ALLOCATED      : %X : offset %\x23\ x of a %\x23\ x-byte allocation at %x in OSHeap[%1x]" 
2: .asciz "           - alloc was called by the instruction at %x on Frame %\x23\ x.%04x of Scene %05x" 
3: .asciz " - FREE FRAGMENT  : %X : is in fragment %x in OSHeap[%d], and has %\x23\ x remaining bytes" 
.align 2 
  
  
  
<timestamp.scene> NTSC 1.02 
# leaf takes no args, and returns the following identifying timestamp data: 
# --- returns: 
# r3 = TBU: Upper 32-bits of timestamp 
# r4 = 16-bit fractional scene frame -- measured from TBL 
# r5 = VIRetrace Frame 
# r6 = VIRetrace Frame of last Minor Scene Transition 
# r7 = 32-bit Scene ID (16-bit r10, 8-bit r8, 8-bit r9) 
# r8 = Minor Scene 
# r9 = Major Scene 
# r10 = Scene Reload Counter -- counts up each time a new minor scene of the same r9 is loaded 
# r11 = scene controller base address 
# r12 = isFromHeap.globals 
scene.ba = 0x80479D30 
scene.xMajor = 0x0 
scene.xMinor = 0x3 
globals.xRetraceFrame = -0x4280 
isFromHeap.xLastSceneFrame = 0x40 
isFromHeap.xLastSceneCount = 0x44 
isFromHeap.xLastFrameTime = 0x4C 
  
lis r0, <<isFromHeap.globals>>@h 
ori r12, r0, <<isFromHeap.globals>>@l 
lis r0, scene.ba@h 
lwz r5, globals.xRetraceFrame(r13) 
ori r11, r0, scene.ba@l 
lwz r6, isFromHeap.xLastSceneFrame(r12) 
lbz r7, scene.xMajor(r11) 
lbz r8, scene.xMinor(r11) 
rlwimi r7, r8, 8, 0xFF00 
lhz r10, isFromHeap.xLastSceneCount(r12) 
rlwimi r7, r10, 16, 0xFFFF0000 
0:mftbu r3 
mftbl r4 
mftbu r0 
cmpw r0, r3 
bne- 0b 
lwz r0, isFromHeap.xLastFrameTime(r12) 
sub r4, r4, r0 
mulli r4, r4, 0x1421 
srwi r4, r4, 16 # r4 = approximated 16-bit fractional time component, for frame 
blr 


I will make a followup post soon with more info about how to use the Function Modules to program your own plugin loggers.




--- Memory Editor Query Interface:


Using the Memory Editor Query interface (with logged outputs) will require you to use a couple of Debug Dolphin features.


To open Debug Dolphin, create a shortcut to Dolphin, and modify the path to have a type a -d at the end.
This will create a shortcut that launches Dolphin with the debugger enabled -- allowing you to have access to memory watchs/edits and the Log panel.

To make sure you have all of the panels necessary to interact with the logger, make sure you have the following options enabled:




To see the logged print messages made by this code, you must enable the OSReport stub in the ‘Log Types’ in the log configuration panel, and check the ‘Write to Window’ checkbox; like this:



Now you will be able to see anything that the codes output in the Dolphin Log window; including the Memory Editor Query outputs.


---

To access the Memory Editor Query interface for making inputs that the logger will interpret and output information about; you can create a memory watch value using the static address 804DAA84:



This little watch entry can now act like an input field for you to write a query address to. The ‘Hexadecimal’ field with 00000000 in it can be written to using an address.


By putting an address in this field, hitting enter and progressing at least 1 frame forward in the game will cause the logger to respond with information about your input. Depending on whether or not your input is part of the heap, it may display information about where in the heap it is located:



In the above image, the queries ‘80000000’, ‘80C80000’, and ‘80EF0000’ were given to the input field.

As you can see, if a query is within bounds of a heap fragment, then it will be shown as 'Allocated' or as a 'Free Fragment'. When the query can’t be found inside of an existing fragment, it will simply say that it is ‘Not Dynamic’.

The extra line printed after the ‘ALLOCATED’ line uses special metadata created in the padding of heap header allocations in order to remember where an allocation came from, and information about when it was made.

When a query is made about an address that isn’t at the base of an allocation, then a useful ‘offset’ value is printed to help you figure out information like where file offsets have translated to in RAM.



--- Understanding OS Memory Heaps:


The gamecube OS uses a simple global structure to set up and manage dynamic memory in games. There's the 'arena' and dynamic 'memory heaps'.

The arena is a stack-like allocation structure that has a top and a bottom, or a 'Hi' and a 'Lo' as it's called in the symbols map. These represent how far the stack of allocatable space has been pushed in order to make semi-permanent allocations in RAM. The arena is used by a game early on to define allocations that can be considered static.

In the space that remains after static allocations have been made, a number of ‘heaps’ can be generated to store and purge pseudo-persistent memory. Each of these heaps can then discretely allocate parts of themselves as free-able ‘fragments’ -- and can be destroyed all at once by destroying the heap. This is called 'dynamic' memory, versus 'static' memory.


Essentially, before any ‘heaps’ can be made or used, the OS must decide how it wants to arrange its static allocations, and on a maximum possible heaps it wants to account for with special descriptors. In Melee’s case, it decides on ‘4’ being the maximum -- but only seems to utilize 2 at most. These 4 heap slots become an array of 4 0xC-byte heap descriptors that can be accessed globally from -0x4340(r13) at any point in the game.


Each of these descriptor slots is structured as follows:
Code:
# the place in the array of 4 (0...3) is the ID for this descriptor
0x0 = Bytes allocated to this heap
0x4 = Point to (first?) free fragment
0x8 = Point to (first?) allocated fragment
They line up one by one in an indexable array that you can reach with random access, if given the descriptor ID. Because of this, the OSAllocFromHeap function takes a heap descriptor argument to choose a heap to allocate memory from.

Melee creates 4 of these, and uses the first to create a semi-permanent heap that I'm not sure of the purpose of yet. The second is then built and destroyed by each new scene transition in the game -- leaving the other 2 slots unused. This essentially just leaves one heap actively available for allocating new persistent data fragments within the scope of a scene.

It would appear that the size of the heap changes dramatically depending on the scene; presumably to preload commonly used files in prior scenes.


The ‘fragment’ pointers then point to an entry fragment of the stated ‘Bytes’ that were allocated for this heap, in total.
A heap will essentially start out with just one big ‘free fragment’ that is the size of the heap, and divide it as necessary as requests are made for 'allocations' in dynamic memory.

Each of these ‘fragments’ has 0xC bytes of data in its header, and create a doubly-linked node format:
Code:
0x0 = To Previous fragment in this list (free or allocated)
0x4 = To Next fragment in this list (free or allocated)
0x8 = Size of this allocation, or remaining free bytes


These fragment headers are essentially ‘metadata’ to an allocated data table, because they reside at offset -0x20 of every allocation base that is claimed from the heap. In other words, you can reach any allocated memory’s heap fragment by subtracting 0x20 from the base address.

Since they are only 0xC bytes in one of these headers -- but are always aligned to 0x20 bytes (presumably for data caching purposes) -- there are 0x14 bytes of resulting padding in every one of these fragment metadata headers. This code utilizes some of the wasted space in each fragment for logging information about when and where an allocation was made; and clears the rest for convenient use by users for further extensions by other logging tools.


When making an allocation, the OS just searches through the free fragments for one large enough to accommodate it -- splitting the fragment into a smaller one if possible:


As you can see, the fragment header becomes an allocation header, and both have the same alignment padding. In addition, the allocated bytes themselves have a separate alignment, creating even more padding if the allocated size isn't in a 32 byte alignment. The way these heaps fragment is initially very stack-like, but eventually it may become very tangled as it becomes more fragmented, and elements are freed for re-use. Because of this, the heaps may give you the illusion that some separately allocated structures in the game are contiguous when they are not -- such as the player data tables versus their GObj allocations.


In the end, this means that every allocation you make will round your allocation size up to the nearest 32-byte ceiling, and will add another 32 bytes for the metadata in the fragment header-- creating a minimum of 0x40 bytes claimed per allocation. In structures of a known size that is not aligned to 0x20 bytes, this means that the padding is exploitable -- like in the JObj example displayed in the image above. The allocation is 0x88 bytes, so 0x18 bytes of padding are generated when it's allocated, to keep the fragments cleanly aligned.


When designing your own allocated tables, it may be best to keep these overheads in mind and make the most of your 0x20-byte segments to prevent wasted fragmentation.


---

Melee uses an HSD library for memory management that is far more involved than the OS library; but wraps around it in a way that uses it at a low level. Because of this, you may see most of what the HSD library is doing by following the OS heap mechanics -- which becomes easy to do using the loggers in this code to observe this for yourself in the Dolphin log.

You may use the included 'alloc' functions to create and free allocations from the heap currently being used by the HSD Library (the one most allocations are made in). These functions do not require any prior installation -- so long as the functions are somewhere in your Mods Library for MCM to see.

<alloc> r3 = requested allocation size
Returns: r3 = allocation, r4...r12 are preserved

<alloc.r12> r12 = requested allocation size
Returns: r12 = allocation; r3...r11 are preserved
-- these allocators are very useful for creating new data tables inside of context-sensitive injection codes

<alloc.free> r3 = allocation to free
-- this is just a shortcut to the HSD_Free function, and doesn’t preserve any registers.

This may come in handy when logging information over time in a scene.



--- Default Loggers:

The code installs the default loggers mentioned at the top of the post in order to paint a better context of what’s happening in the background, as the scenes change, or when the game is setting up. They take advantage of some injection contexts needed to keep track of scene changes and heap creation/destruction, allowing for some useful timestamp features that can be reused in other (plugin) loggers.


Here’s a sample output from the vanilla game booting up and navigating to a training mode match:
Code:
# BOOT:
  -- OSArenaLo : has been written to by caller 803430a0  moving from ffffffff to 804f0c00; 0x804f0c01 bytes

  -- OSArenaLo : has been written to by caller 803430dc  moving from 804f0c00 to 804eec00; 0xffffe000 bytes

  -- OSArenaHi : has been written to by caller 803430fc  moving from 0 to 817f8ac0; 0x817f8ac0 bytes

  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 80344544
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 803446c8
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 803432b0
  -- OSArenaLo : has been read (r3 = 804eec00) by caller 803432b8
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 80342ee0
  -- OSArenaLo : has been read (r3 = 804eec00) by caller 80342ee8
  -- OSArenaLo : has been read (r3 = 804eec00) by caller 80342ef0
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 8015ff0c
  -- OSArenaLo : has been read (r3 = 804eec00) by caller 8015ff14
  -- OSArenaLo : has been written to by caller 80228c74  moving from 804eec00 to 804f0c00; 0x2000 bytes

  -- OSArenaLo : has been read (r3 = 804f0c00) by caller 80374fc0
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 80374fcc
  -- OSArenaLo : has been written to by caller 80375104  moving from 804f0c00 to 8061cc00; 0x12c000 bytes

  -- OSArenaLo : has been read (r3 = 8061cc00) by caller 803751bc
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 803751c8
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 80375210
  -- OSArenaLo : has been written to by caller 80375230  moving from 8061cc00 to 8065cc00; 0x40000 bytes

  -- OSArenaLo : has been read (r3 = 8065cc00) by caller 80375328
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 80375330
  -- OSArenaLo : has been read (r3 = 8065cc00) by caller 80375340
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 80375348
  -- OSArenaLo : has been written to by caller 80375380  moving from 8065cc00 to 8065cc40; 0x40 bytes

  -- OSHeap[0] : has been ALLOCATED on VIFrame 0x22: 0x80000 bytes claimed at 8065cc40 ... 806dcc40

  -- OSHeap[1] : has been ALLOCATED on VIFrame 0x22: 0x111be80 bytes claimed at 806dcc40 ... 817f8ac0

  -- OSArenaLo : has been written to by caller 803753e0  moving from 8065cc40 to 817f8ac0; 0x119be80 bytes

  -- OSHeap[1] : has been DESTROYED after 0x3d VIFrames: 0x111be80 bytes freed at  806dcc40 ... 817f8ac0

  -- OSHeap[1] : has been ALLOCATED on VIFrame 0x5f: 0x111b680 bytes claimed at 806dd440 ... 817f8ac0


# INTRO MOVIE:
  -- SCENE TRANSITION 1 : VIFrame 0x8d.a8c7 : Major 28 -> 18, Minor 00 -> 00

  -- OSHeap[1] : has been DESTROYED after 0x2e VIFrames: 0x111b680 bytes freed at  806dd440 ... 817f8ac0

  -- OSHeap[1] : has been ALLOCATED on VIFrame 0x8d: 0x5d7a80 bytes claimed at 80bd5c40 ... 811ad6c0


# TITLE:
  -- SCENE TRANSITION 2 : VIFrame 0x13d.0020 : Major 18 -> 00, Minor 00 -> 01

  -- OSHeap[1] : has been DESTROYED after 0xb0 VIFrames: 0x5d7a80 bytes freed at  80bd5c40 ... 811ad6c0

  -- OSHeap[1] : has been ALLOCATED on VIFrame 0x13d: 0x5d7a80 bytes claimed at 80bd5c40 ... 811ad6c0


# MAIN MENU:
  -- SCENE TRANSITION 3 : VIFrame 0x1cc.01ba : Major 00 -> 01, Minor 00 -> 00

  -- OSHeap[1] : has been DESTROYED after 0x8f VIFrames: 0x5d7a80 bytes freed at  80bd5c40 ... 811ad6c0

  -- OSHeap[1] : has been ALLOCATED on VIFrame 0x1cc: 0xc22e80 bytes claimed at 80bd5c40 ... 817f8ac0


# TRAINING CSS:
  -- SCENE TRANSITION 4 : VIFrame 0x2f1.000b : Major 01 -> 1c, Minor 00 -> 00

  -- OSHeap[1] : has been DESTROYED after 0x125 VIFrames: 0xc22e80 bytes freed at  80bd5c40 ... 817f8ac0

  -- OSHeap[1] : has been ALLOCATED on VIFrame 0x2f1: 0x5d7a80 bytes claimed at 80bd5c40 ... 811ad6c0


# TRAINING SSS:
  -- SCENE TRANSITION 5 : VIFrame 0x419.13fd : Major 1c -> 1c, Minor 00 -> 01

  -- OSHeap[1] : has been DESTROYED after 0x128 VIFrames: 0x5d7a80 bytes freed at  80bd5c40 ... 811ad6c0

  -- OSHeap[1] : has been ALLOCATED on VIFrame 0x419: 0x5d7a80 bytes claimed at 80bd5c40 ... 811ad6c0


# FINAL DESTINATION:
  -- SCENE TRANSITION 6 : VIFrame 0x4f2.3905 : Major 1c -> 1c, Minor 01 -> 02

  -- OSHeap[1] : has been DESTROYED after 0xd9 VIFrames: 0x5d7a80 bytes freed at  80bd5c40 ... 811ad6c0

  -- OSHeap[1] : has been ALLOCATED on VIFrame 0x4f2: 0x5d7a80 bytes claimed at 80bd5c40 ... 811ad6c0


As you can see, the game appears to use the arena early on in boot to manage some static allocations before finally setting up the heap descriptors and letting dynamic memory take over once the scenes begin. The scenes then manage wiping and re-instantiating the heap on minor transitions.


The absolute frame value is derived from the 32-bit VIRetrace frame count update -- which I believe is a special frame timer that uses the time-base registers to pad out each visual frame to a certain frames per second. The frames appear to elapse even when the game is loading, so it's good for timestamping processes in the logger.

The default logger injections use their contexts to keep track of the current scene's starting VIRetrace frame, and combines it with a time-base sample at the start of the scene to make a sub-frame fractional component to the frame counter, as a fixed point. This is technically 2 values, but together they make a 48-bit frame counter that can keep track of 16-bits worth of sub-frame timing for precision time stamping.

In addition, the scene transitions are given a 32-bit counter that can keep track of how many scenes have elapsed. This can be reused as another form of time-stamping, for keeping track of whether or not logged messages come from the same scene (heap).



--- Example Plugin Loggers:

Included with the DOL Mods are 2 examples of custom logger codes called the ‘PathToEntrynum’ and ‘Archive Object’ loggers. These can be combined with the default loggers to create a rich stream of information about what files are being checked for and/or loaded into the heap as scenes play out.

These are examples of loggers that can be made out of the tools available in the Function Modules. They rely on things that the core module installs however, so they must be installed with its injections.


The PathToEntrynum logger is used to capture the name of a path string being checked for on the DVD. The game has a predictable routine for doing this in a way that summarizes the string as an index placement of known file names on the disk, so the logger intercepts the search it makes in order to display information about what it finds.

You can watch this logger in the Dolphin log in order to learn about what files are being checked for on disk, what function called the check, and the address of the buffered string argument (before written to the volatile pathtoentrynum buffer). This can be useful for determining the names of files that are about to be loaded, and works well in tandem with the second example logger code. If a file is checked but not available on disk, you will see the entrynum logged as -1, aka 0xFFFFFFFF


The Archive Object logger is used to announce every Archive that’s created from a DAT file that’s been loaded into RAM, and has just undergone a relocation step (converting file offsets into RAM pointers). This is very useful for learning about which files are being prepared for use by the game, and can help you track how much space is being taken up by loaded files in the heap, where the various DAT sections have been relocated to in RAM, and other information that might be useful for reversing file data implementations.

Interestingly, some DAT files are not a part of an active heap fragment when they are used -- implying that they can be pre-loaded by some mechanism in the game. This can be seen when an Archive is logged, but the allocation is of the type ‘NOT DYNAMIC’.


The vanilla game booting up and navigating to a training mode match becomes much more richly populated with information when both of these plugin loggers are enabled alongside the default logger:
Code:
# BOOT:
  -- OSArenaLo : has been written to by caller 803430a0  moving from ffffffff to 804f0c00; 0x804f0c01 bytes

  -- OSArenaLo : has been written to by caller 803430dc  moving from 804f0c00 to 804eec00; 0xffffe000 bytes

  -- OSArenaHi : has been written to by caller 803430fc  moving from 0 to 817f8ac0; 0x817f8ac0 bytes

  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 80344544
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 803446c8
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 803432b0
  -- OSArenaLo : has been read (r3 = 804eec00) by caller 803432b8
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 80342ee0
  -- OSArenaLo : has been read (r3 = 804eec00) by caller 80342ee8
  -- OSArenaLo : has been read (r3 = 804eec00) by caller 80342ef0
  PathToEntrynum: on Frame 0x22.01de Scene 00000 : 8015fdc0 gets r3= 0xffffffff <- 803d4ac8 - /develop.ini
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 8015ff0c
  -- OSArenaLo : has been read (r3 = 804eec00) by caller 8015ff14
  -- OSArenaLo : has been written to by caller 80228c74  moving from 804eec00 to 804f0c00; 0x2000 bytes

  -- OSArenaLo : has been read (r3 = 804f0c00) by caller 80374fc0
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 80374fcc
  -- OSArenaLo : has been written to by caller 80375104  moving from 804f0c00 to 8061cc00; 0x12c000 bytes

  -- OSArenaLo : has been read (r3 = 8061cc00) by caller 803751bc
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 803751c8
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 80375210
  -- OSArenaLo : has been written to by caller 80375230  moving from 8061cc00 to 8065cc00; 0x40000 bytes

  -- OSArenaLo : has been read (r3 = 8065cc00) by caller 80375328
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 80375330
  -- OSArenaLo : has been read (r3 = 8065cc00) by caller 80375340
  -- OSArenaHi : has been read (r3 = 817f8ac0) by caller 80375348
  -- OSArenaLo : has been written to by caller 80375380  moving from 8065cc00 to 8065cc40; 0x40 bytes

  -- OSHeap[0] : has been ALLOCATED on VIFrame 0x22: 0x80000 bytes claimed at 8065cc40 ... 806dcc40

  -- OSHeap[1] : has been ALLOCATED on VIFrame 0x22: 0x111be80 bytes claimed at 806dcc40 ... 817f8ac0

  -- OSArenaLo : has been written to by caller 803753e0  moving from 8065cc40 to 817f8ac0; 0x119be80 bytes

  PathToEntrynum: on Frame 0x1.0e98 Scene 00000 : 8015fbcc gets r3= 0x04b0 <- 803d4abc - /usa.ini
  PathToEntrynum: on Frame 0x1.14bf Scene 00000 : 80388a30 gets r3= 0x00a9 <- 803bb340 - /audio/us/main.ssm
  PathToEntrynum: on Frame 0x33.ab31 Scene 00000 : 8038da8c gets r3= 0x00be <- 803bb340 - /audio/us/smash2.sem
  PathToEntrynum: on Frame 0x3b.c9ec Scene 00000 : 80388a30 gets r3= 0x00b1 <- 803bb340 - /audio/us/nr_name.ssm
  PathToEntrynum: on Frame 0x48.6394 Scene 00000 : 80388a30 gets r3= 0x00b9 <- 803bb340 - /audio/us/pokemon.ssm
  PathToEntrynum: on Frame 0x55.3edd Scene 00000 : 80388a30 gets r3= 0x0095 <- 803bb340 - /audio/us/end.ssm
  PathToEntrynum: on Frame 0x59.102d Scene 00000 : 80388a30 gets r3= 0x00b3 <- 803bb340 - /audio/us/nr_title.ssm
  -- OSHeap[1] : has been DESTROYED after 0x3d VIFrames: 0x111be80 bytes freed at  806dcc40 ... 817f8ac0

  -- OSHeap[1] : has been ALLOCATED on VIFrame 0x5f: 0x111b680 bytes claimed at 806dd440 ... 817f8ac0

  PathToEntrynum: on Frame 0x5f.85b7 Scene 00028 : 80018498 gets r3= 0x01ec <- 803ba6bc - LbRb.dat
  PathToEntrynum: on Frame 0x5f.8bef Scene 00028 : 800181b8 gets r3= 0x01ec <- 803ba2a0 - LbRb.dat
  - ARCHIVE DATA   : 806dcc60 : 0x340 bytes
  - ARCHIVE RELOCS : 806dcfa0 : 40 pointer(s)
  - ARCHIVE NODES  : 806dd040 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 806dd048 : 0xd byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 806dd060
  - NOT_DYNAMIC    : 806DCC40 : is not a part of any active OSHeap fragments...

  PathToEntrynum: on Frame 0x65.3f50 Scene 00028 : 80016400 gets r3= 0x01e9 <- 803bac94 - LbMcGame.usd
  PathToEntrynum: on Frame 0x65.44c3 Scene 00028 : 800166bc gets r3= 0x01e9 <- 803bac94 - LbMcGame.usd
  ARCHIVE FILE     : 806f51c0 : LbMcGame.usd, 0x4ebe bytes at 806f02e0 : Frame 0x68.19df of Scene 00028
  - ARCHIVE DATA   : 806f0300 : 0x4e14 bytes
  - ARCHIVE RELOCS : 806f5114 : 4 pointer(s)
  - ARCHIVE NODES  : 806f5124 : 5 symbol(s) nodes
  - ARCHIVE SYMSTR : 806f514c : 0x52 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 806f51c0
  - ALLOCATED      : 806F02E0 : offset 0 of a 0x4ec0-byte allocation at 806f02e0 in OSHeap[1]
            - alloc was called by the instruction at 80016e40 on Frame 0x65.443f of Scene 00028

  PathToEntrynum: on Frame 0x68.3409 Scene 00028 : 80016400 gets r3= 0x0216 <- 804d3820 - NtMemAc.usd
  PathToEntrynum: on Frame 0x68.3983 Scene 00028 : 800166bc gets r3= 0x0216 <- 804d3820 - NtMemAc.usd
  ARCHIVE FILE     : 806f62a0 : NtMemAc.usd, 0x103f bytes at 806f5240 : Frame 0x68.43c4 of Scene 00028
  - ARCHIVE DATA   : 806f5260 : 0xeec bytes
  - ARCHIVE RELOCS : 806f614c : 69 pointer(s)
  - ARCHIVE NODES  : 806f6260 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 806f6268 : 0x17 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 806f62a0
  - ALLOCATED      : 806F5240 : offset 0 of a 0x1040-byte allocation at 806f5240 in OSHeap[1]
            - alloc was called by the instruction at 80016e40 on Frame 0x68.38ec of Scene 00028

  PathToEntrynum: on Frame 0x68.5f7f Scene 00028 : 80016400 gets r3= 0x0217 <- 803dd4f4 - NtMsgWin.dat
  PathToEntrynum: on Frame 0x68.6505 Scene 00028 : 800166bc gets r3= 0x0217 <- 803dd4f4 - NtMsgWin.dat
  ARCHIVE FILE     : 806f91c0 : NtMsgWin.dat, 0x2e6f bytes at 806f6320 : Frame 0x6b.3aef of Scene 00028
  - ARCHIVE DATA   : 806f6340 : 0x2b94 bytes
  - ARCHIVE RELOCS : 806f8ed4 : 167 pointer(s)
  - ARCHIVE NODES  : 806f9170 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 806f9178 : 0x17 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 806f91c0
  - ALLOCATED      : 806F6320 : offset 0 of a 0x2e80-byte allocation at 806f6320 in OSHeap[1]
            - alloc was called by the instruction at 80016e40 on Frame 0x68.646e of Scene 00028

  PathToEntrynum: on Frame 0x6b.55d5 Scene 00028 : 80016400 gets r3= 0x0335 <- 803dd51c - SdMsgBox.usd
  PathToEntrynum: on Frame 0x6b.5d56 Scene 00028 : 800166bc gets r3= 0x0335 <- 803dd51c - SdMsgBox.usd
  ARCHIVE FILE     : 806f9da0 : SdMsgBox.usd, 0xb3c bytes at 806f9240 : Frame 0x6e.a83a of Scene 00028
  - ARCHIVE DATA   : 806f9260 : 0xaa0 bytes
  - ARCHIVE RELOCS : 806f9d00 : 25 pointer(s)
  - ARCHIVE NODES  : 806f9d64 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 806f9d6c : 0x10 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 806f9da0
  - ALLOCATED      : 806F9240 : offset 0 of a 0xb40-byte allocation at 806f9240 in OSHeap[1]
            - alloc was called by the instruction at 80016c0c on Frame 0x6b.5c3f of Scene 00028


# INTRO MOVIE:
  -- SCENE TRANSITION 1 : VIFrame 0x8e.ad0a : Major 28 -> 18, Minor 00 -> 00

  -- OSHeap[1] : has been DESTROYED after 0x2f VIFrames: 0x111b680 bytes freed at  806dd440 ... 817f8ac0

  -- OSHeap[1] : has been ALLOCATED on VIFrame 0x8e: 0x5d7a80 bytes claimed at 80bd5c40 ... 811ad6c0

  PathToEntrynum: on Frame 0.bb25 Scene 10018 : 80018620 gets r3= 0x01ec <- 803ba6bc - LbRb.dat
  PathToEntrynum: on Frame 0.bfea Scene 10018 : 800186d0 gets r3= 0x00ef <- 803ba6c8 - EfMnData.dat
  PathToEntrynum: on Frame 0.c4ce Scene 10018 : 800186d0 gets r3= 0x00d7 <- 803ba6d8 - EfCoData.dat
  PathToEntrynum: on Frame 0.ca2d Scene 10018 : 800186d0 gets r3= 0x01e5 <- 804d37e4 - ItCo.usd
  PathToEntrynum: on Frame 0.cf4d Scene 10018 : 800186d0 gets r3= 0x01cc <- 804d37ec - IfAll.usd
  PathToEntrynum: on Frame 0x1.0093 Scene 10018 : 80018620 gets r3= 0x01ec <- 803ba6bc - LbRb.dat
  PathToEntrynum: on Frame 0x1.06a8 Scene 10018 : 800181b8 gets r3= 0x01ec <- 803ba2a0 - LbRb.dat
  PathToEntrynum: on Frame 0x1.0d5e Scene 10018 : 80016400 gets r3= 0x017e <- 804eeb00 - GmTtAll.usd
  PathToEntrynum: on Frame 0x1.12be Scene 10018 : 800166bc gets r3= 0x017e <- 804eeb00 - GmTtAll.usd
  ARCHIVE FILE     : 80c20200 : GmTtAll.usd, 0x43721 bytes at 80bdcaa0 : Frame 0xc.06b3 of Scene 10018
  - ARCHIVE DATA   : 80bdcac0 : 0x41660 bytes
  - ARCHIVE RELOCS : 80c1e120 : 1992 pointer(s)
  - ARCHIVE NODES  : 80c20040 : 13 symbol(s) nodes
  - ARCHIVE SYMSTR : 80c200a8 : 0x119 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80c20200
  - ALLOCATED      : 80BDCAA0 : offset 0 of a 0x43740-byte allocation at 80bdcaa0 in OSHeap[1]
            - alloc was called by the instruction at 80016ce8 on Frame 0x1.1235 of Scene 10018

  PathToEntrynum: on Frame 0xc.218c Scene 10018 : 8038e90c gets r3= 0x006b <- 803bb300 - /audio/opening.hps
  PathToEntrynum: on Frame 0xc.2914 Scene 10018 : 8001eb38 gets r3= 0x0212 <- 803dbfcc - MvOpen.mth
 

# TITLE:
  -- SCENE TRANSITION 2 : VIFrame 0x135.0020 : Major 18 -> 00, Minor 00 -> 01

  -- OSHeap[1] : has been DESTROYED after 0xa7 VIFrames: 0x5d7a80 bytes freed at  80bd5c40 ... 811ad6c0

  -- OSHeap[1] : has been ALLOCATED on VIFrame 0x135: 0x5d7a80 bytes claimed at 80bd5c40 ... 811ad6c0

  PathToEntrynum: on Frame 0.0e41 Scene 20000 : 80018620 gets r3= 0x01ec <- 803ba6bc - LbRb.dat
  PathToEntrynum: on Frame 0.1305 Scene 20000 : 800186d0 gets r3= 0x00ef <- 803ba6c8 - EfMnData.dat
  PathToEntrynum: on Frame 0.17e5 Scene 20000 : 800186d0 gets r3= 0x00d7 <- 803ba6d8 - EfCoData.dat
  PathToEntrynum: on Frame 0.1d43 Scene 20000 : 800186d0 gets r3= 0x01e5 <- 804d37e4 - ItCo.usd
  PathToEntrynum: on Frame 0.2264 Scene 20000 : 800186d0 gets r3= 0x01cc <- 804d37ec - IfAll.usd
  PathToEntrynum: on Frame 0.2d4c Scene 20000 : 800181b8 gets r3= 0x01ec <- 803ba2a0 - LbRb.dat
  PathToEntrynum: on Frame 0.3288 Scene 20000 : 80016400 gets r3= 0x017e <- 804eeb14 - GmTtAll.usd
  PathToEntrynum: on Frame 0.37e9 Scene 20000 : 800166bc gets r3= 0x017e <- 804eeb14 - GmTtAll.usd
  ARCHIVE FILE     : 80c20200 : GmTtAll.usd, 0x43721 bytes at 80bdcaa0 : Frame 0x17.88f6 of Scene 20000
  - ARCHIVE DATA   : 80bdcac0 : 0x41660 bytes
  - ARCHIVE RELOCS : 80c1e120 : 1992 pointer(s)
  - ARCHIVE NODES  : 80c20040 : 13 symbol(s) nodes
  - ARCHIVE SYMSTR : 80c200a8 : 0x119 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80c20200
  - ALLOCATED      : 80BDCAA0 : offset 0 of a 0x43740-byte allocation at 80bdcaa0 in OSHeap[1]
            - alloc was called by the instruction at 80016ce8 on Frame 0.3760 of Scene 20000

  PathToEntrynum: on Frame 0x18.0fb4 Scene 20000 : 80018620 gets r3= 0x01ec <- 803ba6bc - LbRb.dat
  PathToEntrynum: on Frame 0x18.1484 Scene 20000 : 800186d0 gets r3= 0x00ef <- 803ba6c8 - EfMnData.dat
  PathToEntrynum: on Frame 0x18.1970 Scene 20000 : 800186d0 gets r3= 0x00d7 <- 803ba6d8 - EfCoData.dat
  PathToEntrynum: on Frame 0x18.1ed7 Scene 20000 : 800186d0 gets r3= 0x01e5 <- 804d37e4 - ItCo.usd
  PathToEntrynum: on Frame 0x18.23ff Scene 20000 : 800186d0 gets r3= 0x01cc <- 804d37ec - IfAll.usd
  PathToEntrynum: on Frame 0x18.293a Scene 20000 : 80017920 gets r3= 0x0192 <- 803e17f4 - /GrKg.dat
  PathToEntrynum: on Frame 0x18.2ed8 Scene 20000 : 80017920 gets r3= 0x0268 <- 803d28e8 - PlGw.dat
  PathToEntrynum: on Frame 0x18.346e Scene 20000 : 80017920 gets r3= 0x026b <- 803d2904 - PlGwNr.dat
  PathToEntrynum: on Frame 0x18.3a19 Scene 20000 : 80017920 gets r3= 0x0269 <- 803d292c - PlGwAJ.dat
  PathToEntrynum: on Frame 0x18.3fc4 Scene 20000 : 80017920 gets r3= 0x026c <- 803ca308 - PlKb.dat
  PathToEntrynum: on Frame 0x18.4585 Scene 20000 : 80017920 gets r3= 0x0292 <- 803ca320 - PlKbNr.dat
  PathToEntrynum: on Frame 0x18.4a65 Scene 20000 : 80017920 gets r3= 0x00de <- 803bfe18 - EfKbData.dat
  PathToEntrynum: on Frame 0x18.5028 Scene 20000 : 80017920 gets r3= 0x026d <- 803ca4e0 - PlKbAJ.dat
  PathToEntrynum: on Frame 0x18.55e9 Scene 20000 : 80017920 gets r3= 0x027b <- 803ca954 - PlKbCpGw.dat
  PathToEntrynum: on Frame 0x18.5be1 Scene 20000 : 80017920 gets r3= 0x0295 <- 803cb358 - PlKbNrCpGw.dat
  PathToEntrynum: on Frame 0x18.61d5 Scene 20000 : 80017920 gets r3= 0x027c <- 803ca698 - PlKbCpKp.dat
  PathToEntrynum: on Frame 0x18.66d4 Scene 20000 : 80017920 gets r3= 0x00e4 <- 803c01a8 - EfKbKp.dat
  PathToEntrynum: on Frame 0x18.6c88 Scene 20000 : 80017920 gets r3= 0x0273 <- 803ca648 - PlKbCpCa.dat
  PathToEntrynum: on Frame 0x18.717d Scene 20000 : 80017920 gets r3= 0x00dd <- 803c015c - EfKbCa.dat
  PathToEntrynum: on Frame 0x18.7764 Scene 20000 : 80017920 gets r3= 0x02a7 <- 803cf0a0 - PlKp.dat
  PathToEntrynum: on Frame 0x18.7d3a Scene 20000 : 80017920 gets r3= 0x02ac <- 803cf0b8 - PlKpNr.dat
  PathToEntrynum: on Frame 0x18.8228 Scene 20000 : 80017920 gets r3= 0x00ec <- 803bff14 - EfKpData.dat
  PathToEntrynum: on Frame 0x18.8828 Scene 20000 : 80017920 gets r3= 0x02a8 <- 803cf1e0 - PlKpAJ.dat
  PathToEntrynum: on Frame 0x18.8d92 Scene 20000 : 80017920 gets r3= 0x021e <- 803c7598 - PlCa.dat
  PathToEntrynum: on Frame 0x18.92ea Scene 20000 : 80017920 gets r3= 0x0224 <- 803c75b4 - PlCaNr.dat
  PathToEntrynum: on Frame 0x18.97c4 Scene 20000 : 80017920 gets r3= 0x00d6 <- 803bfdf4 - EfCaData.dat
  PathToEntrynum: on Frame 0x18.9d46 Scene 20000 : 80017920 gets r3= 0x021f <- 803c76a0 - PlCaAJ.dat
  PathToEntrynum: on Frame 0x18.a432 Scene 20000 : 80388a30 gets r3= 0x008e <- 803bb340 - /audio/us/captain.ssm
  PathToEntrynum: on Frame 0x31.79d5 Scene 20000 : 80388a30 gets r3= 0x00a1 <- 803bb340 - /audio/us/kirby.ssm
  PathToEntrynum: on Frame 0x4d.341f Scene 20000 : 80388a30 gets r3= 0x00a5 <- 803bb340 - /audio/us/koopa.ssm
  PathToEntrynum: on Frame 0x6c.09a2 Scene 20000 : 80388a30 gets r3= 0x009f <- 803bb340 - /audio/us/gw.ssm
  PathToEntrynum: on Frame 0x83.897c Scene 20000 : 80388a30 gets r3= 0x00a4 <- 803bb340 - /audio/us/kongo.ssm


# MAIN MENU:
  -- SCENE TRANSITION 3 : VIFrame 0x1e6.01ba : Major 00 -> 01, Minor 00 -> 00

  -- OSHeap[1] : has been DESTROYED after 0xb1 VIFrames: 0x5d7a80 bytes freed at  80bd5c40 ... 811ad6c0

  -- OSHeap[1] : has been ALLOCATED on VIFrame 0x1e6: 0xc22e80 bytes claimed at 80bd5c40 ... 817f8ac0

  PathToEntrynum: on Frame 0.0ff0 Scene 30001 : 800184fc gets r3= 0x01ec <- 803ba6bc - LbRb.dat
  PathToEntrynum: on Frame 0.14b4 Scene 30001 : 800185ac gets r3= 0x00ef <- 803ba6c8 - EfMnData.dat
  PathToEntrynum: on Frame 0.1993 Scene 30001 : 800185ac gets r3= 0x00d7 <- 803ba6d8 - EfCoData.dat
  PathToEntrynum: on Frame 0.1eee Scene 30001 : 800185ac gets r3= 0x01e5 <- 804d37e4 - ItCo.usd
  PathToEntrynum: on Frame 0.2409 Scene 30001 : 800185ac gets r3= 0x01cc <- 804d37ec - IfAll.usd
  PathToEntrynum: on Frame 0.294e Scene 30001 : 80016400 gets r3= 0x01e9 <- 803bac94 - LbMcGame.usd
  PathToEntrynum: on Frame 0.2eb5 Scene 30001 : 800166bc gets r3= 0x01e9 <- 803bac94 - LbMcGame.usd
  ARCHIVE FILE     : 80beb3a0 : LbMcGame.usd, 0x4ebe bytes at 80be64c0 : Frame 0x8.9b4b of Scene 30001
  - ARCHIVE DATA   : 80be64e0 : 0x4e14 bytes
  - ARCHIVE RELOCS : 80beb2f4 : 4 pointer(s)
  - ARCHIVE NODES  : 80beb304 : 5 symbol(s) nodes
  - ARCHIVE SYMSTR : 80beb32c : 0x52 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80beb3a0
  - ALLOCATED      : 80BE64C0 : offset 0 of a 0x4ec0-byte allocation at 80be64c0 in OSHeap[1]
            - alloc was called by the instruction at 80016e40 on Frame 0.2e32 of Scene 30001

  PathToEntrynum: on Frame 0x8.b560 Scene 30001 : 80016400 gets r3= 0x0216 <- 804d3820 - NtMemAc.usd
  PathToEntrynum: on Frame 0x8.bacf Scene 30001 : 800166bc gets r3= 0x0216 <- 804d3820 - NtMemAc.usd
  ARCHIVE FILE     : 80bec480 : NtMemAc.usd, 0x103f bytes at 80beb420 : Frame 0x14.697f of Scene 30001
  - ARCHIVE DATA   : 80beb440 : 0xeec bytes
  - ARCHIVE RELOCS : 80bec32c : 69 pointer(s)
  - ARCHIVE NODES  : 80bec440 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 80bec448 : 0x17 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80bec480
  - ALLOCATED      : 80BEB420 : offset 0 of a 0x1040-byte allocation at 80beb420 in OSHeap[1]
            - alloc was called by the instruction at 80016e40 on Frame 0x8.ba38 of Scene 30001

  PathToEntrynum: on Frame 0x14.84ff Scene 30001 : 80016400 gets r3= 0x01eb <- 803bad90 - LbMcSnap.usd
  PathToEntrynum: on Frame 0x14.8a76 Scene 30001 : 800166bc gets r3= 0x01eb <- 803bad90 - LbMcSnap.usd
  ARCHIVE FILE     : 80c2d460 : LbMcSnap.usd, 0x1e7c bytes at 80c2b5c0 : Frame 0x23.1022 of Scene 30001
  - ARCHIVE DATA   : 80c2b5e0 : 0x1e0c bytes
  - ARCHIVE RELOCS : 80c2d3ec : 2 pointer(s)
  - ARCHIVE NODES  : 80c2d3f4 : 3 symbol(s) nodes
  - ARCHIVE SYMSTR : 80c2d40c : 0x30 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80c2d460
  - ALLOCATED      : 80C2B5C0 : offset 0 of a 0x1e80-byte allocation at 80c2b5c0 in OSHeap[1]
            - alloc was called by the instruction at 80016e40 on Frame 0x14.89f0 of Scene 30001

  PathToEntrynum: on Frame 0x23.2a47 Scene 30001 : 800184fc gets r3= 0x01ec <- 803ba6bc - LbRb.dat
  PathToEntrynum: on Frame 0x23.2f18 Scene 30001 : 800185ac gets r3= 0x00ef <- 803ba6c8 - EfMnData.dat
  PathToEntrynum: on Frame 0x23.3404 Scene 30001 : 800185ac gets r3= 0x00d7 <- 803ba6d8 - EfCoData.dat
  PathToEntrynum: on Frame 0x23.396a Scene 30001 : 800185ac gets r3= 0x01e5 <- 804d37e4 - ItCo.usd
  PathToEntrynum: on Frame 0x23.3e93 Scene 30001 : 800185ac gets r3= 0x01cc <- 804d37ec - IfAll.usd
  PathToEntrynum: on Frame 0x23.463b Scene 30001 : 800181b8 gets r3= 0x01ec <- 803ba2a0 - LbRb.dat
  PathToEntrynum: on Frame 0x23.4b84 Scene 30001 : 80016400 gets r3= 0x01f1 <- 804d4b78 - MnMaAll.usd
  PathToEntrynum: on Frame 0x23.50ef Scene 30001 : 800166bc gets r3= 0x01f1 <- 804d4b78 - MnMaAll.usd
  ARCHIVE FILE     : 81306f00 : MnMaAll.usd, 0x20e32f bytes at 810f8ba0 : Frame 0x54.625c of Scene 30001
  - ARCHIVE DATA   : 810f8bc0 : 0x200cd8 bytes
  - ARCHIVE RELOCS : 812f9898 : 11507 pointer(s)
  - ARCHIVE NODES  : 81304c64 : 234 symbol(s) nodes
  - ARCHIVE SYMSTR : 813053b4 : 0x1b1b byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 81306f00
  - ALLOCATED      : 810F8BA0 : offset 0 of a 0x20e340-byte allocation at 810f8ba0 in OSHeap[1]
            - alloc was called by the instruction at 80016ce8 on Frame 0x23.5069 of Scene 30001

  PathToEntrynum: on Frame 0x54.9000 Scene 30001 : 80016400 gets r3= 0x0333 <- 803ec5c0 - SdMenu.usd
  PathToEntrynum: on Frame 0x54.95ed Scene 30001 : 800166bc gets r3= 0x0333 <- 803ec5c0 - SdMenu.usd
  ARCHIVE FILE     : 8131c5c0 : SdMenu.usd, 0x15605 bytes at 81306f80 : Frame 0x55.c76c of Scene 30001
  - ARCHIVE DATA   : 81306fa0 : 0x13cc0 bytes
  - ARCHIVE RELOCS : 8131ac60 : 1604 pointer(s)
  - ARCHIVE NODES  : 8131c570 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 8131c578 : 0xd byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 8131c5c0
  - ALLOCATED      : 81306F80 : offset 0 of a 0x15620-byte allocation at 81306f80 in OSHeap[1]
            - alloc was called by the instruction at 80016c0c on Frame 0x54.94d8 of Scene 30001

  PathToEntrynum: on Frame 0x56.125f Scene 30001 : 80016400 gets r3= 0x0341 <- 803ec5e8 - SdToy.dat
  PathToEntrynum: on Frame 0x56.184b Scene 30001 : 800166bc gets r3= 0x0341 <- 803ec5e8 - SdToy.dat
  ARCHIVE FILE     : 81325760 : SdToy.dat, 0x90e8 bytes at 8131c640 : Frame 0x59.6543 of Scene 30001
  - ARCHIVE DATA   : 8131c660 : 0x8bc0 bytes
  - ARCHIVE RELOCS : 81325220 : 317 pointer(s)
  - ARCHIVE NODES  : 81325714 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 8132571c : 0xc byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 81325760
  - ALLOCATED      : 8131C640 : offset 0 of a 0x9100-byte allocation at 8131c640 in OSHeap[1]
            - alloc was called by the instruction at 80016c0c on Frame 0x56.172c of Scene 30001

  PathToEntrynum: on Frame 0x59.8053 Scene 30001 : 80016400 gets r3= 0x00fa <- 803df660 - GmEvent.dat
  PathToEntrynum: on Frame 0x59.854f Scene 30001 : 800166bc gets r3= 0x00fa <- 803df660 - GmEvent.dat
  ARCHIVE FILE     : 81328260 : GmEvent.dat, 0x2a44 bytes at 813257e0 : Frame 0x5a.339a of Scene 30001
  - ARCHIVE DATA   : 81325800 : 0x2598 bytes
  - ARCHIVE RELOCS : 81327d98 : 283 pointer(s)
  - ARCHIVE NODES  : 81328204 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 8132820c : 0x18 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 81328260
  - ALLOCATED      : 813257E0 : offset 0 of a 0x2a60-byte allocation at 813257e0 in OSHeap[1]
            - alloc was called by the instruction at 80016ce8 on Frame 0x59.8537 of Scene 30001

  PathToEntrynum: on Frame 0x5a.4dbb Scene 30001 : 80016400 gets r3= 0x01e6 <- 803bc8fc - LbAd.dat
  PathToEntrynum: on Frame 0x5a.52fa Scene 30001 : 800166bc gets r3= 0x01e6 <- 803bc8fc - LbAd.dat
  ARCHIVE FILE     : 8132bba0 : LbAd.dat, 0x3898 bytes at 813282e0 : Frame 0x5b.01e7 of Scene 30001
  - ARCHIVE DATA   : 81328300 : 0x3670 bytes
  - ARCHIVE RELOCS : 8132b970 : 124 pointer(s)
  - ARCHIVE NODES  : 8132bb60 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 8132bb68 : 0x10 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 8132bba0
  - ALLOCATED      : 813282E0 : offset 0 of a 0x38a0-byte allocation at 813282e0 in OSHeap[1]
            - alloc was called by the instruction at 80016ce8 on Frame 0x5a.527c of Scene 30001

  PathToEntrynum: on Frame 0x5c.0110 Scene 30001 : 8038e90c gets r3= 0x0059 <- 803bb300 - /audio/menu3.hps


# TRAINING CSS:
  -- SCENE TRANSITION 4 : VIFrame 0x30e.000b : Major 01 -> 1c, Minor 00 -> 00

  -- OSHeap[1] : has been DESTROYED after 0x128 VIFrames: 0xc22e80 bytes freed at  80bd5c40 ... 817f8ac0

  -- OSHeap[1] : has been ALLOCATED on VIFrame 0x30e: 0x5d7a80 bytes claimed at 80bd5c40 ... 811ad6c0

  PathToEntrynum: on Frame 0.0fbb Scene 4001c : 80018620 gets r3= 0x01ec <- 803ba6bc - LbRb.dat
  PathToEntrynum: on Frame 0.147f Scene 4001c : 800186d0 gets r3= 0x00ef <- 803ba6c8 - EfMnData.dat
  PathToEntrynum: on Frame 0.195e Scene 4001c : 800186d0 gets r3= 0x00d7 <- 803ba6d8 - EfCoData.dat
  PathToEntrynum: on Frame 0.1eb8 Scene 4001c : 800186d0 gets r3= 0x01e5 <- 804d37e4 - ItCo.usd
  PathToEntrynum: on Frame 0.23d4 Scene 4001c : 800186d0 gets r3= 0x01cc <- 804d37ec - IfAll.usd
  PathToEntrynum: on Frame 0.2a07 Scene 4001c : 800181b8 gets r3= 0x01ec <- 803ba2a0 - LbRb.dat
  PathToEntrynum: on Frame 0.2f3f Scene 4001c : 80016400 gets r3= 0x01e9 <- 803bac94 - LbMcGame.usd
  PathToEntrynum: on Frame 0.34a7 Scene 4001c : 800166bc gets r3= 0x01e9 <- 803bac94 - LbMcGame.usd
  ARCHIVE FILE     : 80beb5c0 : LbMcGame.usd, 0x4ebe bytes at 80be66e0 : Frame 0x6.19a9 of Scene 4001c
  - ARCHIVE DATA   : 80be6700 : 0x4e14 bytes
  - ARCHIVE RELOCS : 80beb514 : 4 pointer(s)
  - ARCHIVE NODES  : 80beb524 : 5 symbol(s) nodes
  - ARCHIVE SYMSTR : 80beb54c : 0x52 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80beb5c0
  - ALLOCATED      : 80BE66E0 : offset 0 of a 0x4ec0-byte allocation at 80be66e0 in OSHeap[1]
            - alloc was called by the instruction at 80016e40 on Frame 0.3424 of Scene 4001c

  PathToEntrynum: on Frame 0x6.33be Scene 4001c : 80016400 gets r3= 0x0216 <- 804d3820 - NtMemAc.usd
  PathToEntrynum: on Frame 0x6.392d Scene 4001c : 800166bc gets r3= 0x0216 <- 804d3820 - NtMemAc.usd
  ARCHIVE FILE     : 80bec6a0 : NtMemAc.usd, 0x103f bytes at 80beb640 : Frame 0x6.4363 of Scene 4001c
  - ARCHIVE DATA   : 80beb660 : 0xeec bytes
  - ARCHIVE RELOCS : 80bec54c : 69 pointer(s)
  - ARCHIVE NODES  : 80bec660 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 80bec668 : 0x17 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80bec6a0
  - ALLOCATED      : 80BEB640 : offset 0 of a 0x1040-byte allocation at 80beb640 in OSHeap[1]
            - alloc was called by the instruction at 80016e40 on Frame 0x6.3896 of Scene 4001c

  PathToEntrynum: on Frame 0x6.5ebf Scene 4001c : 80388a30 gets r3= 0x00b2 <- 803bb340 - /audio/us/nr_select.ssm
  PathToEntrynum: on Frame 0x10.9a2a Scene 4001c : 80016400 gets r3= 0x01f4 <- 803f1154 - MnSlChr.usd
  PathToEntrynum: on Frame 0x10.9f98 Scene 4001c : 800166bc gets r3= 0x01f4 <- 803f1154 - MnSlChr.usd
  ARCHIVE FILE     : 80f8efa0 : MnSlChr.usd, 0x3a2849 bytes at 80bec720 : Frame 0x65.6da4 of Scene 4001c
  - ARCHIVE DATA   : 80bec740 : 0x397c24 bytes
  - ARCHIVE RELOCS : 80f84364 : 11002 pointer(s)
  - ARCHIVE NODES  : 80f8ef4c : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 80f8ef54 : 0x15 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80f8efa0
  - ALLOCATED      : 80BEC720 : offset 0 of a 0x3a2860-byte allocation at 80bec720 in OSHeap[1]
            - alloc was called by the instruction at 80016c0c on Frame 0x10.9f0f of Scene 4001c

  PathToEntrynum: on Frame 0x65.89cd Scene 4001c : 80016400 gets r3= 0x01ef <- 803f1160 - MnExtAll.usd
  PathToEntrynum: on Frame 0x65.8f43 Scene 4001c : 800166bc gets r3= 0x01ef <- 803f1160 - MnExtAll.usd
  ARCHIVE FILE     : 810d0240 : MnExtAll.usd, 0x1411f9 bytes at 80f8f020 : Frame 0x7c.64d8 of Scene 4001c
  - ARCHIVE DATA   : 80f8f040 : 0x139838 bytes
  - ARCHIVE RELOCS : 810c8878 : 6783 pointer(s)
  - ARCHIVE NODES  : 810cf274 : 105 symbol(s) nodes
  - ARCHIVE SYMSTR : 810cf5bc : 0xc5d byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 810d0240
  - ALLOCATED      : 80F8F020 : offset 0 of a 0x141200-byte allocation at 80f8f020 in OSHeap[1]
            - alloc was called by the instruction at 80016c0c on Frame 0x65.8ebe of Scene 4001c

  PathToEntrynum: on Frame 0x7c.8044 Scene 4001c : 80016400 gets r3= 0x033d <- 803f11a4 - SdSlChr.usd
  PathToEntrynum: on Frame 0x7c.8646 Scene 4001c : 800166bc gets r3= 0x033d <- 803f11a4 - SdSlChr.usd
  ARCHIVE FILE     : 810d2d80 : SdSlChr.usd, 0x2a94 bytes at 810d02c0 : Frame 0x7d.3923 of Scene 4001c
  - ARCHIVE DATA   : 810d02e0 : 0x2900 bytes
  - ARCHIVE RELOCS : 810d2be0 : 87 pointer(s)
  - ARCHIVE NODES  : 810d2d3c : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 810d2d44 : 0x10 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 810d2d80
  - ALLOCATED      : 810D02C0 : offset 0 of a 0x2aa0-byte allocation at 810d02c0 in OSHeap[1]
            - alloc was called by the instruction at 80016c0c on Frame 0x7c.8529 of Scene 4001c

  PathToEntrynum: on Frame 0x7d.bb4f Scene 4001c : 80018620 gets r3= 0x01ec <- 803ba6bc - LbRb.dat
  PathToEntrynum: on Frame 0x7d.c020 Scene 4001c : 800186d0 gets r3= 0x00ef <- 803ba6c8 - EfMnData.dat
  PathToEntrynum: on Frame 0x7d.c50c Scene 4001c : 800186d0 gets r3= 0x00d7 <- 803ba6d8 - EfCoData.dat
  PathToEntrynum: on Frame 0x7d.ca73 Scene 4001c : 800186d0 gets r3= 0x01e5 <- 804d37e4 - ItCo.usd
  PathToEntrynum: on Frame 0x7d.cf9b Scene 4001c : 800186d0 gets r3= 0x01cc <- 804d37ec - IfAll.usd
  PathToEntrynum: on Frame 0x7d.d578 Scene 4001c : 80017920 gets r3= 0x02bd <- 803d4070 - PlMh.dat
  PathToEntrynum: on Frame 0x7d.db51 Scene 4001c : 80017920 gets r3= 0x02bf <- 803d4090 - PlMhNr.dat
  PathToEntrynum: on Frame 0x7d.e140 Scene 4001c : 80017920 gets r3= 0x02be <- 803d40b8 - PlMhAJ.dat
  PathToEntrynum: on Frame 0x7d.e76c Scene 4001c : 80017920 gets r3= 0x0301 <- 803d02f0 - PlPr.dat
  PathToEntrynum: on Frame 0x7d.ed89 Scene 4001c : 80017920 gets r3= 0x0308 <- 803d0430 - PlPrYe.dat
  PathToEntrynum: on Frame 0x7d.f27d Scene 4001c : 80017920 gets r3= 0x00f6 <- 803bfef0 - EfPrData.dat
  PathToEntrynum: on Frame 0x7d.f8c2 Scene 4001c : 80017920 gets r3= 0x0302 <- 803d047c - PlPrAJ.dat
  PathToEntrynum: on Frame 0xdb.64cd Scene 4001c : 80018620 gets r3= 0x01ec <- 803ba6bc - LbRb.dat
  PathToEntrynum: on Frame 0xdb.699d Scene 4001c : 800186d0 gets r3= 0x00ef <- 803ba6c8 - EfMnData.dat
  PathToEntrynum: on Frame 0xdb.6e8a Scene 4001c : 800186d0 gets r3= 0x00d7 <- 803ba6d8 - EfCoData.dat
  PathToEntrynum: on Frame 0xdb.73f0 Scene 4001c : 800186d0 gets r3= 0x01e5 <- 804d37e4 - ItCo.usd
  PathToEntrynum: on Frame 0xdb.7919 Scene 4001c : 800186d0 gets r3= 0x01cc <- 804d37ec - IfAll.usd
  PathToEntrynum: on Frame 0xdb.7f34 Scene 4001c : 80017920 gets r3= 0x0301 <- 803d02f0 - PlPr.dat
  PathToEntrynum: on Frame 0xdb.854d Scene 4001c : 80017920 gets r3= 0x0308 <- 803d0430 - PlPrYe.dat
  PathToEntrynum: on Frame 0xdb.8a3c Scene 4001c : 80017920 gets r3= 0x00f6 <- 803bfef0 - EfPrData.dat
  PathToEntrynum: on Frame 0xdb.907d Scene 4001c : 80017920 gets r3= 0x0302 <- 803d047c - PlPrAJ.dat
  PathToEntrynum: on Frame 0x12a.541d Scene 4001c : 80018620 gets r3= 0x01ec <- 803ba6bc - LbRb.dat
  PathToEntrynum: on Frame 0x12a.58fb Scene 4001c : 800186d0 gets r3= 0x00ef <- 803ba6c8 - EfMnData.dat
  PathToEntrynum: on Frame 0x12a.5df3 Scene 4001c : 800186d0 gets r3= 0x00d7 <- 803ba6d8 - EfCoData.dat
  PathToEntrynum: on Frame 0x12a.6366 Scene 4001c : 800186d0 gets r3= 0x01e5 <- 804d37e4 - ItCo.usd
  PathToEntrynum: on Frame 0x12a.689b Scene 4001c : 800186d0 gets r3= 0x01cc <- 804d37ec - IfAll.usd
  PathToEntrynum: on Frame 0x12a.6e90 Scene 4001c : 80017920 gets r3= 0x02c8 <- 803cf820 - PlMs.dat
  PathToEntrynum: on Frame 0x12a.7484 Scene 4001c : 80017920 gets r3= 0x02cd <- 803cf838 - PlMsNr.dat
  PathToEntrynum: on Frame 0x12a.7980 Scene 4001c : 80017920 gets r3= 0x00f1 <- 803bffa8 - EfMsData.dat
  PathToEntrynum: on Frame 0x12a.7f9f Scene 4001c : 80017920 gets r3= 0x02c9 <- 803cf98c - PlMsAJ.dat
  PathToEntrynum: on Frame 0x12a.85d8 Scene 4001c : 80017920 gets r3= 0x0301 <- 803d02f0 - PlPr.dat
  PathToEntrynum: on Frame 0x12a.8bfc Scene 4001c : 80017920 gets r3= 0x0308 <- 803d0430 - PlPrYe.dat
  PathToEntrynum: on Frame 0x12a.90f9 Scene 4001c : 80017920 gets r3= 0x00f6 <- 803bfef0 - EfPrData.dat
  PathToEntrynum: on Frame 0x12a.9746 Scene 4001c : 80017920 gets r3= 0x0302 <- 803d047c - PlPrAJ.dat
  PathToEntrynum: on Frame 0x186.00b4 Scene 4001c : 80018620 gets r3= 0x01ec <- 803ba6bc - LbRb.dat
  PathToEntrynum: on Frame 0x186.0590 Scene 4001c : 800186d0 gets r3= 0x00ef <- 803ba6c8 - EfMnData.dat
  PathToEntrynum: on Frame 0x186.0a88 Scene 4001c : 800186d0 gets r3= 0x00d7 <- 803ba6d8 - EfCoData.dat
  PathToEntrynum: on Frame 0x186.0ffa Scene 4001c : 800186d0 gets r3= 0x01e5 <- 804d37e4 - ItCo.usd
  PathToEntrynum: on Frame 0x186.152e Scene 4001c : 800186d0 gets r3= 0x01cc <- 804d37ec - IfAll.usd
  PathToEntrynum: on Frame 0x186.1b23 Scene 4001c : 80017920 gets r3= 0x02c8 <- 803cf820 - PlMs.dat
  PathToEntrynum: on Frame 0x186.2113 Scene 4001c : 80017920 gets r3= 0x02cd <- 803cf838 - PlMsNr.dat
  PathToEntrynum: on Frame 0x186.260c Scene 4001c : 80017920 gets r3= 0x00f1 <- 803bffa8 - EfMsData.dat
  PathToEntrynum: on Frame 0x186.2c26 Scene 4001c : 80017920 gets r3= 0x02c9 <- 803cf98c - PlMsAJ.dat
  PathToEntrynum: on Frame 0x186.325b Scene 4001c : 80017920 gets r3= 0x0301 <- 803d02f0 - PlPr.dat
  PathToEntrynum: on Frame 0x186.3880 Scene 4001c : 80017920 gets r3= 0x0308 <- 803d0430 - PlPrYe.dat
  PathToEntrynum: on Frame 0x186.3d7c Scene 4001c : 80017920 gets r3= 0x00f6 <- 803bfef0 - EfPrData.dat
  PathToEntrynum: on Frame 0x186.43c9 Scene 4001c : 80017920 gets r3= 0x0302 <- 803d047c - PlPrAJ.dat
  PathToEntrynum: on Frame 0x186.49fd Scene 4001c : 80017920 gets r3= 0x0301 <- 803d02f0 - PlPr.dat
  PathToEntrynum: on Frame 0x186.5021 Scene 4001c : 80017920 gets r3= 0x0307 <- 803d034c - PlPrRe.dat
  PathToEntrynum: on Frame 0x186.5522 Scene 4001c : 80017920 gets r3= 0x00f6 <- 803bfef0 - EfPrData.dat
  PathToEntrynum: on Frame 0x186.5b6f Scene 4001c : 80017920 gets r3= 0x0302 <- 803d047c - PlPrAJ.dat
  PathToEntrynum: on Frame 0x186.61a3 Scene 4001c : 80017920 gets r3= 0x0301 <- 803d02f0 - PlPr.dat
  PathToEntrynum: on Frame 0x186.67c2 Scene 4001c : 80017920 gets r3= 0x0303 <- 803d0398 - PlPrBu.dat
  PathToEntrynum: on Frame 0x186.6cc2 Scene 4001c : 80017920 gets r3= 0x00f6 <- 803bfef0 - EfPrData.dat
  PathToEntrynum: on Frame 0x186.730f Scene 4001c : 80017920 gets r3= 0x0302 <- 803d047c - PlPrAJ.dat
  PathToEntrynum: on Frame 0x18b.02ba Scene 4001c : 80388a30 gets r3= 0x00ab <- 803bb340 - /audio/us/mars.ssm


# TRAINING SSS:
  -- SCENE TRANSITION 5 : VIFrame 0x499.07ff : Major 1c -> 1c, Minor 00 -> 01

  -- OSHeap[1] : has been DESTROYED after 0x18b VIFrames: 0x5d7a80 bytes freed at  80bd5c40 ... 811ad6c0

  -- OSHeap[1] : has been ALLOCATED on VIFrame 0x499: 0x5d7a80 bytes claimed at 80bd5c40 ... 811ad6c0

  PathToEntrynum: on Frame 0.188c Scene 5011c : 800181b8 gets r3= 0x01ec <- 803ba2a0 - LbRb.dat
  PathToEntrynum: on Frame 0.1dca Scene 5011c : 80016400 gets r3= 0x01f6 <- 803f0a18 - MnSlMap.usd
  PathToEntrynum: on Frame 0.232d Scene 5011c : 800166bc gets r3= 0x01f6 <- 803f0a18 - MnSlMap.usd
  ARCHIVE FILE     : 80c78260 : MnSlMap.usd, 0x9b78b bytes at 80bdcaa0 : Frame 0x17.4858 of Scene 5011c
  - ARCHIVE DATA   : 80bdcac0 : 0x9866c bytes
  - ARCHIVE RELOCS : 80c7512c : 3128 pointer(s)
  - ARCHIVE NODES  : 80c7820c : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 80c78214 : 0x17 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80c78260
  - ALLOCATED      : 80BDCAA0 : offset 0 of a 0x9b7a0-byte allocation at 80bdcaa0 in OSHeap[1]
            - alloc was called by the instruction at 80016c0c on Frame 0.22a2 of Scene 5011c

  PathToEntrynum: on Frame 0x24.a21c Scene 5011c : 80388a30 gets r3= 0x00bc <- 803bb340 - /audio/us/purin.ssm
  PathToEntrynum: on Frame 0xd2.0092 Scene 5011c : 80018620 gets r3= 0x01ec <- 803ba6bc - LbRb.dat
  PathToEntrynum: on Frame 0xd2.0562 Scene 5011c : 800186d0 gets r3= 0x00ef <- 803ba6c8 - EfMnData.dat
  PathToEntrynum: on Frame 0xd2.0a4d Scene 5011c : 800186d0 gets r3= 0x00d7 <- 803ba6d8 - EfCoData.dat
  PathToEntrynum: on Frame 0xd2.0fb4 Scene 5011c : 800186d0 gets r3= 0x01e5 <- 804d37e4 - ItCo.usd
  PathToEntrynum: on Frame 0xd2.14db Scene 5011c : 800186d0 gets r3= 0x01cc <- 804d37ec - IfAll.usd
  PathToEntrynum: on Frame 0xd2.1a1e Scene 5011c : 80017920 gets r3= 0x0199 <- 803e7f84 - /GrNLa.dat
  PathToEntrynum: on Frame 0xd2.2017 Scene 5011c : 80017920 gets r3= 0x02c8 <- 803cf820 - PlMs.dat
  PathToEntrynum: on Frame 0xd2.25fa Scene 5011c : 80017920 gets r3= 0x02cd <- 803cf838 - PlMsNr.dat
  PathToEntrynum: on Frame 0xd2.2ae6 Scene 5011c : 80017920 gets r3= 0x00f1 <- 803bffa8 - EfMsData.dat
  PathToEntrynum: on Frame 0xd2.30f4 Scene 5011c : 80017920 gets r3= 0x02c9 <- 803cf98c - PlMsAJ.dat
  PathToEntrynum: on Frame 0xd2.371c Scene 5011c : 80017920 gets r3= 0x0301 <- 803d02f0 - PlPr.dat
  PathToEntrynum: on Frame 0xd2.3d34 Scene 5011c : 80017920 gets r3= 0x0308 <- 803d0430 - PlPrYe.dat
  PathToEntrynum: on Frame 0xd2.4224 Scene 5011c : 80017920 gets r3= 0x00f6 <- 803bfef0 - EfPrData.dat
  PathToEntrynum: on Frame 0xd2.4865 Scene 5011c : 80017920 gets r3= 0x0302 <- 803d047c - PlPrAJ.dat
  PathToEntrynum: on Frame 0xd2.4e8d Scene 5011c : 80017920 gets r3= 0x0301 <- 803d02f0 - PlPr.dat
  PathToEntrynum: on Frame 0xd2.54a4 Scene 5011c : 80017920 gets r3= 0x0307 <- 803d034c - PlPrRe.dat
  PathToEntrynum: on Frame 0xd2.5993 Scene 5011c : 80017920 gets r3= 0x00f6 <- 803bfef0 - EfPrData.dat
  PathToEntrynum: on Frame 0xd2.5fd4 Scene 5011c : 80017920 gets r3= 0x0302 <- 803d047c - PlPrAJ.dat
  PathToEntrynum: on Frame 0xd2.65fb Scene 5011c : 80017920 gets r3= 0x0301 <- 803d02f0 - PlPr.dat
  PathToEntrynum: on Frame 0xd2.6c0e Scene 5011c : 80017920 gets r3= 0x0303 <- 803d0398 - PlPrBu.dat
  PathToEntrynum: on Frame 0xd2.70fe Scene 5011c : 80017920 gets r3= 0x00f6 <- 803bfef0 - EfPrData.dat
  PathToEntrynum: on Frame 0xd2.773e Scene 5011c : 80017920 gets r3= 0x0302 <- 803d047c - PlPrAJ.dat
  PathToEntrynum: on Frame 0xd7.1de8 Scene 5011c : 80388a30 gets r3= 0x00a6 <- 803bb340 - /audio/us/last.ssm


# FINAL DESTINATION:
  -- SCENE TRANSITION 6 : VIFrame 0x570.2321 : Major 1c -> 1c, Minor 01 -> 02

  -- OSHeap[1] : has been DESTROYED after 0xd7 VIFrames: 0x5d7a80 bytes freed at  80bd5c40 ... 811ad6c0

  -- OSHeap[1] : has been ALLOCATED on VIFrame 0x570: 0x5d7a80 bytes claimed at 80bd5c40 ... 811ad6c0

  PathToEntrynum: on Frame 0.33ac Scene 6021c : 800181b8 gets r3= 0x01ec <- 803ba2a0 - LbRb.dat
  PathToEntrynum: on Frame 0.3a10 Scene 6021c : 80016400 gets r3= 0x01ed <- 803bb200 - LbRf.dat
  PathToEntrynum: on Frame 0.3f48 Scene 6021c : 800166bc gets r3= 0x01ed <- 803bb200 - LbRf.dat
  ARCHIVE FILE     : 80bdeb00 : LbRf.dat, 0x56 bytes at 80bdea80 : Frame 0x13.781f of Scene 6021c
  - ARCHIVE DATA   : 80bdeaa0 : 0x20 bytes
  - ARCHIVE RELOCS : 80bdeac0 : 1 pointer(s)
  - ARCHIVE NODES  : 80bdeac4 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 80bdeacc : 0xa byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80bdeb00
  - ALLOCATED      : 80BDEA80 : offset 0 of a 0x60-byte allocation at 80bdea80 in OSHeap[1]
            - alloc was called by the instruction at 80016ce8 on Frame 0.3ec3 of Scene 6021c

  PathToEntrynum: on Frame 0x13.a007 Scene 6021c : 800181b8 gets r3= 0x00d7 <- 803bfd68 - EfCoData.dat
  - ARCHIVE DATA   : 806ddb60 : 0x1417a0 bytes
  - ARCHIVE RELOCS : 8081f300 : 4321 pointer(s)
  - ARCHIVE NODES  : 80823684 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 8082368c : 0x13 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 808236a0
  - NOT_DYNAMIC    : 806DDB40 : is not a part of any active OSHeap fragments...

  PathToEntrynum: on Frame 0x13.b641 Scene 6021c : 800181b8 gets r3= 0x00ef <- 803c0080 - EfMnData.dat
  - ARCHIVE DATA   : 806dd460 : 0x640 bytes
  - ARCHIVE RELOCS : 806ddaa0 : 2 pointer(s)
  - ARCHIVE NODES  : 806ddaa8 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 806ddab0 : 0x11 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 806ddae0
  - NOT_DYNAMIC    : 806DD440 : is not a part of any active OSHeap fragments...

  PathToEntrynum: on Frame 0x13.cb09 Scene 6021c : 80016400 gets r3= 0x021a <- 803bcdc0 - PdPm.dat
  PathToEntrynum: on Frame 0x14.0077 Scene 6021c : 800166bc gets r3= 0x021a <- 803bcdc0 - PdPm.dat
  ARCHIVE FILE     : 80c12520 : PdPm.dat, 0x1c5 bytes at 80c12320 : Frame 0x2c.45a8 of Scene 6021c
  - ARCHIVE DATA   : 80c12340 : 0x188 bytes
  - ARCHIVE RELOCS : 80c124c8 : 1 pointer(s)
  - ARCHIVE NODES  : 80c124cc : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 80c124d4 : 0x11 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80c12520
  - ALLOCATED      : 80C12320 : offset 0 of a 0x1e0-byte allocation at 80c12320 in OSHeap[1]
            - alloc was called by the instruction at 80016ce8 on Frame 0x13.cfc9 of Scene 6021c

  PathToEntrynum: on Frame 0x2c.5f71 Scene 6021c : 800181b8 gets r3= 0x0199 <- 803e7f84 - /GrNLa.dat
  - ARCHIVE DATA   : 8134fc40 : 0x8d340 bytes
  - ARCHIVE RELOCS : 813dcf80 : 7999 pointer(s)
  - ARCHIVE NODES  : 813e4c7c : 27 symbol(s) nodes
  - ARCHIVE SYMSTR : 813e4d54 : 0x201 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 813e4f60
  - NOT_DYNAMIC    : 8134FC20 : is not a part of any active OSHeap fragments...

  PathToEntrynum: on Frame 0x33.14f1 Scene 6021c : 80016400 gets r3= 0x0371 <- 803fe32c - TyDatai.usd
  PathToEntrynum: on Frame 0x33.1b14 Scene 6021c : 800166bc gets r3= 0x0371 <- 803fe32c - TyDatai.usd
  ARCHIVE FILE     : 80c171c0 : TyDatai.usd, 0x4b93 bytes at 80c12600 : Frame 0x39.2ccc of Scene 6021c
  - ARCHIVE DATA   : 80c12620 : 0x4ac8 bytes
  - ARCHIVE NODES  : 80c170e8 : 7 symbol(s) nodes
  - ARCHIVE SYMSTR : 80c17120 : 0x73 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80c171c0
  - ALLOCATED      : 80C12600 : offset 0 of a 0x4ba0-byte allocation at 80c12600 in OSHeap[1]
            - alloc was called by the instruction at 80016ce8 on Frame 0x33.19d6 of Scene 6021c

  PathToEntrynum: on Frame 0x39.619a Scene 6021c : 800181b8 gets r3= 0x01e5 <- 803f1ee4 - ItCo.usd
  - ARCHIVE DATA   : 80823720 : 0x2aaac8 bytes
  - ARCHIVE RELOCS : 80ace1e8 : 44204 pointer(s)
  - ARCHIVE NODES  : 80af9498 : 1 symbol(s) nodes
  - ARCHIVE EXTERN : 80af94a0 : 6 external symbol(s)
  - ARCHIVE SYMSTR : 80af94d0 : 0x11c byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80af9600
  - NOT_DYNAMIC    : 80823700 : is not a part of any active OSHeap fragments...

  PathToEntrynum: on Frame 0x39.b96f Scene 6021c : 80016400 gets r3= 0x0233 <- 803c0530 - PlCo.dat
  PathToEntrynum: on Frame 0x39.bedd Scene 6021c : 800166bc gets r3= 0x0233 <- 803c0530 - PlCo.dat
  ARCHIVE FILE     : 80c6aac0 : PlCo.dat, 0x2466d bytes at 80c46420 : Frame 0x48.839e of Scene 6021c
  - ARCHIVE DATA   : 80c46440 : 0x239a0 bytes
  - ARCHIVE RELOCS : 80c69de0 : 805 pointer(s)
  - ARCHIVE NODES  : 80c6aa74 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 80c6aa7c : 0x11 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80c6aac0
  - ALLOCATED      : 80C46420 : offset 0 of a 0x24680-byte allocation at 80c46420 in OSHeap[1]
            - alloc was called by the instruction at 80016ce8 on Frame 0x39.be2f of Scene 6021c

  PathToEntrynum: on Frame 0x48.a1a0 Scene 6021c : 800181b8 gets r3= 0x02c8 <- 803cf820 - PlMs.dat
  - ARCHIVE DATA   : 81210820 : 0x27880 bytes
  - ARCHIVE RELOCS : 812380a0 : 3263 pointer(s)
  - ARCHIVE NODES  : 8123b39c : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 8123b3a4 : 0xb byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 8123b3c0
  - NOT_DYNAMIC    : 81210800 : is not a part of any active OSHeap fragments...

  PathToEntrynum: on Frame 0x48.b729 Scene 6021c : 800181b8 gets r3= 0x00f1 <- 803bffa8 - EfMsData.dat
  - ARCHIVE DATA   : 8123b440 : 0x598c bytes
  - ARCHIVE RELOCS : 81240dcc : 249 pointer(s)
  - ARCHIVE NODES  : 812411b0 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 812411b8 : 0x11 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 812411e0
  - NOT_DYNAMIC    : 8123B420 : is not a part of any active OSHeap fragments...

  PathToEntrynum: on Frame 0x48.cd09 Scene 6021c : 800181b8 gets r3= 0x02cd <- 803cf838 - PlMsNr.dat
  - ARCHIVE DATA   : 81241260 : 0x8f430 bytes
  - ARCHIVE RELOCS : 812d0690 : 2015 pointer(s)
  - ARCHIVE NODES  : 812d260c : 2 symbol(s) nodes
  - ARCHIVE SYMSTR : 812d261c : 0x34 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 812d2660
  - NOT_DYNAMIC    : 81241240 : is not a part of any active OSHeap fragments...

  PathToEntrynum: on Frame 0x49.52e8 Scene 6021c : 800181b8 gets r3= 0x02c9 <- 803cf98c - PlMsAJ.dat
  PathToEntrynum: on Frame 0x49.58f0 Scene 6021c : 800168e8 gets r3= 0x02c9 <- 803cf98c - PlMsAJ.dat
  - ARCHIVE DATA   : 80c94ec0 : 0x20b0 bytes
  - ARCHIVE RELOCS : 80c96f70 : 183 pointer(s)
  - ARCHIVE NODES  : 80c9724c : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 80c97254 : 0x28 byte footer
  - PARSE CALLER   : 80085f64 : recieves Archive 804ee9c0 (part of the runtime stack)
  - ALLOCATED      : 80C94EA0 : offset 0 of a 0x8000-byte allocation at 80c94ea0 in OSHeap[1]
            - alloc was called by the instruction at 80085b48 on Frame 0x49.51c0 of Scene 6021c

  - ALLOCATED      : 80C8CE80 : offset 0 of a 0x8000-byte allocation at 80c8ce80 in OSHeap[1]
            - alloc was called by the instruction at 80085b3c on Frame 0x49.51be of Scene 6021c

  PathToEntrynum: on Frame 0x49.ba3b Scene 6021c : 800181b8 gets r3= 0x0301 <- 803d02f0 - PlPr.dat
  - ARCHIVE DATA   : 811ad6e0 : 0x1a400 bytes
  - ARCHIVE RELOCS : 811c7ae0 : 1739 pointer(s)
  - ARCHIVE NODES  : 811c960c : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 811c9614 : 0xc byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 811c9620
  - NOT_DYNAMIC    : 811AD6C0 : is not a part of any active OSHeap fragments...

  PathToEntrynum: on Frame 0x4a.019c Scene 6021c : 800181b8 gets r3= 0x00f6 <- 803bfef0 - EfPrData.dat
  - ARCHIVE DATA   : 811c96a0 : 0x65c8 bytes
  - ARCHIVE RELOCS : 811cfc68 : 180 pointer(s)
  - ARCHIVE NODES  : 811cff38 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 811cff40 : 0x12 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 811cff60
  - NOT_DYNAMIC    : 811C9680 : is not a part of any active OSHeap fragments...

  PathToEntrynum: on Frame 0x4a.187e Scene 6021c : 800181b8 gets r3= 0x0308 <- 803d0430 - PlPrYe.dat
  - ARCHIVE DATA   : 811cffe0 : 0x3fc00 bytes
  - ARCHIVE RELOCS : 8120fbe0 : 722 pointer(s)
  - ARCHIVE NODES  : 81210728 : 3 symbol(s) nodes
  - ARCHIVE SYMSTR : 81210740 : 0x53 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 812107a0
  - NOT_DYNAMIC    : 811CFFC0 : is not a part of any active OSHeap fragments...

  PathToEntrynum: on Frame 0x4a.3b8b Scene 6021c : 800181b8 gets r3= 0x0302 <- 803d047c - PlPrAJ.dat
  PathToEntrynum: on Frame 0x4a.41c5 Scene 6021c : 800168e8 gets r3= 0x0302 <- 803d047c - PlPrAJ.dat
  - ARCHIVE DATA   : 80ccffe0 : 0xc8c bytes
  - ARCHIVE RELOCS : 80cd0c6c : 106 pointer(s)
  - ARCHIVE NODES  : 80cd0e14 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 80cd0e1c : 0x29 byte footer
  - PARSE CALLER   : 80085f64 : recieves Archive 804ee9c0 (part of the runtime stack)
  - ALLOCATED      : 80CCFFC0 : offset 0 of a 0x8000-byte allocation at 80ccffc0 in OSHeap[1]
            - alloc was called by the instruction at 80085b48 on Frame 0x4a.3a30 of Scene 6021c

  - ARCHIVE DATA   : 80cc7fc0 : 0x6d8 bytes
  - ARCHIVE RELOCS : 80cc8698 : 77 pointer(s)
  - ARCHIVE NODES  : 80cc87cc : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 80cc87d4 : 0x26 byte footer
  - PARSE CALLER   : 800cc7e8 : recieves Archive 804ee8fc (part of the runtime stack)
  - ALLOCATED      : 80CC7FA0 : offset 0 of a 0x8000-byte allocation at 80cc7fa0 in OSHeap[1]
            - alloc was called by the instruction at 80085b3c on Frame 0x4a.3a2e of Scene 6021c

  PathToEntrynum: on Frame 0x4a.90af Scene 6021c : 80016400 gets r3= 0x0101 <- 804d4228 - GmPause.usd
  PathToEntrynum: on Frame 0x4a.95b3 Scene 6021c : 800166bc gets r3= 0x0101 <- 804d4228 - GmPause.usd
  ARCHIVE FILE     : 80cf5660 : GmPause.usd, 0x807e bytes at 80ced5c0 : Frame 0x4a.aace of Scene 6021c
  - ARCHIVE DATA   : 80ced5e0 : 0x7d68 bytes
  - ARCHIVE RELOCS : 80cf5348 : 182 pointer(s)
  - ARCHIVE NODES  : 80cf5620 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 80cf5628 : 0x16 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80cf5660
  - ALLOCATED      : 80CED5C0 : offset 0 of a 0x8080-byte allocation at 80ced5c0 in OSHeap[1]
            - alloc was called by the instruction at 80016e40 on Frame 0x4a.9594 of Scene 6021c

  PathToEntrynum: on Frame 0x4a.ca66 Scene 6021c : 800181b8 gets r3= 0x01cc <- 804d5780 - IfAll.usd
  - ARCHIVE DATA   : 80af9680 : 0xd663c bytes
  - ARCHIVE RELOCS : 80bcfcbc : 5780 pointer(s)
  - ARCHIVE NODES  : 80bd570c : 11 symbol(s) nodes
  - ARCHIVE SYMSTR : 80bd5764 : 0xb8 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80bd5820
  - NOT_DYNAMIC    : 80AF9660 : is not a part of any active OSHeap fragments...

  PathToEntrynum: on Frame 0x4b.1d04 Scene 6021c : 80016400 gets r3= 0x0331 <- 803f99ac - SdIntro.dat
  PathToEntrynum: on Frame 0x4b.2476 Scene 6021c : 800166bc gets r3= 0x0331 <- 803f99ac - SdIntro.dat
  ARCHIVE FILE     : 80d08460 : SdIntro.dat, 0x176 bytes at 80d082c0 : Frame 0x4b.a222 of Scene 6021c
  - ARCHIVE DATA   : 80d082e0 : 0x120 bytes
  - ARCHIVE RELOCS : 80d08400 : 8 pointer(s)
  - ARCHIVE NODES  : 80d08420 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 80d08428 : 0xe byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80d08460
  - ALLOCATED      : 80D082C0 : offset 0 of a 0x180-byte allocation at 80d082c0 in OSHeap[1]
            - alloc was called by the instruction at 80016c0c on Frame 0x4b.2364 of Scene 6021c

  PathToEntrynum: on Frame 0x4b.c5e1 Scene 6021c : 80016400 gets r3= 0x01cd <- 803f9e18 - IfCoGet.dat
  PathToEntrynum: on Frame 0x4b.cb3b Scene 6021c : 800166bc gets r3= 0x01cd <- 803f9e18 - IfCoGet.dat
  ARCHIVE FILE     : 80d10100 : IfCoGet.dat, 0x2dc4 bytes at 80d0d300 : Frame 0x4c.7926 of Scene 6021c
  - ARCHIVE DATA   : 80d0d320 : 0x287c bytes
  - ARCHIVE RELOCS : 80d0fb9c : 323 pointer(s)
  - ARCHIVE NODES  : 80d100a8 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 80d100b0 : 0x14 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80d10100
  - ALLOCATED      : 80D0D300 : offset 0 of a 0x2de0-byte allocation at 80d0d300 in OSHeap[1]
            - alloc was called by the instruction at 80016e40 on Frame 0x4b.cac6 of Scene 6021c

  PathToEntrynum: on Frame 0x4c.a00a Scene 6021c : 80016400 gets r3= 0x01e7 <- 803bb088 - LbBf.dat
  PathToEntrynum: on Frame 0x4c.a54a Scene 6021c : 800166bc gets r3= 0x01e7 <- 803bb088 - LbBf.dat
  ARCHIVE FILE     : 80d16280 : LbBf.dat, 0x341 bytes at 80d15f00 : Frame 0x4d.53b0 of Scene 6021c
  - ARCHIVE DATA   : 80d15f20 : 0x2c8 bytes
  - ARCHIVE RELOCS : 80d161e8 : 15 pointer(s)
  - ARCHIVE NODES  : 80d16224 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 80d1622c : 0x15 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80d16280
  - ALLOCATED      : 80D15F00 : offset 0 of a 0x360-byte allocation at 80d15f00 in OSHeap[1]
            - alloc was called by the instruction at 80016ce8 on Frame 0x4c.a4cb of Scene 6021c

  PathToEntrynum: on Frame 0x4d.6d3e Scene 6021c : 8038e90c gets r3= 0x003e <- 803bb300 - /audio/hyaku2.hps
  PathToEntrynum: on Frame 0x4d.a5ce Scene 6021c : 80016400 gets r3= 0x017c <- 804d4150 - GmTrain.usd
  PathToEntrynum: on Frame 0x4d.ab3b Scene 6021c : 800166bc gets r3= 0x017c <- 804d4150 - GmTrain.usd
  ARCHIVE FILE     : 80d487a0 : GmTrain.usd, 0x13fd7 bytes at 80d347a0 : Frame 0x5b.8a14 of Scene 6021c
  - ARCHIVE DATA   : 80d347c0 : 0x12828 bytes
  - ARCHIVE RELOCS : 80d46fe8 : 1499 pointer(s)
  - ARCHIVE NODES  : 80d48754 : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 80d4875c : 0x1b byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80d487a0
  - ALLOCATED      : 80D347A0 : offset 0 of a 0x13fe0-byte allocation at 80d347a0 in OSHeap[1]
            - alloc was called by the instruction at 80016e40 on Frame 0x4d.aab3 of Scene 6021c

  PathToEntrynum: on Frame 0x5b.c476 Scene 6021c : 80016400 gets r3= 0x0346 <- 803d98e4 - SdTrain.usd
  PathToEntrynum: on Frame 0x5b.ca81 Scene 6021c : 800166bc gets r3= 0x0346 <- 803d98e4 - SdTrain.usd
  ARCHIVE FILE     : 80d5ba00 : SdTrain.usd, 0x3485 bytes at 80d58540 : Frame 0x68.1951 of Scene 6021c
  - ARCHIVE DATA   : 80d58560 : 0x3300 bytes
  - ARCHIVE RELOCS : 80d5b860 : 83 pointer(s)
  - ARCHIVE NODES  : 80d5b9ac : 1 symbol(s) nodes
  - ARCHIVE SYMSTR : 80d5b9b4 : 0x11 byte footer
  - PARSE CALLER   : 80016a74 : recieves Archive 80d5ba00
  - ALLOCATED      : 80D58540 : offset 0 of a 0x34a0-byte allocation at 80d58540 in OSHeap[1]
            - alloc was called by the instruction at 80016c0c on Frame 0x5b.c95b of Scene 6021c


The scene transitions have been combined with the Minor and Major IDs to create a 32-bit compound ID. So for example, the final destination scene in the above example reports using the scene ID '6021c' -- which is scene Transition 0x0006, Minor ID 0x02, Major ID 0x1C. The major ID identifies the type of scene, while the minor ID identifies what step of the scene it's in (like CSS -> SSS -> Game)

The 48-bit fixed point frames then use the samples made by each scene transition to make frame timestamps that are relative to the beginning of each scene.
So you'll see the scene transition announce an absolute frame like -- SCENE TRANSITION 6 : VIFrame 0x570.2321 : Major 1c -> 1c, Minor 01 -> 02 -- but the subsequent file load announcments are relative to that -- PathToEntrynum: on Frame 0.33ac Scene 6021c : ...



--- Configuring the Loggers:

Each logger included with the code and the plugin examples has some special global properties that you can access from the <*.loggerSettings> mod container. These can be set from MCM to change the way the loggers behave at boot -- or they can edited after boot by other codes or memory edits from Debug Dolphin.

The easiest way to edit these on the fly is by creating additional watch entries in your watch table. You will need to use the ‘Summary’ tab in MCM in order to find out what RAM addresses each settings table has been installed to on the DOL:



The options are as follows:
Rich (BB code):
<isFromHeap.loggerSettings> NTSC 1.02 
# You can select which loggers you want to enable when the core module is installed: 
# '00' bytes are disabled;  else = enabled 
01  # --- ARENA LOGGER - triggered when the OS Arena functions are invoked 
01  # --- HEAP LOGGER  - triggered when an OS Heap is created or destroyed 
01  # --- SCENE LOGGER - triggered when the scene function writes a new minor ID 
01  # --- MEMORY EDITOR QUERY INTERFACE LOGGER - triggered when a user input is found at 804DAA84 
  
  
<PathToEntrynum.loggerSettings> NTSC 1.02 
# '00' bytes are disabled;  else = enabled 
01  # --- PATHTOENTRYNUM LOGGER - prints a message every time a file name entrynumber is looked up 
01  # --- SAMPLE BUFFER ARGUMENTS - logger uses memory of 0x80432058 buffer arg for path string addr 
0000     # - padding 
00000000 # - (space reserved for buffer) 
  
  
<ArchiveObject.loggerSettings> NTSC 1.02 
# Archive Logger flags can be edited below  using False=0,  True=1 
# flags are in nibbles on little end, to fit discretely in CR: 
0000 # padding 
1 # --- ARCHIVE OBJECT LOGGER    - logs information about archives (dat files) as they are loaded 
1 # --- VERBOSE HEADER LOG       - logs details extracted from the archive header and heap fragment 
1 # --- LOG COPIED ARCHIVES ONCE - logs copied archives (like action state animations) only once 
# - prevents archive copies from flooding the logger by only announcing it once per allocation 
#   - this will prevent the logger from seeing things like action state changes, beyond the first 
1 # --- VERBOSE HEADER ONCE      - prevents serial verbose header logs from the same allocation 
# - prevents verbose copies from flooding the logger with multiple lines every action state 
#   - if logging all archive copy parses, then each copy only shows verbose lines on the first use 


For the most part, these flags are only good for enabling/disabling the individual loggers without uninstalling them..

The archive object logger has a couple of settings that use one of the remaining metadata words in DAT file allocations to store a special tag that lets the logger know what’s already been announced. This prevents it from logging the same allocation multiple times, which can happen when an archive is parsed multiple times in one scene like with action state animations. These are enabled by default to prevent the log from flooding, but can easily be disabled at any time to show ‘archive copies’:


(These archive copies can only be seen when the logger is configured to show them)​



You can also configure a blacklist filter for the various ‘caller’ log messages that are displayed. For example, the <isFromHeap.callerFilter> table includes the default ‘HSD_Alloc’ function responsible for most calls to OSAllocFromHeap -- causing the logger code to reach down one extra caller in the callstack before printing the message. This helps narrow down the displayed caller addresses to unique callers by preventing each of the allocs created from 'HSD_Alloc' from reporting the same common caller.

You may add or remove any addresses to these caller filters by editing their mod containers:
Rich (BB code):
<isFromHeap.callerFilter> NTSC 1.02 
# You can filter out unwanted caller samples by adding them here 
# - when sampled on allocation, the callstack will reach one extra step when finding these 
8037f20c # HSD_MemAlloc call 
8037aa38 # HSD_ObjAllocAddFree 
8037acbc # HSD_ObjAlloc ... something with IDs 
  
80015c40 # HSD_ ... something with loading files 
  
# <- add additional instruction addresses here 
  
  
# - add the address of instructions that call OSAllocFromHeap (80343ef0) 
#   - you can also add instructions that call functions that call OSAllocFromHeap 
#   - each matching address will be skipped in the callstack parse, in favor of the next one 
00000000  # leave a null terminator, for the parser 
  
  
<PathToEntrynum.callerFilter> NTSC 1.02 
800186d0 
800181b8 # unknown DAT index HSD heap funcs 
  
00000000 
  
  
<ArchiveObject.callerFilter> NTSC 1.02 
80069d14 
80069cf4 
80085dec # these are related to action state changes 
  
00000000 
 
Last edited:

DRGN

Technowizard
Moderator
Joined
Aug 20, 2005
Messages
2,178
Location
Sacramento, CA
Amazing work, as usual, Punkline! I've been trying this out, and it's really cool.

First of all, to clarify a few things and make sure I have things strait so far.... The label "NOT_DYNAMIC" isn't to imply that the files identified as such are loaded into consistent addresses, but because the target heaps those are loaded into are of a static or "persistent" location and size, correct? I notice some files loaded with that identifier do load to consistent locations, such as IfAll or LbRb, but others like stage and character files don't. With IfAll, UnclePunch tells me it's because it has its own heap (the second persistent heap; ID 0x1), but I'm guessing it happens with some other files just because they're consistently the first file(s) to be loaded to certain heaps and/or the files before them are always the same size.

It seems that there are 9 heap "slots" described in total. 4 of them are in the table you mentioned, found by following the pointer at -0x4340(r13)/804D7360, with two of the entries there being null; I've noticed both you and UnclePunch call these "OS Heaps", obvioiusly since they're governed by native Dolphin OS functions. And then there appear to be 5 slots in a table describing persistent heaps at 803ba380 (described in the spoiler below, from UnclePunch), with the last entry being null; UnclePunch calls these "HAL Heaps", since they're managed by a bit more advanced set of functions in the HSD library that wrap the native OS heap management functions, as you mentioned. That makes for 6 heaps; 2 OS Heaps which are technically dynamic (change location and size depending on the scene) even though the first one for audio (OSHeap[0]) seems to not change, and the other 4 HAL Heaps being persistent (location/size hardcoded and never change). Although I'm not completely sure about them never changing, because I found some functions in the symbol map called "Heap_UpdatePersistentHeapTable" (8001529c) and "Heap_UpdatePersistentHeapsOnSceneChange" (801a3f48). Maybe it's possible for them to change, but they just don't usually change in practice, at least in Melee.

- Structure is:
- 0x0 = Int32, Main Heap ID​
- 0x4 = Int32, Heap Lo Boundary Behavior​
- 0x8 = Int32, Heap ID to Start After​
- 0xC = Int32, persistent heap size.​

Persistent IDs
0 = unk
1 = IfAll
2 = Fighter file cache
3 = AJ file cache (ARAM)

This logger answers a lot of questions, but like any good science, also leads to a few more:

1) How does the game reference or determine the persistent heaps' starting addresses? Their sizes are in the persistant heap setup table, but not their starts.

2) When does the game load vs. pre-load a file, if there is such a distinction? Is the latter basically just placing it in memory but without performing certain functions on it, like processing the relocation table to recalculate pointers?


I feel like the 'PathToEntrynum' log lines are a little dubious. I know I suggested that function, but it doesn't actually tell us whether or not the file is being loaded/pre-loaded, right? I'm guessing that sometimes the game might check for a file on disc without ever actually loading it. It's still a nice log to be able to toggle on, but maybe there could be a log printout added one function higher up to catch when the file is definitely being loaded? Maybe within OSAllocFromHeap.

More importantly, could the "NOT_DYNAMIC" log lines indicate which HAL heap the file is being loaded to (maybe it should then just say ALLOCATED like the rest in that case)? Searching for "heap" in the symbol map is showing a number of interesting functions that might be able to help with this; HSD_ObjAlloc in particular, which I'd guess might be the HAL Heap equivalent of OSAllocFromHeap.


SinsOfApathy SinsOfApathy and UnclePunch UnclePunch , Do you guys have any ideas or more to add on this topic?
 

Punkline

Dr. Frankenstack
Joined
May 15, 2015
Messages
423
I feel like the 'PathToEntrynum' log lines are a little dubious. I know I suggested that function, but it doesn't actually tell us whether or not the file is being loaded/pre-loaded, right? I'm guessing that sometimes the game might check for a file on disc without ever actually loading it. It's still a nice log to be able to toggle on, but maybe there could be a log printout added one function higher up to catch when the file is definitely being loaded? Maybe within OSAllocFromHeap.
Commonly, you will see an entrynum first get the size of a file so that it can create an allocation in RAM to then load it into. The game usually makes 2 separate queries for the entrynum when doing this, which the logger reports. Each time though, the entrynum is being intercepted just to sample the path name argument, for reporting purposes.

I’ll make a more accurate ‘file is being loaded’ and ‘file size is being checked’ logger soon that uses some of the functions that use these entrynums. Now that I know how the FST is organized, I think I can just manually convert entrynums back into path names using r13.


This logger answers a lot of questions, but like any good science, also leads to a few more:

1) How does the game reference or determine the persistent heaps' starting addresses? Their sizes are in the persistant heap setup table, but not their starts.

2) When does the game load vs. pre-load a file, if there is such a distinction? Is the latter basically just placing it in memory but without performing certain functions on it, like processing the relocation table to recalculate pointers?

...

More importantly, could the "NOT_DYNAMIC" log lines indicate which HAL heap the file is being loaded to (maybe it should then just say ALLOCATED like the rest in that case)? Searching for "heap" in the symbol map is showing a number of interesting functions that might be able to help with this; HSD_ObjAlloc in particular, which I'd guess might be the HAL Heap equivalent of OSAllocFromHeap.

‘Dynamic’ refers to ‘randomly free-able’ with the emphasis on ‘random’. You can free memory from the OS Arena, but only in a sequential manner (or all at once).

In a ‘heap’ you can free any part of it at any time; at the cost of potentially fragmenting it into smaller and smaller sections over time. Because of this, heaps might need to be ‘defragged’, carefully aligned, or just periodically wiped and rebuilt, like it is done in Melee. Stacks like the Arena don’t have that problem, but are incapable of freeing elements that aren’t on top of the stack.

The Heap’ is a sort of colloquial reference to all excess bytes remaining from non-dynamic allocations pushed from the arena. Even though there’s heap[0] and heap[1] -- only heap[1] is ‘the heap’ because it has no dedicated purpose; it’s just extra remaining bytes available for miscellaneous allocations.


There may be other ‘heap’ structures in the non-dynamic regions, but more likely they are just little sub-arenas -- which would work like stack frames that have stacks in them. This would prevent the persistent memory from fragmenting over time.

From my limited observations so far, I’ve noticed they use the same allocation system that the DAT Archive Index elements use -- meaning they are likely dedicated to just preloading DAT files, and are linked together with remote metadata in order to create groups. It’s likely that you can probe the remaining space in these sections by summing the total of bytes reported by these chains, which I’ll try to do in the next plugin logger. I’ll bet that this whole system is just a part of the archive library.


All HSD object memory uses heap[1]. The HSD Object allocation functions all wrap around calls to OSAllocFromHeap through the use of _HSD_Alloc (8037f1e4) -- which I think is the lowest level allocator for the HSD library. You can see this in the caller filter for the logger -- meaning that it will not show up in the logger because it is used in almost every object allocation. You can extend the caller filter list to eliminate other common callers and find more unique entrypoints for reversing the callstack.


PSEdit: I have some extra notes at the end of this post about these preload regions.



---

I made these logger tools to sort of create a ‘shadow’ that follows what the higher level memory management libraries are doing with the simpler OSHeap library. This makes it harder to get the full picture with the HSD library, but still lets me observe how the HSD Library uses heap[1] to keep track of Leftover Arena RAM:




In the above diagram, you can see the main steps that the game takes to build up its RAM structure. Each column shows segments of RAM represented as colored bars, with information about their addresses and contents. In terms of loading time, you can see a progression from left to right as ‘Arena RAM’ turns into what I’m calling ‘HSD RAM’ for clarity. HSD RAM is just the final push of the OSArena -- effectively closing it off and dedicating it for management by the HSD system like a sort of workspace.


In the first and second columns, you can see the various things that take up portions of the 24 MB memory region to create both static and arena memory on boot; before handing off the remainder to HSD RAM:


A - StaticLo - this is the game disc header, DOL definitions, interrupt hooks, debugger info, static data, static vars, small data table, toc, and runtime stack frame are located. They are of a predictable size depending on the size of the contents/specifications of the DOL. Below is a breakdown of the various sections:

A0 - 80000000 - this is the game disk header info and the DOL create a set of OS globals and room for writing interrupt hooks in the first 0x3100 bytes of RAM, as the ‘head’. Following that is the TEXT0 region, which includes the entrypoint for the game program -- followed by some small data sections describing function locations and sizes of a list of functions at the end of TEXT1.

A1 - 80005940 - this big chunk is TEXT1, which contains most of the code used by the game program, and takes up a majority of the DOL file. It’s where injection code/hooks usually go in order to mutate the game code.

A2 - 803b7240 - these are the regular static data sections following TEXT1 that store data for use by the functions in TEXT1. DATA2 and DATA3 appear to be very small pointer allocations for what appear to be exception callbacks. DATA4 and DATA5 is where most of the game’s statically defined data is stored.

A3 - 804316c0 - this is the BSS section -- which I think is a definition of RAM that does not need to be defined by the source literals in the file because it initializes as all zeroes. I believe these are used to allocate variables that get written to at runtime. The SDATA section appears to partially overlap with this section.

A4 - 804d36a0 - this is DATA6, which is the SDATA section -- a very special region of RAM that can be accessed by the CPU (and thus, injection codes) at any time in the game’s runtime by using dedicated r13. It is limited in virtual scope by the range afforded by load/store instructions -- which is -0x8000 ... 0x7FFF -- creating 0x10000 bytes as a maximum window. This section is crucial to effectively navigating both static and dynamic RAM through the use of conveniently available global pointers at implicit offsets known by the functions that use them.

A5 - 804d79e0 - this is DATA7, which is the TOC section -- another special region of RAM that is used in a way similar to r13 for creating a 0x10000-byte base address for shortcut access to global data. TOC uses r2 (also known as ‘rtoc’) instead of r13, and differs from the SDATA section in that it only stores constants as opposed to variables. This means that the TEXT sections only use it for ‘read’ access, and never write to them. (this fact is exploitable.)

(r13 and rtoc point to A4, A5 +0x8000 in order to utilize their maximum load/store range)

A6 - 804dec00 - finally, the last section is a 0x10000-byte region that becomes the run-time stack frame. The end of this section (804eec00) becomes the beginning stack pointer when the TEXT0 entrypoint is executed at boot. The CPU will keep track of this stack pointer in dedicated r1 (also known as ‘sp’) -- and uses it to create a temporary ‘frame’ each time that a function is called in order to save/restore register data quickly and efficiently without requiring any extra space in RAM. The stack frame may also be used to create function-temporal memory for things like string copying, array building, sorting algorithms, recursive processes, and other things. Allocations made this way will persist for the duration of a function call, plus any calls that function makes -- but then become volatile once the function has finished executing.

It’s worth noting that the stack frame intersects with the virtual rtoc range, which may be useful for creating a small number of custom global variables out of the least-used section of the runtime stack frame -- which may or may not have practical use because of the extremely low possibility of volatility.



B - ArenaLo - these are allocations made after the game’s entrypoint has been executed, and are procedural. They occupy the remainder of the 24-MB RAM region that wasn’t used up by static allocations from A and D, and technically encompass C.
(C is worth distinguishing however.)

Arenas work similarly to how stack frames are created in the runtime stack, but in a semi-permanent way. Each ‘push’ of the arena stack is just adding x bytes to a pointer index called ‘ArenaLo’ that’s kept in the globals. ‘ArenaHi’ is the opposite end of the stack, so I suppose it would be more accurate to think of it like a ‘double-ended queue’ structure. The game does not use any pushes to ‘ArenaHi’ -- which makes it easy for us to use for making custom (permanent) allocations at boot with injection code. Below are the ways the game uses ‘ArenaLo’ before creating the heaps:

B0 - 804eec00 - a permanent 0x2000-byte allocation that appears to be used to buffer stdout writes, as seen being used by these logger tools. It gets used automatically by the printf function when formatting string data.

B1 - 804f0c00 - a huge, mysterious allocation with over a megabyte of space. It appears to be unused, which is unlikely but should be confirmed. Anyone know what this is?

B2 - 8061cc00 - not sure what for, but a 256kb block of data is seemingly streamed to or from here. It is probably related to audio, or something else. Maybe music streamed from the DVD?

B3 - 8065cc00 - the last push before the dynamic memory regions are created -- a 0x30-byte array of heap descriptors. The size of this allocation can be increased (or the location can be changed) in order to specify a different number of OSHeap slots, other than 4. If this ArenaLo push were left in place, and the descriptors were moved to a new allocation pushed from ArenaHi -- then it might be possible to define any number of additional small data heaps for use by codes that could each be individually managed. The game ultimately uses a pointer stored in -0x4340(r13) to reach these when operating on one of the OSHeaps.


C - 8065cc40 - the negative space left behind by these other allocations is the remaining Arena RAM that we can use for dynamic memory allocations using sub-arenas and heaps.

D - 817fbac0 - StaticHi - the File System Table (FST) - this is where the game references what files are available on disk, and where on the disk they are located. It is the only allocation that goes in StaticHi, and the middle (start of the strings section) can be located by following the pointer stored in -0x4420(r13).



---

The size of C is variable -- determined by the size of the static and arena-pushed data described above.

This variation in size ultimately only affects the resulting Dynamic Memory Heap size, because the other allocations also have fixed sizes, pushing the boundaries of the post-boot Arena in order to make different dynamic memory setups depending on the scene.

‘HSD RAM’ generally provides roughly 17.6 megabytes for dynamically loading and using files. Some of this goes towards the Dynamic Memory Heap, while others go towards special file loading stacks that preload data for other scenes.

The green portions are the leftover Arena RAM that gets converted into heap[1].
The blue portions are where these ‘NOT_DYNAMIC’ files are being loaded to, and what you and I have been referring to as 'other heaps' made by the HSD system. I have not looked into them closely enough to determine whether or not these are actually heaps -- because it would make more sense if they were just extensions of the Arena. Fragmenting these sections would be dangerous because they are meant to persist longer than a scene.


E - 8065cc40 - this is OSHeap[0], which is only half a megabyte and appears to be used by the AX library for handling audio memory. I know very little about audio handling in Melee, but I know that there is about 9MB leftover after boot of a special type of RAM available for buffering it called ARAM that is not directly accessible by the CPU -- so I would imagine that this heap is like a temporary buffer for piping data or something. It is built alongside OSHeap[1] before the first scene begins, with OSHeap[1] at that time representing all of the remaining ARAM after OSHeap[0].

This time right before the first scene is built is the only time that sections F and G are not built -- implying they are pushed from the leftover Arena RAM boundary. Those sections come AFTER this audio heap. All are basically permanent allocations.


F - 806dcc40 - a tiny 0x800 allocation that appears to load LbRb, which is a very small file that seems to be related to the rumble feature on controllers. It seems to reliably load it only at the time of the arena push -- implying that it’s meant to last for the duration of the game. Interestingly, 0x800 bytes is slightly less than double the size of LbRb, so maybe other stuff goes in here too. (If not, then this section’s remaining space may be exploitable for very small data allocations).

G - 806dd440 - this is a large 4.9MB push of the HSD RAM boundary. The files loaded into this region seem to last for the duration of the game, making it a place to effectively preload files that are commonly used in most scenes/cases. You can see the game load files like ‘EfMnData’, ‘EfCoData’, ‘ItCo’, and ‘IfAll’ here. I still need to find more details about these sections, but their existence can be observed by the ‘shadows’ cast by the OSHeap logger tools and dolphin breakpoints. I have some more info following these RAM section notes.


Beyond this point is entirely dependent on the current scene being loaded, as illustrated in the last 2 columns. Minor scenes appear to shrink down the Dynamic Memory Heap to only 5.6 MB in vanilla cases -- and smaller in cases where additional arena pushes have been made by other codes.


H - 80bd5c40 - this is the ‘Dynamic’ memory heap, OSHeap[1]. It is used by the HSD Library as ‘the heap’ for generic object memory allocations in a way that is no different than using the OS allocator function directly with a dedication to heap[1]. Some functions wrap around the HSD allocator function however in order to create preset size descriptions for things like player property table sizes allocated for GObjs, and other nuanced things. Since they all inevitably use the OS allocator internally -- the OSHeap logger tools will catch their allocations as ‘ALLOCATED’ versus ‘NOT_DYNAMIC’.

This Dynamic Memory Heap is highly volatile compared to the other regions because of the numerous small-data allocations made within it for object instances; however it is also used to load files that persist for the duration of a whole scene. From a sample training mode match between Neutral Marth and Red Marth, I observed ‘PlCo’, ‘PlMsAJ’, ‘PdPm’, ‘LbRf’, ‘TyDatai’, ‘GmPause’, ‘SDIntro’, ‘IfCoGet’, and ‘GmTrain’.

It’s also worth noting that for each fighter in a scene, a 0x8000-byte allocation is made in this heap that appears to buffer sectioned archives included within the animation files for relocating and using animation data on each action state change. This is likely done by copying sections of the ‘*AJ’ files loaded for each character. You can observe these being initialized in the Archive Object logger plugin by configuring its flags to show the messages.


I - 811ad6c0 - this is the only ArenaHi-like push that seems to be made in this game’s RAM structure, aside from the FST. It creates a preload section for loading files in things like the character and stage selection scenes, and is not used in every scene.

When a minor scene isn’t pending (like in the main menu), you can see that the Dynamic Memory Heap is much larger than when a minor scene is pending, or is in use. This is because the preload section is not instantiated.

When a minor scene is pending however, more than half of this large space becomes a ~6MB preload section. This section is used for preloading specific files for the next scene, like in the CSS -> SSS -> Game sequence of scene transitions. In my training mode sample, I observed ‘EfMsData’, ‘PlMs’, ‘PlMsNr’, ‘PlMsRe’, and ‘GrNLa’ loaded into this section. You could imagine this being ‘every requirement made from selections on CSS and SSS’


---

This should account for the entirety of virtually mapped RAM. All individual allocations made within these static and dynamic structures may be further extrapolated upon for more nuanced memory management -- but this should ultimately serve as an effective map for where everything resides within.


---

And then there appear to be 5 slots in a table describing persistent heaps at 803ba380 (described in the spoiler below, from UnclePunch), with the last entry being null; UnclePunch calls these "HAL Heaps", since they're managed by a bit more advanced set of functions in the HSD library that wrap the native OS heap management functions, as you mentioned. That makes for 6 heaps; 2 OS Heaps which are technically dynamic (change location and size depending on the scene) even though the first one for audio (OSHeap[0]) seems to not change, and the other 4 HAL Heaps being persistent (location/size hardcoded and never change). Although I'm not completely sure about them never changing, because I found some functions in the symbol map called "Heap_UpdatePersistentHeapTable" (8001529c) and "Heap_UpdatePersistentHeapsOnSceneChange" (801a3f48). Maybe it's possible for them to change, but they just don't usually change in practice, at least in Melee.

  • Structure is:
  • 0x0 = Int32, Main Heap ID- 0x4 = Int32, Heap Lo Boundary Behavior- 0x8 = Int32, Heap ID to Start After- 0xC = Int32, persistent heap size.
Persistent IDs
0 = unk
1 = IfAll
2 = Fighter file cache
3 = AJ file cache (ARAM)

Great info on the higher level structures! That part about ARAM actually might help explain something that’s been puzzling me about a table I found earlier this week that seems to be instantiated by these values. I’ll post more info when I have it, as I’m still looking into it -- but I think that the table at 80431fb0 indicates that there are also these IDs, despite not being listed there as initial values in the static data UnclePunch UnclePunch :


Code:
# Behavior IDs:
0 - Dynamic          # Dynamically free-able, and auto-destroyed on minor scene changes
1 - Persistent       # loaded once before or at time of first scene beginning, and persists
2 - Semi-Persistent  # loaded from a prior minor scene for use in this or later minor scenes
3 - AJ ARAM Cache
4 - unk ARAM Cache

# 6 HSD Allocation Table Descriptors starting at 80431fb0
00    word     OSHeap ID (if describing a heap -- else, -1)
04    point    to Frame Boundary description (if not describing a heap -- else -1)
08    point    to Address of Boundary Start (or ARAM offset?)
0C    word     size in bytes
10    word     behavior ID
14    word     unknown
18    word     unknown

# Frame Boundary descriptions:
00    point    to next node (?)
04    point    to boundary Lo
08    point    to boundary Hi
0C    point    to Archive Link chain

# Archive Alloc Metadata Links:
00    point    to next allocation associated with this group
04    point    to start of allocation
08    word     size in bytes


The IDs at the beginning of each of those static descriptors you showed me btw ‘2, 3, 4, 5’ are indices that match the instantiated table in 80431fb0. So, you can multiply those by 0x1C and add them to that address to access them. I wonder if there's a nearby param that defines how many descriptors to make.

Here’s a configuration of these 6 Allocation Tables that I sampled from the start of the game:


Code:
# Samples from 803ba380
0: ID=2, behavior=1, size=800
1: ID=3, behavior=1, size=4f8800
2: ID=4, behavior=2, size=6fb400
3: ID=5, behavior=4, size=96c800

# Samples from 80431fb0
0: OSHeap[1]
# - addr=80bd5c40, size=5d7a80, behavior=0 (Dynamic - wipe me)

1: unknown ARAM alloc
# - addr=f966a0, size=69960, behavior=3

2: LbRb.dat (rumble feature ?)
# - addr=806dcc40, size=800, behavior=1 (Persistent)

3: Common Preload Files (IfAll, ItCo, EfCoData, EfMnData)
# - addr=806dd440, size=4f8800, behavior=1 (Persistent)

4: Pending Minor Preload Files (Pl*, Gr*, Ef*)
# - addr=811ad6c0, size=64b400, behavior=2 (Semi-Persistent)

5: unknown ARAM alloc
# - addr=629ea0, size=96c800, behavior=4

I've never encountered use of ARAM before, so I'm not entirely sure about 1 and 5.

I'm juggling a few projects right now, but I'll make some new logger tools using all of the data in this post soon. I'm preeetty sure that 80015bd0 is the special allocator function used to organize these preloaded files for creating an Archive Index.
 
Last edited:

JoshuaMK

Smash Apprentice
Joined
May 19, 2019
Messages
75
I must note that OSArenaHi may not always be at that address, and should definitely never be assumed.

If GeckoLoader gets integrated into MCM at some point to double the available code space, the Arena shall also shrink by that address change as well.

Ultimately this doesnt matter too much since allocation functions will automatically be aware of this change, as it always regrabs the pointer value from the global variable table (which is set by the value at address 0x80000034 on game initialization), but I think it's worth being made aware of :estatic:
 

Punkline

Dr. Frankenstack
Joined
May 15, 2015
Messages
423
I must note that OSArenaHi may not always be at that address, and should definitely never be assumed.

If GeckoLoader gets integrated into MCM at some point to double the available code space, the Arena shall also shrink by that address change as well.

Ultimately this doesnt matter too much since allocation functions will automatically be aware of this change, as it always regrabs the pointer value from the global variable table (which is set by the value at address 0x80000034 on game initialization), but I think it's worth being made aware of :estatic:

That’s right. I sort of meant to illustrate with the ‘staticHi’ section (D), which by circumstance is dedicated solely to the FST.

The FST will need to change size if files are added to the game disk, because it will increase the number of strings and offset indices that are in the FST section of static RAM (which comes BEFORE the arena, sort of lowering the ‘ceiling’ of RAM). Ultimately, anything implementing things in the game like this should defer to pointers kept in the game in order to make use of procedurally-determined boundaries.

Other codes may also push ArenaHi to make permanent allocations adjacent to the FST, and each alloc will avoid fragmentation so that it can be used for small individual pushes if needed for making new global variables. In each of these cases, it will shrink the ‘variable’ memory heap size (C, and ultimately H) that I was referring to by affecting the amount of remaining bytes in the Arena that gets passed over to HSD RAM -- but not any of the fixed size allocation sizes for these other regions that are made through procedural pushes of the Arena.


In cases of troubleshooting custom allocation issues, this is an important thing to note because it narrows problems down to the Dynamic Memory Heap -- which is essentially the only thing that feels the impact of changes to these other region sizes.

If this size shrinks too small, the resulting Dynamic Memory Heap will not be able to load certain files, like in the Character Select Scene. With knowledge of these other non-dynmaic allocation spaces, it may be possible to either tweak the other spaces to maximize the dynamic heap space available at runtime (despite Arena pushes) -- or utilize pockets in other RAM sections that aren’t being used for purposes that might normally be handled by pushing ArenaHi. These would come with the privileges of their respective regions, in terms of volatility -- and may serve as useful for code writing; whether for storing code, or just persistent data.

The ArenaHi pointer information in the static OS Globals from the disk header will cascade down into the various globals that reference it after boot (from the OSHeap to the HSD allocation table) -- causing it to be easy to reference from r13.

In particular, you can use -0x5a90(r13) to reach OSArenaLo, and -0x4330(r13) to reach OSArenaHi.

However, it’s only really useful to read from these before boot has finished initializing, because the Arena is closed after the first scene begins. ArenaLo will be pushed all the way up to ArenaHi when it hands off the remaining RAM to the HSD system -- at which point it's a matter of managing the HSD globals in order to manipulate the ranges. Injections made at that time though can seriously influence the way RAM is allocated for use by codes.
 

JoshuaMK

Smash Apprentice
Joined
May 19, 2019
Messages
75
Great explanation of Melee's memory management.

Its interesting that the game does this with the standard arena.. I haven't seen heap usage like it before.

GeckoLoader makes a non destructive allocation to OSArenaHi just as you described. This is how massive codespace is maintained in all games. A simple kernel patched into a dol and set as the starting address of the game, where the allocation and setup is done before the pointers in the OS Globals are read from..

The only drawback to this method is that the game fundamentally has X bytes less memory to work with for the rest of the game, which can potentially lead to games crashing due to failed allocations.
 
Last edited:
Top Bottom