Red MoonShip battles
Three bugs and a button: the Blackbeard, seen from the wrong places
The first ship battle on PC put the camera inside a hull, lost Little Jack off screen, and left the world map staring up at its keel. Only three of those were bugs.
If you’ve played Skies of Arcadia, you remember the Blackbeard: Baltor’s ship, the first ship battle, and Drachma explaining what that “C!” at the top of the grid means. On the PC port it was where things got strange. Endymion played up to it and, as he put it, the glitches were “a sort of unintended peek behind the scenes”: the camera “frequently rendering from inside one of the ships”, the command menus showing sky and a floating island with no ships in sight, and, after the fight, a world map frozen on the view from underneath Little Jack.
Those looked like one camera problem. They turned out to be two unrelated bugs, a third that had crashed the battle earlier, and one button press that wasn’t a bug at all.
Making the battle last
The first thing I changed was how the battle was tested. The port’s --easy-battles
cheat holds the enemy at 1 HP, so the first volley sinks the Blackbeard, which is
great for getting past it and useless for testing it. I added a narrower cheat that
keeps only the party’s HP and spirit full, so the fight runs its whole length.
It crashed on the S-Cannon turn. The effect number the game looked up was 1,546,321,920. Written in hex that’s 0x5C2B0000, and with its bytes reversed it’s 11,100: a perfectly sensible effect number, still in the GameCube’s byte order. (The GameCube stores numbers big end first; a PC reads them little end first, so the port swaps every number in the game’s data as it loads. Anything it doesn’t know is a number stays reversed.) Those 73 effect records are only ever reached through tables of pointers, so nothing in the decompiled code names them, and the loader never knew to swap them. Describing their layout fixed it.
The camera inside the ship
The battle’s camera isn’t a follow camera. It’s a recorded camera path, a “camera motion” stored in the battle’s model file in Sega’s Ninja format, the same family of formats the Dreamcast version used. The port converts Ninja models, textures and character motions to the PC’s byte order as it loads them, and warns about any chunk it doesn’t recognise. Camera motions were one of those.
So the camera motion’s length, 500 frames, read as about four billion. Every time the game asked “where is the camera at frame N?”, N was a tiny fraction of the motion, the lookup landed on its last key, and the eye ended up wherever that key sat, which was inside a hull. Converting the chunk put the camera back on its path.
Where did Little Jack go?
To check the fix I didn’t trust my eyes; I ran the same memory card and the same button presses in Dolphin, the GameCube emulator, and in the port, and compared the frames. The camera now matched exactly. The menu didn’t: Dolphin showed Little Jack small in front of the island, and the port showed the island alone.


The ship’s position told the story. In Dolphin, Little Jack started the battle at (150, 25, 0). In the port it was at (300, 50, 0): exactly twice as far out, and just out of frame.
The game doesn’t move ships in a battle directly. It bakes each ship’s path ahead of time into a table of samples, one per frame, plus 120 extrapolated samples before the start and after the end so the ship never runs off the table. I logged the samples as they were made: 150, 25. Right. So I set a hardware watchpoint (the debugger stops the moment a chosen piece of memory changes) on the table’s first sample. It was written twice in one go: 150, then 300.
The second write was the padding step. Asked to pad a path with zero samples, it extrapolates from the first sample toward the second, which was never written and so still zero, and lands at twice the first. Then it copies that over the start.
Why zero samples? The path’s length comes from a list of route entries, 88-byte records of plain numbers. The decompiled code described two of those numbers as pointers. On the GameCube a pointer is 4 bytes, so that’s harmless; on a 64-bit PC a pointer is 8 bytes, which pushed the length 4 bytes later in the record (onto a word that holds 0) and stretched every record to 104 bytes. Describing them as numbers again put Little Jack back where Dolphin has it.


The world map that wasn’t stuck
That left the world map after the battle, staring up at Little Jack’s keel. I logged the camera every 50 frames and watched its view number change from the chase view to the view from below, between two samples where nothing else happened.
Except something did. The world map camera has preset views on the D-pad, and my replay file, written to click through the results screens, kept pressing Down after the map came back. Two presses of Up and the camera was back above the ship. The game was doing exactly what it was told.


So: two byte-order bugs, one 64-bit pointer bug, and one button. The battle now plays from the first shot to Baltor’s retreat, and its opening frames match the GameCube’s.
