diff --git a/.github/workflows/cd-assets.yml b/.github/workflows/cd-assets.yml index 250cd6706..571617528 100644 --- a/.github/workflows/cd-assets.yml +++ b/.github/workflows/cd-assets.yml @@ -18,6 +18,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + submodules: recursive - name: Use node uses: actions/setup-node@v4 diff --git a/.gitignore b/.gitignore index 3464b9cc4..7dc3942e0 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ tsconfig.tsbuildinfo temp/ /.yarn /.VSCodeCounter -/.yarn \ No newline at end of file +/.yarn +depots \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index b72b99f10..8c41337cf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,6 +7,7 @@ "alphatest", "APAC", "apcr", + "applypatches", "artefacts", "atgm", "AYUV", @@ -21,6 +22,7 @@ "CDON", "classname", "cleandist", + "closedkrieg", "cockroackdb", "Coef", "cooldown", diff --git a/package.json b/package.json index b4f9ff3f3..8e8b27fca 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "start:website": "next start", "host:docs": "vitepress preview docs", "publish:ratingsleaderboard": "vite-node --script scripts/buildRatingsLeaderboard --publish", + "applypatches": "vite-node --script scripts/applyPatches", "lint": "npx tsc", "lint:website": "next lint", "format": "prettier --write \"**/*.@(json|js|jsx|css|md|ts|tsx|html|xml|yml|gltf)\"", diff --git a/scripts/applyPatches.ts b/scripts/applyPatches.ts new file mode 100644 index 000000000..5afa8c109 --- /dev/null +++ b/scripts/applyPatches.ts @@ -0,0 +1,44 @@ +import { mkdir, writeFile } from 'fs/promises'; +import { parse as parsePath } from 'path'; +import { parse as parseYaml } from 'yaml'; +import { readStringDVPL } from '../src/core/blitz/readStringDVPL'; +import { writeDVPL } from '../src/core/blitz/writeDVPL'; +import { secrets } from '../src/core/blitzkrieg/secrets'; +import { dvp } from '../submodules/closedkrieg/src/dvp'; +import { DATA } from './buildAssets/constants'; + +const versionTextFile = await readStringDVPL(`${DATA}/version.txt.dvpl`); +const currentVersion = versionTextFile + .split(' ')[0] + .split('.') + .slice(0, 3) + .join('.'); + +let patchIndex = 1; +while (true) { + const response = await fetch( + `${secrets.WOTB_DLC_CDN}/dlc/s${currentVersion}_${patchIndex}.yaml`, + ); + + if (response.status === 200) { + const data = parseYaml(await response.text()); + const dvpm = await fetch(`${secrets.WOTB_DLC_CDN}/dlc/${data.dx11}`).then( + (response) => response.arrayBuffer(), + ); + const dvpd = await fetch( + `${secrets.WOTB_DLC_CDN}/dlc/${data.dx11.replace('.dvpm', '.dvpd')}`, + ).then((response) => response.arrayBuffer()); + const files = await dvp(dvpm, dvpd); + + await Promise.all( + files.map(async ({ path, data }) => { + const { dir } = parsePath(path); + + await mkdir(`${DATA}/${dir}`, { recursive: true }); + await writeFile(`${DATA}/${path}.dvpl`, writeDVPL(Buffer.from(data))); + }), + ); + + patchIndex++; + } else break; +} diff --git a/scripts/buildAssets/constants.ts b/scripts/buildAssets/constants.ts index 75907fa3a..d7649d279 100644 --- a/scripts/buildAssets/constants.ts +++ b/scripts/buildAssets/constants.ts @@ -24,7 +24,7 @@ export const POI = { collisionMeshes: '3d/Tanks/CollisionMeshes', optionalDevices: 'XML/item_defs/vehicles/common/optional_devices.xml', optionalDeviceSlots: - 'XML/item_defs/vehicles/common/optional_device_slots.xml', + 'XML/item_defs/vehicles/common/optional_device_slots.xml', optionalDeviceImageMappings: 'UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml', optionalDevicesImage: diff --git a/src/core/blitz/writeDVPL.ts b/src/core/blitz/writeDVPL.ts new file mode 100644 index 000000000..01617086f --- /dev/null +++ b/src/core/blitz/writeDVPL.ts @@ -0,0 +1,50 @@ +import { crc32 } from 'crc'; +import { encodeBlockHC } from 'lz4'; + +/** + * Thanks Maddoxkkm! Modernized heavily. + * + * https://github.com/Maddoxkkm/dvpl_converter/ + */ +export function writeDVPL(buffer: Buffer) { + let output = Buffer.alloc(buffer.length); + const compressedBlockSize = encodeBlockHC(buffer, output); + + if (compressedBlockSize === 0 || compressedBlockSize >= buffer.length) { + const footerBuffer = toDVPLFooter( + buffer.length, + buffer.length, + crc32(buffer), + 0, + ); + + return Buffer.concat([buffer, footerBuffer], buffer.length + 20); + } else { + output = output.subarray(0, compressedBlockSize); + const footerBuffer = toDVPLFooter( + buffer.length, + compressedBlockSize, + crc32(output), + 2, + ); + + return Buffer.concat([output, footerBuffer], compressedBlockSize + 20); + } +} + +function toDVPLFooter( + inputSize: number, + compressedSize: number, + crc32: number, + type: number, +) { + const result = Buffer.alloc(20); + + result.writeUInt32LE(inputSize, 0); + result.writeUInt32LE(compressedSize, 4); + result.writeUInt32LE(crc32, 8); + result.writeUInt32LE(type, 12); + result.write('DVPL', 16, 4, 'utf8'); + + return result; +}