/*******************************************************************************
  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-wx.cpp,v 1.10 2009/08/09 07:46:16 sirakaba Exp $
*******************************************************************************/

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

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

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

struct g_LibInfo
{
	HMODULE hLib;
	HMODULE hLibAlt;
	wxString szPrefix;
	wxString szPrefixAlt;
	wxULongLong nLibIndex;
	long nExFlag;
}	g_LibInfo;

TPI_PROC g_prProc;
bool g_fCallback32bit;
wxString g_szAppPath;

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

#ifdef __LINUX__
void __attribute__((constructor)) Attach(void)
{
	wxStandardPaths p;
	g_szAppPath = wxPathOnly(p.GetExecutablePath());
}

void __attribute__((destructor)) Detach(void)
{
	::FreeLibrary(g_LibInfo.hLib);
}
#else
BOOL __stdcall DllMain(HMODULE, DWORD fdwReason, void *)
{
	switch (fdwReason)
	{
	case DLL_PROCESS_ATTACH:
	{
		wxStandardPaths p;
		g_szAppPath = wxPathOnly(p.GetExecutablePath());
		break;
	}
	case DLL_PROCESS_DETACH:
		::FreeLibrary(g_LibInfo.hLib);
		break;
	}
	return TRUE;
}
#endif

wxDateTime FileTimeToWxDateTime(FILETIME * pft)
{
#ifdef __WINDOWS__
	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;
#else
    return (time_t) 0;
#endif
}

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

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

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

	if (_lpEis != NULL)
	{
		if (g_fCallback32bit)
		{
			// 初期化。
			EXTRACTINGINFOEX * ex = (EXTRACTINGINFOEX *) _lpEis;
			wxString szFileName(ex->exinfo.szSourceFileName, wxConvLibc), szMethod(ex->szMode, wxConvLibc), szDestFileName(ex->exinfo.szDestFileName, wxConvLibc);

			// EXTRACTINGINFOEX -> TPI_FILEINFO変換。
			piInfo.fiInfo.dwAttribute       = 0;
			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      = szFileName;
			piInfo.fiInfo.fnFileName        = wxFileName::FileName(szFileName);
			piInfo.fiInfo.szMethod          = szMethod;

			// EXTRACTINGINFOEX -> TPI_PROCESSINFO変換。
			piInfo.llProcessedSize          = ex->exinfo.dwWriteSize;
			piInfo.fnDestination            = wxFileName::FileName(szDestFileName);
		}
		else
		{
			// 初期化。
			EXTRACTINGINFOEX64 * ex = (EXTRACTINGINFOEX64 *) _lpEis;
			wxString szFileName(ex->exinfo.szSourceFileName, wxConvLibc), szMethod(ex->szMode, wxConvLibc), szDestFileName(ex->exinfo.szDestFileName, wxConvLibc);

			// 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      = szFileName;
			piInfo.fiInfo.fnFileName        = wxFileName::FileName(szFileName);
			piInfo.fiInfo.szMethod          = szMethod;

			// EXTRACTINGINFOEX64 -> TPI_PROCESSINFO変換。
			piInfo.llProcessedSize          = ex->llWriteSize;
			piInfo.fnDestination            = wxFileName::FileName(szDestFileName);
		}
	}

	// 数字の順でなく処理の順で並んでいることに注意。
	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;
	}

	// 拡張フラグの値により逆転させる。
	bool bRetVal = g_prProc(TPI_NOTIFY_COMMON, & piInfo) != TPI_CALLBACK_CANCEL;
	return g_LibInfo.nExFlag == 1 ? ! bRetVal : bRetVal;
}

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

int CalErrorCodeConvert(int nCalErrorCode)
{
	switch (nCalErrorCode)
	{
	case 0:                     return TPI_ERROR_SUCCESS;
	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_UNDEFINED;
	case ERROR_TOO_BIG:         return TPI_ERROR_IO_FILE_WRITE;
	case ERROR_EXECUTABLE_FILE: return TPI_ERROR_UNDEFINED;
	case ERROR_INVALID_VALUE:   return TPI_ERROR_UNDEFINED;
	default:                    return TPI_ERROR_UNDEFINED;
	}
}

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

#ifdef __cplusplus
extern "C"
{
#endif

int __stdcall GetPluginInformation
(
	unsigned int _uInfoId,
	wxULongLong _llSubOption,
	void * _pPtr
)
{
	// パス設定。TODO : 削除。
	wxStandardPaths p;
	g_szAppPath = wxPathOnly(p.GetExecutablePath());

	if (_pPtr == NULL)
	{
		return TPI_ERROR_D_PARAMETER;
	}
	switch (LOWORD(_uInfoId))
	{
	case TPI_INFO_VERSION_MAJOR:
	case TPI_INFO_VERSION_MINOR:
		* (int *) _pPtr = 0;
		break;
	case TPI_INFO_VERSION_API:
		* (int *) _pPtr = 2;
		break;
	case TPI_INFO_SUPPORTED_TYPE:
	case TPI_INFO_SUPPORTED_SUFFIX:
	{
		// xml解析開始。
		wxXmlDocument config(g_szAppPath + wxT("/lib/calLibrary.xml"));
		// 一気に先頭のライブラリの情報を取得。
		wxXmlNode * xmlLibrary = config.GetRoot()->GetChildren();

		// 適当な位置まで移動。
		for (wxULongLong i = 0; i < _llSubOption && xmlLibrary != NULL; i++)
		{
			xmlLibrary = xmlLibrary->GetNext();
		}
		if (xmlLibrary == NULL || xmlLibrary->GetName() != wxT("library"))
		{
			// xml文法エラー。
			return TPI_ERROR_UNDEFINED;
		}

		* (wxString *) _pPtr = xmlLibrary->GetPropVal(LOWORD(_uInfoId) == TPI_INFO_SUPPORTED_TYPE ? wxT("typename") : wxT("suffix"), wxEmptyString);
		break;
	}
	default:
		return TPI_ERROR_D_UNSUPPORTED;
	}
	return TPI_ERROR_SUCCESS;
}

int __stdcall LoadPlugin
(
	const wxString & _szArcName,
	wxULongLong _llSubOption
)
{
	// パス設定。
	wxStandardPaths p;
	g_szAppPath = wxPathOnly(p.GetExecutablePath());

	// xml解析開始。
	wxXmlDocument config(g_szAppPath + wxT("/lib/calLibrary.xml"));
	if (! config.IsOk())
	{
		return TPI_ERROR_UNDEFINED;
	}
	// 一気に先頭のライブラリの情報を取得。
	wxXmlNode * xmlLibrary = config.GetRoot()->GetChildren();

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

		// ライブラリをロード。
		g_LibInfo.szPrefix = xmlLibrary->GetPropVal(wxT("prefix"), wxEmptyString);
#ifdef __LINUX__
		g_LibInfo.hLib = ::LoadLibrary((const WCHAR *) xmlLibrary->GetPropVal(wxT("name"), wxEmptyString).c_str());
#else
		g_LibInfo.hLib = ::LoadLibrary(xmlLibrary->GetPropVal(wxT("name"), wxEmptyString).c_str());
#endif

		// 代替ライブラリもロード。ロードできなくてもエラーにはしない。
		g_LibInfo.szPrefixAlt = xmlLibrary->GetPropVal(wxT("prefix-alt"), wxEmptyString);
#ifdef __LINUX__
		g_LibInfo.hLibAlt = ::LoadLibrary((const WCHAR *) xmlLibrary->GetPropVal(wxT("name-alt"), wxEmptyString).c_str());
#else
		g_LibInfo.hLibAlt = ::LoadLibrary(xmlLibrary->GetPropVal(wxT("name-alt"), wxEmptyString).c_str());
#endif

		// 特殊フラグ取得。
		xmlLibrary->GetPropVal(wxT("exflag"), wxT("0")).ToLong(& g_LibInfo.nExFlag);

		return (g_LibInfo.hLib != NULL) ? TPI_ERROR_SUCCESS : TPI_ERROR_U_LOAD_LIBRARY;
	}

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

		// 代替ライブラリもロード。ロードできなくてもエラーにはしない。
		g_LibInfo.szPrefixAlt = xmlLibrary->GetPropVal(wxT("prefix-alt"), wxEmptyString);
#ifdef __LINUX__
		g_LibInfo.hLibAlt = ::LoadLibrary((const WCHAR *) xmlLibrary->GetPropVal(wxT("name-alt"), wxEmptyString).c_str());
#else
		g_LibInfo.hLibAlt = ::LoadLibrary(xmlLibrary->GetPropVal(wxT("name-alt"), wxEmptyString).c_str());
#endif

		// 特殊フラグ取得。
		xmlLibrary->GetPropVal(wxT("exflag"), wxT("0")).ToLong(& g_LibInfo.nExFlag);

		// 書庫に対応しているかチェック。
		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);
	return TPI_ERROR_SUCCESS;
}

int __stdcall CheckArchive
(
	const wxString & _szArcName,
	int * _nFileCount
)
{
	wxString szAPIName = g_LibInfo.szPrefix + wxT("CheckArchive");
	FARPROC	fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
	if (fpProc == NULL)
	{
		return TPI_ERROR_U_USE_LIBRARY;
	}

	if (! ((BOOL (__stdcall *)(const char *, const int)) fpProc)(_szArcName.char_str(), 0))
	{
		return TPI_ERROR_D_UNSUPPORTED;
	}

	if (_nFileCount != NULL)
	{
		szAPIName = g_LibInfo.szPrefix + wxT("GetFileCount");
		fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
		if (fpProc == NULL)
		{
			return TPI_ERROR_U_USE_LIBRARY;
		}

		* _nFileCount = ((int (__stdcall *)(const char *)) fpProc)(_szArcName.char_str());
		if (* _nFileCount == -1)
		{
			return TPI_ERROR_ARC_UNSUPPORTED;
		}
	}

	return TPI_ERROR_SUCCESS;
}

int __stdcall OpenArchive
(
	const wxString & _szArcName,
	void * * _hArchive
)
{
	wxString szAPIName = g_LibInfo.szPrefix + wxT("OpenArchive");
	FARPROC	fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
	if (fpProc == NULL)
	{
		return TPI_ERROR_U_USE_LIBRARY;
	}

	* _hArchive = ((void * (__stdcall *)(const HWND, LPCSTR, const DWORD)) fpProc)(NULL, _szArcName.char_str(), 0);
	return _hArchive == NULL ? TPI_ERROR_UNDEFINED : TPI_ERROR_SUCCESS;
}

int __stdcall CloseArchive
(
	void * _hArchive
)
{
	wxString szAPIName = g_LibInfo.szPrefix + wxT("CloseArchive");
	FARPROC	fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());

	return fpProc == NULL ? TPI_ERROR_U_USE_LIBRARY : CalErrorCodeConvert(((int (__stdcall *)(void *)) fpProc)(_hArchive));
}

int __stdcall GetFileInformation
(
	void * _hArchive,
	TPI_FILEINFO * _fiInfo,
	bool _bFirst
)
{
	static unsigned int s_uFileID;
	int nResult;
	INDIVIDUALINFO iiInfo;
	wxString szAPIName = g_LibInfo.szPrefix;

	if (_bFirst)
	{
		szAPIName += wxT("FindFirst");
		FARPROC	fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
		if (fpProc == NULL)
		{
			return TPI_ERROR_U_USE_LIBRARY;
		}

		s_uFileID = 0;
		nResult = ((int (__stdcall *)(void *, const char *, LPINDIVIDUALINFO)) fpProc)(_hArchive, "*", & iiInfo);
	}
	else
	{
		szAPIName += wxT("FindNext");
		FARPROC	fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
		if (fpProc == NULL)
		{
			return TPI_ERROR_U_USE_LIBRARY;
		}

		nResult = ((int (__stdcall *)(void *, LPINDIVIDUALINFO)) fpProc)(_hArchive, & iiInfo);
	}

	nResult = CalErrorCodeConvert(nResult);
	if (nResult == TPI_ERROR_SUCCESS)
	{
		szAPIName = g_LibInfo.szPrefix + wxT("GetAttribute");
		FARPROC	fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
		_fiInfo->dwAttribute = (fpProc == NULL) ? 0 : ((int (__stdcall *)(void *)) fpProc)(_hArchive);
		if ((signed) _fiInfo->dwAttribute == -1)
		{
			_fiInfo->dwAttribute = 0;
		}

		wxString szFileName(iiInfo.szFileName, wxConvLibc), szMethod(iiInfo.szMode, wxConvLibc);
		_fiInfo->dwCRC32        = iiInfo.dwCRC;
		_fiInfo->uOSType        = iiInfo.uOSType;
		_fiInfo->llPackedSize   = iiInfo.dwCompressedSize;
		_fiInfo->llUnpackedSize = iiInfo.dwOriginalSize;
		_fiInfo->llFileID       = s_uFileID++;
/*
		_fiInfo->ftAccessTime   = 0;
		_fiInfo->ftCreateTime   = 0;
*/
		_fiInfo->tmModified.SetFromDOS(MAKELONG(iiInfo.wTime, iiInfo.wDate));
		_fiInfo->szStoredName   = szFileName;
		_fiInfo->fnFileName     = wxFileName::FileName(szFileName);
		_fiInfo->szMethod       = szMethod;
		_fiInfo->wCompressRatio = iiInfo.wRatio;
	}

	return nResult;
}

int __stdcall GetArchiveInformation
(
	void * _hArchive,
	TPI_ARCHIVEINFO * _aiInfo
)
{
	wxString szAPIName = g_LibInfo.szPrefix + wxT("GetArcFileName");
	FARPROC	fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
	if (fpProc != NULL)
	{
		// TODO : ファイル名の32768文字制限を撤廃。
		wxString s;
		((int (__stdcall *)(void *, char *, const int)) fpProc)(_hArchive, (char *) (wxChar *) wxStringBuffer(s, 32770), 32769);
		_aiInfo->fnArchive = wxFileName::FileName(s);
	}

	szAPIName = g_LibInfo.szPrefix + wxT("GetArcFileSizeEx");
	fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
	if (fpProc != NULL)
	{
		((BOOL (__stdcall *)(void *, LONGLONG *)) fpProc)(_hArchive, (LONGLONG *) & _aiInfo->llFileSize);
	}
	else
	{
		szAPIName = g_LibInfo.szPrefix + wxT("GetArcFileSize");
		fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
		if (fpProc != NULL)
		{
			_aiInfo->llFileSize = ((DWORD (__stdcall *)(void *)) fpProc)(_hArchive);
		}
	}

	szAPIName = g_LibInfo.szPrefix + wxT("GetArcOriginalSizeEx");
	fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
	if (fpProc != NULL)
	{
		((BOOL (__stdcall *)(void *, LONGLONG *)) fpProc)(_hArchive, (LONGLONG *) & _aiInfo->llUnpackedSize);
	}
	else
	{
		szAPIName = g_LibInfo.szPrefix + wxT("GetArcOriginalSize");
		fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
		if (fpProc != NULL)
		{
			_aiInfo->llUnpackedSize = ((DWORD (__stdcall *)(void *)) fpProc)(_hArchive);
		}
	}

	szAPIName = g_LibInfo.szPrefix + wxT("GetArcCompressedSizeEx");
	fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
	if (fpProc != NULL)
	{
		((BOOL (__stdcall *)(void *, LONGLONG *)) fpProc)(_hArchive, (LONGLONG *) & _aiInfo->llPackedSize);
	}
	else
	{
		szAPIName = g_LibInfo.szPrefix + wxT("GetArcCompressedSize");
		fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
		if (fpProc != NULL)
		{
			_aiInfo->llPackedSize = ((DWORD (__stdcall *)(void *)) fpProc)(_hArchive);
		}
	}

	szAPIName = g_LibInfo.szPrefix + wxT("GetArcReadSizeEx");
	fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
	if (fpProc != NULL)
	{
		((BOOL (__stdcall *)(void *, LONGLONG *)) fpProc)(_hArchive, (LONGLONG *) & _aiInfo->llReadSize);
	}
	else
	{
		szAPIName = g_LibInfo.szPrefix + wxT("GetArcReadSize");
		fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
		if (fpProc != NULL)
		{
			_aiInfo->llReadSize = ((DWORD (__stdcall *)(void *)) fpProc)(_hArchive);
		}
	}

	szAPIName = g_LibInfo.szPrefix + wxT("GetArcRatio");
	fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
	if (fpProc != NULL)
	{
		_aiInfo->wCompressRatio = ((WORD (__stdcall *)(void *)) fpProc)(_hArchive);
	}

	FILETIME ft;
	szAPIName = g_LibInfo.szPrefix + wxT("GetArcAccessTimeEx");
	fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
	if (fpProc != NULL)
	{
		((BOOL (__stdcall *)(void *, FILETIME *)) fpProc)(_hArchive, & ft);
		_aiInfo->tmAccess = FileTimeToWxDateTime(& ft);
	}

	szAPIName = g_LibInfo.szPrefix + wxT("GetArcCreatedTimeEx");
	fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
	if (fpProc != NULL)
	{
		((BOOL (__stdcall *)(void *, FILETIME *)) fpProc)(_hArchive, & ft);
		_aiInfo->tmCreate = FileTimeToWxDateTime(& ft);
	}

	szAPIName = g_LibInfo.szPrefix + wxT("GetArcWriteTimeEx");
	fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
	if (fpProc != NULL)
	{
		((BOOL (__stdcall *)(void *, FILETIME *)) fpProc)(_hArchive, & ft);
		_aiInfo->tmModified = FileTimeToWxDateTime(& ft);
	}

	szAPIName = g_LibInfo.szPrefix + wxT("GetArcOSType");
	fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
	if (fpProc != NULL)
	{
		_aiInfo->uOSType = ((UINT (__stdcall *)(void *)) fpProc)(_hArchive);
	}

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

	return TPI_ERROR_SUCCESS;
}

int __stdcall Command
(
	unsigned int _uCommand,
	TPI_SWITCHES * _swInfo,
	const wxString & _szArcName,
	const wxArrayString & _szFiles
)
{
	// Iniからコマンドラインを取得。
	wxString szPath, szCommandLine, szCommandLineSend;
	// xml解析開始。
	wxXmlDocument config(g_szAppPath + wxT("/lib/calLibrary.xml"));
	// 一気に先頭のライブラリの情報を取得。
	wxXmlNode * xmlLibrary = config.GetRoot()->GetChildren();

	// 適当な位置まで移動。
	for (wxULongLong i = 0; i < g_LibInfo.nLibIndex && xmlLibrary != NULL; i++)
	{
		xmlLibrary = xmlLibrary->GetNext();
	}
	if (xmlLibrary->GetName() != wxT("library"))
	{
		// xml文法エラー。
		return TPI_ERROR_UNDEFINED;
	}

	// APIアドレス取得。
	FARPROC fpProc
		= xmlLibrary->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)
		? ::GetProcAddress(g_LibInfo.hLib, g_LibInfo.szPrefix.char_str())
		: xmlLibrary->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)
			? ::GetProcAddress(g_LibInfo.hLibAlt, g_LibInfo.szPrefixAlt.char_str()) : NULL;

	if (fpProc == NULL)
	{
		return TPI_ERROR_U_USE_LIBRARY;
	}

	// コマンドライン変数を差し替え。
	for (size_t i = 0; i < szCommandLine.Len(); i++)
	{
		if (szCommandLine[i] == wxT('%'))
		{
			// 変数解析開始。
			switch (szCommandLine[++i])
			{
			case wxT('-'):
				// 特殊文字。
				szCommandLineSend += szCommandLine[++i];
				break;
			case wxT('9'):
				switch (szCommandLine[++i])
				{
				case wxT('0'):
				{
					// 書庫名。
					wxString s = _szArcName;
					s.Replace(wxT("/"), wxT("\\"));
					szCommandLineSend += s;
					break;
				}
				case wxT('1'):
				{
					// 出力先。
					szCommandLineSend += _swInfo->fnDestinationDirectory.GetPathWithSep();
					break;
				}
				case wxT('2'):
					// レスポンスファイル。
					szCommandLineSend += ::wxGetCwd() + wxT("\\__callib__listfile");
					break;
				case wxT('3'):
					// ファイルリスト。
					for (size_t j = 0; j < _szFiles.GetCount(); j++)
					{
						szCommandLineSend += wxT("\"") + _szFiles[j] + wxT("\" ");
					}
					break;
				}
				break;
			case wxT('a'):
				switch (szCommandLine[++i])
				{
				case wxT('0'):
					// パスを有効にするか。
					if (szCommandLine[++i] == wxT('{'))
					{
						// 条件判別。
						for (i++; i < szCommandLine.Len() && szCommandLine[i] != wxT('|'); i++)
						{
							if (_swInfo->fStoreDirectoryPathes)
							{
								szCommandLineSend += szCommandLine[i];
							}
						}
						for (i++; i < szCommandLine.Len() && szCommandLine[i] != wxT('}'); i++)
						{
							if (! _swInfo->fStoreDirectoryPathes)
							{
								szCommandLineSend += szCommandLine[i];
							}
						}
					}
					else
					{
						// bool値を返却。
						szCommandLineSend += _swInfo->fStoreDirectoryPathes ? wxT('1') : wxT('0');
						i--;
					}
					break;
				case wxT('1'):
					// SFXを作成するか。
					if (szCommandLine[++i] == wxT('{'))
					{
						// 条件判別。
						for (i++; i < szCommandLine.Len() && szCommandLine[i] != wxT('|'); i++)
						{
							if (_swInfo->fMakeSFX)
							{
								szCommandLineSend += szCommandLine[i];
							}
						}
						for (i++; i < szCommandLine.Len() && szCommandLine[i] != wxT('}'); i++)
						{
							if (! _swInfo->fMakeSFX)
							{
								szCommandLineSend += szCommandLine[i];
							}
						}
					}
					else
					{
						// bool値を返却。
						szCommandLineSend += _swInfo->fMakeSFX ? wxT('1') : wxT('0');
						i--;
					}
					break;
				}
				break;
			}
		}
		else
		{
			szCommandLineSend += szCommandLine[i];
		}
	}

	// 手抜きレスポンスファイル生成。
	wxFile fListFile;
	if (! fListFile.Create(::wxGetCwd() + wxT("/__callib__listfile"), true))
	{
		return TPI_ERROR_UNDEFINED;
	}

	// ファイルリスト書き込み処理。
	if (_szFiles.GetCount() == 0)
	{
		fListFile.Write(wxT("*"));
	}
	else
	{
		// 毎回引用符で括って処理する。
		for (size_t i = 0; i < _szFiles.GetCount(); i++)
		{
			// 最後に改行があると動作がおかしくなる場合がある模様。
			if (i != 0)
			{
				fListFile.Write(wxT("\r\n"));
			}
			fListFile.Write(wxT("\"") + _szFiles[i] + wxT("\""));
		}
	}
	fListFile.Close();

	// コマンドライン実行。
	char szOutput[2000];
	int nCalErrorCode = ((int (__stdcall *)(const HWND, const char *, char *, DWORD)) fpProc)(NULL, szCommandLineSend.char_str(), szOutput, sizeof(szOutput) - 1);
	if (nCalErrorCode != 0)
	{
		wxString sz(szOutput, *wxConvCurrent);
		szPath.Printf(wxT("Error :\n%x\n\nCommandLine:\n%s\n\nOutput:\n%s"), nCalErrorCode, szCommandLineSend.c_str(), sz.c_str());
#ifdef __LINUX__
		::wxMessageBox(szPath);
#else
		MessageBox(NULL, szPath.c_str(), NULL, 0);
#endif
	}

	// レスポンスファイル削除。
	::wxRemoveFile(::wxGetCwd() + wxT("/__callib__listfile"));

	return CalErrorCodeConvert(nCalErrorCode);
}

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

	// APIのアドレスを取得。
	wxString szAPIName = g_LibInfo.szPrefix + wxT("SetOwnerWindowEx64");
	FARPROC	fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());

	BOOL fResult = FALSE;
	if (fpProc == NULL)
	{
		// SetOwnerWindowEx64が実装されていない場合、SetOwnerWindowExを利用。
		szAPIName = g_LibInfo.szPrefix + wxT("SetOwnerWindowEx");
		fpProc = ::GetProcAddress(g_LibInfo.hLib, szAPIName.char_str());
		if (fpProc == NULL)
		{
			return TPI_ERROR_U_USE_LIBRARY;
		}
		g_fCallback32bit = TRUE;
		fResult = ((BOOL (__stdcall *)(HWND, ARCHIVERPROC *)) fpProc)(NULL, (ARCHIVERPROC *) CallbackProc);
	}
	else
	{
		fResult = ((BOOL (__stdcall *)(HWND, ARCHIVERPROC *, DWORD)) fpProc)(NULL, (ARCHIVERPROC *) CallbackProc, sizeof(EXTRACTINGINFOEX64));
	}
	return fResult ? TPI_ERROR_SUCCESS : TPI_ERROR_UNDEFINED;
}

#ifdef __cplusplus
}
#endif
