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