From 73db56f3271299baf6231ddaac278e01bd886a80 Mon Sep 17 00:00:00 2001 From: bnnm Date: Fri, 17 Jul 2020 19:15:35 +0200 Subject: [PATCH] NWA cleanup and improve performance --- src/coding/coding.h | 8 +- src/coding/nwa_decoder.c | 604 ++++++++++++++++++++++----------------- src/coding/nwa_decoder.h | 48 ++-- src/meta/nwa.c | 138 ++++----- src/vgmstream.c | 23 +- src/vgmstream.h | 6 - 6 files changed, 442 insertions(+), 385 deletions(-) diff --git a/src/coding/coding.h b/src/coding/coding.h index 2854c03a..6854ccaf 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -146,8 +146,14 @@ STREAMFILE* acm_get_streamfile(acm_codec_data* data); /* nwa_decoder */ -void decode_nwa(NWAData* nwa, sample *outbuf, int32_t samples_to_do); +typedef struct nwa_codec_data nwa_codec_data; +nwa_codec_data* init_nwa(STREAMFILE* sf); +void decode_nwa(nwa_codec_data* data, sample_t* outbuf, int32_t samples_to_do); +void seek_nwa(nwa_codec_data *data, int32_t sample); +void reset_nwa(nwa_codec_data *data); +void free_nwa(nwa_codec_data* data); +STREAMFILE* nwa_get_streamfile(nwa_codec_data* data); /* msadpcm_decoder */ void decode_msadpcm_stereo(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t first_sample, int32_t samples_to_do); diff --git a/src/coding/nwa_decoder.c b/src/coding/nwa_decoder.c index 62fb8786..64cc91ee 100644 --- a/src/coding/nwa_decoder.c +++ b/src/coding/nwa_decoder.c @@ -1,4 +1,33 @@ -/* originally from nwatowav.cc 2007.7.28 version, which read: */ +/* Originally from nwatowav.cc (2007.7.28 version) by jagarl. + * - http://www.creator.club.ne.jp/~jagarl/ + * + * Converted to .c by hcs (redone as a lib without RIFF/main handling), some cleanup by bnnm. + * + * nwa format (abridged from the original) + * NWA Header + * data offset index + * data block<0> + * data block<1> + * ... + * data block + * + * - NWA header: 0x2c with nwa info (channels, compression level, etc), no magic number + * - data offset index: pointers to data blocks + * - data block: variable sized DPCM blocks to fixed size PCM (a,b,c compresses to (a),b-a,c-b), + * DPCM codes use variable bits. Usually for 16-bit PCM ends ups using 6-8 bits. + * - Block format: + * - mono: initial PCM (8 or 16-bit) then bitstream + * - stereo: initial PCM for left + right channel then bitstream + * Differential accuracy isn't high so initial PCM is used to correct data in each block (?) + * - bitstream: Roughly each code has an 'exponent' (2 bits) + 'mantissa' (variable bits). + * Depending on compression level + type it configures final shift value and matissa bits. + * There is a run length encoding mode in some cases (Tomoyo After voice files). + * Bitstream bytes follow little endian. + * (some examples here in the original, see decoder). + * + * Original copyright: + */ + /* * Copyright 2001-2007 jagarl / Kazunori Ueno * All Rights Reserved. @@ -33,319 +62,382 @@ #include #include "nwa_decoder.h" -/* can serve up 8 bits at a time */ -static int -getbits (STREAMFILE *file, off_t *offset, int *shift, int bits) -{ - int ret; - if (*shift > 8) - { - ++*offset; - *shift -= 8; + +//NWAInfo::UseRunLength +static int is_use_runlength(NWAData* nwa) { + if (nwa->channels == 2 && nwa->bps == 16 && nwa->complevel == 2) { + return 0; /* sw2 */ } - ret = read_16bitLE(*offset,file) >> *shift; - *shift += bits; - return ret & ((1 << bits) - 1); /* mask */ + + if (nwa->complevel == 5) { + if (nwa->channels == 2) + return 0; // BGM*.nwa in Little Busters! + return 1; // Tomoyo After (.nwk koe file) + } + + return 0; } -NWAData * -open_nwa (STREAMFILE * streamFile, const char *filename) -{ +NWAData* nwalib_open(STREAMFILE* sf) { + uint8_t header[0x2c] = {0}; int i; - NWAData * const nwa = malloc(sizeof(NWAData)); + NWAData* const nwa = malloc(sizeof(NWAData)); if (!nwa) goto fail; - nwa->channels = read_16bitLE(0x00,streamFile); - nwa->bps = read_16bitLE(0x02,streamFile); - nwa->freq = read_32bitLE(0x04,streamFile); - nwa->complevel = read_32bitLE(0x08,streamFile); - nwa->blocks = read_32bitLE(0x10,streamFile); - nwa->datasize = read_32bitLE(0x14,streamFile); - nwa->compdatasize = read_32bitLE(0x18,streamFile); - nwa->samplecount = read_32bitLE(0x1c,streamFile); - nwa->blocksize = read_32bitLE(0x20,streamFile); - nwa->restsize = read_32bitLE(0x24,streamFile); + //NWAData::ReadHeader + + read_streamfile(header, 0x00, sizeof(header), sf); + nwa->channels = get_s16le(header+0x00); + nwa->bps = get_s16le(header+0x02); + nwa->freq = get_s32le(header+0x04); + nwa->complevel = get_s32le(header+0x08); + nwa->dummy = get_s32le(header+0x0c); + nwa->blocks = get_s32le(header+0x10); + nwa->datasize = get_s32le(header+0x14); + nwa->compdatasize = get_s32le(header+0x18); + nwa->samplecount = get_s32le(header+0x1c); + nwa->blocksize = get_s32le(header+0x20); + nwa->restsize = get_s32le(header+0x24); + nwa->dummy2 = get_s32le(header+0x28); + nwa->offsets = NULL; - nwa->buffer = NULL; - nwa->buffer_readpos = NULL; - nwa->file = NULL; + nwa->outdata = NULL; + nwa->outdata_readpos = NULL; + nwa->tmpdata = NULL; + nwa->filesize = get_streamfile_size(sf); - /* PCM not handled here */ - if (nwa->complevel < 0 || nwa->complevel > 5) goto fail; - if (nwa->channels != 1 && nwa->channels != 2) goto fail; + if (nwa->blocks <= 0 || nwa->blocks > 1000000) + /* 1時間を超える曲ってのはないでしょ*/ //surely there won't be songs over 1 hour + goto fail; - if (nwa->bps != 8 && nwa->bps != 16) goto fail; + // NWAData::CheckHeader: - if (nwa->blocks <= 0) goto fail; + if (nwa->channels != 1 && nwa->channels != 2) + goto fail; - if (nwa->compdatasize == 0 - || get_streamfile_size(streamFile) != nwa->compdatasize) goto fail; + if (nwa->bps != 8 && nwa->bps != 16) + goto fail; - if (nwa->datasize != nwa->samplecount * (nwa->bps/8)) goto fail; + // (PCM not handled) - if (nwa->samplecount != - (nwa->blocks-1) * nwa->blocksize + nwa->restsize) goto fail; + if (nwa->complevel < 0 || nwa->complevel > 5) + goto fail; - nwa->offsets = malloc(sizeof(off_t)*nwa->blocks); + if (nwa->filesize != nwa->compdatasize) + goto fail; + + + if (nwa->datasize != nwa->samplecount * (nwa->bps / 8)) + goto fail; + + if (nwa->samplecount != (nwa->blocks - 1) * nwa->blocksize + nwa->restsize) + goto fail; + + /* offset index 読み込み */ //read offset index + nwa->offsets = malloc(sizeof(off_t) * nwa->blocks); if (!nwa->offsets) goto fail; - for (i = 0; i < nwa->blocks; i++) - { - int32_t o = read_32bitLE(0x2c+i*4,streamFile); + for (i = 0; i < nwa->blocks; i++) { + int32_t o = read_s32le(0x2c + i*4, sf); if (o < 0) goto fail; nwa->offsets[i] = o; } - if (nwa->offsets[nwa->blocks-1] >= nwa->compdatasize) goto fail; - - if (nwa->restsize > nwa->blocksize) nwa->buffer = - malloc(sizeof(sample)*nwa->restsize); - else nwa->buffer = - malloc(sizeof(sample)*nwa->blocksize); - if (nwa->buffer == NULL) goto fail; - - nwa->buffer_readpos = nwa->buffer; - - nwa->samples_in_buffer = 0; + if (nwa->offsets[nwa->blocks-1] >= nwa->compdatasize) + goto fail; + nwa->use_runlength = is_use_runlength(nwa); nwa->curblock = 0; - /* if we got this far, it's probably safe */ - nwa->file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!nwa->file) goto fail; + + //extra + if (nwa->restsize > nwa->blocksize) { + nwa->outdata = malloc(sizeof(int16_t) * nwa->restsize); + } + else { + nwa->outdata = malloc(sizeof(int16_t) * nwa->blocksize); + } + if (!nwa->outdata) + goto fail; + + /* これ以上の大きさはないだろう、、、 */ //probably not over this size + nwa->tmpdata = malloc(sizeof(uint8_t) * nwa->blocksize * (nwa->bps / 8) * 2); + if (!nwa->tmpdata) + goto fail; + + nwa->outdata_readpos = nwa->outdata; + nwa->samples_in_buffer = 0; return nwa; fail: - if (nwa) - { - close_nwa(nwa); - } - + nwalib_close(nwa); return NULL; } -void -close_nwa(NWAData * nwa) -{ +void nwalib_close(NWAData * nwa) { if (!nwa) return; - if (nwa->offsets) - free (nwa->offsets); - nwa->offsets = NULL; - if (nwa->buffer) - free (nwa->buffer); - nwa->buffer = NULL; - if (nwa->file) - close_streamfile (nwa->file); - nwa->file = NULL; + free(nwa->offsets); + free(nwa->outdata); + free(nwa->tmpdata); free(nwa); } -void -reset_nwa(NWAData *nwa) -{ +//NWAData::Rewind +void nwalib_reset(NWAData* nwa) { nwa->curblock = 0; - nwa->buffer_readpos = nwa->buffer; + nwa->outdata_readpos = nwa->outdata; nwa->samples_in_buffer = 0; } -static int use_runlength(NWAData *nwa) -{ - if (nwa->channels == 2 && nwa->bps == 16 && nwa->complevel == 2) - { - /* sw2 */ - return 0; +// can serve up 8 bits at a time +static int getbits(const uint8_t** p_data, int* shift, int bits) { + int ret; + if (*shift > 8) { + (*p_data)++; + *shift -= 8; } - if (nwa->complevel == 5) - { - if (nwa->channels == 2) return 0; /* BGM*.nwa in Little Busters! */ - return 1; /* Tomoyo After (.nwk koe file) */ + + ret = get_s16le(*p_data) >> *shift; + *shift += bits; + return ret & ((1 << bits) - 1); /* mask */ +} + +// NWADecode +static void decode_block(NWAData* nwa, const uint8_t* data, int outdatasize) { + sample d[2]; + int i; + int shift = 0; + + int dsize = outdatasize / (nwa->bps / 8); + int flip_flag = 0; /* stereo 用 */ //for stereo + int runlength = 0; + + /* 最初のデータを読み込む */ //read initial data + for (i = 0; i < nwa->channels; i++) { + if (nwa->bps == 8) { + d[i] = get_s8(data); + data += 1; + } + else { /* nwa->bps == 16 */ + d[i] = get_s16le(data); + data += 2; + } } + + for (i = 0; i < dsize; i++) { + if (runlength == 0) { /* コピーループ中でないならデータ読み込み */ //read data if not in the copy loop + int type = getbits(&data, &shift, 3); + + /* type により分岐:0, 1-6, 7 */ //fork depending on type + if (type == 7) { + /* 7 : 大きな差分 */ //big diff + /* RunLength() 有効時(CompLevel==5, 音声ファイル) では無効 */ //invalid when using RLE (comp=5, voice file) + if (getbits(&data, &shift, 1) == 1) { + d[flip_flag] = 0; /* 未使用 */ //unused + } + else { + int BITS, SHIFT; + if (nwa->complevel >= 3) { + BITS = 8; + SHIFT = 9; + } + else { + BITS = 8 - nwa->complevel; + SHIFT = 2 + 7 + nwa->complevel; + } + + { + const int MASK1 = (1 << (BITS - 1)); + const int MASK2 = (1 << (BITS - 1)) - 1; + int b = getbits(&data, &shift, BITS); + if (b & MASK1) + d[flip_flag] -= (b & MASK2) << SHIFT; + else + d[flip_flag] += (b & MASK2) << SHIFT; + } + } + } + else if (type != 0) { + /* 1-6 : 通常の差分 */ //normal diff + int BITS, SHIFT; + if (nwa->complevel >= 3) { + BITS = nwa->complevel + 3; + SHIFT = 1 + type; + } + else { + BITS = 5 - nwa->complevel; + SHIFT = 2 + type + nwa->complevel; + } + { + const int MASK1 = (1 << (BITS - 1)); + const int MASK2 = (1 << (BITS - 1)) - 1; + int b = getbits(&data, &shift, BITS); + if (b & MASK1) + d[flip_flag] -= (b & MASK2) << SHIFT; + else + d[flip_flag] += (b & MASK2) << SHIFT; + } + } + else { /* type == 0 */ + /* ランレングス圧縮なしの場合はなにもしない */ //does nothing in case of no RLE compression + if (nwa->use_runlength) { + /* ランレングス圧縮ありの場合 */ //in case of RLE compression + runlength = getbits(&data, &shift, 1); + if (runlength == 1) { + runlength = getbits(&data, &shift, 2); + if (runlength == 3) { + runlength = getbits(&data, &shift, 8); + } + } + } + } + } + else { + runlength--; + } + + if (nwa->bps == 8) { + nwa->outdata[i] = d[flip_flag] * 256; //extra (original outputs 8-bit) + } + else { + nwa->outdata[i] = d[flip_flag]; + } + + if (nwa->channels == 2) + flip_flag ^= 1; /* channel 切り替え */ //channel swap + } + + nwa->samples_in_buffer = dsize; +} + +//NWAData::Decode +int nwalib_decode(STREAMFILE* sf, NWAData* nwa) { + /* some wav/pcm handling skipped here */ + + /* 今回読み込む/デコードするデータの大きさを得る */ //get current read/decode data size + int curblocksize, curcompsize; + if (nwa->curblock != nwa->blocks - 1) { + curblocksize = nwa->blocksize * (nwa->bps / 8); + curcompsize = nwa->offsets[nwa->curblock + 1] - nwa->offsets[nwa->curblock]; + if (curblocksize >= nwa->blocksize * (nwa->bps / 8) * 2) { + return -1; // Fatal error + } + } + else { //last block + curblocksize = nwa->restsize * (nwa->bps / 8); + curcompsize = nwa->blocksize * (nwa->bps / 8) * 2; + } + // (in practice compsize is ~200-400 and blocksize ~0x800, but last block can be different) + + /* データ読み込み */ //data read (may read less on last block?) + read_streamfile(nwa->tmpdata, nwa->offsets[nwa->curblock], curcompsize, sf); + + nwa->samples_in_buffer = 0; + nwa->outdata_readpos = nwa->outdata; + + decode_block(nwa, nwa->tmpdata, curblocksize); + + nwa->curblock++; //todo check not over max blocks? + return 0; } -static void -nwa_decode_block(NWAData *nwa) -{ - /* 今回読み込む/デコードするデータの大きさを得る */ - int curblocksize; - if (nwa->curblock != nwa->blocks - 1) - { - curblocksize = nwa->blocksize * (nwa->bps / 8); - //curcompsize = nwa->offsets[nwa->curblock + 1] - nwa->offsets[nwa->curblock]; - } - else - { - curblocksize = nwa->restsize * (nwa->bps / 8); - //curcompsize = nwa->blocksize * (nwa->bps / 8) * 2; - } - - nwa->samples_in_buffer = 0; - nwa->buffer_readpos = nwa->buffer; - - { - sample d[2]; - int i; - int shift = 0; - off_t offset = nwa->offsets[nwa->curblock]; - int dsize = curblocksize / (nwa->bps / 8); - int flip_flag = 0; /* stereo 用 */ - int runlength = 0; - - /* read initial sample value */ - for (i=0;ichannels;i++) - { - if (nwa->bps == 8) { d[i] = read_8bit(offset,nwa->file); } - else /* bps == 16 */ - { - d[i] = read_16bitLE(offset,nwa->file); - offset += 2; - } - } - - for (i = 0; i < dsize; i++) - { - if (runlength == 0) - { /* コピーループ中でないならデータ読み込み */ - int type = getbits(nwa->file, &offset, &shift, 3); - /* type により分岐:0, 1-6, 7 */ - if (type == 7) - { - /* 7 : 大きな差分 */ - /* RunLength() 有効時(CompLevel==5, 音声ファイル) では無効 */ - if (getbits(nwa->file, &offset, &shift, 1) == 1) - { - d[flip_flag] = 0; /* 未使用 */ - } - else - { - int BITS, SHIFT; - if (nwa->complevel >= 3) - { - BITS = 8; - SHIFT = 9; - } - else - { - BITS = 8 - nwa->complevel; - SHIFT = 2 + 7 + nwa->complevel; - } - { - const int MASK1 = (1 << (BITS - 1)); - const int MASK2 = (1 << (BITS - 1)) - 1; - int b = getbits(nwa->file, &offset, &shift, BITS); - if (b & MASK1) - d[flip_flag] -= (b & MASK2) << SHIFT; - else - d[flip_flag] += (b & MASK2) << SHIFT; - } - } - } - else if (type != 0) - { - /* 1-6 : 通常の差分 */ - int BITS, SHIFT; - if (nwa->complevel >= 3) - { - BITS = nwa->complevel + 3; - SHIFT = 1 + type; - } - else - { - BITS = 5 - nwa->complevel; - SHIFT = 2 + type + nwa->complevel; - } - { - const int MASK1 = (1 << (BITS - 1)); - const int MASK2 = (1 << (BITS - 1)) - 1; - int b = getbits(nwa->file, &offset, &shift, BITS); - if (b & MASK1) - d[flip_flag] -= (b & MASK2) << SHIFT; - else - d[flip_flag] += (b & MASK2) << SHIFT; - } - } - else - { /* type == 0 */ - /* ランレングス圧縮なしの場合はなにもしない */ - if (use_runlength(nwa)) - { - /* ランレングス圧縮ありの場合 */ - runlength = getbits(nwa->file, &offset, &shift, 1); - if (runlength == 1) - { - runlength = getbits(nwa->file, &offset, &shift, 2); - if (runlength == 3) - { - runlength = getbits(nwa->file, &offset, &shift, 8); - } - } - } - } - } - else - { - runlength--; - } - if (nwa->bps == 8) - { - nwa->buffer[i] = d[flip_flag]*0x100; - } - else - { - nwa->buffer[i] = d[flip_flag]; - } - nwa->samples_in_buffer++; - if (nwa->channels == 2) - flip_flag ^= 1; /* channel 切り替え */ - } - } - - nwa->curblock++; - return; -} - -void -seek_nwa(NWAData *nwa, int32_t seekpos) -{ - int dest_block = seekpos/(nwa->blocksize/nwa->channels); - int32_t remainder = seekpos%(nwa->blocksize/nwa->channels); +//NWAFILE::Seek (not too similar) +void nwalib_seek(STREAMFILE* sf, NWAData* nwa, int32_t seekpos) { + int dest_block = seekpos / (nwa->blocksize / nwa->channels); + int32_t remainder = seekpos % (nwa->blocksize / nwa->channels); nwa->curblock = dest_block; - nwa_decode_block(nwa); + nwalib_decode(sf, nwa); - nwa->buffer_readpos = nwa->buffer + remainder*nwa->channels; + nwa->outdata_readpos = nwa->outdata + remainder * nwa->channels; nwa->samples_in_buffer -= remainder*nwa->channels; } -/* interface to vgmstream */ -void -decode_nwa(NWAData *nwa, sample *outbuf, - int32_t samples_to_do) -{ - while (samples_to_do > 0) - { - int32_t samples_to_read; +/* ****************************************************************** */ - if (nwa->samples_in_buffer > 0) - { - samples_to_read = nwa->samples_in_buffer/nwa->channels; +#include "coding.h" + + +struct nwa_codec_data { + STREAMFILE* sf; + NWAData* nwa; +}; + +/* interface to vgmstream */ +void decode_nwa(nwa_codec_data* data, sample_t* outbuf, int32_t samples_to_do) { + NWAData* nwa = data->nwa; + + while (samples_to_do > 0) { + if (nwa->samples_in_buffer > 0) { + int32_t samples_to_read = nwa->samples_in_buffer / nwa->channels; if (samples_to_read > samples_to_do) samples_to_read = samples_to_do; - memcpy(outbuf,nwa->buffer_readpos, - sizeof(sample)*samples_to_read*nwa->channels); + memcpy(outbuf, nwa->outdata_readpos, sizeof(sample_t) * samples_to_read * nwa->channels); - nwa->buffer_readpos += samples_to_read*nwa->channels; - nwa->samples_in_buffer -= samples_to_read*nwa->channels; - outbuf += samples_to_read*nwa->channels; + nwa->outdata_readpos += samples_to_read * nwa->channels; + nwa->samples_in_buffer -= samples_to_read * nwa->channels; + outbuf += samples_to_read * nwa->channels; samples_to_do -= samples_to_read; } - else - { - nwa_decode_block(nwa); + else { + int err = nwalib_decode(data->sf, nwa); + if (err < 0) { + VGM_LOG("NWA: decoding error\n"); + return; + } } } } + + +nwa_codec_data* init_nwa(STREAMFILE* sf) { + nwa_codec_data *data = NULL; + char filename[PATH_LIMIT]; + + data = malloc(sizeof(nwa_codec_data)); + if (!data) goto fail; + + data->nwa = nwalib_open(sf); + if (!data->nwa) goto fail; + + get_streamfile_name(sf,filename,sizeof(filename)); + data->sf = open_streamfile(sf, filename); + if (!data->sf) goto fail; + + return data; + +fail: + free_nwa(data); + return NULL; +} + +void seek_nwa(nwa_codec_data *data, int32_t sample) { + if (!data) return; + + nwalib_seek(data->sf, data->nwa, sample); +} + +void reset_nwa(nwa_codec_data *data) { + if (!data) return; + + nwalib_reset(data->nwa); +} + +void free_nwa(nwa_codec_data* data) { + if (!data) return; + + close_streamfile(data->sf); + nwalib_close(data->nwa); + free(data); +} + +STREAMFILE* nwa_get_streamfile(nwa_codec_data* data) { + if (!data) return NULL; + + return data->sf; + } diff --git a/src/coding/nwa_decoder.h b/src/coding/nwa_decoder.h index 68116cf4..613927de 100644 --- a/src/coding/nwa_decoder.h +++ b/src/coding/nwa_decoder.h @@ -1,4 +1,4 @@ -/* originally from nwatowav.cc 2007.7.28 version, which read: */ +/* derived from nwatowav.cc 2007.7.28 version, which read: */ /* * Copyright 2001-2007 jagarl / Kazunori Ueno * All Rights Reserved. @@ -35,33 +35,39 @@ #include "../streamfile.h" -typedef struct NWAData_s -{ +typedef struct NWAData_s { int channels; - int bps; /* bits per sample */ - int freq; /* samples per second */ - int complevel; /* compression level */ - int blocks; /* block count */ - int datasize; /* all data size */ - int compdatasize; /* compressed data size */ - int samplecount; /* all samples */ - int blocksize; /* samples per block */ - int restsize; /* samples of the last block */ + int bps; /* bits per sample */ + int freq; /* samples per second */ + + int complevel; /* compression level */ + int dummy; /* ? : 0x00 */ + + int blocks; /* block count */ + int datasize; /* all data size */ + + int compdatasize; /* compressed data size */ + int samplecount; /* all samples */ + int blocksize; /* samples per block */ + int restsize; /* samples of the last block */ + int dummy2; /* ? : 0x89 */ int curblock; - off_t *offsets; + off_t* offsets; + int filesize; - STREAMFILE *file; + int use_runlength; //extra - /* temporarily store samples */ - sample *buffer; - sample *buffer_readpos; + uint8_t *tmpdata; + int16_t *outdata; + int16_t *outdata_readpos; int samples_in_buffer; } NWAData; -NWAData *open_nwa(STREAMFILE *streamFile, const char *filename); -void close_nwa(NWAData *nwa); -void reset_nwa(NWAData *nwa); -void seek_nwa(NWAData *nwa, int32_t seekpos); +NWAData* nwalib_open(STREAMFILE* sf); +void nwalib_close(NWAData* nwa); +int nwalib_decode(STREAMFILE* sf, NWAData* nwa); +void nwalib_seek(STREAMFILE* sf, NWAData* nwa, int32_t seekpos); +void nwalib_reset(NWAData* nwa); #endif diff --git a/src/meta/nwa.c b/src/meta/nwa.c index 26003fbb..ca554442 100644 --- a/src/meta/nwa.c +++ b/src/meta/nwa.c @@ -1,18 +1,15 @@ #include "meta.h" -#include "../util.h" -#include "../coding/nwa_decoder.h" +#include "../coding/coding.h" #include #include -static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start); -static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start, int32_t *out_loop_end); -static nwa_codec_data *open_nwa_vgmstream(STREAMFILE *streamFile); -static void free_nwa_vgmstream(nwa_codec_data *data); +static int get_loops_nwainfo_ini(STREAMFILE *sf, int *p_loop_flag, int32_t *p_loop_start); +static int get_loops_gameexe_ini(STREAMFILE *sf, int *p_loop_flag, int32_t *p_loop_start, int32_t *p_loop_end); /* NWA - Visual Art's streams [Air (PC), Clannad (PC)] */ -VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_nwa(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; int channel_count, loop_flag = 0; int32_t loop_start_sample = 0, loop_end_sample = 0; @@ -21,26 +18,26 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) { /* checks */ - if (!check_extensions(streamFile, "nwa")) + if (!check_extensions(sf, "nwa")) goto fail; - channel_count = read_16bitLE(0x00,streamFile); + channel_count = read_16bitLE(0x00,sf); if (channel_count != 1 && channel_count != 2) goto fail; /* check if we're using raw pcm */ - if ( read_32bitLE(0x08,streamFile)==-1 || /* compression level */ - read_32bitLE(0x10,streamFile)==0 || /* block count */ - read_32bitLE(0x18,streamFile)==0 || /* compressed data size */ - read_32bitLE(0x20,streamFile)==0 || /* block size */ - read_32bitLE(0x24,streamFile)==0 ) { /* restsize */ + if ( read_32bitLE(0x08,sf)==-1 || /* compression level */ + read_32bitLE(0x10,sf)==0 || /* block count */ + read_32bitLE(0x18,sf)==0 || /* compressed data size */ + read_32bitLE(0x20,sf)==0 || /* block size */ + read_32bitLE(0x24,sf)==0 ) { /* restsize */ compression_level = -1; } else { - compression_level = read_32bitLE(0x08,streamFile); + compression_level = read_32bitLE(0x08,sf); } /* loop points come from external files */ - nwainfo_ini_found = get_loops_nwainfo_ini(streamFile, &loop_flag, &loop_start_sample); - gameexe_ini_found = !nwainfo_ini_found && get_loops_gameexe_ini(streamFile, &loop_flag, &loop_start_sample, &loop_end_sample); + nwainfo_ini_found = get_loops_nwainfo_ini(sf, &loop_flag, &loop_start_sample); + gameexe_ini_found = !nwainfo_ini_found && get_loops_gameexe_ini(sf, &loop_flag, &loop_start_sample, &loop_end_sample); start_offset = 0x2c; @@ -48,12 +45,12 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = read_32bitLE(0x04,streamFile); - vgmstream->num_samples = read_32bitLE(0x1c,streamFile) / channel_count; + vgmstream->sample_rate = read_32bitLE(0x04,sf); + vgmstream->num_samples = read_32bitLE(0x1c,sf) / channel_count; switch(compression_level) { case -1: - switch (read_16bitLE(0x02,streamFile)) { + switch (read_16bitLE(0x02,sf)) { case 8: vgmstream->coding_type = coding_PCM8; vgmstream->interleave_block_size = 0x01; @@ -76,7 +73,7 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) { case 5: vgmstream->coding_type = coding_NWA; vgmstream->layout_type = layout_none; - vgmstream->codec_data = open_nwa_vgmstream(streamFile); + vgmstream->codec_data = init_nwa(sf); if (!vgmstream->codec_data) goto fail; break; @@ -103,7 +100,7 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) { } - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) goto fail; return vgmstream; @@ -114,8 +111,8 @@ fail: /* try to locate NWAINFO.INI in the same directory */ -static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start) { - STREAMFILE *streamLoops; +static int get_loops_nwainfo_ini(STREAMFILE *sf, int *p_loop_flag, int32_t *p_loop_start) { + STREAMFILE *sf_loop; char namebase[PATH_LIMIT]; const char * ext; int length; @@ -127,15 +124,15 @@ static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int int32_t loop_start_sample = 0; - streamLoops = open_streamfile_by_filename(streamFile, "NWAINFO.INI"); - if (!streamLoops) goto fail; + sf_loop = open_streamfile_by_filename(sf, "NWAINFO.INI"); + if (!sf_loop) goto fail; - get_streamfile_filename(streamFile,namebase,PATH_LIMIT); + get_streamfile_filename(sf,namebase,PATH_LIMIT); /* ini found, try to find our name */ ext = filename_extension(namebase); length = ext - 1 - namebase; - file_size = get_streamfile_size(streamLoops); + file_size = get_streamfile_size(sf_loop); for (found = 0, offset = 0; !found && offset < file_size; offset++) { off_t suboffset; @@ -145,12 +142,12 @@ static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int for (suboffset = offset; suboffset < file_size && suboffset-offset < length && - read_8bit(suboffset,streamLoops) == namebase[suboffset-offset]; + read_8bit(suboffset,sf_loop) == namebase[suboffset-offset]; suboffset++) { /* skip */ } - if (suboffset-offset==length && read_8bit(suboffset,streamLoops)==0x09) { /* tab */ + if (suboffset-offset==length && read_8bit(suboffset,sf_loop)==0x09) { /* tab */ found = 1; found_off = suboffset+1; } @@ -160,7 +157,7 @@ static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int if (found) { char loopstring[9] = {0}; - if (read_streamfile((uint8_t*)loopstring,found_off,8,streamLoops) == 8) { + if (read_streamfile((uint8_t*)loopstring,found_off,8,sf_loop) == 8) { loop_start_sample = atol(loopstring); if (loop_start_sample > 0) loop_flag = 1; @@ -168,22 +165,22 @@ static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int } - *out_loop_flag = loop_flag; - *out_loop_start = loop_start_sample; + *p_loop_flag = loop_flag; + *p_loop_start = loop_start_sample; - close_streamfile(streamLoops); + close_streamfile(sf_loop); return 1; fail: - close_streamfile(streamLoops); + close_streamfile(sf_loop); return 0; } /* try to locate Gameexe.ini in the same directory */ -static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start, int32_t *out_loop_end) { - STREAMFILE *streamLoops; +static int get_loops_gameexe_ini(STREAMFILE* sf, int* p_loop_flag, int32_t* p_loop_start, int32_t* p_loop_end) { + STREAMFILE*sf_loop; char namebase[PATH_LIMIT]; - const char * ext; + const char* ext; int length; int found; off_t offset; @@ -193,15 +190,15 @@ static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int int32_t loop_start_sample = 0, loop_end_sample = 0; - streamLoops = open_streamfile_by_filename(streamFile, "Gameexe.ini"); - if (!streamLoops) goto fail; + sf_loop = open_streamfile_by_filename(sf, "Gameexe.ini"); + if (!sf_loop) goto fail; - get_streamfile_filename(streamFile,namebase,PATH_LIMIT); + get_streamfile_filename(sf,namebase,PATH_LIMIT); /* ini found, try to find our name */ ext = filename_extension(namebase); length = ext-1-namebase; - file_size = get_streamfile_size(streamLoops); + file_size = get_streamfile_size(sf_loop); /* format of line is: * #DSTRACK = 00000000 - eeeeeeee - ssssssss = "name" = "name2?" @@ -212,20 +209,20 @@ static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int off_t suboffset; uint8_t buf[10]; - if (read_8bit(offset,streamLoops)!='#') continue; - if (read_streamfile(buf,offset+1,10,streamLoops)!=10) break; + if (read_8bit(offset,sf_loop)!='#') continue; + if (read_streamfile(buf,offset+1,10,sf_loop)!=10) break; if (memcmp("DSTRACK = ",buf,10)) continue; - if (read_8bit(offset+44,streamLoops)!='\"') continue; + if (read_8bit(offset+44,sf_loop)!='\"') continue; for (suboffset = offset+45; suboffset < file_size && suboffset-offset-45 < length && - tolower(read_8bit(suboffset,streamLoops)) == tolower(namebase[suboffset-offset-45]); + tolower(read_8bit(suboffset,sf_loop)) == tolower(namebase[suboffset-offset-45]); suboffset++) { /* skip */ } - if (suboffset-offset-45==length && read_8bit(suboffset,streamLoops)=='\"') { /* tab */ + if (suboffset-offset-45==length && read_8bit(suboffset,sf_loop)=='\"') { /* tab */ found = 1; found_off = offset+22; /* loop end */ } @@ -234,9 +231,9 @@ static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int if (found) { char loopstring[9] = {0}; int start_ok = 0, end_ok = 0; - int32_t total_samples = read_32bitLE(0x1c,streamFile) / read_16bitLE(0x00,streamFile); + int32_t total_samples = read_32bitLE(0x1c,sf) / read_16bitLE(0x00,sf); - if (read_streamfile((uint8_t*)loopstring,found_off,8,streamLoops)==8) + if (read_streamfile((uint8_t*)loopstring,found_off,8,sf_loop)==8) { if (!memcmp("99999999",loopstring,8)) { loop_end_sample = total_samples; @@ -246,7 +243,7 @@ static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int } end_ok = 1; } - if (read_streamfile((uint8_t*)loopstring,found_off+11,8,streamLoops)==8) + if (read_streamfile((uint8_t*)loopstring,found_off+11,8,sf_loop)==8) { if (!memcmp("99999999",loopstring,8)) { /* not ok to start at last sample, @@ -265,43 +262,14 @@ static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int } /* if found file name in INI */ - *out_loop_flag = loop_flag; - *out_loop_start = loop_start_sample; - *out_loop_end = loop_end_sample; + *p_loop_flag = loop_flag; + *p_loop_start = loop_start_sample; + *p_loop_end = loop_end_sample; - close_streamfile(streamLoops); + close_streamfile(sf_loop); return 1; fail: - close_streamfile(streamLoops); + close_streamfile(sf_loop); return 0; } - - -static nwa_codec_data *open_nwa_vgmstream(STREAMFILE *streamFile) { - nwa_codec_data *data = NULL; - char filename[PATH_LIMIT]; - - streamFile->get_name(streamFile,filename,sizeof(filename)); - - data = malloc(sizeof(nwa_codec_data)); - if (!data) goto fail; - - data->nwa = open_nwa(streamFile,filename); - if (!data->nwa) goto fail; - - return data; - -fail: - free_nwa_vgmstream(data); - return NULL; -} - -static void free_nwa_vgmstream(nwa_codec_data *data) { - if (data) { - if (data->nwa) { - close_nwa(data->nwa); - } - free(data); - } -} diff --git a/src/vgmstream.c b/src/vgmstream.c index e77c73f7..1c14ad47 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -756,8 +756,7 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { } if (vgmstream->coding_type == coding_NWA) { - nwa_codec_data *data = vgmstream->codec_data; - if (data) reset_nwa(data->nwa); + reset_nwa(vgmstream->codec_data); } /* reset custom layouts */ @@ -950,13 +949,8 @@ void close_vgmstream(VGMSTREAM * vgmstream) { } if (vgmstream->coding_type == coding_NWA) { - if (vgmstream->codec_data) { - nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data; - if (data->nwa) - close_nwa(data->nwa); - free(data); - vgmstream->codec_data = NULL; - } + free_nwa(vgmstream->codec_data); + vgmstream->codec_data = NULL; } @@ -2062,8 +2056,8 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do, vgmstream->channels); break; case coding_NWA: - decode_nwa(((nwa_codec_data*)vgmstream->codec_data)->nwa, - buffer+samples_written*vgmstream->channels, samples_to_do); + decode_nwa(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, + samples_to_do); break; case coding_MSADPCM: case coding_MSADPCM_int: @@ -2367,9 +2361,7 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { #endif if (vgmstream->coding_type == coding_NWA) { - nwa_codec_data *data = vgmstream->codec_data; - if (data) - seek_nwa(data->nwa, vgmstream->loop_sample); + seek_nwa(vgmstream->codec_data, vgmstream->loop_sample); } /* restore! */ @@ -2744,8 +2736,7 @@ fail: static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM * vgmstream, int channel) { if (vgmstream->coding_type == coding_NWA) { - nwa_codec_data *data = vgmstream->codec_data; - return (data && data->nwa) ? data->nwa->file : NULL; + return nwa_get_streamfile(vgmstream->codec_data); } if (vgmstream->coding_type == coding_ACM) { diff --git a/src/vgmstream.h b/src/vgmstream.h index 2b9b93f9..aad6d4e5 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -1046,12 +1046,6 @@ typedef struct { } layered_layout_data; -/* for compressed NWA */ -typedef struct { - NWAData *nwa; -} nwa_codec_data; - - #ifdef VGM_USE_FFMPEG typedef struct { /*** IO internals ***/