refactor seed load/save and add file info hook

This commit is contained in:
TakaRikka
2026-08-13 02:04:41 -07:00
parent 11055282ff
commit 8229879aab
7 changed files with 191 additions and 26 deletions
+1 -1
View File
@@ -37,7 +37,7 @@ public:
CPaneMgrAlpha* getNoDatBase() { return mNoDatBase; }
void draw() { _draw(); }
private:
// private:
/* 0x04 */ JKRArchive* mArchive;
/* 0x08 */ dDlst_FileInfo_c mFileInfo;
/* 0x1C */ u8 field_0x1c[4];
+83 -8
View File
@@ -13,13 +13,15 @@
#include "d/actor/d_a_alink.h"
#include "d/d_file_select.h"
#include "d/d_file_sel_info.h"
#include "d/d_meter2_info.h"
#include "d/d_save.h"
#include "d/d_shop_system.h"
DEFINE_HOOK(&dFile_select_c::selectDataNameMove, dFile_select_c__selectDataNameMove);
DEFINE_HOOK(&dFile_select_c::dataSelect, dFile_select_c__dataSelect);
DEFINE_HOOK(&dFile_select_c::nameInput2, dFile_select_c__nameInput2);
DEFINE_HOOK(&dFile_info_c::setSaveData, dFile_info_c__setSaveData);
DEFINE_HOOK(&dSv_event_c::isEventBit, dSv_event_c__isEventBit);
DEFINE_HOOK(&dSv_event_c::onEventBit, dSv_event_c__onEventBit);
@@ -142,12 +144,41 @@ HookAction hookPreSelectDataNameMove(ModContext*, void* args, void* retval, void
return HOOK_SKIP_ORIGINAL;
}
void hookPostNameInput2(ModContext*, void* args, void* retval, void* userdata) {
dFile_select_c* i_this = mods::arg<dFile_select_c*>(args, 0);
void hookPostSetSaveData(ModContext* ctx, void* args, void* retval, void* userdata) {
dFile_info_c* i_this = mods::arg<dFile_info_c*>(args, 0);
u8 i_dataNo = mods::arg<u8>(args, 3);
if (i_this->mIsSelectEnd) {
if (!randomizer_GetContext().mHash.empty()) {
session::setupRandomizerFile();
if (*static_cast<int*>(retval) == 0) {
char hash[64];
size_t size = sizeof(hash) - 1;
ModResult rt = session::svc_mng.save->peek_blob(ctx, i_dataNo, "seed_hash", hash, &size);
if (rt != MOD_OK || size == 0) {
// leave file text vanilla if seed hash isn't found
mods::log::debug("no seed_hash found for file {}", i_dataNo);
return;
}
hash[size] = 0;
const std::string curFileSeedHash = hash;
if (!curFileSeedHash.empty()) {
const auto setHBinding = [](J2DTextBox* tbox, J2DTextBoxHBinding bind) {
tbox->mFlags &= 0b0011;
tbox->mFlags |= ((bind & 3) << 2);
};
// Overwrite "Save time" text with "Randomizer"
auto saveTimeText = (J2DTextBox*)i_this->mFileInfo.Scr->search(MULTI_CHAR('f_s_t_02'));
SafeStringCopy(saveTimeText->getStringPtr(), "Randomizer");
setHBinding(saveTimeText, J2DTextBoxHBinding::HBIND_LEFT);
// Overwrite the "Total play time" text with the seed hash
auto playTimeText = (J2DTextBox*)i_this->mFileInfo.Scr->search(MULTI_CHAR('f_p_t_02'));
SafeStringCopy(playTimeText->getStringPtr(), curFileSeedHash.c_str());
// Give the text double the space on the menu incase the seed hash is long
setHBinding(playTimeText, J2DTextBoxHBinding::HBIND_LEFT);
playTimeText->resize(playTimeText->getWidth() * 2, playTimeText->getHeight());
}
}
}
@@ -1191,6 +1222,8 @@ ModResult initialize() {
ADD_HOOK_PRE(dFile_select_c__selectDataNameMove, hookPreSelectDataNameMove);
ADD_HOOK_PRE(dFile_select_c__dataSelect, hookPreDataSelect);
ADD_HOOK_POST(dFile_info_c__setSaveData, hookPostSetSaveData);
ADD_HOOK_PRE(dSv_event_c__isEventBit, hookPreIsEventBit);
ADD_HOOK_PRE(dSv_event_c__onEventBit, hookPreOnEventBit);
@@ -1219,8 +1252,6 @@ ModResult initialize() {
ADD_HOOK_POST(dEvt_control_c__talkEnd, hookPostTalkEnd);
ADD_HOOK_POST(dFile_select_c__nameInput2, hookPostNameInput2);
ADD_HOOK_PRE(dComIfG_play_c__getLayerNo_common_common, hookPreGetLayerNo);
ADD_HOOK_PRE(dItem_getItemFunc, hookPreGetItemFunc);
@@ -1230,4 +1261,48 @@ ModResult initialize() {
return MOD_OK;
}
ModResult uninstall() {
auto svc_hook = session::svc_mng.hook;
mods::hook::uninstall<dFile_select_c__selectDataNameMove>(svc_hook);
mods::hook::uninstall<dFile_select_c__dataSelect>(svc_hook);
mods::hook::uninstall<dFile_info_c__setSaveData>(svc_hook);
mods::hook::uninstall<dSv_event_c__isEventBit>(svc_hook);
mods::hook::uninstall<dSv_event_c__onEventBit>(svc_hook);
mods::hook::uninstall<dSv_memBit_c__isSwitch>(svc_hook);
mods::hook::uninstall<dSv_memBit_c__onSwitch>(svc_hook);
mods::hook::uninstall<dSv_memBit_c__onDungeonItem>(svc_hook);
mods::hook::uninstall<dSv_memBit_c__offDungeonItem>(svc_hook);
mods::hook::uninstall<dSv_memBit_c__isDungeonItem>(svc_hook);
mods::hook::uninstall<dSv_player_status_b_c__isDarkClearLV>(svc_hook);
mods::hook::uninstall<dSv_player_item_c__checkEmptyBottle>(svc_hook);
mods::hook::uninstall<dSv_player_item_c__setLineUpItem>(svc_hook);
mods::hook::uninstall<dSv_info_c__onSwitch>(svc_hook);
mods::hook::uninstall<ObjGb_Create>(svc_hook);
mods::hook::uninstall<readItemTexture>(svc_hook);
mods::hook::uninstall<dShopSystem_c__seq_decide_yes>(svc_hook);
mods::hook::uninstall<dItemData_CheckFieldItemCreateHeap>(svc_hook);
mods::hook::uninstall<dEvt_control_c__talkEnd>(svc_hook);
mods::hook::uninstall<dComIfG_play_c__getLayerNo_common_common>(svc_hook);
mods::hook::uninstall<dItem_getItemFunc>(svc_hook);
mods::hook::uninstall<dItem_checkItemGet>(svc_hook);
mods::hook::uninstall<onStageSwitch>(svc_hook);
return MOD_OK;
}
}
+1
View File
@@ -4,4 +4,5 @@
namespace randomizer::hooks {
ModResult initialize();
ModResult uninstall();
}
+2 -1
View File
@@ -56,8 +56,9 @@ MOD_EXPORT ModResult mod_update(ModError*) {
}
MOD_EXPORT ModResult mod_shutdown(ModError*) {
randomizer::session::deactivateSeed();
randomizer::hooks::uninstall();
svc_log->info(mod_ctx, "randomizer unloaded");
randomizer::item::restore_item_data_tables();
return MOD_OK;
}
}
+97 -10
View File
@@ -16,15 +16,14 @@
namespace randomizer::session {
ServiceManager svc_mng;
std::string g_pending_seed_hash{};
ModResult initialize(const ServiceManager& services) {
svc_mng = services;
return MOD_OK;
}
SaveObserverHandle s_save_observer{};
ItemCheckHandle s_check_resolver{};
ItemGiveHandle s_check_observer{};
std::vector<StageActorHandle> s_stage_edits{};
constexpr const char* kSeedHashBlobName = "seed_hash";
struct DerivedKey {
int stage_id;
@@ -123,18 +122,46 @@ void observe_give(ModContext*, const ItemGiveInfo* info, void*) {
}
}
void activateSeed() {
bool activateSeed(const char* hash) {
auto& ctx = randomizer_GetContext();
ctx = RandomizerContext();
if (auto err = ctx.LoadFromHash(hash); err.has_value() || ctx.mHash.empty()) {
mods::log::error("failed to load seed {}", hash);
return false;
}
item::apply_item_data_tables();
svc_mng.item->set_check_resolver(mod_ctx, nullptr, resolve_check, nullptr, &s_check_resolver);
svc_mng.item->observe_gives(mod_ctx, observe_give, nullptr, &s_check_observer);
registerStageEdits();
mods::log::info("activated seed {}", ctx.mHash);
return true;
}
void deactivateSeed() {
if (s_check_resolver != 0) {
svc_mng.item->clear_check_resolver(mod_ctx, s_check_resolver);
s_check_resolver = 0;
}
if (s_check_observer != 0) {
svc_mng.item->unobserve_gives(mod_ctx, s_check_observer);
s_check_observer = 0;
}
for (auto handle : s_stage_edits) {
svc_mng.stage->remove_actor_edit(mod_ctx, handle);
}
s_stage_edits.clear();
item::restore_item_data_tables();
randomizer_GetContext() = RandomizerContext{};
g_randomizerState = RandomizerState{};
}
void setupRandomizerFile() {
activateSeed();
// Setup file based on randomizer data
auto& randoData = randomizer_GetContext();
randoData.mCreatingSave = true;
@@ -239,6 +266,10 @@ void registerStageEdits() {
res = svc_mng.stage->patch_actor(
mod_ctx, stage, room, layer, crc, bytes.data(), bytes.size(), &handle);
}
if (res == MOD_OK) {
s_stage_edits.push_back(handle);
}
}
}
@@ -252,9 +283,65 @@ void registerStageEdits() {
const s8 layer = static_cast<s8>(key & 0xFF);
for (const auto& bytes : additions) {
StageActorHandle handle{};
svc_mng.stage->add_actor(mod_ctx, stage, room, layer, bytes.data(), bytes.size(), &handle);
ModResult rt;
rt = svc_mng.stage->add_actor(mod_ctx, stage, room, layer, bytes.data(), bytes.size(), &handle);
if (rt == MOD_OK) {
s_stage_edits.push_back(handle);
}
}
}
}
void onNewSave(ModContext*, uint32_t, void*) {
const std::string hash = g_pending_seed_hash;
if (hash.empty())
return;
if (!activateSeed(hash.c_str()))
return;
svc_mng.save->set_blob(svc_mng.mod_ctx, kSeedHashBlobName, hash.data(), hash.size());
setupRandomizerFile();
}
void onSaveLoaded(ModContext*, uint32_t, void*) {
size_t size = 0;
if (svc_mng.save->get_blob(mod_ctx, kSeedHashBlobName, nullptr, &size) != MOD_OK || size == 0) {
mods::log::error("seed_hash not found!");
deactivateSeed();
return;
}
std::string hash(size, '\0');
if (svc_mng.save->get_blob(mod_ctx, kSeedHashBlobName, hash.data(), &size) != MOD_OK) {
mods::log::error("failed to get seed_hash!");
deactivateSeed();
return;
}
if (randomizer_GetContext().mHash != hash) {
deactivateSeed();
activateSeed(hash.c_str());
}
loadAncientDocumentNum();
}
ModResult initialize(const ServiceManager& services) {
svc_mng = services;
ModResult rt = svc_mng.save->observe_saves(
svc_mng.mod_ctx,
onNewSave,
onSaveLoaded,
nullptr,
nullptr,
&s_save_observer);
if (rt != MOD_OK) {
return rt;
}
return MOD_OK;
}
}
+4
View File
@@ -10,6 +10,8 @@
#include "mods/svc/stage.h"
#include "mods/svc/item.h"
#include <string>
namespace randomizer::session {
struct ServiceManager {
ModContext* mod_ctx;
@@ -25,9 +27,11 @@ struct ServiceManager {
};
extern ServiceManager svc_mng;
extern std::string g_pending_seed_hash;
ModResult initialize(const ServiceManager& services);
void deactivateSeed();
void setupRandomizerFile();
void registerStageEdits();
}
+3 -6
View File
@@ -553,7 +553,7 @@ ModResult buildPlayTab(ModContext* ctx, UiWindowHandle, UiElementHandle leftPane
for (const auto& entry : std::filesystem::directory_iterator(paths::GetRandomizerSeedsPath())) {
if (entry.is_directory()) {
std::string hash = entry.path().filename().string();
if (randomizer_GetContext().mHash == hash) {
if (session::g_pending_seed_hash == hash) {
break;
}
idx++;
@@ -567,10 +567,7 @@ ModResult buildPlayTab(ModContext* ctx, UiWindowHandle, UiElementHandle leftPane
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();
randomizer_GetContext() = RandomizerContext();
randomizer_GetContext().LoadFromHash(hash);
session::registerStageEdits();
session::g_pending_seed_hash = entry.path().filename().string();
break;
}
idx++;
@@ -590,7 +587,7 @@ ModResult buildPlayTab(ModContext* ctx, UiWindowHandle, UiElementHandle leftPane
};
desc.user_data = &g_file_select_window_ctx.window_handle;
desc.is_disabled = [](ModContext*, void*) {
return randomizer_GetContext().mHash.empty();
return session::g_pending_seed_hash.empty();
};
session::svc_mng.ui->pane_add_control(session::svc_mng.mod_ctx, leftPane, &desc, nullptr);
}