XScript Compiler
A modern scripting language and compiler for X3 Farnham’s Legacy — write cleaner scripts, catch errors before they reach the game.
⬇ Download Quick StartOverview
XScript is a high-level scripting language that compiles to the native XML format used by X3 Farnham’s Legacy. Rather than working directly in the game’s built-in editor with its limitations, you write scripts in a clean, readable syntax and let the compiler produce correct, optimised XML output.
XScript adds a number of capabilities that aren’t possible in standard X3 scripts:
Object types are tracked through assignments. Calling a ship method on a sector variable produces a warning.
Use function return values directly as arguments or chain with -> without intermediate variables.
$x += 1, ++$count, $x *= 2 — all translated automatically.
#ifdef, #define, #include — conditional compilation and file inclusion.
while($i++), while(arraySize($a) < 10) — increment and function calls in conditions re-evaluated correctly each iteration.
Variables assigned inside a gosub sub are visible to the calling code via a pre-pass.
Convert existing compiled XML scripts back to readable XScript source.
Utils::random(...) — group related functions and constants for cleaner code and better autocomplete.
foreach($item, $array) { ... } — X3 has no native for/foreach; the compiler expands macros to while loops at compile time.
Multiple function signatures share one name — the compiler picks the right one by argument count and type.
IntelliSense, syntax highlighting, hover docs, and one-click compile from the editor.
Download
| File | Description |
|---|---|
XScriptCompiler-0.7.zip | Compiler executable, data builder, and decompiler |
x3fl.xml | XScript definition file — function database, constants, datatypes, namespaces, and macros |
default_data.dat | Pre-built binary data file (requires the game’s Data\ folder to regenerate) |
xscript-x3fl-extension.zip | VS Code extension v1.3.0 — syntax highlighting, IntelliSense, and compiler integration |
Quick Start
1. Set up the folder
Place the compiler and data file in a working folder. If you need to regenerate the data file from scratch, your Data\ folder from the game installation must be present:
XScriptCompiler.exe
default_data.dat
x3fl.xml
Data\
0001-L044.xml
TShips.txt
TDocks.txt
TMissiles.txt
... (other game data files)
2. Write a script
Create a .xs file. Here’s a minimal example:
#DESCRIPTION “My Plugin Script”
#VERSION 1
$message = “Hello from XScript”;
incomingMessage($message, PlayerLog::Alert, TRUE);
return null;
3. Compile
The output myscript.xml can be placed directly in your mod’s script folder.
4. Rebuild the data file (optional)
Only needed when x3fl.xml has been updated:
Command Reference
| Command | Purpose |
|---|---|
--load_data <file> |
Load the compiled data file (default_data.dat or x3fl.dat). Required for compile and decompile. |
--compile <file> |
Compile an XScript .xs source file to XML. |
--out <file> |
Output file path. Required for all commands. |
--define:NAME |
Pre-define a symbol for use with #ifdef. Multiple --define: flags are supported. |
--builddata <file> |
Build the binary data file from x3fl.xml and the game’s Data\ folder. |
--decompile <file> |
Decompile a compiled XML script back to XScript source. |
--usenamespace |
When decompiling, emit namespaced calls (e.g. Utils::random) instead of the plain function name, where a namespace mapping exists. |
Language Overview
All statements end with a semicolon. Variables begin with $ and may contain letters, numbers, underscores, and periods. X3 only supports integer values — no floating point.
Variables and Assignment
$my.variable = “hello”;
$ship = PLAYERSHIP;
$array[0] = 42;
Object Methods and Properties
Use -> to call methods or access properties on an object. Properties map to getter/setter functions — no parentheses needed when reading or writing:
$exists = $ship->exists();
$ship->setCommand(null);
// Properties — getter
$name = $ship->name;
$sector = $ship->sector;
// Properties — setter
$ship->name = “My Freighter”;
$ship->commander = PLAYERSHIP;
Nested Function Calls XScript only
Standard X3 scripts require every return value to be assigned to a variable first. XScript allows direct nesting — the compiler generates the temporary variables automatically:
$name = $sector->name;
Conditions and While Loops
Keywords: if, else, not, while, break, continue. Unlike standard X3, do if and skip if are not written explicitly — the compiler selects the correct instruction automatically based on block size.
else if ($value > 50) { … }
else { … }
while ($count < 10)
{
if ($count == 5) continue;
$count += 1;
}
While Loop Expressions v0.6
inc/dec and ++/-- operators can appear directly in a while condition. Function calls in conditions are also re-evaluated correctly on each iteration:
while ($i++ < 10) { … }
// Pre-increment in condition
while (++$i < 10) { … }
// Function call re-evaluated each iteration
while (arraySize($myArray) < 10) { … }
Constants
Named constants use the :: namespace separator. Race constants are top-level:
$page = TextPage::MiscVoice;
$race = Xenon;
$ship = PLAYERSHIP; $null = NULL;
Labels, Goto and Gosub
$result = $myVar->exists(); // $myVar is known here
return null;
initialise:
$myVar = PLAYERSHIP;
endsub;
gosub target before compiling the calling code, so variables assigned inside a sub are correctly typed in the code that follows the gosub. This is handled automatically.
Compound Assignment and Increment
++$count; $count++; —$count; $count—;
// Post-increment in array subscript
$array[$i++] = 10; // uses $i as index, then increments
Chained Assignment v0.7
Assign the same value to two variables in a single statement — the right-hand variable is assigned first, then copied to the left-hand variable:
$x = $y = random(2);
Namespaces v0.7
Functions and constants can be grouped under a namespace using ::. This is purely organisational — a namespaced call resolves to the same underlying function as calling it directly:
$flag = RaceFlag::NPC; // namespaced constant
The VS Code extension provides full autocomplete for namespace members — type Utils:: to see everything available.
Function Overloads v0.7
Some functions have multiple variants with different argument counts, sharing a single name. The compiler picks the best match automatically:
$x = random(5, 10); // randomFrom(min, max) — 2 arguments, same name
Where a function has both a “script name” (CALLNAME) variant and a general string/variable variant, the compiler disambiguates by argument type — a string literal prefers the CALLNAME variant, a variable prefers the general one:
registerHotkey($scriptName); // variable — general variant
Preprocessor v0.7
Preprocessor directives begin with # and are processed before compilation. They control which code is compiled, set script metadata, allow file inclusion, and provide type hints.
Script Metadata
Set the script description, version, and command ID directly at the top of your source file:
#VERSION 42
#COMMAND 1234
Datatype Hints v0.7
Tell the compiler the type(s) a variable will hold, so object methods and properties can be validated even when the type can’t be inferred from an assignment (e.g. a variable received as a script argument):
#datatype $obj DATATYPE_SHIP|DATATYPE_STATION
Multiple types are separated by |. This is a hint only — it generates no script output.
Defines
#define ADD(a, b) a + b
#define DEBUG // presence-only define for #ifdef
#undef DEBUG
$limit = MAX_COUNT;
$sum = ADD($x, $y);
Multi-line defines v0.7 — a trailing \ continues the define onto the next line:
100 + 200 + 300
#define COMPLEX_CHECK(a, b) \
(a > 0) && \
(b > 0)
Conditional Compilation
#ifdef PLATFORM_PC
$platform = 1;
#elseif PLATFORM_LINUX
$platform = 2;
#else
$platform = 0;
#endif
// Value comparison
#define VERSION 2
#ifdef VERSION == 2
// compiled only when VERSION equals 2
#endif
// ifndef — compile when symbol is NOT defined
#ifndef DEBUG
$logLevel = 0;
#endif
Comparison operators supported in #ifdef: ==, !=, >, <, >=, <=. Nesting is fully supported. Symbols can also be pre-defined on the command line:
Include Files
Include a utility script inline at any point — paths are relative to the including file’s directory:
#include “common/helpers.xs”
Nested includes are supported. Circular includes are detected and reported as an error.
Function Macros v0.7
X3 has no native for/foreach — only while. XScript provides language-level macros, defined in the data file, that expand to native while loops at compile time. The built-in foreach macro:
{
incomingMessage($item->name, PlayerLog::Alert, TRUE);
}
This expands to the equivalent counter-based while loop — written exactly as if you had typed it yourself. The generated script contains no trace of the macro.
A single-statement body (no braces) is also supported:
incomingMessage($item->name, PlayerLog::Alert, TRUE);
$myArray is uninitialised or not an array, the warning points at the foreach(...) call itself, not the internal expanded code.
Additional macros can be defined in x3fl.xml under <Macros>, using the same <Block>/<BlockCommands/>/%ARGn% substitution pattern as foreach.
Error Output
Errors and warnings are printed to stdout with the exact file, line, and column, plus the source line and a caret pointing to the problem:
$result = createSheep(RACE.Argon, $sector, 100)
^
Compile Warning [#2]: [myscript.xs:7:0] – Object ‘$ship’ datatype mismatch
$result = $ship->getSectorName()
^
Errors prevent the output file from being written. Warnings are informational — the script still compiles.
Mod Support
Custom Commands
Third-party mods can add ship commands that aren’t in the base data file. XScript supports these via a prefix notation defined in x3fl.xml. If a DataType has a prefix (e.g. SHIPCOMMAND_), you can reference any command ID directly:
The decompiler also emits these correctly — unknown command IDs are written as PREFIX_id rather than raw integers.
Rebuilding the Data File
If you are maintaining a mod that extends x3fl.xml with new functions, constants, or DataTypes, rebuild the data file after each change:
The Data\ folder from the game must be present in the working directory as it is read for ware type lists and text IDs.
VS Code Integration
A Visual Studio Code extension is included (xscript-x3fl-extension.zip) providing full editor support for .xs files.
Keywords, operators, constants, variables, strings, and all preprocessor directives — including #datatype and multi-line #define — are coloured correctly.
Autocomplete for functions, methods, properties, constants, namespaces (Utils::), and function macros (foreach) — with parameter hints and inline documentation.
Hover over any function, constant, namespace member, or macro to see its description, parameters, and return type.
Compiler errors and warnings appear as underlines directly in the editor and in the Problems panel.
Click the play button in the editor title bar, or right-click for compile options.
Optionally compile automatically every time a .xs file is saved.
Installing the Extension
- Extract
xscript-x3fl-extension.zip - Open Visual Studio Code
- Press Ctrl+Shift+X to open the Extensions panel
- Click the … menu (top right of the panel) and choose Install from VSIX…
- Select the
.vsixfile (runvsce packageinside the extracted folder first if needed) and reload when prompted
Configuring the Extension
Place default_data.dat (or x3fl.dat) in your workspace root folder — the extension loads function definitions and constants from it automatically on startup.
Open Settings (Ctrl+,) and search for xscript to configure the compiler integration:
| Setting | Description |
|---|---|
xscript.compiler.exePath |
Full path to XScriptCompiler.exe. Leave blank to auto-detect in the workspace root or on PATH. |
xscript.compiler.dataFile |
Path to default_data.dat. Auto-detected in the workspace root or next to the exe if left blank. |
xscript.compiler.outputDir |
Where compiled .xml files are written. Defaults to the same folder as the .xs source file. |
xscript.compiler.compileOnSave |
Set to true to compile automatically every time a .xs file is saved. |
xscript.compiler.defines |
Array of symbols to pre-define for #ifdef use, e.g. ["DEBUG", "PLATFORM_PC"]. |
Version History
0.7 BETA — Current
- Chained (double) assignment —
$x = $y = -1;and$x = $y = random(2); - Function overloads/aliases — multiple functions sharing a name, resolved by argument count, with
CALLNAMEvs string-literal disambiguation (e.g.registerHotkey) - Namespaces —
Utils::random(...)resolves to the underlying global function; namespaced constants (e.g.RaceFlag::NPC) supported alongside namespaced functions - Function macros — language-level constructs (e.g.
foreach($item, $array) { ... }) that expand to nativewhileloops at compile time; additional macros can be defined inx3fl.xml #datatypepreprocessor directive — hints a variable’s type(s) for the type checker- Multi-line
#define— trailing\continues a define onto subsequent lines - Fixed nested
ifblocks producing incorrect consecutive closing braces - Fixed
$x - 2being misparsed as negation;random(-1)and other negative-literal arguments now compile correctly --usenamespacedecompiler flag — emitUtils::randomstyle calls when decompiling- VS Code extension v1.3.0 — namespace/macro autocomplete, hover, signature help;
#datatypeand multi-line#definesyntax highlighting
0.6 BETA
- While loop condition re-evaluation —
++/--and function calls inwhile(...)conditions re-evaluate correctly on each iteration breakandcontinueinside while loops- Nested function calls as arguments —
random($i + 10),if(inc($count)), etc. inc/decwith expression arguments —inc($i + 10)compiles to$i = $i + 10- Post-increment in array subscripts —
$array[$i++]uses then increments - Mixed increment/decrement in single expressions —
$array[$i++] = --$j else ifwith function calls in condition — temp vars correctly hoisted before the whole if/else chain- Full preprocessor system —
#define,#undef,#ifdef,#ifndef,#elseif,#else,#endifwith value comparisons and nesting - Script metadata directives —
#DESCRIPTION,#VERSION,#COMMAND #include— inline file inclusion with circular include detection- Command-line defines —
--define:NAMEflag (multiple supported) - Uninitialised variable warnings now fire for variables inside function expression arguments
- Compiler comment in compiled scripts identifies the XScript version and website
- VS Code extension v1.2.0 — preprocessor syntax highlighting,
xscript.compiler.definessetting
0.5 BETA
- Full XScript language support including arrays, tables, compound assignment, namespace constants, and custom prefix constants
- Two-pass compilation for correct type tracking across
gosub/label boundaries - Object type propagation through assignments and function return values
- Boolean arguments correctly emit
TRUE/FALSEin decompiled output - DataType prefix support for mod-added commands (
SHIPCOMMAND_1000) - Improved error messages with source line and caret indicator
- Property getter/setter support via
->operator - Nested function calls and method chaining
