def write_to(self, io): """Writes ERF to file. :param io: A file path. """ out = io io, path = tempfile.mkstemp() fnlen = Erf.filename_length(self.fversion) lstr_iter = iter(sorted(self.localized_strings.items())) locstr = [] for k, v in lstr_iter: locstr.append(struct.pack("<L L %ds x" % len(v), k, len(v) + 1, v.encode(get_encoding()))) locstr = b''.join(locstr) keylist = [] for i, co in enumerate(self.content): pad = 0 if len(co.resref) < fnlen: pad = fnlen - len(co.resref) keylist.append(struct.pack("<%ds %dx L h h" % (len(co.resref), pad), co.resref.encode(get_encoding()), i, co.res_type, 0)) keylist = b''.join(keylist) offset = 160 + len(locstr) + len(keylist) + 8 * len(self.content) reslist = [] for co in self.content: reslist.append(struct.pack("< L L", offset, co.size)) offset += co.size reslist = b''.join(reslist) offset_to_locstr = 160 offset_to_keylist = offset_to_locstr + len(locstr) offset_to_resourcelist = offset_to_keylist + len(keylist) header = struct.pack("8s LL LL LL LL L 116x", (self.ftype + ' ' + self.fversion).encode(get_encoding()), len(self.localized_strings), len(locstr), len(self.content), offset_to_locstr, offset_to_keylist, offset_to_resourcelist, self.year, self.day_of_year, self.desc_strref) os.write(io, header) os.write(io, locstr) os.write(io, keylist) os.write(io, reslist) for co in self.content: os.write(io, co.get()) os.close(io) shutil.copy(path, out) os.remove(path)
def write(self, io): header = struct.pack("4s 4s I I I", self.ftype.encode('ascii'), self.fvers.encode('ascii'), self.lang, len(self), self.HEADER_SIZE + len(self) * self.DATA_ELEMENT_SIZE) io.write(header) offset = 0 strings = [] for i in range(len(self)): n = self[i] entries = struct.pack("I 16s I I I I f", 0x1 if len(n) else 0, b"", 0, 0, offset if len(n) else 0, len(n), 0) io.write(entries) offset += len(n) strings.append(n) io.write(bytearray(''.join(strings), get_encoding()))
def write_tlk(self, io, lang): header = struct.pack("4s 4s I I I", b'TLK ', b'V3.0', lang, len(self), Tlk.HEADER_SIZE + len(self) * Tlk.DATA_ELEMENT_SIZE) io.write(header) offset = 0 strings = [] # Always inject Bad Strref if not self[0]: self[0] = 'Bad Strref' for i in range(len(self)): n = self[i] entries = struct.pack("I 16s I I I I f", 0x1 if len(n) else 0, b"", 0, 0, offset if len(n) else 0, len(n), 0) io.write(entries) if len(n): offset += len(n) strings.append(n) io.write(bytearray(''.join(strings), get_encoding()))
def unpack(source, offset): """Parses a gff cexostring.""" source.seek(offset) length = struct.unpack('I', source.read(4))[0] pattern = "%ds" % length data = struct.unpack(pattern, source.read(length))[0].decode(get_encoding()) return NWString(data)
def unpack(source, offset): """Parses a gff resref.""" # identify the current position, read and parse the resref, and return to the current position source.seek(offset) length = struct.unpack('B', source.read(1))[0] if length == 0: val = '' else: pattern = "%ds" % length val = struct.unpack(pattern, source.read(length))[0].decode(get_encoding()) return NWResref(val)
def __init__(self, source): if isinstance(source, str): source = ContentObject.from_file(source) elif not isinstance(source, ContentObject): raise ValueError("Unsupported source type %s!" % type(source)) self.columns = [] self.rows = [] self.max = None self.newline = "\n" self.default = None self.co = source data = source.get() if not isinstance(data, str): data = data.decode(get_encoding()) self.parse(data)
def write(self, io): header = struct.pack( "4s 4s I I I", self.ftype.encode('ascii'), self.fvers.encode('ascii'), self.lang, len(self), self.HEADER_SIZE + len(self) * self.DATA_ELEMENT_SIZE) io.write(header) offset = 0 strings = [] for i in range(len(self)): n = self[i] entries = struct.pack("I 16s I I I I f", 0x1 if len(n) else 0, b"", 0, 0, offset if len(n) else 0, len(n), 0) io.write(entries) offset += len(n) strings.append(n) io.write(bytearray(''.join(strings), get_encoding()))
def __getitem__(self, i): """Get a TLK element. Tlk supports integer indices and Python slices. Please note that taking a huge slice say a reverse (tlk[::-1] can be a very costly. """ if isinstance(i, slice): indices = i.indices(len(self)) n = Tlk(None) n.ftype = self.ftype n.fvers = self.fvers n.lang = self.lang n.cache = {} for i in range(*indices): n.add(**self[i]) return n if i == 0xffffffff or i >= len(self) or i < 0: return "" elif i in self.entries: return self.entries[i] elif self.io is not None: seek_to = self.HEADER_SIZE + i * self.DATA_ELEMENT_SIZE self.io.seek(seek_to) data = self.io.read(self.DATA_ELEMENT_SIZE) temp = struct.unpack("I 16s I I I I f", data) flags, sound_resref, v_variance, p_variance, offset, size, sound_length = temp sound_resref = sound_resref self.io.seek(self.str_offset + offset) text = self.io.read(size) try: text = text.decode(get_encoding()) if flags & 0x1 > 0 else "" except UnicodeDecodeError: print("Encoding Error: Unable to read entry %d" % i) text = '' return text return ""
def unpack(source, offset): """Parses a gff cexolocstring.""" # identify the current position, read and parse the cexolocstring, and return to the current position position = source.tell() source.seek(offset) length, stringref, count = struct.unpack('IiI', source.read(12)) result = [] if count > 0: for substring in range(0, count): id, length = struct.unpack('2I', source.read(8)) pattern = "%ds" % length data = struct.unpack(pattern, source.read( struct.calcsize(pattern)))[0].decode( get_encoding()) result.append([id, data]) source.seek(position) return NWLocalizedString(stringref, result)
def write_tlk(self, io, lang): header = struct.pack( "4s 4s I I I", b'TLK ', b'V3.0', lang, len(self), Tlk.HEADER_SIZE + len(self) * Tlk.DATA_ELEMENT_SIZE) io.write(header) offset = 0 strings = [] # Always inject Bad Strref if not self[0]: self[0] = 'Bad Strref' for i in range(len(self)): n = self[i] entries = struct.pack("I 16s I I I I f", 0x1 if len(n) else 0, b"", 0, 0, offset if len(n) else 0, len(n), 0) io.write(entries) if len(n): offset += len(n) strings.append(n) io.write(bytearray(''.join(strings), get_encoding()))
def __init__(self, fname, data_path): super(Key, self).__init__() self.root = data_path self.bif = [] with open(fname, 'rb') as io: header = io.read(8 + (4 * 6) + 32) hs = struct.unpack("<4s 4s LLLLLL 32s", header) self.ftype = hs[0] self.fvers = hs[1] bif_count = hs[2] key_count = hs[3] offset_to_file_table = hs[4] offset_to_key_table = hs[5] self.year = hs[6] self.day_of_year = hs[7] reserved = hs[8] io.seek(offset_to_file_table) data = io.read(12 * bif_count) self.file_table = [] for c in chunks(data, 12): if len(c) != 12: break size, name_offset, name_size, drives = struct.unpack("IIhh", c) io.seek(name_offset) name = io.read(name_size) name = struct.unpack("%ds" % name_size, name)[0] name = name.decode(get_encoding()) name = name.rstrip(' \t\r\n\0') name = os.path.join(self.root, name.replace('\\', os.sep)) name = os.path.abspath(name) self.bif.append(Bif(self, name)) self.file_table.append((size, name, drives)) self.key_table = {} io.seek(offset_to_key_table) data = io.read(22 * key_count) for c in chunks(data, 22): if len(c) != 22: break resref, res_type, res_id = struct.unpack("<16s hL", c) resref = resref.decode(get_encoding()) self.key_table[res_id] = (resref.rstrip(' \t\r\n\0'), res_type) self.fn_to_co = {} for res_id, (resref, res_type) in self.key_table.items(): bif_idx = res_id >> 20 bif = self.bif[bif_idx] res_id &= 0xfffff # print res_id, resref, res_type, bif_idx if res_id not in bif.contained: msg = "%s does not have %d" % (bif.io.name, res_id) raise ValueError(msg) ofs, sz, _rt = bif.contained[res_id] o = res.ContentObject(resref, res_type, bif.io, ofs, sz) fn = o.get_filename() if fn in self.fn_to_co and self.fn_to_co[fn][2] < bif_idx: oo, biff, unused = self.fn_to_co[fn] print("%s, in %s shadowed by file in %s" % (fn, biff.io, biff.io)) self.content.remove(oo) self.fn_to_co[fn] = (o, bif, bif_idx) self.add(o)
def unpack(source, offset): """Parses a gff cexolocstring.""" # identify the current position, read and parse the cexolocstring, and return to the current position position = source.tell() source.seek(offset) length, stringref, count = struct.unpack('IiI', source.read(12)) result = [] if count > 0: for substring in range(0, count): id, length = struct.unpack('2I', source.read(8)) pattern = "%ds" % length data = struct.unpack(pattern, source.read(struct.calcsize(pattern)))[0].decode(get_encoding()) result.append([id, data]) source.seek(position) return NWLocalizedString(stringref, result)
def from_file(fname): """Create an Erf from a file handle. :param fname: File name. """ new_erf = None with open(fname, 'rb') as io: header = io.read(160) hs = struct.unpack("< 4s 4s LL LL LL LL L 116s", header) ftype = hs[0].decode(get_encoding()).strip() if ftype not in Erf.TYPES: raise ValueError("Invalid file type!") fvers = hs[1].decode(get_encoding()) fname_len = Erf.filename_length(fvers) new_erf = Erf(ftype, fvers) new_erf.io = fname lstr_count = hs[2] lstr_size = hs[3] entry_count = hs[4] offset_to_lstr = hs[5] offset_to_keys = hs[6] offset_to_res = hs[7] new_erf.year = hs[8] new_erf.day_of_year = hs[9] new_erf.desc_strref = hs[10] io.seek(offset_to_lstr) lstr = io.read(lstr_size) for ls in range(lstr_count): if len(lstr) == 0: print("locstr table: not enough entries (expected: %d, got: %d)" % (lstr_count, ls), file=sys.stderr) break if len(lstr) < 8: print("locstr table: not enough entries (expected: %d, got: %d) partial data: %s" % ( lstr_count, ls, lstr), file=sys.stderr) break lid, strsz = struct.unpack("<L L", lstr[:8]) if strsz > len(lstr) - 8: strsz = len(lstr) - 8 # Necessary for hacking around the fact that erf.exe adds an extra null # to the end of the description string. try: str = struct.unpack("8x %ds" % strsz, lstr)[0].decode(get_encoding()) # except struct.error: str = struct.unpack("8x %ds" % (strsz + 1,), lstr)[0].decode(get_encoding()) # new_erf.localized_strings[lid] = str.rstrip(' \t\r\n\0') lstr = lstr[8 + len(str):] keylist_entry_size = fname_len + 4 + 2 + 2 io.seek(offset_to_keys) keylist = io.read(keylist_entry_size * entry_count) fmt = "%ds I h h" % fname_len fmt = fmt * entry_count fmt = '<' + fmt keylist = struct.unpack(fmt, keylist) for resref, res_id, res_type, unused in chunks(keylist, 4): co = res.ContentObject(resref.decode(get_encoding()).rstrip(' \t\r\n\0'), res_type, fname) new_erf.add(co) resourcelist_entry_size = 4 + 4 io.seek(offset_to_res) resourcelist = io.read(resourcelist_entry_size * entry_count) resourcelist = struct.unpack("I I" * entry_count, resourcelist) _index = -1 for offset, size in chunks(resourcelist, 2): _index += 1 try: co = new_erf.content[_index] co.offset = offset co.size = size except IndexError: print("WARNING: Attempt to index invalid content object in '%s' at offset %X" % (fname, offset), file=sys.stderr) return new_erf