Browse Source

Integrate SecurePreloader into pfPatcher

Adam Johnson 11 years ago
parent
commit
3299267976
  1. 1
      Sources/Plasma/Apps/plClient/CMakeLists.txt
  2. 43
      Sources/Plasma/Apps/plClient/plClient.cpp
  3. 1
      Sources/Plasma/Apps/plClient/plClient.h
  4. 2
      Sources/Plasma/CoreLib/hsStream.h
  5. 1
      Sources/Plasma/FeatureLib/CMakeLists.txt
  6. 1
      Sources/Plasma/FeatureLib/inc/pfAllCreatables.h
  7. 170
      Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp
  8. 13
      Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.h
  9. 22
      Sources/Plasma/FeatureLib/pfSecurePreloader/CMakeLists.txt
  10. 414
      Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.cpp
  11. 96
      Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.h
  12. 57
      Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloaderCreatable.h
  13. 4
      Sources/Plasma/NucleusLib/inc/plCreatableIndex.h
  14. 2
      Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.cpp
  15. 1
      Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.h
  16. 29
      Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp
  17. 2
      Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h
  18. 8
      Sources/Plasma/PubUtilLib/plFile/plSecureStream.cpp
  19. 28
      Sources/Plasma/PubUtilLib/plFile/plStreamSource.cpp
  20. 9
      Sources/Plasma/PubUtilLib/plFile/plStreamSource.h
  21. 1
      Sources/Plasma/PubUtilLib/plMessage/CMakeLists.txt
  22. 3
      Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h
  23. 67
      Sources/Plasma/PubUtilLib/plMessage/plPreloaderMsg.h
  24. 4
      Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp
  25. 1
      Sources/Tools/MaxMain/CMakeLists.txt
  26. 1
      Sources/Tools/MaxPlasmaLights/CMakeLists.txt

1
Sources/Plasma/Apps/plClient/CMakeLists.txt

@ -86,7 +86,6 @@ target_link_libraries(plClient pfJournalBook)
target_link_libraries(plClient pfLocalizationMgr) target_link_libraries(plClient pfLocalizationMgr)
target_link_libraries(plClient pfMessage) target_link_libraries(plClient pfMessage)
target_link_libraries(plClient pfPython) target_link_libraries(plClient pfPython)
target_link_libraries(plClient pfSecurePreloader)
target_link_libraries(plClient pfSurface) target_link_libraries(plClient pfSurface)
target_link_libraries(plClient plAgeDescription) target_link_libraries(plClient plAgeDescription)
target_link_libraries(plClient plAgeLoader) target_link_libraries(plClient plAgeLoader)

