CnC_Remastered_Collection

Command and Conquer: Red Alert
Log | Files | Refs | README | LICENSE

MIXFILE.CPP (28494B)


      1 //
      2 // Copyright 2020 Electronic Arts Inc.
      3 //
      4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 
      5 // software: you can redistribute it and/or modify it under the terms of 
      6 // the GNU General Public License as published by the Free Software Foundation, 
      7 // either version 3 of the License, or (at your option) any later version.
      8 
      9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 
     10 // in the hope that it will be useful, but with permitted additional restrictions 
     11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 
     12 // distributed with this program. You should have received a copy of the 
     13 // GNU General Public License along with permitted additional restrictions 
     14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
     15 
     16 /* $Header: /CounterStrike/MIXFILE.CPP 2     3/13/97 2:06p Steve_tall $ */
     17 /***********************************************************************************************
     18  ***              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               ***
     19  ***********************************************************************************************
     20  *                                                                                             *
     21  *                 Project Name : Command & Conquer                                            *
     22  *                                                                                             *
     23  *                    File Name : MIXFILE.CPP                                                  *
     24  *                                                                                             *
     25  *                   Programmer : Joe L. Bostic                                                *
     26  *                                                                                             *
     27  *                   Start Date : August 8, 1994                                               *
     28  *                                                                                             *
     29  *                  Last Update : July 12, 1996 [JLB]                                          *
     30  *                                                                                             *
     31  *                                                                                             *
     32  *                  Modified by Vic Grippi for WwXlat Tool 10/14/96                            *
     33  *                                                                                             *
     34  *---------------------------------------------------------------------------------------------*
     35  * Functions:                                                                                  *
     36  *   MixFileClass::Cache -- Caches the named mixfile into RAM.                                 *
     37  *   MixFileClass::Cache -- Loads this particular mixfile's data into RAM.                     *
     38  *   MixFileClass::Finder -- Finds the mixfile object that matches the name specified.         *
     39  *   MixFileClass::Free -- Uncaches a cached mixfile.                                          *
     40  *   MixFileClass::MixFileClass -- Constructor for mixfile object.                             *
     41  *   MixFileClass::Offset -- Searches in mixfile for matching file and returns offset if found.*
     42  *   MixFileClass::Retrieve -- Retrieves a pointer to the specified data file.                 *
     43  *   MixFileClass::~MixFileClass -- Destructor for the mixfile object.                         *
     44  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
     45 
     46 
     47 #include	"buff.h"
     48 #include	"function.h"
     49 #include	<direct.h>
     50 #include	<fcntl.h>
     51 #include	<io.h>
     52 #include	<dos.h>
     53 #include	<errno.h>
     54 #include	<share.h>
     55 #include	"mixfile.h"
     56 
     57 #include	"cdfile.h"
     58 extern MFCD temp;
     59 
     60 //template<class T> int Compare(T const *obj1, T const *obj2) {
     61 //	if (*obj1 < *obj2) return(-1);
     62 //	if (*obj1 > *obj2) return(1);
     63 //	return(0);
     64 //};
     65 
     66 
     67 /*
     68 **	This is the pointer to the first mixfile in the list of mixfiles registered
     69 **	with the mixfile system.
     70 */
     71 template<class T>
     72 List<MixFileClass<T> > MixFileClass<T>::List;
     73 
     74 template class MixFileClass<CCFileClass>;
     75 
     76 /***********************************************************************************************
     77  * MixFileClass::Free -- Uncaches a cached mixfile.                                            *
     78  *                                                                                             *
     79  *    Use this routine to uncache a mixfile that has been cached.                              *
     80  *                                                                                             *
     81  * INPUT:   filename -- Pointer to the filename of the mixfile that is to be uncached.         *
     82  *                                                                                             *
     83  * OUTPUT:  bool; Was the mixfile found and freed?                                             *
     84  *                                                                                             *
     85  * WARNINGS:   none                                                                            *
     86  *                                                                                             *
     87  * HISTORY:                                                                                    *
     88  *   01/23/1995 JLB : Created.                                                                 *
     89  *=============================================================================================*/
     90 template<class T>
     91 bool MixFileClass<T>::Free(char const * filename)
     92 {
     93 	MixFileClass * ptr = Finder(filename);
     94 
     95 	if (ptr) {
     96 		ptr->Free();
     97 		return(true);
     98 	}
     99 	return(false);
    100 }
    101 
    102 
    103 /***********************************************************************************************
    104  * MixFileClass::~MixFileClass -- Destructor for the mixfile object.                           *
    105  *                                                                                             *
    106  *    This destructor will free all memory allocated by this mixfile and will remove it from   *
    107  *    the system. A mixfile removed in this fashion must be created anew in order to be        *
    108  *    subsequent used.                                                                         *
    109  *                                                                                             *
    110  * INPUT:   none                                                                               *
    111  *                                                                                             *
    112  * OUTPUT:  none                                                                               *
    113  *                                                                                             *
    114  * WARNINGS:   none                                                                            *
    115  *                                                                                             *
    116  * HISTORY:                                                                                    *
    117  *   08/08/1994 JLB : Created.                                                                 *
    118  *   01/06/1995 JLB : Puts mixfile header table into EMS.                                      *
    119  *=============================================================================================*/
    120 template<class T>
    121 MixFileClass<T>::~MixFileClass(void)
    122 {
    123 	/*
    124 	**	Deallocate any allocated memory.
    125 	*/
    126 	if (Filename) {
    127 		free((char *)Filename);
    128 	}
    129 	if (Data != NULL && IsAllocated) {
    130 		delete [] Data;
    131 		IsAllocated = false;
    132 	}
    133 	Data = NULL;
    134 
    135 	if (HeaderBuffer != NULL) {
    136 		delete [] HeaderBuffer;
    137 		HeaderBuffer = NULL;
    138 	}
    139 
    140 	/*
    141 	**	Unlink this mixfile object from the chain.
    142 	*/
    143 	Unlink();
    144 }
    145 
    146 
    147 /***********************************************************************************************
    148  * MixFileClass::MixFileClass -- Constructor for mixfile object.                               *
    149  *                                                                                             *
    150  *    This is the constructor for the mixfile object. It takes a filename and a memory         *
    151  *    handler object and registers the mixfile object with the system. The index block is      *
    152  *    allocated and loaded from disk by this routine.                                          *
    153  *                                                                                             *
    154  * INPUT:   filename -- Pointer to the filename of the mixfile object.                         *
    155  *                                                                                             *
    156  * OUTPUT:  none                                                                               *
    157  *                                                                                             *
    158  * WARNINGS:   none                                                                            *
    159  *                                                                                             *
    160  * HISTORY:                                                                                    *
    161  *   08/08/1994 JLB : Created.                                                                 *
    162  *   07/12/1996 JLB : Handles compressed file header.                                          *
    163  *=============================================================================================*/
    164 template<class T>
    165 MixFileClass<T>::MixFileClass(char const * filename, PKey const * key) :
    166 	IsDigest(false),
    167 	IsEncrypted(false),
    168 	IsAllocated(false),
    169 	Filename(0),
    170 	Count(0),
    171 	DataSize(0),
    172 	DataStart(0),
    173 	HeaderBuffer(0),
    174 	Data(0)
    175 {
    176 	if (filename == NULL) return;	// ST - 5/9/2019
    177 
    178 	/*
    179 	**	Check to see if the file is available. If it isn't, then
    180 	**	no further processing is needed or possible.
    181 	*/
    182 	if (!Force_CD_Available(RequiredCD)) {
    183 		Prog_End("MixFileClass Force_CD_Available failed", true);
    184 		if (!RunningAsDLL) {	//PG
    185 			Emergency_Exit(EXIT_FAILURE);
    186 		}
    187 	}
    188 
    189 	T file(filename);		// Working file object.
    190 	Filename = strdup(file.File_Name());
    191 	FileStraw fstraw(file);
    192 	PKStraw pstraw(PKStraw::DECRYPT, CryptRandom);
    193 	Straw * straw = &fstraw;
    194 
    195 	if (!file.Is_Available()) return;
    196 
    197 	/*
    198 	**	Stuctures used to hold the various file headers.
    199 	*/
    200 	FileHeader fileheader;
    201 	struct {
    202 		short First;		// Always zero for extended mixfile format.
    203 		short Second;		// Bitfield of extensions to this mixfile.
    204 	} alternate;
    205 
    206 	/*
    207 	**	Fetch the first bit of the file. From this bit, it is possible to detect
    208 	**	whether this is an extended mixfile format or the plain format. An
    209 	**	extended format may have extra options or data layout.
    210 	*/
    211 	int got = straw->Get(&alternate, sizeof(alternate));
    212 
    213 	/*
    214 	**	Detect if this is an extended mixfile. If so, then see if it is encrypted
    215 	**	and/or has a message digest attached. Otherwise, just retrieve the
    216 	**	plain mixfile header.
    217 	*/
    218 	if (alternate.First == 0) {
    219 		IsDigest = ((alternate.Second & 0x01) != 0);
    220 		IsEncrypted = ((alternate.Second & 0x02) != 0);
    221 
    222 		if (IsEncrypted) {
    223 			pstraw.Key(key);
    224 			pstraw.Get_From(&fstraw);
    225 			straw = &pstraw;
    226 		}
    227 		straw->Get(&fileheader, sizeof(fileheader));
    228 
    229 	} else {
    230 		memmove(&fileheader, &alternate, sizeof(alternate));
    231 		straw->Get(((char*)&fileheader)+sizeof(alternate), sizeof(fileheader)-sizeof(alternate));
    232 	}
    233 
    234 	Count = fileheader.count;
    235 	DataSize = fileheader.size;
    236 //BGMono_Printf("Mixfileclass %s DataSize: %08x   \n",filename,DataSize);Get_Key();
    237 	/*
    238 	**	Load up the offset control array. If RAM is exhausted, then the mixfile is invalid.
    239 	*/
    240 	HeaderBuffer = new SubBlock [Count];
    241 	if (HeaderBuffer == NULL) return;
    242 	straw->Get(HeaderBuffer, Count * sizeof(SubBlock));
    243 
    244 	/*
    245 	**	The start of the embedded mixfile data will be at the current file offset.
    246 	**	This should be true even if the file header has been encrypted because the file
    247 	**	header was cleverly written with just the sufficient number of padding bytes so
    248 	**	that this condition would be true.
    249 	*/
    250 	DataStart = file.Seek(0, SEEK_CUR) + file.BiasStart;
    251 //	DataStart = file.Seek(0, SEEK_CUR);
    252 
    253 	/*
    254 	**	Attach to list of mixfiles.
    255 	*/
    256 	List.Add_Tail(this);
    257 }
    258 
    259 
    260 /***********************************************************************************************
    261  * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file.                   *
    262  *                                                                                             *
    263  *    This routine will return with a pointer to the specified data file if the file resides   *
    264  *    in memory. Otherwise, this routine returns NULL. Use this routine to access a resident   *
    265  *    file directly rather than going through the process of pseudo disk access. This will     *
    266  *    save both time and RAM.                                                                  *
    267  *                                                                                             *
    268  * INPUT:   filename -- Pointer to the filename of the data file to retrieve a pointer to.     *
    269  *                                                                                             *
    270  * OUTPUT:  Returns with a pointer to the data file's data. If the file is not in RAM, then    *
    271  *          NULL is returned.                                                                  *
    272  *                                                                                             *
    273  * WARNINGS:   none                                                                            *
    274  *                                                                                             *
    275  * HISTORY:                                                                                    *
    276  *   08/23/1994 JLB : Created.                                                                 *
    277  *=============================================================================================*/
    278 template<class T>
    279 void const * MixFileClass<T>::Retrieve(char const * filename)
    280 {
    281 	void * ptr = 0;
    282 	Offset(filename, &ptr);
    283 	return(ptr);
    284 };
    285 
    286 
    287 /***********************************************************************************************
    288  * MixFileClass::Finder -- Finds the mixfile object that matches the name specified.           *
    289  *                                                                                             *
    290  *    This routine will scan through all registered mixfiles and return with a pointer to      *
    291  *    the matching mixfile. If no mixfile could be found that matches the name specified,      *
    292  *    then NULL is returned.                                                                   *
    293  *                                                                                             *
    294  * INPUT:   filename -- Pointer to the filename to search for.                                 *
    295  *                                                                                             *
    296  * OUTPUT:  Returns with a pointer to the matching mixfile -- if found.                        *
    297  *                                                                                             *
    298  * WARNINGS:   none                                                                            *
    299  *                                                                                             *
    300  * HISTORY:                                                                                    *
    301  *   08/08/1994 JLB : Created.                                                                 *
    302  *   06/08/1996 JLB : Only compares filename and extension.                                    *
    303  *=============================================================================================*/
    304 template<class T>
    305 MixFileClass<T> * MixFileClass<T>::Finder(char const * filename)
    306 {
    307 	MixFileClass<T> * ptr = List.First();
    308 	while (ptr->Is_Valid()) {
    309 		char path[_MAX_PATH];
    310 		char name[_MAX_FNAME];
    311 		char ext[_MAX_EXT];
    312 
    313 		/*
    314 		**	Strip the drive and path (if present) off of the filename
    315 		**	in the mixfile list. This enables a simple comparison to the
    316 		**	filename specified. The filename specified won't have a path attached and
    317 		**	the full pathname in the mixfile list WILL have a path attached. Hence, this
    318 		** stripping of the path is necessary.
    319 		*/
    320 		_splitpath(ptr->Filename, NULL, NULL, name, ext);
    321 		_makepath(path, NULL, NULL, name, ext);
    322 
    323 		if (stricmp(path, filename) == 0) {
    324 			return(ptr);
    325 		}
    326 		ptr = ptr->Next();
    327 	}
    328 	return(0);
    329 }
    330 
    331 
    332 /***********************************************************************************************
    333  * MixFileClass::Cache -- Caches the named mixfile into RAM.                                   *
    334  *                                                                                             *
    335  *    This routine will cache the mixfile, specified by name, into RAM.                        *
    336  *                                                                                             *
    337  * INPUT:   filename -- The name of the mixfile that should be cached.                         *
    338  *                                                                                             *
    339  * OUTPUT:  bool; Was the cache successful?                                                    *
    340  *                                                                                             *
    341  * WARNINGS:   This routine could go to disk for a very long time.                             *
    342  *                                                                                             *
    343  * HISTORY:                                                                                    *
    344  *   08/08/1994 JLB : Created.                                                                 *
    345  *=============================================================================================*/
    346 template<class T>
    347 bool MixFileClass<T>::Cache(char const * filename, Buffer const * buffer)
    348 {
    349 	MixFileClass<T> * mixer = Finder(filename);
    350 
    351 	if (mixer != NULL) {
    352 		return(mixer->Cache(buffer));
    353 	}
    354 	return(false);
    355 }
    356 
    357 
    358 /***********************************************************************************************
    359  * MixFileClass::Cache -- Loads this particular mixfile's data into RAM.                       *
    360  *                                                                                             *
    361  *    This load the mixfile data into ram for this mixfile object. This is the counterpart     *
    362  *    to the Free() function.                                                                  *
    363  *                                                                                             *
    364  * INPUT:   none                                                                               *
    365  *                                                                                             *
    366  * OUTPUT:  bool; Was the file load successful?  It could fail if there wasn't enough room     *
    367  *                to allocate the raw data block.                                              *
    368  *                                                                                             *
    369  * WARNINGS:   This routine goes to disk for a potentially very long time.                     *
    370  *                                                                                             *
    371  * HISTORY:                                                                                    *
    372  *   08/08/1994 JLB : Created.                                                                 *
    373  *   07/12/1996 JLB : Handles attached message digest.                                         *
    374  *=============================================================================================*/
    375 template<class T>
    376 bool MixFileClass<T>::Cache(Buffer const * buffer)
    377 {
    378 	/*
    379 	**	If the mixfile is already cached, then no action needs to be performed.
    380 	*/
    381 	if (Data != NULL) return(true);
    382 
    383 	/*
    384 	**	If a buffer was supplied (and it is big enough), then use it as the data block
    385 	**	pointer. Otherwise, the data block must be allocated.
    386 	*/
    387 	if (buffer != NULL) {
    388 		if (buffer->Get_Size() == 0 || buffer->Get_Size() >= DataSize) {
    389 			Data = buffer->Get_Buffer();
    390 		}
    391 	} else {
    392 		Data = new char [DataSize];
    393 		IsAllocated = true;
    394 	}
    395 
    396 	/*
    397 	**	If there is a data buffer to fill, then fill it now.
    398 	*/
    399 	if (Data != NULL) {
    400 		T file(Filename);
    401 
    402 		FileStraw fstraw(file);
    403 		Straw * straw = &fstraw;
    404 
    405 		/*
    406 		**	If a message digest is attached, then link a SHA straw segment to the data
    407 		**	stream so that the actual SHA can be compared with the attached one.
    408 		*/
    409 		SHAStraw sha;
    410 		if (IsDigest) {
    411 			sha.Get_From(fstraw);
    412 			straw = &sha;
    413 		}
    414 
    415 		/*
    416 		**	Bias the file to the actual start of the data. This is necessary because the
    417 		**	real data starts some distance (not so easily determined) from the beginning of
    418 		**	the real file.
    419 		*/
    420 		file.Open(READ);
    421 		file.Bias(0);
    422 		file.Bias(DataStart);
    423 
    424 		/*
    425 		**	Fetch the whole mixfile data in one step. If the number of bytes retrieved
    426 		**	does not equal that requested, then this indicates a serious error.
    427 		*/
    428 		long actual = straw->Get(Data, DataSize);
    429 		if (actual != DataSize) {
    430 			delete [] Data;
    431 			Data = NULL;
    432 			file.Error(EIO);
    433 			return(false);
    434 		}
    435 
    436 		/*
    437 		**	If there is a digest attached to this mixfile, then read it in and
    438 		**	compare it to the generated digest. If they don't match, then
    439 		**	return with the "failure to cache" error code.
    440 		*/
    441 		if (IsDigest) {
    442 			char digest1[20];
    443 			char digest2[20];
    444 			sha.Result(digest2);
    445 			fstraw.Get(digest1, sizeof(digest1));
    446 			if (memcmp(digest1, digest2, sizeof(digest1)) != 0) {
    447 				delete [] Data;
    448 				Data = NULL;
    449 				return(false);
    450 			}
    451 		}
    452 
    453 		return(true);
    454 	}
    455 	IsAllocated = false;
    456 	return(false);
    457 }
    458 
    459 
    460 /***********************************************************************************************
    461  * MixFileClass::Free -- Frees the allocated raw data block (not the index block).             *
    462  *                                                                                             *
    463  *    This routine will free the (presumably large) raw data block, but leave the index        *
    464  *    block intact. By using this in conjunction with the Cache() function, one can maintain   *
    465  *    tight control of memory usage. If the index block is desired to be freed, then the       *
    466  *    mixfile object must be deleted.                                                          *
    467  *                                                                                             *
    468  * INPUT:   none                                                                               *
    469  *                                                                                             *
    470  * OUTPUT:  none                                                                               *
    471  *                                                                                             *
    472  * WARNINGS:   none                                                                            *
    473  *                                                                                             *
    474  * HISTORY:                                                                                    *
    475  *   08/08/1994 JLB : Created.                                                                 *
    476  *=============================================================================================*/
    477 template<class T>
    478 void MixFileClass<T>::Free(void)
    479 {
    480 	if (Data != NULL && IsAllocated) {
    481 		delete [] Data;
    482 	}
    483 	Data = NULL;
    484 	IsAllocated = false;
    485 }
    486 
    487 
    488 int compfunc(void const * ptr1, void const * ptr2)
    489 {
    490 	if (*(long const *)ptr1 < *(long const *)ptr2) return(-1);
    491 	if (*(long const *)ptr1 > *(long const *)ptr2) return(1);
    492 	return(0);
    493 }
    494 
    495 
    496 /***********************************************************************************************
    497  * MixFileClass::Offset -- Determines the offset of the requested file from the mixfile system.*
    498  *                                                                                             *
    499  *    This routine will take the filename specified and search through the mixfile system      *
    500  *    looking for it. If the file was found, then the mixfile it was found in, the offset      *
    501  *    from the start of the mixfile, and the size of the embedded file will be returned.       *
    502  *    Using this method it is possible for the CCFileClass system to process it as a normal    *
    503  *    file.                                                                                    *
    504  *                                                                                             *
    505  * INPUT:   filename    -- The filename to search for.                                         *
    506  *                                                                                             *
    507  *          realptr     -- Stores a pointer to the start of the file in memory here. If the    *
    508  *                         file is not in memory, then NULL is stored here.                    *
    509  *                                                                                             *
    510  *          mixfile     -- The pointer to the corresponding mixfile is placed here. If no      *
    511  *                         mixfile was found that contains the file, then NULL is stored here. *
    512  *                                                                                             *
    513  *          offset      -- The starting offset from the beginning of the parent mixfile is     *
    514  *                         stored here.                                                        *
    515  *                                                                                             *
    516  *          size        -- The size of the embedded file is stored here.                       *
    517  *                                                                                             *
    518  * OUTPUT:  bool; Was the file found? The file may or may not be resident, but it does exist   *
    519  *                 and can be opened.                                                           *
    520  *                                                                                             *
    521  * WARNINGS:   none                                                                            *
    522  *                                                                                             *
    523  * HISTORY:                                                                                    *
    524  *   10/17/1994 JLB : Created.                                                                 *
    525  *=============================================================================================*/
    526 template<class T>
    527 bool MixFileClass<T>::Offset(char const * filename, void ** realptr, MixFileClass ** mixfile, long * offset, long * size) 
    528 {
    529 	MixFileClass<T> * ptr;
    530 
    531 	if (filename == NULL) {
    532 assert(filename != NULL);//BG
    533 		return(false);
    534 	}
    535 
    536 	/*
    537 	**	Create the key block that will be used to binary search for the file.
    538 	*/
    539 	// Can't call strupr on a const string. ST - 5/20/2019
    540 	//long crc = Calculate_CRC(strupr((char *)filename), strlen(filename));
    541 	char filename_upper[_MAX_PATH];
    542 	strcpy(filename_upper, filename);
    543 	strupr(filename_upper);
    544 	long crc = Calculate_CRC(strupr(filename_upper), strlen(filename_upper));
    545 	SubBlock key;
    546 	key.CRC = crc;
    547 
    548 	/*
    549 	**	Sweep through all registered mixfiles, trying to find the file in question.
    550 	*/
    551 	ptr = List.First();
    552 	while (ptr->Is_Valid()) {
    553 		SubBlock * block;
    554 
    555 		/*
    556 		**	Binary search for the file in this mixfile. If it is found, then extract the
    557 		**	appropriate information and store it in the locations provided and then return.
    558 		*/
    559 		block = (SubBlock *)bsearch(&key, ptr->HeaderBuffer, ptr->Count, sizeof(SubBlock), compfunc);
    560 		if (block != NULL) {
    561 			if (mixfile != NULL) *mixfile = ptr;
    562 			if (size != NULL) *size = block->Size;
    563 			if (realptr != NULL) *realptr = NULL;
    564 			if (offset != NULL) *offset = block->Offset;
    565 			if (realptr != NULL && ptr->Data != NULL) {
    566 				*realptr = (char *)ptr->Data + block->Offset;
    567 			}
    568 			if (ptr->Data == NULL && offset != NULL) {
    569 				*offset += ptr->DataStart;
    570 			}
    571 			return(true);
    572 		}
    573 
    574 		/*
    575 		**	Advance to next mixfile.
    576 		*/
    577 		ptr = ptr->Next();
    578 	}
    579 
    580 	/*
    581 	**	All the mixfiles have been examined but no match was found. Return with the non success flag.
    582 	*/
    583 assert(1);//BG
    584 	return(false);
    585 }
    586 
    587 
    588 
    589 // ST - 12/18/2019 11:36AM
    590 template<class T>
    591 void MixFileClass<T>::Free_All(void)
    592 {
    593 	MixFileClass<T> * ptr = List.First();
    594 	while (ptr->Is_Valid()) {
    595 		delete ptr;
    596 		ptr = List.First();
    597 	}
    598 }