Skip to content

Commit

Permalink
Merge pull request #59 from kion-dgl/58-update-logo-for-megaman-legen…
Browse files Browse the repository at this point in the history
…ds-2-in-demo-loading-screen

58 update logo for megaman legends 2 in demo loading screen
  • Loading branch information
kion-dgl authored Sep 25, 2024
2 parents dd216c6 + d5f14ba commit 83b2746
Show file tree
Hide file tree
Showing 11 changed files with 499 additions and 0 deletions.
Binary file added bin/DEMO.BIN
Binary file not shown.
Binary file added bin/GAME.BIN
Binary file not shown.
Binary file added bin/LOGO.BIN
Binary file not shown.
Binary file added bin/SUBSCN.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 @@ -22,6 +22,7 @@ import {
replaceHunterSeeker,
replaceDrillArm,
} from "./src/EncodeWeapon";
import { updateDemoLogo } from "./src/GAME";

encodeTitle("miku/title.png");
// process.exit();
Expand Down Expand Up @@ -212,6 +213,7 @@ replaceDrillArm("miku/weapons/PL00R10_001.obj");
encodeApronMegaman();
updateST03T("miku/apron/body-01.png", "miku/faces/ST03T.png");
updateSceneModel();
updateDemoLogo("miku/title-smol.png");

/**
Encode Rom
Expand Down
Binary file added 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.
1 change: 1 addition & 0 deletions src/EncodeRom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,7 @@ const encodeRom = () => {
"cut-ST1802T.BIN",
"cut-ST1803.BIN",
"cut-ST2501.BIN",
"GAME.BIN",
];

console.log("--- Replacing Cut Scene Textures ---");
Expand Down
110 changes: 110 additions & 0 deletions src/EncodeTitle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,116 @@ type Command = {
word: number;
};

const updateSmallLogo = (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),
};

tim.width *= 4;

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;
}
}

// Read the image data
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);
}

// Update title to debug palette
const { width, height } = tim;
let index = 0;
for (let y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
if (x >= 48 && x < 48 + 96) {
imageData[index] = Math.floor((x - 48) / 12);
if (y >= 64 + 20 && y < 64 + 40) {
imageData[index] += 8;
}
}
}
}

// Re-encode the image data into a buffer
ofs = 0;
for (let i = 0; i < imageData.length; i += 2) {
const a = imageData[i] & 0x0f;
const b = imageData[i + 1] & 0x0f;
const byte = a | (b << 4);
target.writeUInt8(byte, ofs);
ofs++;
}

return target;
};

const encodeBitfield = (bits: boolean[]): Buffer => {
const length = Math.ceil(bits.length / 32) * 4;
let ofs = 0;
Expand Down
183 changes: 183 additions & 0 deletions src/GAME.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/**
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 } from "./EncodeTexture";

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 updateDemoLogo = (pngPath: string) => {
const bin = readFileSync("bin/GAME.BIN");
const pngData = readFileSync(pngPath);

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);
}

console.log("Encoded Logo: 0x%s", encodedLogo.length.toString(16));
console.log("Encoded Texture: 0x%s", encodedTexture.length.toString(16));

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;
}

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

const [bodyBitField, compressedBody] = compressNewTexture(
Buffer.alloc(0),
encodedTexture,
0,
);
const len = bodyBitField.length + compressedBody.length;
console.log("Segment 2: 0x%s", len.toString(16));

for (let i = 0x41830; i < 0x432f2; i++) {
bin[i] = 0;
}

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

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

if (ofs <= 0x43000) {
console.log("too short!!!");
} else if (len > 0x43800) {
console.log("too long");
} else {
console.log("yaya!!!");
}

console.log("End: 0x%s", ofs.toString(16));
bin.writeInt16LE(bodyBitField.length, 0x41824);

writeFileSync("out/GAME.BIN", bin);
};

export default updateDemoLogo;
export { updateDemoLogo };
Loading

0 comments on commit 83b2746

Please sign in to comment.