securityos/node_modules/resedit/dist/resource/IconGroupEntry.js

293 lines
11 KiB
JavaScript

import IconItem from '../data/IconItem.js';
import RawIconItem from '../data/RawIconItem.js';
import { readUint8WithLastOffset, readUint16WithLastOffset, readUint32WithLastOffset, } from '../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; i < filteredIDs.length; ++i) {
var id = filteredIDs[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: readUint8WithLastOffset(view, offset, totalSize),
height: readUint8WithLastOffset(view, offset + 1, totalSize),
colors: readUint8WithLastOffset(view, offset + 2, totalSize),
planes: readUint16WithLastOffset(view, offset + 4, totalSize),
bitCount: readUint16WithLastOffset(view, offset + 6, totalSize),
dataSize: readUint32WithLastOffset(view, offset + 8, totalSize),
iconID: 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.from(bin);
}
else {
var c = item.icon;
return RawIconItem.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.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;
}());
export default IconGroupEntry;