Cached at:
08/21/26, 12:49 AM
# Decoding Magic School Lunar's Save Game Format
Source: [https://www.mistys-internet.website/blog/blog/2026/08/01/decoding-magic-school-lunars-save-game-format/](https://www.mistys-internet.website/blog/blog/2026/08/01/decoding-magic-school-lunars-save-game-format/)
As I’ve mentioned in a[previous post](https://www.mistys-internet.website/blog/blog/2026/03/27/forget-spreadsheets/), I’ve been working on a fan translation for a Sega Saturn game called*Magic School Lunar\!*As I get closer to the end of the process I’ve been doing more playtesting, but since I’m only testing the script at this point I found myself wanting to cut out the parts of the game I don’t really need to be testing right now\. This game has an infamously high random battle rate; at least half my time playing the game is just going through battles[1](https://www.mistys-internet.website/blog/blog/2026/08/01/decoding-magic-school-lunars-save-game-format/#fn:1), and there’s no reason to waste my time with any of that when I just want to check the script in\-game\.
I already know how to turn the encounter rate off, but that wouldn’t solve boss battles\. I’d need to be able to fight those for real,*and*since I’d be skipping all of the random battles I’d be going into each boss with underpowered level 1 characters\.
There are probably other ways to have tackled this, but I decided to go about it by working out the game’s save file format so that I could just start the game giving every character maxed out stats\. Being able to edit the save files further down the line might turn out to be handy\.
The Saturn, like other CD\-based systems, uses a memory card[2](https://www.mistys-internet.website/blog/blog/2026/08/01/decoding-magic-school-lunars-save-game-format/#fn:2)format with multiple saves per memory card, so I started by extracting the individual game save[3](https://www.mistys-internet.website/blog/blog/2026/08/01/decoding-magic-school-lunars-save-game-format/#fn:3)as its own file so that I didn’t need to worry about running into anything unrelated from other saves on the same cards\. I made a save game right at the start of the game, noted down my party’s stats, then started digging in the file to see if I could find them\.
…but I didn’t, which surprised me\. I started out looking for key stats like attack and defense, but none of the primary stats I wanted seemed to be present in the save game except HP and MP\. I was a little puzzled, but as I tried searching for other stats I realized what was happening: the only value I could find was the character’s total experience points… and that was also the only thing I*needed*to find\.
*Magic School Lunar\!*has no way for your character to raise their stats outside of gaining levels, and no way to gain levels outside of gaining experience\. It wasn’t storing the rest of those stats because it*didn’t need to*: given just your current experience pool, the game can calculate what level you’re at by checking where that puts you on the level curve, and then look up what your stats*should*be at that level\. I wasn’t finding the rest of those stats since they never really exist outside of memory\!
I’d been hoping to just put all my stats up to the maximum, so this wasn’t*quite*what I was after, but it’s good enough[4](https://www.mistys-internet.website/blog/blog/2026/08/01/decoding-magic-school-lunars-save-game-format/#fn:4)\. I could just give everyone the maximum experience to bump them up to the highest possible level, and that’ll be plenty[5](https://www.mistys-internet.website/blog/blog/2026/08/01/decoding-magic-school-lunars-save-game-format/#fn:5)\. So I edited one character’s XP, loaded it in the game, and… it didn’t work\.

I wasn’t all that surprised, though\. Save game formats often have some kind of integrity protection feature, either to protect against cheating or corruption, so I’d been expecting something like that here too\. For a game like this it would almost certainly be using a simple checksum, which is usually just handled by literally summing all of the bytes in the data and storing that somewhere in the file\. It’s not a terribly secure hashing routine, but for something like this it doesn’t need to be\.
So I tried loading the save, sitting for a minute, and then saving again\. I figured that changing nothing else, not even moving my characters, should minimize the number of unrelated changes in the file and help me isolate the checksum\. Unfortunately, as it turns out, it did that a little*too*well \- the new save only had four bytes different, and I quickly identified those bytes not as a checksum but as a 32\-bit integer at the start of the save tracking your playtime in seconds from the start of the game\. It’s useful knowing that’s*not*the data I wanted, I suppose, but why didn’t the checksum change?
So I tried something different\. I went out to fight a few battles, letting exactly one of my characters gain a few experience points\. I then tried comparing*that*save to the original one to see what changed\. Obviously I had a lot more values to sift through, but I started narrowing it down\. I also started producing some values to compare it to\. Betting that this is probably a simple checksum format, I tried actually calculating the checksum for a few different possible slices of data and comparing them in 16\-bit and 32\-bit integer formats[6](https://www.mistys-internet.website/blog/blog/2026/08/01/decoding-magic-school-lunars-save-game-format/#fn:6)to see if any of those values that had changed between the saves resembled them\.
This took me a little longer than I’d expected, mostly because I made a bad assumption going in\. Given the amount of data being summed here \(about 1012 bytes\), I presumed we were looking at at least a 16\-bit value\. Maybe it would reserve a full four bytes, even if it wouldn’t need that much space\. But as I started looking at potential hashes for the game, I noticed something\. There was*one*byte that did resemble the checksums I’d found… just one byte\.
As it turns out,*Magic School Lunar\!*really has reserved a single byte for the checksum\. It calculates a full sum over \(most of\) the save file, but then clips it down to only the least significant byte\. So, for example, if the sum of all bytes is 18279 \(`0x4767`\), then it takes only the last byte of that number \(`0x67`\)\. The checksum also doesn’t cover the*entire*file; it seems to exclude the first eight bytes, which act as a sort of header\. That header includes the playtime \(which I’d already discovered wasn’t checksummed\!\), the current chapter and average party level[7](https://www.mistys-internet.website/blog/blog/2026/08/01/decoding-magic-school-lunars-save-game-format/#fn:7), and the checksum itself\. Once I started checksumming everything*past*the first eight bytes, I was able to produce a checksum that matched what the game itself did and ensured it would be happy to accept my changes\.

So I tested it out and confirmed that, yes, the game was happy to accept my changes now\! As it happens, a single character starts the game with the maximum amount of experience[8](https://www.mistys-internet.website/blog/blog/2026/08/01/decoding-magic-school-lunars-save-game-format/#fn:8), so I even knew the maximum experience I could assign everyone else to max their stats out and make them as powerful as I could\.

To make these tweaks easier on myself, I threw together a quick and dirty save editor in Python\. I’ve also typed up my notes on where everything is located in the save file in case anyone else finds that useful in the future\. I’ve[put it up on Codeberg](https://codeberg.org/mistydemeo/msl-save-editor), and in the meantime I’m back to testing now that I never have to worry about winning a battle again\.