mirror of
https://github.com/izzy2lost/Diddy-Kong-Racing.git
synced 2026-06-19 01:16:26 -07:00
1337 lines
39 KiB
C
1337 lines
39 KiB
C
/* The comment below is needed for this file to be picked up by generate_ld */
|
|
/* RAM_POS: 0x80072250 */
|
|
|
|
#include "save_data.h"
|
|
#include "memory.h"
|
|
#include "PR/pfs.h"
|
|
#include "PR/os_cont.h"
|
|
#include "macros.h"
|
|
#include "structs.h"
|
|
#include "camera.h"
|
|
#include "objects.h"
|
|
#include "game.h"
|
|
#include "unknown_0CEC50.h"
|
|
|
|
/************ .data ************/
|
|
|
|
s8 *D_800DE440 = 0;
|
|
|
|
u8 gN64FontCodes[] = "\0 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#'*+,-./:=?@";
|
|
|
|
u8 sControllerPaksPresent = 0; // Bits 0, 1, 2, and 3 of the bit pattern correspond to Controllers 1, 2, 3, and 4. 1 if a controller pak is present
|
|
s32 D_800DE48C = 0;
|
|
|
|
/*******************************/
|
|
|
|
/************ .rodata ************/
|
|
|
|
const char D_800E7590[] = "Region = %d loc = %x size = %x\t";
|
|
const char D_800E75B4[] = "FREE";
|
|
const char D_800E75BC[] = "ALLOCATED";
|
|
const char D_800E75C8[] = "ALLOCATED,FIXED";
|
|
const char D_800E75D8[] = "\n";
|
|
const char D_800E75DC[] = "\n";
|
|
const char D_800E75E0[] = "Region number = %d\t";
|
|
const char D_800E75F4[] = "maxSlots = %d\t";
|
|
const char D_800E7604[] = "slotsUsed = %d\t";
|
|
const char D_800E7614[] = "loc = %x\t";
|
|
const char D_800E7620[] = "size = %x\n";
|
|
const char D_800E762C[] = "\n";
|
|
|
|
const char D_800E7630[] = "DKRACING-ADV";
|
|
const char D_800E7640[] = "DKRACING-ADV";
|
|
const char D_800E7650[] = "DKRACING-TIMES";
|
|
const char D_800E7660[] = "DKRACING-TIMES";
|
|
const char D_800E7670[] = "DKRACING-ADV";
|
|
const char D_800E7680[] = "DKRACING-ADV";
|
|
const char D_800E7690[] = "DKRACING-TIMES";
|
|
const char D_800E76A0[] = "DKRACING-TIMES";
|
|
|
|
const char D_800E76B0[] = "WARNING : No Eprom\n";
|
|
const char D_800E76C4[] = "WARNING : No Eprom\n";
|
|
const char D_800E76D8[] = "WARNING : No Eprom\n";
|
|
const char D_800E76EC[] = "WARNING : No Eprom\n";
|
|
const char D_800E7700[] = "WARNING : No Eprom\n";
|
|
const char D_800E7714[] = "WARNING : No Eprom\n";
|
|
const char D_800E7728[] = "WARNING : No Eprom\n";
|
|
|
|
const char D_800E773C[] = "DKRACING-GHOSTS"; // Used in func_80074B34
|
|
const u8 D_800E774C[4] = { 0, 0, 0, 0 }; // Used in func_80074B34, but I'm confused to what this is for.
|
|
const char D_800E7750[] = "warning: corrupt ghost\n";
|
|
|
|
/*********************************/
|
|
|
|
/************ .bss ************/
|
|
|
|
OSMesgQueue *sControllerMesgQueue;
|
|
s32 D_80124014;
|
|
|
|
OSPfs pfs[MAXCONTROLLERS];
|
|
|
|
typedef struct unk_801241B8 {
|
|
/* 0x00 */ s16 unk0;
|
|
/* 0x02 */ s16 unk2;
|
|
/* 0x04 */ s16 unk4;
|
|
/* 0x06 */ s16 unk6;
|
|
/* 0x08 */ s16 unk8;
|
|
} unk_801241B8;
|
|
unk_801241B8 D_801241B8[MAXCONTROLLERS];
|
|
|
|
s32 *sUnkMiscAsset19; //Misc Asset 19
|
|
/**
|
|
* Values for Misc Asset 19
|
|
* 00 2D 00 12
|
|
* 00 46 00 0A
|
|
* 00 3C 00 3C
|
|
* 00 64 00 0F
|
|
* 00 3C 00 36
|
|
* 00 32 00 0F
|
|
* 00 32 00 34
|
|
* 00 46 00 3C
|
|
* 00 55 00 3C
|
|
* 00 46 00 16
|
|
* 00 50 00 4B
|
|
* 00 64 00 0C
|
|
* 00 3C 00 18
|
|
* 00 50 00 1E
|
|
* 00 64 00 2A
|
|
* 00 32 00 3C
|
|
* 00 28 00 0A
|
|
* 00 28 00 0A
|
|
* 00 64 00 0F
|
|
**/
|
|
u8 D_801241E4;
|
|
u8 sRumblePaksPresent; // Bits 0, 1, 2, and 3 of the bit pattern correspond to Controllers 1, 2, 3, and 4. 1 if a rumble pak is present
|
|
u8 D_801241E6;
|
|
u8 D_801241E7;
|
|
s32 D_801241E8;
|
|
s8 *D_801241EC;
|
|
s32 D_801241F0;
|
|
s32 D_801241F4;
|
|
|
|
/*******************************/
|
|
|
|
// Guessing, but maybe this gets player id from controller index,
|
|
// but it might check for something like jointventure and will return
|
|
// the same value for controllers one and two?
|
|
u8 func_80072250(s32 arg0) {
|
|
if ((arg0 == 0 || arg0 == 1) && func_8000E158()) {
|
|
arg0 = 1 - arg0;
|
|
}
|
|
return get_player_id(arg0);
|
|
}
|
|
|
|
void func_80072298(u8 arg0) {
|
|
D_801241E4 = arg0;
|
|
if (arg0 != 0) {
|
|
D_801241E8 = 0x79;
|
|
D_801241E6 = 0xF;
|
|
return;
|
|
}
|
|
func_80072708();
|
|
}
|
|
|
|
UNUSED s32 func_800722E8(s16 arg0) {
|
|
s32 temp;
|
|
if (arg0 < 0 || arg0 >= 4) {
|
|
return 0;
|
|
}
|
|
temp = ((1 << func_80072250(arg0)) & 0xFF);
|
|
return sRumblePaksPresent & temp;
|
|
}
|
|
|
|
#ifdef NON_EQUIVALENT
|
|
//In the right ballpark, but not right yet.
|
|
void func_80072348(s16 arg0, u8 arg1) {
|
|
s32 arg1AsS32 = arg1;
|
|
s16 *temp_v0;
|
|
unk_801241B8 *temp_v1;
|
|
u16 playerId;
|
|
|
|
if (arg1AsS32 < 19 && arg0 >= 0) {
|
|
if (arg0 < 4) {
|
|
playerId = (func_80072250(arg0));
|
|
temp_v1 = &D_801241B8[playerId];
|
|
if (arg1AsS32 == temp_v1->unk0) {
|
|
if (temp_v1->unk8 < 0) {
|
|
temp_v1->unk8 = -300;
|
|
}
|
|
temp_v1->unk4 = sUnkMiscAsset19[arg1AsS32 + 1];
|
|
} else {
|
|
temp_v1->unk0 = arg1AsS32;
|
|
temp_v0 = &sUnkMiscAsset19[arg1AsS32];
|
|
if (*temp_v0 != 0) {
|
|
func_80072578(arg0, temp_v0[0], temp_v0[1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_80072348.s")
|
|
#endif
|
|
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_80072424.s")
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_80072578.s")
|
|
|
|
void func_80072708(void) {
|
|
D_800DE48C = 3;
|
|
}
|
|
|
|
//Rumbles controller?
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_80072718.s")
|
|
|
|
#ifdef NON_EQUIVALENT
|
|
u16 func_80072C54(s32 arg0) {
|
|
// v0 v1 a1 a2 a3 t0 t1 t2 t3 t4 t5 t6 t7 t8
|
|
u32 v0;
|
|
u32 v1;
|
|
s32 a1;
|
|
u32 a2;
|
|
u8 *a3;
|
|
s32 t0;
|
|
u32 *t1;
|
|
u32 *t2;
|
|
u8 **t3;
|
|
u32 t4;
|
|
u32 t5;
|
|
u32 t6;
|
|
u32 t7;
|
|
u32 t8;
|
|
u32 t9;
|
|
|
|
v1 = 0;
|
|
if (arg0 <= 0) {
|
|
return v1;
|
|
}
|
|
// L80072C64
|
|
v0 = 1 << (arg0 + 0x1F);
|
|
if (arg0 != 0) {
|
|
a1 = -(arg0 & 3);
|
|
t0 = a1 + arg0;
|
|
if (a1 != 0) {
|
|
// L80072CA0
|
|
do {
|
|
a2 = D_801241F4;
|
|
a1 = arg0 - 1;
|
|
t6 = v0 >> 1;
|
|
if (a2 == 0) {
|
|
a3 = D_801241EC;
|
|
a2 = 0x80;
|
|
t8 = *a3;
|
|
D_801241EC = a3 + 1;
|
|
D_801241F4 = 0x80;
|
|
D_801241F0 = t8;
|
|
}
|
|
// L80072CCC
|
|
arg0 = a1;
|
|
if (D_801241F0 & a2 != 0) {
|
|
v1 |= v0;
|
|
}
|
|
// L80072CE4
|
|
v0 = t6;
|
|
D_801241F4 = a2 >> 1;
|
|
} while (t0 != a1);
|
|
if (a1 == 0) {
|
|
return v1; // should jump to end
|
|
}
|
|
}
|
|
// L80072CF4
|
|
t1 = &D_801241F4;
|
|
t2 = &D_801241F0;
|
|
t3 = &D_801241EC;
|
|
t4 = 128;
|
|
// L80072D10
|
|
do {
|
|
a2 = *t1;
|
|
t6 = v0 >> 1;
|
|
arg0 -= 4;
|
|
if (a2 == 0) {
|
|
a3 = *t3;
|
|
a2 = t4;
|
|
t9 = *a3;
|
|
*t3 = a3 + 1;
|
|
*t1 = t4;
|
|
*t2 = t9;
|
|
}
|
|
// L80072D3C
|
|
a1 = *t2;
|
|
t8 = a2 << 1;
|
|
t7 = a1 & a2;
|
|
a2 = t8;
|
|
if (t7 != 0) {
|
|
v1 |= v0;
|
|
}
|
|
// L80072D54
|
|
v0 = t6;
|
|
*t1 = t8;
|
|
if (t8 == 0) {
|
|
a3 = *t3;
|
|
a2 = t4;
|
|
a1 = *a3;
|
|
*t3 = a3 + 1;
|
|
*t1 = t4;
|
|
*t2 = a1;
|
|
}
|
|
// L80072D7C
|
|
t7 = a1 & a2;
|
|
t6 = v0 >> 1;
|
|
if (t7 != 0) {
|
|
v1 |= v0;
|
|
}
|
|
// L80072D8C
|
|
t8 = a2 >> 1;
|
|
*t1 = t8;
|
|
a2 = t8;
|
|
v0 = t6;
|
|
if (t8 == 0) {
|
|
a3 = *t3;
|
|
a2 = t4;
|
|
a1 = *a3;
|
|
*t3 = a3 + 1;
|
|
*t1 = t4;
|
|
*t2 = a1;
|
|
}
|
|
// L80072DBC
|
|
t7 = a1 & a2;
|
|
t6 = v0 >> 1;
|
|
if (t7 != 0) {
|
|
v1 |= v0;
|
|
}
|
|
// L80072DCC
|
|
t8 = a2 >> 1;
|
|
*t1 = t8;
|
|
a2 = t8;
|
|
v0 = t6;
|
|
if (t8 == 0) {
|
|
a3 = *t3;
|
|
a2 = t4;
|
|
a1 = *a3;
|
|
*t3 = a3 + 1;
|
|
*t1 = t4;
|
|
*t2 = a1;
|
|
}
|
|
// L80072DFC
|
|
t7 = a1 & a2;
|
|
t6 = v0 >> 1;
|
|
if (t7 != 0) {
|
|
v1 |= v0;
|
|
}
|
|
// L80072E0C
|
|
t8 = a2 >> 1;
|
|
*t1 = t8;
|
|
v0 = t6;
|
|
} while (arg0 != 0);
|
|
}
|
|
// L80072E1C
|
|
return v1;
|
|
}
|
|
#else
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_80072C54.s")
|
|
#endif
|
|
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_80072E28.s")
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_8007306C.s")
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_800732E8.s")
|
|
|
|
//arg1 is eepromData, from read_eeprom_data
|
|
//arg2 seems to be a flag for either lap times or course initials?
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_80073588.s")
|
|
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_800738A4.s")
|
|
|
|
s32 get_game_data_file_size(void) {
|
|
return 256;
|
|
}
|
|
|
|
s32 get_time_data_file_size(void) {
|
|
return 512;
|
|
}
|
|
|
|
//Probably get_file_extension
|
|
//func_80073C5C(s32 controllerIndex, s32 fileType, u8 **fileExt);
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_80073C5C.s")
|
|
|
|
//Read DKRACING-ADV data into settings?
|
|
s32 read_game_data_from_controller_pak(s32 controllerIndex, u8 *fileExt, Settings *settings) {
|
|
s32 *alloc;
|
|
s32 ret;
|
|
s32 fileNumber;
|
|
s32 fileSize;
|
|
|
|
//Initialize controller pak
|
|
ret = get_si_device_status(controllerIndex);
|
|
if (ret != CONTROLLER_PAK_GOOD) {
|
|
start_reading_controller_data(controllerIndex);
|
|
return (controllerIndex << 30) | ret;
|
|
}
|
|
//D_800E7670 = "DKRACING-ADV"
|
|
ret = get_file_number(controllerIndex, (u8 *)D_800E7670, fileExt, &fileNumber);
|
|
if (ret == CONTROLLER_PAK_GOOD) {
|
|
ret = get_file_size(controllerIndex, fileNumber, &fileSize);
|
|
if (fileSize == 0) {
|
|
ret = CONTROLLER_PAK_BAD_DATA;
|
|
}
|
|
if (ret == CONTROLLER_PAK_GOOD) {
|
|
alloc = allocate_from_main_pool_safe(fileSize, COLOUR_TAG_BLACK);
|
|
ret = read_data_from_controller_pak(controllerIndex, fileNumber, (u8 *)alloc, fileSize);
|
|
|
|
if (ret == CONTROLLER_PAK_GOOD) {
|
|
if (*alloc == GAMD) {
|
|
func_8007306C(settings, (s32) (alloc + 1));
|
|
if (settings->newGame != 0) {
|
|
ret = CONTROLLER_PAK_CHANGED;
|
|
}
|
|
}
|
|
else {
|
|
ret = CONTROLLER_PAK_BAD_DATA;
|
|
}
|
|
}
|
|
|
|
free_from_memory_pool(alloc);
|
|
}
|
|
}
|
|
|
|
start_reading_controller_data(controllerIndex);
|
|
if (ret != CONTROLLER_PAK_GOOD) {
|
|
ret |= (controllerIndex << 30);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
s32 write_game_data_to_controller_pak(s32 controllerIndex, Settings *arg1) {
|
|
UNUSED s32 pad;
|
|
u8 *fileExt;
|
|
u8 *gameData; //Probably a struct where the first s32 is GAMD
|
|
s32 ret;
|
|
s32 fileSize;
|
|
|
|
fileSize = get_game_data_file_size(); // 256 bytes
|
|
gameData = allocate_from_main_pool_safe(fileSize, COLOUR_TAG_WHITE);
|
|
*((s32 *)gameData) = GAMD;
|
|
func_800732E8(arg1, gameData + 4);
|
|
ret = func_80073C5C(controllerIndex, 3, &fileExt);
|
|
if (ret == 0) {
|
|
// D_800E7680 = DKRACING-ADV
|
|
ret = write_controller_pak_file(controllerIndex, -1, (u8 *) D_800E7680, (u8 *) &fileExt, gameData, fileSize);
|
|
}
|
|
free_from_memory_pool(gameData);
|
|
if (ret != 0) {
|
|
ret |= (controllerIndex << 30);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
//Read time data from controller pak into settings
|
|
s32 read_time_data_from_controller_pak(s32 controllerIndex, u8 *fileExt, Settings *settings) {
|
|
s32 *cpakData;
|
|
s32 status;
|
|
s32 fileNumber;
|
|
s32 fileSize;
|
|
|
|
status = get_si_device_status(controllerIndex);
|
|
if (status != CONTROLLER_PAK_GOOD) {
|
|
start_reading_controller_data(controllerIndex);
|
|
return (controllerIndex << 30) | status;
|
|
}
|
|
|
|
//D_800E7690 = DKRACING-TIMES
|
|
status = get_file_number(controllerIndex, (u8 *)D_800E7690, fileExt, &fileNumber);
|
|
if (status == CONTROLLER_PAK_GOOD) {
|
|
status = get_file_size(controllerIndex, fileNumber, &fileSize);
|
|
if (fileSize == 0) {
|
|
status = CONTROLLER_PAK_BAD_DATA;
|
|
}
|
|
|
|
if (status == CONTROLLER_PAK_GOOD) {
|
|
cpakData = allocate_from_main_pool_safe(fileSize, COLOUR_TAG_BLACK);
|
|
|
|
status = read_data_from_controller_pak(controllerIndex, fileNumber, (u8 *)cpakData, fileSize);
|
|
if (status == CONTROLLER_PAK_GOOD) {
|
|
if (*cpakData == TIMD) {
|
|
func_80073588(settings, (u64 *) (cpakData + 1), 3);
|
|
} else {
|
|
status = CONTROLLER_PAK_BAD_DATA;
|
|
}
|
|
}
|
|
|
|
free_from_memory_pool(cpakData);
|
|
}
|
|
}
|
|
|
|
start_reading_controller_data(controllerIndex);
|
|
if (status != CONTROLLER_PAK_GOOD) {
|
|
status |= (controllerIndex << 30);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
s32 write_time_data_to_controller_pak(s32 controllerIndex, Settings *arg1) {
|
|
u8 *timeData; //Should probably be a struct or maybe an array?
|
|
s32 ret;
|
|
s32 fileSize;
|
|
UNUSED s32 temp_v0;
|
|
u8 *fileExt;
|
|
|
|
fileSize = get_time_data_file_size(); // 512 bytes
|
|
timeData = allocate_from_main_pool_safe(fileSize, COLOUR_TAG_WHITE);
|
|
*((s32 *)timeData) = TIMD;
|
|
func_800738A4(arg1, timeData + 4);
|
|
ret = func_80073C5C(controllerIndex, 4, &fileExt);
|
|
if (ret == CONTROLLER_PAK_GOOD) {
|
|
//D_800E76A0 = DKRACING-TIMES
|
|
ret = write_controller_pak_file(controllerIndex, -1, (u8 *) &D_800E76A0, (u8 *) &fileExt, timeData, fileSize);
|
|
}
|
|
free_from_memory_pool(timeData);
|
|
if (ret != CONTROLLER_PAK_GOOD) {
|
|
ret |= (controllerIndex << 30);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_80074204.s")
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_8007431C.s")
|
|
|
|
/**
|
|
* Writes Eeprom in 5 block chunks of data starting at either 0x0, 0x5, or 0xA
|
|
* In Save File:
|
|
* 0x00 - 0x27 Save 1
|
|
* 0x28 - 0x4F Save 2
|
|
* 0x50 - 0x77 Save 3
|
|
*/
|
|
s32 write_save_data(s32 saveFileNum, Settings *settings) {
|
|
s32 startingAddress;
|
|
u64 *alloc;
|
|
s32 address;
|
|
s32 blocks;
|
|
s32 i;
|
|
|
|
if (osEepromProbe(get_si_mesg_queue()) == 0) {
|
|
return -1;
|
|
}
|
|
|
|
switch(saveFileNum) {
|
|
case 0:
|
|
startingAddress = 0;
|
|
break;
|
|
case 1:
|
|
startingAddress = 5;
|
|
break;
|
|
case 2:
|
|
startingAddress = 10;
|
|
break;
|
|
default:
|
|
startingAddress = 10;
|
|
break;
|
|
}
|
|
|
|
blocks = 5;
|
|
alloc = allocate_from_main_pool_safe(blocks * sizeof(u64), COLOUR_TAG_WHITE);
|
|
func_800732E8(settings, (u8 *)alloc);
|
|
|
|
if (!is_reset_pressed()) {
|
|
for (i = 0, address = startingAddress; i < blocks; i++, address++) {
|
|
osEepromWrite(get_si_mesg_queue(), address, (u8 *)&alloc[i]);
|
|
}
|
|
}
|
|
|
|
free_from_memory_pool(alloc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Read Eeprom Data for addresses 0x10 - 0x39
|
|
* arg1 is a flag
|
|
* arg1 is descended from (D_800DD37C & 3) so the first 2 bits of that value.
|
|
* bit 1 is for 0x10 - 0x27
|
|
* bit 2 is for 0x28 - 0x39
|
|
*/
|
|
s32 read_eeprom_data(Settings *arg0, u8 arg1) {
|
|
u64 *alloc;
|
|
s32 i;
|
|
|
|
if (osEepromProbe(get_si_mesg_queue()) == 0) {
|
|
return -1;
|
|
}
|
|
|
|
alloc = allocate_from_main_pool_safe(0x200, COLOUR_TAG_WHITE);
|
|
|
|
if (arg1 & 1) {
|
|
s32 blocks = 24;
|
|
for (i = 0; i < blocks; i++) {
|
|
osEepromRead(get_si_mesg_queue(), i + 0x10, (u8 *)&alloc[i]);
|
|
}
|
|
func_80073588(arg0, alloc, 1);
|
|
}
|
|
|
|
if (arg1 & 2) {
|
|
s32 blocks = 24;
|
|
for (i = 0; i < blocks; i++) {
|
|
osEepromRead(get_si_mesg_queue(), i + 0x28, (u8 *)(&alloc[24] + i));
|
|
}
|
|
func_80073588(arg0, alloc, 2);
|
|
}
|
|
|
|
free_from_memory_pool(alloc);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Write Eeprom Data for addresses 0x10 - 0x39
|
|
* 0x80 - 0x1C8 in file
|
|
* arg1 is a flag
|
|
* bit 1 is for 0x10 - 0x27
|
|
* bit 2 is for 0x28 - 0x39
|
|
*/
|
|
s32 write_eeprom_data(Settings *arg0, u8 arg1) {
|
|
u64 *alloc;
|
|
s32 i;
|
|
|
|
if (osEepromProbe(get_si_mesg_queue()) == 0) {
|
|
return -1;
|
|
}
|
|
|
|
alloc = allocate_from_main_pool_safe(0x200, COLOUR_TAG_WHITE);
|
|
|
|
func_800738A4(arg0, (u8 *)alloc);
|
|
|
|
if (arg1 & 1) {
|
|
s32 size = 24;
|
|
if (1){} //Fake Match
|
|
if (!is_reset_pressed()) {
|
|
for (i = 0; i != size; i++) {
|
|
osEepromWrite(get_si_mesg_queue(), i + 16, (u8 *)&alloc[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (arg1 & 2) {
|
|
s32 size = 24;
|
|
if (!is_reset_pressed()) {
|
|
for (i = 0; i != size; i++) {
|
|
osEepromWrite(get_si_mesg_queue(), i + 40, (u8 *)(&alloc[24] + i));
|
|
}
|
|
}
|
|
}
|
|
|
|
free_from_memory_pool(alloc);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Calculates a checksum for the game settings, and compares
|
|
* against this to validate the data on load.
|
|
*/
|
|
s32 calculate_eeprom_settings_checksum(u64 eepromSettings) {
|
|
s32 ret;
|
|
s32 i;
|
|
|
|
ret = 5;
|
|
for (i = 0; i <= 13; i++) {
|
|
ret += (u16)(eepromSettings >> (i << 2)) & 0xF;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Reads eeprom address 0xF (15)
|
|
* This contains the settings data, and global unlocks like characters
|
|
* Address (0xF * sizeof(u64)) = 0x78 - 0x80 of the actual save data file
|
|
*/
|
|
s32 read_eeprom_settings(u64 *eepromSettings) {
|
|
s32 temp;
|
|
s32 sp20;
|
|
|
|
if (osEepromProbe(get_si_mesg_queue()) == 0) {
|
|
return -1;
|
|
}
|
|
|
|
osEepromRead(get_si_mesg_queue(), 0xF, (u8 *) eepromSettings);
|
|
sp20 = calculate_eeprom_settings_checksum(*eepromSettings);
|
|
temp = *eepromSettings >> 56;
|
|
if (sp20 != temp) {
|
|
//bit 24 = Unknown
|
|
//bit 25 = Seems to be a flag for whether subtitles are enabled or not.
|
|
*eepromSettings = 0x3000000; //Sets bits 24 and 25 high
|
|
*eepromSettings <<= 8;
|
|
*eepromSettings >>= 8;
|
|
*eepromSettings |= (u64)calculate_eeprom_settings_checksum(*eepromSettings) << 56;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Writes eeprom address 0xF (15)
|
|
* This contains the settings data, and global unlocks like characters
|
|
* Address (0xF * sizeof(u64)) = 0x78 - 0x80 of the actual save data file
|
|
*/
|
|
s32 write_eeprom_settings(u64 *eepromSettings) {
|
|
if (osEepromProbe(get_si_mesg_queue()) == 0) {
|
|
return -1;
|
|
}
|
|
*eepromSettings <<= 8;
|
|
*eepromSettings >>= 8;
|
|
*eepromSettings |= (s64)(calculate_eeprom_settings_checksum(*eepromSettings)) << 56;
|
|
if (is_reset_pressed() == 0) {
|
|
osEepromWrite(get_si_mesg_queue(), 0xF, (u8 *) eepromSettings);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
s16 calculate_ghost_header_checksum(GhostHeader *ghostHeader) {
|
|
s16 i;
|
|
s16 len;
|
|
s16 sum;
|
|
|
|
sum = 0;
|
|
len = (s16)(ghostHeader->nodeCount * sizeof(GhostDataFrame)) + sizeof(GhostHeader);
|
|
for (i = 2; i < len; i++) {
|
|
sum += ((u8 *)ghostHeader)[i];
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
void func_80074AA8(GhostHeader *ghostHeader, s16 characterID, s16 time, s16 nodeCount, u8 *dest) {
|
|
ghostHeader->checksum = 0;
|
|
ghostHeader->characterID = characterID;
|
|
ghostHeader->unk3 = 0;
|
|
ghostHeader->time = time;
|
|
ghostHeader->nodeCount = nodeCount;
|
|
bcopy(dest, (u8 *)ghostHeader + 8, nodeCount * sizeof(GhostDataFrame));
|
|
ghostHeader->checksum = calculate_ghost_header_checksum(ghostHeader);
|
|
}
|
|
|
|
//Seems to only be called when used as an argument for func_800860A8. Effectively just returns 0x6700
|
|
s32 func_80074B1C(void) {
|
|
//func_80074EB8 Makes extensive use out of 0x1100, and even does a * 6 to it.
|
|
//func_80075000 Uses 0x1100 once as well.
|
|
//Another possibility for 0x1100, seems to be from func_8007BA5C which seems to do (0xF5101100 & 0xFFFF);
|
|
int x = 0x1100;
|
|
return (&x)[0] * 6 + 0x100;
|
|
}
|
|
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_80074B34.s")
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_80074EB8.s")
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_80075000.s")
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_800753D8.s")
|
|
GLOBAL_ASM("asm/non_matchings/save_data/func_800756D4.s")
|
|
|
|
SIDeviceStatus get_si_device_status(s32 controllerIndex) {
|
|
OSMesg unusedMsg;
|
|
s32 ret;
|
|
s32 bytes_not_used;
|
|
s32 i;
|
|
|
|
if (sControllerMesgQueue->validCount == 0) {
|
|
if (osMotorInit(sControllerMesgQueue, &pfs[controllerIndex], controllerIndex) == 0) {
|
|
return RUMBLE_PAK;
|
|
}
|
|
}
|
|
|
|
//Couldn't get a for loop to match this
|
|
i = 0;
|
|
while (sControllerMesgQueue->validCount != 0 && i < 10) {
|
|
osRecvMesg(sControllerMesgQueue, &unusedMsg, OS_MESG_NOBLOCK);
|
|
i++;
|
|
}
|
|
|
|
for (i = 0; i <= 4; i++) {
|
|
ret = osPfsFreeBlocks( &pfs[controllerIndex], &bytes_not_used);
|
|
if (ret == PFS_ERR_INVALID) {
|
|
ret = osPfsInit(sControllerMesgQueue, &pfs[controllerIndex], controllerIndex);
|
|
}
|
|
if (ret == PFS_ERR_ID_FATAL) {
|
|
if (osMotorInit(sControllerMesgQueue, &pfs[controllerIndex], controllerIndex) == 0) {
|
|
return RUMBLE_PAK;
|
|
}
|
|
}
|
|
if (ret == PFS_ERR_NEW_PACK) {
|
|
if ((osPfsInit(sControllerMesgQueue, &pfs[controllerIndex], controllerIndex) == PFS_ERR_ID_FATAL)
|
|
&& (osMotorInit(sControllerMesgQueue, &pfs[controllerIndex], controllerIndex) == 0)) {
|
|
return RUMBLE_PAK;
|
|
}
|
|
return CONTROLLER_PAK_CHANGED;
|
|
}
|
|
if (ret == PFS_ERR_NOPACK || ret == PFS_ERR_DEVICE) {
|
|
return NO_CONTROLLER_PAK;
|
|
}
|
|
if (ret == PFS_ERR_BAD_DATA) {
|
|
return CONTROLLER_PAK_BAD_DATA;
|
|
}
|
|
if (ret == PFS_ERR_ID_FATAL) {
|
|
return CONTROLLER_PAK_WITH_BAD_ID;
|
|
}
|
|
if (ret == PFS_ERR_INCONSISTENT) {
|
|
return CONTROLLER_PAK_INCONSISTENT;
|
|
}
|
|
if (ret == 0) {
|
|
return CONTROLLER_PAK_GOOD;
|
|
}
|
|
}
|
|
|
|
return NO_CONTROLLER_PAK;
|
|
}
|
|
|
|
s32 start_reading_controller_data(UNUSED s32 controllerIndex) {
|
|
osContStartReadData(sControllerMesgQueue);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef NON_MATCHING
|
|
// For some reason it seems likely that D_801241E7 is volatile.
|
|
// Still needs matching though to prove that.
|
|
// volatile u8 D_801241E7;
|
|
void init_controller_paks(void) {
|
|
s32 controllerIndex;
|
|
s32 ret;
|
|
u8 controllerBit;
|
|
u8 pakPattern;
|
|
s8 maxControllers;
|
|
|
|
sControllerMesgQueue = get_si_mesg_queue();
|
|
sUnkMiscAsset19 = get_misc_asset(19), //Comma is here on purpose
|
|
D_801241E7 = 0xF;
|
|
D_801241E6 = 0xF;
|
|
D_801241E4 = 1;
|
|
D_801241E8 = 0;
|
|
D_800DE48C = 1;
|
|
sRumblePaksPresent = 0; //sRumblePaksPresent
|
|
sControllerPaksPresent = 0; //sControllerPaksPresent
|
|
|
|
//pakPattern will set the first 4 bits representing each controller
|
|
//and it will be 1 if there's something attached.
|
|
osPfsIsPlug(sControllerMesgQueue, &pakPattern);
|
|
|
|
for (controllerIndex = 0, controllerBit = 1, maxControllers = MAXCONTROLLERS; controllerIndex != maxControllers; controllerIndex++, controllerBit <<= 1) {
|
|
D_801241B8[controllerIndex].unk2 = 0;
|
|
D_801241B8[controllerIndex].unk4 = -1;
|
|
D_801241B8[controllerIndex].unk0 = -1;
|
|
D_801241B8[controllerIndex].unk6 = D_801241B8[controllerIndex].unk2;
|
|
|
|
//If something is plugged into the controller
|
|
if (pakPattern & controllerBit) {
|
|
ret = osPfsInit(sControllerMesgQueue, &pfs[controllerIndex], controllerIndex);
|
|
if (ret == PFS_ERR_NEW_PACK) {
|
|
ret = osPfsInit(sControllerMesgQueue, &pfs[controllerIndex], controllerIndex);
|
|
}
|
|
if (ret == 0) {
|
|
//If we found a controller pak, set the bit that has one
|
|
sControllerPaksPresent |= controllerBit;
|
|
}
|
|
else if (ret == PFS_ERR_ID_FATAL) {
|
|
if (osMotorInit(sControllerMesgQueue, &pfs[controllerIndex], controllerIndex) == 0) {
|
|
//If we found a rumble pak, set the bit that has one
|
|
sRumblePaksPresent |= controllerBit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
osContStartReadData(sControllerMesgQueue);
|
|
}
|
|
#else
|
|
GLOBAL_ASM("asm/non_matchings/save_data/init_controller_paks.s")
|
|
#endif
|
|
|
|
UNUSED SIDeviceStatus check_for_rumble_pak(s32 controllerIndex) {
|
|
s32 ret;
|
|
|
|
ret = get_si_device_status(controllerIndex);
|
|
start_reading_controller_data(controllerIndex);
|
|
|
|
if (ret == RUMBLE_PAK) {
|
|
sRumblePaksPresent |= 1 << controllerIndex;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//Inspects and repairs the Controller Pak's file system
|
|
SIDeviceStatus repair_controller_pak(s32 controllerIndex) {
|
|
s32 ret;
|
|
s32 status = get_si_device_status(controllerIndex);
|
|
if (status == CONTROLLER_PAK_GOOD || status == CONTROLLER_PAK_INCONSISTENT) {
|
|
status = osPfsChecker(&pfs[controllerIndex]);
|
|
if (status == 0) {
|
|
ret = CONTROLLER_PAK_GOOD;
|
|
} else if (status == PFS_ERR_NEW_PACK) {
|
|
ret = CONTROLLER_PAK_CHANGED;
|
|
} else {
|
|
ret = CONTROLLER_PAK_INCONSISTENT;
|
|
}
|
|
} else {
|
|
ret = CONTROLLER_PAK_GOOD;
|
|
}
|
|
start_reading_controller_data(controllerIndex);
|
|
return ret;
|
|
}
|
|
|
|
//Reformat Controller Pak
|
|
SIDeviceStatus reformat_controller_pak(s32 controllerIndex) {
|
|
s32 ret;
|
|
s32 status = get_si_device_status(controllerIndex);
|
|
if (status == CONTROLLER_PAK_GOOD || status == CONTROLLER_PAK_INCONSISTENT || status == CONTROLLER_PAK_WITH_BAD_ID) {
|
|
status = osPfsReFormat(&pfs[controllerIndex], sControllerMesgQueue, controllerIndex);
|
|
if (status == 0) {
|
|
ret = CONTROLLER_PAK_GOOD;
|
|
} else if (status == PFS_ERR_NEW_PACK) {
|
|
ret = CONTROLLER_PAK_CHANGED;
|
|
} else {
|
|
ret = CONTROLLER_PAK_INCONSISTENT;
|
|
}
|
|
} else {
|
|
ret = CONTROLLER_PAK_GOOD;
|
|
}
|
|
start_reading_controller_data(controllerIndex);
|
|
return ret;
|
|
}
|
|
|
|
s32 get_controller_pak_file_list(s32 controllerIndex, s32 maxNumOfFilesToGet, u8 **fileNames, u8 **fileExtensions, u32 *fileSizes, u8 *fileTypes) {
|
|
OSPfsState state;
|
|
s32 ret;
|
|
s32 maxNumOfFilesOnCpak;
|
|
s32 files_used;
|
|
s8 *temp_D_800DE440;
|
|
s32 i;
|
|
u32 gameCode;
|
|
|
|
ret = get_si_device_status(controllerIndex);
|
|
if (ret != CONTROLLER_PAK_GOOD) {
|
|
start_reading_controller_data(controllerIndex);
|
|
return (controllerIndex << 30) | ret;
|
|
}
|
|
|
|
if (osPfsNumFiles(&pfs[controllerIndex], &maxNumOfFilesOnCpak, &files_used) != 0) {
|
|
start_reading_controller_data(controllerIndex);
|
|
return (controllerIndex << 30) | CONTROLLER_PAK_BAD_DATA;
|
|
}
|
|
|
|
if (get_language() == LANGUAGE_JAPANESE) {
|
|
gameCode = JPN_GAME_CODE;
|
|
} else if (osTvType == TV_TYPE_PAL) {
|
|
gameCode = PAL_GAME_CODE;
|
|
} else {
|
|
gameCode = NTSC_GAME_CODE;
|
|
}
|
|
|
|
if (maxNumOfFilesToGet < maxNumOfFilesOnCpak) {
|
|
maxNumOfFilesOnCpak = maxNumOfFilesToGet;
|
|
}
|
|
|
|
if (D_800DE440 != NULL) {
|
|
free_from_memory_pool(D_800DE440);
|
|
}
|
|
|
|
files_used = maxNumOfFilesOnCpak * 24;
|
|
D_800DE440 = allocate_from_main_pool_safe(files_used, COLOUR_TAG_BLACK);
|
|
bzero(D_800DE440, files_used);
|
|
temp_D_800DE440 = D_800DE440;
|
|
|
|
//TODO: There's probably an unidentified struct here
|
|
for (i = 0; i < maxNumOfFilesOnCpak; i++) {
|
|
fileNames[i] = (u8 *) temp_D_800DE440;
|
|
temp_D_800DE440 += 0x12;
|
|
fileExtensions[i] = (u8 *) temp_D_800DE440;
|
|
fileSizes[i] = 0;
|
|
fileTypes[i] = -1;
|
|
temp_D_800DE440 += 6;
|
|
}
|
|
|
|
while (i < maxNumOfFilesToGet) {
|
|
fileExtensions[i] = 0;
|
|
fileNames[i] = 0;
|
|
fileSizes[i] = 0;
|
|
fileTypes[i] = -1;
|
|
i++;
|
|
}
|
|
|
|
for (i = 0; i < maxNumOfFilesOnCpak; i++) {
|
|
ret = osPfsFileState(&pfs[controllerIndex], i, &state);
|
|
if (ret == PFS_ERR_INVALID) {
|
|
fileNames[i] = 0;
|
|
continue;
|
|
}
|
|
|
|
if (ret != 0) {
|
|
start_reading_controller_data(controllerIndex);
|
|
return CONTROLLER_PAK_BAD_DATA;
|
|
}
|
|
|
|
font_codes_to_string((u8 *)&state.game_name, (u8 *)fileNames[i], PFS_FILE_NAME_LEN);
|
|
font_codes_to_string((u8 *)&state.ext_name, (u8 *)fileExtensions[i], PFS_FILE_EXT_LEN);
|
|
fileSizes[i] = state.file_size;
|
|
fileTypes[i] = 6; // Unknown file type? Possibly from another game?
|
|
|
|
if ((state.game_code == gameCode) && (state.company_code == COMPANY_CODE)) {
|
|
fileTypes[i] = get_file_type(controllerIndex, i);
|
|
}
|
|
}
|
|
|
|
start_reading_controller_data(controllerIndex);
|
|
return CONTROLLER_PAK_GOOD;
|
|
}
|
|
|
|
//Free D_800DE440
|
|
void func_80076164(void) {
|
|
if (D_800DE440 != 0) {
|
|
free_from_memory_pool(D_800DE440);
|
|
}
|
|
D_800DE440 = 0;
|
|
}
|
|
|
|
//Get Available Space in Controller Pak
|
|
s32 get_free_space(s32 controllerIndex, u32 *bytesFree, s32 *notesFree) {
|
|
s32 ret;
|
|
s32 bytesNotUsed;
|
|
s32 maxNotes;
|
|
s32 notesUsed;
|
|
|
|
ret = get_si_device_status(controllerIndex);
|
|
if (ret == CONTROLLER_PAK_GOOD) {
|
|
if (bytesFree != 0) {
|
|
ret = osPfsFreeBlocks(&pfs[controllerIndex], &bytesNotUsed);
|
|
if (ret != 0) {
|
|
start_reading_controller_data(controllerIndex);
|
|
return (controllerIndex << 30) | CONTROLLER_PAK_BAD_DATA;
|
|
}
|
|
*bytesFree = bytesNotUsed;
|
|
}
|
|
if (notesFree != 0) {
|
|
ret = osPfsNumFiles(&pfs[controllerIndex], &maxNotes, ¬esUsed);
|
|
if (ret != 0) {
|
|
start_reading_controller_data(controllerIndex);
|
|
return (controllerIndex << 30) | CONTROLLER_PAK_BAD_DATA;
|
|
}
|
|
if (notesUsed >= 16) {
|
|
*notesFree = 0;
|
|
} else {
|
|
*notesFree = 16 - notesUsed;
|
|
}
|
|
}
|
|
} else {
|
|
ret |= controllerIndex << 30;
|
|
}
|
|
start_reading_controller_data(controllerIndex);
|
|
return ret;
|
|
}
|
|
|
|
s32 delete_file(s32 controllerIndex, s32 fileNum) {
|
|
OSPfsState state;
|
|
s32 ret;
|
|
|
|
ret = get_si_device_status(controllerIndex);
|
|
if (ret != CONTROLLER_PAK_GOOD) {
|
|
start_reading_controller_data(controllerIndex);
|
|
return (controllerIndex << 30) | ret;
|
|
}
|
|
|
|
ret = (controllerIndex << 30) | CONTROLLER_PAK_BAD_DATA;
|
|
|
|
if (osPfsFileState(&pfs[controllerIndex], fileNum, &state) == 0) {
|
|
if (osPfsDeleteFile(&pfs[controllerIndex], state.company_code, state.game_code, (u8 *)&state.game_name, (u8 *)&state.ext_name) == 0) {
|
|
ret = CONTROLLER_PAK_GOOD;
|
|
}
|
|
}
|
|
|
|
start_reading_controller_data(controllerIndex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Copies a file from one controller pak to the other
|
|
s32 copy_controller_pak_data(s32 controllerIndex, s32 fileNumber, s32 secondControllerIndex) {
|
|
UNUSED s32 pad;
|
|
u8 fileName[PFS_FILE_NAME_LEN];
|
|
UNUSED s32 pad2;
|
|
u8 fileExt[PFS_FILE_EXT_LEN];
|
|
OSPfsState state;
|
|
s32 status;
|
|
u8 *alloc;
|
|
|
|
status = get_si_device_status(controllerIndex);
|
|
if (status != CONTROLLER_PAK_GOOD) {
|
|
start_reading_controller_data(controllerIndex);
|
|
return (controllerIndex << 30) | status;
|
|
}
|
|
|
|
if (osPfsFileState(&pfs[controllerIndex], fileNumber, &state) != 0) {
|
|
start_reading_controller_data(controllerIndex);
|
|
return (controllerIndex << 30) | CONTROLLER_PAK_BAD_DATA;
|
|
}
|
|
|
|
alloc = allocate_from_main_pool_safe(state.file_size, COLOUR_TAG_BLACK);
|
|
|
|
status = read_data_from_controller_pak(controllerIndex, fileNumber, alloc, state.file_size);
|
|
start_reading_controller_data(controllerIndex);
|
|
if (status != CONTROLLER_PAK_GOOD) {
|
|
free_from_memory_pool(alloc);
|
|
return (controllerIndex << 30) | status;
|
|
}
|
|
|
|
font_codes_to_string((u8 *)&state.game_name, fileName, PFS_FILE_NAME_LEN);
|
|
font_codes_to_string((u8 *)&state.ext_name, fileExt, PFS_FILE_EXT_LEN);
|
|
|
|
status = write_controller_pak_file(secondControllerIndex, -1, fileName, fileExt, alloc, state.file_size);
|
|
if (status != CONTROLLER_PAK_GOOD) {
|
|
status |= (secondControllerIndex << 30);
|
|
}
|
|
|
|
free_from_memory_pool(alloc);
|
|
return status;
|
|
}
|
|
|
|
// There is likely a struct that looks like this in here.
|
|
// typedef struct fileStruct {
|
|
// s32 fileType;
|
|
// u8 game_name[PFS_FILE_NAME_LEN];
|
|
// } fileStruct;
|
|
SIDeviceStatus get_file_number(s32 controllerIndex, u8 *fileName, u8 *fileExt, s32 *fileNumber) {
|
|
u32 gameCode;
|
|
u8 fileNameAsFontCodes[PFS_FILE_NAME_LEN];
|
|
UNUSED s32 pad;
|
|
u8 fileExtAsFontCodes[PFS_FILE_EXT_LEN];
|
|
UNUSED s32 pad2;
|
|
s32 ret;
|
|
|
|
string_to_font_codes(fileName, fileNameAsFontCodes, PFS_FILE_NAME_LEN);
|
|
string_to_font_codes(fileExt, fileExtAsFontCodes, PFS_FILE_EXT_LEN);
|
|
|
|
if (get_language() == LANGUAGE_JAPANESE) {
|
|
gameCode = JPN_GAME_CODE;
|
|
} else if (osTvType == TV_TYPE_PAL) {
|
|
gameCode = PAL_GAME_CODE;
|
|
} else {
|
|
gameCode = NTSC_GAME_CODE;
|
|
}
|
|
|
|
|
|
ret = osPfsFindFile(&pfs[controllerIndex], COMPANY_CODE, gameCode, (u8 *)fileNameAsFontCodes, (u8 *)fileExtAsFontCodes, fileNumber);
|
|
if (ret == 0) {
|
|
return CONTROLLER_PAK_GOOD;
|
|
}
|
|
if ((ret == PFS_ERR_NOPACK) || (ret == PFS_ERR_DEVICE)) {
|
|
return NO_CONTROLLER_PAK;
|
|
}
|
|
if (ret == PFS_ERR_INCONSISTENT) {
|
|
return CONTROLLER_PAK_INCONSISTENT;
|
|
}
|
|
if (ret == PFS_ERR_ID_FATAL) {
|
|
return CONTROLLER_PAK_WITH_BAD_ID;
|
|
}
|
|
if (ret == PFS_ERR_INVALID) {
|
|
return CONTROLLER_PAK_CHANGED;
|
|
}
|
|
|
|
return CONTROLLER_PAK_BAD_DATA;
|
|
}
|
|
|
|
SIDeviceStatus read_data_from_controller_pak(s32 controllerIndex, s32 fileNum, u8 *data, s32 dataLength) {
|
|
s32 readResult = osPfsReadWriteFile(&pfs[controllerIndex], fileNum, PFS_READ, 0, dataLength, data);
|
|
|
|
if (readResult == 0) {
|
|
return CONTROLLER_PAK_GOOD;
|
|
}
|
|
if ((readResult == PFS_ERR_NOPACK) || (readResult == PFS_ERR_DEVICE)) {
|
|
return NO_CONTROLLER_PAK;
|
|
}
|
|
if (readResult == PFS_ERR_INCONSISTENT) {
|
|
return CONTROLLER_PAK_INCONSISTENT;
|
|
}
|
|
if (readResult == PFS_ERR_ID_FATAL) {
|
|
return CONTROLLER_PAK_WITH_BAD_ID;
|
|
}
|
|
if (readResult == PFS_ERR_INVALID) {
|
|
return CONTROLLER_PAK_CHANGED;
|
|
}
|
|
|
|
return CONTROLLER_PAK_BAD_DATA;
|
|
}
|
|
|
|
//If fileNumber -1, it creates a new file?
|
|
SIDeviceStatus write_controller_pak_file(s32 controllerIndex, s32 fileNumber, u8 *fileName, u8 *fileExt, u8 *dataToWrite, s32 fileSize) {
|
|
s32 temp;
|
|
u8 fileNameAsFontCodes[PFS_FILE_NAME_LEN];
|
|
UNUSED s32 temp2;
|
|
u8 fileExtAsFontCodes[PFS_FILE_EXT_LEN];
|
|
s32 ret;
|
|
s32 file_number;
|
|
s32 bytesToSave;
|
|
u32 game_code;
|
|
|
|
ret = get_si_device_status(controllerIndex);
|
|
if (ret != 0) {
|
|
start_reading_controller_data(controllerIndex);
|
|
return ret;
|
|
}
|
|
|
|
bytesToSave = fileSize;
|
|
temp = fileSize & 0xFF;
|
|
if (temp != 0) {
|
|
bytesToSave = (fileSize - temp) + 0x100;
|
|
}
|
|
|
|
string_to_font_codes(fileName, fileNameAsFontCodes, PFS_FILE_NAME_LEN);
|
|
string_to_font_codes(fileExt, fileExtAsFontCodes, PFS_FILE_EXT_LEN);
|
|
|
|
if (get_language() == LANGUAGE_JAPANESE) {
|
|
game_code = JPN_GAME_CODE;
|
|
} else if (osTvType == TV_TYPE_PAL) {
|
|
game_code = PAL_GAME_CODE;
|
|
} else {
|
|
game_code = NTSC_GAME_CODE;
|
|
}
|
|
|
|
ret = get_file_number(controllerIndex, fileName, fileExt, &file_number);
|
|
if (ret == CONTROLLER_PAK_GOOD) {
|
|
if (fileNumber != -1 && fileNumber != file_number) {
|
|
ret = CONTROLLER_PAK_BAD_DATA;
|
|
}
|
|
} else if (ret == CONTROLLER_PAK_CHANGED) {
|
|
if (fileNumber != -1) {
|
|
ret = CONTROLLER_PAK_BAD_DATA;
|
|
} else {
|
|
temp = osPfsAllocateFile(&pfs[controllerIndex], COMPANY_CODE, game_code, fileNameAsFontCodes, fileExtAsFontCodes, bytesToSave, &file_number);
|
|
if (temp == 0) {
|
|
ret = CONTROLLER_PAK_GOOD;
|
|
} else if (temp == PFS_DATA_FULL || temp == PFS_DIR_FULL) {
|
|
ret = CONTROLLER_PAK_FULL;
|
|
}
|
|
else {
|
|
ret = CONTROLLER_PAK_BAD_DATA;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ret == CONTROLLER_PAK_GOOD) {
|
|
temp = osPfsReadWriteFile(&pfs[controllerIndex], file_number, PFS_WRITE, 0, bytesToSave, dataToWrite);
|
|
if (temp == 0) {
|
|
ret = CONTROLLER_PAK_GOOD;
|
|
} else if ((temp == PFS_ERR_NOPACK) || (temp == PFS_ERR_DEVICE)) {
|
|
ret = NO_CONTROLLER_PAK;
|
|
} else if (temp == PFS_ERR_INCONSISTENT) {
|
|
ret = CONTROLLER_PAK_INCONSISTENT;
|
|
} else if (temp == PFS_ERR_ID_FATAL) {
|
|
ret = CONTROLLER_PAK_WITH_BAD_ID;
|
|
}
|
|
else {
|
|
ret = CONTROLLER_PAK_BAD_DATA;
|
|
}
|
|
}
|
|
|
|
start_reading_controller_data(controllerIndex);
|
|
return ret;
|
|
}
|
|
|
|
//Get File size for given controller's Control Pak
|
|
SIDeviceStatus get_file_size(s32 controllerIndex, s32 fileNum, s32 *fileSize) {
|
|
OSPfsState state;
|
|
|
|
*fileSize = 0;
|
|
if (osPfsFileState(&pfs[controllerIndex], fileNum, &state) == 0) {
|
|
*fileSize = state.file_size;
|
|
return CONTROLLER_PAK_GOOD;
|
|
}
|
|
return CONTROLLER_PAK_BAD_DATA;
|
|
}
|
|
|
|
//Converts N64 Font codes used in controller pak file names, into C ASCII a coded string
|
|
u8 *font_codes_to_string(u8 *inString, u8 *outString, s32 stringLength) {
|
|
s32 index = *inString;
|
|
u8 *ret = outString;
|
|
|
|
while (index != 0 && stringLength != 0) {
|
|
// Less than sizeof(gN64FontCodes) - 3.
|
|
// So basically make sure it's a valid font code value
|
|
if (index <= 65) {
|
|
*outString = gN64FontCodes[index];
|
|
outString++;
|
|
} else {
|
|
//Replace invalid characters with a hyphen
|
|
*outString = '-';
|
|
outString++;
|
|
}
|
|
|
|
inString++;
|
|
stringLength--;
|
|
index = *inString;
|
|
}
|
|
|
|
//Pad any extra characters with NULL font code
|
|
while (stringLength != 0) {
|
|
*outString = 0;
|
|
stringLength--;
|
|
outString++;
|
|
}
|
|
|
|
*outString = 0;
|
|
return ret;
|
|
}
|
|
|
|
//Converts a C ASCII string into N64 Font codes for controller pak file names
|
|
u8 *string_to_font_codes(u8 *inString, u8 *outString, s32 stringLength) {
|
|
s32 i;
|
|
u8 currentChar;
|
|
u8 *ret = outString;
|
|
|
|
while (*inString != 0 && stringLength != 0) {
|
|
*outString = 0;
|
|
for (i = 0; i < 65; i++) {
|
|
currentChar = *inString;
|
|
if (currentChar == gN64FontCodes[i]) {
|
|
*outString = i;
|
|
outString++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
inString++;
|
|
stringLength--;
|
|
}
|
|
|
|
while (stringLength != 0) {
|
|
*outString = 0;
|
|
stringLength--;
|
|
outString++;
|
|
}
|
|
|
|
*outString = 0;
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* For the given controller, and file, this will return a value for which type of file it is.
|
|
* 3 = GAMD / Game Data
|
|
* 4 = TIMD / Time Data
|
|
* 5 = GHSS / Ghost Data
|
|
* 6 = Unknown? Possibly from another game?
|
|
*/
|
|
s32 get_file_type(s32 controllerIndex, s32 fileNum) {
|
|
s32 *data;
|
|
UNUSED s32 pad;
|
|
s32 ret;
|
|
|
|
ret = 6;
|
|
data = allocate_from_main_pool_safe(0x100, COLOUR_TAG_BLACK);
|
|
if (read_data_from_controller_pak(controllerIndex, fileNum, (u8 *)data, 0x100) == CONTROLLER_PAK_GOOD) {
|
|
switch (*data) {
|
|
case GAMD:
|
|
ret = 3;
|
|
break;
|
|
case TIMD:
|
|
ret = 4;
|
|
break;
|
|
case GHSS:
|
|
ret = 5;
|
|
break;
|
|
default:
|
|
ret = 6;
|
|
break;
|
|
}
|
|
}
|
|
free_from_memory_pool(data);
|
|
return ret;
|
|
}
|