Branan Purvine-Riley
11 years ago
20 changed files with 1164 additions and 2794 deletions
@ -1,28 +0,0 @@ |
|||||||
add_definitions(-D_LIB) |
|
||||||
|
|
||||||
include_directories("../../CoreLib") |
|
||||||
include_directories("../../NucleusLib/inc") |
|
||||||
include_directories("../../NucleusLib") |
|
||||||
include_directories("../../PubUtilLib") |
|
||||||
|
|
||||||
include_directories(${OPENSSL_INCLUDE_DIR}) |
|
||||||
|
|
||||||
set(plClientPatcher_HEADERS |
|
||||||
Intern.h |
|
||||||
Pch.h |
|
||||||
UruPlayer.h |
|
||||||
) |
|
||||||
|
|
||||||
set(plClientPatcher_SOURCES |
|
||||||
UruPlayer.cpp |
|
||||||
) |
|
||||||
|
|
||||||
add_library(plClientPatcher STATIC ${plClientPatcher_HEADERS} ${plClientPatcher_SOURCES}) |
|
||||||
target_link_libraries(plClientPatcher CoreLib plAudioCore plStatusLog) |
|
||||||
|
|
||||||
if(USE_VLD) |
|
||||||
target_link_libraries(plClientPatcher ${VLD_LIBRARY}) |
|
||||||
endif() |
|
||||||
|
|
||||||
source_group("Header Files" FILES ${plClientPatcher_HEADERS}) |
|
||||||
source_group("Source Files" FILES ${plClientPatcher_SOURCES}) |
|
@ -1,58 +0,0 @@ |
|||||||
/*==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==*/ |
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* $/Plasma20/Sources/Plasma/Apps/plClientPatcher/Intern.h |
|
||||||
*
|
|
||||||
***/ |
|
||||||
|
|
||||||
#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_INTERN_H |
|
||||||
#error "Header $/Plasma20/Sources/Plasma/Apps/plClientPatcher/Intern.h included more than once" |
|
||||||
#endif |
|
||||||
#define PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_INTERN_H |
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* SelfPatcher.cpp |
|
||||||
* |
|
||||||
***/ |
|
@ -1,75 +0,0 @@ |
|||||||
/*==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==*/ |
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* $/Plasma20/Sources/Plasma/Apps/plClientPatcher/Pch.h |
|
||||||
*
|
|
||||||
***/ |
|
||||||
|
|
||||||
#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_PCH_H |
|
||||||
#error "Header $/Plasma20/Sources/Plasma/Apps/plClientPatcher/Pch.h included more than once" |
|
||||||
#endif |
|
||||||
#define PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_PCH_H |
|
||||||
|
|
||||||
#include "hsWindows.h" |
|
||||||
#include <process.h> |
|
||||||
#include <ctime> |
|
||||||
#include "pnUtils/pnUtils.h" |
|
||||||
#include "pnNetBase/pnNetBase.h" |
|
||||||
#include "pnAsyncCore/pnAsyncCore.h" |
|
||||||
#include "plProduct.h" |
|
||||||
#include "pnNetCli/pnNetCli.h" |
|
||||||
#include "plNetGameLib/plNetGameLib.h" |
|
||||||
#include "pnEncryption/plChecksum.h" |
|
||||||
#include "plAgeDescription/plAgeManifest.h" |
|
||||||
#include "plAudioCore/plAudioFileReader.h" |
|
||||||
|
|
||||||
#define USES_PROTOCOL_CLI2AUTH |
|
||||||
#include "pnNetProtocol/pnNetProtocol.h" |
|
||||||
|
|
||||||
#include "UruPlayer.h" |
|
||||||
|
|
||||||
#include "plCompression/plZlibStream.h" |
|
||||||
#include "Intern.h" |
|
||||||
#include "../plUruLauncher/plLauncherInfo.h" |
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -1,67 +0,0 @@ |
|||||||
/*==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==*/ |
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* $/Plasma20/Sources/Plasma/Apps/plClientPatcher/UruPlayer.h |
|
||||||
*
|
|
||||||
***/ |
|
||||||
|
|
||||||
#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_URUPLAYER_H |
|
||||||
#error "Header $/Plasma20/Sources/Plasma/Apps/plClientPatcher/UruPlayer.h included more than once" |
|
||||||
#endif |
|
||||||
#define PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_URUPLAYER_H |
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* UruPlayer.cpp |
|
||||||
* |
|
||||||
***/ |
|
||||||
|
|
||||||
void InitAsyncCore (); |
|
||||||
void ShutdownAsyncCore () ; |
|
||||||
void UruPrepProc (void * param); |
|
||||||
void UruStartProc (void * param); |
|
||||||
void PlayerTerminateProc (void * param); |
|
||||||
void PlayerStopProc (void * param); |
|
||||||
|
|
||||||
extern plFileName kPatcherExeFilename; |
|
@ -1,65 +0,0 @@ |
|||||||
/*==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==*/ |
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/plLauncherCallback.h |
|
||||||
*
|
|
||||||
***/ |
|
||||||
|
|
||||||
#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PLLAUNCHERCALLBACK_H |
|
||||||
#error "Header $/Plasma20/Sources/Plasma/Apps/plUruLauncher/plLauncherCallback.h included more than once" |
|
||||||
#endif |
|
||||||
#define PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PLLAUNCHERCALLBACK_H |
|
||||||
|
|
||||||
enum EStatus { |
|
||||||
kStatusOk, |
|
||||||
}; |
|
||||||
|
|
||||||
typedef void (*launcherCallback)(int status, void *param); |
|
||||||
struct plLauncherCallback { |
|
||||||
launcherCallback prepCallback; |
|
||||||
launcherCallback initCallback; |
|
||||||
launcherCallback startCallback; |
|
||||||
launcherCallback stopCallback; |
|
||||||
launcherCallback terminateCallback; |
|
||||||
|
|
||||||
}; |
|
@ -1,61 +0,0 @@ |
|||||||
/*==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==*/ |
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/Intern.h |
|
||||||
*
|
|
||||||
***/ |
|
||||||
|
|
||||||
#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_INTERN_H |
|
||||||
#error "Header $/Plasma20/Sources/Plasma/Apps/plUruLauncher/Intern.h included more than once" |
|
||||||
#endif |
|
||||||
#define PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_INTERN_H |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* SelfPatcher.cpp |
|
||||||
* |
|
||||||
***/ |
|
||||||
bool SelfPatch (bool noSelfPatch, bool * abort, ENetError * result, plLauncherInfo *info); |
|
||||||
void SetReturnCode (DWORD retCode); |
|
@ -1,884 +0,0 @@ |
|||||||
/*==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==*/ |
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/Main.cpp |
|
||||||
*
|
|
||||||
***/ |
|
||||||
|
|
||||||
#include "Pch.h" |
|
||||||
#include "hsThread.h" |
|
||||||
#include <algorithm> |
|
||||||
#pragma hdrstop |
|
||||||
|
|
||||||
|
|
||||||
#include "resource.h" |
|
||||||
#include <commctrl.h> |
|
||||||
#define WIN32_LEAN_AND_MEAN |
|
||||||
#define WHITESPACE L" \"\t\r\n\x1A" |
|
||||||
#define UPDATE_STATUSMSG_SECONDS 30 // Must be an int
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* Private |
|
||||||
* |
|
||||||
***/ |
|
||||||
|
|
||||||
enum ELogSev { |
|
||||||
kLogInfo, |
|
||||||
kLogErr, |
|
||||||
kNumLogSev |
|
||||||
}; |
|
||||||
|
|
||||||
enum { |
|
||||||
kEventTimer = 1, |
|
||||||
}; |
|
||||||
|
|
||||||
enum EEventType { |
|
||||||
kEventSetProgress, |
|
||||||
kEventSetText, |
|
||||||
kEventSetStatusText, |
|
||||||
kEventSetTimeRemaining, |
|
||||||
kEventSetBytesRemaining, |
|
||||||
}; |
|
||||||
|
|
||||||
// base window event
|
|
||||||
struct WndEvent { |
|
||||||
LINK(WndEvent) link; |
|
||||||
EEventType type; |
|
||||||
}; |
|
||||||
|
|
||||||
struct SetProgressEvent : WndEvent { |
|
||||||
int progress; |
|
||||||
}; |
|
||||||
|
|
||||||
struct SetTextEvent : WndEvent { |
|
||||||
char text[MAX_PATH]; |
|
||||||
}; |
|
||||||
|
|
||||||
struct SetStatusTextEvent : WndEvent { |
|
||||||
char text[MAX_PATH]; |
|
||||||
}; |
|
||||||
|
|
||||||
struct SetTimeRemainingEvent : WndEvent { |
|
||||||
unsigned seconds; |
|
||||||
}; |
|
||||||
|
|
||||||
struct SetBytesRemainingEvent : WndEvent { |
|
||||||
unsigned bytes; |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* Private data |
|
||||||
* |
|
||||||
***/ |
|
||||||
|
|
||||||
static bool s_shutdown; |
|
||||||
static bool s_prepared; |
|
||||||
static int s_retCode = 1; |
|
||||||
static long s_terminationIssued; |
|
||||||
static bool s_terminated;
|
|
||||||
static plLauncherInfo s_launcherInfo; |
|
||||||
static HANDLE s_thread; |
|
||||||
static HANDLE s_event; |
|
||||||
static HINSTANCE s_hInstance; |
|
||||||
static HWND s_dialog; |
|
||||||
static hsSemaphore s_dialogCreateEvent(0); |
|
||||||
static hsMutex s_critsect; |
|
||||||
static LISTDECL(WndEvent, link) s_eventQ; |
|
||||||
static hsSemaphore s_shutdownEvent(0); |
|
||||||
static plFileName s_workingDir; |
|
||||||
static hsSemaphore s_statusEvent(0); |
|
||||||
static char s_curlError[CURL_ERROR_SIZE]; |
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* Local functions |
|
||||||
* |
|
||||||
***/ |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void Abort () { |
|
||||||
s_retCode = 0; |
|
||||||
s_shutdown = true; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void PostEvent (WndEvent *event) { |
|
||||||
s_critsect.Lock(); |
|
||||||
s_eventQ.Link(event); |
|
||||||
s_critsect.Unlock(); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void LogV (ELogSev sev, const wchar_t fmt[], va_list args) { |
|
||||||
static struct { FILE * file; const wchar_t * pre; } s_log[] = { |
|
||||||
{ stdout, L"Inf" }, |
|
||||||
{ stderr, L"Err" }, |
|
||||||
}; |
|
||||||
static_assert(arrsize(s_log) == kNumLogSev, "Log severity array and enum have different sizes"); |
|
||||||
|
|
||||||
fwprintf (s_log[sev].file, L"%s: ", s_log[sev].pre); |
|
||||||
vfwprintf(s_log[sev].file, fmt, args); |
|
||||||
fwprintf (s_log[sev].file, L"\n"); |
|
||||||
|
|
||||||
if (sev >= kLogErr) |
|
||||||
Abort(); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void Log (ELogSev sev, const wchar_t fmt[], ...) { |
|
||||||
va_list args; |
|
||||||
va_start(args, fmt); |
|
||||||
LogV(sev, fmt, args); |
|
||||||
va_end(args); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
// NOTE: Must use LocalFree() on the return value of this function when finished with the string
|
|
||||||
static wchar_t *TranslateErrorCode(DWORD errorCode) { |
|
||||||
LPVOID lpMsgBuf; |
|
||||||
|
|
||||||
FormatMessageW( |
|
||||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
||||||
FORMAT_MESSAGE_FROM_SYSTEM, |
|
||||||
NULL, |
|
||||||
errorCode, |
|
||||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
|
||||||
(wchar_t *) &lpMsgBuf, |
|
||||||
0,
|
|
||||||
NULL
|
|
||||||
); |
|
||||||
return (wchar_t *)lpMsgBuf; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static BOOL WINAPI CtrlHandler (DWORD) { |
|
||||||
static unsigned s_ctrlCount; |
|
||||||
if (++s_ctrlCount == 3) |
|
||||||
_exit(1); // exit process immediately upon 3rd Ctrl-C.
|
|
||||||
Abort(); |
|
||||||
return TRUE; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void PrepareGame () { |
|
||||||
SetText("Connecting to server..."); |
|
||||||
(void)_beginthread(UruPrepProc, 0, (void *) &s_launcherInfo);
|
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void InitGame () { |
|
||||||
s_launcherInfo.initCallback(kStatusOk, nil); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void StartGame () { |
|
||||||
(void)_beginthread(UruStartProc, 0, (void *) &s_launcherInfo); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void StopGame () { |
|
||||||
(void)_beginthread(PlayerStopProc, 0, (void *) &s_launcherInfo); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void TerminateGame () { |
|
||||||
if (!AtomicSet(&s_terminationIssued, 1)) |
|
||||||
_beginthread(PlayerTerminateProc, 0, (void *) &s_launcherInfo); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void Recv_SetProgress (HWND hwnd, const SetProgressEvent &event) {
|
|
||||||
SendMessage(GetDlgItem(s_dialog, IDC_PROGRESS), PBM_SETPOS, event.progress, NULL); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void Recv_SetText (HWND hwnd, const SetTextEvent &event) {
|
|
||||||
bool b = SendMessage(GetDlgItem(s_dialog, IDC_TEXT), WM_SETTEXT, 0, (LPARAM) event.text); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void Recv_SetStatusText (HWND hwnd, const SetStatusTextEvent &event) { |
|
||||||
bool b = SendMessage(GetDlgItem(s_dialog, IDC_STATUS_TEXT), WM_SETTEXT, 0, (LPARAM) event.text); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void Recv_SetTimeRemaining (HWND hwnd, const SetTimeRemainingEvent &event) { |
|
||||||
unsigned days; |
|
||||||
unsigned hours; |
|
||||||
unsigned minutes; |
|
||||||
unsigned seconds; |
|
||||||
|
|
||||||
if(event.seconds == 0xffffffff) |
|
||||||
{ |
|
||||||
SendMessage(GetDlgItem(s_dialog, IDC_TIMEREMAINING), WM_SETTEXT, 0, (LPARAM) "estimating..."); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
seconds = event.seconds; |
|
||||||
|
|
||||||
days = seconds / (60 * 60 * 24); |
|
||||||
seconds -= (days * 60 * 60 * 24); |
|
||||||
hours = seconds / (60 * 60); |
|
||||||
seconds -= hours * 60 * 60; |
|
||||||
minutes = seconds / 60; |
|
||||||
seconds -= minutes * 60; |
|
||||||
seconds = seconds; |
|
||||||
|
|
||||||
char text[64] = {0}; |
|
||||||
if(days) |
|
||||||
{ |
|
||||||
if(days > 1) |
|
||||||
StrPrintf(text, arrsize(text), "%d days ", days); |
|
||||||
else |
|
||||||
StrPrintf(text, arrsize(text), "%d day ", days); |
|
||||||
} |
|
||||||
if(hours) |
|
||||||
{ |
|
||||||
if(hours > 1) |
|
||||||
StrPrintf(text, arrsize(text), "%s%d hours ", text, hours); |
|
||||||
else |
|
||||||
StrPrintf(text, arrsize(text), "%s%d hour ", text, hours); |
|
||||||
} |
|
||||||
if(minutes) |
|
||||||
StrPrintf(text, arrsize(text), "%s%d min ", text, minutes); |
|
||||||
if( seconds || !text[0]) |
|
||||||
StrPrintf(text, arrsize(text), "%s%d sec", text, seconds); |
|
||||||
bool b = SendMessage(GetDlgItem(s_dialog, IDC_TIMEREMAINING), WM_SETTEXT, 0, (LPARAM) text); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void Recv_SetBytesRemaining (HWND hwnd, const SetBytesRemainingEvent &event) { |
|
||||||
char text[32]; |
|
||||||
unsigned MB; |
|
||||||
unsigned decimal; |
|
||||||
unsigned bytes = event.bytes; |
|
||||||
|
|
||||||
unsigned GB = bytes / 1000000000; |
|
||||||
if(GB) |
|
||||||
{ |
|
||||||
bytes -= GB * 1000000000; |
|
||||||
decimal = bytes / 100000000; // to two decimal places
|
|
||||||
StrPrintf(text, arrsize(text), "%d.%d GB", GB, decimal); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
MB = bytes / 1000000; |
|
||||||
bytes -= MB * 1000000; |
|
||||||
decimal = bytes / 100000; // to one decimal place
|
|
||||||
StrPrintf(text, arrsize(text), "%d.%d MB", MB, decimal); |
|
||||||
} |
|
||||||
bool b = SendMessage(GetDlgItem(s_dialog, IDC_BYTESREMAINING), WM_SETTEXT, 0, (LPARAM) text); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void DispatchEvents (HWND hwnd) { |
|
||||||
LISTDECL(WndEvent, link) eventQ; |
|
||||||
|
|
||||||
s_critsect.Lock(); |
|
||||||
{
|
|
||||||
eventQ.Link(&s_eventQ); |
|
||||||
} |
|
||||||
s_critsect.Unlock(); |
|
||||||
|
|
||||||
#define DISPATCH(a) case kEvent##a: Recv_##a(hwnd, *(const a##Event *) event); break |
|
||||||
while (WndEvent *event = eventQ.Head()) {
|
|
||||||
switch (event->type) { |
|
||||||
DISPATCH(SetProgress); |
|
||||||
DISPATCH(SetText); |
|
||||||
DISPATCH(SetStatusText); |
|
||||||
DISPATCH(SetTimeRemaining); |
|
||||||
DISPATCH(SetBytesRemaining); |
|
||||||
DEFAULT_FATAL(event->type); |
|
||||||
} |
|
||||||
delete event; // unlinks from list
|
|
||||||
} |
|
||||||
#undef DISPATCH |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void OnTimer(HWND hwnd, unsigned int timerId) { |
|
||||||
if(s_shutdown) return; |
|
||||||
switch (timerId) { |
|
||||||
case kEventTimer: |
|
||||||
DispatchEvents(hwnd); |
|
||||||
break; |
|
||||||
|
|
||||||
DEFAULT_FATAL(timerId); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//===========================================================================
|
|
||||||
static void MessagePump (HWND hwnd) { |
|
||||||
for (;;) { |
|
||||||
// wait for a message or the shutdown event
|
|
||||||
const DWORD result = MsgWaitForMultipleObjects( |
|
||||||
1, |
|
||||||
&s_event, |
|
||||||
false, |
|
||||||
INFINITE, |
|
||||||
QS_ALLEVENTS |
|
||||||
); |
|
||||||
if (result == WAIT_OBJECT_0) |
|
||||||
return; |
|
||||||
|
|
||||||
// process windows messages
|
|
||||||
MSG msg; |
|
||||||
|
|
||||||
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { |
|
||||||
if (!IsDialogMessage(s_dialog, &msg)) { |
|
||||||
TranslateMessage(&msg); |
|
||||||
DispatchMessage(&msg); |
|
||||||
} |
|
||||||
if (msg.message == WM_QUIT) { |
|
||||||
return; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
BOOL CALLBACK SplashDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) |
|
||||||
{ |
|
||||||
switch( uMsg ) |
|
||||||
{ |
|
||||||
case WM_INITDIALOG:
|
|
||||||
{ |
|
||||||
PostMessage( GetDlgItem(hwndDlg, IDC_PROGRESS), PBM_SETRANGE, 0, MAKELPARAM(0, 1000));
|
|
||||||
} |
|
||||||
break;
|
|
||||||
|
|
||||||
case WM_COMMAND: |
|
||||||
if(HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL) { |
|
||||||
// we dont shutdown the window here, but instead let the patcher know it needs to shutdown, and display our shutting down message.
|
|
||||||
// setting s_shutdown also wont allow any more Set text messages.
|
|
||||||
if(!s_shutdown) |
|
||||||
{ |
|
||||||
s_shutdown = true; |
|
||||||
SendMessage(GetDlgItem(s_dialog, IDC_TEXT), WM_SETTEXT, 0, (LPARAM) "Shutting Down..."); |
|
||||||
EnableWindow(GetDlgItem(s_dialog, IDCANCEL), false); |
|
||||||
} |
|
||||||
} |
|
||||||
break; |
|
||||||
|
|
||||||
case WM_KEYDOWN: |
|
||||||
break; |
|
||||||
|
|
||||||
case WM_NCHITTEST: |
|
||||||
SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, (LONG_PTR)HTCAPTION); |
|
||||||
return TRUE; |
|
||||||
|
|
||||||
case WM_TIMER: |
|
||||||
OnTimer(hwndDlg, wParam); |
|
||||||
break; |
|
||||||
|
|
||||||
case WM_QUIT: |
|
||||||
::DestroyWindow(hwndDlg); |
|
||||||
break; |
|
||||||
|
|
||||||
case WM_DESTROY: |
|
||||||
PostQuitMessage(0); |
|
||||||
break; |
|
||||||
|
|
||||||
default: |
|
||||||
return DefWindowProc(hwndDlg, uMsg, wParam, lParam); |
|
||||||
} |
|
||||||
return TRUE; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void WindowThreadProc(void *) { |
|
||||||
#ifdef USE_VLD |
|
||||||
VLDEnable(); |
|
||||||
#endif |
|
||||||
|
|
||||||
InitCommonControls(); |
|
||||||
s_event = CreateEvent( |
|
||||||
(LPSECURITY_ATTRIBUTES) 0, |
|
||||||
false, // auto reset
|
|
||||||
false, // initial state off
|
|
||||||
(LPCTSTR) 0 // name
|
|
||||||
); |
|
||||||
|
|
||||||
s_dialog = ::CreateDialog( s_hInstance, MAKEINTRESOURCE( IDD_DIALOG ), NULL, SplashDialogProc ); |
|
||||||
SetWindowText(s_dialog, "URU Launcher"); |
|
||||||
|
|
||||||
|
|
||||||
::SetDlgItemText( s_dialog, IDC_TEXT, "Initializing patcher..."); |
|
||||||
SetTimer(s_dialog, kEventTimer, 250, 0); |
|
||||||
|
|
||||||
SendMessage(GetDlgItem(s_dialog, IDC_PRODUCTSTRING), WM_SETTEXT, 0, |
|
||||||
(LPARAM)plProduct::ProductString().c_str()); |
|
||||||
|
|
||||||
s_dialogCreateEvent.Signal(); |
|
||||||
|
|
||||||
MessagePump(s_dialog); |
|
||||||
|
|
||||||
s_dialog = 0; |
|
||||||
s_shutdown = true; |
|
||||||
s_shutdownEvent.Signal(); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static size_t CurlCallback(void *buffer, size_t size, size_t nmemb, void *) |
|
||||||
{ |
|
||||||
static char status[256]; |
|
||||||
|
|
||||||
strncpy(status, (const char *)buffer, std::min<size_t>(size * nmemb, 256)); |
|
||||||
status[255] = 0; |
|
||||||
SetStatusText(status); |
|
||||||
return size * nmemb; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void StatusCallback(void *) |
|
||||||
{ |
|
||||||
#ifdef USE_VLD |
|
||||||
VLDEnable(); |
|
||||||
#endif |
|
||||||
|
|
||||||
const char *serverUrl = GetServerStatusUrl(); |
|
||||||
|
|
||||||
CURL * hCurl = curl_easy_init(); |
|
||||||
curl_easy_setopt(hCurl, CURLOPT_ERRORBUFFER, s_curlError); |
|
||||||
|
|
||||||
// update while we are running
|
|
||||||
while(!s_shutdown) |
|
||||||
{ |
|
||||||
curl_easy_setopt(hCurl, CURLOPT_USERAGENT, "UruClient/1.0"); |
|
||||||
curl_easy_setopt(hCurl, CURLOPT_URL, serverUrl); |
|
||||||
curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, &CurlCallback); |
|
||||||
|
|
||||||
if (serverUrl[0] && curl_easy_perform(hCurl) != 0) // only perform request if there's actually a URL set
|
|
||||||
SetStatusText(s_curlError); |
|
||||||
|
|
||||||
for(unsigned i = 0; i < UPDATE_STATUSMSG_SECONDS && !s_shutdown; ++i) |
|
||||||
{ |
|
||||||
Sleep(1000); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
curl_easy_cleanup(hCurl); |
|
||||||
|
|
||||||
s_statusEvent.Signal(); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* Exports |
|
||||||
* |
|
||||||
***/ |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
void PrepCallback (int id, void *param) { |
|
||||||
s_prepared = true; |
|
||||||
if (id) |
|
||||||
s_shutdown = true; |
|
||||||
|
|
||||||
if (!s_shutdown) |
|
||||||
InitGame(); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
void InitCallback (int id, void *param) { |
|
||||||
if (id) |
|
||||||
s_shutdown = true; |
|
||||||
if (!s_shutdown) |
|
||||||
StartGame(); |
|
||||||
} |
|
||||||
|
|
||||||
//=============================================================================
|
|
||||||
void StartCallback( int id, void *param) { |
|
||||||
if(id == kStatusError) { |
|
||||||
MessageBox(nil, "Failed to launch URU", "URU Launcher", MB_ICONERROR); |
|
||||||
} |
|
||||||
StopGame(); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
void StopCallback (int id, void *param) {
|
|
||||||
s_shutdown = true; |
|
||||||
TerminateGame(); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
void TerminateCallback (int id, void *param) { |
|
||||||
s_shutdown = true; |
|
||||||
s_terminated = true; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
void ExitCallback (int id, void *param) { |
|
||||||
TerminateGame(); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
void ProgressCallback (int id, void *param) { |
|
||||||
PatchInfo *patchInfo = (PatchInfo *)param; |
|
||||||
SetProgress(patchInfo->progress); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
void SetTextCallback (const char text[]) { |
|
||||||
SetText(text); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
void SetStatusTextCallback (const char text[]) { |
|
||||||
SetStatusText(text); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
void SetTimeRemainingCallback (unsigned seconds) { |
|
||||||
SetTimeRemaining(seconds); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
void SetBytesRemainingCallback (unsigned bytes) { |
|
||||||
SetBytesRemaining(bytes); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
enum { |
|
||||||
kArgServerIni, |
|
||||||
kArgNoSelfPatch, |
|
||||||
kArgBuildId, |
|
||||||
kArgCwd, |
|
||||||
}; |
|
||||||
|
|
||||||
static const CmdArgDef s_cmdLineArgs[] = { |
|
||||||
{ kCmdArgFlagged | kCmdTypeString, L"ServerIni", kArgServerIni }, |
|
||||||
{ kCmdArgFlagged | kCmdTypeBool, L"NoSelfPatch", kArgNoSelfPatch }, |
|
||||||
{ kCmdArgFlagged | kCmdTypeInt, L"BuildId", kArgBuildId }, |
|
||||||
{ kCmdArgFlagged | kCmdTypeBool, L"Cwd", kArgCwd }, |
|
||||||
}; |
|
||||||
|
|
||||||
#include "pfConsoleCore/pfConsoleEngine.h" |
|
||||||
PF_CONSOLE_LINK_FILE(Core) |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
int __stdcall WinMain ( |
|
||||||
HINSTANCE hInstance, |
|
||||||
HINSTANCE hPrevInstance, |
|
||||||
LPSTR lpCmdLine, |
|
||||||
int nCmdShow |
|
||||||
){ |
|
||||||
PF_CONSOLE_INITIALIZE(Core) |
|
||||||
|
|
||||||
wchar_t token[256]; |
|
||||||
const wchar_t *appCmdLine = AppGetCommandLine(); |
|
||||||
StrTokenize(&appCmdLine, token, arrsize(token), WHITESPACE); |
|
||||||
while(!StrStr(token, L".exe") && !StrStr(token, L".tmp"))
|
|
||||||
{ |
|
||||||
StrTokenize(&appCmdLine, token, arrsize(token), WHITESPACE); |
|
||||||
}
|
|
||||||
while (*appCmdLine == L' ') |
|
||||||
++appCmdLine; |
|
||||||
|
|
||||||
bool isTempPatcher = false; |
|
||||||
|
|
||||||
plFileName curPatcherFile = plFileSystem::GetCurrentAppPath(); |
|
||||||
plFileName newPatcherFile = plFileName::Join(curPatcherFile.StripFileName(), kPatcherExeFilename); |
|
||||||
|
|
||||||
// If our exe name doesn't match the "real" patcher exe name, then we are a newly
|
|
||||||
// downloaded patcher that needs to be copied over to the "real" exe.. so do that,
|
|
||||||
// exec it, and exit.
|
|
||||||
if (0 != curPatcherFile.AsString().CompareI(newPatcherFile.AsString())) { |
|
||||||
isTempPatcher = true; |
|
||||||
} |
|
||||||
|
|
||||||
CCmdParser cmdParser(s_cmdLineArgs, arrsize(s_cmdLineArgs)); |
|
||||||
cmdParser.Parse(); |
|
||||||
|
|
||||||
if (!cmdParser.IsSpecified(kArgCwd)) |
|
||||||
s_workingDir = plFileSystem::GetCurrentAppPath().StripFileName(); |
|
||||||
|
|
||||||
s_hInstance = hInstance; |
|
||||||
memset(&s_launcherInfo, 0, sizeof(s_launcherInfo)); |
|
||||||
StrPrintf(s_launcherInfo.cmdLine, arrsize(s_launcherInfo.cmdLine), appCmdLine); |
|
||||||
s_launcherInfo.returnCode = 0; |
|
||||||
|
|
||||||
curl_global_init(CURL_GLOBAL_ALL); |
|
||||||
|
|
||||||
plFileName serverIni = "server.ini"; |
|
||||||
if (cmdParser.IsSpecified(kArgServerIni)) |
|
||||||
serverIni = plString::FromWchar(cmdParser.GetString(kArgServerIni)); |
|
||||||
|
|
||||||
// Load the server.ini so we know what to connect to
|
|
||||||
FILE *serverini = plFileSystem::Open(serverIni, "rb"); |
|
||||||
if (serverini) |
|
||||||
{ |
|
||||||
fclose(serverini); |
|
||||||
pfConsoleEngine tempConsole; |
|
||||||
tempConsole.ExecuteFile(serverIni); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
hsMessageBox("No server.ini file found. Please check your URU installation.", "Error", hsMessageBoxNormal); |
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
if(!isTempPatcher) |
|
||||||
{ |
|
||||||
// create window thread
|
|
||||||
s_thread = (HANDLE)_beginthread( |
|
||||||
WindowThreadProc, |
|
||||||
0, |
|
||||||
nil
|
|
||||||
); |
|
||||||
if(cmdParser.IsSpecified(kArgBuildId)) |
|
||||||
s_launcherInfo.buildId = cmdParser.GetInt(kArgBuildId); |
|
||||||
|
|
||||||
// Wait for the dialog to be created
|
|
||||||
s_dialogCreateEvent.Wait(); |
|
||||||
_beginthread(StatusCallback, 0, nil); // get status
|
|
||||||
} |
|
||||||
|
|
||||||
for (;;) { |
|
||||||
// Wait for previous process to exit. This will happen if we just patched.
|
|
||||||
HANDLE mutex = CreateMutexW(NULL, TRUE, kPatcherExeFilename.AsString().ToWchar()); |
|
||||||
DWORD wait = WaitForSingleObject(mutex, 0); |
|
||||||
while(!s_shutdown && wait != WAIT_OBJECT_0) |
|
||||||
wait = WaitForSingleObject(mutex, 100); |
|
||||||
|
|
||||||
// User canceled
|
|
||||||
if (s_shutdown) |
|
||||||
break; |
|
||||||
|
|
||||||
// If our exe name doesn't match the "real" patcher exe name, then we are a newly
|
|
||||||
// downloaded patcher that needs to be copied over to the "real" exe.. so do that,
|
|
||||||
// exec it, and exit.
|
|
||||||
if (isTempPatcher) { |
|
||||||
// MessageBox(nil, "Replacing patcher file", "Msg", MB_OK);
|
|
||||||
|
|
||||||
// Wait for the other process to exit
|
|
||||||
Sleep(1000); |
|
||||||
|
|
||||||
if (!plFileSystem::Unlink(newPatcherFile)) { |
|
||||||
wchar_t error[256]; |
|
||||||
DWORD errorCode = GetLastError(); |
|
||||||
wchar_t *msg = TranslateErrorCode(errorCode); |
|
||||||
|
|
||||||
StrPrintf(error, arrsize(error), L"Failed to delete old patcher executable. %s", msg); |
|
||||||
MessageBoxW(GetTopWindow(nil), error, L"Error", MB_OK); |
|
||||||
LocalFree(msg); |
|
||||||
break; |
|
||||||
} |
|
||||||
if (!plFileSystem::Move(curPatcherFile, newPatcherFile)) { |
|
||||||
wchar_t error[256]; |
|
||||||
DWORD errorCode = GetLastError(); |
|
||||||
wchar_t *msg = TranslateErrorCode(errorCode); |
|
||||||
|
|
||||||
StrPrintf(error, arrsize(error), L"Failed to replace old patcher executable. %s", msg); |
|
||||||
MessageBoxW(GetTopWindow(nil), error, L"Error", MB_OK); |
|
||||||
// attempt to clean up this tmp file
|
|
||||||
plFileSystem::Unlink(curPatcherFile); |
|
||||||
LocalFree(msg); |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
// launch new patcher
|
|
||||||
STARTUPINFOW si; |
|
||||||
PROCESS_INFORMATION pi; |
|
||||||
memset(&si, 0, sizeof(si)); |
|
||||||
memset(&pi, 0, sizeof(pi)); |
|
||||||
si.cb = sizeof(si); |
|
||||||
|
|
||||||
wchar_t cmdline[MAX_PATH]; |
|
||||||
StrPrintf(cmdline, arrsize(cmdline), L"%S %s", newPatcherFile.AsString().c_str(), s_launcherInfo.cmdLine); |
|
||||||
|
|
||||||
// we have only successfully patched if we actually launch the new version of the patcher
|
|
||||||
(void)CreateProcessW(
|
|
||||||
NULL, |
|
||||||
cmdline, |
|
||||||
NULL, |
|
||||||
NULL, |
|
||||||
FALSE, |
|
||||||
DETACHED_PROCESS, |
|
||||||
NULL, |
|
||||||
NULL, |
|
||||||
&si, |
|
||||||
&pi |
|
||||||
); |
|
||||||
|
|
||||||
SetReturnCode( pi.dwProcessId ); |
|
||||||
CloseHandle( pi.hThread ); |
|
||||||
CloseHandle( pi.hProcess ); |
|
||||||
|
|
||||||
// We're done.
|
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
// Clean up old temp files
|
|
||||||
plFileName fileSpec = plFileSystem::GetCurrentAppPath().StripFileName(); |
|
||||||
std::vector<plFileName> tmpFiles = plFileSystem::ListDir(fileSpec, "*.tmp"); |
|
||||||
std::for_each(tmpFiles.begin(), tmpFiles.end(), [](const plFileName &tmp) { |
|
||||||
plFileSystem::Unlink(tmp); |
|
||||||
}); |
|
||||||
|
|
||||||
SetConsoleCtrlHandler(CtrlHandler, TRUE); |
|
||||||
InitAsyncCore(); // must do this before self patch, since it needs to connect to the file server
|
|
||||||
|
|
||||||
// check to see if the patcher needs to be updated, and do it if so.
|
|
||||||
ENetError selfPatchResult; |
|
||||||
if (false == (SelfPatch(cmdParser.IsSpecified(kArgNoSelfPatch), &s_shutdown, &selfPatchResult, &s_launcherInfo)) && IS_NET_SUCCESS(selfPatchResult)) { |
|
||||||
// We didn't self-patch, so check for client updates and download them, then exec the client
|
|
||||||
StrCopy(s_launcherInfo.path, s_workingDir.AsString().ToWchar(), arrsize(s_launcherInfo.path)); |
|
||||||
s_launcherInfo.prepCallback = PrepCallback; |
|
||||||
s_launcherInfo.initCallback = InitCallback; |
|
||||||
s_launcherInfo.startCallback = StartCallback; |
|
||||||
s_launcherInfo.stopCallback = StopCallback; |
|
||||||
s_launcherInfo.terminateCallback = TerminateCallback; |
|
||||||
s_launcherInfo.progressCallback = ProgressCallback; |
|
||||||
s_launcherInfo.exitCallback = ExitCallback; |
|
||||||
s_launcherInfo.SetText = SetTextCallback; |
|
||||||
s_launcherInfo.SetStatusText = SetStatusTextCallback; |
|
||||||
s_launcherInfo.SetTimeRemaining = SetTimeRemainingCallback; |
|
||||||
s_launcherInfo.SetBytesRemaining = SetBytesRemainingCallback; |
|
||||||
PrepareGame(); |
|
||||||
|
|
||||||
while (!s_shutdown) // wait for window to be closed
|
|
||||||
AsyncSleep(10); |
|
||||||
|
|
||||||
StopGame(); |
|
||||||
|
|
||||||
// Wait for the PrepareGame thread to exit
|
|
||||||
while (!s_prepared) |
|
||||||
AsyncSleep(10); |
|
||||||
|
|
||||||
// Wait for the StopGame thread to exit
|
|
||||||
while (!s_terminated) |
|
||||||
Sleep(10); |
|
||||||
} |
|
||||||
else if (IS_NET_ERROR(selfPatchResult)) { |
|
||||||
// Self-patch failed
|
|
||||||
SetText("Self-patch failed. Exiting..."); |
|
||||||
if (!s_shutdown) {
|
|
||||||
wchar_t str[256]; |
|
||||||
StrPrintf(str, arrsize(str), L"Patcher update failed. Error %u, %s", selfPatchResult, NetErrorToString(selfPatchResult)); |
|
||||||
MessageBoxW(GetTopWindow(nil), str, L"Error", MB_OK); |
|
||||||
} |
|
||||||
} |
|
||||||
else { |
|
||||||
// We self-patched, so just exit (self-patcher already launched the new patcher.
|
|
||||||
// it is now waiting for our process to shutdown and release the shared mutex).
|
|
||||||
SetText("Patcher updated. Restarting..."); |
|
||||||
s_shutdown = true; |
|
||||||
} |
|
||||||
|
|
||||||
ShutdownAsyncCore(); |
|
||||||
s_statusEvent.Wait(); |
|
||||||
|
|
||||||
PostMessage(s_dialog, WM_QUIT, 0, 0); // tell our window to shutdown
|
|
||||||
s_shutdownEvent.Wait(); // wait for our window to shutdown
|
|
||||||
|
|
||||||
SetConsoleCtrlHandler(CtrlHandler, FALSE); |
|
||||||
|
|
||||||
if (s_event) |
|
||||||
CloseHandle(s_event); |
|
||||||
|
|
||||||
s_eventQ.Clear(); |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
curl_global_cleanup(); |
|
||||||
|
|
||||||
return s_launcherInfo.returnCode; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
void SetReturnCode (DWORD retCode) { |
|
||||||
s_launcherInfo.returnCode = retCode; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* Window Events |
|
||||||
* |
|
||||||
***/ |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
void SetProgress (unsigned progress) { |
|
||||||
SetProgressEvent *event = new SetProgressEvent(); |
|
||||||
event->type = kEventSetProgress; |
|
||||||
event->progress = progress; |
|
||||||
PostEvent(event); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
void SetText (const char text[]) { |
|
||||||
SetTextEvent *event = new SetTextEvent(); |
|
||||||
event->type = kEventSetText; |
|
||||||
StrCopy(event->text, text, arrsize(event->text)); |
|
||||||
PostEvent(event); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
void SetStatusText (const char text[]) { |
|
||||||
SetTextEvent *event = new SetTextEvent(); |
|
||||||
event->type = kEventSetStatusText; |
|
||||||
StrCopy(event->text, text, arrsize(event->text)); |
|
||||||
PostEvent(event); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
void SetTimeRemaining (unsigned seconds) { |
|
||||||
SetTimeRemainingEvent *event = new SetTimeRemainingEvent; |
|
||||||
event->type = kEventSetTimeRemaining; |
|
||||||
event->seconds = seconds; |
|
||||||
PostEvent(event); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
void SetBytesRemaining (unsigned bytes) { |
|
||||||
SetBytesRemainingEvent *event = new SetBytesRemainingEvent; |
|
||||||
event->type = kEventSetBytesRemaining; |
|
||||||
event->bytes = bytes; |
|
||||||
PostEvent(event); |
|
||||||
} |
|
@ -1,72 +0,0 @@ |
|||||||
/*==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==*/ |
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/Pch.h |
|
||||||
*
|
|
||||||
***/ |
|
||||||
|
|
||||||
#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PCH_H |
|
||||||
#error "Header $/Plasma20/Sources/Plasma/Apps/plUruLauncher/Pch.h included more than once" |
|
||||||
#endif |
|
||||||
#define PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PCH_H |
|
||||||
|
|
||||||
#include "hsWindows.h" |
|
||||||
#include <process.h> |
|
||||||
#include <ctime> |
|
||||||
|
|
||||||
#include <curl/curl.h> |
|
||||||
|
|
||||||
#include "pnUtils/pnUtils.h" |
|
||||||
#include "pnNetBase/pnNetBase.h" |
|
||||||
#include "pnAsyncCore/pnAsyncCore.h" |
|
||||||
#include "plProduct.h" |
|
||||||
#include "pnNetCli/pnNetCli.h" |
|
||||||
#include "plNetGameLib/plNetGameLib.h" |
|
||||||
#include "pnEncryption/plChecksum.h" |
|
||||||
|
|
||||||
#include "plCompression/plZlibStream.h" |
|
||||||
#include "plClientPatcher/UruPlayer.h" |
|
||||||
|
|
||||||
#include "plLauncherInfo.h" |
|
||||||
#include "Intern.h" |
|
||||||
|
|
@ -1,338 +0,0 @@ |
|||||||
/*==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==*/ |
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/SelfPatcher.cpp |
|
||||||
*
|
|
||||||
***/ |
|
||||||
|
|
||||||
#include "Pch.h" |
|
||||||
#include "plStatusLog/plStatusLog.h" |
|
||||||
#pragma hdrstop |
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* Private Data |
|
||||||
* |
|
||||||
***/ |
|
||||||
|
|
||||||
#ifndef PLASMA_EXTERNAL_RELEASE |
|
||||||
static const wchar_t s_manifest[] = L"InternalPatcher"; |
|
||||||
#else |
|
||||||
static const wchar_t s_manifest[] = L"ExternalPatcher"; |
|
||||||
#endif |
|
||||||
|
|
||||||
class SelfPatcherStream : public plZlibStream { |
|
||||||
public: |
|
||||||
virtual uint32_t Write(uint32_t byteCount, const void* buffer); |
|
||||||
static plLauncherInfo *info; |
|
||||||
static unsigned totalBytes; |
|
||||||
static unsigned progress; |
|
||||||
}; |
|
||||||
|
|
||||||
unsigned SelfPatcherStream::totalBytes = 0; |
|
||||||
unsigned SelfPatcherStream::progress = 0; |
|
||||||
|
|
||||||
static bool s_downloadComplete; |
|
||||||
static long s_numFiles; |
|
||||||
static ENetError s_patchResult; |
|
||||||
static bool s_updated; |
|
||||||
static plFileName s_newPatcherFile; |
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* Private Functions |
|
||||||
* |
|
||||||
***/ |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void NetErrorHandler (ENetProtocol protocol, ENetError error) { |
|
||||||
plString msg = plString::Format("NetErr: %S", NetErrorToString(error)); |
|
||||||
plStatusLog::AddLineS("patcher.log", msg.c_str()); |
|
||||||
|
|
||||||
if (IS_NET_SUCCESS(s_patchResult)) |
|
||||||
s_patchResult = error; |
|
||||||
s_downloadComplete = true; |
|
||||||
|
|
||||||
switch(error) { |
|
||||||
case kNetErrServerBusy: |
|
||||||
MessageBox(0, "Due to the high demand, the server is currently busy. Please try again later, or for alternative download options visit: http://www.mystonline.com/play/", "UruLauncher", MB_OK); |
|
||||||
s_patchResult = kNetErrServerBusy; |
|
||||||
s_downloadComplete = true; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void DownloadCallback ( |
|
||||||
ENetError result, |
|
||||||
void * param, |
|
||||||
const plFileName & filename, |
|
||||||
hsStream * writer |
|
||||||
) { |
|
||||||
if(IS_NET_ERROR(result)) { |
|
||||||
switch (result) { |
|
||||||
case kNetErrTimeout: |
|
||||||
writer->Rewind(); |
|
||||||
NetCliFileDownloadRequest(filename, writer, DownloadCallback, param); |
|
||||||
break; |
|
||||||
|
|
||||||
default: |
|
||||||
plString msg = plString::Format("Error getting patcher file: %S", NetErrorToString(result)); |
|
||||||
plStatusLog::AddLineS("patcher.log", msg.c_str()); |
|
||||||
|
|
||||||
if (IS_NET_SUCCESS(s_patchResult)) |
|
||||||
s_patchResult = result; |
|
||||||
break; |
|
||||||
} |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
writer->Close(); |
|
||||||
delete writer; |
|
||||||
AtomicAdd(&s_numFiles, -1); |
|
||||||
|
|
||||||
if(!s_numFiles) { |
|
||||||
s_downloadComplete = true; |
|
||||||
s_updated = true; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static bool MD5Check (const plFileName &filename, const char *md5) { |
|
||||||
// Do md5 check
|
|
||||||
plMD5Checksum existingMD5(filename); |
|
||||||
plMD5Checksum latestMD5; |
|
||||||
|
|
||||||
latestMD5.SetFromHexString(md5); |
|
||||||
return (existingMD5 == latestMD5); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void ManifestCallback ( |
|
||||||
ENetError result, |
|
||||||
void * param, |
|
||||||
const wchar_t group[], |
|
||||||
const NetCliFileManifestEntry manifest[], |
|
||||||
unsigned entryCount |
|
||||||
) { |
|
||||||
if(IS_NET_ERROR(result)) { |
|
||||||
switch (result) { |
|
||||||
case kNetErrTimeout: |
|
||||||
NetCliFileManifestRequest(ManifestCallback, nil, s_manifest); |
|
||||||
break; |
|
||||||
|
|
||||||
default: |
|
||||||
plString msg = plString::Format("Error getting patcher manifest: %S", NetErrorToString(result)); |
|
||||||
plStatusLog::AddLineS("patcher.log", msg.c_str()); |
|
||||||
|
|
||||||
if (IS_NET_SUCCESS(s_patchResult)) |
|
||||||
s_patchResult = result; |
|
||||||
break; |
|
||||||
} |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
#ifndef PLASMA_EXTERNAL_RELEASE |
|
||||||
if (entryCount == 0) { // dataserver does not contain a patcher
|
|
||||||
s_downloadComplete = true; |
|
||||||
return; |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
// MD5 check current patcher against value in manifest
|
|
||||||
ASSERT(entryCount == 1); |
|
||||||
plFileName curPatcherFile = plFileSystem::GetCurrentAppPath(); |
|
||||||
if (!MD5Check(curPatcherFile, plString::FromWchar(manifest[0].md5, 32).c_str())) { |
|
||||||
// MessageBox(GetTopWindow(nil), "MD5 failed", "Msg", MB_OK);
|
|
||||||
SelfPatcherStream::totalBytes += manifest[0].zipSize; |
|
||||||
|
|
||||||
AtomicAdd(&s_numFiles, 1); |
|
||||||
SetText("Downloading new patcher..."); |
|
||||||
|
|
||||||
SelfPatcherStream * stream = new SelfPatcherStream; |
|
||||||
if (!stream->Open(s_newPatcherFile, "wb")) |
|
||||||
ErrorAssert(__LINE__, __FILE__, "Failed to create file: %s, errno: %u", s_newPatcherFile.AsString().c_str(), errno); |
|
||||||
|
|
||||||
NetCliFileDownloadRequest(plString::FromWchar(manifest[0].downloadName), stream, DownloadCallback, nil); |
|
||||||
} |
|
||||||
else { |
|
||||||
s_downloadComplete = true; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static void FileSrvIpAddressCallback ( |
|
||||||
ENetError result, |
|
||||||
void * param, |
|
||||||
const wchar_t addr[] |
|
||||||
) { |
|
||||||
NetCliGateKeeperDisconnect(); |
|
||||||
|
|
||||||
if (IS_NET_ERROR(result)) { |
|
||||||
plString msg = plString::Format("FileSrvIpAddressRequest failed: %S", NetErrorToString(result)); |
|
||||||
plStatusLog::AddLineS("patcher.log", msg.c_str()); |
|
||||||
|
|
||||||
s_patchResult = result; |
|
||||||
s_downloadComplete = true; |
|
||||||
} |
|
||||||
|
|
||||||
// Start connecting to the server
|
|
||||||
const char* caddr = hsWStringToString(addr); |
|
||||||
NetCliFileStartConnect(&caddr, 1, true); |
|
||||||
delete[] caddr; |
|
||||||
|
|
||||||
s_newPatcherFile = plFileSystem::GetCurrentAppPath().StripFileName(); |
|
||||||
s_newPatcherFile = plFileSystem::GetTempFilename(kPatcherExeFilename.AsString().c_str(), s_newPatcherFile); |
|
||||||
plFileSystem::Unlink(s_newPatcherFile); |
|
||||||
|
|
||||||
NetCliFileManifestRequest(ManifestCallback, nil, s_manifest); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
static bool SelfPatcherProc (bool * abort, plLauncherInfo *info) { |
|
||||||
|
|
||||||
bool patched = false; |
|
||||||
s_downloadComplete = false; |
|
||||||
s_patchResult = kNetSuccess; |
|
||||||
|
|
||||||
NetClientInitialize(); |
|
||||||
NetClientSetErrorHandler(NetErrorHandler); |
|
||||||
|
|
||||||
const char** addrs; |
|
||||||
unsigned count; |
|
||||||
|
|
||||||
count = GetGateKeeperSrvHostnames(&addrs); |
|
||||||
|
|
||||||
// Start connecting to the server
|
|
||||||
NetCliGateKeeperStartConnect(addrs, count); |
|
||||||
|
|
||||||
// request a file server ip address
|
|
||||||
NetCliGateKeeperFileSrvIpAddressRequest(FileSrvIpAddressCallback, nil, true); |
|
||||||
|
|
||||||
while(!s_downloadComplete && !*abort) { |
|
||||||
NetClientUpdate(); |
|
||||||
AsyncSleep(10); |
|
||||||
}
|
|
||||||
|
|
||||||
NetCliFileDisconnect(); |
|
||||||
NetClientUpdate(); |
|
||||||
|
|
||||||
// Shutdown the client/server networking subsystem
|
|
||||||
NetClientDestroy(); |
|
||||||
|
|
||||||
if (s_downloadComplete && !*abort && s_updated && IS_NET_SUCCESS(s_patchResult)) { |
|
||||||
|
|
||||||
// launch new patcher
|
|
||||||
STARTUPINFOW si; |
|
||||||
PROCESS_INFORMATION pi; |
|
||||||
memset(&si, 0, sizeof(si)); |
|
||||||
memset(&pi, 0, sizeof(pi)); |
|
||||||
si.cb = sizeof(si); |
|
||||||
|
|
||||||
wchar_t cmdline[MAX_PATH]; |
|
||||||
StrPrintf(cmdline, arrsize(cmdline), L"%s %s", |
|
||||||
s_newPatcherFile.AsString().ToWchar().GetData(), info->cmdLine); |
|
||||||
|
|
||||||
// we have only successfully patched if we actually launch the new version of the patcher
|
|
||||||
patched = CreateProcessW( |
|
||||||
NULL, |
|
||||||
cmdline, |
|
||||||
NULL, |
|
||||||
NULL, |
|
||||||
FALSE,
|
|
||||||
DETACHED_PROCESS, |
|
||||||
NULL, |
|
||||||
NULL, |
|
||||||
&si, |
|
||||||
&pi |
|
||||||
); |
|
||||||
SetReturnCode(pi.dwProcessId); |
|
||||||
CloseHandle( pi.hThread ); |
|
||||||
CloseHandle( pi.hProcess ); |
|
||||||
ASSERT(patched); |
|
||||||
} |
|
||||||
|
|
||||||
return patched; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* ProgressStream Functions |
|
||||||
* |
|
||||||
***/ |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
uint32_t SelfPatcherStream::Write(uint32_t byteCount, const void* buffer) { |
|
||||||
progress += byteCount; |
|
||||||
float p = (float)progress / (float)totalBytes * 100; // progress
|
|
||||||
SetProgress( (int)p ); |
|
||||||
return plZlibStream::Write(byteCount, buffer); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* Protected Functions |
|
||||||
* |
|
||||||
***/ |
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
// if return value is true, there was an update and the patcher should be shutdown, so the new patcher can take over
|
|
||||||
bool SelfPatch (bool noSelfPatch, bool * abort, ENetError * result, plLauncherInfo *info) { |
|
||||||
bool patched = false; |
|
||||||
if (!noSelfPatch) { |
|
||||||
SetText("Checking for patcher update..."); |
|
||||||
patched = SelfPatcherProc(abort, info); |
|
||||||
} |
|
||||||
*result = s_patchResult; |
|
||||||
return patched; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/* Enable themes in Windows XP and later */ |
|
||||||
#pragma comment(linker,"\"/manifestdependency:type='win32' \ |
|
||||||
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
|
|
||||||
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") |
|
@ -0,0 +1,468 @@ |
|||||||
|
/*==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 "HeadSpin.h" |
||||||
|
#include "plClientLauncher.h" |
||||||
|
#include "plFileSystem.h" |
||||||
|
#include "plProduct.h" |
||||||
|
#include "hsThread.h" |
||||||
|
#include "hsTimer.h" |
||||||
|
|
||||||
|
#include "pnUtils/pnUtils.h" // for CCmdParser |
||||||
|
#include "pnAsyncCore/pnAsyncCore.h" |
||||||
|
#include "plNetGameLib/plNetGameLib.h" |
||||||
|
#include "plStatusLog/plStatusLog.h" |
||||||
|
|
||||||
|
#include "pfPatcher/plManifests.h" |
||||||
|
#include "pfPatcher/pfPatcher.h" |
||||||
|
|
||||||
|
#include "pfConsoleCore/pfConsoleEngine.h" |
||||||
|
PF_CONSOLE_LINK_FILE(Core) |
||||||
|
|
||||||
|
#include <algorithm> |
||||||
|
#include <curl/curl.h> |
||||||
|
#include <deque> |
||||||
|
|
||||||
|
plClientLauncher::ErrorFunc s_errorProc = nullptr; // don't even ask, cause I'm not happy about this.
|
||||||
|
|
||||||
|
const int kNetTransTimeout = 5 * 60 * 1000; // 5m
|
||||||
|
const int kShardStatusUpdateTime = 5; // 5s
|
||||||
|
const int kAsyncCoreShutdownTime = 2 * 1000; // 2s
|
||||||
|
const int kNetCoreUpdateSleepTime = 10; // 10ms
|
||||||
|
|
||||||
|
// ===================================================
|
||||||
|
|
||||||
|
class plShardStatus : public hsThread |
||||||
|
{ |
||||||
|
double fLastUpdate; |
||||||
|
volatile bool fRunning; |
||||||
|
hsEvent fUpdateEvent; |
||||||
|
char fCurlError[CURL_ERROR_SIZE]; |
||||||
|
|
||||||
|
public: |
||||||
|
plClientLauncher::StatusFunc fShardFunc; |
||||||
|
|
||||||
|
plShardStatus() : |
||||||
|
fRunning(true), fLastUpdate(0) |
||||||
|
{ } |
||||||
|
|
||||||
|
virtual hsError Run(); |
||||||
|
void Shutdown(); |
||||||
|
void Update(); |
||||||
|
}; |
||||||
|
|
||||||
|
static size_t ICurlCallback(void* buffer, size_t size, size_t nmemb, void* thread) |
||||||
|
{ |
||||||
|
static char status[256]; |
||||||
|
|
||||||
|
strncpy(status, (const char *)buffer, std::min<size_t>(size * nmemb, arrsize(status))); |
||||||
|
status[arrsize(status) - 1] = 0; |
||||||
|
static_cast<plShardStatus*>(thread)->fShardFunc(status); |
||||||
|
return size * nmemb; |
||||||
|
} |
||||||
|
|
||||||
|
hsError plShardStatus::Run() |
||||||
|
{ |
||||||
|
{ |
||||||
|
const char* url = GetServerStatusUrl(); |
||||||
|
|
||||||
|
// initialize CURL
|
||||||
|
std::unique_ptr<CURL, std::function<void(CURL*)>> curl(curl_easy_init(), curl_easy_cleanup); |
||||||
|
curl_easy_setopt(curl.get(), CURLOPT_ERRORBUFFER, fCurlError); |
||||||
|
curl_easy_setopt(curl.get(), CURLOPT_USERAGENT, "UruClient/1.0"); |
||||||
|
curl_easy_setopt(curl.get(), CURLOPT_URL, url); |
||||||
|
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, this); |
||||||
|
curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, ICurlCallback); |
||||||
|
|
||||||
|
// we want to go ahead and run once
|
||||||
|
fUpdateEvent.Signal(); |
||||||
|
|
||||||
|
// loop until we die!
|
||||||
|
do |
||||||
|
{ |
||||||
|
fUpdateEvent.Wait(); |
||||||
|
if (!fRunning) |
||||||
|
break; |
||||||
|
|
||||||
|
if (url[0] && curl_easy_perform(curl.get())) |
||||||
|
fShardFunc(fCurlError); |
||||||
|
fLastUpdate = hsTimer::GetSysSeconds(); |
||||||
|
} while (fRunning); |
||||||
|
} |
||||||
|
|
||||||
|
return hsOK; |
||||||
|
} |
||||||
|
|
||||||
|
void plShardStatus::Shutdown() |
||||||
|
{ |
||||||
|
fRunning = false; |
||||||
|
fUpdateEvent.Signal(); |
||||||
|
} |
||||||
|
|
||||||
|
void plShardStatus::Update() |
||||||
|
{ |
||||||
|
double now = hsTimer::GetSysSeconds(); |
||||||
|
if ((now - fLastUpdate) >= kShardStatusUpdateTime) |
||||||
|
fUpdateEvent.Signal(); |
||||||
|
} |
||||||
|
|
||||||
|
// ===================================================
|
||||||
|
|
||||||
|
class plRedistUpdater : public hsThread |
||||||
|
{ |
||||||
|
bool fSuccess; |
||||||
|
|
||||||
|
public: |
||||||
|
plClientLauncher* fParent; |
||||||
|
plClientLauncher::InstallRedistFunc fInstallProc; |
||||||
|
std::deque<plFileName> fRedistQueue; |
||||||
|
|
||||||
|
plRedistUpdater() |
||||||
|
: fSuccess(true) |
||||||
|
{ } |
||||||
|
|
||||||
|
~plRedistUpdater() |
||||||
|
{ |
||||||
|
// If anything is left in the deque, it was not installed.
|
||||||
|
// We should unlink them so the next launch will redownload and install them.
|
||||||
|
std::for_each(fRedistQueue.begin(), fRedistQueue.end(), |
||||||
|
[] (const plFileName& file) { |
||||||
|
plFileSystem::Unlink(file); |
||||||
|
} |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
virtual void OnQuit() |
||||||
|
{ |
||||||
|
// If we succeeded, then we should launch the game client...
|
||||||
|
if (fSuccess) |
||||||
|
fParent->LaunchClient(); |
||||||
|
} |
||||||
|
|
||||||
|
virtual hsError Run() |
||||||
|
{ |
||||||
|
while (!fRedistQueue.empty()) { |
||||||
|
if (fInstallProc(fRedistQueue.back())) |
||||||
|
fRedistQueue.pop_back(); |
||||||
|
else { |
||||||
|
s_errorProc(kNetErrInternalError, fRedistQueue.back().AsString()); |
||||||
|
fSuccess = false; |
||||||
|
return hsFail; |
||||||
|
} |
||||||
|
} |
||||||
|
return hsOK; |
||||||
|
} |
||||||
|
|
||||||
|
virtual void Start() |
||||||
|
{ |
||||||
|
if (fRedistQueue.empty()) |
||||||
|
OnQuit(); |
||||||
|
else |
||||||
|
hsThread::Start(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// ===================================================
|
||||||
|
|
||||||
|
plClientLauncher::plClientLauncher() : |
||||||
|
fFlags(0), |
||||||
|
fServerIni("server.ini"), |
||||||
|
fPatcherFactory(nullptr), |
||||||
|
fClientExecutable(plManifest::ClientExecutable()), |
||||||
|
fStatusThread(new plShardStatus()), |
||||||
|
fInstallerThread(new plRedistUpdater()) |
||||||
|
{ |
||||||
|
pfPatcher::GetLog()->AddLine(plProduct::ProductString().c_str()); |
||||||
|
} |
||||||
|
|
||||||
|
plClientLauncher::~plClientLauncher() { } |
||||||
|
|
||||||
|
// ===================================================
|
||||||
|
|
||||||
|
plString plClientLauncher::GetAppArgs() const |
||||||
|
{ |
||||||
|
// If -Repair was specified, there are no args for the next call...
|
||||||
|
if (hsCheckBits(fFlags, kRepairGame)) { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
plStringStream ss; |
||||||
|
ss << "-ServerIni="; |
||||||
|
ss << fServerIni.AsString(); |
||||||
|
|
||||||
|
// optional args
|
||||||
|
if (hsCheckBits(fFlags, kClientImage)) |
||||||
|
ss << " -Image"; |
||||||
|
|
||||||
|
return ss.GetString(); |
||||||
|
} |
||||||
|
|
||||||
|
void plClientLauncher::IOnPatchComplete(ENetError result, const plString& msg) |
||||||
|
{ |
||||||
|
if (IS_NET_SUCCESS(result)) { |
||||||
|
// a couple of options
|
||||||
|
// 1. we self-patched and didn't update anything. patch the main client.
|
||||||
|
// 2. we self-patched and did things and stuff... re-run myself.
|
||||||
|
// 3. we patched the client... run it.
|
||||||
|
if (!hsCheckBits(fFlags, kHaveSelfPatched) && (fClientExecutable == plManifest::ClientExecutable())) { |
||||||
|
// case 1
|
||||||
|
hsSetBits(fFlags, kHaveSelfPatched); |
||||||
|
PatchClient(); |
||||||
|
} else { |
||||||
|
// cases 2 & 3 -- update any redistributables, then launch the client.
|
||||||
|
fInstallerThread->fParent = this; |
||||||
|
fInstallerThread->Start(); |
||||||
|
} |
||||||
|
} else if (s_errorProc) |
||||||
|
s_errorProc(result, msg); |
||||||
|
} |
||||||
|
|
||||||
|
bool plClientLauncher::IApproveDownload(const plFileName& file) |
||||||
|
{ |
||||||
|
// So, for a repair, what we want to do is quite simple.
|
||||||
|
// That is: download everything that is NOT in the root directory.
|
||||||
|
plFileName path = file.StripFileName(); |
||||||
|
return !path.AsString().IsEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
void plClientLauncher::LaunchClient() const |
||||||
|
{ |
||||||
|
if (fStatusFunc) |
||||||
|
fStatusFunc("Launching..."); |
||||||
|
fLaunchClientFunc(fClientExecutable, GetAppArgs()); |
||||||
|
} |
||||||
|
|
||||||
|
void plClientLauncher::PatchClient() |
||||||
|
{ |
||||||
|
if (fStatusFunc) { |
||||||
|
if (hsCheckBits(fFlags, kGameDataOnly)) |
||||||
|
fStatusFunc("Verifying game data..."); |
||||||
|
else |
||||||
|
fStatusFunc("Checking for updates..."); |
||||||
|
} |
||||||
|
hsAssert(fPatcherFactory, "why is the patcher factory nil?"); |
||||||
|
|
||||||
|
pfPatcher* patcher = fPatcherFactory(); |
||||||
|
patcher->OnCompletion(std::bind(&plClientLauncher::IOnPatchComplete, this, std::placeholders::_1, std::placeholders::_2)); |
||||||
|
patcher->OnSelfPatch([&](const plFileName& file) { fClientExecutable = file; }); |
||||||
|
patcher->OnRedistUpdate([&](const plFileName& file) { fInstallerThread->fRedistQueue.push_back(file); }); |
||||||
|
|
||||||
|
// If this is a repair, we need to approve the downloads...
|
||||||
|
if (hsCheckBits(fFlags, kGameDataOnly)) |
||||||
|
patcher->OnFileDownloadDesired(std::bind(&plClientLauncher::IApproveDownload, this, std::placeholders::_1)); |
||||||
|
|
||||||
|
// Let's get 'er done.
|
||||||
|
if (hsCheckBits(fFlags, kHaveSelfPatched)) { |
||||||
|
if (hsCheckBits(fFlags, kClientImage)) |
||||||
|
patcher->RequestManifest(plManifest::ClientImageManifest()); |
||||||
|
else |
||||||
|
patcher->RequestManifest(plManifest::ClientManifest()); |
||||||
|
} else |
||||||
|
patcher->RequestManifest(plManifest::PatcherManifest()); |
||||||
|
patcher->Start(); |
||||||
|
} |
||||||
|
|
||||||
|
bool plClientLauncher::CompleteSelfPatch(std::function<void(void)> waitProc) const |
||||||
|
{ |
||||||
|
if (hsCheckBits(fFlags, kHaveSelfPatched)) |
||||||
|
return false; |
||||||
|
|
||||||
|
plString myExe = plFileSystem::GetCurrentAppPath().GetFileName(); |
||||||
|
if (myExe.CompareI(plManifest::PatcherExecutable().AsString()) != 0) { |
||||||
|
waitProc(); |
||||||
|
|
||||||
|
// so now we need to unlink the old patcher, and move ME into that fool's place...
|
||||||
|
// then we can continue on our merry way!
|
||||||
|
if (!plFileSystem::Unlink(plManifest::PatcherExecutable())) { |
||||||
|
hsMessageBox("Failed to delete old patcher executable!", "Error", hsMessageBoxNormal, hsMessageBoxIconError); |
||||||
|
return true; |
||||||
|
} |
||||||
|
if (!plFileSystem::Move(plFileSystem::GetCurrentAppPath(), plManifest::PatcherExecutable())) { |
||||||
|
hsMessageBox("Failed to move patcher executable!", "Error", hsMessageBoxNormal, hsMessageBoxIconError); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// Now, execute the new patcher...
|
||||||
|
fLaunchClientFunc(plManifest::PatcherExecutable(), GetAppArgs() + " -NoSelfPatch"); |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// ===================================================
|
||||||
|
|
||||||
|
static void IGotFileServIPs(ENetError result, void* param, const wchar_t* addr) |
||||||
|
{ |
||||||
|
plClientLauncher* launcher = static_cast<plClientLauncher*>(param); |
||||||
|
NetCliGateKeeperDisconnect(); |
||||||
|
|
||||||
|
if (IS_NET_SUCCESS(result)) { |
||||||
|
// bah... why do I even bother
|
||||||
|
plString eapSucks = plString::FromWchar(addr); |
||||||
|
const char* eapReallySucks[] = { eapSucks.c_str() }; |
||||||
|
NetCliFileStartConnect(eapReallySucks, 1, true); |
||||||
|
|
||||||
|
// Who knows if we will actually connect. So let's start updating.
|
||||||
|
launcher->PatchClient(); |
||||||
|
} else if (s_errorProc) |
||||||
|
s_errorProc(result, "Failed to get FileServ addresses"); |
||||||
|
} |
||||||
|
|
||||||
|
static void IEapSucksErrorProc(ENetProtocol protocol, ENetError error) |
||||||
|
{ |
||||||
|
if (s_errorProc) { |
||||||
|
plString msg = plString::Format("Protocol: %S", NetProtocolToString(protocol)); |
||||||
|
s_errorProc(error, msg); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void plClientLauncher::InitializeNetCore() |
||||||
|
{ |
||||||
|
// initialize shard status
|
||||||
|
hsTimer::SetRealTime(true); |
||||||
|
fStatusThread->Start(); |
||||||
|
|
||||||
|
// init eap...
|
||||||
|
AsyncCoreInitialize(); |
||||||
|
|
||||||
|
NetClientInitialize(); |
||||||
|
NetClientSetErrorHandler(IEapSucksErrorProc); |
||||||
|
NetClientSetTransTimeoutMs(kNetTransTimeout); |
||||||
|
|
||||||
|
// Gotta grab the filesrvs from the gate
|
||||||
|
const char** addrs; |
||||||
|
uint32_t num = GetGateKeeperSrvHostnames(&addrs); |
||||||
|
|
||||||
|
NetCliGateKeeperStartConnect(addrs, num); |
||||||
|
NetCliGateKeeperFileSrvIpAddressRequest(IGotFileServIPs, this, true); |
||||||
|
} |
||||||
|
|
||||||
|
// ===================================================
|
||||||
|
|
||||||
|
void plClientLauncher::PumpNetCore() const |
||||||
|
{ |
||||||
|
// this ain't net core, but it needs to be pumped :(
|
||||||
|
hsTimer::IncSysSeconds(); |
||||||
|
|
||||||
|
// pump eap
|
||||||
|
NetClientUpdate(); |
||||||
|
|
||||||
|
// pump shard status
|
||||||
|
fStatusThread->Update(); |
||||||
|
|
||||||
|
// don't nom all the CPU... kthx
|
||||||
|
hsSleep::Sleep(kNetCoreUpdateSleepTime); |
||||||
|
} |
||||||
|
|
||||||
|
void plClientLauncher::ShutdownNetCore() const |
||||||
|
{ |
||||||
|
// shutdown shard status
|
||||||
|
fStatusThread->Shutdown(); |
||||||
|
|
||||||
|
// unhook the neterr callback at this point because all transactions
|
||||||
|
// will fail when we call NetClientDestroy
|
||||||
|
s_errorProc = nullptr; |
||||||
|
|
||||||
|
// shutdown eap
|
||||||
|
NetCliGateKeeperDisconnect(); |
||||||
|
NetCliFileDisconnect(); |
||||||
|
NetClientDestroy(); |
||||||
|
|
||||||
|
// shutdown eap (part deux)
|
||||||
|
AsyncCoreDestroy(kAsyncCoreShutdownTime); |
||||||
|
} |
||||||
|
|
||||||
|
// ===================================================
|
||||||
|
|
||||||
|
bool plClientLauncher::LoadServerIni() const |
||||||
|
{ |
||||||
|
PF_CONSOLE_INITIALIZE(Core); |
||||||
|
|
||||||
|
pfConsoleEngine console; |
||||||
|
return console.ExecuteFile(fServerIni); |
||||||
|
} |
||||||
|
|
||||||
|
void plClientLauncher::ParseArguments() |
||||||
|
{ |
||||||
|
#define APPLY_FLAG(arg, flag) \ |
||||||
|
if (cmdParser.GetBool(arg)) \
|
||||||
|
fFlags |= flag; |
||||||
|
|
||||||
|
enum { kArgServerIni, kArgNoSelfPatch, kArgImage, kArgRepairGame }; |
||||||
|
const CmdArgDef cmdLineArgs[] = { |
||||||
|
{ kCmdArgFlagged | kCmdTypeString, L"ServerIni", kArgServerIni }, |
||||||
|
{ kCmdArgFlagged | kCmdTypeBool, L"NoSelfPatch", kArgNoSelfPatch }, |
||||||
|
{ kCmdArgFlagged | kCmdTypeBool, L"Image", kArgImage }, |
||||||
|
{ kCmdArgFlagged | kCmdTypeBool, L"Repair", kArgRepairGame }, |
||||||
|
}; |
||||||
|
|
||||||
|
CCmdParser cmdParser(cmdLineArgs, arrsize(cmdLineArgs)); |
||||||
|
cmdParser.Parse(); |
||||||
|
|
||||||
|
// cache 'em
|
||||||
|
if (cmdParser.IsSpecified(kArgServerIni)) |
||||||
|
fServerIni = plString::FromWchar(cmdParser.GetString(kArgServerIni)); |
||||||
|
APPLY_FLAG(kArgNoSelfPatch, kHaveSelfPatched); |
||||||
|
APPLY_FLAG(kArgImage, kClientImage); |
||||||
|
APPLY_FLAG(kArgRepairGame, kRepairGame); |
||||||
|
|
||||||
|
// last chance setup
|
||||||
|
if (hsCheckBits(fFlags, kRepairGame)) |
||||||
|
fClientExecutable = plManifest::PatcherExecutable(); |
||||||
|
|
||||||
|
#undef APPLY_FLAG |
||||||
|
} |
||||||
|
|
||||||
|
void plClientLauncher::SetErrorProc(ErrorFunc proc) |
||||||
|
{ |
||||||
|
s_errorProc = proc; |
||||||
|
} |
||||||
|
|
||||||
|
void plClientLauncher::SetInstallerProc(InstallRedistFunc proc) |
||||||
|
{ |
||||||
|
fInstallerThread->fInstallProc = proc; |
||||||
|
} |
||||||
|
|
||||||
|
void plClientLauncher::SetShardProc(StatusFunc proc) |
||||||
|
{ |
||||||
|
fStatusThread->fShardFunc = proc; |
||||||
|
} |
@ -0,0 +1,165 @@ |
|||||||
|
/*==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==*/ |
||||||
|
|
||||||
|
#ifndef _plClientLauncher_inc_ |
||||||
|
#define _plClientLauncher_inc_ |
||||||
|
|
||||||
|
#include "plFileSystem.h" |
||||||
|
#include "pnNetBase/pnNbError.h" |
||||||
|
|
||||||
|
#include <functional> |
||||||
|
#include <memory> |
||||||
|
|
||||||
|
class plClientLauncher |
||||||
|
{ |
||||||
|
public: |
||||||
|
typedef std::function<class pfPatcher*(void)> CreatePatcherFunc; |
||||||
|
typedef std::function<void(ENetError, const plString&)> ErrorFunc; |
||||||
|
typedef std::function<bool(const plFileName&)> InstallRedistFunc; |
||||||
|
typedef std::function<void(const plFileName&, const plString&)> LaunchClientFunc; |
||||||
|
typedef std::function<void(const plString&)> StatusFunc; |
||||||
|
|
||||||
|
private: |
||||||
|
enum Flags |
||||||
|
{ |
||||||
|
kHaveSelfPatched = 1<<0, |
||||||
|
kClientImage = 1<<1, |
||||||
|
kGameDataOnly = (1<<2), |
||||||
|
|
||||||
|
kRepairGame = kHaveSelfPatched | kClientImage | kGameDataOnly, |
||||||
|
}; |
||||||
|
|
||||||
|
uint32_t fFlags; |
||||||
|
plFileName fServerIni; |
||||||
|
|
||||||
|
plFileName fClientExecutable; |
||||||
|
|
||||||
|
std::unique_ptr<class plShardStatus> fStatusThread; |
||||||
|
std::unique_ptr<class plRedistUpdater> fInstallerThread; |
||||||
|
|
||||||
|
CreatePatcherFunc fPatcherFactory; |
||||||
|
LaunchClientFunc fLaunchClientFunc; |
||||||
|
StatusFunc fStatusFunc; |
||||||
|
|
||||||
|
plString GetAppArgs() const; |
||||||
|
|
||||||
|
void IOnPatchComplete(ENetError result, const plString& msg); |
||||||
|
bool IApproveDownload(const plFileName& file); |
||||||
|
|
||||||
|
public: |
||||||
|
plClientLauncher(); |
||||||
|
~plClientLauncher(); |
||||||
|
|
||||||
|
/** Launch whatever client we think is appropriate. Please note that you should not call this unless you know
|
||||||
|
* absolutely without question what you are doing! |
||||||
|
*/ |
||||||
|
void LaunchClient() const; |
||||||
|
|
||||||
|
/** Begin the next logical patch operation. We are internally tracking if this is a self patch or a client patch.
|
||||||
|
* All you need to do is make certain the doggone callbacks are set so that your UI will update. In theory, you |
||||||
|
* should never call this from your UI code since we manage this state for you. |
||||||
|
*/ |
||||||
|
void PatchClient(); |
||||||
|
|
||||||
|
/** Attempt to complete a self-patch left in progress by an older launcher. Specifically, we want to rename
|
||||||
|
* the launcher to something sane (UruLauncher.exe.tmp -> UruLauncher.exe). If we complete a self-patch in
|
||||||
|
* here, then we need to relaunch ourselves so that the game client will look like what the server expects. |
||||||
|
* \returns True if a self-patch was completed. False if not. |
||||||
|
*/ |
||||||
|
bool CompleteSelfPatch(std::function<void(void)> waitProc) const; |
||||||
|
|
||||||
|
/** Start eap's weird network subsystem and the shard status pinger.
|
||||||
|
* \remarks Please note that this will also enqueue the first patch. |
||||||
|
*/ |
||||||
|
void InitializeNetCore(); |
||||||
|
|
||||||
|
/** This pumps eap's network subsystem and runs any queued transaction completion callbacks.
|
||||||
|
* The thread that you call this from will be the thread that all your UI updates come from. |
||||||
|
* So be certain that you've thought that through! |
||||||
|
* \remarks This method will cause the thread to sleep so that we don't hog the CPU. |
||||||
|
*/ |
||||||
|
void PumpNetCore() const; |
||||||
|
|
||||||
|
/** Shutdown eap's netcore and purge any other crap that needs to happen while the app is
|
||||||
|
* visible. In other words, tear down evil threaded crap. |
||||||
|
*/ |
||||||
|
void ShutdownNetCore() const; |
||||||
|
|
||||||
|
/** Load the server configuration file. Note that you MUST parse the command
|
||||||
|
* arguments before calling this function! |
||||||
|
*/ |
||||||
|
bool LoadServerIni() const; |
||||||
|
|
||||||
|
/** Parse the command line options. */ |
||||||
|
void ParseArguments(); |
||||||
|
|
||||||
|
/** Set a callback function that is called on a network error.
|
||||||
|
* \remarks This will be called from the network thread. |
||||||
|
*/ |
||||||
|
void SetErrorProc(ErrorFunc proc); |
||||||
|
|
||||||
|
/** Set a callback that will execute and wait for redistributable installers.
|
||||||
|
* \remarks This will be called from a worker thread. |
||||||
|
*/ |
||||||
|
void SetInstallerProc(InstallRedistFunc proc); |
||||||
|
|
||||||
|
/** Set a patcher factory. */ |
||||||
|
void SetPatcherFactory(CreatePatcherFunc factory) { fPatcherFactory = factory; } |
||||||
|
|
||||||
|
/** Set a callback that launches an arbitrary executable.
|
||||||
|
* \remarks This will be called from an arbitrary thread. |
||||||
|
*/ |
||||||
|
void SetLaunchClientProc(LaunchClientFunc proc) { fLaunchClientFunc = proc; } |
||||||
|
|
||||||
|
/** Set a callback that displays the shard status.
|
||||||
|
* \remarks This will be called from a worker thread. |
||||||
|
*/ |
||||||
|
void SetShardProc(StatusFunc proc); |
||||||
|
|
||||||
|
/** Set a callback that displays the patcher status.
|
||||||
|
* \remarks This will be called from the network thread. Note that any time |
||||||
|
* this is called, you should consider it a state reset (so undo your progress bars). |
||||||
|
*/ |
||||||
|
void SetStatusProc(StatusFunc proc) { fStatusFunc = proc; } |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // _plClientLauncher_inc_
|
@ -1,102 +0,0 @@ |
|||||||
/*==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==*/ |
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/plLauncherCallback.h |
|
||||||
*
|
|
||||||
***/ |
|
||||||
|
|
||||||
#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PLLAUNCHERCALLBACK_H |
|
||||||
#error "Header $/Plasma20/Sources/Plasma/Apps/plUruLauncher/plLauncherCallback.h included more than once" |
|
||||||
#endif |
|
||||||
#define PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PLLAUNCHERCALLBACK_H |
|
||||||
|
|
||||||
enum EStatus { |
|
||||||
kStatusOk, |
|
||||||
kStatusError, |
|
||||||
kStatusPending, |
|
||||||
}; |
|
||||||
|
|
||||||
struct PatchInfo { |
|
||||||
unsigned progress; |
|
||||||
unsigned stage; |
|
||||||
unsigned progressStage; |
|
||||||
}; |
|
||||||
|
|
||||||
typedef void (*launcherCallback)(int status, void *param); |
|
||||||
typedef void (*setTextCallback)(const char text[]); |
|
||||||
typedef void (*setStatusTextCallback)(const char text[]); |
|
||||||
typedef void (*setTimeRemainingCallback)(unsigned seconds); |
|
||||||
typedef void (*setBytesRemainingCallback)(unsigned bytes); |
|
||||||
|
|
||||||
struct plLauncherInfo { |
|
||||||
wchar_t path[MAX_PATH]; |
|
||||||
wchar_t cmdLine[512]; |
|
||||||
unsigned buildId; // buildId override
|
|
||||||
launcherCallback prepCallback; |
|
||||||
launcherCallback initCallback; |
|
||||||
launcherCallback startCallback; |
|
||||||
launcherCallback stopCallback; |
|
||||||
launcherCallback terminateCallback; |
|
||||||
launcherCallback progressCallback; |
|
||||||
launcherCallback exitCallback; |
|
||||||
setTextCallback SetText;
|
|
||||||
setStatusTextCallback SetStatusText; |
|
||||||
setTimeRemainingCallback SetTimeRemaining; |
|
||||||
setBytesRemainingCallback SetBytesRemaining; |
|
||||||
|
|
||||||
PatchInfo patchInfo; |
|
||||||
DWORD returnCode; // used so we can pass a new process id back to gametap. That way gametap wont think uru has exited when the patcher quits.
|
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
* |
|
||||||
* Main.cpp |
|
||||||
* |
|
||||||
***/ |
|
||||||
|
|
||||||
void SetProgress (unsigned progress) ; |
|
||||||
void SetText (const char text[]); |
|
||||||
void SetStatusText (const char text[]); |
|
||||||
void SetTimeRemaining(unsigned seconds); |
|
||||||
void SetBytesRemaining(unsigned bytes); |
|
@ -0,0 +1,428 @@ |
|||||||
|
/*==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 "HeadSpin.h" |
||||||
|
#include "plFileSystem.h" |
||||||
|
#include "plProduct.h" |
||||||
|
|
||||||
|
#include "pfPatcher/plManifests.h" |
||||||
|
#include "pfPatcher/pfPatcher.h" |
||||||
|
|
||||||
|
#include "plClientLauncher.h" |
||||||
|
|
||||||
|
#include "hsWindows.h" |
||||||
|
#include "resource.h" |
||||||
|
#include <commctrl.h> |
||||||
|
#include <shellapi.h> |
||||||
|
#include <shlobj.h> |
||||||
|
|
||||||
|
// ===================================================
|
||||||
|
|
||||||
|
#define PLASMA_PHAILURE 1 |
||||||
|
#define PLASMA_OK 0 |
||||||
|
|
||||||
|
static HWND s_dialog; |
||||||
|
static plString s_error; // This is highly unfortunate.
|
||||||
|
static plClientLauncher s_launcher; |
||||||
|
static UINT s_taskbarCreated = RegisterWindowMessageW(L"TaskbarButtonCreated"); |
||||||
|
static ITaskbarList3* s_taskbar = nullptr; |
||||||
|
|
||||||
|
// ===================================================
|
||||||
|
|
||||||
|
/** Create a global patcher mutex that is backwards compatible with eap's */ |
||||||
|
static HANDLE CreatePatcherMutex() |
||||||
|
{ |
||||||
|
return CreateMutexW(nullptr, FALSE, plManifest::PatcherExecutable().AsString().ToWchar()); |
||||||
|
} |
||||||
|
|
||||||
|
static bool IsPatcherRunning() |
||||||
|
{ |
||||||
|
HANDLE mut = CreatePatcherMutex(); |
||||||
|
return WaitForSingleObject(mut, 0) != WAIT_OBJECT_0; |
||||||
|
} |
||||||
|
|
||||||
|
static void WaitForOldPatcher() |
||||||
|
{ |
||||||
|
HANDLE mut = CreatePatcherMutex(); |
||||||
|
WaitForSingleObject(mut, INFINITE); |
||||||
|
} |
||||||
|
|
||||||
|
// ===================================================
|
||||||
|
|
||||||
|
static inline void IShowErrorDialog(const wchar_t* msg) |
||||||
|
{ |
||||||
|
// This bypasses all that hsClientMinimizeGuard crap we have in CoreLib.
|
||||||
|
MessageBoxW(nullptr, msg, L"Error", MB_ICONERROR | MB_OK); |
||||||
|
} |
||||||
|
|
||||||
|
static inline void IQuit(int exitCode=PLASMA_OK) |
||||||
|
{ |
||||||
|
// hey, guess what?
|
||||||
|
// PostQuitMessage doesn't work if you're not on the main thread...
|
||||||
|
PostMessageW(s_dialog, WM_QUIT, exitCode, 0); |
||||||
|
} |
||||||
|
|
||||||
|
static inline void IShowMarquee(bool marquee=true) |
||||||
|
{ |
||||||
|
// NOTE: This is a HACK to workaround a bug that causes progress bars that were ever
|
||||||
|
// marquees to reanimate when changing the range or position
|
||||||
|
ShowWindow(GetDlgItem(s_dialog, IDC_MARQUEE), marquee ? SW_SHOW : SW_HIDE); |
||||||
|
ShowWindow(GetDlgItem(s_dialog, IDC_PROGRESS), marquee ? SW_HIDE : SW_SHOW); |
||||||
|
PostMessageW(GetDlgItem(s_dialog, IDC_MARQUEE), PBM_SETMARQUEE, static_cast<WPARAM>(marquee), 0); |
||||||
|
} |
||||||
|
|
||||||
|
BOOL CALLBACK PatcherDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) |
||||||
|
{ |
||||||
|
// NT6 Taskbar Majick
|
||||||
|
if (uMsg == s_taskbarCreated) { |
||||||
|
if (s_taskbar) |
||||||
|
s_taskbar->Release(); |
||||||
|
HRESULT result = CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_ALL, IID_ITaskbarList3, (void**)&s_taskbar); |
||||||
|
if (FAILED(result)) |
||||||
|
s_taskbar = nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
switch (uMsg) { |
||||||
|
case WM_COMMAND: |
||||||
|
// Did they press cancel?
|
||||||
|
if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL) { |
||||||
|
EnableWindow(GetDlgItem(s_dialog, IDCANCEL), false); |
||||||
|
SetWindowTextW(GetDlgItem(s_dialog, IDC_TEXT), L"Shutting Down..."); |
||||||
|
IQuit(); |
||||||
|
} |
||||||
|
break; |
||||||
|
case WM_DESTROY: |
||||||
|
if (s_taskbar) |
||||||
|
s_taskbar->Release(); |
||||||
|
PostQuitMessage(PLASMA_OK); |
||||||
|
s_dialog = nullptr; |
||||||
|
break; |
||||||
|
case WM_NCHITTEST: |
||||||
|
SetWindowLongW(hwndDlg, DWL_MSGRESULT, (LONG_PTR)HTCAPTION); |
||||||
|
return TRUE; |
||||||
|
case WM_QUIT: |
||||||
|
s_launcher.ShutdownNetCore(); |
||||||
|
DestroyWindow(hwndDlg); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
return DefWindowProcW(hwndDlg, uMsg, wParam, lParam); |
||||||
|
} |
||||||
|
|
||||||
|
static void ShowPatcherDialog(HINSTANCE hInstance) |
||||||
|
{ |
||||||
|
s_dialog = ::CreateDialogW(hInstance, MAKEINTRESOURCEW(IDD_DIALOG), nullptr, PatcherDialogProc); |
||||||
|
SetDlgItemTextW(s_dialog, IDC_TEXT, L"Connecting..."); |
||||||
|
SetDlgItemTextW(s_dialog, IDC_PRODUCTSTRING, plProduct::ProductString().ToWchar()); |
||||||
|
SetDlgItemTextW(s_dialog, IDC_DLSIZE, L""); |
||||||
|
SetDlgItemTextW(s_dialog, IDC_DLSPEED, L""); |
||||||
|
IShowMarquee(); |
||||||
|
} |
||||||
|
|
||||||
|
static void PumpMessages() |
||||||
|
{ |
||||||
|
MSG msg; |
||||||
|
do { |
||||||
|
// Pump all Win32 messages
|
||||||
|
while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) { |
||||||
|
if (!IsDialogMessageW(s_dialog, &msg)) { |
||||||
|
TranslateMessage(&msg); |
||||||
|
DispatchMessageW(&msg); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Now we need to pump the netcore while we have some spare time...
|
||||||
|
s_launcher.PumpNetCore(); |
||||||
|
} while (msg.message != WM_QUIT); |
||||||
|
} |
||||||
|
|
||||||
|
// ===================================================
|
||||||
|
|
||||||
|
static void IOnDownloadBegin(const plFileName& file) |
||||||
|
{ |
||||||
|
plString msg = plString::Format("Downloading... %s", file.AsString().c_str()); |
||||||
|
SetDlgItemTextW(s_dialog, IDC_TEXT, msg.ToWchar()); |
||||||
|
} |
||||||
|
|
||||||
|
static void IOnProgressTick(uint64_t curBytes, uint64_t totalBytes, const plString& status) |
||||||
|
{ |
||||||
|
// Swap marquee/real progress
|
||||||
|
IShowMarquee(false); |
||||||
|
|
||||||
|
// DL size
|
||||||
|
plString size = plString::Format("%s / %s", plFileSystem::ConvertFileSize(curBytes).c_str(), |
||||||
|
plFileSystem::ConvertFileSize(totalBytes).c_str()); |
||||||
|
SetDlgItemTextW(s_dialog, IDC_DLSIZE, size.ToWchar()); |
||||||
|
|
||||||
|
// DL speed
|
||||||
|
SetDlgItemTextW(s_dialog, IDC_DLSPEED, status.ToWchar()); |
||||||
|
HWND progress = GetDlgItem(s_dialog, IDC_PROGRESS); |
||||||
|
|
||||||
|
// hey look... ULONGLONG. that's exactly what we need >.<
|
||||||
|
if (s_taskbar) |
||||||
|
s_taskbar->SetProgressValue(s_dialog, curBytes, totalBytes); |
||||||
|
|
||||||
|
// Windows can only do signed 32-bit int progress bars.
|
||||||
|
// So, chop it into smaller chunks until we get something we can represent.
|
||||||
|
while (totalBytes > INT32_MAX) { |
||||||
|
totalBytes /= 1024; |
||||||
|
curBytes /= 1024; |
||||||
|
} |
||||||
|
|
||||||
|
PostMessageW(progress, PBM_SETRANGE32, 0, static_cast<int32_t>(totalBytes)); |
||||||
|
PostMessageW(progress, PBM_SETPOS, static_cast<int32_t>(curBytes), 0); |
||||||
|
} |
||||||
|
|
||||||
|
// ===================================================
|
||||||
|
|
||||||
|
static void ISetDownloadStatus(const plString& status) |
||||||
|
{ |
||||||
|
SetDlgItemTextW(s_dialog, IDC_TEXT, status.ToWchar()); |
||||||
|
|
||||||
|
// consider this a reset of the download status...
|
||||||
|
IShowMarquee(); |
||||||
|
SetDlgItemTextW(s_dialog, IDC_DLSIZE, L""); |
||||||
|
SetDlgItemTextW(s_dialog, IDC_DLSPEED, L""); |
||||||
|
|
||||||
|
if (s_taskbar) |
||||||
|
s_taskbar->SetProgressState(s_dialog, TBPF_INDETERMINATE); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static HANDLE ICreateProcess(const plFileName& exe, const plString& args) |
||||||
|
{ |
||||||
|
STARTUPINFOW si; |
||||||
|
PROCESS_INFORMATION pi; |
||||||
|
memset(&si, 0, sizeof(si)); |
||||||
|
memset(&pi, 0, sizeof(pi)); |
||||||
|
si.cb = sizeof(si); |
||||||
|
|
||||||
|
// Create wchar things and stuff :/
|
||||||
|
plString cmd = plString::Format("%s %s", exe.AsString().c_str(), args.c_str()); |
||||||
|
plStringBuffer<wchar_t> file = exe.AsString().ToWchar(); |
||||||
|
plStringBuffer<wchar_t> params = cmd.ToWchar(); |
||||||
|
|
||||||
|
// Guess what? CreateProcess isn't smart enough to throw up an elevation dialog... We need ShellExecute for that.
|
||||||
|
// But guess what? ShellExecute won't run ".exe.tmp" files. GAAAAAAAAHHHHHHHHH!!!!!!!
|
||||||
|
BOOL result = CreateProcessW( |
||||||
|
file, |
||||||
|
const_cast<wchar_t*>(params.GetData()), |
||||||
|
nullptr, |
||||||
|
nullptr, |
||||||
|
FALSE, |
||||||
|
DETACHED_PROCESS, |
||||||
|
nullptr, |
||||||
|
nullptr, |
||||||
|
&si, |
||||||
|
&pi |
||||||
|
); |
||||||
|
|
||||||
|
// So maybe it needs elevation... Or maybe everything arseploded.
|
||||||
|
if (result != FALSE) { |
||||||
|
CloseHandle(pi.hThread); |
||||||
|
return pi.hProcess; |
||||||
|
} else if (GetLastError() == ERROR_ELEVATION_REQUIRED) { |
||||||
|
SHELLEXECUTEINFOW info; |
||||||
|
memset(&info, 0, sizeof(info)); |
||||||
|
info.cbSize = sizeof(info); |
||||||
|
info.lpFile = file.GetData(); |
||||||
|
info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NOASYNC; |
||||||
|
info.lpParameters = args.ToWchar(); |
||||||
|
hsAssert(ShellExecuteExW(&info), "ShellExecuteExW phailed"); |
||||||
|
|
||||||
|
return info.hProcess; |
||||||
|
} else { |
||||||
|
wchar_t* msg = nullptr; |
||||||
|
FormatMessageW( |
||||||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, |
||||||
|
nullptr, |
||||||
|
GetLastError(), |
||||||
|
LANG_USER_DEFAULT, |
||||||
|
msg, |
||||||
|
0, |
||||||
|
nullptr |
||||||
|
); |
||||||
|
s_error = plString::FromWchar(msg); |
||||||
|
LocalFree(msg); |
||||||
|
} |
||||||
|
|
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
static bool IInstallRedist(const plFileName& exe) |
||||||
|
{ |
||||||
|
ISetDownloadStatus(plString::Format("Installing... %s", exe.AsString().c_str())); |
||||||
|
Sleep(2500); // let's Sleep for a bit so the user can see that we're doing something before the UAC dialog pops up!
|
||||||
|
|
||||||
|
// Try to guess some arguments... Unfortunately, the file manifest format is fairly immutable.
|
||||||
|
plStringStream ss; |
||||||
|
if (exe.AsString().CompareI("oalinst.exe") == 0) |
||||||
|
ss << "/s"; // rarg nonstandard
|
||||||
|
else |
||||||
|
ss << "/q"; |
||||||
|
if (exe.AsString().Find("vcredist", plString::kCaseInsensitive) != -1) |
||||||
|
ss << " /norestart"; // I don't want to image the accusations of viruses and hacking if this happened...
|
||||||
|
|
||||||
|
// Now fire up the process...
|
||||||
|
HANDLE process = ICreateProcess(exe, ss.GetString()); |
||||||
|
if (process) { |
||||||
|
WaitForSingleObject(process, INFINITE); |
||||||
|
|
||||||
|
// Get the exit code so we can indicate success/failure to the redist thread
|
||||||
|
DWORD code = PLASMA_OK; |
||||||
|
hsAssert(GetExitCodeProcess(process, &code), "failed to get redist exit code"); |
||||||
|
CloseHandle(process); |
||||||
|
|
||||||
|
return code != PLASMA_PHAILURE; |
||||||
|
} |
||||||
|
return PLASMA_PHAILURE; |
||||||
|
} |
||||||
|
|
||||||
|
static void ILaunchClientExecutable(const plFileName& exe, const plString& args) |
||||||
|
{ |
||||||
|
// Once we start launching something, we no longer need to trumpet any taskbar status
|
||||||
|
if (s_taskbar) |
||||||
|
s_taskbar->SetProgressState(s_dialog, TBPF_NOPROGRESS); |
||||||
|
|
||||||
|
// Only launch a client executable if we're given one. If not, that's probably a cue that we're
|
||||||
|
// done with some service operation and need to go away.
|
||||||
|
if (!exe.AsString().IsEmpty()) { |
||||||
|
HANDLE hEvent = CreateEventW(nullptr, TRUE, FALSE, L"UruPatcherEvent"); |
||||||
|
HANDLE process = ICreateProcess(exe, args); |
||||||
|
|
||||||
|
// if this is the real game client, then we need to make sure it gets this event...
|
||||||
|
if (plManifest::ClientExecutable().AsString().CompareI(exe.AsString()) == 0) { |
||||||
|
WaitForInputIdle(process, 1000); |
||||||
|
WaitForSingleObject(hEvent, INFINITE); |
||||||
|
} |
||||||
|
|
||||||
|
CloseHandle(process); |
||||||
|
CloseHandle(hEvent); |
||||||
|
} |
||||||
|
|
||||||
|
// time to hara-kiri...
|
||||||
|
IQuit(); |
||||||
|
} |
||||||
|
|
||||||
|
static void IOnNetError(ENetError result, const plString& msg) |
||||||
|
{ |
||||||
|
if (s_taskbar) |
||||||
|
s_taskbar->SetProgressState(s_dialog, TBPF_ERROR); |
||||||
|
|
||||||
|
s_error = plString::Format("Error: %S\r\n%s", NetErrorAsString(result), msg.c_str()); |
||||||
|
IQuit(PLASMA_PHAILURE); |
||||||
|
} |
||||||
|
|
||||||
|
static void ISetShardStatus(const plString& status) |
||||||
|
{ |
||||||
|
SetDlgItemTextW(s_dialog, IDC_STATUS_TEXT, status.ToWchar()); |
||||||
|
} |
||||||
|
|
||||||
|
static pfPatcher* IPatcherFactory() |
||||||
|
{ |
||||||
|
pfPatcher* patcher = new pfPatcher(); |
||||||
|
patcher->OnFileDownloadBegin(IOnDownloadBegin); |
||||||
|
patcher->OnProgressTick(IOnProgressTick); |
||||||
|
|
||||||
|
return patcher; |
||||||
|
} |
||||||
|
|
||||||
|
// ===================================================
|
||||||
|
|
||||||
|
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLink, int nCmdShow) |
||||||
|
{ |
||||||
|
// Let's initialize our plClientLauncher friend
|
||||||
|
s_launcher.ParseArguments(); |
||||||
|
s_launcher.SetErrorProc(IOnNetError); |
||||||
|
s_launcher.SetInstallerProc(IInstallRedist); |
||||||
|
s_launcher.SetLaunchClientProc(ILaunchClientExecutable); |
||||||
|
s_launcher.SetPatcherFactory(IPatcherFactory); |
||||||
|
s_launcher.SetShardProc(ISetShardStatus); |
||||||
|
s_launcher.SetStatusProc(ISetDownloadStatus); |
||||||
|
|
||||||
|
// If we're newly updated, then our filename will be something we don't expect!
|
||||||
|
// Let's go ahead and take care of that nao.
|
||||||
|
if (s_launcher.CompleteSelfPatch(WaitForOldPatcher)) |
||||||
|
return PLASMA_OK; // see you on the other side...
|
||||||
|
|
||||||
|
// Load the doggone server.ini
|
||||||
|
if (!s_launcher.LoadServerIni()) { |
||||||
|
IShowErrorDialog(L"No server.ini file found. Please check your URU installation."); |
||||||
|
return PLASMA_PHAILURE; |
||||||
|
} |
||||||
|
|
||||||
|
// Ensure there is only ever one patcher running...
|
||||||
|
if (IsPatcherRunning()) { |
||||||
|
plString text = plString::Format("%s is already running", plProduct::LongName().c_str()); |
||||||
|
IShowErrorDialog(text.ToWchar()); |
||||||
|
return PLASMA_OK; |
||||||
|
} |
||||||
|
HANDLE _onePatcherMut = CreatePatcherMutex(); |
||||||
|
|
||||||
|
// Initialize the network core
|
||||||
|
s_launcher.InitializeNetCore(); |
||||||
|
|
||||||
|
// Welp, now that we know we're (basically) sane, let's create our client window
|
||||||
|
// and pump window messages until we're through.
|
||||||
|
ShowPatcherDialog(hInstance); |
||||||
|
PumpMessages(); |
||||||
|
|
||||||
|
// So there appears to be some sort of issue with calling MessageBox once we've set up our dialog...
|
||||||
|
// WTF?!?! So, to hack around that, we'll wait until everything shuts down to display any error.
|
||||||
|
if (!s_error.IsEmpty()) |
||||||
|
IShowErrorDialog(s_error.ToWchar()); |
||||||
|
|
||||||
|
// Alrighty now we just need to clean up behind ourselves!
|
||||||
|
// NOTE: We shut down the netcore in the WM_QUIT handler so
|
||||||
|
// we don't have a windowless, zombie process if that takes
|
||||||
|
// awhile (it can... dang eap...)
|
||||||
|
ReleaseMutex(_onePatcherMut); |
||||||
|
CloseHandle(_onePatcherMut); |
||||||
|
|
||||||
|
// kthxbai
|
||||||
|
return s_error.IsEmpty() ? PLASMA_OK : PLASMA_PHAILURE; |
||||||
|
} |
||||||
|
|
||||||
|
/* Enable themes in Windows XP and later */ |
||||||
|
#pragma comment(linker,"\"/manifestdependency:type='win32' \ |
||||||
|
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
|
||||||
|
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") |
Loading…
Reference in new issue