/***************************************************************************** * Copyright (c) 2014-2023 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 * * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ #include "AssetPack.h" #include "Context.h" #include "core/Json.hpp" #include "core/Path.hpp" #include "core/Zip.h" #include "drawing/Image.h" #include "localisation/LocalisationService.h" #include "object/Object.h" #include using namespace OpenRCT2; constexpr std::string_view ManifestFileName = "manifest.json"; AssetPack::AssetPack(const fs::path& path) : Path(path) { } AssetPack::~AssetPack() { } bool AssetPack::IsEnabled() const { return _enabled; } void AssetPack::SetEnabled(bool value) { _enabled = value; } void AssetPack::Fetch() { auto archive = Zip::Open(Path.u8string(), ZIP_ACCESS::READ); if (!archive->Exists(ManifestFileName)) { throw std::runtime_error("Manifest does not exist."); } auto manifestJson = archive->GetFileData(ManifestFileName); auto jManifest = Json::FromVector(manifestJson); Id = jManifest["id"].get(); Version = jManifest["version"].get(); // TODO use a better string table class that can be used for objects, park files and asset packs auto& localisationService = GetContext()->GetLocalisationService(); auto locale = std::string(localisationService.GetCurrentLanguageLocale()); Name = GetString(jManifest, "name", locale); Description = GetString(jManifest, "description", locale); } std::string AssetPack::GetString(json_t& jManifest, const std::string& key, const std::string& locale) { if (jManifest.contains("strings")) { auto& jStrings = jManifest["strings"]; if (jStrings.contains(key)) { auto& jKey = jStrings[key]; if (jKey.contains(locale)) { return jKey[locale].get(); } if (jKey.contains("en-GB")) { return jKey["en-GB"].get(); } if (jKey.contains("en-US")) { return jKey["en-US"].get(); } } } return {}; } bool AssetPack::ContainsObject(std::string_view id) const { auto it = std::find_if(_entries.begin(), _entries.end(), [id](const Entry& entry) { return entry.ObjectId == id; }); return it != _entries.end(); } void AssetPack::LoadSamplesForObject(std::string_view id, AudioSampleTable& objectTable) { auto it = std::find_if(_entries.begin(), _entries.end(), [id](const Entry& entry) { return entry.ObjectId == id; }); if (it != _entries.end()) { objectTable.LoadFrom(_sampleTable, it->TableIndex, it->TableLength); } } class AssetPackLoadContext : public IReadObjectContext { private: std::string _zipPath; IZipArchive* _zipArchive; public: AssetPackLoadContext(std::string_view zipPath, IZipArchive* zipArchive) : _zipPath(zipPath) , _zipArchive(zipArchive) { } virtual ~AssetPackLoadContext() override { } std::string_view GetObjectIdentifier() override { throw std::runtime_error("Not implemented"); } IObjectRepository& GetObjectRepository() override { throw std::runtime_error("Not implemented"); } bool ShouldLoadImages() override { return true; } std::vector GetData(std::string_view path) override { return _zipArchive->GetFileData(path); } ObjectAsset GetAsset(std::string_view path) override { if (Path::IsAbsolute(path)) return ObjectAsset(path); return ObjectAsset(_zipPath, path); } void LogVerbose(ObjectError code, const utf8* text) override { } void LogWarning(ObjectError code, const utf8* text) override { } void LogError(ObjectError code, const utf8* text) override { } }; void AssetPack::Load() { auto path = Path.u8string(); auto archive = Zip::Open(path, ZIP_ACCESS::READ); if (!archive->Exists(ManifestFileName)) { throw std::runtime_error("Manifest does not exist."); } AssetPackLoadContext loadContext(path, archive.get()); auto manifestJson = archive->GetFileData(ManifestFileName); auto jManifest = Json::FromVector(manifestJson); auto& jObjects = jManifest["objects"]; _entries.clear(); for (auto& jObject : jObjects) { Entry entry; entry.ObjectId = jObject["id"].get(); if (jObject.contains("samples")) { entry.TableIndex = _sampleTable.GetCount(); _sampleTable.ReadFromJson(&loadContext, jObject); entry.TableLength = _sampleTable.GetCount() - entry.TableIndex; } _entries.push_back(entry); } }