UNPKG

@shockpkg/dir-projector

Version:

Package for creating Shockwave Director projectors

232 lines (217 loc) 5.31 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.machoAppLauncher = machoAppLauncher; exports.machoAppLauncherFat = machoAppLauncherFat; exports.machoAppLauncherThin = machoAppLauncherThin; exports.machoTypesData = machoTypesData; exports.machoTypesFile = machoTypesFile; var _promises = require("node:fs/promises"); var _util = require("../util.js"); const FAT_MAGIC = 0xcafebabe; const MH_MAGIC = 0xfeedface; const MH_CIGAM = 0xcefaedfe; const MH_MAGIC_64 = 0xfeedfacf; const MH_CIGAM_64 = 0xcffaedfe; const CPU_TYPE_POWERPC = 0x00000012; const CPU_TYPE_I386 = 0x00000007; /** * Mach-O type. */ /** * Get types of Mach-O data, array if FAT binary, else a single object. * * @param data Mach-O data. * @returns Mach-O types. */ function machoTypesData(data) { let le = false; const dv = new DataView(data.buffer, data.byteOffset, data.byteLength); /** * Read type at offset. * * @param offset File offset. * @returns Type object. */ const type = offset => ({ cpuType: dv.getUint32(offset, le), cpuSubtype: dv.getUint32(offset + 4, le) }); const magic = dv.getUint32(0, le); switch (magic) { case FAT_MAGIC: { const r = []; const count = dv.getUint32(4, le); let offset = 8; for (let i = 0; i < count; i++) { r.push(type(offset)); offset += 20; } return r; } case MH_MAGIC: case MH_MAGIC_64: { return type(4); } case MH_CIGAM: case MH_CIGAM_64: { le = true; return type(4); } default: { throw new Error(`Unknown header magic: 0x${magic.toString(16)}`); } } } /** * Get types of Mach-O file, array if FAT binary, else a single object. * * @param path Mach-O file. * @returns Mach-O types. */ async function machoTypesFile(path) { let data; const f = await (0, _promises.open)(path, 'r'); try { const m = 8; const h = new Uint8Array(m); const { bytesRead } = await f.read(h, 0, m, 0); if (bytesRead < m) { data = h.subarray(0, bytesRead); } else { const v = new DataView(h.buffer, h.byteOffset, h.byteLength); const n = v.getUint32(0, false) === FAT_MAGIC ? v.getUint32(4, false) * 20 : 4; const d = new Uint8Array(m + n); d.set(h); const { bytesRead } = await f.read(d, m, n, m); data = d.subarray(0, m + bytesRead); } } finally { await f.close(); } return machoTypesData(data); } /** * Get Mach-O app launcher for a single type. * * @param type Mach-O type. * @returns Launcher data. */ async function machoAppLauncherThin(type) { const { cpuType } = type; let id = ''; switch (cpuType) { case CPU_TYPE_POWERPC: { id = 'mac-app-ppc'; break; } case CPU_TYPE_I386: { id = 'mac-app-i386'; break; } default: { throw new Error(`Unknown CPU type: 0x${cpuType.toString(16)}`); } } return (0, _util.launcher)(id); } /** * Get Mach-O app launcher for a type list. * * @param types Mach-O types. * @returns Launcher data. */ async function machoAppLauncherFat(types) { // The lipo utility always uses 12/4096 for ppc, ppc64, i386, and x86_64. const align = 12; // eslint-disable-next-line no-bitwise const alignSize = 1 << align >>> 0; // Create the FAT header. const headD = new Uint8Array(8); const headV = new DataView(headD.buffer, headD.byteOffset, headD.byteLength); headV.setUint32(0, FAT_MAGIC, false); headV.setUint32(4, types.length, false); // The pieces and their total length. const pieces = [headD]; let total = headD.length; /** * Helper to add pieces and update total length. * * @param data Data. */ const add = data => { pieces.push(data); total += data.length; }; /** * Helper to pad pieces. */ const pad = () => { const over = total % alignSize; if (over) { add(new Uint8Array(alignSize - over)); } }; // Create a head and get the body for each type. const parts = []; for (const { type, body } of await Promise.all(types.map(async type => machoAppLauncherThin(type).then(body => ({ type, body }))))) { const headD = new Uint8Array(20); const headV = new DataView(headD.buffer, headD.byteOffset, headD.byteLength); headV.setUint32(0, type.cpuType, false); headV.setUint32(4, type.cpuSubtype, false); headV.setUint32(16, align, false); parts.push({ headV, body }); add(headD); } // Add binaries aligned, updating their headers. for (const { headV, body } of parts) { pad(); headV.setUint32(8, total, false); headV.setUint32(12, body.length, false); add(body); } // Merge all the pieces. const r = new Uint8Array(total); let i = 0; for (const piece of pieces) { r.set(piece, i); i += piece.length; } return r; } /** * Get Mach-O app launcher for a single or multiple types. * * @param types Mach-O types. * @returns Launcher data. */ async function machoAppLauncher(types) { return Array.isArray(types) ? machoAppLauncherFat(types) : machoAppLauncherThin(types); } //# sourceMappingURL=mac.js.map