Is it possible to encrypt a CSV file? Or delete it from the included game maker files?

Greetings, Game Makers.

I have a language localization CSV file inside the included files that I do not want users opening and checking out the content from.

Not being able to find a solution to make the CSV file secure, I made a script to run it the first time of the game, then pass all the information to a ds_secure_map, then delete the CSV file...
However, the CSV file does get deleted, even though it exists and Game Maker finds the file.

I have checked to see if GM finds it with:
if file_exists("localization.csv")
{
show_message("File exists");
file_delete("localization.csv");
}


1. Is there a way to encrypt a CSV file or make a CSV file locked?
If not...
2. Is there a way to delete included game files from the game install directory? (not from where other game files like saves or options, or ini files are stored)

Has anyone got insights on how to do any of those 2 things? Or any better suggestions for this situation.
Thanks in advance.
 

TsukaYuriko

☄️
Forum Staff
Moderator
1) You can encrypt any file you have I/O permissions for. You can't "lock" a file as described in your post.
2) No. Even if this was possible, users could read this file before starting the game for the first time.

Stick with the included file, but encrypt that file - the one that's added as an included file, not a local storage copy of it or anything - before the game is compiled. Then, load it into memory, decrypt it, read from it and pray that people aren't desperate enough to read it via a memory editor, and you have 99.9% of read attempts covered.
 
Thank you so much for the reply.

Do you know if GM2 would be able to open a password protected zip and use the CSV file inside of it?
I'm not able to find evidence on this.

I have thought of a solution for the final build using the ds_map secured file:
- have the csv file run in GM and use ds_map_secure command to produce a secure ds_map file
- delete manually CSV file from the GM project included file
- have some code to use the new secure file that replaced the csv file.

I will try this later.
 

rui.r6o

Member
Thank you so much for the reply.

Do you know if GM2 would be able to open a password protected zip and use the CSV file inside of it?
I'm not able to find evidence on this.

I have thought of a solution for the final build using the ds_map secured file:
- have the csv file run in GM and use ds_map_secure command to produce a secure ds_map file
- delete manually CSV file from the GM project included file
- have some code to use the new secure file that replaced the csv file.

I will try this later.
Using the ds_map_secure function won't work, from the documentation:
This function will save the contents of the given DS map to a file that is linked to the device it was created on (meaning it can't be read if transferred to any other device).
Regarding the password protected ZIP archive: the code used by GameMaker itself might be able to handle password protected archives, however that is not exposed to us users as you can see that only the zip_unzip function is available and there is no mention on how to supply a password in the documentation.

So as mentioned you can just encrypt the file before including it in the game. Take a look at the marketplace for several encryption options. You can even apply a simplistic encryption algorithm like a simple XOR, as that will be enough to deter most users from snooping. After all, any user willing to break the encryption will always find alternative paths to view the file (memory inspection, extracting the encryption key from the executable, etc).

If you don't care about users viewing the contents but just about users editing them then you could also just compute the hash (using one of md5_file or sha1_file) and store the expected hash in the game code. Then you would compute it at game startup and compare the actual hash against the stored hash to check for file tampering. Again, dedicated users would find ways around this, but it works as a simple deterrent.
 

gnysek

Member
Sadly, there's no elegant way to include files like JSON or CSV (or any other text format) inside game but not as separate file which will be visible on disk, to read data "from inside" of GM resources list, to prevent users from easy previewing file. Of course you can create script file and return string from there, but this will be far from optimal. I'm also missing better way, a "text" read-only resources. This could be used for storing databases with items, maps, levels, dictionaries, etc.
 
Hey, guys. I would like to thank you all for your time and efforts.

As rui.r60 said, ds_map wouldn't do it.

I managed to solved this doing as TsukaYuriko proposed. Thanks a lot, TsukaYuriko! You are a life saver.

It might still be exploitable and there might be more elegant/smarter/more efficient ways, but this is better than nothing.

I used this free extension, hope it helps you guys.

Here are the steps I took. Hopefully this will help someone else too:
1. Take the regular CSV file and encrypt it to a new file.
2. Open new encrypted file.
3. Take necessary code out of it.
4. Encrypt it again at the end of the use.

Note:
Run this once only at the start of your game before any of the code inside the required file is used.
The file must be decrypted before use and immediately encrypted after use.
The file is accessible/readable to anyone while it is decrypted .
Before releasing a build YOU MUST:
- Remove the unprotected file from the project datafiles folder so that it is not included in the build;
- Add the encrypted file to the project datafiles folder;

Wish you all a great day.

Here is the code:
GML:
// script Translation();

enum LOCAL { PT, ESP, EN, FR, ITA, DE };
global.local = LOCAL.EN;
global.enckey = "#notmyrealglobal.enckey";

InitTranslations();

function InitTranslations()
{
    // Unprotected file
    // This must be disabled when doing the final build
    if file_exists("localization.csv")
    {
        global.locData = load_csv("localization.csv");

        var hh = ds_grid_height(global.locData);
        var translations = ds_map_create();

        for (var i = 0; i < hh; i++)
        {
            ds_map_add(translations, global.locData[# 0, i], i);
        }
        global.translations = translations;

        // Encrypt Translation File
        // Encrypting to a different file
        file_fast_crypt_ultra_zlib("localization.csv", "localization_enc.csv", true, global.enckey);
    }

    // Decrypt Encrypted File
    if file_exists("localization_enc.csv")
    {
        // Decrypting encrypted file to the same file:
        file_fast_crypt_ultra_zlib("localization_enc.csv", "localization_enc.csv", false, global.enckey);   

        global.locData = load_csv("localization_enc.csv");

        var hh = ds_grid_height(global.locData);
        var translations = ds_map_create();

        for (var i = 0; i < hh; i++)
        {
            ds_map_add(translations, global.locData[# 0, i], i);
        }
        global.translations = translations;       

        // Encrypting to the same file:
        file_fast_crypt_ultra_zlib("localization_enc.csv", "localization_enc.csv", true, global.enckey);
    }
}
 
Top