By Shawn Hargreaves, November 1999
As of the 3.9.x work-in-progress series which is current at the time of this writing, Allegro has been ported to DOS (djgpp and Watcom), Windows (MSVC, mingw32, and RSXNTDJ), Linux (console and X) and BeOS. By the time you read this, version 4.0 will probably be out, or perhaps even some later release with support for more different platforms. Allegro gives you the exact same library functions no matter where you are running it, and this makes it very easy to produce versions of your program for all of these systems: in theory, a simple recompile should be all the porting work required. Things are rarely quite that simple in the real world, though, so you will probably need to tweak a few things to get your program working on each new platform. This document tries to guess what the most likely problem areas will be, in hopes that you can avoid them right from the start rather than getting stuck having to do lots of fiddling later on. Some of these are Allegro things, but most are general C things: in either case, please let me know if you can think of any other problem areas that I left out!
typedef struct MYSTRUCT { char a; short b; int c; };you might expect that sizeof(MYSTRUCT) will be 7, but in fact most Intel compilers will pad it to 8 bytes. It is quite possible, though, that some might pad to 12, or that a future 64 bit platform might make that int variable twice as large as you are expecting. So you need to avoid code which cares about such changes, and be sure to use sizeof() whenever you need to allocate space for a data structure.
Most importantly, you need to distinguish between read-only data, global modifiable data, and user-specific data. Assuming that someone has downloaded your game, unpacked it, compiled it, and run it, it is a good thing for your game not to modify the directory where it is installed, so that this directory could be mounted read-only. If you want to save out variable data such as a hiscore table, put this in the /var/games directory (or if you need many such files, create a /var/games/mygame subdirectory, and put your data in that). But don't forget that there can be many users on the same machine, and they may not all want to share the same stored information! Anything that is specific to the current user, such as save game files or controller configuration, should go in their home directory, which can be located by reading the HOME environment variable (eg. char *mydir = getenv("HOME")). Again, if you need to put many files here, you can create a subdirectory for yourself.
That is probably enough to make your game work well in a multiuser environment, but if you want to go further and set up a really beautiful configuration (for example to provide a "make install" target as well as the regular compilation rules) you should be aware of the following locations:
Most binaries go in /usr/local/bin, but games should be placed in /usr/local/games. This directory is only for the executable itself, which is specific to the current machine architecture, and can be run directly by the user.
Shared libraries and other executable code go in /usr/local/lib. The contents of this directory are also specific to one machine architecture, but the user does not run them directly (for example the Allegro shared library goes in here).
Non-executable resources go in /usr/local/share/, or for games, /usr/local/share/games/. The point of this directory is that it contains material which remains the same no matter what type of computer your game is running on, so if someone has a network containing a mix of i386 machines, Alpha boxes, and PPC systems, they can still mount these files over the network and use the same data on all hardware. So this is the place to put your graphics, sounds, and level data. If you have more than one file to put in here, make a subdirectory to contain them all.
#ifdef GFX_MODEX if (gfx_driver->id == GFX_MODEX) { /* do funky mode-X graphics stuff */ } else #endif { /* do normal graphics stuff */ }This code will simply not bother to compile the mode-X routines if there is no mode-X driver on the current platform, and it is better than checking for the platform itself, because this test will adapt to things like the Linux version where mode-X support might or might not be available, depending on what options were passed to the configure script.
// call bmp_select() once at the very start of your drawing operation bmp_select(bmp); for (y=top; y<bottom; y++) { // call bmp_write_line() once for every horizontal line unsigned long address = bmp_write_line(bmp, y); for (x=left; x<right; x++) { // use bmp_write*() macros to write the pixels bmp_write8(address+x, color); } } // call bmp_unwrite_line() once at the very end of the drawing operation bmp_unwrite_line(bmp);Yes, it looks horrific, but half of these macros will just expand into noops on any given platform, and the other half tend to collapse into just a bit of pointer twiddling or inline asm, so it really isn't all that bad. And this is the only way to make direct memory access code that is 100% portable to any platform.
For higher color depths, change bmp_write8() to bmp_write15(), bmp_write16(), etc, and multiply the x coordinate by a scaling factor before you add it to the destination address. For 15 and 16 bit modes, you should multiply by sizeof(short). For 24 bit modes, multiply by 3. For 32 bit modes, multiply by sizeof(long).