# Copyright (c) 2001-2009 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved.
# This component and the accompanying materials are made available
# under the terms of "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:
# Wrapper to support the EPOC AIF Compiler
# 
#


use FindBin;		# for FindBin::Bin
use File::Copy;		# for copy()
use Cwd;		# for cwd
use File::Basename;	# for basename()

my $PerlBinPath;	# fully qualified pathname of the directory containing this script
my $curdrive="x";	    	# will be initialised when first needed

# establish the path to the Perl binaries
BEGIN {
	require 5.005_03;		# check user has a version of perl that will cope
	$PerlBinPath = $FindBin::Bin;	# X:/epoc32/tools
	$PerlBinPath =~ s/\//\\/g;	# X:\epoc32\tools
}
use lib $PerlBinPath;
use E32Variant;         # for variant specific macros
use Pathutl;
use Preprocessor;

sub print_usage
	{
#........1.........2.........3.........4.........5.........6.........7.....
	print <<USAGE_EOF;

Usage:
  epocaif [options] srcfile [-o outputfile] [-t tmpdir] [-b "bmps" | -m mbm] [-l "TargetPath:CWD"]


The available options are

   -Ixxx  -- C++ preprocessor arguments
   -o	  -- output AIF file name including path
   -t	  -- tempory directory for intermediate files
   -b	  -- list of bitmaps Eg., "-b/c8\\location\\bmp1 /c8\\location\\bmp2.."
   -m	  -- compiled MBM file (alternative to -b)
   -l	  -- if specified, captures all source to \\epoc32\\localisation\\...

The aif resource file is then passed through the C++ preprocessor, using any 
specified preprocessor arguments, and then compiled with RCOMP.EXE to 
generate a compiled resource.
The -m or -b option is used to generate a suitable MBM file, if required.
The MBM and the compiled resource are then combined to produce an AIF file.


USAGE_EOF
	}

#-------------------------------------------------------
# Process commandline arguments
#
# Can't use the Getopt package because it doesn't like the -D and -I style options
#
my $sourcefile="";
my $opt_o="";
my $opt_h="";	
my $tmpdir="";
my $opt_v=1;
my $opt_b="";
my $opt_l="";
my $opt_m="";
my $TrgPath;
my $xipaif=1;

my $exe = &PreprocessorToUseExe();
my $cpp_spec= "$exe -undef -C ";	    # preserve comments

my $errors = 0;
while (@ARGV)
	{
	my $arg = shift @ARGV;

	if ($arg =~ /^-I-$/)
		{
		$cpp_spec .= "-I- ";
		next;
		}
	if ($arg =~ /^-I(.*)$/)
		{
		$cpp_spec .= "-I ";
		if ($1 eq "")
		    {
		    $arg = shift @ARGV;
		    }
		else
		    {
		    $arg = $1;
		    }
		$cpp_spec .= quoted_path($arg)." ";
		next;
		}
	if ($arg =~ /^-v$/)
		{
		$opt_v =1;
		next;
		}
	if ($arg =~ /^-o(.*)$/)
		{
		$opt_o =$1;
		$TrgPath = $opt_o;
		next;
		}

	if ($arg =~ /^-t(.*)\\?$/)
		{
		$tmpdir =$1;
		next;
		}
	if ($arg =~ /^-b(.*)$/)
		{
		$opt_b =$1;
		next;
		}	
	if ($arg =~ /^-m(.*)$/)
		{
		$opt_m =$1;
		next;
		}	
	if ($arg =~ /^-l(.*)$/)
		{
		$opt_l =$1;
		next;
		}	

	if ($arg =~ /^-/)
		{
		print "Unknown arg: $arg\n";
		$errors++;
		next;
		}
	$sourcefile=$arg;
	}

if ($opt_m ne "" && $opt_b ne "")
	{
	print "Can't specify both -m and -b\n";
	$errors++;
	}
if ($errors || $sourcefile eq "")
	{
	print_usage();
	exit 1;
	}

my $rss_base = basename($sourcefile);
my ($rssfile) = split(/\./, $rss_base);	    # remove extension
my $rpp_name = "$tmpdir\\$rssfile.rpp";
my $outputfile="$tmpdir\\AIF.RSC";	
my $headerfile=$opt_h;

if ($opt_v)
	{
	print "* Source file:   $sourcefile\n";
	print "* Resource file: $outputfile\n" if ($outputfile ne "");
	}

$opt_o = "-o\"$outputfile\"" if ($outputfile ne "");
$opt_h = "-h\"$headerfile\"" if ($headerfile ne "");


#-------------------------------------------------------
# Run the preprocessor
#

my $variantMacroHRHFile = Variant_GetMacroHRHFile();
if($variantMacroHRHFile){
    my $variantFilePath = Path_Split('Path',$variantMacroHRHFile);
    chop ( $variantFilePath );
    $cpp_spec .= " -I \"" . &Path_RltToWork($variantFilePath) . "\" -include " . &Path_RltToWork($variantMacroHRHFile) . " "; 
}

$cpp_spec .= "-I $PerlBinPath\\..\\include ";	# extra path to support shared tools
$cpp_spec .= "-D_UNICODE ";

$cpp_spec .= quoted_path($sourcefile) ." -o ". quoted_path($rpp_name);

print "* $cpp_spec\n" if ($opt_v);
system($cpp_spec);

my $cpp_status = $?;
die "* cpp failed\n" if ($cpp_status != 0);


#-------------------------------------------------------
# Copy source to epoc32\localisation
#

if ($opt_l ne "")
{
use lockit_info;
	my ($rssfile, $FileType) = split(/\./, basename($TrgPath));
	&Lockit_SrcFile($rssfile, $rpp_name, $opt_l, $FileType, $opt_b);
}

#-------------------------------------------------------
# Merge rls strings to rpp
#
&Merge_rls_string($rpp_name);

#-------------------------------------------------------
# Run the resource compiler
#

my $rcomp_spec = "rcomp -u ";
$rcomp_spec .= "-:$tmpdir\\_dump_of_resource_ "; # causes Rcomp to dump each resource (uncompressed and unpadded) in $tmpdir\\_dump_of_resource_1, $tmpdir\\_dump_of_resource_2, etc
$rcomp_spec .= "$opt_o $opt_h -s\"$rpp_name\" -i\"$sourcefile\"";

print "* $rcomp_spec\n" if ($opt_v);
system($rcomp_spec);
if ($? != 0)
	{
	print "* RCOMP failed - deleting output files\n";
	unlink $outputfile if ($outputfile ne "");
	unlink $headerfile if ($headerfile ne "");
	exit 1;
	}
print "* deleting $rpp_name\n" if ($opt_v);
unlink $rpp_name;

#-------------------------------------------------------
# Run bmconv, if needed
#

if ($opt_b ne "")
	{
	print "* bmconv /q $tmpdir\\AIF.MBM $opt_b\n" if ($opt_v);
	system("bmconv /q $tmpdir\\AIF.MBM $opt_b");
	if ($? != 0)
		{
		print "* BMCONV failed\n";
		exit 1;
		}
	print "* bmconv /q /s $tmpdir\\AIF_XIP.MBM $opt_b\n" if ($opt_v);
	system("bmconv /q /s $tmpdir\\AIF_xip.MBM $opt_b");
	
	if ($? != 0)
		{
		print "* BMCONV failed\n";
		exit 1;
		}
	}
elsif ($opt_m ne "")
	{
	print "* copy $opt_m $tmpdir\\AIF.MBM\n" if ($opt_v); 
	copy($opt_m, "$tmpdir\\AIF.MBM");
	# no xip file genarated 
	$xipaif=0;
	}
else
	{
	# no bitmap specified - this is legitimate
	unlink("$tmpdir\\AIF.MBM");
	unlink("$tmpdir\\AIF_xip.MBM");
	}

#-------------------------------------------------------
# Get the from UID from the first four bytes of "$tmpdir\\_dump_of_resource_1"
#

