From 7da046760971556d2137294a4a5b00ecfc7219f2 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sun, 24 Nov 2013 16:24:37 -0500 Subject: [PATCH 1/6] plString-ify plOperationProgress --- Sources/Plasma/Apps/plClient/plClient.cpp | 8 ++--- .../PubUtilLib/plAgeLoader/plResPatcher.cpp | 2 +- .../plNetClient/plNetCliAgeJoiner.cpp | 2 +- .../PubUtilLib/plPipeline/plDTProgressMgr.cpp | 10 +++---- .../plProgressMgr/plProgressMgr.cpp | 24 --------------- .../PubUtilLib/plProgressMgr/plProgressMgr.h | 30 +++++++++---------- 6 files changed, 26 insertions(+), 50 deletions(-) diff --git a/Sources/Plasma/Apps/plClient/plClient.cpp b/Sources/Plasma/Apps/plClient/plClient.cpp index 498df1c1..808f4222 100644 --- a/Sources/Plasma/Apps/plClient/plClient.cpp +++ b/Sources/Plasma/Apps/plClient/plClient.cpp @@ -1196,7 +1196,7 @@ void plClient::IRoomLoaded(plSceneNode* node, bool hold) }; char name[256]; - strcpy(name, &fProgressBar->GetTitle()[strlen("Loading ")]); + strcpy(name, &fProgressBar->GetTitle().c_str()[strlen("Loading ")]); name[strlen(name)-3] = '\0'; // Get the precalculated value for how many messages will be @@ -1222,7 +1222,7 @@ void plClient::IRoomLoaded(plSceneNode* node, bool hold) #ifndef PLASMA_EXTERNAL_RELEASE if (plDispatchLogBase::IsLogging()) - plDispatchLogBase::GetInstance()->LogStatusBarChange(fProgressBar->GetTitle(), "displaying messages"); + plDispatchLogBase::GetInstance()->LogStatusBarChange(fProgressBar->GetTitle().c_str(), "displaying messages"); #endif // PLASMA_EXTERNAL_RELEASE #endif } @@ -1349,7 +1349,7 @@ void plClient::IStartProgress( const char *title, float len ) fProgressBar = plProgressMgr::GetInstance()->RegisterOperation(len, title, plProgressMgr::kNone, false, true); #ifndef PLASMA_EXTERNAL_RELEASE if (plDispatchLogBase::IsLogging()) - plDispatchLogBase::GetInstance()->LogStatusBarChange(fProgressBar->GetTitle(), "starting"); + plDispatchLogBase::GetInstance()->LogStatusBarChange(fProgressBar->GetTitle().c_str(), "starting"); #endif // PLASMA_EXTERNAL_RELEASE ((plResManager*)hsgResMgr::ResMgr())->SetProgressBarProc(IReadKeyedObjCallback); @@ -1371,7 +1371,7 @@ void plClient::IStopProgress( void ) { #ifndef PLASMA_EXTERNAL_RELEASE if (plDispatchLogBase::IsLogging()) - plDispatchLogBase::GetInstance()->LogStatusBarChange(fProgressBar->GetTitle(), "done"); + plDispatchLogBase::GetInstance()->LogStatusBarChange(fProgressBar->GetTitle().c_str(), "done"); #endif // PLASMA_EXTERNAL_RELEASE plDispatch::SetMsgRecieveCallback(nil); diff --git a/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp b/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp index 446c4be1..896059de 100644 --- a/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp +++ b/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp @@ -249,7 +249,7 @@ void plResPatcher::IssueRequest() } } - fProgress->SetTitle(title.c_str()); + fProgress->SetTitle(title); } } diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp b/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp index 582e3599..4502bf7b 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp @@ -188,7 +188,7 @@ void plNCAgeJoiner::IDispatchMsgReceiveCallback () { void plNCAgeJoiner::IResMgrProgressBarCallback (plKey key) { #ifndef PLASMA_EXTERNAL_RELEASE if (s_instance) - s_instance->progressBar->SetStatusText(key->GetName().c_str()); + s_instance->progressBar->SetStatusText(key->GetName()); #endif } diff --git a/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp b/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp index 94ba4a42..ab197cb5 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp @@ -245,16 +245,16 @@ bool plDTProgressMgr::IDrawTheStupidThing(plPipeline *p, plOperationProgress if (drawText) { - if (prog->GetTitle()) + if (!prog->GetTitle().IsEmpty()) { - text.DrawString( x, y, prog->GetTitle(), (uint32_t)0xccb0b0b0 ); - x += (uint16_t)text.CalcStringWidth( prog->GetTitle() ); + text.DrawString_TEMP( x, y, prog->GetTitle(), (uint32_t)0xccb0b0b0 ); + x += (uint16_t)text.CalcStringWidth_TEMP( prog->GetTitle() ); drew_something = true; } - if (prog->GetStatusText()) + if (!prog->GetStatusText().IsEmpty()) { - text.DrawString( x, y, prog->GetStatusText(), (uint32_t)0xccb0b0b0 ); + text.DrawString_TEMP( x, y, prog->GetStatusText(), (uint32_t)0xccb0b0b0 ); drew_something = true; } } diff --git a/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.cpp b/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.cpp index a3ac71f2..363cc764 100644 --- a/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.cpp @@ -268,8 +268,6 @@ plOperationProgress::plOperationProgress( float length ) : fRemainingSecs(0), fAmtPerSec(0.f) { - memset( fStatusText, 0, sizeof( fStatusText ) ); - memset( fTitle, 0, sizeof( fTitle ) ); } plOperationProgress::~plOperationProgress() @@ -347,28 +345,6 @@ void plOperationProgress::SetHowMuch( float howMuch ) plProgressMgr::GetInstance()->IUpdateCallbackProc( this ); } -//// SetStatusText /////////////////////////////////////////////////////////// - -void plOperationProgress::SetStatusText( const char *text ) -{ - if( text != nil ) - strncpy( fStatusText, text, sizeof( fStatusText ) ); - else - fStatusText[ 0 ] = 0; -} - -//// SetTitle //////////////////////////////////////////////////////////////// - -void plOperationProgress::SetTitle( const char *text ) -{ - if (text != nil) - { - strncpy(fTitle, text, sizeof(fTitle)); - } - else - fTitle[0] = 0; -} - //// SetLength /////////////////////////////////////////////////////////////// void plOperationProgress::SetLength( float length ) diff --git a/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.h b/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.h index 7ecbf390..bf36035d 100644 --- a/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.h +++ b/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.h @@ -56,7 +56,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #define _plProgressMgr_h #include "HeadSpin.h" - +#include "plString.h" class plPipeline; class plPlate; @@ -74,10 +74,10 @@ class plOperationProgress protected: float fValue, fMax; - char fStatusText[ 256 ]; - char fTitle[ 256 ]; - uint32_t fContext; - double fStartTime; + plString fTitle; + plString fStatusText; + uint32_t fContext; + double fStartTime; uint32_t fElapsedSecs, fRemainingSecs; float fAmtPerSec; @@ -109,11 +109,11 @@ class plOperationProgress ~plOperationProgress(); - float GetMax( void ) const { return fMax; } - float GetProgress( void ) const { return fValue; } - const char * GetTitle( void ) const { return fTitle; } - const char * GetStatusText( void ) const { return fStatusText; } - uint32_t GetContext( void ) const { return fContext; } + float GetMax() const { return fMax; } + float GetProgress() const { return fValue; } + plString GetTitle() const { return fTitle; } + plString GetStatusText() const { return fStatusText; } + uint32_t GetContext() const { return fContext; } uint32_t GetElapsedSecs() { return fElapsedSecs; } uint32_t GetRemainingSecs() { return fRemainingSecs; } float GetAmtPerSec() { return fAmtPerSec; } @@ -127,16 +127,16 @@ class plOperationProgress // Set the length void SetLength( float length ); - // Sets the display text above the bar (nil for nothing) - void SetStatusText( const char *text ); + /** Sets the progress bar's status text */ + void SetStatusText(const plString& status) { fStatusText = status; } - // Sets the title - void SetTitle( const char *title ); + /** Sets the progress bar's title */ + void SetTitle(const plString& title) { fTitle = title; } // Application data void SetContext( uint32_t context ) { fContext = context;} - bool IsDone( void ) { return ( fValue < fMax ) ? false : true; } + bool IsDone() { return ( fValue < fMax ) ? false : true; } // True if this is the initial update (progress was just created) bool IsInitUpdate() { return hsCheckBits(fFlags, kInitUpdate); } From 275f15087c05ad27e87b1995b0ec68f7f37ac4fb Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sun, 24 Nov 2013 17:42:37 -0500 Subject: [PATCH 2/6] Separate plOperationProgress titles and statuses. Now we render titles above the progress bar and the status below (in the same color as the progress bar!). Also, introduce a new info field that is guaranteed to be right justified. --- .../plNetClient/plNetCliAgeJoiner.cpp | 10 +- .../PubUtilLib/plPipeline/plDTProgressMgr.cpp | 98 +++++++------------ .../PubUtilLib/plProgressMgr/plProgressMgr.h | 7 +- 3 files changed, 46 insertions(+), 69 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp b/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp index 4502bf7b..a1eefe80 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp @@ -252,12 +252,10 @@ void plNCAgeJoiner::ExecNextOp () { LogMsg(kLogPerf, L"AgeJoiner: Exec:kLoadAge"); // Start progress bar - char str[256]; - #ifdef PLASMA_EXTERNAL_RELEASE - StrCopy(str, "Loading age...", arrsize(str)); - #else - StrPrintf(str, arrsize(str), "Loading age %s...", age.ageDatasetName); - #endif + char str[128]; +#ifndef PLASMA_EXTERNAL_RELEASE + snprintf(str, arrsize(str), "Loading age... %s", age.ageDatasetName); +#endif progressBar = plProgressMgr::GetInstance()->RegisterOperation(0, str, plProgressMgr::kNone, false, true); plDispatch::SetMsgRecieveCallback(IDispatchMsgReceiveCallback); ((plResManager*)hsgResMgr::ResMgr())->SetProgressBarProc(IResMgrProgressBarCallback); diff --git a/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp b/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp index ab197cb5..0aec3e13 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp @@ -57,6 +57,14 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "hsTimer.h" +// Draw Colors +enum +{ + kTitleColor = 0xccb0b0b0, + kProgressBarColor = 0xff302b3a, + kInfoColor = 0xff635e6d, +}; + //// Constructor & Destructor //////////////////////////////////////////////// @@ -178,85 +186,51 @@ bool plDTProgressMgr::IDrawTheStupidThing(plPipeline *p, plOperationProgress { plDebugText &text = plDebugText::Instance(); bool drew_something = false; + uint16_t downsz = (text.GetFontSize() << 1) + 4; - // Lets just set the color to blue - uint32_t color = 0xff302b3a; + // draw the title + if (!prog->GetTitle().IsEmpty()) { + y -= downsz; + text.DrawString_TEMP(x, y, prog->GetTitle(), kTitleColor); + y += downsz; + drew_something = true; + } - if( prog->GetMax() > 0.f ) - { - text.Draw3DBorder(x, y, x + width - 1, y + height - 1, color, color); + // draw a progress bar + if (prog->GetMax() > 0.f) { + text.Draw3DBorder(x, y, x + width - 1, y + height - 1, kProgressBarColor, kProgressBarColor); x += 2; y += 2; width -= 4; height -= 4; - uint16_t drawWidth = width; - int16_t drawX = x; - uint16_t rightX = drawX + drawWidth; + uint16_t drawWidth = width; + int16_t drawX = x; + uint16_t rightX = drawX + drawWidth; if (prog->GetProgress() <= prog->GetMax()) - drawWidth = (uint16_t)( (float)width * prog->GetProgress() / prog->GetMax() ); + drawWidth = (uint16_t)((float)width * prog->GetProgress() / prog->GetMax()); rightX = drawX + drawWidth; + if (drawWidth > 0) + text.DrawRect(drawX, y, rightX, y + height, kProgressBarColor); + y += height + 2; - if( drawWidth > 0 ) - text.DrawRect( drawX, y, rightX, y + height, color ); - - uint32_t timeRemain = prog->fRemainingSecs; - if (timeRemain > 0) { - plStringStream ss; - ss << "APPROXIMATELY "; - if (timeRemain > 3600) - { - uint32_t hours = timeRemain / 3600; - const char* plural = (hours > 1) ? "S" : ""; - ss << hours << " HOUR" << plural << " "; - timeRemain %= 3600; - } - if (timeRemain > 60) - { - uint32_t minutes = timeRemain / 60; - const char* plural = (minutes > 1) ? "S" : ""; - ss << minutes << " MINUTE" << plural << " "; - timeRemain %= 60; - } - if (timeRemain > 0) - { - const char* plural = (timeRemain > 1) ? "S" : ""; - ss << timeRemain << " SECOND" << plural << " "; - } - ss << "REMAINING"; - text.DrawString(x, y + height + 2, ss.GetString().c_str(), (uint32_t)0xff635e6d); - } - - x -= 2; - y -= 2; drew_something = true; } - y -= ( text.GetFontSize() << 1 ) + 4; - -#ifndef PLASMA_EXTERNAL_RELEASE - static bool drawText = true; -#else - static bool drawText = false; -#endif - - if (drawText) - { - if (!prog->GetTitle().IsEmpty()) - { - text.DrawString_TEMP( x, y, prog->GetTitle(), (uint32_t)0xccb0b0b0 ); - x += (uint16_t)text.CalcStringWidth_TEMP( prog->GetTitle() ); - drew_something = true; - } + // draw the left justified status text + if (!prog->GetStatusText().IsEmpty()) { + text.DrawString_TEMP(x, y, prog->GetStatusText(), kInfoColor); + drew_something = true; + } - if (!prog->GetStatusText().IsEmpty()) - { - text.DrawString_TEMP( x, y, prog->GetStatusText(), (uint32_t)0xccb0b0b0 ); - drew_something = true; - } + // draw the right justified info text + if (!prog->GetInfoText().IsEmpty()) { + uint16_t right_x = 2 + x + width - text.CalcStringWidth_TEMP(prog->GetInfoText()); + text.DrawString_TEMP(right_x, y, prog->GetInfoText(), kInfoColor); + drew_something = true; } // return whether or not we drew stuff diff --git a/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.h b/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.h index bf36035d..4fc07eb7 100644 --- a/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.h +++ b/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.h @@ -76,6 +76,7 @@ class plOperationProgress float fValue, fMax; plString fTitle; plString fStatusText; + plString fInfoText; uint32_t fContext; double fStartTime; @@ -113,6 +114,7 @@ class plOperationProgress float GetProgress() const { return fValue; } plString GetTitle() const { return fTitle; } plString GetStatusText() const { return fStatusText; } + plString GetInfoText() const { return fInfoText; } uint32_t GetContext() const { return fContext; } uint32_t GetElapsedSecs() { return fElapsedSecs; } uint32_t GetRemainingSecs() { return fRemainingSecs; } @@ -127,7 +129,10 @@ class plOperationProgress // Set the length void SetLength( float length ); - /** Sets the progress bar's status text */ + /** Sets the progress bar's right justified info text */ + void SetInfoText(const plString& info) { fInfoText = info; } + + /** Sets the progress bar's left justified status text */ void SetStatusText(const plString& status) { fStatusText = status; } /** Sets the progress bar's title */ From 346b6f8ac866ba837d154dc836d73154a967f2ef Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sat, 23 Nov 2013 14:56:57 -0500 Subject: [PATCH 3/6] Implement pfPatcher backend --- Sources/Plasma/CoreLib/hsThread.h | 1 + Sources/Plasma/CoreLib/hsThread_Unix.cpp | 1 + Sources/Plasma/CoreLib/hsThread_Win.cpp | 1 + Sources/Plasma/CoreLib/plFileSystem.cpp | 24 + Sources/Plasma/CoreLib/plFileSystem.h | 3 + Sources/Plasma/FeatureLib/CMakeLists.txt | 1 + .../FeatureLib/pfPatcher/CMakeLists.txt | 19 + .../Plasma/FeatureLib/pfPatcher/pfPatcher.cpp | 450 ++++++++++++++++++ .../Plasma/FeatureLib/pfPatcher/pfPatcher.h | 112 +++++ .../FeatureLib/pfPatcher/plManifests.cpp | 90 ++++ .../Plasma/FeatureLib/pfPatcher/plManifests.h | 73 +++ 11 files changed, 775 insertions(+) create mode 100644 Sources/Plasma/FeatureLib/pfPatcher/CMakeLists.txt create mode 100644 Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp create mode 100644 Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.h create mode 100644 Sources/Plasma/FeatureLib/pfPatcher/plManifests.cpp create mode 100644 Sources/Plasma/FeatureLib/pfPatcher/plManifests.h diff --git a/Sources/Plasma/CoreLib/hsThread.h b/Sources/Plasma/CoreLib/hsThread.h index 49d0da2c..ae5e35f0 100644 --- a/Sources/Plasma/CoreLib/hsThread.h +++ b/Sources/Plasma/CoreLib/hsThread.h @@ -96,6 +96,7 @@ public: virtual hsError Run() = 0; // override this to do your work virtual void Start(); // initializes stuff and calls your Run() method virtual void Stop(); // sets fQuit = true and the waits for the thread to stop + virtual void OnQuit() { } // Static functions static void* Alloc(size_t size); // does not call operator::new(), may return nil diff --git a/Sources/Plasma/CoreLib/hsThread_Unix.cpp b/Sources/Plasma/CoreLib/hsThread_Unix.cpp index 4c3deb54..7ac17a66 100644 --- a/Sources/Plasma/CoreLib/hsThread_Unix.cpp +++ b/Sources/Plasma/CoreLib/hsThread_Unix.cpp @@ -76,6 +76,7 @@ extern "C" { pthread_mutex_lock(((hsThread*)param)->GetStartupMutex()); void* ret = (void*)(uintptr_t)((hsThread*)param)->Run(); pthread_mutex_unlock(((hsThread*)param)->GetStartupMutex()); + ((hsThread*)param)->OnQuit(); pthread_exit(ret); return ret; } diff --git a/Sources/Plasma/CoreLib/hsThread_Win.cpp b/Sources/Plasma/CoreLib/hsThread_Win.cpp index 31646497..4fb4e3c8 100644 --- a/Sources/Plasma/CoreLib/hsThread_Win.cpp +++ b/Sources/Plasma/CoreLib/hsThread_Win.cpp @@ -68,6 +68,7 @@ static unsigned int __stdcall gEntryPointBT(void* param) WinThreadParam* wtp = (WinThreadParam*)param; unsigned int result = wtp->fThread->Run(); ::ReleaseSemaphore(wtp->fQuitSemaH, 1, nil); // signal that we've quit + wtp->fThread->OnQuit(); delete param; return result; } diff --git a/Sources/Plasma/CoreLib/plFileSystem.cpp b/Sources/Plasma/CoreLib/plFileSystem.cpp index 59fd4c73..51aa3dc2 100644 --- a/Sources/Plasma/CoreLib/plFileSystem.cpp +++ b/Sources/Plasma/CoreLib/plFileSystem.cpp @@ -570,3 +570,27 @@ plFileName plFileSystem::GetTempFilename(const char *prefix, const plFileName &p return result; #endif } + +plString plFileSystem::ConvertFileSize(uint64_t size) +{ + const char* labels[] = { "KiB", "MiB", "GiB", "TiB", "PiB", "EiB" }; + if (size < 1024) + return plString::Format("%i B"); + + uint64_t last_div = size; + for (size_t i = 0; i < arrsize(labels); ++i) { + uint64_t my_div = last_div / 1024; + if (my_div < 1024) { + float decimal = static_cast(last_div) / 1024.f; + // Kilobytes are so small that we only care about whole numbers + if (i < 1) + return plString::Format("%.0f %s", decimal, labels[i]); + else + return plString::Format("%.2f %s", decimal, labels[i]); + } + last_div = my_div; + } + + // this should never happen + return plString::Format("%i %s", last_div, labels[arrsize(labels) - 1]); +} diff --git a/Sources/Plasma/CoreLib/plFileSystem.h b/Sources/Plasma/CoreLib/plFileSystem.h index 1b0bcb32..317e8a03 100644 --- a/Sources/Plasma/CoreLib/plFileSystem.h +++ b/Sources/Plasma/CoreLib/plFileSystem.h @@ -338,6 +338,9 @@ namespace plFileSystem * system temp path is used. */ plFileName GetTempFilename(const char *prefix = "tmp", const plFileName &path = ""); + + /** Convert a file size from bytes to a human readable size. */ + plString ConvertFileSize(uint64_t size); } #endif // plFileSystem_Defined diff --git a/Sources/Plasma/FeatureLib/CMakeLists.txt b/Sources/Plasma/FeatureLib/CMakeLists.txt index 8b2b13a5..07552faa 100644 --- a/Sources/Plasma/FeatureLib/CMakeLists.txt +++ b/Sources/Plasma/FeatureLib/CMakeLists.txt @@ -17,6 +17,7 @@ add_subdirectory(pfJournalBook) # add_subdirectory(pfKI) add_subdirectory(pfLocalizationMgr) add_subdirectory(pfMessage) +add_subdirectory(pfPatcher) add_subdirectory(pfPython) add_subdirectory(pfSecurePreloader) add_subdirectory(pfSurface) diff --git a/Sources/Plasma/FeatureLib/pfPatcher/CMakeLists.txt b/Sources/Plasma/FeatureLib/pfPatcher/CMakeLists.txt new file mode 100644 index 00000000..71a2e8dc --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfPatcher/CMakeLists.txt @@ -0,0 +1,19 @@ +include_directories("../../CoreLib") +include_directories("../../NucleusLib") +include_directories("../../NucleusLib/inc") +include_directories("../../PubUtilLib") + +set(pfPatcher_SOURCES + plManifests.cpp + pfPatcher.cpp +) + +set(pfPatcher_HEADERS + plManifests.h + pfPatcher.h +) + +add_library(pfPatcher STATIC ${pfPatcher_SOURCES} ${pfPatcher_HEADERS}) + +source_group("Source Files" FILES ${pfPatcher_SOURCES}) +source_group("Header Files" FILES ${pfPatcher_HEADERS}) diff --git a/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp b/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp new file mode 100644 index 00000000..080b9c01 --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp @@ -0,0 +1,450 @@ +/*==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 . + +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 +#include + +#include "pfPatcher.h" + +#include "HeadSpin.h" +#include "plCompression/plZlibStream.h" +#include "pnEncryption/plChecksum.h" +#include "plFileSystem.h" +#include "pnNetBase/pnNbError.h" +#include "plNetGameLib/plNetGameLib.h" +#include "plStatusLog/plStatusLog.h" +#include "hsStream.h" +#include "hsThread.h" +#include "hsTimer.h" + +// Some log helper defines +#define PatcherLogGreen(...) pfPatcher::GetLog()->AddLineF(plStatusLog::kGreen, __VA_ARGS__) +#define PatcherLogRed(...) pfPatcher::GetLog()->AddLineF(plStatusLog::kRed, __VA_ARGS__) +#define PatcherLogWhite(...) pfPatcher::GetLog()->AddLineF(plStatusLog::kWhite, __VA_ARGS__) +#define PatcherLogYellow(...) pfPatcher::GetLog()->AddLineF(plStatusLog::kYellow, __VA_ARGS__) + +/** Patcher grunt work thread */ +struct pfPatcherWorker : public hsThread +{ + /** Represents a File/Auth download request */ + struct Request + { + enum { kFile, kManifest }; + + plString fName; + uint8_t fType; + class pfPatcherStream* fStream; + + Request(const plString& name, uint8_t type, class pfPatcherStream* s=nullptr) : + fName(name), fType(type), fStream(s) + { } + }; + + /** Human readable file flags */ + enum FileFlags + { + // Sound files only + kSndFlagCacheSplit = 1<<0, + kSndFlagStreamCompressed = 1<<1, + kSndFlagCacheStereo = 1<<2, + + // Any file + kFlagZipped = 1<<3, + }; + + std::deque fRequests; + std::deque fQueuedFiles; + + hsMutex fRequestMut; + hsMutex fFileMut; + hsSemaphore fFileSignal; + + pfPatcher::CompletionFunc fOnComplete; + pfPatcher::FileDownloadFunc fFileBeginDownload; + pfPatcher::FileDownloadFunc fFileDownloaded; + pfPatcher::ProgressTickFunc fProgressTick; + + pfPatcher* fParent; + volatile bool fStarted; + volatile bool fRequestActive; + + uint64_t fCurrBytes; + uint64_t fTotalBytes; + + pfPatcherWorker(); + ~pfPatcherWorker(); + + void OnQuit(); + + void EndPatch(ENetError result, const plString& msg=plString::Null); + bool IssueRequest(); + virtual hsError Run(); + void ProcessFile(); +}; + +// =================================================== + +class pfPatcherStream : public plZlibStream +{ + pfPatcherWorker* fParent; + plFileName fFilename; + uint32_t fFlags; + + uint64_t fBytesWritten; + float fDLStartTime; + + plString IMakeStatusMsg() const + { + float secs = hsTimer::GetSysSeconds() - fDLStartTime; + float bytesPerSec = fBytesWritten / secs; + return plFileSystem::ConvertFileSize(bytesPerSec) + "/s"; + } + + void IUpdateProgress(uint32_t count) + { + fBytesWritten += count; // just this file + fParent->fCurrBytes += count; // the entire everything + + // tick-tick-tick, tick-tick-tock + if (fParent->fProgressTick) + fParent->fProgressTick(fParent->fCurrBytes, fParent->fTotalBytes, IMakeStatusMsg()); + } + +public: + pfPatcherStream(pfPatcherWorker* parent, const plFileName& filename, const NetCliFileManifestEntry& entry) + : fParent(parent), fFlags(entry.flags), fBytesWritten(0) + { + // ugh. eap removed the compressed flag in his fail manifests + if (filename.GetFileExt().CompareI("gz") == 0) { + fFlags |= pfPatcherWorker::kFlagZipped; + parent->fTotalBytes += entry.zipSize; + } else + parent->fTotalBytes += entry.fileSize; + } + + virtual bool Open(const plFileName& filename, const char* mode) + { + fFilename = filename; + return plZlibStream::Open(filename, mode); + } + + virtual uint32_t Write(uint32_t count, const void* buf) + { + // tick whatever progress bar we have + IUpdateProgress(count); + + // write the appropriate blargs + if (hsCheckBits(fFlags, pfPatcherWorker::kFlagZipped)) + return plZlibStream::Write(count, buf); + else + return fOutput->Write(count, buf); + } + + void Begin() { fDLStartTime = hsTimer::GetSysSeconds(); } + plFileName GetFileName() const { return fFilename; } + void Unlink() const { plFileSystem::Unlink(fFilename); } +}; + +// =================================================== + +static void IFileManifestDownloadCB(ENetError result, void* param, const wchar_t group[], const NetCliFileManifestEntry manifest[], unsigned entryCount) +{ + pfPatcherWorker* patcher = static_cast(param); + + if (IS_NET_SUCCESS(result)) { + PatcherLogGreen("\tDownloaded Manifest '%S'", group); + { + hsTempMutexLock lock(patcher->fFileMut); + for (unsigned i = 0; i < entryCount; ++i) + patcher->fQueuedFiles.push_back(manifest[i]); + patcher->fFileSignal.Signal(); + } + patcher->IssueRequest(); + } else { + PatcherLogRed("\tDownload Failed: Manifest '%S'", group); + patcher->EndPatch(result, plString::FromWchar(group)); + } +} + +static void IFileThingDownloadCB(ENetError result, void* param, const plFileName& filename, hsStream* writer) +{ + pfPatcherWorker* patcher = static_cast(param); + pfPatcherStream* stream = static_cast(writer); + stream->Close(); + + if (IS_NET_SUCCESS(result)) { + PatcherLogGreen("\tDownloaded File '%s'", stream->GetFileName().AsString().c_str()); + if (patcher->fFileDownloaded) + patcher->fFileDownloaded(stream->GetFileName()); + patcher->IssueRequest(); + } else { + PatcherLogRed("\tDownloaded Failed: File '%s'", stream->GetFileName().AsString().c_str()); + stream->Unlink(); + patcher->EndPatch(result, filename.AsString()); + } + + delete stream; +} + +// =================================================== + +pfPatcherWorker::pfPatcherWorker() : + fStarted(false), fCurrBytes(0), fTotalBytes(0), fRequestActive(true) +{ } + +pfPatcherWorker::~pfPatcherWorker() +{ + { + hsTempMutexLock lock(fRequestMut); + std::for_each(fRequests.begin(), fRequests.end(), + [] (const Request& req) { + if (req.fStream) req.fStream->Close(); + delete req.fStream; + } + ); + fRequests.clear(); + } + + { + hsTempMutexLock lock(fFileMut); + fQueuedFiles.clear(); + } +} + +void pfPatcherWorker::OnQuit() +{ + // the thread's Run() has exited sanely... now we can commit hara-kiri + delete fParent; +} + +void pfPatcherWorker::EndPatch(ENetError result, const plString& msg) +{ + // Guard against multiple calls + if (fStarted) { + // Send end status + if (fOnComplete) + fOnComplete(result, msg); + + // yay log hax + if (IS_NET_SUCCESS(result)) + PatcherLogWhite("--- Patch Complete ---"); + else { + PatcherLogRed("\tNetwork Error: %S", NetErrorToString(result)); + PatcherLogWhite("--- Patch Killed by Error ---"); + } + } + + fStarted = false; + fFileSignal.Signal(); +} + +bool pfPatcherWorker::IssueRequest() +{ + hsTempMutexLock lock(fRequestMut); + if (fRequests.empty()) { + fRequestActive = false; + fFileSignal.Signal(); // make sure the patch thread doesn't deadlock! + return false; + } else + fRequestActive = true; + + const Request& req = fRequests.front(); + switch (req.fType) { + case Request::kFile: + req.fStream->Begin(); + if (fFileBeginDownload) + fFileBeginDownload(req.fStream->GetFileName()); + + NetCliFileDownloadRequest(req.fName, req.fStream, IFileThingDownloadCB, this); + break; + case Request::kManifest: + NetCliFileManifestRequest(IFileManifestDownloadCB, this, req.fName.ToWchar()); + break; + DEFAULT_FATAL(req.fType); + } + + fRequests.pop_front(); + return true; +} + +hsError pfPatcherWorker::Run() +{ + // So here's the rub: + // We have one or many manifests in the fRequests deque. We begin issuing those requests one-by one, starting here. + // As we receive the answer, the NetCli thread populates fQueuedFiles and pings the fFileSignal semaphore, then issues the next request... + // In this non-UI/non-Net thread, we do the stutter-prone/time-consuming IO/hashing operations. (Typically, the UI thread == Net thread) + // As we find files that need updating, we add them to fRequests. + // If there is no net request from ME when we find a file, we issue the request + // Once a file is downloaded, the next request is issued. + // When there are no files in my deque and no requests in my deque, we exit without errors. + + PatcherLogWhite("--- Patch Started (%i requests) ---", fRequests.size()); + fStarted = true; + IssueRequest(); + + // Now, work until we're done processing files + do { + fFileSignal.Wait(); + + hsTempMutexLock fileLock(fFileMut); + if (!fQueuedFiles.empty()) { + ProcessFile(); + continue; + } + + // This makes sure both queues are empty before exiting. + if (!fRequestActive) + if(!IssueRequest()) + break; + } while (fStarted); + + EndPatch(kNetSuccess); + return hsOK; +} + +void pfPatcherWorker::ProcessFile() +{ + do { + const NetCliFileManifestEntry& entry = fQueuedFiles.front(); + + // eap sucks + plString clName = plString::FromWchar(entry.clientName); + plString dlName = plString::FromWchar(entry.downloadName); + + // Check to see if ours matches + plFileInfo mine(clName); + if (mine.FileSize() == entry.fileSize) { + plMD5Checksum cliMD5(clName); + plMD5Checksum srvMD5; + srvMD5.SetFromHexString(plString::FromWchar(entry.md5, 32).c_str()); + + if (cliMD5 == srvMD5) { + fQueuedFiles.pop_front(); + continue; + } + } + + // If you got here, they're different. + PatcherLogYellow("\tEnqueuing '%S'", entry.clientName); + plFileSystem::CreateDir(plFileName(clName).StripFileName()); + + pfPatcherStream* s = new pfPatcherStream(this, dlName, entry); + s->Open(clName, "wb"); + + hsTempMutexLock lock(fRequestMut); + fRequests.push_back(Request(dlName, Request::kFile, s)); + fQueuedFiles.pop_front(); + + if (!fRequestActive) + IssueRequest(); + } while (!fQueuedFiles.empty()); +} + +// =================================================== + +plStatusLog* pfPatcher::GetLog() +{ + static plStatusLog* log = nullptr; + if (!log) + { + log = plStatusLogMgr::GetInstance().CreateStatusLog( + 20, + "patcher.log", + plStatusLog::kFilledBackground | plStatusLog::kAlignToTop | plStatusLog::kDeleteForMe); + } + return log; +} + +pfPatcher::pfPatcher() : fWorker(new pfPatcherWorker) { } +pfPatcher::~pfPatcher() { } + +// =================================================== + +void pfPatcher::OnCompletion(CompletionFunc cb) +{ + fWorker->fOnComplete = cb; +} + +void pfPatcher::OnFileDownloadBegin(FileDownloadFunc cb) +{ + fWorker->fFileBeginDownload = cb; +} + +void pfPatcher::OnFileDownloaded(FileDownloadFunc cb) +{ + fWorker->fFileDownloaded = cb; +} + +void pfPatcher::OnProgressTick(ProgressTickFunc cb) +{ + fWorker->fProgressTick = cb; +} + +// =================================================== + +void pfPatcher::RequestManifest(const plString& mfs) +{ + hsTempMutexLock lock(fWorker->fRequestMut); + fWorker->fRequests.push_back(pfPatcherWorker::Request(mfs, pfPatcherWorker::Request::kManifest)); +} + +void pfPatcher::RequestManifest(const std::vector& mfs) +{ + hsTempMutexLock lock(fWorker->fRequestMut); + std::for_each(mfs.begin(), mfs.end(), + [&] (const plString& name) { + fWorker->fRequests.push_back(pfPatcherWorker::Request(name, pfPatcherWorker::Request::kManifest)); + } + ); +} + +bool pfPatcher::Start() +{ + hsAssert(!fWorker->fStarted, "pfPatcher is one-use only. kthx."); + if (!fWorker->fStarted) { + fWorker->fParent = this; // wheeeee circular + fWorker->Start(); + return true; + } + return false; +} + diff --git a/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.h b/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.h new file mode 100644 index 00000000..5262a1ec --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.h @@ -0,0 +1,112 @@ +/*==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 . + +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 _pfPatcher_inc_ +#define _pfPatcher_inc_ + +#include +#include +#include + +#include "plString.h" +#include "pnNetBase/pnNbError.h" + +class plFileName; +class plStatusLog; + +/** Plasma File Patcher + * This is used to patch the client with one or many manifests at once. It assumes that + * we have permission to modify the game files, so be sure that you do! We memory manage + * ourselves, so allocate a new pfPatcher, add your manifests, and Start! + */ +class pfPatcher +{ + std::unique_ptr fWorker; + +public: + static plStatusLog* GetLog(); + +public: + /** Represents a function that takes the status and an optional message on completion. */ + typedef std::function CompletionFunc; + + /** Represents a function that takes (const plFileName&) on an interesting file operation. */ + typedef std::function FileDownloadFunc; + + /** Represents a function that takes (bytesDLed, totalBytes, statsStr) as a progress indicator. */ + typedef std::function ProgressTickFunc; + + pfPatcher(); + ~pfPatcher(); + + /** Set a callback that will be fired when the patcher finishes its dirty work. + * \remarks This may be called from any thread, so make sure your callback is + * thread safe! + */ + void OnCompletion(CompletionFunc cb); + + /** Set a callback that will be fired when the patcher issues a download request to the server. + * \remarks This will be called from the network thread. + */ + void OnFileDownloadBegin(FileDownloadFunc cb); + + /** Set a callback that will be fired when the patcher has finished downloading a file from the server. + * \remarks This will be called from the network thread. + */ + void OnFileDownloaded(FileDownloadFunc cb); + + /** Set a callback that will be fired when the patcher receives a chunk from the server. The status string + * will contain the current download speed. + * \remarks This will be called from the network thread. + */ + void OnProgressTick(ProgressTickFunc cb); + + void RequestManifest(const plString& mfs); + void RequestManifest(const std::vector& mfs); + + /** Start patching the requested manifests. Please note that after calling this, you should + * discard your pointer to this object as it will memory-manage itself. + */ + bool Start(); +}; + +#endif // _pfPatcher_inc_ diff --git a/Sources/Plasma/FeatureLib/pfPatcher/plManifests.cpp b/Sources/Plasma/FeatureLib/pfPatcher/plManifests.cpp new file mode 100644 index 00000000..d368c2db --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfPatcher/plManifests.cpp @@ -0,0 +1,90 @@ +/*==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 . + +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 "plManifests.h" +#include "plFileSystem.h" + +// Helper that returns the appropriate string per build +#ifdef PLASMA_EXTERNAL_RELEASE +# define MANIFEST(in, ex) ex +#else +# define MANIFEST(in, ex) in +#endif // PLASMA_EXTERNAL_RELEASE + +plFileName plManifest::ClientExecutable() +{ + return MANIFEST("plClient.exe", "UruExplorer.exe"); +} + +plFileName plManifest::PatcherExecutable() +{ + return MANIFEST("plUruLauncher.exe", "UruLauncher.exe"); +} + +plString plManifest::ClientManifest() +{ + return MANIFEST("ThinInternal", "ThinExternal"); +} + +plString plManifest::ClientImageManifest() +{ + return MANIFEST("Internal", "External"); +} + +plString plManifest::PatcherManifest() +{ + return MANIFEST("InternalPatcher", "ExternalPatcher"); +} + +std::vector plManifest::EssentialGameManifests() +{ + std::vector mfs; + mfs.push_back("CustomAvatars"); + mfs.push_back("GlobalAnimations"); + mfs.push_back("GlobalAvatars"); + mfs.push_back("GlobalClothing"); + mfs.push_back("GlobalMarkers"); + mfs.push_back("GUI"); + + return mfs; +} + diff --git a/Sources/Plasma/FeatureLib/pfPatcher/plManifests.h b/Sources/Plasma/FeatureLib/pfPatcher/plManifests.h new file mode 100644 index 00000000..f21d039a --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfPatcher/plManifests.h @@ -0,0 +1,73 @@ +/*==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 . + +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 _plManifests_inc_ +#define _plManifests_inc_ + +#include + +class plFileName; +class plString; + +namespace plManifest +{ + /** Get the name of the client executable for this build type.*/ + plFileName ClientExecutable(); + + /** Get the name of the patcher executable for this build type.*/ + plFileName PatcherExecutable(); + + /** Get the name of the baseline client manifest for this build type. */ + plString ClientManifest(); + + /** Get the name of the full game manifest for this build type. */ + plString ClientImageManifest(); + + /** Get the name of the patcher manifest for this build type. */ + plString PatcherManifest(); + + /** Get a vector containing all manifests the game requires to initialize. */ + std::vector EssentialGameManifests(); + +} + +#endif // _plManifests_inc_ From 9d162a7a321024268b12e3ea22e310b9d268bb25 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sat, 23 Nov 2013 18:30:21 -0500 Subject: [PATCH 4/6] Reroute plResPatcher --- Sources/Plasma/Apps/plClient/plClient.cpp | 18 +- .../PubUtilLib/plAgeLoader/CMakeLists.txt | 2 + .../PubUtilLib/plAgeLoader/plAgeLoader.cpp | 21 +- .../PubUtilLib/plAgeLoader/plResPatcher.cpp | 306 ++++-------------- .../PubUtilLib/plAgeLoader/plResPatcher.h | 60 ++-- .../PubUtilLib/plMessage/plResPatcherMsg.h | 15 +- .../plNetClient/plNetCliAgeJoiner.cpp | 2 +- 7 files changed, 118 insertions(+), 306 deletions(-) diff --git a/Sources/Plasma/Apps/plClient/plClient.cpp b/Sources/Plasma/Apps/plClient/plClient.cpp index 808f4222..d68cfedc 100644 --- a/Sources/Plasma/Apps/plClient/plClient.cpp +++ b/Sources/Plasma/Apps/plClient/plClient.cpp @@ -153,6 +153,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "pfSecurePreloader/pfSecurePreloader.h" #include "pfLocalizationMgr/pfLocalizationMgr.h" +#include "pfPatcher/plManifests.h" #include "plTweak.h" @@ -1613,19 +1614,10 @@ bool plClient::StartInit() //============================================================================ void plClient::IPatchGlobalAgeFiles( void ) { - plResPatcher* patcher = plResPatcher::GetInstance(); - if (!gDataServerLocal) - { - patcher->RequestManifest("CustomAvatars"); - patcher->RequestManifest("GlobalAnimations"); - patcher->RequestManifest("GlobalAvatars"); - patcher->RequestManifest("GlobalClothing"); - patcher->RequestManifest("GlobalMarkers"); - patcher->RequestManifest("GUI"); - } - plgDispatch::Dispatch()->RegisterForExactType(plResPatcherMsg::Index(), GetKey()); - patcher->Start(); + + plResPatcher* patcher = plResPatcher::GetInstance(); + patcher->Update(plManifest::EssentialGameManifests()); } void plClient::InitDLLs() @@ -2550,7 +2542,7 @@ void plClient::IHandlePatcherMsg (plResPatcherMsg * msg) { plgDispatch::Dispatch()->UnRegisterForExactType(plResPatcherMsg::Index(), GetKey()); if (!msg->Success()) { - plNetClientApp::GetInstance()->QueueDisableNet(true, msg->GetError()); + plNetClientApp::GetInstance()->QueueDisableNet(true, msg->GetError().c_str()); return; } diff --git a/Sources/Plasma/PubUtilLib/plAgeLoader/CMakeLists.txt b/Sources/Plasma/PubUtilLib/plAgeLoader/CMakeLists.txt index 5ea91cf5..572ce974 100644 --- a/Sources/Plasma/PubUtilLib/plAgeLoader/CMakeLists.txt +++ b/Sources/Plasma/PubUtilLib/plAgeLoader/CMakeLists.txt @@ -18,5 +18,7 @@ set(plAgeLoader_HEADERS add_library(plAgeLoader STATIC ${plAgeLoader_SOURCES} ${plAgeLoader_HEADERS}) +target_link_libraries(plAgeLoader pfPatcher) + source_group("Source Files" FILES ${plAgeLoader_SOURCES}) source_group("Header Files" FILES ${plAgeLoader_HEADERS}) diff --git a/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.cpp b/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.cpp index 25ba08a2..a59b9dbb 100644 --- a/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.cpp +++ b/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.cpp @@ -69,15 +69,13 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plResMgr/plRegistryNode.h" #include "plResMgr/plResManager.h" #include "plFile/plEncryptedStream.h" +#include "plProgressMgr/plProgressMgr.h" /// TEMP HACK TO LOAD CONSOLE INIT FILES ON AGE LOAD #include "plMessage/plConsoleMsg.h" #include "plMessage/plLoadAvatarMsg.h" #include "plMessage/plAgeLoadedMsg.h" - -extern bool gDataServerLocal; - // static plAgeLoader* plAgeLoader::fInstance=nil; @@ -119,6 +117,7 @@ void plAgeLoader::Init() RegisterAs( kAgeLoader_KEY ); plgDispatch::Dispatch()->RegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey()); plgDispatch::Dispatch()->RegisterForExactType(plClientMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plResPatcherMsg::Index(), GetKey()); } // @@ -156,7 +155,12 @@ bool plAgeLoader::MsgReceive(plMessage* msg) return true; } - + // sadface thread protection + if (plResPatcherMsg::ConvertNoRef(msg)) { + delete plResPatcher::GetInstance()->fProgress; + plResPatcher::GetInstance()->fProgress = nullptr; + } + return plReceiver::MsgReceive(msg); } @@ -173,14 +177,7 @@ bool plAgeLoader::LoadAge(const char ageName[]) //============================================================================ void plAgeLoader::UpdateAge(const char ageName[]) { - if (gDataServerLocal) - // We have to send this msg ourselves since we're not actually updating - plgDispatch::Dispatch()->MsgSend(new plResPatcherMsg); - else - { - plResPatcher::GetInstance()->RequestManifest(ageName); - plResPatcher::GetInstance()->Start(); - } + plResPatcher::GetInstance()->Update(ageName); } //============================================================================ diff --git a/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp b/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp index 896059de..a0139533 100644 --- a/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp +++ b/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp @@ -42,153 +42,19 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plResPatcher.h" #include "hsResMgr.h" +#include "plgDispatch.h" #include "plAgeLoader/plAgeLoader.h" -#include "plCompression/plZlibStream.h" -#include "pnEncryption/plChecksum.h" #include "plMessage/plResPatcherMsg.h" -#include "pnNetBase/pnNbError.h" -#include "plNetGameLib/plNetGameLib.h" +#include "pfPatcher/pfPatcher.h" #include "plProgressMgr/plProgressMgr.h" #include "plResMgr/plResManager.h" -#include "plStatusLog/plStatusLog.h" -///////////////////////////////////////////////////////////////////////////// - -class plResDownloadStream : public plZlibStream -{ - plOperationProgress* fProgress; - plFileName fFilename; - bool fIsZipped; - -public: - plResDownloadStream(plOperationProgress* prog, const plFileName& reqFile) - : fProgress(prog) - { - fIsZipped = reqFile.GetFileExt().CompareI("gz") == 0; - } - - virtual bool Open(const plFileName& filename, const char* mode) - { - fFilename = filename; - return plZlibStream::Open(filename, mode); - } - - virtual uint32_t Write(uint32_t count, const void* buf) - { - fProgress->Increment((float)count); - if (fIsZipped) - return plZlibStream::Write(count, buf); - else - return fOutput->Write(count, buf); - } - - plFileName GetFileName() const { return fFilename; } - bool IsZipped() const { return fIsZipped; } - void Unlink() const { plFileSystem::Unlink(fFilename); } -}; - -///////////////////////////////////////////////////////////////////////////// - -static void FileDownloaded( - ENetError result, - void* param, - const plFileName & filename, - hsStream* writer) -{ - plResPatcher* patcher = (plResPatcher*)param; - plFileName file = filename; - if (((plResDownloadStream*)writer)->IsZipped()) - file = file.StripFileExt(); // Kill off .gz - writer->Close(); - - switch (result) - { - case kNetSuccess: - { - PatcherLog(kStatus, " Download Complete: %s", file.AsString().c_str()); - - // If this is a PRP, then we need to add it to the ResManager - plFileName clientPath = static_cast(writer)->GetFileName(); - if (clientPath.GetFileExt().CompareI("prp") == 0) - { - plResManager* clientResMgr = static_cast(hsgResMgr::ResMgr()); - clientResMgr->AddSinglePage(clientPath); - } - - // Continue down the warpath - patcher->IssueRequest(); - delete writer; - return; - } - case kNetErrFileNotFound: - PatcherLog(kError, " Download Failed: %s not found", file.AsString().c_str()); - break; - default: - char* error = hsWStringToString(NetErrorToString(result)); - PatcherLog(kError, " Download Failed: %s", error); - delete[] error; - break; - } - - // Failure case - static_cast(writer)->Unlink(); - patcher->Finish(false); - delete writer; -} - -static void ManifestDownloaded( - ENetError result, - void* param, - const wchar_t group[], - const NetCliFileManifestEntry manifest[], - uint32_t entryCount) -{ - plResPatcher* patcher = (plResPatcher*)param; - plString name = plString::FromWchar(group); - if (IS_NET_SUCCESS(result)) - PatcherLog(kInfo, " Downloaded manifest %s", name.c_str()); - else { - PatcherLog(kError, " Failed to download manifest %s", name.c_str()); - patcher->Finish(false); - return; - } - - for (uint32_t i = 0; i < entryCount; ++i) - { - const NetCliFileManifestEntry mfs = manifest[i]; - plFileName fileName = plString::FromWchar(mfs.clientName); - plFileName downloadName = plString::FromWchar(mfs.downloadName); - - // See if the files are the same - // 1. Check file size before we do time consuming md5 operations - // 2. Do wasteful md5. We should consider implementing a CRC instead. - if (plFileInfo(fileName).FileSize() == mfs.fileSize) - { - plMD5Checksum cliMD5(fileName); - plMD5Checksum srvMD5; - srvMD5.SetFromHexString(plString::FromWchar(mfs.md5, 32).c_str()); - - if (cliMD5 == srvMD5) - continue; - else - PatcherLog(kInfo, " Enqueueing %s: MD5 Checksums Differ", fileName.AsString().c_str()); - } else - PatcherLog(kInfo, " Enqueueing %s: File Sizes Differ", fileName.AsString().c_str()); - - // If we're still here, then we need to update the file. - float size = mfs.zipSize ? (float)mfs.zipSize : (float)mfs.fileSize; - patcher->GetProgress()->SetLength(size + patcher->GetProgress()->GetMax()); - patcher->RequestFile(downloadName, fileName); - } - - patcher->IssueRequest(); -} +extern bool gDataServerLocal; ///////////////////////////////////////////////////////////////////////////// -static char* sLastError = nil; -plResPatcher* plResPatcher::fInstance = nil; +plResPatcher* plResPatcher::fInstance = nullptr; plResPatcher* plResPatcher::GetInstance() { @@ -199,134 +65,102 @@ plResPatcher* plResPatcher::GetInstance() void plResPatcher::Shutdown() { - // Better not call this while we're patching delete fInstance; } ///////////////////////////////////////////////////////////////////////////// -plResPatcher::plResPatcher() - : fPatching(false), fProgress(nil) { } - -plResPatcher::~plResPatcher() +void plResPatcher::OnCompletion(ENetError result, const plString& status) { - if (fProgress) - delete fProgress; + plString error = plString::Null; + if (IS_NET_ERROR(result)) + error = plString::Format("Update Failed: %S\n%s", NetErrorAsString(result), status.c_str()); + plgDispatch::Dispatch()->MsgQueue(new plResPatcherMsg(IS_NET_SUCCESS(result), error)); } -void plResPatcher::IssueRequest() +void plResPatcher::OnFileDownloadBegin(const plFileName& file) { - if (!fPatching) return; - if (fRequests.empty()) - // Wheee! - Finish(); - else { - Request req = fRequests.front(); - fRequests.pop(); - - plString title; - if (req.fType == kManifest) - { - PatcherLog(kMajorStatus, " Downloading manifest... %s", req.fFile.AsString().c_str()); - title = plString::Format("Checking %s for updates...", req.fFile.AsString().c_str()); - NetCliFileManifestRequest(ManifestDownloaded, this, req.fFile.AsString().ToWchar()); - } else if (req.fType == kFile) { - PatcherLog(kMajorStatus, " Downloading file... %s", req.fFriendlyName.AsString().c_str()); - title = plString::Format("Downloading... %s", req.fFriendlyName.GetFileName().c_str()); + fProgress->SetTitle(plString::Format("Downloading %s...", file.GetFileName().c_str())); - // If this is a PRP, we need to unload it from the ResManager - - if (req.fFriendlyName.GetFileExt().CompareI("prp") == 0) - ((plResManager*)hsgResMgr::ResMgr())->RemoveSinglePage(req.fFriendlyName); - - plFileSystem::CreateDir(req.fFriendlyName.StripFileName(), true); - plResDownloadStream* stream = new plResDownloadStream(fProgress, req.fFile); - if (stream->Open(req.fFriendlyName, "wb")) - NetCliFileDownloadRequest(req.fFile, stream, FileDownloaded, this); - else { - PatcherLog(kError, " Unable to create file %s", req.fFriendlyName.AsString().c_str()); - Finish(false); - } - } - - fProgress->SetTitle(title); + if (file.GetFileExt().CompareI("prp") == 0) { + plResManager* mgr = static_cast(hsgResMgr::ResMgr()); + if (mgr) + mgr->RemoveSinglePage(file); } } -void plResPatcher::Finish(bool success) +void plResPatcher::OnFileDownloaded(const plFileName& file) { - while (fRequests.size()) - fRequests.pop(); - - fPatching = false; - if (success) - PatcherLog(kHeader, "--- Patch Completed Successfully ---"); - else - { - PatcherLog(kHeader, "--- Patch Killed by Error ---"); - if (fProgress) - fProgress->SetAborting(); + if (file.GetFileExt().CompareI("prp") == 0) { + plResManager* mgr = static_cast(hsgResMgr::ResMgr()); + if (mgr) + mgr->AddSinglePage(file); } - delete fProgress; fProgress = nil; - - plResPatcherMsg* pMsg = new plResPatcherMsg(success, sLastError); - delete[] sLastError; sLastError = nil; - pMsg->Send(); // whoosh... off it goes } -void plResPatcher::RequestFile(const plFileName& srvName, const plFileName& cliName) +void plResPatcher::OnProgressTick(uint64_t dl, uint64_t total, const plString& msg) { - fRequests.push(Request(srvName, kFile, cliName)); + if (dl && total) { + fProgress->SetLength(total); + fProgress->SetHowMuch(dl); + } + + plString status = plString::Format("%s / %s", + plFileSystem::ConvertFileSize(dl).c_str(), + plFileSystem::ConvertFileSize(total).c_str() + ); + + fProgress->SetStatusText(status); + fProgress->SetInfoText(msg); } -void plResPatcher::RequestManifest(const plString& age) +pfPatcher* plResPatcher::CreatePatcher() { - fRequests.push(Request(age, kManifest)); + pfPatcher* patcher = new pfPatcher; + patcher->OnCompletion(std::bind(&plResPatcher::OnCompletion, this, std::placeholders::_1, std::placeholders::_2)); + patcher->OnFileDownloadBegin(std::bind(&plResPatcher::OnFileDownloadBegin, this, std::placeholders::_1)); + patcher->OnFileDownloaded(std::bind(&plResPatcher::OnFileDownloaded, this, std::placeholders::_1)); + patcher->OnProgressTick(std::bind(&plResPatcher::OnProgressTick, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + return patcher; } -void plResPatcher::Start() +void plResPatcher::InitProgress() { - hsAssert(!fPatching, "Too many calls to plResPatcher::Start"); - fPatching = true; - PatcherLog(kHeader, "--- Patch Started (%i requests) ---", fRequests.size()); - fProgress = plProgressMgr::GetInstance()->RegisterOperation(0.0, "Checking for updates...", - plProgressMgr::kUpdateText, false, true); - IssueRequest(); + // this is deleted in plAgeLoader::MsgReceive for thread safety + fProgress = plProgressMgr::GetInstance()->RegisterOperation(0.f, nullptr, plProgressMgr::kUpdateText); } ///////////////////////////////////////////////////////////////////////////// -void PatcherLog(PatcherLogType type, const char* format, ...) +plResPatcher::plResPatcher() + : fProgress(nullptr) { } + +plResPatcher::~plResPatcher() { - uint32_t color = 0; - switch (type) - { - case kHeader: color = plStatusLog::kWhite; break; - case kInfo: color = plStatusLog::kBlue; break; - case kMajorStatus: color = plStatusLog::kYellow; break; - case kStatus: color = plStatusLog::kGreen; break; - case kError: color = plStatusLog::kRed; break; - } + delete fProgress; +} - static plStatusLog* gStatusLog = nil; - if (!gStatusLog) - { - gStatusLog = plStatusLogMgr::GetInstance().CreateStatusLog( - 20, - "patcher.log", - plStatusLog::kFilledBackground | plStatusLog::kAlignToTop | plStatusLog::kDeleteForMe); +void plResPatcher::Update(const std::vector& manifests) +{ + if (gDataServerLocal) + plgDispatch::Dispatch()->MsgSend(new plResPatcherMsg()); + else { + InitProgress(); + pfPatcher* patcher = CreatePatcher(); + patcher->RequestManifest(manifests); + patcher->Start(); // whoosh... off it goes } +} - va_list args; - va_start(args, format); - - if (type == kError) - { - sLastError = new char[1024]; // Deleted by Finish(false) - vsnprintf(sLastError, 1024, format, args); - gStatusLog->AddLine(sLastError, color); - } else - gStatusLog->AddLineV(color, format, args); - - va_end(args); +void plResPatcher::Update(const plString& manifest) +{ + if (gDataServerLocal) + plgDispatch::Dispatch()->MsgSend(new plResPatcherMsg()); + else { + InitProgress(); + pfPatcher* patcher = CreatePatcher(); + patcher->RequestManifest(manifest); + patcher->Start(); // whoosh... off it goes + } } + diff --git a/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h b/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h index 4de86111..8987fbb0 100644 --- a/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h +++ b/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h @@ -39,57 +39,47 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com Mead, WA 99021 *==LICENSE==*/ + #ifndef plResPatcher_h_inc #define plResPatcher_h_inc -#include "HeadSpin.h" #include "plFileSystem.h" -#include -#include +#include "pnNetBase/pnNbError.h" +#include class plOperationProgress; +class plString; +/** + * Plasma Resource Patcher + * This is a thin wrapper around \sa pfPatcher that ensures updates are propagated to the rest of the engine. + */ class plResPatcher { - enum { kManifest, kFile }; - struct Request - { - plFileName fFile; - plFileName fFriendlyName; - uint8_t fType; - - Request(const plFileName& file, uint8_t type, const plFileName& friendly = "") - : fFile(file), fFriendlyName(friendly), fType(type) { } - }; - - static plResPatcher* fInstance; - std::queue fRequests; - plOperationProgress* fProgress; - bool fPatching; + plOperationProgress* fProgress; + static plResPatcher* fInstance; - plResPatcher(); - ~plResPatcher(); + friend class plAgeLoader; + + void OnCompletion(ENetError, const plString& msg); + void OnFileDownloadBegin(const plFileName& file); + void OnFileDownloaded(const plFileName& file); + void OnProgressTick(uint64_t dl, uint64_t total, const plString& msg); + + class pfPatcher* CreatePatcher(); + void InitProgress(); public: static plResPatcher* GetInstance(); static void Shutdown(); - plOperationProgress* GetProgress() { return fProgress; } +public: + plResPatcher(); + ~plResPatcher(); - void Finish(bool success = true); - void IssueRequest(); - void RequestFile(const plFileName& file, const plFileName& friendlyName); - void RequestManifest(const plString& age); - void Start(); + void Update(const std::vector& manifests); + void Update(const plString& manifest); }; -enum PatcherLogType -{ - kHeader, - kInfo, - kMajorStatus, - kStatus, - kError, -}; -void PatcherLog(PatcherLogType type, const char* format, ...); + #endif // _plResPatcher_h diff --git a/Sources/Plasma/PubUtilLib/plMessage/plResPatcherMsg.h b/Sources/Plasma/PubUtilLib/plMessage/plResPatcherMsg.h index be26542c..4381be7f 100644 --- a/Sources/Plasma/PubUtilLib/plMessage/plResPatcherMsg.h +++ b/Sources/Plasma/PubUtilLib/plMessage/plResPatcherMsg.h @@ -42,31 +42,28 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #ifndef _PLMESSAGE_PLRESPATCHERMSG_H #define _PLMESSAGE_PLRESPATCHERMSG_H -#include "HeadSpin.h" +#include "plString.h" #include "pnMessage/plMessage.h" // This message is sent when plResPatcher has completed its async operation class plResPatcherMsg : public plMessage { bool fSuccess; - char* fError; + plString fError; public: - plResPatcherMsg() : fSuccess(true), fError(nil) { SetBCastFlag(kBCastByExactType); } - plResPatcherMsg(bool success, const char* error) : fSuccess(success) + plResPatcherMsg() : fSuccess(true) { SetBCastFlag(kBCastByExactType); } + plResPatcherMsg(bool success, const plString& error) : fSuccess(success), fError(error) { SetBCastFlag(kBCastByExactType); - fError = hsStrcpy(error); } - - ~plResPatcherMsg() { delete[] fError; } CLASSNAME_REGISTER(plResPatcherMsg); GETINTERFACE_ANY(plResPatcherMsg, plMessage); - + void Read (hsStream *, hsResMgr *) { FATAL("What the hell are you doing?"); } void Write (hsStream *, hsResMgr *) { FATAL("What the hell are you doing?"); } - const char* GetError() const { return fError; } + plString GetError() const { return fError; } bool Success() const { return fSuccess; } }; diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp b/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp index a1eefe80..9d012d52 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp @@ -387,7 +387,7 @@ bool plNCAgeJoiner::MsgReceive (plMessage * msg) { ); LogMsg(kLogPerf, L"AgeJoiner: Next:kNoOp (age updated)"); } else - Complete(false, resMsg->GetError()); + Complete(false, resMsg->GetError().c_str()); return true; } From 3299267976a95b12186a68502348dd98a8eabe2e Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Mon, 25 Nov 2013 11:55:29 -0500 Subject: [PATCH 5/6] Integrate SecurePreloader into pfPatcher --- Sources/Plasma/Apps/plClient/CMakeLists.txt | 1 - Sources/Plasma/Apps/plClient/plClient.cpp | 45 +- Sources/Plasma/Apps/plClient/plClient.h | 1 - Sources/Plasma/CoreLib/hsStream.h | 2 +- Sources/Plasma/FeatureLib/CMakeLists.txt | 1 - .../Plasma/FeatureLib/inc/pfAllCreatables.h | 1 - .../Plasma/FeatureLib/pfPatcher/pfPatcher.cpp | 170 ++++++- .../Plasma/FeatureLib/pfPatcher/pfPatcher.h | 13 + .../pfSecurePreloader/CMakeLists.txt | 22 - .../pfSecurePreloader/pfSecurePreloader.cpp | 414 ------------------ .../pfSecurePreloader/pfSecurePreloader.h | 96 ---- .../pfSecurePreloaderCreatable.h | 57 --- .../Plasma/NucleusLib/inc/plCreatableIndex.h | 4 +- .../NucleusLib/pnKeyedObject/plFixedKey.cpp | 2 - .../NucleusLib/pnKeyedObject/plFixedKey.h | 1 - .../PubUtilLib/plAgeLoader/plResPatcher.cpp | 29 +- .../PubUtilLib/plAgeLoader/plResPatcher.h | 2 + .../PubUtilLib/plFile/plSecureStream.cpp | 8 +- .../PubUtilLib/plFile/plStreamSource.cpp | 28 +- .../Plasma/PubUtilLib/plFile/plStreamSource.h | 9 +- .../PubUtilLib/plMessage/CMakeLists.txt | 1 - .../PubUtilLib/plMessage/plMessageCreatable.h | 3 - .../PubUtilLib/plMessage/plPreloaderMsg.h | 67 --- .../plNetClientComm/plNetClientComm.cpp | 4 + Sources/Tools/MaxMain/CMakeLists.txt | 1 - Sources/Tools/MaxPlasmaLights/CMakeLists.txt | 1 - 26 files changed, 241 insertions(+), 742 deletions(-) delete mode 100644 Sources/Plasma/FeatureLib/pfSecurePreloader/CMakeLists.txt delete mode 100644 Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.cpp delete mode 100644 Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.h delete mode 100644 Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloaderCreatable.h delete mode 100644 Sources/Plasma/PubUtilLib/plMessage/plPreloaderMsg.h diff --git a/Sources/Plasma/Apps/plClient/CMakeLists.txt b/Sources/Plasma/Apps/plClient/CMakeLists.txt index e36b338b..811008e1 100644 --- a/Sources/Plasma/Apps/plClient/CMakeLists.txt +++ b/Sources/Plasma/Apps/plClient/CMakeLists.txt @@ -86,7 +86,6 @@ target_link_libraries(plClient pfJournalBook) target_link_libraries(plClient pfLocalizationMgr) target_link_libraries(plClient pfMessage) target_link_libraries(plClient pfPython) -target_link_libraries(plClient pfSecurePreloader) target_link_libraries(plClient pfSurface) target_link_libraries(plClient plAgeDescription) target_link_libraries(plClient plAgeLoader) diff --git a/Sources/Plasma/Apps/plClient/plClient.cpp b/Sources/Plasma/Apps/plClient/plClient.cpp index d68cfedc..8fdb544f 100644 --- a/Sources/Plasma/Apps/plClient/plClient.cpp +++ b/Sources/Plasma/Apps/plClient/plClient.cpp @@ -73,7 +73,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "pnMessage/plCameraMsg.h" #include "plMessage/plTransitionMsg.h" #include "plMessage/plLinkToAgeMsg.h" -#include "plMessage/plPreloaderMsg.h" #include "plMessage/plNetCommMsgs.h" #include "plMessage/plAgeLoadedMsg.h" #include "plMessage/plResPatcherMsg.h" @@ -151,7 +150,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plNetCommon/plNetCommonConstants.h" #include "plNetGameLib/plNetGameLib.h" -#include "pfSecurePreloader/pfSecurePreloader.h" #include "pfLocalizationMgr/pfLocalizationMgr.h" #include "pfPatcher/plManifests.h" @@ -312,11 +310,6 @@ bool plClient::Shutdown() plAgeLoader::GetInstance()->UnRegisterAs(kAgeLoader_KEY); // deletes instance plAgeLoader::SetInstance(nil); } - - if (pfSecurePreloader::GetInstance()) - { - pfSecurePreloader::GetInstance()->Shutdown(); // will unregister itself - } if (fInputManager) { @@ -853,14 +846,6 @@ bool plClient::MsgReceive(plMessage* msg) return true; } - //============================================================================ - // plPreloaderMsg - //============================================================================ - if (plPreloaderMsg * preloaderMsg = plPreloaderMsg::ConvertNoRef(msg)) { - IHandlePreloaderMsg(preloaderMsg); - return true; - } - //============================================================================ // plResPatcherMsg //============================================================================ @@ -1565,8 +1550,7 @@ bool plClient::StartInit() plgDispatch::Dispatch()->RegisterForExactType(plNetCommAuthMsg::Index(), GetKey()); plNetClientMgr::GetInstance()->Init(); plAgeLoader::GetInstance()->Init(); - pfSecurePreloader::GetInstance()->Init(); - + plCmdIfaceModMsg* pModMsg2 = new plCmdIfaceModMsg; pModMsg2->SetBCastFlag(plMessage::kBCastByExactType); pModMsg2->SetSender(fConsole->GetKey()); @@ -2516,27 +2500,6 @@ void plClient::ICompleteInit () { clientMsg->Send(); } -//============================================================================ -void plClient::IHandlePreloaderMsg (plPreloaderMsg * msg) { - - plgDispatch::Dispatch()->UnRegisterForExactType(plPreloaderMsg::Index(), GetKey()); - if (pfSecurePreloader* sp = pfSecurePreloader::GetInstance()) - sp->Shutdown(); - - if (!msg->fSuccess) { - char str[1024]; - StrPrintf( - str, - arrsize(str), - "Secure file preloader failed" - ); - plNetClientApp::GetInstance()->QueueDisableNet(true, str); - return; - } - - IPatchGlobalAgeFiles(); -} - //============================================================================ void plClient::IHandlePatcherMsg (plResPatcherMsg * msg) { plgDispatch::Dispatch()->UnRegisterForExactType(plResPatcherMsg::Index(), GetKey()); @@ -2569,8 +2532,6 @@ void plClient::IHandleNetCommAuthMsg (plNetCommAuthMsg * msg) { return; } - plgDispatch::Dispatch()->RegisterForExactType(plPreloaderMsg::Index(), GetKey()); - - // Precache our secure files - pfSecurePreloader::GetInstance()->Start(); + // Patch them global files! + IPatchGlobalAgeFiles(); } diff --git a/Sources/Plasma/Apps/plClient/plClient.h b/Sources/Plasma/Apps/plClient/plClient.h index cdc482c0..108b9c36 100644 --- a/Sources/Plasma/Apps/plClient/plClient.h +++ b/Sources/Plasma/Apps/plClient/plClient.h @@ -181,7 +181,6 @@ protected: void ICompleteInit (); void IOnAsyncInitComplete (); void IHandlePatcherMsg (plResPatcherMsg * msg); - void IHandlePreloaderMsg (plPreloaderMsg * msg); void IHandleNetCommAuthMsg (plNetCommAuthMsg * msg); bool IHandleAgeLoaded2Msg (plAgeLoaded2Msg * msg); diff --git a/Sources/Plasma/CoreLib/hsStream.h b/Sources/Plasma/CoreLib/hsStream.h index b0caf96c..c80787cf 100644 --- a/Sources/Plasma/CoreLib/hsStream.h +++ b/Sources/Plasma/CoreLib/hsStream.h @@ -350,7 +350,7 @@ public: virtual ~hsRAMStream(); virtual bool Open(const plFileName &, const char *) { hsAssert(0, "hsRAMStream::Open NotImplemented"); return false; } - virtual bool Close() { hsAssert(0, "hsRAMStream::Close NotImplemented"); return false; } + virtual bool Close() { return false; } virtual bool AtEnd(); diff --git a/Sources/Plasma/FeatureLib/CMakeLists.txt b/Sources/Plasma/FeatureLib/CMakeLists.txt index 07552faa..3b34f88d 100644 --- a/Sources/Plasma/FeatureLib/CMakeLists.txt +++ b/Sources/Plasma/FeatureLib/CMakeLists.txt @@ -19,5 +19,4 @@ add_subdirectory(pfLocalizationMgr) add_subdirectory(pfMessage) add_subdirectory(pfPatcher) add_subdirectory(pfPython) -add_subdirectory(pfSecurePreloader) add_subdirectory(pfSurface) diff --git a/Sources/Plasma/FeatureLib/inc/pfAllCreatables.h b/Sources/Plasma/FeatureLib/inc/pfAllCreatables.h index e45f3bd5..8ac3f02f 100644 --- a/Sources/Plasma/FeatureLib/inc/pfAllCreatables.h +++ b/Sources/Plasma/FeatureLib/inc/pfAllCreatables.h @@ -57,6 +57,5 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "pfCCR/plCCRCreatable.h" #include "pfJournalBook/pfJournalBookCreatable.h" #include "pfGameMgr/pfGameMgrCreatables.h" -#include "pfSecurePreloader/pfSecurePreloaderCreatable.h" #endif // pfAllCreatables_inc diff --git a/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp b/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp index 080b9c01..d4705e62 100644 --- a/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp +++ b/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp @@ -68,7 +68,7 @@ struct pfPatcherWorker : public hsThread /** Represents a File/Auth download request */ struct Request { - enum { kFile, kManifest }; + enum { kFile, kManifest, kSecurePreloader, kAuthFile, kPythonList, kSdlList }; plString fName; uint8_t fType; @@ -101,6 +101,7 @@ struct pfPatcherWorker : public hsThread pfPatcher::CompletionFunc fOnComplete; pfPatcher::FileDownloadFunc fFileBeginDownload; pfPatcher::FileDownloadFunc fFileDownloaded; + pfPatcher::GameCodeDiscoverFunc fGameCodeDiscovered; pfPatcher::ProgressTickFunc fProgressTick; pfPatcher* fParent; @@ -119,6 +120,7 @@ struct pfPatcherWorker : public hsThread bool IssueRequest(); virtual hsError Run(); void ProcessFile(); + void WhitelistFile(const plFileName& file, bool justDownloaded, hsStream* s=nullptr); }; // =================================================== @@ -150,6 +152,13 @@ class pfPatcherStream : public plZlibStream } public: + pfPatcherStream(pfPatcherWorker* parent, const plFileName& filename, uint64_t size) + : fParent(parent), fFilename(filename), fFlags(0), fBytesWritten(0) + { + fParent->fTotalBytes += size; + fOutput = new hsRAMStream; + } + pfPatcherStream(pfPatcherWorker* parent, const plFileName& filename, const NetCliFileManifestEntry& entry) : fParent(parent), fFlags(entry.flags), fBytesWritten(0) { @@ -179,6 +188,15 @@ public: return fOutput->Write(count, buf); } + virtual bool AtEnd() { return fOutput->AtEnd(); } + virtual uint32_t GetEOF() { return fOutput->GetEOF(); } + virtual uint32_t GetPosition() const { return fOutput->GetPosition(); } + virtual uint32_t GetSizeLeft() const { return fOutput->GetSizeLeft(); } + virtual uint32_t Read(uint32_t count, void* buf) { return fOutput->Read(count, buf); } + virtual void Rewind() { fOutput->Rewind(); } + virtual void SetPosition(uint32_t pos) { fOutput->SetPosition(pos); } + virtual void Skip(uint32_t deltaByteCount) { fOutput->Skip(deltaByteCount); } + void Begin() { fDLStartTime = hsTimer::GetSysSeconds(); } plFileName GetFileName() const { return fFilename; } void Unlink() const { plFileSystem::Unlink(fFilename); } @@ -186,20 +204,94 @@ public: // =================================================== -static void IFileManifestDownloadCB(ENetError result, void* param, const wchar_t group[], const NetCliFileManifestEntry manifest[], unsigned entryCount) +static void IAuthThingDownloadCB(ENetError result, void* param, const plFileName& filename, hsStream* writer) +{ + pfPatcherWorker* patcher = static_cast(param); + + if (IS_NET_SUCCESS(result)) { + PatcherLogGreen("\tDownloaded Legacy File '%s'", filename.AsString().c_str()); + patcher->IssueRequest(); + + // Now, we pass our RAM-backed file to the game code handlers. In the main client, + // this will trickle down and add a new friend to plStreamSource. This should never + // happen in any other app... + writer->Rewind(); + patcher->WhitelistFile(filename, true, writer); + } else { + PatcherLogRed("\tDownloaded Failed: File '%s'", filename.AsString().c_str()); + patcher->EndPatch(result, filename.AsString()); + } +} + +static void IGotAuthFileList(ENetError result, void* param, const NetCliAuthFileInfo infoArr[], unsigned infoCount) { pfPatcherWorker* patcher = static_cast(param); if (IS_NET_SUCCESS(result)) { - PatcherLogGreen("\tDownloaded Manifest '%S'", group); + // so everything goes directly into the Requests deque because AuthSrv lists + // don't have any hashes attached. WHY did eap think this was a good idea?!?! { - hsTempMutexLock lock(patcher->fFileMut); - for (unsigned i = 0; i < entryCount; ++i) - patcher->fQueuedFiles.push_back(manifest[i]); - patcher->fFileSignal.Signal(); + hsTempMutexLock lock(patcher->fRequestMut); + for (unsigned i = 0; i < infoCount; ++i) { + PatcherLogYellow("\tEnqueuing Legacy File '%S'", infoArr[i].filename); + + plFileName fn = plString::FromWchar(infoArr[i].filename); + plFileSystem::CreateDir(fn.StripFileName()); + + // We purposefully do NOT Open this stream! This uses a special auth-file constructor that + // utilizes a backing hsRAMStream. This will be fed to plStreamSource later... + pfPatcherStream* s = new pfPatcherStream(patcher, fn, infoArr[i].filesize); + pfPatcherWorker::Request req = pfPatcherWorker::Request(fn.AsString(), pfPatcherWorker::Request::kAuthFile, s); + patcher->fRequests.push_back(req); + } } patcher->IssueRequest(); } else { + PatcherLogRed("\tSHIT! Some legacy manifest phailed"); + patcher->EndPatch(result, "SecurePreloader failed"); + } +} + +static void IHandleManifestDownload(pfPatcherWorker* patcher, const wchar_t group[], const NetCliFileManifestEntry manifest[], unsigned entryCount) +{ + PatcherLogGreen("\tDownloaded Manifest '%S'", group); + { + hsTempMutexLock lock(patcher->fFileMut); + for (unsigned i = 0; i < entryCount; ++i) + patcher->fQueuedFiles.push_back(manifest[i]); + patcher->fFileSignal.Signal(); + } + patcher->IssueRequest(); +} + +static void IPreloaderManifestDownloadCB(ENetError result, void* param, const wchar_t group[], const NetCliFileManifestEntry manifest[], unsigned entryCount) +{ + pfPatcherWorker* patcher = static_cast(param); + + if (IS_NET_SUCCESS(result)) + IHandleManifestDownload(patcher, group, manifest, entryCount); + else { + PatcherLogYellow("\tWARNING: *** Falling back to AuthSrv file lists to get game code ***"); + + // so, we need to ask the AuthSrv about our game code + { + hsTempMutexLock lock(patcher->fRequestMut); + patcher->fRequests.push_back(pfPatcherWorker::Request(plString::Null, pfPatcherWorker::Request::kPythonList)); + patcher->fRequests.push_back(pfPatcherWorker::Request(plString::Null, pfPatcherWorker::Request::kSdlList)); + } + + // continue pumping requests + patcher->IssueRequest(); + } +} + +static void IFileManifestDownloadCB(ENetError result, void* param, const wchar_t group[], const NetCliFileManifestEntry manifest[], unsigned entryCount) +{ + pfPatcherWorker* patcher = static_cast(param); + + if (IS_NET_SUCCESS(result)) + IHandleManifestDownload(patcher, group, manifest, entryCount); + else { PatcherLogRed("\tDownload Failed: Manifest '%S'", group); patcher->EndPatch(result, plString::FromWchar(group)); } @@ -213,8 +305,7 @@ static void IFileThingDownloadCB(ENetError result, void* param, const plFileName if (IS_NET_SUCCESS(result)) { PatcherLogGreen("\tDownloaded File '%s'", stream->GetFileName().AsString().c_str()); - if (patcher->fFileDownloaded) - patcher->fFileDownloaded(stream->GetFileName()); + patcher->WhitelistFile(stream->GetFileName(), true); patcher->IssueRequest(); } else { PatcherLogRed("\tDownloaded Failed: File '%s'", stream->GetFileName().AsString().c_str()); @@ -299,6 +390,25 @@ bool pfPatcherWorker::IssueRequest() case Request::kManifest: NetCliFileManifestRequest(IFileManifestDownloadCB, this, req.fName.ToWchar()); break; + case Request::kSecurePreloader: + // so, yeah, this is usually the "SecurePreloader" manifest on the file server... + // except on legacy servers, this may not exist, so we need to fall back without nuking everything! + NetCliFileManifestRequest(IPreloaderManifestDownloadCB, this, req.fName.ToWchar()); + break; + case Request::kAuthFile: + // ffffffuuuuuu + req.fStream->Begin(); + if (fFileBeginDownload) + fFileBeginDownload(req.fStream->GetFileName()); + + NetCliAuthFileRequest(req.fName, req.fStream, IAuthThingDownloadCB, this); + break; + case Request::kPythonList: + NetCliAuthFileListRequest(L"Python", L"pak", IGotAuthFileList, this); + break; + case Request::kSdlList: + NetCliAuthFileListRequest(L"SDL", L"sdl", IGotAuthFileList, this); + break; DEFAULT_FATAL(req.fType); } @@ -347,7 +457,7 @@ void pfPatcherWorker::ProcessFile() const NetCliFileManifestEntry& entry = fQueuedFiles.front(); // eap sucks - plString clName = plString::FromWchar(entry.clientName); + plFileName clName = plString::FromWchar(entry.clientName); plString dlName = plString::FromWchar(entry.downloadName); // Check to see if ours matches @@ -358,6 +468,7 @@ void pfPatcherWorker::ProcessFile() srvMD5.SetFromHexString(plString::FromWchar(entry.md5, 32).c_str()); if (cliMD5 == srvMD5) { + WhitelistFile(clName, false); fQueuedFiles.pop_front(); continue; } @@ -379,6 +490,33 @@ void pfPatcherWorker::ProcessFile() } while (!fQueuedFiles.empty()); } +void pfPatcherWorker::WhitelistFile(const plFileName& file, bool justDownloaded, hsStream* stream) +{ + // if this is a newly downloaded file, fire off a completion callback + if (justDownloaded && fFileDownloaded) + fFileDownloaded(file); + + // we want to whitelist our game code, so here we go... + if (fGameCodeDiscovered) { + plString ext = file.GetFileExt(); + if (ext.CompareI("pak") == 0 || ext.CompareI("sdl") == 0) { + if (!stream) { + stream = new hsUNIXStream; + stream->Open(file, "rb"); + } + + // if something terrible goes wrong (eg bad encryption), we can exit sanely + // callback eats stream + if (!fGameCodeDiscovered(file, stream)) + EndPatch(kNetErrInternalError, "SecurePreloader failed."); + } + } else if (stream) { + // no dad gum memory leaks, m'kay? + stream->Close(); + delete stream; + } +} + // =================================================== plStatusLog* pfPatcher::GetLog() @@ -414,6 +552,11 @@ void pfPatcher::OnFileDownloaded(FileDownloadFunc cb) fWorker->fFileDownloaded = cb; } +void pfPatcher::OnGameCodeDiscovery(GameCodeDiscoverFunc cb) +{ + fWorker->fGameCodeDiscovered = cb; +} + void pfPatcher::OnProgressTick(ProgressTickFunc cb) { fWorker->fProgressTick = cb; @@ -421,6 +564,12 @@ void pfPatcher::OnProgressTick(ProgressTickFunc cb) // =================================================== +void pfPatcher::RequestGameCode() +{ + hsTempMutexLock lock(fWorker->fRequestMut); + fWorker->fRequests.push_back(pfPatcherWorker::Request("SecurePreloader", pfPatcherWorker::Request::kSecurePreloader)); +} + void pfPatcher::RequestManifest(const plString& mfs) { hsTempMutexLock lock(fWorker->fRequestMut); @@ -447,4 +596,3 @@ bool pfPatcher::Start() } return false; } - diff --git a/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.h b/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.h index 5262a1ec..6610b722 100644 --- a/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.h +++ b/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.h @@ -52,6 +52,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com class plFileName; class plStatusLog; +class hsStream; /** Plasma File Patcher * This is used to patch the client with one or many manifests at once. It assumes that @@ -72,6 +73,11 @@ public: /** Represents a function that takes (const plFileName&) on an interesting file operation. */ typedef std::function FileDownloadFunc; + /** Represents a function that takes (const plFileName&, hsStream*) on game code discovery. + * You are responsible for closing and deleting the provided stream. + */ + typedef std::function GameCodeDiscoverFunc; + /** Represents a function that takes (bytesDLed, totalBytes, statsStr) as a progress indicator. */ typedef std::function ProgressTickFunc; @@ -94,12 +100,19 @@ public: */ void OnFileDownloaded(FileDownloadFunc cb); + /** This is called when the patcher discovers an up-to-date Python package or SDL file. + * \remarks This can be called from any thread when the patcher downloads or encounters an up-to-date + * python package or SDL file that the server knows about. + */ + void OnGameCodeDiscovery(GameCodeDiscoverFunc cb); + /** Set a callback that will be fired when the patcher receives a chunk from the server. The status string * will contain the current download speed. * \remarks This will be called from the network thread. */ void OnProgressTick(ProgressTickFunc cb); + void RequestGameCode(); void RequestManifest(const plString& mfs); void RequestManifest(const std::vector& mfs); diff --git a/Sources/Plasma/FeatureLib/pfSecurePreloader/CMakeLists.txt b/Sources/Plasma/FeatureLib/pfSecurePreloader/CMakeLists.txt deleted file mode 100644 index aad818e9..00000000 --- a/Sources/Plasma/FeatureLib/pfSecurePreloader/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -include_directories(../../CoreLib) -include_directories(../../NucleusLib) -include_directories(../../NucleusLib/inc) -include_directories(../../PubUtilLib) - -if(WIN32) - add_definitions(-DWIN32) -endif(WIN32) - -set(pfSecurePreloader_SOURCES - pfSecurePreloader.cpp -) - -set(pfSecurePreloader_HEADERS - pfSecurePreloader.h - pfSecurePreloaderCreatable.h -) - -add_library(pfSecurePreloader STATIC ${pfSecurePreloader_SOURCES} ${pfSecurePreloader_HEADERS}) - -source_group("Source Files" FILES ${pfSecurePreloader_SOURCES}) -source_group("Header Files" FILES ${pfSecurePreloader_HEADERS}) diff --git a/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.cpp b/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.cpp deleted file mode 100644 index a7e86ec1..00000000 --- a/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.cpp +++ /dev/null @@ -1,414 +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 . - -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 "pfSecurePreloader.h" - -#include "hsStream.h" -#include "plgDispatch.h" -#include "plCompression/plZlibStream.h" -#include "pnEncryption/plChecksum.h" -#include "plFile/plSecureStream.h" -#include "plFile/plStreamSource.h" -#include "plMessage/plNetCommMsgs.h" -#include "plMessage/plPreloaderMsg.h" -#include "plProgressMgr/plProgressMgr.h" - -bool gSkipPreload = false; -pfSecurePreloader* pfSecurePreloader::fInstance = nil; - -///////////////////////////////////////////////////////////////////// - -typedef std::pair WcharPair; - -struct AuthRequestParams -{ - pfSecurePreloader* fThis; - std::queue fFileGroups; - - AuthRequestParams(pfSecurePreloader* parent) - : fThis(parent) { } -}; - -///////////////////////////////////////////////////////////////////// - -void ProcAuthDownloadParams(AuthRequestParams* params); - -void GotAuthSrvManifest( - ENetError result, - void* param, - const NetCliAuthFileInfo infoArr[], - uint32_t infoCount -) { - AuthRequestParams* arp = (AuthRequestParams*)param; - if (IS_NET_ERROR(result)) - { - FATAL("Failed to get AuthSrv manifest!"); - arp->fThis->Terminate(); - delete arp; - } else { - arp->fThis->PreloadManifest(infoArr, infoCount); - ProcAuthDownloadParams(arp); - } -} - -void GotFileSrvManifest( - ENetError result, - void* param, - const wchar_t group[], - const NetCliFileManifestEntry manifest[], - uint32_t entryCount -) { - pfSecurePreloader* sp = (pfSecurePreloader*)param; - if (result == kNetErrFileNotFound) - { - AuthRequestParams* params = new AuthRequestParams(sp); - params->fFileGroups.push(WcharPair(L"Python", L"pak")); - params->fFileGroups.push(WcharPair(L"SDL", L"sdl")); - ProcAuthDownloadParams(params); - return; - } else if (!entryCount) { - FATAL("SecurePreloader manifest empty!"); - sp->Terminate(); - return; - } - - sp->PreloadManifest(manifest, entryCount); -} - -void FileDownloaded( - ENetError result, - void* param, - const plFileName & filename, - hsStream* writer -) { - pfSecurePreloader* sp = (pfSecurePreloader*)param; - if (IS_NET_ERROR(result)) - { - FATAL("SecurePreloader download failed"); - sp->Terminate(); - } else { - sp->FilePreloaded(filename, writer); - } -} - -void ProcAuthDownloadParams(AuthRequestParams* params) -{ - // Request the "manifests" until there are none left, then download the files - if (params->fFileGroups.empty()) - { - params->fThis->PreloadNextFile(); - delete params; - } else { - WcharPair wp = params->fFileGroups.front(); - params->fFileGroups.pop(); - NetCliAuthFileListRequest(wp.first, wp.second, GotAuthSrvManifest, params); - } -} - -///////////////////////////////////////////////////////////////////// - -class pfSecurePreloaderStream : public plZlibStream -{ - plOperationProgress* fProgress; - bool fIsZipped; - -public: - - pfSecurePreloaderStream(plOperationProgress* prog, bool zipped) - : fProgress(prog), fIsZipped(zipped), plZlibStream() - { - fOutput = new hsRAMStream; - } - - ~pfSecurePreloaderStream() - { - delete fOutput; - fOutput = nil; - plZlibStream::Close(); - } - - bool AtEnd() { return fOutput->AtEnd(); } - uint32_t GetEOF() { return fOutput->GetEOF(); } - uint32_t GetPosition() const { return fOutput->GetPosition(); } - uint32_t GetSizeLeft() const { return fOutput->GetSizeLeft(); } - uint32_t Read(uint32_t count, void* buf) { return fOutput->Read(count, buf); } - void Rewind() { fOutput->Rewind(); } - void SetPosition(uint32_t pos) { fOutput->SetPosition(pos); } - void Skip(uint32_t deltaByteCount) { fOutput->Skip(deltaByteCount); } - - uint32_t Write(uint32_t count, const void* buf) - { - if (fProgress) - fProgress->Increment((float)count); - if (fIsZipped) - return plZlibStream::Write(count, buf); - else - return fOutput->Write(count, buf); - } -}; - -///////////////////////////////////////////////////////////////////// - -hsRAMStream* pfSecurePreloader::LoadToMemory(const plFileName& file) const -{ - if (!plFileInfo(file).Exists()) - return nil; - - hsUNIXStream s; - hsRAMStream* ram = new hsRAMStream; - s.Open(file); - - uint32_t loadLen = 1024 * 1024; - uint8_t* buf = new uint8_t[loadLen]; - while (uint32_t read = s.Read(loadLen, buf)) - ram->Write(read, buf); - delete[] buf; - - s.Close(); - ram->Rewind(); - return ram; -} - -void pfSecurePreloader::SaveFile(hsStream* file, const plFileName& name) const -{ - hsUNIXStream s; - s.Open(name, "wb"); - uint32_t pos = file->GetPosition(); - file->Rewind(); - - uint32_t loadLen = 1024 * 1024; - uint8_t* buf = new uint8_t[loadLen]; - while (uint32_t read = file->Read(loadLen, buf)) - s.Write(read, buf); - file->SetPosition(pos); - s.Close(); - delete[] buf; -} - -bool pfSecurePreloader::IsZipped(const plFileName& filename) const -{ - return filename.GetFileExt().CompareI("gz") == 0; -} - -void pfSecurePreloader::PreloadNextFile() -{ - if (fManifestEntries.empty()) - { - Finish(); - return; - } - - plFileName filename = fDownloadEntries.front(); - hsStream* s = new pfSecurePreloaderStream(fProgress, IsZipped(filename)); - - // Thankfully, both callbacks have the same arguments - if (fLegacyMode) - NetCliAuthFileRequest(filename, s, FileDownloaded, this); - else - NetCliFileDownloadRequest(filename, s, FileDownloaded, this); -} - -void pfSecurePreloader::Init() -{ - if (!fInstance) - fInstance = new pfSecurePreloader; - - fInstance->RegisterAs(kSecurePreloader_KEY); - // TODO: If we're going to support reconnects, then let's do it right. - // Later... - //plgDispatch::Dispatch()->RegisterForExactType(plNetCommAuthConnectedMsg::Index(), fInstance->GetKey()); -} - -void pfSecurePreloader::Start() -{ -#ifndef PLASMA_EXTERNAL_RELEASE - // Finer grained control of the preloader allows us to have synched data but our own python/SDL - // This is useful on outdated/black-box shards like MOULa - if (gSkipPreload) - { - Finish(); - return; - } -#endif - - NetCliAuthGetEncryptionKey(fEncryptionKey, 4); - - // TODO: Localize - fProgress = plProgressMgr::GetInstance()->RegisterOperation(0.0f, "Checking for updates", plProgressMgr::kUpdateText, false, true); - - // Now, we need to fetch the "SecurePreloader" manifest from the file server, which will contain the python and SDL files. - // We're basically reimplementing plResPatcher here, except preferring to keep everything in memory, then flush to disk - // when we're done. If this fails, then we shall download everything from the AuthSrv like in the old days. - NetCliFileManifestRequest(GotFileSrvManifest, this, L"SecurePreloader"); -} - -void pfSecurePreloader::Terminate() -{ - FATAL("pfSecurePreloader failure"); - fProgress->SetAborting(); - - plPreloaderMsg* msg = new plPreloaderMsg; - msg->fSuccess = false; - plgDispatch::Dispatch()->MsgSend(msg); -} - -void pfSecurePreloader::Finish() -{ - plPreloaderMsg* msg = new plPreloaderMsg; - msg->fSuccess = true; - plgDispatch::Dispatch()->MsgSend(msg); -} - -void pfSecurePreloader::Shutdown() -{ - SetInstance(nil); - if (fProgress) - { - delete fProgress; - fProgress = nil; - } - - // Takes care of UnReffing us - UnRegisterAs(kSecurePreloader_KEY); -} - -void pfSecurePreloader::PreloadManifest(const NetCliAuthFileInfo manifestEntries[], uint32_t entryCount) -{ - uint32_t totalBytes = 0; - if (fProgress) - totalBytes = (uint32_t)fProgress->GetMax(); - fLegacyMode = true; - - for (uint32_t i = 0; i < entryCount; ++i) - { - const NetCliAuthFileInfo mfs = manifestEntries[i]; - plFileName filename = plString::FromWchar(mfs.filename); - fDownloadEntries.push(filename); - if (IsZipped(filename)) - fManifestEntries.push(filename.StripFileExt()); - else - fManifestEntries.push(filename); - - totalBytes += mfs.filesize; - } - - if (fProgress) - { - fProgress->SetLength((float)totalBytes); - fProgress->SetTitle("Downloading..."); - } -} - -void pfSecurePreloader::PreloadManifest(const NetCliFileManifestEntry manifestEntries[], uint32_t entryCount) -{ - uint32_t totalBytes = 0; - for (uint32_t i = 0; i < entryCount; ++i) - { - const NetCliFileManifestEntry mfs = manifestEntries[i]; - bool fetchMe = true; - hsRAMStream* s = nil; - plFileName clientName = plString::FromWchar(mfs.clientName); - plFileName downloadName = plString::FromWchar(mfs.downloadName); - - if (plFileInfo(clientName).Exists()) - { - s = LoadToMemory(clientName); - if (s) - { - // Damn this - plMD5Checksum srvHash; - srvHash.SetFromHexString(plString::FromWchar(mfs.md5, 32).c_str()); - - // Now actually copare the hashes - plMD5Checksum lclHash; - lclHash.CalcFromStream(s); - fetchMe = (srvHash != lclHash); - } - } - - if (fetchMe) - { - fManifestEntries.push(clientName); - fDownloadEntries.push(downloadName); - if (IsZipped(downloadName)) - totalBytes += mfs.zipSize; - else - totalBytes += mfs.fileSize; - } else { - plSecureStream* ss = new plSecureStream(s, fEncryptionKey); - plStreamSource::GetInstance()->InsertFile(clientName, ss); - } - - if (s) - delete s; - } - - if (totalBytes && fProgress) - { - fProgress->SetLength((float)totalBytes); - fProgress->SetTitle("Downloading..."); - } - - // This method uses only one manifest, so we're good to go now! - PreloadNextFile(); -} - -void pfSecurePreloader::FilePreloaded(const plFileName& file, hsStream* stream) -{ - // Clear out queue - fDownloadEntries.pop(); - plFileName clientName = fManifestEntries.front(); - fManifestEntries.pop(); - - if (!fLegacyMode) // AuthSrv data caching is useless - { - plFileSystem::CreateDir(clientName.StripFileName(), true); - SaveFile(stream, clientName); - } - - plSecureStream* ss = new plSecureStream(stream, fEncryptionKey); - plStreamSource::GetInstance()->InsertFile(clientName, ss); - delete stream; // SecureStream holds its own decrypted buffer - - // Continue down the warpath - PreloadNextFile(); -} diff --git a/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.h b/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.h deleted file mode 100644 index a4660fe4..00000000 --- a/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.h +++ /dev/null @@ -1,96 +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 . - -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 __pfSecurePreloader_h__ -#define __pfSecurePreloader_h__ - -#include "HeadSpin.h" -#include "pnKeyedObject/hsKeyedObject.h" -#include "plNetGameLib/plNetGameLib.h" -#include - -class plOperationProgress; -class hsRAMStream; - -/////////////////////////////////////////////////////////////////////////////// -// pfSecurePreloader - a class for handling files we want downloaded from the -// server into a temporary directory, secured, and deleted on exit. Puts stuff -// into plStreamSource for us -/////////////////////////////////////////////////////////////////////////////// -class pfSecurePreloader : public hsKeyedObject -{ -private: - - static pfSecurePreloader* fInstance; - std::queue fManifestEntries; - std::queue fDownloadEntries; - plOperationProgress* fProgress; - uint32_t fEncryptionKey[4]; - bool fLegacyMode; - - hsRAMStream* LoadToMemory(const plFileName& file) const; - void SaveFile(hsStream* file, const plFileName& name) const; - bool IsZipped(const plFileName& filename) const; - -public: - pfSecurePreloader() : fProgress(nil), fLegacyMode(false) { } - - CLASSNAME_REGISTER(pfSecurePreloader); - GETINTERFACE_ANY(pfSecurePreloader, hsKeyedObject); - - void Init(); - void Start(); - void Terminate(); - void Finish(); - void Shutdown(); - - void PreloadManifest(const NetCliFileManifestEntry manifestEntries[], uint32_t entryCount); - void PreloadManifest(const NetCliAuthFileInfo manifestEntries[], uint32_t entryCount); - void PreloadNextFile(); - void FilePreloaded(const plFileName& filename, hsStream* stream); - - plOperationProgress* GetProgressBar() { return fProgress; } - - static pfSecurePreloader* GetInstance() { return fInstance; } - static void SetInstance(pfSecurePreloader* instance) { fInstance = instance; } -}; - -#endif // __pfSecurePreloader_h__ diff --git a/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloaderCreatable.h b/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloaderCreatable.h deleted file mode 100644 index ee2e0209..00000000 --- a/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloaderCreatable.h +++ /dev/null @@ -1,57 +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 . - -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/FeatureLib/pfSecurePreloader/pfSecurePreloaderCreatable.h -* -***/ - -#ifndef PLASMA20_SOURCES_PLASMA_FEATURELIB_PFSECUREPRELOADER_PFSECUREPRELOADERCREATABLE_H -#define PLASMA20_SOURCES_PLASMA_FEATURELIB_PFSECUREPRELOADER_PFSECUREPRELOADERCREATABLE_H - -#include "pnFactory/plCreator.h" - -#include "pfSecurePreloader.h" -REGISTER_NONCREATABLE(pfSecurePreloader); - - -#endif // PLASMA20_SOURCES_PLASMA_FEATURELIB_PFSECUREPRELOADER_PFSECUREPRELOADERCREATABLE_H diff --git a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h index 262da7fe..a185eedc 100644 --- a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h +++ b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h @@ -84,7 +84,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plModifier), CLASS_INDEX(plSingleModifier), CLASS_INDEX(plSimpleModifier), - CLASS_INDEX(pfSecurePreloader), + CLASS_INDEX(UNUSED_pfSecurePreloader), CLASS_INDEX(UNUSED_plRandomTMModifier), CLASS_INDEX(plInterestingModifier), CLASS_INDEX(plDetectorModifier), @@ -725,7 +725,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plAvBrainDrive), CLASS_INDEX(plAvBrainSample), CLASS_INDEX(plAvBrainGeneric), - CLASS_INDEX(plPreloaderMsg), + CLASS_INDEX(UNUSED_plPreloaderMsg), CLASS_INDEX(plAvBrainLadder), CLASS_INDEX(plInputIfaceMgrMsg), CLASS_INDEX(pfKIMsg), diff --git a/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.cpp b/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.cpp index b0b0d5ce..f01895c2 100644 --- a/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.cpp +++ b/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.cpp @@ -118,8 +118,6 @@ plKeySeed SeedList[] = { { kJournalBookMgr_KEY, CLASS_INDEX_SCOPED( pfJournalBook ), "kJournalBookMgr_KEY", }, { kAgeLoader_KEY, CLASS_INDEX_SCOPED( plAgeLoader), "kAgeLoader_KEY", }, { kBuiltIn3rdPersonCamera_KEY, CLASS_INDEX_SCOPED( plCameraModifier1 ), "kBuiltIn3rdPersonCamera_KEY", }, - { kSecurePreloader_KEY, CLASS_INDEX_SCOPED( pfSecurePreloader ), "kSecurePreloader_KEY", }, - { kLast_Fixed_KEY, CLASS_INDEX_SCOPED( plSceneObject ), "kLast_Fixed_KEY", } }; diff --git a/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.h b/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.h index 31fa53ac..0e2cbc2b 100644 --- a/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.h +++ b/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.h @@ -85,7 +85,6 @@ enum plFixedKeyId kJournalBookMgr_KEY, kAgeLoader_KEY, kBuiltIn3rdPersonCamera_KEY, - kSecurePreloader_KEY, kLast_Fixed_KEY }; diff --git a/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp b/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp index a0139533..b3366867 100644 --- a/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp +++ b/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp @@ -45,12 +45,16 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plgDispatch.h" #include "plAgeLoader/plAgeLoader.h" +#include "plFile/plEncryptedStream.h" +#include "plFile/plStreamSource.h" +#include "plFile/plSecureStream.h" #include "plMessage/plResPatcherMsg.h" #include "pfPatcher/pfPatcher.h" #include "plProgressMgr/plProgressMgr.h" #include "plResMgr/plResManager.h" extern bool gDataServerLocal; +bool gSkipPreload = false; ///////////////////////////////////////////////////////////////////////////// @@ -98,6 +102,21 @@ void plResPatcher::OnFileDownloaded(const plFileName& file) } } +bool plResPatcher::OnGameCodeDiscovered(const plFileName& file, hsStream* stream) +{ + plSecureStream* ss = new plSecureStream(false, plStreamSource::GetInstance()->GetEncryptionKey()); + if (ss->Open(stream)) { + plStreamSource::GetInstance()->InsertFile(file, ss); + + // SecureStream will hold a decrypted buffer... + stream->Close(); + delete stream; + } else + plStreamSource::GetInstance()->InsertFile(file, stream); + + return true; // ASSume success for now... +} + void plResPatcher::OnProgressTick(uint64_t dl, uint64_t total, const plString& msg) { if (dl && total) { @@ -121,6 +140,14 @@ pfPatcher* plResPatcher::CreatePatcher() patcher->OnFileDownloadBegin(std::bind(&plResPatcher::OnFileDownloadBegin, this, std::placeholders::_1)); patcher->OnFileDownloaded(std::bind(&plResPatcher::OnFileDownloaded, this, std::placeholders::_1)); patcher->OnProgressTick(std::bind(&plResPatcher::OnProgressTick, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + + // sneaky hax: do the old SecurePreloader thing.... except here + if (!fRequestedGameCode && !gSkipPreload) { + patcher->OnGameCodeDiscovery(std::bind(&plResPatcher::OnGameCodeDiscovered, this, std::placeholders::_1, std::placeholders::_2)); + patcher->RequestGameCode(); + fRequestedGameCode = true; + } + return patcher; } @@ -133,7 +160,7 @@ void plResPatcher::InitProgress() ///////////////////////////////////////////////////////////////////////////// plResPatcher::plResPatcher() - : fProgress(nullptr) { } + : fProgress(nullptr), fRequestedGameCode(false) { } plResPatcher::~plResPatcher() { diff --git a/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h b/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h index 8987fbb0..ee29e962 100644 --- a/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h +++ b/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h @@ -58,12 +58,14 @@ class plResPatcher { plOperationProgress* fProgress; static plResPatcher* fInstance; + bool fRequestedGameCode; friend class plAgeLoader; void OnCompletion(ENetError, const plString& msg); void OnFileDownloadBegin(const plFileName& file); void OnFileDownloaded(const plFileName& file); + bool OnGameCodeDiscovered(const plFileName& file, class hsStream* stream); void OnProgressTick(uint64_t dl, uint64_t total, const plString& msg); class pfPatcher* CreatePatcher(); diff --git a/Sources/Plasma/PubUtilLib/plFile/plSecureStream.cpp b/Sources/Plasma/PubUtilLib/plFile/plSecureStream.cpp index 87bf0f15..391bb807 100644 --- a/Sources/Plasma/PubUtilLib/plFile/plSecureStream.cpp +++ b/Sources/Plasma/PubUtilLib/plFile/plSecureStream.cpp @@ -667,17 +667,13 @@ bool plSecureStream::IsSecureFile(const plFileName& fileName) hsStream* plSecureStream::OpenSecureFile(const plFileName& fileName, const uint32_t flags /* = kRequireEncryption */, uint32_t* key /* = nil */) { bool requireEncryption = flags & kRequireEncryption; -#ifndef PLASMA_EXTERNAL_RELEASE - requireEncryption = false; -#endif - bool deleteOnExit = flags & kDeleteOnExit; bool isEncrypted = IsSecureFile(fileName); - hsStream* s = nil; + hsStream* s = nullptr; if (isEncrypted) s = new plSecureStream(deleteOnExit, key); - else if (!requireEncryption) // If this isn't an external release, let them use unencrypted data + else if (!requireEncryption) s = new hsUNIXStream; if (s) diff --git a/Sources/Plasma/PubUtilLib/plFile/plStreamSource.cpp b/Sources/Plasma/PubUtilLib/plFile/plStreamSource.cpp index ba8b145a..cba9a979 100644 --- a/Sources/Plasma/PubUtilLib/plFile/plStreamSource.cpp +++ b/Sources/Plasma/PubUtilLib/plFile/plStreamSource.cpp @@ -40,7 +40,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ -#include +#include "HeadSpin.h" #include "plStreamSource.h" #include "plSecureStream.h" #include "plEncryptedStream.h" @@ -49,8 +49,15 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com # include #endif +plStreamSource::plStreamSource() +{ + memset(fServerKey, 0, arrsize(fServerKey)); +} + void plStreamSource::ICleanup() { + hsTempMutexLock lock(fMutex); + // loop through all the file data records, and delete the streams decltype(fFileData.begin()) curData; for (curData = fFileData.begin(); curData != fFileData.end(); curData++) @@ -65,6 +72,8 @@ void plStreamSource::ICleanup() hsStream* plStreamSource::GetFile(const plFileName& filename) { + hsTempMutexLock lock(fMutex); + plFileName sFilename = filename.Normalize('/'); if (fFileData.find(sFilename) == fFileData.end()) { @@ -78,14 +87,15 @@ hsStream* plStreamSource::GetFile(const plFileName& filename) fFileData[sFilename].fExt = sFilename.GetFileExt(); if (plSecureStream::IsSecureFile(filename)) { - uint32_t encryptionKey[4]; - if (!plSecureStream::GetSecureEncryptionKey(filename, encryptionKey, 4)) - { - FATAL("Hey camper... You need an NTD key file!"); - return nil; - } + hsStream* ss = nullptr; - fFileData[sFilename].fStream = plSecureStream::OpenSecureFile(filename, 0, encryptionKey); + uint32_t encryptionKey[4]; + if (plSecureStream::GetSecureEncryptionKey(filename, encryptionKey, 4)) + ss = plSecureStream::OpenSecureFile(filename, 0, encryptionKey); + else + ss = plSecureStream::OpenSecureFile(filename, 0, fServerKey); + fFileData[sFilename].fStream = ss; + hsAssert(ss, "failed to open a SecureStream for a disc file!"); } else // otherwise it is an encrypted or plain stream, this call handles both fFileData[sFilename].fStream = plEncryptedStream::OpenEncryptedFile(filename); @@ -102,6 +112,7 @@ std::vector plStreamSource::GetListOfNames(const plFileName& dir, co { plFileName sDir = dir.Normalize('/'); hsAssert(ext.CharAt(0) != '.', "Don't add a dot"); + hsTempMutexLock lock(fMutex); // loop through all the file data records, and create the list std::vector retVal; @@ -131,6 +142,7 @@ bool plStreamSource::InsertFile(const plFileName& filename, hsStream* stream) { plFileName sFilename = filename.Normalize('/'); + hsTempMutexLock lock(fMutex); if (fFileData.find(sFilename) != fFileData.end()) return false; // duplicate entry, return failure diff --git a/Sources/Plasma/PubUtilLib/plFile/plStreamSource.h b/Sources/Plasma/PubUtilLib/plFile/plStreamSource.h index a565ad95..f9b3908c 100644 --- a/Sources/Plasma/PubUtilLib/plFile/plStreamSource.h +++ b/Sources/Plasma/PubUtilLib/plFile/plStreamSource.h @@ -43,8 +43,8 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #define plStreamSource_h_inc #include -#include #include "hsStream.h" +#include "hsThread.h" // A class for holding and accessing file streams. The preloader will insert // files in here once they are loaded. In internal builds, if a requested file @@ -60,10 +60,12 @@ private: hsStream* fStream; // we own this pointer, so clean it up }; std::map fFileData; // key is filename + hsMutex fMutex; + uint32_t fServerKey[4]; void ICleanup(); // closes all file pointers and cleans up after itself - plStreamSource() {} + plStreamSource(); public: ~plStreamSource() {ICleanup();} @@ -77,6 +79,9 @@ public: // For other classes to insert files (takes ownership of the stream if successful) bool InsertFile(const plFileName& filename, hsStream* stream); + /** Gets a pointer to our encryption key */ + uint32_t* GetEncryptionKey() { return fServerKey; } + // Instance handling static plStreamSource* GetInstance(); }; diff --git a/Sources/Plasma/PubUtilLib/plMessage/CMakeLists.txt b/Sources/Plasma/PubUtilLib/plMessage/CMakeLists.txt index 8caa137c..077f347f 100644 --- a/Sources/Plasma/PubUtilLib/plMessage/CMakeLists.txt +++ b/Sources/Plasma/PubUtilLib/plMessage/CMakeLists.txt @@ -98,7 +98,6 @@ set(plMessage_HEADERS plOneShotMsg.h plParticleUpdateMsg.h plPickedMsg.h - plPreloaderMsg.h plRenderMsg.h plRenderRequestMsg.h plReplaceGeometryMsg.h diff --git a/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h b/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h index 906e050c..812f998a 100644 --- a/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h +++ b/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h @@ -319,9 +319,6 @@ REGISTER_CREATABLE(plNetCommPublicAgeListMsg); REGISTER_CREATABLE(plNetCommPublicAgeMsg); REGISTER_CREATABLE(plNetCommRegisterAgeMsg); -#include "plPreloaderMsg.h" -REGISTER_CREATABLE(plPreloaderMsg); - #include "plNetClientMgrMsg.h" REGISTER_CREATABLE(plNetClientMgrMsg); diff --git a/Sources/Plasma/PubUtilLib/plMessage/plPreloaderMsg.h b/Sources/Plasma/PubUtilLib/plMessage/plPreloaderMsg.h deleted file mode 100644 index 3cf88dd8..00000000 --- a/Sources/Plasma/PubUtilLib/plMessage/plPreloaderMsg.h +++ /dev/null @@ -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 . - -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/PubUtilLib/plMessage/plPreloaderMsg.h -* -***/ - -#ifndef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLMESSAGE_PLPRELOADERMSG_H -#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLMESSAGE_PLPRELOADERMSG_H - -#include "pnMessage/plMessage.h" - -class plPreloaderMsg : public plMessage { -public: - bool fSuccess; - - plPreloaderMsg () { SetBCastFlag(kBCastByExactType); } - - CLASSNAME_REGISTER(plPreloaderMsg); - GETINTERFACE_ANY(plPreloaderMsg, plMessage); - - void Read (hsStream* stream, hsResMgr* ) { FATAL("plPreloaderMsg::Read"); } - void Write (hsStream* stream, hsResMgr* ) { FATAL("plPreloaderMsg::Write"); } -}; - - -#endif // PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLMESSAGE_PLPRELOADERMSG_H diff --git a/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp b/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp index 4db84e39..58094b1e 100644 --- a/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp @@ -60,6 +60,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plVault/plVault.h" #include "plMessage/plAccountUpdateMsg.h" #include "plNetClient/plNetClientMgr.h" +#include "plFile/plStreamSource.h" #include "pfMessage/pfKIMsg.h" @@ -414,6 +415,9 @@ static void INetCliAuthLoginRequestCallback ( if (!wantsStartUpAge && 0 == StrCmpI(s_players[i].playerName, s_iniStartupPlayerName, (unsigned)-1)) s_player = &s_players[i]; } + + // store this server's encryption key for posterity + NetCliAuthGetEncryptionKey(plStreamSource::GetInstance()->GetEncryptionKey(), 4); } else s_account.accountUuid = kNilUuid; diff --git a/Sources/Tools/MaxMain/CMakeLists.txt b/Sources/Tools/MaxMain/CMakeLists.txt index 0df9965a..71b2ec93 100644 --- a/Sources/Tools/MaxMain/CMakeLists.txt +++ b/Sources/Tools/MaxMain/CMakeLists.txt @@ -124,7 +124,6 @@ target_link_libraries(MaxMain pfJournalBook) target_link_libraries(MaxMain pfLocalizationMgr) target_link_libraries(MaxMain pfMessage) target_link_libraries(MaxMain pfPython) -target_link_libraries(MaxMain pfSecurePreloader) target_link_libraries(MaxMain pfSurface) target_link_libraries(MaxMain plAgeDescription) target_link_libraries(MaxMain plAgeLoader) diff --git a/Sources/Tools/MaxPlasmaLights/CMakeLists.txt b/Sources/Tools/MaxPlasmaLights/CMakeLists.txt index cbb6402d..1a890d1b 100644 --- a/Sources/Tools/MaxPlasmaLights/CMakeLists.txt +++ b/Sources/Tools/MaxPlasmaLights/CMakeLists.txt @@ -57,7 +57,6 @@ target_link_libraries(MaxPlasmaLights pfJournalBook) target_link_libraries(MaxPlasmaLights pfLocalizationMgr) target_link_libraries(MaxPlasmaLights pfMessage) target_link_libraries(MaxPlasmaLights pfPython) -target_link_libraries(MaxPlasmaLights pfSecurePreloader) target_link_libraries(MaxPlasmaLights pfSurface) target_link_libraries(MaxPlasmaLights plAgeDescription) target_link_libraries(MaxPlasmaLights plAgeLoader) From 34c88190c177e01c1cddd1bfddc7fe180806619d Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Mon, 25 Nov 2013 21:29:56 -0500 Subject: [PATCH 6/6] Update StartUp in initial batch ONE PATCH! This commit brings us to half of the patching goal: on launch of the client, we only show ONE "Updating..." message before we enter the game proper. Go us! --- Sources/Plasma/FeatureLib/pfPatcher/plManifests.cpp | 1 + Sources/Plasma/FeatureLib/pfPatcher/plManifests.h | 1 - .../PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp | 13 +++++++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Sources/Plasma/FeatureLib/pfPatcher/plManifests.cpp b/Sources/Plasma/FeatureLib/pfPatcher/plManifests.cpp index d368c2db..71a135e4 100644 --- a/Sources/Plasma/FeatureLib/pfPatcher/plManifests.cpp +++ b/Sources/Plasma/FeatureLib/pfPatcher/plManifests.cpp @@ -84,6 +84,7 @@ std::vector plManifest::EssentialGameManifests() mfs.push_back("GlobalClothing"); mfs.push_back("GlobalMarkers"); mfs.push_back("GUI"); + mfs.push_back("StartUp"); return mfs; } diff --git a/Sources/Plasma/FeatureLib/pfPatcher/plManifests.h b/Sources/Plasma/FeatureLib/pfPatcher/plManifests.h index f21d039a..c436ec23 100644 --- a/Sources/Plasma/FeatureLib/pfPatcher/plManifests.h +++ b/Sources/Plasma/FeatureLib/pfPatcher/plManifests.h @@ -67,7 +67,6 @@ namespace plManifest /** Get a vector containing all manifests the game requires to initialize. */ std::vector EssentialGameManifests(); - } #endif // _plManifests_inc_ diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp b/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp index 9d012d52..197b4e65 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp @@ -224,13 +224,18 @@ void plNCAgeJoiner::Start () { // if we're linking to startup then set the OfflineAge flag // so we by-pass the game server - if (StrLen(age.ageDatasetName) == 0 || StrCmpI(age.ageDatasetName, "StartUp") == 0) + if (StrLen(age.ageDatasetName) == 0 || StrCmpI(age.ageDatasetName, "StartUp") == 0) { nc->SetFlagsBit(plNetClientApp::kLinkingToOfflineAge); - else + + // no need to update if we're not using a GameSrv + plgDispatch::MsgSend(new plResPatcherMsg()); + } else { nc->SetFlagsBit(plNetClientApp::kLinkingToOfflineAge, false); - plAgeLoader* al = plAgeLoader::GetInstance(); - al->UpdateAge(age.ageDatasetName); + // we only need to update the age if we're using a GameSrv + plAgeLoader* al = plAgeLoader::GetInstance(); + al->UpdateAge(age.ageDatasetName); + } } //============================================================================