----------------------------------------------------------------------------------------------

Write in Python, Read in C

Introduction

As it is: I'm still trying my hand at game programming at a fairly low level. My current puzzle is creating spritesheets of character animations and importing them in to the engine I'm writing in C. The images come from Blender, I wrote a Python script that takes the individual rendered frames and copies them onto a 1024x1024 .png file. So: For every frame I write I know:

  • The model name
  • The animation name (walk, run, etc.)
  • The direction (north, northeast, etc.)
  • The frame width and height
  • The frame's position on the new sprite sheet (x, y)

My needs for the entity data were more complicated and I made a super basic json importer, but I figured with the flatness of this data I could do something a little better here.

The Python Part

My first thought was a pipe-separated list of values to be read by the engine:

player|walk|north|64|64|0|0
player|walk|north|64|64|64|0
player|walk|north|64|64|128|0

and so on

But I got self-conscious about all of those strings, and was like, I could keep a file in common with the C and Python code to hold the string tokens, so now it's:

player = 1
animation = 2
direction = 5

1|2|5|64|64|0|0
1|2|5|64|64|64|0
1|2|5|64|64|128|0

Which is definitely smaller, but technically, I'm still using strings. And there's the pipe separator to deal with.

I could pad the numbers and get rid of the pipes, but it's still a string. Unless I can somehow save this as a binary file instead of a text file...

So:

In Python I convert the integer data into hex, so 1 becomes, well, 1. But 10 is now A and 11 is B and 16 is, well, 10.

If I can store each number in two hex characters, that gives me plenty of space to work with. Except for the coordinates, which will definitely go above 256, but I could just save the panel numbers and multiply that by the width or height to get the right place on the image.

Indeed:

Now we have hexadedimal strings:

01020540400000
01020540400100
01020540400200

(which is kind of a terrible example, since there aren't any of the A-F characters)

and we really don't need the newlines there either, since this data is totally fixed width:

010205404000000102054040010001020540400200

And since it's a long hexadedimal data stream, we can save that as a binary file from Python.

Great!

Size-wize, for those 3 entries:

data.txt: 87 bytes (the original data with pipes and names)
data.bin: 22 bytes (the binarified hex data)

Almost a 75% savings! But that's not even the neat part...

So what does that mean from C? (the neat part)

The thing about C is it has data structures, which are made up of contiguous memory. So if I have, say, a structure that has:

uint8_t model;
uint8_t animation;
uint8_t direction;
uint8_t height;
uint8_t width;
uint8_t x;
uint8_t y;

It's going to take up a fixed amount of memory, and each field will be neighbors. So if you were to separate these into pipes, you'd get something like:

model|animation|direction|height|width|x|y|

Or, using a hex-representation of the uint8_t (8 bit) data type:

00|00|00|00|00|00|00

And without pipes:

00000000000000

Which, I hope, should look somewhat familiar.

Now all that's left is reading that data in from a file and doing some stuff with it. The structure of my animation data in the game is a bunch of linked lists, so my goal here was to read in to a defined data structure and then iterate over that creating the real animation data I need (and linking it within the larger structure).