/*
 * PSP Software Development Kit - https://github.com/pspdev
 * -----------------------------------------------------------------------
 * Licensed under the BSD license, see LICENSE in PSPSDK root for details.
 *
 * psp-prxgen.c - Simple program to build a PRX file (and strip at the same time)
 *
 * Copyright (c) 2005 James Forshaw <tyranid@gmail.com>
 *
 */

#include <stdio.h>
#include <getopt.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <ctype.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "types.h"
#include "elftypes.h"
#include "prxtypes.h"

/* Arrangement of ELF file after stripping
 *
 * ELF Header - 52 bytes
 * Program Headers 
 * .text data
 * .data data
 * Section Headers
 * Relocation data
 * Section Header String Table
 *
 * When stripping the sections remove anything which isn't an allocated section or a relocation section.
 * The section string section we will rebuild.
 */

static const char *g_outfile;
static const char *g_infile;
static unsigned char *g_elfdata = NULL;
static struct ElfHeader g_elfhead = {0};
static struct ElfSection *g_elfsections = NULL;
static struct ElfSection *g_modinfo = NULL;
static int g_out_sects = 2;
static int g_alloc_size = 0;
static int g_mem_size = 0;
static int g_reloc_size = 0;
static int g_str_size = 1;

/* Base addresses in the Elf */
static int g_phbase = 0;
static int g_allocbase = 0;
static int g_shbase = 0;
static int g_relocbase = 0;
static int g_shstrbase = 0;

/* Specifies that the current usage is to the print the pspsdk path */
static int g_verbose = 0;

static struct option arg_opts[] = 
{
	{"verbose", no_argument, NULL, 'v'},
	{ NULL, 0, NULL, 0 }
};

/* Process the arguments */
int process_args(int argc, char **argv)
{
	int ch;

	g_outfile = NULL;
	g_infile = NULL;

	ch = getopt_long(argc, argv, "v", arg_opts, NULL);
	while(ch != -1)
	{
		switch(ch)
		{
			case 'v' : g_verbose = 1;
					   break;
			default  : break;
		};

		ch = getopt_long(argc, argv, "v", arg_opts, NULL);
	}

	argc -= optind;
	argv += optind;

	if(argc < 2)
	{
		return 0;
	}

	g_infile = argv[0];
	g_outfile = argv[1];

	if(g_verbose)
	{
		fprintf(stderr, "Loading %s, outputting to %s\n", g_infile, g_outfile);
	}

	return 1;
}

void print_help(void)
{
	fprintf(stderr, "Usage: psp-prxgen [-v] infile.elf outfile.prx\n");
	fprintf(stderr, "Options:\n");
	fprintf(stderr, "-v, --verbose           : Verbose output\n");
}

unsigned char *load_file(const char *file)
{
	FILE *fp;
	unsigned int size;
	unsigned char *data = NULL;

	do
	{
		fp = fopen(file, "rb");
		if(fp != NULL)
		{
			(void) fseek(fp, 0, SEEK_END);
			size = ftell(fp);
			rewind(fp);

			if(size < sizeof(Elf32_Ehdr))
			{
				fprintf(stderr, "Error, invalid file size\n");
				fclose(fp);
				break;
			}

			data = (unsigned char *) malloc(size);
			if(data == NULL)
			{
				fprintf(stderr, "Error, could not allocate memory for ELF\n");
				fclose(fp);
				break;
			}

			size_t result = fread(data, 1, size, fp);
			if (result < 0) {
				fprintf(stderr, "Error, could not read the ELF\n");
				fclose(fp);
				break;
			}
			fclose(fp);
		}
		else
		{
			fprintf(stderr, "Error, could not find file %s\n", file);
		}
	}
	while(0);

	return data;
}

