XScript Compiler

Version 0.8 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.

User-defined functions v0.8

function test($arg) { return($x); } — local-scoped, with full variable isolation. Call like any built-in function.

Sub block syntax v0.8

sub myHelper() { ... } — readable wrapper for the native label/gosub/endsub pattern, sharing the global variable scope.

Decompiler

Convert existing compiled XML scripts back to readable XScript source — now outputs v0.8 syntax with function main() and sub blocks.

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.8 is functional but under active development. Please report any issues.
FileDescription
XScriptCompiler-0.8.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.4.0 — syntax highlighting, IntelliSense, and compiler integration

⬇ Download XScript 0.8 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. Every v0.8 script wraps its body in a function main(...) block:

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

function main(VARSECTOR $sector)
{
    $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.

Script Structure v0.8

Every v0.8 script must wrap its body in a function main(...) block. Optional metadata directives and user-defined functions/subs appear outside it:

#DESCRIPTION “My plugin script”
#VERSION 1

// Optional: user-defined functions and subs
function DATATYPE_INT clamp(NUMBER $v, NUMBER $lo, NUMBER $hi)
{
    if ($v < $lo) { return($lo); }
    if ($v > $hi) { return($hi); }
    return($v);
}

sub initialise()
{
    $count = 0;
}

// Required: main function
function main(VARSECTOR $sector)
{
    gosub initialise;
    $count = clamp($count, 0, 100);
    return(null);
}

User-Defined Functions v0.8

Define your own reusable functions with full variable isolation. All $variables inside a user function are private — the compiler mangles them automatically so they never collide with variables elsewhere in the script.

// Function with typed parameters and a return value
function DATATYPE_INT getSectorCount(VARSECTOR $sector)
{
    if ($sector == NULL) { return(0); }
    $count = $sector->shipCount(TRUE);
    return($count);
}

// Untyped parameters default to VALUE (accepts anything)
function getDoubled($value)
{
    return($value * 2);
}

// Calling user functions
$ships = getSectorCount($sector); // with return value
getDoubled($x); // without

User functions can appear before or after main — forward declarations work automatically via a prepass scan. Recursive calls are allowed but produce a warning.

Sub Blocks v0.8

Subs are the X3-native label:/endsub; pattern in a more readable form. Unlike user functions, subs share the global variable scope — variables assigned inside a sub are visible everywhere.

sub initialise()
{
    $count = 0;
    $flag = TRUE;
}

function main(VARSECTOR $sector)
{
    gosub initialise;
    $count += 1; // $count is accessible here
    return(null);
}

Use endsub; inside a sub body to exit early. The classic label:/endsub; inline syntax also remains valid.

User functionSub
Variable scopePrivate (isolated)Shared with global
ArgumentsDeclared in (...)None
Return valueYes, via return($x)No
Call syntax$r = test($arg);gosub test;

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:

$exists = $ship->exists();
$ship->setCommand(null);

$name = $ship->name; // getter
$ship->name = “My Freighter”; // setter

Nested Function Calls XScript only

Standard X3 scripts require every return value to be assigned to a variable first. XScript allows direct nesting:

Standard X3 (2 lines)
$sector = getSectorByCoord(22, 3);
$name = $sector->name;
XScript (1 line)
$name = getSectorByCoord(22, 3)->name;

Conditions and While Loops

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:

while ($i++ < 10) { … }
while (arraySize($myArray) < 10) { … }

Constants

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

Labels, Goto and Gosub

Plain labels and jump instructions are still available alongside the new sub block syntax:

gosub doWork;

$result = $workVar;
goto cleanup;

cleanup:
    $count = 0;

doWork:
    $workVar = random(10);
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

$x = $y = 1;
$x = $y = random(2);

Namespaces v0.7

$x = Utils::random(10);  // same as random(10)
$flag = RaceFlag::NPC;  // namespaced constant

Function Overloads v0.7

$x = random(10);    // random(max) — 1 argument
$x = random(5, 10);  // random(min, max) — 2 arguments, same name

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

Metadata directives appear before function main(...):

#DESCRIPTION “My plugin script”
#VERSION 42
#COMMAND 1234

Datatype Hints v0.7

#datatype $wing DATATYPE_WING
#datatype $obj DATATYPE_SHIP|DATATYPE_STATION

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.

Conditional Compilation

#ifdef PLATFORM_PC
$platform = 1;
#elseif PLATFORM_LINUX
$platform = 2;
#else
$platform = 0;
#endif

#ifndef DEBUG
$logLevel = 0;
#endif

Comparison operators in #ifdef: ==, !=, >, <, >=, <=. 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 “utils.xs”
#include “common/helpers.xs”

Function Macros v0.7

X3 has no native for/foreach — only while. XScript provides language-level macros that expand to native while loops at compile time:

foreach($item, $myArray)
{
    incomingMessage($item->name, PlayerLog::Alert, TRUE);
}

Additional macros can be defined in x3fl.xml under <Macros>.

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:

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

Rebuilding the Data File

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

VS Code Integration

A Visual Studio Code extension is included (xscript-x3fl-extension.zip) providing full editor support for .xs files.

Syntax highlighting

Keywords (function, sub, endsub, void), function/sub definition headers, return types, variables, operators, strings, and all preprocessor directives.

Snippet completions v1.4

function main, function, sub, if, while — structural templates with tab stops that appear in autocomplete.

IntelliSense

Autocomplete for all ~2,500 functions, methods, properties, constants, namespaces (Utils::), and function macros (foreach).

Hover docs

Hover over any function, constant, or namespace member 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, press Ctrl+Shift+B, or right-click for compile options. Optional compile-on-save.

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) and choose Install from VSIX…
  5. Select the .vsix file and reload when prompted

Configuring the Extension

Place default_data.dat (or x3fl.dat) in your workspace root — the extension loads it automatically on startup.

SettingDescription
xscript.compiler.exePathFull path to XScriptCompiler.exe.
xscript.compiler.dataFilePath to default_data.dat. Auto-detected if left blank.
xscript.compiler.outputDirWhere compiled .xml files are written. Defaults to alongside the source file.
xscript.compiler.compileOnSaveSet to true to compile automatically on every save.
xscript.compiler.definesArray of symbols to pre-define, e.g. ["DEBUG", "PLATFORM_PC"].

Version History

0.8 BETA — Current

  • function main(...) wrapper — all scripts wrap their body in a function main(...) block; script arguments are declared as typed parameters in the function signature instead of SetArgument() calls; #DESCRIPTION, #VERSION, #COMMAND directives replace the equivalent runtime calls; return(value) at end of main
  • User-defined local-scoped functionsfunction name(params) { ... } with full variable isolation; return($x) exits the function (not the script); forward declarations work automatically; recursion warning; name-collision checking against existing commands
  • sub name() { ... } block syntax — readable wrapper for native label/gosub/endsub; subs share the global variable scope; endsub; for early exit; correct output ordering regardless of source position relative to main
  • Pardef-free parametersfunction test($var) defaults untyped parameters to VALUE
  • DATATYPE_* constants in parameter lists — accepted where an exact single-type pardef match exists
  • Duplicate detection — clear errors for name collisions across labels, subs, and user functions; function names checked against existing script commands
  • Decompiler updated — output uses function main(...) syntax; sub blocks (label:/endsub; groups) are emitted as sub name() { ... } outside main; sub. label prefix is stripped automatically
  • VS Code extension v1.4.0function/sub/endsub/void keyword highlighting; function and sub definition header patterns; structural snippet completions

0.7 BETA

  • Chained (double) assignment — $x = $y = -1;
  • Function overloads/aliases — multiple functions sharing a name, resolved by argument count
  • Namespaces — Utils::random(...); namespaced constants (e.g. RaceFlag::NPC)
  • Function macros — foreach($item, $array) { ... } and custom macros in x3fl.xml
  • #datatype preprocessor directive
  • Multi-line #define with trailing \
  • --usenamespace decompiler flag
  • VS Code extension v1.3.0 — namespace/macro autocomplete, hover, signature help

0.6 BETA

  • While loop condition re-evaluation — ++/-- and function calls in while(...) conditions
  • break and continue inside while loops
  • Nested function calls as arguments and in conditions
  • Post-increment in array subscripts
  • Full preprocessor system — #define, #ifdef/#ifndef/#elseif/#else/#endif, #include
  • Script metadata — #DESCRIPTION, #VERSION, #COMMAND
  • Command-line --define:NAME flag
  • VS Code extension v1.2.0 — preprocessor syntax highlighting

0.5 BETA

  • Full XScript language — arrays, tables, compound assignment, namespace constants, object methods/properties
  • Two-pass compilation for correct type tracking
  • Object type propagation
  • DataType prefix support for mod-added commands (SHIPCOMMAND_1000)