I recently treated myself to a Powkiddy RGB30 handheld console. It’s a great little device with an unusual square screen, which is perfect for playing Pico-8 games on (which is something else I’ve been getting into recently - more on that in another blog post!). Anyway, it turns out it’s also really good at running emulators, so after a bit of playing with it I inevitably ended up revisiting probably the favourite game of my childhood: Sensible World of Soccer (or SWOS for short).
Of course being a programmer I couldn’t just play it; I had to tinker with it first (is this Programmer’s Curse? It is now). I knew that a bunch of enthusiasts had been releasing updates to the data files long after the game’s final release (which was the ‘96/‘97 season), and I was not surprised to find that they still do. So I knew that I had to grab the latest season.. but what else? Back when I first played it, SWOS blew my mind for seemingly having every single team in the world on it (on two floppy disks, no less). Sadly that’s not quite true though - despite having 700 teams, it only has 3 Asian leagues and Singapore isn’t one of them. So I decided to challenge myself to see if I could add Singapore’s premier league to the game.
Getting the latest database
First of all, I needed somewhere to start from. I read that SWOS 2020 had just released an update for the 23/24 season - great! However, the Amiga version wasn’t released yet (which is the one I wanted to play on my emulator). And while the Windows frontend is free to download, I couldn’t find a way to just get the team files. So I ended up installing SWOS 2020 and running the updater, which downloaded the latest database locally (into SWOS’s
data directory), which I could then use on the Amiga version… sort of.
Editing SWOS on the Amiga
The main difference between the Amiga and DOS versions of SWOS from what I can tell are that the Amiga’s data files are compressed. I guess that’s how they managed to pull off fitting so many teams onto floppy disks. Doing some digging, I found that the program used to compress the files is called RNC ProPack (you can see the string ‘RNC’ in the header of the files). Fortunately, the source of this program is now available on GitHub - a single
.c file that I could easily compile locally using
cc. I considered reimplementing it in Go, but decided it wasn’t really necessary.
Basically, SWOS’s data directory is full of
TEAM.XXX files where XXX is the number of the country those teams represent (eg.
008 is England). Amiga ROMs are typically compressed as
.lha, but you can extract them to see these files. So to convert the SWOS 2020 database to Amiga files, you just need to compress them using RNC ProPack, for example
pp p /swos2020/data/TEAM.008 /roms/swos/data/TEAM.008, changing paths as necessary. So the full process I followed was:
- Download PC and Amiga versions of SWOS
- Install SWOS 2020
- Download and compile RNC ProPack
- Run SWOS 2020 and locate the updated data files
- Extract the Amiga ROM into a temporary location
- Compress the desired files using
ppinto the Amiga ROM’s data dir (I only decide to update England)
- Compress the Amiga ROM again to the original format
After this, I had the Amiga SWOS updated to the 23/24 season, but how could I add Singapore?
Editing a League on SWOS
I’m not an expert on hacking/hex editing old programs, but one thing that seems to be a rule is that if you modify anything out of the bounds of what the original program expects, it’s likely to crash. This means that you can’t really add a new league to SWOS (without difficult changes to the executable), and you can’t change the number of teams in a league either. But you can change existing leagues! So I decided I’d go ahead and replace the Chinese super league with Singapore’s premier league. China itself was only added in SWOS 2020 - it replaced Taiwan from the original version of the game. Although the league only has one division (like Singapore), it has 12 teams while Singapore only has 9. Everything I read said that changing the number of teams wasn’t possible, so I opted for an easy solution: add 3 teams from Malaysia to complete the division.
The binary format of the SWOS team files has been mostly reverse-engineered and published online - I followed this specificaiton. It starts with a header (number of teams), then for each team it has a team header (team name, kit colours etc), and each team has the players (name and number and 7 skill attributes). Rather than edit the existing file for China, I decided to recreate the entire thing from scratch (using dummy data at first). This took a lot of trial and error, because there’s no validation of any kind - if I made any mistake in the format, I knew it wouldn’t work. Fortunately the game didn’t crash completely, it just had messed up player names and numbers etc, so it was quite easy to see where I had made mistakes (for example if the first team in the file looked correct but the second one didn’t). What helped the most was counting the size of each team and verifying it against the expected size - my mistakes were usually because I’d used the wrong number of bytes for some field.
After a few failed attempts, the game successfully loaded with my modified data and I was able to play a game! I was feeling pretty good, now it was just a case of sourcing the player data from Singapore.
Getting Football Player Data
I discovered that while there’s no readily available, open data for football teams (at least not for global teams), many projects seem to rely on scraping [Transfermarkt](https://www.transfermarkt.com](https://www.transfermarkt.com/). I hadn’t heard of this site before, but it has an amazing wealth of information on pretty much every football team there is, including more than enough than I needed from the Singapore football league. There’s a project called transfermarkt-scraper on GitHub; at the time I tried it, it wasn’t able to get the player market values, but it was a simple patch to get it working.
The data from Transfermarkt took a bit of processing for SWOS, for example:
- Player names had to be converted to ASCII and truncated to 22 characters
- Numbers had to be changed to 1-16 (higher numbers result in weird bugs, like glitched sprites instead of numbers above the player heads)
- The values had to be converted into SWOS market values (relative to 96/97).
Then there were two more areas that took a bit of work:
- Generating skill attributes for the players
- Selecting 16 players from the entire squad
For the skill attributes, I wrote a randomiser based on the transfer value and the player position. It doesn’t really matter - the game is playable with whatever skills you use - so I just played around with it until I got something that looked reasonable.
For the 16 players, I fixed every team to a 4-4-2 formation, then tried to find a player for each of the roles. If I couldn’t find a match for a given role (for example left-back), I’d try other positions in order (eg. defender, right-back, midfield) to find the closest player who could fit. If there were multiple matches, I’d choose the one with the lowest squad number (thus assuming that means they’re likely to be in the first team in real life). After the initial 11, I’d fill the bench with at least 1 goalkeeper, 1 defender, 1 midfield and 1 attack, then take whatever was left. This seemed to work well enough for the 12 teams I had.
If I wanted to change any of the teams manually, I could do an intermediate step of editing a JSON file before writing the binary format, but I decided the automated version was good enough for me. If I was releasing this for real I’d likely do the whole thing manually - there’s only 12 teams after all.
The only things I had to do manually were the kit colours and coach names, which I sourced from Wikipedia. For each team, I mapped the kit to the closest SWOS style and colours (there’s a table in the specification link above) and hard-coded a mapping to augment the team data. That was pretty much it - my Singapore league was complete!
Divide By Zero
I thought I had everything working, so happily kicked off a new career as coach of Hougang United, played through a full game and then… Crash!. I got an Amiga Workbench screen with an error saying ‘divide by zero’. This gave me a sinking feeling, because I had no idea what I’d done wrong. Everything seemed fine while I was actually playing a game, so I’d done everything correctly, as far as I knew.
This had me stumped, so I just tried again with a different team, and to my surprise it didn’t crash! Then I tried a few more teams and noticed a suspicious pattern: all the Malaysian teams worked, most Singapore teams crashed. This gave me an idea, because I knew that the Malaysian teams were likely ‘better’ (no offence) and upon closer inspection of the Singapore teams, the better were indeed the ones that worked (like Forrest Li’s Lion City Sailors with million-dollar star players…). So I adjusted the formula where I was generating the values of the players and bumped it up a bit, making every player worth a bit more. Suddenly that fixed it, and no teams were crashing!
I don’t know exactly what the ‘divide by zero’ error was, and it’s not really possible without access to the source code of the game. But given that increasing the skills and valuations fixed it, I would assume that the game is hitting a case where it is never expecting to get a zero value. So perhaps it was a team where no attackers had a ‘shooting’ attribute of more than zero, or something like that. In other words, the teams I had created in my Singapore league were probably the worst in the game, and so bad that they caused the game to crash. I found that amusing.
Is it Fun?
It was definitely a fun experiment getting the Singapore league into SWOS, and seeing modern Singaporean players in an almost 30-year old game was fun. But the league itself doesn’t really make for a fun play through in career mode. Firstly, there’s only one division, so there’s no real progression for the teams: you can win the league and the cup, and that’s it. Unlike a career in England for example, there’s no promotion, no job offers from higher divisions, no purchasing players from higher divisions etc.
Secondly, a nice feature of the original game was that you could eventually get offered the job of the national team. Although Singapore does have a national team, I don’t think it’s possible to manage them - the Singapore league is still really China under the hood, and you can’t choose Singaporean as a nationality for your manager. Also, Singapore isn’t in any of the competitions, so there’d literally be nothing to play for. Apparently it is possible with some more data modifications to substitute Singapore with another country, but I couldn’t find out where exactly to make that change, and that seems a bit too fake anyway.
I think I’ll stick to a playthrough with Blackpool’s latest squad instead, but maybe I’ll try and poach a few players over from the Singapore league for fun…