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