Retro
This project is born to fix a bug in Crash Bash that caused a character to receive two consecutive hits when hit by a physical attack while holding a box. In particular the character would first get hit by the physical attack and successively by the box it was holding, when it crashes to the ground. An example of the bug is shown at reference [4].
When I download or dump the ROM, I have the .bin and .cue files. By using binwalk on the .bin file I can see that it contains MIPS executable code, so I understand PS1 games use MIPS instructions set.
binwalk -Y crashbash.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
2317 0x90D MIPS executable code, 32/64-bit, little endian, at least 527 valid instruct'
I try to extract the content of the bin file and I obtain a .iso file:
binwalk -e crashbash.bin
file _crashbash.bin.extracted/1318.iso
_crashbash.bin.extracted/1318.iso: ISO 9660 CD-ROM filesystem data 'SCES_02834'
and by executing the file
command on it i see that it contains an ISO 9660 CD-ROM filesystem data related to 'SCES_02834' that is, indeed, Crash Bash (PAL version) serial number.
The .iso file (and the .bin too) results to be corrupted and cannot be opened with the standard Windows explorer. By using IsoBuster
I've been able to see the structure of the disk and extract many files. Some sections result to be corrupted though, and I have ignored them,and these corrupted section seem to be related to Spyro3 Demo files and in particular to SPEECH.STR file.
So on IsoBuster documentation[1] I look at the different extraction modes and the correct one seems to be Extract RAW, that luckily seem to only work on CDs, and is recommended for engineering puposes.
So I extract 2 copies: one for RAW and the other for User understandable data (it is important to notice though, that all the work and modifications have been done on the user understandable copy); the structure of the extracted folders is the same for both, but the actual content will be different due to the uninteligible data not being discarded in RAW extraction:
The folder CRASHBSH contains:
- CRASHBSH.DAT
And the folder SPYRO3:
-
SPEECH.STR
-
SPYRO3.EXE
-
WAD.WAD
retro@retro:/mnt/c/Users/lmong/Desktop/Progetti/Ricerca/PS1_games_reverse_engineering/crash_bash/SCES_02834$ file * */*
BASHY: data
SCES_028.34: Sony Playstation executable PC=0x8002e4bc, .text=[0x80010000,0x6a800], Stack=0x801ffff0, (North America area)
SYSTEM.CNF: ASCII text, with CRLF line terminators
CRASHBSH/CRASHBSH.DAT: data
SPYRO3/SPEECH.STR: empty
SPYRO3/SPYRO3.EXE: Sony Playstation executable PC=0x8005af6c, .text=[0x80010000,0x5c000], Stack=0x801ffff0, (North America area)
SPYRO3/WAD.WAD: xBase index, root pointer 0x800, free node pointer 0x20000, reserved counter 0x20800, key length 0, index options (0), at 16 reserved 0x20800, at 492 reserved 0xf6d80000000000## REFERENCES
Executing the file
command on the files in the RAW folders will only return data as result. On the contrary, as said before, in the human readable extracted folder, the file
SPEECH.STR
is shown as empty.
I can suppose that CRASHBSH/CRASHBSH.DAT may contain textures and audio track, but these may be compressed. BASHY instead is totally unclear to me: May it contain functions or properties of the objects? or else vice versa?
I can say that the first option is the best because by running strings
on the two files I get many known in game strings on CRASHBSH/CRASHBSH.DAT while I don't get anything more than "HpSt." on BASHY. Moreover running binwalk
on the first file returns the following:
retro@retro:/mnt/c/Users/lmong/Desktop/Progetti/Ricerca/PS1_games_reverse_engineering/crash_bash$ binwalk -B SCES_02834_RAW/CRASHBSH/CRASHBSH.DAT
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
221196 0x3600C mcrypt 2.2 encrypted data, algorithm: blowfish-448, mode: CBC, keymode: 8bit
4738508 0x484DCC LZMA compressed data, properties: 0xBE, dictionary size: 16777216 bytes, uncompressed size: 32 bytes
4738704 0x484E90 LZMA compressed data, properties: 0xBE, dictionary size: 50331648 bytes, uncompressed size: 32 bytes
8126692 0x7C00E4 LZMA compressed data, properties: 0x5A, dictionary size: 16777216 bytes, uncompressed size: 32 bytes
10055787 0x99706B StuffIt Deluxe Segment (data): ffffffffffffffV5""""""RV#""""""2effffffffffffV#""""""2ef6#""""""2effffffffffV#""""""2cfffV#"""""2Sffffffffff5#"""""2efffff:"""""
10643616 0xA268A0 LZMA compressed data, properties: 0x65, dictionary size: 134217728 bytes, uncompressed size: 32 bytes
10837000 0xA55C08 MySQL ISAM index file Version 1
11901944 0xB59BF8 LZMA compressed data, properties: 0x66, dictionary size: 201326592 bytes, uncompressed size: 32 bytes
13074392 0xC77FD8 LZMA compressed data, properties: 0x66, dictionary size: 67108864 bytes, uncompressed size: 32 bytes
14047230 0xD657FE PGP RSA encrypted session key - keyid: 79E6 4E010C03 RSA (Encrypt or Sign) 8129b
14047278 0xD6582E PGP RSA encrypted session key - keyid: 70ED 5050C03 RSA (Encrypt or Sign) 8129b
14049246 0xD65FDE PGP RSA encrypted session key - keyid: 79E6 4E010C03 RSA (Encrypt or Sign) 8129b
14049294 0xD6600E PGP RSA encrypted session key - keyid: CFE8 47060C03 RSA (Encrypt or Sign) 8129b
14476699 0xDCE59B MySQL MISAM index file Version 1
14837502 0xE266FE MySQL MISAM compressed data file Version 2
14947794 0xE415D2 MySQL ISAM compressed data file Version 9
14948719 0xE4196F MySQL MISAM index file Version 7
14956740 0xE438C4 MySQL ISAM index file Version 9
14975644 0xE4829C MySQL MISAM index file Version 3
14975824 0xE48350 MySQL MISAM index file Version 3
14985090 0xE4A782 MySQL MISAM compressed data file Version 5
15872295 0xF23127 LZMA compressed data, properties: 0x5C, dictionary size: 0 bytes, uncompressed size: 1024 bytes
17150848 0x105B380 LANCOM WWAN firmware
21993837 0x14F996D MySQL MISAM index file Version 3
30073769 0x1CAE3A9 MySQL MISAM index file Version 1
34641353 0x21095C9 MySQL MISAM index file Version 1
38952086 0x2525C96 MySQL MISAM index file Version 3
44649290 0x2A94B4A MySQL MISAM index file Version 1
70901968 0x439E0D0 MySQL ISAM index file Version 3
80492161 0x4CC3681 StuffIt Deluxe Segment (data): fG5
81490733 0x4DB732D Intel x86 or x64 microcode, pf_mask 0x3311fe04, 1FE2-10-10, rev 0x419c6210, size 32510705
82209841 0x4E66C31 StuffIt Deluxe Segment (data): fG5
83289580 0x4F6E5EC StuffIt Deluxe Segment (data): ff#
83722348 0x4FD806C StuffIt Deluxe Segment (data): ff#
84025756 0x502219C StuffIt Deluxe Segment (data): ff#
84639628 0x50B7F8C StuffIt Deluxe Segment (data): ff#
84898348 0x50F722C StuffIt Deluxe Segment (data): ff#
85748659 0x51C6BB3 mcrypt 2.2 encrypted data, algorithm: blowfish-448, mode: CBC, keymode: 8bit
86021239 0x5209477 mcrypt 2.2 encrypted data, algorithm: blowfish-448, mode: CBC, keymode: 8bit
86586035 0x52932B3 mcrypt 2.2 encrypted data, algorithm: blowfish-448, mode: CBC, keymode: 8bit
87331455 0x534927F mcrypt 2.2 encrypted data, algorithm: blowfish-448, mode: CBC, keymode: 8bit
87579913 0x5385D09 Toshiba SSD Firmware Update
88012407 0x53EF677 mcrypt 2.2 encrypted data, algorithm: blowfish-448, mode: CBC, keymode: 8bit
88298116 0x5435284 StuffIt Deluxe Segment (data): ffwwwwwwwwwwwwTfffwwwwwwwwwwwweffwwwwwwwwwwwwwfffvwwwwwwwwwwwweffffVvwwwwwwwww3efET4dvwwwwwwwwDDEBUDUfwwwwwwwwUUDUTUUfvwwwwwwwfE
88658199 0x548D117 mcrypt 2.2 encrypted data, algorithm: blowfish-448, mode: CBC, keymode: 8bit
89835196 0x55AC6BC StuffIt Deluxe Segment (data): ff#
90093916 0x55EB95C StuffIt Deluxe Segment (data): ff#
91157345 0x56EF361 MySQL MISAM index file Version 1
91435297 0x5733121 MySQL MISAM index file Version 1
91626097 0x5761A71 MySQL MISAM index file Version 1
91815985 0x5790031 MySQL MISAM index file Version 1
92015905 0x57C0D21 MySQL MISAM index file Version 1
92221153 0x57F2EE1 MySQL MISAM index file Version 1
92992200 0x58AF2C8 MySQL MISAM index file Version 1
92992280 0x58AF318 MySQL MISAM index file Version 1
93969647 0x599DCEF MySQL MISAM index file Version 1
94264628 0x59E5D34 MySQL MISAM index file Version 1
94264708 0x59E5D84 MySQL MISAM index file Version 1
95305948 0x5AE40DC StuffIt Deluxe Segment (data): ff#
95658748 0x5B3A2FC StuffIt Deluxe Segment (data): ff#
96804172 0x5C51D4C StuffIt Deluxe Segment (data): ff#
97307500 0x5CCCB6C StuffIt Deluxe Segment (data): ff#
97994284 0x5D7462C StuffIt Deluxe Segment (data): ff#
98370604 0x5DD042C StuffIt Deluxe Segment (data): ff#
98699884 0x5E20A6C StuffIt Deluxe Segment (data): ff#
100527123 0x5FDEC13 Intel x86 or x64 microcode, sig 0x2cfc8e00, pf_mask 0x1022100, 1E00-01-01, rev 0x4fc8e00, size 139521
105852888 0x64F2FD8 StuffIt Deluxe Segment (data): ffwwwwwwwwwwwwTfffwwwwwwwwwwwweffwwwwwwwwwwwwwfffvwwwwwwwwwwwweffffVvwwwwwwwww3efET4dvwwwwwwwwDDEBUDUfwwwwwwwwUUDUTUUfvwwwwwwwfE
106334713 0x65689F9 Toshiba SSD Firmware Update
106544304 0x659BCB0 StuffIt Deluxe Segment (data): ffwwwwwwwwwwwwTfffwwwwwwwwwwwweffwwwwwwwwwwwwwfffvwwwwwwwwwwwweffffVvwwwwwwwww3efET4dvwwwwwwwwDDEBUDUfwwwwwwwwUUDUTUUfvwwwwwwwfE
108309798 0x674AD26 Sega MegaDrive/Genesis raw ROM dump, Name: "NY COMPUTER ENTE", "I MATSUSHITA",
108999292 0x67F327C StuffIt Deluxe Segment (data): ff#
110514356 0x69650B4 Intel x86 or x64 microcode, pf_mask 0x229f2220, 2000-02-22, size 2048
114900714 0x6D93EEA MySQL MISAM compressed data file Version 1
115590806 0x6E3C696 MySQL ISAM compressed data file Version 3
117642960 0x70316D0 MySQL MISAM index file Version 1
117642966 0x70316D6 MySQL MISAM index file Version 1
118481406 0x70FE1FE MySQL ISAM index file Version 1
121258348 0x73A416C MySQL ISAM index file Version 1
It may not seem a realistic result but something gave me an important detail is the line:
108309798 0x674AD26 Sega MegaDrive/Genesis raw ROM dump, Name: "NY COMPUTER ENTE", "I MATSUSHITA"
since for Crash Bash and Crash Team Racing, Sony Computer Entertainment made a deal with Universal Interactive Studios that at that time was an asset of a japanese company, MATSUSHITA (Today known as Panasonic). All the other files seem to be compressed though, and it is not granted that all the results are affordable.
This does not seem to be the right way to follow since the file are corrupted or may be compressed.
Definitely:
SYSTEM.CNF
contains game boot information
SCES_028.34
is the main executable of the game with Program Counter=0x8002e4bc (address of the start function), text section .text=[0x80010000,0x6a800] and Stack Pointer at Stack=0x801ffff0
CRASHBSH/CRASHBSH.DAT
may be a file containing assets that may be compressed, and in that case it is necessary to find the compression algorithm in the main executable
BASHY
still unknown file
To first load, disassemble and try to decompile the game I've used Ghidra by installing the extension psx-ldr[2]. In particulare to grant the compatibility with the extension I've had to download the precise version PUBLIC 10.4 of both Ghidra and the extension. I just had to install the extension by importing the zip and not its content.
After first making a mistake by loading the entire .bin file, that was just a little distraction, I've first tried to load the file SCES_028.34
contained in the folder extracted in a RAW form. I've noticed that it returned many errors, so there probably were many bytes out of place.
Finally I've tried to open the same file but taken from the folder which contained the files converted to "user understandable" files and obtained "understandable" disassembled MIPS code and a way more clear decompiled code and had a confirm that the PC=8002e4bc is the address of the first function, start.
Then it was time for the experiments. I wanted an emulator to execute the game and analyze the memory to search for some values. The addresses that I was looking for are player 1's life bar and the invincibility. For this purpose I've decided to use DuckStation: epsxe didn't work at all at the last version, while the previous ones didn't load the BIOS as requested.
DuckStation provides a memory analyser with which I've been able to understant that the life bar ranges between 0 (minimum) and 20 (maximum), while the invulnerability has two addresses, one is the invulnerability state flag and the other is the timer.
The timer ranges between 0x28-0 (ms?) and the flags are:
-
0: normal state
-
2: blinking character and invincible
Following, the addresses I've found:
The first is the life bar, the second the number of won matches, the third the invincibility timer, and finally the invincibility flag.
The addresses don't seem to be RANDOM
Another set of addressess is the following, defining if the character is carrying a box or not (all values will be 0)
The only fixed addresses are 9ba8e
and 9ba8f
At this point it may seem trivial to find the function or the instruction that sets the value 2 at the address 0x0009baaf. That's wrong.
At first I've tried with No$psx: I've set the breakpoint as follows [0009baaf]!
so that the execution could stop the execution in the moment in which the value stored at that specific byte would have changed. But it didn't work.
So I've changed the emulator to pcsx-redux
and did the same, by setting the breakpoint for that address for any write. But once the match was loaded I've noticed that the breakpoint was hit continuously. So I could suppose that that address is part of a larger section of memory that is continuously read and written and traces the values of the battle. BUT this implies that the value 0x2
is being saved somewhere else before being written in this specific address (stack or registry?)
At this point it seems that I must proceed statically, but analysing all the code is undoable. So I will search instructions that move or add the value 2 to a register or a memory address. I need to filter out as many instructions as possible. I can do it with a regexp so that I can filter out all the operations that work on the stack pointer:
/(mov[a-z]*|add[a-z]*)\s(((?!sp).)*),(((?!sp).)*),0x2$/gm
and if necessary I can only work in the range of addresses of instruction executed by debugger during a single cycle.
Plot twist: I forgot about THE SECOND ADDRESS.
-
0x0009baaf: can be set on 0 or 2, if you're invincible or not
-
0x0009baac: this sets a timer
But while the first one seems to be continuously written even if the state of the character does not change, the second one is only reached and written when the character is hit and when the timer decreases.
These are the points in which the breakpoint is hit:
-
8008558c: The character has already been hit, and is on the ground and the timer seems to be set to 0x28=40 (it is correct indeed, it is set to 0x28 at 80085554)
-
800856f4: the character is still on the ground and the timer is started and cyclically (notice the addiu $v0, -0x0001)
IN ORDER TO GET A CONFIRMATION: I've tried both the NTSC-U and NTSC-J versions to exclude that the reason for the "bug" could be the FPS difference from the PAL version and the bug happens on all the versions. The doubt rose in this moment because it could have been possible that the counter had different TIME durations based on the execution speed on the different versions, so the 0x28 didn't represent the exact time duration in the PAL and NTSCs. But at this moment I can completely exclude that the bug depends from this. I can confirm this thanks to the emulators that actually execute the PAL game at 50 FPS and the NTSC at 60 FPS.
Now, if I try to search those addresses in Ghidra I get a disappointing result:
In fact, we are in the RAM section and it is not possible to statically know what the content of these section will be.
Two alternatives
-
PCSX-REDUX should give the possibility to export the memory at runtime, so I can try to do that and load it on ghidra again?
-
Search for any function that loads something in one of the RAM address that I've found before.
The first option allows to find the function faster, just by loading the game, dumping the memory and finally studying the three functions.
The second one requires some extra steps:
-
using the address of one of the three commands and write breakpoint to check when the istruction is written to a specific address.
-
When the addess is written check for the command that has been executed and find where the function is actually stored.
The pro of the second metodology is that it is possible to understand and modify the game definitively while the first only allows to understand the function, avoiding the extra steps to find the storage of the function.
For completeness I'll proceed using the longer method (the second).
OR (many years have passed...) I can just search the particular instruction I've found at those addresses on ghidra.
The possible methods to search the instruction is by writing them literally or by searching their opcodes. Remember that it is necessary to search the opcodes in little endian on GHIDRA, so using the least relevant byte first (search the opcode found on pcsx-redux debugger, but reversed)
I need to load the CRASHBSH.DAT file because it could be there. -> There is nothing that is immediately recognizable.
I've finally opened the file in Hexedit[3] and I've searched for the opcode of the operation that sets the invulnerability timer to 0x28 (24020028) but in little endian so 28000224 and it hasn't found anything. So I've opened the file CRASHBSH/CRASHBSH.DAT and searched it there. The next step has been adding the last byte of the next opcode, to find two consequent operations, so 2800022404 (from the successive opcode 8e820004). And finally the magic has happened:
Have you noticed anything?
Right, the opcodes belong to the function related to the setting of the invulnerability timer.
Now we have:
-
The RAM address of the interesting functions: 0x80085554 (we take as example li $v0, 0x0028$)
-
The address in CRASHBSH.DAT of the same instruction: 0x068e58bc
-
The starting address of the commands loaded in RAM on the emulator PCSX-Redux: 0x80010000
What should this numbers be useful for? What we need now is the address of the start of the code in the file CRASHBSH.DAT file since it also contains other assets than code. This address will allow to convert the bytes to actual MIPS code on the Disassembler.
The calculation I've done is:
80085554 - 80010000 = 75554 #This is the offset of the instruction
068E58bc - 75554 = 6870368 #This should be the actual address on the file.
So the code will probably start at 0x6870368. Let's check:
It doesn't seem to be correct.
But this could have been correct only supposing that the code is all loaded like one single block in RAM from CRASHBSH.DAT.
By scrolling backwards manually and doing some intermediate checks, the last (first) point in which there is a match between the emulator and the file to disassemble is at the addresses 0x8007a498 (emulator) and 0x068da800 (disassembler).
Similarities, on the other hand, end at the addresses 0x80097acc (emulator) and 0x68f7e34 (disassembler).
and by using ghidra to convert this section to code, the disassembled instruction remain the same until the addresses 0x80093e08 (emulator) and 0x68f4170 (disassembler):
Well, now we have all the useful code to permanently modify the game's values.
Note: the offset between the emulator address and the disassembly address is 0x7979fc98.
Now is time to work dynamically in order to understand how the process of damage, hit and invincibility work. Once I understand it, I can modify it perpetually.
Let's take as example the function at address 0x800856f0 (timer decrease by one) and in particular the moment in which it is executed when writing on 0x0009baac (Player one's damage timer):
addiu $v0, -0x1
Notice that if we put the Exec breakpoint on the function address stated above, it would be hit for every char. So i can suppose that it is called using as argument the address of the timer of the character that is being hit. Considering this, we should see somewhere the point in which the particular address of the timer for the player one is passed as argument.
I can notice that the value of the timer is first calculated and then stored at a referenced address+0x44. This means that there are chunks of bytes that keep all the information about each character. In particular these are the beginning of the chunks for each player:
-
Player one: 0009ba68
-
Player two: 0009bad4
-
Player three: 0009bb40
-
Player four: 0009bbac
The difference between the starting point of one chunk and the other is 0x6c.
When I put a READ breakpoint at 0x0009baae (address of the value related to the invincibility) I notice that at 0x0009bab0 and 0x0009bab2 there is a timer of 0x19 related to the animation in which the character falls and gets up, at the end of which the actual timer begins... SO?
WHEN THIS SECOND TIMER IS GREATER THAN 0 THE TIMER OF THE INVINCIBILITY IS STILL 0. I CAN SUPPOSE THAT WHEN YOU GET HIT A SECOND TIME WHEN THE INVINCIBILITY TIMER IS STILL 0 THERE IS A CHANCE THAT THE CHARACTER CAN GET TWO CONSECUTIVE DAMAGES.
Now just a little graphic explanation, and the interesting numbers are highlighted. CONTEXT: I'm about to get hit:
-
0x0009baac: Invincibility timer is still 0
-
0x0009baae: Invincibility value, that I've later discovered it takes two bytes so it's not just 2, but 0x200, being it little endian.
-
0x0009bab0: A timer for the falling animation
-
0x0009bab2: as above.
Notice that the memory analyzer in the picture uses addresses starting with 0x800, even if they're effectively referenced with addresses starting with 0x000. I'll use this notation to distinguish addresses of the code from the content of the memory.
Well. I guess it's time to check who writes on those addresses, and above all to check if there is a frame in which both these timers, and the invincibility timer are equal to 0, while switching from one to another, because that may be the moment in which the studied bug happens. And effectively this happens:
-
The portion of memory when the animation of the fall is about to happen:
-
The animation is finished; one timer is equal to zero, while the second is equal to one (one is faster than the other?)
Does the bug happen again if we change the animation timer to a slightly longer time?
To know this, we have to check the functions that write to those addresses.
With breakpoint at 0009bab0:
With breakpoint at 0009bab2:
First thing to be noticed: The timer at 0x0009bab2 is initialized before the one at 0009bab0, but it is updated by subtracting 1 AFTER the other one.
Second thing to be noticed, as seen before: the invincibility timer (0x0009baac) is initialized to 0x28 when the timer at 0x0009bab0 is equal to 0, while 0x0009bab2 is equal to 1.
But by playing the game a little more there is also a THIRD FUNDAMENTAL THING to notice. The timer at 0x0009bab2 is not set, nor it works when i get a physical attack.
Indeed... No timer at 0x0009bab2, while it should be the first to be set.
My idea now is to edit the offset of the functions at 0x800bf0c4 and 0x80085538 to write at offset 0x4a (two bytes forward) instead of the other one.
For some reason the function at 0x800bf0c4 is not located where i expected in the file CRASHBSH.DAT (address-0x7979fc98) but at address 0x05A77708. I'll modify the instruction at this address in the file together with instructions at addresses 0x068E5890 and 0x068E58a0
Clearly the problem is not solved, but on the other hand we obtained some changes in game: now the bug is overly exaggerated, switching from a little chance to happen to a thing that always happens (all the characters can get hit by two boxes consecutively).
I should find a way to only add the command to write on the address 0x0009bab2 without touching the other timer. It seems that when a physical attack arrives, the function to set the second timer at 0x0009bab2 is not even called.
I think about a thing then:
This is a picture from before: I can see that the function to set the invisibility timer (0x80085554) is called after the animation timer reaches 0. May I stop the timer when it reaches another immediate small value near to zero (e.g. 1,2,3...)
I take the clean CRASHBSH.DAT file and try to do this change at the address 0x068E58A8:
-
0x34010001
→li $at, 1
-
0x54210012
→bne $v0, $at, 12th successive instruction
But the game got stuck whenever a character got it.
Now I'll just edit a little the values at 0x68ef284 and 0x5a77708.
An higher value than 0x19 makes it happen more often because the animation timer also sets a period of time in which the character cannot move and this happens for both the timers. A lower value instead makes the bug happen "before than normally".
So i will now try to fine tune it decreasing the value by one, starting from 0x18 until i see the bug doesn't happen anymore.
Didn't work. I must find another way.
Set the invincibility timer (offset 0x44) as additional timer together with the animation timer. I have to be careful about the instruction at 800bf0bc; I edit it too so that it jumps 3 instructions instead of two, so that I can steal the NOP to replace it with a more useful instruction:
Then i notice that that address of the invincibility time is set to 0 during the animation at 0x800854d0 (0x068E5838). I'll switch it to a NOP:
And finally make the timers decrease together (0x068E58A4):
It does not work because when a physical attack occurs, 0x0009baaf remains 0.
So It is not necessary to decrease the timer but i must put the 2 in memory.
To do so I need to load a value in a register and then store it to the desired address.
addiu $at, $zero, 2 |
24010002 |
---|---|
sh $at, 0x47($s0) |
A4410047 |
By playing a little more, I notice that it is not necessary to have 02 written on 0x0009baaf, but the invincibility timer and the invincibility value must be different from 0. I'll just replace 0x8008553C to store on s0 at offset 0x47 (0x0009baaf).
Now the invincibility Timer only lasts 0x19 cycles, being it contemporary to the animation timer. But what if I sum 0x19+0x28 (animation + invincibility duration) to have invincibility during all the process?
I replaced 0x41 to 0x19 at 0x800bf0c0, I tried it, and it finally worked!
[1] Extract Options - ISOBuster documentation
[2] ghidra_psx_ldr: Sony Playstation PSX executables loader for GHIDRA
[4] Crash Bash: Space Bash gameplay #1 - YouTube (2:24)
https://psx-spx.consoledev.net/
CDROM Drive - PlayStation Specifications - psx-spx (license_unlock)
Exploring Tokimeki Memorial: Tools and resources for PSX reverse-engineering
How to Reverse-Engineer a PS1 Game – X-MAS CTF 2020 Writeup · GitHub
MIPS Reference Sheet : David Broman
https://student.cs.uwaterloo.ca/~cs350/common/r3000-manual.pdf
Games - ps1-links -> Contains something about CTR that may have the same structure as Crash Bash