Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

57
backends/dlc/dlcdesc.h Normal file
View File

@@ -0,0 +1,57 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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/>.
*
*/
#ifndef BACKENDS_DLC_DLCDESC_H
#define BACKENDS_DLC_DLCDESC_H
#include "common/str.h"
namespace DLC {
struct DLCDesc {
public:
enum State {
kAvailable,
kInProgress,
kDownloaded,
kCancelled,
kErrorDownloading
};
Common::String name;
Common::String id;
Common::String url;
Common::String platform;
Common::String gameid;
Common::String description;
Common::String language;
Common::String extra;
Common::String engineid;
Common::String guioptions;
uint32 size = 0;
uint32 idx = 0;
State state = State::kAvailable;
};
} // End of namespace DLC
#endif

131
backends/dlc/dlcmanager.cpp Normal file
View File

@@ -0,0 +1,131 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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/>.
*
*/
#include "common/system.h"
#include "backends/dlc/dlcmanager.h"
#include "backends/dlc/dlcdesc.h"
#include "backends/dlc/scummvmcloud.h"
#include "gui/dlcsdialog.h"
#include "gui/launcher.h"
namespace Common {
DECLARE_SINGLETON(DLC::DLCManager);
}
namespace DLC {
DLCManager::DLCManager() : CommandSender(nullptr) {
_store = g_system->getDLCStore();
}
DLCManager::~DLCManager() {
for (uint32 i = 0; i < _dlcs.size(); ++i) {
delete _dlcs[i];
}
}
void DLCManager::init() {}
void DLCManager::getAllDLCs() {
_store->getAllDLCs();
}
void DLCManager::refreshDLCList() {
sendCommand(GUI::kRefreshDLCList, 0);
}
void DLCManager::refreshLauncherGameList() {
if (_launcher) {
_launcher->rebuild();
}
}
void DLCManager::setLauncher(GUI::LauncherDialog *launcher) {
_launcher = launcher;
}
void DLCManager::addDownload(uint32 idx) {
if (_dlcs[idx]->state == DLCDesc::kInProgress) {
// if DLC is already in queue, don't add again
return;
}
_dlcs[idx]->state = DLCDesc::kInProgress;
_queuedDownloadTasks.push(_dlcs[idx]);
_dlcsInProgress.push_back(_dlcs[idx]);
if (!_isDLCDownloading) {
// if queue is not already processing
DLCManager::processDownloadQueue();
}
}
void DLCManager::processDownloadQueue() {
_currentDownloadedSize = 0;
_isDLCDownloading = true;
if (!_queuedDownloadTasks.empty()) {
if (_queuedDownloadTasks.front()->state == DLCDesc::kInProgress) {
_currentDownloadingDLC = _queuedDownloadTasks.front()->id;
DLCManager::startDownloadAsync(_queuedDownloadTasks.front()->id, _queuedDownloadTasks.front()->url);
} else {
// state is already cancelled/downloaded -> skip download
_queuedDownloadTasks.pop();
_dlcsInProgress.remove_at(0);
// process next download in the queue
processDownloadQueue();
}
} else {
// no more download in queue
_isDLCDownloading = false;
_currentDownloadingDLC = "";
}
}
void DLCManager::startDownloadAsync(const Common::String &id, const Common::String &url) {
_store->startDownloadAsync(id, url);
}
bool DLCManager::cancelDownload(uint32 idx) {
if (_queuedDownloadTasks.front()->idx == idx) {
// if already downloading, interrupt startDownloadAsync
_interruptCurrentDownload = true;
} else {
// if not started, skip it in processDownload()
_dlcs[idx]->state = DLCDesc::kCancelled;
DLCMan.refreshDLCList();
}
return true;
}
Common::String DLCManager::getCurrentDownloadingDLC() const {
return _currentDownloadingDLC;
}
uint DLCManager::getDLCIdxFromId(const Common::String &id) const {
for (uint32 i = 0; i < _dlcs.size(); ++i) {
if (_dlcs[i]->id == id) return i;
}
return -1;
}
} // End of namespace DLC

95
backends/dlc/dlcmanager.h Normal file
View File

@@ -0,0 +1,95 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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/>.
*
*/
#ifndef BACKENDS_DLC_DLCMANAGER_H
#define BACKENDS_DLC_DLCMANAGER_H
#include "common/str.h"
#include "common/str-array.h"
#include "common/queue.h"
#include "common/singleton.h"
#include "gui/object.h"
#include "backends/networking/http/request.h"
namespace GUI {
class LauncherDialog;
}
namespace DLC {
class Store;
struct DLCDesc;
}
namespace DLC {
class DLCManager : public Common::Singleton<DLCManager>, public GUI::CommandSender {
Store *_store;
GUI::LauncherDialog *_launcher;
bool _isDLCDownloading = false;
Common::String _currentDownloadingDLC;
public:
bool _fetchDLCs = false;
bool _interruptCurrentDownload = false;
uint32 _currentDownloadedSize;
Common::String _errorText;
Common::Array<DLCDesc*> _dlcs;
Common::Array<DLCDesc*> _dlcsInProgress;
Common::Queue<DLCDesc*> _queuedDownloadTasks;
DLCManager();
virtual ~DLCManager();
void init();
// Runs only once
void getAllDLCs();
void refreshDLCList();
void refreshLauncherGameList();
void setLauncher(GUI::LauncherDialog *launcher);
// Add download task to queue, runs on click download button,
void addDownload(uint32 idx);
bool cancelDownload(uint32 idx);
void processDownloadQueue();
Common::String getCurrentDownloadingDLC() const;
uint getDLCIdxFromId(const Common::String &id) const;
void startDownloadAsync(const Common::String &id, const Common::String &url);
};
#define DLCMan DLC::DLCManager::instance()
} // End of namespace DLC
#endif

View File

