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 Cwd;
use Cwd 'abs_path';
use Config;
use File::Temp qw(tempdir tempfile mktemp);
use File::Basename;

my $debug = 0;
my $hhelp = 0;
my @srcdirs;
my $intermediate_mfa;
my $package_name;
my $package_dirname;
my $ignore_verify_mfa = 0;
my $template_dir = "last_stable";
my $exedir = abs_path($0);
my $phy_support = 0;
my $phy_img  = "";
my $certificate_file = "";
my @extra_arguments;
my $is_extra_arguments = 0;

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

my $platform;
my $outdir = ".";




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

for(my $i=0; $i<(@ARGV); $i++) {
    if ($ARGV[$i] eq "-h") {
        usage();
        exit 0;
    } elsif ($ARGV[$i] eq "--hhelp") {
        $hhelp = 1;
        usage();
        exit 0;
    } elsif ($ARGV[$i] eq "-t") {
        $i++;
        $template_dir = checkRefineArgs($ARGV[$i], "-t");
    } elsif (($ARGV[$i] eq "--source-dir") || ($ARGV[$i] eq "-s")) {
        $i++;
        push @srcdirs, checkRefineArgs($ARGV[$i], "--source-dir");
    } elsif ($ARGV[$i] eq "--out-dir") {
        $i++;
        $outdir = checkRefineArgs($ARGV[$i], "--out-dir");
    } elsif ($ARGV[$i] eq "--sfx-name") {
        $i++;
        $package_name = $ARGV[$i];
    } elsif ($ARGV[$i] eq "--platform") {
        $i++;
        $platform = $ARGV[$i];
    } elsif ($ARGV[$i] eq "--debug") {
        $debug = 1;
    } elsif ($ARGV[$i] eq "--bins2mfa") {
        $intermediate_mfa = 1;
    } elsif ($ARGV[$i] eq "--noverify") {
        $ignore_verify_mfa = 1;
    } elsif ($ARGV[$i] eq "--phy-support") {
        $phy_support = 1;
    } elsif ($ARGV[$i] eq "--phy-img") {
        $i++;
        $phy_img = $ARGV[$i];
        if (!defined  $phy_img) {
            die("-E- Missing argument for option --phy-img\n");
        }
        if ($phy_img =~ m/^\s*-.*/) {
            die("-E- Missing argument for option --phy-img\n");
        }
        unless (-e $phy_img) {
            die("-E- $phy_img dose not exist!");
        } 
    } elsif ($ARGV[$i] eq "--certificate") {
        $i++;
        $certificate_file = $ARGV[$i];
        if (!defined  $certificate_file) {
            die("-E- Missing argument for option --certificate\n");
        }
        if ($certificate_file =~ m/^\s*-.*/) {
            die("-E- Missing argument for option --certificate\n");
        }
        unless (-e $certificate_file) {
            die("-E- $certificate_file dose not exist!");
        }
    } elsif ($ARGV[$i] eq "--extra-args") {
        $is_extra_arguments = 1;
        $i++;
        if (!defined $ARGV[$i]) {
            die("-E- Missing argument for option --extra-args\n");
        }
        @extra_arguments = split(',', $ARGV[$i]);
    } else {
        print STDERR "-E- Unknown option $ARGV[$i]\n";
        usage();
        exit(255);
    }
}

#-- Validate Arguments  -------------------------------------------------------
if (!defined($platform)) {
    $platform = "";
}

if ($phy_support && $phy_img eq "") {
    print STDERR "-E- Please specify --phy-img directory\n";
    exit(255);    
}

if (@srcdirs == 0) {
    print STDERR "-E- Must specify source directory. Please run with -h flag for help.\n";
    exit(255);
}

