aboutsummaryrefslogtreecommitdiff
path: root/devtools/blade_runner/subtitles/quotesSpreadsheetCreator
diff options
context:
space:
mode:
authorantoniou792018-12-24 14:59:38 +0200
committerEugene Sandulenko2018-12-25 12:35:52 +0100
commite1f1c3eef954bdad18a4409367f43c8fd9684d5e (patch)
treedc433c7c52d4de9262909db0c3234376be2217f2 /devtools/blade_runner/subtitles/quotesSpreadsheetCreator
parent8008f14fcd4026c4124e650529776c835dc24162 (diff)
downloadscummvm-rg350-e1f1c3eef954bdad18a4409367f43c8fd9684d5e.tar.gz
scummvm-rg350-e1f1c3eef954bdad18a4409367f43c8fd9684d5e.tar.bz2
scummvm-rg350-e1f1c3eef954bdad18a4409367f43c8fd9684d5e.zip
DEVTOOLS: Blade Runner subtitles python scripts
Diffstat (limited to 'devtools/blade_runner/subtitles/quotesSpreadsheetCreator')
-rw-r--r--devtools/blade_runner/subtitles/quotesSpreadsheetCreator/audFileDecode.py210
-rw-r--r--devtools/blade_runner/subtitles/quotesSpreadsheetCreator/audFileLib.py289
-rw-r--r--devtools/blade_runner/subtitles/quotesSpreadsheetCreator/samples/actornames.txt75
-rw-r--r--devtools/blade_runner/subtitles/quotesSpreadsheetCreator/samples/outSpeech-01-11-2018-1414.xlsbin0 -> 902656 bytes
-rw-r--r--devtools/blade_runner/subtitles/quotesSpreadsheetCreator/samples/sampleCMDParameters.txt1
-rw-r--r--devtools/blade_runner/subtitles/quotesSpreadsheetCreator/sortBladeRunnerWavs02.py722
-rw-r--r--devtools/blade_runner/subtitles/quotesSpreadsheetCreator/treFileLib.py107
7 files changed, 1404 insertions, 0 deletions
diff --git a/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/audFileDecode.py b/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/audFileDecode.py
new file mode 100644
index 0000000000..a46b3d8798
--- /dev/null
+++ b/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/audFileDecode.py
@@ -0,0 +1,210 @@
+#!/usr/bin/python
+# -*- coding: UTF-8 -*-
+#
+import ctypes
+from struct import *
+
+my_module_version = "0.50"
+my_module_name = "audFileDecode"
+
+aud_ima_index_adjust_table = [-1, -1, -1, -1, 2, 4, 6, 8]
+
+# aud_ima_step_table has 89 entries
+aud_ima_step_table = [
+ 7, 8, 9, 10, 11, 12, 13, 14, 16,
+ 17, 19, 21, 23, 25, 28, 31, 34, 37,
+ 41, 45, 50, 55, 60, 66, 73, 80, 88,
+ 97, 107, 118, 130, 143, 157, 173, 190, 209,
+ 230, 253, 279, 307, 337, 371, 408, 449, 494,
+ 544, 598, 658, 724, 796, 876, 963, 1060, 1166,
+ 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749,
+ 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
+ 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289,
+ 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 ]
+
+aud_ws_step_table2 = [-2, -1, 0, 1]
+
+aud_ws_step_table4 = [
+ -9, -8, -6, -5, -4, -3, -2, -1,
+ 0, 1, 2, 3, 4, 5, 6, 8
+]
+
+# (const xccTHA::byte* audio_in, short* audio_out, int& index, int& sample, int cs_chunk)
+# index and sample are passed by reference and changed here...
+# audio_out is definitely affected!
+def aud_decode_ima_chunk(audioBufferIn, index, sample, cs_chunk):
+ code = -1
+ delta = -1
+ step = -1
+
+ audioBufferOut = []
+ #for i in range(0, len(audioBufferIn)):
+ # print '%d: %d'%(i, int(audioBufferIn[i]))
+
+ for sample_index in range (0, cs_chunk):
+ try:
+ code = audioBufferIn[sample_index >> 1]
+ except:
+ code = 0xa9 # dummy workaround because the c code is accessing an out of bounds index sometimes due to this shift here
+ #print "cs_chunk %d, sample_index %d, shifted %d, code: %d" % (cs_chunk, sample_index, sample_index >> 1, int(audioBufferIn[sample_index >> 1]))
+ #print "cs_chunk %s, sample_index %s, shifted %s, code: %s" % \
+ # (''.join('{:04X}'.format(cs_chunk)), ''.join('{:02X}'.format(sample_index)), ''.join('{:02X}'.format(sample_index >> 1)), ''.join('{:04X}'.format(int(code))))
+ code = code >> 4 if (sample_index & 1) else code & 0xf
+ step = aud_ima_step_table[index]
+ delta = step >> 3
+ if (code & 1):
+ delta += step >> 2
+ if (code & 2):
+ delta += step >> 1
+ if (code & 4):
+ delta += step
+ if (code & 8):
+ sample -= delta
+ if (sample < -32768):
+ sample = -32768
+ else:
+ sample += delta
+ if (sample > 32767):
+ sample = 32767
+ audioBufferOut.append(ctypes.c_short( sample ).value )
+ #audioBufferOut.append(sample) # it's not different from above... ctypes.c_short( sample ).value
+ #print "audio_out[%s]: %s" % (''.join('{:02X}'.format(sample_index)), ''.join('{:02X}'.format(audioBufferOut[sample_index])));
+ index += aud_ima_index_adjust_table[code & 7]
+ if (index < 0):
+ index = 0
+ elif (index > 88):
+ index = 88
+ ## output buffer of shorts
+ #binDataOut = struct.pack('h'*len(audioBufferOut), *audioBufferOut)
+ #return (binDataOut, index, sample)
+ return (audioBufferOut, index, sample)
+#
+#
+#
+def aud_decode_clip8(v):
+ if (v < 0):
+ return 0
+ return 0xff if (v > 0xff) else v
+#
+#
+#
+
+# (const xccTHA::byte* r, char* w, int cb_s, int cb_d)
+def aud_decode_ws_chunk(inputChunkBuffer, cb_s, cb_d):
+ outputChunkBuffer = []
+ inpChBuffIter = 0
+ outChBuffIter = 0
+
+ if (cb_s == cb_d):
+ # outputChunkBuffer = inputChunkBuffer[:cb_s] # memcpy(w, r, cb_s) # FIX
+ for mcp in range(0, cb_s):
+ outputChunkBuffer.append(ctypes.c_char(inputChunkBuffer[inpChBuffIter + mcp]).value)
+ #binDataOut = struct.pack('b'*len(outputChunkBuffer), *outputChunkBuffer)
+ #return binDataOut
+ return outputChunkBuffer
+
+# const xccTHA::byte* s_end = inputChunkBuffer + cb_s; # FIX
+
+ s_end = inpChBuffIter + cb_s
+ sample = ctypes.c_int(0x80).value #int sample
+ while (inpChBuffIter < s_end):
+ inpChBuffIter += 1
+ count = ctypes.c_char(inputChunkBuffer[inpChBuffIter] & 0x3f).value # char count
+ switchKey = inputChunkBuffer[inpChBuffIter - 1] >> 6 # inputChunkBuffer[-1] # b[-1] is *(b - 1)
+ if switchKey == 0:
+ count += 1
+ for iter in range (count, 0, -1):
+ inpChBuffIter += 1
+ code = ctypes.c_int(inputChunkBuffer[inpChBuffIter]).value # int code
+ # assignment in C was right to left so:
+ # *(outputChunkBuffer++) = sample = clip8(sample + aud_ws_step_table2[code & 3])
+ # is:
+ # *(outputChunkBuffer++) = (sample = clip8(sample + aud_ws_step_table2[code & 3]))
+ # which is equivalent to these two commands:
+ # sample = clip8(sample + aud_ws_step_table2[code & 3])
+ # *(outputChunkBuffer++) = sample
+ # SO:
+ sample = aud_decode_clip8(sample + aud_ws_step_table2[code & 3])
+ outputChunkBuffer.append(ctypes.c_char(sample).value)
+ outChBuffIter += 1
+ sample = aud_decode_clip8(sample + aud_ws_step_table2[code >> 2 & 3])
+ outputChunkBuffer.append(ctypes.c_char(sample).value)
+ outChBuffIter += 1
+ sample = aud_decode_clip8(sample + aud_ws_step_table2[code >> 4 & 3])
+ outputChunkBuffer.append(ctypes.c_char(sample).value)
+ outChBuffIter += 1
+ sample = aud_decode_clip8(sample + aud_ws_step_table2[code >> 6])
+ outputChunkBuffer.append(ctypes.c_char(sample).value)
+ outChBuffIter += 1
+ elif switchKey == 1:
+ count += 1
+ for iter in range (count, 0, -1):
+ inpChBuffIter += 1
+ code = inputChunkBuffer[inpChBuffIter] # int code
+ sample += aud_ws_step_table4[code & 0xf]
+ sample = aud_decode_clip8(sample)
+ outputChunkBuffer.append(ctypes.c_char(sample).value)
+ outChBuffIter += 1
+ sample += aud_ws_step_table4[code >> 4]
+ sample = aud_decode_clip8(sample)
+ outputChunkBuffer.append(ctypes.c_char(sample).value)
+ outChBuffIter += 1
+ elif switchKey == 2:
+ if (count & 0x20):
+ #sample += static_cast<char>(count << 3) >> 3
+ #*(outputChunkBuffer++) = sample
+ sample += ((count & 0xFF) << 3 ) >> 3
+ outputChunkBuffer.append(ctypes.c_char(sample).value)
+ outChBuffIter += 1
+ else:
+ count += 1
+ # memcpy(outputChunkBuffer, inputChunkBuffer, count) # FIX
+ for mcp in range(0, count):
+ outputChunkBuffer.append(ctypes.c_char(inputChunkBuffer[inpChBuffIter + mcp]).value)
+ inpChBuffIter += count
+ outChBuffIter += count
+ sample = inputChunkBuffer[inpChBuffIter - 1]
+ else:
+ count += 1
+ # memset(outputChunkBuffer, sample, ++count)
+ for mst in range(0, count):
+ outputChunkBuffer.append(ctypes.c_char(sample).value)
+ outChBuffIter += count;
+ # output buffer of chars
+ #binDataOut = struct.pack('b'*len(outputChunkBuffer), *outputChunkBuffer)
+ #return binDataOut
+ return outputChunkBuffer
+
+#
+#
+#
+
+class audFileDecode:
+ m_index = -1
+ m_sample = -1
+
+ def __init__(self, index = 0, sample = 0):
+ self.m_index = index;
+ self.m_sample = sample;
+ return
+
+ def index(self):
+ return self.m_index
+
+ # (const xccTHA::byte* audio_in, short* audio_out, int cs_chunk)
+ def decode_chunk(self, audio_in, cs_chunk):
+ (audio_Out, outIndex, outSample) = aud_decode_ima_chunk(audio_in, self.m_index, self.m_sample, cs_chunk)
+ self.m_index = outIndex
+ self.m_sample = outSample
+ return audio_Out
+
+if __name__ == '__main__':
+ # main()
+ print "Running %s as main module" % (my_module_name)
+ decodeInstance = audFileDecode()
+
+else:
+ #debug
+ #print "Running %s imported from another module" % (my_module_name)
+ pass
+ \ No newline at end of file
diff --git a/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/audFileLib.py b/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/audFileLib.py
new file mode 100644
index 0000000000..1adf4bc969
--- /dev/null
+++ b/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/audFileLib.py
@@ -0,0 +1,289 @@
+#!/usr/bin/python
+# -*- coding: UTF-8 -*-
+#
+import os, sys, shutil
+import wave
+import struct
+from struct import *
+from audFileDecode import *
+import ctypes
+
+my_module_version = "0.50"
+my_module_name = "audFileLib"
+
+#constants
+aud_chunk_id = 0x0000deaf
+SIZE_OF_AUD_HEADER_IN_BYTES = 12
+SIZE_OF_AUD_CHUNK_HEADER_IN_BYTES = 8
+
+class AudHeader:
+ samplerate = -1 #// Frequency // int16_t // TODO should be unsigned (?)
+ size_in = -1 #// Size of file (without header) // int32_t // TODO should be unsigned (?)
+ size_out = -1 #// Size of output data // int32_t // TODO should be unsigned (?)
+ flags = -1 #// bit 0=stereo, bit 1=16bit // int8_t
+ compression = -1 #// 1=WW compressed, 99=IMA ADPCM (0x63) // int8_t
+
+ def __init__(self):
+ return
+
+
+#//The rest of the AUD files is divided in chunks. These are usually 512
+#//bytes long, except for the last one.
+class AudChunkHeader:
+ size_in = -1 #// Size of compressed data // int16_t // TODO should be unsigned (?)
+ size_out = -1 #// Size of output data // int16_t // TODO should be unsigned (?)
+ id = 0x0000FFFF #// Always 0x0000DEAF // int32_t
+
+ def __init__(self):
+ return
+
+#
+#
+#
+class audFile:
+ m_header = AudHeader()
+ def __init__(self):
+ return
+
+ # std::fstream& fs, AudFileNS::pos_type startAudFilepos, AudFileNS::pos_type endAudFilepos, const std::string& filename
+ def extract_as_wav(self, audBytesBuff, filename):
+ print "Saving to wav: " + filename
+
+ cvirtualBinaryD = self.decode(audBytesBuff)
+# TODO DEBUG REMOVED FOR NOW. TODO RESTORE THIS!!!
+# if (not cvirtualBinaryD):
+# return 1
+
+ cb_sample = self.get_cb_sample()
+ cs_remaining = self.get_c_samples()
+
+ waveWritFile = wave.open(filename, 'wb')
+ waveWritFile.setnchannels(self.get_c_channels())
+ waveWritFile.setsampwidth(cb_sample)
+ waveWritFile.setframerate(self.get_samplerate())
+ waveWritFile.setnframes(cs_remaining)
+ #waveWritFile.setcomptype(None, '')
+ waveWritFile.writeframesraw(cvirtualBinaryD)
+ waveWritFile.close()
+
+# t_wav_header header;
+# memset(&header, 0, sizeof(t_wav_header));
+# header.file_header.id = wav_file_id; // # "RIFF"
+# header.file_header.size = sizeof(header) - sizeof(header.file_header) + (cs_remaining << 1);
+# header.form_type = wav_form_id; // # "WAVE"
+# header.format_chunk.header.id = wav_format_id; // #"fmt "
+# header.format_chunk.header.size = sizeof(header.format_chunk) - sizeof(header.format_chunk.header);
+# header.format_chunk.formattag = 1;
+# header.format_chunk.c_channels = 1;
+# header.format_chunk.samplerate = get_samplerate();
+# header.format_chunk.byterate = cb_sample * get_samplerate();
+# header.format_chunk.blockalign = cb_sample;
+# header.format_chunk.cbits_sample = cb_sample << 3;
+# header.data_chunk_header.id = wav_data_id; # "data"
+# header.data_chunk_header.size = cb_sample * cs_remaining;
+# error = f.write(&header, sizeof(t_wav_header));
+# return error ? error : f.write(d);
+ return 0 # TODO fix
+
+
+ def loadAudFile(self, audBytesBuff, maxLength):
+ offsInAudFile = 0
+ tmpTuple = struct.unpack_from('h', audBytesBuff, offsInAudFile)
+ self.header().samplerate = tmpTuple[0]
+ offsInAudFile += 2
+ tmpTuple = struct.unpack_from('i', audBytesBuff, offsInAudFile)
+ self.header().size_in = tmpTuple[0]
+ offsInAudFile += 4
+ tmpTuple = struct.unpack_from('i', audBytesBuff, offsInAudFile)
+ self.header().size_out = tmpTuple[0]
+ offsInAudFile += 4
+ tmpTuple = struct.unpack_from('b', audBytesBuff, offsInAudFile)
+ self.header().flags = tmpTuple[0]
+ offsInAudFile += 1
+ tmpTuple = struct.unpack_from('b', audBytesBuff, offsInAudFile)
+ self.header().compression = tmpTuple[0]
+ offsInAudFile += 1
+ print "samplerate: %d\tsizeIn: %d\tsizeOut: %d\tflags: %d\tcompression: %d" % (self.get_samplerate(), self.header().size_in, self.header().size_out, self.header().flags, self.header().compression)
+
+ if self.get_samplerate() < 8000 or self.get_samplerate() > 48000 or self.header().size_in > (maxLength - SIZE_OF_AUD_HEADER_IN_BYTES ):
+ print "AUD HEADER SIZE ERROR::2"
+ return False
+ else:
+ if self.header().compression == 1:
+ if (self.header().flags != 0):
+ return False
+ elif self.header().compression == 0x63:
+ if (self.header().flags != 2):
+ return False
+ return True
+
+ # int AudFile::get_chunk_header(int i, std::fstream& fs, AudFileNS::pos_type startAudFilepos, AudFileNS::pos_type endAudFilepos, AudChunkHeader& outAudChunkHeader)
+ def get_chunk_header(self, chunkIdx, inAudFileBytesBuffer, inAudFileSize ):
+ #fs.seekg(int(startAudFilepos) + int(SIZE_OF_AUD_HEADER_IN_BYTES), fs.beg);
+ #AudFileNS::pos_type rAudPos;
+ #rAudPos = fs.tellg();
+ outAudChunkHeader = AudChunkHeader()
+ rAudPos = SIZE_OF_AUD_HEADER_IN_BYTES
+
+ #AudChunkHeader tmpInremediateChunkheader;
+ tmpInremediateChunkheader = AudChunkHeader()
+ #while (i--) # value of i is decreased after checked by while loop
+ while(chunkIdx > 0):
+ chunkIdx -= 1
+ if (rAudPos + SIZE_OF_AUD_CHUNK_HEADER_IN_BYTES > inAudFileSize):
+ return (-1, rAudPos, None)
+
+ tmpAudFileOffset = rAudPos
+ tmpTuple = struct.unpack_from('h', inAudFileBytesBuffer, tmpAudFileOffset)
+ tmpInremediateChunkheader.size_in = tmpTuple[0]
+ tmpAudFileOffset += 2
+ tmpTuple = struct.unpack_from('h', inAudFileBytesBuffer, tmpAudFileOffset)
+ tmpInremediateChunkheader.size_out = tmpTuple[0]
+ tmpAudFileOffset += 2
+ tmpTuple = struct.unpack_from('i', inAudFileBytesBuffer, tmpAudFileOffset)
+ tmpInremediateChunkheader.id = tmpTuple[0]
+ tmpAudFileOffset += 4
+ #fs.read((char*)&tmpInremediateChunkheader, SIZE_OF_AUD_CHUNK_HEADER_IN_BYTES);
+ rAudPos += SIZE_OF_AUD_CHUNK_HEADER_IN_BYTES + tmpInremediateChunkheader.size_in
+ #fs.seekg(int(rAudPos), fs.beg);
+
+ if (rAudPos + SIZE_OF_AUD_CHUNK_HEADER_IN_BYTES > inAudFileSize ):
+ return (-1, rAudPos, None)
+ # write to FINAL output chunk header
+ tmpAudFileOffset = rAudPos
+ tmpTuple = struct.unpack_from('h', inAudFileBytesBuffer, tmpAudFileOffset)
+ outAudChunkHeader.size_in = tmpTuple[0]
+ tmpAudFileOffset += 2
+ tmpTuple = struct.unpack_from('h', inAudFileBytesBuffer, tmpAudFileOffset)
+ outAudChunkHeader.size_out = tmpTuple[0]
+ tmpAudFileOffset += 2
+ tmpTuple = struct.unpack_from('i', inAudFileBytesBuffer, tmpAudFileOffset)
+ outAudChunkHeader.id = tmpTuple[0]
+ tmpAudFileOffset += 4
+ #fs.read((char*)&outAudChunkHeader, SIZE_OF_AUD_CHUNK_HEADER_IN_BYTES);
+ if (rAudPos + SIZE_OF_AUD_CHUNK_HEADER_IN_BYTES + outAudChunkHeader.size_in > inAudFileSize):
+ return (-1, rAudPos, None)
+ rAudPos += SIZE_OF_AUD_CHUNK_HEADER_IN_BYTES
+ return (0, rAudPos, outAudChunkHeader) # //reinterpret_cast<const AudChunkHeader*>(r);
+
+ # int AudFile::get_chunk_data(int i, std::fstream& fs, int sizeToRead, AudFileNS::byte* byteChunkDataPtr)
+ def get_chunk_data(self, inAudFileBytesBuffer, startOffs, sizeToRead):
+ #fs.read((char*)byteChunkDataPtr, sizeToRead)
+ outChunkDataLst = []
+ #print "startOffs %d, sizeToRead %d" % (startOffs, sizeToRead)
+ for i in range(startOffs, startOffs + sizeToRead):
+ #outChunkDataLst.append(ctypes.c_char(inAudFileBytesBuffer[i]).value)
+ #outChunkDataLst.append(ctypes.c_byte(inAudFileBytesBuffer[i]).value)
+ tmpTuple = struct.unpack_from('b', inAudFileBytesBuffer, i)
+ outChunkDataLst.append(tmpTuple[0])
+ #byteChunkDataOut = struct.pack('b'*len(outChunkDataLst), *outChunkDataLst)
+ #return (0, byteChunkDataOut)
+ return (0, outChunkDataLst)
+
+
+ # std::fstream& fs, AudFileNS::pos_type startAudFilepos, AudFileNS::pos_type endAudFilepos
+ # returned Cvirtual_binary
+ def decode(self, audBytesBuff):
+ # The * operator unpacks an argument list. It allows you to call a function with the list items as individual arguments.
+ # binDataOut = struct.pack('i'*len(data), *data)
+ print "DECODING..."
+# Cvirtual_binary d;
+ binaryDataOutLst = []
+ binaryDataOutBuff = None
+ cb_audio = self.get_cb_sample() * self.get_c_samples() # int cb_audio - basically this should be the size_out
+ if self.header().compression == 1:
+ # write_start allocates space for virtualBinary
+ # AudFileNS::byte* w = d.write_start(cb_audio);
+ errGetChunk = 0 # int errGetChunk
+ #for (int chunk_i = 0; w != d.data_end(); chunk_i++)
+ chunk_i = 0
+ wIndex = 0
+ while (wIndex < cb_audio):
+ #AudChunkHeader out_chunk_header;
+ #out_chunk_header = AudChunkHeader()
+ (errGetChunk, bufferDataPos, out_chunk_header) = self.get_chunk_header(chunk_i, audBytesBuff, len(audBytesBuff))
+ if errGetChunk != 0:
+# print "Error OR End file case while getting uncompressed chunk header!"
+ break
+ #print "Get uncompressed chunk header returned:: %d " % (out_chunk_header.id)
+ #Cvirtual_binary out_chunk_data;
+ #AudFileNS::byte* byteChunkDataPtr = out_chunk_data.write_start(out_chunk_header.size_in);
+ (errorGCD, byteChunkDataLst) = self.get_chunk_data(audBytesBuff, bufferDataPos, out_chunk_header.size_in)
+ # export decoded chunk to w (output) buffer (of CHARS) at the point where we're currently at (so append there)
+ decodedAudioChunkAsLst = aud_decode_ws_chunk(byteChunkDataLst, out_chunk_header.size_in, out_chunk_header.size_out)
+ binaryDataOutLst.extend(decodedAudioChunkAsLst)
+ wIndex += out_chunk_header.size_out
+ chunk_i += 1
+ binaryDataOutBuff = struct.pack('b'*len(binaryDataOutLst), *binaryDataOutLst)
+ elif self.header().compression == 0x63:
+ decodeInstance = audFileDecode();
+ #decodeInstance.init();
+ #AudFileNS::byte* w = d.write_start(cb_audio);
+ errGetChunk = 0 # int errGetChunk
+ # for (int chunk_i = 0; w != d.data_end(); chunk_i++)
+ chunk_i = 0
+ wIndex = 0
+ while (wIndex < cb_audio):
+ #print("chunkI: %d\t Windex: %d\t cb_audio: %d") % (chunk_i,wIndex,cb_audio)
+ #AudChunkHeader out_chunk_header;
+ #out_chunk_header = AudChunkHeader()
+ #errGetChunk = self.get_chunk_header(chunk_i, fs, startAudFilepos, endAudFilepos, out_chunk_header);
+ (errGetChunk, bufferDataPos, out_chunk_header) = self.get_chunk_header(chunk_i, audBytesBuff, len(audBytesBuff))
+ if errGetChunk != 0:
+ print "Error OR End file case while getting COMPRESSED chunk header!"
+ break
+ #print "Get COMPRESSED chunk header returned:: headerInSize: %d headerOutSize: %d id: %d" % (out_chunk_header.size_in, out_chunk_header.size_out, out_chunk_header.id)
+ #Cvirtual_binary out_chunk_data;
+ #AudFileNS::byte* byteChunkDataPtr = out_chunk_data.write_start(out_chunk_header.size_in);
+ (errorGCD, byteChunkDataLst) = self.get_chunk_data(audBytesBuff, bufferDataPos, out_chunk_header.size_in)
+ # export decoded chunk to w (output) buffer (of SHORTS) at the point where we're currently at (so append there)
+ #print "byteChunkDataLst len: %d, size_in was: %d" % (len(byteChunkDataLst), out_chunk_header.size_in)
+ decodedAudioChunkAsLst = decodeInstance.decode_chunk(byteChunkDataLst, out_chunk_header.size_out / self.get_cb_sample());
+ binaryDataOutLst.extend(decodedAudioChunkAsLst)
+ wIndex += out_chunk_header.size_out
+ #print("new Windex: %d\t cb_audio: %d") % (wIndex,cb_audio)
+ chunk_i += 1
+ binaryDataOutBuff = struct.pack('h'*len(binaryDataOutLst), *binaryDataOutLst)
+ return binaryDataOutBuff
+
+ def header(self):
+ return self.m_header
+
+ def get_c_samples(self):
+ return self.m_header.size_out / self.get_cb_sample()
+
+ def get_samplerate(self):
+ return self.m_header.samplerate;
+
+ # flag bit 0 is stereo(set) mono(clear)
+ def get_c_channels(self):
+ return 2 if (self.m_header.flags & 0x01) else 1;
+
+ # flag bit 1 is 16bit(set) 8bit (clear)
+ def get_cb_sample(self):
+ return 2 if (self.m_header.flags & 0x02) else 1
+#
+#
+#
+if __name__ == '__main__':
+ # main()
+ print "Running %s as main module" % (my_module_name)
+ # assumes a file of name 000000.AUD in same directory
+ inAUDFile = None
+ errorFound = False
+ try:
+ inAUDFile = open(os.path.join('.','00000000.AUD'), 'rb')
+ except:
+ errorFound = True
+ print "Unexpected error:", sys.exc_info()[0]
+ raise
+ if not errorFound:
+ allOfAudFileInBuffer = inAUDFile.read()
+ audFileInstance = audFile()
+ audFileInstance.loadAudFile(allOfAudFileInBuffer, len(allOfAudFileInBuffer))
+ audFileInstance.extract_as_wav(allOfAudFileInBuffer, './tmp.wav')
+ inAUDFile.close()
+else:
+ #debug
+ #print "Running %s imported from another module" % (my_module_name)
+ pass \ No newline at end of file
diff --git a/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/samples/actornames.txt b/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/samples/actornames.txt
new file mode 100644
index 0000000000..2836d18de8
--- /dev/null
+++ b/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/samples/actornames.txt
@@ -0,0 +1,75 @@
+Id Short ActorDesc #skip first row
+0 MCCOY McCoy
+1 STEEL Steele
+2 GORDO Gordo
+3 DEKTO Dektora
+4 GUZZA Guzza
+5 CLOVI Clovis
+6 LLUCY Lucy
+7 IIIZO Izo
+8 SADIK Sadik
+9 CRAZY Crazylegs
+10 LUTHE Luther
+11 GRIGO Grigorian
+12 TRANS Transient
+13 LANCE Lance
+14 BBBOB Bullet Bob
+15 RUNCI Runciter
+16 INSEC Insect Dealer
+17 TGUAR Tyrell Guard
+18 EARLQ Early Q
+19 ZUBEN Zuben
+20 HASAN Hasan
+21 MARCU Marcus
+22 MMMIA Mia
+23 OLEAR Officer Leary
+24 OGRAY Officer Grayford
+25 HANOI Hanoi
+26 BAKER Baker
+27 DCLER Desk Clerk
+28 HOWIE Howie Lee
+29 FISHD Fish Dealer
+30 KLEIN Klein
+31 MURRA Murray
+32 HBARK Hawker's Barkeep
+33 HOLLO Holloway
+34 SWALL Sergeant Walls
+35 MORAJ Moraji
+36 TBARD The Bard
+37 PHOTG Photographer
+38 DISPA Dispatcher
+39 ANSWM Answering Machine
+40 RAJIF Rajif
+41 GKOLV Governor Kolvig
+42 ERLQB Early Q Bartender
+43 HPARR Hawker's Parrot
+44 TAFPA Taffy Patron
+45 LOCGU Lockup Guard
+46 TEENA Teenager
+47 HPATA Hysteria Patron A
+48 HPATB Hysteria Patron B
+49 HPATC Hysteria Patron C
+50 SHOES Shoeshine Man
+51 TYREL Tyrell
+52 CCHEW Chew
+53 GGAFF Gaff
+54 BRYAN Bryant
+55 TAFFY Taffy
+56 SEBAS Sebastian
+57 RACHA Rachael
+58 GDOLL General Doll
+59 ISABE Isabella
+60 BLIMP Blimp Guy
+61 NEWSC Newscaster
+62 LLEON Leon
+63 MALAN Male Announcer
+64 FREEA Free Slot A
+65 FREEB Free Slot B
+66 MAGGI Maggie
+67 ACTGA Actor Genwalker A
+68 ACTGB Actor Genwalker B
+69 ACTGC Actor Genwalker C
+70 MUTAA Mutant A
+71 MUTAB Mutant B
+72 MUTAC Mutant C
+99 MAINF Mainframe
diff --git a/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/samples/outSpeech-01-11-2018-1414.xls b/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/samples/outSpeech-01-11-2018-1414.xls
new file mode 100644
index 0000000000..1a70a220c6
--- /dev/null
+++ b/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/samples/outSpeech-01-11-2018-1414.xls
Binary files differ
diff --git a/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/samples/sampleCMDParameters.txt b/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/samples/sampleCMDParameters.txt
new file mode 100644
index 0000000000..a41e6c291a
--- /dev/null
+++ b/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/samples/sampleCMDParameters.txt
@@ -0,0 +1 @@
+python2.7 sortBladeRunnerWavs02.py -op F:\WORKSPACE_\BladeRunnerExtrTools\br-mixer-master\data\WAV -ip H:/Games/BladeRunner -xtre \ No newline at end of file
diff --git a/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/sortBladeRunnerWavs02.py b/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/sortBladeRunnerWavs02.py
new file mode 100644
index 0000000000..4bc5bb7e23
--- /dev/null
+++ b/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/sortBladeRunnerWavs02.py
@@ -0,0 +1,722 @@
+#!/usr/bin/python
+# -*- coding: UTF-8 -*-
+#
+# Created by Praetorian (ShadowNate) for Classic Adventures in Greek
+# classic.adventures.in.greek@gmail.com
+#
+# TODO update README
+# TODO test recreation of TRE file (Especially in Credits which the original has a few special characters (font delegates))
+#
+# DONE Add code and switch option: to get the blade runner installation directory as input, then find the TLK files and extract them with proper naming
+# DONE fix proper names for sheets as per latest code changes
+#
+import os, sys, shutil
+from os import walk, errno
+import xlwt
+import csv
+import os.path
+from xlwt import *
+from audFileLib import *
+from treFileLib import *
+
+# encoding=utf8
+#reload(sys)
+#sys.setdefaultencoding('utf8')
+
+company_email = "classic.adventures.in.greek@gmail.com"
+app_version = "0.50"
+app_name = "sortBladeRunnerWavs"
+app_name_spaced = "Sort Blade Runner Audio Speech Files"
+stringReplacementForRootFolderWithExtractedFiles = ""
+numReplaceStartingCharacters = 0
+
+OUTPUT_XLS_FILENAME = 'out.xls'
+OUTPUT_XLS_QUOTES_SHEET = 'INGQUO_E.TRE'
+
+supportedTLKInputFiles = [('1.TLK', 'TLK01'), ('2.TLK', 'TLK02'), ('3.TLK', 'TLK03'), ('A.TLK', 'TLK0A'), ('SPCHSFX.TLK', 'TLKSPCHSFX')]
+supportedMIXInputFiles = ['STARTUP.MIX']
+# 15 TRE files
+supportedExportedTREFiles = ['CLUES.TRE','ACTORS.TRE','CRIMES.TRE','CLUETYPE.TRE','KIA.TRE','SPINDEST.TRE','VK.TRE','OPTIONS.TRE','DLGMENU.TRE','ENDCRED.TRE','HELP.TRE','SCORERS.TRE','KIACRED.TRE','ERRORMSG.TRE','AUTOSAVE.TRE']
+
+wavfiles = []
+wavfilesNoDups = []
+actorPropertyEntries = [] #[0]:id, [1]:ShortHand Name [2]:Full Name
+actorPropertyEntriesWasInit = False
+
+
+# strFileName should be the full file name (including extension)
+def calculateFoldHash(strFileName):
+ i = 0
+ hash = 0
+ strParam = strFileName.upper()
+ lenFileName = len(strParam);
+ while i < lenFileName and i < 12:
+ groupSum = 0
+ # work in groups of 4 bytes
+ for j in range(0, 4):
+ # LSB first, so the four letters in the string are re-arranged (first letter goes to lower place)
+ groupSum >>= 8;
+ if (i < lenFileName):
+ groupSum |= (ord(strParam[i]) << 24)
+ i += 1
+ else: # if i >= lenFileName but still haven't completed the four byte loop add 0s
+ groupSum |= 0
+ hash = ((hash << 1) | ((hash >> 31) & 1)) + groupSum
+ hash &= 0xFFFFFFFF # mask here!
+ #print (strParam +': ' +''.join('{:08X}'.format(hash)))
+ return hash
+
+# Fill the actorPropertyEntries table
+def initActorPropertyEntries():
+ global actorPropertyEntriesWasInit
+ global actorPropertyEntries
+ firstLine = True
+# print "opening actornames"
+ with open("./actornames.txt") as tsv:
+ for line in csv.reader(tsv, dialect="excel-tab"):
+ #skip first line header
+ if firstLine == True:
+# print "skippingHeader"
+ firstLine = False
+ else:
+ actorPropertyEntries.append(line)
+ actorPropertyEntriesWasInit = True
+ tsv.close()
+
+def getActorShortNameById(lookupActorId):
+ global actorPropertyEntriesWasInit
+ global actorPropertyEntries
+ if not actorPropertyEntriesWasInit:
+ return ''
+ else:
+ for actorEntryTmp in actorPropertyEntries:
+ if int(actorEntryTmp[0]) == int(lookupActorId):
+ return actorEntryTmp[1]
+ return ''
+
+
+def getActorFullNameById(lookupActorId):
+ global actorPropertyEntriesWasInit
+ global actorPropertyEntries
+ if not actorPropertyEntriesWasInit:
+ return ''
+ else:
+ for actorEntryTmp in actorPropertyEntries:
+ if int(actorEntryTmp[0]) == int(lookupActorId):
+ return actorEntryTmp[2]
+ return ''
+
+def getActorIdByShortName(lookupActorShortName):
+ global actorPropertyEntriesWasInit
+ global actorPropertyEntries
+ if not actorPropertyEntriesWasInit:
+ return ''
+ else:
+ for actorEntryTmp in actorPropertyEntries:
+ if actorEntryTmp[1] == lookupActorShortName:
+ return actorEntryTmp[0].zfill(2)
+ return ''
+
+def getActorShortNameAndLocalQuoteIdByAUDHashID(audHashId):
+ actorId = 0
+ actorShortName = ''
+ actorLocalQuoteId = 0
+ if not actorPropertyEntriesWasInit:
+ print "Error actor properties were not initialized!"
+ return (actorId, actorShortName, actorLocalQuoteId)
+
+ for actorEntryTmp in actorPropertyEntries:
+ if( (audHashId - (int(actorEntryTmp[0]) * 10000) ) >= 0) and ((audHashId - (int(actorEntryTmp[0]) * 10000)) < 10000):
+ actorId = int(actorEntryTmp[0])
+ actorShortName = actorEntryTmp[1]
+ actorLocalQuoteId = audHashId - (actorId * 10000)
+ return (actorId, actorShortName, actorLocalQuoteId)
+ return (actorId, actorShortName, actorLocalQuoteId)
+
+# Aux. Ensure existence of output directory
+def ensure_dir(directory):
+ try:
+ os.makedirs(directory)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+
+#
+# Reading in the INPUT TLK files and checking all the AUD file properties
+#
+def inputTLKsExtract(inputTLKpath, outputWAVpath):
+ # try to open all TLK file entries from supportedTLKInputFiles
+ # then per TLK file
+ # create an output folder in the OUTPUT PATH named TLK## for the 1, 2, 3 TLK and TLKSPCHSFX for the SPCHSFX.TLK
+ # printout:
+ # total entries
+ # total data size
+ # and per entry the
+ # fileID
+ # segment offset
+ # file size
+ print "Checking in %s for TLK files to extract to %s" % (inputTLKpath, outputWAVpath)
+ inputTLKFilesFound = []
+ # breaking after first for loop yields only the top directory files, which is what we want
+ for (dirpath, dirnames, filenames) in walk(inputTLKpath):
+ for filename in filenames:
+ for tlkTuple in supportedTLKInputFiles:
+ if filename.upper() == tlkTuple[0]:
+ inputTLKFilesFound.append(tlkTuple)
+ break
+ for tmpTLKfileTuple in inputTLKFilesFound:
+ print "Found TLK: %s" % ('"' + inputTLKpath + tmpTLKfileTuple[0] + '"')
+ errorFound = False
+ inTLKFile = None
+ #
+ # Create output folder if not exists at output path
+ print "Ensuring output directory %s" % (os.path.join(outputWAVpath, tmpTLKfileTuple[1] ))
+ ensure_dir(os.path.join(outputWAVpath, tmpTLKfileTuple[1] ) )
+ try:
+ inTLKFile = open(os.path.join(inputTLKpath,tmpTLKfileTuple[0]), 'rb')
+ except:
+ errorFound = True
+ print "Unexpected error:", sys.exc_info()[0]
+ raise
+ if not errorFound:
+ tmpBuff = inTLKFile.read(2)
+ # H: unsigned short (2 bytes) followed by I: unsigned int (4 bytes)
+ tlkFileEntriesNumTuple = struct.unpack('H', tmpBuff)
+ numOfEntriesToExtract = tlkFileEntriesNumTuple[0]
+ tmpBuff = inTLKFile.read(4)
+ tlkFileDataSegmentSizeTuple = struct.unpack('I', tmpBuff)
+ allTlkFileSize = tlkFileDataSegmentSizeTuple[0]
+ inTLKFile.seek(0, 2) # go to file end
+ allActualBytesInMixFile = inTLKFile.tell()
+ inTLKFile.seek(6, 0) # go to start of table of TLK file entries (right after the 6 bytes header)
+ # 2 + 4 = 6 bytes short MIX header
+ # 12 bytes per TLK entry in entries table
+ # quick size validation
+ print "Entries: %d, data segment %d bytes" % (numOfEntriesToExtract, allTlkFileSize)
+ if allActualBytesInMixFile != 2 + 4 + 12 * numOfEntriesToExtract + allTlkFileSize:
+ print "Error: TLK file size mismatch with reported size in header for %s!" % (tmpTLKfileTuple[0])
+ else:
+ #
+ # 12 bytes per entry
+ # 4 bytes: ID
+ # 4 bytes: Offset in data segment
+ # 4 bytes: Size of data
+ #
+ for i in range(0, numOfEntriesToExtract):
+ inTLKFile.seek(2 + 4 + 12*i)
+ tmpBuff = inTLKFile.read(4)
+ tmpRdTuple = struct.unpack('I', tmpBuff)
+ idOfAUDEntry = tmpRdTuple[0]
+ tmpBuff = inTLKFile.read(4)
+ tmpRdTuple = struct.unpack('I', tmpBuff)
+ offsetOfAUDEntry = tmpRdTuple[0]
+ tmpBuff = inTLKFile.read(4)
+ tmpRdTuple = struct.unpack('I', tmpBuff)
+ sizeOfAUDEntry = tmpRdTuple[0]
+ print "Entry: %s, offset %s, data segment %s bytes" % (''.join('{:08X}'.format(idOfAUDEntry)), ''.join('{:08X}'.format(offsetOfAUDEntry)),''.join('{:08X}'.format(sizeOfAUDEntry)))
+ #
+ # put file in AUD object
+ # do we need AUD decode?
+ # create WAV from AUD
+ # write WAV to appropriate output folder
+ # Figure out proper naming for file
+ # then:
+ # AudFile aud;
+ # aud.loadAudFile(fs); (fs is file stream)
+ # aud.extract_as_wav(fs, offset, offset + int(sizeof(AudHeader)) + aud.header().size_in, target);
+ #
+ #
+ inTLKFile.seek(2 + 4 + 12*numOfEntriesToExtract + offsetOfAUDEntry)
+ if(offsetOfAUDEntry + sizeOfAUDEntry > allTlkFileSize):
+ print "Error: AUD file size mismatch with reported size in entry header!"
+ else:
+ audFileBuffer = inTLKFile.read(sizeOfAUDEntry)
+ if (len(audFileBuffer) == sizeOfAUDEntry):
+ # load Aud file
+ thisAudFile = audFile()
+ if (thisAudFile.loadAudFile(audFileBuffer, allTlkFileSize)):
+ # print "AUD file load successful!"
+ # find
+ # print "Emulating Wav write to appropriate folder..."
+ (actorID, actorSName, localQuoteId) = getActorShortNameAndLocalQuoteIdByAUDHashID(idOfAUDEntry)
+ targetSimpleFileName = actorSName + '_' + str(localQuoteId).zfill(4) + '_' + ''.join('{:08X}'.format(idOfAUDEntry)).upper()+'.WAV'
+ #print os.path.join(outputWAVpath, tmpTLKfileTuple[1], targetSimpleFileName)
+ if not os.path.isfile(os.path.join(outputWAVpath, tmpTLKfileTuple[1], targetSimpleFileName) ):
+ thisAudFile.extract_as_wav(audFileBuffer, os.path.join(outputWAVpath, tmpTLKfileTuple[1], targetSimpleFileName) )
+ else:
+ print "Output file %s already exists. Skipping..." % (os.path.join(outputWAVpath, tmpTLKfileTuple[1], targetSimpleFileName))
+ else:
+ print "Error while LOADING aud file!"
+ else:
+ print "Error while reading AUD file %s into mem buffer" % (''.join('{:08X}'.format(idOfAUDEntry)))
+ inTLKFile.close()
+
+
+ # SYS EXIT IS HERE ONLY FOR DEBUG PURPOSES OF PARSING TLK FILES - SHOULD BE COMMENTED OUT NORMALLY
+ # sys.exit(0)
+ return
+
+def inputMIXExtractTREs(inputMIXpath, excelOutBook = None):
+ print "Checking in %s for MIX files to extract TRE's from" % (inputMIXpath)
+ inputMIXFilesFound = []
+ # breaking after first for loop yields only the top directory files, which is what we want
+ for (dirpath, dirnames, filenames) in walk(inputMIXpath):
+ for filename in filenames:
+ for mixFileName in supportedMIXInputFiles:
+ if filename.upper() == mixFileName:
+ inputMIXFilesFound.append(mixFileName)
+ break
+ for tmpMIXfileName in inputMIXFilesFound:
+ print "Found MIX: %s" % ('"' + inputMIXpath + tmpMIXfileName + '"')
+ errorFound = False
+ inMIXFile = None
+ #
+ try:
+ inMIXFile = open(os.path.join(inputMIXpath,tmpMIXfileName), 'rb')
+ except:
+ errorFound = True
+ print "Unexpected error:", sys.exc_info()[0]
+ raise
+ if not errorFound:
+ totalTREs = 0
+ tmpBuff = inMIXFile.read(2)
+ # H: unsigned short (2 bytes) followed by I: unsigned int (4 bytes)
+ mixFileEntriesNumTuple = struct.unpack('H', tmpBuff)
+ numOfEntriesToExtract = mixFileEntriesNumTuple[0]
+ tmpBuff = inMIXFile.read(4)
+ mixFileDataSegmentSizeTuple = struct.unpack('I', tmpBuff)
+ allMixFileSize = mixFileDataSegmentSizeTuple[0]
+ inMIXFile.seek(0, 2) # go to file end
+ allActualBytesInMixFile = inMIXFile.tell()
+ inMIXFile.seek(6, 0) # go to start of table of MIX file entries (right after the 6 bytes header)
+ # 2 + 4 = 6 bytes short MIX header
+ # 12 bytes per MIX entry in entries table
+ # quick size validation
+ print "Entries: %d, data segment %d bytes" % (numOfEntriesToExtract, allMixFileSize)
+ if allActualBytesInMixFile != 2 + 4 + 12 * numOfEntriesToExtract + allMixFileSize:
+ print "Error: MIX file size mismatch with reported size in header for %s!" % (tmpMIXfileName)
+ else:
+ #
+ # 12 bytes per entry
+ # 4 bytes: ID
+ # 4 bytes: Offset in data segment
+ # 4 bytes: Size of data
+ #
+ for i in range(0, numOfEntriesToExtract):
+ foundTREFile = False
+ currTreFileName = 'UNKNOWN.TRE'
+ inMIXFile.seek(2 + 4 + 12*i)
+ tmpBuff = inMIXFile.read(4)
+ tmpRdTuple = struct.unpack('I', tmpBuff)
+ idOfMIXEntry = tmpRdTuple[0]
+ tmpBuff = inMIXFile.read(4)
+ tmpRdTuple = struct.unpack('I', tmpBuff)
+ offsetOfMIXEntry = tmpRdTuple[0]
+ tmpBuff = inMIXFile.read(4)
+ tmpRdTuple = struct.unpack('I', tmpBuff)
+ sizeOfMIXEntry = tmpRdTuple[0]
+
+ for suppTREFileName in supportedExportedTREFiles:
+ if(idOfMIXEntry == calculateFoldHash(suppTREFileName)):
+ foundTREFile = True
+ currTreFileName = suppTREFileName
+ break
+
+ if (foundTREFile == True):
+ print "Entry Name: %s, Entry ID: %s, offset %s, data segment %s bytes" % (currTreFileName, ''.join('{:08X}'.format(idOfMIXEntry)), ''.join('{:08X}'.format(offsetOfMIXEntry)),''.join('{:08X}'.format(sizeOfMIXEntry)))
+ #
+ # IF TRE FILE:
+ # put file in TRE object
+ #
+ #
+ inMIXFile.seek(2 + 4 + 12*numOfEntriesToExtract + offsetOfMIXEntry)
+ if(offsetOfMIXEntry + sizeOfMIXEntry > allMixFileSize):
+ print "Error: TRE file size mismatch with reported size in entry header!"
+ else:
+ treFileBuffer = inMIXFile.read(sizeOfMIXEntry)
+ if (len(treFileBuffer) == sizeOfMIXEntry):
+ # load TRE file
+ thisTreFile = treFile()
+ if (thisTreFile.loadTreFile(treFileBuffer, allMixFileSize)):
+ print "TRE file loaded"
+ if excelOutBook != None:
+ sh = excelOutBook.add_sheet(currTreFileName)
+ n = 0 # keeps track of rows
+ col1_name = 'Text Resource File: %s' % (currTreFileName)
+ sh.write(n, 0, col1_name)
+ # Second Row
+ n = 1
+ col1_name = 'TextId'
+ col2_name = 'Text'
+ sh.write(n, 0, col1_name)
+ sh.write(n, 1, col2_name)
+ n+=1
+ for m, e1 in enumerate(thisTreFile.stringEntriesLst, n):
+ sh.write(m, 0, e1[0])
+ objStr = e1[1]
+ #print type (objUTF8SafeStr) # the type is STR here
+ # python strings are immutable (can't replace characters) but we have an issue with certain special characters in the ORIGINAL TRE (kiacred and endcred)
+ # (they are out of their order from their proper order in windwos-1252)
+ # so we need to create a new string.
+ objUTF8SafeStr = ""
+ for i in range(0, len(objStr)):
+ if (objStr[i] == '\x81'):
+ objUTF8SafeStr += 'ü'
+ elif (objStr[i] == '\x82'):
+ objUTF8SafeStr += 'é'
+ else:
+ objUTF8SafeStr += objStr[i]
+ #objUTF8Safe = objUTF8Safe.replace('\x81',u'u') #'ü' # this does not work
+ #objUTF8Safe = objUTF8Safe.replace('\x82',u'e') #'é' # this does not work
+ objUTF8Unicode = unicode(objUTF8SafeStr, 'utf-8')
+ sh.write(m, 1, objUTF8Unicode)
+
+
+ #for tupleIdString in thisTreFile.stringEntriesLst:
+ # #print "Id: %d\t Text: %s" % (tupleIdString[0], tupleIdString[1])
+ # pass
+ totalTREs = totalTREs + 1
+ else:
+ print "Error while LOADING TRE file!"
+ else:
+ print "Error while reading TRE file %s into mem buffer" % (''.join('{:08X}'.format(idOfMIXEntry)))
+ inMIXFile.close()
+ print "Total TREs: %d " % (totalTREs)
+ return
+
+
+#
+# Creating the OUTPUT XLS file with one sheet named as the @param sheet with entries based on the list1 (wav files, without duplicates)
+#
+def outputXLS(filename, sheet, list1, parseTREResourcesAlso = False, mixInputFolderPath = ''):
+ global stringReplacementForRootFolderWithExtractedFiles
+ global numReplaceStartingCharacters
+ book = xlwt.Workbook()
+ sh = book.add_sheet(sheet)
+# First Row
+ n = 0 # keeps track of rows
+# variables = [x, y, z]
+# x_desc = 'Display'
+# y_desc = 'Dominance'
+# z_desc = 'Test'
+# desc = [x_desc, y_desc, z_desc]
+#
+#
+# #You may need to group the variables together
+# #for n, (v_desc, v) in enumerate(zip(desc, variables)):
+# for n, (v_desc, v) in enumerate(zip(desc, variables)):
+# sh.write(n, 0, v_desc)
+# sh.write(n, 1, v)
+ col1_name = 'BladeRunnerTLK In-Game dialogue / voiceover quotes'
+ sh.write(n, 0, col1_name)
+# Second Row
+ n = 1
+ col1_name = 'Filename'
+ col2_name = 'Quote'
+ col3_name = 'By Actor'
+ col4_name = 'Notes'
+ col5_name = 'To Actor'
+ col6_name = 'Resource'
+ col7_name = 'ShortHandFileName'
+
+ sh.write(n, 0, col1_name)
+ sh.write(n, 1, col2_name)
+ sh.write(n, 2, col3_name)
+ sh.write(n, 3, col4_name)
+ sh.write(n, 4, col5_name)
+ sh.write(n, 5, col6_name)
+ sh.write(n, 6, col7_name)
+
+ n+=1
+
+ for m, e1 in enumerate(list1, n):
+ twoTokensOfRelDirnameAndFilenameXLS = e1.split('&', 2)
+ if len(twoTokensOfRelDirnameAndFilenameXLS) == 3:
+ fourTokensOfFilename = twoTokensOfRelDirnameAndFilenameXLS[0].split('#', 3)
+ if len(fourTokensOfFilename) == 4:
+ # fix rogue _ chars in 3rd token of filename (split at '_')
+ tmpAUDFileName = fourTokensOfFilename[0] + '-' + fourTokensOfFilename[1] + '.AUD'
+ #ActorId-QuoteId.AUD
+ sh.write(m, 0, tmpAUDFileName)
+ twoTokensOfQuote = fourTokensOfFilename[2].split('-', 1)
+ if len(twoTokensOfQuote) == 2:
+ #Quote
+ sh.write(m, 1, twoTokensOfQuote[1])
+ else:
+ #Quote
+ sh.write(m, 1, fourTokensOfFilename[2])
+ #Notes
+ sh.write(m, 3, 'TODO')
+ #byActor
+ sh.write(m, 2, fourTokensOfFilename[3])
+ #ResourceFolder
+ sh.write(m, 5, twoTokensOfRelDirnameAndFilenameXLS[1])
+ #ShortHandFileName
+ tmpActorShortHand = getActorShortNameById(fourTokensOfFilename[0])
+ shortHandFileName = tmpActorShortHand + '_' + fourTokensOfFilename[1] + '_' + fourTokensOfFilename[2] + '.WAV'
+ # real path of filename
+ realPathOfFileNameToLink = twoTokensOfRelDirnameAndFilenameXLS[2]
+ # checks if not empty
+ if stringReplacementForRootFolderWithExtractedFiles and numReplaceStartingCharacters > 0:
+ realPathOfFileNameToLink = realPathOfFileNameToLink.replace(realPathOfFileNameToLink[:numReplaceStartingCharacters], stringReplacementForRootFolderWithExtractedFiles)
+
+ #works in Linux + Libreoffice
+ # also works in Windows + LibreOffice (run from msys) -- tried something like:
+ # python sortBladeRunnerWavs.py -p /g/WORKSPACE/BladeRunnerWorkspace/br-mixer-master/data/WAV -m "G:/WORKSPACE/BladeRunnerWorkspace/br-mixer-master/data/WAV"
+ #TODO put real full path for each file as FILE URL, and real (or approximate shorthand file name as alias)
+ hyperlinkAudioFormula = 'HYPERLINK("file://%s","%s")' % (realPathOfFileNameToLink, shortHandFileName)
+ sh.write(m, 6, Formula(hyperlinkAudioFormula))
+ else:
+ sh.write(m, 0, e1)
+ #Notes
+ sh.write(m, 3, 'error')
+ else:
+ sh.write(m, 0, e1)
+ #Notes
+ sh.write(m, 3, 'error')
+
+
+ # works for filenames where some rogue greek characters exist
+ #sh.write(m, 0, str.decode("%s" % e1, 'utf-8'))
+
+# for m, e2 in enumerate(list2, n+1):
+# sh.write(m, 1, e2)
+
+ if parseTREResourcesAlso == True and mixInputFolderPath != '':
+ inputMIXExtractTREs(mixInputFolderPath, book)
+ # TODO add sheets
+ # TODO handle special string characters (to UTF-8)
+
+ book.save(filename)
+
+#
+#
+#
+# ########################
+# main
+# 00_0000 -- DealsInInsects dupl TLK01, TLK0A
+# 00_0510 -- ThinkingOfChangingJobs-Leon dupl TLK02, TLK03
+# 00-8520 -- WhatDoYouKnow dupl TLK01, TLK0A
+
+# Total unique quotes seems to be 5495!
+# TODO rename files in folders to conform to the underscore '_' and '-' format (a few don't -- let's have them all conforming!)
+# #########################
+#
+if __name__ == "__main__":
+ TMProotFolderWithExtractedFiles = ""
+ TMProotFolderWithInputTLKFiles = ""
+
+ extractWavFilesMode = False
+ extractTreFilesMode = False
+
+ invalidSyntax = False
+# print "Len of sysargv = %s" % (len(sys.argv))
+ if len(sys.argv) == 2:
+ if(sys.argv[1] == '--help'or sys.argv[1] == '-h'):
+ print "%s %s supports Blade Runner (English version, CD edition)." % (app_name_spaced, app_version)
+ print "Created by Praetorian of the classic adventures in Greek team."
+ print "Always keep backups!"
+ print "--------------------"
+ print "Preparatory steps:"
+ print "1. Put actornames.txt file in the same folder with this tool."
+ print "--------------------"
+ print "%s takes has one mandatory argument, the folder of the extracted WAV files:" % (app_name_spaced)
+ print "Valid syntax: %s -ip [folderpath_for_TLK_Files] -op [folderpath_for_extracted_wav_Files] -m [stringPathToReplaceFolderpathInExcelLinks]" % (app_name)
+ print "The -op switch has an argument that is the path for extracted WAV files folder. The -op switch is REQUIRED always."
+ print "The -ip switch has an argument that is the path for the input (TLK or MIX) files folder (can be the same as the Blade Runner installation folder)."
+ print "The -m switch has an argument that is a replacement string for the path to the folder of extracted WAV files which will be used as a prefix for the links in the output XLS file."
+ print "The -xwav switch enables the WAV audio extract mode from the TLK files. It requires an INPUT path to be set with the -ip switch."
+ print "The -xtre switch enables the TRE parsing mode from the original MIX files. It requires an INPUT path to be set with the -ip switch."
+ print "If the app finishes successfully a sortedWavs.xls file will be created in the same folder with the app."
+ print "--------------------"
+ print "Thank you for using this app."
+ print "Please provide any feedback to: %s " % (company_email)
+ sys.exit()
+ elif(sys.argv[1] == '--version' or sys.argv[1] == '-v'):
+ print "%s %s supports Blade Runner (English version, CD edition)." % (app_name_spaced, app_version)
+ print "Please provide any feedback to: %s " % (company_email)
+ sys.exit()
+ else:
+ invalidSyntax = True
+ elif len(sys.argv) > 2:
+ for i in range(1, len(sys.argv)):
+ if( i < (len(sys.argv) - 1) and sys.argv[i][:1] == '-' and sys.argv[i+1][:1] != '-'):
+ if (sys.argv[i] == '-op'):
+ TMProotFolderWithExtractedFiles = sys.argv[i+1]
+ numReplaceStartingCharacters = len(TMProotFolderWithExtractedFiles)
+ elif (sys.argv[i] == '-ip'):
+ TMProotFolderWithInputTLKFiles = sys.argv[i+1]
+ elif (sys.argv[i] == '-m'):
+ stringReplacementForRootFolderWithExtractedFiles = sys.argv[i+1]
+ elif (sys.argv[i] == '-xwav'):
+ print "Extract WAVs from TLK files mode enabled."
+ extractWavFilesMode = True
+ elif (sys.argv[i] == '-xtre'):
+ print "Extract TRE mode enabled."
+ extractTreFilesMode = True
+ if not TMProotFolderWithExtractedFiles: # this argument is mandatory
+ invalidSyntax = True
+
+ if (extractWavFilesMode == True or extractTreFilesMode == True) and (TMProotFolderWithInputTLKFiles == ''):
+ invalidSyntax = True
+
+ if not invalidSyntax:
+
+ # parse Actors files:
+ initActorPropertyEntries()
+# for actorEntryTmp in actorPropertyEntries:
+# print "Found actor: %s %s %s" % (actorEntryTmp[0], actorEntryTmp[1], actorEntryTmp[2])
+ #
+ # Checking for the optional case of parsing the input TLK files to extract to WAV
+ #
+ if TMProotFolderWithInputTLKFiles != '':
+ if (extractWavFilesMode == True):
+ inputTLKsExtract(TMProotFolderWithInputTLKFiles, TMProotFolderWithExtractedFiles)
+ #if (extractTreFilesMode == True):
+ # inputMIXExtractTREs(TMProotFolderWithInputTLKFiles)
+ #
+ # Parsing the extracted WAV files
+ #
+ print "Parsing the extracted WAV audio files. Please wait (it could take a while)..."
+ for (dirpath, dirnames, filenames) in walk(TMProotFolderWithExtractedFiles):
+ for nameIdx, nameTmp in enumerate(filenames):
+ relDirName = ''
+# os.path.split would Split the pathname path into a pair, (head, tail) where tail is the last pathname component and head is everything leading up to that. The tail part will never contain a slash
+ pathTokens = dirpath.split(os.path.sep)
+ for pTokenTmp in pathTokens:
+ if pTokenTmp.find("TLK") != -1:
+ relDirName = pTokenTmp
+# print os.path.dirname(dirpath)
+# print os.path.abspath(os.path.join(os.path.join(dirpath, nameTmp), os.pardir))
+ filenames[nameIdx] = filenames[nameIdx] +'&' + relDirName + '&' + os.path.join(dirpath, nameTmp)
+ wavfiles.extend(filenames)
+# break
+ for fileIdx, filenameTmp in enumerate(wavfiles):
+ twoTokensOfFilenameAndRelDirname = filenameTmp.split('&', 1)
+ if len(twoTokensOfFilenameAndRelDirname) != 2:
+ print "ERROR in filename and rel dirname split: %s" % (filenameTmp)
+ sys.exit(0)
+ twoTokensOfFilenameForExt = twoTokensOfFilenameAndRelDirname[0].split('.', 1)
+ if len(twoTokensOfFilenameForExt) == 2:
+ if twoTokensOfFilenameForExt[1] != 'WAV' and twoTokensOfFilenameForExt[1] != 'wav':
+ print "ERROR in proper extension (not WAV): %s" % (twoTokensOfFilenameAndRelDirname[0])
+ sys.exit(0)
+ else:
+ print "ERROR in extension split: %s" % (twoTokensOfFilenameAndRelDirname[0])
+ sys.exit(0)
+ #remove WAV extension here
+# filenameTmp = twoTokensOfFilenameAndRelDirname[0] + '&' + twoTokensOfFilenameForExt[0]
+# print "Found %s" % (filenameTmp)
+
+ threeTokensOfFilename = twoTokensOfFilenameForExt[0].split('_', 2)
+ if len(threeTokensOfFilename) == 3:
+ # fix rogue _ chars in 3rd token of filename (split at '_')
+ threeTokensOfFilename[2] = threeTokensOfFilename[2].replace("_", "-")
+ # Replace first token
+ # replace actor name shorthand with ActorID in first part
+ tmpActorId = getActorIdByShortName(threeTokensOfFilename[0])
+ tmpActorFullName = ''
+ if(tmpActorId != '' and tmpActorId is not None):
+ tmpActorFullName = getActorFullNameById(tmpActorId)
+ if(tmpActorFullName != '' and tmpActorFullName is not None):
+ threeTokensOfFilename[0] = tmpActorId.zfill(2)
+ threeTokensOfFilename.append(tmpActorFullName)
+ else:
+ #fatal error if something cannot convert to spot it immediately
+ print "ERROR in actorIdMatch match: %s %s" % (tmpActorId, twoTokensOfFilenameForExt[0])
+ sys.exit(0)
+ else:
+ #fatal error if something cannot convert to spot it immediately
+ print "ERROR in shorthand match: %s %s" % (threeTokensOfFilename[0], twoTokensOfFilenameForExt[0])
+ sys.exit(0)
+#
+#
+# foundMatchForActorShortHand = False
+# for actorEntryTmp in actorPropertyEntries:
+# if actorEntryTmp[1] == threeTokensOfFilename[0]:
+# threeTokensOfFilename[0] = actorEntryTmp[0].zfill(2)
+# threeTokensOfFilename.append(actorEntryTmp[2])
+# foundMatchForActorShortHand = True
+# break
+ # end of replace actor name shorthand
+ twoTokensOfFilenameForExt[0] = '#'.join(threeTokensOfFilename)
+ filenameTmp = twoTokensOfFilenameForExt[0] + '&' + twoTokensOfFilenameAndRelDirname[1]
+ wavfiles[fileIdx] = filenameTmp
+ else:
+ print "ERROR in spliting tokens on _: %s" % (filenameTmp)
+ sys.exit(0)
+ #sort in-place
+ #
+ #
+ wavfiles.sort()
+# #
+# #
+# # Code for renaming non conforming filenames - just to be consistent in file naming
+# # TO BE RUN ONCE FOR CONFORMANCE. No NEED TO Re-RUN
+# # If its run though, comment this section and re-run the tool to get proper links in Excel file
+# #
+# for filenameSrcTmp in wavfiles:
+# # get real full path from last token when split at &
+# # create target full path from the parentdir of last token and the current state of first 3 tokens when splitting at '#'
+# # report mismatch
+# # print (BUT DON'T PROCEED AT THIS POINT) what you would rename to what.
+# threeTokensOfFilenameAndRelDirname = filenameSrcTmp.split('&', 2)
+# currentSrcFullPath = threeTokensOfFilenameAndRelDirname[2]
+# fourTokensOfTargetFileName = threeTokensOfFilenameAndRelDirname[0].split('#', 3)
+# tmpActorShortHand = getActorShortNameById(fourTokensOfTargetFileName[0])
+# targetFileName = tmpActorShortHand + '_' + fourTokensOfTargetFileName[1] + '_' + fourTokensOfTargetFileName[2] + '.WAV'
+# # os.path.split would Split the pathname path into a pair, (head, tail) where tail is the last pathname component and head is everything leading up to that. The tail part will never contain a slash
+# (srcParentDir, srcTail) = os.path.split(currentSrcFullPath)
+# targetFullPath = os.path.join(srcParentDir, targetFileName)
+# # os.rename(src, dst)
+# if(currentSrcFullPath != targetFullPath):
+# print currentSrcFullPath
+# print targetFullPath
+# os.rename(currentSrcFullPath, targetFullPath)
+ #
+ # END OF: Code for renaming non conforming filenames - just to be consistent in file naming
+ #
+ #
+ for filenameSrcTmp in wavfiles:
+ duplicateFound = False
+# print "Converted %s" % (filenameSrcTmp)
+ # Weed out duplicates by copying to another table (quick and dirty)
+ twoTokensOfRelDirnameAndFilenameSrc = filenameSrcTmp.split('&', 2)
+ tmpRelDirNameSrc = twoTokensOfRelDirnameAndFilenameSrc[1]
+ threeTokensOfQuoteFilenameSrc = twoTokensOfRelDirnameAndFilenameSrc[0].split('#', 2)
+ #concatenate actorID and quoteID for search key
+ keyForDuplicateSearchSrc = threeTokensOfQuoteFilenameSrc[0] + threeTokensOfQuoteFilenameSrc[1]
+ for fileTargIdx, filenameTargTmp in enumerate(wavfilesNoDups):
+ twoTokensOfRelDirnameAndFilenameTarg = filenameTargTmp.split('&', 2)
+ tmpRelDirNameTarg = twoTokensOfRelDirnameAndFilenameTarg[1]
+ threeTokensOfQuoteFilenameTarg = twoTokensOfRelDirnameAndFilenameTarg[0].split('#', 2)
+ #concatenate actorID and quoteID for search key
+ keyForDuplicateSearchTarg = threeTokensOfQuoteFilenameTarg[0] + threeTokensOfQuoteFilenameTarg[1]
+ if(keyForDuplicateSearchSrc == keyForDuplicateSearchTarg):
+ #print "Found duplicate %s" % (filenameSrcTmp)
+ duplicateFound = True
+ wavfilesNoDups[fileTargIdx] = twoTokensOfRelDirnameAndFilenameTarg[0] + '&' + tmpRelDirNameSrc + ',' + tmpRelDirNameTarg + '&' + twoTokensOfRelDirnameAndFilenameTarg[2]
+ break
+ if(duplicateFound == False):
+ wavfilesNoDups.append(filenameSrcTmp)
+# for filenameSrcTmp in wavfilesNoDups:
+# print "Unique %s" % (filenameSrcTmp)
+
+ print "Creating output excel %s file..." % (OUTPUT_XLS_FILENAME)
+ outputXLS(OUTPUT_XLS_FILENAME, OUTPUT_XLS_QUOTES_SHEET, wavfilesNoDups, extractTreFilesMode, TMProotFolderWithInputTLKFiles)
+ else:
+ invalidSyntax = True
+
+ if invalidSyntax == True:
+ print "Invalid syntax\n Try: \n %s -op [folderpath_for_extracted_wav_Files] \n %s --help for more info \n %s --version for version info " % (app_name, app_name, app_name)
+ tmpi = 0
+ for tmpArg in sys.argv:
+ if tmpi==0: #skip first argument
+ tmpi+=1
+ continue
+ print "\nArgument: %s" % (tmpArg)
+ tmpi+=1
+else:
+ ## debug
+ #print '%s was imported from another module' % (app_name_spaced,)
+ pass
diff --git a/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/treFileLib.py b/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/treFileLib.py
new file mode 100644
index 0000000000..b9d147248d
--- /dev/null
+++ b/devtools/blade_runner/subtitles/quotesSpreadsheetCreator/treFileLib.py
@@ -0,0 +1,107 @@
+#!/usr/bin/python
+# -*- coding: UTF-8 -*-
+#
+import os, sys, shutil
+import struct
+from struct import *
+
+my_module_version = "0.50"
+my_module_name = "treFileLib"
+
+
+class TreHeader:
+ numOfTextResources = -1
+ def __init__(self):
+ return
+
+
+class treFile:
+ m_header = TreHeader()
+ stringEntriesLst = [] # list of two-value tuples. First value is ID, second value is String content
+ stringOffsets = []
+ def __init__(self):
+ del self.stringEntriesLst[:]
+ del self.stringOffsets[:]
+ return
+
+ def loadTreFile(self, treBytesBuff, maxLength):
+ offsInTreFile = 0
+ #
+ # parse TRE file fields for header
+ #
+ try:
+ tmpTuple = struct.unpack_from('I', treBytesBuff, offsInTreFile) # unsigned integer 4 bytes
+ self.header().numOfTextResources = tmpTuple[0]
+ offsInTreFile += 4
+ #
+ # string IDs table (each entry is unsigned integer 4 bytes)
+ #
+ print "Total texts in TRE: %d" % (self.header().numOfTextResources)
+ for idx in range(0, self.header().numOfTextResources):
+ tmpTuple = struct.unpack_from('I', treBytesBuff, offsInTreFile) # unsigned integer 4 bytes
+ self.stringEntriesLst.append( (tmpTuple[0], '') )
+ offsInTreFile += 4
+
+ # string offsets table (each entry is unsigned integer 4 bytes)
+ for idx in range(0, self.header().numOfTextResources):
+ tmpTuple = struct.unpack_from('I', treBytesBuff, offsInTreFile) # unsigned integer 4 bytes
+ self.stringOffsets.append( tmpTuple[0] )
+ offsInTreFile += 4
+ #
+ # strings (all entries are null terminated)
+ # TODO +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ absStartOfIndexTable = 4
+ #absStartOfOffsetTable = absStartOfIndexTable + (self.header().numOfTextResources * 4)
+ #absStartOfStringTable = absStartOfOffsetTable + ((self.header().numOfTextResources+1) * 4)
+
+ #print "buffer type " , type(treBytesBuff) # it is str
+
+ for idx in range(0, self.header().numOfTextResources):
+ currOffset = self.stringOffsets[idx] + absStartOfIndexTable
+ # the buffer (treBytesBuff) where we read the TRE file into, is "str" type but contains multiple null terminated strings
+ # the solution here (to not get out of index errors when reading the null terminator points) is
+ # to split the substring starting at the indicated offset each time, at the null character, and get the first string token.
+ # This works ok.
+ #
+ allTextsFound = treBytesBuff[currOffset:].split('\x00')
+ # check "problematic" character cases:
+ if currOffset == 5982 or currOffset == 6050 or currOffset == 2827 or currOffset == 2880:
+ print "Offs: %d\tFound String: %s" % ( currOffset,''.join(allTextsFound[0]) )
+ #print "Offs: %d\tFound String: %s" % ( currOffset,''.join(allTextsFound[0]) )
+ (theId, stringOfIdx) = self.stringEntriesLst[idx]
+ self.stringEntriesLst[idx] = (theId, ''.join(allTextsFound[0]))
+ #print "ID: %d\tFound String: %s" % ( theId,''.join(allTextsFound[0]) )
+ return True
+ except:
+ print "Loading failure!"
+ return False
+
+ def header(self):
+ return self.m_header
+#
+#
+#
+if __name__ == '__main__':
+ # main()
+ print "Running %s as main module" % (my_module_name)
+ # assumes a file of name ACTORS.TRE in same directory
+ inTREFile = None
+ errorFound = False
+ try:
+ inTREFile = open(os.path.join('.','ACTORS.TRE'), 'rb')
+ except:
+ errorFound = True
+ print "Unexpected error:", sys.exc_info()[0]
+ raise
+ if not errorFound:
+ allOfTreFileInBuffer = inTREFile.read()
+ treFileInstance = treFile()
+ if (treFileInstance.loadTreFile(allOfTreFileInBuffer, len(allOfTreFileInBuffer))):
+ print "TRE file loaded successfully!"
+ else:
+ print "Error while loading TRE file!"
+ inTREFile.close()
+else:
+ #debug
+ #print "Running %s imported from another module" % (my_module_name)
+ pass \ No newline at end of file