eval '(exit $?0)' && eval 'exec perl -w -S "$0" ${1+"$@"}'
    & eval 'exec perl -w -S $0 $argv:q'
    if 0;
# vim: set filetype=perl : 

use strict;
use Config;
use Cwd;
use Cwd 'abs_path';
use File::Temp qw(tempdir);
                               #0x(major)(minor)
my $mfa_version = 0x00000001;  #0x(0000)(0001)
my $start_time = time();
my $dir = $ARGV[0];
my @fails = ();
my @inis;
my $tmpdir;
my @srcdirs;
my $use_existing_dir = 1;
my $use_existing_txz = 0;
my @psid2bin;
my $use_xz = 1;

my $simulate = 0;

my $debug = 0;
my $mf_archive = undef;
my $txz_archive;
my $ignore_verify_mfa = 0;
my $exedir = abs_path($0);
my $currdir;

$exedir = invospath($exedir);
$exedir =~ s/\/[^\/]*$//;


sub remove_tree
{
    my ($path) = @_;
    my $cmd;

    if ($Config{osname} eq "linux") {
        $cmd = "rm -rf $path";
    } else {
        $cmd = "del /s /q $path";
    }
    print "DEBUG remove_tree: $cmd\n" if ($debug);
    `$cmd`;
}


sub xzcmd
{
    my $xzcmd;

    if ($Config{osname} eq "linux") {
        $xzcmd = "xz";
    } else {
        $xzcmd = ospath("$exedir/xz.exe");
    }
    # set compression preset to 8 to allow for efficient compression of 16MB images
    $xzcmd .= " -8";
    print "DEBUG xzcmd: $xzcmd\n" if ($debug);
    return $xzcmd;
}


sub copycmd
{
    my $copycmd;

    if ($Config{osname} eq "linux") {
        $copycmd = "cp";
    } else {
        $copycmd = "copy";
    }
    print "DEBUG copycmd: $copycmd\n" if ($debug);
    return $copycmd;
}


sub getCurrentDir
{
    my $currdir;

    if ($Config{osname} eq "linux") {
        $currdir = getcwd();
    } else {
        $currdir = getdcwd();
    }
    print "DEBUG getCurrentDir: $currdir\n" if ($debug);
    return $currdir;
}


sub getDirListing
{
    my ($path) = @_;
    my $cmd;
    if ($Config{osname} eq "linux") {
        $cmd = "/bin/ls " . $path;
    } else {
        $cmd = "dir /B " . $path;
    }
    my @list = `$cmd`;
    chomp @list;
    for (my $i = 0; $i < @list; $i++) {
        while ($list[$i] =~ /\//) {
            $list[$i] =~ s/^[^\/]*\///;
        }
    }
    print "DEBUG getDirListing: CMD: $cmd\n" if ($debug);
    print "DEBUG getDirListing: @list\n" if ($debug);
    return @list;
}


sub ospath
{
    my ($path) = @_;

    if ($Config{osname} ne "linux") {
        $path =~ s/\//\\/g; # 
    }
    if ($path =~ /( |\+)/) {
        if ($path !~ /\"/) {
            $path = '"'.$path.'"';
        }
    }
    print "DEBUG ospath: $path\n" if ($debug);
    return $path;
}


sub ospath_int
{
    my ($path) = @_;

    if ($Config{osname} ne "linux") {
        $path =~ s/\//\\/g;
    }

    $path =~ s/ /\ /g;
    print "DEBUG ospath_int: $path\n" if ($debug);
    return $path;
}


sub invospath
{
    my ($path) = @_;

    if ($Config{osname} ne "linux") {
        $path =~ s/\\/\//g;
    }
    print "DEBUG invospath: $path\n" if ($debug);
    return $path;
}


sub mktempDir
{
    my $tmpdir = tempdir();

#    if ($Config{osname} eq "linux") {
#        $tmpdir = `mktemp -d`;
#        chomp $tmpdir;
#    } else {
#        $tmpdir = ".\\tmp123";
#    }

    return $tmpdir;
}


