converting Crusader ingame sprites into *.tga files


well was browsing some russian coder's forums and stumbled upon very interesting code.

The guy managed to understand the code of Crusader sprites, read thema nd convert to TGA files. He wrote a code for a tool, which could be compiled into an easy convertor. Here is the code. LAter on I'll translate russian text remarks into english ones. So far i'm going to sleep :D
The source adress -

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#ifndef O_BINARY
    #define O_BINARY 0

// sprites from Crusader 8-bit palette
// the palette used in the game from gamepal.pal, just in case here it is in the format for tga
unsigned char palette[] = {

#pragma pack(1)
// array of indicators for the sprite animation
struct frame_ptr
    unsigned frame_offset;
    unsigned frame_size;

// sprite header
struct CrusaderSprite{
    unsigned version; // ??, always 0x0101
    unsigned short frame_cnt;
    frame_ptr frames[];

// header of the animation frame
struct CrusaderSpriteFrame
    unsigned short resource_id;
    unsigned short frame_num;
    unsigned unknown; // ??, i have no idea what it is
    unsigned flags;
    unsigned width;
    unsigned height;
    unsigned unknown2, unknown3; // probably its a sprites hotspot
    unsigned row_offsets[/* height */];

struct TGAHeader{
    unsigned char id_len;
    unsigned char colormap_type;
    unsigned char image_type;
    unsigned short cm_first_entry;
    unsigned short cm_length;
    unsigned char cm_entry_size;
    unsigned short x_origin;
    unsigned short y_origin;
    unsigned short width;
    unsigned short height;
    unsigned char depth;
    unsigned char img_desc;

#pragma pack()

int main(int argc, char **argv)
    int inf = open(argv[1], O_RDONLY|O_BINARY);
    int len = lseek(inf, 0, SEEK_END); lseek(inf, 0, SEEK_SET);
    char *res = new char[len];
    read(inf, res, len);

    TGAHeader tga;
    memset(&tga, 0, sizeof(tga));

    CrusaderSprite &sprite=(CrusaderSprite&)res[0];

    for(int cur_frame=0; cur_frame<sprite.frame_cnt; ++cur_frame)
        unsigned frame_offset = sprite.frames[cur_frame].frame_offset & 0x7fffffff; // 0x80000000 always the same. what is the flag?

        CrusaderSpriteFrame &frame = (CrusaderSpriteFrame &) res[frame_offset];

        char frame_file_name[1024];
        snprintf(frame_file_name, sizeof(frame_file_name), "%04x_%04x.tga", frame.resource_id, frame.frame_num);
        printf("Saving %s...\n", frame_file_name);

        int ouf = open(frame_file_name, O_RDWR|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE);
        unsigned sprite_data_len = frame.width*frame.height;
        unsigned char *sprite_data = new unsigned char[frame.width*frame.height];

        // background color, let it be green
        memset(sprite_data, 0x44, sprite_data_len);

        unsigned char *src, *dst;

        for(int row=0; row<frame.height; ++row)
            // the deviation of the point calculated from the row element of array and not from the beginning
            // array of frame
            src = (unsigned char *)&frame.row_offsets[row] + frame.row_offsets[row];
            dst = &sprite_data[row*frame.width];

            // in the beginning of row src[0] transparent pixels
            int col = *src;
            dst += *src++;

                // rle chunks, first byte is a lenght (and optional the flag is copy/fill), futher is the chain of copied bytes
                // byte - the number of transparent pixels after the chain
                int chunk_len = *src++;

                bool fill = false;

                // byte of the lenght of chunk, includes copy/fill flag
                    fill = chunk_len & 1;

                if(fill) // fill chain
                    memset(dst, *src++, chunk_len);

                else  // copy chain
                    memcpy(dst, src, chunk_len);

                // final hole, could be neglated at the end of the row
                col += *src;
                dst += *src++;


        write(ouf, &tga, sizeof(tga));
        write(ouf, palette, sizeof(palette));
        write(ouf, sprite_data, sprite_data_len);

        delete[] sprite_data;

    delete[] res;

    return 0;
Cool !

I don't remember though where were these sprites located in Crusader.
Nice C code :)

If I remember right no Hope uses the same sprites, no ? Maybe you could try it out for it.
nice to see you here RPGman, enjoy your stay with our growing Crusader-fan community. From my part i'll add in russian: Здарова братуха!!!  ;D
Welcome RPGman!

Maybe now you can create a codec to watch the cutscenes in Windows.  :D
does this extract them with the origin/bounding box information. In other words if I play the sprite in an animated sequence they wont 'hope' or 'bounce' around? Is there a compiled version around somewhere?
wild_qwerty link said:
does this extract them with the origin/bounding box information.
Is there a compiled version around somewhere?

TGA don't have fields to store such info. If you have your format, you (or I) can fix it. Sprite origin stored in CrusaderSpriteFrame struct.
I can upload compiled versions for linux and windows. Does this site has file archive, or should I do it with rapidshare?
yes, it has, however I locked it for security reasons, better to use free uploading resources. Rapidshare should be fine, i can download it from there and host here, will be no troubles.
RPGman link said:
TGA don't have fields to store such info. If you have your format, you (or I) can fix it. Sprite origin stored in CrusaderSpriteFrame struct.
I can upload compiled versions for linux and windows. Does this site has file archive, or should I do it with rapidshare?

Is there some way you can add this information in a separate txt or ini file for each image? Or be able to export the images into animated GIF files? That way the offsets will be included (or is TNG one of those file formats that is animated, I'm making a wally out of myself).
Sorry for the double post, but I've got a couple of questions...

RPGman link said:
TGA don't have fields to store such info. If you have your format, you (or I) can fix it. Sprite origin stored in CrusaderSpriteFrame struct.

Is it possible for you to export the crusader files directly into an animated gif file? If not I think there are animated *.mng files which are a 'free' format.

RPGman link said:
I can upload compiled versions for linux and windows. Does this site has file archive, or should I do it with rapidshare?

Has this been uploaded anywhere yet? Could yo please post a link to rapidshare? Thanks :)
Here is your converter (5Mb)

Converter takes crusader sprite as parameters, and create two files - mng file with the same name, and ini file with origin.
Both versions compiled - win32 and linux. Some sprites added for testing purposes (and a couple of mng files)
Note - complete sprites required. Crusader explorer available from this site can save only separate animation frames.
Hey bro, I didn't get how to work with the tool. Could you describe the procedure of extracting *.spr files and converting them into mng.
Here is *.flx unpacker. It will rip separate sprites from *.flx files.
Run it like `flx_unpacker.exe  shapes.flx`, and you'll get a lot of files like File_0001.spr, File_0002.spr....
Id's the same as in left panel of CrusaderExplorer.

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#ifndef O_BINARY
#define O_BINARY 0

#pragma pack(1)
struct FilePos
    unsigned offset;
    unsigned size;
#pragma pack()

int main(int argc, char **argv)
    int arc = open(argv[1], O_BINARY|O_RDONLY);
    lseek(arc, 0x54, SEEK_SET);

    int files;
    read(arc, &files, sizeof(files));

    FilePos *dir = new FilePos[files];
    lseek(arc, 0x80, SEEK_SET);
    read(arc, dir, files*sizeof(FilePos));

    static const int storage_size = 1024*1024;
    char *storage = new char[storage_size];

    printf("Files: %d\n", files);

    for(int i=0; i<files; ++i)
	if(dir[i].offset && dir[i].size)
	    char name[128];
	    snprintf(name, sizeof(name), "File_%04x.spr", i);
	    printf("Writing %s...\n", name);
	    int ouf = open(name, O_RDWR|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE);
	    int blocks = dir[i].size/storage_size;
	    int rest = dir[i].size%storage_size;

	    lseek(arc, dir[i].offset, SEEK_SET);
	    for(int j=0; j<blocks; ++j)
		read(arc, storage, storage_size);
		write(ouf, storage, storage_size);
		read(arc, storage, rest);
		write(ouf, storage, rest);

    delete[] storage;
    delete[] dir;

When you got *.spr, run `crusder2mng File_0001.spr`  to get File_0001.mng and File_0001.ini.
heh I do not have any compiler to compiler the flx_unpacker.exe. Could you compile it if that's not a trouble for you. If so, tell me, maybe I can download free compiler to compiler the file.