#! /usr/bin/perl -w
use strict;
use POSIX;

my $SPECS_DIR	= $ENV{'SPEC_DIR'}?$ENV{'SPEC_DIR'}:'/usr/local/mipsel-linux/lib/snow-specs';
my $LIBS_DIR	= $ENV{'SNOW_DIR'}?$ENV{'SNOW_DIR'}:'/usr/local/mipsel-linux/lib/snow';
my $REWRITE		= 0;

if      (@ARGV == 1) {
	if($ARGV[0] !~ /^-r(ewrite)?$/) {
		die "Usage: $0 [-rewrite]\n";
	} else {
		$REWRITE = 1;
	}
} elsif (@ARGV != 0) {
	die "Usage: $0 [-rewrite]\n";
}


my $StartAddr	= 0x08000000;
my $Modulo		= 0x100000;
my %DontChange	= ();
my @AllLibs		= ();
my @SortedLibs	= ();
my @Remain		= ();
my %DepResolv	= ();

my $prev_file	= "";
my $prev_addr	= 0;
my $prev_size	= 0;
my $prev_len	= 0;
my $addr		= 0;

#
# Read the appropriate info for each of the spec files.
#

LOAD_SPEC:
foreach my $file (glob($SPECS_DIR.'/*.spec')) {
	local *F;
	my $depends = '';
	my $sso;
	my $size;
	my @a;

	open(F,'< '.$file);
	while(<F>) {
		chomp; s/^\s+//; s/\s+$//;
		$addr    = hex($1) if /^LIBRARY_ADDRESS\s*=\s*(\S+)$/;
		$depends = $1 if /^LIBRARY_DEPENDS\s*=\s*(.+)$/;
	}
	close(F);

	$depends =~ s/^"(.*)"$/$1/;
	$depends =~ s/^\s+//;
	$depends =~ s/\s+$//;

    $file =~ /^.+?([^\/]+)\.spec$/;
	$sso = $1;
	@a = glob($LIBS_DIR.'/'.$sso.'.*.sso');
	@a = glob($LIBS_DIR.'/'.$sso.'.sso') if !@a;
	if(!@a) {
		print STDERR "Could not find library for '$file'\n";
		next LOAD_SPEC;
	}
	$sso = $a[0];
	@a = stat($sso);
	if(!@a) {
		print STDERR "Could not stat library '$sso'\n";
		next LOAD_SPEC;
	}
	$size = $a[7];
	push @AllLibs, {'file'=>$file,'addr'=>$addr,'size'=>$size,'deps'=>$depends};
}

#
# Sort the entries based on the library address.
#
@SortedLibs = sort { $a->{'addr'} <=> $b->{'addr'} } @AllLibs;

#
# Print the current memeory map.
#

print "Current memory map:\n";
foreach my $e (@SortedLibs) {
	print "\t",sprintf("%-20s 0x%08x",$e->{'file'},$e->{'addr'}),"\n";
}

#
# Calculate a compacted memory map.
#
foreach my $e (@SortedLibs) {
	#
	# Verify that there's no overlap.
	#
	print "Overlap of ",$e->{'file'}," onto end of $prev_file\n"
		if $e->{'addr'} < $prev_addr + $prev_size;
	$prev_file = $e->{'file'};
	$prev_addr = $e->{'addr'};
	$prev_size = $e->{'size'};
}

print "Compacted memory map:\n";
$prev_addr = $StartAddr;
$prev_size = 0;
$addr = 0;

COMPACT_FILE:
foreach my $e (@SortedLibs) {
	local *OF;
	local *NF;
	$addr = int(ceil(($prev_addr + $prev_size) / $Modulo)) * $Modulo;
	if(exists $DontChange{$e->{'file'}} && $DontChange{$e->{'file'}}) {
		print "\t",sprintf("%-20s 0x%08x",$e->{'file'},$e->{'addr'}),"\n";
		next COMPACT_FILE;
	}

	print "\t",sprintf("%-20s 0x%08x",$e->{'file'},$addr),"\n";

    if(!$REWRITE) {
    	$prev_addr = $addr;
    	$prev_size = $e->{'size'};
		next COMPACT_FILE;
    }

	#
	# Open the input and output files.
	#
	if(!open(OF,'< '.$e->{'file'})) {
		print STDERR "Could not open '",$e->{'file'},"' for reading\n";
		next COMPACT_FILE;
	}
	if(!open(NF,'> '.$e->{'file'}.'.new')) {
		print STDERR "Could not open '",$e->{'file'},".new' for writing\n";
		next COMPACT_FILE;
	}

	while(<OF>) {
		if(/^LIBRARY_ADDRESS/) {
			print OF sprintf("LIBRARY_ADDRESS=0x%08x\n",$addr);
		} else {
			print OF $_;
		}
	}
	close(OF);
	close(NF);
	if(!unlink($e->{'file'})) {
		print STDERR "Could not delete '",$e->{'file'},"'\n";
		next COMPACT_FILE;
	}
	if(!rename($e->{'file'}.'.new',$e->{'file'})) {
		print STDERR "Could not rename '",$e->{'file'},".new'\n";
		next COMPACT_FILE;
	}

	$prev_addr = $addr;
	$prev_size = $e->{'size'};
}
$addr = int(ceil(($prev_addr + $prev_size) / $Modulo)) * $Modulo;
print "\t",sprintf("%-20s 0x%08x","Next address:",$addr),"\n";

print "Dependencies:\n";
@Remain = @AllLibs;
while(@Remain) {
	my @NewRemain = ();
	$prev_len = @Remain;
	foreach my $e (@Remain) {
		my $resolved = 1;
		my $sso;
        $e->{'file'} =~ /^.+?([^\/]+).spec$/;
        $sso = $1;
		foreach my $d (split(/\s+/,$e->{'deps'})) {
			$resolved = 0 if !exists $DepResolv{$d} || !$DepResolv{$d};
		}
		if($resolved) {
			print "\t",$sso,': ',$e->{'deps'},"\n";
			$DepResolv{$sso} = 1;
		} else {
			push @NewRemain,$e;
		}
	}
	@Remain = @NewRemain;
	if($prev_len == @Remain) {
		print "Circular dependencies found:\n\t",
			join("\n\t",map { $_->{'file'} =~ /^.+?([^\/]+).spec$/; $1.': '.$_->{'deps'} } @Remain),
			"\n";
		exit -1;
	}
}
