EDIT:
Aw, man! You guys ninja'd me all up in the head!
Original post is below.
I swear, every time I crack a compression algorithm, it works on some principle I've never heard of before. Today's magical methodology? The circular buffer.
Here's the contents of the .h file:
// Items : Dummies, Trigzones, Splines, Cameras
// GENERATED FILE, MODIFY AT YOUR OWN RISK.
#ifndef ZED_ITEMS
#define ZED_ITEMS
typedef struct { float x,y,z; } vector;
typedef struct { float x,y,z, r; } sphere;
typedef struct { float Xx,Yx,Zx, x, Xy,Yy,Zy, y, Xz,Yz,Zz, z; } matrix;
typedef struct { vector pos, tan; float curve_pos; } control_point;
typedef struct { control_point* cpoints; float nbcp; } spline;
#endif
// DUMMIES
enum TRON_DUMMY
{
TRON_DUM_MNG_TRON,
TRON_DUM_BABY1,
TRON_DUM_BABY2,
TRON_DUM_BABY3,
TRON_DUM_BABY4,
TRON_DUM_HAUTGAUCHE,
TRON_DUM_BASDROITE,
TRON_NB_DUMMIES
};
extern matrix tron_dum[TRON_NB_DUMMIES];
// CAMERAS
enum TRON_CAMERA
{
TRON_CAM_TRON,
TRON_NB_CAMERAS
};
extern matrix tron_cam[TRON_NB_CAMERAS];
And of the .c file:
// Items : Dummies, Trigzones, Splines, Cameras
// GENERATED FILE, MODIFY AT YOUR OWN RISK.
// Matrices are row major.
// XYZ -> -XZY. HAVE FUN.
#include ""
// DUMMIES
matrix tron_dum[TRON_NB_DUMMIES] =
{
{ // TRON_DUM_MNG_TRON
1, 0, 0, 0.440365,
0, 1, 0, 0.0383208,
0, 0, 1, 3.00757
},
{ // TRON_DUM_BABY1
1, 0, 0, 2.62759,
0, 1, 0, -0.0113605,
0, 0, 1, 4.77104
},
{ // TRON_DUM_BABY2
1, 0, 0, -1.71902,
0, 1, 0, 0,
0, 0, 1, 4.77078
},
{ // TRON_DUM_BABY3
-0.99945, 0, 0.0331614, -1.73066,
0, 1, 0, 0,
-0.0331614, 0, -0.99945, 1.24962
},
{ // TRON_DUM_BABY4
-0.999999, 0, 0.0011544, 2.57853,
0, 1, 0, 0,
-0.0011544, 0, -0.999999, 1.30426
},
{ // TRON_DUM_HAUTGAUCHE
1, 0, 0, 3.31478,
0, 1, 0, -0.0214868,
0, 0, 1, 5.33592
},
{ // TRON_DUM_BASDROITE
1, 0, 0, -2.43594,
0, 1, 0, 0,
0, 0, 1, 0.681589
},
};
// CAMERAS
matrix tron_cam[TRON_NB_CAMERAS] =
{
{ // TRON_CAM_TRON
-1, -2.03576e-008, 1.85891e-008, 0.490089,
0, 0.674304, 0.738454, 4.47602,
-2.75679e-008, 0.738454, -0.674304, -0.646258
},
};
And biberon_anim.BMP:

