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

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:
Function Modules:
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:
Function Modules:
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
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: