def save(self, writer=None): if writer is None: writer = BinaryIO() start = writer.tell() writer.write(self.magic) sizeofs = writer.tell() writer.writeUInt32(0) writer.writeUInt16(self.num) writer.writeUInt16(0) for entry in self.entries: writer.writeUInt32(entry.start) writer.writeUInt32(entry.stop) size = writer.tell() - start with writer.seek(sizeofs): writer.writeUInt32(size) return writer
def save(self, writer=None): if writer is None: writer = BinaryIO() start = writer.tell() writer.write(self.magic) writer.writeUInt16(self.endian) writer.writeUInt16(self.version) sizeofs = writer.tell() writer.writeUInt32(0) headersizeofs = writer.tell() writer.writeUInt16(0) writer.writeUInt16(self.numblocks) size = writer.tell() - start with writer.seek(headersizeofs): writer.writeUInt16(size) writer.writeUInt32(writer.tell() + 4 - start) # block[0] ofs writer = self.tex.save(writer) size = writer.tell() - start with writer.seek(sizeofs): writer.writeUInt32(size) return writer
def save(self, writer=None): writer = BinaryIO() # FIXME blocks = {} num = 0 for name in self.files: match = re.match( '^(?P<block>[0-9]+c?)_' '(?P<idx>[0-9]{1,5})' '(?P<flags>[A-O]*c)?' '(?:\\[(?P<key>[0-9A-F]{1,4})\\])?$', name) if not match: raise ValueError( '{0} is not a valid identifier+options'.format(name)) block_name = match.group('block') if block_name not in blocks: blocks[block_name] = {} idx = int(match.group('idx')) num = max(idx, num) flags = match.group('flags') try: key = int(match.group('key'), 16) except: key = 0 blocks[block_name][idx] = (flags, key, self.files[name]) if self.version in game.GEN_IV: if set(blocks.keys()) | {'0c', '0'} != {'0c', '0'}: raise ValueError('Gen IV cannot have any blocks other than' ' 0 (and 0c). Got: {0}'.format(blocks.keys())) blocks.pop('0c', None) # TODO: handle comment blocks self.numblocks = 1 else: self.numblocks = len(blocks) # base_offset = self.size() # if self.version > game.GEN_IV: # base_offset += 4*self.numblocks # base_offset += TableEntry.instance(self.version).size()*self.numblocks self.num = num + 1 start = writer.tell() writer = AtomicStruct.save(self, writer) text_writer = BinaryIO() text_offs = writer.tell() + 8 * self.num prev_text_pos = 0 if self.version in game.GEN_IV: for i, block_name in enumerate(blocks): # if self.version > game.GEN_IV: # text_offs += 4*self.numblocks+text_writer.tell()-prev_text_pos # prev_text_pos = text_writer.tell() for j in xrange(self.num): try: flags, key, text = blocks[block_name][j] except KeyError: flags = key = None text = '' string = [] cidx = 0 while cidx < len(text): char = text[cidx] cidx += 1 if char == '\\': char = text[cidx] cidx += 1 if char == 'x': # n = int(text[cidx:cidx+2], 16) n = rtable['\\x' + text[cidx:cidx + 2]] cidx += 2 elif char == 'n': n = 0xE000 elif char == 'r': n = 0x25BC elif char == 'f': n = 0x25BD elif char == 'u': n = rtable['\\u' + text[cidx:cidx + 4]] cidx += 4 elif char == '?': n = int(text[cidx:cidx + 4], 16) cidx += 4 else: n = 1 string.append(n) elif char == '\n': string.append(0xE000) elif char == '\r': string.append(0x25BC) elif char == '\f': string.append(0x25BD) elif char == 'V' and text[cidx:cidx + 3] == 'AR(': eov = text.find(')', cidx + 3) if eov == -1: raise RuntimeError( 'Could not find end of VAR()') args = [] for arg in text[cidx + 3:eov].split(','): args.append(int(arg.strip(), 0)) cidx = eov + 1 string.append(0xFFFE) string.append(args.pop(0)) string.append(len(args)) string.extend(args) else: string.append(rtable[char]) if flags and 'c' in flags: string = compress(string, 15) string.append(0xFFFF) size = len(string) text_writer.writeAlign(4) state = (((self.seed * 0x2FD) & 0xFFFF) * (j + 1)) & 0xFFFF key = state | state << 16 writer.writeUInt32(key ^ (text_offs + text_writer.tell())) writer.writeUInt32(key ^ size) key = (TEXT_KEY4_INIT * (j + 1)) & 0xFFFF for char in string: text_writer.writeUInt16(char ^ key) key = (key + TEXT_KEY4_STEP) & 0xFFFF # TODO: comments writer.write(text_writer.getvalue()) else: block = Editable() block.uint32('size') block.array('entries', TableEntry(self.version).base_struct, length=self.num) block.freeze() block_offset_pos = writer.tell() for i in xrange(self.numblocks): writer.writeUInt32(0) for i, block_name in enumerate(blocks): text_writer = BinaryIO() block.save(text_writer) for j, entry in enumerate(block.entries): entry.offset = text_writer.tell() try: flags, key, text = blocks[block_name][j] except KeyError: flags = key = None text = '' string = [] cidx = 0 while cidx < len(text): char = text[cidx] cidx += 1 if char == '\\': char = text[cidx] cidx += 1 if char == 'x': n = int(text[cidx:cidx + 2], 16) cidx += 2 elif char == 'u' or char == '?': n = int(text[cidx:cidx + 4], 16) cidx += 4 elif char == 'n': n = 0xFFFE elif char == 'r': string.append(0xF000) string.append(0xBE01) string.append(0) continue elif char == 'f': string.append(0xF000) string.append(0xBE00) string.append(0) continue else: n = 1 string.append(n) elif char == '\n': string.append(0xFFFE) elif char == '\r': string.append(0xF000) string.append(0xBE01) string.append(0) elif char == '\f': string.append(0xF000) string.append(0xBE00) string.append(0) elif char == 'V' and text[cidx:cidx + 3] == 'AR(': eov = text.find(')', cidx + 3) if eov == -1: raise RuntimeError( 'Could not find end of VAR()') args = [] for arg in text[cidx + 3:eov].split(','): args.append(int(arg.strip(), 0)) cidx = eov + 1 string.append(0xF000) string.append(args.pop(0)) string.append(len(args)) string.extend(args) else: string.append(ord(char)) flag = 0 if flags: for shift in range(16): if chr(65 + shift) in flags: flag |= 1 << shift if 'c' in flags: string = compress(string, 16) if not key: key = 0 string.append(0xFFFF) encchars = [] while string: char = string.pop() ^ key key = ((key >> 3) | (key << 13)) & 0xFFFF encchars.insert(0, char) entry.charcount = len(encchars) entry.flags = flag for char in encchars: text_writer.writeUInt16(char) text_writer.writeAlign(4) block.size = text_writer.tell() with text_writer.seek(0): block.save(text_writer) block_offset = writer.tell() - start with writer.seek(block_offset_pos + 4 * i): writer.writeUInt32(block_offset) writer.write(text_writer.getvalue()) self.filesize = writer.tell() - start with writer.seek(start): AtomicStruct.save(self, writer) return writer