43
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 "pnMessage/plCameraMsg.h"
#include "plMessage/plTransitionMsg.h" #include "plMessage/plTransitionMsg.h"
#include "plMessage/plLinkToAgeMsg.h" #include "plMessage/plLinkToAgeMsg.h"
#include "plMessage/plPreloaderMsg.h"
#include "plMessage/plNetCommMsgs.h" #include "plMessage/plNetCommMsgs.h"
#include "plMessage/plAgeLoadedMsg.h" #include "plMessage/plAgeLoadedMsg.h"
#include "plMessage/plResPatcherMsg.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 "plNetCommon/plNetCommonConstants.h"
#include "plNetGameLib/plNetGameLib.h" #include "plNetGameLib/plNetGameLib.h"
#include "pfSecurePreloader/pfSecurePreloader.h"
#include "pfLocalizationMgr/pfLocalizationMgr.h" #include "pfLocalizationMgr/pfLocalizationMgr.h"
#include "pfPatcher/plManifests.h" #include "pfPatcher/plManifests.h"
@ -313,11 +311,6 @@ bool plClient::Shutdown()
plAgeLoader::SetInstance(nil); plAgeLoader::SetInstance(nil);
} }
if (pfSecurePreloader::GetInstance())
{
pfSecurePreloader::GetInstance()->Shutdown(); // will unregister itself
}
if (fInputManager) if (fInputManager)
{ {
fInputManager->UnRegisterAs(kInput_KEY); fInputManager->UnRegisterAs(kInput_KEY);
@ -853,14 +846,6 @@ bool plClient::MsgReceive(plMessage* msg)
return true; return true;
} }
//============================================================================
// plPreloaderMsg
//============================================================================
if (plPreloaderMsg * preloaderMsg = plPreloaderMsg::ConvertNoRef(msg)) {
IHandlePreloaderMsg(preloaderMsg);
return true;
}
//============================================================================ //============================================================================
// plResPatcherMsg // plResPatcherMsg
//============================================================================ //============================================================================
@ -1565,7 +1550,6 @@ bool plClient::StartInit()
plgDispatch::Dispatch()->RegisterForExactType(plNetCommAuthMsg::Index(), GetKey()); plgDispatch::Dispatch()->RegisterForExactType(plNetCommAuthMsg::Index(), GetKey());
plNetClientMgr::GetInstance()->Init(); plNetClientMgr::GetInstance()->Init();
plAgeLoader::GetInstance()->Init(); plAgeLoader::GetInstance()->Init();
pfSecurePreloader::GetInstance()->Init();
plCmdIfaceModMsg* pModMsg2 = new plCmdIfaceModMsg; plCmdIfaceModMsg* pModMsg2 = new plCmdIfaceModMsg;
pModMsg2->SetBCastFlag(plMessage::kBCastByExactType); pModMsg2->SetBCastFlag(plMessage::kBCastByExactType);
@ -2516,27 +2500,6 @@ void plClient::ICompleteInit () {
clientMsg->Send(); 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) { void plClient::IHandlePatcherMsg (plResPatcherMsg * msg) {
plgDispatch::Dispatch()->UnRegisterForExactType(plResPatcherMsg::Index(), GetKey()); plgDispatch::Dispatch()->UnRegisterForExactType(plResPatcherMsg::Index(), GetKey());
@ -2569,8 +2532,6 @@ void plClient::IHandleNetCommAuthMsg (plNetCommAuthMsg * msg) {
return; return;
} }
plgDispatch::Dispatch()->RegisterForExactType(plPreloaderMsg::Index(), GetKey()); // Patch them global files!
IPatchGlobalAgeFiles();
// Precache our secure files
pfSecurePreloader::GetInstance()->Start();
} }

1
Sources/Plasma/Apps/plClient/plClient.h

@ -181,7 +181,6 @@ protected:
void ICompleteInit (); void ICompleteInit ();
void IOnAsyncInitComplete (); void IOnAsyncInitComplete ();
void IHandlePatcherMsg (plResPatcherMsg * msg); void IHandlePatcherMsg (plResPatcherMsg * msg);
void IHandlePreloaderMsg (plPreloaderMsg * msg);
void IHandleNetCommAuthMsg (plNetCommAuthMsg * msg); void IHandleNetCommAuthMsg (plNetCommAuthMsg * msg);
bool IHandleAgeLoaded2Msg (plAgeLoaded2Msg * msg); bool IHandleAgeLoaded2Msg (plAgeLoaded2Msg * msg);

2
Sources/Plasma/CoreLib/hsStream.h

@ -350,7 +350,7 @@ public:
virtual ~hsRAMStream(); virtual ~hsRAMStream();
virtual bool Open(const plFileName &, const char *) { hsAssert(0, "hsRAMStream::Open NotImplemented"); return false; } 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(); virtual bool AtEnd();

1
Sources/Plasma/FeatureLib/CMakeLists.txt

@ -19,5 +19,4 @@ add_subdirectory(pfLocalizationMgr)
add_subdirectory(pfMessage) add_subdirectory(pfMessage)
add_subdirectory(pfPatcher) add_subdirectory(pfPatcher)
add_subdirectory(pfPython) add_subdirectory(pfPython)
add_subdirectory(pfSecurePreloader)
add_subdirectory(pfSurface) add_subdirectory(pfSurface)

1
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 "pfCCR/plCCRCreatable.h"
#include "pfJournalBook/pfJournalBookCreatable.h" #include "pfJournalBook/pfJournalBookCreatable.h"
#include "pfGameMgr/pfGameMgrCreatables.h" #include "pfGameMgr/pfGameMgrCreatables.h"
#include "pfSecurePreloader/pfSecurePreloaderCreatable.h"
#endif // pfAllCreatables_inc #endif // pfAllCreatables_inc

