近期在 mycard 项目中尝试了自动化编译 YGOPRO,发现官方采用 premake 生成项目文件,但并没有给出具体方法。故在此记录下折腾的成果。
本笔记基本上是参考了 DailyShana 的 build 分支写成,感谢 soarqin 和 DailyShana 的工作。
本笔记的操作在 Windows 7 和 Visual Studio Community 2015 上测试通过,理论上也可以适用于XP之后的系统。

0. 准备

必须下载:

建议下载:

  • Git for Windows
    用于自动获取和更新 YGOPRO 的源码。此外其包含了 MSYS2,提供后文需要用到的 patch 工具。
    如果不使用 git,你将需要手动下载 YGOPRO 的子模块。此外,在上游和你都对源码做出修改后,你需要手动合并代码。
    本文默认你已经安装了 Git for Windows,并且设置了账号和 GitHub 的 ssh 公钥。
  • DirectX SDK
    让 YGOPRO 支持 Direct3D 模式的必备组件。
    如果不安装 DirectX SDK,你将需要更改 Irrlicht 的配置文件,并且编译出的 YGOPRO 将只能支持 OpenGL 模式。
  • Visual Studio Community 2015
    免费的正版Visual Studio,功能完全满足业余开发者的需求。

1. 获取 YGOPRO 源码

YGOPRO 以 GPL 协议开源,托管在 GitHub,用 git clone 即可获得。clone 时用 --recursive 来同时克隆子模块。

git clone https://github.com/Fluorohydride/ygopro.git --recursive

子模块的引用往往不是最新的,需要 git checkout master

cd ygopro/
cd ocgcore/
git checkout master
cd ../script/
git checkout master
cd ..

2. Premake 简介

Premake 是一个命令行工具,用脚本定义软件工程,然后根据脚本为 Visual Studio, Xcode, 或 GNU Make 等工具生成项目文件。1
Premake 的脚本采用 lua 语言,十分简洁易懂,参考 premake4.luaocgcore\premake4.lua 即可略知一二。
于是,我们要做的就是给 libeventFreeTypeIrrlichtLuaSQLite 写 premake 文件,并最后用 Premake 生成 Visual Studio 的项目文件(解决方案)。

3. 配置 libevent

解压 libevent-2.0.22-stable.tar.gz 到项目目录,将文件夹重命名为 event
为兼容 YGOPRO 源码,将 WIN32-Code 文件夹里的文件全部移动到 include 文件夹。
根据 libevent 的 Makefile,得到如下 premake4.lua,放到 event 文件夹里。

project "event"
kind "StaticLib"

includedirs { "include", "compat" }

files { "event.c", "evthread.c", "buffer.c", "bufferevent.c", "bufferevent_sock.c",
"bufferevent_filter.c", "bufferevent_pair.c", "listener.c", "bufferevent_ratelim.c",
"evmap.c", "log.c", "evutil.c", "evutil_rand.c", "strlcpy.c", "signal.c",
"event_tagging.c", "http.c", "evdns.c", "evrpc.c" }

configuration "windows"
files { "win32select.c", "evthread_win32.c", "buffer_iocp.c", "event_iocp.c", "bufferevent_async.c" }

4. 配置 FreeType

解压 freetype-2.7.tar.bz2 到项目目录,将文件夹重命名为 freetype
根据 freetype 的 vcxproj,得到如下 premake4.lua,放到 freetype 文件夹里。

project "freetype"
kind "StaticLib"

includedirs { "include" }
defines { "FT2_BUILD_LIBRARY" }

files { "src/autofit/autofit.c", "src/bdf/bdf.c", "src/cff/cff.c", "src/base/ftbase.c",
"src/base/ftbitmap.c", "src/cache/ftcache.c", "src/base/ftfstype.c", "src/base/ftgasp.c",
"src/base/ftglyph.c", "src/gzip/ftgzip.c", "src/base/ftinit.c", "src/lzw/ftlzw.c",
"src/base/ftstroke.c", "src/base/ftsystem.c", "src/smooth/smooth.c", "src/base/ftbbox.c",
"src/base/ftfntfmt.c", "src/base/ftmm.c", "src/base/ftpfr.c", "src/base/ftsynth.c",
"src/base/fttype1.c", "src/base/ftwinfnt.c", "src/base/ftlcdfil.c", "src/base/ftgxval.c",
"src/base/ftotval.c", "src/base/ftpatent.c", "src/pcf/pcf.c", "src/pfr/pfr.c",
"src/psaux/psaux.c", "src/pshinter/pshinter.c", "src/psnames/psmodule.c",
"src/raster/raster.c", "src/sfnt/sfnt.c", "src/truetype/truetype.c",
"src/type1/type1.c", "src/cid/type1cid.c", "src/type42/type42.c", "src/winfonts/winfnt.c" }

configuration "windows"
files { "builds/windows/ftdebug.c" }

5. 配置 Irrlicht

解压 irrlicht-1.8.4.zip 到项目目录,将文件夹重命名为 irrlicht
为使文件夹结构更加简洁,将 source\Irrlicht 剪切到 irrlicht 文件夹,重命名为 src
Irrlicht 本身对中文显示和输入的支持存在问题,soarqin 和 DailyShana 给出了用于 Irrlicht 1.8.0 的中文支持。用在 1.8.4 只需要再修改一点点代码。
将以下代码保存为 irrlicht.patch,在MSYS的命令行下用 patch 应用即可。

diff -ur --strip-trailing-cr irrlicht/include/IOSOperator.h irrlicht-fixed/include/IOSOperator.h
--- irrlicht/include/IOSOperator.h 2012-11-03 10:08:34.000000000 +0000
+++ irrlicht-fixed/include/IOSOperator.h 2015-06-14 17:32:58.000000000 +0000
@@ -26,11 +26,11 @@
}

