From 22591ba72de6deb57e5c34e20b184a3795bd9ce8 Mon Sep 17 00:00:00 2001 From: edo999 Date: Sat, 25 May 2024 16:48:14 +0100 Subject: [PATCH] =?UTF-8?q?Generate=20ROM=20hacks=20with=20modified=20pale?= =?UTF-8?q?ttes=20=F0=9F=92=A5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- experiments/relocateNtAttrs.js | 5 +- experiments/updatePreps.js | 3 +- package.json | 4 +- src/App.js | 40 +++--------- src/components/DownloadRom.js | 77 ++++++++++++++++++++++ src/components/Footer.js | 72 ++++++++++----------- src/components/Header.js | 21 +++++- src/components/Layout.js | 20 +++--- src/containers/DropZoneContainer.js | 31 ++++++--- src/containers/ResourceExplorer.js | 7 +- src/containers/RoomsContainer.js | 16 +++-- src/containers/TitlesContainer.js | 16 +++-- src/contexts/RomContext.js | 50 +++++++++++++++ src/index.js | 13 +++- src/lib/cliUtils.js | 69 +------------------- src/lib/parser/parseRom.js | 4 +- src/lib/resources.js | 16 ++--- src/lib/romUtils.js | 99 +++++++++++++++++++++++++++++ src/lib/utils.js | 14 +--- 20 files changed, 381 insertions(+), 199 deletions(-) create mode 100644 src/components/DownloadRom.js create mode 100644 src/contexts/RomContext.js create mode 100644 src/lib/romUtils.js diff --git a/README.md b/README.md index 1b245ac..bf46c81 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # SCUMM NES resource explorer -> An app to explore the content of the Maniac Mansion game on NES. +> An app to explore and modify the content of the Maniac Mansion game on NES. ## What is this? @@ -84,7 +84,6 @@ Then deploy the content of the `dist` folder. - Write more tests. - Parse more resource types (scripts, sounds...). - QoF improvements (store the ROM files locally...) -- Enable modification of the resources to create new games. ### Out of scope for now diff --git a/experiments/relocateNtAttrs.js b/experiments/relocateNtAttrs.js index 70c31c6..940a860 100644 --- a/experiments/relocateNtAttrs.js +++ b/experiments/relocateNtAttrs.js @@ -2,7 +2,8 @@ import { parseArgs } from 'node:util'; import { basename } from 'node:path'; -import { loadRom, saveRom, expandRom, inject } from '../src/lib/cliUtils.js'; +import { loadRom, saveRom, expandRom } from '../src/lib/cliUtils.js'; +import { inject } from '../src/lib/romUtils.js'; import parseRoom from '../src/lib/parser/parseRooms.js'; import { zeroPad, hex } from '../src/lib/utils.js'; @@ -119,7 +120,7 @@ for (let i = 0; i < roomNum; i++) { nametableLength, hex(bankOffset + nametableLength, 4), hex(atOffset + headerLength), - attrsLength + attrsLength, ); bankOffset += nametableLength; bankOffset += attrsLength; diff --git a/experiments/updatePreps.js b/experiments/updatePreps.js index bb51231..ab15bb8 100644 --- a/experiments/updatePreps.js +++ b/experiments/updatePreps.js @@ -2,7 +2,8 @@ import { parseArgs } from 'node:util'; import { basename } from 'node:path'; -import { loadRom, saveRom, inject } from '../src/lib/cliUtils.js'; +import { loadRom, saveRom } from '../src/lib/cliUtils.js'; +import { inject } from '../src/lib/romUtils.js'; import serialisePreps from '../src/lib/serialiser/serialisePreps.js'; /* diff --git a/package.json b/package.json index 4c2d29f..0f4878a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "scumm-nes", - "version": "0.1.2", - "description": "An app to explore the content of the Maniac Mansion game on NES.", + "version": "0.2.0", + "description": "An app to explore and modify the content of the Maniac Mansion game on NES.", "author": "edo999@gmail.com", "license": "BlueOak-1.0.0", "keywords": [ diff --git a/src/App.js b/src/App.js index f671ba9..1bb33b2 100644 --- a/src/App.js +++ b/src/App.js @@ -1,41 +1,19 @@ -import { useState } from 'react'; -import { ErrorBoundary } from 'react-error-boundary'; -import { useNavigate } from 'react-router-dom'; +import { useRom } from './contexts/RomContext'; import Layout from './components/Layout'; import DropZoneContainer from './containers/DropZoneContainer'; import ResourceExplorer from './containers/ResourceExplorer'; -import ErrorMessage from './components/ErrorMessage'; const App = () => { - const [rom, setRom] = useState(null); - const [res, setRes] = useState(null); - const [resources, setResources] = useState(null); - const navigate = useNavigate(); - - const onFile = async (rom, res) => { - const { default: parseRom } = await import('./lib/parser/parseRom'); - setRom(rom); - setRes(res); - setResources(parseRom(rom, res)); - - // Redirect to the first room. - navigate('/rooms/1'); - }; + const { prg, res, resources } = useRom(); return ( - - - {!rom || !res ? ( - - ) : ( - - )} - - + + {!prg || !res || !resources ? ( + + ) : ( + + )} + ); }; diff --git a/src/components/DownloadRom.js b/src/components/DownloadRom.js new file mode 100644 index 0000000..a30eb34 --- /dev/null +++ b/src/components/DownloadRom.js @@ -0,0 +1,77 @@ +import { useRef, useState } from 'react'; +import { useRom } from '../contexts/RomContext'; +import { prependNesHeader, inject, debugArrayBuffer } from '../lib/romUtils'; +import serialisePalette from '../lib/serialiser/serialisePalette'; + +const type = 'application/x-nes-rom'; +const fileName = 'mm-hack.nes'; + +const DownloadRom = ({ children }) => { + const { prg, resources } = useRom(); + const [aHref, setAHref] = useState(null); + const aRef = useRef(null); + + if (prg === null) { + return ( + + ); + } + + // Inject the new content into the PRG and return a NES ROM buffer. + const generateRomHackFile = () => { + const screens = [...resources.titles, ...resources.rooms]; + + // Update screen titles and rooms palette. + for (let i = 0; i < screens.length; i++) { + const screen = screens[i]; + const { offset, size, id } = screen.metadata; + if (!size) { + continue; + } + + const { from, to } = screen.map.find(({ type }) => type === 'palette'); + const buffer = serialisePalette(screen.palette); + + const overwrites = inject(prg, buffer, offset + from, to - from); + if (overwrites) { + console.log('Injecting screen \x1b[33m%i\x1b[0m palette', id); + } + } + + // Add the iNES header to make it playable on emulators. + return prependNesHeader(prg); + }; + + return ( + <> + + {/* eslint-disable-next-line jsx-a11y/anchor-has-content */} + + + ); +}; + +export default DownloadRom; diff --git a/src/components/Footer.js b/src/components/Footer.js index 3e6d738..32135bf 100644 --- a/src/components/Footer.js +++ b/src/components/Footer.js @@ -46,40 +46,40 @@ const navigation = [ }, ]; -export default function Footer() { - return ( - +); + +export default Footer; diff --git a/src/components/Header.js b/src/components/Header.js index 6b25eac..59eb6e1 100644 --- a/src/components/Header.js +++ b/src/components/Header.js @@ -1,5 +1,6 @@ import { Fragment, useState } from 'react'; import { Link } from 'react-router-dom'; +import DownloadRom from './DownloadRom'; import { Dialog, DialogPanel, @@ -7,6 +8,7 @@ import { TransitionChild, } from '@headlessui/react'; import { + ArrowDownTrayIcon, Cog8ToothIcon, Bars3Icon, XMarkIcon, @@ -22,7 +24,7 @@ const navigation = [ { name: 'Settings', href: '/settings', sideBarOnly: true }, ]; -export default function Header() { +const Header = () => { const [mobileMenuOpen, setMobileMenuOpen] = useState(false); return ( @@ -63,7 +65,13 @@ export default function Header() { ))} -
+
+ + + ))} + + + Download modified ROM + +
@@ -139,4 +152,6 @@ export default function Header() { ); -} +}; + +export default Header; diff --git a/src/components/Layout.js b/src/components/Layout.js index c4dd6a5..5a5eb14 100644 --- a/src/components/Layout.js +++ b/src/components/Layout.js @@ -1,14 +1,14 @@ import Header from './Header'; import Footer from './Footer'; -export default function Layout({ children }) { - return ( -
-
-
- {children} -
-