mirror of
https://github.com/N64Recomp/N64ModernRuntime.git
synced 2025-10-30 08:02:29 +00:00
Added save type selection and validation (#68)
This commit is contained in:
parent
9ee0e7369a
commit
ef6e84cb8a
5 changed files with 174 additions and 5 deletions
|
|
@ -9,12 +9,22 @@
|
|||
#include <ultramodern/ultramodern.hpp>
|
||||
|
||||
namespace recomp {
|
||||
enum class SaveType {
|
||||
None,
|
||||
Eep4k,
|
||||
Eep16k,
|
||||
Sram,
|
||||
Flashram,
|
||||
AllowAll, // Allows all save types to work and reports eeprom size as 16kbit.
|
||||
};
|
||||
|
||||
struct GameEntry {
|
||||
uint64_t rom_hash;
|
||||
std::string internal_name;
|
||||
std::u8string game_id;
|
||||
std::string mod_game_id;
|
||||
std::span<const char> cache_data;
|
||||
SaveType save_type = SaveType::None;
|
||||
bool is_enabled;
|
||||
|
||||
gpr entrypoint_address;
|
||||
|
|
@ -73,7 +83,6 @@ namespace recomp {
|
|||
* It must be called only once and it must be called before `ultramodern::preinit`.
|
||||
*/
|
||||
void start(
|
||||
uint32_t rdram_size,
|
||||
const Version& project_version,
|
||||
ultramodern::renderer::WindowHandle window_handle,
|
||||
const recomp::rsp::callbacks_t& rsp_callbacks,
|
||||
|
|
@ -86,6 +95,11 @@ namespace recomp {
|
|||
const ultramodern::threads::callbacks_t& threads_callbacks
|
||||
);
|
||||
|
||||
SaveType get_save_type();
|
||||
bool eeprom_allowed();
|
||||
bool sram_allowed();
|
||||
bool flashram_allowed();
|
||||
|
||||
void start_game(const std::u8string& game_id);
|
||||
std::u8string current_game_id();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "librecomp/recomp.h"
|
||||
#include "librecomp/game.hpp"
|
||||
|
||||
#include "ultramodern/ultra64.h"
|
||||
|
||||
|
|
@ -12,10 +13,23 @@ constexpr int eep16_size = 16384;
|
|||
constexpr int eep16_block_count = eep16_size / eeprom_block_size;
|
||||
|
||||
extern "C" void osEepromProbe_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
ctx->r2 = 0x02; // EEP16K
|
||||
switch (recomp::get_save_type()) {
|
||||
case recomp::SaveType::AllowAll:
|
||||
case recomp::SaveType::Eep16k:
|
||||
ctx->r2 = 0x02; // EEPROM_TYPE_16K
|
||||
case recomp::SaveType::Eep4k:
|
||||
ctx->r2 = 0x01; // EEPROM_TYPE_4K
|
||||
default:
|
||||
ctx->r2 = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void osEepromWrite_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
if (!recomp::eeprom_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use EEPROM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
uint8_t eep_address = ctx->r5;
|
||||
gpr buffer = ctx->r6;
|
||||
int32_t nbytes = eeprom_block_size;
|
||||
|
|
@ -29,6 +43,11 @@ extern "C" void osEepromWrite_recomp(uint8_t* rdram, recomp_context* ctx) {
|
|||
}
|
||||
|
||||
extern "C" void osEepromLongWrite_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
if (!recomp::eeprom_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use EEPROM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
uint8_t eep_address = ctx->r5;
|
||||
gpr buffer = ctx->r6;
|
||||
int32_t nbytes = ctx->r7;
|
||||
|
|
@ -42,6 +61,11 @@ extern "C" void osEepromLongWrite_recomp(uint8_t* rdram, recomp_context* ctx) {
|
|||
}
|
||||
|
||||
extern "C" void osEepromRead_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
if (!recomp::eeprom_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use EEPROM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
uint8_t eep_address = ctx->r5;
|
||||
gpr buffer = ctx->r6;
|
||||
int32_t nbytes = eeprom_block_size;
|
||||
|
|
@ -55,6 +79,11 @@ extern "C" void osEepromRead_recomp(uint8_t* rdram, recomp_context* ctx) {
|
|||
}
|
||||
|
||||
extern "C" void osEepromLongRead_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
if (!recomp::eeprom_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use EEPROM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
uint8_t eep_address = ctx->r5;
|
||||
gpr buffer = ctx->r6;
|
||||
int32_t nbytes = ctx->r7;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <ultramodern/ultramodern.hpp>
|
||||
#include "librecomp/recomp.h"
|
||||
#include "librecomp/addresses.hpp"
|
||||
#include "librecomp/game.hpp"
|
||||
|
||||
// TODO move this out into ultramodern code
|
||||
|
||||
|
|
@ -22,16 +23,31 @@ 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) {
|
||||
if (!recomp::flashram_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
ctx->r2 = recomp::flash_handle;
|
||||
}
|
||||
|
||||
extern "C" void osFlashReadStatus_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
if (!recomp::flashram_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
PTR(u8) flash_status = ctx->r4;
|
||||
|
||||
MEM_B(0, flash_status) = 0;
|
||||
}
|
||||
|
||||
extern "C" void osFlashReadId_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
if (!recomp::flashram_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
PTR(u32) flash_type = ctx->r4;
|
||||
PTR(u32) flash_maker = ctx->r5;
|
||||
|
||||
|
|
@ -41,16 +57,31 @@ extern "C" void osFlashReadId_recomp(uint8_t * rdram, recomp_context * ctx) {
|
|||
}
|
||||
|
||||
extern "C" void osFlashClearStatus_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
if (!recomp::flashram_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
extern "C" void osFlashAllErase_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
if (!recomp::flashram_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
save_clear(0, ultramodern::save_size, 0xFF);
|
||||
|
||||
ctx->r2 = 0;
|
||||
}
|
||||
|
||||
extern "C" void osFlashAllEraseThrough_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
if (!recomp::flashram_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
save_clear(0, ultramodern::save_size, 0xFF);
|
||||
|
||||
ctx->r2 = 0;
|
||||
|
|
@ -58,6 +89,11 @@ extern "C" void osFlashAllEraseThrough_recomp(uint8_t * rdram, recomp_context *
|
|||
|
||||
// This function is named sector but really means page.
|
||||
extern "C" void osFlashSectorErase_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
if (!recomp::flashram_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
uint32_t page_num = (uint32_t)ctx->r4;
|
||||
|
||||
// Prevent out of bounds erase
|
||||
|
|
@ -73,6 +109,11 @@ extern "C" void osFlashSectorErase_recomp(uint8_t * rdram, recomp_context * ctx)
|
|||
|
||||
// Same naming issue as above.
|
||||
extern "C" void osFlashSectorEraseThrough_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
if (!recomp::flashram_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
uint32_t page_num = (uint32_t)ctx->r4;
|
||||
|
||||
// Prevent out of bounds erase
|
||||
|
|
@ -87,11 +128,21 @@ extern "C" void osFlashSectorEraseThrough_recomp(uint8_t * rdram, recomp_context
|
|||
}
|
||||
|
||||
extern "C" void osFlashCheckEraseEnd_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
if (!recomp::flashram_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
// All erases are blocking in this implementation, so this should always return OK.
|
||||
ctx->r2 = 0; // FLASH_STATUS_ERASE_OK
|
||||
}
|
||||
|
||||
extern "C" void osFlashWriteBuffer_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
if (!recomp::flashram_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
OSIoMesg* mb = TO_PTR(OSIoMesg, ctx->r4);
|
||||
int32_t pri = ctx->r5;
|
||||
PTR(void) dramAddr = ctx->r6;
|
||||
|
|
@ -109,6 +160,11 @@ extern "C" void osFlashWriteBuffer_recomp(uint8_t * rdram, recomp_context * ctx)
|
|||
}
|
||||
|
||||
extern "C" void osFlashWriteArray_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
if (!recomp::flashram_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
uint32_t page_num = ctx->r4;
|
||||
|
||||
// Copy the write buffer into the save file
|
||||
|
|
@ -118,6 +174,11 @@ extern "C" void osFlashWriteArray_recomp(uint8_t * rdram, recomp_context * ctx)
|
|||
}
|
||||
|
||||
extern "C" void osFlashReadArray_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
if (!recomp::flashram_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
OSIoMesg* mb = TO_PTR(OSIoMesg, ctx->r4);
|
||||
int32_t pri = ctx->r5;
|
||||
uint32_t page_num = ctx->r6;
|
||||
|
|
@ -138,5 +199,10 @@ extern "C" void osFlashReadArray_recomp(uint8_t * rdram, recomp_context * ctx) {
|
|||
}
|
||||
|
||||
extern "C" void osFlashChange_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
if (!recomp::flashram_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
assert(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ void recomp::do_rom_pio(uint8_t* rdram, gpr ram_address, uint32_t physical_addr)
|
|||
}
|
||||
|
||||
struct {
|
||||
std::array<char, 0x20000> save_buffer;
|
||||
std::vector<char> save_buffer;
|
||||
std::thread saving_thread;
|
||||
moodycamel::LightweightSemaphore write_sempahore;
|
||||
std::mutex save_buffer_mutex;
|
||||
|
|
@ -143,6 +143,8 @@ void saving_thread_func(RDRAM_ARG1) {
|
|||
}
|
||||
|
||||
void 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);
|
||||
|
|
@ -152,6 +154,8 @@ 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) {
|
||||
assert(offset + count <= save_context.save_buffer.size());
|
||||
|
||||
{
|
||||
std::lock_guard lock { save_context.save_buffer_mutex };
|
||||
for (gpr i = 0; i < count; i++) {
|
||||
|
|
@ -163,6 +167,8 @@ void save_write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t cou
|
|||
}
|
||||
|
||||
void 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 (gpr i = 0; i < count; i++) {
|
||||
MEM_B(i, rdram_address) = save_context.save_buffer[offset + i];
|
||||
|
|
@ -170,6 +176,8 @@ void save_read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t coun
|
|||
}
|
||||
|
||||
void 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);
|
||||
|
|
@ -178,12 +186,31 @@ void save_clear(uint32_t start, uint32_t size, char value) {
|
|||
save_context.write_sempahore.signal();
|
||||
}
|
||||
|
||||
size_t get_save_size(recomp::SaveType save_type) {
|
||||
switch (save_type) {
|
||||
case recomp::SaveType::AllowAll:
|
||||
case recomp::SaveType::Flashram:
|
||||
return 0x20000;
|
||||
case recomp::SaveType::Sram:
|
||||
return 0x8000;
|
||||
case recomp::SaveType::Eep16k:
|
||||
return 0x800;
|
||||
case recomp::SaveType::Eep4k:
|
||||
return 0x200;
|
||||
case recomp::SaveType::None:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
save_context.save_buffer.resize(get_save_size(recomp::get_save_type()));
|
||||
|
||||
// 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()) {
|
||||
|
|
@ -191,7 +218,7 @@ void ultramodern::init_saving(RDRAM_ARG1) {
|
|||
}
|
||||
else {
|
||||
// Otherwise clear the save file to all zeroes.
|
||||
save_context.save_buffer.fill(0);
|
||||
std::fill(save_context.save_buffer.begin(), save_context.save_buffer.end(), 0);
|
||||
}
|
||||
|
||||
save_context.saving_thread = std::thread{saving_thread_func, PASS_RDRAM};
|
||||
|
|
@ -214,6 +241,10 @@ void do_dma(RDRAM_ARG PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_
|
|||
// Send a message to the mq to indicate that the transfer completed
|
||||
osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK);
|
||||
} else if (physical_addr >= recomp::sram_base) {
|
||||
if (!recomp::sram_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use SRAM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
// read sram
|
||||
save_read(rdram, rdram_address, physical_addr - recomp::sram_base, size);
|
||||
|
||||
|
|
@ -227,6 +258,10 @@ void do_dma(RDRAM_ARG PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_
|
|||
// write cart rom
|
||||
throw std::runtime_error("ROM DMA write unimplemented");
|
||||
} else if (physical_addr >= recomp::sram_base) {
|
||||
if (!recomp::sram_allowed()) {
|
||||
ultramodern::error_handling::message_box("Attempted to use SRAM saving with other save type");
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
// write sram
|
||||
save_write(rdram, rdram_address, physical_addr - recomp::sram_base, size);
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@ std::unordered_map<std::u8string, recomp::GameEntry> game_roms {};
|
|||
std::unique_ptr<recomp::mods::ModContext> mod_context = std::make_unique<recomp::mods::ModContext>();
|
||||
// The project's version.
|
||||
recomp::Version project_version;
|
||||
// The current game's save type.
|
||||
recomp::SaveType save_type = recomp::SaveType::None;
|
||||
|
||||
std::u8string recomp::GameEntry::stored_filename() const {
|
||||
return game_id + u8".z64";
|
||||
|
|
@ -547,6 +549,7 @@ bool wait_for_game_started(uint8_t* rdram, recomp_context* context) {
|
|||
|
||||
recomp::init_heap(rdram, recomp::mod_rdram_start + mod_ram_used);
|
||||
|
||||
save_type = game_entry.save_type;
|
||||
ultramodern::init_saving(rdram);
|
||||
ultramodern::load_shader_cache(game_entry.cache_data);
|
||||
|
||||
|
|
@ -566,8 +569,30 @@ bool wait_for_game_started(uint8_t* rdram, recomp_context* context) {
|
|||
}
|
||||
}
|
||||
|
||||
recomp::SaveType recomp::get_save_type() {
|
||||
return save_type;
|
||||
}
|
||||
|
||||
bool recomp::eeprom_allowed() {
|
||||
return
|
||||
save_type == SaveType::Eep4k ||
|
||||
save_type == SaveType::Eep16k ||
|
||||
save_type == SaveType::AllowAll;
|
||||
}
|
||||
|
||||
bool recomp::sram_allowed() {
|
||||
return
|
||||
save_type == SaveType::Sram ||
|
||||
save_type == SaveType::AllowAll;
|
||||
}
|
||||
|
||||
bool recomp::flashram_allowed() {
|
||||
return
|
||||
save_type == SaveType::Flashram ||
|
||||
save_type == SaveType::AllowAll;
|
||||
}
|
||||
|
||||
void recomp::start(
|
||||
uint32_t rdram_size,
|
||||
const recomp::Version& version,
|
||||
ultramodern::renderer::WindowHandle window_handle,
|
||||
const recomp::rsp::callbacks_t& rsp_callbacks,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue