#๐ Binary editor for DS game saves
70 messages ยท Page 1 of 1 (latest)
@mystic juniper
Remember to:
- Ask your Python question, not if you can ask or if there's an expert who can help.
- Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
- Explain what you expect to happen and what actually happens.
:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.
whoops, .sav isn't allowed here, sorry. here's the original message:
so, i've been looking at making a save editor for ds games for a while now. i'm not really sure if python is the best candidate for this, but it's all i have any experience with.
as far as i know, most ds games save their player data in binary format, completely up to the developers of each game (see file for an example, Need for Speed ProStreet, i have this one mostly mapped out in a spreadsheet).
i've messed with it some time ago, wrote a terminal save viewer specifically for ProStreet, but the menu and navigation system was hard for me to wrap my head around, and i abandoned it. all i have that's relevant are a script that updates the save's checksum (so i can modify things for testing), and a script that converts the game's textures to a standard format, like png (that one isn't related to saves though).
i'd love some guidance on how to properly start a foundation for an editor that can support more than one game (a plugin system? maybe). i know it'll be a lot of manual work to add support for each game, and it's all very niche... i just think it's neat
"binary format" can mean anything (technically everything is binary data, "binary format" is used for anything that can't be read as text - even .png is "binary format"), so unless you actually know the specs of the save file, there's nothing we can do
It may be just some kind of memory dump of the objects, it may be a more structured form but encoded in some way...
that's fair, i can try to be a little more specific
Even for such simple thing as numbers, could be big endian or little endian... And that changes a lot
>>> int.from_bytes(b'\x01\x02')
258
>>> int.from_bytes(b'\x01\x02',byteorder='little')
513
And that's for 2-byte test, numbers are usually stored as 4-byte
it's the latter so far. prostreet stores race times, best finish positions, which race days are locked/unlocked/won/dominated, cars and parts owned and equipped, high scores for a minigame, which locations are unlocked for quick races, user settings, the list goes on
it's all little endian on the DS
and seems to be signed numbers even where it doesn't make any sense, for prostreet at least
The main way to figure something like this out would be to change some values in the save file, load it, and see what changed
It was just an example how without knowing details about a format, even such simple things like numbers are not that simple ๐
that is what i've done so far
That might just be because C ints are signed by default.
pokemon saves are pretty much solved for example with pkhex but if you have something that nobody else has done before, i wish you luck
i'm looking at how to make a "foundation", upon which support for individual games can be added
flip some bits/values and see what happens
universal i guess
yeah that can cause some silly shenanigans
try to be deterministic
as in leaving everything the same except one change
and see what changes in the save file after re-saving in game
run a diff
Are you using the struct module or something like it?
https://docs.python.org/3/library/struct.html#module-struct
With unsigned, you might also end up with negative overflow which is not as easily fixed as with "if <0, then make it 0" ๐
I've once played a game in alpha where a weak mob would one-shot high-geared players because after the calculations of all defense etc damage would end up negative... only for it to overflow
i was not, but i probably should
It helps with the common binary types.
some of the values (mainly car config) is not aligned to the byte
Awkward. You'll have to do some special stuff yourself there.
But that might be: grab a binary chunk, turn it into a big int, shift bits off it as needed. very use case dependent.
there are five levels of parts for each part, between two bytes, the first part could be 00011111 00000000 and the next part 11100000 00000011 , and so on (i think i formatted that properly)
where each 1 represents whether a certain level for a certain part is owned
Right. A bitmap.
ah
that name makes sense
support for each game on its own is one thing, but what about the platform to build this support onto? or should they all be their own independent scripts?
i don't even know if i want a gui or not
Personally I'd probably have a module (.py file) per game, to hold the binary format and the tools to decode 9and maybe encode) it.
I can't advise on GUIs, I find them very cumbersome.
Maybe make a dsgame package, with a module per game format, and modules for the front end (GUI of command line) etc.
I don't suppose each save file has some identifier for the game in some nice standard way? So that you can pick one up and know which decoder to use?
the few i've looked at so far have some data that never seems to change
prostreet's seems to be a 4 byte magic at the beginning
E7 B1 10 4B, not sure if it's supposed to represent anything in particular
I don't know if this is of any use: https://docs.fileformat.com/game/sav/
A magic is pretty much just a nice identifier for software to know what it's dealing with.
my script for updating the checksum, checks the file length and magic before touching anything else
I was just imagining having a bunch of classes, one per game-specific format, and a factory function which could open a file, look at the header/magic, and pick the right class for the rest of the decode/
makes sense. if the magic in ps is altered in any way, it pretends there is no save data at all and creates it anew
as opposed to altering anything else in the file, which makes the checksum not match anymore, and the game warns about corruption instead
You definitely want the magic header. Typically there will be a fixed part which essentially denotes "this is a DS game save" and then an additional variable part which says something like "this is for game X", or even "game X, version Y". And so forth.
Of course, there might be nothing very formal. I have in the back of my mind that once upon a tim the "binary save" of a M$ Word document was just a dump of Word's memory. Ghastly and unportable.
they don't seem to have a standard header for all ds games
Not even the very beginning? Alas.
I know nothing about these files, BTW. Just binary encode/decode in general.
Save files on most consoles are usually proprietary and company-dependent iirc.
yeah, the beginning is completely different between prostreet and, say, indy 500 legends
they use different checksum methods
prostreet's is per-save slot so you can lose one to corruption but keep the other, indy's is across the entire save, so you'd lose all of it
prostreet's just counts all the bytes in a fancy way, indy's is crc32
super mario 64 ds seems to keep two copies of all data, from what i remember
no consistency
they can't make it easy can they?
i'm sure there are some consoles where all the games' saves follow a standard
the ds is not one of them
I'd imagine that even if they do the standards aren't public so it's pretty much up to reverse engineering to do its magic.
This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.