// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//

#include <string.h>
#include <io.h>

#if defined(__MSVCDOTNET__) || defined(__TOOLS2__)
#include <iomanip>
#else //!__MSVCDOTNET__
#include <iomanip.h>
#endif //__MSVCDOTNET__

#include "h_utl.h"
#include "h_ver.h"

// ROMMASK.EXE cl parameters
char *gRomImage=NULL;
char *gOutputRom=NULL;
int	gRomSize=0;
char gFormat='P';
const int KMaxSections=8;
int gNumberOfSections=0;
int gSectionSize[KMaxSections];
int gHeader=ETrue;
int gVerbose=EFalse;


TInt MaskPlain(TInt aRomSize,ifstream& aPsionImageFile,ofstream& aMaskRomImageFile)
//
// Create a plain mask rom image file
//
	{

	const char KMaskRomFillCharacter='\377';
	const TInt KBufferSize=1024;
	char empty[KBufferSize];
	for (TInt y=0;y<KBufferSize;y++)
		empty[y]=KMaskRomFillCharacter;
	char buffer[KBufferSize];
	TUint checksum32=0;
	TUint checksum8=0;
	for (TInt x=0;x<aRomSize;x+=KBufferSize)
		{
		memcpy(buffer,empty,KBufferSize);
		aPsionImageFile.read(buffer,KBufferSize);
		aMaskRomImageFile.write(buffer,KBufferSize);
		TInt z;
		for (z=0;z<KBufferSize;z+=4)
			checksum32+=*(TUint32*)&buffer[z];
		for (z=0;z<KBufferSize;z++)
			checksum8+=(TUint8)buffer[z];
		}

	if (gVerbose)
		{
		cout << dec;
		cout << aRomSize << " byte PLAIN\r\n";
		cout << "32 bit checksum 0x";
		cout << setw(8);
		cout << hex;
		cout << setfill('0');
		cout << checksum32 << "\r\n";
		cout << " 8 bit checksum 0x";
		cout << setw(8);
		cout << hex;
		cout << setfill('0');
		cout << checksum8 << "\r\n";
		}
	return(KErrNone);
	}

TInt MaskMotorola(TInt /*aRomSize*/,ifstream& /*aPsionImageFile*/,ofstream& /*aMaskRomImageFile*/)
//
// Create a motorola s-record mask rom image file
//
	{
	return(KErrNone);
	}

TInt MaskRom(TInt aRomSize,ifstream& aPsionImageFile,const char* aMaskRomImageFileName,const char* aMaskRomFormat)
	{
	ofstream MaskRomImageFile;

	TInt r;
	if (!MaskRomImageFile)
		{
		cout << "Error:  Cannot create mask rom image file (" << aMaskRomImageFileName << ")\r\n";
		r=KErrArgument;
		}
	else
		{
		if (gVerbose)
			{
			cout << "\r\nMask ROM image file: ";
			cout << aMaskRomImageFileName << "\r\n"; 
			}
		switch (aMaskRomFormat[0])
			{
		case 'P':
			MaskRomImageFile.open(aMaskRomImageFileName, ios::in | ios::binary);
			r=MaskPlain(aRomSize,aPsionImageFile,MaskRomImageFile);
			break;
		case 'M':
			MaskRomImageFile.open(aMaskRomImageFileName, ios::in);
			r=MaskMotorola(aRomSize,aPsionImageFile,MaskRomImageFile);
			break;
		default:
			cerr << "Error:  Rom format not recognised\r\n";
			r=KErrGeneral;
			break;
			}
		MaskRomImageFile.close();
		}
	return r;
	}


TInt getIntegerArg(int argc, char *argv[], int param)
	{

	int error=KErrNone;
	if (param>=argc)
		error=KErrArgument;
	if (error==KErrNone)
		{
		int val;
//		if (!isNumber(argv[param]))
//			return KErrArgument;

#ifdef __TOOLS2__
istringstream s(argv[param]);
#else
istrstream s(argv[param], strlen(argv[param]));
#endif

#if defined(__MSVCDOTNET__) || defined(__TOOLS2__)
		s >> setbase(0);
#endif //__MSVCDOTNET__

		s>>val;
		if (!s.fail())
			return val;
		}
	cerr << "Error:  Integer argument required for ";
	cerr << argv[param-1];
	cerr << "\r\n";
	return -1;
	}

char *getStringArg(int argc, char *argv[], int param)
	{

	if (param>=argc)
		{
		cerr << "Error:  String argument required for ";
		cerr << argv[param-1];
		cerr << "\r\n";
		return NULL;
		}
	return argv[param];
	}

char getCharArg(int argc, char *argv[], int param)
	{
	
	char *p=getStringArg(argc,argv,param);
	if (p!=NULL)
		return p[0];
	return '\000';
	}

