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