/* 
	implementation of elf parsing
*/

#if defined(PS3)

#define eCryModule eCryM_Launcher
#include <CryModuleDefs.h>
#include <platform.h>
#include "SPULoaderDefs.h"
#include "Elf.h"

const NSPU::NElf::EParseResult NSPU::NElf::ParseElf
(
	void* __restrict pSpuElf, 
	NSPU::NElf::SElfInfo*& __restrict rpElfInfo
)
{
	const unsigned int cParsed = 'DONE';						//magic number to check if elf is already cParsed

	unsigned int *pElfIdentifier = (unsigned int*)pSpuElf;

	if(*pElfIdentifier == cParsed) 
	{
		rpElfInfo = (SElfInfo*)(pElfIdentifier+1);	//elf is already cParsed
		return ePR_Success;
	}

	SElf32Ehdr *pEhdr = (SElf32Ehdr*)pSpuElf;
	const unsigned int cElfMagicNumber = '\177ELF';	//magic number to identify an elf

	if(*pElfIdentifier != cElfMagicNumber)
	{
		rpElfInfo = (SElfInfo*)0;
		return ePR_NoElf;//not an elf file
	}
	if (pEhdr->elfMachine != 0x17)		//verify the machine code (SPU=0x17)
	{
		rpElfInfo = (SElfInfo*)0;
		return ePR_NoSPUElf;
	}
	
	SElfInfo elfInfo;
	unsigned int spuImageStart = ~0;
	unsigned int spuImageEnd	 = 0;
	unsigned int spuImageLSDestination = ~0;

	SElf32Shdr *pShdr =(SElf32Shdr*)((char*)pEhdr + pEhdr->elfShoff);

	for(SElf32Shdr *pSh=pShdr; pSh<&pShdr[pEhdr->elfShnum]; ++pSh)	//calculate image size, bss parameters and ls destination address
	{
		if(pSh->shFlags & scSHFAlloc)		//only consider segments that have the ALLOC flag set 
		{
			if(pSh->shSize )
			{
				if(pSh->shOffset < spuImageStart)	//find image start address
					spuImageStart = pSh->shOffset;
				if(pSh->shType & scSHTNoBits)		//check if this section is a BSS section and remember its details
				{
					elfInfo.bssStart	= pSh->shOffset;
					elfInfo.bssSize		= pSh->shSize;
				}
				if ((pSh->shOffset + pSh->shSize) > spuImageEnd)	//find image end address
					spuImageEnd = (pSh->shOffset + pSh->shSize);
				if (pSh->shAddr < spuImageLSDestination)	//find lowest destination address
					spuImageLSDestination = pSh->shAddr;
			}
		}
	}
	//strip bss section if requested
	elfInfo.imageSize = ((spuImageEnd - spuImageStart) + 15 ) & ~15;	//round size up the next 16 bytes for transfer

	if(elfInfo.imageSize > NSPU::scLSSize)
	{
		rpElfInfo = (SElfInfo*)0;
		return ePR_ElfTooBig;
	}

	elfInfo.spuImageEA.ull = (uint64)((uint32)pSpuElf + spuImageStart);
	if(elfInfo.spuImageEA.ull % 16 )
	{
		rpElfInfo = (SElfInfo*)0;
		return ePR_NoQWAddress;	//start address is not 16 bytes aligned
	}
	elfInfo.spuElfEA.ull = (uint64)(uint32)pSpuElf;
	elfInfo.LSDestination = spuImageLSDestination;
	elfInfo.entry = pEhdr->elfEntry;
	elfInfo.entryOff = elfInfo.entry - elfInfo.LSDestination;
	*pElfIdentifier = cParsed;	//mark elf as cParsed
	++pElfIdentifier;

	memcpy(pElfIdentifier, &elfInfo, sizeof(elfInfo));	//overwrite elf header with SElfInfo structure

	rpElfInfo = (SElfInfo*)pElfIdentifier;
	return ePR_Success;
}

#endif //PS3
