/* * Hook * Append own ARM7 code without affecting CRC32 of the file and patchability * This could be used for trainers */ void Hook(char *ndsfilename, char *arm7filename) { fNDS = fopen(ndsfilename, "r+b"); if (!fNDS) { fprintf(stderr, "Cannot open file '%s'.\n", ndsfilename); exit(1); } fseek(fNDS, 0, SEEK_SET); fread(&header, 512, 1, fNDS); // load additional ARM7 code FILE *fARM7 = fopen(arm7filename, "rb"); if (!fARM7) { fprintf(stderr, "Cannot open file '%s'.\n", arm7filename); exit(1); } fseek(fARM7, 0, SEEK_END); unsigned int add_arm7_size = (ftell(fARM7) + 3) &~ 3; unsigned char *add_arm7 = new unsigned char [add_arm7_size]; fseek(fARM7, 0, SEEK_SET); fread(add_arm7, 1, add_arm7_size, fARM7); fclose(fARM7); // restore backup of original header if found if (header.offset_0x160) { fseek(fNDS, header.offset_0x78, SEEK_SET); Header originalHeader; fread(&originalHeader, 512, 1, fNDS); if (*(unsigned int *)header.gamecode == *(unsigned int *)originalHeader.gamecode) header = originalHeader; } // calculate new offsets unsigned int new_arm7_offset = (header.application_end_offset + 0x100 + 0x1FF) &~ 0x1FF; unsigned int add_arm7_offset = new_arm7_offset + header.arm7_size; unsigned int header_backup_offset = add_arm7_offset + add_arm7_size; unsigned int new_application_end_offset = header_backup_offset + 0x200; // read original ARM7 code unsigned char *arm7 = new unsigned char [header.arm7_size]; fseek(fNDS, header.arm7_rom_offset, SEEK_SET); fread(arm7, 1, header.arm7_size, fNDS); // write original header, original ARM7 code and append own ARM7 code //fseek(fNDS, new_arm7_offset, SEEK_SET); //fwrite(arm7, 1, header.arm7_size, fNDS); FFixCrc32(fNDS, new_arm7_offset, arm7, header.arm7_size); //fseek(fNDS, add_arm7_offset, SEEK_SET); //fwrite(add_arm7, 1, add_arm7_size, fNDS); FFixCrc32(fNDS, add_arm7_offset, add_arm7, add_arm7_size); //fseek(fNDS, header_backup_offset, SEEK_SET); //fwrite(&header, 1, 0x200, fNDS); FFixCrc32(fNDS, header_backup_offset, (unsigned char *)&header, 0x200); // write new header information header.offset_0x78 = header_backup_offset; // ROM offset of header backup header.offset_0x7C = header.arm7_ram_address + header.arm7_size + add_arm7_size; // RAM location of header backup header.arm7_entry_address = header.arm7_entry_address + header.arm7_size; header.arm9_entry_address = 0x027FFE18; *(unsigned_int *)(header.reserved1 + 3) = 0xE59FF004; header.arm7_rom_offset = new_arm7_offset; header.arm7_size = header.arm7_size + add_arm7_size + 0x200; // also load our code and the original header into memory header.application_end_offset = new_application_end_offset; header.header_crc = CalcHeaderCRC(header); //fseek(fNDS, 0, SEEK_SET); //fwrite(&header, 1, 0x200, fNDS); FFixCrc32(fNDS, 0, (unsigned char *)&header, 0x200, header.application_end_offset); fclose(fNDS); }
/* * Create */ void Create() { fNDS = fopen(ndsfilename, "wb"); if (!fNDS) { fprintf(stderr, "Cannot open file '%s'.\n", ndsfilename); exit(1); } bool bSecureSyscalls = false; char *headerfilename = (headerfilename_or_size && (strtoul(headerfilename_or_size,0,0) == 0)) ? headerfilename_or_size : 0; u32 headersize = headerfilename_or_size ? strtoul(headerfilename_or_size,0,0) : 0x200; // initial header data if (headerfilename) { // header template FILE *fi = fopen(headerfilename, "rb"); if (!fi) { fprintf(stderr, "Cannot open file '%s'.\n", headerfilename); exit(1); } fread(&header, 1, 0x200, fi); fclose(fi); if ((header.arm9_ram_address + 0x800 == header.arm9_entry_address) || (header.rom_header_size > 0x200)) { bSecureSyscalls = true; } } else // set header default values { // clear header memset(&header, 0, sizeof(header)); memcpy(header.gamecode, "####", 4); if ((arm9RamAddress + 0x800 == arm9Entry) || (headersize > 0x200)) { bSecureSyscalls = true; } else { header.reserved2 = 0x04; // autostart *(unsigned_int *)(((unsigned char *)&header) + 0x0) = 0xEA00002E; // for PassMe's that start @ 0x08000000 } *(unsigned_int *)(((unsigned char *)&header) + 0x60) = 1<<22 | latency2<<16 | 1<<14 | 1<<13 | latency1; // ROM control info 1 *(unsigned_int *)(((unsigned char *)&header) + 0x64) = 1<<29 | latency2<<16 | latency1; // ROM control info 2 *(unsigned_short *)(((unsigned char *)&header) + 0x6E) = 0x051E; // ROM control info 3 } if (headersize) header.rom_header_size = headersize; if (header.rom_header_size == 0) header.rom_header_size = bSecureSyscalls ? 0x4000 : 0x200; // load a logo if (logofilename) { char *p = strrchr(logofilename, '.'); if (!strcmp(p, ".bmp")) { CRaster raster; if (raster.LoadBMP(logofilename) < 0) exit(1); unsigned char white = (raster.palette[0].rgbGreen >= 128) ? 0 : 1; if (LogoConvert(raster.raster, header.logo, white) < 0) exit(1); } else { FILE *fi = fopen(logofilename, "rb"); if (!fi) { fprintf(stderr, "Cannot open file '%s'.\n", logofilename); exit(1); } fread(&header.logo, 1, 156, fi); fclose(fi); } } else if (bSecureSyscalls) // use Nintendo logo { memcpy(((unsigned char *)&header) + 0xC0, nintendo_logo, sizeof(nintendo_logo)); } else // add small NDS loader { if (loadme_size != 156) { fprintf(stderr, "loadme size error\n"); exit(1); } memcpy(header.logo, loadme, loadme_size); // self-contained NDS loader for *Me GBA cartridge boot memcpy(&header.offset_0xA0, "SRAM_V110", 9); // allow GBA cartridge SRAM backup memcpy(&header.offset_0xAC, "PASS01\x96", 7); // automatically start with FlashMe, make it look more like a GBA rom } // override default title/game/maker codes if (title) strncpy(header.title, title, 12); if (gamecode) strncpy(header.gamecode, gamecode, 4); if (makercode) strncpy((char *)header.makercode, makercode, 2); // -------------------------- fseek(fNDS, header.rom_header_size, SEEK_SET); // ARM9 binary if (arm9filename) { header.arm9_rom_offset = (ftell(fNDS) + arm9_align) &~ arm9_align; fseek(fNDS, header.arm9_rom_offset, SEEK_SET); unsigned int entry_address = arm9Entry ? arm9Entry : (unsigned int)header.arm9_entry_address; // template unsigned int ram_address = arm9RamAddress ? arm9RamAddress : (unsigned int)header.arm9_ram_address; // template if (!ram_address && entry_address) ram_address = entry_address; if (!entry_address && ram_address) entry_address = ram_address; if (!ram_address) { ram_address = entry_address = 0x02000000; } // add dummy area for secure syscalls header.arm9_size = 0; if (bSecureSyscalls) { unsigned_int x; FILE *fARM9 = fopen(arm9filename, "rb"); if (fARM9) { fread(&x, sizeof(x), 1, fARM9); fclose(fARM9); if (x != 0xE7FFDEFF) // not already exist? { x = 0xE7FFDEFF; for (int i=0; i<0x800/4; i++) fwrite(&x, sizeof(x), 1, fNDS); header.arm9_size = 0x800; } } } unsigned int size = 0; if (HasElfExtension(arm9filename) || HasElfHeader(arm9filename) ) CopyFromElf(arm9filename, &entry_address, &ram_address, &size); else CopyFromBin(arm9filename, 0, &size); header.arm9_entry_address = entry_address; header.arm9_ram_address = ram_address; header.arm9_size = header.arm9_size + ((size + 3) &~ 3); } else { fprintf(stderr, "ARM9 binary file required.\n"); exit(1); } // ARM9 overlay table if (arm9ovltablefilename) { unsigned_int x1 = 0xDEC00621; fwrite(&x1, sizeof(x1), 1, fNDS); // 0x2106c0de magic unsigned_int x2 = 0x00000AD8; fwrite(&x2, sizeof(x2), 1, fNDS); // ??? unsigned_int x3 = 0x00000000; fwrite(&x3, sizeof(x3), 1, fNDS); // ??? header.arm9_overlay_offset = ftell(fNDS); // do not align fseek(fNDS, header.arm9_overlay_offset, SEEK_SET); unsigned int size = 0; CopyFromBin(arm9ovltablefilename, &size); header.arm9_overlay_size = size; overlay_files += size / sizeof(OverlayEntry); if (!size) header.arm9_overlay_offset = 0; } // COULD BE HERE: ARM9 overlay files, no padding before or between. end is padded with 0xFF's and then followed by ARM7 binary // fseek(fNDS, 1388772, SEEK_CUR); // test for ASME // ARM7 binary header.arm7_rom_offset = (ftell(fNDS) + arm7_align) &~ arm7_align; fseek(fNDS, header.arm7_rom_offset, SEEK_SET); char *devkitProPATH; devkitProPATH = getenv("DEVKITPRO"); #ifdef __WIN32__ // convert to standard windows path if ( devkitProPATH && devkitProPATH[0] == '/' ) { devkitProPATH[0] = devkitProPATH[1]; devkitProPATH[1] = ':'; } #endif if ( !arm7filename) { char arm7PathName[MAXPATHLEN]; if (!devkitProPATH) { fprintf(stderr,"No arm7 specified and DEVKITPRO missing from environment!\n"); exit(1); } strcpy(arm7PathName,devkitProPATH); strcat(arm7PathName,"/libnds/default.elf"); arm7filename = arm7PathName; } unsigned int entry_address = arm7Entry ? arm7Entry : (unsigned int)header.arm7_entry_address; // template unsigned int ram_address = arm7RamAddress ? arm7RamAddress : (unsigned int)header.arm7_ram_address; // template if (!ram_address && entry_address) ram_address = entry_address; if (!entry_address && ram_address) entry_address = ram_address; if (!ram_address) { ram_address = entry_address = 0x037f8000; } unsigned int size = 0; if (HasElfExtension(arm7filename)) CopyFromElf(arm7filename, &entry_address, &ram_address, &size); else CopyFromBin(arm7filename, &size); header.arm7_entry_address = entry_address; header.arm7_ram_address = ram_address; header.arm7_size = ((size + 3) &~ 3); // ARM7 overlay table if (arm7ovltablefilename) { header.arm7_overlay_offset = ftell(fNDS); // do not align fseek(fNDS, header.arm7_overlay_offset, SEEK_SET); unsigned int size = 0; CopyFromBin(arm7ovltablefilename, &size); header.arm7_overlay_size = size; overlay_files += size / sizeof(OverlayEntry); if (!size) header.arm7_overlay_offset = 0; } // COULD BE HERE: probably ARM7 overlay files, just like for ARM9 // if (overlay_files && !overlaydir) { fprintf(stderr, "Overlay directory required!.\n"); exit(1); } // filesystem //if (filerootdir || overlaydir) { // read directory structure free_file_id = overlay_files; free_dir_id++; directory_count++; TreeNode *filetree; if (filerootdir) filetree = ReadDirectory(new TreeNode(), filerootdir); else filetree = new TreeNode(); // dummy root node 0xF000 // calculate offsets required for FNT and FAT _entry_start = 8*directory_count; // names come after directory structs header.fnt_offset = (ftell(fNDS) + fnt_align) &~ fnt_align; header.fnt_size = _entry_start + // directory structs total_name_size + // total number of name characters for dirs and files directory_count*4 + // directory: name length (1), dir id (2), end-character (1) file_count*1 + // files: name length (1) - 3; // root directory only has an end-character file_count += overlay_files; // didn't take overlay files into FNT size, but have to be calculated into FAT size header.fat_offset = (header.fnt_offset + header.fnt_size + fat_align) &~ fat_align; header.fat_size = file_count * 8; // each entry contains top & bottom offset // banner after FNT/FAT if (bannerfilename) { header.banner_offset = (header.fat_offset + header.fat_size + banner_align) &~ banner_align; file_top = header.banner_offset + 0x840; fseek(fNDS, header.banner_offset, SEEK_SET); if (bannertype == BANNER_IMAGE) { char * Ext = strrchr(bannerfilename, '.'); if (Ext && strcasecmp(Ext, ".bmp") == 0) IconFromBMP(); else if (Ext && strcasecmp(Ext, ".grf") == 0) IconFromGRF(); else { fprintf(stderr, "Banner File Error: Unknown extension '%s'!\n", Ext); exit(1); } } else { CopyFromBin(bannerfilename, 0); } } else { file_top = header.fat_offset + header.fat_size; header.banner_offset = 0; } file_end = file_top; // no file data as yet // add (hidden) overlay files for (unsigned int i=0; i<overlay_files; i++) { char s[32]; sprintf(s, OVERLAY_FMT, i/*free_file_id*/); AddFile(overlaydir, "/", s, i/*free_file_id*/); //free_file_id++; // incremented up to overlay_files } // add all other (visible) files AddDirectory(filetree, "/", 0xF000, directory_count); fseek(fNDS, file_end, SEEK_SET); if (verbose) { printf("%u directories.\n", directory_count); printf("%u normal files.\n", file_count - overlay_files); printf("%u overlay files.\n", overlay_files); } } // -------------------------- // align file size unsigned int newfilesize = file_end; //ftell(fNDS); newfilesize = (newfilesize + 3) & ~3; // align to 4 bytes header.application_end_offset = newfilesize; if (newfilesize != file_end ) { fseek(fNDS, newfilesize-1, SEEK_SET); fputc(0, fNDS); } // calculate device capacity newfilesize |= newfilesize >> 16; newfilesize |= newfilesize >> 8; newfilesize |= newfilesize >> 4; newfilesize |= newfilesize >> 2; newfilesize |= newfilesize >> 1; newfilesize++; if (newfilesize <= 128*1024) newfilesize = 128*1024; int devcap = -18; unsigned int x = newfilesize; while (x != 0) { x >>= 1; devcap++; } header.devicecap = (devcap < 0) ? 0 : devcap; // fix up header CRCs and write header header.logo_crc = CalcLogoCRC(header); header.header_crc = CalcHeaderCRC(header); fseek(fNDS, 0, SEEK_SET); fwrite(&header, 0x200, 1, fNDS); fclose(fNDS); }