/* Validate the ELF header */
int validate_header(unsigned char *data)
{
	Elf32_Ehdr *head;
	int ret = 0;

	head = (Elf32_Ehdr*) data;

	do
	{
		/* Read in the header structure */
		g_elfhead.iMagic = LW(head->e_magic);
		g_elfhead.iClass = head->e_class;
		g_elfhead.iData = head->e_data;
		g_elfhead.iIdver = head->e_idver;
		g_elfhead.iType = LH(head->e_type);
		g_elfhead.iMachine = LH(head->e_machine);
		g_elfhead.iVersion = LW(head->e_version);
		g_elfhead.iEntry = LW(head->e_entry);
		g_elfhead.iPhoff = LW(head->e_phoff);
		g_elfhead.iShoff = LW(head->e_shoff);
		g_elfhead.iFlags = LW(head->e_flags);
		g_elfhead.iEhsize = LH(head->e_ehsize);
		g_elfhead.iPhentsize = LH(head->e_phentsize);
		g_elfhead.iPhnum = LH(head->e_phnum);
		g_elfhead.iShentsize = LH(head->e_shentsize);
		g_elfhead.iShnum = LH(head->e_shnum);
		g_elfhead.iShstrndx = LH(head->e_shstrndx);

		if(g_verbose)
		{
			fprintf(stderr, "Magic %08X, Class %02X, Data %02X, Idver %02X\n", g_elfhead.iMagic,
					g_elfhead.iClass, g_elfhead.iData, g_elfhead.iIdver);
			fprintf(stderr, "Type %04X, Machine %04X, Version %08X, Entry %08X\n", g_elfhead.iType,
					g_elfhead.iMachine, g_elfhead.iVersion, g_elfhead.iEntry);
			fprintf(stderr, "Phoff %08X, Shoff %08X, Flags %08X, Ehsize %08X\n", g_elfhead.iPhoff,
					g_elfhead.iShoff, g_elfhead.iFlags, g_elfhead.iEhsize);
			fprintf(stderr, "Phentsize %04X, Phnum %04X\n", g_elfhead.iPhentsize, g_elfhead.iPhnum);
			fprintf(stderr, "Shentsize %04X, Shnum %08X, Shstrndx %04X\n", g_elfhead.iShentsize,
					g_elfhead.iShnum, g_elfhead.iShstrndx);
		}

		if(g_elfhead.iMagic != ELF_MAGIC)
		{
			fprintf(stderr, "Error, invalid magic in the header\n");
			break;
		}

		if((g_elfhead.iType != ELF_EXEC_TYPE) && (g_elfhead.iType != ELF_PRX_TYPE))
		{
			fprintf(stderr, "Error, not EXEC type elf\n");
			break;
		}

		if(g_elfhead.iMachine != ELF_MACHINE_MIPS)
		{
			fprintf(stderr, "Error, not MIPS type ELF\n");
			break;
		}

		if(g_elfhead.iShnum < g_elfhead.iShstrndx)
		{
			fprintf(stderr, "Error, number of headers is less than section string index\n");
			break;
		}

		ret = 1;
	}
	while(0);

	return ret;
}

/* Load sections into ram */
int load_sections(unsigned char *data)
{
	int ret = 0;
	int found_rel = 0;
	unsigned int load_addr = 0xFFFFFFFF;

	if(g_elfhead.iShnum > 0)
	{
		do
		{
			Elf32_Shdr *sect;
			int i;

			g_elfsections = (struct ElfSection *) malloc(sizeof(struct ElfSection) * g_elfhead.iShnum);
			if(g_elfsections == NULL)
			{
				fprintf(stderr, "Error, could not allocate memory for sections\n");
				break;
			}

			memset(g_elfsections, 0, sizeof(struct ElfSection) * g_elfhead.iShnum);

			for(i = 0; i < g_elfhead.iShnum; i++)
			{
				sect = (Elf32_Shdr *) (g_elfdata + g_elfhead.iShoff + (i * g_elfhead.iShentsize));

				g_elfsections[i].iName = LW(sect->sh_name);
				g_elfsections[i].iType = LW(sect->sh_type);
				g_elfsections[i].iAddr = LW(sect->sh_addr);
				g_elfsections[i].iFlags = LW(sect->sh_flags);
				g_elfsections[i].iOffset = LW(sect->sh_offset);
				g_elfsections[i].iSize = LW(sect->sh_size);
				g_elfsections[i].iLink = LW(sect->sh_link);
				g_elfsections[i].iInfo = LW(sect->sh_info);
				g_elfsections[i].iAddralign = LW(sect->sh_addralign);
				g_elfsections[i].iEntsize = LW(sect->sh_entsize);
				g_elfsections[i].iIndex = i;

				if(g_elfsections[i].iOffset != 0)
				{
					g_elfsections[i].pData = g_elfdata + g_elfsections[i].iOffset;
				}

				if(g_elfsections[i].iFlags & SHF_ALLOC)
				{
					g_elfsections[i].blOutput = 1;
					if(g_elfsections[i].iAddr < load_addr)
					{
						load_addr = g_elfsections[i].iAddr;
					}
				}
			}

			/* Okay so we have loaded all the sections, lets find relocations and fix up the names */
			for(i = 0; i < g_elfhead.iShnum; i++)
			{
				if(((g_elfsections[i].iType == SHT_REL) || (g_elfsections[i].iType == SHT_PRXRELOC)) 
						&& (g_elfsections[g_elfsections[i].iInfo].iFlags & SHF_ALLOC))
				{
					g_elfsections[i].pRef = &g_elfsections[g_elfsections[i].iInfo];
					found_rel = 1;
					g_elfsections[i].blOutput = 1;
				}

				strcpy(g_elfsections[i].szName, (char *) (g_elfsections[g_elfhead.iShstrndx].pData + g_elfsections[i].iName));
				if(strcmp(g_elfsections[i].szName, PSP_MODULE_INFO_NAME) == 0)
				{
					g_modinfo = &g_elfsections[i];
				}
				else if(strcmp(g_elfsections[i].szName, PSP_MODULE_REMOVE_REL) == 0)
				{
					/* Don't output .rel.lib.stub relocations */
					g_elfsections[i].blOutput = 0;
				}
			}

			if(g_verbose)
			{
				for(i = 0; i < g_elfhead.iShnum; i++)
				{
					fprintf(stderr, "\nSection %d: %s\n", i, g_elfsections[i].szName);
					fprintf(stderr, "Name %08X, Type %08X, Flags %08X, Addr %08X\n", 
							g_elfsections[i].iName, g_elfsections[i].iType,
							g_elfsections[i].iFlags, g_elfsections[i].iAddr);
					fprintf(stderr, "Offset %08X, Size %08X, Link %08X, Info %08X\n", 
							g_elfsections[i].iOffset, g_elfsections[i].iSize,
							g_elfsections[i].iLink, g_elfsections[i].iInfo);
					fprintf(stderr, "Addralign %08X, Entsize %08X pData %p\n", 
							g_elfsections[i].iAddralign, g_elfsections[i].iEntsize,
							g_elfsections[i].pData);
				}

				fprintf(stderr, "ELF Load Base address %08X\n", load_addr);
			}

			if(g_modinfo == NULL)
			{
				fprintf(stderr, "Error, no sceModuleInfo section found\n");
				break;
			}

			if(!found_rel)
			{
				fprintf(stderr, "Error, found no relocation sections\n");
				break;
			}

			if(load_addr != 0)
			{
				fprintf(stderr, "Error, ELF not loaded to address 0 (%08X)\n", load_addr);
				break;
			}

			ret = 1;
		}
		while(0);
	}
	else
	{
		fprintf(stderr, "Error, no sections in the ELF\n");
	}

	return ret;
}

int remove_weak_relocs(struct ElfSection *pReloc, struct ElfSection *pSymbol, struct ElfSection *pString)
{
	int iCount;
	int iMaxSymbol;
	void *pNewRel = NULL;
	Elf32_Rel *pInRel;
	Elf32_Rel *pOutRel;
	Elf32_Sym *pSymData = (Elf32_Sym *) pSymbol->pData;
	char *pStrData = NULL;
	int iOutput;
	int i;

	if(pString != NULL)
	{
		pStrData = (char *) pString->pData;
	}

	iMaxSymbol = pSymbol->iSize / sizeof(Elf32_Sym);
	iCount = pReloc->iSize / sizeof(Elf32_Rel);

	pNewRel = malloc(pReloc->iSize);
	if(pNewRel == NULL)
	{
		return 0;
	}
	pOutRel = (Elf32_Rel *) pNewRel;
	pInRel = (Elf32_Rel *) pReloc->pData;
	iOutput = 0;

	if(g_verbose)
	{
		fprintf(stderr, "[%s] Processing %d relocations, %d symbols\n", pReloc->szName, iCount, iMaxSymbol);
	}

	for(i = 0; i < iCount; i++)
	{
		int iSymbol;

		iSymbol = ELF32_R_SYM(LW(pInRel->r_info));
		if(g_verbose)
		{
			fprintf(stderr, "Relocation %d - Symbol %x\n", iOutput, iSymbol);
		}

		if(iSymbol >= iMaxSymbol)
		{
			fprintf(stderr, "Warning: Ignoring relocation as cannot find matching symbol\n");
		}
		else
		{
			if(g_verbose)
			{
				if(pStrData != NULL)
				{
					fprintf(stderr, "Symbol %d - Name %s info %x ndx %x\n", iSymbol, &pStrData[pSymData[iSymbol].st_name], 
							pSymData[iSymbol].st_info, pSymData[iSymbol].st_shndx);
				}
				else
				{
					fprintf(stderr, "Symbol %d - Name %d info %x ndx %x\n", iSymbol, pSymData[iSymbol].st_name, 
							pSymData[iSymbol].st_info, pSymData[iSymbol].st_shndx);
				}
			}

			/* Remove PC16 relocations (unsupported by PSP, and useless) */
			if(LH(pSymData[iSymbol].st_shndx) == 0 || ELF32_R_TYPE(LW(pInRel->r_info)) == R_MIPS_PC16 || ELF32_R_TYPE(LW(pInRel->r_info)) == R_MIPS_GPREL16)
			{
				if(g_verbose)
				{
					fprintf(stderr, "Deleting relocation\n");
				}
			}
			else
			{
				/* We are keeping this relocation, copy it across */
				*pOutRel = *pInRel;
				pOutRel++;
				iOutput++;
			}
		}

		pInRel++;
	}

	/* If we deleted some relocations */
	if(iOutput < iCount)
	{
		int iSize;

		iSize = iOutput * sizeof(Elf32_Rel);
		if(g_verbose)
		{
			fprintf(stderr, "Old relocation size %d, new %d\n", pReloc->iSize, iSize);
		}
		pReloc->iSize = iSize;
		/* If size is zero then delete this section */
		if(iSize == 0)
		{
			pReloc->blOutput = 0;
		}
		else
		{
			/* Copy across the new relocation data */
			memcpy(pReloc->pData, pNewRel, pReloc->iSize);
		}
	}

	free(pNewRel);

	return 1;
}

/* Let's remove the weak relocations from the list */
int process_relocs(void)
{
	int i;

	for(i = 0; i < g_elfhead.iShnum; i++)
	{
		if((g_elfsections[i].blOutput) && (g_elfsections[i].iType == SHT_REL))
		{
			struct ElfSection *pReloc;

			pReloc = &g_elfsections[i];
			if((pReloc->iLink < g_elfhead.iShnum) && (g_elfsections[pReloc->iLink].iType == SHT_SYMTAB))
			{
				struct ElfSection *pStrings = NULL;
				struct ElfSection *pSymbols;

				pSymbols = &g_elfsections[pReloc->iLink];
				if((pSymbols->iLink < g_elfhead.iShnum) && (g_elfsections[pSymbols->iLink].iType == SHT_STRTAB))
				{
					pStrings = &g_elfsections[pSymbols->iLink];
				}

				if(!remove_weak_relocs(pReloc, pSymbols, pStrings))
				{
					return 0;
				}
			}
			else
			{
				if(g_verbose)
				{
					fprintf(stderr, "Ignoring relocation section %d, invalid link number\n", i);
				}
			}
		}
	}

	return 1;
}

/* Reindex the sections we are keeping */
void reindex_sections(void)
{
	int i;
	int sect = 1;

	for(i = 0; i < g_elfhead.iShnum; i++)
	{
		if(g_elfsections[i].blOutput)
		{
			g_elfsections[i].iIndex = sect++;
		}
	}
}

