diff --git a/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp b/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp index 5eb775f5..fd3771ec 100644 --- a/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp +++ b/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp @@ -161,6 +161,11 @@ plClientLauncher::~plClientLauncher() { } plString plClientLauncher::GetAppArgs() const { + // If -Repair was specified, there are no args for the next call... + if (hsCheckBits(fFlags, kRepairGame)) { + return ""; + } + plStringStream ss; ss << "-ServerIni="; ss << fServerIni.AsString(); @@ -190,16 +195,33 @@ void plClientLauncher::IOnPatchComplete(ENetError result, const plString& msg) s_errorProc(result, msg); } +bool plClientLauncher::IApproveDownload(const plFileName& file) +{ + // So, for a repair, what we want to do is quite simple. + // That is: download everything that is NOT in the root directory. + plFileName path = file.StripFileName(); + return !path.AsString().IsEmpty(); +} + void plClientLauncher::PatchClient() { - if (fStatusFunc) - fStatusFunc("Checking for updates..."); + if (fStatusFunc) { + if (hsCheckBits(fFlags, kGameDataOnly)) + fStatusFunc("Verifying game data..."); + else + fStatusFunc("Checking for updates..."); + } hsAssert(fPatcherFactory, "why is the patcher factory nil?"); pfPatcher* patcher = fPatcherFactory(); patcher->OnCompletion(std::bind(&plClientLauncher::IOnPatchComplete, this, std::placeholders::_1, std::placeholders::_2)); patcher->OnSelfPatch([&](const plFileName& file) { fClientExecutable = file; }); + // If this is a repair, we need to approve the downloads... + if (hsCheckBits(fFlags, kGameDataOnly)) + patcher->OnFileDownloadDesired(std::bind(&plClientLauncher::IApproveDownload, this, std::placeholders::_1)); + + // Let's get 'er done. if (hsCheckBits(fFlags, kHaveSelfPatched)) { if (hsCheckBits(fFlags, kClientImage)) patcher->RequestManifest(plManifest::ClientImageManifest()); @@ -336,11 +358,12 @@ void plClientLauncher::ParseArguments() if (cmdParser.GetBool(arg)) \ fFlags |= flag; - enum { kArgServerIni, kArgNoSelfPatch, kArgImage }; + enum { kArgServerIni, kArgNoSelfPatch, kArgImage, kArgRepairGame }; const CmdArgDef cmdLineArgs[] = { { kCmdArgFlagged | kCmdTypeString, L"ServerIni", kArgServerIni }, { kCmdArgFlagged | kCmdTypeBool, L"NoSelfPatch", kArgNoSelfPatch }, { kCmdArgFlagged | kCmdTypeBool, L"Image", kArgImage }, + { kCmdArgFlagged | kCmdTypeBool, L"Repair", kArgRepairGame }, }; CCmdParser cmdParser(cmdLineArgs, arrsize(cmdLineArgs)); @@ -351,6 +374,11 @@ void plClientLauncher::ParseArguments() fServerIni = plString::FromWchar(cmdParser.GetString(kArgServerIni)); APPLY_FLAG(kArgNoSelfPatch, kHaveSelfPatched); APPLY_FLAG(kArgImage, kClientImage); + APPLY_FLAG(kArgRepairGame, kRepairGame); + + // last chance setup + if (hsCheckBits(fFlags, kRepairGame)) + fClientExecutable = plManifest::PatcherExecutable(); #undef APPLY_FLAG } diff --git a/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.h b/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.h index ab97000d..464d3082 100644 --- a/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.h +++ b/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.h @@ -62,6 +62,9 @@ private: { kHaveSelfPatched = 1<<0, kClientImage = 1<<1, + kGameDataOnly = (1<<2), + + kRepairGame = kHaveSelfPatched | kClientImage | kGameDataOnly, }; uint32_t fFlags; @@ -77,6 +80,7 @@ private: plString GetAppArgs() const; void IOnPatchComplete(ENetError result, const plString& msg); + bool IApproveDownload(const plFileName& file); public: plClientLauncher(); diff --git a/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp b/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp index 07dff2f5..75b9615e 100644 --- a/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp +++ b/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp @@ -104,6 +104,7 @@ struct pfPatcherWorker : public hsThread pfPatcher::CompletionFunc fOnComplete; pfPatcher::FileDownloadFunc fFileBeginDownload; + pfPatcher::FileDesiredFunc fFileDownloadDesired; pfPatcher::FileDownloadFunc fFileDownloaded; pfPatcher::GameCodeDiscoverFunc fGameCodeDiscovered; pfPatcher::ProgressTickFunc fProgressTick; @@ -482,7 +483,16 @@ void pfPatcherWorker::ProcessFile() } } - // If you got here, they're different. + // It's different... but do we want it? + if (fFileDownloadDesired) { + if (!fFileDownloadDesired(clName)) { + PatcherLogRed("\tDeclined '%S'", entry.clientName); + fQueuedFiles.pop_front(); + continue; + } + } + + // If you got here, they're different and we want it. PatcherLogYellow("\tEnqueuing '%S'", entry.clientName); plFileSystem::CreateDir(plFileName(clName).StripFileName()); @@ -564,6 +574,11 @@ void pfPatcher::OnFileDownloadBegin(FileDownloadFunc cb) fWorker->fFileBeginDownload = cb; } +void pfPatcher::OnFileDownloadDesired(FileDesiredFunc cb) +{ + fWorker->fFileDownloadDesired = cb; +} + void pfPatcher::OnFileDownloaded(FileDownloadFunc cb) { fWorker->fFileDownloaded = cb; diff --git a/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.h b/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.h index dee1b77b..3147cf6e 100644 --- a/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.h +++ b/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.h @@ -70,6 +70,9 @@ 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&) and approves it. */ + typedef std::function FileDesiredFunc; + /** Represents a function that takes (const plFileName&) on an interesting file operation. */ typedef std::function FileDownloadFunc; @@ -95,6 +98,12 @@ public: */ void OnFileDownloadBegin(FileDownloadFunc cb); + /** Set a callback that will be fired when the patcher wants to download a file. You are + * given the ability to approve or veto the download. With great power comes great responsibility... + * \remarks This will be called from the patcher thread. + */ + void OnFileDownloadDesired(FileDesiredFunc 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. */