Skip to content

Commit

Permalink
encode the textures
Browse files Browse the repository at this point in the history
  • Loading branch information
kion-dgl committed Sep 25, 2024
1 parent 71453b8 commit d5f14ba
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 134 deletions.
Binary file modified miku/title-smol.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file added out/.gitkeep
Empty file.
155 changes: 22 additions & 133 deletions src/GAME.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,52 +20,7 @@
**/

import { readFileSync, writeFileSync } from "fs";
import { PNG } from "pngjs";
import {
encodePalette,
encodeCutSceneTexture,
encodeTexel,
compressNewSegment,
encodeBitfield,
readPixel,
} from "./EncodeTexture";

type Pixel = {
r: number;
g: number;
b: number;
a: number;
};

const wordToColor = (word: number): Pixel => {
const r = ((word >> 0x00) & 0x1f) << 3;
const g = ((word >> 0x05) & 0x1f) << 3;
const b = ((word >> 0x0a) & 0x1f) << 3;
const a = word > 0 ? 255 : 0;
return { r, g, b, a };
};

const compressData = (decompressed: Buffer) => {
const SEGMENT_LENGTH = 0x2000;
const segmentCount = Math.ceil(decompressed.length / SEGMENT_LENGTH);
const segments: Buffer[] = [];
for (let i = 0; i < segmentCount; i++) {
segments.push(
decompressed.subarray(i * SEGMENT_LENGTH, (i + 1) * SEGMENT_LENGTH),
);
}

const bucket: boolean[] = [];
const loads: Buffer[] = [];
segments.forEach((segment, index) => {
const { bits, outBuffer } = compressNewSegment(segment, 0);
bits.forEach((bit) => bucket.push(bit));
loads.push(outBuffer);
});

const bitfied = encodeBitfield(bucket);
return [bitfied, Buffer.concat(loads)];
};
import { encodeCutSceneTexture, compressNewTexture } from "./EncodeTexture";

const decompress = (src: Buffer) => {
const tim = {
Expand Down Expand Up @@ -155,111 +110,45 @@ const decompress = (src: Buffer) => {
}
}

const imageData: number[] = new Array();
for (ofs = 0; ofs < target.length; ofs++) {
const byte = target.readUInt8(ofs);
imageData.push(byte & 0xf);
imageData.push(byte >> 4);
}

return imageData;
};

const encodeImage = (src: Buffer) => {
const face = PNG.sync.read(src);
const { data, width, height } = face;

let inOfs = 0;
const pal: number[] = [];
const img: number[] = [];

for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x += 2) {
const lowByte = readPixel(data, inOfs, pal);
img.push(lowByte);
inOfs += 4;
const highByte = readPixel(data, inOfs, pal);
img.push(highByte);
inOfs += 4;
}
}

return { pal, img };
return target;
};

const updateDemoLogo = (pngPath: string) => {
const bin = readFileSync("bin/GAME.BIN");
const pngData = readFileSync(pngPath);

// Encode Image
const { pal, img } = encodeImage(pngData);
const imgOfs = 0x041800;
const pal: number[] = [];

const encodedLogo = encodeCutSceneTexture(pal, pngData);
const encodedTexture = decompress(Buffer.from(bin.subarray(imgOfs)));

// Update Palette
const palOfs = 0x44800;
for (let i = 0; i < pal.length; i++) {
bin.writeUInt16LE(pal[i], palOfs + 0x30 + i * 2);
}

// Update Image
const imgOfs = 0x041800;
// First thing we want to do is get the decompressed texture
const buffer = Buffer.from(bin.subarray(imgOfs));
const imgData = decompress(buffer);

// Then we splice in our updated encoded texture
const height = 128;
const width = 256;
let inOfs = 0;
let outOfs = 0;
for (let y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
if (y >= 64 && y < 104) {
if (x >= 48 && x < 144) {
imgData[outOfs] = img[inOfs];
inOfs++;
}
}
outOfs++;
}
}
console.log("Encoded Logo: 0x%s", encodedLogo.length.toString(16));
console.log("Encoded Texture: 0x%s", encodedTexture.length.toString(16));

// Write the texture to png to confirm it's working'
const png = new PNG({ width, height });

let index = 0;
let dst = 0;
for (let y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
const colorIndex = imgData[index++];
const { r, g, b, a } = wordToColor(pal[colorIndex!]);
png.data[dst++] = r;
png.data[dst++] = g;
png.data[dst++] = b;
png.data[dst++] = a;
let texOfs = 0x2000;
let logoOfs = 0;
for (let y = 0; y < 40; y++) {
texOfs += 24;
for (let x = 0; x < 48; x++) {
encodedTexture[texOfs++] = encodedLogo[logoOfs++];
}
texOfs += 56;
}

// Export file
const dat = PNG.sync.write(png);
writeFileSync(`out/smol.png`, dat);

// Convert the image data back to a 4bit encoded buffer
const decompressed = Buffer.alloc((width * height) / 2, 0);

index = 0;
outOfs = 0;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x += 2) {
const lowByte = imgData[index++];
const highByte = imgData[index++];
const byte = ((highByte << 4) | lowByte) & 0xff;
decompressed[outOfs] = byte;
outOfs++;
}
}
console.log("Logo Pos: 0x%s", logoOfs.toString(16));

// And then we compress and put it back in
const [bodyBitField, compressedBody] = compressData(decompressed);
const [bodyBitField, compressedBody] = compressNewTexture(
Buffer.alloc(0),
encodedTexture,
0,
);
const len = bodyBitField.length + compressedBody.length;
console.log("Segment 2: 0x%s", len.toString(16));

Expand Down
2 changes: 1 addition & 1 deletion test/logo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ const renderImage = (src: Buffer, pos: number, palette: Pixel[]) => {

// Export file
const buffer = PNG.sync.write(png);
writeFileSync(`out/logos_${pos.toString(16)}.png`, buffer);
writeFileSync(`out/miku_${pos.toString(16)}.png`, buffer);
};

test("it should search for textures in the file", () => {
Expand Down

0 comments on commit d5f14ba

Please sign in to comment.