mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-01 01:27:20 +01:00
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:
parent
c6d3e4d343
commit
a4bb71f17d
@ -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 \
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
38
src/layout/scd_int_layout.c
Normal file
38
src/layout/scd_int_layout.c
Normal 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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"
|
||||||
>
|
>
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user