Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import os, sys, struct
- def hexstr(data, length): #Convert input into hex string
- return hex(data).lstrip("0x").rstrip("L").zfill(length).upper()
- def binr(byte):
- return bin(byte).lstrip("0b").zfill(8)
- def uint8(data, pos):
- return struct.unpack(">B", data[pos:pos + 1])[0]
- def uint16(data, pos):
- return struct.unpack(">H", data[pos:pos + 2])[0]
- def uint24(data, pos):
- return struct.unpack(">I", "\00" + data[pos:pos + 3])[0] #HAX
- def uint32(data, pos):
- return struct.unpack(">I", data[pos:pos + 4])[0]
- def check(length, size, percent):
- length = float(length);size = float(size)
- test = round(length / size, 2) #Percent complete as decimal
- test = test * 100 #Percent
- if test % 5 == 0: #Count up by 5s so we don't spam
- if percent != test: #New Number
- print(str(test)[:-2] + "%")
- percent = test
- return percent
- def calchash(name, multiplier):
- result = 0
- for x in xrange(len(name)):
- result = ord(name[x]) + result * multiplier
- return result
- def getstr(data):
- x = data.find("\x00")
- if x != -1:
- return data[:x]
- else:
- return data
- class Yaz0(object):
- def decompress(self, data):
- print("Reading Yaz0....")
- pos = 0
- magic = data[pos:pos + 4];pos += 4
- if magic != "Yaz0":
- print("Not a Yaz0 file")
- sys.exit(1)
- size = uint32(data, pos);pos += 4 #Uncompressed filesize
- unk = uint32(data, pos);pos += 4 #Unknown (0x80 ???)
- pad = uint32(data, pos);pos += 4 #Padding
- out = [];dstpos = 0
- print("Decompressing Yaz0....")
- percent = 0
- while len(out) < size: #Read Entire File
- code = uint8(data, pos);code = binr(code);pos += 1
- for x in xrange(8):
- percent = check(len(out), size, percent)
- if len(out) >= size: break
- if code[x] == "1":
- out.append(uint8(data, pos));pos += 1
- if code[x] == "0":
- rle = hexstr(uint16(data, pos), 4);pos += 2
- if rle[0] == "0":
- read = uint8(data, pos);read += 0x12;pos += 1
- else:
- read = int(rle[0], 16);read += 2
- b = int(rle[1], 16)
- bckpos = ((b << 8) | int(rle[2:], 16)) + 1
- dstpos = len(out) - bckpos
- for x in xrange(read):
- try:
- out.append(out[dstpos + x])
- except:
- print(pos)
- SARChive = SARC()
- SARChive.extract(out, 1)
- class SARC(object):
- def extract(self, data, mode):
- print("Reading SARC....")
- pos = 0
- if mode == 1:
- magic = ""
- for x in xrange(4):
- magic += chr(data[x]);pos += 1
- if mode == 0:
- magic = data[pos:pos + 4];pos += 4
- name, ext = os.path.splitext(sys.argv[1])
- if magic != "SARC":
- if mode == 1: #Sent here by Yaz0
- print("Not a SARC Archive!")
- print("Writing Decompressed File....")
- f = open(name + ".bin", "wb")
- for x in xrange(len(data)):
- f.write(struct.pack(">B", data[x]))
- sys.exit(1)
- if mode == 0:
- print("Not a SARC Archive!")
- sys.exit(1)
- if mode == 1:
- data2 = data;data = ""
- for x in xrange(len(data2)):
- data += chr(data2[x])
- hdr = uint16(data, pos);pos += 2 #Length of header, always 0x14
- order = uint16(data, pos);pos += 2 #Byte Order Mark
- if order == 65279: #0xFEFF - Big Endian
- pass
- else:
- print("Little endian not supported!")
- sys.exit(1)
- size = uint32(data, pos);pos += 4 #Size of the entire file
- doff = uint32(data, pos);pos += 4 #Start of data section
- unk = uint32(data, pos);pos += 4 #???
- #---------------------------------------------------------------
- magic2 = data[pos:pos + 4];pos += 4
- if magic2 != "SFAT": #File Attribute Table
- print("Unknown Data Section: " + magic2)
- sys.exit(1)
- hdr2 = uint16(data, pos);pos += 2 #Length of header, always 0xC
- nodec = uint16(data, pos);pos += 2 #Node Count
- hashr = uint32(data, pos);pos += 4 #Hash Multiplier ??? - Always 0x65
- nodes = [];percent = 0
- print("Reading File Attribute Table...")
- for x in xrange(nodec):
- percent = check(x, nodec, percent)
- hash = data[pos:pos + 4];pos += 4
- hash = calchash(hash, hashr)
- unk = uint8(data, pos);pos += 1
- off = uint24(data, pos);pos += 3 #Name Offset
- srt = uint32(data, pos);pos += 4 #File Offset Start
- end = uint32(data, pos);pos += 4 #File Offset End
- nodes.append([hash, unk, off, srt, end])
- #---------------------------------------------------------------
- magic3 = data[pos:pos + 4];pos += 4
- if magic3 != "SFNT": #File Name Table
- print("Unknown Data Section: " + magic3)
- sys.exit(1)
- hdr3 = uint16(data, pos);pos += 2
- unk2 = uint16(data, pos);pos += 2
- strings = [];percent = 0
- print("Reading Filenames....")
- for x in xrange(nodec):
- percent = check(x, nodec, percent)
- string = getstr(data[pos:]);pos += len(string)
- while uint8(data, pos) == 0: pos += 1 #Move to the next string
- strings.append(string)
- #---------------------------------------------------------------
- print("Writing Files....")
- try:
- os.mkdir(name)
- except:
- print("Folder already exists, continuing....")
- for x in xrange(nodec):
- print("File " + str(x) + " of " + str(nodec))
- f = open(name + "/" + strings[x], "wb")
- f.write(data[nodes[x][3] + doff:nodes[x][4] + doff])
- f.close()
- def main():
- print("SARCExtract v0.1 by NWPlayer123")
- f = open(sys.argv[1], "rb")
- data = f.read()
- f.close()
- magic = data[0:4]
- if magic == "Yaz0":
- SARChive = Yaz0()
- SARChive.decompress(data)
- elif magic == "SARC":
- SARChive = SARC()
- SARChive.extract(data, 0)
- else:
- print("Unknown File Format!")
- sys.exit(1)
- if __name__ == "__main__":
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement