Good news. After years of going through the game files, learning and debugging, I have finally been able to solve the last two remaining issues of this project.
Both the subtitle syncing issue, and the cutscene model issue, have both been resolved.
It took quite a while, mostly due to the fact that Other M is one of those games that completely lacks any kind of documentation when it comes to its inner workings, so I had to pretty much dig around almost all of the numbered extension-less files to properly narrow down where the exact data for the subtitles frames and the cutscene model loading routine was found in, and interestingly, both of those were found within the REL file for the game (game.rel).
I'll give a slight detail on how both issues were found, just for documentation sake, but the complete documentation for both fixes has been added to the repository for Other M Redux as well:
- Subtitle syncing fix:
The subtitle data (not the subtitle texts, those are in message_all.dat) for all the cutscenes is located within the game.rel around 0x710000-0x720000. The subtitle data is stored in the format 0C A3 XX XX YY YY 08 16 ZZ ZZ, with each pair meaning:
- 0C A3 = Display subtitle custom opcode (Next 2 bytes = subtitle ID, Following 2 bytes = duration)
- XX XX = Subtitle ID / Message ID
- YY YY = Subtitle duration on-screen (in frames)
- 08 16 = Set frame timestamp custom opcode
- ZZ ZZ = Subtitle trigger frame (in frames)
For dm36to43 (the Ridley cutscene), its subtitle data is located at 0x711438 and 0x711534. 0x711438 holds the subtitle data for the English audio track, while 0x711534 holds the subtitle data for the Japanese audio track, since both tracks are stored within the same SFD.
For dm52to56 (the Sector Zero cutscene), its subtitle data is located at 0x711648 (English) and 0x711AD8 (Japanese) respectively
With that, and following the explained format above, I was able to properly resync the subtitles.
One additional bit of information regarding the subtitle data, since I had to remove certain subtitles from appearing, I had to do two things:
- Remove the text data for that specific message/subtitle ID. Simply blanking it out through a Hex editor with spaces does it.
- Change its frame data to a frame value before the next visible subtitle.
- Since I changed the order of a lot of subtitles for both cutscenes, some of the removed subtitles had to also be moved in order for them to follow their correct subtitle order (I didn't want to bother with reordering the subtitles as well as their frame data). So with that, I had to move the subtitles so they didn't appear on-screen. I found out that if two subtitles have colliding appearing/trigger frames, then they don't show up at all when called. So I ended up cancelling subtitles that I wanted to remove by having them use the same frame trigger value, for example, two subtitles had their trigger frame set to 06 00, making them not appear on screen.
As you can see, the process was quite ellaborate, more so for syncing each subtitle for the corresponding cutscene to the proper frame, so they behave similarly to how they do in the original game (in terms of when and how long they appear on-screen) and matching them with the visuals.
- Cutscene model loading fix:
This is the one that took me the longest to accomplish. I was working on both the subtitle and model fixes, and the subtitle data fix I had it ready by April 2026. The model one required a lot, and I mean a LOT of debugging to find the proper place to hook into and what to read from in order to make it possible.
Here's a short summary of the problem with the cutscene models:
- The original game handled which model to load depending on a set of comparisons against an ID of sorts starting at 0xCADAC inside the game.rel file, more specifically, 0xCADC0 through 0xCAE87.
I'm not completely sure if it was checking against a room ID, an area ID or some sort of internal cutscene ID format, since I couldn't really narrow down what that value was. Basically, what that set of checks was doing, is that it was checking a lot of IDs, and if one of those IDs matched, then the model for the Varia Suit (253) would load into the cutscenes with the Power Suit model (252) being the default one, and the ID checks being the only thing that told the game when to load Varia. The one opcode that loads Varia after getting a matching ID is the one found at 0xCAE8C.
- Since I couldn't really figure out what the ID it was reading was precisely, I decided to instead ditch the entire decision tree found at that precise location, and instead change it to a custom one that checks for gear_struct_base. What this means is that I am now reading directly from the address that keeps track of which items the player has obtained throughout the game, with that address specifically being 0x80CA4E20 and 0x80CA4E28. 0x80CA4E28 is reliable later in the game, since the whole struct shifts at some point, and 0x80CA4E28 sets bit 0 when the player has obtained the Gravity feature in the game, with the value in my playthrough changing from 0x26 to 0x27. So that one was used to determine when Gravity is obtained in the original game, and therefore, check against it and load the correct model for the Gravity Suit (254) instead of the one for Varia Suit (253).
- However, that's not the end of it, since adding that check alone completely got rid of the Power Suit loading for cutscenes, something that was already working in the original game. This confirmed to me that the original game loads the Power Suit model (252) by default, and the decision tree from before was telling the game exactly when to load the Varia Suit specifically depending on the checked ID. One more issue with this, when starting the game, the data for the Gravity suit is not reliable, since that one stays at a consistent 0x0A for some reason, and instead, the gear_struct_base address is found at 0x80CA4E20 until a certain point in the game. Not sure entirely when it changes, but it does. 0x80CA4E20 stays at a consistent value of 0x11 all the way throughout the start of the game, right up to the point when the player get the Varia Suit, so that is our second checking point, check for 0x80CA4E20 and its bit 0 in order to know when we have obtained the Varia Suit to load either Power Suit (252) or Varia Suit (253) for cutscenes.
- The last missing piece to get this working 100% was the final cutscene of the game, the one where Samus returns to the Bottle Ship, when she enters the room where Adam's helmet is and deactivates her suit, so we get Zero Suit Samus in-game. The very start of this cutscene, when loaded directly from the Theater mode, for some reason has its gear struct changed from 0x80CA4E28 to 0x80CA4E20. And not only that, but the value at the start of the scene at 0x80CA4E20 is 0x15 now, and when the cutscene triggers, it changes to 0x1D. As a quick workaround for this, I made the custom routine check for 0x15 at 0x80CA4E20 as well, and if it did, then load the Gravity model (254) for it. I checked against other parts of the game, and no other part of the game had 0x15 in that specific address before that point in the game (post-game), so this can be considered safe. Interestingly enough, loading the last cutscene (dm65_02) sequentially from a previous entry in the Theater mode does NOT trigger that change in address, it's only when loading that cutscene directly that it does.
The cutscene models fix was without a doubt one of the most technical and difficult things to achieve, but thankfully, it seems to be working now.
With that, both of the last remaining issues for Other M Redux have
finally been completed!
It was quite a ride, years to figure out just a couple of, what seems to be, minuscule things to find, but the lack of interest from the hacking community towards Other M surely made this difficult to accomplish.
An Other M Decompilation project cannot come soon enough, I'd love to see one come to fruition and get an eventual PC port out of the game, but unfortunately, there's not even one started decompilation project for Other M, so sadly, that would still be years off from becoming a reality.
However, I hope that my Other M Redux project brings more replayability and a more friendly way to enjoy the game, without many of the weird story-driven aspects that detracted from the experience.
I think the one final thing still missing is one custom cutscene is still missing a Gravity Suit colouring mask, but that one is very minimal, and can only be seen for like 2-3 seconds tops. I'll tackle it next and that should be it 100% for this project!
