diff --git a/packages/scripts/src/buildAssets/tankIcons.ts b/packages/scripts/src/buildAssets/tankIcons.ts index 3a0fbfc22..fb74388d9 100644 --- a/packages/scripts/src/buildAssets/tankIcons.ts +++ b/packages/scripts/src/buildAssets/tankIcons.ts @@ -1,6 +1,7 @@ import { NATION_IDS } from "@blitzkit/core"; import sharp from "sharp"; import type { Vector3Tuple } from "three"; +import { parsePackedSpriteRect } from "../core/blitz/parsePackedSpriteRect"; import { AssetUploader } from "../core/github/assetUploader"; import { vfs } from "./constants"; import type { VehicleDefinitionList } from "./definitions"; @@ -37,6 +38,45 @@ export interface TankParameters { }; } +async function extractPackedTankIcon(packedBuffer: Buffer, context: string) { + const texture = sharp(packedBuffer); + const spriteRect = parsePackedSpriteRect(packedBuffer); + + if (!spriteRect) { + throw new Error(`Missing RIFF sprite bounds for ${context}`); + } + + const [left, top, width, height] = spriteRect; + const bounds = [left, top, width, height]; + + if (!bounds.every(Number.isInteger)) { + throw new Error( + `Invalid RIFF sprite bounds for ${context}: ${bounds.join(" ")}`, + ); + } + + const metadata = await texture.metadata(); + + if (metadata.width === undefined || metadata.height === undefined) { + throw new Error(`Failed to read image dimensions for ${context}`); + } + + if ( + left < 0 || + top < 0 || + width <= 0 || + height <= 0 || + left + width > metadata.width || + top + height > metadata.height + ) { + throw new Error( + `Out-of-bounds RIFF sprite for ${context}: ${bounds.join(" ")} in ${metadata.width}x${metadata.height}`, + ); + } + + return await texture.extract({ left, top, width, height }).toBuffer(); +} + export async function tankIcons() { console.log("Building tank icons..."); @@ -67,12 +107,14 @@ export async function tankIcons() { const bigPath = `Data/${parameters.resourcesPath.bigIconPath .replace(/~res:\//, "") .replace(/\..+/, "")}.packed.webp`; - const big = await sharp(await vfs.file(bigPath)) - .trim() - .toBuffer(); - const small = await sharp(await vfs.file(smallPath)) - .trim() - .toBuffer(); + const big = await extractPackedTankIcon( + await vfs.file(bigPath), + `tank ${tankKey} big`, + ); + const small = await extractPackedTankIcon( + await vfs.file(smallPath), + `tank ${tankKey} small`, + ); if (big) { await uploader.add({