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
This commit is contained in:
halleyscometsw 2012-08-24 17:36:40 +00:00
parent c6d3e4d343
commit a4bb71f17d
8 changed files with 298 additions and 3 deletions

View File

@ -53,7 +53,8 @@ LAYOUT_OBJS=layout/ast_blocked.o \
layout/bdsp_blocked.o \ layout/bdsp_blocked.o \
layout/tra_blocked.o \ layout/tra_blocked.o \
layout/ps2_iab_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_OBJS=meta/adx_header.o \
meta/afc_header.o \ meta/afc_header.o \

View File

@ -36,5 +36,6 @@ liblayout_la_SOURCES += bdsp_blocked.c
liblayout_la_SOURCES += tra_blocked.c liblayout_la_SOURCES += tra_blocked.c
liblayout_la_SOURCES += ps2_iab_blocked.c liblayout_la_SOURCES += ps2_iab_blocked.c
liblayout_la_SOURCES += ps2_strlr_blocked.c liblayout_la_SOURCES += ps2_strlr_blocked.c
liblayout_la_SOURCES += scd_int_layout.c
EXTRA_DIST = layout.h EXTRA_DIST = layout.h

View File

@ -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_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 psx_mgav_block_update(off_t block_offset, VGMSTREAM * vgmstream);
void ps2_adm_block_update(off_t block_offset, VGMSTREAM * vgmstream); void ps2_adm_block_update(off_t block_offset, VGMSTREAM * vgmstream);

View File

@ -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;
}
}

View File

@ -1366,6 +1366,10 @@
RelativePath=".\layout\psx_mgav_blocked.c" RelativePath=".\layout\psx_mgav_blocked.c"
> >
</File> </File>
<File
RelativePath=".\layout\scd_int_layout.c"
>
</File>
<File <File
RelativePath=".\layout\str_snds_blocked.c" RelativePath=".\layout\str_snds_blocked.c"
> >

View File

@ -3,6 +3,23 @@
#include "../util.h" #include "../util.h"
/* Square-Enix SCD (FF XIII, XIV) */ /* 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 * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
char filename[260]; char filename[260];
@ -193,8 +210,71 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
break; break;
case 0xA: case 0xA:
/* GC/Wii DSP ADPCM */ /* 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;i<channel_count;i++) {
STREAMFILE * intfile =
open_scdint_with_STREAMFILE(file, "ARBITRARY.DSP", start_offset+interleave_size*i, interleave_size, stride_size, total_size);
data->substreams[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: default:
goto fail; goto fail;
} }
@ -202,6 +282,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
vgmstream->meta_type = meta_SQEX_SCD; vgmstream->meta_type = meta_SQEX_SCD;
/* open the file for reading */ /* open the file for reading */
if (vgmstream->layout_type != layout_scd_int)
{ {
int i; int i;
STREAMFILE * file; STREAMFILE * file;
@ -223,3 +304,124 @@ fail:
if (vgmstream) close_vgmstream(vgmstream); if (vgmstream) close_vgmstream(vgmstream);
return NULL; 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;
}

View File

@ -481,6 +481,17 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
nwa_codec_data *data = vgmstream->codec_data; nwa_codec_data *data = vgmstream->codec_data;
reset_nwa(data->nwa); reset_nwa(data->nwa);
} }
if (vgmstream->layout_type==layout_scd_int) {
scd_int_codec_data *data = vgmstream->codec_data;
int i;
for (i=0;i<data->substream_count;i++)
{
reset_vgmstream(data->substreams[i]);
}
}
} }
/* simply allocate memory for the VGMSTREAM and its channels */ /* simply allocate memory for the VGMSTREAM and its channels */
@ -689,6 +700,29 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
vgmstream->codec_data = NULL; 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;i<data->substream_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 */ /* now that the special cases have had their chance, clean up the standard items */
for (i=0;i<vgmstream->channels;i++) { for (i=0;i<vgmstream->channels;i++) {
if (vgmstream->ch[i].streamfile) { if (vgmstream->ch[i].streamfile) {
@ -779,6 +813,9 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
case layout_aax: case layout_aax:
render_vgmstream_aax(buffer,sample_count,vgmstream); render_vgmstream_aax(buffer,sample_count,vgmstream);
break; 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: case layout_tra_blocked:
snprintf(temp,TEMPSIZE,"TRA blocked"); snprintf(temp,TEMPSIZE,"TRA blocked");
break; break;
case layout_scd_int:
snprintf(temp,TEMPSIZE,"SCD multistream interleave");
break;
default: default:
snprintf(temp,TEMPSIZE,"INCONCEIVABLE"); snprintf(temp,TEMPSIZE,"INCONCEIVABLE");
} }

View File

@ -182,6 +182,7 @@ typedef enum {
layout_tra_blocked, /* DefJam Rapstar .tra blocks */ layout_tra_blocked, /* DefJam Rapstar .tra blocks */
layout_ps2_iab_blocked, layout_ps2_iab_blocked,
layout_ps2_strlr_blocked, layout_ps2_strlr_blocked,
layout_scd_int, /* deinterleave done by the SCDINTSTREAMFILE */
} layout_t; } 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. */ /* 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; NWAData *nwa;
} nwa_codec_data; } 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 */ /* do format detection, return pointer to a usable VGMSTREAM, or NULL on failure */
VGMSTREAM * init_vgmstream(const char * const filename); VGMSTREAM * init_vgmstream(const char * const filename);