mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-24 14:49:42 -06:00
Implement resource packs
This commit is contained in:
@ -5,6 +5,9 @@ add_library(uicommon
|
||||
DiscordPresence.cpp
|
||||
GameFile.cpp
|
||||
GameFileCache.cpp
|
||||
ResourcePack/Manager.cpp
|
||||
ResourcePack/Manifest.cpp
|
||||
ResourcePack/ResourcePack.cpp
|
||||
UICommon.cpp
|
||||
USBUtils.cpp
|
||||
VideoUtils.cpp
|
||||
@ -14,6 +17,7 @@ target_link_libraries(uicommon
|
||||
PUBLIC
|
||||
common
|
||||
cpp-optparse
|
||||
minizip
|
||||
|
||||
PRIVATE
|
||||
$<$<BOOL:APPLE>:${IOK_LIBRARY}>
|
||||
|
185
Source/Core/UICommon/ResourcePack/Manager.cpp
Normal file
185
Source/Core/UICommon/ResourcePack/Manager.cpp
Normal file
@ -0,0 +1,185 @@
|
||||
// Copyright 2018 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "UICommon/ResourcePack/Manager.h"
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileSearch.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/IniFile.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace
|
||||
{
|
||||
std::vector<ResourcePack::ResourcePack> packs;
|
||||
|
||||
std::string packs_path;
|
||||
} // namespace
|
||||
|
||||
namespace ResourcePack
|
||||
{
|
||||
IniFile GetPackConfig()
|
||||
{
|
||||
packs_path = File::GetUserPath(D_RESOURCEPACK_IDX) + "/Packs.ini";
|
||||
|
||||
IniFile file;
|
||||
file.Load(packs_path);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
bool Init()
|
||||
{
|
||||
packs.clear();
|
||||
auto pack_list = Common::DoFileSearch({File::GetUserPath(D_RESOURCEPACK_IDX)}, {".zip"});
|
||||
|
||||
bool error = false;
|
||||
|
||||
IniFile file = GetPackConfig();
|
||||
|
||||
auto* order = file.GetOrCreateSection("Order");
|
||||
|
||||
std::sort(pack_list.begin(), pack_list.end(), [order](std::string& a, std::string& b) {
|
||||
std::string order_a = a, order_b = b;
|
||||
|
||||
order->Get(ResourcePack(a).GetManifest()->GetID(), &order_a);
|
||||
order->Get(ResourcePack(b).GetManifest()->GetID(), &order_b);
|
||||
|
||||
return order_a < order_b;
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < pack_list.size(); i++)
|
||||
{
|
||||
const auto& path = pack_list[i];
|
||||
|
||||
error |= !Add(path);
|
||||
|
||||
order->Set(packs[i].GetManifest()->GetID(), static_cast<u64>(i));
|
||||
}
|
||||
|
||||
file.Save(packs_path);
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
std::vector<ResourcePack>& GetPacks()
|
||||
{
|
||||
return packs;
|
||||
}
|
||||
|
||||
std::vector<ResourcePack*> GetLowerPriorityPacks(ResourcePack& pack)
|
||||
{
|
||||
std::vector<ResourcePack*> list;
|
||||
for (auto it = std::find(packs.begin(), packs.end(), pack) + 1; it != packs.end(); it++)
|
||||
{
|
||||
auto& entry = *it;
|
||||
if (!IsInstalled(pack))
|
||||
continue;
|
||||
|
||||
list.push_back(&entry);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
std::vector<ResourcePack*> GetHigherPriorityPacks(ResourcePack& pack)
|
||||
{
|
||||
std::vector<ResourcePack*> list;
|
||||
auto end = std::find(packs.begin(), packs.end(), pack);
|
||||
|
||||
for (auto it = packs.begin(); it != end; it++)
|
||||
{
|
||||
auto& entry = *it;
|
||||
if (!IsInstalled(entry))
|
||||
continue;
|
||||
list.push_back(&entry);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
bool Add(const std::string& path, int offset)
|
||||
{
|
||||
if (offset == -1)
|
||||
offset = static_cast<int>(packs.size());
|
||||
|
||||
ResourcePack pack(path);
|
||||
|
||||
IniFile file = GetPackConfig();
|
||||
|
||||
auto* order = file.GetOrCreateSection("Order");
|
||||
|
||||
order->Set(pack.GetManifest()->GetID(), offset);
|
||||
|
||||
for (int i = offset; i < static_cast<int>(packs.size()); i++)
|
||||
order->Set(packs[i].GetManifest()->GetID(), i + 1);
|
||||
|
||||
file.Save(packs_path);
|
||||
|
||||
packs.insert(packs.begin() + offset, std::move(pack));
|
||||
|
||||
return pack.IsValid();
|
||||
}
|
||||
|
||||
bool Remove(ResourcePack& pack)
|
||||
{
|
||||
const auto result = pack.Uninstall(File::GetUserPath(D_USER_IDX));
|
||||
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
auto pack_iterator = std::find(packs.begin(), packs.end(), pack);
|
||||
|
||||
if (pack_iterator == packs.end())
|
||||
return false;
|
||||
|
||||
std::string filename;
|
||||
|
||||
IniFile file = GetPackConfig();
|
||||
|
||||
auto* order = file.GetOrCreateSection("Order");
|
||||
|
||||
order->Delete(pack.GetManifest()->GetID());
|
||||
|
||||
int offset = pack_iterator - packs.begin();
|
||||
|
||||
for (int i = offset + 1; i < static_cast<int>(packs.size()); i++)
|
||||
order->Set(packs[i].GetManifest()->GetID(), i - 1);
|
||||
|
||||
file.Save(packs_path);
|
||||
|
||||
packs.erase(pack_iterator);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetInstalled(const ResourcePack& pack, bool installed)
|
||||
{
|
||||
IniFile file = GetPackConfig();
|
||||
|
||||
auto* install = file.GetOrCreateSection("Installed");
|
||||
|
||||
if (installed)
|
||||
install->Set(pack.GetManifest()->GetID(), installed);
|
||||
else
|
||||
install->Delete(pack.GetManifest()->GetID());
|
||||
|
||||
file.Save(packs_path);
|
||||
}
|
||||
|
||||
bool IsInstalled(const ResourcePack& pack)
|
||||
{
|
||||
IniFile file = GetPackConfig();
|
||||
|
||||
auto* install = file.GetOrCreateSection("Installed");
|
||||
|
||||
bool installed;
|
||||
|
||||
install->Get(pack.GetManifest()->GetID(), &installed, false);
|
||||
|
||||
return installed;
|
||||
}
|
||||
|
||||
} // namespace ResourcePack
|
25
Source/Core/UICommon/ResourcePack/Manager.h
Normal file
25
Source/Core/UICommon/ResourcePack/Manager.h
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2018 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "UICommon/ResourcePack/ResourcePack.h"
|
||||
|
||||
namespace ResourcePack
|
||||
{
|
||||
bool Init();
|
||||
|
||||
bool Add(const std::string& path, int offset = -1);
|
||||
bool Remove(ResourcePack& pack);
|
||||
void SetInstalled(const ResourcePack& pack, bool installed);
|
||||
bool IsInstalled(const ResourcePack& pack);
|
||||
|
||||
std::vector<ResourcePack>& GetPacks();
|
||||
|
||||
std::vector<ResourcePack*> GetHigherPriorityPacks(ResourcePack& pack);
|
||||
std::vector<ResourcePack*> GetLowerPriorityPacks(ResourcePack& pack);
|
||||
} // namespace ResourcePack
|
102
Source/Core/UICommon/ResourcePack/Manifest.cpp
Normal file
102
Source/Core/UICommon/ResourcePack/Manifest.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright 2018 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "UICommon/ResourcePack/Manifest.h"
|
||||
|
||||
#include <picojson/picojson.h>
|
||||
|
||||
namespace ResourcePack
|
||||
{
|
||||
Manifest::Manifest(const std::string& json)
|
||||
{
|
||||
picojson::value out;
|
||||
auto error = picojson::parse(out, json);
|
||||
|
||||
if (!error.empty())
|
||||
{
|
||||
m_error = "Failed to parse manifest.";
|
||||
m_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Required fields
|
||||
picojson::value& name = out.get("name");
|
||||
picojson::value& version = out.get("version");
|
||||
picojson::value& id = out.get("id");
|
||||
|
||||
// Optional fields
|
||||
picojson::value& authors = out.get("authors");
|
||||
picojson::value& description = out.get("description");
|
||||
picojson::value& website = out.get("website");
|
||||
|
||||
if (!name.is<std::string>() || !id.is<std::string>() || !version.is<std::string>())
|
||||
{
|
||||
m_error = "Some objects have a bad type.";
|
||||
m_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
m_name = name.to_str();
|
||||
m_version = version.to_str();
|
||||
m_id = id.to_str();
|
||||
|
||||
if (authors.is<picojson::array>())
|
||||
{
|
||||
std::string author_list;
|
||||
for (const auto& o : authors.get<picojson::array>())
|
||||
{
|
||||
author_list += o.to_str() + ", ";
|
||||
}
|
||||
|
||||
if (!author_list.empty())
|
||||
m_authors = author_list.substr(0, author_list.size() - 2);
|
||||
}
|
||||
|
||||
if (description.is<std::string>())
|
||||
m_description = description.to_str();
|
||||
|
||||
if (website.is<std::string>())
|
||||
m_website = website.to_str();
|
||||
}
|
||||
|
||||
bool Manifest::IsValid() const
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
const std::string& Manifest::GetName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
const std::string& Manifest::GetVersion() const
|
||||
{
|
||||
return m_version;
|
||||
}
|
||||
|
||||
const std::string& Manifest::GetID() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
const std::string& Manifest::GetError() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
const std::optional<std::string>& Manifest::GetAuthors() const
|
||||
{
|
||||
return m_authors;
|
||||
}
|
||||
|
||||
const std::optional<std::string>& Manifest::GetDescription() const
|
||||
{
|
||||
return m_description;
|
||||
}
|
||||
|
||||
const std::optional<std::string>& Manifest::GetWebsite() const
|
||||
{
|
||||
return m_website;
|
||||
}
|
||||
} // namespace ResourcePack
|
40
Source/Core/UICommon/ResourcePack/Manifest.h
Normal file
40
Source/Core/UICommon/ResourcePack/Manifest.h
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2018 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace ResourcePack
|
||||
{
|
||||
class Manifest
|
||||
{
|
||||
public:
|
||||
explicit Manifest(const std::string& text);
|
||||
|
||||
bool IsValid() const;
|
||||
|
||||
const std::string& GetName() const;
|
||||
const std::string& GetVersion() const;
|
||||
const std::string& GetID() const;
|
||||
const std::string& GetError() const;
|
||||
|
||||
const std::optional<std::string>& GetAuthors() const;
|
||||
const std::optional<std::string>& GetDescription() const;
|
||||
const std::optional<std::string>& GetWebsite() const;
|
||||
|
||||
private:
|
||||
bool m_valid = true;
|
||||
|
||||
std::string m_name;
|
||||
std::string m_version;
|
||||
std::string m_id;
|
||||
std::string m_error;
|
||||
|
||||
std::optional<std::string> m_authors;
|
||||
std::optional<std::string> m_description;
|
||||
std::optional<std::string> m_website;
|
||||
};
|
||||
} // namespace ResourcePack
|
326
Source/Core/UICommon/ResourcePack/ResourcePack.cpp
Normal file
326
Source/Core/UICommon/ResourcePack/ResourcePack.cpp
Normal file
@ -0,0 +1,326 @@
|
||||
// Copyright 2018 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "UICommon/ResourcePack/ResourcePack.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <minizip/unzip.h>
|
||||
|
||||
#include "Common/FileSearch.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "UICommon/ResourcePack/Manager.h"
|
||||
#include "UICommon/ResourcePack/Manifest.h"
|
||||
|
||||
static const char* TEXTURE_PATH = "Load/Textures/";
|
||||
|
||||
namespace ResourcePack
|
||||
{
|
||||
// Since minzip doesn't provide a way to unzip a file of a length > 65535, we have to implement
|
||||
// this ourselves
|
||||
static bool ReadCurrentFileUnlimited(unzFile file, std::vector<char>& destination)
|
||||
{
|
||||
const uint32_t MAX_BUFFER_SIZE = 65535;
|
||||
|
||||
if (unzOpenCurrentFile(file) != UNZ_OK)
|
||||
return false;
|
||||
|
||||
uint32_t bytes_to_go = static_cast<uint32_t>(destination.size());
|
||||
|
||||
while (bytes_to_go > 0)
|
||||
{
|
||||
int bytes_read = unzReadCurrentFile(file, &destination[destination.size() - bytes_to_go],
|
||||
std::min(bytes_to_go, MAX_BUFFER_SIZE));
|
||||
|
||||
if (bytes_read < 0)
|
||||
{
|
||||
unzCloseCurrentFile(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
bytes_to_go -= bytes_read;
|
||||
}
|
||||
|
||||
unzCloseCurrentFile(file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ResourcePack::ResourcePack(const std::string& path) : m_path(path)
|
||||
{
|
||||
auto file = unzOpen(path.c_str());
|
||||
|
||||
if (file == nullptr)
|
||||
{
|
||||
m_valid = false;
|
||||
m_error = "Failed to open resource pack";
|
||||
return;
|
||||
}
|
||||
|
||||
if (unzLocateFile(file, "manifest.json", 0) == UNZ_END_OF_LIST_OF_FILE)
|
||||
{
|
||||
m_valid = false;
|
||||
m_error = "Resource pack is missing a manifest.";
|
||||
return;
|
||||
}
|
||||
|
||||
unz_file_info manifest_info;
|
||||
|
||||
unzGetCurrentFileInfo(file, &manifest_info, nullptr, 0, nullptr, 0, nullptr, 0);
|
||||
|
||||
std::vector<char> manifest_contents;
|
||||
|
||||
manifest_contents.resize(manifest_info.uncompressed_size);
|
||||
|
||||
if (!ReadCurrentFileUnlimited(file, manifest_contents))
|
||||
{
|
||||
m_valid = false;
|
||||
m_error = "Failed to read manifest.json";
|
||||
return;
|
||||
}
|
||||
|
||||
unzCloseCurrentFile(file);
|
||||
|
||||
m_manifest =
|
||||
std::make_shared<Manifest>(std::string(manifest_contents.begin(), manifest_contents.end()));
|
||||
|
||||
if (!m_manifest->IsValid())
|
||||
{
|
||||
m_valid = false;
|
||||
m_error = "Manifest error: " + m_manifest->GetError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (unzLocateFile(file, "logo.png", 0) != UNZ_END_OF_LIST_OF_FILE)
|
||||
{
|
||||
unz_file_info logo_info;
|
||||
|
||||
unzGetCurrentFileInfo(file, &logo_info, nullptr, 0, nullptr, 0, nullptr, 0);
|
||||
|
||||
m_logo_data.resize(logo_info.uncompressed_size);
|
||||
|
||||
if (!ReadCurrentFileUnlimited(file, m_logo_data))
|
||||
{
|
||||
m_valid = false;
|
||||
m_error = "Failed to read logo.png";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
unzGoToFirstFile(file);
|
||||
|
||||
do
|
||||
{
|
||||
std::string filename;
|
||||
|
||||
filename.resize(256);
|
||||
|
||||
unz_file_info texture_info;
|
||||
|
||||
unzGetCurrentFileInfo(file, &texture_info, &filename[0], static_cast<uint16_t>(filename.size()),
|
||||
nullptr, 0, nullptr, 0);
|
||||
|
||||
if (filename.compare(0, 9, "textures/") != 0 || texture_info.uncompressed_size == 0)
|
||||
continue;
|
||||
|
||||
// If a texture is compressed, abort.
|
||||
if (texture_info.compression_method != 0)
|
||||
{
|
||||
m_valid = false;
|
||||
m_error = "Texture " + filename + " is compressed!";
|
||||
return;
|
||||
}
|
||||
|
||||
m_textures.push_back(filename.substr(9));
|
||||
} while (unzGoToNextFile(file) != UNZ_END_OF_LIST_OF_FILE);
|
||||
|
||||
unzClose(file);
|
||||
}
|
||||
|
||||
bool ResourcePack::IsValid() const
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
const std::vector<char>& ResourcePack::GetLogo() const
|
||||
{
|
||||
return m_logo_data;
|
||||
}
|
||||
|
||||
const std::string& ResourcePack::GetPath() const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
const std::string& ResourcePack::GetError() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
const Manifest* ResourcePack::GetManifest() const
|
||||
{
|
||||
return m_manifest.get();
|
||||
}
|
||||
|
||||
const std::vector<std::string>& ResourcePack::GetTextures() const
|
||||
{
|
||||
return m_textures;
|
||||
}
|
||||
|
||||
bool ResourcePack::Install(const std::string& path)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
m_error = "Invalid pack";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto file = unzOpen(m_path.c_str());
|
||||
|
||||
for (const auto& texture : m_textures)
|
||||
{
|
||||
bool provided_by_other_pack = false;
|
||||
|
||||
// Check if a higher priority pack already provides a given texture, don't overwrite it
|
||||
for (const auto& pack : GetHigherPriorityPacks(*this))
|
||||
{
|
||||
if (std::find(pack->GetTextures().begin(), pack->GetTextures().end(), texture) !=
|
||||
pack->GetTextures().end())
|
||||
{
|
||||
provided_by_other_pack = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (provided_by_other_pack)
|
||||
continue;
|
||||
|
||||
if (unzLocateFile(file, ("textures/" + texture).c_str(), 0) != UNZ_OK)
|
||||
{
|
||||
m_error = "Failed to locate texture " + texture;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string m_full_dir;
|
||||
|
||||
SplitPath(path + TEXTURE_PATH + texture, &m_full_dir, nullptr, nullptr);
|
||||
|
||||
if (!File::CreateFullPath(m_full_dir))
|
||||
{
|
||||
m_error = "Failed to create full path " + m_full_dir;
|
||||
return false;
|
||||
}
|
||||
|
||||
unz_file_info texture_info;
|
||||
|
||||
unzGetCurrentFileInfo(file, &texture_info, nullptr, 0, nullptr, 0, nullptr, 0);
|
||||
|
||||
std::vector<char> data;
|
||||
data.resize(texture_info.uncompressed_size);
|
||||
|
||||
if (!ReadCurrentFileUnlimited(file, data))
|
||||
{
|
||||
m_error = "Failed to read texture " + texture;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ofstream out(path + TEXTURE_PATH + texture, std::ios::trunc | std::ios::binary);
|
||||
|
||||
if (!out.good())
|
||||
{
|
||||
m_error = "Failed to write " + texture;
|
||||
return false;
|
||||
}
|
||||
|
||||
out.write(data.data(), data.size());
|
||||
out.flush();
|
||||
}
|
||||
|
||||
unzClose(file);
|
||||
|
||||
SetInstalled(*this, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourcePack::Uninstall(const std::string& path)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
m_error = "Invalid pack";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto lower = GetLowerPriorityPacks(*this);
|
||||
|
||||
SetInstalled(*this, false);
|
||||
|
||||
for (const auto& texture : m_textures)
|
||||
{
|
||||
bool provided_by_other_pack = false;
|
||||
|
||||
// Check if a higher priority pack already provides a given texture, don't delete it
|
||||
for (const auto& pack : GetHigherPriorityPacks(*this))
|
||||
{
|
||||
if (std::find(pack->GetTextures().begin(), pack->GetTextures().end(), texture) !=
|
||||
pack->GetTextures().end())
|
||||
{
|
||||
provided_by_other_pack = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (provided_by_other_pack)
|
||||
continue;
|
||||
|
||||
// Check if a lower priority pack provides a given texture - if so, install it.
|
||||
for (auto& pack : lower)
|
||||
{
|
||||
if (std::find(pack->GetTextures().rbegin(), pack->GetTextures().rend(), texture) !=
|
||||
pack->GetTextures().rend())
|
||||
{
|
||||
pack->Install(path);
|
||||
|
||||
provided_by_other_pack = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (provided_by_other_pack)
|
||||
continue;
|
||||
|
||||
if (File::Exists(path + TEXTURE_PATH + texture) && !File::Delete(path + TEXTURE_PATH + texture))
|
||||
{
|
||||
m_error = "Failed to delete texture " + texture;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Recursively delete empty directories
|
||||
|
||||
std::string dir;
|
||||
|
||||
SplitPath(path + TEXTURE_PATH + texture, &dir, nullptr, nullptr);
|
||||
|
||||
while (dir.length() > (path + TEXTURE_PATH).length())
|
||||
{
|
||||
auto is_empty = Common::DoFileSearch({dir}).empty();
|
||||
|
||||
if (is_empty)
|
||||
File::DeleteDir(dir);
|
||||
|
||||
SplitPath(dir.substr(0, dir.size() - 2), &dir, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourcePack::operator==(const ResourcePack& pack)
|
||||
{
|
||||
return pack.GetPath() == m_path;
|
||||
}
|
||||
|
||||
} // namespace ResourcePack
|
45
Source/Core/UICommon/ResourcePack/ResourcePack.h
Normal file
45
Source/Core/UICommon/ResourcePack/ResourcePack.h
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2018 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
#include "UICommon/ResourcePack/Manifest.h"
|
||||
|
||||
namespace ResourcePack
|
||||
{
|
||||
class ResourcePack
|
||||
{
|
||||
public:
|
||||
explicit ResourcePack(const std::string& path);
|
||||
|
||||
bool IsValid() const;
|
||||
const std::vector<char>& GetLogo() const;
|
||||
|
||||
const std::string& GetPath() const;
|
||||
const std::string& GetError() const;
|
||||
const Manifest* GetManifest() const;
|
||||
const std::vector<std::string>& GetTextures() const;
|
||||
|
||||
bool Install(const std::string& path);
|
||||
bool Uninstall(const std::string& path);
|
||||
|
||||
bool operator==(const ResourcePack& pack);
|
||||
|
||||
private:
|
||||
bool m_valid = true;
|
||||
|
||||
std::string m_path;
|
||||
std::string m_error;
|
||||
|
||||
std::shared_ptr<Manifest> m_manifest;
|
||||
std::vector<std::string> m_textures;
|
||||
std::vector<char> m_logo_data;
|
||||
};
|
||||
} // namespace ResourcePack
|
@ -156,6 +156,7 @@ void SetLocale(std::string locale_name)
|
||||
|
||||
void CreateDirectories()
|
||||
{
|
||||
File::CreateFullPath(File::GetUserPath(D_RESOURCEPACK_IDX));
|
||||
File::CreateFullPath(File::GetUserPath(D_USER_IDX));
|
||||
File::CreateFullPath(File::GetUserPath(D_CACHE_IDX));
|
||||
File::CreateFullPath(File::GetUserPath(D_COVERCACHE_IDX));
|
||||
|
@ -53,6 +53,9 @@
|
||||
<ClCompile Include="AutoUpdate.cpp" />
|
||||
<ClCompile Include="CommandLineParse.cpp" />
|
||||
<ClCompile Include="DiscordPresence.cpp" />
|
||||
<ClCompile Include="ResourcePack\Manager.cpp" />
|
||||
<ClCompile Include="ResourcePack\Manifest.cpp" />
|
||||
<ClCompile Include="ResourcePack\ResourcePack.cpp" />
|
||||
<ClCompile Include="UICommon.cpp" />
|
||||
<ClCompile Include="Disassembler.cpp" />
|
||||
<ClCompile Include="USBUtils.cpp">
|
||||
@ -66,6 +69,9 @@
|
||||
<ClInclude Include="AutoUpdate.h" />
|
||||
<ClInclude Include="CommandLineParse.h" />
|
||||
<ClInclude Include="DiscordPresence.h" />
|
||||
<ClInclude Include="ResourcePack\Manager.h" />
|
||||
<ClInclude Include="ResourcePack\Manifest.h" />
|
||||
<ClInclude Include="ResourcePack\ResourcePack.h" />
|
||||
<ClInclude Include="UICommon.h" />
|
||||
<ClInclude Include="Disassembler.h" />
|
||||
<ClInclude Include="USBUtils.h" />
|
||||
|
Reference in New Issue
Block a user