My Audiosurf/Quest3D reverse engineering journey

If you have a lot to say about something and feel like writing an Effort Post™ about it, post it here!
Post Reply
User avatar
m1nt_
Posts: 1
Joined: Thu Sep 14, 2023 4:32 pm

My Audiosurf/Quest3D reverse engineering journey

Post by m1nt_ »

Audiosurf is a game that was released on Steam in 2008 by a guy called Dylan Fitterer (but his wife, Elizabeth, worked on it too!)
As far as I know, it was kind of a decently big deal back then, so you may or may not have heard of it before (but hopefully, you have :) ). It's pretty straightforward, you put in an audio CD or select an audio file from your PC, and there you go! The game generates a track based on it using fast Fourier Transform magic (not quite sure how exactly the game does this yet) and lets you play through it using a character of your choice, each of them having their own special ability. As far as gameplay goes however, it basically boils down to:
  • collect colored blocks to fill your grid
  • (when playing as Mono) avoid grey blocks
  • avoid overfilling your grid
  • get points when three or more blocks of the same color touch
  • ideally spit on your friends by dethroning them 5 minutes after they set a score, then send them a message on steam that reads "lol ez", with a screenshot of your score attached to it. make sure to include your friend's score below yours in the screenshot to achieve maximum tear output
It's an extremely fun game with visuals that are borderline psychedelic and suprisingly pretty for its time and I highly recommend it to anyone with a huge collection of locally stored music.

As for its internals, it runs on an old engine called Quest3D which was made by a Dutch company called Act-3D B.V.
There is basically no documentation available on it online. I don't much about it ever existed in the first place, but whatever DID exist is mostly lost to time. This includes the whole editor part of the engine itself!
So anyway, for some reason I genuinely forgot (did I ever even have a real reason to in the first place?), I decided I wanted to mod this game. The "why" factor is especially big here, since when I started, I knew basically nothing about modding games from scratch, reverse engineering, or C++. Hell, I'd say I wasn't too competent in any programming language. I was pretty much doing the equivalent of banging my head against a brick wall and hoping it feels sorry eventually and decides to move out of the way by itself, but... it actually worked out in the end!

My first, extremely wonky attempt at modding was when I tried adding Discord Rich Presence to the game - Audiosurf provided some really weird APIs based on WM_COPYDATA. It used C# and WinForms for a terrible GUI, and C++ for an even worse DLL that did the actual WM_COPYDATA stuff. It sucked really bad. But, this wouldn't remain my only attempt at tackling Discord Rich Presence support for the game. After some time I got back to it, and each attempt was noticeably better than the last and I'm actually considering trying again for a 4th time now :lol: !
The Rich Presence modding stuff wasn't that big but served as an important stepping stone nonetheless. After this, I began to poke at the engine itself.

The main part of the engine is contained in a file called HighPoly.dll and is accompanied by an executable called QuestViewer.exe. Said executable loads a .q3d file, which in turn loads the actual game files, which are .cgr files that also require a metric shitload of DLLs to be loaded. Whew. That's a lot to unpack.
The CGR files are what actually store the game assets and code - you see, Quest3D uses visual scripting! An application made with Quest3D is comprised of a lot of "channels" which are connected together. Every single one of these channels is its own, seperate DLL file! There's one DLL file for a channel that stores a string, one storing a texture, one storing a number, and the list goes on, and on, and on, and on... Audiosurf has a whopping 230 DLLs in its "channels" folder!
It also allows developers to make their own channels to integrate into the engine, which Dylan makes use of in Audiosurf a decent amount, especially for loading the songs, the track generation, and some weird hashing stuff, apparently?

Armed with a tiny bit more knowledge after the Rich Presence modding, I decided to make my own tools to examine the game and its engine further. Somehow, I soon managed to get my hands on some version of the Quest3D 4.0 Editor and its SDK (just a demo version for now, but that didn't really affect me) which helped greatly. After wrangling C++ and fixing up the absolutely archaic Quest3D SDK to compile on a modern system, I got to work.
I built an in-game GUI for my tool using Dear ImGui which allowed me to sift through the game's huge pile of channels easily. Keep in mind, my UI looks like this:
121410792-40245400-c963-11eb-8816-40b3f8dfd06c.png
No fancy node view, nothing. Just number inputs to select the channel group and channel to examine. This was quite painful, but still allowed me to see some really cool things! Like dialogue for a hidden story mode that was supposed to be a thing at some point in development. This actually got me to email the dev and ask about it, and I actually got an answer from him! All in all, he's a really cool guy and told me a lot of interesting things; like how he wanted a story mode with unlock progression because all the big games at the time had those! (Well, seems like he didn't need those to make a fun and successful game anyway!)
Other notable features of my tooling are:
  • The ability to save .cgr files without protection or compression. That's right, they can be compressed! The "protection" is something I still have no idea about, but it can literally be defeated by calling something like

    Code: Select all

    channelGroup.setProtected(false)
    Also, some changelog somewhere mentions encryption - this seems to be blatantly wrong from what I've seen, unless compression counts as encryption KEKW
  • The ability to dump channel groups to a GraphViz DOT file, which can then be rendered as an SVG for "easier" viewing of channel groups. Notice how I put the word "easier" in quotation marks? That's because it barely made it any easier - loading/displaying these SVGs was often very hard for software. Can't really complain, one of them was about 14MB big!
