tiboyse/tiboyse_makeapp/makeapp.cpp
Brendan Fletcher 6482429a81 Initial commit
2020-03-04 02:05:06 -05:00

212 lines
6.5 KiB
C++

#include <cstring>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <limits>
#include <string>
#include "rabbitsign.h"
#ifdef USE_WINDOWS_RESOURCE
#include <Windows.h>
#include "resource.h"
#endif
const unsigned long TIBOY_SIZE = 0x4000 - 96;
void wait()
{
std::cout << "< Press Enter to continue >";
getchar();
}
unsigned char getHex(char letter)
{
if(letter >= '0' && letter <= '9') return letter - '0';
if(letter >= 'A' && letter <= 'Z') return letter - 'A' + 0xA;
if(letter >= 'a' && letter <= 'z') return letter - 'a' + 0xA;
return 0;
}
std::string escape(std::string& name)
{
std::string valid = name;
size_t search = std::string::npos;
while((search = name.find('\\',search+1)) != std::string::npos)
{
name.replace(search, 3, 1, getHex(name[search+1])*16 + getHex(name[search+2]));
valid.replace(search, 3, 1, '-');
}
return valid;
}
int main(int argc, char** argv)
{
std::cout << "ROM to APP converter for TI-Boy Beta 0.2.04\n\n";
if (argc < 3)
{
atexit(wait);
}
std::string romfilename;
if(argc >= 2)
{
romfilename = argv[1];
}
else
{
std::cout << "Enter the filepath/name of the ROM to convert: ";
std::getline(std::cin, romfilename);
}
std::ifstream romfile;
romfile.open(romfilename.c_str(), std::ios::binary | std::ios::ate);
if(romfile.fail())
{
std::cout << "Error opening ROM file!\n";
return EXIT_FAILURE;
}
unsigned long romfilesize = (unsigned long)romfile.tellg();
romfile.seekg(0, std::ios::beg);
if (romfilesize & 0x3FFF)
{
std::cout << "Warning, ROM file is not a multiple of 16KB!\n";
std::cout << "Ignoring last " << (romfilesize & 0x3FFF) << " bytes.\n\n";
romfilesize &= ~0x3FFF;
}
char* romdata = new char[romfilesize];
romfile.read(romdata, romfilesize);
romfile.close();
char internal_name[16];
strncpy(internal_name, romdata+0x134, 15);
internal_name[15] = 0;
std::cout << "Successfully opened ROM: " << internal_name << "\n";
std::string appname, appfilename;
size_t maxName = (romdata[0x149] == 3 ? 7 : 8);
if(argc >= 3)
{
appfilename = escape(appname = argv[2]);
}
if(argc < 3 || appname.length() > maxName)
{
if (argc >= 3)
{
atexit(wait);
}
do
{
std::cout << "Enter a name for the APP (" << maxName << " char max): ";
std::cin >> appname;
std::cin.ignore();
appfilename = escape(appname);
}
while(appname.length() > maxName);
}
std::string appPath = argv[0];
appPath = appPath.substr(0,appPath.find_last_of('\\')+1);
#ifdef USE_WINDOWS_RESOURCE
HRSRC resourceInfo = FindResource(nullptr, MAKEINTRESOURCE(TIBOYSE_RESOURCE), RT_RCDATA);
if (resourceInfo == nullptr)
{
std::cout << "Error: Could not find embedded tiboyse resource\n";
return EXIT_FAILURE;
}
if (SizeofResource(nullptr, resourceInfo) != TIBOY_SIZE)
{
std::cout << "Error: Embedded tiboyse resource seems to be corrupt (incorrect size)\n";
return EXIT_FAILURE;
}
HGLOBAL resourceData = LoadResource(nullptr, resourceInfo);
if (resourceData == nullptr)
{
std::cout << "Error: Failed to load embedded tiboyse resource\n";
return EXIT_FAILURE;
}
const void* resourcePtr = LockResource(resourceData);
if (resourcePtr == nullptr)
{
std::cout << "Error: Failed to lock embedded tiboyse resource\n";
return EXIT_FAILURE;
}
#else
std::ifstream tiboyfile;
tiboyfile.open((appPath+"tiboyse.bin").c_str(), std::ios::binary | std::ios::ate);
if(tiboyfile.fail())
{
std::cout << "Error: Could not locate vital file tiboyse.bin\n";
return EXIT_FAILURE;
}
if((unsigned long)tiboyfile.tellg() != TIBOY_SIZE)
{
std::cout << "Error: tiboyse.bin seems to be corrupt (incorrect file size)\n";
return EXIT_FAILURE;
}
tiboyfile.seekg(0, std::ios::beg);
#endif
unsigned long newsize = romfilesize;
unsigned char comp_byte = romdata[--newsize];
if(comp_byte == 0x00 || comp_byte == 0xFF)
while(romdata[--newsize] == comp_byte);
romfilesize = (newsize | 0x3FFF) + 1;
char* appdata = (char*)malloc(TIBOY_SIZE + romfilesize);
#ifdef USE_WINDOWS_RESOURCE
memcpy(appdata, resourcePtr, TIBOY_SIZE);
#else
tiboyfile.read(appdata, TIBOY_SIZE);
tiboyfile.close();
#endif
memcpy(appdata + 0x0012, appname.c_str(), appname.length());
memcpy(appdata + TIBOY_SIZE, romdata + TIBOY_SIZE, romfilesize - TIBOY_SIZE);
memcpy(appdata + romfilesize, romdata, TIBOY_SIZE);
RSKey *key = rs_key_new();
rs_key_find_for_id(key, 0x0104, 0);
RSProgram *prgm = rs_program_new();
prgm->calctype = RS_CALC_TI83P;
prgm->datatype = RS_DATA_APP;
prgm->data = (unsigned char*)appdata;
prgm->length = prgm->length_a = TIBOY_SIZE + romfilesize;
rs_repair_ti8x_app(prgm, (RSRepairFlags)((int)RS_IGNORE_ALL_WARNINGS | (int)RS_FIX_PAGE_COUNT));
if (rs_sign_ti8x_app(prgm, key, 0) == RS_SUCCESS)
{
std::string binout = appPath + appfilename + ".8xk";
FILE *appfile = fopen(binout.c_str(), "wb");
if (rs_write_program_file(prgm, appfile, 0, 0, 0, (RSOutputFlags)0) == RS_SUCCESS)
{
std::cout << "\nGenerated a " << romfilesize + 0x4000 << " byte (" << romfilesize/0x4000+1 << " page) APP.\n\n";
if((unsigned char)romdata[0x143] == 0xC0)
std::cout << "Warning: This is a Game Boy Color ROM. TI-Boy does not support GBC.\n\n";
if(romfilesize > 1540096)
std::cout << "Warning: This APP will not fit on any TI-83+ or TI-84+ calculator.\n\n";
else if(romfilesize > 491520)
std::cout << "Warning: This APP will only fit on a Silver Edition calculator.\n\n";
if(romdata[0x149] == 3)
std::cout << "Warning: This APP may require more than 48KB RAM to emulate.\nIt might not work on new TI-84+ models.\nHowever, Pokemon Red/Blue/Yellow have been confirmed to work on all models.\nTo see if your model has 128KB RAM, check the serial number on the back.\nIf the ending letter is from A-G or has no letter, there is enough RAM.\nOtherwise, just test it and see if it gives an error.\n\n";
}
else
{
std::cout << "\nAn error occurred when writing to the output file!\n\n";
}
fclose(appfile);
}
else
{
std::cout << "\nAn error occurred when signing the application!\n\n";
}
return EXIT_SUCCESS;
}