diff options
Diffstat (limited to 'contrib/award_plugin_roms/award_plugin_roms.pl')
-rwxr-xr-x | contrib/award_plugin_roms/award_plugin_roms.pl | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/contrib/award_plugin_roms/award_plugin_roms.pl b/contrib/award_plugin_roms/award_plugin_roms.pl new file mode 100755 index 00000000..2b95eed1 --- /dev/null +++ b/contrib/award_plugin_roms/award_plugin_roms.pl @@ -0,0 +1,341 @@ +#!/usr/bin/perl -w +use strict; +use FileHandle; +use integer; + +sub unsigned_little_endian_to_value +{ + # Assumes the data is initially little endian + my ($buffer) = @_; + my $bytes = length($buffer); + my $value = 0; + my $i; + for($i = $bytes -1; $i >= 0; $i--) { + my $byte = unpack('C', substr($buffer, $i, 1)); + $value = ($value * 256) + $byte; + } + return $value; +} + +sub decode_fixed_string +{ + my ($data, $bytes) = @_; + return $data; +} + +sub decode_pstring +{ + my ($buf_ref, $offset_ref) = @_; + # Decode a pascal string + my $offset = ${$offset_ref}; + my $len = unpack('C',substr(${$buf_ref}, $offset, 1)); + my $data = substr(${$buf_ref}, $offset +1, $len); + ${$offset_ref} = $offset + $len +1; + return $data; +} + +sub decode_cstring +{ + # Decode a c string + my ($buf_ref, $offset_ref) = @_; + my ($data, $byte); + my $index = ${$offset_ref}; + while(1) { + $byte = substr(${$buf_ref}, $index, 1); + if (!defined($byte) || ($byte eq "\0")) { + last; + } + $data .= $byte; + $index++; + } + ${$offset_ref} = $index; + return $data; +} + +sub type_size +{ + my ($entry) = @_; + my %type_length = ( + byte => 1, + half => 2, + word => 4, + xword => 8, + 'fixed-string' => $entry->[2], + pstring => 0, + cstring => 0, + ); + my $type = $entry->[0]; + if (!exists($type_length{$type})) { + die "unknown type $type"; + } + my $length = $type_length{$type}; + return $length; +} + +sub decode_fixed_type +{ + my ($type, $data, $bytes) = @_; + my %decoders = ( + 'byte' => \&unsigned_little_endian_to_value, + 'half' => \&unsigned_little_endian_to_value, + 'word' => \&unsigned_little_endian_to_value, + 'xword' => \&unsigned_little_endian_to_value, + 'fixed-string' => \&decode_fixed_string, + ); + my $decoder = $decoders{$type} or die "unknow fixed type $type"; + return $decoder->($data, $bytes); +} + +sub decode_variable_type +{ + my ($type, $buf_ref, $offset_ref) = @_; + my %decoders = ( + 'pstring' => \&decode_pstring, + 'cstring' => \&decode_cstring, + ); + my $decoder = $decoders{$type} or die "unknow variable type $type"; + return $decoder->($buf_ref, $offset_ref); +} + +sub decode_struct +{ + my ($buf_ref, $offset, $layout) = @_; + my $initial_offset = $offset; + my ($entry, %results); + foreach $entry (@$layout) { + my ($type, $name) = @$entry; + my $bytes = type_size($entry); + if ($bytes > 0) { + my $data = substr(${$buf_ref}, $offset, $bytes); + $results{$name} = decode_fixed_type($type, $data, $bytes); + $offset += $bytes; + } else { + $results{$name} = decode_variable_type($type, $buf_ref, \$offset); + } + } + return (\%results, $offset - $initial_offset); +} + +sub print_big_hex +{ + my ($min_digits, $value) = @_; + my @digits; + while($min_digits > 0 || ($value > 0)) { + my $digit = $value%16; + $value /= 16; + unshift(@digits, $digit); + $min_digits--; + } + my $digit; + foreach $digit (@digits) { + printf("%01x", $digit); + } +} + + + +my %lha_signatures = ( + '-com-' => 1, + '-lhd-' => 1, + '-lh0-' => 1, + '-lh1-' => 1, + '-lh2-' => 1, + '-lh3-' => 1, + '-lh4-' => 1, + '-lh5-' => 1, + '-lzs-' => 1, + '-lz4-' => 1, + '-lz5-' => 1, + '-afx-' => 1, + '-lzf-' => 1, +); + +my %lha_os = ( + 'M' => 'MS-DOS', + '2' => 'OS/2', + '9' => 'OS9', + 'K' => 'OS/68K', + '3' => 'OS/386', + 'H' => 'HUMAN', + 'U' => 'UNIX', + 'C' => 'CP/M', + 'F' => 'FLEX', + 'm' => 'Mac', + 'R' => 'Runser', + 'T' => 'TownOS', + 'X' => 'XOSK', + 'A' => 'Amiga', + 'a' => 'atari', + ' ' => 'Award ROM', +); + + +my @lha_level_1_header = ( + [ 'byte', 'header_size' ], # 1 + [ 'byte', 'header_sum', ], # 2 + [ 'fixed-string', 'method_id', 5 ], # 7 + [ 'word', 'skip_size', ], # 11 + [ 'word', 'original_size' ], # 15 + [ 'half', 'dos_time' ], # 17 + [ 'half', 'dos_date' ], # 19 + [ 'byte', 'fixed' ], # 20 + [ 'byte', 'level' ], # 21 + [ 'pstring', 'filename' ], # 22 + [ 'half', 'crc' ], + [ 'fixed-string', 'os_id', 1 ], + [ 'half', 'ext_size' ], +); + +# General lha_header +my @lha_header = ( + [ 'byte', 'header_size' ], + [ 'byte', 'header_sum', ], + [ 'fixed-string', 'method_id', 5 ], + [ 'word', 'skip_size', ], + [ 'word', 'original_size' ], + [ 'half', 'dos_time' ], + [ 'half', 'dos_date' ], + [ 'half', 'rom_addr' ], + [ 'half', 'rom_flags' ], + [ 'byte', 'fixed' ], + [ 'byte', 'level' ], + [ 'pstring', 'filename' ], + [ 'half', 'crc' ], + [ 'lha_os', 'os_id', 1 ], + [ 'half', 'ext_size' ], + [ 'byte', 'zero' ], + [ 'byte', 'total_checksum' ], + [ 'half', 'total_size' ], +); + +sub print_struct +{ + my ($layout, $self) = @_; + my $entry; + my $width = 0; + foreach $entry(@$layout) { + my ($type, $name) = @$entry; + if (length($name) > $width) { + $width = length($name); + } + } + foreach $entry (@$layout) { + my ($type, $name) = @$entry; + printf("%*s = ", $width, $name); + my $value = $self->{$name}; + if (!defined($value)) { + print "undefined"; + } + elsif ($type eq "lha_os") { + print "$lha_os{$value}"; + } + elsif ($type =~ m/string/) { + print "$value"; + } + else { + my $len = type_size($entry); + print "0x"; + print_big_hex($len *2, $value); + } + print "\n"; + } +} + +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; + } + return $sum; +} + +sub decode_lha_header +{ + my ($buf_ref, $offset) = @_; + my $level = unpack('C',substr(${$buf_ref}, $offset + 20, 1)); + + my %self; + my ($struct, $bytes); + if ($level == 1) { + ($struct, $bytes) + = decode_struct($buf_ref, $offset, \@lha_level_1_header); + %self = %$struct; + if ($self{fixed} != 0x20) { + die "bad fixed value"; + } + $self{total_size} = $self{header_size} + 2 + $self{skip_size}; + if ($bytes != $self{header_size} +2) { + die "$bytes != $self{header_size} +2"; + } + my $checksum = checksum($buf_ref, $offset +2, $self{header_size}); + if ($checksum != $self{header_sum}) { + printf("WARN: Header bytes checksum to %02lx\n", + $checksum); + } + # If we are an award rom... + if ($self{os_id} eq ' ') { + @self{qw(zero total_checksum)} = + unpack('CC', substr($$buf_ref, + $offset + $self{total_size}, 2)); + if ($self{zero} != 0) { + warn "Award ROM without trailing zero"; + } + else { + $self{total_size}++; + } + my $checksum = + checksum($buf_ref, $offset, $self{total_size}); + if ($self{total_checksum} != $checksum) { + printf("WARN: Image bytes checksum to %02lx\n", + $checksum); + } + else { + $self{total_size}++; + } + $self{rom_addr} = $self{dos_time}; + $self{rom_flags} = $self{dos_date}; + delete @self{qw(dos_time dos_date)}; + } + } + else { + die "Unknown header type"; + } + return \%self; +} + +sub main +{ + my ($filename, $rom_length) = @_; + my $fd = new FileHandle; + if (!defined($rom_length)) { + my ($dev, $ino, $mode, $nlink, $uid, $gid,$rdev,$size, + $atime, $mtime, $ctime, $blksize, $blocks) + = stat($filename); + $rom_length = $size; + } + $fd->open("<$filename") or die "Cannot ope $filename"; + my $data; + $fd->read($data, $rom_length); + $fd->close(); + + my $i; + for($i = 0; $i < $rom_length; $i++) { + my $sig = substr($data, $i, 5); + if (exists($lha_signatures{$sig})) { + my $start = $i -2; + my $header = decode_lha_header(\$data, $start); + + my $length = $header->{total_size}; + print "AT: $start - @{[$start + $length -1]}, $length bytes\n"; + print_struct(\@lha_header, $header); + print "\n"; + + } + } +} + +main(@ARGV); |