dslinux/toolchain/ndstool/source banner.cpp bigint.cpp crc.cpp header.cpp hook.cpp logo.cpp ndscodes.cpp ndscreate.cpp ndsextract.cpp ndstool.cpp ndstree.cpp passme.cpp passme.vhd raster.cpp sha1.cpp
stsp
stsp at user.in-berlin.de
Sun Aug 13 22:25:33 CEST 2006
Update of /cvsroot/dslinux/dslinux/toolchain/ndstool/source
In directory antilope:/tmp/cvs-serv5897/source
Added Files:
banner.cpp bigint.cpp crc.cpp header.cpp hook.cpp logo.cpp
ndscodes.cpp ndscreate.cpp ndsextract.cpp ndstool.cpp
ndstree.cpp passme.cpp passme.vhd raster.cpp sha1.cpp
Log Message:
Adding pristine copy of ndstool so I can branch from it.
--- NEW FILE: logo.cpp ---
/*
costs per 4 pixels: (# = black)
1 bit:
----
4 bits:
#---
##--
--##
####
5 bits:
---#
#-##
-###
-#--
--#-
6 bits:
#-#-
-##-
###-
#--#
-#-#
##-#
*/
#include <stdio.h>
void LogoPackBits(unsigned char *srcp, unsigned char *destp)
{
unsigned int destbit = 0;
unsigned int destvalue = 0;
unsigned int d = 0;
for (unsigned int s=0; s<1664; s++)
{
if (srcp[s]) destvalue |= 1<<destbit;
if (++destbit == 8)
{
destp[d++] = destvalue;
destbit = 0;
destvalue = 0;
}
}
}
struct LogoPattern
{
unsigned int value;
unsigned int bits;
} logoPatterns[16] =
{
{0x01, 1}, {0x06, 4}, {0x0A, 5}, {0x04, 4},
{0x02, 5}, {0x1E, 6}, {0x16, 6}, {0x06, 6},
{0x06, 5}, {0x1F, 6}, {0x17, 6}, {0x07, 6},
{0x02, 4}, {0x0E, 5}, {0x07, 5}, {0x00, 4},
};
int LogoCompress(unsigned char *src, unsigned char *dst)
{
unsigned int data_out = 0;
unsigned int outbit = 31;
unsigned int outbitcnt = 0;
for (int i=0; i<212; i++)
{
unsigned int data = *src++;
for (int j=0; j<8; j+=4)
{
LogoPattern &lh = logoPatterns[data >> j & 0xF];
outbitcnt += lh.bits;
for (int b=lh.bits-1; b>=0; b--)
{
data_out |= (lh.value >> b & 1) << outbit;
if (outbit == 0)
{
if (outbitcnt <= 156*8)
{
*dst++ = data_out >> 0;
*dst++ = data_out >> 8;
*dst++ = data_out >> 16;
*dst++ = data_out >> 24;
}
outbit = 31;
data_out = 0;
}
else
{
outbit--;
}
}
}
}
if (outbit != 31)
{
if (outbitcnt <= 156*8)
{
*dst++ = data_out >> 0;
*dst++ = data_out >> 8;
*dst++ = data_out >> 16;
*dst++ = data_out >> 24;
}
}
return 156*8 - outbitcnt;
};
void LogoDiff(unsigned char *srcp, unsigned char *dstp)
{
unsigned int *intp_dst = (unsigned int *)dstp;
*intp_dst++ = 0xD0 << 8 | 0x80 | 2; // header
unsigned short *shortp_dst = (unsigned short *)intp_dst;
unsigned short *shortp_src = (unsigned short *)srcp;
unsigned short prev = 0;
for (unsigned int i=0; i<0xD0; i+=2)
{
*shortp_dst++ = *shortp_src - prev;
prev = *shortp_src++;
}
}
int LogoConvert(unsigned char *srcp, unsigned char *dstp, unsigned char white)
{
// convert to tiles
unsigned char tiles[1664];
for (int ty=0; ty<2; ty++)
{
for (int y=0; y<8; y++)
{
for (int tx=0; tx<13; tx++)
{
for (int x=0; x<8; x++)
{
tiles[(ty*13 + tx)*64 + y*8 + x] = (*srcp++ == white) ? 0 : 1;
}
}
}
}
// bitpack
unsigned char bitpacked[1664/8];
LogoPackBits(tiles, bitpacked);
// diff
unsigned char diffed[4 + 1664/8];
LogoDiff(bitpacked, diffed);
// compress
int r = LogoCompress(diffed, dstp);
if (r < 0)
{
fprintf(stderr, "Compressed logo is %u bit(s) too big. Please simplify.\n", -r);
return -1;
}
return 0;
}
--- NEW FILE: banner.cpp ---
#include "ndstool.h"
#include "raster.h"
#include "banner.h"
#include "crc.h"
char *bannerLanguages[] = { "Japanese", "English", "French", "German", "Italian", "Spanish" };
#define RGB16(r,g,b) ((r) | (g<<5) | (b<<10))
/*
* RGBQuadToRGB16
*/
inline unsigned short RGBQuadToRGB16(RGBQUAD quad)
{
unsigned short r = quad.rgbRed;
unsigned short g = quad.rgbGreen;
unsigned short b = quad.rgbBlue;
return RGB16(r>>3, g>>3, b>>3);
}
/*
* CalcBannerCRC
*/
unsigned short CalcBannerCRC(Banner &banner)
{
return CalcCrc16((unsigned char *)&banner + 32, 0x840 - 32);
}
/*
* IconFromBMP
*/
void IconFromBMP()
{
CRaster bmp;
int rval = bmp.LoadBMP(bannerfilename);
if (rval < 0) exit(1);
if (bmp.width != 32 || bmp.height != 32) { fprintf(stderr, "Image should be 32 x 32.\n"); exit(1); }
Banner banner;
memset(&banner, 0, sizeof(banner));
banner.version = 1;
// tile data (4 bit / tile, 4x4 total tiles)
// 32 bytes per tile (in 4 bit mode)
for (int row=0; row<4; row++)
{
for (int col=0; col<4; col++)
{
for (int y=0; y<8; y++)
{
for (int x=0; x<8; x+=2)
{
unsigned char b0 = bmp[row*8 + y][col*8 + x + 0];
unsigned char b1 = bmp[row*8 + y][col*8 + x + 1];
banner.tile_data[row][col][y][x/2] = (b1 << 4) | b0;
}
}
}
}
// palette
for (int i = 0; i < 16; i++)
{
banner.palette[i] = RGBQuadToRGB16(bmp.palette[i]);
}
// put title
for (int i=0; bannertext[i]; i++)
{
char c = bannertext[i];
if (c == ';') c = 0x0A;
for (int l=0; l<6; l++)
{
banner.title[l][i] = c;
}
}
// calculate CRC
banner.crc = CalcBannerCRC(banner);
fwrite(&banner, 1, sizeof(banner), fNDS);
}
--- NEW FILE: ndstree.cpp ---
#include "ndstool.h"
#include "ndstree.h"
/*
* Variables
*/
unsigned int _entry_start; // current position in name entry table
unsigned int file_top; // current position to write new file to
unsigned int free_dir_id = 0xF000; // incremented in ReadDirectory
unsigned int directory_count = 0; // incremented in ReadDirectory
unsigned int file_count = 0; // incremented in ReadDirectory
unsigned int total_name_size = 0; // incremented in ReadDirectory
unsigned int file_end = 0; // end of all file data. updated in AddFile
unsigned int free_file_id = 0; // incremented in AddDirectory
/*
* ReadDirectory
* Read directory tree into memory structure
* returns last directory entry
*/
Tree *ReadDirectory(Tree *tree, char *path)
{
//printf("%s\n", path);
DIR *dir = opendir(path);
if (!dir) { fprintf(stderr, "Cannot open directory '%s'.\n", path); exit(1); }
struct dirent *de;
while ((de = readdir(dir)))
{
if (!strcmp(de->d_name, ".")) continue;
if (!strcmp(de->d_name, "..")) continue;
char strbuf[MAXPATHLEN];
strcpy(strbuf, path);
strcat(strbuf, "/");
strcat(strbuf, de->d_name);
//printf("%s\n", strbuf);
struct stat st;
if (stat(strbuf, &st)) { fprintf(stderr, "Cannot get stat of '%s'.\n", strbuf); exit(1); }
//if (S_ISDIR(st.st_mode) && !subdirs) continue; // skip subdirectories
tree->next = new Tree();
tree = tree->next;
tree->name = strdup(de->d_name);
total_name_size += strlen(de->d_name);
if (S_ISDIR(st.st_mode))
{
tree->dir_id = free_dir_id++;
tree->directory = new Tree();
directory_count++;
ReadDirectory(tree->directory, strbuf);
}
else if (S_ISREG(st.st_mode))
{
file_count++;
}
else
{
fprintf(stderr, "'%s' is not a file or directory!\n", strbuf);
exit(1);
}
}
closedir(dir);
return tree;
}
--- NEW FILE: crc.cpp ---
/*
Cyclic Redundancy Code (CRC) functions
by Rafael Vuijk (aka DarkFader)
*/
unsigned short crc16tab[] =
{
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};
unsigned long crc32tab[] =
{
0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL,
0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L,
0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L,
0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L,
0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL,
0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L,
0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL,
0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L,
0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L,
0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL,
0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L,
0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L,
0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L,
0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL,
0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L,
0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL,
0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL,
0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L,
0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L,
0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L,
0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL,
0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L,
0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL,
0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L,
0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L,
0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL,
0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L,
0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L,
0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L,
0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL,
0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L,
0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL,
0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL,
0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L,
0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L,
0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L,
0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL,
0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L,
0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL,
0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L,
0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L,
0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL,
0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L,
0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L,
0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L,
0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL,
0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L,
0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL,
0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL,
0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L,
0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L,
0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L,
0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL,
0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L,
0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL,
0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L,
0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L,
0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL,
0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L,
0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L,
0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L,
0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL,
0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L,
0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL
};
--- NEW FILE: passme.cpp ---
/*
* Includes
*/
#include "ndstool.h"
#include "crc.h"
#include "passme_sram.h"
/*
*
*/
#define DS_RAM 0x02000000
#define RAM_SIZE (4*1024*1024)
unsigned char *pc_ram;
/*
* Find
*/
template <typename T> unsigned long Find(FILE *f, char *fmt, unsigned long begin, unsigned long end, T data, T mask, int thumb, int armnr)
{
for (unsigned long i=begin; i<end; i+=sizeof(T))
{
T w = *(T *)(pc_ram + i - DS_RAM);
w &= mask;
if (w == data)
{
if (armnr == 7)
header.arm7_entry_address = i + thumb; // ARM7 entry
else
header.arm9_entry_address = i + thumb; // ARM9 entry
if (f) fprintf(f, fmt, i + thumb);
printf(fmt, i + thumb);
return i;
}
}
return 0;
}
/*
* PassMe
*/
int PassMe(char *ndsfilename, char *vhdfilename, char *sramfilename)
{
fNDS = fopen(ndsfilename, "rb");
if (!fNDS) { fprintf(stderr, "Cannot open file '%s'.\n", ndsfilename); exit(1); }
FILE *fVHD = fopen(vhdfilename, "wt");
if (vhdfilename && !fVHD) { fprintf(stderr, "Cannot open file '%s'.\n", vhdfilename); exit(1); }
fread(&header, 512, 1, fNDS);
if (fVHD)
{
fprintf(fVHD, "-- PassMe generated CPLD code\n");
fprintf(fVHD, "-- Game code: \t"); for (unsigned int i=0; i<sizeof(header.gamecode); i++) fprintf(fVHD, "%c", header.gamecode[i]); fprintf(fVHD, "\n");
fprintf(fVHD, "-- ROM version: \t"); fprintf(fVHD, "%d\n", header.romversion);
fprintf(fVHD, "-- Game title: \t"); for (unsigned int i=0; i<sizeof(header.title); i++) if (header.title[i]) fprintf(fVHD, "%c", header.title[i]);
fprintf(fVHD, "\n\n");
}
pc_ram = new unsigned char[RAM_SIZE];
// load ARM7 binary
fseek(fNDS, header.arm7_rom_offset, SEEK_SET);
fread(pc_ram + header.arm7_ram_address - DS_RAM, 1, header.arm7_size, fNDS);
// load ARM9 binary
fseek(fNDS, header.arm9_rom_offset, SEEK_SET);
fread(pc_ram + header.arm9_ram_address - DS_RAM, 1, header.arm9_size, fNDS);
header.arm7_entry_address = 0x55555555;
header.arm9_entry_address = 0x55555555;
unsigned char old_header[512];
memcpy(old_header, &header, 512);
bool bError = false;
unsigned int begin9 = 0x02000000, end9 = 0x02280000;
//unsigned int begin9 = header.arm9_ram_address, end9 = header.arm9_ram_address + header.arm9_size;
if (!Find<unsigned long>(fVHD, "-- BX LR @ 0x%08X\n", begin9, end9, 0xE120001E, 0xFFF000FF, 0, 9)) // BX LR
// if (!Find<unsigned short>(fVHD, "-- BX LR @ 0x%08X\n", begin9, end9, 0x4770, 0xFFFF, 1, 9)) // BX LR
{ printf("BX LR instruction not found!\n"); bError = true; }
unsigned int begin7 = 0x02000000, end7 = 0x023FE000;
//unsigned int begin7 = header.arm7_ram_address, end7 = header.arm7_ram_address + header.arm7_size;
// if (!Find<unsigned long>(fVHD, "-- SWI 0xAF @ 0x%08X\n", begin7, end7, 0xEFAF0000, 0xFFFF0000, 0, 7)) // SWI
// if (!Find<unsigned long>(fVHD, "-- SWI 0xFF @ 0x%08X\n", begin7, end7, 0xEFFF0000, 0xFFFF0000, 0, 7)) // SWI
// if (!Find<unsigned long>(fVHD, "-- SWI 0xA4 @ 0x%08X\n", begin7, end7, 0xEFA40000, 0xFFFF0000, 0, 7)) // SWI
// if (!Find<unsigned long>(fVHD, "-- SWI 0xEA @ 0x%08X\n", begin7, end7, 0xEFEA0000, 0xFFFF0000, 0, 7)) // SWI
if (!Find<unsigned short>(fVHD, "-- SWI 0xAF @ 0x%08X\n", begin7, end7, 0xDFAF, 0xFFFF, 1, 7)) // SWI
if (!Find<unsigned short>(fVHD, "-- SWI 0xFF @ 0x%08X\n", begin7, end7, 0xDFFF, 0xFFFF, 1, 7)) // SWI
if (!Find<unsigned short>(fVHD, "-- SWI 0xA4 @ 0x%08X\n", begin7, end7, 0xDFA4, 0xFFFF, 1, 7)) // SWI
if (!Find<unsigned short>(fVHD, "-- SWI 0xEA @ 0x%08X\n", begin7, end7, 0xDFEA, 0xFFFF, 1, 7)) // SWI
{ printf("SWI instruction not found!\n"); bError = true; }
//Find<unsigned long>("%08X\n", 0x037F8000, 0x0380F000, 0xEFEA0000, 0xFFFF0000, 0); // SWI
//...
//Find<unsigned short>("%08X\n", 0x037F8000, 0x0380F000, 0xDFA4, 0xFFFF, 1); // SWI
//...
if (bError)
{
printf("Cannot patch.\n");
return -1;
}
else
{
/*
* VHD
*/
header.reserved2 |= 0x04; // set autostart bit
bool forcepatch[512];
memset(forcepatch, 0, sizeof(forcepatch));
// ldr pc, 0x027FFE24
((unsigned char *)&header)[0x4] = 0x18;
((unsigned char *)&header)[0x5] = 0xF0;
((unsigned char *)&header)[0x6] = 0x9F;
((unsigned char *)&header)[0x7] = 0xE5;
header.header_crc = CalcCrc16((unsigned char *)&header, 0x15E);
if (fVHD) fputs(
#include "passme_vhd1.h"
, fVHD
);
for (int i=0; i<512; i++)
{
if (((unsigned char *)&header)[i] != old_header[i])
{
printf("Patch:\t0x%03X\t0x%02X\n", i, ((unsigned char *)&header)[i]);
if (fVHD) fprintf(fVHD, "\t\t\twhen 16#%03X# => patched_data <= X\"%02X\";\n", i, ((unsigned char *)&header)[i]);
}
}
if (fVHD) fputs(
#include "passme_vhd2.h"
, fVHD
);
if (fVHD) fclose(fVHD);
/*
* SRAM
*/
printf("ARM9 patched entry address: \t0x%08X\n", (unsigned int)header.arm9_entry_address);
FILE *fSRAM = fopen(sramfilename, "wb");
if (sramfilename && !fSRAM) { fprintf(stderr, "Cannot open file '%s'.\n", sramfilename); exit(1); }
if (fSRAM)
{
for (int i=0; i<passme_sram_size; i++)
{
unsigned char c = passme_sram[i];
// patch with address of ARM9 loop so it can be redirected by the code in SRAM.
if ((i &~ 1) == 0x764) c = header.arm9_entry_address >> 0 & 0xFF;
if ((i &~ 1) == 0x796) c = header.arm9_entry_address >> 8 & 0xFF;
if ((i &~ 1) == 0x7C8) c = header.arm9_entry_address >> 16 & 0xFF;
if ((i &~ 1) == 0x7FA) c = header.arm9_entry_address >> 24 & 0xFF;
fputc(c, fSRAM);
}
fclose(fSRAM);
}
}
fclose(fNDS);
return 0;
}
--- NEW FILE: passme.vhd ---
-- standard libraries
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity PassMe is
port
(
DSSLOT_CLK : in std_logic;
DSSLOT_ROMCS : in std_logic;
DSSLOT_RESET : in std_logic;
DSSLOT_EEPCS : in std_logic;
DSSLOT_IRQ : out std_logic;
DSSLOT_IO : inout std_logic_vector(7 downto 0);
DSCART_CLK : out std_logic;
DSCART_ROMCS : out std_logic;
DSCART_RESET : out std_logic;
DSCART_EEPCS : out std_logic;
DSCART_IRQ : in std_logic;
DSCART_IO : inout std_logic_vector(7 downto 0);
LED0 : out std_logic
);
end entity;
architecture rtl of passme is
-- removes Xilinx mapping errors
attribute CLOCK_BUFFER : string;
attribute CLOCK_BUFFER of DSSLOT_CLK: signal is "ibuf";
attribute CLOCK_BUFFER of DSCART_CLK: signal is "obuf";
signal is_command : boolean;
signal cmddata_cnt : natural range 0 to 511; -- 8 + 504
signal patched_data : std_logic_vector(7 downto 0);
signal patch_en : boolean;
begin
-- direct passthrough
DSCART_CLK <= DSSLOT_CLK;
DSCART_ROMCS <= DSSLOT_ROMCS;
DSCART_RESET <= DSSLOT_RESET;
DSSLOT_IRQ <= DSCART_IRQ;
DSCART_EEPCS <= DSSLOT_EEPCS;
-- activity LED
LED0 <= not DSSLOT_ROMCS;
-- patch
process (cmddata_cnt)
begin
case (cmddata_cnt - 8) is
--! ALL PATCHES ARE TO BE GENERATED HERE
when others => patched_data <= DSCART_IO;
end case;
end process;
-- dataswitcher
process (DSSLOT_RESET, DSSLOT_ROMCS, DSSLOT_EEPCS, DSSLOT_IO, DSCART_IO, patched_data)
begin
DSSLOT_IO <= (others => 'Z'); -- default is high impedance
DSCART_IO <= (others => 'Z'); -- default is high impedance
if (DSSLOT_RESET='1') then -- if not reset
if (DSSLOT_ROMCS='0') then -- ROM is selected
if (is_command) then -- is command byte
DSCART_IO <= DSSLOT_IO; -- from DS to cartridge
else -- is data byte
if (patch_en) then -- patch enabled
DSSLOT_IO <= patched_data;
else
DSSLOT_IO <= DSCART_IO;
end if;
end if;
elsif (DSSLOT_EEPCS='0') then -- EEPROM is selected
DSCART_IO(7) <= DSSLOT_IO(7); -- pass serial data
DSSLOT_IO(6) <= DSCART_IO(6); -- pass serial data in opposite direction
end if;
end if;
end process;
-- patch_en
process (DSSLOT_RESET, DSSLOT_CLK)
begin
if (DSSLOT_RESET='0') then
patch_en <= true; -- patch header
elsif (rising_edge(DSSLOT_CLK)) then
if (is_command) then
if (DSCART_IO(5) = '1') then -- detect 3C command, assume other command bytes are 00
patch_en <= false; -- do not patch other data
end if;
end if;
end if;
end process;
-- cmddata_cnt, is_command
process (DSSLOT_ROMCS, DSSLOT_CLK)
begin
if (DSSLOT_ROMCS='1') then
cmddata_cnt <= 0; -- new transfer
is_command <= true; -- start with command
elsif (rising_edge(DSSLOT_CLK)) then
if (cmddata_cnt mod 8 = 7) then
is_command <= false; -- next byte is data
end if;
cmddata_cnt <= cmddata_cnt + 1; -- next byte
end if;
end process;
end architecture;
--- NEW FILE: raster.cpp ---
#include "ndstool.h"
#include "raster.h"
int CRaster::LoadBMP(char *filename)
{
// open file
FILE *f = fopen(filename, "rb");
if (!f) { fprintf(stderr, "Could not open '%s'.\n", filename); return -1; }
// load bitmap fileheader & infoheader
BITMAPFILEHEADER bmfh;
fread((char*)&bmfh, 1, sizeof(bmfh), f);
BITMAPINFO bmi;
fread((char*)&bmi, 1, sizeof(bmi), f);
BITMAPINFOHEADER &bmih = bmi.bmiHeader;
memcpy(palette, bmi.bmiColors, sizeof(palette));
// check filetype signature
if ((bmfh.bfType[0] != 'B') || (bmfh.bfType[1] != 'M')) { fprintf(stderr, "Not a bitmap file.\n"); return -2; }
if (bmih.biBitCount > 8) { fprintf(stderr, "Bitmap must have a palette.\n"); return -3; }
// assign some short variables:
width = bmih.biWidth;
height = (bmih.biHeight > 0) ? (int)bmih.biHeight : -(int)bmih.biHeight; // absoulte value
int pitch = width * bmih.biBitCount / 8;
pitch += (4 - pitch%4) % 4;
// load raster
unsigned int pixelsPerByte = 8 / bmih.biBitCount;
unsigned int biBitCount_mask = (1<<bmih.biBitCount)-1;
raster = new unsigned char [width * height];
for (unsigned int y=0; y<height; y++)
{
if (bmih.biHeight > 0) // if height is positive the bmp is bottom-up, read it reversed
fseek(f, bmfh.bfOffBits + pitch*(height-1-y), SEEK_SET);
else
fseek(f, bmfh.bfOffBits + pitch*y, SEEK_SET);
for (unsigned int x=0; x<width; x+=pixelsPerByte)
{
unsigned char data = fgetc(f);
unsigned int shift = 8;
for (unsigned int p=0; p<pixelsPerByte; p++)
{
shift -= bmih.biBitCount;
(*this)[y][x+p] = data >> shift & biBitCount_mask;
}
}
}
fclose(f);
return 0;
}
--- NEW FILE: ndstool.cpp ---
/*
Nintendo DS rom tool
by Rafael Vuijk (aka DarkFader)
*/
#include <ndstool.h>
#include <ndstool_version.h>
#include <unistd.h>
#include "sha1.h"
#include "ndscreate.h"
#include "ndsextract.h"
#include "passme.h"
#include "hook.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;
char *headerfilename = 0;
char *uniquefilename = 0;
char *logofilename = 0;
char *makercode = 0;
char *gamecode = 0;
char *vhdfilename = 0;
char *sramfilename = 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 "VER" - %s %s by Rafael Vuijk (aka DarkFader)\n",CompileDate,CompileTime);
if (EncryptSecureArea) printf("WARNING: This is a private version!\n");
}
/*
* Help
*/
void Help(char *unknownoption = 0)
{
Title();
if (unknownoption)
{
printf("Unknown option: %s\n\n", unknownoption);
}
printf("\n");
printf("Parameter Syntax Comments\n");
printf("--------- ------ --------\n");
printf("Show this help: -?\n");
printf("Show information: -i [file.nds]\n");
printf("Information options: optional\n");
printf(" Show more info -v [roms_rc.dat] checksums, warnings, release info\n");
printf("PassMe: -p [file.nds] [passme.vhd] [passme.sav]\n");
printf("Hook ARM7 executable -k [file.nds] see manual\n");
printf("Fix header CRC -f [file.nds]\n");
//printf("Test -T [file.nds]\n");
if (EncryptSecureArea) printf("En/decrypt secure area -s [file.nds]\n");
//printf("Sign multiboot -n [file.nds]");
//printf("Hash file & compare: -@ [arm7.bin]\n"); // used in buildscript
printf("List files: -l [file.nds]\n");
printf("Create -c [file.nds]\n");
printf("Extract -x [file.nds]\n");
printf("Create/Extract options: optional\n");
printf(" Show more info -v[v...] filenames etc.\n");
//printf("\n");
printf(" ARM9 executable -9 file.bin\n");
printf(" ARM7 executable -7 file.bin\n");
printf(" ARM9 overlay table -y9 file.bin\n");
printf(" ARM7 overlay table -y7 file.bin\n");
printf(" Data files -d directory\n");
printf(" Overlay files -y directory\n");
printf(" Banner bitmap/text -b file.bmp \"text;text;text\" 3 lines max.\n");
printf(" Banner binary -t file.bin\n");
//printf("\n");
printf(" Header template -h file.bin\n");
printf(" Logo bitmap/binary -o file.bmp/file.bin\n");
printf(" Maker code -m code\n");
printf(" Game code -g code\n");
//printf(" unique ID filename -u file.bin for homebrew, auto generated\n");
printf(" ARM9 RAM address -r9 address\n");
printf(" ARM7 RAM address -r7 address\n");
printf(" ARM9 RAM entry -e9 address\n");
printf(" ARM7 RAM entry -e7 address\n");
printf(" Wildcard filemask(s) -w [filemask]... * and ? are special\n");
printf("Common options:\n");
printf(" NDS filename [file.nds]\n");
printf("\n");
printf("You can perform multiple actions. They are performed in specified order.\n");
printf("You only need to specify the NDS filename once. This and other options can appear anywhere.\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 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
for (int a=1; a<argc; a++)
{
if (argv[a][0] == '-')
{
switch (argv[a][1])
{
case 'i': // show information
{
ADDACTION(ACTION_SHOWINFO);
OPTIONAL(ndsfilename);
break;
}
case 'f': // fix header CRC
{
ADDACTION(ACTION_FIXHEADERCRC);
OPTIONAL(ndsfilename);
break;
}
case 's': // en-/decrypt secure area
{
if (EncryptSecureArea)
{
ADDACTION(ACTION_ENCRYPTSECUREAREA);
OPTIONAL(ndsfilename);
break;
}
}
case 'p': // PassMe
{
ADDACTION(ACTION_PASSME);
OPTIONAL(ndsfilename);
OPTIONAL(vhdfilename);
OPTIONAL(sramfilename);
break;
}
case 'l': // list files
{
ADDACTION(ACTION_LISTFILES);
OPTIONAL(ndsfilename);
break;
}
case 'x': // extract
{
ADDACTION(ACTION_EXTRACT);
OPTIONAL(ndsfilename);
break;
}
case 'w': // wildcard filemasks
{
while (1)
{
char *filemask = 0;
OPTIONAL(filemask);
if (!(filemasks[filemask_num] = filemask)) break;
if (++filemask_num >= 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
REQUIRED(headerfilename);
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 '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);
if (strlen(makercode) != 2)
{
fprintf(stderr, "Maker code must be 2 characters!\n");
return 1;
}
break;
case 'g': // game code
REQUIRED(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[a] >= 'a') && (gamecode[a] <= 'z'))
{
fprintf(stderr, "Warning: Gamecode contains lowercase characters.\n");
break;
}
if (gamecode[a] == 'A')
{
fprintf(stderr, "Warning: Gamecode starts with 'A', which might be used for another commercial product.\n");
break;
}
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 '?': // help
{
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();
// perform actions
int status = 0;
for (int i=0; i<num_actions; i++)
{
switch (actions[i])
{
case ACTION_SHOWINFO:
ShowInfo(ndsfilename);
break;
case ACTION_FIXHEADERCRC:
FixHeaderCRC(ndsfilename);
break;
case ACTION_EXTRACT:
if (arm9filename) Extract(arm9filename, true, 0x20, true, 0x2C, true);
if (arm7filename) Extract(arm7filename, true, 0x30, true, 0x3C);
if (bannerfilename) Extract(bannerfilename, true, 0x68, false, 0x840);
if (headerfilename) Extract(headerfilename, false, 0x0, false, 0x200);
if (arm9ovltablefilename) Extract(arm9ovltablefilename, true, 0x50, true, 0x54);
if (arm7ovltablefilename) Extract(arm7ovltablefilename, true, 0x58, true, 0x5C);
if (overlaydir) ExtractOverlayFiles();
if (filerootdir) ExtractFiles(ndsfilename);
break;
case ACTION_CREATE:
/*status =*/ Create();
break;
case ACTION_PASSME:
status = PassMe(ndsfilename, vhdfilename, sramfilename);
break;
case ACTION_LISTFILES:
/*status =*/ ExtractFiles(ndsfilename);
break;
case ACTION_HASHFILE:
{
char *filename = arm7filename;
if (!filename) filename = ndsfilename;
if (!filename) return 1;
unsigned char sha1[SHA1_DIGEST_SIZE];
int r = HashAndCompareWithList(filename, sha1);
status = -1;
if (r > 0)
{
for (int i=0; i<SHA1_DIGEST_SIZE; i++) printf("%02X", sha1[i]);
printf("\n");
status = 0;
}
break;
}
case ACTION_HOOK:
{
Hook(ndsfilename, arm7filename);
break;
}
case ACTION_ENCRYPTSECUREAREA:
/*status =*/ EnDecryptSecureArea(ndsfilename);
break;
}
}
return (status < 0) ? 1 : 0;
}
--- NEW FILE: sha1.cpp ---
#include <string.h>
#include "sha1.h"
#define SHA_LITTLE_ENDIAN 1234 /* byte 0 is least significant (i386) */
#define SHA_BIG_ENDIAN 4321 /* byte 0 is most significant (mc68k) */
#include <sys/param.h>
#ifndef BYTE_ORDER
#error "BYTE_ORDER not defined"
#endif
#if BYTE_ORDER != BIG_ENDIAN
#define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN
#else
#define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN
#endif
#if !defined(PLATFORM_BYTE_ORDER)
# error Please set undetermined byte order (lines 87 or 89 of sha1.c).
#endif
#define rotl32(x,n) (((x) << n) | ((x) >> (32 - n)))
#if (PLATFORM_BYTE_ORDER == SHA_BIG_ENDIAN)
#define swap_b32(x) (x)
#elif defined(bswap_32)
#define swap_b32(x) bswap_32(x)
#else
#define swap_b32(x) ((rotl32((x), 8) & 0x00ff00ff) | (rotl32((x), 24) & 0xff00ff00))
#endif
#define SHA1_MASK (SHA1_BLOCK_SIZE - 1)
/* reverse byte order in 32-bit words */
#define ch(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
#define parity(x,y,z) ((x) ^ (y) ^ (z))
#define maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
/* A normal version as set out in the FIPS. This version uses */
/* partial loop unrolling and is optimised for the Pentium 4 */
#define rnd(f,k) \
t = a; a = rotl32(a,5) + f(b,c,d) + e + k + w[i]; \
e = d; d = c; c = rotl32(b, 30); b = t
void sha1_compile(sha1_ctx ctx[1])
{ sha1_32t w[80], i, a, b, c, d, e, t;
/* note that words are compiled from the buffer into 32-bit */
/* words in big-endian order so an order reversal is needed */
/* here on little endian machines */
for(i = 0; i < SHA1_BLOCK_SIZE / 4; ++i)
w[i] = swap_b32(ctx->wbuf[i]);
for(i = SHA1_BLOCK_SIZE / 4; i < 80; ++i)
w[i] = rotl32(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
a = ctx->hash[0];
b = ctx->hash[1];
c = ctx->hash[2];
d = ctx->hash[3];
e = ctx->hash[4];
for(i = 0; i < 20; ++i)
{
rnd(ch, 0x5a827999);
}
for(i = 20; i < 40; ++i)
{
rnd(parity, 0x6ed9eba1);
}
for(i = 40; i < 60; ++i)
{
rnd(maj, 0x8f1bbcdc);
}
for(i = 60; i < 80; ++i)
{
rnd(parity, 0xca62c1d6);
}
ctx->hash[0] += a;
ctx->hash[1] += b;
ctx->hash[2] += c;
ctx->hash[3] += d;
ctx->hash[4] += e;
}
void sha1_begin(sha1_ctx ctx[1])
{
ctx->count[0] = ctx->count[1] = 0;
ctx->hash[0] = 0x67452301;
ctx->hash[1] = 0xefcdab89;
ctx->hash[2] = 0x98badcfe;
ctx->hash[3] = 0x10325476;
ctx->hash[4] = 0xc3d2e1f0;
}
/* SHA1 hash data in an array of bytes into hash buffer and call the */
/* hash_compile function as required. */
void sha1_hash(const unsigned char data[], unsigned int len, sha1_ctx ctx[1])
{ sha1_32t pos = (sha1_32t)(ctx->count[0] & SHA1_MASK),
space = SHA1_BLOCK_SIZE - pos;
const unsigned char *sp = data;
if((ctx->count[0] += len) < len)
++(ctx->count[1]);
while(len >= space) /* tranfer whole blocks while possible */
{
memcpy(((unsigned char*)ctx->wbuf) + pos, sp, space);
sp += space; len -= space; space = SHA1_BLOCK_SIZE; pos = 0;
sha1_compile(ctx);
}
memcpy(((unsigned char*)ctx->wbuf) + pos, sp, len);
}
/* SHA1 final padding and digest calculation */
#if (PLATFORM_BYTE_ORDER == SHA_LITTLE_ENDIAN)
sha1_32t mask[4] =
{ 0x00000000, 0x000000ff, 0x0000ffff, 0x00ffffff };
sha1_32t bits[4] =
{ 0x00000080, 0x00008000, 0x00800000, 0x80000000 };
#else
sha1_32t mask[4] =
{ 0x00000000, 0xff000000, 0xffff0000, 0xffffff00 };
sha1_32t bits[4] =
{ 0x80000000, 0x00800000, 0x00008000, 0x00000080 };
#endif
void sha1_end(unsigned char hval[], sha1_ctx ctx[1])
{ sha1_32t i = (sha1_32t)(ctx->count[0] & SHA1_MASK);
/* mask out the rest of any partial 32-bit word and then set */
/* the next byte to 0x80. On big-endian machines any bytes in */
/* the buffer will be at the top end of 32 bit words, on little */
/* endian machines they will be at the bottom. Hence the AND */
/* and OR masks above are reversed for little endian systems */
/* Note that we can always add the first padding byte at this */
/* because the buffer always contains at least one empty slot */
ctx->wbuf[i >> 2] = (ctx->wbuf[i >> 2] & mask[i & 3]) | bits[i & 3];
/* we need 9 or more empty positions, one for the padding byte */
/* (above) and eight for the length count. If there is not */
/* enough space pad and empty the buffer */
if(i > SHA1_BLOCK_SIZE - 9)
{
if(i < 60) ctx->wbuf[15] = 0;
sha1_compile(ctx);
i = 0;
}
else /* compute a word index for the empty buffer positions */
i = (i >> 2) + 1;
while(i < 14) /* and zero pad all but last two positions */
ctx->wbuf[i++] = 0;
/* assemble the eight byte counter in in big-endian format */
ctx->wbuf[14] = swap_b32((ctx->count[1] << 3) | (ctx->count[0] >> 29));
ctx->wbuf[15] = swap_b32(ctx->count[0] << 3);
sha1_compile(ctx);
/* extract the hash value as bytes in case the hash buffer is */
/* misaligned for 32-bit words */
for(i = 0; i < SHA1_DIGEST_SIZE; ++i)
hval[i] = (unsigned char)(ctx->hash[i >> 2] >> 8 * (~i & 3));
}
void sha1(unsigned char hval[], const unsigned char data[], unsigned int len)
{ sha1_ctx cx[1];
sha1_begin(cx); sha1_hash(data, len, cx); sha1_end(hval, cx);
}
--- NEW FILE: ndsextract.cpp ---
#include "ndstool.h"
#include "overlay.h"
#include <errno.h>
/*
* MkDir
*/
void MkDir(char *name)
{
#ifdef __MINGW32__
if (mkdir(name))
#else
if (mkdir(name, S_IRWXU))
#endif
{
if (errno != EEXIST)
{
fprintf(stderr, "Cannot create directory '%s'.\n", name);
exit(1);
}
}
}
/*
* ExtractFile
* if rootdir==0 nothing will be written
*/
void ExtractFile(char *rootdir, char *prefix, char *entry_name, unsigned int file_id)
{
unsigned int save_filepos = ftell(fNDS);
// read FAT data
fseek(fNDS, header.fat_offset + 8*file_id, SEEK_SET);
unsigned_int top;
fread(&top, 1, sizeof(top), fNDS);
unsigned_int bottom;
fread(&bottom, 1, sizeof(bottom), fNDS);
unsigned int size = bottom - top;
if (size > (1U << (17 + header.devicecap))) { fprintf(stderr, "File %u: Size is too big. FAT offset 0x%X contains invalid data.\n", file_id, header.fat_offset + 8*file_id); exit(1); }
// print file info
if (!rootdir || verbose)
{
printf("%5u 0x%08X 0x%08X %9u %s%s\n", file_id, (int)top, (int)bottom, size, prefix, entry_name);
}
// extract file
if (rootdir)
{
// make filename
char filename[MAXPATHLEN];
strcpy(filename, rootdir);
strcat(filename, prefix);
strcat(filename, entry_name);
fseek(fNDS, top, SEEK_SET);
FILE *fo = fopen(filename, "wb");
if (!fo) { fprintf(stderr, "Cannot create file '%s'.\n", filename); exit(1); }
while (size > 0)
{
unsigned char copybuf[1024];
unsigned int size2 = (size >= sizeof(copybuf)) ? sizeof(copybuf) : size;
fread(copybuf, 1, size2, fNDS);
fwrite(copybuf, 1, size2, fo);
size -= size2;
}
fclose(fo);
}
fseek(fNDS, save_filepos, SEEK_SET);
}
/*
* MatchName
*/
bool MatchName(char *name, char *mask, int level=0)
{
char *a = name;
char *b = mask;
//for (int i=0; i<level; i++) printf(" "); printf("matching a='%s' b='%s'\n", a, b);
while (1)
{
//for (int i=0; i<level; i++) printf("a=%s b=%s\n", a, b);
if (*b == '*')
{
while (*b == '*') b++;
//for (int i=0; i<level; i++) printf(" "); printf("* '%s' '%s'\n", a, b);
bool match = false;
char *a2 = a;
do
{
if (MatchName(a2, b, level+1)) { a = a2; match = true; break; }
} while (*a2++);
if (!match) return false;
//for (int i=0; i<level; i++) printf(" "); printf("matched a='%s' b='%s'\n", a, b);
}
//for (int i=0; i<level; i++) printf(" "); printf("a=%s b=%s\n", a, b);
if (!*a && !*b) return true;
if (*a && !*b) return false;
if (!*a && *b) return false;
if (*b != '?' && *a != *b) return false;
a++; b++;
}
}
/*
* ExtractDirectory
* filerootdir can be 0 for just listing files
*/
void ExtractDirectory(char *prefix, unsigned int dir_id)
{
char strbuf[MAXPATHLEN];
unsigned int save_filepos = ftell(fNDS);
fseek(fNDS, header.fnt_offset + 8*(dir_id & 0xFFF), SEEK_SET);
unsigned_int entry_start; // reference location of entry name
fread(&entry_start, 1, sizeof(entry_start), fNDS);
unsigned_short top_file_id; // file ID of top entry
fread(&top_file_id, 1, sizeof(top_file_id), fNDS);
unsigned_short parent_id; // ID of parent directory or directory count (root)
fread(&parent_id, 1, sizeof(parent_id), fNDS);
fseek(fNDS, header.fnt_offset + entry_start, SEEK_SET);
// print directory name
//printf("%04X ", dir_id);
if (!filerootdir || verbose)
{
printf("%s\n", prefix);
}
for (unsigned int file_id=top_file_id; ; file_id++)
{
unsigned char entry_type_name_length;
fread(&entry_type_name_length, 1, sizeof(entry_type_name_length), fNDS);
unsigned int name_length = entry_type_name_length & 127;
bool entry_type_directory = (entry_type_name_length & 128) ? true : false;
if (name_length == 0) break;
char entry_name[128];
memset(entry_name, 0, 128);
fread(entry_name, 1, entry_type_name_length & 127, fNDS);
if (entry_type_directory)
{
unsigned_short dir_id;
fread(&dir_id, 1, sizeof(dir_id), fNDS);
if (filerootdir)
{
strcpy(strbuf, filerootdir);
strcat(strbuf, prefix);
strcat(strbuf, entry_name);
MkDir(strbuf);
}
strcpy(strbuf, prefix);
strcat(strbuf, entry_name);
strcat(strbuf, "/");
ExtractDirectory(strbuf, dir_id);
}
else
{
if (1)
{
bool match = (filemask_num == 0);
for (int i=0; i<filemask_num; i++)
{
//printf("%s %s\n", entry_name, filemasks[i]);
if (MatchName(entry_name, filemasks[i])) { match = true; break; }
}
//if (!directorycreated)
//{
//}
//printf("%d\n", match);
if (match) ExtractFile(filerootdir, prefix, entry_name, file_id);
}
}
}
fseek(fNDS, save_filepos, SEEK_SET);
}
/*
* ExtractFiles
*/
void ExtractFiles(char *ndsfilename)
{
//printf("%d\n", MatchName("hello", "h*el*lo")); exit(0); // TEST
fNDS = fopen(ndsfilename, "rb");
if (!fNDS) { fprintf(stderr, "Cannot open file '%s'.\n", ndsfilename); exit(1); }
fread(&header, 512, 1, fNDS);
if (filerootdir)
{
MkDir(filerootdir);
}
ExtractDirectory("/", 0xF000); // list or extract
fclose(fNDS);
}
/*
* ExtractOverlayFiles2
*/
void ExtractOverlayFiles2(unsigned int overlay_offset, unsigned int overlay_size)
{
OverlayEntry overlayEntry;
if (overlay_size)
{
fseek(fNDS, overlay_offset, SEEK_SET);
for (unsigned int i=0; i<overlay_size; i+=sizeof(OverlayEntry))
{
fread(&overlayEntry, 1, sizeof(overlayEntry), fNDS);
char s[32]; sprintf(s, OVERLAY_FMT, overlayEntry.file_id);
ExtractFile(overlaydir, "/", s, overlayEntry.file_id);
}
}
}
/*
* ExtractOverlayFiles
*/
void ExtractOverlayFiles()
{
fNDS = fopen(ndsfilename, "rb");
if (!fNDS) { fprintf(stderr, "Cannot open file '%s'.\n", ndsfilename); exit(1); }
fread(&header, 512, 1, fNDS);
if (overlaydir)
{
MkDir(overlaydir);
}
ExtractOverlayFiles2(header.arm9_overlay_offset, header.arm9_overlay_size);
ExtractOverlayFiles2(header.arm7_overlay_offset, header.arm7_overlay_size);
fclose(fNDS);
}
/*
* Extract
*/
void Extract(char *outfilename, bool indirect_offset, unsigned int offset, bool indirect_size, unsigned size, bool with_footer)
{
fNDS = fopen(ndsfilename, "rb");
if (!fNDS) { fprintf(stderr, "Cannot open file '%s'.\n", ndsfilename); exit(1); }
fread(&header, 512, 1, fNDS);
if (indirect_offset) offset = *((unsigned int *)&header + offset/4);
if (indirect_size) size = *((unsigned int *)&header + size/4);
fseek(fNDS, offset, SEEK_SET);
FILE *fo = fopen(outfilename, "wb");
if (!fo) { fprintf(stderr, "Cannot create file '%s'.\n", outfilename); exit(1); }
unsigned char copybuf[1024];
while (size > 0)
{
unsigned int size2 = (size >= sizeof(copybuf)) ? sizeof(copybuf) : size;
fread(copybuf, 1, size2, fNDS);
fwrite(copybuf, 1, size2, fo);
size -= size2;
}
if (with_footer)
{
unsigned_int nitrocode;
fread(&nitrocode, sizeof(nitrocode), 1, fNDS);
if (nitrocode == 0xDEC00621)
{
for (int i=0; i<3; i++) // write additional 3 words
{
fwrite(&nitrocode, sizeof(nitrocode), 1, fo);
fread(&nitrocode, sizeof(nitrocode), 1, fNDS); // next field
}
}
}
fclose(fo);
fclose(fNDS);
}
--- NEW FILE: ndscodes.cpp ---
#include "ndstool.h"
Country countries[] =
{
{ 'J', "JPN" },
{ 'E', "USA" } ,
{ 'P', "EUR" },
{ 'D', "NOE" },
{ 'F', "NOE" },
{ 'I', "ITA" },
{ 'S', "SPA" },
{ 'H', "HOL" },
{ 'K', "KOR" },
{ 'X', "EUU" },
};
int NumCountries = sizeof(countries) / sizeof(countries[0]);
Maker makers[] =
{
{ "01", "Nintendo" },
{ "02", "Rocket Games, Ajinomoto" },
{ "03", "Imagineer-Zoom" },
{ "04", "Gray Matter?" },
{ "05", "Zamuse" },
{ "06", "Falcom" },
{ "07", "Enix?" },
{ "08", "Capcom" },
{ "09", "Hot B Co." },
{ "0A", "Jaleco" },
{ "0B", "Coconuts Japan" },
{ "0C", "Coconuts Japan/G.X.Media" },
{ "0D", "Micronet?" },
{ "0E", "Technos" },
{ "0F", "Mebio Software" },
{ "0G", "Shouei System" },
{ "0H", "Starfish" },
{ "0J", "Mitsui Fudosan/Dentsu" },
{ "0L", "Warashi Inc." },
{ "0N", "Nowpro" },
{ "0P", "Game Village" },
{ "10", "?????????????" },
{ "12", "Infocom" },
{ "13", "Electronic Arts Japan" },
{ "15", "Cobra Team" },
{ "16", "Human/Field" },
{ "17", "KOEI" },
{ "18", "Hudson Soft" },
{ "19", "S.C.P." },
{ "1A", "Yanoman" },
{ "1C", "Tecmo Products" },
{ "1D", "Japan Glary Business" },
{ "1E", "Forum/OpenSystem" },
{ "1F", "Virgin Games" },
{ "1G", "SMDE" },
{ "1J", "Daikokudenki" },
{ "1P", "Creatures Inc." },
{ "1Q", "TDK Deep Impresion" },
{ "20", "Destination Software, KSS" },
{ "21", "Sunsoft/Tokai Engineering??" },
{ "22", "POW, VR 1 Japan??" },
{ "23", "Micro World" },
{ "25", "San-X" },
{ "26", "Enix" },
{ "27", "Loriciel/Electro Brain" },
{ "28", "Kemco Japan" },
{ "29", "Seta" },
{ "2A", "Culture Brain" },
{ "2C", "Palsoft" },
{ "2D", "Visit Co.,Ltd." },
{ "2E", "Intec" },
{ "2F", "System Sacom" },
{ "2G", "Poppo" },
{ "2H", "Ubisoft Japan" },
{ "2J", "Media Works" },
{ "2K", "NEC InterChannel" },
{ "2L", "Tam" },
{ "2M", "Jordan" },
{ "2N", "Smilesoft ???, Rocket ???" },
{ "2Q", "Mediakite" },
{ "30", "Viacom" },
{ "31", "Carrozzeria" },
{ "32", "Dynamic" },
// { "33", "NOT A COMPANY!" },
{ "34", "Magifact" },
{ "35", "Hect" },
{ "36", "Codemasters" },
{ "37", "Taito/GAGA Communications" },
{ "38", "Laguna" },
{ "39", "Telstar Fun & Games, Event/Taito" },
{ "3B", "Arcade Zone Ltd" },
{ "3C", "Entertainment International/Empire Software?" },
{ "3D", "Loriciel" },
{ "3E", "Gremlin Graphics" },
{ "3F", "K.Amusement Leasing Co." },
{ "40", "Seika Corp." },
{ "41", "Ubi Soft Entertainment" },
{ "42", "Sunsoft US?" },
{ "44", "Life Fitness" },
{ "46", "System 3" },
{ "47", "Spectrum Holobyte" },
{ "49", "IREM" },
{ "4B", "Raya Systems" },
{ "4C", "Renovation Products" },
{ "4D", "Malibu Games" },
{ "4F", "Eidos (was U.S. Gold <=1995)" },
{ "4G", "Playmates Interactive?" },
{ "4J", "Fox Interactive" },
{ "4K", "Time Warner Interactive" },
{ "4Q", "Disney Interactive" },
{ "4S", "Black Pearl" },
{ "4U", "Advanced Productions" },
{ "4X", "GT Interactive" },
{ "4Y", "RARE?" },
{ "4Z", "Crave Entertainment" },
{ "50", "Absolute Entertainment" },
{ "51", "Acclaim" },
{ "52", "Activision" },
{ "53", "American Sammy" },
{ "54", "Take 2 Interactive (before it was GameTek)" },
{ "55", "Hi Tech" },
{ "56", "LJN LTD." },
{ "58", "Mattel" },
{ "5A", "Mindscape, Red Orb Entertainment?" },
{ "5B", "Romstar" },
{ "5C", "Taxan" },
{ "5D", "Midway (before it was Tradewest)" },
{ "5F", "American Softworks" },
{ "5G", "Majesco Sales Inc" },
{ "5H", "3DO" },
{ "5K", "Hasbro" },
{ "5L", "NewKidCo" },
{ "5M", "Telegames" },
{ "5N", "Metro3D" },
{ "5P", "Vatical Entertainment" },
{ "5Q", "LEGO Media" },
{ "5S", "Xicat Interactive" },
{ "5T", "Cryo Interactive" },
{ "5W", "Red Storm Entertainment" },
{ "5X", "Microids" },
{ "5Z", "Conspiracy/Swing" },
{ "60", "Titus" },
{ "61", "Virgin Interactive" },
{ "62", "Maxis" },
{ "64", "LucasArts Entertainment" },
{ "67", "Ocean" },
{ "69", "Electronic Arts" },
{ "6B", "Laser Beam" },
{ "6E", "Elite Systems" },
{ "6F", "Electro Brain" },
{ "6G", "The Learning Company" },
{ "6H", "BBC" },
{ "6J", "Software 2000" },
{ "6L", "BAM! Entertainment" },
{ "6M", "Studio 3" },
{ "6Q", "Classified Games" },
{ "6S", "TDK Mediactive" },
{ "6U", "DreamCatcher" },
{ "6V", "JoWood Produtions" },
{ "6W", "SEGA" },
{ "6X", "Wannado Edition" },
{ "6Y", "LSP" },
{ "6Z", "ITE Media" },
{ "70", "Infogrames" },
{ "71", "Interplay" },
{ "72", "JVC" },
{ "73", "Parker Brothers" },
{ "75", "Sales Curve" },
{ "78", "THQ" },
{ "79", "Accolade" },
{ "7A", "Triffix Entertainment" },
{ "7C", "Microprose Software" },
{ "7D", "Universal Interactive, Sierra, Simon & Schuster?" },
{ "7F", "Kemco" },
{ "7G", "Rage Software" },
{ "7H", "Encore" },
{ "7J", "Zoo" },
{ "7K", "BVM" },
{ "7L", "Simon & Schuster Interactive" },
{ "7M", "Asmik Ace Entertainment Inc./AIA" },
{ "7N", "Empire Interactive?" },
{ "7Q", "Jester Interactive" },
{ "7T", "Scholastic" },
{ "7U", "Ignition Entertainment" },
{ "7W", "Stadlbauer" },
{ "80", "Misawa" },
{ "81", "Teichiku" },
{ "82", "Namco Ltd." },
{ "83", "LOZC" },
{ "84", "KOEI" },
{ "86", "Tokuma Shoten Intermedia" },
{ "87", "Tsukuda Original" },
{ "88", "DATAM-Polystar" },
{ "8B", "Bulletproof Software" },
{ "8C", "Vic Tokai Inc." },
{ "8E", "Character Soft" },
{ "8F", "I'Max" },
{ "8G", "Saurus" },
{ "8J", "General Entertainment" },
{ "8N", "Success" },
{ "8P", "SEGA Japan" },
{ "90", "Takara Amusement" },
{ "91", "Chun Soft" },
{ "92", "Video System, McO'River???" },
{ "93", "BEC" },
{ "95", "Varie" },
{ "96", "Yonezawa/S'pal" },
{ "97", "Kaneko" },
{ "99", "Victor Interactive Software, Pack in Video" },
{ "9A", "Nichibutsu/Nihon Bussan" },
{ "9B", "Tecmo" },
{ "9C", "Imagineer" },
{ "9F", "Nova" },
{ "9G", "Den'Z" },
{ "9H", "Bottom Up" },
{ "9J", "TGL" },
{ "9L", "Hasbro Japan?" },
{ "9N", "Marvelous Entertainment" },
{ "9P", "Keynet Inc." },
{ "9Q", "Hands-On Entertainment" },
{ "A0", "Telenet" },
{ "A1", "Hori" },
{ "A4", "Konami" },
{ "A5", "K.Amusement Leasing Co." },
{ "A6", "Kawada" },
{ "A7", "Takara" },
{ "A9", "Technos Japan Corp." },
{ "AA", "JVC, Victor Musical Indutries" },
{ "AC", "Toei Animation" },
{ "AD", "Toho" },
{ "AF", "Namco" },
{ "AG", "Media Rings Corporation" },
{ "AH", "J-Wing" },
{ "AJ", "Pioneer LDC" },
{ "AK", "KID" },
{ "AL", "Mediafactory" },
{ "AP", "Infogrames Hudson" },
{ "AQ", "Kiratto. Ludic Inc" },
{ "B0", "Acclaim Japan" },
{ "B1", "ASCII (was Nexoft?)" },
{ "B2", "Bandai" },
{ "B4", "Enix" },
{ "B6", "HAL Laboratory" },
{ "B7", "SNK" },
{ "B9", "Pony Canyon" },
{ "BA", "Culture Brain" },
{ "BB", "Sunsoft" },
{ "BC", "Toshiba EMI" },
{ "BD", "Sony Imagesoft" },
{ "BF", "Sammy" },
{ "BG", "Magical" },
{ "BH", "Visco" },
{ "BJ", "Compile " },
{ "BL", "MTO Inc." },
{ "BN", "Sunrise Interactive" },
{ "BP", "Global A Entertainment" },
{ "BQ", "Fuuki" },
{ "C0", "Taito" },
{ "C2", "Kemco" },
{ "C3", "Square" },
{ "C4", "Tokuma Shoten" },
{ "C5", "Data East" },
{ "C6", "Tonkin House (was Tokyo Shoseki)" },
{ "C8", "Koei" },
{ "CA", "Konami/Ultra/Palcom" },
{ "CB", "NTVIC/VAP" },
{ "CC", "Use Co.,Ltd." },
{ "CD", "Meldac" },
{ "CE", "Pony Canyon" },
{ "CF", "Angel, Sotsu Agency/Sunrise" },
{ "CJ", "Boss" },
{ "CG", "Yumedia/Aroma Co., Ltd" },
{ "CK", "Axela/Crea-Tech?" },
{ "CL", "Sekaibunka-Sha, Sumire kobo?, Marigul Management Inc.?" },
{ "CM", "Konami Computer Entertainment Osaka" },
{ "CP", "Enterbrain" },
{ "D0", "Taito/Disco" },
{ "D1", "Sofel" },
{ "D2", "Quest, Bothtec" },
{ "D3", "Sigma, ?????" },
{ "D4", "Ask Kodansha" },
{ "D6", "Naxat" },
{ "D7", "Copya System" },
{ "D8", "Capcom Co., Ltd." },
{ "D9", "Banpresto" },
{ "DA", "TOMY" },
{ "DB", "LJN Japan" },
{ "DD", "NCS" },
{ "DE", "Human Entertainment" },
{ "DF", "Altron" },
{ "DG", "Jaleco???" },
{ "DH", "Gaps Inc." },
{ "DL", "????" },
{ "DN", "Elf" },
{ "E0", "Jaleco" },
{ "E1", "????" },
{ "E2", "Yutaka" },
{ "E3", "Varie" },
{ "E4", "T&ESoft" },
{ "E5", "Epoch" },
{ "E7", "Athena" },
{ "E8", "Asmik" },
{ "E9", "Natsume" },
{ "EA", "King Records" },
{ "EB", "Atlus" },
{ "EC", "Epic/Sony Records" },
{ "EE", "IGS" },
{ "EG", "Chatnoir" },
{ "EH", "Right Stuff" },
{ "EJ", "????" },
{ "EL", "Spike" },
{ "EM", "Konami Computer Entertainment Tokyo" },
{ "EN", "Alphadream Corporation" },
{ "F0", "A Wave" },
{ "F1", "Motown Software" },
{ "F2", "Left Field Entertainment" },
{ "F3", "Extreme Ent. Grp." },
{ "F4", "TecMagik" },
{ "F9", "Cybersoft" },
{ "FB", "Psygnosis" },
{ "FE", "Davidson/Western Tech." },
{ "G1", "PCCW Japan" },
{ "G4", "KiKi Co Ltd" },
{ "G5", "Open Sesame Inc???" },
{ "G6", "Sims" },
{ "G7", "Broccoli" },
{ "G8", "Avex" },
{ "G9", "D3 Publisher" },
{ "GB", "Konami Computer Entertainment Japan" },
{ "GD", "Square-Enix" },
{ "IH", "Yojigen" }
};
int NumMakers = sizeof(makers) / sizeof(makers[0]);
--- NEW FILE: ndscreate.cpp ---
#include <time.h>
#include <ndstool.h>
#include <ndstool_version.h>
#include "default_arm7.h"
#include "logo.h"
#include "raster.h"
#include "banner.h"
#include "overlay.h"
#include "loadme.h"
#include "ndstree.h"
unsigned int overlay_files = 0;
const char CompileDate[] = __DATE__;
const char CompileTime[] = __TIME__;
/*
* HasElfExtension
*/
bool HasElfExtension(char *filename)
{
char *p = strrchr(filename, '.');
if (!p) return false;
return (strcmp(p, ".elf") == 0);
}
/*
* CopyFromBin
*/
int CopyFromBin(char *binFilename, unsigned int *size = 0, unsigned int *size_without_footer = 0)
{
FILE *fi = fopen(binFilename, "rb");
if (!fi) { fprintf(stderr, "Cannot open file '%s'.\n", binFilename); exit(1); }
unsigned int _size = 0;
while (1)
{
unsigned char buffer[1024];
int bytesread = fread(buffer, 1, sizeof(buffer), fi);
if (bytesread <= 0) break;
fwrite(buffer, 1, bytesread, fNDS);
_size += bytesread;
}
if (size) *size = _size;
// check footer
if (size_without_footer)
{
fseek(fi, _size - 3*4, SEEK_SET);
unsigned_int nitrocode;
fread(&nitrocode, sizeof(nitrocode), 1, fi);
if (nitrocode == 0xDEC00621)
*size_without_footer = _size - 3*4;
else
*size_without_footer = _size;
}
fclose(fi);
return 0;
}
/*
* CopyFromElf
*/
int CopyFromElf(char *elfFilename, unsigned int *entry, unsigned int *ram_address, unsigned int *size)
{
int fd = open(elfFilename, O_RDONLY);
if (fd < 0) { fprintf(stderr, "Cannot open file '%s'.\n", elfFilename); exit(1); }
if (elf_version(EV_CURRENT) == EV_NONE) { fprintf(stderr, "libelf out of date!\n"); exit(1); }
Elf *elf;
if ((elf = elf_begin(fd, ELF_C_READ, 0)) == 0) { fprintf(stderr, "Cannot open ELF file!\n"); exit(1); }
Elf32_Ehdr *ehdr;
if ((ehdr = elf32_getehdr(elf)) == 0) { fprintf(stderr, "Cannot read ELF header!\n"); exit(1); }
if (ehdr->e_machine != EM_ARM) { fprintf(stderr, "Not an ARM ELF file!\n"); exit(1); }
*entry = ehdr->e_entry;
*size = 0;
*ram_address = 0;
// printf("entry = 0x%X\n", ehdr->e_entry);
Elf_Scn *scn = elf_getscn(elf, 0);
Elf32_Shdr *shdr = elf32_getshdr(scn);
while (shdr)
{
if (shdr->sh_flags & SHF_ALLOC)
{
/* char *name;
if (!(name = elf_strptr(elf, ehdr->e_shstrndx, shdr->sh_name))) name = "???";
printf("%s\n", name);*/
if (!*ram_address) *ram_address = shdr->sh_addr; // use first address (assume it's .text)
// don't mind the garbage here
// printf("sh_addr=0x%X sh_offset=0x%X sh_size=0x%X sh_link=%u sh_entsize=%u sh_addralign=%u\n", shdr->sh_addr, shdr->sh_offset, shdr->sh_size, shdr->sh_link, shdr->sh_entsize, shdr->sh_addralign);
// Elf32_Word sh_name;
// Elf32_Word sh_type;
// Elf32_Word sh_flags;
// Elf32_Addr sh_addr;
// Elf32_Off sh_offset;
// Elf32_Word sh_size;
// Elf32_Word sh_link;
// Elf32_Word sh_info;
// Elf32_Word sh_addralign;
// Elf32_Word sh_entsize;
Elf_Data *data;
if ((data = elf_getdata(scn, NULL)))
{
/*for (int i=0; i<data->d_size; i++)
{
printf("%02X ", ((unsigned char *)data->d_buf)[i]);
}
printf("\n");*/
fwrite(data->d_buf, 1, data->d_size, fNDS);
*size += data->d_size;
}
}
scn = elf_nextscn(elf, scn);
shdr = elf32_getshdr(scn);
}
elf_end(elf);
return 0;
}
/*
* AddFile
*/
void AddFile(char *rootdir, char *prefix, char *entry_name, unsigned int file_id, unsigned int alignmask)
{
// make filename
char strbuf[MAXPATHLEN];
strcpy(strbuf, rootdir);
strcat(strbuf, prefix);
strcat(strbuf, entry_name);
//unsigned int file_end = ftell(fNDS);
file_top = (file_top + alignmask) &~ alignmask; // align to alignmask+1 bytes
fseek(fNDS, file_top, SEEK_SET);
FILE *fi = fopen(strbuf, "rb");
if (!fi) { fprintf(stderr, "Cannot open file '%s'.\n", strbuf); exit(1); }
fseek(fi, 0, SEEK_END);
unsigned int size = ftell(fi);
unsigned int file_bottom = file_top + size;
fseek(fi, 0, SEEK_SET);
// print
if (verbose)
{
printf("%5u 0x%08X 0x%08X %9u %s%s\n", file_id, file_top, file_bottom, size, prefix, entry_name);
}
// write data
unsigned int sizeof_copybuf = 256*1024;
unsigned char *copybuf = new unsigned char [sizeof_copybuf];
while (size > 0)
{
unsigned int size2 = (size >= sizeof_copybuf) ? sizeof_copybuf : size;
fread(copybuf, 1, size2, fi);
fwrite(copybuf, 1, size2, fNDS);
size -= size2;
}
delete [] copybuf;
fclose(fi);
if ((unsigned int)ftell(fNDS) > file_end) file_end = ftell(fNDS);
// write fat
fseek(fNDS, header.fat_offset + 8*file_id, SEEK_SET);
unsigned_int top = file_top;
fwrite(&top, 1, sizeof(top), fNDS);
unsigned_int bottom = file_bottom;
fwrite(&bottom, 1, sizeof(bottom), fNDS);
file_top = file_bottom;
}
/*
* AddDirectory
* Walks the tree and adds files to NDS
*/
void AddDirectory(Tree *tree, char *prefix, unsigned int this_dir_id, unsigned int _parent_id, unsigned int alignmask)
{
// skip dummy node
tree = tree->next;
if (verbose) printf("%s\n", prefix);
// write directory info
fseek(fNDS, header.fnt_offset + 8*(this_dir_id & 0xFFF), SEEK_SET);
unsigned_int entry_start = _entry_start; // reference location of entry name
fwrite(&entry_start, 1, sizeof(entry_start), fNDS);
unsigned int _top_file_id = free_file_id;
unsigned_short top_file_id = _top_file_id; // file ID of top entry
fwrite(&top_file_id, 1, sizeof(top_file_id), fNDS);
unsigned_short parent_id = _parent_id; // ID of parent directory or directory count (root)
fwrite(&parent_id, 1, sizeof(parent_id), fNDS);
//printf("dir %X file_id %u +\n", this_dir_id, (int)top_file_id);
// directory entrynames
{
// start of directory entrynames
fseek(fNDS, header.fnt_offset + _entry_start, SEEK_SET);
// write filenames
for (Tree *t=tree; t; t=t->next)
{
if (!t->directory)
{
int namelen = strlen(t->name);
fputc(t->directory ? namelen | 128 : namelen, fNDS); _entry_start += 1;
fwrite(t->name, 1, namelen, fNDS); _entry_start += namelen;
//printf("[ %s -> %u ]\n", t->name, free_file_id);
free_file_id++;
}
}
// write directorynames
for (Tree *t=tree; t; t=t->next)
{
if (t->directory)
{
//printf("*entry %s\n", t->name);
int namelen = strlen(t->name);
fputc(t->directory ? namelen | 128 : namelen, fNDS); _entry_start += 1;
fwrite(t->name, 1, namelen, fNDS); _entry_start += namelen;
//printf("[ %s -> %X ]\n", t->name, t->dir_id);
unsigned_short _dir_id_tmp = t->dir_id;
fwrite(&_dir_id_tmp, 1, sizeof(_dir_id_tmp), fNDS);
_entry_start += sizeof(_dir_id_tmp);
}
}
fputc(0, fNDS); _entry_start += 1; // end of directory entrynames
}
// add files
unsigned int file_id = _top_file_id;
for (Tree *t=tree; t; t=t->next)
{
//printf("*2* %s\n", t->name);
if (!t->directory)
{
AddFile(filerootdir, prefix, t->name, file_id++, alignmask);
}
}
// add subdirectories
for (Tree *t=tree; t; t=t->next)
{
//printf("*2* %s\n", t->name);
if (t->directory)
{
char strbuf[MAXPATHLEN];
strcpy(strbuf, prefix);
strcat(strbuf, t->name);
strcat(strbuf, "/");
AddDirectory(t->directory, strbuf, t->dir_id, this_dir_id, alignmask);
}
}
}
/*
* Create
*/
void Create()
{
fNDS = fopen(ndsfilename, "wb");
if (!fNDS) { fprintf(stderr, "Cannot open file '%s'.\n", ndsfilename); exit(1); }
// 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);
}
else
{
// clear header
memset(&header, 0, 0x200);
memcpy(header.gamecode, "####", 4);
header.reserved2 = 0x04; // autostart
unsigned char romcontrol[] = { 0x00,0x60,0x58,0x00,0xF8,0x08,0x18,0x00 };
memcpy(((unsigned char *)&header) + 0x60, romcontrol, sizeof(romcontrol));
*(unsigned_int *)&header = 0xEA00002E; // for PassMe's that start @ 0x08000000
}
// 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 (!headerfilename) // homebrew?
{
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
}
// unique ID... just for homebrew, not very used... obsolete?
if (uniquefilename)
{
unsigned_int id[2];
FILE *f = fopen(uniquefilename, "rb");
if (f)
{
fread(&id[0], sizeof(id[0]), 1, f);
fread(&id[1], sizeof(id[1]), 1, f);
}
else
{
f = fopen(uniquefilename, "wb");
if (!f) { fprintf(stderr, "Cannot open file '%s'.\n", uniquefilename); exit(1); }
for (char *p=ndsfilename; *p; p++) srand(*p ^ VER[1] ^ VER[3] ^ (rand()<<30) ^ (rand()<<15) ^ rand() ^ time(0) ^ header.arm9_size);
id[0] = (rand()<<15) ^ rand() ^ header.arm7_size;
for (char *p=ndsfilename; *p; p++) srand(*p ^ VER[0] ^ VER[2] ^ (rand()<<30) ^ (rand()<<15) ^ rand() ^ time(0) ^ header.arm7_size);
fwrite(&id[0], sizeof(id[0]), 1, f);
id[1] = (rand()<<30) ^ (rand()<<15) ^ rand() ^ header.arm9_size;
fwrite(&id[1], sizeof(id[1]), 1, f);
}
fclose(f);
header.offset_0x78 = id[0];
header.offset_0x7C = id[1];
}
// game/maker codes
if (gamecode) strncpy(header.gamecode, gamecode, 4);
if (makercode) strncpy((char *)header.makercode, makercode, 2);
// header size
unsigned int header_size = header.rom_header_size;
if (!header_size) { header_size = 0x200; header.rom_header_size = header_size; }
fseek(fNDS, header_size, SEEK_SET);
// --------------------------
// ARM9 binary
if (arm9filename)
{
header.arm9_rom_offset = (ftell(fNDS) + 0x1FF) &~ 0x1FF; // align to 512 bytes
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; }
unsigned int size = 0;
if (HasElfExtension(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 = ((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); // ???
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) + 0x1FF) &~ 0x1FF; // align to 512 bytes
fseek(fNDS, header.arm7_rom_offset, SEEK_SET);
if (arm7filename)
{
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 = 0x03800000; }
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);
}
else // default ARM7 binary
{
fwrite(default_arm7, 1, default_arm7_size, fNDS);
header.arm7_entry_address = 0x03800000;
header.arm7_ram_address = 0x03800000;
header.arm7_size = ((default_arm7_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);
}
// banner
if (bannerfilename)
{
header.banner_offset = (ftell(fNDS) + 0x1FF) &~ 0x1FF; // align to 512 bytes
fseek(fNDS, header.banner_offset, SEEK_SET);
if (bannertype == BANNER_IMAGE)
{
IconFromBMP();
}
else
{
CopyFromBin(bannerfilename, 0);
}
}
else
{
header.banner_offset = 0;
}
// filesystem
if (filerootdir || overlaydir)
{
// read directory structure
Tree *filetree = new Tree(); // dummy root node 0xF000
free_dir_id++;
directory_count++;
if (filerootdir) ReadDirectory(filetree, filerootdir); // fill empty directory
// calculate offsets required for FNT and FAT
_entry_start = 8*directory_count; // names come after directory structs
header.fnt_offset = ftell(fNDS); // not aligned //(ftell(fNDS) + 0x1FF) &~ 0x1FF; // align to 512 bytes
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 + 0x1FF) &~ 0x1FF; // align to 512 bytes;
header.fat_offset = (header.fnt_offset + header.fnt_size + 0x3) &~ 0x3; // align to 4 bytes. should be 0xFF's
//header.fat_offset = header.fnt_offset + header.fnt_size; // not aligned
header.fat_size = file_count * 8; // each entry contains top & bottom offset
file_top = header.fat_offset + header.fat_size;
// add overlay files
for (unsigned int i=0; i<overlay_files; i++)
{
char s[32]; sprintf(s, OVERLAY_FMT, free_file_id);
AddFile(overlaydir, "/", s, free_file_id, 0x3);
free_file_id++; // incremented up to overlay_files
}
AddDirectory(filetree, "/", 0xF000, directory_count, 0x3);
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);
}
}
// --------------------------
// application end offset
int pad = ((ftell(fNDS) + 0x3) &~ 0x3) - ftell(fNDS); // align to 4 bytes
while (pad--) fputc(0, fNDS);
header.application_end_offset = ftell(fNDS);
// 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);
}
--- NEW FILE: hook.cpp ---
// tests... don't mind these
// make && cp ASNE.nds 1.nds && ndstool -v -i -k -i 1.nds -7 c:\work\ds\dummyhook\dummyhook.bin | grep CRC32
// C:\work\DS\buildscripts\tools\nds\ndstool>make && cp submarine_tech_demo_e3_2005.nds 1.nds && ndstool -v -i -T 1.nds -i -7 c:\work\ds\dummyhook\dummyhook.bin
#include <ndstool.h>
#include "crc.h"
/*
* 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);
}
--- NEW FILE: bigint.cpp ---
/*
big integer class for RSA calculation
Numbers stored in big endian order.
*/
/*
* Includes
*/
#include <stdio.h>
#include <string.h>
#include "bigint.h"
/*
* Sub
* Subtract (shifted) b from a.
*/
int BigInt::Sub(BigInt &a, BigInt &b, int b_shift)
{
unsigned int b_byte = sizeof(data) - 1 + (b_shift / 8);
int b_bit = (b_shift % 8);
int carry = 0;
for (int ia=sizeof(data)-1; ia>=0; ia--)
{
unsigned char b_data_shifted = 0;
if (b_byte + 1 < sizeof(b.data)) b_data_shifted |= b.data[b_byte + 1] >> (8 - b_bit);
if (b_byte + 0 < sizeof(b.data)) b_data_shifted |= b.data[b_byte + 0] << b_bit;
b_byte--;
int r = a.data[ia] - b_data_shifted + carry;
data[ia] = r;
carry = r>>8;
}
return carry;
}
/*
* MulMod
* Multiply two numbers and perform modulo.
*/
void BigInt::MulMod(BigInt &a, BigInt &b, BigInt &m)
{
memset(this, 0, sizeof(*this));
for (unsigned int ia=sizeof(data) - 1; ia>=sizeof(data)/2; ia--)
{
int carry = 0;
for (unsigned int ib=sizeof(data) - 1; ib>=sizeof(data)/2; ib--)
{
int iab = ia + ib - sizeof(data) + 1;
unsigned int r = a.data[ia] * b.data[ib] + carry + data[iab];
data[iab] = r;
carry = r>>8;
}
}
for (int shift=sizeof(data)/2*8; shift>=0; shift--)
{
BigInt subbed;
if (!subbed.Sub(*this, m, shift)) // positive?
{
memcpy(this, &subbed, sizeof(*this));
}
}
}
/*
* PowMod
* Raise to power 65537 and perform modulo.
*/
void BigInt::PowMod(BigInt &n, BigInt &m)
{
BigInt n65536;
memcpy(&n65536, &n, sizeof(n));
for (int i=0; i<16; i++) // 16 times n^2 = n^65536
{
BigInt tmp;
tmp.MulMod(n65536, n65536, m);
memcpy(&n65536, &tmp, sizeof(n));
}
MulMod(n, n65536, m); // final result: n^1 * n^65536
}
/*
* print
*/
void BigInt::print()
{
printf("0x");
bool show = false;
for (unsigned int i=0; i<sizeof(data); i++)
{
if (data[i]) show = true;
if (show) printf("%02X", data[i]);
}
printf(show ? "\n" : "0\n");
}
/*
* Set
*/
void BigInt::Set(unsigned char *data, unsigned int length)
{
memset(this, 0, sizeof(*this));
memcpy(this->data + sizeof(this->data) - length, data, length);
}
/*
* Get
*/
void BigInt::Get(unsigned char *data, unsigned int length)
{
memcpy(data, this->data + sizeof(this->data) - length, length);
}
--- NEW FILE: header.cpp ---
#include "ndstool.h"
#include "banner.h"
#include "sha1.h"
#include "crc.h"
#include "bigint.h"
#include "arm7_sha1_homebrew.h"
#include "arm7_sha1_nintendo.h"
/*
* Data
*/
unsigned char publicKeyNintendo[] =
{
0x9E, 0xC1, 0xCC, 0xC0, 0x4A, 0x6B, 0xD0, 0xA0, 0x6D, 0x62, 0xED, 0x5F, 0x15, 0x67, 0x87, 0x12,
0xE6, 0xF4, 0x77, 0x1F, 0xD8, 0x5C, 0x81, 0xCE, 0x0C, 0xD0, 0x22, 0x31, 0xF5, 0x89, 0x08, 0xF5,
0xBE, 0x04, 0xCB, 0xC1, 0x4F, 0x63, 0xD9, 0x5A, 0x98, 0xFF, 0xEB, 0x36, 0x0F, 0x9C, 0x5D, 0xAD,
0x15, 0xB9, 0x99, 0xFB, 0xC6, 0x86, 0x2C, 0x0A, 0x0C, 0xFC, 0xE6, 0x86, 0x03, 0x60, 0xD4, 0x87,
0x28, 0xD5, 0x66, 0x42, 0x9C, 0xF7, 0x04, 0x14, 0x4E, 0x6F, 0x73, 0x20, 0xC3, 0x3E, 0x3F, 0xF5,
0x82, 0x2E, 0x78, 0x18, 0xD6, 0xCD, 0xD5, 0xC2, 0xDC, 0xAA, 0x1D, 0x34, 0x91, 0xEC, 0x99, 0xC9,
0xF7, 0xBF, 0xBF, 0xA0, 0x0E, 0x1E, 0xF0, 0x25, 0xF8, 0x66, 0x17, 0x54, 0x34, 0x28, 0x2D, 0x28,
0xA3, 0xAE, 0xF0, 0xA9, 0xFA, 0x3A, 0x70, 0x56, 0xD2, 0x34, 0xA9, 0xC5, 0x9E, 0x5D, 0xF5, 0xE1,
};
void (*EncryptSecureArea)(unsigned char *data) = 0;
void (*DecryptSecureArea)(unsigned char *data) = 0;
/*
* CalcHeaderCRC
*/
unsigned short CalcHeaderCRC(Header &header)
{
return CalcCrc16((unsigned char *)&header, 0x15E);
}
/*
* CalcLogoCRC
*/
unsigned short CalcLogoCRC(Header &header)
{
return CalcCrc16((unsigned char *)&header + 0xC0, 156);
}
/*
* DetectRomType
*/
int DetectRomType()
{
fseek(fNDS, 0x4000, SEEK_SET);
unsigned int data[3];
fread(data, 1, sizeof(data), fNDS);
if (header.arm9_rom_offset < 0x4000) return ROMTYPE_HOMEBREW;
if (data[0] == 0x00000000 && data[1] == 0x00000000) return ROMTYPE_MULTIBOOT;
if (data[0] == 0xE7FFDEFF && data[1] == 0xE7FFDEFF) return ROMTYPE_NDSDUMPED;
fseek(fNDS, 0x200, SEEK_SET);
for (int i=0x200; i<0x4000; i++)
if (fgetc(fNDS)) return ROMTYPE_MASKROM; // found something odd ;)
return ROMTYPE_ENCRSECURE;
}
/*
* CalcSecureAreaCRC
*/
unsigned short CalcSecureAreaCRC(bool encrypt = false)
{
fseek(fNDS, 0x4000, SEEK_SET);
unsigned char data[0x4000];
fread(data, 1, 0x4000, fNDS);
if (EncryptSecureArea && encrypt) EncryptSecureArea(data);
return CalcCrc16(data, 0x4000);
}
/*
* FixHeaderCRC
*/
void FixHeaderCRC(char *ndsfilename)
{
fNDS = fopen(ndsfilename, "r+b");
if (!fNDS) { fprintf(stderr, "Cannot open file '%s'.\n", ndsfilename); exit(1); }
fread(&header, 512, 1, fNDS);
header.header_crc = CalcHeaderCRC(header);
fseek(fNDS, 0, SEEK_SET);
fwrite(&header, 512, 1, fNDS);
fclose(fNDS);
}
/*
* EnDecryptSecureArea
*/
void EnDecryptSecureArea(char *ndsfilename)
{
fNDS = fopen(ndsfilename, "r+b");
if (!fNDS) { fprintf(stderr, "Cannot open file '%s'.\n", ndsfilename); exit(1); }
fread(&header, 512, 1, fNDS);
int romType = DetectRomType();
unsigned char data[0x4000];
fseek(fNDS, 0x4000, SEEK_SET);
fread(data, 1, 0x4000, fNDS);
if (EncryptSecureArea && romType == ROMTYPE_NDSDUMPED)
{
EncryptSecureArea(data);
header.secure_area_crc = CalcCrc16(data, 0x4000);
header.header_crc = CalcHeaderCRC(header);
printf("Encrypted.\n");
}
else if (DecryptSecureArea && romType >= ROMTYPE_ENCRSECURE)
{
DecryptSecureArea(data);
printf("Decrypted.\n");
}
else
{
fprintf(stderr, "File doesn't appear to have a secure area!\n"); exit(1);
}
fseek(fNDS, 0x4000, SEEK_SET);
fwrite(data, 1, 0x800, fNDS);
fseek(fNDS, 0, SEEK_SET);
fwrite(&header, 512, 1, fNDS);
fclose(fNDS);
}
/*
* ShowHeaderInfo
*/
void ShowHeaderInfo(Header &header, int romType, unsigned int length = 0x200)
{
printf("0x00\t%-25s\t", "Game title");
for (unsigned int i=0; i<sizeof(header.title); i++)
if (header.title[i]) putchar(header.title[i]); printf("\n");
printf("0x0C\t%-25s\t", "Game code");
for (unsigned int i=0; i<sizeof(header.gamecode); i++)
if (header.gamecode[i]) putchar(header.gamecode[i]);
for (int i=0; i<NumCountries; i++)
{
if (countries[i].countrycode == header.gamecode[3])
{
printf(" (NTR-");
for (unsigned int j=0; j<sizeof(header.gamecode); j++)
if (header.gamecode[j]) putchar(header.gamecode[j]);
printf("-%s)", countries[i].name);
break;
}
}
printf("\n");
printf("0x10\t%-25s\t", "Maker code"); for (unsigned int i=0; i<sizeof(header.makercode); i++)
if (header.makercode[i]) putchar(header.makercode[i]);
for (int j=0; j<NumMakers; j++)
{
if ((makers[j].makercode[0] == header.makercode[0]) && (makers[j].makercode[1] == header.makercode[1]))
{
printf(" (%s)", makers[j].name);
break;
}
}
printf("\n");
printf("0x12\t%-25s\t0x%02X\n", "Unit code", header.unitcode);
printf("0x13\t%-25s\t0x%02X\n", "Device type", header.devicetype);
printf("0x14\t%-25s\t0x%02X (%d Mbit)\n", "Device capacity", header.devicecap, 1<<header.devicecap);
printf("0x15\t%-25s\t", "reserved 1"); for (unsigned int i=0; i<sizeof(header.reserved1); i++) printf("%02X", header.reserved1[i]); printf("\n");
printf("0x1E\t%-25s\t0x%02X\n", "ROM version", header.romversion);
printf("0x1F\t%-25s\t0x%02X\n", "reserved 2", header.reserved2);
printf("0x20\t%-25s\t0x%X\n", "ARM9 ROM offset", (int)header.arm9_rom_offset);
printf("0x24\t%-25s\t0x%X\n", "ARM9 entry address", (int)header.arm9_entry_address);
printf("0x28\t%-25s\t0x%X\n", "ARM9 RAM address", (int)header.arm9_ram_address);
printf("0x2C\t%-25s\t0x%X\n", "ARM9 code size", (int)header.arm9_size);
printf("0x30\t%-25s\t0x%X\n", "ARM7 ROM offset", (int)header.arm7_rom_offset);
printf("0x34\t%-25s\t0x%X\n", "ARM7 entry address", (int)header.arm7_entry_address);
printf("0x38\t%-25s\t0x%X\n", "ARM7 RAM address", (int)header.arm7_ram_address);
printf("0x3C\t%-25s\t0x%X\n", "ARM7 code size", (int)header.arm7_size);
printf("0x40\t%-25s\t0x%X\n", "File name table offset", (int)header.fnt_offset);
printf("0x44\t%-25s\t0x%X\n", "File name table size", (int)header.fnt_size);
printf("0x48\t%-25s\t0x%X\n", "FAT offset", (int)header.fat_offset);
printf("0x4C\t%-25s\t0x%X\n", "FAT size", (int)header.fat_size);
printf("0x50\t%-25s\t0x%X\n", "ARM9 overlay offset", (int)header.arm9_overlay_offset);
printf("0x54\t%-25s\t0x%X\n", "ARM9 overlay size", (int)header.arm9_overlay_size);
printf("0x58\t%-25s\t0x%X\n", "ARM7 overlay offset", (int)header.arm7_overlay_offset);
printf("0x5C\t%-25s\t0x%X\n", "ARM7 overlay size", (int)header.arm7_overlay_size);
printf("0x60\t%-25s\t0x%08X\n", "ROM control info 1", (int)header.rom_control_info1);
printf("0x64\t%-25s\t0x%08X\n", "ROM control info 2", (int)header.rom_control_info2);
printf("0x68\t%-25s\t0x%X\n", "Icon/title offset", (int)header.banner_offset);
unsigned short secure_area_crc = CalcSecureAreaCRC((romType == ROMTYPE_NDSDUMPED));
char *s1, *s2 = "";
if (romType == ROMTYPE_HOMEBREW) s1 = "-";
else if (secure_area_crc == header.secure_area_crc) s1 = "OK";
else
{
s1 = "INVALID";
if (!EncryptSecureArea && romType == ROMTYPE_NDSDUMPED) s1 = "DON'T KNOW";
}
switch (romType)
{
case ROMTYPE_HOMEBREW: s2 = "homebrew"; break;
case ROMTYPE_MULTIBOOT: s2 = "multiboot"; break;
case ROMTYPE_NDSDUMPED: s2 = "decrypted"; break;
case ROMTYPE_ENCRSECURE: s2 = "encrypted"; break;
case ROMTYPE_MASKROM: s2 = "mask ROM"; break;
}
printf("0x6C\t%-25s\t0x%04X (%s, %s)\n", "Secure area CRC", (int)header.secure_area_crc, s1, s2);
printf("0x6E\t%-25s\t0x%04X\n", "ROM control info 3", (int)header.rom_control_info3);
printf("0x70\t%-25s\t0x%X\n", "ARM9 ?", (int)header.offset_0x70);
printf("0x74\t%-25s\t0x%X\n", "ARM7 ?", (int)header.offset_0x74);
printf("0x78\t%-25s\t0x%08X\n", "Magic 1", (int)header.offset_0x78);
printf("0x7C\t%-25s\t0x%08X\n", "Magic 2", (int)header.offset_0x7C);
printf("0x80\t%-25s\t0x%08X\n", "Application end offset", (int)header.application_end_offset);
printf("0x84\t%-25s\t0x%08X\n", "ROM header size", (int)header.rom_header_size);
for (unsigned int i=0x88; i<0xC0; i+=4)
{
unsigned_int &x = ((unsigned_int *)&header)[i/4];
if (x != 0) printf("0x%02X\t%-25s\t0x%08X\n", i, "?", (int)x);
}
unsigned short logo_crc = CalcLogoCRC(header);
printf("0x15C\t%-25s\t0x%04X (%s)\n", "Logo CRC", (int)header.logo_crc, (logo_crc == header.logo_crc) ? "OK" : "INVALID");
unsigned short header_crc = CalcHeaderCRC(header);
printf("0x15E\t%-25s\t0x%04X (%s)\n", "Header CRC", (int)header.header_crc, (header_crc == header.header_crc) ? "OK" : "INVALID");
for (unsigned int i=0x160; i<length; i+=4)
{
unsigned_int &x = ((unsigned_int *)&header)[i/4];
if (x != 0) printf("0x%02X\t%-25s\t0x%08X\n", i, "?", (int)x);
}
}
/*
* HeaderSha1
*/
void HeaderSha1(FILE *fNDS, unsigned char *header_sha1, int romType)
{
sha1_ctx m_sha1;
sha1_begin(&m_sha1);
unsigned char buf[32 + 0x200];
fseek(fNDS, 0x200, SEEK_SET); // check for 32 bytes text + alternate header
fread(buf, 1, sizeof(buf), fNDS);
if (!memcmp(buf, "DS DOWNLOAD PLAY", 16)) // found?
{
sha1_hash(buf + 0x20, 0x160, &m_sha1); // alternate header
if (verbose >= 2)
{
printf("{ DS Download Play(TM) / Wireless MultiBoot header information:\n");
ShowHeaderInfo(*(Header *)(buf + 0x20), romType, 0x160);
printf("}\n");
}
}
else
{
fseek(fNDS, 0, SEEK_SET);
fread(buf, 1, sizeof(buf), fNDS);
sha1_hash(buf, 0x160, &m_sha1);
}
sha1_end(header_sha1, &m_sha1);
}
/*
* Arm9Sha1Multiboot
*/
void Arm9Sha1Multiboot(FILE *fNDS, unsigned char *arm9_sha1)
{
sha1_ctx m_sha1;
sha1_begin(&m_sha1);
fseek(fNDS, header.arm9_rom_offset, SEEK_SET);
unsigned int len = header.arm9_size;
unsigned char *buf = new unsigned char [len];
fread(buf, 1, len, fNDS);
//printf("%u\n", len);
sha1_hash(buf, len, &m_sha1);
delete [] buf;
sha1_end(arm9_sha1, &m_sha1);
}
/*
* Arm9Sha1ClearedOutArea
*/
void Arm9Sha1ClearedOutArea(FILE *fNDS, unsigned char *arm9_sha1)
{
sha1_ctx m_sha1;
sha1_begin(&m_sha1);
fseek(fNDS, header.arm9_rom_offset, SEEK_SET);
unsigned int len = header.arm9_size;
unsigned char *buf = new unsigned char [len];
int len1 = (0x5000 - header.arm9_rom_offset); // e.g. 0x5000 - 0x4000 = 0x1000
int len3 = header.arm9_size - (0x7000 - header.arm9_rom_offset); // e.g. 0x10000 - (0x7000 - 0x4000) = 0xD000
int len2 = header.arm9_size - len1 - len3; // e.g. 0x10000 - 0x1000 - 0xD000 = 0x2000
if (len1 > 0) fread(buf, 1, len1, fNDS);
if (len2 > 0) memset(buf + len1, 0, len2); // gets cleared for security?
if (len3 > 0) fread(buf + len1 + len2, 1, len3, fNDS);
// printf("%X %X %X\n", len1, len2, len3);
sha1_hash(buf, len, &m_sha1);
delete [] buf;
sha1_end(arm9_sha1, &m_sha1);
}
/*
* Arm7Sha1
*/
void Arm7Sha1(FILE *fNDS, unsigned char *arm7_sha1)
{
sha1_ctx m_sha1;
sha1_begin(&m_sha1);
fseek(fNDS, header.arm7_rom_offset, SEEK_SET);
unsigned int len = header.arm7_size;
unsigned char *buf = new unsigned char [len];
fread(buf, 1, len, fNDS);
//printf("%u\n", len);
sha1_hash(buf, len, &m_sha1);
delete [] buf;
sha1_end(arm7_sha1, &m_sha1);
}
/*
* CompareSha1WithList
*/
int CompareSha1WithList(unsigned char *arm7_sha1, const unsigned char *text, unsigned int textSize)
{
while (1)
{
//printf("\n");
for (int i=0; i<SHA1_DIGEST_SIZE; i++)
{
unsigned char b = 0;
for (int n=0; n<2; n++)
{
//printf("%c", *text);
if (!*text) return -1;
b = b << 4 | ((*text > '9') ? ((*text - 'A') & 7) + 10 : *text - '0');
text++;
}
//printf("%02X", b);
if (b != arm7_sha1[i]) break; else if (i == 19) return 0;
}
while (*text && (*text >= ' ')) text++; // line end
while (*text && (*text < ' ')) text++; // new line
}
}
/*
* HashAndCompareWithList
* -1=error, 0=match, 1=no match
*/
int HashAndCompareWithList(char *filename, unsigned char sha1[])
{
FILE *f = fopen(filename, "rb");
if (!f) return -1;
sha1_ctx m_sha1;
sha1_begin(&m_sha1);
unsigned char buf[1024];
unsigned int r;
do
{
r = fread(buf, 1, 1024, f);
sha1_hash(buf, r, &m_sha1);
} while (r > 0);
fclose(f);
sha1_end(sha1, &m_sha1);
if (CompareSha1WithList(sha1, arm7_sha1_homebrew, arm7_sha1_homebrew_size)) return 1; // not yet in list
return 0;
}
/*
* strsepc
*/
char *strsepc(char **s, char d)
{
char *r = *s;
for (char *p = *s; ; p++)
{
if (*p == 0) { *s = p; break; }
if (*p == d) { *s = p+1; *p = 0; break; }
}
return r;
}
/*
* RomListInfo
*/
void RomListInfo(unsigned int crc32_match)
{
if (!romlistfilename) return;
FILE *fRomList = fopen(romlistfilename, "rt");
if (!fRomList) { fprintf(stderr, "Cannot open file '%s'.\n", romlistfilename); exit(1); }
char s[1024];
while (fgets(s, 1024, fRomList)) // empty, title, title, title, title, filename, CRC32
{
char *p = s;
if (strlen(strsepc(&p, '\xAC')) == 0)
{
char *title = strsepc(&p, '\xAC');
unsigned int index = strtoul(title, 0, 10);
title += 7;
char *b1 = strchr(title, '(');
char *b2 = b1 ? strchr(b1+1, ')') : 0;
char *b3 = b2 ? strchr(b2+1, '(') : 0;
char *b4 = b3 ? strchr(b3+1, ')') : 0;
char *group = 0;
if (b1 + 2 == b2) if (b3 && b4) { *b3 = 0; *b4 = 0; group = b3+1; } // remove release group name
strsepc(&p, '\xAC'); strsepc(&p, '\xAC');
strsepc(&p, '\xAC'); strsepc(&p, '\xAC');
unsigned long crc32 = strtoul(strsepc(&p, '\xAC'), 0, 16);
if (crc32 == crc32_match)
{
printf("Release index: \t%u\n", index);
printf("Release title: \t%s\n", title);
printf("Release group: \t%s\n", group ? group : "");
}
//for (int i=0; i<10; i++) printf("%d %s\n", i, strsepc(&p, '\xAC'));
}
}
}
/*
* ShowVerboseInfo
*/
void ShowVerboseInfo(FILE *fNDS, Header &header, int romType)
{
// calculate SHA1 of ARM7 binary
unsigned char arm7_sha1[SHA1_DIGEST_SIZE];
Arm7Sha1(fNDS, arm7_sha1);
// find signature data
unsigned_int signature_id = 0;
fseek(fNDS, header.application_end_offset, SEEK_SET);
fread(&signature_id, sizeof(signature_id), 1, fNDS);
if (signature_id != 0x00016361)
{
fseek(fNDS, header.application_end_offset - 136, SEEK_SET); // try again
fread(&signature_id, sizeof(signature_id), 1, fNDS);
}
if (signature_id == 0x00016361)
{
printf("\n");
unsigned char signature[128];
fread(signature, 1, sizeof(signature), fNDS);
unsigned char sha_parts[3*SHA1_DIGEST_SIZE + 4];
fread(sha_parts + 3*SHA1_DIGEST_SIZE, 4, 1, fNDS); // some number
//printf("%08X\n", *(unsigned int *)(sha_parts + 3*SHA1_DIGEST_SIZE));
unsigned char header_sha1[SHA1_DIGEST_SIZE];
HeaderSha1(fNDS, header_sha1, romType);
memcpy(sha_parts + 0*SHA1_DIGEST_SIZE, header_sha1, SHA1_DIGEST_SIZE);
unsigned char arm9_sha1[SHA1_DIGEST_SIZE];
if (romType == ROMTYPE_MULTIBOOT)
{
Arm9Sha1Multiboot(fNDS, arm9_sha1);
}
else
{
Arm9Sha1ClearedOutArea(fNDS, arm9_sha1);
}
memcpy(sha_parts + 1*SHA1_DIGEST_SIZE, arm9_sha1, SHA1_DIGEST_SIZE);
memcpy(sha_parts + 2*SHA1_DIGEST_SIZE, arm7_sha1, SHA1_DIGEST_SIZE);
unsigned char sha_final[SHA1_DIGEST_SIZE];
{
sha1_ctx m_sha1;
sha1_begin(&m_sha1);
unsigned int len = sizeof(sha_parts);
unsigned char *buf = sha_parts;
sha1_hash(buf, len, &m_sha1);
sha1_end(sha_final, &m_sha1);
}
// calculate SHA1 from signature
unsigned char sha1_from_sig[SHA1_DIGEST_SIZE];
{
BigInt _signature;
_signature.Set(signature, sizeof(signature));
//printf("signature: "); _signature.print();
BigInt _publicKey;
_publicKey.Set(publicKeyNintendo, sizeof(publicKeyNintendo));
//printf("public key: "); _publicKey.print();
BigInt big_sha1;
big_sha1.PowMod(_signature, _publicKey);
//printf("big_sha1: "); big_sha1.print();
big_sha1.Get(sha1_from_sig, sizeof(sha1_from_sig));
}
bool ok = (memcmp(sha_final, sha1_from_sig, SHA1_DIGEST_SIZE) == 0);
printf("DS Download Play(TM) / Wireless MultiBoot signature: %s\n", ok ? "OK" : "INVALID");
if (!ok)
{
printf("header hash: \t"); for (int i=0; i<SHA1_DIGEST_SIZE; i++) printf("%02X", (sha_parts + 0*SHA1_DIGEST_SIZE)[i]); printf("\n");
printf("ARM9 hash: \t"); for (int i=0; i<SHA1_DIGEST_SIZE; i++) printf("%02X", (sha_parts + 1*SHA1_DIGEST_SIZE)[i]); printf("\n");
printf("ARM7 hash: \t"); for (int i=0; i<SHA1_DIGEST_SIZE; i++) printf("%02X", (sha_parts + 2*SHA1_DIGEST_SIZE)[i]); printf("\n");
printf("combined hash: \t"); for (int i=0; i<SHA1_DIGEST_SIZE; i++) printf("%02X", sha_final[i]); printf("\n");
printf("signature hash: \t"); for (int i=0; i<SHA1_DIGEST_SIZE; i++) printf("%02X", sha1_from_sig[i]); printf("\n");
}
}
// CRC32
{
unsigned char *buf = new unsigned char [0x10000];
fseek(fNDS, 0, SEEK_SET);
unsigned long crc32 = ~0;
int r;
while ((r = fread(buf, 1, 0x10000, fNDS)) > 0)
{
crc32 = CalcCrc32(buf, r, crc32);
}
crc32 = ~crc32;
delete [] buf;
printf("\nFile CRC32: \t%08X\n", (unsigned int)crc32);
RomListInfo(crc32);
}
// ROM dumper 1.0 bad data
{
unsigned char buf[0x200];
fseek(fNDS, 0x7E00, SEEK_SET);
fread(buf, 1, 0x200, fNDS);
unsigned long crc32 = ~CalcCrc32(buf, 0x200);
printf("\nSMT dumper v1.0 corruption check: \t%s\n", (crc32 == 0x7E8B456F) ? "CORRUPTED" : "OK");
}
// Verify ARM7 SHA1 hash against known default binaries
int bKnownArm7 = 0;
{
printf("\nARM7 binary hash : \t"); for (int i=0; i<SHA1_DIGEST_SIZE; i++) printf("%02X", arm7_sha1[i]); printf("\n");
if (CompareSha1WithList(arm7_sha1, arm7_sha1_homebrew, arm7_sha1_homebrew_size) == 0)
{
bKnownArm7 = 1; printf("ARM7 binary is homebrew verified.\n");
}
if (CompareSha1WithList(arm7_sha1, arm7_sha1_nintendo, arm7_sha1_nintendo_size) == 0)
{
bKnownArm7 = 2; printf("ARM7 binary is Nintendo verified.\n");
}
if (!bKnownArm7) printf("WARNING! ARM7 binary is NOT verified!\n");
}
// check ARM7 RAM address
if (bKnownArm7 != 2)
if ((header.arm7_ram_address < 0x03000000) || (header.arm7_ram_address >= 0x04000000))
{
printf("\nWARNING! ARM7 RAM address does not point to shared memory!\n");
}
// check ARM7 entry address
if ((header.arm7_entry_address < header.arm7_ram_address) ||
(header.arm7_entry_address > header.arm7_ram_address + header.arm7_size))
{
printf("\nWARNING! ARM7 entry address points outside of ARM7 binary!\n");
}
}
/*
* ShowInfo
*/
void ShowInfo(char *ndsfilename)
{
fNDS = fopen(ndsfilename, "rb");
if (!fNDS) { fprintf(stderr, "Cannot open file '%s'.\n", ndsfilename); exit(1); }
fread(&header, 512, 1, fNDS);
int romType = DetectRomType();
printf("Header information:\n");
ShowHeaderInfo(header, romType);
// banner info
if (header.banner_offset)
{
Banner banner;
fseek(fNDS, header.banner_offset, SEEK_SET);
if (fread(&banner, 1, sizeof(banner), fNDS))
{
unsigned short banner_crc = CalcBannerCRC(banner);
printf("\n");
printf("Banner CRC: \t0x%04X (%s)\n", (int)banner.crc, (banner_crc == banner.crc) ? "OK" : "INVALID");
for (int language=1; language<=1; language++)
{
int line = 1;
bool nextline = true;
for (int i=0; i<128; i++)
{
unsigned short c = banner.title[language][i];
if (c >= 128) c = '_';
if (c == 0x00) { printf("\n"); break; }
if (c == 0x0A)
{
nextline = true;
}
else
{
if (nextline)
{
if (line != 1) printf("\n");
printf("%s banner text, line %d:", bannerLanguages[language], line);
for (unsigned int i=0; i<11 - strlen(bannerLanguages[language]); i++) putchar(' ');
printf("\t");
nextline = false;
line++;
}
putchar(c);
}
}
}
}
}
// ARM9 footer
fseek(fNDS, header.arm9_rom_offset + header.arm9_size, SEEK_SET);
unsigned_int nitrocode;
if (fread(&nitrocode, sizeof(nitrocode), 1, fNDS) && (nitrocode == 0xDEC00621))
{
printf("\n");
printf("ARM9 footer found.\n");
unsigned_int x;
fread(&x, sizeof(x), 1, fNDS);
fread(&x, sizeof(x), 1, fNDS);
}
// more information
if (verbose >= 1)
{
ShowVerboseInfo(fNDS, header, romType);
}
fclose(fNDS);
}
More information about the dslinux-commit
mailing list