if (!defined($intermediate_mfa)) {
    my $ls_bin_cnt = 0;
    my $ls_mfa_cnt = 0;
    my $ls_other_cnt = 0;
    my @files;

    for (my $k = 0; $k < @srcdirs; $k++) {
        my @lfiles = getDirListing(ospath("$srcdirs[$k]"));
        @files = (@files, @lfiles);
    }

    for (my $i = 0; $i < @files; $i++) {
        if (($files[$i] eq ".") || ($files[$i] eq "..")) {
            next;
        }
        if($files[$i] =~ /\.bin$/) {
            $ls_bin_cnt++;
        } elsif($files[$i] =~ /\.mfa$/) {
            $ls_mfa_cnt++;
        } else {
            $ls_other_cnt++;
        }
    }
    if ($ls_other_cnt > 0) {
        #print "-W- Found unrecongnized file types in source directory.\n";
        #print "Please provide files with extensions '.bin' or '.mfa' (but not both) in the source directory.\n";
    }
    if (($ls_bin_cnt > 0) && ($ls_mfa_cnt > 0)) {
        die("-E- Found a mixture of .mfa and .bin files in the source directory.\nPlease provide one type of format only in the FW source directory\n");
    }
    if (($ls_bin_cnt == 0) && ($ls_mfa_cnt == 0)) {
        die("-E- No FW files found in source directory.\n");
    }

    $intermediate_mfa = 0;
    if ($ls_bin_cnt > 0) {
        $intermediate_mfa = 1;
    }
}


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

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


sub zipcmd
{
    my $zcmd;

    if ($Config{osname} =~ /linux/) {
        $zcmd = "zip";
    } else {
        $zcmd = ospath("$exedir/zip.exe");
    }
    print "DEBUG xzcmd: $zcmd\n" if ($debug);
    return $zcmd;
}


sub copycmd
{
    my $copycmd;

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


sub getCurrentDir
{
    my $currdir;

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


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

    if ($Config{osname} =~ /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);
    return @list;
}


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

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

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


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

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

    if ($path =~ /( |\+)/) {
        if ($path !~ /\"/) {
            $path = '"'.$path.'"';
        }
    }
    print "DEBUG ospath: $path\n" if ($debug);
    return $path;
}


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

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


sub mktempDir
{
    my $tmpdir = tempdir();
    return $tmpdir;
}


#-- Main  ---------------------------------------------------------------------

if (!defined($package_name)) {
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
    $package_name = sprintf("mlxfwmanager-%04d%02d%02d",$year + 1900, $mon + 1, $mday);
    my $odir = $outdir;
    if ($platform ne "") {
        $odir .= "/$platform";
    }
    my @pkls = getDirListing(ospath($odir));
    my $max = 0;
    if (@pkls) {
        for (my $i = 0; $i < @pkls; $i++) {
            my $fl = $pkls[$i];
            if ($fl =~ /$package_name\-(\d+)[^\d]?/) {
                my $m = int($1);
                $max = $m if ($m > $max);
            }
        }
    }
    $max++;
    $package_name .= "-$max";
    if ($Config{osname} !~ /linux/) {
        $package_name .= ".exe";
    }
} else {
    if ($platform =~ /^win/) {
        $package_name .= ".exe";
    }
}

my $rc;
if (!(-e $outdir)) {
    $rc = mkdir($outdir);
    die("-E- Failed to create dir $outdir\n") if (!$rc);
}

if ($platform ne "") {
    if (-e "$outdir/$platform") {
        #`rm -rf $outdir/$platform`;
    } else {
        $rc = mkdir(ospath("$outdir/$platform"));
        die("-E- Failed to create dir ". ospath("$outdir/$platform") ."\n") if (!$rc);
    }
}

my $gLogname = $package_name;
$gLogname =~ s/\.[^\.]+$//;
$gLogname .=".log";
if ($platform ne "") {
    $gLogname = "$outdir/$platform/".$gLogname;
} else {
    $gLogname = "$outdir/".$gLogname;
}
$gLogname = ospath_int($gLogname);
open (GLOG,'>', $gLogname) or die("-E- Unable to create log file $gLogname\n");

my $package_path = "$outdir";
if ($platform ne "") {
    $package_path .= "/$platform";
}
$package_path .= "/$package_name";
ospath($package_path);

$rc = create_package($package_path, @srcdirs);

close GLOG;
print "\nLog name: $gLogname\n";
die("Failed to create package!\n") if( $rc );


#-- Functions------------------------------------------------------------------
sub usage
{
    print "$0 --source-dir <FW images directory>  --out-dir <output directory>  [--sfx-name <sfx file name>] [--phy-support --phy-img <phy-img>] [--extra-args <args>]\n" if(!$hhelp);
    print "$0 --source-dir <FW images directory>  --out-dir <output directory>  [--sfx-name <sfx file name>] [--platform <platform>] [--extra-args <args>]\n" if ($hhelp);
    print "    --source-dir:  Directory containing Mellanox firmware images to be included in the package\n";
    print "                   This option may be used more than once to specify more than one source directory.\n";
    print "    --out-dir:     Specifies output directory\n";
    print "    --certificate: SSL certificate\n";
    print "    --phy-support: Generate extractor with mlxphyburn support.\n";
    print "    --phy-img    : PHY firmware image.\n";
    print "    --platform:    Specify the platform you want for the created executable: linux64, linux32, win64, win32, esxi_6_5_native, esxi_7_0_native, PPC64, PPC64LE, ARM, fbsd10_64, fbsd10_32, fbsd10_1_64, fbsd11_64\n" if ($hhelp);
    print "    --sfx-name:    The self-extracting executable filename. Default name is mlxfwmanager-YYYYMMDD-<build number>,\n";
    print "                   where build number is the previous maximum build number existing in the output directory\n";
    print "    --extra-args:  Extra args passed to mlxfwmanager default arguments, format is comma seprated if multiple args, Example --extra-args --ret-lvim,--online\n";
}


sub printERR
{
    my $str = "@_";
    print STDERR $str;
    print GLOG $str;
}


sub printNLog
{
    my $str = "@_";
    print $str;
    print GLOG $str;
}


sub component_name
{
    my ($name) = @_;
    #-- DB ------------------------------------------------------------------------
    my %component_name_win64 = (
       "mlx_sfx_stub" => "mlx_sfx_stub.exe",
       "mlxfwmanager" => "i2cbrdg.dll libgcc_s_seh-1.dll libmcables-1.dll libmlxfwops-1.dll libmtcr-1.dll libstdc++-6.dll libwinpthread-1.dll libreg_access-1.dll mlxfwmanager.exe libcmdif-1.dll libtools_sync-1.dll mst64.sys",
       "mlxphyburn"   => "mlxphyburn.exe",
    );

    my %component_name_win32 = (
       "mlx_sfx_stub" => "mlx_sfx_stub.exe",
       "mlxfwmanager" => "i2cbrdg.dll libgcc_s_sjlj-1.dll libmcables-1.dll libmlxfwops-1.dll libmtcr-1.dll libstdc++-6.dll libwinpthread-1.dll libreg_access-1.dll mlxfwmanager.exe libcmdif-1.dll libtools_sync-1.dll mst32.sys",
       "mlxphyburn"   => "mlxphyburn.exe"
    );

    my %component_name_linux32 = (
        "mlx_sfx_stub" => "mlx_sfx_stub",
        "mlxfwmanager" => "mlxfwmanager",
        "mlxphyburn"   => "mlxphyburn"
    );

    my %component_name_linux64 = (
        "mlx_sfx_stub" => "mlx_sfx_stub",
        "mlxfwmanager" => "mlxfwmanager",
        "mlxphyburn"   => "mlxphyburn"
    );

    my %component_name_PPC64 = (
        "mlx_sfx_stub" => "mlx_sfx_stub",
        "mlxfwmanager" => "mlxfwmanager",
        "mlxphyburn"   => "mlxphyburn"
    );

    my %component_name_PPC64LE = (
        "mlx_sfx_stub" => "mlx_sfx_stub",
        "mlxfwmanager" => "mlxfwmanager",
        "mlxphyburn"   => "mlxphyburn"
    );

    my %component_name_esxi6_5_native = (
        "mlx_sfx_stub" => "mlx_sfx_stub",
        "mlxfwmanager" => "mlxfwmanager_vmware.py mlxfwmanager",
        "mlxphyburn"   => "mlxphyburn"
    );

    my %component_name_esxi7_0_native = (
        "mlx_sfx_stub" => "mlx_sfx_stub",
        "mlxfwmanager" => "mlxfwmanager_vmware.py mlxfwmanager",
        "mlxphyburn"   => "mlxphyburn"
    );

    my %component_name_ARM = (
        "mlx_sfx_stub" => "mlx_sfx_stub",
        "mlxfwmanager" => "mlxfwmanager",
        "mlxphyburn"   => "mlxphyburn"
    );

    my %component_name_freebsd = (
        "mlx_sfx_stub" => "mlx_sfx_stub",
        "mlxfwmanager" => "mlxfwmanager",
        "mlxphyburn"   => "mlxphyburn"
    );

    my %component_name_notspecified = (
        "mlx_sfx_stub" => "mlx_sfx_stub" . (($Config{osname} =~ /linux/)?"":".exe"),
        "mlxfwmanager" => "mlxfwmanager" . (($Config{osname} =~ /linux/)?($phy_support)? "": "_pci":".exe"),
        "mlxphyburn"   => "mlxphyburn"   . (($Config{osname} =~ /linux/)?"":".exe")
    );

    if ($platform eq "") {
        return $component_name_notspecified{$name};
    }
    if ($platform eq "win64") {
        return $component_name_win64{$name};
    }
    if ($platform eq "win32") {
        return $component_name_win32{$name};
    }
    if ($platform eq "linux32") {
        return $component_name_linux32{$name};
    }
    if ($platform eq "linux64") {
        return $component_name_linux64{$name};
    }
    if (($platform eq "PPC64") || ($platform eq "ppc64")) {
        return $component_name_PPC64{$name};
    }
    if (($platform eq "PPC64LE") || ($platform eq "ppc64le")) {
        return $component_name_PPC64LE{$name};
    }
    if ($platform eq "esxi_6_5_native") {
        return $component_name_esxi6_5_native{$name};
    }
    if ($platform eq "esxi_7_0_native") {
        return $component_name_esxi7_0_native{$name};
    }
    if ($platform eq "fbsd10_64" || $platform eq "fbsd10_32" || $platform eq "fbsd10_1_64" || $platform eq "fbsd11_64") {
        return $component_name_freebsd{$name};
    }
    if ($platform eq "ARM") {
        return $component_name_ARM{$name};
    }
    die("Unknown component name $name on '$platform'\n");
}


