add standard dsp and rs03 (looping still off), and dual file stereo

git-svn-id: https://vgmstream.svn.sourceforge.net/svnroot/vgmstream@57 51a99a44-fe44-0410-b1ba-c3e57ba2b86b
This commit is contained in:
halleyscometsw 2008-03-25 07:30:04 +00:00
parent 4bad78dc4d
commit 7bec522340
7 changed files with 406 additions and 5 deletions

View File

@ -20,7 +20,9 @@ META_OBJS=meta/adx_header.o \
meta/halpst.o \
meta/nds_strm.o \
meta/ngc_adpdtk.o \
meta/rsf.o
meta/rsf.o \
meta/rs03.o \
meta/ngc_dsp_std.o
OBJECTS=vgmstream.o streamfile.o util.o $(CODING_OBJS) $(LAYOUT_OBJS) $(META_OBJS)

97
src/meta/ngc_dsp_std.c Normal file
View File

@ -0,0 +1,97 @@
#include "ngc_dsp_std.h"
#include "../coding/ngc_dsp_decoder.h"
#include "../util.h"
/* The standard .dsp */
VGMSTREAM * init_vgmstream_ngc_dsp_std(const char * const filename) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * infile = NULL;
int loop_flag;
off_t start_offset;
int i;
/* check extension, case insensitive */
if (strcasecmp("dsp",filename_extension(filename))) goto fail;
/* try to open the file for header reading */
infile = open_streamfile(filename);
if (!infile) goto fail;
start_offset = 0x60;
/* check initial predictor/scale */
if (read_16bitBE(0x3e,infile) != (uint8_t)read_8bit(start_offset,infile))
goto fail;
/* check type==0 and gain==0 */
if (read_16bitBE(0x0e,infile) || read_16bitBE(0x3c,infile))
goto fail;
loop_flag = read_16bitBE(0xc,infile);
if (loop_flag) {
off_t loop_off;
/* check loop predictor/scale */
loop_off = read_32bitBE(0x10,infile)/16*8;
if (read_16bitBE(0x44,infile) != (uint8_t)read_8bit(start_offset+loop_off,infile))
goto fail;
}
/* compare num_samples with nibble count */
fprintf(stderr,"num samples (literal): %d\n",read_32bitBE(0,infile));
fprintf(stderr,"num samples (nibbles): %d\n",dsp_nibbles_to_samples(read_32bitBE(4,infile)));
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(1,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->num_samples = read_32bitBE(0,infile);
vgmstream->sample_rate = read_32bitBE(8,infile);
/*
vgmstream->loop_start_sample =
read_32bitBE(0x10,infile)/16*14;
vgmstream->loop_end_sample =
read_32bitBE(0x14,infile)/16*14;
*/
vgmstream->loop_start_sample = dsp_nibbles_to_samples(
read_32bitBE(0x10,infile));
vgmstream->loop_end_sample = dsp_nibbles_to_samples(
read_32bitBE(0x14,infile))+1;
/* don't know why, but it does happen*/
if (vgmstream->loop_end_sample > vgmstream->num_samples)
vgmstream->loop_end_sample = vgmstream->num_samples;
start_offset = 0x60;
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_DSP_STD;
for (i=0;i<16;i++)
vgmstream->ch[0].adpcm_coef[i]=read_16bitBE(0x1c+i*2,infile);
close_streamfile(infile); infile=NULL;
/* open the file for reading */
{
vgmstream->ch[0].streamfile = open_streamfile(filename);
if (!vgmstream->ch[0].streamfile) goto fail;
vgmstream->ch[0].channel_start_offset=
vgmstream->ch[0].offset=start_offset;
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (infile) close_streamfile(infile);
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

8
src/meta/ngc_dsp_std.h Normal file
View File

@ -0,0 +1,8 @@
#include "../vgmstream.h"
#ifndef _NGC_DSP_STD_H
#define _NGC_DSP_STD_H
VGMSTREAM * init_vgmstream_ngc_dsp_std(const char * const filename);
#endif

87
src/meta/rs03.c Normal file
View File

@ -0,0 +1,87 @@
#include "rs03.h"
#include "../coding/ngc_dsp_decoder.h"
#include "../util.h"
/* .dsp w/ RS03 header - from Metroid Prime 2 */
VGMSTREAM * init_vgmstream_rs03(const char * const filename) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * infile = NULL;
int channel_count;
int loop_flag;
off_t start_offset;
int i;
/* check extension, case insensitive */
if (strcasecmp("dsp",filename_extension(filename))) goto fail;
/* try to open the file for header reading */
infile = open_streamfile(filename);
if (!infile) goto fail;
/* check header */
if ((uint32_t)read_32bitBE(0,infile)!=0x52530003)
goto fail;
channel_count = read_32bitBE(4,infile);
if (channel_count != 2) goto fail;
/* build the VGMSTREAM */
loop_flag = read_16bitBE(0x14,infile);
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->num_samples = read_32bitBE(8,infile);
vgmstream->sample_rate = read_32bitBE(0xc,infile);
vgmstream->loop_start_sample = dsp_nibbles_to_samples(
read_32bitBE(0x18,infile));
vgmstream->loop_end_sample = + dsp_nibbles_to_samples(
read_32bitBE(0x1c,infile)*2+16);
start_offset = 0x60;
vgmstream->coding_type = coding_NGC_DSP;
if (channel_count == 2) {
vgmstream->layout_type = layout_interleave_shortblock;
vgmstream->interleave_block_size = 0x8f00;
vgmstream->interleave_smallblock_size = (((get_streamfile_size(infile)-start_offset)%(0x8f00*2))/2+7)/8*8;
} else
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_DSP_RS03;
for (i=0;i<16;i++)
vgmstream->ch[0].adpcm_coef[i]=read_16bitBE(0x20+i*2,infile);
if (channel_count==2) {
for (i=0;i<16;i++)
vgmstream->ch[1].adpcm_coef[i]=read_16bitBE(0x40+i*2,infile);
}
close_streamfile(infile); infile=NULL;
/* open the file for reading by each channel */
{
int i;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = open_streamfile_buffer(filename,0x8f00);
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=
start_offset+0x8f00*i;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (infile) close_streamfile(infile);
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

8
src/meta/rs03.h Normal file
View File

@ -0,0 +1,8 @@
#include "../vgmstream.h"
#ifndef _RS03_H
#define _RS03_H
VGMSTREAM * init_vgmstream_rs03(const char * const filename);
#endif

View File

@ -1,4 +1,6 @@
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "vgmstream.h"
#include "meta/adx_header.h"
#include "meta/brstm.h"
@ -9,6 +11,8 @@
#include "meta/afc_header.h"
#include "meta/ast.h"
#include "meta/halpst.h"
#include "meta/rs03.h"
#include "meta/ngc_dsp_std.h"
#include "layout/interleave.h"
#include "layout/nolayout.h"
#include "layout/blocked.h"
@ -24,7 +28,7 @@
* List of functions that will recognize files. These should correspond pretty
* directly to the metadata types
*/
#define INIT_VGMSTREAM_FCNS 9
#define INIT_VGMSTREAM_FCNS 11
VGMSTREAM * (*init_vgmstream_fcns[INIT_VGMSTREAM_FCNS])(const char * const) = {
init_vgmstream_adx,
init_vgmstream_brstm,
@ -35,22 +39,42 @@ VGMSTREAM * (*init_vgmstream_fcns[INIT_VGMSTREAM_FCNS])(const char * const) = {
init_vgmstream_afc,
init_vgmstream_ast,
init_vgmstream_halpst,
init_vgmstream_rs03,
init_vgmstream_ngc_dsp_std,
};
/* format detection and VGMSTREAM setup */
/* format detection and VGMSTREAM setup, uses default parameters */
VGMSTREAM * init_vgmstream(const char * const filename) {
return init_vgmstream_internal(filename,
1 /* do dual file detection */
);
}
/* internal version with all parameters */
VGMSTREAM * init_vgmstream_internal(const char * const filename, int do_dfs) {
int i;
/* try a series of formats, see which works */
for (i=0;i<INIT_VGMSTREAM_FCNS;i++) {
VGMSTREAM * vgmstream = (init_vgmstream_fcns[i])(filename);
if (vgmstream) {
/* everything should have a reasonable sample rate */
/* these are little hacky checks */
/* everything should have a reasonable sample rate
* (a verification of the metadata) */
if (!check_sample_rate(vgmstream->sample_rate)) {
close_vgmstream(vgmstream);
continue;
}
/* dual file stereo */
if (do_dfs && vgmstream->meta_type == meta_DSP_STD && vgmstream->channels == 1) {
try_dual_file_stereo(vgmstream, filename);
}
/* save start things so we can restart for seeking */
/* TODO: we may need to save other things here */
memcpy(vgmstream->start_ch,vgmstream->ch,sizeof(VGMSTREAMCHANNEL)*vgmstream->channels);
vgmstream->start_block_offset = vgmstream->current_block_offset;
return vgmstream;
@ -318,6 +342,12 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
vgmstream->loop_ch[i].adpcm_history2_32 = vgmstream->ch[i].adpcm_history2_32;
}
*/
int i;
for (i=0;i<vgmstream->channels;i++) {
fprintf(stderr,"ch%d hist: %04x %04x loop hist: %04x %04x\n",i,
vgmstream->ch[i].adpcm_history1_16,vgmstream->ch[i].adpcm_history2_16,
vgmstream->loop_ch[i].adpcm_history1_16,vgmstream->loop_ch[i].adpcm_history2_16);
}
/* restore! */
memcpy(vgmstream->ch,vgmstream->loop_ch,sizeof(VGMSTREAMCHANNEL)*vgmstream->channels);
vgmstream->current_sample=vgmstream->loop_sample;
@ -489,8 +519,165 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
case meta_HALPST:
snprintf(temp,TEMPSIZE,"HALPST header");
break;
case meta_DSP_RS03:
snprintf(temp,TEMPSIZE,"Retro Studios RS03 header");
break;
case meta_DSP_STD:
snprintf(temp,TEMPSIZE,"Standard NGC DSP header");
break;
default:
snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET");
}
concatn(length,desc,temp);
}
/* */
#define DFS_PAIR_COUNT 4
const char * const dfs_pairs[DFS_PAIR_COUNT][2] = {
{"L","R"},
{"l","r"},
{"_0","_1"},
{"left","right"},
};
void try_dual_file_stereo(VGMSTREAM * opened_stream, const char * const filename) {
char * filename2;
char * ext;
int dfs_name= -1; /*-1=no stereo, 0=opened_stream is left, 1=opened_stream is right */
VGMSTREAM * new_stream = NULL;
int i,j;
if (opened_stream->channels != 1) return;
/* we need at least a base and a name ending to replace */
if (strlen(filename)<2) return;
/* one extra for terminator, one for possible extra character (left>=right) */
filename2 = malloc(strlen(filename)+2);
if (!filename2) return;
strcpy(filename2,filename);
/* look relative to the extension; */
ext = (char *)filename_extension(filename2);
/* we treat the . as part of the extension */
if (ext-filename2 >= 1 && ext[-1]=='.') ext--;
for (i=0; dfs_name==-1 && i<DFS_PAIR_COUNT; i++) {
for (j=0; dfs_name==-1 && j<2; j++) {
/* find a postfix on the name */
if (!memcmp(ext-strlen(dfs_pairs[i][j]),
dfs_pairs[i][j],
strlen(dfs_pairs[i][j]))) {
int other_name=j^1;
int moveby;
dfs_name=j;
/* move the extension */
moveby = strlen(dfs_pairs[i][other_name]) -
strlen(dfs_pairs[i][dfs_name]);
memmove(ext+moveby,ext,strlen(ext)+1); /* terminator, too */
/* make the new name */
memcpy(ext+moveby-strlen(dfs_pairs[i][other_name]),dfs_pairs[i][other_name],strlen(dfs_pairs[i][other_name]));
}
}
}
/* did we find a name for the other file? */
if (dfs_name==-1) goto fail;
#if 0
printf("input is: %s\n"
"other file would be: %s\n",
filename,filename2);
#endif
new_stream = init_vgmstream_internal(filename2,
0 /* don't do dual file on this, to prevent recursion */
);
/* see if we were able to open the file, and if everything matched nicely */
if (new_stream &&
new_stream->channels == 1 &&
/* we have seen legitimate pairs where these are off by one... */
/* but leaving it commented out until I can find those and recheck */
/* abs(new_stream->num_samples-opened_stream->num_samples <= 1) && */
new_stream->num_samples == opened_stream->num_samples &&
new_stream->sample_rate == opened_stream->sample_rate &&
new_stream->meta_type == opened_stream->meta_type &&
new_stream->coding_type == opened_stream->coding_type &&
new_stream->layout_type == opened_stream->layout_type &&
new_stream->loop_flag == opened_stream->loop_flag &&
/* check these even if there is no loop, because they should then
* be zero in both */
new_stream->loop_start_sample == opened_stream->loop_start_sample &&
new_stream->loop_end_sample == opened_stream->loop_end_sample &&
/* check even if the layout doesn't use them, because it is
* difficult to determine when it does, and they should be zero
* otherwise, anyway */
new_stream->interleave_block_size == opened_stream->interleave_block_size &&
new_stream->interleave_smallblock_size == opened_stream->interleave_smallblock_size &&
new_stream->start_block_offset == opened_stream->start_block_offset) {
/* We seem to have a usable, matching file. Merge in the second channel. */
VGMSTREAMCHANNEL * new_chans;
VGMSTREAMCHANNEL * new_loop_chans = NULL;
VGMSTREAMCHANNEL * new_start_chans = NULL;
/* build the channels */
new_chans = calloc(2,sizeof(VGMSTREAMCHANNEL));
if (!new_chans) goto fail;
memcpy(&new_chans[dfs_name],&opened_stream->ch[0],sizeof(VGMSTREAMCHANNEL));
memcpy(&new_chans[dfs_name^1],&new_stream->ch[0],sizeof(VGMSTREAMCHANNEL));
/* loop and start will be initialized later, we just need to
* allocate them here */
new_start_chans = calloc(2,sizeof(VGMSTREAMCHANNEL));
if (!new_start_chans) {
free(new_chans);
goto fail;
}
if (opened_stream->loop_ch) {
new_loop_chans = calloc(2,sizeof(VGMSTREAMCHANNEL));
if (!new_loop_chans) {
free(new_chans);
free(new_start_chans);
goto fail;
}
}
/* remove the existing structures */
/* not using close_vgmstream as that would close the file */
free(opened_stream->ch);
free(new_stream->ch);
free(opened_stream->start_ch);
free(new_stream->start_ch);
if (opened_stream->loop_ch) {
free(opened_stream->loop_ch);
free(new_stream->loop_ch);
}
/* fill in the new structures */
opened_stream->ch = new_chans;
opened_stream->start_ch = new_start_chans;
opened_stream->loop_ch = new_loop_chans;
/* stereo! */
opened_stream->channels = 2;
/* discard the second VGMSTREAM */
free(new_stream);
}
if (filename2) free(filename2);
return;
fail:
if (filename2) free(filename2);
}

View File

@ -123,7 +123,7 @@ typedef struct {
* isn't closed twice, but also that everything is deallocated. Generally
* a channel should only have one STREAMFILE in its lifetime.
*/
VGMSTREAMCHANNEL * start_ch; /* coptes of channel status as they were at the beginning of the stream */
VGMSTREAMCHANNEL * start_ch; /* copies of channel status as they were at the beginning of the stream */
VGMSTREAMCHANNEL * loop_ch; /* copies of channel status as they were at the loop point */
/* layout-specific */
@ -152,6 +152,10 @@ typedef struct {
/* do format detection, return pointer to a usable VGMSTREAM, or NULL on failure */
VGMSTREAM * init_vgmstream(const char * const filename);
/* internal vgmstream that takes parameters the library user shouldn't have to know
* about */
VGMSTREAM * init_vgmstream_internal(const char * const filename, int do_dfs);
/* allocate a VGMSTREAM and channel stuff */
VGMSTREAM * allocate_vgmstream(int channel_count, int looped);
@ -183,7 +187,15 @@ int vgmstream_samples_to_do(int samples_this_block, int samples_per_frame, VGMST
* Returns 1 if loop was done. */
int vgmstream_do_loop(VGMSTREAM * vgmstream);
/* Write a description of the stream into array pointed by desc,
* which must be length bytes long. Will always be null-terminated if length > 0
*/
void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length);
/* See if there is a second file which may be the second channel, given
* already opened mono opened_stream which was opened from filename.
* If a suitable file is found, open it and change opened_stream to a
* stereo stream. */
void try_dual_file_stereo(VGMSTREAM * opened_stream, const char * const filename);
#endif