mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-25 16:44:42 +01:00
The merge keeps the items the store-copy check reads in every mod and writes them to Mods/.reskate/reskate-items.json (highest priority first, so an item two mods ship is the one whose copy the game uses); a live merge keeps the entries of the mods it did not read again, and a patch built before the file existed is built again once. The game reads it when it builds its item catalogue: an item a mod adds says "From: <mod> by <author>" under its name, one of the game's own that a mod changes "Changed by <mod>", unless it has a description of its own.
72 lines
2.6 KiB
C++
72 lines
2.6 KiB
C++
#include "mod_item_sources.h"
|
|
|
|
#include "Engine/Core/Json/json.h"
|
|
|
|
#include <algorithm>
|
|
#include <fstream>
|
|
#include <iterator>
|
|
#include <stdexcept>
|
|
|
|
namespace dingosdk::mods {
|
|
namespace fs = std::filesystem;
|
|
|
|
std::vector<ItemSource> read_item_sources(const fs::path& file) noexcept {
|
|
std::vector<ItemSource> sources;
|
|
try {
|
|
std::ifstream input(file, std::ios::binary);
|
|
if (!input) return {};
|
|
const std::string text{std::istreambuf_iterator<char>(input), std::istreambuf_iterator<char>()};
|
|
const auto root = Json::parse(text, {64u << 20, 32, 1u << 24});
|
|
const auto& mods = root.at("mods");
|
|
for (std::size_t i = 0; i < mods.size(); ++i) {
|
|
const auto& entry = mods.at(i);
|
|
ItemSource source{entry.at("mod").string(), entry.value("title", std::string()), entry.value("author", std::string()), {}};
|
|
const auto& keys = entry.at("items");
|
|
for (std::size_t k = 0; k < keys.size(); ++k) source.keys.push_back(keys.at(k).string());
|
|
sources.push_back(std::move(source));
|
|
}
|
|
} catch (...) {
|
|
return {}; // an unreadable file only means no card says where its item is from
|
|
}
|
|
return sources;
|
|
}
|
|
|
|
void write_item_sources(const fs::path& file, const std::vector<ItemSource>& sources) {
|
|
auto mods = Json::array();
|
|
for (const auto& source : sources) {
|
|
auto entry = Json::object();
|
|
entry["mod"] = source.mod;
|
|
entry["title"] = source.title;
|
|
entry["author"] = source.author;
|
|
entry["items"] = Json(source.keys);
|
|
mods.push_back(std::move(entry));
|
|
}
|
|
auto root = Json::object();
|
|
root["schema"] = 1u;
|
|
root["mods"] = std::move(mods);
|
|
const auto text = root.dump(1);
|
|
// Whole or not at all: the game may read it while a merge runs beside it.
|
|
auto temporary = file;
|
|
temporary += L".new";
|
|
{
|
|
std::ofstream output(temporary, std::ios::binary | std::ios::trunc);
|
|
output.write(text.data(), static_cast<std::streamsize>(text.size()));
|
|
if (!output.flush()) throw std::runtime_error("Cannot write " + temporary.string());
|
|
}
|
|
std::error_code error;
|
|
fs::rename(temporary, file, error);
|
|
if (error) {
|
|
fs::remove(temporary, error);
|
|
throw std::runtime_error("Cannot write " + file.string());
|
|
}
|
|
}
|
|
|
|
std::string item_source_name(const ItemSource& source) {
|
|
std::string name = source.title.empty() ? source.mod : source.title;
|
|
std::replace(name.begin(), name.end(), '_', ' ');
|
|
if (!source.author.empty()) name += " by " + source.author;
|
|
return name;
|
|
}
|
|
|
|
} // namespace dingosdk::mods
|