This tool is also on my list of "things to rewrite with my newfound knowledge", because the code for it is downright terrifying, and I really do mean that.

All of the previous knowledge I collected however served to bring me closer towards one huge final goal: Making a custom server for the game. Yet another thing I was completely inexperienced in, naturally!
For this, I used node.js. For my first attempts, I used Express.js, then switched to Fastify and TypeScript. The biggest hurdle here was the "status" hash that the server returned, which I needed to replicate correctly to have logins work. It took me so damn long to figure out how that was computed, but by hooking some hashing functions, I found out the hash was made of a combination of some random constant strings, with some data provided by the client upon login sprinkled in there. Sweet!
Then, after I figured that out, Dylan retired the ancient Audiosurf accounts system (which used MD5 to hash passwords, by the way! That wasn't a good idea even back then!). Now, all authentication was handled by Steam and its auth tickets, which means some of my prior work was basically useless now, especially since it got rid of the "status" hash entirely. Of course, I was and still am relieved that this means authentication is more secure now. While we're on the topic of security though, using MD5 wasn't the only problem here; there's one or two specific endpoints which the game specifically requests over HTTP (without the S). Finding this out confused me a lot and before I realized what was happening, I thought my server (which was only listening for HTTPS) was at fault. Oh and by the way, redirecting HTTP to HTTPS simply didn't work, because the game can't handle being redirected. Yay.
Now, the changes that the retirement of the old account system brought with it weren't all good. The game's website was now just gone! No more viewing scores or rival profiles on the web!
Therefore, having a custom server became a lot more important to me and some of my fellow players.
And... from there, the project progressed rather quickly! I managed to actually get things done and make a frontend, as well as have the backend support basically everything the original server did. It's still not completely done, but the vast majority of features are there! That includes Audiosurf Radio, which was a service provided on the official server that had a changing selection of songs players could download in-game, for free. By replacing the RadioSelector.cgr file with that from an earlier build and by using the Quest3D tooling I made previously, I'm able to create and serve custom CGR files as Audiosurf Radio songs!

All in all, I'm proud of what I've achieved so far and I can't believe I've come this far pretty much by trial and error, somehow. There's not much else to say now, but before I end this post, let me talk about the most recent development:
I managed to figure out the CGR file format.
MASSIVE shoutout to MattKC, when he mentioned RIFF in his video about Lego Island's SI files and showed what the format looked like, I IMMEDIATELY thought "Wait, that looks EXACTLY like an uncompressed CGR!"
So... I checked, and it seems that CGR files do indeed use RIFF (or at least something that's extremely similar)! I literally jumped when I discovered that! So, thanks MattKC! :lol:

(I'd like to put some links to the source code and the website of the custom server here, but I'm not sure if that's allowed due to the "no advertising" rule so I'll leave them out until I can be sure I can do that.)
Thanks for reading my post. If you have any questions, please go ahead and ask! :)
User avatar
levywilson
Posts: 9
Joined: Mon Aug 21, 2023 6:19 am

Re: My Audiosurf/Quest3D reverse engineering journey

Post by levywilson »

I still play Audiosurf occasionally. Perhaps the only rhythm game I can play because I suck at OSU and button match type games. I feel it's superior to its sequel by the fact that it doesn't randomize the level after every play. Audiosurf 2 randomizes the blocks so it's different every time. If you need more information, you could probably contact Dylan. He still updates the original occasionally. Most recent updates include better widescreen resolutions, more gradual corkscrews, and tying the high scores to Steam profiles.
User avatar
unsimp
Posts: 13
Joined: Tue Dec 27, 2022 9:37 pm
Location: Suffolk,England
Contact:

Re: My Audiosurf/Quest3D reverse engineering journey

Post by unsimp »

yeah i also play audiosurf and discovered this a while ago and didnt know you are a mattkc fan! Ive loved audiosurf since i got it and still try and play it with modern songs (although with really bad scores since i play mono and vegas not that well)
Heyyyyyy its un and i do random shit on the internet when im bored 3Head
My Github
User avatar
WinNT4SP6
Posts: 3
Joined: Thu Aug 24, 2023 12:54 pm

Re: My Audiosurf/Quest3D reverse engineering journey

Post by WinNT4SP6 »

I play Audiosurf every once in a while and it's never struck me that its technical background could be so complicated. Great reading!
PogChamp
Post Reply