Yarnspin Code Components


I thought I’d talk a bit about the code components making up Yarnspin, my story-telling engine. Just a high-level overview, a look at what libraries I’m using and why, and how the different parts of the code is organized.

The full source code is here: https://github.com/mattiasgustavsson/yarnspin

Let’s start off by looking at the Yarnspin code files, before looking into the libs.  They are only just over 6k lines of code. There are two separate applications in yarnspin. One is the compiler and runtime, the other is the image editing tool. The latter is all contained in imgedit.h, and the former is the other files:

Image

As you can see, there is just one .c file - everything else is a header file. Yarnspin is built from stb-style single-file libraries, and with the small projects I do, it makes a lot of sense to put everything in a single compile unit. This is a pattern I use in all my projects.

So yarnspin.c contains the program entry point, `main`. And at the top of the file, I include all the libraries I am using - I’ll talk more about the different libs later. The thing about stb-style libs, is that doing these includes only gets you declarations, not definitions.
Image

This makes them different from C++ template header-only libs, which gets you both declarations and definitions at the same time. At the end of yarnspin.c, I include the same .h files again, but this time, each is preceded by an `_IMPLEMENTATION` define to enable definitions.

Image

These defines work similar to include guards, and toggle on the majority of the code in the lib, the actual definitions. If I was using a lot of huge libs, and they were to cause long compile times, I could put this second set of includes in a separate .c file. Compile times is never a problem for my projects though, so I keep things to a single C file for simplicity and convenience. Compiling yarnspin.c, which includes all the libs, is pretty much instant.

Now, I could have added the `_IMPLEMENTATION` defines before the includes at the top of yarnspin.c, and then I wouldn’t need the second set at all. But the all the internal symbols and defines of the lib and files it includes would be visible in my app code and to intellisense. And that just makes things a bit noisy, so I prefer splitting things up like this. But having just a single set of includes for both declarations and definitions works just as well, and I sometimes do that.

The `main` function in yarnspin.c will look at the commandline parameters, and decide to launch the image editor or to compile and load a yarn and launch it in the game runtime Yarnspin use my own app.h lib which create a window and gl context, and calls your own `app_proc` func.

ImageImage

app.h runs on Windows, Linux, Mac and in the browser, and compiles with gcc, clang, msvc and tcc. It can be built as either C or C++, and has a very streamlined API surface. A stand-alone sample of app.h can be found in this github template project: https://github.com/mattiasgustavsson/template_project

Now let’s have a brief look at all the libraries I’m using, and what I’m using them for. 

Image

I guess stb_image.h should be obvious - I use it to load images of any format, jpg, png, gifs etc. It’s an amazing file loader lib, highly recommended.

The next one, lzma.h, is the public domain LzmaLib conpression library by Igor Pavlov, repackaged into a stb-style single header lib by myself. I use it to compress the final data file for the game - more to make is content more obfuscated than because it really needs compressions.

stb_truetype.h reads ttf files, and renders them to pixels. I use this to convert ttf fonts to my own format for efficient rendering, as can be seen hereI’ll talk more about the in-game font renderer, pixelfont.h, in a bit.

The next lib (skipping app.h which we already looked at) is cstr.h, a fairly new and not entirely complete lib by myself for handling strings in C. It is mostly designed for ease of use and convenience, while still not wasteful in terms of performance. I’m still trying it out :)

stb_image_resize.h is used by both imgedit and yarnspin, to resize images from whatever input size to the fixed output size of Yarnspin. stb_image_write.h is not strictly necessary, but I use it to save a copy of each processed image as a PNG in the “cache” folder.

crtemu.h and crtemu_pc.h are two variants of my shader that emulates old CRT screens. What I really want to do at some point, is to implement the settings parameters in those libraries, and unify them into one lib with two default settings.

thread.h is my own library for doing multithreading - mutexes, threads, signals etc. It has implementations for Windows and Posix. It is used only by imgedit, to have a background thread which process images without blocking the main UI thread.

ini.h is a library I wrote to read and write classic old-school .ini files. I like the simplicity of those, and I use them whenever appropriate instead of XML or JSON. In Yarnspin, they are used to store the settings made with imgedit, so images gets processed according to those.

Yarnspin is limited to 256 colors, and the palette is loaded from an image file for simplicity. If the image contains 256 different colors or less, they are used as is. If there are more than 256 colors, it will use palettize.h to run a median-cut palettization algorithm on it.

paldither.h is used to take a 32-bit image and a palette, and convert it to an 8-bit palettized image using the specified palette and a choice of dither patterns. It is rather slow, as it loops through all combinations to find the best match. Really needs algorithmic optimization.

For drawing text in the game, I use my own pixelfont.h. It can draw with different alignment, wrapping, bold/italic/underline, even slow-print. I use it all the time, for all my text rendering needs. It has functions to create a font in the right format, from bitmaps of glyphs.

Once paldither.h have produced an 8-bit image, I use palrle.h to make a run-length-encoding of it. This is what’s saved to the output file, and the lib has functions to blit straight from the RLE data.

buffer.h is just a simple helper lib for reading/writing binary data to/from a dynamically growing buffer. It is quite handy when implementing custom binary formats. It’s used like this.

By default, yarnspin does a bunch of adjustments to images before palettizing them, things like increasing contrast/saturation and applying a ”sharpen” filter. With imgedit you can set how they are applied. The img.h is a wip lib I use for doing that kind of image manipulation.

array.h is another new lib, for handling dynamic arrays in C. It works reasonably well, but is a bit lacking when it comes to type safety.

dir.h and file.h are simple utility libs for listing files and loading/saving them. 

sysfont.h is a fixed font renderer with embedded font data, used only by imgedit to not have to rely on a specific font file being present.

So there we have it, all the libs used in yarnspin :) 


If you are into this sort of things, why not join the freshly started Yarnspin discord server, and come chat about everything related to interactive fiction, text adventures and visual novels.

https://discord.gg/ZVuX2pwS24


Get Yarnspin

Comments

Log in with itch.io to leave a comment.

Great stuff <3

(+1)

Thank you! <3