Thursday, June 20, 2024

The GCNrd Story.

The most important piece of software I ever released was a Nintendo GameCube debugger over 20 years ago. It was not a financial success, receiving roughly $200 in donations. The initial version was released in full and for free on 2004-03-31, around 9 months after development began.

The Seed Planted

I was a bit of a boaster. After several years of creating cheat device codes[1] that were a bit more creative than the standard flare of "infinite health" or "start on level X", I had given myself license to make bold claims without any real need to back them up. I had a knack for finding the developer's hidden debug menus and changing game physics in unintended ways, allowing players to explore the game world outside of the predefined boundaries. So, one day in 2003 when I claimed that I was going to do something to shake up the game hacking community by creating a GameCube debugger, a lot of people probably accepted it without thinking twice. On the other hand, I had no idea if I could actually pull it off.

A few similar tools had already existed for other platforms. The most popular at the time was probably Caetla. Connecting your PlayStation through an ISA "Comm board" and an original Action Replay / GameShark enabled full control over the PS1 software including a debugger, disassembler, and some homebrew development tools. It was an obvious inspiration for GCNrd.

Action Replay was released for GameCube on 2003-02-18. And this, in my opinion, is where it all began. Datel had already done all of the legwork to break into the system and execute arbitrary code. Not much is known about how it all happened, or even who was involved. The biggest problem for the game hacking community was that the codes were all encrypted, and only Datel (CodeJunkies) had the tools to create codes that would be accepted by the AR. Inputting random characters never worked. The AR would always be able to determine it was not a legitimate code, or it would notice that the code was valid but was for the wrong game.

For the first few months, nothing really interesting happened. CodeJunkies continued to very slowly release new codes, usually only one batch for whatever the newest game was at the time. Older games were mostly ignored after the first batch of codes was released for them. If you wanted a specific kind of code for your favorite game, you were out of luck and at the mercy of CodeJunkies to do what they wanted to do.

On 2003-06-17, everything changed. A program called PSUL[2] was released by CrazyNation (legendary N64 sceners) that allowed anyone to execute arbitrary code through a broadband adapter (BBA) and the Phantasy Star Online Episode 1 & 2 game. In mere hours a number of game disc images were also released in the warez scene. One of them was a version of the Action Replay.

I saw this as an opportunity. After a few days of reverse engineering the PowerPC assembly from the AR dump, I released a tool that was capable of decrypting existing AR codes and encrypting brand new "homebrew" codes without Datel's permission. GCNcrypt released on 2003-06-22, just 5 days after the PSUL release. Not bad but could have happened sooner if not for all of the weird stuff Datel did with the AR codes. I had to interpret the meaning of the "identifier code" which is one of the ways the AR rejected random inputs. Real life was also slowing me down, I had work and nothing to go on but intrinsic motivation.

According to my notes, it wasn't until two weeks after the release of GCNcrypt that I decided to actually make the debugger a reality. Two months into the project, I started working on the network driver that would become the foundation for the debugger. Most of it was reverse engineered from the official Nintendo SDK and compared to datasheets available for a very similar (though not identical) Macronix ethernet controller. Within 5 days, I had enough working that I could send and receive UDP packets to a PC connected on the same network. This was 2003-09-01, and though I worked on the network driver mostly on my own, I was also moonlighting as a scener, releasing GameCube ISOs and writing tools for the group to use.

One of the sceners I worked closely with at the time was Aftermath. He and I came up with an idea to run the ISOs on GameCube hardware by streaming parts of it over the BBA. And after the network driver was usable enough, that's exactly what we did. We picked the easiest possible game to start with, Animal Crossing. Because it was a small game, and it was known at the time that you could remove the disc after the title screen and the game would just keep running forever. Everything it needed was loaded at the start, and then it would not need to read the disc ever again. It was the perfect test case. Animal Crossing Loader was released on 2003-10-10. It was the first publicly available method of loading ISO backups on the GameCube.

ACloader went through several iterations, starting with compatibility improvements, and each release fixed bugs or increased network performance. At its core, the idea was simple. After initializing the network driver and setting a fixed IP, it would send packets to a predetermined server IP to establish a session. The GameCube would request the initial boot blocks from the virtual disc, which the server would send as requested. It followed the basic boot process, reading the boot block and running the AppLoader, etc. After the main executable was loaded by the AppLoader, the memory would be scanned for the Read() function, which would be patched to read from the simulated DVD drive over the network.

