Mercurial > otakunoraifu
annotate 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 |
rev | line source |
---|---|
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
1 /* nwatowav : Visual Arts 系のゲームのデモで使われる nwa 形式の |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
2 ** ファイルを wav 形式に変換する |
0 | 3 ** |
4 ** compile : gcc -O2 -o nwatowav nwatowav.cc | |
5 ** usage : nwatowav [nwa-file [outfile]] | |
6 ** nwatowav [nwk-file [outfile]] | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
7 ** example : nwatowav HM06.nwa HM06.wav # BGMファイル。HM06.wav に展開される |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
8 ** nwatowav z2813.nwk z2813 # 音声ファイル。 z2813-100.wav などのファイル名で展開される |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
9 ** nwatowav z0513.ovk z0513 # 音声ファイル。 z0513-100.ogg などのファイル名で展開される |
0 | 10 ** |
11 ** | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
12 ** 2004.5.19 小松さん<s1100089@u-aizu.ac.jp> から CLANNAD の無圧縮nwa形式に対応する |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
13 ** パッチをいただいたので、適用しました。ありがとうございます。 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
14 ** 2006.9.10 「智代アフター」の音声ファイル形式 (complevel = 5) をサポート |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
15 ** .nwk という拡張子を持つファイルを受け取ると音声ファイルとして |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
16 ** 解釈、分割して展開するようにする |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
17 ** 2007.7.28 「リトルバスターズ!」の音声ファイル形式 (*.ovk; ogg 連結型) |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
18 ** をサポート。.ovk という拡張子をもつファイルを受け取ると |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
19 ** 音声ファイルとして解釈、分割して展開するようにする |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
20 ** 「リトルバスターズ!」のBGMファイルに多量のノイズが乗る問題も |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
21 ** 解決(ランレングス圧縮の処理が不必要だった) |
0 | 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 * | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
31 * このプログラムの作者は jagarl です。 |
0 | 32 * |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
33 * このプログラム、及びコンパイルによって生成したバイナリは |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
34 * プログラムを変更する、しないにかかわらず再配布可能です。 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
35 * その際、上記 Copyright 表示を保持するなどの条件は課しま |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
36 * せん。対応が面倒なのでバグ報告を除き、メールで連絡をする |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
37 * などの必要もありません。ソースの一部を流用することを含め、 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
38 * ご自由にお使いください。 |
0 | 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 ** | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
57 ** nwa フォーマットについて |
0 | 58 ** |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
59 ** 全体としては以下の構造を持つ |
0 | 60 ** NWA Header |
61 ** data offset index | |
62 ** data block<0> | |
63 ** data block<1> | |
64 ** ... | |
65 ** data block<N> | |
66 ** | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
67 ** NWA Header: ファイル先頭から 44 bytes |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
68 ** magic number などはないのでnwa ファイルかは |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
69 ** データの整合性から判断する必要がある |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
70 ** データは全て little endian で、 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
71 ** short(signed 2byte)または int(signed 4byte) である。 |
0 | 72 ** |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
73 ** +00 short channel 数(1/2) |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
74 ** +02 short sample 一つあたりの bit 数(16) |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
75 ** +04 int 周波数(一秒あたりのデータ数) |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
76 ** +08 int 圧縮レベル:-1~5.2で最小のデータ、0で最大の復元度(-1は無圧縮rawデータとみなされる) |
0 | 77 ** +12 int ? |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
78 ** +16 int ブロック数 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
79 ** +20 int 展開後のデータの大きさ(バイト単位) |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
80 ** +24 int 圧縮時のデータの大きさ(nwa ファイルの大きさ。バイト単位) |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
81 ** +28 int サンプル数:展開後のデータ数(16bit dataなら short 単位==サンプル単位のデータの大きさ) |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
82 ** +32 int データ1ブロックを展開した時のサンプル単位のデータ数 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
83 ** +36 int 最終ブロックを展開した時のサンプル単位のデータ数 |
0 | 84 ** +40 int ? |
85 ** | |
86 ** data offset index | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
87 ** 全ブロック数 x 4 byte のデータ |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
88 ** それぞれ int のデータが全ブロック数続いている |
0 | 89 ** |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
90 ** データブロックの先頭を指すファイル先頭からの位置(オフセット) |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
91 ** が格納されている |
0 | 92 ** |
93 ** data block | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
94 ** 長さは可変。展開することで一定の大きさをもつデータに展開される。 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
95 ** データはDPCM形式。元 PCM データが a,b,c ならば (a),b-a, c-b と |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
96 ** いった差分データが、仮数3-5bit,指数3bitの形式で保存されている。 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
97 ** 結果的に、16bit のデータが多くの場合 6-8bit で格納される。 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
98 ** 仮数のビット数は圧縮レベル0で5bit、圧縮レベル2で3bitとなる。 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
99 ** 以下、圧縮レベル2の場合について話を進める。 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
100 ** モノラルの場合: |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
101 ** +00 short ブロック内の最初のデータ |
0 | 102 ** +02- bit stream |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
103 ** ステレオの場合: |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
104 ** +00 short 左(?)チャンネルの最初のデータ |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
105 ** +02 short 右(?)チャンネルの最初のデータ |
0 | 106 ** +04- bit stream |
107 ** | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
108 ** 差分データの精度が高くないので各ブロックの先頭で |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
109 ** 正確なデータにより補正される(?) |
0 | 110 ** |
111 ** bit stream | |
112 ** little endian | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
113 ** +0 - +2 : 指数 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
114 ** +3 - +5 : 仮数 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
115 ** の形式。例えば a,b,c という8bitデータがあれば、 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
116 ** a&0x07 : データ1の指数 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
117 ** (a>>3)&0x07 : データ1の仮数(signed ; |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
118 ** ((b<<2)|(a>>6))&0x07 : データ2の指数 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
119 ** (b>>1)&0x07 : データ2の仮数 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
120 ** となる。 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
121 ** ただし、指数の値により仮数のbit数が変化することがある。 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
122 ** 指数 = 1 - 6 の場合: |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
123 ** a=指数、b=仮数、p=前のデータとして、今回のデータd は |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
124 ** bの2bit目が立っている場合: |
0 | 125 ** d = p - (b&3)<<(4+a) |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
126 ** 立ってない場合: |
0 | 127 ** d = p + (b&3)<<(4+a) |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
128 ** 指数 = 0 の場合:仮数は存在しない(データは3bitとなる) |
0 | 129 ** d = p |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
130 ** 「智代アフター」の音声ファイル (complevel == 5) ではランレングス圧縮用に使われている。 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
131 ** 指数 = 7 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
132 ** 次の bit が立っている場合: |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
133 ** d = 0 (現在未使用) |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
134 ** (データは4bitとなる) |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
135 ** 次の bit が立ってない場合: |
0 | 136 ** complevel = 0,1,2: |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
137 ** 仮数 b = 6bit |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
138 ** b の 5bit 目が立っている場合: |
0 | 139 ** d = p - (b&0x1f)<<(4+7) |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
140 ** 立ってない場合: |
0 | 141 ** d = p + (b&0x1f)<<(4+7) |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
142 ** (データは10bitとなる) |
0 | 143 ** complevel = 3,4,5: |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
144 ** 仮数 b = 8bit |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
145 ** b の 7bit 目が立っている場合: |
0 | 146 ** d = p - (b&0x7f)<<9 |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
147 ** 立ってない場合: |
0 | 148 ** d = p + (b&0x1f)<<9 |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
149 ** (データは10bitとなる) |
0 | 150 ** |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
151 ** 圧縮レベルが異なる場合、たとえば圧縮レベル==0で |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
152 ** 指数==1~6でdの最上位bitが立っている場合 |
0 | 153 ** d = p - (b&0x0f)<<(2+a) |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
154 ** 指数==7でdの最上位bitが立っている場合 |
0 | 155 ** d = p - (b&0x7f)<<(2+7) |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
156 ** (b : 8bitなのでデータは12bitとなる) |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
157 ** のように、精度だけが変化するようになっている。 |
0 | 158 ** |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
159 ** ヘッダ読み込みについてはNWAData::ReadHeader()参照 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
160 ** bit stream からのデータ展開については NWADecode()参照 |
0 | 161 ************************************************************** |
162 */ | |
163 | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
164 // #define NDEBUG /* なぜか assertが入った方が速い、、、 */ |
0 | 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. | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
175 /* もし big endian のシステムに対応させる場合 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
176 ** 以下の *_little_endian_* 及び |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
177 ** getbits() 関数を変更する必要がある |
0 | 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 | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
203 /* 指定された形式のヘッダをつくる */ |
0 | 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 | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
230 /* NWA の bitstream展開に必要となる情報 */ |
0 | 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; | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
259 /* 最初のデータを読み込む */ |
0 | 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); | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
267 int flip_flag = 0; /* stereo 用 */ |
0 | 268 int runlength = 0; |
269 for (i=0; i<dsize; i++) { | |
270 if (data >= dataend) break; | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
271 if (runlength == 0) { // コピーループ中でないならデータ読み込み |
0 | 272 int type = getbits(data, shift, 3); |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
273 /* type により分岐:0, 1-6, 7 */ |
0 | 274 if (type == 7) { |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
275 /* 7 : 大きな差分 */ |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
276 /* RunLength() 有効時(CompLevel==5, 音声ファイル) では無効 */ |
0 | 277 if (getbits(data, shift, 1) == 1) { |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
278 d[flip_flag] = 0; /* 未使用 */ |
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) { | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
297 /* 1-6 : 通常の差分 */ |
0 | 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 */ | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
314 /* ランレングス圧縮なしの場合はなにもしない */ |
0 | 315 if (info.UseRunLength() == true) { |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
316 /* ランレングス圧縮ありの場合 */ |
0 | 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 } | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
335 if (info.Channels() == 2) flip_flag ^= 1; /* channel 切り替え */ |
0 | 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 } | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
380 /* data は BlockLength 以上の長さを持つこと |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
381 ** 返り値は作成したデータの長さ。終了時は 0。 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
382 ** エラー時は -1 |
52 | 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; | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
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); | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
423 if (complevel == -1) { /* 無圧縮rawデータ */ |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
424 /* 適当に決め打ちする */ |
0 | 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) { | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
430 /* 1時間を超える曲ってのはないでしょ*/ |
0 | 431 fprintf(stderr,"too large blocks : %d\n",blocks); |
432 return; | |
433 } | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
434 /* regular file なら filesize 読み込み */ |
0 | 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; | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
446 /* offset index 読み込み */ |
0 | 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; |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
468 /* データそのもののチェック */ |
0 | 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 } | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
495 /* 整合性チェック */ |
0 | 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 } | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
513 tmpdata = new char[blocksize*byps*2]; /* これ以上の大きさはないだろう、、、 */ |
0 | 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) { |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
526 if (complevel == -1) { /* 無圧縮時の処理 */ |
0 | 527 if (feof(in) || ferror(in)) return -1; |
528 if (curblock == -1) { | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
529 /* 最初のブロックなら、wave header 出力 */ |
0 | 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) { | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
558 /* 最初のブロックなら、wave header 出力 */ |
0 | 559 memcpy(data, make_wavheader(datasize, channels, bps, freq), 0x2c); |
560 curblock++; | |
561 return 0x2c; | |
562 } | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
563 /* 今回読み込む/デコードするデータの大きさを得る */ |
0 | 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 } | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
579 /* データ読み込み */ |
0 | 580 fread(tmpdata, 1, curcompsize, in); |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
581 /* 展開 */ |
0 | 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 |