Skip to content

Commit

Permalink
Rebuild with latest tools (new version: v1.2.1):
Browse files Browse the repository at this point in the history
* Fixes build issues with latest tools and greatly reduces binary size
* Fixes 3DSX compatibility with Luma3DS v8.0+ (from 2017!) and hb autoboot
* Rewrite README. 3DSX build is now the recommended way to run this program, and is now shipped as a single file
  • Loading branch information
TuxSH committed Sep 22, 2024
1 parent 2e08b17 commit dfda167
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 79 deletions.
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ INCLUDES := include
#ROMFS := romfs
APP_AUTHOR := TuxSH
APP_TITLE := TWLSaveTool
APP_DESCRIPTION := A tool to backup and restore save files from NDS cartridges (requires either PokeTransporter or Pokemon Dream Radar)
APP_DESCRIPTION := A tool to backup and restore save files from NDS cartridges
ICON := app/IconLarge.png
BNR_IMAGE := app/banner.cfgx
BNR_AUDIO := app/DSLite_BootSound.cwav
Expand All @@ -50,7 +50,7 @@ CFLAGS := -g -Wall -O2 -mword-relocations \
-ffunction-sections \
$(ARCH)

CFLAGS += $(INCLUDE) -DARM11 -D_3DS
CFLAGS += $(INCLUDE) -D__3DS__

CXXFLAGS := $(CFLAGS) -fno-rtti -std=gnu++11

Expand Down Expand Up @@ -187,7 +187,7 @@ $(OUTPUT).smdh : $(APP_ICON)
$(OUTPUT).cia.smdh : $(APP_ICON)
@bannertool makesmdh -s "$(APP_TITLE)" -l "$(APP_TITLE)" -p "$(APP_AUTHOR)" -i $(APP_ICON) -o $@
@echo "built ... $(notdir $@)"

$(OUTPUT).cia : $(OUTPUT).elf $(OUTPUT).cia.smdh $(TARGET).bnr
@makerom -f cia -target t -exefslogo -o $@ \
-elf $(OUTPUT).elf -rsf $(TOPDIR)/$(RSF_FILE) \
Expand Down
21 changes: 13 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
# TWLSaveTool

**This project is no longer really maintained, beside compilation issue fixes. I created it 9 years ago, back in 2015, as my first major contribution to the 3DS scene, and its functionality has since then been replicated in other 3DS homebrew applications (with my permission): Checkpoint, PKSM, FBI, and GodMode9.**

## Description
TWLSaveTool is a 3DS homebrew that allows you to read, write, and erase save files from **NDS cartridges** (just like savegame-manager!)
TWLSaveTool is a 3DS homebrew that allows you to read, write, and erase save files from **NDS cartridges** (just like savegame-manager back then).

**The CIA build** requires an access to CFWs. If it's the case, this is the best option; you'll be able to see the awesome banner Apache Thunder made :wink:.
Even though a CIA build is provided for historical reasons, **the 3DSX build** is the recommended way to use this application. You are expected to run Luma3DS.

