From f55c1a15fdba25f7ab51d7f9ae8a09f0717be9fc Mon Sep 17 00:00:00 2001 From: kion Date: Fri, 12 Jul 2024 16:23:15 +0900 Subject: [PATCH 1/4] add face and body texture --- index.ts | 34 +- png/body-1.png | Bin 0 -> 9374 bytes png/face-1.png | Bin 0 -> 4190 bytes src/Encode.ts | 1129 ------------------------------------------ src/EncodeModel.ts | 988 ++++++++++++++++++++++++++++++++++++ src/EncodeRom.ts | 8 + src/EncodeTexture.ts | 136 +++++ 7 files changed, 1139 insertions(+), 1156 deletions(-) create mode 100644 png/body-1.png create mode 100644 png/face-1.png delete mode 100644 src/Encode.ts create mode 100644 src/EncodeModel.ts create mode 100644 src/EncodeRom.ts create mode 100644 src/EncodeTexture.ts diff --git a/index.ts b/index.ts index 4f0c315..191e755 100644 --- a/index.ts +++ b/index.ts @@ -1,31 +1,9 @@ -import { encodeModel } from "./src/Encode"; +import { encodeModel } from "./src/EncodeModel"; +import { encodeTexture } from "./src/EncodeTexture"; +import { encodeRom } from "./src/EncodeRom"; -// Helmet + Normal Shoes -encodeModel( - "PL00P000.BIN", - // Body Encoding - "obj/02_BODY.obj", - "obj/03_HIPS.obj", - "obj/10_LEG_RIGHT_TOP.obj", - "obj/11_LEG_RIGHT_BOTTOM.obj", - "obj/13_LEG_LEFT_TOP.obj", - "obj/14_LEG_LEFT_BOTTOM.obj", - // Feet - "obj/12_RIGHT_FOOT.obj", - "obj/15_LEFT_FOOT.obj", - // Left Arm - "obj/07_LEFT_SHOULDER.obj", - "obj/08_LEFT_ARM.obj", - "obj/09_LEFT_HAND.obj", - // Right Arm - "obj/04_RIGHT_SHOULDER.obj", - "obj/05_RIGHT_ARM.obj", - "obj/06_RIGHT_HAND.obj", - // Head - "obj/01_HEAD_HAIR.obj", - "obj/01_HEAD_FACE.obj", - "obj/01_HEAD_MOUTH.obj", -); +// Encode the Texture +encodeTexture("png/body-1.png", "png/face-1.png"); // No helmet + Normal Shoes encodeModel( @@ -53,3 +31,5 @@ encodeModel( "obj/01_HEAD_FACE.obj", "obj/01_HEAD_MOUTH.obj", ); + +encodeRom(); diff --git a/png/body-1.png b/png/body-1.png new file mode 100644 index 0000000000000000000000000000000000000000..64e3dab6141a8cc0932ca18a2b292a220f0c1b97 GIT binary patch literal 9374 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 Voa?V9_XS6Q!YftT3Te}z{{v4$(aQh; literal 0 HcmV?d00001 diff --git a/png/face-1.png b/png/face-1.png new file mode 100644 index 0000000000000000000000000000000000000000..520b086cafb1c38271323c758385a5b27b9736ce GIT binary patch literal 4190 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 { - const rClr = Math.floor((r >> 3) & 0xff); - const gClr = Math.floor((g >> 3) & 0xff); - const bClr = Math.floor((b >> 3) & 0xff); - const aClr = a === 0 ? 0 : 0x8000 - const texel = rClr | (gClr << 5) | (bClr << 5) | aClr; - return texel; -} - -// Function expects a png buffer for the image -const encodeImage = (pngSrc: Buffer) => { - - const pngInfo = PNG.sync.read(pngSrc); - const { width, height, data } = pngInfo; - - console.log('Encoding image'); - - if (width !== 256 || height !== 256) { - throw new Error("Encoder expects a 256x256 image"); - } - - console.log(data.length.toString(16)) - - let inOfs = 0; - let outOfs = 0; - const palette: number[] = [0] - const pal = Buffer.alloc(0x20, 0) - const img = Buffer.alloc(0x8000, 0) - - const readPixel = () => { - const a = data.readUInt8(inOfs + 3) === 0 ? 0 : 255; - const r = a === 0 ? 0 : data.readUInt8(inOfs + 0) - const g = a === 0 ? 0 : data.readUInt8(inOfs + 1) - const b = a === 0 ? 0 : data.readUInt8(inOfs + 2) - const texel = encodeTexel(r, g, b, a); - inOfs += 4 - - // Search through the existing palette - const index = palette.indexOf(texel); - - // If doesn't exist, we add it to the palette - if (index === -1) { - const pos = palette.length; - palette.push(texel) - return pos; - } - - return index; - } - - for (let y = 0; y < height; y++) { - for (let x = 0; x < width; x += 2) { - const lowByte = readPixel() - const highByte = readPixel() - const byte = ((highByte << 4) | lowByte) & 0xff - img[outOfs] = byte; - outOfs++; - } - } - - // for(let i = 0; i < palette.length; i++){ - // console.log(palette[i].toString(16)) - // } - - outOfs = 0 - console.log('Palette colors: ') - for (let i = 0; i < 16; i++) { - const texel = palette[i] || 0x0000; - console.log(texel.toString(16)) - pal.writeUInt16LE(texel, outOfs) - outOfs += 2 - } - - return [pal, img] -} - -const replaceTexture = ( - gamefile: Buffer, - bodyBuffer: Buffer, - faceBuffer: Buffer, -) => { - - const modded = Buffer.from(gamefile) - const [bodyPal, bodyImg] = encodeImage(bodyBuffer) - const [facePal, faceImg] = encodeImage(faceBuffer) - - let ofs = 0; - - // Paltte 0 - const bodyPalOfs = 0x30 - ofs = bodyPalOfs - for (let i = 0; i < bodyPal.length; i++) { - modded[bodyPalOfs + i] = bodyPal[i] - } - - // Image 0 - const bodyImgOfs = 0x800 - for (let i = 0; i < 0x8000; i++) { - modded[bodyImgOfs + i] = bodyImg[i]; - } - - // palette 1 - // Black for the second texture - ofs = 0x8830 - for (let pal = 0; pal < 3; pal++) { - for (let i = 0; i < 16; i++) { - modded.writeUInt16LE(0x8000, ofs) - ofs += 2; - } - } - - // Pallete 2 - const facePalOfs = 0x9030 - for (let i = 0; i < facePal.length; i++) { - modded[facePalOfs + i] = facePal[i] - } - - // Image 1 - const faceImgOfs = 0x9800 - for (let i = 0; i < 0x4000; i++) { - modded[faceImgOfs + i] = faceImg[i] - } - - return modded; -} -const encodeVertex = (x: number, y: number, z: number) => { - - try { - const xInt = encodeVertexBits(x) - const yInt = encodeVertexBits(y) - const zInt = encodeVertexBits(z) - // Shift and merge vertex to make a 32 bit value - const vertex = xInt | (yInt << 10) | (zInt << 20) - return vertex - } catch (err) { - console.log("0 Scale invalid: ", x, y, z) - } - - try { - const xInt = encodeVertexBits(Math.floor(x / 2)) - const yInt = encodeVertexBits(Math.floor(y / 2)) - const zInt = encodeVertexBits(Math.floor(z / 2)) - // Shift and merge vertex to make a 32 bit value - const vertex = xInt | (yInt << 10) | (zInt << 20) | (1 << 30) - return vertex - } catch (err) { - console.log("1 Scale invalid: ", x, y, z) - throw err; - } - - -} - -// Encode the Vertices -const encodeVertexBits = (num: number) => { - - if (num < 0) { - const lowBits = 512 + num; - const encodedVert = 0x200 | lowBits; - if (encodedVert > 0x3ff) { - return 0x3ff; - throw new Error("Encoded vertex is too larged (neg)") - } - return encodedVert - } else { - if (num > 0x1ff) { - return 0x1ff; - throw new Error("Encoded vertex is too larged (pos)") - } - return num; - } -} - -const encodeDirect = (verts: number[], tris: RawFace[], quads: RawFace[]): Primitive => { - - const SCALE = 1 / 0.00125; - const ROT_X = new Matrix4(); - ROT_X.makeRotationX(Math.PI); - - // First step is to break the file down into primitives - - - const vertices = Buffer.alloc(verts.length * 4, 0) - let vertOfs = 0; - console.log(verts) - for (let i = 0; i < verts.length; i++) { - // Extract string values for x,y,z - vertices.writeUInt32LE(verts[i]) - vertOfs += 4 - } - - - // Encode the triangles for each of the faces - const FACE_MASK = 0x7f; - const tri = Buffer.alloc(tris.length * 12, 0); - let triOfs = 0; - console.log(tris) - for (let i = 0; i < tris.length; i++) { - const { au, av, bu, bv, cu, cv, dword } = tris[i]; - - tri.writeUInt8(au, triOfs) - triOfs++; - tri.writeUInt8(av, triOfs) - triOfs++; - - tri.writeUInt8(bu, triOfs) - triOfs++; - tri.writeUInt8(bv, triOfs) - triOfs++; - - tri.writeUInt8(cu, triOfs) - triOfs++; - tri.writeUInt8(cv, triOfs) - triOfs++; - - tri.writeUInt8(0, triOfs) - triOfs++; - tri.writeUInt8(0, triOfs) - triOfs++; - - // Encode the face indices to a dword - tri.writeUInt32LE(dword, triOfs) - triOfs += 4 - } - - console.log(tri); - - const quad = Buffer.alloc(quads.length * 12, 0); - let quadOfs = 0; - for (let i = 0; i < quads.length; i++) { - const { au, av, bu, bv, cu, cv, du, dv, dword } = quads[i]; - - quad.writeUInt8(au, quadOfs) - quadOfs++; - quad.writeUInt8(av, quadOfs) - quadOfs++; - - quad.writeUInt8(bu, quadOfs) - quadOfs++; - quad.writeUInt8(bv, quadOfs) - quadOfs++; - - quad.writeUInt8(cu, quadOfs) - quadOfs++; - quad.writeUInt8(cv, quadOfs) - quadOfs++; - - quad.writeUInt8(du, quadOfs) - quadOfs++; - quad.writeUInt8(dv, quadOfs) - quadOfs++; - - // Encode the face indices to a dword - quad.writeUInt32LE(dword, quadOfs) - quadOfs += 4 - } - - return { tri, quad, vertices } -} - -const encodeMesh = (obj: string, faceMat: boolean = false): Primitive => { - - const SCALE = 1 / 0.00125; - const ROT_X = new Matrix4(); - ROT_X.makeRotationX(Math.PI); - - // First step is to break the file down into primitives - const lines = obj.split('\n'); - const verts: string[] = []; - const uvs: string[] = []; - const tris: string[] = []; - const quads: string[] = [] - - lines.forEach(line => { - if (line.indexOf('v ') === 0) { - verts.push(line) - } - - if (line.indexOf('vt ') === 0) { - uvs.push(line) - } - - if (line.indexOf('f ') === 0) { - const parts = line.split(' ') - let edge = 0; - parts.forEach(p => { - edge += p.indexOf('/') !== -1 ? 1 : 0 - }) - switch (edge) { - case 3: - tris.push(line) - break; - case 4: - quads.push(line) - break; - default: - throw new Error("Wait, what the fuck? " + line) - break; - } - - } - }) - - const vertices = Buffer.alloc(verts.length * 4, 0) - let vertOfs = 0; - for (let i = 0; i < verts.length; i++) { - // Extract string values for x,y,z - const v = verts[i].split(' '); - const xRaw = parseFloat(v[1]) - const yRaw = parseFloat(v[2]) - const zRaw = parseFloat(v[3]) - - // // Scale and rotate to match psx orientation - const vec3 = new Vector3(xRaw, yRaw, zRaw); - vec3.multiplyScalar(SCALE); - vec3.applyMatrix4(ROT_X); - // vec3.applyMatrix4(ROT_Y); - - // // Round each value to nearest whole int - vec3.x = Math.round(vec3.x) - vec3.y = Math.round(vec3.y) - vec3.z = Math.round(vec3.z) - - // // Encode x,y,z to signed 10 but values - const { x, y, z } = vec3; - - // Shift and merge vertex to make a 32 bit value - const vertex = encodeVertex(x, y, z) - vertices.writeUInt32LE(vertex, vertOfs) - vertOfs += 4 - } - - const PIXEL_TO_FLOAT_RATIO = 0.00390625; - const PIXEL_ADJUSTMEST = 0.001953125; - const pixels: [number, number][] = []; - - for (let i = 0; i < uvs.length; i++) { - // Parse the information from the string - const uv = uvs[i].split(' '); - const uRaw = parseFloat(uv[1]) - // Flip V - const vRaw = 1 - parseFloat(uv[2]) - - // // Approximate the pixel - const uAdjusted = (uRaw / PIXEL_TO_FLOAT_RATIO) - PIXEL_ADJUSTMEST - const vAdjusted = (vRaw / PIXEL_TO_FLOAT_RATIO) - PIXEL_ADJUSTMEST - - // // Eniminate rounding to make sure it's a pixel reference - const uFloor = Math.floor(uAdjusted) - const vFloor = Math.floor(vAdjusted) - - // // Make sure it fits in one byte - const u = uFloor > 255 ? 255 : uFloor < 0 ? 0 : uFloor; - const v = vFloor > 255 ? 255 : vFloor < 0 ? 0 : vFloor; - - // Push the pixels to be referenced - pixels.push([u, v]) - } - - // Encode the triangles for each of the faces - const FACE_MASK = 0x7f; - const tri = Buffer.alloc(tris.length * 12, 0); - let triOfs = 0; - for (let i = 0; i < tris.length; i++) { - const f = tris[i].split(' '); - - const [aStr, aIdx] = f[2].split('/') - const [bStr, bIdx] = f[1].split('/') - const [cStr, cIdx] = f[3].split('/') - - // Obj Indices start at 1 not 0 - const a = parseInt(aStr) - 1 - const b = parseInt(bStr) - 1 - const c = parseInt(cStr) - 1 - - // Same, Obj Indices start at 1 not 0 - const [au, av] = pixels[parseInt(aIdx) - 1]; - const [bu, bv] = pixels[parseInt(bIdx) - 1]; - const [cu, cv] = pixels[parseInt(cIdx) - 1]; - - tri.writeUInt8(au, triOfs) - triOfs++; - tri.writeUInt8(av, triOfs) - triOfs++; - - tri.writeUInt8(bu, triOfs) - triOfs++; - tri.writeUInt8(bv, triOfs) - triOfs++; - - tri.writeUInt8(cu, triOfs) - triOfs++; - tri.writeUInt8(cv, triOfs) - triOfs++; - - tri.writeUInt8(0, triOfs) - triOfs++; - tri.writeUInt8(0, triOfs) - triOfs++; - - // Encode the face indices to a dword - const indexA = a & FACE_MASK - const indexB = b & FACE_MASK - const indexC = c & FACE_MASK - const indexD = 0 - - const materialIndex = faceMat ? 2 : 0; - - // Material Index 0 = Img 0 - Palette 0 - // Material Index 1 = Img 0 - Palette 1 - - const dword = indexA | (indexB << 7) | (indexC << 14) | (indexD << 21) | materialIndex << 28 - tri.writeUInt32LE(dword, triOfs) - triOfs += 4 - } - - console.log(tri); - - const quad = Buffer.alloc(quads.length * 12, 0); - let quadOfs = 0; - for (let i = 0; i < quads.length; i++) { - const f = quads[i].split(' '); - - const [aStr, aIdx] = f[1].split('/') - const [bStr, bIdx] = f[4].split('/') - const [cStr, cIdx] = f[2].split('/') - const [dStr, dIdx] = f[3].split('/') - - // Obj Indices start at 1 not 0 - const a = parseInt(aStr) - 1 - const b = parseInt(bStr) - 1 - const c = parseInt(cStr) - 1 - const d = parseInt(dStr) - 1 - - // Same, Obj Indices start at 1 not 0 - const [au, av] = pixels[parseInt(aIdx) - 1]; - const [bu, bv] = pixels[parseInt(bIdx) - 1]; - const [cu, cv] = pixels[parseInt(cIdx) - 1]; - const [du, dv] = pixels[parseInt(dIdx) - 1]; - - quad.writeUInt8(au, quadOfs) - quadOfs++; - quad.writeUInt8(av, quadOfs) - quadOfs++; - - quad.writeUInt8(bu, quadOfs) - quadOfs++; - quad.writeUInt8(bv, quadOfs) - quadOfs++; - - quad.writeUInt8(cu, quadOfs) - quadOfs++; - quad.writeUInt8(cv, quadOfs) - quadOfs++; - - quad.writeUInt8(du, quadOfs) - quadOfs++; - quad.writeUInt8(dv, quadOfs) - quadOfs++; - - // Encode the face indices to a dword - const indexA = a & FACE_MASK - const indexB = b & FACE_MASK - const indexC = c & FACE_MASK - const indexD = d & FACE_MASK - - const materialIndex = 0; - - // Material Index 0 = Img 0 - Palette 0 - // Material Index 1 = Img 0 - Palette 1 - - const dword = indexA | (indexB << 7) | (indexC << 14) | (indexD << 21) | materialIndex << 28 - quad.writeUInt32LE(dword, quadOfs) - quadOfs += 4 - } - - return { tri, quad, vertices } -} - -const encodeModelBody = ( - bodyObj: string, // filename - hipsObj: string, //filename, - rLegTopObject: string, //filename - rLegBtmObject: string, // filename - lLegTopObject: string, // filename - lLegBtmObject: string // filename -) => { - - const BODY_START = 0x80; - const BODY_END = 0xe80 - const BODY_LEN = BODY_END - BODY_START; - - const prims: Primitive[] = [] - const limbs = [ - readFileSync(bodyObj, 'ascii'), - readFileSync(hipsObj, 'ascii'), - readFileSync(rLegTopObject, 'ascii'), - readFileSync(rLegBtmObject, 'ascii'), - readFileSync(lLegTopObject, 'ascii'), - readFileSync(lLegBtmObject, 'ascii') - ] - - const START_OFS = 0x110; - let shadowPtr = START_OFS - for (let i = 0; i < limbs.length; i++) { - const prim = encodeMesh(limbs[i]); - const { tri, quad, vertices } = prim; - prims.push(prim); - shadowPtr += tri.length; - shadowPtr += quad.length; - shadowPtr += vertices.length; - } - - - const mesh = Buffer.alloc(BODY_LEN, 0x80); - // Need to zero out the header - for (let i = 0; i < START_OFS; i++) { - mesh[i] = 0; - } - let headerOfs = 0; - let contentOfs = START_OFS - BODY_START; - prims.forEach(prim => { - const { tri, quad, vertices } = prim; - const triCount = tri.length / 12; - const quadCount = quad.length / 12; - const vertCount = vertices.length / 4; - - console.log("Vert Count: ", vertCount) - - // Write the header for each primitive - mesh.writeUInt8(triCount, headerOfs + 0) // tris - mesh.writeUInt8(quadCount, headerOfs + 1) // quads - mesh.writeUInt8(vertCount, headerOfs + 2) // verts - mesh.writeUInt8(0, headerOfs + 3) // nop - headerOfs += 4; - - // Triangle Definition Offset - mesh.writeUInt32LE(contentOfs + BODY_START, headerOfs) - headerOfs += 4; - for (let i = 0; i < tri.length; i++) { - mesh[contentOfs++] = tri[i] - } - - // Quad Definition Offset - mesh.writeUInt32LE(contentOfs + BODY_START, headerOfs) - headerOfs += 4; - for (let i = 0; i < quad.length; i++) { - mesh[contentOfs++] = quad[i] - } - - // Vertex Definition Offset - mesh.writeUInt32LE(contentOfs + BODY_START, headerOfs) - headerOfs += 4; - for (let i = 0; i < vertices.length; i++) { - mesh[contentOfs++] = vertices[i] - } - - // Triangle Shadow Offset - mesh.writeUInt32LE(shadowPtr, headerOfs) - headerOfs += 4; - - // Quad Shadow Offset - mesh.writeUInt32LE(shadowPtr, headerOfs) - headerOfs += 4; - }) - - console.log("End Offset: 0x%s", contentOfs.toString(16)) - console.log("Length: 0x%s", BODY_LEN.toString(16)) - - return mesh; -} - - - -const encodeModelFeet = ( - rightFoot: string, // filename - leftFoot: string, //filename, -) => { - - const MESH_START = 0x1a80; - const MESH_END = 0x1f00 - const MESH_LEN = MESH_END - MESH_START; - - const prims: Primitive[] = [] - const limbs = [ - readFileSync(rightFoot, 'ascii'), - readFileSync(leftFoot, 'ascii'), - ] - - const START_OFS = 0x1ab0; - let shadowPtr = START_OFS - for (let i = 0; i < limbs.length; i++) { - const prim = encodeMesh(limbs[i]); - const { tri, quad, vertices } = prim; - prims.push(prim); - shadowPtr += tri.length; - shadowPtr += quad.length; - shadowPtr += vertices.length; - } - - - const mesh = Buffer.alloc(MESH_LEN, 0x80); - // Need to zero out the header - for (let i = 0; i < START_OFS; i++) { - mesh[i] = 0; - } - let headerOfs = 0; - let contentOfs = START_OFS - MESH_START; - prims.forEach(prim => { - const { tri, quad, vertices } = prim; - const triCount = tri.length / 12; - const quadCount = quad.length / 12; - const vertCount = vertices.length / 4; - - console.log("Vert Count: ", vertCount) - - // Write the header for each primitive - mesh.writeUInt8(triCount, headerOfs + 0) // tris - mesh.writeUInt8(quadCount, headerOfs + 1) // quads - mesh.writeUInt8(vertCount, headerOfs + 2) // verts - mesh.writeUInt8(0, headerOfs + 3) // nop - headerOfs += 4; - - // Triangle Definition Offset - mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs) - headerOfs += 4; - for (let i = 0; i < tri.length; i++) { - mesh[contentOfs++] = tri[i] - } - - // Quad Definition Offset - mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs) - headerOfs += 4; - for (let i = 0; i < quad.length; i++) { - mesh[contentOfs++] = quad[i] - } - - // Vertex Definition Offset - mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs) - headerOfs += 4; - for (let i = 0; i < vertices.length; i++) { - mesh[contentOfs++] = vertices[i] - } - - // Triangle Shadow Offset - mesh.writeUInt32LE(shadowPtr, headerOfs) - headerOfs += 4; - - // Quad Shadow Offset - mesh.writeUInt32LE(shadowPtr, headerOfs) - headerOfs += 4; - }) - - console.log("End Offset: 0x%s", contentOfs.toString(16)) - console.log("Length: 0x%s", MESH_LEN.toString(16)) - - return mesh; -} - -const encodeModelLeftArm = ( - m0: string, - m1: string, - m2: string -) => { - - const MESH_START = 0x1f00; - const MESH_END = 0x2c00 - const MESH_LEN = MESH_END - MESH_START; - - const prims: Primitive[] = [] - const limbs = [ - readFileSync(m0, 'ascii'), - readFileSync(m1, 'ascii'), - readFileSync(m2, 'ascii'), - ] - - - const START_OFS = 0x1f48; - let shadowPtr = START_OFS - for (let i = 0; i < limbs.length; i++) { - const prim = encodeMesh(limbs[i]); - const { tri, quad, vertices } = prim; - prims.push(prim); - shadowPtr += tri.length; - shadowPtr += quad.length; - shadowPtr += vertices.length; - } - - - const mesh = Buffer.alloc(MESH_LEN, 0x80); - // Need to zero out the header - for (let i = 0; i < START_OFS; i++) { - mesh[i] = 0; - } - let headerOfs = 0; - let contentOfs = START_OFS - MESH_START; - prims.forEach(prim => { - const { tri, quad, vertices } = prim; - const triCount = tri.length / 12; - const quadCount = quad.length / 12; - const vertCount = vertices.length / 4; - - console.log("Vert Count: ", vertCount) - - // Write the header for each primitive - mesh.writeUInt8(triCount, headerOfs + 0) // tris - mesh.writeUInt8(quadCount, headerOfs + 1) // quads - mesh.writeUInt8(vertCount, headerOfs + 2) // verts - mesh.writeUInt8(0, headerOfs + 3) // nop - headerOfs += 4; - - // Triangle Definition Offset - mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs) - headerOfs += 4; - for (let i = 0; i < tri.length; i++) { - mesh[contentOfs++] = tri[i] - } - - // Quad Definition Offset - mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs) - headerOfs += 4; - for (let i = 0; i < quad.length; i++) { - mesh[contentOfs++] = quad[i] - } - - // Vertex Definition Offset - mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs) - headerOfs += 4; - for (let i = 0; i < vertices.length; i++) { - mesh[contentOfs++] = vertices[i] - } - - // Triangle Shadow Offset - mesh.writeUInt32LE(0x8a8, headerOfs) - headerOfs += 4; - - // Quad Shadow Offset - mesh.writeUInt32LE(0x8a8, headerOfs) - headerOfs += 4; - }) - - console.log("End Offset: 0x%s", contentOfs.toString(16)) - console.log("Length: 0x%s", MESH_LEN.toString(16)) - - return mesh; -} - -const encodeModelRightArm = ( - m0: string, - m1: string, - m2: string -) => { - - const MESH_START = 0x2c00; - const MESH_END = 0x3200 - const MESH_LEN = MESH_END - MESH_START; - - const prims: Primitive[] = [] - const limbs = [ - readFileSync(m0, 'ascii'), - readFileSync(m1, 'ascii'), - readFileSync(m2, 'ascii'), - ] - - - const START_OFS = 0x1f48; - let shadowPtr = START_OFS - for (let i = 0; i < limbs.length; i++) { - const prim = encodeMesh(limbs[i]); - const { tri, quad, vertices } = prim; - prims.push(prim); - shadowPtr += tri.length; - shadowPtr += quad.length; - shadowPtr += vertices.length; - } - - - const mesh = Buffer.alloc(MESH_LEN, 0x80); - // Need to zero out the header - for (let i = 0; i < START_OFS; i++) { - mesh[i] = 0; - } - let headerOfs = 0; - let contentOfs = START_OFS - MESH_START; - prims.forEach(prim => { - const { tri, quad, vertices } = prim; - const triCount = tri.length / 12; - const quadCount = quad.length / 12; - const vertCount = vertices.length / 4; - - console.log("Vert Count: ", vertCount) - - // Write the header for each primitive - mesh.writeUInt8(triCount, headerOfs + 0) // tris - mesh.writeUInt8(quadCount, headerOfs + 1) // quads - mesh.writeUInt8(vertCount, headerOfs + 2) // verts - mesh.writeUInt8(0, headerOfs + 3) // nop - headerOfs += 4; - - // Triangle Definition Offset - mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs) - headerOfs += 4; - for (let i = 0; i < tri.length; i++) { - mesh[contentOfs++] = tri[i] - } - - // Quad Definition Offset - mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs) - headerOfs += 4; - for (let i = 0; i < quad.length; i++) { - mesh[contentOfs++] = quad[i] - } - - // Vertex Definition Offset - mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs) - headerOfs += 4; - for (let i = 0; i < vertices.length; i++) { - mesh[contentOfs++] = vertices[i] - } - - // Triangle Shadow Offset - mesh.writeUInt32LE(0x8a8, headerOfs) - headerOfs += 4; - - // Quad Shadow Offset - mesh.writeUInt32LE(0x8a8, headerOfs) - headerOfs += 4; - }) - - console.log("End Offset: 0x%s", contentOfs.toString(16)) - console.log("Length: 0x%s", MESH_LEN.toString(16)) - - return mesh; -} - -const encodeModelHead = ( - m0: string, // hair - m1: string, // face - m2: string // mouth -) => { - - const MESH_START = 0xe80; - const MESH_END = 0x1a80 - const MESH_LEN = MESH_END - MESH_START; - - const prims: Primitive[] = [] - const limbs = [ - readFileSync(m0, 'ascii'), - readFileSync(m1, 'ascii'), - readFileSync(m2, 'ascii'), - ] - - console.log(limbs[2]); - - - const START_OFS = 0xec8; - - // Encode head - const hd = encodeMesh(limbs[0], false); - prims.push(hd); - - prims.push( - { - vertices: Buffer.from('5f2cde3ba12fde3bb06faf3b00402f39b5a32f3b4ba02f3b007c1f380598bf38fb9bbf38001cfe38a6cfce3b5accce3b506caf3b', 'hex'), - tri: Buffer.from('01101f1b051f00008a8100e03a2b3d363a360000838301a0072f002f03260000078401a03336382b39360000888101a0201b3f103b1f0000830503e0', 'hex'), - quad: Buffer.from('2222201b3a223b1f874181e11e2206221f1b051f08c240e01f1b01101f000002034522a0201b20003f103f0283c402a0', 'hex'), - }) - prims.push( - { - vertices: Buffer.from('0060003bb5a32f3b4ba02f3b2d14103bd317103b00ec4f390598bf38fb9bbf38', 'hex'), - tri: Buffer.from('202b2f2e20360000850100a0102e1f2b1f360000840200a022231f2b1d23000086c201a0', 'hex'), - quad: Buffer.from('06231d23102e1f2b8103a1a03a232f2e2223202b8281a1a0', 'hex'), - } - ) - - - - const mesh = Buffer.alloc(MESH_LEN, 0x80); - // Need to zero out the header - for (let i = 0; i < START_OFS; i++) { - mesh[i] = 0; - } - let headerOfs = 0; - let contentOfs = START_OFS - MESH_START; - prims.forEach(prim => { - const { tri, quad, vertices } = prim; - const triCount = tri.length / 12; - const quadCount = quad.length / 12; - const vertCount = vertices.length / 4; - - console.log("Vert Count: ", vertCount) - - // Write the header for each primitive - mesh.writeUInt8(triCount, headerOfs + 0) // tris - mesh.writeUInt8(quadCount, headerOfs + 1) // quads - mesh.writeUInt8(vertCount, headerOfs + 2) // verts - mesh.writeUInt8(0, headerOfs + 3) // nop - headerOfs += 4; - - // Triangle Definition Offset - mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs) - headerOfs += 4; - for (let i = 0; i < tri.length; i++) { - mesh[contentOfs++] = tri[i] - } - - // Quad Definition Offset - mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs) - headerOfs += 4; - for (let i = 0; i < quad.length; i++) { - mesh[contentOfs++] = quad[i] - } - - // Vertex Definition Offset - mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs) - headerOfs += 4; - for (let i = 0; i < vertices.length; i++) { - mesh[contentOfs++] = vertices[i] - } - - // Triangle Shadow Offset - mesh.writeUInt32LE(0x8a8, headerOfs) - headerOfs += 4; - - // Quad Shadow Offset - mesh.writeUInt32LE(0x8a8, headerOfs) - headerOfs += 4; - }) - - - writeFileSync('head_debug.bin', mesh) - console.log("--- Head ----"); - console.log("End Offset: 0x%s", contentOfs.toString(16)) - console.log("Length: 0x%s", MESH_LEN.toString(16)) - - return mesh; -} - -// We should be modifying the instance of the ROM -const replaceInRom = (file: Buffer, filepos: number, ROM: Buffer) => { - const len = Math.ceil(file.length / 0x800); - const segments: Buffer[] = []; - - // First we split the file into segments - for (let i = 0; i < len; i++) { - const start = i * 0x800; - const end = (i + 1) * 0x800 - const stop = file.length < end ? file.length : end; - const segment = file.subarray(start, stop); - segments.push(segment); - } - - // Then copy it over to the ROM - let ofs = filepos; - segments.forEach(segment => { - for (let i = 0; i < 0x800; i++) { - ROM[ofs + i] = segment[i] - } - ofs += 0x930; - }) -} - -const replaceModel = ( - gamefile: Buffer, - bodyBuffer: Buffer, - feetBuffer: Buffer, - leftBuffer: Buffer, - rightBuffer: Buffer, - headBuffer: Buffer, -) => { - - const modded = Buffer.from(gamefile) - const read = gamefile.subarray(0x30) - - let seek = 0xe80; - for (let i = 0; i < 3; i++) { - const triCount = read.readUInt8(seek + 0); - const quadCount = read.readUInt8(seek + 1); - const vertCount = read.readUInt8(seek + 2); - - console.log("%s %s %s", triCount.toString(16), quadCount.toString(16), vertCount.toString(16)) - const triOfs = read.readUInt32LE(seek + 4); - console.log("Tris ofs: 0x%s", triOfs.toString(16), i); - - const quadOfs = read.readUInt32LE(seek + 8); - const vertOfs = read.readUInt32LE(seek + 12); - seek += 0x18; - - if (i === 0) { - continue; - } - - console.log("Reading face: ", i) - const vertices = read.subarray(vertOfs, vertOfs + (vertCount * 4)) - const tri = read.subarray(triOfs, triOfs + (triCount * 12)) - const quad = read.subarray(quadOfs, quadOfs + (quadCount * 12)) - - const prim: Primitive = { - vertices, - tri, - quad - } - - console.log(`{ - vertices: Buffer.from('${vertices.toString('hex')}', 'hex'), - tri: Buffer.from('${tri.toString('hex')}', 'hex'), - quad: Buffer.from('${quad.toString('hex')}', 'hex'), -}`) - - } - - - // Zero out the entire model - for (let i = 0xb0; i < 0x31F0; i++) { - modded[i] = 0; - } - - const BIN_HEADER_SIZE = 0x30; - - - // Replace with our updated model - const bodyOfs = 0x0080 + BIN_HEADER_SIZE - for (let i = 0; i < bodyBuffer.length; i++) { - modded[bodyOfs + i] = bodyBuffer[i] - } - - const headOfs = 0xe80 + BIN_HEADER_SIZE - for (let i = 0; i < headBuffer.length; i++) { - modded[headOfs + i] = headBuffer[i] - } - - const feetOfs = 0x1a80 + BIN_HEADER_SIZE - for (let i = 0; i < feetBuffer.length; i++) { - modded[feetOfs + i] = feetBuffer[i] - } - - const leftOfs = 0x1f00 + BIN_HEADER_SIZE - for (let i = 0; i < leftBuffer.length; i++) { - modded[leftOfs + i] = leftBuffer[i] - } - - const rightOfs = 0x2c00 + BIN_HEADER_SIZE - for (let i = 0; i < leftBuffer.length; i++) { - modded[rightOfs + i] = rightBuffer[i] - } - - return modded; -} - -const encodeModel = ( - filename: string, - // Body Section - bodyObject: string, - hipsObject: string, - rLegTopObject: string, - rLegBtmObject: string, - lLegTopObject: string, - lLegBtmObject: string, - // Feet - rightFootObject: string, - leftFootObject: string, - // Left Arm - leftShoulder: string, - leftArm: string, - leftHand: string, - // Right Arm - rightShoulder: string, - rightArm: string, - rightHand: string, - // Head - hairObject: string, - eyesObject: string, - mouthObject: string -) => { - - // Encode the body - const srcModel = readFileSync(`bin/${filename}`) - const body = encodeModelBody(bodyObject, hipsObject, rLegTopObject, rLegBtmObject, lLegTopObject, lLegBtmObject); - const feet = encodeModelFeet(rightFootObject, leftFootObject); - const lArm = encodeModelLeftArm(leftShoulder, leftArm, leftHand); - const rArm = encodeModelRightArm(rightShoulder, rightArm, rightHand); - const head = encodeModelHead(hairObject, eyesObject, mouthObject); - const updatedModel = replaceModel(srcModel, body, feet, lArm, rArm, head); - writeFileSync(`mod/${filename}`, updatedModel.subarray(0x30, 0x1f00 + 0x30)) -} - -const encodeTexture = ( - bodyTexture: string, - faceTexture: string, -) => { - - // Encode the body and face texture to write to ROM - const srcTexture = readFileSync('bin/PL01T.BIN') - const bodyBuffer = readFileSync(bodyTexture); - const faceBuffer = readFileSync(faceTexture); - const modTexture = replaceTexture(srcTexture, bodyBuffer, faceBuffer) - writeFileSync('mod/PL01T.BIN', modTexture) -} - - - -export { encodeModel, encodeTexture } \ No newline at end of file diff --git a/src/EncodeModel.ts b/src/EncodeModel.ts new file mode 100644 index 0000000..85cd5d9 --- /dev/null +++ b/src/EncodeModel.ts @@ -0,0 +1,988 @@ +import { readFileSync, writeFileSync } from "fs"; +import { Vector3, Matrix4 } from "three"; + +type Primitive = { + tri: Buffer; + quad: Buffer; + vertices: Buffer; +}; + +type RawFace = { + au: number; + av: number; + bu: number; + bv: number; + cu: number; + cv: number; + du: number; + dv: number; + isQuad: boolean; + dword: number; +}; + +// Function expects a png buffer for the image + +const encodeVertex = (x: number, y: number, z: number) => { + try { + const xInt = encodeVertexBits(x); + const yInt = encodeVertexBits(y); + const zInt = encodeVertexBits(z); + // Shift and merge vertex to make a 32 bit value + const vertex = xInt | (yInt << 10) | (zInt << 20); + return vertex; + } catch (err) { + console.log("0 Scale invalid: ", x, y, z); + } + + try { + const xInt = encodeVertexBits(Math.floor(x / 2)); + const yInt = encodeVertexBits(Math.floor(y / 2)); + const zInt = encodeVertexBits(Math.floor(z / 2)); + // Shift and merge vertex to make a 32 bit value + const vertex = xInt | (yInt << 10) | (zInt << 20) | (1 << 30); + return vertex; + } catch (err) { + console.log("1 Scale invalid: ", x, y, z); + throw err; + } +}; + +// Encode the Vertices +const encodeVertexBits = (num: number) => { + if (num < 0) { + const lowBits = 512 + num; + const encodedVert = 0x200 | lowBits; + if (encodedVert > 0x3ff) { + return 0x3ff; + throw new Error("Encoded vertex is too larged (neg)"); + } + return encodedVert; + } else { + if (num > 0x1ff) { + return 0x1ff; + throw new Error("Encoded vertex is too larged (pos)"); + } + return num; + } +}; + +const encodeDirect = ( + verts: number[], + tris: RawFace[], + quads: RawFace[], +): Primitive => { + const SCALE = 1 / 0.00125; + const ROT_X = new Matrix4(); + ROT_X.makeRotationX(Math.PI); + + // First step is to break the file down into primitives + + const vertices = Buffer.alloc(verts.length * 4, 0); + let vertOfs = 0; + console.log(verts); + for (let i = 0; i < verts.length; i++) { + // Extract string values for x,y,z + vertices.writeUInt32LE(verts[i]); + vertOfs += 4; + } + + // Encode the triangles for each of the faces + const FACE_MASK = 0x7f; + const tri = Buffer.alloc(tris.length * 12, 0); + let triOfs = 0; + console.log(tris); + for (let i = 0; i < tris.length; i++) { + const { au, av, bu, bv, cu, cv, dword } = tris[i]; + + tri.writeUInt8(au, triOfs); + triOfs++; + tri.writeUInt8(av, triOfs); + triOfs++; + + tri.writeUInt8(bu, triOfs); + triOfs++; + tri.writeUInt8(bv, triOfs); + triOfs++; + + tri.writeUInt8(cu, triOfs); + triOfs++; + tri.writeUInt8(cv, triOfs); + triOfs++; + + tri.writeUInt8(0, triOfs); + triOfs++; + tri.writeUInt8(0, triOfs); + triOfs++; + + // Encode the face indices to a dword + tri.writeUInt32LE(dword, triOfs); + triOfs += 4; + } + + console.log(tri); + + const quad = Buffer.alloc(quads.length * 12, 0); + let quadOfs = 0; + for (let i = 0; i < quads.length; i++) { + const { au, av, bu, bv, cu, cv, du, dv, dword } = quads[i]; + + quad.writeUInt8(au, quadOfs); + quadOfs++; + quad.writeUInt8(av, quadOfs); + quadOfs++; + + quad.writeUInt8(bu, quadOfs); + quadOfs++; + quad.writeUInt8(bv, quadOfs); + quadOfs++; + + quad.writeUInt8(cu, quadOfs); + quadOfs++; + quad.writeUInt8(cv, quadOfs); + quadOfs++; + + quad.writeUInt8(du, quadOfs); + quadOfs++; + quad.writeUInt8(dv, quadOfs); + quadOfs++; + + // Encode the face indices to a dword + quad.writeUInt32LE(dword, quadOfs); + quadOfs += 4; + } + + return { tri, quad, vertices }; +}; + +const encodeMesh = (obj: string, faceMat: boolean = false): Primitive => { + const SCALE = 1 / 0.00125; + const ROT_X = new Matrix4(); + ROT_X.makeRotationX(Math.PI); + + // First step is to break the file down into primitives + const lines = obj.split("\n"); + const verts: string[] = []; + const uvs: string[] = []; + const tris: string[] = []; + const quads: string[] = []; + + lines.forEach((line) => { + if (line.indexOf("v ") === 0) { + verts.push(line); + } + + if (line.indexOf("vt ") === 0) { + uvs.push(line); + } + + if (line.indexOf("f ") === 0) { + const parts = line.split(" "); + let edge = 0; + parts.forEach((p) => { + edge += p.indexOf("/") !== -1 ? 1 : 0; + }); + switch (edge) { + case 3: + tris.push(line); + break; + case 4: + quads.push(line); + break; + default: + throw new Error("Wait, what the fuck? " + line); + break; + } + } + }); + + const vertices = Buffer.alloc(verts.length * 4, 0); + let vertOfs = 0; + for (let i = 0; i < verts.length; i++) { + // Extract string values for x,y,z + const v = verts[i].split(" "); + const xRaw = parseFloat(v[1]); + const yRaw = parseFloat(v[2]); + const zRaw = parseFloat(v[3]); + + // // Scale and rotate to match psx orientation + const vec3 = new Vector3(xRaw, yRaw, zRaw); + vec3.multiplyScalar(SCALE); + vec3.applyMatrix4(ROT_X); + // vec3.applyMatrix4(ROT_Y); + + // // Round each value to nearest whole int + vec3.x = Math.round(vec3.x); + vec3.y = Math.round(vec3.y); + vec3.z = Math.round(vec3.z); + + // // Encode x,y,z to signed 10 but values + const { x, y, z } = vec3; + + // Shift and merge vertex to make a 32 bit value + const vertex = encodeVertex(x, y, z); + vertices.writeUInt32LE(vertex, vertOfs); + vertOfs += 4; + } + + const PIXEL_TO_FLOAT_RATIO = 0.00390625; + const PIXEL_ADJUSTMEST = 0.001953125; + const pixels: [number, number][] = []; + + for (let i = 0; i < uvs.length; i++) { + // Parse the information from the string + const uv = uvs[i].split(" "); + const uRaw = parseFloat(uv[1]); + // Flip V + const vRaw = 1 - parseFloat(uv[2]); + + // // Approximate the pixel + const uAdjusted = uRaw / PIXEL_TO_FLOAT_RATIO - PIXEL_ADJUSTMEST; + const vAdjusted = vRaw / PIXEL_TO_FLOAT_RATIO - PIXEL_ADJUSTMEST; + + // // Eniminate rounding to make sure it's a pixel reference + const uFloor = Math.floor(uAdjusted); + const vFloor = Math.floor(vAdjusted); + + // // Make sure it fits in one byte + const u = uFloor > 255 ? 255 : uFloor < 0 ? 0 : uFloor; + const v = vFloor > 255 ? 255 : vFloor < 0 ? 0 : vFloor; + + // Push the pixels to be referenced + pixels.push([u, v]); + } + + // Encode the triangles for each of the faces + const FACE_MASK = 0x7f; + const tri = Buffer.alloc(tris.length * 12, 0); + let triOfs = 0; + for (let i = 0; i < tris.length; i++) { + const f = tris[i].split(" "); + + const [aStr, aIdx] = f[2].split("/"); + const [bStr, bIdx] = f[1].split("/"); + const [cStr, cIdx] = f[3].split("/"); + + // Obj Indices start at 1 not 0 + const a = parseInt(aStr) - 1; + const b = parseInt(bStr) - 1; + const c = parseInt(cStr) - 1; + + // Same, Obj Indices start at 1 not 0 + const [au, av] = pixels[parseInt(aIdx) - 1]; + const [bu, bv] = pixels[parseInt(bIdx) - 1]; + const [cu, cv] = pixels[parseInt(cIdx) - 1]; + + tri.writeUInt8(au, triOfs); + triOfs++; + tri.writeUInt8(av, triOfs); + triOfs++; + + tri.writeUInt8(bu, triOfs); + triOfs++; + tri.writeUInt8(bv, triOfs); + triOfs++; + + tri.writeUInt8(cu, triOfs); + triOfs++; + tri.writeUInt8(cv, triOfs); + triOfs++; + + tri.writeUInt8(0, triOfs); + triOfs++; + tri.writeUInt8(0, triOfs); + triOfs++; + + // Encode the face indices to a dword + const indexA = a & FACE_MASK; + const indexB = b & FACE_MASK; + const indexC = c & FACE_MASK; + const indexD = 0; + + const materialIndex = faceMat ? 2 : 0; + + // Material Index 0 = Img 0 - Palette 0 + // Material Index 1 = Img 0 - Palette 1 + + const dword = + indexA | + (indexB << 7) | + (indexC << 14) | + (indexD << 21) | + (materialIndex << 28); + tri.writeUInt32LE(dword, triOfs); + triOfs += 4; + } + + console.log(tri); + + const quad = Buffer.alloc(quads.length * 12, 0); + let quadOfs = 0; + for (let i = 0; i < quads.length; i++) { + const f = quads[i].split(" "); + + const [aStr, aIdx] = f[1].split("/"); + const [bStr, bIdx] = f[4].split("/"); + const [cStr, cIdx] = f[2].split("/"); + const [dStr, dIdx] = f[3].split("/"); + + // Obj Indices start at 1 not 0 + const a = parseInt(aStr) - 1; + const b = parseInt(bStr) - 1; + const c = parseInt(cStr) - 1; + const d = parseInt(dStr) - 1; + + // Same, Obj Indices start at 1 not 0 + const [au, av] = pixels[parseInt(aIdx) - 1]; + const [bu, bv] = pixels[parseInt(bIdx) - 1]; + const [cu, cv] = pixels[parseInt(cIdx) - 1]; + const [du, dv] = pixels[parseInt(dIdx) - 1]; + + quad.writeUInt8(au, quadOfs); + quadOfs++; + quad.writeUInt8(av, quadOfs); + quadOfs++; + + quad.writeUInt8(bu, quadOfs); + quadOfs++; + quad.writeUInt8(bv, quadOfs); + quadOfs++; + + quad.writeUInt8(cu, quadOfs); + quadOfs++; + quad.writeUInt8(cv, quadOfs); + quadOfs++; + + quad.writeUInt8(du, quadOfs); + quadOfs++; + quad.writeUInt8(dv, quadOfs); + quadOfs++; + + // Encode the face indices to a dword + const indexA = a & FACE_MASK; + const indexB = b & FACE_MASK; + const indexC = c & FACE_MASK; + const indexD = d & FACE_MASK; + + const materialIndex = 0; + + // Material Index 0 = Img 0 - Palette 0 + // Material Index 1 = Img 0 - Palette 1 + + const dword = + indexA | + (indexB << 7) | + (indexC << 14) | + (indexD << 21) | + (materialIndex << 28); + quad.writeUInt32LE(dword, quadOfs); + quadOfs += 4; + } + + return { tri, quad, vertices }; +}; + +const encodeModelBody = ( + bodyObj: string, // filename + hipsObj: string, //filename, + rLegTopObject: string, //filename + rLegBtmObject: string, // filename + lLegTopObject: string, // filename + lLegBtmObject: string, // filename +) => { + const BODY_START = 0x80; + const BODY_END = 0xe80; + const BODY_LEN = BODY_END - BODY_START; + + const prims: Primitive[] = []; + const limbs = [ + readFileSync(bodyObj, "ascii"), + readFileSync(hipsObj, "ascii"), + readFileSync(rLegTopObject, "ascii"), + readFileSync(rLegBtmObject, "ascii"), + readFileSync(lLegTopObject, "ascii"), + readFileSync(lLegBtmObject, "ascii"), + ]; + + const START_OFS = 0x110; + let shadowPtr = START_OFS; + for (let i = 0; i < limbs.length; i++) { + const prim = encodeMesh(limbs[i]); + const { tri, quad, vertices } = prim; + prims.push(prim); + shadowPtr += tri.length; + shadowPtr += quad.length; + shadowPtr += vertices.length; + } + + const mesh = Buffer.alloc(BODY_LEN, 0x80); + // Need to zero out the header + for (let i = 0; i < START_OFS; i++) { + mesh[i] = 0; + } + let headerOfs = 0; + let contentOfs = START_OFS - BODY_START; + prims.forEach((prim) => { + const { tri, quad, vertices } = prim; + const triCount = tri.length / 12; + const quadCount = quad.length / 12; + const vertCount = vertices.length / 4; + + console.log("Vert Count: ", vertCount); + + // Write the header for each primitive + mesh.writeUInt8(triCount, headerOfs + 0); // tris + mesh.writeUInt8(quadCount, headerOfs + 1); // quads + mesh.writeUInt8(vertCount, headerOfs + 2); // verts + mesh.writeUInt8(0, headerOfs + 3); // nop + headerOfs += 4; + + // Triangle Definition Offset + mesh.writeUInt32LE(contentOfs + BODY_START, headerOfs); + headerOfs += 4; + for (let i = 0; i < tri.length; i++) { + mesh[contentOfs++] = tri[i]; + } + + // Quad Definition Offset + mesh.writeUInt32LE(contentOfs + BODY_START, headerOfs); + headerOfs += 4; + for (let i = 0; i < quad.length; i++) { + mesh[contentOfs++] = quad[i]; + } + + // Vertex Definition Offset + mesh.writeUInt32LE(contentOfs + BODY_START, headerOfs); + headerOfs += 4; + for (let i = 0; i < vertices.length; i++) { + mesh[contentOfs++] = vertices[i]; + } + + // Triangle Shadow Offset + mesh.writeUInt32LE(shadowPtr, headerOfs); + headerOfs += 4; + + // Quad Shadow Offset + mesh.writeUInt32LE(shadowPtr, headerOfs); + headerOfs += 4; + }); + + console.log("End Offset: 0x%s", contentOfs.toString(16)); + console.log("Length: 0x%s", BODY_LEN.toString(16)); + + return mesh; +}; + +const encodeModelFeet = ( + rightFoot: string, // filename + leftFoot: string, //filename, +) => { + const MESH_START = 0x1a80; + const MESH_END = 0x1f00; + const MESH_LEN = MESH_END - MESH_START; + + const prims: Primitive[] = []; + const limbs = [ + readFileSync(rightFoot, "ascii"), + readFileSync(leftFoot, "ascii"), + ]; + + const START_OFS = 0x1ab0; + let shadowPtr = START_OFS; + for (let i = 0; i < limbs.length; i++) { + const prim = encodeMesh(limbs[i]); + const { tri, quad, vertices } = prim; + prims.push(prim); + shadowPtr += tri.length; + shadowPtr += quad.length; + shadowPtr += vertices.length; + } + + const mesh = Buffer.alloc(MESH_LEN, 0x80); + // Need to zero out the header + for (let i = 0; i < START_OFS; i++) { + mesh[i] = 0; + } + let headerOfs = 0; + let contentOfs = START_OFS - MESH_START; + prims.forEach((prim) => { + const { tri, quad, vertices } = prim; + const triCount = tri.length / 12; + const quadCount = quad.length / 12; + const vertCount = vertices.length / 4; + + console.log("Vert Count: ", vertCount); + + // Write the header for each primitive + mesh.writeUInt8(triCount, headerOfs + 0); // tris + mesh.writeUInt8(quadCount, headerOfs + 1); // quads + mesh.writeUInt8(vertCount, headerOfs + 2); // verts + mesh.writeUInt8(0, headerOfs + 3); // nop + headerOfs += 4; + + // Triangle Definition Offset + mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs); + headerOfs += 4; + for (let i = 0; i < tri.length; i++) { + mesh[contentOfs++] = tri[i]; + } + + // Quad Definition Offset + mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs); + headerOfs += 4; + for (let i = 0; i < quad.length; i++) { + mesh[contentOfs++] = quad[i]; + } + + // Vertex Definition Offset + mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs); + headerOfs += 4; + for (let i = 0; i < vertices.length; i++) { + mesh[contentOfs++] = vertices[i]; + } + + // Triangle Shadow Offset + mesh.writeUInt32LE(shadowPtr, headerOfs); + headerOfs += 4; + + // Quad Shadow Offset + mesh.writeUInt32LE(shadowPtr, headerOfs); + headerOfs += 4; + }); + + console.log("End Offset: 0x%s", contentOfs.toString(16)); + console.log("Length: 0x%s", MESH_LEN.toString(16)); + + return mesh; +}; + +const encodeModelLeftArm = (m0: string, m1: string, m2: string) => { + const MESH_START = 0x1f00; + const MESH_END = 0x2c00; + const MESH_LEN = MESH_END - MESH_START; + + const prims: Primitive[] = []; + const limbs = [ + readFileSync(m0, "ascii"), + readFileSync(m1, "ascii"), + readFileSync(m2, "ascii"), + ]; + + const START_OFS = 0x1f48; + let shadowPtr = START_OFS; + for (let i = 0; i < limbs.length; i++) { + const prim = encodeMesh(limbs[i]); + const { tri, quad, vertices } = prim; + prims.push(prim); + shadowPtr += tri.length; + shadowPtr += quad.length; + shadowPtr += vertices.length; + } + + const mesh = Buffer.alloc(MESH_LEN, 0x80); + // Need to zero out the header + for (let i = 0; i < START_OFS; i++) { + mesh[i] = 0; + } + let headerOfs = 0; + let contentOfs = START_OFS - MESH_START; + prims.forEach((prim) => { + const { tri, quad, vertices } = prim; + const triCount = tri.length / 12; + const quadCount = quad.length / 12; + const vertCount = vertices.length / 4; + + console.log("Vert Count: ", vertCount); + + // Write the header for each primitive + mesh.writeUInt8(triCount, headerOfs + 0); // tris + mesh.writeUInt8(quadCount, headerOfs + 1); // quads + mesh.writeUInt8(vertCount, headerOfs + 2); // verts + mesh.writeUInt8(0, headerOfs + 3); // nop + headerOfs += 4; + + // Triangle Definition Offset + mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs); + headerOfs += 4; + for (let i = 0; i < tri.length; i++) { + mesh[contentOfs++] = tri[i]; + } + + // Quad Definition Offset + mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs); + headerOfs += 4; + for (let i = 0; i < quad.length; i++) { + mesh[contentOfs++] = quad[i]; + } + + // Vertex Definition Offset + mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs); + headerOfs += 4; + for (let i = 0; i < vertices.length; i++) { + mesh[contentOfs++] = vertices[i]; + } + + // Triangle Shadow Offset + mesh.writeUInt32LE(0x8a8, headerOfs); + headerOfs += 4; + + // Quad Shadow Offset + mesh.writeUInt32LE(0x8a8, headerOfs); + headerOfs += 4; + }); + + console.log("End Offset: 0x%s", contentOfs.toString(16)); + console.log("Length: 0x%s", MESH_LEN.toString(16)); + + return mesh; +}; + +const encodeModelRightArm = (m0: string, m1: string, m2: string) => { + const MESH_START = 0x2c00; + const MESH_END = 0x3200; + const MESH_LEN = MESH_END - MESH_START; + + const prims: Primitive[] = []; + const limbs = [ + readFileSync(m0, "ascii"), + readFileSync(m1, "ascii"), + readFileSync(m2, "ascii"), + ]; + + const START_OFS = 0x1f48; + let shadowPtr = START_OFS; + for (let i = 0; i < limbs.length; i++) { + const prim = encodeMesh(limbs[i]); + const { tri, quad, vertices } = prim; + prims.push(prim); + shadowPtr += tri.length; + shadowPtr += quad.length; + shadowPtr += vertices.length; + } + + const mesh = Buffer.alloc(MESH_LEN, 0x80); + // Need to zero out the header + for (let i = 0; i < START_OFS; i++) { + mesh[i] = 0; + } + let headerOfs = 0; + let contentOfs = START_OFS - MESH_START; + prims.forEach((prim) => { + const { tri, quad, vertices } = prim; + const triCount = tri.length / 12; + const quadCount = quad.length / 12; + const vertCount = vertices.length / 4; + + console.log("Vert Count: ", vertCount); + + // Write the header for each primitive + mesh.writeUInt8(triCount, headerOfs + 0); // tris + mesh.writeUInt8(quadCount, headerOfs + 1); // quads + mesh.writeUInt8(vertCount, headerOfs + 2); // verts + mesh.writeUInt8(0, headerOfs + 3); // nop + headerOfs += 4; + + // Triangle Definition Offset + mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs); + headerOfs += 4; + for (let i = 0; i < tri.length; i++) { + mesh[contentOfs++] = tri[i]; + } + + // Quad Definition Offset + mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs); + headerOfs += 4; + for (let i = 0; i < quad.length; i++) { + mesh[contentOfs++] = quad[i]; + } + + // Vertex Definition Offset + mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs); + headerOfs += 4; + for (let i = 0; i < vertices.length; i++) { + mesh[contentOfs++] = vertices[i]; + } + + // Triangle Shadow Offset + mesh.writeUInt32LE(0x8a8, headerOfs); + headerOfs += 4; + + // Quad Shadow Offset + mesh.writeUInt32LE(0x8a8, headerOfs); + headerOfs += 4; + }); + + console.log("End Offset: 0x%s", contentOfs.toString(16)); + console.log("Length: 0x%s", MESH_LEN.toString(16)); + + return mesh; +}; + +const encodeModelHead = ( + m0: string, // hair + m1: string, // face + m2: string, // mouth +) => { + const MESH_START = 0xe80; + const MESH_END = 0x1a80; + const MESH_LEN = MESH_END - MESH_START; + + const prims: Primitive[] = []; + const limbs = [ + readFileSync(m0, "ascii"), + readFileSync(m1, "ascii"), + readFileSync(m2, "ascii"), + ]; + + console.log(limbs[2]); + + const START_OFS = 0xec8; + + // Encode head + const hd = encodeMesh(limbs[0], false); + prims.push(hd); + + prims.push({ + vertices: Buffer.from( + "5f2cde3ba12fde3bb06faf3b00402f39b5a32f3b4ba02f3b007c1f380598bf38fb9bbf38001cfe38a6cfce3b5accce3b506caf3b", + "hex", + ), + tri: Buffer.from( + "01101f1b051f00008a8100e03a2b3d363a360000838301a0072f002f03260000078401a03336382b39360000888101a0201b3f103b1f0000830503e0", + "hex", + ), + quad: Buffer.from( + "2222201b3a223b1f874181e11e2206221f1b051f08c240e01f1b01101f000002034522a0201b20003f103f0283c402a0", + "hex", + ), + }); + prims.push({ + vertices: Buffer.from( + "0060003bb5a32f3b4ba02f3b2d14103bd317103b00ec4f390598bf38fb9bbf38", + "hex", + ), + tri: Buffer.from( + "202b2f2e20360000850100a0102e1f2b1f360000840200a022231f2b1d23000086c201a0", + "hex", + ), + quad: Buffer.from( + "06231d23102e1f2b8103a1a03a232f2e2223202b8281a1a0", + "hex", + ), + }); + + const mesh = Buffer.alloc(MESH_LEN, 0x80); + // Need to zero out the header + for (let i = 0; i < START_OFS; i++) { + mesh[i] = 0; + } + let headerOfs = 0; + let contentOfs = START_OFS - MESH_START; + prims.forEach((prim) => { + const { tri, quad, vertices } = prim; + const triCount = tri.length / 12; + const quadCount = quad.length / 12; + const vertCount = vertices.length / 4; + + console.log("Vert Count: ", vertCount); + + // Write the header for each primitive + mesh.writeUInt8(triCount, headerOfs + 0); // tris + mesh.writeUInt8(quadCount, headerOfs + 1); // quads + mesh.writeUInt8(vertCount, headerOfs + 2); // verts + mesh.writeUInt8(0, headerOfs + 3); // nop + headerOfs += 4; + + // Triangle Definition Offset + mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs); + headerOfs += 4; + for (let i = 0; i < tri.length; i++) { + mesh[contentOfs++] = tri[i]; + } + + // Quad Definition Offset + mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs); + headerOfs += 4; + for (let i = 0; i < quad.length; i++) { + mesh[contentOfs++] = quad[i]; + } + + // Vertex Definition Offset + mesh.writeUInt32LE(contentOfs + MESH_START, headerOfs); + headerOfs += 4; + for (let i = 0; i < vertices.length; i++) { + mesh[contentOfs++] = vertices[i]; + } + + // Triangle Shadow Offset + mesh.writeUInt32LE(0x8a8, headerOfs); + headerOfs += 4; + + // Quad Shadow Offset + mesh.writeUInt32LE(0x8a8, headerOfs); + headerOfs += 4; + }); + + writeFileSync("head_debug.bin", mesh); + console.log("--- Head ----"); + console.log("End Offset: 0x%s", contentOfs.toString(16)); + console.log("Length: 0x%s", MESH_LEN.toString(16)); + + return mesh; +}; + +// We should be modifying the instance of the ROM +const replaceInRom = (file: Buffer, filepos: number, ROM: Buffer) => { + const len = Math.ceil(file.length / 0x800); + const segments: Buffer[] = []; + + // First we split the file into segments + for (let i = 0; i < len; i++) { + const start = i * 0x800; + const end = (i + 1) * 0x800; + const stop = file.length < end ? file.length : end; + const segment = file.subarray(start, stop); + segments.push(segment); + } + + // Then copy it over to the ROM + let ofs = filepos; + segments.forEach((segment) => { + for (let i = 0; i < 0x800; i++) { + ROM[ofs + i] = segment[i]; + } + ofs += 0x930; + }); +}; + +const replaceModel = ( + gamefile: Buffer, + bodyBuffer: Buffer, + feetBuffer: Buffer, + leftBuffer: Buffer, + rightBuffer: Buffer, + headBuffer: Buffer, +) => { + const modded = Buffer.from(gamefile); + const read = gamefile.subarray(0x30); + + let seek = 0xe80; + for (let i = 0; i < 3; i++) { + const triCount = read.readUInt8(seek + 0); + const quadCount = read.readUInt8(seek + 1); + const vertCount = read.readUInt8(seek + 2); + + console.log( + "%s %s %s", + triCount.toString(16), + quadCount.toString(16), + vertCount.toString(16), + ); + const triOfs = read.readUInt32LE(seek + 4); + console.log("Tris ofs: 0x%s", triOfs.toString(16), i); + + const quadOfs = read.readUInt32LE(seek + 8); + const vertOfs = read.readUInt32LE(seek + 12); + seek += 0x18; + + if (i === 0) { + continue; + } + + console.log("Reading face: ", i); + const vertices = read.subarray(vertOfs, vertOfs + vertCount * 4); + const tri = read.subarray(triOfs, triOfs + triCount * 12); + const quad = read.subarray(quadOfs, quadOfs + quadCount * 12); + + const prim: Primitive = { + vertices, + tri, + quad, + }; + + console.log(`{ + vertices: Buffer.from('${vertices.toString("hex")}', 'hex'), + tri: Buffer.from('${tri.toString("hex")}', 'hex'), + quad: Buffer.from('${quad.toString("hex")}', 'hex'), +}`); + } + + // Zero out the entire model + for (let i = 0xb0; i < 0x31f0; i++) { + modded[i] = 0; + } + + const BIN_HEADER_SIZE = 0x30; + + // Replace with our updated model + const bodyOfs = 0x0080 + BIN_HEADER_SIZE; + for (let i = 0; i < bodyBuffer.length; i++) { + modded[bodyOfs + i] = bodyBuffer[i]; + } + + const headOfs = 0xe80 + BIN_HEADER_SIZE; + for (let i = 0; i < headBuffer.length; i++) { + modded[headOfs + i] = headBuffer[i]; + } + + const feetOfs = 0x1a80 + BIN_HEADER_SIZE; + for (let i = 0; i < feetBuffer.length; i++) { + modded[feetOfs + i] = feetBuffer[i]; + } + + const leftOfs = 0x1f00 + BIN_HEADER_SIZE; + for (let i = 0; i < leftBuffer.length; i++) { + modded[leftOfs + i] = leftBuffer[i]; + } + + const rightOfs = 0x2c00 + BIN_HEADER_SIZE; + for (let i = 0; i < leftBuffer.length; i++) { + modded[rightOfs + i] = rightBuffer[i]; + } + + return modded; +}; + +const encodeModel = ( + filename: string, + // Body Section + bodyObject: string, + hipsObject: string, + rLegTopObject: string, + rLegBtmObject: string, + lLegTopObject: string, + lLegBtmObject: string, + // Feet + rightFootObject: string, + leftFootObject: string, + // Left Arm + leftShoulder: string, + leftArm: string, + leftHand: string, + // Right Arm + rightShoulder: string, + rightArm: string, + rightHand: string, + // Head + hairObject: string, + eyesObject: string, + mouthObject: string, +) => { + // Encode the body + const srcModel = readFileSync(`bin/${filename}`); + const body = encodeModelBody( + bodyObject, + hipsObject, + rLegTopObject, + rLegBtmObject, + lLegTopObject, + lLegBtmObject, + ); + const feet = encodeModelFeet(rightFootObject, leftFootObject); + const lArm = encodeModelLeftArm(leftShoulder, leftArm, leftHand); + const rArm = encodeModelRightArm(rightShoulder, rightArm, rightHand); + const head = encodeModelHead(hairObject, eyesObject, mouthObject); + const updatedModel = replaceModel(srcModel, body, feet, lArm, rArm, head); + writeFileSync(`mod/${filename}`, updatedModel.subarray(0x30, 0x1f00 + 0x30)); +}; + +export { encodeModel }; diff --git a/src/EncodeRom.ts b/src/EncodeRom.ts new file mode 100644 index 0000000..fbdb28e --- /dev/null +++ b/src/EncodeRom.ts @@ -0,0 +1,8 @@ +import { readFileSync, writeFileSync } from "fs"; + +const encodeRom = () => { + console.log("Encoding the rom"); +}; + +export { encodeRom }; +export default encodeRom; diff --git a/src/EncodeTexture.ts b/src/EncodeTexture.ts new file mode 100644 index 0000000..7e3006f --- /dev/null +++ b/src/EncodeTexture.ts @@ -0,0 +1,136 @@ +import { readFileSync, writeFileSync } from "fs"; +import { PNG } from "pngjs"; + +const encodeTexel = (r: number, g: number, b: number, a: number) => { + const rClr = Math.floor((r >> 3) & 0xff); + const gClr = Math.floor((g >> 3) & 0xff); + const bClr = Math.floor((b >> 3) & 0xff); + const aClr = a === 0 ? 0 : 0x8000; + const texel = rClr | (gClr << 5) | (bClr << 5) | aClr; + return texel; +}; + +const encodeImage = (pngSrc: Buffer) => { + const pngInfo = PNG.sync.read(pngSrc); + const { width, height, data } = pngInfo; + + console.log("Encoding image"); + + if (width !== 256 || height !== 256) { + throw new Error("Encoder expects a 256x256 image"); + } + + console.log(data.length.toString(16)); + + let inOfs = 0; + let outOfs = 0; + const palette: number[] = [0]; + const pal = Buffer.alloc(0x20, 0); + const img = Buffer.alloc(0x8000, 0); + + const readPixel = () => { + const a = data.readUInt8(inOfs + 3) === 0 ? 0 : 255; + const r = a === 0 ? 0 : data.readUInt8(inOfs + 0); + const g = a === 0 ? 0 : data.readUInt8(inOfs + 1); + const b = a === 0 ? 0 : data.readUInt8(inOfs + 2); + const texel = encodeTexel(r, g, b, a); + inOfs += 4; + + // Search through the existing palette + const index = palette.indexOf(texel); + + // If doesn't exist, we add it to the palette + if (index === -1) { + const pos = palette.length; + palette.push(texel); + return pos; + } + + return index; + }; + + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x += 2) { + const lowByte = readPixel(); + const highByte = readPixel(); + const byte = ((highByte << 4) | lowByte) & 0xff; + img[outOfs] = byte; + outOfs++; + } + } + + // for(let i = 0; i < palette.length; i++){ + // console.log(palette[i].toString(16)) + // } + + outOfs = 0; + console.log("Palette colors: "); + for (let i = 0; i < 16; i++) { + const texel = palette[i] || 0x0000; + console.log(texel.toString(16)); + pal.writeUInt16LE(texel, outOfs); + outOfs += 2; + } + + return [pal, img]; +}; + +const replaceTexture = ( + gamefile: Buffer, + bodyBuffer: Buffer, + faceBuffer: Buffer, +) => { + const modded = Buffer.from(gamefile); + const [bodyPal, bodyImg] = encodeImage(bodyBuffer); + const [facePal, faceImg] = encodeImage(faceBuffer); + + let ofs = 0; + + // Paltte 0 + const bodyPalOfs = 0x30; + ofs = bodyPalOfs; + for (let i = 0; i < bodyPal.length; i++) { + modded[bodyPalOfs + i] = bodyPal[i]; + } + + // Image 0 + const bodyImgOfs = 0x800; + for (let i = 0; i < 0x8000; i++) { + modded[bodyImgOfs + i] = bodyImg[i]; + } + + // palette 1 + // Black for the second texture + ofs = 0x8830; + for (let pal = 0; pal < 3; pal++) { + for (let i = 0; i < 16; i++) { + modded.writeUInt16LE(0x8000, ofs); + ofs += 2; + } + } + + // Pallete 2 + const facePalOfs = 0x9030; + for (let i = 0; i < facePal.length; i++) { + modded[facePalOfs + i] = facePal[i]; + } + + // Image 1 + const faceImgOfs = 0x9800; + for (let i = 0; i < 0x4000; i++) { + modded[faceImgOfs + i] = faceImg[i]; + } + + return modded; +}; + +const encodeTexture = (bodyTexture: string, faceTexture: string) => { + // Encode the body and face texture to write to ROM + const srcTexture = readFileSync("bin/PL01T.BIN"); + const bodyBuffer = readFileSync(bodyTexture); + const faceBuffer = readFileSync(faceTexture); + const modTexture = replaceTexture(srcTexture, bodyBuffer, faceBuffer); + writeFileSync("mod/PL01T.BIN", modTexture); +}; + +export { encodeTexture }; From e17201e02ec899e94c0cd27acaa384098e7504b2 Mon Sep 17 00:00:00 2001 From: kion Date: Fri, 12 Jul 2024 20:58:44 +0900 Subject: [PATCH 2/4] replace files in rom --- index.ts | 17 ----------------- mod/PL01T.BIN | Bin 0 -> 73728 bytes src/EncodeModel.ts | 46 +++++++++++++++++++++++---------------------- src/EncodeRom.ts | 25 +++++++++++++++++++++++- 4 files changed, 48 insertions(+), 40 deletions(-) create mode 100644 mod/PL01T.BIN diff --git a/index.ts b/index.ts index 191e755..a93efbe 100644 --- a/index.ts +++ b/index.ts @@ -8,28 +8,11 @@ encodeTexture("png/body-1.png", "png/face-1.png"); // No helmet + Normal Shoes encodeModel( "PL00P010.BIN", - // Body Encoding - "obj/02_BODY.obj", - "obj/03_HIPS.obj", - "obj/10_LEG_RIGHT_TOP.obj", - "obj/11_LEG_RIGHT_BOTTOM.obj", - "obj/13_LEG_LEFT_TOP.obj", - "obj/14_LEG_LEFT_BOTTOM.obj", // Feet "obj/12_RIGHT_FOOT.obj", "obj/15_LEFT_FOOT.obj", - // Left Arm - "obj/07_LEFT_SHOULDER.obj", - "obj/08_LEFT_ARM.obj", - "obj/09_LEFT_HAND.obj", - // Right Arm - "obj/04_RIGHT_SHOULDER.obj", - "obj/05_RIGHT_ARM.obj", - "obj/06_RIGHT_HAND.obj", // Head "obj/01_HEAD_HELMET.obj", - "obj/01_HEAD_FACE.obj", - "obj/01_HEAD_MOUTH.obj", ); encodeRom(); diff --git a/mod/PL01T.BIN b/mod/PL01T.BIN new file mode 100644 index 0000000000000000000000000000000000000000..4ef5937f61100a4763254ccab14124fbf51fc062 GIT binary patch literal 73728 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 literal 0 HcmV?d00001 diff --git a/src/EncodeModel.ts b/src/EncodeModel.ts index 85cd5d9..9a00607 100644 --- a/src/EncodeModel.ts +++ b/src/EncodeModel.ts @@ -898,12 +898,6 @@ const replaceModel = ( tri, quad, }; - - console.log(`{ - vertices: Buffer.from('${vertices.toString("hex")}', 'hex'), - tri: Buffer.from('${tri.toString("hex")}', 'hex'), - quad: Buffer.from('${quad.toString("hex")}', 'hex'), -}`); } // Zero out the entire model @@ -943,30 +937,38 @@ const replaceModel = ( }; const encodeModel = ( + // Filename to replace filename: string, - // Body Section - bodyObject: string, - hipsObject: string, - rLegTopObject: string, - rLegBtmObject: string, - lLegTopObject: string, - lLegBtmObject: string, // Feet rightFootObject: string, leftFootObject: string, // Left Arm - leftShoulder: string, - leftArm: string, - leftHand: string, - // Right Arm - rightShoulder: string, - rightArm: string, - rightHand: string, + // Head hairObject: string, - eyesObject: string, - mouthObject: string, ) => { + // Body Section + const bodyObject = "obj/02_BODY.obj"; + const hipsObject = "obj/03_HIPS.obj"; + const rLegTopObject = "obj/10_LEG_RIGHT_TOP.obj"; + const rLegBtmObject = "obj/11_LEG_RIGHT_BOTTOM.obj"; + const lLegTopObject = "obj/13_LEG_LEFT_TOP.obj"; + const lLegBtmObject = "obj/14_LEG_LEFT_BOTTOM.obj"; + + // Left Arm + const leftShoulder = "obj/07_LEFT_SHOULDER.obj"; + const leftArm = "obj/08_LEFT_ARM.obj"; + const leftHand = "obj/09_LEFT_HAND.obj"; + + // Right Arm + const rightShoulder = "obj/04_RIGHT_SHOULDER.obj"; + const rightArm = "obj/05_RIGHT_ARM.obj"; + const rightHand = "obj/06_RIGHT_HAND.obj"; + + // Eyes and mouth + const eyesObject = "obj/01_HEAD_FACE.obj"; + const mouthObject = "obj/01_HEAD_MOUTH.obj"; + // Encode the body const srcModel = readFileSync(`bin/${filename}`); const body = encodeModelBody( diff --git a/src/EncodeRom.ts b/src/EncodeRom.ts index fbdb28e..54d74d6 100644 --- a/src/EncodeRom.ts +++ b/src/EncodeRom.ts @@ -1,7 +1,30 @@ -import { readFileSync, writeFileSync } from "fs"; +import { readFileSync, writeFileSync, readdirSync } from "fs"; + +const replaceInRom = (sourceRom: Buffer, sourceFile: Buffer, moddedFile: Buffer) { + +} const encodeRom = () => { console.log("Encoding the rom"); + const sourceRom = process.env.SRC_ROM; + if (!sourceRom) { + throw new Error("Need to set SRC_ROM value in .env"); + } + + const romDst = process.env.DST_ROM; + if (!romDst) { + throw new Error("Need to set DST_ROM value in .env"); + } + const rom = readFileSync(sourceRom); + + const replaceFiles = readdirSync("./mod"); + replaceFiles.forEach((filename) => { + const sourceFile = readFileSync(`./bin/${filename}`); + const moddedFile = readFileSync(`./mod/${filename}`); + replaceInRom(rom, sourceFile, moddedFile) + }); + + writeFileSync(romDst, rom) }; export { encodeRom }; From 34a82f1f3f881a9ab8079395c587a84e668c5fd0 Mon Sep 17 00:00:00 2001 From: kion Date: Sat, 13 Jul 2024 07:57:13 +0900 Subject: [PATCH 3/4] search in rom --- head_debug.bin | Bin 3072 -> 0 bytes index.ts | 7 ++++- mod/PL00P000.BIN | Bin 7936 -> 0 bytes src/EncodeModel.ts | 29 --------------------- src/EncodeRom.ts | 63 ++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 65 insertions(+), 34 deletions(-) delete mode 100644 head_debug.bin delete mode 100644 mod/PL00P000.BIN diff --git a/head_debug.bin b/head_debug.bin deleted file mode 100644 index aa9b77aa52b435047267108aac71501d92ff05be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3072 zcmeHJ{ZmtC7CyO`eCH-HC~-tc<^~M2h*7DSOwRJku?d(pS(HXmT&k5uHy8l4AbLXD- zdCz^`=RD`VFY#D5Kv)g%b}T^GB!Dxr@mn0jL75)lDBfYbU*RkxgP(D1oD5*a`xSme z7tsyjShNEmwbI~8UJ2lv4=`nf9b-oT_6&-E9h)+eJO&_U8Hh}d7$|?t2Vm(k_n6NC z9M}yIF$^RUF5d%S@EBGS51S1D&CFg!6F#o9kX>V|&Uq6+J;*@PGj@zPn-~T#qn>h< zH&|d1mpubG_<9U#NQtg1kyE}5XE1+}2yUR_hz=lD43NCi;7P3nI3yASi<+mP=7vTQ zBx6MRaXDh33L~fuu1^8;IdhMt3!qUcfuYF*i4g;UVhKY$$b_5&IBsJ0LNmbe{SwG| z(^j2b1E6n|ASXMPgxUgcNH9lHk693ZOAMrpMf}7YQp`yiAq{c+$T!(D%Ab@k0XM&e z-u}ECfhdrhqN{KHgsoU^=Y8oOC`9`#bG31r+D!c`3(S zq5!)$&uec5NDC;zTkEfFJcfPnDX*;_@4rZ@C%6}I5-Cl2$|NIdpRJy#iZshzxufnzL zQ9WI#$61)p(^|b>a;VeI6Q^0nVZAPXHR?}Vroy`1(b!a^f_>-%X*lz#3iafnB7nd6 z39sh7n15zif$QdZ$)A~@lUK$0RQqZ4$BhmWe+TjxV?=t)Q&iB@+tR4Vu7PTfb{W8xH5_`9r=}iVZdZdB-61>&aoTU>ZT2@4kiR&eYR_e0d^E$rxT>2K;qZ@<$*OMgoj#h-ku#ajHev~Jc~4eqP)eexUJ zlUr~%P%lnBRKbeSvXiAJ0g4vIKtZq&FMzfbDV`^h$#kCVyNG9wZam&`5zm&o>2>tQ z-+R1+vDZ23=~&SgtlOODVTSn`v+Fu3&O@?dfBMAc(CVl;C^Ryia$;G5Iy@Lka% zC^?Q7&goKi$?;qENqbk3$UEVf&?+cbNnk&Cs8bcV( ztt))a_Bs0;VQ1K}zcpLZ=j_B$r$afYk`gxT9Ck1(jnXI1VdoRau)_y4qzXTtbOC7o z^!74#>B&M=xjcA(?*&nxGfYely?C>fI6Ix44t-*>JdE!mlcO<`FMmQT$a(nN1@cSt zuP^AEee<>8!74dDFTH+!!F$QyvvzdEQI}n_WY+^xv_h;>r~E*!-ya*8*sMET_Cv3N zo-}@t`geuyaBP4(!nGD}C{far#w-6x_{3)Jh`QyA>RzQLpx)25st=ZSf3Bn_jaT*f z6*NzI!<*hxD(X2r!_@~=|2C-x5H}&-75=s197RQV{4&&*auv7m!g2dgIxdRhc=6Mls)`6+h_}tQ zj^j99JIALQf3qSyTKByQlF}q2hBnE-1r-CTHL}a;S0%TlkO}`P5Wy0y5I$~+fiq?E zvd^oSmdhJOXJ1^Icd?;|Ij}y+>|L+Uy12DGv%A5N1-@%yerlT}cji?|dJJS*eU_Nb zmo|z@o?n@_*w@3j@#e)BlpL1{G>ma9nuS56=^d5)+;+Fv`J3g4aA;M>XkEpY0%t$9H*OhHxRc% zpCCJ3GnM`Q_9jMkXm;$LB!|@Xk0OmnrdJ+%mL~o9QJ3`ni3ze*Ra4oEFfazPuHPdwOto?8c{Q(#+8= zDY);4JF!nJJ;!TFs1XJ`kQ%GV;FwxvjwmoDwS!VDiW9qJ* k?ICc3@M?{;iCW=-UMs*41-AdKg+Eh%{rlB{|L4Ge0j4+!CjbBd diff --git a/index.ts b/index.ts index a93efbe..f95a2ed 100644 --- a/index.ts +++ b/index.ts @@ -3,7 +3,12 @@ import { encodeTexture } from "./src/EncodeTexture"; import { encodeRom } from "./src/EncodeRom"; // Encode the Texture -encodeTexture("png/body-1.png", "png/face-1.png"); +encodeTexture( + // Body Texture + "png/body-1.png", + // Face Texture + "png/face-1.png", +); // No helmet + Normal Shoes encodeModel( diff --git a/mod/PL00P000.BIN b/mod/PL00P000.BIN deleted file mode 100644 index 22ba8e57c4b32c963d7b0d178aa4e4622259e7a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7936 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 { - const len = Math.ceil(file.length / 0x800); - const segments: Buffer[] = []; - - // First we split the file into segments - for (let i = 0; i < len; i++) { - const start = i * 0x800; - const end = (i + 1) * 0x800; - const stop = file.length < end ? file.length : end; - const segment = file.subarray(start, stop); - segments.push(segment); - } - - // Then copy it over to the ROM - let ofs = filepos; - segments.forEach((segment) => { - for (let i = 0; i < 0x800; i++) { - ROM[ofs + i] = segment[i]; - } - ofs += 0x930; - }); -}; - const replaceModel = ( gamefile: Buffer, bodyBuffer: Buffer, diff --git a/src/EncodeRom.ts b/src/EncodeRom.ts index 54d74d6..d96ddd0 100644 --- a/src/EncodeRom.ts +++ b/src/EncodeRom.ts @@ -1,8 +1,63 @@ import { readFileSync, writeFileSync, readdirSync } from "fs"; +import { RGBA_ASTC_10x5_Format } from "three"; +const CHUNK_SIZE = 0x800; +const STRIDE_SIZE = 0x930; -const replaceInRom = (sourceRom: Buffer, sourceFile: Buffer, moddedFile: Buffer) { +const replaceInRom = ( + sourceRom: Buffer, + sourceFile: Buffer, + moddedFile: Buffer, +) => { + const totalChunks = Math.ceil(sourceFile.length / CHUNK_SIZE); + const chunkPos: number[] = []; + for (let i = 0; i < totalChunks; i++) { + const start = i * CHUNK_SIZE; + const end = start + CHUNK_SIZE; + const needle = sourceFile.subarray(start, end); + chunkPos.push(sourceRom.indexOf(needle)); + } + + // Find the largest continuous section with a difference of STRIDE_SIZE + let longestSeqStart = 0; + let longestSeqLength = 0; + let currentSeqStart = 0; + let currentSeqLength = 1; + + for (let i = 0; i < chunkPos.length - 1; i++) { + const diff = chunkPos[i + 1] - chunkPos[i]; + if (diff === STRIDE_SIZE) { + currentSeqLength++; + } else { + if (currentSeqLength > longestSeqLength) { + longestSeqStart = currentSeqStart; + longestSeqLength = currentSeqLength; + } + currentSeqStart = i + 1; + currentSeqLength = 1; + } + } + + // Check the last sequence + if (currentSeqLength > longestSeqLength) { + longestSeqStart = currentSeqStart; + longestSeqLength = currentSeqLength; + } -} + // Normalize positions + for (let i = longestSeqStart - 1; i >= 0; i--) { + chunkPos[i] = chunkPos[i + 1] - STRIDE_SIZE; + } + for (let i = longestSeqStart + 1; i < chunkPos.length; i++) { + chunkPos[i] = chunkPos[i - 1] + STRIDE_SIZE; + } + + let ofs = 0; + chunkPos.forEach((pos) => { + for (let i = 0; i < CHUNK_SIZE; i++) { + sourceRom[pos + i] = moddedFile[ofs++]; + } + }); +}; const encodeRom = () => { console.log("Encoding the rom"); @@ -21,10 +76,10 @@ const encodeRom = () => { replaceFiles.forEach((filename) => { const sourceFile = readFileSync(`./bin/${filename}`); const moddedFile = readFileSync(`./mod/${filename}`); - replaceInRom(rom, sourceFile, moddedFile) + replaceInRom(rom, sourceFile, moddedFile); }); - writeFileSync(romDst, rom) + writeFileSync(romDst, rom); }; export { encodeRom }; From 6a56c1527490c3284935c391a56731c39483919a Mon Sep 17 00:00:00 2001 From: kion Date: Sat, 13 Jul 2024 07:58:21 +0900 Subject: [PATCH 4/4] remove plater dat --- player.dat | Bin 11072 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 player.dat diff --git a/player.dat b/player.dat deleted file mode 100644 index b6071b38a076e8a8a173be447077b864918db118..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11072 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