Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
da54226
Implement controller pak functions
sonicdcer Jul 18, 2025
ed7db19
get rid of file_no in IsFileEmpty()
sonicdcer Jul 18, 2025
7e4a786
WIP C++ changes
sonicdcer Jul 20, 2025
118ad93
WIP C++ changes
sonicdcer Jul 20, 2025
b18f6b3
Refactor. State: OK
sonicdcer Jul 20, 2025
c964f29
quick fix
sonicdcer Jul 21, 2025
e48e7b5
fully working and tested with mk64 and <?>
sonicdcer Jul 21, 2025
bf84e07
add padding to OSPfsState
sonicdcer Jul 21, 2025
7361eba
forgot this check
sonicdcer Jul 21, 2025
19d26be
simplify code and fix DeleteFile bug
sonicdcer Jul 22, 2025
9a658b4
ARRAY_COUNT
sonicdcer Jul 22, 2025
c2a525e
Pak: int32_t queue
sonicdcer Jul 22, 2025
3322856
pak queue as int32_t
sonicdcer Jul 22, 2025
ece4870
forgot to fix osPfsInitPak_recomp
sonicdcer Jul 22, 2025
1834cef
Pak.cpp: cleanup
sonicdcer Jul 22, 2025
381e070
osPfsAllocateFile_recomp: add check for max files
sonicdcer Jul 23, 2025
257b5db
Rewrite VI functionality for higher accuracy
Mr-Wiseguy Aug 10, 2025
d34934a
Implement osSetTime and move update screen to before VI update
Mr-Wiseguy Aug 11, 2025
826afd1
Merge remote-tracking branch 'upstream/vi-fixes' into controllerpakimpl
sonicdcer Aug 12, 2025
c93aadd
update to vi-fixes
sonicdcer Aug 12, 2025
ff5daef
update n64recomp
sonicdcer Aug 12, 2025
48d4f28
Fix duke nukem zero hour
sonicdcer Nov 1, 2025
212c0cd
fix endianess in the OSPfsState struct
sonicdcer Nov 1, 2025
0a6fcc5
merge sonicdcer/controllerpakvifixes branch
gcsmith Jan 14, 2026
9818d28
WAR performance issue for now
gcsmith Jan 16, 2026
0fb6b85
rumblepak state tracking
gcsmith Jan 16, 2026
f5ba39e
stash work
gcsmith Jan 16, 2026
89923ed
track state properly
gcsmith Jan 16, 2026
c0a774f
Merge branch 'N64Recomp:main' into rumble_pak
gcsmith Jan 16, 2026
f00c630
Merge branch 'main' into controller_pak
gcsmith Jan 16, 2026
a078e4e
Merge branch 'rumble_pak' into controller_pak
gcsmith Jan 16, 2026
b86c022
Merge branch 'N64Recomp:main' into controller_pak
gcsmith Jan 16, 2026
29cc838
Merge branch 'N64Recomp:main' into controller_pak
gcsmith Jan 17, 2026
eff5044
Merge branch 'N64Recomp:main' into controller_pak
gcsmith Jan 17, 2026
c733dd7
Merge branch 'N64Recomp:main' into controller_pak
gcsmith Jan 17, 2026
1c798b1
move file APIs without changes
gcsmith Jan 18, 2026
b42d7bd
move save APIs without changes
gcsmith Jan 18, 2026
496639b
update code to reflect relocation
gcsmith Jan 18, 2026
8eaa832
pass gameid to init_saving()
gcsmith Jan 18, 2026
d990ef0
fix memory copies
gcsmith Jan 18, 2026
af3e3aa
add missing ultramodern::set_save_type
gcsmith Jan 18, 2026
127ac96
include <mutex> to fix windows build
gcsmith Jan 18, 2026
552a8cf
Merge branch 'save_api' into controller_pak
gcsmith Jan 18, 2026
b7f1e9c
support args >= 4 via the stack
gcsmith Jan 18, 2026
97ee622
Merge branch 'stack_args' into controller_pak
gcsmith Jan 18, 2026
6b5fd29
thunks and declarations
gcsmith Jan 19, 2026
b05625b
rename to nbytes for consistency
gcsmith Jan 19, 2026
69a2a78
fix offset
gcsmith Jan 19, 2026
f6d0a28
Merge branch 'stack_args' into controller_pak
gcsmith Jan 19, 2026
1760d27
add cast to gpr
gcsmith Jan 19, 2026
cb0a22c
modified controllerpak code
gcsmith Jan 20, 2026
8d997fc
cleanup + write pfs files to save directory
gcsmith Jan 23, 2026
7081cbb
initialize other OSPfs fields
gcsmith Jan 23, 2026
15ff1c0
rename
gcsmith Jan 23, 2026
315c696
remove byteswap in ultramodern
gcsmith Jan 23, 2026
e47b14e
cleanup
gcsmith Jan 23, 2026
7b9b172
remove <format> usage
gcsmith Jan 24, 2026
584494b
fix lazy mistake
gcsmith Jan 24, 2026
7f77d18
fix byteswapping
gcsmith Jan 24, 2026
e2a57d9
add alignment checks and clear header before read
gcsmith Jan 25, 2026
8ca6a9f
no magic numbers
gcsmith Jan 25, 2026
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
3 changes: 1 addition & 2 deletions librecomp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ add_library(librecomp STATIC
"${CMAKE_CURRENT_SOURCE_DIR}/src/eep.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/euc-jp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/extensions.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/files.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/flash.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/heap.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/math_routines.cpp"
Expand All @@ -23,8 +22,8 @@ add_library(librecomp STATIC
"${CMAKE_CURRENT_SOURCE_DIR}/src/mod_manifest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/mod_config_api.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/overlays.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/pak.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/patcher.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/pfs.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/pi.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/print.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/recomp.cpp"
Expand Down
16 changes: 2 additions & 14 deletions librecomp/include/librecomp/game.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,10 @@
#include "recomp.h"
#include "rsp.hpp"
#include <ultramodern/ultramodern.hpp>
#include <ultramodern/save.hpp>

namespace recomp {
enum class SaveType {
None,
Eep4k,
Eep16k,
Sram,
Flashram,
AllowAll, // Allows all save types to work and reports eeprom size as 16kbit.
};

using SaveType = ultramodern::SaveType;
struct GameEntry {
uint64_t rom_hash;
std::string internal_name;
Expand Down Expand Up @@ -109,11 +102,6 @@ namespace recomp {
///
void start(const Configuration& cfg);

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();
std::string current_mod_game_id();
Expand Down
22 changes: 20 additions & 2 deletions librecomp/include/librecomp/helpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
#include <ultramodern/ultra64.h>

template<int index, typename T>
T _arg(uint8_t* rdram, recomp_context* ctx) {
static_assert(index < 4, "Only args 0 through 3 supported");
T _arg(uint8_t* rdram, recomp_context* ctx) requires(index < 4) {
gpr raw_arg = (&ctx->r4)[index];
if constexpr (std::is_same_v<T, float>) {
if constexpr (index < 2) {
Expand Down Expand Up @@ -38,6 +37,25 @@ T _arg(uint8_t* rdram, recomp_context* ctx) {
}
}

template<int index, typename T>
T _arg(uint8_t* rdram, recomp_context* ctx) requires(index >= 4) {
const auto raw_arg = MEM_W(index * 4, ctx->r29);
if constexpr (std::is_pointer_v<T>) {
static_assert (!std::is_pointer_v<std::remove_pointer_t<T>>, "Double pointers not supported");
return TO_PTR(std::remove_pointer_t<T>, raw_arg);
}
else if constexpr (std::is_integral_v<T>) {
static_assert(sizeof(T) <= 4, "64-bit args not supported");
return static_cast<T>(raw_arg);
}
else {
// static_assert in else workaround
[] <bool flag = false>() {
static_assert(flag, "Unsupported type");
}();
}
}

inline float _arg_float_a1(uint8_t* rdram, recomp_context* ctx) {
(void)rdram;
union {
Expand Down
47 changes: 31 additions & 16 deletions librecomp/src/eep.cpp
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
#include "recomp.h"
#include "librecomp/game.hpp"

#include "ultramodern/ultra64.h"

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);
#include <ultramodern/save.hpp>
#include <ultramodern/ultra64.h>

constexpr int eeprom_block_size = 8;
static std::vector<uint8_t> save_buffer;

extern "C" void osEepromProbe_recomp(uint8_t* rdram, recomp_context* ctx) {
switch (recomp::get_save_type()) {
case recomp::SaveType::AllowAll:
case recomp::SaveType::Eep16k:
switch (ultramodern::get_save_type()) {
case ultramodern::SaveType::AllowAll:
case ultramodern::SaveType::Eep16k:
ctx->r2 = 0x02; // EEPROM_TYPE_16K
break;
case recomp::SaveType::Eep4k:
case ultramodern::SaveType::Eep4k:
ctx->r2 = 0x01; // EEPROM_TYPE_4K
break;
default:
Expand All @@ -24,7 +23,7 @@ extern "C" void osEepromProbe_recomp(uint8_t* rdram, recomp_context* ctx) {
}

extern "C" void osEepromWrite_recomp(uint8_t* rdram, recomp_context* ctx) {
if (!recomp::eeprom_allowed()) {
if (!ultramodern::eeprom_allowed()) {
ultramodern::error_handling::message_box("Attempted to use EEPROM saving with other save type");
ULTRAMODERN_QUICK_EXIT();
}
Expand All @@ -33,13 +32,17 @@ extern "C" void osEepromWrite_recomp(uint8_t* rdram, recomp_context* ctx) {
gpr buffer = ctx->r6;
int32_t nbytes = eeprom_block_size;

save_write(rdram, buffer, eep_address * eeprom_block_size, nbytes);
save_buffer.resize(nbytes);
for (uint32_t i = 0; i < nbytes; i++) {
save_buffer[i] = MEM_B(i, buffer);
}
ultramodern::save_write_ptr(save_buffer.data(), eep_address * eeprom_block_size, nbytes);

ctx->r2 = 0;
}

extern "C" void osEepromLongWrite_recomp(uint8_t* rdram, recomp_context* ctx) {
if (!recomp::eeprom_allowed()) {
if (!ultramodern::eeprom_allowed()) {
ultramodern::error_handling::message_box("Attempted to use EEPROM saving with other save type");
ULTRAMODERN_QUICK_EXIT();
}
Expand All @@ -50,13 +53,17 @@ extern "C" void osEepromLongWrite_recomp(uint8_t* rdram, recomp_context* ctx) {

assert((nbytes % eeprom_block_size) == 0);

save_write(rdram, buffer, eep_address * eeprom_block_size, nbytes);
save_buffer.resize(nbytes);
for (uint32_t i = 0; i < nbytes; i++) {
save_buffer[i] = MEM_B(i, buffer);
}
ultramodern::save_write_ptr(save_buffer.data(), eep_address * eeprom_block_size, nbytes);

ctx->r2 = 0;
}

extern "C" void osEepromRead_recomp(uint8_t* rdram, recomp_context* ctx) {
if (!recomp::eeprom_allowed()) {
if (!ultramodern::eeprom_allowed()) {
ultramodern::error_handling::message_box("Attempted to use EEPROM saving with other save type");
ULTRAMODERN_QUICK_EXIT();
}
Expand All @@ -65,13 +72,17 @@ extern "C" void osEepromRead_recomp(uint8_t* rdram, recomp_context* ctx) {
gpr buffer = ctx->r6;
int32_t nbytes = eeprom_block_size;

save_read(rdram, buffer, eep_address * eeprom_block_size, nbytes);
save_buffer.resize(nbytes);
ultramodern::save_read_ptr(save_buffer.data(), eep_address * eeprom_block_size, nbytes);
for (uint32_t i = 0; i < nbytes; i++) {
MEM_B(i, buffer) = save_buffer[i];
}

ctx->r2 = 0;
}

extern "C" void osEepromLongRead_recomp(uint8_t* rdram, recomp_context* ctx) {
if (!recomp::eeprom_allowed()) {
if (!ultramodern::eeprom_allowed()) {
ultramodern::error_handling::message_box("Attempted to use EEPROM saving with other save type");
ULTRAMODERN_QUICK_EXIT();
}
Expand All @@ -82,7 +93,11 @@ extern "C" void osEepromLongRead_recomp(uint8_t* rdram, recomp_context* ctx) {

assert((nbytes % eeprom_block_size) == 0);

save_read(rdram, buffer, eep_address * eeprom_block_size, nbytes);
save_buffer.resize(nbytes);
ultramodern::save_read_ptr(save_buffer.data(), eep_address * eeprom_block_size, nbytes);
for (uint32_t i = 0; i < nbytes; i++) {
MEM_B(i, buffer) = save_buffer[i];
}

ctx->r2 = 0;
}
49 changes: 25 additions & 24 deletions librecomp/src/flash.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <array>
#include <cassert>
#include <ultramodern/save.hpp>
#include <ultramodern/ultra64.h>
#include <ultramodern/ultramodern.hpp>
#include "recomp.h"
Expand All @@ -15,15 +16,11 @@ 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;
std::vector<uint8_t> save_buffer;

extern "C" void osFlashInit_recomp(uint8_t * rdram, recomp_context * ctx) {
if (!recomp::flashram_allowed()) {
if (!ultramodern::flashram_allowed()) {
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
ULTRAMODERN_QUICK_EXIT();
}
Expand All @@ -32,7 +29,7 @@ extern "C" void osFlashInit_recomp(uint8_t * rdram, recomp_context * ctx) {
}

extern "C" void osFlashReadStatus_recomp(uint8_t * rdram, recomp_context * ctx) {
if (!recomp::flashram_allowed()) {
if (!ultramodern::flashram_allowed()) {
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
ULTRAMODERN_QUICK_EXIT();
}
Expand All @@ -43,7 +40,7 @@ extern "C" void osFlashReadStatus_recomp(uint8_t * rdram, recomp_context * ctx)
}

extern "C" void osFlashReadId_recomp(uint8_t * rdram, recomp_context * ctx) {
if (!recomp::flashram_allowed()) {
if (!ultramodern::flashram_allowed()) {
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
ULTRAMODERN_QUICK_EXIT();
}
Expand All @@ -57,7 +54,7 @@ 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()) {
if (!ultramodern::flashram_allowed()) {
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
ULTRAMODERN_QUICK_EXIT();
}
Expand All @@ -66,30 +63,30 @@ extern "C" void osFlashClearStatus_recomp(uint8_t * rdram, recomp_context * ctx)
}

extern "C" void osFlashAllErase_recomp(uint8_t * rdram, recomp_context * ctx) {
if (!recomp::flashram_allowed()) {
if (!ultramodern::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);
ultramodern::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()) {
if (!ultramodern::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);
ultramodern::save_clear(0, ultramodern::save_size, 0xFF);

ctx->r2 = 0;
}

// This function is named sector but really means page.
extern "C" void osFlashSectorErase_recomp(uint8_t * rdram, recomp_context * ctx) {
if (!recomp::flashram_allowed()) {
if (!ultramodern::flashram_allowed()) {
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
ULTRAMODERN_QUICK_EXIT();
}
Expand All @@ -102,14 +99,14 @@ extern "C" void osFlashSectorErase_recomp(uint8_t * rdram, recomp_context * ctx)
return;
}

save_clear(page_num * page_size, page_size, 0xFF);
ultramodern::save_clear(page_num * page_size, page_size, 0xFF);

ctx->r2 = 0;
}

// Same naming issue as above.
extern "C" void osFlashSectorEraseThrough_recomp(uint8_t * rdram, recomp_context * ctx) {
if (!recomp::flashram_allowed()) {
if (!ultramodern::flashram_allowed()) {
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
ULTRAMODERN_QUICK_EXIT();
}
Expand All @@ -122,13 +119,13 @@ extern "C" void osFlashSectorEraseThrough_recomp(uint8_t * rdram, recomp_context
return;
}

save_clear(page_num * page_size, page_size, 0xFF);
ultramodern::save_clear(page_num * page_size, page_size, 0xFF);

ctx->r2 = 0;
}

extern "C" void osFlashCheckEraseEnd_recomp(uint8_t * rdram, recomp_context * ctx) {
if (!recomp::flashram_allowed()) {
if (!ultramodern::flashram_allowed()) {
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
ULTRAMODERN_QUICK_EXIT();
}
Expand All @@ -138,7 +135,7 @@ extern "C" void osFlashCheckEraseEnd_recomp(uint8_t * rdram, recomp_context * ct
}

extern "C" void osFlashWriteBuffer_recomp(uint8_t * rdram, recomp_context * ctx) {
if (!recomp::flashram_allowed()) {
if (!ultramodern::flashram_allowed()) {
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
ULTRAMODERN_QUICK_EXIT();
}
Expand All @@ -160,21 +157,21 @@ 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()) {
if (!ultramodern::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
save_write_ptr(write_buffer.data(), page_num * page_size, page_size);
ultramodern::save_write_ptr(write_buffer.data(), page_num * page_size, page_size);

ctx->r2 = 0;
}

extern "C" void osFlashReadArray_recomp(uint8_t * rdram, recomp_context * ctx) {
if (!recomp::flashram_allowed()) {
if (!ultramodern::flashram_allowed()) {
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
ULTRAMODERN_QUICK_EXIT();
}
Expand All @@ -190,7 +187,11 @@ 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);
save_buffer.resize(count);
ultramodern::save_read_ptr(save_buffer.data(), offset, count);
for (uint32_t i = 0; i < count; i++) {
MEM_B(i, dramAddr) = save_buffer[i];
}

// Send the message indicating read completion
ultramodern::enqueue_external_message_src(mq, 0, false, ultramodern::EventMessageSource::Pi);
Expand All @@ -199,7 +200,7 @@ 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()) {
if (!ultramodern::flashram_allowed()) {
ultramodern::error_handling::message_box("Attempted to use FlashRAM saving with other save type");
ULTRAMODERN_QUICK_EXIT();
}
Expand Down
4 changes: 2 additions & 2 deletions librecomp/src/mod_manifest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
#include "json/json.hpp"

#include "recompiler/context.h"
#include "librecomp/files.hpp"
#include "librecomp/mods.hpp"
#include <ultramodern/files.hpp>

static bool read_json(std::ifstream input_file, nlohmann::json &json_out) {
if (!input_file.good()) {
Expand All @@ -27,7 +27,7 @@ static bool read_json_with_backups(const std::filesystem::path &path, nlohmann::
}

// Try reading and parsing the backup file.
if (read_json(recomp::open_input_backup_file(path), json_out)) {
if (read_json(ultramodern::open_input_backup_file(path), json_out)) {
return true;
}

Expand Down
Loading
Loading