mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-28 08:20:54 +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/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 \
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
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"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\layout\scd_int_layout.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\layout\str_snds_blocked.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;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:
|
||||
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;
|
||||
}
|
||||
|
@ -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;i<data->substream_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;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 */
|
||||
for (i=0;i<vgmstream->channels;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");
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user