UNPKG

@shockpkg/dir-projector

Version:

Package for creating Shockwave Director projectors

335 lines (270 loc) 7.36 kB
import { basename as pathBasename, join as pathJoin } from 'path'; import { fsWalk } from "@shockpkg/archive-files/module.mjs"; import { patchWindowsS3dInstalledDisplayDriversSize } from "../patcher.mjs"; import { Projector } from "../projector.mjs"; import { defaultFalse, defaultNull, entryIsEmptyResourceFork, pathRelativeBase, pathRelativeBaseMatch, rcedit } from "../util.mjs"; /** * ProjectorWindows constructor. * * @param options Options object. */ export class ProjectorWindows extends Projector { /** * Patch the Shockave 3D Xtra to have a larger buffer to avoid a crash. * The buffer for resolving InstalledDisplayDrivers to a path is small. * Changes to the values stored in InstalledDisplayDrivers cause issues. * The value is now supposed to hold full paths on modern Windows. * In particular, Nvidia drivers which do this need this patch. * * @default false */ /** * Icon file, requires Windows or Wine. * * @default null */ /** * Version strings, requires Windows or Wine. * * @default null */ /** * Product version, requires Windows or Wine. * * @default null */ /** * Version strings, requires Windows or Wine. * * @default null */ constructor(options) { super(options); this.patchShockwave3dInstalledDisplayDriversSize = void 0; this.iconFile = void 0; this.fileVersion = void 0; this.productVersion = void 0; this.versionStrings = void 0; this.iconFile = defaultNull(options.iconFile); this.fileVersion = defaultNull(options.fileVersion); this.productVersion = defaultNull(options.productVersion); this.versionStrings = defaultNull(options.versionStrings); this.patchShockwave3dInstalledDisplayDriversSize = defaultFalse(options.patchShockwave3dInstalledDisplayDriversSize); } /** * Projector file extension. * * @returns File extension. */ get projectorExtension() { return '.exe'; } /** * Config file newline characters. * * @returns Newline characters. */ get configNewline() { return '\r\n'; } /** * Config file newline characters. * * @returns Newline characters. */ get lingoNewline() { return '\r\n'; } /** * Newline characters. * * @returns Newline characters. */ get newline() { return '\r\n'; } /** * Splash image file extension. * * @returns File extension. */ get splashImageExtension() { return '.BMP'; } /** * Get the SKL name. * * @returns File name. */ get sklName() { return 'Projec32.skl'; } /** * Write out the projector. * * @param path Save path. * @param name Save name. */ async write(path, name) { await super.write(path, name); await this._patch(path, name); await this._updateResources(path, name); } /** * Write the projector skeleton from archive. * * @param path Save path. * @param name Save name. */ async _writeSkeleton(path, name) { const { shockwave, sklName, xtrasDirectoryName } = this; const xtrasPath = this.getXtrasPath(name); const xtrasMappings = this.getIncludeXtrasMappings(); let foundProjectorSkl = false; let foundXtras = false; const xtrasHandler = async entry => { // Check if Xtras path. const xtrasRel = pathRelativeBase(entry.volumePath, xtrasDirectoryName, true); if (xtrasRel === null) { return false; } foundXtras = true; // Find output path if being included, else skip. const dest = this.includeXtrasMappingsDest(xtrasMappings, xtrasRel); if (!dest) { return true; } await entry.extract(pathJoin(path, xtrasPath, dest)); return true; }; const projectorSklHandler = async entry => { const entryPath = entry.volumePath; // Should not be in sub directory. if (entryPath.includes('/')) { return false; } // Check if skl path. if (!pathRelativeBaseMatch(entryPath, sklName, true)) { return false; } foundProjectorSkl = true; await entry.extract(pathJoin(path, name)); return true; }; const projectorDllHandler = async entry => { const entryPath = entry.volumePath; // Should not be in sub directory. if (entryPath.includes('/')) { return false; } // Check if dll path. if (!/\.dll$/i.test(entryPath)) { return false; } // Exclude if shockwave projector. if (shockwave) { return true; } await entry.extract(pathJoin(path, entryPath)); return true; }; const archive = await this.getSkeletonArchive(); await archive.read(async entry => { // Skip empty resource forks (every file in DMG). if (entryIsEmptyResourceFork(entry)) { return; } if (await xtrasHandler(entry)) { return; } if (await projectorSklHandler(entry)) { return; } if (await projectorDllHandler(entry)) { return; } }); if (!foundProjectorSkl) { throw new Error(`Failed to locate: ${sklName}`); } if (!foundXtras) { throw new Error(`Failed to locate: ${xtrasDirectoryName}`); } } /** * Patch projector. * * @param path Save path. * @param name Save name. */ async _patch(path, name) { await this._patchShockwave3dInstalledDisplayDriversSize(path, name); } /** * Patch projector, Shockwave 3D InstalledDisplayDrivers size. * * @param path Save path. * @param name Save name. */ async _patchShockwave3dInstalledDisplayDriversSize(path, name) { if (!this.patchShockwave3dInstalledDisplayDriversSize) { return; } const xtrasDir = pathJoin(path, this.getXtrasPath(name)); const search = 'Shockwave 3D Asset.x32'; const searchLower = search.toLowerCase(); let found = false; await fsWalk(xtrasDir, async (path, stat) => { if (!stat.isFile()) { return; } const fn = pathBasename(path); if (fn.toLowerCase() !== searchLower) { return; } found = true; await patchWindowsS3dInstalledDisplayDriversSize(pathJoin(xtrasDir, path)); }, { ignoreUnreadableDirectories: true }); if (!found) { throw new Error(`Failed to locate for patching: ${search}`); } } /** * Update projector Windows resources. * * @param path Save path. * @param name Save name. */ async _updateResources(path, name) { const { iconFile, fileVersion, productVersion, versionStrings } = this; const options = {}; let optionsSet = false; if (iconFile) { options.iconPath = iconFile; optionsSet = true; } if (fileVersion !== null) { options.fileVersion = fileVersion; optionsSet = true; } if (productVersion !== null) { options.productVersion = productVersion; optionsSet = true; } if (versionStrings !== null) { options.versionStrings = versionStrings; optionsSet = true; } // Do not update if no changes are specified. if (!optionsSet) { return; } const file = pathJoin(path, name); await rcedit(file, options); } } //# sourceMappingURL=windows.mjs.map