/* Nintendo DS rom tool by Rafael Vuijk (aka DarkFader) */ #include #include "ndstool.h" #include "sha1.h" #include "ndscreate.h" #include "ndsextract.h" #include "passme.h" #include "hook.h" #include "encryption.h" /* * Variables */ int verbose = 0; Header header; FILE *fNDS = 0; char *romlistfilename = 0; char *filemasks[MAX_FILEMASKS]; int filemask_num = 0; char *ndsfilename = 0; char *arm7filename = 0; char *arm9filename = 0; char *filerootdir = 0; char *overlaydir = 0; char *arm7ovltablefilename = 0; char *arm9ovltablefilename = 0; char *bannerfilename = 0; char *bannertext = 0; //bool compatibility = false; char *headerfilename_or_size = 0; //char *uniquefilename = 0; char *logofilename = 0; char *title = 0; char *makercode = 0; char *gamecode = 0; char *vhdfilename = 0; char *sramfilename = 0; int latency1 = 0x1FFF; //0x8F8; int latency2 = 0x3F; //0x18; int romversion = 0; char endecrypt_option = 0; int bannertype; unsigned int arm9RamAddress = 0; unsigned int arm7RamAddress = 0; unsigned int arm9Entry = 0; unsigned int arm7Entry = 0; /* * Title */ void Title() { printf("Nintendo DS rom tool "VERSION" - %s\nby Rafael Vuijk, Dave Murphy, Alexei Karpenko\n",CompileDate); } /* * Help data */ struct HelpLine { const char *option_char; const char *text; void Print() { char s[1024]; strcpy(s, text); printf("%-22s ", strtok(s, "\n")); char *p = strtok(0, "\n"); if (p) printf("%-30s ", p); for (int i=0; ; i++) { char *p = strtok(0, "\n"); if (!p) { printf("\n"); break; } if (i) printf("\n%54s", ""); printf("%s", p); } } }; HelpLine helplines[] = { {"", "Parameter\nSyntax\nComments"}, {"", "---------\n------\n--------"}, {"?", "Show this help:\n-?[option]\nAll or single help for an option."}, {"i", "Show information:\n-i [file.nds]\nHeader information."}, {"v", " Show more info\n-v [roms_rc.dat]\nChecksums, warnings, release info"}, {"p", "PassMe:\n-p [file.nds] [passme.vhd] [passme.sav]\nGenerates PassMe2 files."}, {"k", "Hook ARM7 executable\n-k [file.nds]\nCurrently not tested."}, {"f", "Fix header CRC\n-f [file.nds]\nYou only need this after manual editing."}, //{"T", "Test\n-T [file.nds]"}, {"s", "En/decrypt secure area\n-s[e|E|d] [file.nds]\nEn/decrypt the secure area and\nput/remove card encryption tables and test patterns.\nOptionally add: d for decryption, e/E for encryption.\n(e: Nintendo offsets, E: others)"}, //{"@", "Hash file & compare:\n-@ [arm7.bin]"}, // used in buildscript {"1", "List files:\n-l [file.nds]\nGive a list of contained files."}, {"v", " Show offsets/sizes\n-v"}, {"cx", "Create/Extract\n-c/-x [file.nds]"}, {"v", " Show more info\n-v[v...]\nShow filenames and more header info.\nUse multiple v's for more information."}, {"9", " ARM9 executable\n-9 file.bin"}, {"7", " ARM7 executable\n-7 file.bin"}, {"y9", " ARM9 overlay table\n-y9 file.bin"}, {"y7", " ARM7 overlay table\n-y7 file.bin"}, {"d", " Data files\n-d directory"}, {"y", " Overlay files\n-y directory"}, {"b", " Banner bitmap/text\n-b file.bmp \"text;text;text\"\nThe three lines are shown at different sizes."}, {"t", " Banner binary\n-t file.bin"}, {"h", " Header template\n-h file.bin\nUse the header from another ROM as a template."}, {"h", " Header size\n-h size\nA header size of 0x4000 is default for real cards, 0x200 for homebrew."}, {"n", " Latency\n-n [L1] [L2]\ndefault=maximum"}, {"o", " Logo bitmap/binary\n-o file.bmp/file.bin"}, {"g", " Game info\n-g gamecode [makercode] [title] [rom ver]\nSets game-specific information.\nGame code is 4 characters. Maker code is 2 characters.\nTitle can be up to 12 characters."}, {"r", " ARM9 RAM address\n-r9 address"}, {"r", " ARM7 RAM address\n-r7 address"}, {"e", " ARM9 RAM entry\n-e9 address"}, {"e", " ARM7 RAM entry\n-e7 address"}, {"w", " Wildcard filemask(s)\n-w [filemask]...\n* and ? are wildcard characters."}, }; /* * Help */ void Help(char *specificoption = 0) { Title(); printf("\n"); if (specificoption) { bool found = false; for (unsigned int i=0; i<(sizeof(helplines) / sizeof(helplines[0])); i++) { for (const char *o = helplines[i].option_char; *o; o++) { if (*o == *specificoption) { helplines[i].Print(); found = true; } } } if (!found) { printf("Unknown option: %s\n\n", specificoption); } } else { for (unsigned int i=0; i<(sizeof(helplines) / sizeof(helplines[0])); i++) { helplines[i].Print(); } printf("\n"); printf("You only need to specify the NDS filename once if you want to perform multiple actions.\n"); printf("Actions are performed in the specified order.\n"); printf("Addresses can be prefixed with '0x' to use hexadecimal format.\n"); } } #define REQUIRED(var) var = ((argc > a+1) ? argv[++a] : 0) // must precede OPTIONAL #define OPTIONAL(var) { /*fprintf(stderr, "'%s'\n", argv[a]);*/ char *t = ((argc > a+1) && (argv[a+1][0] != '-') ? argv[++a] : 0); if (!var) var = t; else if (t) fprintf(stderr, "%s is already specified!\n", #var); } // final paramter requirement checks are done when performing actions #define OPTIONAL_INT(var) { char *t = ((argc > a+1) && (argv[a+1][0] != '-') ? argv[++a] : 0); if (t) var = strtoul(t,0,0); } // like OPTIONAL, but for (positive) integers #define MAX_ACTIONS 32 #define ADDACTION(a) { if (num_actions < MAX_ACTIONS) actions[num_actions++] = a; } enum { ACTION_SHOWINFO, ACTION_FIXHEADERCRC, ACTION_ENCRYPTSECUREAREA, ACTION_PASSME, ACTION_LISTFILES, ACTION_EXTRACT, ACTION_CREATE, ACTION_HASHFILE, ACTION_HOOK, }; /* * main */ int main(int argc, char *argv[]) { #ifdef _NDSTOOL_P_H if (sizeof(Header) != 0x200) { fprintf(stderr, "Header size %d != %d\n", sizeof(Header), 0x200); return 1; } #endif if (argc < 2) { Help(); return 0; } int num_actions = 0; int actions[MAX_ACTIONS]; /* * parse parameters to actions */ for (int a=1; a= MAX_FILEMASKS) return 1; } break; } case 'c': // create { ADDACTION(ACTION_CREATE); OPTIONAL(ndsfilename); break; } // file root directory case 'd': REQUIRED(filerootdir); break; // ARM7 filename case '7': REQUIRED(arm7filename); break; // ARM9 filename case '9': REQUIRED(arm9filename); break; // hash file case '@': { ADDACTION(ACTION_HASHFILE); OPTIONAL(arm7filename); break; } // hook ARM7 executable case 'k': { ADDACTION(ACTION_HOOK); OPTIONAL(ndsfilename); break; } case 't': REQUIRED(bannerfilename); bannertype = BANNER_BINARY; break; case 'b': bannertype = BANNER_IMAGE; REQUIRED(bannerfilename); REQUIRED(bannertext); break; case 'o': REQUIRED(logofilename); break; case 'h': // load header or header size REQUIRED(headerfilename_or_size); break; /*case 'u': // unique ID file REQUIRED(uniquefilename); break;*/ case 'v': // verbose for (char *p=argv[a]; *p; p++) if (*p == 'v') verbose++; OPTIONAL(romlistfilename); break; case 'n': // latency //compatibility = true; OPTIONAL_INT(latency1); OPTIONAL_INT(latency2); break; case 'r': // RAM address switch (argv[a][2]) { case '7': arm7RamAddress = (argc > a) ? strtoul(argv[++a], 0, 0) : 0; break; case '9': arm9RamAddress = (argc > a) ? strtoul(argv[++a], 0, 0) : 0; break; default: Help(argv[a]); return 1; } break; case 'e': // entry point switch (argv[a][2]) { case '7': arm7Entry = (argc > a) ? strtoul(argv[++a], 0, 0) : 0; break; case '9': arm9Entry = (argc > a) ? strtoul(argv[++a], 0, 0) : 0; break; default: Help(argv[a]); return 1; } break; case 'm': // maker code REQUIRED(makercode); break; case 'g': // game code REQUIRED(gamecode); OPTIONAL(makercode); OPTIONAL(title); OPTIONAL_INT(romversion); break; case 'y': // overlay table file / directory switch (argv[a][2]) { case '7': REQUIRED(arm7ovltablefilename); break; case '9': REQUIRED(arm9ovltablefilename); break; case 0: REQUIRED(overlaydir); break; default: Help(argv[a]); return 1; } break; case '?': // global or specific help { Help(argv[a][2] ? argv[a]+2 : 0); // 0=global help return 0; // do not perform any other actions } default: { Help(argv[a]); return 1; } } } else { //Help(); if (ndsfilename) { fprintf(stderr, "NDS filename is already given!\n"); return 1; } ndsfilename = argv[a]; break; } } Title(); /* * sanity checks */ if (gamecode) { if (strlen(gamecode) != 4) { fprintf(stderr, "Game code must be 4 characters!\n"); return 1; } for (int i=0; i<4; i++) if ((gamecode[i] >= 'a') && (gamecode[i] <= 'z')) { fprintf(stderr, "Warning: Gamecode contains lowercase characters.\n"); break; } if (gamecode[0] == 'A') { fprintf(stderr, "Warning: Gamecode starts with 'A', which might be used for another commercial product.\n"); } } if (makercode && (strlen(makercode) != 2)) { fprintf(stderr, "Maker code must be 2 characters!\n"); return 1; } if (title && (strlen(title) > 12)) { fprintf(stderr, "Title can be no more than 12 characters!\n"); return 1; } /* * perform actions */ int status = 0; for (int i=0; i 0) { for (int i=0; i