TInt processCommandLine(int argc, char *argv[])
	{

	if (argc==1)
		return KErrArgument;
	int param=1;
	while (param<argc)
		{
		switch (argv[param][1])
			{
		case 'r':
		case 'R':
			// rom name
			param++;
			gRomImage=getStringArg(argc, argv, param);
			break;
		case 's':
		case 'S':
			if (argv[param][2]=='i')
				{
				// rom size
				param++;
				gRomSize=getIntegerArg(argc, argv, param);
				if (gRomSize==-1)
					return KErrGeneral;
				}
			else
				{
				// section
				if (gNumberOfSections>=KMaxSections)
					{
					cerr << "Error:  Too many sections\r\n";
					return KErrGeneral;
					}
				param++;
				gSectionSize[gNumberOfSections]=getIntegerArg(argc, argv, param);
				if (gSectionSize[gNumberOfSections]==-1)
					return KErrGeneral;
				if (gSectionSize[gNumberOfSections]==0)
					{
					cerr << "Error:  Section is zero bytes long\r\n";
					return KErrGeneral;
					}
				if (gSectionSize[gNumberOfSections]>64)
					{
					cerr << "Error: Section too big\r\n";
					return KErrGeneral;
					}
				gNumberOfSections++;
				}
				break;
			case 'f':
			case 'F':
				param++;
				gRomSize=getCharArg(argc, argv, param);
				break;
			case 'l':
			case 'L':
				cerr << "Error:  Use -verbose instead of -log";
				break;
			case 'v':
			case 'V':
				gVerbose=ETrue;
				break;
			case 'o':
			case 'O':
				param++;
				gOutputRom=getStringArg(argc, argv, param);
				break;
			case 'n':
			case 'N':
				// -no-header
				gHeader=EFalse;
				break;
			default:
				cout << "Error:  Unrecognised switch '"<<argv[param]<<"'\r\n";
				return KErrArgument;
			}
		param++;
		}
	if (gRomImage==NULL)
		{
		cerr << "Error:  No rom image specified\r\n";
		return KErrArgument;
		}
	if (gOutputRom==NULL)
		{
		cerr << "Error:  No output rom file specified\r\n";
		return KErrArgument;
		}
	if (gRomSize>64)
		{
		cerr << "Error:  Rom too big\r\n";
		return KErrGeneral;
		}
	if (gRomSize==0)
		{
		cerr << "Error: No rom size specified\r\n";
		return KErrArgument;
		}
	if (gFormat!='P' && gFormat!='M')
		{
		cerr << "Error:  Invalid mask rom format specified\r\n";
		return KErrArgument;
		}

	return KErrNone;
	}

TInt Align1M(TInt aVal)
	{
	return (aVal+0xfffff) & 0x7ff00000;
	}

TInt main(int argc, char *argv[])
	{

	const TInt KPsionImageFileHeaderSize=0x100;

	cout << "\r\nROMMASK - Rom masker V" << MajorVersion << "." << MinorVersion << "(Build " << Build << ")\r\n";
	cout << Copyright;

	char HelpText[] = 
		"Syntax: ROMMASK -romimage <psion img file> -output <rom img name>\r\n"
		"                [-verbose] [-size <total rom size>]\r\n"
		"                [-no-header] [-format <format>] [-section <size>]*\r\n"
		"Format: MOTOROLA (ascii s-record format)\r\n"
		"        PLAIN (plain binary format) default\r\n";
	int r=processCommandLine(argc, argv);
	if (r==KErrArgument)
		{
		cout << HelpText;
		return(KErrArgument);
		}

// Open the psion image file

	ifstream PsionImageFile;
	char*& PsionImageFileName=gRomImage;

#if defined(__MSVCDOTNET__) || defined(__TOOLS2__)
	PsionImageFile.open(PsionImageFileName, ios::in | ios::binary);
#else //!__MSVCDOTNET__
	PsionImageFile.open(PsionImageFileName, ios::nocreate | ios::binary);
#endif //__MSVCDOTNET__

	if (!PsionImageFile)
		{
		cerr << "Error:  Cannot open psion image file (" << PsionImageFileName << ")\r\n";
		return(KErrArgument);
		}
	
	gRomSize*=1024*1024; // in Mb
	// resolve sections to cover whole rom
	int size=0;
	int i;
	for (i=0; i<gNumberOfSections; i++)
		{
		gSectionSize[i]*=1024*1024; // in Mb
		size+=gSectionSize[i];
		}
	if (size<gRomSize)
		gSectionSize[gNumberOfSections++]=gRomSize-size;
	if (size>gRomSize)
		{
		cerr << "Error:  Sections too big for rom";
		return KErrGeneral;
		}

// Create the mask rom image file

	ofstream MaskRomImageFile;
	char*& MaskRomImageFileName=gOutputRom;

 	MaskRomImageFile.open(MaskRomImageFileName);
	if (!MaskRomImageFile)
		{
		cerr << "Error:  Cannot create mask rom image file (" << MaskRomImageFileName << ")\r\n";
		PsionImageFile.close();
		return(KErrArgument);
		}

	if (gHeader)
		{
		PsionImageFile.ignore(KPsionImageFileHeaderSize);
		int count=PsionImageFile.gcount();
		if (count!=KPsionImageFileHeaderSize)
			{
			cerr << "Error:  Corrupt Psion image file\r\n";
			return(KErrGeneral);
			}
		}

	r=KErrNone;
	for (i=0; i<gNumberOfSections; i++)
		{
		r=MaskRom(gSectionSize[i],PsionImageFile,MaskRomImageFileName,&gFormat);
		if (r!=KErrNone)
			{
			cerr << "Error:  An error occured while processing Rom image\r\n";
			return r;
			}
		// next section
		char* ptr=MaskRomImageFileName;
		while (*++ptr!=0)
			;
		*--ptr=(char)(i+'2');
		}

	PsionImageFile.close();
	return(r);
	}
