botw-completer/source/save.c
badbl0cks 6bff4b6fcd
Refactor codebase and complete implementation
- Consolidate development files into final implementation
- Replace basic prototypes with full feature implementations
- Add complete hash-based save data access system
- Implement advanced map pin management algorithms
- Add comprehensive error handling and user feedback
- Complete Korok seed database with all 908 locations
2023-05-24 09:08:09 -07:00

136 lines
No EOL
4.1 KiB
C

#include <string.h>
#include <stdio.h>
#include <dirent.h>
#include <stdlib.h>
#include <math.h>
#include <switch.h>
#include "save.h"
#include "ui.h"
#include "map.h"
gameDataSav GAMEDATASAV;
const u32 KOROK_COUNTER_HASH = 0x8a94e07a;
void openSave() {
GAMEDATASAV.saveFile = NULL;
FILE *fp = fopen("save:/5/game_data.sav", "rb"); //TODO: Dont hardcode save slot
if (fp != NULL) {
/* Go to the end of the file. */
if (fseek(fp, 0L, SEEK_END) == 0) {
/* Get the size of the file. */
long bufsize = ftell(fp);
if (bufsize == -1) { /* Error */ }
/* Allocate our buffer to that size. */
GAMEDATASAV.saveFile = malloc(sizeof(char) * (bufsize));
/* Go back to the start of the file. */
if (fseek(fp, 0L, SEEK_SET) != 0) { /* Error */ }
/* Read the entire file into memory. */
GAMEDATASAV.length = fread(GAMEDATASAV.saveFile, sizeof(char), bufsize, fp);
if (ferror( fp ) != 0) {
printMessage("Error reading file.");
} else {
printMessage("Read %.2f kbytes successfully.", GAMEDATASAV.length/1024.0);
}
}
fclose(fp);
} else {
printMessage("Error opening file.");
}
}
void processSave() {
countMapPins();
printMessage("number of korok seeds: %d.", readU32FromHash(KOROK_COUNTER_HASH));
loadPlayerLocation();
}
u32 getSaveSize() {
return GAMEDATASAV.length;
}
void writeSave() {
FILE *fp = fopen("save:/5/game_data.sav", "wb"); //TODO: dont hardcode save slot
if (fp != NULL) {
size_t bytes_writen = fwrite(GAMEDATASAV.saveFile, sizeof(char), GAMEDATASAV.length, fp);
if (ferror( fp ) != 0) {
printMessage("Error writing file.");
} else {
printMessage("Wrote %f.2 kbytes successfully.", bytes_writen/1024.0);
}
fclose(fp);
} else {
printMessage("Error opening file for writing.");
}
}
u32 _searchHash(u32 hash) {
for(u32 i=0x0c; i<GAMEDATASAV.length; i+=8) {
u32 val = GAMEDATASAV.saveFile[i] | ( (u32)GAMEDATASAV.saveFile[i+1] << 8 ) | ( (u32)GAMEDATASAV.saveFile[i+2] << 16 ) | ( (u32)GAMEDATASAV.saveFile[i+3] << 24 );
if(hash == val) {
return i;
}
}
return 0;
}
u32 readU32FromHash(u32 hash) {
return readU32FromAddr(_searchHash(hash));
}
u32 readU32FromAddr(u32 addr) {
if(addr != 0) {
u32 val = ((u32)GAMEDATASAV.saveFile[addr+4]) | ( (u32)GAMEDATASAV.saveFile[addr+5] << 8 ) | ( (u32)GAMEDATASAV.saveFile[addr+6] << 16 ) | ( (u32)GAMEDATASAV.saveFile[addr+7] << 24 );
return val;
}
return 0;
}
float readF32FromHash(u32 hash, u32 offset) {
return readF32FromAddr(_searchHash(hash) + offset);
}
float readF32FromAddr(u32 addr) {
if(addr != 0) {
Float val;
val.m_bytes[0] = GAMEDATASAV.saveFile[addr+4];
val.m_bytes[1] = GAMEDATASAV.saveFile[addr+5];
val.m_bytes[2] = GAMEDATASAV.saveFile[addr+6];
val.m_bytes[3] = GAMEDATASAV.saveFile[addr+7];
return val.m_float;
}
return 0;
}
void writeF32ToAddr(u32 addr, float value) {
if(addr != 0) {
Float val;
val.m_float = value;
GAMEDATASAV.saveFile[addr+4] = val.m_bytes[0];
GAMEDATASAV.saveFile[addr+5] = val.m_bytes[1];
GAMEDATASAV.saveFile[addr+6] = val.m_bytes[2];
GAMEDATASAV.saveFile[addr+7] = val.m_bytes[3];
//printMessage("wrote f32 %f to 0x%x, read check: %f\n", value, addr, readF32FromAddr(addr));
}
}
void writeU32ToAddr(u32 addr, u32 value) {
if (addr != 0) {
GAMEDATASAV.saveFile[addr+4] = value & 0x00000ff;
GAMEDATASAV.saveFile[addr+5] = (value & 0x0000ff00) >> 8;
GAMEDATASAV.saveFile[addr+6] = (value & 0x00ff0000) >> 16;
GAMEDATASAV.saveFile[addr+7] = (value & 0xff000000) >> 24;
//printMessage("wrote u32 0x%x to 0x%x, read check: 0x%x", value, addr, readU32FromAddr(addr));
}
}
void closeAndCommitSave() {
writeSave();
if (R_FAILED(fsdevCommitDevice("save"))) {
printMessage("Committing failed.");
}
free(GAMEDATASAV.saveFile);
}