
#include"CPackFileManager.h"
#include"../CString.h"
#include<stdio.h>

#include"CFileReadNormal.h"
#include"CFileReadFileRange.h"
#include"CFileReadMemory.h"
#include"CFileWrite.h"
#include"CFileRead.h"

#include"../Debug/CWarning.h"
#include"../Debug/CTrace.h"
#include"../CRand.h"
#include"../CString.h"

#include"../Compress/CCompressZlib.h"
#include"../Windows/CShell.h"

namespace Maid
{
	using namespace CString;

	enum COMPRESSTYPE
	{
		COMPRESSTYPE_NONE=0,	//!<	無圧縮
		COMPRESSTYPE_ZLIB,		//!<	ZLib での圧縮
	};

	/*!
	 *	\class	PACKHEAD CPackFileManager.h
	 *	\brief	パックファイルのヘッダ
	\n			ファイルの先頭に来る
	 *
	 */
	struct PACKHEAD
	{
		unt32	FileCode;	//!<	'DAF2'
		enum VERSION
		{
			VERSION_0x0100=0x0100,
		};

		VERSION	Version;		//!<	ファイルバージョン
		unt32	FileCount;		//!<	全部で何ファイル格納されているか？
		unt32	HashTable;		//!<	検索時に使うハッシュテーブルの長さ

		unt32	InfoSizeComp;	//!<	アーカイブ情報が入っているデータの長さ（圧縮中のサイズ）
		unt32	InfoSize;		//!<	アーカイブ情報が入っているデータの長さ（展開後のサイズ）

		COMPRESSTYPE CompressType;		//!<	圧縮形式
		unt32	ArchiveOffset;	//!<	アーカイブデータがあるファイルの先頭からのオフセット
		unt32	KeyCode[4];		//!<	アーカイブ情報を復号するときのキー
	};

	static const unt32 PACKHEAD_CODE = Str2Binary32( 'D', 'A', 'F', '2' );

	/*!
	 *	\class	FILEINFO DenPackFileManager.h
	 *	\brief	パックファイル内にある各ファイルの情報ヘッダ
	\n			PACKHEAD の後に PACKHEAD::FileCount 個連続して並んでいる
	\n			この構造体の各アドレスは４の倍数になるようにうまく調節します
	 *
	 */
	struct FILEINFO
	{
		unt32 StructSize;		//!<	この構造体の大きさ（バイト単位）
		unt32 Position;		//!<	指定ファイルの位置（アーカイブデータの先頭からのオフセット）
		unt32 CompressSize;	//!<	アーカイブ内のファイルサイズ
		unt32 FileSize;		//!<	実際のファイルサイズ

		COMPRESSTYPE CompressType;		//!<	圧縮形式
		char FileName[1];		//!<	このファイル名( StructSize - sizeof(FILEINFO) 分ある）すべて小文字になっています
	};

	//! ハッシュテーブルに最適な数を求める
	/*!
	 *	MinValue	より大きい素数が返ります
	 *
	 *	\param  MinValue		[i ]	最低限必要な値
	 *
	 *	\return MinValue	以上の素数
	 *			見つからなかったら・・・・知らない（＾＾；
	 */
	inline unt32	GetHashTableNum( unt32 MinValue )
	{
		MySTL::vector<unt32>	ValueList;	//	今まで検出された素数
		unt32 CmpValue;

		ValueList.push_back(2);
		CmpValue = 1;

		while( true )
		{
			CmpValue += 2;	//	偶数に素数はないので２ずつ増やす
			unt32 j =  0;

			while( true )
			{
				if( j==ValueList.size() )
				{	//	最後まで検索して見つからなかったら素数です
					if( MinValue < CmpValue ) { return CmpValue; }
					ValueList.push_back(CmpValue);
					break;
				}

				//	途中の値で割り切れたら素数ではない
				if( (CmpValue%ValueList[j])==0 ) { break; }
				++j;
			}
		}

		return CmpValue;
	}


static const unt32 COPY_PACET = 1024*256;	//	一回のコピーで実行されるファイルサイズ
static const unt32 MD5CHECKSIZE = 1024*256;	//	一回のコピーで実行されるファイルサイズ
static unt08 COPYBUF[COPY_PACET];				//	MD5 のチェック時にも使われる




/*!
 *	\class	CPackFile DenPackFileManager2.h
 *	\brief	パックファイルを管理するクラス
 *
 */	
class CPackFile
{
public:
	void	Load( const mstring& FileName );
	void	Clear();
	boost::shared_ptr<IFileRead>	FindFile( const mstring& FileName );

