diff --git a/readme.txt b/readme.txt index 304a2888..afa2f18f 100644 --- a/readme.txt +++ b/readme.txt @@ -22,10 +22,11 @@ Formats supported by this version of vgmstream ($Revision$): - .brstm (RSTM: GC/Wii DSP ADPCM, 8/16 bit PCM) - .strm (STRM: NDS IMA ADPCM, 8/16 bit PCM) - .adp (GC DTK ADPCM) -- .agsc (GC ADPCM) +- .agsc (GC DSP ADPCM) - .rsf (CCITT G.721 ADPCM) - .afc (GC AFC ADPCM) - .ast (GC/Wii AFC ADPCM, 16 bit PCM) +- .hps (GC DSP ADPCM) Enjoy! -hcs diff --git a/src/coding/ngc_dsp_decoder.c b/src/coding/ngc_dsp_decoder.c index 21575d35..42b38ae2 100644 --- a/src/coding/ngc_dsp_decoder.c +++ b/src/coding/ngc_dsp_decoder.c @@ -35,3 +35,19 @@ void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci stream->adpcm_history1_16 = hist1; stream->adpcm_history2_16 = hist2; } + +/* + * The original DSP spec uses nibble counts for loop points, and some + * variants don't have a proper sample count, so all we (who are interested + * in sample counts) need to do this conversion occasionally. + */ +int32_t dsp_nibbles_to_samples(int32_t nibbles) { + int32_t whole_frames = nibbles/16; + int32_t remainder = nibbles%16; + + if (remainder > 0 && remainder < 14) + return whole_frames*14 + remainder; + else if (remainder >= 14) + fprintf(stderr,"last frame %d leftover nibbles makes no sense\n",remainder); + return whole_frames*14; +} diff --git a/src/coding/ngc_dsp_decoder.h b/src/coding/ngc_dsp_decoder.h index ac850ca8..5ff56640 100644 --- a/src/coding/ngc_dsp_decoder.h +++ b/src/coding/ngc_dsp_decoder.h @@ -5,4 +5,6 @@ void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +int32_t dsp_nibbles_to_samples(int32_t nibbles); + #endif diff --git a/src/layout/ast_blocked.c b/src/layout/ast_blocked.c index d72e3acf..697926d4 100644 --- a/src/layout/ast_blocked.c +++ b/src/layout/ast_blocked.c @@ -16,41 +16,3 @@ void ast_block_update(off_t block_offset, VGMSTREAM * vgmstream) { 0x20 + vgmstream->current_block_size*i; } } - -void render_vgmstream_ast_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - int samples_written=0; - - int frame_size = get_vgmstream_frame_size(vgmstream); - int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); - int samples_this_block; - - samples_this_block = vgmstream->current_block_size / frame_size * samples_per_frame; - - while (samples_writtenloop_flag && vgmstream_do_loop(vgmstream)) { - samples_this_block = vgmstream->current_block_size / frame_size * samples_per_frame; - continue; - } - - samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); - - if (samples_written+samples_to_do > sample_count) - samples_to_do=sample_count-samples_written; - - decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); - - samples_written += samples_to_do; - vgmstream->current_sample += samples_to_do; - vgmstream->samples_into_block+=samples_to_do; - - if (vgmstream->samples_into_block==samples_this_block) { - ast_block_update(vgmstream->next_block_offset,vgmstream); - - samples_this_block = vgmstream->current_block_size / frame_size * samples_per_frame; - vgmstream->samples_into_block=0; - } - - } -} diff --git a/src/layout/ast_blocked.h b/src/layout/ast_blocked.h index 20da9e32..35d0f62a 100644 --- a/src/layout/ast_blocked.h +++ b/src/layout/ast_blocked.h @@ -7,8 +7,6 @@ #ifndef _AST_BLOCKED_H #define _AST_BLOCKED_H -void render_vgmstream_ast_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); - void ast_block_update(off_t block_ofset, VGMSTREAM * vgmstream); #endif diff --git a/src/layout/blocked.c b/src/layout/blocked.c new file mode 100644 index 00000000..2c5efac6 --- /dev/null +++ b/src/layout/blocked.c @@ -0,0 +1,61 @@ +#include "ast_blocked.h" +#include "halpst_blocked.h" +#include "../vgmstream.h" + +void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { + int samples_written=0; + + int frame_size = get_vgmstream_frame_size(vgmstream); + int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + int samples_this_block; + + samples_this_block = vgmstream->current_block_size / frame_size * samples_per_frame; + + while (samples_writtenloop_flag && vgmstream_do_loop(vgmstream)) { + samples_this_block = vgmstream->current_block_size / frame_size * samples_per_frame; + continue; + } + + samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); + + if (samples_written+samples_to_do > sample_count) + samples_to_do=sample_count-samples_written; + + if (vgmstream->current_block_offset>=0) + decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); + else { + int i; + /* we've run off the end! */ + for (i=samples_written*vgmstream->channels; + i<(samples_written+samples_to_do)*vgmstream->channels;i++) + buffer[i]=0; + } + + samples_written += samples_to_do; + vgmstream->current_sample += samples_to_do; + vgmstream->samples_into_block+=samples_to_do; + + if (vgmstream->samples_into_block==samples_this_block) { + switch (vgmstream->layout_type) { + case layout_ast_blocked: + ast_block_update(vgmstream->next_block_offset,vgmstream); + break; + case layout_halpst_blocked: + if (vgmstream->next_block_offset>=0) + halpst_block_update(vgmstream->next_block_offset,vgmstream); + else + vgmstream->current_block_offset=-1; + break; + default: + break; + } + + samples_this_block = vgmstream->current_block_size / frame_size * samples_per_frame; + vgmstream->samples_into_block=0; + } + + } +} diff --git a/src/layout/blocked.h b/src/layout/blocked.h new file mode 100644 index 00000000..9a8f204b --- /dev/null +++ b/src/layout/blocked.h @@ -0,0 +1,12 @@ +/* + * blocked.h - blocking + */ +#include "../streamtypes.h" +#include "../vgmstream.h" + +#ifndef _BLOCKED_H +#define _BLOCKED_H + +void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); + +#endif diff --git a/src/layout/halpst_blocked.c b/src/layout/halpst_blocked.c new file mode 100644 index 00000000..86595ee7 --- /dev/null +++ b/src/layout/halpst_blocked.c @@ -0,0 +1,19 @@ +#include "halpst_blocked.h" +#include "../vgmstream.h" + +/* set up for the block at the given offset */ +void halpst_block_update(off_t block_offset, VGMSTREAM * vgmstream) { + int i; + vgmstream->current_block_offset = block_offset; + vgmstream->current_block_size = read_32bitBE( + vgmstream->current_block_offset, + vgmstream->ch[0].streamfile)/vgmstream->channels; + vgmstream->next_block_offset = read_32bitBE( + vgmstream->current_block_offset+8, + vgmstream->ch[0].streamfile); + + for (i=0;ichannels;i++) { + vgmstream->ch[i].offset = vgmstream->current_block_offset + + 0x20 + vgmstream->current_block_size*i; + } +} diff --git a/src/layout/halpst_blocked.h b/src/layout/halpst_blocked.h new file mode 100644 index 00000000..2abab4d3 --- /dev/null +++ b/src/layout/halpst_blocked.h @@ -0,0 +1,12 @@ +/* + * halpst_blocked.h - HALPST blocking + */ +#include "../streamtypes.h" +#include "../vgmstream.h" + +#ifndef _HALPST_BLOCKED_H +#define _HALPST_BLOCKED_H + +void halpst_block_update(off_t block_ofset, VGMSTREAM * vgmstream); + +#endif diff --git a/src/meta/halpst.c b/src/meta/halpst.c new file mode 100644 index 00000000..78d5e36f --- /dev/null +++ b/src/meta/halpst.c @@ -0,0 +1,130 @@ +#include "halpst.h" +#include "../coding/ngc_dsp_decoder.h" +#include "../layout/halpst_blocked.h" +#include "../util.h" + +VGMSTREAM * init_vgmstream_halpst(const char * const filename) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * infile = NULL; + + int channel_count; + int loop_flag = 0; + + int32_t samples_l,samples_r; + int32_t start_sample = 0; + + size_t max_block; + + /* check extension, case insensitive */ + if (strcasecmp("hps",filename_extension(filename))) goto fail; + + /* try to open the file for header reading */ + infile = open_streamfile_buffer(filename,0x20); + if (!infile) goto fail; + + /* check header */ + if ((uint32_t)read_32bitBE(0,infile)!=0x2048414C || /* " HAL" */ + read_32bitBE(4,infile)!=0x50535400) /* "PST\0" */ + goto fail; + + /* details */ + channel_count = read_32bitBE(0xc,infile); + max_block = read_32bitBE(0x10,infile); + + /* have I ever seen a mono .hps? */ + if (channel_count!=2) goto fail; + + /* yay for redundancy, gives us something to test */ + samples_l = dsp_nibbles_to_samples(read_32bitBE(0x18,infile)); + samples_r = dsp_nibbles_to_samples(read_32bitBE(0x50,infile)); + + if (samples_l != samples_r) goto fail; + + /* + * looping info is implicit in the "next block" field of the final + * block, so we have to find that + */ + { + off_t offset = 0x80, last_offset = 0; + off_t loop_offset; + + /* determine if there is a loop */ + while (offset > last_offset) { + last_offset = offset; + offset = read_32bitBE(offset+8,infile); + } + if (offset < 0) loop_flag = 0; + else { + /* one more pass to determine start sample */ + int32_t start_nibble = 0; + loop_flag = 1; + + loop_offset = offset; + offset = 0x80; + while (offset != loop_offset) { + start_nibble += read_32bitBE(offset,infile); + offset = read_32bitBE(offset+8,infile); + } + + start_sample = dsp_nibbles_to_samples(start_nibble); + } + + } + + /* build the VGMSTREAM */ + + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + /* fill in the vital statistics */ + vgmstream->num_samples = samples_l; + vgmstream->sample_rate = read_32bitBE(8,infile); + /* channels and loop flag are set by allocate_vgmstream */ + if (loop_flag) { + vgmstream->loop_start_sample = start_sample; + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_halpst_blocked; + vgmstream->meta_type = meta_HALPST; + + /* load decode coefs */ + { + int i; + for (i=0;i<16;i++) + vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x20+i*2,infile); + for (i=0;i<16;i++) + vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x58+i*2,infile); + } + + close_streamfile(infile); infile=NULL; + + /* open the file for reading by each channel */ + { + int i; + for (i=0;ich[i].streamfile = open_streamfile_buffer(filename, + (i==0? + max_block+0x20: /* first buffer a bit bigger to + read block header without + inefficiency */ + max_block + ) + ); + + if (!vgmstream->ch[i].streamfile) goto fail; + } + } + + /* start me up */ + halpst_block_update(0x80,vgmstream); + + return vgmstream; + + /* clean up anything we may have opened */ +fail: + if (infile) close_streamfile(infile); + if (vgmstream) close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/halpst.h b/src/meta/halpst.h new file mode 100644 index 00000000..6ef419f2 --- /dev/null +++ b/src/meta/halpst.h @@ -0,0 +1,8 @@ +#include "../vgmstream.h" + +#ifndef _HALPST_H +#define _HALPST_H + +VGMSTREAM * init_vgmstream_halpst(const char * const filename); + +#endif diff --git a/src/vgmstream.c b/src/vgmstream.c index a16f783b..8f847320 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -7,9 +7,10 @@ #include "meta/rsf.h" #include "meta/afc_header.h" #include "meta/ast.h" +#include "meta/halpst.h" #include "layout/interleave.h" #include "layout/nolayout.h" -#include "layout/ast_blocked.h" +#include "layout/blocked.h" #include "coding/adx_decoder.h" #include "coding/ngc_dsp_decoder.h" #include "coding/pcm_decoder.h" @@ -22,7 +23,7 @@ * List of functions that will recognize files. These should correspond pretty * directly to the metadata types */ -#define INIT_VGMSTREAM_FCNS 8 +#define INIT_VGMSTREAM_FCNS 9 VGMSTREAM * (*init_vgmstream_fcns[INIT_VGMSTREAM_FCNS])(const char * const) = { init_vgmstream_adx, init_vgmstream_brstm, @@ -32,6 +33,7 @@ VGMSTREAM * (*init_vgmstream_fcns[INIT_VGMSTREAM_FCNS])(const char * const) = { init_vgmstream_rsf, init_vgmstream_afc, init_vgmstream_ast, + init_vgmstream_halpst, }; /* format detection and VGMSTREAM setup */ @@ -131,7 +133,8 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre render_vgmstream_nolayout(buffer,sample_count,vgmstream); break; case layout_ast_blocked: - render_vgmstream_ast_blocked(buffer,sample_count,vgmstream); + case layout_halpst_blocked: + render_vgmstream_blocked(buffer,sample_count,vgmstream); break; } } @@ -406,6 +409,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream) { case layout_ast_blocked: printf("AST blocked"); break; + case layout_halpst_blocked: + printf("HALPST blocked"); + break; default: printf("INCONCEIVABLE"); } @@ -447,6 +453,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream) { case meta_AST: printf("AST header"); break; + case meta_HALPST: + printf("HALPST header"); + break; default: printf("THEY SHOULD HAVE SENT A POET"); } diff --git a/src/vgmstream.h b/src/vgmstream.h index 67eb8b8b..f72ed04e 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -37,8 +37,8 @@ typedef enum { #endif /* headered blocks */ layout_ast_blocked, /* .ast STRM with BLCK blocks*/ + layout_halpst_blocked, /* blocks with HALPST-format header */ #if 0 - layout_halp_blocked, /* blocks with HALP-format header */ layout_strm_blocked, /* */ #endif /* otherwise odd */ @@ -66,7 +66,7 @@ typedef enum { meta_NGC_ADPDTK, /* NGC DTK/ADP, no header (.adp) */ meta_kRAW, /* almost headerless PCM */ meta_RSF, /* Retro Studios RSF, no header (.rsf) */ - + meta_HALPST, /* HAL Labs HALPST */ } meta_t; typedef struct { diff --git a/test/Makefile b/test/Makefile index 413db9fe..97a87440 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,7 +1,7 @@ SHELL = /bin/sh CFLAGS=-lm -Wall -O3 -VGMSTREAMFILES = ../src/streamfile.c ../src/vgmstream.c ../src/util.c ../src/meta/adx_header.c ../src/coding/adx_decoder.c ../src/coding/ngc_dsp_decoder.c ../src/meta/brstm.c ../src/layout/interleave.c ../src/layout/nolayout.c ../src/coding/pcm_decoder.c ../src/meta/nds_strm.c ../src/coding/ima_decoder.c ../src/meta/agsc.c ../src/meta/ngc_adpdtk.c ../src/coding/ngc_dtk_decoder.c ../src/coding/g721_decoder.c ../src/meta/rsf.c ../src/meta/afc_header.c ../src/coding/ngc_afc_decoder.c ../src/meta/ast.c ../src/layout/ast_blocked.c +VGMSTREAMFILES = ../src/streamfile.c ../src/vgmstream.c ../src/util.c ../src/meta/adx_header.c ../src/coding/adx_decoder.c ../src/coding/ngc_dsp_decoder.c ../src/meta/brstm.c ../src/layout/interleave.c ../src/layout/nolayout.c ../src/coding/pcm_decoder.c ../src/meta/nds_strm.c ../src/coding/ima_decoder.c ../src/meta/agsc.c ../src/meta/ngc_adpdtk.c ../src/coding/ngc_dtk_decoder.c ../src/coding/g721_decoder.c ../src/meta/rsf.c ../src/meta/afc_header.c ../src/coding/ngc_afc_decoder.c ../src/meta/ast.c ../src/layout/ast_blocked.c ../src/layout/halpst_blocked.c ../src/layout/blocked.c ../src/meta/halpst.c test: test.c $(VGMSTREAMFILES) gcc $(CFLAGS) test.c $(VGMSTREAMFILES) -o test