XScript Compiler

Version 0.7 BETA

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 Start

Overview

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:

Type checking

Object types are tracked through assignments. Calling a ship method on a sector variable produces a warning.

Nested calls

Use function return values directly as arguments or chain with -> without intermediate variables.

Compound assignment

$x += 1, ++$count, $x *= 2 — all translated automatically.

Preprocessor

#ifdef, #define, #include — conditional compilation and file inclusion.

While loop expressions

while($i++), while(arraySize($a) < 10) — increment and function calls in conditions re-evaluated correctly each iteration.

Sub variable tracking

Variables assigned inside a gosub sub are visible to the calling code via a pre-pass.

Decompiler

Convert existing compiled XML scripts back to readable XScript source.

Namespaces v0.7

Utils::random(...) — group related functions and constants for cleaner code and better autocomplete.

Function macros v0.7

foreach($item, $array) { ... } — X3 has no native for/foreach; the compiler expands macros to while loops at compile time.

Function overloads v0.7

Multiple function signatures share one name — the compiler picks the right one by argument count and type.

VS Code extension

IntelliSense, syntax highlighting, hover docs, and one-click compile from the editor.

Download

Beta release — XScript 0.7 is functional but under active development. Please report any issues.
FileDescription
XScriptCompiler-0.7.zipCompiler executable, data builder, and decompiler
x3fl.xmlXScript definition file — function database, constants, datatypes, namespaces, and macros
default_data.datPre-built binary data file (requires the game’s Data\ folder to regenerate)
xscript-x3fl-extension.zipVS Code extension v1.3.0 — syntax highlighting, IntelliSense, and compiler integration

⬇ Download XScript 0.7 BETA

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:

// myscript.xs — shows a message to the player
#DESCRIPTION “My Plugin Script”
#VERSION 1

$message = “Hello from XScript”;
incomingMessage($message, PlayerLog::Alert, TRUE);
return null;

3. Compile

XScriptCompiler.exe –load_data default_data.dat –compile myscript.xs –out myscript.xml

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:

XScriptCompiler.exe –builddata x3fl.xml –out default_data.dat

Command Reference

CommandPurpose
--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

$count = 0;
$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:

// Methods
$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:

Standard X3 (2 lines)
$sector = getSectorByCoord(22, 3);
$name = $sector->name;
XScript (1 line)
$name = getSectorByCoord(22, 3)->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.

if ($value > 100) { … }
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:

// Post-increment in condition
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:

$flag = RaceFlag::NPC;
$page = TextPage::MiscVoice;
$race = Xenon;
$ship = PLAYERSHIP;  $null = NULL;

Labels, Goto and Gosub

gosub initialise;

$result = $myVar->exists(); // $myVar is known here
return null;

initialise:
    $myVar = PLAYERSHIP;
endsub;
Sub variable pre-pass — the compiler scans each 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 += 1;    $count -= 1;    $count *= 2;    $count /= 2;
++$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 = 1;
$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:

$x = Utils::random(10);  // same as random(10)
$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(10);    // random(max) — 1 argument
$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(“plugin.myscript”);  // string literal — CALLNAME variant
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:

#DESCRIPTION “My plugin script”
#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 $wing DATATYPE_WING
#datatype $obj DATATYPE_SHIP|DATATYPE_STATION

Multiple types are separated by |. This is a hint only — it generates no script output.

Defines

#define MAX_COUNT 100
#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:

#define BIG_VALUE \
    100 + 200 + 300

#define COMPLEX_CHECK(a, b) \
    (a > 0) && \
    (b > 0)

Conditional Compilation

#define PLATFORM_PC

#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:

XScriptCompiler.exe –load_data data.dat –compile script.xs –out script.xml –define:DEBUG –define:PLATFORM_PC

Include Files

Include a utility script inline at any point — paths are relative to the including file’s directory:

#include “utils.xs”
#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:

foreach($item, $myArray)
{
    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:

foreach($item, $myArray)
    incomingMessage($item->name, PlayerLog::Alert, TRUE);
Accurate warnings — if $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:

Compile Error [#5]:   [myscript.xs:12:4]  – Unknown function ‘createSheep’
    $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:

$cmd = SHIPCOMMAND_1000;  // mod-added command with ID 1000

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:

XScriptCompiler.exe –builddata x3fl.xml –out default_data.dat

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.

Syntax highlighting

Keywords, operators, constants, variables, strings, and all preprocessor directives — including #datatype and multi-line #define — are coloured correctly.

IntelliSense

Autocomplete for functions, methods, properties, constants, namespaces (Utils::), and function macros (foreach) — with parameter hints and inline documentation.

Hover docs

Hover over any function, constant, namespace member, or macro to see its description, parameters, and return type.

Error squiggles

Compiler errors and warnings appear as underlines directly in the editor and in the Problems panel.

One-click compile

Click the play button in the editor title bar, or right-click for compile options.

Compile on save

Optionally compile automatically every time a .xs file is saved.

Installing the Extension

  1. Extract xscript-x3fl-extension.zip
  2. Open Visual Studio Code
  3. Press Ctrl+Shift+X to open the Extensions panel
  4. Click the menu (top right of the panel) and choose Install from VSIX…
  5. Select the .vsix file (run vsce package inside 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:

SettingDescription
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 CALLNAME vs 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 native while loops at compile time; additional macros can be defined in x3fl.xml
  • #datatype preprocessor directive — hints a variable’s type(s) for the type checker
  • Multi-line #define — trailing \ continues a define onto subsequent lines
  • Fixed nested if blocks producing incorrect consecutive closing braces
  • Fixed $x - 2 being misparsed as negation; random(-1) and other negative-literal arguments now compile correctly
  • --usenamespace decompiler flag — emit Utils::random style calls when decompiling
  • VS Code extension v1.3.0 — namespace/macro autocomplete, hover, signature help; #datatype and multi-line #define syntax highlighting

0.6 BETA

  • While loop condition re-evaluation — ++/-- and function calls in while(...) conditions re-evaluate correctly on each iteration
  • break and continue inside while loops
  • Nested function calls as arguments — random($i + 10), if(inc($count)), etc.
  • inc/dec with 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 if with function calls in condition — temp vars correctly hoisted before the whole if/else chain
  • Full preprocessor system — #define, #undef, #ifdef, #ifndef, #elseif, #else, #endif with value comparisons and nesting
  • Script metadata directives — #DESCRIPTION, #VERSION, #COMMAND
  • #include — inline file inclusion with circular include detection
  • Command-line defines — --define:NAME flag (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.defines setting

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/FALSE in 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