2015-07-15 06:40:12 +00:00
import re
import os
import sys
2015-07-29 03:48:33 +00:00
import multiprocessing
2015-07-15 06:40:12 +00:00
import importlib
import os . path
2015-07-29 03:48:33 +00:00
import timeit
import cProfile
2015-07-15 06:40:12 +00:00
class FileEntry ( object ) :
2015-07-29 03:48:33 +00:00
"""
Class representing a file in the mod directory . This
contains all processed nodes within the file data .
"""
2015-07-15 06:40:12 +00:00
path = None
global_functions = None
bound_functions = None
datablocks = None
2016-04-21 22:32:47 +00:00
2015-07-15 06:40:12 +00:00
def __init__ ( self , path ) :
self . path = path
self . global_functions = [ ]
self . bound_functions = { }
self . datablocks = [ ]
2016-04-21 22:32:47 +00:00
2015-07-15 06:40:12 +00:00
class Function ( object ) :
2015-07-29 03:48:33 +00:00
"""
Class representing a Function entity in the game code tree
that the parse stage produces .
"""
2015-07-15 06:40:12 +00:00
name = None
parameters = None
type = None
filepath = None
line = None
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
aliases = None
comments = None
2016-04-21 22:32:47 +00:00
2015-07-15 06:40:12 +00:00
def __init__ ( self , name , type , parameters , filepath , line ) :
self . name = name
self . parameters = parameters
self . filepath = filepath
self . line = line
2015-07-17 04:34:34 +00:00
self . aliases = [ ]
2015-07-15 06:40:12 +00:00
self . type = type
2015-07-17 04:34:34 +00:00
class Global ( object ) :
2015-07-29 03:48:33 +00:00
"""
Class representing a global variable . This is currently unused
in the coding .
"""
2015-07-17 04:34:34 +00:00
name = None
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
def __init__ ( self , name ) :
self . name = name
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
def __repr__ ( self ) :
return " $ %s " % self . name
2016-04-21 22:32:47 +00:00
2015-07-15 06:40:12 +00:00
class Datablock ( object ) :
2015-07-29 03:48:33 +00:00
"""
Class representing a datablock entry . It contains the type , derived
datablock name , the datablock name itself and all assigned properties .
"""
2015-07-15 06:40:12 +00:00
name = None
type = None
derived = None
2015-07-17 04:34:34 +00:00
line = None
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
aliases = None
properties = None
filepath = None
comments = None
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
def __init__ ( self , name , type , properties , filepath , line , derived ) :
2015-07-15 06:40:12 +00:00
self . name = name
self . type = type
self . derived = derived
2015-07-17 04:34:34 +00:00
self . line = line
self . aliases = [ ]
self . properties = properties
self . filepath = filepath
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
def scrape_file ( input ) :
"""
This method is a performance critical code segment in the scraper .
It is what performs the initial parsing step to produce a sort of
high level representation of the mod for later steps to process
and eventually output .
"""
filepath , parameter_split , combined_pattern = input
2015-07-15 06:40:12 +00:00
2015-07-29 03:48:33 +00:00
key_value_pattern = re . compile ( " (?<!.) \ s*[A-z]+ \ s*= \ s*( \ S+); " )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
global_usages = re . compile ( " \ { .* \ $[A-z]+(::([A-z]+))*?.* \ } " )
global_pattern = re . compile ( " (?<!.) \ s*$[A-z]+(::([A-z]+))*? " )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
parameter_split = re . compile ( " \ s*, \ s* " )
assignment_split = re . compile ( " \ s*= \ s* " )
2016-04-21 22:32:47 +00:00
2016-04-22 03:18:05 +00:00
comment_pattern = re . compile ( " //.* " )
2015-07-29 03:48:33 +00:00
with open ( filepath , " r " ) as handle :
2016-04-22 03:18:05 +00:00
file = FileEntry ( filepath )
2015-07-29 03:48:33 +00:00
file_data = handle . read ( )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
# Parse for all sequences now
for match in re . finditer ( combined_pattern , file_data ) :
line = file_data [ 0 : match . start ( ) ] . count ( " \n " ) + 1
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
match_text = match . group ( 0 ) . strip ( )
if ( match_text [ 0 : 8 ] == " function " ) :
# :: Can't occur correctly in TS in just the function body, so we determine bound functions via the
# presence of ::
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
if ( " :: " in match_text ) :
match_split = match . group ( 0 ) . strip ( ) [ 9 : ] . split ( " :: " )
type = match_split [ 0 ] . lower ( )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
match_split = match_split [ 1 ] . split ( " ( " )
name = match_split [ 0 ] . lower ( )
match_split = match_split [ 1 ] . replace ( " ) " , " " ) . split ( " , " )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
parameters = [ ]
for parameter in match_split :
if ( parameter == " " ) :
continue
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
parameters . append ( parameter . lstrip ( ) . rstrip ( ) )
2016-04-21 22:32:47 +00:00
2016-04-22 03:18:05 +00:00
file . bound_functions . setdefault ( type , [ ] )
file . bound_functions [ type ] . append ( Function ( name , type , parameters , filepath , line ) )
2015-07-29 03:48:33 +00:00
else :
match_split = match . group ( 0 ) . strip ( ) [ 9 : ] . split ( " ( " )
name = match_split [ 0 ] . lower ( )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
match_split = re . split ( parameter_split , match_split [ 1 ] . replace ( " ) " , " " ) )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
parameters = [ ]
for parameter in match_split :
if ( parameter == " " ) :
continue
2016-04-21 22:32:47 +00:00
2016-04-22 03:18:05 +00:00
parameters . append ( parameter . strip ( ) )
file . global_functions . append ( Function ( name , None , parameters , filepath , line ) )
2015-07-29 03:48:33 +00:00
else :
line = file_data [ 0 : match . start ( ) ] . count ( " \n " ) + 1
match_text = match . group ( 0 ) . lstrip ( ) . rstrip ( )
2016-04-21 22:32:47 +00:00
header = match_text [ 0 : match_text . find ( " { " ) ]
2015-07-29 03:48:33 +00:00
type = header [ 10 : header . find ( " ( " ) ] . strip ( ) . lower ( )
name = header [ header . find ( " ( " ) + 1 : header . find ( " ) " ) ] . strip ( ) . lower ( )
2016-04-21 22:32:47 +00:00
2016-04-22 03:18:05 +00:00
# Rip off commenting that we sometimes get in our lines
header = re . sub ( comment_pattern , " " , header ) . rstrip ( )
2015-07-29 03:48:33 +00:00
# Inherited?
inherited = None
inheritor = header . find ( " : " )
2016-04-22 03:18:05 +00:00
2015-07-29 03:48:33 +00:00
if ( inheritor != - 1 ) :
2016-04-22 03:18:05 +00:00
inherited = [ header [ inheritor + 1 : ] . strip ( ) . lower ( ) ]
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
# Blow through key, values
properties = { }
for property_match in re . finditer ( key_value_pattern , match_text ) :
property_text = property_match . group ( 0 )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
key , value = re . split ( assignment_split , property_text , 1 )
key = key . lstrip ( ) . lower ( )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
value = value . rstrip ( ) . rstrip ( " ; " )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
# Global reference
if ( value [ 0 ] == " $ " ) :
value = Global ( value [ 1 : ] )
# String
elif ( value [ 0 ] == " \" " ) :
2016-04-21 22:32:47 +00:00
value = value [ 1 : value . rfind ( " \" " ) ]
2015-07-29 03:48:33 +00:00
# Numerics
else :
try :
value = float ( value )
except ValueError as e :
2016-04-22 03:18:05 +00:00
# If this was raised, treat it as a string
2015-07-29 03:48:33 +00:00
pass
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
properties [ key ] = value
2016-04-21 22:32:47 +00:00
2016-04-22 03:18:05 +00:00
file . datablocks . append ( Datablock ( name , type , properties , filepath , line , inherited ) )
2016-04-21 22:32:47 +00:00
2016-04-22 03:18:05 +00:00
return ( file . global_functions , file . bound_functions , file . datablocks , file )
2015-07-29 03:48:33 +00:00
class TSScraper ( object ) :
_process_count = None
_target_directories = None
_dependencies = None
2016-04-21 22:32:47 +00:00
2016-04-22 03:43:35 +00:00
_combined_pattern = re . compile ( " (?<!.) \ s*function \ s+(([A-z]|_))+(::([A-z]|_)+)* \ ( \ s*( % [A-z]+( \ s*, \ s* % [A-z]+)*)* \ s* \ )|((?<!.) \ s*datablock \ s+[A-z]+ \ s* \ ( \ s* \ S+ \ s* \ ) \ s*(: \ s*[A-z]+)? \ s*(//.*)? \ s* \ { ( \ s| \ S)*? \ s*(?<!.) \ s* \ };) " )
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
bound_function_pattern = re . compile ( " (?<!.) \ s*function \ s+(([A-z]|_)+::)([A-z]|_)+ \ ( \ s*( % [A-z]+( \ s*, \ s* % [A-z]+)*)* \ s* \ ) " )
function_pattern = re . compile ( " (?<!.) \ s*function \ s+([A-z]|_)+ \ ( \ s*( % [A-z]+( \ w*, \ s* % [A-z]+)*)* \ s* \ ) " )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
function_pattern = re . compile ( " (?<!.) \ s*function \ s+(([A-z]|_)+::)([A-z]|_)+ \ ( \ s*( % [A-z]+( \ w*, \ s* % [A-z]+)*)* \ s* \ ) " )
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
datablock_pattern = re . compile ( " (?<!.) \ s*datablock \ s+[A-z]+ \ s* \ ( \ s* \ S+ \ s* \ ) \ s*(: \ s*[A-z]+)? \ s*(//.*)? \ s* \ { ( \ s| \ S)*? \ s*(?<!.) \ }; " )
key_value_pattern = re . compile ( " (?<!.) \ s*[A-z]+ \ s*= \ s*( \ S+); " )
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
#block_iterator = re.compile("function\s+[A-z]+\s*\(\s*(%[A-z]+(\w*,\s*%[A-z]+)*)*\s*\)\{\S*\}")
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
# (?<!{)\s*\$[A-z]+(::([A-z]+))*?\s*(?!})
global_usages = re . compile ( " \ { .* \ $[A-z]+(::([A-z]+))*?.* \ } " )
global_pattern = re . compile ( " (?<!.) \ s*$[A-z]+(::([A-z]+))*? " )
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
parameter_split = re . compile ( " \ s*, \ s* " )
assignment_split = re . compile ( " \ s*= \ s* " )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
_log_lines = None
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
# Rules for verifying datablock information
_datablock_rules = {
2015-07-17 04:34:34 +00:00
" tracerprojectiledata " : {
" references " : [ " splash " , " explosion " , " sound " ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" declared " : [ ] ,
" checks " : {
" fizzletimems " : ( lambda x : x > = 0 , " Cannot use negative fizzle time! " )
}
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" shapebaseimagedata " : {
2015-07-29 03:48:33 +00:00
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-29 03:48:33 +00:00
" declared " : [ ] ,
2016-04-21 22:32:47 +00:00
" checks " : {
2015-07-17 04:34:34 +00:00
}
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" itemdata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" declared " : [ ] ,
2016-04-22 03:18:05 +00:00
" checks " : { " pickupradius " : ( lambda x : x > = 1 , " Items should have >= 1 pickup radius. " )
2015-07-29 03:48:33 +00:00
}
2015-07-17 04:34:34 +00:00
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" audioprofile " : {
2016-04-22 06:19:49 +00:00
" references " : [ " description " ] ,
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" simdatablock " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" jeteffectdata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
" declared " : [ " texture " ] ,
2015-07-17 04:34:34 +00:00
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" hovervehicledata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2016-04-22 03:18:05 +00:00
" declared " : [ " catagory " ] ,
2016-04-22 06:19:49 +00:00
" checks " : { " dragforce " : ( lambda x : x > = 0.01 , " dragForce must be at least 0.01 " ) ,
" vertfactor " : ( lambda x : x > = 0 and x < = 1.0 , " vertFactor must be >= 0 && <= 1.0 " ) ,
" floatingthrustfactor " : ( lambda x : x > = 0 and x < = 1.0 , " floatThrustFactor must be >= 0 && <= 1.0 " ) } ,
2016-04-21 22:32:47 +00:00
} ,
2015-07-17 04:34:34 +00:00
" stationfxpersonaldata " : {
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" references " : [ ] ,
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" cameradata " : {
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" references " : [ ] ,
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" triggerdata " : {
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" references " : [ ] ,
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" wheeledvehicledata " : {
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" references " : [ ] ,
2016-04-22 03:18:05 +00:00
" declared " : [ " catagory " ] ,
2015-07-17 04:34:34 +00:00
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" tsshapeconstructor " : {
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" references " : [ ] ,
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" bombprojectiledata " : {
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" references " : [ ] ,
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" stationfxvehicledata " : {
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" references " : [ ] ,
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2016-04-22 03:18:05 +00:00
" runninglightdata " : {
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2016-04-22 03:18:05 +00:00
" references " : [ ] ,
" declared " : [ ] ,
" checks " : { " radius " : ( lambda x : x > = 1 , " Lights should have a radius of >= 1. " ) } ,
} ,
2015-07-17 04:34:34 +00:00
" staticshapedata " : {
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" references " : [ ] ,
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" decaldata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
" declared " : [ " texturename " ] ,
2015-07-17 04:34:34 +00:00
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" repairprojectiledata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" explosiondata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" linearprojectiledata " : {
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" references " : [ ] ,
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" elfprojectiledata " : {
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" references " : [ ] ,
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" linearflareprojectiledata " : {
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" references " : [ ] ,
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" sensordata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" forcefieldbaredata " : {
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" references " : [ ] ,
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" particledata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" particleemitterdata " : {
2016-04-22 06:19:49 +00:00
" references " : [ " particles " ] ,
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" playerdata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" declared " : [ ] ,
2016-04-22 03:18:05 +00:00
" checks " : { " shapefile " : ( lambda x : x is not None and x != " " , " Must have a valid shapefile! " ) } ,
2015-07-17 04:34:34 +00:00
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" turretdata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" turretimagedata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" shockwavedata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" seekerprojectiledata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" debrisdata " : {
" references " : [ ] ,
" declared " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" grenadeprojectiledata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" sniperprojectiledata " : {
" references " : [ ] ,
" declared " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" sniperprojectiledata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" flyingvehicledata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2016-04-22 03:18:05 +00:00
" declared " : [ " catagory " ] ,
2015-07-17 04:34:34 +00:00
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" splashdata " : {
" references " : [ ] ,
" declared " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" energyprojectiledata " : {
" references " : [ ] ,
" declared " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" flareprojectiledata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" targetprojectiledata " : {
" references " : [ ] ,
" declared " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
" shocklanceprojectiledata " : {
" references " : [ ] ,
" declared " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-17 04:34:34 +00:00
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
" effectprofile " : {
" references " : [ ] ,
" declared " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-29 03:48:33 +00:00
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
" precipitationdata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-29 03:48:33 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
" commandericondata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-29 03:48:33 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
" missionmarkerdata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-29 03:48:33 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
" particleemissiondummydata " : {
" references " : [ ] ,
" declared " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-29 03:48:33 +00:00
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
" fireballatmospheredata " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-29 03:48:33 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
" audiodescription " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-29 03:48:33 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
" lightningdata " : {
" references " : [ ] ,
" declared " : [ ] ,
" checks " : { } ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-29 03:48:33 +00:00
} ,
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
" audioenvironment " : {
" references " : [ ] ,
2016-04-22 06:19:49 +00:00
" optional_references " : [ ] ,
2015-07-29 03:48:33 +00:00
" declared " : [ ] ,
" checks " : { } ,
} ,
2015-07-17 04:34:34 +00:00
}
2016-04-21 22:32:47 +00:00
2016-04-22 03:18:05 +00:00
def __init__ ( self , target_directory , process_count = 0 , previous_results = None ) :
2015-07-29 03:48:33 +00:00
self . _process_count = process_count
2016-04-22 03:18:05 +00:00
self . _target_directory = target_directory
self . previous_results = previous_results
2015-07-29 03:48:33 +00:00
self . _log_lines = [ ]
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
def get_file_list ( self , directory ) :
output = [ ]
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
previous_working_directory = os . getcwd ( )
os . chdir ( directory )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
for root , dirs , files in os . walk ( " . " ) :
2016-04-21 22:32:47 +00:00
for filename in files :
2015-07-29 03:48:33 +00:00
relative_path = os . path . join ( root , filename )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
if ( not os . path . isfile ( relative_path ) ) :
continue
2016-04-21 22:32:47 +00:00
absolute_path = os . path . realpath ( relative_path )
2015-07-29 03:48:33 +00:00
# Only check TS files
name , extension = os . path . splitext ( filename )
if ( extension != " .cs " ) :
2016-04-21 22:32:47 +00:00
continue
2015-07-15 06:40:12 +00:00
2015-07-29 03:48:33 +00:00
output . append ( ( absolute_path , relative_path . lower ( ) ) )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
os . chdir ( previous_working_directory )
return output
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
def _parse_stage ( self , target_files ) :
results = None
if ( self . _process_count > 0 ) :
# Create a list with all the required data for the multi-process
input = [ ]
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
for target_file in target_files :
input . append ( ( target_file , self . parameter_split , self . _combined_pattern ) )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
pool = multiprocessing . Pool ( processes = self . _process_count )
results = pool . map ( scrape_file , input )
else :
results = [ ]
for target_file in target_files :
results . append ( scrape_file ( ( target_file , self . parameter_split , self . _combined_pattern ) ) )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
return results
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
def _declaration_stage ( self , parse_results ) :
# Entries we've already processed
2015-07-17 04:34:34 +00:00
processed_entries = { }
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
# For each file entry
2015-07-29 03:48:33 +00:00
known_datablocks = { }
2016-04-21 22:32:47 +00:00
for file in parse_results :
2015-07-17 04:34:34 +00:00
# For each global function
for global_function in file . global_functions :
processed_entries . setdefault ( global_function . name , global_function )
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
# Check for declarations
if ( processed_entries [ global_function . name ] is not global_function ) :
known_entry = processed_entries [ global_function . name ]
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
# Redeclaration with different param count
if ( len ( known_entry . parameters ) != len ( global_function . parameters ) ) :
global_function . aliases . append ( known_entry )
known_entry . aliases . append ( global_function )
print ( " Warning: Global function ' %s ' redeclared with %u parameters in %s , line %u ! (Original declaration in %s , line %u with %u parameters) " % ( known_entry . name , len ( global_function . parameters ) , global_function . filepath , global_function . line , known_entry . filepath , known_entry . line , len ( known_entry . parameters ) ) )
# Regular Redeclaration
else :
global_function . aliases . append ( known_entry )
known_entry . aliases . append ( global_function )
print ( " Warning: Global function ' %s ' redeclared in %s , line %u ! (Original declaration in %s , line %u ) " % ( known_entry . name , global_function . filepath , global_function . line , known_entry . filepath , known_entry . line ) )
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
processed_entries = { }
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
# For each bound function
for bound_type in file . bound_functions . keys ( ) :
for bound_function in file . bound_functions [ bound_type ] :
processed_entries . setdefault ( bound_function . type , { } )
processed_entries [ bound_function . type ] . setdefault ( bound_function . name , bound_function )
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
# Check for declarations
if ( processed_entries [ bound_function . type ] [ bound_function . name ] is not bound_function ) :
known_entry = processed_entries [ bound_function . type ] [ bound_function . name ]
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
# Redeclaration with different param count
if ( len ( known_entry . parameters ) != len ( bound_function . parameters ) ) :
bound_function . aliases . append ( known_entry )
known_entry . aliases . append ( bound_function )
print ( " Warning: Bound function ' %s :: %s ' redeclared with %u parameters in %s , line %u ! (Original declaration in %s , line %u with %u parameters) " % ( known_entry . type , known_entry . name , len ( bound_function . parameters ) , bound_function . filepath , bound_function . line , known_entry . filepath , known_entry . line , len ( known_entry . parameters ) ) )
# Regular Redeclaration
else :
bound_function . aliases . append ( known_entry )
known_entry . aliases . append ( bound_function )
print ( " Warning: Bound function ' %s :: %s ' redeclared in %s , line %u ! (Original declaration in %s , line %u ) " % ( known_entry . type , known_entry . name , bound_function . filepath , bound_function . line , known_entry . filepath , known_entry . line ) )
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
processed_entries = { }
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
# For each datablock
for datablock in file . datablocks :
processed_entries . setdefault ( datablock . name , datablock )
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
known_datablocks . setdefault ( datablock . name , [ ] )
known_datablocks [ datablock . name ] . append ( datablock )
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
# Check for declarations
if ( processed_entries [ datablock . name ] is not datablock ) :
known_entry = processed_entries [ datablock . name ]
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
# Redeclaration with different parent
if ( known_entry . derived != datablock . derived ) :
known_entry . aliases . append ( datablock )
datablock . aliases . append ( known_entry )
print ( " Warning: Datablock ' %s ' redeclared in %s , line %u with parent ' %s ' ! (Original declaration in %s , line %u with parent ' %s ' ) " % ( datablock . name , datablock . filepath , datablock . line , datablock . derived , known_entry . filepath , known_entry . line , known_entry . derived ) )
# Regular Redeclaration
else :
known_entry . aliases . append ( datablock )
datablock . aliases . append ( known_entry )
print ( " Warning: Datablock ' %s ' redeclared in %s , line %u ! (Original declaration in %s , line %u " % ( datablock . name , datablock . filepath , datablock . line , known_entry . filepath , known_entry . line ) )
2016-04-21 22:32:47 +00:00
2015-07-17 04:34:34 +00:00
return known_datablocks
2016-04-21 22:32:47 +00:00
def _inheritance_stage ( self , parse_results , datablock_list ) :
2015-07-29 03:48:33 +00:00
# For each file entry
for file in parse_results :
# For each datablock
for datablock in file . datablocks :
2016-04-22 03:18:05 +00:00
# Process all parents
if ( datablock . derived is not None ) :
for parent in datablock . derived :
if ( parent . lower ( ) not in datablock_list . keys ( ) ) :
2016-04-22 03:43:35 +00:00
print ( " Warning: Datablock ' %s ' derives from non-existent parent ' %s ' ! (Declaration in %s , line %u ) " % ( datablock . name , parent , datablock . filepath , datablock . line ) )
2016-04-22 03:18:05 +00:00
datablock . derived . remove ( parent )
2015-07-29 03:48:33 +00:00
elif ( datablock . derived is not None ) :
datablock . derived = datablock_list [ datablock . derived ]
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
def _reference_stage ( self , parse_results , datablock_list ) :
# For each file entry
for file in parse_results :
# For each datablock
for datablock in file . datablocks :
if ( datablock . type in self . _datablock_rules ) :
2016-04-22 06:19:49 +00:00
# We have a bunch of rules to check, so we recurse parent classes where necessary.
parent_classes = [ datablock . name ]
# Recurse down the hierarchy
def process_parents ( current , result ) :
if ( current . derived is not None ) :
result + = current . derived
for parent in current . derived :
parent_declarations = datablock_list [ parent ]
# Check for the property on each parent block
for parent in parent_declarations :
process_parents ( parent , result )
return result
process_parents ( datablock , parent_classes )
2015-07-29 03:48:33 +00:00
# Flip through each reference in the table
for reference in self . _datablock_rules [ datablock . type ] [ " references " ] :
2016-04-22 06:19:49 +00:00
found_reference = False
for parent in parent_classes :
# FIXME: Deal with datablock redeclarations?
parent_datablock = datablock_list [ parent ] [ 0 ]
if ( reference in datablock . properties ) :
if ( datablock . properties [ reference ] . lower ( ) not in datablock_list . keys ( ) ) :
print ( " Reference Warning: %s Datablock ' %s ' references ' %s ' in property ' %s ' , which does not exist! (Declaration in %s , line %u ) " % ( datablock . type , datablock . name , datablock . properties [ reference ] , reference , datablock . filepath , datablock . line ) )
break
else :
found_reference = True
break
if ( found_reference is False ) :
2015-07-29 03:48:33 +00:00
print ( " Reference Warning: %s datablock ' %s ' has no ' %s ' declaration! (Declaration in %s , line %u ) " % ( datablock . type , datablock . name , reference , datablock . filepath , datablock . line ) )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
# Check each declaration
for declaration in self . _datablock_rules [ datablock . type ] [ " declared " ] :
2016-04-22 06:19:49 +00:00
found_declaration = False
for parent in parent_classes :
# FIXME: Deal with datablock redeclarations?
parent_datablock = datablock_list [ parent ] [ 0 ]
if ( declaration in datablock . properties ) :
found_declaration = True
break
if ( found_declaration is False ) :
2015-07-29 03:48:33 +00:00
print ( " Declaration Warning: %s Datablock ' %s ' required property ' %s ' not declared! (Declaration in %s , line %u ) " % ( datablock . type , datablock . name , declaration , datablock . filepath , datablock . line ) )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
# Run custom checks
for check in self . _datablock_rules [ datablock . type ] [ " checks " ] . keys ( ) :
2016-04-22 06:19:49 +00:00
found_check = False
for parent in parent_classes :
# FIXME: Deal with datablock redeclarations?
parent_datablock = datablock_list [ parent ] [ 0 ]
# Is it declared?
if ( check not in parent_datablock . properties ) :
continue
found_check = True
method , message = self . _datablock_rules [ parent_datablock . type ] [ " checks " ] [ check ]
if ( not method ( parent_datablock . properties [ check ] ) ) :
2015-07-29 03:48:33 +00:00
print ( " Property Warning (Datablock ' %s ' , type %s . Declaration in %s , line %u ): %s " % ( datablock . name , datablock . type , datablock . filepath , datablock . line , message ) )
2016-04-22 06:19:49 +00:00
break
if ( found_check is False ) :
print ( " Inherited Property Warning: %s Datablock %s ' %s ' property not declared and parent datablocks do not declare it! (Declaration in %s , line %u ) " % ( datablock . type , datablock . name , check , datablock . filepath , datablock . line ) )
2015-07-29 03:48:33 +00:00
else :
print ( " Program Error: Unknown datablock type ' %s ' ! This means the software does not know how to check this datablock. (Declaration in %s , line %u ) " % ( datablock . type , datablock . filepath , datablock . line ) )
2015-07-17 04:34:34 +00:00
2016-04-21 22:32:47 +00:00
def process ( self ) :
2015-07-29 03:48:33 +00:00
# Process each directory sequentially
target_files = { }
2016-04-21 22:32:47 +00:00
2016-04-22 03:18:05 +00:00
if ( os . path . isdir ( self . _target_directory ) is False ) :
raise IOError ( " No such directory to recurse (# %u ): ' %s ' " % ( index , self . _target_directory ) )
2016-04-21 22:32:47 +00:00
2016-04-22 03:18:05 +00:00
print ( " INFO: Building file list for directory ' %s ' ... " % self . _target_directory )
current_files = self . get_file_list ( self . _target_directory )
2016-04-21 22:32:47 +00:00
# Does a previous entry exist in the target file list?
for current_absolute_path , current_relative_path in current_files :
target_files [ current_relative_path ] = current_absolute_path
2015-07-29 03:48:33 +00:00
# Build the list now
target_file_list = [ ]
2016-04-21 22:32:47 +00:00
for current_relative_file in target_files . keys ( ) :
2015-07-29 03:48:33 +00:00
target_file_list . append ( target_files [ current_relative_file ] )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
# Perform the initial parse
2016-04-21 22:32:47 +00:00
print ( " INFO: Performing parse stage ... " )
2016-04-22 03:18:05 +00:00
file_list = [ ]
global_function_list = [ ]
bound_function_list = { }
datablock_list = { }
for payload in self . _parse_stage ( target_file_list ) :
global_functions , bound_functions , datablocks , file = payload
file_list . append ( file )
global_function_list + = global_functions
# Write out datablocks
for datablock in datablocks :
datablock_list [ datablock . name ] = datablock
for classname in bound_functions :
bound_function_list . setdefault ( classname , [ ] )
bound_function_list [ classname ] + = bound_functions [ classname ]
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
# Perform the declaration analysis
print ( " INFO: Performing declaration analysis. ... " )
2016-04-22 03:18:05 +00:00
datablock_list = self . _declaration_stage ( file_list )
# Combine previous datablock listings with current ones
# TODO: Refactor the programming to use a global lookup when performing referential checks
if ( self . previous_results is not None ) :
for datablock_name in self . previous_results [ " datablocks " ] :
# Don't overwrite current datablock listings with base ones
if ( datablock_name not in datablock_list ) :
datablock_list [ datablock_name ] = self . previous_results [ " datablocks " ] [ datablock_name ]
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
# Perform DB inheritance analysis
print ( " INFO: Performing datablock inheritance analysis ... " )
2016-04-22 03:18:05 +00:00
self . _inheritance_stage ( file_list , datablock_list )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
# Perform DB reference analysis
print ( " INFO: Performing datablock reference analysis ... " )
2016-04-22 03:18:05 +00:00
self . _reference_stage ( file_list , datablock_list )
2016-04-21 22:32:47 +00:00
2015-07-29 03:48:33 +00:00
# We're done, return the results
print ( " INFO: Done. " )
2016-04-21 22:32:47 +00:00
2016-04-22 03:18:05 +00:00
return { " files " : file_list , " datablocks " : datablock_list , " bound_functions " : bound_function_list ,
" global_functions " : global_function_list }