Skip to content

Instantly share code, notes, and snippets.

@NikolasTzimoulis
Forked from mfgmfg/witness_lz4d.py
Last active March 2, 2024 07:01
Show Gist options
  • Save NikolasTzimoulis/0ffed1d5c56d8228c89353f7cc0fe392 to your computer and use it in GitHub Desktop.
Save NikolasTzimoulis/0ffed1d5c56d8228c89353f7cc0fe392 to your computer and use it in GitHub Desktop.
Extract .SOUND files from the videogame The Witness
import struct
import sys
import os
MAX_OUTPUT_SIZE = 1 << 24 # 16 megabytes
def load_binary_file(fn, nbytes=100000000):
data = []
with open(fn, "rb") as src:
byte = src.read(1)
idx = 0
while (len(byte) > 0) and (idx < nbytes):
data.append(ord(byte))
byte = src.read(1)
idx += 1
#print(data[:200])
return data
def getSoundFiles(mypath = '.'):
return [f for f in os.listdir(mypath) if f.endswith('.sound')]
def main(srcfn):
indata = load_binary_file(srcfn)
inbytes = len(indata)
outlen = struct.unpack('<I',bytearray(indata[8:12]))[0]
if (outlen < 0) or (outlen > MAX_OUTPUT_SIZE):
print("Outsize larger than max size {}; aborting. File might not be compressed.".format(MAX_OUTPUT_SIZE))
return
outdata = bytearray(outlen)
inptr=12
outptr=0
printout=0
while(outptr < outlen):
copybytes = (indata[inptr] & 0xf0) >> 4
matchbytes = (indata[inptr] & 0x0f)
inptr += 1
if (copybytes == 15):
while( True ):
copybytes += indata[inptr]
inptr += 1
if (indata[inptr-1] != 0xff):
break
#print("Copying %d bytes" % copybytes)
if (copybytes > 0):
outdata[outptr:outptr+copybytes] = indata[inptr:inptr+copybytes]
inptr += copybytes
outptr += copybytes
if (inptr >= inbytes):
break
offset = indata[inptr] + (indata[inptr+1] << 8)
inptr += 2
if (matchbytes == 15):
while( True ):
matchbytes += indata[inptr]
inptr += 1
if (indata[inptr-1] != 0xff):
break
matchbytes += 4
#print("Offset is %d, copying %d match bytes" % (offset, matchbytes))
if (matchbytes >= offset): #overlapping copy
for i in range(matchbytes):
outdata[outptr+i] = outdata[outptr-offset+i] # slow
else:
outdata[outptr:outptr+matchbytes] = outdata[outptr-offset:outptr-offset+matchbytes]
outptr += matchbytes
if (outptr > printout):
# print("%d/%d" % (outptr,outlen))
printout += 256*1024
# print("%d/%d" % (outptr,outlen))
if bytearray(indata).find(b'Ogg') > -1:
destfn = srcfn+'.ogg'
else:
destfn = srcfn+'.wav'
with open(destfn,"wb") as outfile:
startPos = bytearray(outdata).find(b'RIFF4')
if startPos > -1:
outfile.write(outdata[outdata.find(b'RIFF4'):-15])
else:
outfile.write(bytearray(indata[16:-15]))
if __name__ == '__main__':
if (len(sys.argv) < 2):
#print("need input and output file names")
count = 0
filesList = getSoundFiles()
length = len(filesList)
for s in filesList:
count +=1
main(s)
print(str(count)+"/"+str(length)+"\t", s)
else:
main(sys.argv[1])
@creikey
Copy link

creikey commented Apr 19, 2023

it works thanks!

@max-unfinity
Copy link

Oh my God, thank you so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment