main funcions fixes

This commit is contained in:
2025-09-29 22:06:11 +09:00
parent 40e016e128
commit c8c3274527
7995 changed files with 1517998 additions and 1057 deletions

View File

@@ -0,0 +1,45 @@
import { Type } from 'pe-library';
import IconItem from '../data/IconItem.js';
import RawIconItem from '../data/RawIconItem.js';
export interface IconGroupItem {
width: number;
height: number;
colors: number;
planes: number;
bitCount: number;
dataSize: number;
iconID: number;
}
/**
* A class that treats icon-group resource data (`RT_ICON_GROUP`).
* Note that this class does not treat `RT_ICON` data.
*
* - To pick all icons, use `IconGroupEntry.fromEntries`
* and `IconGroupEntry.prototype.getIconItemsFromEntries`.
* - The easiest way to add/replace icons is using `IconGroupEntry.replaceIconsForResource`,
* which treats both `RT_ICON_GROUP` and `RT_ICON` entries.
*/
export default class IconGroupEntry {
id: string | number;
lang: string | number;
readonly icons: IconGroupItem[];
private constructor();
static fromEntries(entries: readonly Type.ResourceEntry[]): IconGroupEntry[];
generateEntry(): Type.ResourceEntry;
/**
* Return an array of `IconItem` / `RawIconItem`, which are in the group of this `IconGroupEntry` instance,
* from specified resource entries.
*/
getIconItemsFromEntries(entries: readonly Type.ResourceEntry[]): Array<IconItem | RawIconItem>;
/**
* Add or replace icon resource entries with specified icon data.
* The IDs of individual icon resources (`RT_ICON`) are calculated automatically.
* @param destEntries base (destination) resource entries.
* @param iconGroupID the icon ID for the new resource data.
* If the icon-group resource of the ID and 'lang' value already exists,
* the resource data is replaced; otherwise the resource data is appended.
* @param lang the language for specified icons (0 for neutral, 0x409 for en-US)
* @param icons the icons to replace
*/
static replaceIconsForResource(destEntries: Type.ResourceEntry[], iconGroupID: string | number, lang: string | number, icons: Array<IconItem | RawIconItem>): void;
}

View File

@@ -0,0 +1,294 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var IconItem_js_1 = require("../data/IconItem.js");
var RawIconItem_js_1 = require("../data/RawIconItem.js");
var functions_js_1 = require("../util/functions.js");
function generateEntryBinary(icons) {
var count = icons.length;
if (count > 65535) {
count = 65535;
}
var size = 6 + 14 * icons.length;
var bin = new ArrayBuffer(size);
var view = new DataView(bin);
view.setUint16(0, 0, true); // reserved
view.setUint16(2, 1, true); // icon type
view.setUint16(4, count, true);
var offset = 6;
icons.forEach(function (icon) {
view.setUint8(offset, icon.width >= 256 ? 0 : icon.width);
view.setUint8(offset + 1, icon.height >= 256 ? 0 : icon.height);
view.setUint8(offset + 2, icon.colors >= 256 ? 0 : icon.colors);
view.setUint8(offset + 3, 0);
view.setUint16(offset + 4, icon.planes, true);
view.setUint16(offset + 6, icon.bitCount, true);
view.setUint32(offset + 8, icon.dataSize, true);
view.setUint16(offset + 12, icon.iconID, true);
offset += 14;
});
return bin;
}
function findUnusedIconID(entries, lang, isCursor) {
var type = isCursor ? 1 : 3;
// (ignore string id)
var filteredIDs = entries
.filter(function (e) { return e.type === type && e.lang === lang && typeof e.id === 'number'; })
.map(function (e) { return e.id; })
.sort(function (a, b) { return a - b; });
var idCurrent = 1;
for (var _i = 0, filteredIDs_1 = filteredIDs; _i < filteredIDs_1.length; _i++) {
var id = filteredIDs_1[_i];
if (idCurrent < id) {
return {
id: idCurrent,
last: false,
};
}
else if (idCurrent === id) {
++idCurrent;
}
}
return {
id: idCurrent,
last: true,
};
}
/**
* A class that treats icon-group resource data (`RT_ICON_GROUP`).
* Note that this class does not treat `RT_ICON` data.
*
* - To pick all icons, use `IconGroupEntry.fromEntries`
* and `IconGroupEntry.prototype.getIconItemsFromEntries`.
* - The easiest way to add/replace icons is using `IconGroupEntry.replaceIconsForResource`,
* which treats both `RT_ICON_GROUP` and `RT_ICON` entries.
*/
var IconGroupEntry = /** @class */ (function () {
function IconGroupEntry(groupEntry) {
var view = new DataView(groupEntry.bin);
var totalSize = view.byteLength;
var icons = [];
if (view.getUint16(2, true) === 1) {
var count = view.getUint16(4, true);
var offset = 6;
for (var i = 0; i < count; ++i) {
icons.push({
width: functions_js_1.readUint8WithLastOffset(view, offset, totalSize),
height: functions_js_1.readUint8WithLastOffset(view, offset + 1, totalSize),
colors: functions_js_1.readUint8WithLastOffset(view, offset + 2, totalSize),
planes: functions_js_1.readUint16WithLastOffset(view, offset + 4, totalSize),
bitCount: functions_js_1.readUint16WithLastOffset(view, offset + 6, totalSize),
dataSize: functions_js_1.readUint32WithLastOffset(view, offset + 8, totalSize),
iconID: functions_js_1.readUint16WithLastOffset(view, offset + 12, totalSize),
});
offset += 14; // 16 for .ico file, but 14 for resource data
}
}
this.id = groupEntry.id;
this.lang = groupEntry.lang;
this.icons = icons;
}
IconGroupEntry.fromEntries = function (entries) {
return entries
.filter(function (e) { return e.type === 14; })
.map(function (e) { return new IconGroupEntry(e); });
};
IconGroupEntry.prototype.generateEntry = function () {
var bin = generateEntryBinary(this.icons);
return {
type: 14,
id: this.id,
lang: this.lang,
codepage: 0,
bin: bin,
};
};
/**
* Return an array of `IconItem` / `RawIconItem`, which are in the group of this `IconGroupEntry` instance,
* from specified resource entries.
*/
IconGroupEntry.prototype.getIconItemsFromEntries = function (entries) {
var _this = this;
return entries
.map(function (e) {
if (e.type !== 3 || e.lang !== _this.lang) {
return null;
}
var c = _this.icons
.filter(function (icon) { return e.id === icon.iconID; })
.shift();
if (!c) {
return null;
}
return {
entry: e,
icon: c,
};
})
.filter(function (item) { return !!item; })
.map(function (item) {
var bin = item.entry.bin;
var view = new DataView(bin);
if (view.getUint32(0, true) === 0x28) {
return IconItem_js_1.default.from(bin);
}
else {
var c = item.icon;
return RawIconItem_js_1.default.from(bin, c.width, c.height, c.bitCount);
}
});
};
/**
* Add or replace icon resource entries with specified icon data.
* The IDs of individual icon resources (`RT_ICON`) are calculated automatically.
* @param destEntries base (destination) resource entries.
* @param iconGroupID the icon ID for the new resource data.
* If the icon-group resource of the ID and 'lang' value already exists,
* the resource data is replaced; otherwise the resource data is appended.
* @param lang the language for specified icons (0 for neutral, 0x409 for en-US)
* @param icons the icons to replace
*/
IconGroupEntry.replaceIconsForResource = function (destEntries, iconGroupID, lang, icons) {
// find existing entry
var entry = destEntries
.filter(function (e) { return e.type === 14 && e.id === iconGroupID && e.lang === lang; })
.shift();
var tmpIconArray = icons.map(function (icon) {
if (icon.isIcon()) {
var width = icon.width, height = icon.height;
if (width === null) {
width = icon.bitmapInfo.width;
}
if (height === null) {
height = icon.bitmapInfo.height;
// if mask is specified, the icon height must be the half of bitmap height
if (icon.masks !== null) {
height = Math.floor(height / 2);
}
}
return {
base: icon,
bm: {
width: width,
height: height,
planes: icon.bitmapInfo.planes,
bitCount: icon.bitmapInfo.bitCount,
},
bin: icon.generate(),
id: 0,
};
}
else {
return {
base: icon,
bm: {
width: icon.width,
height: icon.height,
planes: 1,
bitCount: icon.bitCount,
},
bin: icon.bin,
id: 0,
};
}
});
if (entry) {
// remove unused icon data
for (var i = destEntries.length - 1; i >= 0; --i) {
var e = destEntries[i];
if (e != null && e.type === 3) {
// RT_ICON
if (!isIconUsed(e, destEntries, entry)) {
destEntries.splice(i, 1);
}
}
}
}
else {
// create new entry
entry = {
type: 14,
id: iconGroupID,
lang: lang,
codepage: 0,
// set later
bin: null,
};
destEntries.push(entry);
}
// append icons
var idInfo;
tmpIconArray.forEach(function (icon) {
if (!(idInfo === null || idInfo === void 0 ? void 0 : idInfo.last)) {
idInfo = findUnusedIconID(destEntries, lang, false);
}
else {
++idInfo.id;
}
destEntries.push({
type: 3,
id: idInfo.id,
lang: lang,
codepage: 0,
bin: icon.bin,
});
// set 'id' field to use in generateEntryBinary
icon.id = idInfo.id;
});
var binEntry = generateEntryBinary(tmpIconArray.map(function (icon) {
var width = Math.abs(icon.bm.width);
if (width >= 256) {
width = 0;
}
var height = Math.abs(icon.bm.height);
if (height >= 256) {
height = 0;
}
var colors = 0;
if (icon.base.isIcon()) {
var bmBase = icon.base.bitmapInfo;
colors = bmBase.colorUsed || bmBase.colors.length;
if (!colors) {
switch (bmBase.bitCount) {
case 1:
colors = 2;
break;
case 4:
colors = 16;
break;
// case 8:
// colors = 256;
// break;
}
}
if (colors >= 256) {
colors = 0;
}
}
return {
width: width,
height: height,
colors: colors,
planes: icon.bm.planes,
bitCount: icon.bm.bitCount,
dataSize: icon.bin.byteLength,
iconID: icon.id,
};
}));
// rewrite entry
entry.bin = binEntry;
function isIconUsed(icon, allEntries, excludeGroup) {
return allEntries.some(function (e) {
if (e.type !== 14 ||
(e.id === excludeGroup.id && e.lang === excludeGroup.lang)) {
return false;
}
var g = new IconGroupEntry(e);
return g.icons.some(function (c) {
return c.iconID === icon.id;
});
});
}
};
return IconGroupEntry;
}());
exports.default = IconGroupEntry;