/* Load an ELF file */
int load_elf(const char *elf)
{
	int ret = 0;

	do
	{
		g_elfdata = load_file(elf);
		if(g_elfdata == NULL)
		{
			break;
		}

		if(!validate_header(g_elfdata))
		{
			break;
		}

		if(!load_sections(g_elfdata))
		{
			break;
		}

		if(!process_relocs())
		{
			break;
		}

		reindex_sections();

		ret = 1;
	}
	while(0);

	return ret;
}

int calculate_outsize(void)
{
	/* out_sects starts at two for the null section and the section string table */
	int out_sects = 2;
	int alloc_size = 0;
	int reloc_size = 0;
	int mem_size = 0;
	/* 1 for the NUL for the NULL section */
	int str_size = 1;
	int i;

	/* Calculate how big our output file needs to be */
	/* We have elf header + 1 PH + allocated data + section headers + relocation data */

	/* Note that the ELF should be based from 0, we use this to calculate the alloc and mem sizes */

	/* Skip null section */
	for(i = 1; i < g_elfhead.iShnum; i++)
	{
		if(g_elfsections[i].blOutput)
		{
			if(g_elfsections[i].iType == SHT_PROGBITS)
			{
				unsigned int top_addr;

				top_addr = g_elfsections[i].iAddr + g_elfsections[i].iSize;

				if(top_addr > alloc_size)
				{
					alloc_size = top_addr;
				}

				if(top_addr > mem_size)
				{
					mem_size = top_addr;
				}
				
				str_size += strlen(g_elfsections[i].szName) + 1;
			}
			else if((g_elfsections[i].iType == SHT_REL) || (g_elfsections[i].iType == SHT_PRXRELOC))
			{
				reloc_size += g_elfsections[i].iSize;
				str_size += strlen(g_elfsections[i].szName) + 1;
			}
			else
			{
				unsigned int top_addr;

				top_addr = g_elfsections[i].iAddr + g_elfsections[i].iSize;

				if(top_addr > mem_size)
				{
					mem_size = top_addr;
				}
				
				str_size += strlen(g_elfsections[i].szName) + 1;
			}
			out_sects++;
		}
	}

	alloc_size = (alloc_size + 3) & ~3;
	mem_size = (mem_size + 3) & ~3;
	str_size = (str_size + 3) & ~3;
	str_size += strlen(ELF_SH_STRTAB) + 1;

	if(g_verbose)
	{
		fprintf(stderr, "Out_sects %d, alloc_size %d, reloc_size %d, str_size %d, mem_size %d\n", 
				out_sects, alloc_size, reloc_size, str_size, mem_size);
	}

	/* Save them for future use */
	g_out_sects = out_sects;
	g_alloc_size = alloc_size;
	g_reloc_size = reloc_size;
	g_mem_size = mem_size;
	g_str_size = str_size;

	/* Lets build the offsets */
	g_phbase = sizeof(Elf32_Ehdr);
	/* The allocated data needs to be 16 byte aligned */
	g_allocbase = (g_phbase + sizeof(Elf32_Shdr) + 0xF) & ~0xF;
	g_shbase = g_allocbase + g_alloc_size;
	g_relocbase = g_shbase + (g_out_sects * sizeof(Elf32_Shdr));
	g_shstrbase = g_relocbase + g_reloc_size;

	if(g_verbose)
	{
		fprintf(stderr, "PHBase %08X, AllocBase %08X, SHBase %08X\n", g_phbase, g_allocbase, g_shbase);
		fprintf(stderr, "Relocbase %08X, Shstrbase %08X\n", g_relocbase, g_shstrbase);
		fprintf(stderr, "Total size %d\n", g_shstrbase + g_str_size);
	}

	return (g_shstrbase + g_str_size);
}

