#!/usr/bin/perl -w # (C) 2003-2007 Willem Jan Hengeveld # Web: http://www.xs4all.nl/~itsme/ # http://wiki.xda-developers.com/ # # $Id$ # use strict; use IO::File; use Getopt::Long qw(:config no_ignore_case); my $filebaseofs= 0; my $g_test= 0; my @patches; my %datasize= ( V=>4, N=>4, v=>2, n=>2, C=>1, ); sub usage { return <<__EOF__ Usage: hexedit [patches] file -pd ofs:dword,dword -pN ofs:netdword,netdword -pw ofs:word,word -pn ofs:netword,netword -pb ofs:byte,byte OR ofs:bytebytebytebyte -ps ofs:string -pu ofs:string stored as unicode -pf ofs-ofs:byte fill inclusive range. -pl ofs:file:seek:len -t test only all numbers hexdigits __EOF__ } GetOptions( "b=s"=> sub { $filebaseofs= eval($_[1]); }, "pd=s"=> sub { push @patches, ParseData($_[1], "V"); }, "pN=s"=> sub { push @patches, ParseData($_[1], "N"); }, "pw=s"=> sub { push @patches, ParseData($_[1], "v"); }, "pn=s"=> sub { push @patches, ParseData($_[1], "n"); }, "pb=s"=> sub { push @patches, ParseData($_[1], "C"); }, "ps=s"=> sub { push @patches, ParseStringData($_[1]); }, "pu=s"=> sub { push @patches, ParseWStringData($_[1]); }, "pf=s"=> sub { push @patches, ParseFill($_[1]); }, "pl=s"=> sub { push @patches, ParseFileData($_[1]); }, "t" => \$g_test, ) or die usage(); sub ParseData { my ($ext, $datatype)= @_; if ($ext =~ /^(\w+):(.*)/) { my $ofs= hex($1); my $ascdata= $2; if ($datatype eq "C" && $ascdata =~ /^\w+$/ && length($ascdata)>2) { # right align odd length data $ascdata="0$ascdata" if length($ascdata)&1; return { offset=>$ofs, data=>pack("H*", $ascdata) }; } my @data= map { hex($_) } split /,/, $ascdata; return { offset=>$ofs, data=>pack($datatype."*", @data), }; } die "Invalid data edit format $ext\n"; } sub ParseFill { my ($ext)= @_; if ($ext =~ /^(\w+)-(\w+):(\w+)/) { my ($sofs, $eofs, $val)= (hex($1), hex($2), hex($3)); return { offset=>$sofs, data => chr($val) x ($eofs-$sofs+1), }; } die "invalid data fill spec: $ext\n"; } sub ParseStringData { my ($ext)= @_; if ($ext =~ /^(\w+):(.*)/) { my $ofs= hex($1); my $data= eval("\"$2\""); return { offset=>$ofs, data=>$data, }; } die "Invalid data edit format $ext\n"; } # todo: this does not convert utf8 from the commandline properly sub ParseWStringData { my ($ext)= @_; if ($ext =~ /^(\w+):(.*)/) { my $ofs= hex($1); my $data= pack("v*", unpack("C*", eval("\"$2\""))); return { offset=>$ofs, data=>$data, }; } die "Invalid data edit format $ext\n"; } sub ParseFileData { my ($ext)= @_; if ($ext =~ /^(\w+):(.+?)(?::(\w+)(?::(\w+))?)?$/) { my $romofs= hex($1); my $filename= $2; my $fileofs= defined $3 ? hex($3) : 0; my $filelen= defined $4 ? hex($4) : (-s $filename)-$fileofs; my $fh=IO::File->new($filename, "r") or die "$filename: $!\n"; binmode $fh; $fh->seek($fileofs, SEEK_SET); my $data; $fh->read($data, $filelen); $fh->close(); if (length($data) != $filelen) { die "error reading $filelen bytes from $filename\n"; } return { offset=>$romofs, data=>$data, }; } die "Invalid data edit format $ext\n"; } my $filename = shift || die usage(); my $fh=IO::File->new($filename, "r+") or die "$filename: $!\n"; binmode $fh; for (sort { $a->{offset} <=> $b->{offset} } @patches) { $fh->seek($_->{offset}-$filebaseofs, SEEK_SET) or warn "$!"; if ($g_test) { my $data; $fh->read($data, length($_->{data})) or warn "$!"; print unpack("H*", $data), "\n"; } else { $fh->write($_->{data}) or warn "$!"; } } $fh->seek(0, SEEK_END) or warn "$!"; $fh->close();