Save System, Read & Write Any Data to Compressed Binary Files
Custom Save System To Binary Files Original Author: () Dear Community, Using what I explain in this tutorial you can write your own custom save systems wherein you write out literally any game-rela...
Custom Save System To Binary Files
Original Author: ()
Dear Community,
Using what I explain in this tutorial you can write your own custom save systems wherein you
- write out literally any game-related data you want
- read back in this data any time you want, from hard-disk
- compress (ZLIB) these files to minimize usage of the end-user's computer hard disk space
I've already been writing out and loading back in levels that are zipped binary data files for my in-game editor.
I am currently able to save literally any custom data I want, including dynamic arrays, for all my custom classes.
I've developed a streamlined method for doing all this by overloading a specific UE4 C++ operator, so yay thank you thank you Epic for C++ access!
Pre-Summary
There's a lot of concepts in this tutorial, try out each part and get it working before moving on to the next
Just copy-pasting code will not work here, you need to understand the basics, because all I am presenting is the basics,
and you must apply the basics to your own project's needs.
After all, this is about a save system for any arbitrary project-specific custom class data :)
Two Levels of Conversions
When you want to save custom variable data for your custom save system,
there are TWO major steps involved
Step 1 = variable format -> binary array (serialized by Archive class of UE4)
Step 2 = Binary array -> hard disk
These steps are then done in reverse to read back in data from hard disk.
===Binary Array = TArray===
Binary data is represented in a very UE4 C++ friendly looking way as a dynamic array of uint8.
So any time you see TArray in my code in this tutorial, that literally means "Binary Array" from UE4 C++ standpoint.
Step 1: Variable Format -> Binary Array
An int32 takes up 4 bytes, as does a float.
An int64 takes up 8 bytes.
An FName takes up 8 bytes.
An FString takes up 16 bytes.
An FVector takes up 3 x float bytes.
etc.
So even a single int32 is actually an array of bytes, not a single entry.
Now let's suppose your save system needs to store
-
3 FVector's
-
40 int32's
-
20 FName's
Do all the math and this is a lot of bytes!
3 x 3 x 4 = 36
40 x 4 = 160
20 x 8 = 160
356
So this means that before your data leaves UE4 and goes to hard disk, you need a TArray that has 356 entries.
Step 2: Binary Array -> Hard Disk
UE4 C++ gives you functions via FileManager.h to write out TArray to hard disk!
Optional Step 3: Compressed Binary
UE4 gives you functionality via Archive.h to compress a TArray before sending it to the FileManager
''' C++ Code For You '''
Below I am giving you the functions that I use to to read and write binary files of any custom data I choose
Core Header Files
Archive.h and ArchiveBase.h
See Archive.h and ArchiveBase.h for all the info you need about getting from your varibles and custom class data to binary format (serialized data).
FileManager.h
All the functions you need to
- create directories
- delete directories
- create files
- delete files
- get a listing of all files in a given path
- get a listing of all folders in a given path
- get the age of a file
and more are found in FileManager.h
You access these functions from anywhere using
if(GFileManager) GFileManager->TheFunction()
BufferArchive
The buffer archive is both a binary array (TArray), and a MemoryWriter
Archive.h
/**
* Buffer archiver.
*/
class FBufferArchive : public FMemoryWriter, public TArray
{
Because of this multiple-inheritance, the BufferArchive is my preferred way to write data to binary file.
As my code will show, because the GFileManager wants to receive a TArray, not a MemoryArchive.
Review my Steps 1 and 2 to see why this is such an awesome class. :)
Thanks UE4 Devs!
FMemoryReader
To read Data back from a binary array, that is retrieved by the FileManager, you need a MemoryReader
Archive.h
/**
* Archive for reading arbitrary data from the specified memory location
*/
class FMemoryReader : public FMemoryArchive
{
public: