CCFILE.CPP (33090B)
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/CCFILE.CPP 2 3/13/97 2:05p 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 : CCFILE.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : August 8, 1994 * 28 * * 29 * Last Update : August 5, 1996 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * CCFileClass::CCFileClass -- Default constructor for file object. * 34 * CCFileClass::CCFileClass -- Filename based constructor for C&C file. * 35 * CCFileClass::Close -- Closes the file. * 36 * CCFileClass::Error -- Handles displaying a file error message. * 37 * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. * 38 * CCFileClass::Is_Open -- Determines if the file is open. * 39 * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. * 40 * CCFileClass::Read -- Reads data from the file. * 41 * CCFileClass::Seek -- Moves the current file pointer in the file. * 42 * CCFileClass::Size -- Determines the size of the file. * 43 * CCFileClass::Write -- Writes data to the file (non mixfile files only). * 44 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 45 46 47 #include "function.h" 48 #include <errno.h> 49 #include "ccfile.h" 50 51 52 /*********************************************************************************************** 53 * CCFileClass::CCFileClass -- Filename based constructor for C&C file. * 54 * * 55 * Use this constructor for a file when the filename is known at construction time. * 56 * * 57 * INPUT: filename -- Pointer to the filename to use for this file object. * 58 * * 59 * OUTPUT: none * 60 * * 61 * WARNINGS: The filename pointer is presumed to be inviolate throughout the duration of * 62 * the file object. If this is not guaranteed, then use the default constructor * 63 * and then set the name manually. * 64 * * 65 * HISTORY: * 66 * 03/20/1995 JLB : Created. * 67 *=============================================================================================*/ 68 CCFileClass::CCFileClass(char const * filename) : 69 Position(0) 70 { 71 CCFileClass::Set_Name(filename); 72 } 73 74 75 /*********************************************************************************************** 76 * CCFileClass::CCFileClass -- Default constructor for file object. * 77 * * 78 * This is the default constructor for a C&C file object. * 79 * * 80 * INPUT: none * 81 * * 82 * OUTPUT: none * 83 * * 84 * WARNINGS: none * 85 * * 86 * HISTORY: * 87 * 03/20/1995 JLB : Created. * 88 *=============================================================================================*/ 89 CCFileClass::CCFileClass(void) : 90 Position(0) 91 { 92 } 93 94 95 /*********************************************************************************************** 96 * CCFileClass::Error -- Handles displaying a file error message. * 97 * * 98 * Display an error message as indicated. If it is allowed to retry, then pressing a key * 99 * will return from this function. Otherwise, it will exit the program with "exit()". * 100 * * 101 * INPUT: error -- The error number (same as the DOSERR.H error numbers). * 102 * * 103 * canretry -- Can this routine exit normally so that retrying can occur? If this is * 104 * false, then the program WILL exit in this routine. * 105 * * 106 * filename -- Optional filename to report with this error. If no filename is * 107 * supplied, then no filename is listed in the error message. * 108 * * 109 * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is * 110 * false or the player pressed ESC. * 111 * * 112 * WARNINGS: This routine may not return at all. It handles being in text mode as well as * 113 * if in a graphic mode. * 114 * * 115 * HISTORY: * 116 * 10/17/1994 JLB : Created. * 117 *=============================================================================================*/ 118 void CCFileClass::Error(int , int , char const * ) 119 { 120 if (!Force_CD_Available(RequiredCD)) { 121 Prog_End("CCFileClass::Error CD not found", true); 122 if (!RunningAsDLL) { //PG 123 Emergency_Exit(EXIT_FAILURE); 124 } 125 } 126 } 127 128 129 /*********************************************************************************************** 130 * CCFileClass::Write -- Writes data to the file (non mixfile files only). * 131 * * 132 * This routine will write data to the file, but NOT to a file that is part of a mixfile. * 133 * * 134 * INPUT: buffer -- Pointer to the buffer that holds the data to be written. * 135 * * 136 * size -- The number of bytes to write. * 137 * * 138 * OUTPUT: Returns the number of bytes actually written. * 139 * * 140 * WARNINGS: none * 141 * * 142 * HISTORY: * 143 * 08/08/1994 JLB : Created. * 144 *=============================================================================================*/ 145 long CCFileClass::Write(void const * buffer, long size) 146 { 147 /* 148 ** If this is part of a mixfile, then writing is not allowed. Error out with a fatal 149 ** message. 150 */ 151 if (Is_Resident()) { 152 Error(EACCES, false, File_Name()); 153 } 154 155 return(CDFileClass::Write(buffer, size)); 156 } 157 158 159 /*********************************************************************************************** 160 * CCFileClass::Read -- Reads data from the file. * 161 * * 162 * This routine determines if the file is part of the mixfile system. If it is, then * 163 * the file is copied from RAM if it is located there. Otherwise it is read from disk * 164 * according to the correct position of the file within the parent mixfile. * 165 * * 166 * INPUT: buffer -- Pointer to the buffer to place the read data. * 167 * * 168 * size -- The number of bytes to read. * 169 * * 170 * OUTPUT: Returns the actual number of bytes read (this could be less than requested). * 171 * * 172 * WARNINGS: none * 173 * * 174 * HISTORY: * 175 * 08/08/1994 JLB : Created. * 176 *=============================================================================================*/ 177 long CCFileClass::Read(void * buffer, long size) 178 { 179 bool opened = false; 180 181 /* 182 ** If the file isn't currently open, then open it. 183 */ 184 if (!Is_Open()) { 185 if (Open()) { 186 opened = true; 187 } 188 } 189 190 /* 191 ** If the file is part of a loaded mixfile, then a mere copy is 192 ** all that is required for the read. 193 */ 194 if (Is_Resident()) { 195 long maximum = Data.Get_Size() - Position; 196 197 size = maximum < size ? maximum : size; 198 // size = MIN(maximum, size); 199 if (size) { 200 memmove(buffer, (char *)Data + Position, size); 201 // Mem_Copy((char *)Pointer + Position, buffer, size); 202 Position += size; 203 } 204 if (opened) Close(); 205 return(size); 206 } 207 208 long s = CDFileClass::Read(buffer, size); 209 210 /* 211 ** If the file was opened by this routine, then close it at this time. 212 */ 213 if (opened) Close(); 214 215 /* 216 ** Return with the number of bytes read. 217 */ 218 return(s); 219 } 220 221 222 /*********************************************************************************************** 223 * CCFileClass::Seek -- Moves the current file pointer in the file. * 224 * * 225 * This routine will change the current file pointer to the position specified. It follows * 226 * the same rules the a normal Seek() does, but if the file is part of the mixfile system, * 227 * then only the position value needs to be updated. * 228 * * 229 * INPUT: pos -- The position to move the file to relative to the position indicated * 230 * by the "dir" parameter. * 231 * * 232 * dir -- The direction to affect the position change against. This can be * 233 * either SEEK_CUR, SEEK_END, or SEEK_SET. * 234 * * 235 * OUTPUT: Returns with the position of the new location. * 236 * * 237 * WARNINGS: none * 238 * * 239 * HISTORY: * 240 * 08/08/1994 JLB : Created. * 241 *=============================================================================================*/ 242 long CCFileClass::Seek(long pos, int dir) 243 { 244 /* 245 ** When the file is resident, a mere adjustment of the virtual file position is 246 ** all that is required of a seek. 247 */ 248 if (Is_Resident()) { 249 switch (dir) { 250 case SEEK_END: 251 Position = Data.Get_Size(); 252 break; 253 254 case SEEK_SET: 255 Position = 0; 256 break; 257 258 case SEEK_CUR: 259 default: 260 break; 261 } 262 Position += pos; 263 Position = Position < 0 ? 0 : Position; 264 Position = Position > Data.Get_Size() ? Data.Get_Size() : Position; 265 // Position = Bound(Position+pos, 0L, Length); 266 return(Position); 267 } 268 return(CDFileClass::Seek(pos, dir)); 269 } 270 271 272 /*********************************************************************************************** 273 * CCFileClass::Size -- Determines the size of the file. * 274 * * 275 * If the file is part of the mixfile system, then the size of the file is already * 276 * determined and available. Otherwise, go to the low level system to find the file * 277 * size. * 278 * * 279 * INPUT: none * 280 * * 281 * OUTPUT: Returns with the size of the file in bytes. * 282 * * 283 * WARNINGS: none * 284 * * 285 * HISTORY: * 286 * 08/08/1994 JLB : Created. * 287 * 08/05/1996 JLB : Handles returning size of embedded file. * 288 *=============================================================================================*/ 289 long CCFileClass::Size(void) 290 { 291 /* 292 ** If the file is resident, the the size is already known. Just return the size in this 293 ** case. 294 */ 295 if (Is_Resident()) return(Data.Get_Size()); 296 297 /* 298 ** If the file is not available as a stand alone file, then search for it in the 299 ** mixfiles in order to get its size. 300 */ 301 if (!CDFileClass::Is_Available()) { 302 long length = 0; 303 MFCD::Offset(File_Name(), NULL, NULL, NULL, &length); 304 return(length); 305 } 306 307 return(CDFileClass::Size()); 308 } 309 310 311 /*********************************************************************************************** 312 * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. * 313 * * 314 * This routine will examine the mixfile system looking for the file. If the file could * 315 * not be found there, then the disk is examined directly. * 316 * * 317 * INPUT: none * 318 * * 319 * OUTPUT: bool; Is the file available for opening? * 320 * * 321 * WARNINGS: none * 322 * * 323 * HISTORY: * 324 * 08/08/1994 JLB : Created. * 325 *=============================================================================================*/ 326 int CCFileClass::Is_Available(int ) 327 { 328 /* 329 ** A file that is open is presumed available. 330 */ 331 if (Is_Open()) return(true); 332 333 /* 334 ** A file that is part of a mixfile is also presumed available. 335 */ 336 if (MFCD::Offset(File_Name())) { 337 return(true); 338 } 339 340 /* 341 ** Otherwise a manual check of the file system is required to 342 ** determine if the file is actually available. 343 */ 344 return(CDFileClass::Is_Available()); 345 } 346 347 348 /*********************************************************************************************** 349 * CCFileClass::Is_Open -- Determines if the file is open. * 350 * * 351 * A mixfile is open if there is a pointer to the mixfile data. In absence of this, * 352 * the the file is open if the file handle is valid. * 353 * * 354 * INPUT: none * 355 * * 356 * OUTPUT: bool; Is the file open? * 357 * * 358 * WARNINGS: none * 359 * * 360 * HISTORY: * 361 * 08/08/1994 JLB : Created. * 362 *=============================================================================================*/ 363 int CCFileClass::Is_Open(void) const 364 { 365 /* 366 ** If the file is part of a cached file, then return that it is opened. A closed file 367 ** doesn't have a valid pointer. 368 */ 369 if (Is_Resident()) return(true); 370 371 /* 372 ** Otherwise, go to a lower level to determine if the file is open. 373 */ 374 return(CDFileClass::Is_Open()); 375 } 376 377 378 /*********************************************************************************************** 379 * CCFileClass::Close -- Closes the file. * 380 * * 381 * If this is a mixfile file, then only the pointers need to be adjusted. * 382 * * 383 * INPUT: none * 384 * * 385 * OUTPUT: none * 386 * * 387 * WARNINGS: none * 388 * * 389 * HISTORY: * 390 * 08/08/1994 JLB : Created. * 391 *=============================================================================================*/ 392 void CCFileClass::Close(void) 393 { 394 new(&Data) ::Buffer; 395 Position = 0; // Starts at beginning offset. 396 CDFileClass::Close(); 397 } 398 399 400 /*********************************************************************************************** 401 * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. * 402 * * 403 * This routine will open the specified file. It examines the mixfile system to find a * 404 * match. If one is found then the file is "opened" in a special cached way. Otherwise * 405 * it is opened as a standard DOS file. * 406 * * 407 * INPUT: rights -- The access rights desired. * 408 * * 409 * OUTPUT: bool; Was the file opened successfully? * 410 * * 411 * WARNINGS: none * 412 * * 413 * HISTORY: * 414 * 08/08/1994 JLB : Created. * 415 *=============================================================================================*/ 416 int CCFileClass::Open(int rights) 417 { 418 /* 419 ** Always close the file if it was open. 420 */ 421 Close(); 422 423 /* 424 ** Perform a preliminary check to see if the specified file 425 ** exists on the disk. If it does, then open this file regardless 426 ** of whether it also exists in RAM. This is slower, but allows 427 ** upgrade files to work. 428 */ 429 if ((rights & WRITE) || CDFileClass::Is_Available()) { 430 return(CDFileClass::Open(rights)); 431 } 432 433 /* 434 ** Check to see if file is part of a mixfile and that mixfile is currently loaded 435 ** into RAM. 436 */ 437 MFCD * mixfile = NULL; 438 void * pointer = NULL; 439 long length = 0; 440 long start = 0; 441 if (MFCD::Offset(File_Name(), &pointer, &mixfile, &start, &length)) { 442 443 assert(mixfile != NULL); 444 445 /* 446 ** If the mixfile is located on disk, then fake out the file system to read from 447 ** the mixfile, but think it is reading from a solitary file. 448 */ 449 if (pointer == NULL && mixfile != NULL) { 450 451 /* 452 ** This is a legitimate open to the file. All access to the file through this 453 ** file object will be appropriately adjusted for mixfile support however. Also 454 ** note that the filename attached to this object is NOT the same as the file 455 ** attached to the file handle. 456 */ 457 char * dupfile = strdup(File_Name()); 458 Open(mixfile->Filename, READ); 459 Searching(false); // Disable multi-drive search. 460 Set_Name(dupfile); 461 Searching(true); 462 free(dupfile); 463 Bias(0); 464 Bias(start, length); 465 Seek(0, SEEK_SET); 466 } else { 467 new (&Data) ::Buffer(pointer, length); 468 Position = 0; 469 } 470 471 } else { 472 473 /* 474 ** The file cannot be found in any mixfile, so it must reside as 475 ** an individual file on the disk. Or else it is just plain missing. 476 */ 477 return(CDFileClass::Open(rights)); 478 } 479 return(true); 480 } 481 482 483 /*********************************************************************************************** 484 * CCFileClass::Get_Date_Time -- Gets the date and time the file was last modified. * 485 * * 486 * Use this routine to get the date and time of the file. * 487 * * 488 * INPUT: none * 489 * * 490 * OUTPUT: Returns with the file date and time as a long. * 491 * Use the YEAR(long), MONTH(),.... * 492 * * 493 * WARNINGS: none * 494 * * 495 * HISTORY: * 496 * 11/14/1995 DRD : Created. * 497 *=============================================================================================*/ 498 unsigned long CCFileClass::Get_Date_Time(void) 499 { 500 unsigned long datetime; 501 MFCD * mixfile; 502 503 datetime = CDFileClass::Get_Date_Time(); 504 505 if ( !datetime ) { 506 if (MFCD::Offset(File_Name(), NULL, &mixfile, NULL, NULL)) { 507 // 508 // check for nested MIX files 509 // 510 return( CCFileClass(mixfile->Filename).Get_Date_Time() ); 511 } 512 // else return 0 indicating no file 513 } 514 515 return( datetime ); 516 } 517 518 519 /*********************************************************************************************** 520 * CCFileClass::Set_Date_Time -- Sets the date and time the file was last modified. * 521 * * 522 * Use this routine to set the date and time of the file. * 523 * * 524 * INPUT: the file date and time as a long * 525 * * 526 * OUTPUT: successful or not if the file date and time was changed. * 527 * * 528 * WARNINGS: none * 529 * * 530 * HISTORY: * 531 * 11/14/1995 DRD : Created. * 532 *=============================================================================================*/ 533 bool CCFileClass::Set_Date_Time( unsigned long datetime ) 534 { 535 bool status; 536 MFCD * mixfile; 537 538 status = CDFileClass::Set_Date_Time( datetime ); 539 540 if ( !status ) { 541 if (MFCD::Offset(File_Name(), NULL, &mixfile, NULL, NULL)) { 542 // 543 // check for nested MIX files 544 // 545 return( CCFileClass(mixfile->Filename).Set_Date_Time( datetime ) ); 546 } 547 // else return 0 indicating no file 548 } 549 550 return( status ); 551 } 552 553 554 /*********************************************************************************** 555 ** Backward compatibility section. 556 */ 557 //extern "C" { 558 559 560 static CCFileClass Handles[10]; 561 562 int __cdecl Open_File(char const * file_name, int mode) 563 { 564 for (int index = 0; index < ARRAY_SIZE(Handles); index++) { 565 if (!Handles[index].Is_Open()) { 566 if (Handles[index].Open(file_name, mode)) { 567 return(index); 568 } 569 break; 570 } 571 } 572 return(WWERROR); 573 } 574 575 void __cdecl Close_File(int handle) 576 { 577 if (handle != WWERROR && Handles[handle].Is_Open()) { 578 Handles[handle].Close(); 579 } 580 } 581 582 long __cdecl Read_File(int handle, void * buf, unsigned long bytes) 583 { 584 if (handle != WWERROR && Handles[handle].Is_Open()) { 585 return(Handles[handle].Read(buf, bytes)); 586 } 587 return(0); 588 } 589 590 long __cdecl Write_File(int handle, void const * buf, unsigned long bytes) 591 { 592 if (handle != WWERROR && Handles[handle].Is_Open()) { 593 return(Handles[handle].Write(buf, bytes)); 594 } 595 return(0); 596 } 597 598 int __cdecl Find_File(char const * file_name) 599 { 600 CCFileClass file(file_name); 601 return(file.Is_Available()); 602 } 603 604 #ifdef NEVER 605 int __cdecl Delete_File(char const * file_name) 606 { 607 return(CCFileClass(file_name).Delete()); 608 } 609 610 int __cdecl Create_File(char const * file_name) 611 { 612 return(CCFileClass(file_name).Create()); 613 } 614 615 unsigned long __cdecl Load_Data(char const * name, void * ptr, unsigned long size) 616 { 617 return(CCFileClass(name).Read(ptr, size)); 618 } 619 #endif 620 621 void * __cdecl Load_Alloc_Data(char const * name, int ) 622 { 623 CCFileClass file(name); 624 625 return(Load_Alloc_Data(file)); 626 } 627 628 unsigned long __cdecl File_Size(int handle) 629 { 630 if (handle != WWERROR && Handles[handle].Is_Open()) { 631 return(Handles[handle].Size()); 632 } 633 return(0); 634 } 635 636 #ifdef NEVER 637 unsigned long __cdecl Write_Data(char const * name, void const * ptr, unsigned long size) 638 { 639 return(CCFileClass(name).Write(ptr, size)); 640 } 641 #endif 642 643 unsigned long __cdecl Seek_File(int handle, long offset, int starting) 644 { 645 if (handle != WWERROR && Handles[handle].Is_Open()) { 646 return(Handles[handle].Seek(offset, starting)); 647 } 648 return(0); 649 } 650 651 #ifdef NEVER 652 bool __cdecl Multi_Drive_Search(bool on) 653 { 654 // return(CCFileClass::Multi_Drive_Search(on)); 655 return(on); 656 } 657 658 void __cdecl WWDOS_Init(void) 659 { 660 } 661 662 void __cdecl WWDOS_Shutdown(void) 663 { 664 } 665 666 int __cdecl Find_Disk_Number(char const *) 667 { 668 return(0); 669 } 670 #endif 671 672 //unsigned long __cdecl Load_Uncompress(char const * file, BuffType uncomp_buff, BuffType dest_buff, void * reserved_data) 673 //{ 674 // return(Load_Uncompress(CCFileClass(file), uncomp_buff, dest_buff, reserved_data)); 675 // return(CCFileClass(file).Load_Uncompress(uncomp_buff, dest_buff, reserved_data)); 676 //} 677 678 #ifdef WIN32 679 extern "C" { 680 int MaxDevice; 681 int DefaultDrive; 682 char CallingDOSInt; 683 684 } 685 #endif 686 687 688 void Unfragment_File_Cache(void) 689 { 690 } 691 692