Artifact Content

Not logged in

Artifact de96d041f43cbd6fed6047e0da21dd4c86bbaf7b



#include "stdafx.h"
#include "ArcCpt.h"
#include "NoahApp.h"

//--- CArchiver --------------------------------------------------

bool CArcCpt::v_check( const kiPath& aname )
{
	bool ans = ( cpt.open( aname ) && read_main_hdr() && check_index_crc() );
	cpt.close();
	return ans;
}

bool CArcCpt::v_list( const arcname& aname, aflArray& files )
{
	::SetCurrentDirectory( aname.basedir );

	bool ans = ( cpt.open( aname.lname ) && read_main_hdr() && operation_for_each( true, &files ) );
	cpt.close();
	return ans;
}

int CArcCpt::v_melt( const arcname& aname, const kiPath& ddir, const aflArray* files )
{
	::SetCurrentDirectory( aname.basedir );
	if( !cpt.open( aname.lname ) )
		return 0xffff;

	::SetCurrentDirectory( ddir );
	bool ans = ( read_main_hdr() && operation_for_each( false, const_cast<aflArray*>(files) ) );
	cpt.close();
	return ans ? 0 : 0x8020;
}

int CArcCpt::v_contents( const kiPath& aname, kiPath& dname )
{
	int ans=aUnknown;
	if( cpt.open( aname ) && read_main_hdr() )
	{
		if( cpt.read( tmp, 7 ) )
		{
			WORD EntryNum = (tmp[4]<<8)  + tmp[5];
			if( EntryNum == 1 )
				ans = aSingleFile;
			else
			{
				cpt.read( tmp, tmp[6] );
				tmp[0] = cpt.getc();
				if( tmp[0] & 0x80 ) // Folder
				{
					unsigned long size = (tmp[0]&0x3f) + 2;
					if( size == cpt.read( tmp+1, size ) )
						if( EntryNum == 1 + (tmp[size-1]<<8) + tmp[size] )
						{
							dname = "";
							tmp[ 1+tmp[0] ] = '\0';
							for( char* pp=(char*)tmp+1; *pp; pp=kiStr::next(pp) )
							{
								if( kiStr::isLeadByte(*pp) )
									dname += *pp, dname += *(pp+1);
								else if( *pp<' ' || *pp>'~' )
									dname += '_';
								else switch( *pp )
								{
									case '\\': case '/': case ':': case '*': 
									case '?': case '\"': case '<': case '>': case '|':
										dname += '_';
									default:
										dname += *pp;
								}
							}

							ans = aSingleDir;
						}
				}
			}
		}
	}
	cpt.close();
	return ans;
}

//--- CRC ----------------------------------------------------------

static unsigned long crctbl[256] = { 1 };

static void init_crc_table()
{
	if( crctbl[0]==1 ) // uninitialized
		for( unsigned long c,n=0; n!=256; n++ )
		{
			c = n;
			for( unsigned long k=8; k; k-- )
				c = (c&1) ? ((0xedb88320L)^(c>>1)) : (c>>1);
			crctbl[n] = c;
		}
}

static unsigned long crc( unsigned long c, unsigned char* cp,int cnt )
{
	while( cnt-- )
		c = (c>>8)^crctbl[(c&0xff)^*cp++];
	return c;
}

//--- cpt ----------------------------------------------------------

bool CArcCpt::read_main_hdr()
{
	//-- 先頭のバイトは MagicNumber : 0x01 なはず。
	m_nMacBinOffset = 0;
	if( 8 != cpt.read( tmp, 8 ) )
		return false;
	if( tmp[0] != 1 )
	{
		if(  0  != tmp[0]
		 || 120 != cpt.read( tmp, 120 )
		 ||  8  != cpt.read( tmp,  8  )
		 || tmp[0] != 1 ) // MacBinスキップ
			return false;
		m_nMacBinOffset = 128;
	}

	//-- indexまで跳ぶ
	cpt.seek( (tmp[4]<<24) + (tmp[5]<<16) + (tmp[6]<<8) + (tmp[7]) - 8 );
	return true;
}

