Where I left off
Catch-up in three sentences. I’m rebuilding the server for Fury, a PvP game that pulled the plug on its servers in 2008. All I have is the client, the program that ran on players’ PCs, and the last few weeks I’ve turned its compiled game code into ~2,000 files of readable script. Last post I found the server logic sitting inside that code, switched off.
Code is only half a game though. The other half is data: how much a fireball
costs, what’s in a shop, how big each map is. In Fury that lives in a folder
called Content/Export/Client/, and it’s 197 files with a .bin extension and
no documentation anywhere.
Time to open them.
What’s actually in the box
So a .bin file is just “bytes, format unspecified”, and the extension tells
you nothing. Could be anything. The first thing you do is dump one as hex and stare
at it. Here’s the smallest interesting one, ABI_DamageTypes.bin, all 87 bytes:
| |
That left column is the position in the file, the middle is the raw bytes in hex, the right is those same bytes as text (dots where the byte isn’t a printable character). Almost none of it is text, so this isn’t a config file someone typed. It’s a structured format, packed by a program, for a program.
The way you read one of these is you find the numbers that are obviously counts or sizes and see if the rest of the file lines up behind them. First twelve bytes here are three 4-byte numbers: 17, 42, 5. The file is 87 bytes long. 42 is a position inside it. And if you jump to byte 42, the rest of the file is exactly five identical 9-byte chunks. Five rows. It’s a table.
That’s basically the entire format. A header, a one-line schema that says “this table has 3 columns: a byte, then two 4-byte integers”, a lookup table for fast searching that turns out to be a copy of information already in the file, and then the rows. Strings, when a table has them, are just written straight into the row and capped with a zero byte: English as plain bytes, other languages as two-bytes-per-letter.
Six column types in the whole set: yes/no, small number, big number, decimal, text, foreign-language text. That’s it. Whoever built this kept it lean.
The part where it just works
I wrote a decoder, a script that reads the header, reads the schema, then walks the rows. The test I trusted: run it on all 197 files and check that it lands exactly on the last byte of every single one, no leftovers, no running off the end. It did. First real try, after I fixed one wrong assumption about a multi-part key. 197 for 197.
Then a bonus I wasn’t expecting. Remember those 2,000 files of decompiled script? Buried in them are definitions like this:
| |
That’s the game’s own description of a row in CMN_GameMaps.bin, and it has the
field names. 24 of them, and they line up one-for-one with the 24 columns my
decoder found. So I could match every .bin table to its struct by shape and
borrow the names. 191 of the 197 now come out with real headers, like
challengeCost, teamSizeRequirement, bCanDuel, instead of “column 14”.
The character-creation map, decoded, first rows:
| |
BB is Bloodbath. AVA_Creation is the character screen. There are 49 maps in
there, a couple marked _DEPRECATED, which is a nice little fossil: someone
retired a map and left the tombstone in.
[SIDE NOTE] the base English string table has entries like CHANGE ME still
sitting in it, and every string that never got translated has a placeholder in
the other-language columns that’s just… the English run through a Pig Latin
generator. “ANGECHAY EMAY”. Shipping game. 2008. I love it.
The half that isn’t here
Here’s the thing I keep coming back to. The folder is called
Content/Export/Client/. Client. And the code that opens these files has a
line in it that picks between two folders:
| |
There is no Server/ folder in what I have. It was on their machines.
So I made a list. Every table the code knows how to load, minus every table that’s actually in the box. 87 tables come up missing. And it’s not leftovers. Look at what they are:
- All 39 combat-effect tables. The client ships the tooltip for every
ability, the words you read on hover. It does not ship the numbers behind
them. How much a hit actually takes off, how long a stun lasts, how a shield
stacks:
Server/only. - The server’s ability table has 80 columns. The client’s version has 58. The 22 it drops are exactly the combat-relevant ones: damage scaling, aggro, interrupt rules.
- Every monster table. Templates, spawns, patrol routes, the lot. The whole data side of PvE.
- The reward maths. Score-per-kill tables. Bot config.
I already knew from earlier digging that the code to run a match is in the client. Turns out a lot of the data that code needs to feel like Fury, specifically the entire feel of combat, never left Auran’s building.
That’s not a wall. Every one of those 87 missing tables still has its struct in the decompiled code, so I know the exact shape of every one: what columns, what types, what they’re called. I know precisely what I have to rebuild. It’s just going to be a lot of it, and a lot of it will be me guessing numbers and tuning until a fight feels right instead of reading them off a file.
Which, honestly, might be the most fun part.
Next up
Phase 2, reading the client and cataloguing everything, is basically done. I’ve got the code, the data, and a map of the server-shaped hole. Next is writing that all up properly and then actually starting Phase 3: standing up a tiny arena server from scratch and getting one unmodified client to connect to it and move a character around. That’s the first moment this stops being archaeology and starts being a game again.