**The 3dsx build** requires you to have either PokéTransporter or Pokémon Dream Radar installed on your 3DS. In both cases you'll need to pay to be able to download them.
Check [the latest release](https://github.com/TuxSH/TWLSaveTool/releases/latest) for downloads.

## Compatibility list
**All games** except WarioWare D.I.Y., Band Brothers DX, Art Academy DS, and Pokémon Typing Adventure should be supported.
**All genuine games** except WarioWare D.I.Y., Band Brothers DX, Art Academy DS, and Pokémon Typing Adventure should be supported.

## How to build
Have libctru and devkitARM correctly installed and set up, as well as makerom and bannertool in an accessible path, then run: ```make```
Have libctru and devkitARM correctly installed and set up:
* install `dkp-pacman` (or, for distributions that already provide pacman, add repositories): https://devkitpro.org/wiki/devkitPro_pacman
* install packages from `3ds-dev` metapackage: `sudo dkp-pacman -S 3ds-dev --needed`

Then, have `makerom` and `bannertool` in `$PATH`, then run: `make`. If you don't need the CIA build, you can run `make 3dsx` and skip these two extra dependencies.

## Special thanks
I'm deeply grateful to:
Many thanks to:

* Current contributors
* Apache Thunder, for making the amazing banner and icon used by this homebrew
* Apache Thunder, for making the amazing banner and icon this application uses
* Steveice10, for having RE'd [PXIDEV:SPIMultiWriteRead](https://www.3dbrew.org/wiki/PXIDEV:SPIMultiWriteRead)
* idgrepthat, for [pointing out that PokéTransporter was indeed using that function](https://github.com/TuxSH/TWLSaveTool/commit/388c9d86091d51d89363de80df5eaf44e0438dae#commitcomment-15494744)
* Everyone else who helped
9 changes: 6 additions & 3 deletions TWLSaveTool.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<!-- This was needed *hax2.x. Keeping file in repo for historical reasons. Please just
use Luma3DS instead. -->

<targets selectable="false">
<title mediatype="1">00040000000C9C00</title>
<title mediatype="1">00040000000AE100</title>
</targets>
<title mediatype="1">00040000000C9C00</title> <!-- Poké Transporter -->
<title mediatype="1">00040000000AE100</title> <!-- Pokemon Dream Radar -->
</targets>
106 changes: 41 additions & 65 deletions source/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,66 +18,35 @@

#include <cstdio>
#include <cstring>
#include <3ds.h>

extern "C"{
#include <sys/stat.h>
#include <unistd.h>
#include <3ds/console.h>
}

#include "TWLCard.h"


// Fix compile error. This should be properly initialized if you fiddle with the title stuff!
u8 sysLang = 0;


// Override the default service init/exit functions
extern "C" {
void __appInit() {
// Initialize services
srvInit();
aptInit();
gfxInitDefault();
hidInit();
fsInit();
sdmcInit();
pxiDevInit();
consoleInit(GFX_TOP, NULL);
}

void __appExit() {
// Exit services
pxiDevExit();
sdmcExit();
fsExit();
hidExit();
gfxExit();
aptExit();
srvExit();
}
}

void updateProgressBar(u32 offset, u32 total) {
const int nbBars = 40;
std::string bar(nbBars*offset/total, '#');

bar += std::string(nbBars - nbBars*offset/total, '-');

printf("\r[%s]\t%d%%", bar.c_str(), (int)(100*offset/total));

gfxFlushBuffers();
gfxSwapBuffers();
}

std::string sizeToStr(u32 sz) {
char buf[50];
if(sz < 1024)
sprintf(buf, "%lu B", sz);
else if(sz < (1 << 20))
sprintf(buf, "%lu KB", sz >> 10);
else sprintf(buf, "%lu MB", sz >> 20);

return std::string(buf);
}

Expand Down Expand Up @@ -108,32 +77,36 @@ catch(std::exception const& e){\
}

int main(void) {
pxiDevInit();
gfxInitDefault();
consoleInit(GFX_TOP, NULL);

restart:
mkdir("sdmc:/TWLSaveTool", 0777);
chdir("sdmc:/TWLSaveTool");

u8 fileNumber = 0;

bool once = false, error_occured = false;
TWLCard* card = NULL;
consoleClear();

printf("\x1b[1m\x1b[0;12HTWLSaveTool 1.2 by TuxSH\x1B[0m\n\n\n");
printf("\x1b[1m\x1b[0;12HTWLSaveTool 1.2.1 by TuxSH\x1B[0m\n\n\n");

try { card = new TWLCard; }
HANDLE_INIT_ERRORS(true)

if(error_occured) goto main_loop;

try {
if(card->isTWL()) {
if(card->isTWL()) {
if(card->cardType() == NO_CHIP) {
delete card;
card = NULL;
printf("\x1B[31mUnsupported save file type. It is possible that this game doesn't store any save data.\x1B[0m\n");
error_occured = true;
}

printf("Game title:\t\t\t%s\nGamecode:\t\t\t%s\n", card->cardHeader().gameTitle.c_str(), card->cardHeader().gameCode.c_str());
printf("Save file size:\t%s\n", sizeToStr(card->saveSize()).c_str());
if(card->cardType() >= FLASH_256KB_1)
Expand All @@ -143,7 +116,7 @@ int main(void) {
printf("(LEFT/RIGHT)\tChange file name\n");
printf("Current save file name: %s", card->generateFileName(fileNumber).c_str());
}

else {
delete card;
card = NULL;
Expand All @@ -152,18 +125,18 @@ int main(void) {
}
}
HANDLE_INIT_ERRORS(false)
main_loop:

main_loop:
while(aptMainLoop()) {
hidScanInput();
auto keys = hidKeysDown();
auto keys = hidKeysDown();

if(keys & KEY_START) break;
else if(keys & KEY_Y) {
else if(keys & KEY_Y) {
gfxFlushBuffers();
gfxSwapBuffers();
gspWaitForVBlank();
delete card;
delete card;
card = NULL;
goto restart;
}
Expand All @@ -173,15 +146,15 @@ int main(void) {
printf("\rCurrent save file name: %s ", card->generateFileName(fileNumber).c_str());
goto flush_buffers;
}
if(!once) {

if(!once) {
if(error_occured) goto end_error;

if (keys & (KEY_B | KEY_A | KEY_X)) {
std::string fileName = card->generateFileName(fileNumber);
FILE* f = NULL;
printf("\n\n\n");

if(keys & KEY_B) {
try{
f = fopen(fileName.c_str(), "rb");
Expand All @@ -194,16 +167,16 @@ int main(void) {
else if(hidKeysDown() & KEY_DOWN) goto end_error;
}
}

printf("\nReading save file...\n\n");
card->backupSaveFile(fileName, &updateProgressBar);
printf("\n");
}
HANDLE_ERRORS()
}



else if(keys & KEY_A) {
try{
f = fopen(fileName.c_str(), "rb");
Expand All @@ -214,19 +187,19 @@ int main(void) {
fseek(f, 0, SEEK_END);
u32 sz = ftell(f);
fclose(f);

if(sz != card->saveSize()){
printf("\x1B[31mError: incorrect file size\x1B[0m\n");
goto end_error;
}

printf("\nWriting save file...\n\n");
card->restoreSaveFile(fileName, &updateProgressBar);
printf("\n");
}
HANDLE_ERRORS()
}

else if(keys & KEY_X) {
try{
printf("\x1B[33mAre you REALLY sure you want to erase your save data?\n\n(UP) Yes\t\t(DOWN) No\x1B[0m\n");
Expand All @@ -241,25 +214,28 @@ int main(void) {
}
HANDLE_ERRORS()
}

end:
once = true;
printf("\nDone.\n");
}
else goto flush_buffers;
else goto flush_buffers;
end_error:
once = true;
printf("Press START to exit, Y to restart.\n");
}


flush_buffers:
gfxFlushBuffers();
gfxSwapBuffers();
gspWaitForVBlank();
}

if(fill_buf != NULL) delete[] fill_buf;
delete card;

gfxExit();
pxiDevExit();
return 0;
}

0 comments on commit dfda167

Please sign in to comment.