bool CArcCpt::check_index_crc()
{
	bool folder;
	unsigned int size;

	//-- CRC(DWORD), EntryNum(WORD), CommentLen(BYTE)
	if( !cpt.read( tmp, 7 ) )
		return false;

	init_crc_table();

	DWORD     CRC = (tmp[0]<<24) + (tmp[1]<<16) + (tmp[2]<<8) + tmp[3];
	WORD EntryNum = (tmp[4]<<8)  + tmp[5];
	if( tmp[6] != cpt.read( tmp+7, tmp[6] ) )
		return false;
	DWORD chk_crc = crc( 0xffffffff, tmp+4, 3+tmp[6] );

	//-- 全ヘッダを走査してCRC計算
	for( WORD i=0; i!=EntryNum; i++ )
	{
		tmp[0] = cpt.getc();

		if( tmp[0] & 0x80 )	folder = true,  size = (tmp[0]&0x3f) + 2;
		else				folder = false, size = (tmp[0]) + 45;
		if( size != cpt.read( tmp+1, size ) ) return false;

		chk_crc = crc( chk_crc, tmp, 1+size );
    }

	return CRC == chk_crc;
}

bool CArcCpt::operation_for_each( bool o_list, aflArray* files )
{
	//-- Entry数を得る
	if( !cpt.read( tmp, 7 ) )
		return false;
	WORD EntryNum = (tmp[4]<<8)  + tmp[5];
	cpt.read( tmp, tmp[6] );

	//-- ダイアログ準備
	pdlg = files ? NULL : new CArcProgressDlg( EntryNum );

	//-- 操作
	kiPath path;
	m_nIndexPos = cpt.tell();
	bool ans = recurse( o_list, files, path, 0, EntryNum );
	delete pdlg;
	return ans;
}

bool CArcCpt::recurse( bool o_list, aflArray* files, kiPath& path, int base, int num )
{
	bool folder;
	unsigned int size;
	kiPath pthtmp;

	for( int i=0; i<num; i++ )
	{
		cpt.seekTo( m_nIndexPos );

	//-- tmpへindex読み込み

		tmp[0] = cpt.getc();
		if( tmp[0] & 0x80 )	folder = true,  size = (tmp[0]&0x3f) + 2;
		else				folder = false, size = (tmp[0]) + 45;
		if( size != cpt.read( tmp+1, size ) ) return false;
		m_nIndexPos = cpt.tell(); // 次のIndexの位置を記憶

	//-- ヘッダ解析

		//   0: filename_len (BYTE)
		// 1-n: filename     (with no '\0')
		char filename[MAX_PATH];
		ki_memcpy( filename, tmp+1, tmp[0] );
		filename[ *tmp ] = '\0';
		for( char* pp=filename; *pp; pp=kiStr::next(pp) )
		{
			if( kiStr::isLeadByte(*pp) )
				continue;
			if( *pp<' ' || *pp>'~' )
				*pp = '_';
			else switch( *pp )
			{
				case '\\': case '/': case ':': case '*': 
				case '?': case '\"': case '<': case '>': case '|':
					*pp = '_';
			}
		}
		// filenameが2個以上の.のみからなっていたら_に書き換え
		{
			int dots = 0;
			char* pp;
			for( pp=filename; *pp; pp=kiStr::next(pp) )
				if( *pp == '.' ) { ++dots; }
				else { dots=-1; break; }
			if( dots >= 2 )
				for( pp=filename; *pp; ++pp )
					*pp = '_';
		}

	//-- ダイアログ処理

		pthtmp = path, pthtmp += filename;

		if( pdlg )
		{
			pdlg->change( pthtmp, base+i+1 );
			if( !pdlg->msgloop() )
				return false;
		}

	//-- フォルダ処理

		if( folder )
		{
			unsigned long fldlen = (tmp[size-1]<<8) + tmp[size];
			pthtmp += '\\';

			if( o_list )
			{
				files->forcelen( base+i+1 );
				ki_strcpy( (*files)[base+i].inf.szFileName, pthtmp );
				(*files)[base+i].isfile = false;
			}

			if( !recurse( o_list, files, pthtmp, base+i+1, fldlen ) )
				return false;
			i += fldlen;
		}

	//-- ファイル処理

		else
		{
			const unsigned char* hdr = tmp + (size+1) - 80;

			unsigned long rsrcSkip; bool lzhFlag;
			if( (hdr[68]<<24) + (hdr[69]<<16) + (hdr[70]<<8) + hdr[71] != 0 )
			{
				// もし dataForkが存在するならば
				dataULen = (hdr[68]<<24) + (hdr[69]<<16) + (hdr[70]<<8) + hdr[71];
				dataCLen = (hdr[76]<<24) + (hdr[77]<<16) + (hdr[78]<<8) + hdr[79];
				rsrcSkip = (hdr[72]<<24) + (hdr[73]<<16) + (hdr[74]<<8) + hdr[75];
				lzhFlag  = (hdr[63]&4) !=0;
			}
			else
			{
				// dataForkが存在しないならば
				dataULen = (hdr[64]<<24) + (hdr[65]<<16) + (hdr[66]<<8) + hdr[67];
				dataCLen = (hdr[72]<<24) + (hdr[73]<<16) + (hdr[74]<<8) + hdr[75];
				rsrcSkip = 0;
				lzhFlag  = (hdr[63]&2) !=0;
			}

			if( o_list ) // リストへ加える
			{
				files->forcelen( base+i+1 );
				ki_strcpy( (*files)[base+i].inf.szFileName, pthtmp );
				ki_strcpy( (*files)[base+i].inf.szMode, lzhFlag ? "rle+lzh" : "rle" );
				(*files)[base+i].inf.dwCompressedSize = dataCLen;
				(*files)[base+i].inf.dwOriginalSize = dataULen;
				(*files)[base+i].isfile = true;
			}
			else if( !files || (*files)[base+i].selected )
			{
				if( !(hdr[63] & 1) )
				{
					pthtmp.mkdir();
					cpt.seekTo( (hdr[36]<<24) + (hdr[37]<<16) + (hdr[38]<<8) + hdr[39] +
								rsrcSkip + m_nMacBinOffset ); // filepos + rsrcCLen + (0 | 128)
					if( out.open( pthtmp, false ) )
					{
						cptmelt( lzhFlag );
						out.close();
					}
				}
			}
		}
    }

	return true;
}

