Implement TransferPakRTCReader and add a "Print Game Clock" menu option temporarily

This commit is contained in:
Philippe Symons
2025-03-04 13:12:58 +01:00
parent d106863b50
commit a6fb80f8c4
15 changed files with 220 additions and 5 deletions

View File

@@ -11,6 +11,9 @@ N64_ROM_REGIONFREE=1
N64_ROM_CONTROLLER_TYPE1=n64,pak=transfer
N64_ROM_TITLE="PokeMe64"
# Uncomment this when generating a dev build
N64_C_AND_CXX_FLAGS += -DDEV_BUILD
SRCS := $(shell find $(SOURCE_DIR) -type f -name '*.cpp')
OBJS := $(patsubst $(SOURCE_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(SRCS))

View File

@@ -116,4 +116,6 @@ void askConfirmationWipeSave(void* context, const void* param);
*/
void resetRTC(void* context, const void* param);
void printClock(void* context, const void* param);
#endif

View File

@@ -22,6 +22,7 @@ public:
void render(RDPQGraphics& gfx, const Rectangle& sceneBounds) override;
protected:
private:
char headerTextString_[128];
uint8_t fontIdMainFont_;
uint8_t fontMainFontStyleWhiteId_;
sprite_t* logoLibDragon_;

View File

@@ -32,6 +32,7 @@ private:
void loadSaveMetadata();
const char* getGameTypeString();
char pokeMe64String_[48];
sprite_t* menu9SliceSprite_;
TransferPakDetectionWidget tpakDetectWidget_;
WidgetFocusChainSegment tpakDetectWidgetSegment_;

View File

@@ -54,6 +54,11 @@ public:
*/
void switchGBROMBank(uint8_t bankIndex);
/**
* @brief Returns whether we called setRAMEnabled(true) on this TransferPakManager instance before
*/
bool isRAMEnabled() const;
/**
* @brief This function enables/disables gameboy RAM/RTC access.
* WARNING: it switches to transfer pak bank 0
@@ -110,6 +115,7 @@ protected:
private:
joypad_port_t port_;
bool isPoweredOn_;
bool ramEnabled_;
uint8_t currentSRAMBank_;
uint16_t readBufferBankOffset_;
uint16_t writeBufferSRAMBankOffset_;

View File

@@ -0,0 +1,28 @@
#ifndef _TRANSFERPAKRTCREADER_H
#define _TRANSFERPAKRTCREADER_H
#include "RTCReader.h"
class TransferPakManager;
/**
* @brief This class implements the IRTCReader interface specifically for the N64 Transfer Pak with libdragon
*/
class TransferPakRTCReader : public BaseRTCReader
{
public:
TransferPakRTCReader(TransferPakManager& tpakManager);
virtual ~TransferPakRTCReader();
/**
* This function will grab the current values of the RTC chip and store them internally
*
* Use the getXYZ() functions to actually retrieve this data
*/
void latch() override;
protected:
private:
TransferPakManager& tpakManager_;
};
#endif

8
include/version.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef _VERSION_H
#define _VERSION_H
#define POKEME64_VERSION_NUMBER "0.4"
const char* getPokeMe64VersionString();
#endif

View File

@@ -70,6 +70,10 @@ MenuItemData gen2MenuEntries[] = {
.title = "Reset Game Clock",
.onConfirmAction = resetRTC
},
{
.title = "Print Game Clock",
.onConfirmAction = printClock
},
{
.title = "About",
.onConfirmAction = goToAboutScene
@@ -107,6 +111,10 @@ MenuItemData gen2CrystalMenuEntries[] = {
.title = "Reset Game Clock",
.onConfirmAction = resetRTC
},
{
.title = "Print Game Clock",
.onConfirmAction = printClock
},
{
.title = "About",
.onConfirmAction = goToAboutScene

View File

@@ -10,6 +10,7 @@
#include "transferpak/TransferPakManager.h"
#include "transferpak/TransferPakRomReader.h"
#include "transferpak/TransferPakSaveManager.h"
#include "transferpak/TransferPakRTCReader.h"
#define POKEMON_CRYSTAL_ITEM_ID_GS_BALL 0x73
@@ -795,13 +796,47 @@ void resetRTC(void* context, const void* param)
TransferPakManager& tpakManager = scene->getDependencies().tpakManager;
TransferPakRomReader romReader(tpakManager);
TransferPakSaveManager saveManager(tpakManager);
TransferPakRTCReader rtcReader(tpakManager);
Gen2GameReader gameReader(romReader, saveManager, gameType, language);
tpakManager.setRAMEnabled(true);
gameReader.resetRTC();
gameReader.getClockManager(rtcReader).resetRTC();
tpakManager.finishWrites();
tpakManager.setRAMEnabled(false);
setDialogDataText(*diag, "The games' clock was reset! Start the game to reconfigure it! Don't forget to save!");
scene->showDialog(diag);
}
void printClock(void* context, const void* param)
{
MenuScene* scene = static_cast<MenuScene*>(context);
auto diag = new DialogData{
.shouldDeleteWhenDone = true
};
if(scene->getDependencies().generation != 2)
{
setDialogDataText(*diag, "Sorry! This is only supported for Gen 2 Pokémon games!");
scene->showDialog(diag);
return;
}
const Gen2GameType gameType = static_cast<Gen2GameType>(scene->getDependencies().specificGenVersion);
const Gen2LocalizationLanguage language = static_cast<Gen2LocalizationLanguage>(scene->getDependencies().localization);
TransferPakManager& tpakManager = scene->getDependencies().tpakManager;
TransferPakRomReader romReader(tpakManager);
TransferPakSaveManager saveManager(tpakManager);
TransferPakRTCReader rtcReader(tpakManager);
Gen2GameReader gameReader(romReader, saveManager, gameType, language);
Gen2ClockManager clockManager = gameReader.getClockManager(rtcReader);
tpakManager.setRAMEnabled(true);
clockManager.latchRTC();
tpakManager.setRAMEnabled(false);
setDialogDataText(*diag, "The current time is set to %s, %02hhu:%02hhu:%02hhu!\nRTC: %04hx %02hhx %02hhx %02hhx, off: %02hhx %02hhx %02hhx %02hhx", toString(clockManager.getDay()), clockManager.getHours(), clockManager.getMinutes(), clockManager.getSeconds(), rtcReader.getDaysCounter(), rtcReader.getHours(), rtcReader.getMinutes(), rtcReader.getSeconds(), clockManager.getRTCDayOffset(), clockManager.getRTCHourOffset(), clockManager.getRTCMinuteOffset(), clockManager.getRTCSecondOffset());
scene->showDialog(diag);
}

View File

@@ -1,6 +1,7 @@
#include "scenes/AboutScene.h"
#include "core/FontManager.h"
#include "scenes/SceneManager.h"
#include "version.h"
static const ScrollWidgetStyle scrollWidgetStyle = {
.scrollStep = 15,
@@ -37,7 +38,7 @@ TESTING/VALIDATION
MajorUpgrade
)delim";
static const char* headerTextString = R"delim(PokeMe64 Version 0.3
static const char* headerTextStringFormat = R"delim(PokeMe64 Version %s
by risingPhil
SPECIAL THANKS TO:
@@ -46,6 +47,7 @@ SPECIAL THANKS TO:
AboutScene::AboutScene(SceneDependencies& deps, void*)
: AbstractUIScene(deps)
, headerTextString_()
, fontIdMainFont_(1)
, fontMainFontStyleWhiteId_(0)
, logoLibDragon_(nullptr)
@@ -76,6 +78,7 @@ AboutScene::AboutScene(SceneDependencies& deps, void*)
})
, bButtonPressed_(false)
{
snprintf(headerTextString_, sizeof(headerTextString_), headerTextStringFormat, getPokeMe64VersionString());
fontIdMainFont_ = deps.fontManager.getFont("rom://Arial.font64");
const rdpq_fontstyle_t mainFontWhite = {
@@ -129,7 +132,7 @@ void AboutScene::init()
headerText_.setBounds(headerTextBounds);
headerText_.setStyle(headerTextStyle);
headerText_.setData(headerTextString);
headerText_.setData(headerTextString_);
scrollWidget_.addWidget(&headerText_);
const ImageWidgetStyle imgDragonStyle = {

View File

@@ -7,6 +7,7 @@
#include "gen1/Gen1GameReader.h"
#include "gen2/Gen2GameReader.h"
#include "menu/MenuEntries.h"
#include "version.h"
#include <unistd.h>
@@ -27,6 +28,7 @@ static void tpakWidgetStateChangedCallback(void* context, TransferPakWidgetState
InitTransferPakScene::InitTransferPakScene(SceneDependencies& deps, void*)
: SceneWithDialogWidget(deps)
, pokeMe64String_()
, menu9SliceSprite_(nullptr)
, tpakDetectWidget_(deps.animationManager, deps.tpakManager)
, tpakDetectWidgetSegment_(WidgetFocusChainSegment{
@@ -45,6 +47,8 @@ InitTransferPakScene::~InitTransferPakScene()
void InitTransferPakScene::init()
{
uint8_t systemEntropy[4];
snprintf(pokeMe64String_, sizeof(pokeMe64String_), "PokeMe64 by risingPhil. Version %s", getPokeMe64VersionString());
menu9SliceSprite_ = sprite_load("rom://menu-bg-9slice.sprite");
SceneWithDialogWidget::init();
@@ -77,7 +81,7 @@ void InitTransferPakScene::destroy()
void InitTransferPakScene::render(RDPQGraphics& gfx, const Rectangle& sceneBounds)
{
gfx.drawText(Rectangle{0, 10, 320, 16}, "PokeMe64 by risingPhil. Version 0.3", pokeMe64TextSettings_);
gfx.drawText(Rectangle{0, 10, 320, 16}, pokeMe64String_, pokeMe64TextSettings_);
tpakDetectWidget_.render(gfx, sceneBounds);
SceneWithDialogWidget::render(gfx, sceneBounds);

View File

@@ -11,6 +11,7 @@ static const uint16_t sramBankStartGBAddress = 0xA000;
TransferPakManager::TransferPakManager()
: port_(JOYPAD_PORT_1)
, isPoweredOn_(false)
, ramEnabled_(false)
, currentSRAMBank_(0)
, readBufferBankOffset_(0xFFFF)
, writeBufferSRAMBankOffset_(0xFFFF)
@@ -139,6 +140,11 @@ void TransferPakManager::switchGBROMBank(uint8_t bankIndex)
readBufferBankOffset_ = 0xFFFF;
}
bool TransferPakManager::isRAMEnabled() const
{
return ramEnabled_;
}
void TransferPakManager::setRAMEnabled(bool enabled)
{
uint8_t data[TPAK_BLOCK_SIZE];
@@ -157,6 +163,7 @@ void TransferPakManager::setRAMEnabled(bool enabled)
debugf("[TransferPakManager]: %s: ERROR: transfer pak not ready yet. Current status is %hu\r\n", __FUNCTION__, status);
status = getStatus();
}
ramEnabled_ = enabled;
}
void TransferPakManager::switchGBSRAMBank(uint8_t bankIndex)

View File

@@ -0,0 +1,57 @@
#include "transferpak/TransferPakRTCReader.h"
#include "transferpak/TransferPakManager.h"
TransferPakRTCReader::TransferPakRTCReader(TransferPakManager& tpakManager)
: tpakManager_(tpakManager)
{
}
TransferPakRTCReader::~TransferPakRTCReader()
{
}
void TransferPakRTCReader::latch()
{
uint8_t data[TPAK_BLOCK_SIZE];
const int port = tpakManager_.getPort();
// enable SRAM and RTC first
const bool previousRAMState = tpakManager_.isRAMEnabled();
tpakManager_.setRAMEnabled(true);
tpakManager_.finishWrites();
// now latch the RTC data into the RTC registers
// https://gbdev.io/pandocs/MBC3.html
memset(data, 0, sizeof(data));
tpak_write(port, 0x6000, data, TPAK_BLOCK_SIZE);
memset(data, 1, sizeof(data));
tpak_write(port, 0x6000, data, TPAK_BLOCK_SIZE);
// RTC S (seconds)
tpakManager_.switchGBSRAMBank(0x08);
tpak_read(port, 0xA000, data, sizeof(data));
seconds_ = data[0];
// RTC M (minutes)
tpakManager_.switchGBSRAMBank(0x09);
tpak_read(port, 0xA000, data, sizeof(data));
minutes_ = data[0];
// RTC H (hours)
tpakManager_.switchGBSRAMBank(0x0A);
tpak_read(port, 0xA000, data, sizeof(data));
hours_ = data[0];
// RTC DH (days counter upper 8 bits)
tpakManager_.switchGBSRAMBank(0x0C);
tpak_read(port, 0xA000, data, sizeof(data));
daysRaw_ = data[0];
daysRaw_ <<= 8;
// RTC DL (days counter lower 8 bits)
tpakManager_.switchGBSRAMBank(0x0B);
tpak_read(port, 0xA000, data, sizeof(data));
daysRaw_ |= data[0];
tpakManager_.setRAMEnabled(previousRAMState);
}

52
src/version.cpp Normal file
View File

@@ -0,0 +1,52 @@
#include "version.h"
#include <cstring>
#include <cstdio>
#include <cstdint>
#ifdef DEV_BUILD
static const char DEV_SUFFIX_STRING[] = "-dev";
static const char *months[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
static uint8_t get_month_number(const char *month)
{
for (uint8_t i = 0; i < 12; ++i)
{
if (strcmp(month, months[i]) == 0)
{
return i + 1;
}
}
return 0; // Should never happen
}
#endif
const char* getPokeMe64VersionString()
{
static char versionString[20];
memcpy(versionString, POKEME64_VERSION_NUMBER, sizeof(POKEME64_VERSION_NUMBER));
#ifdef DEV_BUILD
size_t currentStringLength;
char month[4];
uint16_t year;
int day;
uint8_t monthNumber;
strcat(versionString, DEV_SUFFIX_STRING);
// -2 to not count the null terminators
currentStringLength = sizeof(POKEME64_VERSION_NUMBER) + sizeof(DEV_SUFFIX_STRING) - 2;
// Parse __DATE__ ("MMM DD YYYY")
// We can't use strptime() because libdragon doesn't appear to have it.
sscanf(__DATE__, "%3s %d %hu", month, &day, &year);
monthNumber = get_month_number(month);
snprintf(versionString + currentStringLength, sizeof(versionString) - currentStringLength, "%04hu%02hhu%02d", year, monthNumber, day);
#endif
return versionString;
}