Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
1fe0f8e
fix: Cleanup structure in INSTALL.md
Mityno Jun 19, 2026
1890bff
fix: Clean undefined link
Mityno Jun 19, 2026
1682e68
feat: Copypaste decompiling.md and start porting over
Mityno Jun 19, 2026
f0ed89f
feat: Keep porting decompiling.md and adding some details
Mityno Jun 22, 2026
99efe76
feat: Add images and fix links
Mityno Jun 22, 2026
46de45d
Apply suggestions from code review
Mityno Jun 22, 2026
1b26e1a
Apply suggestions from code review
Mityno Jun 22, 2026
a5a2494
feat: Update decomp.me export instructions, adapt review about thumb …
Mityno Jun 22, 2026
b549770
feat: Add instructions to reserve non-actor files
Mityno Jun 23, 2026
1d4a241
feat: Add small details for LSP and reminder for installation
Mityno Jun 24, 2026
a8de8f5
feat: Add instructions for coding style (format and naming)
Mityno Jun 24, 2026
8d3814a
fix: Expand on UnkSystem naming
Mityno Jun 24, 2026
5b7b3c7
feat: Add details for objdiff errors
Mityno Jun 26, 2026
3273dcc
feat: Add information about symbols and tips.md
Mityno Aug 2, 2026
64e0dac
feat: Add information about UnkAngleStruct
Mityno Aug 2, 2026
78e20ce
fix: Update the CI build command in tips.md with the actual one
Mityno Aug 2, 2026
69d676b
feat: Add some information about naming guidelines
Mityno Aug 3, 2026
a891122
feat: Add information about vtable symbols
Mityno Aug 3, 2026
fc4236d
feat: Small improvements
Mityno Aug 4, 2026
2feacff
feat: Update doc's links layout a bit
Mityno Aug 4, 2026
6471adb
feat: Some updates to install.md
Mityno Aug 4, 2026
a9183f5
Merge remote-tracking branch 'upstream/main' into port-docs
Mityno Aug 8, 2026
f49eafa
feat: Apply reviews
Mityno Aug 8, 2026
32b7819
`func_ovxxx_xxxxxxx` -> `func_ovxxx_02xxxxxx`
Yanis002 Aug 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
- [Project structure](#project-structure)
- [Decompiling](#decompiling)
- [Code style](#code-style)
- [Creating new `.c`/`.cpp` files](#creating-new-ccpp-files)
<!-- - [Creating new `.c`/`.cpp` files](#creating-new-ccpp-files) -->

## Project structure
- `build/`: Build output
Expand Down
4 changes: 3 additions & 1 deletion INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Contents:
- [Prerequisites](#prerequisites)
- [Build the ROM](#build-the-rom)
- [Matching the base ROM](#matching-the-base-rom)
- [Building with non-matching code](#building-with-non-matching-code)
<!-- - [Building with non-matching code](#building-with-non-matching-code) -->
- [[Optional] LSP setup](#lsp-setup)

## Prerequisites
Expand Down Expand Up @@ -40,6 +40,8 @@ Now you can run `ninja` to build a ROM for the chosen version.
> [!NOTE]
> For Linux users: Wibo is used by default. If you want to use Wine instead, run `configure.py` with `-w <path/to/wine>`.

## Build the ROM

### Matching the base ROM

**This is optional!** You only need to follow these steps if you want a matching ROM.
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ The Legend of Zelda: Spirit Tracks
[Discord Badge]: https://img.shields.io/discord/688807550715560050?color=%237289DA&logo=discord&logoColor=%23FFFFFF
[discord]: https://discord.com/invite/DqwyCBYKqf/

**Work in progress!** This project aims to recreate source code for ***The Legend of Zelda: Spirit Tracks*** by decompiling its code by hand. **The repository does not contain assets or assembly code.** To build the ROM, you must own an existing
copy of the game to extract assets from.
**Work in progress!** This project aims to recreate source code for ***The Legend of Zelda: Spirit Tracks*** by decompiling its code by hand. **The repository does not contain assets or assembly code.** To build the ROM, you must own an existing copy of the game to extract assets from.

> [!NOTE]
> The project targets the European and Japanese versions, and other versions might be supported later.
Expand Down
104 changes: 104 additions & 0 deletions docs/decompiling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Decompiling
This document describes how you can start decompiling code and contribute to the project. Feel free to ask for help if you get
stuck or need assistance.
- [Pick a source file](#pick-a-source-file)
- [Decompiling a source file](#decompiling-a-source-file)
- [Decompiling a function](#decompiling-a-function)
- [Decompiling `.init` functions](#decompiling-init-functions)
- [The Ghidra project](#the-ghidra-project)

## Pick a source file
Comment thread
Mityno marked this conversation as resolved.
A reservation sheet exists for a list of delinked source files that are ready to be decompiled. This list grows as more source files are delinked from the rest of the base ROM. You can request access to the sheet in the ZeldaRet discord [channels for ST](https://discord.com/channels/688807550715560050/1453177153502969977) (you can join the server with [this invite link](https://discord.gg/6tjntnU8hC)).
Comment thread
Mityno marked this conversation as resolved.
Outdated

You can claim a source file (called an "actor") by filling in the "Reserved by" column. Once you started decompilation, create a PR on the ST repository for the actor you're decompiling. The decomp-dev bot will follow your PR and give information about the decompilation progress of your code.

<!-- TODO -->
If you want to unclaim the file, <!-- leave another comment so we can be certain that the source file is available to be claimed again. -->
Remember to make a pull request of any progress you made on the source file, whether it is just header files or partially decompiled code.
Comment thread
Mityno marked this conversation as resolved.
Outdated

## Decompiling a source file
We use the object diffing tool [`objdiff`](https://github.com/encounter/objdiff) to track differences between our decompiled C++ code and the base ROM's code.
1. [Download the latest release.](https://github.com/encounter/objdiff/releases/latest)
1. Run `configure.py <eur|usa>` and `ninja` to generate `objdiff.json` in the repository root (don't forget to follow the instructions in [INSTALL.md](../INSTALL.md) first).
Comment thread
Mityno marked this conversation as resolved.
Outdated
1. In `objdiff`, set the project directory to the repository root (it should load `objdiff.json` itself).
1. [WSL only] If you're using WSL (which is possible), navigate to the project directory with window's directory picker tool and select
1. Select your source file in the left sidebar:
Comment thread
Mityno marked this conversation as resolved.
![List of objects in objdiff](https://github.com/zeldaret/ph/blob/main/docs/images/objdiff_objects.png)
Comment thread
Mityno marked this conversation as resolved.
Outdated
1. See the list of functions and data to decompile:
![List of symbols in objdiff](images/objdiff_symbols.png)

> [!NOTE]
> If a source file is missing in `objdiff`, or `objdiff` fails to build a file, first rerun `ninja` to update `objdiff.json`.
> If the problem persists, feel free to ask for help.

## Decompiling a function
Once you've opened a source file in `objdiff`, you can choose to decompile the functions in any order. We recommend starting
with a small function if you're unfamiliar with decompilation. Here's an example:

![Function in objdiff](images/objdiff_function.png)

As a starting point, we look at the decompiler output in Ghidra. You can request access to our shared Ghidra project [in this section](#the-ghidra-project).

![Decompiler in Ghidra](images/ghidra_decomp.png)

Looking at this output, we might try writing something like this:
```cpp
ARM bool Actor::Drop(Vec3p *vel) {
Comment thread
Mityno marked this conversation as resolved.
Outdated
if (mGrabbed) {
mVel = *vel;
mGrabbed = false;
return true;
}
return false;
}
```

Now we can go back to `objdiff` and look at the result:

![Matching function in objdiff](images/objdiff_match.png)

Success! Note that this was a simple example and that you'll sometimes get stuck on a function. In that case, try the
following:
- Decompile a different function and come back later.
- Export to [decomp.me](https://decomp.me/):
1. Press the `decomp.me` button in `objdiff`.
1. Once you're sent to `decomp.me`, go to "Options" and change the preset to "Phantom Hourglass".
1. Paste your code into the "Source code" tab.
1. Share the link with us!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
1. Share the link with us!
1. Share the link with us!
Note: if the function is using THUMB mode you can use `THUMB_BEGIN` and `THUMB_END` before and after the function to create a THUMB region, anything outside of the region will use ARM.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yeah also, as I learned yesterday, if you have inlines in a header and #include the header outside of the region it will use ARM, but if you do include it inside the thumb region it will use thumb

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yeah also, as I learned yesterday, if you have inlines in a header and #include the header outside of the region it will use ARM, but if you do include it inside the thumb region it will use thumb

I tried to include that in the description if that's what you wanted, let me know how it works for you


## Decompiling `.init` functions
> [!NOTE]
> This section will be updated as we learn more about global objects. Feel free to contribute or provide us with more
> information!

Functions in the `.init` section are static initializers. Their purpose is to call C++ constructors on global objects, and to
register destructors so the global objects can be destroyed when their overlay unloads.

Static initializers are generated implicitly and do not require us to write any code ourselves. So, to generate one, you must
define a global variable by using a constructor.

If the static initializer calls `__register_global_object`, that means the global object has a destructor. This means you'll
have to declare a destructor if it doesn't exist already.

Another consequence of having a destructor is that a `DestructorChain` object will be added to the `.bss` section. This struct
is 12 (`0xc`) bytes long and is also implicit, so we don't need to define it ourselves.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to this I'd like to add that if you have multiple ctors in the same file they will all end up in the same static initializer function, meaning the order of the declarations will change the order of the code from the sinit function (also something else to know is that you only have one sinit per source file)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unsure how to formulate that since I don't fully understand what is going on, I'll leave that small comment to you if you have time

> [!IMPORTANT]
> An important thing to keep in mind is that a static initializer can construct multiple global objects.

## Decompiling data
> [!NOTE]
> Under construction! It's not fully clear how data is decompiled, as the compiler is strict on how it orders global variables.
> Feel free to contribute to this section or provide us with more information!

Other than `.text` and `.init` which contain code, there are the following sections for data:
- `.rodata`: Global or static constants
- `.data`: Global or static variables
- `.bss`/`.sbss`: Global or static uninitialized variables
Comment thread
Mityno marked this conversation as resolved.
Outdated

You can see examples of these data sections in the [compilation section in `build_system.md`](/docs/build_system.md#compiling-code).

## The Ghidra project
We use a shared Ghidra project to analyze the game and decompile functions. To gain access to the project, install
[Ghidra version 11.2.1](https://github.com/NationalSecurityAgency/ghidra/releases/tag/Ghidra_11.2.1_build) and request access
from @aetias on Discord.
Loading