The debugger worked on the same basic principle, so it's no wonder GCNrd shares a common ancestry with ACloader. They share about 80% the same code, which we called "libeur". The initial GCNrd release in 2004-03-31 removed all of the "DVDsim" capability, which I kept in private builds. GCNrd was most well-known as a debugger for commercial game discs, but it played a role behind the scenes that made it possible to release new codes as soon as a game hit store shelves, sometimes even earlier.

Sceners raced each other to get the newest games as quickly as possible, and sometimes that meant a scener embedded somewhere in the logistics for game distribution would end up taking a disc home prior to the official release date where it would be copied and uploaded to FTP sites across the internet. On the other end, I would download the ISO and run it locally with the debugger's DVD simulator. I would create codes and designate a new unique game ID for it. In some cases, Datel would adopt the same game ID for their official code list.

The Nitty Gritty Details

It wasn't all roses and rainbows. Though what I was able to achieve with GCNrd is hard to overstate, the saga was laced with drama and technical challenges. Aftermath ended up doing a large number of ACloader-spinoff projects on his own, infrequently contributing his improvements back to GCNrd. Some of them are lost forever. I had a contentious relationship with Costis, of PSOLoad and SDLoad fame. I made some very minor contributions to SDLoad, which made launching GCNrd about 100 times faster. Datel copied the idea for their own SD Media Launcher about 2 years later. LOL.

Development with the PSO loaders was very rough. It took about 2 minutes from power-on to first Arbitrary Code Execution. Several compatibility bugs that GCNrd had were due to state leftover from PSO. Typically, some hardware needed to be reset before starting the new game. Star Wars: Rogue Squadron II - Rogue Leader was the one that gave me the most trouble. I would spend several hours across multiple days trying to find the cause of the game crashing in the level intro. I think I eventually fixed it, but I no longer remember what the actual problem was. And even then, SDLoad didn't have the same crash bug. So, I may have not fixed it at all, instead recommending SDLoad as "the right way" to use GCNrd.

Programming in assembly and low-level C for the console was harsh. It was too easy to screw things up, especially because there is no cache coherency in the CPU. Doing any kind of self-modifying code was a unique challenge. There was no real debugging, apart from what GCNrd itself implemented! I could send formatted strings in UDP packets, which is how I did a lot of the initial development. And ultimately, I added an exception handler to catch crashes and report thread context. It was a slog, lots of time resetting the GC and waiting through the PSO loading screens. Debugging was just trial and error for months on end.

I had limited memory available, about 32 KiB which I reserved for the debugger and its network stack. I was able to offload some of the logic to the PC-side by implementing commands that could read and write arbitrary memory locations. This was probably the saving grace for the project. The memory-resident debugger could get away with just the bare minimum functionality for using a pre-initialized broadband adapter (the menu would initialize prior to loading and running the memory-resident half) and some basic functions to control the debugging circuits on the CPU. Most of the interesting parts of the debugger were built on top of these simple primitives as a "remote debugger". Searching for library SDK functions just needed to read memory, for instance. The comparison code existed on the PC-side. Patching was the same, the assembly routines were on the PC-side and the only thing needed on the GC-side was the capability of writing to memory.

In hindsight, it would have been much easier with a smaller memory footprint to create a serial interface instead of using the BBA. However, I had good reasons (at the time) for preferring the BBA:

  1. I lacked any kind of hardware knowledge. Designing even a simple serial interface at that point was not something I could have done. In 2004, it was unclear if the GC even had a general-purpose serial port available to use (e.g. not the controller ports).
  2. The BBA was already available, and it was much faster than what could be done with a bespoke serial port. The full 24 MiB of main memory could be downloaded in about 14.5 seconds, according to a pre-release note that I kept in my logs. A rate of about 1.7 MiB per second. This was about half of the max theoretical bandwidth of the EXI bus.

Source control was basically non-existent at the time. Mercurial and Git wouldn't have been released for another 2 years. Code was copied to other directories or zipped to make backups, whenever I felt like it. Which clearly wasn't enough. I no longer have the code for the initial released version, or the second release, "v1.10b"[3]. I instead have a few variations of source code from intermediate points between (and after) both releases. One day I would like to go through these variations and try to understand what was changed between them.

I could release the code, but I don't even know if it compiles. There was a point in time when I thought I would clean it all up and make the memory-resident debugger relocatable and remove all of the duplicate code between the menu and the debugger. That subproject was incomplete, and I am sure it left the code in an unusable state. The code is also absolutely terrible, being a specimen from my earliest developer days. It would be a really bad release without some real curation effort going into it first.

