From a4bb71f17dc37a12a33873386ce68b55ac0d23fa Mon Sep 17 00:00:00 2001 From: halleyscometsw Date: Fri, 24 Aug 2012 17:36:40 +0000 Subject: [PATCH] DSP SCD support for DQX. Not completely happy with how this turned out (see TODOs), but it works. git-svn-id: https://vgmstream.svn.sourceforge.net/svnroot/vgmstream@992 51a99a44-fe44-0410-b1ba-c3e57ba2b86b --- src/Makefile | 3 +- src/layout/Makefile.unix.am | 1 + src/layout/layout.h | 2 + src/layout/scd_int_layout.c | 38 +++++++ src/libvgmstream.vcproj | 4 + src/meta/sqex_scd.c | 206 +++++++++++++++++++++++++++++++++++- src/vgmstream.c | 40 +++++++ src/vgmstream.h | 7 ++ 8 files changed, 298 insertions(+), 3 deletions(-) create mode 100644 src/layout/scd_int_layout.c diff --git a/src/Makefile b/src/Makefile index 7c41bb71..ddafdada 100644 --- a/src/Makefile +++ b/src/Makefile @@ -53,7 +53,8 @@ LAYOUT_OBJS=layout/ast_blocked.o \ layout/bdsp_blocked.o \ layout/tra_blocked.o \ layout/ps2_iab_blocked.o \ - layout/ps2_strlr_blocked.o + layout/ps2_strlr_blocked.o \ + layout/scd_int_layout.o META_OBJS=meta/adx_header.o \ meta/afc_header.o \ diff --git a/src/layout/Makefile.unix.am b/src/layout/Makefile.unix.am index 673928a1..caa724d5 100644 --- a/src/layout/Makefile.unix.am +++ b/src/layout/Makefile.unix.am @@ -36,5 +36,6 @@ liblayout_la_SOURCES += bdsp_blocked.c liblayout_la_SOURCES += tra_blocked.c liblayout_la_SOURCES += ps2_iab_blocked.c liblayout_la_SOURCES += ps2_strlr_blocked.c +liblayout_la_SOURCES += scd_int_layout.c EXTRA_DIST = layout.h diff --git a/src/layout/layout.h b/src/layout/layout.h index 5aa9c597..599e9353 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -58,6 +58,8 @@ void render_vgmstream_aix(sample * buffer, int32_t sample_count, VGMSTREAM * vgm void render_vgmstream_aax(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +void render_vgmstream_scd_int(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); + void psx_mgav_block_update(off_t block_offset, VGMSTREAM * vgmstream); void ps2_adm_block_update(off_t block_offset, VGMSTREAM * vgmstream); diff --git a/src/layout/scd_int_layout.c b/src/layout/scd_int_layout.c new file mode 100644 index 00000000..1a2c5e8a --- /dev/null +++ b/src/layout/scd_int_layout.c @@ -0,0 +1,38 @@ +#include "layout.h" +#include "../vgmstream.h" + +/* TODO: currently only properly handles mono substreams */ +/* TODO: there must be a reasonable way to respect the loop settings, as is + the substreams are in their own little world */ + +#define INTERLEAVE_BUF_SIZE 512 +void render_vgmstream_scd_int(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { + sample interleave_buf[INTERLEAVE_BUF_SIZE]; + int32_t samples_done = 0; + scd_int_codec_data *data = vgmstream->codec_data; + + while (samples_done < sample_count) + { + int32_t samples_to_do = INTERLEAVE_BUF_SIZE; + int c; + if (samples_to_do > sample_count - samples_done) + samples_to_do = sample_count - samples_done; + + for (c=0; c < data->substream_count; c++) + { + int32_t i; + + render_vgmstream(interleave_buf, + samples_to_do, data->substreams[c]); + + for (i=0; i < samples_to_do; i++) + { + buffer[(samples_done+i)*data->substream_count + c] = interleave_buf[i]; + } + } + + samples_done += samples_to_do; + + } +} + diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 997e76fc..06d63172 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1366,6 +1366,10 @@ RelativePath=".\layout\psx_mgav_blocked.c" > + + diff --git a/src/meta/sqex_scd.c b/src/meta/sqex_scd.c index 3bb03818..bd232495 100644 --- a/src/meta/sqex_scd.c +++ b/src/meta/sqex_scd.c @@ -3,6 +3,23 @@ #include "../util.h" /* Square-Enix SCD (FF XIII, XIV) */ + +/* special streamfile type to handle deinterleaving of complete files, + (based heavily on AIXSTREAMFILE */ +typedef struct _SCDINTSTREAMFILE +{ + STREAMFILE sf; + STREAMFILE *real_file; + const char * filename; + off_t start_physical_offset; + off_t current_logical_offset; + off_t interleave_block_size; + off_t stride_size; + size_t total_size; +} SCDINTSTREAMFILE; + +static STREAMFILE *open_scdint_with_STREAMFILE(STREAMFILE *file, const char * filename, off_t start_offset, off_t interleave_block_size, off_t stride_size, size_t total_size); + VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[260]; @@ -193,8 +210,71 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { break; case 0xA: /* GC/Wii DSP ADPCM */ - /* TODO */ - goto fail; + { + STREAMFILE * file; + int i; + const off_t interleave_size = 0x800; + const off_t stride_size = interleave_size * channel_count; + + size_t total_size; + + scd_int_codec_data * data = NULL; + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_scd_int; + + /* a normal DSP header... */ + vgmstream->num_samples = read_32bitBE(start_offset+0,streamFile); + total_size = (read_32bitBE(start_offset+4,streamFile)+1)/2; + + if (loop_flag) { + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end+1; + } + + /* verify other channel headers */ + for (i = 1; i < channel_count; i++) { + if (read_32bitBE(start_offset+interleave_size*i+0,streamFile) != vgmstream->num_samples || + (read_32bitBE(start_offset+4,streamFile)+1)/2 != total_size) { + goto fail; + } + + } + + /* the primary streamfile we'll be using */ + file = streamFile->open(streamFile,filename,stride_size); + if (!file) + goto fail; + + vgmstream->ch[0].streamfile = file; + + data = malloc(sizeof(scd_int_codec_data)); + data->substream_count = channel_count; + data->substreams = calloc(channel_count, sizeof(VGMSTREAM *)); + data->intfiles = calloc(channel_count, sizeof(STREAMFILE *)); + + vgmstream->codec_data = data; + + for (i=0;isubstreams[i] = init_vgmstream_ngc_dsp_std(intfile); + data->intfiles[i] = intfile; + if (!data->substreams[i]) + goto fail; + + /* TODO: only handles mono substreams, though that's all we have with DSP */ + /* save start things so we can restart for seeking/looping */ + /* copy the channels */ + memcpy(data->substreams[i]->start_ch,data->substreams[i]->ch,sizeof(VGMSTREAMCHANNEL)*1); + /* copy the whole VGMSTREAM */ + memcpy(data->substreams[i]->start_vgmstream,data->substreams[i],sizeof(VGMSTREAM)); + + } + + } + break; default: goto fail; } @@ -202,6 +282,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { vgmstream->meta_type = meta_SQEX_SCD; /* open the file for reading */ + if (vgmstream->layout_type != layout_scd_int) { int i; STREAMFILE * file; @@ -223,3 +304,124 @@ fail: if (vgmstream) close_vgmstream(vgmstream); return NULL; } + +static STREAMFILE *open_scdint_impl(SCDINTSTREAMFILE *streamfile,const char * const filename,size_t buffersize) +{ + SCDINTSTREAMFILE *newfile; + + if (strcmp(filename, streamfile->filename)) + return NULL; + + newfile = malloc(sizeof(SCDINTSTREAMFILE)); + if (!newfile) + return NULL; + + memcpy(newfile,streamfile,sizeof(SCDINTSTREAMFILE)); + return &newfile->sf; +} + +static void close_scdint(SCDINTSTREAMFILE *streamfile) +{ + free(streamfile); + return; +} + +static size_t get_size_scdint(SCDINTSTREAMFILE *streamfile) +{ + return streamfile->total_size; +} + +static size_t get_offset_scdint(SCDINTSTREAMFILE *streamfile) +{ + return streamfile->current_logical_offset; +} + +static void get_name_scdint(SCDINTSTREAMFILE *streamfile, char *buffer, size_t length) +{ + strncpy(buffer,streamfile->filename,length); + buffer[length-1]='\0'; +} + +static size_t read_scdint(SCDINTSTREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length) +{ + size_t sz = 0; + + while (length > 0) + { + off_t to_read; + off_t length_available; + off_t block_num; + off_t intrablock_offset; + off_t physical_offset; + + + block_num = offset / streamfile->interleave_block_size; + intrablock_offset = offset % streamfile->interleave_block_size; + streamfile->current_logical_offset = offset; + physical_offset = streamfile->start_physical_offset + block_num * streamfile->stride_size + intrablock_offset; + + length_available = + streamfile->interleave_block_size - intrablock_offset; + + if (length < length_available) + { + to_read = length; + } + else + { + to_read = length_available; + } + + if (to_read > 0) + { + size_t bytes_read; + + bytes_read = read_streamfile(dest, + physical_offset, + to_read, streamfile->real_file); + + sz += bytes_read; + + streamfile->current_logical_offset = offset + bytes_read; + + if (bytes_read != to_read) + { + /* an error which we will not attempt to handle here */ + return sz; + } + + dest += bytes_read; + offset += bytes_read; + length -= bytes_read; + } + } + + return sz; +} + +/* start_offset is for *this* interleaved stream */ +static STREAMFILE *open_scdint_with_STREAMFILE(STREAMFILE *file, const char * filename, off_t start_offset, off_t interleave_block_size, off_t stride_size, size_t total_size) +{ + SCDINTSTREAMFILE * scd = malloc(sizeof(SCDINTSTREAMFILE)); + + if (!scd) + return NULL; + + scd->sf.read = (void*)read_scdint; + scd->sf.get_size = (void*)get_size_scdint; + scd->sf.get_offset = (void*)get_offset_scdint; + scd->sf.get_name = (void*)get_name_scdint; + scd->sf.get_realname = (void*)get_name_scdint; + scd->sf.open = (void*)open_scdint_impl; + scd->sf.close = (void*)close_scdint; + + scd->real_file = file; + scd->filename = filename; + scd->start_physical_offset = start_offset; + scd->current_logical_offset = 0; + scd->interleave_block_size = interleave_block_size; + scd->stride_size = stride_size; + scd->total_size = total_size; + + return &scd->sf; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index 759f0743..03235410 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -481,6 +481,17 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { nwa_codec_data *data = vgmstream->codec_data; reset_nwa(data->nwa); } + + if (vgmstream->layout_type==layout_scd_int) { + scd_int_codec_data *data = vgmstream->codec_data; + int i; + + for (i=0;isubstream_count;i++) + { + reset_vgmstream(data->substreams[i]); + } + } + } /* simply allocate memory for the VGMSTREAM and its channels */ @@ -689,6 +700,29 @@ void close_vgmstream(VGMSTREAM * vgmstream) { vgmstream->codec_data = NULL; } + if (vgmstream->layout_type==layout_scd_int) { + scd_int_codec_data *data = vgmstream->codec_data; + + if (data) { + if (data->substreams) { + int i; + for (i=0;isubstream_count;i++) { + + /* note that the scd_int close_streamfile won't do anything + * but deallocate itself, there is only one open file and + * that is in vgmstream->ch[0].streamfile */ + close_vgmstream(data->substreams[i]); + close_streamfile(data->intfiles[i]); + } + free(data->substreams); + free(data->intfiles); + } + + free(data); + } + vgmstream->codec_data = NULL; + } + /* now that the special cases have had their chance, clean up the standard items */ for (i=0;ichannels;i++) { if (vgmstream->ch[i].streamfile) { @@ -779,6 +813,9 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_aax: render_vgmstream_aax(buffer,sample_count,vgmstream); break; + case layout_scd_int: + render_vgmstream_scd_int(buffer,sample_count,vgmstream); + break; } } @@ -1943,6 +1980,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { case layout_tra_blocked: snprintf(temp,TEMPSIZE,"TRA blocked"); break; + case layout_scd_int: + snprintf(temp,TEMPSIZE,"SCD multistream interleave"); + break; default: snprintf(temp,TEMPSIZE,"INCONCEIVABLE"); } diff --git a/src/vgmstream.h b/src/vgmstream.h index 3f00cf98..9ef4ddea 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -182,6 +182,7 @@ typedef enum { layout_tra_blocked, /* DefJam Rapstar .tra blocks */ layout_ps2_iab_blocked, layout_ps2_strlr_blocked, + layout_scd_int, /* deinterleave done by the SCDINTSTREAMFILE */ } layout_t; /* The meta type specifies how we know what we know about the file. We may know because of a header we read, some of it may have been guessed from filenames, etc. */ @@ -741,6 +742,12 @@ typedef struct { NWAData *nwa; } nwa_codec_data; +typedef struct { + int substream_count; + VGMSTREAM **substreams; + STREAMFILE **intfiles; +} scd_int_codec_data; + /* do format detection, return pointer to a usable VGMSTREAM, or NULL on failure */ VGMSTREAM * init_vgmstream(const char * const filename);