This manual currently covers the following topics:
- Filesystem specification
- overlay conf
- scene conf
- Entrance cutscenes
- Animation banks
- misc directory
- Sounds and music
- Adapting old tutorials
- scene TEXT-DATA
zzromtool isn't magic. It expects folders to follow a certain specification, and if they don't, it may crash. When you dump a rom, the following files and folders are created. As you make changes, keep these things in mind about each.
|actor||This folder contains every actor overlay in the game.|
Every folder within this folder must start with a number. For example, '2' and '2 - Stalfos' are both suitable names. 'Stalfos - 2' is not.
Within each folder, the name 'actor.zovl' is expected for the actor, with initialization values and other settings being stored within 'conf.txt'. If zzromtool fails to locate either, the entry will be skipped.
conf.txt must remain below 1 KB. If parsing it fails, the program will complain and exit. Values in conf.txt are assumed to be decimal unless preceded by 0x for hex.
The actor overlay itself has the magic string scubadiver in place of the first part of its initialization data so the program can find them during import. The affected values are Type, Room, Flags, and Object. These get pulled from conf.txt during import. Keep this in mind when working with custom actors.
Read overlay conf for information on the other parts.
|anim||This folder contains Link's external animation data. See Animation banks for more info.|
|cache||When building a compressed rom, this folder is created to store a compressed copy of every file in the game. The initial compression should take no longer than 5 minutes. Subsequent compressions will reference this cache for files that have not been changed, cutting compression time from minutes to seconds.|
|misc||Contains miscellaneous files such as Link's animation data, all the game's text, the font, etc. See misc directory for more info.|
|object||This folder contains every object file in the game.|
Every folder within this folder must start with a number. For example, '50' and '50 - Stalfos' are both suitable names. 'Stalfos - 50' is not.
Within each folder, the name 'object.zobj' is expected for the object. If zzromtool fails to locate it, the entry will be skipped.
|particle||This folder contains every particle effect in the game.|
The folder structure is the same as actor, with some slight differences.
The first four bytes of initialization data are overwritten with the magic string tuna so the program can find them during import. When writing custom particle overlays, keep this in mind.
Read overlay conf for a generalized explanation.
|patch||Novelty feature for compartmentalizing multiple edits to the same file. Read patch directory for an example. Introduced in revision 0.|
|scene||This folder contains every scene (map) in the game.|
Every folder within this folder must start with a number. For example, '85' and '85 - Kokiri Forest' are both suitable names. 'Kokiri Forest - 85' is not.
Within each folder, the name scene.zscene is expected for the scene. If zzromtool fails to locate scene.zscene, it will choose whatever file with the extension .zscene (case sensitive) it finds first. If zzromtool still fails to locate a scene, the entry will be skipped.
Also within each folder, room_x.zmap is expected for each room, with x starting at 0 and incrementing by 1 for each. If filenames of the form room_x.zmap are not found, it will search for files with the extension .zmap (case sensitive) and use the last number found in the filename for x. If it finds a scene but no rooms, it will complain.
External scene settings will be stored in conf.txt. See Adding scenes (maps) for an explanation of how it works.
An optional title.png can be included in a scene's folder to use as the title card when entering an area.
|shader||These are more formally considered "scene render initialization functions", but that's a mouthful.|
Each sub-folder name is expected to start with a number. Counting starts at 0 and numbers should not be skipped. This is the order in which they are indexed and referenced by scenes, so keep that in mind.
A file shader.bin is expected in each folder. If one is not found, the entry is skipped.
Shader assembly should contain only one jr ra, and at the end of the function. Shaders are how scrolling textures, day/night textures, pulsing colors in places like Death Mountain Crater, etc are possible.
When a guide for custom shaders is written, a link will be added here.
|skybox||Currently unused|This folder contains every sky box in the game.
Inspect the contents of this folder. They are self-explanatory. You are limited to editing existing sky boxes only. Adding new sky boxes will break the program.
|system||This folder contains files most people won't be tampering with. Edit them only if you know what you're doing. Some of these files support being made larger, but not all.|
|repoint.tsv||Contains a list of pointers to update across files that have hard-coded references to other files. See the repoint.tsv section of this document.|
|route.txt||Configures how scenes are to be routed together. See Routes section of this document.|
|entrance-cutscenes.txt||Configures when and where entrance cutscenes should play. See Entrance cutscenes section of this document.|
The conf.txt within an actor folder looks like this:
Reserve 0x00000000 (disabled as of revision 3)
Everything indented below
Initialization gets written into actor.zovl during import, when the program finds the magic string scubadiver within the actor. The reason for this is to make it easier to share assets between projects, and also make it possible for the program to have a means of detecting where the initialization data is stored within the actor. Otherwise, you'd have to point to the initialization data directly, and every time you rebuild the actor (assuming it's custom), the initialization data may move, and thus there would be an extra step after each rebuild: manually adjusting the pointer within conf.txt. This is to avoid such madness. tl;dr: In your custom actor's source, use the string
scubadiver in the appropriate section of the initialization data.
As of 0.01 revision 2, an additional search method is available:
In an actor's initialization data, the first two bytes, which normally represent the actor's number
, should be replaced with the bytes 0xDEAD
. Ten bytes later are two unused padding bytes
, which we replace with 0xBEEF
. See seagull.c
for an example of this applied in C. You may also omit the Initialization section of conf.txt
* During import, 0xDEAD is replaced with the number of the folder the actor is in, and 0xBEEF is replaced with 0x0000.
is for allocation type, described on the wiki
Reserve is a hackish setup for how many bytes follow the overlays in VRAM. Some overlays reserve a few bytes beyond the end of themselves. For relaying information between multiple instances, I would assume. ex: the Lizalfos tag-team
Disabled as of revision 3 (it turned out to be unnecessary clutter.)
VRAM is basically the relative start address that was used when compiling the actor. If you change this value for an actor, you'll have to rebuild it from source. If you're writing a custom actor, this will most likely be
0x80800000 and you'll never worry about it beyond that.
The same rules apply for particle overlays, but the magic string is
tuna instead of
scubadiver, and the only parts of conf.txt are
VRAM. They work the same way as described above.
Custom actor and particle overlay examples will be posted below as they are made.
conf.txt within a scene folder looks like this:
unk-b refer to unknown parts of the scene format. See the wiki for more information on that.
shader refers to the scene render initialization function used for the scene, or shader for short. In this example, the value
4 indicates that the shader in the folder
shader/4/shader.bin is the one used here.
save refers to the save data slot used by the scene, for storing flags like cleared puzzles. By default, every scene in the game has its own save data slot, but due to the nature of the save file format, we won't be expanding it any time soon, meaning the best way to add new scenes to the game and still have flag storage available for them is to have scenes share save data slots. This is feasible because most outdoor scenes in the game use little to no flags. Dungeons, which are very flag-heavy, should not share flags, but you're free to make even dungeons share flags if you want to. This value should not exceed 100.
restrict is a decimal value containing restriction flags. Read more about restriction flags here. A helper utility for generating restriction flags would be nice, perhaps something that would embed into a webpage. I'm looking at you, CrookedPoe.
Numbers are all decimal, not hexadecimal.
Thanks to the one-and-only CloudMax, we have an additional
code file, which we'll dub as
code1. The max on this is 1 MB (0x100000 bytes). All custom assembly should go in this file. Custom
shader functions will automatically be appended to
code1 during the build process, so try to keep it a few KB short of 1 MB. It is recommended to keep this file small and only expand as needed.
code1 starts at
80700000 in ram, so a function or data stored at the offset
0x50 (to make an example) would be accessed through the ram address
code1 is located in the
As of revision 3, you can edit the
CODE1VRAM=0x80700000 in the ZZRP file to any other ram address that suits your needs.
Find and open
entrance-cutscenes.txt. Its contents are formatted as such:
Route Age Flag Offset
389 2 0xA0 02013AA0
The first line in the file describes which columns do what.
Each subsequent line specifies how and when each entrance cutscene should be played.
Route specifies which route (decimal) Link must travel through to activate the cutscene.
Age specifies which Link activates the cutscene. (
0 = Old,
1 = Young,
2 = Both)
Flag is how the game knows if the cutscene has played or not. The value used for flag specifies which slot of
event_chk_inf to store this information in. This will be unique for every entrance cutscene except for the Epona escape cutscenes, which share the same value so the game knows the player has acquired Epona.
Offset specifies the offset (hexadecimal) of the cutscene data within the scene file. For custom maps with custom entrance cutscenes, you may not know what value to use here. Write
AUTO and zzromtool will use the first cutscene it finds in the scene. If it doesn't find any, it will complain. You can specify
n_AUTO to use the nth cutscene found in the file.
n is a decimal number. Counting starts at 0, meaning
0_AUTO is the first found. Keep in mind it is up to the map converter you use to have an implementation of TEXT-DATA in order to use the auto-detection feature. Read
scene TEXT-DATA for implementation details.
Route Age Flag Offset
389 2 0xA0 0_AUTO
Route Age Flag Offset
389 2 0xA0 1_AUTO
misc folder, Link's animation data resides in the form of two files:
link_animetion_table [sic]. Due to the nature of how Link's animation table is referenced by the game, the program does not yet offer a solution for adding animations, only replacing. On the plus side, a custom animation can still have either more or less frames than the one you're replacing.
When the Blender plug-in for generating custom Link animation data is ready, a link to a tutorial will be posted here.
This directory contains miscellaneous files that don't really belong anywhere else. The contents of this folder are not compressed during the compression routine, much like the original game. A folder named
comp exists within
misc, for storing files meant to be compressed. This is intentional by design, as some files in the game are not meant to be compressed. Please do not move existing files from
comp unless you know what you are doing.
The order in which these files are placed in the rom is based on the contents of
repoint.tsv, except that the contents of
comp are loaded in after the contents of
misc can contain more folders than
comp. By default, the program will create
skybox and another
comp folder within
comp directory in this case gets compressed as well. As a general rule, any file reference containing
/comp/ gets compressed. This applies even to new folders you create and the folders within them. Please reference skybox directory for more information on the naming schemes used for skyboxes.
The files in these directories should NOT have extensions.
Files not referenced within
repoint.tsv will be skipped.
Some files in the game don't belong in any kind of table and are otherwise unreferenced. Couple this with the fact that they are often referenced across multiple files via hard-coded pointers and you have a recipe for trouble when moving files around in the rom.
repoint.tsv exists as a crude remedy. You can edit it in a plain text editor such as Notepad++, but you may have an easier time working in a tool that can parse TSV, such as LibreOffice Calc.
repoint.tsv should only be used to describe files stored in
misc for now.
Files described outside of
misc will be imported twice: once when parsing
repoint.tsv, and again when dealing with the file's primary reference.
Every row follows this format:
File High Low Point To DmpSz Notes
The meaning of each column:
File file to modify
High offset of the opcode containing the upper half of the pointer
Low offset of the opcode containing the lower half of the pointer
if FFFFFF, High is considered to be the whole pointer
Point To file of which the pointer will be written
if .end is appended, the pointer points to the end of the file
DmpSz the size of the file when it was dumped
you won't be editing this and it's used internally only
Notes store notes in this column
this is the only column that is optional
Filenames may not contain spaces.
Please keep less than 4096 entries in this document.
The initial dump creates a
repoint.tsv based on assumptions about a rom that hasn't undergone major modifications. You can get away with never modifying this file, but if you do any complex wizardry, you may find yourself changing its contents to better fit your needs.
Finally, here's an excerpt from it to drive the point home:
system/ovl_file_choose.zovl 00DCF0 00DD08 misc/title_static
system/ovl_file_choose.zovl 00DCF4 00DD04 misc/title_static.end
The pointer split between the opcodes
0xDD08 in the file
ovl_file_choose.zovl in the directory
system will be updated to reflect the new start offset of the file
title_static in the directory
Then, the same is done for the end offset of
As of revision 0, you could can do
misc/title_static.size for the size of the file.
This file contains information on how scenes are linked together. Because the terms exit and entrance tend to confuse, we've gone with route here to confuse people even more.
Here's a direct copy and paste from the file:
#Index Scene Spawn Music Title FadeIn FadeOut
0 0 0 STOP ON 2 2
1 0 0 STOP ON 2 2
2 0 0 STOP ON 2 2
3 0 0 STOP ON 2 2
Let's get this out of the way first: all numbers are decimal.
Also, you can add comments by writing the
# character. Anything after it on to the end of the line will be ignored when parsing.
Index: The "exit" number used on collision in maps to warp Link somewhere
Scene: The scene that this will warp Link to when he walks on it. It corresponds directly with the numbering of the scenes in the scene folder in a dumped rom.
Spawn: The spawn point within the scene specified for Link to spawn at.
Music: Will either be
STOP makes the music that's playing stop while warping so the next song can fade in.
GO is for if the music is to remain playing. A good example of
GO is when you enter the Sacred Forest Meadow from the Lost Woods. The music keeps playing.
OFF; determines if the title card displays or not when entering the scene specified.
FadeIn: The effect used when fading into the next scene when entering it.
FadeOut: The effect used when fading out when warping to the next scene.
Finally, a note on how these values are used and why there seem to be three additional copies for each route. Let's say you set the collision to warp Link through route 0. The game does the following:
Is it currently night?
Is Link an adult?
Route 0 takes you to the Deku Tree. If it's night, route 1 is used. If Link is an adult and it's day, route 2. If Link is an adult and it's night, route 3. This is why every route has so many variants: to account for these different situations. So if you edit or add any routes, it's a common pitfall to not also edit the "copies". They will always be in groups of four, unless you write an assembly hack that does otherwise.
Ignore this section for now. You can use dedicated texture tools to edit the raw data in the misc/skybox directory.
When the program is doing a filesystem dump, it references
repoint.tsv, which contains information on how to dump sky boxes. The encoded binaries for the sky boxes go in
misc/skybox/comp and their palettes go in
misc/skybox. This must remain consistent due to assumptions that are made during the build process. These are converted to PNG in the main
skybox directory in the project's root directory for easier editing.
When changes to these PNGs are detected, they get compiled back into the
misc/skybox directories. Let's make an example of a custom
majora.png skybox. It gets indexed and its palette gets saved as
misc/skybox. Then, the skybox itself gets saved as
misc/skybox/comp. The whole thing is forced to use one 256-color palette.
If you want to have a sky box that uses a massive palette (one sub-palette per sub-image), you'd name as
majora#1.png, etc. This pattern is detected and
majora files are generated accordingly.
If this is confusing, please reference the
skybox directory generated when dumping a rom. Compare the contents of
misc/skybox/comp, and it should explain itself a little better.
The audio files are stored in
misc and named
audiotable. To use custom music, you will need to rip the modified equivalents from your edited rom and overwrite the ones in the folder.
Say you found a tutorial that tells you something like this.
Go to 0xB9D1A8 in the rom and write the bytes FF 99 EE to make the Kokiri Tunic pink
You can't follow these instructions, as there is no rom. You're editing the contents of the filesystem directly. If you plan on following any instructions like this, you'll have to apply them before the initial dump. You can't apply these changes after a rebuild, because the build process rearranges nearly every file.
Alternatively, you can just edit the files directly. These instructions would translate to:
Go to 0x1091A8 in the file "code" and write the bytes FF 99 EE to make the Kokiri Tunic pink
Once you rebuild the rom, you have a pink Kokiri Tunic.
How was this done? Well, if you look at the offset
0xB9D1A8, you'll notice it's between the offsets
0xBCEF30, which is the block that the file named
code is stored in.
Now that we know what file the byte(s) are in, we need an offset relative to the start of that file. Open your calculator, switch to
Programmer/Scientific/whatever Mode, and then switch the notation to
Now we use this formula to get the offset relative to the start of the file.
rom offset - file start = relative offset
That would be
0xB9D1A8 - 0xA94000 = 0x1091A8
I'm sure these instructions are grating for anyone who already knows this stuff, but we have to teach the next generation somehow, don't we? 8)
Walter and Max are fighting over how to merge commits on
system/ovl_player_actor.zovl. With the addition of the
patch directory, they don't have to. Walter compiles his mod into a CloudPatch named
system#ovl_player_actor.zovl(walter).txt, and Max labels his likewise. The two patches are dropped into the
patch directory, and the file
system/ovl_player_actor.zovl gets both patches applied during the build process. The original file remains unmodified so a clean file is patched each time, and the two proceed to argue over the purity of pseudo instructions.
Let's break down this path name
Everything in parenthesis is regarded as a comment and discarded. The comment is here so we know whose is whose. Then every
character gets replaced with a
is dropped. The
is how the program detects it's a CloudPatch, and more patching features may be added in time.
We are left with the path to the file in question, relative to the root of the project folder.
Now here's an example of a simple patch to change the tunic color. We create a
patch directory in the root project folder, and place a
.txt within that we name as such:
As for the contents, we do:
0x1091A8 is the offset of the Kokiri Tunic color within the file
FF99EE are the bytes we're writing. Alternatively, you could do something like this to combine multiple edits at multiple offsets into one patch file:
This is a lot of trouble to change the tunic color, and you will most likely not even use this feature unless you're doing weird assembly stuff with big files and have multiple people placing their own code chunks at different offsets within the file.
As of revision 3, zzromtool will throw errors if there happens to be a patch that it can't find the matching file for.
This is a shoddy method of storing additional information in a scene file, like entrance cutscenes, which are not referenced anywhere else within a scene file and are therefore not reliably detectable. If you would like entrance cutscenes to be automatically detected in custom maps created in your software, you'll need a TEXT-DATA implementation. It's very simple.
At the end of the scene file, and at an offset that is 16-byte-aligned (offset ends in 0x0, not 0x8), there will be the bytes:
23 40 21 54 45 58 54 2D 44 41 54 41 21 40 23 30
or, as text
where the trailing
'0' character is for version tracking. It will likely never change, but it's there just in case.
Immediately after this 16-byte identifier is text data, all the way to the end of the file.
x is a hexadecimal offset to cutscene data within the scene
ram segment 02 is required
If you want to specify the offsets
0xFF00 as entrance cutscenes, for example, that data would be formatted within the TEXT-DATA section as such:
Finally, the TEXT-DATA section and everything after it are excluded when scenes are imported into the rom, so don't worry about any space going to waste.