From 388c9d86091d51d89363de80df5eaf44e0438dae Mon Sep 17 00:00:00 2001 From: TuxSH Date: Sun, 27 Dec 2015 21:04:23 +0100 Subject: [PATCH] Made the UI, fixed some things --- app/build-cia.rsf | 6 +- include/TWLCard.h | 55 +++++++++++++++- include/errors.h | 23 +++++-- source/TWLCard.cpp | 95 ++++++++++++++------------- source/main.cpp | 158 ++++++++++++++++++++++++++++++--------------- 5 files changed, 224 insertions(+), 113 deletions(-) diff --git a/app/build-cia.rsf b/app/build-cia.rsf index 3c25c03..78dbc84 100644 --- a/app/build-cia.rsf +++ b/app/build-cia.rsf @@ -1,7 +1,7 @@ BasicInfo: - Title : "TSVT" + Title : "TWLSaveTool" CompanyCode : "00" - ProductCode : "CTR-N-TSVT" + ProductCode : "TWLSaveTool" ContentType : Application # Application / SystemUpdate / Manual / Child / Trial Logo : Nintendo # Nintendo / Licensed / Distributed / iQue / iQueForSystem @@ -23,7 +23,7 @@ CardInfo: Option: UseOnSD : true # true if App is to be installed to SD EnableCompress : true # Compresses exefs code - FreeProductCode : false # Removes limitations on ProductCode + FreeProductCode : true # Removes limitations on ProductCode EnableCrypt : false # Enables encryption for NCCH and CIA MediaFootPadding : false # If true CCI files are created with padding diff --git a/include/TWLCard.h b/include/TWLCard.h index 7877966..9577db3 100644 --- a/include/TWLCard.h +++ b/include/TWLCard.h @@ -1,14 +1,19 @@ #pragma once + #include <3ds.h> #include #include #include #include "errors.h" +extern const int validSizes[7]; namespace TWLCard { struct Header { - std::string gameTitle, gameCode, makerCode; + std::string gameTitle, gameCode, makerCode; + bool isTWL; // "DSi enhanced" + + std::string generateCodeName(void) const; Header(u8* data = NULL); }; @@ -19,8 +24,52 @@ namespace TWLCard { void closeSaveFile(Handle f); u64 getSaveFileSize(void); - void readSaveFile(u8* out, u64 nb = 0); - void writeToSaveFile(u8* in, u64 nb = 0); + u64 getSPISize(void); + + template + void readSaveFile(u8* out, u64 nb, CallbackT cb){ + Handle f = openSaveFile(FS_OPEN_READ); + Result res; + + u32 bytesRead; + + u64 offset = 0; + u64 spiSize = getSPISize(); + + for(offset = 0; offset < nb; offset += 512){ + u64 expected = (nb-offset < 512) ? (nb-offset) : 512; + res = FSFILE_Read(f, &bytesRead, offset, out+offset, expected); + if(res != 0){ closeSaveFile(f); throw Error(res,__FILE__, __LINE__); } + if(expected != bytesRead) { closeSaveFile(f); throw std::runtime_error("too few bytes were read"); } + cb(offset, nb); + } + cb(nb,nb); + + closeSaveFile(f); + } + + template + void writeToSaveFile(u8* in, u64 nb, CallbackT cb){ + Handle f = openSaveFile(FS_OPEN_WRITE); + Result res; + + u32 bytesWritten; + + u64 offset = 0; + + for(offset = 0; offset < nb; offset += 512){ + u64 expected = (nb-offset < 512) ? (nb-offset) : 512; + res = FSFILE_Write(f, &bytesWritten, offset, in+offset, expected, FS_WRITE_FLUSH); + if(res != 0){ closeSaveFile(f); throw Error(res,__FILE__, __LINE__); } + if(expected != bytesWritten) { closeSaveFile(f); throw std::runtime_error("too few bytes were written"); } + cb(offset, nb); + } + + cb(nb, nb); + + closeSaveFile(f); + } + } diff --git a/include/errors.h b/include/errors.h index 3747ad3..c713dba 100644 --- a/include/errors.h +++ b/include/errors.h @@ -1,18 +1,29 @@ - #pragma once - #include <3ds.h> - #include - +#pragma once +#include <3ds.h> +#include + + struct Error : public std::runtime_error { - Error(u32 code) : std::runtime_error("It failed"), errorCode(code) {} + Error(u32 code, const char* fn, int l) : std::runtime_error("It failed"), errorCode(code), fileName(fn), line(l) {} - u32 getErrorCode(void) const throw(){ + u32 getErrorCode(void) const throw(){ return errorCode; } + const char* getFileName(void) const throw(){ + return fileName; + } + + int getLine(void) const throw(){ + return line; + } + virtual const char* what() const throw(){ return "It failed"; } protected: u32 errorCode; + const char* fileName; + int line; }; \ No newline at end of file diff --git a/source/TWLCard.cpp b/source/TWLCard.cpp index d4b05c5..076b355 100644 --- a/source/TWLCard.cpp +++ b/source/TWLCard.cpp @@ -1,5 +1,10 @@ #include "TWLCard.h" +#include "games.h" + +const int validSizes[7] = { 512, 8192, 65536, 262144, 524288, 1048576, 8388608 }; + +#define __FILE__ "TWLCard.cpp" namespace TWLCard { Header::Header(u8* data) { if(data == NULL) return; @@ -12,21 +17,30 @@ namespace TWLCard { gameTitle = std::string(in1); gameCode = std::string(in2); makerCode = std::string(in3); + isTWL = (data[0x12] & 0x2) != 0; + } + + std::string Header::generateCodeName(void) const { + using std::string; + string first = (isTWL) ? string("TWL") : string("NTR"); + return first + string("-") + gameCode + string("-") + makerCode; } bool isCardTWL(void) { FS_CardType t; Result res = FSUSER_GetCardType(&t); - if(res != 0) throw Error(res); + if(res != 0) throw Error(res,__FILE__, __LINE__); return t == CARD_TWL; } Header getHeader(void) { - u8 data[18] = {0}; - Result res = FSUSER_GetLegacyRomHeader2(18, MEDIATYPE_GAME_CARD, 0LL, data); - if(res != 0) throw Error(res); - return Header(data); + u8* data = new u8[0x3b4]; + Result res = FSUSER_GetLegacyRomHeader(MEDIATYPE_GAME_CARD, 0LL, data); + if(res != 0) throw Error(res,__FILE__, __LINE__); + Header ret(data); + delete[] data; + return ret; } Handle openSaveFile(u32 openFlags) { @@ -34,63 +48,48 @@ namespace TWLCard { Result res = FSUSER_OpenFileDirectly(&f, (FS_Archive){ARCHIVE_CARD_SPIFS, fsMakePath(PATH_EMPTY, NULL)}, fsMakePath(PATH_UTF16, L"/"), openFlags, 0); - if(res != 0) throw Error(res); + if(res != 0) throw Error(res,__FILE__, __LINE__); return f; } void closeSaveFile(Handle f) { Result res = FSFILE_Close(f); - if(res != 0) throw Error(res); + if(res != 0) throw Error(res,__FILE__, __LINE__); } u64 getSaveFileSize(void) { - Handle f = openSaveFile(FS_OPEN_READ); - u64 sz; - Result res = FSFILE_GetSize(f, &sz); - if(res != 0) throw Error(res); - closeSaveFile(f); - return sz; - } - - void readSaveFile(u8* out, u64 nb) { - Handle f = openSaveFile(FS_OPEN_READ); - Result res; - - if(nb == 0){ - FSFILE_GetSize(f, &nb); - if(res != 0) throw Error(res); - } + int sz = 0; + u8* buf = new u8[512]; + u8* buf2 = new u8[512]; + Handle f; u32 bytesRead; + if(FSUSER_OpenFileDirectly(&f, (FS_Archive){ARCHIVE_CARD_SPIFS, fsMakePath(PATH_EMPTY, NULL)}, + fsMakePath(PATH_UTF16, L"/"), FS_OPEN_READ, 0) != 0) goto end1; - res = FSFILE_Read(f, &bytesRead, 0LL, out, (u32)nb); - - if(res != 0) throw Error(res); - if(nb != bytesRead) throw std::runtime_error("too few bytes were read"); - closeSaveFile(f); + if(FSFILE_Read(f, &bytesRead, 0LL, buf, 512) != 0 || bytesRead != 512) goto end; + + for(int offset : validSizes){ + if(FSFILE_Read(f, &bytesRead, offset, buf2, 512) != 0 || bytesRead != 512) goto end; + if(std::equal(buf, buf+512, buf2)){ + sz = offset; + break; + } + } + end: FSFILE_Close(f); + end1: + delete[] buf; + delete[] buf2; + return (u64) sz; } - void writeToSaveFile(u8* out, u64 nb) { - if(nb == 0) - nb = getSaveFileSize(); - - Handle f = openSaveFile(FS_OPEN_WRITE); - Result res; - - if(nb == 0){ - FSFILE_GetSize(f, &nb); - if(res != 0) throw Error(res); - } - - u32 bytesWritten; - - res = FSFILE_Write(f, &bytesWritten, 0LL, out, (u32)nb, FS_WRITE_FLUSH); - - if(res != 0) throw Error(res); - if(nb != bytesWritten) throw std::runtime_error("too few bytes were written"); - + u64 getSPISize(void){ + Handle f = openSaveFile(FS_OPEN_READ); + u64 sz; + Result res = FSFILE_GetSize(f, &sz); + if(res != 0) throw Error(res,__FILE__, __LINE__); closeSaveFile(f); + return sz; } - } diff --git a/source/main.cpp b/source/main.cpp index 4393d00..8d333dd 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,23 +1,5 @@ -/* - * TWLSaveTool is a Nintendo DS save backup tool for the Nintendo 3DS. - * Copyright (C) 2015 TuxSH - * - * 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 3 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, see +#include extern "C"{ #include @@ -25,7 +7,8 @@ extern "C"{ } #include "TWLCard.h" - +#include "games.h" +#include <3ds/console.h> // Fix compile error. This should be properly initialized if you fiddle with the title stuff! @@ -40,10 +23,11 @@ extern "C" // Initialize services srvInit(); aptInit(); - gfxInit(GSP_RGB565_OES, GSP_RGB565_OES, false); + gfxInitDefault(); hidInit(); fsInit(); sdmcInit(); + consoleInit(GFX_TOP, NULL); } void __appExit() @@ -61,88 +45,144 @@ extern "C" using namespace TWLCard; +void updateProgressBar(u64 offset, u64 total){ + const int nbBars = 40; + std::string bar(nbBars*offset/total, '#'); + + bar += std::string(nbBars - nbBars*offset/total, '-'); + + PrintConsole* csl = consoleGetDefault(); + std::string centerPadding((49 - (nbBars + 5 + csl->tabSize))/2, ' '); + + printf("\r%s[%s]\t%2d%%", centerPadding.c_str(), bar.c_str(), (int)(100*offset/total)); + fflush(stdout); +} + +std::string sizeToStr(u64 sz){ + char buf[50]; + if(sz < 1024) + sprintf(buf, "%llu B", sz); + else if(sz < (1 << 20)) + sprintf(buf, "%llu KB", sz >> 10); + else sprintf(buf, "%llu MB", sz >> 20); + + return std::string(buf); +} + +u64 selectSaveFileSize(u64 initialValue){ + int pos = std::distance(validSizes, std::find(validSizes, validSizes + 7, initialValue)); + std::string filler(49, ' '); + + while(aptMainLoop()){ + printf("\rSave file size (L/R = -/+, A = confirm): %s", sizeToStr(validSizes[pos]).c_str()); + fflush(stdout); + hidScanInput(); + + if(hidKeysDown() & (KEY_L | KEY_R)){ + printf("\r%s", filler.c_str()); // clears the current line + fflush(stdout); + + if(hidKeysDown() & KEY_L) pos = (7 + pos - 1)%7; // because of how modulus works in C ... + else if(hidKeysDown() & KEY_R) pos = (pos + 1)%7; + } + + else if(hidKeysDown() & KEY_A) break; + } + printf("\n"); + return validSizes[pos]; +} + int main() { +restart: + mkdir("sdmc:/TWLSaveTool", 0777); chdir("sdmc:/TWLSaveTool"); + bool once = false; u64 saveSz = 0; Header h; + u8* data = NULL; - consoleInit(GFX_TOP, NULL); - printf("TWLSaveTool 0.1a by TuxSH\n\n\n"); + consoleClear(); + printf("\x1b[1m\x1b[0;12HTWLSaveTool v0.1a by TuxSH\x1B[0m\n\n\n"); try { bool isTWL = isCardTWL(); if(isTWL) { h = getHeader(); saveSz = getSaveFileSize(); - printf("Game title: %s\nGame code: %s\nMaker code: %s\n", h.gameTitle.c_str(), h.gameCode.c_str(), h.makerCode.c_str()); - - if(saveSz < 1024) printf("Save file size: %llu B\n", saveSz); - else printf("Save file size: %llu KB\n", saveSz >> 10); - - printf("(A) Read save file\n(Y) Write save file\n(HOME) Exit\n(file name used: %s.sav)\n\n", h.gameTitle.c_str()); + printf("Game title:\t%s\nCode name: %s\n", h.gameTitle.c_str(), h.generateCodeName().c_str()); + printf("Possible save file size:\t%s\n", sizeToStr(saveSz).c_str()); + //printf("SPI size:\t%s\n", sizeToStr(getSPISize()).c_str()); + printf("(A)\tRead save file\n(Y)\tWrite save file\n(B)\tExit\n(SELECT)\tRestart\n(file name used: %s.sav)\n\n", h.gameTitle.c_str()); } else{ once = true; - printf("Please insert a valid NDS game card!\n"); + printf("\x1B[31mPlease insert a valid NDS game card!\x1B[0m\n"); } } catch(Error const& e){ - printf("An error occured: error code %x\n", e.getErrorCode()); + printf("\x1B[31mAn error occured: error code %x (%s, line %d)\x1B[0m\n", e.getErrorCode(), e.getFileName(), e.getLine()); } catch(std::exception const& e){ - printf("An error occured: %s\n", e.what()); + printf("\x1B[31mAn error occured: %s\x1B[0m\n", e.what()); } - + std::string fileName = h.gameTitle + ".sav"; - u8* data = new u8[(size_t) saveSz]; FILE* f = NULL; while(aptMainLoop()) { hidScanInput(); + auto keys = hidKeysDown(); + + if(!once && (keys & (KEY_A | KEY_Y))) { + saveSz = selectSaveFileSize(saveSz); - if(!once) { - if(hidKeysDown() & KEY_A){ - + data = new u8[(size_t) saveSz]; + printf("\n"); + if(keys & KEY_A){ try{ f = fopen(fileName.c_str(), "rb"); if(f != NULL){ fclose(f); - printf("Error: file %s already exists\n", fileName.c_str()); - goto end; + printf("\x1B[33mFile %s already exists.\nOverwrite (A = yes, B = no)?\x1B[0m\n", fileName.c_str()); + while(aptMainLoop()){ + hidScanInput(); + if(hidKeysDown() & KEY_A) break; + else if(hidKeysDown() & KEY_B) goto end; + } } f = fopen(fileName.c_str(), "wb+"); if(f == NULL){ - printf("Error: cannot create file %s\n", fileName.c_str()); + printf("\x1B[31mError: cannot create file %s\x1B[0m\n", fileName.c_str()); goto end; } - printf("Please wait...\n"); - readSaveFile(data, saveSz); + printf("Reading save file...\n\n"); + readSaveFile(data, saveSz, updateProgressBar); + printf("\n"); fwrite(data, 1, saveSz, f); fclose(f); } catch(Error const& e){ - printf("An error occured: error code %x\n", e.getErrorCode()); + printf("\x1B[31mAn error occured: error code %x (%s, line %d)\x1B[0m\n", e.getErrorCode(), e.getFileName(), e.getLine()); fclose(f); } catch(std::exception const& e){ - printf("An error occured: %s\n", e.what()); + printf("\x1B[31mAn error occured: %s\x1B[0m\n", e.what()); fclose(f); } } - else if(hidKeysDown() & KEY_Y){ - + else if(keys & KEY_Y){ try{ f = fopen(fileName.c_str(), "rb"); if(f == NULL){ - printf("Error: cannot open file %s\n", fileName.c_str()); + printf("\x1B[31mError: cannot open file %s\x1B[0m\n", fileName.c_str()); goto end; } fseek(f, 0, SEEK_END); @@ -150,27 +190,39 @@ int main() rewind(f); if(sz != saveSz){ - printf("Error: incorrect file size\n"); + printf("\x1B[31mError: incorrect file size\x1B[0m\n"); } - printf("Please wait...\n"); + printf("Writing save file...\n\n"); fread(data, 1, saveSz, f); - writeToSaveFile(data, saveSz); + writeToSaveFile(data, saveSz, updateProgressBar); + printf("\n"); fclose(f); } catch(Error const& e){ - printf("An error occured: error code %x\n", e.getErrorCode()); + printf("\x1B[31mAn error occured: error code %x (%s, line %d)\x1B[0m\n", e.getErrorCode(), e.getFileName(), e.getLine()); fclose(f); } catch(std::exception const& e){ - printf("An error occured: %s\n", e.what()); + printf("\x1B[31mAn error occured: %s\x1B[0m\n", e.what()); fclose(f); } } + + end: once = true; - printf("Done.\n"); + printf("\nDone.\nPress B to exit, SELECT to restart.\n"); + } + + if(keys & KEY_B) break; + else if(keys & KEY_SELECT){ + gfxFlushBuffers(); + gfxSwapBuffers(); + gspWaitForVBlank(); + delete[] data; + goto restart; } gfxFlushBuffers();