//-- 解凍処理 ------------------------------------------------

#define	ESC1		0x81
#define	ESC2		0x82
#define NONESEEN	0
#define ESC1SEEN	1
#define ESC2SEEN	2

void CArcCpt::cptmelt( bool isRL )
{
	cpt_outstat = NONESEEN;
	cpt_LZptr = 0;
	cpt_blocksize = 0x1fff0;

	if( isRL )
		cpt_rle_lzh();
	else
		while( dataCLen-- )
			cpt_outch( cpt.getc() );
}

void CArcCpt::cpt_outch(unsigned char ch)
{
	cpt_LZbuff[ cpt_LZptr++ & (CIRCSIZE-1) ] = ch;

	switch( cpt_outstat )
	{
	case NONESEEN:
		if( ch==ESC1 )
			cpt_outstat = ESC1SEEN;
		else
			dataULen--,out.putc( cpt_savechar=ch );
		break;

	case ESC1SEEN:
		if( ch==ESC2 )
			cpt_outstat = ESC2SEEN;
		else
		{
			dataULen--,out.putc( cpt_savechar=ESC1 );
			if( ch!=ESC1 )
			{
				cpt_outstat = NONESEEN;
				dataULen--,out.putc( cpt_savechar=ch );
			}
		}
		break;

	case ESC2SEEN:
		cpt_outstat = NONESEEN;
		if( ch!=0 )
			while( --ch )
				dataULen--,out.putc(cpt_savechar);
		else
		{
			dataULen--,out.putc( ESC1 );
			dataULen--,out.putc( cpt_savechar=ESC2 );
		}
    }
}

void CArcCpt::cpt_rle_lzh()
{
	int block_count;
	unsigned int bptr;
	int Huffchar, LZlength, LZoffs;

	cpt_LZbuff[CIRCSIZE - 3] = 0;
	cpt_LZbuff[CIRCSIZE - 2] = 0;
	cpt_LZbuff[CIRCSIZE - 1] = 0;
	cpt_LZptr = 0;

	while( dataULen!=0 )
	{
		cpt_readHuff(256,cpt_Hufftree);
		cpt_readHuff( 64,cpt_LZlength);
		cpt_readHuff(128,cpt_LZoffs  );
		block_count = 0;
		cpt_newbits = (cpt.getc()<<8);
		cpt_newbits = cpt_newbits | cpt.getc();
		cpt_newbits = cpt_newbits << 16;
		cpt_bitsavail = 16;
		while( block_count<cpt_blocksize && dataULen!=0 )
		{
			if( cpt_getbit() )
			{
				Huffchar = gethuffbyte(cpt_Hufftree);
				cpt_outch((unsigned char)Huffchar);
				block_count += 2;
			}
			else
			{
				LZlength = gethuffbyte(cpt_LZlength);
				LZoffs = gethuffbyte(cpt_LZoffs);
				LZoffs = (LZoffs << 6) | cpt_get6bits();
				bptr = cpt_LZptr - LZoffs;
				while( LZlength-->0 )
					cpt_outch(cpt_LZbuff[bptr++&(CIRCSIZE-1)]);
				block_count += 3;
			}
		}
	}
}

