diff --git a/src/dump/index.js b/src/dump/index.js index b1bba3a..42097f8 100644 --- a/src/dump/index.js +++ b/src/dump/index.js @@ -164,7 +164,7 @@ function dumpADSScripts(filepath, resindex) { } } -const filepath = path.join(__dirname, '../../data'); +const filepath = path.join(__dirname, '../../data/castway'); const fc = fs.readFileSync(path.join(filepath, 'RESOURCE.MAP')); const buffer = fc.buffer.slice(fc.byteOffset, fc.byteOffset + fc.byteLength); diff --git a/src/resources/bmp.js b/src/resources/bmp.js index 3c03d75..f837fef 100644 --- a/src/resources/bmp.js +++ b/src/resources/bmp.js @@ -37,38 +37,37 @@ export function loadBMPResourceEntry(entry) { images[i].height = h; } block = getString(entry.data, offset, 3); - if (block !== 'BIN') { - throw `Invalid Type ${block}: expecting block type BIN`; - } - let blockSize = entry.data.getUint32(offset + 4, true); - const compressionType = entry.data.getUint8(offset + 8, true); - /* const uncompressedSize = */ entry.data.getUint32(offset + 9, true); - offset += 13; - blockSize -= 5; // take type and size out of the block - const compressedData = new DataView(entry.buffer.slice(offset, offset + blockSize)); - const data = decompress(compressionType, compressedData, 0, compressedData.byteLength); - let dataIndex = 0; - let pixelIndex = 0; - for (let i = 0; i < numImages; i += 1) { - const image = images[i]; - for (let h = 0; h < image.height; h += 1) { - for (let w = 0; w < image.width; w += 1) { - let c = data[dataIndex]; - if (pixelIndex % 2 === 0) { - c >>= 4; - } else { - c &= 0x0f; - dataIndex += 1; + if (block === 'BIN') { + let blockSize = entry.data.getUint32(offset + 4, true); + const compressionType = entry.data.getUint8(offset + 8, true); + /* const uncompressedSize = */ entry.data.getUint32(offset + 9, true); + offset += 13; + blockSize -= 5; // take type and size out of the block + const compressedData = new DataView(entry.buffer.slice(offset, offset + blockSize)); + const data = decompress(compressionType, compressedData, 0, compressedData.byteLength); + let dataIndex = 0; + let pixelIndex = 0; + for (let i = 0; i < numImages; i += 1) { + const image = images[i]; + for (let h = 0; h < image.height; h += 1) { + for (let w = 0; w < image.width; w += 1) { + let c = data[dataIndex]; + if (pixelIndex % 2 === 0) { + c >>= 4; + } else { + c &= 0x0f; + dataIndex += 1; + } + image.buffer[w + image.width * h] = c; + image.pixels[w + image.width * h] = { + index: c, + a: PALETTE[c].a, + r: PALETTE[c].r, + g: PALETTE[c].g, + b: PALETTE[c].b, + }; + pixelIndex += 1; } - image.buffer[w + image.width * h] = c; - image.pixels[w + image.width * h] = { - index: c, - a: PALETTE[c].a, - r: PALETTE[c].r, - g: PALETTE[c].g, - b: PALETTE[c].b, - }; - pixelIndex += 1; } } } diff --git a/src/resources/data/scripting.js b/src/resources/data/scripting.js index cb211ec..4fbcb09 100644 --- a/src/resources/data/scripting.js +++ b/src/resources/data/scripting.js @@ -1,34 +1,34 @@ export const TTMCommandType = [ - { opcode: 0x0020, command: 'SAVE_BACKGROUND' }, // not used + { opcode: 0x0020, command: 'SAVE_BACKGROUND' }, { opcode: 0x0080, command: 'DRAW_BACKGROUND' }, { opcode: 0x0110, command: 'PURGE' }, { opcode: 0x0FF0, command: 'UPDATE' }, { opcode: 0x1020, command: 'SET_DELAY' }, { opcode: 0x1050, command: 'SLOT_IMAGE' }, { opcode: 0x1060, command: 'SLOT_PALETTE' }, - { opcode: 0x1100, command: 'UNKNOWN_0' }, // Scene related? + { opcode: 0x1100, command: 'UNKNOWN_0' }, { opcode: 0x1110, command: 'SET_SCENE' }, { opcode: 0x1120, command: 'SET_BACKGROUND' }, { opcode: 0x1200, command: 'GOTO' }, { opcode: 0x2000, command: 'SET_COLORS' }, - { opcode: 0x2010, command: 'SET_FRAME1' }, - { opcode: 0x2020, command: 'UNKNOWN_3' }, // SET_FRAME2 ??? + { opcode: 0x2010, command: 'SET_FRAME' }, + { opcode: 0x2020, command: 'SET_TIMER' }, { opcode: 0x4000, command: 'SET_CLIP_REGION' }, { opcode: 0x4110, command: 'FADE_OUT' }, { opcode: 0x4120, command: 'FADE_IN' }, - { opcode: 0x4200, command: 'SAVE_IMAGE0' }, - { opcode: 0x4210, command: 'SAVE_IMAGE1' }, - { opcode: 0xA000, command: 'UNKNOWN_4' }, // Draw Line related? - { opcode: 0xA050, command: 'UNKNOWN_5' }, // Draw Line related? - { opcode: 0xA060, command: 'UNKNOWN_6' }, // Draw Line related? + { opcode: 0x4200, command: 'DRAW_BACKGROUND_REGION' }, + { opcode: 0x4210, command: 'SAVE_IMAGE_REGION' }, + { opcode: 0xA000, command: 'UNKNOWN_4' }, + { opcode: 0xA050, command: 'SAVE_REGION' }, + { opcode: 0xA060, command: 'RESTORE_REGION' }, { opcode: 0xA0A0, command: 'DRAW_LINE' }, { opcode: 0xA100, command: 'DRAW_RECT' }, { opcode: 0xA400, command: 'DRAW_BUBBLE' }, { opcode: 0xA500, command: 'DRAW_SPRITE' }, - { opcode: 0xA510, command: 'DRAW_SPRITE1' }, // not used + { opcode: 0xA510, command: 'DRAW_SPRITE1' }, { opcode: 0xA520, command: 'DRAW_SPRITE_FLIP' }, - { opcode: 0xA530, command: 'DRAW_SPRITE3' }, // not used + { opcode: 0xA530, command: 'DRAW_SPRITE3' }, { opcode: 0xA600, command: 'CLEAR_SCREEN' }, { opcode: 0xB600, command: 'DRAW_SCREEN' }, { opcode: 0xC020, command: 'LOAD_SAMPLE' }, diff --git a/src/resources/data/types.js b/src/resources/data/types.js deleted file mode 100644 index 324aa6d..0000000 --- a/src/resources/data/types.js +++ /dev/null @@ -1,14 +0,0 @@ -import { loadADSResourceEntry } from '../ads'; -import { loadBMPResourceEntry } from '../bmp'; -import { loadPALResourceEntry } from '../pal'; -import { loadSCRResourceEntry } from '../scr'; -import { loadTTMResourceEntry } from '../ttm'; - - -export const ResourceType = [ - { type: 'ADS', callback: loadADSResourceEntry }, // Animation sequences - { type: 'BMP', callback: loadBMPResourceEntry }, // Various raw images - { type: 'PAL', callback: loadPALResourceEntry }, // VGA palette - { type: 'SCR', callback: loadSCRResourceEntry }, // Background raw images - { type: 'TTM', callback: loadTTMResourceEntry }, // Scripting macros -]; diff --git a/src/resources/index.js b/src/resources/index.js index dcc6c99..2f31ffa 100644 --- a/src/resources/index.js +++ b/src/resources/index.js @@ -1,7 +1,38 @@ import { INDEX_STRING_SIZE } from '../constants'; import { getString } from '../utils/string'; -import { ResourceType } from './data/types'; +import { loadADSResourceEntry } from './ads'; +import { loadBMPResourceEntry } from './bmp'; +import { loadPALResourceEntry } from './pal'; +import { loadSCRResourceEntry } from './scr'; +import { loadTTMResourceEntry } from './ttm'; + +const NOP = () => {}; + +export const ResourceType = [ + { type: 'ADS', callback: loadADSResourceEntry }, // Animation sequences + { type: 'BMP', callback: loadBMPResourceEntry }, // Various raw images + { type: 'PAL', callback: loadPALResourceEntry }, // VGA palette + { type: 'SCR', callback: loadSCRResourceEntry }, // Background raw images + { type: 'TTM', callback: loadTTMResourceEntry }, // Scripting macros + { type: 'VIN', callback: NOP }, // + { type: 'SDS', callback: NOP }, // + { type: 'FNT', callback: NOP }, // + { type: 'DAT', callback: NOP }, // + { type: 'DDS', callback: NOP }, // + { type: 'TDS', callback: NOP }, // + { type: 'REQ', callback: NOP }, // + { type: 'WLD', callback: NOP }, // + { type: 'SNG', callback: NOP }, // + { type: 'ADL', callback: NOP }, // + { type: 'ADH', callback: NOP }, // + { type: 'RST', callback: NOP }, // + { type: 'OVL', callback: NOP }, // + { type: 'GDS', callback: NOP }, // + { type: 'RST', callback: NOP }, // + { type: 'SX', callback: NOP }, // + { type: 'RES', callback: NOP }, // +]; const INDEX_HEADER_SIZE = 6; @@ -10,7 +41,7 @@ const INDEX_HEADER_SIZE = 6; * @param {*} filepath Full path of the file * @param {*} filename File name */ -export const loadResources = (buffer) => { +export const loadResourceMap = (buffer) => { let offset = 0; // current resource offest const resources = []; // list of resource files const data = new DataView(buffer, offset, buffer.byteLength); @@ -60,75 +91,103 @@ export const loadResources = (buffer) => { }; }; -export function loadResourceEntry(entry) { +/** + * Load all Resource details based on index resource file + * @param {*} filepath Full path of the file + * @param {*} filename File name + */ +export const loadResourcebyName = (resource, resbuffer) => { + const res = { ...resource }; + res.size = resbuffer.byteLength; + const resData = new DataView(resbuffer, 0, res.size); + + for (let e = 0; e < res.numEntries; e += 1) { + const entry = res.entries[e]; + const name = getString(resData, entry.offset, INDEX_STRING_SIZE); + const entryCompressedSize = resData.getUint32(entry.offset + 13, true); + const startOffset = entry.offset + 17; + const endOffset = startOffset + entryCompressedSize; + + entry.name = name; + entry.type = name.split('.')[1]; + entry.compressedSize = entryCompressedSize; + entry.buffer = resbuffer.slice(startOffset, endOffset); + entry.data = new DataView(resbuffer, startOffset, entryCompressedSize); + + res.entries.push({ ...entry }); + } + + return res; +}; + +export const loadResourceEntry = (entry) => { const resType = ResourceType.find((r) => r.type === entry.type); return resType.callback(entry); -} +}; + +/** + * Load all Resource details based on index resource file + * @param {*} filepath Full path of the file + * @param {*} filename File name + */ +export function loadResources(buffer, resbuffer) { + let offset = 0; // current resource offest + const resources = []; // list of resource files + const data = new DataView(buffer, offset, buffer.byteLength); + + const header = { + unk0: data.getUint8(offset, true), + unk1: data.getUint8(offset + 1, true), // number of entries? + unk2: data.getUint8(offset + 2, true), + unk3: data.getUint8(offset + 3, true), + numResources: data.getUint8(offset + 4, true), + unk5: data.getUint8(offset + 5, true), + }; + offset += INDEX_HEADER_SIZE; + + // Read resource files and entries + // Read number of resource files (castaway only uses a single one) + for (let r = 0; r < header.numResources; r += 1) { + let innerOffset = offset; + const res = { + name: getString(data, innerOffset, INDEX_STRING_SIZE), + numEntries: data.getUint16(innerOffset + 13, true), + size: 0, + entries: [], + }; + resources.push(res); + innerOffset += 15; + res.size = resbuffer.byteLength; + const resData = new DataView(resbuffer, 0, res.size); + + for (let e = 0; e < res.numEntries; e += 1) { + // from index + const entrySize = data.getUint16(innerOffset, true); // uncompressed size + const entryOffset = data.getUint32(innerOffset + 4, true); + // from resource + const name = getString(resData, entryOffset, INDEX_STRING_SIZE); + const entryCompressedSize = resData.getUint32(entryOffset + 13, true); + const startOffset = entryOffset + 17; + const endOffset = startOffset + entryCompressedSize; -// /** -// * Load all Resource details based on index resource file -// * @param {*} filepath Full path of the file -// * @param {*} filename File name -// */ -// export function loadResources(buffer, resbuffer) { -// let offset = 0; // current resource offest -// const resources = []; // list of resource files -// const data = new DataView(buffer, offset, buffer.byteLength); - -// const header = { -// unk0: data.getUint8(offset, true), -// unk1: data.getUint8(offset + 1, true), // number of entries? -// unk2: data.getUint8(offset + 2, true), -// unk3: data.getUint8(offset + 3, true), -// numResources: data.getUint8(offset + 4, true), -// unk5: data.getUint8(offset + 5, true), -// }; -// offset += INDEX_HEADER_SIZE; - -// // Read resource files and entries -// // Read number of resource files (castaway only uses a single one) -// for (let r = 0; r < header.numResources; r += 1) { -// let innerOffset = offset; -// const res = { -// name: getString(data, innerOffset, INDEX_STRING_SIZE), -// numEntries: data.getUint16(innerOffset + 13, true), -// size: 0, -// entries: [], -// }; -// resources.push(res); -// innerOffset += 15; - -// res.size = resbuffer.byteLength; -// const resData = new DataView(resbuffer, 0, res.size); - -// for (let e = 0; e < res.numEntries; e += 1) { -// // from index -// const entrySize = data.getUint16(innerOffset, true); // uncompressed size -// const entryOffset = data.getUint32(innerOffset + 4, true); -// // from resource -// const name = getString(resData, entryOffset, INDEX_STRING_SIZE); -// const entryCompressedSize = resData.getUint32(entryOffset + 13, true); -// const startOffset = entryOffset + 17; -// const endOffset = startOffset + entryCompressedSize; - -// const entry = { -// name, -// type: name.split('.')[1], -// size: entrySize, // uncompressed size -// offset: entryOffset, -// compressedSize: entryCompressedSize, -// buffer: resbuffer.slice(startOffset, endOffset), -// data: new DataView(resbuffer, startOffset, entryCompressedSize), -// }; -// innerOffset += 8; - -// res.entries.push(entry); -// } -// } - -// return { -// header, -// resources -// }; -// } + const entry = { + name, + type: name.split('.')[1], + size: entrySize, // uncompressed size + offset: entryOffset, + compressedSize: entryCompressedSize, + buffer: resbuffer.slice(startOffset, endOffset), + data: new DataView(resbuffer, startOffset, entryCompressedSize), + }; + innerOffset += 8; + + res.entries.push(entry); + } + } + + return { + header, + resources + }; +} diff --git a/src/resources/scr.js b/src/resources/scr.js index 1530fa6..2cc8540 100644 --- a/src/resources/scr.js +++ b/src/resources/scr.js @@ -14,67 +14,67 @@ export function loadSCRResourceEntry(entry) { offset += 8; let block = getString(entry.data, offset, 3); - if (block !== 'DIM') { - throw `Invalid Type ${block}: expecting block type DIM`; - } - let blockSize = entry.data.getUint32(offset + 4, true); - const width = entry.data.getUint16(offset + 8, true); - const height = entry.data.getUint16(offset + 10, true); - offset += 12; + if (block === 'DIM') { + let blockSize = entry.data.getUint32(offset + 4, true); + const width = entry.data.getUint16(offset + 8, true); + const height = entry.data.getUint16(offset + 10, true); + offset += 12; - block = getString(entry.data, offset, 3); - if (block !== 'BIN') { - throw `Invalid Type ${block}: expecting block type BIN`; - } - blockSize = entry.data.getUint32(offset + 4, true); - const compressionType = entry.data.getUint8(offset + 8, true); - /* const uncompressedSize = */ entry.data.getUint32(offset + 9, true); - offset += 13; - blockSize -= 5; // take type and size out of the block + block = getString(entry.data, offset, 3); + if (block !== 'BIN') { + throw `Invalid Type ${block}: expecting block type BIN`; + } + blockSize = entry.data.getUint32(offset + 4, true); + const compressionType = entry.data.getUint8(offset + 8, true); + /* const uncompressedSize = */ entry.data.getUint32(offset + 9, true); + offset += 13; + blockSize -= 5; // take type and size out of the block - const compressedData = new DataView(entry.buffer.slice(offset, offset + blockSize)); - const data = decompress(compressionType, compressedData, 0, compressedData.byteLength); + const compressedData = new DataView(entry.buffer.slice(offset, offset + blockSize)); + const data = decompress(compressionType, compressedData, 0, compressedData.byteLength); - const numImages = 1; - const images = [{ - width, - height, - buffer: [], - pixels: [] - }]; - const image = images[0]; - let dataIndex = 0; - let pixelIndex = 0; - for (let h = 0; h < height; h += 1) { - for (let w = 0; w < width; w += 1) { - let c = data[dataIndex]; - if (pixelIndex % 2 === 0) { - c >>= 4; - } else { - c &= 0x0f; - dataIndex += 1; + const numImages = 1; + const images = [{ + width, + height, + buffer: [], + pixels: [] + }]; + const image = images[0]; + let dataIndex = 0; + let pixelIndex = 0; + for (let h = 0; h < height; h += 1) { + for (let w = 0; w < width; w += 1) { + let c = data[dataIndex]; + if (pixelIndex % 2 === 0) { + c >>= 4; + } else { + c &= 0x0f; + dataIndex += 1; + } + const pal = PALETTE[c]; + image.buffer[w + image.width * h] = c; + image.pixels[w + image.width * h] = { + index: c, + a: pal.a, + r: pal.r, + g: pal.g, + b: pal.b, + }; + pixelIndex += 1; } - const pal = PALETTE[c]; - image.buffer[w + image.width * h] = c; - image.pixels[w + image.width * h] = { - index: c, - a: pal.a, - r: pal.r, - g: pal.g, - b: pal.b, - }; - pixelIndex += 1; } + return { + name: entry.name, + type, + totalSize, + flags, + width, + height, + numImages, + images, + }; } - return { - name: entry.name, - type, - totalSize, - flags, - width, - height, - numImages, - images, - }; + return null; } diff --git a/src/ui/ViewerApp.jsx b/src/ui/ViewerApp.jsx index 6e4d659..c1e6f25 100644 --- a/src/ui/ViewerApp.jsx +++ b/src/ui/ViewerApp.jsx @@ -7,7 +7,7 @@ import ResourceList from './components/ResourceList'; import ResourceContent from './components/ResourceContent'; import { preloadFileAsync } from '../utils/preload'; -import { loadResources } from '../resources'; +import { loadResourceMap } from '../resources'; const GameResources = { castaway: 'RESOURCE.MAP', @@ -24,7 +24,7 @@ const ViewerApp = ({ game }) => { async.auto({ resindex: preloadFileAsync(`data/${game}/${GameResources[game]}`), }, (err, files) => { - setResindex(loadResources(files.resindex)); + setResindex(loadResourceMap(files.resindex)); }); return () => {}; }, [game]); @@ -32,7 +32,7 @@ const ViewerApp = ({ game }) => { return (