UNPKG

unreal.js

Version:

A pak reader for games like VALORANT & Fortnite written in Node.JS

488 lines (487 loc) 16.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EExportCommandType = exports.FExportBundleEntry = exports.FExportBundleHeader = exports.FExportMapEntry = exports.FPackageSummary = exports.FPackageObjectIndex_EType = exports.FZenPackageSummary = exports.FZenPackageVersioningInfo = exports.FPackageObjectIndex = exports.FMappedName_EType = exports.FMappedName = exports.FPackageImportReference = void 0; const FArchive_1 = require("../reader/FArchive"); const CityHash_1 = require("../../util/CityHash"); const long_1 = __importDefault(require("long")); const Game_1 = require("../versions/Game"); const ObjectVersion_1 = require("../versions/ObjectVersion"); const CustomVersion_1 = require("../objects/core/serialization/CustomVersion"); class FPackageImportReference { constructor(importedPackageIndex, importedPublicExportHashIndex) { this.importedPackageIndex = importedPackageIndex; this.importedPublicExportHashIndex = importedPublicExportHashIndex; } } exports.FPackageImportReference = FPackageImportReference; /** * FMappedName */ class FMappedName { /** * Creates an instance using an UE4 Reader * @param {FArchive} Ar UE4 Reader to use * @constructor * @public */ constructor(Ar) { /** * Index * @type {number} * @public */ this.index = FMappedName.INVALID_INDEX; /** * Num * @type {number} * @public */ this.num = FMappedName.INVALID_INDEX; if (Ar) { this.index = Ar.readUInt32(); this.num = Ar.readUInt32(); } } /** * Creates an instance * @param {number} index Index to use * @param {number} num Number to use * @param {FMappedName_EType} type Type of FMappedName * @returns {FMappedName} Instance * @public * @static */ static create(index, num, type) { if (index > 2147483647) throw new Error("Bad name index"); const mappedName = new FMappedName(); mappedName.index = (type << FMappedName.TYPE_SHIFT) | index; mappedName.num = num; return mappedName; } /** * Creates an instance from FMinimalName * @param {FMinimalName} minimalName Minimal name to use * @returns {FMappedName} * @public */ static fromMinimalName(minimalName) { const mappedName = new FMappedName(); mappedName.index = minimalName.index.value; mappedName.num = minimalName.num; return mappedName; } /** * Whether is resolved to minimal name * @param {FMinimalName} minimalName Minimal name to check * @returns {boolean} * @public */ static isResolvedToMinimalName(minimalName) { const mappedName = this.fromMinimalName(minimalName); return mappedName.isValid(); } /** * Whether is valid * @returns {boolean} Result * @public */ isValid() { return this.index !== FMappedName.INVALID_INDEX && this.num !== FMappedName.INVALID_INDEX; } /** * Gets type * @returns {number} Type * @public */ getType() { return (this.index & FMappedName.TYPE_MASK) >>> FMappedName.TYPE_SHIFT; } /** * Whether is global * @returns {boolean} Result * @public */ isGlobal() { return ((this.index & FMappedName.TYPE_MASK) >> FMappedName.TYPE_SHIFT) !== 0; } /** * Gets index * @returns {number} Index * @public */ getIndex() { return this.index & FMappedName.INDEX_MASK; } /** * Whether equals another object * @param {?any} other Object to check * @returns {boolean} * @public */ equals(other) { if (this === other) return true; if (!(other instanceof FMappedName)) return false; other; if (this.index !== other.index) return false; return this.num === other.num; } } exports.FMappedName = FMappedName; FMappedName.INVALID_INDEX = ~0; FMappedName.INDEX_BITS = 30; FMappedName.INDEX_MASK = (1 << FMappedName.INDEX_BITS) - 1; FMappedName.TYPE_MASK = ~FMappedName.INDEX_MASK; FMappedName.TYPE_SHIFT = FMappedName.INDEX_BITS; /** * FMappedName_EType * @enum */ var FMappedName_EType; (function (FMappedName_EType) { FMappedName_EType[FMappedName_EType["Package"] = 0] = "Package"; FMappedName_EType[FMappedName_EType["Container"] = 1] = "Container"; FMappedName_EType[FMappedName_EType["Global"] = 2] = "Global"; })(FMappedName_EType = exports.FMappedName_EType || (exports.FMappedName_EType = {})); /** * FPackageObjectIndex */ class FPackageObjectIndex { /** DO NOT USE THIS CONSTRUCTOR, THIS IS FOR THE LIBRARY */ constructor(x, y) { /** * typeAndId * @type {Long} * @private */ this.typeAndId = FPackageObjectIndex.INVALID; if (x) { if (x instanceof FArchive_1.FArchive) { this.typeAndId = long_1.default.fromString(x.readUInt64().toString(), true); } else { this.typeAndId = long_1.default.fromNumber(x, true).shl(FPackageObjectIndex.TYPE_SHIFT).or(y); } } } /** * Generates import hash from object path * @param {string} objectPath Object path to hash * @returns {Long} Hash * @public * @static */ static generateImportHashFromObjectPath(objectPath) { const fullImportPath = objectPath.split(""); fullImportPath.forEach((c, i) => { if (c === "." || c === ":") { fullImportPath[i] = "/"; } else { fullImportPath[i] = c.toLowerCase(); } }); const data = Buffer.from(fullImportPath.join(""), "utf16le"); let hash = CityHash_1.CityHash.cityHash64(data, 0, data.length).toUnsigned(); hash = hash.and(long_1.default.fromNumber(3).shl(62).not()); return hash; } /** * Creates instance from export index * @param {number} index Export index * @returns {FPackageObjectIndex} Instance * @public */ static fromExportIndex(index) { return new FPackageObjectIndex(FPackageObjectIndex_EType.Export, long_1.default.fromNumber(index, true)); } /** * Creates instance from script path * @param {string} scriptObjectPath Script object path * @returns {FPackageObjectIndex} Instance * @public */ static fromScriptPath(scriptObjectPath) { return new FPackageObjectIndex(FPackageObjectIndex_EType.ScriptImport, this.generateImportHashFromObjectPath(scriptObjectPath)); } /** * Creates instance from package path * @param {string} packageObjectPath Package objectPath path * @returns {FPackageObjectIndex} Instance * @public */ static fromPackagePath(packageObjectPath) { return new FPackageObjectIndex(FPackageObjectIndex_EType.PackageImport, this.generateImportHashFromObjectPath(packageObjectPath)); } /** * Whether is null * @returns {boolean} Result * @public */ isNull() { return this.typeAndId.equals(FPackageObjectIndex.INVALID); } /** * Whether is export * @returns {boolean} Result * @public */ isExport() { return this.typeAndId .shru(FPackageObjectIndex.TYPE_SHIFT) .toInt() === FPackageObjectIndex_EType.Export; } /** * Whether is import * @returns {boolean} Result * @public */ isImport() { return this.isScriptImport() || this.isPackageImport(); } /** * Whether is script import * @returns {boolean} Result * @public */ isScriptImport() { return this.typeAndId .shru(FPackageObjectIndex.TYPE_SHIFT) .toInt() === FPackageObjectIndex_EType.ScriptImport; } /** * Whether is package import * @returns {boolean} Result * @public */ isPackageImport() { return this.typeAndId .shru(FPackageObjectIndex.TYPE_SHIFT) .toInt() === FPackageObjectIndex_EType.PackageImport; } /** * Returns export value * @returns {number} Export * @public */ toExport() { if (!this.isExport()) throw new Error("Cannot cast an import to export."); return this.typeAndId.toUnsigned(); } /** * Turns object into package import reference * @returns {FPackageImportReference} Object * @public */ toPackageImportRef() { const importedPackageIndex = this.typeAndId .and(FPackageObjectIndex.INDEX_MASK) .shr(32) .toUnsigned().toInt(); const exportHash = this.typeAndId.toUnsigned().toInt(); return new FPackageImportReference(importedPackageIndex, exportHash); } /** * Returns type * @returns {number} type * @public */ type() { return this.typeAndId.shr(FPackageObjectIndex.TYPE_SHIFT).toInt(); // custom } /** * Returns value * @returns {number} value * @public */ value() { return this.typeAndId.and(FPackageObjectIndex.INDEX_MASK).toNumber(); } /** * Whether equals other object * @param {?any} other Object to check * @returns {boolean} Result * @public */ equals(other) { if (this === other) return true; if (!(other instanceof FPackageObjectIndex)) return false; other; return this.value().toString() === other.value().toString(); } } exports.FPackageObjectIndex = FPackageObjectIndex; FPackageObjectIndex.INDEX_BITS = long_1.default.fromNumber(62, true); FPackageObjectIndex.INDEX_MASK = long_1.default.UONE.shl(FPackageObjectIndex.INDEX_BITS).sub(long_1.default.UONE); FPackageObjectIndex.TYPE_SHIFT = FPackageObjectIndex.INDEX_BITS; FPackageObjectIndex.INVALID = long_1.default.UZERO.not(); /** * FZenPackageVersioningInfo */ class FZenPackageVersioningInfo { constructor(Ar) { this.version = Ar.readInt32(); this.packageVersion = new ObjectVersion_1.FPackageFileVersion(Ar); this.licenseeVersion = Ar.readInt32(); const customVersionsLen = Ar.readInt32(); this.customVersions = new Array(customVersionsLen); for (let i = 0; i < customVersionsLen; ++i) this.customVersions[i] = new CustomVersion_1.FCustomVersion(Ar); } } exports.FZenPackageVersioningInfo = FZenPackageVersioningInfo; /** * FZenPackageSummary */ class FZenPackageSummary { constructor(Ar) { this.bHasVersioningInfo = Ar.readBoolean(); this.headerSize = Ar.readUInt32(); this.name = new FMappedName(Ar); this.packageFlags = Ar.readUInt32(); this.cookedHeaderSize = Ar.readUInt32(); this.importedPublicExportHashesOffset = Ar.readInt32(); this.importMapOffset = Ar.readInt32(); this.exportMapOffset = Ar.readInt32(); this.exportBundleEntriesOffset = Ar.readInt32(); this.graphDataOffset = Ar.readInt32(); } } exports.FZenPackageSummary = FZenPackageSummary; /** * FPackageObjectIndex_EType * @enum */ var FPackageObjectIndex_EType; (function (FPackageObjectIndex_EType) { FPackageObjectIndex_EType[FPackageObjectIndex_EType["Export"] = 0] = "Export"; FPackageObjectIndex_EType[FPackageObjectIndex_EType["ScriptImport"] = 1] = "ScriptImport"; FPackageObjectIndex_EType[FPackageObjectIndex_EType["PackageImport"] = 2] = "PackageImport"; FPackageObjectIndex_EType[FPackageObjectIndex_EType["Null"] = 3] = "Null"; })(FPackageObjectIndex_EType = exports.FPackageObjectIndex_EType || (exports.FPackageObjectIndex_EType = {})); /** * FPackageSummary */ class FPackageSummary { /** * Creates an instance using an UE4 Reader * @param {FArchive} Ar UE4 Reader to use * @constructor * @public */ constructor(Ar) { this.name = new FMappedName(Ar); this.sourceName = new FMappedName(Ar); this.packageFlags = -~Ar.readUInt32() - 1; // TODO is this right? following original code gives inaccurate output this.cookedHeaderSize = Ar.readUInt32(); this.nameMapNamesOffset = Ar.readInt32(); this.nameMapNamesSize = Ar.readInt32(); this.nameMapHashesOffset = Ar.readInt32(); this.nameMapHashesSize = Ar.readInt32(); this.importMapOffset = Ar.readInt32(); this.exportMapOffset = Ar.readInt32(); this.exportBundlesOffset = Ar.readInt32(); this.graphDataOffset = Ar.readInt32(); this.graphDataSize = Ar.readInt32(); this.pad = Ar.readInt32(); } } exports.FPackageSummary = FPackageSummary; /** * FExportMapEntry */ class FExportMapEntry { /** * Creates an instance using an UE4 Reader * @param {FArchive} Ar UE4 Reader to use * @constructor * @public */ constructor(Ar) { /** * cookedSerialOffset * @type {number} * @public */ this.cookedSerialOffset = 0; /** * cookedSerialSize * @type {number} * @public */ this.cookedSerialSize = 0; const start = Ar.pos; this.cookedSerialOffset = Number(Ar.readUInt64()); this.cookedSerialSize = Number(Ar.readUInt64()); this.objectName = new FMappedName(Ar); this.outerIndex = new FPackageObjectIndex(Ar); this.classIndex = new FPackageObjectIndex(Ar); this.superIndex = new FPackageObjectIndex(Ar); this.templateIndex = new FPackageObjectIndex(Ar); if (Ar.game >= Game_1.Game.GAME_UE5_BASE) { this.globalImportIndex = new FPackageObjectIndex(); this.publicExportHash = Ar.readInt64(); } else { this.globalImportIndex = new FPackageObjectIndex(Ar); this.publicExportHash = 0n; } this.objectFlags = Ar.readUInt32(); this.filterFlags = Ar.readUInt8(); Ar.pos = start + FExportMapEntry.SIZE; } } exports.FExportMapEntry = FExportMapEntry; FExportMapEntry.SIZE = 72; /** * FExportBundleHeader */ class FExportBundleHeader { /** * Creates an instance using an UE4 Reader * @param {FArchive} Ar UE4 Reader to use * @constructor * @public */ constructor(Ar) { this.serialOffset = Ar.game >= Game_1.Game.GAME_UE5_BASE ? Ar.readUInt64() : 0xffffffffffffffffn; this.firstEntryIndex = Ar.readUInt32(); this.entryCount = Ar.readUInt32(); } } exports.FExportBundleHeader = FExportBundleHeader; /** * FExportBundleEntry */ class FExportBundleEntry { /** * Creates an instance using an UE4 Reader * @param {FArchive} Ar UE4 Reader to use * @constructor * @public */ constructor(Ar) { this.localExportIndex = Ar.readInt32(); this.commandType = Ar.readInt32(); } } exports.FExportBundleEntry = FExportBundleEntry; /** * EExportCommandType * @enum */ var EExportCommandType; (function (EExportCommandType) { EExportCommandType[EExportCommandType["ExportCommandType_Create"] = 0] = "ExportCommandType_Create"; EExportCommandType[EExportCommandType["ExportCommandType_Serialize"] = 1] = "ExportCommandType_Serialize"; EExportCommandType[EExportCommandType["ExportCommandType_Count"] = 2] = "ExportCommandType_Count"; })(EExportCommandType = exports.EExportCommandType || (exports.EExportCommandType = {}));