#-- Command-Line Arguments  ---------------------------------------------------

for(my $i=0; $i<(@ARGV); $i++) {
    if ($ARGV[$i] eq "-h") {
       usage();
       exit 0;
    } elsif ($ARGV[$i] eq "-s") {
        $i++;
        push @srcdirs, checkRefineArgs($ARGV[$i], "-s");
    } elsif ($ARGV[$i] eq "-p") {
        $i++;
        if (!defined $ARGV[$i]) {
            print STDERR "-E- Must specify package name\n";
            usage();
            exit 1;
        }

        if ($ARGV[$i] !~ /\.mfa$/) {
            $ARGV[$i] = $ARGV[$i] . ".mfa";
        }
        $mf_archive = $ARGV[$i];
        $txz_archive = $ARGV[$i];
        $txz_archive =~ s/\.mfa/\.txz/;
        
    } elsif ($ARGV[$i] eq "-noxz") {
        $use_xz = 0;
    } elsif ($ARGV[$i] eq "-debug") {
        $debug = 1;
    } elsif ($ARGV[$i] eq "-noverify") {
        $ignore_verify_mfa = 1;
    } else {
        print STDERR "-E- Unknown option $ARGV[$i]\n";
        usage();
        exit(255);
    }	
}

$currdir = getCurrentDir();

if (@srcdirs == 0) {
    print STDERR "-E- Must specify source dir\n";
    usage();
    exit 1;
}

if (!defined $mf_archive) {
    print STDERR "-E- Must specify package name\n";
    usage();
    exit 1;
}

$tmpdir = mktempDir();
print "$tmpdir\n" if($debug);

my %orig_file;
my $cps = 0;
my $total_cps = 0;
for (my $k = 0; $k < @srcdirs; $k++) {
    print "Adding bins from $srcdirs[$k]\n";
    $cps = copy_bins($srcdirs[$k], $tmpdir, $total_cps);
    if ($cps < 0) {
        die("Failed to copy files...\n");
    }
    $total_cps += $cps;
}

if (!$total_cps) {
   die("-E- Failed to find any binary file, Please specify a directory with bins!\n");
}
print "\n";

print "Querying images ...\n";
my $rc = query_images($tmpdir);
die("\n-E- Failed during query\n") if ($rc);

$rc = create_map_file();
die("-E- Failed to create map file\n") if ($rc);
my $ext = "";
if ($use_xz) {
    print "Compressing ... (this may take a minute)\n";
    $ext = ".xz";
    my $cmd = xzcmd() . " -q --check=crc32 " . ospath("$tmpdir/MAP_FILE");
    print "$cmd\n" if($debug);
    system($cmd);
    die("Failed to run xz on ".ospath("$tmpdir/MAP_FILE")) if ($?);

    $cmd = xzcmd() . " -q --check=crc32 " . ospath("$tmpdir/TOC_FILE");
    print "$cmd\n" if($debug);
    system($cmd);
    die("Failed to run xz on ".ospath("$tmpdir/TOC_FILE")) if ($?);

    $cmd = xzcmd() . " -q --check=crc32 ". ospath("$tmpdir/DATA_FILE");
    print "$cmd\n" if($debug);
    system($cmd);
    die("Failed to run xz on ".ospath("$tmpdir/DATA_FILE")) if ($?);
}

my $argcrc = 0x0;
$rc = create_mfa($mf_archive, ospath("$tmpdir/MAP_FILE$ext"), ospath("$tmpdir/TOC_FILE$ext") , ospath("$tmpdir/DATA_FILE$ext"));
$argcrc = get_crc($mf_archive);
if (!$argcrc) {
    die("-E- Failed to calculate crc\n");
}
add_crc($mf_archive , $argcrc);

print "\nArchive: $mf_archive\n";

remove_tree($tmpdir);

my $end_time = time();
my $mins = int(($end_time - $start_time)/60);
my $seconds = int(($end_time - $start_time)-$mins*60);
print "Total time: $mins"."m$seconds"."s\n";

