Skip to content

Releases: YeOldPress/alttp-zig

v1.5

Choose a tag to compare

@github-actions github-actions released this 02 Oct 02:36

v1.5, or: it turns out 71 pixels either side is a lot of places to hide a bug

The port plays A Link to the Past. As of this release it plays it properly in widescreen, instead of in a 4:3 box with the camera politely pretending nothing had changed. It also lets you move the buttons without editing an ini file, gives the second item on X a switch and a box of its own, and adds a small mercy for anyone who has ever lost an afternoon to the digging game. Your saves, settings, rumble and MSU pack are all where you left them.

Widescreen that actually looks at the screen

Previous releases drew a wider picture and left everything else alone. The camera still parked itself where a 256 pixel screen wanted it, so the extra space was mostly a window onto whatever happened to be lying around in video memory.

Now the camera knows. Outdoors it stops a margin in from an area's edge so there's real map out at the sides. Indoors it does the same, and because a dungeon room is exactly as wide as the SNES tilemap, that meant holding two rooms at once: the room you're in and the one you're walking into, drawn straight from the game's own room buffers while it loads. Walking between rooms is one continuous picture now, margins and all, rather than a scroll that ends with the sides snapping open.

Everything that only ever had to work in the middle 256 pixels then queued up to complain:

  • The HUD spreads to both edges, and stays spread through the places it used to snap back: beating a boss, opening either map, the flute menu, dying, and the warp to the pyramid.
  • The death screen is widescreen, red flash, GAME OVER and all.
  • The credits are widescreen, all the way through the tour.
  • The file select, copy, erase and name screens keep their backdrop going past the old edges, with the boxes exactly where they were.
  • The lantern draws its cone of light into the margins instead of showing you three more copies of it, and it knows to go out when you walk up the stairs into a lit room.
  • The Triforce room no longer greets the saviour of Hyrule with mirrored wall corners and no curtains.
  • Zora's Domain, the Master Sword's grove and the other special areas get the same camera as everywhere else.
  • Sprites wake up only as far as the margins really reach. Before this, standing in the armos statue half of a double width room in Ganon's Tower woke the Lanmolas in the other half, who started the fight without you, in a room you hadn't entered, out of sight.
  • Garnishes, which is the game's word for little trailing effects, stop being binned the moment they cross the old screen edge. The fire snake in the dark rooms has all four of its flames again instead of crawling around with one.
  • The magical boomerang flies to the edge of the screen you actually have, rather than turning back with a third of the picture still ahead of it.

There is a Widescreen Camera switch in the settings if you want the old framing back.

Buttons go where you tell them

Remapping used to mean opening zelda3.ini, finding [KeyMap], counting to seven in a comma separated list and hoping the seventh thing was A.

There's a Controls screen now, both on the start menu and in the game (Select, Settings, then R until you reach the Power Glove). It shows a SNES pad in line art with the button you're on lit up, and every button's keyboard key and controller button side by side. Press A on one, then press anything: whichever you press, key or pad button, is what changes. Taking a button another one already has swaps the two, keys that already do something else are refused, and there's a reset to defaults for when it all goes wrong.

The pad is drawn in code, pixel by pixel, because this repo ships none of Nintendo's art and the game has no picture of its own controller to borrow. You are, after all, holding it.

The second item on X

It used to be a stowaway inside "Switch Items With L/R", so you couldn't have one without the other. It has its own switch now, Second Item On X, and a box in the HUD, because assigning an item to X and then having to remember what it was is not a feature, it's a memory test. At 4:3 the box sits under the item box; in widescreen it sits beside it and the counters shuffle over.

The canes needed a word about this. A Link to the Past was written by people who assumed there would only ever be one item button, which was fair, since there was only one. So Byrna's spark checked every frame whether it was still the Y item and killed itself on frame one if not, the cane swing took its colour from whatever was on Y (put the two canes on two buttons and they swapped identities), and the Magic Cape only drained magic while it was on Y. They all ask about the button that actually started them now.

A pity timer for the digging game

The heart piece at the digging game is a 1 in 32 chance a dig, and only from the 25th dig of a round. That reads fine on paper and ruins an afternoon in practice.