View File

@@ -0,0 +1,30 @@
import { NtExecutableResource, Type } from 'pe-library';
/** Utility class to create / parse String Table resource */
export default class StringTable {
/** Language value */
lang: string | number;
private items;
constructor();
/** Create StringTable instance from resource entries, with specified language. */
static fromEntries(lang: string | number, entries: readonly Type.ResourceEntry[]): StringTable;
/** Return all string entries. */
getAllStrings(): Array<{
id: number;
text: string;
}>;
/** Return the string data for ID value, which can be used for Win32API LoadString. */
getById(id: number): string | null;
/**
* Set/overwide the string data for ID value, which can be used for Win32API LoadString.
* @param id data ID
* @param text string data (entry will be removed if null or empty string is specified)
*/
setById(id: number, text: string | null): void;
/** Generates an array of Entry for resource processings */
generateEntries(): Type.ResourceEntry[];
/**
* Replace all string entries for NtExecutableResource with containing resource data.
* The only entries of same language are replaced.
*/
replaceStringEntriesForExecutable(res: NtExecutableResource): void;
}

View File

@@ -0,0 +1,132 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var StringTableItem_js_1 = require("./StringTableItem.js");
/** Utility class to create / parse String Table resource */
var StringTable = /** @class */ (function () {
function StringTable() {
this.lang = 0;
this.items = [];
}
/** Create StringTable instance from resource entries, with specified language. */
StringTable.fromEntries = function (lang, entries) {
var r = new StringTable();
entries.forEach(function (e) {
// 6: RT_STRING
if (e.type !== 6 ||
e.lang !== lang ||
typeof e.id !== 'number' ||
e.id <= 0) {
return;
}
r.items[e.id - 1] = StringTableItem_js_1.default.fromEntry(e.bin, 0, e.bin.byteLength);
});
r.lang = lang;
return r;
};
/** Return all string entries. */
StringTable.prototype.getAllStrings = function () {
return this.items
.map(function (e, i) {
return e
.getAll()
.map(function (x, j) {
return x !== null && x !== ''
? { id: (i << 4) + j, text: x }
: null;
})
.filter(function (x) { return !!x; });
})
.reduce(function (p, c) { return p.concat(c); }, []);
};
/** Return the string data for ID value, which can be used for Win32API LoadString. */
StringTable.prototype.getById = function (id) {
var _a;
if (id < 0) {
return null;
}
var entryIndex = id >> 4;
var entryPos = id & 15;
var e = this.items[entryIndex];
return (_a = e === null || e === void 0 ? void 0 : e.get(entryPos)) !== null && _a !== void 0 ? _a : null;
};
/**
* Set/overwide the string data for ID value, which can be used for Win32API LoadString.
* @param id data ID
* @param text string data (entry will be removed if null or empty string is specified)
*/
StringTable.prototype.setById = function (id, text) {
if (id < 0) {
return;
}
var entryIndex = id >> 4;
var entryPos = id & 15;
var e = this.items[entryIndex];
if (!e) {
this.items[entryIndex] = e = new StringTableItem_js_1.default();
}
e.set(entryPos, text);
};
/** Generates an array of Entry for resource processings */
StringTable.prototype.generateEntries = function () {
var _this = this;
return this.items
.map(function (e, i) {
var len = e.calcByteLength();
var bin = new ArrayBuffer(len);
e.generate(bin, 0);
return {
type: 6,
id: i + 1,
lang: _this.lang,
codepage: 1200,
bin: bin,
};
})
.filter(function (e) { return !!e; });
};
/**
* Replace all string entries for NtExecutableResource with containing resource data.
* The only entries of same language are replaced.
*/
StringTable.prototype.replaceStringEntriesForExecutable = function (res) {
var entries = this.generateEntries();
var dest = res.entries;
// first try -- replace same type and same language
for (var i = 0; i < dest.length; ++i) {
var e = dest[i];
if (e != null && e.type === 6 && e.lang === this.lang) {
for (var j = dest.length - 1; j >= i; --j) {
var e2 = dest[j];
if (e2 != null && e2.type === 6 && e2.lang === this.lang) {
dest.splice(j, 1);
}
}
var f = dest.splice.bind(dest, i, 0);
f.apply(void 0, entries);
return;
}
}
// second try -- add entries next to previous language
for (var i = 0; i < dest.length; ++i) {
var e = dest[i];
if (e != null && e.type === 6 && e.lang < this.lang) {
var f = dest.splice.bind(dest, i + 1, 0);
f.apply(void 0, entries);
return;
}
}
// third try -- add entries next to the last 'String' entry
for (var i = dest.length - 1; i >= 0; --i) {
var e = dest[i];
if (e != null && e.type === 6) {
var f = dest.splice.bind(dest, i + 1, 0);
f.apply(void 0, entries);
return;
}
}
// otherwise -- add entries to the last
dest.push.apply(dest, entries);
};
return StringTable;
}());
exports.default = StringTable;

View File

