#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; }