#!/usr/bin/perl

# Generates a Windows Mozilla registry.dat file from XML document

use strict;
use English;
use Getopt::Std;
use IO::Handle;
use Fcntl qw/SEEK_SET/;
use XML::Simple;
use Data::Dumper;

use vars qw/$opt_f/;

getopts('f:');

sub usage {
  my ($msg) = @_;
  print STDERR "$msg\n";
  die "Usage: $0 -f file\n";
}

my $ref = XMLin(\*STDIN, forcearray => 1);

#print STDERR Dumper($ref);

#exit(0);
my @buffer=();
my $bufferLength=0;

sub appendToBuffer {
  my ($newData) = @_;
  my (@data) = unpack("C*", $newData);
  push(@buffer, @data);
  $bufferLength += @data;
}

sub storeIntoBuffer {
  my ($pos, $newData) = @_;
  my (@data) = unpack("C*", $newData);
  for(my $i=0; $i<@data; $i++) {
    $buffer[$pos+$i] = $data[$i];
  }
}

sub getBufferLength {
  return $bufferLength;
}

sub makeHeader {
  appendToBuffer(pack("LSSLLC112", 0x76644441, 1, 2, 0, 0));
}

sub makeEntry {
  my ($entry, $ptr, $parent) = @_;

  # Store name
  my $namePtr = getBufferLength();
  my $name = $entry->{name}->[0];

  $name .= "\0";
  my $nameLen = length($name);
  appendToBuffer($name);

  # If needed, store value
  my $type = $entry->{type}->[0];
  my $down=0;
  my $valuePtr=0;
  my $valueLen=0;
  if(($type & 0x10) != 0) {
    $valuePtr = getBufferLength();
    my $value = $entry->{value}->[0];
    if(ref($value) eq "HASH") {
      $value = "";
    }
    if($type eq 0x11) {
      $value .= "\0";
    } elsif($type eq 0x13) {
      if($value =~ /^0x(.*)/) {
	$value = $1;
	$value =~ s/(..)/pack("c",hex($1))/ge;
      }
    }
    $valueLen = length($value);
    $down = $entry->{down}->[0];
    appendToBuffer($value);
  }

  my $offset = getBufferLength();
  storeIntoBuffer($ptr, pack("L", $offset));
  appendToBuffer(pack("LLSSLLLLL", $offset, $namePtr, $nameLen, $type,
		      0, $down, $valuePtr, $valueLen, $parent));
  # Append subentries
  if(($type & 0x10) == 0) {
    # Append atoms
    my $ptr = $offset + 20;
    foreach my $subEntry (@{$entry->{atom}}) {
      my $subOffset = makeEntry($subEntry, $ptr, $offset);
      $ptr = $subOffset + 12;
    }

    # Append subdirectories
    $ptr = $offset + 16;
    foreach my $subEntry (@{$entry->{dir}}) {
      my $subOffset = makeEntry($subEntry, $ptr, $offset);
      $ptr = $subOffset + 12;
    }
  }
  return $offset;
}

makeHeader();
makeEntry($ref, 12, 0);
storeIntoBuffer(8, pack("L", getBufferLength()));
binmode(STDOUT, ":raw");
syswrite STDOUT,pack("C*", @buffer),$bufferLength;
