#!/usr/bin/perl

# reipd version 0.1

# Copyright (C) 2008, ashley willis <barry@venamous.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.
#
# See the GNU General Public License in the COPYING file at the
# root directory of this project for more details.

# reconstructs an .ipd file to <BASENAME>.ipd2. Argument to reipd is the
# directory containing the .deipd files, or the <BASENAME>.info file. No
# argument needed if in the directory containing these files. This will
# recreate the original .ipd file byte-for-byte if the data was unchanged.
# Alternatively, .deipd files can be given on the command line, with output
# to "new.ipd2".

use Cwd;

if (@ARGV) {
  if (($count == 1) and !($ARGV[0] =~ /\.deipd2?$/) ) {
    $arg = $ARGV[0];
    if ($arg =~ /\.info$/) {
      open(DBNAMES,"$arg") || die;
      $basename = $arg;
      $basename =~ s/\.info$//;
      $basename =~ s/^(.*)\///g;
      $dir = $1;
    }
    else {
      $arg =~ s/\/$//;
      $basename = $arg;
      $basename =~ s/^(.*)\///g;
      open(DBNAMES,"$arg/$basename.info") || die;
      $dir = $arg;
    }
    ($dir) or $dir = ".";
    print STDERR "dir=\"$dir\"\n";
    print STDERR "base=\"$basename\"\n";
    while(<DBNAMES>) {
      chomp $_;
      (/^$/) or $dbname[$i++] = $_;
    }
  }

  else {
    $dir = ".";
    $basename = "new";
    foreach (@ARGV) {
      s/\.deipd2?$// || die;
      $dbname[$i++] = $_;

    }
  }
}
else {

  $dir = cwd();
  #chomp $dir;
  $basename = $dir;
  $basename =~ s/^(.*)\///g;
  print STDERR "dir=\"$dir\"\n";
  print STDERR "base=\"$basename\"\n";

  open(DBNAMES,"$basename.info") || die "$basename.info not found!\n";
  while(<DBNAMES>) {
    chomp $_;
    (/^$/) or $dbname[$i++] = $_;
  }
}

$numOfDB = @dbname;
$numOfDBBIN = reverse(dec2Bin($numOfDB,2));

open(OUT, ">$basename.ipd2") || die;
print OUT "Inter\@ctive Pager Backup/Restore File\n" . $numOfDBBIN . " ";

for ($dbnum = 0; $dbnum < @dbname; $dbnum++) {
  $db = $dbname[$dbnum] . " ";
  $nameLength = split(//,$db);
  print OUT dec2Bin($nameLength,2) . $db;
}

for ($dbnum = 0; $dbnum < @dbname; $dbnum++) {
  $db = $dbname[$dbnum];
  open(IN, "$dir/$db.deipd") || die "$dir/$db.deipd";
  @dbLine = <IN>;
  close(IN);
  $dbName = $dbLine[0];
  $dbName =~ s/"\n// or die;
  $dbName =~ s/^dbName          = "// || die;
  ($dbName eq $db) || die;
  if ($dbLine[1] =~ /^\n/) {
    for ($c = 2; $c < @dbLine; $c++) {
      $current = $dbLine[$c];
      if ($current =~ s/^dbID  *= //) {
        chomp $current;
        $dbID = $current;
        $dbIDBIN = dec2Bin($dbID,2);
        $found = 1;
      }
      elsif($current =~ s/^dbRecordLength  *= //) {
        chomp $current;
        $dbRecordLength = $current;
        $dbRecordLengthBIN = dec2Bin($dbRecordLength,4);
        ($VERBOSE) and print STDERR "dbRecordLength = $dbRecordLength = \"$dbRecordLengthBIN\"\n";
        $found = 1;
      }
      elsif($current =~ s/^dbVersion  *= //) {
        chomp $current;
        $dbVersion = $current;
        $dbVersionBIN = reverse(dec2Bin($dbVersion));
        $found = 1;
      }
      elsif($current =~ s/^dbRecordHandler  *= //) {
        chomp $current;
        $dbRecordHandler = $current;
        $dbRecordHandlerBIN = dec2Bin($dbRecordHandler,2);
        $found = 1;
      }
      elsif($current =~ s/^recordID  *= //) {
        chomp $current;
        $recordID = $current;
        $recordIDBIN = reverse(pack("H*", $recordID));
        $found = 1;
      }
      elsif($current =~ s/^field  *= //) {
        ($fieldType, $fieldData) = split(/' -> "/, $current);
        $fieldType =~ s/^'// || die;
        if ($fieldType eq "\\n") {
          $fieldType = "\n";
        }
        ### fieldData
        do {
          if ($fieldData =~ /"\n$/) {
            # maybe end?
            if ($dbLine[$c+1] =~ /^field length    = \d+$/ or
                $dbLine[$c+1] =~ /^field           = /  or
                $c+1 >= @dbLine) {
              $fieldData =~ s/"\n$//;
              $cont = 0;	# done
            }
            elsif ($dbLine[$c+1] =~ /^\n$/) {
              if ($dbLine[$c+2] =~ /^dbID            = \d+$/ or
                  $dbLine[$c+2] =~ /^dbRecordLength  = \d+$/ or
                  $dbLine[$c+2] =~ /^dbVersion       = \d+$/) {
                $fieldData =~ s/"\n$//;
                $cont = 0;	# done
              }
              elsif ($c+2 >= @dbLine) {		# EOF
                $fieldData =~ s/"\n$//;
                $cont = 0;	# done
              }
              else {
                $c++;
                $fieldData .= $dbLine[$c];
                $cont = 1;
              }
            }
            else {	# next line !/^field/ and !/^$/
              $c++;
              $fieldData .= $dbLine[$c];
              $cont = ($c < @dbLine) ? 1 : 0;
            }
          }
          else {	# $fieldData does not end with /"/
            # add next line and repeat
            $c++;
            $fieldData .= $dbLine[$c];
            $cont = ($c < @dbLine) ? 1 : 0;
          }
        } while ($cont);
        ### END fieldData

        $fieldLength = split(//, $fieldData);
        $fieldLengthBIN = dec2Bin($fieldLength,2);
        $fieldTypeBIN = $fieldType;
        $fieldDataBIN = $fieldData;
        $field[$fieldCount++] = $fieldLengthBIN . $fieldTypeBIN . $fieldDataBIN;
        ($VERBOSE) and print STDERR "$fieldLength: \'$fieldTypeBIN\' \"$fieldDataBIN\"\n";
        $found = 1;
      }
      elsif($current =~ /^$/) {	# clear
        ($found) or die;
        $database = $dbVersionBIN . $dbRecordHandlerBIN . $recordIDBIN;
        foreach $item (@field) {
          $database .= $item;
        }
        unless ($dbRecordLength) {
          $dbRecordLength = split(//, $database);
          $dbRecordLengthBIN = dec2Bin($dbRecordLength,4);
        }
        $database = $dbIDBIN . $dbRecordLengthBIN . $database;
        print OUT $database;

        undef $dbID, $dbRecordLength, $dbRecordHandler, $recordID,
              $fieldType, $fieldData;
        undef @field;
        $fieldCount = 0;
        undef $found;
      }
    }
    if ($found) {
      $database = $dbVersionBIN . $dbRecordHandlerBIN . $recordIDBIN;
      foreach $item (@field) {
        $database .= $item;
      }
      unless ($dbRecordLength) {
        $dbRecordLength = split(//, $database);
        $dbRecordLengthBIN = dec2Bin($dbRecordLength,4);
      }
      $database = $dbIDBIN . $dbRecordLengthBIN . $database;
      print OUT $database;

      undef $dbID, $dbRecordLength, $dbRecordHandler, $recordID,
            $fieldType, $fieldData;
      undef @field;
      $fieldCount = 0;
      undef $found;
    }

  }
  else {
    # Database is empty.
  }

}


#######################

sub dec2Bin() {
  return pack("h*", dec2Hex(@_));
}

sub dec2Hex() {
  my $dec = $_[0];
  my $size = ($_[1] ? $_[1] : 1) * 2;
  my $hex = '';
  my $tmp = '';
  while ($dec > 0) {
    $tmp = $dec % 16;
    if ($tmp >= 10) {
      $tmp -= 10;
      $tmp =~ tr/0-5/a-f/;
    }
    $dec = int($dec / 16);
    $hex .= $tmp;
  }
  while (split(//,$hex) < $size) {
    $hex .= '0';
  }
  return $hex;
}