/* Output the ELF header */
void output_header(unsigned char *data)
{
	Elf32_Ehdr *head;

	head = (Elf32_Ehdr*) data;

	SW(&head->e_magic, g_elfhead.iMagic);
	head->e_class = g_elfhead.iClass;
	head->e_data = g_elfhead.iData;
	head->e_idver = g_elfhead.iIdver;
	SH(&head->e_type, ELF_PRX_TYPE);
	SH(&head->e_machine, g_elfhead.iMachine);
	SW(&head->e_version, g_elfhead.iVersion);
	SW(&head->e_entry, g_elfhead.iEntry);
	SW(&head->e_phoff, g_phbase);
	SW(&head->e_shoff, g_shbase);
	SW(&head->e_flags, g_elfhead.iFlags);
	SH(&head->e_ehsize, sizeof(Elf32_Ehdr));
	SH(&head->e_phentsize, sizeof(Elf32_Phdr));
	SH(&head->e_phnum, 1);
	SH(&head->e_shentsize, sizeof(Elf32_Shdr));
	SH(&head->e_shnum, g_out_sects);
	SH(&head->e_shstrndx, g_out_sects-1);
}

/* Output the program header */
void output_ph(unsigned char *data)
{
	Elf32_Phdr *phdr;
	struct PspModuleInfo *pModinfo;
	int mod_flags;

	phdr = (Elf32_Phdr*) data;
	pModinfo = (struct PspModuleInfo *) (g_modinfo->pData);
	mod_flags = LW(pModinfo->flags);

	SW(&phdr->p_type, 1);
	/* Starts after the program header */
	SW(&phdr->p_offset, g_allocbase);
	SW(&phdr->p_vaddr, 0);

	/* Check if this is a kernel module */
	if(mod_flags & 0x1000)
	{
		SW(&phdr->p_paddr, 0x80000000 | (g_modinfo->iAddr + g_allocbase));
	}
	else
	{
		SW(&phdr->p_paddr, (g_modinfo->iAddr + g_allocbase));
	}
	SW(&phdr->p_filesz, g_alloc_size);
	SW(&phdr->p_memsz, g_mem_size);
	SW(&phdr->p_flags, 5);
	SW(&phdr->p_align, 0x10);
}

/* Output the allocated sections */
void output_alloc(unsigned char *data)
{
	int i;

	for(i = 0; i < g_elfhead.iShnum; i++)
	{
		if((g_elfsections[i].blOutput) && (g_elfsections[i].iType == SHT_PROGBITS))
		{
			memcpy(&data[g_elfsections[i].iAddr], g_elfsections[i].pData, g_elfsections[i].iSize);
		}
	}
}

/* Output the section headers */
void output_sh(unsigned char *data)
{
	unsigned int reloc_ofs;
	unsigned int str_ofs;
	Elf32_Shdr *shdr;
	int i;

	shdr = (Elf32_Shdr*) data;
	/* For the NULL section */
	shdr++;
	memset(data, 0, g_out_sects * sizeof(Elf32_Shdr));

	reloc_ofs = g_relocbase;
	str_ofs = 1;

	for(i = 1; i < g_elfhead.iShnum; i++)
	{
		if(g_elfsections[i].blOutput)
		{
			SW(&shdr->sh_name, str_ofs);
			str_ofs += strlen(g_elfsections[i].szName) + 1;
			SW(&shdr->sh_flags, g_elfsections[i].iFlags);
			SW(&shdr->sh_addr, g_elfsections[i].iAddr);
			SW(&shdr->sh_size, g_elfsections[i].iSize);
			SW(&shdr->sh_link, 0);
			SW(&shdr->sh_addralign, g_elfsections[i].iAddralign);
			SW(&shdr->sh_entsize, g_elfsections[i].iEntsize);

			if((g_elfsections[i].iType == SHT_REL) || (g_elfsections[i].iType == SHT_PRXRELOC)) 
			{
				SW(&shdr->sh_type, SHT_PRXRELOC);
				if (g_elfsections[i].pRef)
					SW(&shdr->sh_info, g_elfsections[i].pRef->iIndex);
				else
					SW(&shdr->sh_info, 0);
				SW(&shdr->sh_offset, reloc_ofs);
				reloc_ofs += g_elfsections[i].iSize;
			}
			else if(g_elfsections[i].iType == SHT_PROGBITS)
			{
				SW(&shdr->sh_type, g_elfsections[i].iType);
				SW(&shdr->sh_info, 0);
				SW(&shdr->sh_offset, g_allocbase + g_elfsections[i].iAddr);
			}
			else
			{
				SW(&shdr->sh_type, g_elfsections[i].iType);
				SW(&shdr->sh_info, 0);
				/* Point it to the end of the allocated section */
				SW(&shdr->sh_offset, g_allocbase + g_alloc_size);
			}

			shdr++;
		}
	}

	/* Fill in the shstrtab section */
	SW(&shdr->sh_name, str_ofs);
	SW(&shdr->sh_flags, 0);
	SW(&shdr->sh_addr, 0);
	SW(&shdr->sh_size, g_str_size);
	SW(&shdr->sh_link, 0);
	SW(&shdr->sh_addralign, 1);
	SW(&shdr->sh_entsize, 0);
	SW(&shdr->sh_type, SHT_STRTAB);
	SW(&shdr->sh_info, 0);
	SW(&shdr->sh_offset, g_shstrbase);
}

