mirror of
https://github.com/izzy2lost/2ship2harkinian-Android.git
synced 2026-06-19 01:20:08 -07:00
Remove decomp docs (#438)
This commit is contained in:
@@ -1,47 +0,0 @@
|
||||
# Building using Docker
|
||||
|
||||
## Dependencies
|
||||
|
||||
You will need [Docker](https://docs.docker.com/get-docker/) Follow the instructions for your operating system.
|
||||
|
||||
## 1. Clone the Repository
|
||||
|
||||
You will need to prepare a local version of the project with a copied base ROM (see steps [2](../README.md#2-clone-the-repository) and [4](../README.md#4-prepare-a-base-rom) of the Linux instructions).
|
||||
|
||||
## 2. Create the Docker image
|
||||
|
||||
From inside your local project, run the following command:
|
||||
|
||||
```bash
|
||||
docker build . -t mm
|
||||
```
|
||||
|
||||
This will build a docker image with the name of `mm`, to verify the image was made you can run the command
|
||||
|
||||
```bash
|
||||
docker image ls
|
||||
```
|
||||
|
||||
and look for `mm` under the "REPOSITORY" column.
|
||||
|
||||
## 3. Start the container
|
||||
|
||||
To start the container, you can mount your local filesystem into the Docker container and run an interactive bash session.
|
||||
|
||||
```bash
|
||||
docker run -it --rm --mount type=bind,source="$(pwd)",destination=/mm mm /bin/bash
|
||||
```
|
||||
|
||||
- The `-it` flags Keep STDIN open even if not attached to the container and allocates a pseudo-tty terminal.
|
||||
- The `--rm` flags causes Docker to automatically remove the container when it exits.
|
||||
- The `--mount` flag allows you to mount volumes and host-directories in a container. By specifying the type to be a bind, we are saying there are files to be mounted from the host machine (the one that runs your docker daemon) onto the container. Source is specifying where we are mounting the files from in this case, we want the `mm` folder created earlier. The $(pwd) sub-command expands to the current working directory. The destination takes as its value the path where the file or directory is mounted in the container. This makes it so that the image should rarely change and will reduce the need to rebuild with any change to the codebase.
|
||||
|
||||
## 4. Setup and Build the ROM
|
||||
|
||||
Once inside the container, you will need to set the repository as a trusted repository with the following command:
|
||||
|
||||
```bash
|
||||
git config --global --add safe.directory /mm
|
||||
```
|
||||
|
||||
After that continue with step [5. Make and Build the ROM](../README.md#5-make-and-build-the-rom) of the Linux instructions to setup and build the ROM, or run any other command you need.
|
||||
@@ -1,92 +0,0 @@
|
||||
# Building on macOS
|
||||
|
||||
**N.B. C++17 is required to build the asset processing program that we use (ZAPD), so check your OS version can support this before proceeding**
|
||||
|
||||
## Dependencies
|
||||
|
||||
For macOS, use Homebrew to install the following dependencies:
|
||||
|
||||
* coreutils
|
||||
* make
|
||||
* python3
|
||||
* libpng
|
||||
* bash
|
||||
|
||||
You can install them with the following commands:
|
||||
|
||||
```bash
|
||||
brew update
|
||||
brew install coreutils make python3 libpng bash
|
||||
```
|
||||
|
||||
(The repository expects Homebrew-installed programs to be either linked correctly in `$PATH` etc. or in their default locations.)
|
||||
|
||||
Homebrew does not have clang-format-14 but MacPorts does
|
||||
|
||||
(Have been unable to confirm actual commands with somone that has a Mac)
|
||||
|
||||
## Building mips-linux-binutils
|
||||
|
||||
The following instructions are written for MacOS users but should apply to any Unix-like system, with maybe some modifications at the end regarding the bash_profile.
|
||||
|
||||
Create destination dir for binutils
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /opt/cross
|
||||
```
|
||||
|
||||
Create and enter local working dir
|
||||
|
||||
```bash
|
||||
mkdir ~/binutils-tmp
|
||||
cd ~/binutils-tmp
|
||||
```
|
||||
|
||||
Get and extract binutils source
|
||||
|
||||
```bash
|
||||
wget https://ftp.gnu.org/gnu/binutils/binutils-2.35.tar.bz2
|
||||
tar xjf binutils-2.35.tar.bz2
|
||||
```
|
||||
|
||||
(You may find this command does not work: if so, just access the URL in a browser and save it to `~/binutils-tmp`.)
|
||||
|
||||
Create and enter a build directory
|
||||
|
||||
```bash
|
||||
mkdir build-binutils
|
||||
cd build-binutils
|
||||
```
|
||||
|
||||
Configure the build
|
||||
|
||||
```bash
|
||||
../binutils-2.35/configure --target=mips-linux-gnu --prefix=/opt/cross --disable-gprof --disable-gdb --disable-werror
|
||||
```
|
||||
|
||||
Make and install binutils
|
||||
|
||||
```bash
|
||||
make -j
|
||||
sudo make install
|
||||
```
|
||||
|
||||
Edit your `~/.bash_profile`/`~/.zsh_profile` (or whichever shell you use) to add the new binutils binaries to the system PATH
|
||||
|
||||
```bash
|
||||
echo "export PATH=$PATH:/opt/cross/bin" >> ~/.bash_profile
|
||||
```
|
||||
|
||||
Reload ~/.bash_profile (or just launch a new terminal tab)
|
||||
|
||||
```bash
|
||||
source ~/.bash_profile
|
||||
```
|
||||
|
||||
If this worked, you can now delete the temporary directory `~/binutils-tmp`.
|
||||
|
||||
## Final note
|
||||
|
||||
Apple's version of `make` is very out-of-date, so you should use the brew-installed `gmake` in place of `make` in this repo from now on.
|
||||
|
||||
You should now be able to continue from [step 2](../README.md#2-clone-the-repository) of the Linux instructions.
|
||||
@@ -1,149 +0,0 @@
|
||||
# Contributing to the Majora's Mask Decompilation Project
|
||||
|
||||
Thanks for helping us reverse engineer *The Legend of Zelda: Majora's Mask* for the N64!
|
||||
All contributions are welcome. This is a group effort, and even small contributions can make a difference. Some tasks also don't require much knowledge to get started.
|
||||
|
||||
This document is meant to be a set of tips and guidelines for contributing to the project.
|
||||
For general information about the project, see [our readme](https://github.com/zeldaret/mm/blob/main/README.md).
|
||||
|
||||
Most discussions happen on our [Discord Server](https://discord.zelda64.dev) where you are welcome to ask if you need help getting started, or if you have any questions regarding this project and other decompilation projects.
|
||||
|
||||
## Useful Links
|
||||
|
||||
- [Installation guide](https://github.com/zeldaret/mm/blob/main/README.md#installation) - Instructions for getting this repository set up and built on your machine.
|
||||
- [Style Guide](STYLE.md) - Description of the project style that we ask contributors to adhere to.
|
||||
- [Code Review Guidelines](REVIEWING.md) - These are the guidelines that reviewers will be using when reviewing your code. Good to be familiar with these before submitting your code.
|
||||
|
||||
- [Zelda 64 Reverse Engineering Website](https://zelda64.dev/games/mm) - Our homepage, with FAQ and progress graph :chart_with_upwards_trend:.
|
||||
- [MM decomp tutorial](tutorial/contents.md) Detailed tutorial for learning in general how decomp works and how to decompile a small, simple file.
|
||||
- [Introduction to OOT decomp](https://github.com/zeldaret/oot/blob/main/docs/tutorial/contents.md) - The tutorial the MM one was based on. For OOT, but largely applicable to MM as well. Covers slightly different topics, including how to get your data OK with `vbindiff`.
|
||||
- The `#resources` channel on the Discord contains many more links on specific details of decompiling IDO MIPS code.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### What should I know to take part?
|
||||
|
||||
Basic knowledge of C, particularly arrays and pointers, is extremely useful. Knowledge of MIPS is not required initially, you can pick it up as you work through some decompilation.
|
||||
|
||||
You should be familiar with using git and GitHub. There are a number of tutorials available online, [such as this one](https://github.com/firstcontributions/first-contributions) which can help you get started.
|
||||
|
||||
The most useful knowledge to have is a general understanding of how the game works. An afternoon of constructive mucking about in the [MM Practice Rom](https://kz.zeldacodes.org/) (aka KZ) or the [OoT Practice Rom](http://practicerom.com/) (aka GZ) will be very beneficial if you have not looked at either of the N64 Zelda's internals before.
|
||||
|
||||
The [OoT Decompilation Project](https://github.com/zeldaret/oot) is farther along than this project, so it can also be a great resource.
|
||||
|
||||
This project only uses *publicly available code*.
|
||||
|
||||
**N.B.** Anyone who wishes to contribute to the OOT or MM projects **must not have accessed leaked source code at any point in time** for Nintendo 64 SDK, iQue player SDK, libultra, Ocarina of Time, Majora's Mask, Animal Crossing/Animal Forest, or any other game that shares the same game engine or significant portions of code to a Zelda 64 game or any other console similar to the Nintendo 64.
|
||||
|
||||
### Environment Setup
|
||||
|
||||
Get started by following the [installation guide in the readme](https://github.com/zeldaret/mm/blob/main/README.md#installation).
|
||||
You should be able to build a matching ROM before you start making any changes.
|
||||
|
||||
### First Contribution
|
||||
|
||||
Usually, the best place to get started is to decompile an actor overlay.
|
||||
An *actor* is any thing in the game that moves or performs actions or interactions. This includes things like Link, enemies, NPCs, doors, pots, etc. Actors are good for a first file because they are generally small, self-contained systems.
|
||||
|
||||
We recommend that you [join the Discord](https://discord.zelda64.dev/) to say hello and get suggestions on where to start on the `#mm-decomp` channel.
|
||||
|
||||
We track who is working on what on some Google Sheets available in the Discord. Once you've decided on or been recommended a good first file, mark it as Reserved.
|
||||
|
||||
The workflow is:
|
||||
|
||||
- Reserve a file,
|
||||
- decompile it,
|
||||
- submit a PR,
|
||||
- repeat while addressing review comments.
|
||||
|
||||
The expectation is that one reservation goes to one file which ends up in a one file PR, although naturally some files are more sensibly worked on as a group, for example two actors that work together. This also does not apply to large asset files like `gameplay_keep`: you can just reserve the parts that are used in your files.
|
||||
|
||||
If possible, we expect reserved files to be completed. If you find you cannot complete a file, because it is intractable for one reason or another, or real-life circumstances get in the way, please talk to one of the leads in Discord; we may find someone else interested in helping you finish, or who is happy to take over the file from you completely. If you unreserve a file on which you have useful progress, please leave a link to your branch in the Notes column on the Google Sheet that the next person who works on the file can use.
|
||||
|
||||
## Style Guide & Conventions
|
||||
|
||||
See the [Style Guide](STYLE.md).
|
||||
|
||||
## `NON_MATCHING` and `NON_EQUIVALENT`
|
||||
|
||||
If you make significant progress on decompiling a function, but can't get it to match perfectly, you can use a `NON_MATCHING` block to commit your code but have it excluded from the build, like so:
|
||||
|
||||
```c
|
||||
#ifdef NON_MATCHING
|
||||
void CollisionCheck_SpawnWaterDroplets(PlayState* play, Vec3f* v) {
|
||||
// ...
|
||||
}
|
||||
#else
|
||||
void CollisionCheck_SpawnWaterDroplets(PlayState* play, Vec3f* v);
|
||||
#pragma GLOBAL_ASM("asm/non_matchings/code/z_collision_check/CollisionCheck_SpawnWaterDroplets.s")
|
||||
#endif
|
||||
```
|
||||
|
||||
Before PRing with a `NON_MATCHING`, you can try
|
||||
|
||||
- using the [decomp-permuter](https://github.com/simonlindholm/decomp-permuter) to find a closer match,
|
||||
- Asking in `#mm-decomp-help` in Discord; the easiest way to allow other people to play around with the function you are stuck on is to make a scratch on [decomp.me](http://decomp.me).
|
||||
|
||||
`NON_EQUIVALENT` can be used with the same syntax as `NON_MATCHING`, but it is used to mark sections of code which do not match *and* do not have the same behavior as the original code.
|
||||
|
||||
## Matching and Documenting
|
||||
|
||||
Usually, the first step of decompiling a section of code is to get it *matching*: to produce a C version of the code that can be compiled into an identical ROM.
|
||||
|
||||
However, the goal of this project is to produce a codebase that can be understood and modified.
|
||||
Therefore once C code produces matching assembly, the next step is to *document* the code.
|
||||
|
||||
Documenting is more than just adding comments. Documenting also includes:
|
||||
|
||||
- Renaming functions, variables, and struct members
|
||||
- Using (or adding) constants, enums, and macros when possible
|
||||
- Explaining sections of code that are not straightforward
|
||||
|
||||
Overlays are not required to be documented at this time, but files from `code/` and `boot/` should be documented. When documentation on a file has been started it should be as complete as reasonable.
|
||||
|
||||
See the [Style Guide](STYLE.md) for more details on documentation style.
|
||||
|
||||
## Pull Requests (PRs)
|
||||
|
||||
### Checklist
|
||||
|
||||
Before opening a PR, walk through the following steps to ensure that your code conforms to the style guide and conventions.
|
||||
|
||||
- `make` successfully builds a matching ROM.
|
||||
- `./format.py` was run to apply standard formatting.
|
||||
- No new compiler warnings were introduced during the build process.
|
||||
- Can be verified locally by running `tools/warnings_count/check_new_warnings.sh`
|
||||
- New variables & functions should follow standard naming conventions.
|
||||
- Comments and variables have correct spelling.
|
||||
|
||||
Feel free to reach out on the Discord if you have any questions about these steps or encounter any issues.
|
||||
|
||||
### Pull Request Process
|
||||
|
||||
After opening a PR, the Jenkins agent will test formatting, the contents of the spec, build the rom and check for warnings.
|
||||
If there is an error, double-check that you can successfully
|
||||
|
||||
```bash
|
||||
make disasm
|
||||
./extract_assets.py -f
|
||||
make clean
|
||||
make
|
||||
```
|
||||
|
||||
locally. If the build is `OK`, the next thing to check is that all added/modified files were `git add`-ed to your commit. The final check before posting on Discord for help is that there are no new warnings added to the code causing Jenkins to fail. You can check this by running: `tools/warnings_count/check_new_warnings.sh`.
|
||||
|
||||
Each PR needs a review from two reviewers, at least one a project lead, and final approval from Kenix.
|
||||
|
||||
If the PR author agrees with a reviewer's suggestion, they make the change and resolve the conversation. If they disagree, have a better idea, or want to comment on something, they should at least leave a comment, and discuss it in Discord if it's not going to be resolved quickly, since long conversations on GitHub are hard to read.
|
||||
|
||||
Once all comments is addressed and all reviewers have approved, the PR will be merged.
|
||||
|
||||
Project leads are responsible for ensuring that these conventions are followed.
|
||||
|
||||
### Some git notes
|
||||
|
||||
- You should work on a branch on your fork separate from your copy of main: it is always useful to have a clean main branch around if you need to fix something.
|
||||
- When a PR is merged into main, it may conflict with your work. While your branch is private (in particular, not used for a PR), you can rebase, but when your branch is public/used for a PR always merge main instead of rebasing: it makes it much easier for people to understand what you changed since the last review.
|
||||
- We squash commits when merging, so your commit history does not have to be completely spotless.
|
||||
|
||||
Throughout the PR process, you (the author) should update the rows on the appropriate Google Sheets with the appropriate information as the decompilation process progresses.
|
||||
@@ -1,58 +0,0 @@
|
||||
# Reviewing Pull Requests to the Majora's Mask Decompilation Project
|
||||
|
||||
Thanks for helping us reverse engineer *The Legend of Zelda: Majora's Mask*!
|
||||
We encourage all contributors to participate in code review: this is your codebase too!
|
||||
Every review submitted helps us keep code quality high and code merged in more quickly.
|
||||
|
||||
This document is meant to be a set of tips and guidelines for reviewers of pull requests to the project.
|
||||
For general information about the project, see [our readme](https://github.com/zeldaret/mm/blob/main/README.md).
|
||||
|
||||
Most discussions happen on our [Discord Server](https://discord.zelda64.dev) where you are welcome to ask if you need help getting started, or if you have any questions regarding this project and other decompilation projects.
|
||||
|
||||
Other links are available in the [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
||||
## Getting Started
|
||||
|
||||
### What should I know to take part in the review process?
|
||||
|
||||
You should first famiiarise yourself with our [Contributing guide](CONTRIBUTING.md) and [Style guide](STYLE.md). It is also recommended that you have already successfully submitted a merged pull request to understand how the process works before submitting a review.
|
||||
|
||||
## Pull Requests (PRs)
|
||||
|
||||
### How to review a Pull Request
|
||||
|
||||
- When reviewing a PR it is suggested that you follow the checklist outlined in this section.
|
||||
- You are not required to go through *every* item in the checklist. It is meant as a guide; if you have only one thing you want to mention, that's fine too.
|
||||
- Once the PR author has addressed all of your comments, you should add a review with approval to the PR to signify to the project leads that this PR has been through peer review.
|
||||
- If someone does not address your comments and expresses that a different way is better than yours, look for feedback from other contributors (we encourage discussing this sort of thing in Discord, since long GitHub conversations get hard to read). The project leads will have final say in these situations. All decisions are generally guided by a consensus of contributors.
|
||||
|
||||
### Reviewer Checklist
|
||||
|
||||
- [ ] Jenkins build is successful.
|
||||
- [ ] `make` builds a matching ROM.
|
||||
- [ ] `format.py` was run.
|
||||
- [ ] `spec` contains correct relocation files.
|
||||
- [ ] Any new compiler warnings that were added are required for matching. Ensure there is good reason if the warnings files have changed.
|
||||
- [ ] Files with `NON_MATCHING` functions have equivalent behaviour.
|
||||
- [ ] `code` and `boot` segment files should be documented as much as possible. Overlays with documentation should be as complete as is reasonable.
|
||||
- [ ] Overlays should have macros to define access to parameters if the parameter uses bitwise access. The params should have an enum when it makes sense.
|
||||
- [ ] Comments and variables have correct spelling.
|
||||
- [ ] The following should be declared in an Actor header file. There should be nothing else in the Actor header file.
|
||||
- [ ] Main Actor struct
|
||||
- [ ] Extern'd initVar data.
|
||||
- [ ] Types used in the actor struct. Specific example would be actionFunc typedefs.
|
||||
- [ ] Param field macros and/or enums.
|
||||
- [ ] For any other additional `enum`/`struct`/`define`/`function`/`global`, there needs to be evidence it is needed in another file.
|
||||
- [ ] New variables and functions should follow standard naming conventions.
|
||||
- [ ] Constants are converted to whichever looks best in context: hexadecimal, decimal, or float
|
||||
- [ ] Rotation angles should always be in hexadecimal
|
||||
- [ ] Colour values should always be in decimal.
|
||||
- [ ] Functions, structs, unions, enums, and typedefs are `TitleCase` (`DmRavine`)
|
||||
- [ ] Functions within a system separate the system from the function's own subname with an underscore (`DmRavine_Init`)
|
||||
- [ ] Variable names are `camelCase` (`actionFunc`)
|
||||
- [ ] Global variables start with `g` (`gSaveContext`)
|
||||
- [ ] Static global variables start with `s` (`sSphereInit`)
|
||||
- [ ] Macros and enum constants are `SCREAMING_SNAKE_CASE` (`DM_RAVINE_STATE_ACTIVE`)
|
||||
- [ ] Trailing commas in array and struct definitions chosen to make it look best (see e.g. `EnJcMatoDamageTable`)
|
||||
|
||||
etc.
|
||||
@@ -1,249 +0,0 @@
|
||||
# Majora's Mask decompilation style guide
|
||||
|
||||
In general, completed documented files are a good place to look to understand project style in general.
|
||||
|
||||
## Types
|
||||
|
||||
Use the types from `ultratypes.h`, not the standard C types: i.e. `u8`,`s8`,`s16`,`u16`,`s32`,`u32`,`f32` rather than `char`, `short`, `int`, `float` and their `signed`/`unsigned` varieties.
|
||||
|
||||
We always write our enums and structs as `typedef`s. (Usually one can't use an enum typedef as a function argument since enum typedefs are implicitly `s32`.)
|
||||
|
||||
## Naming
|
||||
|
||||
Names are "big-endian": the most significant/largest part of the system goes first, e.g. `DM_RAVINE_STATE_ACTIVE` rather than `DM_RAVINE_ACTIVE_STATE`.
|
||||
|
||||
| Type | Style | Example |
|
||||
| -------------------- | ----------------------- | ----------------------- |
|
||||
| Local variables | camelCase | `yawToPlayer` |
|
||||
| Global variables | gCamelCase | `gSaveContext` |
|
||||
| Static variables[^1] | sCamelCase | `sZeroVec` |
|
||||
| Struct members | camelCase | `actionFunc` |
|
||||
| Struct names | PascalCase | `EnFirefly` |
|
||||
| Enum types | PascalCase | `EnFireflyMainType` |
|
||||
| Enum values | SCREAMING_SNAKE_CASE | `AT_ON` |
|
||||
| Defines/macros | SCREAMING_SNAKE_CASE | `SCREEN_WIDTH`,`ABS(x)` |
|
||||
| Functions | SystemName_FunctionName | `Actor_SpawnAsChild` |
|
||||
| Files | snake_case | `z_en_firefly.c` |
|
||||
|
||||
[^1]: including in-function static
|
||||
|
||||
Action functions are usually named with a simple present-tense verb or verb phrase: `{...}_Talk`, `{...}_Wait`, `{...}_FallToGround`, etc. Setup functions are `Setup{name of action}`.
|
||||
|
||||
Ideally names should be both short and clear, although it's better to be clear than short.
|
||||
|
||||
## Formatting
|
||||
|
||||
A lot of formatting is done by clang-format, such as
|
||||
|
||||
- indent is 4 spaces, tabs are not used
|
||||
- case labels indented
|
||||
- 120 column limit
|
||||
- brackets go on the same line (`if (1) {`)
|
||||
- pointer goes on type (`s32* var;` not `s32 *var;`)
|
||||
|
||||
There are various other conventions that it does not catch, though:
|
||||
|
||||
- Blank line between declarations and code:
|
||||
|
||||
```c
|
||||
s32 var;
|
||||
|
||||
func();
|
||||
```
|
||||
|
||||
- combine declarations and definitions if possible:
|
||||
|
||||
```c
|
||||
s32 var = 0;
|
||||
|
||||
func();
|
||||
```
|
||||
|
||||
instead of
|
||||
|
||||
```c
|
||||
s32 var;
|
||||
|
||||
var = 0;
|
||||
func();
|
||||
```
|
||||
|
||||
- blank lines between switch cases if they're long (use your judgement).
|
||||
|
||||
## Numbers
|
||||
|
||||
### dec(imal)
|
||||
|
||||
- timers
|
||||
- colours and alpha
|
||||
- Usually array accesses and sizes
|
||||
|
||||
### hex(adecimal)
|
||||
|
||||
- angles (for now; the code itself is very inconsistent with round hex, round dec, and degrees)
|
||||
- Addresses
|
||||
- Bitmasks (i.e. `& 0x80` etc.)
|
||||
- Struct offset comments
|
||||
|
||||
Numbers below `10`/`0xA` do not need the `0x` if by themselves in code.
|
||||
|
||||
### Booleans
|
||||
|
||||
If a function returns only `0` or `1`, and is used as a boolean (i.e. in conditionals), replace the returns by `false` and `true`. (We do not use `bool`, partly because is a C99 thing, and partly because the original has used almost every integer type as a boolean return at some point!)
|
||||
|
||||
### Floats
|
||||
|
||||
Floats usually need an `f` on the end to match, or IDO will use doubles. Our floats are always of the form `1.0f`, even when the decimal part is zero.
|
||||
|
||||
## Conditionals/Loops
|
||||
|
||||
- Spacing out conditional or loop blocks from surrounding code often makes them easier to read.
|
||||
- Avoid assigning or mutating variables in conditionals if possible (including `++`/`--`), avoid side effects in the loop increment slot (i.e. incrementing/assigning to loop variables is fine, something like `*a = b++` is not).
|
||||
- We *always* use `{}` on conditional/loop blocks, even if they're one line (clang-tidy will enforce this).
|
||||
- When conditions are `&&`d or `||`d together, use brackets around each that includes an arithmetic comparison or bitwise operator (i.e. not `!var` or `func()`, but ones with `==` or `&` etc.)
|
||||
- Flag checks or functions that return booleans do not need the `== 0`/`!= 0`.
|
||||
- Prefer `if-else` over `if { return; }`, i.e.
|
||||
|
||||
```c
|
||||
if (cond) {
|
||||
foo();
|
||||
} else {
|
||||
bar();
|
||||
}
|
||||
```
|
||||
|
||||
over
|
||||
|
||||
```c
|
||||
if (cond) {
|
||||
foo();
|
||||
return;
|
||||
}
|
||||
bar();
|
||||
```
|
||||
|
||||
**Exception**: After `Actor_Kill` or sometimes setting the action function, if it makes sense to do so (this expresses the finality a bit better).
|
||||
|
||||
## Macros and enums
|
||||
|
||||
Become familiar with the various defines and enums we have available. There are too many to list all of them here, but the following are common:
|
||||
|
||||
- Those in `macros.h`
|
||||
- `ABS`, `ABS_ALT`,
|
||||
- `CLAMP` and friends,
|
||||
- `BINANG_*`, which are used for angles, especially when there's a lot of `s16` casts around
|
||||
- `MTXMODE` for many of the `sys_matrix` functions
|
||||
- CollisionCheck flags: `AT_ON` and so on. Pick the appropriate one for the collider type.
|
||||
- Actor flags, `ACTOR_FLAG_N`.
|
||||
|
||||
Damage flag enums are not being used at present: we want to wait until we have a better idea what the common groupings should be.
|
||||
|
||||
Pre-C99, commas at the end of the last item in an enum will cause a compiler warning, so leave them off.
|
||||
|
||||
All compound flag lists (e.g. `ACTOR_FLAG_UNFRIENDLY | ACTOR_FLAG_FRIENDLY`) should be listed in *ascending* order
|
||||
|
||||
## Arrays
|
||||
|
||||
- It's better to not hardcode array sizes (easier to mod)
|
||||
- Use `sizeof` or `ARRAY_COUNT`/`ARRAY_COUNTU` where it makes sense, e.g. in loops that are using an array.
|
||||
- clang-format sometimes does weird things to array formatting. Experiment with and without a comma after the last element and see which looks better.
|
||||
|
||||
## Play2
|
||||
|
||||
In some particular instances, IDO requires the function argument `play` to be cast to a second variable of the same type to match. In these particular instances, the function argument should be renamed to `play2` and than this `play2` just assigned to a stack variable called `play`. This cast should occur before the actor `THIS` cast is made. For example in `z_en_firefly.c`
|
||||
```c
|
||||
void EnFirefly_Update(Actor* thisx, PlayState* play2) {
|
||||
PlayState* play = play2;
|
||||
EnFirefly* this = THIS;
|
||||
```
|
||||
|
||||
In other places the cast is actually not explictly needed, but a stack `pad` variable is still needed. For this there should just be a stack variable called `pad` of type `s32` before the actor `THIS` cast. For example in `z_bg_goron_oyu`
|
||||
|
||||
```c
|
||||
void BgGoronOyu_Init(Actor* thisx, PlayState* play) {
|
||||
s32 pad;
|
||||
BgGoronOyu* this = THIS;
|
||||
CollisionHeader* colHeader = NULL;
|
||||
```
|
||||
|
||||
In general, pads should be `s32`, or `s16`/`s8` if required.
|
||||
|
||||
## Documentation and Comments
|
||||
|
||||
Documentation includes:
|
||||
|
||||
- Naming functions
|
||||
- Naming struct variables
|
||||
- Naming data
|
||||
- Naming local variables
|
||||
- Describing the general purpose of the file
|
||||
- Describing any unusual, interesting or strange features of how the file or parts of its content work
|
||||
- Labelling and explaining bugs
|
||||
- Making enums or defines for significant numbers for the file, like actor params values.
|
||||
- Naming the contents of the asset file(s) the file may use (for an actor, the object(s) it uses)
|
||||
|
||||
If you are not sure what something does, it is better to leave it unnamed than name it wrongly. It is fine to make a note of something you are not sure about when PRing, it means the reviewers will pay special attention to it.
|
||||
|
||||
We use comments for:
|
||||
|
||||
- Top of file: a short description of the system. For actors there is already a brief description of our current understanding, but feel free to add to it.
|
||||
- For function descriptions, we use multiline comments,
|
||||
|
||||
```c
|
||||
/**
|
||||
* Describe what the function does
|
||||
*/
|
||||
```
|
||||
|
||||
These are *optional*: if you think the code is clear enough, you do not need to put a comment. You can use Doxygen formatting if you think it adds something, but it is also not required.
|
||||
- If something in a function is strange, or unintuitive, do leave a comment explaining what's going on. We use `//` for this.
|
||||
- We also use `//` for temporary comments above a function. Feel free to use `TODO:` in these if appropriate.
|
||||
- A bug should be commented with an `//! @bug Bug description` above the code that causes the bug.
|
||||
|
||||
## What goes where
|
||||
|
||||
This section mostly applies to actors.
|
||||
|
||||
### Functions
|
||||
|
||||
All functions should go in the main C file in the same order as the assembly (the latter is required to match anyway). (We may make exceptions for particularly large files with a particular organisational structure, but we ask that you check on Discord first before doing this)
|
||||
|
||||
### Data
|
||||
|
||||
- If in doubt, leave all the data at the top of the file. Reviewers will decide for you.
|
||||
- Data must go in the same order as in the assembly files, but is only constrained by other data, not functions or rodata.
|
||||
- Some data has to be inline static to match. Generally it's better to not use `static` on data outside funtions until the file is matching, since `static` data is left out of the mapfile and this makes debugging harder.
|
||||
- *This is even more true of bss, where we have trouble with IDO unpredictably reordering it in certain files.*
|
||||
- For small arrays or simple data that is used in only one function, we usually inline it, if it fits in the ordering.
|
||||
- Generally data that is only used by the draw functions is put down near them: this is one of the few consistencies in ordering of actors' functions.
|
||||
|
||||
### Enums and defines
|
||||
|
||||
- Actors that bitpack params should have macros made for each access or write that is made. `z_en_dg.h` has an undocumented example,
|
||||
|
||||
```c
|
||||
#define ENDG_GET_FC00(thisx) (((thisx)->params & 0xFC00) >> 0xA)
|
||||
#define ENDG_GET_3E0(thisx) (((thisx)->params & 0x3E0) >> 5)
|
||||
```
|
||||
|
||||
while `z_en_firefly.h` has a documented one,
|
||||
|
||||
```c
|
||||
#define KEESE_INVISIBLE (1 << 0xF)
|
||||
#define KEESE_GET_MAIN_TYPE(thisx) ((thisx)->params & 0x7FFF)
|
||||
```
|
||||
|
||||
- In a similar manner, actors that use `home.rot.(x|y|z)` like params should also macros made for accesses and writes. (See, e.g. `z_obj_bean.h`.)
|
||||
- Stuff that only the actor itself will use goes in the C file unless needed in the header.
|
||||
- Anything actor-specific that might be used by another file goes in the header, in particular params access macros.
|
||||
- Anything that is expected to have widespread use should go in `macros.h` or an appropriate header in `include`.
|
||||
|
||||
### Objects
|
||||
|
||||
Are covered in the [ZAPD extraction xml spec](../tools/ZAPD/docs/zapd_extraction_xml_reference.md). Symbol names are `gPrefixDescriptionSuffix` for symbols accessed from the header (they will be global). Texture OutNames are in snake_case since they are filenames.
|
||||
|
||||
## Above all else
|
||||
|
||||
*All of the above is subservient to matching.* Sometimes IDO cares about newlines, for example.
|
||||
|
||||
If you are not sure about any of the above, please ask in Discord.
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 216 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 84 KiB |
@@ -1,311 +0,0 @@
|
||||
# Table of Contnets
|
||||
|
||||
- [Table of Contnets](#table-of-contnets)
|
||||
- [Introduction](#introduction)
|
||||
- [In the repository](#in-the-repository)
|
||||
- [`diff.py`](#diffpy)
|
||||
- [`tools/m2ctx.py`](#toolsm2ctxpy)
|
||||
- [`tools/overlayhelpers/actor_symbols.py`](#toolsoverlayhelpersactor_symbolspy)
|
||||
- [`first_diff.py`](#first_diffpy)
|
||||
- [`sym_info.py`](#sym_infopy)
|
||||
- [`extract_assets.py`](#extract_assetspy)
|
||||
- [`tools/assist.py`](#toolsassistpy)
|
||||
- [`tools/get_actor_sizes.py`](#toolsget_actor_sizespy)
|
||||
- [`tools/progress.py`](#toolsprogresspy)
|
||||
- [`tools/regconvert.py`](#toolsregconvertpy)
|
||||
- [`tools/rename_global_asm.py`](#toolsrename_global_asmpy)
|
||||
- [`tools/rename_sym.sh`](#toolsrename_symsh)
|
||||
- [`tools/actorfixer.py`](#toolsactorfixerpy)
|
||||
- [`tools/timeconv.py`](#toolstimeconvpy)
|
||||
- [`tools/sfx_convert.py`](#toolssfx_convertpy)
|
||||
- [`tools/vt_fmt.py`](#toolsvt_fmtpy)
|
||||
- [`tools/graphovl.py`](#toolsgraphovlpy)
|
||||
- [`tools/warnings_count/check_new_warnings.sh`](#toolswarnings_countcheck_new_warningssh)
|
||||
- [`tools/warnings_count/update_current_warnings.sh`](#toolswarnings_countupdate_current_warningssh)
|
||||
- [`fixle.sh`](#fixlesh)
|
||||
- [`format.py`](#formatpy)
|
||||
- [External tools](#external-tools)
|
||||
- [mips_to_c](#mips_to_c)
|
||||
- [Permuter](#permuter)
|
||||
- [vbindiff](#vbindiff)
|
||||
- [Texture64](#texture64)
|
||||
- [Z64Utils](#z64utils)
|
||||
- [Retired tools](#retired-tools)
|
||||
- [`tools/overlayhelpers/ichaindis.py`](#toolsoverlayhelpersichaindispy)
|
||||
- [`tools/overlayhelpers/colliderinit.py`](#toolsoverlayhelperscolliderinitpy)
|
||||
- [`tools/overlayhelpers/colchkinfoinit.py`](#toolsoverlayhelperscolchkinfoinitpy)
|
||||
- [`tools/overlayhelpers/damage_table.py`](#toolsoverlayhelpersdamage_tablepy)
|
||||
- [`tools/vtxdis`](#toolsvtxdis)
|
||||
- [Reservation Tracking](#reservation-tracking)
|
||||
|
||||
## Introduction
|
||||
|
||||
There are a variety of tools that are used to assist in the decompilation process. This guide is an introduction to some of the tools that are used. Most of these tools are located in the `mm/tools` directory, others are either in the project root, or are separate programs entirely. Almost all of these programs have more information available via running them with `-h`.
|
||||
|
||||
## In the repository
|
||||
|
||||
### `diff.py`
|
||||
|
||||
Your best friend for most of the decompilation work. Compares the original assembly extracted from the ROM instruction-for-instruction with what the code you have written compiles to.
|
||||
|
||||
To use `diff.py`, you need a copy of a matching build folder to diff against. In MM, this can be made with `make diff-init`, which will rebuild both ROMs, check for OK, and copy the build folder and the roms.
|
||||
|
||||
`diff.py` takes the function symbol name as the main argument:
|
||||
|
||||
```bash
|
||||
./diff.py <flags> ObjTree_Init
|
||||
```
|
||||
|
||||
`diff.py` reads the respective `mm.map` files to find the function. If the function has been renamed, it will not be able to find it. You should not edit map files yourself, but instead rerun `make diff-init` (it is possible to copy the build folder manually, but it's better not to, since `make diff-init` guarantees an OK `expected` folder).
|
||||
|
||||
The recommended flags used are `-mwo`, `-mwo3`, or `-mwob`:
|
||||
|
||||
- `-m` ("make") builds the file used for diffing automatically when `diff.py` is run.
|
||||
- `-w` ("watch") will watch the C file containing the function (and only the C file!) and automatically recompile when it changes.
|
||||
- `-o` ("object file") uses only the `.o` file. This is faster than without, and means you can see symbol names, but obscures the data behind relative addresses: if you have differences in the .text section that are invisible with `-o`, or you suspect the data is shifted in some way, try without
|
||||
- `-3` creates a three-way diff: TARGET/CURRENT/PREVIOUS, which is particularly useful for seeing changes.
|
||||
- `-b` creates a three-way diff: TARGET/CURRENT/BASE, where BASE is the version from when the program was started.
|
||||
- `-s` ("stop") stops diffing at the next `jr $ra`, commonly the end of the function. Beware that some functions with conditionals may have multiple `jr $ra`s, so this feature can chop the bottom off the diff.
|
||||
|
||||
There are numerous other options that are not used as often (changing the diffing algorithm, viewing the source code in the diff, ...), and the various flags have dependencies among themselves, so check its documentation with `-h` to get fuller information.
|
||||
|
||||
Colour explanations (sadly GitHub markdown does not allow for fancy colouring here):
|
||||
|
||||
- white/grey is matching instructions
|
||||
- red is missing instructions
|
||||
- green is extra instructions
|
||||
- blue means something in the instruction is wrong, be it numerical differences in the immediates, or the whole thing. Not all blue is meaningful: using `-o` to get symbols, the addresses of imported data will be blue since the relative address looks different from the absolute one.
|
||||
- yellow means the instruction itself is correct, but the registers used are not (*regalloc*, register allocation)
|
||||
- Each register gets its own colour when the regalloc is wrong, which makes it much easier to follow their usage.
|
||||
- a branch is indicated by coloured `~>`s on the right of the instruction, and their targets by the same colour `~>` on the left of the instruction (remember delay slots!). The colours matching is a good indication that the branches are correct, although not infallible, since there are only so many colours it will use.
|
||||
- occasionally instructions will be coloured a <span style="color:#767676; font-weight: bold; background-color:#0C0C0C; padding: .2rem">dark grey</span>. You should consider this the same as blue.
|
||||
|
||||
Example Diff:
|
||||
|
||||

|
||||
|
||||
### `tools/m2ctx.py`
|
||||
|
||||
Pass it the path to a C file to generate the context for that C file, to help [mips_to_c](#mips_to_c). Writes to a file called `ctx.c` in the root directory of the repo.
|
||||
|
||||
### `tools/overlayhelpers/actor_symbols.py`
|
||||
|
||||
Takes a VRAM or VROM address to get overlay file and offset for an Actor.
|
||||
|
||||
### `first_diff.py`
|
||||
|
||||
Gives you the addresses of first difference in the ROM, the difference, and a count of how many bytes differ, or if the whole ROM is shifted.
|
||||
|
||||
### `sym_info.py`
|
||||
|
||||
Can be given a symbol (function or variable name, for example), and will find its ROM, VRAM, and file using the map file. E.g.
|
||||
|
||||
```bash
|
||||
$ ./sym_info.py ObjTree_Init
|
||||
Symbol ObjTree_Init (RAM: 0x80B9A0B0, ROM: 0xFFF210, build/src/overlays/actors/ovl_Obj_Tree/z_obj_tree.o)
|
||||
```
|
||||
|
||||
### `extract_assets.py`
|
||||
|
||||
A tool that will use ZAPD to extract assets from the baserom.
|
||||
|
||||
### `tools/assist.py`
|
||||
|
||||
Searches for similar functions to the one you are passing, and it tells you which one are decompiled. You can pass the name of an overlay and it will search for similar functions for every function in said overlay.
|
||||
|
||||
(Still a little broken)
|
||||
|
||||
### `tools/get_actor_sizes.py`
|
||||
|
||||
Generates a list of actors with various statistics about their function sizes. Run with `-h` for information on flags.
|
||||
|
||||
(nonmatching does not work currently: we need to adapt it to MM's assembly files)
|
||||
|
||||
### `tools/progress.py`
|
||||
|
||||
Gives the progress output that the website uses. Run for that warm glow.
|
||||
|
||||
### `tools/regconvert.py`
|
||||
|
||||
Convert `mips2c`'s `gRegEditor->data[n]` output (or a raw offset) into the appropriate variable in the REG pages. Can also be run on a file to mass-convert them: run with `-h` for details.
|
||||
|
||||
### `tools/rename_global_asm.py`
|
||||
|
||||
Will rename the single-function assembly files in `asm/non_matchings` when the function they contain has been renamed. Should mean you have to run `make disasm` far less frequently.
|
||||
|
||||
### `tools/rename_sym.sh`
|
||||
|
||||
Will rename a symbol throughout the codebase. **Be very careful with this script**: it has no sanity checks, so you can do a lot of damage if you're not careful. Best to commit before using it.
|
||||
|
||||
### `tools/actorfixer.py`
|
||||
|
||||
Provided it is kept up-to-date with function renames, you can run this to automatically update your branch with the new names. `.` will cause it to replace in the whole repo, or you can pass it a specific file path to run it on only that file.
|
||||
|
||||
### `tools/timeconv.py`
|
||||
|
||||
Changes a raw `u16` value into a macro for clock time in-game:
|
||||
|
||||
```bash
|
||||
$ ./tools/timeconv.py 0x4800
|
||||
6,45 -> 0x4800
|
||||
CLOCK_TIME(6, 45)
|
||||
```
|
||||
|
||||
Either dec or hex input will work. It will warn if the macro output will not match:
|
||||
|
||||
```bash
|
||||
$ ./tools/timeconv.py 44102
|
||||
16,09 -> 0xAC44
|
||||
CLOCK_TIME(16, 9)
|
||||
Warning: Result does not match as-is
|
||||
```
|
||||
|
||||
### `tools/sfx_convert.py`
|
||||
|
||||
Replaces sfx ids by their corresponding defines from `sfx.h`. Run on a single file to replace all of them.
|
||||
|
||||
### `tools/vt_fmt.py`
|
||||
|
||||
Replaces VT symbols by their corresponding macros. Not many files in MM need this; ask if you need help with it.
|
||||
|
||||
### `tools/graphovl.py`
|
||||
|
||||
Creates a graph of action functions (black and green arrows) and function calls (blue arrows) for a given overlay file. For best results, run this from the root directory
|
||||
|
||||
Example: `./tools/graphovl/graphovl.py En_Firefly`
|
||||
|
||||
This will save a `.png` of the overlay in the `/graphs` directory.
|
||||
|
||||
See Example below.
|
||||
|
||||

|
||||
|
||||
### `tools/warnings_count/check_new_warnings.sh`
|
||||
|
||||
Runs a make from clean and checks if new warnings have been produced: we use Jenkins to check this as well, but you should run this before opening a PR.
|
||||
|
||||
You can specify how many threads you would like this to run with by adding the `-jN` flag. Where N is the number of threads. By default this will run using 1 thread (i.e. `-j1`).
|
||||
|
||||
Run `check_new_warnings.sh -h` for more information.
|
||||
|
||||
### `tools/warnings_count/update_current_warnings.sh`
|
||||
|
||||
If you have to add new warnings, **and have permission from the leads**, run this to update the file used for warnings comparison.
|
||||
|
||||
### `fixle.sh`
|
||||
|
||||
Fixes line endings in the repo to Linux style (LF), which is required for the build process to work. (You may be better off creating a new clone directly in Linux/WSL, though)
|
||||
|
||||
### `format.py`
|
||||
|
||||
Formats all C files in the repo using `clang-format-14`, `clang-tidy`, and `clang-apply-replacements` (when multiprocessing). This will touch all files in the repo, so the next `make` will take longer.
|
||||
|
||||
You can specify how many threads you would like this to run with by adding the `-jN` flag. Where N is the number of threads. By default this will run using 1 thread (i.e. `-j1`).
|
||||
|
||||
`clang-14` is available in many native package managers, but if not try:
|
||||
|
||||
Linux:
|
||||
Download llvm's setup script, run it, than install normally
|
||||
```bash
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 14
|
||||
rm llvm.sh
|
||||
sudo apt install clang-format-14 clang-tidy-14 clang-apply-replacements-14
|
||||
```
|
||||
|
||||
Mac:
|
||||
Brew does not have clang-format-14, but MacPorts does.
|
||||
|
||||
(Have been unable to confirm actual commands with somone that has a Mac)
|
||||
|
||||
|
||||
## External tools
|
||||
|
||||
### mips_to_c
|
||||
|
||||
mips_to_c (or mips2c) is a tool that takes MIPS assembly and will attempt to convert it to C.
|
||||
|
||||
There are different ways you can run mips2c:
|
||||
|
||||
- Clone the repo and run it loclly on your own machine. Follow set up instructions at <https://github.com/matt-kempster/mips_to_c>
|
||||
- An online version is available at <https://simonsoftware.se/other/mips_to_c.py>.
|
||||
|
||||
mips_to_c's accuracy can be improved when some context to what the existing C source code is like. See more on how to generate this in [m2ctx](#m2ctx.py)
|
||||
|
||||
### Permuter
|
||||
|
||||
Use when you're low on ideas for matching a function. This goes through a series of heuristic random replacements known to help improve matching, although often at the expense of clarity.
|
||||
|
||||
To set up the permuter, clone the repository <https://github.com/simonlindholm/decomp-permuter> in a directory of your choice. Follow the usage section of the README and ensure you have all the prerequisites installed.
|
||||
|
||||
```bash
|
||||
./import.py <path/to/file.c> <path/to/func.s>
|
||||
```
|
||||
|
||||
on the files to import the code on which to run the permuter to `nonmatchings/func`, and then
|
||||
|
||||
```bash
|
||||
./permuter.py <nonmatchings/func>
|
||||
```
|
||||
|
||||
will run the permuter
|
||||
|
||||
Flags:
|
||||
|
||||
- `-jN` for multithreading
|
||||
- `-J` for using permuter@home (ask in Discord for someone to vouch for you to use this). (Can be combined with `-jN`.)
|
||||
- `--better-only` only report improvements on base score (and not ties)
|
||||
- `--best-only` only report improvements on current best score
|
||||
- `--stack-diffs` take into account differences in placement on the stack (ignored by default)
|
||||
|
||||
More information on these can be found in the permuter's own documentation. There are macros that can be added to the C to get the permuter to run particular types of transformation; these are also detailed in its manual.
|
||||
|
||||
### vbindiff
|
||||
|
||||
Your fallback for anything that you need to correct that is not visible in `diff.py`. Typically you use it to open the nonmatching uncompressed ROM and the uncompressed baserom and look at the differences highlighted in red. `first_diff.py` will usually tell you where to look. Controls are detailed in the program itself.
|
||||
|
||||
### Texture64
|
||||
|
||||
Probably the best of the Nintendo 64 texture viewing programs. It is quite simple, but very good for the one thing that it does.
|
||||
|
||||
### Z64Utils
|
||||
|
||||
Basically essential for convenient analysis of object files. Can analyse and display DisplayLists, some textures, skeletons, animations, and a few other resources. Download from <https://github.com/zeldaret/Z64Utils>.
|
||||
|
||||
## Retired tools
|
||||
|
||||
The following tools are hopefully rendered obsolete by the data extraction that has already been done.
|
||||
|
||||
### `tools/overlayhelpers/ichaindis.py`
|
||||
|
||||
Used for extracting the InitChain information automatically from the ROM.
|
||||
|
||||
### `tools/overlayhelpers/colliderinit.py`
|
||||
|
||||
Used for decompliling the Collider struct of an actor. To use colliderinit, you will need to know the address (VRAM or VROM) of the struct, and the type of collider. There are different types of colliderinits: `ColliderJntSph, ColliderTrisInit, ColliderQuadInit, ColliderSphereInit`.
|
||||
|
||||
### `tools/overlayhelpers/colchkinfoinit.py`
|
||||
|
||||
Similar to `colliderinit.py` but for `CollisionCheckInfoInit`s.
|
||||
|
||||
### `tools/overlayhelpers/damage_table.py`
|
||||
|
||||
Extracts a damagetable from its address. Can also reconvert existing damagetables should the format change.
|
||||
|
||||
### `tools/vtxdis`
|
||||
|
||||
Extracts vertex data from a file. Essentially irrelevant since MM requires extracting this data with ZAPD anyway.
|
||||
|
||||
### Reservation Tracking
|
||||
|
||||
We use a [Google Sheet](https://docs.google.com/spreadsheets/d/1X83YCPRa532v-Zo0WgUsJ2kB1X9RxBta5_p9aWA8uro/edit#gid=0) to keep track of all the files' decompilation statuses and their function statistics. On this board one can reserve a file to work on. The columns are
|
||||
|
||||
- **Actor Id**: The enum that uniquely identifies the actor for spawning etc., useful for looking in KZ/VerboseOcarina/Spectrum. For `boot`/`code`, the VRAM of the file's .text start is given instead.
|
||||
- **Overlay/File name**: Name of the Overlay or File in the codebase.
|
||||
- **Function size statistics**: Intended as a crude estimate of how hard a file will be. Beginners should look for small largest function size and total size; the columns give a rough estimate of the distribution of function sizes without getting unnecessarily statistically descriptive. As you become more experienced, you should work on larger files to leave the smaller ones for other beginners.
|
||||
- **Description**: What the file is. It's helpful if you can fill this in if you know! They should be synchronised with the short top-of-file descriptions.
|
||||
- **Status**: (Free)/Reserved/PR/Merged. To be kept up-to-date by the reserver.
|
||||
- **Reserved**: To reserve a file, put your Discord name in the "Reserved" column. It is common courtesy to not work on a file that is being worked on by another contributor, so ensure the "Reserved" column is blank before working on a file. If it is not, you can ask the reserver(s) if they want to release it or collaborate on it, but don't expect them to agree. More information on what is expected when you reserve a file is available in the [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
- **Interested**: If you would like to work on a file, but don't want to reserve it, or would be interested in collaboration, etc. You should talk to any Interested people if you want to work on the file.
|
||||
- **Notes**: Any other useful information: partial progress by someone unable to finish the file, other files it works with, etc.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,63 +0,0 @@
|
||||
# Getting started
|
||||
|
||||
## [Introduction to decomp](introduction.md)
|
||||
|
||||
- What we are doing
|
||||
- Structure of the code
|
||||
|
||||
## Pre-decompilation
|
||||
|
||||
- [Introduction to git](intro_to_git.md)
|
||||
- Building the repo (follow the instructions in the [README.md](../../README.md))
|
||||
- Most of us use VSCode. Some useful information is [here](vscode.md).
|
||||
<!-- Feel free to document Emacs/Vi/Sublime/whatever if you're familiar with them -->
|
||||
- Choosing a first actor (You want something small that has simple interactions with the environment. A simple NPC can also work, and is what we will use as an illustration for most of the tutorial. There is a collection of actors we think are suitable for beginners on the spreadsheet or Trello)
|
||||
|
||||
## Decompilation
|
||||
|
||||
- [Begining decompilation: order, Init and the actor struct](beginning_decomp.md)
|
||||
- Order of decompilation
|
||||
- Init and common actor features
|
||||
- Initchains
|
||||
- Actors and dynapoly actors
|
||||
- Colliders
|
||||
- Skelanime
|
||||
|
||||
- [The rest of the functions in the actor](other_functions.md)
|
||||
- Order of decompilation
|
||||
- Action Functions and other functions
|
||||
|
||||
- [Draw functions](draw_functions.md)
|
||||
|
||||
- [Data, migration and non-migration](data.md)
|
||||
- Importing the data: early and late
|
||||
- Segmented pointers
|
||||
- Fake symbols
|
||||
- Inlining
|
||||
|
||||
- [Documenting a decompiled file](documenting.md)
|
||||
|
||||
## [Object Decompilation](object_decomp.md)
|
||||
|
||||
- Object files
|
||||
- How we decompile objects
|
||||
|
||||
## After Decompilation
|
||||
|
||||
- See the [CONTRIBUTING.md](../CONTRIBUTING.md) for most of the details for submitting PRs. Remember to format again after making adjustments from reviews!
|
||||
- More information about specific preparations is in [this document](merging.md).
|
||||
|
||||
## Appendices
|
||||
|
||||
- [Types, Structs and Padding](types_structs_padding.md) (a miscellany of useful stuff)
|
||||
- [Advanced control flow](advanced_control_flow.md) (an example of a more complex function which mips2c is not so good at)
|
||||
- [Using the diff script and the permuter](diff_and_permuter.md) (using the diff script and the permuter to match something)
|
||||
- control flow (branches) -> instruction ordering -> register allocation -> stack
|
||||
- [Helper scripts] TODO: link when merged
|
||||
|
||||
To be written, maybe
|
||||
|
||||
- How we use git and GitHub
|
||||
- Some notes on the basic structure of N64 MIPS
|
||||
- Glossary
|
||||
- Conventions
|
||||
@@ -1,177 +0,0 @@
|
||||
# Data
|
||||
|
||||
- Up: [Contents](contents.md)
|
||||
- Previous: [Draw functions](draw_functions.md)
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Data](#data)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Data first](#data-first)
|
||||
- [Extern and data last](#extern-and-data-last)
|
||||
- [Segmented pointers and object symbols](#segmented-pointers-and-object-symbols)
|
||||
- [Fake symbols](#fake-symbols)
|
||||
- [Inlining](#inlining)
|
||||
- [Finally: .bss](#finally-bss)
|
||||
|
||||
Each actor's data is stored in a separate file. EnRecepgirl's data is in `data/overlays/ovl_En_Recepgirl/ovl_En_Recepgirl.data.s`, for example. At some point in the decompilation process we need to convert this raw data into recognisable information for the C to use.
|
||||
|
||||
There are two main ways to do this: either
|
||||
|
||||
1. import the data first and type it later, or
|
||||
2. wait until the data appears in functions, extern it, then import it at the end
|
||||
|
||||
Sometimes something between these two is appropriate: wait until the largest or strangest bits of data appear in functions, get some typing information out of that, and then import it, but for now, let's stick to both of these.
|
||||
|
||||
Both approaches have their advantages and disadvantages.
|
||||
|
||||
## Data first
|
||||
|
||||
This way is good for smaller actors with little data. The OoT tutorial [covers this in plenty of detail](https://github.com/zeldaret/oot/blob/main/docs/tutorial/data.md), and the process in MM is essentially identical, so we won't go over it here.
|
||||
|
||||
## Extern and data last
|
||||
|
||||
Externing is explained in detail in the document about the [Init function](beginning_decomp.md). To summarize, every time a `D_address` appears that is in the data file, we put a
|
||||
|
||||
```C
|
||||
extern UNK_TYPE D_address;
|
||||
```
|
||||
|
||||
at the top of the file, in the same order that the data appears in the data file. We can also give it a type if we know what the type actually is (e.g. for colliders, initchains, etc.), and convert the actual data and place it commented-out under the corresponding line. This means we don't have to do everything at once at the end.
|
||||
|
||||
Once we have decompiled enough things to know what the data is, we can import it. The advantage of doing it this way is we should know what type everything is already: in our work on EnRecepgirl, for example, we ended up with the following data at the top of the file
|
||||
|
||||
```C
|
||||
#if 0
|
||||
ActorInit En_Recepgirl_InitVars = {
|
||||
/**/ ACTOR_EN_RECEPGIRL,
|
||||
/**/ ACTORCAT_NPC,
|
||||
/**/ FLAGS,
|
||||
/**/ OBJECT_BG,
|
||||
/**/ sizeof(EnRecepgirl),
|
||||
/**/ EnRecepgirl_Init,
|
||||
/**/ EnRecepgirl_Destroy,
|
||||
/**/ EnRecepgirl_Update,
|
||||
/**/ EnRecepgirl_Draw,
|
||||
};
|
||||
|
||||
static void* D_80C106B0[4] = { (void*)0x600F8F0, (void*)0x600FCF0, (void*)0x60100F0, (void*)0x600FCF0 };
|
||||
|
||||
// static InitChainEntry sInitChain[] = {
|
||||
static InitChainEntry D_80C106C0[] = {
|
||||
ICHAIN_U8(targetMode, 6, ICHAIN_CONTINUE),
|
||||
ICHAIN_F32(targetArrowOffset, 1000, ICHAIN_STOP),
|
||||
};
|
||||
|
||||
static s32 D_80C106C8 = 0;
|
||||
|
||||
#endif
|
||||
```
|
||||
|
||||
and the main thing we need to understand is `D_80C106B0`
|
||||
|
||||
*Before doing anything else, make sure `make` gives `OK`.*
|
||||
|
||||
First, we tell the compiler to ignore the original data file. To do this, open the file called `spec` in the main directory of the repository, and search for the actor name. You will find a section that looks like
|
||||
|
||||
```
|
||||
beginseg
|
||||
name "ovl_En_Recepgirl"
|
||||
compress
|
||||
include "build/src/overlays/actors/ovl_En_Recepgirl/z_en_recepgirl.o"
|
||||
include "build/data/ovl_En_Recepgirl/ovl_En_Recepgirl.data.o"
|
||||
include "build/data/ovl_En_Recepgirl/ovl_En_Recepgirl.reloc.o"
|
||||
endseg
|
||||
```
|
||||
|
||||
We will eventually remove both of the bottom two lines and replace them with our own reloc file, but for now, just comment out the data line:
|
||||
|
||||
```
|
||||
beginseg
|
||||
name "ovl_En_Recepgirl"
|
||||
compress
|
||||
include "build/src/overlays/actors/ovl_En_Recepgirl/z_en_recepgirl.o"
|
||||
//include "build/data/ovl_En_Recepgirl/ovl_En_Recepgirl.data.o"
|
||||
include "build/data/ovl_En_Recepgirl/ovl_En_Recepgirl.reloc.o"
|
||||
endseg
|
||||
```
|
||||
|
||||
Next remove all the externs, and uncomment their corresponding commented data:
|
||||
|
||||
```C
|
||||
ActorInit En_Recepgirl_InitVars = {
|
||||
/**/ ACTOR_EN_RECEPGIRL,
|
||||
/**/ ACTORCAT_NPC,
|
||||
/**/ FLAGS,
|
||||
/**/ OBJECT_BG,
|
||||
/**/ sizeof(EnRecepgirl),
|
||||
/**/ EnRecepgirl_Init,
|
||||
/**/ EnRecepgirl_Destroy,
|
||||
/**/ EnRecepgirl_Update,
|
||||
/**/ EnRecepgirl_Draw,
|
||||
};
|
||||
|
||||
static void* D_80C106B0[4] = { (void*)0x600F8F0, (void*)0x600FCF0, (void*)0x60100F0, (void*)0x600FCF0 };
|
||||
|
||||
// static InitChainEntry sInitChain[] = {
|
||||
static InitChainEntry D_80C106C0[] = {
|
||||
ICHAIN_U8(targetMode, 6, ICHAIN_CONTINUE),
|
||||
ICHAIN_F32(targetArrowOffset, 1000, ICHAIN_STOP),
|
||||
};
|
||||
|
||||
static s32 D_80C106C8 = 0;
|
||||
```
|
||||
|
||||
That should be everything, and we should now be able to `make` without the data file with no issues.
|
||||
|
||||
## Segmented pointers and object symbols
|
||||
|
||||
The game has a convenient system that allows it to sometimes effectively use offsets into a file instead of raw memory addresses to reference things. This is done by setting a file address to a *segment*. A segmented address is of the form `0x0XYYYYYY`, where `X` is the segment number. There are 16 available segments, and actors always set segment 6 to their object file, which is a file containing assets (skeleton, animations, textures, etc.) that they use. This is what all those `D_06...` are, and it is also what the entries in `D_80C106B0` are: they are currently raw numbers instead of symbols, though, and we would like to replace them.
|
||||
|
||||
There is an obvious problem here, which is that is that these symbols have to be defined *somewhere*, or the linker will complain (indeed, if we change the ones in the array to `D_...`, even if we extern them, we get
|
||||
|
||||
```
|
||||
mips-linux-gnu-ld: build/src/overlays/actors/ovl_En_Recepgirl/z_en_recepgirl.o:(.data+0x20): undefined reference to `D_0600F8F0'
|
||||
```
|
||||
|
||||
As we'd expect, of course: we didn't fulfil our promise that they were defined elsewhere.)
|
||||
|
||||
For actors which have yet to be decompiled, this is mitigated by use of the file `undefined_syms.txt`, which feeds the linker the raw addresses to use as the symbol definitions. However, we want to replace these segmented addresses with proper object symbols whenever possible. In `En_Recepgirl_InitVars`, we can see that this actor uses the object `OBJECT_BG`:
|
||||
|
||||
```c
|
||||
ActorInit En_Recepgirl_InitVars = {
|
||||
/**/ ACTOR_EN_RECEPGIRL,
|
||||
/**/ ACTORCAT_NPC,
|
||||
/**/ FLAGS,
|
||||
/**/ OBJECT_BG,
|
||||
};
|
||||
```
|
||||
|
||||
If we open up `assets/objects/object_bg.h`, we can see a bunch of different names corresponding to every asset in the object. You may notice that some of these names look a bit familiar; `object_bg_Tex_00F8F0` seems very close to the segmented address `(void*)0x600F8F0`. This is the proper object symbol for this segmented address, so we should `#include` this header in our actor and use these object symbols like so:
|
||||
|
||||
```c
|
||||
static void* D_80C106B0[4] = { object_bg_Tex_00F8F0, object_bg_Tex_00FCF0, object_bg_Tex_0100F0, object_bg_Tex_00FCF0 };
|
||||
```
|
||||
|
||||
After replacing every segmented pointer with an object symbol, you should go ahead and delete every segmented pointer associated with this actor from `undefined_syms`.
|
||||
|
||||
We will come back and name these later when we do the object.
|
||||
|
||||
## Fake symbols
|
||||
|
||||
Some symbols in the data have been decompiled wrongly, being incorrectly separated from the previous symbol due to how it was accessed by the actor's functions. However, most of these have now been fixed. Some more detail is given in [Types, structs and padding](types_structs_padding.md) If you are unsure, ask!
|
||||
|
||||
## Inlining
|
||||
|
||||
After the file is finished, it is possible to move some static data into functions. This requires that:
|
||||
|
||||
1. The data is used in only one function
|
||||
2. The ordering of the data can be maintained
|
||||
|
||||
Additionally, we prefer to keep larger data (more than a line or two) out of functions anyway.
|
||||
|
||||
# Finally: .bss
|
||||
|
||||
A .bss contains data that is uninitialised (actually initialised to `0`). For most actors all you need to do is declare it at the top of the actor file without giving it a value, once you find out what type it is. In `code`, it's much more of a problem.
|
||||
|
||||
Next: [Documenting](documenting.md)
|
||||
@@ -1,145 +0,0 @@
|
||||
# `diff.py` and the permuter
|
||||
|
||||
This document is intended as a step-by-step demonstration of matching a reasonably complex function using the diff script `diff.py` and the decomp permuter, both included in the repo. For general information on both see [the tools documentation](../tools.md).
|
||||
|
||||
Until such time as someone finds a suitable function, you can look at the OoT tutorial: [here for diff.py](https://github.com/zeldaret/oot/blob/main/docs/tutorial/beginning_decomp.md#diff) and [here for the permuter](https://github.com/zeldaret/oot/blob/main/docs/tutorial/other_functions.md#the-permuter).
|
||||
|
||||
|
||||
<!--
|
||||
The following is left here to give a rough idea of what the diff script doc could look like.
|
||||
|
||||
|
||||
This gives the following:
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Large image, click to show.
|
||||
</summary>
|
||||
|
||||

|
||||
</details>
|
||||
|
||||
The code we want is on the left, current code on the right. To spot where the function ends, either look for where stuff is added and subtracted from the stack pointer in successive lines, or for a
|
||||
```MIPS
|
||||
jr ra
|
||||
nop
|
||||
```
|
||||
|
||||
The colours mean the following:
|
||||
|
||||
- White/gray is matching lines
|
||||
- Red is lines missing
|
||||
- Green is extra lines
|
||||
- Blue denotes significant differences in instructions, be they just numerical ones, or whole instructions
|
||||
- Yellow/Gold denotes that instructions are correct but register usage is wrong
|
||||
- Other colors are used to distinguish incorrectly used registers or stack variables, to make it easy to follow where they are used.
|
||||
- The colored arrows denote branching. An arrow of one color on the right leads to the arrow of the same color on the left.
|
||||
|
||||
Obviously we want to make the whole thing white. This is the tricky bit: you have to have the imagination to try different things until you get the diff to match. You learn these with experience.
|
||||
|
||||
Generally, the order of what to fix should be:
|
||||
|
||||
1. Control flow (conditionals, where branches go)
|
||||
2. Instruction ordering and type (functions cannot change order, which is a useful indicator)
|
||||
3. Regalloc (register allocation) differences
|
||||
4. Stack differences
|
||||
|
||||
(It is this order because the things that happen earlier can influence the things that happen later.)
|
||||
|
||||
You can keep the diff open in the terminal, and it will refresh when the C file (but not the H file) is changed with these settings.
|
||||
|
||||
In this case, we see that various branches are happening in the wrong place. Here I fear experience is necessary: notice that the function has three blocks that look quite similar, and three separate conditionals that depend on the same variable. This is a good indicator of a switch. Changing the function to use a switch,
|
||||
|
||||
```C
|
||||
void EnJj_Init(Actor* thisx, PlayState* play) {
|
||||
EnJj* this = THIS;
|
||||
|
||||
s32 sp4C;
|
||||
s16 temp_v0;
|
||||
|
||||
sp4C = 0;
|
||||
Actor_ProcessInitChain(&this->dyna.actor, D_80A88CE0);
|
||||
ActorShape_Init(&this->dyna.actor.shape, 0.0f, NULL, 0.0f);
|
||||
temp_v0 = this->dyna.actor.params;
|
||||
|
||||
switch (temp_v0) {
|
||||
case -1:
|
||||
SkelAnime_InitFlex(play, &this->skelAnime, &D_0600B9A8, &D_06001F4C, this->jointTable,
|
||||
this->morphTable, 22);
|
||||
Animation_PlayLoop(&this->skelAnime, &D_06001F4C);
|
||||
this->unk_30A = 0;
|
||||
this->unk_30E = 0;
|
||||
this->unk_30F = 0;
|
||||
this->unk_310 = 0;
|
||||
this->unk_311 = 0;
|
||||
if ((gSaveContext.eventChkInf[3] & 0x400) != 0) {
|
||||
func_80A87800(this, func_80A87BEC);
|
||||
} else {
|
||||
func_80A87800(this, func_80A87C30);
|
||||
}
|
||||
this->childActor = Actor_SpawnAsChild(
|
||||
&play->actorCtx, &this->dyna.actor, play, ACTOR_EN_JJ, this->dyna.actor.world.pos.x - 10.0f,
|
||||
this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, 0, this->dyna.actor.world.rot.y, 0, 0);
|
||||
DynaPolyActor_Init(&this->dyna, 0);
|
||||
CollisionHeader_GetVirtual(&D_06000A1C, &sp4C);
|
||||
this->dyna.bgId =
|
||||
DynaPoly_SetBgActor(play, &play->colCtx.dyna, &this->dyna.actor, sp4C);
|
||||
Collider_InitCylinder(play, &this->collider);
|
||||
Collider_SetCylinder(play, &this->collider, &this->dyna.actor, &D_80A88CB4);
|
||||
this->dyna.actor.colChkInfo.mass = 0xFF;
|
||||
break;
|
||||
case 0:
|
||||
DynaPolyActor_Init(&this->dyna, 0);
|
||||
CollisionHeader_GetVirtual(&D_06001830, &sp4C);
|
||||
// temp_a1_2 = &play->colCtx.dyna;
|
||||
// sp44 = temp_a1_2;
|
||||
this->dyna.bgId =
|
||||
DynaPoly_SetBgActor(play, &play->colCtx.dyna, &this->dyna.actor, sp4C);
|
||||
func_8003ECA8(play, &play->colCtx.dyna, this->dyna.bgId);
|
||||
this->dyna.actor.update = func_80A87F44;
|
||||
this->dyna.actor.draw = NULL;
|
||||
Actor_SetScale(&this->dyna.actor, 0.087f);
|
||||
break;
|
||||
case 1:
|
||||
DynaPolyActor_Init(&this->dyna, 0);
|
||||
CollisionHeader_GetVirtual(&D_0600BA8C, &sp4C);
|
||||
this->dyna.bgId =
|
||||
DynaPoly_SetBgActor(play, &play->colCtx.dyna, &this->dyna.actor, sp4C);
|
||||
this->dyna.actor.update = func_80A87F44;
|
||||
this->dyna.actor.draw = NULL;
|
||||
Actor_SetScale(&this->dyna.actor, 0.087f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
we see that the diff is nearly correct (note that `-3` lets you compare current with previous):
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Large image, click to show.
|
||||
</summary>
|
||||
|
||||

|
||||
</details>
|
||||
|
||||
except we still have some stack issues. Now that `temp_v0` is only used once, it looks fake. Eliminating it actually seems to make the stack worse. To fix this, we employ something that we have evidence that the developers did: namely, we make a copy of `play` (the theory is that they actually used `gameState` as an argument of the main 4 functions, just like we used `Actor* thisx` as the first argument.) The quick way to do this is to change the top of the function to
|
||||
```C
|
||||
void EnJj_Init(Actor* thisx, PlayState* play2) {
|
||||
PlayState* play = play2;
|
||||
EnJj* this = THIS;
|
||||
...
|
||||
```
|
||||
|
||||
It turns out that this is enough to completely fix the diff:
|
||||
|
||||

|
||||
(last two edits, only top shown for brevity)
|
||||
|
||||
Everything *looks* fine, but we only know for sure when we run `make`. Thankfully doing so gives
|
||||
```
|
||||
zelda_ocarina_mq_dbg.z64: OK
|
||||
```
|
||||
|
||||
which is either a sense of triumph or relief depending on how long you've spent on a function. -->
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
# Disassembly quirks
|
||||
|
||||
As MM's disassembly is automatic, there are certain unique problems it has.
|
||||
|
||||
## Renaming functions and variables
|
||||
|
||||
A function must be renamed in `tools/disasm/functions.txt` in addition to the source code, for the disassembler to know what to call the symbol at that address when it sees it.
|
||||
|
||||
Variables must be renamed in `tools/disasm/variables.txt`. It may also be necessary to change their type, count or size to stop the disassembler misusing them.
|
||||
|
||||
You can avoid having to redisassemble every time by running `rename_global_asm.py`, which will rename the individual functions' assembly files in `asm/nonmatchings/` to the name of the function they contain.
|
||||
|
||||
## Fake and incorrect symbols
|
||||
|
||||
TODO
|
||||
|
||||
## Resplitting a file
|
||||
|
||||
The files `boot` and `code` are each divided up into dozens of separate files, that are all joined together into one text, data, rodata and bss section when building the ROM. As such, it has been necessary to guess where the file boundaries are, and not every file contains the correct functions or the correct data (rodata is mostly the exception since it is automatically split).
|
||||
|
||||
To change a split for a file, find its entry in `tools/disasm/files.txt`, and change or create entries to accurately reflect where the file(s) should start. For example, it was found that the last function in `z_nmi_buff.c` had nothing to do with the rest, so it should be split into its own file. Looking up the address of the last function, it was found to be at `0x8010C1B0`, so adding the line:
|
||||
|
||||
```diff
|
||||
0x8010C0C0 : "z_nmi_buff",
|
||||
+++ 0x8010C1B0 : "code_8010C1B0",
|
||||
0x8010C230 : "z_olib",
|
||||
```
|
||||
|
||||
to the file will extract it correctly as a separate file. It also is necessary to make a new C file and move the `GLOBAL_ASM` declaration into it.
|
||||
|
||||
Unfortunately you essentially have to redisassemble after telling the disassembler to resplit a file.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,330 +0,0 @@
|
||||
# Draw functions
|
||||
|
||||
- Up: [Contents](contents.md)
|
||||
- Previous: [The rest of the functions in the actor](other_functions.md)
|
||||
|
||||
Draw functions behave completely differently from the other functions in an actor. They often use a lot of macros.
|
||||
|
||||
This document will be a bit different: we will look at the draw functions in EnRecepgirl, then consider some more complicated examples.
|
||||
|
||||
## A first example
|
||||
|
||||
Unless it is completely invisible, an actor usually has a draw function as one of the main four actor functions. Hence its prototype looks like
|
||||
|
||||
```C
|
||||
void EnRecepgirl_Draw(Actor* thisx, PlayState* play);
|
||||
```
|
||||
|
||||
From now on, the process is rather different from the decompilation process used for the other functions. Here is the output of mips2c after sorting out the actor struct from Init, and with the arguments set back to `Actor* thisx`:
|
||||
```C
|
||||
s32 func_80C10558(PlayState* play, s32 limbIndex, Gfx **dList, Vec3f *pos, Vec3s *rot, Actor *actor); // extern
|
||||
void func_80C10590(PlayState* play, s32 limbIndex, Actor *actor); // extern
|
||||
void *D_80C106B0[4] = {(void *)0x600F8F0, (void *)0x600FCF0, (void *)0x60100F0, (void *)0x600FCF0};
|
||||
|
||||
void EnRecepgirl_Draw(Actor* thisx, PlayState* play) {
|
||||
EnRecepgirl* this = (EnRecepgirl *) thisx;
|
||||
GraphicsContext *sp30;
|
||||
Gfx *temp_v1;
|
||||
GraphicsContext *temp_a0;
|
||||
|
||||
temp_a0 = play->state.gfxCtx;
|
||||
sp30 = temp_a0;
|
||||
Gfx_SetupDL25_Opa(temp_a0);
|
||||
temp_v1 = sp30->polyOpa.p;
|
||||
sp30->polyOpa.p = temp_v1 + 8;
|
||||
temp_v1->words.w0 = 0xDB060020;
|
||||
temp_v1->words.w1 = (u32) D_80C106B0[this->unk_2AC];
|
||||
SkelAnime_DrawTransformFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, (s32) this->skelAnime.dListCount, func_80C10558, NULL, func_80C10590, (Actor *) this);
|
||||
}
|
||||
```
|
||||
|
||||
Notable features are the GraphicsContext temps, and blocks of the form
|
||||
|
||||
```C
|
||||
temp_v1 = sp30->polyOpa.p;
|
||||
sp30->polyOpa.p = temp_v1 + 8;
|
||||
temp_v1->words.w0 = 0xDB060020;
|
||||
temp_v1->words.w1 = (u32) D_80C106B0[this->unk_2AC];
|
||||
```
|
||||
|
||||
(This is a particularly simple example, since there's only one of these blocks. We will give a more involved example later.)
|
||||
|
||||
Each of these blocks converts into a graphics macro. They are usually (but not always) straightforward, but manually converting them is a pain, and there are sometimes special cases. To deal with them easily, we will use a tool from glank's N64 tools. To install these, follow the instructions [here](https://practicerom.com/public/packages/debian/howto.txt).
|
||||
|
||||
For our purposes, we only need one of the programs this provides: `gfxdis.f3dex2`.
|
||||
|
||||
Graphics are actually 64-bit on the Nintendo 64. This code block is a result of instructions telling the processor what to do with the graphics pointer. There are two main types of graphics pointer (there are a couple of others used in `code`, but actors will only use these two),
|
||||
|
||||
- polyOpa ("opaque") for solid textures
|
||||
- polyXlu ("Xlucent" i.e. "translucent") for translucent textures
|
||||
|
||||
Our example is polyOpa, not surprisingly since our receptionist is solid.
|
||||
|
||||
`words.w0` and `words.w1` contain the actual graphics instruction, in hex format. Usually, `w0` is constant and `w1` contains the arguments. To find out what sort of macro we are dealing with, we use `gfxdis.f3dex2`. `w1` is variable, but we need to give the program a constant placeholder. A common word to use is 12345678, so in this case we run
|
||||
|
||||
```
|
||||
gfxdis.f3dex2 -x -g "POLY_OPA_DISP++" -d DB06002012345678
|
||||
```
|
||||
|
||||
- `-x` uses hex instead of the default qu macros (never mind what those are, MM doesn't use them)
|
||||
- `-g` is used to specify which graphics pointer macro to use
|
||||
- `-d` is for the graphics dword
|
||||
|
||||
Our standard now is to use decimal colors. If you have a constant second argument rather than a variable one, you can also use `-dc` to get decimal colors instead of the default hex.
|
||||
|
||||
The output looks like
|
||||
|
||||
```
|
||||
gSPSegment(POLY_OPA_DISP++, 0x08, 0x12345678);
|
||||
```
|
||||
|
||||
We can now replace the `0x12345678` by the actual second word, namely `D_80C106B0[this->unk_2AC]`. We can see mips2c has pulled in this data again: we saw it before in the `Init`.
|
||||
|
||||
The words look like pointers to assets in the actor's object segment, which would make sense if we're looking for textures to draw. Because this data is used in a graphics macro, it will be either a displaylist or a texture; it may as well stay as `void*` until we come back to it later.
|
||||
|
||||
```C
|
||||
gSPSegment(POLY_OPA_DISP++, 0x08, D_80C106B0[this->unk_2AC]);
|
||||
```
|
||||
|
||||
You repeat this for every block in the function.
|
||||
|
||||
If you have worked on OoT, you will be aware of the functions `Graph_OpenDisps` and `Graph_CloseDisps`, and might be surprised to see them missing here. These functions are actually a debug feature: the `OPEN_DISPS` and `CLOSE_DISPS` macros still exist, but they don't expand to functions. Of course this means you have to guess where they go. A sensible guess for `OPEN_DISPS` is where the `gfxCtx` temp assignment first happens; `CLOSE_DISPS` is a bit harder, although it's basically just a `}`, so it *shouldn't* matter as much.
|
||||
|
||||
It's sensible to eliminate all the `gfxCtx` temps and reintroduce as needed. Also remember to change the prototype and function definition back!
|
||||
|
||||
```C
|
||||
s32 func_80C10558(PlayState* play, s32 limbIndex, Gfx **dList, Vec3f *pos, Vec3s *rot, Actor *actor);
|
||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10558.s")
|
||||
|
||||
void func_80C10590(PlayState* play, s32 limbIndex, Actor *actor);
|
||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10590.s")
|
||||
|
||||
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Draw.s")
|
||||
void EnRecepgirl_Draw(Actor* thisx, PlayState* play) {
|
||||
EnRecepgirl* this = THIS;
|
||||
|
||||
OPEN_DISPS(play->state.gfxCtx);
|
||||
|
||||
Gfx_SetupDL25_Opa(play->state.gfxCtx);
|
||||
|
||||
gSPSegment(POLY_OPA_DISP++, 0x08, D_80C106B0[this->unk_2AC]);
|
||||
|
||||
SkelAnime_DrawTransformFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, func_80C10558, NULL, func_80C10590, &this->actor);
|
||||
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
}
|
||||
```
|
||||
|
||||
And this matches.
|
||||
|
||||
The last two functions in the actor are used as arguments in `SkelAnime_DrawTransformFlexOpa`. This is a `SkelAnime` function, except unlike the OoT ones, it has three function callback arguments instead of two: in `functions.h` or `z_skelanime.c`, we find
|
||||
|
||||
```C
|
||||
void SkelAnime_DrawTransformFlexOpa(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
|
||||
OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, TransformLimbDrawOpa transformLimbDraw, Actor* actor)
|
||||
```
|
||||
|
||||
The typedefs of the callbacks it uses are in `z64animation.h`:
|
||||
|
||||
```C
|
||||
typedef s32 (*OverrideLimbDrawOpa)(struct PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot,
|
||||
struct Actor* thisx);
|
||||
|
||||
typedef void (*PostLimbDrawOpa)(struct PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot,
|
||||
struct Actor* thisx);
|
||||
|
||||
[...]
|
||||
|
||||
typedef void (*TransformLimbDrawOpa)(struct PlayState* play, s32 limbIndex, struct Actor* thisx);
|
||||
```
|
||||
|
||||
which is where mips2c got them from.
|
||||
|
||||
In this case, only two of them are used, and it is these that are the last functions standing between us and a decompiled actor.
|
||||
|
||||
## OverrideLimbDraw, PostLimbDraw, TransformLimbDraw
|
||||
|
||||
Well, we don't have a PostLimbDraw here, but as we see from the prototype, it's much the same as the OverrideLimbDraw but without the `pos` argument and no return value.
|
||||
|
||||
```C
|
||||
s32 func_80C10558(PlayState* play, s32 limbIndex, Gfx **dList, Vec3f *pos, Vec3s *rot, Actor *actor) {
|
||||
if (limbIndex == 5) {
|
||||
rot->x += actor->unk2B0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Only two things to do here: we need to use `EnRecepgirl` to get to `actor + 0x2B0`, and the return value is used as a boolean, so we replace `0` by `false` (`true` means "don't draw the limb", and is hardly ever used).
|
||||
|
||||
```C
|
||||
s32 func_80C10558(PlayState* play, s32 limbIndex, Gfx **dList, Vec3f *pos, Vec3s *rot, Actor *thisx) {
|
||||
EnRecepgirl* this = THIS;
|
||||
|
||||
if (limbIndex == 5) {
|
||||
rot->x += this->unk_2AE.y;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
As for the TransformLimbDraw, it has a much simpler prototype. mips2c gives
|
||||
|
||||
```C
|
||||
void func_80C10590(PlayState* play, s32 limbIndex, Actor *actor) {
|
||||
if (limbIndex == 5) {
|
||||
Matrix_RotateYS((s16) (0x400 - actor->unk2AE), 1);
|
||||
Matrix_MultVecX(500.0f, (Vec3f *) &actor->focus);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
There is only minor cleanup needed here:
|
||||
|
||||
- recasting the last argument,
|
||||
- replacing the last argument of `Matrix_RotateYS` by the enum `MTXMODE_APPLY` (which means "use the current matrix instead of starting from a new identity matrix"), and the first argument by `0x400 - this->unk_2AE.x`.
|
||||
- `(Vec3f *) &actor->focus` to `&actor->focus.pos` (this is the same issue as `(Actor*)this`, where mips2c doesn't climb deep enough into the struct).
|
||||
|
||||
```C
|
||||
void func_80C10590(PlayState* play, s32 limbIndex, Actor *thisx) {
|
||||
EnRecepgirl* this = THIS;
|
||||
|
||||
if (limbIndex == 5) {
|
||||
Matrix_RotateYS(0x400 - this->unk_2AE.x, MTXMODE_APPLY);
|
||||
Matrix_MultVecX(500.0f, &this->actor.focus.pos);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Some more examples: ObjTree
|
||||
|
||||
Since EnRecepgirl was a bit light on graphics macros, we will look at an example that has a few more. A nice simple one is `ObjTree_Draw`: the original mips2c output is
|
||||
|
||||
```C
|
||||
void ObjTree_Draw(Actor* thisx, PlayState* play) {
|
||||
s16 sp36;
|
||||
s16 sp34;
|
||||
Gfx *sp28;
|
||||
Gfx *sp20;
|
||||
Gfx *temp_v0;
|
||||
Gfx *temp_v0_2;
|
||||
Gfx *temp_v0_3;
|
||||
Gfx *temp_v0_4;
|
||||
GraphicsContext *temp_a0;
|
||||
GraphicsContext *temp_s0;
|
||||
|
||||
sp36 = (s16) (s32) (f32) thisx->shape.rot.x;
|
||||
sp34 = (s16) (s32) (f32) thisx->shape.rot.z;
|
||||
temp_a0 = play->state.gfxCtx;
|
||||
temp_s0 = temp_a0;
|
||||
Gfx_SetupDL25_Opa(temp_a0);
|
||||
temp_v0 = temp_s0->polyOpa.p;
|
||||
temp_s0->polyOpa.p = temp_v0 + 8;
|
||||
temp_v0->words.w0 = 0xDA380003;
|
||||
sp28 = temp_v0;
|
||||
sp28->words.w1 = Matrix_NewMtx(play->state.gfxCtx);
|
||||
temp_v0_2 = temp_s0->polyOpa.p;
|
||||
temp_s0->polyOpa.p = temp_v0_2 + 8;
|
||||
temp_v0_2->words.w1 = (u32) &D_06000680;
|
||||
temp_v0_2->words.w0 = 0xDE000000;
|
||||
Matrix_RotateZYX(sp36, 0, sp34, 1);
|
||||
temp_v0_3 = temp_s0->polyOpa.p;
|
||||
temp_s0->polyOpa.p = temp_v0_3 + 8;
|
||||
temp_v0_3->words.w0 = 0xDA380003;
|
||||
sp20 = temp_v0_3;
|
||||
sp20->words.w1 = Matrix_NewMtx(play->state.gfxCtx);
|
||||
temp_v0_4 = temp_s0->polyOpa.p;
|
||||
temp_s0->polyOpa.p = temp_v0_4 + 8;
|
||||
temp_v0_4->words.w1 = (u32) &D_060007C8;
|
||||
temp_v0_4->words.w0 = 0xDE000000;
|
||||
}
|
||||
```
|
||||
|
||||
We can see there are four blocks here, although only two different macros:
|
||||
|
||||
```C
|
||||
temp_v0 = temp_s0->polyOpa.p;
|
||||
temp_s0->polyOpa.p = temp_v0 + 8;
|
||||
temp_v0->words.w0 = 0xDA380003;
|
||||
sp28 = temp_v0;
|
||||
sp28->words.w1 = Matrix_NewMtx(play->state.gfxCtx);
|
||||
```
|
||||
|
||||
gfxdis gives
|
||||
|
||||
```
|
||||
$ gfxdis.f3dex2 -x -g POLY_OPA_DISP++ -d DA38000312345678
|
||||
gSPMatrix(POLY_OPA_DISP++, 0x12345678, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
```
|
||||
|
||||
so it becomes
|
||||
|
||||
```C
|
||||
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
```
|
||||
|
||||
```C
|
||||
temp_v0_2 = temp_s0->polyOpa.p;
|
||||
temp_s0->polyOpa.p = temp_v0_2 + 8;
|
||||
temp_v0_2->words.w1 = (u32) &D_06000680;
|
||||
temp_v0_2->words.w0 = 0xDE000000;
|
||||
```
|
||||
|
||||
```
|
||||
$ gfxdis.f3dex2 -x -g POLY_OPA_DISP++ -d DE00000012345678
|
||||
gSPDisplayList(POLY_OPA_DISP++, 0x12345678);
|
||||
```
|
||||
|
||||
so this one is
|
||||
|
||||
```C
|
||||
gSPDisplayList(POLY_OPA_DISP++, D_06000680);
|
||||
```
|
||||
|
||||
```C
|
||||
temp_v0_3 = temp_s0->polyOpa.p;
|
||||
temp_s0->polyOpa.p = temp_v0_3 + 8;
|
||||
temp_v0_3->words.w0 = 0xDA380003;
|
||||
sp20 = temp_v0_3;
|
||||
sp20->words.w1 = Matrix_NewMtx(play->state.gfxCtx);
|
||||
```
|
||||
|
||||
This is the same as the first one. Indeed, it's identical.
|
||||
|
||||
```C
|
||||
temp_v0_4 = temp_s0->polyOpa.p;
|
||||
temp_s0->polyOpa.p = temp_v0_4 + 8;
|
||||
temp_v0_4->words.w1 = (u32) &D_060007C8;
|
||||
temp_v0_4->words.w0 = 0xDE000000;
|
||||
```
|
||||
|
||||
This is the same as the second one, but with a different second word.
|
||||
|
||||
Tidying up and inserting `OPEN_DISPS` and `CLOSE_DISPS`, we end up with
|
||||
|
||||
```C
|
||||
void ObjTree_Draw(Actor* thisx, PlayState* play) {
|
||||
s16 sp36 = (f32) thisx->shape.rot.x;
|
||||
s16 sp34 = (f32) thisx->shape.rot.z;
|
||||
|
||||
OPEN_DISPS(play->state.gfxCtx);
|
||||
|
||||
Gfx_SetupDL25_Opa(play->state.gfxCtx);
|
||||
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
gSPDisplayList(POLY_OPA_DISP++, D_06000680);
|
||||
|
||||
Matrix_RotateZYX(sp36, 0, sp34, MTXMODE_APPLY);
|
||||
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
gSPDisplayList(POLY_OPA_DISP++, D_060007C8);
|
||||
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
}
|
||||
```
|
||||
|
||||
## RGB macros and bitpacking
|
||||
|
||||
TODO: find some examples for this one.
|
||||
|
||||
For even more examples, you can consult [the OoT tutorial](https://github.com/zeldaret/oot/blob/main/docs/tutorial/draw_functions.md)
|
||||
|
||||
Next: [Data](data.md)
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 113 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 63 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.5 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user