SDL_RWops Tutorial

A while back I was trying to play with the zlib library, which allows programs to read data that's stored in .gz files. I found that the library made it easy to open up a compressed file and read the data into memory... but what was I to do with it then? My options seemed grim: I could write my own memory-to-SDL_Surface converter. I could pester the SDL developers. I could ask on the mailing list.

Fortunately, someone else asked on the mailing list so I didn't have to. SDL_RWops, an undocumented feature of SDL, is capable of taking pointers to memory instead of files (though it can handle files too). Once an RWops structure is set up, it can be passed to SDL_LoadBMP_RW.

Enough poorly-written synopsis. On with the code!


Part 1: Basic RWops

There are three steps for setting up SDL_RWops:
  1. Get an RWops structure. This can be done one of four ways:
    1. From a file, given a filename (using SDL_RWFromFile)
    2. From a file pointer (using SDL_RWFromFP)
    3. From a pointer in memory (using SDL_RWFromMem)
    4. Allocating and filling it in yourself (using SDL_AllocRW)
  2. Use the RWops to get whatever you wanted from the file or memory location.
  3. Free the RWops structure.

Here's a simple example. Note that I don't do any error checking whatsoever - that's fine for a simple application, but you don't want actual programs crashing when they can't find their files.

#include "SDL.h"
#include <stdio.h>

int main(void) {
  FILE *file;
  SDL_Surface *screen;
  SDL_Surface *one;
  SDL_Rect rect;

  /* This is the RWops structure we'll be using */
  SDL_RWops *rw;

  SDL_Init(SDL_INIT_VIDEO);

  atexit(SDL_Quit);

  screen = SDL_SetVideoMode(640, 480, 16, SDL_DOUBLEBUF);

  /* Here we get a file pointer to a bitmap */
  file = fopen("penguin.bmp", "r");
  if(!file) {
    printf("Couldn't load penguin.bmp\n");
    exit(1);
  }

  /* This is where we actually create our RWops structure.  Here we're
     instructing SDL to get it from the file pointer we just opened */
  rw = SDL_RWFromFP(file, 0);

  /* SDL_LoadBMP_RW is the RWops version of the old standby,
     SDL_LoadBMP.  This will get the image from the RWops you passed
     in; in this case the file you've opened */
  one = SDL_LoadBMP_RW(rw, 0);

  /* Clean up after ourselves */
  SDL_FreeRW(rw);
  fclose(file);

  /* Haphazard way of getting stuff to the screen */
  rect.x = rect.y = 20;
  rect.w = one -> w;
  rect.y = one -> h;
  SDL_BlitSurface(one, NULL, screen, &rect);

  SDL_Flip(screen);

  SDL_Delay(3000);
}

(You can get the listing from here, and a penguin from here)

You can compile this program by typing:

gcc -o rwtut1 rwtut1.c `sdl-config --cflags --libs`
(Note, those are backticks. On my keyboard, they're on the same key as the tilde (~), right next to the '1')

If everything went well, you should see a window with Tux in it. If it didn't go well, then check the code over. Make sure you haven't made a mistake. Then make sure I haven't made any mistakes :)

Okay, so you've used RWops, but it doesn't really look like you've done a whole lot with it. In fact, you've done almost exactly what SDL_LoadBMP does on its own. Well, that's why they call it a trivial example. Let's do something a bit more interesting.

Part 2: Useful RWops stuff with zlib

Like the title implies, we're going to be using zlib in this example. Most every linux distribution comes with zlib, but make sure you have it (as well as the header files and such that you'll need) before going on, because I wouldn't want you to type all this in and then find out that it doesn't work.

The zlib library allows a semi-transparent way of loading data from a file that's been compressed - specifically, .gz files. So if you've compressed some text for your program, you can point zlib at it and then read it into memory. The same holds for images - they can be loaded into memory as well. How to get at them then? That's what the RWops are for :)

This second example is similar to the first, but has some key differences that are commented in detail. Note again the lack of error checking - if you're like me and forgot to put a penguin.gz file in your directory, it's going to segfault.

#include "SDL.h"
#include <stdio.h>
#include <zlib.h>

