mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-20 20:41:08 +01:00
Fix some .ktsl2asbin/atsl [Nioh (PC)]
This commit is contained in:
parent
89ed00a980
commit
91e12ab559
302
src/meta/atsl.c
302
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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user