open(DUMP_OF_RESOURCE_1, "< $tmpdir\\_dump_of_resource_1") or die("* Can't open dump file\n");
binmode(DUMP_OF_RESOURCE_1);
my $data;
my $numberOfBytesRead=read(DUMP_OF_RESOURCE_1, $data, 4);
defined($numberOfBytesRead) or die("* Can't read from dump file\n");
($numberOfBytesRead>=4) or die("* Dump file too short\n");
my $uid=(unpack('V', $data))[0];
undef($data);
undef($numberOfBytesRead);
close(DUMP_OF_RESOURCE_1) or die("* Can't close dump file\n");

#-------------------------------------------------------
# Produce the AIF file from the RSC and MBM files
#

my $uidcrc = "uidcrc.exe 0x101fb032 0 ".sprintf('0x%08x', $uid)." $tmpdir\\out.aif";
my $uidcrc_xip = "uidcrc.exe 0x101fb032 0 ".sprintf('0x%08x', $uid)." $tmpdir\\out_xip.aif";

print "* $uidcrc\n" if ($opt_v);
system($uidcrc);
if ($? != 0)
	{
	print "* UIDCRC failed\n";
	exit 1;
	}
if ($xipaif ne 0)
	{	
	print "* $uidcrc\n" if ($opt_v);
	system($uidcrc_xip);
	if ($? != 0)
		{
		print "* UIDCRC failed\n";
		exit 1;
		}
	}


open(OUT_AIF, ">> $tmpdir\\out.aif") or die("* Can't open temporary file\n");
binmode(OUT_AIF);

if ($xipaif ne 0)
	{
	open(OUTXIP_AIF, ">> $tmpdir\\out_xip.aif") or die("* Can't open temporary file\n");
	binmode(OUTXIP_AIF);
	}

print "* Writing length of the RSC-block\n" if ($opt_v);
my $lengthOfRscBlock=-s("$tmpdir\\aif.rsc");
my $numberOfPaddingBytes=(4-($lengthOfRscBlock%4))%4;
print(OUT_AIF pack('V', $lengthOfRscBlock));
if ($xipaif ne 0)
	{
	print(OUTXIP_AIF pack('V', $lengthOfRscBlock));
	}
print "* Appending the RSC-block\n" if ($opt_v);
&appendFile(\*OUT_AIF, "$tmpdir\\aif.rsc");
if ($xipaif ne 0)
	{
	&appendFile(\*OUTXIP_AIF, "$tmpdir\\aif.rsc");
	}
# append any necessary padding bytes so that the file-offset of the start of the MBM-block is a multiple of 4-bytes
print(OUT_AIF ('_' x $numberOfPaddingBytes));
if ($xipaif ne 0)
	{
	print(OUTXIP_AIF ('_' x $numberOfPaddingBytes));
	}
if (-e("$tmpdir\\aif.mbm"))
	{
	print "* Appending the MBM-block\n" if ($opt_v);
	&appendFile(\*OUT_AIF, "$tmpdir\\aif.mbm");
	}
if (-e("$tmpdir\\aif_xip.mbm"))
	{
	print "* Appending the XIPMBM-block\n" if ($opt_v);
	&appendFile(\*OUTXIP_AIF, "$tmpdir\\aif_xip.mbm");
	}

close(OUT_AIF) or die("* Can't close temporary file\n");
if ($xipaif ne 0)
	{
	close(OUTXIP_AIF) or die("* Can't close temporary file\n");
	}
print "* copy $tmpdir\\out.aif $TrgPath\n" if ($opt_v);
copy("$tmpdir\\out.aif", "$TrgPath");
if ($xipaif ne 0)
	{
	my $basepath = &Path_Split('Path', $TrgPath);
	my $ext=&Path_Split('Ext',  $TrgPath);
	my $basename = basename($TrgPath, $ext);
	my $xip="_xip";
	print "* copy $tmpdir\\out_xip.aif $basepath$basename$xip$ext\n" if ($opt_v);
	copy("$tmpdir\\out_xip.aif", "$basepath$basename$xip$ext");
	}