int CArcCpt::gethuffbyte(node* l_nodelist)
{
	register node *np;
	np = l_nodelist;
	while(np->flag == 0)
		np = cpt_getbit() ? np->one : np->zero;
	return np->byte;
}

void CArcCpt::cpt_readHuff(int size,node* Hufftree)
{
	sf_entry tree_entry[256 + SLACK];
	int tree_entries;
	int tree_MaxLength;
	int treeBytes, i, len;
	sf_entry *ejm1;
	int j;
	sf_entry *entry;
    sf_entry tmp;
    int entries;
    unsigned a, b;
	int codelen, lvlstart, next, parents;
	int tree_count[32];

	treeBytes = cpt.getc();
	if( size<treeBytes*2 )
		return;
	for( i=0; i!=32; i++ )
		tree_count[i] = 0;
	i = 0;
	tree_MaxLength = 0;
	tree_entries = 0;
	while( treeBytes-->0 )
	{
		int c=cpt.getc();
		len = c >> 4;

		if(len != 0)
		{
			if(len > tree_MaxLength)
				tree_MaxLength = len;
			tree_count[len]++;
			tree_entry[tree_entries].Value = i;
			tree_entry[tree_entries++].BitLength = len;
		}
		i++;
		len = c & 0x0f;
		if(len != 0)
		{
			if(len > tree_MaxLength)
				tree_MaxLength = len;
			tree_count[len]++;
			tree_entry[tree_entries].Value = i;
			tree_entry[tree_entries++].BitLength = len;
		}
		i++;
	}

	j = 0;
    for( i=0; i<=tree_MaxLength; i++ )
		j = (j << 1) + tree_count[i];
	j = (1 <<tree_MaxLength) - j;
	for( i=0; i<j; i++ )
	{
		tree_entry[tree_entries].Value = size;
		tree_entry[tree_entries++].BitLength = tree_MaxLength;
	}

	entry = &(tree_entry[0]);
	entries = tree_entries;
    for( i=0; ++i<entries; )
	{
		tmp = entry[i];
		b = tmp.BitLength;
		j = i;
		while((j > 0) && ((a = (ejm1 = &(entry[j - 1]))->BitLength) >= b))
		{
			if((a == b) && (ejm1->Value <= tmp.Value))
				break;
			*(ejm1 + 1) = *ejm1;
			--j;
		}
		entry[j] = tmp;
	}

    i = tree_entries - 1;
    lvlstart = next = size * 2 + SLACK - 1;
    for(codelen = tree_MaxLength; codelen >= 1; --codelen)
	{
		while((i >= 0) && (tree_entry[i].BitLength == codelen))
		{
			Hufftree[next].byte = tree_entry[i].Value;
			Hufftree[next].flag = 1;
			next--;
			i--;
		}
		parents = next;
		if(codelen > 1)
		{
			for(j = lvlstart; j > parents + 1; j-= 2)
			{
				Hufftree[next].one = &(Hufftree[j]);
				Hufftree[next].zero = &(Hufftree[j - 1]);
				Hufftree[next].flag = 0;
				next--;
			}
		}
		lvlstart = parents;
	}
	Hufftree[0].one = &(Hufftree[next + 2]);
	Hufftree[0].zero = &(Hufftree[next + 1]);
	Hufftree[0].flag = 0;
}

int CArcCpt::cpt_get6bits()
{
	int cn,b=(cpt_newbits >> 26) & 0x3f;
	cpt_bitsavail -= 6;
	cpt_newbits <<= 6;
	if(cpt_bitsavail < 16)
	{
		cn = (cpt.getc() << 8);
		cn |= cpt.getc();
		cpt_newbits |= (cn << (16 - cpt_bitsavail));
		cpt_bitsavail += 16;
	}
	return b;
}

int CArcCpt::cpt_getbit()
{
	int b = (cpt_newbits >> 31) & 1;
	cpt_bitsavail--;
	if( cpt_bitsavail<16 )
	{
		cpt_newbits |= (cpt.getc() << 8);
		cpt_newbits |= cpt.getc();
		cpt_bitsavail += 16;
	}
	cpt_newbits <<= 1;
	return b;
}