From a04988589c14e5136324a8b1e81b5a99f6cbb762 Mon Sep 17 00:00:00 2001 From: EdnessP <55930127+EdnessP@users.noreply.github.com> Date: Mon, 12 Jun 2023 21:37:25 +0300 Subject: [PATCH] AWD: RenderWare Audio Wave Dictionary --- src/formats.c | 4 + src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 6 ++ src/meta/awd.c | 143 +++++++++++++++++++++++++++++++ src/meta/meta.h | 2 + src/vgmstream.c | 1 + src/vgmstream_types.h | 1 + 7 files changed, 158 insertions(+) create mode 100644 src/meta/awd.c diff --git a/src/formats.c b/src/formats.c index a51c3d85..ac8324dd 100644 --- a/src/formats.c +++ b/src/formats.c @@ -89,6 +89,7 @@ static const char* extension_list[] = { "awa", //txth/reserved [Missing Parts Side A (PS2)] "awb", "awc", + "awd", "b1s", "baf", @@ -230,6 +231,7 @@ static const char* extension_list[] = { "hxg", "hxx", "hwas", + "hwd", "iab", "iadp", @@ -316,6 +318,7 @@ static const char* extension_list[] = { "lsf", "lstm", //fake extension for .stm "lwav", //fake extension for .wav + "lwd", "lwma", //fake extension for .wma, FFmpeg/not parsed "mab", @@ -1420,6 +1423,7 @@ static const meta_info meta_info_list[] = { {meta_VAB, "Sony VAB header"}, {meta_BIGRP, "Inti Creates .BIGRP header"}, {meta_DIC1, "Codemasters DIC1 header"}, + {meta_AWD, "RenderWare Audio Wave Dictionary header"}, }; void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index a4f990cc..d4cadd40 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -358,6 +358,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index f80325d4..8b565ebf 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -910,6 +910,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files @@ -2068,5 +2071,8 @@ util\Source Files + + meta\Source Files + \ No newline at end of file diff --git a/src/meta/awd.c b/src/meta/awd.c new file mode 100644 index 00000000..d9c3871c --- /dev/null +++ b/src/meta/awd.c @@ -0,0 +1,143 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../util/endianness.h" + + +/* AWD - Audio Wave Dictionary (RenderWare) */ +VGMSTREAM* init_vgmstream_awd(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + char header_name[STREAM_NAME_SIZE], stream_name[STREAM_NAME_SIZE]; + int bit_depth = 0, channels = 0, sample_rate = 0, stream_codec = -1, total_subsongs = 0, target_subsong = sf->stream_index; + int interleave, loop_flag; + off_t data_offset, data_offset_2, header_name_offset, misc_data_offset, linked_list_offset, wavedict_offset; + off_t entry_info_offset, entry_name_offset, entry_uuid_offset, next_dict_entry, prev_dict_entry, stream_offset; + read_u32_t read_u32; + size_t misc_data_size, stream_size = 0; + + /* checks */ + if ((read_u32le(0x00, sf) != 0x809) && (read_u32be(0x00, sf) != 0x809)) + goto fail; + + /* .awd: standard (Black, Burnout series, Call of Duty: Finest Hour) + * .hwd/lwd: high/low vehicle engine sounds (Burnout series) */ + if (!check_extensions(sf, "awd,hwd,lwd")) + goto fail; + + read_u32 = guess_endian32(0x00, sf) ? read_u32be : read_u32le; + + data_offset = read_u32(0x08, sf); + wavedict_offset = read_u32(0x0C, sf); + /* Platform UUIDs in big endian + * {FD9D32D3-E179-426A-8424-14720AC7F648}: GameCube + * {ACC9EAAA-38FC-1749-AE81-64EADBC79353}: PlayStation 2 + * {042D3A45-5FE4-C84B-81F0-DF758B01F273}: Xbox */ + //platf_uuid_1 = read_u64be(0x18, sf); + //platf_uuid_2 = read_u64be(0x20, sf); + data_offset_2 = read_u32(0x28, sf); + + if (data_offset != data_offset_2) + goto fail; + + header_name_offset = wavedict_offset + 0x04; + linked_list_offset = wavedict_offset + 0x0C; + + if (header_name_offset) /* not used in Black */ + read_string(header_name, STREAM_NAME_SIZE, header_name_offset, sf); + + if (target_subsong == 0) + target_subsong = 1; + + /* Linked lists have no total subsong count; instead iterating + * through all of them, until it returns to the 1st song again */ + prev_dict_entry = read_u32(linked_list_offset + 0x00, sf); + next_dict_entry = read_u32(linked_list_offset + 0x04, sf); + + while (next_dict_entry != linked_list_offset) { + total_subsongs++; + + entry_info_offset = read_u32(next_dict_entry + 0x08, sf); + + prev_dict_entry = read_u32(next_dict_entry + 0x00, sf); + next_dict_entry = read_u32(next_dict_entry + 0x04, sf); + + /* is at the correct target song index */ + if (total_subsongs == target_subsong) { + entry_uuid_offset = read_u32(entry_info_offset + 0x00, sf); /* only used in Burnout games */ + entry_name_offset = read_u32(entry_info_offset + 0x04, sf); + + sample_rate = read_u32(entry_info_offset + 0x10, sf); + stream_codec = read_u32(entry_info_offset + 0x14, sf); + stream_size = read_u32(entry_info_offset + 0x18, sf); + bit_depth = read_8bit(entry_info_offset + 0x1C, sf); + channels = read_8bit(entry_info_offset + 0x1D, sf); /* always 1, haven't seen any stereo entries */ + if (channels != 1) + goto fail; + + /* stores a "00: GCN ADPCM Header" section, otherwise empty */ + misc_data_offset = read_u32(entry_info_offset + 0x20, sf); + misc_data_size = read_u32(entry_info_offset + 0x24, sf); + + stream_offset = read_u32(entry_info_offset + 0x4C, sf) + data_offset; + + read_string(stream_name, STREAM_NAME_SIZE, entry_name_offset, sf); + } + } + + if (total_subsongs < 1 || target_subsong > total_subsongs) + goto fail; + + interleave = 0; + loop_flag = 0; + + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) + goto fail; + + vgmstream->meta_type = meta_AWD; + vgmstream->layout_type = layout_none; + vgmstream->sample_rate = sample_rate; + vgmstream->stream_size = stream_size; + vgmstream->num_streams = total_subsongs; + vgmstream->interleave_block_size = interleave; + + if (header_name_offset) + snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%s/%s", header_name, stream_name); + else + snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%s", stream_name); + + switch (stream_codec) { + case 0x00: /* PS2 (All Games) */ + vgmstream->num_samples = ps_bytes_to_samples(stream_size, channels); + vgmstream->coding_type = coding_PSX; + break; + + case 0x01: /* Xbox (Black, Burnout) */ + vgmstream->num_samples = pcm16_bytes_to_samples(stream_size, channels); + vgmstream->coding_type = coding_PCM16LE; + break; + + case 0x03: /* GCN (Call of Duty: Finest Hour) */ + vgmstream->num_samples = dsp_bytes_to_samples(stream_size, channels); + dsp_read_coefs_be(vgmstream, sf, misc_data_offset + 0x1C, 0); + vgmstream->coding_type = coding_NGC_DSP; + break; + + case 0x04: /* Xbox (Black, Call of Duty: Finest Hour) */ + vgmstream->num_samples = xbox_ima_bytes_to_samples(stream_size, channels); + vgmstream->coding_type = coding_XBOX_IMA; + break; + + default: + VGM_LOG("AWD: unknown codec type %d\n", stream_codec); + goto fail; + } + + if (!vgmstream_open_stream(vgmstream, sf, stream_offset)) + goto fail; + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/meta.h b/src/meta/meta.h index cf20adec..7bee9ea9 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -21,6 +21,8 @@ VGMSTREAM * init_vgmstream_agsc(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ast(STREAMFILE *streamFile); +VGMSTREAM* init_vgmstream_awd(STREAMFILE *sf); + VGMSTREAM * init_vgmstream_brstm(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_cstr(STREAMFILE *streamFile); diff --git a/src/vgmstream.c b/src/vgmstream.c index dbfbe0f0..495c4ba3 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -28,6 +28,7 @@ init_vgmstream_t init_vgmstream_functions[] = { init_vgmstream_nds_strm, init_vgmstream_afc, init_vgmstream_ast, + init_vgmstream_awd, init_vgmstream_halpst, init_vgmstream_rs03, init_vgmstream_ngc_dsp_std, diff --git a/src/vgmstream_types.h b/src/vgmstream_types.h index 84cb27e2..596daa1c 100644 --- a/src/vgmstream_types.h +++ b/src/vgmstream_types.h @@ -711,6 +711,7 @@ typedef enum { meta_VAB, meta_BIGRP, meta_DIC1, + meta_AWD, } meta_t;