sub create_package
{
    my ($package_name, @srcdirs) = @_;
    my $res = -1;
    my $rc;
    my $intermediate_mfa_created = 0;
    my $intermediate_mfa_dir;
    my $tempwork_dir;
    my $tmpdir = mktempDir();
    my $xdir = $exedir;
    my $phy_temp_dir = "";

    if ($phy_support) {
       $phy_temp_dir = "$tmpdir/phy_mfa";
       printNLog("Creating $phy_temp_dir direcotry\n");
       $rc = mkdir($phy_temp_dir);
       die("-E- Failed to create dir $phy_temp_dir\n") if (!$rc);
       $phy_temp_dir = ospath($phy_temp_dir);
    }
       
    printNLog("Package name: $package_name\n");
    printNLog("Contents:\n");
    $rc = open(PKG, ">".ospath_int($package_name));
     
    if (!defined($rc)) {
        printERR("Failed to open output file : $package_name\n");
        return -1;
    };
    binmode PKG;

    printNLog("Source dirs: @srcdirs\n");
    my $fwmanager_dir = "$xdir";
    my $sfx_stub = "$xdir/";
    if ($platform ne "") {
        $sfx_stub .= "$platform/";
        $fwmanager_dir .= "/$platform";
    }

    if ($certificate_file eq "") {
        $certificate_file = (($Config{osname} =~ /linux/)?"/etc/mft/ca-bundle.crt":"$xdir//ca-bundle.crt");
    }

    unless (-e $certificate_file) {
        printNLog("-W- Certificate : $certificate_file dose not exist!, Certificate is very important for online update\n");
    } else {
        $certificate_file = ospath($certificate_file);
        copyFile($tmpdir, $certificate_file);
    }
    $sfx_stub .= component_name("mlx_sfx_stub");
    $sfx_stub = ospath_int($sfx_stub);
    printNLog("sfx_stub file: $sfx_stub\n");
    my $sfx_offset = (stat($sfx_stub))[7];
    my $mod = int($sfx_offset) % 4;
    if ($mod) {
        $sfx_offset += 4 - $mod;
    }
    $sfx_stub = ospath_int($sfx_stub);
    $rc = open(EXTR, "<", $sfx_stub);
    if (!defined($rc)) {
        printERR("Failed to open input file $sfx_stub\n");
        goto clean_up;
    }
    binmode EXTR;
    my $buf;
    while (my $n = read(EXTR, $buf, 1024)) {
        print PKG $buf;
    }
    close(EXTR);

    if ($mod) {
        my $deltamod = 4 - $mod;
        my $modpad = pack("a[$deltamod]", 0); 
        print PKG $modpad;
    }

    $tempwork_dir = mktempDir();
    if ($?) {
        printERR("Failed to create temporary work directory\n");
        $tempwork_dir = undef;
        goto clean_up;
    }

    my @image_src_path = @srcdirs;
    if ($intermediate_mfa) {
        $intermediate_mfa_dir = $tempwork_dir;
        @image_src_path = ();
        push @image_src_path, $intermediate_mfa_dir;
        $intermediate_mfa_created = 1;
        printNLog("Creating intermediate MFA archive from binary files:\n");
        my $tmp_mfa = ospath("$tempwork_dir/srcs.mfa");
        my $srcdir_args = "";
        for (my $k = 0; $k < @srcdirs; $k++) {
            my @srcls = getDirListing(ospath($srcdirs[$k]));
            my $srclsstr = join("\n", @srcls);
            printNLog("$srclsstr\n");
            $srcdir_args .= " -s ".ospath($srcdirs[$k]);
        }
        my $mfatool = ospath("$xdir/mlx_mfa_gen");
        printNLog("mfa tool: $mfatool\n");
        my $dbgstr = "";
        $dbgstr = " -debug" if($debug);
        my $cmd0 = "$mfatool -p $tmp_mfa $srcdir_args$dbgstr";
        printNLog("mfa cmd: $cmd0\n");
        system($cmd0);
        if ($?) {
            printERR("Failed to create archive\n");
            goto clean_up;
        }
    }
   
    for (my $k = 0; $k < @image_src_path; $k++) {
        $rc = copyFiles($image_src_path[$k], $tmpdir, '.mfa$');
        if ($rc == 0) {
            printERR("No mfa files were found in $image_src_path[$k]\n");
            goto clean_up;
        }
        if ($rc < 0) {
            printERR("Failed to copy from $image_src_path[$k]\n");
            goto clean_up;
        }
    }

    if ($platform eq "") {
        my @srcfls;
        if ($Config{osname} =~ /linux/) {
            @srcfls = qw(mlxfwmanager);
        } else {
            @srcfls = split / /, "i2cbrdg.dll libmcables-1.dll libmlxfwops-1.dll libmtcr-1.dll libstdc++-6.dll libwinpthread-1.dll libreg_access-1.dll mlxfwmanager.exe libcmdif-1.dll libtools_sync-1.dll";
            my $mstsys_cnt = 0;
            $rc = copyFiles($fwmanager_dir, $tmpdir, "^mst32.sys\$");
            $mstsys_cnt++ if($rc > 0);
            $rc = copyFiles($fwmanager_dir, $tmpdir, "^mst64.sys\$");
            $mstsys_cnt++ if($rc > 0);
            if ($mstsys_cnt == 0) {
                printERR("Failed to copy mstXX.sys\n");
                goto clean_up;
            }
            $rc = copyFiles($fwmanager_dir, $tmpdir, "^libgcc_s_[a-z]+-1.dll\$");
            if ($rc <= 0) {
                printERR("Failed to copy libgcc dll\n");
                goto clean_up;
            }
        }
        for (my $k = 0; $k < @srcfls; $k++) {
            copyFile($tmpdir, "$fwmanager_dir/$srcfls[$k]");
        }
    } else {
        my @srcfls = split / /, component_name("mlxfwmanager");
        for (my $k = 0; $k < @srcfls; $k++) {
            copyFile($tmpdir, "$fwmanager_dir/mlxfwmanager/$srcfls[$k]");
        }
    }

    if ($phy_support) {
       printNLog("Copying $phy_img to $phy_temp_dir\n");
       copyFile($phy_temp_dir, $phy_img);
       if ($platform eq "") {
           my @phySrcFiles;
           if ($Config{osname} =~ /linux/) {
               @phySrcFiles = qw(mlxphyburn);
           } else {
               @phySrcFiles = qw(mlxphyburn.exe);
           }
           for (my $k = 0; $k < @phySrcFiles; $k++) {
               $rc = copyFiles($fwmanager_dir, $tmpdir, "^$phySrcFiles[$k]\$");
               if ($rc <= 0) {
                   printERR("Failed to copy ".ospath("$fwmanager_dir/$phySrcFiles[$k]")."\n");
                   goto clean_up;
               }
           }
       } else {
           my $mlxphyburn = component_name("mlxphyburn");
           copyFile($tmpdir, "$fwmanager_dir/mlxfwmanager/$mlxphyburn");
       }       
    } 
    my $zip_name = ospath("$tempwork_dir/zippackage.zip");
    $rc = gzip_srcdir($tmpdir, $zip_name, $tempwork_dir);
    if ($rc) {
        printERR("Failed to create zipped file $zip_name\n");
        goto clean_up;
    }
    $rc = open(ZP, "<".ospath_int($zip_name));
    if (!defined($rc)) {
        printERR("Failed to open zipped file $zip_name\n");
        goto clean_up;
    }
    binmode ZP;
    $buf = "";
    my $totn = 0;
    while (my $n = read(ZP, $buf, 1024)) {
        print PKG $buf;
        $totn += $n;
    }
    close(ZP);

    $mod = int($totn) % 4;
    my $deltamod = 0;
    if ($mod) {
        $deltamod = 4 - $mod;
        my $modpad = pack("a[$deltamod]", 0); 
        print PKG $modpad;
    }
    my $zip_size = $deltamod + int($totn);
    my $sfx_cmd;
    if ($platform eq "esxi_6_5_native" || $platform eq "esxi_7_0_native") {
       $sfx_cmd = "mlxfwmanager_vmware.py ESXI_NATIVE -u --log-on-update --ssl-certificate \%ca-bundle.crt\% \%current-dir\%"; 
    } else {
        $sfx_cmd = "mlxfwmanager -u --log-on-update --ssl-certificate \%ca-bundle.crt\% \%current-dir\%";
    }
    if (($platform =~ /^win/) || (($platform eq "") && ($Config{osname} !~ /linux/))) {
        $sfx_cmd = "mlxfwmanager.exe -u --log-on-update --ssl-certificate \%ca-bundle.crt\% \%current-dir\%";
    }

    if ($is_extra_arguments != 0) {
        for my $arg (@extra_arguments) {
            $sfx_cmd = "$sfx_cmd $arg";
        }
    }
    $sfx_cmd = "$sfx_cmd  \%argv\%";
    my $sfx_cmd_size = 256;
    if (length $sfx_cmd > $sfx_cmd_size -1) {
        printERR("Command line exceeded maximum allowed length which is $sfx_cmd_size bytes\n");
        goto clean_up;
    }
    printNLog("sfx auto-run command:\n $sfx_cmd\n");
    my $sfx_exec_cmd = pack("a[$sfx_cmd_size]", $sfx_cmd); 
    print PKG $sfx_exec_cmd;

    my $packed_zip_size = pack("N", $zip_size);
    print PKG $packed_zip_size;

    if ($phy_support) {
        my $phy_pattern = "MLNX_EXTRA_TOOL_PATT" . ":";
        my $packed_phy_pattern = pack("a[32]", $phy_pattern);
        print PKG $packed_phy_pattern;

        my $phy_cmd = "mlxphyburn \%device\% -i ./phy_mfa/" . basename($phy_img) . " b";
        if (($platform =~ /^win/) || (($platform eq "") && ($Config{osname} !~ /linux/))) {
            $phy_cmd = "mlxphyburn.exe \%device\% -i ./phy_mfa/" . basename($phy_img) . " b";
        }
        printNLog("\nmlxphyburn auto-run command:\n $phy_cmd\n");
        my $packed_phy_cmd = pack("a[256]", $phy_cmd);
        print PKG $packed_phy_cmd;        
    }
    
    my $version_pattern = "MLNX_VERSION_PATT" . ":"; # In case this script was in the attachment.
    my $packed_version_pattern = pack("a[32]", $version_pattern);
    print PKG $packed_version_pattern;

    my $current_version = "2.0";  # Make sure this version match sfx_stub version, both of them should be compatible.
    my $packed_current_version =  pack("a[8]", $current_version);
    print PKG $packed_current_version;

    $res = 0;

clean_up:
    close(PKG);

    if (defined($tempwork_dir)) {
        removeTree($tempwork_dir);
    }

    if ($res) {
        unlink($package_name);
    } else {
        chmod(0755, $package_name);
        unlink($zip_name);
    }
    removeTree("$tmpdir");
    return $res;
}

sub copyFile
{
    my ($dest, $file) = @_;
    printNLog("Adding file: ". $file ."\n");
    my $cmd;
    if ($Config{osname} =~ /linux/) {
        $cmd = copycmd(). " " . "\"$file\"" . " " . $dest;
    } else {
        $cmd = copycmd(). " " . ospath("$file") . " " . $dest;
    }
    system($cmd);
    if($?) {
        die("-E- Failed to copy $file to $dest\n");        
    }
    return 0;
}
sub copyFiles
{
    my ($src, $tmpdir, $pattern) = @_;
    my $res = 0;
    my $count = 0;

    my @files;

    @files = getDirListing(ospath($src));

    for (my $i = 0; $i < @files; $i++) {
        if (defined($pattern)) {
            if ($files[$i] !~ /$pattern/) {
                next;
            }
        }
        if (!(-f ospath_int("$src/$files[$i]"))) {
            next;
        }
        printNLog("Adding file: ". ospath("$src/$files[$i]")."\n");
        my $cmd = copycmd()." ". ospath("$src/$files[$i]")." ". $tmpdir;
        print "DEBUG copyFiles: $cmd\n" if ($debug);
        system($cmd);
        if($?) {
            printERR("Failed to copy $files[$i] to $tmpdir\n");
            $res++;
        }
        $count++;
    }
    if ($res) {
        return -$res;
    }
    return $count;
}


sub gzip_srcdir
{
    my ($srcdir, $zipname, $tempwork_dir) = @_;
    my $cmd;
    my $out;
    my $res;

    printNLog("Creating zip $zipname\n");
    print "Zipping dir: $srcdir\n" if($debug);
    if ($Config{osname} =~ /linux/) {
        $cmd = "which zip 2> /dev/null";
        $out = `$cmd`;
        $res = $?;
        if ($res != 0) {
            $out = "Could not find zip utility, please install it!";
        } else {
            $cmd = "cd $srcdir; zip -9 -r $zipname .";
            $out = `$cmd`;
            $res = $?;
        }
    } else {
        $res = createZipper($tempwork_dir);
        return $res if ($res != 0);
        my $old_dir = getCurrentDir();
        chdir($srcdir);
        $cmd = "cscript ".ospath("$tempwork_dir/zipper.vbs"). " $srcdir $zipname";
        $out = `$cmd`;
        $res = $?;
        chdir($old_dir);
    }
    printNLog("$out\n");
    return $res;
}


sub verifyMFAList
{
    my ($srcdir) = @_;

    my $res = 0; #Retrun 0 //not failing on this for now

    #Verify PSID uniqueness in MFAs
    printERR("WARNING: PSID uniqueness is not checked! Please make sure all provided images contain unique PSIDs\n");

    return $res;
}


sub createZipper
{
    my ($tmpw) = @_;

    my $zippercode = <<ZIPPERCODE;
'Get command-line arguments.
Set objArgs = WScript.Arguments
Set fso = CreateObject("Scripting.FileSystemObject")
sSourceFile = fso.GetAbsolutePathName(objArgs.item(0))
sTargetFile = fso.GetAbsolutePathName(objArgs.item(1))
'InputFolder = objArgs(0)
InputFolder = sSourceFile
ZipFile = sTargetFile

'Create empty ZIP file.
CreateObject("Scripting.FileSystemObject").CreateTextFile(ZipFile, True).Write "PK" & Chr(5) & Chr(6) & String(18, vbNullChar)

Set objShell = CreateObject("Shell.Application")

'wscript.echo (InputFolder)
'wscript.echo (ZipFile)
Set source = objShell.NameSpace(InputFolder).Items()
'wscript.echo source.count
'wscript.echo source.item(0)

Set objTarget = objShell.NameSpace(ZipFile)
objTarget.CopyHere source, 4

'Wait for file to begin
WScript.Sleep 2000
'Wait until copy finishes
Do Until objTarget.Items().Count = source.Count
    WScript.Sleep 300
Loop
ZIPPERCODE

    my $filename = ospath("$tmpw/zipper.vbs");
    my $rc = open(OUTP, ">$filename");
    if (!defined($rc)) {
        printERR("-E- Failed to open $filename for writing\n");
        return 1;
    }
    print OUTP $zippercode;
    close(OUTP);
    return 0;
}

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;
}
