From f7a0e460dcb2ff4c24cb1765d8c4c2d46f9c1315 Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 2 Sep 2019 20:41:04 +0200 Subject: [PATCH] Fix some .acb+awb names --- src/meta/acb.c | 804 +++++++++++++++++++++++++------------------------ 1 file changed, 415 insertions(+), 389 deletions(-) diff --git a/src/meta/acb.c b/src/meta/acb.c index 4c13ab0d..6207099f 100644 --- a/src/meta/acb.c +++ b/src/meta/acb.c @@ -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);