Mercurial > otakunoraifu
annotate music2/nwatowav.cc @ 54:d7cde171a1de
* scn2k_grp.cc now handles commands in a cleanier way \o/
* some cleaning
author | thib |
---|---|
date | Mon, 20 Apr 2009 16:18:55 +0000 |
parents | 15a18fbe6f21 |
children | 4416cfac86ae |
rev | line source |
---|---|
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 | |
52 | 166 #include <stdio.h> |
167 #include <stdlib.h> | |
168 #include <unistd.h> // for isatty() function | |
169 #include <sys/stat.h> | |
170 #include <string.h> | |
0 | 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 { | |
52 | 232 private: |
233 int channels; | |
234 int bps; | |
235 int complevel; | |
236 bool use_runlength; | |
237 public: | |
238 NWAInfo(int c,int b,int cl) { | |
239 channels=c; | |
240 bps=b; | |
241 complevel=cl; | |
242 use_runlength = false; | |
243 if (cl == 5) { | |
244 use_runlength = true; // Tomoyo After (.nwk koe file) | |
245 if (channels == 2) use_runlength = false; // BGM*.nwa in Little Busters! | |
246 } | |
0 | 247 } |
52 | 248 int Channels(void) const{return channels;} |
249 int Bps(void) const { return bps;} | |
250 int CompLevel(void) const { return complevel;} | |
251 int UseRunLength(void) const { return use_runlength; } | |
0 | 252 }; |
253 | |
254 template<class NWAI> void NWADecode(const NWAI& info,const char* data, char* outdata, int datasize, int outdatasize) { | |
255 int d[2]; | |
43
01aa5ddf7dc8
A lot of very minor improvements (deleted some unused variables, and other things like that...)
thib
parents:
0
diff
changeset
|
256 int i; |
0 | 257 int shift = 0; |
258 const char* dataend = data+datasize; | |
259 /* 最初のデータを読み込む */ | |
260 if (info.Bps() == 8) {d[0] = *data++; datasize--;} | |
261 else /* info.Bps() == 16 */ {d[0] = read_little_endian_short(data); data+=2; datasize-=2;} | |
262 if (info.Channels() == 2) { | |
263 if (info.Bps() == 8) {d[1] = *data++; datasize--;} | |
264 else /* info.Bps() == 16 */ {d[1] = read_little_endian_short(data); data+=2; datasize-=2;} | |
265 } | |
266 int dsize = outdatasize / (info.Bps()/8); | |
267 int flip_flag = 0; /* stereo 用 */ | |
268 int runlength = 0; | |
269 for (i=0; i<dsize; i++) { | |
270 if (data >= dataend) break; | |
271 if (runlength == 0) { // コピーループ中でないならデータ読み込み | |
272 int type = getbits(data, shift, 3); | |
273 /* type により分岐:0, 1-6, 7 */ | |
274 if (type == 7) { | |
275 /* 7 : 大きな差分 */ | |
276 /* RunLength() 有効時(CompLevel==5, 音声ファイル) では無効 */ | |
277 if (getbits(data, shift, 1) == 1) { | |
278 d[flip_flag] = 0; /* 未使用 */ | |
279 } else { | |
280 int BITS, SHIFT; | |
281 if (info.CompLevel() >= 3) { | |
282 BITS = 8; | |
283 SHIFT = 9; | |
284 } else { | |
285 BITS = 8-info.CompLevel(); | |
286 SHIFT = 2+7+info.CompLevel(); | |
287 } | |
288 const int MASK1 = (1<<(BITS-1)); | |
289 const int MASK2 = (1<<(BITS-1))-1; | |
290 int b = getbits(data, shift, BITS); | |
291 if (b&MASK1) | |
292 d[flip_flag] -= (b&MASK2)<<SHIFT; | |
293 else | |
294 d[flip_flag] += (b&MASK2)<<SHIFT; | |
295 } | |
296 } else if (type != 0) { | |
297 /* 1-6 : 通常の差分 */ | |
298 int BITS, SHIFT; | |
299 if (info.CompLevel() >= 3) { | |
300 BITS = info.CompLevel()+3; | |
301 SHIFT = 1+type; | |
302 } else { | |
303 BITS = 5-info.CompLevel(); | |
304 SHIFT = 2+type+info.CompLevel(); | |
305 } | |
306 const int MASK1 = (1<<(BITS-1)); | |
307 const int MASK2 = (1<<(BITS-1))-1; | |
308 int b = getbits(data, shift, BITS); | |
309 if (b&MASK1) | |
310 d[flip_flag] -= (b&MASK2)<<SHIFT; | |
311 else | |
312 d[flip_flag] += (b&MASK2)<<SHIFT; | |
313 } else { /* type == 0 */ | |
314 /* ランレングス圧縮なしの場合はなにもしない */ | |
315 if (info.UseRunLength() == true) { | |
316 /* ランレングス圧縮ありの場合 */ | |
317 runlength = getbits(data,shift,1); | |
318 if (runlength==1) { | |
319 runlength = getbits(data,shift,2); | |
320 if (runlength == 3) { | |
321 runlength = getbits(data, shift, 8); | |
322 } | |
323 } | |
324 } | |
325 } | |
326 } else { | |
327 runlength--; | |
328 } | |
329 if (info.Bps() == 8) { | |
330 *outdata++ = d[flip_flag]; | |
331 } else { | |
332 write_little_endian_short(outdata, d[flip_flag]); | |
333 outdata += 2; | |
334 } | |
335 if (info.Channels() == 2) flip_flag ^= 1; /* channel 切り替え */ | |
336 } | |
337 return; | |
52 | 338 } |
0 | 339 |
340 class NWAData { | |
52 | 341 public: |
342 int channels; | |
343 int bps; /* bits per sample */ | |
344 int freq; /* samples per second */ | |
345 private: | |
346 int complevel; /* compression level */ | |
347 int dummy; /* ? : 0x00 */ | |
348 public: | |
349 int blocks; /* block count */ | |
350 int datasize; /* all data size */ | |
351 private: | |
352 int compdatasize; /* compressed data size */ | |
353 int samplecount; /* all samples */ | |
354 int blocksize; /* samples per block */ | |
355 int restsize; /* samples of the last block */ | |
356 int dummy2; /* ? : 0x89 */ | |
357 int curblock; | |
358 int* offsets; | |
359 int offset_start; | |
360 int filesize; | |
361 char* tmpdata; | |
362 public: | |
363 void ReadHeader(FILE* in, int file_size=-1); | |
364 int CheckHeader(void); /* false: invalid true: valid */ | |
365 NWAData(void) { | |
366 offsets = NULL; | |
367 tmpdata = NULL; | |
0 | 368 } |
52 | 369 ~NWAData(void) { |
370 if (offsets) delete[] offsets; | |
371 if (tmpdata) delete[] tmpdata; | |
372 } | |
373 int BlockLength(void) { | |
374 if (complevel != -1) { | |
375 if (offsets == NULL) return false; | |
376 if (tmpdata == NULL) return false; | |
377 } | |
378 return blocksize * (bps/8); | |
379 } | |
380 /* data は BlockLength 以上の長さを持つこと | |
381 ** 返り値は作成したデータの長さ。終了時は 0。 | |
382 ** エラー時は -1 | |
383 */ | |
384 int Decode(FILE* in, char* data, int& skip_count); | |
385 void Rewind(FILE* in); | |
0 | 386 }; |
387 | |
388 void NWAData::ReadHeader(FILE* in, int _file_size) { | |
389 char header[0x2c]; | |
390 struct stat sb; | |
391 int i; | |
392 if (offsets) delete[] offsets; | |
393 if (tmpdata) delete[] tmpdata; | |
52 | 394 offsets = NULL; |
395 tmpdata = NULL; | |
0 | 396 filesize = 0; |
397 offset_start = ftell(in); | |
398 if (offset_start == -1) offset_start = 0; | |
399 if (_file_size != -1) filesize = _file_size; | |
400 curblock = -1; | |
401 /* header 読み込み */ | |
52 | 402 if (in == NULL || feof(in) || ferror(in)) { |
0 | 403 fprintf(stderr,"invalid stream\n"); |
404 return; | |
405 } | |
406 fread(header, 0x2c, 1, in); | |
407 if (feof(in) || ferror(in)) { | |
408 fprintf(stderr,"invalid stream\n"); | |
409 return; | |
410 } | |
411 channels = read_little_endian_short(header+0x00); | |
412 bps = read_little_endian_short(header+0x02); | |
413 freq = read_little_endian_int(header+0x04); | |
414 complevel = read_little_endian_int(header+0x08); | |
415 dummy = read_little_endian_int(header+0x0c); | |
416 blocks = read_little_endian_int(header+0x10); | |
417 datasize = read_little_endian_int(header+0x14); | |
418 compdatasize = read_little_endian_int(header+0x18); | |
419 samplecount = read_little_endian_int(header+0x1c); | |
420 blocksize = read_little_endian_int(header+0x20); | |
421 restsize = read_little_endian_int(header+0x24); | |
422 dummy2 = read_little_endian_int(header+0x28); | |
423 if (complevel == -1) { /* 無圧縮rawデータ */ | |
424 /* 適当に決め打ちする */ | |
425 blocksize = 65536; | |
426 restsize = (datasize % (blocksize * (bps/8))) / (bps/8); | |
427 blocks = datasize / (blocksize * (bps/8)) + (restsize > 0 ? 1 : 0); | |
428 } | |
429 if (blocks <= 0 || blocks > 1000000) { | |
430 /* 1時間を超える曲ってのはないでしょ*/ | |
431 fprintf(stderr,"too large blocks : %d\n",blocks); | |
432 return; | |
433 } | |
434 /* regular file なら filesize 読み込み */ | |
435 if (filesize == 0 && fstat(fileno(in), &sb)==0 && (sb.st_mode&S_IFMT) == S_IFREG) { | |
436 int pos = ftell(in); | |
52 | 437 fseek(in, 0, SEEK_END); |
0 | 438 filesize = ftell(in); |
52 | 439 fseek(in, pos, SEEK_SET); |
0 | 440 if (pos+blocks*4 >= filesize) { |
441 fprintf(stderr,"offset block is not exist\n"); | |
442 return; | |
443 } | |
444 } | |
445 if (complevel == -1) return; | |
446 /* offset index 読み込み */ | |
447 offsets = new int[blocks]; | |
448 fread(offsets, blocks, 4, in); | |
449 for (i=0; i<blocks; i++) { | |
450 offsets[i] = read_little_endian_int((char*)(offsets+i)); | |
451 } | |
452 if (feof(in) || ferror(in)) { | |
453 fprintf(stderr,"invalid stream\n"); | |
454 delete[] offsets; | |
52 | 455 offsets = NULL; |
0 | 456 return; |
457 } | |
458 } | |
52 | 459 |
0 | 460 void NWAData::Rewind(FILE* in) { |
461 curblock = -1; | |
52 | 462 fseek(in, 0x2c, SEEK_SET); |
463 if (offsets) fseek(in, blocks*4, SEEK_CUR); | |
0 | 464 } |
52 | 465 |
0 | 466 int NWAData::CheckHeader(void) { |
52 | 467 if (complevel != -1 && offsets == NULL) return false; |
0 | 468 /* データそのもののチェック */ |
469 if (channels != 1 && channels != 2) { | |
470 fprintf(stderr,"This program only supports mono / stereo data : data have %d channels.\n",channels); | |
471 return false; | |
472 } | |
473 if (bps != 8 && bps != 16) { | |
474 fprintf(stderr,"This program only supports 8 / 16bit data : data is %d bits\n",bps); | |
475 return false; | |
476 } | |
477 if (complevel == -1) { | |
478 int byps = bps/8; /* bytes per sample */ | |
479 if (datasize != samplecount*byps) { | |
480 fprintf(stderr,"invalid datasize : datasize %d != samplecount %d * samplesize %d\n",datasize,samplecount,byps); | |
481 return false; | |
482 } | |
483 if (samplecount != (blocks-1)*blocksize+restsize ) { | |
484 fprintf(stderr,"total sample count is invalid : samplecount %d != %d*%d+%d(block*blocksize+lastblocksize).\n",samplecount,blocks-1,blocksize,restsize); | |
485 return false; | |
486 } | |
487 else | |
488 return true; | |
489 } | |
490 //if (complevel < 0 || complevel > 2) { | |
491 if (complevel < 0 || complevel > 5) { | |
492 fprintf(stderr,"This program only supports -1,0,1,2 compression level : the level of data is %d\n",complevel); | |
493 return false; | |
494 } | |
495 /* 整合性チェック */ | |
496 if (filesize != 0 && filesize != compdatasize) { | |
497 fprintf(stderr,"file size is invalid : %d != %d\n",filesize,compdatasize); | |
498 return false; | |
499 } | |
500 if (offsets[blocks-1] >= compdatasize) { | |
501 fprintf(stderr,"the last offset overruns the file.\n"); | |
502 return false; | |
503 } | |
504 int byps = bps/8; /* bytes per sample */ | |
505 if (datasize != samplecount*byps) { | |
506 fprintf(stderr,"invalid datasize : datasize %d != samplecount %d * samplesize %d\n",datasize,samplecount,byps); | |
507 return false; | |
508 } | |
509 if (samplecount != (blocks-1)*blocksize+restsize ) { | |
510 fprintf(stderr,"total sample count is invalid : samplecount %d != %d*%d+%d(block*blocksize+lastblocksize).\n",samplecount,blocks-1,blocksize,restsize); | |
511 return false; | |
512 } | |
513 tmpdata = new char[blocksize*byps*2]; /* これ以上の大きさはないだろう、、、 */ | |
514 return true; | |
515 } | |
516 | |
517 class NWAInfo_sw2 { | |
52 | 518 public: |
519 int Channels(void) const{return 2;} | |
520 int Bps(void) const { return 16;} | |
521 int CompLevel(void) const { return 2;} | |
522 int UseRunLength(void) const { return false; } | |
0 | 523 }; |
52 | 524 |
0 | 525 int NWAData::Decode(FILE* in, char* data, int& skip_count) { |
526 if (complevel == -1) { /* 無圧縮時の処理 */ | |
527 if (feof(in) || ferror(in)) return -1; | |
528 if (curblock == -1) { | |
529 /* 最初のブロックなら、wave header 出力 */ | |
530 memcpy(data, make_wavheader(datasize, channels, bps, freq), 0x2c); | |
531 curblock++; | |
532 fseek(in, offset_start + 0x2c, SEEK_SET); | |
533 return 0x2c; | |
534 } | |
535 if (skip_count > blocksize/channels) { | |
536 skip_count -= blocksize/channels; | |
537 fseek(in, blocksize*(bps/8), SEEK_CUR); | |
538 curblock++; | |
539 return -2; | |
540 } | |
541 if (curblock < blocks) { | |
542 int readsize = blocksize; | |
543 if (skip_count) { | |
544 fseek(in, skip_count*channels*(bps/8), SEEK_CUR); | |
545 readsize -= skip_count * channels; | |
546 skip_count = 0; | |
547 } | |
548 int err = fread(data, 1, readsize * (bps/8), in); | |
549 curblock++; | |
550 return err; | |
551 } | |
552 return -1; | |
553 } | |
52 | 554 if (offsets == NULL || tmpdata == NULL) return -1; |
0 | 555 if (blocks == curblock) return 0; |
556 if (feof(in) || ferror(in)) return -1; | |
557 if (curblock == -1) { | |
558 /* 最初のブロックなら、wave header 出力 */ | |
559 memcpy(data, make_wavheader(datasize, channels, bps, freq), 0x2c); | |
560 curblock++; | |
561 return 0x2c; | |
562 } | |
563 /* 今回読み込む/デコードするデータの大きさを得る */ | |
564 int curblocksize, curcompsize; | |
565 if (curblock != blocks-1) { | |
566 curblocksize = blocksize * (bps/8); | |
567 curcompsize = offsets[curblock+1] - offsets[curblock]; | |
568 if (curblocksize >= blocksize*(bps/8)*2) return -1; // Fatal error | |
569 } else { | |
570 curblocksize = restsize * (bps/8); | |
571 curcompsize = blocksize*(bps/8)*2; | |
572 } | |
573 if (skip_count > blocksize/channels) { | |
574 skip_count -= blocksize/channels; | |
575 fseek(in, curcompsize, SEEK_CUR); | |
576 curblock++; | |
577 return -2; | |
578 } | |
579 /* データ読み込み */ | |
580 fread(tmpdata, 1, curcompsize, in); | |
581 /* 展開 */ | |
582 if (channels == 2 && bps == 16 && complevel == 2) { | |
583 NWAInfo_sw2 info; | |
584 NWADecode(info, tmpdata, data, curcompsize, curblocksize); | |
585 } else { | |
586 NWAInfo info(channels, bps, complevel); | |
587 NWADecode(info, tmpdata, data, curcompsize, curblocksize); | |
588 } | |
589 int retsize = curblocksize; | |
590 if (skip_count) { | |
591 int skip_c = skip_count * channels * (bps/8); | |
592 retsize -= skip_c; | |
593 memmove(data, data+skip_c, skip_c); | |
594 skip_count = 0; | |
595 } | |
596 curblock++; | |
597 return retsize; | |
598 } | |
599 | |
600 #ifdef USE_MAIN | |
601 | |
602 void conv(FILE* in, FILE* out, int skip_count, int in_size = -1) { | |
603 NWAData h; | |
604 h.ReadHeader(in, in_size); | |
605 h.CheckHeader(); | |
606 int bs = h.BlockLength(); | |
607 char* d = new char[bs]; | |
608 int err; | |
609 while( (err=h.Decode(in, d, skip_count)) != 0) { | |
610 if (err == -1) break; | |
611 if (err == -2) continue; | |
612 fwrite(d, err, 1, out); | |
613 } | |
614 return; | |
615 } | |
52 | 616 |
0 | 617 int main(int argc, char** argv) { |
618 int skip_count = 0; | |
619 | |
620 if (argc > 2 && strcmp(argv[1], "--skip") == 0) { | |
621 skip_count = atoi(argv[2]); | |
622 argc -= 2; | |
623 argv[1] = argv[3]; | |
624 argv[2] = argv[4]; | |
625 } | |
626 if (argc != 2 && argc != 3) { | |
627 fprintf(stderr,"usage : nwatowav [inputfile [outputfile]]\n"); | |
628 return -1; | |
629 } | |
52 | 630 if (strstr(argv[1], ".nwk") != NULL || strstr(argv[1], ".ovk") != NULL) { |
0 | 631 bool is_ovk; |
632 int headblk_sz; | |
52 | 633 const char* out_ext; |
0 | 634 |
635 char* outpath = new char[strlen(argv[1])+10]; | |
636 char buf[1024]; | |
637 memset(buf, 0, 1024); | |
638 FILE* in = fopen(argv[1], "rb"); | |
52 | 639 if (in == NULL) { |
0 | 640 fprintf(stderr,"Cannot open file : %s\n",argv[1]); |
641 return -1; | |
642 } | |
52 | 643 if (strstr(argv[1], ".ovk") != NULL) { |
0 | 644 is_ovk = true; |
645 headblk_sz = 16; | |
646 out_ext = "ogg"; | |
647 } else { | |
648 is_ovk = false; | |
649 headblk_sz = 12; | |
650 out_ext = "wav"; | |
651 } | |
652 fread(buf, 1, 4, in); | |
653 int index = read_little_endian_int(buf); | |
654 if (index <= 0) { | |
655 if (is_ovk) | |
656 fprintf(stderr,"Invalid Ogg-ovk file : %s : index = %d\n",argv[1],index); | |
657 else | |
658 fprintf(stderr,"Invalid Koe-nwk file : %s : index = %d\n",argv[1],index); | |
659 return -1; | |
660 } | |
661 int* tbl_off = new int[index]; | |
662 int* tbl_siz = new int[index]; | |
663 int* tbl_cnt = new int[index]; | |
664 int* tbl_origsiz = new int[index]; | |
665 int i; | |
666 for (i=0; i<index; i++) { | |
667 fread(buf, 1, headblk_sz, in); | |
668 tbl_siz[i] = read_little_endian_int(buf); | |
669 tbl_off[i] = read_little_endian_int(buf+4); | |
670 tbl_cnt[i] = read_little_endian_int(buf+8); | |
671 tbl_origsiz[i] = read_little_endian_int(buf+12); | |
672 } | |
52 | 673 fseek(in, 0, SEEK_END); |
0 | 674 int fsize = ftell(in); |
675 for (i=0; i<index; i++) { | |
676 if (tbl_off[i] <= 0 || tbl_siz[i] <= 0 || tbl_off[i]+tbl_siz[i] > fsize) { | |
677 fprintf(stderr,"Invalid table[%d] : cnt %d off %d size %d / %d\n",i,tbl_cnt[i],tbl_off[i],tbl_siz[i],fsize); | |
678 continue; | |
679 } | |
680 if (argc == 2) | |
681 sprintf(outpath, "%s-%d.%s", argv[1], tbl_cnt[i],out_ext); | |
682 else | |
683 sprintf(outpath, "%s-%d.%s", argv[2], tbl_cnt[i],out_ext); | |
684 FILE* out = fopen(outpath, "wb"); | |
52 | 685 if (out == NULL) { |
0 | 686 fprintf(stderr,"Cannot open output file %s\n",outpath); |
687 continue; | |
688 } | |
689 fprintf(stderr,"Writing file %s...\n",outpath); | |
52 | 690 fseek(in, tbl_off[i], SEEK_SET); |
0 | 691 if (is_ovk) { // copy file |
692 int sz = tbl_siz[i]; | |
693 char buf[32*1024]; | |
694 while(sz > 32*1024) { | |
695 fread(buf, 32*1024, 1, in); | |
696 fwrite(buf, 32*1024, 1, out); | |
697 sz -= 1024*32; | |
698 } | |
699 if (sz > 0) { | |
700 fread(buf, sz, 1, in); | |
701 fwrite(buf, sz, 1, out); | |
702 } | |
703 } else { // .nwk | |
704 conv(in, out, 0, tbl_siz[i]); | |
705 } | |
706 fclose(out); | |
707 } | |
708 fclose(in); | |
709 return 0; | |
710 } | |
711 FILE* in = fopen(argv[1],"rb"); | |
52 | 712 if (in == NULL) { |
0 | 713 fprintf(stderr,"Cannot open file : %s\n",argv[1]); |
714 return -1; | |
715 } | |
716 FILE* out; | |
717 if (argc != 3 && (!isatty(fileno(stdout)))) { // wave file is written to stdout if stdout is redirected to a file | |
718 out = stdout; | |
719 } else { // make a new file or use argv[2] for output file name | |
720 char* outpath = new char[strlen(argv[1])+10]; | |
721 sprintf(outpath, "%s.wav",argv[1]); | |
722 if (argc == 3) outpath = argv[2]; | |
723 out = fopen(outpath, "wb"); | |
52 | 724 if (out == NULL) { |
0 | 725 fprintf(stderr,"Cannot open file : %s\n",outpath); |
726 return -1; | |
727 } | |
728 } | |
729 conv(in, out, skip_count); | |
730 fclose(in); | |
731 if (out != stdout) fclose(out); | |
732 return 0; | |
733 } | |
734 #else | |
735 | |
736 #include"wavfile.h" | |
737 | |
738 void NWAFILE::Seek(int count) { | |
52 | 739 if (data == NULL) data = new char[block_size]; |
0 | 740 nwa->Rewind(stream); |
741 int dmy = 0; | |
742 nwa->Decode(stream, data, dmy); // skip wav header | |
743 data_len = 0; | |
744 skip_count = count; | |
745 } | |
746 NWAFILE::NWAFILE(FILE* _stream) { | |
747 skip_count = 0; | |
52 | 748 data = NULL; |
0 | 749 stream = _stream; |
750 nwa = new NWAData; | |
751 nwa->ReadHeader(stream); | |
752 if (!nwa->CheckHeader()) { | |
753 return; | |
754 } | |
755 block_size = nwa->BlockLength(); | |
756 data = new char[block_size]; | |
757 data_len = 0; | |
758 | |
759 wavinfo.SamplingRate = nwa->freq; | |
760 wavinfo.Channels = nwa->channels; | |
761 wavinfo.DataBits = nwa->bps; | |
762 | |
763 int dmy = 0; | |
764 data_len = nwa->Decode(stream, data, dmy); // skip wav header | |
765 | |
766 return; | |
767 } | |
768 NWAFILE::~NWAFILE() { | |
769 if (stream) fclose(stream); | |
770 if (data) delete[] data; | |
771 if (nwa) delete nwa; | |
772 } | |
52 | 773 |
0 | 774 int NWAFILE::Read(char* buf, int blksize, int blklen) { |
52 | 775 if (data == NULL) return -1; // end of file |
0 | 776 |
777 if (data_len > blksize * blklen) { | |
778 int len = blksize * blklen; | |
779 memcpy(buf, data, len); | |
780 memmove(data, data+len, data_len-len); | |
781 data_len -= len; | |
782 return blklen; | |
783 } | |
784 memcpy(buf, data, data_len); | |
785 int copied_length = data_len; | |
786 data_len = 0; | |
787 | |
52 | 788 if (stream == NULL) { |
0 | 789 delete[] data; |
52 | 790 data = NULL; |
0 | 791 return copied_length / blksize; |
792 } | |
793 | |
52 | 794 //TODO: Rewrite this joke |
0 | 795 // read |
796 do { | |
797 int err; | |
798 retry: | |
799 err = nwa->Decode(stream, data, skip_count); | |
800 if (err == 0 || err == -1) { // eof or error | |
801 delete[] data; | |
52 | 802 data = NULL; |
0 | 803 return copied_length / blksize; |
804 } | |
805 if (err == -2) goto retry; // EAGAIN | |
806 data_len = err; | |
807 if (copied_length + data_len < blklen*blksize) { | |
808 memcpy(buf+copied_length, data, data_len); | |
809 copied_length += data_len; | |
810 goto retry; | |
811 } | |
812 } while(0); | |
813 | |
814 // determine return length | |
815 int datablks = (data_len+copied_length)/blksize; | |
816 if (datablks <= 0) return 0; | |
817 if (datablks > blklen) datablks = blklen; | |
818 int rest_len = datablks * blksize - copied_length; | |
819 if (rest_len) { | |
820 memcpy(buf+copied_length, data, rest_len); | |
821 memmove(data, data+rest_len, data_len-rest_len); | |
822 data_len -= rest_len; | |
823 } | |
824 return datablks; | |
825 } | |
826 | |
827 char* NWAFILE::ReadAll(FILE* in, int& total_size) { | |
828 NWAData h; | |
52 | 829 if (in == NULL) return NULL; |
0 | 830 h.ReadHeader(in); |
831 h.CheckHeader(); | |
832 int bs = h.BlockLength(); | |
833 total_size = h.datasize+0x2c; | |
834 char* d = new char[total_size + bs*2]; | |
835 int dcur = 0; | |
836 int err; | |
837 int skip = 0; | |
838 while(dcur < total_size+bs && (err=h.Decode(in, d+dcur, skip)) != 0) { | |
839 if (err == -1) break; | |
840 if (err == -2) continue; | |
841 dcur += err; | |
842 } | |
843 return d; | |
844 } | |
845 | |
52 | 846 #include "music.h" |
0 | 847 |
848 char* decode_koe_nwa(AvgKoeInfo info, int* data_len) { | |
849 NWAData h; | |
52 | 850 if (info.stream == NULL) return NULL; |
851 fseek(info.stream, info.offset, SEEK_SET); | |
0 | 852 h.ReadHeader(info.stream, info.length); |
52 | 853 if (h.CheckHeader() == false) return NULL; |
0 | 854 int bs = h.BlockLength(); |
855 int total = h.datasize + 0x2c; | |
856 char* d = new char[total + bs*2]; | |
857 int dcur = 0; | |
858 int err; | |
859 int skip = 0; | |
860 while(dcur < total+bs && (err=h.Decode(info.stream, d+dcur, skip)) != 0) { | |
861 if (err == -1) break; | |
862 if (err == -2) continue; | |
863 dcur += err; | |
864 } | |
865 if (data_len) { | |
866 *data_len = dcur; | |
867 if (*data_len > total) *data_len = total; | |
868 } | |
869 return d; | |
870 } | |
871 | |
872 #endif |