模组工具。X4:奠基的XML差异与修补工具

模组工具。X4:奠基的XML差异与修补工具

该工具是用于《X4: 基石》的简易XML差异对比与补丁工具。旨在帮助模组制作者对比和修补XML文件。差异XML文件的格式与gave diff.xsd格式定义兼容。这意味着——你可以

Utilities

模组制作工具:X4:基奠的XML差异与补丁工具

本工具集是为X4:基奠设计的简易XML差异与补丁工具。旨在帮助模组制作者比较和修补XML文件。

差异XML文件的格式与相应的 diff.xsd 格式定义兼容。这意味着,你可以使用此工具为游戏中使用的任何XML文件创建差异文件。 同样,你可以使用相应的工具来用差异文件修补XML文件,这样做有助于检查你的差异文件将如何应用到原始XML文件,或者更好地理解其他模组制作者在他们的模组中所做的修改。

重要说明

强烈建议使用 diff.xsd 文件来验证差异XML文件。在使用 XMLDiff.exe 创建它们时尤其重要。

但当前版本的 diff.xsd 文件(随游戏版本 8.00HF3/4 分发)有一个 显著的限制——它对 replace 操作规则非常严格。它只支持 replace 元素中包含一个子元素。这意味着,如果你的文件中 replace 元素包含多个子元素,验证将会失败。因此,在这种情况下请不要使用 diff.xsd 文件进行验证。

如果 diff.xsd 文件位于“当前”文件夹中,它将被自动使用。如果你想使用另一个 diff.xsd 文件,可以使用 -x 选项指定。

如何使用

  • 从以下位置下载最新版本:
    • GitHub 发布页面 - 存档文件 XMLDiffAndPatch.zip
    • NexusMods - 存档文件 XMLDiffAndPatch.zip
  • 将存档文件解压到任意目标位置。
  • 内部将有一个名为 XMLDiffAndPatch 的文件夹,包含两个可执行文件——XMLDiff.exeXMLPatch.exe

如何创建差异文件

以下是 XMLDiff 工具的命令行帮助:

