XML Diff Patching
xmldiff
Generate an X4-compatible XML diff patch from a base file and a modified copy.
x4cat xmldiff --base <base.xml> --mod <modified.xml> [-o <output.xml>]
Options:
| Option | Description |
|---|---|
--base FILE | Base/original XML file (required) |
--mod FILE | Modified XML file (required) |
-o, --output FILE | Output diff patch file. If omitted, prints to stdout. |
Examples:
# Generate a diff patch and write to file
x4cat xmldiff --base ./base/assets/units/size_s/macros/ship_arg_s_fighter_01_a_macro.xml \
--mod ./modified/assets/units/size_s/macros/ship_arg_s_fighter_01_a_macro.xml \
-o src/assets/units/size_s/macros/ship_arg_s_fighter_01_a_macro.xml
# Print diff patch to stdout
x4cat xmldiff --base original.xml --mod changed.xml
The generated diff uses <add>, <replace>, and <remove> operations with XPath selectors, following the X4 diff patch format.
validate-diff
Validate XML diff patches against base game files. Checks that every sel XPath in each diff operation matches at least one element in the base game XML.
x4cat validate-diff <game_dir> <mod_dir> [options]
Arguments:
| Argument | Description |
|---|---|
game_dir | Path to X4 install directory |
mod_dir | Directory containing diff patch XML files |
Options:
| Option | Description |
|---|---|
-p, --prefix PREFIX | Catalog filename prefix for game files |
--strict | Treat unsupported XPath warnings as errors (exit code 1) |
Examples:
# Validate all diff patches in src/ against the base game
x4cat validate-diff "/path/to/X4 Foundations" ./src
# Strict mode: fail on any warning
x4cat validate-diff "/path/to/X4 Foundations" ./src --strict
Output format:
libraries/wares.xml:3: PASS sel="/wares" (root element matched)
libraries/wares.xml:4: PASS sel="/wares/ware[@id='energycells']/price/@max" (1 element(s) matched)
libraries/wares.xml:7: FAIL sel="/wares/ware[@id='doesnotexist']" (no elements matched: ware[@id='doesnotexist'])
1 file(s), 3 operation(s): 2 passed, 1 failed, 0 warning(s), 0 skipped
Exit code is 0 if all operations pass, 1 if any fail (or if --strict is set and there are warnings).
validate-schema
Validate MD and AI scripts against schema rules indexed from the game’s XSD files. Checks element names are valid in context and required attributes are present — in milliseconds instead of the 72 seconds that XSD compilation takes.
x4cat validate-schema <mod_dir>
Arguments:
| Argument | Description |
|---|---|
mod_dir | Mod directory containing md/ and/or aiscripts/ |
The index database is auto-detected from ~/.cache/x4cat/. Use the global --db flag to specify a database: x4cat --db path.db validate-schema <mod_dir>.
Checks performed:
- Unknown actions — element inside
<actions>not found in schema groups - Unknown conditions — element inside
<conditions>not found in schema groups - Missing required attributes — required attributes from the schema not present
Example:
x4cat validate-schema src/
# Output:
# ERROR: md/my_script.xml: unknown action <nonexistent_action>
# WARN: md/my_script.xml: <set_value> missing attribute 'name'
#
# 1 error(s), 1 warning(s)
Requires the schema to be indexed. Run x4cat index <game_dir> first. The schema extraction adds XSD rules to the SQLite index alongside the existing wares/macros/components data.
check-conflicts
Detect conflicts between two or more mods’ diff patches by comparing their XPath selectors.
x4cat check-conflicts <mod_dir1> <mod_dir2> [mod_dir3 ...] [--verbose]
Arguments:
| Argument | Description |
|---|---|
mod_dirs | Two or more mod directories to compare |
Options:
| Option | Description |
|---|---|
--verbose | Show safe overlaps (add+add) in addition to conflicts |
Severity levels:
| Level | Pattern | Risk |
|---|---|---|
| CONFLICT | replace + replace on same sel, or remove on a parent of another mod’s replace/add | Last mod wins, first mod’s change is silently lost |
| INFO | Mixed operations on the same sel that may interact | Depends on specifics |
| SAFE | Multiple add operations to the same parent | No conflict — all children get added |
Example:
x4cat check-conflicts ./my_mod ./other_mod
# Output:
# CONFLICT libraries/wares.xml sel="/wares/ware[@id='energy']/price/@max" mods: my_mod, other_mod
#
# 1 shared file(s): 1 conflict(s), 0 safe, 0 info
# With verbose:
x4cat check-conflicts ./my_mod ./other_mod --verbose
# Also shows:
# SAFE libraries/wares.xml sel="/wares" mods: my_mod, other_mod
Exit code is 0 if no conflicts, 1 if any CONFLICT detected.