#!/usr/bin/perl

my ($filename) = @ARGV;

open(FILE, $filename) or die("Could not open input file\n");
my @fconts = <FILE>;
close(FILE);

open(FILE, 'fltkernel.h') or die("Could not open fltkernel.h\n");
my @fltkernel = <FILE>;
close(FILE);

%Functions = ();
my $length = @fltkernel;

foreach $origfctname (@fconts)
{
  $origfctname =~ s/\n//gism;
  if($origfctname =~ /^_(Flt.+?)@[0-9]+$/)
  {
    my $fctname = $1;
    $Functions{$origfctname} = "";
    for($i = 0; $i < $length; $i++)
    {
      if($fltkernel[$i] =~ /^[\s\t]*?(?:\/\/|#define)/)
      {
        next;
      }
      if($fltkernel[$i] =~ /$fctname[\s\t]*\(/)
      {
        my ($k, $str) = ($i - 1, '');
        while($k >= 0)
        {
          my ($currstr) = ($fltkernel[$k]);
          if($currstr =~ /^[\t\s]*$/gism) # Skip empty lines
          {
            last;
          }
          $k--;
        }

        while($k < $length)
        {
          my ($currstr) = ($fltkernel[$k++]);
          $str .= " ".$currstr." ";
          if($currstr =~ /\)[\t\s]*?;/gism)
          {
            last; #  Get out of the inner for-loop
          }
        }
        $i = ++$k;
        if($Functions{$origfctname} ne "")
        {
          print "// WARNING: Overwriting value for $origfctname -> $Functions{$origfctname}\n";
        }
        $Functions{$origfctname} = CleanString($str);
      }
    }
  }
}

my $str = "// Auto-generated script
#include <idc.idc>

static main(void)
{
\tauto i, ea, fcttype;

\tMessage(\"Started\\n\");

\tfor(i = 0; i < GetEntryPointQty(); i++)
\t{
\t\tea = GetEntryPoint(GetEntryOrdinal(i));
";

my $j = 0;
foreach $x (sort keys %Functions)
{
  if($Functions{$x} ne "")
  {
$str .= "
\t\tif((ea != BADADDR) && (GetFunctionName(ea) == \"$x\"))
\t\t{
\t\t\tfcttype = \"$Functions{$x}\";
\t\t\tif(!SetType(ea, fcttype))
\t\t\t\tMessage(\"Failed %s\\n\", fcttype);
\t\t\tcontinue;
\t\t}
";

  }
  else
  {
    $j++;
  }
}

$str .= "\t}
\tMessage(\"Finished\\n\");
}";

print "$str";

#print sprintf("\n%d out of %d undefined\n", $j, scalar(keys %Functions));

sub CleanString
{
  my ($str) = @_;
  $str =~ s/ OPTIONAL([\s\t]*,|\n)/$1/gsm;
  $str =~ s/OUT //gsm;
  $str =~ s/IN //gsm;
  $str =~ s/CONST //gsm;
  $str =~ s/\n/ /gism;
  $str =~ s/\t/ /gism;
  $str =~ s/\s+/ /gism;
  $str =~ s/FLTAPI/__stdcall/gism;
  $str =~ s/^[\s\t]+//gsm;
  $str =~ s/[\s\t]+$//gsm;
  $str =~ s/\s*\(\s+/(/gsm;
  $str =~ s/\s+\)/)/gsm;
  $str =~ s/^VOID\s/void /gsm;
  $str =~ s/([^P])VOID\s/$1void /gism;
  $str =~ s/(P[C]{0,1}FLT.+?)\s/PVOID /gism;
  $str =~ s/(PSECURITY_SUBJECT_CONTEXT|PCHECK_FOR_TRAVERSE_ACCESS|PEX_PUSH_LOCK|PFILE_LOCK|P[A-Z0-9_]+_ROUTINE|POPLOCK|PSECURITY_DESCRIPTOR|FILTER_REPORT_CHANGE|PNOTIFY_SYNC)\s/PVOID /gsm;
  $str =~ s/(FLT_POST_OPERATION_FLAGS|FILTER_VOLUME_INFORMATION_CLASS|FLT_PREOP_CALLBACK_STATUS|FILTER_INFORMATION_CLASS|FLT_FILE_NAME_OPTIONS|FLT_IO_OPERATION_FLAGS|INSTANCE_INFORMATION_CLASS|FLT_SET_CONTEXT_OPERATION)\s/ULONG /gsm;
  $str =~ s/(FLT_CONTEXT_TYPE)\s/USHORT /gsm;
  $str =~ s/(FLT_REGISTRATION)\s/VOID /gsm;
  $str =~ s/(PCUNICODE_STRING)\s/PUNICODE_STRING /gsm;
  $str =~ s/\(VOID\)/()/gsm;
  $str =~ s/PPVOID/PVOID*/gism;
  $str =~ s/[^P]VOID\s*\*\s*/PVOID /gism;


  if($str =~ /((?:FLT_|FILTER_|INSTANCE_).+?)\s/)
  {
    print "// Unknown type $1\n";
  }

  if($str =~ /^#define/)
  {
    $str = '';
  }

  return $str;
}

__END__