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
+}