From 90c4724ddbc62adc3a9f764736103199aa80a175 Mon Sep 17 00:00:00 2001 From: EdnessP <55930127+EdnessP@users.noreply.github.com> Date: Sun, 25 Jun 2023 17:11:01 +0300 Subject: [PATCH 1/2] RWS: pre-AWD variant 0x809 (Burnout 2: Point of Impact) --- src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 + src/meta/meta.h | 6 +- src/meta/rws_mono.c | 134 +++++++++++++++++++++++++++++++ src/vgmstream.c | 1 + 5 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 src/meta/rws_mono.c diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index d08efabe..0003b4bf 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -606,6 +606,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 01de1365..164ec6d5 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -1639,6 +1639,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/meta.h b/src/meta/meta.h index 7bee9ea9..ae3d5266 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -21,8 +21,6 @@ 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); @@ -990,4 +988,8 @@ VGMSTREAM* init_vgmstream_sscf_encrypted(STREAMFILE* sf); VGMSTREAM* init_vgmstream_ego_dic(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_awd(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_rws_mono(STREAMFILE* sf); + #endif /*_META_H*/ diff --git a/src/meta/rws_mono.c b/src/meta/rws_mono.c new file mode 100644 index 00000000..1b12f5ac --- /dev/null +++ b/src/meta/rws_mono.c @@ -0,0 +1,134 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../util/endianness.h" + + +/* RWS - RenderWare Stream (with the tag 0x809) */ +VGMSTREAM* init_vgmstream_rws_mono(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + bool big_endian; + char header_name[STREAM_NAME_SIZE], stream_name[STREAM_NAME_SIZE]; + int bit_depth = 0, channels = 0, idx, interleave, loop_flag, sample_rate = 0, total_subsongs, target_subsong = sf->stream_index; + read_u32_t read_u32; + off_t chunk_offset, header_offset, stream_offset = 0; + size_t chunk_size, header_size, stream_size = 0; + + + /* checks */ + if (read_u32le(0x00, sf) != 0x809) /* File ID */ + goto fail; + if (read_u32le(0x04, sf) + 0x0C != get_streamfile_size(sf)) + goto fail; + /* Burnout 2: Point of Impact (PS2, GCN, Xbox): + * Predecessor to the common 0x80D-0x80F tag rws.c (which is also used in B2) + * with some parts of it later reworked into awd.c seemingly */ + if (!check_extensions(sf, "rws")) + goto fail; + + /* Uses various chunk IDs across the file: + * 0x00000809: File ID + * 0x0000080A: File header ID + * 0x0000080C: File data ID + * + * 0x00000802: Stream ID + * 0x00000803: Stream header ID + * 0x00000804: Stream data ID + */ + + chunk_offset = 0x0C; + if (read_u32le(chunk_offset, sf) != 0x80A) /* File header ID */ + goto fail; + chunk_size = read_u32le(chunk_offset + 0x04, sf); /* usually 0x44 */ + + read_string(header_name, STREAM_NAME_SIZE, chunk_offset + 0x40, sf); + + chunk_offset += chunk_size + 0x0C; + if (read_u32le(chunk_offset, sf) != 0x80C) /* File data ID */ + goto fail; + + big_endian = guess_endian32(chunk_offset + 0x0C, sf); + read_u32 = big_endian ? read_u32be : read_u32le; + + total_subsongs = read_u32(chunk_offset + 0x0C, sf); + + if (!target_subsong) + target_subsong = 1; + + chunk_offset += 0x10; + for (idx = 1; idx <= total_subsongs; idx++) { + if (read_u32le(chunk_offset, sf) != 0x802) /* Stream ID */ + goto fail; + chunk_size = read_u32le(chunk_offset + 0x04, sf); + + if (idx == target_subsong) { + header_offset = chunk_offset + 0x0C; + if (read_u32le(header_offset, sf) != 0x803) /* Stream header ID */ + goto fail; + header_size = read_u32le(header_offset + 0x04, sf); /* usually 0xA0 */ + + sample_rate = read_u32(header_offset + 0x10, sf); + stream_size = read_u32(header_offset + 0x18, sf); + bit_depth = read_u8(header_offset + 0x1C, sf); + channels = read_u8(header_offset + 0x1D, sf); /* always 1? */ + if (channels != 1) + goto fail; + + /* Assumed misc data offs/size at header_offset + 0x20 to +0x24 + * which is always empty since GCN uses PCM S16BE encoding here */ + + read_string(stream_name, STREAM_NAME_SIZE, header_offset + 0x7C, sf); + + stream_offset = header_offset + header_size + 0x0C; + if (read_u32le(stream_offset, sf) != 0x804) /* Stream data ID */ + goto fail; + if (read_u32le(stream_offset + 0x04, sf) != stream_size) + goto fail; + } + chunk_offset += chunk_size + 0x0C; + } + + if (total_subsongs < 1 || target_subsong > total_subsongs) + goto fail; + + interleave = 0; + loop_flag = 0; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) + goto fail; + + vgmstream->meta_type = meta_RWS; + 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; + + /* Likely unreliable, but currently only can be tested with Burnout 2 */ + switch (bit_depth) { + case 4: /* PS-ADPCM, normally DSP-ADPCM would be 4 too (as is in awd.c) but GCN uses PCM */ + vgmstream->num_samples = ps_bytes_to_samples(stream_size, channels); + vgmstream->coding_type = coding_PSX; + break; + + case 16: /* PCM Signed 16-bit */ + vgmstream->num_samples = pcm16_bytes_to_samples(stream_size, channels); + vgmstream->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE; + break; + + default: + goto fail; + } + + snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%s/%s", header_name, stream_name); + + if (!vgmstream_open_stream(vgmstream, sf, stream_offset + 0x0C)) + goto fail; + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} \ No newline at end of file diff --git a/src/vgmstream.c b/src/vgmstream.c index 495c4ba3..e3a90bca 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -529,6 +529,7 @@ init_vgmstream_t init_vgmstream_functions[] = { init_vgmstream_s_p_sth, init_vgmstream_utf_ahx, init_vgmstream_ego_dic, + init_vgmstream_rws_mono, /* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */ init_vgmstream_scd_pcm, From 0a1e79d27cc7332d228c400befa3180d66602a99 Mon Sep 17 00:00:00 2001 From: EdnessP <55930127+EdnessP@users.noreply.github.com> Date: Sun, 25 Jun 2023 17:25:19 +0300 Subject: [PATCH 2/2] RWS 0x809: remove size check (PS2 sometimes pads 4 extra bytes) --- src/meta/rws_mono.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/meta/rws_mono.c b/src/meta/rws_mono.c index 1b12f5ac..ea88ccf5 100644 --- a/src/meta/rws_mono.c +++ b/src/meta/rws_mono.c @@ -17,8 +17,7 @@ VGMSTREAM* init_vgmstream_rws_mono(STREAMFILE* sf) { /* checks */ if (read_u32le(0x00, sf) != 0x809) /* File ID */ goto fail; - if (read_u32le(0x04, sf) + 0x0C != get_streamfile_size(sf)) - goto fail; + /* Burnout 2: Point of Impact (PS2, GCN, Xbox): * Predecessor to the common 0x80D-0x80F tag rws.c (which is also used in B2) * with some parts of it later reworked into awd.c seemingly */ @@ -29,7 +28,7 @@ VGMSTREAM* init_vgmstream_rws_mono(STREAMFILE* sf) { * 0x00000809: File ID * 0x0000080A: File header ID * 0x0000080C: File data ID - * + * * 0x00000802: Stream ID * 0x00000803: Stream header ID * 0x00000804: Stream data ID @@ -73,7 +72,7 @@ VGMSTREAM* init_vgmstream_rws_mono(STREAMFILE* sf) { if (channels != 1) goto fail; - /* Assumed misc data offs/size at header_offset + 0x20 to +0x24 + /* Assumed misc data offs/size at header_offset + 0x20 to +0x24 * which is always empty since GCN uses PCM S16BE encoding here */ read_string(stream_name, STREAM_NAME_SIZE, header_offset + 0x7C, sf); @@ -131,4 +130,4 @@ VGMSTREAM* init_vgmstream_rws_mono(STREAMFILE* sf) { fail: close_vgmstream(vgmstream); return NULL; -} \ No newline at end of file +}