Fix some .acb+awb names

This commit is contained in:
bnnm 2019-09-02 20:41:04 +02:00
parent a2fe3aa343
commit f7a0e460dc

View File

@ -71,9 +71,11 @@ fail:
/* ************************************** */
typedef struct {
/* keep track of these tables so they can be closed when done */
utf_context *Header;
utf_context *CueNameTable;
utf_context *CueTable;
utf_context *BlockTable;
utf_context *SequenceTable;
utf_context *TrackTable;
utf_context *TrackEventTable;
@ -81,42 +83,22 @@ typedef struct {
utf_context *SynthTable;
utf_context *WaveformTable;
char name[1024];
/* config */
int is_memory;
int target_waveid;
int has_TrackEventTable;
int has_CommandTable;
int16_t CueNameIndex;
const char* CueName;
int16_t CueIndex;
int16_t ReferenceIndex;
int8_t ReferenceType;
/* to avoid infinite/circular references (AtomViewer crashes otherwise) */
int synth_depth;
int sequence_depth;
int16_t NumTracks;
uint32_t TrackIndex_offset;
uint32_t TrackIndex_size;
int16_t TrackIndex;
int16_t EventIndex;
uint32_t Command_offset;
uint32_t Command_size;
int16_t SynthIndex_count;
int16_t SynthIndex_list[255];
int16_t SynthIndex;
int8_t SynthType;
uint32_t ReferenceItems_offset;
uint32_t ReferenceItems_size;
int ReferenceItems_count;
int16_t ReferenceItems_list[255];
int16_t ReferenceItem;
int16_t AwbId;
int8_t AwbStreaming;
int is_wave_found;
int AwbName_count;
int16_t AwbName_list[255];
/* name stuff */
int16_t cuename_index;
const char * cuename_name;
int awbname_count;
int16_t awbname_list[255];
char name[1024];
} acb_header;
@ -138,286 +120,406 @@ fail:
return 0;
}
static int load_acb_cue_info(STREAMFILE *acbFile, acb_header* acb) {
/* read Cue[CueNameIndex] */
if (!utf_query_s16(acbFile, acb->CueNameTable, acb->CueNameIndex, "CueIndex", &acb->CueIndex))
goto fail;
if (!utf_query_string(acbFile, acb->CueNameTable, acb->CueNameIndex, "CueName", &acb->CueName))
goto fail;
//;VGM_LOG("ACB: CueName[%i]: CueIndex=%i, CueName=%s\n", acb->CueNameIndex, acb->CueIndex, acb->CueName);
/* read Cue[CueIndex] */
if (!load_utf_subtable(acbFile, acb, &acb->CueTable, "CueTable", NULL))
goto fail;
if (!utf_query_s8 (acbFile, acb->CueTable, acb->CueIndex, "ReferenceType", &acb->ReferenceType))
goto fail;
if (!utf_query_s16(acbFile, acb->CueTable, acb->CueIndex, "ReferenceIndex", &acb->ReferenceIndex))
goto fail;
//;VGM_LOG("ACB: Cue[%i]: ReferenceType=%i, ReferenceIndex=%i\n", acb->CueIndex, acb->ReferenceType, acb->ReferenceIndex);
return 1;
fail:
return 0;
}
static int load_acb_sequence(STREAMFILE *acbFile, acb_header* acb) {
/* read Sequence[ReferenceIndex] */
if (!load_utf_subtable(acbFile, acb, &acb->SequenceTable, "SequenceTable", NULL))
goto fail;
if (!utf_query_s16(acbFile, acb->SequenceTable, acb->ReferenceIndex, "NumTracks", &acb->NumTracks))
goto fail;
if (!utf_query_data(acbFile, acb->SequenceTable, acb->ReferenceIndex, "TrackIndex", &acb->TrackIndex_offset, &acb->TrackIndex_size))
goto fail;
//;VGM_LOG("ACB: Sequence[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", acb->ReferenceIndex, acb->NumTracks, acb->TrackIndex_offset,acb->TrackIndex_size);
if (acb->NumTracks * 0x02 > acb->TrackIndex_size) { /* padding may exist */
VGM_LOG("ACB: unknown TrackIndex size\n");
goto fail;
}
return 1;
fail:
return 0;
}
static int load_acb_track_command(STREAMFILE *acbFile, acb_header* acb) {
/* read Track[TrackIndex] */
if (!load_utf_subtable(acbFile, acb, &acb->TrackTable, "TrackTable", NULL))
goto fail;
if (!utf_query_s16(acbFile, acb->TrackTable, acb->TrackIndex, "EventIndex", &acb->EventIndex))
goto fail;
//;VGM_LOG("ACB: Track[%i]: EventIndex=%i\n", acb->TrackIndex, acb->EventIndex);
/* depending on version next stuff varies a bit, check by table existence */
if (acb->has_TrackEventTable) {
/* read TrackEvent[EventIndex] */
if (!load_utf_subtable(acbFile, acb, &acb->TrackEventTable, "TrackEventTable", NULL))
goto fail;
if (!utf_query_data(acbFile, acb->TrackEventTable, acb->EventIndex, "Command", &acb->Command_offset, &acb->Command_size))
goto fail;
//;VGM_LOG("ACB: TrackEvent[%i]: Command={%x,%x}\n", acb->EventIndex, acb->Command_offset,acb->Command_size);
}
else if (acb->has_CommandTable) {
/* read Command[EventIndex] */
if (!load_utf_subtable(acbFile, acb, &acb->CommandTable, "CommandTable", NULL))
goto fail;
if (!utf_query_data(acbFile, acb->CommandTable, acb->EventIndex, "Command", &acb->Command_offset, &acb->Command_size))
goto fail;
//;VGM_LOG("ACB: Command[%i]: Command={%x,%x}\n", acb->EventIndex, acb->Command_offset,acb->Command_size);
}
else {
VGM_LOG("ACB: unknown command table\n");
}
/* read Command (some kind of multiple TLVs, this seems ok) */
{
uint32_t offset = acb->Command_offset;
uint32_t max_offset = acb->Command_offset + acb->Command_size;
uint16_t code, subcode, subindex;
uint8_t size;
acb->SynthIndex_count = 0;
while (offset < max_offset) {
code = read_u16be(offset + 0x00, acbFile);
size = read_u8 (offset + 0x02, acbFile);
offset += 0x03;
if (code == 0x07D0) {
if (size < 0x04) {
VGM_LOG("ACB: subcommand with unknown size\n");
break;
}
subcode = read_u16be(offset + 0x00, acbFile);
subindex = read_u16be(offset + 0x02, acbFile);
/* reference to Synth/Waveform like those in Synth? */
if (subcode != 0x02) {
//todo some like Yakuza Kiwami 2 usen.acb/Yakuza 6 haichi_amb_siren.acb use 0x03
// ('random' type pointing to Sequence, see Synth)
VGM_LOG("ACB: subcommand with unknown subcode at %x\n", offset);
break;
}
acb->SynthIndex_list[acb->SynthIndex_count] = subindex;
acb->SynthIndex_count++;
if (acb->SynthIndex_count >= 254)
acb->ReferenceItems_count = 254; /* ??? */
//;VGM_LOG("ACB: subcommand index %i found\n", subindex);
}
/* 0x07D1 comes suspiciously often paired with 0x07D0 too */
offset += size;
}
}
return 1;
fail:
return 0;
}
static int load_acb_synth(STREAMFILE *acbFile, acb_header* acb) {
int i;
/* read Synth[SynthIndex] */
if (!load_utf_subtable(acbFile, acb, &acb->SynthTable, "SynthTable", NULL))
goto fail;
if (!utf_query_s8(acbFile, acb->SynthTable, acb->SynthIndex, "Type", &acb->SynthType))
goto fail;
if (!utf_query_data(acbFile, acb->SynthTable, acb->SynthIndex, "ReferenceItems", &acb->ReferenceItems_offset, &acb->ReferenceItems_size))
goto fail;
//;VGM_LOG("ACB: Synth[%i]: ReferenceItems={%x,%x}\n", acb->SynthIndex, acb->ReferenceItems_offset, acb->ReferenceItems_size);
acb->ReferenceItems_count = acb->ReferenceItems_size / 0x04;
if (acb->ReferenceItems_count >= 254)
acb->ReferenceItems_count = 254; /* ??? */
/* ReferenceType 2 uses Synth.Type, while 3 always sets it to 0 and uses Sequence.Type instead
* they probably affect which item in the reference list is picked:
* 0: polyphonic
* 1: sequential
* 2: shuffle
* 3: random
* 4: no repeat
* 5: switch game variable
* 6: combo sequential
* 7: switch selector
* 8: track transition by selector
* other: undefined?
*/
for (i = 0; i < acb->ReferenceItems_count; i++) {
uint16_t type, subtype, index, subindex;
uint32_t suboffset, subsize;
type = read_u16be(acb->ReferenceItems_offset + i*0x04 + 0x00, acbFile);
index = read_u16be(acb->ReferenceItems_offset + i*0x04 + 0x02, acbFile);
//;VGM_LOG("ACB: Synth reference type=%x, index=%x\n", type, index);
switch(type) {
case 0x00: /* no reference */
acb->ReferenceItems_count = 0;
break;
case 0x01: /* Waveform reference (most common) */
acb->ReferenceItems_list[i] = index;
break;
case 0x02: /* Synth reference (rare, found in Sonic Lost World with ReferenceType 2) */
if (!utf_query_data(acbFile, acb->SynthTable, index, "ReferenceItems", &suboffset, &subsize))
goto fail;
/* assuming only 1:1 references are ok */
if (subsize != 0x04) {
VGM_LOG("ACB: unknown Synth subreference size\n");
break;
}
subtype = read_u16be(suboffset + 0x00, acbFile);
subindex = read_u16be(suboffset + 0x02, acbFile);
/* AtomViewer crashes if it points to another to Synth */
if (subtype != 0x01) {
VGM_LOG("ACB: unknown Synth subreference type\n");
break;
}
acb->ReferenceItems_list[i] = subindex;
//;VGM_LOG("ACB: Synth subreference type=%x, index=%x\n", subtype, subindex);
break;
case 0x03: /* random Synths with % in TrackValues (rare, found in Sonic Lost World with ReferenceType 2) */
//todo fix: points to N Sequences (in turn pointing to Tracks > Synths) ex. se_phantom_asteroid.acb
default: /* undefined/crashes AtomViewer */
VGM_LOG("ACB: unknown Synth reference type\n");
acb->ReferenceItems_count = 0;
break;
}
}
return 1;
fail:
return 0;
}
static int load_acb_waveform(STREAMFILE *acbFile, acb_header* acb, int waveid) {
/* read Waveform[ReferenceItem] */
if (!load_utf_subtable(acbFile, acb, &acb->WaveformTable, "WaveformTable", NULL))
goto fail;
if (!utf_query_s8(acbFile, acb->WaveformTable, acb->ReferenceItem, "Streaming", &acb->AwbStreaming))
goto fail;
if (!utf_query_s16(acbFile, acb->WaveformTable, acb->ReferenceItem, "Id", &acb->AwbId)) { /* older versions use Id */
if (acb->is_memory) {
if (!utf_query_s16(acbFile, acb->WaveformTable, acb->ReferenceItem, "MemoryAwbId", &acb->AwbId))
goto fail;
} else {
if (!utf_query_s16(acbFile, acb->WaveformTable, acb->ReferenceItem, "StreamAwbId", &acb->AwbId))
goto fail;
}
}
//;VGM_LOG("ACB: Waveform[%i]: AwbId=%i, AwbStreaming=%i\n", acb->ReferenceItem, acb->AwbId, acb->AwbStreaming);
acb->is_wave_found = 0; /* reset */
if (acb->AwbId != waveid)
return 1;
/* 0=memory, 1=streaming, 2=memory (preload)+stream */
if ((acb->is_memory && acb->AwbStreaming == 1) || (!acb->is_memory && acb->AwbStreaming == 0))
return 1;
acb->is_wave_found = 1;
return 1;
fail:
return 0;
}
static void add_acb_name(STREAMFILE *acbFile, acb_header* acb) {
static void add_acb_name(STREAMFILE *acbFile, acb_header* acb, int8_t Waveform_Streaming) {
//todo safe string ops
/* aaand finally get name (phew) */
/* ignore name repeats */
if (acb->AwbName_count) {
if (acb->awbname_count) {
int i;
for (i = 0; i < acb->AwbName_count; i++) {
if (acb->AwbName_list[i] == acb->CueNameIndex)
for (i = 0; i < acb->awbname_count; i++) {
if (acb->awbname_list[i] == acb->cuename_index)
return;
}
}
/* since waveforms can be reused by cues multiple names are a thing */
if (acb->AwbName_count) {
if (acb->awbname_count) {
strcat(acb->name, "; ");
strcat(acb->name, acb->CueName);
strcat(acb->name, acb->cuename_name);
}
else {
strcpy(acb->name, acb->CueName);
strcpy(acb->name, acb->cuename_name);
}
if (acb->AwbStreaming == 2 && acb->is_memory) {
if (Waveform_Streaming == 2 && acb->is_memory) {
strcat(acb->name, " [pre]");
}
acb->AwbName_list[acb->AwbName_count] = acb->CueNameIndex;
acb->AwbName_count++;
if (acb->AwbName_count >= 254)
acb->AwbName_count = 254; /* ??? */
acb->awbname_list[acb->awbname_count] = acb->cuename_index;
acb->awbname_count++;
if (acb->awbname_count >= 254)
acb->awbname_count = 254; /* ??? */
//;VGM_LOG("ACB: found cue for waveid=%i: %s\n", acb->AwbId, acb->CueName);
//;VGM_LOG("ACB: found cue for waveid=%i: %s\n", acb->target_waveid, acb->cuename_name);
}
static int load_acb_waveform(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
int16_t Waveform_Id;
int8_t Waveform_Streaming;
/* read Waveform[Index] */
if (!load_utf_subtable(acbFile, acb, &acb->WaveformTable, "WaveformTable", NULL))
goto fail;
if (!utf_query_s16(acbFile, acb->WaveformTable, Index, "Id", &Waveform_Id)) { /* older versions use Id */
if (acb->is_memory) {
if (!utf_query_s16(acbFile, acb->WaveformTable, Index, "MemoryAwbId", &Waveform_Id))
goto fail;
} else {
if (!utf_query_s16(acbFile, acb->WaveformTable, Index, "StreamAwbId", &Waveform_Id))
goto fail;
}
}
if (!utf_query_s8(acbFile, acb->WaveformTable, Index, "Streaming", &Waveform_Streaming))
goto fail;
//;VGM_LOG("ACB: Waveform[%i]: Id=%i, Streaming=%i\n", Index, Waveform_Id, Waveform_Streaming);
/* not found but valid */
if (Waveform_Id != acb->target_waveid)
return 1;
/* must match our target's (0=memory, 1=streaming, 2=memory (prefetch)+stream) */
if ((acb->is_memory && Waveform_Streaming == 1) || (!acb->is_memory && Waveform_Streaming == 0))
return 1;
/* aaand finally get name (phew) */
add_acb_name(acbFile, acb, Waveform_Streaming);
return 1;
fail:
return 0;
}
/* define here for Synths pointing to Sequences */
static int load_acb_sequence(STREAMFILE *acbFile, acb_header* acb, int16_t Index);
static int load_acb_synth(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
int i, count;
int8_t Synth_Type;
uint32_t Synth_ReferenceItems_offset;
uint32_t Synth_ReferenceItems_size;
/* read Synth[Index] */
if (!load_utf_subtable(acbFile, acb, &acb->SynthTable, "SynthTable", NULL))
goto fail;
if (!utf_query_s8(acbFile, acb->SynthTable, Index, "Type", &Synth_Type))
goto fail;
if (!utf_query_data(acbFile, acb->SynthTable, Index, "ReferenceItems", &Synth_ReferenceItems_offset, &Synth_ReferenceItems_size))
goto fail;
//;VGM_LOG("ACB: Synth[%i]: Type=%x, ReferenceItems={%x,%x}\n", Index, Synth_Type, Synth_ReferenceItems_offset, Synth_ReferenceItems_size);
acb->synth_depth++;
if (acb->synth_depth > 2) {
VGM_LOG("ACB: Synth depth too high\n");
goto fail; /* max Synth > Synth > Waveform (ex. Yakuza 6) */
}
/* Cue.ReferenceType 2 uses Synth.Type, while 3 always sets it to 0 and uses Sequence.Type instead
* Both look the same and probably affect which item in the ReferenceItems list is picked:
* - 0: polyphonic (1 item)
* - 1: sequential (1 to N?)
* - 2: shuffle (1 from N?)
* - 3: random (1 from N?)
* - 4: no repeat
* - 5: switch game variable
* - 6: combo sequential
* - 7: switch selector
* - 8: track transition by selector
* - other: undefined?
* Since we want to find all possible Waveforms that could match our id, we ignore Type and just parse all ReferenceItems.
*/
count = Synth_ReferenceItems_size / 0x04;
for (i = 0; i < count; i++) {
uint16_t Synth_ReferenceItem_type = read_u16be(Synth_ReferenceItems_offset + i*0x04 + 0x00, acbFile);
uint16_t Synth_ReferenceItem_index = read_u16be(Synth_ReferenceItems_offset + i*0x04 + 0x02, acbFile);
//;VGM_LOG("ACB: Synth.ReferenceItem: type=%x, index=%x\n", Synth_ReferenceItem_type, Synth_ReferenceItem_index);
switch(Synth_ReferenceItem_type) {
case 0x00: /* no reference */
count = 0;
break;
case 0x01: /* Waveform (most common) */
if (!load_acb_waveform(acbFile, acb, Synth_ReferenceItem_index))
goto fail;
break;
case 0x02: /* Synth, possibly random (rare, found in Sonic Lost World with ReferenceType 2) */
if (!load_acb_synth(acbFile, acb, Synth_ReferenceItem_index))
goto fail;
break;
case 0x03: /* Sequence of Synths w/ % in Synth.TrackValues (rare, found in Sonic Lost World with ReferenceType 2) */
if (!load_acb_sequence(acbFile, acb, Synth_ReferenceItem_index))
goto fail;
break;
case 0x06: /* this seems to point to Synth but results don't make sense (rare, from Sonic Lost World) */
default: /* undefined/crashes AtomViewer */
VGM_LOG("ACB: unknown Synth.ReferenceItem type %x at %x + %x\n", Synth_ReferenceItem_type, Synth_ReferenceItems_offset, Synth_ReferenceItems_size);
count = 0; /* force end without failing */
break;
}
}
acb->synth_depth--;
return 1;
fail:
return 0;
}
static int load_acb_track_event_command(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
int16_t Track_EventIndex;
uint32_t Track_Command_offset;
uint32_t Track_Command_size;
/* read Track[Index] */
if (!load_utf_subtable(acbFile, acb, &acb->TrackTable, "TrackTable", NULL))
goto fail;
if (!utf_query_s16(acbFile, acb->TrackTable, Index, "EventIndex", &Track_EventIndex))
goto fail;
//;VGM_LOG("ACB: Track[%i]: EventIndex=%i\n", Index, Track_EventIndex);
/* next link varies with version, check by table existence */
if (acb->has_CommandTable) { /* <=v1.27 */
/* read Command[EventIndex] */
if (!load_utf_subtable(acbFile, acb, &acb->CommandTable, "CommandTable", NULL))
goto fail;
if (!utf_query_data(acbFile, acb->CommandTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size))
goto fail;
//;VGM_LOG("ACB: Command[%i]: Command={%x,%x}\n", Track_EventIndex, Track_Command_offset,Track_Command_size);
}
else if (acb->has_TrackEventTable) { /* >=v1.28 */
/* read TrackEvent[EventIndex] */
if (!load_utf_subtable(acbFile, acb, &acb->TrackEventTable, "TrackEventTable", NULL))
goto fail;
if (!utf_query_data(acbFile, acb->TrackEventTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size))
goto fail;
//;VGM_LOG("ACB: TrackEvent[%i]: Command={%x,%x}\n", Track_EventIndex, Track_Command_offset,Track_Command_size);
}
else {
VGM_LOG("ACB: unknown command table\n");
goto fail;
}
/* read Command (some kind of multiple TLVs, this seems ok) */
{
uint32_t offset = Track_Command_offset;
uint32_t max_offset = Track_Command_offset + Track_Command_size;
uint16_t tlv_code, tlv_type, tlv_index;
uint8_t tlv_size;
while (offset < max_offset) {
tlv_code = read_u16be(offset + 0x00, acbFile);
tlv_size = read_u8 (offset + 0x02, acbFile);
offset += 0x03;
if (tlv_code == 0x07D0) {
if (tlv_size < 0x04) {
VGM_LOG("ACB: TLV with unknown size\n");
break;
}
tlv_type = read_u16be(offset + 0x00, acbFile);
tlv_index = read_u16be(offset + 0x02, acbFile);
//;VGM_LOG("ACB: TLV at %x: type %x, index=%x\n", offset, tlv_type, tlv_index);
/* probably same as Synth_ReferenceItem_type */
switch(tlv_type) {
case 0x02: /* Synth (common) */
if (!load_acb_synth(acbFile, acb, tlv_index))
goto fail;
break;
case 0x03: /* Sequence of Synths (common, ex. Yakuza 6, Yakuza Kiwami 2) */
if (!load_acb_sequence(acbFile, acb, tlv_index))
goto fail;
break;
default:
VGM_LOG("ACB: unknown TLV type %x at %x + %x\n", tlv_type, offset, tlv_size);
max_offset = 0; /* force end without failing */
break;
}
}
/* 0x07D1 comes suspiciously often paired with 0x07D0 too */
offset += tlv_size;
}
}
return 1;
fail:
return 0;
}
static int load_acb_sequence(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
int i;
int16_t Sequence_NumTracks;
uint32_t Sequence_TrackIndex_offset;
uint32_t Sequence_TrackIndex_size;
/* read Sequence[Index] */
if (!load_utf_subtable(acbFile, acb, &acb->SequenceTable, "SequenceTable", NULL))
goto fail;
if (!utf_query_s16(acbFile, acb->SequenceTable, Index, "NumTracks", &Sequence_NumTracks))
goto fail;
if (!utf_query_data(acbFile, acb->SequenceTable, Index, "TrackIndex", &Sequence_TrackIndex_offset, &Sequence_TrackIndex_size))
goto fail;
//;VGM_LOG("ACB: Sequence[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, Sequence_NumTracks, Sequence_TrackIndex_offset,Sequence_TrackIndex_size);
acb->sequence_depth++;
if (acb->sequence_depth > 3) {
VGM_LOG("ACB: Sequence depth too high\n");
goto fail; /* max Sequence > Sequence > Sequence > Synth > Waveform (ex. Yakuza 6) */
}
if (Sequence_NumTracks * 0x02 > Sequence_TrackIndex_size) { /* padding may exist */
VGM_LOG("ACB: wrong Sequence.TrackIndex size\n");
goto fail;
}
/* read Tracks inside Sequence */
for (i = 0; i < Sequence_NumTracks; i++) {
int16_t Sequence_TrackIndex_index = read_s16be(Sequence_TrackIndex_offset + i*0x02, acbFile);
if (!load_acb_track_event_command(acbFile, acb, Sequence_TrackIndex_index))
goto fail;
}
acb->sequence_depth--;
return 1;
fail:
return 0;
}
static int load_acb_block(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
int i;
int16_t Block_NumTracks;
uint32_t Block_TrackIndex_offset;
uint32_t Block_TrackIndex_size;
/* read Block[Index] */
if (!load_utf_subtable(acbFile, acb, &acb->BlockTable, "BlockTable", NULL))
goto fail;
if (!utf_query_s16(acbFile, acb->BlockTable, Index, "NumTracks", &Block_NumTracks))
goto fail;
if (!utf_query_data(acbFile, acb->BlockTable, Index, "TrackIndex", &Block_TrackIndex_offset, &Block_TrackIndex_size))
goto fail;
//;VGM_LOG("ACB: Block[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, Block_NumTracks, Block_TrackIndex_offset,Block_TrackIndex_size);
if (Block_NumTracks * 0x02 > Block_TrackIndex_size) { /* padding may exist */
VGM_LOG("ACB: wrong Block.TrackIndex size\n");
goto fail;
}
/* read Tracks inside Block */
for (i = 0; i < Block_NumTracks; i++) {
int16_t Block_TrackIndex_index = read_s16be(Block_TrackIndex_offset + i*0x02, acbFile);
if (!load_acb_track_event_command(acbFile, acb, Block_TrackIndex_index))
goto fail;
}
return 1;
fail:
return 0;
}
static int load_acb_cue(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
int8_t Cue_ReferenceType;
int16_t Cue_ReferenceIndex;
/* read Cue[Index] */
if (!load_utf_subtable(acbFile, acb, &acb->CueTable, "CueTable", NULL))
goto fail;
if (!utf_query_s8 (acbFile, acb->CueTable, Index, "ReferenceType", &Cue_ReferenceType))
goto fail;
if (!utf_query_s16(acbFile, acb->CueTable, Index, "ReferenceIndex", &Cue_ReferenceIndex))
goto fail;
//;VGM_LOG("ACB: Cue[%i]: ReferenceType=%i, ReferenceIndex=%i\n", Index, Cue_ReferenceType, Cue_ReferenceIndex);
/* usually older games use older references but not necessarily */
switch(Cue_ReferenceType) {
case 1: /* Cue > Waveform (ex. PES 2015) */
if (!load_acb_waveform(acbFile, acb, Cue_ReferenceIndex))
goto fail;
break;
case 2: /* Cue > Synth > Waveform (ex. Ukiyo no Roushi) */
if (!load_acb_synth(acbFile, acb, Cue_ReferenceIndex))
goto fail;
break;
case 3: /* Cue > Sequence > Track > Command > Synth > Waveform (ex. Valkyrie Profile anatomia, Yakuza Kiwami 2) */
if (!load_acb_sequence(acbFile, acb, Cue_ReferenceIndex))
goto fail;
break;
case 8: /* Cue > Block > Track > Command > Synth > Waveform (ex. Sonic Lost World, rare) */
if (!load_acb_block(acbFile, acb, Cue_ReferenceIndex))
goto fail;
break;
default:
VGM_LOG("ACB: unknown Cue.ReferenceType=%x, Cue.ReferenceIndex=%x\n", Cue_ReferenceType, Cue_ReferenceIndex);
break; /* ignore and continue */
}
return 1;
fail:
return 0;
}
static int load_acb_cuename(STREAMFILE *acbFile, acb_header* acb, int16_t Index) {
int16_t CueName_CueIndex;
const char* CueName_CueName;
/* read CueName[Index] */
if (!load_utf_subtable(acbFile, acb, &acb->CueNameTable, "CueNameTable", NULL))
goto fail;
if (!utf_query_s16(acbFile, acb->CueNameTable, Index, "CueIndex", &CueName_CueIndex))
goto fail;
if (!utf_query_string(acbFile, acb->CueNameTable, Index, "CueName", &CueName_CueName))
goto fail;
//;VGM_LOG("ACB: CueName[%i]: CueIndex=%i, CueName=%s\n", Index, CueName_CueIndex, CueName_CueName);
/* save as will be needed if references waveform */
acb->cuename_index = Index;
acb->cuename_name = CueName_CueName;
if (!load_acb_cue(acbFile, acb, CueName_CueIndex))
goto fail;
return 1;
fail:
return 0;
}
void load_acb_wave_name(STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid, int is_memory) {
acb_header acb = {0};
int CueName_rows, CueName_i, TrackIndex_i, ReferenceItems_i, SynthIndex_i;
int i, CueName_rows;
if (!acbFile || !vgmstream || waveid < 0)
return;
@ -427,126 +529,50 @@ void load_acb_wave_name(STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid, i
* Multiple cues can use the same wave (meaning multiple names), and one cue may use multiple waves.
* There is no easy way to map cue name <> wave name so basically we parse the whole thing.
*
* .acb cues are created in CRI Atom Craft roughly like this:
* - user creates N Cues with CueName
* - Cues define Sequences of Tracks
* - depending on reference types:
* - Track points directly to Waveform (type 1)
* - Track points to Synth then to Waveform (type 2)
* - Track points to Commands with binary Command that points to Synth then to Waveform (type 3 <=v1.27)
* - Track points to TrackEvent with binary Command that points to Synth then to Waveform (type 3 >=v1.28)
* (games may use multiple versions and reference types)
* - Waveforms are audio materials encoded in some format
* - Waveforms are saved into .awb, that can be streamed (loaded manually) or internal,
* and has a checksum/hash to validate
* - there is a version value (sometimes written incorrectly) and string,
* Atom Craft may only target certain .acb versions
* .acb are created in CRI Atom Craft, where user defines N Cues with CueName each, then link somehow
* to a Waveform (.awb=streamed or memory .acb=internal, data 'material' encoded in some format),
* depending on reference types. Typical links are:
* - CueName > Cue > Waveform (type 1)
* - CueName > Cue > Synth > Waveform (type 2)
* - CueName > Cue > Sequence > Track > Command > Synth > Waveform (type 3, <=v1.27)
* - CueName > Cue > Sequence > Track > Command > Synth > Synth > Waveform (type 3, <=v1.27)
* - CueName > Cue > Sequence > Track > TrackEvent > Command > Synth > Waveform (type 3, >=v1.28)
* - CueName > Cue > Sequence > Track > TrackEvent > Command > Synth > Synth > Waveform (type 3, >=v1.28)
* - CueName > Cue > Sequence > Track > TrackEvent > Command > Sequence > (...) > Synth > Waveform (type 3, >=v1.28)
* - CueName > Cue > Block > Track > Command > Synth > Synth > Waveform (type 8)
* - others should be possible but haven't been observed
* Atom Craft may only target certain .acb versions so some links are later removed
* Not all cues to point to though Waveforms, as some are just config events/commands.
* .acb link to .awb by name (loaded manually), though they have a checksum/hash to validate.
*/
//;VGM_LOG("ACB: find waveid=%i\n", waveid);
acb.Header = utf_open(acbFile, 0x00, NULL, NULL);
if (!acb.Header) goto done;
if (!acb.Header) goto fail;
acb.target_waveid = waveid;
acb.is_memory = is_memory;
acb.has_TrackEventTable = utf_query_data(acbFile, acb.Header, 0, "TrackEventTable", NULL,NULL);
acb.has_CommandTable = utf_query_data(acbFile, acb.Header, 0, "CommandTable", NULL,NULL);
/* read CueName[i] */
if (!load_utf_subtable(acbFile, &acb, &acb.CueNameTable, "CueNameTable", &CueName_rows)) goto done;
//;VGM_LOG("ACB: CueNames=%i\n", CueName_rows);
/* read all possible cue names and find which waveids are referenced by it */
if (!load_utf_subtable(acbFile, &acb, &acb.CueNameTable, "CueNameTable", &CueName_rows))
goto fail;
for (i = 0; i < CueName_rows; i++) {
for (CueName_i = 0; CueName_i < CueName_rows; CueName_i++) {
acb.CueNameIndex = CueName_i;
if (!load_acb_cue_info(acbFile, &acb))
goto done;
/* meaning of Cue.ReferenceIndex */
switch(acb.ReferenceType) {
case 1: { /* Cue > Waveform (ex. PES 2015) */
acb.ReferenceItem = acb.ReferenceIndex;
if (!load_acb_waveform(acbFile, &acb, waveid))
goto done;
if (acb.is_wave_found)
add_acb_name(acbFile, &acb);
break;
}
case 2: { /* Cue > Synth > Waveform (ex. Ukiyo no Roushi) */
acb.SynthIndex = acb.ReferenceIndex;
if (!load_acb_synth(acbFile, &acb))
goto done;
for (ReferenceItems_i = 0; ReferenceItems_i < acb.ReferenceItems_count; ReferenceItems_i++) {
acb.ReferenceItem = acb.ReferenceItems_list[ReferenceItems_i];
if (!load_acb_waveform(acbFile, &acb, waveid))
goto done;
if (acb.is_wave_found)
add_acb_name(acbFile, &acb);
}
break;
}
case 3: { /* Cue > Sequence > Track > Command > Synth > Waveform (ex. Valkyrie Profile anatomia, Yakuza Kiwami 2) */
if (!load_acb_sequence(acbFile, &acb))
goto done;
/* read Tracks inside Sequence */
for (TrackIndex_i = 0; TrackIndex_i < acb.NumTracks; TrackIndex_i++) {
acb.TrackIndex = read_s16be(acb.TrackIndex_offset + TrackIndex_i*0x02, acbFile);
if (!load_acb_track_command(acbFile, &acb))
goto done;
for (SynthIndex_i = 0; SynthIndex_i < acb.SynthIndex_count; SynthIndex_i++) {
acb.SynthIndex = acb.SynthIndex_list[SynthIndex_i];
if (!load_acb_synth(acbFile, &acb))
goto done;
for (ReferenceItems_i = 0; ReferenceItems_i < acb.ReferenceItems_count; ReferenceItems_i++) {
acb.ReferenceItem = acb.ReferenceItems_list[ReferenceItems_i];
if (!load_acb_waveform(acbFile, &acb, waveid))
goto done;
if (acb.is_wave_found)
add_acb_name(acbFile, &acb);
}
}
}
break;
}
case 8: //todo found on Sonic Lost World (maybe not references wave?)
default:
VGM_LOG("ACB: unknown reference type\n");
break;
}
//if (acb.AwbId_count > 0)
// break;
if (!load_acb_cuename(acbFile, &acb, i))
goto fail;
}
/* meh copy */
if (acb.AwbName_count > 0) {
if (acb.awbname_count > 0) {
strncpy(vgmstream->stream_name, acb.name, STREAM_NAME_SIZE);
vgmstream->stream_name[STREAM_NAME_SIZE - 1] = '\0';
}
done:
fail:
utf_close(acb.Header);
utf_close(acb.CueNameTable);
utf_close(acb.CueTable);