Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ dist
dist_custom
dist_portable
dist_web
.gdl-out
**/tests/cache
out
doc
coverage
Expand Down
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[submodule "game-description-language"]
path = game-description-language
url = https://github.com/Nexus-Mods/game-description-language.git
branch = main
13 changes: 6 additions & 7 deletions extensions/games/game-xrebirth/build.mjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import * as path from "node:path";

import { createConfig, bundle } from "../../../scripts/extensions-rolldown.mjs";
import { buildGdlExtension } from "../../../scripts/build-gdl-extension.mjs";

const extensionPath = path.resolve(import.meta.dirname);
const entryPoint = path.resolve(extensionPath, "src", "index.ts");
const output = path.resolve(extensionPath, "dist", "index.cjs");

const config = createConfig(entryPoint, output);
await bundle(config);
// This extension is described declaratively in game.yaml and compiled by the
// Game Description Language (GDL) toolchain (the game-description-language
// submodule) into a Vortex-loadable dist/index.js. The imperative bits
// (content.xml installer, health checks) live in src/hooks.ts.
await buildGdlExtension(path.resolve(import.meta.dirname));
149 changes: 149 additions & 0 deletions extensions/games/game-xrebirth/game.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
gdl: 1
version: 1.0.1

game:
id: xrebirth
name: X Rebirth
executable: XRebirth.exe
requiredFiles: [XRebirth.exe]
logo: gameart.webp
author: Black Tree Gaming Ltd.
nexusDomain: xrebirth
# All X Rebirth mods deploy under the game's `extensions` folder.
queryModPath: extensions

stores:
steam: "2870"

# Mod types are classification tags only: every type deploys to the same
# `extensions` folder (matching the original extension, which set modType tags
# but never gave them distinct paths).
modTypes:
- { id: xrebirth-savegame, name: "X Rebirth - savegame", path: "${installPath}/extensions" }
- {
id: xrebirth-shader-injector,
name: "X Rebirth - shader injector",
path: "${installPath}/extensions",
}
- { id: xrebirth-utility, name: "X Rebirth - utility", path: "${installPath}/extensions" }
- { id: xrebirth-dropin, name: "X Rebirth - drop-in", path: "${installPath}/extensions" }
- { id: xrebirth-save-patch, name: "X Rebirth - save patch", path: "${installPath}/extensions" }
- {
id: xrebirth-documentation,
name: "X Rebirth - documentation",
path: "${installPath}/extensions",
}

# Priority order (lower = tried first) mirrors the original PRIORITIES table.
installers:
# 50 - canonical content.xml mod: parses the XML and emits attribute
# instructions, so it runs through a custom install hook.
- id: content-xml
priority: 50
when: { hasFile: "**/content.xml" }
install: { hook: installContentXml }

# 60 - save_NNN.xml / quicksave.xml at any depth (keep archive layout).
- id: savegame
priority: 60
when: { matches: '(?i)(^|/)(quicksave|save_\d+)\.xml$' }
copy: { stripCommonRoot: false }
modType: xrebirth-savegame

# 65 - SweetFX / ReShade shader injector markers.
- id: shader-injector
priority: 65
when:
any:
- { matches: '(?i)(^|/)d3d9\.dll$' }
- { matches: '(?i)(^|/)dxgi\.dll$' }
- { matches: '(?i)(^|/)d3d9\.ini$' }
- { matches: '(?i)(^|/)SweetFX([\\/]|_)' }
- { matches: "(?i)(^|/)reshade-shaders/" }
- { matches: "(?i)(^|/)ReShade/" }
copy: { stripCommonRoot: true }
modType: xrebirth-shader-injector

# 70 - any .exe present (standalone utility/tool).
- id: utility
priority: 70
when: { extensions: { list: [".exe"], mode: any } }
copy: { stripCommonRoot: true }
modType: xrebirth-utility

