#!/usr/bin/perl -w

use strict;
use FileHandle;
use integer;

# A Small but Useful (tm) utility to do checksums for firmware for the
# NWR04B router.  Can do either Network Everywhere checksum, or Repotec/
# Runtop/Lanware checksums.  Expects as arguments:
# 
# 	your application.bin.gz
# 	the name of the bootloader
#
# The new image is spat to STDOUT.
# 
# Copyright 2005 Hugh Brown.  Released under version 2 of the GPL.
#
#
# $Id$

# Okay, so here's the structure of the checksum:
#
# 0x00: branch ahead
# 0x04: unknown
# 0x08: unknown
# 0x0c: version number.  
# 0x10: unknown
# 0x14: image length - 32 (0x20) (ie, bootloader + application.bin.gz)
# 0x18: sum of bytes with offset 32 (0x20) into file (ie, bootloader + application.bin.gz)
# 0x1c: sum of bytes from 0x04 through 0x1b
#
# The Network Everywhere firmware does this only once, then some additional lengths at
# the end of bootloader.  The Repotec/Runtop/Lanware firmware repeats the checksum at
# offset 32, with the offsets for 0x34 and 0x38 being 64 bytes (0x40).

sub checksum {
	my ($buf_ref, $offset, $length) = @_;
	my ($i, $sum);
	$sum = 0;
	for ($i = 0; $i < $length; $i++) {
		my $byte = unpack ('C', substr ($$buf_ref, $offset + $i, 1));
		#$sum = ($sum + $byte) %256;
		$sum += $byte;
	}
	return $sum;
}

if ($ARGV[0] eq "-h") {
	print "FIXME:  Help goes here.\n";
	print "For right now:\n";
	print "checksum [application.bin.gz] [bootloader]\n";
	exit;
} 

my $app = $ARGV[0];
my $app_data;
my $app_length = (stat($app))[7];

my $bootloader = "bootloader";
if ($ARGV[1]) {
	$bootloader = $ARGV[1];
}

my $boot_length = (stat($bootloader))[7];
my $boot_data;
#my $offset = $length - 12;
my $fd = new FileHandle;
my $combined_data; my $sum;
my $second_sum;
my $length;

$fd->open("<$app") or die ("Can't open $app for reading: $!");
$fd->read($app_data, $app_length);
$fd->close();

$fd->open("<$bootloader") or die ("Can't open $bootloader for reading: $!");
$fd->read($boot_data, $boot_length);
$fd->close();

$combined_data = $boot_data . $app_data;

# Need to do second checksum first.

$length = $boot_length + $app_length - 64;
$sum = checksum (\$combined_data, 64, $length);
$sum = pack ("V", $sum);
$length = pack ("V", $length);
$sum = $length . $sum;
substr ($combined_data, 52, 8, $sum);
$sum = checksum (\$combined_data, 36, 24);
$sum = pack ("V", $sum);
substr ($combined_data, 60, 4, $sum);

# Now the first checksum:

$length = $boot_length + $app_length - 32;
$sum = checksum (\$combined_data, 32, $length);
$sum = pack ("V", $sum);
$length = pack ("V", $length);
$sum = $length . $sum;
substr ($combined_data, 20, 8, $sum);
$sum = checksum (\$combined_data, 4, 24);
$sum = pack ("V", $sum);
substr ($combined_data, 28, 4, $sum);
print $combined_data;

exit;


#$fd->open("<$bootloader") or die ("Can't open $bootloader for reading: $!");
#$fd->read($boot_data, $boot_length);
#$fd->close();

#substr ($boot_data, -12, 8, "$length$sum");

#print $boot_data;
#print $data;
