def LoadTilesetNames(self): """ Loads block 1, the tileset names """ data = struct.unpack_from('32s32s32s32s', self.blocks[0]) self.tileset0 = bytes_to_string(data[0]) self.tileset1 = bytes_to_string(data[1]) self.tileset2 = bytes_to_string(data[2]) self.tileset3 = bytes_to_string(data[3]) if self.tileset0 not in globals.szsData: ret = False if self.tileset0 in globals.Pa0Tilesets: reply = QtWidgets.QMessageBox.question(globals.mainWindow, 'Warning', '"%s" tileset was not found in the level data!\n' \ 'Do you want to load the default one from miyamotoextras?' % self.tileset0, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.Yes: ret = globals.mainWindow.LoadDefaultTileset(self.tileset0, dirty=True) if not ret: self.tileset0 = '' if self.tileset1 not in globals.szsData: self.tileset1 = '' if self.tileset2 not in globals.szsData: self.tileset2 = '' if self.tileset3 not in globals.szsData: self.tileset3 = ''
def LoadTilesetNames(self): """ Loads block 1, the tileset names """ data = struct.unpack_from('32s32s32s32s', self.blocks[0]) self.tileset0 = bytes_to_string(data[0]) self.tileset1 = bytes_to_string(data[1]) self.tileset2 = bytes_to_string(data[2]) self.tileset3 = bytes_to_string(data[3]) if self.tileset0 not in globals.szsData: self.tileset0 = '' if self.tileset1 not in globals.szsData: self.tileset1 = '' if self.tileset2 not in globals.szsData: self.tileset2 = '' if self.tileset3 not in globals.szsData: self.tileset3 = ''
def convFile(f, dest, dest_bom): magic = bytes_to_string(f[:4]) if magic in supp_STM and dest in supp_STM: outputBuffer = STMtoSTM(f, magic, dest, dest_bom) elif magic in supp_WAV and dest in supp_WAV: outputBuffer = WAVtoWAV(f, magic, dest, dest_bom) elif magic in supp_STM and dest in supp_WAV: outputBuffer = STMtoWAV(f, magic, dest, dest_bom) elif magic in supp_WAV and dest in supp_STM: print("\nBFWAV/BCWAV to BFSTM/BCSTM/BFSTP is not implemented!") print("\nExiting in 5 seconds...") time.sleep(5) sys.exit(1) else: print("\nUnsupported file format!") print("\nExiting in 5 seconds...") time.sleep(5) sys.exit(1) return outputBuffer
def LoadTilesetNames(self): data = struct.unpack_from('32s32s32s32s', self.blocks[0]) self.tileset0 = bytes_to_string(data[0]) self.tileset1 = bytes_to_string(data[1]) self.tileset2 = bytes_to_string(data[2]) self.tileset3 = bytes_to_string(data[3])
def readFile(f): pos = 0 if f[4:6] == b'\xFF\xFE': bom = '<' elif f[4:6] == b'\xFE\xFF': bom = '>' else: print("\nInvalid BOM!") print("\nExiting in 5 seconds...") time.sleep(5) sys.exit(1) header = Header(bom) header.data(f, pos) if bytes_to_string(header.magic) not in ["FWAV", "CWAV"]: print("\nUnsupported file format!") print("\nExiting in 5 seconds...") time.sleep(5) sys.exit(1) print("\nMagic: " + bytes_to_string(header.magic)) print("Header size: " + hex(header.size_)) print("File version: " + hex(header.version)) print("File size: " + hex(header.fileSize)) print("Number of blocks: " + str(header.numBlocks)) pos += header.size sized_refs = {} for i in range(1, header.numBlocks + 1): sized_refs[i] = Ref(bom) sized_refs[i].data(f, pos + 12 * (i - 1)) sized_refs[i].block_size = struct.unpack( bom + "I", f[pos + 12 * (i - 1) + 8:pos + 12 * i])[0] if sized_refs[i].offset not in [0, -1]: if sized_refs[i].type_ == 0x7000: print("\nInfo Block offset: " + hex(sized_refs[i].offset)) elif sized_refs[i].type_ == 0x7001: print("\nData Block offset: " + hex(sized_refs[i].offset)) else: print("\n" + hex(sized_refs[i].type_) + " Block offset: " + hex(sized_refs[i].offset)) print("Size: " + hex(sized_refs[i].block_size)) if sized_refs[1].type_ != 0x7000 or sized_refs[1].offset in [0, -1]: print("\nSomething went wrong!\nError code: 5") print("\nExiting in 5 seconds...") time.sleep(5) sys.exit(1) pos = sized_refs[1].offset info = BLKHeader(bom) info.data(f, pos) info.pos = pos print("\nInfo Block Magic: " + bytes_to_string(info.magic)) print("Size: " + hex(info.size_)) pos += info.size wavInfo = WAVInfo(bom) wavInfo.data(f, pos) codec = {0: "PCM8", 1: "PCM16", 2: "DSP ADPCM", 3: "IMA ADPCM"} if wavInfo.codec in codec: print("\nEncoding: " + codec[wavInfo.codec]) else: print("\nEncoding: " + str(wavInfo.codec)) print("Loop Flag: " + str(wavInfo.loop_flag)) print("Sample Rate: " + str(wavInfo.sample)) print("Loop Start Frame: " + str(wavInfo.loop_start)) print("Loop End Frame: " + str(wavInfo.loop_end)) pos += wavInfo.size channelInfoTable = {} ADPCMInfo_ref = {} count = struct.unpack(bom + "I", f[pos:pos + 4])[0] print("Channel Count: " + str(count)) countPos = pos for i in range(1, count + 1): pos = countPos + 4 channelInfoTable[i] = Ref(bom) channelInfoTable[i].data(f, pos + 8 * (i - 1)) if channelInfoTable[i].offset not in [0, -1]: pos = channelInfoTable[i].offset + countPos print("\nChannel " + str(i) + " Info Entry offset: " + hex(pos)) sampleData_ref = Ref(bom) sampleData_ref.data(f, pos) if sampleData_ref.offset not in [0, -1]: for z in range(1, header.numBlocks + 1): if sized_refs[z].offset not in [0, -1]: if sized_refs[z].type_ == 0x7001: print("\nChannel " + str(i) + " Info Entry Sample Data offset: " + hex(sampleData_ref.offset + sized_refs[z].offset + 8)) pos += 8 print("\nChannel " + str(i) + " Info Entry ADPCM Info Reference offset: " + hex(pos)) ADPCMInfo_ref[i] = Ref(bom) ADPCMInfo_ref[i].data(f, pos) if ADPCMInfo_ref[i].offset not in [0, -1]: print("\nADPCM Info offset: " + hex(ADPCMInfo_ref[i].offset + pos - 8)) print("Type: " + hex(ADPCMInfo_ref[i].type_)) pos = ADPCMInfo_ref[i].offset + pos - 8 if ADPCMInfo_ref[i].type_ == 0x0300: param = b'' for i in range(1, 17): param += struct.unpack( bom + "H", f[pos + 2 * (i - 1):pos + 2 * (i - 1) + 2])[0].to_bytes(2, 'big') print("Param: " + str(param)) pos += 32 context = DSPContext(bom) context.data(f, pos) print("Context Predictor and Scale: " + hex(context.predictor_scale)) print("Context Previous Sample: " + hex(context.preSample)) print("Context Second Previous Sample: " + hex(context.preSample2)) pos += context.size loopContext = DSPContext(bom) loopContext.data(f, pos) print("Loop Context Predictor and Scale: " + hex(loopContext.predictor_scale)) print("Loop Context Previous Sample: " + hex(loopContext.preSample)) print("Loop Context Second Previous Sample: " + hex(loopContext.preSample2)) pos += loopContext.size pos += 2 elif ADPCMInfo_ref[i].type_ == 0x0301: context = IMAContext(bom) context.data(f, pos) print("Context Data: " + hex(context.data_)) print("Context Table Index: " + hex(context.tableIndex)) pos += context.size loopContext = IMAContext(bom) loopContext.data(f, pos) print("Loop Context Data: " + hex(loopContext.data_)) print("Loop Context Table Index: " + hex(loopContext.tableIndex)) pos += loopContext.size for i in range(1, header.numBlocks + 1): if sized_refs[i].offset not in [0, -1]: if sized_refs[i].type_ == 0x7001: pos = sized_refs[i].offset data = BLKHeader(bom) data.data(f, pos) print("\nData Block Magic: " + bytes_to_string(data.magic)) print("Size: " + hex(data.size_)) pos += data.size data.data_ = f[pos:pos + data.size_ - 8]
def read(file): with open(file, "rb") as inf: f = inf.read() if f[:4] != b"FRES" or f[4:8] == b' ': QtWidgets.QMessageBox.warning(None, "Error", "Invalid file header!") return False version = f[4] if version not in [3, 4]: QtWidgets.QMessageBox.warning(None, "Error", "Unsupported BFRES version!") return False group = empty() group.pos = struct.unpack(">i", f[0x24:0x28])[0] if group.pos == 0: return False group.pos += 0x28 group.count = struct.unpack(">i", f[group.pos:group.pos + 4])[0] group.pos += 20 textures = [] texNames = [] texSizes = [] for i in range(group.count): nameAddr = struct.unpack( ">i", f[group.pos + 16 * i + 8:group.pos + 16 * i + 12])[0] nameAddr += group.pos + 16 * i + 8 name = bytes_to_string(f, nameAddr) pos = struct.unpack(">i", f[group.pos + 16 * i + 12:group.pos + 16 * i + 16])[0] pos += group.pos + 16 * i + 12 ftex = empty() ftex.headAddr = pos pos += 4 surface = GX2Surface() surface.data(f, pos) pos += surface.size if version == 4: surface.numMips = 1 elif surface.numMips > 14: continue mipOffsets = [] for j in range(13): mipOffsets.append(f[j * 4 + pos] << 24 | f[j * 4 + 1 + pos] << 16 | f[j * 4 + 2 + pos] << 8 | f[j * 4 + 3 + pos]) pos += 68 compSel = [] compSel2 = [] for j in range(4): comp = f[pos + j] compSel2.append(comp) if comp == 4: # Sorry, but this is unsupported. comp = j compSel.append(comp) pos += 24 ftex.name = name ftex.dim = surface.dim ftex.width = surface.width ftex.height = surface.height ftex.depth = surface.depth ftex.numMips = surface.numMips ftex.format = surface.format_ ftex.aa = surface.aa ftex.use = surface.use ftex.imageSize = surface.imageSize ftex.imagePtr = surface.imagePtr ftex.mipSize = surface.mipSize ftex.mipPtr = surface.mipPtr ftex.tileMode = surface.tileMode ftex.swizzle = surface.swizzle ftex.alignment = surface.alignment ftex.pitch = surface.pitch ftex.compSel = compSel ftex.compSel2 = compSel2 ftex.mipOffsets = mipOffsets ftex.surfInfo = addrlib.getSurfaceInfo(ftex.format, ftex.width, ftex.height, ftex.depth, ftex.dim, ftex.tileMode, ftex.aa, 0) if ftex.format in globals.BCn_formats: ftex.blkWidth, ftex.blkHeight = 4, 4 else: ftex.blkWidth, ftex.blkHeight = 1, 1 ftex.bpp = addrlib.surfaceGetBitsPerPixel(surface.format_) // 8 dataAddr = struct.unpack(">i", f[ftex.headAddr + 0xB0:ftex.headAddr + 0xB4])[0] dataAddr += ftex.headAddr + 0xB0 ftex.dataAddr = dataAddr ftex.data = f[dataAddr:dataAddr + ftex.imageSize] mipAddr = struct.unpack(">i", f[ftex.headAddr + 0xB4:ftex.headAddr + 0xB8])[0] if mipAddr and ftex.mipSize: mipAddr += ftex.headAddr + 0xB4 ftex.mipData = f[mipAddr:mipAddr + ftex.mipSize] else: ftex.mipData = b'' textures.append(ftex) texNames.append(name) texSizes.append([ftex.imageSize, ftex.mipSize]) globals.fileData = bytearray(f) globals.texSizes = texSizes return textures, texNames
def readFile(f): pos = 0 if f[4:6] == b'\xFF\xFE': bom = '<' elif f[4:6] == b'\xFE\xFF': bom = '>' else: print("Invalid BOM!") print("\nExiting in 5 seconds...") time.sleep(5) sys.exit(1) header = Header(bom) header.data(f, pos) if bytes_to_string(header.magic) not in ["FSTM", "CSTM", "FSTP"]: print("Unsupported file format!") print("\nExiting in 5 seconds...") time.sleep(5) sys.exit(1) print("Magic: " + bytes_to_string(header.magic)) print("Header size: " + hex(header.size_)) print("File version: " + hex(header.version)) print("File size: " + hex(header.fileSize)) print("Number of blocks: " + str(header.numBlocks)) pos += header.size sized_refs = {} for i in range(1, header.numBlocks + 1): sized_refs[i] = Ref(bom) sized_refs[i].data(f, pos + 12 * (i - 1)) sized_refs[i].block_size = struct.unpack( bom + "I", f[pos + 12 * (i - 1) + 8:pos + 12 * i])[0] if sized_refs[i].offset not in [0, -1]: if sized_refs[i].type_ == 0x4000: print("\nInfo Block offset: " + hex(sized_refs[i].offset)) elif sized_refs[i].type_ == 0x4001: print("\nSeek Block offset: " + hex(sized_refs[i].offset)) elif sized_refs[i].type_ == 0x4002: print("\nData Block offset: " + hex(sized_refs[i].offset)) elif sized_refs[i].type_ == 0x4004: print("\nPrefetch Data Block offset: " + hex(sized_refs[i].offset)) else: print("\n" + hex(sized_refs[i].type_) + " Block offset: " + hex(sized_refs[i].offset)) print("Size: " + hex(sized_refs[i].block_size)) if sized_refs[1].type_ != 0x4000 or sized_refs[1].offset in [0, -1]: print("\nSomething went wrong!\nError code: 1") print("\nExiting in 5 seconds...") time.sleep(5) sys.exit(1) pos = sized_refs[1].offset info = BLKHeader(bom) info.data(f, pos) print("\nInfo Block Magic: " + bytes_to_string(info.magic)) print("Size: " + hex(info.size_)) pos += info.size stmInfo_ref = Ref(bom) stmInfo_ref.data(f, pos) if stmInfo_ref.type_ != 0x4100 or stmInfo_ref.offset in [0, -1]: print("\nSomething went wrong!\nError code: 2") print("\nExiting in 5 seconds...") time.sleep(5) sys.exit(1) print("\nStream Info offset: " + hex(stmInfo_ref.offset + pos)) stmInfo_ref.pos = pos pos += stmInfo_ref.size trkInfoTable_ref = Ref(bom) trkInfoTable_ref.data(f, pos) if trkInfoTable_ref.offset not in [0, -1 ] and trkInfoTable_ref.type_ == 0x0101: print("\nTrack Info Reference Table offset: " + hex(trkInfoTable_ref.offset + stmInfo_ref.pos)) elif not trkInfoTable_ref.type_ or trkInfoTable_ref.offset in [0, -1]: pass else: print("\nSomething went wrong!\nError code: 3") print("\nExiting in 5 seconds...") time.sleep(5) sys.exit(1) pos += trkInfoTable_ref.size channelInfoTable_ref = Ref(bom) channelInfoTable_ref.data(f, pos) if channelInfoTable_ref.type_ != 0x0101: print("\nSomething went wrong!\nError code: 4") print("\nExiting in 5 seconds...") time.sleep(5) sys.exit(1) if channelInfoTable_ref.offset not in [0, -1]: print("\nChannel Info Reference Table offset: " + hex(channelInfoTable_ref.offset + stmInfo_ref.pos)) pos = stmInfo_ref.offset + stmInfo_ref.pos stmInfo = STMInfo(bom) stmInfo.data(f, pos) codec = {0: "PCM8", 1: "PCM16", 2: "DSP ADPCM", 3: "IMA ADPCM"} if stmInfo.codec in codec: print("\nEncoding: " + codec[stmInfo.codec]) else: print("\nEncoding: " + str(stmInfo.codec)) print("Loop Flag: " + str(stmInfo.loop_flag)) print("Channel Count: " + str(stmInfo.count)) print("Sample Rate: " + str(stmInfo.sample)) print("Loop Start Frame: " + str(stmInfo.loop_start)) print("Loop End Frame: " + str(stmInfo.loop_end)) print("Sample Block Count: " + str(stmInfo.sampleBlk_count)) print("Sample Block Size: " + hex(stmInfo.sampleBlk_size)) print("Sample Block Sample Count: " + str(stmInfo.sampleBlk_sampleCount)) print("Last Sample Block Size: " + hex(stmInfo.lSampleBlk_size)) print("Last Sample Block Sample Count: " + str(stmInfo.lSampleBlk_sampleCount)) print("Last Sample Block Padded Size: " + hex(stmInfo.lSampleBlk_padSize)) print("Seek Data Size: " + hex(stmInfo.seek_size)) print("Seek Interval Sample Count: " + str(stmInfo.SISC)) pos += stmInfo.size sampleData_ref = Ref(bom) sampleData_ref.data(f, pos) if sampleData_ref.offset not in [0, -1]: for i in range(1, header.numBlocks + 1): if sized_refs[i].offset not in [0, -1]: if sized_refs[i].type_ == 0x4002: print("Sample Data offset: " + hex(sampleData_ref.offset + sized_refs[i].offset + 8)) if sized_refs[i].type_ == 0x4004: print("Prefetch Data offset: " + hex(sampleData_ref.offset)) pos += 8 trkInfoTable = {} trkInfo = {} if trkInfoTable_ref.offset not in [0, -1]: pos = trkInfoTable_ref.offset + stmInfo_ref.pos count = struct.unpack(bom + "I", f[pos:pos + 4])[0] pos += 4 for i in range(1, count + 1): pos = trkInfoTable_ref.offset + stmInfo_ref.pos + 4 trkInfoTable[i] = Ref(bom) trkInfoTable[i].data(f, pos + 8 * (i - 1)) if trkInfoTable[i].offset not in [0, -1]: print("\nTrack Info Entry " + str(i) + " offset: " + hex(trkInfoTable[i].offset + pos - 4)) pos = trkInfoTable[i].offset + pos - 4 trkInfo[i] = TRKInfo(bom) trkInfo[i].data(f, pos) print("Volume: " + str(trkInfo[i].volume)) print("Pan: " + str(trkInfo[i].pan)) print("Unknown value: " + str(trkInfo[i].unk)) pos += trkInfo[i].size channelIndexByteTable_ref = Ref(bom) channelIndexByteTable_ref.data(f, pos) if channelIndexByteTable_ref.offset not in [0, -1]: print("Channel Index Byte Table offset: " + hex(channelIndexByteTable_ref.offset + pos - trkInfo[i].size)) pos = channelIndexByteTable_ref.offset + pos - trkInfo[ i].size count = struct.unpack(bom + "I", f[pos:pos + 4])[0] pos += 4 elem = f[pos:pos + count] print("Elements: " + str(elem)) channelInfoTable = {} ADPCMInfo_ref = {} pos = channelInfoTable_ref.offset + stmInfo_ref.pos count = struct.unpack(bom + "I", f[pos:pos + 4])[0] pos += 4 for i in range(1, count + 1): pos = channelInfoTable_ref.offset + stmInfo_ref.pos + 4 channelInfoTable[i] = Ref(bom) channelInfoTable[i].data(f, pos + 8 * (i - 1)) if channelInfoTable[i].offset not in [0, -1]: print("\nChannel " + str(i) + " Info Entry ADPCM Info Reference offset: " + hex(channelInfoTable[i].offset + pos - 4)) pos = channelInfoTable[i].offset + pos - 4 ADPCMInfo_ref[i] = Ref(bom) ADPCMInfo_ref[i].data(f, pos) if ADPCMInfo_ref[i].offset not in [0, -1]: print("\nADPCM Info offset: " + hex(ADPCMInfo_ref[i].offset + pos)) print("Type: " + hex(ADPCMInfo_ref[i].type_)) pos = ADPCMInfo_ref[i].offset + pos if ADPCMInfo_ref[i].type_ == 0x0300: param = b'' for i in range(1, 17): param += struct.unpack( bom + "H", f[pos + 2 * (i - 1):pos + 2 * (i - 1) + 2])[0].to_bytes(2, 'big') print("Param: " + str(param)) pos += 32 context = DSPContext(bom) context.data(f, pos) print("Context Predictor and Scale: " + hex(context.predictor_scale)) print("Context Previous Sample: " + hex(context.preSample)) print("Context Second Previous Sample: " + hex(context.preSample2)) pos += context.size loopContext = DSPContext(bom) loopContext.data(f, pos) print("Loop Context Predictor and Scale: " + hex(loopContext.predictor_scale)) print("Loop Context Previous Sample: " + hex(loopContext.preSample)) print("Loop Context Second Previous Sample: " + hex(loopContext.preSample2)) pos += loopContext.size pos += 2 elif ADPCMInfo_ref[i].type_ == 0x0301: context = IMAContext(bom) context.data(f, pos) print("Context Data: " + hex(context.data_)) print("Context Table Index: " + hex(context.tableIndex)) pos += context.size loopContext = IMAContext(bom) loopContext.data(f, pos) print("Loop Context Data: " + hex(loopContext.data_)) print("Loop Context Table Index: " + hex(loopContext.tableIndex)) pos += loopContext.size for i in range(1, header.numBlocks + 1): if sized_refs[i].offset not in [0, -1]: if sized_refs[i].type_ == 0x4001: pos = sized_refs[i].offset seek = BLKHeader(bom) seek.data(f, pos) print('') print("Seek Block Magic: " + bytes_to_string(seek.magic)) print("Size: " + hex(seek.size_)) pos += seek.size seek.data_ = f[pos:pos + seek.size_ - 8] elif sized_refs[i].type_ in [0x4002, 0x4004]: pos = sized_refs[i].offset data = BLKHeader(bom) data.data(f, pos) print('') if sized_refs[i].type_ == 0x4002: print("Data Block Magic: " + bytes_to_string(data.magic)) else: print("Prefetch Data Block Magic: " + bytes_to_string(data.magic)) print("Size: " + hex(data.size_)) pos += data.size data.data_ = f[pos:pos + data.size_ - 8]
def read(file): with open(file, "rb") as inf: f = inf.read() pos = 0 if f[0xc:0xe] == b'\xFF\xFE': bom = '<' elif f[0xc:0xe] == b'\xFE\xFF': bom = '>' else: QtWidgets.QMessageBox.warning(None, "Error", "Invalid BOM!") return False header = BNTXHeader(bom) header.data(f, pos) pos += header.size if header.magic != b'BNTX\0\0\0\0': QtWidgets.QMessageBox.warning(None, "Error", "Invalid file header!") return False fnameLen = struct.unpack(bom + 'H', f[header.fileNameAddr - 2:header.fileNameAddr])[0] fname = bytes_to_string( f[header.fileNameAddr:header.fileNameAddr + fnameLen], fnameLen) texContainer = TexContainer(bom) texContainer.data(f, pos) pos += texContainer.size if texContainer.target not in [b'NX ', b'Gen ']: QtWidgets.QMessageBox.warning(None, "Error", "Unsupported target platform!") return False target = 0 if texContainer.target == b'Gen ' else 1 textures = [] texNames = [] texSizes = [] for i in range(texContainer.count): pos = struct.unpack( bom + 'q', f[texContainer.infoPtrsAddr + i * 8:texContainer.infoPtrsAddr + i * 8 + 8])[0] infoHeader = BlockHeader(bom) infoHeader.data(f, pos) pos += infoHeader.size info = TextureInfo(bom) info.data(f, pos) if infoHeader.magic != b'BRTI': continue nameLen = struct.unpack(bom + 'H', f[info.nameAddr:info.nameAddr + 2])[0] name = bytes_to_string( f[info.nameAddr + 2:info.nameAddr + 2 + nameLen], nameLen) compSel = [] compSel2 = [] for i in range(4): value = (info.compSel >> (8 * (3 - i))) & 0xff compSel2.append(value) if value == 0: value = 5 - len(compSel) compSel.append(value) if info.type_ not in globals.types: globals.types[info.type_] = "Unknown" dataAddr = struct.unpack(bom + 'q', f[info.ptrsAddr:info.ptrsAddr + 8])[0] mipOffsets = {0: 0} for i in range(1, info.numMips): mipOffset = struct.unpack( bom + 'q', f[info.ptrsAddr + (i * 8):info.ptrsAddr + (i * 8) + 8])[0] mipOffsets[i] = mipOffset - dataAddr tex = TexInfo() tex.infoAddr = pos tex.info = info tex.bom = bom tex.target = target tex.name = name tex.readTexLayout = info.flags & 1 tex.sparseBinding = info.flags >> 1 tex.sparseResidency = info.flags >> 2 tex.dim = info.dim tex.tileMode = info.tileMode tex.numMips = info.numMips tex.width = info.width tex.height = info.height tex.format = info.format_ tex.arrayLength = info.arrayLength tex.blockHeightLog2 = info.textureLayout & 7 tex.imageSize = info.imageSize tex.compSel = compSel tex.compSel2 = compSel2 tex.alignment = info.alignment tex.type = info.type_ tex.mipOffsets = mipOffsets tex.dataAddr = dataAddr tex.data = f[dataAddr:dataAddr + info.imageSize] textures.append(tex) texNames.append(name) texSizes.append(info.imageSize) globals.fileData = bytearray(f) globals.texSizes = texSizes return fname, texContainer.target.decode('utf-8'), textures, texNames
def _load(self, data): # SARC Header ----------------------------------------- # File magic (0x00 - 0x03) if not data.startswith(b'SARC'): return 1 # Come back to header length later, when we have endianness # Endianness/BOM (0x06 - 0x07) bom = bytes(data[0x06:0x08]) endians = {b'\xFE\xFF': '>', b'\xFF\xFE': '<'} if bom not in endians: return 2 self.endianness = endians[bom] # Header length (0x04 - 0x05) headLen = struct.unpack(self.endianness + 'H', data[0x04:0x06])[0] if headLen != 0x14: return 3 # File Length (0x08 - 0x0B) filelen = struct.unpack(self.endianness + 'I', data[0x08:0x0C])[0] if len(data) != filelen: return 4 # Beginning Of Data offset (0x0C - 0x0F) begOfDat = struct.unpack(self.endianness + 'I', data[0x0C:0x10])[0] # SFAT Header ----------------------------------------- # Sanity check (0x14 - 0x17) if data[0x14:0x18] != b'SFAT': return 5 # Header length (0x18 - 0x19) headLen = struct.unpack(self.endianness + 'H', data[0x18:0x1A])[0] if headLen != 0x0C: return 6 # Node count (0x1A - 0x1C) nodeCount = struct.unpack(self.endianness + 'H', data[0x1A:0x1C])[0] # Hash multiplier (0x1D - 0x1F) self.hashMultiplier = struct.unpack(self.endianness + 'I', data[0x1C:0x20])[0] # SFAT Nodes (0x20 - 0x20+(0x10*nodeCount)) SFATNodes = [] SFATNodeOffset = 0x20 for nodeNum in range(nodeCount): if self.endianness == '>': unkFlagOffset = SFATNodeOffset + 4 fileNameTableEntryOffsetOffset = SFATNodeOffset + 5 else: unkFlagOffset = SFATNodeOffset + 7 fileNameTableEntryOffsetOffset = SFATNodeOffset + 4 # Unknown flag: Could function as a file/folder flag. unkFlag = data[unkFlagOffset] # File Name Table Entry offset if self.endianness == '>': fileNameTableEntryOffsetData = (b'\x00' + data[fileNameTableEntryOffsetOffset: fileNameTableEntryOffsetOffset + 3]) else: fileNameTableEntryOffsetData = \ data[fileNameTableEntryOffsetOffset: fileNameTableEntryOffsetOffset + 3] + b'\x00' fileNameTableEntryOffset = struct.unpack( self.endianness + 'I', fileNameTableEntryOffsetData)[0] # Beginning of Node File Data fileDataStart = struct.unpack( self.endianness + 'I', data[SFATNodeOffset + 8:SFATNodeOffset + 0x0C])[0] # End of Node File Data fileDataEnd = struct.unpack( self.endianness + 'I', data[SFATNodeOffset + 0x0C:SFATNodeOffset + 0x10])[0] # Calculate file data length fileDataLength = fileDataEnd - fileDataStart # Add an entry to the node list SFATNodes.append( (unkFlag, fileNameTableEntryOffset, fileDataStart, fileDataLength)) # Increment the offset counter SFATNodeOffset += 0x10 # SFNT Header ----------------------------------------- # From now on we need to keep track of an offset variable offset = 0x20 + (0x10 * nodeCount) # Sanity check (offset - offset+0x03) if data[offset:offset + 0x04] != b'SFNT': return 7 # Header length (offset+0x04 - offset+0x05) headLen = struct.unpack(self.endianness + 'H', data[offset + 0x04:offset + 0x06])[0] if headLen != 0x08: return 8 # Increment the offset offset += 0x08 # Add the files to the self.contents set -------------- self.clear() for unkFlag, fileNameTableEntryOffset, fileDataStart, fileDataLength in SFATNodes: # Get the file name (search for the first null byte manually) nameOffset = offset + (fileNameTableEntryOffset * 4) name = bytes_to_string(data, nameOffset) # Get the file data fileData = data[begOfDat + fileDataStart: begOfDat + fileDataStart + fileDataLength] # Split it into its folders folderStructure = name.split('/') # Handle it differently if the file is not in a folder if len(folderStructure) == 1: self.contents.add(File(name, fileData)) else: # Get the first folder, or make one if needed folderName = folderStructure[0] for foundFolder in self.contents: if not isinstance(foundFolder, Folder): continue if foundFolder.name == folderName: break else: foundFolder = Folder(folderName) self.contents.add(foundFolder) # Now find/make the rest of them outerFolder = foundFolder for folderName in folderStructure[1:-1]: for foundFolder in outerFolder.contents: if not isinstance(foundFolder, Folder): continue if foundFolder.name == folderName: break else: foundFolder = Folder(folderName) outerFolder.addFolder(foundFolder) outerFolder = foundFolder # Now make a new file and add it to self.contents outerFolder.addFile(File(folderStructure[-1], fileData)) # We're done! Return True so no exception will be thrown. return True