/*******************************************************************************
  TPI - flexible but useless plug-in framework.
  Copyright (C) 2002-2009 Silky

  This library is free software; you can redistribute it and/or modify it under
  the terms of the GNU Lesser General Public License as published by the Free
  Software Foundation; either version 2.1 of the License, or (at your option)
  any later version.

  This library is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
  for more details.

  You should have received a copy of the GNU Lesser General Public License along
  with this library; if not, write to the Free Software Foundation, Inc.,
  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

  $Id: calLibrary.cpp 147 2009-11-22 03:33:42Z sirakaba $
*******************************************************************************/

//******************************************************************************
//    Includes
//******************************************************************************

#include "../../common/header/plugin.h"
#include "../../common/header/plugin-extra.h"
#include "../../common/library/library.h"
#include <wx/dynlib.h>
#include <wx/config.h>
#include <wx/stdpaths.h>
#include <wx/xml/xml.h>
#include <windows.h>
#include "calLibrary.h"

//******************************************************************************
//    Global varients
//******************************************************************************

struct g_LibInfo
{
	HMODULE hLib;
	wxString szPrefix;
	int nLibIndex;
	wxXmlNode node;
	bool fUnicode;
	bool fCallback32bit;
	bool fRunning;
}	g_LibInfo, g_LibInfoAlt;

TPI_PROC g_prProc;
bool g_fExistUnicodeMode;

//******************************************************************************
//    Entry
//******************************************************************************

BOOL __stdcall DllMain(HMODULE, DWORD fdwReason, void *)
{
	switch (fdwReason)
	{
	case DLL_PROCESS_DETACH:
		::FreeLibrary(g_LibInfo.hLib);
		break;
	}
	return TRUE;
}

wxDateTime FileTimeToWxDateTime(FILETIME * pft)
{
	FILETIME ftLocal;
	SYSTEMTIME st;
	if (! ::FileTimeToLocalFileTime(pft, & ftLocal) || ! ::FileTimeToSystemTime(& ftLocal, & st))
	{
		return (time_t) 0;
	}
	wxDateTime dt(st.wDay, wxDateTime::Month(st.wMonth - 1), st.wYear, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
	return dt;
}

//******************************************************************************
//    Callback Wrapper
//******************************************************************************

BOOL __stdcall CallbackProc(HWND, unsigned int _uMsg, unsigned int _uState, void * _lpEis)
{
	if (_uMsg != ::RegisterWindowMessage((const wchar_t *) WM_ARCEXTRACT))
	{
		// 独自仕様などに対する予備コード。
		return TRUE;
	}

	// 構造体を初期化。
	TPI_PROCESSINFO piInfo;
	piInfo.uMessage = TPI_MESSAGE_STATUS;

	if (_lpEis != NULL)
	{
		if ((g_LibInfo.fRunning && g_LibInfo.fCallback32bit) || (g_LibInfoAlt.fRunning && g_LibInfoAlt.fCallback32bit))
		{
			if ((g_LibInfo.fRunning && g_LibInfo.fUnicode) || (g_LibInfoAlt.fRunning && g_LibInfoAlt.fUnicode))
			{
				// 初期化。
				EXTRACTINGINFOEXW * ex = (EXTRACTINGINFOEXW *) _lpEis;

				// EXTRACTINGINFOEXW -> TPI_FILEINFO変換。
				piInfo.fiInfo.dwCRC32           = ex->dwCRC;
				piInfo.fiInfo.tmModified.SetFromDOS(ex->wTime | (ex->wDate << 16));
				piInfo.fiInfo.llPackedSize      = ex->dwCompressedSize;
				piInfo.fiInfo.llUnpackedSize    = ex->exinfo.dwFileSize;
				piInfo.fiInfo.uOSType           = ex->uOSType;
				piInfo.fiInfo.wCompressRatio    = ex->wRatio;
				piInfo.fiInfo.szStoredName      = WC2String(ex->exinfo.szSourceFileName);
				piInfo.fiInfo.szMethod          = WC2String(ex->szMode);

				// EXTRACTINGINFOEXW -> TPI_PROCESSINFO変換。
				piInfo.llProcessedSize          = ex->exinfo.dwWriteSize;
				piInfo.fnDestination            = wxFileName(WC2String(ex->exinfo.szDestFileName));
			}
			else
			{
				// 初期化。
				EXTRACTINGINFOEX * ex = (EXTRACTINGINFOEX *) _lpEis;

				// EXTRACTINGINFOEX -> TPI_FILEINFO変換。
				piInfo.fiInfo.dwCRC32           = ex->dwCRC;
				piInfo.fiInfo.tmModified.SetFromDOS(ex->wTime | (ex->wDate << 16));
				piInfo.fiInfo.llPackedSize      = ex->dwCompressedSize;
				piInfo.fiInfo.llUnpackedSize    = ex->exinfo.dwFileSize;
				piInfo.fiInfo.uOSType           = ex->uOSType;
				piInfo.fiInfo.wCompressRatio    = ex->wRatio;
				piInfo.fiInfo.szStoredName      = UTF82String(ex->exinfo.szSourceFileName);
				piInfo.fiInfo.szMethod          = UTF82String(ex->szMode);

				// EXTRACTINGINFOEX -> TPI_PROCESSINFO変換。
				piInfo.llProcessedSize          = ex->exinfo.dwWriteSize;
				piInfo.fnDestination            = wxFileName(UTF82String(ex->exinfo.szDestFileName));
			}
			piInfo.fiInfo.dwAttribute       = 0;
		}
		else
		{
			if ((g_LibInfo.fRunning && g_LibInfo.fUnicode) || (g_LibInfoAlt.fRunning && g_LibInfoAlt.fUnicode))
			{
				// 初期化。
				EXTRACTINGINFOEX64W * ex = (EXTRACTINGINFOEX64W *) _lpEis;

				// EXTRACTINGINFOEX64W -> TPI_FILEINFO変換。
				piInfo.fiInfo.dwAttribute       = ex->dwAttributes;
				piInfo.fiInfo.dwCRC32           = ex->dwCRC;
				piInfo.fiInfo.tmAccess          = FileTimeToWxDateTime(& ex->ftAccessTime);
				piInfo.fiInfo.tmCreate          = FileTimeToWxDateTime(& ex->ftCreateTime);
				piInfo.fiInfo.tmModified        = FileTimeToWxDateTime(& ex->ftWriteTime);
				piInfo.fiInfo.llPackedSize      = ex->llCompressedSize;
				piInfo.fiInfo.llUnpackedSize    = ex->llFileSize;
				piInfo.fiInfo.uOSType           = ex->uOSType;
				piInfo.fiInfo.wCompressRatio    = ex->wRatio;
				piInfo.fiInfo.szStoredName      = WC2String(ex->exinfo.szSourceFileName);
				piInfo.fiInfo.szMethod          = WC2String(ex->szMode);

				// EXTRACTINGINFOEX64 -> TPI_PROCESSINFO変換。
				piInfo.llProcessedSize          = ex->llWriteSize;
				piInfo.fnDestination            = wxFileName(WC2String(ex->exinfo.szDestFileName));
			}
			else
			{
				// 初期化。
				EXTRACTINGINFOEX64 * ex = (EXTRACTINGINFOEX64 *) _lpEis;

				// EXTRACTINGINFOEX64 -> TPI_FILEINFO変換。
				piInfo.fiInfo.dwAttribute       = ex->dwAttributes;
				piInfo.fiInfo.dwCRC32           = ex->dwCRC;
				piInfo.fiInfo.tmAccess          = FileTimeToWxDateTime(& ex->ftAccessTime);
				piInfo.fiInfo.tmCreate          = FileTimeToWxDateTime(& ex->ftCreateTime);
				piInfo.fiInfo.tmModified        = FileTimeToWxDateTime(& ex->ftWriteTime);
				piInfo.fiInfo.llPackedSize      = ex->llCompressedSize;
				piInfo.fiInfo.llUnpackedSize    = ex->llFileSize;
				piInfo.fiInfo.uOSType           = ex->uOSType;
				piInfo.fiInfo.wCompressRatio    = ex->wRatio;
				piInfo.fiInfo.szStoredName      = UTF82String(ex->exinfo.szSourceFileName);
				piInfo.fiInfo.szMethod          = UTF82String(ex->szMode);

				// EXTRACTINGINFOEX64 -> TPI_PROCESSINFO変換。
				piInfo.llProcessedSize          = ex->llWriteSize;
				piInfo.fnDestination            = wxFileName(UTF82String(ex->exinfo.szDestFileName));
			}
		}
		piInfo.fiInfo.fnFileName        = wxFileName(piInfo.fiInfo.szStoredName);
	}

	// 数字の順でなく処理の順で並んでいることに注意。
	switch (_uState)
	{
	case ARCEXTRACT_OPEN:
		// 当該書庫の処理を開始。
		piInfo.uStatus = TPI_STATUS_OPENARCHIVE;
		break;
	case 5:
		// 当該ファイルの検索中。
		piInfo.uStatus = TPI_STATUS_SEEKFILE;
		break;
	case ARCEXTRACT_BEGIN:
		// 当該ファイルの処理を開始。
		piInfo.uStatus = TPI_STATUS_BEGINPROCESS;
		break;
	case ARCEXTRACT_INPROCESS:
		// 当該ファイルの処理を実行中。
		piInfo.uStatus = TPI_STATUS_INPROCESS;
		break;
	case 6:
		// 当該ファイルの処理を終了。
		piInfo.uStatus = TPI_STATUS_ENDPROCESS;
		break;
	case 7:
		// 当該書庫の検査中。
		piInfo.uStatus = TPI_STATUS_TESTARCHIVE;
		break;
	case ARCEXTRACT_COPY:
		// 当該書庫の書き戻し中。
		piInfo.uStatus = TPI_STATUS_COPYARCHIVE;
		break;
	case ARCEXTRACT_END:
		// 当該書庫の処理を終了。
		piInfo.uStatus = TPI_STATUS_CLOSEARCHIVE;
		break;
	}

	// コールバック関数に送信。
	if (g_prProc == NULL)
	{
		return TRUE;
	}

	return g_prProc(TPI_NOTIFY_COMMON, & piInfo) != TPI_CALLBACK_CANCEL;
}

//******************************************************************************
//    Inside Functions
//******************************************************************************

#define GetAPIAddress(name, unicode) GetProcAddress(g_LibInfo.hLib, (g_LibInfo.szPrefix + wxT(name) + ((unicode && g_LibInfo.fUnicode) ? wxT("W") : wxEmptyString)).char_str())
#define GetAPIAddressAlt(name, unicode) GetProcAddress(g_LibInfoAlt.hLib, (g_LibInfoAlt.szPrefix + wxT(name) + ((unicode && g_LibInfoAlt.fUnicode) ? wxT("W") : wxEmptyString)).char_str())

int ErrorCodeConvert(int nErrorCode)
{
	switch (nErrorCode)
	{
	case 0:                     return TPI_ERROR_SUCCESS;
	case -1:					return TPI_ERROR_S_ENDOFDATA;
	case ERROR_DISK_SPACE:      return TPI_ERROR_IO_ARC_WRITE;
	case ERROR_READ_ONLY:       return TPI_ERROR_IO_FILE_WRITE;
	case ERROR_USER_SKIP:       return TPI_ERROR_D_SKIPPED;
	case ERROR_UNKNOWN_TYPE:    return TPI_ERROR_IO_MISC;
	case ERROR_METHOD:          return TPI_ERROR_ARC_UNSUPPORTED;
	case ERROR_PASSWORD_FILE:   return TPI_ERROR_ARC_ENCRYPTED;
	case ERROR_VERSION:         return TPI_ERROR_ARC_UNSUPPORTED;
	case ERROR_FILE_CRC:        return TPI_ERROR_ARC_BROKEN_SUM;
	case ERROR_FILE_OPEN:       return TPI_ERROR_IO_FILE_OPEN;
	case ERROR_MORE_FRESH:      return TPI_ERROR_IO_FILE_MISC;
	case ERROR_NOT_EXIST:       return TPI_ERROR_IO_FILE_ACCESS;
	case ERROR_ALREADY_EXIST:   return TPI_ERROR_IO_FILE_ACCESS;
	case ERROR_TOO_MANY_FILES:  return TPI_ERROR_IO_ARC_MISC;
	case ERROR_MAKEDIRECTORY:   return TPI_ERROR_IO_DIR_OPEN;
	case ERROR_CANNOT_WRITE:    return TPI_ERROR_IO_FILE_WRITE;
	case ERROR_HUFFMAN_CODE:    return TPI_ERROR_ARC_BROKEN_MISC;
	case ERROR_COMMENT_HEADER:  return TPI_ERROR_ARC_BROKEN_HEADER;
	case ERROR_HEADER_CRC:      return TPI_ERROR_ARC_BROKEN_SUM;
	case ERROR_HEADER_BROKEN:   return TPI_ERROR_ARC_BROKEN_HEADER;
	case ERROR_ARC_FILE_OPEN:   return TPI_ERROR_IO_ARC_OPEN;
	case ERROR_NOT_ARC_FILE:    return TPI_ERROR_D_UNSUPPORTED;
	case ERROR_CANNOT_READ:     return TPI_ERROR_IO_ARC_READ;
	case ERROR_FILE_STYLE:      return TPI_ERROR_D_UNSUPPORTED;
	case ERROR_COMMAND_NAME:    return TPI_ERROR_UNDEFINED;
	case ERROR_MORE_HEAP_MEMORY:return TPI_ERROR_D_OUTOFMEMORY;
	case ERROR_ENOUGH_MEMORY:   return TPI_ERROR_D_OUTOFMEMORY;
	case ERROR_ALREADY_RUNNING: return TPI_ERROR_UNDEFINED;
	case ERROR_USER_CANCEL:     return TPI_ERROR_D_SKIPPED;
	case ERROR_HARC_ISNOT_OPENED: return TPI_ERROR_D_PARAMETER;
	case ERROR_NOT_SEARCH_MODE: return TPI_ERROR_D_PARAMETER;
	case ERROR_NOT_SUPPORT:     return TPI_ERROR_U_USE_LIBRARY;
	case ERROR_TIME_STAMP:      return TPI_ERROR_UNDEFINED;
	case ERROR_TMP_OPEN:        return TPI_ERROR_IO_TMP_OPEN;
	case ERROR_LONG_FILE_NAME:  return TPI_ERROR_IO_FILE_ACCESS;
	case ERROR_ARC_READ_ONLY:   return TPI_ERROR_IO_ARC_ACCESS;
	case ERROR_SAME_NAME_FILE:  return TPI_ERROR_UNDEFINED;
	case ERROR_NOT_FIND_ARC_FILE: return TPI_ERROR_IO_ARC_NOTFOUND;
	case ERROR_RESPONSE_READ:   return TPI_ERROR_IO_MISC_READ;
	case ERROR_NOT_FILENAME:    return TPI_ERROR_IO_FILE_NOTFOUND;
	case ERROR_TMP_COPY:        return TPI_ERROR_IO_TMP_COPY;
	case ERROR_EOF:             return TPI_ERROR_ARC_BROKEN_SIZE;
	case ERROR_ADD_TO_LARC:     return TPI_ERROR_UNDEFINED;
	case ERROR_TMP_BACK_SPACE:  return TPI_ERROR_IO_TMP_MOVE;
	case ERROR_SHARING:         return TPI_ERROR_IO_FILE_ACCESS;
	case ERROR_NOT_FIND_FILE:   return TPI_ERROR_IO_NOTFOUND;
	case ERROR_LOG_FILE:        return TPI_ERROR_IO_MISC_WRITE;
	case ERROR_NO_DEVICE:       return TPI_ERROR_IO_FILE_ACCESS;
	case ERROR_GET_ATTRIBUTES:  return TPI_ERROR_IO_FILE_GETINFO;
	case ERROR_SET_ATTRIBUTES:  return TPI_ERROR_IO_FILE_SETINFO;
	case ERROR_GET_INFORMATION: return TPI_ERROR_IO_FILE_GETINFO;
	case ERROR_GET_POINT:       return TPI_ERROR_IO_FILE_POINT;
	case ERROR_SET_POINT:       return TPI_ERROR_IO_FILE_POINT;
	case ERROR_CONVERT_TIME:    return TPI_ERROR_UNDEFINED;
	case ERROR_GET_TIME:        return TPI_ERROR_IO_FILE_GETINFO;
	case ERROR_SET_TIME:        return TPI_ERROR_IO_FILE_SETINFO;
	case ERROR_CLOSE_FILE:      return TPI_ERROR_IO_FILE_CLOSE;
	case ERROR_HEAP_MEMORY:     return TPI_ERROR_D_USEMEMORY;
	case ERROR_HANDLE:          return TPI_ERROR_UNDEFINED;
	case ERROR_TIME_STAMP_RANGE:return TPI_ERROR_UNDEFINED;
	case ERROR_MAKE_ARCHIVE:    return TPI_ERROR_ARC_BROKEN_MISC;
	case ERROR_NOT_CONFIRM_NAME:return TPI_ERROR_ARC_BROKEN_HEADER;
	case ERROR_UNEXPECTED_EOF:  return TPI_ERROR_ARC_BROKEN_HEADER;
	case ERROR_INVALID_END_MARK:return TPI_ERROR_ARC_BROKEN_SIZE;
	case ERROR_INVOLVED_LZH:    return TPI_ERROR_ARC_BROKEN_MISC;
	case ERROR_NO_END_MARK:     return TPI_ERROR_ARC_BROKEN_SIZE;
	case ERROR_HDR_INVALID_SIZE:return TPI_ERROR_ARC_BROKEN_HEADER;
	case ERROR_UNKNOWN_LEVEL:   return TPI_ERROR_ARC_UNSUPPORTED;
	case ERROR_BROKEN_DATA:     return TPI_ERROR_ARC_BROKEN_MISC;
	case ERROR_INVALID_PATH:    return TPI_ERROR_ARC_DANGER;
	case ERROR_TOO_BIG:         return TPI_ERROR_IO_FILE_WRITE;
	case ERROR_EXECUTABLE_FILE: return TPI_ERROR_ARC_DANGER;
	case ERROR_INVALID_VALUE:   return TPI_ERROR_UNDEFINED;
	case ERROR_HDR_EXPLOIT:     return TPI_ERROR_ARC_DANGER;
	case ERROR_HDR_NO_CRC:      return TPI_ERROR_ARC_BROKEN_HEADER;
	case ERROR_HDR_NO_NAME:     return TPI_ERROR_ARC_BROKEN_HEADER;
	default:                    return TPI_ERROR_UNDEFINED;
	}
}

int CalSetCallbackProc(bool fAlt)
{
	// 先にコールバック関数の設定を解除しておく。
	FARPROC fpProc;
	if (fAlt)
	{
		fpProc = ::GetAPIAddressAlt("KillOwnerWindowEx64", false);
		g_LibInfoAlt.fCallback32bit = fpProc == NULL;
		if (g_LibInfoAlt.fCallback32bit)
		{
			fpProc = ::GetAPIAddressAlt("KillOwnerWindowEx", false);
		}
	}
	else
	{
		fpProc = ::GetAPIAddress("KillOwnerWindowEx64", false);
		g_LibInfo.fCallback32bit = fpProc == NULL;
		if (g_LibInfo.fCallback32bit)
		{
			fpProc = ::GetAPIAddress("KillOwnerWindowEx", false);
		}
	}
	if (fpProc == NULL)
	{
		return TPI_ERROR_U_USE_LIBRARY;
	}
	// エラーでも無視する。
	((BOOL (__stdcall *)(HWND)) fpProc)(NULL);

	// 続いてコールバック関数の設定。
	if (fAlt)
	{
		fpProc = ::GetAPIAddressAlt("SetOwnerWindowEx64", false);
		g_LibInfoAlt.fCallback32bit = fpProc == NULL;
		if (g_LibInfoAlt.fCallback32bit)
		{
			fpProc = ::GetAPIAddressAlt("SetOwnerWindowEx", true);
		}
	}
	else
	{
		fpProc = ::GetAPIAddress("SetOwnerWindowEx64", false);
		g_LibInfo.fCallback32bit = fpProc == NULL;
		if (g_LibInfo.fCallback32bit)
		{
			fpProc = ::GetAPIAddress("SetOwnerWindowEx", true);
		}
	}

	return (fpProc == NULL || ! ((fAlt ? g_LibInfoAlt.fCallback32bit : g_LibInfo.fCallback32bit)
		? ((BOOL (__stdcall *)(HWND, ARCHIVERPROC *       )) fpProc)(NULL, (ARCHIVERPROC *) CallbackProc)
		: ((BOOL (__stdcall *)(HWND, ARCHIVERPROC *, DWORD)) fpProc)(NULL, (ARCHIVERPROC *) CallbackProc, (fAlt ? g_LibInfoAlt.fUnicode : g_LibInfo.fUnicode) ? sizeof(EXTRACTINGINFOEX64W) : sizeof(EXTRACTINGINFOEX64))))
		? TPI_ERROR_U_USE_LIBRARY : TPI_ERROR_SUCCESS;
}

//******************************************************************************
//    Functions
//******************************************************************************

#ifdef __cplusplus
extern "C"
{
#endif

int __stdcall GetPluginInformation
(
	unsigned int _uInfoId,
	wxULongLong,
	void * _pPtr
)
{
	if (_pPtr == NULL)
	{
		return TPI_ERROR_D_PARAMETER;
	}
	switch (_uInfoId)
	{
	case TPI_INFO_VERSION_MAJOR:
	case TPI_INFO_VERSION_MINOR:
		* (int *) _pPtr = 0;
		break;
	case TPI_INFO_VERSION_API:
		* (int *) _pPtr = 2;
		break;
	default:
		return TPI_ERROR_D_UNSUPPORTED;
	}
	return TPI_ERROR_SUCCESS;
}

int __stdcall GetFormatInformation(TPI_FORMATINFO * _fiInfo, bool _bFirst)
{
	static unsigned int s_uFileID;
	wxStandardPaths p;
	wxXmlDocument config(wxPathOnly(p.GetExecutablePath()) + wxT("/lib/calLibrary.xml"));
	// 一気に先頭のライブラリの情報を取得。
	wxXmlNode * xmlLibrary = config.GetRoot()->GetChildren();

	if (_bFirst)
	{
		// xml解析開始。
		s_uFileID = 0;
	}
	else
	{
		for (unsigned int i = 0; i < s_uFileID && xmlLibrary != NULL; i++)
		{
			xmlLibrary = xmlLibrary->GetNext();
		}
	}

	if (xmlLibrary == NULL || xmlLibrary->GetName() != wxT("library"))
	{
		// データの終端に達した場合。
		return TPI_ERROR_S_ENDOFDATA;
	}

	MakeFormatInfo(wxT("calLibrary"), _fiInfo, xmlLibrary, s_uFileID++);

	return TPI_ERROR_SUCCESS;
}

int __stdcall LoadPlugin
(
	const wxString & _szArcName,
	wxULongLong _llSubOption
)
{
	// xml解析開始。
	wxStandardPaths p;
	wxXmlDocument config(wxPathOnly(p.GetExecutablePath()) + wxT("/lib/calLibrary.xml"));
	if (! config.IsOk())
	{
		return TPI_ERROR_UNDEFINED;
	}
	// 一気に先頭のライブラリの情報を取得。
	wxXmlNode * xmlLibrary = config.GetRoot()->GetChildren();

	// 対象が存在するならば対応するライブラリを調査、
	// 対象が存在しないならば指示されたライブラリをロード。
	if (! ::wxFileExists(_szArcName))
	{
		// 適当な位置まで移動。
		for (g_LibInfo.nLibIndex = 0; g_LibInfo.nLibIndex < _llSubOption && xmlLibrary != NULL; g_LibInfo.nLibIndex++)
		{
			xmlLibrary = xmlLibrary->GetNext();
		}
		if (xmlLibrary == NULL || xmlLibrary->GetName() != wxT("library"))
		{
			// xml文法エラー。
			return TPI_ERROR_UNDEFINED;
		}
		g_LibInfo.node = * xmlLibrary;

		// ライブラリをロード。
		g_LibInfo.hLib = ::LoadLibrary((const WCHAR *) xmlLibrary->GetPropVal(wxT("name"), wxEmptyString).c_str());
		if (g_LibInfo.hLib == NULL)
		{
			return TPI_ERROR_U_LOAD_LIBRARY;
		}
		g_LibInfo.szPrefix = xmlLibrary->GetPropVal(wxT("prefix"), wxEmptyString);
		g_LibInfo.fUnicode = xmlLibrary->GetPropVal(wxT("unicode"), wxT("0")) == wxT("1");

		// 代替ライブラリもロード。ロードできなくてもエラーにはしない。
		g_LibInfoAlt.szPrefix = xmlLibrary->GetPropVal(wxT("prefix-alt"), wxEmptyString);
		g_LibInfoAlt.fUnicode = xmlLibrary->GetPropVal(wxT("unicode-alt"), wxT("0")) == wxT("1");
		g_LibInfoAlt.hLib = ::LoadLibrary((const WCHAR *) xmlLibrary->GetPropVal(wxT("name-alt"), wxEmptyString).c_str());

		return TPI_ERROR_SUCCESS;
	}

	// 無限ループに陥らないよう上限を設定。
	for (g_LibInfo.nLibIndex = 0; g_LibInfo.nLibIndex < 300 && xmlLibrary != NULL; g_LibInfo.nLibIndex++)
	{
		// ライブラリをロード。
		g_LibInfo.hLib = ::LoadLibrary((const WCHAR *) xmlLibrary->GetPropVal(wxT("name"), wxEmptyString).c_str());
		if (g_LibInfo.hLib == NULL)
		{
			xmlLibrary = xmlLibrary->GetNext();
			continue;
		}
		g_LibInfo.szPrefix = xmlLibrary->GetPropVal(wxT("prefix"), wxEmptyString);
		g_LibInfo.fUnicode = xmlLibrary->GetPropVal(wxT("unicode"), wxT("0")) == wxT("1");

		// 代替ライブラリもロード。ロードできなくてもエラーにはしない。
		g_LibInfoAlt.szPrefix = xmlLibrary->GetPropVal(wxT("prefix-alt"), wxEmptyString);
		g_LibInfoAlt.fUnicode = xmlLibrary->GetPropVal(wxT("unicode-alt"), wxT("0")) == wxT("1");
		g_LibInfoAlt.hLib = ::LoadLibrary((const WCHAR *) xmlLibrary->GetPropVal(wxT("name-alt"), wxEmptyString).c_str());
		g_LibInfo.node = * xmlLibrary;

		// 書庫に対応しているかチェック。
		if (CheckArchive(_szArcName, NULL) == TPI_ERROR_SUCCESS)
		{
			// 対応していれば処理を終了。
			return TPI_ERROR_SUCCESS;
		}

		::FreeLibrary(g_LibInfo.hLib);
		xmlLibrary = xmlLibrary->GetNext();
	}

	return TPI_ERROR_U_LOAD_LIBRARY;
}

int __stdcall FreePlugin
(
	void * // _pReserved
)
{
	::FreeLibrary(g_LibInfo.hLib);
	::FreeLibrary(g_LibInfoAlt.hLib);
	return TPI_ERROR_SUCCESS;
}

int __stdcall CheckArchive
(
	const wxString & _szArcName,
	int * _nFileCount
)
{
	// Unicodeモードに設定。
	FARPROC	fpProc = ::GetAPIAddress("SetUnicodeMode", false);
	g_fExistUnicodeMode = fpProc != NULL;
	if (g_fExistUnicodeMode)
	{
		((BOOL (__stdcall *)(BOOL)) fpProc)(TRUE);
	}

	fpProc = ::GetAPIAddress("CheckArchive", true);
	if (fpProc == NULL)
	{
		return TPI_ERROR_U_USE_LIBRARY;
	}

	if (! (g_LibInfo.fUnicode
		? ((BOOL (__stdcall *)(const wchar_t *, const int)) fpProc)(_szArcName.wchar_str(), 0)
		: ((BOOL (__stdcall *)(const char    *, const int)) fpProc)(_szArcName.ToUTF8(),    0)
	))
	{
		return TPI_ERROR_D_UNSUPPORTED;
	}

	if (_nFileCount != NULL)
	{
		fpProc = ::GetAPIAddress("GetFileCount", true);
		if (fpProc == NULL)
		{
			return TPI_ERROR_U_USE_LIBRARY;
		}

		* _nFileCount = g_LibInfo.fUnicode
			? ((int (__stdcall *)(const wchar_t *)) fpProc)(_szArcName.wchar_str())
			: ((int (__stdcall *)(const char *)) fpProc)(_szArcName.ToUTF8());
		if (* _nFileCount == -1)
		{
			return TPI_ERROR_ARC_UNSUPPORTED;
		}
	}

	return TPI_ERROR_SUCCESS;
}

int __stdcall OpenArchive
(
	const wxString & _szArcName,
	void * * _hArchive
)
{
	FARPROC fpProc = ::GetAPIAddress("OpenArchive", true);
	if (fpProc == NULL)
	{
		return TPI_ERROR_U_USE_LIBRARY;
	}

	* _hArchive = g_LibInfo.fUnicode
		? ((void * (__stdcall *)(const HWND, const wchar_t *, const DWORD)) fpProc)(NULL, _szArcName.wchar_str(), 0)
		: ((void * (__stdcall *)(const HWND, const char    *, const DWORD)) fpProc)(NULL, _szArcName.ToUTF8(),  0);
	return _hArchive == NULL ? TPI_ERROR_UNDEFINED : TPI_ERROR_SUCCESS;
}

int __stdcall CloseArchive
(
	void * _hArchive
)
{
	FARPROC	fpProc = ::GetAPIAddress("CloseArchive", false);
	return fpProc == NULL ? TPI_ERROR_U_USE_LIBRARY : ErrorCodeConvert(((int (__stdcall *)(void *)) fpProc)(_hArchive));
}

int __stdcall GetFileInformation
(
	void * _hArchive,
	TPI_FILEINFO * _fiInfo,
	bool _bFirst
)
{
	static unsigned int s_uFileID;
	static FARPROC fpNext, fpAttr;
	int nErrorCode;
	INDIVIDUALINFO iiInfo;
	INDIVIDUALINFOW iiInfoW;
	memset(& iiInfo,  0, sizeof(iiInfo));
	memset(& iiInfoW, 0, sizeof(iiInfoW));

	if (_bFirst)
	{
		s_uFileID = 0;
		FARPROC fpProc = ::GetAPIAddress("FindFirst", true);
		fpNext = ::GetAPIAddress("FindNext", true);
		fpAttr = ::GetAPIAddress("GetAttribute", false);
		if (fpProc == NULL)
		{
			return TPI_ERROR_U_USE_LIBRARY;
		}

		nErrorCode = g_LibInfo.fUnicode
			? ((int (__stdcall *)(void *, const wchar_t *, LPINDIVIDUALINFOW)) fpProc)(_hArchive, L"*", & iiInfoW)
			: ((int (__stdcall *)(void *, const char *,    LPINDIVIDUALINFO))  fpProc)(_hArchive,  "*", & iiInfo);
	}
	else
	{
		if (fpNext == NULL)
		{
			return TPI_ERROR_U_USE_LIBRARY;
		}

		nErrorCode = g_LibInfo.fUnicode
			? ((int (__stdcall *)(void *, LPINDIVIDUALINFOW)) fpNext)(_hArchive, & iiInfoW)
			: ((int (__stdcall *)(void *, LPINDIVIDUALINFO))  fpNext)(_hArchive, & iiInfo);
	}

	nErrorCode = ErrorCodeConvert(nErrorCode);
	if (nErrorCode == TPI_ERROR_SUCCESS)
	{
		_fiInfo->dwAttribute = (fpAttr == NULL) ? 0 : ((int (__stdcall *)(void *)) fpAttr)(_hArchive);
		if ((signed) _fiInfo->dwAttribute == -1)
		{
			_fiInfo->dwAttribute = 0;
		}

		if (g_LibInfo.fUnicode)
		{
			_fiInfo->dwCRC32        = iiInfoW.dwCRC;
			_fiInfo->uOSType        = iiInfoW.uOSType;
			_fiInfo->llPackedSize   = iiInfoW.dwCompressedSize;
			_fiInfo->llUnpackedSize = iiInfoW.dwOriginalSize;
			_fiInfo->tmModified.SetFromDOS(MAKELONG(iiInfoW.wTime, iiInfoW.wDate));
			_fiInfo->szStoredName   = WC2String(iiInfoW.szFileName);
			_fiInfo->szMethod       = WC2String(iiInfoW.szMode);
			_fiInfo->wCompressRatio = iiInfoW.wRatio;
		}
		else
		{
			_fiInfo->dwCRC32        = iiInfo.dwCRC;
			_fiInfo->uOSType        = iiInfo.uOSType;
			_fiInfo->llPackedSize   = iiInfo.dwCompressedSize;
			_fiInfo->llUnpackedSize = iiInfo.dwOriginalSize;
			_fiInfo->tmModified.SetFromDOS(MAKELONG(iiInfo.wTime, iiInfo.wDate));
			_fiInfo->szStoredName   = UTF82String(iiInfo.szFileName);
			_fiInfo->szMethod       = UTF82String(iiInfo.szMode);
			_fiInfo->wCompressRatio = iiInfo.wRatio;
		}
		_fiInfo->llFileID       = s_uFileID++;
		_fiInfo->fnFileName     = wxFileName(_fiInfo->szStoredName);
//		_fiInfo->ftAccessTime   = 0;
//		_fiInfo->ftCreateTime   = 0;
	}

	return nErrorCode;
}

int __stdcall GetArchiveInformation
(
	void * _hArchive,
	TPI_ARCHIVEINFO * _aiInfo
)
{
	FARPROC	fpProc = ::GetAPIAddress("GetArcFileName", true);
	if (fpProc != NULL)
	{
		// TODO : ファイル名の文字数制限を撤廃。
		wxString s;
		if (g_LibInfo.fUnicode)
		{
			wchar_t sz[2049];
			((int (__stdcall *)(void *, wchar_t *, const int)) fpProc)(_hArchive, sz, 2048);
			s = WC2String(sz);
		}
		else
		{
			char sz[2049];
			((int (__stdcall *)(void *, char *, const int)) fpProc)(_hArchive, sz, 2048);
			s = UTF82String(sz);
		}
		_aiInfo->fnArchive = wxFileName(s);
	}

	fpProc = ::GetAPIAddress("GetArcFileSizeEx", false);
	if (fpProc != NULL)
	{
		((BOOL (__stdcall *)(void *, LONGLONG *)) fpProc)(_hArchive, (LONGLONG *) & _aiInfo->llFileSize);
	}
	else
	{
		fpProc = ::GetAPIAddress("GetArcFileSize", false);
		if (fpProc != NULL)
		{
			_aiInfo->llFileSize = ((DWORD (__stdcall *)(void *)) fpProc)(_hArchive);
		}
	}

	fpProc = ::GetAPIAddress("GetArcOriginalSizeEx", false);
	if (fpProc != NULL)
	{
		((BOOL (__stdcall *)(void *, LONGLONG *)) fpProc)(_hArchive, (LONGLONG *) & _aiInfo->llUnpackedSize);
	}
	else
	{
		fpProc = ::GetAPIAddress("GetArcOriginalSize", false);
		if (fpProc != NULL)
		{
			_aiInfo->llUnpackedSize = ((DWORD (__stdcall *)(void *)) fpProc)(_hArchive);
		}
	}

	fpProc = ::GetAPIAddress("GetArcCompressedSizeEx", false);
	if (fpProc != NULL)
	{
		((BOOL (__stdcall *)(void *, LONGLONG *)) fpProc)(_hArchive, (LONGLONG *) & _aiInfo->llPackedSize);
	}
	else
	{
		fpProc = ::GetAPIAddress("GetArcCompressedSize", false);
		if (fpProc != NULL)
		{
			_aiInfo->llPackedSize = ((DWORD (__stdcall *)(void *)) fpProc)(_hArchive);
		}
	}

	fpProc = ::GetAPIAddress("GetArcReadSizeEx", false);
	if (fpProc != NULL)
	{
		((BOOL (__stdcall *)(void *, LONGLONG *)) fpProc)(_hArchive, (LONGLONG *) & _aiInfo->llReadSize);
	}
	else
	{
		fpProc = ::GetAPIAddress("GetArcReadSize", false);
		if (fpProc != NULL)
		{
			_aiInfo->llReadSize = ((DWORD (__stdcall *)(void *)) fpProc)(_hArchive);
		}
	}

	fpProc = ::GetAPIAddress("GetArcRatio", false);
	if (fpProc != NULL)
	{
		_aiInfo->wCompressRatio = ((WORD (__stdcall *)(void *)) fpProc)(_hArchive);
	}

	FILETIME ft;
	fpProc = ::GetAPIAddress("GetArcAccessTimeEx", false);
	if (fpProc != NULL)
	{
		((BOOL (__stdcall *)(void *, FILETIME *)) fpProc)(_hArchive, & ft);
		_aiInfo->tmAccess = FileTimeToWxDateTime(& ft);
	}

	fpProc = ::GetAPIAddress("GetArcCreatedTimeEx", false);
	if (fpProc != NULL)
	{
		((BOOL (__stdcall *)(void *, FILETIME *)) fpProc)(_hArchive, & ft);
		_aiInfo->tmCreate = FileTimeToWxDateTime(& ft);
	}

	fpProc = ::GetAPIAddress("GetArcWriteTimeEx", false);
	if (fpProc != NULL)
	{
		((BOOL (__stdcall *)(void *, FILETIME *)) fpProc)(_hArchive, & ft);
		_aiInfo->tmModified = FileTimeToWxDateTime(& ft);
	}

	fpProc = ::GetAPIAddress("GetArcOSType", false);
	if (fpProc != NULL)
	{
		_aiInfo->uOSType = ((UINT (__stdcall *)(void *)) fpProc)(_hArchive);
	}

	fpProc = ::GetAPIAddress("IsSFXFile", false);
	if (fpProc != NULL)
	{
		_aiInfo->nSFXType = ((int (__stdcall *)(void *)) fpProc)(_hArchive);
	}
	if (fpProc == NULL || _aiInfo->nSFXType < 0)
	{
		// エラーの場合はとりあえず通常の書庫とみなす。
		_aiInfo->nSFXType = 0;
	}

	// 形式に関する情報を取得。
	MakeFormatInfo(wxT("calLibrary"), & _aiInfo->fiInfo, & g_LibInfo.node, 0);

	return TPI_ERROR_SUCCESS;
}

int __stdcall Command
(
	unsigned int _uCommand,
	TPI_SWITCHES * _swInfo,
	const wxString & _szArcName,
	const wxArrayString & _szFiles
)
{
	// Iniからコマンドラインを取得。
	wxString szPath, szCommandLine;

	// APIアドレス取得。
	g_LibInfoAlt.fRunning
		= g_LibInfo.node.GetPropVal(
			_uCommand == TPI_COMMAND_ADD     ? wxT("add-alt") :
			_uCommand == TPI_COMMAND_EXTRACT ? wxT("extract-alt") : 
			_uCommand == TPI_COMMAND_DELETE  ? wxT("delete-alt") : 
			_uCommand == TPI_COMMAND_UPDATE  ? wxT("update-alt") : 
			_uCommand == TPI_COMMAND_TEST    ? wxT("test-alt") : 
			_uCommand == TPI_COMMAND_REPAIR  ? wxT("repair-alt") : 
			_uCommand == TPI_COMMAND_MOVE    ? wxT("move-alt") : 
			_uCommand == TPI_COMMAND_SFX     ? wxT("sfx-alt") : 
			_uCommand == TPI_COMMAND_UNSFX   ? wxT("unsfx-alt") : wxEmptyString, & szCommandLine);
	g_LibInfo.fRunning
		= g_LibInfo.node.GetPropVal(
			_uCommand == TPI_COMMAND_ADD     ? wxT("add") :
			_uCommand == TPI_COMMAND_EXTRACT ? wxT("extract") : 
			_uCommand == TPI_COMMAND_DELETE  ? wxT("delete") : 
			_uCommand == TPI_COMMAND_UPDATE  ? wxT("update") : 
			_uCommand == TPI_COMMAND_TEST    ? wxT("test") : 
			_uCommand == TPI_COMMAND_REPAIR  ? wxT("repair") : 
			_uCommand == TPI_COMMAND_MOVE    ? wxT("move") : 
			_uCommand == TPI_COMMAND_SFX     ? wxT("sfx") : 
			_uCommand == TPI_COMMAND_UNSFX   ? wxT("unsfx") : wxEmptyString, & szCommandLine);

	FARPROC fpProc =
		g_LibInfo.fRunning    ? ::GetAPIAddress(   "", true) :
		g_LibInfoAlt.fRunning ? ::GetAPIAddressAlt("", true) : NULL;
	if (fpProc == NULL || szCommandLine.IsEmpty())
	{
		return TPI_ERROR_U_USE_LIBRARY;
	}

	// コマンドライン・レスポンスファイル作成。
	wxString
		szResponceFileName = MakeResponceFile(_szFiles, g_LibInfo.node.GetPropVal(wxT("quote-resp"), wxT("1")) == wxT("1")),
		szCommandLineSend  = MakeCommandLineSend(szCommandLine, _szArcName, _swInfo, _szFiles, szResponceFileName);

	// コマンドライン実行。
	int nErrorCode;
	wxString szOutput;
	if ((g_LibInfo.fRunning && g_LibInfo.fUnicode) || (g_LibInfoAlt.fRunning && g_LibInfoAlt.fUnicode))
	{
		wchar_t szTmpOut[2049];
		nErrorCode = ((int (__stdcall *)(const HWND, const wchar_t *, wchar_t *, DWORD)) fpProc)(NULL, szCommandLineSend.wchar_str(), szTmpOut, 2048);
		szOutput = WC2String(szTmpOut);
	}
	else
	{
		char szTmpOut[2049];
		nErrorCode = ((int (__stdcall *)(const HWND, const char *, char *, DWORD)) fpProc)(NULL, szCommandLineSend.ToUTF8(), szTmpOut, 2048);
		szOutput = UTF82String(szTmpOut);
	}

	// レスポンスファイル削除。
	::wxRemoveFile(szResponceFileName);

	if (nErrorCode != 0)
	{
		MessageBox(NULL, wxString::Format(wxT("Error :\n%x\n\nCommandLine:\n%s\n\nOutput:\n%s"), nErrorCode, szCommandLineSend.c_str(), szOutput.c_str()), NULL, 0);
	}
	return ErrorCodeConvert(nErrorCode);
}

int __stdcall SetCallbackProc
(
	TPI_PROC _prArcProc
)
{
	// ポインタを保存。
	if (_prArcProc == NULL)
	{
		return TPI_ERROR_D_PARAMETER;
	}
	g_prProc = * _prArcProc;

	// コールバック関数を設定。
	CalSetCallbackProc(true);
	return CalSetCallbackProc(false);
}

#ifdef __cplusplus
}
#endif
