node-apk
Version:
A library to parse Android application manifest and signature
154 lines (152 loc) • 6.3 kB
JavaScript
"use strict";
/*Copyright (c) 2019 XdevL. All rights reserved.
This work is licensed under the terms of the MIT license.
For a copy, see <https://opensource.org/licenses/MIT>.*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Resources = exports.Resource = exports.Locale = void 0;
/*Reference implementation found at:
frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h
frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.cpp*/
var common_1 = require("./common");
var Table = /** @class */ (function () {
function Table(chunk) {
this.packageCount = chunk.headerSource.readUInt();
this.stringPool = new common_1.StringPool(new common_1.Chunk(chunk.chunkSource, common_1.ChunkType.STRING_POOL));
this.packages = new Map();
for (var i = 0; i < this.packageCount; ++i) {
var innerChunk = new common_1.Chunk(chunk.chunkSource);
if (innerChunk.type === common_1.ChunkType.TABLE_PACKAGE) {
var tablePackageChunk = new TablePackageChunk(innerChunk, this.stringPool);
this.packages.set(tablePackageChunk.id, tablePackageChunk);
}
}
}
return Table;
}());
var TablePackageChunk = /** @class */ (function () {
function TablePackageChunk(chunk, stringPool) {
this.id = chunk.headerSource.readUInt();
this.name = chunk.headerSource.readUtf16String(128 * 2);
this.typeStrings = chunk.headerSource.readUInt();
this.lastPublicType = chunk.headerSource.readUInt();
this.keyStrings = chunk.headerSource.readUInt();
this.lastPublicKey = chunk.headerSource.readUInt();
this.types = new Map();
while (chunk.chunkSource.getCursorAndMove(0) < chunk.chunkSize - chunk.headerSize) {
var innerChunk = new common_1.Chunk(chunk.chunkSource);
if (innerChunk.type === common_1.ChunkType.TABLE_TYPE) {
var tableTypeChunk = new TableTypeChunk(innerChunk, stringPool);
TablePackageChunk.getOrCreate(this.types, tableTypeChunk.id).push(tableTypeChunk);
}
}
}
TablePackageChunk.getOrCreate = function (map, key) {
return (map.has(key) ? map : map.set(key, [])).get(key);
};
return TablePackageChunk;
}());
var TableTypeChunk = /** @class */ (function () {
function TableTypeChunk(chunk, stringPool) {
this.id = chunk.headerSource.readUByte();
this.flags = chunk.headerSource.readUByte();
this.reserved = chunk.headerSource.readUShort();
this.entryCount = chunk.headerSource.readUInt();
this.entriesStart = chunk.headerSource.readUInt();
this.resTableConfig = new ResTableConfig(chunk);
var indexes = [];
for (var i = 0; i < this.entryCount; ++i) {
indexes.push(chunk.chunkSource.readUInt());
}
this.entries = new Map();
for (var i = 0; i < indexes.length; ++i) {
if (indexes[i] !== TableTypeChunk.NO_ENTRY) {
chunk.chunkSource.moveAt(this.entriesStart - chunk.headerSize + indexes[i]);
this.entries.set(i, new TableEntry(chunk.chunkSource, stringPool));
}
}
}
TableTypeChunk.prototype.resolve = function (index) {
return (this.entries.get(index) || {}).value;
};
TableTypeChunk.NO_ENTRY = 0xffffffff;
return TableTypeChunk;
}());
var ResTableConfig = /** @class */ (function () {
function ResTableConfig(chunk) {
this.size = chunk.headerSource.readUInt();
this.imsi = chunk.headerSource.readUInt();
this.locale = new Locale(chunk.headerSource);
this.screenType = chunk.headerSource.readUInt();
this.input = chunk.headerSource.readUInt();
this.screenSize = chunk.headerSource.readUInt();
this.version = chunk.headerSource.readUInt();
}
return ResTableConfig;
}());
var Locale = /** @class */ (function () {
function Locale(source) {
this.languageCode = source.readUtf8String(2);
this.countryCode = source.readUtf8String(2);
}
Locale.prototype.convertCode = function (code) {
return code !== Locale.EMPTY_CODE ? code : undefined;
};
Object.defineProperty(Locale.prototype, "language", {
get: function () {
return this.convertCode(this.languageCode);
},
enumerable: false,
configurable: true
});
Object.defineProperty(Locale.prototype, "country", {
get: function () {
return this.convertCode(this.countryCode);
},
enumerable: false,
configurable: true
});
Locale.EMPTY_CODE = "\u0000\u0000";
return Locale;
}());
exports.Locale = Locale;
var TableEntry = /** @class */ (function () {
function TableEntry(source, stringPool) {
this.size = source.readUShort();
this.flags = source.readUShort();
this.index = source.readUInt();
this.value = this.flags % 2 === 0 ? (0, common_1.parseResourceValue)(source, stringPool) : null;
}
return TableEntry;
}());
var Resource = /** @class */ (function () {
function Resource(value, locale) {
this.value = value;
if (locale.language || locale.country) {
this.locale = locale;
}
}
return Resource;
}());
exports.Resource = Resource;
var Resources = /** @class */ (function () {
function Resources(source) {
var chunk = new common_1.Chunk(source, common_1.ChunkType.TABLE);
this.table = new Table(chunk);
}
Resources.prototype.resolve = function (id) {
var packageId = Math.floor(id / Math.pow(2, 24)) % Math.pow(2, 8);
var typeId = Math.floor(id / Math.pow(2, 16)) % Math.pow(2, 8);
var index = id % Math.pow(2, 16);
var packageResources = this.table.packages.get(packageId);
if (packageResources) {
var types = packageResources.types.get(typeId);
if (types) {
return types.map(function (type) { return new Resource(type.resolve(index), type.resTableConfig.locale); })
.filter(function (resource) { return !!resource.value; });
}
}
return [];
};
return Resources;
}());
exports.Resources = Resources;