mirror of
https://github.com/N64Recomp/N64ModernRuntime.git
synced 2026-05-11 11:22:05 +00:00
Merge 49c40ee687 into 0a53855333
This commit is contained in:
commit
e2ebb4d522
8 changed files with 191 additions and 144 deletions
|
|
@ -21,6 +21,7 @@ add_library(librecomp STATIC
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/print.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/print.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/recomp.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/recomp.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/rsp.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/rsp.cpp"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/save.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/sp.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/sp.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/ultra_stubs.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/ultra_stubs.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/ultra_translation.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/ultra_translation.cpp"
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,10 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include "ultramodern/ultramodern.hpp"
|
||||||
|
|
||||||
#include "recomp.h"
|
#include "recomp.h"
|
||||||
#include "rsp.hpp"
|
#include "rsp.hpp"
|
||||||
#include <ultramodern/ultramodern.hpp>
|
|
||||||
|
|
||||||
namespace recomp {
|
namespace recomp {
|
||||||
struct GameEntry {
|
struct GameEntry {
|
||||||
|
|
|
||||||
20
librecomp/include/librecomp/save.hpp
Normal file
20
librecomp/include/librecomp/save.hpp
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef __LIBRECOMP_SAVE_HPP__
|
||||||
|
#define __LIBRECOMP_SAVE_HPP__
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "ultramodern/ultra64.h"
|
||||||
|
|
||||||
|
namespace recomp {
|
||||||
|
namespace save {
|
||||||
|
void init(RDRAM_ARG1);
|
||||||
|
void join_thread();
|
||||||
|
|
||||||
|
void write_ptr(const void* in, uint32_t offset, uint32_t count);
|
||||||
|
void write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count);
|
||||||
|
void read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count);
|
||||||
|
void clear(uint32_t start, uint32_t size, char value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <ultramodern/ultra64.h>
|
|
||||||
#include <ultramodern/ultramodern.hpp>
|
#include "ultramodern/ultra64.h"
|
||||||
|
#include "ultramodern/ultramodern.hpp"
|
||||||
|
|
||||||
#include "recomp.h"
|
#include "recomp.h"
|
||||||
|
#include "librecomp/save.hpp"
|
||||||
|
|
||||||
// TODO move this out into ultramodern code
|
// TODO move this out into ultramodern code
|
||||||
|
|
||||||
|
|
@ -13,11 +16,6 @@ constexpr uint32_t page_count = flash_size / page_size;
|
||||||
constexpr uint32_t sector_size = page_size * pages_per_sector;
|
constexpr uint32_t sector_size = page_size * pages_per_sector;
|
||||||
constexpr uint32_t sector_count = flash_size / sector_size;
|
constexpr uint32_t sector_count = flash_size / sector_size;
|
||||||
|
|
||||||
void save_write_ptr(const void* in, uint32_t offset, uint32_t count);
|
|
||||||
void save_write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count);
|
|
||||||
void save_read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count);
|
|
||||||
void save_clear(uint32_t start, uint32_t size, char value);
|
|
||||||
|
|
||||||
std::array<char, page_size> write_buffer;
|
std::array<char, page_size> write_buffer;
|
||||||
|
|
||||||
extern "C" void osFlashInit_recomp(uint8_t * rdram, recomp_context * ctx) {
|
extern "C" void osFlashInit_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||||
|
|
@ -44,13 +42,13 @@ extern "C" void osFlashClearStatus_recomp(uint8_t * rdram, recomp_context * ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void osFlashAllErase_recomp(uint8_t * rdram, recomp_context * ctx) {
|
extern "C" void osFlashAllErase_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||||
save_clear(0, ultramodern::save_size, 0xFF);
|
recomp::save::clear(0, ultramodern::save_size, 0xFF);
|
||||||
|
|
||||||
ctx->r2 = 0;
|
ctx->r2 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void osFlashAllEraseThrough_recomp(uint8_t * rdram, recomp_context * ctx) {
|
extern "C" void osFlashAllEraseThrough_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||||
save_clear(0, ultramodern::save_size, 0xFF);
|
recomp::save::clear(0, ultramodern::save_size, 0xFF);
|
||||||
|
|
||||||
ctx->r2 = 0;
|
ctx->r2 = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -65,7 +63,7 @@ extern "C" void osFlashSectorErase_recomp(uint8_t * rdram, recomp_context * ctx)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
save_clear(page_num * page_size, page_size, 0xFF);
|
recomp::save::clear(page_num * page_size, page_size, 0xFF);
|
||||||
|
|
||||||
ctx->r2 = 0;
|
ctx->r2 = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +78,7 @@ extern "C" void osFlashSectorEraseThrough_recomp(uint8_t * rdram, recomp_context
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
save_clear(page_num * page_size, page_size, 0xFF);
|
recomp::save::clear(page_num * page_size, page_size, 0xFF);
|
||||||
|
|
||||||
ctx->r2 = 0;
|
ctx->r2 = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +109,7 @@ extern "C" void osFlashWriteArray_recomp(uint8_t * rdram, recomp_context * ctx)
|
||||||
uint32_t page_num = ctx->r4;
|
uint32_t page_num = ctx->r4;
|
||||||
|
|
||||||
// Copy the write buffer into the save file
|
// Copy the write buffer into the save file
|
||||||
save_write_ptr(write_buffer.data(), page_num * page_size, page_size);
|
recomp::save::write_ptr(write_buffer.data(), page_num * page_size, page_size);
|
||||||
|
|
||||||
ctx->r2 = 0;
|
ctx->r2 = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -128,7 +126,7 @@ extern "C" void osFlashReadArray_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||||
uint32_t count = n_pages * page_size;
|
uint32_t count = n_pages * page_size;
|
||||||
|
|
||||||
// Read from the save file into the provided buffer
|
// Read from the save file into the provided buffer
|
||||||
save_read(PASS_RDRAM dramAddr, offset, count);
|
recomp::save::read(PASS_RDRAM dramAddr, offset, count);
|
||||||
|
|
||||||
// Send the message indicating read completion
|
// Send the message indicating read completion
|
||||||
osSendMesg(PASS_RDRAM mq, 0, OS_MESG_NOBLOCK);
|
osSendMesg(PASS_RDRAM mq, 0, OS_MESG_NOBLOCK);
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,11 @@
|
||||||
#include "recomp.h"
|
#include "recomp.h"
|
||||||
#include "game.hpp"
|
#include "game.hpp"
|
||||||
#include "files.hpp"
|
#include "files.hpp"
|
||||||
#include <ultramodern/ultra64.h>
|
|
||||||
#include <ultramodern/ultramodern.hpp>
|
#include "ultramodern/ultra64.h"
|
||||||
|
#include "ultramodern/ultramodern.hpp"
|
||||||
|
|
||||||
|
#include "librecomp/save.hpp"
|
||||||
|
|
||||||
static std::vector<uint8_t> rom;
|
static std::vector<uint8_t> rom;
|
||||||
|
|
||||||
|
|
@ -87,128 +90,6 @@ void recomp::do_rom_pio(uint8_t* rdram, gpr ram_address, uint32_t physical_addr)
|
||||||
MEM_B(3, ram_address) = *rom_addr++;
|
MEM_B(3, ram_address) = *rom_addr++;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct {
|
|
||||||
std::array<char, 0x20000> save_buffer;
|
|
||||||
std::thread saving_thread;
|
|
||||||
moodycamel::LightweightSemaphore write_sempahore;
|
|
||||||
std::mutex save_buffer_mutex;
|
|
||||||
} save_context;
|
|
||||||
|
|
||||||
const std::u8string save_folder = u8"saves";
|
|
||||||
|
|
||||||
extern std::filesystem::path config_path;
|
|
||||||
|
|
||||||
std::filesystem::path get_save_file_path() {
|
|
||||||
return config_path / save_folder / (std::u8string{recomp::current_game_id()} + u8".bin");
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_save_file() {
|
|
||||||
bool saving_failed = false;
|
|
||||||
{
|
|
||||||
std::ofstream save_file = recomp::open_output_file_with_backup(get_save_file_path(), std::ios_base::binary);
|
|
||||||
|
|
||||||
if (save_file.good()) {
|
|
||||||
std::lock_guard lock{ save_context.save_buffer_mutex };
|
|
||||||
save_file.write(save_context.save_buffer.data(), save_context.save_buffer.size());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
saving_failed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!saving_failed) {
|
|
||||||
saving_failed = !recomp::finalize_output_file_with_backup(get_save_file_path());
|
|
||||||
}
|
|
||||||
if (saving_failed) {
|
|
||||||
ultramodern::error_handling::message_box("Failed to write to the save file. Check your file permissions and whether the save folder has been moved to Dropbox or similar, as this can cause issues.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern std::atomic_bool exited;
|
|
||||||
|
|
||||||
void saving_thread_func(RDRAM_ARG1) {
|
|
||||||
while (!exited) {
|
|
||||||
bool save_buffer_updated = false;
|
|
||||||
// Repeatedly wait for a new action to be sent.
|
|
||||||
constexpr int64_t wait_time_microseconds = 10000;
|
|
||||||
constexpr int max_actions = 128;
|
|
||||||
int num_actions = 0;
|
|
||||||
|
|
||||||
// Wait up to the given timeout for a write to come in. Allow multiple writes to coalesce together into a single save.
|
|
||||||
// Cap the number of coalesced writes to guarantee that the save buffer eventually gets written out to the file even if the game
|
|
||||||
// is constantly sending writes.
|
|
||||||
while (save_context.write_sempahore.wait(wait_time_microseconds) && num_actions < max_actions) {
|
|
||||||
save_buffer_updated = true;
|
|
||||||
num_actions++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If an action came through that affected the save file, save the updated contents.
|
|
||||||
if (save_buffer_updated) {
|
|
||||||
update_save_file();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void save_write_ptr(const void* in, uint32_t offset, uint32_t count) {
|
|
||||||
{
|
|
||||||
std::lock_guard lock { save_context.save_buffer_mutex };
|
|
||||||
memcpy(&save_context.save_buffer[offset], in, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
save_context.write_sempahore.signal();
|
|
||||||
}
|
|
||||||
|
|
||||||
void save_write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count) {
|
|
||||||
{
|
|
||||||
std::lock_guard lock { save_context.save_buffer_mutex };
|
|
||||||
for (uint32_t i = 0; i < count; i++) {
|
|
||||||
save_context.save_buffer[offset + i] = MEM_B(i, rdram_address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
save_context.write_sempahore.signal();
|
|
||||||
}
|
|
||||||
|
|
||||||
void save_read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count) {
|
|
||||||
std::lock_guard lock { save_context.save_buffer_mutex };
|
|
||||||
for (size_t i = 0; i < count; i++) {
|
|
||||||
MEM_B(i, rdram_address) = save_context.save_buffer[offset + i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void save_clear(uint32_t start, uint32_t size, char value) {
|
|
||||||
{
|
|
||||||
std::lock_guard lock { save_context.save_buffer_mutex };
|
|
||||||
std::fill_n(save_context.save_buffer.begin() + start, size, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
save_context.write_sempahore.signal();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ultramodern::init_saving(RDRAM_ARG1) {
|
|
||||||
std::filesystem::path save_file_path = get_save_file_path();
|
|
||||||
|
|
||||||
// Ensure the save file directory exists.
|
|
||||||
std::filesystem::create_directories(save_file_path.parent_path());
|
|
||||||
|
|
||||||
// Read the save file if it exists.
|
|
||||||
std::ifstream save_file = recomp::open_input_file_with_backup(save_file_path, std::ios_base::binary);
|
|
||||||
if (save_file.good()) {
|
|
||||||
save_file.read(save_context.save_buffer.data(), save_context.save_buffer.size());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Otherwise clear the save file to all zeroes.
|
|
||||||
save_context.save_buffer.fill(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
save_context.saving_thread = std::thread{saving_thread_func, PASS_RDRAM};
|
|
||||||
}
|
|
||||||
|
|
||||||
void ultramodern::join_saving_thread() {
|
|
||||||
if (save_context.saving_thread.joinable()) {
|
|
||||||
save_context.saving_thread.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void do_dma(RDRAM_ARG PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_addr, uint32_t size, uint32_t direction) {
|
void do_dma(RDRAM_ARG PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_addr, uint32_t size, uint32_t direction) {
|
||||||
// TODO asynchronous transfer
|
// TODO asynchronous transfer
|
||||||
// TODO implement unaligned DMA correctly
|
// TODO implement unaligned DMA correctly
|
||||||
|
|
@ -221,7 +102,7 @@ void do_dma(RDRAM_ARG PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_
|
||||||
osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK);
|
osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK);
|
||||||
} else if (physical_addr >= sram_base) {
|
} else if (physical_addr >= sram_base) {
|
||||||
// read sram
|
// read sram
|
||||||
save_read(rdram, rdram_address, physical_addr - sram_base, size);
|
recomp::save::read(rdram, rdram_address, physical_addr - sram_base, size);
|
||||||
|
|
||||||
// Send a message to the mq to indicate that the transfer completed
|
// Send a message to the mq to indicate that the transfer completed
|
||||||
osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK);
|
osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK);
|
||||||
|
|
@ -234,7 +115,7 @@ void do_dma(RDRAM_ARG PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_
|
||||||
throw std::runtime_error("ROM DMA write unimplemented");
|
throw std::runtime_error("ROM DMA write unimplemented");
|
||||||
} else if (physical_addr >= sram_base) {
|
} else if (physical_addr >= sram_base) {
|
||||||
// write sram
|
// write sram
|
||||||
save_write(rdram, rdram_address, physical_addr - sram_base, size);
|
recomp::save::write(rdram, rdram_address, physical_addr - sram_base, size);
|
||||||
|
|
||||||
// Send a message to the mq to indicate that the transfer completed
|
// Send a message to the mq to indicate that the transfer completed
|
||||||
osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK);
|
osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK);
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@
|
||||||
#include "ultramodern/ultramodern.hpp"
|
#include "ultramodern/ultramodern.hpp"
|
||||||
#include "ultramodern/error_handling.hpp"
|
#include "ultramodern/error_handling.hpp"
|
||||||
|
|
||||||
|
#include "librecomp/save.hpp"
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
inline uint32_t byteswap(uint32_t val) {
|
inline uint32_t byteswap(uint32_t val) {
|
||||||
return _byteswap_ulong(val);
|
return _byteswap_ulong(val);
|
||||||
|
|
@ -434,7 +436,7 @@ void recomp::start(
|
||||||
ultramodern::error_handling::message_box("Error opening stored ROM! Please restart this program.");
|
ultramodern::error_handling::message_box("Error opening stored ROM! Please restart this program.");
|
||||||
}
|
}
|
||||||
|
|
||||||
ultramodern::init_saving(rdram);
|
recomp::save::init(rdram);
|
||||||
|
|
||||||
auto find_it = game_roms.find(current_game.value());
|
auto find_it = game_roms.find(current_game.value());
|
||||||
const recomp::GameEntry& game_entry = find_it->second;
|
const recomp::GameEntry& game_entry = find_it->second;
|
||||||
|
|
@ -469,5 +471,5 @@ void recomp::start(
|
||||||
game_thread.join();
|
game_thread.join();
|
||||||
ultramodern::join_event_threads();
|
ultramodern::join_event_threads();
|
||||||
ultramodern::join_thread_cleaner_thread();
|
ultramodern::join_thread_cleaner_thread();
|
||||||
ultramodern::join_saving_thread();
|
recomp::save::join_thread();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
146
librecomp/src/save.cpp
Normal file
146
librecomp/src/save.cpp
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
#include <array>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "ultramodern/error_handling.hpp"
|
||||||
|
|
||||||
|
#include "librecomp/files.hpp"
|
||||||
|
#include "librecomp/game.hpp"
|
||||||
|
#include "librecomp/recomp.h"
|
||||||
|
#include "librecomp/save.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
struct {
|
||||||
|
std::array<char, 0x20000> save_buffer;
|
||||||
|
std::thread saving_thread;
|
||||||
|
moodycamel::LightweightSemaphore write_sempahore;
|
||||||
|
std::mutex save_buffer_mutex;
|
||||||
|
} save_context;
|
||||||
|
|
||||||
|
const std::u8string save_folder = u8"saves";
|
||||||
|
|
||||||
|
extern std::filesystem::path config_path;
|
||||||
|
|
||||||
|
std::filesystem::path get_save_file_path() {
|
||||||
|
return config_path / save_folder / (std::u8string{recomp::current_game_id()} + u8".bin");
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_save_file() {
|
||||||
|
bool saving_failed = false;
|
||||||
|
{
|
||||||
|
std::ofstream save_file = recomp::open_output_file_with_backup(get_save_file_path(), std::ios_base::binary);
|
||||||
|
|
||||||
|
if (save_file.good()) {
|
||||||
|
std::lock_guard lock{ save_context.save_buffer_mutex };
|
||||||
|
save_file.write(save_context.save_buffer.data(), save_context.save_buffer.size());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
saving_failed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!saving_failed) {
|
||||||
|
saving_failed = !recomp::finalize_output_file_with_backup(get_save_file_path());
|
||||||
|
}
|
||||||
|
if (saving_failed) {
|
||||||
|
ultramodern::error_handling::message_box("Failed to write to the save file. Check your file permissions and whether the save folder has been moved to Dropbox or similar, as this can cause issues.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern std::atomic_bool exited;
|
||||||
|
|
||||||
|
void saving_thread_func(RDRAM_ARG1) {
|
||||||
|
while (!exited) {
|
||||||
|
bool save_buffer_updated = false;
|
||||||
|
// Repeatedly wait for a new action to be sent.
|
||||||
|
constexpr int64_t wait_time_microseconds = 10000;
|
||||||
|
constexpr int max_actions = 128;
|
||||||
|
int num_actions = 0;
|
||||||
|
|
||||||
|
// Wait up to the given timeout for a write to come in. Allow multiple writes to coalesce together into a single save.
|
||||||
|
// Cap the number of coalesced writes to guarantee that the save buffer eventually gets written out to the file even if the game
|
||||||
|
// is constantly sending writes.
|
||||||
|
while (save_context.write_sempahore.wait(wait_time_microseconds) && num_actions < max_actions) {
|
||||||
|
save_buffer_updated = true;
|
||||||
|
num_actions++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an action came through that affected the save file, save the updated contents.
|
||||||
|
if (save_buffer_updated) {
|
||||||
|
update_save_file();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void recomp::save::write_ptr(const void* in, uint32_t offset, uint32_t count) {
|
||||||
|
assert(offset + count < save_context.save_buffer.size());
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard lock { save_context.save_buffer_mutex };
|
||||||
|
memcpy(&save_context.save_buffer[offset], in, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
save_context.write_sempahore.signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
void recomp::save::write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count) {
|
||||||
|
assert(offset + count < save_context.save_buffer.size());
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard lock { save_context.save_buffer_mutex };
|
||||||
|
for (uint32_t i = 0; i < count; i++) {
|
||||||
|
save_context.save_buffer[offset + i] = MEM_B(i, rdram_address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
save_context.write_sempahore.signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
void recomp::save::read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count) {
|
||||||
|
assert(offset + count < save_context.save_buffer.size());
|
||||||
|
|
||||||
|
std::lock_guard lock { save_context.save_buffer_mutex };
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
MEM_B(i, rdram_address) = save_context.save_buffer[offset + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void recomp::save::clear(uint32_t start, uint32_t size, char value) {
|
||||||
|
assert(start + size < save_context.save_buffer.size());
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard lock { save_context.save_buffer_mutex };
|
||||||
|
std::fill_n(save_context.save_buffer.begin() + start, size, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
save_context.write_sempahore.signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
void recomp::save::init(uint8_t *rdram) {
|
||||||
|
std::filesystem::path save_file_path = get_save_file_path();
|
||||||
|
|
||||||
|
// Ensure the save file directory exists.
|
||||||
|
std::filesystem::create_directories(save_file_path.parent_path());
|
||||||
|
|
||||||
|
// Read the save file if it exists.
|
||||||
|
std::ifstream save_file = recomp::open_input_file_with_backup(save_file_path, std::ios_base::binary);
|
||||||
|
if (save_file.good()) {
|
||||||
|
save_file.read(save_context.save_buffer.data(), save_context.save_buffer.size());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Otherwise clear the save file to all zeroes.
|
||||||
|
save_context.save_buffer.fill(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
save_context.saving_thread = std::thread{saving_thread_func, PASS_RDRAM};
|
||||||
|
}
|
||||||
|
|
||||||
|
void recomp::save::join_thread() {
|
||||||
|
if (save_context.saving_thread.joinable()) {
|
||||||
|
save_context.saving_thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -36,7 +36,6 @@ constexpr uint32_t save_size = 1024 * 1024 / 8; // Maximum save size, 1Mbit for
|
||||||
|
|
||||||
// Initialization.
|
// Initialization.
|
||||||
void preinit(RDRAM_ARG renderer::WindowHandle window_handle);
|
void preinit(RDRAM_ARG renderer::WindowHandle window_handle);
|
||||||
void init_saving(RDRAM_ARG1);
|
|
||||||
void init_events(RDRAM_ARG renderer::WindowHandle window_handle);
|
void init_events(RDRAM_ARG renderer::WindowHandle window_handle);
|
||||||
void init_timers(RDRAM_ARG1);
|
void init_timers(RDRAM_ARG1);
|
||||||
void init_thread_cleanup();
|
void init_thread_cleanup();
|
||||||
|
|
@ -124,7 +123,6 @@ bool is_game_started();
|
||||||
void quit();
|
void quit();
|
||||||
void join_event_threads();
|
void join_event_threads();
|
||||||
void join_thread_cleaner_thread();
|
void join_thread_cleaner_thread();
|
||||||
void join_saving_thread();
|
|
||||||
|
|
||||||
void set_audio_callbacks(const audio_callbacks_t& callbacks);
|
void set_audio_callbacks(const audio_callbacks_t& callbacks);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue