qzdoom/tools/makewad/makewad.c
Randy Heit 67da924a02 - Changed makewad's zip routines so that it simply stores files that cannot be
compressed smaller instead of using deflate anyway. Saves 133 bytes currently.


SVN r147 (trunk)
2006-05-27 02:17:30 +00:00

493 lines
12 KiB
C

// makewad.c
//
// Reads a wad specification from stdin.
// Produces a wad file -or- writes a Makefile to stdout.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include "zip.h"
#define MAX_LUMPS 4096
typedef struct
{
char magic[4];
unsigned int numlumps;
int infotableofs;
} wadinfo_t;
typedef struct
{
int filepos;
unsigned int size;
char name[8];
} filelump_t;
/*
// appendlump(wadfile, filename):
//
// open the file by filename, write all of its contents to the wadfile
//
// returns: 0 = success, 1 = error
*/
int appendlump (FILE *wadfile, char *filename)
{
char readbuf[64*1024];
FILE *lumpfile;
size_t readlen;
int ret = 0;
lumpfile = fopen (filename, "rb");
if (lumpfile == NULL)
{
fprintf (stderr, "Could not open %s: %s\n", filename, strerror(errno));
return 1;
}
while (lumpfile != NULL)
{
// try to read a chunk of data
readlen = fread (readbuf, 1, sizeof(readbuf), lumpfile);
// if we reached the end, or hit an error
if (readlen < sizeof(readbuf))
{
// if it's an error,
if (ferror (lumpfile))
{
// diagnose
fprintf (stderr, "Error reading %s: %s\n", filename, strerror(errno));
// set return code to error
ret |= 1;
}
// in any case, close the lump file to break the loop
fclose (lumpfile);
lumpfile = NULL;
}
// write whatever data we have in the buffer
// if we hit an error (less bytes written than given)
if (fwrite (readbuf, 1, readlen, wadfile) < readlen)
{
// diagnose
fprintf (stderr, "Error writing to wad: %s\n", strerror(errno));
// close the lump file to break the loop
fclose (lumpfile);
lumpfile = NULL;
// set return code to error
ret |= 1;
}
// if the lump file got closed, the loop exits
}
return ret;
}
/*
// appendtozip(zipFile, zipname, filename):
//
// write a given lump file (filename) to the zipFile as zipname
//
// zipFile: zip object to be written to
//
// zipname: name of the file inside the zip
// filename: file to read data from
//
// returns: 0 = success, 1 = error
*/
int appendtozip (zipFile zipfile, const char * zipname, const char *filename)
{
char *readbuf;
FILE *lumpfile;
size_t readlen;
size_t len;
zip_fileinfo zip_inf;
time_t currenttime;
struct tm * ltime;
// clear zip_inf structure
memset(&zip_inf, 0, sizeof(zip_inf));
// try to determine local time
time(&currenttime);
ltime = localtime(&currenttime);
// if succeeded,
if (ltime != NULL)
{
// put it into the zip_inf structure
zip_inf.tmz_date.tm_sec = ltime->tm_sec;
zip_inf.tmz_date.tm_min = ltime->tm_min;
zip_inf.tmz_date.tm_hour = ltime->tm_hour;
zip_inf.tmz_date.tm_mday = ltime->tm_mday;
zip_inf.tmz_date.tm_mon = ltime->tm_mon;
zip_inf.tmz_date.tm_year = ltime->tm_year;
}
// lumpfile = source file
lumpfile = fopen (filename, "rb");
if (lumpfile == NULL)
{
fprintf (stderr, "Could not open %s: %s\n", filename, strerror(errno));
return 1;
}
// len = source size
fseek (lumpfile, 0, SEEK_END);
len = ftell(lumpfile);
fseek (lumpfile, 0, SEEK_SET);
// allocate a buffer for the whole source file
readbuf = (char*)malloc(len);
if (readbuf == NULL)
{
fclose(lumpfile);
fprintf (stderr, "Could not allocate %d bytes\n", len);
return 1;
}
// read the whole source file into buffer
readlen = fread (readbuf, 1, len, lumpfile);
fclose(lumpfile);
// if read less bytes than expected,
if (readlen != len)
{
// diagnose and return error
free (readbuf);
fprintf (stderr, "Unable to read %s\n", filename);
return 1;
}
// file loaded
// create zip entry, giving entry name and zip_inf data,
// write data into zipfile, and close the zip entry
if (Z_OK != zipAddFileToZip(zipfile, zipname, &zip_inf, readbuf, (unsigned)len))
{
free (readbuf);
fprintf (stderr, "Unable to write %s to zip\n", filename);
return 1;
}
// all done
free (readbuf);
return 0;
}
/*
// buildwad(listfile, listfilename, makecmd, makefile)
//
// go through the listfile, either:
// writing dependencies (listfile + lumps with files) into a makefile,
// -or-
// writing lumps into a wad/zip file
//
// listfile: already opened source file
// listfilename: filename, if any - only for the makefile
// makecmd: given as argv[0] - only for the makefile
// makefile: output filename for makefile. determines mode:
// - if specified, we're writing deps into a makefile
// - otherwise, we're writing lumps into a wad/zip
//
*/
int buildwad (FILE *listfile, char *listfilename, char *makecmd, char *makefile)
{
// destination we're writing output into -
// one of these:
zipFile zipfile = NULL;
FILE *wadfile = NULL;
wadinfo_t header;
filelump_t directory[MAX_LUMPS];
char str[256]; // buffer for reading listfile line by line
char *pt;
char *lumpname, *filename;
int lineno = 0;
int ret = 0;
int i;
// prepare PWAD data
strncpy (header.magic, "PWAD", 4);
header.infotableofs = 0;
header.numlumps = 0;
memset (directory, 0, sizeof(directory));
// read the listfile line by line
while (fgets (str, sizeof(str), listfile))
{
lineno++; // counting lines for diagnostic purposes
// Strip comments
pt = strchr (str, '#');
if (pt) *pt = 0;
// Strip trailing whitespace
pt = str + strlen (str) - 1;
while (pt >= str && isspace(*pt)) pt--;
if (pt < str) continue;
pt[1] = 0;
// Skip leading whitespace
pt = str;
while (isspace(*pt)) pt++;
if (*pt == '@')
{ // Rest of line is wadfile to create
if (wadfile != NULL || zipfile != NULL)
{
fprintf (stderr, "Line %d: Tried to reopen wadfile as %s.\n", lineno, pt + 1);
if (wadfile != NULL) fclose (wadfile);
if (zipfile != NULL) zipClose (zipfile, NULL);
return 1;
}
if (!makefile) // if it's not a makefile,
{
int ln = (int)strlen(pt+1);
filename = pt+1;
if (ln >= 4)
{
// If the output file has an extension '.zip' or '.pk3' it will be in Zip format.
if (!stricmp(filename+ln-3, "ZIP") || !stricmp(filename+ln-3, "PK3"))
{
zipfile = zipOpen(filename, APPEND_STATUS_CREATE);
if (zipfile == NULL)
{
fprintf (stderr, "Line %d: Could not open %s: %s\n", lineno, filename, strerror(errno));
return 1;
}
}
}
}
else filename = makefile;
if (!zipfile) // if we didn't end up opening zip, it's a regular file
{
// open wadfile
wadfile = fopen (filename, makefile ? "w" : "wb");
if (wadfile == NULL)
{
fprintf (stderr, "Line %d: Could not open %s: %s\n", lineno, filename, strerror(errno));
return 1;
}
if (makefile)
{ // Write out the only rule the makefile has
fprintf (wadfile, "%s: %s", pt+1, listfilename);
}
else
{
// The correct header will be written once the wad is complete
fwrite (&header, sizeof(header), 1, wadfile);
}
}
continue;
} // @
// Everything up to the next whitespace is the lump name
lumpname = pt;
filename = NULL;
while (*pt && !isspace(*pt)) pt++;
// If there is more, skip whitespace and use the remainder of the line
// as the file to insert into the lump. If no filename is given, then
// a 0-length marker lump is inserted.
if (*pt)
{
*pt = 0;
filename = pt + 1;
while (*filename && isspace(*filename)) filename++;
if (*filename == 0) filename = NULL;
}
// must have output selected
if (wadfile == NULL && zipfile == NULL)
{
fprintf (stderr, "Line %d: No wad specified before lumps.\n", lineno);
return 1;
}
// if we're writing a makefile,
if (makefile)
{
// and the lump has a filename,
if (filename != NULL)
{
// add it as a dependency (with quotes, if needed)
if (strchr (filename, ' '))
{
fprintf (wadfile, " \\\n\t\"%s\"", filename);
}
else
{
fprintf (wadfile, " \\\n\t%s", filename);
}
}
}
else // not writing a makefile
{
if (zipfile == NULL) // must be a wadfile
{
// convert lumpname to uppercase
for (i = 0; lumpname[i]; ++i)
{
lumpname[i] = toupper(lumpname[i]);
}
// put name into directory entry
strncpy (directory[header.numlumps].name, lumpname, 8);
// put filepos into directory entry
directory[header.numlumps].filepos = ftell (wadfile);
// if filename given,
if (filename != NULL)
{
// append data to the wadfile
ret |= appendlump (wadfile, filename);
}
// put size into directory entry (how many bytes were just written)
directory[header.numlumps].size = ftell (wadfile) - directory[header.numlumps].filepos;
}
else if (filename != NULL) // writing a zip, and filename is non-null
{
// convert lumpname to lowercase
for (i = 0; lumpname[i]; ++i)
{
lumpname[i] = tolower(lumpname[i]);
}
// add lump data to the zip
ret |= appendtozip(zipfile, lumpname, filename);
}
// count all lumps
header.numlumps++;
}
} // end of line-by-line reading
// the finishing touches:
// - makefile: terminate the line, and write the action line
// - unzipped wad: write the directory at the end,
// and update the header with directory's position
// - zipped wad: just close it
// if we were writing a plain file,
if (wadfile != NULL)
{
// if it's makefile,
if (makefile)
{
// terminate the dependencies line,
// and write the action command
fprintf (wadfile, "\n\t%s %s\n", makecmd, listfilename);
}
else // otherwise, it's plain wad
{
// the directory of lumps will be written at the end,
// starting at the current position - put it into the header
header.infotableofs = ftell (wadfile);
// swap endianness, if needed
#ifdef WORDS_BIGENDIAN
#define SWAP(x) ((((x)>>24)|(((x)>>8) & 0xff00)|(((x)<<8) & 0xff0000)|((x)<<24)))
for (i = 0; i < header.numlumps; ++i)
{
directory[i].filepos = SWAP(directory[i].filepos);
directory[i].size = SWAP(directory[i].size);
}
#endif
// write the whole directory of lumps
if (fwrite (directory, sizeof(directory[0]), header.numlumps, wadfile) != header.numlumps)
{
// failed to write the whole directory
fprintf (stderr, "Error writing to wad: %s\n", strerror(errno));
ret |= 1;
}
else // success - seek to 0 and rewrite the header, now with offset
{
// endianness
#ifdef WORDS_BIGENDIAN
SWAP(header.infotableofs);
SWAP(header.numlumps);
#endif
// seek to 0
fseek (wadfile, 0, SEEK_SET);
// rewrite the header
if (fwrite (&header, sizeof(header), 1, wadfile) != 1)
{
fprintf (stderr, "Error writing to wad: %s\n", strerror(errno));
ret |= 1;
}
}
} // plain wad
fclose (wadfile);
} // wadfile!=NULL - wad or makefile
else if (zipfile != NULL)
{
// zip - just close it
zipClose(zipfile, NULL);
}
return ret;
}
// If building with GCC and not MinGW, macro __cdecl to nothing.
#if defined(__GNUC__) && !defined(__MINGW32__)
#define __cdecl
#endif
int __cdecl main (int argc, char **argv)
{
FILE *listfile = NULL;
char *listfilename = NULL;
char *makefile = NULL;
int ret;
int i;
for (i = 1; i < argc; ++i)
{
if (strcmp (argv[i], "-make") == 0)
{
if (i >= argc-1)
{
goto baduse;
}
makefile = argv[++i];
}
else if (listfile == NULL)
{
listfilename = argv[i];
listfile = fopen (listfilename, "r");
if (listfile == NULL)
{
fprintf (stderr, "Can't open %s: %s\n", listfilename, strerror(errno));
return 1;
}
}
else
{
fclose (listfile);
goto baduse;
}
}
if (makefile != NULL && listfile == NULL)
{
fprintf (stderr, "You must specify a listfile if you want a makefile\n");
return 1;
}
ret = buildwad (listfile ? listfile : stdin, listfilename, argv[0], makefile);
if (listfile != NULL)
{
fclose (listfile);
}
return ret;
baduse:
fprintf (stderr, "Usage: makewad [-make makefile] [listfile]\n");
return 1;
}