Wrapping Up

So, why write about this now? Well, for starters, it all happened a very long time ago. Everything about GameCube is already known by now, but back then we were pioneering. I left the warez scene sometime in 2008 because I felt like I had done everything I set out to do. I made piracy possible on GameCube and had a pretty big impact on the DS as well. These days, game piracy is completely alien to me. I just felt a need to reminisce about a very interesting time in my life, at least the good parts!

It's very weird watching something like an MVG video where he describes your work but doesn't say anything about who you were or how you did it. Clearly, I had an impact on many people's lives, and they don't know anything about me. The ACloader was built mostly in anonymity (for obvious reasons)[4], but I'm happy to claim responsibility for it. It was an awesome hack that let hundreds of thousands of people[5] play games that they would otherwise not have access to. On the other hand, GCNrd was always associated with my internet handle (Parasyte), just stripped down to remove the piracy features.

Given the chance, would I do it again? In a heartbeat! It's as close as I have ever come to feeling like my existence was meaningful. That I wasn't just consuming resources, but also producing something of value. It's what I imagine the early phreakers and hackers must have felt in the 60's and 70's, before I was born. Doing something that was generally looked down upon, but in its own right created immense value by allowing people to connect in ways they couldn't before.

I am proud of GCNrd and ACloader. And even though I have a chip on my shoulder because I was never able to top those projects (despite untold amounts of effort) I still believe it was one of my crowning achievements. I was 20 years old, self-taught, working a terrible low-wage job, and I just personally enjoyed messing around in game internals. It was inevitable that I would create tools to help other people do the same thing.

All that said, GCNrd and ACloader was not my only project that gained wide attention. I hinted about DS earlier, but I also did a lot of stuff with N64, NES, SNES, PS1, all of the GameBoys, and dozens of emulators for several platforms. It's too much to list, but there are a few other good stories in there that have never been told. Maybe someday I'll feel nostalgic enough to write about them, too.

In summary, working on the project was miserable at times and it's now largely forgotten. It was all worth it, though.


  1. "Cheat devices" were mostly unlicensed cartridges or discs for home video game machines that would allow players to make some real-time modifications to games. The most common effects were "cheats" like being able to take an infinite amount of damage or give yourself all of the unlockable upgrades in a game.

  2. As far as I can make out, PSUL and PSOSocks were both released on the same day. PSOSocks contains a disc dumper and other tools. It kicked off the GameCube piracy scene. At least as far as copying went. PSUL was more homebrew-oriented, and it can be said to have kicked off the GameCube homebrew scene.

  3. I DO have the complete source code for the very first ACloader, however. Which probably deserves a release just for the historical significance.

  4. The initial ACloader clearly lists "AFTERMATH AND PARASYTE" in the scroller, and the PC-side server includes the same information. But I tended to distance myself from it in the open.

  5. I really don't know how to estimate the actual number, but 6-digits is easily in the right ballpark.

Monday, March 18, 2024

Goto is an Abomination

In response to Goto is Not a Horror, I must implore you witness:

func main() {
	var i = 0

	loop:
	i += 1
	if i > 10 {
		goto done
	}

	fmt.Println("Hello, world", i)

	goto loop
	done:
}

You might say, "that isn't fair, this is just bad code using bad practices". And you would be half correct.

Goto can be used for evil. And it is. Too often, goto is abused. But here's the thing: Goto can only be abused. The example is fair criticism because there is no "good" use case for it.

That was Dijkstra's point.

The go to statement as it stands is just too primitive; it is too much an invitation to make a mess of one's program.

It's too unconstrained, or "unbridled" as Dijkstra put it. This is still the case with the goto keyword today. And it will remain the case 56 years from now.

"It's not a problem anymore, that goto is gone," says this unusual justification for an abomination, as if Go supposedly fixed it. You've clearly read the original 1968 letter, but seem to have missed its 1987 follow up, "On a Somewhat Disappointing Correspondence."

The real myth is that somehow jumping around arbitrarily just magically stopped being an issue. As the 1987 paper alludes to, we have better problems to solve than your decision to avoid constraint.

You can write code without using the goto keyword at all and it will make your code less enigmatic. Try it sometime. Stop defending the indefensible.

As for me, I thought that by now professional programmers would have learned something.