mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-18 15:54:05 +01:00
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:
parent
4bad78dc4d
commit
7bec522340
@ -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
97
src/meta/ngc_dsp_std.c
Normal 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
8
src/meta/ngc_dsp_std.h
Normal 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
87
src/meta/rs03.c
Normal 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
8
src/meta/rs03.h
Normal file
@ -0,0 +1,8 @@
|
||||
#include "../vgmstream.h"
|
||||
|
||||
#ifndef _RS03_H
|
||||
#define _RS03_H
|
||||
|
||||
VGMSTREAM * init_vgmstream_rs03(const char * const filename);
|
||||
|
||||
#endif
|
193
src/vgmstream.c
193
src/vgmstream.c
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user