@@ -0,0 +1,11 @@
export default class StringTableItem {
readonly length = 16;
private _a;
constructor();
static fromEntry(bin: ArrayBuffer, offset: number, byteLength: number): StringTableItem;
get(index: number): string | null;
getAll(): Array<string | null>;
set(index: number, val: string | null): void;
calcByteLength(): number;
generate(bin: ArrayBuffer, offset: number): number;
}

View File

@@ -0,0 +1,73 @@
"use strict";
// StringTable entry:
// 16-times of {<WORD length> [<UTF-16 string>]}
Object.defineProperty(exports, "__esModule", { value: true });
var StringTableItem = /** @class */ (function () {
function StringTableItem() {
this.length = 16;
this._a = [];
this._a.length = 16;
for (var i = 0; i < 16; ++i) {
this._a[i] = '';
}
}
StringTableItem.fromEntry = function (bin, offset, byteLength) {
var view = new DataView(bin, offset, byteLength);
var ret = new StringTableItem();
var o = 0;
for (var i = 0; i < 16; ++i) {
var len = view.getUint16(o, true);
o += 2;
var s = '';
for (var j = 0; j < len; ++j) {
s += String.fromCharCode(view.getUint16(o, true));
o += 2;
}
ret._a[i] = s;
}
return ret;
};
StringTableItem.prototype.get = function (index) {
var value = this._a[index];
return value != null && value !== '' ? value : null;
};
StringTableItem.prototype.getAll = function () {
return this._a.map(function (s) { return s || null; });
};
StringTableItem.prototype.set = function (index, val) {
this._a[index] = ("" + (val !== null && val !== void 0 ? val : '')).substr(0, 4097); // length must be no longer than 4097
};
StringTableItem.prototype.calcByteLength = function () {
var len = 0;
for (var i = 0; i < 16; ++i) {
var item = this._a[i];
len += 2;
if (item != null) {
len += 2 * item.length; // UTF-16 length
}
}
// 16 alignment
return Math.floor((len + 15) / 16) * 16;
};
StringTableItem.prototype.generate = function (bin, offset) {
var out = new DataView(bin, offset);
var len = 0;
for (var i = 0; i < 16; ++i) {
var s = this._a[i];
var l = s == null ? 0 : s.length > 4097 ? 4097 : s.length;
out.setUint16(len, l, true);
len += 2;
if (s != null) {
for (var j = 0; j < l; ++j) {
// output as UTF-16
out.setUint16(len, s.charCodeAt(j), true);
len += 2;
}
}
}
// 16 alignment
return Math.floor((len + 15) / 16) * 16;
};
return StringTableItem;
}());
exports.default = StringTableItem;

View File

@@ -0,0 +1,13 @@
/**
* Flag values used by VersionEntry.fixedInfo field.
* Zero or more enum values are stored (with OR operator).
*/
declare enum VersionFileFlags {
Debug = 1,
Prerelease = 2,
Patched = 4,
PrivateBuild = 8,
InfoInferred = 16,
SpecialBuild = 32
}
export default VersionFileFlags;

View File

@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Flag values used by VersionEntry.fixedInfo field.
* Zero or more enum values are stored (with OR operator).
*/
var VersionFileFlags;
(function (VersionFileFlags) {
VersionFileFlags[VersionFileFlags["Debug"] = 1] = "Debug";
VersionFileFlags[VersionFileFlags["Prerelease"] = 2] = "Prerelease";
VersionFileFlags[VersionFileFlags["Patched"] = 4] = "Patched";
VersionFileFlags[VersionFileFlags["PrivateBuild"] = 8] = "PrivateBuild";
VersionFileFlags[VersionFileFlags["InfoInferred"] = 16] = "InfoInferred";
VersionFileFlags[VersionFileFlags["SpecialBuild"] = 32] = "SpecialBuild";
})(VersionFileFlags || (VersionFileFlags = {}));
exports.default = VersionFileFlags;

View File

@@ -0,0 +1,20 @@
/**
* OS values used by VersionEntry.fixedInfo field.
*/
declare enum VersionFileOS {
Unknown = 0,
_Windows16 = 1,
_PM16 = 2,
_PM32 = 3,
_Windows32 = 4,
DOS = 65536,
OS2_16 = 131072,
OS2_32 = 196608,
NT = 262144,
DOS_Windows16 = 65537,
DOS_Windows32 = 65540,
NT_Windows32 = 262148,
OS2_16_PM16 = 131074,
OS2_32_PM32 = 196611
}
export default VersionFileOS;

View File

@@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* OS values used by VersionEntry.fixedInfo field.
*/
var VersionFileOS;
(function (VersionFileOS) {
VersionFileOS[VersionFileOS["Unknown"] = 0] = "Unknown";
VersionFileOS[VersionFileOS["_Windows16"] = 1] = "_Windows16";
VersionFileOS[VersionFileOS["_PM16"] = 2] = "_PM16";
VersionFileOS[VersionFileOS["_PM32"] = 3] = "_PM32";
VersionFileOS[VersionFileOS["_Windows32"] = 4] = "_Windows32";
VersionFileOS[VersionFileOS["DOS"] = 65536] = "DOS";
VersionFileOS[VersionFileOS["OS2_16"] = 131072] = "OS2_16";
VersionFileOS[VersionFileOS["OS2_32"] = 196608] = "OS2_32";
VersionFileOS[VersionFileOS["NT"] = 262144] = "NT";
VersionFileOS[VersionFileOS["DOS_Windows16"] = 65537] = "DOS_Windows16";
VersionFileOS[VersionFileOS["DOS_Windows32"] = 65540] = "DOS_Windows32";
VersionFileOS[VersionFileOS["NT_Windows32"] = 262148] = "NT_Windows32";
VersionFileOS[VersionFileOS["OS2_16_PM16"] = 131074] = "OS2_16_PM16";
VersionFileOS[VersionFileOS["OS2_32_PM32"] = 196611] = "OS2_32_PM32";
})(VersionFileOS || (VersionFileOS = {}));
exports.default = VersionFileOS;

View File

@@ -0,0 +1,20 @@
export declare enum VersionFileDriverSubtype {
Unknown = 0,
Printer = 1,
Keyboard = 2,
Language = 3,
Display = 4,
Mouse = 5,
Network = 6,
System = 7,
Installable = 8,
Sound = 9,
Comm = 10,
VersionedPrinter = 12
}
export declare enum VersionFileFontSubtype {
Unknown = 0,
Raster = 1,
Vector = 2,
TrueType = 3
}

View File

@@ -0,0 +1,25 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.VersionFileFontSubtype = exports.VersionFileDriverSubtype = void 0;
var VersionFileDriverSubtype;
(function (VersionFileDriverSubtype) {
VersionFileDriverSubtype[VersionFileDriverSubtype["Unknown"] = 0] = "Unknown";
VersionFileDriverSubtype[VersionFileDriverSubtype["Printer"] = 1] = "Printer";
VersionFileDriverSubtype[VersionFileDriverSubtype["Keyboard"] = 2] = "Keyboard";
VersionFileDriverSubtype[VersionFileDriverSubtype["Language"] = 3] = "Language";
VersionFileDriverSubtype[VersionFileDriverSubtype["Display"] = 4] = "Display";
VersionFileDriverSubtype[VersionFileDriverSubtype["Mouse"] = 5] = "Mouse";
VersionFileDriverSubtype[VersionFileDriverSubtype["Network"] = 6] = "Network";
VersionFileDriverSubtype[VersionFileDriverSubtype["System"] = 7] = "System";
VersionFileDriverSubtype[VersionFileDriverSubtype["Installable"] = 8] = "Installable";
VersionFileDriverSubtype[VersionFileDriverSubtype["Sound"] = 9] = "Sound";
VersionFileDriverSubtype[VersionFileDriverSubtype["Comm"] = 10] = "Comm";
VersionFileDriverSubtype[VersionFileDriverSubtype["VersionedPrinter"] = 12] = "VersionedPrinter";
})(VersionFileDriverSubtype = exports.VersionFileDriverSubtype || (exports.VersionFileDriverSubtype = {}));
var VersionFileFontSubtype;
(function (VersionFileFontSubtype) {
VersionFileFontSubtype[VersionFileFontSubtype["Unknown"] = 0] = "Unknown";
VersionFileFontSubtype[VersionFileFontSubtype["Raster"] = 1] = "Raster";
VersionFileFontSubtype[VersionFileFontSubtype["Vector"] = 2] = "Vector";
VersionFileFontSubtype[VersionFileFontSubtype["TrueType"] = 3] = "TrueType";
})(VersionFileFontSubtype = exports.VersionFileFontSubtype || (exports.VersionFileFontSubtype = {}));

