GMS 2.3+ [SOLVED]Saving encrypted INI files to the Steam Cloud

gdkid

Member
Hi guys

I use Steam Cloud to save and load the player data and stuff.

However, the data is saved using INI file, which is very easy for players to modify.

Is there a way to encrypt an INI file before uploading it to Steam Cloud ?

There's one possible solution using the Steam Auto-cloud, however, I wonder if there's another solution for this.

Thank you so much
 
Last edited:

Nocturne

Friendly Tyrant
Forum Staff
Admin
GameMaker doesn't have any encryption functions built in but you can find some here that you could use: https://www.gmlscripts.com/script/Computation/Data_Encryption/

That said, you could also use the built in encoding functions. This will make the INI contents unreadable to the human eye, which in most cases is more than enough to put of a casual cheater. I mean, ANYTHING you do will be hackable if the person is willing to put in the effort, so generally just simply encoding the data in this way is enough. SO, you'd create the INI, then when you call ini_close() save the string (ini_close returns a string of the ini data). This string would then be encoded using base64_encode(). You'd then write this string to a file and save THAT (and probably delete the original INI file). TTo load it again, just read the string from the file, decode it, and then use ini_open_from_string() to read the contents.

I would say though, that the best solution for simply making a file unreadable is to use a buffer rather than an ini file. Buffers have their own save/load and encode functions, so they're generally easier to use and the buffer file can be used easily within the Steam systems (I've done this myself with all my games on Steam).
 

gdkid

Member
Wow, thanks @Nocturne for your answer and solution.

By the way, do you have any tutorial on how to use buffer to save/load game data? It looks very promising!
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
I don't have any tutorial off the top of my head that I can link to, but they are actually REALLY easy to use. Here's an example from my current game of how I'm saving game options:

GML:
function menu_save_options()
{
    // Create the buffer and save the options
    var _buff = buffer_create(1, buffer_grow, 1);
    buffer_write(_buff, buffer_bool, global.Sound);
    buffer_write(_buff, buffer_f16, global.Music);
    buffer_write(_buff, buffer_bool, global.Fullscreen);
    buffer_write(_buff, buffer_bool, global.Transitions);
    // Further buffer write stuff here...
    // Then encode the buffer and store it as a string
    var _bufferstring =  buffer_base64_encode(_buff, 0, buffer_tell(_buff)); 
    // Now open a file and save the string
    var _file = file_text_open_write("save.dat");
    file_text_write_string(_file, _bufferstring);
    file_text_close(_file);
    // Clean up the buffer   
    buffer_delete(_buff);
}
Now, to read the data back, you would simply reverse the process:

GML:
function menu_load_options()
{
    var _file = file_text_open_read("save.dat");
    var _bufferstring = file_text_read_string(_file);
    file_text_close(_file);
    var _buff = buffer_base64_decode(_bufferstring);
    // Check the buffer exists before reading from it
    if buffer_exists(_buff)
        {
        global.Sound                = buffer_read(_buff, buffer_bool);
        global.Music                = buffer_read(_buff, buffer_f16);
        global.Fullscreen            = buffer_read(_buff, buffer_bool);
        global.Transitions            = buffer_read(_buff, buffer_bool);
        // read in the rest of the buffer data, making sure you do it in the same order it was written, and using the same data-types (ie: buffer_bool, buffer_u8, etc...)
        buffer_delete(_buff);
        }
    else
        {
        // Deal with a failed load and set the options to a default value
        } 
}
So, with this system you have a save file called "save.dat" which contains the buffer data that has been base64 encoded, and as such it's pretty secure. Since buffers use binary data, they are already kind-of-encoded, and then using the base64 encode on top of that just adds and extra layer. Btw, if you decide to test any of the code above, check the manual for the functions!!!! I've written this from memory so some of the function names/arguments may not be quite right... :p

Hope that helps!
 

gdkid

Member
It looks great to me, thanks @Nocturne

Now I know about the basic steps I can find ways to make it work.

P/S: Your answer really made my day, I've been reading your post since I started studying Game Maker Studio. Thank you for taking your time to answer my thread :D
 

gdkid

Member
Oh, and one more thing, if you don't mind

Does buffer support array?

For example, I also use array to save/load my list of achievement

GML:
for (i=0;i<global.maxach;i+=1;)
            {
                ini_write_real("Achievements",global.ach[i,2],global.ach[i,0]); // global.ach[i,2] : name of the achievement, global.ach[i,0] : state (lock/unlock)
            }
Is there a way I can do this using buffer?
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
There is no "native" support to save an array into a buffer, but there are ways around it. The simplest would be to use a for loop (or two of them for a 2D array) and write each value into the buffer. However, a better method may be to convert the array into a JSON string using the json_stringify function, and then save the string to the buffer as a single buffer write. You can then read the string back from the buffer, and convert it once more from JSON into an array using the json_parse function.
 
Top