Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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
  • Loading branch information
metroid-maniac committed Jan 1, 2023
1 parent 854b16d commit 24d3a0a
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 26 deletions.
24 changes: 11 additions & 13 deletions arm9/source/date.cpp
Expand Up @@ -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);
Expand Down
12 changes: 4 additions & 8 deletions arm9/source/date.h
Expand Up @@ -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
19 changes: 18 additions & 1 deletion arm9/source/dumpOperations.cpp
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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",
Expand Down
3 changes: 2 additions & 1 deletion arm9/source/file_browse.cpp
Expand Up @@ -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"})) {
Expand Down
115 changes: 112 additions & 3 deletions arm9/source/gba.cpp
Expand Up @@ -45,16 +45,13 @@
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
#define MAGIC_FLAS 0x53414c46

#define MAGIC_H1M_ 0x5f4d3148


// -----------------------------------------------------------
bool gbaIsGame()
{
Expand Down Expand Up @@ -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;
}
13 changes: 13 additions & 0 deletions arm9/source/gba.h
Expand Up @@ -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();
Expand All @@ -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__

0 comments on commit 24d3a0a

Please sign in to comment.