@@ -0,0 +1,232 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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/>.
*
*/
#if defined(POSIX)
#define FORBIDDEN_SYMBOL_EXCEPTION_unlink
#include <unistd.h>
#endif
#include "common/archive.h"
#include "common/compression/unzip.h"
#include "common/file.h"
#include "common/punycode.h"
#include "common/config-manager.h"
#include "common/formats/json.h"
#include "gui/gui-manager.h"
#include "backends/networking/http/sessionrequest.h"
#include "backends/dlc/scummvmcloud.h"
#include "backends/dlc/dlcmanager.h"
#include "backends/dlc/dlcdesc.h"
#include "engines/metaengine.h"
namespace DLC {
namespace ScummVMCloud {
void ScummVMCloud::jsonCallbackGetAllDLCs(const Networking::JsonResponse &response) {
const Common::JSONValue *json = response.value;
if (json == nullptr || !json->isObject()) {
return;
}
debug(1, "DLC list JSON response: %s", json->stringify(true).c_str());
Common::JSONObject result = json->asObject();
if (result.contains("entries")) {
Common::JSONArray items = result.getVal("entries")->asArray();
for (uint32 i = 0; i < items.size(); ++i) {
if (!Networking::HttpJsonRequest::jsonIsObject(items[i], "ScummVMCloud")) continue;
Common::JSONObject item = items[i]->asObject();
DLC::DLCDesc *dlc = new DLC::DLCDesc();
dlc->id = item.getVal("id")->asString();
dlc->name = item.getVal("name")->asString();
dlc->url = item.getVal("url")->asString();
dlc->platform = item.getVal("platform")->asString();
dlc->gameid = item.getVal("gameid")->asString();
dlc->description = item.getVal("description")->asString();
dlc->language = item.getVal("language")->asString();
dlc->extra = item.getVal("extra")->asString();
dlc->engineid = item.getVal("engineid")->asString();
dlc->guioptions = item.getVal("guioptions")->asString();
if (item.getVal("size")->isString()) {
dlc->size = item.getVal("size")->asString().asUint64();
} else {
dlc->size = item.getVal("size")->asIntegerNumber();
}
dlc->idx = i;
DLCMan._dlcs.push_back(dlc);
}
}
// send refresh DLC list command to GUI
DLCMan.refreshDLCList();
}
void ScummVMCloud::errorCallbackGetAllDLCs(const Networking::ErrorResponse &error) {
warning("JsonRequest Error - getAllDLCs");
}
void ScummVMCloud::getAllDLCs() {
Common::String url("https://scummvm-dlcs-default-rtdb.firebaseio.com/dlcs.json"); // temporary mock api
Networking::JsonCallback callback = new Common::Callback<ScummVMCloud, const Networking::JsonResponse &>(this, &ScummVMCloud::jsonCallbackGetAllDLCs);
Networking::ErrorCallback failureCallback = new Common::Callback<ScummVMCloud, const Networking::ErrorResponse &>(this, &ScummVMCloud::errorCallbackGetAllDLCs);
Networking::HttpJsonRequest *request = new Networking::HttpJsonRequest(
callback, failureCallback, url);
request->execute();
}
void ScummVMCloud::downloadFileCallback(const Networking::DataResponse &r) {
Networking::SessionFileResponse *response = static_cast<Networking::SessionFileResponse *>(r.value);
DLCMan._currentDownloadedSize += response->len;
if (DLCMan._interruptCurrentDownload) {
_rq->close();
DLCMan._interruptCurrentDownload = false;
// delete the download cache (the incomplete .zip)
Common::Path relativeFilePath = Common::Path(DLCMan._queuedDownloadTasks.front()->id);
removeCacheFile(relativeFilePath);
DLCMan._queuedDownloadTasks.front()->state = DLCDesc::kCancelled;
DLCMan.refreshDLCList();
// handle next download
DLCMan._queuedDownloadTasks.pop();
DLCMan._dlcsInProgress.remove_at(0);
DLCMan.processDownloadQueue();
}
if (response->eos) {
DLC::DLCDesc *dlc = DLCMan._queuedDownloadTasks.front();
debug(1, "Downloaded: %s", dlc->name.c_str());
_rq->close(); // delete request
Common::Path relativeFilePath = Common::Path(dlc->id);
// extract the downloaded zip
Common::String gameDir = Common::punycode_encodefilename(dlc->name);
Common::Path destPath(ConfMan.getPath("dlcspath").appendComponent(gameDir));
Common::Error error = extractZip(relativeFilePath, destPath);
// remove cache (the downloaded .zip)
removeCacheFile(relativeFilePath);
if (error.getCode() == Common::kNoError) {
// add downloaded game entry in scummvm configuration file
addEntryToConfig(destPath);
dlc->state = DLCDesc::kDownloaded;
DLCMan._errorText = "";
} else {
// if there is any error in extraction
dlc->state = DLCDesc::kErrorDownloading;
DLCMan._errorText = error.getDesc();
}
DLCMan.refreshDLCList();
// handle next download
DLCMan._queuedDownloadTasks.pop();
DLCMan._dlcsInProgress.remove_at(0);
DLCMan.processDownloadQueue();
}
}
void ScummVMCloud::errorCallback(const Networking::ErrorResponse &error) {
// error downloading - start next download in queue
DLCMan._queuedDownloadTasks.front()->state = DLCDesc::kErrorDownloading;
DLCMan._queuedDownloadTasks.pop();
DLCMan._dlcsInProgress.remove_at(0);
DLCMan.processDownloadQueue();
}
void ScummVMCloud::startDownloadAsync(const Common::String &id, const Common::String &url) {
Common::Path localFile(ConfMan.getPath("dlcspath").appendComponent(id));
Networking::DataCallback callback = new Common::Callback<ScummVMCloud, const Networking::DataResponse &>(this, &ScummVMCloud::downloadFileCallback);
Networking::ErrorCallback failureCallback = new Common::Callback<ScummVMCloud, const Networking::ErrorResponse &>(this, &ScummVMCloud::errorCallback);
_rq = new Networking::SessionRequest(url, localFile, callback, failureCallback);
_rq->start();
}
Common::Error ScummVMCloud::extractZip(const Common::Path &file, const Common::Path &destPath) {
Common::Archive *dataArchive = nullptr;
Common::Path dlcPath(ConfMan.getPath("dlcspath"));
Common::FSNode *fs = new Common::FSNode(dlcPath.join(file));
Common::Error error = Common::kNoError;
if (fs->exists()) {
dataArchive = Common::makeZipArchive(*fs);
// dataArchive is nullptr if zip file is incomplete
if (dataArchive != nullptr) {
error = dataArchive->dumpArchive(destPath);
} else {
error = Common::Error(Common::kCreatingFileFailed, DLCMan._queuedDownloadTasks.front()->name + "Archive is broken, please re-download");
}
}
delete fs;
delete dataArchive;
return error;
}
void ScummVMCloud::removeCacheFile(const Common::Path &file) {
Common::Path dlcPath(ConfMan.getPath("dlcspath"));
Common::Path fileToDelete = dlcPath.join(file);
#if defined(POSIX)
unlink(fileToDelete.toString(Common::Path::kNativeSeparator).c_str());
#elif defined(WIN32)
_unlink(fileToDelete.toString(Common::Path::kNativeSeparator).c_str());
#else
warning("ScummVMCloud::removeCacheFile(): Removing is unimplemented");
#endif
}
void ScummVMCloud::addEntryToConfig(Common::Path gamePath) {
Common::FSNode dir(gamePath);
Common::FSList fsnodes;
if (!dir.getChildren(fsnodes, Common::FSNode::kListAll)) {
warning("ScummVMCloud::addEntryToConfig(): Game directory does not exists");
return;
}
if (fsnodes.size() == 1 && fsnodes[0].isDirectory()) {
// if extraction process created a new folder inside gamePath, set gamePath to that directory
gamePath = gamePath.appendComponent(fsnodes[0].getFileName());
}
// add a new entry in scummvm config file
DLC::DLCDesc *dlc = DLCMan._queuedDownloadTasks.front();
Common::String domain = EngineMan.generateUniqueDomain(dlc->gameid);
ConfMan.addGameDomain(domain);
ConfMan.set("engineid", dlc->engineid, domain);
ConfMan.set("gameid", dlc->gameid, domain);
ConfMan.set("description", dlc->description, domain);
ConfMan.set("language", dlc->language, domain);
ConfMan.set("platform", dlc->platform, domain);
ConfMan.set("path", gamePath.toString(), domain);
ConfMan.set("extra", dlc->extra, domain);
ConfMan.set("guioptions", dlc->guioptions, domain);
ConfMan.set("download", dlc->id, domain);
// send refresh launcher command to GUI
DLCMan.refreshLauncherGameList();
}
} // End of namespace ScummVMCloud
} // End of namespace DLC

