1
2 #include "stdafx.h"
3 #include "ArcMsc.h"
4 #include "NoahApp.h"
5
6 bool CArcMsc::header( kiFile& fp, unsigned long* siz, char* ext )
7 {
8 // 読み出す
9 unsigned char p[14];
10 if( 14!=fp.read(p,14) )
11 return false;
12
13 // ヘッダ構造:SZDD芋'3A [ext3rd] [orisiz(dword)]
14 static const unsigned char head[9]={ 0x53,0x5A,0x44,0x44,0x88,0xF0,0x27,0x33,0x41 };//="SZDD芋'3A";
15
16 for( int i=0; i!=9; i++ )
17 if( p[i]!=head[i] )
18 return false;
19
20 if( ext )
21 *ext = (char)p[9];
22
23 DWORD x = p[10]+(p[11]<<8)+(p[12]<<16)+(p[13]<<24);
24 if( siz )
25 *siz=x;
26
27 // 原理的に、16倍という圧縮率は出ない
28 return (x <= fp.getSize()*16);
29 }
30
31 void CArcMsc::filename( char* oname, const char* ol, char ext )
32 {
33 // コピー
34 ki_strcpy( oname, ol );
35
36 // 最後が '_' なら消しておく。
37 bool bIs_=false;
38 for( char* p=oname; *p; p=kiStr::next(p) )
39 bIs_ = (*p=='_');
40 if( bIs_ )
41 *(--p)='\0';
42
43
44 if( ext )
45 {
46 // 拡張子復元
47 *p++ = ext;
48 *p = '\0';
49 }
50 else
51 {
52 // 拡張子自動補完
53 const char* x = kiPath::ext(oname);
54 if( ki_strlen(x)==2 )
55 {
56 ::CharLower(oname);
57
58 if( 0==ki_strcmp(x,"ex") )(*p++)='e';
59 else if( 0==ki_strcmp(x,"co") )(*p++)='m';
60 else if( 0==ki_strcmp(x,"sc") )(*p++)='r';
61
62 else if( 0==ki_strcmp(x,"dl") )(*p++)='l';
63 else if( 0==ki_strcmp(x,"oc") )(*p++)='x';
64 else if( 0==ki_strcmp(x,"dr") )(*p++)='v';
65 else if( 0==ki_strcmp(x,"vx") )(*p++)='d';
66 else if( 0==ki_strcmp(x,"38") )(*p++)='6';
67 else if( 0==ki_strcmp(x,"sy") )(*p++)='s';
68 else if( 0==ki_strcmp(x,"cp") )(*p++)='l';
69 else if( 0==ki_strcmp(x,"li") )(*p++)='b';
70 else if( 0==ki_strcmp(x,"tt") )(*p++)='f';
71
72 else if( 0==ki_strcmp(x,"ch") )(*p++)='m';
73 else if( 0==ki_strcmp(x,"hl") )(*p++)='p';
74 else if( 0==ki_strcmp(x,"cn") )(*p++)='t';
75 else if( 0==ki_strcmp(x,"da") )(*p++)='t';
76
77 else if( 0==ki_strcmp(x,"tx") )(*p++)='t';
78 else if( 0==ki_strcmp(x,"wr") )(*p++)='i';
79 else if( 0==ki_strcmp(x,"wa") )(*p++)='v';
80 else if( 0==ki_strcmp(x,"mi") )(*p++)='d';
81 else if( 0==ki_strcmp(x,"rm") )(*p++)='i';
82 else if( 0==ki_strcmp(x,"bm") )(*p++)='p';
83 else if( 0==ki_strcmp(x,"rl") )(*p++)='e';
84 else if( 0==ki_strcmp(x,"cu") )(*p++)='r';
85 else if( 0==ki_strcmp(x,"do") )(*p++)='c';
86 else if( 0==ki_strcmp(x,"ic") )(*p++)='o';
87 else if( 0==ki_strcmp(x,"re") )(*p++)='g';
88 else if( 0==ki_strcmp(x,"rt") )(*p++)='f';
89 else if( 0==ki_strcmp(x,"ht") )(*p++)='m';
90 else (*p++)='#';
91
92 *p = '\0';
93 }
94 }
95 }
96
97 bool CArcMsc::v_check( const kiPath& aname )
98 {
99 kiFile fp;
100 if( fp.open( aname ) )
101 return header( fp,NULL,NULL );
102 return false;
103 }
104
105 bool CArcMsc::v_list( const arcname& aname, aflArray& files )
106 {
107 kiPath fname(aname.basedir); fname+=aname.lname;
108
109 arcfile x;
110 kiFile fp;
111 char ext;
112 if( !fp.open( fname ) || !header( fp, &x.inf.dwOriginalSize, &ext ) )
113 return false;
114 ki_strcpy( x.inf.szMode, "-msc-" );
115 filename( x.inf.szFileName, aname.lname, ext );
116 x.inf.dwCompressedSize = fp.getSize();
117 x.isfile = true;
118
119 // TODO: 日時をどうする?
120
121 files.add( x );
122 return true;
123 }
124
125 int CArcMsc::v_melt( const arcname& aname, const kiPath& ddir, const aflArray* files )
126 {
127 kiFile fp;
128 kiPath fname(aname.basedir); fname+=aname.lname;
129 kiPath oname(ddir);
130 char tmp[MAX_PATH];
131
132 // ヘッダ読み込み
133 char ext;
134 unsigned long alllen;
135 if( !fp.open( fname ) || !header(fp,&alllen,&ext) )
136 return 0xffff;
137 fp.close();
138 filename( tmp, aname.lname, ext );
139 oname += tmp;
140
141 // 書庫・出力先を開く
142 OFSTRUCT of;
143 of.cBytes = sizeof(of);
144 int FROM = ::LZOpenFile( const_cast<char*>((const char*)fname),&of,OF_READ );
145 int TO = ::LZOpenFile( const_cast<char*>((const char*)oname),&of,OF_WRITE|OF_CREATE );
146 if( FROM<0 || TO<0 )
147 return 0xffff;
148 // 解凍
149 bool ans = (0<=::LZCopy( FROM,TO ));
150 // 終了
151 ::LZClose( TO );
152 ::LZClose( FROM );
153 return ans?0:0xffff;
154 }
155
156 int CArcMsc::v_compress( const kiPath& base, const wfdArray& files, const kiPath& ddir, int method, bool sfx )
157 {
158 ::SetCurrentDirectory( base );
159
160 // 元ファイルを開く
161 kiFile in;
162 if( !in.open( files[0].cFileName ) )
163 return 0xffff;
164
165 // ヘッダ情報・圧縮先ファイル名
166 char h_Ext3 = '\0';
167 unsigned long h_Len = in.getSize();
168
169 char aname[MAX_PATH];
170 ki_strcpy( aname, files[0].cFileName );
171 for( char *x=aname, *last=aname; *x; x=kiStr::next(x) )
172 last = x;
173 if( !IsDBCSLeadByte(*last) )
174 h_Ext3=*last;
175 *last = '_', *(last+1) = '\0';
176
177 // 圧縮先開く
178 ::SetCurrentDirectory( ddir );
179
180 // 圧縮先を開く
181 kiFile out;
182 if( !out.open( aname, false ) )
183 return 0xffff;
184
185 // タイムスタンプコピー
186 FILETIME ct, at, mt;
187 ::GetFileTime(in.getHandle(), &ct, &at, &mt);
188 ::SetFileTime(out.getHandle(), &ct, &at, &mt);
189
190 // ヘッダ書き込み
191 unsigned char head[14]={ 0x53,0x5A,0x44,0x44,0x88,0xF0,0x27,0x33,0x41,(unsigned char)h_Ext3,
192 (unsigned char)(h_Len&0xff),(unsigned char)((h_Len>>8)&0xff),
193 (unsigned char)((h_Len>>16)&0xff),(unsigned char)((h_Len>>24)&0xff) };
194 out.write( head, 14 );
195
196 // 圧縮作業
197 CArcProgressDlg dlg( h_Len, true );
198 dlg.change( files[0].cFileName );
199 if( !do_lzss( in, out, dlg ) )
200 {
201 out.close();
202 ::DeleteFile(aname);
203 return 0x8020;
204 }
205 return 0;
206 }
207
208
209 //-- 12bit LZSS -----------------------------------------------//
210
211
212 #define N 4096 // slide窓のサイズ 2^12 bytes
213 #define F 18 // 最長一致長 2^(16-12)+2 bytes
214
215 static unsigned char window[N+F-1];
216 static int dad[N+1], lson[N+1], rson[N+257];
217 static int matchpos, matchlen;
218
219 static void init_tree()
220 {
221 //-- 木を初期化
222
223 int i;
224 for( i=N+1; i<=N+256; i++ ) // root: 0x00 -- 0xff
225 rson[i] = N;
226 for( i=0; i<N ; i++ )
227 dad[i] = N;
228 }
229
230 static void insert_node( int r )
231 {
232 //-- [位置r]から始まる[列str]を木に登録
233 unsigned char* str = window + r;
234 rson[r] = lson[r] = N;
235
236 //-- ついでに一致長・位置も記録する
237 matchlen = 2;
238
239 // 一文字目でrootを選ぶ
240 int i, p = N+1+str[0], cmp=1;
241 for(; ;)
242 {
243 if( cmp >= 0 )
244 {
245 // 右に進む
246 if( rson[p] != N )
247 p = rson[p];
248 // 右にはもうnodeがないのでそこに登録して終了
249 else
250 {
251 rson[p] = r;
252 dad[r] = p;
253 return;
254 }
255 }
256 else
257 {
258 // 左に進む
259 if( lson[p] != N )
260 p = lson[p];
261 // 左にはもうnodeがないのでそこに登録して終了
262 else
263 {
264 lson[p] = r;
265 dad[r] = p;
266 return;
267 }
268 }
269
270 // 現在のnodeとstrを比較( i==一致長 )
271 for( i=1; i<F; i++ )
272 if( cmp = str[i] - window[p+i] )
273 break;
274
275 // 今まで見つけたものの中で最長だったら記憶
276 if( i > matchlen )
277 {
278 matchpos = p;
279 if( (matchlen=i) == F )
280 break;
281 }
282 }
283
284 // [位置p]の列と長さFで一致した場合、ここに来る
285 // p の在ったところを r で置き換える
286
287 dad[r] = dad[p];
288 lson[r] = lson[p];
289 rson[r] = rson[p];
290 dad[lson[p]] = r;
291 dad[rson[p]] = r;
292
293 if( rson[dad[p]]==p )
294 rson[dad[p]] = r;
295 else
296 lson[dad[p]] = r;
297
298 dad[p] = N;
299 }
300
301 static void delete_node( int p )
302 {
303 //-- [位置p]の要素を木から削除
304
305 if( dad[p] == N ) // 既に木に入ってないのでおしまい
306 return;
307
308 int q;
309
310 if( rson[p] == N )
311 q = lson[p]; // 唯一の子を上に持ち上げる
312 else if( lson[p] == N )
313 q = rson[p]; // 唯一の子を上に持ち上げる
314 else
315 {
316 q = lson[p];
317
318 if( rson[q] != N )
319 {
320 // 左の枝の最右、つまり自分より一つ小さいnodeを持ち上げる
321 do
322 q = rson[q];
323 while( rson[q] != N );
324
325 rson[dad[q]] = lson[q];
326 dad[lson[q]] = dad[q];
327 lson[q] = lson[p];
328 dad[lson[p]] = q;
329 }
330 rson[q] = rson[p];
331 dad[rson[p]] = q;
332 }
333
334 dad[q] = dad[p];
335 if( rson[dad[p]] == p )
336 rson[dad[p]] = q;
337 else
338 lson[dad[p]] = q;
339 dad[p] = N;
340 }
341
342 bool CArcMsc::do_lzss( kiFile& in, kiFile& out, CArcProgressDlg& dlg )
343 {
344 int i, c, len, r, s;
345 unsigned char code[17]={0}, mask=1, codeptr=1;
346
347 s = 2; // s = データ読込位置
348 r = N - 16; // r = 木への挿入位置
349
350 // クリア
351 init_tree();
352 ki_memset( window+2, ' ', N-F );
353
354 // 先頭18bytes入力
355 for( len=0 ; len<F ; len++ )
356 {
357 if( -1 == (c = in.getc()) )
358 break;
359 window[r+len] = c;
360 }
361 window[0] = window[r+16];
362 window[1] = window[r+17];
363 if( len==0 )
364 return true;
365
366 // 木へ挿入
367 for( i=F ; i>=0 ; i-- )
368 insert_node( r-i );
369
370 // ループ
371 unsigned int total_read=18,prgr_read=0;
372
373 do
374 {
375 if( prgr_read > 5000 )
376 {
377 dlg.change( NULL, total_read+=prgr_read );
378 prgr_read-=5000;
379 if( !dlg.msgloop() )
380 {
381 prgr_read=0xffffffff;
382 break;
383 }
384 }
385
386 if( matchlen > len )
387 matchlen=len;
388
389 if( matchlen < 3 ) // 一致なし
390 {
391 matchlen = 1;
392 code[0] |= mask;
393 code[codeptr++] = window[r];
394 }
395 else // 一致あり
396 {
397 // [pos&0xff] [pos&0xf00 | len-3]
398 code[codeptr++] = (unsigned char)matchpos;
399 code[codeptr++] = (unsigned char)(((matchpos>>4)&0xf0) | (matchlen-3));
400 }
401
402 if( (mask<<=1)==0 ) // code が 8Block になっていたら出力
403 {
404 out.write( code, codeptr );
405 // コードバッファ初期化
406 code[0] = 0;
407 codeptr = mask = 1;
408 }
409
410 // 出力した分読み込む
411 int lastmatchlen = matchlen;
412 for( i=0 ; i<lastmatchlen; i++ )
413 {
414 if( -1 == (c=in.getc()) )
415 break;
416 prgr_read++;
417
418 // [位置s]に一文字書き込み
419 delete_node( s );
420 window[s] = c;
421 if( s < F-1 ) window[ N+s ] = c;
422
423 s = (s+1) & (N-1); // s++
424 r = (r+1) & (N-1); // r++
425
426 // 木に[位置r]のデータを挿入
427 insert_node( r );
428 }
429
430 // EOF後処理
431 while( i++ < lastmatchlen )
432 {
433 delete_node( s );
434 s = (s+1) & (N-1); // s++
435 r = (r+1) & (N-1); // r++
436 if( --len )
437 insert_node( r );
438 }
439
440 } while( len > 0 );
441
442 if( prgr_read==0xffffffff )
443 return false;
444
445 // 8block境界に揃える
446 if( mask != 1 )
447 {
448 while( mask<<=1 )
449 code[codeptr++] = 0;
450 out.write( code, codeptr );
451 }
452
453 return true;
454 }
455