Skip to content

Instantly share code, notes, and snippets.

@brokenprogrammer
Created April 24, 2022 10:45
Show Gist options
  • Select an option

  • Save brokenprogrammer/3fdbd623186f4c7614b4cae27550644f to your computer and use it in GitHub Desktop.

Select an option

Save brokenprogrammer/3fdbd623186f4c7614b4cae27550644f to your computer and use it in GitHub Desktop.
Implementation of LBP Serializer
// This is a reference implementation that I wrote to understand and to keep my own archive of the LBP Serializer
// as well as to try it out before using it within Brainroll.
// Oswald Hurlem presents this in his article: https://yave.handmade.network/blog/p/2723-how_media_molecule_does_serialization
// Credit goes to him and of course the people at Media Molecule.
//
// This implementation also briefly showcases the example use of CHECK_INTEGRITY which you can place in places
// where it makes sense for you.
//
// Build with: cl.exe -nologo /Zi /FC LBPSerialization.cpp /link -opt:ref -incremental:no /Debug:full /out:lbp.exe
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <assert.h>
struct lbp_serializer
{
uint32_t DataVersion;
FILE *FilePointer;
bool IsWriting;
int32_t Counter;
};
enum serialization_versions : int32_t
{
SV_Initial = 1,
SV_FirstRevision,
SV_AddIntegrityChecks,
// Keep this as the last element
SV_LatestPlusOne,
};
#define LATEST_VERSION (SV_LatestPlusOne - 1)
#define ADD(_FieldAdded, _FieldName) \
if (LBPSerializer->DataVersion >= (_FieldAdded)) \
{ \
Serialize(LBPSerializer, &(Datum->_FieldName)); \
}
#define REM(_FieldAdded, _FieldRemoved, _Type, _FieldName, _DefaultValue) \
_Type _FieldName = (_DefaultValue); \
if (LBPSerializer->DataVersion >= (_FieldAdded) && \
LBPSerializer->DataVersion < (_FieldRemoved)) \
{ \
Serialize(LBPSerializer, &(_FieldName)); \
}
#define CHECK_INTEGRITY(_CheckAdded) \
if (LBPSerializer->DataVersion >= (_CheckAdded)) \
{ \
int32_t Check = LBPSerializer->Counter; \
Serialize(LBPSerializer, &Check); \
assert(Check == LBPSerializer->Counter++); \
}
static void
Serialize(lbp_serializer *LBPSerializer, int32_t *Datum)
{
if (LBPSerializer->IsWriting)
{
fwrite(Datum, sizeof(uint32_t), 1, LBPSerializer->FilePointer);
}
else
{
fread(Datum, sizeof(uint32_t), 1, LBPSerializer->FilePointer);
}
}
static void
Serialize(lbp_serializer *LBPSerializer, uint32_t *Datum)
{
if (LBPSerializer->IsWriting)
{
fwrite(Datum, sizeof(uint32_t), 1, LBPSerializer->FilePointer);
}
else
{
fread(Datum, sizeof(uint32_t), 1, LBPSerializer->FilePointer);
}
}
static void
Serialize(lbp_serializer *LBPSerializer, int64_t *Datum)
{
if (LBPSerializer->IsWriting)
{
fwrite(Datum, sizeof(int64_t), 1, LBPSerializer->FilePointer);
}
else
{
fread(Datum, sizeof(int64_t), 1, LBPSerializer->FilePointer);
}
}
static void
Serialize(lbp_serializer *LBPSerializer, uint64_t *Datum)
{
if (LBPSerializer->IsWriting)
{
fwrite(Datum, sizeof(uint64_t), 1, LBPSerializer->FilePointer);
}
else
{
fread(Datum, sizeof(uint64_t), 1, LBPSerializer->FilePointer);
}
}
struct my_state
{
int32_t Health;
uint32_t Score;
int64_t Weight;
uint64_t DistanceTraveled;
};
static void
Serialize(lbp_serializer *LBPSerializer, my_state *Datum)
{
ADD(SV_Initial, Health);
ADD(SV_Initial, Score);
ADD(SV_FirstRevision, Weight);
ADD(SV_FirstRevision, DistanceTraveled);
CHECK_INTEGRITY(SV_AddIntegrityChecks);
}
static bool
SerializeIncludingVersion(lbp_serializer *LBPSerializer, my_state *MyState)
{
if (LBPSerializer->IsWriting)
{
LBPSerializer->DataVersion = LATEST_VERSION;
}
Serialize(LBPSerializer, &LBPSerializer->DataVersion);
if (LBPSerializer->DataVersion > (LATEST_VERSION))
{
// NOTE(Oskar): Attempt to read from newer version.
return false;
}
else
{
Serialize(LBPSerializer, MyState);
return true;
}
}
static void
InitializeState(my_state *State)
{
State->Health = 0;
State->Score = 0;
State->Weight = (0x7fffffff) + 1;
State->DistanceTraveled = (uint64_t) -1;
}
static void
RandomizeState(my_state *State)
{
State->Health = rand();
State->Score = -1;
State->Weight = (0x7fffffff) + rand();
State->DistanceTraveled = (uint64_t) -1;
}
static void
PrintState(my_state *State)
{
printf("\nState:\n");
printf("Health: %d\n", State->Health);
printf("Score: %ud\n", State->Score);
printf("Weight: %lld\n", State->Weight);
printf("DistanceTraveled: %I64u\n", State->DistanceTraveled);
}
int
main(int argc, char **argv)
{
if (argc < 2)
{
printf("Error: Expected a filename. \nUsage: LBPSerialization.exe [filename]");
}
char *FileName = argv[1];
FILE *FilePointer;
srand(time(NULL));
my_state State = {0};
// NOTE(Oskar): Read or create new state file.
if (FilePointer = fopen(FileName, "r"))
{
// NOTE(Oskar): Read in state
lbp_serializer LBPSerializer = {0};
LBPSerializer.IsWriting = false;
LBPSerializer.FilePointer = FilePointer;
if (SerializeIncludingVersion(&LBPSerializer, &State))
{
printf("Successfully read file %s.\n", FileName);
PrintState(&State);
}
else
{
printf("Failed to open file for reading.\n");
return 0;
}
fclose(FilePointer);
}
else
{
// NOTE(Oskar): Couldn't open so build new state
printf("No file exists so building new state.\n");
InitializeState(&State);
}
RandomizeState(&State);
if (FilePointer = fopen(FileName, "w"))
{
lbp_serializer LBPSerializer = {0};
LBPSerializer.IsWriting = true;
LBPSerializer.FilePointer = FilePointer;
SerializeIncludingVersion(&LBPSerializer, &State);
printf("Wrote serialized state to file\n");
fclose(FilePointer);
}
else
{
printf("Failed to open file for saving.\n");
return 0;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment