RAWFILE.CPP (55762B)
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/RAWFILE.CPP 1 3/03/97 10:25a 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 : Westwood Library * 22 * * 23 * File Name : RAWFILE.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : August 8, 1994 * 28 * * 29 * Last Update : August 4, 1996 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * RawFileClass::Bias -- Bias a file with a specific starting position and length. * 34 * RawFileClass::Close -- Perform a closure of the file. * 35 * RawFileClass::Create -- Creates an empty file. * 36 * RawFileClass::Delete -- Deletes the file object from the disk. * 37 * RawFileClass::Error -- Handles displaying a file error message. * 38 * RawFileClass::Get_Date_Time -- Gets the date and time the file was last modified. * 39 * RawFileClass::Is_Available -- Checks to see if the specified file is available to open. * 40 * RawFileClass::Open -- Assigns name and opens file in one operation. * 41 * RawFileClass::Open -- Opens the file object with the rights specified. * 42 * RawFileClass::RawFileClass -- Simple constructor for a file object. * 43 * RawFileClass::Raw_Seek -- Performs a seek on the unbiased file * 44 * RawFileClass::Read -- Reads the specified number of bytes into a memory buffer. * 45 * RawFileClass::Seek -- Reposition the file pointer as indicated. * 46 * RawFileClass::Set_Date_Time -- Sets the date and time the file was last modified. * 47 * RawFileClass::Set_Name -- Manually sets the name for a file object. * 48 * RawFileClass::Size -- Determines size of file (in bytes). * 49 * RawFileClass::Write -- Writes the specified data to the buffer specified. * 50 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 51 52 53 #include <stdlib.h> 54 #include <stdio.h> 55 #include <string.h> 56 #include <direct.h> 57 #include <share.h> 58 #include <stddef.h> 59 60 #include "rawfile.h" 61 62 #ifndef WIN32 63 #include <fcntl.h> 64 #include <io.h> 65 #include <dos.h> 66 extern short Hard_Error_Occured; 67 #endif 68 69 70 /*********************************************************************************************** 71 * RawFileClass::Error -- Handles displaying a file error message. * 72 * * 73 * Display an error message as indicated. If it is allowed to retry, then pressing a key * 74 * will return from this function. Otherwise, it will exit the program with "exit()". * 75 * * 76 * INPUT: error -- The error number (same as the DOSERR.H error numbers). * 77 * * 78 * canretry -- Can this routine exit normally so that retrying can occur? If this is * 79 * false, then the program WILL exit in this routine. * 80 * * 81 * filename -- Optional filename to report with this error. If no filename is * 82 * supplied, then no filename is listed in the error message. * 83 * * 84 * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is * 85 * false or the player pressed ESC. * 86 * * 87 * WARNINGS: This routine may not return at all. It handles being in text mode as well as * 88 * if in a graphic mode. * 89 * * 90 * HISTORY: * 91 * 10/17/1994 JLB : Created. * 92 *=============================================================================================*/ 93 void RawFileClass::Error(int , int , char const * ) 94 { 95 } 96 97 98 /*********************************************************************************************** 99 * RawFileClass::RawFileClass -- Simple constructor for a file object. * 100 * * 101 * This constructor is called when a file object is created with a supplied filename, but * 102 * not opened at the same time. In this case, an assumption is made that the supplied * 103 * filename is a constant string. A duplicate of the filename string is not created since * 104 * it would be wasteful in that case. * 105 * * 106 * INPUT: filename -- The filename to assign to this file object. * 107 * * 108 * OUTPUT: none * 109 * * 110 * WARNINGS: none * 111 * * 112 * HISTORY: * 113 * 10/17/1994 JLB : Created. * 114 *=============================================================================================*/ 115 RawFileClass::RawFileClass(char const * filename) : 116 Rights(0), 117 BiasStart(0), 118 BiasLength(-1), 119 Handle(NULL_HANDLE), 120 Filename(filename), 121 Date(0), 122 Time(0), 123 Allocated(false) 124 { 125 } 126 127 128 /*********************************************************************************************** 129 * RawFileClass::Set_Name -- Manually sets the name for a file object. * 130 * * 131 * This routine will set the name for the file object to the name specified. This name is * 132 * duplicated in free store. This allows the supplied name to be a temporarily constructed * 133 * text string. Setting the name in this fashion doesn't affect the closed or opened state * 134 * of the file. * 135 * * 136 * INPUT: filename -- The filename to assign to this file object. * 137 * * 138 * OUTPUT: Returns with a pointer to the allocated copy of this filename. This pointer is * 139 * guaranteed to remain valid for the duration of this file object or until the name * 140 * is changed -- whichever is sooner. * 141 * * 142 * WARNINGS: Because of the allocation this routine must perform, memory could become * 143 * fragmented. * 144 * * 145 * HISTORY: * 146 * 10/17/1994 JLB : Created. * 147 *=============================================================================================*/ 148 char const * RawFileClass::Set_Name(char const * filename) 149 { 150 if (Filename != NULL && Allocated) { 151 free((char *)Filename); 152 ((char *&)Filename) = 0; 153 Allocated = false; 154 } 155 156 if (filename == NULL) return(NULL); 157 158 Bias(0); 159 160 ((char *&)Filename) = strdup(filename); 161 if (Filename == NULL) { 162 Error(ENOMEM, false, filename); 163 } 164 Allocated = true; 165 return(Filename); 166 } 167 168 169 /*********************************************************************************************** 170 * RawFileClass::Open -- Assigns name and opens file in one operation. * 171 * * 172 * This routine will assign the specified filename to the file object and open it at the * 173 * same time. If the file object was already open, then it will be closed first. If the * 174 * file object was previously assigned a filename, then it will be replaced with the new * 175 * name. Typically, this routine is used when an anonymous file object has been crated and * 176 * now it needs to be assigned a name and opened. * 177 * * 178 * INPUT: filename -- The filename to assign to this file object. * 179 * * 180 * rights -- The open file access rights to use. * 181 * * 182 * OUTPUT: bool; Was the file opened? The return value of this is moot, since the open file * 183 * is designed to never return unless it succeeded. * 184 * * 185 * WARNINGS: none * 186 * * 187 * HISTORY: * 188 * 10/17/1994 JLB : Created. * 189 *=============================================================================================*/ 190 int RawFileClass::Open(char const * filename, int rights) 191 { 192 Set_Name(filename); 193 return(Open(rights)); 194 } 195 196 197 /*********************************************************************************************** 198 * RawFileClass::Open -- Opens the file object with the rights specified. * 199 * * 200 * This routine is used to open the specified file object with the access rights indicated. * 201 * This only works if the file has already been assigned a filename. It is guaranteed, by * 202 * the error handler, that this routine will always return with success. * 203 * * 204 * INPUT: rights -- The file access rights to use when opening this file. This is a * 205 * combination of READ and/or WRITE bit flags. * 206 * * 207 * OUTPUT: bool; Was the file opened successfully? This will always return true by reason of * 208 * the error handler. * 209 * * 210 * WARNINGS: none * 211 * * 212 * HISTORY: * 213 * 10/17/1994 JLB : Created. * 214 *=============================================================================================*/ 215 int RawFileClass::Open(int rights) 216 { 217 Close(); 218 219 /* 220 ** Verify that there is a filename associated with this file object. If not, then this is a 221 ** big error condition. 222 */ 223 if (Filename == NULL) { 224 Error(ENOENT, false); 225 } 226 227 /* 228 ** Record the access rights used for this open call. These rights will be used if the 229 ** file object is duplicated. 230 */ 231 Rights = rights; 232 233 /* 234 ** Repetitively try to open the file. Abort if a fatal error condition occurs. 235 */ 236 for (;;) { 237 238 /* 239 ** Try to open the file according to the access rights specified. 240 */ 241 #ifndef WIN32 242 Hard_Error_Occured = 0; 243 #endif 244 switch (rights) { 245 246 /* 247 ** If the access rights are not recognized, then report this as 248 ** an invalid access code. 249 */ 250 default: 251 errno = EINVAL; 252 break; 253 254 case READ: 255 #ifdef WIN32 256 Handle = CreateFile(Filename, GENERIC_READ, FILE_SHARE_READ, 257 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 258 #else 259 _dos_open(Filename, O_RDONLY|SH_DENYNO, &Handle); 260 #endif 261 break; 262 263 case WRITE: 264 #ifdef WIN32 265 Handle = CreateFile(Filename, GENERIC_WRITE, 0, 266 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 267 #else 268 _dos_creat(Filename, 0, &Handle); 269 #endif 270 break; 271 272 case READ|WRITE: 273 #ifdef WIN32 274 Handle = CreateFile(Filename, GENERIC_READ | GENERIC_WRITE, 0, 275 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 276 #else 277 _dos_open(Filename, O_RDWR|O_CREAT|SH_DENYWR, &Handle); 278 #endif 279 break; 280 } 281 282 /* 283 ** Biased files must be positioned past the bias start position. 284 */ 285 if (BiasStart != 0 || BiasLength != -1) { 286 Seek(0, SEEK_SET); 287 } 288 289 /* 290 ** If the handle indicates the file is not open, then this is an error condition. 291 ** For the case of the file cannot be found, then allow a retry. All other cases 292 ** are fatal. 293 */ 294 if (Handle == NULL_HANDLE) { 295 296 #ifdef WIN32 297 // return(false); 298 Error(GetLastError(), false, Filename); 299 // continue; 300 #else 301 /* 302 ** If this flag is set, then some hard error occurred. Just assume that the error 303 ** is probably a removed CD-ROM and allow a retry. 304 */ 305 if (Hard_Error_Occured) { 306 Error(Hard_Error_Occured, true, Filename); 307 } else { 308 if (errno == ENOENT) { 309 Error(ENOENT, true, Filename); 310 } else { 311 Error(errno, false, Filename); 312 } 313 } 314 continue; 315 #endif 316 } 317 break; 318 } 319 320 return(true); 321 } 322 323 324 /*********************************************************************************************** 325 * RawFileClass::Is_Available -- Checks to see if the specified file is available to open. * 326 * * 327 * This routine will examine the disk system to see if the specified file can be opened * 328 * or not. Use this routine before opening a file in order to make sure that is available * 329 * or to perform other necessary actions. * 330 * * 331 * INPUT: force -- Should this routine keep retrying until the file becomes available? If * 332 * in this case it doesn't become available, then the program will abort. * 333 * * 334 * OUTPUT: bool; Is the file available to be opened? * 335 * * 336 * WARNINGS: Depending on the parameter passed in, this routine may never return. * 337 * * 338 * HISTORY: * 339 * 10/18/1994 JLB : Created. * 340 *=============================================================================================*/ 341 int RawFileClass::Is_Available(int forced) 342 { 343 #ifndef WIN32 344 bool open_failed; 345 #endif 346 347 if (Filename == NULL) return(false); 348 349 /* 350 ** If the file is already open, then is must have already passed the availability check. 351 ** Return true in this case. 352 */ 353 if (Is_Open()) return(true); 354 355 /* 356 ** If this is a forced check, then go through the normal open channels, since those 357 ** channels ensure that the file must exist. 358 */ 359 if (forced) { 360 RawFileClass::Open(READ); 361 RawFileClass::Close(); 362 return(true); 363 } 364 365 /* 366 ** Perform a raw open of the file. If this open fails for ANY REASON, including a missing 367 ** CD-ROM, this routine will return a failure condition. In all but the missing file 368 ** condition, go through the normal error recover channels. 369 */ 370 for (;;) { 371 372 #ifdef WIN32 373 Handle = CreateFile(Filename, GENERIC_READ, FILE_SHARE_READ, 374 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 375 if (Handle == NULL_HANDLE) { 376 return(false); 377 } 378 break; 379 #else 380 381 Hard_Error_Occured = 0; 382 open_failed = _dos_open(Filename, O_RDONLY|SH_DENYNO, &Handle); 383 384 /* 385 ** If DOS reports that everything is just fine except that the file is not present, 386 ** then return with this fact. Any other case will fall through to the error handler 387 ** routine. 388 */ 389 if (open_failed && errno == ENOENT) return(false); 390 391 /* 392 ** If we got an access error it could be because there is no cd in 393 ** the drive. Call the error handler but allow a continue if it 394 ** returns. 395 */ 396 if (open_failed && errno == EACCES) { 397 Error(errno, true, Filename); 398 continue; 399 } 400 401 /* 402 ** If the file could not be found, then return with this information. If a more 403 ** serious error occurred, then display the error message and abort. 404 */ 405 if (Hard_Error_Occured) { 406 Error(Hard_Error_Occured, true, Filename); 407 continue; 408 } else { 409 if (open_failed) { 410 /* 411 ** An unhandled error condition is fatal. Display the error message and then 412 ** abort. 413 */ 414 Error(errno, false, Filename); 415 } 416 } 417 if (!open_failed) break; 418 #endif 419 } 420 421 /* 422 ** Since the file could be opened, then close it and return that the file exists. 423 */ 424 #ifdef WIN32 425 if (!CloseHandle(Handle)) { 426 Error(GetLastError(), false, Filename); 427 } 428 #else 429 if (_dos_close(Handle)) { 430 Error(errno, false, Filename); 431 } 432 #endif 433 Handle = NULL_HANDLE; 434 435 return(true); 436 } 437 438 439 /*********************************************************************************************** 440 * RawFileClass::Close -- Perform a closure of the file. * 441 * * 442 * Close the file object. In the rare case of an error, handle it as appropriate. * 443 * * 444 * INPUT: none * 445 * * 446 * OUTPUT: none * 447 * * 448 * WARNINGS: Some rare error conditions may cause this routine to abort the program. * 449 * * 450 * HISTORY: * 451 * 10/18/1994 JLB : Created. * 452 *=============================================================================================*/ 453 void RawFileClass::Close(void) 454 { 455 /* 456 ** If the file is open, then close it. If the file is already closed, then just return. This 457 ** isn't considered an error condition. 458 */ 459 if (Is_Open()) { 460 461 #ifdef WIN32 462 /* 463 ** Try to close the file. If there was an error (who knows what that could be), then 464 ** call the error routine. 465 */ 466 if (!CloseHandle(Handle)) { 467 Error(GetLastError(), false, Filename); 468 } 469 #else 470 for (;;) { 471 /* 472 ** Close the file. If there was an error in the close operation -- abort. 473 */ 474 Hard_Error_Occured = 0; 475 if (_dos_close(Handle)) { 476 477 /* 478 ** By definition, this error can only be a bad file handle. This a fatal condition 479 ** of course, so abort with an error message. 480 */ 481 Error(errno, false, Filename); 482 } 483 484 /* 485 ** In the condition (if it is even possible) of a hard error occurring, then 486 ** assume it is the case of missing media. Display an error message and try 487 ** again if indicated. 488 */ 489 if (Hard_Error_Occured) { 490 Error(Hard_Error_Occured, true, Filename); 491 continue; 492 } 493 break; 494 } 495 496 #endif 497 498 /* 499 ** At this point the file must have been closed. Mark the file as empty and return. 500 */ 501 Handle = NULL_HANDLE; 502 } 503 } 504 505 506 /*********************************************************************************************** 507 * RawFileClass::Read -- Reads the specified number of bytes into a memory buffer. * 508 * * 509 * This routine will read the specified number of bytes and place the data into the buffer * 510 * indicated. It is legal to call this routine with a request for more bytes than are in * 511 * the file. This condition can result in fewer bytes being read than requested. Determine * 512 * this by examining the return value. * 513 * * 514 * INPUT: buffer -- Pointer to the buffer to read data into. If NULL is passed, no read * 515 * is performed. * 516 * * 517 * size -- The number of bytes to read. If NULL is passed, then no read is * 518 * performed. * 519 * * 520 * OUTPUT: Returns with the number of bytes read into the buffer. If this number is less * 521 * than requested, it indicates that the file has been exhausted. * 522 * * 523 * WARNINGS: none * 524 * * 525 * HISTORY: * 526 * 10/18/1994 JLB : Created. * 527 *=============================================================================================*/ 528 long RawFileClass::Read(void * buffer, long size) 529 { 530 long bytesread = 0; // Running count of the number of bytes read into the buffer. 531 int opened = false; // Was the file opened by this routine? 532 533 /* 534 ** If the file isn't opened, open it. This serves as a convenience 535 ** for the programmer. 536 */ 537 if (!Is_Open()) { 538 539 /* 540 ** The error check here is moot. Open will never return unless it succeeded. 541 */ 542 if (!Open(READ)) { 543 return(0); 544 } 545 opened = true; 546 } 547 548 /* 549 ** A biased file has the requested read length limited to the bias length of 550 ** the file. 551 */ 552 if (BiasLength != -1) { 553 int remainder = BiasLength - Seek(0); 554 size = size < remainder ? size : remainder; 555 } 556 557 #ifdef WIN32 558 long total = 0; 559 while (size > 0) { 560 bytesread = 0; 561 562 SetErrorMode(SEM_FAILCRITICALERRORS); 563 if (!ReadFile(Handle, buffer, size, &(unsigned long&)bytesread, NULL)) { 564 size -= bytesread; 565 total += bytesread; 566 Error(GetLastError(), true, Filename); 567 SetErrorMode(0); 568 continue; 569 } 570 SetErrorMode(0); 571 size -= bytesread; 572 total += bytesread; 573 if (bytesread == 0) break; 574 } 575 bytesread = total; 576 577 #else 578 579 int readresult; 580 581 /* 582 ** Read the file in convenient chunk sizes. When the actual number 583 ** of bytes read does not match the desired, then assume that the file 584 ** is exhausted and bail. This loop was adjusted to take into 585 ** consideration the fact that "read" returns a SIGNED value whereas 586 ** it takes an UNSIGNED value as the byte count. 587 */ 588 while (size) { 589 unsigned desired; // Bytes desired to be read this pass. 590 unsigned actual; // Actual number of bytes read. 591 592 /* 593 ** Break the read request into chunks no bigger than the low level DOS read 594 ** can handle. 595 */ 596 desired = size < 32000L ? size : 32000L; 597 598 Hard_Error_Occured = 0; 599 readresult = _dos_read(Handle, buffer, desired, &actual); 600 601 /* 602 ** If a hard error occurred, then assume that it is the case of the CD-ROM or 603 ** floppy media having been removed. Display the error and retry as directed. 604 */ 605 if (Hard_Error_Occured) { 606 Error(Hard_Error_Occured, true, Filename); 607 continue; // Not technically needed, but to be consistent... 608 } else { 609 610 /* 611 ** If negative one is returned from the read operation, then this indicates 612 ** either a bad file number or invalid access. These are fatal conditions, so 613 ** display the error and then abort. 614 */ 615 if (readresult != 0) { 616 Error(errno, false, Filename); 617 } else { 618 619 /* 620 ** No error occurred during the read. Adjust the pointers and size counters and 621 ** loop again if more data is needed to be read. 622 */ 623 buffer = (char *)buffer + actual; 624 bytesread += actual; 625 size -= actual; 626 if (actual != desired) break; // No more data? 627 } 628 } 629 } 630 #endif //WIN32 631 632 /* 633 ** Close the file if it was opened by this routine and return 634 ** the actual number of bytes read into the buffer. 635 */ 636 if (opened) Close(); 637 return(bytesread); 638 } 639 640 641 /*********************************************************************************************** 642 * RawFileClass::Write -- Writes the specified data to the buffer specified. * 643 * * 644 * This routine will write the data specified to the file. * 645 * * 646 * INPUT: buffer -- The buffer that holds the data to write. * 647 * * 648 * size -- The number of bytes to write to the file. * 649 * * 650 * OUTPUT: Returns with the number of bytes written to the file. This routine catches the * 651 * case of a disk full condition, so this routine will always return with the number * 652 * matching the size request. * 653 * * 654 * WARNINGS: A fatal file condition could cause this routine to never return. * 655 * * 656 * HISTORY: * 657 * 10/18/1994 JLB : Created. * 658 *=============================================================================================*/ 659 long RawFileClass::Write(void const * buffer, long size) 660 { 661 long bytesread = 0; 662 int opened = false; // Was the file manually opened? 663 664 /* 665 ** Check to open status of the file. If the file is open, then merely write to 666 ** it. Otherwise, open the file for writing and then close the file when the 667 ** output is finished. 668 */ 669 if (!Is_Open()) { 670 if (!Open(WRITE)) { 671 return(0); 672 } 673 opened = true; 674 } 675 676 #ifdef WIN32 677 if (!WriteFile(Handle, buffer, size, &(unsigned long&)bytesread, NULL)) { 678 Error(GetLastError(), false, Filename); 679 } 680 681 #else 682 683 int writeresult; 684 /* 685 ** Write the data to the file in chunks no bigger than what the low level DOS write 686 ** can handle. 687 */ 688 while (size) { 689 unsigned desired; // Bytes desired to be write this pass. 690 unsigned actual; // Actual number of bytes written. 691 692 Hard_Error_Occured = 0; 693 // desired = (unsigned)MIN(size, Transfer_Block_Size()); 694 desired = size; 695 writeresult = _dos_write(Handle, buffer, desired, &actual); 696 697 /* 698 ** If a hard error occurred, then assume it is the case of the media being 699 ** removed. Print the error message an retry as directed. 700 */ 701 if (Hard_Error_Occured) { 702 Error(Hard_Error_Occured, true, Filename); 703 continue; // Not technically needed, but to be consistent... 704 } else { 705 706 /* 707 ** If negative one is returned by the DOS read, then this indicates a bad file 708 ** handle or invalid access. Either condition is fatal -- display error condition 709 ** and abort. 710 */ 711 if (writeresult != 0) { 712 Error(errno, false, Filename); 713 } else { 714 715 /* 716 ** A successful write occurred. Update pointers and byte counter as appropriate. 717 */ 718 buffer = (char *)buffer + actual; 719 bytesread += actual; 720 size -= actual; 721 722 /* 723 ** If the actual bytes written is less than requested, assume this is a case of 724 ** the disk being full. Consider this a fatal error condition. 725 */ 726 if (actual != desired) { 727 Error(ENOSPC, false, Filename); 728 } 729 } 730 } 731 } 732 #endif //WIN32 733 734 /* 735 ** Fixup the bias length if necessary. 736 */ 737 if (BiasLength != -1) { 738 if (Raw_Seek(0) > BiasStart+BiasLength) { 739 BiasLength = Raw_Seek(0) - BiasStart; 740 } 741 } 742 743 /* 744 ** If this routine had to open the file, then close it before returning. 745 */ 746 if (opened) { 747 Close(); 748 } 749 750 /* 751 ** Return with the number of bytes written. This will always be the number of bytes 752 ** requested, since the case of the disk being full is caught by this routine. 753 */ 754 return(bytesread); 755 } 756 757 758 /*********************************************************************************************** 759 * RawFileClass::Seek -- Reposition the file pointer as indicated. * 760 * * 761 * Use this routine to move the filepointer to the position indicated. It can move either * 762 * relative to current position or absolute from the beginning or ending of the file. This * 763 * routine will only return if it successfully performed the seek. * 764 * * 765 * INPUT: pos -- The position to seek to. This is interpreted as relative to the position * 766 * indicated by the "dir" parameter. * 767 * * 768 * dir -- The relative position to relate the seek to. This can be either SEEK_SET * 769 * for the beginning of the file, SEEK_CUR for the current position, or * 770 * SEEK_END for the end of the file. * 771 * * 772 * OUTPUT: This routine returns the position that the seek ended up at. * 773 * * 774 * WARNINGS: If there was a file error, then this routine might never return. * 775 * * 776 * HISTORY: * 777 * 10/18/1994 JLB : Created. * 778 *=============================================================================================*/ 779 long RawFileClass::Seek(long pos, int dir) 780 { 781 782 /* 783 ** A file that is biased will have a seek operation modified so that the file appears to 784 ** exist only within the bias range. All bytes outside of this range appear to be 785 ** non-existant. 786 */ 787 if (BiasLength != -1) { 788 switch (dir) { 789 case SEEK_SET: 790 if (pos > BiasLength) { 791 pos = BiasLength; 792 } 793 pos += BiasStart; 794 break; 795 796 case SEEK_CUR: 797 break; 798 799 case SEEK_END: 800 dir = SEEK_SET; 801 pos += BiasStart + BiasLength; 802 // pos = (pos <= BiasStart+BiasLength) ? pos : BiasStart+BiasLength; 803 // pos = (pos >= BiasStart) ? pos : BiasStart; 804 break; 805 } 806 807 /* 808 ** Perform the modified raw seek into the file. 809 */ 810 long newpos = Raw_Seek(pos, dir) - BiasStart; 811 812 /* 813 ** Perform a final double check to make sure the file position fits with the bias range. 814 */ 815 if (newpos < 0) { 816 newpos = Raw_Seek(BiasStart, SEEK_SET) - BiasStart; 817 } 818 if (newpos > BiasLength) { 819 newpos = Raw_Seek(BiasStart+BiasLength, SEEK_SET) - BiasStart; 820 } 821 return(newpos); 822 } 823 824 /* 825 ** If the file is not biased in any fashion, then the normal seek logic will 826 ** work just fine. 827 */ 828 return(Raw_Seek(pos, dir)); 829 } 830 831 832 /*********************************************************************************************** 833 * RawFileClass::Size -- Determines size of file (in bytes). * 834 * * 835 * Use this routine to determine the size of the file. The file must exist or this is an * 836 * error condition. * 837 * * 838 * INPUT: none * 839 * * 840 * OUTPUT: Returns with the number of bytes in the file. * 841 * * 842 * WARNINGS: This routine handles error conditions and will not return unless the file * 843 * exists and can successfully be queried for file length. * 844 * * 845 * HISTORY: * 846 * 10/18/1994 JLB : Created. * 847 *=============================================================================================*/ 848 long RawFileClass::Size(void) 849 { 850 long size = 0; 851 852 /* 853 ** A biased file already has its length determined. 854 */ 855 if (BiasLength != -1) { 856 return(BiasLength); 857 } 858 859 /* 860 ** If the file is open, then proceed normally. 861 */ 862 if (Is_Open()) { 863 864 #ifdef WIN32 865 size = GetFileSize(Handle, NULL); 866 867 /* 868 ** If there was in internal error, then call the error function. 869 */ 870 if (size == 0xFFFFFFFF) { 871 Error(GetLastError(), false, Filename); 872 } 873 #else 874 875 /* 876 ** Repetitively try to determine the file size until a fatal error condition or success 877 ** is achieved. 878 */ 879 for (;;) { 880 Hard_Error_Occured = 0; 881 size = filelength(Handle); 882 883 /* 884 ** If a hard error occurred, then assume it is the case of removed media. Display an 885 ** error condition and allow retry. 886 */ 887 if (Hard_Error_Occured) { 888 Error(Hard_Error_Occured, true, Filename); 889 continue; 890 } else { 891 if (size == -1) { 892 Error(errno, false, Filename); 893 } 894 } 895 break; 896 } 897 #endif 898 899 } else { 900 901 /* 902 ** If the file wasn't open, then open the file and call this routine again. Count on 903 ** the fact that the open function must succeed. 904 */ 905 if (Open()) { 906 size = Size(); 907 908 /* 909 ** Since we needed to open the file we must remember to close the file when the 910 ** size has been determined. 911 */ 912 Close(); 913 } 914 } 915 916 BiasLength = size-BiasStart; 917 return(BiasLength); 918 } 919 920 921 /*********************************************************************************************** 922 * RawFileClass::Create -- Creates an empty file. * 923 * * 924 * This routine will create an empty file from the file object. The file object's filename * 925 * must already have been assigned before this routine will function. * 926 * * 927 * INPUT: none * 928 * * 929 * OUTPUT: bool; Was the file successfully created? This routine will always return true. * 930 * * 931 * WARNINGS: A fatal error condition could occur with this routine. Especially if the disk * 932 * is full or a read-only media was selected. * 933 * * 934 * HISTORY: * 935 * 10/18/1994 JLB : Created. * 936 *=============================================================================================*/ 937 int RawFileClass::Create(void) 938 { 939 Close(); 940 if (Open(WRITE)) { 941 942 /* 943 ** A biased file must be at least as long as the bias offset. Seeking to the 944 ** appropriate start offset has the effect of lengthening the file to the 945 ** correct length. 946 */ 947 if (BiasLength != -1) { 948 Seek(0, SEEK_SET); 949 } 950 951 Close(); 952 return(true); 953 } 954 return(false); 955 } 956 957 958 /*********************************************************************************************** 959 * RawFileClass::Delete -- Deletes the file object from the disk. * 960 * * 961 * This routine will delete the file object from the disk. If the file object doesn't * 962 * exist, then this routine will return as if it had succeeded (since the effect is the * 963 * same). * 964 * * 965 * INPUT: none * 966 * * 967 * OUTPUT: bool; Was the file deleted? If the file was already missing, the this value will * 968 * be false. * 969 * * 970 * WARNINGS: none * 971 * * 972 * HISTORY: * 973 * 10/18/1994 JLB : Created. * 974 *=============================================================================================*/ 975 int RawFileClass::Delete(void) 976 { 977 /* 978 ** If the file was open, then it must be closed first. 979 */ 980 Close(); 981 982 /* 983 ** If there is no filename associated with this object, then this indicates a fatal error 984 ** condition. Report this and abort. 985 */ 986 if (!Filename) { 987 Error(ENOENT, false); 988 } 989 990 /* 991 ** Repetitively try to delete the file if possible. Either return with success, or 992 ** abort the program with an error. 993 */ 994 for (;;) { 995 996 /* 997 ** If the file is already missing, then return with this fact. No action is necessary. 998 ** This can occur as this section loops if the file exists on a floppy and the floppy 999 ** was removed, the file deleted on another machine, and then the floppy was 1000 ** reinserted. Admittedly, this is a rare case, but is handled here. 1001 */ 1002 if (!Is_Available()) { 1003 return(false); 1004 } 1005 1006 #ifdef WIN32 1007 if (!DeleteFile(Filename)) { 1008 Error(GetLastError(), false, Filename); 1009 return(false); 1010 } 1011 #else 1012 Hard_Error_Occured = 0; 1013 if (remove(Filename) == -1) { 1014 1015 /* 1016 ** If a hard error occurred, then assume that the media has been removed. Display 1017 ** error message and retry as directed. 1018 */ 1019 if (Hard_Error_Occured) { 1020 Error(Hard_Error_Occured, true, Filename); 1021 continue; 1022 } 1023 1024 /* 1025 ** If at this point, DOS says the file doesn't exist, then just exit with this 1026 ** fact. It should have been caught earlier, but in any case, this is a legal 1027 ** condition. 1028 */ 1029 if (errno == ENOENT) break; 1030 1031 /* 1032 ** The only way it can reach this point is if DOS indicates that access is denied 1033 ** on the file. This occurs when trying to delete a file on a read-only media such 1034 ** as a CD-ROM. Report this as a fatal error and then abort. 1035 */ 1036 Error(errno, false, Filename); 1037 } 1038 #endif 1039 break; 1040 } 1041 1042 /* 1043 ** DOS reports that the file was successfully deleted. Return with this fact. 1044 */ 1045 return(true); 1046 } 1047 1048 1049 /*********************************************************************************************** 1050 * RawFileClass::Get_Date_Time -- Gets the date and time the file was last modified. * 1051 * * 1052 * Use this routine to get the date and time of the file. * 1053 * * 1054 * INPUT: none * 1055 * * 1056 * OUTPUT: Returns with the file date and time as a long. * 1057 * Use the YEAR(long), MONTH(),.... * 1058 * * 1059 * WARNINGS: none * 1060 * * 1061 * HISTORY: * 1062 * 11/14/1995 DRD : Created. * 1063 * 07/13/1996 JLB : Handles win32 method. * 1064 *=============================================================================================*/ 1065 unsigned long RawFileClass::Get_Date_Time(void) 1066 { 1067 #ifdef WIN32 1068 BY_HANDLE_FILE_INFORMATION info; 1069 1070 if (GetFileInformationByHandle(Handle, &info)) { 1071 WORD dosdate; 1072 WORD dostime; 1073 FileTimeToDosDateTime(&info.ftLastWriteTime, &dosdate, &dostime); 1074 return((dosdate << 16) | dostime); 1075 } 1076 return(0); 1077 #else 1078 unsigned short time; 1079 unsigned short date; 1080 unsigned long datetime = 0; 1081 1082 1083 // 1084 // If the file is open, then proceed normally. 1085 // 1086 if ( RawFileClass::Is_Open() ) { 1087 if ( _dos_getftime( Handle, &date, &time ) ) { 1088 // 1089 // return 0 indicating error with no date and time 1090 // 1091 return( datetime ); 1092 } 1093 } else { 1094 1095 // 1096 // If the file wasn't open, then see if the file exists. 1097 // 1098 if ( RawFileClass::Is_Available() ) { 1099 RawFileClass::Open(); 1100 1101 if ( _dos_getftime( Handle, &date, &time ) ) { 1102 RawFileClass::Close(); 1103 // 1104 // return 0 indicating error with no date and time 1105 // 1106 return( datetime ); 1107 } 1108 1109 RawFileClass::Close(); 1110 } else { 1111 // 1112 // return 0 indicating error with no date and time 1113 // 1114 return( datetime ); 1115 } 1116 } 1117 1118 // 1119 // combine the date and time as a long 1120 // 1121 datetime = (date << 16) + time; 1122 1123 return( datetime ); 1124 #endif 1125 } 1126 1127 1128 /*********************************************************************************************** 1129 * RawFileClass::Set_Date_Time -- Sets the date and time the file was last modified. * 1130 * * 1131 * Use this routine to set the date and time of the file. * 1132 * * 1133 * INPUT: the file date and time as a long * 1134 * * 1135 * OUTPUT: successful or not if the file date and time was changed. * 1136 * * 1137 * WARNINGS: none * 1138 * * 1139 * HISTORY: * 1140 * 11/14/1995 DRD : Created. * 1141 * 07/13/1996 JLB : Handles win 32 method * 1142 *=============================================================================================*/ 1143 bool RawFileClass::Set_Date_Time(unsigned long datetime) 1144 { 1145 #ifdef WIN32 1146 if (RawFileClass::Is_Open()) { 1147 BY_HANDLE_FILE_INFORMATION info; 1148 1149 if (GetFileInformationByHandle(Handle, &info)) { 1150 FILETIME filetime; 1151 if (DosDateTimeToFileTime((WORD)(datetime >> 16), (WORD)(datetime & 0x0FFFF), &filetime)) { 1152 return(SetFileTime(Handle, &info.ftCreationTime, &filetime, &filetime)); 1153 } 1154 } 1155 } 1156 return(false); 1157 #else 1158 unsigned short time; 1159 unsigned short date; 1160 1161 // 1162 // If the file is open, then proceed normally. 1163 // 1164 if ( RawFileClass::Is_Open() ) { 1165 // 1166 // only set the date and time if open for READ only 1167 // 1168 if ( Rights == READ ) { 1169 time = (unsigned short)(datetime & 0xFFFF); 1170 date = (unsigned short)((datetime >> 16) & 0xFFFF); 1171 1172 if ( !_dos_setftime( Handle, date, time ) ) { 1173 // 1174 // return true indicating success 1175 // 1176 return( true ); 1177 } 1178 } 1179 } else { 1180 1181 // 1182 // If the file wasn't open, then see if the file exists. 1183 // 1184 if ( RawFileClass::Is_Available() ) { 1185 RawFileClass::Open(); 1186 1187 time = (unsigned short)(datetime & 0xFFFF); 1188 date = (unsigned short)((datetime >> 16) & 0xFFFF); 1189 1190 if ( !_dos_setftime( Handle, date, time ) ) { 1191 RawFileClass::Close(); 1192 // 1193 // return true indicating success 1194 // 1195 return( true ); 1196 } 1197 1198 RawFileClass::Close(); 1199 } 1200 } 1201 1202 // 1203 // return false indicating error 1204 // 1205 return( false ); 1206 #endif 1207 } 1208 1209 1210 /*********************************************************************************************** 1211 * RawFileClass::Bias -- Bias a file with a specific starting position and length. * 1212 * * 1213 * This will bias a file by giving it an artificial starting position and length. By * 1214 * using this routine, it is possible to 'fool' the file into ignoring a header and * 1215 * trailing extra data. An example of this would be a file inside of a mixfile. * 1216 * * 1217 * INPUT: start -- The starting offset that will now be considered the start of the * 1218 * file. * 1219 * * 1220 * length -- The forced length of the file. For files that are opened for write, * 1221 * this serves as the artificial constraint on the file's length. For * 1222 * files opened for read, this limits the usable file size. * 1223 * * 1224 * OUTPUT: none * 1225 * * 1226 * WARNINGS: none * 1227 * * 1228 * HISTORY: * 1229 * 06/02/1996 JLB : Created. * 1230 *=============================================================================================*/ 1231 void RawFileClass::Bias(int start, int length) 1232 { 1233 if (start == 0) { 1234 BiasStart = 0; 1235 BiasLength = -1; 1236 return; 1237 } 1238 1239 BiasLength = RawFileClass::Size(); 1240 BiasStart += start; 1241 if (length != -1) { 1242 BiasLength = BiasLength < length ? BiasLength : length; 1243 } 1244 BiasLength = BiasLength > 0 ? BiasLength : 0; 1245 1246 /* 1247 ** Move the current file offset to a legal position if necessary and the 1248 ** file was open. 1249 */ 1250 if (Is_Open()) { 1251 RawFileClass::Seek(0, SEEK_SET); 1252 } 1253 } 1254 1255 1256 /*********************************************************************************************** 1257 * RawFileClass::Raw_Seek -- Performs a seek on the unbiased file * 1258 * * 1259 * This will perform a seek on the file as if it were unbiased. This is in spite of any * 1260 * bias setting the file may have. The ability to perform a raw seek in this fasion is * 1261 * necessary to maintain the bias ability. * 1262 * * 1263 * INPUT: pos -- The position to seek the file relative to the "dir" parameter. * 1264 * * 1265 * dir -- The origin of the seek operation. * 1266 * * 1267 * OUTPUT: Returns with the new position of the seek operation. * 1268 * * 1269 * WARNINGS: none * 1270 * * 1271 * HISTORY: * 1272 * 08/04/1996 JLB : Created. * 1273 *=============================================================================================*/ 1274 long RawFileClass::Raw_Seek(long pos, int dir) 1275 { 1276 /* 1277 ** If the file isn't opened, then this is a fatal error condition. 1278 */ 1279 if (!Is_Open()) { 1280 Error(EBADF, false, Filename); 1281 } 1282 1283 #ifdef WIN32 1284 switch (dir) { 1285 case SEEK_SET: 1286 dir = FILE_BEGIN; 1287 break; 1288 1289 case SEEK_CUR: 1290 dir = FILE_CURRENT; 1291 break; 1292 1293 case SEEK_END: 1294 dir = FILE_END; 1295 break; 1296 } 1297 1298 pos = SetFilePointer(Handle, pos, NULL, dir); 1299 1300 /* 1301 ** If there was an error in the seek, then bail with an error condition. 1302 */ 1303 if (pos == 0xFFFFFFFF) { 1304 Error(GetLastError(), false, Filename); 1305 } 1306 #else 1307 1308 /* 1309 ** Keep trying to seek until a non-retry condition occurs. 1310 */ 1311 for (;;) { 1312 1313 /* 1314 ** Perform the low level seek on the file. 1315 */ 1316 Hard_Error_Occured = 0; 1317 pos = lseek(Handle, pos, dir); 1318 1319 /* 1320 ** If a hard error occurred, then assume that it is the case of removed media. Display 1321 ** error message and retry. 1322 */ 1323 if (Hard_Error_Occured) { 1324 Error(Hard_Error_Occured, true, Filename); 1325 continue; 1326 } else { 1327 1328 /* 1329 ** A negative one indicates a fatal error with the seek operation. Display error 1330 ** condition and then abort. 1331 */ 1332 if (pos == -1) { 1333 Error(errno, false, Filename); 1334 } 1335 } 1336 break; 1337 } 1338 #endif 1339 1340 /* 1341 ** Return with the new position of the file. This will range between zero and the number of 1342 ** bytes the file contains. 1343 */ 1344 return(pos); 1345 }