source-engine/devtools/bin/make360def.pl
2020-10-22 13:51:27 +03:00

330 lines
6.1 KiB
Perl
Executable File

###############################################################
#
# generateDEF.pl
#
# Parses a .map file and generates a .def file
# for exporting functions from a dll.
#
# Note: Map Exports must be enabled in the project properties
#
###############################################################
my @baselist;
my @output;
my $baseLen = 63;
######################################
# Adds tabs for better formatting
sub Add_Tabs
{
my $name = shift;
my $num = int( ($baseLen - length( $name )) / 8 );
for( $i = 0; $i < $num; $i++ )
{
push( @output, "\t" );
}
}
######################################
# Open, validate, and read a file (not implemented yet)
sub Read_File
{
my $name = shift;
my @file = shift;
# read in the file
if ( open(INFILE, "$name" ) )
{
@file = <INFILE>;
close( INFILE );
}
else
{
print( "Error opening file $name\n" );
exit 1;
}
}
#####################################
# Start of script
print( "Valve Software - make360def.pl\n" );
print( "Copyright (c) 1996-2006, Valve LLC, All rights reserved.\n" );
my $filename = $ARGV[0];
my $numArgs = 1;
my @lines = ();
my @deflines = ();
if ( $ARGV[0] =~ /-check$/i )
{
$numArgs = 3;
$check = 1;
$filename = $ARGV[1];
$defname = $ARGV[2];
}
elsif ( $ARGV[0] =~ /-checkauto/i )
{
$defname = $ARGV[1];
$check = 2;
}
if ( @ARGV < $numArgs )
{
print( "ERROR: Missing filename(s)\n" );
exit 1;
}
if ( $check == 1 )
{
# swap filenames if necessary
if ( $filename =~ /.def/ )
{
my $temp = $filename;
$filename = $defname;
$defname = $temp;
}
# validate extensions
unless ( $filename =~ /.map/ && $defname =~ /.def/ )
{
print( "ERROR: Invalid file extensions. -check requires a .map file and a .def file.\n" );
exit 1;
}
# read in the def file
if ( open(INFILE, "$defname" ) )
{
@deflines = <INFILE>;
close( INFILE );
}
else
{
print( "ERROR: Couldn't open file $defname.\n" );
exit 1;
}
}
elsif ( $check == 2 )
{
# read in the def file
if ( open(INFILE, "$defname" ) )
{
@deflines = <INFILE>;
close( INFILE );
}
else
{
print( "ERROR: Couldn't open file $defname.\n" );
exit 1;
}
# validate that the first export is CreateInterface*
# validate that the export ordinals are sequential and ordered
my $line;
my $start = false;
my $idx = 1;
for ( @deflines )
{
$line = $_;
if ( $line =~ /@(\d+)[ |\n]/ )
{
if ( $1 == 1 )
{
unless ( $line =~ /CreateInterface/ )
{
# first export must be CreateInterface*
$line =~ /\s+([\S]*)/;
print( "**************************************************\n" );
print( " ERROR: First export must be CreateInterface*. \n" );
print( " Export \"", $1, "\" found instead! \n" );
print( " This is a FATAL ERROR with the def file. \n" );
print( " Please contact an Xbox 360 engineer immediately.\n" );
print( "**************************************************\n" );
exit 1;
}
}
if ( $1 != $idx )
{
# exports are out of order
print( "**************************************************\n" );
print( " ERROR: Def file exports are not sequential \n" );
print( " This may cause unexpected behavior at runtime. \n" );
print( " Please contact an Xbox 360 engineer immediately.\n" );
print( "**************************************************\n" );
exit 1;
}
++$idx;
}
}
exit 0;
}
# Get the base name
$filename =~ /(\w+(\.360)?).map/;
my $basename = $1;
# Read in the source file
if ( open(INFILE, "$filename" ) )
{
@lines = <INFILE>;
close( INFILE );
}
else
{
print( "ERROR: Couldn't open file $filename.\n" );
exit 1;
}
# Delete the lines up to the exports section
my $len = 0;
my $exportsFound = 0;
for( @lines )
{
$len++;
if( /^ Exports$/ )
{
splice( @lines, 0, $len+3 );
$exportsFound = 1;
}
}
if ( $exportsFound == 0 )
{
print( "ERROR: No Exports section found in $filename. " );
print( "Relink the project with 'Map Exports' enabled.\n" );
exit 1;
}
if ( $check == 1 )
{
# Check for exports in the map that aren't in the def
print( "make360def: Comparing $filename and $defname\n" );
# strip the first 2 lines from the def
splice( @deflines, 0, 2 );
my $defEntryCt = $#deflines + 1;
my $defMatches = 0;
# for each line in the map
for( @lines )
{
my $found = 0;
# Pull the export name from the map line
my $mapline;
if ( /(\d+)\s+(\S+)/ )
{
$mapline = $2;
}
else
{
# ignore this line
next;
}
# for each line in the def
for( @deflines )
{
/(\S+)/;
if ( $1 =~ /^\Q$mapline\E$/ )
{
$found = 1;
$defMatches++;
last;
}
}
if ( $found == 0 )
{
print( "ERROR: New export found in $filename, " );
print( "so the map file and def file are out of sync. " );
print( "You must relink the project to generate a new def file.\n" );
exit 1;
}
}
# Make sure all the def lines were matched
if ( $defMatches != $defEntryCt )
{
print( "ERROR: An export was removed from $filename, " );
print( "so the map file and def file are out of sync. " );
print( "You must relink the project to generate a new def file.\n" );
exit 1;
}
print( "make360def: Comparison complete, files match.\n" );
exit 0;
}
# start the def file
print( "make360def: Generating $basename.def\n" );
push( @output, "LIBRARY\t$basename.dll\n" );
push( @output, "EXPORTS\n" );
# process each line in the export section
my $interfacePrefix = "0000000000";
for( @lines )
{
if ( /(\d+)\s+(\S+)/ )
{
my $func = $2;
if ( $func =~ /CreateInterface/ )
{
# Force createInterface to sort first
$func = join( '', $interfacePrefix, $func );
}
push( @baselist, $func );
}
}
# sort the list
my @sortedlist = sort {uc($a) cmp uc($b)} @baselist;
#my @sortedlist = @baselist;
my $ordinal = 1;
for( @sortedlist )
{
my $func = $_;
if ( /$interfacePrefix(.*)/ )
{
# Strip the added characters
$func = $1;
}
push( @output, "\t$func\t" );
Add_Tabs( $func );
push( @output, "\@$ordinal\n" );
$ordinal++;
}
# write the def file
print( "make360def: Saving $basename.def.\n" );
open ( OUTFILE, ">$basename.def" );
print OUTFILE @output;
close ( OUTFILE );
print( "make360def: Finished.\n" );
exit 0;