Search code examples
windowsperlscriptinghex

How can I edit a binary file under Windows by scripting


I've got some technical files produced by a scientific device. From time to time, these recorded files get corrupted and we have to do some hexadecimal modification by hand.

I'm wondering how I could automate it. I was thinking of Perl, as I've got some knowledge in that, but even if I manage to read the offset of interest, I don't know how to write the new value.

I've got two things to do:

  1. Write at offset 4 the size of the file minus 8
  2. Count the number of "TRCKfmt" pattern, which is 5452434B666D74 in hex, then write it down at offset 5C (92) in hex value.

I've tried to use sysread and syswrite on a filehandle, but I can't get through the different steps.

Maybe Perl is not a good option, I don't know how to sort it out.

Here is my actual script:

use warnings;
use strict;
use diagnostics;

use Fcntl qw(:seek);

my($fh, $filename, $byte_position, $byte_value);

$filename      = "MYFILE.tac";
$byte_position = 4;
my $filesize = -s $filename;
    print "Size: $filesize\n";
    
    
open($fh, "<", $filename)
  || die "can't open $filename: $!";

binmode($fh)
  || die "can't binmode $filename";

sysseek($fh, $byte_position, SEEK_CUR)  # NB: 0-based
  || die "couldn't see to byte $byte_position in $filename: $!";

sysread($fh, $byte_value, 1) == 1
  || die "couldn't read byte from $filename: $!";

printf "read byte with ordinal value %#02x at position %d\n",
     ord($byte_value), $byte_position;

Solution

  • Here is the working code based on Ikegami's answer:

    #!c:/Perl64/bin/perl.exe
    use warnings;
    use strict;
    use diagnostics;
    
    
    my $dir = 'MYDIRECTORY';
    
    opendir DIR, $dir or die "cannot open dir $dir: $!";
    my @files = glob "$dir/*.tac";
    closedir(DIR);
    
    foreach(@files){
      my $qfn = $_;
    
    my $file;
    {
       open(my $fh, '<:raw', $qfn)
          or die("Can't open \"$qfn\": $!\n");
    
       local $/;
       $file = <$fh>;
    }
    
    {
       my $packed_length = pack('V', length($file) - 8);
    
       substr($file, 0x0004, length($packed_length), $packed_length);
    }
    
    {
       my $num_blocks;
       ++$num_blocks while $file =~ /TRCKfmt/g;
       my $packed_num_blocks = pack('V', $num_blocks);
       substr($file, 0x005C, length($packed_num_blocks), $packed_num_blocks);
    }
    
    {
       open(my $fh, '>:raw', $qfn)
          or die("Can't create \"$qfn\": $!\n");
    
       print($fh $file);
    }
    }