XMLDiff 1.0.1
Developed by Chem O`Dun

  -o, --original_xml            Required. Original XML file or directory.

  -m, --modified_xml            Required. Modified XML file or directory.

  -d, --diff_xml                Required. Output diff file or directory.

  -x, --xsd                     Path to diff.xsd (default: diff.xsd).

  -l, --log-to-file             Enable file logging at the specified level: error|warn|info|debug. Console always logs at info level.

  -a, --append-to-log           (Default: false) Append to existing log file instead of overwriting.

  --only-full-path              (Default: false) Generate only full absolute XPath (no // shorthand).

  --use-all-attributes          (Default: false) Include all attributes in XPath predicates.

  --ignore-diff-in-attribute    Attribute name to ignore when comparing elements.

  --help                        Display this help screen.

  --version                     Display version information.

示例:

XMLDiff.exe -o vanilla.xml -m modified.xml -d diff.xml

生成的差异文件示例

以下是工具创建的差异文件示例:

  • 带有 add 操作:
    <?xml version="1.0" encoding="utf-8" standalone="yes"?>
    <diff>
    <add sel="//ware[@id="scanningarrays"]" pos="before">
        <ware id="xenon_psi_emitter_mk1" name="{1972092403, 7002}" description="{1972092403, 7002}" transport="equipment" volume="1" tags="satellite noplayerbuild">
        <price min="845800" average="901420" max="1054580" />
        <production time="60" amount="0" method="default" name="Xenon Psi Emitter" />
        <production time="60" amount="0" method="xenon" name="Xenon Psi Emitter" />
        <production time="60" amount="0" method="terran" name="Xenon Psi Emitter" />
        <component ref="xenon_psi_emitter_macro" amount="0" />
        <use threshold="0" />
        </ware>
    </add>
    </diff>
  • 带有 replace 操作:
    <?xml version='1.0' encoding='UTF-8'?>
    <diff>
      <replace sel="//do_if[@value="@$speak and not this.assignedcontrolled.nextorder and (@$defaultorder.id != 'Patrol') and (@$defaultorder.id != 'ProtectPosition') and (@$defaultorder.id != 'ProtectShip') and (@$defaultorder.id != 'ProtectStation') and (@$defaultorder.id != 'Plunder') and (@$defaultorder.id != 'Police') and (not this.assignedcontrolled.commander or (this.assignedcontrolled.commander == player.occupiedship)) and notification.npc_await_orders.active"]/@value">@$speak and not this.assignedcontrolled.nextorder and (@$defaultorder.id != 'ProtectSector') and (@$defaultorder.id != 'Patrol') and (@$defaultorder.id != 'ProtectPosition') and (@$defaultorder.id != 'ProtectShip') and (@$defaultorder.id != 'ProtectStation') and (@$defaultorder.id != 'Plunder') and (@$defaultorder.id != 'Police') and (not this.assignedcontrolled.commander or (this.assignedcontrolled.commander == player.occupiedship)) and notification.npc_await_orders.active</replace>
    </diff>

路径选项

默认:全局唯一时使用 // 简写

默认情况下,当元素在文档中全局唯一时,工具会使用 // XPath 简写,生成更短、更具可读性的 sel 路径。

示例:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<diff>
<add sel="//ware[@id="scanningarrays"]" pos="before">
    <ware id="xenon_psi_emitter_mk1" ...>
    ...
    </ware>
</add>
</diff>

--only-full-path:始终使用完整绝对路径

--only-full-path 选项强制工具始终生成完整路径(以 /rootElement/... 开头),并且从不使用 // 简写。

示例:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<diff>
<add sel="/wares/ware[@id="scanningarrays"]" pos="before">
    <ware id="xenon_psi_emitter_mk1" name="{1972092403, 7002}" description="{1972092403, 7002}" transport="equipment" volume="1" tags="satellite noplayerbuild">
    <price min="845800" average="901420" max="1054580" />
    <production time="60" amount="0" method="default" name="Xenon Psi Emitter" />
    <production time="60" amount="0" method="xenon" name="Xenon Psi Emitter" />
    <production time="60" amount="0" method="terran" name="Xenon Psi Emitter" />
    <component ref="xenon_psi_emitter_macro" amount="0" />
    <use threshold="0" />
    </ware>
</add>
</diff>

最小化XPath属性谓词

工具仅在需要使路径步骤在其父元素内唯一时,才会添加属性谓词。如果元素名称在那一级已经唯一,则不添加任何属性。只有直到实现唯一性时,才逐一追加额外的属性。

与总是包含第一个属性(无论是否需要)相比,这会产生更简洁、更具可读性的 sel 路径。

在XPath中使用所有属性

--use-all-attributes 选项强制将元素的所有属性包含在其 XPath 谓词中,无论其唯一性如何。当需要最大特异性时很有用。

示例:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<diff>
<add sel="/wares/ware[@id="scanningarrays"][@name="{20201,3301}"][@description="{20201,3302}"][@factoryname="{20201,3304}"][@group="hightech"][@transport="container"][@volume="38"][@tags="container economy"]" pos="before">
    <ware id="xenon_psi_emitter_mk1" name="{1972092403, 7002}" description="{1972092403, 7002}" transport="equipment" volume="1" tags="satellite noplayerbuild">
    <price min="845800" average="901420" max="1054580" />
    <production time="60" amount="0" method="default" name="Xenon Psi Emitter" />
    <production time="60" amount="0" method="xenon" name="Xenon Psi Emitter" />
    <production time="60" amount="0" method="terran" name="Xenon Psi Emitter" />
    <component ref="xenon_psi_emitter_macro" amount="0" />
    <use threshold="0" />
    </ware>
</add>
</diff>

忽略指定属性的差异

--ignore-diff-in-attribute 选项会在比较元素时忽略指定属性的差异。当你想要忽略诸如 versioncomment 等属性的差异时很有用。

通过内联注释定义差异的位置生成

addpos 属性通常设置为 after,考虑到程序运行的通用逻辑。但在某些情况下,你可能希望差异文件的 pos 属性设置为 before。这在配合 VS Code 使用时特别有用。 你可以通过在原始XML文件中使用内联注释来定义差异文件中 add 元素的位置。注释应放置在要添加的元素之前,并包含文本

如何应用差异文件

以下是 XMLPatch 工具的命令行帮助:

XMLPatch 1.0.1
Developed by Chem O`Dun

  -o, --original_xml    Required. Path to the original XML file or directory.

  -d, --diff_xml        Required. Path to the diff XML file or directory.

  -u, --output_xml      Required. Path for the output XML file or directory.

  -x, --xsd             Path to diff.xsd (default: diff.xsd).

  -l, --log-to-file     Enable file logging at the specified level: error|warn|info|debug. Console always logs at info level.

  -a, --append-to-log   (Default: false) Append to existing log file instead of overwriting.

  --allow-doubles       (Default: false) Skip duplicate-element guard when applying <add> operations.

  --help                Display this help screen.

  --version             Display version information.

示例:

XMLPatch.exe -o vanilla.xml -d diff.xml -u modified.xml

生成的修补XML文件示例

以下是工具创建的修补XML文件示例:

  • 带有 add 操作:
      <ware id="satellite_mk2" name="{20201,20401}" description="{20201,20402}" transport="equipment" volume="1" tags="equipment satellite">
        <price min="44380" average="52215" max="60045"/>
        <production time="60" amount="1" method="default" name="{20206,101}">
          <primary>
            <ware ware="advancedelectronics" amount="5"/>
            <ware ware="energycells" amount="10"/>
            <ware ware="scanningarrays" amount="5"/>
          </primary>
        </production>
        <production time="60" amount="1" method="xenon" name="{20206,601}" tags="noplayerbuild">
          <primary>
            <ware ware="energycells" amount="10"/>
            <ware ware="silicon" amount="1"/>
          </primary>
        </production>
        <component ref="eq_arg_satellite_02_macro"/>
        <use threshold="0"/>
      </ware>
      <ware id="xenon_psi_emitter_mk1" name="{1972092403, 7002}" description="{1972092403, 7002}" transport="equipment" volume="1" tags="satellite noplayerbuild">
        <price min="845800" average="901420" max="1054580"/>
        <production time="60" amount="0" method="default" name="Xenon Psi Emitter"/>
        <production time="60" amount="0" method="xenon" name="Xenon Psi Emitter"/>
        <production time="60" amount="0" method="terran" name="Xenon Psi Emitter"/>
        <component ref="xenon_psi_emitter_macro" amount="0"/>
        <use threshold="0"/>
      </ware>
  • 带有 replace 操作:
        <set_to_default_flight_control_model object="this.assignedcontrolled"/>
        <set_value name="$defaultorder" exact="this.assignedcontrolled.defaultorder"/>
        <do_if value="@$speak and not this.assignedcontrolled.nextorder and (@$defaultorder.id != 'Patrol') and (@$defaultorder.id != 'ProtectSector') and (@$defaultorder.id != 'ProtectPosition') and (@$defaultorder.id != 'ProtectShip') and (@$defaultorder.id != 'ProtectStation') and (@$defaultorder.id != 'Plunder') and (@$defaultorder.id != 'Police') and (not this.assignedcontrolled.commander or (this.assignedcontrolled.commander == player.occupiedship)) and notification.npc_await_orders.active">
          <set_value name="$speakline" exact="10304" comment="Awaiting orders."/>

如果输出XML是目录

如果输出XML是目录,工具将在输出目录中创建一个与原始XML文件名相同的新XML文件。 例如,如果原始XML文件是 vanilla.xml,输出目录是 output,工具将创建一个新XML文件 output/vanilla.xml

如何将工具应用于目录

将XMLDiff应用于目录

您可以将XMLDiff工具应用于目录。在这种情况下,工具将遍历目录结构并为具有相同名称和相对路径的XML文件创建差异文件。过程如下:

  • 如果所有输入参数都是目录,工具将为目录中的所有XML文件创建差异文件。
  • 工具将递归遍历目录结构,使用修改后的文件作为“键”。
  • 对于每个更改的文件,将检查原始目录中具有相同相对路径的相应原始XML文件。
  • 如果找不到原始XML文件,则跳过该操作。
  • 如果找到原始XML文件,则在输出目录中创建具有相同相对路径的差异文件。

示例:

XMLDiff.exe -o vanilla_dir -m modified_dir -d diff_dir

将XMLPatch应用于目录

您可以将XMLPatch工具应用于目录。在这种情况下,工具将遍历目录结构并将补丁应用于具有相同名称和相对路径的XML文件。过程如下:

  • 如果所有输入参数都是目录,工具将为目录中的所有XML文件应用补丁。
  • 工具将递归遍历目录结构,使用差异文件作为“键”。
  • 对于每个差异文件,将检查原始目录中具有相同相对路径的相应原始XML文件。
  • 如果找不到原始XML文件,则跳过该操作。
  • 如果找到原始XML文件,则将差异文件与原始XML文件修补,并在输出目录中创建一个新的修补XML文件,具有相同的相对路径。

示例:

XMLPatch.exe -o vanilla_dir -d diff_dir -u modified_dir

问题报告

如果您使用工具有任何问题,请在问题页面上创建一个问题。 如果您能提供所用工具的版本以及相应的 XMLDiff.logXMLPatch.log 文件,我们将不胜感激。 要创建这样的日志文件,请使用 -l 选项并指定 debug 级别,例如 -l debug。控制台输出始终是 info 级别;详细的调试信息仅写入文件。

许可证

本工具根据 Apache License, Version 2.0 许可证授权。您可以在 LICENSE 文件中找到它。

鸣谢

特别感谢 Duncaroos 的耐心、测试和宝贵的反馈。

附加链接

EGOSOFT论坛 上有一个与本工具集相关的主题。

防病毒扫描

请注意——每个发布存档都有指向 VirusTotal 的相应链接。请点击链接以确保存档安全。

更新日志

[1.0.3] - 2026-05-15

  • 修复:
    • 单个属性更改处理。
    • 文本节点比较。

[1.0.2] - 2026-05-15

  • 改进:
    • 日志记录。

[1.0.1] - 2026-05-15

  • 改进:
    • 完全内部重写。

[0.2.31] - 2026-05-15

  • 修复:
    • XMLDiff:replace/remove 操作的属性检测逻辑

[0.2.30] - 2026-05-15

  • 修复:
    • XMLDiff:replace/remove 操作检测逻辑

[0.2.29] - 2026-03-02

  • 改进:
    • XMLDiff:replace 操作检测逻辑

[0.2.28] - 2026-02-22

  • 改进:
    • XMLDiff 和 XMLPatch:增加了处理多行 replace 差异元素的可能性。

[0.2.27] - 2025-08-28

  • 改进:
    • XMLDiff:添加了 --ignore-diff-in-attribute 选项,用于在比较元素时忽略指定属性的差异。当您想忽略 X4 脚本文件中 version 属性的差异时很有用。

[0.2.26] - 2025-06-16

  • 改进:
    • XMLDiff:增加了对修改后的XML文件中 add 元素的内联注释处理。允许在差异文件中定义 add 元素的 position 属性。

[0.2.25] - 2025-03-31

  • 修复:
    • XMLDiff:修复了遗漏根元素属性比较的问题
    • XMLDiff:修复了在后续差异操作中使用已删除元素路径的问题
    • XMLDiff:修复了在 XPath 中寻址元素时索引号不正确的问题
    • XMLDiff:修复了通过 XPath 中的 sibling 关键字识别元素的问题

[0.2.24] - 2025-03-17

  • 改进:
    • 两个工具:结果文件夹将在递归处理中创建,现在与单个文件的方式相同。
    • XMLDiff:在 XPath 中对元素使用 sibling 关键字。
    • XMLDiff:当元素有可以唯一标识它的子元素时,生成 XPath。
  • 更改:
    • XMLDiff:--only-full-path 选项替换为 --anywhere-is-allowed。并且默认行为是使用完整路径。
    • 两个工具:控制台的日志级别不会比日志文件更详细。
    • 两个工具:未知选项将被忽略。
  • 修复:
    • XMLDiff:如果使用了多个属性,XPath 中元素的第一个属性重复出现。

[0.2.23] - 2025-03-15

  • 修复:
    • XMLDiff:修复了最后一个子元素比较的问题(索引越界)

[0.2.22] - 2025-03-10

  • 改进:
    • XMLDiff:元素的第一个属性将始终被添加到 XPath 中,以使差异更清晰。
    • XMLDiff:如果一个属性不足以定义该元素,则将下一个属性添加到 XPath,迭代进行。
    • XMLDiff:添加了关于通过 RunXMLDiff.bat 脚本和相应扩展与 VSCode 集成的简短描述,以便在编辑 XML 文件(修改过的)时“即时”创建差异。

[0.2.21] - 2025-03-03

  • 修复:
    • XMLDiff:修复了文本节点不同的问题
    • XMLDiff:改进了“pos”定义逻辑
    • XMLDiff:改进了日志信息
  • 新增:
    • XMLPatch:添加了 --allow-doubles 选项,对脚本修补很有用
    • XMLPatch:添加了注释处理,现在注释将被添加到结果 XML 文件中

[0.2.20] - 2025-02-27

  • 修复:
    • XMLDiff:路径的错误属性选择
    • XMLDiff:修复了未应用 --append-to-log 选项的问题
    • XMLDiff:修复了更改属性计数检测错误的问题
    • XMLDiff:修复了对元素使用 remove/add 而不是 replace 的问题
    • XMLPatch:跳过没有 diff 元素的差异文件
  • 改进:
    • 两个工具:--log-to-file 选项现在需要日志级别(error、warn、info、debug)作为参数

[0.2.17] - 2025-02-25

  • 修复:
    • 添加了检查以防止在添加过程中出现重复元素

[0.2.16] - 2025-02-25

  • 修复:
    • 修复了元素替换的问题
  • 改进:
    • 日志信息

[0.2.15] - 2025-02-24

  • 修复:
    • 修复了加载 diff.xsd 的问题
    • 修复了结果文件必须位于当前文件夹时的问题
  • 改进: 日志信息,特别是关于错误的 sel 值。记录了更多关于已处理的 XML 元素的信息。
  • 新增:
    • 支持附加到已有的调试日志

[0.2.14] - 2025-01-17

  • 新增:
    • 第一个用 C# 编码的公开版本。