mirror of
https://github.com/suloku/savegame-manager.git
synced 2026-03-21 18:04:51 -05:00
- Add some very preliminary bits for importing a custom font fileExists - Add "displayDebugF" function for easier debugging - Add some comments on dsiwarehax mode to instructions file
573 lines
13 KiB
C++
573 lines
13 KiB
C++
/*
|
|
* savegame_manager: a tool to backup and restore savegames from Nintendo
|
|
* DS cartridges. Nintendo DS and all derivative names are trademarks
|
|
* by Nintendo. EZFlash 3-in-1 is a trademark by EZFlash.
|
|
*
|
|
* display.cpp: A collection of shared functions used to print various
|
|
* status messages and feedback on the screens.
|
|
*
|
|
* Copyright (C) Pokedoc (2010)
|
|
*/
|
|
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <nds.h>
|
|
#include <fat.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "display.h"
|
|
#include "auxspi.h"
|
|
#include "hardware.h"
|
|
#include "fileselect.h"
|
|
#include "gba.h"
|
|
#include "globals.h"
|
|
#include "strings.h"
|
|
|
|
|
|
PrintConsole upperScreen;
|
|
PrintConsole lowerScreen;
|
|
|
|
|
|
|
|
//===========================================================
|
|
void displayInit()
|
|
{
|
|
videoSetMode(MODE_0_2D);
|
|
vramSetBankA(VRAM_A_MAIN_BG);
|
|
consoleInit(&upperScreen, 3,BgType_Text4bpp, BgSize_T_256x256, 31, 0, true, true);
|
|
|
|
videoSetModeSub(MODE_0_2D);
|
|
vramSetBankC(VRAM_C_SUB_BG);
|
|
consoleInit(&lowerScreen, 3,BgType_Text4bpp, BgSize_T_256x256, 31, 0, false, true);
|
|
}
|
|
|
|
void displayTitle()
|
|
{
|
|
displayMessageF(STR_TITLE_MSG, VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO, VERSION_EXTRA);
|
|
|
|
displayStateF(STR_STR, "Press (B) to continue");
|
|
while (!(keysCurrent() & KEY_B));
|
|
}
|
|
|
|
void displayPrintUpper(bool fc)
|
|
{
|
|
bool gba = (mode == 1);
|
|
u32 dstype = (mode == 3) ? 1 : 0;
|
|
|
|
// print upper screen (background)
|
|
consoleSelect(&upperScreen);
|
|
consoleSetWindow(&upperScreen, 0, 0, 32, 24);
|
|
consoleClear();
|
|
iprintf("Mode :\n");
|
|
iprintf("Memory :\n");
|
|
iprintf("--- SLOT 1 ---------------------");
|
|
iprintf("Game ID :\n");
|
|
iprintf("Game name:\n");
|
|
iprintf("Game save:\n");
|
|
iprintf("Special :\n");
|
|
if (dstype == 1) {
|
|
// DSi mode
|
|
iprintf("--- SD-SLOT --------------------");
|
|
iprintf("Status :\n");
|
|
} else {
|
|
// old DS phat/lite
|
|
iprintf("--- SLOT 2 ---------------------");
|
|
iprintf("Game ID :\n");
|
|
iprintf("Game name:\n");
|
|
iprintf("Game save:\n");
|
|
iprintf("Special :\n");
|
|
}
|
|
|
|
// print upper screen
|
|
consoleSetWindow(&upperScreen, 10, 3, 22, 4);
|
|
consoleClear();
|
|
consoleSetWindow(&upperScreen, 10, 8, 22, 4);
|
|
consoleClear();
|
|
|
|
// fetch cartridge header (maybe, calling "cardReadHeader" on a FC messes with libfat!)
|
|
sNDSHeader nds;
|
|
if (!fc && (slot_1_type != AUXSPI_FLASH_CARD))
|
|
cardReadHeader((uint8*)&nds);
|
|
else
|
|
slot_1_type = AUXSPI_FLASH_CARD;
|
|
|
|
char name[MAXPATHLEN];
|
|
// 0) print the mode
|
|
consoleSetWindow(&upperScreen, 10, 0, 20, 1);
|
|
switch (mode) {
|
|
case 0:
|
|
sprintf(&name[0], "WiFi/FTP");
|
|
break;
|
|
case 1:
|
|
sprintf(&name[0], "GBA");
|
|
break;
|
|
case 2:
|
|
sprintf(&name[0], "3in1");
|
|
break;
|
|
case 3:
|
|
if (sdslot) {
|
|
sprintf(&name[0], "DSi/SD");
|
|
} else {
|
|
sprintf(&name[0], "DSi/iEvo");
|
|
}
|
|
break;
|
|
case 4:
|
|
sprintf(&name[0], "Slot 2");
|
|
break;
|
|
case 5:
|
|
sprintf(&name[0], "Download Play");
|
|
break;
|
|
default:
|
|
sprintf(&name[0], "* ??? *");
|
|
break;
|
|
}
|
|
consoleClear();
|
|
iprintf("%s", name);
|
|
// 0.5) memory buffer size
|
|
consoleSetWindow(&upperScreen, 10, 1, 20, 1);
|
|
iprintf("%i kB", size_buf >> 10);
|
|
|
|
// 1) The cart id.
|
|
consoleSetWindow(&upperScreen, 10, 3, 22, 1);
|
|
sprintf(&name[0], "----");
|
|
if (slot_1_type == AUXSPI_FLASH_CARD) {
|
|
sprintf(&name[0], "Flash Card");
|
|
} else {
|
|
memcpy(&name[0], &nds.gameCode[0], 4);
|
|
name[4] = 0x00;
|
|
}
|
|
if (dstype == 1)
|
|
sprintf(name, "LOCKED");
|
|
consoleClear();
|
|
iprintf("%s", name);
|
|
|
|
// 2) The cart name.
|
|
consoleSetWindow(&upperScreen, 10, 4, 22, 1);
|
|
sprintf(&name[0], "----");
|
|
if (slot_1_type == AUXSPI_FLASH_CARD) {
|
|
sprintf(&name[0], "Flash Card");
|
|
} else {
|
|
memcpy(&name[0], &nds.gameTitle[0], 12);
|
|
name[12] = 0x00;
|
|
}
|
|
if (dstype == 1)
|
|
sprintf(name, "LOCKED");
|
|
consoleClear();
|
|
iprintf("%s", name);
|
|
|
|
// 3) The save type
|
|
consoleSetWindow(&upperScreen, 10, 5, 22, 1);
|
|
sprintf(&name[0], "----");
|
|
if (slot_1_type == AUXSPI_FLASH_CARD) {
|
|
sprintf(&name[0], "Flash Card");
|
|
} else {
|
|
uint8 type = auxspi_save_type(slot_1_type);
|
|
uint8 size = auxspi_save_size_log_2(slot_1_type);
|
|
// some debug output may need this so iprintf prints to the correct region
|
|
consoleSetWindow(&upperScreen, 10, 5, 22, 1);
|
|
switch (type) {
|
|
case 1:
|
|
sprintf(&name[0], "Eeprom (%i Bytes)", size);
|
|
break;
|
|
case 2:
|
|
sprintf(&name[0], "FRAM (%i kB)", 1 << (size - 10));
|
|
break;
|
|
case 3:
|
|
if (size == 0)
|
|
sprintf(&name[0], "Flash (ID:%x)", auxspi_save_jedec_id(slot_1_type));
|
|
else
|
|
sprintf(&name[0], "Flash (%i kB)", 1 << (size - 10));
|
|
break;
|
|
default:
|
|
sprintf(&name[0], "???");
|
|
break;
|
|
}
|
|
}
|
|
if (dstype == 1)
|
|
sprintf(name, "LOCKED");
|
|
consoleClear();
|
|
iprintf("%s", name);
|
|
|
|
// 4) Special properties (infrared device...)
|
|
consoleSetWindow(&upperScreen, 10, 6, 22, 1);
|
|
consoleClear();
|
|
memset(&name[0], 0, MAXPATHLEN);
|
|
switch (slot_1_type) {
|
|
case AUXSPI_INFRARED:
|
|
sprintf(&name[0], "Infrared");
|
|
break;
|
|
case AUXSPI_BBDX:
|
|
sprintf(name, "XXL");
|
|
break;
|
|
case AUXSPI_BLUETOOTH:
|
|
sprintf(name, "Bluetooth");
|
|
break;
|
|
default:
|
|
sprintf(&name[0], "----");
|
|
}
|
|
if (dstype == 1)
|
|
sprintf(name, "LOCKED");
|
|
consoleClear();
|
|
iprintf("%s", name);
|
|
|
|
// Slot 2/SD status
|
|
if (dstype == 1) {
|
|
consoleSetWindow(&upperScreen, 10, 8, 22, 1);
|
|
consoleClear();
|
|
memset(&name[0], 0, MAXPATHLEN);
|
|
// Test if we booted from sudokuhax/DSi Homebrew Channel,
|
|
// which means that the SD-slot is accessible.
|
|
if (sdslot)
|
|
iprintf("Available.");
|
|
else
|
|
iprintf("LOCKED.");
|
|
|
|
return;
|
|
}
|
|
// 5) GBA game id
|
|
consoleSetWindow(&upperScreen, 10, 8, 22, 1);
|
|
consoleClear();
|
|
memset(&name[0], 0, MAXPATHLEN);
|
|
if (ezflash) {
|
|
if (ezflash == 0x89168916)
|
|
sprintf(name, "3in1 (512M)");
|
|
else if (ezflash == 0x227E2218)
|
|
sprintf(name, "3in1 (256M V2)");
|
|
else if (ezflash == 0x227E2202)
|
|
sprintf(name, "3in1 (256M V1)");
|
|
else
|
|
sprintf(name, "3in1 (???M)");
|
|
} else if (gba)
|
|
sprintf(name, "%.4s", (char*)0x080000ac);
|
|
else if (slot2 > 0)
|
|
sprintf(name, "Flash Card");
|
|
else if (dstype == 0)
|
|
sprintf(name, "----");
|
|
if (dstype == 0)
|
|
iprintf("%s", name);
|
|
|
|
// 6) GBA game name
|
|
consoleSetWindow(&upperScreen, 10, 9, 22, 1);
|
|
consoleClear();
|
|
memset(&name[0], 0, MAXPATHLEN);
|
|
if (ezflash)
|
|
sprintf(name, "3in1");
|
|
else if (gba)
|
|
sprintf(name, "%.12s", (char*)0x080000a0);
|
|
else if (slot2 > 0)
|
|
sprintf(name, "Flash Card");
|
|
else if (dstype == 0)
|
|
sprintf(name, "----");
|
|
if (dstype == 0)
|
|
iprintf(name);
|
|
|
|
// 7) GBA save size
|
|
consoleSetWindow(&upperScreen, 10, 10, 22, 1);
|
|
consoleClear();
|
|
memset(&name[0], 0, MAXPATHLEN);
|
|
if (ezflash)
|
|
sprintf(name, "SRAM");
|
|
else if (gba) {
|
|
saveTypeGBA type = GetSlot2SaveType(CART_GBA_GAME);
|
|
u8 size = gbaGetSaveSizeLog2(type);
|
|
switch (type) {
|
|
case SAVE_GBA_EEPROM_05:
|
|
case SAVE_GBA_EEPROM_8:
|
|
sprintf(name, "EEPROM (%i bytes)", 1 << size);
|
|
break;
|
|
case SAVE_GBA_SRAM_32:
|
|
sprintf(name, "SRAM (%i kB)", 1 << (size - 10));
|
|
break;
|
|
case SAVE_GBA_FLASH_64:
|
|
case SAVE_GBA_FLASH_128:
|
|
sprintf(name, "Flash (%i kB)", 1 << (size - 10));
|
|
break;
|
|
default:
|
|
sprintf(name, "(none)");
|
|
}
|
|
}
|
|
else if (slot2 > 0)
|
|
sprintf(name, "Flash Card");
|
|
else if (dstype == 0)
|
|
sprintf(name, "----");
|
|
if (dstype == 0)
|
|
iprintf(name);
|
|
|
|
// 8) GBA special stuff
|
|
consoleSetWindow(&upperScreen, 10, 11, 22, 1);
|
|
consoleClear();
|
|
memset(&name[0], 0, MAXPATHLEN);
|
|
if (ezflash)
|
|
sprintf(name, "NOR + PSRAM");
|
|
else if (gba)
|
|
// TODO: test for RTC, add function for syncing RTC?
|
|
sprintf(name, "???");
|
|
else if (slot2 > 0)
|
|
sprintf(name, "----");
|
|
else if (dstype == 0)
|
|
sprintf(name, "----");
|
|
if (dstype == 0)
|
|
iprintf(name);
|
|
}
|
|
|
|
void displayPrintLower()
|
|
{
|
|
consoleSelect(&lowerScreen);
|
|
consoleSetWindow(&lowerScreen, 0, 0, 32, 24);
|
|
consoleClear();
|
|
iprintf("+------------------------------+");
|
|
for (int i = 0; i < 6; i++) {
|
|
iprintf("| |");
|
|
}
|
|
iprintf("+------------------------------+");
|
|
iprintf("+------------------------------+");
|
|
for (int i = 0; i < 6; i++) {
|
|
iprintf("| |");
|
|
}
|
|
iprintf("+------------------------------+");
|
|
iprintf("+------------------------------+");
|
|
for (int i = 0; i < 6; i++) {
|
|
iprintf("| |");
|
|
}
|
|
iprintf("+------------------------------+");
|
|
|
|
consoleSetWindow(&lowerScreen, 1, 1, 30, 6);
|
|
iprintf("\n\n BACKUP\n");
|
|
iprintf(" Game -> .sav");
|
|
|
|
consoleSetWindow(&lowerScreen, 1, 9, 30, 6);
|
|
iprintf("\n\n RESTORE\n");
|
|
iprintf(" .sav -> Game");
|
|
|
|
consoleSetWindow(&lowerScreen, 1, 17, 30, 6);
|
|
iprintf("\n RESET\n");
|
|
iprintf(stringsGetMessageString(STR_MM_WIPE));
|
|
}
|
|
|
|
void displayPrintState(const char *txt)
|
|
{
|
|
swiWaitForVBlank();
|
|
consoleSelect(&upperScreen);
|
|
consoleSetWindow(0, 0, 22, 32, 1);
|
|
consoleClear();
|
|
iprintf("%s", txt);
|
|
}
|
|
|
|
// ----- internal function
|
|
char *ParseLine(char *start, const char *end, int &length)
|
|
{
|
|
// This function takes a line and does a quick word-wrap, by fitting it into a certain fixed-length line
|
|
int len = 0;
|
|
char *cur = start;
|
|
char *separator = start;
|
|
|
|
while (start < end) {
|
|
if (*start == '\n') {
|
|
length = 0;
|
|
return start;
|
|
}
|
|
|
|
// look for a working "start" position
|
|
if ((*start == ' ') || (*start == '\n') || (*start == '\t')) {
|
|
start++; cur++; separator++;
|
|
continue;
|
|
}
|
|
|
|
// do count characters
|
|
if (len < length) {
|
|
cur++; len++;
|
|
if ((*cur == ' ') || (*cur == '\n') || (*cur == '\t') || (*cur == '\0')) {
|
|
separator = cur;
|
|
if ((*cur == '\n') || (*cur == '\0'))
|
|
break;
|
|
}
|
|
} else {
|
|
// emergency exit
|
|
if (!separator)
|
|
separator = cur;
|
|
break;
|
|
}
|
|
}
|
|
|
|
length = separator - start;
|
|
return start;
|
|
}
|
|
// ------------------
|
|
|
|
void displayStateF(int id, ...)
|
|
{
|
|
// prevent flickering
|
|
swiWaitForVBlank();
|
|
|
|
va_list argp;
|
|
va_start(argp, id);
|
|
memset(txt, 0, 256);
|
|
vsnprintf(txt, 256, stringsGetMessageString(id), argp);
|
|
va_end(argp);
|
|
|
|
consoleSelect(&upperScreen);
|
|
consoleSetWindow(0, 0, 22, 32, 1);
|
|
consoleClear();
|
|
|
|
iprintf("%s", txt);
|
|
}
|
|
|
|
void displayProgressBar(int cur, int max0)
|
|
{
|
|
swiWaitForVBlank();
|
|
consoleSelect(&upperScreen);
|
|
consoleSetWindow(0, 0, 23, 32, 1);
|
|
consoleClear();
|
|
|
|
char buffer[33];
|
|
|
|
int percent = float(cur)/float(max0)*100;
|
|
if (percent > 100)
|
|
percent = 100;
|
|
sprintf(&buffer[14], "%i%%", percent);
|
|
|
|
buffer[0] = '[';
|
|
int steps = float(cur)/float(max0)*30;
|
|
if (steps > 30)
|
|
steps = 30;
|
|
for (int i = 1; i <= 30; i++) {
|
|
if ((i >= 14) && (i <= 15))
|
|
continue;
|
|
if ((i == 16) && (percent >= 10))
|
|
continue;
|
|
if ((i == 17) && (percent == 100))
|
|
continue;
|
|
if (i <= steps)
|
|
buffer[i] = '#';
|
|
else
|
|
buffer[i] = '-';
|
|
}
|
|
buffer[31] = ']';
|
|
buffer[32] = 0;
|
|
|
|
if (max0 == 0)
|
|
buffer[0] = 0;
|
|
|
|
iprintf("%s", buffer);
|
|
}
|
|
|
|
void displayMessageF(int id, ...)
|
|
{
|
|
va_list argp;
|
|
va_start(argp, id);
|
|
memset(txt, 0, 256);
|
|
vsnprintf(txt, 256, stringsGetMessageString(id), argp);
|
|
va_end(argp);
|
|
|
|
consoleSelect(&upperScreen);
|
|
consoleSetWindow(&upperScreen, 0, 16, 32, 6);
|
|
consoleClear();
|
|
|
|
char *start = txt;
|
|
char *end = start + strlen(txt);
|
|
for (int i = 0; i < 6; i++) {
|
|
int l;
|
|
l = 32;
|
|
start = ParseLine(start, end, l);
|
|
consoleSetWindow(&upperScreen, 0, i+16, 32, 1);
|
|
char tmp = *(start+l);
|
|
*(start+l) = '\0';
|
|
iprintf(start);
|
|
*(start+l) = tmp;
|
|
start += l+1;
|
|
if (start >= end)
|
|
break;
|
|
}
|
|
}
|
|
|
|
void displayMessage2F(int id, ...)
|
|
{
|
|
va_list argp;
|
|
va_start(argp, id);
|
|
memset(txt, 0, 256);
|
|
vsnprintf(txt, 256, stringsGetMessageString(id), argp);
|
|
va_end(argp);
|
|
|
|
consoleSelect(&lowerScreen);
|
|
consoleSetWindow(&lowerScreen, 0, 0, 32, 24);
|
|
consoleClear();
|
|
|
|
char *start = txt;
|
|
char *end = start + strlen(txt);
|
|
for (int i = 0; i < 20; i++) {
|
|
int l;
|
|
l = 28;
|
|
start = ParseLine(start, end, l);
|
|
consoleSetWindow(&lowerScreen, 2, i+2, 28, 1);
|
|
char tmp = *(start+l);
|
|
*(start+l) = '\0';
|
|
iprintf(start);
|
|
*(start+l) = tmp;
|
|
start += l+1;
|
|
if (start >= end)
|
|
break;
|
|
}
|
|
}
|
|
|
|
void displayWarning2F(int id, ...)
|
|
{
|
|
va_list argp;
|
|
va_start(argp, id);
|
|
memset(txt, 0, 256);
|
|
vsnprintf(txt, 256, stringsGetMessageString(id), argp);
|
|
va_end(argp);
|
|
|
|
consoleSelect(&lowerScreen);
|
|
consoleSetWindow(&lowerScreen, 0, 0, 32, 24);
|
|
consoleClear();
|
|
|
|
iprintf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
|
for (int i = 0; i < 22; i++)
|
|
iprintf("! !");
|
|
iprintf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
|
|
|
char *start = txt;
|
|
char *end = start + strlen(txt);
|
|
for (int i = 0; i < 20; i++) {
|
|
int l;
|
|
l = 28;
|
|
start = ParseLine(start, end, l);
|
|
consoleSetWindow(&lowerScreen, 2, i+2, 28, 1);
|
|
char tmp = *(start+l);
|
|
*(start+l) = '\0';
|
|
iprintf(start);
|
|
*(start+l) = tmp;
|
|
start += l+1;
|
|
if (start >= end)
|
|
break;
|
|
}
|
|
}
|
|
|
|
void displayDebugF(const char *format, ...)
|
|
{
|
|
va_list argp;
|
|
va_start(argp, format);
|
|
memset(txt, 0, 256);
|
|
|
|
consoleSelect(&upperScreen);
|
|
consoleSetWindow(&upperScreen, 0, 12, 32, 4);
|
|
consoleClear();
|
|
|
|
vprintf(format, argp);
|
|
va_end(argp);
|
|
}
|