CnC_Remastered_Collection

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

MIXFILE.CPP (27674B)


      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:   F:\projects\c&c\vcs\code\mixfile.cpv   2.18   16 Oct 1995 16:48:46   JOE_BOSTIC  $ */
     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 : January 23, 1995 [JLB]                                       *
     30  *                                                                                             *
     31  *---------------------------------------------------------------------------------------------*
     32  * Functions:                                                                                  *
     33  *   MixFileClass::Cache -- Caches the named mixfile into RAM.                                 *
     34  *   MixFileClass::Cache -- Loads this particular mixfile's data into RAM.                     *
     35  *   MixFileClass::Finder -- Finds the mixfile object that matches the name specified.         *
     36  *   MixFileClass::Free -- Frees the allocated raw data block (not the index block).           *
     37  *   MixFileClass::MixFileClass -- Constructor for mixfile object.                             *
     38  *   MixFileClass::Offset -- Determines if the file is in a mixfile and where its sublocation is.*
     39  *   MixFileClass::Offset -- Searches in mixfile for matching file and returns offset if found.*
     40  *   MixFileClass::Retrieve -- Retrieves a pointer to the specified data file.                 *
     41  *   MixFileClass::~MixFileClass -- Destructor for the mixfile object.                         *
     42  *   MixFileClass::Offset -- Determines the offset of the requested file from the mixfile system.*
     43  *   MixFileClass::Free -- Uncaches a cached mixfile.                                          * 
     44  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
     45 
     46 
     47 #include	"function.h"
     48 #include	<direct.h>
     49 #include	<fcntl.h>
     50 #include	<io.h>
     51 #include	<dos.h>
     52 #include	<errno.h>
     53 #include	<share.h>
     54 #include	"mixfile.h"
     55 
     56 
     57 template<class T> int Compare(T const *obj1, T const *obj2) {
     58 	if (*obj1 < *obj2) return(-1);
     59 	if (*obj1 > *obj2) return(1);
     60 	return(0);
     61 };
     62 
     63 
     64 /*
     65 **	This is the pointer to the first mixfile in the list of mixfiles registered
     66 **	with the mixfile system.
     67 */
     68 MixFileClass * MixFileClass::First = 0;
     69 
     70 
     71 /*********************************************************************************************** 
     72  * MixFileClass::Free -- Uncaches a cached mixfile.                                            * 
     73  *                                                                                             * 
     74  *    Use this routine to uncache a mixfile that has been cached.                              * 
     75  *                                                                                             * 
     76  * INPUT:   filename -- Pointer to the filename of the mixfile that is to be uncached.         * 
     77  *                                                                                             * 
     78  * OUTPUT:  bool; Was the mixfile found and freed?                                             * 
     79  *                                                                                             * 
     80  * WARNINGS:   none                                                                            * 
     81  *                                                                                             * 
     82  * HISTORY:                                                                                    * 
     83  *   01/23/1995 JLB : Created.                                                                 * 
     84  *=============================================================================================*/
     85 bool MixFileClass::Free(char const *filename)
     86 {
     87 	MixFileClass * ptr = Finder(filename);
     88 
     89 	if (ptr) {
     90 		ptr->Free();
     91 		return(true);
     92 	}
     93 	return(false);
     94 }
     95 
     96 
     97 //#ifndef NOMEMCHECK
     98 void MixFileClass::Free_All(void)
     99 {
    100 	while (First) {
    101 		delete First;
    102 	}
    103 }
    104 //#endif
    105 
    106 
    107 /***********************************************************************************************
    108  * MixFileClass::~MixFileClass -- Destructor for the mixfile object.                           *
    109  *                                                                                             *
    110  *    This destructor will free all memory allocated by this mixfile and will remove it from   *
    111  *    the system. A mixfile removed in this fashion must be created anew in order to be        *
    112  *    subsequent used.                                                                         *
    113  *                                                                                             *
    114  * INPUT:   none                                                                               *
    115  *                                                                                             *
    116  * OUTPUT:  none                                                                               *
    117  *                                                                                             *
    118  * WARNINGS:   none                                                                            *
    119  *                                                                                             *
    120  * HISTORY:                                                                                    *
    121  *   08/08/1994 JLB : Created.                                                                 *
    122  *   01/06/1995 JLB : Puts mixfile header table into EMS.                                      * 
    123  *=============================================================================================*/
    124 MixFileClass::~MixFileClass(void)
    125 {
    126 
    127 	/*
    128 	**	Deallocate any allocated memory.
    129 	*/
    130 	if (Filename) {
    131 		free((char *)Filename);
    132 	}
    133 	if (Data) {
    134 		delete [] Data;
    135 	}
    136 	if (Buffer) {
    137 		delete [] Buffer;
    138 	}
    139 
    140 	/*
    141 	**	Unlink this mixfile object from the chain.
    142 	*/
    143 	if (this == First) {
    144 		First = (MixFileClass *)Get_Next();
    145 	} else {
    146 		Remove();
    147 	}
    148 	
    149 	// Can't do this here since the link class destructor hasn't been called yet, so clearing out the data will mess up the
    150 	// linked list. How did this work before? Did the watcom compiler call the destructors in a different order?
    151 	// ST - 1/3/2019 5:34PM
    152 	//Zap();
    153 }
    154 
    155 
    156 /***********************************************************************************************
    157  * MixFileClass::MixFileClass -- Constructor for mixfile object.                               *
    158  *                                                                                             *
    159  *    This is the constructor for the mixfile object. It takes a filename and a memory         *
    160  *    handler object and registers the mixfile object with the system. The index block is      *
    161  *    allocated and loaded from disk by this routine.                                          *
    162  *                                                                                             *
    163  * INPUT:   filename -- Pointer to the filename of the mixfile object.                         *
    164  *                                                                                             *
    165  * OUTPUT:  none                                                                               *
    166  *                                                                                             *
    167  * WARNINGS:   none                                                                            *
    168  *                                                                                             *
    169  * HISTORY:                                                                                    *
    170  *   08/08/1994 JLB : Created.                                                                 *
    171  *=============================================================================================*/
    172 MixFileClass::MixFileClass(char const *filename)
    173 {
    174 	CCFileClass file;		// Working file object.
    175 
    176 	/*
    177 	**	Load in the control block. It always remains resident.
    178 	*/
    179 	Data = 0;
    180 	Count = 0;
    181 	Buffer = 0;
    182 	file.Set_Name(filename);
    183 	Filename = strdup(file.File_Name());
    184 
    185 	if (!Force_CD_Available(RequiredCD)) {
    186 		Prog_End("MixFileClass::MixFileClass CD not found", true);
    187 		if (!RunningAsDLL) {
    188 			exit(EXIT_FAILURE);
    189 		}
    190 		return;
    191 	}
    192 
    193 	if (file.Is_Available(true)) {
    194 		FileHeader fileheader;
    195 
    196 		file.Open();
    197 		file.Read(&fileheader, sizeof(fileheader));
    198 		Count = fileheader.count;
    199 		DataSize = fileheader.size;
    200 
    201 		/*
    202 		**	Load up the offset control array. This could be located in
    203 		**	EMS if possible.
    204 		*/
    205 		Buffer = new SubBlock [Count];
    206 		if (Buffer) {
    207 			file.Read(Buffer, Count * sizeof(SubBlock));
    208 		}
    209 		file.Close();
    210 	} else {
    211 //		delete this;
    212 		return;
    213 	}
    214 
    215 	/*
    216 	**	Raw data block starts uncached.
    217 	*/
    218 	Data = 0;
    219 
    220 	/*
    221 	**	Attach to list of mixfiles.
    222 	*/
    223 	Zap();
    224 	if (!First) {
    225 		First = this;
    226 	} else {
    227 		Add_Tail(*First);
    228 	}
    229 }
    230 
    231 
    232 /***********************************************************************************************
    233  * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file.                   *
    234  *                                                                                             *
    235  *    This routine will return with a pointer to the specified data file if the file resides   *
    236  *    in memory. Otherwise, this routine returns NULL. Use this routine to access a resident   *
    237  *    file directly rather than going through the process of pseudo disk access. This will     *
    238  *    save both time and RAM.                                                                  *
    239  *                                                                                             *
    240  * INPUT:   filename -- Pointer to the filename of the data file to retrieve a pointer to.     *
    241  *                                                                                             *
    242  * OUTPUT:  Returns with a pointer to the data file's data. If the file is not in RAM, then    *
    243  *          NULL is returned.                                                                  *
    244  *                                                                                             *
    245  * WARNINGS:   none                                                                            *
    246  *                                                                                             *
    247  * HISTORY:                                                                                    *
    248  *   08/23/1994 JLB : Created.                                                                 *
    249  *=============================================================================================*/
    250 void const * MixFileClass::Retrieve(char const *filename) {
    251 	void *ptr = 0;
    252 	Offset(filename, &ptr);
    253 //	if (!ptr) {
    254 //		errno = ENOENT;
    255 //		File_Fatal(filename);
    256 //	}
    257 	return(ptr); 
    258 };
    259 
    260 
    261 /***********************************************************************************************
    262  * MixFileClass::Finder -- Finds the mixfile object that matches the name specified.           *
    263  *                                                                                             *
    264  *    This routine will scan through all registered mixfiles and return with a pointer to      *
    265  *    the matching mixfile. If no mixfile could be found that matches the name specified,      *
    266  *    then NULL is returned.                                                                   *
    267  *                                                                                             *
    268  * INPUT:   filename -- Pointer to the filename to search for.                                 *
    269  *                                                                                             *
    270  * OUTPUT:  Returns with a pointer to the matching mixfile -- if found.                        *
    271  *                                                                                             *
    272  * WARNINGS:   none                                                                            *
    273  *                                                                                             *
    274  * HISTORY:                                                                                    *
    275  *   08/08/1994 JLB : Created.                                                                 *
    276  *=============================================================================================*/
    277 MixFileClass * MixFileClass::Finder(char const *filename)
    278 {
    279 	MixFileClass * ptr;
    280 
    281 	ptr = First;
    282 	while (ptr) {
    283 		if (stricmp(&ptr->Filename[strlen(ptr->Filename)-strlen(filename)], filename) == 0) {
    284 			return(ptr);
    285 		}
    286 		ptr = (MixFileClass *)ptr->Get_Next();
    287 	}
    288 	return(0);
    289 }
    290 
    291 
    292 /***********************************************************************************************
    293  * MixFileClass::Cache -- Caches the named mixfile into RAM.                                   *
    294  *                                                                                             *
    295  *    This routine will cache the mixfile, specified by name, into RAM.                        *
    296  *                                                                                             *
    297  * INPUT:   filename -- The name of the mixfile that should be cached.                         *
    298  *                                                                                             *
    299  * OUTPUT:  bool; Was the cache successful?                                                    *
    300  *                                                                                             *
    301  * WARNINGS:   This routine could go to disk for a very long time.                             *
    302  *                                                                                             *
    303  * HISTORY:                                                                                    *
    304  *   08/08/1994 JLB : Created.                                                                 *
    305  *=============================================================================================*/
    306 bool MixFileClass::Cache(char const *filename)
    307 {
    308 	MixFileClass * mixer = Finder(filename);
    309 
    310 	if (mixer) {
    311 		return(mixer->Cache());
    312 	}
    313 	return(false);
    314 }
    315 
    316 
    317 /***********************************************************************************************
    318  * MixFileClass::Cache -- Loads this particular mixfile's data into RAM.                       *
    319  *                                                                                             *
    320  *    This load the mixfile data into ram for this mixfile object. This is the counterpart     *
    321  *    to the Free() function.                                                                  *
    322  *                                                                                             *
    323  * INPUT:   none                                                                               *
    324  *                                                                                             *
    325  * OUTPUT:  bool; Was the file load successful?  It could fail if there wasn't enough room     *
    326  *                to allocate the raw data block.                                              *
    327  *                                                                                             *
    328  * WARNINGS:   This routine goes to disk for a potentially very long time.                     *
    329  *                                                                                             *
    330  * HISTORY:                                                                                    *
    331  *   08/08/1994 JLB : Created.                                                                 *
    332  *=============================================================================================*/
    333 bool MixFileClass::Cache(void)
    334 {
    335 	if (Data) return(true);
    336 
    337 	Data = new char [DataSize];
    338 	if (Data) {
    339 		CCFileClass file(Filename);
    340 
    341 		file.Open();
    342 		file.Seek(sizeof(SubBlock) * Count + sizeof(FileHeader));
    343 		long actual = file.Read(Data, DataSize);
    344 		if (actual != DataSize) {
    345 #ifdef GERMAN
    346 			Fatal("Korrupte .MIX-Datei \"%s\". Beim Versuch, %ld zu lesen, nur %ld gefunden.", Filename, DataSize, actual);
    347 #else
    348 #ifdef FRENCH
    349 			Fatal("Fichier .MIX corrumpu \"%s\". Essai de lecture de %ld, mais %ld obtenu.", Filename, DataSize, actual);
    350 #else
    351 			Fatal("Corrupt .MIX file \"%s\". Tried to read %ld, but got %ld.", Filename, DataSize, actual);
    352 #endif
    353 #endif
    354 		}
    355 		file.Close();
    356 		return(true);
    357 	}
    358 #ifdef GERMAN
    359 	Fatal("Kann Datei \"%s\" nicht laden.", Filename);
    360 #else
    361 #ifdef FRENCH
    362 	Fatal("Impossible de charger \"%s\".", Filename);
    363 #else
    364 	Fatal("Unable to load \"%s\".", Filename);
    365 #endif
    366 #endif
    367 	return(false);
    368 }
    369 
    370 
    371 /***********************************************************************************************
    372  * MixFileClass::Free -- Frees the allocated raw data block (not the index block).             *
    373  *                                                                                             *
    374  *    This routine will free the (presumably large) raw data block, but leave the index        *
    375  *    block intact. By using this in conjunction with the Cache() function, one can maintain   *
    376  *    tight control of memory usage. If the index block is desired to be freed, then the       *
    377  *    mixfile object must be deleted.                                                          *
    378  *                                                                                             *
    379  * INPUT:   none                                                                               *
    380  *                                                                                             *
    381  * OUTPUT:  none                                                                               *
    382  *                                                                                             *
    383  * WARNINGS:   none                                                                            *
    384  *                                                                                             *
    385  * HISTORY:                                                                                    *
    386  *   08/08/1994 JLB : Created.                                                                 *
    387  *=============================================================================================*/
    388 void MixFileClass::Free(void)
    389 {
    390 	if (Data) {
    391 		delete [] Data;
    392 		Data = 0;
    393 	}
    394 }
    395 
    396 
    397 /***********************************************************************************************
    398  * MixFileClass::Offset -- Determines if the file is in a mixfile and where its sublocation is.*
    399  *                                                                                             *
    400  *    This routine will scan through all registers mixfiles in an attempt to locate the file   *
    401  *    in question. Whether the file is located in RAM or on disk does not restrict this        *
    402  *    search operation.                                                                        *
    403  *                                                                                             *
    404  * INPUT:   filename    -- The filename of the file to search within the mixfile system.       *
    405  *                                                                                             *
    406  *          realptr     -- Pointer to pointer that will be filled with the RAM pointer to      *
    407  *                         the file if it happens to be in RAM. NULL if otherwise.             *
    408  *                                                                                             *
    409  *          mixfile     -- Pointer to the mixfile object that contains the file in question.   *
    410  *                                                                                             *
    411  *          offset      -- The offset to the start of the data of the file. If the file is     *
    412  *                         on disk then this is the offset from the start of the file, if      *
    413  *                         it is located in RAM, then it is the offset from the start of the   *
    414  *                         raw data block.                                                     *
    415  *                                                                                             *
    416  *          size        -- Pointer to where the size of the file will be stored.               *
    417  *                                                                                             *
    418  * OUTPUT:  bool; Was the file found in one of the mixfiles?  The value stored in "realptr"    *
    419  *                will be NULL if it is on disk, otherwise it is in RAM.                       *
    420  *                                                                                             *
    421  * WARNINGS:   none                                                                            *
    422  *                                                                                             *
    423  * HISTORY:                                                                                    *
    424  *   08/08/1994 JLB : Created.                                                                 *
    425  *=============================================================================================*/
    426 //int _USERENTRY Compare(MixFileClass::SubBlock const *, MixFileClass::SubBlock const *);
    427 
    428 // int _USERENTRY compfunc(void const *ptr1, void const *ptr2) 
    429 int compfunc(void const *ptr1, void const *ptr2) 
    430 {
    431 //	long diff = *(long const *)ptr1 - *(long const *)ptr2;
    432 //	return FP_SEG(diff);
    433 
    434 	if (*(long const *)ptr1 < *(long const *)ptr2) return(-1);
    435 	if (*(long const *)ptr1 > *(long const *)ptr2) return(1);
    436 	return(0);
    437 }
    438 
    439 
    440 /***********************************************************************************************
    441  * MixFileClass::Offset -- Determines the offset of the requested file from the mixfile system.*
    442  *                                                                                             *
    443  *    This routine will take the falename specified and search through the mixfile system      *
    444  *    looking for it. If the file was found, then the mixfile it was found int, the offset     *
    445  *    from the start of the mixfile, and the size of the embedded file will be returned.       *
    446  *    Using this method it is possible for the CCFileClass system to process it as a normal    *
    447  *    file.                                                                                    *
    448  *                                                                                             *
    449  * INPUT:   filename    -- The filename to search for.                                         *
    450  *                                                                                             *
    451  *          realptr     -- Stores a pointer to the start of the file in memory here. If the    *
    452  *                         file is not in memory, then NULL is stored here.                    *
    453  *                                                                                             *
    454  *          mixfile     -- The pointer to the corresponding mixfile is placed here. If no      *
    455  *                         mixfile was found that contains the file, then NULL is stored here. *
    456  *                                                                                             *
    457  *          offset      -- The starting offset from the beginning of the parent mixfile is     *
    458  *                         stored here.                                                        *
    459  *                                                                                             *
    460  *          size        -- The size of the embedded file is stored here.                       *
    461  *                                                                                             *
    462  * OUTPUT:  bool; Was the file found? The file may or may not be resident, but it does exist   *
    463  *                and can be opened.                                                           *
    464  *                                                                                             *
    465  * WARNINGS:   none                                                                            *
    466  *                                                                                             *
    467  * HISTORY:                                                                                    *
    468  *   10/17/1994 JLB : Created.                                                                 *
    469  *=============================================================================================*/
    470 bool MixFileClass::Offset(char const *filename, void ** realptr, MixFileClass ** mixfile, long * offset, long * size)
    471 {
    472 	MixFileClass * ptr;
    473 
    474 	if (!filename) return(false);
    475 
    476 	/*
    477 	**	Create the key block that will be used to binary search for the file.
    478 	*/
    479 	char file_name_copy[_MAX_PATH];
    480 	strcpy(file_name_copy, filename);
    481 	strupr(file_name_copy);
    482 	long crc = Calculate_CRC(file_name_copy, strlen(file_name_copy));
    483 	SubBlock key;
    484 	key.CRC = crc;
    485 
    486 	/*
    487 	**	Sweep through all registered mixfiles, trying to find the file in question.
    488 	*/
    489 	ptr = First;
    490 	while (ptr) {
    491 		SubBlock * block;
    492 
    493 		/*
    494 		**	Binary search for the file in this mixfile. If it is found, then extract the
    495 		**	appropriate information and store it in the locations provided and then return.
    496 		*/
    497 		block = (SubBlock *)bsearch(&key, ptr->Buffer, ptr->Count, sizeof(SubBlock), compfunc);
    498 		if (block) {
    499 			if (mixfile) *mixfile = ptr;
    500 			if (size) *size = block->Size;
    501 			if (realptr) *realptr = 0;
    502 			if (offset) *offset = block->Offset;
    503 			if (realptr && ptr->Data) {
    504 				*realptr = Add_Long_To_Pointer(ptr->Data, block->Offset);
    505 			}
    506 			if (!ptr->Data && offset) {
    507 				*offset += sizeof(SubBlock) * ptr->Count + sizeof(FileHeader);
    508 			}
    509 			return(true);
    510 		}
    511 
    512 		/*
    513 		**	Advance to next mixfile.
    514 		*/
    515 		ptr = (MixFileClass *)ptr->Get_Next();
    516 	}
    517 
    518 	/*
    519 	**	All the mixfiles have been examined but no match was found. Return with the non success flag.
    520 	*/
    521 	return(false);
    522 }