Project log
Golden Sun Recompiled
What is this? This isn't Golden Sun running in an emulator. The game has been translated into C++ ahead of time and compiled into a normal Windows program. Same game, but now we can actually mess with it instead of just pretending to be a Game Boy Advance really fast.
So what actually is this?
It's a static recompilation. Fancy term, pretty simple idea. Instead of an emulator reading the GBA instructions while you're playing, a tool reads the game ahead of time, translates those instructions into equivalent C++, and then that gets compiled into a normal executable. So your PC is running the game, not running a fake GBA which then runs the game.
In Golden Sun's case that's just under 27,000 functions, plus 95 overlay banks the game swaps in and out while moving through towns, dungeons and battles. The old instruction-by-instruction path is still there as a fallback because deleting your parachute before the plane has landed would be a bit stupid, but it barely gets used: one catch across 186,079 native calls in one snapshot.
The part that isn't translated is the actual GBA hardware stuff: graphics, sound, timers, interrupts, all that fun shit. That's hand-written C++ basically pretending to be the bits of a GBA the game still expects to exist. Unsurprisingly, that's also where most of the remaining problems live.
No game data is shipped with the project. You bring your own cartridge dump and BIOS, both get hash-checked, and then it runs. It's built on gbarecomp by mstan, with an edited copy in the project because Golden Sun needs a couple of changes in the graphics and ARM layers that don't really belong upstream. I intend to make this BIOS agnostic as well when I release this, so that you only need a legit ROM.
Why do this when emulators already exist?
The end goal is simple: widescreen should mean "render more of the world", not "invent a pile of hacks around the original 240×160 image."
The goal from pretty much day one
Because this gives us a completely different kind of control. An emulator still has to respect the original hardware and work around it. Once the game is native code and its data is sitting right there, a lot of those old limits stop being sacred. The screen size is a number. Text speed is a number. Item data is a table. Suddenly you can change the actual game instead of constantly negotiating with a 2001 handheld.
The wider view
The original GBA screen is 240×160. This currently runs at 360×240, so 50% more in both directions. And no, the extra space isn't stretched, blurred or AI-generated into existence. It's built from Golden Sun's own map data, read straight from memory. If there's ground beyond the old edge of the screen, you're actually seeing that ground.
Naturally, making the screen wider broke a bunch of things that had basically nothing to do with drawing the map. Golden Sun parks unused sprites just outside the visible screen, so widening it suddenly made some of those sprites visible again. Menus and dialogue also hide layers using rectangular screen regions, and the new columns weren't inside those rectangles, so bits of the hidden world started reappearing next to text boxes. And if the map genuinely has nothing to show, we now draw black instead of whatever random garbage happened to be sitting in video memory. Very glamorous stuff.
How the room buffer actually happened
Making the screen wider sounds like a rendering problem. It really isn't. The GBA only keeps a 256×256 pixel ring of map in video memory, which is barely more than one screen, and the hardware wraps that ring when you read past the edge. The pixels outside the old screen genuinely do not exist anywhere. Read them anyway and you get the opposite side of the screen repeating, which is exactly the tiled garbage the early builds were proudly displaying.
So the fix was to stop asking video memory and go to the source. Golden Sun keeps the whole room in main memory: a 128×128 grid of metatile IDs, plus an atlas where each ID expands into four 8-pixel tiles. Both are already flat arrays sitting there. Which means the "room buffer" copies nothing at all — it's the room rectangle, one offset per background layer, and a resolver. Any pixel past the old screen edge is two array lookups. Nice side-effect: because it reads live, an event that shoves a pillar across the room patches the grid and the widened view picks it up for free.
The bit that took five attempts: where each layer actually sits
Every background layer reads the room at its own offset, and getting that offset wrong doesn't crash anything. It just quietly draws the wrong part of the map. Every version below looked plausible on screen, which is why all of them had to be measured against real video memory instead of eyeballed:
- Work it out from the room's size. Perfect in 512×512 rooms and wrong everywhere else, because that's the one size where the offsets happen to equal the room's own dimensions. Very convincing until you walk into a room shaped like anything else.
- Assume the 16 resident cells are the 16 aligned to a multiple of 16. They're the 16 starting at the camera. Fixing that one assumption moved the match rate from 46.8% to 71.0%.
- Require rooms to start at 0,0. True in every room measured at the time, but not actually a rule of the format. Goma Cave Entrance got rejected outright, 3.7 million samples declined for "no room", and the whole margin went black.
- Use the ground layer's position for every layer. A layer whose offset isn't a multiple of 16 sits somewhere else in the ring entirely. Offsets of 32 hide this, offsets of 60 absolutely do not. Giving each layer its own position lifted two layers from 66.9% to 93.9% and 62.0% to 95.3%.
- Count in 16-pixel cells. The one that just bit me in Goma. Half-cell offsets exist, whole-cell arithmetic cannot express them, and the layer sat there refusing to answer for 298 frames straight. Now it counts in 8-pixel tiles.
The thing that made any of this findable is the self-check: every frame it rebuilds the 16×16 cells the hardware is currently holding, per layer, and compares them to what's actually in video memory. That's the difference between "the cave looks a bit off" and "ground layer 99.32%, props 99.81%, tile layer refused 298 out of 298 frames, offset 0,8". You can guess at the first sentence for a week. The second one just tells you what's broken.
What works right now
Small but important disclaimer: all of this is still experimental. It works in the situations I've tested, but none of it has been proven across the whole game yet. Golden Sun is very good at hiding some weird edge case three towns later, so for now "working" means "working so far," not "finished and bulletproof."
-
Expanded view, 360×240
Working / WiP50% more world in both directions, built from Golden Sun's own map data.
-
Instant text
Working / WiPGolden Sun's "Fast" text setting is hilariously not that fast. It draws one word per frame and stops at every space. Remove that one stop and the whole line appears instantly, while every actual "press A to continue" pause stays exactly where it should.
-
Smooth automatic overclock
In testingIf the game can't finish a frame in time, it gets more CPU time. The old version just doubled it, which fixed the slowdown and created a lovely new flicker problem. Sixteenth-sized steps were the obvious fix and made it noticeably worse, because a struggling frame steps every frame and they stack — eight frames in a row moved it 50%, which is a doubling wearing a disguise. It now drifts by 1/256th per frame, and the runtime no longer throws away its in-flight cycle accounting every time the rate changes. Whether that's actually the flicker's cause is the current open question.
-
Walk and run speed
Working / WiPActual movement speed gets multiplied. The whole game isn't being fast-forwarded like some emulator turbo button.
-
Clean 60 Hz timing
Working / WiPThe original GBA runs at roughly 59.73 Hz. There's a switch to run Golden Sun at a clean 60 Hz instead, without speeding the game up — it's off by default and you turn it on in the in-game settings. Tiny difference on paper, but it's one of those nice "we don't actually have to inherit the hardware's exact timing anymore" wins.
-
Sprite placement fixes
Working / WiPCharacters were jumping around and wrapping near the new screen edges. That part is fixed. Sprites still disappear earlier than they should, and one standing above the top of the original screen still gets cut in half, so this one is genuinely half-done.
-
Measurement tooling
Working / WiPRecorders for map data, sprite positions, text timing and per-frame CPU headroom. Boring as hell to look at, extremely useful when the alternative is guessing why Isaac just teleported six pixels to the left.
What's next?
-
Lighting in the extra space
PlannedCave lighting isn't built from the same map tables and doesn't scroll with the room, so the widened area is currently too bright. In a sunny town you won't notice shit. In a dark cave it looks very, very wrong.
-
Rooms that don't fit the grid
Fix written, in testingGoma Cave was showing garbage in the extra space, and my first guess at why was wrong. The room itself is fine: 496×464, a clean number of cells. The actual culprit is that one of the background layers sits exactly 8 pixels — half a cell — below the ground layer, and the lookup could only count in whole cells. So it refused to answer for all 298 measured frames and fell back to garbage. It now counts in 8-pixel tiles instead, which the map data already supports.
-
Running without a BIOS file
PlannedThe cartridge dump stays required. The separate BIOS file really shouldn't be.
-
One proper in-game options menu
PlannedRight now the settings are split between the launcher and an overlay because apparently I enjoy making my own life harder. Eventually this should just be one menu you can open while playing.
-
Editing game tables
PlannedItems, Psynergy, prices, stats. The funny part is that this barely needs new tech. Find the table, change the value. This is where the project starts going from "native port" to "actual modding toolkit."
-
Replacing functions outright
PlannedThis is the big missing piece. Every original function already exists as its own C++ function behind a lookup table, so replacing one with hand-written code is a pretty obvious seam. It just isn't wired up yet.
-
Native music
ShelvedPartly built, currently parked until the map and renderer stop demanding attention every five minutes. Shelved, not dead.
So where does it actually stand?
It's playable locally as far as I've taken it, with the wider view, instant text and speed controls all enabled. It's still a project build though, not a release. There isn't a public download yet. AI disclaimer; AI was used to aid me in making this project a reality.
Built on gbarecomp (PolyForm Noncommercial 1.0.0). Golden Sun is © Nintendo / Camelot; no game code, assets or BIOS data will be distributed by this project.
Reacties
Een reactie posten