	bool	IsExist( const mstring& FileName );

	mstring GetFileName() const { return m_FileName; }

private:
	PACKHEAD	m_PackHead;	//	ファイルヘッダ

	mstring		m_FileName;	//!<	読み込んだファイル名

	unt32		m_ArchiveKey;	//!<	アーカイブを復号するときのキーコード

	struct _FILEINFO
	{
		unt32 StructSize;			//!<	この構造体の大きさ（バイト単位）
		unt32 Position;				//!<	指定ファイルの位置（アーカイブデータの先頭からのオフセット）
		unt32 CompressSize;			//!<	アーカイブ内のファイルサイズ
		unt32 FileSize;				//!<	実際のファイルサイズ

		COMPRESSTYPE CompressType;	//!<	圧縮形式
		mstring FileName;			//!<	このファイル名
	};

	typedef MySTL::multimap<mstring, _FILEINFO>	FILEMAP;
	typedef MySTL::pair<mstring, _FILEINFO>		FILEMAPPAIR;

	FILEMAP	m_InfoTable;	//!<	m_FileInfo を参照しているテーブル

};


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! パックファイルを読み込む
/*!
 *	\param	FileName	[i ]	読み込むパックファイル名
 *
 *	\return		関数の成功で void_OK
\n				失敗すると void_OK 以外
 */
void CPackFile::Load( const mstring& FileName )
{
	Clear();

	{	//	まずはヘッダの読み込み
		CFileReadNormal	hFile;
		hFile.Open(FileName);
		hFile.Read( &m_PackHead, sizeof(m_PackHead) );

		m_ArchiveKey = 0;
		m_ArchiveKey |= (((m_PackHead.KeyCode[0]>> 0)&0xFF)<<24);
		m_ArchiveKey |= (((m_PackHead.KeyCode[1]>> 8)&0xFF)<<16);
		m_ArchiveKey |= (((m_PackHead.KeyCode[2]>>16)&0xFF)<< 8);
		m_ArchiveKey |= (((m_PackHead.KeyCode[3]>>24)&0xFF)<< 0);

		m_PackHead.FileCount     ^= m_ArchiveKey;
		m_PackHead.HashTable     ^= m_ArchiveKey;
		m_PackHead.InfoSizeComp  ^= m_ArchiveKey;
		m_PackHead.InfoSize      ^= m_ArchiveKey;
		m_PackHead.ArchiveOffset ^= m_ArchiveKey;
	}

	{
		if( m_PackHead.FileCode!=PACKHEAD_CODE ) { return; }

		switch( m_PackHead.Version )
		{
		case PACKHEAD::VERSION_0x0100:{}break;
		default:
			{
				MAID_ASSERT( true, MAIDTEXT("バージョンがおかしいです ") << m_PackHead.Version );
			}break;
		}
	}

	MySTL::vector<unt08>	InfoImage;
	{
		//	パックされているファイル情報を読み取る

		MySTL::vector<unt08>	tmp(m_PackHead.InfoSizeComp);
		InfoImage.resize( m_PackHead.InfoSize );

		CFileReadNormal	hFile;
		hFile.Open(FileName);
		hFile.Seek( sizeof(m_PackHead), IFileRead::POSITION_BEGIN );
		hFile.Read( &(tmp[0]), (int)tmp.size() );

		if( m_PackHead.CompressType==COMPRESSTYPE_ZLIB )
		{
			CCompressZlib cmp;
			cmp.Decode( &(tmp[0]), (unt32)tmp.size(), &(InfoImage[0]), (unt32)InfoImage.size() );
		}else
		{
			InfoImage = tmp;
		}
	}

	{	//	読み込んだデータをメンバに振り分ける
		FILEINFO* pInfo = (FILEINFO*)&InfoImage[0];

		for( unt32 i=0; i<m_PackHead.FileCount; ++i )
		{
			_FILEINFO dat;

			dat.StructSize   = pInfo->StructSize   ^ m_ArchiveKey;
			dat.Position     = pInfo->Position	 ^ m_ArchiveKey;
			dat.CompressSize = pInfo->CompressSize ^ m_ArchiveKey;
			dat.FileSize     = pInfo->FileSize	 ^ m_ArchiveKey;
			dat.CompressType = pInfo->CompressType;
			dat.FileName     = CString::ConvertSJIStoMAID(pInfo->FileName);

//			MAID_TRACE( dat.FileName );
			m_InfoTable.insert(FILEMAPPAIR(dat.FileName,dat));
			pInfo = (FILEINFO*)((unt08*)pInfo+ dat.StructSize);
		}
	}

	m_FileName = FileName;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! メンバのクリア
/*!
 */
void	CPackFile::Clear()
{
	m_InfoTable.clear();
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 読み込んであるパックファイルの中にデータがあるかしらべる
/*!
 *	\param	FileName	[i ]	探し出すデータ
 *
 *	\return		ファイルが見つかったら true
\n				失敗すると false
 */
bool	CPackFile::IsExist( const mstring& FileName )
{
	const FILEINFO* pInfo = NULL;

	{
		MySTL::pair< FILEMAP::iterator, FILEMAP::iterator > HashRange = m_InfoTable.equal_range( FileName );

		for( FILEMAP::iterator ite=HashRange.first; ite!=HashRange.second; ++ite )
		{
			const _FILEINFO& p = (*ite).second;
			if( p.FileName==FileName ) { return true; }
		}
	}

	return false;
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 読み込んであるパックファイルからデータを取得する
/*!
 	\param	FileName	[i ]	探し出すデータ
 	\param	pFile		[ o]	見つかったデータ
 */
boost::shared_ptr<IFileRead> CPackFile::FindFile( const mstring& FileName )
{
	_FILEINFO pInfo;

	{
		MySTL::pair< FILEMAP::iterator, FILEMAP::iterator > HashRange = m_InfoTable.equal_range( FileName );

		for( FILEMAP::iterator ite=HashRange.first; ite!=HashRange.second; ++ite )
		{
			const _FILEINFO& p = (*ite).second;
			if( p.FileName==FileName )
			{
				pInfo = p;
				break;
			}
		}

		if( pInfo.FileName.empty() )
		{
			MAID_THROWEXCEPTION(MAIDTEXT("リストに存在しません") );
		}
	}





	//	データが見つかったので引数に渡して終了

	boost::shared_ptr<IFileRead> pRet;

	switch( pInfo.CompressType )
	{
	case COMPRESSTYPE_NONE:
		{
			//	無圧縮の場合はファイルから読み込むようにする

			CFileReadFileRange* p = new CFileReadFileRange;

			p->Open( m_FileName, m_PackHead.ArchiveOffset+pInfo.Position, pInfo.FileSize );

			pRet.reset( p );

		}break;

	case COMPRESSTYPE_ZLIB:
		{
			//	圧縮されている場合は展開して終了

			boost::shared_array<unt08>	pDst( new unt08[pInfo.FileSize] );
			MySTL::vector<unt08>		Src ( pInfo.CompressSize );

			CFileReadNormal hFile;

			hFile.Open( m_FileName );
			hFile.Seek( m_PackHead.ArchiveOffset+pInfo.Position, IFileRead::POSITION_BEGIN );
			hFile.Read( &(Src[0]), (int32)Src.size() );

			{
				CCompressZlib	cmp;
				const unt32	DecSize = cmp.Decode( &(Src[0]), (int32)Src.size(), pDst.get(), pInfo.FileSize );

				MAID_ASSERT( pInfo.FileSize!=DecSize, MAIDTEXT("展開サイズが違います ") << pInfo.FileSize << MAIDTEXT(" ") << DecSize );

				CFileReadMemory* p = new CFileReadMemory;
				p->Open( pDst, pInfo.FileSize );
				pRet.reset( p );
			}
		}break;

	default:
		{
			MAID_ASSERT( true, "圧縮形式が不明です" );
		}break;
	}

	return pRet;
}


/*!
 *	\class	CPackFileManager DenPackFileManager2.h
 *	\brief	CPackFileを管理するクラス
 *
 */

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! コンストラクタ
/*!
 */
CPackFileManager::CPackFileManager()
{
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! パックファイルの登録
/*!
 *	\param	PackFileName	[i ]	登録するパックファイル名
 *
 *	\return		ファイルを開くのに成功すると void_OK
\n				失敗すると void_OK 以外
 */
void CPackFileManager::Mount( const mstring& PackFileName,  const mstring& MountDirectory )
{
	if( !CFileRead::IsExist(PackFileName) )
	{
		MAID_WARNING( PackFileName + MAIDTEXT("が存在してません") );
		return ;
	}

	SPPACKFILE pPackFile( new CPackFile );

	pPackFile->Load(PackFileName);

	mstring dir;

	if( MountDirectory.empty() )
	{
		const mstring KeyName = PackFileName.substr(0,PackFileName.length()-(GetExtension(PackFileName).length()+1) );
		dir = CreateMountDirectory(ToLower(KeyName));
	}else
	{
		dir = CreateMountDirectory(ToLower(MountDirectory));
	}

	m_PackFileList[dir].push_back(pPackFile);
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! パックファイルの登録解除
/*!
 	\param	PackFileName	[i ]	登録解除するパックファイル名
 */
void CPackFileManager::UnMount( const mstring& PackFileName )
{
	const mstring dir = CreateMountDirectory(ToLower(PackFileName));

	MOUNTLIST::iterator ite = m_PackFileList.find(dir);

	if( ite==m_PackFileList.end() ) { return; }

	{
		PACKLIST& vec = ite->second;

		for( PACKLIST::iterator i=vec.begin(); i!=vec.end(); ++i )
		{
			if( (*i)->GetFileName()==PackFileName )
			{
				vec.erase(i);
				break;
			}
		}
	}
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 読み込んであるパックファイルの中にデータがあるかしらべる
/*!
 *	\param	FileName	[i ]	探し出すデータ
 *
 *	\return		ファイルが見つかったら true
\n				失敗すると false
 */
bool CPackFileManager::IsExist( const mstring& FileName )
{
	//	CPackFileManager::FindFile　のコピペ。いじったらそっちも変えてね

	//	階層式パックファイルの読み込み
	//	FileName が \\data\hoge\default.txt と指定された場合
	//	１： \\data\hoge パックファイル内の default.txt を探す
	//	２： それも無かったら \\data パックファイル内の hoge\default.txt を探す

	const mstring name = CreateMountDirectory(ToLower(FileName));

	mstring Dir  = GetDirectory(name);
	mstring File = GetFileName(name);

	while( true )
	{
		if( Dir.empty() ) { break; }	//	フォルダが無くなったら検索終了＝＝失敗

		MOUNTLIST::iterator ite = m_PackFileList.find(Dir);

		if( ite!=m_PackFileList.end() )
		{
			PACKLIST& dat = ite->second;

			for( PACKLIST::reverse_iterator ite=dat.rbegin(); ite!=dat.rend(); ++ite )
			{
				if( (*ite)->IsExist(File) ) 
				{	//	見つかったら終了
					return true; 
				}
			}
		}

		//	結局見つからなかったら、フォルダをずらして次
		const mstring newDir = GetDirectory(Dir);

		File = Dir.substr(newDir.length()+1) + MAIDTEXT("\\") + File;
		Dir  = newDir;
	}

	return false;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 登録されているパックファイルの中からファイルを探し出す
/*!
 	@param	FileName	[i ]	探すファイル名
 	@param	pFile		[ o]	見つかったファイル
 */
boost::shared_ptr<IFileRead> CPackFileManager::FindFile( const mstring& FileName )
{
	//	階層式パックファイルの読み込み
	//	FileName が \\data\hoge\default.txt と指定された場合
	//	１： \\data\hoge パックファイル内の default.txt を探す
	//	２： それも無かったら \\data パックファイル内の hoge\default.txt を探す

	const mstring name = CreateMountDirectory(ToLower(FileName));

	mstring Dir  = GetDirectory(name);
	mstring File = GetFileName(name);

	while( true )
	{
		if( Dir.empty() ) { break; }	//	フォルダが無くなったら検索失敗

		MOUNTLIST::iterator ite = m_PackFileList.find(Dir);

		if( ite!=m_PackFileList.end() )
		{
			PACKLIST& dat = ite->second;

			for( PACKLIST::reverse_iterator ite=dat.rbegin(); ite!=dat.rend(); ++ite )
			{
				boost::shared_ptr<IFileRead> pFile = (*ite)->FindFile(File);

				if( pFile.get()!=NULL ) { return pFile; }
			}
		}

		//	結局見つからなかったら、フォルダをずらして次
		const mstring newDir = GetDirectory(Dir);

		File = Dir.substr(newDir.length()+1) + MAIDTEXT("\\") + File;
		Dir  = newDir;
	}

	MAID_THROWEXCEPTION(MAIDTEXT("アーカイブが見つかりません"));

	return boost::shared_ptr<IFileRead>();
}


mstring CPackFileManager::CreateMountDirectory( const mstring& name )
{
	//	ファイル中の '/' を '\' に変換する
	mstring dir = name;

	{
		//	１文字目は必ず'\'にしておく
		if( dir.empty() ) { dir = MAIDTEXT("\\"); }
		else
		{
			dir.insert(0,MAIDTEXT("\\"));
		}
	}

	for( unt32 i=0; i<dir.length(); ++i )
	{
		unt32 c = dir[i];
		if( c=='/' ) { dir[i] = '\\'; }
	}

	return dir;
}




/*!
 *	\class	CPackFileCreater DenPackFileManager2.h
 *	\brief	パックファイルを作成するクラス
 *
 */

	struct TEMPORARYFILEINFO
	{
		mstring	SourcePath;		//	変換前のファイルパス
		mstring	TemporaryPath;	//	テンポラリに入っているときのファイル名
		unt32			SourceFileSize;	//	圧縮前のファイルサイズ
		unt32			DestFileSize;	//	圧縮後のファイルサイズ
		COMPRESSTYPE	CompressType;	//	圧縮方法
	};



static const char TEMPORARY_DIRECTORY[] = "tmp";	//	ファイルをアーカイブするときに一時ファイルにおいておくフォルダ名





//	テンポラリに作成するファイル名に '\' ':' が入っているのでそれを変換する
mstring ConvertTmporaryDirectoryName( const mstring& DirectoryName )
{
	mstring ret;

	for( int i=0; i<(int)DirectoryName.length(); ++i )
	{
		const unt32 c = DirectoryName[i];
		switch( c )
		{
		case '\\':
		case ':': { ret += '.';	}break;
		default:  { ret += c;	}break;
		}
	}

	return ret;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 圧縮しないファイルの設定
/*!
 *	\param	List	[i ]	圧縮しないファイルを識別する拡張子(大文字・小文字は区別しません）
 */
void CPackFileCreater::SetNoCompressList( const MySTL::vector<mstring>& List )
{
	//	拡張子で区別しないで、正規表現で区別すべきかも・・・めんどい(；´Д｀)

	m_NoCompressList.clear();

	for( unt32 i=0; i<List.size(); ++i )
	{
		m_NoCompressList.insert( ToLower(List[i]) );
	}
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! パックファイル作成の開始
/*!
 *	この関数が成功したら GetStatus() で状態を見つつ 
\n	STATE_SUCCESS	がくると正常終了
\n	STATE_ERROR		がくると失敗して終了
 *
 *	\param	DirectoryName	[i ]	パックするファイルのあるディレクトリ
 *	\param	OutputName		[i ]	出力先ファイル名
 *
 *	\return 作成が開始されると void_OK
\n			されなかったら void_OK 以外
 */
void CPackFileCreater::BeginArchive( const mstring& DirectoryName, const mstring& OutputName )
{
	m_Status.State = STATUS::STATE_OK;
	m_Status.Progress = 0;
	m_Status.Text.clear();

	m_DirectoryName = DirectoryName;
	m_OutputName    = OutputName;

	m_Thread.SetFunc( MakeThreadObject( &CPackFileCreater::PackThread, this) );
	m_Thread.Execute();
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 現在の状態を取得する
/*!
 *	\return 現在の状態
 */
CPackFileCreater::STATUS CPackFileCreater::GetStatus()
{
	STATUS s;
	{
		CThreadMutexLocker Lock(m_Section);
		s = m_Status;
	}

	return s;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 実行中のパック処理を強制終了させる
/*!
 *	当然のことながら BeginArchive() を呼んだ後にしか意味がありません
 */
void   CPackFileCreater::OnCancel()
{
	m_Thread.Close();
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! Zlibを使った圧縮を行う
/*!
 *	圧縮に失敗しても、普通のファイルが作成されます
 *	\param	src	[i ]	元データのファイルパス
 *	\param	dst	[i ]	作成先のファイルパス
 */
void CPackFileCreater::CreateZlibCompressFile( const mstring& src, const mstring& dst )
{
	MySTL::vector<unt08>	tmp;
	CFileRead::Read( src, tmp );

	if( tmp.empty() ) { CFileWrite::Create( dst ); return ; }

	MySTL::vector<unt08>	ArcData;
	ArcData.resize( tmp.size() );

	unt32 EncSize;
	CCompressZlib cmp;
	EncSize = cmp.Encode( &(tmp[0]), (unt32)tmp.size(), &(ArcData[0]), (unt32)ArcData.size() );
	if( EncSize < tmp.size() )
	{
		ArcData.resize( EncSize );

		AddAlignmentByte( ArcData, 16 );
		CFileWrite::Write( dst, &(ArcData[0]), ArcData.size() );
	}else
	{
		CreateNoCompressFile( src, dst );
	}
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! ファイルの転送を行う
/*!
 *	\param	src	[i ]	元データのファイルパス
 *	\param	dst	[i ]	作成先のファイルパス
 */
void CPackFileCreater::CreateNoCompressFile( const mstring& src, const mstring& dst )
{
	MySTL::vector<unt08> dat;

	CFileRead::Read( src, dat );

	if( dat.empty() ) { CFileWrite::Create( dst ); return ; }

	AddAlignmentByte( dat, 16 );
	CFileWrite::Write( dst, &(dat[0]), dat.size() );
}


void CPackFileCreater::AddAlignmentByte( MySTL::vector<unt08>& dat, int size )
{
	const int mod = dat.size() % size;

	if( mod==0 ) { return ; }

	const int alignment = size - mod;

	for( int i=0; i<alignment; ++i )
	{
		dat.push_back( 0xCC );
	}
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 実際にパックファイルを作成する関数
/*!
 *	この関数は別スレッドで動いています
 *
 *	\param	Brige	[i ]	受け渡しデータ
 *
 *	\return 常に０
 */
unt32	CPackFileCreater::PackThread( volatile  CThreadController::BRIGEDATA& Brige )
{
	MySTL::vector<TEMPORARYFILEINFO>	TemporaryInfo;


	{	// ファイルの検索
		{
			CThreadMutexLocker Lock(m_Section);
			m_Status.Text = MAIDTEXT("パックするファイルの検索中");
		}
		FILELIST	PackList;	//	圧縮するファイルリスト（すべて小文字化されています）
		PickupFile( m_DirectoryName, PackList );

		TemporaryInfo.resize(PackList.size());
		for( int i=0; i<(int)PackList.size(); ++i )
		{
			TemporaryInfo[i].SourcePath = PackList[i];
		}
	}

/*	//	テストルーチン
	for( FILELIST::iterator ite=PackList.begin(); ite!=PackList.end(); ++ite ) { MAID_TRACE( "%s", ite->c_str() );	}
//*/

	CShell::CreateDirectory( MAIDTEXT(TEMPORARY_DIRECTORY) );

	{
		//	アーカイブするファイルの作成
		//	テンポラリフォルダを作成してそこに一旦ファイルを作成する

		const EXTLIST& ExtList = m_NoCompressList;

		for( int i=0; i<(int)TemporaryInfo.size(); ++i ) 
		{
			TEMPORARYFILEINFO& info = TemporaryInfo[i];

			if( Brige.IsExit )
			{
				CThreadMutexLocker Lock(m_Section);
				m_Status.State = STATUS::STATE_CANCEL;
				m_Status.Text  = MAIDTEXT("強制終了しました");
				return 0;
			}


			//	ファイルを圧縮してtmpフォルダにコピーする
			//	圧縮できなかった場合は普通にコピーする
			const mstring SrcPath= info.SourcePath;
			const mstring TmporaryFileName = ConvertTmporaryDirectoryName(SrcPath);
			const mstring DstPath= MAIDTEXT(TEMPORARY_DIRECTORY)+MAIDTEXT("\\")+TmporaryFileName;
			{
				CThreadMutexLocker Lock(m_Section);
				m_Status.Text  = SrcPath + MAIDTEXT(" の圧縮中");
				m_Status.Progress = i*50/(int)TemporaryInfo.size();
			}

//			bool	IsCompFail = false;

			// 非圧縮リストにあるなら圧縮しない
			if( ExtList.find( GetExtension(SrcPath) )!=ExtList.end() ) 
			{
				CreateNoCompressFile( SrcPath, DstPath );
				info.CompressType   = COMPRESSTYPE_NONE;
				info.SourceFileSize = CShell::GetFileSize(SrcPath);
				info.DestFileSize   = CShell::GetFileSize(DstPath);
				info.TemporaryPath  = DstPath;
			}
			else
			{
				CreateZlibCompressFile( SrcPath, DstPath );

				const unt SrcSize = CShell::GetFileSize(SrcPath);
				const unt DstSize = CShell::GetFileSize(DstPath);

				if( SrcSize==DstSize )
				{
					info.CompressType   = COMPRESSTYPE_NONE;
					m_NoCompressFileList.push_back(MAIDTEXT("圧縮に失敗したので圧縮しないで固めます:") + SrcPath );
				}else
				{
					info.CompressType   = COMPRESSTYPE_ZLIB;
				}
				info.SourceFileSize = SrcSize;
				info.DestFileSize   = DstSize;
				info.TemporaryPath  = DstPath;
			}
		}
	}



	//	実際にファイルを作る
	{
		{
			CThreadMutexLocker Lock(m_Section);
			m_Status.Text  = MAIDTEXT(" アーカイブヘッダの作成");
		//	m_Status.Progress = i*50/TemporaryInfo.size();
		}


		MySTL::vector<unt08>	ArchiveInfo;
		MySTL::vector<unt08>	ArchiveInfoCompress;
		const unt32 HashValue = GetHashTableNum((unt32)TemporaryInfo.size());

		//	ちょっとだけ暗号をかけるので、それ用の暗号キーを作成する
		const unt32 AngouKey = Str2Binary32(CRand::Get(0x100),CRand::Get(0x100),CRand::Get(0x100),CRand::Get(0x100));


		{
			//	アーカイブ情報の作成
			const unt32 CurrentDirCount = (unt32)m_DirectoryName.length()+1;	// +1 は '\\' 分
			ArchiveInfo.resize((sizeof(TEMPORARYFILEINFO)+256)*TemporaryInfo.size());	//	圧縮前のデータが並んでいる配列（これだけあればこえることはないはず）

			ZERO( &ArchiveInfo[0], ArchiveInfo.size() );
			unt32 pos=0;
			unt32 offset=0;

			for( int i=0; i<(int)TemporaryInfo.size(); ++i ) 
			{
				TEMPORARYFILEINFO& src	= TemporaryInfo[i];
				FILEINFO* pDst			= (FILEINFO*)&ArchiveInfo[pos];

				const MySTL::string PackName = ConvertMAIDtoSJIS(src.SourcePath.substr(CurrentDirCount));

				const unt32 StructSize = (sizeof(FILEINFO) + (unt32)PackName.length()+3)&~3;

				pDst->StructSize = StructSize ^ AngouKey;
				pDst->Position   = offset^ AngouKey;
				pDst->CompressSize   = src.DestFileSize ^ AngouKey;
				pDst->FileSize		 = src.SourceFileSize ^ AngouKey;
				pDst->CompressType   = src.CompressType;
				::memcpy( pDst->FileName, PackName.c_str(), PackName.length() );

//				MAID_TRACE(		MAIDTEXT("name:")<<src.SourcePath
//							<<	MAIDTEXT("Position:")<<offset
//							<<	MAIDTEXT("CompressSize:")<<src.DestFileSize
//							<<	MAIDTEXT("FileSize:")<<src.SourceFileSize
//							);

				if( Brige.IsExit )
				{
					CThreadMutexLocker Lock(m_Section);
					m_Status.State = STATUS::STATE_CANCEL;
					m_Status.Text  = MAIDTEXT("強制終了しました");
					return 0;
				}

				pos    += StructSize;
				offset += src.DestFileSize;
			}


			{	//	圧縮作業

				ArchiveInfoCompress.resize(ArchiveInfo.size());
				CCompressZlib cmp;
				const unt32 EncSize = cmp.Encode( &(ArchiveInfo[0]), (unt32)ArchiveInfo.size(), &(ArchiveInfoCompress[0]), (unt32)ArchiveInfoCompress.size() );

				if( EncSize < ArchiveInfo.size() )
				{
					ArchiveInfoCompress.resize(EncSize);
				}else
				{
					ArchiveInfoCompress = ArchiveInfo;
				}
			}
		}


		//	ヘッダの作成

		PACKHEAD	Head;

		const unt32 ArchiveOffset = (sizeof(PACKHEAD)+(unt32)ArchiveInfoCompress.size()+3)&(~3);
		const unt32 dummybyte = ArchiveOffset - (sizeof(PACKHEAD)+(unt32)ArchiveInfoCompress.size());

		{
			Head.FileCode = PACKHEAD_CODE;
			Head.Version  = PACKHEAD::VERSION_0x0100;
			Head.FileCount= (unt32)TemporaryInfo.size() ^ AngouKey;
			Head.HashTable= GetHashTableNum((unt32)TemporaryInfo.size()) ^ AngouKey;
			Head.InfoSizeComp = (unt32)ArchiveInfoCompress.size() ^ AngouKey;
			Head.InfoSize     = (unt32)ArchiveInfo.size() ^ AngouKey;
			Head.ArchiveOffset= ArchiveOffset ^ AngouKey;

			if( ArchiveInfoCompress.size()==ArchiveInfo.size() )	{ Head.CompressType = COMPRESSTYPE_NONE; }
			else													{ Head.CompressType = COMPRESSTYPE_ZLIB; }


			Head.KeyCode[0] = Str2Binary32(((AngouKey>>24)&0xFF),CRand::Get(0x100),CRand::Get(0x100),CRand::Get(0x100));
			Head.KeyCode[1] = Str2Binary32(CRand::Get(0x100),((AngouKey>>16)&0xFF),CRand::Get(0x100),CRand::Get(0x100));
			Head.KeyCode[2] = Str2Binary32(CRand::Get(0x100),CRand::Get(0x100),((AngouKey>> 8)&0xFF),CRand::Get(0x100));
			Head.KeyCode[3] = Str2Binary32(CRand::Get(0x100),CRand::Get(0x100),CRand::Get(0x100),((AngouKey>> 0)&0xFF));
		}

		//	ファイルへの書き込み

		CFileWrite hFile;

		hFile.Open( m_OutputName, CFileWrite::OPENOPTION_NEW );
		hFile.Write( &Head, sizeof(Head) );
		hFile.Write( &(ArchiveInfoCompress[0]), (int)ArchiveInfoCompress.size() );

		{
			//	ダミーデータの作成
			if( dummybyte!=0 )
			{
				MySTL::vector<unt08> dummy(dummybyte,0);
				hFile.Write( &(dummy[0]), (int)dummy.size() );
			}
		}
//		MAID_TRACE( MAIDTEXT("Head:") << sizeof(Head)
//				<< MAIDTEXT("ArchiveInfo:") << ArchiveInfoCompress.size()
//				<< MAIDTEXT("ArchiveOffset:") << ArchiveOffset
//				<< MAIDTEXT("dummybyte:") << dummybyte
//				);

		for( int i=0; i<(int)TemporaryInfo.size(); ++i ) 
		{
			TEMPORARYFILEINFO& src	= TemporaryInfo[i];

			if( Brige.IsExit )
			{
				CThreadMutexLocker Lock(m_Section);
				m_Status.State = STATUS::STATE_CANCEL;
				m_Status.Text  = MAIDTEXT("強制終了しました");
				return 0;
			}

			{
				CThreadMutexLocker Lock(m_Section);
				m_Status.Progress = i*50/(unt32)TemporaryInfo.size()+50;
				m_Status.Text  = src.SourcePath + MAIDTEXT(" のアーカイブ中");
			}

			MySTL::vector<unt08>	tmp;
			CFileRead::Read( src.TemporaryPath, tmp );

			if( !tmp.empty() )
			{
				hFile.Write( &(tmp[0]), (unt32)tmp.size() );
			}
		}
	}





	//	テンポラリに作成したファイルの削除
	for( int i=0; i<(int)TemporaryInfo.size(); ++i ) 
	{
		TEMPORARYFILEINFO& src	= TemporaryInfo[i];

		CShell::DeleteFile( src.TemporaryPath );
	}

	//	ディレクトリも消す
	CShell::DeleteDirectory( MAIDTEXT(TEMPORARY_DIRECTORY) );



	{
		CThreadMutexLocker Lock(m_Section);

		m_Status.State = STATUS::STATE_SUCCESS;
		m_Status.Text  = MAIDTEXT("終了しました");
		m_Status.Progress = 100;
	}

	return 0;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 指定したディレクトリ内にあるファイル名を列挙する
/*!
 	ディレクトリ内のディレクトリも検索します
 
 	\param	Dir		[i ]	検索するディレクトリ名
 	\param	List	[ o]	列挙先
 */
void	CPackFileCreater::PickupFile( const mstring& Dir, FILELIST& List )
{
	CShell::FINDOBJECTLIST	CandidateFile;

	CandidateFile = CShell::PickupFile( Dir+MAIDTEXT("\\*.*") );

	for( CShell::FINDOBJECTLIST::iterator ite=CandidateFile.begin(); ite!=CandidateFile.end(); ++ite )
	{
		const mstring str = ite->GetFileName();

		if( str[0]=='.' ) { continue; }

		switch( ite->GetFileType() )
		{
		case CShell::CFindObject::TYPE_FILE:
			{
				List.push_back( ToLower(Dir+MAIDTEXT("\\")+str) );	//<- ついでに小文字化しておく
			}break;

		case CShell::CFindObject::TYPE_DIRECTORY:
			{
				PickupFile( Dir+MAIDTEXT("\\")+str, List );
			}break;
		}
	}
}


}
