-
-
Save NikolasTzimoulis/0ffed1d5c56d8228c89353f7cc0fe392 to your computer and use it in GitHub Desktop.
Extract .SOUND files from the videogame The Witness
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
it works thanks!