@shockpkg/ria-packager
Version:
Package for creating Adobe AIR packages
702 lines (562 loc) • 14.6 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
/* eslint-disable max-classes-per-file */
import { deflateRaw } from 'zlib';
import bufferCrc32 from 'buffer-crc32';
/**
* Convert Date object or timestamp to DOS date and time values.
*
* @param date Data object or timestamp.
* @returns Date and time values.
*/
function dateToDosTime(date) {
const d = typeof date === 'number' ? new Date(date * 1000) : date;
return {
date: // eslint-disable-next-line no-bitwise
d.getDate() & 0x1F | (d.getMonth() + 1 & 0xF) << 5 | (d.getFullYear() - 1980 & 0x7F) << 9,
time: // eslint-disable-next-line no-bitwise
Math.floor(d.getSeconds() / 2) | (d.getMinutes() & 0x3F) << 5 | (d.getHours() & 0x1F) << 11
};
}
/**
* Zipper Entry Extra Field.
*/
export class ZipperEntryExtraField extends Object {
/**
* Type ID.
*/
/**
* Data for the type.
*/
constructor() {
super();
_defineProperty(this, "type", 0);
_defineProperty(this, "data", null);
}
/**
* Encode type and data as buffer.
*
* @returns Buffer data.
*/
toBuffer() {
const {
data
} = this;
const b = Buffer.alloc(4);
b.writeUInt16LE(this.type, 0);
if (data) {
b.writeUInt16LE(data.length, 2);
}
return data ? Buffer.concat([b, data]) : b;
}
/**
* Init Info-ZIP UNIX type 2 data, local header.
*
* @param uid User ID.
* @param gid Group ID.
*/
initInfoZipUnix2Local(uid = 0, gid = 0) {
this._initInfoZipUnix2(true, uid, gid);
}
/**
* Init Info-ZIP UNIX type 2 data, central header.
*
* @param uid User ID.
* @param gid Group ID.
*/
initInfoZipUnix2Central(uid = 0, gid = 0) {
this._initInfoZipUnix2(false, uid, gid);
}
/**
* Init Extended Timestamp data, local header.
*
* @param mtime Modification time.
* @param atime Access time.
* @param ctime Creation time.
*/
initExtendedTimestampLocal(mtime = null, atime = null, ctime = null) {
this._initExtendedTimestamp(true, mtime, atime, ctime);
}
/**
* Init Extended Timestamp data, central header.
*
* @param mtime Modification time.
* @param atime Access time.
* @param ctime Creation time.
*/
initExtendedTimestampCentral(mtime = null, atime = null, ctime = null) {
this._initExtendedTimestamp(false, mtime, atime, ctime);
}
/**
* Init Info-ZIP UNIX type 2 data.
*
* @param local Local header or central.
* @param uid User ID.
* @param gid Group ID.
*/
_initInfoZipUnix2(local, uid, gid) {
const d = local ? Buffer.alloc(4) : null;
if (d) {
d.writeUInt16LE(uid, 0);
d.writeUInt16LE(gid, 2);
} // Type: 'Ux'
this.type = 0x7855;
this.data = d;
}
/**
* Init Extended Timestamp data.
*
* @param local Local header or central.
* @param mtime Modification time.
* @param atime Access time.
* @param ctime Creation time.
*/
_initExtendedTimestamp(local, mtime, atime, ctime) {
let flags = 0;
const flagsB = Buffer.alloc(1);
const buffers = [flagsB];
[mtime, atime, ctime].forEach((v, i) => {
if (v === null) {
return;
} // eslint-disable-next-line no-bitwise
flags |= 1 << i;
if (!local && !i) {
return;
}
const time = typeof v === 'number' ? v : Math.round(v.getTime() / 1000);
const b = Buffer.alloc(4);
b.writeUInt32LE(time, 0);
buffers.push(b);
});
flagsB.writeUInt8(flags, 0); // Type: 'UT'
this.type = 0x5455;
this.data = Buffer.concat(buffers);
}
}
/**
* Zipper Entry.
*/
export class ZipperEntry extends Object {
/**
* Tag signature, local header.
*/
/**
* Tag signature, central header.
*/
/**
* Extract version.
*/
/**
* Extract host OS.
*/
/**
* Create version.
*/
/**
* Create host OS.
*/
/**
* Extract flags.
*/
/**
* Compression type.
*/
/**
* DOS time.
*/
/**
* DOS date.
*/
/**
* Data CRC32.
*/
/**
* Size compressed.
*/
/**
* Size uncompressed.
*/
/**
* Disk number start.
*/
/**
* Internal attributes.
*/
/**
* External attributes.
*/
/**
* Header offset, local header.
*/
/**
* Entry path.
*/
/**
* Entry comment.
*/
/**
* Extra fields, local header.
*/
/**
* Extra fields, central header.
*/
constructor() {
super();
_defineProperty(this, "signatureLocal", 0x4034B50);
_defineProperty(this, "signatureCentral", 0x2014B50);
_defineProperty(this, "extractVersion", 0x14);
_defineProperty(this, "extractHostOS", 0);
_defineProperty(this, "createVersion", 0x14);
_defineProperty(this, "createHostOS", 0);
_defineProperty(this, "flags", 0);
_defineProperty(this, "compression", 0);
_defineProperty(this, "time", 0);
_defineProperty(this, "date", 0);
_defineProperty(this, "crc32", 0);
_defineProperty(this, "sizeCompressed", 0);
_defineProperty(this, "sizeUncompressed", 0);
_defineProperty(this, "diskNumberStart", 0);
_defineProperty(this, "internalAttributes", 0);
_defineProperty(this, "externalAttributes", 0);
_defineProperty(this, "headerOffsetLocal", 0);
_defineProperty(this, "path", '');
_defineProperty(this, "comment", '');
_defineProperty(this, "extraFieldsLocal", []);
_defineProperty(this, "extraFieldsCentral", []);
}
/**
* Create new ZipperEntryExtraField object.
*
* @returns ZipperEntryExtraField object.
*/
createExtraField() {
return new ZipperEntryExtraField();
}
/**
* Set date from a date object or timestamp.
*
* @param date Date object or timestamp.
*/
setDate(date) {
const dosTime = this._dateToDosTime(date);
this.date = dosTime.date;
this.time = dosTime.time;
}
/**
* Get path as data.
*
* @returns Path as data buffer.
*/
getPathBuffer() {
return Buffer.from(this.path, 'utf8');
}
/**
* Get comment as data.
*
* @returns Comment as data buffer.
*/
getCommentBuffer() {
return Buffer.from(this.comment, 'utf8');
}
/**
* Get the file record extra fields as data.
*
* @returns Extra fields as data.
*/
getExtraFieldsLocalBuffer() {
return Buffer.concat(this.extraFieldsLocal.map(e => e.toBuffer()));
}
/**
* Get the director entry extra fields as data.
*
* @returns Extra fields as data.
*/
getExtraFieldsCentralBuffer() {
return Buffer.concat(this.extraFieldsCentral.map(e => e.toBuffer()));
}
/**
* Get file record data.
*
* @returns File record data.
*/
getLocalBuffer() {
const pathBuffer = this.getPathBuffer();
const extraFieldsBuffer = this.getExtraFieldsLocalBuffer();
const head = Buffer.alloc(30);
head.writeUInt32LE(this.signatureLocal, 0);
head.writeUInt8(this.extractVersion, 4);
head.writeUInt8(this.extractHostOS, 5);
head.writeUInt16LE(this.flags, 6);
head.writeUInt16LE(this.compression, 8);
head.writeUInt16LE(this.time, 10);
head.writeUInt16LE(this.date, 12);
head.writeUInt32LE(this.crc32, 14);
head.writeUInt32LE(this.sizeCompressed, 18);
head.writeUInt32LE(this.sizeUncompressed, 22);
head.writeUInt16LE(pathBuffer.length, 26);
head.writeUInt16LE(extraFieldsBuffer.length, 28);
return Buffer.concat([head, pathBuffer, extraFieldsBuffer]);
}
/**
* Get directory entry data.
*
* @returns Directory entry data.
*/
getCentralBuffer() {
const pathBuffer = this.getPathBuffer();
const extraFieldsBuffer = this.getExtraFieldsCentralBuffer();
const commentBuffer = this.getCommentBuffer();
const head = Buffer.alloc(46);
head.writeUInt32LE(this.signatureCentral, 0);
head.writeUInt8(this.createVersion, 4);
head.writeUInt8(this.createHostOS, 5);
head.writeUInt8(this.extractVersion, 6);
head.writeUInt8(this.extractHostOS, 7);
head.writeUInt16LE(this.flags, 8);
head.writeUInt16LE(this.compression, 10);
head.writeUInt16LE(this.time, 12);
head.writeUInt16LE(this.date, 14);
head.writeUInt32LE(this.crc32, 16);
head.writeUInt32LE(this.sizeCompressed, 20);
head.writeUInt32LE(this.sizeUncompressed, 24);
head.writeUInt16LE(pathBuffer.length, 28);
head.writeUInt16LE(extraFieldsBuffer.length, 30);
head.writeUInt16LE(commentBuffer.length, 32);
head.writeUInt16LE(this.diskNumberStart, 34);
head.writeUInt16LE(this.internalAttributes, 36);
head.writeUInt32LE(this.externalAttributes, 38);
head.writeUInt32LE(this.headerOffsetLocal, 42);
return Buffer.concat([head, pathBuffer, extraFieldsBuffer, commentBuffer]);
}
/**
* Setup data for entry.
*
* @param data Data for the entry.
* @param compress Compress option, true to force, false to disable.
* @returns Resulting data, or null if no data passed.
*/
async initData(data, compress = null) {
this.compression = 0;
this.crc32 = 0;
this.sizeCompressed = 0;
this.sizeUncompressed = 0;
if (!data) {
return null;
}
const crc32 = this._bufferCrc32(data);
if (compress === false) {
this.crc32 = crc32;
this.sizeCompressed = this.sizeUncompressed = data.length;
this.compression = 0;
return data;
}
if (compress === true) {
const comp = await this._zlibDeflateRaw(data);
this.crc32 = crc32;
this.sizeUncompressed = data.length;
this.sizeCompressed = comp.length;
this.compression = 8;
return comp;
}
const comp = await this._zlibDeflateRaw(data);
const r = comp.length < data.length ? comp : data;
this.crc32 = crc32;
this.sizeUncompressed = data.length;
this.sizeCompressed = r.length;
this.compression = r === data ? 0 : 8;
return r;
}
/**
* Add extra fields for Extended Timestamp.
*
* @param mtime Modification time.
* @param atime Access time.
* @param ctime Creation time.
*/
addExtraFieldsExtendedTimestamp(mtime = null, atime = null, ctime = null) {
const efl = this.createExtraField();
efl.initExtendedTimestampLocal(mtime, atime, ctime);
this.extraFieldsLocal.push(efl);
const efc = this.createExtraField();
efc.initExtendedTimestampCentral(mtime, atime, ctime);
this.extraFieldsCentral.push(efc);
}
/**
* Add extra fields for Info-ZIP UNIX type 2.
*
* @param uid User ID.
* @param gid Group ID.
*/
addExtraFieldsInfoZipUnix2(uid = 0, gid = 0) {
const efl = this.createExtraField();
efl.initInfoZipUnix2Local(uid, gid);
this.extraFieldsLocal.push(efl);
const efc = this.createExtraField();
efc.initInfoZipUnix2Central(uid, gid);
this.extraFieldsCentral.push(efc);
}
/**
* Convert date from a date object or timestamp.
*
* @param date Date object or timestamp.
* @returns DOS time.
*/
_dateToDosTime(date) {
return dateToDosTime(date);
}
/**
* Calculate the CRC32 hash for data.
*
* @param data Data to be hashed.
* @returns CRC32 hash.
*/
_bufferCrc32(data) {
return bufferCrc32.unsigned(data);
}
/**
* Zlib deflate raw data.
*
* @param data Data to be compressed.
* @returns Compressed data.
*/
async _zlibDeflateRaw(data) {
return new Promise((resolve, reject) => {
deflateRaw(data, (err, comp) => {
if (err) {
reject(err);
return;
}
resolve(comp);
});
});
}
}
/**
* Zipper, a low-level ZIP file writter.
*
* @param output Writable stream.
*/
export class Zipper extends Object {
/**
* Tag signature.
*/
/**
* Archive comment.
*/
/**
* Added entries.
*/
/**
* Current offset.
*/
/**
* Output stream.
*/
constructor(output) {
super();
_defineProperty(this, "signature", 0x6054B50);
_defineProperty(this, "comment", '');
_defineProperty(this, "entries", []);
_defineProperty(this, "_offset", 0);
_defineProperty(this, "_output", void 0);
this._output = output;
}
/**
* Create new ZipperEntry object.
*
* @returns ZipperEntry object.
*/
createEntry() {
return new ZipperEntry();
}
/**
* Get comment as data.
*
* @returns Comment data.
*/
getCommentBuffer() {
return Buffer.from(this.comment, 'utf8');
}
/**
* Get directory buffer data.
*
* @returns Directory data.
*/
getDirectoryBuffer() {
const {
_offset,
entries
} = this;
const directoryData = Buffer.concat(entries.map(e => e.getCentralBuffer()));
const commentBuffer = this.getCommentBuffer();
const end = Buffer.alloc(22);
end.writeUInt32LE(this.signature, 0);
end.writeUInt16LE(0, 4);
end.writeUInt16LE(0, 6);
end.writeUInt16LE(entries.length, 8);
end.writeUInt16LE(entries.length, 10);
end.writeUInt32LE(directoryData.length, 12);
end.writeUInt32LE(_offset, 16);
end.writeUInt16LE(commentBuffer.length, 20);
return Buffer.concat([directoryData, end, commentBuffer]);
}
/**
* Add Entry and any associated data.
*
* @param entry Entry object.
* @param data Data from the entry initData method.
*/
async addEntry(entry, data = null) {
const {
_offset
} = this;
const {
sizeCompressed
} = entry;
if (data) {
if (data.length !== sizeCompressed) {
throw new Error('Data length and compressed size must match');
}
} else if (sizeCompressed) {
throw new Error('Data required when compressed size not zero');
}
entry.headerOffsetLocal = _offset;
this.entries.push(entry);
await this._writeOutput(entry.getLocalBuffer());
if (data) {
await this._writeOutput(data);
}
}
/**
* Close stream.
*/
async close() {
await this._writeOutput(this.getDirectoryBuffer());
await new Promise((resolve, reject) => {
this._output.end(err => {
if (err) {
reject(err);
return;
}
resolve();
});
});
}
/**
* Write data buffer to output stream.
*
* @param data Data buffer.
*/
async _writeOutput(data) {
await new Promise((resolve, reject) => {
this._output.write(data, err => {
if (err) {
reject(err);
return;
}
resolve();
});
});
this._offset += data.length;
}
}
//# sourceMappingURL=zipper.mjs.map