Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save cleanup #54

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions librecomp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ add_library(librecomp STATIC
"${CMAKE_CURRENT_SOURCE_DIR}/src/print.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/recomp.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/ultra_stubs.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/ultra_translation.cpp"
Expand Down
3 changes: 2 additions & 1 deletion librecomp/include/librecomp/game.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
#include <vector>
#include <filesystem>

#include "ultramodern/ultramodern.hpp"

#include "recomp.h"
#include "rsp.hpp"
#include <ultramodern/ultramodern.hpp>

namespace recomp {
struct GameEntry {
Expand Down
20 changes: 20 additions & 0 deletions librecomp/include/librecomp/save.hpp
Original file line number Diff line number Diff line change
@@ -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
24 changes: 11 additions & 13 deletions librecomp/src/flash.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#include <array>
#include <cassert>
#include <ultramodern/ultra64.h>
#include <ultramodern/ultramodern.hpp>

#include "ultramodern/ultra64.h"
#include "ultramodern/ultramodern.hpp"

#include "recomp.h"
#include "librecomp/save.hpp"

// TODO move this out into ultramodern code

Expand All @@ -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_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;

extern "C" void osFlashInit_recomp(uint8_t * rdram, recomp_context * ctx) {
Expand All @@ -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) {
save_clear(0, ultramodern::save_size, 0xFF);
recomp::save::clear(0, ultramodern::save_size, 0xFF);

ctx->r2 = 0;
}

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;
}
Expand All @@ -65,7 +63,7 @@ extern "C" void osFlashSectorErase_recomp(uint8_t * rdram, recomp_context * ctx)
return;
}

save_clear(page_num * page_size, page_size, 0xFF);
recomp::save::clear(page_num * page_size, page_size, 0xFF);

ctx->r2 = 0;
}
Expand All @@ -80,7 +78,7 @@ extern "C" void osFlashSectorEraseThrough_recomp(uint8_t * rdram, recomp_context
return;
}

save_clear(page_num * page_size, page_size, 0xFF);
recomp::save::clear(page_num * page_size, page_size, 0xFF);

ctx->r2 = 0;
}
Expand Down Expand Up @@ -111,7 +109,7 @@ extern "C" void osFlashWriteArray_recomp(uint8_t * rdram, recomp_context * ctx)
uint32_t page_num = ctx->r4;

// 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;
}
Expand All @@ -128,7 +126,7 @@ extern "C" void osFlashReadArray_recomp(uint8_t * rdram, recomp_context * ctx) {
uint32_t count = n_pages * page_size;

// 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
osSendMesg(PASS_RDRAM mq, 0, OS_MESG_NOBLOCK);
Expand Down
133 changes: 7 additions & 126 deletions librecomp/src/pi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
#include "recomp.h"
#include "game.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;

Expand Down Expand Up @@ -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++;
}

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) {
// TODO asynchronous transfer
// TODO implement unaligned DMA correctly
Expand All @@ -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);
} else if (physical_addr >= sram_base) {
// 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
osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK);
Expand All @@ -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");
} else if (physical_addr >= sram_base) {
// 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
osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK);
Expand Down
6 changes: 4 additions & 2 deletions librecomp/src/recomp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include "ultramodern/ultramodern.hpp"
#include "ultramodern/error_handling.hpp"

#include "librecomp/save.hpp"

#ifdef _MSC_VER
inline uint32_t byteswap(uint32_t val) {
return _byteswap_ulong(val);
Expand Down Expand Up @@ -434,7 +436,7 @@ void recomp::start(
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());
const recomp::GameEntry& game_entry = find_it->second;
Expand Down Expand Up @@ -469,5 +471,5 @@ void recomp::start(
game_thread.join();
ultramodern::join_event_threads();
ultramodern::join_thread_cleaner_thread();
ultramodern::join_saving_thread();
recomp::save::join_thread();
}
Loading