int main(void) {
  SDL_Surface *screen;
  SDL_Surface *one;
  SDL_Rect rect;

  /* This is the RWops structure we'll be using */
  SDL_RWops *rw;

  /* gzFile is the Zlib equivalent of FILE from stdio */
  gzFile file;

  /* We'll be needing space to store our graphic into.  The penguin
     graphic is about 12k, so we'll fake it for now.  You'd want to
     figure out how much space you need for other graphics in a real
     application, though. */
  Uint8 buffer[13000];

  /* We'll need to store the actual size of the file when it comes in
   */
  int filesize;

  SDL_Init(SDL_INIT_VIDEO);

  atexit(SDL_Quit);

  screen = SDL_SetVideoMode(640, 480, 16, SDL_DOUBLEBUF);

  /* Opens up the file with Zlib */
  file = gzopen("penguin.gz", "r");

  /* Decompresses the file into memory (the buffer we set aside),
     13000 bytes max.  gzread returns the number of (decompressed)
     bytes it actually read, and we need that information for
     later. */
  filesize = gzread(file, buffer, 13000);

  /* Gives us an RWops from memory - SDL_RWFromMem needs to know where
     the data is, and how big it is (thus why we saved the filesize)
  */
  rw = SDL_RWFromMem(buffer, filesize);

  /* The function that does the loading doesn't change at all */
  one = SDL_LoadBMP_RW(rw, 0);
  
  /* And clean up */
  SDL_FreeRW(rw);
  gzclose(file);

  /* Haphazard way of getting stuff to the screen */
  rect.x = rect.y = 20;
  rect.w = one -> w;
  rect.y = one -> h;
  SDL_BlitSurface(one, NULL, screen, &rect);

  SDL_Flip(screen);

  SDL_Delay(3000);
}

(The listing is here, and a gzipped penguin is here)

You can compile this program by typing:

gcc -o rwtut2 rwtut2.c `sdl-config --cflags --libs` -lz

Note the -lz at the end of the command - we're using the Zlib library now, so we need to link it in.

If everything went well, this program will run exactly the same as the one before it. You'll see Tux, the screen will stay up for three seconds, and then it'll be over. If it pops up a window a brief moment before segfaulting, it's probably because you forgot to put the penguin.gz file in the directory that the program's running from.

If you want to use the zlib library in your own projects, it's covered under the zlib license which is OSI-approved.

Part 3: Very useful RWops stuff with zziplib

The example in part 2 is the essense of RWops - you should be able to adopt that to use essentially any library with SDL. (And SDL addon libraries - SDL_Image and SDL_Mixer are two such libraries that support RWops.) This part of the tutorial is specific to zziplib, so if you're not interested then you're not missing out on anything new related to the RWops.

Zziplib is a library which allows you to almost transparently access files in a .zip archive. For all of you out there who have wanted to put all your game resources in one place, this is the library (and accompanying tutorial) you've been looking for. Make sure you've installed it on your system, as well as all the header files -- if you've compiled from source, this isn't a problem; if you're getting the .rpm files, make sure you get the zziplib-devel files as well.

All set? Let's rewrite our second example there to use zziplib.

#include "SDL.h"
#include <stdio.h>
#include <zzip.h>

int main(void) {
  SDL_Surface *screen;
  SDL_Surface *one;
  SDL_Rect rect;

  /* This is the RWops structure we'll be using */
  SDL_RWops *rw;

  /* ZZIP_FILE is the zzip equivalent of the stdio FILE.*/
  ZZIP_FILE *file;

  /* Again, we're cheating because I know how big the file is */
  Uint8 buffer[13000];

  int filesize;

  SDL_Init(SDL_INIT_VIDEO);

  atexit(SDL_Quit);

  screen = SDL_SetVideoMode(640, 480, 16, SDL_DOUBLEBUF);

  /* zzip_open does some magic here - if there is a directory called
     "penguin" and there is a "penguin.bmp" there, zzip_read (which
     we'll use later) will act just like stdio's read.  If, however,
     there is a penguins.zip that has a penguin.bmp in it, that will be
     read.  Keep in mind that zzip will prefer real files to files in
     .zip archives if there's a conflict. */
  file = zzip_open("penguins/penguin.bmp", 0);
  if(file == NULL) {
    printf("Can't load penguins/penguin.bmp\n");
    exit(1);
  }

  /* Isn't it nice how all these libraries mimic read(2) for us? */
  filesize = zzip_read(file, buffer, 13000);

  rw = SDL_RWFromMem(buffer, filesize);
  one = SDL_LoadBMP_RW(rw, 0);
  
  /* And clean up */
  SDL_FreeRW(rw);
  zzip_close(file);

  /* Haphazard way of getting stuff to the screen */
  rect.x = rect.y = 20;
  rect.w = one -> w;
  rect.y = one -> h;
  SDL_BlitSurface(one, NULL, screen, &rect);

  SDL_Flip(screen);

  SDL_Delay(3000);
}

(Your listing is here, and a zip file with several penguins can be found here)

Compilation can be accomplished via the following:

gcc -o rwtut3 rwtut3.c `sdl-config --cflags --libs` `zzip-config --libs`
(Note that the zzip-config script that shipped with zziplib 0.10.25 doesn't give correct output for the --cflags option - this has been fixed in the latest version)

This does the same thing as the last two examples, so there should be no surprises there. You could put all your resource files for your game in one .zip file, and access them through zziplib in much the same way we did above. But, you say, surely I'm not the first person who's thought of using zzip as a way to store images for SDL? Of course not - that honor belongs to the Zzip authors.

Those of you who glanced through the zziplib website and noted the link titled "Example to make an SDL_rwops interface" are probably scowling at me right now for making you go through all the typing of listing three up there. But like any tutorial, we had to do it the hard way first. The Zzip authors were kind enough to provide a way to get an rwops directly from the library, without any of the wrangling we had to do above. The page goes into great detail here, but doesn't provide an example. Well, that's what tutorials are for :)

#include "SDL.h"
#include "SDL_rwops_zzip.h"
#include <stdio.h>
#include <zzip.h>

int main(void) {
  SDL_Surface *screen;
  SDL_Surface *one;
  SDL_Surface *two;
  SDL_Rect rect;

  /* This is the RWops structure we'll be using */
  SDL_RWops *rw;

  SDL_Init(SDL_INIT_VIDEO);

  atexit(SDL_Quit);

  screen = SDL_SetVideoMode(640, 480, 16, SDL_DOUBLEBUF);

  /* SDL_RWFromZZIP will create an RWops for us, saving us the trouble
     of doing it ourselves like we had to in the last example.  It
     also does the same filename magic that zzip_open does. */
  rw = SDL_RWFromZZIP("penguins/penguin.bmp", "r");
  one = SDL_LoadBMP_RW(rw, 0);
  SDL_FreeRW(rw);

  /* We'll load two, just to point out how easy it is */
  rw = SDL_RWFromZZIP("penguins/penguin2.bmp", "r");
  two = SDL_LoadBMP_RW(rw, 0);
  SDL_FreeRW(rw);

  /* Haphazard way of getting stuff to the screen */
  rect.x = rect.y = 20;
  rect.w = one -> w;
  rect.y = one -> h;
  SDL_BlitSurface(one, NULL, screen, &rect);

  rect.x += rect.w + 10;
  rect.w = two -> w;
  rect.h = two -> h;
  SDL_BlitSurface(two, NULL, screen, &rect);

  SDL_Flip(screen);

  SDL_Delay(3000);
}

(As usual, here's a listing. Since the SDL bindings aren't included in the zzip library, you'll also need SDL_rwops_zzip.c and SDL_rwops_zzip.h, which you can get from those links or from the Zzip website.)

This compile line is long:

gcc -o rwtut4 rwtut4.c SDL_rwops_zzip.c -I. `sdl-config --cflags --libs` `zzip-config --libs`

In a radical departure from the other examples, this one will display two Tuxes on the screen, one larger than the other. Adapting this example to your game should be a trivial task - mainly because the Zziplib SDL extentions make it so easy.


Back to the tutorial index

Copyright 2001, Roger Ostrander