View File

@@ -0,0 +1,13 @@
/**
* File type values used by VersionEntry.fixedInfo field.
*/
declare enum VersionFileType {
Unknown = 0,
App = 1,
DLL = 2,
Driver = 3,
Font = 4,
VxD = 5,
StaticLibrary = 7
}
export default VersionFileType;

View File

@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* File type values used by VersionEntry.fixedInfo field.
*/
var VersionFileType;
(function (VersionFileType) {
VersionFileType[VersionFileType["Unknown"] = 0] = "Unknown";
VersionFileType[VersionFileType["App"] = 1] = "App";
VersionFileType[VersionFileType["DLL"] = 2] = "DLL";
VersionFileType[VersionFileType["Driver"] = 3] = "Driver";
VersionFileType[VersionFileType["Font"] = 4] = "Font";
VersionFileType[VersionFileType["VxD"] = 5] = "VxD";
VersionFileType[VersionFileType["StaticLibrary"] = 7] = "StaticLibrary";
})(VersionFileType || (VersionFileType = {}));
exports.default = VersionFileType;

View File

@@ -0,0 +1,197 @@
import { Type } from 'pe-library';
/**
* String values for the version information.
* In most cases predefined names are used for the key names (such as 'FileDescription', 'FileVersion', etc.)
* Note that the key names are case-sensitive; this library does not convert keys
* (e.g. `'fileVersion'` --> `'FileVersion'`).
*/
export declare type VersionStringValues = Record<string, string>;
/** Used by `VersionInfo.create` */
export interface VersionStringTable {
lang: number;
codepage: number;
/** Any string values */
values: VersionStringValues;
}
/** Translation information, containing LANGID and codepage value. */
export interface VersionTranslation {
lang: number;
/** Almost all cases are set to 1200 (Unicode) */
codepage: number;
}
/** Fixed version info, containing file version, product version, etc. (`VS_FIXEDFILEINFO`) */
export interface VersionFixedInfo {
/** usually major version in HIWORD(fileVersionMS), minor version in LOWORD(fileVersionMS) */
fileVersionMS: number;
/** usually patch version in HIWORD(fileVersionLS), revision in LOWORD(fileVersionLS) */
fileVersionLS: number;
productVersionMS: number;
productVersionLS: number;
/** valid values of fileFlags */
fileFlagsMask: number;
/** zero or more VersionFileFlags values, masked by fileFlagsMask */
fileFlags: number;
/** VersionFileOS value */
fileOS: number;
/** VersionFileType value */
fileType: number;
/**
* subtype values depended on fileType, such as
* `VersionFileDriverSubtype` or `VersionFileFontSubtype`.
* (if no suitable value, zero is stored)
*/
fileSubtype: number;
fileDateMS: number;
fileDateLS: number;
}
export interface VersionInfoCreateParam {
lang: string | number;
/** This field can be as a partial object; default values (zero) are used for all unspecified field. */
fixedInfo: Partial<Readonly<VersionFixedInfo>>;
strings: readonly VersionStringTable[];
}
/**
* Treats 'Version information' (`VS_VERSIONINFO`) resource data.
*/
export default class VersionInfo {
private readonly data;
private constructor();
/** Returns new `VersionInfo` instance with empty data. */
static createEmpty(): VersionInfo;
/**
* Returns new `VersionInfo` instance with specified parameters.
* `fixedInfo` can be specified as a partial object;
* default values (zero) are used for all unspecified field.
*/
static create(lang: string | number, fixedInfo: Partial<Readonly<VersionFixedInfo>>, strings: readonly VersionStringTable[]): VersionInfo;
/** Returns new `VersionInfo` instance with specified parameters. */
static create(param: Readonly<VersionInfoCreateParam>): VersionInfo;
/** Pick up all version-info entries */
static fromEntries(entries: readonly Type.ResourceEntry[]): VersionInfo[];
/** A language value for this resource entry. */
get lang(): string | number;
set lang(value: string | number);
/**
* The property of fixed version info, containing file version, product version, etc.
* (data: `VS_FIXEDFILEINFO`)
*
* Although this property is read-only, you can rewrite
* each child fields directly to apply data.
*/
get fixedInfo(): VersionFixedInfo;
/**
* Returns all languages that the executable supports. (data: `VarFileInfo`)
*
* Usually the returned array is equal to the one returned by `getAllLanguagesForStringValues`,
* but some resource-generating tools doesn't generate same values.
*/
getAvailableLanguages(): VersionTranslation[];
/**
* Replaces all languages that the executable supports.
*/
replaceAvailableLanguages(languages: readonly VersionTranslation[]): void;
/**
* Returns all string values for the specified language. (data: values in lang-charset block of `StringFileInfo`)
*/
getStringValues(language: VersionTranslation): VersionStringValues;
/**
* Returns all languages used by string values. (data: lang-charset name of `StringFileInfo`)
*
* Usually the returned array is equal to the one returned by `getAvailableLanguages`,
* but some resource-generating tools doesn't generate same values.
*/
getAllLanguagesForStringValues(): VersionTranslation[];
/**
* Add or replace the string values.
* @param language language info
* @param values string values (key-value pairs)
* @param addToAvailableLanguage set `true` to add `language` into available languages
* if not existing in `getAvailableLanguages()` (default: `true`)
*/
setStringValues(language: VersionTranslation, values: VersionStringValues, addToAvailableLanguage?: boolean): void;
/**
* Add or replace the string value.
* @param language language info
* @param key the key name of string value
* @param value the string value
* @param addToAvailableLanguage set `true` to add `language` into available languages
* if not existing in `getAvailableLanguages()` (default: `true`)
*/
setStringValue(language: VersionTranslation, key: string, value: string, addToAvailableLanguage?: boolean): void;
/**
* Remove all string values for specified language.
* @param language language info
* @param removeFromAvailableLanguage set `true` to remove `language` from available languages
* if existing in `getAvailableLanguages()` (default: `true`)
*/
removeAllStringValues(language: VersionTranslation, removeFromAvailableLanguage?: boolean): void;
/**
* Remove specified string value for specified language.
* @param language language info
* @param key the key name of string value to be removed
* @param removeFromAvailableLanguage set `true` to remove `language` from available languages
* if no more string values exist for `language` (default: `true`)
*/
removeStringValue(language: VersionTranslation, key: string, removeFromAvailableLanguage?: boolean): void;
/**
* Creates `Type.ResourceEntry` object for this instance.
* Usually `outputToResourceEntries` is suitable for generating resource data
* into executables, but you can use this method if necessary.
*/
generateResource(): Type.ResourceEntry;
/**
* Generates version info resource data (using `generateResource()`) and emits into `entries` array.
* If version info resource already exists in `entries`, this method replaces it with the new one.
* @param entries resource entry array for output
*/
outputToResourceEntries(entries: Type.ResourceEntry[]): void;
private getDefaultVersionLang;
/**
* Sets 'FileVersion' property with specified values.
* This methods writes `fixedInfo.fileVersionMS` and `fixedInfo.fileVersionLS` fields,
* and writes `FileVersion` string with the value `<major>.<minor>.<micro>.<revision>`.
* @param major The major version (clamped between 0 and 65535)
* @param minor The minor version (clamped between 0 and 65535)
* @param micro The micro version (clamped between 0 and 65535; default is 0)
* @param revision The revision value (clamped between 0 and 65535; default is 0)
* @param lang The language (default: this.lang -> picked from existings -> 1033)
* @note
* If you want to use 'Neutral' language for the version string, specify `lang` parameter to 0 explicitly
*/
setFileVersion(major: number, minor: number, micro?: number, revision?: number, lang?: number): void;
/**
* Sets 'FileVersion' property with specified values.
* This methods writes `fixedInfo.fileVersionMS` and `fixedInfo.fileVersionLS` fields,
* and writes `FileVersion` string with the value `<major>.<minor>.<micro>.<revision>`.
* @param version The version string value (should be `x.x.x.x` format; each integer clamped between 0 and 65535)
* @param lang The language (default: this.lang -> picked from existings -> 1033)
* @note
* If you want to use 'Neutral' language for the version string, specify `lang` parameter to 0 explicitly
*/
setFileVersion(version: string, lang?: number): void;
private setFileVersionImpl;
/**
* Sets 'ProductVersion' property with specified values.
* This methods writes `fixedInfo.productVersionMS` and `fixedInfo.productVersionLS` fields,
* and writes `ProductVersion` string with the value `<major>.<minor>.<micro>.<revision>`.
* @param major The major version (clamped between 0 and 65535)
* @param minor The minor version (clamped between 0 and 65535)
* @param micro The micro version (clamped between 0 and 65535; default is 0)
* @param revision The revision value (clamped between 0 and 65535; default is 0)
* @param lang The language (default: this.lang -> picked from existings -> 1033)
* @note
* If you want to use 'Neutral' language for the version string, specify `lang` parameter to 0 explicitly
*/
setProductVersion(major: number, minor: number, micro?: number, revision?: number, lang?: number): void;
/**
* Sets 'ProductVersion' property with specified values.
* This methods writes `fixedInfo.productVersionMS` and `fixedInfo.productVersionLS` fields,
* and writes `ProductVersion` string with the value `<major>.<minor>.<micro>.<revision>`.
* @param version The version string value (should be `x.x.x.x` format; each integer clamped between 0 and 65535)
* @param lang The language (default: this.lang -> picked from existings -> 1033)
* @note
* If you want to use 'Neutral' language for the version string, specify `lang` parameter to 0 explicitly
*/
setProductVersion(version: string, lang?: number): void;
private setProductVersionImpl;
}

