mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-18 15:54:05 +01:00
add Cstr (lots of checking), add company names to metadata formats, some dsp looping debugging stuff (with DEBUG defined)
git-svn-id: https://vgmstream.svn.sourceforge.net/svnroot/vgmstream@62 51a99a44-fe44-0410-b1ba-c3e57ba2b86b
This commit is contained in:
parent
67f6550ea3
commit
2d04ecf01a
@ -22,12 +22,13 @@ META_OBJS=meta/adx_header.o \
|
||||
meta/ngc_adpdtk.o \
|
||||
meta/rsf.o \
|
||||
meta/rs03.o \
|
||||
meta/ngc_dsp_std.o
|
||||
meta/ngc_dsp_std.o \
|
||||
meta/Cstr.o
|
||||
|
||||
OBJECTS=vgmstream.o streamfile.o util.o $(CODING_OBJS) $(LAYOUT_OBJS) $(META_OBJS)
|
||||
|
||||
libvgmstream.a: $(OBJECTS)
|
||||
$(AR) crsv libvgmstream.a $(OBJECTS)
|
||||
$(AR) crs libvgmstream.a $(OBJECTS)
|
||||
|
||||
vgmstream-deps:
|
||||
$(CC) $(CFLAGS) -M -o vgmstream-deps
|
||||
|
@ -20,6 +20,11 @@ void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
int sample_byte = read_8bit(framesin*8+stream->offset+1+i/2,stream->streamfile);
|
||||
|
||||
#if DEBUG
|
||||
if (hist1==stream->loop_history1 && hist2==stream->loop_history2) fprintf(stderr,"yo! %#x (start %#x) %d\n",stream->offset+framesin*8+i/2,stream->channel_start_offset,stream->samples_done);
|
||||
stream->samples_done++;
|
||||
#endif
|
||||
|
||||
outbuf[sample_count] = clamp16((
|
||||
(((i&1?
|
||||
get_low_nibble_signed(sample_byte):
|
||||
|
228
src/meta/Cstr.c
Normal file
228
src/meta/Cstr.c
Normal file
@ -0,0 +1,228 @@
|
||||
#include "Cstr.h"
|
||||
#include "../coding/ngc_dsp_decoder.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* .dsp w/ Cstr header, seen in Star Fox Assault and Donkey Konga */
|
||||
|
||||
VGMSTREAM * init_vgmstream_Cstr(const char * const filename) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE * infile = NULL;
|
||||
|
||||
int loop_flag;
|
||||
off_t start_offset;
|
||||
off_t first_data;
|
||||
off_t loop_offset;
|
||||
size_t interleave;
|
||||
int loop_adjust;
|
||||
|
||||
/* 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)!=0x43737472) /* "Cstr" */
|
||||
goto fail;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"header ok\n");
|
||||
#endif
|
||||
|
||||
interleave = read_16bitBE(0x06,infile);
|
||||
start_offset = 0xe0;
|
||||
first_data = start_offset+read_32bitBE(0x0c,infile);
|
||||
loop_flag = read_16bitBE(0x2c,infile);
|
||||
|
||||
if (!loop_flag) {
|
||||
/* Nonlooped tracks seem to follow no discernable pattern
|
||||
* with where they actually start.
|
||||
* But! with the magic of initial p/s redundancy, we can guess.
|
||||
*/
|
||||
while (first_data<start_offset+0x800 &&
|
||||
(read_16bitBE(0x5e,infile) != (uint8_t)read_8bit(first_data,infile) ||
|
||||
read_16bitBE(0xbe,infile) != (uint8_t)read_8bit(first_data+interleave,infile)))
|
||||
first_data+=8;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"guessed first_data at %#x\n",first_data);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* check initial predictor/scale */
|
||||
if (read_16bitBE(0x5e,infile) != (uint8_t)read_8bit(first_data,infile))
|
||||
goto fail;
|
||||
if (read_16bitBE(0xbe,infile) != (uint8_t)read_8bit(first_data+interleave,infile))
|
||||
goto fail;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"p/s ok\n");
|
||||
#endif
|
||||
|
||||
/* check type==0 and gain==0 */
|
||||
if (read_16bitBE(0x2e,infile) || read_16bitBE(0x5c,infile))
|
||||
goto fail;
|
||||
if (read_16bitBE(0x8e,infile) || read_16bitBE(0xbc,infile))
|
||||
goto fail;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"type & gain ok\n");
|
||||
#endif
|
||||
|
||||
/* check for loop flag agreement */
|
||||
if (read_16bitBE(0x2c,infile) != read_16bitBE(0x8c,infile))
|
||||
goto fail;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop flags agree\n");
|
||||
#endif
|
||||
|
||||
loop_offset = start_offset+read_32bitBE(0x10,infile)*2;
|
||||
if (loop_flag) {
|
||||
int loops_ok=0;
|
||||
/* check loop predictor/scale */
|
||||
/* some fuzz allowed */
|
||||
for (loop_adjust=0;loop_adjust>=-0x10;loop_adjust-=8) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"looking for loop p/s at %#x,%#x\n",loop_offset-interleave+loop_adjust,loop_offset+loop_adjust);
|
||||
#endif
|
||||
if (read_16bitBE(0x64,infile) == (uint8_t)read_8bit(loop_offset-interleave+loop_adjust,infile) &&
|
||||
read_16bitBE(0xc4,infile) == (uint8_t)read_8bit(loop_offset+loop_adjust,infile)) {
|
||||
loops_ok=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!loops_ok)
|
||||
for (loop_adjust=interleave;loop_adjust<=interleave+0x10;loop_adjust+=8) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"looking for loop p/s at %#x,%#x\n",loop_offset-interleave+loop_adjust,loop_offset+loop_adjust);
|
||||
#endif
|
||||
if (read_16bitBE(0x64,infile) == (uint8_t)read_8bit(loop_offset-interleave+loop_adjust,infile) &&
|
||||
read_16bitBE(0xc4,infile) == (uint8_t)read_8bit(loop_offset+loop_adjust,infile)) {
|
||||
loops_ok=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!loops_ok) goto fail;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop p/s ok (with %#4x adjust)\n",loop_adjust);
|
||||
#endif
|
||||
|
||||
|
||||
/* check for agreement */
|
||||
/* loop end (channel 1 & 2 headers) */
|
||||
if (read_32bitBE(0x34,infile) != read_32bitBE(0x94,infile))
|
||||
goto fail;
|
||||
/* loop start (Cstr header and channel 1 header) */
|
||||
if (read_32bitBE(0x30,infile) != read_32bitBE(0x10,infile)
|
||||
#if 0
|
||||
/* this particular glitch only true for SFA, though it
|
||||
* seems like something similar happens in Donkey Konga */
|
||||
/* loop start (Cstr, channel 1 & 2 headers) */
|
||||
|| (read_32bitBE(0x0c,infile)+read_32bitLE(0x30,infile)) != read_32bitBE(0x90,infile)
|
||||
#endif
|
||||
)
|
||||
/* alternatively (Donkey Konga) the header loop is 0x0c+0x10 */
|
||||
if (
|
||||
/* loop start (Cstr header and channel 1 header) */
|
||||
read_32bitBE(0x30,infile) != read_32bitBE(0x10,infile)+read_32bitBE(0x0c,infile))
|
||||
/* further alternatively (Donkey Konga), if we loop back to
|
||||
* the very first frame 0x30 might be 0x00000002 (which
|
||||
* is a *valid* std dsp loop start, imagine that) while 0x10
|
||||
* is 0x00000000 */
|
||||
if (read_32bitBE(0x30,infile) != 2 || read_32bitBE(0x10,infile) != 0)
|
||||
goto fail;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop points agree\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* assure that sample counts, sample rates agree */
|
||||
if (
|
||||
/* sample count (channel 1 & 2 headers) */
|
||||
read_32bitBE(0x20,infile) != read_32bitBE(0x80,infile) ||
|
||||
/* sample rate (channel 1 & 2 headers) */
|
||||
read_32bitBE(0x28,infile) != read_32bitBE(0x88,infile) ||
|
||||
/* sample count (Cstr header and channel 1 header) */
|
||||
read_32bitLE(0x14,infile) != read_32bitBE(0x20,infile) ||
|
||||
/* sample rate (Cstr header and channel 1 header) */
|
||||
(uint16_t)read_16bitLE(0x18,infile) != read_32bitBE(0x28,infile))
|
||||
goto fail;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
|
||||
vgmstream = allocate_vgmstream(2,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->sample_rate = read_32bitBE(0x28,infile);
|
||||
/* This is a slight hack to counteract their hack.
|
||||
* All the data is ofset by first_data so that the loop
|
||||
* point occurs at a block boundary. However, I always begin decoding
|
||||
* right after the header, as that is the start of the first block and
|
||||
* my interleave code relies on starting at the beginning of a block.
|
||||
* So we decode a few silent samples at the beginning, and here we make up
|
||||
* for it by lengthening the track by that much.
|
||||
*/
|
||||
vgmstream->num_samples = read_32bitBE(0x20,infile) +
|
||||
(first_data-start_offset)/8*14;
|
||||
|
||||
if (loop_flag) {
|
||||
off_t loop_start_bytes = loop_offset-start_offset-interleave;
|
||||
vgmstream->loop_start_sample = dsp_nibbles_to_samples((loop_start_bytes/(2*interleave)*interleave+loop_start_bytes%(interleave*2))*2);
|
||||
/*dsp_nibbles_to_samples(loop_start_bytes);*/
|
||||
/*dsp_nibbles_to_samples(read_32bitBE(0x30,infile)*2-inter);*/
|
||||
vgmstream->loop_end_sample = dsp_nibbles_to_samples(
|
||||
read_32bitBE(0x34,infile))+1;
|
||||
if (vgmstream->loop_end_sample > vgmstream->num_samples) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop_end_sample > num_samples, adjusting\n");
|
||||
#endif
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
}
|
||||
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
vgmstream->meta_type = meta_DSP_CSTR;
|
||||
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<16;i++)
|
||||
vgmstream->ch[0].adpcm_coef[i]=read_16bitBE(0x3c+i*2,infile);
|
||||
for (i=0;i<16;i++)
|
||||
vgmstream->ch[1].adpcm_coef[i]=read_16bitBE(0x9c+i*2,infile);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
vgmstream->ch[0].loop_history1 = read_16bitBE(0x66,infile);
|
||||
vgmstream->ch[0].loop_history2 = read_16bitBE(0x68,infile);
|
||||
vgmstream->ch[1].loop_history1 = read_16bitBE(0xc6,infile);
|
||||
vgmstream->ch[1].loop_history2 = read_16bitBE(0xc8,infile);
|
||||
#endif
|
||||
|
||||
close_streamfile(infile); infile=NULL;
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<2;i++) {
|
||||
vgmstream->ch[i].streamfile = open_streamfile_buffer(filename,interleave);
|
||||
|
||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=
|
||||
start_offset+interleave*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/Cstr.h
Normal file
8
src/meta/Cstr.h
Normal file
@ -0,0 +1,8 @@
|
||||
#include "../vgmstream.h"
|
||||
|
||||
#ifndef _CSTR_H
|
||||
#define _CSTR_H
|
||||
|
||||
VGMSTREAM * init_vgmstream_Cstr(const char * const filename);
|
||||
|
||||
#endif
|
@ -13,6 +13,7 @@
|
||||
#include "meta/halpst.h"
|
||||
#include "meta/rs03.h"
|
||||
#include "meta/ngc_dsp_std.h"
|
||||
#include "meta/Cstr.h"
|
||||
#include "layout/interleave.h"
|
||||
#include "layout/nolayout.h"
|
||||
#include "layout/blocked.h"
|
||||
@ -28,7 +29,7 @@
|
||||
* List of functions that will recognize files. These should correspond pretty
|
||||
* directly to the metadata types
|
||||
*/
|
||||
#define INIT_VGMSTREAM_FCNS 11
|
||||
#define INIT_VGMSTREAM_FCNS 12
|
||||
VGMSTREAM * (*init_vgmstream_fcns[INIT_VGMSTREAM_FCNS])(const char * const) = {
|
||||
init_vgmstream_adx,
|
||||
init_vgmstream_brstm,
|
||||
@ -41,6 +42,7 @@ VGMSTREAM * (*init_vgmstream_fcns[INIT_VGMSTREAM_FCNS])(const char * const) = {
|
||||
init_vgmstream_halpst,
|
||||
init_vgmstream_rs03,
|
||||
init_vgmstream_ngc_dsp_std,
|
||||
init_vgmstream_Cstr,
|
||||
};
|
||||
|
||||
|
||||
@ -335,26 +337,30 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
|
||||
/* if (vgmstream->loop_flag) {*/
|
||||
/* is this the loop end? */
|
||||
if (vgmstream->current_sample==vgmstream->loop_end_sample) {
|
||||
/* RS03 and the Metroid Prime standard DSP files are
|
||||
* apparently built with the assumption that the history
|
||||
* is preserved through looping
|
||||
*/
|
||||
/* against everything I hold sacred, preserve adpcm
|
||||
* history through loop for certain types */
|
||||
if (vgmstream->meta_type == meta_DSP_STD ||
|
||||
vgmstream->meta_type == meta_DSP_RS03) {
|
||||
vgmstream->meta_type == meta_DSP_RS03 ||
|
||||
vgmstream->meta_type == meta_DSP_CSTR) {
|
||||
int i;
|
||||
for (i=0;i<vgmstream->channels;i++) {
|
||||
vgmstream->loop_ch[i].adpcm_history1_16 = vgmstream->ch[i].adpcm_history1_16;
|
||||
vgmstream->loop_ch[i].adpcm_history2_16 = vgmstream->ch[i].adpcm_history2_16;
|
||||
}
|
||||
}
|
||||
/*
|
||||
#if DEBUG
|
||||
{
|
||||
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);
|
||||
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);
|
||||
fprintf(stderr,"ch%d offset: %x loop offset: %x\n",i,
|
||||
vgmstream->ch[i].offset,
|
||||
vgmstream->loop_ch[i].offset);
|
||||
}
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
/* restore! */
|
||||
memcpy(vgmstream->ch,vgmstream->loop_ch,sizeof(VGMSTREAMCHANNEL)*vgmstream->channels);
|
||||
vgmstream->current_sample=vgmstream->loop_sample;
|
||||
@ -464,7 +470,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
||||
snprintf(temp,TEMPSIZE,"interleave with short last block");
|
||||
break;
|
||||
case layout_dtk_interleave:
|
||||
snprintf(temp,TEMPSIZE,"DTK nibble interleave");
|
||||
snprintf(temp,TEMPSIZE,"ADP/DTK nibble interleave");
|
||||
break;
|
||||
case layout_ast_blocked:
|
||||
snprintf(temp,TEMPSIZE,"AST blocked");
|
||||
@ -497,40 +503,43 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
||||
|
||||
switch (vgmstream->meta_type) {
|
||||
case meta_RSTM:
|
||||
snprintf(temp,TEMPSIZE,"RSTM header");
|
||||
snprintf(temp,TEMPSIZE,"Nintendo RSTM header");
|
||||
break;
|
||||
case meta_STRM:
|
||||
snprintf(temp,TEMPSIZE,"NDS STRM header");
|
||||
snprintf(temp,TEMPSIZE,"Nintendo STRM header");
|
||||
break;
|
||||
case meta_ADX_03:
|
||||
snprintf(temp,TEMPSIZE,"ADX header type 03");
|
||||
snprintf(temp,TEMPSIZE,"CRI ADX header type 03");
|
||||
break;
|
||||
case meta_ADX_04:
|
||||
snprintf(temp,TEMPSIZE,"ADX header type 04");
|
||||
snprintf(temp,TEMPSIZE,"CRI ADX header type 04");
|
||||
break;
|
||||
case meta_DSP_AGSC:
|
||||
snprintf(temp,TEMPSIZE,"AGSC header");
|
||||
snprintf(temp,TEMPSIZE,"Retro Studios AGSC header");
|
||||
break;
|
||||
case meta_NGC_ADPDTK:
|
||||
snprintf(temp,TEMPSIZE,"assumed NGC DTK by .adp extension and valid first frame");
|
||||
snprintf(temp,TEMPSIZE,"assumed Nintendo ADP by .adp extension and valid first frame");
|
||||
break;
|
||||
case meta_RSF:
|
||||
snprintf(temp,TEMPSIZE,"assumed Retro Studios RSF by .rsf extension");
|
||||
snprintf(temp,TEMPSIZE,"assumed Retro Studios RSF by .rsf extension and valid first bytes");
|
||||
break;
|
||||
case meta_AFC:
|
||||
snprintf(temp,TEMPSIZE,"AFC header");
|
||||
snprintf(temp,TEMPSIZE,"Nintendo AFC header");
|
||||
break;
|
||||
case meta_AST:
|
||||
snprintf(temp,TEMPSIZE,"AST header");
|
||||
snprintf(temp,TEMPSIZE,"Nintendo AST header");
|
||||
break;
|
||||
case meta_HALPST:
|
||||
snprintf(temp,TEMPSIZE,"HALPST header");
|
||||
snprintf(temp,TEMPSIZE,"HAL Laboratory 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");
|
||||
snprintf(temp,TEMPSIZE,"Standard Nintendo DSP header");
|
||||
break;
|
||||
case meta_DSP_CSTR:
|
||||
snprintf(temp,TEMPSIZE,"Namco Cstr header");
|
||||
break;
|
||||
default:
|
||||
snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET");
|
||||
|
@ -99,6 +99,11 @@ typedef struct {
|
||||
|
||||
struct g72x_state g72x_state; /* state for G.721 decoder, sort of big but we
|
||||
might as well keep it around */
|
||||
|
||||
#ifdef DEBUG
|
||||
int samples_done;
|
||||
int16_t loop_history1,loop_history2;
|
||||
#endif
|
||||
} VGMSTREAMCHANNEL;
|
||||
|
||||
typedef struct {
|
||||
|
Loading…
x
Reference in New Issue
Block a user