UNPKG

@shockpkg/dir-projector

Version:

Package for creating Shockwave Director projectors

550 lines (451 loc) 10.7 kB
import { join as pathJoin } from 'path'; import { ArchiveDir, ArchiveHdi, createArchiveByFileExtension } from "@shockpkg/archive-files/module.mjs"; import fse from 'fs-extra'; import { defaultFalse, defaultNull, pathRelativeBase, trimExtension } from "./util.mjs"; /** * Projector constructor. * * @param options Options object. */ export class Projector extends Object { /** * Skeleton file or directory. * * @default null */ /** * Movie file. * * @default null */ /** * Movie data. * * @default null */ /** * Movie name. * * @default null */ /** * Movie data. * * @default false */ /** * Config file. * * @default null */ /** * Config data. * * @default null */ /** * Lingo file. * * @default null */ /** * Lingo data. * * @default null */ /** * Splash image file. * * @default null */ /** * Splash image data. * * @default null */ /** * Xtras include map. * * @default null */ /** * Nest xtras in a Configuration directory. * * @default false */ /** * Path to hdiutil binary. * * @default null */ constructor(options = {}) { super(); this.skeleton = void 0; this.movieFile = void 0; this.movieData = void 0; this.movieName = void 0; this.shockwave = void 0; this.configFile = void 0; this.configData = void 0; this.lingoFile = void 0; this.lingoData = void 0; this.splashImageFile = void 0; this.splashImageData = void 0; this.includeXtras = void 0; this.nestXtrasConfiguration = void 0; this.pathToHdiutil = void 0; this.skeleton = defaultNull(options.skeleton); this.movieFile = defaultNull(options.movieFile); this.movieData = defaultNull(options.movieData); this.movieName = defaultNull(options.movieName); this.shockwave = defaultFalse(options.shockwave); this.configFile = defaultNull(options.configFile); this.configData = defaultNull(options.configData); this.lingoFile = defaultNull(options.lingoFile); this.lingoData = defaultNull(options.lingoData); this.splashImageFile = defaultNull(options.splashImageFile); this.splashImageData = defaultNull(options.splashImageData); this.includeXtras = defaultNull(options.includeXtras); this.nestXtrasConfiguration = defaultFalse(options.nestXtrasConfiguration); this.pathToHdiutil = defaultNull(options.pathToHdiutil); } /** * Config file extension. * * @returns File extension. */ get configExtension() { return '.INI'; } /** * Config file encoding. * * @returns File encoding. */ get configEncoding() { return 'ascii'; } /** * Lingo file name. * * @returns File name. */ get lingoName() { return 'LINGO.INI'; } /** * Lingo file encoding. * * @returns File encoding. */ get lingoEncoding() { return 'ascii'; } /** * Xtras directory name. * * @returns Directory encoding. */ get xtrasDirectoryName() { return 'xtras'; } /** * Configuration directory name. * * @returns Directory encoding. */ get configurationDirectoryName() { return 'Configuration'; } /** * Get movie data if any specified, from data or file. * * @returns Movie data or null. */ async getMovieData() { return this._dataFromBufferOrFile(this.movieData, this.movieFile); } /** * Get config data if any specified, from data or file. * * @returns Config data or null. */ async getConfigData() { return this._dataFromValueOrFile(this.configData, this.configFile, this.configNewline, this.configEncoding); } /** * Get lingo data if any specified, from data or file. * * @returns Lingo data or null. */ async getLingoData() { return this._dataFromValueOrFile(this.lingoData, this.lingoFile, this.lingoNewline, this.lingoEncoding); } /** * Get splash image data if any specified, from data or file. * * @returns Splash image data or null. */ async getSplashImageData() { return this._dataFromBufferOrFile(this.splashImageData, this.splashImageFile); } /** * Get the name of a projector trimming the extension, case insensitive. * * @param name Projector name. * @returns Projector name without extension. */ getProjectorNameNoExtension(name) { return trimExtension(name, this.projectorExtension, true); } /** * Get the Xtras path. * * @param name Save name. * @returns Xtras path. */ getXtrasPath(name) { const cdn = this.configurationDirectoryName; return this.nestXtrasConfiguration && cdn ? `${cdn}/${this.xtrasDirectoryName}` : this.xtrasDirectoryName; } /** * Get the config path. * * @param name Save name. * @returns Config path. */ getConfigPath(name) { const n = this.getProjectorNameNoExtension(name); return `${n}${this.configExtension}`; } /** * Get the splash image path. * * @param name Save name. * @returns Config path. */ getSplashImagePath(name) { const n = this.getProjectorNameNoExtension(name); return `${n}${this.splashImageExtension}`; } /** * Get the skeleton file or directory as an Archive instance. * * @returns Archive instance. */ async getSkeletonArchive() { const { skeleton } = this; if (!skeleton) { throw new Error('Projector skeleton not specified'); } const stat = await fse.stat(skeleton); if (stat.isDirectory()) { return new ArchiveDir(skeleton); } if (!stat.isFile()) { throw new Error('Projector skeleton not file or directory'); } const r = createArchiveByFileExtension(skeleton); if (!r) { throw new Error('Projector skeleton archive file type unknown'); } if (r instanceof ArchiveHdi) { const { pathToHdiutil } = this; if (pathToHdiutil) { r.mounterMac.hdiutil = pathToHdiutil; } r.nobrowse = true; } return r; } /** * Get include Xtras as a list of mappings. * * @returns Mappings list. */ getIncludeXtrasMappings() { const { includeXtras } = this; const r = []; if (includeXtras) { for (const src of Object.keys(includeXtras)) { const dest = includeXtras[src]; r.push({ src, dest }); } } return r; } /** * Find the best match for a path in a list of Xtras mappings. * Path search is case-insensitive. * * @param mappings Mappings list. * @param path Path to search for. * @returns Best match or null. */ findIncludeXtrasMappingsBestMatch(mappings, path) { let best = null; let bestScore = -1; for (const map of mappings) { const { src } = map; const relative = src === '' ? path : pathRelativeBase(path, src, true); if (relative === null || bestScore >= src.length) { continue; } best = { map, relative }; bestScore = src.length; } return best; } /** * Find output path for an Xtra. * * @param mappings Mappings list. * @param path Path to search for. * @returns Output path or null. */ includeXtrasMappingsDest(mappings, path) { const best = this.findIncludeXtrasMappingsBestMatch(mappings, path); if (!best) { return null; } const { map, relative } = best; const base = map.dest || map.src; // eslint-disable-next-line no-nested-ternary return base ? relative ? `${base}/${relative}` : base : relative; } /** * Write out the projector. * * @param path Save path. * @param name Save name. */ async write(path, name) { await this._writeSkeleton(path, name); await this._writeMovie(path, name); await this._writeConfig(path, name); await this._writeSplashImage(path, name); await this._writeLingo(path, name); } /** * Write out the projector movie file. * * @param path Save path. * @param name Save name. */ async _writeMovie(path, name) { const data = await this.getMovieData(); if (!data) { return; } const { movieName } = this; if (!movieName) { throw new Error('Cannot write movie data without a movie name'); } await fse.writeFile(pathJoin(path, movieName), data); } /** * Write out the projector config file. * * @param path Save path. * @param name Save name. */ async _writeConfig(path, name) { await this._maybeWriteFile((await this.getConfigData()), pathJoin(path, this.getConfigPath(name))); } /** * Write out the projector splash image file. * * @param path Save path. * @param name Save name. */ async _writeSplashImage(path, name) { await this._maybeWriteFile((await this.getSplashImageData()), pathJoin(path, this.getSplashImagePath(name))); } /** * Write out the projector lingo file. * * @param path Save path. * @param name Save name. */ async _writeLingo(path, name) { await this._maybeWriteFile((await this.getLingoData()), pathJoin(path, this.lingoName)); } /** * Get data from buffer or file. * * @param data Data buffer. * @param file File path. * @returns Data buffer. */ async _dataFromBufferOrFile(data, file) { if (data) { return data; } if (file) { return fse.readFile(file); } return null; } /** * Get data from value or file. * * @param data Data value. * @param file File path. * @param newline Newline string. * @param encoding String encoding. * @returns Data buffer. */ async _dataFromValueOrFile(data, file, newline, encoding) { let str = null; if (typeof data === 'string') { str = data; } else if (Array.isArray(data)) { if (newline === null) { throw new Error('New line delimiter required'); } str = data.join(newline); } else { return this._dataFromBufferOrFile(data, file); } if (!encoding) { throw new Error('String data encoding required'); } return Buffer.from(str, encoding); } /** * Maybe write file if data is not null. * * @param data Data to maybe write. * @param path Output path. */ async _maybeWriteFile(data, path) { if (!data) { return; } await fse.writeFile(path, data); } /** * Splash image file extension. * * @returns File extension. */ } //# sourceMappingURL=projector.mjs.map