View File

@@ -0,0 +1,741 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var functions_js_1 = require("../util/functions.js");
function readStringToNullChar(view, offset, last) {
var r = '';
while (offset + 2 <= last) {
var c = view.getUint16(offset, true);
if (!c) {
break;
}
r += String.fromCharCode(c);
offset += 2;
}
return r;
}
function writeStringWithNullChar(view, offset, value) {
for (var i = 0; i < value.length; ++i) {
view.setUint16(offset, value.charCodeAt(i), true);
offset += 2;
}
view.setUint16(offset, 0, true);
return offset + 2;
}
function createFixedInfo() {
return {
fileVersionMS: 0,
fileVersionLS: 0,
productVersionMS: 0,
productVersionLS: 0,
fileFlagsMask: 0,
fileFlags: 0,
fileOS: 0,
fileType: 0,
fileSubtype: 0,
fileDateMS: 0,
fileDateLS: 0,
};
}
////////////////////////////////////////////////////////////////////////////////
// parsings
// returns offset and structure
function parseStringTable(view, offset, last) {
var tableLen = view.getUint16(offset, true);
var valueLen = view.getUint16(offset + 2, true);
if (offset + tableLen < last) {
last = offset + tableLen;
}
// value type check is not needed; because no value is needed
var tableName = readStringToNullChar(view, offset + 6, last);
offset += functions_js_1.roundUp(6 + 2 * (tableName.length + 1), 4);
var langAndCp = parseInt(tableName, 16);
if (isNaN(langAndCp)) {
throw new Error('Invalid StringTable data format');
}
// this should be zero
offset += functions_js_1.roundUp(valueLen, 4);
var r = {
lang: Math.floor(langAndCp / 0x10000),
codepage: langAndCp & 0xffff,
values: {},
};
while (offset < last) {
// String structure
var childDataLen = view.getUint16(offset, true);
var childValueLen = view.getUint16(offset + 2, true);
// value type must be string; if not, skip it
if (view.getUint16(offset + 4, true) !== 1) {
offset += childDataLen;
continue;
}
var childDataLast = offset + childDataLen;
if (childDataLast > last) {
childDataLast = last;
}
var name_1 = readStringToNullChar(view, offset + 6, childDataLast);
offset = functions_js_1.roundUp(offset + 6 + 2 * (name_1.length + 1), 4);
var childValueLast = offset + childValueLen * 2;
if (childValueLast > childDataLast) {
childValueLast = childDataLast;
}
var value = readStringToNullChar(view, offset, childValueLast);
offset = functions_js_1.roundUp(childValueLast, 4);
r.values[name_1] = value;
}
// return 'last' instead of 'offset'
return [last, r];
}
function parseStringFileInfo(view, offset, last) {
var valueLen = view.getUint16(offset + 2, true);
// value type check is not needed; because no value is needed
offset += 36; // roundUp(6 + ByteLenWithNull(L'StringFileInfo'), 4)
// this should be zero
offset += functions_js_1.roundUp(valueLen, 4);
var r = [];
var _loop_1 = function () {
// StringTable structure
var childData = parseStringTable(view, offset, last);
var table = childData[1];
var a = r.filter(function (e) { return e.lang === table.lang && e.codepage === table.codepage; });
if (a.length === 0) {
r.push(table);
}
else {
// merge values
for (var key in table.values) {
var value = table.values[key];
if (value != null) {
a[0].values[key] = value;
}
}
}
offset = functions_js_1.roundUp(childData[0], 4);
};
while (offset < last) {
_loop_1();
}
return r;
}
function parseVarFileInfo(view, offset, last) {
var valueLen = view.getUint16(offset + 2, true);
// value type check is not needed; because no value is needed
offset += 32; // roundUp(6 + ByteLenWithNull(L'VarFileInfo'), 4)
// this should be zero
offset += functions_js_1.roundUp(valueLen, 4);
var r = [];
while (offset < last) {
// Var structure
var childDataLen = view.getUint16(offset, true);
var childValueLen = view.getUint16(offset + 2, true);
// value type must be binary; if not, skip it
if (view.getUint16(offset + 4, true) !== 0) {
offset += functions_js_1.roundUp(childDataLen, 4);
continue;
}
var childDataLast = offset + childDataLen;
if (childDataLast > last) {
childDataLast = last;
}
var name_2 = readStringToNullChar(view, offset + 6, childDataLast);
offset = functions_js_1.roundUp(offset + 6 + 2 * (name_2.length + 1), 4);
if (name_2 !== 'Translation' || childValueLen % 4 !== 0) {
// unknown entry
offset = functions_js_1.roundUp(childDataLast, 4);
continue;
}
var _loop_2 = function (child) {
if (offset + 4 > childDataLast) {
return "break";
}
var lang = view.getUint16(offset, true);
var codepage = view.getUint16(offset + 2, true);
offset += 4;
if (r.filter(function (e) { return e.lang === lang && e.codepage === codepage; })
.length === 0) {
r.push({ lang: lang, codepage: codepage });
}
};
for (var child = 0; child < childValueLen; child += 4) {
var state_1 = _loop_2(child);
if (state_1 === "break")
break;
}
offset = functions_js_1.roundUp(childDataLast, 4);
}
return r;
}
function parseVersionEntry(view, entry) {
var totalLen = view.getUint16(0, true);
var dataLen = view.getUint16(2, true);
// value type must be binary
if (view.getUint16(4, true) !== 0) {
throw new Error('Invalid version data format');
}
// 40 === roundUp(6 + ByteLenWithNull(L'VS_VERSION_INFO'), 4)
if (totalLen < dataLen + 40) {
throw new Error('Invalid version data format');
}
if (readStringToNullChar(view, 6, totalLen) !== 'VS_VERSION_INFO') {
throw new Error('Invalid version data format');
}
var d = {
lang: entry.lang,
fixedInfo: createFixedInfo(),
strings: [],
translations: [],
unknowns: [],
};
var offset = 38; // without padding
if (dataLen) {
dataLen += 40; // with padding
var sig = functions_js_1.readUint32WithLastOffset(view, 40, dataLen);
var sVer = functions_js_1.readUint32WithLastOffset(view, 44, dataLen);
// check signature
if (sig === 0xfeef04bd && sVer <= 0x10000) {
d.fixedInfo = {
fileVersionMS: functions_js_1.readUint32WithLastOffset(view, 48, dataLen),
fileVersionLS: functions_js_1.readUint32WithLastOffset(view, 52, dataLen),
productVersionMS: functions_js_1.readUint32WithLastOffset(view, 56, dataLen),
productVersionLS: functions_js_1.readUint32WithLastOffset(view, 60, dataLen),
fileFlagsMask: functions_js_1.readUint32WithLastOffset(view, 64, dataLen),
fileFlags: functions_js_1.readUint32WithLastOffset(view, 68, dataLen),
fileOS: functions_js_1.readUint32WithLastOffset(view, 72, dataLen),
fileType: functions_js_1.readUint32WithLastOffset(view, 76, dataLen),
fileSubtype: functions_js_1.readUint32WithLastOffset(view, 80, dataLen),
fileDateMS: functions_js_1.readUint32WithLastOffset(view, 84, dataLen),
fileDateLS: functions_js_1.readUint32WithLastOffset(view, 88, dataLen),
};
}
offset = dataLen;
}
offset = functions_js_1.roundUp(offset, 4);
// parse children
while (offset < totalLen) {
var childLen = view.getUint16(offset, true);
var childLast = offset + childLen;
// failsafe
if (childLast > totalLen) {
childLast = totalLen;
}
var name_3 = readStringToNullChar(view, offset + 6, childLast);
switch (name_3) {
case 'StringFileInfo':
d.strings = d.strings.concat(parseStringFileInfo(view, offset, childLast));
break;
case 'VarFileInfo':
d.translations = d.translations.concat(parseVarFileInfo(view, offset, childLast));
break;
default:
// unknown or unsupported type
d.unknowns.push({
name: name_3,
entireBin: functions_js_1.allocatePartialBinary(view, offset, childLen),
});
break;
}
offset += functions_js_1.roundUp(childLen, 4);
}
return d;
}
////////////////////////////////////////////////////////////////////////////////
// serializings
function generateStringTable(table) {
// estimate size
var size = 24; // roundUp(6 + ByteLenWithNull(L'xxxxxxxx'), 4)
var keys = Object.keys(table.values);
size = keys.reduce(function (prev, key) {
var value = table.values[key];
if (value == null) {
return prev;
}
var childHeaderSize = functions_js_1.roundUp(6 + 2 * (key.length + 1), 4);
var newSize = functions_js_1.roundUp(prev + childHeaderSize + 2 * (value.length + 1), 4);
// limit to 65532 because the table size is restricted to 16-bit value
return newSize > 65532 ? prev : newSize;
}, size);
// generate binary
var bin = new ArrayBuffer(size);
var view = new DataView(bin);
view.setUint16(0, size, true);
view.setUint16(2, 0, true); // no value length
view.setUint16(4, 1, true);
var langAndCp = ((table.lang & 0xffff) * 0x10000 +
(table.codepage & 0xffff))
.toString(16)
.toLowerCase();
// fixed length
if (langAndCp.length < 8) {
var l = 8 - langAndCp.length;
langAndCp = '00000000'.substr(0, l) + langAndCp;
}
var offset = functions_js_1.roundUp(writeStringWithNullChar(view, 6, langAndCp), 4);
keys.forEach(function (key) {
var value = table.values[key];
if (value == null) {
return;
}
var childHeaderSize = functions_js_1.roundUp(6 + 2 * (key.length + 1), 4);
var newSize = functions_js_1.roundUp(childHeaderSize + 2 * (value.length + 1), 4);
if (offset + newSize <= 65532) {
view.setUint16(offset, newSize, true);
view.setUint16(offset + 2, value.length + 1, true); // value length is in character count
view.setUint16(offset + 4, 1, true);
offset = functions_js_1.roundUp(writeStringWithNullChar(view, offset + 6, key), 4);
offset = functions_js_1.roundUp(writeStringWithNullChar(view, offset, value), 4);
}
});
return bin;
}
function generateStringTableInfo(tables) {
// estimate size
var size = 36; // roundUp(6 + ByteLenWithNull(L'StringFileInfo'), 4)
var tableBins = tables.map(function (table) { return generateStringTable(table); });
// (all table sizes are rounded up)
size += tableBins.reduce(function (p, c) { return p + c.byteLength; }, 0);
var bin = new ArrayBuffer(size);
var view = new DataView(bin);
view.setUint16(0, size, true);
view.setUint16(2, 0, true); // no value length
view.setUint16(4, 1, true);
var offset = functions_js_1.roundUp(writeStringWithNullChar(view, 6, 'StringFileInfo'), 4);
tableBins.forEach(function (table) {
var len = table.byteLength;
functions_js_1.copyBuffer(bin, offset, table, 0, len);
offset += len;
});
return bin;
}
function generateVarFileInfo(translations) {
// estimate size
var size = 32; // roundUp(6 + ByteLenWithNull(L'VarFileInfo'), 4)
// (translation data is fixed length)
var translationsValueSize = translations.length * 4;
size += 32 + translationsValueSize;
var bin = new ArrayBuffer(size);
var view = new DataView(bin);
view.setUint16(0, size, true);
view.setUint16(2, 0, true); // no value length
view.setUint16(4, 1, true);
var offset = functions_js_1.roundUp(writeStringWithNullChar(view, 6, 'VarFileInfo'), 4);
view.setUint16(offset, 32 + translationsValueSize, true);
view.setUint16(offset + 2, translationsValueSize, true);
view.setUint16(offset + 4, 0, true);
offset = functions_js_1.roundUp(writeStringWithNullChar(view, offset + 6, 'Translation'), 4);
translations.forEach(function (translation) {
view.setUint16(offset, translation.lang, true);
view.setUint16(offset + 2, translation.codepage, true);
offset += 4;
});
return bin;
}
function generateVersionEntryBinary(entry) {
var size = 92; // roundUp(6 + ByteLenWithNull(L'VS_VERSION_INFO'), 4) + 52 (sizeof VS_FIXEDFILEINFO)
var stringTableInfoBin = generateStringTableInfo(entry.strings);
var stringTableInfoLen = stringTableInfoBin.byteLength;
size += stringTableInfoLen;
var varFileInfoBin = generateVarFileInfo(entry.translations);
var varFileInfoLen = varFileInfoBin.byteLength;
size += varFileInfoLen;
size = entry.unknowns.reduce(function (p, data) { return p + functions_js_1.roundUp(data.entireBin.byteLength, 4); }, size);
var bin = new ArrayBuffer(size);
var view = new DataView(bin);
view.setUint16(0, size, true);
view.setUint16(2, 52, true);
view.setUint16(4, 0, true); // value is binary
var offset = functions_js_1.roundUp(writeStringWithNullChar(view, 6, 'VS_VERSION_INFO'), 4);
view.setUint32(offset, 0xfeef04bd, true); // signature
view.setUint32(offset + 4, 0x10000, true); // structure version
view.setUint32(offset + 8, entry.fixedInfo.fileVersionMS, true);
view.setUint32(offset + 12, entry.fixedInfo.fileVersionLS, true);
view.setUint32(offset + 16, entry.fixedInfo.productVersionMS, true);
view.setUint32(offset + 20, entry.fixedInfo.productVersionLS, true);
view.setUint32(offset + 24, entry.fixedInfo.fileFlagsMask, true);
view.setUint32(offset + 28, entry.fixedInfo.fileFlags, true);
view.setUint32(offset + 32, entry.fixedInfo.fileOS, true);
view.setUint32(offset + 36, entry.fixedInfo.fileType, true);
view.setUint32(offset + 40, entry.fixedInfo.fileSubtype, true);
view.setUint32(offset + 44, entry.fixedInfo.fileDateMS, true);
view.setUint32(offset + 48, entry.fixedInfo.fileDateLS, true);
offset += 52;
functions_js_1.copyBuffer(bin, offset, stringTableInfoBin, 0, stringTableInfoLen);
offset += stringTableInfoLen;
functions_js_1.copyBuffer(bin, offset, varFileInfoBin, 0, varFileInfoLen);
offset += varFileInfoLen;
entry.unknowns.forEach(function (e) {
var len = e.entireBin.byteLength;
functions_js_1.copyBuffer(bin, offset, e.entireBin, 0, len);
offset += functions_js_1.roundUp(len, 4);
});
return bin;
}
////////////////////////////////////////////////////////////////////////////////
function clampInt(val, min, max) {
if (isNaN(val) || val < min) {
return min;
}
else if (val >= max) {
return max;
}
return Math.floor(val);
}
function parseVersionArguments(arg1, arg2, arg3, arg4, arg5) {
var _a;
var major;
var minor;
var micro;
var revision;
var lang;
if (typeof arg1 === 'string' &&
(typeof arg2 === 'undefined' || typeof arg2 === 'number') &&
typeof arg3 === 'undefined') {
_a = arg1
.split('.')
.map(function (token) { return clampInt(Number(token), 0, 65535); })
// add zeros for missing fields
.concat(0, 0, 0), major = _a[0], minor = _a[1], micro = _a[2], revision = _a[3];
lang = arg2;
}
else {
major = clampInt(Number(arg1), 0, 65535);
minor = clampInt(Number(arg2), 0, 65535);
micro = clampInt(typeof arg3 === 'undefined' ? 0 : Number(arg3), 0, 65535);
revision = clampInt(typeof arg4 === 'undefined' ? 0 : Number(arg4), 0, 65535);
lang = arg5;
}
return [major, minor, micro, revision, lang];
}
////////////////////////////////////////////////////////////////////////////////
/**
* Treats 'Version information' (`VS_VERSIONINFO`) resource data.
*/
var VersionInfo = /** @class */ (function () {
function VersionInfo(entry) {
if (!entry) {
this.data = {
lang: 0,
fixedInfo: createFixedInfo(),
strings: [],
translations: [],
unknowns: [],
};
}
else {
var view = new DataView(entry.bin);
this.data = parseVersionEntry(view, entry);
}
}
/** Returns new `VersionInfo` instance with empty data. */
VersionInfo.createEmpty = function () {
return new VersionInfo();
};
VersionInfo.create = function (arg1, fixedInfo, strings) {
var lang;
if (typeof arg1 === 'object') {
lang = arg1.lang;
fixedInfo = arg1.fixedInfo;
strings = arg1.strings;
}
else {
lang = arg1;
}
var vi = new VersionInfo();
vi.data.lang = lang;
// copy all specified values
// (if unspecified, use default value set by `createFixedInfo`)
for (var _fixedInfoKey in fixedInfo) {
var fixedInfoKey = _fixedInfoKey;
if (fixedInfoKey in fixedInfo) {
var value = fixedInfo[fixedInfoKey];
if (value != null) {
vi.data.fixedInfo[fixedInfoKey] = value;
}
}
}
vi.data.strings = strings.map(function (_a) {
var lang = _a.lang, codepage = _a.codepage, values = _a.values;
return ({
lang: lang,
codepage: codepage,
values: functions_js_1.cloneObject(values),
});
});
vi.data.translations = strings.map(function (_a) {
var lang = _a.lang, codepage = _a.codepage;
return ({ lang: lang, codepage: codepage });
});
return vi;
};
/** Pick up all version-info entries */
VersionInfo.fromEntries = function (entries) {
return entries
.filter(function (e) { return e.type === 16; })
.map(function (e) { return new VersionInfo(e); });
};
Object.defineProperty(VersionInfo.prototype, "lang", {
/** A language value for this resource entry. */
get: function () {
return this.data.lang;
},
set: function (value) {
this.data.lang = value;
},
enumerable: false,
configurable: true
});
Object.defineProperty(VersionInfo.prototype, "fixedInfo", {
/**
* The property of fixed version info, containing file version, product version, etc.
* (data: `VS_FIXEDFILEINFO`)
*
* Although this property is read-only, you can rewrite
* each child fields directly to apply data.
*/
get: function () {
return this.data.fixedInfo;
},
enumerable: false,
configurable: true
});
/**
* Returns all languages that the executable supports. (data: `VarFileInfo`)
*
* Usually the returned array is equal to the one returned by `getAllLanguagesForStringValues`,
* but some resource-generating tools doesn't generate same values.
*/
VersionInfo.prototype.getAvailableLanguages = function () {
return this.data.translations.slice(0);
};
/**
* Replaces all languages that the executable supports.
*/
VersionInfo.prototype.replaceAvailableLanguages = function (languages) {
this.data.translations = languages.slice(0);
};
/**
* Returns all string values for the specified language. (data: values in lang-charset block of `StringFileInfo`)
*/
VersionInfo.prototype.getStringValues = function (language) {
var a = this.data.strings
.filter(function (e) {
return e.lang === language.lang && e.codepage === language.codepage;
})
.map(function (e) { return e.values; });
return a.length > 0 ? a[0] : {};
};
/**
* Returns all languages used by string values. (data: lang-charset name of `StringFileInfo`)
*
* Usually the returned array is equal to the one returned by `getAvailableLanguages`,
* but some resource-generating tools doesn't generate same values.
*/
VersionInfo.prototype.getAllLanguagesForStringValues = function () {
return this.data.strings.map(function (_a) {
var codepage = _a.codepage, lang = _a.lang;
return ({ codepage: codepage, lang: lang });
});
};
/**
* Add or replace the string values.
* @param language language info
* @param values string values (key-value pairs)
* @param addToAvailableLanguage set `true` to add `language` into available languages
* if not existing in `getAvailableLanguages()` (default: `true`)
*/
VersionInfo.prototype.setStringValues = function (language, values, addToAvailableLanguage) {
if (addToAvailableLanguage === void 0) { addToAvailableLanguage = true; }
var a = this.data.strings.filter(function (e) { return e.lang === language.lang && e.codepage === language.codepage; });
var table;
if (a.length === 0) {
table = {
lang: language.lang,
codepage: language.codepage,
values: {},
};
this.data.strings.push(table);
}
else {
table = a[0];
}
for (var key in values) {
var value = values[key];
if (value != null) {
table.values[key] = value;
}
}
if (addToAvailableLanguage) {
// if no translation is available, then add it
var t = this.data.translations.filter(function (e) {
return e.lang === language.lang && e.codepage === language.codepage;
});
if (t.length === 0) {
this.data.translations.push({
lang: language.lang,
codepage: language.codepage,
});
}
}
};
/**
* Add or replace the string value.
* @param language language info
* @param key the key name of string value
* @param value the string value
* @param addToAvailableLanguage set `true` to add `language` into available languages
* if not existing in `getAvailableLanguages()` (default: `true`)
*/
VersionInfo.prototype.setStringValue = function (language, key, value, addToAvailableLanguage) {
var _a;
if (addToAvailableLanguage === void 0) { addToAvailableLanguage = true; }
this.setStringValues(language, (_a = {}, _a[key] = value, _a), addToAvailableLanguage);
};
/**
* Remove all string values for specified language.
* @param language language info
* @param removeFromAvailableLanguage set `true` to remove `language` from available languages
* if existing in `getAvailableLanguages()` (default: `true`)
*/
VersionInfo.prototype.removeAllStringValues = function (language, removeFromAvailableLanguage) {
if (removeFromAvailableLanguage === void 0) { removeFromAvailableLanguage = true; }
var strings = this.data.strings;
var len = strings.length;
for (var i = 0; i < len; ++i) {
var e = strings[i];
if (e != null &&
e.lang === language.lang &&
e.codepage === language.codepage) {
strings.splice(i, 1);
if (removeFromAvailableLanguage) {
var translations = this.data.translations;
for (var j = 0; j < translations.length; j++) {
var t = translations[j];
if (t != null &&
t.lang === language.lang &&
t.codepage === language.codepage) {
translations.splice(j, 1);
break;
}
}
}
break;
}
}
};
/**
* Remove specified string value for specified language.
* @param language language info
* @param key the key name of string value to be removed
* @param removeFromAvailableLanguage set `true` to remove `language` from available languages
* if no more string values exist for `language` (default: `true`)
*/
VersionInfo.prototype.removeStringValue = function (language, key, removeFromAvailableLanguage) {
if (removeFromAvailableLanguage === void 0) { removeFromAvailableLanguage = true; }
var strings = this.data.strings;
var len = strings.length;
for (var i = 0; i < len; ++i) {
var e = strings[i];
if (e != null &&
e.lang === language.lang &&
e.codepage === language.codepage) {
try {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete e.values[key];
}
catch (_ex) { }
if (removeFromAvailableLanguage &&
Object.keys(e.values).length === 0) {
// if no entries are left, remove table and translations
strings.splice(i, 1);
var translations = this.data.translations;
for (var j = 0; j < translations.length; j++) {
var t = translations[j];
if (t != null &&
t.lang === language.lang &&
t.codepage === language.codepage) {
translations.splice(j, 1);
break;
}
}
}
break;
}
}
};
/**
* Creates `Type.ResourceEntry` object for this instance.
* Usually `outputToResourceEntries` is suitable for generating resource data
* into executables, but you can use this method if necessary.
*/
VersionInfo.prototype.generateResource = function () {
var bin = generateVersionEntryBinary(this.data);
return {
type: 16,
id: 1,
lang: this.lang,
codepage: 1200,
bin: bin,
};
};
/**
* Generates version info resource data (using `generateResource()`) and emits into `entries` array.
* If version info resource already exists in `entries`, this method replaces it with the new one.
* @param entries resource entry array for output
*/
VersionInfo.prototype.outputToResourceEntries = function (entries) {
var res = this.generateResource();
var len = entries.length;
for (var i = 0; i < len; ++i) {
var e = entries[i];
if (e != null &&
e.type === 16 &&
e.id === res.id &&
e.lang === res.lang) {
entries[i] = res;
return;
}
}
entries.push(res);
};
// utility methods
VersionInfo.prototype.getDefaultVersionLang = function (propName) {
// first, use `this.lang` if it is a numeric value
var num = Number(this.lang);
if (this.lang !== '' && !isNaN(num)) {
return num;
}
// second, use lang value for propName if there is only one language
var a = this.data.strings
.filter(function (e) { return propName in e.values && e.values[propName] != null; })
.map(function (e) { return e.lang; });
if (a.length === 1) {
return a[0];
}
// use English language
return 1033;
};
VersionInfo.prototype.setFileVersion = function (arg1, arg2, arg3, arg4, arg5) {
this.setFileVersionImpl.apply(this, parseVersionArguments(arg1, arg2, arg3, arg4, arg5));
};
VersionInfo.prototype.setFileVersionImpl = function (major, minor, micro, revision, lang) {
lang =
typeof lang !== 'undefined'
? lang
: this.getDefaultVersionLang('FileVersion');
this.fixedInfo.fileVersionMS = (major << 16) | minor;
this.fixedInfo.fileVersionLS = (micro << 16) | revision;
this.setStringValue({ lang: lang, codepage: 1200 }, 'FileVersion', major + "." + minor + "." + micro + "." + revision, true);
};
VersionInfo.prototype.setProductVersion = function (arg1, arg2, arg3, arg4, arg5) {
this.setProductVersionImpl.apply(this, parseVersionArguments(arg1, arg2, arg3, arg4, arg5));
};
VersionInfo.prototype.setProductVersionImpl = function (major, minor, micro, revision, lang) {
lang =
typeof lang !== 'undefined'
? lang
: this.getDefaultVersionLang('ProductVersion');
this.fixedInfo.productVersionMS = (major << 16) | minor;
this.fixedInfo.productVersionLS = (micro << 16) | revision;
this.setStringValue({ lang: lang, codepage: 1200 }, 'ProductVersion', major + "." + minor + "." + micro + "." + revision, true);
};
return VersionInfo;
}());
exports.default = VersionInfo;

