2019-02-13 19:18:36 +00:00
|
|
|
/*---------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
Basic template code for starting a DS app
|
|
|
|
|
|
|
|
---------------------------------------------------------------------------------*/
|
|
|
|
#include <nds.h>
|
|
|
|
#include <cstdio>
|
|
|
|
#include <fstream>
|
|
|
|
#include <vector>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fat.h>
|
2020-04-24 20:26:37 +00:00
|
|
|
#include <algorithm>
|
2019-02-13 19:18:36 +00:00
|
|
|
|
|
|
|
#include "file_browse.h"
|
|
|
|
#include "headers.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "menu.h"
|
2019-02-13 22:32:22 +00:00
|
|
|
#include "inihandler.h"
|
2019-02-13 19:18:36 +00:00
|
|
|
#include "apppatch.h"
|
|
|
|
|
2019-02-13 22:32:22 +00:00
|
|
|
IniFile bootsrtapconfig;
|
|
|
|
IniFile bootstrap_template;
|
2019-02-13 19:18:36 +00:00
|
|
|
|
|
|
|
PrintConsole upperScreen;
|
|
|
|
PrintConsole lowerScreen;
|
|
|
|
|
|
|
|
void WriteMessage(std::string text, bool clear = false, PrintConsole* screen = nullptr) {
|
|
|
|
if(screen)
|
|
|
|
consoleSelect(screen);
|
|
|
|
if(clear)
|
|
|
|
consoleClear();
|
|
|
|
if(text.size() <= DISPLAY_COLUMNS) {
|
|
|
|
iprintf(text.c_str());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::vector<std::string> words;
|
|
|
|
std::string temp;
|
|
|
|
for(int i = 0; i < (int)text.size(); i++) {
|
|
|
|
if(text[i] == ' ' || text[i] == '\n') {
|
|
|
|
words.push_back(temp);
|
|
|
|
temp.clear();
|
|
|
|
if(text[i] == '\n')
|
|
|
|
words.push_back("\n");
|
|
|
|
} else
|
|
|
|
temp += text[i];
|
|
|
|
}
|
|
|
|
if(temp.size())
|
|
|
|
words.push_back(temp);
|
|
|
|
int column = 0;
|
|
|
|
for(auto word : words) {
|
|
|
|
if(word.size() == 1 && word[0] == '\n') {
|
|
|
|
if(column != DISPLAY_COLUMNS)
|
|
|
|
iprintf("\n");
|
|
|
|
column = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int chkval = column + (int)word.size();
|
|
|
|
if(column)
|
|
|
|
chkval++;
|
|
|
|
if(chkval <= DISPLAY_COLUMNS) {
|
|
|
|
if(column) {
|
|
|
|
iprintf(" ");
|
|
|
|
column++;
|
|
|
|
}
|
|
|
|
iprintf(word.c_str());
|
|
|
|
column += (int)word.size();
|
|
|
|
} else {
|
|
|
|
if(column != DISPLAY_COLUMNS)
|
|
|
|
iprintf("\n");
|
|
|
|
column = (int)word.size();
|
|
|
|
iprintf(word.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-03 02:32:09 +00:00
|
|
|
menu savefilemenu;
|
2019-02-13 19:18:36 +00:00
|
|
|
menu yesno;
|
|
|
|
|
2021-08-03 02:32:09 +00:00
|
|
|
std::string saveformat = ".sav";
|
|
|
|
|
2019-02-13 19:18:36 +00:00
|
|
|
void displayInit() {
|
|
|
|
lowerScreen = *consoleDemoInit();
|
|
|
|
videoSetMode(MODE_0_2D);
|
|
|
|
vramSetBankA(VRAM_A_MAIN_BG);
|
|
|
|
consoleInit(&upperScreen, 3, BgType_Text4bpp, BgSize_T_256x256, 31, 0, true, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool fileExists(const std::string& file) {
|
2019-02-13 20:02:02 +00:00
|
|
|
struct stat buf;
|
|
|
|
return (stat(file.c_str(), &buf) == 0);
|
2019-02-13 19:18:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) {
|
2019-02-13 20:02:02 +00:00
|
|
|
size_t start_pos = 0;
|
|
|
|
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
|
|
|
str.replace(start_pos, from.length(), to);
|
|
|
|
start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
|
|
|
|
}
|
|
|
|
return str;
|
2019-02-13 19:18:36 +00:00
|
|
|
}
|
2019-02-13 20:02:02 +00:00
|
|
|
void stop(void) {
|
|
|
|
//---------------------------------------------------------------------------------
|
|
|
|
while(true) {
|
2019-02-13 19:18:36 +00:00
|
|
|
swiWaitForVBlank();
|
|
|
|
}
|
|
|
|
}
|
2019-02-13 20:02:02 +00:00
|
|
|
void wait(void) {
|
|
|
|
//---------------------------------------------------------------------------------
|
|
|
|
while(true) {
|
2019-02-13 19:18:36 +00:00
|
|
|
swiWaitForVBlank();
|
|
|
|
scanKeys();
|
|
|
|
int pressed = keysDown();
|
|
|
|
if(pressed & KEY_A) break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char* GetErrorString(int code) {
|
2019-02-13 20:02:02 +00:00
|
|
|
switch(code) {
|
|
|
|
case 1:
|
|
|
|
return "fatInitDefault failed!";
|
|
|
|
case 2:
|
|
|
|
return "The MakeForwarder folder is missing!";
|
|
|
|
case 3:
|
|
|
|
return "Template.nds is missing from the MakeForwarder folder!";
|
|
|
|
case 4:
|
|
|
|
return "Error when reading the template.nds file, make sure to have the correct one!";
|
|
|
|
case 5:
|
|
|
|
return "Couldn't open the target rom, or the rom is not a valid target!";
|
|
|
|
default:
|
|
|
|
return "";
|
2019-02-13 19:18:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void PrintError(int errorcode, bool halt = false) {
|
|
|
|
consoleSetWindow(&upperScreen, 0, 3, DISPLAY_COLUMNS, 23);
|
|
|
|
consoleSelect(&upperScreen);
|
|
|
|
WriteMessage(GetErrorString(errorcode), true);
|
2019-02-13 20:02:02 +00:00
|
|
|
if(halt)
|
2019-02-13 19:18:36 +00:00
|
|
|
stop();
|
|
|
|
else {
|
|
|
|
WriteMessage("Press A to continue", false);
|
|
|
|
wait();
|
|
|
|
consoleClear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string GetHexid(std::string file, int* chk) {
|
|
|
|
std::ifstream infile(file, std::ifstream::binary);
|
|
|
|
infile.seekg(0xc);
|
2019-02-13 20:02:02 +00:00
|
|
|
if(infile.tellg() != 0xc) {
|
2019-02-13 19:18:36 +00:00
|
|
|
*chk = 1;
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
std::string gameid;
|
|
|
|
gameid.resize(4);
|
|
|
|
infile.read(&gameid[0], 4);
|
|
|
|
infile.close();
|
|
|
|
return string_to_hex(gameid);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CreateForwarder() {
|
2019-02-13 20:02:02 +00:00
|
|
|
std::vector<std::string> extensionList = { ".nds" };
|
2019-02-13 19:18:36 +00:00
|
|
|
WriteMessage("Select the target rom", true, &upperScreen);
|
|
|
|
consoleSelect(&lowerScreen);
|
|
|
|
std::string file = browseForFile(extensionList);
|
|
|
|
chdir("sd:/");
|
2019-02-13 20:02:02 +00:00
|
|
|
|
2019-02-13 19:18:36 +00:00
|
|
|
int chk = 0;
|
|
|
|
std::string gameidhex = GetHexid(file, &chk);
|
2019-02-13 20:02:02 +00:00
|
|
|
if(chk) {
|
2019-02-13 19:18:36 +00:00
|
|
|
PrintError(5);
|
|
|
|
return;
|
|
|
|
}
|
2021-08-03 02:32:09 +00:00
|
|
|
if (saveformat != ".sav") {
|
|
|
|
gameidhex = gameidhex.replace(6, 2, string_to_hex(saveformat.substr(4, 1)));
|
|
|
|
}
|
2019-02-13 19:18:36 +00:00
|
|
|
std::string folderpath("sd:/title/00030004/" + gameidhex);
|
2019-02-13 20:02:02 +00:00
|
|
|
|
2019-02-13 19:18:36 +00:00
|
|
|
if(fileExists(folderpath + "/content/00000000.app")) {
|
|
|
|
WriteMessage("A DsiWare with the same id already exists, do you want to overwrite it?", true, &upperScreen);
|
|
|
|
int ret = yesno.DoMenu(&lowerScreen);
|
|
|
|
consoleSelect(&upperScreen);
|
|
|
|
consoleClear();
|
|
|
|
if(ret)
|
|
|
|
return;
|
|
|
|
}
|
2019-02-13 20:02:02 +00:00
|
|
|
|
2019-02-13 19:18:36 +00:00
|
|
|
WriteMessage("Creating forwarder", true, &lowerScreen);
|
2019-02-13 20:02:02 +00:00
|
|
|
|
2021-08-03 02:32:09 +00:00
|
|
|
ReplaceBanner("sd:/MakeForwarder/template.nds", file, "sd:/MakeForwarder/banner.nds", gameidhex);
|
2019-02-13 20:02:02 +00:00
|
|
|
|
2019-02-13 19:18:36 +00:00
|
|
|
Patch("sd:/MakeForwarder/banner.nds", false);
|
|
|
|
|
|
|
|
MakeTmd("sd:/MakeForwarder/banner.nds", "sd:/MakeForwarder/title.tmd");
|
2019-02-13 20:02:02 +00:00
|
|
|
|
2019-02-13 19:18:36 +00:00
|
|
|
chk = PathStringReplace("title/00030004/" + gameidhex + "/data/");
|
2019-02-13 20:02:02 +00:00
|
|
|
|
2019-02-13 19:18:36 +00:00
|
|
|
if(chk) {
|
|
|
|
PrintError(chk);
|
|
|
|
remove("sd:/MakeForwarder/banner.nds");
|
|
|
|
remove("sd:/MakeForwarder/title.tmd");
|
|
|
|
return;
|
|
|
|
}
|
2019-02-13 20:02:02 +00:00
|
|
|
|
2019-02-13 19:18:36 +00:00
|
|
|
if(CreatePath("title/00030004/" + gameidhex + "/data", "sd:/") && CreatePath("title/00030004/" + gameidhex + "/content", "sd:/")) {
|
|
|
|
Movefile("sd:/MakeForwarder/banner.nds", folderpath + "/content/00000000.app");
|
|
|
|
Movefile("sd:/MakeForwarder/title.tmd", folderpath + "/content/title.tmd");
|
|
|
|
if(bootstrap_template.HasFileHandle()) {
|
2019-02-13 22:32:22 +00:00
|
|
|
bootsrtapconfig.SaveFile(folderpath + "/data/config.ini");
|
|
|
|
bootstrap_template.SetValue("NDS-BOOTSTRAP", "NDS_PATH", file.c_str());
|
2020-04-24 20:26:37 +00:00
|
|
|
std::string save = file;
|
|
|
|
auto it = std::find(save.rbegin(), save.rend(), '/');
|
|
|
|
save.insert(save.rend() - it, "saves/");
|
2021-08-03 02:32:09 +00:00
|
|
|
std::string savePath = ReplaceAll(save, ".nds", saveformat);
|
2019-02-13 22:32:22 +00:00
|
|
|
bootstrap_template.SetValue("NDS-BOOTSTRAP", "SAV_PATH", savePath.c_str());
|
|
|
|
bootstrap_template.SaveFile(folderpath + "/data/bootstrap.ini");
|
2019-02-13 19:18:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetBootstrap() {
|
2019-02-13 20:02:02 +00:00
|
|
|
std::vector<std::string> extensionList = { ".nds" };
|
2019-02-13 19:18:36 +00:00
|
|
|
WriteMessage("Select the target bootstrap file to be used", true, &upperScreen);
|
|
|
|
consoleSelect(&lowerScreen);
|
|
|
|
std::string file = browseForFile(extensionList);
|
|
|
|
chdir("sd:/");
|
2019-02-13 20:02:02 +00:00
|
|
|
|
|
|
|
size_t found = file.find_last_of("/");
|
|
|
|
|
|
|
|
std::string bootstrappath = file.substr(0, found + 1);
|
|
|
|
std::string bootstrapversion = file.substr(found + 1);
|
|
|
|
|
2019-02-13 22:32:22 +00:00
|
|
|
bootsrtapconfig.SetValue("NDS-FORWARDER", "BOOTSTRAP_PATH", bootstrappath.c_str());
|
|
|
|
bootsrtapconfig.SetValue("NDS-FORWARDER", "BOOTSTRAP_VERSION", bootstrapversion.c_str());
|
2019-02-13 20:02:02 +00:00
|
|
|
|
2019-02-13 22:32:22 +00:00
|
|
|
bootstrap_template.LoadFile(bootstrappath + "nds-bootstrap.ini");
|
2019-02-13 19:18:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CheckResources() {
|
2019-02-13 20:02:02 +00:00
|
|
|
if(!fileExists("sd:/MakeForwarder"))
|
2019-02-13 19:18:36 +00:00
|
|
|
PrintError(2, true);
|
2019-02-13 20:02:02 +00:00
|
|
|
if(!fileExists("sd:/MakeForwarder/template.nds"))
|
2019-02-13 19:18:36 +00:00
|
|
|
PrintError(3, true);
|
|
|
|
std::ifstream ndstemplate("sd:/MakeForwarder/template.nds", std::ifstream::binary);
|
|
|
|
std::string str((std::istreambuf_iterator<char>(ndstemplate)),
|
2019-02-13 20:02:02 +00:00
|
|
|
std::istreambuf_iterator<char>());
|
2019-02-13 19:18:36 +00:00
|
|
|
std::size_t found = str.find("sd:/kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk");
|
|
|
|
if(found == std::string::npos)
|
|
|
|
PrintError(4, true);
|
|
|
|
}
|
|
|
|
|
2021-08-03 02:32:09 +00:00
|
|
|
void SetSavefile() {
|
|
|
|
WriteMessage("Select your target save file", true, &upperScreen);
|
|
|
|
int ret = savefilemenu.DoMenu(&lowerScreen);
|
|
|
|
if(ret != 0) {
|
|
|
|
saveformat = ".sav" + std::to_string(ret);
|
|
|
|
}
|
|
|
|
consoleSelect(&upperScreen);
|
|
|
|
consoleClear();
|
|
|
|
}
|
|
|
|
|
2019-02-13 19:18:36 +00:00
|
|
|
int main() {
|
|
|
|
displayInit();
|
|
|
|
consoleSetWindow(&upperScreen, 0, 0, DISPLAY_COLUMNS, 3);
|
2020-04-25 16:07:11 +00:00
|
|
|
WriteMessage("Forwarder maker by edo9300 v1.1b", true, &upperScreen);
|
2019-02-13 19:18:36 +00:00
|
|
|
consoleSetWindow(&upperScreen, 0, 3, DISPLAY_COLUMNS, 23);
|
2019-02-13 20:02:02 +00:00
|
|
|
if(!fatInitDefault())
|
2019-02-13 19:18:36 +00:00
|
|
|
PrintError(1, true);
|
|
|
|
CheckResources();
|
|
|
|
menu mainmenu;
|
|
|
|
mainmenu.AddOption("Create Forwarder");
|
|
|
|
mainmenu.AddOption("Set target bootstrap");
|
2021-08-03 02:32:09 +00:00
|
|
|
mainmenu.AddOption("Select target save file");
|
|
|
|
savefilemenu.AddOption("Default Save file (.sav)");
|
|
|
|
savefilemenu.AddOption("Save file 1 (.sav1)");
|
|
|
|
savefilemenu.AddOption("Save file 2 (.sav2)");
|
|
|
|
savefilemenu.AddOption("Save file 3 (.sav3)");
|
|
|
|
savefilemenu.AddOption("Save file 4 (.sav4)");
|
|
|
|
savefilemenu.AddOption("Save file 5 (.sav5)");
|
|
|
|
savefilemenu.AddOption("Save file 6 (.sav6)");
|
|
|
|
savefilemenu.AddOption("Save file 7 (.sav7)");
|
|
|
|
savefilemenu.AddOption("Save file 8 (.sav8)");
|
|
|
|
savefilemenu.AddOption("Save file 9 (.sav9)");
|
2019-02-13 19:18:36 +00:00
|
|
|
yesno.AddOption("Yes");
|
|
|
|
yesno.AddOption("No");
|
2019-02-13 20:02:02 +00:00
|
|
|
while(true) {
|
2019-02-13 19:18:36 +00:00
|
|
|
swiWaitForVBlank();
|
|
|
|
WriteMessage("Use the option \"Set target bootstrap\" to avoid configuring the created forwarders at startup", true, &upperScreen);
|
|
|
|
int ret = mainmenu.DoMenu(&lowerScreen);
|
2019-02-13 20:02:02 +00:00
|
|
|
if(ret == 0)
|
2019-02-13 19:18:36 +00:00
|
|
|
CreateForwarder();
|
2019-02-13 20:02:02 +00:00
|
|
|
else if(ret == 1)
|
2019-02-13 19:18:36 +00:00
|
|
|
SetBootstrap();
|
2021-08-03 02:32:09 +00:00
|
|
|
else if(ret == 2) {
|
|
|
|
SetSavefile();
|
|
|
|
}
|
2019-02-13 19:18:36 +00:00
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
stop();
|
|
|
|
return 0;
|
|
|
|
}
|