#!/usr/bin/perl
################################################################################
###
###  Written by Oliver Schneider (assarbad.net) in 2007
###
###  PUBLIC DOMAIN. The author disclaims warranty or liability for use of any
###  kind (including modification, derivative works)!
###
################################################################################
my ($filename) = @ARGV;
open(FILE, $filename) or die "$!";
my @fconts = <FILE>;
close(FILE);


$functions = '';

foreach $line (@fconts)
{
  unless(($line =~ /^;/i) || ($line =~ /^EXPORTS/i) || ($line =~ /^[\t\s]*$/i))
  {
    if($line =~ /(?:[\t\s]*)([^\t\s]+)[\t\s]+@[\t\s]+([0-9]+)[\t\s]+NONAME/i)
    {
      $functions .= "  RenameDefFunction(\"$1\", $2);\n";
    }
  }
}

$filename =~ s/^(.+?)\.(.+?)$/$1/i;
$filename = uc($filename);

$outfile = "////////////////////////////////////////////////////////////////////////////////
/// Auto-generated IDC script for ordinal imports of $filename.dll
////////////////////////////////////////////////////////////////////////////////
#include <idc.idc>

#define DLL_NAME_PREFIX \"$filename\"

// Tries to create a counted variant of a mangled function/method name
static CreateCountedName_(name, count)
{
  if((substr(name, 0, 2) != \"??\") && (substr(name, 0, 1) == \"?\"))
  {
    auto i;
    for(i = 1; i < strlen(name); i++)
    {
      // Find the first @
      if(substr(name, i, i+1) == \"@\")
      {
        return form(\"%s_x%d%s\", substr(name, 0, i), count, substr(name, i, -1));
      }
    }
  }
  return name; // Default to the original name
}

// This is a workhorse used three times, so it was swapped out into a single function
static ActualRename_(ea, name, counted)
{
  // Attempt to rename it
  if(MakeNameEx(ea, name, SN_NOWARN | SN_CHECK))
  {
    Message(\" ... renamed.\\n\");
    return 1;
  }
  else if(counted >= 0) // Second try
  {
    auto newname;
    Message(\" ... trying alternative method\");
    newname = CreateCountedName_(name, counted);
    if(MakeNameEx(ea, newname, SN_NOWARN | SN_CHECK))
    {
       Message(form(\" ... renamed (%s).\\n\", newname));
       return 1;
    }
  }

  Message(\" ... could not be renamed. Commenting it instead.\\n\");
  // Comment it
  MakeComm(ea, Demangle(name, GetLongPrm(INF_LONG_DN)));
  return 0;
}

// Finds a function and returns its address - also checks function type
static FindFunction_(ordinal, name)
{
  auto ea;

  // Get the function address
  ea = LocByName(name);

  // If we found it by that name ...
  if(ea != BADADDR)
  {
    // Make sure this is a function, not just a local name
    if(GetFunctionFlags(ea) == -1)
    {
      ea = BADADDR;
    }

    // If everything was okay ...
    if(BADADDR != ea)
    {
      // ... be verbose
      Message(form(\"%8.8X - Found ordinal %i as %s\", ea, ordinal, name));
    }
  }
  return ea;
}

// Does all the renaming
static RenameDefFunction(name, ordinal)
{
  auto ea, origname, i, countedname;

  origname = form(\"%s_%d\", DLL_NAME_PREFIX, ordinal);

  // Find the function by its name
  ea = FindFunction_(ordinal, origname);
  if(BADADDR != ea)
  {
    // Rename it and if this fails comment it instead
    ActualRename_(ea, name, -1);
  }
  // Check other possible names
  for(i = 0; i < 20; i++)
  {
    countedname = form(\"%s_%i\", origname, i);
    // Find any of the numbered names as well
    ea = FindFunction_(ordinal, countedname);
    if(BADADDR != ea) // ... found
    {
      // Rename it and if this fails comment it instead
      ActualRename_(ea, name, i);
    }
  }
}

// Everything starts out here
static main()
{
$functions
  Message(\"Finished renaming ...\");
}
";

open(FILE, ">conv_$filename.idc");
print FILE $outfile;
close(FILE);