mirror of
https://github.com/rvtr/GodMode9i.git
synced 2025-11-02 00:11:07 -04:00
Improve dumping menu, add metadata dumping (#128)
* Improve dumping menu, add metadata dumping * Use `NONE` as fallback DS save instead of `UNK` Matches GM9 for my DSTT and バトル&ゲット:ポケモンタイピングDS * Fix metadata check for NAND save
This commit is contained in:
parent
b491b6f656
commit
aeb13dba29
@ -23,17 +23,17 @@ std::string RetTime()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current time formatted for filenames.
|
||||
* Get the current time formatted as specified.
|
||||
* @return std::string containing the time.
|
||||
*/
|
||||
std::string RetTimeForFilename()
|
||||
std::string RetTime(const char *format)
|
||||
{
|
||||
time_t raw;
|
||||
time(&raw);
|
||||
const struct tm *Time = localtime(&raw);
|
||||
|
||||
char tmp[8];
|
||||
strftime(tmp, sizeof(tmp), "%H%M%S", Time);
|
||||
char tmp[64];
|
||||
strftime(tmp, sizeof(tmp), format, Time);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
@ -10,9 +10,9 @@
|
||||
std::string RetTime();
|
||||
|
||||
/**
|
||||
* Get the current time formatted for filenames.
|
||||
* Get the current time formatted as specified.
|
||||
* @return std::string containing the time.
|
||||
*/
|
||||
std::string RetTimeForFilename();
|
||||
std::string RetTime(const char *format);
|
||||
|
||||
#endif // DATE_H
|
||||
|
||||
@ -35,6 +35,7 @@
|
||||
#include "fileOperations.h"
|
||||
#include "font.h"
|
||||
#include "language.h"
|
||||
#include "read_card.h"
|
||||
#include "startMenu.h"
|
||||
|
||||
#define ENTRIES_START_ROW 1
|
||||
@ -59,6 +60,8 @@ bool flashcardMountSkipped = true;
|
||||
static bool flashcardMountRan = true;
|
||||
static int dmCursorPosition = 0;
|
||||
static std::vector<DriveMenuOperation> dmOperations;
|
||||
static char romTitle[13] = {0};
|
||||
static u32 romSize, romSizeTrimmed;
|
||||
|
||||
static u8 gbaFixedValue = 0;
|
||||
|
||||
@ -104,7 +107,7 @@ void dm_drawTopScreen(void) {
|
||||
|| (ramdrive2Mounted && nitroCurrentDrive == Drive::ramDrive2)
|
||||
|| (nandMounted && nitroCurrentDrive == Drive::nand)
|
||||
|| (imgMounted && nitroCurrentDrive == Drive::fatImg)))
|
||||
font->print(256 - font->width(), i + 1, true, "[x]", Alignment::right, pal);
|
||||
font->print(-1, i + 1, true, "[x]", Alignment::right, pal);
|
||||
break;
|
||||
case DriveMenuOperation::fatImage:
|
||||
if ((sdMounted && imgCurrentDrive == Drive::sdCard)
|
||||
@ -115,16 +118,14 @@ void dm_drawTopScreen(void) {
|
||||
font->printf(0, i + 1, true, Alignment::left, pal, STR_FAT_LABEL_NAMED.c_str(), imgLabel[0] == 0 ? STR_UNTITLED.c_str() : imgLabel);
|
||||
} else {
|
||||
font->print(0, i + 1, true, STR_FAT_LABEL, Alignment::left, pal);
|
||||
font->print(256 - font->width(), i + 1, true, "[x]", Alignment::right, pal);
|
||||
font->print(-1, i + 1, true, "[x]", Alignment::right, pal);
|
||||
}
|
||||
break;
|
||||
case DriveMenuOperation::gbaCart:
|
||||
font->print(0, i + 1, true, STR_GBA_GAMECART, Alignment::left, pal);
|
||||
if (gbaFixedValue != 0x96)
|
||||
font->print(256 - font->width(), i + 1, true, "[x]", Alignment::right, pal);
|
||||
font->printf(0, i + 1, true, Alignment::left, pal, STR_GBA_GAMECART.c_str(), romTitle);
|
||||
break;
|
||||
case DriveMenuOperation::ndsCard:
|
||||
font->print(0, i + 1, true, STR_NDS_GAMECARD, Alignment::left, pal);
|
||||
font->printf(0, i + 1, true, Alignment::left, pal, STR_NDS_GAMECARD.c_str(), romTitle);
|
||||
break;
|
||||
case DriveMenuOperation::none:
|
||||
break;
|
||||
@ -176,16 +177,16 @@ void dm_drawBottomScreen(void) {
|
||||
font->printf(0, 2, false, Alignment::left, Palette::white, STR_N_FREE.c_str(), getDriveBytes(getBytesFree("fat:/")).c_str());
|
||||
break;
|
||||
case DriveMenuOperation::gbaCart:
|
||||
font->print(0, 0, false, STR_GBA_GAMECART);
|
||||
font->print(0, 1, false, STR_GBA_GAME);
|
||||
font->printf(0, 0, false, Alignment::left, Palette::white, STR_GBA_GAMECART.c_str(), romTitle);
|
||||
font->printf(0, 1, false, Alignment::left, Palette::white, STR_GBA_GAME.c_str(), getBytes(romSize).c_str());
|
||||
break;
|
||||
case DriveMenuOperation::nitroFs:
|
||||
font->print(0, 0, false, STR_NITROFS_LABEL);
|
||||
font->print(0, 1, false, STR_GAME_VIRTUAL);
|
||||
break;
|
||||
case DriveMenuOperation::ndsCard:
|
||||
font->print(0, 0, false, STR_NDS_GAMECARD);
|
||||
font->print(0, 1, false, STR_NDS_GAME);
|
||||
font->printf(0, 0, false, Alignment::left, Palette::white, STR_NDS_GAMECARD.c_str(), romTitle);
|
||||
font->printf(0, 1, false, Alignment::left, Palette::white, STR_NDS_GAME.c_str(), getBytes(romSize).c_str(), getBytes(romSizeTrimmed).c_str());
|
||||
break;
|
||||
case DriveMenuOperation::ramDrive1:
|
||||
font->print(0, 0, false, STR_RAMDRIVE1_LABEL);
|
||||
@ -237,10 +238,39 @@ void driveMenu (void) {
|
||||
dmOperations.push_back(DriveMenuOperation::nitroFs);
|
||||
if (expansionPakFound
|
||||
|| (io_dldi_data->ioInterface.features & FEATURE_SLOT_GBA)
|
||||
|| (isDSiMode() && !arm7SCFGLocked && !(REG_SCFG_MC & BIT(0))))
|
||||
|| (isDSiMode() && !arm7SCFGLocked && !(REG_SCFG_MC & BIT(0)))) {
|
||||
dmOperations.push_back(DriveMenuOperation::ndsCard);
|
||||
if (!isDSiMode() && isRegularDS)
|
||||
if(romTitle[0] == 0) {
|
||||
sNDSHeaderExt ndsHeader;
|
||||
cardInit(&ndsHeader);
|
||||
tonccpy(romTitle, ndsHeader.gameTitle, 12);
|
||||
romSize = 0x20000 << ndsHeader.deviceSize;
|
||||
romSizeTrimmed = (isDSiMode() && (ndsHeader.unitCode != 0) && (ndsHeader.twlRomSize > 0))
|
||||
? ndsHeader.twlRomSize : ndsHeader.romSize + 0x88;
|
||||
}
|
||||
} else if (!isDSiMode() && isRegularDS && gbaFixedValue == 0x96) {
|
||||
dmOperations.push_back(DriveMenuOperation::gbaCart);
|
||||
if(romTitle[0] == 0) {
|
||||
tonccpy(romTitle, (char*)0x080000A0, 12);
|
||||
romSize = 0;
|
||||
for (romSize = (1 << 20); romSize < (1 << 25); romSize <<= 1) {
|
||||
vu16 *rompos = (vu16*)(0x08000000 + romSize);
|
||||
bool romend = true;
|
||||
for (int j = 0; j < 0x1000; j++) {
|
||||
if (rompos[j] != j) {
|
||||
romend = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (romend)
|
||||
break;
|
||||
}
|
||||
romSizeTrimmed = romSize;
|
||||
}
|
||||
} else if (romTitle[0] != 0) {
|
||||
romTitle[0] = 0;
|
||||
romSizeTrimmed = romSize = 0;
|
||||
}
|
||||
|
||||
dm_drawBottomScreen();
|
||||
dm_drawTopScreen();
|
||||
|
||||
@ -9,12 +9,15 @@
|
||||
#include "read_card.h"
|
||||
#include "tonccpy.h"
|
||||
#include "language.h"
|
||||
#include "screenshot.h"
|
||||
#include "version.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <nds.h>
|
||||
#include <nds/arm9/dldi.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
extern u8 copyBuf[];
|
||||
|
||||
@ -22,6 +25,112 @@ extern bool expansionPakFound;
|
||||
|
||||
static sNDSHeaderExt ndsCardHeader;
|
||||
|
||||
enum DumpOption {
|
||||
none = 0,
|
||||
rom = 1,
|
||||
romTrimmed = 2,
|
||||
save = 4,
|
||||
metadata = 8,
|
||||
all = rom | save | metadata,
|
||||
allTrimmed = romTrimmed | save | metadata
|
||||
};
|
||||
|
||||
DumpOption dumpMenu(std::vector<DumpOption> allowedOptions, const char *dumpName) {
|
||||
u16 pressed = 0, held = 0;
|
||||
int optionOffset = 0;
|
||||
|
||||
char dumpToStr[256];
|
||||
snprintf(dumpToStr, sizeof(dumpToStr), STR_DUMP_TO.c_str(), dumpName, sdMounted ? "sd" : "fat");
|
||||
|
||||
int y = font->calcHeight(dumpToStr) + 1;
|
||||
|
||||
while (true) {
|
||||
font->clear(false);
|
||||
|
||||
font->print(0, 0, false, dumpToStr);
|
||||
|
||||
int row = y;
|
||||
for(DumpOption option : allowedOptions) {
|
||||
switch(option) {
|
||||
case DumpOption::all:
|
||||
font->print(3, row++, false, STR_DUMP_ALL);
|
||||
break;
|
||||
case DumpOption::allTrimmed:
|
||||
font->print(3, row++, false, STR_DUMP_ALL_TRIMMED);
|
||||
break;
|
||||
case DumpOption::rom:
|
||||
font->print(3, row++, false, STR_DUMP_ROM);
|
||||
break;
|
||||
case DumpOption::romTrimmed:
|
||||
font->print(3, row++, false, STR_DUMP_ROM_TRIMMED);
|
||||
break;
|
||||
case DumpOption::save:
|
||||
font->print(3, row++, false, STR_DUMP_SAVE);
|
||||
break;
|
||||
case DumpOption::metadata:
|
||||
font->print(3, row++, false, STR_DUMP_METADATA);
|
||||
break;
|
||||
case DumpOption::none:
|
||||
row++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
font->print(3, ++row, false, STR_A_SELECT_B_CANCEL);
|
||||
|
||||
// Show cursor
|
||||
font->print(0, y + optionOffset, false, "->");
|
||||
|
||||
font->update(false);
|
||||
|
||||
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
|
||||
do {
|
||||
// Print time
|
||||
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
|
||||
font->update(true);
|
||||
|
||||
scanKeys();
|
||||
pressed = keysDownRepeat();
|
||||
held = keysHeld();
|
||||
swiWaitForVBlank();
|
||||
} while (!(pressed & (KEY_UP| KEY_DOWN | KEY_A | KEY_B | KEY_L))
|
||||
#ifdef SCREENSWAP
|
||||
&& !(pressed & KEY_TOUCH)
|
||||
#endif
|
||||
);
|
||||
|
||||
if (pressed & KEY_UP)
|
||||
optionOffset--;
|
||||
if (pressed & KEY_DOWN)
|
||||
optionOffset++;
|
||||
|
||||
if (optionOffset < 0) // Wrap around to bottom of list
|
||||
optionOffset = allowedOptions.size() - 1;
|
||||
|
||||
if (optionOffset >= (int)allowedOptions.size()) // Wrap around to top of list
|
||||
optionOffset = 0;
|
||||
|
||||
if (pressed & KEY_A)
|
||||
return allowedOptions[optionOffset];
|
||||
|
||||
if (pressed & KEY_B)
|
||||
return DumpOption::none;
|
||||
|
||||
#ifdef SCREENSWAP
|
||||
// Swap screens
|
||||
if (pressed & KEY_TOUCH) {
|
||||
screenSwapped = !screenSwapped;
|
||||
screenSwapped ? lcdMainOnBottom() : lcdMainOnTop();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Make a screenshot
|
||||
if ((held & KEY_R) && (pressed & KEY_L)) {
|
||||
screenshot();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dumpFailMsg(std::string_view msg) {
|
||||
font->clear(false);
|
||||
font->print(0, 0, false, msg, Alignment::left, Palette::red);
|
||||
@ -423,26 +532,66 @@ void ndsCardSaveRestore(const char *filename) {
|
||||
}
|
||||
|
||||
void ndsCardDump(void) {
|
||||
int pressed = 0;
|
||||
//bool showGameCardMsgAgain = false;
|
||||
|
||||
font->clear(false);
|
||||
font->printf(0, 0, false, Alignment::left, Palette::white, STR_DUMP_NDS_ROM_TO.c_str(), sdMounted ? "sd" : "fat");
|
||||
font->print(0, 2, false, STR_A_YES_Y_TRIM_B_NO_X_SAVE_ONLY);
|
||||
font->print(0, 0, false, STR_LOADING);
|
||||
font->update(false);
|
||||
|
||||
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
|
||||
do {
|
||||
// Print time
|
||||
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
|
||||
font->update(true);
|
||||
std::vector allowedOptions = {DumpOption::all};
|
||||
u8 allowedBitfield = DumpOption::metadata;
|
||||
char gameTitle[13] = {0};
|
||||
char gameCode[7] = {0};
|
||||
char fileName[32] = {0};
|
||||
bool spiSave = cardEepromGetTypeFixed() != -1;
|
||||
bool nandSave = false;
|
||||
|
||||
scanKeys();
|
||||
pressed = keysDownRepeat();
|
||||
swiWaitForVBlank();
|
||||
} while (!(pressed & (KEY_A | KEY_Y | KEY_B | KEY_X)));
|
||||
int cardInited = cardInit(&ndsCardHeader);
|
||||
if(cardInited == 0) {
|
||||
allowedOptions.push_back(DumpOption::allTrimmed);
|
||||
allowedOptions.push_back(DumpOption::rom);
|
||||
allowedOptions.push_back(DumpOption::romTrimmed);
|
||||
allowedBitfield |= DumpOption::rom | DumpOption::romTrimmed;
|
||||
|
||||
if (pressed & KEY_X) {
|
||||
nandSave = cardNandGetSaveSize() != 0;
|
||||
|
||||
if(spiSave || nandSave) {
|
||||
allowedOptions.push_back(DumpOption::save);
|
||||
allowedBitfield |= DumpOption::save;
|
||||
}
|
||||
}
|
||||
allowedOptions.push_back(DumpOption::metadata);
|
||||
|
||||
tonccpy(gameTitle, ndsCardHeader.gameTitle, 12);
|
||||
tonccpy(gameCode, ndsCardHeader.gameCode, 6);
|
||||
if (gameTitle[0] == 0 || gameTitle[0] == 0x2E || gameTitle[0] == 0xFF) {
|
||||
sprintf(gameTitle, "NO-TITLE");
|
||||
} else {
|
||||
for(uint i = 0; i < sizeof(gameTitle); i++) {
|
||||
switch(gameTitle[i]) {
|
||||
case '>':
|
||||
case '<':
|
||||
case ':':
|
||||
case '"':
|
||||
case '/':
|
||||
case '\x5C':
|
||||
case '|':
|
||||
case '?':
|
||||
case '*':
|
||||
gameTitle[i] = '_';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (gameCode[0] == 0 || gameCode[0] == 0x23 || gameCode[0] == 0xFF) {
|
||||
sprintf(gameCode, "NONE00");
|
||||
}
|
||||
sprintf(fileName, "%s_%s_%02X", gameTitle, gameCode, ndsCardHeader.romversion);
|
||||
|
||||
DumpOption dumpOption = dumpMenu(allowedOptions, fileName);
|
||||
|
||||
if(dumpOption & DumpOption::romTrimmed)
|
||||
strcat(fileName, "_trim");
|
||||
|
||||
// Ensure directories exist
|
||||
if((dumpOption & allowedBitfield) != DumpOption::none) {
|
||||
char folderPath[2][256];
|
||||
sprintf(folderPath[0], "%s:/gm9i", (sdMounted ? "sd" : "fat"));
|
||||
sprintf(folderPath[1], "%s:/gm9i/out", (sdMounted ? "sd" : "fat"));
|
||||
@ -458,272 +607,95 @@ void ndsCardDump(void) {
|
||||
font->update(false);
|
||||
mkdir(folderPath[1], 0777);
|
||||
}
|
||||
}
|
||||
|
||||
if (cardInit(&ndsCardHeader) != 0) {
|
||||
dumpFailMsg(STR_UNABLE_TO_DUMP_SAVE);
|
||||
return;
|
||||
}
|
||||
char gameTitle[13] = {0};
|
||||
tonccpy(gameTitle, ndsCardHeader.gameTitle, 12);
|
||||
char gameCode[7] = {0};
|
||||
tonccpy(gameCode, ndsCardHeader.gameCode, 6);
|
||||
char destSavPath[256];
|
||||
sprintf(destSavPath, "%s:/gm9i/out/%s_%s_%02x.sav", (sdMounted ? "sd" : "fat"), gameTitle, gameCode, ndsCardHeader.romversion);
|
||||
ndsCardSaveDump(destSavPath);
|
||||
} else if ((pressed & KEY_A) || (pressed & KEY_Y)) {
|
||||
bool trimRom = (pressed & KEY_Y);
|
||||
char folderPath[2][256];
|
||||
sprintf(folderPath[0], "%s:/gm9i", (sdMounted ? "sd" : "fat"));
|
||||
sprintf(folderPath[1], "%s:/gm9i/out", (sdMounted ? "sd" : "fat"));
|
||||
if (access(folderPath[0], F_OK) != 0) {
|
||||
font->clear(false);
|
||||
font->print(0, 0, false, STR_CREATING_DIRECTORY);
|
||||
font->update(false);
|
||||
mkdir(folderPath[0], 0777);
|
||||
}
|
||||
if (access(folderPath[1], F_OK) != 0) {
|
||||
font->clear(false);
|
||||
font->print(0, 0, false, STR_CREATING_DIRECTORY);
|
||||
font->update(false);
|
||||
mkdir(folderPath[1], 0777);
|
||||
}
|
||||
/*if (expansionPakFound) {
|
||||
font->clear(false)
|
||||
font->print(0, 0, false, "Please switch to the game card, then press A.");
|
||||
font->update(false);
|
||||
//flashcardUnmount();
|
||||
io_dldi_data->ioInterface.shutdown();
|
||||
// Dump ROM
|
||||
if((dumpOption & allowedBitfield) & (DumpOption::rom | DumpOption::romTrimmed)) {
|
||||
font->clear(false);
|
||||
font->printf(0, 0, false, Alignment::left, Palette::white, STR_NDS_IS_DUMPING.c_str(), fileName);
|
||||
font->print(0, 2, false, STR_DO_NOT_REMOVE_CARD);
|
||||
font->update(false);
|
||||
|
||||
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
|
||||
do {
|
||||
// Determine ROM size
|
||||
u32 romSize;
|
||||
if (dumpOption & DumpOption::romTrimmed) {
|
||||
romSize = (isDSiMode() && (ndsCardHeader.unitCode != 0) && (ndsCardHeader.twlRomSize > 0))
|
||||
? ndsCardHeader.twlRomSize : ndsCardHeader.romSize+0x88;
|
||||
} else {
|
||||
romSize = 0x20000 << ndsCardHeader.deviceSize;
|
||||
}
|
||||
|
||||
// Dump!
|
||||
char destPath[256];
|
||||
sprintf(destPath, "%s:/gm9i/out/%s.nds", (sdMounted ? "sd" : "fat"), fileName);
|
||||
u32 currentSize = romSize;
|
||||
FILE* destinationFile = fopen(destPath, "wb");
|
||||
if (destinationFile) {
|
||||
font->print(0, 4, false, STR_PROGRESS);
|
||||
font->print(0, 5, false, "[");
|
||||
font->print(-1, 5, false, "]");
|
||||
for (u32 src = 0; src < romSize; src += 0x8000) {
|
||||
// Print time
|
||||
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
|
||||
font->update(true);
|
||||
|
||||
scanKeys();
|
||||
pressed = keysDownRepeat();
|
||||
swiWaitForVBlank();
|
||||
} while (!(pressed & KEY_A));
|
||||
}*/
|
||||
font->print((src / (romSize / (SCREEN_COLS - 2))) + 1, 5, false, "=");
|
||||
font->printf(0, 6, false, Alignment::left, Palette::white, STR_N_OF_N_BYTES.c_str(), src, romSize);
|
||||
font->update(false);
|
||||
|
||||
int cardInited = cardInit(&ndsCardHeader);
|
||||
char gameTitle[13] = {0};
|
||||
char gameCode[7] = {0};
|
||||
char destPath[256] = {0};
|
||||
char destSavPath[256] = {0};
|
||||
char fileName[32] = {0};
|
||||
tonccpy(gameTitle, ndsCardHeader.gameTitle, 12);
|
||||
tonccpy(gameCode, ndsCardHeader.gameCode, 6);
|
||||
if (gameTitle[0] == 0 || gameTitle[0] == 0x2E || gameTitle[0] == 0xFF) {
|
||||
sprintf(gameTitle, "NO-TITLE");
|
||||
} else {
|
||||
for(uint i = 0; i < sizeof(gameTitle); i++) {
|
||||
switch(gameTitle[i]) {
|
||||
case '>':
|
||||
case '<':
|
||||
case ':':
|
||||
case '"':
|
||||
case '/':
|
||||
case '\x5C':
|
||||
case '|':
|
||||
case '?':
|
||||
case '*':
|
||||
gameTitle[i] = '_';
|
||||
for (u32 i = 0; i < 0x8000; i += 0x200) {
|
||||
cardRead (src+i, copyBuf+i, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (gameCode[0] == 0 || gameCode[0] == 0x23 || gameCode[0] == 0xFF) {
|
||||
sprintf(gameCode, "NONE00");
|
||||
}
|
||||
sprintf(fileName, "%s_%s_%02x%s", gameTitle, gameCode, ndsCardHeader.romversion, (trimRom ? "_trim" : ""));
|
||||
sprintf(destPath, "%s:/gm9i/out/%s.nds", (sdMounted ? "sd" : "fat"), fileName);
|
||||
sprintf(destSavPath, "%s:/gm9i/out/%s.sav", (sdMounted ? "sd" : "fat"), fileName);
|
||||
|
||||
if (cardInited == 0) {
|
||||
font->clear(false);
|
||||
font->printf(0, 0, false, Alignment::left, Palette::white, STR_NDS_IS_DUMPING.c_str(), fileName);
|
||||
font->print(0, 2, false, STR_DO_NOT_REMOVE_CARD);
|
||||
font->update(false);
|
||||
} else {
|
||||
font->clear(false);
|
||||
font->print(0, 0, false, STR_UNABLE_TO_DUMP_ROM);
|
||||
font->update(false);
|
||||
for (int i = 0; i < 60*2; i++) {
|
||||
swiWaitForVBlank();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Determine ROM size
|
||||
u32 romSize = 0;
|
||||
if (trimRom) {
|
||||
romSize = (isDSiMode() && (ndsCardHeader.unitCode != 0) && (ndsCardHeader.twlRomSize > 0))
|
||||
? ndsCardHeader.twlRomSize : ndsCardHeader.romSize+0x88;
|
||||
} else switch (ndsCardHeader.deviceSize) {
|
||||
case 0x00:
|
||||
romSize = 0x20000;
|
||||
break;
|
||||
case 0x01:
|
||||
romSize = 0x40000;
|
||||
break;
|
||||
case 0x02:
|
||||
romSize = 0x80000;
|
||||
break;
|
||||
case 0x03:
|
||||
romSize = 0x100000;
|
||||
break;
|
||||
case 0x04:
|
||||
romSize = 0x200000;
|
||||
break;
|
||||
case 0x05:
|
||||
romSize = 0x400000;
|
||||
break;
|
||||
case 0x06:
|
||||
romSize = 0x800000;
|
||||
break;
|
||||
case 0x07:
|
||||
romSize = 0x1000000;
|
||||
break;
|
||||
case 0x08:
|
||||
romSize = 0x2000000;
|
||||
break;
|
||||
case 0x09:
|
||||
romSize = 0x4000000;
|
||||
break;
|
||||
case 0x0A:
|
||||
romSize = 0x8000000;
|
||||
break;
|
||||
case 0x0B:
|
||||
romSize = 0x10000000;
|
||||
break;
|
||||
case 0x0C:
|
||||
romSize = 0x20000000;
|
||||
break;
|
||||
}
|
||||
// Dump!
|
||||
/*if (expansionPakFound) {
|
||||
u32 currentSize = ((expansionPakFound && romSize > 0x800000) ? 0x800000 : romSize);
|
||||
u32 src = 0;
|
||||
u32 writeSrc = 0;
|
||||
FILE* destinationFile;
|
||||
bool destinationFileOpened = false;
|
||||
while (currentSize > 0) {
|
||||
if (showGameCardMsgAgain) {
|
||||
iprintf ("\x1b[8;0H");
|
||||
iprintf (" \n");
|
||||
|
||||
iprintf("\x1b[15;0H");
|
||||
iprintf("Please switch to the\ngame card, then press A.\n");
|
||||
//flashcardUnmount();
|
||||
io_dldi_data->ioInterface.shutdown();
|
||||
|
||||
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
|
||||
do {
|
||||
// Print time
|
||||
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
|
||||
font->update(true);
|
||||
|
||||
scanKeys();
|
||||
pressed = keysDownRepeat();
|
||||
swiWaitForVBlank();
|
||||
} while (!(pressed & KEY_A));
|
||||
|
||||
consoleSelect(&bottomConsole);
|
||||
iprintf ("\x1b[15;0H");
|
||||
iprintf(" \n \n");
|
||||
cardInit(&ndsCardHeader);
|
||||
if (fwrite(copyBuf, 1, (currentSize>=0x8000 ? 0x8000 : currentSize), destinationFile) < 1) {
|
||||
dumpFailMsg(STR_FAILED_TO_DUMP_ROM);
|
||||
break;
|
||||
}
|
||||
showGameCardMsgAgain = true;
|
||||
|
||||
// Read from game card
|
||||
for (src = src; src < currentSize; src += 0x200) {
|
||||
// Print time
|
||||
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
|
||||
font->update(true);
|
||||
|
||||
consoleSelect(&bottomConsole);
|
||||
iprintf ("\x1B[47m"); // Print foreground white color
|
||||
iprintf ("\x1b[8;0H");
|
||||
iprintf ("Read:\n");
|
||||
iprintf ("%i/%i Bytes ", (int)src, (int)romSize);
|
||||
cardRead (src, (void*)0x09000000+(src % 0x800000), false);
|
||||
}
|
||||
iprintf("\x1b[15;0H");
|
||||
iprintf("Please switch to the\nflashcard, then press A.\n");
|
||||
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
|
||||
do {
|
||||
// Print time
|
||||
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
|
||||
font->update(true);
|
||||
|
||||
scanKeys();
|
||||
pressed = keysDownRepeat();
|
||||
swiWaitForVBlank();
|
||||
} while (!(pressed & KEY_A));
|
||||
|
||||
consoleSelect(&bottomConsole);
|
||||
iprintf("\x1b[15;0H");
|
||||
iprintf(" \n \n");
|
||||
|
||||
iprintf ("\x1B[47m"); // Print foreground white color
|
||||
iprintf ("\x1b[11;0H");
|
||||
iprintf ("Written:\n");
|
||||
|
||||
// Write back to flashcard
|
||||
cardInit(&ndsCardHeader);
|
||||
io_dldi_data->ioInterface.startup();
|
||||
//flashcardMounted = flashcardMount();
|
||||
if (!destinationFileOpened) {
|
||||
destinationFile = fopen(destPath, "wb");
|
||||
destinationFileOpened = true;
|
||||
}
|
||||
for (writeSrc = writeSrc; writeSrc < currentSize; writeSrc += 0x200) {
|
||||
// Print time
|
||||
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
|
||||
font->update(true);
|
||||
|
||||
consoleSelect(&bottomConsole);
|
||||
printf ("\x1B[47m"); // Print foreground white color
|
||||
printf ("\x1b[12;0H");
|
||||
printf ("%i/%i Bytes ", (int)writeSrc, (int)romSize);
|
||||
fwrite((void*)0x09000000+(writeSrc % 0x800000), 1, currentSize, destinationFile);
|
||||
}
|
||||
|
||||
currentSize -= 0x800000;
|
||||
currentSize -= 0x8000;
|
||||
}
|
||||
fclose(destinationFile);
|
||||
} else {*/
|
||||
remove(destPath);
|
||||
u32 currentSize = romSize;
|
||||
FILE* destinationFile = fopen(destPath, "wb");
|
||||
if (destinationFile) {
|
||||
} else {
|
||||
dumpFailMsg(STR_FAILED_TO_DUMP_ROM);
|
||||
}
|
||||
}
|
||||
|
||||
font->print(0, 4, false, STR_PROGRESS);
|
||||
font->print(0, 5, false, "[");
|
||||
font->print(-1, 5, false, "]");
|
||||
for (u32 src = 0; src < romSize; src += 0x8000) {
|
||||
// Print time
|
||||
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
|
||||
font->update(true);
|
||||
// Dump save
|
||||
if ((dumpOption & allowedBitfield) & DumpOption::save) {
|
||||
char destPath[256];
|
||||
sprintf(destPath, "%s:/gm9i/out/%s.sav", (sdMounted ? "sd" : "fat"), fileName);
|
||||
ndsCardSaveDump(destPath);
|
||||
}
|
||||
|
||||
font->print((src / (romSize / (SCREEN_COLS - 2))) + 1, 5, false, "=");
|
||||
font->printf(0, 6, false, Alignment::left, Palette::white, STR_N_OF_N_BYTES.c_str(), src, romSize);
|
||||
font->update(false);
|
||||
// Dump metadata
|
||||
if ((dumpOption & allowedBitfield) & DumpOption::metadata) {
|
||||
font->clear(false);
|
||||
font->print(0, 0, false, STR_DUMPING_METADATA);
|
||||
font->update(false);
|
||||
|
||||
for (u32 i = 0; i < 0x8000; i += 0x200) {
|
||||
cardRead (src+i, copyBuf+i, false);
|
||||
}
|
||||
if (fwrite(copyBuf, 1, (currentSize>=0x8000 ? 0x8000 : currentSize), destinationFile) < 1) {
|
||||
dumpFailMsg(STR_FAILED_TO_DUMP_ROM);
|
||||
break;
|
||||
}
|
||||
currentSize -= 0x8000;
|
||||
}
|
||||
fclose(destinationFile);
|
||||
} else {
|
||||
dumpFailMsg(STR_FAILED_TO_DUMP_ROM);
|
||||
}
|
||||
ndsCardSaveDump(destSavPath);
|
||||
//}
|
||||
char destPath[256];
|
||||
sprintf(destPath, "%s:/gm9i/out/%s.txt", (sdMounted ? "sd" : "fat"), fileName);
|
||||
FILE* destinationFile = fopen(destPath, "wb");
|
||||
if (destinationFile) {
|
||||
fprintf(destinationFile,
|
||||
"Title String : %.12s\n"
|
||||
"Product Code : %.6s\n"
|
||||
"Revision : %u\n"
|
||||
"Cart ID : %08lX\n"
|
||||
"Platform : %s\n"
|
||||
"Save Type : %s\n",
|
||||
gameTitle, gameCode, ndsCardHeader.romversion, cardGetId(),
|
||||
(ndsCardHeader.unitCode == 0x2) ? "DSi Enhanced" : (ndsCardHeader.unitCode == 0x3) ? "DSi Exclusive" : "DS",
|
||||
spiSave ? "SPI" : (nandSave ? "RETAIL_NAND" : "NONE"));
|
||||
|
||||
if(spiSave)
|
||||
fprintf(destinationFile, "Save chip ID : 0x%06lX\n", cardEepromReadID());
|
||||
|
||||
fprintf(destinationFile,
|
||||
"Timestamp : %s\n"
|
||||
"GM9i Version : " VER_NUMBER "\n",
|
||||
RetTime("%Y-%m-%d %H:%M:%S").c_str());
|
||||
|
||||
fclose(destinationFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -742,7 +714,6 @@ void gbaCartSaveDump(const char *filename) {
|
||||
u8 *buffer = new u8[size];
|
||||
gbaReadSave(buffer, 0, size, type);
|
||||
|
||||
remove(filename);
|
||||
FILE *destinationFile = fopen(filename, "wb");
|
||||
fwrite(buffer, 1, size, destinationFile);
|
||||
fclose(destinationFile);
|
||||
@ -825,36 +796,31 @@ void readChange(void) {
|
||||
}
|
||||
|
||||
void gbaCartDump(void) {
|
||||
int pressed = 0;
|
||||
|
||||
font->clear(false);
|
||||
font->printf(0, 0, false, Alignment::left, Palette::white, STR_DUMP_GBA_ROM_TO.c_str(), sdMounted ? "sd" : "fat");
|
||||
font->print(0, 2, false, STR_A_YES_B_NO_X_SAVE_ONLY);
|
||||
font->print(0, 0, false, STR_LOADING);
|
||||
font->update(false);
|
||||
|
||||
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
|
||||
do {
|
||||
// Print time
|
||||
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
|
||||
font->update(true);
|
||||
std::vector allowedOptions = {DumpOption::all, DumpOption::rom};
|
||||
u8 allowedBitfield = DumpOption::rom | DumpOption::metadata;
|
||||
char gameTitle[13] = {0};
|
||||
char gameCode[7] = {0};
|
||||
char fileName[32] = {0};
|
||||
saveTypeGBA saveType = gbaGetSaveType();
|
||||
|
||||
scanKeys();
|
||||
pressed = keysDownRepeat();
|
||||
swiWaitForVBlank();
|
||||
} while (!(pressed & (KEY_A | KEY_B | KEY_X)));
|
||||
if(saveType != saveTypeGBA::SAVE_GBA_NONE) {
|
||||
allowedOptions.push_back(DumpOption::save);
|
||||
allowedBitfield |= DumpOption::save;
|
||||
}
|
||||
allowedOptions.push_back(DumpOption::metadata);
|
||||
|
||||
// Get name
|
||||
char gbaHeaderGameTitle[13] = {0};
|
||||
tonccpy(gbaHeaderGameTitle, (u8*)(0x080000A0), 12);
|
||||
char gbaHeaderGameCode[5] = {0};
|
||||
tonccpy(gbaHeaderGameCode, (u8*)(0x080000AC), 4);
|
||||
char gbaHeaderMakerCode[3] = {0};
|
||||
tonccpy(gbaHeaderMakerCode, (u8*)(0x080000B0), 2);
|
||||
if (gbaHeaderGameTitle[0] == 0 || gbaHeaderGameTitle[0] == 0xFF) {
|
||||
sprintf(gbaHeaderGameTitle, "NO-TITLE");
|
||||
tonccpy(gameTitle, (u8*)(0x080000A0), 12);
|
||||
tonccpy(gameCode, (u8*)(0x080000AC), 6);
|
||||
if (gameTitle[0] == 0 || gameTitle[0] == 0xFF) {
|
||||
sprintf(gameTitle, "NO-TITLE");
|
||||
} else {
|
||||
for(uint i = 0; i < sizeof(gbaHeaderGameTitle); i++) {
|
||||
switch(gbaHeaderGameTitle[i]) {
|
||||
for(uint i = 0; i < sizeof(gameTitle); i++) {
|
||||
switch(gameTitle[i]) {
|
||||
case '>':
|
||||
case '<':
|
||||
case ':':
|
||||
@ -864,37 +830,39 @@ void gbaCartDump(void) {
|
||||
case '|':
|
||||
case '?':
|
||||
case '*':
|
||||
gbaHeaderGameTitle[i] = '_';
|
||||
gameTitle[i] = '_';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (gbaHeaderGameCode[0] == 0 || gbaHeaderGameCode[0] == 0xFF) {
|
||||
sprintf(gbaHeaderGameCode, "NONE");
|
||||
if (gameCode[0] == 0 || gameCode[0] == 0xFF) {
|
||||
sprintf(gameCode, "NONE00");
|
||||
}
|
||||
if (gbaHeaderMakerCode[0] == 0 || gbaHeaderMakerCode[0] == 0xFF) {
|
||||
sprintf(gbaHeaderMakerCode, "00");
|
||||
}
|
||||
u8 gbaHeaderSoftwareVersion = *(u8*)(0x080000BC);
|
||||
char fileName[32] = {0};
|
||||
sprintf(fileName, "%s_%s%s_%x", gbaHeaderGameTitle, gbaHeaderGameCode, gbaHeaderMakerCode, gbaHeaderSoftwareVersion);
|
||||
u8 romVersion = *(u8*)(0x080000BC);
|
||||
sprintf(fileName, "%s_%s_%02X", gameTitle, gameCode, romVersion);
|
||||
|
||||
if (pressed & KEY_A) {
|
||||
if (access("fat:/gm9i", F_OK) != 0) {
|
||||
DumpOption dumpOption = dumpMenu(allowedOptions, fileName);
|
||||
|
||||
// Ensure directories exist
|
||||
if((dumpOption & allowedBitfield) != DumpOption::none) {
|
||||
char folderPath[2][256];
|
||||
sprintf(folderPath[0], "%s:/gm9i", (sdMounted ? "sd" : "fat"));
|
||||
sprintf(folderPath[1], "%s:/gm9i/out", (sdMounted ? "sd" : "fat"));
|
||||
if (access(folderPath[0], F_OK) != 0) {
|
||||
font->clear(false);
|
||||
font->print(0, 0, false, STR_CREATING_DIRECTORY);
|
||||
font->update(false);
|
||||
mkdir("fat:/gm9i", 0777);
|
||||
mkdir(folderPath[0], 0777);
|
||||
}
|
||||
if (access("fat:/gm9i/out", F_OK) != 0) {
|
||||
if (access(folderPath[1], F_OK) != 0) {
|
||||
font->clear(false);
|
||||
font->print(0, 0, false, STR_CREATING_DIRECTORY);
|
||||
font->update(false);
|
||||
mkdir("fat:/gm9i/out", 0777);
|
||||
mkdir(folderPath[1], 0777);
|
||||
}
|
||||
}
|
||||
|
||||
char destPath[256] = {0};
|
||||
sprintf(destPath, "fat:/gm9i/out/%s.gba", fileName);
|
||||
|
||||
// Dump ROM
|
||||
if ((dumpOption & allowedBitfield) & DumpOption::rom) {
|
||||
font->clear(false);
|
||||
font->printf(0, 0, false, Alignment::left, Palette::white, STR_GBA_IS_DUMPING.c_str(), fileName);
|
||||
font->print(0, 2, false, STR_DO_NOT_REMOVE_CART);
|
||||
@ -916,7 +884,6 @@ void gbaCartDump(void) {
|
||||
}
|
||||
|
||||
// Dump!
|
||||
remove(destPath);
|
||||
// Reset data at virtual address
|
||||
u32 rstCmd[4] = {
|
||||
0x11, // Command
|
||||
@ -925,6 +892,9 @@ void gbaCartDump(void) {
|
||||
0x8, // Size (in 0x200 byte blocks)
|
||||
};
|
||||
writeChange(rstCmd);
|
||||
|
||||
char destPath[256];
|
||||
sprintf(destPath, "fat:/gm9i/out/%s.gba", fileName);
|
||||
FILE* destinationFile = fopen(destPath, "wb");
|
||||
if (destinationFile) {
|
||||
bool failed = false;
|
||||
@ -989,23 +959,48 @@ void gbaCartDump(void) {
|
||||
}
|
||||
}
|
||||
|
||||
if(pressed & (KEY_A | KEY_X)) {
|
||||
if (access("fat:/gm9i", F_OK) != 0) {
|
||||
font->clear(false);
|
||||
font->print(0, 0, false, STR_CREATING_DIRECTORY);
|
||||
font->update(false);
|
||||
mkdir("fat:/gm9i", 0777);
|
||||
}
|
||||
if (access("fat:/gm9i/out", F_OK) != 0) {
|
||||
font->clear(false);
|
||||
font->print(0, 0, false, STR_CREATING_DIRECTORY);
|
||||
font->update(false);
|
||||
mkdir("fat:/gm9i/out", 0777);
|
||||
}
|
||||
// Dump save
|
||||
if((dumpOption & allowedBitfield) & DumpOption::save) {
|
||||
char destPath[256];
|
||||
sprintf(destPath, "fat:/gm9i/out/%s.sav", fileName);
|
||||
gbaCartSaveDump(destPath);
|
||||
}
|
||||
|
||||
char destSavPath[256] = {0};
|
||||
sprintf(destSavPath, "fat:/gm9i/out/%s.sav", fileName);
|
||||
// Dump metadata
|
||||
if ((dumpOption & allowedBitfield) & DumpOption::metadata) {
|
||||
font->clear(false);
|
||||
font->print(0, 0, false, STR_DUMPING_METADATA);
|
||||
font->update(false);
|
||||
|
||||
gbaCartSaveDump(destSavPath);
|
||||
char destPath[256];
|
||||
sprintf(destPath, "%s:/gm9i/out/%s.txt", (sdMounted ? "sd" : "fat"), fileName);
|
||||
FILE* destinationFile = fopen(destPath, "wb");
|
||||
if (destinationFile) {
|
||||
fprintf(destinationFile,
|
||||
"Title String : %.12s\n"
|
||||
"Product Code : %.6s\n"
|
||||
"Revision : %u\n"
|
||||
"Platform : GBA\n",
|
||||
gameTitle, gameCode, romVersion);
|
||||
|
||||
fprintf(destinationFile,
|
||||
"Save Type : %s\n",
|
||||
saveType == SAVE_GBA_NONE ? "NONE" :
|
||||
saveType == SAVE_GBA_EEPROM_05 ? "EEPROM 4K" :
|
||||
saveType == SAVE_GBA_EEPROM_8 ? "EEPROM 64K" :
|
||||
saveType == SAVE_GBA_SRAM_32 ? "SRAM" :
|
||||
saveType == SAVE_GBA_FLASH_64 ? "FLASH 512K" :
|
||||
saveType == SAVE_GBA_FLASH_128 ? "FLASH 1M" : "UNK");
|
||||
|
||||
if(saveType == SAVE_GBA_FLASH_64 || saveType == SAVE_GBA_FLASH_128)
|
||||
fprintf(destinationFile, "Save chip ID : 0x%04X\n", gbaGetFlashId());
|
||||
|
||||
fprintf(destinationFile,
|
||||
"Timestamp : %s\n"
|
||||
"GM9i Version : " VER_NUMBER "\n",
|
||||
RetTime("%Y-%m-%d %H:%M:%S").c_str());
|
||||
|
||||
fclose(destinationFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,7 +303,7 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) {
|
||||
switch(operations[optionOffset]) {
|
||||
case FileOperation::bootFile: {
|
||||
applaunch = true;
|
||||
font->print(3, optionOffset + y, false, STR_NOW_LOADING);
|
||||
font->print(3, optionOffset + y, false, STR_LOADING);
|
||||
font->update(false);
|
||||
break;
|
||||
} case FileOperation::bootstrapFile: {
|
||||
|
||||
@ -222,7 +222,7 @@ bool gbaReadSave(u8 *dst, u32 src, u32 len, saveTypeGBA type)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gbaIsAtmel()
|
||||
u16 gbaGetFlashId()
|
||||
{
|
||||
*(vu8*)0x0a005555 = 0xaa;
|
||||
swiDelay(10);
|
||||
@ -244,10 +244,13 @@ bool gbaIsAtmel()
|
||||
//char txt[128];
|
||||
// sprintf(txt, "Man: %x, Dev: %x", man, dev);
|
||||
// displayStateF(STR_STR, txt);
|
||||
if ((man == 0x3d) && (dev == 0x1f))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
||||
return dev << 8 | man;
|
||||
}
|
||||
|
||||
bool gbaIsAtmel()
|
||||
{
|
||||
return gbaGetFlashId() == 0x3d1f;
|
||||
}
|
||||
|
||||
bool gbaWriteSave(u32 dst, u8 *src, u32 len, saveTypeGBA type)
|
||||
|
||||
@ -40,6 +40,7 @@ bool gbaIsGame();
|
||||
saveTypeGBA gbaGetSaveType();
|
||||
uint32 gbaGetSaveSize(saveTypeGBA type = SAVE_GBA_NONE);
|
||||
uint32 gbaGetSaveSizeLog2(saveTypeGBA type = SAVE_GBA_NONE);
|
||||
u16 gbaGetFlashId();
|
||||
|
||||
bool gbaReadSave(u8 *dst, u32 src, u32 len, saveTypeGBA type);
|
||||
bool gbaWriteSave(u32 dst, u8 *src, u32 len, saveTypeGBA type);
|
||||
|
||||
@ -4,7 +4,7 @@ STRING(UNTITLED, "UNTITLED")
|
||||
STRING(ROOT, "[root]")
|
||||
STRING(DIR, "(dir)")
|
||||
STRING(TIME_FORMAT, "%k:%M")
|
||||
STRING(NOW_LOADING, "Now loading...")
|
||||
STRING(LOADING, "Loading...")
|
||||
STRING(CREATING_DIRECTORY, "Creating directory...")
|
||||
STRING(ENTERING_DIRECTORY, "Entering directory...")
|
||||
STRING(COPYING, "Copying...")
|
||||
@ -45,15 +45,15 @@ STRING(SYSNAND_LABEL, "[nand:] SYSNAND")
|
||||
STRING(NITROFS_LABEL, "[nitro:] NDS GAME IMAGE")
|
||||
STRING(FAT_LABEL_NAMED, "[nitro:] FAT IMAGE (%s)")
|
||||
STRING(FAT_LABEL, "[nitro:] FAT IMAGE")
|
||||
STRING(GBA_GAMECART, "GBA GAMECART")
|
||||
STRING(NDS_GAMECARD, "NDS GAMECARD")
|
||||
STRING(GBA_GAMECART, "GBA GAMECART (%s)")
|
||||
STRING(NDS_GAMECARD, "NDS GAMECARD (%s)")
|
||||
|
||||
// Drive bottom screen labels
|
||||
STRING(SD_FAT, "(SD FAT, %s)")
|
||||
STRING(N_FREE, "%s free")
|
||||
STRING(SLOT1_FAT, "(Slot-1 SD FAT, %s)")
|
||||
STRING(GBA_GAME, "(GBA Game)")
|
||||
STRING(NDS_GAME, "(NDS Game)")
|
||||
STRING(GBA_GAME, "(GBA Game, %s)")
|
||||
STRING(NDS_GAME, "(NDS Game, %s (%s trimmed))")
|
||||
STRING(GAME_VIRTUAL, "(Game Virtual)")
|
||||
STRING(RAMDRIVE_9MB, "(RAMdrive FAT, 9 MB)")
|
||||
STRING(RAMDRIVE_16MB, "(RAMdrive FAT, 16 MB)")
|
||||
@ -129,12 +129,18 @@ STRING(PRESS_B_TO_CANCEL, "Press \\B to cancel")
|
||||
STRING(EOF_NO_RESULTS, "Reached end of file\nwith no results")
|
||||
|
||||
// Dumping
|
||||
STRING(DUMP_NDS_ROM_TO, "Dump NDS card ROM to\n\"%s:/gm9i/out\"?")
|
||||
STRING(DUMP_GBA_ROM_TO, "Dump GBA cart ROM to\n\"%s:/gm9i/out\"?")
|
||||
STRING(DUMP_TO, "Dump \"%s\" to\n\"%s:/gm9i/out\"?")
|
||||
STRING(DUMP_ALL, "All")
|
||||
STRING(DUMP_ALL_TRIMMED, "All (Trimmed ROM)")
|
||||
STRING(DUMP_ROM, "ROM")
|
||||
STRING(DUMP_ROM_TRIMMED, "ROM (Trimmed)")
|
||||
STRING(DUMP_SAVE, "Save")
|
||||
STRING(DUMP_METADATA, "Metadata")
|
||||
STRING(DO_NOT_REMOVE_CARD, "Do not remove the NDS card.")
|
||||
STRING(DO_NOT_REMOVE_CART, "Do not remove the GBA cart.")
|
||||
STRING(DUMPING_SAVE, "Dumping save...")
|
||||
STRING(RESTORING_SAVE, "Restoring save...")
|
||||
STRING(DUMPING_METADATA, "Dumping metadata...")
|
||||
STRING(FAILED_TO_DUMP_ROM, "Failed to dump the ROM.")
|
||||
STRING(UNABLE_TO_DUMP_ROM, "Unable to dump the ROM.")
|
||||
STRING(FAILED_TO_DUMP_SAVE, "Failed to dump the save.")
|
||||
|
||||
@ -66,6 +66,7 @@ static bool normalChip = false; // As defined by GBAtek, normal chip secure area
|
||||
static u32 portFlags = 0;
|
||||
static u32 headerData[0x1000/sizeof(u32)] = {0};
|
||||
static u32 secureArea[CARD_SECURE_AREA_SIZE/sizeof(u32)] = {0};
|
||||
static u32 iCardId;
|
||||
|
||||
static bool nandChip = false;
|
||||
static int nandSection = -1; // -1 = ROM, above that is the current 128 KiB section in RW
|
||||
@ -355,7 +356,7 @@ int cardInit (sNDSHeaderExt* ndsHeader)
|
||||
|
||||
toncset(headerData, 0, 0x1000);
|
||||
|
||||
u32 iCardId=cardReadID(CARD_CLK_SLOW);
|
||||
iCardId=cardReadID(CARD_CLK_SLOW);
|
||||
while(REG_ROMCTRL & CARD_BUSY);
|
||||
|
||||
normalChip = (iCardId & BIT(31)) != 0; // ROM chip ID MSB
|
||||
@ -510,6 +511,10 @@ int cardInit (sNDSHeaderExt* ndsHeader)
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
u32 cardGetId() {
|
||||
return iCardId;
|
||||
}
|
||||
|
||||
void cardRead (u32 src, void* dest, bool nandSave)
|
||||
{
|
||||
sNDSHeaderExt* ndsHeader = (sNDSHeaderExt*)headerData;
|
||||
|
||||
@ -43,6 +43,8 @@ int cardInit (sNDSHeaderExt* ndsHeader);
|
||||
|
||||
void cardRead (u32 src, void* dest, bool nandSave);
|
||||
|
||||
u32 cardGetId (void);
|
||||
|
||||
void cardWriteNand (void* src, u32 dest);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@ -102,7 +102,7 @@ bool screenshot(void) {
|
||||
mkdir((sdMounted ? "sd:/gm9i/out" : "fat:/gm9i/out"), 0777);
|
||||
}
|
||||
|
||||
std::string fileTimeText = RetTimeForFilename();
|
||||
std::string fileTimeText = RetTime("%H%M%S");
|
||||
char snapPath[40];
|
||||
// Take top screenshot
|
||||
snprintf(snapPath, sizeof(snapPath), "%s:/gm9i/out/snap_%s_top.bmp", (sdMounted ? "sd" : "fat"), fileTimeText.c_str());
|
||||
|
||||
@ -4,7 +4,7 @@ UNTITLED=UNTITLED
|
||||
ROOT=[root]
|
||||
DIR=(dir)
|
||||
TIME_FORMAT=%k:%M
|
||||
NOW_LOADING=Now loading...
|
||||
LOADING=Loading...
|
||||
CREATING_DIRECTORY=Creating directory...
|
||||
ENTERING_DIRECTORY=Entering directory...
|
||||
COPYING=Copying...
|
||||
@ -44,14 +44,14 @@ SYSNAND_LABEL=[nand:] SYSNAND
|
||||
NITROFS_LABEL=[nitro:] NDS GAME IMAGE
|
||||
FAT_LABEL_NAMED=[nitro:] FAT IMAGE (%s)
|
||||
FAT_LABEL=[nitro:] FAT IMAGE
|
||||
GBA_GAMECART=GBA GAMECART
|
||||
NDS_GAMECARD=NDS GAMECARD
|
||||
GBA_GAMECART=GBA GAMECART (%s)
|
||||
NDS_GAMECARD=NDS GAMECARD (%s)
|
||||
|
||||
SD_FAT=(SD FAT, %s)
|
||||
N_FREE=%s free
|
||||
SLOT1_FAT=(Slot-1 SD FAT, %s)
|
||||
GBA_GAME=(GBA Game)
|
||||
NDS_GAME=(NDS Game)
|
||||
GBA_GAME=(GBA Game, %s)
|
||||
NDS_GAME=(NDS Game, %s (%s trimmed))
|
||||
GAME_VIRTUAL=(Game Virtual)
|
||||
RAMDRIVE_9MB=(RAMdrive FAT, 9 MB)
|
||||
RAMDRIVE_16MB=(RAMdrive FAT, 16 MB)
|
||||
@ -84,7 +84,7 @@ REBOOT=Reboot
|
||||
LANGUAGE=Language...
|
||||
SELECT_LANGUAGE=Select Language
|
||||
NITROFS_NOT_MOUNTED=NitroFS could not be mounted, please load GodMode9i from TWiLight Menu++ or nds-hb-menu.
|
||||
STR_NITROFS_UNMOUNTED=Another title's NitroFS has been mounted, please reload GodMode9i to change the language.
|
||||
NITROFS_UNMOUNTED=Another title's NitroFS has been mounted, please reload GodMode9i to change the language.
|
||||
|
||||
BOOT_FILE=Boot file
|
||||
BOOT_FILE_DIRECT=Boot file (Direct)
|
||||
@ -121,12 +121,18 @@ SEARCHING=Searching
|
||||
PRESS_B_TO_CANCEL=Press \B to cancel
|
||||
EOF_NO_RESULTS=Reached end of file\nwith no results
|
||||
|
||||
DUMP_NDS_ROM_TO=Dump NDS card ROM to\n"%s:/gm9i/out"?
|
||||
DUMP_GBA_ROM_TO=Dump GBA cart ROM to\n"%s:/gm9i/out"?
|
||||
DUMP_TO=Dump "%s" to\n"%s:/gm9i/out"?
|
||||
DUMP_ALL=All
|
||||
DUMP_ALL_TRIMMED=All (Trimmed ROM)
|
||||
DUMP_ROM=ROM
|
||||
DUMP_ROM_TRIMMED=ROM (Trimmed)
|
||||
DUMP_SAVE=Save
|
||||
DUMP_METADATA=Metadata
|
||||
DO_NOT_REMOVE_CARD=Do not remove the NDS card.
|
||||
DO_NOT_REMOVE_CART=Do not remove the GBA cart.
|
||||
DUMPING_SAVE=Dumping save...
|
||||
RESTORING_SAVE=Restoring save...
|
||||
DUMPING_METADATA=Dumping metadata...
|
||||
FAILED_TO_DUMP_ROM=Failed to dump the ROM.
|
||||
UNABLE_TO_DUMP_ROM=Unable to dump the ROM.
|
||||
FAILED_TO_DUMP_SAVE=Failed to dump the save.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user