Mercurial > otakunoraifu
view music2/nwatowav.cc @ 66:d112357a0ec1
Fix a bug with savegames introduced with changeset c7bcc0ec2267.
Warning: savegames created since c7bcc0ec2267 are probably corrupted,
you may have to start the game over.
If you chose not to do so, you should replace all occurrences of 'TextWindow' by 'TextImplWindow',
and 'Text Window' by 'TextImpl Window' in your save files.
author | Thibaut Girka <thib@sitedethib.com> |
---|---|
date | Sat, 11 Dec 2010 18:36:20 +0100 |
parents | 4416cfac86ae |
children |
line wrap: on
line source
/* nwatowav : Visual Arts 系のゲームのデモで使われる nwa 形式の ** ファイルを wav 形式に変換する ** ** compile : gcc -O2 -o nwatowav nwatowav.cc ** usage : nwatowav [nwa-file [outfile]] ** nwatowav [nwk-file [outfile]] ** example : nwatowav HM06.nwa HM06.wav # BGMファイル。HM06.wav に展開される ** nwatowav z2813.nwk z2813 # 音声ファイル。 z2813-100.wav などのファイル名で展開される ** nwatowav z0513.ovk z0513 # 音声ファイル。 z0513-100.ogg などのファイル名で展開される ** ** ** 2004.5.19 小松さん<s1100089@u-aizu.ac.jp> から CLANNAD の無圧縮nwa形式に対応する ** パッチをいただいたので、適用しました。ありがとうございます。 ** 2006.9.10 「智代アフター」の音声ファイル形式 (complevel = 5) をサポート ** .nwk という拡張子を持つファイルを受け取ると音声ファイルとして ** 解釈、分割して展開するようにする ** 2007.7.28 「リトルバスターズ!」の音声ファイル形式 (*.ovk; ogg 連結型) ** をサポート。.ovk という拡張子をもつファイルを受け取ると ** 音声ファイルとして解釈、分割して展開するようにする ** 「リトルバスターズ!」のBGMファイルに多量のノイズが乗る問題も ** 解決(ランレングス圧縮の処理が不必要だった) */ /* * Copyright 2001-2007 jagarl / Kazunori Ueno <jagarl@creator.club.ne.jp> * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted. * * このプログラムの作者は jagarl です。 * * このプログラム、及びコンパイルによって生成したバイナリは * プログラムを変更する、しないにかかわらず再配布可能です。 * その際、上記 Copyright 表示を保持するなどの条件は課しま * せん。対応が面倒なのでバグ報告を除き、メールで連絡をする * などの必要もありません。ソースの一部を流用することを含め、 * ご自由にお使いください。 * * THIS SOFTWARE IS PROVIDED BY KAZUNORI 'jagarl' UENO ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KAZUNORI UENO BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * */ /******************************************** ** ** nwa フォーマットについて ** ** 全体としては以下の構造を持つ ** NWA Header ** data offset index ** data block<0> ** data block<1> ** ... ** data block<N> ** ** NWA Header: ファイル先頭から 44 bytes ** magic number などはないのでnwa ファイルかは ** データの整合性から判断する必要がある ** データは全て little endian で、 ** short(signed 2byte)または int(signed 4byte) である。 ** ** +00 short channel 数(1/2) ** +02 short sample 一つあたりの bit 数(16) ** +04 int 周波数(一秒あたりのデータ数) ** +08 int 圧縮レベル:-1~5.2で最小のデータ、0で最大の復元度(-1は無圧縮rawデータとみなされる) ** +12 int ? ** +16 int ブロック数 ** +20 int 展開後のデータの大きさ(バイト単位) ** +24 int 圧縮時のデータの大きさ(nwa ファイルの大きさ。バイト単位) ** +28 int サンプル数:展開後のデータ数(16bit dataなら short 単位==サンプル単位のデータの大きさ) ** +32 int データ1ブロックを展開した時のサンプル単位のデータ数 ** +36 int 最終ブロックを展開した時のサンプル単位のデータ数 ** +40 int ? ** ** data offset index ** 全ブロック数 x 4 byte のデータ ** それぞれ int のデータが全ブロック数続いている ** ** データブロックの先頭を指すファイル先頭からの位置(オフセット) ** が格納されている ** ** data block ** 長さは可変。展開することで一定の大きさをもつデータに展開される。 ** データはDPCM形式。元 PCM データが a,b,c ならば (a),b-a, c-b と ** いった差分データが、仮数3-5bit,指数3bitの形式で保存されている。 ** 結果的に、16bit のデータが多くの場合 6-8bit で格納される。 ** 仮数のビット数は圧縮レベル0で5bit、圧縮レベル2で3bitとなる。 ** 以下、圧縮レベル2の場合について話を進める。 ** モノラルの場合: ** +00 short ブロック内の最初のデータ ** +02- bit stream ** ステレオの場合: ** +00 short 左(?)チャンネルの最初のデータ ** +02 short 右(?)チャンネルの最初のデータ ** +04- bit stream ** ** 差分データの精度が高くないので各ブロックの先頭で ** 正確なデータにより補正される(?) ** ** bit stream ** little endian ** +0 - +2 : 指数 ** +3 - +5 : 仮数 ** の形式。例えば a,b,c という8bitデータがあれば、 ** a&0x07 : データ1の指数 ** (a>>3)&0x07 : データ1の仮数(signed ; ** ((b<<2)|(a>>6))&0x07 : データ2の指数 ** (b>>1)&0x07 : データ2の仮数 ** となる。 ** ただし、指数の値により仮数のbit数が変化することがある。 ** 指数 = 1 - 6 の場合: ** a=指数、b=仮数、p=前のデータとして、今回のデータd は ** bの2bit目が立っている場合: ** d = p - (b&3)<<(4+a) ** 立ってない場合: ** d = p + (b&3)<<(4+a) ** 指数 = 0 の場合:仮数は存在しない(データは3bitとなる) ** d = p ** 「智代アフター」の音声ファイル (complevel == 5) ではランレングス圧縮用に使われている。 ** 指数 = 7 ** 次の bit が立っている場合: ** d = 0 (現在未使用) ** (データは4bitとなる) ** 次の bit が立ってない場合: ** complevel = 0,1,2: ** 仮数 b = 6bit ** b の 5bit 目が立っている場合: ** d = p - (b&0x1f)<<(4+7) ** 立ってない場合: ** d = p + (b&0x1f)<<(4+7) ** (データは10bitとなる) ** complevel = 3,4,5: ** 仮数 b = 8bit ** b の 7bit 目が立っている場合: ** d = p - (b&0x7f)<<9 ** 立ってない場合: ** d = p + (b&0x1f)<<9 ** (データは10bitとなる) ** ** 圧縮レベルが異なる場合、たとえば圧縮レベル==0で ** 指数==1~6でdの最上位bitが立っている場合 ** d = p - (b&0x0f)<<(2+a) ** 指数==7でdの最上位bitが立っている場合 ** d = p - (b&0x7f)<<(2+7) ** (b : 8bitなのでデータは12bitとなる) ** のように、精度だけが変化するようになっている。 ** ** ヘッダ読み込みについてはNWAData::ReadHeader()参照 ** bit stream からのデータ展開については NWADecode()参照 ************************************************************** */ // #define NDEBUG /* なぜか assertが入った方が速い、、、 */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> // for isatty() function #include <sys/stat.h> #include <string.h> #ifdef WORDS_BIGENDIAN #error Sorry, This program does not support BIG-ENDIAN system yet. /* もし big endian のシステムに対応させる場合 ** 以下の *_little_endian_* 及び ** getbits() 関数を変更する必要がある */ #endif inline int read_little_endian_int(const char* buf) { return *(int*)buf; } inline int read_little_endian_short(const char* buf) { return *(short*)buf; } inline int write_little_endian_int(char* buf, int number) { int c = *(int*)buf; *(int*)buf = number; return c; } inline int write_little_endian_short(char* buf, int number) { int c = *(short*)buf; *(short*)buf = number; return c; } inline int getbits(const char*& data, int& shift, int bits) { if (shift > 8) { data++; shift-=8;} int ret = read_little_endian_short(data)>>shift; shift += bits; return ret & ((1<<bits)-1); /* mask */ } /* 指定された形式のヘッダをつくる */ const char* make_wavheader(int size, int channels, int bps, int freq) { static char wavheader[0x2c] = { 'R','I','F','F', 0,0,0,0, /* +0x04: riff size*/ 'W','A','V','E', 'f','m','t',' ', 16,0,0,0, /* +0x10 : fmt size=0x10 */ 1, 0, /* +0x14 : tag : pcm = 1 */ 2, 0, /* +0x16 : channels */ 0,0,0,0, /* +0x18 : samples per second */ 0,0,0,0, /* +0x1c : average bytes per second */ 0,0, /* +0x20 : block alignment */ 0,0, /* +0x22 : bits per sample */ 'd','a','t','a', 0,0,0,0};/* +0x28 : data size */ write_little_endian_int(wavheader+0x04, size+0x24); write_little_endian_int(wavheader+0x28, size); write_little_endian_short(wavheader+0x16, channels); write_little_endian_short(wavheader+0x22, bps); write_little_endian_int(wavheader+0x18, freq); int byps = (bps+7)>>3; write_little_endian_int(wavheader+0x1c, freq*byps*channels); write_little_endian_short(wavheader+0x20, byps*channels); return wavheader; } /* NWA の bitstream展開に必要となる情報 */ class NWAInfo { private: int channels; int bps; int complevel; bool use_runlength; public: NWAInfo(int c,int b,int cl) { channels=c; bps=b; complevel=cl; use_runlength = false; if (cl == 5) { use_runlength = true; // Tomoyo After (.nwk koe file) if (channels == 2) use_runlength = false; // BGM*.nwa in Little Busters! } } int Channels(void) const{return channels;} int Bps(void) const { return bps;} int CompLevel(void) const { return complevel;} int UseRunLength(void) const { return use_runlength; } }; template<class NWAI> void NWADecode(const NWAI& info,const char* data, char* outdata, int datasize, int outdatasize) { int d[2]; int i; int shift = 0; const char* dataend = data+datasize; /* 最初のデータを読み込む */ if (info.Bps() == 8) {d[0] = *data++; datasize--;} else /* info.Bps() == 16 */ {d[0] = read_little_endian_short(data); data+=2; datasize-=2;} if (info.Channels() == 2) { if (info.Bps() == 8) {d[1] = *data++; datasize--;} else /* info.Bps() == 16 */ {d[1] = read_little_endian_short(data); data+=2; datasize-=2;} } int dsize = outdatasize / (info.Bps()/8); int flip_flag = 0; /* stereo 用 */ int runlength = 0; for (i=0; i<dsize; i++) { if (data >= dataend) break; if (runlength == 0) { // コピーループ中でないならデータ読み込み int type = getbits(data, shift, 3); /* type により分岐:0, 1-6, 7 */ if (type == 7) { /* 7 : 大きな差分 */ /* RunLength() 有効時(CompLevel==5, 音声ファイル) では無効 */ if (getbits(data, shift, 1) == 1) { d[flip_flag] = 0; /* 未使用 */ } else { int BITS, SHIFT; if (info.CompLevel() >= 3) { BITS = 8; SHIFT = 9; } else { BITS = 8-info.CompLevel(); SHIFT = 2+7+info.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 : 通常の差分 */ int BITS, SHIFT; if (info.CompLevel() >= 3) { BITS = info.CompLevel()+3; SHIFT = 1+type; } else { BITS = 5-info.CompLevel(); SHIFT = 2+type+info.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 */ /* ランレングス圧縮なしの場合はなにもしない */ if (info.UseRunLength() == true) { /* ランレングス圧縮ありの場合 */ runlength = getbits(data,shift,1); if (runlength==1) { runlength = getbits(data,shift,2); if (runlength == 3) { runlength = getbits(data, shift, 8); } } } } } else { runlength--; } if (info.Bps() == 8) { *outdata++ = d[flip_flag]; } else { write_little_endian_short(outdata, d[flip_flag]); outdata += 2; } if (info.Channels() == 2) flip_flag ^= 1; /* channel 切り替え */ } return; } class NWAData { public: int channels; int bps; /* bits per sample */ int freq; /* samples per second */ private: int complevel; /* compression level */ int dummy; /* ? : 0x00 */ public: int blocks; /* block count */ int datasize; /* all data size */ private: 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; int* offsets; int offset_start; int filesize; char* tmpdata; public: void ReadHeader(FILE* in, int file_size=-1); int CheckHeader(void); /* false: invalid true: valid */ NWAData(void) { offsets = NULL; tmpdata = NULL; } ~NWAData(void) { if (offsets) delete[] offsets; if (tmpdata) delete[] tmpdata; } int BlockLength(void) { if (complevel != -1) { if (offsets == NULL) return false; if (tmpdata == NULL) return false; } return blocksize * (bps/8); } /* data は BlockLength 以上の長さを持つこと ** 返り値は作成したデータの長さ。終了時は 0。 ** エラー時は -1 */ int Decode(FILE* in, char* data, int& skip_count); void Rewind(FILE* in); }; void NWAData::ReadHeader(FILE* in, int _file_size) { char header[0x2c]; struct stat sb; int i; if (offsets) delete[] offsets; if (tmpdata) delete[] tmpdata; offsets = NULL; tmpdata = NULL; filesize = 0; offset_start = ftell(in); if (offset_start == -1) offset_start = 0; if (_file_size != -1) filesize = _file_size; curblock = -1; /* header 読み込み */ if (in == NULL || feof(in) || ferror(in)) { fprintf(stderr,"invalid stream\n"); return; } fread(header, 0x2c, 1, in); if (feof(in) || ferror(in)) { fprintf(stderr,"invalid stream\n"); return; } channels = read_little_endian_short(header+0x00); bps = read_little_endian_short(header+0x02); freq = read_little_endian_int(header+0x04); complevel = read_little_endian_int(header+0x08); dummy = read_little_endian_int(header+0x0c); blocks = read_little_endian_int(header+0x10); datasize = read_little_endian_int(header+0x14); compdatasize = read_little_endian_int(header+0x18); samplecount = read_little_endian_int(header+0x1c); blocksize = read_little_endian_int(header+0x20); restsize = read_little_endian_int(header+0x24); dummy2 = read_little_endian_int(header+0x28); if (complevel == -1) { /* 無圧縮rawデータ */ /* 適当に決め打ちする */ blocksize = 65536; restsize = (datasize % (blocksize * (bps/8))) / (bps/8); blocks = datasize / (blocksize * (bps/8)) + (restsize > 0 ? 1 : 0); } if (blocks <= 0 || blocks > 1000000) { /* 1時間を超える曲ってのはないでしょ*/ fprintf(stderr,"too large blocks : %d\n",blocks); return; } /* regular file なら filesize 読み込み */ if (filesize == 0 && fstat(fileno(in), &sb)==0 && (sb.st_mode&S_IFMT) == S_IFREG) { int pos = ftell(in); fseek(in, 0, SEEK_END); filesize = ftell(in); fseek(in, pos, SEEK_SET); if (pos+blocks*4 >= filesize) { fprintf(stderr,"offset block is not exist\n"); return; } } if (complevel == -1) return; /* offset index 読み込み */ offsets = new int[blocks]; fread(offsets, blocks, 4, in); for (i=0; i<blocks; i++) { offsets[i] = read_little_endian_int((char*)(offsets+i)); } if (feof(in) || ferror(in)) { fprintf(stderr,"invalid stream\n"); delete[] offsets; offsets = NULL; return; } } void NWAData::Rewind(FILE* in) { curblock = -1; fseek(in, 0x2c, SEEK_SET); if (offsets) fseek(in, blocks*4, SEEK_CUR); } int NWAData::CheckHeader(void) { if (complevel != -1 && offsets == NULL) return false; /* データそのもののチェック */ if (channels != 1 && channels != 2) { fprintf(stderr,"This program only supports mono / stereo data : data have %d channels.\n",channels); return false; } if (bps != 8 && bps != 16) { fprintf(stderr,"This program only supports 8 / 16bit data : data is %d bits\n",bps); return false; } if (complevel == -1) { int byps = bps/8; /* bytes per sample */ if (datasize != samplecount*byps) { fprintf(stderr,"invalid datasize : datasize %d != samplecount %d * samplesize %d\n",datasize,samplecount,byps); return false; } if (samplecount != (blocks-1)*blocksize+restsize ) { fprintf(stderr,"total sample count is invalid : samplecount %d != %d*%d+%d(block*blocksize+lastblocksize).\n",samplecount,blocks-1,blocksize,restsize); return false; } else return true; } //if (complevel < 0 || complevel > 2) { if (complevel < 0 || complevel > 5) { fprintf(stderr,"This program only supports -1,0,1,2 compression level : the level of data is %d\n",complevel); return false; } /* 整合性チェック */ if (filesize != 0 && filesize != compdatasize) { fprintf(stderr,"file size is invalid : %d != %d\n",filesize,compdatasize); return false; } if (offsets[blocks-1] >= compdatasize) { fprintf(stderr,"the last offset overruns the file.\n"); return false; } int byps = bps/8; /* bytes per sample */ if (datasize != samplecount*byps) { fprintf(stderr,"invalid datasize : datasize %d != samplecount %d * samplesize %d\n",datasize,samplecount,byps); return false; } if (samplecount != (blocks-1)*blocksize+restsize ) { fprintf(stderr,"total sample count is invalid : samplecount %d != %d*%d+%d(block*blocksize+lastblocksize).\n",samplecount,blocks-1,blocksize,restsize); return false; } tmpdata = new char[blocksize*byps*2]; /* これ以上の大きさはないだろう、、、 */ return true; } class NWAInfo_sw2 { public: int Channels(void) const{return 2;} int Bps(void) const { return 16;} int CompLevel(void) const { return 2;} int UseRunLength(void) const { return false; } }; int NWAData::Decode(FILE* in, char* data, int& skip_count) { if (complevel == -1) { /* 無圧縮時の処理 */ if (feof(in) || ferror(in)) return -1; if (curblock == -1) { /* 最初のブロックなら、wave header 出力 */ memcpy(data, make_wavheader(datasize, channels, bps, freq), 0x2c); curblock++; fseek(in, offset_start + 0x2c, SEEK_SET); return 0x2c; } if (skip_count > blocksize/channels) { skip_count -= blocksize/channels; fseek(in, blocksize*(bps/8), SEEK_CUR); curblock++; return -2; } if (curblock < blocks) { int readsize = blocksize; if (skip_count) { fseek(in, skip_count*channels*(bps/8), SEEK_CUR); readsize -= skip_count * channels; skip_count = 0; } int err = fread(data, 1, readsize * (bps/8), in); curblock++; return err; } return -1; } if (offsets == NULL || tmpdata == NULL) return -1; if (blocks == curblock) return 0; if (feof(in) || ferror(in)) return -1; if (curblock == -1) { /* 最初のブロックなら、wave header 出力 */ memcpy(data, make_wavheader(datasize, channels, bps, freq), 0x2c); curblock++; return 0x2c; } /* 今回読み込む/デコードするデータの大きさを得る */ int curblocksize, curcompsize; if (curblock != blocks-1) { curblocksize = blocksize * (bps/8); curcompsize = offsets[curblock+1] - offsets[curblock]; if (curblocksize >= blocksize*(bps/8)*2) return -1; // Fatal error } else { curblocksize = restsize * (bps/8); curcompsize = blocksize*(bps/8)*2; } if (skip_count > blocksize/channels) { skip_count -= blocksize/channels; fseek(in, curcompsize, SEEK_CUR); curblock++; return -2; } /* データ読み込み */ fread(tmpdata, 1, curcompsize, in); /* 展開 */ if (channels == 2 && bps == 16 && complevel == 2) { NWAInfo_sw2 info; NWADecode(info, tmpdata, data, curcompsize, curblocksize); } else { NWAInfo info(channels, bps, complevel); NWADecode(info, tmpdata, data, curcompsize, curblocksize); } int retsize = curblocksize; if (skip_count) { int skip_c = skip_count * channels * (bps/8); retsize -= skip_c; memmove(data, data+skip_c, skip_c); skip_count = 0; } curblock++; return retsize; } #ifdef USE_MAIN void conv(FILE* in, FILE* out, int skip_count, int in_size = -1) { NWAData h; h.ReadHeader(in, in_size); h.CheckHeader(); int bs = h.BlockLength(); char* d = new char[bs]; int err; while( (err=h.Decode(in, d, skip_count)) != 0) { if (err == -1) break; if (err == -2) continue; fwrite(d, err, 1, out); } return; } int main(int argc, char** argv) { int skip_count = 0; if (argc > 2 && strcmp(argv[1], "--skip") == 0) { skip_count = atoi(argv[2]); argc -= 2; argv[1] = argv[3]; argv[2] = argv[4]; } if (argc != 2 && argc != 3) { fprintf(stderr,"usage : nwatowav [inputfile [outputfile]]\n"); return -1; } if (strstr(argv[1], ".nwk") != NULL || strstr(argv[1], ".ovk") != NULL) { bool is_ovk; int headblk_sz; const char* out_ext; char* outpath = new char[strlen(argv[1])+10]; char buf[1024]; memset(buf, 0, 1024); FILE* in = fopen(argv[1], "rb"); if (in == NULL) { fprintf(stderr,"Cannot open file : %s\n",argv[1]); return -1; } if (strstr(argv[1], ".ovk") != NULL) { is_ovk = true; headblk_sz = 16; out_ext = "ogg"; } else { is_ovk = false; headblk_sz = 12; out_ext = "wav"; } fread(buf, 1, 4, in); int index = read_little_endian_int(buf); if (index <= 0) { if (is_ovk) fprintf(stderr,"Invalid Ogg-ovk file : %s : index = %d\n",argv[1],index); else fprintf(stderr,"Invalid Koe-nwk file : %s : index = %d\n",argv[1],index); return -1; } int* tbl_off = new int[index]; int* tbl_siz = new int[index]; int* tbl_cnt = new int[index]; int* tbl_origsiz = new int[index]; int i; for (i=0; i<index; i++) { fread(buf, 1, headblk_sz, in); tbl_siz[i] = read_little_endian_int(buf); tbl_off[i] = read_little_endian_int(buf+4); tbl_cnt[i] = read_little_endian_int(buf+8); tbl_origsiz[i] = read_little_endian_int(buf+12); } fseek(in, 0, SEEK_END); int fsize = ftell(in); for (i=0; i<index; i++) { if (tbl_off[i] <= 0 || tbl_siz[i] <= 0 || tbl_off[i]+tbl_siz[i] > fsize) { fprintf(stderr,"Invalid table[%d] : cnt %d off %d size %d / %d\n",i,tbl_cnt[i],tbl_off[i],tbl_siz[i],fsize); continue; } if (argc == 2) sprintf(outpath, "%s-%d.%s", argv[1], tbl_cnt[i],out_ext); else sprintf(outpath, "%s-%d.%s", argv[2], tbl_cnt[i],out_ext); FILE* out = fopen(outpath, "wb"); if (out == NULL) { fprintf(stderr,"Cannot open output file %s\n",outpath); continue; } fprintf(stderr,"Writing file %s...\n",outpath); fseek(in, tbl_off[i], SEEK_SET); if (is_ovk) { // copy file int sz = tbl_siz[i]; char buf[32*1024]; while(sz > 32*1024) { fread(buf, 32*1024, 1, in); fwrite(buf, 32*1024, 1, out); sz -= 1024*32; } if (sz > 0) { fread(buf, sz, 1, in); fwrite(buf, sz, 1, out); } } else { // .nwk conv(in, out, 0, tbl_siz[i]); } fclose(out); } fclose(in); return 0; } FILE* in = fopen(argv[1],"rb"); if (in == NULL) { fprintf(stderr,"Cannot open file : %s\n",argv[1]); return -1; } FILE* out; if (argc != 3 && (!isatty(fileno(stdout)))) { // wave file is written to stdout if stdout is redirected to a file out = stdout; } else { // make a new file or use argv[2] for output file name char* outpath = new char[strlen(argv[1])+10]; sprintf(outpath, "%s.wav",argv[1]); if (argc == 3) outpath = argv[2]; out = fopen(outpath, "wb"); if (out == NULL) { fprintf(stderr,"Cannot open file : %s\n",outpath); return -1; } } conv(in, out, skip_count); fclose(in); if (out != stdout) fclose(out); return 0; } #else #include"wavfile.h" void NWAFILE::Seek(int count) { if (data == NULL) data = new char[block_size]; nwa->Rewind(stream); int dmy = 0; nwa->Decode(stream, data, dmy); // skip wav header data_len = 0; skip_count = count; } NWAFILE::NWAFILE(FILE* _stream) { skip_count = 0; data = NULL; stream = _stream; nwa = new NWAData; nwa->ReadHeader(stream); if (!nwa->CheckHeader()) { return; } block_size = nwa->BlockLength(); data = new char[block_size]; data_len = 0; wavinfo.SamplingRate = nwa->freq; wavinfo.Channels = nwa->channels; wavinfo.DataBits = nwa->bps; int dmy = 0; data_len = nwa->Decode(stream, data, dmy); // skip wav header return; } NWAFILE::~NWAFILE() { if (stream) fclose(stream); if (data) delete[] data; if (nwa) delete nwa; } int NWAFILE::Read(char* buf, int blksize, int blklen) { if (data == NULL) return -1; // end of file if (data_len > blksize * blklen) { int len = blksize * blklen; memcpy(buf, data, len); memmove(data, data+len, data_len-len); data_len -= len; return blklen; } memcpy(buf, data, data_len); int copied_length = data_len; data_len = 0; if (stream == NULL) { delete[] data; data = NULL; return copied_length / blksize; } //TODO: Rewrite this joke // read do { int err; retry: err = nwa->Decode(stream, data, skip_count); if (err == 0 || err == -1) { // eof or error delete[] data; data = NULL; return copied_length / blksize; } if (err == -2) goto retry; // EAGAIN data_len = err; if (copied_length + data_len < blklen*blksize) { memcpy(buf+copied_length, data, data_len); copied_length += data_len; goto retry; } } while(0); // determine return length int datablks = (data_len+copied_length)/blksize; if (datablks <= 0) return 0; if (datablks > blklen) datablks = blklen; int rest_len = datablks * blksize - copied_length; if (rest_len) { memcpy(buf+copied_length, data, rest_len); memmove(data, data+rest_len, data_len-rest_len); data_len -= rest_len; } return datablks; } char* NWAFILE::ReadAll(FILE* in, int& total_size) { NWAData h; if (in == NULL) return NULL; h.ReadHeader(in); h.CheckHeader(); int bs = h.BlockLength(); total_size = h.datasize+0x2c; char* d = new char[total_size + bs*2]; int dcur = 0; int err; int skip = 0; while(dcur < total_size+bs && (err=h.Decode(in, d+dcur, skip)) != 0) { if (err == -1) break; if (err == -2) continue; dcur += err; } return d; } #include "music.h" char* decode_koe_nwa(AvgKoeInfo info, int* data_len) { NWAData h; if (info.stream == NULL) return NULL; fseek(info.stream, info.offset, SEEK_SET); h.ReadHeader(info.stream, info.length); if (h.CheckHeader() == false) return NULL; int bs = h.BlockLength(); int total = h.datasize + 0x2c; char* d = new char[total + bs*2]; int dcur = 0; int err; int skip = 0; while(dcur < total+bs && (err=h.Decode(info.stream, d+dcur, skip)) != 0) { if (err == -1) break; if (err == -2) continue; dcur += err; } if (data_len) { *data_len = dcur; if (*data_len > total) *data_len = total; } return d; } #endif