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 }