From 4919ebfe48531f1777828ff34bc35efa726ca297 Mon Sep 17 00:00:00 2001 From: edo999 Date: Sun, 19 May 2024 14:25:11 +0100 Subject: [PATCH] =?UTF-8?q?Parse=20the=20title=20graphics=20=F0=9F=8E=9E?= =?UTF-8?q?=EF=B8=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Header.js | 2 +- src/components/RoomGfxList.js | 6 +- src/components/TitleGfxList.js | 25 ++++++++ .../{RoomGfxContainer.js => GfxContainer.js} | 30 ++++++---- src/containers/ResourceExplorer.js | 34 ++++++++++- src/lib/parser/parseRom.js | 16 +++++- src/lib/parser/parseTitles.js | 57 +++++++++++++++++++ src/lib/resources.js | 8 +++ 8 files changed, 158 insertions(+), 20 deletions(-) create mode 100644 src/components/TitleGfxList.js rename src/containers/{RoomGfxContainer.js => GfxContainer.js} (51%) create mode 100644 src/lib/parser/parseTitles.js diff --git a/src/components/Header.js b/src/components/Header.js index 8b27000..d5c05e2 100644 --- a/src/components/Header.js +++ b/src/components/Header.js @@ -15,7 +15,7 @@ import meteor from '../assets/meteor.png'; const navigation = [ { name: 'Rooms', href: '/rooms/1' }, - { name: 'Room Gfx', href: '/roomgfx/0' }, + { name: 'Gfx', href: '/roomgfx/0' }, { name: 'Scripts', href: '/scripts/1' }, { name: 'Prepositions', href: '/preps' }, { name: 'ROM map', href: '/rom-map' }, diff --git a/src/components/RoomGfxList.js b/src/components/RoomGfxList.js index 43e9cb3..7883853 100644 --- a/src/components/RoomGfxList.js +++ b/src/components/RoomGfxList.js @@ -1,11 +1,11 @@ import ColumnListHeader from './ColumnListHeader'; import ColumnListItem from './ColumnListItem'; -const RoomGfxList = ({ roomgfx, currentId }) => { +const RoomGfxList = ({ gfx, currentId }) => { return ( <> - Room Gfx - {roomgfx.map(({ metadata }) => { + Room gfx + {gfx.map(({ metadata }) => { const selected = metadata.id === currentId; const path = `/roomgfx/${metadata.id}`; const label = `Tileset ${metadata.id}`; diff --git a/src/components/TitleGfxList.js b/src/components/TitleGfxList.js new file mode 100644 index 0000000..6938aaf --- /dev/null +++ b/src/components/TitleGfxList.js @@ -0,0 +1,25 @@ +import ColumnListHeader from './ColumnListHeader'; +import ColumnListItem from './ColumnListItem'; + +const TitleGfxList = ({ gfx, currentId }) => { + return ( + <> + Title gfx + {gfx.map(({ metadata }) => { + const selected = metadata.id === currentId; + const path = `/titlegfx/${metadata.id}`; + const label = `Tileset ${metadata.id}`; + + return ( + + {label} + + ); + })} + + ); +}; + +export default TitleGfxList; diff --git a/src/containers/RoomGfxContainer.js b/src/containers/GfxContainer.js similarity index 51% rename from src/containers/RoomGfxContainer.js rename to src/containers/GfxContainer.js index cc99d2e..370fe13 100644 --- a/src/containers/RoomGfxContainer.js +++ b/src/containers/GfxContainer.js @@ -1,19 +1,21 @@ -import { useParams } from 'react-router-dom'; +import { useMatch, useParams } from 'react-router-dom'; import PrimaryColumn from '../components/PrimaryColumn'; import RoomGfxList from '../components/RoomGfxList'; +import TitleGfxList from '../components/TitleGfxList'; import Main from '../components/Main'; import MainHeader from '../components/MainHeader'; import ResourceMetadata from '../components/ResourceMetadata'; import GfxCanvasContainer from './GfxCanvasContainer'; -const RoomGfxContainer = ({ roomgfx }) => { +const GfxContainer = ({ roomgfx, titlegfx }) => { + const isRoomGfx = !!useMatch('/roomgfx/:gfcId'); const { gfcId } = useParams(); const currentGfcId = typeof gfcId === 'undefined' ? null : parseInt(gfcId, 10); - const roomgfc = roomgfx[currentGfcId]; + const gfc = isRoomGfx ? roomgfx[currentGfcId] : titlegfx[currentGfcId]; - if (!roomgfc) { + if (!gfc) { return null; } @@ -21,23 +23,29 @@ const RoomGfxContainer = ({ roomgfx }) => { <> +
{currentGfcId !== null && ( - + )}
@@ -45,4 +53,4 @@ const RoomGfxContainer = ({ roomgfx }) => { ); }; -export default RoomGfxContainer; +export default GfxContainer; diff --git a/src/containers/ResourceExplorer.js b/src/containers/ResourceExplorer.js index ed2178b..da24dc4 100644 --- a/src/containers/ResourceExplorer.js +++ b/src/containers/ResourceExplorer.js @@ -1,6 +1,6 @@ import { Routes, Route } from 'react-router-dom'; import RoomsContainer from './RoomsContainer'; -import RoomGfxContainer from './RoomGfxContainer'; +import GfxContainer from './GfxContainer'; import PrepositionsContainer from './PrepositionsContainer'; import RomMapContainer from './RomMapContainer'; import SettingsContainer from './SettingsContainer'; @@ -35,10 +35,38 @@ const ResourceExplorer = ({ rom, res, resources }) => { }> + element={ + + }> } + element={ + + } + /> + + + }> + + } /> { const rooms = []; @@ -10,7 +11,7 @@ const parseRom = (arrayBuffer, res) => { const globdata = []; const scripts = []; const preps = []; - let objects = []; + const titles = []; for (let i = 0; i < res?.rooms?.length; i++) { const [offset, length] = res.rooms[i]; @@ -56,13 +57,24 @@ const parseRom = (arrayBuffer, res) => { preps.push(item); } + // The title screens are stored outside of SCUMM. + for (let i = 0; i < res?.titleoffs?.length; i++) { + const [offset, length] = res.titleoffs[i]; + + // @todo Figure out the length of the title chunks. + const buffer = arrayBuffer.slice(offset); //, offset + length); + const item = parseTitles(buffer, i, offset); + item.buffer = buffer; + titles.push(item); + } + return { rooms, roomgfx, globdata, preps, scripts, - objects, + titles, }; }; diff --git a/src/lib/parser/parseTitles.js b/src/lib/parser/parseTitles.js new file mode 100644 index 0000000..93e2747 --- /dev/null +++ b/src/lib/parser/parseTitles.js @@ -0,0 +1,57 @@ +import Parser from './parser.js'; +import { hex } from '../utils.js'; + +const assert = console.assert; + +const parseTitles = (arrayBuffer, i = 0, offset = 0) => { + const parser = new Parser(arrayBuffer); + const metadata = { + id: i, + offset, + size: arrayBuffer.byteLength, + // decompressedSize: 0, // Comment out decompressed size until the buffer size is known. + }; + + const unk1 = parser.getUint16(); // Probably resource length unused in titles. + const unk2 = parser.getUint16(); + + assert(unk1 === 0, 'Unknown 1 is not 0.'); + assert(unk2 === 0x0f10, 'Unknown 2 is not 0x0f10.'); + + const numberOfTiles = parser.getUint8() + 1; + + console.log('numberOfTiles', numberOfTiles); + + const gfx = []; + let n = 0; + while (n < numberOfTiles * 16) { + const loop = parser.getUint8(); + if (loop & 0x80) { + for (let j = 0; j < (loop & 0x7f); j++) { + gfx[n++] = parser.getUint8(); + } + } else { + const data = parser.getUint8(); + for (let j = 0; j < (loop & 0x7f); j++) { + gfx[n++] = data; + } + } + } + + assert( + numberOfTiles === gfx.length / 8 / 2, + 'Number of tiles byte does not match number of tiles decoded.', + ); + + // metadata.decompressedSize = gfx.length; + + return { + metadata, + unk1, + unk2, + numberOfTiles, + gfx, + }; +}; + +export default parseTitles; diff --git a/src/lib/resources.js b/src/lib/resources.js index 2ee6b53..b190021 100644 --- a/src/lib/resources.js +++ b/src/lib/resources.js @@ -102,6 +102,7 @@ const usa = { sprdata: [[0x2ce11, 0x2be0], [0x07f6b, 0x008a]], charset: [[0x3f6ee, 0x0090]], preplist: [[0x3fb5a, 0x000e]], + titleoffs: [[0x2701, 0x0000], [0x324d, 0x0000]], characters: {}, version: 'USA', lang: 'en-US', @@ -212,6 +213,7 @@ const eur = { sprdata: [[0x2ce11, 0x2be0], [0x0be28, 0x008a]], charset: [[0x3f724, 0x0090]], preplist: [[0x3fb90, 0x000e]], + titleoffs: [[0x2701, 0x0000], [0x324d, 0x0000]], characters: {}, version: 'Europe', lang: 'en-GB', @@ -317,6 +319,7 @@ const swe = { sprdata: [[0x2c401, 0x2be0], [0x0fe6b, 0x008a]], charset: [[0x3f739, 0x0094]], preplist: [[0x3fba9, 0x000e]], + titleoffs: [[0x02701, 0x0000], [0x0320f, 0x0000]], characters: { '<': 'ä', '[': 'é', '\\': 'å', '>': 'ö', // The 'ù' sign is in the base tileset but @@ -426,6 +429,7 @@ const fra = { sprdata: [[0x2ca28, 0x2be0], [0x07e48, 0x008a]], charset: [[0x3f739, 0x009a]], preplist: [[0x3fbaf, 0x0010]], + titleoffs: [[0x02701, 0x0000], [0x0320f, 0x0000]], characters: { '[': 'é', '<': 'à', '\\': 'è', '>': 'ç', ']': 'ê', '|': 'ô', '{': 'î', '=': 'â', '}': 'ù', '_': 'û', @@ -534,6 +538,7 @@ const ger = { sprdata: [[0x2c8ee, 0x2be0], [0x0fe61, 0x008a]], charset: [[0x3f739, 0x0096]], preplist: [[0x3fbab, 0x000e]], + titleoffs: [[0x02701, 0x0000], [0x0320f, 0x0000]], characters: { '=': 'ß', '\\': 'ä', '{': 'ö', '[': 'ü', '(': '(', ')': ')', // The 'è' sign is in the base tileset but @@ -643,6 +648,7 @@ const esp = { sprdata: [[0x2c401, 0x2be0], [0x0fe67, 0x008a]], charset: [[0x3f739, 0x0099]], preplist: [[0x3fbae, 0x000f]], + titleoffs: [[0x02701, 0x0000], [0x0320f, 0x0000]], characters: { '[': 'á', '<': 'é', '|': 'í', '>': 'ó', ']': 'ú', '{': '¿', '}': '¡', '=': 'ñ', '_': 'ü', @@ -751,6 +757,7 @@ const ita = { sprdata: [[0x2c8c0, 0x2be0], [0x0fe61, 0x008a]], charset: [[0x3f739, 0x0095]], preplist: [[0x3fbaa, 0x0010]], + titleoffs: [[0x02701, 0x0000], [0x0320f, 0x0000]], characters: { '<': 'à', '\\': 'è', '>': 'ì', '|': 'ò', '}': 'ù', }, @@ -863,6 +870,7 @@ const proto = { sprdata: [[0x2ce11, 0x2be0], [0x07f6b, 0x008a]], charset: [[0x3f6ee, 0x0090]], preplist: [[0x3fb5a, 0x000e]], + titleoffs: [[0x2701, 0x0000], [0x325b, 0x0000]], characters: {}, version: 'prototype', lang: 'en-US',