Contents
About
This is a preprocessor in the form of a ucc commandlet. Right now it's only available for UT1, but I'm working on a version for Unreal 1. For unknown reasons it gives me lots of errors on Unreal 1 224 sources.
Install
Copy files in the archive to your <UTdir> folder.
Download
As always source code is included in zip file.
Unreal Tournament
link: UEnginePPC v. 0.5.296 for UT(~103kb)
Rune
link: UEnginePPC v. 0.3.168 for Rune (~633kb)
Unreal
Preprocessor is already included in patch 227 for Unreal 1.
Usage
In order to use the preprocessor you have to call ucc with the following parameters:
ucc uengineppc.parse project=[<project_dir>/<project_file>] [-option...] [-globals...]
Parameters
- <project_dir> - relative project directory.
- <project_file> - file (.upc extension) containing all options. If file is detected, no further modifiers are checked
Options
option | description | override by project file |
-clean | deletes preprocessor directives from .uc file | yes |
-debug | turns on debug mode (prints every operation on parsed .uc file) | yes |
-printglobals | prints all global variables | yes |
-normalizeeol | tries to find \r and \n and change them into \r\n | yes |
-bIsPackage | when defining <project_dir> you may type only the package name. Path will be detected automatically. | yes |
-bIniVersion | macro __UENGINEVERSION__ will return uengine version saved in INI (FirstRun param), if false, it'll return version saved in engine. | yes |
-deletelog | scans UScript source for log functions and deletes it | yes |
-force | forces all source .uc files to be parsed | yes |
Globals
All other parameters will be considered to be global variables. If = is not detected, global variable is equal null. Example:
val1=1 val val2=3
Directives
Currently supported directives are:
directive | description |
`process | should be in the first line of .uc file. Tells preprocessor to parse file |
`include(file) | embed file in the currently opened .uc (do not parse it) |
`include(file,false) | embed file in the currently opened .uc (do not parse it) |
`include(file,true) | embed file in the currently opened .uc and parses it |
`require(file) | embed file in the currently opened .uc (do not parse it). If required file doesn't exist, it stops parsing current file and produce error. |
`require(file,false) | embed file in the currently opened .uc (do not parse it). If required file doesn't exist, it stops parsing current file and produce error. |
`require(file,true) | embed file in the currently opened .uc and parses it. If required file doesn't exist, it stops parsing current file and produce error. |
`define(name) | defines variable name (used in `ifdef and `ifndef directives) |
`define(name,value) | defines variable name with specified value (used in `if and ternary operation) |
`undef(name) | removes name from local definitions |
`error(name1,true) | produces error message and exits commandlet |
`error(name1) | produces error message and stops parsing current file |
`warn(name1) | produces warning message |
`log(name1) | produces message |
`ifdef(name) | evaluates to true if variable name is defined |
`ifndef(name) | evaluates to true if variable name is not defined |
`if ([expression1] [operator] [expression2]) | checks to see if the first condition is true by comparing expression1 to expression2 using the operator. |
`else if ([expression1] [operator] [expression2]) | checks to see if the first condition is true by comparing expression1 to expression2 using the operator, only if first condition is false.. |
`else | part of conditional statement |
`endif | ends conditional statement |
`write(name) | writes defined variable name |
`write(name1==name2option1:option2) | if statement evaluates to true (variable name1 equals variable name2) writes option1 otherwise writes option 2 |
`write(name1<>name2?option1:option2) | if statement evaluates to true (variable name1 does not match variable name2) writes option1 otherwise writes option 2 |
`write(name1>name2?option1:option2) | if statement evaluates to true (variable name1 is greater then variable name2) writes option1 otherwise writes option 2 |
`write(name1<name2?option1:option2) | if statement evaluates to true (variable name1 is less than variable name2) writes option1 otherwise writes option 2 |
`write(name1?option1:option2) | if statement evaluates to true (variable name1 is defined) writes option1 otherwise writes option 2 |
`import(directory,extension,type,group,lodset,flags,package) | can be used to import textures/sounds from chosen directory |
`namespace(name,value) | defines namespace name with specified value. It's a combination of `define and `write. If namespace is detected, it'll be replaced by value, without need of using `write |
`remove.start | everything below this directive will be deleted |
`remove.end | disables `remove.start |
Notice that all variables used in directive `if and ternary operation are parsed in the following order:
- Returns value from global variables if correct name is found, otherwise...
- Returns value from local variables if correct name is found, otherwise...
- Assumes that name is value.
`import details:
As type you can use only TEXTURE and SOUND. If the extension is uax or utx the preprocessor will create #exec obj load instead of #exec type import. For example code below:
`import(tex,pcx,TEXTURE,HUD)
will make the preprocessor iterate through all files in folder <UT>/<Project>/tex in search for all *.pcx files. When a file with extension pcx is found, the preprocessor will create a UScript #exec directive to import texture into group HUD. Group, LodStet, Flags and Package parameters are optional. Result will look like:
#exec TEXTURE IMPORT NAME=Tex001 FILE="tex/Tex001.pcx" #exec TEXTURE IMPORT NAME=Tex002 FILE="tex/Tex002.pcx" #exec TEXTURE IMPORT NAME=Tex003 FILE="tex/Tex003.pcx"
`namespace details:
Namespace can be useful to replace large parts of text, without need of use `write and `define directives. For example if you write directive:
`namespace(__SOMECLASS__,class'SomeClass'.static)
and use it in code:
__SOMECLASS__.SomeFunction();
parsed code will change to:
class'SomeClass'.static.SomeFunction();
You can also use macros:
`namespace(__SOMECLASS__,class'__SELF__.SomeClass'.static)
assuming that your package is MyPackege this directive means:
`namespace(__SOMECLASS__,class'MyPackege.SomeClass'.static)
Namespace works also in `require and `include directive.
Operators in conditional statement and write directive
operator | description | type |
== | equal | string, float, integer, bool |
<> | not equal | string, float, integer, bool |
>= | greater or equal | float, integer |
<= | less or equal | float, integer |
< | less | float, integer |
> | greater | float, integer |
! | negation, works as `ifndef |
Unreal Engine version
Since 0.2.106 UE1PreProcessorCommandlet can check the Unreal Engine version. This will be useful once the preprocessor is stable and compiled to U1.
`if(__UENGINEVERSION__==436) //some UT436 specific code `endif
Macros
Macros are in fact hardcoded constants. Each macro will write something in the currently parsed .uc file. Currently supported macros are:
- __FILE__ - will write name of currently parsed file, usable in conditional statements
- __CLASS__ - will write name of currently parsed class, usable in conditional statements
- __DATE__ - will write time
- __SELF__ - will write current package, usable in conditional statements
- __UENGINEVERSION__ - will write Unreal Engine version, usable in conditional statements
- __NUMERATE_CPP__ - replaces itself with a number and increments a counter (used in native functions). It's value depends on native_offset
- __LINE__ - will write current line (parsed output)
- __RELATIVE_LINE__ - will write current line (unparsed input)
Functions
You can also define functions in new section of project file, eg.:
[functions] `log($value)=log($value,'ResidualDecay')
Now when you call in code:
`log("Log message");
the parser will change it to:
log("Log message",'ResidualDecay');
Tips:
- Avoid using functions with similar names - eg.: `log and `logx will screw up result code.
- Avoid using parameters with similar names in one function - eg.: `log($value1, $value2); will not work too good :).
- Functions can only be defined in project file.
Eventually all bugs listed above will be eliminated (current implementation was written in ~5-10 minutes).
Project file
Project file must have upc extension, and 'path' must be relative to ucc.exe location. Default location for files with preprocessor Unreal Script files is:
<project_folder>/classes/preprocessor
parsed .uc files will be stored in:
<project_folder>/classes
Here's all commands for project file.
[project] - project information path=path - path to project debug=true - turns on debug mode (prints every operation on parsed .uc) make=true - if true, ucc commandlet will run make after parsing all files make_ini=make.ini - ini used in ucc make commandlet clean=true - if true, will delete preprocessor directives output=folder - override default output folder where parsed .uc files are written input=folder - override default input folder where parsed .uc files are stored bIsPackage=true - when defining path you may type only name of package. Path will be detected automatically. bIniVersion=true - if true, macro __UENGINEVERSION__ will return uengine version saved in INI (FirstRun param), if false, it'll return version saved in engine. bDeleteLog=true - scans UScript source for log functions and deletes it native_offset - offset for __NUMERATE_CPP__ macro (starting number) bForce=true - if true forces all source .uc files to be parsed printnamespace=true - will print global namespace if true bClearOutput=true - will delete all output files before parsing input printmacros=true - will print all global macros if true native_offset=1600 - offset when using __NUMERATE_CPP__ macro [globals] - group containing global variables for whole project someglobal=somevalue - global variable (sample) [namespace] some_namespace=some_value - declares namespace 'some_namespace' with value equal 'some_value' (for usage see `namespace details) [functions] `some_function($some_value)=log($some_value,'ResidualDecay') - declares function `some_function
example:
[project] path=../MyProject/ debug=true make=true make_ini=make.ini clean=true output=classes input=classes/preprocessor bIsPackage=false [globals] global_value1=test1 global_value2=test2
Example
Let's say you have project file in <UDir>/system called REmitter.upc with content:
[project] path=../REmitter/ debug=true make=false make_ini=make.ini clean=true output=classes input=classes/preprocessor printglobals=true [globals] __NUM_NATIVES__=1
and classes:
REmitterBase.uc
`process `include(classes/includes/default_header.uc,true) // Base class for emitter related actors. // Quaternion implementation originally written by UsAaR33. class REmitterBase extends Actor native; struct Quat { var() config float W, X, Y, Z; }; //converts rotator to quaternion native`write(__NUM_NATIVES__==1?(2330):) static final function Quat RotationToQuat( rotator R, bool bHighPrecision);
default_header.uc
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Copyright 2005-2008 Dead Cow Studios. All Rights Reserved. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Coder: Raven // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Last revision: __DATE__ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You run preprocessor and:
- directive `process is found, so preprocessor knows that this class has to be parsed.
- directive `include is found. Preprocessor embeds file default_header.uc and parses it
- macro __DATE__ is found and current date is inserted at its place
- directive `write is found. Because expression evaluates to true, first value - (2330) - is inserted at it's place
output .uc file will look like this:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Copyright 2005-2008 Dead Cow Studios. All Rights Reserved. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Coder: Raven // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Last revision: 21-9-2008 20:1 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Base class for emitter related actors. // Quaternion implementation originally written by UsAaR33. class REmitterBase extends Actor native; struct Quat { var() config float W, X, Y, Z; }; //converts rotator to quaternion native(2330) static final function Quat RotationToQuat( rotator R, bool bHighPrecision);
Changelog
- v 0.5.296
- Added two new macros
- v 0.5.262
- Added new advanced functions to define in new section of project file
- v 0.4.194
- Added new `remove.start and `remove.end directives to remove larger parts of code
- Namespaces are now usable in `include and `require directives
- v 0.4.194
- Added new global namespace
- v 0.3.185
- Added new __NUMERATE_CPP__ macro
- Added new commadline option -force
- Added new project option bForce
- Various updates and fixes
- v 0.3.168
- Fixed bug in GetVariable function. Variable search is aborted if name is NULL.
- v 0.3.150
- added option to delete log calls out of UScript source
- v 0.3.144
- added bIniVersion to commandline and project file (changes the way macro __UENGINEVERSION__ works)
- v 0.2.123
- new directive `else if
- v 0.2.106
- new directive `namespace
- new macro __UENGINEVERSION__
- new macro __SELF__
- macros can be used in conditional statements
- v 0.2.56
- new directive `import used to create #exec directive for large number of textures/sounds
- v 0.1.35
- fixed directive `write bug (now it doesn't ignore defined variables)
- v 0.1.5
- added new option bIsPackage
- v 0.1.4
- fixed bug with inline `write directive
- v 0.1.1
- initial release