/*
    dlditool - Dynamically Linked Disk Interface patch tool
    Copyright (C) 2006  Michael Chisholm (Chishm)

	Send all queries to chishm@hotmail.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

	* v1.24d - 2010-06-14 - zeromus
		* Modified for inclusion in desmume

	* v1.24 - 2007-08-02 - SmileyDude
		* Now using EXIT_SUCCESS and EXIT_FAILURE at the suggestion of MachinShin.
		* Defined EXIT_NO_DLDI_SECTION for when there is no DLDI section in a file.
		* Added cast to strcmp() call to appease the compiler.

	* v1.23 - 2007-01-23 - Chishm
		* Fixed bug when DLDI section doesn't exist
		* addr_t is now a signed int

	* v1.22 - 2007-01-12 - WinterMute
		* add search paths for dldi files

	* v1.21 - 2007-01-12 - Chishm
		* Improved error messages

	* v1.20 - 2007-01-11 - Chishm
		* Changed offset calculation method

	* v1.10 - 2007-01-07 - Chishm
		* Removed assumptions about endianess of computer
			* Word size shouldn't matter now either, except that an int type must be at least 32 bits long
		* Added *.sc.nds and *.gba.nds file extensions
		* Improved command line argument parsing

	* v1.01 - 2006-12-30 - Chishm
		* Minor bugfix parsing 3 arguments

	* v1.00 - 2006-12-25 - Chishm
		* Original release
*/
//#define VERSION "v1.24"

#include <cstdio>
#include <cstring>
#include <cctype>
#include <cerrno>
#include <cstdlib>
#include <cstdarg>

#include "pstdint.h"
#ifndef _MSC_VER
# include <unistd.h>
# include <sys/param.h>
#else
#define MAXPATHLEN 1024
#endif

#include <sys/stat.h>

namespace DLDI
{

typedef int32_t addr_t;
typedef unsigned char data_t;

#define FEATURE_MEDIUM_CANREAD 0x00000001
#define FEATURE_MEDIUM_CANWRITE 0x00000002
#define FEATURE_SLOT_GBA 0x00000010
#define FEATURE_SLOT_NDS 0x00000020

#define MAGIC_TOKEN 0xBF8DA5ED

#define FIX_ALL 0x01
#define FIX_GLUE 0x02
#define FIX_GOT 0x04
#define FIX_BSS 0x08

#define DLDI_VERSION 1

#define EXIT_NO_DLDI_SECTION	2

enum DldiOffsets
{
	DO_magicString = 0x00, // "\xED\xA5\x8D\xBF Chishm"
	DO_magicToken = 0x00, // 0xBF8DA5ED
	DO_magicShortString = 0x04, // " Chishm"
	DO_version = 0x0C,
	DO_driverSize = 0x0D,
	DO_fixSections = 0x0E,
	DO_allocatedSpace = 0x0F,

	DO_friendlyName = 0x10,

	DO_text_start = 0x40, // Data start
	DO_data_end = 0x44, // Data end
	DO_glue_start = 0x48, // Interworking glue start -- Needs address fixing
	DO_glue_end = 0x4C, // Interworking glue end
	DO_got_start = 0x50, // GOT start -- Needs address fixing
	DO_got_end = 0x54, // GOT end
	DO_bss_start = 0x58, // bss start -- Needs setting to zero
	DO_bss_end = 0x5C, // bss end

