Skip to content

Commit

Permalink
burn scene
Browse files Browse the repository at this point in the history
  • Loading branch information
kion-dgl committed Oct 10, 2024
1 parent 3db2b40 commit 92114d0
Show file tree
Hide file tree
Showing 6 changed files with 286 additions and 57 deletions.
Binary file added bin/flutter-ST1ET.BIN
Binary file not shown.
2 changes: 2 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { updateYosyonkePaintings2 } from "./src/ST0AT";
import { updateYosyonkePaintings3 } from "./src/ST0CT";
import { updateNinoGiftShop } from "./src/ST1AT";
import { updateNinoPigRoom } from "./src/ST1BT";
import { updateFlutterFire } from "./src/ST1ET";

encodeTitle("miku/title.png");
// process.exit();
Expand Down Expand Up @@ -233,6 +234,7 @@ updateFlutterPaintings(
);

updateFlutterPaintings2("miku/paintings/dashie2.png");
updateFlutterFire("miku/paintings/dashie2-burn.png");

updateYosyonkePaintings(
"miku/paintings/room-203.png",
Expand Down
Binary file added miku/paintings/dashie2-burn.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/EncodeRom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,7 @@ const encodeRom = () => {
"GAME.BIN",
"flutter-ST05T.BIN",
"flutter-ST06T.BIN",
"flutter-ST1ET.BIN",
"yosyonke-ST47T.BIN",
"yosyonke-ST0AT.BIN",
"yosyonke-ST0CT.BIN",
Expand Down
223 changes: 223 additions & 0 deletions src/ST1ET.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
/**
Miku-Legends-2
Copyright (C) 2024, DashGL Project
By Kion ([email protected])
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**/

import { readFileSync, writeFileSync } from "fs";
import {
encodeCutSceneTexture,
compressNewTexture,
encodeTexel,
} from "./EncodeTexture";
import { PNG } from "pngjs";

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 decompress = (src: Buffer) => {
const tim = {
type: src.readUInt32LE(0x00),
fullSize: src.readUInt32LE(0x04),
paletteX: src.readUInt16LE(0x0c),
paletteY: src.readUInt16LE(0x0e),
colorCount: src.readUInt16LE(0x10),
paletteCount: src.readUInt16LE(0x12),
imageX: src.readUInt16LE(0x14),
imageY: src.readUInt16LE(0x16),
width: src.readUInt16LE(0x18),
height: src.readUInt16LE(0x1a),
bitfieldSize: src.readUInt16LE(0x24),
payloadSize: src.readUInt16LE(0x26),
};

switch (tim.colorCount) {
case 16:
tim.width *= 4;
break;
case 256:
tim.width *= 2;
break;
default:
tim.paletteCount *= tim.colorCount / 16;
tim.colorCount = 16;
tim.width *= 4;
break;
}

const { fullSize, bitfieldSize } = tim;
const bitfield: number[] = new Array();
const target = Buffer.alloc(fullSize);

// Read Bitfield

const bitfieldBuffer = src.subarray(0x30, 0x30 + bitfieldSize);
let ofs = 0x30;
for (let i = 0; i < bitfieldSize; i += 4) {
const dword = src.readUInt32LE(ofs + i);
for (let k = 31; k > -1; k--) {
bitfield.push(dword & (1 << k) ? 1 : 0);
}
}

ofs += bitfieldSize;
const payloadStart = 0;

// Decompress

let outOfs = 0;
let windowOfs = 0;
let cmdCount = 0;
let bytes = 0;

for (let i = 0; i < bitfield.length; i++) {
const bit = bitfield[i];
if (outOfs === fullSize) {
const payload = src.subarray(0x30 + bitfieldSize, ofs);
break;
}

const word = src.readUInt16LE(ofs);
ofs += 2;

switch (bit) {
case 0:
target.writeUInt16LE(word, outOfs);
outOfs += 2;
break;
case 1:
if (word === 0xffff) {
windowOfs += 0x2000;
cmdCount = 0;
bytes = 0;
} else {
cmdCount++;
const copyFrom = windowOfs + ((word >> 3) & 0x1fff);
const copyLen = ((word & 0x07) + 2) * 2;
bytes += copyLen;
for (let i = 0; i < copyLen; i++) {
target[outOfs++] = target[copyFrom + i];
}
}
break;
}
}

return target;
};

const updateFlutter = (bin: Buffer, pngPath: string) => {
const pngData = readFileSync(pngPath);

const imgOfs = 0x02a800;
// 38295 (light brown)
const pal: number[] = [];
const encodedLogo = encodeCutSceneTexture(pal, pngData);
const mpTexture = decompress(Buffer.from(bin.subarray(imgOfs)));

const includedPal = Buffer.from(mpTexture.subarray(0, 0x20));
const encodedTexture = Buffer.from(mpTexture.subarray(0x20));

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

const ROW_LEN = 0x80;
const X_START = 0;
const Y_START = 88;
let texOfs = ROW_LEN * Y_START; // + PAL_OFS;
let logoOfs = 0;
const HEIGHT = 40;
const WIDTH = 64;
for (let y = 0; y < HEIGHT; y++) {
texOfs += X_START / 2;
for (let x = 0; x < WIDTH / 2; x++) {
encodedTexture[texOfs++] = encodedLogo[logoOfs++];
}
texOfs += (256 - X_START - WIDTH) / 2;
}

// console.log("Logo Pos: 0x%s", logoOfs.toString(16));

const imageData: number[] = new Array();
for (let ofs = 0; ofs < encodedTexture.length; ofs++) {
const byte = encodedTexture.readUInt8(ofs);

imageData.push(byte & 0xf);
imageData.push(byte >> 4);
}

const [bodyBitField, compressedBody] = compressNewTexture(
includedPal,
encodedTexture,
1,
);
const len = bodyBitField.length + compressedBody.length;

for (let i = 0x2a830; i < 0x2f110; i++) {
bin[i] = 0;
}

let ofs = 0x2a830;
for (let i = 0; i < bodyBitField.length; i++) {
bin[ofs++] = bodyBitField[i];
}

for (let i = 0; i < compressedBody.length; i++) {
bin[ofs++] = compressedBody[i];
}

console.log("End: 0x%s", ofs.toString(16));

if (ofs <= 0x2f000) {
console.log("too short!!!");
throw new Error("fire flutter painting too short");
} else if (len > 0x2f800) {
console.log("too long");
throw new Error("fire flutter painting too long");
} else {
console.log("yaya!!!");
}

bin.writeInt16LE(bodyBitField.length, 0x2a824);
};

const updateFlutterFire = (dashie: string) => {
const bin = readFileSync("bin/flutter-ST1ET.BIN");
updateFlutter(bin, dashie);
writeFileSync("out/flutter-ST1ET.BIN", bin);
};

export default updateFlutterFire;
export { updateFlutterFire };
117 changes: 60 additions & 57 deletions test/yosyonke.skip
Original file line number Diff line number Diff line change
Expand Up @@ -263,66 +263,69 @@ const renderImg = (

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

test("it should search for textures in the flutter", () => {
const src = readFileSync("bin/cut-ST0305.BIN");
const pals: Pixel[][] = [
[
{ r: 0, g: 0, b: 0, a: 0 },
{ r: 0, g: 0, b: 0, a: 255 },
{ r: 16, g: 16, b: 16, a: 255 },
{ r: 32, g: 32, b: 32, a: 255 },
{ r: 48, g: 48, b: 48, a: 255 },
{ r: 64, g: 64, b: 64, a: 255 },
{ r: 72, g: 72, b: 72, a: 255 },
{ r: 90, g: 90, b: 90, a: 255 },
{ r: 110, g: 110, b: 110, a: 255 },
{ r: 120, g: 120, b: 120, a: 255 },
{ r: 130, g: 130, b: 130, a: 255 },
{ r: 140, g: 140, b: 140, a: 255 },
{ r: 150, g: 150, b: 150, a: 255 },
{ r: 160, g: 160, b: 160, a: 255 },
{ r: 190, g: 190, b: 190, a: 255 },
{ r: 210, g: 210, b: 210, a: 255 },
{ r: 220, g: 220, b: 220, a: 255 },
{ r: 255, g: 255, b: 255, a: 255 },
],
];

for (let i = 0; i < src.length; i += 0x800) {
const tim = {
type: src.readUInt32LE(i + 0x00),
fullSize: src.readUInt32LE(i + 0x04),
paletteX: src.readUInt16LE(i + 0x0c),
paletteY: src.readUInt16LE(i + 0x0e),
colorCount: src.readUInt16LE(i + 0x10),
paletteCount: src.readUInt16LE(i + 0x12),
imageX: src.readUInt16LE(i + 0x14),
imageY: src.readUInt16LE(i + 0x16),
width: src.readUInt16LE(i + 0x18),
height: src.readUInt16LE(i + 0x1a),
bitfieldSize: src.readUInt16LE(i + 0x24),
payloadSize: src.readUInt16LE(i + 0x26),
};

if (tim.type !== 2) {
continue;
}
const name = `out/${base}_${pos.toString(16)}.png`;
console.log(name);

if (tim.width == 0 || tim.height == 0) {
continue;
}
writeFileSync(name, buffer);
};

const img = src.subarray(i);
renderImg(img, "scene", i, pals[0]);
}
});
// test("it should search for textures in the flutter", () => {
// const src = readFileSync("bin/flutter-ST1ET.BIN");
// const pals: Pixel[][] = [
// [
// { r: 0, g: 0, b: 0, a: 0 },
// { r: 0, g: 0, b: 0, a: 255 },
// { r: 16, g: 16, b: 16, a: 255 },
// { r: 32, g: 32, b: 32, a: 255 },
// { r: 48, g: 48, b: 48, a: 255 },
// { r: 64, g: 64, b: 64, a: 255 },
// { r: 72, g: 72, b: 72, a: 255 },
// { r: 90, g: 90, b: 90, a: 255 },
// { r: 110, g: 110, b: 110, a: 255 },
// { r: 120, g: 120, b: 120, a: 255 },
// { r: 130, g: 130, b: 130, a: 255 },
// { r: 140, g: 140, b: 140, a: 255 },
// { r: 150, g: 150, b: 150, a: 255 },
// { r: 160, g: 160, b: 160, a: 255 },
// { r: 190, g: 190, b: 190, a: 255 },
// { r: 210, g: 210, b: 210, a: 255 },
// { r: 220, g: 220, b: 220, a: 255 },
// { r: 255, g: 255, b: 255, a: 255 },
// ],
// ];

// for (let i = 0; i < src.length; i += 0x800) {
// const tim = {
// type: src.readUInt32LE(i + 0x00),
// fullSize: src.readUInt32LE(i + 0x04),
// paletteX: src.readUInt16LE(i + 0x0c),
// paletteY: src.readUInt16LE(i + 0x0e),
// colorCount: src.readUInt16LE(i + 0x10),
// paletteCount: src.readUInt16LE(i + 0x12),
// imageX: src.readUInt16LE(i + 0x14),
// imageY: src.readUInt16LE(i + 0x16),
// width: src.readUInt16LE(i + 0x18),
// height: src.readUInt16LE(i + 0x1a),
// bitfieldSize: src.readUInt16LE(i + 0x24),
// payloadSize: src.readUInt16LE(i + 0x26),
// };

// if (tim.type !== 2 && tim.type !== 3) {
// continue;
// }

// if (tim.width == 0 || tim.height == 0) {
// continue;
// }

// const img = src.subarray(i);
// renderImg(img, "scene", i, pals[0]);
// }
// });

test("it should search for room203 palette", () => {
const src = readFileSync("bin/cut-ST0305.BIN");
const img = src.subarray(0x1a000);
const src = readFileSync("bin/flutter-ST1ET.BIN");
const img = src.subarray(0x02a800);

for (let i = 0; i < src.length; i += 0x800) {
const tim = {
Expand Down Expand Up @@ -354,6 +357,6 @@ test("it should search for room203 palette", () => {
pal.push(wordToColor(word));
}

renderImg(img, "poster", i, pal);
renderImage(img, "poster", i, pal);
}
});

0 comments on commit 92114d0

Please sign in to comment.