Advertisement
Guest User

SARCExtract.py v0.1 by NWPlayer123

a guest
Jan 17th, 2015
1,168
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.59 KB | None | 0 0
  1. import os, sys, struct
  2.  
  3. def hexstr(data, length): #Convert input into hex string
  4.     return hex(data).lstrip("0x").rstrip("L").zfill(length).upper()
  5. def binr(byte):
  6.     return bin(byte).lstrip("0b").zfill(8)
  7. def uint8(data, pos):
  8.     return struct.unpack(">B", data[pos:pos + 1])[0]
  9. def uint16(data, pos):
  10.     return struct.unpack(">H", data[pos:pos + 2])[0]
  11. def uint24(data, pos):
  12.     return struct.unpack(">I", "\00" + data[pos:pos + 3])[0] #HAX
  13. def uint32(data, pos):
  14.     return struct.unpack(">I", data[pos:pos + 4])[0]
  15. def check(length, size, percent):
  16.     length = float(length);size = float(size)
  17.     test = round(length / size, 2) #Percent complete as decimal
  18.     test = test * 100 #Percent
  19.     if test % 5 == 0: #Count up by 5s so we don't spam
  20.         if percent != test: #New Number
  21.             print(str(test)[:-2] + "%")
  22.             percent = test
  23.     return percent
  24. def calchash(name, multiplier):
  25.     result = 0
  26.     for x in xrange(len(name)):
  27.         result = ord(name[x]) + result * multiplier
  28.     return result
  29. def getstr(data):
  30.     x = data.find("\x00")
  31.     if x != -1:
  32.         return data[:x]
  33.     else:
  34.         return data
  35.  
  36. class Yaz0(object):
  37.     def decompress(self, data):
  38.         print("Reading Yaz0....")
  39.         pos = 0
  40.         magic = data[pos:pos + 4];pos += 4
  41.         if magic != "Yaz0":
  42.             print("Not a Yaz0 file")
  43.             sys.exit(1)
  44.         size = uint32(data, pos);pos += 4 #Uncompressed filesize
  45.         unk  = uint32(data, pos);pos += 4 #Unknown (0x80 ???)
  46.         pad  = uint32(data, pos);pos += 4 #Padding
  47.         out = [];dstpos = 0
  48.         print("Decompressing Yaz0....")
  49.         percent = 0
  50.         while len(out) < size: #Read Entire File
  51.             code = uint8(data, pos);code = binr(code);pos += 1
  52.             for x in xrange(8):
  53.                 percent = check(len(out), size, percent)
  54.                 if len(out) >= size: break
  55.                 if code[x] == "1":
  56.                     out.append(uint8(data, pos));pos += 1
  57.                 if code[x] == "0":
  58.                     rle = hexstr(uint16(data, pos), 4);pos += 2
  59.                     if rle[0] == "0":
  60.                         read = uint8(data, pos);read += 0x12;pos += 1
  61.                     else:
  62.                         read = int(rle[0], 16);read += 2
  63.                     b = int(rle[1], 16)
  64.                     bckpos = ((b << 8) | int(rle[2:], 16)) + 1
  65.                     dstpos = len(out) - bckpos
  66.                     for x in xrange(read):
  67.                         try:
  68.                             out.append(out[dstpos + x])
  69.                         except:
  70.                             print(pos)
  71.         SARChive = SARC()
  72.         SARChive.extract(out, 1)
  73.  
  74. class SARC(object):
  75.     def extract(self, data, mode):
  76.         print("Reading SARC....")
  77.         pos = 0
  78.         if mode == 1:
  79.             magic = ""
  80.             for x in xrange(4):
  81.                 magic += chr(data[x]);pos += 1
  82.         if mode == 0:
  83.             magic = data[pos:pos + 4];pos += 4
  84.         name, ext = os.path.splitext(sys.argv[1])
  85.         if magic != "SARC":
  86.             if mode == 1: #Sent here by Yaz0
  87.                 print("Not a SARC Archive!")
  88.                 print("Writing Decompressed File....")
  89.                 f = open(name + ".bin", "wb")
  90.                 for x in xrange(len(data)):
  91.                     f.write(struct.pack(">B", data[x]))
  92.                 sys.exit(1)
  93.             if mode == 0:
  94.                 print("Not a SARC Archive!")
  95.                 sys.exit(1)
  96.         if mode == 1:
  97.             data2 = data;data = ""
  98.             for x in xrange(len(data2)):
  99.                 data += chr(data2[x])
  100.         hdr = uint16(data, pos);pos += 2 #Length of header, always 0x14
  101.         order = uint16(data, pos);pos += 2 #Byte Order Mark
  102.         if order == 65279: #0xFEFF - Big Endian
  103.             pass
  104.         else:
  105.             print("Little endian not supported!")
  106.             sys.exit(1)
  107.         size = uint32(data, pos);pos += 4 #Size of the entire file
  108.         doff = uint32(data, pos);pos += 4 #Start of data section
  109.         unk  = uint32(data, pos);pos += 4 #???
  110.         #---------------------------------------------------------------
  111.         magic2 = data[pos:pos + 4];pos += 4
  112.         if magic2 != "SFAT": #File Attribute Table
  113.             print("Unknown Data Section: " + magic2)
  114.             sys.exit(1)
  115.         hdr2 = uint16(data, pos);pos += 2 #Length of header, always 0xC
  116.         nodec = uint16(data, pos);pos += 2 #Node Count
  117.         hashr = uint32(data, pos);pos += 4 #Hash Multiplier ??? - Always 0x65
  118.         nodes = [];percent = 0
  119.         print("Reading File Attribute Table...")
  120.         for x in xrange(nodec):
  121.             percent = check(x, nodec, percent)
  122.             hash = data[pos:pos + 4];pos += 4
  123.             hash = calchash(hash, hashr)
  124.             unk  = uint8(data, pos);pos += 1
  125.             off  = uint24(data, pos);pos += 3 #Name Offset
  126.             srt  = uint32(data, pos);pos += 4 #File Offset Start
  127.             end  = uint32(data, pos);pos += 4 #File Offset End
  128.             nodes.append([hash, unk, off, srt, end])
  129.         #---------------------------------------------------------------
  130.         magic3 = data[pos:pos + 4];pos += 4
  131.         if magic3 != "SFNT": #File Name Table
  132.             print("Unknown Data Section: " + magic3)
  133.             sys.exit(1)
  134.         hdr3 = uint16(data, pos);pos += 2
  135.         unk2 = uint16(data, pos);pos += 2
  136.         strings = [];percent = 0
  137.         print("Reading Filenames....")
  138.         for x in xrange(nodec):
  139.             percent = check(x, nodec, percent)
  140.             string = getstr(data[pos:]);pos += len(string)
  141.             while uint8(data, pos) == 0: pos += 1 #Move to the next string
  142.             strings.append(string)
  143.         #---------------------------------------------------------------
  144.         print("Writing Files....")
  145.         try:
  146.             os.mkdir(name)
  147.         except:
  148.             print("Folder already exists, continuing....")
  149.         for x in xrange(nodec):
  150.             print("File " + str(x) + " of " + str(nodec))
  151.             f = open(name + "/" + strings[x], "wb")
  152.             f.write(data[nodes[x][3] + doff:nodes[x][4] + doff])
  153.             f.close()
  154.  
  155. def main():
  156.     print("SARCExtract v0.1 by NWPlayer123")
  157.     f = open(sys.argv[1], "rb")
  158.     data = f.read()
  159.     f.close()
  160.     magic = data[0:4]
  161.     if magic == "Yaz0":
  162.         SARChive = Yaz0()
  163.         SARChive.decompress(data)
  164.     elif magic == "SARC":
  165.         SARChive = SARC()
  166.         SARChive.extract(data, 0)
  167.     else:
  168.         print("Unknown File Format!")
  169.         sys.exit(1)
  170.  
  171. if __name__ == "__main__":
  172.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement