diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b30c4da3..3e2f2f96 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,14 @@ - [Project structure](#project-structure) - [Decompiling](#decompiling) - [Code style](#code-style) -- [Creating new `.c`/`.cpp` files](#creating-new-ccpp-files) + - [Naming new things](#naming-new-things) + - [Creating a class](#creating-a-class) + + +## Decompiling +[/docs/decompiling.md](/docs/decompiling.md) has most information as to how to approach decompilation and how to get started. + +Reading the rest of this file gives information on the structure of the project and coding style, it is highly recommended to read it. ## Project structure - `build/`: Build output @@ -25,11 +32,71 @@ - `mangle.py`: Shows mangled symbol names in a given C/C++ file - `requirements.txt`: Python libraries - `setup.py`: Sets up the project + - `vtable_sym.py`: Renames and relocates vtable symbols - `*.sha1`: SHA-1 digests of different versions of the game -## Decompiling -See [/docs/decompiling.md](/docs/decompiling.md). - ## Code style This project has a `.clang-format` file and all C/C++ files in this project should follow it. We recommend using an editor compatible with `clang-format` to format the code as you save. + +As a rule of thumb, try to mimick the style that can be observed in already decompiled files. \ +Please write hexadecimal numbers in upper case (`0x9ABCDEF` instead of `0x9abcdef`). Lowercase numbers are used for global names (old functions names like `func_ovxxx_02xxxxxx`, data, etc). +Class members use uppercase numbers too (e.g., `mUnk_0C` for a member placed at position `0xC`). +New function names should follow `PascalCase`, even if they include numbers. + +### Naming new things + +You may have to create new classes, structs, member attributes or functions, etc. Here is described how to name them according to what they are. + +Once you find out what something does, it helps to give it a meaningfull name (eg. `ModelRender` class, `Actor::IsAlive()` function or `Actor.mPrevPos` member attribute). + +If you don't know yet what a piece of code does, try to follow this rough format: `{type}_ov{num}_{address}`. +- `type` is the kind of code you're naming, `UnkStruct` for a struct, `mUnk` for a member attribute, `Unk{D}System{X}` for a class or group of functions. In the last case, `X` would then be an arbitrary, unique identifier. Likely a number that would increase for every new `System` to name. `D` is optional and aimed to give more information about the context in which the system is used (eg. `File` or `Actor`). +- `num` is the id of the overlay the code is part of. +- `address` is the address of the data you're naming. This may not always be applicable, in which case you can ignore it (and remove the trailing `_` of the format given above). + +You can also name thing based on where they are used. Say that some class `ActorP` has a member `mUnk_{X}` that needs it class, you can call the class `UnkStruct_ActorP_{X}`. Same goes for functions (but try to rather name things with their vtable address when applicable, so that they are easier to find and merge later on). + +### Creating a class +If you are to create a new class, try to follow this structure: +```cpp +class Foo { +public: + /* 00 */ int mBar; + /* 04 */ + + Foo(); + + /* 00 */ virtual void vfunc_00(); + /* 04 */ virtual void vfunc_04(); + /* 08 */ virtual ~Foo(); + /* 0C */ + + // itcm + bool func_01fff1e0(); + + // overlay 0 + void func_ov000_0208a318(unk32 param1, unk32 param2, unk32 param3); + void func_ov000_0208bbd4(unk32 param1, VecFx32 *param2, u16 param3); + + static UnkStruct_027e0ce0_34 *func_ov000_0205c904(); + + // overlay 1 + void func_ov001_020bc5f8(); + void func_ov001_020bc524(bool param1); + + static Foo *Create(); + static void Destroy(); + + // overlay 17 + void func_ov017_020bd69c(); +}; +``` + +In order, the parts are: +- Member attributes. +- Constructor (ctor). +- Virtual functions (the placement of the destructor (dtor), if there is any, can vary). +- Other methods, grouped by overlay with a comment indicating which one. + +Using `private` may sometime be required to enable some inlining, so feel free to when use it you feel like you should. diff --git a/INSTALL.md b/INSTALL.md index 615f117b..5fd6a772 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -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) + - [[Optional] LSP setup](#lsp-setup) ## Prerequisites @@ -13,33 +13,36 @@ Contents: 1. Use one of these platforms: - Windows (recommended) - Linux -2. Install the following: +1. Install the following: - Python 3.11+ and pip - GCC 9+ - Ninja -3. Install the Python dependencies: -```shell -python -m pip install -r tools/requirements.txt -``` -4. Install pre-commit hooks: -```shell -pre-commit install -``` -5. Run the Ninja configure script: -```shell -python tools/configure.py -``` -By default this will configure for any version that has a baserom in the `extract` folder (see below), to configure for one specific version: -```shell -python tools/configure.py [--version | -v] -``` -6. Put one or more base ROMs in the [`/extract/`](/extract/README.md) directory of this repository. +1. Clone the Github repository on your machine and go to the root of the project. +1. Install the Python dependencies: + ```shell + python -m pip install -r tools/requirements.txt + ``` +1. Install pre-commit hooks: + ```shell + pre-commit install + ``` +1. Run the Ninja configure script: + ```shell + python tools/configure.py + ``` + By default this will configure for any version that has a baserom in the `extract` folder (see below), to configure for one specific version: + ```shell + python tools/configure.py [--version | -v] + ``` +1. Put one or more base ROMs in the [`/extract/`](/extract/README.md) directory of this repository. See the [README.md](/extract/README.md) of that directory for file naming instructions. 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 `. +## Build the ROM + ### Matching the base ROM **This is optional!** You only need to follow these steps if you want a matching ROM. @@ -54,17 +57,21 @@ ARM7 BIOS in the root directory of this repository, and verify that your dumped ## LSP setup -By default `configure.py` will create the config file for clangd (at the root folder of the project), if you want to use the alternative setup you can run `configure.py` with `--noclangd | -c` to disable the creation of the file. - -If you wish to use CMake: - **This is likely not necessary.** Most C++ editors usually have their one LSP (Language Server Protocol, a tool for code completion and more) configuration that should recognize the project structure and work out of the box. This section is about how to setup your LSP yourself **if the need be**. +### Already included +The repository now comes with a `.clangd` at the root. \ +By default `configure.py` will create `compile_commands.json` (at the root folder of the project), if you want to use the alternative setup you can run `configure.py` with `--noclangd | -c` to disable the creation of the file. \ +With both these files at the root of the project, any usual LSP should be able to detect them and work out of the box. + +### If you wish to use CMake The repository contains a [`CMakeLists.txt`](CMakeLists.txt) that allows generating a compilation database. For now, the `CMakeLists.txt` can only be used to generate `compile_commands.json` and similar files, not compiling the project. To generate the compilation database, run `cmake -S . -G "Unix Makefiles" -B cmake` from the root directory of the project. This will create a `cmake/` directory that contains the `compile_commands.json`. -Once the file is generated, you can dynamically link it to the root directory and let your LSP detect it (make sure not to `git add` it though), or edit your `.clangd` as follows for it to recognize the compilation database: +Once the file is generated, you can dynamically link it to the root directory and let your LSP detect it (make sure not to `git add` it though, even though the project's `.gitignore` should prevent it), or edit your `.clangd` as follows for it to recognize the compilation database: ```clangd CompileFlags: - CompilationDatabase: "cmake" + CompilationDatabase: "cmake" # path to the compilation database ``` +Since the project already includes a `.clangd`, make sure not to stage and commit any of those local changes to the repository. + This setup is adapted from a [tutorial by Strus](https://gist.github.com/Strus/042a92a00070a943053006bf46912ae9), refer to his post for further details. diff --git a/README.md b/README.md index c1b9e410..fc413656 100644 --- a/README.md +++ b/README.md @@ -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. @@ -19,7 +18,7 @@ copy of the game to extract assets from. See [INSTALL.md](INSTALL.md) for instructions on how to install the project. ## Contribution -A work in progress, but [CONTRIBUTING.md](CONTRIBUTING.md) has guidelines for how to contribute to the project. +A work in progress, but [CONTRIBUTING.md](CONTRIBUTING.md) has guidelines for how to get started and contribute to the project. Make sure to follow instructions on [installation](#how-to-install) first. ## Documentation -See [/docs](/docs) for documentation about the game. +See [/docs](/docs) for documentation about the game and other useful information about the project. diff --git a/docs/decompiling.md b/docs/decompiling.md new file mode 100644 index 00000000..3e36f6a9 --- /dev/null +++ b/docs/decompiling.md @@ -0,0 +1,171 @@ +# 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) +- [About symbols](#about-symbols) +- [The Ghidra project](#the-ghidra-project) + +[tips.md](tips.md) has extra information that is not required to get started, but may help you get familiar with the project's code. Check it out when needed! + +## Pick a source file +For actors and map objects, 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)). + +You can claim a source file (called an "actor"[^1]) by changing its state to "reserved" 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. + +[^1]: That is, for most usual files, a non-actor code file can be a Map Object for example. + +If you want to unclaim the file, leave a comment your the PR and mark the actor as "available" on the reservation sheet 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, and mark it as ready so that it can be registered by other members. + +> [!NOTE] +> If you want to decompile a non-actor file, instead of filling an entry in the spreadsheet, open [an issue](https://github.com/zeldaret/st/issues) for it on top of the PR and mark it with appropriate labels ("decomp", "reserved", etc). You can find a detailed list of the labels [on github](https://github.com/zeldaret/st/labels). For more details visit the discord channel for ST. + +## 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. [`ghidra`](https://github.com/NationalSecurityAgency/ghidra) is a popular software for decompiling and is the one used in this project. You can use any tool you're familiar with, but these are the ones used by other members and the ones we'll be able to provide help with. + +1. Download the latest releases: [`objdiff`](https://github.com/encounter/objdiff/releases/latest), [`ghidra`](https://github.com/NationalSecurityAgency/ghidra/releases/latest) (only `objdiff` is needed in this section). +1. Run `tools/configure.py [--version|-v ]` and `ninja` to generate `objdiff.json` in the repository root (don't forget to follow the instructions in [INSTALL.md](../INSTALL.md) first). Note: if `--version` isn't passed the project will be configured to use all supported versions (meaning all versions will be showed on objdiff). +1. Open `objdiff`. There, set the project directory to the repository root (it should load `objdiff.json` itself, if present in the directory, it should have been generated at the previous step). + - [WSL only] If you're using WSL (which is possible to do, although a few things may not work perfectly), navigate to the project directory with window's directory picker tool and select it. Do not type the path manually unless you know what you're doing, `objdiff` may use different path format over time. +1. Select your source file in the left sidebar: +An example can be `src/000_Second/Actor/Actor`[^objdiff_src_path] +![List of objects in objdiff](images/objdiff_objects.png) +1. See the list of functions and data to decompile: +![List of symbols in objdiff](images/objdiff_symbols.png) + +[^objdiff_src_path]: This path was valid when this doc was written, the project structure may have changed since. + +The following sections explain how to decompile the different parts you see in `objdiff`. + +> [!NOTE] +> If a source file is missing in `objdiff`, or `objdiff` fails to build a file, first rerun `ninja` to update `objdiff.json` (you can run `ninja objdiff` to only re-generate the `objdiff.json` file). +> You can see more details on an `objdiff` error by looking for a context window called "Jobs" at the top of the window (hoverring on the red text should show a full description of the run command and the error). +> 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` (search for the function name there first, if you don't find it [see common tips](tips.md#finding-a-function-in-ghidra)). You can request access to our shared `ghidra` project [in this section](#the-ghidra-project), it will provide you with prepared files for decompilation and avoid having to setup ghidra yourself. \ +We get this code from ghidra (in the rightmost window): + +![Decompiler in Ghidra](images/ghidra_decomp.png) + +Looking at this output, we might try writing something like this: +```cpp +bool Actor::Drop(Vec3p *vel) { + if (mGrabbed) { + mVel = *vel; // Vec3p struct copy + 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. Paste your code into the "Source code" tab. The whole file may be needed to access defined globals. + 1. On `decomp.me`, switch to the `objdiff` tab[^2], you can check that you see what was expected from your local diff. + 1. Share the link with us! (Reminder [link to the ZeldaRET discord server](https://discord.gg/6tjntnU8hC).) + +[^2]: You can set this as the default in `decomp.me` settings. + +> [!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. +> If you have inlines in a header and `#include` the header outside of the region it will use ARM. But if you include it inside the thumb region it will use thumb. + +## 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. + +> [!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 (requires `const`) +- `.data`: Global or static variables (requires not using `const` except if it's used in a static initializer, in which case all of the data will be set to zero) +- `.bss`: Global or static uninitialized variables + +You can see examples of these data sections in the [compilation section in `build_system.md`](/docs/build_system.md#compiling-code). + +## About symbols + +### Updating a symbol + +When decompiling function calls or data accesses, their may be discrepancies between what names (more precisely, "symbols") the project currently knows and what is expected to match the original binary. In `objdiff`, such situation can be seen as such: + +![When a symbol has a default value](images/symbol_default.png) +or +![When a symbol is outdated](images/symbol_outdated.png) + +In the first case, the base symbol (on the left) has a default name that was defined when delinking the file. +In the second case, the base symbol has already been renamed before, but the name must change again since more context is known (this is less frequent but still happens). \ +In both cases, the base symbol can be renamed to the new one by editing the appropriate `symbols.txt` file. There is one of these files per overlay, in this case it's overlay `000` as we can see in the function's name. You can find them under `config//arm9/overlays/ov/symbols.txt`. \ +There, search for the symbol, either with its full name (might not always work), with its address [^3] or its mangled name. + +[^3]: It is visible for symbols that do not yet have a meaningful name, in the examples above `02081ecc` is the address of the function. + +You can get a symbol's mangled name in `objdiff` by right clicking it's name. In this example, the second line (starting with `_ZN18`) corresponds to the mangled name of the symbol. + +![`objdiff` symbol right click](images/objdiff_symbol_right_click.png) + +Once you found the symbol to be renamed, copy the new mangled name (from `objdiff`, as explained just above) and replace the current symbol in `symbols.txt` by the new one. The symbol is only the first word of the line in `symbols.txt`, leave the rest of the line unchanged. + +After you updated the symbols, you can run `ninja objdiff` to re-generate `objdiff.json` and update `objdiff`'s config. After a short time (on WSL, it can take a while), `objdiff` should update its output and the names should line up as such: + +![Symbols match in `objdiff`](images/objdiff_symbol_match.png) + +### Already existing symbols + +In some other cases, the symbol shown in `objdiff` can have more information than your compiled code: + +![When a symbol already exists](images/symbol_exists.png) + +This usually means that a function/data already exists but hasn't been used in the code. Search for the symbol in source files of the project to see if you missed a struct, class or data definition. \ +If you don't find a definition matching the symbol shown in `objdiff`, it may have been wrongly updated before or left after other renames. You can consider renaming it if you're sure that the existing symbol is wrong. In any case, feel free to ask for help on the discord channel to help clear out the situation (see link earlier in this file). + +### Already existing symbols with the same visual name + +This happens most often with ctors/dtors, because there exist mutiple of them for one class that are only differenced by their mangled name and not their regular name: + +![Symbols that have the exact same visual name](images/symbol_have_same_name.png) + +In such case, check the symbols mangled names by right-clicking them and determine if a symbol update is needed or if you are using the wrong one. + +> [!NOTE] +> Fully matching symbols should never appear in any colorful way in `objdiff`. Color are always used to indicate differences, even if the names match. This section is an example that the difference may not appear at the first glance, but that there is one nevertheless. + +## The Ghidra project +We use a shared Ghidra project to analyze the game and decompile functions (for both EUR and JP versions). 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. \ No newline at end of file diff --git a/docs/images/ghidra_decomp.png b/docs/images/ghidra_decomp.png new file mode 100644 index 00000000..07e54d73 Binary files /dev/null and b/docs/images/ghidra_decomp.png differ diff --git a/docs/images/ghidra_left_column.png b/docs/images/ghidra_left_column.png new file mode 100644 index 00000000..3dc205a4 Binary files /dev/null and b/docs/images/ghidra_left_column.png differ diff --git a/docs/images/objdiff_function.png b/docs/images/objdiff_function.png new file mode 100644 index 00000000..26247156 Binary files /dev/null and b/docs/images/objdiff_function.png differ diff --git a/docs/images/objdiff_match.png b/docs/images/objdiff_match.png new file mode 100644 index 00000000..06dec2ff Binary files /dev/null and b/docs/images/objdiff_match.png differ diff --git a/docs/images/objdiff_objects.png b/docs/images/objdiff_objects.png new file mode 100644 index 00000000..0bde5aaa Binary files /dev/null and b/docs/images/objdiff_objects.png differ diff --git a/docs/images/objdiff_symbol_match.png b/docs/images/objdiff_symbol_match.png new file mode 100644 index 00000000..b92107c8 Binary files /dev/null and b/docs/images/objdiff_symbol_match.png differ diff --git a/docs/images/objdiff_symbol_right_click.png b/docs/images/objdiff_symbol_right_click.png new file mode 100644 index 00000000..fa5e2708 Binary files /dev/null and b/docs/images/objdiff_symbol_right_click.png differ diff --git a/docs/images/objdiff_symbols.png b/docs/images/objdiff_symbols.png new file mode 100644 index 00000000..d1af868b Binary files /dev/null and b/docs/images/objdiff_symbols.png differ diff --git a/docs/images/symbol_default.png b/docs/images/symbol_default.png new file mode 100644 index 00000000..1e18e28d Binary files /dev/null and b/docs/images/symbol_default.png differ diff --git a/docs/images/symbol_exists.png b/docs/images/symbol_exists.png new file mode 100644 index 00000000..1b421c5f Binary files /dev/null and b/docs/images/symbol_exists.png differ diff --git a/docs/images/symbol_have_same_name.png b/docs/images/symbol_have_same_name.png new file mode 100644 index 00000000..40f2b602 Binary files /dev/null and b/docs/images/symbol_have_same_name.png differ diff --git a/docs/images/symbol_outdated.png b/docs/images/symbol_outdated.png new file mode 100644 index 00000000..f6c85f2d Binary files /dev/null and b/docs/images/symbol_outdated.png differ diff --git a/docs/images/symbol_vtable_rename.png b/docs/images/symbol_vtable_rename.png new file mode 100644 index 00000000..9e44746f Binary files /dev/null and b/docs/images/symbol_vtable_rename.png differ diff --git a/docs/images/symbol_vtable_rename_completed.png b/docs/images/symbol_vtable_rename_completed.png new file mode 100644 index 00000000..ed593003 Binary files /dev/null and b/docs/images/symbol_vtable_rename_completed.png differ diff --git a/docs/tips.md b/docs/tips.md new file mode 100644 index 00000000..58b41444 --- /dev/null +++ b/docs/tips.md @@ -0,0 +1,139 @@ +# Tips + +Here we gather useful tips that may help to get started with and solve common problems in case of doubt. \ +Some miscellaneous information are also reported here, for example about the build rules or the github workflow. + +- [Maths](#maths) + - [General](#general) + - [Fx32](#fx32) + - [Angles](#Angles) + - [Random operations](#random) +- [Symbols](#symbols) + - [Updating a vtable symbol](#updating-a-vtable-symbol) +- [Ghidra](#ghidra) + - [Finding a function in Ghidra](#finding-a-function-in-ghidra) +- [Ninja](#ninja) + - [Build targets](#build-targets) +- [Github](#github) + - [CI/CD](#cicd) + +# Maths + +Most of the following information is about types and macros defined in `libs/nitro/include/nitro/math.h`. \ +They are important information to efficiently use built in types of the project, and taking a look at that file may be interesting. + +## General + +There is a `ABS(x)` macro that computes the absolute value of the given value. + +## Fx32 + +`fx32` (and smaller `fx` types) represent **F**ixed **P**oint floats used in the source code. They appear very often, especially with the `VexFx32` struct (3 `fx32`s components that represent a position or other kind of vectors). + +Some macros exists to operate on them, two major ones are `FLOAT_TO_FX32(n)` (takes a C `float` and transforms it into a `fx32`) and `MUL_FX32(a, b)` (takes two `fx32` and performs multiplication). + +## Angles + +There is a `DEG_TO_ANG(n)` macro that converts an angle from degrees to an internal hexadecimal representation. + +There are two macros `SIN(n)` and `COS(n)` that compute the expected trigonometric values by doing a table lookup[^sincos]. Note that both macros access the same table, but the kind of operation performed can usually be determined by looking at the lookup pattern: +- for `SIN`, `gSinCosTable` is accessed at roughly `2 * n`; +- for `COS`, `gSinCosTable` is accessed at roughly `2 * n + 1`. The `+1` allows for the differentiation. + +[^sincos]: You can usually spot these operations by seing a lookup to the table in ghidra. + +`Actor.mAngle` sometime is unexpectedly saved onto the stack when used in function calls. It's default type is `fx16`, but this type usually doesn't lead to the stack save. The member's type is actually an union, also including `mAngleStruct` of type `UnkAngleStruct`, using this type usually solves the stack save pattern. + + +## Random + +Random operations are handled by the `gRandom` class. The most common operation is `gRandom.Next32(u32 factor)`, with `factor=0` being a very common value. \ +Such calls can be tricky to find because they are usually inlined, but if you see lots of computations involving `gRandom` and it's members, chances are that it's a `Next32` computation (the argument may vary though, but starting by setting `0` may help to spot the actual `factor` used). + +# Symbols + +An introduction about symbols is already given in [decompiling.md](decompiling.md#about-symbols), more specific information are available here. + +## Updating a vtable symbol + +When creating a symbol for a class vtable, you may encounter the following situation: + +![Vtable symbol mismatch](images/symbol_vtable_rename.png) + +We can notice that a `+0x8` is missing on the left. A realignment (and possibly a rename) of the symbol is needed to fully match this pattern. This can be done using [`tools/vtable_sym.py`](../tools/vtable_sym.py) (run with `-h` to get a detailed explanation of the usage). \ +It is used to rename and place a vtable symbol, simply call `tools/vtable_sym.py old_name new_name` with the mangled namesto do both these things. In the example, the call would be `tools/vtable_sym.py data_ov063_02163174 _ZTV19ActorProfileUnkCASE`. \ +The tool will explicitly give all changes applied, which should include the name change and multiple `add: 0x{X}` (`X` can vary, in the case above it's `8`). Make sure to check them as sometime address matches may not target the same symbol accross overlays. + +Once the tool has been applied, update your symbols and you should see that the vtable now matches: + +![Vtable symbom match](images/symbol_vtable_rename_completed.png) + +> [!NOTE] +> By default, the tool works for the EUR version. You can apply the same changes to the JP versions by adding the arguments `[-v | --version] jp` when running the tool. + +> [!IMPORTANT] +> Be mindful, [`tools/vtable_sym.py`](../tools/vtable_sym.py) should ONLY be used when a realignment is needed. In other cases (renames), simply edit the `symbols.txt` file manually (see [decompiling.md](decompiling.md#about-symbols)). + +# Ghidra + +## Finding a function in Ghidra + +First of all, search for the current name of the function in Ghidra's left column, in the "Symbol Tree" section: + + + +If putting the function's name there doesn't show your function, it likely has a different name in Ghidra. \ +Search for your function's name but in the `symbols.txt` files instead, this should lead you to it's symbol definition. \ +From there, you can get the address of the symbol. Use it to search Ghidra instead, if you find a `func_ov_
` that matches your address and overlay, then it most likely is your searched function and will be renamed in the Ghidra file at a later update. + +> [!NOTE] +> This is also valid for data symbols, if you wish to see the actual data there. \ +> You can also find the function or data source with Ghidra by double-clicking on the function or data name (it may take multiple steps to get to the actual source). + +# Ninja + +## Build targets + +The default `ninja` commands run many checks and ensures that compilation gives the same output as the original file. +During development, you may want to run checks with re-compiling the whole project. The following section give details about targets that may help you with that. + +- [`objdiff`](#objdiff) +- [`report_`](#report_version) +- [`rom_`](#rom_version) +- [`check_`](#check_version) +- [`sha1_`](#sha1_version) +- [Github CI/CD's rules](#github-cicds-rules) + +### `objdiff` + +`ninja objdiff` re-generated `objdiff.json` and will warn you about illegal name access, a wrong renaming or broken addresses. + +### `report_` + +`ninja report_eur` (or `report_jp`) will run part of the compilation process and reveal compilation errors. + +### `rom_` + +`ninja rom_eur` builds the rom for the given version. (Takes some time.) + +### `check_` + +`ninja check_eur` runs various checks about the rom linking and overlays configurations (symbols locations, etc). + +### `sha1_` + +`ninja sha1_eur` builds the rom and checks that its sha1 sum matches the original rom's. (Takes some time.) + +### Github CI/CD's rules + +For each supported version (as of now, EUR and JP), the CI/CD runs the following command: `ninja arm9_ report_ check_`. \ +You may run that command locally before pushing to your branch to see if CI/CD should pass or not. (Note that differences in the command result may still be observed because of non-committed changes or changes to the local configuration.) + +# Github + +## CI/CD + +The rules run for each versions are detailed [in another section](#github-cicds-rules). + +The style checks performed can be replicated by running `pre-commit run`. \ +Note that this command only checks **current changes**. Changes from previous commits may not be checked by this command. To ensure that it runs on the entire project, you can add `-a` (or `--all-files`) at the end of the command. \ No newline at end of file