The file format of these compressed files is pretty simple:
Fields are read little-endian
File Structure
==============================================================
MagicNumber UInt32 4 Must be 0x87654321
PackedSize UInt32 4 Size of compressed data in file
UnpackedSize UInt32 4 Size of decompressed data
PackedData Byte[] * PackedSize bytes of data
==============================================================
PackedData is a stream of packed LZSS chunks.
Each chunk begins with a single byte where each bit, starting with the lowest,
represents one data unit to decode.
1 - If the flag is set, then copy one byte from the input to the output.
0 - If the flag is cleared, then copy two bytes to process an LZSS string.
The decoder has a 4096-byte circular buffer that begins at byte 0xFEE. When
bytes are written to the output via either unit type, they are also written to
the circlar buffer. After a byte at location 0xFFF is written, the next byte
will be at 0x000, then at 0x001, and so-on.
The "distance" value of the distance/length pair actually specifies a location
within the circular buffer. It is not a typical distance value seen in ordinary
LZ specifications.
When processing a flag with a value of 0, two bytes are loaded.
* The first byte is the lower 8 bits of the "distance" value.
* The second byte contains the highest 4 bits of "distance", and the length.
Bits 4-7 of the second byte, which are the higher four bits, actually specify
bits 8-11 of the "distance" value, meaning the next four bits above 255.
Consider both bytes, read in order, and displayed as their component bits:
aaaaaaaa bbbbcccc
For such a pair of bytes, the "distance" value is composited as bbbbaaaaaaaa.
The length value, then, becomes only cccc.
Lengths are biased by 3, so add 3 to the processed value to retrieve the actual
length.
When processing the "distance"/length pair, start in the circular buffer at the
location specified by "distance", and from there copy length bytes into both
the output as well as the circular buffer itself from the last-written
position.
While investigating the format, I discovered what I believe to be the original source files used in the very "clzss" utility used to compress the files in the first place. They look to be a public domain of a particular LZSS implementation, structured as a C++ class definition:
• LZSS.h
• LZSS.cpp
These source files were especially helpful, because they follow exactly the format used by the Babysitting Party resources, at least in terms of the packed LZSS streams.
After some personal investigation and dozens of minutes programming, I've produced a C source file that compiles directly to a utility that will decompress these Babysitting Party files.
I'm calling the program "debabyz" over here, but that's not set in stone. It's a command line utility with the following usage:
debabyz <infile> <outfile>
debabyz.c:
#include <stdio.h>
#include <stdlib.h>
// Loads a file into a buffer with optional size limitations
int LoadFile(char *filename, unsigned char **data, int minsize, int maxsize) {
FILE *file = NULL;
int len, err;
// Error checking
if (data != NULL) *data = NULL;
if (filename == NULL || data == NULL) goto catch;
// Open the file
file = fopen(filename, "rb");
if (file == NULL) goto catch;
// Get the file's size, throwing an error if it's too big or too small
fseek(file, 0, SEEK_END);
len = ftell(file);
if (len < minsize || (maxsize && len > maxsize) )
goto catch;
// Load the file data
*data = malloc(len);
if (*data == NULL) goto catch;
fseek(file, 0, SEEK_SET);
err = fread(*data, 1, len, file);
if (err != len) goto catch;
// Close the file and return its length
fclose(file);
return len;
catch: // Error handling. Don't judge; you need goto for try/catch in C.
// Cleanup local resources
if (file != NULL)
fclose(file);
if (data != NULL && *data != NULL)
{ free(*data); *data = NULL; }
// Return an error code
return -1;
}
// Inline function for extracting a 32-bit integer from a byte buffer
#define GetInt32(data, offset) ( \
( (int) data[offset + 3] << 24 ) | \
( (int) data[offset + 2] << 16 ) | \
( (int) data[offset + 1] << 8 ) | \
(int) data[offset] \
)
// Program entry point
int main(int argc, char **argv) {
int fLen, magic, size_p, size_u, offset_i, offset_o, offset_c;
int flags, distance, length, c;
unsigned char *fData, *oData, circle[0x1000];
FILE *file;
// Check the input parameters
if (argc != 3) {
printf("Usage: %s <infile> <outfile>\n", argv[0]);
return 1;
}
// Load the file
fLen = LoadFile(argv[1], &fData, 12, 0); // Must be at least 12 bytes
if (fLen == -1) {
printf("ERROR: There was a problem loading %s\n", argv[1]);
return 2;
}
// Read the file header
magic = GetInt32(fData, 0);
size_p = GetInt32(fData, 4);
size_u = GetInt32(fData, 8);
if (magic != 0x87654321 || size_p > fLen - 12) {
printf("ERROR: The file does not appear to be supported.\n");
free(fData); // Delete the file buffer
return 3;
}
// Prepare an output buffer
oData = malloc(size_u);
if (oData == NULL) {
printf("ERROR: Not enough memory for output buffer.\n");
free(fData); // Delete the file buffer
return 4;
}
// Decode the file
offset_i = 12; // Offset within the input buffer
offset_o = 0; // Offset within the output buffer
offset_c = 0xFEE; // Starting point in the circular buffer
flags = 0; // Processing flags for raw/distance+length values
// Loop through all input bytes
for ( ; ; ) {
// Read a new flags byte if necessary
if (!(flags & 0x100)) {
// Prevent a buffer overrun
if (offset_i >= fLen) break;
// I learned this trick from the reference implementation.
// Flags will be > 255 if we have processed fewer than 8 bits.
flags = 0xFF00 | (int) fData[offset_i++];
}
// Decode a raw byte
if (flags & 1) {
// Prevent a buffer overrun
if (offset_i >= fLen || offset_o >= size_u)
break;
// Copy the next byte from input to output
oData[offset_o] = fData[offset_i++];
circle[offset_c++] = oData[offset_o++];
offset_c &= 0xFFF; // Clamp to buffer range
}
// Decode a "distance"/length pair
else {
// Prevent a buffer overrun
if (offset_i + 1 >= fLen) break;
// Process "distance" and length values
// Distance is actually the offset in the circular buffer
// Bits 4-7 of length are actually bits 8-11 of distance
distance = fData[offset_i++];
length = fData[offset_i++];
distance |= ( (length & 0xF0) << 4);
length = (length & 0x0F) + 3;
// Prevent a buffer overflow
if (offset_o + length > size_u) break;
// Copy bytes from the circular buffer to the output
for (c = 0 ; c < length; c++) {
oData[offset_o] = circle[(distance + c) & 0xFFF];
circle[offset_c++] = oData[offset_o++];
offset_c &= 0xFFF; // Clamp to buffer range
}
}
// Move to next flag
flags >>= 1;
} // for loop
// Save the decoded file data
file = fopen(argv[2], "wb");
if (file != NULL) {
fwrite(oData, 1, size_u, file);
fclose(file);
} else printf("ERROR: Could not open output file %s\n", argv[2]);
// Indicate if a decoding error may have occurred
if (offset_o != size_u)
printf("WARNING: The data may have contained errors.\n");
// Cleanup and exit
free(fData);
free(oData);
return 0;
}
Vague Rant, if you are able to compile that and get it to work on your own, perfect. If not, post a reply here in this thread and I'll see about getting you an EXE version of it.
|