Files
ReSkate/Engine/Vfs/mod_item_sources.cpp
T
AyeZeeBB 4cbec93a3e Customization: a mod's item says which mod it is from
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.
2026-10-17 10:53:45 -05:00

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