Digging Game Pity, off by default in Features, hands it over outright after 250 digs without one. The count is a plain variable in the port: nothing of it reaches your save file, which stays exactly the 8K a cartridge holds, so saves still work on real hardware. Once you've dug the piece up the game stops offering it, which it should always have done.

Smaller things that were wrong for a long time

  • The Magic Hammer came out of its chest one room to the left, off screen, along with the door debris and some water effects. They all worked out where a new room was by assuming the camera was parked at the old room's edge.
  • Silver arrows left the tip of the old brown one poking out beside them in the HUD, but only if you had 1000 rupees or more. Four digits push the counter row across; the silver arrow hadn't been told.
  • The splash you make landing in deep water is three sprites, and the middle one has been flying 512 pixels to the left of where it belongs for as long as anyone has been looking. It is back where it should be, which means a lot of people are about to see it for the first time.
  • A fade during dungeon transitions could run past the end of its own palette table and cycle every colour on screen forever, which was briefly spectacular.

Getting it running

  1. Download the game for your system, and the tools if you want them.
  2. macOS (Apple Silicon, macOS 11+): unzip and drag the app to Applications, then run xattr -dr com.apple.quarantine /Applications/alttp-zig.app (and the same for zelda3-tools.app) once. Still not notarized. Still haven't paid Apple $99.
  3. Linux (x86_64, glibc 2.35+): chmod +x the AppImage and run it. If it complains about FUSE, install libfuse2 or fuse3, or add --appimage-extract-and-run.
  4. Windows (x86_64): unzip anywhere and run zelda3.exe or zelda3-tools.exe. SmartScreen will still clutch its pearls: "More info", then "Run anyway".
  5. Give the game your US ROM and press Play. For randomizer seeds, generate one on alttpr.com from the Japanese 1.0 ROM (MD5 03a63945398191337e896e5771f77173) and drop it on the Randomizer page. No game data is included, as ever.

v1.3

Choose a tag to compare

@github-actions github-actions released this 29 Sep 13:59

v1.3, or: I put a whole SNES in the port so you can lose to a randomizer

The port plays A Link to the Past. As of this release it also plays alttpr.com randomizer seeds, and it brings an item tracker that fills itself in while you play. The normal game hasn't changed. Your saves, settings, rumble and MSU pack are all where you left them.

Randomizer seeds

There's a Randomizer button on the start menu now, under Play. It opens two choices:

  • Built-In Randomizer, greyed out, with a COMING SOON sign on it. It's coming. Soon. Ish.
  • ALTTPR.COM Randomizer, where you drop seeds.

Generate a seed on alttpr.com from the Japanese 1.0 ROM, drag the .sfc onto the window, and press Play This Seed. Or run zelda3 seed.sfc and skip the clicking.

