1
0
mirror of https://github.com/Escartem/AnimeWwise.git synced 2026-06-04 23:40:25 +08:00
Files
AnimeWwise/wavescan.py
2026-02-03 18:26:48 -05:00

254 lines
6.2 KiB
Python

# Custom rewrite of the Wwise AKPK packages extractor, original by Nicknine and bnnm
import os
import io
import traceback
from bnk import bnk2wem
from vfs import decrypt
from filereader import FileReader
reader = None
bank_version = 0
wwise_data = []
filename = ""
def get_data(data, _filename):
global wwise_data
global bank_version
global reader
global filename
filename = _filename
wwise_data = []
reader = FileReader(io.BytesIO(data), "little")
vfs = False
# check file
magic = reader.ReadBytes(4)
if magic == b":)xD":
# file is endfield VFS
print("file is VFS !!")
vfs = True
reader.SetBufferPos(4)
header_size = reader.ReadUInt32()
print(header_size)
reader.SetBufferPos(0)
header = bytearray(reader.ReadBytes(header_size+8))
decrypt(header, 12, header_size - 4, header_size, 0)
# recreate file
dec_data = bytearray()
dec_data += header
dec_data += data[header_size+8:]
dec_data[0:4] = b"AKPK"
dec_data[8:12] = (1).to_bytes(4, "little")
data = dec_data
reader = FileReader(io.BytesIO(data), "little") # reset reader
magic = reader.ReadBytes(4)
if magic != b"AKPK":
# file.close()
raise Exception("not a valid audio file")
# check endianness
reader.SetBufferPos(0x08)
endian_check = reader.ReadLong() # this is the same bytes as the flag sector, which seems to be always 1
if endian_check == 1:
endianness = 0 # little
elif endian_check == 0x1000000:
endianness = 1 # big
else:
raise Exception("couldn't detect endianness")
# retrieve sectors in header
reader.SetBufferPos(0x04)
header_size = reader.ReadLong()
flag = reader.ReadLong()
languages_sector_size = reader.ReadLong()
banks_sector_size = reader.ReadLong()
sounds_sector_size = reader.ReadLong()
externals_sector_size = 0
if languages_sector_size + banks_sector_size + sounds_sector_size + 0x10 < header_size:
externals_sector_size = reader.ReadLong()
sectors = [[True, banks_sector_size, 0, 0, "bnk"], [False, sounds_sector_size, 1, 0, "wem"], [False, externals_sector_size, 1, 1, "wem"]]
# get langs in the file
try:
lang_array = get_langs(languages_sector_size)
except Exception as e:
raise Exception(f"failed to read languages, {e}, {traceback.format_exc()}")
# extract each sector
curr_sector = None
try:
for sector in sectors:
curr_sector = sector
extract_sector(*sector[1:], endianness, lang_array, bank_version)
if sector[0] and bank_version == 0:
if externals_sector_size == 0:
print("can't detect bank version")
bank_version = 62
except Exception as e:
raise Exception(f"failed to extract sector {curr_sector}, {e}, {traceback.format_exc()}")
return wwise_data
def get_langs(langs_sector_size):
string_offset = reader.GetBufferPos()
lang_array = {}
langs = reader.ReadLong()
for i in range(langs):
lang_offset = reader.ReadLong()
lang_id = reader.ReadLong()
lang_offset += string_offset
current = reader.GetBufferPos()
reader.SetBufferPos(lang_offset)
# get dummy bytes to detect encoding
test_byte_1 = reader.ReadBytes(1)
test_byte_2 = reader.ReadBytes(1)
reader.SetBufferPos(lang_offset)
if test_byte_1 == 0 or test_byte_2 == 0:
lang_name = reader.ReadBytes(0x20).decode("utf-16le", "ignore").replace("\x00", "")
else:
lang_name = reader.ReadBytes(0x10).decode("utf-8", "ignore").replace("\x00", "")
lang_array[lang_id] = lang_name
reader.SetBufferPos(current)
reader.SetBufferPos(string_offset + langs_sector_size)
return lang_array
def detect_bank_version(offset):
global bank_version
current = reader.GetBufferPos()
reader.SetBufferPos(offset)
# maybe update buffer pos instead
dummy = reader.ReadLong()
dummy = reader.ReadLong()
bank_version = reader.ReadLong()
if bank_version > 0x1000:
print("wrong bank version")
bank_version = 62
reader.SetBufferPos(current)
def extract_sector(section_size, is_sounds, is_externals, ext, endianness, lang_array, bank_version, filter_bnk_only=0, filter_wem_only=0, include_name=False):
global wwise_data
# check sector validity
if section_size == 0:
return
files = reader.ReadLong()
if files == 0:
return
entry_size = (section_size - 0x04) / files
if entry_size == 0x18:
alt_mode = 1
else:
alt_mode = 0
for i in range(files):
# ids must be unsigned here, if signed you need to do id += 2**32 afterwards
if alt_mode == 1 and is_externals == 1:
if endianness == 0:
file_id_2 = reader.ReadULong()
file_id_1 = reader.ReadULong()
else:
file_id_1 = reader.ReadULong()
file_id_2 = reader.ReadULong()
else:
file_id = reader.ReadULong()
block_size = reader.ReadLong()
# get file size
if alt_mode == 1 and is_externals == 1:
size = reader.ReadLong()
elif alt_mode == 1:
size = reader.ReadLongLong()
else:
size = reader.ReadLong()
offset = reader.ReadLong()
lang_id = reader.ReadLong()
if block_size != 0:
offset *= block_size
# bank version must be detected at this offset
if is_sounds == 0 and bank_version == 0:
detect_bank_version(offset)
# update extension for olders banks using differents codecs
if is_sounds == 1 and bank_version < 62:
current = reader.GetBufferPos()
codec_offset = offset + 0x14
reader.SetBufferPos(codec_offset)
codec = reader.ReadInt16()
if codec == 0x0401 or codec == 0x0166:
ext = "xma"
elif codec == 0xFFFF:
ext = "ogg"
else:
ext = "wav"
reader.SetBufferPos(current)
# set file path
if lang_id == 0:
path = ""
else:
path = "".join([f"{e}/" for e in list(lang_array.values())])
# set file name
if alt_mode == 1 and is_externals == 1:
name = f"externals/{path}{file_id_1:08x}{file_id_2:08x}.{ext}"
else:
name = f"{path}{file_id}.{ext}"
# filtering utilities
if filter_bnk_only == 1 and ext != "bnk":
continue
if filter_wem_only == 1 and ext != "wem":
continue
# file infos
if ext == "bnk":
# get data from bnk
pos = reader.GetBufferPos()
reader.SetBufferPos(offset)
bnk_data = reader.ReadBytes(size)
reader.SetBufferPos(pos)
wems = bnk2wem(bnk_data, f"{filename}@{pos}.{size}")
for wem in wems:
wwise_data.append([f"{os.path.basename(name).split('.')[0]}_{wem[0]}.wem", offset+wem[1], wem[2], filename])
else:
wwise_data.append([os.path.basename(name), offset, size, filename])