Zelda64Recomp/src/game/quicksaving.cpp

122 lines
4.1 KiB
C++

// Quicksaving is disabled for now
#if 0
#include "recomp_helpers.h"
#include "recomp_input.h"
#include "../ultramodern/ultramodern.hpp"
enum class QuicksaveAction {
None,
Save,
Load
};
std::atomic<QuicksaveAction> cur_quicksave_action = QuicksaveAction::None;
void recomp::quicksave_save() {
cur_quicksave_action.store(QuicksaveAction::Save);
}
void recomp::quicksave_load() {
cur_quicksave_action.store(QuicksaveAction::Load);
}
uint8_t saved_rdram[ultramodern::rdram_size];
thread_local recomp_context saved_context;
void save_context(recomp_context* ctx) {
saved_context = *ctx;
}
void load_context(recomp_context* ctx) {
*ctx = saved_context;
// Restore the pointer to the odd floats for correctly handling mips3 float mode.
if (ctx->mips3_float_mode) {
// FR = 1, odd single floats point to their own registers
ctx->f_odd = &ctx->f1.u32l;
}
else {
// FR = 0, odd single floats point to the upper half of the previous register
ctx->f_odd = &ctx->f0.u32h;
}
}
extern "C" void recomp_handle_quicksave_actions(uint8_t* rdram, recomp_context* ctx) {
QuicksaveAction action = cur_quicksave_action.load();
if (action != QuicksaveAction::None) {
PTR(OSMesgQueue) quicksave_enter_mq = _arg<0, PTR(OSMesgQueue)>(rdram, ctx);
PTR(OSMesgQueue) quicksave_exit_mq = _arg<1, PTR(OSMesgQueue)>(rdram, ctx);
printf("saving context for thread %d\n", TO_PTR(OSThread, ultramodern::this_thread())->id);
// Save or load the thread's context as needed based on the action.
if (action == QuicksaveAction::Save) {
save_context(ctx);
}
else if (action == QuicksaveAction::Load) {
load_context(ctx);
}
else {
assert(false);
}
// Tell the main thread that one of the other permanent threads is ready for performing a quicksave action.
osSendMesg(rdram, quicksave_enter_mq, NULLPTR, OS_MESG_NOBLOCK);
// Wait for the main thread to signal that other permanent threads are safe to continue.
osRecvMesg(rdram, quicksave_exit_mq, NULLPTR, OS_MESG_BLOCK);
}
}
extern "C" void wake_threads_for_quicksave_action(uint8_t* rdram, recomp_context* ctx);
extern "C" void recomp_handle_quicksave_actions_main(uint8_t* rdram, recomp_context* ctx) {
QuicksaveAction action = cur_quicksave_action.load();
if (action != QuicksaveAction::None) {
PTR(OSMesgQueue) quicksave_enter_mq = _arg<0, PTR(OSMesgQueue)>(rdram, ctx);
PTR(OSMesgQueue) quicksave_exit_mq = _arg<1, PTR(OSMesgQueue)>(rdram, ctx);
wake_threads_for_quicksave_action(rdram, ctx);
// Wait for all other permanent threads (hence the minus 1) to signal that they're ready for a quicksave action.
for (uint32_t i = 0; i < ultramodern::permanent_thread_count() - 1; i++) {
osRecvMesg(rdram, quicksave_enter_mq, NULLPTR, OS_MESG_BLOCK);
}
// Allow any temporary threads to complete by lowering this thread's priority to 0.
// TODO this won't cause all temporary threads to complete if any are blocked by permanent threads
// or events like timers. Situations like that will need to be handed on a case-by-case basis for a given game.
if (ultramodern::temporary_thread_count() != 0) {
OSPri old_pri = osGetThreadPri(rdram, NULLPTR);
osSetThreadPri(rdram, NULLPTR, 0);
osSetThreadPri(rdram, NULLPTR, old_pri);
}
if (action == QuicksaveAction::Save) {
std::copy(rdram, rdram + ultramodern::rdram_size, saved_rdram);
}
else if (action == QuicksaveAction::Load) {
std::copy(saved_rdram, saved_rdram + ultramodern::rdram_size, rdram);
}
else {
assert(false);
}
printf("Quicksave action complete\n");
cur_quicksave_action.store(QuicksaveAction::None);
// Tell all other permanent threads that they're good to continue.
for (uint32_t i = 0; i < ultramodern::permanent_thread_count() - 1; i++) {
osSendMesg(rdram, quicksave_exit_mq, NULLPTR, OS_MESG_BLOCK);
}
}
}
#endif