//////////////////////////////////////////////////////////////////////////////
// bin2o.cpp                                                                //
//////////////////////////////////////////////////////////////////////////////
/*
	Binary to Object converter for GCC

	History:
		*TODO: v1.04 -
			filesize export

		v1.04 - 30 mar 2002
			no-thumb-interworking flag

		v1.03 - 8 feb 2002
			added size header option '-s' (requested by myself)

		v1.02 - 11 oct 2001
			now every character not in 'A'-'Z','a'-'z','0'-'9' range is converted to '_'

		v1.01 - 8 oct 2001
			added '~' option

		v1.00
			initial release
*/

//////////////////////////////////////////////////////////////////////////////
// Includes                                                                 //
//////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <string.h>

//////////////////////////////////////////////////////////////////////////////
// Defines                                                                  //
//////////////////////////////////////////////////////////////////////////////
#define VER		"v1.04"
#define PATCH(block, offset, value1, value2)	\
	((*(int *)(block + offset)) = value1 + value2)

#define EF_INTERWORK	0x04
#define EM_ARM			40

//////////////////////////////////////////////////////////////////////////////
// Variables                                                                //
//////////////////////////////////////////////////////////////////////////////
/*// ELF Header (32-bit implementations)
struct Elf32_External_Ehdr
{
	unsigned char e_ident[16];            // ELF "magic number"
	unsigned char e_type[2];              // Identifies object file type
	unsigned char e_machine[2];           // Specifies required architecture
	unsigned char e_version[4];           // Identifies object file version
	unsigned char e_entry[4];             // Entry point virtual address
	unsigned char e_phoff[4];             // Program header table file offset
	unsigned char e_shoff[4];             // Section header table file offset
	unsigned char e_flags[4];             // Processor-specific flags
	unsigned char e_ehsize[2];            // ELF header size in bytes
	unsigned char e_phentsize[2];         // Program header table entry size
	unsigned char e_phnum[2];             // Program header table entry count
	unsigned char e_shentsize[2];         // Section header table entry size
	unsigned char e_shnum[2];             // Section header table entry count
	unsigned char e_shstrndx[2];          // Section header string table index
};*/

// 52 bytes?

unsigned char header1[] =
{
	0x7F,0x45,0x4C,0x46,0x01,0x01,0x01,0x61,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	// magic
	0x01,0x00,				// type
	0x28,0x00,				// machine
	0x01,0x00,0x00,0x00,	// version
	0x00,0x00,0x00,0x00,	// entry
	0x00,0x00,0x00,0x00,	// phoff
	0xA8,0x00,0x00,0x00,	// shoff
	0x04,0x00,0x00,0x00,	// flags (default thumb-interworking)
	0x34,0x00,				// ehsize
	0x00,0x00,				// phentsize
	0x00,0x00,				// phnum
	0x28,0x00,				// shentsize
	0x08,0x00,				// shnum
	0x05,0x00,				// shstrndx
};

unsigned char trailer1[] =	// uhmm.. whatever
{
	0x00,0x2E,0x73,0x79,0x6D,0x74,0x61,0x62,0x00,0x2E,0x73,0x74,0x72,0x74,0x61,0x62,
	0x00,0x2E,0x73,0x68,0x73,0x74,0x72,0x74,0x61,0x62,0x00,0x2E,0x74,0x65,0x78,0x74,
	0x00,0x2E,0x64,0x61,0x74,0x61,0x00,0x2E,0x62,0x73,0x73,0x00,0x2E,0x72,0x6F,0x64,
	0x61,0x74,0x61,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1B,0x00,0x00,0x00,
	0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x34,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x27,0x00,0x00,0x00,
	0x08,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x34,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x2C,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x00,0x00,0x00,
	0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xB4,0x00,0x00,0x00,
	0x34,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x28,0x02,0x00,0x00,0x70,0x00,0x00,0x00,0x07,0x00,0x00,0x00,
	0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,
	0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x98,0x02,0x00,0x00,
	0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x04,0x00,0xF1,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x03,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x03,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x03,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,
	0x11,0x00,0x04,0x00,0x00,'b','i','n','2','o','.','c',0x00,
};

unsigned char *header = header1;
unsigned char *trailer = trailer1;

//////////////////////////////////////////////////////////////////////////////
// strc                                                                     //
//////////////////////////////////////////////////////////////////////////////
// replaces non-identifier characters with underscores
void strc(char *s)
{
	while (*s)
	{
		if (!(((*s >= 'a') && (*s <= 'z')) || ((*s >= 'A') && (*s <= 'Z')) || ((*s >= '0') && (*s <= '9')))) *s = '_';
		s++;
	}
}

//////////////////////////////////////////////////////////////////////////////
// main                                                                     //
//////////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
	// syntax
	if (argc < 2)
	{
		printf("bin2o for GCC "VER" by Rafael Vuijk (aka Dark Fader)\n");
		printf("Syntax:\n");
		printf("  bin2o [-flags] <input.bin> <output.o> [[@|~|!]<prefix/identifier>]\n");
		printf("\n");
		printf("Flags:\n");
		printf("  -n            no thumb interworking\n");
		printf("\n");
		printf("Prefix parameter and identifier output: (for input.bin)\n");
		printf("                _binary_input_start\n");
		printf("  \"\"            input\n");
		printf("  @             input\n");
		printf("  blah_         blah_input\n");
		printf("  @blah_        blah_input\n");
		printf("  ~             input_bin\n");
		printf("  ~blah_        blah_input_bin\n");
		printf("  !identifier   identifier\n");
		printf("\n");
		return -1;
	}

	char *arg_input = 0;//argv[1];	// input
	char *arg_output = 0;//argv[2];	// output
	char *arg_prefix = 0;//argv[3];	// prefix

	for (int i=1; i<argc; i++)
	{
		if (argv[i][0] == '-')
		{
			for (char *c=argv[i]+1; *c; c++) switch (*c)
			{
				case 'n':	// no thumb-interworking
				{
					header[36] &=~ EF_INTERWORK;
					break;
				}
				default: { fprintf(stderr, "Warning: Ignoring option: '%c'!\n", *c); }
			}
		}
		else
		{
			if (!arg_input) arg_input = argv[i];
			else if (!arg_output) arg_output = argv[i];
			else if (!arg_prefix) arg_prefix = argv[i];
			else { fprintf(stderr, "Error: Redundant options found!\n"); return -1; }
		}
	}

	// check file/prefix args
	if (!arg_input) { fprintf(stderr, "Error: Need input filename!\n"); return -1; }
	if (!arg_output) { fprintf(stderr, "Error: Need output filename!\n"); return -1; }
	//if (!arg_prefix) { fprintf(stderr, "Error: Need prefix!\n"); return -1; }

	// open files
	FILE *fi = fopen(arg_input, "rb");
	if (!fi) { printf("Error opening input file!\n"); return -1; }
	FILE *fo = fopen(arg_output, "wb");
	if (!fo) { printf("Error opening output file!\n"); return -1; }

	// size
	fseek(fi, 0, SEEK_END);
	int size = (ftell(fi)+3) &~ 3;		// align
	fseek(fi, 0, SEEK_SET);

	// identifier
	bool gcc_prefix = true;
	bool full_name = true;		// include extension (default for gcc like prefix)
	// bool lowercase = false;

	char identifier[256] = "";
	char own_prefix[256] = "";
	if (arg_prefix)
	{
		gcc_prefix = false;				// now it's custom
		if (arg_prefix[0] == '!')			// fixed identifier
		{
			strcpy(identifier, &arg_prefix[1]);
		}
		else if (arg_prefix[0] == '@')		// prefix without extension
		{
			full_name = false;			// without extension
			strcpy(own_prefix, &arg_prefix[1]);
		}
		else if (arg_prefix[0] == '~')		// prefix including extension
		{
			full_name = true;			// including extension
			strcpy(own_prefix, &arg_prefix[1]);
		}
		else							// default
		{
			full_name = false;			// without extension
			strcpy(own_prefix, &arg_prefix[0]);
		}
	}

	// generate id from filename?
	if (!identifier[0])
	{
		char s[256]; strcpy(s, arg_input);
		char *p = strrchr(s, '\\');
		if (!p) p = strrchr(s, '/');	// remove directory
		if (p) p++; else p = s;

		if (full_name)
		{
			// including extension
		}
		else
		{
			// remove extension
			if (strchr(p, '.')) strcpy(strrchr(p, '.'), "");
		}
		strc(p);	// convert non-identifier characters
		// if (lowercase) strlwr(p);	// convert to lowercase?

		strcpy(identifier, own_prefix);	// can be empty ofcourse
		if (gcc_prefix) strcat(identifier, "_binary_");
		strcat(identifier, p);
		if (gcc_prefix) strcat(identifier, "_start");
	}
	int idsize = strlen(identifier);

	// patch
	int patch_nr = 0;
	switch (patch_nr)
	{
		case 0:	// simple
		{
			// array was 64 bytes
			PATCH(header, 0x20, size, 0xA8 - 64);
			PATCH(trailer, 0xE8, size, 0x40 - 64);
			PATCH(trailer, 0x10C, size, 0x74 - 64);
			PATCH(trailer, 0x134, size, 0x1E8 - 64);
			PATCH(trailer, 0x15C, size, 0x258 - 64);
			PATCH(trailer, 0x1DC, size, 0x40 - 64);

			// "_binary_blaat_bin_start" -> 23
			PATCH(trailer, 0x160, idsize, 0x21 - 23);
			break;
		}

		case 1:
		{
			break;
		}
	}

	// write data
	fwrite(header, sizeof(header1), 1, fo);
	unsigned char *buf = new unsigned char[size];
	if (size >= 3) { buf[size-1] = 0; buf[size-2] = 0; buf[size-3] = 0; }
	fread(buf, size, 1, fi);
	fwrite(buf, size, 1, fo);
	delete buf;
	fwrite(trailer, sizeof(trailer1), 1, fo);
	fprintf(fo, "%s", identifier);
	fputc(0, fo);

	// close files
	fclose(fi);
	fclose(fo);

	return 0;
}