# 75 - looks like an X Rebirth drop-in: loose game content (archives, game
# subfolders, audio/video/config) that deploys into extensions/ as-is.
- id: dropin
priority: 75
when:
any:
- { hasFile: "**/*.cat" }
- { hasFile: "**/*.dat" }
- { hasFile: "**/*.cur" }
- { hasFile: "**/*.ini" }
- { hasFile: "**/*.{ogg,mp3,wav}" }
- { hasFile: "**/*.{mkv,mp4,webm}" }
- { hasFile: "**/t/*.xml" }
- { hasFile: "**/assets/**" }
- { hasFile: "**/libraries/**/*.xml" }
- { hasFile: "**/maps/**/*.xml" }
- { hasFile: "**/md/**/*.xml" }
- { hasFile: "**/cinematics/**" }
- { hasFile: "**/aiscripts/**/*.xml" }
- { hasFile: "**/voice-*/**/*.{ogg,wav}" }
- { hasFile: "**/ui/**" }
- { hasFile: "**/sfx/**" }
copy: { stripCommonRoot: true }
modType: xrebirth-dropin

# 80 - every file is .xml/.txt and at least one is .xml (XML-only save patch).
- id: save-patch
priority: 80
when:
all:
- { extensions: { list: [".xml", ".txt"], mode: all } }
- { extensions: { list: [".xml"], mode: any } }
copy: { stripCommonRoot: false }
modType: xrebirth-save-patch

# 90 - pure documentation (every file is a doc extension).
- id: documentation
priority: 90
when:
extensions:
list:
[
".pdf",
".png",
".jpg",
".jpeg",
".gif",
".svg",
".xlsx",
".xls",
".docx",
".doc",
".odt",
".ods",
".md",
".rtf",
]
mode: all
copy: { stripCommonRoot: true }
modType: xrebirth-documentation

# Corpus testing: run the installers against real X Rebirth mod manifests
# fetched from Nexus (`gdl test:corpus --fetch`). When the extension is built,
# the corpus drives the real installer chain (including the content.xml hook)
# and health checks; syntheticContent supplies the bytes the hook reads, since
# manifests carry only file lists.
tests:
corpus: nexus
syntheticContent:
content.xml: '<content id="mod-${manifestId}" name="Test ${manifestId}" version="1.0" author="harness"/>'

# In-game health checks (registered via context.registerHealthCheck).
diagnostics:
- hook: modHasFilesCheck
- hook: contentXmlCustomFileNameCheck
- hook: modShapeRecognisedCheck
19 changes: 2 additions & 17 deletions extensions/games/game-xrebirth/package.json

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to update the build nx target config. I recommend adding a new default target to the root nx.json file:

"gdl": [
  "{workspaceRoot}/scripts/build-gdl-extension.mjs",
  "{projectRoot}/game.yaml"
]

And adding gdl to the build inputs:

"build": {
  "inputs": ["default", "typescript", "vortex-api", "gdl"]
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
"version": "1.0.1",
"description": "Support for X Rebirth",
"scripts": {
"_assets": "copyfiles -u 1 -f ./assets/* dist",
"build": "node build.mjs && pnpm run _assets && pnpm extractInfo",
"build": "node build.mjs",
"test": "pnpm exec vitest run"
},
"author": "Black Tree Gaming Ltd.",
Expand All @@ -14,17 +13,13 @@
"config": {
"game": "X Rebirth"
},
"vortex": {
"gameExtensionTest": true
},
"dependencies": {
"xml2js": "catalog:"
},
"devDependencies": {
"@nexusmods/vortex-api": "workspace:*",
"@types/xml2js": "catalog:",
"@vortex/extension-test-mocks": "workspace:*",
"@vortex/game-extension-test": "workspace:*"
"@vortex/extension-test-mocks": "workspace:*"
},
"nx": {
"tags": [
Expand All @@ -37,16 +32,6 @@
"typescript",
"vortex-api"
]
},
"test:game-extensions": {
"executor": "nx:run-commands",
"options": {
"command": "pnpm --filter @vortex/game-extension-test run start -- --game game-xrebirth"
},
"inputs": [
"{projectRoot}/src/**/*.ts"
],
"cache": false
}
}
}
Expand Down
128 changes: 0 additions & 128 deletions extensions/games/game-xrebirth/src/diagnostic.test.ts

This file was deleted.

Loading
Loading