summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSonic Dreamcaster <alejandro.asenjo88@gmail.com>2025-07-23 16:04:31 -0300
committerSonic Dreamcaster <alejandro.asenjo88@gmail.com>2025-07-23 16:07:59 -0300
commit55a0b79bfc001ccb801927010b1c25e2d7782ea9 (patch)
tree8a060afa9b394181e62d2c4dd29cb2b7992205b1
parentf9019a2548f51c725557f2495e9dc1791199ffd6 (diff)
Better controller pak with File deletion menubettercontrollerpak
Co-authored-by: Dario <dariosamo@gmail.com> Co-authored-by: Mr-Wiseguy <mrwiseguyromhacking@gmail.com>
-rw-r--r--src/engine/Rulesets.h22
-rw-r--r--src/port/pak.cpp371
-rw-r--r--src/save.c9
-rw-r--r--src/stubs.c81
4 files changed, 387 insertions, 96 deletions
diff --git a/src/engine/Rulesets.h b/src/engine/Rulesets.h
index 26e67b594..eba4d4795 100644
--- a/src/engine/Rulesets.h
+++ b/src/engine/Rulesets.h
@@ -1,11 +1,11 @@
-#pragma once
-
-#include <libultraship.h>
-#include "World.h"
-
-class Rulesets {
-public:
- virtual void PreLoad();
- virtual void PreInit();
- virtual void PostInit();
-};
+#pragma once
+
+#include <libultraship.h>
+#include "World.h"
+
+class Rulesets {
+public:
+ virtual void PreLoad();
+ virtual void PreInit();
+ virtual void PostInit();
+};
diff --git a/src/port/pak.cpp b/src/port/pak.cpp
new file mode 100644
index 000000000..560f4b4f5
--- /dev/null
+++ b/src/port/pak.cpp
@@ -0,0 +1,371 @@
+#include <filesystem>
+#include <fstream>
+
+#include <libultraship.h>
+#include <libultraship/libultra.h>
+#include <save.h>
+
+#define MAX_FILES 16
+#define EXT_NAME_SIZE 4
+#define GAME_NAME_SIZE 16
+
+typedef struct ControllerPak {
+ std::fstream header;
+ std::fstream file;
+} ControllerPak;
+
+void Pfs_PakHeader_Write(u32* file_size, u32* game_code, u16* company_code, u8* ext_name, u8* game_name, u8 fileIndex) {
+ ControllerPak pak;
+
+ pak.header.open("controllerPak_header.sav", std::ios::binary | std::ios::in | std::ios::out);
+
+ if (!pak.header.good()) {
+ assert(false);
+ }
+ if (!pak.header.is_open()) {
+ assert(false);
+ }
+
+ /* Set file parameters to header */
+ u32 seek = fileIndex * sizeof(OSPfsState);
+
+ // file_size
+ pak.header.seekp(seek + 0x0, std::ios::beg);
+ pak.header.write((char*) file_size, 4);
+ // game_code
+ pak.header.seekp(seek + 0x4, std::ios::beg);
+ pak.header.write((char*) game_code, 4);
+ // company_code
+ pak.header.seekp(seek + 0x08, std::ios::beg);
+ pak.header.write((char*) company_code, 2);
+ // ext_name
+ pak.header.seekp(seek + 0x0C, std::ios::beg);
+ pak.header.write((char*) ext_name, EXT_NAME_SIZE);
+ // game_name
+ pak.header.seekp(seek + 0x10, std::ios::beg);
+ pak.header.write((char*) game_name, GAME_NAME_SIZE);
+
+ pak.header.close();
+}
+
+void Pfs_PakHeader_Read(u32* file_size, u32* game_code, u16* company_code, char* ext_name, char* game_name,
+ u8 fileIndex) {
+ ControllerPak pak;
+
+ pak.header.open("controllerPak_header.sav", std::ios::binary | std::ios::in | std::ios::out);
+
+ if (!pak.header.good()) {
+ assert(false);
+ }
+ if (!pak.header.is_open()) {
+ assert(false);
+ }
+
+ /* Set file parameters to header */
+ u32 seek = fileIndex * sizeof(OSPfsState);
+
+ // file_size
+ pak.header.seekg(seek + 0x0, std::ios::beg);
+ pak.header.read((char*) file_size, 4);
+ // game_code
+ pak.header.seekg(seek + 0x4, std::ios::beg);
+ pak.header.read((char*) game_code, 4);
+ // company_code
+ pak.header.seekg(seek + 0x08, std::ios::beg);
+ pak.header.read((char*) company_code, 2);
+ // ext_name
+ pak.header.seekg(seek + 0x0C, std::ios::beg);
+ pak.header.read((char*) ext_name, EXT_NAME_SIZE);
+ // game_name
+ pak.header.seekg(seek + 0x10, std::ios::beg);
+ pak.header.read((char*) game_name, GAME_NAME_SIZE);
+
+ pak.header.close();
+}
+
+extern "C" s32 osPfsIsPlug(OSMesgQueue* queue, u8* pattern) {
+ *pattern = 1;
+ return PFS_NO_ERROR;
+}
+
+extern "C" s32 osPfsInit(OSMesgQueue* queue, OSPfs* pfs, int channel) {
+ pfs->queue = queue;
+ pfs->channel = channel;
+ pfs->status = PFS_INITIALIZED;
+
+ ControllerPak pak;
+
+ // If a header file doesn't exist, create it.
+ if (!std::filesystem::exists("controllerPak_header.sav")) {
+ pak.header.open("controllerPak_header.sav", std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc);
+ pak.header.close();
+ }
+
+ return PFS_NO_ERROR;
+}
+
+extern "C" s32 osPfsFreeBlocks(OSPfs* pfs, s32* bytes_not_used) {
+ ControllerPak pak;
+
+ pak.header.open("controllerPak_header.sav", std::ios::binary | std::ios::in | std::ios::out);
+
+ if (!pak.header.good()) {
+ assert(false);
+ }
+ if (!pak.header.is_open()) {
+ assert(false);
+ }
+
+ s32 usedSpace = 0;
+ for (size_t i = 0; i < MAX_FILES; i++) {
+ u32 file_size = 0;
+ u32 game_code = 0;
+ u16 company_code = 0;
+ char ext_name[EXT_NAME_SIZE] = { 0 };
+ char game_name[GAME_NAME_SIZE] = { 0 };
+
+ Pfs_PakHeader_Read(&file_size, &game_code, &company_code, ext_name, game_name, i);
+
+ if ((company_code == 0) || (game_code == 0)) {
+ continue;
+ } else {
+ usedSpace += file_size >> 8;
+ }
+ }
+
+ pak.header.close();
+
+ *bytes_not_used = (123 - usedSpace) << 8;
+
+ return PFS_NO_ERROR;
+}
+
+extern "C" s32 osPfsAllocateFile(OSPfs* pfs, u16 company_code, u32 game_code, u8* game_name, u8* ext_name,
+ int file_size_in_bytes, s32* file_no) {
+
+ if ((company_code == 0) || (game_code == 0)) {
+ return PFS_ERR_INVALID;
+ }
+
+ ControllerPak pak;
+
+ /* Search for a free slot */
+ u8 freeFileIndex = 0;
+ for (size_t i = 0; i < MAX_FILES; i++) {
+ u32 file_size_ = 0;
+ u32 game_code_ = 0;
+ u16 company_code_ = 0;
+ char ext_name_[EXT_NAME_SIZE] = { 0 };
+ char game_name_[GAME_NAME_SIZE] = { 0 };
+
+ Pfs_PakHeader_Read(&file_size_, &game_code_, &company_code_, ext_name_, game_name_, i);
+
+ if ((company_code_ == 0) || (game_code_ == 0)) {
+ freeFileIndex = i;
+ break;
+ }
+ }
+
+ if (freeFileIndex == MAX_FILES) {
+ return PFS_DIR_FULL;
+ }
+
+ Pfs_PakHeader_Write((u32*) &file_size_in_bytes, &game_code, &company_code, ext_name, game_name, freeFileIndex);
+
+ /* Create empty file */
+ char filename[100];
+ sprintf(filename, "controllerPak_file_%d.sav", freeFileIndex);
+ pak.file.open(filename, std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc);
+
+ file_size_in_bytes = (file_size_in_bytes + 31) & ~31;
+
+ char* zero_block = (char*) malloc(file_size_in_bytes);
+ for (size_t i = 0; i < file_size_in_bytes; i++) {
+ zero_block[i] = 0;
+ }
+
+ pak.file.seekp(0, std::ios::beg);
+ pak.file.write(zero_block, file_size_in_bytes);
+
+ free(zero_block);
+
+ pak.file.close();
+
+ *file_no = freeFileIndex;
+
+ return PFS_NO_ERROR;
+}
+
+extern "C" s32 osPfsFileState(OSPfs* pfs, s32 file_no, OSPfsState* state) {
+ u32 file_size = 0;
+ u32 game_code = 0;
+ u16 company_code = 0;
+ char ext_name[EXT_NAME_SIZE] = { 0 };
+ char game_name[GAME_NAME_SIZE] = { 0 };
+
+ // should pass the state of the requested file_no to the incoming state pointer,
+ // games call this function 16 times, once per file
+ // fills the incoming state with the information inside the header of the pak.
+
+ char filename[100];
+ sprintf(filename, "controllerPak_file_%d.sav", file_no);
+ if (!std::filesystem::exists(filename)) {
+ return PFS_ERR_INVALID;
+ }
+
+ /* Read game info from pak */
+ Pfs_PakHeader_Read(&file_size, &game_code, &company_code, ext_name, game_name, file_no);
+
+ state->file_size = file_size;
+ state->company_code = game_code;
+ state->game_code = game_code;
+
+ for (size_t j = 0; j < GAME_NAME_SIZE; j++) {
+ state->game_name[j] = game_name[j];
+ }
+ for (size_t j = 0; j < EXT_NAME_SIZE; j++) {
+ state->ext_name[j] = ext_name[j];
+ }
+
+ return PFS_NO_ERROR;
+}
+
+extern "C" s32 osPfsFindFile(OSPfs* pfs, u16 company_code, u32 game_code, u8* game_name, u8* ext_name, s32* file_no) {
+ ControllerPak pak;
+
+ for (size_t i = 0; i < MAX_FILES; i++) {
+ u32 file_size_ = 0;
+ u32 game_code_ = 0;
+ u16 company_code_ = 0;
+ char ext_name_[EXT_NAME_SIZE] = { 0 };
+ char game_name_[GAME_NAME_SIZE] = { 0 };
+
+ Pfs_PakHeader_Read(&file_size_, &game_code_, &company_code_, ext_name_, game_name_, i);
+
+ if ((company_code_ == 0) || (game_code_ == 0)) {
+ continue;
+ } else {
+ if ((game_code == game_code_) && (company_code == company_code_) &&
+ (strcmp((const char*) game_name, (const char*) game_name_) == 0) &&
+ strcmp((const char*) ext_name, (const char*) ext_name_) == 0) {
+ // File found
+ *file_no = i;
+ return PFS_NO_ERROR;
+ }
+ }
+ }
+
+ // File not found
+ return PFS_ERR_INVALID;
+}
+
+extern "C" s32 osPfsReadWriteFile(OSPfs* pfs, s32 file_no, u8 flag, int offset, int size_in_bytes, u8* data_buffer) {
+ ControllerPak pak;
+
+ char filename[100];
+ sprintf(filename, "controllerPak_file_%d.sav", file_no);
+ pak.file.open(filename, std::ios::binary | std::ios::in | std::ios::out);
+
+ if (!std::filesystem::exists(filename)) {
+ return PFS_ERR_INVALID;
+ }
+ if (!pak.file.good()) {
+ return PFS_ERR_INVALID;
+ }
+ if (!pak.file.is_open()) {
+ return PFS_ERR_INVALID;
+ }
+
+ if (flag == 0) {
+ pak.file.seekg(offset, std::ios::beg);
+ pak.file.read((char*) data_buffer, size_in_bytes);
+ } else {
+ pak.file.seekp(offset, std::ios::beg);
+ pak.file.write((char*) data_buffer, size_in_bytes);
+ }
+
+ pak.file.close();
+
+ return PFS_NO_ERROR;
+}
+
+extern "C" s32 osPfsNumFiles(OSPfs* pfs, s32* max_files, s32* files_used) {
+ u8 files = 0;
+ for (size_t i = 0; i < MAX_FILES; i++) {
+ u32 file_size = 0;
+ u32 game_code = 0;
+ u16 company_code = 0;
+ char ext_name[EXT_NAME_SIZE] = { 0 };
+ char game_name[GAME_NAME_SIZE] = { 0 };
+
+ Pfs_PakHeader_Read(&file_size, &game_code, &company_code, ext_name, game_name, i);
+
+ if ((company_code != 0) || (game_code != 0)) {
+ files++;
+ }
+ }
+
+ *files_used = files;
+ *max_files = MAX_FILES;
+
+ return PFS_NO_ERROR;
+}
+
+extern "C" s32 osPfsDeleteFile(OSPfs* pfs, u16 company_code, u32 game_code, u8* game_name, u8* ext_name) {
+ if (company_code == 0 || game_code == 0) {
+ return PFS_ERR_INVALID;
+ }
+
+ ControllerPak pak;
+
+ for (int i = 0; i < MAX_FILES; i++) {
+ u32 file_size_ = 0;
+ u32 game_code_ = 0;
+ u16 company_code_ = 0;
+ char ext_name_[4] = { 0 };
+ char game_name_[16] = { 0 };
+
+ Pfs_PakHeader_Read(&file_size_, &game_code_, &company_code_, ext_name_, game_name_, i);
+
+ if ((company_code_ == 0) || (game_code_ == 0)) {
+ continue;
+ } else {
+ if ((game_code == game_code_) && (strcmp((const char*) game_name, (const char*) game_name_) == 0) &&
+ strcmp((const char*) ext_name, (const char*) ext_name_) == 0) {
+ // File found
+
+ pak.header.open("controllerPak_header.sav", std::ios::binary | std::ios::in | std::ios::out);
+
+ if (!pak.header.good()) {
+ assert(false);
+ }
+ if (!pak.header.is_open()) {
+ assert(false);
+ }
+
+ u32 seek = i * sizeof(OSPfsState);
+
+ // Zero out the header for this file.
+ u8* zero_block = (u8*) malloc(sizeof(OSPfsState));
+ for (size_t i = 0; i < sizeof(OSPfsState); i++) {
+ zero_block[i] = 0;
+ }
+ pak.header.seekp(seek + 0x0, std::ios::beg);
+ pak.header.write((char*) zero_block, sizeof(OSPfsState));
+
+ free(zero_block);
+
+ pak.header.close();
+
+ char filename[100];
+ sprintf(filename, "controllerPak_file_%d.sav", i);
+ remove(filename);
+
+ return PFS_NO_ERROR;
+ }
+ }
+ }
+
+ // File not found
+ return PFS_ERR_INVALID;
+}
diff --git a/src/save.c b/src/save.c
index abba01a42..10f3e332e 100644
--- a/src/save.c
+++ b/src/save.c
@@ -33,7 +33,9 @@ s8 sControllerPak2State = BAD;
// default time trial records in little endian form
const u8 D_800F2E60[4] = { 0xc0, 0x27, 0x09, 0x00 };
// osPfsFindFile -> gGameName ("MARIOKART64" in nosFont)
-const u8 gGameName[] = "MARIOKART64";
+const u8 gGameName[] = {
+ 0x26, 0x1a, 0x2b, 0x22, 0x28, 0x24, 0x1a, 0x2b, 0x2d, 0x16, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00
+};
// ext_name param to osPfsFindFile (four total bytes, but only one is setable)
const u8 gExtCode[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
@@ -867,9 +869,8 @@ s32 func_800B64EC(s32 arg0) {
}
sPlayerGhostReplay = (u32*) &D_802BFB80.arraySize8[0][D_80162DC8][3];
- temp_v0 =
- osPfsReadWriteFile(&gControllerPak1FileHandle, gControllerPak1FileNote, PFS_READ,
- (arg0 * (sizeof(u8) * 0x1000)) + 0x100, sizeof(u8) * 0x1000, (u8*) sPlayerGhostReplay);
+ temp_v0 = osPfsReadWriteFile(&gControllerPak1FileHandle, gControllerPak1FileNote, PFS_READ,
+ (arg0 * (sizeof(u8) * 0x1000)) + 0x100, sizeof(u8) * 0x1000, (u8*) sPlayerGhostReplay);
if (temp_v0 == 0) {
// clang-format off
phi_s1 = (u8 *) &D_8018EE10[arg0]; temp_s0 = 0; while (1) {
diff --git a/src/stubs.c b/src/stubs.c
index eefbb7d78..11a7b74a1 100644
--- a/src/stubs.c
+++ b/src/stubs.c
@@ -63,84 +63,3 @@ void osSpTaskYield(void) {
OSYieldResult osSpTaskYielded(OSTask* task) {
}
-
-s32 osPfsDeleteFile(OSPfs* pfs, u16 company_code, u32 game_code, u8* game_name, u8* ext_name) {
- char filename[1024];
- sprintf(filename, "channel_%d_%hu_%hd_%s.sav", pfs->channel, company_code, game_code, game_name);
- remove(filename);
- return PFS_NO_ERROR;
-}
-
-s32 osPfsReadWriteFile(OSPfs* pfs, s32 file_no, u8 flag, int offset, int size_in_bytes, u8* data_buffer) {
- if (flag == PFS_READ) {
- fseek(openFile[file_no].file, offset, SEEK_SET);
- fread(data_buffer, size_in_bytes, 1, openFile[file_no].file);
- } else {
- fseek(openFile[file_no].file, offset, SEEK_SET);
- fwrite(data_buffer, size_in_bytes, 1, openFile[file_no].file);
- }
- return PFS_NO_ERROR;
-}
-
-s32 osPfsAllocateFile(OSPfs* pfs, u16 company_code, u32 game_code, u8* game_name, u8* ext_name, int file_size_in_bytes,
- s32* file_no) {
- char filename[1024];
- sprintf(filename, "channel_%d_%hu_%hd_%s.sav", pfs->channel, company_code, game_code, game_name);
- *file_no = fileIndex++;
- openFile[*file_no].file = fopen(filename, "w+");
- fwrite("\0", 1, file_size_in_bytes, openFile[*file_no].file);
- openFile[*file_no].state.company_code = company_code;
- openFile[*file_no].state.game_code = game_code;
- strcpy(openFile[*file_no].state.game_name, game_name);
- strcpy(openFile[*file_no].state.ext_name, ext_name);
- return PFS_NO_ERROR;
-}
-
-s32 osPfsIsPlug(OSMesgQueue* queue, u8* pattern) {
- *pattern = 1;
- return 1;
-}
-
-s32 osPfsInit(OSMesgQueue* queue, OSPfs* pfs, int channel) {
- pfs->queue = queue;
- pfs->channel = channel;
- pfs->status = 0;
- pfs->status |= PFS_INITIALIZED;
- return PFS_NO_ERROR;
-}
-
-s32 osPfsNumFiles(OSPfs* pfs, s32* max_files, s32* files_used) {
- *max_files = 16;
- *files_used = fileIndex;
- return 0;
-}
-
-s32 osPfsFileState(OSPfs* pfs, s32 file_no, OSPfsState* state) {
-}
-
-s32 osPfsFreeBlocks(OSPfs* pfs, s32* bytes_not_used) {
- *bytes_not_used = 0x1000;
- return PFS_NO_ERROR;
-}
-
-s32 osPfsFindFile(OSPfs* pfs, u16 company_code, u32 game_code, u8* game_name, u8* ext_name, s32* file_no) {
- char filename[1024];
- sprintf(filename, "channel_%d_%hu_%hd_%s.sav", pfs->channel, company_code, game_code, game_name);
- for (size_t i = 0; i < 16; i++) {
- if (openFile[i].state.game_code == game_code && openFile[i].state.company_code == company_code &&
- strcmp(openFile[i].state.game_name, game_name) == 0 && strcmp(openFile[i].state.ext_name, ext_name) == 0) {
- *file_no = i;
- return PFS_NO_ERROR;
- }
- }
- *file_no = fileIndex++;
- openFile[*file_no].file = fopen(filename, "r+");
- if (openFile[*file_no].file == NULL) {
- openFile[*file_no].file = fopen(filename, "w+");
- }
- openFile[*file_no].state.company_code = company_code;
- openFile[*file_no].state.game_code = game_code;
- strcpy(openFile[*file_no].state.game_name, game_name);
- strcpy(openFile[*file_no].state.ext_name, ext_name);
- return PFS_NO_ERROR;
-}