• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

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.
 
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.
Yeah, you are right. But this tutorial works also for older GM versions (8.1, 8 etc.).
 

rytan451

Member
Writing data, byte-by-byte, to an SSD can actually cause permanent damage to the SSD, beyond being slower than writing the data as a buffer. That's one of the primary reasons why I prefer using a buffer over writing byte-by-byte.
 

Sedgwick2K

Member
Yes, I've used a similar thing in my uncomplete demo Undead. I made it in a such way the game has three different savedatas at the same time (one for each game mode).
 

Rin~

Member
what does the >> and & operator do? why are you writing multiple times in order to save the number? is this written so that one number or string is one entire file? how would you save multiple numbers to the same file and differentiate between them when reading? what's the relationship between the number 1 and
`string_char_at()`? your tutorial assumes the reader knows more that the average person looking up how to do it.
 

rIKmAN

Member
what does the >> and & operator do?
Have a read up of bitwise operators in the manual.

your tutorial assumes the reader knows more that the average person looking up how to do it.
To be fair the OP does say "You should know what is bit, byte, bit shifting/multiplication operations are. Also you need to know the precision of data types ranges." and provides a link to follow and research.

If you did the above you'd have the answers to a few of your questions, bitwise stuff isn't really meant for beginners to jump in and use without knowing and understanding what's going on.
 

FrostyCat

Redemption Seeker
what does the >> and & operator do?
Do what rIKmAN said --- read up.

While the Manual has a page on bitwise operators, I think Wikipedia's article on bitwise operators has better visuals and more coverage of applied use cases.

why are you writing multiple times in order to save the number? is this written so that one number or string is one entire file?
That's because a byte can only fit a number ranging from 0 to 255. For anything over that range, you need to dissect it into multiple parts. It is like how you can write any integer from one to nine using one decimal digit (e.g. 9), but any integer ten or higher requires you to dissect it into multiple decimal digits (e.g. 12).

In my opinion, this tutorial is obsolete and should no longer be used. Modern buffer functions could perform this bytewise dissection automatically without manual work. Even its author had to admit it was written with legacy GM in mind, which predate the introduction of buffer functions.

how would you save multiple numbers to the same file and differentiate between them when reading?
You don't. This is one of the primary challenges of low-level binary operations --- there is no type safety at this level. The only way you can read back what you wrote, under a binary file scheme, is to read in the same order as you wrote it.

what's the relationship between the number 1 and `string_char_at()`?
In GML, 1 is the first position of a string. This is clearly documented in the Manual entry for string_char_at.

your tutorial assumes the reader knows more that the average person looking up how to do it.
This tutorial is written for average programmers, not average muggles. Programmers know what arithmetic and bitwise operators are, and they certainly know how to subindex characters off a string in their language of choice. These are some of the first things new programmers learn. You can't belong in this trade if you don't know these things.
 
Top