Dump GBA RTC data in metadata and saves (#205)

* Add barebones RTC (actually GPIO) detect

* RTC reading gotta clean up

* Nicer printing

* Add date sanity check (detect RTC presence)

* Append RTC data to dumped saves mGBA-style if present

* Actually allow restoring save with RTC included
This commit is contained in:
Metroid Maniac 2023-01-01 07:10:42 +00:00 committed by GitHub
parent 854b16d8a1
commit 24d3a0ae66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 160 additions and 26 deletions

View File

@ -6,24 +6,22 @@
#include <string>
#include <time.h>
/**
* Get the current time formatted for the top bar.
* @return std::string containing the time.
*/
std::string RetTime()
{
return RetTime(STR_TIME_FORMAT.c_str());
}
/**
* Get the current time formatted as specified.
* @return std::string containing the time.
*/
std::string RetTime(const char *format)
std::string RetTime(const char *format, time_t *raw)
{
time_t raw;
time(&raw);
const struct tm *Time = localtime(&raw);
if (!format)
{
format = STR_TIME_FORMAT.c_str();
}
time_t systime;
if (!raw) {
raw = &systime;
time(raw);
}
const struct tm *Time = localtime(raw);
char tmp[64];
strftime(tmp, sizeof(tmp), format, Time);

View File

@ -4,15 +4,11 @@
#include <string>
/**
* Get the current time formatted for the top bar.
* Format the time as specified.
* If no format is specified, use format for the top bar.
* If no time is specified, use the system time.
* @return std::string containing the time.
*/
std::string RetTime();
/**
* Get the current time formatted as specified.
* @return std::string containing the time.
*/
std::string RetTime(const char *format);
std::string RetTime(const char *format = nullptr, time_t *raw = nullptr);
#endif // DATE_H

View File

@ -964,6 +964,14 @@ void gbaCartSaveDump(const char *filename) {
FILE *destinationFile = fopen(filename, "wb");
fwrite(buffer, 1, size, destinationFile);
u8 cartRtc[RTC_SIZE];
if (gbaGetRtc(cartRtc)) {
fwrite(cartRtc, 1, RTC_SIZE, destinationFile);
u64 systime = time(nullptr);
fwrite(&systime, 1, 8, destinationFile);
}
fclose(destinationFile);
delete[] buffer;
}
@ -996,7 +1004,7 @@ void gbaCartSaveRestore(const char *filename) {
fseek(sourceFile, 0, SEEK_END);
size_t length = ftell(sourceFile);
fseek(sourceFile, 0, SEEK_SET);
if(length != size) {
if(length != size && length != size + 16) {
fclose(sourceFile);
dumpFailMsg(STR_SAVE_SIZE_MISMATCH_CART);
@ -1254,6 +1262,15 @@ void gbaCartDump(void) {
if(saveType == SAVE_GBA_FLASH_64 || saveType == SAVE_GBA_FLASH_128)
fprintf(destinationFile, "Save chip ID : 0x%04X\n", gbaGetFlashId());
u8 cartRtc[RTC_SIZE];
if (gbaGetRtc(cartRtc)) {
struct tm cartTm = gbaRtcToTm(cartRtc);
time_t cartTime = mktime(&cartTm);
fprintf(destinationFile,
"Cart time : %s\n",
RetTime("%Y-%m-%d %H:%M:%S", &cartTime).c_str());
}
fprintf(destinationFile,
"Timestamp : %s\n"
"GM9i Version : " VER_NUMBER "\n",

View File

@ -196,7 +196,8 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) {
if(extension(entry->name, {"sav", "sav1", "sav2", "sav3", "sav4", "sav5", "sav6", "sav7", "sav8", "sav9"})) {
if(!(io_dldi_data->ioInterface.features & FEATURE_SLOT_NDS) || entry->size <= (1 << 20))
operations.push_back(FileOperation::restoreSaveNds);
if(isRegularDS && (entry->size == 512 || entry->size == 8192 || entry->size == 32768 || entry->size == 65536 || entry->size == 131072))
if(isRegularDS && (entry->size == 512 || entry->size == 8192 || entry->size == 32768 || entry->size == 65536 || entry->size == 131072
|| entry->size == 528 || entry->size == 8208 || entry->size == 32784 || entry->size == 65552 || entry->size == 131088))
operations.push_back(FileOperation::restoreSaveGba);
}
if(currentDrive != Drive::fatImg && extension(entry->name, {"img", "sd", "sav", "pub", "pu1", "pu2", "pu3", "pu4", "pu5", "pu6", "pu7", "pu8", "pu9", "prv", "pr1", "pr2", "pr3", "pr4", "pr5", "pr6", "pr7", "pr8", "pr9", "0000"})) {

View File

@ -45,8 +45,6 @@
inline u32 min(u32 i, u32 j) { return (i < j) ? i : j;}
inline u32 max(u32 i, u32 j) { return (i > j) ? i : j;}
// -----------------------------------------------------
#define MAGIC_EEPR 0x52504545
#define MAGIC_SRAM 0x4d415253
@ -54,7 +52,6 @@ inline u32 max(u32 i, u32 j) { return (i > j) ? i : j;}
#define MAGIC_H1M_ 0x5f4d3148
// -----------------------------------------------------------
bool gbaIsGame()
{
@ -380,3 +377,115 @@ bool gbaFormatSave(saveTypeGBA type)
}
return true;
}
#define GPIO_DAT (*(vu16*) 0x080000c4)
#define GPIO_DIR (*(vu16*) 0x080000c6)
#define GPIO_CNT (*(vu16*) 0x080000c8)
#define RTC_CMD_READ(x) (((x)<<1) | 0x61)
#define RTC_CMD_WRITE(x) (((x)<<1) | 0x60)
static void rtcEnable()
{
GPIO_CNT = 1;
}
static void rtcDisable()
{
GPIO_CNT = 0;
}
static void rtcWriteCmd(u8 cmd)
{
int l;
u16 b;
u16 v = cmd <<1;
for(l=7; l>=0; l--)
{
b = (v>>l) & 0x2;
GPIO_DAT = b | 4;
GPIO_DAT = b | 4;
GPIO_DAT = b | 4;
GPIO_DAT = b | 5;
}
}
static void rtcWriteData(u8 data)
{
int l;
u16 b;
u16 v = data <<1;
for(l=0; l<8; l++)
{
b = (v>>l) & 0x2;
GPIO_DAT = b | 4;
GPIO_DAT = b | 4;
GPIO_DAT = b | 4;
GPIO_DAT = b | 5;
}
}
static u8 rtcReadData()
{
int j,l;
u16 b;
int v = 0;
for(l=0; l<8; l++)
{
for(j=0;j<5; j++)
GPIO_DAT = 4;
GPIO_DAT = 5;
b = GPIO_DAT;
v = v | ((b & 2)<<l);
}
v = v>>1;
return v;
}
bool gbaGetRtc(u8 *rtc)
{
rtcEnable();
int i;
GPIO_DAT = 1;
GPIO_DIR = 7;
GPIO_DAT = 1;
GPIO_DAT = 5;
rtcWriteCmd(RTC_CMD_READ(2));
GPIO_DIR = 5;
for(i=0; i<4; i++)
rtc[i] = rtcReadData();
GPIO_DIR = 5;
for(i=4; i<7; i++)
rtc[i] = rtcReadData();
GPIO_DAT = 1;
GPIO_DIR = 7;
GPIO_DAT = 1;
GPIO_DAT = 5;
rtcWriteCmd(RTC_CMD_READ(4));
GPIO_DIR = 5;
rtc[7] = rtcReadData();
rtcDisable();
// Month must be 1 to 12 in BCD for valid RTC
// If month is 0, invalid RTC
return rtc[RTC_MONTH] >= 0x01 && rtc[RTC_MONTH] <= 0x12;
}
static uint8_t unBCD(uint8_t byte) {
return (byte >> 4) * 10 + (byte & 0xF);
}
struct tm gbaRtcToTm(const u8 *rtc)
{
struct tm res;
res.tm_year = unBCD(rtc[RTC_YEAR]) + 100;
res.tm_mon = unBCD(rtc[RTC_MONTH]) - 1;
res.tm_mday = unBCD(rtc[RTC_DAY]);
res.tm_hour = unBCD(rtc[RTC_HOUR]);
res.tm_min = unBCD(rtc[RTC_MINUTE]);
res.tm_sec = unBCD(rtc[RTC_SECOND]);
res.tm_isdst = -1;
return res;
}

View File

@ -34,6 +34,17 @@ enum saveTypeGBA {
SAVE_GBA_FLASH_128 // 128k
};
enum GbaRtc {
RTC_YEAR,
RTC_MONTH,
RTC_DAY,
RTC_WEEKDAY,
RTC_HOUR,
RTC_MINUTE,
RTC_SECOND,
RTC_CONTROL,
RTC_SIZE
};
// --------------------
bool gbaIsGame();
@ -46,5 +57,7 @@ bool gbaReadSave(u8 *dst, u32 src, u32 len, saveTypeGBA type);
bool gbaWriteSave(u32 dst, u8 *src, u32 len, saveTypeGBA type);
bool gbaFormatSave(saveTypeGBA type);
bool gbaGetRtc(u8 *rtc);
struct tm gbaRtcToTm(const u8 *rtc);
#endif // __GBA_H__