//! Copies text to the clipboard
- virtual void copyToClipboard(const c8* text) const = 0;
+ virtual void copyToClipboard(const c16* text) const = 0;

//! Get text from the clipboard
/** \return Returns 0 if no string is in there. */
- virtual const c8* getTextFromClipboard() const = 0;
+ virtual const c16* getTextFromClipboard() const = 0;

//! Get the processor speed in megahertz
/** \param MHz The integer variable to store the speed in.
diff -ur --strip-trailing-cr irrlicht/include/irrTypes.h irrlicht-fixed/include/irrTypes.h
--- irrlicht/include/irrTypes.h 2012-11-03 10:08:34.000000000 +0000
+++ irrlicht-fixed/include/irrTypes.h 2015-06-14 17:32:58.000000000 +0000
@@ -48,7 +48,9 @@
typedef signed short s16;
#endif

-
+//! 16 bit character variable.
+/** This is a typedef for wchar_t, it ensures portability of the engine. */
+typedef wchar_t c16;

//! 32 bit unsigned variable.
/** This is a typedef for unsigned int, it ensures portability of the engine. */
diff -ur --strip-trailing-cr irrlicht/include/Keycodes.h irrlicht-fixed/include/Keycodes.h
--- irrlicht/include/Keycodes.h 2012-11-03 10:08:32.000000000 +0000
+++ irrlicht-fixed/include/Keycodes.h 2015-06-14 17:32:58.000000000 +0000
@@ -89,7 +89,7 @@
KEY_KEY_X = 0x58, // X key
KEY_KEY_Y = 0x59, // Y key
KEY_KEY_Z = 0x5A, // Z key
- KEY_LWIN = 0x5B, // Left Windows key (Microsoft?Natural?keyboard)
+ KEY_LWIN = 0x5B, // Left Windows key (Microsoft庐 Natural庐 keyboard)
KEY_RWIN = 0x5C, // Right Windows key (Natural keyboard)
KEY_APPS = 0x5D, // Applications key (Natural keyboard)
KEY_SLEEP = 0x5F, // Computer Sleep key
diff -ur --strip-trailing-cr irrlicht/src/CD3D9ShaderMaterialRenderer.cpp irrlicht-fixed/src/CD3D9ShaderMaterialRenderer.cpp
--- irrlicht/src/CD3D9ShaderMaterialRenderer.cpp 2012-11-03 10:08:10.000000000 +0000
+++ irrlicht-fixed/src/CD3D9ShaderMaterialRenderer.cpp 2015-06-14 17:32:58.000000000 +0000
@@ -329,7 +329,7 @@
strDllName += (int)D3DX_SDK_VERSION;
strDllName += ".dll";

- HMODULE hMod = LoadLibrary(strDllName.c_str());
+ HMODULE hMod = LoadLibraryA(strDllName.c_str());
if (hMod)
pFn = (AssembleShaderFunction)GetProcAddress(hMod, "D3DXAssembleShader");

@@ -389,7 +389,7 @@
strDllName += (int)D3DX_SDK_VERSION;
strDllName += ".dll";

- HMODULE hMod = LoadLibrary(strDllName.c_str());
+ HMODULE hMod = LoadLibraryA(strDllName.c_str());
if (hMod)
pFn = (AssembleShaderFromFileFunction)GetProcAddress(hMod, "D3DXAssembleShaderFromFileA");

@@ -450,7 +450,7 @@
strDllName += (int)D3DX_SDK_VERSION;
strDllName += ".dll";

- HMODULE hMod = LoadLibrary(strDllName.c_str());
+ HMODULE hMod = LoadLibraryA(strDllName.c_str());
if (hMod)
pFn = (D3DXCompileShaderFunction)GetProcAddress(hMod, "D3DXCompileShader");

@@ -510,7 +510,7 @@
strDllName += (int)D3DX_SDK_VERSION;
strDllName += ".dll";

- HMODULE hMod = LoadLibrary(strDllName.c_str());
+ HMODULE hMod = LoadLibraryA(strDllName.c_str());
if (hMod)
pFn = (D3DXCompileShaderFromFileFunction)GetProcAddress(hMod, "D3DXCompileShaderFromFileA");

diff -ur --strip-trailing-cr irrlicht/src/CGUIEditBox.cpp irrlicht-fixed/src/CGUIEditBox.cpp
--- irrlicht/src/CGUIEditBox.cpp 2012-11-03 10:08:16.000000000 +0000
+++ irrlicht-fixed/src/CGUIEditBox.cpp 2015-06-14 17:32:58.000000000 +0000
@@ -286,7 +286,7 @@
const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;

- core::stringc s;
+ core::stringw s;
s = Text.subString(realmbgn, realmend - realmbgn).c_str();
Operator->copyToClipboard(s.c_str());
}
@@ -299,7 +299,7 @@
const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;

// copy
- core::stringc sc;
+ core::stringw sc;
sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
Operator->copyToClipboard(sc.c_str());

@@ -329,7 +329,7 @@
const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;

