Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import os
- import struct
- import io
- # Parameters
- idxFile=r"D:\Games\Steam\steamapps\common\Detroit Become Human\BigFile_PC.idx"
- bnkDir=r"E:\GameRips\detroit\pc"
- wemDir=r"E:\GameRips\detroit\pc\wem"
- extractBnk=True
- extractWem=False
- # Script starts here
- idx=dict()
- datHandles=dict()
- bigFileName=None
- class FileEntry:
- def __init__(self,f):
- data=struct.unpack(">7I",f.read(0x1c))
- self.typ=data[0]
- self.count=data[1]
- self.id=data[2]
- self.offset=data[3]
- self.size=data[4]
- self.extraSize=data[5]
- self.datNum=data[6]
- def parseIdx(fname):
- name,ext=os.path.splitext(fname)
- global bigFileName
- bigFileName=name
- f=open(fname,"rb")
- f.seek(0x65)
- numFiles=struct.unpack(">I",f.read(0x04))[0]
- for i in range(numFiles):
- entry=FileEntry(f)
- idx[entry.typ,entry.id]=entry
- f.close()
- def loadFile(ref):
- entry=idx[ref]
- if entry.datNum in datHandles:
- f=datHandles[entry.datNum]
- else:
- name,ext=os.path.splitext(bigFileName)
- newExt=".dat" if entry.datNum==0 else ".d%02d" % (entry.datNum)
- datName=name+newExt
- f=open(datName,"rb")
- datHandles[entry.datNum]=f
- f.seek(entry.offset)
- data=f.read(entry.size)
- return data
- def readHeader(f,expectedMagic=None):
- magic,ver=struct.unpack("<8sI",f.read(0x0c))
- if expectedMagic and magic!=expectedMagic:
- raise Exception("Bad header magic at 0x%08x! Expected %s, got %s!" %
- (f.tell()-0x0c,expectedMagic,magic))
- def readString(f):
- size=struct.unpack("<I",f.read(0x04))[0]
- return f.read(size).decode()
- def readQZip(f):
- magic,flag=struct.unpack("<4sB",f.read(0x05))
- if magic!=b"QZIP":
- raise Exception("Bad QZIP magic at 0x%08x!" % (f.tell()-0x05))
- def readComCont(f,dc=False):
- readHeader(f,b"COM_CONT")
- size,numRefs=struct.unpack("<II",f.read(0x08))
- refs=list()
- for i in range(numRefs):
- typ,id,flag=struct.unpack("<IIB",f.read(0x09))
- refs.append((typ,id))
- # Legacy thing from Fahrenheit and early Heavy Rain, always empty.
- readHeader(f,b"LOADCONT")
- if not dc:
- size=struct.unpack("<I",f.read(0x04))[0]
- f.seek(size,1)
- return refs
- def closeDats():
- for f in datHandles.values():
- f.close()
- datHandles.clear()
- def extractSoundBanks(outpath):
- print("Extracting BNK...")
- os.makedirs(outpath,exist_ok=True)
- for ref in idx:
- if ref[0]!=1022:
- continue
- #print("%s: %s" % (ref[0],ref[1]))
- f=io.BytesIO(loadFile(ref))
- comRefs=readComCont(f)
- readHeader(f,b"CSNDBNK_")
- size=struct.unpack("<I",f.read(0x04))[0]
- bnkId=readString(f)
- bnkName=readString(f)
- print("%s: %s" % (bnkId,bnkName))
- f2=io.BytesIO(loadFile(comRefs[0]))
- # HACK: There's no offsets table for assets inside Data Container
- # so we just look for CSNDBKDT string.
- pos=f2.getvalue().find(b"CSNDBKDT")
- if pos==-1:
- print("No BNK data for %s!" % (bnkName))
- continue
- f2.seek(pos)
- readHeader(f2)
- size=struct.unpack("<I",f2.read(0x04))[0]
- bnkData=f2.read(size)
- out=open(os.path.join(outpath,bnkName+".bnk"),"wb")
- out.write(bnkData)
- out.close()
- closeDats()
- def extractSoundData(outpath):
- print("Extracting WEM...")
- os.makedirs(outpath,exist_ok=True)
- for ref in idx:
- if ref[0]!=1033:
- continue
- f=io.BytesIO(loadFile(ref))
- readQZip(f)
- readHeader(f,b"CSNDDATA")
- size=struct.unpack("<I",f.read(0x04))[0]
- unkStr=readString(f)
- unkNum=struct.unpack("<I",f.read(0x04))[0]
- wemName=readString(f)
- wemSize=struct.unpack("<I",f.read(0x04))[0]
- wemData=f.read(wemSize)
- #print(wemName)
- out=open(os.path.join(outpath,wemName+".wem"),"wb")
- out.write(wemData)
- out.close()
- closeDats()
- if __name__=="__main__":
- parseIdx(idxFile)
- if extractBnk:
- extractSoundBanks(bnkDir)
- if extractWem:
- extractSoundData(wemDir)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement