omega-rpg/tools/crypt.c

242 lines
5.7 KiB
C

/* WDT: todo later: move this into the Omega source, probably changing both
* encode and decode into command line options to omega itself. Oh, and
* change the name from 'crypt' to 'pack'. */
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "bwt.h"
#define VERSION 2
/* These _must_ match the equivalent values in Omega, and if they
* change, I need to define a new version. */
#define MAXWIDTH 128
#define MAXLENGTH 64
static char *fileName;
static int lineNumber;
void error(char *explanation, ...)
{
va_list ap;
va_start (ap, explanation);
fprintf (stderr, "%s:%d: ", fileName, lineNumber);
vfprintf (stderr, explanation, ap);
va_end (ap);
abort();
}
int unique(int mapnumber)
{
static int cities[256];
static int max=0;
int i=0;
while (i<max)
if ( cities[i++] == mapnumber )
return 0;
if ( max >= 255 ) return 0;
cities[max++] = mapnumber;
return 1;
}
void fputint(int x, FILE *f)
{
assert(sizeof(int) == 4); /* This assumption is explicit here, but it's
* also elsewhere. */
fputc(x &0xFF,f);
fputc((x>>8) &0xFF,f);
fputc((x>>16)&0xFF,f);
fputc((x>>24)&0xFF,f);
}
void fputshort(short x, FILE *f)
{
assert(sizeof(int) == 4); /* This assumption is explicit here, but it's
* also elsewhere. */
fputc(x &0xFF,f);
fputc((x>>8) &0xFF,f);
}
/*
* RLE from in to out, returning the amount of output data.
* Max expansion is 2 times original size (if every char has
* the high bit set and no compression can be done).
*/
int rle_encode(char *in, char *out, int lengthIn)
{
int pos = 0;
int run = 0;
int c = in[pos];
int offset = 0;
while (pos < lengthIn)
{
if (in[pos] == c && run < 127)
run++;
else
{
if ( run > 1 )
{
out[ offset++ ] = run | 0x80;
run = 1;
}
else if ( c & 0x80 || c == 0x55 )
{
out[ offset++ ] = 0x55;
}
out[ offset++ ] = c;
}
c = in[pos++];
}
/* Be sure to dump the last token. */
if ( run > 1 )
{
out[ offset++ ] = run | 0x80;
run = 1;
}
else if ( c & 0x80 || c == 0x55 )
{
out[ offset++ ] = 0x55;
}
out[ offset++ ] = c;
return offset;
}
void encode(char **data, int *length)
{
char *result = malloc(*length * 2);
*length = rle_encode(*data,result, *length);
free(*data);
*data = result;
}
int main(int num_args, char *args[])
{
char *code;
int c, i, x,y,scanned, depth;
int size;
FILE *out, *in = 0, *data;
int data_offset = 0;
fileName = ""; lineNumber = 0;
if ( num_args < 3 || num_args > 258 )
error("Usage: %s <output-filename> <input-filename>"
" [more input filenames, max 256...]\n",
args[0]);
/* A file to hold the data portion of the conglomeration; it will be
* appended to the output file when we're finished. This allows us to
* build the header portion of the file easily. */
data = tmpfile();
if ( data == NULL )
error("Unable to open temporary file.\n");
/* The final output. */
out = fopen(args[1],"wb");
if ( out == NULL )
error("Unable to open output file '%s'.\n", args[1]);
fputc(VERSION,out); /* The file version */
fputshort(num_args-2,out); /* The number of files it's supposed to contain */
for ( i=2; i<num_args; i++ )
{
int levels;
fileName = args[i]; lineNumber = 0;
in = fopen(args[i],"r");
if (!in)
error("Unable to open map file '%s'.\n", args[i]);
lineNumber++;
scanned = fscanf(in,"v%d\n",&c);
if (scanned != 1)
error("File %s missing version header.\n", args[i]);
if (c != VERSION)
error("Unable to convert file %s: bad version, %d (expected %d).\n",
args[i], c, VERSION);
lineNumber++;
scanned = fscanf(in,"map %d\n",&c);
if (scanned != 1)
error("File %s missing map number header.\n", args[i]);
if (!unique(c))
error("Map header number %d is not unique in map file %s.\n",
c, args[i]);
fputshort(c, out);
lineNumber++;
scanned = fscanf(in,"%d %d,%d\n", &depth, &x, &y);
if (scanned != 3)
error("%s:3: missing dimensions header.\n", args[i]);
if ( (unsigned)x > 256 || (unsigned)y > 256 || !x || !y )
{
error("Unable to convert file: bad size, %dx%d.\n",
x, y);
}
if ( (unsigned)depth > 255 || !depth )
{
error("Unable to convert file: impossible number of maps, %d.\n",
depth);
}
fputc(x,out); fputc(y,out); fputc(depth,out);
fputint(data_offset,out);
levels = depth;
code = malloc(x*y + 2);
while (depth--)
{
int lines = y;
size = 0;
while (lines--) {
fgets(code+size, x+2, in);
if ( strlen(code+size) != x+1 && code[size+x] != '\n' )
error("Map line %d wrong size: was %d, expected %d\n",
y, strlen(code+size)-1, x );
lineNumber++;
size += x;
}
/* Encode and store the whole map level. */
/* encode(&code,&size);*/
/* The size of the encoded data precedes each encoded block. */
fputshort(size,data);
fwrite( code, size, 1, data );
data_offset += size + 2; /* account for the putshort above. */
/* Permit any number of lines beginning with "=" to be at the
* end of a level (they can be used as a seperator between levels).
*/
c = fgetc(in);
if ( c != '=' )
error("level %d missing seperator line\n", levels-depth);
else
{
lineNumber++;
while ( c != '\n' && !feof(in) )
c = fgetc(in);
}
}
free(code);
}
/* Finally, dump the data in the data file into the output file. */
rewind(data);
do {
c = fgetc(data);
if ( c==EOF ) break;
fputc(c,out);
} while ( 1 );
fclose(in);
fclose(out);
fclose(data);
exit(EXIT_SUCCESS);
}