exit $rc;
#--------------------------------------------------------------------------------------------


sub get_crc
{
    my ($file) = @_;
    my $crc = 0;
    my $cmd = "";
    my $mlxfwmanager;
    if ($Config{osname} eq "linux") {
        $mlxfwmanager = ospath("$exedir/mlxfwmanager")
    } else {
        $mlxfwmanager = ospath("$exedir/mlxfwmanager.exe")
    }
    $cmd = $mlxfwmanager . " --crc -i " . ospath($file);
    print "$cmd\n" if($debug);
    my $x = `$cmd`;
    if ($x =~ /CRC32\s+=\s+(\S+)/) {
        $crc = $1;
    }
    return $crc;
}


sub copy_bins
{
    my $res = 0;
    my ($srcdir, $tmpdir, $offset) = @_;
    print "DEBUG copy_bins: $srcdir $tmpdir $offset\n" if($debug);
    my @fls = getDirListing(ospath("$srcdir"));
    my @bins;
    my $cntr = 0;
    print "\nFiles copied:    ";
    for (my $i = 0; $i < @fls; $i++) {
        if ($fls[$i] !~ /\.bin$/) {
            next;
        }
        $cntr++;
	    my $fname = sprintf("%04d", $i+1 + $offset);
        $orig_file{$fname} = $fls[$i];
        my $cpc = copycmd() . " " . ospath("$srcdir/$fls[$i]") . " " . ospath("$tmpdir/$fname");
        `$cpc`;
        if ($?) {
            print STDERR "Failed to copy $fls[$i] to ". ospath("$tmpdir/$fname")."\n";
            $res++;
            last;
        } else {
            print "\b\b\b".sprintf("%-3d", $cntr);
        }
        push @bins, $fls[$i];
    }
    print "\n";
    if ($res > 0) {
        return -$res;
    }
    return @bins;
}


sub query_images
{
    my %psid2fl;
    my $res = 0;
    my ($tmpdir) = @_;
    my @bins = getDirListing(ospath("$tmpdir/*"));
    my $offset = 0;
    my $cntr = 0;

    open OUTocF, ">".ospath("$tmpdir/TOC_FILE") or die("Couldn't open ".ospath("$tmpdir/TOC_FILE")." for writing!\n");
    binmode OUTocF;
    open OUTF, ">".ospath("$tmpdir/DATA_FILE") or die("Couldn't open ".ospath("$tmpdir/DATA_FILE")." for writing!\n");
    binmode OUTF;

    print "Files queried:    ";
    for (my $i = 0; $i < @bins; $i++) {
        my $version;
        my $psid;
        my $pn;
        my $description = "";
        my $cmd = "flint -i ".ospath("$tmpdir/$bins[$i]")." q";
        my @ts = `$cmd`;
        my $pxe_version = "";
        my $clp_version = "";
        my $uefi_version = "";
        my $fcode_version = "";
        my $branch = "";

        if($?) {
            print STDERR "Failed to query $bins[$i]\n";
            $res++;
            next;
        } else {
            for (my $j = 0; $j < @ts; $j++) {
                my $t = $ts[$j];
                if ($t =~ /fw version:\s+(\S+)/i) {
                    $version = $1;
                }
                if ($t =~ /psid:\s+(\S+)/i) {
                    $psid = $1;
                }
                if ($t =~ /type\=CLP/im) {
                    if ($t =~ /version[^\=]*\=(\S+)/) {
                        $clp_version = $1;
                    }
                }
                if ($t =~ /type\=UEFI/i) {
                    if ($t =~ /version\=(\S+)/) {
                        $uefi_version = $1;
                    }
                }
                if ($t =~ /type\=FCODE/i) {
                    if ($t =~ /version\=(\S+)/) {
                        $fcode_version = $1;
                    }
                }
                if ($t =~ /type\=PXE/i) {
                    if ($t =~ /version\=(\S+)/) {
                        $pxe_version = $1;
                    }
                }
            }
        }
        $cmd = "flint -i ".ospath("$tmpdir/$bins[$i]")." dc";
        my $t = `$cmd`;
        if($?) {
            print "flint dc command failed, trying query full\n" if ($debug);
            $cmd = "flint -i ".ospath("$tmpdir/$bins[$i]")." q full";
            my $t = `$cmd`;
            if($?) {
                print STDERR "Failed to query $bins[$i] for PN\n";
                $res++;
            } else {
               if ($t =~ /Part Number:\s+(\S+)/i) {
                   $pn = $1;
               }
               if ($t =~ /Description:\s+(\S.*)(\s*)$/mi) {
                   $description = $1;
               }
               $cntr++;
               print "\b\b\b".sprintf("%-3d", $cntr);
            }
        } else {
            print "flint dc command succeed\n" if ($debug);
            if ($t =~ /Name\s+\=\s+(\S+)/i) {
                $pn = $1;
            }
            if ($t =~ /Description\s+\=\s+(\S.*)(\s*)$/mi) {
                $description = $1;
            }
            $cntr++;
            print "\b\b\b".sprintf("%-3d", $cntr);
        }
        print "$psid $version $pn $description\n" if ($debug);

        my $binsize = (stat(ospath_int("$tmpdir/$bins[$i]")))[7];
        my $data_offset = pack("N", $offset); #
        my $data_size = pack("N", $binsize); #
        my $subimage_type = pack("n", 1); # 1: FW Image
        my $resvd = pack("C",0);
		my $num_ver_fields = pack("C", 3);
        my ($ver0, $ver1, $ver2, $ver3);
        if ($version =~ /(\d+)\.(\d+)\.(\d+)/) {
            $ver0 = pack("n",int($1));
            $ver1 = pack("n",int($2));
            $ver2 = pack("n",int($3));
            $ver3 = pack("n", 0);
        } elsif ($version =~ /.+((_\d{4})|(-\d{3}))/) {
            $ver0 = pack("n", 0);
            $ver1 = pack("n", 0);
            $ver2 = pack("n", 0);
            $ver3 = pack("n", 0);
            my $version_length = length($version);
            $branch = pack("a[$version_length]", $version);
        } else {
            die("Failed to extract version $version number $i: $bins[$i]\n");
        }
        my $metadatasz = 0;
        my $metadata_size = pack("n", ($metadatasz));
	    print OUTocF $data_offset;
	    print OUTocF $data_size;
	    print OUTocF $subimage_type;
	    print OUTocF $resvd;
	    print OUTocF $num_ver_fields;
	    print OUTocF $ver0;
	    print OUTocF $ver1;
	    print OUTocF $ver2;
	    print OUTocF $ver3;
	    print OUTocF $resvd;
	    print OUTocF $resvd;
	    print OUTocF $metadata_size;

        my $metad_size = pack("n", (0));
        if ($pxe_version) {
            my $data_offset = pack("N", 0xffffffff); #
            my $data_size = pack("N", 0); #
            my $subimage_type = pack("n", 0x110); # 0x110:  PXE Image
            my $resvd = pack("C",0);
	    	my $num_ver_fields = pack("C", 3);
            my ($ver0, $ver1, $ver2, $ver3);
            if ($pxe_version =~ /(\d+)\.(\d+)\.(\d+)/) {
                $ver0 = pack("n",int($1));
                $ver1 = pack("n",int($2));
                $ver2 = pack("n",int($3));
                $ver3 = pack("n", 0);
            } else {
                die("Failed to extract pxe version $pxe_version number $i: $bins[$i]\n");
            }
     	    print OUTocF $data_offset;
	        print OUTocF $data_size;
    	    print OUTocF $subimage_type;
	        print OUTocF $resvd;
    	    print OUTocF $num_ver_fields;
	        print OUTocF $ver0;
    	    print OUTocF $ver1;
	        print OUTocF $ver2;
	        print OUTocF $ver3;
    	    print OUTocF $resvd;
	        print OUTocF $resvd;
	        print OUTocF $metad_size;
        }

        if ($clp_version) {
            my $data_offset = pack("N", 0xffffffff); #
            my $data_size = pack("N", 0); #
            my $subimage_type = pack("n", 0x101); # 0x101:  CLP Image
            my $resvd = pack("C",0);
            my $num_ver_fields;
            my ($ver0, $ver1, $ver2, $ver3);
            if ($clp_version =~ /(\d+)\.(\d+)\.(\d+)/) {
                $num_ver_fields = pack("C", 3);
                $ver0 = pack("n",int($1));
                $ver1 = pack("n",int($2));
                $ver2 = pack("n",int($3));
                $ver3 = pack("n", 0);
            } elsif ($clp_version =~ /(\d+)/) {
                $num_ver_fields = pack("C", 1);
                $ver0 = pack("n",int($1));
                $ver1 = pack("n", 0);
                $ver2 = pack("n", 0);
                $ver3 = pack("n", 0);
            } else {
                die("Failed to extract clp version $clp_version number $i: $bins[$i]\n");
            }
     	    print OUTocF $data_offset;
	        print OUTocF $data_size;
    	    print OUTocF $subimage_type;
	        print OUTocF $resvd;
    	    print OUTocF $num_ver_fields;
	        print OUTocF $ver0;
    	    print OUTocF $ver1;
	        print OUTocF $ver2;
	        print OUTocF $ver3;
    	    print OUTocF $resvd;
	        print OUTocF $resvd;
	        print OUTocF $metad_size;
        }

        $metad_size = pack("n", (0));
        if ($uefi_version) {
            my $data_offset = pack("N", 0xffffffff); #
            my $data_size = pack("N", 0); #
            my $subimage_type = pack("n", 0x111); # 0x111:  UEFI Image
            my $resvd = pack("C",0);
	    	my $num_ver_fields = pack("C", 3);
            my ($ver0, $ver1, $ver2, $ver3);
            if ($uefi_version =~ /(\d+)\.(\d+)\.(\d+)/) {
                $ver0 = pack("n",int($1));
                $ver1 = pack("n",int($2));
                $ver2 = pack("n",int($3));
                $ver3 = pack("n", 0);
            } else {
                die("Failed to extract uefi version $uefi_version number $i: $bins[$i]\n");
            }
     	    print OUTocF $data_offset;
	        print OUTocF $data_size;
    	    print OUTocF $subimage_type;
	        print OUTocF $resvd;
    	    print OUTocF $num_ver_fields;
	        print OUTocF $ver0;
    	    print OUTocF $ver1;
	        print OUTocF $ver2;
	        print OUTocF $ver3;
    	    print OUTocF $resvd;
	        print OUTocF $resvd;
	        print OUTocF $metad_size;
        }
         
        $metad_size = pack("n", (0));
        if ($fcode_version) {
            my $data_offset = pack("N", 0xffffffff); #
            my $data_size = pack("N", 0); #
            my $subimage_type = pack("n", 0x121); # 0x121:  FCODE Image
            my $resvd = pack("C",0);
	    	my $num_ver_fields = pack("C", 3);
            my ($ver0, $ver1, $ver2, $ver3);
            if ($fcode_version =~ /(\d+)\.(\d+)\.(\d+)/) {
                $ver0 = pack("n",int($1));
                $ver1 = pack("n",int($2));
                $ver2 = pack("n",int($3));
                $ver3 = pack("n", 0);
            } else {
                die("Failed to extract fcode version $fcode_version number $i: $bins[$i]\n");
            }
     	    print OUTocF $data_offset;
	        print OUTocF $data_size;
    	    print OUTocF $subimage_type;
	        print OUTocF $resvd;
    	    print OUTocF $num_ver_fields;
	        print OUTocF $ver0;
    	    print OUTocF $ver1;
	        print OUTocF $ver2;
	        print OUTocF $ver3;
    	    print OUTocF $resvd;
	        print OUTocF $resvd;
	        print OUTocF $metad_size;
        }

        open INF, "<".ospath_int("$tmpdir/$bins[$i]") or die("Couldn't open $bins[$i] for reading!\n");
        binmode INF;
        my $buffer;
        while (read(INF,$buffer,65536)) {
	        print OUTF $buffer;
        }
        close INF;

        $offset += $binsize;
#        print "psid => $psid\n";
#        print "fwfile => ".rmpath($bins[$i])."\n";
#        print "opns => $pn\n";
#        print "version => $version\n";
#        print "size => ".(stat($bins[$i]))[7]."\n";
#        print "offset => ( $binsize )\n";
#        print "metadata_size => $metadatasz\n";
#        print "description => $description\n";
#        print "clp_version => $clp_version\n";
#        print "pxe_version => $pxe_version\n";
#        print "uefi_version => $uefi_version\n";
#        print "fcode_version => $fcode_version\n";
#        print "branch => $branch\n";
	    push @psid2bin, {"psid" => $psid, "arch" => 'noarch',"fwfile" => rmpath($bins[$i]), "exprom_file" => "", "opns" => $pn, "version" => $version, "size" => (stat(ospath("$tmpdir/$bins[$i]")))[7], "offset" => ( $binsize ), "metadata_size" => $metadatasz, "description" => $description, "clp_version" => $clp_version, "pxe_version" => $pxe_version, "uefi_version" => $uefi_version, "fcode_version" => $fcode_version, "branch" => $branch};

        if (!exists($psid2fl{$psid})) {
            $psid2fl{$psid} = rmpath($bins[$i]);
        } else {
            print STDERR "PSID=$psid appears more than once, Found in binary files: $orig_file{$psid2fl{$psid}} and ".$orig_file{rmpath($bins[$i])}."\n";
            $res++;
        }
    }
    print "\n";
    close OUTocF;
    close OUTF;
    return $res;
}


sub rmpath
{
    my ($f) = @_;

    if ($f =~ /\/([^\/]*)$/) {
        $f = $1;
    }

    return $f;
}

        
sub create_map_file
{
    open OUT,">".ospath("$tmpdir/MAP_FILE");
    binmode OUT;
    my $sz = @psid2bin;
    my $sum = 0;
    for (my $i = 0; $i < @psid2bin; $i++) {
        my $board_type_id = pack("a[32]", $psid2bin[$i]{"psid"});
        my $numfiles = 1;
        $numfiles++ if ($psid2bin[$i]{"pxe_version"});
        $numfiles++ if ($psid2bin[$i]{"clp_version"});
        $numfiles++ if ($psid2bin[$i]{"uefi_version"});
        $numfiles++ if ($psid2bin[$i]{"fcode_version"});
        my $files_per_psid = pack("C",$numfiles);
        my $resvd = pack("C",0);
        my $metadata_sz = pack("n", 152);
        print OUT $board_type_id;
        print OUT $files_per_psid;
        print OUT $resvd;
        print OUT $metadata_sz;
        my $metadata_hdr0 = pack("C", 0x1);
        my $metadata_hdr1 = pack("C", 0x0);
        my $metadata_hdr2; # number of keys in metadata
        # key0 is PN
        # key1 is description
        # key2 is branch - optional
        my $md_length2 += length($psid2bin[$i]{"branch"}) + 1; 
        if ( $md_length2 > 1) # check if branch is presented
        {
             $metadata_hdr2 = pack("n", 3);
        } else {
             $metadata_hdr2 = pack("n", 2);
        }
        print OUT $metadata_hdr0;
        print OUT $metadata_hdr1;
        print OUT $metadata_hdr2;
        my $md_key0 = pack("a[3]", "PN");
        my $md_length0 += length($psid2bin[$i]{"opns"}) + 1;
        my $md_key1 = pack("a[12]", "DESCRIPTION");
        my $md_length1 += length($psid2bin[$i]{"description"}) + 1;
        my $md_length_left = 152 - $md_length0 - $md_length1 - 3 - 12 - 4 ;
        my $md_value1;
        if ( $md_length2 > 1 ) # check if branch is presented
        {
            $md_length_left = $md_length_left - $md_length2 - 7;
        }
        if ( $md_length_left < 0 )
        { # check if size of the keys combined exceeds total size
            $md_length1 += $md_length_left; # subtracts from description
            $md_length_left = 0;
            my $tmp_md_length1 = $md_length1 - 4; # place for "..." and null
            my $tmp_md_value1 = pack("a[$tmp_md_length1]a3", $psid2bin[$i]{"description"}, "...");
            $md_value1 = pack("a[$md_length1]", $tmp_md_value1);
        } else {
            $md_value1 = pack("a[$md_length1]", $psid2bin[$i]{"description"});
        }
        my $md_value0 = pack("a[$md_length0]", $psid2bin[$i]{"opns"});
        my $md_value_left = pack("a[$md_length_left]", "");
        print OUT $md_key0;
        print OUT $md_value0;
        print OUT $md_key1;
        print OUT $md_value1;
        if ( $md_length2 > 1 )
        { # adding branch if presented
            my $md_key2 = pack("a[7]", "BRANCH");
            my $md_value2 = pack("a[$md_length2]", $psid2bin[$i]{"branch"});
            print OUT $md_key2;
            print OUT $md_value2;
        }
        print OUT $md_value_left;

        my $file_cnt = 1;
        my $toc_offset = pack("N", $sum);
        my $image_type = pack("n",1);
        my $select_tag = pack("a[32]", "");
        print OUT $toc_offset;
        print OUT $image_type;
        print OUT $resvd;
        my $group_id = pack("C", $file_cnt);
        print OUT $group_id;
        print OUT $select_tag;
        $sum += $psid2bin[$i]{"metadata_size"} + 24;

        if ($psid2bin[$i]{"pxe_version"}) {
            $file_cnt++;
            my $toc_offset = pack("N", $sum);
            my $image_type = pack("n",1);
            my $select_tag = pack("a[32]", "");
            print OUT $toc_offset;
            print OUT $image_type;
            print OUT $resvd;
            $group_id = pack("C", $file_cnt);
            print OUT $group_id;
            print OUT $select_tag;
            $sum += 24;
        }
        if ($psid2bin[$i]{"clp_version"}) {
            $file_cnt++;
            my $toc_offset = pack("N", $sum);
            my $image_type = pack("n",1);
            my $select_tag = pack("a[32]", "");
            print OUT $toc_offset;
            print OUT $image_type;
            print OUT $resvd;
            $group_id = pack("C", $file_cnt);
            print OUT $group_id;
            print OUT $select_tag;
            $sum += 24;
        }
        if ($psid2bin[$i]{"uefi_version"}) {
            $file_cnt++;
            my $toc_offset = pack("N", $sum);
            my $image_type = pack("n",1);
            my $select_tag = pack("a[32]", "");
            print OUT $toc_offset;
            print OUT $image_type;
            print OUT $resvd;
            $group_id = pack("C", $file_cnt);
            print OUT $group_id;
            print OUT $select_tag;
            $sum += 24;
        }
        if ($psid2bin[$i]{"fcode_version"}) {
            $file_cnt++;
            my $toc_offset = pack("N", $sum);
            my $image_type = pack("n",1);
            my $select_tag = pack("a[32]", "");
            print OUT $toc_offset;
            print OUT $image_type;
            print OUT $resvd;
            $group_id = pack("C", $file_cnt);
            print OUT $group_id;
            print OUT $select_tag;
            $sum += 24;
        }
    }

    close OUT;
    return 0;
}


