diff --git a/src/formats.c b/src/formats.c index a67c6580..6286e8e7 100644 --- a/src/formats.c +++ b/src/formats.c @@ -79,6 +79,7 @@ static const char* extension_list[] = { "atsl", "atsl3", "atsl4", + "atslx", "atx", "aud", "audio", //txth/reserved [Grimm Echoes (Android)] diff --git a/src/meta/atsl.c b/src/meta/atsl.c index 5ed9b9d9..6ed299f9 100644 --- a/src/meta/atsl.c +++ b/src/meta/atsl.c @@ -7,18 +7,22 @@ VGMSTREAM* init_vgmstream_atsl(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; STREAMFILE* temp_sf = NULL; int total_subsongs, target_subsong = sf->stream_index; - int type, version, big_endian = 0, entries; + int big_endian = 0, entries, version, platform, format; uint32_t subfile_offset = 0, subfile_size = 0, header_size, entry_size; init_vgmstream_t init_vgmstream = NULL; - const char* fake_ext; + const char* fake_ext = NULL; /* checks */ if (!is_id32be(0x00,sf, "ATSL")) goto fail; - /* .atsl: header id (for G1L extractions), .atsl3: PS3 games, .atsl4: PS4 games */ - if (!check_extensions(sf,"atsl,atsl3,atsl4")) + + /* .atsl: common extension (PC/PS4/etc) + * .atsl3: PS3 games + * .atsl4: some PS4 games + * .atslx: X360 games */ + if (!check_extensions(sf,"atsl,atsl3,atsl4,atslx")) goto fail; /* main header (LE) */ @@ -31,67 +35,44 @@ VGMSTREAM* init_vgmstream_atsl(STREAMFILE* sf) { /* 0x20: subheader size */ /* 0x24/28: null */ - /* Type byte may be wrong (could need header id tests instead). Example flags at 0x08/0x0c: + /* 0x08: unknown, varies */ + + /* 0x0c(1): 0/1 (version? seen both in even in the same game) */ + version = read_u8(0x0c, sf); /* <~2017 = v0, both seen in Nioh (PC) */ + platform = read_u8(0x0d, sf); + /* 0x0e: header version? (00~04: size 0x28, 05~06: size 0x30), 03~05 seen in Nioh (PS4) */ + /* 0x0f: always 1? */ + + big_endian = (platform == 0x02 || platform == 0x03); /* PS3/X360 */ + + /* Example flags at 0x08/0x0c for reference: * - 00010101 00020001 .atsl3 from One Piece Pirate Warriors (PS3)[ATRAC3] - * - 00000201 00020001 .atsl3 from Fist of North Star: Ken's Rage 2 (PS3)[ATRAC3] + * - 00000201 00020001 .atsl3 from Fist of North Star: Ken's Rage 2 (PS3)[ATRAC3]-bgm007 + * - 01010301 00030201 .atsl3 from Fist of North Star: Ken's Rage 2 (X360)[ATRAC3]-bgm007 * 00000301 00020101 (same) * - 01040301 00060301 .atsl4 from Nobunaga's Ambition: Sphere of Influence (PS4)[ATRAC9] * - 00060301 00040301 .atsl in G1L from One Piece Pirate Warriors 3 (Vita)[ATRAC9] * - 00060301 00010301 .atsl in G1L from One Piece Pirate Warriors 3 (PC)[KOVS] * - 000A0301 00010501 .atsl in G1L from Warriors All-Stars (PC)[KOVS] 2017-09 - * - 01000000 01010501 .atsl from Nioh (PC)[KOVS] 2017-11 - * - 01000000 00010501 .atsl from Nioh (PC)[KOVS] 2017-11 * - 000B0301 00080601 .atsl in G1l from Sengoku Musou Sanada Maru (Switch)[KTSS] 2017-09 + * - 01000000 00010501 .atsl from Nioh (PC)[KOVS] 2017-11 + * - 01000000 01010501 .atsl from Nioh (PC)[KOVS] 2017-11 * - 03070301 01060301 .atsl from Nioh (PS4)[ATRAC9] - * - 00090301 01060501 .atsl from Nioh (PS4)[ATRAC9]-bigger header + * - 00080301 01060401 .atsl from Nioh (PS4)[ATRAC9] + * - 00090301 01060501 .atsl from Nioh (PS4)[ATRAC9] (bigger header) * - 010C0301 01060601 .atsl from Dynasty Warriors 9 (PS4)[KTAC] * - 010D0301 01010601 .atsl from Dynasty Warriors 9 DLC (PC)[KOVS] */ - type = read_u16le(0x0c, sf); - version = read_u16le(0x0e, sf); - switch(type) { - case 0x0100: /* KOVS */ - init_vgmstream = init_vgmstream_ogg_vorbis; - fake_ext = "kvs"; + switch(version) { + case 0x00: entry_size = 0x28; break; - case 0x0101: - init_vgmstream = init_vgmstream_ogg_vorbis; - fake_ext = "kvs"; + case 0x01: entry_size = 0x3c; break; - case 0x0200: /* ATRAC3 */ - init_vgmstream = init_vgmstream_riff; - fake_ext = "at3"; - entry_size = 0x28; - big_endian = 1; - break; - case 0x0400: - case 0x0600: /* ATRAC9 */ - init_vgmstream = init_vgmstream_riff; - fake_ext = "at9"; - entry_size = 0x28; - break; - case 0x0601: /* KTAC / ATRAC9 */ - if (version == 0x0103 || version == 0x0105) { - init_vgmstream = init_vgmstream_riff; - fake_ext = "at9"; - entry_size = 0x3c; - } - else { - init_vgmstream = init_vgmstream_ktac; - fake_ext = "ktac"; - entry_size = 0x3c; - } - break; - case 0x0800: /* KTSS */ - init_vgmstream = init_vgmstream_ktss; - fake_ext = "ktss"; - entry_size = 0x28; - break; default: - vgm_logi("ATSL: unknown type %x (report)\n", type); + vgm_logi("ATSL: unknown version %x (report)\n", version); goto fail; } @@ -108,11 +89,36 @@ VGMSTREAM* init_vgmstream_atsl(STREAMFILE* sf) { int is_unique = 1; uint32_t entry_subfile_offset, entry_subfile_size; - /* 0x00: id */ + /* entry header (values match subfile header) */ + /* 0x00: id (0..N, usually) */ entry_subfile_offset = read_u32(header_size + i*entry_size + 0x04,sf); entry_subfile_size = read_u32(header_size + i*entry_size + 0x08,sf); - /* 0x10+: config, channels/sample rate/num_samples/loop_start/channel layout/etc (matches subfile header) */ + if (version == 0x00) { + format = 0x00; + /* 0x0c: sample rate */ + /* 0x10: samples */ + /* 0x14: loop start */ + /* 0x18: loop end */ + /* 0x1c: null? */ + /* 0x28: offset to unknown extra table (after entries) */ + /* 0x0c: unknown table entries (0x2C each) */ + } + else { + /* 0x0c: null? */ + /* 0x10: offset to unknown extra table (after entries) */ + /* 0x14: unknown table entries (0x2C each) */ + /* 0x18: channels */ + /* 0x1c: some hash/id? (same for all entries) */ + format = read_u32(header_size + i*entry_size + 0x20, sf); + /* 0x24: sample rate */ + /* 0x28: samples */ + /* 0x2c: encoder delay */ + /* 0x30: loop start (includes delay) */ + /* 0x34: channel layout */ + /* 0x38: null? */ + } + //TODO use silence subsong? /* dummy entry, seen in DW9 DLC (has unique config though) */ if (!entry_subfile_offset && !entry_subfile_size) continue; @@ -136,11 +142,64 @@ VGMSTREAM* init_vgmstream_atsl(STREAMFILE* sf) { subfile_size = entry_subfile_size; } } - } - if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail; - if (!subfile_offset || !subfile_size) goto fail; - /* some kind of seek/switch table may follow (optional, found in .atsl3) */ + if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail; + if (!subfile_offset || !subfile_size) goto fail; + + /* some kind of switch table may follow (referenced in entries) */ + } + + /* similar codec values also seen in KTSR */ + switch(platform) { + case 0x01: /* PC */ + if (format == 0x0000 || format == 0x0005) { + init_vgmstream = init_vgmstream_ogg_vorbis; + fake_ext = "kvs"; + } + break; + + case 0x02: /* PS3 */ + if (format == 0x0000) { + init_vgmstream = init_vgmstream_riff; + fake_ext = "at3"; + } + break; + + case 0x03: /* X360 */ + if (format == 0x0000) { + init_vgmstream = init_vgmstream_xma; + fake_ext = "xma"; + } + break; + + case 0x04: /* Vita */ + case 0x06: /* PS4 */ + if (format == 0x0000 || format == 0x1001) { /* Nioh (PS4)-1001 */ + init_vgmstream = init_vgmstream_riff; + fake_ext = "at9"; + } + else if (format == 0x1000) { /* Dynasty Warriors 9 (PS4) patch BGM */ + init_vgmstream = init_vgmstream_ktac; + fake_ext = "ktac"; + } + break; + + case 0x08: /* Switch */ + if (format == 0x0000 || format == 0x0005) {/* Dynasty Warriors 9 (Switch)-0005 w/ Opus */ + init_vgmstream = init_vgmstream_ktss; + fake_ext = "ktss"; + } + break; + + default: + break; + } + + if (init_vgmstream == NULL || fake_ext == NULL) { + vgm_logi("ATSL: unknown platform %x + format %x (report)\n", platform, format); + goto fail; + } + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, fake_ext); if (!temp_sf) goto fail;