diff --git a/CATool.py b/CATool.py new file mode 100644 index 0000000..49128d0 --- /dev/null +++ b/CATool.py @@ -0,0 +1,183 @@ +import os, sys + +def extrCombAudio(): + def find_segments(file_path): + with open(file_path, 'rb') as f: + data = f.read() + + segments = [] + start_idx = 0 + while True: + fsb5_start = data.find(b'FSB5', start_idx) + if fsb5_start == -1: + break + + fsb5_end = data.find(b'FSB5', fsb5_start + 4) + if fsb5_end == -1: + fsb5_end = len(data) + + segments.append(data[fsb5_start:fsb5_end]) + start_idx = fsb5_end + return segments + + def save_segments(segments): + os.makedirs('.\\out_path', exist_ok=True) + for i, segment in enumerate(segments): + with open(f'.\\out_path\\segment_{i}.fsb', 'wb') as f: + f.write(segment) + + file_path = sys.argv[2] + file_path = file_path.replace("\\",'/') + segments = find_segments(file_path) + save_segments(segments) + +def find_segment_name(): + file_path = sys.argv[2] + search_pattern = b'\x04\x00\x00\x00' + max_search_length = 0xFF + + try: + with open(file_path, 'rb') as f: + segment = f.read(max_search_length) + except FileNotFoundError: + print(f"File not found: {file_path}") + return None + except IOError as e: + print(f"Error reading file: {e}") + return None + + pattern_start = segment.find(search_pattern) + if pattern_start == -1: + print("Pattern not found") + return None + + string_start = pattern_start + len(search_pattern) + string_end = string_start + while string_end < len(segment) and segment[string_end] != 0x00: + string_end += 1 + + segment_name = segment[string_start:string_end].decode('utf-8') + return segment_name + +def rename_segment(): + segment_file = sys.argv[2] + segment_name1 = find_segment_name() + seg_dir = os.path.dirname(segment_file) + os.rename(segment_file,f"{seg_dir}\\{segment_name1}.fsb") + +def collect_header(): + audio_file = sys.argv[2] + os.makedirs('.\\out_path', exist_ok=True) + with open(audio_file,'rb+') as f: + data = f.read(0x1A2C) + with open(f".\\out_path\\header_data.bin",'wb') as of: + of.write(data) + +def rebCombAudio(): + audio_files = sys.argv[2] + header = f"{audio_files}\\header_data.bin" + if os.path.exists(header) != True: + print(f"Please Extract the Header from your CombinedAudio.bin File.") + sys.exit(1) + if os.path.exists(f"{audio_files}\\segment_0.fsb") != True: + print(f"This Function Requires that the Audio Files are Named into the following scheme:\n 'segment_XXX.fsb' nothing else.") + sys.exit(1) + + with open(header,'rb+') as header_file: + header_data = header_file.read() + + with open('.\\ModifiedCombinedAudio.bin','wb+') as f: + f.write(header_data) + for i in range(558): + with open(f"{audio_files}\\segment_{i}.fsb",'rb+') as sf: + fsb_data = sf.read() + f.write(fsb_data) + +def extractByName(): + audio_path = sys.argv[2] + search_text = sys.argv[3] + file_names = os.path.basename(audio_path) + directory_name = os.path.dirname(__file__) + with open(audio_path, 'rb') as file: + content = file.read() + + search_text_bytes = search_text.encode('utf-8') + fsb_magic = b'FSB5' + search_text_index = content.find(search_text_bytes) + if search_text_index == -1: + print(f"\nSearch text '{search_text}' not found in file.\nDid you mistype the Sound-ID?") + sys.exit(1) + + closest_fsb_index = content.rfind(fsb_magic, 0, search_text_index) + if closest_fsb_index == -1: + print("\nFSB5 Magic String not found before the search text.\nAre you sure this is a Soundbank FSB Archive?\n") + sys.exit(1) + + next_fsb_index = content.find(fsb_magic, closest_fsb_index + len(fsb_magic)) + if next_fsb_index == -1: + next_fsb_index = len(content) + + extracted_data = content[closest_fsb_index:next_fsb_index] + + with open(f'.\\{search_text}.fsb', 'wb') as output_file: + output_file.write(extracted_data) + + print(f"\nExtracted Soundbank FSB From '{file_names}' Archive.\nTo Output PATH: '{directory_name}\\{search_text}.fsb'.\n") + +def addPadding(): + originalFile = sys.argv[2] + modifiedFile = sys.argv[3] + with open(originalFile, 'rb') as f: + originalData = f.read() + ogDataLen = len(originalData) + + with open(modifiedFile,'rb') as mf: + modifiedData = mf.read() + modDataLen = len(modifiedData) + mf.close() + + difference = ogDataLen-modDataLen + with open(modifiedFile,'ab') as of: + for i in range(difference): + of.write(b'\x00') + +def info_help(): + print("""\n\npython CATool.py + --eca, extract-ca [CombinedAudioPATH] > Extracts All FSB Soundbank Files from the CombinedAudio.bin Archive. + --fn, find-sn [SegmentFilePATH] > Find the Segment Name from an Extracted FSB Soundbank File via --eca Flag. + --rs, rename-s [SegmentFilePATH] > Rename a Segment File FSB Soundbank File back to it's Original Filename. + --gh, get-header [CombinedAudioPATH] > Extracts the Header from the CombinedAudio.bin Archive. + --rca, rebuild-ca [SegmentOutFolderPATH] > Rebuilds the CombinedAudio.bin Archive via Segment Files. + --ap, add-padding [OriginalFilePATH] [ModifiedFilePATH] > Adds padding to set the Modified File Equal to the Original File Size. + --ne, name-extract [CombinedAudioPATH] [Sound Name/ID] > Extracts a FSB Soundbank File from the CombinedAudio.bin Archive via Name/SoundID. + --h, help > Displays this Message.\n\n\n""") + os.system('pause') + +if __name__ == '__main__': + try: + callable = sys.argv[1] + if callable == 'extract-ca' or callable == '--eca': + extrCombAudio() + + if callable == 'find-sn' or callable == '--fn': + print(find_segment_name()) + + if callable == 'rename-s' or callable == '--rs': + rename_segment() + + if callable == 'get-header' or callable == '--gh': + collect_header() + + if callable == 'rebuild-ca' or callable == '--rca': + rebCombAudio() + + if callable == 'add-padding' or callable == '--ap': + addPadding() + + if callable == "name-extract" or callable == '--ne': + extractByName() + + if callable == 'help' or callable == '--h': + info_help() + except IndexError: + info_help() \ No newline at end of file