170
Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp

@ -68,7 +68,7 @@ struct pfPatcherWorker : public hsThread
/** Represents a File/Auth download request */ /** Represents a File/Auth download request */
struct Request struct Request
{ {
enum { kFile, kManifest }; enum { kFile, kManifest, kSecurePreloader, kAuthFile, kPythonList, kSdlList };
plString fName; plString fName;
uint8_t fType; uint8_t fType;
@ -101,6 +101,7 @@ struct pfPatcherWorker : public hsThread
pfPatcher::CompletionFunc fOnComplete; pfPatcher::CompletionFunc fOnComplete;
pfPatcher::FileDownloadFunc fFileBeginDownload; pfPatcher::FileDownloadFunc fFileBeginDownload;
pfPatcher::FileDownloadFunc fFileDownloaded; pfPatcher::FileDownloadFunc fFileDownloaded;
pfPatcher::GameCodeDiscoverFunc fGameCodeDiscovered;
pfPatcher::ProgressTickFunc fProgressTick; pfPatcher::ProgressTickFunc fProgressTick;
pfPatcher* fParent; pfPatcher* fParent;
@ -119,6 +120,7 @@ struct pfPatcherWorker : public hsThread
bool IssueRequest(); bool IssueRequest();
virtual hsError Run(); virtual hsError Run();
void ProcessFile(); void ProcessFile();
void WhitelistFile(const plFileName& file, bool justDownloaded, hsStream* s=nullptr);
}; };
// =================================================== // ===================================================
@ -150,6 +152,13 @@ class pfPatcherStream : public plZlibStream
} }
public: 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) pfPatcherStream(pfPatcherWorker* parent, const plFileName& filename, const NetCliFileManifestEntry& entry)
: fParent(parent), fFlags(entry.flags), fBytesWritten(0) : fParent(parent), fFlags(entry.flags), fBytesWritten(0)
{ {
@ -179,6 +188,15 @@ public:
return fOutput->Write(count, buf); 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(); } void Begin() { fDLStartTime = hsTimer::GetSysSeconds(); }
plFileName GetFileName() const { return fFilename; } plFileName GetFileName() const { return fFilename; }
void Unlink() const { plFileSystem::Unlink(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<pfPatcherWorker*>(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<pfPatcherWorker*>(param); pfPatcherWorker* patcher = static_cast<pfPatcherWorker*>(param);
if (IS_NET_SUCCESS(result)) { 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); hsTempMutexLock lock(patcher->fRequestMut);
for (unsigned i = 0; i < entryCount; ++i) for (unsigned i = 0; i < infoCount; ++i) {
patcher->fQueuedFiles.push_back(manifest[i]); PatcherLogYellow("\tEnqueuing Legacy File '%S'", infoArr[i].filename);
patcher->fFileSignal.Signal();
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(); patcher->IssueRequest();
} else { } 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<pfPatcherWorker*>(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<pfPatcherWorker*>(param);
if (IS_NET_SUCCESS(result))
IHandleManifestDownload(patcher, group, manifest, entryCount);
else {
PatcherLogRed("\tDownload Failed: Manifest '%S'", group); PatcherLogRed("\tDownload Failed: Manifest '%S'", group);
patcher->EndPatch(result, plString::FromWchar(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)) { if (IS_NET_SUCCESS(result)) {
PatcherLogGreen("\tDownloaded File '%s'", stream->GetFileName().AsString().c_str()); PatcherLogGreen("\tDownloaded File '%s'", stream->GetFileName().AsString().c_str());
if (patcher->fFileDownloaded) patcher->WhitelistFile(stream->GetFileName(), true);
patcher->fFileDownloaded(stream->GetFileName());
patcher->IssueRequest(); patcher->IssueRequest();
} else { } else {
PatcherLogRed("\tDownloaded Failed: File '%s'", stream->GetFileName().AsString().c_str()); PatcherLogRed("\tDownloaded Failed: File '%s'", stream->GetFileName().AsString().c_str());
@ -299,6 +390,25 @@ bool pfPatcherWorker::IssueRequest()
case Request::kManifest: case Request::kManifest:
NetCliFileManifestRequest(IFileManifestDownloadCB, this, req.fName.ToWchar()); NetCliFileManifestRequest(IFileManifestDownloadCB, this, req.fName.ToWchar());
break; 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); DEFAULT_FATAL(req.fType);
} }
@ -347,7 +457,7 @@ void pfPatcherWorker::ProcessFile()
const NetCliFileManifestEntry& entry = fQueuedFiles.front(); const NetCliFileManifestEntry& entry = fQueuedFiles.front();
// eap sucks // eap sucks
plString clName = plString::FromWchar(entry.clientName); plFileName clName = plString::FromWchar(entry.clientName);
plString dlName = plString::FromWchar(entry.downloadName); plString dlName = plString::FromWchar(entry.downloadName);
// Check to see if ours matches // Check to see if ours matches
@ -358,6 +468,7 @@ void pfPatcherWorker::ProcessFile()
srvMD5.SetFromHexString(plString::FromWchar(entry.md5, 32).c_str()); srvMD5.SetFromHexString(plString::FromWchar(entry.md5, 32).c_str());
if (cliMD5 == srvMD5) { if (cliMD5 == srvMD5) {
WhitelistFile(clName, false);
fQueuedFiles.pop_front(); fQueuedFiles.pop_front();
continue; continue;
} }
@ -379,6 +490,33 @@ void pfPatcherWorker::ProcessFile()
} while (!fQueuedFiles.empty()); } 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() plStatusLog* pfPatcher::GetLog()
@ -414,6 +552,11 @@ void pfPatcher::OnFileDownloaded(FileDownloadFunc cb)
fWorker->fFileDownloaded = cb; fWorker->fFileDownloaded = cb;
} }
void pfPatcher::OnGameCodeDiscovery(GameCodeDiscoverFunc cb)
{
fWorker->fGameCodeDiscovered = cb;
}
void pfPatcher::OnProgressTick(ProgressTickFunc cb) void pfPatcher::OnProgressTick(ProgressTickFunc cb)
{ {
fWorker->fProgressTick = 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) void pfPatcher::RequestManifest(const plString& mfs)
{ {
hsTempMutexLock lock(fWorker->fRequestMut); hsTempMutexLock lock(fWorker->fRequestMut);
@ -447,4 +596,3 @@ bool pfPatcher::Start()
} }
return false; return false;
} }

