1
2 #include "stdafx.h"
3 #include "ArcCpt.h"
4 #include "NoahApp.h"
5
6 //--- CArchiver --------------------------------------------------
7
8 bool CArcCpt::v_check( const kiPath& aname )
9 {
10 bool ans = ( cpt.open( aname ) && read_main_hdr() && check_index_crc() );
11 cpt.close();
12 return ans;
13 }
14
15 bool CArcCpt::v_list( const arcname& aname, aflArray& files )
16 {
17 ::SetCurrentDirectory( aname.basedir );
18
19 bool ans = ( cpt.open( aname.lname ) && read_main_hdr() && operation_for_each( true, &files ) );
20 cpt.close();
21 return ans;
22 }
23
24 int CArcCpt::v_melt( const arcname& aname, const kiPath& ddir, const aflArray* files )
25 {
26 ::SetCurrentDirectory( aname.basedir );
27 if( !cpt.open( aname.lname ) )
28 return 0xffff;
29
30 ::SetCurrentDirectory( ddir );
31 bool ans = ( read_main_hdr() && operation_for_each( false, const_cast<aflArray*>(files) ) );
32 cpt.close();
33 return ans ? 0 : 0x8020;
34 }
35
36 int CArcCpt::v_contents( const kiPath& aname, kiPath& dname )
37 {
38 int ans=aUnknown;
39 if( cpt.open( aname ) && read_main_hdr() )
40 {
41 if( cpt.read( tmp, 7 ) )
42 {
43 WORD EntryNum = (tmp[4]<<8) + tmp[5];
44 if( EntryNum == 1 )
45 ans = aSingleFile;
46 else
47 {
48 cpt.read( tmp, tmp[6] );
49 tmp[0] = cpt.getc();
50 if( tmp[0] & 0x80 ) // Folder
51 {
52 unsigned long size = (tmp[0]&0x3f) + 2;
53 if( size == cpt.read( tmp+1, size ) )
54 if( EntryNum == 1 + (tmp[size-1]<<8) + tmp[size] )
55 {
56 dname = "";
57 tmp[ 1+tmp[0] ] = '\0';
58 for( char* pp=(char*)tmp+1; *pp; pp=kiStr::next(pp) )
59 {
60 if( kiStr::isLeadByte(*pp) )
61 dname += *pp, dname += *(pp+1);
62 else if( *pp<' ' || *pp>'~' )
63 dname += '_';
64 else switch( *pp )
65 {
66 case '\\': case '/': case ':': case '*':
67 case '?': case '\"': case '<': case '>': case '|':
68 dname += '_';
69 default:
70 dname += *pp;
71 }
72 }
73
74 ans = aSingleDir;
75 }
76 }
77 }
78 }
79 }
80 cpt.close();
81 return ans;
82 }
83
84 //--- CRC ----------------------------------------------------------
85
86 static unsigned long crctbl[256] = { 1 };
87
88 static void init_crc_table()
89 {
90 if( crctbl[0]==1 ) // uninitialized
91 for( unsigned long c,n=0; n!=256; n++ )
92 {
93 c = n;
94 for( unsigned long k=8; k; k-- )
95 c = (c&1) ? ((0xedb88320L)^(c>>1)) : (c>>1);
96 crctbl[n] = c;
97 }
98 }
99
100 static unsigned long crc( unsigned long c, unsigned char* cp,int cnt )
101 {
102 while( cnt-- )
103 c = (c>>8)^crctbl[(c&0xff)^*cp++];
104 return c;
105 }
106
107 //--- cpt ----------------------------------------------------------
108
109 bool CArcCpt::read_main_hdr()
110 {
111 //-- 先頭のバイトは MagicNumber : 0x01 なはず。
112 m_nMacBinOffset = 0;
113 if( 8 != cpt.read( tmp, 8 ) )
114 return false;
115 if( tmp[0] != 1 )
116 {
117 if( 0 != tmp[0]
118 || 120 != cpt.read( tmp, 120 )
119 || 8 != cpt.read( tmp, 8 )
120 || tmp[0] != 1 ) // MacBinスキップ
121 return false;
122 m_nMacBinOffset = 128;
123 }
124
125 //-- indexまで跳ぶ
126 cpt.seek( (tmp[4]<<24) + (tmp[5]<<16) + (tmp[6]<<8) + (tmp[7]) - 8 );
127 return true;
128 }
129
130 bool CArcCpt::check_index_crc()
131 {
132 bool folder;
133 unsigned int size;
134
135 //-- CRC(DWORD), EntryNum(WORD), CommentLen(BYTE)
136 if( !cpt.read( tmp, 7 ) )
137 return false;
138
139 init_crc_table();
140
141 DWORD CRC = (tmp[0]<<24) + (tmp[1]<<16) + (tmp[2]<<8) + tmp[3];
142 WORD EntryNum = (tmp[4]<<8) + tmp[5];
143 if( tmp[6] != cpt.read( tmp+7, tmp[6] ) )
144 return false;
145 DWORD chk_crc = crc( 0xffffffff, tmp+4, 3+tmp[6] );
146
147 //-- 全ヘッダを走査してCRC計算
148 for( WORD i=0; i!=EntryNum; i++ )
149 {
150 tmp[0] = cpt.getc();
151
152 if( tmp[0] & 0x80 ) folder = true, size = (tmp[0]&0x3f) + 2;
153 else folder = false, size = (tmp[0]) + 45;
154 if( size != cpt.read( tmp+1, size ) ) return false;
155
156 chk_crc = crc( chk_crc, tmp, 1+size );
157 }
158
159 return CRC == chk_crc;
160 }
161
162 bool CArcCpt::operation_for_each( bool o_list, aflArray* files )
163 {
164 //-- Entry数を得る
165 if( !cpt.read( tmp, 7 ) )
166 return false;
167 WORD EntryNum = (tmp[4]<<8) + tmp[5];
168 cpt.read( tmp, tmp[6] );
169
170 //-- ダイアログ準備
171 pdlg = files ? NULL : new CArcProgressDlg( EntryNum );
172
173 //-- 操作
174 kiPath path;
175 m_nIndexPos = cpt.tell();
176 bool ans = recurse( o_list, files, path, 0, EntryNum );
177 delete pdlg;
178 return ans;
179 }
180
181 bool CArcCpt::recurse( bool o_list, aflArray* files, kiPath& path, int base, int num )
182 {
183 bool folder;
184 unsigned int size;
185 kiPath pthtmp;
186
187 for( int i=0; i<num; i++ )
188 {
189 cpt.seekTo( m_nIndexPos );
190
191 //-- tmpへindex読み込み
192
193 tmp[0] = cpt.getc();
194 if( tmp[0] & 0x80 ) folder = true, size = (tmp[0]&0x3f) + 2;
195 else folder = false, size = (tmp[0]) + 45;
196 if( size != cpt.read( tmp+1, size ) ) return false;
197 m_nIndexPos = cpt.tell(); // 次のIndexの位置を記憶
198
199 //-- ヘッダ解析
200
201 // 0: filename_len (BYTE)
202 // 1-n: filename (with no '\0')
203 char filename[MAX_PATH];
204 ki_memcpy( filename, tmp+1, tmp[0] );
205 filename[ *tmp ] = '\0';
206 for( char* pp=filename; *pp; pp=kiStr::next(pp) )
207 {
208 if( kiStr::isLeadByte(*pp) )
209 continue;
210 if( *pp<' ' || *pp>'~' )
211 *pp = '_';
212 else switch( *pp )
213 {
214 case '\\': case '/': case ':': case '*':
215 case '?': case '\"': case '<': case '>': case '|':
216 *pp = '_';
217 }
218 }
219 // filenameが2個以上の.のみからなっていたら_に書き換え
220 {
221 int dots = 0;
222 char* pp;
223 for( pp=filename; *pp; pp=kiStr::next(pp) )
224 if( *pp == '.' ) { ++dots; }
225 else { dots=-1; break; }
226 if( dots >= 2 )
227 for( pp=filename; *pp; ++pp )
228 *pp = '_';
229 }
230
231 //-- ダイアログ処理
232
233 pthtmp = path, pthtmp += filename;
234
235 if( pdlg )
236 {
237 pdlg->change( pthtmp, base+i+1 );
238 if( !pdlg->msgloop() )
239 return false;
240 }
241
242 //-- フォルダ処理
243
244 if( folder )
245 {
246 unsigned long fldlen = (tmp[size-1]<<8) + tmp[size];
247 pthtmp += '\\';
248
249 if( o_list )
250 {
251 files->forcelen( base+i+1 );
252 ki_strcpy( (*files)[base+i].inf.szFileName, pthtmp );
253 (*files)[base+i].isfile = false;
254 }
255
256 if( !recurse( o_list, files, pthtmp, base+i+1, fldlen ) )
257 return false;
258 i += fldlen;
259 }
260
261 //-- ファイル処理
262
263 else
264 {
265 const unsigned char* hdr = tmp + (size+1) - 80;
266
267 unsigned long rsrcSkip; bool lzhFlag;
268 if( (hdr[68]<<24) + (hdr[69]<<16) + (hdr[70]<<8) + hdr[71] != 0 )
269 {
270 // もし dataForkが存在するならば
271 dataULen = (hdr[68]<<24) + (hdr[69]<<16) + (hdr[70]<<8) + hdr[71];
272 dataCLen = (hdr[76]<<24) + (hdr[77]<<16) + (hdr[78]<<8) + hdr[79];
273 rsrcSkip = (hdr[72]<<24) + (hdr[73]<<16) + (hdr[74]<<8) + hdr[75];
274 lzhFlag = (hdr[63]&4) !=0;
275 }
276 else
277 {
278 // dataForkが存在しないならば
279 dataULen = (hdr[64]<<24) + (hdr[65]<<16) + (hdr[66]<<8) + hdr[67];
280 dataCLen = (hdr[72]<<24) + (hdr[73]<<16) + (hdr[74]<<8) + hdr[75];
281 rsrcSkip = 0;
282 lzhFlag = (hdr[63]&2) !=0;
283 }
284
285 if( o_list ) // リストへ加える
286 {
287 files->forcelen( base+i+1 );
288 ki_strcpy( (*files)[base+i].inf.szFileName, pthtmp );
289 ki_strcpy( (*files)[base+i].inf.szMode, lzhFlag ? "rle+lzh" : "rle" );
290 (*files)[base+i].inf.dwCompressedSize = dataCLen;
291 (*files)[base+i].inf.dwOriginalSize = dataULen;
292 (*files)[base+i].isfile = true;
293 }
294 else if( !files || (*files)[base+i].selected )
295 {
296 if( !(hdr[63] & 1) )
297 {
298 pthtmp.mkdir();
299 cpt.seekTo( (hdr[36]<<24) + (hdr[37]<<16) + (hdr[38]<<8) + hdr[39] +
300 rsrcSkip + m_nMacBinOffset ); // filepos + rsrcCLen + (0 | 128)
301 if( out.open( pthtmp, false ) )
302 {
303 cptmelt( lzhFlag );
304 out.close();
305 }
306 }
307 }
308 }
309 }
310
311 return true;
312 }
313
314 //-- 解凍処理 ------------------------------------------------
315
316 #define ESC1 0x81
317 #define ESC2 0x82
318 #define NONESEEN 0
319 #define ESC1SEEN 1
320 #define ESC2SEEN 2
321
322 void CArcCpt::cptmelt( bool isRL )
323 {
324 cpt_outstat = NONESEEN;
325 cpt_LZptr = 0;
326 cpt_blocksize = 0x1fff0;
327
328 if( isRL )
329 cpt_rle_lzh();
330 else
331 while( dataCLen-- )
332 cpt_outch( cpt.getc() );
333 }
334
335 void CArcCpt::cpt_outch(unsigned char ch)
336 {
337 cpt_LZbuff[ cpt_LZptr++ & (CIRCSIZE-1) ] = ch;
338
339 switch( cpt_outstat )
340 {
341 case NONESEEN:
342 if( ch==ESC1 )
343 cpt_outstat = ESC1SEEN;
344 else
345 dataULen--,out.putc( cpt_savechar=ch );
346 break;
347
348 case ESC1SEEN:
349 if( ch==ESC2 )
350 cpt_outstat = ESC2SEEN;
351 else
352 {
353 dataULen--,out.putc( cpt_savechar=ESC1 );
354 if( ch!=ESC1 )
355 {
356 cpt_outstat = NONESEEN;
357 dataULen--,out.putc( cpt_savechar=ch );
358 }
359 }
360 break;
361
362 case ESC2SEEN:
363 cpt_outstat = NONESEEN;
364 if( ch!=0 )
365 while( --ch )
366 dataULen--,out.putc(cpt_savechar);
367 else
368 {
369 dataULen--,out.putc( ESC1 );
370 dataULen--,out.putc( cpt_savechar=ESC2 );
371 }
372 }
373 }
374
375 void CArcCpt::cpt_rle_lzh()
376 {
377 int block_count;
378 unsigned int bptr;
379 int Huffchar, LZlength, LZoffs;
380
381 cpt_LZbuff[CIRCSIZE - 3] = 0;
382 cpt_LZbuff[CIRCSIZE - 2] = 0;
383 cpt_LZbuff[CIRCSIZE - 1] = 0;
384 cpt_LZptr = 0;
385
386 while( dataULen!=0 )
387 {
388 cpt_readHuff(256,cpt_Hufftree);
389 cpt_readHuff( 64,cpt_LZlength);
390 cpt_readHuff(128,cpt_LZoffs );
391 block_count = 0;
392 cpt_newbits = (cpt.getc()<<8);
393 cpt_newbits = cpt_newbits | cpt.getc();
394 cpt_newbits = cpt_newbits << 16;
395 cpt_bitsavail = 16;
396 while( block_count<cpt_blocksize && dataULen!=0 )
397 {
398 if( cpt_getbit() )
399 {
400 Huffchar = gethuffbyte(cpt_Hufftree);
401 cpt_outch((unsigned char)Huffchar);
402 block_count += 2;
403 }
404 else
405 {
406 LZlength = gethuffbyte(cpt_LZlength);
407 LZoffs = gethuffbyte(cpt_LZoffs);
408 LZoffs = (LZoffs << 6) | cpt_get6bits();
409 bptr = cpt_LZptr - LZoffs;
410 while( LZlength-->0 )
411 cpt_outch(cpt_LZbuff[bptr++&(CIRCSIZE-1)]);
412 block_count += 3;
413 }
414 }
415 }
416 }
417
418 int CArcCpt::gethuffbyte(node* l_nodelist)
419 {
420 register node *np;
421 np = l_nodelist;
422 while(np->flag == 0)
423 np = cpt_getbit() ? np->one : np->zero;
424 return np->byte;
425 }
426
427 void CArcCpt::cpt_readHuff(int size,node* Hufftree)
428 {
429 sf_entry tree_entry[256 + SLACK];
430 int tree_entries;
431 int tree_MaxLength;
432 int treeBytes, i, len;
433 sf_entry *ejm1;
434 int j;
435 sf_entry *entry;
436 sf_entry tmp;
437 int entries;
438 unsigned a, b;
439 int codelen, lvlstart, next, parents;
440 int tree_count[32];
441
442 treeBytes = cpt.getc();
443 if( size<treeBytes*2 )
444 return;
445 for( i=0; i!=32; i++ )
446 tree_count[i] = 0;
447 i = 0;
448 tree_MaxLength = 0;
449 tree_entries = 0;
450 while( treeBytes-->0 )
451 {
452 int c=cpt.getc();
453 len = c >> 4;
454
455 if(len != 0)
456 {
457 if(len > tree_MaxLength)
458 tree_MaxLength = len;
459 tree_count[len]++;
460 tree_entry[tree_entries].Value = i;
461 tree_entry[tree_entries++].BitLength = len;
462 }
463 i++;
464 len = c & 0x0f;
465 if(len != 0)
466 {
467 if(len > tree_MaxLength)
468 tree_MaxLength = len;
469 tree_count[len]++;
470 tree_entry[tree_entries].Value = i;
471 tree_entry[tree_entries++].BitLength = len;
472 }
473 i++;
474 }
475
476 j = 0;
477 for( i=0; i<=tree_MaxLength; i++ )
478 j = (j << 1) + tree_count[i];
479 j = (1 <<tree_MaxLength) - j;
480 for( i=0; i<j; i++ )
481 {
482 tree_entry[tree_entries].Value = size;
483 tree_entry[tree_entries++].BitLength = tree_MaxLength;
484 }
485
486 entry = &(tree_entry[0]);
487 entries = tree_entries;
488 for( i=0; ++i<entries; )
489 {
490 tmp = entry[i];
491 b = tmp.BitLength;
492 j = i;
493 while((j > 0) && ((a = (ejm1 = &(entry[j - 1]))->BitLength) >= b))
494 {
495 if((a == b) && (ejm1->Value <= tmp.Value))
496 break;
497 *(ejm1 + 1) = *ejm1;
498 --j;
499 }
500 entry[j] = tmp;
501 }
502
503 i = tree_entries - 1;
504 lvlstart = next = size * 2 + SLACK - 1;
505 for(codelen = tree_MaxLength; codelen >= 1; --codelen)
506 {
507 while((i >= 0) && (tree_entry[i].BitLength == codelen))
508 {
509 Hufftree[next].byte = tree_entry[i].Value;
510 Hufftree[next].flag = 1;
511 next--;
512 i--;
513 }
514 parents = next;
515 if(codelen > 1)
516 {
517 for(j = lvlstart; j > parents + 1; j-= 2)
518 {
519 Hufftree[next].one = &(Hufftree[j]);
520 Hufftree[next].zero = &(Hufftree[j - 1]);
521 Hufftree[next].flag = 0;
522 next--;
523 }
524 }
525 lvlstart = parents;
526 }
527 Hufftree[0].one = &(Hufftree[next + 2]);
528 Hufftree[0].zero = &(Hufftree[next + 1]);
529 Hufftree[0].flag = 0;
530 }
531
532 int CArcCpt::cpt_get6bits()
533 {
534 int cn,b=(cpt_newbits >> 26) & 0x3f;
535 cpt_bitsavail -= 6;
536 cpt_newbits <<= 6;
537 if(cpt_bitsavail < 16)
538 {
539 cn = (cpt.getc() << 8);
540 cn |= cpt.getc();
541 cpt_newbits |= (cn << (16 - cpt_bitsavail));
542 cpt_bitsavail += 16;
543 }
544 return b;
545 }
546
547 int CArcCpt::cpt_getbit()
548 {
549 int b = (cpt_newbits >> 31) & 1;
550 cpt_bitsavail--;
551 if( cpt_bitsavail<16 )
552 {
553 cpt_newbits |= (cpt.getc() << 8);
554 cpt_newbits |= cpt.getc();
555 cpt_bitsavail += 16;
556 }
557 cpt_newbits <<= 1;
558 return b;
559 }