2
/*****************************************\
4
* GRFMerge - A program to integrate a *
5
* .GRD file generated by *
6
* GRFDiff into the respective *
10
* Copyright (C) 2003 by Josef Drexler *
13
* Permission granted to copy and redist- *
14
* ribute under the terms of the GNU GPL. *
15
* For more info please read the file *
16
* COPYING which should have come with *
19
\*****************************************/
32
static const U32 GRDmagic = 0x67fb49ad;
33
#define TEMPFILE "grfmerge.tmp"
35
static int alwaysyes = 0;
36
static char *grdfile = NULL;
37
static long grdofs = 0;
38
static int onlyshow = 0, issfx = 0;
40
static void usage(void)
44
" GRFMerge [options] %s[<GRF-File>]\n"
45
" Change sprites in the GRF file to the new ones from the GRD file.\n"
46
" If the GRF file is not specified, GRFMerge will modify the one\n"
47
" which the GRD file was generated from.\n"
50
" -h Show this help\n"
51
" -l Only show which sprites the GRD file contains, don't integrate them\n"
52
" -y Answer 'y' to all questions\n"
54
"GRFMerge is Copyright (C) 2003 by Josef Drexler <jdrexler@uwo.ca>\n"
55
"It may be freely copied and distributed.\n",
56
issfx?"":"<GRD-File> "
62
static int checkisselfextr(const char *exe)
70
if (!f && errno == ENOENT) {
71
// try appending .exe for Win2k
72
char *altexe = (char*) malloc(strlen(exe)+5);
74
strcat(altexe, ".exe");
75
f = fopen(altexe, "rb");
81
if (c[0] != 'M' || c[1] != 'Z')
84
fseek(f, 0x1c, SEEK_SET);
88
if (!strcmp(c, "JD")) {
92
grdofs = (long) r * (1L << e);
93
fseek(f, grdofs, SEEK_SET);
95
fread(&magic, 4, 1, f);
99
return issfx = magic == GRDmagic;
102
static void die(const char *text, ...)
106
va_start(argptr, text);
107
vprintf(text, argptr);
113
static int yesno(const char *txt)
121
c = tolower(getc(stdin));
134
static char *block = NULL;
135
#define BLOCKSIZE 8192
136
static void copyblock(size_t size, FILE *from, FILE *to)
141
if (size > BLOCKSIZE)
142
thisblock = BLOCKSIZE;
146
fread(block, 1, thisblock, from);
148
if (to) fwrite(block, 1, thisblock, to);
154
// this function reads a piece of data, and copies it to the other files
155
static void copy(void *data, size_t size, size_t n, FILE *from, FILE *to)
159
res = fread(data, size, n, from);
161
die("Error while reading, wanted %d, got %d: %s", n, res, strerror(errno));
164
res = fwrite(data, size, n, to);
166
die("Error while writing, wanted %d, wrote %d: %s", n, res, strerror(errno));
171
// Copy the data of a sprite from one file to another
172
// this is complicated because for some sprites, the GRF file stores
173
// the uncompressed length instead of how many bytes are in the file
175
// This code mostly copied from sprites.c, but to keep this program
176
// small I didn't link in sprites.c and removed the unnecessary steps
177
// from decodesprite (we're not actually interested in the uncompressed data,
181
static U16 copysprite(FILE *from, FILE *to)
190
printf("Copying from pos. %ld\n", ftell(from));
192
printf("Copying to pos. %ld\n", ftell(to));
195
if (feof(from)) return 0;
197
copy(&size, 2, 1, from, to);
199
if (!size) return 0; // EOF is also indicated by a zero-size sprite
201
copy(&info, 1, 1, from, to);
203
if (info == 0xff) { // verbatim data
204
copyblock(size, from, to);
208
if (info & 2) { // size is compressed size
209
copyblock(size-1, from, to); // already copied one byte
213
// the size given is the uncompressed size, so we need to go through
214
// the uncompression routine until we have enough bytes
215
copyblock(7, from, to);
218
copy(&code, 1, 1, from, to);
221
copy(&ofs, 1, 1, from, to);
222
reallen = -(code >> 3);
224
reallen = code ? code : 128;
225
copyblock(reallen, from, to);
228
die("\nOops, got too many bytes. How did that happen?\n"
229
"Size is %d, len is %d at GRF file pos %ld\n",
230
size, reallen, ftell(from));
237
static void skipsprite(FILE *f)
242
static void showpct(long now, long total, int spriteno, int *pct)
244
int newpct = 100L*now/total;
249
printf("\rSprite%5d Done:%3d%% \r", spriteno, newpct);
253
static int mergeset(FILE *grd, const char *grffile)
255
FILE *grf = NULL, *tmp = NULL;
256
char grflen, *grfname;
259
U16 version, i, j, numsprites, spriteno, curno;
260
int lastfrom = -2, lastto = lastfrom, lastpct = -1;
263
int skipped = onlyshow;
265
fread(&version, 2, 1, grd);
267
if (version > 1) die("This is a GRD file version %d, I don't know how to handle that.\n", version);
269
fread(&numsprites, 2, 1, grd);
270
fread(&grflen, 1, 1, grd);
272
grfname = (char*) malloc(grflen + 4); // +4 for .bak extension (safety margin)
273
if (!grfname) die("Out of memory.\n");
275
fread(grfname, 1, grflen, grd);
278
printf("Generated from: %s.grf\nSprites in file: ", grfname);
281
c=strrchr(grffile, '\\');
282
if (!c) c=strchr(grffile, ':');
289
j=strrchr(c, '.') - c;
293
if (strnicmp(c, grfname, j)) {
294
printf("Warning, this GRD file was generated from %s.GRF.\n", grfname);
295
printf("Are you sure you want to apply it to %s?", grffile);
296
if (!yesno("Skipping file\n")) {
297
for (i=0; i<numsprites; i++) {
298
fread(&spriteno, 2, 1, grd);
305
strcat(grfname, ".grf");
309
grf = fopen(grffile, "rb");
311
printf("Can't open %s: %s. File skipped.\n", grffile, strerror(errno));
316
fseek(grf, 0, SEEK_END);
317
grfsize = ftell(grf);
318
fseek(grf, 0, SEEK_SET);
320
c=strchr(grffile, ':');
322
strncpy(tempfile, grffile, sizeof(tempfile)-1);
324
if (i<sizeof(tempfile))
328
strncat(tempfile, TEMPFILE, sizeof(tempfile)-strlen(tempfile)-1);
330
tmp = fopen(tempfile, "wb");
331
if (!tmp) die("Can't open %s: %s\n", tempfile, strerror(errno));
333
printf("Writing temporary file %s\n", tempfile);
337
for (i=0; i<numsprites; i++) {
338
fread(&spriteno, 2, 1, grd);
342
if (spriteno != lastto+1) {
343
if (lastfrom != lastto) printf("-%d", lastto);
344
if (lastfrom >= 0) printf(", ");
346
printf("%d", lastfrom);
354
while (++curno <= spriteno) {
355
copysprite(grf, tmp);
356
showpct(ftell(grf), grfsize, curno, &lastpct);
360
copysprite(grd, tmp);
361
showpct(ftell(grf), grfsize, curno, &lastpct);
367
printf("No sprites.");
368
else if (lastfrom != lastto)
369
printf("-%d", lastto);
371
} else if (!skipped) {
372
// copy remaining sprites, if any
373
for (; copysprite(grf, tmp); )
374
showpct(ftell(grf), grfsize, ++curno, &lastpct);
375
showpct(grfsize, grfsize, curno, &lastpct);
377
// write the dummy checksum
379
fwrite(&dummy, 4, 1, tmp);
382
if (tmp) fclose(tmp);
383
if (grf) fclose(grf);
390
// rename grf to bak if bak doesn't exist
391
strcpy(block, grffile);
392
c = strrchr(block, '.');
393
if (!c) c = block + strlen(block);
396
tmp = fopen(block, "rb");
398
if (!tmp && (errno == ENOENT)) {
399
// .bak doesn't exist, rename .grf to .bak
400
printf("Renaming %s to %s\n", grffile, block);
401
if (rename(grffile, block)) {
402
printf("Error while renaming: %s\n", strerror(errno));
403
printf("Shall I delete it instead?");
404
if (!yesno("Aborted.\n")) {
407
errno = EEXIST; // go delete it
409
errno = ENOENT; // don't try to delete it
412
if (tmp || (errno != ENOENT)) {
413
printf("Deleting %s\n", grffile);
415
die("Error while deleting: %s\n", strerror(errno));
419
printf("Renaming %s to %s\n", tempfile, grffile);
420
if (rename(tempfile, grffile))
421
die("Error while renaming: %s\n", strerror(errno));
423
printf("All done!\n");
431
static int domerge(int argc, char **argv)
437
grd = fopen(grdfile, "rb");
438
if (!grd) die("Can't open %s: %s\n", grdfile, strerror(errno));
440
block = (char*) malloc(BLOCKSIZE);
441
if (!block) die("Out of memory.\n");
443
// read all sets of sprites
448
fseek(grd, grdofs, SEEK_SET);
449
fread(&magic, 4, 1, grd);
451
if (magic != GRDmagic) {
453
die("This is not a GRD file.\n");
460
mergeset(grd, optind < argc ? argv[optind++] : NULL);
466
if (grd) fclose(grd);
473
int main(int argc, char **argv)
477
printf("GRFMerge version " GRFCODECVER " - Copyright (C) 2003 by Josef Drexler\n");
483
checkisselfextr(argv[0]);
486
opt = getopt(argc, argv, "hly");
488
if (opt == (char) EOF)
502
if (!issfx && (optind < argc) ) // no first arg if we're self-extracting
503
grdfile = argv[optind++];
506
// see if the GRD file specified is a self-extracting executable
507
// also sets grdofs to skip the .exe code
508
checkisselfextr(grdfile);
510
// no GRD file specified; see if we're a self-extracting executable
514
printf("No GRD file specified!\n");
519
return domerge(argc, argv);