unreal.js
Version:
A pak reader for games like VALORANT & Fortnite written in Node.JS
488 lines (487 loc) • 16.3 kB
JavaScript
"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 = {}));