
#include	"stdafx.h"
#include	"ExtractBase.h"
#include	"Image.h"
#include	"Susie.h"

typedef int (WINAPI* SPI_PROGRESS)(int, int, long);
typedef int (WINAPI* GetPluginInfoProc)(int infono, LPSTR buf, int buflen);
typedef int (WINAPI* IsSupportedProc)(LPSTR filename, DWORD dw);
typedef int (WINAPI* GetArchiveInfoProc)(LPSTR buf, long len, unsigned int flag, HLOCAL* lphInf);
typedef int (WINAPI* GetFileProc)(LPSTR src, long len, LPSTR dest, unsigned int flag, FARPROC prgressCallback, long lData);
typedef int (WINAPI* GetPictureProc)(LPSTR buf, long len, unsigned int flag, HLOCAL* pHBInfo, HLOCAL* pHBm, SPI_PROGRESS lpPrgressCallback, long lData);
typedef int (WINAPI* ConfigurationDlgProc)(HWND, int);

#pragma pack(push, 1)
struct fileInfo {
	BYTE	method[8];		// 圧縮法の種類
	DWORD	position;		// ファイル上での位置
	DWORD	compsize;		// 圧縮されたサイズ
	DWORD	filesize;		// 元のファイルサイズ
	DWORD	timestamp;		// ファイルの更新日時
	char	path[200];		// 相対パス
	char	filename[200];	// ファイルネーム
	DWORD	crc;			// CRC
};
#pragma pack(pop, 1)

std::vector<SSusieInfo>	CSusie::m_stsiMain;
std::vector<SSusieInfo>	CSusie::m_stsiTemporary;

//////////////////////////////////////////////////////////////////////////////////////////
//	初期化

void CSusie::Init()
{
	m_stsiTemporary = m_stsiMain;
}

//////////////////////////////////////////////////////////////////////////////////////////
//	適用

void CSusie::Apply()
{
	m_stsiMain = m_stsiTemporary;
}

//////////////////////////////////////////////////////////////////////////////////////////
//	マウント

BOOL	CSusie::Mount(
	CArcFile*			pclArc							// アーカイブ
	)
{
	// ヘッダ取得

	BYTE*				pbtHeader = pclArc->GetHed();

	// マウント

	for( size_t i = 0 ; i < m_stsiMain.size() ; i++ )
	{
		SSusieInfo*			pstsiTarget = &m_stsiMain[i];

		if( pstsiTarget->bValidity == 0 )
		{
			// 無効なSusieプラグイン

			continue;
		}

		// IsSupported()取得

		IsSupportedProc	IsSupported = (IsSupportedProc) pstsiTarget->cllPlugin.GetProcAddress( _T("IsSupported") );

		if( IsSupported == NULL )
		{
			// IsSupported関数が実装されていない

			continue;
		}

		// アーカイブファイルパスのコピー

		char				szPathToArc[MAX_PATH];
		YCStringA			clsPathToArc = pclArc->GetArcPath();

		strcpy( szPathToArc, clsPathToArc );

		// IsSupported()呼び出し

		BYTE				abtHeader[2048];

		memcpy( abtHeader, pbtHeader, sizeof(abtHeader) );

		if( IsSupported( szPathToArc, (DWORD) abtHeader ) == 0 )
		{
			// 対応していないアーカイブファイル

			continue;
		}

		// GetArchiveInfo()取得

		GetArchiveInfoProc	GetArchiveInfo = (GetArchiveInfoProc) pstsiTarget->cllPlugin.GetProcAddress( _T("GetArchiveInfo") );

		if( GetArchiveInfo == NULL )
		{
			// IsSupported()を信用してマウント

			return	pclArc->Mount();
		}

		// アーカイブファイルパスのコピー(IsSupported()で書き換えられている恐れがあるため)

		strcpy( szPathToArc, clsPathToArc );

		// GetArchiveInfo()呼び出し

		YCLocalMemory		cllmFileInfo;

		if( GetArchiveInfo( szPathToArc, 0, 0, &cllmFileInfo.GetHandle() ) != 0 )
		{
			// GetArchiveInfo関数内でエラー発生

			cllmFileInfo.Free();

			// IsSupported()を信用してマウント

			return	pclArc->Mount();
		}

		if( cllmFileInfo.GetHandle() == NULL )
		{
			// GetArchiveInfo関数内でエラー発生

			// IsSupported関数を信用してマウント

			return	pclArc->Mount();
		}

		// ファイル情報の取得

		fileInfo*			pstFileInfo = (fileInfo*) cllmFileInfo.Lock();

		size_t				uSusieFileInfoSize = cllmFileInfo.GetSize();
		size_t				uFileCount = uSusieFileInfoSize / sizeof( fileInfo );

		for( size_t uIndex = 0 ; uIndex < uFileCount ; uIndex++ )
		{
			if( pstFileInfo->method[0] == '\0' )
			{
				// 終了

				break;
			}

			char				szFileName[MAX_PATH];

			if( pstFileInfo->path[0] != '\0' )
			{
				// パスが存在する

				strcpy( szFileName, pstFileInfo->path );
				PathAppendA( szFileName, pstFileInfo->filename );
			}
			else
			{
				strcpy( szFileName, pstFileInfo->filename );
			}

			// ファイル情報を設定

			SFileInfo			stFileInfo;

			stFileInfo.name = szFileName;
			stFileInfo.sizeCmp = (pstFileInfo->compsize == 0) ? pstFileInfo->filesize : pstFileInfo->compsize;
			stFileInfo.sizeOrg = pstFileInfo->filesize;
			stFileInfo.start = pstFileInfo->position;
			stFileInfo.end = stFileInfo.start + stFileInfo.sizeCmp;
			stFileInfo.format.Append( (LPTSTR)pstFileInfo->method, 8 );

			pclArc->AddFileInfo( stFileInfo );

			pstFileInfo++;
		}

		// 解放

		cllmFileInfo.Free();

		return	TRUE;
	}

	return	FALSE;
}

//////////////////////////////////////////////////////////////////////////////////////////
//	デコード

BOOL	CSusie::Decode(
	CArcFile*			pclArc							// アーカイブ
	)
{
	YCStringA			clsPathToArc = pclArc->GetArcPath();
	YCStringA			clsFileName = pclArc->GetOpenFileInfo()->name;

	// ヘッダ取得

	BYTE*				pbtHeader = pclArc->GetHed();

	// ファイル入力GetPicture

	std::vector<YCLibrary*>	vtSupportPlugin;

	for( size_t i = 0, uPluginIndex = 0 ; i < m_stsiMain.size() ; i++ )
	{
		SSusieInfo*			pstsiTarget = &m_stsiMain[i];

		if( pstsiTarget->bValidity == 0 )
		{
			// 無効なSusieプラグイン

			continue;
		}

		// IsSupported()取得

		IsSupportedProc		IsSupported = (IsSupportedProc) pstsiTarget->cllPlugin.GetProcAddress( _T("IsSupported") );

		if( IsSupported == NULL )
		{
			// IsSupported関数が実装されていない

			continue;
		}

		// アーカイブファイルパスのコピー

		char				szPathToArc[MAX_PATH];

		strcpy( szPathToArc, clsPathToArc );

		BYTE				abtHeader[2048];

		memcpy( abtHeader, pbtHeader, sizeof(abtHeader) );

		if( !IsSupported( szPathToArc, (DWORD) abtHeader ) )
		{
			// 対応していないアーカイブファイル

			continue;
		}

		// 対応しているアーカイブファイル

		vtSupportPlugin.push_back( &pstsiTarget->cllPlugin );

		// アーカイブファイルパスのコピー

		strcpy( szPathToArc, clsPathToArc );

		// GetPicture()取得

		GetPictureProc		GetPicture = (GetPictureProc) pstsiTarget->cllPlugin.GetProcAddress( _T("GetPicture") );

		if( GetPicture == NULL )
		{
			// GetPicture関数が実装されていない

			continue;
		}

		YCLocalMemory		cllmBitmapInfo;
		YCLocalMemory		cllmBitmapData;

		// GetPicture()呼び出し

		if( GetPicture( szPathToArc, pclArc->GetOpenFileInfo()->start, 0x0000, &cllmBitmapInfo.GetHandle(), &cllmBitmapData.GetHandle(), NULL, 0 ) != 0 )
		{
			// GetPicture失敗

			continue;
		}

		// 画像情報取得

		LPBITMAPINFO	pBMPInfo = (LPBITMAPINFO) cllmBitmapInfo.Lock();
		LPBYTE			pHBm = (LPBYTE) cllmBitmapData.Lock();
		size_t			HBmSize = cllmBitmapData.GetSize();

		// 画像出力

		CImage			image;

		image.Init( pclArc, pBMPInfo->bmiHeader.biWidth, pBMPInfo->bmiHeader.biHeight, pBMPInfo->bmiHeader.biBitCount, (LPBYTE) pBMPInfo->bmiColors );
		image.Write( pHBm, HBmSize );
		image.Close();

		// 終了

		cllmBitmapInfo.Free();
		cllmBitmapData.Free();

		return	TRUE;
	}

	// ファイル入力GetFile

	YCLocalMemory		cllmSrc;

	for( size_t uCnt = 0 ; uCnt < vtSupportPlugin.size() ; uCnt++ )
	{
		// アーカイブファイルパスのコピー

		char				szPathToArc[MAX_PATH];

		strcpy( szPathToArc, clsPathToArc );

		// GetFile()取得

		GetFileProc			GetFile = (GetFileProc) vtSupportPlugin[uCnt]->GetProcAddress( _T("GetFile") );

		if( GetFile == NULL )
		{
			// GetFile関数が実装されていない

			continue;
		}

		// GetFile()呼び出し

		if( GetFile( szPathToArc, pclArc->GetOpenFileInfo()->start, (LPSTR) &cllmSrc.GetHandle(), 0x0100, NULL, 0 ) != 0 )
		{
			// GetFile失敗

			cllmSrc.Free();
			continue;
		}

		// 正常終了

		break;
	}

	// 読み込み先のメモリをロック

	BOOL				bGetFileSuccess = TRUE;
	LPBYTE				pbtSrc = NULL;
	DWORD				dwSrcSize;

	if( cllmSrc.GetHandle() != NULL )
	{
		// GetFileに成功している

		pbtSrc = (LPBYTE) cllmSrc.Lock();
		dwSrcSize = cllmSrc.GetSize();
	}
	else
	{
		// GetFileに失敗した

		bGetFileSuccess = FALSE;

		// メモリ確保

		dwSrcSize = pclArc->GetOpenFileInfo()->sizeCmp;

		cllmSrc.Alloc( LHND, dwSrcSize );
		pbtSrc = (LPBYTE) cllmSrc.Lock();

		// 読み込み

		pclArc->Read( pbtSrc, dwSrcSize );
		pclArc->SeekCur( -(INT64)dwSrcSize );
	}

	// メモリ入力GetPicture

	for( size_t i = 0 ; i < m_stsiMain.size() ; i++ )
	{
		SSusieInfo*			pstsiTarget = &m_stsiMain[i];

		if( pstsiTarget->bValidity == 0 )
		{
			// 無効なSusieプラグイン

			continue;
		}

		// IsSupported()取得

		IsSupportedProc		IsSupported = (IsSupportedProc) pstsiTarget->cllPlugin.GetProcAddress( _T("IsSupported") );

		if( IsSupported == NULL )
		{
			// IsSupported関数が実装されていない

			continue;
		}

		// ファイルパスのコピー
		// フルパスにしないと落ちるプラグインが存在する

		char				szPathToFile[MAX_PATH];

		strcpy( szPathToFile, clsPathToArc.GetDirPath() );
		PathAppendA( szPathToFile, clsFileName );

		// 2KB必要

		BYTE				abtSrcHeader[2048];
		DWORD				dwCopySize = (dwSrcSize <= sizeof(abtSrcHeader)) ? dwSrcSize : sizeof(abtSrcHeader);

		ZeroMemory( abtSrcHeader, sizeof(abtSrcHeader) );
		memcpy( abtSrcHeader, pbtSrc, dwCopySize );

		// IsSupported()呼び出し

		if( !IsSupported( szPathToFile, (DWORD) abtSrcHeader ) )
		{
			// 対応していないファイル

			continue;
		}

		// GetPicture()取得

		GetPictureProc		GetPicture = (GetPictureProc) pstsiTarget->cllPlugin.GetProcAddress( _T("GetPicture") );

		if( GetPicture == NULL )
		{
			// GetPicture関数が実装されていない

			continue;
		}

		YCLocalMemory		cllmBitmapInfo;
		YCLocalMemory		cllmBitmapData;

		// GetPicture()呼び出し

		if( GetPicture( (LPSTR) pbtSrc, dwSrcSize, 0x0001, &cllmBitmapInfo.GetHandle(), &cllmBitmapData.GetHandle(), NULL, 0 ) != 0 )
		{
			// GetPicture失敗

			continue;
		}

		// 画像情報取得

		LPBITMAPINFO		pBMPInfo = (LPBITMAPINFO) cllmBitmapInfo.Lock();
		LPBYTE				pHBm = (LPBYTE) cllmBitmapData.Lock();
		size_t				HBmSize = cllmBitmapData.GetSize();

		// 画像出力

		CImage				image;

		image.Init( pclArc, pBMPInfo->bmiHeader.biWidth, pBMPInfo->bmiHeader.biHeight, pBMPInfo->bmiHeader.biBitCount, (BYTE*) pBMPInfo->bmiColors );
		image.Write( pHBm, HBmSize );
		image.Close();

		// 終了

		cllmBitmapInfo.Free();
		cllmBitmapData.Free();
		cllmSrc.Free();

		return	TRUE;
	}

	// GetFile()でファイルが取得できてなければ終了

	if( !bGetFileSuccess )
	{
		cllmSrc.Free();

		return	FALSE;
	}

	// GetFile()で取得したファイルを出力

	pclArc->OpenFile();
	pclArc->WriteFile( pbtSrc, dwSrcSize, pclArc->GetOpenFileInfo()->sizeCmp );
	pclArc->CloseFile();

	// 終了

	cllmSrc.Free();

	return	TRUE;
}

//////////////////////////////////////////////////////////////////////////////////////////
//	Susieプラグインのロード

void	CSusie::LoadSpi(
	const YCString&		rfclsPathToSusieFolder			// Susieフォルダパス
	)
{
	// Susieプラグインの解放

	m_stsiMain.clear();

	// Susieプラグインへのパス

	TCHAR				szPathToSusiePlugin[MAX_PATH];

	lstrcpy( szPathToSusiePlugin, rfclsPathToSusieFolder );
	PathAppend( szPathToSusiePlugin, _T("*.spi") );

	// Susieプラグインの取得

	YCFileFindSx			clffsSusie;
	std::vector<YCString>	vcPathToSusiePlugin;

	clffsSusie.FindFile( vcPathToSusiePlugin, rfclsPathToSusieFolder, _T("*.spi"), FALSE );

	// Susieプラグイン情報の取得

	YCIni				cliSusie( SBL_STR_INI_SUSIE );

	cliSusie.SetSection( _T("Plugins") );

	size_t				uIndex = 0;

	m_stsiMain.resize( vcPathToSusiePlugin.size() );

	for( size_t i = 0 ; i < vcPathToSusiePlugin.size() ; i++ )
	{
		SSusieInfo*			pstsiTarget = &m_stsiMain[uIndex];
		LPCTSTR				pszPathToTarget = vcPathToSusiePlugin[i];

		// SPIのロード

		if( !pstsiTarget->cllPlugin.Load( pszPathToTarget ) )
		{
			// ロード失敗

			continue;
		}

		// SPIの有効性の取得

		cliSusie.SetKey( PathFindFileName( pszPathToTarget ) );
		cliSusie.ReadDec( &pstsiTarget->bValidity, 1 );

		// SPIのファイルパスの取得

		pstsiTarget->clsPath = pszPathToTarget;

		// SPIのファイル名の取得

		pstsiTarget->clsName = PathFindFileName( pszPathToTarget );

		// SPI情報の取得

		char				szBuffer[1024];

		GetPluginInfoProc	GetPluginInfo = (GetPluginInfoProc) pstsiTarget->cllPlugin.GetProcAddress( _T("GetPluginInfo") );

		if( GetPluginInfo != NULL )
		{
			if( GetPluginInfo( 0, szBuffer, sizeof(szBuffer) ) != 0 )
			{
				// SPI情報の取得に成功

				pstsiTarget->clsVersion = szBuffer;
			}

			if( GetPluginInfo( 1, szBuffer, sizeof(szBuffer) ) != 0 )
			{
				// SPI情報の取得に成功

				pstsiTarget->clsInfo = szBuffer;
			}

			for( int nInfoNumber = 2 ; ; nInfoNumber += 2 )
			{
				if( GetPluginInfo( nInfoNumber, szBuffer, sizeof(szBuffer) ) == 0 )
				{
					// これ以上情報が存在しない

					break;
				}

				if( !pstsiTarget->clsSupportFormat.IsEmpty() )
				{
					pstsiTarget->clsSupportFormat += _T(";");
				}

				pstsiTarget->clsSupportFormat += szBuffer;
			}
		}

		// SPIのコンフィグ実装の有無の取得

		ConfigurationDlgProc	ConfigurationDlg = (ConfigurationDlgProc) pstsiTarget->cllPlugin.GetProcAddress( _T("ConfigurationDlg") );

		pstsiTarget->bConfig = (ConfigurationDlg != NULL);

		// 次に備える

		uIndex++;
	}

	// ロード出来た分だけにする

	m_stsiMain.resize( uIndex );
}

//////////////////////////////////////////////////////////////////////////////////////////
//	Susieプラグイン情報の保存

void	CSusie::SaveSpi()
{
	YCIni				cliSusie( SBL_STR_INI_SUSIE );

	cliSusie.SetSection( _T("Plugins") );

	// フォルダに存在しなくなったSusieプラグインの情報を残したままにしないよう削除

	cliSusie.DeleteSection();

	// Susieプラグイン情報の保存

	for( size_t i = 0 ; i < m_stsiMain.size() ; i++ )
	{
		SSusieInfo*			pstsiTarget = &m_stsiMain[i];

		cliSusie.SetKey( pstsiTarget->clsName );
		cliSusie.WriteDec( pstsiTarget->bValidity );
	}
}
