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 }