NWA cleanup and improve performance

This commit is contained in:
bnnm 2020-07-17 19:15:35 +02:00
parent 7592e72bae
commit 73db56f327
6 changed files with 442 additions and 385 deletions

View File

@ -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);

View File

@ -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<N>
*
* - 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 <jagarl@creator.club.ne.jp>
* All Rights Reserved.
@ -33,319 +62,382 @@
#include <stdlib.h>
#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;i<nwa->channels;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;
}

View File

@ -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 <jagarl@creator.club.ne.jp>
* 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

View File

@ -1,18 +1,15 @@
#include "meta.h"
#include "../util.h"
#include "../coding/nwa_decoder.h"
#include "../coding/coding.h"
#include <string.h>
#include <ctype.h>
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);
}
}

View File

@ -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) {

View File

@ -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 ***/