daily pastebin goal
55%
SHARE
TWEET

Untitled

a guest Nov 21st, 2017 53 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. # Made by Skyth
  2.  
  3. import struct
  4. import sys
  5. import os
  6. import time
  7. import uuid
  8.  
  9. from itertools import groupby
  10.  
  11. class BINAStream:
  12.     def __init__(self, f):
  13.         self.f = f
  14.         self.offsets = []
  15.         self.fillOffsets = {}
  16.         self.strings = []
  17.         self.integers = []
  18.        
  19.     def seek(self, offset):
  20.         self.f.seek(offset, 0)
  21.        
  22.     def move(self, amount):
  23.         self.f.seek(amount, 1)
  24.        
  25.     def tell(self):
  26.         return self.f.tell()
  27.        
  28.     def readAll(self):
  29.         return self.f.read()
  30.        
  31.     def readBytes(self, count):
  32.         return self.f.read(count)
  33.        
  34.     def read(self, format, prefix="<"):
  35.         format = prefix + format
  36.         return struct.unpack(format, self.readBytes(struct.calcsize(format)))
  37.        
  38.     def readUInt(self):
  39.         return self.read("I")[0]
  40.        
  41.     def readString(self):
  42.         result = ""
  43.  
  44.         while True:
  45.             char = self.readBytes(1)
  46.             if char != "\0":
  47.                 result += char
  48.             else:
  49.                 break
  50.                
  51.         return result
  52.        
  53.     def writeBytes(self, bytes):
  54.         self.f.write(bytes)
  55.        
  56.     def write(self, format, *args):
  57.         self.f.write(struct.pack(format, *args))
  58.  
  59.     def writeByte(self, v):
  60.         self.write("B", v)
  61.        
  62.     def writeUInt(self, v):
  63.         self.write("I", v)
  64.        
  65.     def writeInt(self, v):
  66.         self.write("i", v)
  67.        
  68.     def writeULong(self, v):
  69.         self.write("Q", v)
  70.        
  71.     def writeString(self, v):
  72.         self.writeBytes(v)
  73.         self.writeBytes("\0")
  74.        
  75.     def addString(self, v):
  76.         offset = self.tell()
  77.         self.writeNulls(8)
  78.        
  79.         if v != "":
  80.             self.offsets.append(offset)
  81.            
  82.             for s in self.strings:
  83.                 if s[0] == v:
  84.                     s[1].append(offset)
  85.                     return
  86.                
  87.             self.strings.append((v, [offset]))
  88.        
  89.     def writeNulls(self, count):
  90.         self.writeBytes("\0" * count)
  91.        
  92.     def addOffset(self, key):
  93.         offset = self.tell()
  94.         self.offsets.append(offset)
  95.         self.fillOffsets[key] = offset
  96.         self.writeNulls(8)
  97.            
  98.     def fillOffset(self, key):
  99.         offset = self.tell()
  100.         self.seek(self.fillOffsets[key])
  101.         self.writeULong(offset)
  102.         self.seek(offset)                
  103.         self.fillOffsets.pop(key)                
  104.        
  105.     def addIntegers(self, integers, i=0):
  106.         offset = self.tell()
  107.         self.writeNulls(8)
  108.        
  109.         if len(integers):
  110.             self.offsets.append(offset)
  111.             self.integers.append((offset, integers, i))
  112.        
  113.     def writeOffsetTable(self):
  114.         startOffset = self.tell()
  115.        
  116.         self.offsets = list(set(self.offsets))
  117.         self.offsets.sort()
  118.        
  119.         currentOffset = 0
  120.         for offset in self.offsets:
  121.             difference = offset - currentOffset
  122.            
  123.             if difference > 0xFFFC:
  124.                 self.write(">I", 0xC0000000 | (difference >> 2))
  125.             elif difference > 0xFC:
  126.                 self.write(">H", 0x8000 | (difference >> 2))
  127.             else:
  128.                 self.write(">B", 0x40 | (difference >> 2))
  129.            
  130.             currentOffset += difference
  131.            
  132.         self.pad(8)
  133.         return self.tell() - startOffset
  134.        
  135.     def writeStringTable(self):
  136.         self.strings.sort(key=lambda x: x[1][0])
  137.    
  138.         startOffset = self.tell()
  139.        
  140.         for s in self.strings:
  141.             offset = self.tell()
  142.             self.writeString(s[0])
  143.             endOffset = self.tell()
  144.                
  145.             for o in s[1]:
  146.                 self.seek(o)
  147.                 self.writeULong(offset)
  148.                
  149.             self.seek(endOffset)
  150.            
  151.         self.pad(8)
  152.         return self.tell() - startOffset
  153.        
  154.     def writeIntegerTable(self):
  155.         self.integers.sort(key=lambda x: x[2])
  156.        
  157.         startOffset = self.tell()
  158.        
  159.         for index, integers in groupby(self.integers, lambda x: x[2]):
  160.            for i in integers:
  161.                 offset = self.tell()
  162.                
  163.                 for v in i[1]:
  164.                     self.writeUInt(v)
  165.                    
  166.                 self.pad(8)
  167.                    
  168.                 endOffset = self.tell()
  169.                 self.seek(i[0])
  170.                 self.writeULong(offset)
  171.                 self.seek(endOffset)
  172.            
  173.         return self.tell() - startOffset
  174.        
  175.     def pad(self, amount):
  176.         count = amount - self.tell() % amount
  177.        
  178.         if count != amount:
  179.             self.writeNulls(count)
  180.            
  181. ForcesPacResourceTypes = {
  182.     "asm":"ResAnimator",
  183.     "anm.hkx":"ResAnimSkeleton",
  184.     "uv-anim":"ResAnimTexSrt",
  185.     "material":"ResMirageMaterial",
  186.     "model":"ResModel",
  187.     "rfl":"ResReflection",
  188.     "skl.hkx":"ResSkeleton",
  189.     "dds":"ResTexture",
  190.     "cemt":"ResCyanEffect",
  191.     "cam-anim":"ResAnimCameraContainer",
  192.     "effdb":"ResParticleLocation",
  193.     "mat-anim":"ResAnimMaterial",
  194.     "phy.hkx":"ResHavokMesh",
  195.     "vis-anim":"ResAnimVis",
  196.     "scfnt":"ResScalableFontSet",
  197.     "pt-anim":"ResAnimTexPat",
  198.     "scene":"ResScene",
  199.     "pso":"ResMiragePixelShader",
  200.     "vso":"ResMirageVertexShader",
  201.     "shader-list":"ResShaderList",
  202.     "vib":"ResVibration",
  203.     "bfnt":"ResBitmapFont",
  204.     "codetbl":"ResCodeTable",
  205.     "cnvrs-text":"ResText",
  206.     "cnvrs-meta":"ResTextMeta",
  207.     "cnvrs-proj":"ResTextProject",
  208.     "shlf":"ResSHLightField",
  209.     "swif":"ResSurfRideProject",
  210.     "gedit":"ResObjectWorld",
  211.     "fxcol.bin":"ResFxColFile",
  212.     "path":"ResSplinePath",
  213.     "lit-anim":"ResAnimLightContainer",
  214.     "gism":"ResGismoConfig",
  215.     "light":"ResMirageLight",
  216.     "probe":"ResProbe",
  217.     "svcol.bin":"ResSvCol",
  218.     "terrain-instanceinfo":"ResMirageTerrainInstanceInfo",
  219.     "terrain-model":"ResMirageTerrainModel",
  220.     "model-instanceinfo":"ResModelInstanceInfo",
  221.     "grass.bin":"ResTerrainGrassInfo"
  222.     }
  223.    
  224. ForcesPacSpecialExtensions = [
  225. "asm",
  226. "anm.hkx",
  227. "cemt",
  228. "phy.hkx",
  229. "skl.hkx",
  230. "rfl",
  231. "bfnt",
  232. "effdb",
  233. "vib",
  234. "scene",
  235. "shlf",
  236. "scfnt",
  237. "codetbl",
  238. "cnvrs-text",
  239. "swif",
  240. "fxcol.bin",
  241. "path",
  242. "gism",
  243. "light",
  244. "probe",
  245. "svcol.bin",
  246. "terrain-instanceinfo",
  247. "model-instanceinfo",
  248. "grass.bin",
  249. "shader-list",
  250. "gedit",
  251. "cnvrs-meta",
  252. "cnvrs-proj"]
  253.  
  254. class ForcesPacNode:
  255.     def __init__(self):
  256.         self.name = ""
  257.         self.data = None
  258.         self.parent = None
  259.         self.nodes = []
  260.         self.uuid = uuid.uuid1()
  261.         self.extra = None
  262.         self.idx = -1
  263.        
  264.     def __str__(self):
  265.         result = " " * self.depth + '"' + self.name + '"'
  266.        
  267.         for node in self.nodes:
  268.             result += "\n" + str(node)
  269.        
  270.         return result
  271.        
  272.     @property
  273.     def siblings(self):
  274.         if self.parent is None:
  275.             return None
  276.         else:
  277.             return self.parent.nodes
  278.        
  279.     @property
  280.     def fullPath(self):
  281.         if self.parent is None:
  282.             return self.name
  283.         else:
  284.             return self.parent.fullPath + self.name
  285.    
  286.     @property
  287.     def depth(self):
  288.         if self.parent is None:
  289.             return 0
  290.         else:
  291.             return self.parent.depth + 1
  292.        
  293.     @property
  294.     def index(self):
  295.         if self.parent is None:
  296.             return 0
  297.         else:
  298.             return self.parent.nodes.index(self)
  299.            
  300.     @property
  301.     def absoluteIndex(self):
  302.         if self.parent is None:
  303.             return 0
  304.         else:
  305.             index = 0
  306.             for node in self.siblings[:self.index]:
  307.                 index += node.absoluteCount + 1
  308.             return self.parent.absoluteIndex + index + 1
  309.    
  310.     @property
  311.     def count(self):
  312.         return len(self.nodes)
  313.        
  314.     @property
  315.     def absoluteCount(self):
  316.         count = self.count
  317.         for node in self.nodes:
  318.             count += node.absoluteCount
  319.         return count
  320.            
  321.     @property
  322.     def dataCount(self):
  323.         count = 0
  324.         for node in self.nodes:
  325.             if node.data:
  326.                 count += 1
  327.             count += node.dataCount
  328.         return count
  329.            
  330.     def getDataNodes(self):
  331.         for node in self.nodes:
  332.             if node.data:
  333.                 yield node
  334.             else:
  335.                 for n in node.getDataNodes():
  336.                     yield n
  337.        
  338.     def getAllNodes(self):
  339.         for node in self.nodes:
  340.             yield node
  341.            
  342.             for n in node.getAllNodes():
  343.                 yield n
  344.        
  345.     def newChild(self, name="", data=None):
  346.         fileNode = ForcesPacNode()
  347.         fileNode.name = name
  348.         fileNode.parent = self
  349.         self.nodes.append(fileNode)
  350.        
  351.         if data != None:
  352.             dataNode = ForcesPacNode()
  353.             dataNode.data = data
  354.             dataNode.parent = fileNode
  355.             fileNode.nodes.append(dataNode)
  356.             return dataNode
  357.            
  358.         else:
  359.             return fileNode
  360.            
  361.     def newChildren1(self, d):
  362.         nodes = []
  363.         node = self
  364.        
  365.         for data in d:
  366.             nodes.append(node.newChild(data[0], data[1]))
  367.             if node.count == 254:    
  368.                 node = node.newChild()
  369.        
  370.         return nodes
  371.                
  372.     def newChildren(self, d):
  373.         nodes = []
  374.        
  375.         # Special cases
  376.         dLen = len(d)
  377.        
  378.         # Length equals zero:
  379.         if dLen == 0:
  380.             return nodes
  381.            
  382.         # Length equals one:
  383.         if dLen == 1:
  384.             node = self.newChild(d[0][0], d[0][1])
  385.             nodes.append(node)
  386.             return nodes
  387.            
  388.         # Length is above 1:
  389.        
  390.         # Sort data by name:
  391.         d.sort(key=lambda x: x[0])
  392.        
  393.         # Minimum match
  394.         lenMin = 255
  395.        
  396.         # Match data
  397.         matches = []
  398.         noMatches = []
  399.        
  400.         # Loop through every data and
  401.         # compare data, to first string
  402.         for data in d:
  403.             sLen = len(data[0])
  404.            
  405.             # Empty string is not acceptable.
  406.             if sLen == 0:
  407.                 raise error("Empty string found during string compression!")
  408.                
  409.             # Set lenMin from sLen
  410.             lenMin = min(lenMin, sLen)
  411.            
  412.             # Start comparison.
  413.             matchLen = 0
  414.             for i in range(lenMin):
  415.                 # Use first string as reference.
  416.                 if d[0][0][i] != data[0][i]:
  417.                     break
  418.                 else:
  419.                     matchLen += 1
  420.                    
  421.             # There's at least one match:
  422.             if matchLen >= 1:
  423.                 matches.append(data)
  424.                 lenMin = min(lenMin, matchLen)
  425.             # No match:
  426.             else:
  427.                 noMatches.append(data)
  428.                
  429.         #print("Match: {}".format([x[0] for x in matches]))
  430.         #print("No Match: {}\n".format([x[0] for x in noMatches]))
  431.                
  432.         # There are matches:
  433.         matchLen = len(matches)
  434.         if matchLen >= 1:
  435.             # Just one match:
  436.             if matchLen == 1:
  437.                 node = self.newChild(matches[0][0], matches[0][1])
  438.                 nodes.append(node)
  439.             else:
  440.                 dataToConvert = []
  441.                 parentNode = None
  442.                 # Since we did comparison using
  443.                 # first string, in case minLen
  444.                 # equals first string's length,
  445.                 # don't crop it.
  446.                 if lenMin == len(d[0][0]):
  447.                     node = self.newChild(d[0][0], d[0][1])
  448.                     nodes.append(node)
  449.                     parentNode = node.parent
  450.                     matches = matches[1:]                    
  451.                 else:
  452.                     parentNode = self.newChild(d[0][0][:lenMin])
  453.                    
  454.                 for data in matches:    
  455.                     dataToConvert.append((data[0][lenMin:], data[1]))
  456.                
  457.                 # Do a recursive conversion again.
  458.                 nodes += parentNode.newChildren(dataToConvert)
  459.                  
  460.         # There are data with no match:
  461.         noMatchLen = len(noMatches)
  462.         if noMatchLen >= 1:
  463.             # Only a match, directly add it:
  464.             if noMatchLen == 1:
  465.                 node = self.newChild(noMatches[0][0], noMatches[0][1])
  466.                 nodes.append(node)
  467.             else:
  468.                 # Or check if there's in fact a char match:
  469.                 match = False
  470.                 for data in noMatches:
  471.                     for data2 in noMatches:
  472.                         if data2 != data and data2[0][0] == data[0][0]:
  473.                             match = True
  474.                             break
  475.                    
  476.                     if match:
  477.                         break
  478.                        
  479.                 # If there is, do a recursive conversion
  480.                 if match:
  481.                     nodes += self.newChildren(noMatches)
  482.                 else:
  483.                     for data in noMatches:
  484.                         node = self.newChild(data[0], data[1])
  485.                         nodes.append(node)
  486.        
  487.         return nodes
  488.        
  489.     def sort(self):
  490.         self.nodes.sort(key=lambda x: x.name.lower())
  491.        
  492.         for node in self.nodes:
  493.             node.sort()
  494.            
  495.     def makeIndices(self, i=0):
  496.         self.idx = i
  497.        
  498.         i += 1
  499.         for node in self.nodes:
  500.             i = node.makeIndices(i)
  501.        
  502.         return i
  503.                      
  504.     def read(self, s):
  505.         n = s.read("3Q3i4B")
  506.         nOffset = s.tell()
  507.        
  508.         if n[0]:
  509.             s.seek(n[0])
  510.             self.name = s.readString()
  511.            
  512.         self.data = n[1]
  513.        
  514.         s.seek(nOffset)
  515.         for i in range(n[6]):
  516.             child = self.newChild()
  517.             child.read(s)
  518.            
  519.     def write(self, s, m=0):
  520.         self.extra = s.tell()
  521.    
  522.         b1 = self.name != ""
  523.         b2 = self.data != None
  524.        
  525.         s.addString(self.name)
  526.        
  527.         if b2:
  528.             s.addOffset(self.uuid)
  529.         else:
  530.             s.writeNulls(8)
  531.            
  532.         s.addIntegers([x.idx for x in self.nodes], 1)
  533.            
  534.         if self.parent is None:
  535.             s.writeInt(-1)
  536.         else:
  537.             s.writeInt(self.parent.idx)
  538.            
  539.         s.writeInt(self.idx)
  540.  
  541.         if b2:
  542.             s.writeInt(m)
  543.             m += 1
  544.         else:
  545.             s.writeInt(-1)
  546.            
  547.         s.writeByte(self.count)
  548.         s.writeByte(0)
  549.         s.writeByte(b2)
  550.         s.writeByte(len(self.fullPath) - len(self.name))
  551.            
  552.         for node in self.nodes:
  553.             m = node.write(s, m)
  554.            
  555.         return m
  556.                
  557. class ForcesPacNodeTree:
  558.     def __init__(self):
  559.         self.rootNode = ForcesPacNode()
  560.  
  561.     def read(self, s):
  562.         header = s.read("2I2Q")
  563.        
  564.         s.seek(header[2])
  565.         self.rootNode.read(s)
  566.        
  567.     def write(self, s):
  568.         self.rootNode.sort()
  569.         count = self.rootNode.makeIndices()
  570.        
  571.         s.writeUInt(count)
  572.         s.writeUInt(self.rootNode.dataCount)
  573.        
  574.         offset = s.tell()
  575.         s.offsets.append(offset)
  576.         s.writeULong(offset + 16)
  577.         s.addIntegers([x.idx for x in self.rootNode.getDataNodes()])
  578.         self.rootNode.write(s)
  579.        
  580. class ForcesPacEntry:
  581.     def __init__(self):
  582.         self.name = ""
  583.         self.extension = ""
  584.         self.resType = ""
  585.         self.offset = 0
  586.         self.size = 0
  587.         self.srcPath = ""
  588.         self.node = None
  589.         self.idx = -1
  590.        
  591.     @property
  592.     def isSpecialExtension(self):
  593.         return self.extension in ForcesPacSpecialExtensions
  594.                
  595. class ForcesPacArchive:
  596.     def __init__(self):
  597.         self.entries = []
  598.        
  599.     def loadSingle(self, path):
  600.         with open(path, "rb") as f:
  601.             s = BINAStream(f)
  602.             return self.read(s, path)
  603.        
  604.     def load(self, path):
  605.         pacCount = self.loadSingle(path)
  606.        
  607.         for i in range(pacCount):
  608.             splitPath = path + "." + str(i).zfill(3)
  609.            
  610.             if os.path.exists(splitPath):
  611.                 self.loadSingle(splitPath)
  612.                
  613.     def unpack(self, outputPath):
  614.         if not os.path.exists(outputPath):
  615.             os.mkdir(outputPath)
  616.            
  617.         # Avoid unnecessary stream open/closes
  618.         fileStreams = {}
  619.        
  620.         for entry in self.entries:
  621.             if not fileStreams.has_key(entry.srcPath):
  622.                 fileStreams[entry.srcPath] = open(entry.srcPath, "rb")
  623.            
  624.             f = fileStreams[entry.srcPath]
  625.             f.seek(entry.offset)
  626.             d = f.read(entry.size)
  627.                
  628.             name = entry.name + "." + entry.extension
  629.             print(name)
  630.                
  631.             with open(os.path.join(outputPath, name), "wb") as o:
  632.                 o.write(d)
  633.                
  634.         for f in fileStreams.values():
  635.             f.close()
  636.            
  637.     def addFolder(self, inputDir):
  638.         for path, subDirs, names in os.walk(inputDir):
  639.             for name in names:
  640.                 if os.path.isfile(os.path.join(path, name)):
  641.                     index = name.find(".")
  642.                     if index != -1:
  643.                         extension = name[index+1:].lower()
  644.        
  645.                         if ForcesPacResourceTypes.has_key(extension):
  646.                             entry = ForcesPacEntry()
  647.                             entry.name = name[:index]
  648.                             entry.extension = extension
  649.                             entry.srcPath = os.path.join(path, name)
  650.                             entry.size = os.path.getsize(entry.srcPath)
  651.                             self.entries.append(entry)
  652.  
  653.     def read(self, s, p):
  654.         if s.readBytes(8) != "PACx301L":
  655.             raise exception("Unknown file format.")
  656.  
  657.         header = s.read("8I2HI")
  658.        
  659.         types = ForcesPacNodeTree()
  660.         types.read(s)
  661.  
  662.         for typeNode in types.rootNode.getDataNodes():
  663.             s.seek(typeNode.data)
  664.        
  665.             files = ForcesPacNodeTree()
  666.             files.read(s)
  667.                
  668.             for node in files.rootNode.getDataNodes():
  669.                 s.seek(node.data)
  670.                    
  671.                 if s.readUInt() == header[0]:
  672.                     e = s.read("QI4Q")
  673.                    
  674.                     if e[5] != 1:
  675.                         entry = ForcesPacEntry()
  676.                         entry.name = node.fullPath
  677.                         entry.resType = typeNode.fullPath
  678.                         entry.entOffset = node.data
  679.                        
  680.                         s.seek(e[4])
  681.                         entry.extension = s.readString()
  682.                        
  683.                         entry.offset = e[2]
  684.                         entry.size = e[0]
  685.                         entry.srcPath = p
  686.                            
  687.                         self.entries.append(entry)
  688.            
  689.         return header[10]
  690.        
  691.     def write(self, s, idx=-1, name="", h=1):
  692.         if idx == -1:
  693.             print("Root PAC:")
  694.         else:
  695.             print("PAC {}:".format(idx+1))
  696.    
  697.         s.writeNulls(0x30)
  698.        
  699.         print(" Making nodes...")
  700.        
  701.         entries = None
  702.         if idx == -1:
  703.             entries = self.entries
  704.         else:
  705.             entries = []
  706.             for e in self.entries:
  707.                 if e.idx == idx:
  708.                     entries.append(e)
  709.        
  710.         types = []
  711.         entries.sort(key=lambda x: x.extension)
  712.         for extension, e in groupby(entries, lambda x: x.extension):
  713.             fileTree = ForcesPacNodeTree()
  714.             for n in fileTree.rootNode.newChildren([(x.name, x) for x in e]):
  715.                 n.data.node = n
  716.             types.append((ForcesPacResourceTypes[extension], fileTree))
  717.            
  718.         print(" Writing nodes...")
  719.            
  720.         typeTree = ForcesPacNodeTree()
  721.         typeTree.rootNode.newChildren(types)
  722.         typeTree.write(s)
  723.        
  724.         for dNode in typeTree.rootNode.getDataNodes():
  725.             s.fillOffset(dNode.uuid)
  726.             dNode.data.write(s)
  727.            
  728.         nodesSize = s.tell()+s.writeIntegerTable()-0x30
  729.        
  730.         pStart = s.tell()
  731.        
  732.         m = -1
  733.         if idx == -1:
  734.             for e in entries:
  735.                 m = max(m, e.idx)
  736.                
  737.             if m != -1:
  738.                 s.writeULong(m+1)
  739.                
  740.                 for i in range(m+1):
  741.                     offset = s.tell()
  742.                     s.offsets.append(offset)
  743.                     s.writeULong(offset+8)
  744.                     s.addString(name+"."+str(i).zfill(3))
  745.        
  746.         pacsSize = s.tell() - pStart
  747.        
  748.         print(" Writing entries...")
  749.        
  750.         eStart = s.tell()
  751.         entries.sort(key=lambda x: x.node.extra)
  752.         for entry in entries:
  753.             s.fillOffset(entry.node.uuid)
  754.                    
  755.             s.writeUInt(h)
  756.                    
  757.             s.writeULong(entry.size)
  758.             s.writeNulls(4)
  759.                
  760.             if entry.idx == idx:
  761.                 s.addOffset(entry.name+entry.extension)
  762.             else:
  763.                 s.writeNulls(8)
  764.                    
  765.             s.writeNulls(8)
  766.                    
  767.             s.addString(entry.extension)
  768.                
  769.             if entry.idx == idx:
  770.                with open(entry.srcPath, "rb") as f:
  771.                     if f.read(4) == "BINA":
  772.                         s.writeULong(2)
  773.                     else:
  774.                         s.writeULong(0)
  775.             else:
  776.                 s.writeULong(1)
  777.            
  778.         entriesSize = s.tell() - eStart
  779.         stringsSize = s.writeStringTable()
  780.         s.pad(8)
  781.        
  782.         dStart = s.tell()
  783.        
  784.         print(" Copying file data...")
  785.        
  786.         for entry in entries:
  787.             if entry.idx == idx:
  788.                 s.pad(16)
  789.                 s.fillOffset(entry.name+entry.extension)
  790.            
  791.                 with open(entry.srcPath, "rb") as f:
  792.                     s.writeBytes(f.read())
  793.                
  794.         s.pad(8)
  795.                
  796.         dataSize = s.tell() - dStart
  797.         offsetsSize = s.writeOffsetTable()
  798.         fileSize = s.tell()
  799.        
  800.         s.seek(0)
  801.         s.writeBytes("PACx301L")
  802.         s.writeUInt(h)
  803.         s.writeUInt(fileSize)
  804.         s.writeUInt(nodesSize)
  805.         s.writeUInt(pacsSize)
  806.         s.writeUInt(entriesSize)
  807.         s.writeUInt(stringsSize)
  808.         s.writeUInt(dataSize)
  809.         s.writeUInt(offsetsSize)
  810.        
  811.         if idx != -1:
  812.             s.writeByte(2)
  813.         elif idx == -1 and m != -1:
  814.             s.writeByte(5)
  815.         else:
  816.             s.writeByte(1)
  817.            
  818.         s.writeByte(0)
  819.         s.writeByte(8)
  820.         s.writeByte(1)
  821.        
  822.         s.writeUInt(m+1)
  823.                
  824.     def save(self, path):
  825.         totalSize = 0
  826.         idx = 0
  827.         hasSplit = False
  828.         for e in self.entries:
  829.             if not e.isSpecialExtension:
  830.                 hasSplit = True
  831.                 totalSize += e.size
  832.                 if totalSize >= 0x1E00000:
  833.                     totalSize = 0
  834.                     idx += 1
  835.                 e.idx = idx
  836.                
  837.         h = int(time.time())
  838.        
  839.         with open(path, "wb") as f:
  840.             s = BINAStream(f)
  841.             self.write(s, -1, os.path.basename(path), h)
  842.            
  843.         if hasSplit:
  844.             for i in range(idx+1):
  845.                 with open(path+"."+str(i).zfill(3), "wb") as f:
  846.                     s = BINAStream(f)
  847.                     self.write(s, i, "", h)
  848.        
  849. if __name__ == "__main__":
  850.     if len(sys.argv) <= 1:
  851.         print("PAC Unpacker/Packer made by Skyth")
  852.         print("Usage: [*]")
  853.         print("Drag and drop a .PAC to unpack.")
  854.         print("Drag and drop a folder to pack.")
  855.         raw_input()
  856.     else:
  857.         path = sys.argv[1]
  858.        
  859.         if os.path.isfile(path) and path.endswith(".pac"):
  860.             archive = ForcesPacArchive()
  861.             archive.load(path)
  862.            
  863.             outputPath = os.path.splitext(path)[0]
  864.             archive.unpack(outputPath)
  865.                
  866.         elif os.path.isdir(path):
  867.             archive = ForcesPacArchive()
  868.             archive.addFolder(path)
  869.             archive.save(path + ".pac")
RAW Paste Data
Top