// SoftEther VPN Source Code
// Mayaqua Kernel
// 
// SoftEther VPN Server, Client and Bridge are free software under GPLv2.
// 
// Copyright (c) 2012-2014 Daiyuu Nobori.
// Copyright (c) 2012-2014 SoftEther VPN Project, University of Tsukuba, Japan.
// Copyright (c) 2012-2014 SoftEther Corporation.
// 
// All Rights Reserved.
// 
// http://www.softether.org/
// 
// Author: Daiyuu Nobori
// Comments: Tetsuo Sugiyama, Ph.D.
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// version 2 as published by the Free Software Foundation.
// 
// This program 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 General Public License for more details.
// 
// You should have received a copy of the GNU General Public License version 2
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// 
// THE LICENSE AGREEMENT IS ATTACHED ON THE SOURCE-CODE PACKAGE
// AS "LICENSE.TXT" FILE. READ THE TEXT FILE IN ADVANCE TO USE THE SOFTWARE.
// 
// 
// THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN,
// UNDER JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY,
// MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS
// SOFTWARE, THAT ANY JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS
// SOFTWARE OR ITS CONTENTS, AGAINST US (SOFTETHER PROJECT, SOFTETHER
// CORPORATION, DAIYUU NOBORI OR OTHER SUPPLIERS), OR ANY JURIDICAL
// DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND OF USING, COPYING,
// MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING, AND/OR
// SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
// CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO
// EXCLUSIVE JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO,
// JAPAN. YOU MUST WAIVE ALL DEFENSES OF LACK OF PERSONAL JURISDICTION
// AND FORUM NON CONVENIENS. PROCESS MAY BE SERVED ON EITHER PARTY IN
// THE MANNER AUTHORIZED BY APPLICABLE LAW OR COURT RULE.
// 
// USE ONLY IN JAPAN. DO NOT USE IT IN OTHER COUNTRIES. IMPORTING THIS
// SOFTWARE INTO OTHER COUNTRIES IS AT YOUR OWN RISK. SOME COUNTRIES
// PROHIBIT ENCRYPTED COMMUNICATIONS. USING THIS SOFTWARE IN OTHER
// COUNTRIES MIGHT BE RESTRICTED.
// 
// 
// SOURCE CODE CONTRIBUTION
// ------------------------
// 
// Your contribution to SoftEther VPN Project is much appreciated.
// Please send patches to us through GitHub.
// Read the SoftEther VPN Patch Acceptance Policy in advance:
// http://www.softether.org/5-download/src/9.patch
// 
// 
// DEAR SECURITY EXPERTS
// ---------------------
// 
// If you find a bug or a security vulnerability please kindly inform us
// about the problem immediately so that we can fix the security problem
// to protect a lot of users around the world as soon as possible.
// 
// Our e-mail address for security reports is:
// softether-vpn-security [at] softether.org
// 
// Please note that the above e-mail address is not a technical support
// inquiry address. If you need technical assistance, please visit
// http://www.softether.org/ and ask your question on the users forum.
// 
// Thank you for your cooperation.
// 
// 
// NO MEMORY OR RESOURCE LEAKS
// ---------------------------
// 
// The memory-leaks and resource-leaks verification under the stress
// test has been passed before release this source code.


// Win32.c
// Microsoft Windows dependent code

#include <GlobalConst.h>

#ifdef	WIN32

#define	_WIN32_WINNT		0x0502
#define	WINVER				0x0502
#include <winsock2.h>
#include <windows.h>
#include <Dbghelp.h>
#include <commctrl.h>
#include <process.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <stdarg.h>
#include <time.h>
#include <errno.h>
#include <Mayaqua/Mayaqua.h>

static HANDLE heap_handle = NULL;
static HANDLE hstdout = INVALID_HANDLE_VALUE;
static HANDLE hstdin = INVALID_HANDLE_VALUE;

// Thread data for Win32
typedef struct WIN32THREAD
{
	HANDLE hThread;
	DWORD thread_id;
} WIN32THREAD;

// Thread startup information for Win32
typedef struct WIN32THREADSTARTUPINFO
{
	THREAD_PROC *thread_proc;
	void *param;
	THREAD *thread;
} WIN32THREADSTARTUPINFO;

// Function prototype for Win32
DWORD CALLBACK Win32DefaultThreadProc(void *param);

// Current process handle
static HANDLE hCurrentProcessHandle = NULL;
static CRITICAL_SECTION fasttick_lock;
static UINT64 start_tick = 0;
static bool use_heap_api = false;
static bool win32_is_nt = false;

// File I/O data for Win32
typedef struct WIN32IO
{
	HANDLE hFile;
	bool WriteMode;
} WIN32IO;

// Mutex data for Win32
typedef struct WIN32MUTEX
{
	HANDLE hMutex;
} WIN32MUTEX;

// Set the Thread name
#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
	DWORD dwType; // Must be 0x1000.
	LPCSTR szName; // Pointer to name (in user addr space).
	DWORD dwThreadID; // Thread ID (-1=caller thread).
	DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)

// Create a dispatch table
OS_DISPATCH_TABLE *Win32GetDispatchTable()
{
	static OS_DISPATCH_TABLE t =
	{
		Win32Init,
		Win32Free,
		Win32MemoryAlloc,
		Win32MemoryReAlloc,
		Win32MemoryFree,
		Win32GetTick,
		Win32GetSystemTime,
		Win32Inc32,
		Win32Dec32,
		Win32Sleep,
		Win32NewLock,
		Win32Lock,
		Win32Unlock,
		Win32DeleteLock,
		Win32InitEvent,
		Win32SetEvent,
		Win32ResetEvent,
		Win32WaitEvent,
		Win32FreeEvent,
		Win32WaitThread,
		Win32FreeThread,
		Win32InitThread,
		Win32ThreadId,
		Win32FileOpen,
		Win32FileOpenW,
		Win32FileCreate,
		Win32FileCreateW,
		Win32FileWrite,
		Win32FileRead,
		Win32FileClose,
		Win32FileFlush,
		Win32FileSize,
		Win32FileSeek,
		Win32FileDelete,
		Win32FileDeleteW,
		Win32MakeDir,
		Win32MakeDirW,
		Win32DeleteDir,
		Win32DeleteDirW,
		Win32GetCallStack,
		Win32GetCallStackSymbolInfo,
		Win32FileRename,
		Win32FileRenameW,
		Win32Run,
		Win32RunW,
		Win32IsSupportedOs,
		Win32GetOsInfo,
		Win32Alert,
		Win32AlertW,
		Win32GetProductId,
		Win32SetHighPriority,
		Win32RestorePriority,
		Win32NewSingleInstance,
		Win32FreeSingleInstance,
		Win32GetMemInfo,
		Win32Yield,
	};

	return &t;
}

// Set the thread name
void Win32SetThreadName(UINT thread_id, char *name)
{
	DWORD ms_vc_exception = 0x406D1388;
	THREADNAME_INFO t;
	// Validate arguments
	if (thread_id == 0 || name == NULL)
	{
		return;
	}

	Zero(&t, sizeof(t));
	t.dwType = 0x1000;
	t.szName = name;
	t.dwThreadID = thread_id;
	t.dwFlags = 0;

	__try
	{
		RaiseException(ms_vc_exception, 0, sizeof(t) / sizeof(ULONG_PTR), (ULONG_PTR *)&t);
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
	}
}

// Initialization function of the new thread
void Win32InitNewThread()
{
	static HINSTANCE hDll = NULL;
	static bool (WINAPI *_SetThreadLocale)(LCID) = NULL;

	if (hDll == NULL)
	{
		hDll = LoadLibrary("kernel32.dll");

		_SetThreadLocale =
			(bool (__stdcall *)(LCID))
			GetProcAddress(hDll, "SetThreadLocale");
	}

	if (_SetThreadLocale != NULL)
	{
		_SetThreadLocale(LOCALE_USER_DEFAULT);
	}
}

// Set the compression flag of the folder
bool Win32SetFolderCompressW(wchar_t *path, bool compressed)
{
	HANDLE h;
	UINT retsize = 0;
	USHORT flag;
	wchar_t tmp[MAX_PATH];
	// Validate arguments
	if (path == NULL)
	{
		return false;
	}

	if (IsNt() == false)
	{
		char *path_a = CopyUniToStr(path);
		bool ret = Win32SetFolderCompress(path_a, compressed);

		Free(path_a);

		return ret;
	}

	InnerFilePathW(tmp, sizeof(tmp), path);

	// Open the folder
	h = CreateFileW(tmp, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);

	if (h == INVALID_HANDLE_VALUE)
	{
		return false;
	}

	flag = compressed ? COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE;

	if (DeviceIoControl(h, FSCTL_SET_COMPRESSION, &flag, sizeof(USHORT),
		NULL, 0, &retsize, NULL) == false)
	{
		return false;
	}

	CloseHandle(h);

	return true;
}
bool Win32SetFolderCompress(char *path, bool compressed)
{
	HANDLE h;
	UINT retsize = 0;
	USHORT flag;
	char tmp[MAX_PATH];
	// Validate arguments
	if (path == NULL)
	{
		return false;
	}

	InnerFilePath(tmp, sizeof(tmp), path);

	// Open the folder
	h = CreateFile(tmp, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);

	if (h == INVALID_HANDLE_VALUE)
	{
		return false;
	}

	flag = compressed ? COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE;

	if (DeviceIoControl(h, FSCTL_SET_COMPRESSION, &flag, sizeof(USHORT),
		NULL, 0, &retsize, NULL) == false)
	{
		return false;
	}

	CloseHandle(h);

	return true;
}

// Get the free space of the disk
bool Win32GetDiskFreeW(wchar_t *path, UINT64 *free_size, UINT64 *used_size, UINT64 *total_size)
{
	wchar_t tmp[MAX_SIZE];
	UINT count = 0;
	UINT i, n, len;
	ULARGE_INTEGER v1, v2, v3;
	bool ret = false;
	// Validate arguments
	if (path == NULL)
	{
		return false;
	}

	if (IsNt() == false)
	{
		bool ret;
		char *path_a = CopyUniToStr(path);

		ret = Win32GetDiskFree(path_a, free_size, used_size, total_size);

		Free(path_a);

		return ret;
	}

	Zero(&v1, sizeof(v1));
	Zero(&v2, sizeof(v2));
	Zero(&v3, sizeof(v3));

	NormalizePathW(tmp, sizeof(tmp), path);

	// Get the directory name
	if (UniStartWith(path, L"\\\\"))
	{
		count = 4;
	}
	else
	{
		count = 1;
	}

	len = UniStrLen(tmp);
	n = 0;
	for (i = 0;i < len;i++)
	{
		if (tmp[i] == L'\\')
		{
			n++;
			if (n >= count)
			{
				tmp[i + 1] = 0;
				break;
			}
		}
	}

	if (GetDiskFreeSpaceExW(tmp, &v1, &v2, &v3))
	{
		ret = true;
	}

	if (free_size != NULL)
	{
		*free_size = v1.QuadPart;
	}

	if (total_size != NULL)
	{
		*total_size = v2.QuadPart;
	}

	if (used_size != NULL)
	{
		*used_size = v2.QuadPart - v1.QuadPart;
	}

	return ret;
}
bool Win32GetDiskFree(char *path, UINT64 *free_size, UINT64 *used_size, UINT64 *total_size)
{
	char tmp[MAX_SIZE];
	UINT count = 0;
	UINT i, n, len;
	ULARGE_INTEGER v1, v2, v3;
	bool ret = false;
	// Validate arguments
	if (path == NULL)
	{
		return false;
	}

	Zero(&v1, sizeof(v1));
	Zero(&v2, sizeof(v2));
	Zero(&v3, sizeof(v3));

	NormalizePath(tmp, sizeof(tmp), path);

	// Get the directory name
	if (StartWith(path, "\\\\"))
	{
		count = 4;
	}
	else
	{
		count = 1;
	}

	len = StrLen(tmp);
	n = 0;
	for (i = 0;i < len;i++)
	{
		if (tmp[i] == '\\')
		{
			n++;
			if (n >= count)
			{
				tmp[i + 1] = 0;
				break;
			}
		}
	}

	if (GetDiskFreeSpaceEx(tmp, &v1, &v2, &v3))
	{
		ret = true;
	}

	if (free_size != NULL)
	{
		*free_size = v1.QuadPart;
	}

	if (total_size != NULL)
	{
		*total_size = v2.QuadPart;
	}

	if (used_size != NULL)
	{
		*used_size = v2.QuadPart - v1.QuadPart;
	}

	return ret;
}

// Enumeration of directory
DIRLIST *Win32EnumDirEx(char *dirname, COMPARE *compare)
{
	DIRLIST *ret;
	wchar_t *dirname_w = CopyStrToUni(dirname);

	ret = Win32EnumDirExW(dirname_w, compare);

	Free(dirname_w);

	return ret;
}
DIRLIST *Win32EnumDirExW(wchar_t *dirname, COMPARE *compare)
{
	WIN32_FIND_DATAA data_a;
	WIN32_FIND_DATAW data_w;
	HANDLE h;
	wchar_t tmp[MAX_PATH];
	wchar_t tmp2[MAX_PATH];
	wchar_t dirname2[MAX_PATH];
	LIST *o;
	DIRLIST *d;

	UniStrCpy(tmp2, sizeof(tmp2), dirname);

	if (UniStrLen(tmp2) >= 1 && tmp[UniStrLen(tmp2) - 1] == L'\\')
	{
		tmp2[UniStrLen(tmp2) - 1] = 0;
	}

	UniFormat(tmp, sizeof(tmp), L"%s\\*.*", tmp2);
	NormalizePathW(tmp, sizeof(tmp), tmp);
	NormalizePathW(dirname2, sizeof(dirname2), tmp2);

	o = NewListFast(compare);

	Zero(&data_a, sizeof(data_a));
	Zero(&data_w, sizeof(data_w));

	if (IsNt())
	{
		h = FindFirstFileW(tmp, &data_w);
	}
	else
	{
		char *tmp_a = CopyUniToStr(tmp);

		h = FindFirstFileA(tmp_a, &data_a);

		Free(tmp_a);
	}

	if (h != INVALID_HANDLE_VALUE)
	{
		bool b = true;

		do
		{
			if (IsNt() == false)
			{
				Zero(&data_w, sizeof(data_w));
				StrToUni(data_w.cFileName, sizeof(data_w.cFileName), data_a.cFileName);
				data_w.dwFileAttributes = data_a.dwFileAttributes;
				data_w.ftCreationTime = data_a.ftCreationTime;
				data_w.ftLastWriteTime = data_a.ftLastWriteTime;
				data_w.nFileSizeHigh = data_a.nFileSizeHigh;
				data_w.nFileSizeLow = data_a.nFileSizeLow;
			}

			if (UniStrCmpi(data_w.cFileName, L"..") != 0 &&
				UniStrCmpi(data_w.cFileName, L".") != 0)
			{
				DIRENT *f = ZeroMalloc(sizeof(DIRENT));
				SYSTEMTIME t1, t2;
				wchar_t fullpath[MAX_SIZE];
				bool ok = false;

				f->FileNameW = UniCopyStr(data_w.cFileName);
				f->FileName = CopyUniToStr(f->FileNameW);
				f->Folder = (data_w.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false;

				CombinePathW(fullpath, sizeof(fullpath), dirname2, f->FileNameW);

				// Attempt to get the file information
				if (MsIsNt())
				{
					HANDLE h = CreateFileW(fullpath, 0,
						FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
						NULL, OPEN_EXISTING, 0, NULL);

					if (h != INVALID_HANDLE_VALUE)
					{
						BY_HANDLE_FILE_INFORMATION info;

						Zero(&info, sizeof(info));

						if (MsGetFileInformation(h, &info))
						{
							Zero(&t1, sizeof(t1));
							Zero(&t2, sizeof(t2));
							FileTimeToSystemTime(&info.ftCreationTime, &t1);
							FileTimeToSystemTime(&info.ftLastWriteTime, &t2);
							f->CreateDate = SystemToUINT64(&t1);
							f->UpdateDate = SystemToUINT64(&t2);

							if (f->Folder == false)
							{
								f->FileSize = ((UINT64)info.nFileSizeHigh * (UINT64)((UINT64)MAXDWORD + (UINT64)1)) + (UINT64)info.nFileSizeLow;
							}

							ok = true;
						}

						CloseHandle(h);
					}
				}

				if (ok == false)
				{
					Zero(&t1, sizeof(t1));
					Zero(&t2, sizeof(t2));
					FileTimeToSystemTime(&data_w.ftCreationTime, &t1);
					FileTimeToSystemTime(&data_w.ftLastWriteTime, &t2);
					f->CreateDate = SystemToUINT64(&t1);
					f->UpdateDate = SystemToUINT64(&t2);

					if (f->Folder == false)
					{
						f->FileSize = ((UINT64)data_w.nFileSizeHigh * (UINT64)((UINT64)MAXDWORD + (UINT64)1)) + (UINT64)data_w.nFileSizeLow;
					}
				}

				Add(o, f);
			}

			Zero(&data_w, sizeof(data_w));
			Zero(&data_a, sizeof(data_a));

			if (IsNt())
			{
				b = FindNextFileW(h, &data_w);
			}
			else
			{
				b = FindNextFileA(h, &data_a);
			}
		}
		while (b);

		FindClose(h);
	}

	Sort(o);

	d = ZeroMalloc(sizeof(DIRLIST));
	d->NumFiles = LIST_NUM(o);
	d->File = ToArray(o);

	ReleaseList(o);

	return d;
}

// Get the EXE file name
void Win32GetExeNameW(wchar_t *name, UINT size)
{
	// Validate arguments
	if (name == NULL)
	{
		return;
	}

	if (IsNt() == false)
	{
		char name_a[MAX_PATH];

		Win32GetExeName(name_a, sizeof(name_a));

		StrToUni(name, size, name_a);

		return;
	}

	UniStrCpy(name, size, L"");

	GetModuleFileNameW(NULL, name, size);
}
void Win32GetExeName(char *name, UINT size)
{
	// Validate arguments
	if (name == NULL)
	{
		return;
	}

	StrCpy(name, size, "");

	GetModuleFileName(NULL, name, size);
}

// Get the current directory
void Win32GetCurrentDirW(wchar_t *dir, UINT size)
{
	// Validate arguments
	if (dir == NULL)
	{
		return;
	}

	if (IsNt() == false)
	{
		char dir_a[MAX_PATH];

		Win32GetCurrentDir(dir_a, sizeof(dir_a));

		StrToUni(dir, size, dir_a);

		return;
	}

	GetCurrentDirectoryW(size, dir);
}
void Win32GetCurrentDir(char *dir, UINT size)
{
	// Validate arguments
	if (dir == NULL)
	{
		return;
	}

	GetCurrentDirectory(size, dir);
}

// Yield
void Win32Yield()
{
	Sleep(0);
}

// Get the memory information
void Win32GetMemInfo(MEMINFO *info)
{
	static HINSTANCE hDll = NULL;
	static bool (WINAPI *_GlobalMemoryStatusEx)(LPMEMORYSTATUSEX) = NULL;
	// Validate arguments
	if (info == NULL)
	{
		return;
	}

	Zero(info, sizeof(MEMINFO));

	if (hDll == NULL)
	{
		hDll = LoadLibrary("kernel32.dll");
	}
	if (hDll != NULL)
	{
		if (_GlobalMemoryStatusEx == NULL)
		{
			_GlobalMemoryStatusEx =
				(bool (__stdcall *)(LPMEMORYSTATUSEX))GetProcAddress(hDll, "GlobalMemoryStatusEx");
		}
	}


	if (_GlobalMemoryStatusEx == NULL)
	{
		// Old API
		MEMORYSTATUS st;
		Zero(&st, sizeof(st));
		st.dwLength = sizeof(st);

		GlobalMemoryStatus(&st);

		// Amount of the logical memory
		info->TotalMemory = (UINT64)st.dwTotalPageFile;
		info->FreeMemory = (UINT64)st.dwAvailPageFile;
		info->UsedMemory = info->TotalMemory - info->FreeMemory;

		// Amount of the physical memory
		info->TotalPhys = (UINT64)st.dwTotalPhys;
		info->FreePhys = (UINT64)st.dwAvailPhys;
		info->UsedPhys = info->TotalPhys - info->FreePhys;
	}
	else
	{
		// New API
		MEMORYSTATUSEX st;
		Zero(&st, sizeof(st));
		st.dwLength = sizeof(st);

		_GlobalMemoryStatusEx(&st);

		// Amount of the logical memory
		info->TotalMemory = st.ullTotalPageFile;
		info->FreeMemory = st.ullAvailPageFile;
		info->UsedMemory = info->TotalMemory - info->FreeMemory;

		// Amount of the physical memory
		info->TotalPhys = st.ullTotalPhys;
		info->FreePhys = st.ullAvailPhys;
		info->UsedPhys = info->TotalPhys - info->FreePhys;
	}
}

// Creating a single instance
void *Win32NewSingleInstance(char *instance_name)
{
	WIN32MUTEX *ret;
	char tmp[MAX_SIZE];
	HANDLE hMutex;
	// Validate arguments
	if (instance_name == NULL)
	{
		char exe_path[MAX_PATH];
		GetModuleFileName(NULL, exe_path, sizeof(exe_path));
		HashInstanceName(tmp, sizeof(tmp), exe_path);
		instance_name = tmp;
	}

	hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, instance_name);
	if (hMutex != NULL)
	{
		CloseHandle(hMutex);
		return NULL;
	}

	hMutex = CreateMutex(NULL, FALSE, instance_name);
	if (hMutex == NULL)
	{
		CloseHandle(hMutex);
		return NULL;
	}

	ret = Win32MemoryAlloc(sizeof(WIN32MUTEX));
	ret->hMutex = hMutex;

	return (void *)ret;
}

// Release the single instance
void Win32FreeSingleInstance(void *data)
{
	WIN32MUTEX *m;
	// Validate arguments
	if (data == NULL)
	{
		return;
	}

	m = (WIN32MUTEX *)data;
	ReleaseMutex(m->hMutex);
	CloseHandle(m->hMutex);

	Win32MemoryFree(m);
}

// Raise the priority
void Win32SetHighPriority()
{
	SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
}

// Restore the priority
void Win32RestorePriority()
{
	SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
}

// Get the node information
char* Win32GetProductId()
{
	char *product_id;

	return CopyStr("--");

	// Product ID
	product_id = MsRegReadStr(REG_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "ProductId");
	if (product_id == NULL)
	{
		product_id = MsRegReadStr(REG_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion", "ProductId");
	}

	return product_id;
}

// Acquisition whether the OS is currently supported
bool Win32IsSupportedOs()
{
	if (Win32GetOsType() == 0)
	{
		Win32Alert(
			CEDAR_PRODUCT_STR " VPN doesn't support this Windows Operating System.\n"
			CEDAR_PRODUCT_STR " VPN requires " SUPPORTED_WINDOWS_LIST ".\n\n"
			"Please contact your system administrator.", NULL);
		return false;
	}

	return true;
}

// Show an alert
void Win32AlertW(wchar_t *msg, wchar_t *caption)
{
	char *s;
	// Validate arguments
	if (msg == NULL)
	{
		msg = L"Alert";
	}
	if (caption == NULL)
	{
		caption = CEDAR_PRODUCT_STR_W L" VPN Kernel";
	}

	s = GetCommandLineStr();

	if (SearchStr(s, "win9x_uninstall", 0) == INFINITE && SearchStr(s, "win9x_install", 0) == INFINITE)
	{
		// Hide during the uninstallation in Win9x service mode
		MessageBoxW(NULL, msg, caption, MB_SETFOREGROUND | MB_TOPMOST | MB_SERVICE_NOTIFICATION | MB_OK | MB_ICONEXCLAMATION);
	}

	Free(s);
}
void Win32Alert(char *msg, char *caption)
{
	char *s;
	// Validate arguments
	if (msg == NULL)
	{
		msg = "Alert";
	}
	if (caption == NULL)
	{
		caption = CEDAR_PRODUCT_STR " VPN Kernel";
	}

	s = GetCommandLineStr();

	if (SearchStr(s, "win9x_uninstall", 0) == INFINITE && SearchStr(s, "win9x_install", 0) == INFINITE)
	{
		// Hide during the uninstallation in Win9x service mode
		MessageBox(NULL, msg, caption, MB_SETFOREGROUND | MB_TOPMOST | MB_SERVICE_NOTIFICATION | MB_OK | MB_ICONEXCLAMATION);
	}

	Free(s);
}
void Win32DebugAlert(char *msg)
{
	// Validate arguments
	if (msg == NULL)
	{
		msg = "Alert";
	}

	MessageBox(NULL, msg, "Debug", MB_SETFOREGROUND | MB_TOPMOST | MB_SERVICE_NOTIFICATION | MB_OK | MB_ICONEXCLAMATION);
}

// Get the OS information
void Win32GetOsInfo(OS_INFO *info)
{
	UINT type = Win32GetOsType();
	OSVERSIONINFOEX os;
	char tmp[MAX_SIZE];
	// Validate arguments
	if (info == NULL)
	{
		return;
	}

	Zero(&os, sizeof(os));
	os.dwOSVersionInfoSize = sizeof(os);
	Win32GetVersionExInternal((LPOSVERSIONINFOA)&os);

	info->OsType = Win32GetOsType();
	info->OsServicePack = os.wServicePackMajor;
	if (OS_IS_WINDOWS_NT(info->OsType))
	{
		char *s;
		char *keyname = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
		info->OsSystemName = CopyStr("Windows NT");
		Format(tmp, sizeof(tmp), "Build %u", os.dwBuildNumber);
		if (s = MsRegReadStr(REG_LOCAL_MACHINE, keyname, "CurrentType"))
		{
			char str[MAX_SIZE];
			Format(str, sizeof(str), ", %s", s);
			StrCat(tmp, sizeof(tmp), str);
			Free(s);
		}
		if (os.wServicePackMajor != 0)
		{
			char str[MAX_SIZE];
			Format(str, sizeof(str), ", Service Pack %u", os.wServicePackMajor);
			StrCat(tmp, sizeof(tmp), str);
		}
		if (s = MsRegReadStr(REG_LOCAL_MACHINE, keyname, "BuildLab"))
		{
			char str[MAX_SIZE];
			Format(str, sizeof(str), " (%s)", s);
			StrCat(tmp, sizeof(tmp), str);
			Free(s);
		}
		info->OsVersion = CopyStr(tmp);
		info->KernelName = CopyStr("NTOS Kernel");
		Format(tmp, sizeof(tmp), "Build %u", os.dwBuildNumber);
		if (s = MsRegReadStr(REG_LOCAL_MACHINE, keyname, "CurrentType"))
		{
			char str[MAX_SIZE];
			Format(str, sizeof(str), " %s", s);
			StrCat(tmp, sizeof(tmp), str);
			Free(s);
		}
		info->KernelVersion = CopyStr(tmp);
	}
	else
	{
		OSVERSIONINFO os;
		Zero(&os, sizeof(os));
		os.dwOSVersionInfoSize = sizeof(os);
		GetVersionEx(&os);
		Format(tmp, sizeof(tmp), "Build %u %s", LOWORD(os.dwBuildNumber), os.szCSDVersion);
		Trim(tmp);
		info->OsVersion = CopyStr(tmp);
		info->OsSystemName = CopyStr("Windows");
		info->KernelName = CopyStr("Windows 9x Kernel");
		info->KernelVersion = CopyStr(tmp);
	}

	info->OsProductName = CopyStr(OsTypeToStr(info->OsType));
	info->OsVendorName = CopyStr("Microsoft Corporation");
}

// GetVersionEx API (Ignore the tricky features that have been added to the Windows 8.2 or later)
bool Win32GetVersionExInternal(void *info)
{
	OSVERSIONINFOA os;
	// Validate arguments
	if (info == NULL)
	{
		return false;
	}

	Zero(&os, sizeof(os));
	os.dwOSVersionInfoSize = sizeof(os);

	if (GetVersionExA(&os))
	{
		if (os.dwPlatformId == VER_PLATFORM_WIN32_NT)
		{
			if ((os.dwMajorVersion == 6 && os.dwMinorVersion >= 2) ||
				(os.dwMajorVersion == 7))
			{
				// Windows 8 later
				return Win32GetVersionExInternalForWindows81orLater(info);
			}
		}
	}

	return GetVersionExA(info);
}

// GetVersionEx for Windows 8.1 and later
bool Win32GetVersionExInternalForWindows81orLater(void *info)
{
	OSVERSIONINFOEXA *ex = (OSVERSIONINFOEXA *)info;
	char *str;
	// Validate arguments
	if (info == NULL)
	{
		return false;
	}

	if (ex->dwOSVersionInfoSize != sizeof(OSVERSIONINFOEXA) &&
		ex->dwOSVersionInfoSize != sizeof(OSVERSIONINFOA))
	{
		return GetVersionExA(info);
	}

	if (GetVersionExA(info) == false)
	{
		return false;
	}

	str = MsRegReadStrEx2(REG_LOCAL_MACHINE,
		"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
		"CurrentVersion",
		false, true);

	if (IsEmptyStr(str) == false)
	{
		// Is the version string formed as x.y?
		TOKEN_LIST *t = ParseToken(str, ".");

		if (t != NULL && t->NumTokens == 2)
		{
			UINT major = ToInt(t->Token[0]);
			UINT minor = ToInt(t->Token[1]);

			if (major >= 6)
			{
				// Version number acquisition success
				ex->dwMajorVersion = major;
				ex->dwMinorVersion = minor;
			}
		}

		FreeToken(t);
	}

	Free(str);

	return true;
}

// Acquisition whether it's a Windows NT
bool Win32IsNt()
{
	OSVERSIONINFO os;
	Zero(&os, sizeof(os));
	os.dwOSVersionInfoSize = sizeof(os);

	if (GetVersionEx(&os) == FALSE)
	{
		// Failure?
		return false;
	}

	if (os.dwPlatformId == VER_PLATFORM_WIN32_NT)
	{
		// NT
		return true;
	}

	// 9x
	return false;
}

// Get the OS type
UINT Win32GetOsType()
{
	OSVERSIONINFO os;
	Zero(&os, sizeof(os));
	os.dwOSVersionInfoSize = sizeof(os);

	if (Win32GetVersionExInternal(&os) == FALSE)
	{
		// Failure?
		return 0;
	}

	if (os.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
	{
		// Windows 9x system
		if (os.dwMajorVersion == 4)
		{
			if (os.dwMinorVersion == 0)
			{
				return OSTYPE_WINDOWS_95;
			}
			else if (os.dwMinorVersion == 10)
			{
				return OSTYPE_WINDOWS_98;
			}
			else if (os.dwMinorVersion == 90)
			{
				return OSTYPE_WINDOWS_ME;
			}
			else
			{
				return OSTYPE_WINDOWS_UNKNOWN;
			}
		}
		else if (os.dwMajorVersion >= 5)
		{
			return OSTYPE_WINDOWS_UNKNOWN;
		}
	}
	else if (os.dwPlatformId == VER_PLATFORM_WIN32_NT)
	{
		UINT sp = Win32GetSpVer(os.szCSDVersion);
		if (os.dwMajorVersion == 4)
		{
			if (sp < 6)
			{
				// SP6 or earlier
				return 0;
			}
		}
		if (os.dwMajorVersion < 4)
		{
			// NT 3.51 or earlier
			return 0;
		}
		else
		{
			OSVERSIONINFOEX os;
			Zero(&os, sizeof(os));
			os.dwOSVersionInfoSize = sizeof(os);
			Win32GetVersionExInternal((LPOSVERSIONINFOA)&os);

			if (os.dwMajorVersion == 4)
			{
				// Windows NT 4.0
				if (os.wProductType == VER_NT_DOMAIN_CONTROLLER || os.wProductType == VER_NT_SERVER)
				{
					if ((os.wSuiteMask & VER_SUITE_TERMINAL) || (os.wSuiteMask & VER_SUITE_SINGLEUSERTS))
					{
						return OSTYPE_WINDOWS_NT_4_TERMINAL_SERVER;
					}
					if (os.wSuiteMask & VER_SUITE_ENTERPRISE)
					{
						return OSTYPE_WINDOWS_NT_4_SERVER_ENTERPRISE;
					}
					if (os.wSuiteMask & VER_SUITE_BACKOFFICE)
					{
						return OSTYPE_WINDOWS_NT_4_BACKOFFICE;
					}
					if ((os.wSuiteMask & VER_SUITE_SMALLBUSINESS) || (os.wSuiteMask & VER_SUITE_SMALLBUSINESS_RESTRICTED))
					{
						return OSTYPE_WINDOWS_NT_4_SMS;
					}
					else
					{
						return OSTYPE_WINDOWS_NT_4_SERVER;
					}
				}
				else
				{
					return OSTYPE_WINDOWS_NT_4_WORKSTATION;
				}
			}
			else if (os.dwMajorVersion == 5)
			{
				// Windows 2000, XP, Server 2003
				if (os.dwMinorVersion == 0)
				{
					// Windows 2000
					if (os.wProductType == VER_NT_DOMAIN_CONTROLLER || os.wProductType == VER_NT_SERVER)
					{
						// Server
						if (os.wSuiteMask & VER_SUITE_DATACENTER)
						{
							return OSTYPE_WINDOWS_2000_DATACENTER_SERVER;
						}
						else if ((os.wSuiteMask & VER_SUITE_SMALLBUSINESS) || (os.wSuiteMask & VER_SUITE_SMALLBUSINESS_RESTRICTED))
						{
							return OSTYPE_WINDOWS_2000_SBS;
						}
						else if (os.wSuiteMask & VER_SUITE_BACKOFFICE)
						{
							return OSTYPE_WINDOWS_2000_BACKOFFICE;
						}
						else if (os.wSuiteMask & VER_SUITE_ENTERPRISE)
						{
							return OSTYPE_WINDOWS_2000_ADVANCED_SERVER;
						}
						else
						{
							return OSTYPE_WINDOWS_2000_SERVER;
						}
					}
					else
					{
						// Client
						return OSTYPE_WINDOWS_2000_PROFESSIONAL;
					}
				}
				else if (os.dwMinorVersion == 1)
				{
					// Windows XP
					if (os.wSuiteMask & VER_SUITE_PERSONAL)
					{
						return OSTYPE_WINDOWS_XP_HOME;
					}
					else
					{
						return OSTYPE_WINDOWS_XP_PROFESSIONAL;
					}
				}
				else if (os.dwMinorVersion == 2)
				{
					// Windows Server 2003
					if (os.wProductType == VER_NT_DOMAIN_CONTROLLER || os.wProductType == VER_NT_SERVER)
					{
						// Server
						if (os.wSuiteMask & VER_SUITE_DATACENTER)
						{
							return OSTYPE_WINDOWS_2003_DATACENTER;
						}
						else if ((os.wSuiteMask & VER_SUITE_SMALLBUSINESS) || (os.wSuiteMask & VER_SUITE_SMALLBUSINESS_RESTRICTED))
						{
							return OSTYPE_WINDOWS_2003_SBS;
						}
						else if (os.wSuiteMask & VER_SUITE_BACKOFFICE)
						{
							return OSTYPE_WINDOWS_2003_BACKOFFICE;
						}
						else if (os.wSuiteMask & VER_SUITE_ENTERPRISE)
						{
							return OSTYPE_WINDOWS_2003_ENTERPRISE;
						}
						else if (os.wSuiteMask & VER_SUITE_BLADE)
						{
							return OSTYPE_WINDOWS_2003_WEB;
						}
						else
						{
							return OSTYPE_WINDOWS_2003_STANDARD;
						}
					}
					else
					{
						// Client (Unknown XP?)
						return OSTYPE_WINDOWS_XP_PROFESSIONAL;
					}
				}
				else
				{
					// Windows Longhorn
					if (os.wProductType == VER_NT_DOMAIN_CONTROLLER || os.wProductType == VER_NT_SERVER)
					{
						return OSTYPE_WINDOWS_LONGHORN_SERVER;
					}
					else
					{
						return OSTYPE_WINDOWS_LONGHORN_PROFESSIONAL;
					}
				}
			}
			else
			{
				if (os.dwMajorVersion == 6 && os.dwMinorVersion == 0)
				{
					// Windows Vista, Server 2008
					if (os.wProductType == VER_NT_DOMAIN_CONTROLLER || os.wProductType == VER_NT_SERVER)
					{
						return OSTYPE_WINDOWS_LONGHORN_SERVER;
					}
					else
					{
						return OSTYPE_WINDOWS_LONGHORN_PROFESSIONAL;
					}
				}
				else if (os.dwMajorVersion == 6 && os.dwMinorVersion == 1)
				{
					if (os.wProductType == VER_NT_WORKSTATION)
					{
						// Windows 7
						return OSTYPE_WINDOWS_7;
					}
					else
					{
						// Windows Server 2008 R2
						return OSTYPE_WINDOWS_SERVER_2008_R2;
					}
				}
				else if (os.dwMajorVersion == 6 && os.dwMinorVersion == 2)
				{
					if (os.wProductType == VER_NT_WORKSTATION)
					{
						// Windows 8
						return OSTYPE_WINDOWS_8;
					}
					else
					{
						// Windows Server 2012
						return OSTYPE_WINDOWS_SERVER_8;
					}
				}
				else if (os.dwMajorVersion == 6 && os.dwMinorVersion == 3)
				{
					if (os.wProductType == VER_NT_WORKSTATION)
					{
						// Windows 8.1
						return OSTYPE_WINDOWS_81;
					}
					else
					{
						// Windows Server 2012 R2
						return OSTYPE_WINDOWS_SERVER_81;
					}
				}
				else
				{
					if (os.wProductType == VER_NT_WORKSTATION)
					{
						// Windows 9?
						return OSTYPE_WINDOWS_9;
					}
					else
					{
						// Windows Server 9?
						return OSTYPE_WINDOWS_SERVER_9;
					}
				}
			}
		}
	}

	// Can not be determined
	return 0;
}

// Get the SP version from the string
UINT Win32GetSpVer(char *str)
{
	UINT ret, i;
	TOKEN_LIST *t;
	// Validate arguments
	if (str == NULL)
	{
		return 0;
	}

	t = ParseToken(str, NULL);
	if (t == NULL)
	{
		return 0;
	}

	ret = 0;
	for (i = 0;i < t->NumTokens;i++)
	{
		ret = ToInt(t->Token[i]);
		if (ret != 0)
		{
			break;
		}
	}

	FreeToken(t);

	return ret;
}

// Kill the process
bool Win32TerminateProcess(void *handle)
{
	HANDLE h;
	// Validate arguments
	if (handle == NULL)
	{
		return false;
	}

	h = (HANDLE)handle;

	TerminateProcess(h, 0);

	return true;
}

// Close the process
void Win32CloseProcess(void *handle)
{
	// Validate arguments
	if (handle == NULL)
	{
		return;
	}

	CloseHandle((HANDLE)handle);
}

// Check whether the specified process is alive
bool Win32IsProcessAlive(void *handle)
{
	HANDLE h;
	// Validate arguments
	if (handle == NULL)
	{
		return false;
	}

	h = (HANDLE)handle;

	if (WaitForSingleObject(h, 0) == WAIT_OBJECT_0)
	{
		return false;
	}

	return true;
}

// Wait for the process termination
bool Win32WaitProcess(void *h, UINT timeout)
{
	// Validate arguments
	if (h == NULL)
	{
		return false;
	}
	if (timeout == 0)
	{
		timeout = INFINITE;
	}

	if (WaitForSingleObject((HANDLE)h, timeout) == WAIT_TIMEOUT)
	{
		return false;
	}

	return true;
}

// Run the process and wait for terminate it
bool Win32RunAndWaitProcess(wchar_t *filename, wchar_t *arg, bool hide, bool disableWow, UINT timeout)
{
	UINT process_id = 0;
	void *p = Win32RunEx3W(filename, arg, hide, &process_id, disableWow);

	if (p == NULL)
	{
		return false;
	}

	return Win32WaitProcess(p, timeout);
}

// Run the process (return the handle)
void *Win32RunExW(wchar_t *filename, wchar_t *arg, bool hide)
{
	return Win32RunEx2W(filename, arg, hide, NULL);
}
void *Win32RunEx2W(wchar_t *filename, wchar_t *arg, bool hide, UINT *process_id)
{
	return Win32RunEx3W(filename, arg, hide, process_id, false);
}
void *Win32RunEx3W(wchar_t *filename, wchar_t *arg, bool hide, UINT *process_id, bool disableWow)
{
	STARTUPINFOW info;
	PROCESS_INFORMATION ret;
	wchar_t cmdline[MAX_SIZE];
	wchar_t name[MAX_PATH];
	void *p;
	// Validate arguments
	if (filename == NULL)
	{
		return NULL;
	}

	if (IsNt() == false)
	{
		char *filename_a = CopyUniToStr(filename);
		char *arg_a = CopyUniToStr(arg);
		void *ret = Win32RunEx3(filename_a, arg_a, hide, process_id, disableWow);

		Free(filename_a);
		Free(arg_a);

		return ret;
	}

	UniStrCpy(name, sizeof(name), filename);
	UniTrim(name);

	if (UniSearchStr(name, L"\"", 0) == INFINITE)
	{
		if (arg == NULL)
		{
			UniFormat(cmdline, sizeof(cmdline), L"%s", name);
		}
		else
		{
			UniFormat(cmdline, sizeof(cmdline), L"%s %s", name, arg);
		}
	}
	else
	{
		if (arg == NULL)
		{
			UniFormat(cmdline, sizeof(cmdline), L"\"%s\"", name);
		}
		else
		{
			UniFormat(cmdline, sizeof(cmdline), L"\"%s\" %s", name, arg);
		}
	}

	Zero(&info, sizeof(info));
	Zero(&ret, sizeof(ret));
	info.cb = sizeof(info);
	info.dwFlags = STARTF_USESHOWWINDOW;
	info.wShowWindow = (hide == false ? SW_SHOWDEFAULT : SW_HIDE);

	UniTrim(cmdline);

	if (disableWow)
	{
		p = MsDisableWow64FileSystemRedirection();
	}

	if (CreateProcessW(NULL, cmdline, NULL, NULL, FALSE,
		(hide == false ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW | CREATE_NEW_CONSOLE) | NORMAL_PRIORITY_CLASS,
		NULL, NULL, &info, &ret) == FALSE)
	{
		if (disableWow)
		{
			MsRestoreWow64FileSystemRedirection(p);
		}
		return NULL;
	}

	if (disableWow)
	{
		MsRestoreWow64FileSystemRedirection(p);
	}

	if (process_id != NULL)
	{
		*process_id = ret.dwProcessId;
	}

	CloseHandle(ret.hThread);
	return ret.hProcess;
}
void *Win32RunEx(char *filename, char *arg, bool hide)
{
	return Win32RunEx2(filename, arg, hide, NULL);
}
void *Win32RunEx2(char *filename, char *arg, bool hide, UINT *process_id)
{
	return Win32RunEx3(filename, arg, hide, process_id, false);
}
void *Win32RunEx3(char *filename, char *arg, bool hide, UINT *process_id, bool disableWow)
{
	STARTUPINFO info;
	PROCESS_INFORMATION ret;
	char cmdline[MAX_SIZE];
	char name[MAX_PATH];
	void *p = NULL;
	// Validate arguments
	if (filename == NULL)
	{
		return NULL;
	}

	StrCpy(name, sizeof(name), filename);
	Trim(name);

	if (SearchStr(name, "\"", 0) == INFINITE)
	{
		if (arg == NULL)
		{
			Format(cmdline, sizeof(cmdline), "%s", name);
		}
		else
		{
			Format(cmdline, sizeof(cmdline), "%s %s", name, arg);
		}
	}
	else
	{
		if (arg == NULL)
		{
			Format(cmdline, sizeof(cmdline), "\"%s\"", name);
		}
		else
		{
			Format(cmdline, sizeof(cmdline), "\"%s\" %s", name, arg);
		}
	}

	Zero(&info, sizeof(info));
	Zero(&ret, sizeof(ret));
	info.cb = sizeof(info);
	info.dwFlags = STARTF_USESHOWWINDOW;
	info.wShowWindow = (hide == false ? SW_SHOWDEFAULT : SW_HIDE);

	Trim(cmdline);

	if (disableWow)
	{
		p = MsDisableWow64FileSystemRedirection();
	}

	if (CreateProcess(NULL, cmdline, NULL, NULL, FALSE,
		(hide == false ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW | CREATE_NEW_CONSOLE) | NORMAL_PRIORITY_CLASS,
		NULL, NULL, &info, &ret) == FALSE)
	{
		if (disableWow)
		{
			MsRestoreWow64FileSystemRedirection(p);
		}
		return NULL;
	}
	if (disableWow)
	{
		MsRestoreWow64FileSystemRedirection(p);
	}

	if (process_id != NULL)
	{
		*process_id = ret.dwProcessId;
	}

	CloseHandle(ret.hThread);
	return ret.hProcess;
}

// Start the process
bool Win32RunW(wchar_t *filename, wchar_t *arg, bool hide, bool wait)
{
	STARTUPINFOW info;
	PROCESS_INFORMATION ret;
	wchar_t cmdline[MAX_SIZE];
	wchar_t name[MAX_PATH];
	// Validate arguments
	if (filename == NULL)
	{
		return false;
	}

	if (IsNt() == false)
	{
		char *filename_a = CopyUniToStr(filename);
		char *arg_a = CopyUniToStr(arg);
		bool ret;

		ret = Win32Run(filename_a, arg_a, hide, wait);

		Free(filename_a);
		Free(arg_a);

		return ret;
	}

	UniStrCpy(name, sizeof(name), filename);
	UniTrim(name);

	if (UniSearchStr(name, L"\"", 0) == INFINITE)
	{
		if (arg == NULL)
		{
			UniFormat(cmdline, sizeof(cmdline), L"%s", name);
		}
		else
		{
			UniFormat(cmdline, sizeof(cmdline), L"%s %s", name, arg);
		}
	}
	else
	{
		if (arg == NULL)
		{
			UniFormat(cmdline, sizeof(cmdline), L"\"%s\"", name);
		}
		else
		{
			UniFormat(cmdline, sizeof(cmdline), L"\"%s\" %s", name, arg);
		}
	}

	Zero(&info, sizeof(info));
	Zero(&ret, sizeof(ret));
	info.cb = sizeof(info);
	info.dwFlags = STARTF_USESHOWWINDOW;
	info.wShowWindow = (hide == false ? SW_SHOWDEFAULT : SW_HIDE);

	UniTrim(cmdline);

	if (CreateProcessW(NULL, cmdline, NULL, NULL, FALSE,
		(hide == false ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW | CREATE_NEW_CONSOLE) | NORMAL_PRIORITY_CLASS,
		NULL, NULL, &info, &ret) == FALSE)
	{
		return false;
	}

	if (wait)
	{
		WaitForSingleObject(ret.hProcess, INFINITE);
	}

	CloseHandle(ret.hThread);
	CloseHandle(ret.hProcess);

	return true;
}
bool Win32Run(char *filename, char *arg, bool hide, bool wait)
{
	STARTUPINFO info;
	PROCESS_INFORMATION ret;
	char cmdline[MAX_SIZE];
	char name[MAX_PATH];
	// Validate arguments
	if (filename == NULL)
	{
		return false;
	}

	StrCpy(name, sizeof(name), filename);
	Trim(name);

	if (SearchStr(name, "\"", 0) == INFINITE)
	{
		if (arg == NULL)
		{
			Format(cmdline, sizeof(cmdline), "%s", name);
		}
		else
		{
			Format(cmdline, sizeof(cmdline), "%s %s", name, arg);
		}
	}
	else
	{
		if (arg == NULL)
		{
			Format(cmdline, sizeof(cmdline), "\"%s\"", name);
		}
		else
		{
			Format(cmdline, sizeof(cmdline), "\"%s\" %s", name, arg);
		}
	}

	Zero(&info, sizeof(info));
	Zero(&ret, sizeof(ret));
	info.cb = sizeof(info);
	info.dwFlags = STARTF_USESHOWWINDOW;
	info.wShowWindow = (hide == false ? SW_SHOWDEFAULT : SW_HIDE);

	Trim(cmdline);

	if (CreateProcess(NULL, cmdline, NULL, NULL, FALSE,
		(hide == false ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW | CREATE_NEW_CONSOLE) | NORMAL_PRIORITY_CLASS,
		NULL, NULL, &info, &ret) == FALSE)
	{
		return false;
	}

	if (wait)
	{
		WaitForSingleObject(ret.hProcess, INFINITE);
	}

	CloseHandle(ret.hThread);
	CloseHandle(ret.hProcess);

	return true;
}

// Get the Thread ID
UINT Win32ThreadId()
{
	return GetCurrentThreadId();
}

// Rename the file
bool Win32FileRenameW(wchar_t *old_name, wchar_t *new_name)
{
	// Validate arguments
	if (old_name == NULL || new_name == NULL)
	{
		return false;
	}

	if (IsNt() == false)
	{
		char *old_name_a = CopyUniToStr(old_name);
		char *new_name_a = CopyUniToStr(new_name);
		bool ret = Win32FileRename(old_name_a, new_name_a);

		Free(old_name_a);
		Free(new_name_a);

		return ret;
	}

	// Rename
	if (MoveFileW(old_name, new_name) == FALSE)
	{
		return false;
	}

	return true;
}
bool Win32FileRename(char *old_name, char *new_name)
{
	// Validate arguments
	if (old_name == NULL || new_name == NULL)
	{
		return false;
	}

	// Rename
	if (MoveFile(old_name, new_name) == FALSE)
	{
		return false;
	}

	return true;
}

// Getting the name of the directory where the EXE file is in
void Win32GetExeDirW(wchar_t *name, UINT size)
{
	wchar_t exe_path[MAX_SIZE];
	wchar_t exe_dir[MAX_SIZE];
	// Validate arguments
	if (name == NULL)
	{
		return;
	}

	if (IsNt() == false)
	{
		char name_a[MAX_PATH];

		Win32GetExeDir(name_a, sizeof(name_a));

		StrToUni(name, size, name_a);

		return;
	}

	// Get the EXE file name
	GetModuleFileNameW(NULL, exe_path, sizeof(exe_path));

	// Get the directory name
	Win32GetDirFromPathW(exe_dir, sizeof(exe_dir), exe_path);

	UniStrCpy(name, size, exe_dir);
}
void Win32GetExeDir(char *name, UINT size)
{
	char exe_path[MAX_SIZE];
	char exe_dir[MAX_SIZE];
	// Validate arguments
	if (name == NULL)
	{
		return;
	}

	// Get the EXE file name
	GetModuleFileName(NULL, exe_path, sizeof(exe_path));

	// Get the directory name
	Win32GetDirFromPath(exe_dir, sizeof(exe_dir), exe_path);

	StrCpy(name, size, exe_dir);
}

// Remove the '\' at the end
void Win32NukuEnW(wchar_t *dst, UINT size, wchar_t *src)
{
	wchar_t str[MAX_SIZE];
	int i;
	if (src)
	{
		UniStrCpy(str, sizeof(str), src);
	}
	else
	{
		UniStrCpy(str, sizeof(str), dst);
	}
	i = UniStrLen(str);
	if (str[i - 1] == L'\\')
	{
		str[i - 1] = 0;
	}
	UniStrCpy(dst, size, str);
}
void Win32NukuEn(char *dst, UINT size, char *src)
{
	char str[MAX_SIZE];
	int i;
	if (src)
	{
		StrCpy(str, sizeof(str), src);
	}
	else
	{
		StrCpy(str, sizeof(str), dst);
	}
	i = StrLen(str);
	if (str[i - 1] == '\\')
	{
		str[i - 1] = 0;
	}
	StrCpy(dst, size, str);
}

// Get the directory name from path
void Win32GetDirFromPathW(wchar_t *dst, UINT size, wchar_t *src)
{
	wchar_t str[MAX_SIZE];
	int i,len;
	wchar_t c;
	wchar_t tmp[MAX_SIZE];
	int wp;
	if (src)
	{
		UniStrCpy(str, sizeof(str), src);
	}
	else
	{
		UniStrCpy(str, sizeof(str), dst);
	}
	Win32NukuEnW(str, sizeof(str), NULL);
	wp = 0;
	len = UniStrLen(str);
	dst[0] = 0;
	for (i = 0;i < len;i++)
	{
		c = str[i];
		switch (c)
		{
		case L'\\':
			tmp[wp] = 0;
			wp = 0;
			UniStrCat(dst, size, tmp);
			UniStrCat(dst, size, L"\\");
			break;
		default:
			tmp[wp] = c;
			wp++;
			break;
		}
	}
	Win32NukuEnW(dst, size, NULL);
}
void Win32GetDirFromPath(char *dst, UINT size, char *src)
{
	char str[MAX_SIZE];
	int i,len;
	char c;
	char tmp[MAX_SIZE];
	int wp;
	if (src)
	{
		StrCpy(str, sizeof(str), src);
	}
	else
	{
		StrCpy(str, sizeof(str), dst);
	}
	Win32NukuEn(str, sizeof(str), NULL);
	wp = 0;
	len = StrLen(str);
	dst[0] = 0;
	for (i = 0;i < len;i++)
	{
		c = str[i];
		switch (c)
		{
		case '\\':
			tmp[wp] = 0;
			wp = 0;
			StrCat(dst, size, tmp);
			StrCat(dst, size, "\\");
			break;
		default:
			tmp[wp] = c;
			wp++;
			break;
		}
	}
	Win32NukuEn(dst, size, NULL);
}

// Delete the directory
bool Win32DeleteDirW(wchar_t *name)
{
	// Validate arguments
	if (name == NULL)
	{
		return false;
	}

	if (IsNt() == false)
	{
		char *name_a = CopyUniToStr(name);
		bool ret = Win32DeleteDir(name_a);

		Free(name_a);

		return ret;
	}

	if (RemoveDirectoryW(name) == FALSE)
	{
		return false;
	}
	return true;
}
bool Win32DeleteDir(char *name)
{
	// Validate arguments
	if (name == NULL)
	{
		return false;
	}

	if (RemoveDirectory(name) == FALSE)
	{
		return false;
	}
	return true;
}

// Create a directory
bool Win32MakeDirW(wchar_t *name)
{
	// Validate arguments
	if (name == NULL)
	{
		return false;
	}

	if (IsNt() == false)
	{
		char *name_a = CopyUniToStr(name);
		bool ret = Win32MakeDir(name_a);

		Free(name_a);

		return ret;
	}

	if (CreateDirectoryW(name, NULL) == FALSE)
	{
		return false;
	}

	return true;
}
bool Win32MakeDir(char *name)
{
	// Validate arguments
	if (name == NULL)
	{
		return false;
	}

	if (CreateDirectory(name, NULL) == FALSE)
	{
		return false;
	}

	return true;
}

// Delete the file
bool Win32FileDeleteW(wchar_t *name)
{
	// Validate arguments
	if (name == NULL)
	{
		return false;
	}

	if (IsNt() == false)
	{
		bool ret;
		char *name_a = CopyUniToStr(name);

		ret = Win32FileDelete(name_a);

		Free(name_a);

		return ret;
	}

	if (DeleteFileW(name) == FALSE)
	{
		return false;
	}
	return true;
}
bool Win32FileDelete(char *name)
{
	// Validate arguments
	if (name == NULL)
	{
		return false;
	}

	if (DeleteFile(name) == FALSE)
	{
		return false;
	}
	return true;
}

// Seek in the file
bool Win32FileSeek(void *pData, UINT mode, int offset)
{
	WIN32IO *p;
	DWORD ret;
	// Validate arguments
	if (pData == NULL)
	{
		return false;
	}
	if (mode != FILE_BEGIN && mode != FILE_END && mode != FILE_CURRENT)
	{
		return false;
	}

	p = (WIN32IO *)pData;
	ret = SetFilePointer(p->hFile, (LONG)offset, NULL, mode);
	if (ret == INVALID_SET_FILE_POINTER || ret == ERROR_NEGATIVE_SEEK)
	{
		return false;
	}
	return true;
}

// Get the file size
UINT64 Win32FileSize(void *pData)
{
	WIN32IO *p;
	UINT64 ret;
	DWORD tmp;
	// Validate arguments
	if (pData == NULL)
	{
		return 0;
	}

	p = (WIN32IO *)pData;
	tmp = 0;
	ret = GetFileSize(p->hFile, &tmp);
	if (ret == (DWORD)-1)
	{
		return 0;
	}

	if (tmp != 0)
	{
		ret += (UINT64)tmp * 4294967296ULL;
	}

	return ret;
}

// Write to the file
bool Win32FileWrite(void *pData, void *buf, UINT size)
{
	WIN32IO *p;
	DWORD write_size;
	// Validate arguments
	if (pData == NULL || buf == NULL || size == 0)
	{
		return false;
	}

	p = (WIN32IO *)pData;
	if (WriteFile(p->hFile, buf, size, &write_size, NULL) == FALSE)
	{
		return false;
	}

	if (write_size != size)
	{
		return false;
	}

	return true;
}

// Read from a file
bool Win32FileRead(void *pData, void *buf, UINT size)
{
	WIN32IO *p;
	DWORD read_size;
	// Validate arguments
	if (pData == NULL || buf == NULL || size == 0)
	{
		return false;
	}

	p = (WIN32IO *)pData;
	if (ReadFile(p->hFile, buf, size, &read_size, NULL) == FALSE)
	{
		return false;
	}

	if (read_size != size)
	{
		return false;
	}
	
	return true;;
}

// Close the file
void Win32FileClose(void *pData, bool no_flush)
{
	WIN32IO *p;
	// Validate arguments
	if (pData == NULL)
	{
		return;
	}

	p = (WIN32IO *)pData;
	if (p->WriteMode && no_flush == false)
	{
		FlushFileBuffers(p->hFile);
	}
	CloseHandle(p->hFile);
	p->hFile = NULL;

	// Memory release
	Win32MemoryFree(p);
}

// Get the date of the file
bool Win32FileGetDate(void *pData, UINT64 *created_time, UINT64 *updated_time, UINT64 *accessed_date)
{
	WIN32IO *p;
	BY_HANDLE_FILE_INFORMATION info;
	SYSTEMTIME st_create, st_update, st_access;
	// Validate arguments
	if (pData == NULL)
	{
		return false;
	}

	p = (WIN32IO *)pData;

	Zero(&info, sizeof(info));

	if (GetFileInformationByHandle(p->hFile, &info) == false)
	{
		return false;
	}

	Zero(&st_create, sizeof(st_create));
	Zero(&st_update, sizeof(st_update));
	Zero(&st_access, sizeof(st_access));

	FileTimeToSystemTime(&info.ftCreationTime, &st_create);
	FileTimeToSystemTime(&info.ftLastWriteTime, &st_update);
	FileTimeToSystemTime(&info.ftLastAccessTime, &st_access);

	if (created_time != NULL)
	{
		*created_time = SystemToUINT64(&st_create);
	}

	if (updated_time != NULL)
	{
		*updated_time = SystemToUINT64(&st_update);
	}

	if (accessed_date != NULL)
	{
		*accessed_date = SystemToUINT64(&st_access);
	}

	return true;
}

// Set the date of the file
bool Win32FileSetDate(void *pData, UINT64 created_time, UINT64 updated_time)
{
	WIN32IO *p;
	SYSTEMTIME st_created_time, st_updated_time;
	FILETIME ft_created_time, ft_updated_time;
	FILETIME *p_created_time = NULL, *p_updated_time = NULL;
	// Validate arguments
	if (pData == NULL || (created_time == 0 && updated_time == 0))
	{
		return false;
	}

	p = (WIN32IO *)pData;

	Zero(&st_created_time, sizeof(st_created_time));
	Zero(&st_updated_time, sizeof(st_updated_time));

	if (created_time != 0)
	{
		UINT64ToSystem(&st_created_time, created_time);

		SystemTimeToFileTime(&st_created_time, &ft_created_time);

		p_created_time = &ft_created_time;
	}

	if (updated_time != 0)
	{
		UINT64ToSystem(&st_updated_time, updated_time);

		SystemTimeToFileTime(&st_updated_time, &ft_updated_time);

		p_updated_time = &ft_updated_time;
	}

	return SetFileTime(p->hFile, p_created_time, NULL, p_updated_time);
}

// Flush to the file
void Win32FileFlush(void *pData)
{
	WIN32IO *p;
	// Validate arguments
	if (pData == NULL)
	{
		return;
	}

	p = (WIN32IO *)pData;
	if (p->WriteMode)
	{
		FlushFileBuffers(p->hFile);
	}
}

// Open the file
void *Win32FileOpenW(wchar_t *name, bool write_mode, bool read_lock)
{
	WIN32IO *p;
	HANDLE h;
	DWORD lock_mode;
	// Validate arguments
	if (name == NULL)
	{
		return NULL;
	}

	if (IsNt() == false)
	{
		void *ret;
		char *name_a = CopyUniToStr(name);

		ret = Win32FileOpen(name_a, write_mode, read_lock);

		Free(name_a);

		return ret;
	}

	if (write_mode)
	{
		lock_mode = FILE_SHARE_READ;
	}
	else
	{
		if (read_lock == false)
		{
			lock_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
		}
		else
		{
			lock_mode = FILE_SHARE_READ;
		}
	}

	// Open the file
	h = CreateFileW(name,
		(write_mode ? GENERIC_READ | GENERIC_WRITE : GENERIC_READ),
		lock_mode,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (h == INVALID_HANDLE_VALUE)
	{
		UINT ret = GetLastError();
		// Failure
		return NULL;
	}

	// Memory allocation
	p = Win32MemoryAlloc(sizeof(WIN32IO));
	// Store Handle
	p->hFile = h;

	p->WriteMode = write_mode;

	return (void *)p;
}
void *Win32FileOpen(char *name, bool write_mode, bool read_lock)
{
	WIN32IO *p;
	HANDLE h;
	DWORD lock_mode;
	// Validate arguments
	if (name == NULL)
	{
		return NULL;
	}

	if (write_mode)
	{
		lock_mode = FILE_SHARE_READ;
	}
	else
	{
		if (read_lock == false)
		{
			lock_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
		}
		else
		{
			lock_mode = FILE_SHARE_READ;
		}
	}

	// Open the file
	h = CreateFile(name,
		(write_mode ? GENERIC_READ | GENERIC_WRITE : GENERIC_READ),
		lock_mode,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (h == INVALID_HANDLE_VALUE)
	{
		UINT ret = GetLastError();
		// Failure
		return NULL;
	}

	// Memory allocation
	p = Win32MemoryAlloc(sizeof(WIN32IO));
	// Store Handle
	p->hFile = h;

	p->WriteMode = write_mode;

	return (void *)p;
}

// Create a file
void *Win32FileCreateW(wchar_t *name)
{
	WIN32IO *p;
	HANDLE h;
	// Validate arguments
	if (name == NULL)
	{
		return NULL;
	}

	if (IsNt() == false)
	{
		void *ret;
		char *name_a = CopyUniToStr(name);

		ret = Win32FileCreate(name_a);

		Free(name_a);

		return ret;
	}

	// Create a file
	h = CreateFileW(name, GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
		NULL);
	if (h == INVALID_HANDLE_VALUE)
	{
		h = CreateFileW(name, GENERIC_READ | GENERIC_WRITE,
			FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN,
			NULL);
		if (h == INVALID_HANDLE_VALUE)
		{
			return NULL;
		}
	}

	// Memory allocation
	p = Win32MemoryAlloc(sizeof(WIN32IO));
	// Store Handle
	p->hFile = h;

	p->WriteMode = true;

	return (void *)p;
}
void *Win32FileCreate(char *name)
{
	WIN32IO *p;
	HANDLE h;
	// Validate arguments
	if (name == NULL)
	{
		return NULL;
	}

	// Create a file
	h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
		NULL);
	if (h == INVALID_HANDLE_VALUE)
	{
		h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
			FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN,
			NULL);
		if (h == INVALID_HANDLE_VALUE)
		{
			return NULL;
		}
	}

	// Memory allocation
	p = Win32MemoryAlloc(sizeof(WIN32IO));
	// Store Handle
	p->hFile = h;

	p->WriteMode = true;

	return (void *)p;
}

#define	SIZE_OF_CALLSTACK_SYM	10000
#define	CALLSTACK_DEPTH			12

// Get the call stack
CALLSTACK_DATA *Win32GetCallStack()
{
#ifndef	WIN32_NO_DEBUG_HELP_DLL
	DWORD current_eip32 = 0, current_esp32 = 0, current_ebp32 = 0;
	UINT64 current_eip = 0, current_esp = 0, current_ebp = 0;
	STACKFRAME64 sf;
	CALLSTACK_DATA *cs = NULL, *s;

#ifdef	CPU_64
	CONTEXT context;
#endif	// CPU_64

	bool ret;
	UINT depth = 0;

#ifndef	CPU_64
	// Register acquisition (32 bit)
	__asm
	{
		mov current_esp32, esp
		mov current_ebp32, ebp
	};

	current_eip32 = (DWORD)Win32GetCallStack;

	current_eip = (UINT64)current_eip32;
	current_esp = (UINT64)current_esp32;
	current_ebp = (UINT64)current_ebp32;
#else	// CPU_64
	// Register acquisition (64 bit)
	Zero(&context, sizeof(context));
	context.ContextFlags = CONTEXT_FULL;
	RtlCaptureContext(&context);
#endif	// CPU_64

	Zero(&sf, sizeof(sf));

#ifndef	CPU_64
	sf.AddrPC.Offset = current_eip;
	sf.AddrStack.Offset = current_esp;
	sf.AddrFrame.Offset = current_ebp;
#else	// CPU_64
	sf.AddrPC.Offset = context.Rip;
	sf.AddrStack.Offset = context.Rsp;
	sf.AddrFrame.Offset = context.Rsp;
#endif	// CPU_64

	sf.AddrPC.Mode = AddrModeFlat;
	sf.AddrStack.Mode = AddrModeFlat;
	sf.AddrFrame.Mode = AddrModeFlat;

	while (true)
	{
		DWORD type = IMAGE_FILE_MACHINE_I386;

#ifdef	CPU_64
		type = IMAGE_FILE_MACHINE_AMD64;
#endif	// CPU_64

		if ((depth++) >= CALLSTACK_DEPTH)
		{
			break;
		}

#ifndef	CPU_64
		ret = StackWalk64(type,
			hCurrentProcessHandle,
			GetCurrentThread(),
			&sf,
			NULL, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL);
#else	// CPU_64
		ret = StackWalk64(type,
			hCurrentProcessHandle,
			GetCurrentThread(),
			&sf,
			&context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL);
#endif	// CPU_64
		if (ret == false || sf.AddrFrame.Offset == 0)
		{
			break;
		}

		if (cs == NULL)
		{
			cs = OSMemoryAlloc(sizeof(CALLSTACK_DATA));
			s = cs;
		}
		else
		{
			s->next = OSMemoryAlloc(sizeof(CALLSTACK_DATA));
			s = s->next;
		}
		s->symbol_cache = false;
		s->next = NULL;
		s->offset = sf.AddrPC.Offset;
		s->disp = 0;
		s->name = NULL;
		s->line = 0;
		s->filename[0] = 0;
	}

	return cs;
#else	// WIN32_NO_DEBUG_HELP_DLL
	return NULL;
#endif	// WIN32_NO_DEBUG_HELP_DLL
}

// Get the symbol information from the call stack
bool Win32GetCallStackSymbolInfo(CALLSTACK_DATA *s)
{
#ifdef	WIN32_NO_DEBUG_HELP_DLL
	return false;
#else	// WIN32_NO_DEBUG_HELP_DLL
	UINT64 disp;
	UINT disp32, len;
	IMAGEHLP_SYMBOL64 *sym;
	IMAGEHLP_LINE64 line;
	char tmp[MAX_PATH];
	// Validate arguments
	if (s == NULL)
	{
		return false;
	}

	if (s->symbol_cache)
	{
		return true;
	}

	sym = OSMemoryAlloc(SIZE_OF_CALLSTACK_SYM);
	sym->SizeOfStruct = SIZE_OF_CALLSTACK_SYM;
	sym->MaxNameLength = SIZE_OF_CALLSTACK_SYM - sizeof(IMAGEHLP_SYMBOL64);

	if (SymGetSymFromAddr64(hCurrentProcessHandle, s->offset, &disp, sym))
	{
		s->disp = disp;
		s->name = OSMemoryAlloc((UINT)strlen(sym->Name) + 1);
		lstrcpy(s->name, sym->Name);
	}
	else
	{
		s->disp = 0;
		s->name = NULL;
	}

	Zero(&line, sizeof(line));
	line.SizeOfStruct = sizeof(line);
	if (SymGetLineFromAddr64(hCurrentProcessHandle, s->offset, &disp32, &line))
	{
		disp = (UINT64)disp32;
		s->line = line.LineNumber;
		lstrcpy(s->filename, line.FileName);
		Win32GetDirFromPath(tmp, sizeof(tmp), s->filename);
		len = lstrlen(tmp);
		lstrcpy(tmp, &s->filename[len + 1]);
		lstrcpy(s->filename, tmp);
	}
	else
	{
		s->line = 0;
		s->filename[0] = 0;
	}

	OSMemoryFree(sym);

	s->symbol_cache = true;

	return true;
#endif	// WIN32_NO_DEBUG_HELP_DLL
}

// Default Win32 thread
DWORD CALLBACK Win32DefaultThreadProc(void *param)
{
	WIN32THREADSTARTUPINFO *info = (WIN32THREADSTARTUPINFO *)param;
	// Validate arguments
	if (info == NULL)
	{
		return 0;
	}

	Win32InitNewThread();

	CoInitialize(NULL);

	// Call the thread function
	info->thread_proc(info->thread, info->param);

	// Release the reference
	ReleaseThread(info->thread);

	Win32MemoryFree(info);

	FreeOpenSSLThreadState();

	CoUninitialize();

	_endthreadex(0);
	return 0;
}

// Wait for the termination of the thread
bool Win32WaitThread(THREAD *t)
{
	WIN32THREAD *w;
	// Validate arguments
	if (t == NULL)
	{
		return false;
	}
	w = (WIN32THREAD *)t->pData;
	if (w == NULL)
	{
		return false;
	}

	// Wait for the thread event
	if (WaitForSingleObject(w->hThread, INFINITE) == WAIT_OBJECT_0)
	{
		// The thread was signaled
		return true;
	}

	// Wait failure (time-out, etc.)
	return false;
}

// Release the thread
void Win32FreeThread(THREAD *t)
{
	WIN32THREAD *w;
	// Validate arguments
	if (t == NULL)
	{
		return;
	}
	w = (WIN32THREAD *)t->pData;
	if (w == NULL)
	{
		return;
	}

	// Close the handle
	CloseHandle(w->hThread);

	// Memory release
	Win32MemoryFree(t->pData);
	t->pData = NULL;
}

// Thread initialization
bool Win32InitThread(THREAD *t)
{
	WIN32THREAD *w;
	HANDLE hThread;
	DWORD thread_id;
	WIN32THREADSTARTUPINFO *info;
	// Validate arguments
	if (t == NULL)
	{
		return false;
	}
	if (t->thread_proc == NULL)
	{
		return false;
	}

	// Thread data generation
	w = Win32MemoryAlloc(sizeof(WIN32THREAD));

	// Creating the startup information
	info = Win32MemoryAlloc(sizeof(WIN32THREADSTARTUPINFO));
	info->param = t->param;
	info->thread_proc = t->thread_proc;
	info->thread = t;
	AddRef(t->ref);

	// Thread creation
	t->pData = w;
	hThread = (HANDLE)_beginthreadex(NULL, 0, Win32DefaultThreadProc, info, 0, &thread_id);
	if (hThread == NULL)
	{
		// Thread creation failure
		t->pData = NULL;
		Release(t->ref);
		Win32MemoryFree(info);
		Win32MemoryFree(w);
		return false;
	}

	// Save the thread information
	w->hThread = hThread;
	w->thread_id = thread_id;

	return true;
}

// Initialize the library for Win32
void Win32Init()
{
	INITCOMMONCONTROLSEX c;
	OSVERSIONINFO os;

	// Get whether it's in a Windows NT
	Zero(&os, sizeof(os));
	os.dwOSVersionInfoSize = sizeof(os);
	GetVersionEx(&os);

	if (os.dwPlatformId == VER_PLATFORM_WIN32_NT)
	{
		// NT system
		win32_is_nt = true;
	}
	else
	{
		// 9x system
		win32_is_nt = false;
	}

	// Open the stdout
	if (hstdout == INVALID_HANDLE_VALUE)
	{
		hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
	}

	// Open the stdin
	if (hstdin == INVALID_HANDLE_VALUE)
	{
		hstdin = GetStdHandle(STD_INPUT_HANDLE);
	}

	Win32InitNewThread();

	CoInitialize(NULL);

	InitializeCriticalSection(&fasttick_lock);

#ifdef	WIN32_USE_HEAP_API_FOR_MEMORY
	use_heap_api = true;
#else	// WIN32_USE_HEAP_API_FOR_MEMORY
	use_heap_api = false;
#endif	// WIN32_USE_HEAP_API_FOR_MEMORY

	if (MayaquaIsDotNetMode())
	{
		// If an heap API is called from .NET API, it might crush
		use_heap_api = false;
	}

	if (IsNt() == false)
	{
		// Do not use the heap related API in Win9x
		use_heap_api = false;
	}

	if (use_heap_api)
	{
		heap_handle = HeapCreate(0, 0, 0);
	}

	// Get the process pseudo handle
	hCurrentProcessHandle = GetCurrentProcess();

	// Initialization of the current directory
	// Win32InitCurrentDir(); /* Don't do */

	// Initialization of the symbol handler
	if (IsMemCheck())
	{
#ifndef	WIN32_NO_DEBUG_HELP_DLL
		SymInitialize(hCurrentProcessHandle, NULL, TRUE);
#endif	// WIN32_NO_DEBUG_HELP_DLL
	}

	// Initialization of the Common Control
	Zero(&c, sizeof(INITCOMMONCONTROLSEX));
	c.dwSize = sizeof(INITCOMMONCONTROLSEX);
	c.dwICC = ICC_ANIMATE_CLASS | ICC_BAR_CLASSES | ICC_COOL_CLASSES |
		ICC_DATE_CLASSES | ICC_HOTKEY_CLASS | ICC_INTERNET_CLASSES |
		ICC_LISTVIEW_CLASSES | ICC_NATIVEFNTCTL_CLASS |
		ICC_PAGESCROLLER_CLASS | ICC_PROGRESS_CLASS |
		ICC_TAB_CLASSES | ICC_TREEVIEW_CLASSES | ICC_UPDOWN_CLASS | ICC_USEREX_CLASSES |
		ICC_WIN95_CLASSES;
	InitCommonControlsEx(&c);
}

// Release the library for Win32
void Win32Free()
{
	// Close the symbol handler
	if (IsMemCheck())
	{
#ifndef	WIN32_NO_DEBUG_HELP_DLL
		SymCleanup(hCurrentProcessHandle);
#endif	// WIN32_NO_DEBUG_HELP_DLL
	}

	if (use_heap_api)
	{
		HeapDestroy(heap_handle);
		heap_handle = NULL;
	}

	CoUninitialize();

	DeleteCriticalSection(&fasttick_lock);
}

// Memory allocation
void *Win32MemoryAlloc(UINT size)
{
	if (use_heap_api)
	{
		return HeapAlloc(heap_handle, 0, size);
	}
	else
	{
		return malloc(size);
	}
}

// Memory reallocation
void *Win32MemoryReAlloc(void *addr, UINT size)
{
	if (use_heap_api)
	{
		return HeapReAlloc(heap_handle, 0, addr, size);
	}
	else
	{
		return realloc(addr, size);
	}
}

// Memory allocation
void Win32MemoryFree(void *addr)
{
	if (use_heap_api)
	{
		HeapFree(heap_handle, 0, addr);
	}
	else
	{
		free(addr);
	}
}

// Get the system timer
UINT Win32GetTick()
{
	return (UINT)timeGetTime();
}

// Get the System Time
void Win32GetSystemTime(SYSTEMTIME *system_time)
{
	// Get the System Time
	GetSystemTime(system_time);
}

// Increment of 32bit integer
void Win32Inc32(UINT *value)
{
	InterlockedIncrement(value);
}

// Decrement of 32bit integer
void Win32Dec32(UINT *value)
{
	InterlockedDecrement(value);
}

// Sleep the thread
void Win32Sleep(UINT time)
{
	Sleep(time);
}

// Creating a lock
LOCK *Win32NewLock()
{
	// Memory allocation
	LOCK *lock = Win32MemoryAlloc(sizeof(LOCK));

	// Allocate a critical section
	CRITICAL_SECTION *critical_section = Win32MemoryAlloc(sizeof(CRITICAL_SECTION));

	if (lock == NULL || critical_section == NULL)
	{
		Win32MemoryFree(lock);
		Win32MemoryFree(critical_section);
		return NULL;
	}

	// Initialize the critical section
	InitializeCriticalSection(critical_section);

	lock->pData = (void *)critical_section;
	lock->Ready = true;

	return lock;
}

// Lock
bool Win32Lock(LOCK *lock)
{
	CRITICAL_SECTION *critical_section;
	if (lock->Ready == false)
	{
		// State is invalid
		return false;
	}

	// Enter the critical section
	critical_section = (CRITICAL_SECTION *)lock->pData;
	EnterCriticalSection(critical_section);

	return true;
}

// Unlock
void Win32Unlock(LOCK *lock)
{
	Win32UnlockEx(lock, false);
}
void Win32UnlockEx(LOCK *lock, bool inner)
{
	CRITICAL_SECTION *critical_section;
	if (lock->Ready == false && inner == false)
	{
		// State is invalid
		return;
	}

	// Leave the critical section
	critical_section = (CRITICAL_SECTION *)lock->pData;
	LeaveCriticalSection(critical_section);
}

// Delete the lock
void Win32DeleteLock(LOCK *lock)
{
	CRITICAL_SECTION *critical_section;
	// Reset the Ready flag safely
	Win32Lock(lock);
	lock->Ready = false;
	Win32UnlockEx(lock, true);

	// Delete the critical section
	critical_section = (CRITICAL_SECTION *)lock->pData;
	DeleteCriticalSection(critical_section);

	// Memory release
	Win32MemoryFree(critical_section);
	Win32MemoryFree(lock);
}

// Initialization of the event
void Win32InitEvent(EVENT *event)
{
	// Creating an auto-reset event
	HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

	event->pData = hEvent;
}

// Set the event
void Win32SetEvent(EVENT *event)
{
	HANDLE hEvent = (HANDLE)event->pData;
	if (hEvent == NULL)
	{
		return;
	}

	SetEvent(hEvent);
}

// Reset the event
void Win32ResetEvent(EVENT *event)
{
	HANDLE hEvent = (HANDLE)event->pData;
	if (hEvent == NULL)
	{
		return;
	}

	ResetEvent(hEvent);
}

// Wait for the event
bool Win32WaitEvent(EVENT *event, UINT timeout)
{
	HANDLE hEvent = (HANDLE)event->pData;
	UINT ret;
	if (hEvent == NULL)
	{
		return false;
	}

	// Wait for an object
	ret = WaitForSingleObject(hEvent, timeout);
	if (ret == WAIT_TIMEOUT)
	{
		// Time-out
		return false;
	}
	else
	{
		// Signaled state
		return true;
	}
}

// Release of the event
void Win32FreeEvent(EVENT *event)
{
	HANDLE hEvent = (HANDLE)event->pData;
	if (hEvent == NULL)
	{
		return;
	}

	CloseHandle(hEvent);
}

// Fast getting 64 bit Tick functions for only Win32
UINT64 Win32FastTick64()
{
	static UINT last_tick = 0;
	static UINT counter = 0;
	UINT64 ret;
	UINT tick;

	EnterCriticalSection(&fasttick_lock);

	// Get the current tick value
	tick = Win32GetTick();

	if (last_tick > tick)
	{
		// When the previously acquired tick value is larger than acquired this time,
		// it can be considered that the counter have gone one around

		counter++;
	}

	last_tick = tick;

	ret = (UINT64)tick + (UINT64)counter * 4294967296ULL;

	LeaveCriticalSection(&fasttick_lock);

	if (start_tick == 0)
	{
		start_tick = ret;
		ret = 0;
	}
	else
	{
		ret -= start_tick;
	}

	return ret + 1;
}

// Read a string from the console
bool Win32InputW(wchar_t *str, UINT size)
{
	bool ret = false;
	// Validate arguments
	if (str == NULL)
	{
		return false;
	}
	if (size == 0)
	{
		size = 0x7fffffff;
	}

	if (str == NULL || size <= sizeof(wchar_t))
	{
		if (str != NULL)
		{
			Zero(str, size);
		}

		return Win32InputFromFileW(NULL, 0);
	}

	if (IsNt())
	{
		DWORD read_size = 0;

		if (ReadConsoleW(hstdin, str, (size - sizeof(wchar_t)), &read_size, NULL))
		{
			str[read_size] = 0;

			UniTrimCrlf(str);

			ret = true;
		}
		else
		{
			ret = Win32InputFromFileW(str, size);
		}
	}
	else
	{
		DWORD read_size = 0;
		UINT a_size = size / sizeof(wchar_t) + 16;
		char *a;

		a = ZeroMalloc(a_size);

		if (ReadConsoleA(hstdin, a, a_size - 1, &read_size, NULL))
		{
			a[read_size] = 0;

			StrToUni(str, size, a);

			UniTrimCrlf(str);

			ret = true;
		}
		else
		{
			ret = Win32InputFromFileW(str, size);
		}

		Free(a);
	}

	return ret;
}
// Get a line from standard input
bool Win32InputFromFileW(wchar_t *str, UINT size)
{
	char *a;
	if (str == NULL)
	{
		wchar_t tmp[MAX_SIZE];
		Win32InputFromFileW(tmp, sizeof(tmp));
		return false;
	}

	a = Win32InputFromFileLineA();
	if (a == NULL)
	{
		UniStrCpy(str, size, L"");
		return false;
	}

	UtfToUni(str, size, a);

	UniTrimCrlf(str);

	Free(a);

	return true;
}
char *Win32InputFromFileLineA()
{
	BUF *b = NewBuf();
	char zero = 0;
	char *ret = NULL;
	bool ok = true;

	while (true)
	{
		char c;
		UINT read_size = 0;

		if (ReadFile(hstdin, &c, 1, &read_size, NULL) == false)
		{
			ok = false;
			break;
		}
		if (read_size != 1)
		{
			ok = false;
			break;
		}

		WriteBuf(b, &c, 1);

		if (c == 10)
		{
			break;
		}
	}

	WriteBuf(b, &zero, 1);

	if (ok)
	{
		ret = CopyStr(b->Buf);
	}

	FreeBuf(b);

	return ret;
}

// Print the string to the console
void Win32PrintW(wchar_t *str)
{
	DWORD write_size = 0;
	// Validate arguments
	if (str == NULL)
	{
		return;
	}

	if (IsNt())
	{
		if (WriteConsoleW(hstdout, str, UniStrLen(str), &write_size, NULL) == false)
		{
			Win32PrintToFileW(str);
		}
	}
	else
	{
		char *ansi_str = CopyUniToStr(str);

		if (WriteConsoleA(hstdout, ansi_str, StrLen(ansi_str), &write_size, NULL) == false)
		{
			Win32PrintToFileW(str);
		}

		Free(ansi_str);
	}
}
void Win32PrintToFileW(wchar_t *str)
{
	char *utf;
	DWORD size = 0;
	// Validate arguments
	if (str == NULL)
	{
		return;
	}

	utf = CopyUniToUtf(str);

	WriteFile(hstdout, utf, StrLen(utf), &size, NULL);

	Free(utf);
}


#endif	// WIN32



// Developed by SoftEther VPN Project at University of Tsukuba in Japan.
// Department of Computer Science has dozens of overly-enthusiastic geeks.
// Join us: http://www.tsukuba.ac.jp/english/admission/