/* Output relocations */
void output_relocs(unsigned char *data)
{
	int i;
	unsigned char *pReloc;

	pReloc = data;

	for(i = 0; i < g_elfhead.iShnum; i++)
	{
		if((g_elfsections[i].blOutput) && 
				((g_elfsections[i].iType == SHT_REL) || (g_elfsections[i].iType == SHT_PRXRELOC)))
		{
			Elf32_Rel *rel;
			int j, count;

			memcpy(pReloc, g_elfsections[i].pData, g_elfsections[i].iSize);
			rel = (Elf32_Rel*) pReloc;
			count = g_elfsections[i].iSize / sizeof(Elf32_Rel);
			for(j = 0; j < count; j++)
			{
				unsigned int sym;

				/* Clear the top 24bits of the info */
				/* Kind of a dirty trick but hey :P */
				sym = LW(rel->r_info);
				sym &= 0xFF;
				SW(&rel->r_info, sym);
				rel++;
			}
			pReloc += g_elfsections[i].iSize;
		}
	}
}

/* Output the section header string table */
void output_shstrtab(unsigned char *data)
{
	int i;
	char *pData;

	/* For the NULL section, memory should be zeroed anyway */
	memset(data, 0, g_str_size);
	pData = (char *) (data + 1);

	for(i = 1; i < g_elfhead.iShnum; i++)
	{
		if(g_elfsections[i].blOutput)
		{
			if(g_verbose)
			{
				fprintf(stderr, "String %d: %s\n", i, g_elfsections[i].szName);
			}

			strcpy(pData, g_elfsections[i].szName);
			pData += strlen(g_elfsections[i].szName) + 1;
		}
	}

	strcpy(pData, ELF_SH_STRTAB);
}

/* Output a stripped prx file */
int output_prx(const char *prxfile)
{
	int size;
	unsigned char *data;
	FILE *fp;

	do
	{
		size = calculate_outsize();
		data = (unsigned char *) malloc(size);
		if(data == NULL)
		{
			fprintf(stderr, "Error, couldn't allocate output data\n");
			break;
		}

		memset(data, 0, size);

		output_header(data);
		output_ph(data + g_phbase);
		output_alloc(data + g_allocbase);
		output_sh(data + g_shbase);
		output_relocs(data + g_relocbase);
		output_shstrtab(data + g_shstrbase);

		fp = fopen(prxfile, "wb");
		if(fp != NULL)
		{
			fwrite(data, 1, size, fp);
			fclose(fp);
		}
		else
		{
			fprintf(stderr, "Error, could not open output file %s\n", prxfile);
		}

		free(data);
	}
	while(0);

	return 0;
}

/* Free allocated memory */
void free_data(void)
{
	if(g_elfdata != NULL)
	{
		free(g_elfdata);
		g_elfdata = NULL;
	}

	if(g_elfsections != NULL)
	{
		free(g_elfsections);
		g_elfsections = NULL;
	}
}

int main(int argc, char **argv)
{
	if(process_args(argc, argv))
	{
		if(load_elf(g_infile))
		{
			(void) output_prx(g_outfile);
			free_data();
		}
	}
	else
	{
		print_help();
	}

	return 0;
}
