diff --git a/src/meta/atsl.c b/src/meta/atsl.c index 0b2f6c5b..13cf94d0 100644 --- a/src/meta/atsl.c +++ b/src/meta/atsl.c @@ -1,157 +1,145 @@ -#include "meta.h" -#include "../coding/coding.h" - -typedef enum { ATRAC3, ATRAC9, KOVS, KTSS, KTAC } atsl_codec; - -/* .ATSL - Koei Tecmo audio container [One Piece Pirate Warriors (PS3), Warriors All-Stars (PC)] */ -VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *temp_streamFile = NULL; - int total_subsongs, target_subsong = streamFile->stream_index; - int type, big_endian = 0, entries; - atsl_codec codec; - const char* fake_ext; - off_t subfile_offset = 0; - size_t subfile_size = 0, header_size, entry_size; - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - - - /* checks */ - /* .atsl: header id (for G1L extractions), .atsl3: PS3 games, .atsl4: PS4 games */ - if ( !check_extensions(streamFile,"atsl,atsl3,atsl4")) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x4154534C) /* "ATSL" */ - goto fail; - - /* main header (LE) */ - header_size = read_32bitLE(0x04,streamFile); - /* 0x08/0c: flags?, 0x10: fixed? (0x03E8) */ - entries = read_32bitLE(0x14,streamFile); - /* 0x18: 0x28, or 0x30 (rarer) */ - /* 0x1c: null, 0x20: subheader size, 0x24/28: null */ - - /* Type byte may be wrong (could need header id tests instead). Example flags at 0x08/0x0c: - * - 00010101 00020001 .atsl3 from One Piece Pirate Warriors (PS3)[ATRAC3] - * - 00000201 00020001 .atsl3 from Fist of North Star: Ken's Rage 2 (PS3)[ATRAC3] - * 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] - * - 000B0301 00080601 atsl in G1l from Sengoku Musou Sanada Maru (Switch)[KTSS] - * - 010C0301 01060601 .atsl from Dynasty Warriors 9 (PS4)[KTAC] - */ - entry_size = 0x28; - type = read_16bitLE(0x0c, streamFile); - switch(type) { - case 0x0100: - codec = KOVS; - fake_ext = "kvs"; - break; - case 0x0200: - codec = ATRAC3; - fake_ext = "at3"; - big_endian = 1; - break; - case 0x0400: - case 0x0600: - codec = ATRAC9; - fake_ext = "at9"; - break; - case 0x0601: - codec = KTAC; - fake_ext = "ktac"; - entry_size = 0x3c; - break; - case 0x0800: - codec = KTSS; - fake_ext = "ktss"; - break; - default: - VGM_LOG("ATSL: unknown type %x\n", type); - goto fail; - } - read_32bit = big_endian ? read_32bitBE : read_32bitLE; - - - /* entries can point to the same file, count unique only */ - { - int i,j; - - total_subsongs = 0; - if (target_subsong == 0) target_subsong = 1; - - /* parse entry header (in machine endianness) */ - for (i = 0; i < entries; i++) { - int is_unique = 1; - - /* 0x00: id */ - off_t entry_subfile_offset = read_32bit(header_size + i*entry_size + 0x04,streamFile); - size_t entry_subfile_size = read_32bit(header_size + i*entry_size + 0x08,streamFile); - /* 0x08+: channels/sample rate/num_samples/loop_start/etc (match subfile header) */ - - /* check if current entry was repeated in a prev entry */ - for (j = 0; j < i; j++) { - off_t prev_offset = read_32bit(header_size + j*entry_size + 0x04,streamFile); - if (prev_offset == entry_subfile_offset) { - is_unique = 0; - break; - } - } - if (!is_unique) - continue; - - total_subsongs++; - - /* target GET, but keep going to count subsongs */ - if (!subfile_offset && target_subsong == total_subsongs) { - subfile_offset = entry_subfile_offset; - 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) */ - - - temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, fake_ext); - if (!temp_streamFile) goto fail; - - /* init the VGMSTREAM */ - switch(codec) { - case ATRAC3: - case ATRAC9: - vgmstream = init_vgmstream_riff(temp_streamFile); - if (!vgmstream) goto fail; - break; -#ifdef VGM_USE_VORBIS - case KOVS: - vgmstream = init_vgmstream_ogg_vorbis(temp_streamFile); - if (!vgmstream) goto fail; - break; -#endif - case KTSS: - vgmstream = init_vgmstream_ktss(temp_streamFile); - if (!vgmstream) goto fail; - break; - case KTAC: - //vgmstream = init_vgmstream_ktac(temp_streamFile); //Koei Tecto VBR-like ATRAC9 - //if (!vgmstream) goto fail; - //break; - default: - goto fail; - } - - vgmstream->num_streams = total_subsongs; - - close_streamfile(temp_streamFile); - return vgmstream; - -fail: - close_streamfile(temp_streamFile); - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + + +/* .ATSL - Koei Tecmo audio container [One Piece Pirate Warriors (PS3), Warriors All-Stars (PC)] */ +VGMSTREAM* init_vgmstream_atsl(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + int total_subsongs, target_subsong = sf->stream_index; + int type, big_endian = 0, entries; + uint32_t subfile_offset = 0, subfile_size = 0, header_size, entry_size; + + VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf) = NULL; + const char* fake_ext; + + + /* 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")) + goto fail; + + /* main header (LE) */ + header_size = read_u32le(0x04,sf); + /* 0x08/0c: flags? */ + /* 0x10: volume? (always 1000) */ + entries = read_u32le(0x14,sf); + /* 0x18: 0x28, or 0x30 (rarer) */ + /* 0x1c: null */ + /* 0x20: subheader size */ + /* 0x24/28: null */ + + /* Type byte may be wrong (could need header id tests instead). Example flags at 0x08/0x0c: + * - 00010101 00020001 .atsl3 from One Piece Pirate Warriors (PS3)[ATRAC3] + * - 00000201 00020001 .atsl3 from Fist of North Star: Ken's Rage 2 (PS3)[ATRAC3] + * 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] + * - 000B0301 00080601 atsl in G1l from Sengoku Musou Sanada Maru (Switch)[KTSS] + * - 010C0301 01060601 .atsl from Dynasty Warriors 9 (PS4)[KTAC] + * - 01000000 01010501 .atsl from Nioh (PC)[KOVS] + * - 01000000 00010501 .atsl from Nioh (PC)[KOVS] + */ + + type = read_u16le(0x0c, sf); + switch(type) { + case 0x0100: /* KOVS */ + init_vgmstream = init_vgmstream_ogg_vorbis; + fake_ext = "kvs"; + entry_size = 0x28; + break; + case 0x0101: + init_vgmstream = init_vgmstream_ogg_vorbis; + fake_ext = "kvs"; + 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 */ + 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); + goto fail; + } + + /* entries can point to the same file, count unique only */ + { + int i, j; + uint32_t (*read_u32)(off_t,STREAMFILE*) = big_endian ? read_u32be : read_u32le; + + total_subsongs = 0; + if (target_subsong == 0) target_subsong = 1; + + /* parse entry header (in machine endianness) */ + for (i = 0; i < entries; i++) { + int is_unique = 1; + + /* 0x00: id */ + uint32_t entry_subfile_offset = read_u32(header_size + i*entry_size + 0x04,sf); + uint32_t entry_subfile_size = read_u32(header_size + i*entry_size + 0x08,sf); + /* 0x08+: channels/sample rate/num_samples/loop_start/etc (match subfile header) */ + + /* check if current entry was repeated in a prev entry */ + for (j = 0; j < i; j++) { + off_t prev_offset = read_u32(header_size + j*entry_size + 0x04,sf); + if (prev_offset == entry_subfile_offset) { + is_unique = 0; + break; + } + } + if (!is_unique) + continue; + + total_subsongs++; + + /* target GET, but keep going to count subsongs */ + if (!subfile_offset && target_subsong == total_subsongs) { + subfile_offset = entry_subfile_offset; + 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) */ + + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, fake_ext); + if (!temp_sf) goto fail; + + /* init the VGMSTREAM */ + vgmstream = init_vgmstream(temp_sf); + if (!vgmstream) goto fail; + + vgmstream->num_streams = total_subsongs; + + close_streamfile(temp_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/ktsr.c b/src/meta/ktsr.c index c74d6972..acecbf5f 100644 --- a/src/meta/ktsr.c +++ b/src/meta/ktsr.c @@ -38,26 +38,25 @@ static layered_layout_data* build_layered_atrac9(ktsr_header* ktsr, STREAMFILE * /* KTSR - Koei Tecmo sound resource countainer */ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; - STREAMFILE *sf_b = NULL; + STREAMFILE* sf_b = NULL; ktsr_header ktsr = {0}; int target_subsong = sf->stream_index; int separate_offsets = 0; /* checks */ + if (!is_id32be(0x00, sf, "KTSR")) + goto fail; + if (read_u32be(0x04, sf) != 0x777B481A) /* hash(?) id: 0x777B481A=as, 0x0294DDFC=st, 0xC638E69E=gc */ + goto fail; /* .ktsl2asbin: common [Atelier Ryza (PC/Switch), Nioh (PC)] */ if (!check_extensions(sf, "ktsl2asbin")) goto fail; /* KTSR can be a memory file (ktsl2asbin), streams (ktsl2stbin) and global config (ktsl2gcbin) - * This accepts ktsl2asbin with internal data, or opening external streams as subsongs. + * This accepts .ktsl2asbin with internal data or external streams as subsongs. * Some info from KTSR.bt */ - if (!is_id32be(0x00, sf, "KTSR")) - goto fail; - if (read_u32be(0x04, sf) != 0x777B481A) /* hash(?) id: 0x777B481A=as, 0x0294DDFC=st, 0xC638E69E=gc */ - goto fail; - if (target_subsong == 0) target_subsong = 1; ktsr.target_subsong = target_subsong; @@ -68,7 +67,7 @@ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) { if (ktsr.is_external) { sf_b = open_streamfile_by_ext(sf, "ktsl2stbin"); if (!sf_b) { - VGM_LOG("KTSR: companion file not found\n"); + vgm_logi("KTSR: companion file '*.ktsl2stbin' not found\n"); goto fail; } } @@ -129,28 +128,23 @@ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) { #ifdef VGM_USE_VORBIS case KVS: { - VGMSTREAM *ogg_vgmstream = NULL; //TODO: meh - STREAMFILE *sf_kvs = setup_subfile_streamfile(sf_b, ktsr.stream_offsets[0], ktsr.stream_sizes[0], "kvs"); - if (!sf_kvs) goto fail; + VGMSTREAM* ogg_vgmstream = NULL; //TODO: meh + STREAMFILE* temp_sf = setup_subfile_streamfile(sf_b, ktsr.stream_offsets[0], ktsr.stream_sizes[0], "kvs"); + if (!temp_sf) goto fail; - ogg_vgmstream = init_vgmstream_ogg_vorbis(sf_kvs); - close_streamfile(sf_kvs); - if (ogg_vgmstream) { - ogg_vgmstream->stream_size = vgmstream->stream_size; - ogg_vgmstream->num_streams = vgmstream->num_streams; - ogg_vgmstream->channel_layout = vgmstream->channel_layout; - /* loops look shared */ - strcpy(ogg_vgmstream->stream_name, vgmstream->stream_name); + ogg_vgmstream = init_vgmstream_ogg_vorbis(temp_sf); + close_streamfile(temp_sf); + if (!ogg_vgmstream) goto fail; - close_vgmstream(vgmstream); - if (sf_b != sf) close_streamfile(sf_b); - return ogg_vgmstream; - } - else { - goto fail; - } + ogg_vgmstream->stream_size = vgmstream->stream_size; + ogg_vgmstream->num_streams = vgmstream->num_streams; + ogg_vgmstream->channel_layout = vgmstream->channel_layout; + /* loops look shared */ + strcpy(ogg_vgmstream->stream_name, vgmstream->stream_name); - break; + close_vgmstream(vgmstream); + if (sf_b != sf) close_streamfile(sf_b); + return ogg_vgmstream; } #endif @@ -158,7 +152,6 @@ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) { goto fail; } - if (!vgmstream_open_stream_bf(vgmstream, sf_b, ktsr.stream_offsets[0], 1)) goto fail; @@ -282,13 +275,14 @@ static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, off_t offset) { int i; uint32_t type; - type = read_u32be(offset + 0x00, sf); + type = read_u32be(offset + 0x00, sf); /* hash-id? */ //size = read_u32le(offset + 0x04, sf); /* probably could check the flag in sound header, but the format is kinda messy */ - switch(type) { /* hash-id? */ + switch(type) { case 0x38D0437D: /* external [Nioh (PC), Atelier Ryza (PC)] */ + case 0x3DEA478D: /* external [Nioh (PC)] */ case 0xDF92529F: /* external [Atelier Ryza (PC)] */ case 0x6422007C: /* external [Atelier Ryza (PC)] */ /* 08 subtype? (ex. 0x522B86B9) @@ -311,10 +305,16 @@ static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, off_t offset) { ktsr->format = read_u32le(offset + 0x14, sf); /* other fields will be read in the external stream */ - ktsr->channel_layout= read_u32le(offset + 0x28, sf); + ktsr->channel_layout = read_u32le(offset + 0x28, sf); - ktsr->stream_offsets[0] = read_u32le(offset + 0x34, sf); - ktsr->stream_sizes[0] = read_u32le(offset + 0x38, sf); + if (type == 0x3DEA478D) { /* Nioh (PC) has one less field, some files only [ABS.ktsl2asbin] */ + ktsr->stream_offsets[0] = read_u32le(offset + 0x30, sf); + ktsr->stream_sizes[0] = read_u32le(offset + 0x34, sf); + } + else { + ktsr->stream_offsets[0] = read_u32le(offset + 0x34, sf); + ktsr->stream_sizes[0] = read_u32le(offset + 0x38, sf); + } ktsr->is_external = 1; if (ktsr->format != 0x05) {