1 module zgrf.filetable.version2xx; 2 3 import core.stdc.stdio : SEEK_SET, SEEK_END; 4 import std.system : Endian; 5 import std.zlib : crc32; 6 import std.uni : toLower; 7 import std.bitmanip : peek, read; 8 9 import zgrf.constants; 10 import zgrf.types; 11 import zgrf.filetable.common; 12 13 /** 14 * Fills the provided GRFFiletable files with [GRFFile] of the input grf. 15 * 16 * Params: 17 * grf = The grf to read the files from 18 * files = The target filetable to store the [GRFFile] to 19 * filters = The filters to check for when reading the files 20 */ 21 void fill(ref GRF grf, ref GRFFiletable files, const(wstring)[] filters = []) 22 in (grf.filehandle.isOpen(), "Filehandle must be open to read the filetable") 23 in (grf.header.grfVersion >= 0x200, "Minimum GRF version allowed for this filetable is 0x200") 24 { 25 grf.filehandle.seek(grf.header.filetableOffset, SEEK_SET); 26 27 ubyte[] filetableSizesBuf = new ubyte[uint.sizeof * 2]; 28 grf.filehandle.rawRead(filetableSizesBuf); 29 30 const compressed_size = filetableSizesBuf.read!(uint, Endian.littleEndian); 31 const uncompressed_size = filetableSizesBuf.read!(uint, Endian.littleEndian); 32 33 ubyte[] zbuffer = new ubyte[compressed_size]; 34 35 grf.filehandle.rawRead(zbuffer); 36 37 ulong offset = 0; 38 39 import zgrf.compression : uncompress; 40 41 ubyte[] buffer = uncompress(zbuffer, uncompressed_size); 42 assert(buffer.length == uncompressed_size); 43 44 if (filters.length > 0) 45 { 46 foreach (i; 0 .. grf.header.filecount) 47 { 48 GRFFile file = extractFile(buffer, offset, grf.header.grfVersion); 49 file.offset_ft += grf.header.filetableOffset + HEADER_LEN; 50 file.grf = &grf; 51 if (inFilter(file, filters) && !isDirectory(file)) 52 { 53 file.hash = crc32(0, file.name.toLower); 54 files.require(file.hash, file); 55 } 56 } 57 } 58 else 59 { 60 foreach (i; 0 .. grf.header.filecount) 61 { 62 GRFFile file = extractFile(buffer, offset, grf.header.grfVersion); 63 if (!isDirectory(file)) 64 { 65 file.offset_ft += grf.header.filetableOffset + HEADER_LEN; 66 file.grf = &grf; 67 file.hash = crc32(0, file.name.toLower); 68 files.require(file.hash, file); 69 } 70 } 71 } 72 } 73 74 /// ditto 75 void fill(ref GRF grf, const(wstring)[] filters = []) 76 in (grf.filehandle.isOpen(), "Filehandle must be open to read the filetable") 77 in (grf.header.grfVersion >= 0x200, "Minimum GRF version allowed for this filetable is 0x200") 78 { 79 fill(grf, grf.files, filters); 80 } 81 82 private GRFFile extractFile(ref ubyte[] buffer, ref ulong offset, uint /*grfVersion*/ ) pure 83 { 84 85 GRFFile file; 86 87 file.offset_ft = offset; 88 89 import core.stdc.string : strlen; 90 91 auto filenameLength = strlen(cast(char*)(buffer.ptr + offset)) + 1; 92 import std.conv : to; 93 94 assert(filenameLength <= FILENAME_LENGTH, 95 "Filename too long (" ~ filenameLength.to!string ~ ") at offset " ~ offset.to!string); 96 97 file.rawName = buffer[offset .. (offset + filenameLength)].dup; 98 99 offset += filenameLength; 100 101 file.compressed_size = buffer.peek!(uint, Endian.littleEndian)(&offset); 102 file.compressed_size_padded = buffer.peek!(uint, Endian.littleEndian)(&offset); 103 file.size = buffer.peek!(uint, Endian.littleEndian)(&offset); 104 file.flags = cast(FileFlags) buffer.peek!ubyte(&offset); 105 file.offset = buffer.peek!(uint, Endian.littleEndian)(&offset); 106 import zencoding.windows949 : fromWindows949; 107 108 file.name = fromWindows949(file.rawName); 109 110 return file; 111 }