	// IO_INTERFACE data
	DO_ioType = 0x60,
	DO_features = 0x64,
	DO_startup = 0x68,
	DO_isInserted = 0x6C,
	DO_readSectors = 0x70,
	DO_writeSectors = 0x74,
	DO_clearStatus = 0x78,
	DO_shutdown = 0x7C,
	DO_code = 0x80
};

const data_t dldiMagicString[] = "\xED\xA5\x8D\xBF Chishm";
const char dldiFileExtension[] = ".dldi";

addr_t readAddr(data_t *mem, addr_t offset)
{
	return mem[offset + 0] | (mem[offset + 1] << 8) | (mem[offset + 2] << 16) | (mem[offset + 3] << 24);
}

void writeAddr(data_t *mem, addr_t offset, addr_t value)
{
	mem[offset + 0] = value & 0xFF;
	mem[offset + 1] = (value >> 8) & 0xFF;
	mem[offset + 2] = (value >> 16) & 0xFF;
	mem[offset + 3] = (value >> 24) & 0xFF;
}

int stringCaseInsensitiveCompare(const char *str1, const char *str2)
{
	while (tolower(*str1) == tolower(*str2))
	{
		if (!*str1)
			return 0;
		++str1;
		++str2;
	}
	return tolower(*str1) - tolower(*str2);
}

bool stringEndsWith(const char *str, const char *end)
{
	if (strlen(str) < strlen(end))
		return false;
	const char *strEnd = &str[strlen (str) - strlen(end)];
	return !stringCaseInsensitiveCompare(strEnd, end);
}

bool stringStartsWith(const char *str, const char *start)
{
	return strstr(str, start) == str;
}

addr_t quickFind(const data_t *data, const data_t *search, size_t dataLen, size_t searchLen)
{
	const int32_t *dataChunk = reinterpret_cast<const int32_t *>(data);
	int searchChunk = reinterpret_cast<const int32_t *>(search)[0];
	addr_t dataChunkEnd = static_cast<addr_t>(dataLen / sizeof(int32_t));

	for (addr_t i = 0; i < dataChunkEnd; ++i)
	{
		if (dataChunk[i] == searchChunk)
		{
			if (i * sizeof(int32_t) + searchLen > dataLen)
				return -1;
			if (!memcmp(&data[i * sizeof(int32_t)], search, searchLen))
				return i * sizeof(int32_t);
		}
	}

	return -1;
}

FILE *openDLDIFile(const char *argv0, char *dldiFileName )
{
	// add .dldi extension to filename
	if (!stringEndsWith(dldiFileName, dldiFileExtension))
		strcat(dldiFileName, dldiFileExtension);

	printf("Trying \"%s\"\n", dldiFileName);
	// try opening from current directory
	FILE *dldiFile = fopen(dldiFileName, "rb");

	if (dldiFile)
		return dldiFile;

	// check if the filename has a path component
	// check both slash varieties, win32 understands both
	// if we have a directory separator don't bother with search paths
	if (strstr(dldiFileName, "\\"))
		return nullptr;
	if (strstr(dldiFileName ,"/"))
		return nullptr;

	// check for DLDIPATH in environment
	char *dldiPATH = getenv("DLDIPATH");

	char appPath[MAXPATHLEN];
	if (dldiPATH)
	{
		strcpy(appPath, dldiPATH);
		if (appPath[strlen(appPath)] != '\\' &&  appPath[strlen(appPath)] != '/')
			strcat(appPath, "/");
		strcat(appPath, dldiFileName);

		printf("Trying \"%s\"\n", appPath);
		dldiFile = fopen(appPath,"rb");

		if (dldiFile)
			return dldiFile;
	}

	char *lastSlash = nullptr;
	char *ptr = const_cast<char *>(argv0);

	while (*(ptr++))
		if (*ptr == '\\' || * ptr == '/')
			lastSlash = ptr;

	char appName[MAXPATHLEN];
	char appPathName[MAXPATHLEN];
	if (lastSlash)
	{
		*(lastSlash++) = '\0';
		strcpy(appPath, argv0);
		strcpy(appName, lastSlash);
		strcat(appPath, "/");
	}
	else
	{
		strcpy(appPath, "");
		strcpy(appName, argv0);
	}

	// finally try in the application path
	// if argv0 contains a directory separator we have a path component

	if (!strstr(appPath,"\\") && !strstr(appPath,"/"))
	{
		// no path in argv0 so search system path
		char *sysPATH = getenv("PATH");
		char *thisPATH = sysPATH;
		printf("Searching system path\n%s\n", sysPATH);

		while (1)
		{
			char *nextPATH = strstr(thisPATH, ":" ); // find next PATH separator

			if (nextPATH)
				*(nextPATH++) = '\0'; // terminate string, point to next component

			strcpy(appPath, thisPATH);
			strcat(appPath, "/");
			strcpy(appPathName, appPath);
			strcat(appPathName, appName); // add application name

			struct stat buf;
			if (!stat(appPathName, &buf)) // if it exists we found the path
				break;

			thisPATH = nextPATH;
			strcpy(appPath, "");// empty path
			if (!thisPATH)
				break;
		}
	}

	strcat(appPath, "dldi/"); // add dldi folder
	strcat(appPath, dldiFileName); // add dldi filename to path
	printf("Trying \"%s\"\n", appPath);

	return fopen(appPath, "rb"); // no more places to check, just return this handle
}

//  Source File: mpcf.dldi
//         Time: 6/14/2010 9:38 PM
// Orig. Offset: 0 / 0x00000000
//       Length: 1876 / 0x00000754 (bytes)
data_t mpcf_dldi[] =
{
    0xED, 0xA5, 0x8D, 0xBF, 0x20, 0x43, 0x68, 0x69, 0x73, 0x68, 0x6D, 0x00, 0x01, 0x0B, 0x0C, 0x00,
    0x47, 0x42, 0x41, 0x20, 0x4D, 0x6F, 0x76, 0x69, 0x65, 0x20, 0x50, 0x6C, 0x61, 0x79, 0x65, 0x72,
    0x20, 0x28, 0x43, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0x20, 0x46, 0x6C, 0x61, 0x73, 0x68, 0x29,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x80, 0xBF, 0x54, 0x07, 0x80, 0xBF, 0x98, 0x00, 0x80, 0xBF, 0x98, 0x00, 0x80, 0xBF,
    0x50, 0x07, 0x80, 0xBF, 0x50, 0x07, 0x80, 0xBF, 0x54, 0x07, 0x80, 0xBF, 0x70, 0x07, 0x80, 0xBF,
    0x4D, 0x50, 0x43, 0x46, 0x13, 0x00, 0x00, 0x00, 0x3C, 0x01, 0x80, 0xBF, 0x98, 0x01, 0x80, 0xBF,
    0x78, 0x02, 0x80, 0xBF, 0xC8, 0x04, 0x80, 0xBF, 0xC8, 0x01, 0x80, 0xBF, 0x10, 0x07, 0x80, 0xBF,
    0x0D, 0xC0, 0xA0, 0xE1, 0xF8, 0xDF, 0x2D, 0xE9, 0x04, 0xB0, 0x4C, 0xE2, 0x28, 0xD0, 0x4B, 0xE2,
    0xF0, 0x6F, 0x9D, 0xE8, 0x1E, 0xFF, 0x2F, 0xE1, 0x10, 0x40, 0x2D, 0xE9, 0x2C, 0x40, 0x9F, 0xE5,
    0x00, 0x30, 0xD4, 0xE5, 0x00, 0x00, 0x53, 0xE3, 0x24, 0x20, 0x9F, 0xE5, 0x05, 0x00, 0x00, 0x1A,
    0x00, 0x00, 0x52, 0xE3, 0x1C, 0x00, 0x9F, 0xE5, 0x0F, 0xE0, 0xA0, 0x11, 0x12, 0xFF, 0x2F, 0x11,
    0x01, 0x30, 0xA0, 0xE3, 0x00, 0x30, 0xC4, 0xE5, 0x10, 0x40, 0xBD, 0xE8, 0x1E, 0xFF, 0x2F, 0xE1,
    0x54, 0x07, 0x80, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x48, 0x07, 0x80, 0xBF, 0x04, 0xE0, 0x2D, 0xE5,
    0x40, 0x30, 0x9F, 0xE5, 0x00, 0x00, 0x53, 0xE3, 0x04, 0xD0, 0x4D, 0xE2, 0x38, 0x00, 0x9F, 0xE5,
    0x38, 0x10, 0x9F, 0xE5, 0x0F, 0xE0, 0xA0, 0x11, 0x13, 0xFF, 0x2F, 0x11, 0x30, 0x00, 0x9F, 0xE5,
    0x00, 0x30, 0x90, 0xE5, 0x00, 0x00, 0x53, 0xE3, 0x28, 0x30, 0x9F, 0xE5, 0x02, 0x00, 0x00, 0x0A,
    0x00, 0x00, 0x53, 0xE3, 0x0F, 0xE0, 0xA0, 0x11, 0x13, 0xFF, 0x2F, 0x11, 0x04, 0xD0, 0x8D, 0xE2,
    0x04, 0xE0, 0x9D, 0xE4, 0x1E, 0xFF, 0x2F, 0xE1, 0x00, 0x00, 0x00, 0x00, 0x48, 0x07, 0x80, 0xBF,
    0x58, 0x07, 0x80, 0xBF, 0x4C, 0x07, 0x80, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x09, 0x14, 0xA0, 0xE3,
    0x06, 0x18, 0x81, 0xE2, 0xB0, 0x30, 0xD1, 0xE1, 0x03, 0x30, 0xE0, 0xE1, 0x03, 0x38, 0xA0, 0xE1,
    0x23, 0x38, 0xA0, 0xE1, 0xFF, 0x30, 0x03, 0xE2, 0xB0, 0x30, 0xC1, 0xE1, 0xB0, 0x20, 0xD1, 0xE1,
    0x04, 0xE0, 0x2D, 0xE5, 0xAA, 0xCC, 0xA0, 0xE3, 0x55, 0xEC, 0xE0, 0xE3, 0x03, 0x00, 0x52, 0xE1,
    0xAA, 0xE0, 0x4E, 0xE2, 0x55, 0xC0, 0x8C, 0xE2, 0x00, 0x00, 0xA0, 0xE3, 0x03, 0x00, 0x00, 0x1A,
    0xB0, 0xE0, 0xC1, 0xE1, 0xB0, 0x30, 0xD1, 0xE1, 0x0C, 0x00, 0x53, 0xE0, 0x01, 0x00, 0xA0, 0x13,
    0x04, 0xE0, 0x9D, 0xE4, 0x1E, 0xFF, 0x2F, 0xE1, 0x26, 0x35, 0xA0, 0xE3, 0x03, 0x37, 0x83, 0xE2,
    0x50, 0x20, 0xA0, 0xE3, 0xB0, 0x20, 0xC3, 0xE1, 0xB0, 0x00, 0xD3, 0xE1, 0x00, 0x08, 0xA0, 0xE1,
    0x20, 0x08, 0xA0, 0xE1, 0xFF, 0x00, 0x00, 0xE2, 0x50, 0x00, 0x50, 0xE3, 0x00, 0x00, 0xA0, 0x13,
    0x01, 0x00, 0xA0, 0x03, 0x1E, 0xFF, 0x2F, 0xE1, 0x09, 0x34, 0xA0, 0xE3, 0x0E, 0x38, 0x83, 0xE2,
    0xB0, 0x20, 0xD3, 0xE1, 0x80, 0x00, 0x12, 0xE3, 0x0C, 0x00, 0x00, 0x0A, 0x00, 0x00, 0xA0, 0xE3,
    0x01, 0x00, 0x00, 0xEA, 0x01, 0x00, 0x50, 0xE1, 0x08, 0x00, 0x00, 0x0A, 0x09, 0x34, 0xA0, 0xE3,
    0x0E, 0x38, 0x83, 0xE2, 0xB0, 0x20, 0xD3, 0xE1, 0x26, 0x17, 0xA0, 0xE3, 0x96, 0x1C, 0x81, 0xE2,
    0x80, 0x00, 0x12, 0xE3, 0x01, 0x00, 0x80, 0xE2, 0x80, 0x10, 0x81, 0xE2, 0xF4, 0xFF, 0xFF, 0x1A,
    0x26, 0x15, 0xA0, 0xE3, 0x03, 0x17, 0x81, 0xE2, 0xB0, 0x30, 0xD1, 0xE1, 0x50, 0x30, 0x13, 0xE2,
    0x01, 0x00, 0xA0, 0x13, 0x1E, 0xFF, 0x2F, 0x11, 0x26, 0x27, 0xA0, 0xE3, 0x96, 0x2C, 0x82, 0xE2,
    0x03, 0x00, 0xA0, 0xE1, 0x80, 0x20, 0x82, 0xE2, 0x01, 0x00, 0x00, 0xEA, 0x02, 0x00, 0x50, 0xE1,
    0x0A, 0x00, 0x00, 0x0A, 0xB0, 0x30, 0xD1, 0xE1, 0x50, 0x30, 0x13, 0xE2, 0x01, 0x00, 0x80, 0xE2,
    0xF9, 0xFF, 0xFF, 0x0A, 0x26, 0x37, 0xA0, 0xE3, 0x96, 0x3C, 0x83, 0xE2, 0x7F, 0x30, 0x83, 0xE2,
    0x03, 0x00, 0x50, 0xE1, 0x00, 0x00, 0xA0, 0xC3, 0x01, 0x00, 0xA0, 0xD3, 0x1E, 0xFF, 0x2F, 0xE1,
    0x03, 0x00, 0xA0, 0xE1, 0x1E, 0xFF, 0x2F, 0xE1, 0xF0, 0x4B, 0x2D, 0xE9, 0x09, 0x34, 0xA0, 0xE3,
    0x0E, 0x38, 0x83, 0xE2, 0xB0, 0xC0, 0xD3, 0xE1, 0x80, 0x00, 0x1C, 0xE3, 0x10, 0xD0, 0x4D, 0xE2,
    0x01, 0x90, 0xA0, 0xE1, 0x02, 0xE0, 0xA0, 0xE1, 0x0C, 0x00, 0x00, 0x0A, 0x00, 0xC0, 0xA0, 0xE3,
    0x01, 0x00, 0x00, 0xEA, 0x01, 0x00, 0x5C, 0xE1, 0x08, 0x00, 0x00, 0x0A, 0x09, 0x34, 0xA0, 0xE3,
    0x0E, 0x38, 0x83, 0xE2, 0xB0, 0x20, 0xD3, 0xE1, 0x26, 0x17, 0xA0, 0xE3, 0x96, 0x1C, 0x81, 0xE2,
    0x80, 0x00, 0x12, 0xE3, 0x01, 0xC0, 0x8C, 0xE2, 0x80, 0x10, 0x81, 0xE2, 0xF4, 0xFF, 0xFF, 0x1A,
    0x26, 0xC5, 0xA0, 0xE3, 0x03, 0xC7, 0x8C, 0xE2, 0xB0, 0x30, 0xDC, 0xE1, 0x50, 0x30, 0x13, 0xE2,
    0x26, 0x17, 0xA0, 0x03, 0x96, 0x1C, 0x81, 0x02, 0x03, 0x20, 0xA0, 0x01, 0x80, 0x10, 0x81, 0x02,
    0x02, 0x00, 0x00, 0x0A, 0x0A, 0x00, 0x00, 0xEA, 0x01, 0x00, 0x52, 0xE1, 0x67, 0x00, 0x00, 0x0A,
    0xB0, 0x30, 0xDC, 0xE1, 0x50, 0x00, 0x13, 0xE3, 0x01, 0x20, 0x82, 0xE2, 0xF9, 0xFF, 0xFF, 0x0A,
    0x26, 0x37, 0xA0, 0xE3, 0x96, 0x3C, 0x83, 0xE2, 0x7F, 0x30, 0x83, 0xE2, 0x03, 0x00, 0x52, 0xE1,
    0x5E, 0x00, 0x00, 0xCA, 0xFF, 0x00, 0x59, 0xE3, 0x00, 0x30, 0xA0, 0x83, 0x0C, 0x30, 0x8D, 0x85,
    0x5E, 0x00, 0x00, 0x9A, 0x00, 0x38, 0xA0, 0xE1, 0x23, 0x38, 0xA0, 0xE1, 0xFF, 0x30, 0x03, 0xE2,
    0x09, 0x54, 0xA0, 0xE3, 0x04, 0x30, 0x8D, 0xE5, 0x0C, 0x30, 0x9D, 0xE5, 0x20, 0x4C, 0xA0, 0xE1,
    0x0E, 0xB0, 0xA0, 0xE1, 0x05, 0x60, 0xA0, 0xE1, 0x20, 0x74, 0xA0, 0xE1, 0x05, 0x20, 0xA0, 0xE1,
    0x20, 0x18, 0xA0, 0xE1, 0x05, 0xC0, 0xA0, 0xE1, 0x05, 0x00, 0xA0, 0xE1, 0x05, 0xE0, 0xA0, 0xE1,
    0x01, 0x57, 0x85, 0xE2, 0xB0, 0x30, 0xC5, 0xE1, 0x04, 0x30, 0x9D, 0xE5, 0x0F, 0x40, 0x04, 0xE2,
    0x06, 0x68, 0x86, 0xE2, 0xB0, 0x30, 0xC6, 0xE1, 0xFF, 0x70, 0x07, 0xE2, 0x02, 0x27, 0x82, 0xE2,
    0xFF, 0x10, 0x01, 0xE2, 0x0A, 0x08, 0x80, 0xE2, 0xE0, 0x40, 0x84, 0xE3, 0x03, 0xC7, 0x8C, 0xE2,
    0x0E, 0xE8, 0x8E, 0xE2, 0x20, 0x30, 0xA0, 0xE3, 0xB0, 0x70, 0xC2, 0xE1, 0xB0, 0x10, 0xC0, 0xE1,
    0xB0, 0x40, 0xCC, 0xE1, 0xB0, 0x30, 0xCE, 0xE1, 0x0B, 0x80, 0xA0, 0xE1, 0x01, 0x90, 0x59, 0xE2,
    0x3E, 0x00, 0x00, 0x3A, 0x26, 0x25, 0xA0, 0xE3, 0x03, 0x27, 0x82, 0xE2, 0xB0, 0x30, 0xD2, 0xE1,
    0x03, 0x38, 0xA0, 0xE1, 0x23, 0x38, 0xA0, 0xE1, 0xFF, 0x30, 0x03, 0xE2, 0x58, 0x00, 0x53, 0xE3,
    0x26, 0x17, 0xA0, 0x13, 0x96, 0x1C, 0x81, 0x12, 0x02, 0x00, 0xA0, 0x11, 0x80, 0x10, 0x81, 0x12,
    0x00, 0x20, 0xA0, 0x13, 0x02, 0x00, 0x00, 0x1A, 0x0D, 0x00, 0x00, 0xEA, 0x01, 0x00, 0x52, 0xE1,
    0x26, 0x00, 0x00, 0x0A, 0xB0, 0x30, 0xD0, 0xE1, 0x03, 0x38, 0xA0, 0xE1, 0x23, 0x38, 0xA0, 0xE1,
    0xFF, 0x30, 0x03, 0xE2, 0x58, 0x00, 0x53, 0xE3, 0x01, 0x20, 0x82, 0xE2, 0xF6, 0xFF, 0xFF, 0x1A,
    0x26, 0x37, 0xA0, 0xE3, 0x96, 0x3C, 0x83, 0xE2, 0x7F, 0x30, 0x83, 0xE2, 0x03, 0x00, 0x52, 0xE1,
    0x1A, 0x00, 0x00, 0xCA, 0x01, 0x00, 0x18, 0xE3, 0x0B, 0x10, 0xA0, 0x01, 0xFF, 0x20, 0xA0, 0x03,
    0x09, 0x04, 0xA0, 0x03, 0x0E, 0x00, 0x00, 0x0A, 0x08, 0x10, 0xA0, 0xE1, 0xFF, 0x00, 0xA0, 0xE3,
    0x09, 0xC4, 0xA0, 0xE3, 0xB0, 0x30, 0xDC, 0xE1, 0x03, 0x38, 0xA0, 0xE1, 0x23, 0x38, 0xA0, 0xE1,
    0x01, 0x00, 0x40, 0xE2, 0x43, 0x24, 0xA0, 0xE1, 0x01, 0x00, 0x70, 0xE3, 0x01, 0x20, 0xC1, 0xE5,
    0x00, 0x30, 0xC1, 0xE5, 0x02, 0x10, 0x81, 0xE2, 0xF5, 0xFF, 0xFF, 0x1A, 0x02, 0x8C, 0x88, 0xE2,
    0xCD, 0xFF, 0xFF, 0xEA, 0x01, 0x20, 0x42, 0xE2, 0xB0, 0x30, 0xD0, 0xE1, 0x01, 0x00, 0x72, 0xE3,
    0xB2, 0x30, 0xC1, 0xE0, 0xFA, 0xFF, 0xFF, 0x1A, 0x02, 0xBC, 0x8B, 0xE2, 0xC6, 0xFF, 0xFF, 0xEA,
    0x00, 0x00, 0xA0, 0xE3, 0x10, 0xD0, 0x8D, 0xE2, 0xF0, 0x4B, 0xBD, 0xE8, 0x1E, 0xFF, 0x2F, 0xE1,
    0x09, 0x38, 0xA0, 0xE1, 0x23, 0x38, 0xA0, 0xE1, 0x0C, 0x30, 0x8D, 0xE5, 0x9C, 0xFF, 0xFF, 0xEA,
    0x01, 0x00, 0xA0, 0xE3, 0xF6, 0xFF, 0xFF, 0xEA, 0xF0, 0x4B, 0x2D, 0xE9, 0x09, 0x34, 0xA0, 0xE3,
    0x0E, 0x38, 0x83, 0xE2, 0xB0, 0xC0, 0xD3, 0xE1, 0x80, 0x00, 0x1C, 0xE3, 0x10, 0xD0, 0x4D, 0xE2,
    0x01, 0x90, 0xA0, 0xE1, 0x02, 0xE0, 0xA0, 0xE1, 0x0C, 0x00, 0x00, 0x0A, 0x00, 0xC0, 0xA0, 0xE3,
    0x01, 0x00, 0x00, 0xEA, 0x01, 0x00, 0x5C, 0xE1, 0x08, 0x00, 0x00, 0x0A, 0x09, 0x34, 0xA0, 0xE3,
    0x0E, 0x38, 0x83, 0xE2, 0xB0, 0x20, 0xD3, 0xE1, 0x26, 0x17, 0xA0, 0xE3, 0x96, 0x1C, 0x81, 0xE2,
    0x80, 0x00, 0x12, 0xE3, 0x01, 0xC0, 0x8C, 0xE2, 0x80, 0x10, 0x81, 0xE2, 0xF4, 0xFF, 0xFF, 0x1A,
    0x26, 0xC5, 0xA0, 0xE3, 0x03, 0xC7, 0x8C, 0xE2, 0xB0, 0x30, 0xDC, 0xE1, 0x50, 0x30, 0x13, 0xE2,
    0x26, 0x17, 0xA0, 0x03, 0x96, 0x1C, 0x81, 0x02, 0x03, 0x20, 0xA0, 0x01, 0x80, 0x10, 0x81, 0x02,
    0x02, 0x00, 0x00, 0x0A, 0x0A, 0x00, 0x00, 0xEA, 0x01, 0x00, 0x52, 0xE1, 0x65, 0x00, 0x00, 0x0A,
    0xB0, 0x30, 0xDC, 0xE1, 0x50, 0x00, 0x13, 0xE3, 0x01, 0x20, 0x82, 0xE2, 0xF9, 0xFF, 0xFF, 0x0A,
    0x26, 0x37, 0xA0, 0xE3, 0x96, 0x3C, 0x83, 0xE2, 0x7F, 0x30, 0x83, 0xE2, 0x03, 0x00, 0x52, 0xE1,
    0x5C, 0x00, 0x00, 0xCA, 0xFF, 0x00, 0x59, 0xE3, 0x00, 0x30, 0xA0, 0x83, 0x0C, 0x30, 0x8D, 0x85,
    0x5C, 0x00, 0x00, 0x9A, 0x00, 0x38, 0xA0, 0xE1, 0x23, 0x38, 0xA0, 0xE1, 0xFF, 0x30, 0x03, 0xE2,
    0x09, 0x54, 0xA0, 0xE3, 0x04, 0x30, 0x8D, 0xE5, 0x0C, 0x30, 0x9D, 0xE5, 0x20, 0x4C, 0xA0, 0xE1,
    0x0E, 0xB0, 0xA0, 0xE1, 0x05, 0x60, 0xA0, 0xE1, 0x20, 0x74, 0xA0, 0xE1, 0x05, 0x20, 0xA0, 0xE1,
    0x20, 0x18, 0xA0, 0xE1, 0x05, 0xC0, 0xA0, 0xE1, 0x05, 0x00, 0xA0, 0xE1, 0x05, 0xE0, 0xA0, 0xE1,
    0x01, 0x57, 0x85, 0xE2, 0xB0, 0x30, 0xC5, 0xE1, 0x04, 0x30, 0x9D, 0xE5, 0x0F, 0x40, 0x04, 0xE2,
    0x06, 0x68, 0x86, 0xE2, 0xB0, 0x30, 0xC6, 0xE1, 0xFF, 0x70, 0x07, 0xE2, 0x02, 0x27, 0x82, 0xE2,
    0xFF, 0x10, 0x01, 0xE2, 0x0A, 0x08, 0x80, 0xE2, 0xE0, 0x40, 0x84, 0xE3, 0x03, 0xC7, 0x8C, 0xE2,
    0x0E, 0xE8, 0x8E, 0xE2, 0x30, 0x30, 0xA0, 0xE3, 0xB0, 0x70, 0xC2, 0xE1, 0xB0, 0x10, 0xC0, 0xE1,
    0xB0, 0x40, 0xCC, 0xE1, 0xB0, 0x30, 0xCE, 0xE1, 0x0B, 0x80, 0xA0, 0xE1, 0x01, 0x90, 0x59, 0xE2,
    0x3C, 0x00, 0x00, 0x3A, 0x26, 0x25, 0xA0, 0xE3, 0x03, 0x27, 0x82, 0xE2, 0xB0, 0x30, 0xD2, 0xE1,
    0x03, 0x38, 0xA0, 0xE1, 0x23, 0x38, 0xA0, 0xE1, 0xFF, 0x30, 0x03, 0xE2, 0x58, 0x00, 0x53, 0xE3,
    0x26, 0x17, 0xA0, 0x13, 0x96, 0x1C, 0x81, 0x12, 0x02, 0x00, 0xA0, 0x11, 0x80, 0x10, 0x81, 0x12,
    0x00, 0x20, 0xA0, 0x13, 0x02, 0x00, 0x00, 0x1A, 0x0D, 0x00, 0x00, 0xEA, 0x01, 0x00, 0x52, 0xE1,
    0x24, 0x00, 0x00, 0x0A, 0xB0, 0x30, 0xD0, 0xE1, 0x03, 0x38, 0xA0, 0xE1, 0x23, 0x38, 0xA0, 0xE1,
    0xFF, 0x30, 0x03, 0xE2, 0x58, 0x00, 0x53, 0xE3, 0x01, 0x20, 0x82, 0xE2, 0xF6, 0xFF, 0xFF, 0x1A,
    0x26, 0x37, 0xA0, 0xE3, 0x96, 0x3C, 0x83, 0xE2, 0x7F, 0x30, 0x83, 0xE2, 0x03, 0x00, 0x52, 0xE1,
    0x18, 0x00, 0x00, 0xCA, 0x01, 0x00, 0x18, 0xE3, 0x0B, 0x10, 0xA0, 0x01, 0xFF, 0x20, 0xA0, 0x03,
    0x09, 0x04, 0xA0, 0x03, 0x0C, 0x00, 0x00, 0x0A, 0x08, 0x10, 0xA0, 0xE1, 0xFF, 0x00, 0xA0, 0xE3,
    0x09, 0xC4, 0xA0, 0xE3, 0x00, 0x30, 0xD1, 0xE5, 0x01, 0x20, 0xD1, 0xE5, 0x01, 0x00, 0x40, 0xE2,
    0x02, 0x34, 0x83, 0xE1, 0x01, 0x00, 0x70, 0xE3, 0xB0, 0x30, 0xCC, 0xE1, 0x02, 0x10, 0x81, 0xE2,
    0xF7, 0xFF, 0xFF, 0x1A, 0x02, 0x8C, 0x88, 0xE2, 0xCF, 0xFF, 0xFF, 0xEA, 0x01, 0x20, 0x42, 0xE2,
    0xB2, 0x30, 0xD1, 0xE0, 0x01, 0x00, 0x72, 0xE3, 0xB0, 0x30, 0xC0, 0xE1, 0xFA, 0xFF, 0xFF, 0x1A,
    0x02, 0xBC, 0x8B, 0xE2, 0xC8, 0xFF, 0xFF, 0xEA, 0x00, 0x00, 0xA0, 0xE3, 0x10, 0xD0, 0x8D, 0xE2,
    0xF0, 0x4B, 0xBD, 0xE8, 0x1E, 0xFF, 0x2F, 0xE1, 0x09, 0x38, 0xA0, 0xE1, 0x23, 0x38, 0xA0, 0xE1,
    0x0C, 0x30, 0x8D, 0xE5, 0x9E, 0xFF, 0xFF, 0xEA, 0x01, 0x00, 0xA0, 0xE3, 0xF6, 0xFF, 0xFF, 0xEA,
    0x04, 0xE0, 0x2D, 0xE5, 0x04, 0xD0, 0x4D, 0xE2, 0xAA, 0xFE, 0xFF, 0xEB, 0x04, 0xD0, 0x8D, 0xE2,
    0x04, 0xE0, 0x9D, 0xE4, 0x1E, 0xFF, 0x2F, 0xE1, 0x0D, 0xC0, 0xA0, 0xE1, 0xF8, 0xDF, 0x2D, 0xE9,
    0x04, 0xB0, 0x4C, 0xE2, 0x28, 0xD0, 0x4B, 0xE2, 0xF0, 0x6F, 0x9D, 0xE8, 0x1E, 0xFF, 0x2F, 0xE1,
    0xDC, 0x00, 0x80, 0xBF, 0x98, 0x00, 0x80, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
}; //unsigned char mpcf_dldi[1876]

bool tryPatch(void *data, size_t size)
{
	// Find the DSDI reserved space in the file
	addr_t patchOffset = quickFind(static_cast<data_t *>(data), dldiMagicString, size, sizeof(dldiMagicString) / sizeof(char));

	// no DLDI section
	if (patchOffset < 0)
		return false;

	data_t *pDH = mpcf_dldi;
	data_t *pAH = static_cast<data_t *>(data) + patchOffset;

	if (pDH[DO_driverSize] > pAH[DO_allocatedSpace])
	{
		printf("Not enough space for patch. Available %d bytes, need %d bytes\n", 1 << pAH[DO_allocatedSpace], 1 << pDH[DO_driverSize]);
		return false;
	}

	if (memcmp(&pAH[DO_friendlyName], "Default (No interface)", 22))
	{
		printf("Would have been a candidate for auto-patch DLDI, but there was already a patch installed.");
		return false;
	}

	//----should be able to patch OK-----

	addr_t memOffset; // Offset of DLDI after the file is loaded into memory
	addr_t relocationOffset; // Value added to all offsets within the patch to fix it properly
	addr_t ddmemOffset; // Original offset used in the DLDI file
	addr_t ddmemStart; // Start of range that offsets can be in the DLDI file
	addr_t ddmemEnd; // End of range that offsets can be in the DLDI file
	addr_t ddmemSize; // Size of range that offsets can be in the DLDI file

	addr_t addrIter;

	memOffset = readAddr(pAH, DO_text_start);
	if (!memOffset)
		memOffset = readAddr(pAH, DO_startup) - DO_code;
	ddmemOffset = readAddr(pDH, DO_text_start);
	relocationOffset = memOffset - ddmemOffset;

	printf("AUTO-PATCHING DLDI to MPCF! Lucky you!\n\n");
	printf("Old driver:          %s\n", &pAH[DO_friendlyName]);
	printf("New driver:          %s\n", &pDH[DO_friendlyName]);
	printf("\n");
	printf("Position in file:    0x%08X\n", patchOffset);
	printf("Position in memory:  0x%08X\n", memOffset);
	printf("Patch base address:  0x%08X\n", ddmemOffset);
	printf("Relocation offset:   0x%08X\n", relocationOffset);
	printf("\n");

	ddmemStart = readAddr(pDH, DO_text_start);
	ddmemSize = 1 << pDH[DO_driverSize];
	ddmemEnd = ddmemStart + ddmemSize;

	// Remember how much space is actually reserved
	pDH[DO_allocatedSpace] = pAH[DO_allocatedSpace];
	// Copy the DLDI patch into the application
	memcpy(pAH, pDH, sizeof(mpcf_dldi));

	// Fix the section pointers in the header
	writeAddr(pAH, DO_text_start, readAddr(pAH, DO_text_start) + relocationOffset);
	writeAddr(pAH, DO_data_end, readAddr(pAH, DO_data_end) + relocationOffset);
	writeAddr(pAH, DO_glue_start, readAddr(pAH, DO_glue_start) + relocationOffset);
	writeAddr(pAH, DO_glue_end, readAddr(pAH, DO_glue_end) + relocationOffset);
	writeAddr(pAH, DO_got_start, readAddr(pAH, DO_got_start) + relocationOffset);
	writeAddr(pAH, DO_got_end, readAddr(pAH, DO_got_end) + relocationOffset);
	writeAddr(pAH, DO_bss_start, readAddr(pAH, DO_bss_start) + relocationOffset);
	writeAddr(pAH, DO_bss_end, readAddr(pAH, DO_bss_end) + relocationOffset);
	// Fix the function pointers in the header
	writeAddr(pAH, DO_startup, readAddr(pAH, DO_startup) + relocationOffset);
	writeAddr(pAH, DO_isInserted, readAddr(pAH, DO_isInserted) + relocationOffset);
	writeAddr(pAH, DO_readSectors, readAddr(pAH, DO_readSectors) + relocationOffset);
	writeAddr(pAH, DO_writeSectors, readAddr(pAH, DO_writeSectors) + relocationOffset);
	writeAddr(pAH, DO_clearStatus, readAddr(pAH, DO_clearStatus) + relocationOffset);
	writeAddr(pAH, DO_shutdown, readAddr(pAH, DO_shutdown) + relocationOffset);

	if (pDH[DO_fixSections] & FIX_ALL)
		// Search through and fix pointers within the data section of the file
		for (addrIter = readAddr(pDH, DO_text_start) - ddmemStart; addrIter < readAddr(pDH, DO_data_end) - ddmemStart; ++addrIter)
			if (ddmemStart <= readAddr(pAH, addrIter) && readAddr(pAH, addrIter) < ddmemEnd)
				writeAddr(pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset);

	if (pDH[DO_fixSections] & FIX_GLUE)
		// Search through and fix pointers within the glue section of the file
		for (addrIter = readAddr(pDH, DO_glue_start) - ddmemStart; addrIter < readAddr(pDH, DO_glue_end) - ddmemStart; ++addrIter)
			if (ddmemStart <= readAddr(pAH, addrIter) && readAddr(pAH, addrIter) < ddmemEnd)
				writeAddr(pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset);

	if (pDH[DO_fixSections] & FIX_GOT)
		// Search through and fix pointers within the Global Offset Table section of the file
		for (addrIter = readAddr(pDH, DO_got_start) - ddmemStart; addrIter < readAddr(pDH, DO_got_end) - ddmemStart; ++addrIter)
			if (ddmemStart <= readAddr(pAH, addrIter) && readAddr(pAH, addrIter) < ddmemEnd)
				writeAddr(pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset);

	if (pDH[DO_fixSections] & FIX_BSS)
		// Initialise the BSS to 0
		memset(&pAH[readAddr(pDH, DO_bss_start) - ddmemStart] , 0, readAddr(pDH, DO_bss_end) - readAddr(pDH, DO_bss_start));

	return true;
}

} //namespace DLDI