13
Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.h

@ -52,6 +52,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
class plFileName; class plFileName;
class plStatusLog; class plStatusLog;
class hsStream;
/** Plasma File Patcher /** Plasma File Patcher
* This is used to patch the client with one or many manifests at once. It assumes that * 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. */ /** Represents a function that takes (const plFileName&) on an interesting file operation. */
typedef std::function<void(const plFileName&)> FileDownloadFunc; typedef std::function<void(const plFileName&)> 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<bool(const plFileName&, hsStream*)> GameCodeDiscoverFunc;
/** Represents a function that takes (bytesDLed, totalBytes, statsStr) as a progress indicator. */ /** Represents a function that takes (bytesDLed, totalBytes, statsStr) as a progress indicator. */
typedef std::function<void(uint64_t, uint64_t, const plString&)> ProgressTickFunc; typedef std::function<void(uint64_t, uint64_t, const plString&)> ProgressTickFunc;
@ -94,12 +100,19 @@ public:
*/ */
void OnFileDownloaded(FileDownloadFunc cb); 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 /** 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. * will contain the current download speed.
* \remarks This will be called from the network thread. * \remarks This will be called from the network thread.
*/ */
void OnProgressTick(ProgressTickFunc cb); void OnProgressTick(ProgressTickFunc cb);
void RequestGameCode();
void RequestManifest(const plString& mfs); void RequestManifest(const plString& mfs);
void RequestManifest(const std::vector<plString>& mfs); void RequestManifest(const std::vector<plString>& mfs);

