Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 42 additions & 30 deletions PyCriCodecs/acb.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from .hca import HCA
import os


# TODO revamp the whole ACB class. ACB is a lot more complex with those @UTF tables.
class ACB(UTF):
""" An ACB is basically a giant @UTF table. Use this class to extract any ACB. """
Expand All @@ -18,46 +19,47 @@ def __init__(self, filename) -> None:
self.filename = filename
self.acbparse(self.payload)
# TODO check on ACB version.

def acbparse(self, payload: list) -> None:
""" Recursively parse the payload. """
for dict in range(len(payload)):
for k, v in payload[dict].items():
if v[0] == UTFTypeValues.bytes:
if v[1].startswith(UTFType.UTF.value): #or v[1].startswith(UTFType.EUTF.value): # ACB's never gets encrypted?
if v[1].startswith(
UTFType.UTF.value): # or v[1].startswith(UTFType.EUTF.value): # ACB's never gets encrypted?
par = UTF(v[1]).get_payload()
payload[dict][k] = par
self.acbparse(par)
self.load_awb()

def load_awb(self) -> None:
# There are two types of ACB's, one that has an AWB file inside it,
# and one with an AWB pair.
if self.payload[0]['AwbFile'][1] == b'':
if type(self.filename) == str:
awbObj = AWB(os.path.join(os.path.dirname(self.filename), self.payload[0]['Name'][1]+".awb"))
awbObj = AWB(os.path.join(os.path.dirname(self.filename), self.payload[0]['Name'][1] + ".awb"))
else:
awbObj = AWB(self.payload[0]['Name'][1]+".awb")
awbObj = AWB(self.payload[0]['Name'][1] + ".awb")
else:
awbObj = AWB(self.payload[0]['AwbFile'][1])
self.awb = awbObj

# revamping...
def exp_extract(self, decode: bool = False, key = 0):
def exp_extract(self, decode: bool = False, key=0):
# There are two types of ACB's, one that has an AWB file inside it,
# and one with an AWB pair. Or multiple AWB's.

# TODO Add multiple AWB loading.
if self.payload[0]['AwbFile'][1] == b'':
if type(self.filename) == str:
awbObj = AWB(os.path.join(os.path.dirname(self.filename), self.payload[0]['Name'][1]+".awb"))
awbObj = AWB(os.path.join(os.path.dirname(self.filename), self.payload[0]['Name'][1] + ".awb"))
else:
awbObj = AWB(self.payload[0]['Name'][1]+".awb")
awbObj = AWB(self.payload[0]['Name'][1] + ".awb")
else:
awbObj = AWB(self.payload[0]['AwbFile'][1])

pl = self.payload[0]
names = [] # Where all filenames will end up in.
names = [] # Where all filenames will end up in.
# cuename > cue > block > sequence > track > track_event > command > synth > waveform
# seems to be the general way to do it, some may repeat, and some may go back to other tables.
# I will try to make this code go through all of them in advance.
Expand All @@ -67,7 +69,7 @@ def exp_extract(self, decode: bool = False, key = 0):
for i in pl["CueNameTable"]:
cue_names_and_indexes.append((i["CueIndex"], i["CueName"]))
srt_names = sorted(cue_names_and_indexes, key=lambda x: x[0])

""" Go through all cues and match wavforms or names. """
for i in cue_names_and_indexes:

Expand All @@ -79,12 +81,15 @@ def exp_extract(self, decode: bool = False, key = 0):
usememory: bool = wavform['Streaming'][1] == 0

if "Id" in wavform:
wavform["MemoryAwbId"] = wavform["Id"] # Old ACB's use "Id", so we default it to the new MemoryAwbId slot.
wavform["MemoryAwbId"] = wavform[
"Id"] # Old ACB's use "Id", so we default it to the new MemoryAwbId slot.

if usememory:
assert len(wavform['MemoryAwbId']) == len(srt_names) # Will error if not so. TODO add extracting without filenames references.
names = [y[1][1] for _,y in sorted(zip([x[1] for x in pl["WaveformTable"]], srt_names), key=lambda z: z[0])]
break # We break, since we did everything in the line above. I don't think ref_type changes between cues.
assert len(wavform['MemoryAwbId']) == len(
srt_names) # Will error if not so. TODO add extracting without filenames references.
names = [y[1][1] for _, y in
sorted(zip([x[1] for x in pl["WaveformTable"]], srt_names), key=lambda z: z[0])]
break # We break, since we did everything in the line above. I don't think ref_type changes between cues.

else:
# TODO
Expand All @@ -96,7 +101,7 @@ def exp_extract(self, decode: bool = False, key = 0):

elif ref_type == 3:
sequence = pl['SequenceTable'][i[0]]
track_type = sequence['Type'][1] # Unused but will leave it here if needed.
track_type = sequence['Type'][1] # Unused but will leave it here if needed.
for tr_idx in iter_unpack(">H", sequence['TrackIndex'][1]):
# TODO I am here currently.
pass
Expand All @@ -107,7 +112,7 @@ def exp_extract(self, decode: bool = False, key = 0):

else:
raise NotImplementedError("Unknown ReferenceType inside ACB.")

def parse_type1(self):
pass

Expand Down Expand Up @@ -138,24 +143,30 @@ def parse_commands(self):
def parse_sequence(self):
pass

def extract(self, decode: bool = False, key: int = 0, dirname: str = ""):
""" Extracts audio files in an AWB/ACB without preserving filenames. """
def extract(self, decode: bool = False, key: int = 0, dirname: str = "", filename: str = ""):
"""Extracts audio files in an AWB/ACB without preserving filenames."""
if dirname:
os.makedirs(dirname, exist_ok=True)
filename = 0
for i in self.awb.getfiles():
Extension: str = self.get_extension(self.payload[0]['WaveformTable'][filename]['EncodeType'][1])

for fileIndex, i in enumerate(self.awb.getfiles()):
Extension = self.get_extension(self.payload[0]['WaveformTable'][fileIndex]['EncodeType'][1])

if filename:
file_prefix = f"{filename}-"
else:
file_prefix = ""

if decode and Extension == ".hca":
hca = HCA(i, key=key, subkey=self.awb.subkey).decode()
open(os.path.join(dirname, str(filename)+".wav"), "wb").write(hca)
filename += 1
hca = HCA(i, key=key, subkey=self.awb.subkey).decode()
with open(os.path.join(dirname, f"{file_prefix}{fileIndex}.wav"), "wb") as f:
f.write(hca)
else:
open(os.path.join(dirname, f"{filename}{Extension}"), "wb").write(i)
filename += 1
with open(os.path.join(dirname, f"{file_prefix}{fileIndex}{Extension}"), "wb") as f:
f.write(i)

def get_extension(self, EncodeType: int) -> str:
if EncodeType == 0 or EncodeType == 3:
return ".adx" # Maybe 0 is ahx?
return ".adx" # Maybe 0 is ahx?
elif EncodeType == 2 or EncodeType == 6:
return ".hca"
elif EncodeType == 7 or EncodeType == 10:
Expand All @@ -175,6 +186,7 @@ def get_extension(self, EncodeType: int) -> str:
else:
return ""


# TODO Have to finish correct ACB extracting first.
class ACBBuilder(UTFBuilder):
pass
pass