| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 |
- /*
- ** 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/>.
- */
- /* $Header: /CounterStrike/MIXFILE.CPP 2 3/13/97 2:06p Steve_tall $ */
- /***********************************************************************************************
- *** 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 : Command & Conquer *
- * *
- * File Name : MIXFILE.CPP *
- * *
- * Programmer : Joe L. Bostic *
- * *
- * Start Date : August 8, 1994 *
- * *
- * Last Update : July 12, 1996 [JLB] *
- * *
- * *
- * Modified by Vic Grippi for WwXlat Tool 10/14/96 *
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * MixFileClass::Cache -- Caches the named mixfile into RAM. *
- * MixFileClass::Cache -- Loads this particular mixfile's data into RAM. *
- * MixFileClass::Finder -- Finds the mixfile object that matches the name specified. *
- * MixFileClass::Free -- Uncaches a cached mixfile. *
- * MixFileClass::MixFileClass -- Constructor for mixfile object. *
- * MixFileClass::Offset -- Searches in mixfile for matching file and returns offset if found.*
- * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file. *
- * MixFileClass::~MixFileClass -- Destructor for the mixfile object. *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "buff.h"
- #include "function.h"
- #include <direct.h>
- #include <fcntl.h>
- #include <io.h>
- #include <dos.h>
- #include <errno.h>
- #include <share.h>
- #include "mixfile.h"
- #include "cdfile.h"
- extern MFCD temp;
- //template<class T> int Compare(T const *obj1, T const *obj2) {
- // if (*obj1 < *obj2) return(-1);
- // if (*obj1 > *obj2) return(1);
- // return(0);
- //};
- /*
- ** This is the pointer to the first mixfile in the list of mixfiles registered
- ** with the mixfile system.
- */
- template<class T>
- List<MixFileClass<T> > MixFileClass<T>::List;
- /***********************************************************************************************
- * MixFileClass::Free -- Uncaches a cached mixfile. *
- * *
- * Use this routine to uncache a mixfile that has been cached. *
- * *
- * INPUT: filename -- Pointer to the filename of the mixfile that is to be uncached. *
- * *
- * OUTPUT: bool; Was the mixfile found and freed? *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 01/23/1995 JLB : Created. *
- *=============================================================================================*/
- template<class T>
- bool MixFileClass<T>::Free(char const * filename)
- {
- MixFileClass * ptr = Finder(filename);
- if (ptr) {
- ptr->Free();
- return(true);
- }
- return(false);
- }
- /***********************************************************************************************
- * MixFileClass::~MixFileClass -- Destructor for the mixfile object. *
- * *
- * This destructor will free all memory allocated by this mixfile and will remove it from *
- * the system. A mixfile removed in this fashion must be created anew in order to be *
- * subsequent used. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 08/08/1994 JLB : Created. *
- * 01/06/1995 JLB : Puts mixfile header table into EMS. *
- *=============================================================================================*/
- template<class T>
- MixFileClass<T>::~MixFileClass(void)
- {
- /*
- ** Deallocate any allocated memory.
- */
- if (Filename) {
- free((char *)Filename);
- }
- if (Data != NULL && IsAllocated) {
- delete [] Data;
- IsAllocated = false;
- }
- Data = NULL;
- if (HeaderBuffer != NULL) {
- delete [] HeaderBuffer;
- HeaderBuffer = NULL;
- }
- /*
- ** Unlink this mixfile object from the chain.
- */
- Unlink();
- }
- /***********************************************************************************************
- * MixFileClass::MixFileClass -- Constructor for mixfile object. *
- * *
- * This is the constructor for the mixfile object. It takes a filename and a memory *
- * handler object and registers the mixfile object with the system. The index block is *
- * allocated and loaded from disk by this routine. *
- * *
- * INPUT: filename -- Pointer to the filename of the mixfile object. *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 08/08/1994 JLB : Created. *
- * 07/12/1996 JLB : Handles compressed file header. *
- *=============================================================================================*/
- template<class T>
- MixFileClass<T>::MixFileClass(char const * filename, PKey const * key) :
- IsDigest(false),
- IsEncrypted(false),
- IsAllocated(false),
- Filename(0),
- Count(0),
- DataSize(0),
- DataStart(0),
- HeaderBuffer(0),
- Data(0)
- {
- /*
- ** Check to see if the file is available. If it isn't, then
- ** no further processing is needed or possible.
- */
- if (!Force_CD_Available(RequiredCD)) {
- //Prog_End();
- Emergency_Exit(EXIT_FAILURE);
- }
- T file(filename); // Working file object.
- Filename = strdup(file.File_Name());
- FileStraw fstraw(file);
- PKStraw pstraw(PKStraw::DECRYPT, CryptRandom);
- Straw * straw = &fstraw;
- if (!file.Is_Available()) return;
- /*
- ** Stuctures used to hold the various file headers.
- */
- FileHeader fileheader;
- struct {
- short First; // Always zero for extended mixfile format.
- short Second; // Bitfield of extensions to this mixfile.
- } alternate;
- /*
- ** Fetch the first bit of the file. From this bit, it is possible to detect
- ** whether this is an extended mixfile format or the plain format. An
- ** extended format may have extra options or data layout.
- */
- int got = straw->Get(&alternate, sizeof(alternate));
- /*
- ** Detect if this is an extended mixfile. If so, then see if it is encrypted
- ** and/or has a message digest attached. Otherwise, just retrieve the
- ** plain mixfile header.
- */
- if (alternate.First == 0) {
- IsDigest = ((alternate.Second & 0x01) != 0);
- IsEncrypted = ((alternate.Second & 0x02) != 0);
- if (IsEncrypted) {
- pstraw.Key(key);
- pstraw.Get_From(&fstraw);
- straw = &pstraw;
- }
- straw->Get(&fileheader, sizeof(fileheader));
- } else {
- memmove(&fileheader, &alternate, sizeof(alternate));
- straw->Get(((char*)&fileheader)+sizeof(alternate), sizeof(fileheader)-sizeof(alternate));
- }
- Count = fileheader.count;
- DataSize = fileheader.size;
- //BGMono_Printf("Mixfileclass %s DataSize: %08x \n",filename,DataSize);Get_Key();
- /*
- ** Load up the offset control array. If RAM is exhausted, then the mixfile is invalid.
- */
- HeaderBuffer = new SubBlock [Count];
- if (HeaderBuffer == NULL) return;
- straw->Get(HeaderBuffer, Count * sizeof(SubBlock));
- /*
- ** The start of the embedded mixfile data will be at the current file offset.
- ** This should be true even if the file header has been encrypted because the file
- ** header was cleverly written with just the sufficient number of padding bytes so
- ** that this condition would be true.
- */
- DataStart = file.Seek(0, SEEK_CUR) + file.BiasStart;
- // DataStart = file.Seek(0, SEEK_CUR);
- /*
- ** Attach to list of mixfiles.
- */
- List.Add_Tail(this);
- }
- /***********************************************************************************************
- * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file. *
- * *
- * This routine will return with a pointer to the specified data file if the file resides *
- * in memory. Otherwise, this routine returns NULL. Use this routine to access a resident *
- * file directly rather than going through the process of pseudo disk access. This will *
- * save both time and RAM. *
- * *
- * INPUT: filename -- Pointer to the filename of the data file to retrieve a pointer to. *
- * *
- * OUTPUT: Returns with a pointer to the data file's data. If the file is not in RAM, then *
- * NULL is returned. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 08/23/1994 JLB : Created. *
- *=============================================================================================*/
- template<class T>
- void const * MixFileClass<T>::Retrieve(char const * filename)
- {
- void * ptr = 0;
- Offset(filename, &ptr);
- return(ptr);
- };
- /***********************************************************************************************
- * MixFileClass::Finder -- Finds the mixfile object that matches the name specified. *
- * *
- * This routine will scan through all registered mixfiles and return with a pointer to *
- * the matching mixfile. If no mixfile could be found that matches the name specified, *
- * then NULL is returned. *
- * *
- * INPUT: filename -- Pointer to the filename to search for. *
- * *
- * OUTPUT: Returns with a pointer to the matching mixfile -- if found. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 08/08/1994 JLB : Created. *
- * 06/08/1996 JLB : Only compares filename and extension. *
- *=============================================================================================*/
- template<class T>
- MixFileClass<T> * MixFileClass<T>::Finder(char const * filename)
- {
- MixFileClass<T> * ptr = List.First();
- while (ptr->Is_Valid()) {
- char path[_MAX_PATH];
- char name[_MAX_FNAME];
- char ext[_MAX_EXT];
- /*
- ** Strip the drive and path (if present) off of the filename
- ** in the mixfile list. This enables a simple comparison to the
- ** filename specified. The filename specified won't have a path attached and
- ** the full pathname in the mixfile list WILL have a path attached. Hence, this
- ** stripping of the path is necessary.
- */
- _splitpath(ptr->Filename, NULL, NULL, name, ext);
- _makepath(path, NULL, NULL, name, ext);
- if (stricmp(path, filename) == 0) {
- return(ptr);
- }
- ptr = ptr->Next();
- }
- return(0);
- }
- /***********************************************************************************************
- * MixFileClass::Cache -- Caches the named mixfile into RAM. *
- * *
- * This routine will cache the mixfile, specified by name, into RAM. *
- * *
- * INPUT: filename -- The name of the mixfile that should be cached. *
- * *
- * OUTPUT: bool; Was the cache successful? *
- * *
- * WARNINGS: This routine could go to disk for a very long time. *
- * *
- * HISTORY: *
- * 08/08/1994 JLB : Created. *
- *=============================================================================================*/
- template<class T>
- bool MixFileClass<T>::Cache(char const * filename, Buffer const * buffer)
- {
- MixFileClass<T> * mixer = Finder(filename);
- if (mixer != NULL) {
- return(mixer->Cache(buffer));
- }
- return(false);
- }
- /***********************************************************************************************
- * MixFileClass::Cache -- Loads this particular mixfile's data into RAM. *
- * *
- * This load the mixfile data into ram for this mixfile object. This is the counterpart *
- * to the Free() function. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: bool; Was the file load successful? It could fail if there wasn't enough room *
- * to allocate the raw data block. *
- * *
- * WARNINGS: This routine goes to disk for a potentially very long time. *
- * *
- * HISTORY: *
- * 08/08/1994 JLB : Created. *
- * 07/12/1996 JLB : Handles attached message digest. *
- *=============================================================================================*/
- template<class T>
- bool MixFileClass<T>::Cache(Buffer const * buffer)
- {
- /*
- ** If the mixfile is already cached, then no action needs to be performed.
- */
- if (Data != NULL) return(true);
- /*
- ** If a buffer was supplied (and it is big enough), then use it as the data block
- ** pointer. Otherwise, the data block must be allocated.
- */
- if (buffer != NULL) {
- if (buffer->Get_Size() == 0 || buffer->Get_Size() >= DataSize) {
- Data = buffer->Get_Buffer();
- }
- } else {
- Data = new char [DataSize];
- IsAllocated = true;
- }
- /*
- ** If there is a data buffer to fill, then fill it now.
- */
- if (Data != NULL) {
- T file(Filename);
- FileStraw fstraw(file);
- Straw * straw = &fstraw;
- /*
- ** If a message digest is attached, then link a SHA straw segment to the data
- ** stream so that the actual SHA can be compared with the attached one.
- */
- SHAStraw sha;
- if (IsDigest) {
- sha.Get_From(fstraw);
- straw = &sha;
- }
- /*
- ** Bias the file to the actual start of the data. This is necessary because the
- ** real data starts some distance (not so easily determined) from the beginning of
- ** the real file.
- */
- file.Open(READ);
- file.Bias(0);
- file.Bias(DataStart);
- /*
- ** Fetch the whole mixfile data in one step. If the number of bytes retrieved
- ** does not equal that requested, then this indicates a serious error.
- */
- long actual = straw->Get(Data, DataSize);
- if (actual != DataSize) {
- delete [] Data;
- Data = NULL;
- file.Error(EIO);
- return(false);
- }
- /*
- ** If there is a digest attached to this mixfile, then read it in and
- ** compare it to the generated digest. If they don't match, then
- ** return with the "failure to cache" error code.
- */
- if (IsDigest) {
- char digest1[20];
- char digest2[20];
- sha.Result(digest2);
- fstraw.Get(digest1, sizeof(digest1));
- if (memcmp(digest1, digest2, sizeof(digest1)) != 0) {
- delete [] Data;
- Data = NULL;
- return(false);
- }
- }
- return(true);
- }
- IsAllocated = false;
- return(false);
- }
- /***********************************************************************************************
- * MixFileClass::Free -- Frees the allocated raw data block (not the index block). *
- * *
- * This routine will free the (presumably large) raw data block, but leave the index *
- * block intact. By using this in conjunction with the Cache() function, one can maintain *
- * tight control of memory usage. If the index block is desired to be freed, then the *
- * mixfile object must be deleted. *
- * *
- * INPUT: none *
- * *
- * OUTPUT: none *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 08/08/1994 JLB : Created. *
- *=============================================================================================*/
- template<class T>
- void MixFileClass<T>::Free(void)
- {
- if (Data != NULL && IsAllocated) {
- delete [] Data;
- }
- Data = NULL;
- IsAllocated = false;
- }
- int compfunc(void const * ptr1, void const * ptr2)
- {
- if (*(long const *)ptr1 < *(long const *)ptr2) return(-1);
- if (*(long const *)ptr1 > *(long const *)ptr2) return(1);
- return(0);
- }
- /***********************************************************************************************
- * MixFileClass::Offset -- Determines the offset of the requested file from the mixfile system.*
- * *
- * This routine will take the filename specified and search through the mixfile system *
- * looking for it. If the file was found, then the mixfile it was found in, the offset *
- * from the start of the mixfile, and the size of the embedded file will be returned. *
- * Using this method it is possible for the CCFileClass system to process it as a normal *
- * file. *
- * *
- * INPUT: filename -- The filename to search for. *
- * *
- * realptr -- Stores a pointer to the start of the file in memory here. If the *
- * file is not in memory, then NULL is stored here. *
- * *
- * mixfile -- The pointer to the corresponding mixfile is placed here. If no *
- * mixfile was found that contains the file, then NULL is stored here. *
- * *
- * offset -- The starting offset from the beginning of the parent mixfile is *
- * stored here. *
- * *
- * size -- The size of the embedded file is stored here. *
- * *
- * OUTPUT: bool; Was the file found? The file may or may not be resident, but it does exist *
- * and can be opened. *
- * *
- * WARNINGS: none *
- * *
- * HISTORY: *
- * 10/17/1994 JLB : Created. *
- *=============================================================================================*/
- template<class T>
- bool MixFileClass<T>::Offset(char const * filename, void ** realptr, MixFileClass ** mixfile, long * offset, long * size) const
- {
- MixFileClass<T> * ptr;
- if (filename == NULL) {
- assert(filename != NULL);//BG
- return(false);
- }
- /*
- ** Create the key block that will be used to binary search for the file.
- */
- long crc = Calculate_CRC(strupr((char *)filename), strlen(filename));
- SubBlock key;
- key.CRC = crc;
- /*
- ** Sweep through all registered mixfiles, trying to find the file in question.
- */
- ptr = List.First();
- while (ptr->Is_Valid()) {
- SubBlock * block;
- /*
- ** Binary search for the file in this mixfile. If it is found, then extract the
- ** appropriate information and store it in the locations provided and then return.
- */
- block = (SubBlock *)bsearch(&key, ptr->HeaderBuffer, ptr->Count, sizeof(SubBlock), compfunc);
- if (block != NULL) {
- if (mixfile != NULL) *mixfile = ptr;
- if (size != NULL) *size = block->Size;
- if (realptr != NULL) *realptr = NULL;
- if (offset != NULL) *offset = block->Offset;
- if (realptr != NULL && ptr->Data != NULL) {
- *realptr = (char *)ptr->Data + block->Offset;
- }
- if (ptr->Data == NULL && offset != NULL) {
- *offset += ptr->DataStart;
- }
- return(true);
- }
- /*
- ** Advance to next mixfile.
- */
- ptr = ptr->Next();
- }
- /*
- ** All the mixfiles have been examined but no match was found. Return with the non success flag.
- */
- assert(1);//BG
- return(false);
- }
|