22
Sources/Plasma/FeatureLib/pfSecurePreloader/CMakeLists.txt

@ -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})

414
Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.cpp

@ -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 <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "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<const wchar_t*, const wchar_t*> WcharPair;
struct AuthRequestParams
{
pfSecurePreloader* fThis;
std::queue<WcharPair> 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();
}

96
Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.h

@ -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 <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef __pfSecurePreloader_h__
#define __pfSecurePreloader_h__
#include "HeadSpin.h"
#include "pnKeyedObject/hsKeyedObject.h"
#include "plNetGameLib/plNetGameLib.h"
#include <queue>
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<plFileName> fManifestEntries;
std::queue<plFileName> 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__

57
Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloaderCreatable.h

@ -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 <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/*****************************************************************************
*
* $/Plasma20/Sources/Plasma/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

4
Sources/Plasma/NucleusLib/inc/plCreatableIndex.h

@ -84,7 +84,7 @@ CLASS_INDEX_LIST_START
CLASS_INDEX(plModifier), CLASS_INDEX(plModifier),
CLASS_INDEX(plSingleModifier), CLASS_INDEX(plSingleModifier),
CLASS_INDEX(plSimpleModifier), CLASS_INDEX(plSimpleModifier),
CLASS_INDEX(pfSecurePreloader), CLASS_INDEX(UNUSED_pfSecurePreloader),
CLASS_INDEX(UNUSED_plRandomTMModifier), CLASS_INDEX(UNUSED_plRandomTMModifier),
CLASS_INDEX(plInterestingModifier), CLASS_INDEX(plInterestingModifier),
CLASS_INDEX(plDetectorModifier), CLASS_INDEX(plDetectorModifier),
@ -725,7 +725,7 @@ CLASS_INDEX_LIST_START
CLASS_INDEX(plAvBrainDrive), CLASS_INDEX(plAvBrainDrive),
CLASS_INDEX(plAvBrainSample), CLASS_INDEX(plAvBrainSample),
CLASS_INDEX(plAvBrainGeneric), CLASS_INDEX(plAvBrainGeneric),
CLASS_INDEX(plPreloaderMsg), CLASS_INDEX(UNUSED_plPreloaderMsg),
CLASS_INDEX(plAvBrainLadder), CLASS_INDEX(plAvBrainLadder),
CLASS_INDEX(plInputIfaceMgrMsg), CLASS_INDEX(plInputIfaceMgrMsg),
CLASS_INDEX(pfKIMsg), CLASS_INDEX(pfKIMsg),

2
Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.cpp

@ -118,8 +118,6 @@ plKeySeed SeedList[] = {
{ kJournalBookMgr_KEY, CLASS_INDEX_SCOPED( pfJournalBook ), "kJournalBookMgr_KEY", }, { kJournalBookMgr_KEY, CLASS_INDEX_SCOPED( pfJournalBook ), "kJournalBookMgr_KEY", },
{ kAgeLoader_KEY, CLASS_INDEX_SCOPED( plAgeLoader), "kAgeLoader_KEY", }, { kAgeLoader_KEY, CLASS_INDEX_SCOPED( plAgeLoader), "kAgeLoader_KEY", },
{ kBuiltIn3rdPersonCamera_KEY, CLASS_INDEX_SCOPED( plCameraModifier1 ), "kBuiltIn3rdPersonCamera_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", } { kLast_Fixed_KEY, CLASS_INDEX_SCOPED( plSceneObject ), "kLast_Fixed_KEY", }
}; };

1
Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.h

@ -85,7 +85,6 @@ enum plFixedKeyId
kJournalBookMgr_KEY, kJournalBookMgr_KEY,
kAgeLoader_KEY, kAgeLoader_KEY,
kBuiltIn3rdPersonCamera_KEY, kBuiltIn3rdPersonCamera_KEY,
kSecurePreloader_KEY,
kLast_Fixed_KEY kLast_Fixed_KEY
}; };

29
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 "plgDispatch.h"
#include "plAgeLoader/plAgeLoader.h" #include "plAgeLoader/plAgeLoader.h"
#include "plFile/plEncryptedStream.h"
#include "plFile/plStreamSource.h"
#include "plFile/plSecureStream.h"
#include "plMessage/plResPatcherMsg.h" #include "plMessage/plResPatcherMsg.h"
#include "pfPatcher/pfPatcher.h" #include "pfPatcher/pfPatcher.h"
#include "plProgressMgr/plProgressMgr.h" #include "plProgressMgr/plProgressMgr.h"
#include "plResMgr/plResManager.h" #include "plResMgr/plResManager.h"
extern bool gDataServerLocal; 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) void plResPatcher::OnProgressTick(uint64_t dl, uint64_t total, const plString& msg)
{ {
if (dl && total) { if (dl && total) {
@ -121,6 +140,14 @@ pfPatcher* plResPatcher::CreatePatcher()
patcher->OnFileDownloadBegin(std::bind(&plResPatcher::OnFileDownloadBegin, this, std::placeholders::_1)); patcher->OnFileDownloadBegin(std::bind(&plResPatcher::OnFileDownloadBegin, this, std::placeholders::_1));
patcher->OnFileDownloaded(std::bind(&plResPatcher::OnFileDownloaded, 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)); 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; return patcher;
} }
@ -133,7 +160,7 @@ void plResPatcher::InitProgress()
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
plResPatcher::plResPatcher() plResPatcher::plResPatcher()
: fProgress(nullptr) { } : fProgress(nullptr), fRequestedGameCode(false) { }
plResPatcher::~plResPatcher() plResPatcher::~plResPatcher()
{ {

2
Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h

@ -58,12 +58,14 @@ class plResPatcher
{ {
plOperationProgress* fProgress; plOperationProgress* fProgress;
static plResPatcher* fInstance; static plResPatcher* fInstance;
bool fRequestedGameCode;
friend class plAgeLoader; friend class plAgeLoader;
void OnCompletion(ENetError, const plString& msg); void OnCompletion(ENetError, const plString& msg);
void OnFileDownloadBegin(const plFileName& file); void OnFileDownloadBegin(const plFileName& file);
void OnFileDownloaded(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); void OnProgressTick(uint64_t dl, uint64_t total, const plString& msg);
class pfPatcher* CreatePatcher(); class pfPatcher* CreatePatcher();

8
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 */) hsStream* plSecureStream::OpenSecureFile(const plFileName& fileName, const uint32_t flags /* = kRequireEncryption */, uint32_t* key /* = nil */)
{ {
bool requireEncryption = flags & kRequireEncryption; bool requireEncryption = flags & kRequireEncryption;
#ifndef PLASMA_EXTERNAL_RELEASE
requireEncryption = false;
#endif
bool deleteOnExit = flags & kDeleteOnExit; bool deleteOnExit = flags & kDeleteOnExit;
bool isEncrypted = IsSecureFile(fileName); bool isEncrypted = IsSecureFile(fileName);
hsStream* s = nil; hsStream* s = nullptr;
if (isEncrypted) if (isEncrypted)
s = new plSecureStream(deleteOnExit, key); 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; s = new hsUNIXStream;
if (s) if (s)

28
Sources/Plasma/PubUtilLib/plFile/plStreamSource.cpp

@ -40,7 +40,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
*==LICENSE==*/ *==LICENSE==*/
#include <string> #include "HeadSpin.h"
#include "plStreamSource.h" #include "plStreamSource.h"
#include "plSecureStream.h" #include "plSecureStream.h"
#include "plEncryptedStream.h" #include "plEncryptedStream.h"
@ -49,8 +49,15 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
# include <wctype.h> # include <wctype.h>
#endif #endif
plStreamSource::plStreamSource()
{
memset(fServerKey, 0, arrsize(fServerKey));
}
void plStreamSource::ICleanup() void plStreamSource::ICleanup()
{ {
hsTempMutexLock lock(fMutex);
// loop through all the file data records, and delete the streams // loop through all the file data records, and delete the streams
decltype(fFileData.begin()) curData; decltype(fFileData.begin()) curData;
for (curData = fFileData.begin(); curData != fFileData.end(); curData++) for (curData = fFileData.begin(); curData != fFileData.end(); curData++)
@ -65,6 +72,8 @@ void plStreamSource::ICleanup()
hsStream* plStreamSource::GetFile(const plFileName& filename) hsStream* plStreamSource::GetFile(const plFileName& filename)
{ {
hsTempMutexLock lock(fMutex);
plFileName sFilename = filename.Normalize('/'); plFileName sFilename = filename.Normalize('/');
if (fFileData.find(sFilename) == fFileData.end()) if (fFileData.find(sFilename) == fFileData.end())
{ {
@ -78,14 +87,15 @@ hsStream* plStreamSource::GetFile(const plFileName& filename)
fFileData[sFilename].fExt = sFilename.GetFileExt(); fFileData[sFilename].fExt = sFilename.GetFileExt();
if (plSecureStream::IsSecureFile(filename)) if (plSecureStream::IsSecureFile(filename))
{ {
uint32_t encryptionKey[4]; hsStream* ss = nullptr;
if (!plSecureStream::GetSecureEncryptionKey(filename, encryptionKey, 4))
{
FATAL("Hey camper... You need an NTD key file!");
return nil;
}
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 else // otherwise it is an encrypted or plain stream, this call handles both
fFileData[sFilename].fStream = plEncryptedStream::OpenEncryptedFile(filename); fFileData[sFilename].fStream = plEncryptedStream::OpenEncryptedFile(filename);
@ -102,6 +112,7 @@ std::vector<plFileName> plStreamSource::GetListOfNames(const plFileName& dir, co
{ {
plFileName sDir = dir.Normalize('/'); plFileName sDir = dir.Normalize('/');
hsAssert(ext.CharAt(0) != '.', "Don't add a dot"); hsAssert(ext.CharAt(0) != '.', "Don't add a dot");
hsTempMutexLock lock(fMutex);
// loop through all the file data records, and create the list // loop through all the file data records, and create the list
std::vector<plFileName> retVal; std::vector<plFileName> retVal;
@ -131,6 +142,7 @@ bool plStreamSource::InsertFile(const plFileName& filename, hsStream* stream)
{ {
plFileName sFilename = filename.Normalize('/'); plFileName sFilename = filename.Normalize('/');
hsTempMutexLock lock(fMutex);
if (fFileData.find(sFilename) != fFileData.end()) if (fFileData.find(sFilename) != fFileData.end())
return false; // duplicate entry, return failure return false; // duplicate entry, return failure

9
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 #define plStreamSource_h_inc
#include <map> #include <map>
#include <string>
#include "hsStream.h" #include "hsStream.h"
#include "hsThread.h"
// A class for holding and accessing file streams. The preloader will insert // 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 // 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 hsStream* fStream; // we own this pointer, so clean it up
}; };
std::map<plFileName, fileData, plFileName::less_i> fFileData; // key is filename std::map<plFileName, fileData, plFileName::less_i> fFileData; // key is filename
hsMutex fMutex;
uint32_t fServerKey[4];
void ICleanup(); // closes all file pointers and cleans up after itself void ICleanup(); // closes all file pointers and cleans up after itself
plStreamSource() {} plStreamSource();
public: public:
~plStreamSource() {ICleanup();} ~plStreamSource() {ICleanup();}
@ -77,6 +79,9 @@ public:
// For other classes to insert files (takes ownership of the stream if successful) // For other classes to insert files (takes ownership of the stream if successful)
bool InsertFile(const plFileName& filename, hsStream* stream); bool InsertFile(const plFileName& filename, hsStream* stream);
/** Gets a pointer to our encryption key */
uint32_t* GetEncryptionKey() { return fServerKey; }
// Instance handling // Instance handling
static plStreamSource* GetInstance(); static plStreamSource* GetInstance();
}; };

1
Sources/Plasma/PubUtilLib/plMessage/CMakeLists.txt

@ -98,7 +98,6 @@ set(plMessage_HEADERS
plOneShotMsg.h plOneShotMsg.h
plParticleUpdateMsg.h plParticleUpdateMsg.h
plPickedMsg.h plPickedMsg.h
plPreloaderMsg.h
plRenderMsg.h plRenderMsg.h
plRenderRequestMsg.h plRenderRequestMsg.h
plReplaceGeometryMsg.h plReplaceGeometryMsg.h

3
Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h

@ -319,9 +319,6 @@ REGISTER_CREATABLE(plNetCommPublicAgeListMsg);
REGISTER_CREATABLE(plNetCommPublicAgeMsg); REGISTER_CREATABLE(plNetCommPublicAgeMsg);
REGISTER_CREATABLE(plNetCommRegisterAgeMsg); REGISTER_CREATABLE(plNetCommRegisterAgeMsg);
#include "plPreloaderMsg.h"
REGISTER_CREATABLE(plPreloaderMsg);
#include "plNetClientMgrMsg.h" #include "plNetClientMgrMsg.h"
REGISTER_CREATABLE(plNetClientMgrMsg); REGISTER_CREATABLE(plNetClientMgrMsg);

67
Sources/Plasma/PubUtilLib/plMessage/plPreloaderMsg.h

@ -1,67 +0,0 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/*****************************************************************************
*
* $/Plasma20/Sources/Plasma/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

4
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 "plVault/plVault.h"
#include "plMessage/plAccountUpdateMsg.h" #include "plMessage/plAccountUpdateMsg.h"
#include "plNetClient/plNetClientMgr.h" #include "plNetClient/plNetClientMgr.h"
#include "plFile/plStreamSource.h"
#include "pfMessage/pfKIMsg.h" #include "pfMessage/pfKIMsg.h"
@ -414,6 +415,9 @@ static void INetCliAuthLoginRequestCallback (
if (!wantsStartUpAge && 0 == StrCmpI(s_players[i].playerName, s_iniStartupPlayerName, (unsigned)-1)) if (!wantsStartUpAge && 0 == StrCmpI(s_players[i].playerName, s_iniStartupPlayerName, (unsigned)-1))
s_player = &s_players[i]; s_player = &s_players[i];
} }
// store this server's encryption key for posterity
NetCliAuthGetEncryptionKey(plStreamSource::GetInstance()->GetEncryptionKey(), 4);
} }
else else
s_account.accountUuid = kNilUuid; s_account.accountUuid = kNilUuid;

1
Sources/Tools/MaxMain/CMakeLists.txt

@ -124,7 +124,6 @@ target_link_libraries(MaxMain pfJournalBook)
target_link_libraries(MaxMain pfLocalizationMgr) target_link_libraries(MaxMain pfLocalizationMgr)
target_link_libraries(MaxMain pfMessage) target_link_libraries(MaxMain pfMessage)
target_link_libraries(MaxMain pfPython) target_link_libraries(MaxMain pfPython)
target_link_libraries(MaxMain pfSecurePreloader)
target_link_libraries(MaxMain pfSurface) target_link_libraries(MaxMain pfSurface)
target_link_libraries(MaxMain plAgeDescription) target_link_libraries(MaxMain plAgeDescription)
target_link_libraries(MaxMain plAgeLoader) target_link_libraries(MaxMain plAgeLoader)

1
Sources/Tools/MaxPlasmaLights/CMakeLists.txt

@ -57,7 +57,6 @@ target_link_libraries(MaxPlasmaLights pfJournalBook)
target_link_libraries(MaxPlasmaLights pfLocalizationMgr) target_link_libraries(MaxPlasmaLights pfLocalizationMgr)
target_link_libraries(MaxPlasmaLights pfMessage) target_link_libraries(MaxPlasmaLights pfMessage)
target_link_libraries(MaxPlasmaLights pfPython) target_link_libraries(MaxPlasmaLights pfPython)
target_link_libraries(MaxPlasmaLights pfSecurePreloader)
target_link_libraries(MaxPlasmaLights pfSurface) target_link_libraries(MaxPlasmaLights pfSurface)
target_link_libraries(MaxPlasmaLights plAgeDescription) target_link_libraries(MaxPlasmaLights plAgeDescription)
target_link_libraries(MaxPlasmaLights plAgeLoader) target_link_libraries(MaxPlasmaLights plAgeLoader)

Loading…
Cancel
Save