|
|
|
|
/*==LICENSE==*
|
|
|
|
|
|
|
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools
|
|
|
|
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
|
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
|
work.
|
|
|
|
|
|
|
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
|
or by snail mail at:
|
|
|
|
|
Cyan Worlds, Inc.
|
|
|
|
|
14617 N Newport Hwy
|
|
|
|
|
Mead, WA 99021
|
|
|
|
|
|
|
|
|
|
*==LICENSE==*/
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <direct.h> // windows directory handling fxns (for chdir)
|
|
|
|
|
#include <process.h>
|
|
|
|
|
|
|
|
|
|
//#define DETACH_EXE // Microsoft trick to force loading of exe to memory
|
|
|
|
|
#ifdef DETACH_EXE
|
|
|
|
|
#include "dmdfm.h" // Windows Load EXE into memory suff
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "HeadSpin.h"
|
|
|
|
|
#include "hsStream.h"
|
|
|
|
|
#include "hsUtils.h"
|
|
|
|
|
#include "plClient.h"
|
|
|
|
|
#include "../plNetClient/plNetClientMgr.h"
|
|
|
|
|
#include "../plNetClient/plNetLinkingMgr.h"
|
|
|
|
|
#include "../plInputCore/plInputManager.h"
|
|
|
|
|
#include "../plUnifiedTime/plUnifiedTime.h"
|
|
|
|
|
#include "plPipeline.h"
|
|
|
|
|
#include "../plResMgr/plResManager.h"
|
|
|
|
|
#include "../plResMgr/plLocalization.h"
|
|
|
|
|
#include "../plFile/plEncryptedStream.h"
|
|
|
|
|
|
|
|
|
|
#include "../plStatusLog/plStatusLog.h"
|
|
|
|
|
#include "../pnProduct/pnProduct.h"
|
|
|
|
|
#include "../plNetGameLib/plNetGameLib.h"
|
|
|
|
|
#include "../plFile/plFileUtils.h"
|
|
|
|
|
|
|
|
|
|
#include "../plPhysX/plSimulationMgr.h"
|
|
|
|
|
|
|
|
|
|
#include "res\resource.h"
|
|
|
|
|
|
|
|
|
|
#include <shellapi.h>
|
|
|
|
|
#include "WinHttp.h"
|
|
|
|
|
//
|
|
|
|
|
// Defines
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#define CLASSNAME "Plasma" // Used in WinInit()
|
|
|
|
|
#define PARABLE_NORMAL_EXIT 0 // i.e. exited WinMain normally
|
|
|
|
|
|
|
|
|
|
#define TIMER_UNITS_PER_SEC (float)1e3
|
|
|
|
|
#define UPDATE_STATUSMSG_SECONDS 30
|
|
|
|
|
#define WM_USER_SETSTATUSMSG WM_USER+1
|
|
|
|
|
|
|
|
|
|
#if BUILD_TYPE == BUILD_TYPE_DEV
|
|
|
|
|
#define STATUS_PATH L"www2.cyanworlds.com"
|
|
|
|
|
#else
|
|
|
|
|
#define STATUS_PATH L"support.cyanworlds.com"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Globals
|
|
|
|
|
//
|
|
|
|
|
hsBool gHasMouse = false;
|
|
|
|
|
|
|
|
|
|
extern hsBool gDataServerLocal;
|
|
|
|
|
extern hsBool gUseBackgroundDownloader;
|
|
|
|
|
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
kArgToDni,
|
|
|
|
|
kArgSkipLoginDialog,
|
|
|
|
|
kArgAuthSrv,
|
|
|
|
|
kArgFileSrv,
|
|
|
|
|
kArgGateKeeperSrv,
|
|
|
|
|
kArgLocalData,
|
|
|
|
|
kArgBackgroundDownloader,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const CmdArgDef s_cmdLineArgs[] = {
|
|
|
|
|
{ kCmdArgFlagged | kCmdTypeBool, L"ToDni", kArgToDni },
|
|
|
|
|
{ kCmdArgFlagged | kCmdTypeBool, L"SkipLoginDialog", kArgSkipLoginDialog },
|
|
|
|
|
{ kCmdArgFlagged | kCmdTypeString, L"AuthSrv", kArgAuthSrv },
|
|
|
|
|
{ kCmdArgFlagged | kCmdTypeString, L"FileSrv", kArgFileSrv },
|
|
|
|
|
{ kCmdArgFlagged | kCmdTypeString, L"GateKeeperSrv", kArgGateKeeperSrv },
|
|
|
|
|
{ kCmdArgFlagged | kCmdTypeBool, L"LocalData", kArgLocalData },
|
|
|
|
|
{ kCmdArgFlagged | kCmdTypeBool, L"BGDownload", kArgBackgroundDownloader },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// Made globals now, so we can set them to zero if we take the border and
|
|
|
|
|
/// caption styles out ala fullscreen (8.11.2000 mcn)
|
|
|
|
|
int gWinBorderDX = GetSystemMetrics( SM_CXSIZEFRAME );
|
|
|
|
|
int gWinBorderDY = GetSystemMetrics( SM_CYSIZEFRAME );
|
|
|
|
|
int gWinMenuDY = GetSystemMetrics( SM_CYCAPTION );
|
|
|
|
|
|
|
|
|
|
//#include "global.h"
|
|
|
|
|
plClient *gClient;
|
|
|
|
|
bool gPendingActivate = false;
|
|
|
|
|
bool gPendingActivateFlag = false;
|
|
|
|
|
|
|
|
|
|
static bool s_loginDlgRunning = false;
|
|
|
|
|
static CEvent s_statusEvent(kEventManualReset);
|
|
|
|
|
|
|
|
|
|
FILE *errFP = nil;
|
|
|
|
|
HINSTANCE gHInst = NULL; // Instance of this app
|
|
|
|
|
|
|
|
|
|
static const unsigned AUTH_LOGIN_TIMER = 1;
|
|
|
|
|
static const unsigned AUTH_FAILED_TIMER = 2;
|
|
|
|
|
|
|
|
|
|
#define FAKE_PASS_STRING "********"
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
// External patcher file
|
|
|
|
|
//============================================================================
|
|
|
|
|
#ifdef PLASMA_EXTERNAL_RELEASE
|
|
|
|
|
|
|
|
|
|
static wchar s_patcherExeName[] = L"UruLauncher.exe";
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
// Internal patcher file
|
|
|
|
|
//============================================================================
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
static wchar s_patcherExeName[] = L"plUruLauncher.exe";
|
|
|
|
|
|
|
|
|
|
#endif // PLASMA_EXTERNAL_RELEASE
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
// PhysX installer
|
|
|
|
|
//============================================================================
|
|
|
|
|
static wchar s_physXSetupExeName[] = L"PhysX_Setup.exe";
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
// TRANSGAMING detection & dialog replacement
|
|
|
|
|
//============================================================================
|
|
|
|
|
typedef BOOL (WINAPI *IsTransgaming) (void);
|
|
|
|
|
typedef const char * (WINAPI *TGGetOS) (void);
|
|
|
|
|
typedef LPVOID (WINAPI *TGLaunchUNIXApp) (const char *pPath, const char *pMode);
|
|
|
|
|
typedef BOOL (WINAPI *TGUNIXAppReadLine) (LPVOID pApp, char *pBuf, int bufSize);
|
|
|
|
|
typedef BOOL (WINAPI *TGUNIXAppWriteLine) (LPVOID pApp, const char *pLine);
|
|
|
|
|
typedef BOOL (WINAPI *TGUNIXAppClose) (LPVOID pApp);
|
|
|
|
|
|
|
|
|
|
static bool TGIsCider = false;
|
|
|
|
|
static TGLaunchUNIXApp pTGLaunchUNIXApp;
|
|
|
|
|
static TGUNIXAppReadLine pTGUNIXAppReadLine;
|
|
|
|
|
static TGUNIXAppWriteLine pTGUNIXAppWriteLine;
|
|
|
|
|
static TGUNIXAppClose pTGUNIXAppClose;
|
|
|
|
|
|
|
|
|
|
#define TG_NEW_LOGIN_PATH "C:\\Program Files\\Uru Live\\Cider\\URU Live Login.app"
|
|
|
|
|
#define TG_NEW_LOGIN_POPEN_PATH "/transgaming/c_drive/Program Files/Uru Live/Cider/URU Live Login.app/Contents/MacOS/URU Live Login"
|
|
|
|
|
#define TG_OLD_LOGIN_POPEN_PATH "/URU Live Login.app/Contents/MacOS/URU Live Login"
|
|
|
|
|
|
|
|
|
|
#define TG_NEW_EULA_PATH "C:\\Program Files\\Uru Live\\Cider\\URU Live EULA.app"
|
|
|
|
|
#define TG_NEW_EULA_POPEN_PATH "/transgaming/c_drive/Program Files/Uru Live/Cider/URU Live EULA.app/Contents/MacOS/URU Live EULA"
|
|
|
|
|
#define TG_OLD_EULA_POPEN_PATH "/URU Live EULA.app/Contents/MacOS/URU Live EULA"
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
// LoginDialogParam
|
|
|
|
|
//============================================================================
|
|
|
|
|
struct LoginDialogParam {
|
|
|
|
|
bool fromGT;
|
|
|
|
|
ENetError authError;
|
|
|
|
|
wchar accountName[kMaxAccountNameLength];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bool AuthenticateNetClientComm(ENetError* result, HWND parentWnd);
|
|
|
|
|
bool IsExpired();
|
|
|
|
|
void GetCryptKey(UInt32* cryptKey, unsigned size);
|
|
|
|
|
static void SaveUserPass (char *username, char *password, ShaDigest *pNamePassHash, bool remember_password,
|
|
|
|
|
bool fromGT);
|
|
|
|
|
static void LoadUserPass (const wchar *accountName, char *username, ShaDigest *pNamePassHash, bool *pRemember,
|
|
|
|
|
bool fromGT, int *pFocus);
|
|
|
|
|
static void AuthFailedStrings (ENetError authError, bool fromGT,
|
|
|
|
|
const char **ppStr1, const char **ppStr2,
|
|
|
|
|
const wchar **ppWStr);
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
// For networking
|
|
|
|
|
const GUID NEXUS_GUID = {
|
|
|
|
|
0x5bfdb060, 0x6a4, 0x11d0, 0x9c, 0x4f, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e};
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Detect whether we're running under TRANSGAMING Cider
|
|
|
|
|
//==============================================================================
|
|
|
|
|
static void TGDoCiderDetection ()
|
|
|
|
|
{
|
|
|
|
|
HMODULE hMod = GetModuleHandle ("ntdll");
|
|
|
|
|
if (!hMod)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
IsTransgaming pIsTg = (IsTransgaming)GetProcAddress (hMod, "IsTransgaming");
|
|
|
|
|
if (!pIsTg || !pIsTg ())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
TGGetOS pTGOS = (TGGetOS)GetProcAddress (hMod, "TGGetOS");
|
|
|
|
|
const char *pOS = NULL;
|
|
|
|
|
if (pTGOS)
|
|
|
|
|
pOS = pTGOS ();
|
|
|
|
|
if (!pOS || strcmp (pOS, "MacOSX"))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
TGIsCider = true;
|
|
|
|
|
pTGLaunchUNIXApp = (TGLaunchUNIXApp)GetProcAddress (hMod, "TGLaunchUNIXApp");
|
|
|
|
|
pTGUNIXAppReadLine = (TGUNIXAppReadLine)GetProcAddress (hMod, "TGUNIXAppReadLine");
|
|
|
|
|
pTGUNIXAppWriteLine = (TGUNIXAppWriteLine)GetProcAddress (hMod, "TGUNIXAppWriteLine");
|
|
|
|
|
pTGUNIXAppClose = (TGUNIXAppClose)GetProcAddress (hMod, "TGUNIXAppClose");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool TGRunLoginDialog (const wchar *accountName, bool fromGT)
|
|
|
|
|
{
|
|
|
|
|
ShaDigest NamePassHash;
|
|
|
|
|
char Username[kMaxAccountNameLength + 5];
|
|
|
|
|
int Focus;
|
|
|
|
|
bool bRemember = false;
|
|
|
|
|
|
|
|
|
|
LoadUserPass (accountName, Username, &NamePassHash, &bRemember, fromGT, &Focus);
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
LPVOID pApp;
|
|
|
|
|
if (GetFileAttributes (TG_NEW_LOGIN_PATH) != INVALID_FILE_ATTRIBUTES)
|
|
|
|
|
pApp = pTGLaunchUNIXApp (TG_NEW_LOGIN_POPEN_PATH, "r+");
|
|
|
|
|
else
|
|
|
|
|
pApp = pTGLaunchUNIXApp (TG_OLD_LOGIN_POPEN_PATH, "r+");
|
|
|
|
|
|
|
|
|
|
if (!pApp)
|
|
|
|
|
{
|
|
|
|
|
hsMessageBox ("Incomplete or corrupted installation!\nUnable to locate Login dialog",
|
|
|
|
|
"Error", hsMessageBoxNormal);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Send user/pwd/remember
|
|
|
|
|
pTGUNIXAppWriteLine (pApp, Username);
|
|
|
|
|
if (bRemember)
|
|
|
|
|
pTGUNIXAppWriteLine (pApp, FAKE_PASS_STRING);
|
|
|
|
|
else
|
|
|
|
|
pTGUNIXAppWriteLine (pApp, "");
|
|
|
|
|
if (bRemember)
|
|
|
|
|
pTGUNIXAppWriteLine (pApp, "y");
|
|
|
|
|
else
|
|
|
|
|
pTGUNIXAppWriteLine (pApp, "n");
|
|
|
|
|
|
|
|
|
|
if (!pTGUNIXAppReadLine (pApp, Username, sizeof (Username)))
|
|
|
|
|
{
|
|
|
|
|
pTGUNIXAppClose (pApp);
|
|
|
|
|
hsMessageBox ("Incomplete or corrupted installation!\nUnable to locate Login dialog",
|
|
|
|
|
"Error", hsMessageBoxNormal);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if user selected 'Cancel'
|
|
|
|
|
if (StrCmp (Username, "text:", 5) != 0)
|
|
|
|
|
{
|
|
|
|
|
pTGUNIXAppClose (pApp);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
memmove (Username, Username + 5, StrLen (Username) - 5);
|
|
|
|
|
Username[StrLen (Username) - 5] = '\0';
|
|
|
|
|
|
|
|
|
|
char Password[kMaxPasswordLength];
|
|
|
|
|
if (!pTGUNIXAppReadLine (pApp, Password, sizeof (Password)))
|
|
|
|
|
{
|
|
|
|
|
pTGUNIXAppClose (pApp);
|
|
|
|
|
hsMessageBox ("Incomplete or corrupted installation!\nLogin dialog not found or working",
|
|
|
|
|
"Error", hsMessageBoxNormal);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char Remember[16];
|
|
|
|
|
if (!pTGUNIXAppReadLine (pApp, Remember, sizeof (Remember)))
|
|
|
|
|
{
|
|
|
|
|
pTGUNIXAppClose (pApp);
|
|
|
|
|
hsMessageBox ("Incomplete or corrupted installation!\nLogin dialog not found or working",
|
|
|
|
|
"Error", hsMessageBoxNormal);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pTGUNIXAppClose (pApp);
|
|
|
|
|
|
|
|
|
|
bRemember = false;
|
|
|
|
|
if (Remember[0] == 'y')
|
|
|
|
|
bRemember = true;
|
|
|
|
|
|
|
|
|
|
SaveUserPass (Username, Password, &NamePassHash, bRemember, fromGT);
|
|
|
|
|
|
|
|
|
|
// Do login & see if it failed
|
|
|
|
|
ENetError auth;
|
|
|
|
|
bool cancelled = AuthenticateNetClientComm(&auth, NULL);
|
|
|
|
|
|
|
|
|
|
if (IS_NET_SUCCESS (auth) && !cancelled)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (!cancelled)
|
|
|
|
|
{
|
|
|
|
|
const char *pStr1, *pStr2;
|
|
|
|
|
const wchar *pWStr;
|
|
|
|
|
unsigned int Len;
|
|
|
|
|
char *pTmpStr;
|
|
|
|
|
|
|
|
|
|
AuthFailedStrings (auth, fromGT, &pStr1, &pStr2, &pWStr);
|
|
|
|
|
|
|
|
|
|
Len = StrLen (pStr1) + 1;
|
|
|
|
|
if (pStr2)
|
|
|
|
|
Len += StrLen (pStr2) + 2;
|
|
|
|
|
if (pWStr)
|
|
|
|
|
Len += StrLen (pWStr) + 2;
|
|
|
|
|
|
|
|
|
|
pTmpStr = TRACKED_NEW char[Len];
|
|
|
|
|
StrCopy (pTmpStr, pStr1, StrLen (pStr1));
|
|
|
|
|
if (pStr2)
|
|
|
|
|
{
|
|
|
|
|
StrCopy (pTmpStr + StrLen (pTmpStr), "\n\n", 2);
|
|
|
|
|
StrCopy (pTmpStr + StrLen (pTmpStr), pStr2, StrLen (pStr2));
|
|
|
|
|
}
|
|
|
|
|
if (pWStr)
|
|
|
|
|
{
|
|
|
|
|
StrCopy (pTmpStr + StrLen (pTmpStr), "\n\n", 2);
|
|
|
|
|
StrToAnsi (pTmpStr + StrLen (pTmpStr), pWStr, StrLen (pWStr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hsMessageBox (pTmpStr, "Error", hsMessageBoxNormal);
|
|
|
|
|
delete [] pTmpStr;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
NetCommDisconnect();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TGRunTOSDialog ()
|
|
|
|
|
{
|
|
|
|
|
char Buf[16];
|
|
|
|
|
LPVOID pApp;
|
|
|
|
|
|
|
|
|
|
if (GetFileAttributes (TG_NEW_EULA_PATH) != INVALID_FILE_ATTRIBUTES)
|
|
|
|
|
pApp = pTGLaunchUNIXApp (TG_NEW_EULA_POPEN_PATH, "r");
|
|
|
|
|
else
|
|
|
|
|
pApp = pTGLaunchUNIXApp (TG_OLD_EULA_POPEN_PATH, "r");
|
|
|
|
|
|
|
|
|
|
if (!pApp)
|
|
|
|
|
{
|
|
|
|
|
hsMessageBox ("Incomplete or corrupted installation!\nTOS dialog not found or working",
|
|
|
|
|
"Error", hsMessageBoxNormal);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!pTGUNIXAppReadLine (pApp, Buf, sizeof (Buf)))
|
|
|
|
|
{
|
|
|
|
|
hsMessageBox ("Incomplete or corrupted installation!\nTOS dialog not found or working",
|
|
|
|
|
"Error", hsMessageBoxNormal);
|
|
|
|
|
pTGUNIXAppClose (pApp);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pTGUNIXAppClose (pApp);
|
|
|
|
|
|
|
|
|
|
return (StrCmp (Buf, "accepted") == 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GetMouseCoords(HWND hWnd, WPARAM wParam, LPARAM lParam, int* xPos, int* yPos, int* fwKeys)
|
|
|
|
|
{
|
|
|
|
|
POINT pt;
|
|
|
|
|
pt.x=LOWORD(lParam);
|
|
|
|
|
pt.y=HIWORD(lParam);
|
|
|
|
|
#if 0
|
|
|
|
|
if (ClientToScreen(hWnd, &pt) == false)
|
|
|
|
|
HSDebugProc("Error converting client mouse coords to screen");
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (xPos)
|
|
|
|
|
*xPos = pt.x; // horizontal position of cursor
|
|
|
|
|
if (yPos)
|
|
|
|
|
*yPos = pt.y; // vertical position of cursor
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
char str[128];
|
|
|
|
|
sprintf(str, "mx=%d my=%d\n", pt.x, pt.y);
|
|
|
|
|
hsStatusMessage(str);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (fwKeys)
|
|
|
|
|
*fwKeys = wParam; // key flags
|
|
|
|
|
|
|
|
|
|
// key flag bits
|
|
|
|
|
// MK_CONTROL Set if the CTRL key is down.
|
|
|
|
|
// MK_LBUTTON Set if the left mouse button is down.
|
|
|
|
|
// MK_MBUTTON Set if the middle mouse button is down.
|
|
|
|
|
// MK_RBUTTON Set if the right mouse button is down.
|
|
|
|
|
// MK_SHIFT Set if the SHIFT key is down.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DebugMsgF(const char* format, ...);
|
|
|
|
|
|
|
|
|
|
// Handles all the windows messages we might receive
|
|
|
|
|
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
|
|
|
{
|
|
|
|
|
static bool gDragging = false;
|
|
|
|
|
static UInt32 keyState=0;
|
|
|
|
|
|
|
|
|
|
// Handle messages
|
|
|
|
|
switch (message) {
|
|
|
|
|
case WM_KEYDOWN :
|
|
|
|
|
case WM_LBUTTONDOWN :
|
|
|
|
|
case WM_RBUTTONDOWN :
|
|
|
|
|
case WM_LBUTTONDBLCLK : // The left mouse button was double-clicked.
|
|
|
|
|
case WM_MBUTTONDBLCLK : // The middle mouse button was double-clicked.
|
|
|
|
|
case WM_MBUTTONDOWN : // The middle mouse button was pressed.
|
|
|
|
|
case WM_RBUTTONDBLCLK : // The right mouse button was double-clicked.
|
|
|
|
|
// If they did anything but move the mouse, quit any intro movie playing.
|
|
|
|
|
{
|
|
|
|
|
if( gClient )
|
|
|
|
|
gClient->SetQuitIntro(true);
|
|
|
|
|
}
|
|
|
|
|
// Fall through to other events
|
|
|
|
|
case WM_KEYUP :
|
|
|
|
|
case WM_LBUTTONUP :
|
|
|
|
|
case WM_RBUTTONUP :
|
|
|
|
|
case WM_MBUTTONUP : // The middle mouse button was released.
|
|
|
|
|
case WM_MOUSEMOVE :
|
|
|
|
|
case 0x020A: // fuc&ing windows b.s...
|
|
|
|
|
{
|
|
|
|
|
if (gClient && gClient->WindowActive() && gClient->GetInputManager())
|
|
|
|
|
{
|
|
|
|
|
gClient->GetInputManager()->HandleWin32ControlEvent(message, wParam, lParam, hWnd);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
case WM_KILLFOCUS:
|
|
|
|
|
SetForegroundWindow(hWnd);
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
case WM_SYSKEYUP:
|
|
|
|
|
case WM_SYSKEYDOWN:
|
|
|
|
|
{
|
|
|
|
|
if (gClient && gClient->WindowActive() && gClient->GetInputManager())
|
|
|
|
|
{
|
|
|
|
|
gClient->GetInputManager()->HandleWin32ControlEvent(message, wParam, lParam, hWnd);
|
|
|
|
|
}
|
|
|
|
|
//DefWindowProc(hWnd, message, wParam, lParam);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WM_SYSCOMMAND:
|
|
|
|
|
switch (wParam) {
|
|
|
|
|
// Trap ALT so it doesn't pause the app
|
|
|
|
|
case SC_KEYMENU :
|
|
|
|
|
//// disable screensavers and monitor power downs too.
|
|
|
|
|
case SC_SCREENSAVE:
|
|
|
|
|
case SC_MONITORPOWER:
|
|
|
|
|
return 0;
|
|
|
|
|
case SC_CLOSE :
|
|
|
|
|
// kill game if window is closed
|
|
|
|
|
gClient->SetDone(TRUE);
|
|
|
|
|
if (plNetClientMgr * mgr = plNetClientMgr::GetInstance())
|
|
|
|
|
mgr->QueueDisableNet(false, nil);
|
|
|
|
|
DestroyWindow(gClient->GetWindowHandle());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WM_ACTIVATE:
|
|
|
|
|
{
|
|
|
|
|
bool active = (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE);
|
|
|
|
|
bool minimized = (HIWORD(wParam) != 0);
|
|
|
|
|
|
|
|
|
|
DebugMsgF("Got WM_ACTIVATE, active=%s, minimized=%s, clicked=%s",
|
|
|
|
|
active ? "true" : "false",
|
|
|
|
|
minimized ? "true" : "false",
|
|
|
|
|
(LOWORD(wParam) == WA_CLICKACTIVE) ? "true" : "false");
|
|
|
|
|
|
|
|
|
|
if (gClient && !minimized && !gClient->GetDone())
|
|
|
|
|
{
|
|
|
|
|
if (LOWORD(wParam) == WA_CLICKACTIVE)
|
|
|
|
|
{
|
|
|
|
|
// See if they've clicked on the frame, in which case they just want to
|
|
|
|
|
// move, not activate, us.
|
|
|
|
|
POINT pt;
|
|
|
|
|
GetCursorPos(&pt);
|
|
|
|
|
ScreenToClient(hWnd, &pt);
|
|
|
|
|
|
|
|
|
|
RECT rect;
|
|
|
|
|
GetClientRect(hWnd, &rect);
|
|
|
|
|
|
|
|
|
|
if( (pt.x < rect.left)
|
|
|
|
|
||(pt.x >= rect.right)
|
|
|
|
|
||(pt.y < rect.top)
|
|
|
|
|
||(pt.y >= rect.bottom) )
|
|
|
|
|
{
|
|
|
|
|
active = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
gClient->WindowActivate(active);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
gPendingActivate = true;
|
|
|
|
|
gPendingActivateFlag = active;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// Let go of the mouse if the window is being moved.
|
|
|
|
|
case WM_ENTERSIZEMOVE:
|
|
|
|
|
DebugMsgF("Got WM_ENTERSIZEMOVE%s", gClient ? "" : ", no client, ignoring");
|
|
|
|
|
gDragging = true;
|
|
|
|
|
if( gClient )
|
|
|
|
|
gClient->WindowActivate(false);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// Redo the mouse capture if the window gets moved
|
|
|
|
|
case WM_EXITSIZEMOVE:
|
|
|
|
|
DebugMsgF("Got WM_EXITSIZEMOVE%s", gClient ? "" : ", no client, ignoring");
|
|
|
|
|
gDragging = false;
|
|
|
|
|
if( gClient )
|
|
|
|
|
gClient->WindowActivate(true);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// Redo the mouse capture if the window gets moved (special case for Colin
|
|
|
|
|
// and his cool program that bumps windows out from under the taskbar)
|
|
|
|
|
case WM_MOVE:
|
|
|
|
|
if (!gDragging && gClient && gClient->GetInputManager())
|
|
|
|
|
{
|
|
|
|
|
gClient->GetInputManager()->Activate(true);
|
|
|
|
|
DebugMsgF("Got WM_MOVE");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
DebugMsgF("Got WM_MOVE, but ignoring");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/// Resize the window
|
|
|
|
|
// (we do WM_SIZING here instead of WM_SIZE because, for some reason, WM_SIZE is
|
|
|
|
|
// sent to the window when we do fullscreen, and what's more, it gets sent BEFORE
|
|
|
|
|
// the fullscreen flag is sent. How does *that* happen? Anyway, WM_SIZING acts
|
|
|
|
|
// just like WM_SIZE, except that it ONLY gets sent when the user changes the window
|
|
|
|
|
// size, not when the window is minimized or restored)
|
|
|
|
|
case WM_SIZING:
|
|
|
|
|
{
|
|
|
|
|
DebugMsgF("Got WM_SIZING");
|
|
|
|
|
RECT r;
|
|
|
|
|
::GetClientRect(hWnd, &r);
|
|
|
|
|
gClient->GetPipeline()->Resize(r.right - r.left, r.bottom - r.top);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WM_SIZE:
|
|
|
|
|
// Let go of the mouse if the window is being minimized
|
|
|
|
|
if (wParam == SIZE_MINIMIZED)
|
|
|
|
|
{
|
|
|
|
|
DebugMsgF("Got WM_SIZE, SIZE_MINIMIZED%s", gClient ? "" : ", but no client, ignoring");
|
|
|
|
|
if (gClient)
|
|
|
|
|
gClient->WindowActivate(false);
|
|
|
|
|
}
|
|
|
|
|
// Redo the mouse capture if the window gets restored
|
|
|
|
|
else if (wParam == SIZE_RESTORED)
|
|
|
|
|
{
|
|
|
|
|
DebugMsgF("Got WM_SIZE, SIZE_RESTORED%s", gClient ? "" : ", but no client, ignoring");
|
|
|
|
|
if (gClient)
|
|
|
|
|
gClient->WindowActivate(true);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WM_CLOSE:
|
|
|
|
|
gClient->SetDone(TRUE);
|
|
|
|
|
if (plNetClientMgr * mgr = plNetClientMgr::GetInstance())
|
|
|
|
|
mgr->QueueDisableNet(false, nil);
|
|
|
|
|
DestroyWindow(gClient->GetWindowHandle());
|
|
|
|
|
return TRUE;
|
|
|
|
|
case WM_DESTROY:
|
|
|
|
|
gClient->SetDone(TRUE);
|
|
|
|
|
if (plNetClientMgr * mgr = plNetClientMgr::GetInstance())
|
|
|
|
|
mgr->QueueDisableNet(false, nil);
|
|
|
|
|
PostQuitMessage(0);
|
|
|
|
|
return TRUE;
|
|
|
|
|
case WM_CREATE:
|
|
|
|
|
// Create renderer
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PumpMessageQueueProc( void )
|
|
|
|
|
{
|
|
|
|
|
MSG msg;
|
|
|
|
|
|
|
|
|
|
// Look for a message
|
|
|
|
|
while (PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ))
|
|
|
|
|
{
|
|
|
|
|
// Handle the message
|
|
|
|
|
TranslateMessage( &msg );
|
|
|
|
|
DispatchMessage( &msg );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void InitNetClientComm()
|
|
|
|
|
{
|
|
|
|
|
NetCommStartup();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL CALLBACK AuthDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
|
|
|
{
|
|
|
|
|
static bool* cancelled = NULL;
|
|
|
|
|
|
|
|
|
|
switch( uMsg )
|
|
|
|
|
{
|
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
|
cancelled = (bool*)lParam;
|
|
|
|
|
SetTimer(hwndDlg, AUTH_LOGIN_TIMER, 10, NULL);
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
case WM_TIMER:
|
|
|
|
|
if (wParam == AUTH_LOGIN_TIMER)
|
|
|
|
|
{
|
|
|
|
|
if (NetCommIsLoginComplete())
|
|
|
|
|
EndDialog(hwndDlg, 1);
|
|
|
|
|
else
|
|
|
|
|
NetCommUpdate();
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
case WM_NCHITTEST:
|
|
|
|
|
SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, (LONG_PTR)HTCAPTION);
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
case WM_DESTROY:
|
|
|
|
|
KillTimer(hwndDlg, AUTH_LOGIN_TIMER);
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
case WM_COMMAND:
|
|
|
|
|
if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL)
|
|
|
|
|
{
|
|
|
|
|
*cancelled = true;
|
|
|
|
|
EndDialog(hwndDlg, 1);
|
|
|
|
|
}
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return DefWindowProc(hwndDlg, uMsg, wParam, lParam);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AuthenticateNetClientComm(ENetError* result, HWND parentWnd)
|
|
|
|
|
{
|
|
|
|
|
if (!NetCliAuthQueryConnected())
|
|
|
|
|
NetCommConnect();
|
|
|
|
|
|
|
|
|
|
bool cancelled = false;
|
|
|
|
|
NetCommAuthenticate(nil);
|
|
|
|
|
|
|
|
|
|
::DialogBoxParam(gHInst, MAKEINTRESOURCE( IDD_AUTHENTICATING ), parentWnd, AuthDialogProc, (LPARAM)&cancelled);
|
|
|
|
|
|
|
|
|
|
if (!cancelled)
|
|
|
|
|
*result = NetCommGetAuthResult();
|
|
|
|
|
else
|
|
|
|
|
*result = kNetSuccess;
|
|
|
|
|
|
|
|
|
|
return cancelled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DeInitNetClientComm()
|
|
|
|
|
{
|
|
|
|
|
NetCommShutdown();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL CALLBACK WaitingForPhysXDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
|
|
|
{
|
|
|
|
|
switch( uMsg )
|
|
|
|
|
{
|
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
|
::SetDlgItemText( hwndDlg, IDC_STARTING_TEXT, "Waiting for PhysX install...");
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool InitPhysX()
|
|
|
|
|
{
|
|
|
|
|
bool physXInstalled = false;
|
|
|
|
|
while (!physXInstalled)
|
|
|
|
|
{
|
|
|
|
|
plSimulationMgr::Init();
|
|
|
|
|
if (!plSimulationMgr::GetInstance())
|
|
|
|
|
{
|
|
|
|
|
int ret = hsMessageBox("PhysX is not installed, or an older version is installed.\nInstall new version? (Game will exit if you click \"No\")",
|
|
|
|
|
"Missing PhysX", hsMessageBoxYesNo);
|
|
|
|
|
if (ret == hsMBoxNo) // exit if no
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// launch the PhysX installer
|
|
|
|
|
STARTUPINFOW startupInfo;
|
|
|
|
|
PROCESS_INFORMATION processInfo;
|
|
|
|
|
ZERO(startupInfo);
|
|
|
|
|
ZERO(processInfo);
|
|
|
|
|
startupInfo.cb = sizeof(startupInfo);
|
|
|
|
|
if(!CreateProcessW(NULL, s_physXSetupExeName, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo))
|
|
|
|
|
{
|
|
|
|
|
hsMessageBox("Failed to launch PhysX installer.\nPlease re-run URU to ensure you have the latest version.", "Error", hsMessageBoxNormal);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// let the user know what's going on
|
|
|
|
|
HWND waitingDialog = ::CreateDialog(gHInst, MAKEINTRESOURCE(IDD_LOADING), NULL, WaitingForPhysXDialogProc);
|
|
|
|
|
|
|
|
|
|
// run a loop to wait for it to quit, pumping the windows message queue intermittently
|
|
|
|
|
DWORD waitRet = WaitForSingleObject(processInfo.hProcess, 100);
|
|
|
|
|
MSG msg;
|
|
|
|
|
while (waitRet == WAIT_TIMEOUT)
|
|
|
|
|
{
|
|
|
|
|
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
|
|
|
{
|
|
|
|
|
TranslateMessage(&msg);
|
|
|
|
|
DispatchMessage(&msg);
|
|
|
|
|
}
|
|
|
|
|
waitRet = WaitForSingleObject(processInfo.hProcess, 100);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// cleanup
|
|
|
|
|
CloseHandle(processInfo.hThread);
|
|
|
|
|
CloseHandle(processInfo.hProcess);
|
|
|
|
|
::DestroyWindow(waitingDialog);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
plSimulationMgr::GetInstance()->Suspend();
|
|
|
|
|
physXInstalled = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool InitClient( HWND hWnd )
|
|
|
|
|
{
|
|
|
|
|
plResManager *resMgr = TRACKED_NEW plResManager;
|
|
|
|
|
resMgr->SetDataPath("dat");
|
|
|
|
|
hsgResMgr::Init(resMgr);
|
|
|
|
|
gClient = TRACKED_NEW plClient;
|
|
|
|
|
if( gClient == nil )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!InitPhysX())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
gClient->SetWindowHandle( hWnd );
|
|
|
|
|
|
|
|
|
|
#ifdef DETACH_EXE
|
|
|
|
|
hInstance = ((LPCREATESTRUCT) lParam)->hInstance;
|
|
|
|
|
#endif
|
|
|
|
|
// If in fullscreen mode, get rid of the window borders. Note: this won't take
|
|
|
|
|
// effect until the next SetWindowPos call
|
|
|
|
|
|
|
|
|
|
#ifdef DETACH_EXE
|
|
|
|
|
|
|
|
|
|
// This Function loads the EXE into Virtual memory...supposedly
|
|
|
|
|
HRESULT hr = DetachFromMedium(hInstance, DMDFM_ALWAYS | DMDFM_ALLPAGES);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if( gClient->InitPipeline() )
|
|
|
|
|
gClient->SetDone(true);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if( gClient->GetPipeline()->IsFullScreen() )
|
|
|
|
|
{
|
|
|
|
|
SetWindowLong(hWnd, GWL_STYLE, WS_POPUP);
|
|
|
|
|
SetWindowLong(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST);
|
|
|
|
|
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
|
|
|
|
|
gWinBorderDX = gWinBorderDY = gWinMenuDY = 0;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
SetWindowLong(hWnd, GWL_STYLE, WS_OVERLAPPED | WS_CAPTION);
|
|
|
|
|
SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int goodWidth = gClient->GetPipeline()->Width() + gWinBorderDX * 2;
|
|
|
|
|
int goodHeight = gClient->GetPipeline()->Height() + gWinBorderDY * 2 + gWinMenuDY;
|
|
|
|
|
|
|
|
|
|
SetWindowPos(
|
|
|
|
|
hWnd,
|
|
|
|
|
nil,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
goodWidth,
|
|
|
|
|
goodHeight,
|
|
|
|
|
SWP_NOCOPYBITS
|
|
|
|
|
| SWP_NOMOVE
|
|
|
|
|
| SWP_NOOWNERZORDER
|
|
|
|
|
| SWP_NOZORDER
|
|
|
|
|
| SWP_FRAMECHANGED
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( gPendingActivate )
|
|
|
|
|
{
|
|
|
|
|
// We need this because the window gets a WM_ACTIVATE before we get to this function, so
|
|
|
|
|
// the above flag lets us know that we need to fake a late activate msg to the client
|
|
|
|
|
gClient->WindowActivate( gPendingActivateFlag );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gClient->SetMessagePumpProc( PumpMessageQueueProc );
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initializes all that windows junk, creates class then shows main window
|
|
|
|
|
BOOL WinInit(HINSTANCE hInst, int nCmdShow)
|
|
|
|
|
{
|
|
|
|
|
// Fill out WNDCLASS info
|
|
|
|
|
WNDCLASS wndClass;
|
|
|
|
|
wndClass.style = CS_DBLCLKS; // CS_HREDRAW | CS_VREDRAW;
|
|
|
|
|
wndClass.lpfnWndProc = WndProc;
|
|
|
|
|
wndClass.cbClsExtra = 0;
|
|
|
|
|
wndClass.cbWndExtra = 0;
|
|
|
|
|
wndClass.hInstance = hInst;
|
|
|
|
|
wndClass.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON_DIRT));
|
|
|
|
|
|
|
|
|
|
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
|
|
|
wndClass.hbrBackground = (struct HBRUSH__*) (GetStockObject(BLACK_BRUSH));
|
|
|
|
|
wndClass.lpszMenuName = CLASSNAME;
|
|
|
|
|
wndClass.lpszClassName = CLASSNAME;
|
|
|
|
|
|
|
|
|
|
// can only run one at a time anyway, so just quit if another is running
|
|
|
|
|
if (!RegisterClass(&wndClass))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
/// 8.11.2000 - Test for OpenGL fullscreen, and if so use no border, no caption;
|
|
|
|
|
/// else, use our normal styles
|
|
|
|
|
|
|
|
|
|
char windowName[256];
|
|
|
|
|
wchar productString[256];
|
|
|
|
|
//#ifdef PLASMA_EXTERNAL_RELEASE
|
|
|
|
|
#if 0 // Show the full product string in external build window title until we roll it into the options dialog -eap
|
|
|
|
|
StrCopy(productString, ProductLongName(), arrsize(productString));
|
|
|
|
|
#else
|
|
|
|
|
ProductString(productString, arrsize(productString));
|
|
|
|
|
#endif
|
|
|
|
|
StrToAnsi(windowName, productString, arrsize(windowName));
|
|
|
|
|
|
|
|
|
|
// Create a window
|
|
|
|
|
HWND hWnd = CreateWindow(
|
|
|
|
|
CLASSNAME, windowName,
|
|
|
|
|
WS_OVERLAPPEDWINDOW,
|
|
|
|
|
0, 0,
|
|
|
|
|
800 + gWinBorderDX * 2,
|
|
|
|
|
600 + gWinBorderDY * 2 + gWinMenuDY,
|
|
|
|
|
NULL, NULL, hInst, NULL
|
|
|
|
|
);
|
|
|
|
|
// gClient->SetWindowHandle((hsWindowHndl)
|
|
|
|
|
|
|
|
|
|
if( !InitClient( hWnd ) )
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
// Return false if window creation failed
|
|
|
|
|
if (!gClient->GetWindowHandle())
|
|
|
|
|
{
|
|
|
|
|
OutputDebugString("Create window failed\n");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
OutputDebugString("Create window OK\n");
|
|
|
|
|
}
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// For error logging
|
|
|
|
|
//
|
|
|
|
|
static FILE* gDebugFile=NULL;
|
|
|
|
|
void DebugMessageProc(const char* msg)
|
|
|
|
|
{
|
|
|
|
|
OutputDebugString(msg);
|
|
|
|
|
OutputDebugString("\n");
|
|
|
|
|
if (gDebugFile != NULL)
|
|
|
|
|
{
|
|
|
|
|
fprintf(gDebugFile, "%s\n", msg);
|
|
|
|
|
fflush(gDebugFile);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DebugMsgF(const char* format, ...)
|
|
|
|
|
{
|
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
|
va_list args;
|
|
|
|
|
va_start(args, format);
|
|
|
|
|
|
|
|
|
|
char buf[256];
|
|
|
|
|
int numWritten = _vsnprintf(buf, sizeof(buf), format, args);
|
|
|
|
|
hsAssert(numWritten > 0, "Buffer too small");
|
|
|
|
|
|
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
|
|
DebugMessageProc(buf);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool IsMachineLittleEndian() {
|
|
|
|
|
int i = 1;
|
|
|
|
|
char *p = (char *) &i;
|
|
|
|
|
if (p[0] == 1) // Lowest address contains the least significant byte
|
|
|
|
|
return true;
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline static dword ToBigEndian (dword value) {
|
|
|
|
|
return ((value) << 24) | ((value & 0x0000ff00) << 8) | ((value & 0x00ff0000) >> 8) | ((value) >> 24);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void AuthFailedStrings (ENetError authError, bool fromGT,
|
|
|
|
|
const char **ppStr1, const char **ppStr2,
|
|
|
|
|
const wchar **ppWStr)
|
|
|
|
|
{
|
|
|
|
|
*ppStr1 = NULL;
|
|
|
|
|
*ppStr2 = NULL;
|
|
|
|
|
*ppWStr = NULL;
|
|
|
|
|
|
|
|
|
|
switch (plLocalization::GetLanguage())
|
|
|
|
|
{
|
|
|
|
|
case plLocalization::kFrench:
|
|
|
|
|
case plLocalization::kGerman:
|
|
|
|
|
case plLocalization::kJapanese:
|
|
|
|
|
*ppStr1 = "Authentication Failed. Please try again.";
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
*ppStr1 = "Authentication Failed. Please try again.";
|
|
|
|
|
|
|
|
|
|
switch (authError)
|
|
|
|
|
{
|
|
|
|
|
case kNetErrAccountNotFound:
|
|
|
|
|
*ppStr2 = "Account Not Found.";
|
|
|
|
|
break;
|
|
|
|
|
case kNetErrAccountNotActivated:
|
|
|
|
|
*ppStr2 = "Account Not Activated.";
|
|
|
|
|
break;
|
|
|
|
|
case kNetErrConnectFailed:
|
|
|
|
|
*ppStr2 = "Unable to connect to Myst Online.";
|
|
|
|
|
break;
|
|
|
|
|
case kNetErrDisconnected:
|
|
|
|
|
*ppStr2 = "Disconnected from Myst Online.";
|
|
|
|
|
break;
|
|
|
|
|
case kNetErrAuthenticationFailed:
|
|
|
|
|
if (fromGT)
|
|
|
|
|
*ppStr2 = "GameTap authentication failed, please enter your GameTap username and password.";
|
|
|
|
|
else
|
|
|
|
|
*ppStr2 = "Incorrect password.\n\nMake sure CAPS LOCK is not on.";
|
|
|
|
|
break;
|
|
|
|
|
case kNetErrGTServerError:
|
|
|
|
|
case kNetErrGameTapConnectionFailed:
|
|
|
|
|
*ppStr2 = "Unable to connect to GameTap, please try again in a few minutes.";
|
|
|
|
|
break;
|
|
|
|
|
case kNetErrAccountBanned:
|
|
|
|
|
*ppStr2 = "Your account has been banned from accessing Myst Online. If you are unsure as to why this happened please contact customer support.";
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
*ppWStr = NetErrorToString (authError);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL CALLBACK AuthFailedDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
|
|
|
{
|
|
|
|
|
switch( uMsg )
|
|
|
|
|
{
|
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
|
{
|
|
|
|
|
LoginDialogParam* loginParam = (LoginDialogParam*)lParam;
|
|
|
|
|
const char *pStr1, *pStr2;
|
|
|
|
|
const wchar *pWStr;
|
|
|
|
|
|
|
|
|
|
AuthFailedStrings (loginParam->authError, loginParam->fromGT,
|
|
|
|
|
&pStr1, &pStr2, &pWStr);
|
|
|
|
|
|
|
|
|
|
if (pStr1)
|
|
|
|
|
::SetDlgItemText( hwndDlg, IDC_AUTH_TEXT, pStr1);
|
|
|
|
|
if (pStr2)
|
|
|
|
|
::SetDlgItemText( hwndDlg, IDC_AUTH_MESSAGE, pStr2);
|
|
|
|
|
if (pWStr)
|
|
|
|
|
::SetDlgItemTextW( hwndDlg, IDC_AUTH_MESSAGE, pWStr);
|
|
|
|
|
}
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
case WM_COMMAND:
|
|
|
|
|
if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK)
|
|
|
|
|
{
|
|
|
|
|
EndDialog(hwndDlg, 1);
|
|
|
|
|
}
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
case WM_NCHITTEST:
|
|
|
|
|
SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, (LONG_PTR)HTCAPTION);
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL CALLBACK UruTOSDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
|
|
|
{
|
|
|
|
|
switch( uMsg )
|
|
|
|
|
{
|
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
|
{
|
|
|
|
|
SetWindowText(hwndDlg, "End User License Agreement");
|
|
|
|
|
SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon((HINSTANCE)lParam, MAKEINTRESOURCE(IDI_ICON_DIRT)));
|
|
|
|
|
|
|
|
|
|
hsUNIXStream stream;
|
|
|
|
|
if (stream.Open("TOS.txt", "rt"))
|
|
|
|
|
{
|
|
|
|
|
char* eulaData = NULL;
|
|
|
|
|
unsigned dataLen = stream.GetSizeLeft();
|
|
|
|
|
|
|
|
|
|
eulaData = TRACKED_NEW char[dataLen + 1];
|
|
|
|
|
ZeroMemory(eulaData, dataLen + 1);
|
|
|
|
|
|
|
|
|
|
stream.Read(dataLen, eulaData);
|
|
|
|
|
|
|
|
|
|
SetDlgItemText(hwndDlg, IDC_URULOGIN_EULATEXT, eulaData);
|
|
|
|
|
delete [] eulaData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case WM_COMMAND:
|
|
|
|
|
if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL))
|
|
|
|
|
{
|
|
|
|
|
bool ok = (LOWORD(wParam) == IDOK);
|
|
|
|
|
EndDialog(hwndDlg, ok);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WM_NCHITTEST:
|
|
|
|
|
SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, (LONG_PTR)HTCAPTION);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void SaveUserPass (char *username, char *password, ShaDigest *pNamePassHash, bool remember_password,
|
|
|
|
|
bool fromGT)
|
|
|
|
|
{
|
|
|
|
|
UInt32 cryptKey[4];
|
|
|
|
|
ZeroMemory(cryptKey, sizeof(cryptKey));
|
|
|
|
|
GetCryptKey(cryptKey, arrsize(cryptKey));
|
|
|
|
|
|
|
|
|
|
wchar wusername[kMaxAccountNameLength];
|
|
|
|
|
wchar wpassword[kMaxPasswordLength];
|
|
|
|
|
|
|
|
|
|
StrToUnicode(wusername, username, arrsize(wusername));
|
|
|
|
|
|
|
|
|
|
// if the password field is the fake string then we've already
|
|
|
|
|
// loaded the namePassHash from the file
|
|
|
|
|
if (StrCmp(password, FAKE_PASS_STRING) != 0)
|
|
|
|
|
{
|
|
|
|
|
StrToUnicode(wpassword, password, arrsize(wpassword));
|
|
|
|
|
|
|
|
|
|
wchar domain[15];
|
|
|
|
|
PathSplitEmail(wusername, nil, 0, domain, arrsize(domain), nil, 0, nil, 0, 0);
|
|
|
|
|
|
|
|
|
|
if (StrLen(domain) == 0 || StrCmpI(domain, L"gametap") == 0) {
|
|
|
|
|
CryptDigest(
|
|
|
|
|
kCryptSha1,
|
|
|
|
|
pNamePassHash,
|
|
|
|
|
StrLen(password) * sizeof(password[0]),
|
|
|
|
|
password
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (IsMachineLittleEndian()) {
|
|
|
|
|
pNamePassHash->data[0] = ToBigEndian(pNamePassHash->data[0]);
|
|
|
|
|
pNamePassHash->data[1] = ToBigEndian(pNamePassHash->data[1]);
|
|
|
|
|
pNamePassHash->data[2] = ToBigEndian(pNamePassHash->data[2]);
|
|
|
|
|
pNamePassHash->data[3] = ToBigEndian(pNamePassHash->data[3]);
|
|
|
|
|
pNamePassHash->data[4] = ToBigEndian(pNamePassHash->data[4]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
CryptHashPassword(wusername, wpassword, pNamePassHash);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetCommSetAccountUsernamePassword(wusername, *pNamePassHash);
|
|
|
|
|
if (TGIsCider)
|
|
|
|
|
NetCommSetAuthTokenAndOS(nil, L"mac");
|
|
|
|
|
else
|
|
|
|
|
NetCommSetAuthTokenAndOS(nil, L"win");
|
|
|
|
|
|
|
|
|
|
if (!fromGT) {
|
|
|
|
|
wchar fileAndPath[MAX_PATH];
|
|
|
|
|
PathGetInitDirectory(fileAndPath, arrsize(fileAndPath));
|
|
|
|
|
PathAddFilename(fileAndPath, fileAndPath, L"login.dat", arrsize(fileAndPath));
|
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
|
// internal builds can use the local init directory
|
|
|
|
|
wchar localFileAndPath[MAX_PATH];
|
|
|
|
|
StrCopy(localFileAndPath, L"init\\login.dat", arrsize(localFileAndPath));
|
|
|
|
|
if (PathDoesFileExist(localFileAndPath))
|
|
|
|
|
StrCopy(fileAndPath, localFileAndPath, arrsize(localFileAndPath));
|
|
|
|
|
#endif
|
|
|
|
|
hsStream* stream = plEncryptedStream::OpenEncryptedFileWrite(fileAndPath, cryptKey);
|
|
|
|
|
if (stream)
|
|
|
|
|
{
|
|
|
|
|
stream->Write(sizeof(cryptKey), cryptKey);
|
|
|
|
|
stream->WriteSafeString(username);
|
|
|
|
|
stream->Writebool(remember_password);
|
|
|
|
|
if (remember_password)
|
|
|
|
|
stream->Write(sizeof(pNamePassHash->data), pNamePassHash->data);
|
|
|
|
|
stream->Close();
|
|
|
|
|
delete stream;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void LoadUserPass (const wchar *accountName, char *username, ShaDigest *pNamePassHash, bool *pRemember,
|
|
|
|
|
bool fromGT, int *pFocus)
|
|
|
|
|
{
|
|
|
|
|
UInt32 cryptKey[4];
|
|
|
|
|
ZeroMemory(cryptKey, sizeof(cryptKey));
|
|
|
|
|
GetCryptKey(cryptKey, arrsize(cryptKey));
|
|
|
|
|
|
|
|
|
|
char* temp;
|
|
|
|
|
*pRemember = false;
|
|
|
|
|
username[0] = '\0';
|
|
|
|
|
|
|
|
|
|
if (!fromGT)
|
|
|
|
|
{
|
|
|
|
|
wchar fileAndPath[MAX_PATH];
|
|
|
|
|
PathGetInitDirectory(fileAndPath, arrsize(fileAndPath));
|
|
|
|
|
PathAddFilename(fileAndPath, fileAndPath, L"login.dat", arrsize(fileAndPath));
|
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
|
// internal builds can use the local init directory
|
|
|
|
|
wchar localFileAndPath[MAX_PATH];
|
|
|
|
|
StrCopy(localFileAndPath, L"init\\login.dat", arrsize(localFileAndPath));
|
|
|
|
|
if (PathDoesFileExist(localFileAndPath))
|
|
|
|
|
StrCopy(fileAndPath, localFileAndPath, arrsize(localFileAndPath));
|
|
|
|
|
#endif
|
|
|
|
|
hsStream* stream = plEncryptedStream::OpenEncryptedFile(fileAndPath, true, cryptKey);
|
|
|
|
|
if (stream && !stream->AtEnd())
|
|
|
|
|
{
|
|
|
|
|
UInt32 savedKey[4];
|
|
|
|
|
stream->Read(sizeof(savedKey), savedKey);
|
|
|
|
|
|
|
|
|
|
if (memcmp(cryptKey, savedKey, sizeof(savedKey)) == 0)
|
|
|
|
|
{
|
|
|
|
|
temp = stream->ReadSafeString();
|
|
|
|
|
|
|
|
|
|
if (temp)
|
|
|
|
|
{
|
|
|
|
|
StrCopy(username, temp, kMaxAccountNameLength);
|
|
|
|
|
delete temp;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
username[0] = '\0';
|
|
|
|
|
|
|
|
|
|
*pRemember = stream->Readbool();
|
|
|
|
|
|
|
|
|
|
if (*pRemember)
|
|
|
|
|
{
|
|
|
|
|
stream->Read(sizeof(pNamePassHash->data), pNamePassHash->data);
|
|
|
|
|
*pFocus = IDOK;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
*pFocus = IDC_URULOGIN_PASSWORD;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stream->Close();
|
|
|
|
|
delete stream;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
StrToAnsi (username, accountName, kMaxAccountNameLength);
|
|
|
|
|
*pFocus = IDC_URULOGIN_PASSWORD;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StatusCallback(void *param)
|
|
|
|
|
{
|
|
|
|
|
HWND hwnd = (HWND)param;
|
|
|
|
|
|
|
|
|
|
while(s_loginDlgRunning)
|
|
|
|
|
{
|
|
|
|
|
// get status message from webpage and display in status area.
|
|
|
|
|
const wchar *path = BuildTypeServerStatusPath();
|
|
|
|
|
if(path)
|
|
|
|
|
{
|
|
|
|
|
HINTERNET hSession = 0;
|
|
|
|
|
HINTERNET hConnect = 0;
|
|
|
|
|
HINTERNET hRequest = 0;
|
|
|
|
|
|
|
|
|
|
hSession = WinHttpOpen(
|
|
|
|
|
L"UruClient/1.0",
|
|
|
|
|
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
|
|
|
|
|
WINHTTP_NO_PROXY_NAME,
|
|
|
|
|
WINHTTP_NO_PROXY_BYPASS, 0
|
|
|
|
|
);
|
|
|
|
|
if(hSession)
|
|
|
|
|
{
|
|
|
|
|
HINTERNET hConnect = WinHttpConnect( hSession, STATUS_PATH, INTERNET_DEFAULT_HTTP_PORT, 0);
|
|
|
|
|
if(hConnect)
|
|
|
|
|
{
|
|
|
|
|
HINTERNET hRequest = WinHttpOpenRequest(
|
|
|
|
|
hConnect,
|
|
|
|
|
L"GET",
|
|
|
|
|
path,
|
|
|
|
|
NULL,
|
|
|
|
|
WINHTTP_NO_REFERER,
|
|
|
|
|
WINHTTP_DEFAULT_ACCEPT_TYPES,
|
|
|
|
|
0
|
|
|
|
|
);
|
|
|
|
|
if(hRequest)
|
|
|
|
|
{
|
|
|
|
|
static char data[256] = {0};
|
|
|
|
|
DWORD bytesRead;
|
|
|
|
|
WinHttpSendRequest(
|
|
|
|
|
hRequest,
|
|
|
|
|
WINHTTP_NO_ADDITIONAL_HEADERS,
|
|
|
|
|
0,
|
|
|
|
|
WINHTTP_NO_REQUEST_DATA,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0
|
|
|
|
|
);
|
|
|
|
|
WinHttpReceiveResponse(hRequest, 0);
|
|
|
|
|
WinHttpReadData(hRequest, data, 255, &bytesRead);
|
|
|
|
|
data[bytesRead] = 0;
|
|
|
|
|
if(bytesRead)
|
|
|
|
|
PostMessage(hwnd, WM_USER_SETSTATUSMSG, 0, (LPARAM) data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
WinHttpCloseHandle(hRequest);
|
|
|
|
|
WinHttpCloseHandle(hConnect);
|
|
|
|
|
WinHttpCloseHandle(hSession);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
break; // no status message
|
|
|
|
|
|
|
|
|
|
for(unsigned i = 0; i < UPDATE_STATUSMSG_SECONDS && s_loginDlgRunning; ++i)
|
|
|
|
|
{
|
|
|
|
|
Sleep(1000);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
s_statusEvent.Signal();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL CALLBACK UruLoginDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
|
|
|
{
|
|
|
|
|
static ShaDigest namePassHash;
|
|
|
|
|
static LoginDialogParam* loginParam;
|
|
|
|
|
static bool showAuthFailed = false;
|
|
|
|
|
|
|
|
|
|
switch( uMsg )
|
|
|
|
|
{
|
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
|
{
|
|
|
|
|
s_loginDlgRunning = true;
|
|
|
|
|
_beginthread(StatusCallback, 0, hwndDlg);
|
|
|
|
|
loginParam = (LoginDialogParam*)lParam;
|
|
|
|
|
|
|
|
|
|
SetWindowText(hwndDlg, "Login");
|
|
|
|
|
SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon(gHInst, MAKEINTRESOURCE(IDI_ICON_DIRT)));
|
|
|
|
|
|
|
|
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
|
|
|
|
|
|
|
|
|
|
char username[kMaxAccountNameLength];
|
|
|
|
|
bool remember_password = false;
|
|
|
|
|
|
|
|
|
|
int focus_control = IDC_URULOGIN_USERNAME;
|
|
|
|
|
|
|
|
|
|
LoadUserPass (loginParam->accountName, username, &namePassHash, &remember_password, loginParam->fromGT, &focus_control);
|
|
|
|
|
|
|
|
|
|
SetDlgItemText(hwndDlg, IDC_URULOGIN_USERNAME, username);
|
|
|
|
|
CheckDlgButton(hwndDlg, IDC_URULOGIN_REMEMBERPASS, remember_password ? BST_CHECKED : BST_UNCHECKED);
|
|
|
|
|
if (remember_password)
|
|
|
|
|
SetDlgItemText(hwndDlg, IDC_URULOGIN_PASSWORD, FAKE_PASS_STRING);
|
|
|
|
|
if (loginParam->fromGT)
|
|
|
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_URULOGIN_REMEMBERPASS), FALSE);
|
|
|
|
|
|
|
|
|
|
SetFocus(GetDlgItem(hwndDlg, focus_control));
|
|
|
|
|
|
|
|
|
|
if (IS_NET_ERROR(loginParam->authError))
|
|
|
|
|
{
|
|
|
|
|
showAuthFailed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char windowName[256];
|
|
|
|
|
wchar productString[256];
|
|
|
|
|
ProductString(productString, arrsize(productString));
|
|
|
|
|
StrToAnsi(windowName, productString, arrsize(windowName));
|
|
|
|
|
SendMessage(GetDlgItem(hwndDlg, IDC_PRODUCTSTRING), WM_SETTEXT, 0, (LPARAM) windowName);
|
|
|
|
|
|
|
|
|
|
SetTimer(hwndDlg, AUTH_LOGIN_TIMER, 10, NULL);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case WM_USER_SETSTATUSMSG:
|
|
|
|
|
SendMessage(GetDlgItem(hwndDlg, IDC_STATUS_TEXT), WM_SETTEXT, 0, lParam);
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
case WM_DESTROY:
|
|
|
|
|
{
|
|
|
|
|
s_loginDlgRunning = false;
|
|
|
|
|
s_statusEvent.Wait(kEventWaitForever);
|
|
|
|
|
KillTimer(hwndDlg, AUTH_LOGIN_TIMER);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case WM_NCHITTEST:
|
|
|
|
|
{
|
|
|
|
|
SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, (LONG_PTR)HTCAPTION);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case WM_PAINT:
|
|
|
|
|
{
|
|
|
|
|
if (showAuthFailed)
|
|
|
|
|
{
|
|
|
|
|
SetTimer(hwndDlg, AUTH_FAILED_TIMER, 10, NULL);
|
|
|
|
|
showAuthFailed = false;
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case WM_COMMAND:
|
|
|
|
|
{
|
|
|
|
|
if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL))
|
|
|
|
|
{
|
|
|
|
|
bool ok = (LOWORD(wParam) == IDOK);
|
|
|
|
|
if (ok)
|
|
|
|
|
{
|
|
|
|
|
char username[kMaxAccountNameLength];
|
|
|
|
|
char password[kMaxPasswordLength];
|
|
|
|
|
bool remember_password = false;
|
|
|
|
|
|
|
|
|
|
GetDlgItemText(hwndDlg, IDC_URULOGIN_USERNAME, username, kMaxAccountNameLength);
|
|
|
|
|
GetDlgItemText(hwndDlg, IDC_URULOGIN_PASSWORD, password, kMaxPasswordLength);
|
|
|
|
|
remember_password = (IsDlgButtonChecked(hwndDlg, IDC_URULOGIN_REMEMBERPASS) == BST_CHECKED);
|
|
|
|
|
|
|
|
|
|
SaveUserPass (username, password, &namePassHash, remember_password, loginParam->fromGT);
|
|
|
|
|
|
|
|
|
|
LoginDialogParam loginParam;
|
|
|
|
|
MemSet(&loginParam, 0, sizeof(loginParam));
|
|
|
|
|
bool cancelled = AuthenticateNetClientComm(&loginParam.authError, hwndDlg);
|
|
|
|
|
|
|
|
|
|
if (IS_NET_SUCCESS(loginParam.authError) && !cancelled)
|
|
|
|
|
EndDialog(hwndDlg, ok);
|
|
|
|
|
else {
|
|
|
|
|
if (!cancelled)
|
|
|
|
|
::DialogBoxParam(gHInst, MAKEINTRESOURCE( IDD_AUTHFAILED ), hwndDlg, AuthFailedDialogProc, (LPARAM)&loginParam);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
NetCommDisconnect();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
EndDialog(hwndDlg, ok);
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
else if (HIWORD(wParam) == EN_CHANGE && LOWORD(wParam) == IDC_URULOGIN_USERNAME)
|
|
|
|
|
{
|
|
|
|
|
char username[kMaxAccountNameLength];
|
|
|
|
|
GetDlgItemText(hwndDlg, IDC_URULOGIN_USERNAME, username, kMaxAccountNameLength);
|
|
|
|
|
|
|
|
|
|
if (StrLen(username) == 0)
|
|
|
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
|
|
|
|
|
else
|
|
|
|
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE);
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_URULOGIN_GAMETAPLINK)
|
|
|
|
|
{
|
|
|
|
|
ShellExecute(NULL, "open", "http://www.mystonline.com/signup.html", NULL, NULL, SW_SHOWNORMAL);
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_URULOGIN_GAMETAPLINK2)
|
|
|
|
|
{
|
|
|
|
|
ShellExecute(NULL, "open", "http://www.mystonline.com/donate", NULL, NULL, SW_SHOWNORMAL);
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case WM_TIMER:
|
|
|
|
|
{
|
|
|
|
|
switch(wParam)
|
|
|
|
|
{
|
|
|
|
|
case AUTH_FAILED_TIMER:
|
|
|
|
|
KillTimer(hwndDlg, AUTH_FAILED_TIMER);
|
|
|
|
|
::DialogBoxParam(gHInst, MAKEINTRESOURCE( IDD_AUTHFAILED ), hwndDlg, AuthFailedDialogProc, (LPARAM)loginParam);
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
case AUTH_LOGIN_TIMER:
|
|
|
|
|
NetCommUpdate();
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL CALLBACK SplashDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
|
|
|
{
|
|
|
|
|
switch( uMsg )
|
|
|
|
|
{
|
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
|
switch (plLocalization::GetLanguage())
|
|
|
|
|
{
|
|
|
|
|
case plLocalization::kFrench:
|
|
|
|
|
::SetDlgItemText( hwndDlg, IDC_STARTING_TEXT, "D<EFBFBD>marrage d'URU. Veuillez patienter...");
|
|
|
|
|
break;
|
|
|
|
|
case plLocalization::kGerman:
|
|
|
|
|
::SetDlgItemText( hwndDlg, IDC_STARTING_TEXT, "Starte URU, bitte warten ...");
|
|
|
|
|
break;
|
|
|
|
|
/* case plLocalization::kSpanish:
|
|
|
|
|
::SetDlgItemText( hwndDlg, IDC_STARTING_TEXT, "Iniciando URU, por favor espera...");
|
|
|
|
|
break;
|
|
|
|
|
case plLocalization::kItalian:
|
|
|
|
|
::SetDlgItemText( hwndDlg, IDC_STARTING_TEXT, "Avvio di URU, attendere...");
|
|
|
|
|
break;
|
|
|
|
|
*/ // default is English
|
|
|
|
|
case plLocalization::kJapanese:
|
|
|
|
|
::SetDlgItemText( hwndDlg, IDC_STARTING_TEXT, "...");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
::SetDlgItemText( hwndDlg, IDC_STARTING_TEXT, "Starting URU. Please wait...");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char sStackTraceMsg[ 10240 ] = "";
|
|
|
|
|
void printStackTrace( char* buffer, int bufferSize, unsigned long stackPtr = 0, unsigned long opPtr = 0 );
|
|
|
|
|
//void StackTraceFromContext( HANDLE hThread, CONTEXT *context, char *outputBuffer );
|
|
|
|
|
|
|
|
|
|
BOOL CALLBACK ExceptionDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
|
|
|
{
|
|
|
|
|
static char *sLastMsg = nil;
|
|
|
|
|
|
|
|
|
|
switch( uMsg )
|
|
|
|
|
{
|
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
|
sLastMsg = (char *)lParam;
|
|
|
|
|
::SetDlgItemText( hwndDlg, IDC_CRASHINFO, sLastMsg );
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case WM_COMMAND:
|
|
|
|
|
if( wParam == IDC_COPY && sLastMsg != nil )
|
|
|
|
|
{
|
|
|
|
|
HGLOBAL copyText = GlobalAlloc( GMEM_DDESHARE, sizeof( TCHAR ) * ( strlen( sLastMsg ) + 1 ) );
|
|
|
|
|
if( copyText != nil )
|
|
|
|
|
{
|
|
|
|
|
LPTSTR copyPtr = (LPTSTR)GlobalLock( copyText );
|
|
|
|
|
memcpy( copyPtr, sLastMsg, ( strlen( sLastMsg ) + 1 ) * sizeof( TCHAR ) );
|
|
|
|
|
GlobalUnlock( copyText );
|
|
|
|
|
|
|
|
|
|
::OpenClipboard( hwndDlg );
|
|
|
|
|
::EmptyClipboard();
|
|
|
|
|
::SetClipboardData( CF_TEXT, copyText );
|
|
|
|
|
::CloseClipboard();
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else if( wParam == IDOK )
|
|
|
|
|
EndDialog( hwndDlg, IDOK );
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LONG WINAPI plCustomUnhandledExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo )
|
|
|
|
|
{
|
|
|
|
|
const char *type = nil;
|
|
|
|
|
switch( ExceptionInfo->ExceptionRecord->ExceptionCode )
|
|
|
|
|
{
|
|
|
|
|
case EXCEPTION_ACCESS_VIOLATION: type = "Access violation"; break;
|
|
|
|
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: type = "Array bounds exceeded"; break;
|
|
|
|
|
case EXCEPTION_BREAKPOINT: type = "Breakpoint"; break;
|
|
|
|
|
case EXCEPTION_DATATYPE_MISALIGNMENT: type = "Datatype misalignment"; break;
|
|
|
|
|
case EXCEPTION_FLT_DENORMAL_OPERAND: type = "Floating operand denormal"; break;
|
|
|
|
|
case EXCEPTION_FLT_DIVIDE_BY_ZERO: type = "Floating-point divide-by-zero"; break;
|
|
|
|
|
case EXCEPTION_FLT_INEXACT_RESULT: type = "Floating-point inexact result"; break;
|
|
|
|
|
case EXCEPTION_FLT_INVALID_OPERATION: type = "Floating-point invalid operation"; break;
|
|
|
|
|
case EXCEPTION_FLT_OVERFLOW: type = "Floating-point overflow"; break;
|
|
|
|
|
case EXCEPTION_FLT_STACK_CHECK: type = "Floating-point stack error"; break;
|
|
|
|
|
case EXCEPTION_FLT_UNDERFLOW: type = "Floating-point underflow"; break;
|
|
|
|
|
case EXCEPTION_ILLEGAL_INSTRUCTION: type = "Illegal instruction"; break;
|
|
|
|
|
case EXCEPTION_IN_PAGE_ERROR: type = "Exception in page"; break;
|
|
|
|
|
case EXCEPTION_INT_DIVIDE_BY_ZERO: type = "Integer divide-by-zero"; break;
|
|
|
|
|
case EXCEPTION_INT_OVERFLOW: type = "Integer overflow"; break;
|
|
|
|
|
case EXCEPTION_INVALID_DISPOSITION: type = "Invalid disposition"; break;
|
|
|
|
|
case EXCEPTION_NONCONTINUABLE_EXCEPTION: type = "Noncontinuable exception"; break;
|
|
|
|
|
case EXCEPTION_PRIV_INSTRUCTION: type = "Private instruction"; break;
|
|
|
|
|
case EXCEPTION_SINGLE_STEP: type = "Single-step"; break;
|
|
|
|
|
case EXCEPTION_STACK_OVERFLOW: type = "Stack overflow"; break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char prodName[256];
|
|
|
|
|
wchar productString[256];
|
|
|
|
|
ProductString(productString, arrsize(productString));
|
|
|
|
|
StrToAnsi(prodName, productString, arrsize(prodName));
|
|
|
|
|
|
|
|
|
|
sprintf( sStackTraceMsg, "%s\r\nException type: %s\r\n", prodName, ( type != nil ) ? type : "(unknown)" );
|
|
|
|
|
|
|
|
|
|
printStackTrace( sStackTraceMsg, sizeof( sStackTraceMsg ), ExceptionInfo->ContextRecord->Ebp, (unsigned long)ExceptionInfo->ExceptionRecord->ExceptionAddress );
|
|
|
|
|
|
|
|
|
|
/// Print the info out to a log file as well
|
|
|
|
|
hsUNIXStream log;
|
|
|
|
|
wchar fileAndPath[MAX_PATH];
|
|
|
|
|
PathGetLogDirectory(fileAndPath, arrsize(fileAndPath));
|
|
|
|
|
PathAddFilename(fileAndPath, fileAndPath, L"stackDump.log", arrsize(fileAndPath));
|
|
|
|
|
if( log.Open( fileAndPath, L"wt" ) )
|
|
|
|
|
{
|
|
|
|
|
log.WriteString( sStackTraceMsg );
|
|
|
|
|
log.Close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Hopefully we can access this resource, even given the exception (i.e. very-bad-error) we just experienced
|
|
|
|
|
if(TGIsCider || (::DialogBoxParam( gHInst, MAKEINTRESOURCE( IDD_EXCEPTION ), ( gClient != nil ) ? gClient->GetWindowHandle() : nil,
|
|
|
|
|
ExceptionDialogProc, (LPARAM)sStackTraceMsg ) == -1) )
|
|
|
|
|
{
|
|
|
|
|
// The dialog failed, so just fallback to a standard message box
|
|
|
|
|
hsMessageBox( sStackTraceMsg, "UruExplorer Exception", hsMessageBoxNormal );
|
|
|
|
|
}
|
|
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
|
|
|
|
|
{
|
|
|
|
|
// Set global handle
|
|
|
|
|
gHInst = hInst;
|
|
|
|
|
|
|
|
|
|
CCmdParser cmdParser(s_cmdLineArgs, arrsize(s_cmdLineArgs));
|
|
|
|
|
cmdParser.Parse();
|
|
|
|
|
|
|
|
|
|
bool doIntroDialogs = true;
|
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
|
if(cmdParser.IsSpecified(kArgSkipLoginDialog))
|
|
|
|
|
doIntroDialogs = false;
|
|
|
|
|
if(cmdParser.IsSpecified(kArgLocalData))
|
|
|
|
|
gDataServerLocal = true;
|
|
|
|
|
if(cmdParser.IsSpecified(kArgBackgroundDownloader))
|
|
|
|
|
gUseBackgroundDownloader = true;
|
|
|
|
|
#endif
|
|
|
|
|
if(cmdParser.IsSpecified(kArgAuthSrv))
|
|
|
|
|
{
|
|
|
|
|
SetAuthSrvHostname(cmdParser.GetString(kArgAuthSrv));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(cmdParser.IsSpecified(kArgFileSrv))
|
|
|
|
|
{
|
|
|
|
|
SetFileSrvHostname(cmdParser.GetString(kArgFileSrv));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(cmdParser.IsSpecified(kArgGateKeeperSrv))
|
|
|
|
|
{
|
|
|
|
|
SetGateKeeperSrvHostname(cmdParser.GetString(kArgGateKeeperSrv));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check to see if we were launched from the patcher
|
|
|
|
|
bool eventExists = false;
|
|
|
|
|
// we check to see if the event exists that the patcher should have created
|
|
|
|
|
HANDLE hPatcherEvent = CreateEventW(nil, TRUE, FALSE, L"UruPatcherEvent");
|
|
|
|
|
if (hPatcherEvent != NULL)
|
|
|
|
|
{
|
|
|
|
|
// successfully created it, check to see if it was already created
|
|
|
|
|
if (GetLastError() == ERROR_ALREADY_EXISTS)
|
|
|
|
|
{
|
|
|
|
|
// it already existed, so the patcher is waiting, signal it so the patcher can die
|
|
|
|
|
SetEvent(hPatcherEvent);
|
|
|
|
|
eventExists = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TGDoCiderDetection ();
|
|
|
|
|
|
|
|
|
|
#ifdef PLASMA_EXTERNAL_RELEASE
|
|
|
|
|
// if the client was started directly, run the patcher, and shutdown
|
|
|
|
|
STARTUPINFOW si;
|
|
|
|
|
PROCESS_INFORMATION pi;
|
|
|
|
|
ZERO(si);
|
|
|
|
|
ZERO(pi);
|
|
|
|
|
si.cb = sizeof(si);
|
|
|
|
|
wchar cmdLine[MAX_PATH];
|
|
|
|
|
const wchar ** addrs;
|
|
|
|
|
unsigned count;
|
|
|
|
|
|
|
|
|
|
if (!eventExists) // if it is missing, assume patcher wasn't launched
|
|
|
|
|
{
|
|
|
|
|
StrCopy(cmdLine, s_patcherExeName, arrsize(cmdLine));
|
|
|
|
|
count = GetAuthSrvHostnames(&addrs);
|
|
|
|
|
if(count && AuthSrvHostnameOverride())
|
|
|
|
|
StrPrintf(cmdLine, arrsize(cmdLine), L"%ws /AuthSrv=%ws", cmdLine, addrs[0]);
|
|
|
|
|
|
|
|
|
|
count = GetFileSrvHostnames(&addrs);
|
|
|
|
|
if(count && FileSrvHostnameOverride())
|
|
|
|
|
StrPrintf(cmdLine, arrsize(cmdLine), L"%ws /FileSrv=%ws", cmdLine, addrs[0]);
|
|
|
|
|
|
|
|
|
|
count = GetGateKeeperSrvHostnames(&addrs);
|
|
|
|
|
if(count && GateKeeperSrvHostnameOverride())
|
|
|
|
|
StrPrintf(cmdLine, arrsize(cmdLine), L"%ws /GateKeeperSrv=%ws", cmdLine, addrs[0]);
|
|
|
|
|
|
|
|
|
|
if(!CreateProcessW(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
|
|
|
|
|
{
|
|
|
|
|
hsMessageBox("Failed to launch patcher", "Error", hsMessageBoxNormal);
|
|
|
|
|
}
|
|
|
|
|
CloseHandle( pi.hThread );
|
|
|
|
|
CloseHandle( pi.hProcess );
|
|
|
|
|
return PARABLE_NORMAL_EXIT;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
plLocalization::SetDefaultLanguage();
|
|
|
|
|
// If another instance is running, exit. We'll automatically release our
|
|
|
|
|
// lock on the mutex when our process exits
|
|
|
|
|
HANDLE hOneInstance = CreateMutex(nil, FALSE, "UruExplorer");
|
|
|
|
|
if (WaitForSingleObject(hOneInstance,0) != WAIT_OBJECT_0)
|
|
|
|
|
{
|
|
|
|
|
switch (plLocalization::GetLanguage())
|
|
|
|
|
{
|
|
|
|
|
case plLocalization::kFrench:
|
|
|
|
|
hsMessageBox("Une autre copie d'URU est d<EFBFBD>j<EFBFBD> en cours d'ex<EFBFBD>cution", "Erreur", hsMessageBoxNormal);
|
|
|
|
|
break;
|
|
|
|
|
case plLocalization::kGerman:
|
|
|
|
|
hsMessageBox("URU wird bereits in einer anderen Instanz ausgef<EFBFBD>hrt", "Fehler", hsMessageBoxNormal);
|
|
|
|
|
break;
|
|
|
|
|
/* case plLocalization::kSpanish:
|
|
|
|
|
hsMessageBox("En estos momentos se est<EFBFBD> ejecutando otra copia de URU", "Error", hsMessageBoxNormal);
|
|
|
|
|
break;
|
|
|
|
|
case plLocalization::kItalian:
|
|
|
|
|
hsMessageBox("Un'altra copia di URU <EFBFBD> gi<EFBFBD> aperta", "Errore", hsMessageBoxNormal);
|
|
|
|
|
break;
|
|
|
|
|
*/ // default is English
|
|
|
|
|
default:
|
|
|
|
|
hsMessageBox("Another copy of URU is already running", "Error", hsMessageBoxNormal);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return PARABLE_NORMAL_EXIT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IsExpired())
|
|
|
|
|
{
|
|
|
|
|
hsMessageBox("This client is over 30 days old. You need to get a new one.", "Error", hsMessageBoxNormal);
|
|
|
|
|
return PARABLE_NORMAL_EXIT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetCliAuthAutoReconnectEnable(false);
|
|
|
|
|
|
|
|
|
|
NetCommSetReadIniAccountInfo(!doIntroDialogs);
|
|
|
|
|
InitNetClientComm();
|
|
|
|
|
|
|
|
|
|
wchar acctName[kMaxAccountNameLength];
|
|
|
|
|
|
|
|
|
|
// if we're being launched from gametap then don't use the intro dialogs
|
|
|
|
|
if (StrStrI(lpCmdLine, "screenname=")) {
|
|
|
|
|
doIntroDialogs = false;
|
|
|
|
|
|
|
|
|
|
wchar authToken[kMaxPublisherAuthKeyLength];
|
|
|
|
|
wchar os[kMaxGTOSIdLength];
|
|
|
|
|
ShaDigest emptyDigest;
|
|
|
|
|
|
|
|
|
|
MemSet(acctName, 0, sizeof(acctName));
|
|
|
|
|
MemSet(authToken, 0, sizeof(authToken));
|
|
|
|
|
MemSet(os, 0, sizeof(os));
|
|
|
|
|
|
|
|
|
|
const char* temp = lpCmdLine;
|
|
|
|
|
char token[128];
|
|
|
|
|
while (StrTokenize(&temp, token, arrsize(token), " =")) {
|
|
|
|
|
if (StrCmpI(token, "screenname") == 0) {
|
|
|
|
|
if (!StrTokenize(&temp, token, arrsize(token), " ="))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
StrToUnicode(acctName, token, arrsize(acctName));
|
|
|
|
|
}
|
|
|
|
|
else if (StrCmpI(token, "authtoken") == 0) {
|
|
|
|
|
if (!StrTokenize(&temp, token, arrsize(token), " ="))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
StrToUnicode(authToken, token, arrsize(authToken));
|
|
|
|
|
}
|
|
|
|
|
else if (StrCmpI(token, "os") == 0) {
|
|
|
|
|
if (!StrTokenize(&temp, token, arrsize(token), " ="))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
StrToUnicode(os, token, arrsize(os));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetCommSetAccountUsernamePassword(acctName, emptyDigest);
|
|
|
|
|
NetCommSetAuthTokenAndOS(authToken, os);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool needExit = false;
|
|
|
|
|
LoginDialogParam loginParam;
|
|
|
|
|
MemSet(&loginParam, 0, sizeof(loginParam));
|
|
|
|
|
|
|
|
|
|
if (!doIntroDialogs) {
|
|
|
|
|
ENetError auth;
|
|
|
|
|
|
|
|
|
|
bool cancelled = AuthenticateNetClientComm(&auth, NULL);
|
|
|
|
|
|
|
|
|
|
if (IS_NET_ERROR(auth) || cancelled) {
|
|
|
|
|
doIntroDialogs = true;
|
|
|
|
|
|
|
|
|
|
loginParam.fromGT = true;
|
|
|
|
|
loginParam.authError = auth;
|
|
|
|
|
StrCopy(loginParam.accountName, acctName, arrsize(loginParam.accountName));
|
|
|
|
|
|
|
|
|
|
if (cancelled)
|
|
|
|
|
{
|
|
|
|
|
NetCommDisconnect();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (doIntroDialogs) {
|
|
|
|
|
if (TGIsCider)
|
|
|
|
|
needExit = !TGRunLoginDialog (loginParam.accountName, loginParam.fromGT);
|
|
|
|
|
else if (::DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_URULOGIN_MAIN ), NULL, UruLoginDialogProc, (LPARAM)&loginParam ) <= 0)
|
|
|
|
|
needExit = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (doIntroDialogs && !needExit) {
|
|
|
|
|
if (TGIsCider)
|
|
|
|
|
needExit = !TGRunTOSDialog ();
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
HINSTANCE hRichEdDll = LoadLibrary("RICHED20.DLL");
|
|
|
|
|
INT_PTR val = ::DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_URULOGIN_EULA ), NULL, UruTOSDialogProc, (LPARAM)hInst);
|
|
|
|
|
FreeLibrary(hRichEdDll);
|
|
|
|
|
if (val <= 0) {
|
|
|
|
|
DWORD error = GetLastError();
|
|
|
|
|
needExit = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (needExit) {
|
|
|
|
|
DeInitNetClientComm();
|
|
|
|
|
return PARABLE_NORMAL_EXIT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetCliAuthAutoReconnectEnable(true);
|
|
|
|
|
|
|
|
|
|
// VERY VERY FIRST--throw up our splash screen
|
|
|
|
|
HWND splashDialog = ::CreateDialog( hInst, MAKEINTRESOURCE( IDD_LOADING ), NULL, SplashDialogProc );
|
|
|
|
|
|
|
|
|
|
// Install our unhandled exception filter for trapping all those nasty crashes in release build
|
|
|
|
|
#ifndef HS_DEBUGGING
|
|
|
|
|
LPTOP_LEVEL_EXCEPTION_FILTER oldFilter;
|
|
|
|
|
oldFilter = SetUnhandledExceptionFilter( plCustomUnhandledExceptionFilter );
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Set up to log errors by using hsDebugMessage
|
|
|
|
|
//
|
|
|
|
|
gDebugFile = NULL;
|
|
|
|
|
if ( !plStatusLog::fLoggingOff )
|
|
|
|
|
{
|
|
|
|
|
wchar fileAndPath[MAX_PATH];
|
|
|
|
|
PathGetLogDirectory(fileAndPath, arrsize(fileAndPath));
|
|
|
|
|
PathAddFilename(fileAndPath, fileAndPath, L"plasmalog.txt", arrsize(fileAndPath));
|
|
|
|
|
gDebugFile = _wfopen(fileAndPath, L"wt");
|
|
|
|
|
hsAssert(gDebugFile != NULL, "Error creating debug file plasmalog.txt");
|
|
|
|
|
hsSetDebugMessageProc(DebugMessageProc);
|
|
|
|
|
if (gDebugFile != NULL)
|
|
|
|
|
{
|
|
|
|
|
char prdName[256];
|
|
|
|
|
wchar prdString[256];
|
|
|
|
|
ProductString(prdString, arrsize(prdString));
|
|
|
|
|
StrToAnsi(prdName, prdString, arrsize(prdName));
|
|
|
|
|
fprintf(gDebugFile, "%s\n", prdName);
|
|
|
|
|
fflush(gDebugFile);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// log stackdump.log text if the log exists
|
|
|
|
|
char stackDumpText[1024];
|
|
|
|
|
wchar stackDumpTextW[1024];
|
|
|
|
|
memset(stackDumpText, 0, arrsize(stackDumpText));
|
|
|
|
|
memset(stackDumpTextW, 0, arrsize(stackDumpTextW) * sizeof(wchar));
|
|
|
|
|
wchar fileAndPath[MAX_PATH];
|
|
|
|
|
PathGetLogDirectory(fileAndPath, arrsize(fileAndPath));
|
|
|
|
|
PathAddFilename(fileAndPath, fileAndPath, L"stackDump.log", arrsize(fileAndPath));
|
|
|
|
|
FILE *stackDumpLog = _wfopen(fileAndPath, L"r");
|
|
|
|
|
if(stackDumpLog)
|
|
|
|
|
{
|
|
|
|
|
fread(stackDumpText, 1, arrsize(stackDumpText) - 1, stackDumpLog);
|
|
|
|
|
StrToUnicode(stackDumpTextW, stackDumpText, arrsize(stackDumpText));
|
|
|
|
|
NetCliAuthLogStackDump (stackDumpTextW);
|
|
|
|
|
fclose(stackDumpLog);
|
|
|
|
|
plFileUtils::RemoveFile(fileAndPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
// Create Window
|
|
|
|
|
if (!WinInit(hInst, nCmdShow) || gClient->GetDone())
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// We don't have multiplayer localized assets for Italian or Spanish, so force them to English in that case.
|
|
|
|
|
/* if (!plNetClientMgr::GetInstance()->InOfflineMode() &&
|
|
|
|
|
(plLocalization::GetLanguage() == plLocalization::kItalian ||
|
|
|
|
|
plLocalization::GetLanguage() == plLocalization::kSpanish))
|
|
|
|
|
{
|
|
|
|
|
plLocalization::SetLanguage(plLocalization::kEnglish);
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// Done with our splash now
|
|
|
|
|
::DestroyWindow( splashDialog );
|
|
|
|
|
|
|
|
|
|
if (!gClient)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// Show the main window
|
|
|
|
|
ShowWindow(gClient->GetWindowHandle(), SW_SHOW);
|
|
|
|
|
|
|
|
|
|
gHasMouse = GetSystemMetrics(SM_MOUSEPRESENT);
|
|
|
|
|
|
|
|
|
|
// Be really REALLY forceful about being in the front
|
|
|
|
|
BringWindowToTop( gClient->GetWindowHandle() );
|
|
|
|
|
|
|
|
|
|
// Update the window
|
|
|
|
|
UpdateWindow(gClient->GetWindowHandle());
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Init Application here
|
|
|
|
|
//
|
|
|
|
|
if( !gClient->StartInit() )
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// I want it on top! I mean it!
|
|
|
|
|
BringWindowToTop( gClient->GetWindowHandle() );
|
|
|
|
|
|
|
|
|
|
// initialize dinput here:
|
|
|
|
|
if (gClient && gClient->GetInputManager())
|
|
|
|
|
gClient->GetInputManager()->InitDInput(hInst, (HWND)gClient->GetWindowHandle());
|
|
|
|
|
|
|
|
|
|
// Seriously!
|
|
|
|
|
BringWindowToTop( gClient->GetWindowHandle() );
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Main loop
|
|
|
|
|
//
|
|
|
|
|
MSG msg;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
gClient->MainLoop();
|
|
|
|
|
|
|
|
|
|
if( gClient->GetDone() )
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// Look for a message
|
|
|
|
|
while (PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ))
|
|
|
|
|
{
|
|
|
|
|
// Handle the message
|
|
|
|
|
TranslateMessage( &msg );
|
|
|
|
|
DispatchMessage( &msg );
|
|
|
|
|
}
|
|
|
|
|
} while (WM_QUIT != msg.message);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifndef _DEBUG
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
#endif
|
|
|
|
|
//
|
|
|
|
|
// Cleanup
|
|
|
|
|
//
|
|
|
|
|
if (gClient)
|
|
|
|
|
{
|
|
|
|
|
gClient->Shutdown(); // shuts down PhysX for us
|
|
|
|
|
gClient = nil;
|
|
|
|
|
}
|
|
|
|
|
hsAssert(hsgResMgr::ResMgr()->RefCnt()==1, "resMgr has too many refs, expect mem leaks");
|
|
|
|
|
hsgResMgr::Shutdown(); // deletes fResMgr
|
|
|
|
|
DeInitNetClientComm();
|
|
|
|
|
#ifndef _DEBUG
|
|
|
|
|
} catch (...)
|
|
|
|
|
{
|
|
|
|
|
// just catch all the crashes on exit... just to keep GameTap from complaining
|
|
|
|
|
if (gDebugFile)
|
|
|
|
|
fprintf(gDebugFile, "Crashed on shutdown.\n");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (gDebugFile)
|
|
|
|
|
fclose(gDebugFile);
|
|
|
|
|
|
|
|
|
|
// Uninstall our unhandled exception filter, if we installed one
|
|
|
|
|
#ifndef HS_DEBUGGING
|
|
|
|
|
SetUnhandledExceptionFilter( oldFilter );
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Exit WinMain and terminate the app....
|
|
|
|
|
// return msg.wParam;
|
|
|
|
|
return PARABLE_NORMAL_EXIT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IsExpired()
|
|
|
|
|
{
|
|
|
|
|
bool expired = false;
|
|
|
|
|
|
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
|
char ourPath[MAX_PATH];
|
|
|
|
|
GetModuleFileName(NULL, ourPath, sizeof(ourPath));
|
|
|
|
|
DWORD ok = 0;
|
|
|
|
|
DWORD size = GetFileVersionInfoSize(ourPath, &ok);
|
|
|
|
|
if (size > 0)
|
|
|
|
|
{
|
|
|
|
|
void* data = TRACKED_NEW UInt8[size];
|
|
|
|
|
GetFileVersionInfo(ourPath, ok, size, data);
|
|
|
|
|
|
|
|
|
|
unsigned int descLen = 0;
|
|
|
|
|
void* desc = nil;
|
|
|
|
|
if (VerQueryValue(data, "\\StringFileInfo\\040904B0\\FileDescription", &desc, &descLen))
|
|
|
|
|
{
|
|
|
|
|
const char* buildDateStart = strstr((const char*)desc, " - Built ");
|
|
|
|
|
if (buildDateStart)
|
|
|
|
|
{
|
|
|
|
|
buildDateStart += strlen(" - Built ");
|
|
|
|
|
const char* buildDateEnd = strstr(buildDateStart, " at");
|
|
|
|
|
if (buildDateEnd)
|
|
|
|
|
{
|
|
|
|
|
int len = buildDateEnd-buildDateStart;
|
|
|
|
|
|
|
|
|
|
char buf[32];
|
|
|
|
|
strncpy(buf, buildDateStart, len);
|
|
|
|
|
buf[len] = '\0';
|
|
|
|
|
|
|
|
|
|
int month = atoi(strtok(buf, "/"));
|
|
|
|
|
int day = atoi(strtok(nil, "/"));
|
|
|
|
|
int year = atoi(strtok(nil, "/"));
|
|
|
|
|
|
|
|
|
|
SYSTEMTIME curTime, buildTime;
|
|
|
|
|
GetLocalTime(&buildTime);
|
|
|
|
|
GetLocalTime(&curTime);
|
|
|
|
|
buildTime.wDay = day;
|
|
|
|
|
buildTime.wMonth = month;
|
|
|
|
|
buildTime.wYear = year;
|
|
|
|
|
|
|
|
|
|
ULARGE_INTEGER iCurTime, iBuildTime;
|
|
|
|
|
FILETIME ft;
|
|
|
|
|
|
|
|
|
|
SystemTimeToFileTime(&curTime, &ft);
|
|
|
|
|
iCurTime.LowPart = ft.dwLowDateTime;
|
|
|
|
|
iCurTime.HighPart = ft.dwHighDateTime;
|
|
|
|
|
|
|
|
|
|
SystemTimeToFileTime(&buildTime, &ft);
|
|
|
|
|
iBuildTime.LowPart = ft.dwLowDateTime;
|
|
|
|
|
iBuildTime.HighPart = ft.dwHighDateTime;
|
|
|
|
|
|
|
|
|
|
int secsOld = (int)((iCurTime.QuadPart - iBuildTime.QuadPart) / 10000000);
|
|
|
|
|
int daysOld = secsOld / (60 * 60 * 24);
|
|
|
|
|
|
|
|
|
|
if (daysOld > 30)
|
|
|
|
|
expired = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete [] data;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return expired;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GetCryptKey(UInt32* cryptKey, unsigned numElements)
|
|
|
|
|
{
|
|
|
|
|
char volName[] = "C:\\";
|
|
|
|
|
int index = 0;
|
|
|
|
|
DWORD logicalDrives = GetLogicalDrives();
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 32; ++i)
|
|
|
|
|
{
|
|
|
|
|
if (logicalDrives & (1 << i))
|
|
|
|
|
{
|
|
|
|
|
volName[0] = ('C' + i);
|
|
|
|
|
|
|
|
|
|
DWORD volSerialNum = 0;
|
|
|
|
|
BOOL result = GetVolumeInformation(
|
|
|
|
|
volName, //LPCTSTR lpRootPathName,
|
|
|
|
|
NULL, //LPTSTR lpVolumeNameBuffer,
|
|
|
|
|
0, //DWORD nVolumeNameSize,
|
|
|
|
|
&volSerialNum, //LPDWORD lpVolumeSerialNumber,
|
|
|
|
|
NULL, //LPDWORD lpMaximumComponentLength,
|
|
|
|
|
NULL, //LPDWORD lpFileSystemFlags,
|
|
|
|
|
NULL, //LPTSTR lpFileSystemNameBuffer,
|
|
|
|
|
0 //DWORD nFileSystemNameSize
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
cryptKey[index] = (cryptKey[index] ^ volSerialNum);
|
|
|
|
|
|
|
|
|
|
index = (++index) % numElements;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|