ravenengine/IDLINK/IDLINK.C

929 lines
17 KiB
C

#define VERSION "0.4"
/*
=============================================================================
IDLINK
by John Carmack
To do
-----
Allow .. in filenames
lump compression
allow block alignment for dynamic paging
=============================================================================
*/
#include <dir.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <io.h>
#include <dos.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <bios.h>
#include <alloc.h>
#include "\miscsrc\types.h"
#include "\miscsrc\minmisc.h"
#include "\miscsrc\script.h"
#pragma hdrstop
typedef struct
{
char id[4];
int numlumps;
long headeroffset;
int headerlength;
} compprologue_t;
typedef struct
{
char name[14];
int lumptype; // the command number used to produce the lump
long dataoffset;
long datalength;
} compheader_t;
typedef enum
{
co_uncompressed,
co_rle,
co_rlew,
co_huffman,
co_carmacized
} compress_t;
typedef struct
{
long filepos; // debug: make these three byte values?
long size;
unsigned nameofs;
compress_t compress;
} lumpinfo_t;
typedef struct
{
int numlumps;
long infotableofs;
long infotablesize;
} fileinfo_t;
fileinfo_t fileinfo,oldfileinfo;
lumpinfo_t *lumpinfo,*oldlumpinfo;
int *contentbuffer,*oldcontent;
char *content_p;
boolean incompfile;
int comphandle;
compprologue_t compprologue;
compheader_t *compheader,*compheader_p;
char filename[MAXPATH];
char filepath[MAXPATH];
char sourcepath[MAXPATH];
char destpath[MAXPATH];
char scriptfilename[MAXPATH];
char datafilename[MAXPATH];
char contentfilename[MAXPATH];
char compfilepath[MAXPATH];
byte *lumpalloc; // for lumpinfo and the names
char *name_p;
int datahandle;
int filescopied;
boolean fullbuild;
unsigned long oldtime; // time stamp of old file (bit fields)
unsigned long newtime;
/*
output filename
; any line starting with a non alphanumeric char is a comment
filename [-compression] [lumpname]
LABEL [lumpname]
idlink [-b][-path FILENAME] scriptfile
put in a SPARSE command
*/
/*
=============================================================================
GENERIC FUNCTIONS
=============================================================================
*/
int SafeOpenWrite (char *filename)
{
int handle;
handle = open(filename,O_RDWR | O_BINARY | O_CREAT | O_TRUNC
, S_IREAD | S_IWRITE);
if (handle == -1)
MS_Quit ("Error opening %s: %s\n",filename,strerror(errno));
return handle;
}
int SafeOpenRead (char *filename)
{
int handle;
handle = open(filename,O_RDONLY | O_BINARY);
if (handle == -1)
MS_Quit ("Error opening %s: %s\n",filename,strerror(errno));
return handle;
}
void SafeRead (int handle, void far *buffer, unsigned count)
{
unsigned iocount;
_dos_read (handle,buffer,count,&iocount);
if (iocount != count)
MS_Quit ("File read failure\n");
}
void SafeWrite (int handle, void far *buffer, unsigned count)
{
unsigned iocount;
_dos_write (handle,buffer,count,&iocount);
if (iocount != count)
MS_Quit ("File write failure\n");
}
void far *SafeMalloc (long size)
{
void far *ptr;
ptr = farmalloc (size);
if (!ptr)
MS_Quit ("Malloc failure for %l bytes\n",size);
return ptr;
}
/*
=============================================================================
COPOSITE FILE STUFF
=============================================================================
*/
/*
=================
=
= OpenComposite
=
=================
*/
void OpenComposite (void)
{
char *str;
if (incompfile)
{
close (comphandle);
free (compheader);
}
//
// get and qualify composite data file name
//
GetToken (false);
for (str = token ; *str ; str++)
if (*str == '.')
break;
if (!*str)
strcat (str,".DAT");
if (token[0] == '\\')
strcpy (compfilepath,token);
else
{
strcpy (compfilepath,sourcepath);
strcat (compfilepath,token);
}
//
// open it and read in header
//
comphandle = SafeOpenRead (compfilepath);
SafeRead (comphandle,&compprologue,sizeof(compprologue));
if (strncmp(compprologue.id,"SGRB",4))
MS_Quit ("Composite file %s doesn't have SGRB id\n",compfilepath);
compheader = SafeMalloc (compprologue.headerlength);
lseek (comphandle,compprologue.headeroffset,SEEK_SET);
SafeRead (comphandle,compheader,compprologue.headerlength);
getftime(comphandle,(struct ftime *)&newtime);
incompfile = true;
}
/*
=================
=
= CloseComposite
=
=================
*/
void CloseComposite (void)
{
if (!incompfile)
MS_Quit ("$CLOSECOMP issued without an open composite file\n");
close (comphandle);
free (compheader);
incompfile = false;
}
/*
=============================================================================
IDLINK FUNCTIONS
=============================================================================
*/
/*
===================
=
= QualifyFilename
=
===================
*/
void QualifyFilename (void)
{
if (incompfile)
{
if (filename[0] == '\\' || strlen(filename)>13)
MS_Quit ("Illegal comp file member name: %s\n",filename);
strcpy (filepath,compfilepath);
strcat (filepath,":");
strcat (filepath,filename);
return;
}
if (filename[0] == '\\')
strcpy (filepath,filename);
else
{
strcpy (filepath,sourcepath);
strcat (filepath,filename);
}
}
/*
=================
=
= ReadOldFile
=
= Tries to read in info from old file.
= If there is a problem, a full rebuild will be specified
=
=================
*/
void ReadOldFile (void)
{
int iocount,size;
int handle;
//
// open and read data file
//
datahandle = open(datafilename,O_RDWR | O_BINARY);
if (datahandle == -1)
{
printf ("No current file to do a partial build from. Rebuilding.\n");
fullbuild = true;
return;
}
iocount = read (datahandle,&oldfileinfo,sizeof(oldfileinfo));
if (iocount != sizeof(oldfileinfo))
{
printf ("Couldn't read old fileinfo. Rebuilding.\n");
close (datahandle);
fullbuild = true;
return;
}
size = oldfileinfo.infotablesize;
oldlumpinfo = malloc (size);
if (!oldlumpinfo)
{
printf ("Couldn't allocate %u bytes for oldinfotable. Rebuilding.\n"
,size);
close (datahandle);
fullbuild = true;
return;
}
lseek (datahandle,oldfileinfo.infotableofs,SEEK_SET);
iocount = read (datahandle , oldlumpinfo, size);
if (iocount != size)
{
printf ("Couldn't read old fileinfo. Rebuilding.\n");
close (datahandle);
fullbuild = true;
return;
}
//
// get timestamp
//
getftime(datahandle,(struct ftime *)&oldtime);
//
// load in the content file
//
handle = open(contentfilename,O_RDWR | O_BINARY);
if (handle == -1)
{
printf ("No content file. Rebuilding.\n");
close (datahandle);
fullbuild = true;
return;
}
size = filelength (handle);
oldcontent = malloc (size);
if (!oldcontent)
{
printf ("Couldn't allocate %u bytes for oldcontent. Rebuilding.\n"
,size);
close (handle);
close (datahandle);
fullbuild = true;
return;
}
iocount = read (handle,oldcontent,size);
if (iocount != size)
{
printf ("Couldn't read old content file. Rebuilding.\n");
close (handle);
close (datahandle);
fullbuild = true;
return;
}
}
/*
===================
=
= ParseScript
=
===================
*/
void ParseScript (void)
{
char *fname_p;
//
// load the script file into memory
//
LoadScriptFile (scriptfilename);
incompfile = false;
//
// get the output filename
//
GetToken (true);
strcpy (datafilename,destpath);
strcat (datafilename,token);
//
// the content file holds a list of the pathnames used to build the
// data file. Used for future makes
//
strcpy (contentfilename,datafilename);
fname_p = contentfilename+strlen(contentfilename);
while ( *fname_p != '.' )
if (--fname_p == datafilename)
{ // filename didn't have an extension
fname_p = contentfilename+strlen(contentfilename);
break;
}
strcpy (fname_p,".ICN");
printf ("Data file : %s\n",datafilename);
printf ("Content file : %s\n",contentfilename);
if (!fullbuild)
ReadOldFile ();
if (fullbuild)
{
unlink (datafilename);
datahandle = open(datafilename,O_RDWR | O_BINARY | O_CREAT, S_IREAD | S_IWRITE);
if (datahandle == -1)
MS_Quit ("Error opening data file: %s\n",strerror(errno));
}
//
// count the lumps in the script
//
fileinfo.numlumps = 0;
do
{
while (TokenAvailable ()) // skip to the end of line
GetToken (false);
GetToken (true); // get the next token
if (!endofscript && token[0] != '$') // don't count commands
fileinfo.numlumps++;
} while (!endofscript);
printf ("%i lumps in output file\n",fileinfo.numlumps);
//
// allocate space for the lump directory and names
//
lumpalloc = malloc (0xfff0);
if (!lumpalloc)
MS_Quit ("Couldn't allocate lump directory!\n");
lumpinfo = (lumpinfo_t *) (lumpalloc + 16-FP_OFF(lumpalloc));
name_p = (char *)(lumpinfo + fileinfo.numlumps);
//
// allocate space for the content file
//
contentbuffer = malloc (0xfff0);
if (!contentbuffer)
MS_Quit ("Couldn't allocate lump directory!\n");
content_p = (char *)(contentbuffer + fileinfo.numlumps);
*content_p++ = 0; // in case the first lump is a label
//
// position the file pointer to begin writing data
//
if (fullbuild)
// leave space in the data file for the header
lseek (datahandle,sizeof(fileinfo),SEEK_SET);
else
// go to the end of the file
lseek (datahandle,filelength (datahandle),SEEK_SET);
}
/*
===================
=
= CopyFiles
=
===================
*/
#define BUFFERSIZE 0xfff0
void CopyFiles (void)
{
int i;
int lump,oldlump;
int inputhandle;
long size;
char *buffer,*oldname_p;
filescopied = 0;
script_p = scriptbuffer;
GetToken (true); // skip output name
buffer = malloc (BUFFERSIZE);
for (lump=0 ; lump<fileinfo.numlumps ; lump++)
{
memset (&lumpinfo[lump],0,sizeof(lumpinfo[0]));
//
// check for abort out
//
if ( (bioskey(1)&0xff) == 27)
{
bioskey (0);
fprintf (stderr,"\nAborted.");
exit (1);
}
//
// get file to copy or label
//
GetToken (true);
if (token[0] == '$')
{
//
// link commands
//
lump--; // this line isn't a lump
if (!strcmpi(token,"$OPENCOMP") )
{
OpenComposite ();
continue;
}
if (!strcmpi(token,"$CLOSECOMP") )
{
CloseComposite ();
continue;
}
MS_Quit ("Unrocognized command %s\n",token);
}
strcpy (filename,token);
//
// check for a lump name
//
if (TokenAvailable ())
{
GetToken (false);
lumpinfo[lump].nameofs = name_p-(char *)lumpinfo;
strcpy (name_p,token);
name_p += strlen(token)+1;
}
else
lumpinfo[lump].nameofs = 0;
//
// deal with labels
//
if (strcmpi(filename,"LABEL") == 0)
{
// point the content listing at the previous 0
contentbuffer[lump] = content_p-1 - (char *)contentbuffer;
if (lumpinfo[lump].nameofs)
printf ("%4i is LABEL: %s\n",lump,token);
else
printf ("%4i is a LABEL\n",lump);
continue;
}
//
// qualify the filename and add it to the content list
//
QualifyFilename ();
contentbuffer[lump] = content_p - (char *)contentbuffer;
strcpy (content_p,filepath);
content_p += strlen(filepath)+1;
//
// open the source file
//
if (incompfile)
{
//
// look for the filename in the comp file header
//
for (i=0;i<compprologue.numlumps;i++)
if (!strcmp (filename,compheader[i].name) )
break;
if (i == compprologue.numlumps)
MS_Quit ("lump %s is not in comp file %s\n"
,filename,compfilepath);
compheader_p = compheader+i;
}
else
{
//
// open the file on disk
//
inputhandle = open(filepath,O_RDONLY | O_BINARY);
if (inputhandle == -1)
MS_Quit ("Error opening data file %s: %s\n"
,filepath,strerror(errno));
}
//
// if partial build, see if the file is present in
// old file and check time
//
if (!fullbuild)
{
for (oldlump=0 ; oldlump<oldfileinfo.numlumps ; oldlump++)
{
oldname_p = (char *)oldcontent + oldcontent[oldlump];
if (strcmp(filepath , oldname_p) == 0)
break;
}
if (oldlump != oldfileinfo.numlumps)
{
if (!incompfile)
getftime(inputhandle,(struct ftime *)&newtime);
if ( newtime <= oldtime)
{
//
// use the old information
//
lumpinfo[lump].filepos = oldlumpinfo[oldlump].filepos;
lumpinfo[lump].size = oldlumpinfo[oldlump].size;
lumpinfo[lump].compress = oldlumpinfo[oldlump].compress;
if (!incompfile)
close (inputhandle);
continue; // done with lump
}
}
}
//
// copy the file
//
filescopied++;
if (incompfile)
{
lseek (comphandle,compheader_p->dataoffset,SEEK_SET);
size = compheader_p->datalength;
}
else
size = filelength (inputhandle);
printf ("%4i = %s (%lu bytes)\n"
,lump,filepath,size);
lumpinfo[lump].filepos = tell(datahandle);
lumpinfo[lump].size = size;
lumpinfo[lump].compress = co_uncompressed;
do
{
read (inputhandle,buffer,BUFFERSIZE);
if (size < BUFFERSIZE)
write (datahandle,buffer,size);
else
write (datahandle,buffer,BUFFERSIZE);
size -= BUFFERSIZE;
} while (size > 0);
if (!incompfile)
close (inputhandle);
}
free (buffer);
}
/*
===================
=
= WriteDirectory
=
===================
*/
void WriteDirectory (void)
{
//
// write lumpinfo
//
fileinfo.infotableofs = tell(datahandle);
fileinfo.infotablesize = name_p - (char *)lumpinfo;
write (datahandle,lumpinfo,fileinfo.infotablesize);
//
// write fileinfo
//
lseek (datahandle,0,SEEK_SET);
write (datahandle,&fileinfo,sizeof(fileinfo));
close (datahandle);
}
/*
===================
=
= WriteContentFile
=
===================
*/
void WriteContentFile (void)
{
int handle,size,iocount;
unlink (contentfilename);
handle = open(contentfilename,O_RDWR | O_BINARY | O_CREAT, S_IREAD | S_IWRITE);
if (handle == -1)
MS_Quit ("Error opening content file: %s\n",strerror(errno));
size = content_p - (char *)contentbuffer;
iocount = write (handle,contentbuffer,size);
if (iocount != size)
MS_Quit ("Write error on content file\n");
close (handle);
}
/*
===================
=
= main
=
===================
*/
int main (void)
{
int parmnum,parmsleft;
printf ("\nIDLINK "VERSION" by John Carmack, copyright (c) 1992 Id Software\n");
parmsleft = _argc;
//
// check for help
//
if (MS_CheckParm ("?"))
{
printf (
"Usage: idlink [-b] [-source path] [-dest path] [-script scriptfile]\n\n"
"-b Force a full rebuild of the file, rather than a file\n"
" bulking partial update\n"
"\n"
"-source path To place the source for the files in another directory\n"
"\n"
"-dest path To place the linked file in another directory\n"
"\n"
"-script file The script name defaults to LINKFILE.ILN if not specified\n"
);
exit (1);
}
//
// check for full or partial build
//
if (MS_CheckParm ("b"))
{
printf ("Full rebuild\n");
fullbuild = true;
parmsleft--;
}
else
{
printf ("Partial make (file size may increase, use -b to rebuild)\n");
fullbuild = false;
}
//
// get source directory for data files
//
parmnum = MS_CheckParm ("source");
if (parmnum)
{
strcpy (sourcepath,_argv[parmnum+1]);
parmsleft -= 2;
}
else
{
strcpy(sourcepath, "X:\\");
sourcepath[0] = 'A' + getdisk();
getcurdir(0, sourcepath+3);
}
if (sourcepath[strlen(sourcepath)-1] != '\\')
strcat (sourcepath,"\\");
printf ("Source directory : %s\n",sourcepath);
//
// get destination directory for link file
//
parmnum = MS_CheckParm ("dest");
if (parmnum)
{
strcpy (destpath,_argv[parmnum+1]);
parmsleft -= 2;
}
else
{
strcpy(destpath, "X:\\");
destpath[0] = 'A' + getdisk();
getcurdir(0, destpath+3);
}
if (destpath[strlen(destpath)-1] != '\\')
strcat (destpath,"\\");
printf ("Destination directory : %s\n",destpath);
//
// get script file
//
parmnum = MS_CheckParm ("script");
if (parmnum)
{
strcpy (scriptfilename,_argv[parmnum+1]);
parmsleft -= 2;
}
else
{
strcpy(scriptfilename, "X:\\");
scriptfilename[0] = 'A' + getdisk();
getcurdir(0, scriptfilename+3);
strcat (scriptfilename,"\\LINKFILE.ILN");
}
printf ("Script file : %s\n",scriptfilename);
if (parmsleft != 1)
MS_Quit ("Improper parameters. IDLINK -? for help.\n");
//
// start doing stuff
//
ParseScript ();
CopyFiles ();
if (filescopied)
{
WriteDirectory ();
WriteContentFile ();
}
else
{
close (datahandle);
}
printf ("\n%u files copied.\n",filescopied);
return 0;
}