mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-20 05:16:24 -04:00
refactor seed load/save and add file info hook
This commit is contained in:
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,5 @@
|
||||
|
||||
namespace randomizer::hooks {
|
||||
ModResult initialize();
|
||||
ModResult uninstall();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user