View File

@@ -0,0 +1,11 @@
import { Type } from 'pe-library';
import IconGroupEntry, { IconGroupItem } from './IconGroupEntry.js';
import StringTable from './StringTable.js';
import VersionFileFlags from './VersionFileFlags.js';
import VersionFileOS from './VersionFileOS.js';
import { VersionFileDriverSubtype, VersionFileFontSubtype } from './VersionFileSubtypes.js';
import VersionFileType from './VersionFileType.js';
import VersionInfo, { VersionInfoCreateParam, VersionFixedInfo, VersionStringTable, VersionStringValues, VersionTranslation } from './VersionInfo.js';
declare type ResourceEntry = Type.ResourceEntry;
declare type ResourceEntryBaseType<TType extends string | number, TID extends string | number, TLang extends string | number> = Type.ResourceEntryBaseType<TType, TID, TLang>;
export { IconGroupEntry, IconGroupItem, ResourceEntry, ResourceEntryBaseType, StringTable, VersionInfoCreateParam, VersionFileFlags, VersionFileOS, VersionFileDriverSubtype, VersionFileFontSubtype, VersionFileType, VersionFixedInfo, VersionInfo, VersionStringTable, VersionStringValues, VersionTranslation, };

View File

@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.VersionInfo = exports.VersionFileType = exports.VersionFileFontSubtype = exports.VersionFileDriverSubtype = exports.VersionFileOS = exports.VersionFileFlags = exports.StringTable = exports.IconGroupEntry = void 0;
var IconGroupEntry_js_1 = require("./IconGroupEntry.js");
exports.IconGroupEntry = IconGroupEntry_js_1.default;
var StringTable_js_1 = require("./StringTable.js");
exports.StringTable = StringTable_js_1.default;
var VersionFileFlags_js_1 = require("./VersionFileFlags.js");
exports.VersionFileFlags = VersionFileFlags_js_1.default;
var VersionFileOS_js_1 = require("./VersionFileOS.js");
exports.VersionFileOS = VersionFileOS_js_1.default;
var VersionFileSubtypes_js_1 = require("./VersionFileSubtypes.js");
Object.defineProperty(exports, "VersionFileDriverSubtype", { enumerable: true, get: function () { return VersionFileSubtypes_js_1.VersionFileDriverSubtype; } });
Object.defineProperty(exports, "VersionFileFontSubtype", { enumerable: true, get: function () { return VersionFileSubtypes_js_1.VersionFileFontSubtype; } });
var VersionFileType_js_1 = require("./VersionFileType.js");
exports.VersionFileType = VersionFileType_js_1.default;
var VersionInfo_js_1 = require("./VersionInfo.js");
exports.VersionInfo = VersionInfo_js_1.default;