// add new character
- const c8* p = Operator->getTextFromClipboard();
+ const c16* p = Operator->getTextFromClipboard();
if (p)
{
if (MarkBegin == MarkEnd)
diff -ur --strip-trailing-cr irrlicht/src/CIrrDeviceWin32.cpp irrlicht-fixed/src/CIrrDeviceWin32.cpp
--- irrlicht/src/CIrrDeviceWin32.cpp 2012-11-05 07:14:12.000000000 +0000
+++ irrlicht-fixed/src/CIrrDeviceWin32.cpp 2015-06-14 17:32:58.000000000 +0000
@@ -16,14 +16,16 @@
#include "COSOperator.h"
#include "dimension2d.h"
#include "IGUISpriteBank.h"
+#include "IGUIEnvironment.h"
+#include "IGUIElement.h"
#include <winuser.h>
#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
#ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_
#define DIRECTINPUT_VERSION 0x0800
+#define INITGUID
#include <dinput.h>
#ifdef _MSC_VER
#pragma comment(lib, "dinput8.lib")
-#pragma comment(lib, "dxguid.lib")
#endif
#else
#ifdef _MSC_VER
@@ -900,8 +902,36 @@
KEYBOARD_INPUT_HKL = GetKeyboardLayout(0);
KEYBOARD_INPUT_CODEPAGE = LocaleIdToCodepage( LOWORD(KEYBOARD_INPUT_HKL) );
return 0;
+
+ case WM_IME_STARTCOMPOSITION:
+ {
+ dev = getDeviceFromHWnd(hWnd);
+ irr::gui::IGUIElement* ele = dev->getGUIEnvironment()->getFocus();
+ if(!ele)
+ break;
+ irr::core::position2di pos = ele->getAbsolutePosition().UpperLeftCorner;
+ HIMC hIMC = ::ImmGetContext(hWnd);
+ COMPOSITIONFORM CompForm;
+ CompForm.dwStyle = CFS_CANDIDATEPOS;
+ CompForm.ptCurrentPos.x = pos.X;
+ CompForm.ptCurrentPos.y = pos.Y + ele->getAbsolutePosition().getHeight();
+ ::ImmSetCompositionWindow(hIMC, &CompForm);
+ ::ImmReleaseContext(hWnd, hIMC);
+ }
+ break;
+ case WM_IME_CHAR:
+ event.EventType = irr::EET_KEY_INPUT_EVENT;
+ event.KeyInput.PressedDown = true;
+ dev = getDeviceFromHWnd(hWnd);
+ event.KeyInput.Char = wParam;
+ event.KeyInput.Key = irr::KEY_ACCEPT;
+ event.KeyInput.Shift = 0;
+ event.KeyInput.Control = 0;
+ if (dev)
+ dev->postEventFromUser(event);
+ return 0;
}
- return DefWindowProc(hWnd, message, wParam, lParam);
+ return DefWindowProcW(hWnd, message, wParam, lParam);
}


@@ -931,15 +961,15 @@
memset(&DesktopMode, 0, sizeof(DesktopMode));
DesktopMode.dmSize = sizeof(DesktopMode);

- EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &DesktopMode);
+ EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &DesktopMode);

// create the window if we need to and we do not use the null device
if (!CreationParams.WindowId && CreationParams.DriverType != video::EDT_NULL)
{
- const fschar_t* ClassName = __TEXT("CIrrDeviceWin32");
+ const wchar_t* ClassName = L"CIrrDeviceWin32";

// Register Class
- WNDCLASSEX wcex;
+ WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
@@ -954,9 +984,9 @@
wcex.hIconSm = 0;

// if there is an icon, load it
- wcex.hIcon = (HICON)LoadImage(hInstance, __TEXT("irrlicht.ico"), IMAGE_ICON, 0,0, LR_LOADFROMFILE);
+ wcex.hIcon = (HICON)LoadImageW(hInstance, L"irrlicht.ico", IMAGE_ICON, 0,0, LR_LOADFROMFILE);

- RegisterClassEx(&wcex);
+ RegisterClassExW(&wcex);

// calculate client size

@@ -992,7 +1022,7 @@

// create window

- HWnd = CreateWindow( ClassName, __TEXT(""), style, windowLeft, windowTop,
+ HWnd = CreateWindowW( ClassName, L"", style, windowLeft, windowTop,
realWidth, realHeight, NULL, NULL, hInstance, NULL);
CreationParams.WindowId = HWnd;
// CreationParams.WindowSize.Width = realWidth;
@@ -1297,15 +1327,15 @@
void CIrrDeviceWin32::closeDevice()
{
MSG msg;
- PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE);
+ PeekMessageW(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE);
PostQuitMessage(0);
- PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE);
+ PeekMessageW(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE);
if (!ExternalWindow)
{
DestroyWindow(HWnd);
- const fschar_t* ClassName = __TEXT("CIrrDeviceWin32");
+ const wchar_t* ClassName = L"CIrrDeviceWin32";
HINSTANCE hInstance = GetModuleHandle(0);
- UnregisterClass(ClassName, hInstance);
+ UnregisterClassW(ClassName, hInstance);
}
Close=true;
}
@@ -1351,7 +1381,7 @@
{
if (ChangedToFullScreen)
{
- return (ChangeDisplaySettings(&DesktopMode,0)==DISP_CHANGE_SUCCESSFUL);
+ return (ChangeDisplaySettingsW(&DesktopMode,0)==DISP_CHANGE_SUCCESSFUL);
}
else
return true;
@@ -1478,18 +1508,18 @@

void CIrrDeviceWin32::getWindowsVersion(core::stringc& out)
{
- OSVERSIONINFOEX osvi;
+ OSVERSIONINFOEXW osvi;
PGPI pGPI;
BOOL bOsVersionInfoEx;

- ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
- osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);

- bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO*) &osvi);
+ bOsVersionInfoEx = GetVersionExW((OSVERSIONINFOW*) &osvi);
if (!bOsVersionInfoEx)
{
- osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
- if (! GetVersionEx((OSVERSIONINFO *) &osvi))
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
+ if (! GetVersionExW((OSVERSIONINFOW *) &osvi))
return;
}

@@ -1528,7 +1558,7 @@
if (osvi.dwMajorVersion == 6)
{
DWORD dwType;
- pGPI = (PGPI)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo");
+ pGPI = (PGPI)GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "GetProductInfo");
pGPI(osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType);

