Save game states

Whats the best method to save your game states? And how can you make files which can’t be easily modified? ^^

1 Like

It depends on whether you want to save to SD card or EEPROM, and how much data you’ve got to save.

If you want to use EEPROM and your data is reasonably small then your best bet is to make a class/struct filled with all the necessary data and use EEPROM.put.
E.g.

struct SaveData
{
  uint64_t score;
  char name[10];
};

// Somewhere in a function:
SaveData data;
// data is filled out
// data is saved:
EEPROM.put(address, data);

And likewise it can be read with get

SaveData data;
EEPROM.get(address, data);
// use data

Even for larger files, using structures can be very helpful when using EEPROM.

For SD saving, there’s no avoiding a bit of byte manipulation.
If you write yourself lots of helper functions to break the structure down then it becomes easier.

I haven’t actually got round to reading up on the SD card API yet so I can’t provide an example for that one.

When it comes down to it though, you can think of most data stores as an array of bytes if it makes things easier.
In fact sometimes it’s easier to write to an array of bytes and then save that array to file.

(The short version: don’t worry about it, it probably won’t happen.)

Don’t release your source code. :P

Seriously though, even closing your source code is only a deterrent and a sufficiently motivated/educated person would figure it out given time.

In general the best solution is to just open source your code and trust people not to go file hacking.
In my experience, people who like consoles like the Pokitto and Arduboy generally won’t bother hacking files when the code is open source.

If you open source your code then nothing will physically stop someone poking around and understanding your file structure.
Hence making the source available removes pretty much any challenge that hacking the file might present, meaning hacking the save file is no longer an achievement worthy of note, and so generally people won’t bother.

Also I find when people want to share their scores, most people would rather earn those scores.
If people decide to cheat, they only end up cheating themselves out of a sense of achievement.

2 Likes

Whats EEPROM? And can you reset it? And is there a potential of loosing data?

1 Like

EEPROM is ‘Electrically Erasable Programmable Read Only Memory’
(though nobody ever remembers that, I had to look it up :P).

Despite its name, you can write to it as well as read from it.
Basically it’s like RAM, but it will persist even when the Pokitto is turned off.

The Pokitto has 4KB of EPPROM (4096 bytes).
EEPROM does have a limit to the number of times it can be written to,
but you don’t have to worry to much about that.
If I’m reading the data sheet correctly then the Pokitto’s EEPROM has a minimum of 100,000 write cycles guaranteed and typically can achieve up to 1,000,000 write cycles.

You can write whatever you want to it, but so can other games,
so you can’t guarantee that another game won’t overwrite your save area.
There are mechanisms you can put in place to help detect that, but each one has its benefits and drawbacks.

The Pokitto’s EEPROM interface is a direct copy of the standard Arduino EEPROM interface,
which is documented here,
and the source code can be read here.

There have been discussions about creating a system for better EEPROM management,
so that games don’t overwrite each other’s data,
but so far that’s still in the ideas stage.

1 Like

Are there any games which saves to EEPROM so far? Maybe I try that later :smile:

There are.

I can’t give an exhaustive list, but I know Armageddon does at least because I had an issue where another game had written to the area Armageddon uses to store its high score table, so I ended up with a stupidly high highscore that I couldn’t possibly hope to beat.

1 Like

That’s the issue when saving to the EEPROM. You have to either coordinates with other game devs or to ensure the data isn’t corrupted (maybe through a unique ID as the first bytes?).

I’ll go with file save myself when I’ll make my first pokitto game.

1 Like

If i look at the R4 cards that you can use with a Nintendo DS, they all use the sd to store a .SAV file where the savegames are stored.

Assuming nobody uses Pokitto without a sd card, this would be a really simple solution. Every game would have its own .sav file like : PokittoGame.sav on the sd

1 Like

The problem with that is that it only guarantees that the ID hasn’d been altered.
If another game writes its save data into the middle of your data without touching the ID then you’d never know.

One good way to detect that your data is unaltered is to use a checksum.
That way if someone’s overwritten part of your data then the checksum hopefully won’t match up so you can give a “data is corrupted” message.


@carbonacat & @Rakkachi
Personally I agree that just assuming people will use an SD card with their Pokitto would be easiest.
But I’m sure I remember @jonne once saying that he’d rather most games were playable without needing an SD card or something along those lines (I may have misremembered, it was a while ago).

Either way, we do need a better EEPROM system, otherwise the EEPROM would go unused, which would be a big waste since there’s 4KB of it.
There’s a thread dedicated to solving that problem though:

2 Likes

idk if this is going to be helpfull but heres what i did to save the player position in pokitbeasts
this needs SDFileSystem library to work, but gives you folders which helps allot keeping your save separate from the other games

#include "SDFileSystem.h"
SDFileSystem sd(P0_9,P0_8, P0_6, P0_7,"sd");

int x,y;

void save(){
	if(opendir("/sd/pokitbeasts.pok")==0) mkdir("/sd/pokitbeasts.pok", 0777);
	FILE *fp = fopen("/sd/pokitbeasts.pok/save.data", "wb");
	if(fp == NULL) //error
	else{
    	uint16_t buffer[2] = { x , y };
    	fwrite (buffer , sizeof(int16_t),  2, fp);
    	fclose(fp);
    }
}
void load(){
	if(opendir("/sd/pokitbeasts.pok")==0) // error no folder
	else{
		FILE *fp = fopen("/sd/pokitbeasts.pok/save.data", "rb");
    	if(fp == NULL) // error no save to load
    	else{
    		uint16_t buffer[2];
    		fread(buffer, sizeof(int16_t),  2,fp);
    		x = buffer[0];
    		y = buffer[1];
    		fclose(fp);
    	}
    }
}
```
3 Likes