Professional game saving with binary files

GM Version: Studio (all)
Target Platform: Windows
Download: n/a
Links: n/a

Summary:
Hey. In this tutorial i want to teach you how game saving is made in professional games (using binary files). Im not an expert just want to share my experience with all of you!

Intro (Why use binary?)
But why all games are using binary files to save their data, here is the advantages over the text files (INI, JSON, etc.)
1. It is much harder to modify a binary file (to modify binary data you need to know the file structure, so editing your files can be almost impossible)
2. Binary files dont waste your memory (if you compare binary file with INI file for example)
"[Section] value=12345678"
Results:
INI file size = 24 bytes
Raw binary data = 4 bytes
3. Binary files are much easier to post-process (what i mean by "post-process" is data compression/encryption. Because of digital representation you chose - it is similar to how your computer memory organized and it is much more easier for your program to modify data)

Practice
Before we start you need to know the basics of memory organization. You should know what is bit, byte, bit shifting/multiplication operations are.
Also you need to know the precision of data types ranges. Here is the good link for you : https://docs.microsoft.com/en-us/cpp/cpp/data-type-ranges?view=vs-2019
So,
Lets image you have a set of data - integers and strings.
Here is saving data sample:
GML:
//* DATA SAVING *//

var byte = 120;
var short = 16000;
var int = 283400;
var str = "Hello world!";

// Open binary file (0 - reading, 1 - writing, 2 - both)
var binf = file_bin_open(working_directory + "/file.bin", 1);
// Writing first number
// 1 byte is enough (because our number falls in 0-255 range)
file_bin_write_byte(binf, byte);
// Writing second number
// 1 byte is NOT enough
// 2 bytes are enough (because our number falls in 0-65535 range)
file_bin_write_byte(binf, (short >> 8) & 255);
file_bin_write_byte(binf, (short & 255));
// Writing third number
// 2 bytes are NOT enough
// 3 bytes are enough but we use 4
file_bin_write_byte(binf, (int >> 24) & 255);
file_bin_write_byte(binf, (int >> 16) & 255);
file_bin_write_byte(binf, (int >> 8) & 255);
file_bin_write_byte(binf, (int & 255));
// Writing string
// Here is the deal :
// We first write string length, and then character by character write string to file
file_bin_write_byte(binf, string_length(str));
for (i = 1; i <= string_length(str); ++i) { // start from 1 because of string_char_at()
    var c = string_char_at(str, i);
    file_bin_write_byte(binf, ord(c)); // get Unicode value for our symbol
}
file_bin_close(binf);
Here is reading data sample:
GML:
//* DATA READING *//

// Open binary file (0 - reading, 1 - writing, 2 - both)
var binf = file_bin_open(working_directory + "/file.bin", 0);
// Reading first number
// As we know, we wrote only 1 byte - so read it
var byte = file_bin_read_byte(binf);
// Reading second number
// Read 2 bytes
var short = 0;
short |= (file_bin_read_byte(binf) << 8);
short |= (file_bin_read_byte(binf));
// Reading third number
// Read 4 bytes
var int = 0;
int |= (file_bin_read_byte(binf) << 24);
int |= (file_bin_read_byte(binf) << 16);
int |= (file_bin_read_byte(binf) << 8);
int |= (file_bin_read_byte(binf));
// Reading string
// Read string length first (we need to know how many bytes we need to read for our string)
var str = "";
var str_length = file_bin_read_byte(binf);
for (i = 0; i < str_length; ++i) { // start from 1 because of string_char_at()
    str += chr(file_bin_read_byte(binf)); // read character by character and store bytes in string
}
file_bin_close(binf);

// DEBUG
show_message(string(byte));
show_message(string(short));
show_message(string(int));
show_message(str);
P.S.
Here we go - you have implemented binary saving in your game. As you can see this is extremely easy! Tell me if you have any question and if you liked my tutorial.
 
Last edited:

rytan451

Member
Nice tutorial. Just a quick note, it's probably a bad idea to read data from a file, byte by byte. Instead, you should probably load more of the file into a buffer, and read the data from the buffer. This also removes the need to parse strings, ints, and floats, since GMS's buffer system handles that.
 
Top