unlink("$tmpdir\\_dump_of_resource_*");
unlink("$tmpdir\\aif.rsc");
unlink("$tmpdir\\aif.mbm");
unlink("$tmpdir\\out.aif");
unlink("$tmpdir\\aif_xip.mbm");
unlink("$tmpdir\\out_xip.aif");
exit 0;

#-------------------------------------------------------
# Subroutine: convert path into something acceptable to CPP.EXE
#

sub quoted_path
    {
    my ($arg) = @_;
    return "\"$arg\"" if ($arg !~ /^\\[^\\]/);	# not an absolute path
    if ($curdrive eq "x")
		{
		$curdrive="";
		$curdrive=$1 if (cwd =~ /^(.:)/);	
		}
    return "\"$curdrive$arg\"";
    }

#-------------------------------------------------------
# Subroutine: Merge the rls strings in the rpp file specified
#
sub Merge_rls_string
	{
	my ($rppfile) = @_;

	my $line;
	my $StringId;
	my $key;
	my $value;
	my $StringToSubstitute;
	my %ResourceString;
	
	print "* merging text strings to $rppfile\n" if ($opt_v);
	
	open NEWRPP, ">$rppfile.new" or die "* Can't write to $rppfile.new";
	open RPP, "$rppfile" or die "* Can't open $rppfile";
	
	while ($line = <RPP>) 	{
		while (($StringId, $StringToSubstitute)=each %ResourceString)
		{
			$line=~s/\b$StringId\b/$StringToSubstitute/g if ($line !~ /^rls_string/);
		}
	
		# find quoted "" strings
		if($line =~ /^rls_string\s+(\S+)\s+(.*$)/)
		{
			my $text = $2;
			$key = $1;
			$line=~s/(.*)/\/\/$1/;
			my $substr_count = 0;
			if(!exists $ResourceString{$key})
			{
			SUBSTR:	while (1)
			{
				# find quoted "" strings e.g. "hello"
				if($text =~ /^(\s*\"(.*?\\.)*.*?\")/)		
				{
					$value = $1;
					$text = $';
					++$substr_count;
				}
	
				# find quoted '' strings. e.g. 'world'
				elsif($text =~ /^(\s*\'(.*?\\.)*.*?\')/)			
				{
					$value = $1;
					$text = $';
					++$substr_count;
				}
	
				# find hex strings e.g. <0x34><0x45><0x65>
				elsif($text =~ /^(\s*(<.*?>)+)/)		
				{
					$value = $1;
					$text = $';
					++$substr_count;
				}
	
				# find c comment e.g. /*hello world*/ (may exist between strings)
				elsif($text =~ /^(\s*\/\*.*?\*\/)/)		
				{
					$text = $';
					next SUBSTR; # ignore embedded comment
				}
	
				# find c++ comment e.g. //hello world (may exist after strings)
				elsif($text =~ /^(\s*\/\/.*$)/)		
				{
					$text = $';
					next SUBSTR; # ignore trailing comment
				}
	
				# exit search
				else
				{
					if ($substr_count == 0)
					{
						warn("WARNING: rls_string $key either has incorrect syntax or no value\n");
					}
					last SUBSTR;
				}
			$ResourceString{$key} .= $value;
			}
		  	}
		}
		print NEWRPP $line;
	}

	close RPP;
	close NEWRPP;
	copy ("$rppfile.new", "$rppfile");
	unlink ("$rppfile.new");
	}

#-------------------------------------------------------
# Subroutine: Append a file into the open (binary) file already opened
#
sub appendFile
	{
	my $fileHandleOfTarget=shift;
	my $fileNameOfSource=shift;
	open(SOURCE, "< $fileNameOfSource") or die("* Can't open $fileNameOfSource\n");
	binmode(SOURCE);
	for (;;)
		{
		my $data;
		my $numberOfBytesRead=read(SOURCE, $data, 1024);
		defined($numberOfBytesRead) or die("* Can't read from $fileNameOfSource\n");
		if ($numberOfBytesRead==0)
			{
			last;
			}
		print($fileHandleOfTarget $data);
		}
	close(SOURCE) or die("* Can't close $fileNameOfSource\n");
	}