View File

@@ -0,0 +1,71 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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/>.
*
*/
#ifndef BACKENDS_DLC_SCUMMVMCLOUD_H
#define BACKENDS_DLC_SCUMMVMCLOUD_H
#include "common/error.h"
#include "common/queue.h"
#include "backends/dlc/store.h"
#include "backends/networking/http/request.h"
#include "backends/networking/http/httpjsonrequest.h"
namespace Networking {
class SessionRequest;
}
namespace DLC {
namespace ScummVMCloud {
class ScummVMCloud : public DLC::Store {
Networking::SessionRequest *_rq;
public:
ScummVMCloud() {}
virtual ~ScummVMCloud() {}
virtual void getAllDLCs() override;
virtual void startDownloadAsync(const Common::String &id, const Common::String &url) override;
virtual void removeCacheFile(const Common::Path &file) override;
// extracts the provided zip in the provided destination path
Common::Error extractZip(const Common::Path &file, const Common::Path &destPath);
void addEntryToConfig(Common::Path gamePath);
// callback functions
void jsonCallbackGetAllDLCs(const Networking::JsonResponse &response);
void errorCallbackGetAllDLCs(const Networking::ErrorResponse &error);
void downloadFileCallback(const Networking::DataResponse &response);
void errorCallback(const Networking::ErrorResponse &error);
};
} // End of namespace ScummVMCloud
} // End of namespace DLC
#endif

48
backends/dlc/store.h Normal file
View File

@@ -0,0 +1,48 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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/>.
*
*/
#ifndef BACKENDS_DLC_STORE_H
#define BACKENDS_DLC_STORE_H
#include "common/str.h"
#include "common/path.h"
#include "backends/dlc/dlcdesc.h"
namespace DLC {
class Store {
public:
Store() {}
virtual ~Store() {}
virtual void getAllDLCs() = 0;
virtual void startDownloadAsync(const Common::String &id, const Common::String &url) = 0;
virtual void removeCacheFile(const Common::Path &file) = 0;
};
} // End of namespace DLC
#endif