Fix randomizer seed serialization

This commit is contained in:
Luke Street
2026-08-21 11:52:52 -06:00
parent 9977e78758
commit 641855e49e
3 changed files with 75 additions and 71 deletions
+20 -18
View File
@@ -56,16 +56,20 @@ RandomizerContext::FlowNodeType parse_flow_node_type(const YAML::Node& node) {
throw std::runtime_error("Unknown flow node type: " + type);
}
void write_flow_reference(
YAML::Node node, const RandomizerContext::FlowReference& reference) {
YAML::Node write_flow_reference(const RandomizerContext::FlowReference& reference) {
YAML::Node node{};
if (reference.nativeId.has_value()) {
node = reference.nativeId.value();
} else {
node = reference.name;
}
return node;
}
RandomizerContext::FlowReference parse_flow_reference(const YAML::Node& node) {
if (!node.IsScalar()) {
throw std::runtime_error("Flow reference must be a node ID or name");
}
RandomizerContext::FlowReference reference{};
try {
reference.nativeId = node.as<u16>();
@@ -78,15 +82,15 @@ RandomizerContext::FlowReference parse_flow_reference(const YAML::Node& node) {
void write_message_style(
YAML::Node node, const RandomizerContext::MessageStyleData& style) {
node["eventLabelId"] = style.eventLabelId;
node["speaker"] = style.speaker;
node["boxKind"] = style.boxKind;
node["drawType"] = style.drawType;
node["boxPosition"] = style.boxPosition;
node["lineAlignment"] = style.lineAlignment;
node["speakerMood"] = style.speakerMood;
node["cameraAttr"] = style.cameraAttr;
node["talkAnim"] = style.talkAnim;
node["faceAnim"] = style.faceAnim;
node["speaker"] = static_cast<u16>(style.speaker);
node["boxKind"] = static_cast<u16>(style.boxKind);
node["drawType"] = static_cast<u16>(style.drawType);
node["boxPosition"] = static_cast<u16>(style.boxPosition);
node["lineAlignment"] = static_cast<u16>(style.lineAlignment);
node["speakerMood"] = static_cast<u16>(style.speakerMood);
node["cameraAttr"] = static_cast<u16>(style.cameraAttr);
node["talkAnim"] = static_cast<u16>(style.talkAnim);
node["faceAnim"] = static_cast<u16>(style.faceAnim);
node["trailingData"] = style.trailingData;
}
@@ -191,7 +195,7 @@ std::optional<std::string> RandomizerContext::WriteToFile() {
for (const auto& flow : mFlowNodes) {
YAML::Node node{};
node["type"] = flow_node_type_name(flow.type);
node["group"] = flow.group;
node["group"] = static_cast<u16>(flow.group);
if (flow.patchIndex.has_value()) {
node["patchIndex"] = flow.patchIndex.value();
} else {
@@ -202,22 +206,20 @@ std::optional<std::string> RandomizerContext::WriteToFile() {
node["operation"] = flow.operation;
}
if (flow.type == FlowNodeType::MESSAGE) {
write_flow_reference(node["message"], flow.message);
node["message"] = write_flow_reference(flow.message);
}
if (flow.type != FlowNodeType::BRANCH) {
write_flow_reference(node["next"], flow.next);
node["next"] = write_flow_reference(flow.next);
}
for (const auto& result : flow.results) {
YAML::Node resultNode{};
write_flow_reference(resultNode, result);
node["results"].push_back(resultNode);
node["results"].push_back(write_flow_reference(result));
}
out["mFlowNodes"].push_back(node);
}
for (const auto& message : mCustomMessages) {
YAML::Node node{};
node["group"] = message.group;
node["group"] = static_cast<u16>(message.group);
node["name"] = message.name;
write_message_style(node["style"], message.style);
for (const auto& [language, text] : message.text) {
+1 -1
View File
@@ -19,7 +19,7 @@
*/
class RandomizerContext {
public:
static constexpr u32 FORMAT_VERSION = 2;
static constexpr u32 FORMAT_VERSION = 3;
static constexpr size_t ACTR_CRC_SIZE = 32;
static constexpr size_t TGSC_CRC_SIZE = 35; // 3 extra bytes for scale x, y, z
static constexpr size_t OBJ_DELETE_SIZE = 1;
+54 -52
View File
@@ -14,6 +14,7 @@
#include "rando_seed_generation.hpp"
#include "config_store.hpp"
#include <algorithm>
#include <mutex>
#include <thread>
#include <map>
@@ -107,6 +108,31 @@ UiMenuTabHandle g_menu_tab{};
FileSelectGateWindowCtx g_file_select_window_ctx{};
namespace {
std::vector<std::string> get_compatible_seed_hashes() {
const std::filesystem::path seedDir = paths::GetRandomizerSeedsPath();
std::filesystem::create_directories(seedDir);
std::vector<std::string> seedHashes;
for (const auto& entry : std::filesystem::directory_iterator(seedDir)) {
if (!entry.is_directory()) {
continue;
}
try {
const YAML::Node seedData = LoadYAML(entry.path() / "seed.dat");
if (seedData["formatVersion"] &&
seedData["formatVersion"].as<u32>() == RandomizerContext::FORMAT_VERSION) {
seedHashes.push_back(entry.path().filename().string());
}
} catch (const std::exception&) {
// Incomplete or malformed seeds cannot be activated and should not be offered.
}
}
std::ranges::sort(seedHashes);
return seedHashes;
}
// Control Helpers
ModResult add_button(UiElementHandle pane, const char* label, const char* help_rml,
UiPressedFn on_pressed, void* userdata = nullptr, UiElementHandle* out_handle = nullptr)
@@ -353,18 +379,9 @@ ModResult buildSeedManagementTab(ModContext* ctx, UiWindowHandle, UiElementHandl
});
{
std::filesystem::path seed_dir = paths::GetRandomizerSeedsPath();
if (!std::filesystem::exists(seed_dir))
std::filesystem::create_directory(seed_dir);
std::string help_rml = "Select a seed above to delete it.";
std::vector<std::string> seedHashes;
for (const auto& entry : std::filesystem::directory_iterator(seed_dir)) {
if (entry.is_directory()) {
seedHashes.push_back(entry.path().filename().string());
}
}
const std::vector<std::string> seedHashes = get_compatible_seed_hashes();
std::vector<const char*> availableSeeds;
for (const auto& hash : seedHashes) {
@@ -380,20 +397,16 @@ ModResult buildSeedManagementTab(ModContext* ctx, UiWindowHandle, UiElementHandl
out_value->int_value = 0;
},
[](ModContext*, void*, const UiControlValue* value) {
int idx = 0;
for (const auto& entry : std::filesystem::directory_iterator(paths::GetRandomizerSeedsPath())) {
if (entry.is_directory()) {
if (idx == value->int_value) {
std::string hash = entry.path().filename().string();
if (randomizer_GetContext().mHash == hash) {
randomizer_GetContext() = RandomizerContext{};
}
std::filesystem::remove_all(entry);
break;
}
idx++;
}
const std::vector<std::string> seedHashes = get_compatible_seed_hashes();
if (value->int_value < 0 || static_cast<size_t>(value->int_value) >= seedHashes.size()) {
return;
}
const std::string& hash = seedHashes[value->int_value];
if (randomizer_GetContext().mHash == hash) {
randomizer_GetContext() = RandomizerContext{};
}
std::filesystem::remove_all(paths::GetRandomizerSeedsPath() / hash);
});
}
@@ -1087,24 +1100,20 @@ void OnMenuTabSelected(ModContext* ctx, void*) {
ModResult buildPlayTab(ModContext* ctx, UiWindowHandle, UiElementHandle leftPane,
UiElementHandle rightPane, void*, ModError*)
{
std::filesystem::path seed_dir = paths::GetRandomizerSeedsPath();
if (!std::filesystem::exists(seed_dir))
std::filesystem::create_directory(seed_dir);
const std::vector<std::string> seedHashes = get_compatible_seed_hashes();
std::string help_rml = "";
if (std::filesystem::is_empty(seed_dir)) {
if (seedHashes.empty()) {
help_rml = "No seeds generated! You can generate a seed from the Seed Management Tab.";
} else {
help_rml = "Choose which seed you want to play.";
}
std::vector<std::string> seedHashes;
for (const auto& entry : std::filesystem::directory_iterator(seed_dir)) {
if (entry.is_directory()) {
seedHashes.push_back(entry.path().filename().string());
}
if (!session::g_pending_seed_hash.empty() &&
!std::ranges::contains(seedHashes, session::g_pending_seed_hash)) {
session::g_pending_seed_hash.clear();
}
std::vector<const char*> availableSeeds;
for (const auto& hash : seedHashes) {
availableSeeds.push_back(hash.c_str());
@@ -1116,30 +1125,23 @@ ModResult buildPlayTab(ModContext* ctx, UiWindowHandle, UiElementHandle leftPane
availableSeeds.data(),
availableSeeds.size(),
[](ModContext*, void*, UiControlValue* out_value) {
int idx = 0;
for (const auto& entry : std::filesystem::directory_iterator(paths::GetRandomizerSeedsPath())) {
if (entry.is_directory()) {
std::string hash = entry.path().filename().string();
if (session::g_pending_seed_hash == hash) {
break;
}
idx++;
}
const std::vector<std::string> seedHashes = get_compatible_seed_hashes();
const auto selected = std::ranges::find(seedHashes, session::g_pending_seed_hash);
if (selected == seedHashes.end()) {
out_value->int_value = 0;
return;
}
out_value->int_value = idx;
out_value->int_value = static_cast<int32_t>(std::distance(seedHashes.begin(), selected));
},
[](ModContext*, void*, const UiControlValue* value) {
int idx = 0;
for (const auto& entry : std::filesystem::directory_iterator(paths::GetRandomizerSeedsPath())) {
if (entry.is_directory()) {
if (idx == value->int_value) {
session::g_pending_seed_hash = entry.path().filename().string();
break;
}
idx++;
}
const std::vector<std::string> seedHashes = get_compatible_seed_hashes();
if (value->int_value < 0 || static_cast<size_t>(value->int_value) >= seedHashes.size()) {
session::g_pending_seed_hash.clear();
return;
}
session::g_pending_seed_hash = seedHashes[value->int_value];
});
{