- 10
- Posts
- 7
- Years
- Seen Oct 11, 2023
Since late February 2018, I've been looking through Pokemon Mystery Dungeon: Red Rescue Team and would like to share what I've found so far.
Leveling System
Offsets 031DD9C-0357B87 in Red and system.sbin in Blue are the level maps (headed "lvmp"). When decompressed, each of these files contain 100 definitions of 12 bytes each. Each definition is made of a 4-byte integer that defines the cumulative experience to reach a level, 6 bytes defining stat gains, and a null short. The order of the stats is HP, Attack, Special Attack, Defense, and Special Defense. The second stat gain byte is always null. It likely would have been the speed stat if it were used in this game.
Learnsets
Learnset tables are located at offsets 0360C06-036799A. The pointer table for the learnsets is located at offsets 0372600-037333F. Each Pokemon gets 2 entries. The first entry is for level up, the second is for TM's. Each entry is null terminated. In the level up entries, each attack has a 1- or 2-byte attack identifier followed by a 1-byte level. In the TM entries, the level bytes are omitted. Which identifiers correspond to which attacks is unknown. Note that each Unown has its own entries.
Cutscene Scripting
List of commands
I=Relevant ID, P=Pointer, S=Speaker, T=Time in frames
01 00 TT TT XX XX XX XX 00 00 00 00 00 00 00 00 - Warp to location args: Destination
02 00 TT TT II II 00 00 00 00 00 00 00 00 00 00 - Warp to dungeon (I = 00-2C)
03 00 TT TT XX XX XX XX 00 00 00 00 00 00 00 00 - Warp to friend area map args: Unknown boolean
04 00 TT TT II II 00 00 00 00 00 00 00 00 00 00 - Warp to dungeon with map animation (I = 00-2C)
05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - Confirm dungeon rules
08 00 00 00 II II 00 00 00 00 00 00 00 00 00 00 - Change tileset
0C II FF FF 00 00 00 00 00 00 00 00 00 00 00 00 - Start scene? Run a call?
0D II FF FF 00 00 00 00 00 00 00 00 00 00 00 00 - Run a call
1B 00 II II 00 00 00 00 00 00 00 00 00 00 00 00 - Trigger cutscene (I = 73-FC, 101-10A, 14B-155)
22 ?? TT TT 00 00 00 00 00 00 00 00 00 00 00 00 - Stop all movement for a set time?
23 XX TT TT YY YY YY YY 00 00 00 00 00 00 00 00 - Fade out
27 XX YY YY TT TT TT TT ZZ ZZ ZZ ZZ 00 00 00 00 - Fade a color out (T=fade, Z=color)
28 XX YY YY TT TT TT TT ZZ ZZ ZZ ZZ 00 00 00 00 - Fade a color in (T=fade, Z=color)
2D XX YY YY ZZ ZZ 00 00 00 00 00 00 00 00 00 00 - Loads data args: Type, Character, Source
2E XX YY YY ZZ ZZ ZZ ZZ 00 00 00 00 00 00 00 00 - Select face args: Location, Character, Face
30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - Close text box
32 00 SS SS 00 00 00 00 00 00 00 00 PP PP PP PP - Text box w/ pic?
33 00 SS SS 00 00 00 00 00 00 00 00 PP PP PP PP - Scrolling text box w/ pic?
34 00 SS SS 00 00 00 00 00 00 00 00 PP PP PP PP - Text box w/ pic and name?
35 00 SS SS 00 00 00 00 00 00 00 00 PP PP PP PP - Text box w/o name or pic?
36 00 SS SS 00 00 00 00 00 00 00 00 PP PP PP PP - Quick close text box w/ pic
37 00 00 00 00 00 00 00 00 00 00 00 PP PP PP PP - Floating text
39 00 00 00 00 00 00 00 00 00 00 00 PP PP PP PP - Fading floating text
3A 00 XX XX SS SS 00 00 00 00 00 00 PP PP PP PP - Yes/No selection box args: Resulting event
3B XX YY 00 00 00 00 00 00 00 00 00 00 00 00 00 - Thread initialization related?
3C II 00 00 00 00 00 00 00 00 00 00 PP PP PP PP - Special text box (I = Type ID)
3C 2B 00 00 XX XX 00 00 00 00 00 00 00 00 00 00 - Rescue reward args: Item given (ID based on order of definitions in data)
3C 2E XX XX 00 00 TT 00 00 00 00 00 00 00 00 00 - Credits name args: Name
3D 00 00 00 XX 00 00 00 00 00 00 00 00 00 00 00 - Rename Pokemon
3E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - Rename team
42 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - Abruptly stop music
43 00 TT TT 00 00 00 00 00 00 00 00 00 00 00 00 - Fade music
44 00 II II 00 00 00 00 00 00 00 00 00 00 00 00 - Abruptly play music
45 00 II II TT TT 00 00 00 00 00 00 00 00 00 00 - Fade in music
47 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - Pushes r14 to the stack, then moves the program counter to 42's code. Use unknown. Hypothesis: used to return to the previous song.
48 00 TT TT 00 00 00 00 00 00 00 00 00 00 00 00 - Fade out music
49 00 00 00 II II 00 00 00 00 00 00 00 00 00 00 - Play sound?
4C 00 00 00 II II 00 00 00 00 00 00 00 00 00 00 - Play sound (I = C8-DB, 1C7-206, 258-3AB, Possibly more)
4D 00 00 00 II II 00 00 00 00 00 00 00 00 00 00 - Stop sound
52 00 00 00 XX XX XX XX 00 00 00 00 00 00 00 00
53 00 00 00 XX XX XX XX 00 00 00 00 00 00 00 00
54 00 XX XX 00 00 00 00 00 00 00 00 00 00 00 00 - Set animation
56 00 00 00 XX XX XX XX 00 00 00 00 00 00 00 00 - Show effect?
5B 00 00 00 II 00 00 00 00 00 00 00 00 00 00 00 - Warp to a predefined location
61 00 XX XX YY YY YY YY ZZ ZZ ZZ ZZ 00 00 00 00 - Walk to a absolute location without changing orientation?
62 00 XX XX YY YY YY YY ZZ ZZ ZZ ZZ 00 00 00 00 - Walk to a relative location without changing orientation
68 00 XX XX YY YY YY YY 00 00 00 00 00 00 00 00 - Change height
6A 00 XX XX YY YY YY YY ZZ ZZ ZZ ZZ 00 00 00 00 - Walk to a relative location args: Speed, Horizontal Offset, Vertical Offset
6B 00 XX XX II 00 00 00 00 00 00 00 00 00 00 00 - Walk to a predefined location along the grid args: Speed
7A 00 TT TT II 00 00 00 00 00 00 00 00 00 00 00 - Walk directly to a predefined location
8B XX YY 00 00 00 00 00 00 00 00 00 00 00 00 00 - Set facing direction args: Delay, Direction
91 TT XX 00 YY 00 00 00 00 00 00 00 00 00 00 00 - Rotate args: Way to turn, Final direction
92 XX YY YY ZZ ZZ ZZ ZZ 00 00 00 00 00 00 00 00 - Rotation of some sort
93 TT XX 00 YY 00 00 00 00 00 00 00 00 00 00 00 - Rotate args: Way to turn, Final direction?
CF XX YY 00 SS SS 00 00 00 00 00 00 00 00 00 00 - Initialize variable text box args: Type, Condition?
D0 00 XX XX 00 00 00 00 00 00 00 00 PP PP PP PP - Variable text box args: Version (Version is always odd- reason unknown)
D1 00 00 00 00 00 00 00 00 00 00 00 PP PP PP PP - "Else" text box (Used with above)
D5 00 FF FF SS SS 00 00 00 00 00 00 PP PP PP PP - Question
D8 00 FF FF SS SS 00 00 YY YY YY YY 00 00 00 00 - Initialize variable question
D9 00 II 00 00 00 00 00 00 00 00 00 PP PP PP PP - Answer option (I = result label)
DB 00 TT TT 00 00 00 00 00 00 00 00 00 00 00 00 - Pause
E2 00 II II 00 00 00 00 00 00 00 00 00 00 00 00 - Wait for a sound to finish playing?
E3 00 II 00 00 00 00 00 00 00 00 00 00 00 00 00 - Waits for a flag.
E4 00 II 00 00 00 00 00 00 00 00 00 00 00 00 00 - Sets a flag
E5 00 II 00 00 00 00 00 00 00 00 00 00 00 00 00 - Goto?
E7 00 II 00 00 00 00 00 00 00 00 00 00 00 00 00 - Loop until the end of a scene (I = label ID)
E8 00 II II 00 00 00 00 00 00 00 00 00 00 00 00 - Execute script
E9 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - End function, end script on text box close
EE 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - End function, continue script
EF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - End function and script
F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - Close a thread
F1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - Close thread and remove object
F4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - Label
Facing directions go counterclockwise starting from south. Directions of rotation are 1 for clockwise, 2 for counterclockwise, and A for the shortest direction.
Types used in load command
01- Face only? Z is ignored
07- ]All info? Z is ignored
09- Species name? Z is the character ID
Locations used in dialogue picture command
00- Bottom left
01- Center facing right
02- Bottom left
03- Bottom right
04- Inset bottom left
05- Inset bottom right
06- Center facing left
07- Bottom left facing left
08- Bottom right facing right
09- Inset bottom left facing left
0A- Inset bottom right facing right
0B- Top middle facing right
0C- Top left
0D- Top right
0E- Inset top left
0F- Inset top right
10- Top middle facing left
11- TL facing left
12- TR facing right
13- Inset top left facing left
14- Inset top right facing right
15- Same as last position
16- None
17- Off top left
18- None
19- Off top left facing left
1A- Off top, on left facing left
1B- Off top left
1C- Off top left facing left
>1C- Soft lock
Faces used in dialogue picture command for player/partner (others use a different system)
Only the bottom nybble matters. E and F observed as -2 and -1. D would likely appear as -3.
00- Neutral
01- Smiling
02- Pained
03- Angry
04- Confused
05- Sad
06- Crying
07- Shouting
08- Teary-eyed
09- Happy
0A- Delighted
0B- Inspired
0C- Shocked
0D- Empty frame
FE- None
FF- Same as last face
Types used in special dialogue command
D0 and D1 script commands are used to pick dialogue based on certain conditions. For example, when 2 or 3 different messages may appear depending on your partner, the first option uses D0 00 01 00, the second option (if there are 3) uses D0 00 03 00, and the last option (regardless of if there are 2 or 3) uses D1 00 00 00. These are preceded by CF if the resultant dialogue box is a statement or D5 for questions. The method location for D0 and D1 are the same as the command parse loop, meaning nothing is executed by these commands. Hypothesis: In the process of parsing, their pointer argument is pushed to the stack and isn't popped when execution moves on to the next command. The CF command opens a dialogue box that waits for a pointer to be pushed to the stack, then it displays the string associated with anything it finds.
Questions use a D9 command for each option. This command has a pointer to the text of the answer and a label that execution jumps to when that option is chosen. The code for these branches are seen immediately following the question that calls them. This may or may not be required in order for execution to find the labels. If the text box closes when the question is answered, the C0 command is after the label, not with the commands setting up the question box. In the unmodified code, branches created by questions typically end by jumping to the same label to return execution to a linear path. This is not required, enabling ROM hacks to set up multiple story routes based on answers given by the player.
Hypotheses:
Commands 0B, 0E, and 0F may be unused.
Commands 32, 49, and E1 are typically (maybe always) seen together and are likely related in some way.
Command 52 is commonly used near the end of the threads. It may be cleanup related.
Commands 86, 98, and 99 are used in the camera thread. They may be pan, init pan, and end pan, respectively.
Command 9B is commonly used near the end of the player thread. It may be cleanup related.
Commands B3 and CC are used in the map thread. B3 is seen before return and then a label. Skip/branch related? (18C858-18C968)
B9 appears branch related. Used many times in a row. Switch/case? Tends to appear in one scene per area, usually the third scene in ROM. Likely selects scenes and behaviors based on story flags. B7, B8, and BA may assist in this(18C858-18C968)
DD appears after 54 in Dugtrio burrow and Wobbuffet salute. Stop animation on current frame? (18C05C-18C4CC, 18D3C8-18D578)
EA is seen before branch leading to end of script. Likely branch condition (18C858-18C968)
Every thread starts with F6. This is likely a thread initialization command. Player threads follow this with some combination of E8, 2D, and E3 commands followed by DF. This may be a scene init completion signal command.
Scripts are multithreaded. There is a main thread that sets up a cutscene, dispatches threads for each character (and maybe the environment), waits for them to finish, and cleans up after the cutscene before exiting. Each thread can only affect the character associated with that thread. General actions such as dialogue boxes run in the player's thread. Synchronization is achieved using a flag system. The E4 command is used to set a flag. This is often followed by the E3 to wait for a flag. The main thread typically uses flag 3, the player thread typically uses flag 5, the partner thread typically uses flag 6, and other threads continue from there. It is possible for a thread to wait for any flag and multiple threads can wait for the same flag at once. Text data is typically located immediately after the code for the thread that references it. Each main thread has a 12-byte footer. This consists of 3 unknown bytes (in Tiny Woods they're always 900107), 5 null bytes, and a pointer to the first line of the main thread.
Map and scene data are broken up into what I've dubbed "calls". Most maps and scenes have a single call. The first call is run with 0C in the main thread during scene initialization. Subsequent calls are run with 0D by one of the character threads (possibly only the player thread) and result in additional characters being initialized and entering the scene.
Character IDs
00- Player
22- Partner
24- Random starter
25- Wartortle
26- Blastoise
27- Meowth
28- Lickitung
29- Jirachi
2A- Hypno
2B- Noctowl
2C- Metapod
2D- Jirachi
2E- Wigglytuff
2F- Pelipper
30- Pelipper
31- Pelipper
32- Pelipper
33- Azumarill
34- Crash
35- Butterfree
36- Caterpie
37- Pelipper
38- Pelipper
3A- Pelipper
3B- Pelipper
3C- Magnemite
3D- Magnemite
3E- Magnemite
3F- Magneton
40- Magnemite
41- Magnemite
42- Dugtrio
43- Dugtrio
44- Diglett
45- Skarmory
46- Green Kecleon
47- Purple Kecleon
48- Persian
49- Wigglytuff
4A- Gulpin
4B- Kangaskhan
4C- Gulpin
4D- Lombre
4E- Jumpluff
4F- Bellsprout
50- Snubbull
51- Granbull
52- Gardevoir
53- Absol
54- Makuhita
55- Shiftry
56- Nuzleaf
57- Nuzleaf
58- Alakazam
59- Charizard
5A- Tyranitar
5B- Gengar
5C- Ekans
5D- Medicham
5E- Metapod
5F- Jumpluff
60- Zapdos
61- Xatu
62- Wishcash
63- Ninetails
64- Decoy
65- Moltres
66- Articuno
67- Groudon
68- Blastoise
69- Octillery
6A- Golem
6B- Blastoise
6C- Rayquaza
6D- Wynaut
6E- Wobbuffet
6F- Mankey
70- Mankey
71- Mankey
72- Mankey
73- Spinda
74- Entei
75- Raikou
76- Suicune
77- Ho-oh
78- Mewtwo
79- Latios
7A- Latias
7B- Jirachi
7C- Smeargle
7D- Smeargle
7E- Smeargle
7F- Munchlax
80- Mew
81- Regirock
82- Regice
83- Registeel
84- Kyogre
85- Lugia
86- Deoxys
87- Raichu
88- Golbat
89- Rhydon
8A- Mr. Mime
8B- Scyther
8C- Pinsir
8D- Meganium
8E- Aipom
8F- Phanpy
90- Kangaskhan statue
91- Kangaskhan statue
92- Crash
93- Donphan
>93- Either crash or nothing
Format for overworld map data for cutscenes and interactions(idle behaviors and trigger points under research) Note that the order is not important, this simply shows the typical order.
- Map thread code
- A string, typically of the format "../data/ground/ground_data_d01p01_station.c" with various numbers
- A footer for the map thread code
- The main thread code and its footer followed by character thread code for each cutscene
- The character data for each cutscene. Entries consist of:
--- 1-byte character ID
--- 1-byte facing direction (ccw starting south)
--- 2 null bytes
--- 1-byte horizontal coordinate
--- 1-byte vertical coordinate
--- 1-byte bottom nybble of the horizontal coordinate
--- 1-byte bottom nybble of the vertical coordinate? (doesn't seem as consistent for some reason)
--- A pointer to the character's thread code
--- A pointer to a script of unknown use
--- A pointer to the interaction script
--- A pointer to a script of unknown use
- Scene data for each cutscene. Each entry is 12 bytes:
--- Bytes 4-7 are the camera's location (same format as character location)
--- Bytes 8-B appear to be a pointer to camera thread code for scenes involving panning or null for scenes with a static camera
- A pointer to map thread code's footer
- A pointer to the footer of each main thread's code
- The data for each "call" (more on that later). These are broken into 5 8-byte sections. Each may be null if it doesn't apply.
--- The number of characters that are part of this call (4 bytes)
--- A pointer to the data of the first character that is part of this call
--- The number of interactable objects in the area
--- A pointer to the interactable object data
--- An integer (always 1?)
--- A pointer to the camera data
--- Used for trigger points
--- An integer (always 1?)
--- A pointer to the footer pointer
- For each cutscene (counting the map thread), a 4-byte call count and a pointer to the corresponding pointer table
- Data for each waypoint in the map consisting of:
--- The location (same format as character location)
--- 4 unknown bytes (for Tiny Woods, always 01010201)
- An unknown integer (number of cutscenes counting the map thread?). The pointer table for all the overworld locations points here.
- A pointer to map/cutscene data pointer table
- A pointer to waypoint data
Interaction data (typical location in data unknown):
- Size of the interactable area (4 bytes)
- Location of interactable area (4 bytes)
- 3 pointers or null as applicable. All pointers go to script data and the difference between them is unknown.
- 4 null bytes
Graphics(updated)
Dungeon tiles are located at offsets 03B1CCC-049DBF3. Each set of tiles is part of a structure made of several parts:
Some of the dungeons that use these have been identified. They are (starting with 0):
Data for overworld background graphics begins at 1890000. Offsets 1890018-89153F are the pointer table. There are 0x2A5 (1890008-189000B) pairs of pointers. The first in a pair is the component's debug name (1891540-1892C6F). The second points to the beginning of the component's data. There are 4 types of components, which can easily be identified by the default debug name's suffix. Palettes have no suffix, animated excerpts have the suffix 1, block/chunk data has the suffix c, and chunk arrangement has the suffix m. The types of components used for each graphic fall into 6 main types:
Type 0- The basic type. It has a palette, block/chunk data, and chunk arrangement.
Type 1- Graphics with an excerpt. It has a palette, excerpt data, block/chunk data, and chunk arrangement. Examples include Rub-a-Dub River, Hill of the Ancients, and town areas.
Types 2 and 3- Type 0 graphics with an overlay of type 0 or 1, respectively. They have a palette, data for their overlay, block/chunk data, and chunk arrangement. Examples include underwater friend areas and Luminous Cave.
Types 4 and 5- Same as types 2 and 3, but the base graphic is type 1. The only known example is Crater (type 4).
Dream World/Dream Eater, the Aged Chambers, and the team base are special cases. Dream World and Dream Eater share block/chunk and chunk arrangement data but have different palettes. The Aged Chambers share block/chunk data but have different chunk arrangements and palettes. The team base is a series of type 1 graphics, each with multiple excerpts. The graphic used is determined by the player's type and construction progress while the excerpts used are determined by the player's species. Details are under investigation.
Palette data consists of a short representing the number of palettes, an unknown short, and the palettes. Each palette is 15 32-bit colors in RGBA format. Many graphics have several palettes that are identical but shifted over. This is likely to swap out palettes in order to animate tiles that change color but don't move.
Graphics are broken into chunks, which are further broken into 8x8-pixel blocks. Each block in a chunk and each chunk in a graphic is tiled in left-to-right rows going top-to-bottom.
Block data is preceded by a 16-byte header. This consists of the number of blocks in a chunk row (short), the number of blocks in a chunk column (short), the number of defined blocks (short), 8 unknown bytes, and the number of defined chunks (short).
Block data is 4bpp linear in reverse order. Some animated sections of a graphic are placed before the header of the main block data. These include flowers and lily pads seen in town, the sun at Hill of the Ancients, and the water wheel at Rub-a-Dub River. Their header consists of 2 shorts representing the excerpt's width and height in blocks followed by what is likely a series of integers representing the duration of each frame. What determines frame count is unknown.
In the chunk data, each block gets 2 bytes. The first byte and the 2 lsb in the second byte are the block index. Blocks are assigned consecutive indices based on their order in data. The first index is usually 1. However, in fully animated graphics and the aforementioned animated excerpts, the first index varies. For excerpts, the indices continue where the main part ended. Bit 2 of the second byte flips the block horizontally. Bit 3 of the second byte flips the block vertically. The upper nybble of the second byte selects the palette. There are several instances of tiles and palettes that look like they belong to Silent Chasm. This appears to be used as a placeholder when defining the layout of a static dungeon event map (which then uses the tiles for the relevant dungeon). Fully animated graphics such as attack animations precede the tiling data of each frame with an unidentified integer followed by a 16-byte header identical to the block data header (which is omitted from its typical location).
The data that encodes chunk order is preceded by a 12-byte header. This consists of the camera location (2 bytes, horizontal then vertical), the chunk width and height measured in blocks (2 shorts, redundant), the image width and height measured in chunks (2 bytes), an unknown integer (always 1?), and an unknown (boolean?) short. In the data, chunk values are in 3-byte pairs. If each letter is half of a byte, values ABC and abc are encoded as BC cA ab. These values are defined in rows according to the following rules:
- 0x inserts 0 (x+1)*2 times.
- 8x inserts the following pair of values x+1 times.
- Cx inserts the following x+1 pairs of values.
- Dx inserts the following x+17 pairs of values.
The resultant values are xor'ed with the respective indices of the previous row to get the indices for the current row. The first row assumes all previous indices were 0.
Misc.
Boolean 0x09 in dungeon data is true if the last staircase takes you to a rest area and false if it takes you out of the dungeon.
Short 0x04 in attack data is the strength factor. Non-damaging moves usually default to 2. This matches official Prima guide book and has been confirmed experimentally.
I've made a detailed map of the ROM and tagged the songs for sappy. I've also created a generalized AT4PX compressor/decompressor based on Zhorken's original kaomado tool. These files are attached.
I've also created a tool that automatically parses map data as it's understood so far. It should help with further research and will eventually become a full editing tool. If you want to help develop it, tag specific scenes and areas, or use it to discover anything new about map data, let me know. You can find its source code here.
My next goals are (in no particular order):
- Identify tracks 117, 119, 122, and 127 and find the tracks for Final Island, Legendary Island, Healing Forest, and Enclosed Island
- Identify the purpose/effect of each script command
- Analyze map data for idle behaviors and trigger points
- Determine Celebi's character code
- Research team base graphics
Credits:
Some sections of the ROM map and command table were taken from the Data Crystal wiki
The AT4PX tool is a derivative of Zhorken's kaomado program. I have email permission to make a derivative work.
Leveling System
Offsets 031DD9C-0357B87 in Red and system.sbin in Blue are the level maps (headed "lvmp"). When decompressed, each of these files contain 100 definitions of 12 bytes each. Each definition is made of a 4-byte integer that defines the cumulative experience to reach a level, 6 bytes defining stat gains, and a null short. The order of the stats is HP, Attack, Special Attack, Defense, and Special Defense. The second stat gain byte is always null. It likely would have been the speed stat if it were used in this game.
Learnsets
Learnset tables are located at offsets 0360C06-036799A. The pointer table for the learnsets is located at offsets 0372600-037333F. Each Pokemon gets 2 entries. The first entry is for level up, the second is for TM's. Each entry is null terminated. In the level up entries, each attack has a 1- or 2-byte attack identifier followed by a 1-byte level. In the TM entries, the level bytes are omitted. Which identifiers correspond to which attacks is unknown. Note that each Unown has its own entries.
Cutscene Scripting
List of commands
Spoiler:
I=Relevant ID, P=Pointer, S=Speaker, T=Time in frames
01 00 TT TT XX XX XX XX 00 00 00 00 00 00 00 00 - Warp to location args: Destination
02 00 TT TT II II 00 00 00 00 00 00 00 00 00 00 - Warp to dungeon (I = 00-2C)
03 00 TT TT XX XX XX XX 00 00 00 00 00 00 00 00 - Warp to friend area map args: Unknown boolean
04 00 TT TT II II 00 00 00 00 00 00 00 00 00 00 - Warp to dungeon with map animation (I = 00-2C)
05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - Confirm dungeon rules
08 00 00 00 II II 00 00 00 00 00 00 00 00 00 00 - Change tileset
0C II FF FF 00 00 00 00 00 00 00 00 00 00 00 00 - Start scene? Run a call?
0D II FF FF 00 00 00 00 00 00 00 00 00 00 00 00 - Run a call
1B 00 II II 00 00 00 00 00 00 00 00 00 00 00 00 - Trigger cutscene (I = 73-FC, 101-10A, 14B-155)
22 ?? TT TT 00 00 00 00 00 00 00 00 00 00 00 00 - Stop all movement for a set time?
23 XX TT TT YY YY YY YY 00 00 00 00 00 00 00 00 - Fade out
27 XX YY YY TT TT TT TT ZZ ZZ ZZ ZZ 00 00 00 00 - Fade a color out (T=fade, Z=color)
28 XX YY YY TT TT TT TT ZZ ZZ ZZ ZZ 00 00 00 00 - Fade a color in (T=fade, Z=color)
2D XX YY YY ZZ ZZ 00 00 00 00 00 00 00 00 00 00 - Loads data args: Type, Character, Source
2E XX YY YY ZZ ZZ ZZ ZZ 00 00 00 00 00 00 00 00 - Select face args: Location, Character, Face
30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - Close text box
32 00 SS SS 00 00 00 00 00 00 00 00 PP PP PP PP - Text box w/ pic?
33 00 SS SS 00 00 00 00 00 00 00 00 PP PP PP PP - Scrolling text box w/ pic?
34 00 SS SS 00 00 00 00 00 00 00 00 PP PP PP PP - Text box w/ pic and name?
35 00 SS SS 00 00 00 00 00 00 00 00 PP PP PP PP - Text box w/o name or pic?
36 00 SS SS 00 00 00 00 00 00 00 00 PP PP PP PP - Quick close text box w/ pic
37 00 00 00 00 00 00 00 00 00 00 00 PP PP PP PP - Floating text
39 00 00 00 00 00 00 00 00 00 00 00 PP PP PP PP - Fading floating text
3A 00 XX XX SS SS 00 00 00 00 00 00 PP PP PP PP - Yes/No selection box args: Resulting event
3B XX YY 00 00 00 00 00 00 00 00 00 00 00 00 00 - Thread initialization related?
3C II 00 00 00 00 00 00 00 00 00 00 PP PP PP PP - Special text box (I = Type ID)
3C 2B 00 00 XX XX 00 00 00 00 00 00 00 00 00 00 - Rescue reward args: Item given (ID based on order of definitions in data)
3C 2E XX XX 00 00 TT 00 00 00 00 00 00 00 00 00 - Credits name args: Name
3D 00 00 00 XX 00 00 00 00 00 00 00 00 00 00 00 - Rename Pokemon
3E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - Rename team
42 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - Abruptly stop music
43 00 TT TT 00 00 00 00 00 00 00 00 00 00 00 00 - Fade music
44 00 II II 00 00 00 00 00 00 00 00 00 00 00 00 - Abruptly play music
45 00 II II TT TT 00 00 00 00 00 00 00 00 00 00 - Fade in music
47 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - Pushes r14 to the stack, then moves the program counter to 42's code. Use unknown. Hypothesis: used to return to the previous song.
48 00 TT TT 00 00 00 00 00 00 00 00 00 00 00 00 - Fade out music
49 00 00 00 II II 00 00 00 00 00 00 00 00 00 00 - Play sound?
4C 00 00 00 II II 00 00 00 00 00 00 00 00 00 00 - Play sound (I = C8-DB, 1C7-206, 258-3AB, Possibly more)
4D 00 00 00 II II 00 00 00 00 00 00 00 00 00 00 - Stop sound
52 00 00 00 XX XX XX XX 00 00 00 00 00 00 00 00
53 00 00 00 XX XX XX XX 00 00 00 00 00 00 00 00
54 00 XX XX 00 00 00 00 00 00 00 00 00 00 00 00 - Set animation
56 00 00 00 XX XX XX XX 00 00 00 00 00 00 00 00 - Show effect?
5B 00 00 00 II 00 00 00 00 00 00 00 00 00 00 00 - Warp to a predefined location
61 00 XX XX YY YY YY YY ZZ ZZ ZZ ZZ 00 00 00 00 - Walk to a absolute location without changing orientation?
62 00 XX XX YY YY YY YY ZZ ZZ ZZ ZZ 00 00 00 00 - Walk to a relative location without changing orientation
68 00 XX XX YY YY YY YY 00 00 00 00 00 00 00 00 - Change height
6A 00 XX XX YY YY YY YY ZZ ZZ ZZ ZZ 00 00 00 00 - Walk to a relative location args: Speed, Horizontal Offset, Vertical Offset
6B 00 XX XX II 00 00 00 00 00 00 00 00 00 00 00 - Walk to a predefined location along the grid args: Speed
7A 00 TT TT II 00 00 00 00 00 00 00 00 00 00 00 - Walk directly to a predefined location
8B XX YY 00 00 00 00 00 00 00 00 00 00 00 00 00 - Set facing direction args: Delay, Direction
91 TT XX 00 YY 00 00 00 00 00 00 00 00 00 00 00 - Rotate args: Way to turn, Final direction
92 XX YY YY ZZ ZZ ZZ ZZ 00 00 00 00 00 00 00 00 - Rotation of some sort
93 TT XX 00 YY 00 00 00 00 00 00 00 00 00 00 00 - Rotate args: Way to turn, Final direction?
CF XX YY 00 SS SS 00 00 00 00 00 00 00 00 00 00 - Initialize variable text box args: Type, Condition?
D0 00 XX XX 00 00 00 00 00 00 00 00 PP PP PP PP - Variable text box args: Version (Version is always odd- reason unknown)
D1 00 00 00 00 00 00 00 00 00 00 00 PP PP PP PP - "Else" text box (Used with above)
D5 00 FF FF SS SS 00 00 00 00 00 00 PP PP PP PP - Question
D8 00 FF FF SS SS 00 00 YY YY YY YY 00 00 00 00 - Initialize variable question
D9 00 II 00 00 00 00 00 00 00 00 00 PP PP PP PP - Answer option (I = result label)
DB 00 TT TT 00 00 00 00 00 00 00 00 00 00 00 00 - Pause
E2 00 II II 00 00 00 00 00 00 00 00 00 00 00 00 - Wait for a sound to finish playing?
E3 00 II 00 00 00 00 00 00 00 00 00 00 00 00 00 - Waits for a flag.
E4 00 II 00 00 00 00 00 00 00 00 00 00 00 00 00 - Sets a flag
E5 00 II 00 00 00 00 00 00 00 00 00 00 00 00 00 - Goto?
E7 00 II 00 00 00 00 00 00 00 00 00 00 00 00 00 - Loop until the end of a scene (I = label ID)
E8 00 II II 00 00 00 00 00 00 00 00 00 00 00 00 - Execute script
E9 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - End function, end script on text box close
EE 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - End function, continue script
EF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - End function and script
F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - Close a thread
F1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - Close thread and remove object
F4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - Label
Facing directions go counterclockwise starting from south. Directions of rotation are 1 for clockwise, 2 for counterclockwise, and A for the shortest direction.
Types used in load command
01- Face only? Z is ignored
07- ]All info? Z is ignored
09- Species name? Z is the character ID
Locations used in dialogue picture command
Spoiler:
00- Bottom left
01- Center facing right
02- Bottom left
03- Bottom right
04- Inset bottom left
05- Inset bottom right
06- Center facing left
07- Bottom left facing left
08- Bottom right facing right
09- Inset bottom left facing left
0A- Inset bottom right facing right
0B- Top middle facing right
0C- Top left
0D- Top right
0E- Inset top left
0F- Inset top right
10- Top middle facing left
11- TL facing left
12- TR facing right
13- Inset top left facing left
14- Inset top right facing right
15- Same as last position
16- None
17- Off top left
18- None
19- Off top left facing left
1A- Off top, on left facing left
1B- Off top left
1C- Off top left facing left
>1C- Soft lock
Faces used in dialogue picture command for player/partner (others use a different system)
Only the bottom nybble matters. E and F observed as -2 and -1. D would likely appear as -3.
Spoiler:
00- Neutral
01- Smiling
02- Pained
03- Angry
04- Confused
05- Sad
06- Crying
07- Shouting
08- Teary-eyed
09- Happy
0A- Delighted
0B- Inspired
0C- Shocked
0D- Empty frame
FE- None
FF- Same as last face
Types used in special dialogue command
02 | Waiting | 04 | Player name input | 05 | Team name input | 06 | Password input |
07 | Friend Menu | 09 | Yes/No /w large box | 0A | Large text box | 0B | Buy friend areas |
0C | Dungeon list | 0D | Dojo list | 0E | Saving | 10 | Storage /w dialog |
11 | Storage | 12 | Bank /w dialog? | 13 | Bank | 16 | Friend area select |
17 | Green Kecleon shop | 18 | Purple Kecleon shop | 19 | Link shop | 1A | Luminous Cave |
1B | Friend shop | 1C | Friend rescue | 1E | Thank you mail | 1F | PPO help counter |
20 | Bulletin board jobs | 21 | Bulletin board | 24 | Dojo enter | 25 | Dojo success |
26 | Dojo failure | 27 | Dojo all cleared | 28 | Personality quiz | 2A | Scripting menu |
2D | Tool box |
D0 and D1 script commands are used to pick dialogue based on certain conditions. For example, when 2 or 3 different messages may appear depending on your partner, the first option uses D0 00 01 00, the second option (if there are 3) uses D0 00 03 00, and the last option (regardless of if there are 2 or 3) uses D1 00 00 00. These are preceded by CF if the resultant dialogue box is a statement or D5 for questions. The method location for D0 and D1 are the same as the command parse loop, meaning nothing is executed by these commands. Hypothesis: In the process of parsing, their pointer argument is pushed to the stack and isn't popped when execution moves on to the next command. The CF command opens a dialogue box that waits for a pointer to be pushed to the stack, then it displays the string associated with anything it finds.
Questions use a D9 command for each option. This command has a pointer to the text of the answer and a label that execution jumps to when that option is chosen. The code for these branches are seen immediately following the question that calls them. This may or may not be required in order for execution to find the labels. If the text box closes when the question is answered, the C0 command is after the label, not with the commands setting up the question box. In the unmodified code, branches created by questions typically end by jumping to the same label to return execution to a linear path. This is not required, enabling ROM hacks to set up multiple story routes based on answers given by the player.
Hypotheses:
Commands 0B, 0E, and 0F may be unused.
Commands 32, 49, and E1 are typically (maybe always) seen together and are likely related in some way.
Command 52 is commonly used near the end of the threads. It may be cleanup related.
Commands 86, 98, and 99 are used in the camera thread. They may be pan, init pan, and end pan, respectively.
Command 9B is commonly used near the end of the player thread. It may be cleanup related.
Commands B3 and CC are used in the map thread. B3 is seen before return and then a label. Skip/branch related? (18C858-18C968)
B9 appears branch related. Used many times in a row. Switch/case? Tends to appear in one scene per area, usually the third scene in ROM. Likely selects scenes and behaviors based on story flags. B7, B8, and BA may assist in this(18C858-18C968)
DD appears after 54 in Dugtrio burrow and Wobbuffet salute. Stop animation on current frame? (18C05C-18C4CC, 18D3C8-18D578)
EA is seen before branch leading to end of script. Likely branch condition (18C858-18C968)
Every thread starts with F6. This is likely a thread initialization command. Player threads follow this with some combination of E8, 2D, and E3 commands followed by DF. This may be a scene init completion signal command.
Scripts are multithreaded. There is a main thread that sets up a cutscene, dispatches threads for each character (and maybe the environment), waits for them to finish, and cleans up after the cutscene before exiting. Each thread can only affect the character associated with that thread. General actions such as dialogue boxes run in the player's thread. Synchronization is achieved using a flag system. The E4 command is used to set a flag. This is often followed by the E3 to wait for a flag. The main thread typically uses flag 3, the player thread typically uses flag 5, the partner thread typically uses flag 6, and other threads continue from there. It is possible for a thread to wait for any flag and multiple threads can wait for the same flag at once. Text data is typically located immediately after the code for the thread that references it. Each main thread has a 12-byte footer. This consists of 3 unknown bytes (in Tiny Woods they're always 900107), 5 null bytes, and a pointer to the first line of the main thread.
Map and scene data are broken up into what I've dubbed "calls". Most maps and scenes have a single call. The first call is run with 0C in the main thread during scene initialization. Subsequent calls are run with 0D by one of the character threads (possibly only the player thread) and result in additional characters being initialized and entering the scene.
Character IDs
Spoiler:
00- Player
22- Partner
24- Random starter
25- Wartortle
26- Blastoise
27- Meowth
28- Lickitung
29- Jirachi
2A- Hypno
2B- Noctowl
2C- Metapod
2D- Jirachi
2E- Wigglytuff
2F- Pelipper
30- Pelipper
31- Pelipper
32- Pelipper
33- Azumarill
34- Crash
35- Butterfree
36- Caterpie
37- Pelipper
38- Pelipper
3A- Pelipper
3B- Pelipper
3C- Magnemite
3D- Magnemite
3E- Magnemite
3F- Magneton
40- Magnemite
41- Magnemite
42- Dugtrio
43- Dugtrio
44- Diglett
45- Skarmory
46- Green Kecleon
47- Purple Kecleon
48- Persian
49- Wigglytuff
4A- Gulpin
4B- Kangaskhan
4C- Gulpin
4D- Lombre
4E- Jumpluff
4F- Bellsprout
50- Snubbull
51- Granbull
52- Gardevoir
53- Absol
54- Makuhita
55- Shiftry
56- Nuzleaf
57- Nuzleaf
58- Alakazam
59- Charizard
5A- Tyranitar
5B- Gengar
5C- Ekans
5D- Medicham
5E- Metapod
5F- Jumpluff
60- Zapdos
61- Xatu
62- Wishcash
63- Ninetails
64- Decoy
65- Moltres
66- Articuno
67- Groudon
68- Blastoise
69- Octillery
6A- Golem
6B- Blastoise
6C- Rayquaza
6D- Wynaut
6E- Wobbuffet
6F- Mankey
70- Mankey
71- Mankey
72- Mankey
73- Spinda
74- Entei
75- Raikou
76- Suicune
77- Ho-oh
78- Mewtwo
79- Latios
7A- Latias
7B- Jirachi
7C- Smeargle
7D- Smeargle
7E- Smeargle
7F- Munchlax
80- Mew
81- Regirock
82- Regice
83- Registeel
84- Kyogre
85- Lugia
86- Deoxys
87- Raichu
88- Golbat
89- Rhydon
8A- Mr. Mime
8B- Scyther
8C- Pinsir
8D- Meganium
8E- Aipom
8F- Phanpy
90- Kangaskhan statue
91- Kangaskhan statue
92- Crash
93- Donphan
>93- Either crash or nothing
Format for overworld map data for cutscenes and interactions(idle behaviors and trigger points under research) Note that the order is not important, this simply shows the typical order.
Spoiler:
- Map thread code
- A string, typically of the format "../data/ground/ground_data_d01p01_station.c" with various numbers
- A footer for the map thread code
- The main thread code and its footer followed by character thread code for each cutscene
- The character data for each cutscene. Entries consist of:
--- 1-byte character ID
--- 1-byte facing direction (ccw starting south)
--- 2 null bytes
--- 1-byte horizontal coordinate
--- 1-byte vertical coordinate
--- 1-byte bottom nybble of the horizontal coordinate
--- 1-byte bottom nybble of the vertical coordinate? (doesn't seem as consistent for some reason)
--- A pointer to the character's thread code
--- A pointer to a script of unknown use
--- A pointer to the interaction script
--- A pointer to a script of unknown use
- Scene data for each cutscene. Each entry is 12 bytes:
--- Bytes 4-7 are the camera's location (same format as character location)
--- Bytes 8-B appear to be a pointer to camera thread code for scenes involving panning or null for scenes with a static camera
- A pointer to map thread code's footer
- A pointer to the footer of each main thread's code
- The data for each "call" (more on that later). These are broken into 5 8-byte sections. Each may be null if it doesn't apply.
--- The number of characters that are part of this call (4 bytes)
--- A pointer to the data of the first character that is part of this call
--- The number of interactable objects in the area
--- A pointer to the interactable object data
--- An integer (always 1?)
--- A pointer to the camera data
--- Used for trigger points
--- An integer (always 1?)
--- A pointer to the footer pointer
- For each cutscene (counting the map thread), a 4-byte call count and a pointer to the corresponding pointer table
- Data for each waypoint in the map consisting of:
--- The location (same format as character location)
--- 4 unknown bytes (for Tiny Woods, always 01010201)
- An unknown integer (number of cutscenes counting the map thread?). The pointer table for all the overworld locations points here.
- A pointer to map/cutscene data pointer table
- A pointer to waypoint data
Interaction data (typical location in data unknown):
- Size of the interactable area (4 bytes)
- Location of interactable area (4 bytes)
- 3 pointers or null as applicable. All pointers go to script data and the difference between them is unknown.
- 4 null bytes
Graphics(updated)
Dungeon tiles are located at offsets 03B1CCC-049DBF3. Each set of tiles is part of a structure made of several parts:
Spoiler:
A SIRO header, palettes, and a pointer table
Unidentified compressed data
Unidentified compressed data with a SIRO header
The tiles themselves compressed in AT4PX format
Palettes
Unidentified compressed data
Unidentified compressed data with a SIRO header
The tiles themselves compressed in AT4PX format
Palettes
Some of the dungeons that use these have been identified. They are (starting with 0):
Spoiler:
2- Silent Chasm
4- Murky Cave
5- Ground/Rock/Normal Maze
6- Pitfall Valley
7- Unown Relic
9- Mt. Faraway
A- Wyvern Hill
B- Possibly Purity Forest
C- Joyous Tower
D- Darknight Relic
E- Tiny Woods
10- Lapis Cave
4- Murky Cave
5- Ground/Rock/Normal Maze
6- Pitfall Valley
7- Unown Relic
9- Mt. Faraway
A- Wyvern Hill
B- Possibly Purity Forest
C- Joyous Tower
D- Darknight Relic
E- Tiny Woods
10- Lapis Cave
Data for overworld background graphics begins at 1890000. Offsets 1890018-89153F are the pointer table. There are 0x2A5 (1890008-189000B) pairs of pointers. The first in a pair is the component's debug name (1891540-1892C6F). The second points to the beginning of the component's data. There are 4 types of components, which can easily be identified by the default debug name's suffix. Palettes have no suffix, animated excerpts have the suffix 1, block/chunk data has the suffix c, and chunk arrangement has the suffix m. The types of components used for each graphic fall into 6 main types:
Type 0- The basic type. It has a palette, block/chunk data, and chunk arrangement.
Type 1- Graphics with an excerpt. It has a palette, excerpt data, block/chunk data, and chunk arrangement. Examples include Rub-a-Dub River, Hill of the Ancients, and town areas.
Types 2 and 3- Type 0 graphics with an overlay of type 0 or 1, respectively. They have a palette, data for their overlay, block/chunk data, and chunk arrangement. Examples include underwater friend areas and Luminous Cave.
Types 4 and 5- Same as types 2 and 3, but the base graphic is type 1. The only known example is Crater (type 4).
Dream World/Dream Eater, the Aged Chambers, and the team base are special cases. Dream World and Dream Eater share block/chunk and chunk arrangement data but have different palettes. The Aged Chambers share block/chunk data but have different chunk arrangements and palettes. The team base is a series of type 1 graphics, each with multiple excerpts. The graphic used is determined by the player's type and construction progress while the excerpts used are determined by the player's species. Details are under investigation.
Palette data consists of a short representing the number of palettes, an unknown short, and the palettes. Each palette is 15 32-bit colors in RGBA format. Many graphics have several palettes that are identical but shifted over. This is likely to swap out palettes in order to animate tiles that change color but don't move.
Graphics are broken into chunks, which are further broken into 8x8-pixel blocks. Each block in a chunk and each chunk in a graphic is tiled in left-to-right rows going top-to-bottom.
Block data is preceded by a 16-byte header. This consists of the number of blocks in a chunk row (short), the number of blocks in a chunk column (short), the number of defined blocks (short), 8 unknown bytes, and the number of defined chunks (short).
Block data is 4bpp linear in reverse order. Some animated sections of a graphic are placed before the header of the main block data. These include flowers and lily pads seen in town, the sun at Hill of the Ancients, and the water wheel at Rub-a-Dub River. Their header consists of 2 shorts representing the excerpt's width and height in blocks followed by what is likely a series of integers representing the duration of each frame. What determines frame count is unknown.
In the chunk data, each block gets 2 bytes. The first byte and the 2 lsb in the second byte are the block index. Blocks are assigned consecutive indices based on their order in data. The first index is usually 1. However, in fully animated graphics and the aforementioned animated excerpts, the first index varies. For excerpts, the indices continue where the main part ended. Bit 2 of the second byte flips the block horizontally. Bit 3 of the second byte flips the block vertically. The upper nybble of the second byte selects the palette. There are several instances of tiles and palettes that look like they belong to Silent Chasm. This appears to be used as a placeholder when defining the layout of a static dungeon event map (which then uses the tiles for the relevant dungeon). Fully animated graphics such as attack animations precede the tiling data of each frame with an unidentified integer followed by a 16-byte header identical to the block data header (which is omitted from its typical location).
The data that encodes chunk order is preceded by a 12-byte header. This consists of the camera location (2 bytes, horizontal then vertical), the chunk width and height measured in blocks (2 shorts, redundant), the image width and height measured in chunks (2 bytes), an unknown integer (always 1?), and an unknown (boolean?) short. In the data, chunk values are in 3-byte pairs. If each letter is half of a byte, values ABC and abc are encoded as BC cA ab. These values are defined in rows according to the following rules:
- 0x inserts 0 (x+1)*2 times.
- 8x inserts the following pair of values x+1 times.
- Cx inserts the following x+1 pairs of values.
- Dx inserts the following x+17 pairs of values.
The resultant values are xor'ed with the respective indices of the previous row to get the indices for the current row. The first row assumes all previous indices were 0.
Misc.
Boolean 0x09 in dungeon data is true if the last staircase takes you to a rest area and false if it takes you out of the dungeon.
Short 0x04 in attack data is the strength factor. Non-damaging moves usually default to 2. This matches official Prima guide book and has been confirmed experimentally.
I've made a detailed map of the ROM and tagged the songs for sappy. I've also created a generalized AT4PX compressor/decompressor based on Zhorken's original kaomado tool. These files are attached.
I've also created a tool that automatically parses map data as it's understood so far. It should help with further research and will eventually become a full editing tool. If you want to help develop it, tag specific scenes and areas, or use it to discover anything new about map data, let me know. You can find its source code here.
My next goals are (in no particular order):
- Identify tracks 117, 119, 122, and 127 and find the tracks for Final Island, Legendary Island, Healing Forest, and Enclosed Island
- Identify the purpose/effect of each script command
- Analyze map data for idle behaviors and trigger points
- Determine Celebi's character code
- Research team base graphics
Credits:
Some sections of the ROM map and command table were taken from the Data Crystal wiki
The AT4PX tool is a derivative of Zhorken's kaomado program. I have email permission to make a derivative work.
Attachments
Last edited: