mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
Merge pull request #234 from karelpak1/feat-riff-container
This commit is contained in:
2 files changed
+118
-4
No files matched your search
@@ -1,5 +1,6 @@
|
|||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { AssetUploader } from "../core/github/assetUploader";
|
import { AssetUploader } from "../core/github/assetUploader";
|
||||||
|
import { parsePackedSpriteRect } from "../core/blitz/parsePackedSpriteRect";
|
||||||
import { vfs } from "./constants";
|
import { vfs } from "./constants";
|
||||||
import { ConsumablesCommon, ProvisionsCommon } from "./definitions";
|
import { ConsumablesCommon, ProvisionsCommon } from "./definitions";
|
||||||
|
|
||||||
@@ -13,6 +14,42 @@ interface Mappings {
|
|||||||
|
|
||||||
const listItemsPattern = /<items path="(.+)\.xml"\/>/g;
|
const listItemsPattern = /<items path="(.+)\.xml"\/>/g;
|
||||||
|
|
||||||
|
async function extractPackedIcon(
|
||||||
|
texture: sharp.Sharp,
|
||||||
|
sizes: number[],
|
||||||
|
context: string,
|
||||||
|
) {
|
||||||
|
const [left, top, width, height] = sizes;
|
||||||
|
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.clone().extract({ left, top, width, height }).toBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
export async function consumableProvisionIcons() {
|
export async function consumableProvisionIcons() {
|
||||||
console.log("Building consumable and provision icons...");
|
console.log("Building consumable and provision icons...");
|
||||||
|
|
||||||
@@ -57,8 +94,20 @@ export async function consumableProvisionIcons() {
|
|||||||
.replace(".txt", "");
|
.replace(".txt", "");
|
||||||
|
|
||||||
if (await vfs.resolve(`Data/${configPath}.packed.webp`)) {
|
if (await vfs.resolve(`Data/${configPath}.packed.webp`)) {
|
||||||
const image = sharp(await vfs.file(`Data/${configPath}.packed.webp`));
|
const packedBuffer = await vfs.file(`Data/${configPath}.packed.webp`);
|
||||||
const content = await image.trim({ threshold: 100 }).toBuffer();
|
const consumablesTexture = sharp(packedBuffer);
|
||||||
|
const sizes = parsePackedSpriteRect(packedBuffer);
|
||||||
|
let content: Buffer;
|
||||||
|
|
||||||
|
if (sizes) {
|
||||||
|
content = await extractPackedIcon(
|
||||||
|
consumablesTexture,
|
||||||
|
sizes,
|
||||||
|
`consumable ${consumable.icon}`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
content = await consumablesTexture.trim({ threshold: 80 }).toBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
await uploader.add({
|
await uploader.add({
|
||||||
path: `icons/consumables/${consumable.id}.webp`,
|
path: `icons/consumables/${consumable.id}.webp`,
|
||||||
@@ -122,8 +171,20 @@ export async function consumableProvisionIcons() {
|
|||||||
.replace(".txt", "");
|
.replace(".txt", "");
|
||||||
|
|
||||||
if (await vfs.resolve(`Data/${configPath}.packed.webp`)) {
|
if (await vfs.resolve(`Data/${configPath}.packed.webp`)) {
|
||||||
const image = sharp(await vfs.file(`Data/${configPath}.packed.webp`));
|
const packedBuffer = await vfs.file(`Data/${configPath}.packed.webp`);
|
||||||
const content = await image.trim({ threshold: 100 }).toBuffer();
|
const provisionsTexture = sharp(packedBuffer);
|
||||||
|
const sizes = parsePackedSpriteRect(packedBuffer);
|
||||||
|
let content: Buffer;
|
||||||
|
|
||||||
|
if (sizes) {
|
||||||
|
content = await extractPackedIcon(
|
||||||
|
provisionsTexture,
|
||||||
|
sizes,
|
||||||
|
`provision ${provision.icon}`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
content = await provisionsTexture.trim({ threshold: 80 }).toBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
await uploader.add({
|
await uploader.add({
|
||||||
path: `icons/provisions/${provision.id}.webp`,
|
path: `icons/provisions/${provision.id}.webp`,
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
const textDecoder = new TextDecoder();
|
||||||
|
|
||||||
|
function readFourCC(buffer: Uint8Array, offset: number) {
|
||||||
|
return textDecoder.decode(buffer.subarray(offset, offset + 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parsePackedSpriteRect(buffer: Uint8Array): number[] | null {
|
||||||
|
if (
|
||||||
|
buffer.length < 12 ||
|
||||||
|
readFourCC(buffer, 0) !== "RIFF" ||
|
||||||
|
readFourCC(buffer, 8) !== "WEBP"
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const view = new DataView(
|
||||||
|
buffer.buffer,
|
||||||
|
buffer.byteOffset,
|
||||||
|
buffer.byteLength,
|
||||||
|
);
|
||||||
|
let offset = 12;
|
||||||
|
|
||||||
|
while (offset + 8 <= buffer.length) {
|
||||||
|
const chunkId = readFourCC(buffer, offset);
|
||||||
|
const chunkSize = view.getUint32(offset + 4, true);
|
||||||
|
const dataStart = offset + 8;
|
||||||
|
const dataEnd = dataStart + chunkSize;
|
||||||
|
|
||||||
|
if (dataEnd > buffer.length) return null;
|
||||||
|
|
||||||
|
if (chunkId === "extr") {
|
||||||
|
const lines = textDecoder
|
||||||
|
.decode(buffer.subarray(dataStart, dataEnd))
|
||||||
|
.replace(/\0/g, "")
|
||||||
|
.split(/\r?\n/)
|
||||||
|
.map((line) => line.trim().split(/\s+/).map(Number));
|
||||||
|
|
||||||
|
for (const values of lines) {
|
||||||
|
if (values.length < 4) continue;
|
||||||
|
if (!values.slice(0, 4).every(Number.isFinite)) continue;
|
||||||
|
if (values[2] <= 0 || values[3] <= 0) continue;
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = dataEnd + (chunkSize % 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user