mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-19 00:04:04 +01:00
ktsr: add .txtm support
This commit is contained in:
parent
cc29ab1058
commit
5928c8df0d
@ -1600,8 +1600,9 @@ different internally (encrypted, different versions, etc) and not always can be
|
||||
- Codecs: IMUSE
|
||||
- **ktsr.c**
|
||||
- Koei Tecmo KTSR header [*KTSR*]
|
||||
- *ktsr*: `.ktsl2asbin .asbin`
|
||||
- *asrs*: `.srsa`
|
||||
- *ktsr_internal*: `.ktsl2asbin .asbin`
|
||||
- *ktsr_internal*
|
||||
- Subfiles: *riff ogg_vorbis ktss*
|
||||
- Codecs: MSADPCM_int NGC_DSP ATRAC9
|
||||
- **mups.c**
|
||||
|
@ -513,6 +513,10 @@ willow.mpf: willow.mus,willow_o.mus
|
||||
bgm_2_streamfiles.awb: bgm_2.acb
|
||||
```
|
||||
```
|
||||
# hashes of SE1_Common_BGM + ext [Hyrule Warriors: Age of Calamity (Switch)]
|
||||
0x3a160928.srsa: 0x272c6efb.srsa
|
||||
```
|
||||
```
|
||||
# Snack World (Switch) names for .awb (single .acb for all .awb, order matters)
|
||||
bgm.awb: bgm.acb
|
||||
bgm_DLC1.awb: bgm.acb
|
||||
@ -639,7 +643,7 @@ called to play one or multiple audio "waves"/"materials" in another section.
|
||||
Rather than handling cues, vgmstream shows and plays waves, then assigns cue names
|
||||
that point to the wave if possible, since vgmstream mainly deals with streamed/wave
|
||||
audio and simulating cues is out of scope. Figuring out a whole cue format can be a
|
||||
*huge* time investment, so handling waves only is often enough.
|
||||
*huge* time investment, so handling waves only is good enough.
|
||||
|
||||
Cues can be *very* complex, like N cues pointing to 1 wave with varying pitch, or
|
||||
1 cue playing one random wave out of 3. Sometimes not all waves are referenced by
|
||||
|
@ -1,12 +1,14 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../util/companion_files.h"
|
||||
|
||||
typedef enum { NONE, MSADPCM, DSP, GCADPCM, ATRAC9, RIFF_ATRAC9, KOVS, KTSS, } ktsr_codec;
|
||||
|
||||
#define MAX_CHANNELS 8
|
||||
|
||||
typedef struct {
|
||||
uint32_t base_offset;
|
||||
bool is_srsa;
|
||||
int total_subsongs;
|
||||
int target_subsong;
|
||||
@ -43,13 +45,22 @@ static VGMSTREAM* init_vgmstream_ktsr_sub(STREAMFILE* sf_b, ktsr_header* ktsr, V
|
||||
|
||||
/* KTSR - Koei Tecmo sound resource container */
|
||||
VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) {
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00, sf, "KTSR"))
|
||||
return NULL;
|
||||
/* others: see internal */
|
||||
|
||||
/* .ktsl2asbin: common [Atelier Ryza (PC/Switch), Nioh (PC)]
|
||||
* .asbin: Warriors Orochi 4 (PC) */
|
||||
if (!check_extensions(sf, "ktsl2asbin,asbin"))
|
||||
return NULL;
|
||||
|
||||
return init_vgmstream_ktsr_internal(sf, false) ;
|
||||
}
|
||||
|
||||
/* ASRS - container of KTSR found in newer games */
|
||||
VGMSTREAM* init_vgmstream_asrs(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00, sf, "ASRS"))
|
||||
@ -58,27 +69,15 @@ VGMSTREAM* init_vgmstream_asrs(STREAMFILE* sf) {
|
||||
/* 0x08: file size */
|
||||
/* 0x0c: null */
|
||||
|
||||
/* .srsa: header id (as generated by common tools) */
|
||||
/* .srsa: header id (as generated by common tools, probably "(something)asbin") */
|
||||
if (!check_extensions(sf, "srsa"))
|
||||
return NULL;
|
||||
|
||||
|
||||
/* mini-container of memory-KTSR (streams also have a "TSRS" for stream-KTSR).
|
||||
* .srsa/srst usually have hashed filenames, so it isn't easy to match them, so this is mainly useful
|
||||
* for .srsa with internal streams. */
|
||||
uint32_t subfile_offset = 0x10;
|
||||
uint32_t subfile_size = get_streamfile_size(sf) - subfile_offset;
|
||||
* .srsa/srst usually have hashed filenames, so it isn't easy to match them, so this
|
||||
* is mainly useful for .srsa with internal streams. */
|
||||
|
||||
/* use a subfile b/c it's a pain to change all the offsets below and it's also how engine would do it */
|
||||
temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, "ktsl2asbin");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_ktsr_internal(temp_sf, true);
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
return NULL;
|
||||
return init_vgmstream_ktsr_internal(sf, true);
|
||||
}
|
||||
|
||||
|
||||
@ -89,17 +88,18 @@ static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa) {
|
||||
int target_subsong = sf->stream_index;
|
||||
int separate_offsets = 0;
|
||||
|
||||
ktsr.is_srsa = is_srsa;
|
||||
if (ktsr.is_srsa) {
|
||||
ktsr.base_offset = 0x10;
|
||||
}
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00, sf, "KTSR"))
|
||||
if (!is_id32be(ktsr.base_offset + 0x00, sf, "KTSR"))
|
||||
return NULL;
|
||||
if (read_u32be(0x04, sf) != 0x777B481A) /* hash id: 0x777B481A=as, 0x0294DDFC=st, 0xC638E69E=gc */
|
||||
if (read_u32be(ktsr.base_offset + 0x04, sf) != 0x777B481A) /* hash id: 0x777B481A=as, 0x0294DDFC=st, 0xC638E69E=gc */
|
||||
return NULL;
|
||||
|
||||
/* .ktsl2asbin: common [Atelier Ryza (PC/Switch), Nioh (PC)]
|
||||
* .asbin: Warriors Orochi 4 (PC) */
|
||||
if (!check_extensions(sf, "ktsl2asbin,asbin"))
|
||||
return NULL;
|
||||
|
||||
/* KTSR can be a memory file (ktsl2asbin), streams (ktsl2stbin) and global config (ktsl2gcbin)
|
||||
* This accepts .ktsl2asbin with internal data or external streams as subsongs.
|
||||
@ -108,21 +108,28 @@ static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa) {
|
||||
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
ktsr.target_subsong = target_subsong;
|
||||
ktsr.is_srsa = is_srsa;
|
||||
|
||||
if (!parse_ktsr(&ktsr, sf))
|
||||
goto fail;
|
||||
|
||||
/* open companion body */
|
||||
if (ktsr.is_external) {
|
||||
const char* companion_ext = check_extensions(sf, "asbin") ? "stbin" : "ktsl2stbin";
|
||||
if (ktsr.is_srsa)
|
||||
companion_ext = "srst";
|
||||
if (ktsr.is_srsa) {
|
||||
/* try parsing TXTM if present, since .srsa+srst have hashed names and don't match unless renamed */
|
||||
sf_b = read_filemap_file(sf, 0);
|
||||
}
|
||||
|
||||
sf_b = open_streamfile_by_ext(sf, companion_ext);
|
||||
if (!sf_b) {
|
||||
vgm_logi("KTSR: companion file '*.%s' not found\n", companion_ext);
|
||||
goto fail;
|
||||
/* try (name).(ext), as seen in older games */
|
||||
const char* companion_ext = check_extensions(sf, "asbin") ? "stbin" : "ktsl2stbin";
|
||||
if (ktsr.is_srsa)
|
||||
companion_ext = "srst";
|
||||
|
||||
sf_b = open_streamfile_by_ext(sf, companion_ext);
|
||||
if (!sf_b) {
|
||||
vgm_logi("KTSR: companion file '*.%s' not found\n", companion_ext);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -539,8 +546,8 @@ static void parse_longname(ktsr_header* ktsr, STREAMFILE* sf) {
|
||||
uint32_t offset, end, name_offset;
|
||||
uint32_t stream_id;
|
||||
|
||||
offset = 0x40;
|
||||
end = get_streamfile_size(sf);
|
||||
offset = 0x40 + ktsr->base_offset;
|
||||
end = get_streamfile_size(sf) - ktsr->base_offset;
|
||||
while (offset < end) {
|
||||
uint32_t type = read_u32be(offset + 0x00, sf); /* hash-id? */
|
||||
uint32_t size = read_u32le(offset + 0x04, sf);
|
||||
@ -582,18 +589,18 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) {
|
||||
* up to 40: reserved
|
||||
* until end: entries (totals not defined) */
|
||||
|
||||
ktsr->platform = read_u8(0x0b,sf);
|
||||
ktsr->audio_id = read_u32le(0x0c,sf);
|
||||
ktsr->platform = read_u8(ktsr->base_offset + 0x0b,sf);
|
||||
ktsr->audio_id = read_u32le(ktsr->base_offset + 0x0c,sf);
|
||||
|
||||
if (read_u32le(0x18, sf) != read_u32le(0x1c, sf))
|
||||
if (read_u32le(ktsr->base_offset + 0x18, sf) != read_u32le(ktsr->base_offset + 0x1c, sf))
|
||||
goto fail;
|
||||
if (read_u32le(0x1c, sf) != get_streamfile_size(sf)) {
|
||||
if (read_u32le(ktsr->base_offset + 0x1c, sf) != get_streamfile_size(sf) - ktsr->base_offset) {
|
||||
vgm_logi("KTSR: incorrect file size (bad rip?)\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
offset = 0x40;
|
||||
end = get_streamfile_size(sf);
|
||||
offset = 0x40 + ktsr->base_offset;
|
||||
end = get_streamfile_size(sf) - ktsr->base_offset;
|
||||
while (offset < end) {
|
||||
uint32_t type = read_u32be(offset + 0x00, sf); /* hash-id? */
|
||||
uint32_t size = read_u32le(offset + 0x04, sf);
|
||||
@ -661,10 +668,13 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) {
|
||||
build_name(ktsr, sf);
|
||||
|
||||
/* skip TSRS header */
|
||||
if (ktsr->is_external && ktsr->is_srsa) {
|
||||
if (ktsr->base_offset) {
|
||||
//if (ktsr->is_external)
|
||||
for (int i = 0; i < ktsr->channels; i++) {
|
||||
ktsr->stream_offsets[i] += 0x10;
|
||||
ktsr->stream_offsets[i] += ktsr->base_offset;
|
||||
}
|
||||
|
||||
ktsr->extra_offset += ktsr->base_offset;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user