The page reads the seed and shows everything it will admit to: its five hash icons (the same ones alttpr.com shows, so you know it's the right seed), its alttpr.com link, logic, mode, goal, crystal requirements and item count. Seed Details goes further: item pool, key and map shuffles, swords, quickswap, pseudo boots, silver arrows, menu speed, heart beep, heart color, the clock, the tournament flag and starting items. The medallions and every dungeon's prize are in there too, hidden behind a button, for when you've given up.

Why there's an emulator in here now

A seed isn't a few shuffled chests. The randomizer rewrites a lot of the game's own code, so the port can't play it and pretend. Seeds run in a SNES emulator instead: the one the port has always been checked against (LakeSnes, by way of upstream), which I put back together as a whole console. That's a CPU, a PPU, a sound chip, DMA, timers and cartridge save RAM, all stepped together a cycle at a time. It plays the ROM exactly as the randomizer built it. Getting there took fixing an off-by-one in the branch instruction, in both the CPU and the sound chip, which hung the game waiting on a sound chip that had wandered off.

  • Saves go next to the seed as seed.srm.
  • Off for seeds: snapshots, cheats and Turbo. It's a race. No fast-forwarding.
  • Still on: fullscreen, pause, window size, volume, and Ctrl+R to reset.

The tracker

It reads the game's save data every frame, so there's nothing to click and nothing to get wrong. Its icons and maps come out of the seed itself, so it looks like the game and ships no art.

The large layout (the default) has:

  • a counter: items found out of the seed's total, hearts, and crystals against what Ganon's Tower and Ganon want
  • the full inventory
  • a table of all 13 dungeons: checks left, small keys, big key, map, compass, boss and prize, with a colored stripe for how each one stands
  • both world maps with every check marked, and a legend so you know what the colors mean

The compact layout fits the same into a narrower panel. The tracker can go beside the game (either side), over it, in a window of its own, or nowhere. T switches between those while you play.

And there are 21 options for it on the page: layout, which parts show, only the current world's map, full or short dungeon names, prizes once you have the map or always, which medallion Misery Mire and Turtle Rock want (spoiler), dimmed or hidden missing items, greyed or hidden cleared checks, marker size, overlay opacity, corner and size, and a green-screen or magenta background for keying the tracker out on a stream. All of it lives in [Randomizer] in zelda3.ini, apart from the normal game's settings.

What seeds borrow from the port

  • Rumble, on by default, working exactly like it does in the port.
  • Widescreen, off by default and labeled experimental. It mostly works. Outdoors the edges can show stale tiles for a moment while scrolling.
  • MSU-1, using the same pack the normal game plays. Nothing to copy or rename. The seed's own code picks the tracks. A normal-game pack's tracks past 34 are ignored, because the randomizer uses those numbers for its per-dungeon music and would otherwise play your pack's track 35 in Eastern Palace. Ask me how I know.

Things I broke and then fixed before you saw them

  • The very first file select screen of a fresh seed came up pitch black. The game was running fine behind the curtain; the PPU was just looking at the screen's brightness once per frame, and the randomizer turns the screen on a few lines late there. It looks every line now.
  • Seed audio crackled, from two separate bugs in how sound got from the emulated chip to your speakers. It's one smooth, continuous stream now, and it adjusts its speed by half a percent at most to keep up.
  • With the tracker in its own window, you couldn't close the game, and putting the tracker back squished everything. Both fixed.

Also

  • NixOS: a flake.nix, courtesy of adamzwakk in the first outside pull request this fork has ever had. Thank you!
  • The README grew a proper randomizer guide, with every tracker option and its zelda3.ini key in one table.
  • New credits: the alttpr.com team, whose generator and z3randomizer source told me where a seed keeps everything, and kattothepast's alttptracker, where the tracker's check data comes from.

Getting it running

  1. Download the game for your system, and the tools if you want them.
  2. macOS (Apple Silicon, macOS 11+): unzip and drag the app to Applications, then run xattr -dr com.apple.quarantine /Applications/alttp-zig.app (and the same for zelda3-tools.app) once. Still not notarized. Still haven't paid Apple $99.
  3. Linux (x86_64, glibc 2.35+): chmod +x the AppImage and run it. If it complains about FUSE, install libfuse2 or fuse3, or add --appimage-extract-and-run.
  4. Windows (x86_64): unzip anywhere and run zelda3.exe or zelda3-tools.exe. SmartScreen will still clutch its pearls: "More info", then "Run anyway".
  5. Give the game your US ROM and press Play. For seeds, generate one on alttpr.com from the Japanese 1.0 ROM (MD5 03a63945398191337e896e5771f77173) and drop it on the Randomizer page. No game data is included, as ever.

v1.2.1

Choose a tag to compare

@github-actions github-actions released this 28 Sep 14:19

v1.2.1, or: the Python has left the building

A point release on paper. In practice, 7,400 lines of Python got deleted, and the thing that replaced them has a window. The game itself hasn't changed at all. Your saves, your settings and your rumble are exactly where you left them.

Meet zelda3-tools

There's a second download now, next to the game: zelda3-tools. You don't need it to play, since the game still builds its own assets. It's for everything else you might want to do with the ROM:

  • Assets: build zelda3_assets.dat, check a ROM, check an asset file.
  • Modding: export the whole game to a folder, edit it, and build it back. The overworld and every dungeon room come out as YAML, the dialogue as text, and Link, the font, the HUD icons and every sprite sheet as PNGs you can open in any image editor. The music comes out too, for looking at and listening to.
  • Languages: got a German, French, Spanish, Polish, Portuguese, Dutch, Swedish or English Redux ROM? Extract its text, tick it, build, and set Language = de (or whichever) in zelda3.ini. Ten translations in all, and no pip install anywhere.

Everything the window does, the command line does too. zelda3-tools help lists it all.

Why it took this long

Because "close enough" isn't how asset files work. Every file the tools write was checked against what the old Python produced: 515 files per export, byte for byte or pixel for pixel. An untouched export builds back into exactly the standard asset file, with a test pinning the digest. I didn't own a single translated ROM, so I built fake ones: the US ROM stuffed with random text in each language's format. Python and Zig read all ten the same. Then 3,970 random messages built into the same file, byte for byte.

Along the way I also reproduced, on purpose, every Python quirk that shows up in the output. There's a duplicated dictionary entry that gets matched in one slot and encoded as another. There's a trailing space after the word "Fallthrough". And there's a Portuguese translation with no end marker, so the reader stops counting at 397 messages. Compatibility isn't pretty. It's just compatible.

One honest note: the music export is for looking, not editing. The old Python music compiler checked every byte against the original and threw an error if you changed anything, so music editing never actually existed. It still doesn't, but now nothing pretends it does.

Also

  • The README lost 319 lines and gained a proper credits section, with everyone who worked on upstream named. The "Why not Rust?" section is now one word long.
  • CI builds and runs the tools on Linux, macOS and Windows.
  • The MSU OPUZ encoder script is gone. Upstream still has it, and packs that are already OPUZ or PCM play exactly as before.

Getting it running

  1. Download the game for your system, and the tools if you want them.
  2. macOS (Apple Silicon, macOS 11+): unzip and drag the app to Applications, then run xattr -dr com.apple.quarantine /Applications/alttp-zig.app (and the same for zelda3-tools.app) once. Still not notarized. Still haven't paid Apple $99.
  3. Linux (x86_64, glibc 2.35+): chmod +x the AppImage and run it. If it complains about FUSE, install libfuse2 or fuse3, or add --appimage-extract-and-run.
  4. Windows (x86_64): unzip anywhere and run zelda3.exe or zelda3-tools.exe. SmartScreen will still clutch its pearls: "More info", then "Run anyway".
  5. Give the game your US ROM and press Play. No game data included, as ever.

The tools use the same folder as the game (~/Library/Application Support/alttp-zig on macOS, ~/.local/share/alttp-zig on Linux), so an asset file you build with them lands where the game will find it.

v1.2

Choose a tag to compare

@github-actions github-actions released this 28 Sep 03:00

v1.2, or: the controller finally has feelings, and the launcher has been absorbed

Three releases in, and this is the one where A Link to the Past learns two things the Super Nintendo never managed: how to rumble, and how to change a setting without quitting the game. Nintendo had thirty-five years. I had some evenings.

Your controller shakes now

The SNES pad had no motor. Nintendo saved the money, Link took every hit in total silence, and your hands never knew a thing. That era is over.

If your pad has rumble (an Xbox, PlayStation or Switch Pro controller, basically anything made this century), it now shakes when:

  • Link gets hurt. Bigger hits, bigger buzz. Pits count, because pits are rude.
  • A bomb goes off, and every single blast of Bombos, which is a lot of blasts.
  • The screen shakes. Dashing into a wall with the Pegasus Boots, the Quake medallion, dungeons opening up, bosses clawing out of the floor.
  • A boss dies. It builds up the whole time the explosions go, then finishes with one full-power thump when the boss finally leaves. You've earned it.
  • You move something heavy. Push blocks, statues, gravestones, levers, the Sanctuary's sliding shelf. Nintendo used one sound effect for all of them, so one rumble covers the lot. Thanks, 1991 sound budget.
  • An arrow lands, a little firmer in an enemy than in a wall, because hitting what you aimed at should feel better than missing it. Enemy archers missing you from across the room don't count. They miss enough already.
  • Doors slide open, the floor-switch ones included. Chests are left out; opening a chest is exciting enough already.
  • The hammer lands and the hookshot bites, then pulls you along with a gentle rumble the whole way.

None of it touches the game itself. Rumble only watches what's happening, which is why the port still matches the original frame for frame. It's turned up to 100 out of the box. Turn it down if your controller starts walking off the desk.

One bug I'm proud to have found: beat the Armos Knights while the last one is mid-stomp, and the original game leaves the screen shifted by one pixel until you leave the room. Nobody noticed that pixel in thirty-five years. The rumble noticed, decided the room was still shaking, and kept your controller going while you picked up the pendant. It now knows a real shake from a pixel that just hasn't moved.

Settings, inside the game

Press Start, then Select. The old question, Continue or Save and Quit, now has a third answer: Settings.

Pick it and you get a whole screen that looks like it came on the cartridge: the inventory's framed boxes, the Book of Mudora, Magic Mirror, Flute and Pegasus Boots as tabs, hearts for on and off, a magic-meter bar for numbers, and the dialogue font for every word.

  • L / R switches tab.
  • Up / Down picks a setting.
  • Left / Right or A changes it.
  • Y explains what it actually does, because "Game Changing Fixes" has never once explained itself.
  • B saves and puts you back exactly where you were.

Settings that can change mid-game do it on the spot. The rest say they'll apply next start, instead of silently doing nothing until you get suspicious.

It's on the file select screen too: SETTINGS now shares the bottom row with QUIT GAME, because the box had no room for another row and I wasn't going to rebuild Nintendo's box.

The first version of this was three settings per text box, with pages. It was exactly as fun as that sounds. It's gone.

The launcher got absorbed

v1.1 made you start a program whose whole job was starting another program. It worked, the way a fax machine works. Now there's one executable, zelda3, with the launcher living inside it as a start menu:

  • It checks your assets first, every time. If they're missing or don't match, you get asked for your ROM. The game doesn't start on assets it can't vouch for, because stale assets misbehave in ways that look exactly like game bugs, and I've debugged that enough for one lifetime.
  • Don't want the menu? Turn Start Menu off, in the menu itself or under Select, and the game goes straight in.
  • The default settings are built into the executable. A fresh folder with no zelda3.ini just gets one written out, instead of falling over.

Upgrading from v1.1? Your files stay where they were: ~/Library/Application Support/alttp-zig on macOS, ~/.local/share/alttp-zig on Linux, the same folder on Windows. Older zelda3.ini files don't mention the new settings, and that used to mean changing Start Menu or Rumble never actually saved. Now a missing setting shows its real default, and changing it adds it to your file, comment and all, with the rest of the file left exactly as it was.

Behind the scenes

  • zelda3 --render <chapter> <buttons> <out.bmp> plays a scripted button sequence from a save state with no window and saves the final frame. It's how I looked at every screen in this release without playing to it each time, and how I found out I'd given every letter a white outline by mixing up two palette colors.
  • zelda3 --data-dir prints where your settings, assets and saves live. --build-assets and --pad-info came over from the old launcher.

Getting it running

  1. Download the file for your system.
  2. macOS (Apple Silicon, macOS 11+): unzip, drag alttp-zig.app to Applications, then run xattr -dr com.apple.quarantine /Applications/alttp-zig.app once. It's still not notarised; I still haven't paid Apple $99.
  3. Linux (x86_64, glibc 2.35+): chmod +x the AppImage and run it. If it complains about FUSE, install libfuse2 or fuse3, or add --appimage-extract-and-run.
  4. Windows (x86_64): unzip anywhere and run zelda3.exe. There's no launcher .exe any more. SmartScreen will still clutch its pearls: "More info", then "Run anyway".
  5. Give it your US ROM when it asks and press Play. No game data included, as ever.

If it breaks, open an issue. If your controller shakes at something it shouldn't, open an issue and describe exactly what you were doing. I want to know what made it jump.

v1.1

Choose a tag to compare

@github-actions github-actions released this 27 Sep 15:12

v1.1, or: it turns out "it works on my machine" was the whole problem

v1.0 shipped. People could download it. Then I looked more closely at what I'd shipped, and it turned out v1.0 worked beautifully on exactly one kind of computer: the one GitHub built it on.

Things I'm confessing about v1.0

The Mac download needed macOS 26.6. Not 11, not 14, not even 26.0. 26.6. Homebrew's SDL3 and a default Zig build both quietly aim at whatever macOS the build machine runs, and GitHub's build machine runs the latest. If you tried v1.0 on a Mac that's a year old and it wouldn't open, it wasn't your Mac's fault. It was mine. v1.1 builds SDL3 itself and targets macOS 11, and CI now refuses to ship anything that asks for more.

Every binary was tuned to GitHub's CPU. With no target given, Zig builds for the exact processor it's running on, which on a cloud server means every instruction set extension Intel ever dreamed up. On an older PC that's a crash waiting to happen. v1.1 targets a baseline CPU everywhere, so it runs on your hardware and not just theirs.

The Linux build wanted the newest glibc around. It was built on Ubuntu 26.04, against the glibc and SDL3 that come with it, so it needed something about that new. Linux users running the stable release their distro recommends deserved better.

What's actually new

macOS gets a real app. No more folder of loose binaries and a dylib you shouldn't touch. It's alttp-zig.app, with the triforce icon, and you drag it to Applications like a civilised person. SDL3 lives inside it. It still isn't notarised, because I still haven't given Apple $99, so the first launch still needs the quarantine dance (below).

Linux gets an AppImage. One file. Mark it executable, run it. SDL3 is inside, built on Ubuntu 22.04 so it only asks for glibc 2.35, and it picks X11 or Wayland and PulseAudio, PipeWire or ALSA at run time, whatever your desktop happens to be running. Arch users are welcome to build it from source instead. They will anyway.

Your files moved, on those two. An app bundle and an AppImage are both read-only from the inside, and the game's whole deal was writing its settings, assets and saves next to itself. So when the launcher notices it's running from one, it uses a proper per-user folder instead:

  • macOS: ~/Library/Application Support/alttp-zig
  • Linux: ~/.local/share/alttp-zig

That's where zelda3.ini, zelda3_assets.dat and your saves live now, and where an MSU pack goes. Coming from v1.0? Copy your saves folder and zelda3.ini across and you're set. Windows is unchanged and keeps everything in its own folder, because Windows has never once stopped a program writing wherever it likes.

Behind the scenes, where the jokes write themselves

Building SDL3 for macOS 11 meant telling Zig which macOS to build for, and once you do that, Zig stops assuming it's building for the Mac it's running on, and forgets where the Mac SDK is. So it couldn't find OpenGL. On a Mac. Then the Mac binaries turned out to have no room in their headers to point at the SDL3 inside the bundle, because the new library path was 10 characters longer than the old one. Ten. Fixed with a linker flag whose name reads like a sentence: headerpad_max_install_names.

The packaging now builds on every change that touches it, so the next surprise shows up in CI instead of in your Downloads folder.

Getting it running

  1. Download the file for your system.
  2. macOS (Apple Silicon, macOS 11+): unzip, move alttp-zig.app to Applications, then run xattr -dr com.apple.quarantine /Applications/alttp-zig.app once, or allow it under System Settings → Privacy & Security after the first blocked launch.
  3. Linux (x86_64, glibc 2.35+): chmod +x alttp-zig-v1.1-x86_64.AppImage and run it. If it complains about FUSE, install libfuse2 or fuse3, or run it with --appimage-extract-and-run.
  4. Windows (x86_64): unzip anywhere and run zelda3-launcher.exe. SmartScreen will still be dramatic about it. "More info", then "Run anyway".
  5. Give the launcher your US ROM when it asks, press Launch, and the first run builds the assets.

No game data included, as ever. Bring your own ROM.

If it breaks, open an issue. And if it breaks on a computer older than GitHub's, I'd especially like to hear about it.

v1.0

Choose a tag to compare

@github-actions github-actions released this 27 Sep 14:13

The first release of alttp-zig: snesrev's A Link to the Past reimplementation, ported to Zig, with a launcher that builds the game's assets straight from your ROM.

No game data is included. You need your own US ROM of A Link to the Past.

Getting started

  1. Download the archive for your system and unpack it anywhere.
  2. Start zelda3-launcher.
  3. When it asks for a ROM, drag your ROM onto the window, or put it next to the launcher as zelda3.sfc.
  4. Press Launch. The first launch builds zelda3_assets.dat, then starts the game. After that the ROM isn't needed.

Per platform

  • Windows (x86_64): SDL3.dll is included. Windows SmartScreen may warn about an unrecognised app; choose "More info", then "Run anyway".
  • macOS (Apple Silicon): SDL3 is included. The binaries aren't notarised, so macOS will block them the first time. Run xattr -dr com.apple.quarantine alttp-zig on the unpacked folder, or allow them under System Settings → Privacy & Security.
  • Linux (x86_64): needs SDL3 from your package manager: libsdl3-dev or libsdl3-0 on Ubuntu 25.10 or newer, SDL3 on Fedora, sdl3 on Arch.

Settings live in zelda3.ini next to the binaries. You can edit them from the launcher.