#!/usr/bin/perl -w
# wince_rename
# - when run in a directory containing files extracted from
#   a Windows CE installation cabinet, it will rename all files
#   to their "installed" filenames
# - the header file (*.000) will be renamed to header.bin
# - the setup DLL (*.999) will be renamed to setup.dll
# - a REGEDIT4 style file will be made, called setup.reg
use strict;

# expands a decimal number from 0-999 into a filename with a three digit
# decimal number as a file extension, if one exists. Otherwise, undef is
# is returned.
sub get_fname {
  my $x = sprintf '*.%03d', $_[0];
  my @x = glob $x;
  if ($#x > 0) {
    print STDERR "WARNING: more than one '$x' file, using '$x[0]'\n";
  }
  elsif ($#x < 0) {
    return undef;
  }
  return $x[0];
}

sub rename_file {
  print "renaming \"$_[0]\" to \"$_[1]\"\n";
  rename $_[0], $_[1] || print STDERR "$_[0]: $!\n";
}


# get the *.000 file
my $hdrfile = get_fname(0);
if (not defined $hdrfile) {
  print "no header (*.000) file found\n";
  exit;
}

# open the header file
if (open FH, "<$hdrfile") {
  my $x;
  read FH, $x, 0x64;

  # read the fixed header
  # $hdr[0] = "MSCE" signature
  # $hdr[2] = overall length of the header file
  # $hdr[5] = target architecture ID
  # @hdr[6..11] = minimal and maximal versions WinCE versions supported
  # @hdr[12..17] = number of entries in {STRINGS,DIRS,FILES,HIVES,KEYS,LINKS}
  # @hdr[18..23] = file offset of {STRINGS,DIRS,FILES,HIVES,KEYS,LINKS}
  # @hdr[24..25] = {file offset, length} of APPNAME
  # @hdr[26..27] = {file offset, length} of PROVIDER
  # @hdr[28..29] = {file offset, length} of UNSUPPORTED
  # other entries are unknown/undefined
  my @hdr = unpack 'V12v6V6v8', $x;

  # does the file begin with "MSCE"?
  if ($hdr[0] == 0x4543534D) {
    # print appname and provider
    seek FH,$hdr[24],0; read FH,$x,$hdr[25]; chop $x; print "Appname:  $x\n";
    seek FH,$hdr[26],0; read FH,$x,$hdr[27]; chop $x; print "Provider: $x\n";

    # RENAME FILES TO INSTALLED FILENAMES:

    # seek to FILES section and loop for the number of FILES entries
    seek FH, $hdr[20], 0;
    for (1 .. $hdr[14]) {
      # read a FILES entry
      read FH, $x, 12;
      my ($id, $dirid, $realid, $flags, $len) = unpack 'vvvVv',$x;
      read FH, $x, $len; chop $x;

      # get file with decimal extension, rename it to filename given in
      # FILES entry
      rename_file(get_fname($realid), $x);
    }

    # CREATE REGISTRY KEYS LIST

    # create "setup.reg" file in REGEDIT4 format, if any KEYS entries
    if (($hdr[16] > 0) && open REGFH, '>setup.reg') {
      print REGFH "REGEDIT4\r\n";

      my @strs;
      my @hives;
      my $lasthive = -1;

      # seek to STRINGS section and read all STRINGS entries into @strs
      seek FH, $hdr[18], 0;
      for (1 .. $hdr[12]) {
	read FH, $x, 4; my ($id, $len) = unpack 'vv',$x;
	read FH, $strs[$id], $len; chop $strs[$id];
      }

      # seek to HIVES section and read all HIVES entries into @hives
      seek FH, $hdr[21], 0;
      for (1 .. $hdr[15]) {
	read FH, $x, 8; my ($id, $root, $unk, $len) = unpack 'vvvv',$x;
	read FH, $x, $len; chop $x; chop $x;
	$hives[$id] = join '\\',(('HKCR','HKCU','HKLM','HKEY_USERS')[$root-1],
				 (map{$strs[$_]} unpack 'v*', $x));
      }

      # seek to KEYS section and loop for all KEYS entries
      seek FH, $hdr[22], 0;
      for (1 .. $hdr[16]) {
	# read KEYS entry, split off name and data components
	read FH, $x, 12; my ($id,$hive,$unk,$flags,$len) = unpack 'vvvVv',$x;
	read FH, $x, $len;
	my $name = (split /\000/, $x)[0];
	my $data = substr $x, length($name) + 1;

	# print REGEDIT4 entry header for key, print hive header if a
	# different hive has been entered
	print REGFH "\r\n[$hives[$hive]]\r\n" unless $lasthive == $hive;
	print REGFH ''.(($name eq '') ? '@' : "\"$name\"").'=';
	$lasthive = $hive;

	# print appropriate REGEDIT4 format for data
	if (($flags & 0x10001) == 0x10001) {
	  print REGFH sprintf 'dword:%08x', unpack('V', $data);
	}
	elsif (($flags & 0x10001) == 0x00001) {
	  print REGFH 'hex:'.join ',',map{sprintf'%02x',$_}unpack 'c*',$data;
	}
	else {
	  chop $data; chop $data if (($flags & 0x10001) == 0x10000);
	  $data =~ s/\\/\\\\/g; $data =~ s/\000/\\0/g; $data =~ s/\"/\\\"/g;
	  print REGFH '"'.$data.'"';
	}
	print REGFH "\r\n";
      }
      close REGFH;
    }
  }
  else {
    print "$hdrfile: not a Windows CE install cabinet header\n";
  }
  close FH;

  # rename *.000 file to header.bin
  rename_file($hdrfile, 'header.bin');

  # rename *.999 file to setup.dll, if it exists
  rename_file($x, 'setup.dll') if $x = get_fname(999);
}
else {
  print "$hdrfile: $!\n";
}
