0
|
1 /* nwatowav : Visual Arts 系のゲームのデモで使われる nwa 形式の
|
|
2 ** ファイルを wav 形式に変換する
|
|
3 **
|
|
4 ** compile : gcc -O2 -o nwatowav nwatowav.cc
|
|
5 ** usage : nwatowav [nwa-file [outfile]]
|
|
6 ** nwatowav [nwk-file [outfile]]
|
|
7 ** example : nwatowav HM06.nwa HM06.wav # BGMファイル。HM06.wav に展開される
|
|
8 ** nwatowav z2813.nwk z2813 # 音声ファイル。 z2813-100.wav などのファイル名で展開される
|
|
9 ** nwatowav z0513.ovk z0513 # 音声ファイル。 z0513-100.ogg などのファイル名で展開される
|
|
10 **
|
|
11 **
|
|
12 ** 2004.5.19 小松さん<s1100089@u-aizu.ac.jp> から CLANNAD の無圧縮nwa形式に対応する
|
|
13 ** パッチをいただいたので、適用しました。ありがとうございます。
|
|
14 ** 2006.9.10 「智代アフター」の音声ファイル形式 (complevel = 5) をサポート
|
|
15 ** .nwk という拡張子を持つファイルを受け取ると音声ファイルとして
|
|
16 ** 解釈、分割して展開するようにする
|
|
17 ** 2007.7.28 「リトルバスターズ!」の音声ファイル形式 (*.ovk; ogg 連結型)
|
|
18 ** をサポート。.ovk という拡張子をもつファイルを受け取ると
|
|
19 ** 音声ファイルとして解釈、分割して展開するようにする
|
|
20 ** 「リトルバスターズ!」のBGMファイルに多量のノイズが乗る問題も
|
|
21 ** 解決(ランレングス圧縮の処理が不必要だった)
|
|
22 */
|
|
23
|
|
24 /*
|
|
25 * Copyright 2001-2007 jagarl / Kazunori Ueno <jagarl@creator.club.ne.jp>
|
|
26 * All Rights Reserved.
|
|
27 *
|
|
28 * Redistribution and use in source and binary forms, with or without
|
|
29 * modification, are permitted.
|
|
30 *
|
|
31 * このプログラムの作者は jagarl です。
|
|
32 *
|
|
33 * このプログラム、及びコンパイルによって生成したバイナリは
|
|
34 * プログラムを変更する、しないにかかわらず再配布可能です。
|
|
35 * その際、上記 Copyright 表示を保持するなどの条件は課しま
|
|
36 * せん。対応が面倒なのでバグ報告を除き、メールで連絡をする
|
|
37 * などの必要もありません。ソースの一部を流用することを含め、
|
|
38 * ご自由にお使いください。
|
|
39 *
|
|
40 * THIS SOFTWARE IS PROVIDED BY KAZUNORI 'jagarl' UENO ``AS IS'' AND ANY
|
|
41 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
43 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KAZUNORI UENO BE LIABLE
|
|
44 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
45 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
|
46 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
47 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
48 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
49 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
|
50 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
|
51 * DAMAGE.
|
|
52 *
|
|
53 */
|
|
54
|
|
55 /********************************************
|
|
56 **
|
|
57 ** nwa フォーマットについて
|
|
58 **
|
|
59 ** 全体としては以下の構造を持つ
|
|
60 ** NWA Header
|
|
61 ** data offset index
|
|
62 ** data block<0>
|
|
63 ** data block<1>
|
|
64 ** ...
|
|
65 ** data block<N>
|
|
66 **
|
|
67 ** NWA Header: ファイル先頭から 44 bytes
|
|
68 ** magic number などはないのでnwa ファイルかは
|
|
69 ** データの整合性から判断する必要がある
|
|
70 ** データは全て little endian で、
|
|
71 ** short(signed 2byte)または int(signed 4byte) である。
|
|
72 **
|
|
73 ** +00 short channel 数(1/2)
|
|
74 ** +02 short sample 一つあたりの bit 数(16)
|
|
75 ** +04 int 周波数(一秒あたりのデータ数)
|
|
76 ** +08 int 圧縮レベル:-1~5.2で最小のデータ、0で最大の復元度(-1は無圧縮rawデータとみなされる)
|
|
77 ** +12 int ?
|
|
78 ** +16 int ブロック数
|
|
79 ** +20 int 展開後のデータの大きさ(バイト単位)
|
|
80 ** +24 int 圧縮時のデータの大きさ(nwa ファイルの大きさ。バイト単位)
|
|
81 ** +28 int サンプル数:展開後のデータ数(16bit dataなら short 単位==サンプル単位のデータの大きさ)
|
|
82 ** +32 int データ1ブロックを展開した時のサンプル単位のデータ数
|
|
83 ** +36 int 最終ブロックを展開した時のサンプル単位のデータ数
|
|
84 ** +40 int ?
|
|
85 **
|
|
86 ** data offset index
|
|
87 ** 全ブロック数 x 4 byte のデータ
|
|
88 ** それぞれ int のデータが全ブロック数続いている
|
|
89 **
|
|
90 ** データブロックの先頭を指すファイル先頭からの位置(オフセット)
|
|
91 ** が格納されている
|
|
92 **
|
|
93 ** data block
|
|
94 ** 長さは可変。展開することで一定の大きさをもつデータに展開される。
|
|
95 ** データはDPCM形式。元 PCM データが a,b,c ならば (a),b-a, c-b と
|
|
96 ** いった差分データが、仮数3-5bit,指数3bitの形式で保存されている。
|
|
97 ** 結果的に、16bit のデータが多くの場合 6-8bit で格納される。
|
|
98 ** 仮数のビット数は圧縮レベル0で5bit、圧縮レベル2で3bitとなる。
|
|
99 ** 以下、圧縮レベル2の場合について話を進める。
|
|
100 ** モノラルの場合:
|
|
101 ** +00 short ブロック内の最初のデータ
|
|
102 ** +02- bit stream
|
|
103 ** ステレオの場合:
|
|
104 ** +00 short 左(?)チャンネルの最初のデータ
|
|
105 ** +02 short 右(?)チャンネルの最初のデータ
|
|
106 ** +04- bit stream
|
|
107 **
|
|
108 ** 差分データの精度が高くないので各ブロックの先頭で
|
|
109 ** 正確なデータにより補正される(?)
|
|
110 **
|
|
111 ** bit stream
|
|
112 ** little endian
|
|
113 ** +0 - +2 : 指数
|
|
114 ** +3 - +5 : 仮数
|
|
115 ** の形式。例えば a,b,c という8bitデータがあれば、
|
|
116 ** a&0x07 : データ1の指数
|
|
117 ** (a>>3)&0x07 : データ1の仮数(signed ;
|
|
118 ** ((b<<2)|(a>>6))&0x07 : データ2の指数
|
|
119 ** (b>>1)&0x07 : データ2の仮数
|
|
120 ** となる。
|
|
121 ** ただし、指数の値により仮数のbit数が変化することがある。
|
|
122 ** 指数 = 1 - 6 の場合:
|
|
123 ** a=指数、b=仮数、p=前のデータとして、今回のデータd は
|
|
124 ** bの2bit目が立っている場合:
|
|
125 ** d = p - (b&3)<<(4+a)
|
|
126 ** 立ってない場合:
|
|
127 ** d = p + (b&3)<<(4+a)
|
|
128 ** 指数 = 0 の場合:仮数は存在しない(データは3bitとなる)
|
|
129 ** d = p
|
|
130 ** 「智代アフター」の音声ファイル (complevel == 5) ではランレングス圧縮用に使われている。
|
|
131 ** 指数 = 7
|
|
132 ** 次の bit が立っている場合:
|
|
133 ** d = 0 (現在未使用)
|
|
134 ** (データは4bitとなる)
|
|
135 ** 次の bit が立ってない場合:
|
|
136 ** complevel = 0,1,2:
|
|
137 ** 仮数 b = 6bit
|
|
138 ** b の 5bit 目が立っている場合:
|
|
139 ** d = p - (b&0x1f)<<(4+7)
|
|
140 ** 立ってない場合:
|
|
141 ** d = p + (b&0x1f)<<(4+7)
|
|
142 ** (データは10bitとなる)
|
|
143 ** complevel = 3,4,5:
|
|
144 ** 仮数 b = 8bit
|
|
145 ** b の 7bit 目が立っている場合:
|
|
146 ** d = p - (b&0x7f)<<9
|
|
147 ** 立ってない場合:
|
|
148 ** d = p + (b&0x1f)<<9
|
|
149 ** (データは10bitとなる)
|
|
150 **
|
|
151 ** 圧縮レベルが異なる場合、たとえば圧縮レベル==0で
|
|
152 ** 指数==1~6でdの最上位bitが立っている場合
|
|
153 ** d = p - (b&0x0f)<<(2+a)
|
|
154 ** 指数==7でdの最上位bitが立っている場合
|
|
155 ** d = p - (b&0x7f)<<(2+7)
|
|
156 ** (b : 8bitなのでデータは12bitとなる)
|
|
157 ** のように、精度だけが変化するようになっている。
|
|
158 **
|
|
159 ** ヘッダ読み込みについてはNWAData::ReadHeader()参照
|
|
160 ** bit stream からのデータ展開については NWADecode()参照
|
|
161 **************************************************************
|
|
162 */
|
|
163
|
|
164 // #define NDEBUG /* なぜか assertが入った方が速い、、、 */
|
|
165
|
|
166 #include<stdio.h>
|
|
167 #include<stdlib.h>
|
|
168 #include<unistd.h> // for isatty() function
|
|
169 #include<sys/stat.h>
|
|
170 #include<string.h>
|
|
171
|
|
172
|
|
173 #ifdef WORDS_BIGENDIAN
|
|
174 #error Sorry, This program does not support BIG-ENDIAN system yet.
|
|
175 /* もし big endian のシステムに対応させる場合
|
|
176 ** 以下の *_little_endian_* 及び
|
|
177 ** getbits() 関数を変更する必要がある
|
|
178 */
|
|
179 #endif
|
|
180
|
|
181 inline int read_little_endian_int(const char* buf) {
|
|
182 return *(int*)buf;
|
|
183 }
|
|
184
|
|
185 inline int read_little_endian_short(const char* buf) {
|
|
186 return *(short*)buf;
|
|
187 }
|
|
188
|
|
189 inline int write_little_endian_int(char* buf, int number) {
|
|
190 int c = *(int*)buf; *(int*)buf = number; return c;
|
|
191 }
|
|
192
|
|
193 inline int write_little_endian_short(char* buf, int number) {
|
|
194 int c = *(short*)buf; *(short*)buf = number; return c;
|
|
195 }
|
|
196 inline int getbits(const char*& data, int& shift, int bits) {
|
|
197 if (shift > 8) { data++; shift-=8;}
|
|
198 int ret = read_little_endian_short(data)>>shift;
|
|
199 shift += bits;
|
|
200 return ret & ((1<<bits)-1); /* mask */
|
|
201 }
|
|
202
|
|
203 /* 指定された形式のヘッダをつくる */
|
|
204 const char* make_wavheader(int size, int channels, int bps, int freq) {
|
|
205 static char wavheader[0x2c] = {
|
|
206 'R','I','F','F',
|
|
207 0,0,0,0, /* +0x04: riff size*/
|
|
208 'W','A','V','E',
|
|
209 'f','m','t',' ',
|
|
210 16,0,0,0, /* +0x10 : fmt size=0x10 */
|
|
211 1, 0, /* +0x14 : tag : pcm = 1 */
|
|
212 2, 0, /* +0x16 : channels */
|
|
213 0,0,0,0, /* +0x18 : samples per second */
|
|
214 0,0,0,0, /* +0x1c : average bytes per second */
|
|
215 0,0, /* +0x20 : block alignment */
|
|
216 0,0, /* +0x22 : bits per sample */
|
|
217 'd','a','t','a',
|
|
218 0,0,0,0};/* +0x28 : data size */
|
|
219 write_little_endian_int(wavheader+0x04, size+0x24);
|
|
220 write_little_endian_int(wavheader+0x28, size);
|
|
221 write_little_endian_short(wavheader+0x16, channels);
|
|
222 write_little_endian_short(wavheader+0x22, bps);
|
|
223 write_little_endian_int(wavheader+0x18, freq);
|
|
224 int byps = (bps+7)>>3;
|
|
225 write_little_endian_int(wavheader+0x1c, freq*byps*channels);
|
|
226 write_little_endian_short(wavheader+0x20, byps*channels);
|
|
227 return wavheader;
|
|
228 }
|
|
229
|
|
230 /* NWA の bitstream展開に必要となる情報 */
|
|
231 class NWAInfo {
|
|
232 int channels;
|
|
233 int bps;
|
|
234 int complevel;
|
|
235 bool use_runlength;
|
|
236 public:
|
|
237 NWAInfo(int c,int b,int cl) {
|
|
238 channels=c;
|
|
239 bps=b;
|
|
240 complevel=cl;
|
|
241 use_runlength = false;
|
|
242 if (cl == 5) {
|
|
243 use_runlength = true; // Tomoyo After (.nwk koe file)
|
|
244 if (channels == 2) use_runlength = false; // BGM*.nwa in Little Busters!
|
|
245 }
|
|
246 }
|
|
247 int Channels(void) const{return channels;}
|
|
248 int Bps(void) const { return bps;}
|
|
249 int CompLevel(void) const { return complevel;}
|
|
250 int UseRunLength(void) const { return use_runlength; }
|
|
251 };
|
|
252
|
|
253 template<class NWAI> void NWADecode(const NWAI& info,const char* data, char* outdata, int datasize, int outdatasize) {
|
|
254 int d[2];
|
|
255 int i,j;
|
|
256 int shift = 0;
|
|
257 const char* dataend = data+datasize;
|
|
258 /* 最初のデータを読み込む */
|
|
259 if (info.Bps() == 8) {d[0] = *data++; datasize--;}
|
|
260 else /* info.Bps() == 16 */ {d[0] = read_little_endian_short(data); data+=2; datasize-=2;}
|
|
261 if (info.Channels() == 2) {
|
|
262 if (info.Bps() == 8) {d[1] = *data++; datasize--;}
|
|
263 else /* info.Bps() == 16 */ {d[1] = read_little_endian_short(data); data+=2; datasize-=2;}
|
|
264 }
|
|
265 int dsize = outdatasize / (info.Bps()/8);
|
|
266 int flip_flag = 0; /* stereo 用 */
|
|
267 int runlength = 0;
|
|
268 for (i=0; i<dsize; i++) {
|
|
269 if (data >= dataend) break;
|
|
270 if (runlength == 0) { // コピーループ中でないならデータ読み込み
|
|
271 int type = getbits(data, shift, 3);
|
|
272 /* type により分岐:0, 1-6, 7 */
|
|
273 if (type == 7) {
|
|
274 /* 7 : 大きな差分 */
|
|
275 /* RunLength() 有効時(CompLevel==5, 音声ファイル) では無効 */
|
|
276 if (getbits(data, shift, 1) == 1) {
|
|
277 d[flip_flag] = 0; /* 未使用 */
|
|
278 } else {
|
|
279 int BITS, SHIFT;
|
|
280 if (info.CompLevel() >= 3) {
|
|
281 BITS = 8;
|
|
282 SHIFT = 9;
|
|
283 } else {
|
|
284 BITS = 8-info.CompLevel();
|
|
285 SHIFT = 2+7+info.CompLevel();
|
|
286 }
|
|
287 const int MASK1 = (1<<(BITS-1));
|
|
288 const int MASK2 = (1<<(BITS-1))-1;
|
|
289 int b = getbits(data, shift, BITS);
|
|
290 if (b&MASK1)
|
|
291 d[flip_flag] -= (b&MASK2)<<SHIFT;
|
|
292 else
|
|
293 d[flip_flag] += (b&MASK2)<<SHIFT;
|
|
294 }
|
|
295 } else if (type != 0) {
|
|
296 /* 1-6 : 通常の差分 */
|
|
297 int BITS, SHIFT;
|
|
298 if (info.CompLevel() >= 3) {
|
|
299 BITS = info.CompLevel()+3;
|
|
300 SHIFT = 1+type;
|
|
301 } else {
|
|
302 BITS = 5-info.CompLevel();
|
|
303 SHIFT = 2+type+info.CompLevel();
|
|
304 }
|
|
305 const int MASK1 = (1<<(BITS-1));
|
|
306 const int MASK2 = (1<<(BITS-1))-1;
|
|
307 int b = getbits(data, shift, BITS);
|
|
308 if (b&MASK1)
|
|
309 d[flip_flag] -= (b&MASK2)<<SHIFT;
|
|
310 else
|
|
311 d[flip_flag] += (b&MASK2)<<SHIFT;
|
|
312 } else { /* type == 0 */
|
|
313 /* ランレングス圧縮なしの場合はなにもしない */
|
|
314 if (info.UseRunLength() == true) {
|
|
315 /* ランレングス圧縮ありの場合 */
|
|
316 runlength = getbits(data,shift,1);
|
|
317 if (runlength==1) {
|
|
318 runlength = getbits(data,shift,2);
|
|
319 if (runlength == 3) {
|
|
320 runlength = getbits(data, shift, 8);
|
|
321 }
|
|
322 }
|
|
323 }
|
|
324 }
|
|
325 } else {
|
|
326 runlength--;
|
|
327 }
|
|
328 if (info.Bps() == 8) {
|
|
329 *outdata++ = d[flip_flag];
|
|
330 } else {
|
|
331 write_little_endian_short(outdata, d[flip_flag]);
|
|
332 outdata += 2;
|
|
333 }
|
|
334 if (info.Channels() == 2) flip_flag ^= 1; /* channel 切り替え */
|
|
335 }
|
|
336 return;
|
|
337 };
|
|
338
|
|
339 class NWAData {
|
|
340 public:
|
|
341 int channels;
|
|
342 int bps; /* bits per sample */
|
|
343 int freq; /* samples per second */
|
|
344 private:
|
|
345 int complevel; /* compression level */
|
|
346 int dummy; /* ? : 0x00 */
|
|
347 public:
|
|
348 int blocks; /* block count */
|
|
349 int datasize; /* all data size */
|
|
350 private:
|
|
351 int compdatasize; /* compressed data size */
|
|
352 int samplecount; /* all samples */
|
|
353 int blocksize; /* samples per block */
|
|
354 int restsize; /* samples of the last block */
|
|
355 int dummy2; /* ? : 0x89 */
|
|
356 int curblock;
|
|
357 int* offsets;
|
|
358 int offset_start;
|
|
359 int filesize;
|
|
360 char* tmpdata;
|
|
361 public:
|
|
362 void ReadHeader(FILE* in, int file_size=-1);
|
|
363 int CheckHeader(void); /* false: invalid true: valid */
|
|
364 NWAData(void) {
|
|
365 offsets = 0;
|
|
366 tmpdata = 0;
|
|
367 }
|
|
368 ~NWAData(void) {
|
|
369 if (offsets) delete[] offsets;
|
|
370 if (tmpdata) delete[] tmpdata;
|
|
371 }
|
|
372 int BlockLength(void) {
|
|
373 if (complevel != -1) {
|
|
374 if (offsets == 0) return false;
|
|
375 if (tmpdata == 0) return false;
|
|
376 }
|
|
377 return blocksize * (bps/8);
|
|
378 }
|
|
379 /* data は BlockLength 以上の長さを持つこと
|
|
380 ** 返り値は作成したデータの長さ。終了時は 0。
|
|
381 ** エラー時は -1
|
|
382 */
|
|
383 int Decode(FILE* in, char* data, int& skip_count);
|
|
384 void Rewind(FILE* in);
|
|
385 };
|
|
386
|
|
387 void NWAData::ReadHeader(FILE* in, int _file_size) {
|
|
388 char header[0x2c];
|
|
389 struct stat sb;
|
|
390 int i;
|
|
391 if (offsets) delete[] offsets;
|
|
392 if (tmpdata) delete[] tmpdata;
|
|
393 offsets = 0;
|
|
394 tmpdata = 0;
|
|
395 filesize = 0;
|
|
396 offset_start = ftell(in);
|
|
397 if (offset_start == -1) offset_start = 0;
|
|
398 if (_file_size != -1) filesize = _file_size;
|
|
399 curblock = -1;
|
|
400 /* header 読み込み */
|
|
401 if (in == 0 || feof(in) || ferror(in)) {
|
|
402 fprintf(stderr,"invalid stream\n");
|
|
403 return;
|
|
404 }
|
|
405 fread(header, 0x2c, 1, in);
|
|
406 if (feof(in) || ferror(in)) {
|
|
407 fprintf(stderr,"invalid stream\n");
|
|
408 return;
|
|
409 }
|
|
410 channels = read_little_endian_short(header+0x00);
|
|
411 bps = read_little_endian_short(header+0x02);
|
|
412 freq = read_little_endian_int(header+0x04);
|
|
413 complevel = read_little_endian_int(header+0x08);
|
|
414 dummy = read_little_endian_int(header+0x0c);
|
|
415 blocks = read_little_endian_int(header+0x10);
|
|
416 datasize = read_little_endian_int(header+0x14);
|
|
417 compdatasize = read_little_endian_int(header+0x18);
|
|
418 samplecount = read_little_endian_int(header+0x1c);
|
|
419 blocksize = read_little_endian_int(header+0x20);
|
|
420 restsize = read_little_endian_int(header+0x24);
|
|
421 dummy2 = read_little_endian_int(header+0x28);
|
|
422 if (complevel == -1) { /* 無圧縮rawデータ */
|
|
423 /* 適当に決め打ちする */
|
|
424 blocksize = 65536;
|
|
425 restsize = (datasize % (blocksize * (bps/8))) / (bps/8);
|
|
426 blocks = datasize / (blocksize * (bps/8)) + (restsize > 0 ? 1 : 0);
|
|
427 }
|
|
428 if (blocks <= 0 || blocks > 1000000) {
|
|
429 /* 1時間を超える曲ってのはないでしょ*/
|
|
430 fprintf(stderr,"too large blocks : %d\n",blocks);
|
|
431 return;
|
|
432 }
|
|
433 /* regular file なら filesize 読み込み */
|
|
434 if (filesize == 0 && fstat(fileno(in), &sb)==0 && (sb.st_mode&S_IFMT) == S_IFREG) {
|
|
435 int pos = ftell(in);
|
|
436 fseek(in, 0, 2);
|
|
437 filesize = ftell(in);
|
|
438 fseek(in, pos, 0);
|
|
439 if (pos+blocks*4 >= filesize) {
|
|
440 fprintf(stderr,"offset block is not exist\n");
|
|
441 return;
|
|
442 }
|
|
443 }
|
|
444 if (complevel == -1) return;
|
|
445 /* offset index 読み込み */
|
|
446 offsets = new int[blocks];
|
|
447 fread(offsets, blocks, 4, in);
|
|
448 for (i=0; i<blocks; i++) {
|
|
449 offsets[i] = read_little_endian_int((char*)(offsets+i));
|
|
450 }
|
|
451 if (feof(in) || ferror(in)) {
|
|
452 fprintf(stderr,"invalid stream\n");
|
|
453 delete[] offsets;
|
|
454 offsets = 0;
|
|
455 return;
|
|
456 }
|
|
457 return;
|
|
458 }
|
|
459 void NWAData::Rewind(FILE* in) {
|
|
460 curblock = -1;
|
|
461 fseek(in, 0x2c, 0);
|
|
462 if (offsets) fseek(in, blocks*4, 1);
|
|
463 }
|
|
464 int NWAData::CheckHeader(void) {
|
|
465 if (complevel != -1 && offsets == 0) return false;
|
|
466 /* データそのもののチェック */
|
|
467 if (channels != 1 && channels != 2) {
|
|
468 fprintf(stderr,"This program only supports mono / stereo data : data have %d channels.\n",channels);
|
|
469 return false;
|
|
470 }
|
|
471 if (bps != 8 && bps != 16) {
|
|
472 fprintf(stderr,"This program only supports 8 / 16bit data : data is %d bits\n",bps);
|
|
473 return false;
|
|
474 }
|
|
475 if (complevel == -1) {
|
|
476 int byps = bps/8; /* bytes per sample */
|
|
477 if (datasize != samplecount*byps) {
|
|
478 fprintf(stderr,"invalid datasize : datasize %d != samplecount %d * samplesize %d\n",datasize,samplecount,byps);
|
|
479 return false;
|
|
480 }
|
|
481 if (samplecount != (blocks-1)*blocksize+restsize ) {
|
|
482 fprintf(stderr,"total sample count is invalid : samplecount %d != %d*%d+%d(block*blocksize+lastblocksize).\n",samplecount,blocks-1,blocksize,restsize);
|
|
483 return false;
|
|
484 }
|
|
485 else
|
|
486 return true;
|
|
487 }
|
|
488 //if (complevel < 0 || complevel > 2) {
|
|
489 if (complevel < 0 || complevel > 5) {
|
|
490 fprintf(stderr,"This program only supports -1,0,1,2 compression level : the level of data is %d\n",complevel);
|
|
491 return false;
|
|
492 }
|
|
493 /* 整合性チェック */
|
|
494 if (filesize != 0 && filesize != compdatasize) {
|
|
495 fprintf(stderr,"file size is invalid : %d != %d\n",filesize,compdatasize);
|
|
496 return false;
|
|
497 }
|
|
498 if (offsets[blocks-1] >= compdatasize) {
|
|
499 fprintf(stderr,"the last offset overruns the file.\n");
|
|
500 return false;
|
|
501 }
|
|
502 int byps = bps/8; /* bytes per sample */
|
|
503 if (datasize != samplecount*byps) {
|
|
504 fprintf(stderr,"invalid datasize : datasize %d != samplecount %d * samplesize %d\n",datasize,samplecount,byps);
|
|
505 return false;
|
|
506 }
|
|
507 if (samplecount != (blocks-1)*blocksize+restsize ) {
|
|
508 fprintf(stderr,"total sample count is invalid : samplecount %d != %d*%d+%d(block*blocksize+lastblocksize).\n",samplecount,blocks-1,blocksize,restsize);
|
|
509 return false;
|
|
510 }
|
|
511 tmpdata = new char[blocksize*byps*2]; /* これ以上の大きさはないだろう、、、 */
|
|
512 return true;
|
|
513 }
|
|
514
|
|
515 class NWAInfo_sw2 {
|
|
516 public:
|
|
517 int Channels(void) const{return 2;}
|
|
518 int Bps(void) const { return 16;}
|
|
519 int CompLevel(void) const { return 2;}
|
|
520 int UseRunLength(void) const { return false; }
|
|
521 };
|
|
522 int NWAData::Decode(FILE* in, char* data, int& skip_count) {
|
|
523 if (complevel == -1) { /* 無圧縮時の処理 */
|
|
524 if (feof(in) || ferror(in)) return -1;
|
|
525 if (curblock == -1) {
|
|
526 /* 最初のブロックなら、wave header 出力 */
|
|
527 memcpy(data, make_wavheader(datasize, channels, bps, freq), 0x2c);
|
|
528 curblock++;
|
|
529 fseek(in, offset_start + 0x2c, SEEK_SET);
|
|
530 return 0x2c;
|
|
531 }
|
|
532 if (skip_count > blocksize/channels) {
|
|
533 skip_count -= blocksize/channels;
|
|
534 fseek(in, blocksize*(bps/8), SEEK_CUR);
|
|
535 curblock++;
|
|
536 return -2;
|
|
537 }
|
|
538 if (curblock < blocks) {
|
|
539 int readsize = blocksize;
|
|
540 if (skip_count) {
|
|
541 fseek(in, skip_count*channels*(bps/8), SEEK_CUR);
|
|
542 readsize -= skip_count * channels;
|
|
543 skip_count = 0;
|
|
544 }
|
|
545 int err = fread(data, 1, readsize * (bps/8), in);
|
|
546 curblock++;
|
|
547 return err;
|
|
548 }
|
|
549 return -1;
|
|
550 }
|
|
551 if (offsets == 0 || tmpdata == 0) return -1;
|
|
552 if (blocks == curblock) return 0;
|
|
553 if (feof(in) || ferror(in)) return -1;
|
|
554 if (curblock == -1) {
|
|
555 /* 最初のブロックなら、wave header 出力 */
|
|
556 memcpy(data, make_wavheader(datasize, channels, bps, freq), 0x2c);
|
|
557 curblock++;
|
|
558 return 0x2c;
|
|
559 }
|
|
560 /* 今回読み込む/デコードするデータの大きさを得る */
|
|
561 int curblocksize, curcompsize;
|
|
562 if (curblock != blocks-1) {
|
|
563 curblocksize = blocksize * (bps/8);
|
|
564 curcompsize = offsets[curblock+1] - offsets[curblock];
|
|
565 if (curblocksize >= blocksize*(bps/8)*2) return -1; // Fatal error
|
|
566 } else {
|
|
567 curblocksize = restsize * (bps/8);
|
|
568 curcompsize = blocksize*(bps/8)*2;
|
|
569 }
|
|
570 if (skip_count > blocksize/channels) {
|
|
571 skip_count -= blocksize/channels;
|
|
572 fseek(in, curcompsize, SEEK_CUR);
|
|
573 curblock++;
|
|
574 return -2;
|
|
575 }
|
|
576 /* データ読み込み */
|
|
577 fread(tmpdata, 1, curcompsize, in);
|
|
578 /* 展開 */
|
|
579 if (channels == 2 && bps == 16 && complevel == 2) {
|
|
580 NWAInfo_sw2 info;
|
|
581 NWADecode(info, tmpdata, data, curcompsize, curblocksize);
|
|
582 } else {
|
|
583 NWAInfo info(channels, bps, complevel);
|
|
584 NWADecode(info, tmpdata, data, curcompsize, curblocksize);
|
|
585 }
|
|
586 int retsize = curblocksize;
|
|
587 if (skip_count) {
|
|
588 int skip_c = skip_count * channels * (bps/8);
|
|
589 retsize -= skip_c;
|
|
590 memmove(data, data+skip_c, skip_c);
|
|
591 skip_count = 0;
|
|
592 }
|
|
593 curblock++;
|
|
594 return retsize;
|
|
595 }
|
|
596
|
|
597 #ifdef USE_MAIN
|
|
598
|
|
599 void conv(FILE* in, FILE* out, int skip_count, int in_size = -1) {
|
|
600 NWAData h;
|
|
601 h.ReadHeader(in, in_size);
|
|
602 h.CheckHeader();
|
|
603 int bs = h.BlockLength();
|
|
604 char* d = new char[bs];
|
|
605 int err;
|
|
606 while( (err=h.Decode(in, d, skip_count)) != 0) {
|
|
607 if (err == -1) break;
|
|
608 if (err == -2) continue;
|
|
609 fwrite(d, err, 1, out);
|
|
610 }
|
|
611 return;
|
|
612 }
|
|
613 int main(int argc, char** argv) {
|
|
614 int skip_count = 0;
|
|
615
|
|
616 if (argc > 2 && strcmp(argv[1], "--skip") == 0) {
|
|
617 skip_count = atoi(argv[2]);
|
|
618 argc -= 2;
|
|
619 argv[1] = argv[3];
|
|
620 argv[2] = argv[4];
|
|
621 }
|
|
622 if (argc != 2 && argc != 3) {
|
|
623 fprintf(stderr,"usage : nwatowav [inputfile [outputfile]]\n");
|
|
624 return -1;
|
|
625 }
|
|
626 if (strstr(argv[1], ".nwk") != 0 || strstr(argv[1], ".ovk") != 0) {
|
|
627 bool is_ovk;
|
|
628 int headblk_sz;
|
|
629 char* out_ext;
|
|
630
|
|
631 char* outpath = new char[strlen(argv[1])+10];
|
|
632 char buf[1024];
|
|
633 memset(buf, 0, 1024);
|
|
634 FILE* in = fopen(argv[1], "rb");
|
|
635 if (in == 0) {
|
|
636 fprintf(stderr,"Cannot open file : %s\n",argv[1]);
|
|
637 return -1;
|
|
638 }
|
|
639 if (strstr(argv[1], ".ovk") != 0) {
|
|
640 is_ovk = true;
|
|
641 headblk_sz = 16;
|
|
642 out_ext = "ogg";
|
|
643 } else {
|
|
644 is_ovk = false;
|
|
645 headblk_sz = 12;
|
|
646 out_ext = "wav";
|
|
647 }
|
|
648 fread(buf, 1, 4, in);
|
|
649 int index = read_little_endian_int(buf);
|
|
650 if (index <= 0) {
|
|
651 if (is_ovk)
|
|
652 fprintf(stderr,"Invalid Ogg-ovk file : %s : index = %d\n",argv[1],index);
|
|
653 else
|
|
654 fprintf(stderr,"Invalid Koe-nwk file : %s : index = %d\n",argv[1],index);
|
|
655 return -1;
|
|
656 }
|
|
657 int* tbl_off = new int[index];
|
|
658 int* tbl_siz = new int[index];
|
|
659 int* tbl_cnt = new int[index];
|
|
660 int* tbl_origsiz = new int[index];
|
|
661 int i;
|
|
662 for (i=0; i<index; i++) {
|
|
663 fread(buf, 1, headblk_sz, in);
|
|
664 tbl_siz[i] = read_little_endian_int(buf);
|
|
665 tbl_off[i] = read_little_endian_int(buf+4);
|
|
666 tbl_cnt[i] = read_little_endian_int(buf+8);
|
|
667 tbl_origsiz[i] = read_little_endian_int(buf+12);
|
|
668 }
|
|
669 fseek(in, 0, 2);
|
|
670 int fsize = ftell(in);
|
|
671 for (i=0; i<index; i++) {
|
|
672 if (tbl_off[i] <= 0 || tbl_siz[i] <= 0 || tbl_off[i]+tbl_siz[i] > fsize) {
|
|
673 fprintf(stderr,"Invalid table[%d] : cnt %d off %d size %d / %d\n",i,tbl_cnt[i],tbl_off[i],tbl_siz[i],fsize);
|
|
674 continue;
|
|
675 }
|
|
676 if (argc == 2)
|
|
677 sprintf(outpath, "%s-%d.%s", argv[1], tbl_cnt[i],out_ext);
|
|
678 else
|
|
679 sprintf(outpath, "%s-%d.%s", argv[2], tbl_cnt[i],out_ext);
|
|
680 FILE* out = fopen(outpath, "wb");
|
|
681 if (out == 0) {
|
|
682 fprintf(stderr,"Cannot open output file %s\n",outpath);
|
|
683 continue;
|
|
684 }
|
|
685 fprintf(stderr,"Writing file %s...\n",outpath);
|
|
686 fseek(in, tbl_off[i], 0);
|
|
687 if (is_ovk) { // copy file
|
|
688 int sz = tbl_siz[i];
|
|
689 char buf[32*1024];
|
|
690 while(sz > 32*1024) {
|
|
691 fread(buf, 32*1024, 1, in);
|
|
692 fwrite(buf, 32*1024, 1, out);
|
|
693 sz -= 1024*32;
|
|
694 }
|
|
695 if (sz > 0) {
|
|
696 fread(buf, sz, 1, in);
|
|
697 fwrite(buf, sz, 1, out);
|
|
698 }
|
|
699 } else { // .nwk
|
|
700 conv(in, out, 0, tbl_siz[i]);
|
|
701 }
|
|
702 fclose(out);
|
|
703 }
|
|
704 fclose(in);
|
|
705 return 0;
|
|
706 }
|
|
707 FILE* in = fopen(argv[1],"rb");
|
|
708 if (in == 0) {
|
|
709 fprintf(stderr,"Cannot open file : %s\n",argv[1]);
|
|
710 return -1;
|
|
711 }
|
|
712 FILE* out;
|
|
713 if (argc != 3 && (!isatty(fileno(stdout)))) { // wave file is written to stdout if stdout is redirected to a file
|
|
714 out = stdout;
|
|
715 } else { // make a new file or use argv[2] for output file name
|
|
716 char* outpath = new char[strlen(argv[1])+10];
|
|
717 sprintf(outpath, "%s.wav",argv[1]);
|
|
718 if (argc == 3) outpath = argv[2];
|
|
719 out = fopen(outpath, "wb");
|
|
720 if (out == 0) {
|
|
721 fprintf(stderr,"Cannot open file : %s\n",outpath);
|
|
722 return -1;
|
|
723 }
|
|
724 }
|
|
725 conv(in, out, skip_count);
|
|
726 fclose(in);
|
|
727 if (out != stdout) fclose(out);
|
|
728 return 0;
|
|
729 }
|
|
730 #else
|
|
731
|
|
732 #include"wavfile.h"
|
|
733
|
|
734 void NWAFILE::Seek(int count) {
|
|
735 if (data == 0) data = new char[block_size];
|
|
736 nwa->Rewind(stream);
|
|
737 int dmy = 0;
|
|
738 nwa->Decode(stream, data, dmy); // skip wav header
|
|
739 data_len = 0;
|
|
740 skip_count = count;
|
|
741 }
|
|
742 NWAFILE::NWAFILE(FILE* _stream) {
|
|
743 skip_count = 0;
|
|
744 data = 0;
|
|
745 stream = _stream;
|
|
746 nwa = new NWAData;
|
|
747 nwa->ReadHeader(stream);
|
|
748 if (!nwa->CheckHeader()) {
|
|
749 return;
|
|
750 }
|
|
751 block_size = nwa->BlockLength();
|
|
752 data = new char[block_size];
|
|
753 data_len = 0;
|
|
754
|
|
755 wavinfo.SamplingRate = nwa->freq;
|
|
756 wavinfo.Channels = nwa->channels;
|
|
757 wavinfo.DataBits = nwa->bps;
|
|
758
|
|
759 int dmy = 0;
|
|
760 data_len = nwa->Decode(stream, data, dmy); // skip wav header
|
|
761
|
|
762 return;
|
|
763 }
|
|
764 NWAFILE::~NWAFILE() {
|
|
765 if (stream) fclose(stream);
|
|
766 if (data) delete[] data;
|
|
767 if (nwa) delete nwa;
|
|
768 }
|
|
769 int NWAFILE::Read(char* buf, int blksize, int blklen) {
|
|
770 if (data == 0) return -1; // end of file
|
|
771
|
|
772 if (data_len > blksize * blklen) {
|
|
773 int len = blksize * blklen;
|
|
774 memcpy(buf, data, len);
|
|
775 memmove(data, data+len, data_len-len);
|
|
776 data_len -= len;
|
|
777 return blklen;
|
|
778 }
|
|
779 memcpy(buf, data, data_len);
|
|
780 int copied_length = data_len;
|
|
781 data_len = 0;
|
|
782
|
|
783 if (stream == 0) {
|
|
784 delete[] data;
|
|
785 data = 0;
|
|
786 return copied_length / blksize;
|
|
787 }
|
|
788
|
|
789 // read
|
|
790 do {
|
|
791 int err;
|
|
792 retry:
|
|
793 err = nwa->Decode(stream, data, skip_count);
|
|
794 if (err == 0 || err == -1) { // eof or error
|
|
795 delete[] data;
|
|
796 data = 0;
|
|
797 return copied_length / blksize;
|
|
798 }
|
|
799 if (err == -2) goto retry; // EAGAIN
|
|
800 data_len = err;
|
|
801 if (copied_length + data_len < blklen*blksize) {
|
|
802 memcpy(buf+copied_length, data, data_len);
|
|
803 copied_length += data_len;
|
|
804 goto retry;
|
|
805 }
|
|
806 } while(0);
|
|
807
|
|
808 // determine return length
|
|
809 int datablks = (data_len+copied_length)/blksize;
|
|
810 if (datablks <= 0) return 0;
|
|
811 if (datablks > blklen) datablks = blklen;
|
|
812 int rest_len = datablks * blksize - copied_length;
|
|
813 if (rest_len) {
|
|
814 memcpy(buf+copied_length, data, rest_len);
|
|
815 memmove(data, data+rest_len, data_len-rest_len);
|
|
816 data_len -= rest_len;
|
|
817 }
|
|
818 return datablks;
|
|
819 }
|
|
820
|
|
821 char* NWAFILE::ReadAll(FILE* in, int& total_size) {
|
|
822 NWAData h;
|
|
823 if (in == 0) return 0;
|
|
824 h.ReadHeader(in);
|
|
825 h.CheckHeader();
|
|
826 int bs = h.BlockLength();
|
|
827 total_size = h.datasize+0x2c;
|
|
828 char* d = new char[total_size + bs*2];
|
|
829 int dcur = 0;
|
|
830 int err;
|
|
831 int skip = 0;
|
|
832 while(dcur < total_size+bs && (err=h.Decode(in, d+dcur, skip)) != 0) {
|
|
833 if (err == -1) break;
|
|
834 if (err == -2) continue;
|
|
835 dcur += err;
|
|
836 }
|
|
837 return d;
|
|
838 }
|
|
839
|
|
840 #include"music.h"
|
|
841
|
|
842 char* decode_koe_nwa(AvgKoeInfo info, int* data_len) {
|
|
843 NWAData h;
|
|
844 if (info.stream == 0) return 0;
|
|
845 fseek(info.stream, info.offset, 0);
|
|
846 h.ReadHeader(info.stream, info.length);
|
|
847 if (h.CheckHeader() == false) return 0;
|
|
848 int bs = h.BlockLength();
|
|
849 int total = h.datasize + 0x2c;
|
|
850 char* d = new char[total + bs*2];
|
|
851 int dcur = 0;
|
|
852 int err;
|
|
853 int skip = 0;
|
|
854 while(dcur < total+bs && (err=h.Decode(info.stream, d+dcur, skip)) != 0) {
|
|
855 if (err == -1) break;
|
|
856 if (err == -2) continue;
|
|
857 dcur += err;
|
|
858 }
|
|
859 if (data_len) {
|
|
860 *data_len = dcur;
|
|
861 if (*data_len > total) *data_len = total;
|
|
862 }
|
|
863 return d;
|
|
864 }
|
|
865
|
|
866 #endif
|