diff --git a/doc/TXTH.md b/doc/TXTH.md index 34af042d..92eba6c1 100644 --- a/doc/TXTH.md +++ b/doc/TXTH.md @@ -139,10 +139,14 @@ as explained below, but often will use default values. Accepted codec strings: # - PCM4_U PCM 4-bit unsigned # * Variation with modified encoding # - OKI16 OKI ADPCM with 16-bit output (not VOX/Dialogic 12-bit) -# * For few PS2 games (Sweet Legacy, Hooligan) +# * For rare PS2 games (Sweet Legacy, Hooligan) # - AAC Advanced Audio Coding (raw without .mp4) # * For some 3DS games and many iOS games # * Should set skip_samples (around 1024 but varies) +# - TGC Tiger Game.com 4-bit ADPCM +# * For Tiger Game.com +# - ASF Argonaut ASF ADPCM +# * For rare Argonaut games [Croc (SAT)] codec = (codec string) ``` @@ -679,7 +683,7 @@ sample_rate = @0x04 #reads at 0x1204 ## Examples -**Colin McRae DiRT (PC) .wip.txth** +#### Colin McRae DiRT (PC) .wip.txth ``` id_value = 0x00000000 #check that value at 0x00 is really 0x00000000 id_offset = @0x00:BE @@ -693,7 +697,7 @@ loop_start_sample = 0 loop_end_sample = data_size ``` -**Kim Possible: What's the Switch (PS2) .str.txth** +#### Kim Possible: What's the Switch (PS2) .str.txth ``` codec = PSX interleave = 0x2000 @@ -703,7 +707,7 @@ num_samples = data_size interleave_last = auto ``` -**Manhunt (Xbox) .rib.txth** +#### Manhunt (Xbox) .rib.txth ``` codec = XBOX codec_mode = 1 #interleaved XBOX @@ -715,7 +719,7 @@ start_offset = 0x00 num_samples = data_size ``` -**Pitfall The Lost Expedition (PC) .txth** +#### Pitfall The Lost Expedition (PC) .txth ``` codec = DVI_IMA interleave = 0x80 @@ -725,7 +729,7 @@ sample_rate = 44100 num_samples = data_size ``` -**Spy Hunter (GC) .pcm.txth** +#### Spy Hunter (GC) .pcm.txth ``` codec = PCM8 sample_rate = 32000 @@ -734,7 +738,7 @@ start_offset = 0 num_samples = data_size ``` -**Ultimate Board Game Collection (Wii) .dsp.txth** +#### Ultimate Board Game Collection (Wii) .dsp.txth ``` codec = NGC_DSP interleave = 0x10000 @@ -753,7 +757,8 @@ coef_offset = 0x1c coef_spacing = 0x10000 coef_endianness = BE ``` -**Aladdin in Nasira's Revenge (PS1) .cvs.txth** + +#### Aladdin in Nasira's Revenge (PS1) .cvs.txth ``` codec = PSX interleave = 0x10 @@ -763,7 +768,7 @@ padding_size = auto-empty num_samples = data_size ``` -**Shikigami no Shiro - Nanayozuki Gensoukyoku (PS2) bgm.txth** +#### Shikigami no Shiro - Nanayozuki Gensoukyoku (PS2) bgm.txth ``` codec = PSX interleave = 0x1000 @@ -790,7 +795,7 @@ loop_end_sample = @0x10 * channels data_size = @0x08 * channels #for bitrate ``` -**Dragon Poker (Mobile) .snd.txth** +#### Dragon Poker (Mobile) .snd.txth ``` # parse MP3 inside the .snd subfile_extension = mp3 @@ -805,7 +810,7 @@ loop_start_sample = 0 loop_end_sample = data_size ``` -**Simple 2000 Series Vol. 120 - The Saigo no Nihonhei (PS2) .xag.txth** +#### Simple 2000 Series Vol. 120 - The Saigo no Nihonhei (PS2) .xag.txth ``` header_file = TSNDDRVC.IRX @@ -853,15 +858,14 @@ subfile_extension = seb subfile_size = ((@0x04 - @0x00) & 0xFFFFF) * 0x800 ``` - -**Zack & Wiki (Wii) .ssd.txth** +#### Zack & Wiki (Wii) .ssd.txth ``` header_file = bgm_S01.srt name_table = .names.txt base_offset = @0x0c:BE base_offset = base_offset + @0x08:BE + name_value -base_offset = base_offset + @0x00:BE +base_offset = base_offset + @0x00:BE - name_value codec = NGC_DSP channels = 2 @@ -891,7 +895,7 @@ st_s01_02c.ssd: 7*0x04 ``` -**Zack & Wiki (Wii) st_s01_00a.txth** +#### Zack & Wiki (Wii) st_s01_00a.txth ``` #alt from above with untouched folders header_file = Sound/BGM/bgm_S01.srt @@ -900,7 +904,7 @@ name_table = .names.txt base_offset = @0x0c:BE base_offset = base_offset + @0x08:BE + name_value -base_offset = base_offset + @0x00:BE +base_offset = base_offset + @0x00:BE - name_value codec = NGC_DSP channels = 2 @@ -929,3 +933,11 @@ coef_endianness = BE *snd/stream/st_s01_02c.ssd: 7*0x04 # uses wildcards for full paths from plugins ``` + +#### Croc (SAT) .asf.txth +``` +codec = ASF +sample_rate = 22050 +channels = 2 +num_samples = data_size +``` diff --git a/src/coding/asf_decoder.c b/src/coding/asf_decoder.c index 00ee61d7..db81b75b 100644 --- a/src/coding/asf_decoder.c +++ b/src/coding/asf_decoder.c @@ -54,3 +54,8 @@ void decode_asf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, stream->adpcm_history1_32 = hist1; stream->adpcm_history2_32 = hist2; } + +int32_t asf_bytes_to_samples(size_t bytes, int channels) { + if (channels <= 0) return 0; + return bytes / channels / 0x11 * 32; +} diff --git a/src/meta/txth.c b/src/meta/txth.c index f97a8167..f98e6296 100644 --- a/src/meta/txth.c +++ b/src/meta/txth.c @@ -36,7 +36,8 @@ typedef enum { PCM4_U = 26, /* 4-bit unsigned PCM (3rd and 4th gen games) */ OKI16 = 27, /* OKI ADPCM with 16-bit output (unlike OKI/VOX/Dialogic ADPCM's 12-bit) */ AAC = 28, /* Advanced Audio Coding (raw without .mp4) */ - TGC = 29 /* Tiger Game.com 4-bit ADPCM */ + TGC = 29, /* Tiger Game.com 4-bit ADPCM */ + ASF = 30, /* Argonaut ASF 4-bit ADPCM */ } txth_type; typedef struct { @@ -223,6 +224,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { case PCM4_U: coding = coding_PCM4_U; break; case OKI16: coding = coding_OKI16; break; case TGC: coding = coding_TGC; break; + case ASF: coding = coding_ASF; break; default: goto fail; } @@ -334,6 +336,11 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; break; + case coding_ASF: + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x11; + break; + case coding_MS_IMA: if (!txth.interleave) goto fail; /* creates garbage */ @@ -450,7 +457,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { } else { /* fake header FFmpeg */ - uint8_t buf[200]; + uint8_t buf[0x100]; int32_t bytes; if (txth.codec == ATRAC3) { @@ -465,14 +472,14 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { else if (txth.codec == ATRAC3PLUS) { int block_size = txth.interleave; - bytes = ffmpeg_make_riff_atrac3plus(buf, 200, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_size, txth.skip_samples); + bytes = ffmpeg_make_riff_atrac3plus(buf, sizeof(buf), vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_size, txth.skip_samples); ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size); if ( !ffmpeg_data ) goto fail; } else if (txth.codec == XMA1) { int xma_stream_mode = txth.codec_mode == 1 ? 1 : 0; - bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, xma_stream_mode); + bytes = ffmpeg_make_riff_xma1(buf, sizeof(buf), vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, xma_stream_mode); ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size); if ( !ffmpeg_data ) goto fail; } @@ -482,7 +489,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { block_size = txth.interleave ? txth.interleave : 2048; block_count = txth.data_size / block_size; - bytes = ffmpeg_make_riff_xma2(buf, 200, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + bytes = ffmpeg_make_riff_xma2(buf, sizeof(buf), vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size); if ( !ffmpeg_data ) goto fail; } @@ -848,6 +855,7 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char else if (is_string(val,"AAC")) txth->codec = AAC; else if (is_string(val,"TGC")) txth->codec = TGC; else if (is_string(val,"GCOM_ADPCM")) txth->codec = TGC; + else if (is_string(val,"ASF")) txth->codec = ASF; else goto fail; /* set common interleaves to simplify usage @@ -1730,6 +1738,8 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) { #endif case AC3: return ac3_bytes_to_samples(bytes, txth->interleave, txth->channels); + case ASF: + return asf_bytes_to_samples(bytes, txth->channels); /* XMA bytes-to-samples is done at the end as the value meanings are a bit different */ case XMA1: