Given the recent discovery (thanks @Mc-muffin) I deleted the `ABS` macro
(together with a few other unused ones) and started using `abs` instead.
This is not a simple Find & Replace. I also checked every single code
change and removed previously necessary temporary variables.
More PSP matches and a lot of fixes on types and symbols from the PSX
counterpart. I am very happy with the results so far.
`func_80173F74` has some weird `#ifdef VERSION_PSP` I cannot remove. Any
help will be very welcomed.
Matches `func_801713C8`.
This is an important milestone as it is the first function using the
`SEH` instruction. I got this new compiler (and many more) from someone
in ObscureGamers that kindly shared at
https://archive.org/download/compilers-no-license .
This still gives us no clue on what `f32` and the various `.i.hi` stuff
was actually meant to be.
Surprisingly, this uses an s16 for hitboxOffX, so it turns out we need
to adjust the Entity struct in Weapon. Hopefully that's the last of the
changes needed to Entity :)
These two functions are extremely similar so I am doing them in one PR.
Not sure what they actually do, the call to LoadImage is a bit
surprising. This is used for the Skull Shield and Shaman Shield; I don't
know what they have in common.
`func_107000_8017ADF8` is already in the repo and decompiled. I found
that there are several duplicates of this function, so this PR
decompiles all of them.
The only difference between the copies is 1. The address of the data
array, obviously and 2. the assignment to the `size` variable. Some
duplicates use 4 and 6, others use 2 and 3. Otherwise they're exactly
the same.
This is the only heavily-duplicated weapon helper function remaining. I
have decompiled all of the helper functions (the ones that are unique to
the weapon they're part of, rather than being in the standard structure
like `func_ptr_801700XX`) and will be doing PRs for each of them in
sequence.
Decompile SEL func_801B9B7C Mostly based on the work of @sonicdcer (who
had the right code, but wrong struct), and @ser-pounce (who had the
right struct, but unfinished code).
Co-authored-by: Jonathan Hohle <jon@ttkb.co>
OK. Here goes. Version 0.0.1 alpha of the MWCC Global Assembly Processor
(mwccgap). It's currently very simple/limited, but it appears to work
for `src/servant/tt_000/10E8.c`.
There is lot more that can be done to improve mwccgap - i.e. supporting
.rodata migration would be a good addition, but let's see how far we can
get with it in it's current state.
Note that the Makefile could do with some improvements - we don't nede
to use mwccgap for any C file that *dont* have INCLUDE_ASM macros (it's
a waste of time) so these could be ignored, i.e. for SSSV I do something
like this to find the files that need fixing up:
```
GLOBAL_ASM_C_FILES := $(shell $(GREP) GLOBAL_ASM $(SRC_DIR) </dev/null 2>/dev/null)
```
.. although this is perhaps too simple given that SOTN has a mix of PSP
and PSX functions (and therefore there may be INCLUDE_ASM for a PSX
function but none for PSP functions...
After decompiling `func_107000_8017B0AC`, I moved on to more weapon
helper functions, and was surprised to find several more copies of
nearly identical functions. The only difference between these is the
fractional constants applied to the outPoint results (for example, some
use 2/3, others use 3/4, and others still use 1/2).
Given the fact that these are all substantially the same, we could
probably do some de-duplicating by pulling in a header file, but for now
I'm decompiling each one in place, so that we can have all the functions
done.
I believe this is all of the ones that match this structure; hopefully I
didn't miss any!
This follows the model of
#[`collect_hearts.h`](../blob/master/src/st/collect_heart.h) to reuse
the `CollectHeartVessel` implementation across stages that use the same
implementation.
This adds implementations for CEN and WRP as well.
Co-authored-by: Jonathan Hohle <jon@ttkb.co>
This replaces several dozen BIOS trampolines from the extracted ASM to
"decompiled" source. These are modeled on the `INCLUDE_ASM` macro, but
generate the instructions necessary for each trampoline directly instead
of importing an extracted source file.
Because these trampolines never return, and GCC 2.6 doesnt appear to
have builtins for leaving off the return jump postamble, these will
likely need to remain assembly.
This also changes the `main.elf` target to depend on `main.ld`, and
undefined symbols files, allowing `make build` to regenerate those files
if necessary.
Co-authored-by: Jonathan Hohle <jon@ttkb.co>
Not really certain what this one does. Given that weapon 37 is for the
short sword and jewel sword, I wonder if it's related to the colorful
particles that come out when you swing the jewel sword. I'm sure we'll
learn more as we decompile the other functions in the overlay.
As noted in the comment, this is closely related to BottomCornerText,
which exists in the overlays. It shows the text boxes in the lower left
and lower right corners of the screen (an item you picked up, an enemy
you encountered, etc).
I'm not sure how this is triggered in relation to food (I'll see when I
decompile the caller), but there it is. The logic is weird since it
parses characters two at a time.
I tried to make a symbol definition for this function, but it didn't
work. Leaving my attempt in place to show what I did; I can remove it if
desired, or do whatever is needed to rename functions in the weapon
overlays. Anything works.
This is mostly just another weapon function.
However, because this references PLAYER.step, we want to use the
PlayerSteps enum. This enum is in `dra.h` and therefore inaccessible to
weapon functions.
I ran into this problem a while ago dealing with ric.h not having access
to the steps, and just duplicated PlayerSteps. Now that we have a third
place, we instead move PlayerSteps to `game.h`, and adjust `ric.h` to
only use the steps that we need to override. This seems like the most
sustainable solution.
This is the last of the weapons using func_ptr_80170014. It needs a
non-void return type in order to match, so we also had to change the
type for all the other instances so we don't get issues with declaration
mismatches.
In CUE files `FILE` is a stateful, global declaration that applies to
all following `TRACK`s. `sotn-disk` was treating `FILE` declarations as
`TRACK` delimiters which would result in incorrect parsing of CUE files
with a single `FILE`, but multiple tracks.
Now when reading a `FILE` declaration, the path is stored and processing
continues. A `TRACK` declaration will use the previously defined path,
and if a previous track had been started, append that previous track to
the list.
This also builds `sotn-disk` using the local repository instead of
pulling the latest commit from GitHub. The target depends on `sotn-disk`
sources and will rebuild as necessary (or with `make
~/go/bin/sotn-disk`).
As an aside,
[pull/232](https://github.com/Xeeynamo/sotn-decomp/pull/232) ran into
this same error, but fixes in a slightly different way that leaves side
effects that may make supporting things like `INDEX` (for extracting the
placeholder audio, for example) more error prone in the future.
Co-authored-by: Jonathan Hohle <jon@ttkb.co>
I'm working on tracing the logic involved in using weapons.
`func_8010EDB8` is a core part of that process.
I noticed a small improvement that could be made to the control flow,
removing one `goto` statement.
This function could use more work on its overall control flow (there are
so many uses of `goto`), but even a small improvement is still an
improvement.
While tracking the calling of the weapon functions, I made a neat
discovery.
func_8011A4D0 is a crucial function at the heart of the game's engine.
For every entity which is created by its ID, this function assigns the
PfnEntityUpdate function which will run each frame for that entity.
If the ID is E0-EC, or F0-FC, it reads a function from a pointer to
call. It turns out that the array we were indexing into before is not a
real array, because of course it does not have elements below E0. When
you take the minimum offsets into consideration, it turns out that these
arrays are located precisely where the two weapons (in the player's two
hands) are located in memory.
Because D_8017A000 and D_8017D000 are Weapon objects, we can't index
into them as an array, so I had to do a bit of an ugly cast to a
`PfnEntityUpdate*`, unfortunately I don't know a way to do this under
our 80-character limit.
Neat to discover fake symbols and clean them up, as well as to make it
obvious that these two lines are dealing directly with the weapon
functions.
My last PR was for weapon 15, this is the same function for weapon 49.
Substantially similar; interesting that this one uses a switch on the
self->step while the other one just had an `if step == 0`.
Otherwise nothing much of interest here. Going to continue to leave
things as externs until each weapon is done.
There are only a few weapons that make use of the func_ptr_80170014
function, and this is the first of them.
I don't really know how to pull in data for functions, so keeping these
as externs for now. Particularly, I can't find where in the assembly the
bytes of D_6D000_8017A2B0 exist. They don't appear to be in
asm/us/weapon/data.
We identify a couple members of the `ET_Weapon` struct, and since
they're a change to velocity, I named them acceleration. This breaks
weapon 29's code (it uses different datatypes at offset 0x9C), but since
there's already an ET_Weapon29 dedicated alternative entity, we can
continue to use that instead.
Been a while since I've done a PR, nice to pick it back up, hope this is
good :)
Co-authored-by: sozud <122322823+sozud@users.noreply.github.com>
Having `platform: psx` will not correctly disassemble PSP functions with
opcodes that are not part of the R3000 (the PSX CPU) architecture. These
functions would be disassembled by Splat as a bunch of `.word
0xBLAHBLAH`.
Changing the platform to `psp` introduces all sort of new challenges.
Function prototypes needs to be declared earlier. But also the MWCC
compiler will not accept the `%lo` and `%hi` dialect from GNU AS. There
were some patches on `mwcpp.py` to use `la SYMBOL_NAME` that would
expand into a `lui / addiu` combo. But even though symbols needs to be
declared like function prototypes at the top of the file. This is simply
not feasible on bigger overlays.
As a solution to the problem above, I replaced the existing patches by
converting instructions into `.word`. The overlay cannot longer be
relocated with this approach, but it is not an issue as the final goal
is to decompile these functions any way.
The labels in the jump table has the same problem, which forced me to
change the segment type from `rodata` to `data.
This is just another single step to create the conditions to start
including bigger re-compilable PSP overlays. I am sure the `mwcpp.py`
solution will be thrown into the bin at some point, but this PR improves
our current situation.
I improved the PSP Makefile to always remove `*.post.c` regardless if
the compiler fails or succeeds. This also helps when invoking `differ`
on a broken build.
The permuter uses the last Makefile line to create a `compiler.sh`
script used to make a bunch of iterations. Previously it was using `rm
$<.post.c || true` as compiler. Now that I compacted everything into a
single line it can at least import a C file. The produced `compiler.sh`
is still wrong as it cannot properly take in consideration the
`mwcpp.py` tool and the parameters to pass to it.
---
Before this fix:
```bash
$ ./import.py sotn-decomp/src/servant/tt_000/10E8.c sotn-decomp/asm/pspeu/servant/tt_000/nonmatchings/10E8/func_80171ED4.s
Compiler type: gcc
Function name: func_80171ED4
Compiler: python3 tools/mwcpp.py {input} -o {output}
Assembler: mips-linux-gnu-as -march=vr4300 -mabi=32 {input} -o {output}
src/servant/tt_000/10E8.c:1:10: fatal error: servant.h: No such file or directory
1 | #include <servant.h>
| ^~~~~~~~~~~
compilation terminated.
Failed to preprocess input file, when running command:
cpp -P -undef src/servant/tt_000/10E8.c -D__sgi -D_LANGUAGE_C -DNON_MATCHING -DNONMATCHING -DPERMUTER '-D_Static_assert(x, y)=' '-D__attribute__(x)=' '-DGLOBAL_ASM(...)=' '-D__asm__(...)='
```
With this fix:
```bash
$ ./import.py sotn-decomp/src/servant/tt_000/10E8.c sotn-decomp/asm/pspeu/servant/tt_000/nonmatchings/10E8/func_80171ED4.s
Compiler type: gcc
Function name: func_80171ED4
Compiler: python3 tools/mwcpp.py '&&' '((MWCIncludes=bin/' bin/wibo bin/mwccpsp.exe -gccinc -Iinclude -D_internal_version_pspeu -O0 -c -lang c -sdatathreshold 0 src/servant/tt_000/10E8.c.post.c '&&' rm 'src/servant/tt_000/10E8.c.post.c)' '||' '(rm' src/servant/tt_000/10E8.c.post.c '&&' exit '1))' {input} -o {output}
Assembler: mips-linux-gnu-as -march=vr4300 -mabi=32 {input} -o {output}
In file included from include/game.h:78,
from include/servant.h:4,
from src/servant/tt_000/10E8.c:1:
include/primitive.h:146:8: warning: extra tokens at end of #endif directive [-Wendif-labels]
146 | #endif PRIMITIVE_H
| ^~~~~~~~~~~
Preserving no macros. Use --preserve-macros='<regex>' to override.
usage: mwcpp.py [-h] [--version VERSION] [-o OUTPUT] input
mwcpp.py: error: unrecognized arguments: ((MWCIncludes=bin/ bin/wibo bin/mwccpsp.exe -gccinc -Iinclude -D_internal_version_pspeu -O0 -c -lang c -sdatathreshold 0 src/servant/tt_000/10E8.c.post.c && rm src/servant/tt_000/10E8.c.post.c) || (rm src/servant/tt_000/10E8.c.post.c && exit 1)) /tmp/permutereepb8_ih.c
Warning: failed to compile .c file.
```
This improves the function readability from the already decompiled PSX
functions. Since the PSP build is compiled with `-O0`, the statement
order is much clearer. Even `if (entity->facingLeft != 0)` and `if
(!entity->facingLeft)` produces different output on the PSP build. I was
able to remove a couple of temp variables too.
---
There are a few functions I cannot match due to a trailing `nop` at the
end of the function. It looks like the overlay have functions aligned by
`8`. I do not know how to trigger that using MWCC or if it is a linker
thing. Two functions that have this problem are `DestroyEntity` and
`func_801746A0`.
![image](https://github.com/Xeeynamo/sotn-decomp/assets/6128729/754fe1ef-5d2d-4b86-a3fb-736a8058347d)
TT_000 is the first overlay from PlayStation 1 that we are now able to
compile from the source and produce a 1:1 binary. This lead me to start
exploring the same overlay from the game Castlevania: Dracula X
Chronicles, which contains a PSP re-build of Symphony of the Night.
This PR adds all the infrastructure to add the same flow for a PSP
matching decomp. Use `export VERSION=pspeu` and then the usual `sotn`
command to splat the overlay, build it and check if it matches. Running
`make extract_disk` should not be necessary as the same ISO used from
`VERSION=hd` is also used for `pspeu`, so you will probably have it
extracted already.
Some important notes about the PSP build:
* The whole PSP build seems to be compiled with `-O0`, which makes it
much easier to decompile
* Having ŧhe PSX, PSP and Saturn builds will allow to easily
cross-reference the code and reduce fake matches
* `disks/pspeu/PSP_GAME/USRDIR/res/ps/hdbin/tt_000.bin` is the HD PSX
build
* `disks/pspeu/PSP_GAME/USRDIR/res/ps/PSPBIN/tt_000.bin` has the same
code from the HD build, but for PSP
* `disks/pspeu/PSP_GAME/USRDIR/res/ps/PACK_E/TP00.BIN` is the same as
above, but it packs both overlay and graphics. This is the file the PSP
game seems to actually use
* The PSP build uses the Metrowerk CodeWarrior's C compiler, which is
very different from the GCC one used on PSX.
* Thanks to @mkst lead, we found a way to still use the GNU assembler
and linker
* MWCC uses [wibo](https://github.com/decompals/WiBo/), a think
compatibility layer to run Windows CLI tools on Linux. It is much more
lightweight than Wine.
* MWCC does not support the `INCLUDE_ASM` dialect, so the custom
pre-processor `tools/mwcpp` had to be created
* The exact MWCC compiler version is unknown, but I suspect it is `build
147`
* I am not yet sure of any implications for using GNU AS and GNU LD
instead of the MW correspondent tools
* Not all the functions can be correctly disassembled, spimdisasm will
just produce a bunch of `.word 0x________` due to the in-progress effort
of adding the Allegrex-specific opcodes support
---
TO-DO list before marking the PR as ready:
- [X] Add PSP build to the CI
- [x] Add progress reporting to the PSP build
- [x] Integrate source file from `src/servant/tt_000_psp` to
`src/servant/tt_000` to promote the psp build as first-citizen
---
TO-DO in a follow-up PR:
* Figure out what `header` is: can we extract it as assembly code? or
maybe as custom re-compilable asset via splat? Is it a MW stuff or a
Castlevania-specific file?
* Get rid of the last line in `Makefile.psp.mk`
100% decompiled.
`US` adds some padding to have the file exactly 40KB long. The HD
version does not add any padding, hence why I ended up producing
`tt_000_raw.bin`. I would have used just `tt_000.bin` as a name as found
in `disks/pspeu/PSP_GAME/USRDIR/res/ps/hdbin/tt_000.bin`, but on
Windows-based file system it would collide with `TT_000.BIN` due to the
OS having case insensitive names.
I modified `make clean` as I found annoying that `VERSION=hd make clean`
would wipe out `us` build stuff.
The only different function in HD is `ProcessEvent`, which has a weaker
check. Another hint suggesting HD being older than US.
`s32 _unused[26];` added enough padding in the bss section to get an
🆗 . I am pretty sure it is unused data because the final binary is
not aligned by any power of 2.
While attempting to trace the process of an action in the game, I
noticed that the main state machine in EntityAlucard which dispatches
the functions for each player state was still using numerical `case`
statements. I cross-referenced the `PlayerSteps` enum in `dra.h` to the
steps in the function, and swapped in all the enum names. I also gave
proper names to unk24 and unk25, deconflicting this in the Alucard and
Richter player step lists.
Finally, I noticed that EntityAlucard has routines for steps 48-50,
which did not have enum members, so I added those. We'll have to examine
what they do.
Due to the discovery of the 0x100 entity flag in EntityDraculaFinalForm,
I have gone ahead and created the #define for 0x100 being FLAG_DEAD. I
have also searched the codebase for the string `flags & 0x100` and
`flags |= 0x100` and made suitable replacements. There are likely other
places that use the 0x100 flag which use different syntax, and those are
not changed, but hopefully we can clean those up over time as we
discover them.
Nice to expand our knowledge of the different available flags!
Big thanks to @mkst and @Xeeynamo for looking into my rodata issue on
this one! Still not sure why it worked the way it did but glad we got it
working and we can now get this function in.