sub swap_be32
{
    my ($x) = @_;

    my ($a, $b, $c, $d) = ($x & 0xff, $x>>8 & 0xff, $x>>16 & 0xff, $x>>24 & 0xff);
    $x = 0;
    $x |= $a; $x <<= 8;
    $x |= $b; $x <<= 8;
    $x |= $c; $x <<= 8;
    $x |= $d;

    return $x;
}

#    u_int8_t type;
#    u_int8_t flags;
#    u_int16_t num_entrs;
#    u_int32_t size;
   
sub create_mfa
{
    my ($mlxarchive, $mapfile, $tocfile, $datafile) = @_;

    my $header_signature = pack("a4","MFAR"); #MFAR signature
    my $header_version = pack("V",swap_be32($mfa_version)); # version
    my $reserved = pack("V", 0);

    open OUTF, ">$mlxarchive" or die("Couldn't open $mlxarchive for writing!\n");
    binmode OUTF;
    print OUTF $header_signature;
    print OUTF $header_version;
    print OUTF $reserved;
    print OUTF $reserved;

    #MAP section
    my $map_tlv_type = pack("C",0x1); # type: Map Section
    my $map_tlv_flag = pack("C",$use_xz); # flags
    $reserved = pack("C",0x0);
    my $map_tlv_size = pack("N",(stat("$mapfile"))[7]); # map value size

    print OUTF $map_tlv_type;
    print OUTF $reserved;
    print OUTF $reserved;
    print OUTF $map_tlv_flag;
    print OUTF $map_tlv_size;

    my $buffer;
    open INF, "<$mapfile" or die("Couldn't open $mapfile for reading!\n");
    binmode INF;
    while (read(INF,$buffer,65536)) {
	    print OUTF $buffer;
    }
    close INF;

    #TOC section
    my $toc_tlv_type = pack("C",0x2); # type: TOC Section
    my $toc_tlv_flag = pack("C",$use_xz); # flags
    my $toc_tlv_size = pack("N",(stat("$tocfile"))[7]); # TOC value size

    print OUTF $toc_tlv_type;
    print OUTF $reserved;
    print OUTF $reserved;
    print OUTF $toc_tlv_flag;
    print OUTF $toc_tlv_size;

    $buffer = "";
    open INF, "<$tocfile" or die("Couldn't open $tocfile for reading!\n");
    binmode INF;
    while (read(INF,$buffer,65536)) {
	    print OUTF $buffer;
    }
    close INF;

    #Data section
    my $data_tlv_type = pack("C",0x3); # type: Data Section
    my $data_tlv_flag = pack("C",$use_xz); # flags
    my $data_tlv_size = pack("N",(stat($datafile))[7]); # DATA value size
    
    print OUTF $data_tlv_type;
    print OUTF $reserved;
    print OUTF $reserved;
    print OUTF $data_tlv_flag;
    print OUTF $data_tlv_size;

    open INF, "<$datafile" or die("Couldn't open $datafile for reading!\n");
    binmode INF;
    while (read(INF,$buffer,65536)) {
	    print OUTF $buffer;
    }
    close INF;

    close OUTF;

    return 0;
}


sub add_crc
{
    my ($mlxarchive, $argcrc) = @_;
    my $mlxarchive_crc = pack("N",hex($argcrc));
    open OUTF, ">>$mlxarchive" or die("Couldn't open $mlxarchive for writing!\n");
    binmode OUTF;
    print OUTF $mlxarchive_crc;
    close OUTF;
}

sub usage
{
    print "$0 -p <package name> -s <source_dir>\n";
    print " The -s option may be used more than once to specify more than one source directory.\n";
}


sub checkRefineArgs
{
    my ($arg, $option) = @_;
    my $fixedPath;

    if (!defined $arg) {
        die("-E- Missing argument for option $option\n");
    }
    if ($arg =~ m/^\s*-.*/) {
        die("-E- Missing argument for option $option\n");
    }
    $fixedPath = abs_path($arg);
    if (!defined $fixedPath) {
        die("-E- Could not evaluate path $arg, for option $option check if path exist\n");
    }
    if (! -d $fixedPath) {
       die("-E- Directory $arg does not exist\n");
    }
    return $fixedPath;
}
