fix byteswapping

This commit is contained in:
Garrett Smith 2026-01-24 13:58:36 -08:00
parent 584494be42
commit 7f77d1857e
2 changed files with 75 additions and 61 deletions

View file

@ -289,12 +289,15 @@ typedef struct {
} OSPfs;
typedef struct {
/* 0x00 */ u32 file_size; /* bytes */
/* 0x04 */ u32 game_code;
/* 0x0A */ char pad_0A[2];
/* 0x08 */ u16 company_code;
/* 0x0C */ char ext_name[4];
/* 0x10 */ char game_name[16];
u32 file_size;
u32 game_code;
char ext_name_0[2]; // insane layout due to ext_name starting on halfword boundary
u16 company_code;
char game_name_0[2];
char ext_name_1[2];
char game_name_1[12];
char padding[2];
char game_name_2[2];
} OSPfsState; // size = 0x20
// Controller

View file

@ -1,3 +1,4 @@
#include <array>
#include <filesystem>
#include <fstream>
#include <ultramodern/save.hpp>
@ -9,6 +10,30 @@
/* PFS Context */
struct pfs_header_t { // same layout as OSPfsState, but non-byteswapped
uint32_t file_size;
uint32_t game_code;
uint16_t company_code;
std::array<char, 4> ext_name;
std::array<char, 16> game_name;
uint16_t padding;
pfs_header_t() = default;
pfs_header_t(uint32_t fs, uint32_t gc, uint16_t cc, const char* en, const char* gn)
: file_size{fs}, game_code{gc}, company_code{cc} {
std::memcpy(ext_name.data(), en, 4);
std::memcpy(game_name.data(), gn, 16);
}
inline bool valid() const {
return game_code != 0 && company_code != 0;
}
inline bool compare(uint32_t gcode, uint16_t ccode, const char* ename, const char* gname) const {
return game_code == gcode && company_code == ccode &&
std::memcmp(ext_name.data(), ename, 4) == 0 &&
std::memcmp(game_name.data(), gname, 14) == 0;
}
};
inline std::filesystem::path pfs_header_path() {
const auto filename = "controllerpak_header.bin";
return ultramodern::get_save_base_path() / filename;
@ -27,35 +52,30 @@ inline bool pfs_header_alloc() {
return true;
}
inline bool pfs_header_write(int file_no, u32 file_size, u32 game_code, u16 company_code, u8* ext_name, u8* game_name) {
inline bool pfs_header_write(int file_no, const pfs_header_t& hdr) {
std::ofstream out(pfs_header_path(), std::ios::binary | std::ios::in);
if (out.is_open() && out.good()) {
u16 padding = 0;
out.seekp(file_no * sizeof(OSPfsState), std::ios::beg);
out.write((const char*)&file_size, 4);
out.write((const char*)&game_code, 4);
out.write((const char*)&company_code, 2);
out.write((const char*)&padding, 2);
out.write((const char*)ext_name, 4);
out.write((const char*)game_name, 16);
out.seekp(file_no * sizeof(pfs_header_t), std::ios::beg);
out.write((const char*)&hdr.file_size, sizeof(hdr.file_size));
out.write((const char*)&hdr.game_code, sizeof(hdr.game_code));
out.write((const char*)&hdr.company_code, sizeof(hdr.company_code));
out.write((const char*)&hdr.ext_name[0], hdr.ext_name.size());
out.write((const char*)&hdr.game_name[0], hdr.game_name.size());
out.write((const char*)&hdr.padding, sizeof(hdr.padding));
}
return out.good();
}
inline bool pfs_header_write(int file_no, const OSPfsState& hdr) {
return pfs_header_write(file_no, hdr.file_size, hdr.game_code, hdr.company_code, (u8*)hdr.ext_name, (u8*)hdr.game_name);
}
inline bool pfs_header_read(int file_no, OSPfsState& hdr) {
inline bool pfs_header_read(int file_no, pfs_header_t& hdr) {
std::ifstream in(pfs_header_path(), std::ios::binary | std::ios::in);
if (in.is_open() && in.good()) {
in.seekg(file_no * sizeof(OSPfsState), std::ios::beg);
in.read((char*)&hdr.file_size, 4);
in.read((char*)&hdr.game_code, 4);
in.read((char*)&hdr.company_code, 2);
in.read((char*)&hdr.pad_0A, 2);
in.read((char*)&hdr.ext_name[0], 4);
in.read((char*)&hdr.game_name[0], 16);
in.seekg(file_no * sizeof(pfs_header_t), std::ios::beg);
in.read((char*)&hdr.file_size, sizeof(hdr.file_size));
in.read((char*)&hdr.game_code, sizeof(hdr.game_code));
in.read((char*)&hdr.company_code, sizeof(hdr.company_code));
in.read((char*)&hdr.ext_name[0], hdr.ext_name.size());
in.read((char*)&hdr.game_name[0], hdr.game_name.size());
in.read((char*)&hdr.padding, sizeof(hdr.padding));
}
return in.good();
}
@ -175,16 +195,15 @@ extern "C" s32 osPfsChecker(RDRAM_ARG PTR(OSPfs) pfs) {
extern "C" s32 osPfsAllocateFile(RDRAM_ARG PTR(OSPfs) pfs, u16 company_code, u32 game_code, u8* game_name, u8* ext_name, int nbytes, PTR(s32) file_no_) {
s32* file_no = TO_PTR(s32, file_no_);
if ((company_code == 0) || (game_code == 0)) {
if (company_code == 0 || game_code == 0) {
return PFS_ERR_INVALID;
}
pfs_header_t hdr{};
u8 free_file_index = 0;
for (size_t i = 0; i < MAX_FILES; i++) {
OSPfsState hdr{};
pfs_header_read(i, hdr);
if ((hdr.company_code == 0) || (hdr.game_code == 0)) {
if (!hdr.valid()) {
free_file_index = i;
break;
}
@ -193,7 +212,7 @@ extern "C" s32 osPfsAllocateFile(RDRAM_ARG PTR(OSPfs) pfs, u16 company_code, u32
if (free_file_index == MAX_FILES) {
return PFS_DIR_FULL;
}
if (!pfs_header_write(free_file_index, nbytes, game_code, company_code, ext_name, game_name)) {
if (!pfs_header_write(free_file_index, pfs_header_t{(uint32_t)nbytes, game_code, company_code, (char*)ext_name, (char*)game_name})) {
return PFS_ERR_INVALID;
}
if (!pfs_file_alloc(free_file_index, nbytes)) {
@ -204,24 +223,18 @@ extern "C" s32 osPfsAllocateFile(RDRAM_ARG PTR(OSPfs) pfs, u16 company_code, u32
}
extern "C" s32 osPfsFindFile(RDRAM_ARG PTR(OSPfs) pfs_, u16 company_code, u32 game_code, u8* game_name, u8* ext_name, PTR(s32) file_no_) {
OSPfs* pfs = TO_PTR(OSPfs, pfs_);
s32* file_no = TO_PTR(s32, file_no_);
if (company_code == 0 || game_code == 0) {
return PFS_ERR_INVALID;
}
pfs_header_t hdr{};
for (size_t i = 0; i < MAX_FILES; i++) {
OSPfsState hdr{};
pfs_header_read(i, hdr);
if ((game_code == hdr.game_code) && (company_code == hdr.company_code)) {
const auto gn_match = !std::memcmp(game_name, hdr.game_name, sizeof(hdr.game_name));
const auto en_match = !std::memcmp(ext_name, hdr.ext_name, sizeof(hdr.ext_name));
if (gn_match && en_match) {
*file_no = i;
return 0;
}
if (hdr.compare(game_code, company_code, (char*)ext_name, (char*)game_name)) {
*file_no = i;
return 0;
}
}
return PFS_ERR_INVALID;
@ -232,18 +245,13 @@ extern "C" s32 osPfsDeleteFile(RDRAM_ARG PTR(OSPfs) pfs_, u16 company_code, u32
return PFS_ERR_INVALID;
}
pfs_header_t hdr{};
for (int i = 0; i < MAX_FILES; i++) {
OSPfsState hdr{};
pfs_header_read(i, hdr);
if ((game_code == hdr.game_code) && (company_code == hdr.company_code)) {
const auto gn_match = !std::memcmp(game_name, hdr.game_name, sizeof(hdr.game_name));
const auto en_match = !std::memcmp(ext_name, hdr.ext_name, sizeof(hdr.ext_name));
if (gn_match && en_match) {
pfs_header_write(i, OSPfsState{});
std::filesystem::remove(pfs_file_path(i));
return 0;
}
if (hdr.compare(game_code, company_code, (char*)ext_name, (char*)game_name)) {
pfs_header_write(i, pfs_header_t{});
std::filesystem::remove(pfs_file_path(i));
return 0;
}
}
return PFS_ERR_INVALID;
@ -262,6 +270,10 @@ extern "C" s32 osPfsReadWriteFile(RDRAM_ARG PTR(OSPfs) pfs_, s32 file_no, u8 fla
return 0;
}
inline void bswap_copy(char* dst, const char* src, int offset, int n) {
for (int i = 0; i < n; i++) { dst[(i + offset) ^ 3] = src[i + offset]; }
}
extern "C" s32 osPfsFileState(RDRAM_ARG PTR(OSPfs) pfs_, s32 file_no, PTR(OSPfsState) state_) {
OSPfsState *state = TO_PTR(OSPfsState, state_);
@ -269,14 +281,15 @@ extern "C" s32 osPfsFileState(RDRAM_ARG PTR(OSPfs) pfs_, s32 file_no, PTR(OSPfsS
return PFS_ERR_INVALID;
}
OSPfsState hdr{};
pfs_header_t hdr{};
pfs_header_read(file_no, hdr);
state->file_size = hdr.file_size;
state->company_code = hdr.company_code;
state->game_code = hdr.game_code;
std::memcpy(state->game_name, hdr.game_name, sizeof(hdr.game_name));
std::memcpy(state->ext_name, hdr.ext_name, sizeof(hdr.ext_name));
// FIXME OSPfsState layout is an absoute mess. giving up and byte swapping
bswap_copy((char*)state, (char*)&hdr, 10, 20);
return 0;
}
@ -333,11 +346,10 @@ extern "C" s32 osPfsFreeBlocks(RDRAM_ARG PTR(OSPfs) pfs_, PTR(s32) bytes_not_use
s32 *bytes_not_used = TO_PTR(s32, bytes_not_used_);
s32 bytes_used = 0;
pfs_header_t hdr{};
for (size_t i = 0; i < MAX_FILES; i++) {
OSPfsState hdr{};
pfs_header_read(i, hdr);
if ((hdr.company_code != 0) && (hdr.game_code != 0)) {
if (hdr.valid()) {
bytes_used += hdr.file_size >> 8;
}
}
@ -352,11 +364,10 @@ extern "C" s32 osPfsNumFiles(RDRAM_ARG PTR(OSPfs) pfs_, PTR(s32) max_files_, PTR
s32 *files_used = TO_PTR(s32, files_used_);
u8 num_files = 0;
pfs_header_t hdr{};
for (size_t i = 0; i < MAX_FILES; i++) {
OSPfsState hdr{};
pfs_header_read(i, hdr);
if ((hdr.company_code != 0) && (hdr.game_code != 0)) {
if (hdr.valid()) {
num_files++;
}
}