1 /* 2 Copyright (C) 2013 Michael D. Parker 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 28 */ 29 module defile.defile; 30 31 private { 32 import std..string; 33 import std.conv; 34 import std.traits; 35 36 import derelict.physfs.physfs; 37 } 38 39 class DefileException : Exception 40 { 41 public this(string msg, string file = __FILE__, size_t line = __LINE__) 42 { 43 this(msg, true, file, line); 44 } 45 46 public this(string msg, bool getErrString, string file = __FILE__, size_t line = __LINE__) 47 { 48 if(getErrString) { 49 msg = format("%s: %s", msg, Defile.lastError); 50 } 51 super(msg, file, line, null); 52 } 53 } 54 55 enum OpenFor { 56 read, 57 write, 58 append, 59 } 60 61 enum ConfigFlags { 62 none = 0x0, 63 includeCDRoms = 0x1, 64 archivesFirst = 0x2, 65 } 66 67 enum PathType { 68 write, 69 base, 70 } 71 72 enum MountAction { 73 prepend, 74 append, 75 } 76 77 /++ 78 A wrapper of the PhysicsFS library, specifically via the DerelictPHYSFS binding. 79 80 In some cases, the methods of Defile directly wrap PhysicsFS functions, doing nothing 81 more than converting between C and D types and throwing exceptions when a call 82 fails. Other methods are for convenience, wrapping multiple PhysicsFS function 83 calls into a single method. For more information on the details of the wrapped 84 PhysicsFS functions, please refer either to the PHYSFS documentation or physfs.h. 85 86 Note that the static methods below either wrap functions that work with global 87 state or serve as covenience methods that eliminate the need to deal with an 88 indivdual file. The nonstatic methods wrap functions that manipulate files 89 directly. 90 +/ 91 struct Defile 92 { 93 public: 94 /++ 95 Must be called before any other methods. 96 97 Calling other methods without first calling initialize should be 98 considered as undefined behavior. The result will almost surely 99 be a crash, since this method loads and initializes the PhysicsFS library. 100 101 Throws: 102 DerelictException if the physfs library fails to load. 103 DefileException if the physfs library fails to initialize. 104 +/ 105 static void initialize() 106 { 107 import core.runtime; 108 109 version(Win64) 110 enum physfsDLL = "physfs-x86_64.dll"; 111 else version(Win32) 112 enum physfsDLL = "physfs-x86.dll"; 113 else 114 enum physfsDLL = ""; 115 116 DerelictPHYSFS.load(physfsDLL); 117 118 if(PHYSFS_init(Runtime.args[ 0 ].toStringz()) == 0) { 119 throw new DefileException("Failed to initialize virtual file system"); 120 } 121 122 _baseDir = to!string(PHYSFS_getBaseDir()); 123 } 124 125 /++ 126 Should be called before the application exits. 127 128 Calling other methods after calling terminate should be considered 129 as undefined behavior. The result could vary between crashes due to 130 access violations and the throwing of DefileExceptions. It is safe to 131 call this even if the initialize method causes an exception to be 132 thrown, so, e.g., wrapping it in scope(exit) is preferable to 133 scope(success). 134 +/ 135 static void terminate() 136 { 137 if(DerelictPHYSFS.isLoaded) { 138 PHYSFS_deinit(); 139 } 140 } 141 142 /++ 143 Creates the application write directory. 144 145 This function only ensures the write directory is created. It does not 146 add it to the search path. Until this function is called, the 147 Defile.writeDir property is invalid. 148 149 Params: 150 organization = The name to be used as the top-level 151 of the app's write directory tree. Should be the name of 152 your group or company. 153 appName = The name of the application. Will be a subdirectory 154 under 'organization' if organization is null, or the 155 app's top-level write directory. 156 Throws: 157 DefileException if the call fails. 158 +/ 159 static void createWriteDir(string organization, string appName) 160 { 161 auto cstr = PHYSFS_getPrefDir(organization.toStringz(), appName.toStringz()); 162 if(!cstr) throw new DefileException("Failed to create application write directory"); 163 164 _writeDir = to!string(cstr); 165 } 166 167 /* 168 Creates the write directory and adds it to the search path, followed by the base 169 directory. 170 171 It is common in games to search for files first in the write directory, then in 172 the base directory. This allows default files that ship with the game to be 173 maintained in the base directory, then overidden on loading by searching in 174 the write directory first. If this configuration does not suit your application, 175 then do not call this function. Instead, call createWriteDir directly and 176 manually configure the search path using the mount function. 177 178 Params: 179 organization = The name to be used as the top-level 180 of the app's write directory tree. Should be the name of 181 your group or company. 182 appName = The name of the application. Will be a subdirectory 183 under 'organization' if organization is null, or the 184 app's top-level write directory. 185 Throws: 186 DefileException if the call fails. 187 */ 188 static void createDefaultSearchPath(string organization, string appName) 189 { 190 createWriteDir(organization, appName); 191 mount(_writeDir); 192 mount(_baseDir); 193 } 194 195 /++ 196 A wrapper for PHYSFS_mkdir. 197 198 Creates a directory on the local file system, including any parent 199 directories in the specified path that do not exist. See the documentation 200 for PHYSFS_mkdir for details. 201 202 Params: 203 dirName = The relative path in the virtual file system of the 204 directory to create. 205 Throws: 206 DefileException if the call fails. 207 +/ 208 static void mkdir(string dirPath) 209 { 210 if(PHYSFS_mkdir(dirPath.toStringz()) == 0) { 211 throw new DefileException("Failed to create directory " ~ dirPath); 212 } 213 } 214 215 /++ 216 A wrapper for PHYSFS_delete. 217 218 Deletes a file or directory from the physical file system. See the 219 documentation for PHYSFS_delete for details. 220 221 Params: 222 path = The relative path in the virtual file system to the 223 file or directory to delete. 224 Throws: 225 DefileException if an error occurs. 226 +/ 227 static void remove(string path) 228 { 229 if(PHYSFS_delete(path.toStringz()) == 0) { 230 throw new DefileException("Failed to delete file/directory " ~ path); 231 } 232 } 233 234 /++ 235 A wrapper for PHYSFS_exists. 236 237 Params: 238 filePath = a fileName or relative file path for which to search 239 in the virtual file system search path. 240 Returns: 241 true if the given path exists anywhere in the PhysicsFS search 242 path and false if it does not. 243 +/ 244 static bool exists(string filePath) 245 { 246 return PHYSFS_exists(filePath.toStringz()) != 0; 247 } 248 249 /++ 250 A weapper for PHYSFS_mount. 251 252 Adds a directory or archive to the virtual file system search path. 253 See the documentation for PHYSFS_mount for details. 254 255 Params: 256 newDir = directory or archive to add to the search path. 257 mountPoint = location in the tree in which to add newDir. null 258 or "" is equivalent to "/". This is the default. 259 action = indicates if the directory should be appended or prepended 260 to the search path. The default is MountAction.append. 261 Throws: 262 DefileException if the call fails. 263 +/ 264 static void mount(string newDir, string mountPoint = "", MountAction action = MountAction.append) 265 { 266 auto mp = mountPoint is null ? null : mountPoint.toStringz(); 267 if(PHYSFS_mount(newDir.toStringz(), mp, action) == 0) { 268 throw new DefileException("Failed to mount " ~ newDir); 269 } 270 } 271 272 /++ 273 A covenience function that reads the entire content of a file in a 274 single method call. 275 276 The method will first open for reading the file specified by filePath 277 and determine its length. Then it will call the Defile.read method of 278 the file instance, which will allocate or expand the provided buffer 279 as necessary. 280 281 Params: 282 filePath = The relative path to the file in the virtual file system. 283 buffer = The buffer in which the content of the file will be 284 stored. The buffer will be allocated if null and 285 expanded if too small. 286 Returns: 287 The number of bytes read. 288 Throws: 289 DefileException if an error occurs. 290 +/ 291 static size_t readFile(string filePath, ref ubyte[] buffer) 292 { 293 auto file = Defile(filePath, OpenFor.read); 294 auto size = file.length; 295 auto ret = file.read(buffer, size, 1); 296 return ret * size; 297 } 298 299 /++ 300 A convenience function that writes an entire buffer to a file in 301 a single method call. 302 303 The method will first open for writing the file specified by filePath, 304 the will call Defile.write to completely write buffer to the file. 305 306 Params: 307 filePath = The relative path to the file in the virtual file system. 308 buffer = The bytes that will be written to the file. 309 Returns: 310 The number of bytes written, which should equal buffer.length. 311 Throws: 312 DefileException if an error occurs. 313 +/ 314 static size_t writeFile(string filePath, ubyte[] buffer) 315 { 316 auto file = Defile(filePath, OpenFor.write); 317 auto ret = file.write(buffer, buffer.length, 1); 318 return ret * buffer.length; 319 } 320 321 /++ 322 A convenience function which creates a path string to a file in a 323 specific directory. 324 325 Sometimes, a file in the write, base or user directories may need to 326 be opened outside of the virtual file system. In those cases, it is 327 necessary to query Defile for the path to the directory of interest 328 and construct the fill file path. This method condenses that into 329 one call. 330 331 Note that this function does not determine if the file exists. It only 332 builds the path. 333 334 Params: 335 which = Specifies which directory will comprise the path. Either 336 PathType.Write or PathType.Base. 337 fileName = The name of the file that will be appended to the path. 338 Returns: 339 A relative path in the virtual file system. 340 +/ 341 static string makeFilePath(PathType which, string fileName) 342 { 343 version(Windows) string fmtString = "%s\\%s"; 344 else string fmtString = "%s/%s"; 345 346 with(PathType) final switch(which) { 347 case write: 348 assert(_writeDir !is null, "Set write dir before using it in makeFilePath"); 349 return format(fmtString, _writeDir, fileName); 350 351 case base: 352 return format(fmtString, _baseDir, fileName); 353 } 354 } 355 356 /++ 357 Searches for a given file in the write and base directories and, if 358 it exists, returns a path to the file. 359 360 This method first looks for the file in the write directory. If it 361 exists, then a string containing the path "writeDir/fileName" is 362 returned. Otherwise, it then looks for the file in the base directory 363 and returns its path if found. If the file exists in neither directory, 364 the method returns null. 365 366 Params: 367 fileName = The name or relative path of a file to look for. 368 Returns: 369 A string containing the relative path to the file in the virtual 370 file system, or null if the file cannot be found. 371 +/ 372 static string findFilePath(string fileName) 373 { 374 auto path = makeFilePath(PathType.write, fileName); 375 if(exists(path)) return path; 376 377 path = makeFilePath(PathType.base, fileName); 378 if(exists(path)) return path; 379 380 return null; 381 } 382 383 /++ 384 A wrapper for PHYSFS_getLastError(). 385 386 Returns: 387 An string describing the last error to occur in a PHYSFS 388 function call. 389 +/ 390 static string lastError() 391 { 392 return to!string(PHYSFS_getLastError()); 393 } 394 395 /++ 396 Returns: 397 The application's base directory, i.e. where the executable lives. 398 +/ 399 static string baseDir() 400 { 401 return _baseDir; 402 } 403 404 /++ 405 Returns: 406 The current write directory. 407 +/ 408 static string writeDir() 409 { 410 assert(_writeDir !is null); 411 return _writeDir; 412 } 413 414 /++ 415 Sets the current write directory. 416 417 All calls to writeFile or Defile.write will be directed to the 418 directory specified here. 419 420 Params: 421 dir = The new write directory. 422 Throws: 423 DefileException if the call fails. 424 +/ 425 static void writeDir(string dir) 426 { 427 auto ret = PHYSFS_setWriteDir(dir.toStringz()); 428 if(ret == 0) { 429 throw new DefileException("Failed to set write directory " ~ dir); 430 } 431 _writeDir = dir; 432 } 433 434 /++ 435 Returns: 436 An array of strings containing each individual path that is 437 on the virtual file system search path. 438 +/ 439 static string[] searchPath() 440 { 441 string[] ret; 442 auto list = PHYSFS_getSearchPath(); 443 for(size_t i = 0; list[ i ]; ++i) { 444 ret ~= to!string(list[ i ]); 445 } 446 PHYSFS_freeList(list); 447 return ret; 448 } 449 450 /++ 451 Opens a file when it is constructed. 452 +/ 453 this(string fileName, OpenFor ofor) 454 { 455 open(fileName, ofor); 456 } 457 458 /++ 459 Closes a file when it goes out of scope. 460 +/ 461 ~this() 462 { 463 close(); 464 } 465 466 /++ 467 A wrapper for PHYSFS_openRead, PHYSFS_openWrite, and PHYSFS_openAppend. 468 469 A file must be opened before any operations can be performed on it. 470 Failure to do so should be considered undefined behavior, but will 471 most likely result in exceptions being thrown. 472 473 See the documentation for PHYSFS_openRead, PHYSFS_openWrite and 474 PHYSFS_openAppend for more details. 475 476 Params: 477 fileName = The relative path to the file in the virtual file system. 478 ofor = The usage for which the file will be opened, one of 479 OpenFor.Read, OpenFor.Write, or OpenFor.Append. 480 Throws: 481 DefileException if the file could not be opened. 482 +/ 483 void open(string fileName, OpenFor ofor) 484 { 485 auto cname = fileName.toStringz(); 486 with(OpenFor) final switch(ofor) { 487 case read: 488 _handle = PHYSFS_openRead(cname); 489 break; 490 491 case write: 492 _handle = PHYSFS_openWrite(cname); 493 break; 494 495 case append: 496 _handle = PHYSFS_openAppend(cname); 497 break; 498 } 499 500 if(!_handle) { 501 throw new DefileException("Failed to open file " ~ fileName); 502 } 503 504 _name = fileName; 505 } 506 507 /++ 508 A wrapper for PHYSFS_close. 509 510 The destructor will close the file automatically, but sometimes it 511 is necessary to do so manaualy. 512 +/ 513 void close() 514 { 515 if(_handle) { 516 PHYSFS_close(_handle); 517 _handle = null; 518 } 519 } 520 521 /++ 522 A wrapper for PHYSFS_flush. 523 524 Flushes the files internal buffer. See the documentation for 525 PHYSFS_flush for details. 526 527 Throws: 528 DefileException if an error occurs. 529 +/ 530 void flush() 531 { 532 if(PHYSFS_flush(_handle) == 0) { 533 throw new DefileException("Failed to flush file " ~ _name); 534 } 535 } 536 537 /++ 538 A wrapper for PHYSFS_seek. 539 540 Seeks from the beginning of the file to the specified position. See 541 the documentation for PHYSFS_seek for details. 542 543 Params: 544 position = The offset from the beginning of the file to move to. 545 Throws: 546 DefileException if an error occurs. 547 +/ 548 void seek(size_t position) 549 { 550 if(PHYSFS_seek(_handle, position) == 0) { 551 throw new DefileException(format("Failed to seek to position %s in file %s", position, _name)); 552 } 553 } 554 555 /++ 556 A wrapper for PHYSFS_tell. 557 558 See the documentation for PHYSFS_tell for details. 559 560 Returns: 561 The current file position. 562 Throws: 563 DefileException if an error occurs. 564 +/ 565 size_t tell() 566 { 567 auto ret = PHYSFS_tell(_handle); 568 if(ret == -1) { 569 throw new DefileException("Failed to determine position in file " ~ _name); 570 } 571 return cast(size_t)ret; 572 } 573 574 /++ 575 A wrapper for PHYSFS_read. 576 577 Reads data from a file. Note that the file must have been opened 578 with the OpenFor.Read flag set. See the documentation for PHYSFS_read 579 for details. 580 581 Params: 582 buffer = The byte buffer which will be used to store the data 583 read from the file. If the buffer is null, it will be 584 allocated. If it is too small to hold objSize * objCount 585 bytes, it will be extended. 586 objSize = The number of bytes to read at a time. 587 objCount = The number of times to read objSize bytes. 588 Returns: 589 The total number of bytes read. Note that this differs from 590 PHYSFS_read, which returns the number of objects read. 591 Throws: 592 DefileException if an error occurs. 593 +/ 594 size_t read(ref ubyte[] buffer, size_t objSize, size_t objCount) 595 { 596 size_t bytesToRead = objSize * objCount; 597 if(buffer.length == 0) { 598 buffer = new ubyte[ bytesToRead ]; 599 } else if(buffer.length < bytesToRead) { 600 buffer.length += bytesToRead; 601 } 602 603 auto ret = PHYSFS_read(_handle, buffer.ptr, cast(uint)objSize, cast(uint)objCount); 604 if(ret == -1) { 605 throw new DefileException("Failed to read from file " ~ _name); 606 } 607 return cast(size_t)ret * objSize; 608 } 609 610 /++ 611 A wrapper for PHYSFS_read. 612 613 Reads data from a file. Note that the file must have been opened 614 with the OpenFor.Read flag set. See the documentation for PHYSFS_read 615 for details. 616 617 Params: 618 ptr = A pointer that will be used to store the objects read 619 = from the file. 620 objSize = The number of bytes to read at a time. 621 objCount = The number of times to read objSize bytes. 622 Returns: 623 The total number of objects read. 624 Throws: 625 DefileException if an error occurs. 626 +/ 627 628 size_t read(void* ptr, size_t objSize, size_t objCount) 629 { 630 auto ret = PHYSFS_read(_handle, ptr, cast(uint)objSize, cast(uint)objCount); 631 if(ret == -1) { 632 throw new DefileException("Failed to read from file " ~ _name); 633 } 634 return cast(size_t)ret; 635 } 636 637 /++ 638 A wrapper for PHYSFS_write. 639 640 Writes data to a file. Note that the file must have been opened 641 with the OpenFor.Write or OpenFor.Append flag set. See the 642 documentation for PHYSFS_write for details. 643 644 Params: 645 buffer = The buffer containing the bytes which will be written 646 to the file. 647 objSize = The number of bytes to write at a time. 648 objCount = The number of times to write objSize bytes. 649 Returns: 650 The total number of bytes written. Note that this differs from 651 PHYSFS_read, which returns the number of objects written. 652 Throws: 653 DefileException if an error occurs. 654 +/ 655 size_t write(const(ubyte)[] buffer, size_t objSize, size_t objCount) 656 { 657 auto ret = PHYSFS_write(_handle, buffer.ptr, cast(uint)objSize, cast(uint)objCount); 658 if(ret == -1) { 659 throw new DefileException("Failed to write to file " ~ _name); 660 } 661 return cast(size_t)ret * objSize; 662 } 663 664 /++ 665 A wrapper for PHYSFS_write. 666 667 Writes data to a file. Note that the file must have been opened 668 with the OpenFor.Write or OpenFor.Append flag set. See the 669 documentation for PHYSFS_write for details. 670 671 Params: 672 ptr = A pointer to the object(s) that will be written to file. 673 objSize = The number of bytes to write at a time. 674 objCount = The number of times to write objSize bytes. 675 Returns: 676 The total number of objects written. 677 Throws: 678 DefileException if an error occurs. 679 +/ 680 size_t write(const(void)* ptr, size_t objSize, size_t objCount) 681 { 682 auto ret = PHYSFS_write(_handle, ptr, cast(uint)objSize, cast(uint)objCount); 683 if(ret == -1) { 684 throw new DefileException("Failed to write to file " ~ _name); 685 } 686 return cast(size_t)ret; 687 } 688 689 /++ 690 A templated wrapper for the PHYSFS_readSLE/ULE* functions. 691 692 This method only accepts values that are of any integral type except 693 byte and ubyte. 694 695 Returns: 696 A value of type T in little endian byte order. 697 Throws: 698 DefileException if an error occurs. 699 700 +/ 701 T readLE(T)() if(isIntegral!T && !is(T == byte) && !is(T == ubyte)) 702 { 703 int ret; 704 T val; 705 706 static if(is(T == short)) { 707 ret = PHYSFS_readSLE16(_handle, &val); 708 } else static if(is(T == ushort)) { 709 ret = PHYSFS_readULE16(_handle, &val); 710 } else static if(is(T == int)) { 711 ret = PHYSFS_readSLE32(_handle, &val); 712 } else static if(is(T == uint)) { 713 ret = PHYSFS_readULE32(_handle, &val); 714 } else static if(is(T == long)) { 715 ret = PHYSFS_readSLE64(_handle, &val); 716 } else static if(is(T == ulong)) { 717 ret = PHYSFS_readULE64(_handle, &val); 718 } else { 719 static assert(0); 720 } 721 722 if(ret == 0) { 723 throw new DefileException(format("Failed to read %s LE value from file %s", 724 T.stringOf, _name)); 725 } 726 return val; 727 } 728 729 /++ 730 A templated wrapper for the PHYSFS_readSBE/UBE* functions. 731 732 This method only accepts values that are of any integral type except 733 byte and ubyte. 734 735 Returns: 736 A value of type T in big endian byte order. 737 Throws: 738 DefileException if an error occurs. 739 740 +/ 741 T readBE(T)() if(isIntegral!T && !is(T == byte) && !is(T == ubyte)) 742 { 743 int ret; 744 T val; 745 746 static if(is(T == short)) { 747 ret = PHYSFS_readSBE16(_handle, &val); 748 } else static if(is(T == ushort)) { 749 ret = PHYSFS_readUBE16(_handle, &val); 750 } else static if(is(T == int)) { 751 ret = PHYSFS_readSBE32(_handle, &val); 752 } else static if(is(T == uint)) { 753 ret = PHYSFS_readUBE32(_handle, &val); 754 } else static if(is(T == long)) { 755 ret = PHYSFS_readSBE64(_handle, &val); 756 } else static if(is(T == ulong)) { 757 ret = PHYSFS_readUBE64(_handle, &val); 758 } else { 759 static assert(0); 760 } 761 762 if(ret == 0) { 763 throw new DefileException(format("Failed to read %s BE value from file %s", 764 T.stringOf, _name)); 765 } 766 return val; 767 } 768 769 /++ 770 A templated wrapper for the PHYSFS_writeSLE/ULE* functions. 771 772 This method only accepts values that are of any integral type except 773 byte and ubyte. 774 775 Params: 776 val = A value of type T which will be written to the file in 777 little endian byte order. 778 Throws: 779 DefileException if an error occurs. 780 781 +/ 782 void writeLE(T)(T val) if(isIntegral!T && !is(T == byte) && !is(T == ubyte)) 783 { 784 int ret; 785 786 static if(is(T == short)) { 787 ret = PHYSFS_writeSLE16(_handle, val); 788 } else static if(is(T == ushort)) { 789 ret = PHYSFS_writeULE16(_handle, val); 790 } else static if(is(T == int)) { 791 ret = PHYSFS_writeSLE32(_handle, val); 792 } else static if(is(T == uint)) { 793 ret = PHYSFS_writeULE32(_handle, val); 794 } else static if(is(T == long)) { 795 ret = PHYSFS_writeSLE64(_handle, val); 796 } else static if(is(T == ulong)) { 797 ret = PHYSFS_writeULE64(_handle, val); 798 } else { 799 static assert(0); 800 } 801 802 if(ret == 0) { 803 throw new DefileException(format("Failed to write %s LE value to file %s", 804 T.stringOf, _name)); 805 } 806 } 807 808 /++ 809 A templated wrapper for the PHYSFS_writeSBE/UBE* functions. 810 811 This method only accepts values that are of any integral type except 812 byte and ubyte. 813 814 Params: 815 val = A value of type T which will be written to the file in 816 big endian byte order. 817 Throws: 818 DefileException if an error occurs. 819 820 +/ 821 void writeBE(T)(T val) if(isIntegral!T && !is(T == byte) && !is(T == ubyte)) 822 { 823 int ret; 824 825 static if(is(T == short)) { 826 ret = PHYSFS_writeSBE16(_handle, val); 827 } else static if(is(T == ushort)) { 828 ret = PHYSFS_writeUBE16(_handle, val); 829 } else static if(is(T == int)) { 830 ret = PHYSFS_writeSBE32(_handle, val); 831 } else static if(is(T == uint)) { 832 ret = PHYSFS_writeUBE32(_handle, val); 833 } else static if(is(T == long)) { 834 ret = PHYSFS_writeSBE64(_handle, val); 835 } else static if(is(T == ulong)) { 836 ret = PHYSFS_writeUBE64(_handle, val); 837 } else { 838 static assert(0); 839 } 840 841 if(ret == 0) { 842 throw new DefileException(format("Failed to write %s BE value to file %s", 843 T.stringOf, _name)); 844 } 845 } 846 /++ 847 A wrapper for PHYSFS_fileLength. 848 849 Returns: 850 The total size, in bytes, of the file. 851 +/ 852 size_t length() 853 { 854 if(!_handle) return 0; 855 856 auto len = PHYSFS_fileLength(_handle); 857 if(len == -1) { 858 throw new DefileException("Invalid length for file " ~ _name); 859 } 860 return cast(size_t)len; 861 } 862 863 /++ 864 A wrapper for PHYSFS_eof. 865 866 Returns: 867 True if the end of file has been reached, false otherwise. 868 +/ 869 bool eof() 870 { 871 if(!_handle) return true; 872 return PHYSFS_eof(_handle) > 0; 873 } 874 875 /++ 876 A wrapper for PHYSFS_setBuffer. 877 878 Sets the size of the files internal buffer. 879 880 Params: 881 size = The new buffer size. 882 Throws: 883 DefileException if an error occurs. 884 +/ 885 void bufferSize(size_t size) 886 { 887 assert(_handle); 888 if(PHYSFS_setBuffer(_handle, size) == 0) { 889 throw new DefileException("Failed to set buffer size for file " ~ _name); 890 } 891 } 892 893 private: 894 static string _baseDir; 895 static string _writeDir; 896 string _name; 897 PHYSFS_File *_handle; 898 }