commit 8b9a79de53a64c2c55c35202e66b563bf136bded Author: Joel Wetzell Date: Sun Feb 28 15:24:49 2021 -0600 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9104364 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +JAK1/ +JAK2/ +JAK3/ +OUT/ \ No newline at end of file diff --git a/out_convert.bat b/out_convert.bat new file mode 100644 index 0000000..9b13a54 --- /dev/null +++ b/out_convert.bat @@ -0,0 +1,5 @@ +:: This works if all the files in the VAGp folder are mono compressed ADPCM +mkdir "./OUT/WAV" +for %%f in (./OUT/VAGp/*) do ( + ffmpeg -i "./OUT/VAGp/%%f" "./OUT/WAV/%%~nf.wav" +) \ No newline at end of file diff --git a/sbk_header.txt b/sbk_header.txt new file mode 100644 index 0000000..1635c85 --- /dev/null +++ b/sbk_header.txt @@ -0,0 +1,47 @@ +This is the start of an SBK file specifically BEACH.SBK from JAK1 + + +------------??????????????????------------------ +626561636800000000000000000000000000000028000000 + +------------NAME---------------- --????-- +776F726D2D69646C6500000000000000 32000F10 +776F726D2D7269736531000000000000 32000F10 +776F726D2D73696E6B00000000000000 32000F10 +7368656C6C2D75700000000000000000 05800710 +7368656C6C2D646F776E000000000000 05800710 +736E6170000000000000000000000000 05800710 +637261622D736C696465000000000000 05800710 +6C75726B6572637261622D6469657300 05800710 +70656C6963616E2D666C617000000000 64801B10 +70656C6963616E2D67756C7000000000 64801B10 +70757070792D6261726B000000000000 19C00810 +6C75726B6572646F672D69646C650000 19C00810 +6C75726B6572646F672D646965730000 19C00810 +73656167756C6C2D74616B656F666600 3C001910 +67656172732D72756D626C6500000000 0F000F10 +726F70652D7374726574636800000000 05800710 +6D6F6E6B657900000000000000000000 05800710 +62697264000000000000000000000000 05800710 +66757365000000000000000000000000 0A800C10 +7361636B2D6C616E6400000000000000 1E001910 +6C75726B6572646F672D626974650000 05800710 +7361636B2D696E636F6D696E67000000 1E001910 +63616E6E6F6E2D73686F740000000000 96006410 +646972742D6372756D626C6500000000 19800710 +67726F74746F2D706F6C652D68697400 19800710 +6567672D637261636B00000000000000 96003210 +76656E742D726F636B2D627265616B00 50001910 +6567672D686974000000000000000000 05800710 +63616E6E6F6E2D636861726765000000 05800710 +62656163682D616D6232000000000000 32001410 +746F7765722D77696E64730000000000 05800710 +746F7765722D77696E64320000000000 32001410 +746F7765722D77696E64330000000000 05800710 +77617465722D6C617000000000000000 32001410 +776F726D2D6269746500000000000000 1E000A10 +776F726D2D7461756E74000000000000 05800710 +74656C6573636F706500000000000000 05800710 +66616C6C696E672D6567670000000000 05800710 +64726970000000000000000000000000 05800710 +776F726D2D6469657300000000000000 05800710 \ No newline at end of file diff --git a/sbkparser.py b/sbkparser.py new file mode 100644 index 0000000..963fb7d --- /dev/null +++ b/sbkparser.py @@ -0,0 +1,48 @@ +import os +import argparse + +def is_valid_file(parser, arg): + if not os.path.exists(arg): + parser.error("The file %s does not exist!" % arg) + else: + return arg + +parser = argparse.ArgumentParser(description="Process JAK SBK files") + + +parser.add_argument("-i", dest="filepath", required=True, + help="Input path to JAK SBK file", metavar="FILE", + type=lambda x: is_valid_file(parser, x)) + +args = parser.parse_args() + +filename = os.path.basename(args.filepath) + +name_length = 20 +sounds_start = 24 +sounds_length = 20 + +with open(args.filepath, "rb") as f: + soundbank_name = f.read(20).decode('utf-8').strip() + f.seek(24) + soundsFound = 0 + while 1: + empty = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + sounds_chunk = f.read(sounds_length) + if sounds_chunk == empty: + break + else: + sound_name = sounds_chunk[:-4].decode('utf-8').strip() + sound_data = sounds_chunk[-4:] + soundsFound += 1 + print(f'Found {sound_name} with some data {sound_data[0:2].hex()} {sound_data[2:].hex()}') + + + + + + + + + + diff --git a/vagparser.py b/vagparser.py new file mode 100644 index 0000000..e23f883 --- /dev/null +++ b/vagparser.py @@ -0,0 +1,117 @@ +import os +import argparse + +def is_valid_file(parser, arg): + if not os.path.exists(arg): + parser.error("The file %s does not exist!" % arg) + else: + return arg + +def load_name_from_dict(filepath, index, entrySize): + with open(filepath,'rb') as dictFile: + dictFile.seek(index*entrySize) + dictFile.seek(4,1) + return dictFile.read(8).decode('utf-8') + +def check_game(parser,arg): + valid_games = [1,2,3] + if int(arg) not in valid_games: + parser.error(f"Invalid game! Valid game numbers: {valid_games}") + else: + return int(arg) + + + +parser = argparse.ArgumentParser(description="Process JAK VAGWAD files") + + +parser.add_argument("-i", dest="filepath", required=True, + help="Input path to JAK VAGWAD file", metavar="FILE", + type=lambda x: is_valid_file(parser, x)) + +parser.add_argument("-dict", dest="dictpath", required=False, + help="Input path to JAK VAGDIR file to lookup names", metavar="FILE", + type=lambda x: is_valid_file(parser, x)) + +parser.add_argument("-game", dest="game", required=True, default="VAGp", + help="Some VAGWAD things are game specific please enter the game 1,2,3 so I can set these up", + type=lambda x: check_game(parser,x)) + +args = parser.parse_args() + +separator = 'VAGp' +dictionaryEntrySize = 12 + +# file "separator" and dictionary entry size change after Jak 1 +if(args.game != 1): + separator = 'pGAV' + dictionaryEntrySize = 16 + +#setup some byte versions of strings for finding +magic = str.encode(separator) +stereo = str.encode('Stereo') +mono = str.encode('Mono') + +#is this always 2000 bytes? +stereoInterleaveSize = 8192 + +with open(args.filepath, "rb") as f: + #setup directory for file output + if not os.path.exists("./OUT/VAGp"): + os.makedirs("./OUT/VAGp") + + contents = f.read() + fileStart = 0 + firstTime = True + previousFileStart = 0 + fileCount = 0 + skipInterleave = False + + while fileStart != -1: + previousFileStart = fileStart + if firstTime: + fileStart = contents.find(magic,fileStart) + firstTime = False + else: + #if last time the start of a stereo file was found we can start looking for the next file starting after the interleave + if skipInterleave: + fileStart = contents.find(magic,fileStart+len(magic)+stereoInterleaveSize) + skipInterleave = False + else: + fileStart = contents.find(magic,fileStart+len(magic)) + + # I don't think there is a "mono" tag to be found but this will default to mono if none of this is found + stereoLocation = contents.find(stereo) + monoLocation = contents.find(mono) + audioType = ('stereo','mono')[monoLocation= 0 and fileStart != previousFileStart): + fileCount += 1 + outfilename = str(fileCount) + # load name from dictionary for Jak 1 and 2, Jak 3 dictionary is obfuscated so skip it for now + if(args.dictpath and args.game <= 2): + outfilename = load_name_from_dict(args.dictpath,fileCount -1, dictionaryEntrySize) + print(f'{audioType} file named {outfilename} found from {previousFileStart} : {fileStart - 1}') + else: + print(f'{audioType} file #{fileCount} found from {previousFileStart} : {fileStart - 1}') + + with open(f'./OUT/VAGp/{outfilename.strip()}.VAGp','wb+') as out: + f.seek(previousFileStart) + out.write(f.read(fileStart-previousFileStart)) + out.close() + + print(f'Found {fileCount} files') + + + + + + + + +