| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675 |
- /*
- ** Command & Conquer Red Alert(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /***********************************************************************************************
- ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S **
- ***********************************************************************************************
- * *
- * Project Name : Data file creator. *
- * *
- * File Name : MIXFILE.CPP *
- * *
- * Programmer : Joe L. Bostic *
- * *
- * Start Date : August 6, 1994 *
- * *
- * Last Update : 10/27/94 [JLB] *
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * Calc_CRC -- Calculate the CRC value of a block of data. *
- * compfunc -- Comparison function used by qsort(). *
- * DataClass::DataClass -- Constructor for a data file object node. *
- * DataClass::Process_Input -- Process the input file list and builds linked records. *
- * DataClass::Process_Output -- Creates the output data file from the component source files.*
- * DataClass::~DataClass -- Destructor for the data file object. *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #pragma inline
- #include <stdlib.h>
- #include <stdio.h>
- #include <ctype.h>
- #include <dir.h>
- #include <string.h>
- #define FALSE 0
- #define TRUE 1
- long Calc_CRC(void const *data, long size);
- /********************************************************************
- ** This is the data block controlling class. It is used for every data
- ** file as well as processing the entire data file list.
- */
- class DataClass {
- public:
- long CRC;
- static short Count;
- static long TotalSize;
- static char *ExtFrom[10];
- static char *ExtTo[10];
- static int ExtCount;
- static char *AltPath[10];
- static int AltPathCount;
- DataClass(char const *filename);
- ~DataClass(void);
- static void Process_Input(char const *infile, int quiet, int paths);
- static void Process_Output(char const *outfile);
- char const * Output_Filename(void);
- char const * Input_Filename(void);
- private:
- DataClass *Next; // Pointer to next file in chain.
- char *Filename; // Raw original filename.
- long Size; // Size of data element.
- long Offset; // Offset within mixfile for data start.
- int Index; // Write order number.
- static DataClass *First;
- static int Quiet;
- static int Paths;
- };
- char *DataClass::AltPath[10];
- char *DataClass::ExtFrom[10];
- char *DataClass::ExtTo[10];
- int DataClass::ExtCount = 0;
- int DataClass::AltPathCount = 0;
- short DataClass::Count = 0;
- DataClass * DataClass::First = 0;
- int DataClass::Quiet = FALSE;
- int DataClass::Paths = FALSE;
- long DataClass::TotalSize = 0;
- int main(int argc, char ** argv)
- {
- class UsageError{}; // Parameter error or usage display desired.
- char *infile = 0;
- char *outfile = 0;
- int quiet = FALSE;
- int paths = FALSE;
- /*
- ** Banner message.
- */
- printf("MIXFILE V1.4 (c)\n");
- /*
- ** Process the parameter list and dispatch the packing function.
- */
- try {
- /*
- ** If not enough parameters were specified, then immediately
- ** display the usage instructions.
- */
- if (argc < 2) throw UsageError();
- try {
- for (int index = 1; index < argc; index++) {
- char *arg = argv[index];
- switch (*arg) {
- /*
- ** Process any command line switches.
- */
- case '/':
- case '-':
- switch (toupper(arg[1])) {
- case 'Q':
- quiet = TRUE;
- break;
- case 'S':
- paths = TRUE;
- break;
- case 'E':
- if (DataClass::ExtCount >= sizeof(DataClass::ExtFrom)/sizeof(DataClass::ExtFrom[0])) {
- throw "Too many extensions specified";
- } else {
- char * ptr = strupr(strtok(&arg[2], "="));
- if (*ptr == '.') ptr++;
- DataClass::ExtFrom[DataClass::ExtCount] = ptr;
- ptr = strupr(strtok(NULL, "=\r\n"));
- if (*ptr == '.') ptr++;
- DataClass::ExtTo[DataClass::ExtCount] = ptr;
- DataClass::ExtCount++;
- }
- break;
- case 'I':
- if (DataClass::AltPathCount >= sizeof(DataClass::AltPath)/sizeof(DataClass::AltPath[0])) {
- throw "Too many paths specified";
- } else {
- char dir[MAXDIR];
- strcpy(dir, &arg[2]);
- if (dir[strlen(dir)-1] != '\\') {
- strcat(dir, "\\");
- }
- DataClass::AltPath[DataClass::AltPathCount++] = strupr(strdup(dir));
- }
- break;
- default:
- throw "Unrecognized option flag";
- }
- break;
- /*
- ** Process command line filenames for either input or output.
- */
- default:
- if (outfile) throw "Unrecognized parameter";
- if (!infile) {
- FILE *file = fopen(arg, "r");
- if (!file) throw "Unable to open input file";
- fclose(file);
- infile = arg;
- continue;
- }
- if (!outfile) {
- outfile = arg;
- }
- break;
- }
- }
- /*
- ** Perform a last minute check to make sure both an input and
- ** output filename was specified. If not, then throw an
- ** error.
- */
- if (!outfile) throw "No output file specified";
- if (!infile) throw "No input file specified";
- /*
- ** Process the data.
- */
- try {
- DataClass::Process_Input(infile, quiet, paths);
- DataClass::Process_Output(outfile);
- printf("Created mix file '%s'\nEmbedded objects = %d\nTotal file size = %ld\n", outfile, DataClass::Count, DataClass::TotalSize);
- }
- catch (char *message) {
- printf("\nERROR: %s.\n", message);
- exit(EXIT_FAILURE);
- }
- }
- /*
- ** This exception is called for any of the various fatal errors that
- ** can occur while parsing the parameters.
- */
- catch(char *message) {
- printf("\nERROR: %s.\n", message);
- throw UsageError();
- }
- }
- /*
- ** This exception is thrown when the utility can't proceed and the
- ** utility usage instructions are desired to be displayed.
- */
- catch(UsageError) {
- printf("\nUSAGE: DATFILE <controlfile> <datafile>\n"
- "\n"
- " <controlfile> : list of filenames to pack\n"
- " <datafile> : output data filename\n"
- " Options\n"
- " -q : quiet operation\n"
- " -i<path> : alternate to find files\n"
- " -e<ext1>=<ext2> : changes file extensions from <ext1> to <ext2>\n"
- " -s : record paths in ID\n");
- exit(EXIT_FAILURE);
- }
- return EXIT_SUCCESS;
- }
- /***********************************************************************************************
- * DataClass::Process_Input -- Process the input file list and builds linked records. *
- * *
- * This routine will process the list of files in the input file. It builds a linked *
- * list of DataClass objects. This routine is the initial process of creating a packed *
- * data file. * *
- * *
- * INPUT: infile -- Pointer to input filename. *
- * *
- * quiet -- Process quietly? *
- * *
- * paths -- Should the path of the filename be considered as part of its signature?*
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 08/06/1994 JLB : Created. *
- *=============================================================================================*/
- void DataClass::Process_Input(char const *infile, int quiet, int paths)
- {
- FILE *file; // Input file.
- static char buffer[MAXFILE];
- Quiet = quiet;
- Paths = paths;
- if (!infile) throw "No input file specified";
- file = fopen(infile, "ra");
- if (!file) throw "Could not open input file";
- if (!Quiet) {
- printf("Processing '%s'.\n", infile);
- }
- /*
- ** Process the source file list.
- */
- try {
- for (;;) {
- int result = fscanf(file, "%s", buffer);
- if (result == EOF) break;
- new DataClass(buffer);
- }
- }
- /*
- ** This error handler is used if there are any errors that occur while
- ** parsing and verifying the data block filenames. Errors can include
- ** source filename errors as well as missing source files themselves.
- */
- catch (char *message) {
- fclose(file);
- throw message;
- }
- fclose(file);
- }
- /***********************************************************************************************
- * DataClass::DataClass -- Constructor for a data file object node. *
- * *
- * This constructs a data file object node and links it into the list of object nodes. *
- * It performs a preliminary check on the existance of the the source file. *
- * *
- * INPUT: filename -- The filename of the object to add. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 08/06/1994 JLB : Created. *
- * 10/27/94 JLB : Handles multiple data paths. *
- *=============================================================================================*/
- DataClass::DataClass(char const *filename)
- {
- static char buffer[100];
-
- if (!filename) throw "NULL filename";
- Filename = strdup(filename);
- strupr(Filename);
- /*
- ** Try to find the data file.
- */
- FILE *datafile = fopen(Filename, "rb");
- if (!datafile) {
- /*
- ** If the file couldn't be found in the current directory, check
- ** the alternate paths. If the alternate path is successful, then
- ** alter the pathname to match and continue.
- */
- for (int index = 0; index < AltPathCount; index++) {
- strcpy(buffer, AltPath[index]);
- strcat(buffer, Filename);
- datafile = fopen(buffer, "rb");
- if (datafile) {
- free(Filename);
- Filename = strdup(buffer);
- break;
- }
- }
- if (!datafile) {
- sprintf(buffer, "Could not find source file '%s'", Filename);
- throw buffer;
- }
- }
- /*
- ** Determine the CRC identifier for the filename, This can be either
- ** the base filename or the complete path (as indicated by the command
- ** line parameter).
- */
- char const * name = Output_Filename();
- CRC = Calc_CRC(name, strlen(name));
- /*
- ** Find out the size of the source data file.
- */
- fseek(datafile, 0, SEEK_END);
- long size = ftell(datafile);
- fclose(datafile);
- if (size == -1) throw "Seek failure";
- Size = size;
- Index = Count;
- Count++;
- Next = First;
- First = this;
- }
- /***********************************************************************************************
- * DataClass::~DataClass -- Destructor for the data file object. *
- * *
- * This is the destructor for the data file object. It deallocates any memory allocated *
- * and de-links it from the list of file objects. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 08/06/1994 JLB : Created. *
- *=============================================================================================*/
- DataClass::~DataClass(void)
- {
- if (Filename) {
- free(Filename);
- Filename = 0;
- }
- Count--;
- DataClass *ptr = First;
- DataClass *prev = 0;
- while (ptr) {
- if (ptr == this) {
- if (prev) {
- prev->Next = Next;
- }
- return;
- }
- prev = ptr;
- ptr = ptr->Next;
- }
- }
- /***********************************************************************************************
- * compfunc -- Comparison function used by qsort(). *
- * *
- * This is a support function that compares two file data objects against their CRC *
- * values. *
- * *
- * INPUT: ptr1 -- Pointer to object number 1. *
- * *
- * ptr2 -- Pointer to object number 2. *
- * *
- * OUTPUT: Returns with a logical comparison of object 1 to object 2. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 08/06/1994 JLB : Created. *
- *=============================================================================================*/
- int compfunc(const void *ptr1, const void *ptr2)
- {
- DataClass const *obj1 = *(DataClass const **)ptr1;
- DataClass const *obj2 = *(DataClass const **)ptr2;
- if (obj1->CRC < obj2->CRC) {
- return (-1);
- }
- if (obj1->CRC > obj2->CRC) {
- return (1);
- }
- return(0);
- }
- /***********************************************************************************************
- * DataClass::Process_Output -- Creates the output data file from the component source files. *
- * *
- * This is the final step in creation of the data output file. It writes the header *
- * block and then appends the appropriate files to the output file. *
- * *
- * INPUT: outname -- Pointer to the filename to use for output. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 08/06/1994 JLB : Created. *
- *=============================================================================================*/
- void DataClass::Process_Output(char const *outname)
- {
- FILE *outfile;
- if (Count) {
- DataClass **array = new DataClass *[Count];
- /*
- ** Open the output file for creation.
- */
- if (!outname) throw "Missing output filename";
- outfile = fopen(outname, "wb");
- if (!outfile) {
- throw "Unable to open output file";
- }
- /*
- ** Build a working array to the file objects.
- */
- DataClass * ptr = First;
- for (int index = 0; index < Count; index++) {
- array[index] = ptr;
- ptr->Index = index;
- ptr = ptr->Next;
- }
- /*
- ** Precalculate the offset value for each data file will be.
- ** This value will be inserted into the header block. This is
- ** performed BEFORE the files are sorted because the data files
- ** are written to disk in the order that they were specified.
- */
- TotalSize = 0;
- for (index = 0; index < Count; index++) {
- array[index]->Offset = TotalSize;
- TotalSize += array[index]->Size;
- }
- /*
- ** Next, sort the data objects so that a binary search on CRC values
- ** can be used for file retrieval.
- */
- qsort(array, Count, sizeof(array[0]), compfunc);
- /*
- ** Output the header section of the data file. This contains
- ** the count of objects contained, the CRC, and offset for each.
- */
- fwrite(&Count, sizeof(Count), 1, outfile);
- fwrite(&TotalSize, sizeof(TotalSize), 1, outfile);
- for (index = 0; index < Count; index++) {
- fwrite(&array[index]->CRC, sizeof(array[index]->CRC), 1, outfile);
- fwrite(&array[index]->Offset, sizeof(array[index]->Offset), 1, outfile);
- fwrite(&array[index]->Size, sizeof(array[index]->Size), 1, outfile);
- }
- TotalSize += sizeof(Count) + sizeof(TotalSize) + (Count*12);
- if (!Quiet) {
- printf("size CRC Filename\n");
- printf("------ -------- -------------------------\n");
- }
- /*
- ** Now write the actual data -- one file at a time in the order that they were
- ** originally specified.
- */
- for (int order = 0; order < Count; order++) {
- for (index = 0; index < Count; index++) {
- DataClass * entry = array[index];
- if (entry->Index == order) {
- FILE *infile;
- long size;
- static char buffer[1024*30];
- if (!Quiet) {
- printf("%-8ld [%08lX] %s\n", entry->Size, entry->CRC, entry->Output_Filename());
- }
- size = entry->Size;
- infile = fopen(entry->Input_Filename(), "rb");
- while (size > 0) {
- int count;
- count = (size < sizeof(buffer)) ? (int)size : sizeof(buffer);
- count = fread(buffer, sizeof(buffer[0]), count, infile);
- fwrite(buffer, sizeof(buffer[0]), count, outfile);
- size -= count;
- if (!count) break; // Hmm..
- }
- fclose(infile);
- break;
- }
- }
- }
- fclose(outfile);
- }
- }
- /***********************************************************************************************
- * Calc_CRC -- Calculate the CRC value of a block of data. *
- * *
- * This routine is used to create a CRC value from a string of data bytes. *
- * *
- * INPUT: data -- Pointer to the data bytes to calculate the CRC value for. *
- * *
- * size -- The number of bytes in the data block. *
- * *
- * OUTPUT: Returns with the CRC value of the buffer specified. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 08/06/1994 JLB : Created. *
- *=============================================================================================*/
- long Calc_CRC(void const *data, long size)
- {
- long crc = 0; // Accumulating CRC value.
- long const *ptr = static_cast<long const *>(data);
- /*
- ** Process the bulk of the data (4 bytes at a time).
- */
- for (; size > sizeof(long); size -= sizeof(long)) {
- long temp = *ptr++;
- asm {
- mov eax,crc
- rol eax,1
- add eax,temp
- mov crc,eax
- }
- }
- /*
- ** If there are any remainder bytes, then process them
- ** as a group of four, but the trailing left over bytes
- ** are forced to be NULL.
- */
- if (size) {
- long temp = 0;
- memcpy(&temp, ptr, (size_t)size);
- asm {
- mov eax,crc
- rol eax,1
- add eax,temp
- mov crc,eax
- }
- }
- return (crc);
- }
- char const * DataClass::Output_Filename(void)
- {
- char file[MAXFILE];
- char ext[MAXEXT];
- char path[MAXPATH];
- char drive[MAXDRIVE];
- static char filename[255];
- /*
- ** Break the original filename into its component parts.
- */
- fnsplit(Filename, drive, path, file, ext);
- /*
- ** Substitute the extension if a substitution is called for.
- */
- for (int index = 0; index < ExtCount; index++) {
- if (stricmp(ExtFrom[index], &ext[1]) == 0) {
- strcpy(&ext[1], ExtTo[index]);
- break;
- }
- }
- /*
- ** Strip the path from the filename if path stripping is enabled.
- */
- if (!Paths) {
- fnmerge(filename, NULL, NULL, file, ext);
- } else {
- fnmerge(filename, drive, path, file, ext);
- }
- return(&filename[0]);
- }
- char const * DataClass::Input_Filename(void)
- {
- return(Filename);
- }
|