switch (dwType)
@@ -1594,21 +1624,21 @@
else
{
HKEY hKey;
- char szProductType[80];
+ wchar_t szProductType[80];
DWORD dwBufLen;

- RegOpenKeyEx( HKEY_LOCAL_MACHINE,
- __TEXT("SYSTEM\\CurrentControlSet\\Control\\ProductOptions"),
+ RegOpenKeyExW( HKEY_LOCAL_MACHINE,
+ L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
0, KEY_QUERY_VALUE, &hKey );
- RegQueryValueEx( hKey, __TEXT("ProductType"), NULL, NULL,
+ RegQueryValueExW( hKey, L"ProductType", NULL, NULL,
(LPBYTE) szProductType, &dwBufLen);
RegCloseKey( hKey );

- if (_strcmpi( "WINNT", szProductType) == 0 )
+ if (lstrcmpiW( L"WINNT", szProductType) == 0 )
out.append("Professional ");
- if (_strcmpi( "LANMANNT", szProductType) == 0)
+ if (lstrcmpiW( L"LANMANNT", szProductType) == 0)
out.append("Server ");
- if (_strcmpi( "SERVERNT", szProductType) == 0)
+ if (lstrcmpiW( L"SERVERNT", szProductType) == 0)
out.append("Advanced Server ");
}

@@ -1679,7 +1709,7 @@
else
style = WS_THICKFRAME | WS_SYSMENU | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;

- if (!SetWindowLongPtr(HWnd, GWL_STYLE, style))
+ if (!SetWindowLongPtrW(HWnd, GWL_STYLE, style))
os::Printer::log("Could not change window style.");

RECT clientSize;
@@ -1791,15 +1821,15 @@
{
MSG msg;

- while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
{
// No message translation because we don't use WM_CHAR and it would conflict with our
// deadkey handling.
-
+ TranslateMessage(&msg);
if (ExternalWindow && msg.hwnd == HWnd)
WndProc(HWnd, msg.message, msg.wParam, msg.lParam);
else
- DispatchMessage(&msg);
+ DispatchMessageW(&msg);

if (msg.message == WM_QUIT)
Close = true;
@@ -1811,9 +1841,9 @@
void CIrrDeviceWin32::clearSystemMessages()
{
MSG msg;
- while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))
+ while (PeekMessageW(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))
{}
- while (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
+ while (PeekMessageW(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
{}
}

@@ -1821,12 +1851,12 @@
void CIrrDeviceWin32::ReportLastWinApiError()
{
// (based on code from ovidiucucu from http://www.codeguru.com/forum/showthread.php?t=318721)
- LPCTSTR pszCaption = __TEXT("Windows SDK Error Report");
+ LPCWSTR pszCaption = L"Windows SDK Error Report";
DWORD dwError = GetLastError();

if(NOERROR == dwError)
{
- MessageBox(NULL, __TEXT("No error"), pszCaption, MB_OK);
+ MessageBoxW(NULL, L"No error", pszCaption, MB_OK);
}
else
{
@@ -1835,21 +1865,21 @@
FORMAT_MESSAGE_FROM_SYSTEM;

LPVOID pTextBuffer = NULL;
- DWORD dwCount = FormatMessage(dwFormatControl,
+ DWORD dwCount = FormatMessageW(dwFormatControl,
NULL,
dwError,
0,
- (LPTSTR) &pTextBuffer,
+ (LPWSTR)&pTextBuffer,
0,
NULL);
if(0 != dwCount)
{
- MessageBox(NULL, (LPCTSTR)pTextBuffer, pszCaption, MB_OK|MB_ICONERROR);
+ MessageBoxW(NULL, (LPCWSTR)pTextBuffer, pszCaption, MB_OK|MB_ICONERROR);
LocalFree(pTextBuffer);
}
else
{
- MessageBox(NULL, __TEXT("Unknown error"), pszCaption, MB_OK|MB_ICONERROR);
+ MessageBoxW(NULL, L"Unknown error", pszCaption, MB_OK|MB_ICONERROR);
}
}
}
diff -ur --strip-trailing-cr irrlicht/src/CIrrDeviceWin32.h irrlicht-fixed/src/CIrrDeviceWin32.h
--- irrlicht/src/CIrrDeviceWin32.h 2012-11-05 07:14:12.000000000 +0000
+++ irrlicht-fixed/src/CIrrDeviceWin32.h 2015-06-14 17:32:58.000000000 +0000
@@ -395,7 +395,7 @@
bool Resized;
bool ExternalWindow;
CCursorControl* Win32CursorControl;
- DEVMODE DesktopMode;
+ DEVMODEW DesktopMode;

SJoystickWin32Control* JoyControl;
};
diff -ur --strip-trailing-cr irrlicht/src/COpenGLDriver.cpp irrlicht-fixed/src/COpenGLDriver.cpp
--- irrlicht/src/COpenGLDriver.cpp 2012-11-03 10:08:08.000000000 +0000
+++ irrlicht-fixed/src/COpenGLDriver.cpp 2015-06-14 17:32:58.000000000 +0000
@@ -85,11 +85,11 @@
bool COpenGLDriver::initDriver(CIrrDeviceWin32* device)
{
// Create a window to test antialiasing support
- const fschar_t* ClassName = __TEXT("GLCIrrDeviceWin32");
+ const wchar_t* ClassName = L"GLCIrrDeviceWin32";
HINSTANCE lhInstance = GetModuleHandle(0);

// Register Class
- WNDCLASSEX wcex;
+ WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)DefWindowProc;
@@ -103,7 +103,7 @@
wcex.lpszClassName = ClassName;
wcex.hIconSm = 0;
wcex.hIcon = 0;
- RegisterClassEx(&wcex);
+ RegisterClassExW(&wcex);

RECT clientSize;
clientSize.top = 0;
@@ -123,13 +123,13 @@
const s32 windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2;
const s32 windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2;

- HWND temporary_wnd=CreateWindow(ClassName, __TEXT(""), style, windowLeft,
+ HWND temporary_wnd=CreateWindowW(ClassName, L"", style, windowLeft,
windowTop, realWidth, realHeight, NULL, NULL, lhInstance, NULL);

if (!temporary_wnd)
{
os::Printer::log("Cannot create a temporary window.", ELL_ERROR);
- UnregisterClass(ClassName, lhInstance);
+ UnregisterClassW(ClassName, lhInstance);
return false;
}

@@ -201,7 +201,7 @@
os::Printer::log("Cannot create a GL device context", "No suitable format for temporary window.", ELL_ERROR);
ReleaseDC(temporary_wnd, HDc);
DestroyWindow(temporary_wnd);
- UnregisterClass(ClassName, lhInstance);
+ UnregisterClassW(ClassName, lhInstance);
return false;
}

@@ -218,7 +218,7 @@
os::Printer::log("Cannot create a temporary GL rendering context.", ELL_ERROR);
ReleaseDC(temporary_wnd, HDc);
DestroyWindow(temporary_wnd);
- UnregisterClass(ClassName, lhInstance);
+ UnregisterClassW(ClassName, lhInstance);
return false;
}

@@ -234,7 +234,7 @@
wglDeleteContext(hrc);
ReleaseDC(temporary_wnd, HDc);
DestroyWindow(temporary_wnd);
- UnregisterClass(ClassName, lhInstance);
+ UnregisterClassW(ClassName, lhInstance);
return false;
}

@@ -340,7 +340,7 @@
wglDeleteContext(hrc);
ReleaseDC(temporary_wnd, HDc);
DestroyWindow(temporary_wnd);
- UnregisterClass(ClassName, lhInstance);
+ UnregisterClassW(ClassName, lhInstance);

// get hdc
HDc=GetDC(Window);
diff -ur --strip-trailing-cr irrlicht/src/COSOperator.cpp irrlicht-fixed/src/COSOperator.cpp
--- irrlicht/src/COSOperator.cpp 2012-11-03 10:07:52.000000000 +0000
+++ irrlicht-fixed/src/COSOperator.cpp 2015-06-14 17:32:58.000000000 +0000
@@ -52,11 +52,8 @@


//! copies text to the clipboard
-void COSOperator::copyToClipboard(const c8* text) const
+void COSOperator::copyToClipboard(const c16* text) const
{
- if (strlen(text)==0)
- return;
-
// Windows version
#if defined(_IRR_XBOX_PLATFORM_)
#elif defined(_IRR_WINDOWS_API_)
@@ -66,6 +63,19 @@
EmptyClipboard();

HGLOBAL clipbuffer;
+
+#if defined(_UNICODE)
+ wchar_t * utext = (wchar_t *)text;
+ wchar_t * buffer;
+
+ clipbuffer = GlobalAlloc(GMEM_DDESHARE, sizeof(wchar_t) * (wcslen(utext)+1));
+ buffer = (wchar_t*)GlobalLock(clipbuffer);
+
+ wcscpy(buffer, utext);
+
+ GlobalUnlock(clipbuffer);
+ SetClipboardData(CF_UNICODETEXT, clipbuffer);
+#else
char * buffer;

clipbuffer = GlobalAlloc(GMEM_DDESHARE, strlen(text)+1);
@@ -75,6 +85,8 @@

GlobalUnlock(clipbuffer);
SetClipboardData(CF_TEXT, clipbuffer);
+#endif
+
CloseClipboard();

// MacOSX version
@@ -93,7 +105,7 @@

//! gets text from the clipboard
//! \return Returns 0 if no string is in there.
-const c8* COSOperator::getTextFromClipboard() const
+const c16* COSOperator::getTextFromClipboard() const
{
#if defined(_IRR_XBOX_PLATFORM_)
return 0;
@@ -101,10 +113,14 @@
if (!OpenClipboard(NULL))
return 0;

- char * buffer = 0;
+ wchar_t * buffer = 0;

+#ifdef _UNICODE
+ HANDLE hData = GetClipboardData( CF_UNICODETEXT );
+#else
HANDLE hData = GetClipboardData( CF_TEXT );
- buffer = (char*)GlobalLock( hData );
+#endif
+ buffer = (wchar_t*)GlobalLock( hData );
GlobalUnlock( hData );
CloseClipboard();
return buffer;
diff -ur --strip-trailing-cr irrlicht/src/COSOperator.h irrlicht-fixed/src/COSOperator.h
--- irrlicht/src/COSOperator.h 2012-11-03 10:08:00.000000000 +0000
+++ irrlicht-fixed/src/COSOperator.h 2015-06-14 17:32:58.000000000 +0000
@@ -27,11 +27,11 @@
virtual const core::stringc& getOperatingSystemVersion() const;

//! copies text to the clipboard
- virtual void copyToClipboard(const c8* text) const;
+ virtual void copyToClipboard(const c16* text) const;

//! gets text from the clipboard
//! \return Returns 0 if no string is in there.
- virtual const c8* getTextFromClipboard() const;
+ virtual const c16* getTextFromClipboard() const;

//! gets the processor speed in megahertz
//! \param Mhz:
diff -ur --strip-trailing-cr irrlicht/src/CGUIEditBox.cpp irrlicht-fixed/src/CGUIEditBox.cpp
--- irrlicht/src/CGUIEditBox.cpp 2016-09-15 18:12:49.532839000 +0000
+++ irrlicht-fixed/src/CGUIEditBox.cpp 2016-09-15 18:13:37.101559800 +0000
@@ -333,13 +333,7 @@
const c16* p = Operator->getTextFromClipboard();
if (p)
{
- // TODO: we should have such a function in core::string
- size_t lenOld = strlen(p);
- wchar_t *ws = new wchar_t[lenOld + 1];
- size_t len = mbstowcs(ws,p,lenOld);
- ws[len] = 0;
- irr::core::stringw widep(ws);
- delete[] ws;
+ irr::core::stringw widep(p);

if (MarkBegin == MarkEnd)
{

patch -p1 < irrlicht.patch

根据 irrlicht 的 vcxproj,得到如下 premake4.lua,放到 irrlicht 文件夹里。

project "Irrlicht"
kind "StaticLib"

includedirs { "include", "src", "src/jpeglib", "src/libpng", "src/zlib" }

defines { "_IRR_STATIC_LIB_" }
flags { "NoExceptions", "NoRTTI" }

files { "src/CCgMaterialRenderer.cpp",
"src/CD3D9CgMaterialRenderer.cpp",
"src/CDefaultSceneNodeAnimatorFactory.cpp",
"src/CDefaultSceneNodeFactory.cpp",
"src/CGeometryCreator.cpp",
"src/CMeshCache.cpp",
"src/CMeshManipulator.cpp",
"src/COpenGLCgMaterialRenderer.cpp",
"src/CSceneManager.cpp",
"src/C3DSMeshFileLoader.cpp",
"src/CSMFMeshFileLoader.cpp",
"src/CAnimatedMeshHalfLife.cpp",
"src/CAnimatedMeshMD2.cpp",
"src/CAnimatedMeshMD3.cpp",
"src/CB3DMeshFileLoader.cpp",
"src/CBSPMeshFileLoader.cpp",
"src/CColladaFileLoader.cpp",
"src/CCSMLoader.cpp",
"src/CDMFLoader.cpp",
"src/CIrrMeshFileLoader.cpp",
"src/CLMTSMeshFileLoader.cpp",
"src/CLWOMeshFileLoader.cpp",
"src/CMD2MeshFileLoader.cpp",
"src/CMD3MeshFileLoader.cpp",
"src/CMS3DMeshFileLoader.cpp",
"src/CMY3DMeshFileLoader.cpp",
"src/COBJMeshFileLoader.cpp",
"src/COCTLoader.cpp",
"src/COgreMeshFileLoader.cpp",
"src/CPLYMeshFileLoader.cpp",
"src/CQ3LevelMesh.cpp",
"src/CSkinnedMesh.cpp",
"src/CSTLMeshFileLoader.cpp",
"src/CXMeshFileLoader.cpp",
"src/CAnimatedMeshSceneNode.cpp",
"src/CBillboardSceneNode.cpp",
"src/CBoneSceneNode.cpp",
"src/CCameraSceneNode.cpp",
"src/CCubeSceneNode.cpp",
"src/CDummyTransformationSceneNode.cpp",
"src/CEmptySceneNode.cpp",
"src/CLightSceneNode.cpp",
"src/CMeshSceneNode.cpp",
"src/COctreeSceneNode.cpp",
"src/CQuake3ShaderSceneNode.cpp",
"src/CShadowVolumeSceneNode.cpp",
"src/CSkyBoxSceneNode.cpp",
"src/CSkyDomeSceneNode.cpp",
"src/CSphereSceneNode.cpp",
"src/CTerrainSceneNode.cpp",
"src/CTextSceneNode.cpp",
"src/CVolumeLightSceneNode.cpp",
"src/CWaterSurfaceSceneNode.cpp",
"src/CParticleAnimatedMeshSceneNodeEmitter.cpp",
"src/CParticleAttractionAffector.cpp",
"src/CParticleBoxEmitter.cpp",
"src/CParticleCylinderEmitter.cpp",
"src/CParticleFadeOutAffector.cpp",
"src/CParticleGravityAffector.cpp",
"src/CParticleMeshEmitter.cpp",
"src/CParticlePointEmitter.cpp",
"src/CParticleRingEmitter.cpp",
"src/CParticleRotationAffector.cpp",
"src/CParticleScaleAffector.cpp",
"src/CParticleSphereEmitter.cpp",
"src/CParticleSystemSceneNode.cpp",
"src/CMetaTriangleSelector.cpp",
"src/COctreeTriangleSelector.cpp",
"src/CSceneCollisionManager.cpp",
"src/CTerrainTriangleSelector.cpp",
"src/CTriangleBBSelector.cpp",
"src/CTriangleSelector.cpp",
"src/CSceneLoaderIrr.cpp",
"src/CSceneNodeAnimatorCameraFPS.cpp",
"src/CSceneNodeAnimatorCameraMaya.cpp",
"src/CSceneNodeAnimatorCollisionResponse.cpp",
"src/CSceneNodeAnimatorDelete.cpp",
"src/CSceneNodeAnimatorFlyCircle.cpp",
"src/CSceneNodeAnimatorFlyStraight.cpp",
"src/CSceneNodeAnimatorFollowSpline.cpp",
"src/CSceneNodeAnimatorRotation.cpp",
"src/CSceneNodeAnimatorTexture.cpp",
"src/CColladaMeshWriter.cpp",
"src/CIrrMeshWriter.cpp",
"src/COBJMeshWriter.cpp",
"src/CPLYMeshWriter.cpp",
"src/CSTLMeshWriter.cpp",
"src/CVideoModeList.cpp",
"src/CSoftwareDriver.cpp",
"src/CSoftwareTexture.cpp",
"src/CTRFlat.cpp",
"src/CTRFlatWire.cpp",
"src/CTRGouraud.cpp",
"src/CTRGouraudWire.cpp",
"src/CTRTextureFlat.cpp",
"src/CTRTextureFlatWire.cpp",
"src/CTRTextureGouraud.cpp",
"src/CTRTextureGouraudAdd.cpp",
"src/CTRTextureGouraudNoZ.cpp",
"src/CTRTextureGouraudWire.cpp",
"src/CZBuffer.cpp",
"src/COpenGLDriver.cpp",
"src/COpenGLExtensionHandler.cpp",
"src/COpenGLNormalMapRenderer.cpp",
"src/COpenGLParallaxMapRenderer.cpp",
"src/COpenGLShaderMaterialRenderer.cpp",
"src/COpenGLSLMaterialRenderer.cpp",
"src/COpenGLTexture.cpp",
"src/CD3D8Driver.cpp",
"src/CD3D8NormalMapRenderer.cpp",
"src/CD3D8ParallaxMapRenderer.cpp",
"src/CD3D8ShaderMaterialRenderer.cpp",
"src/CD3D8Texture.cpp",
"src/CColorConverter.cpp",
"src/CFPSCounter.cpp",
"src/CImage.cpp",
"src/CNullDriver.cpp",
"src/CImageWriterBMP.cpp",
"src/CImageWriterJPG.cpp",
"src/CImageWriterPCX.cpp",
"src/CImageWriterPNG.cpp",
"src/CImageWriterPPM.cpp",
"src/CImageWriterPSD.cpp",
"src/CImageWriterTGA.cpp",
"src/CImageLoaderBMP.cpp",
"src/CImageLoaderDDS.cpp",
"src/CImageLoaderJPG.cpp",
"src/CImageLoaderPCX.cpp",
"src/CImageLoaderPNG.cpp",
"src/CImageLoaderPPM.cpp",
"src/CImageLoaderPSD.cpp",
"src/CImageLoaderRGB.cpp",
"src/CImageLoaderTGA.cpp",
"src/CImageLoaderWAL.cpp",
"src/CD3D9Driver.cpp",
"src/CD3D9HLSLMaterialRenderer.cpp",
"src/CD3D9NormalMapRenderer.cpp",
"src/CD3D9ParallaxMapRenderer.cpp",
"src/CD3D9ShaderMaterialRenderer.cpp",
"src/CD3D9Texture.cpp",
"src/CBurningShader_Raster_Reference.cpp",
"src/CDepthBuffer.cpp",
"src/CSoftwareDriver2.cpp",
"src/CSoftwareTexture2.cpp",
"src/CTRGouraud2.cpp",
"src/CTRGouraudAlpha2.cpp",
"src/CTRGouraudAlphaNoZ2.cpp",
"src/CTRNormalMap.cpp",
"src/CTRStencilShadow.cpp",
"src/CTRTextureBlend.cpp",
"src/CTRTextureDetailMap2.cpp",
"src/CTRTextureGouraud2.cpp",
"src/CTRTextureGouraudAdd2.cpp",
"src/CTRTextureGouraudAddNoZ2.cpp",
"src/CTRTextureGouraudAlpha.cpp",
"src/CTRTextureGouraudAlphaNoZ.cpp",
"src/CTRTextureGouraudNoZ2.cpp",
"src/CTRTextureGouraudVertexAlpha2.cpp",
"src/CTRTextureLightMap2_Add.cpp",
"src/CTRTextureLightMap2_M1.cpp",
"src/CTRTextureLightMap2_M2.cpp",
"src/CTRTextureLightMap2_M4.cpp",
"src/CTRTextureLightMapGouraud2_M4.cpp",
"src/CTRTextureWire2.cpp",
"src/IBurningShader.cpp",
"src/CLogger.cpp",
"src/COSOperator.cpp",
"src/Irrlicht.cpp",
"src/os.cpp",
"src/lzma/LzmaDec.c",
"src/zlib/adler32.c",
"src/zlib/compress.c",
"src/zlib/crc32.c",
"src/zlib/deflate.c",
"src/zlib/inffast.c",
"src/zlib/inflate.c",
"src/zlib/inftrees.c",
"src/zlib/trees.c",
"src/zlib/uncompr.c",
"src/zlib/zutil.c",
"src/jpeglib/jaricom.c",
"src/jpeglib/jcapimin.c",
"src/jpeglib/jcapistd.c",
"src/jpeglib/jcarith.c",
"src/jpeglib/jccoefct.c",
"src/jpeglib/jccolor.c",
"src/jpeglib/jcdctmgr.c",
"src/jpeglib/jchuff.c",
"src/jpeglib/jcinit.c",
"src/jpeglib/jcmainct.c",
"src/jpeglib/jcmarker.c",
"src/jpeglib/jcmaster.c",
"src/jpeglib/jcomapi.c",
"src/jpeglib/jcparam.c",
"src/jpeglib/jcprepct.c",
"src/jpeglib/jcsample.c",
"src/jpeglib/jctrans.c",
"src/jpeglib/jdapimin.c",
"src/jpeglib/jdapistd.c",
"src/jpeglib/jdarith.c",
"src/jpeglib/jdatadst.c",
"src/jpeglib/jdatasrc.c",
"src/jpeglib/jdcoefct.c",
"src/jpeglib/jdcolor.c",
"src/jpeglib/jddctmgr.c",
"src/jpeglib/jdhuff.c",
"src/jpeglib/jdinput.c",
"src/jpeglib/jdmainct.c",
"src/jpeglib/jdmarker.c",
"src/jpeglib/jdmaster.c",
"src/jpeglib/jdmerge.c",
"src/jpeglib/jdpostct.c",
"src/jpeglib/jdsample.c",
"src/jpeglib/jdtrans.c",
"src/jpeglib/jerror.c",
"src/jpeglib/jfdctflt.c",
"src/jpeglib/jfdctfst.c",
"src/jpeglib/jfdctint.c",
"src/jpeglib/jidctflt.c",
"src/jpeglib/jidctfst.c",
"src/jpeglib/jidctint.c",
"src/jpeglib/jmemmgr.c",
"src/jpeglib/jmemnobs.c",
"src/jpeglib/jquant1.c",
"src/jpeglib/jquant2.c",
"src/jpeglib/jutils.c",
"src/libpng/png.c",
"src/libpng/pngerror.c",
"src/libpng/pngget.c",
"src/libpng/pngmem.c",
"src/libpng/pngpread.c",
"src/libpng/pngread.c",
"src/libpng/pngrio.c",
"src/libpng/pngrtran.c",
"src/libpng/pngrutil.c",
"src/libpng/pngset.c",
"src/libpng/pngtrans.c",
"src/libpng/pngwio.c",
"src/libpng/pngwrite.c",
"src/libpng/pngwtran.c",
"src/libpng/pngwutil.c",
"src/aesGladman/aescrypt.cpp",
"src/aesGladman/aeskey.cpp",
"src/aesGladman/aestab.cpp",
"src/aesGladman/fileenc.cpp",
"src/aesGladman/hmac.cpp",
"src/aesGladman/prng.cpp",
"src/aesGladman/pwd2key.cpp",
"src/aesGladman/sha1.cpp",
"src/aesGladman/sha2.cpp",
"src/bzip2/blocksort.c",
"src/bzip2/bzcompress.c",
"src/bzip2/bzlib.c",
"src/bzip2/crctable.c",
"src/bzip2/decompress.c",
"src/bzip2/huffman.c",
"src/bzip2/randtable.c",
"src/CIrrDeviceConsole.cpp",
"src/CIrrDeviceFB.cpp",
"src/CIrrDeviceLinux.cpp",
"src/CIrrDeviceSDL.cpp",
"src/CIrrDeviceStub.cpp",
"src/CIrrDeviceWin32.cpp",
"src/CIrrDeviceWinCE.cpp",
"src/CAttributes.cpp",
"src/CFileList.cpp",
"src/CFileSystem.cpp",
"src/CLimitReadFile.cpp",
"src/CMemoryFile.cpp",
"src/CMountPointReader.cpp",
"src/CNPKReader.cpp",
"src/CPakReader.cpp",
"src/CReadFile.cpp",
"src/CTarReader.cpp",
"src/CWADReader.cpp",
"src/CWriteFile.cpp",
"src/CXMLReader.cpp",
"src/CXMLWriter.cpp",
"src/CZipReader.cpp",
"src/irrXML.cpp",
"src/CDefaultGUIElementFactory.cpp",
"src/CGUIButton.cpp",
"src/CGUICheckbox.cpp",
"src/CGUIColorSelectDialog.cpp",
"src/CGUIComboBox.cpp",
"src/CGUIContextMenu.cpp",
"src/CGUIEditBox.cpp",
"src/CGUIEnvironment.cpp",
"src/CGUIFileOpenDialog.cpp",
"src/CGUIFont.cpp",
"src/CGUIImage.cpp",
"src/CGUIImageList.cpp",
"src/CGUIInOutFader.cpp",
"src/CGUIListBox.cpp",
"src/CGUIMenu.cpp",
"src/CGUIMeshViewer.cpp",
"src/CGUIMessageBox.cpp",
"src/CGUIModalScreen.cpp",
"src/CGUIScrollBar.cpp",
"src/CGUISkin.cpp",
"src/CGUISpinBox.cpp",
"src/CGUISpriteBank.cpp",
"src/CGUIStaticText.cpp",
"src/CGUITabControl.cpp",
"src/CGUITable.cpp",
"src/CGUIToolBar.cpp",
"src/CGUITreeView.cpp",
"src/CGUIWindow.cpp" }

configuration { "vs*" }
defines { "IRRLICHT_FAST_MATH", "UNICODE", "_UNICODE" }
includedirs { "$(DXSDK_DIR)include" }
libdirs { "$(DXSDK_DIR)Lib/x86" }

configuration { "windows" }
links { "imm32" }

5. 配置 Lua

解压 lua-5.2.4.tar.gz 到项目目录,将文件夹重命名为 lua
为兼容 YGOPRO 源码,将 src 文件夹里除 Makefile 外的文件全部移动到 lua 文件夹。
Lua 源码的文件结构非常简单,直接把如下 premake4.lua 放到 lua 文件夹里即可。

project "lua"
kind "StaticLib"

files { "*.c", "*.h" }

6. 配置 SQLite

解压 sqlite-amalgamation-3140200.zip 到项目目录,将文件夹重命名为 sqlite3
SQLite 源码的文件结构非常简单,直接把如下 premake4.lua 放到 sqlite3 文件夹里即可。

project "sqlite3"
kind "StaticLib"

files { "sqlite3.c", "sqlite3.h" }

7. 生成解决方案

在生成解决方案之前,我们还需要对 premake 做几个设置。
打开项目目录中的 premake4.lua,做如下修改。

configuration "windows"
defines { "WIN32", "_WIN32", "WINVER=0x0501" }

然后添加如下内容。

configuration { "Release", "vs*" }
flags { "StaticRuntime", "LinkTimeOptimization" }

startproject "ygopro"

解压 premake-5.0.0-alpha9-windows.zip 到项目目录。
运行 premake5 vs2015 即可在 build 文件夹内生成sln文件。
程序还需要一个资源文件,把如下 ygopro.rcygopro.ico 放到 gframe 文件夹里。

1 ICON "ygopro.ico"

1 VERSIONINFO
FILEVERSION 1, 0, 33, 11
PRODUCTVERSION 1, 0, 33, 11
FILEOS 0x4
FILETYPE 0x1

BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "080404b0"
BEGIN
VALUE "FileDescription", "YGOPRO"
VALUE "InternalName", "YGOPRO"
VALUE "LegalCopyright", "Copyright (C) 2016 Fluorohydride"
VALUE "OriginalFilename", "ygopro.exe"
VALUE "ProductName", "YGOPRO"
VALUE "FileVersion", "1.033.B.233"
VALUE "ProductVersion", "1.033.B.233"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x804, 1200
END
END

8. 选项设置

完成了以上步骤后,我们的 ygo 项目已经可以用 VS2015 打开并编译了。
如需要程序支持XP,在 ygopro 的属性中将 平台工具集 设置为 Visual Studio 2015 - Windows XP (v140_xp)
别忘记把 解决方案配置 改成 Release