Yeah, <endian.h> isn't found in the include path when I compile with Ocaml. I think I'll have to use the __ppc__ macro (that's pretty common to all Macs, and it's not like we can run IE on other bigendian architectures).
Can you try this code out? You'll need a CBF file to test it with (and a reference BIF file to test the results with). If you need these, let me know and we can figure out a way for me to get them to you.
/* $Id: un_bifc.c,v 1.1 2000/08/18 23:37:01 jedwin Exp $ */
/*
* un_bifc: unpack a compressed .bif file (BG2 style)
*
* This is a sample program from the Infinity Engine File Format Hacking
* Project. Use it as you like. Author assumes no responsibility, yada yada
* yada.
*/
#include <zlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <strings.h>
int
fread_check(void* b, size_t sz, size_t cnt, FILE* fp, const char* fn)
{
if (fread(b, sz, cnt, fp) != cnt) {
fprintf(stderr, "Failed to read %d bytes from %s\n", sz*cnt, fn);
return 1;
}
return 0;
}
/* BS for byte-swap */
#define BS_4BYTE(a) ((((a)&0xFF000000)>>24)|(((a)&0x00FF0000)>>8)|(((a)&0x0000FF00)<<8)|(((a)&0x000000FF)<<24))
#define BS_2BYTE(a) ((((a)&0xFF00)>>8)|(((a)&0x00FF)<<8)
int fread_uint(unsigned int* i, FILE* fp, const char* fn)
{
if (fread_check(&i, 4, 1, fp, fn))
return 1;
#ifdef __ppc__
i = BS_4BYTE(i);
#endif
return 0;
}
/* zerr() and def() are copied directly from zlib example code. */
#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
# include <fcntl.h>
# include <io.h>
# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
#else
# define SET_BINARY_MODE(file)
#endif
#define CHUNK 16384
/* report a zlib or i/o error */
void zerr(int ret)
{
fputs("zpipe: ", stderr);
switch (ret) {
case Z_ERRNO:
if (ferror(stdin))
fputs("error reading stdin\n", stderr);
if (ferror(stdout))
fputs("error writing stdout\n", stderr);
break;
case Z_STREAM_ERROR:
fputs("invalid compression level\n", stderr);
break;
case Z_DATA_ERROR:
fputs("invalid or incomplete deflate data\n", stderr);
break;
case Z_MEM_ERROR:
fputs("out of memory\n", stderr);
break;
case Z_VERSION_ERROR:
fputs("zlib version mismatch!\n", stderr);
}
}
/* Decompress from file source to file dest until stream ends or EOF.
inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
allocated for processing, Z_DATA_ERROR if the deflate data is
invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
the version of the library linked do not match, or Z_ERRNO if there
is an error reading or writing the files. */
int inf(FILE *source, FILE *dest)
{
int ret;
unsigned have;
z_stream strm;
unsigned char in[CHUNK];
unsigned char out[CHUNK];
/* allocate inflate state */
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
ret = inflateInit(&strm);
if (ret != Z_OK)
return ret;
/* decompress until deflate stream ends or end of file */
do {
strm.avail_in = fread(in, 1, CHUNK, source);
if (ferror(source)) {
(void)inflateEnd(&strm);
return Z_ERRNO;
}
if (strm.avail_in == 0)
break;
strm.next_in = in;
/* run inflate() on input until output buffer not full */
do {
strm.avail_out = CHUNK;
strm.next_out = out;
ret = inflate(&strm, Z_NO_FLUSH);
assert(ret != Z_STREAM_ERROR); /* state not clobbered */
switch (ret) {
case Z_NEED_DICT:
ret = Z_DATA_ERROR; /* and fall through */
case Z_DATA_ERROR:
case Z_MEM_ERROR:
(void)inflateEnd(&strm);
return ret;
}
have = CHUNK - strm.avail_out;
if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
(void)inflateEnd(&strm);
return Z_ERRNO;
}
} while (strm.avail_out == 0);
/* done when inflate() says it's done */
} while (ret != Z_STREAM_END);
/* clean up and return */
(void)inflateEnd(&strm);
return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}
int
cbf2bif(const char* cbf_file, const char* bif_file)
{
FILE *cbf_fp;
FILE *bif_fp;
char sigver[9];
unsigned int bif_file_len, cmplen, uncmplen;
int zret;
if (!(cbf_fp = fopen(cbf_file, "rb"))) {
fprintf(stderr, "Failure opening file: %s\n", cbf_file);
return 1;
}
if (!(bif_fp = fopen(bif_file, "wb"))) {
fclose(cbf_fp);
fprintf(stderr, "Failure opening file: %s\n", bif_file);
return 1;
}
sigver[8] = 0;
if (fread_check(sigver, 1, 8, cbf_fp, cbf_file)) {
fclose(cbf_fp);
fclose(bif_fp);
return 1;
}
if (strcmp(sigver, "BIF V1.0")) {
fprintf(stderr, "Incorrect CBF header for file %s\n", cbf_file);
fclose(cbf_fp);
fclose(bif_fp);
return 1;
}
if (fread_uint(&bif_file_len, cbf_fp, cbf_file)) {
fclose(cbf_fp);
fclose(bif_fp);
return 1;
}
if (bif_file_len <=0 || bif_file_len > 128) {
fclose(cbf_fp);
fclose(bif_fp);
fprintf(stderr, "Corrupt CBF file %s\n", cbf_file);
return 1;
}
if (fseek(cbf_fp, bif_file_len, SEEK_CUR)) {
fclose(cbf_fp);
fclose(bif_fp);
fprintf(stderr, "Failed to seek ahead in file %s\n", cbf_file);
return 1;
}
if (fread_uint(&uncmplen, cbf_fp, cbf_file)) {
fclose(cbf_fp);
fclose(bif_fp);
fprintf(stderr, "Failed to seek ahead in file %s\n", cbf_file);
return 1;
}
if (fread_uint(&cmplen, cbf_fp, cbf_file)) {
fclose(cbf_fp);
fclose(bif_fp);
fprintf(stderr, "Failed to seek ahead in file %s\n", cbf_file);
return 1;
}
printf("CBF %s (%d bytes) -> BIF %s [%d bytes]\n", cbf_file, cmplen, bif_file, uncmplen);
if ((zret=inf(cbf_fp, bif_fp)) != Z_OK) {
fclose(cbf_fp);
fclose(bif_fp);
zerr(zret);
return 1;
}
if (fclose(cbf_fp)) {
fclose(bif_fp);
fprintf(stderr, "Failure closing file %s\n", cbf_file);
return 1;
}
if (fclose(bif_fp)) {
fprintf(stderr, "Failure closing file %s\n", bif_file);
return 1;
}
return 0;
}
int main( int argc, char *argv[] )
{
if (argc != 3) {
fprintf(stderr, "Usage: %s <in-cbf-file> <out-bif-file>\n", argv[0]);
exit(1);
}
if (cbf2bif(argv[1], argv[2])) {
fprintf(stderr, "Conversion failed.\n");
exit(1);
}
return 0;
}