yeet
This commit is contained in:
456
node_modules/bplist-creator/bplistCreator.js
generated
vendored
Normal file
456
node_modules/bplist-creator/bplistCreator.js
generated
vendored
Normal file
@ -0,0 +1,456 @@
|
||||
'use strict';
|
||||
|
||||
// adapted from http://code.google.com/p/plist/source/browse/trunk/src/main/java/com/dd/plist/BinaryPropertyListWriter.java
|
||||
|
||||
var streamBuffers = require("stream-buffers");
|
||||
|
||||
var debug = false;
|
||||
|
||||
function Real(value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
module.exports = function(dicts) {
|
||||
var buffer = new streamBuffers.WritableStreamBuffer();
|
||||
buffer.write(new Buffer("bplist00"));
|
||||
|
||||
if (debug) {
|
||||
console.log('create', require('util').inspect(dicts, false, 10));
|
||||
}
|
||||
|
||||
if (dicts instanceof Array && dicts.length === 1) {
|
||||
dicts = dicts[0];
|
||||
}
|
||||
|
||||
var entries = toEntries(dicts);
|
||||
if (debug) {
|
||||
console.log('entries', entries);
|
||||
}
|
||||
var idSizeInBytes = computeIdSizeInBytes(entries.length);
|
||||
var offsets = [];
|
||||
var offsetSizeInBytes;
|
||||
var offsetTableOffset;
|
||||
|
||||
updateEntryIds();
|
||||
|
||||
entries.forEach(function(entry, entryIdx) {
|
||||
offsets[entryIdx] = buffer.size();
|
||||
if (!entry) {
|
||||
buffer.write(0x00);
|
||||
} else {
|
||||
write(entry);
|
||||
}
|
||||
});
|
||||
|
||||
writeOffsetTable();
|
||||
writeTrailer();
|
||||
return buffer.getContents();
|
||||
|
||||
function updateEntryIds() {
|
||||
var strings = {};
|
||||
var entryId = 0;
|
||||
entries.forEach(function(entry) {
|
||||
if (entry.id) {
|
||||
return;
|
||||
}
|
||||
if (entry.type === 'string') {
|
||||
if (!entry.bplistOverride && strings.hasOwnProperty(entry.value)) {
|
||||
entry.type = 'stringref';
|
||||
entry.id = strings[entry.value];
|
||||
} else {
|
||||
strings[entry.value] = entry.id = entryId++;
|
||||
}
|
||||
} else {
|
||||
entry.id = entryId++;
|
||||
}
|
||||
});
|
||||
|
||||
entries = entries.filter(function(entry) {
|
||||
return (entry.type !== 'stringref');
|
||||
});
|
||||
}
|
||||
|
||||
function writeTrailer() {
|
||||
if (debug) {
|
||||
console.log('0x' + buffer.size().toString(16), 'writeTrailer');
|
||||
}
|
||||
// 6 null bytes
|
||||
buffer.write(new Buffer([0, 0, 0, 0, 0, 0]));
|
||||
|
||||
// size of an offset
|
||||
if (debug) {
|
||||
console.log('0x' + buffer.size().toString(16), 'writeTrailer(offsetSizeInBytes):', offsetSizeInBytes);
|
||||
}
|
||||
writeByte(offsetSizeInBytes);
|
||||
|
||||
// size of a ref
|
||||
if (debug) {
|
||||
console.log('0x' + buffer.size().toString(16), 'writeTrailer(offsetSizeInBytes):', idSizeInBytes);
|
||||
}
|
||||
writeByte(idSizeInBytes);
|
||||
|
||||
// number of objects
|
||||
if (debug) {
|
||||
console.log('0x' + buffer.size().toString(16), 'writeTrailer(number of objects):', entries.length);
|
||||
}
|
||||
writeLong(entries.length);
|
||||
|
||||
// top object
|
||||
if (debug) {
|
||||
console.log('0x' + buffer.size().toString(16), 'writeTrailer(top object)');
|
||||
}
|
||||
writeLong(0);
|
||||
|
||||
// offset table offset
|
||||
if (debug) {
|
||||
console.log('0x' + buffer.size().toString(16), 'writeTrailer(offset table offset):', offsetTableOffset);
|
||||
}
|
||||
writeLong(offsetTableOffset);
|
||||
}
|
||||
|
||||
function writeOffsetTable() {
|
||||
if (debug) {
|
||||
console.log('0x' + buffer.size().toString(16), 'writeOffsetTable');
|
||||
}
|
||||
offsetTableOffset = buffer.size();
|
||||
offsetSizeInBytes = computeOffsetSizeInBytes(offsetTableOffset);
|
||||
offsets.forEach(function(offset) {
|
||||
writeBytes(offset, offsetSizeInBytes);
|
||||
});
|
||||
}
|
||||
|
||||
function write(entry) {
|
||||
switch (entry.type) {
|
||||
case 'dict':
|
||||
writeDict(entry);
|
||||
break;
|
||||
case 'number':
|
||||
case 'double':
|
||||
writeNumber(entry);
|
||||
break;
|
||||
case 'UID':
|
||||
writeUID(entry);
|
||||
break;
|
||||
case 'array':
|
||||
writeArray(entry);
|
||||
break;
|
||||
case 'boolean':
|
||||
writeBoolean(entry);
|
||||
break;
|
||||
case 'string':
|
||||
case 'string-utf16':
|
||||
writeString(entry);
|
||||
break;
|
||||
case 'date':
|
||||
writeDate(entry);
|
||||
break;
|
||||
case 'data':
|
||||
writeData(entry);
|
||||
break;
|
||||
default:
|
||||
throw new Error("unhandled entry type: " + entry.type);
|
||||
}
|
||||
}
|
||||
|
||||
function writeDate(entry) {
|
||||
writeByte(0x33);
|
||||
var date = (Date.parse(entry.value)/1000) - 978307200
|
||||
writeDouble(date)
|
||||
}
|
||||
|
||||
function writeDict(entry) {
|
||||
if (debug) {
|
||||
var keysStr = entry.entryKeys.map(function(k) {return k.id;});
|
||||
var valsStr = entry.entryValues.map(function(k) {return k.id;});
|
||||
console.log('0x' + buffer.size().toString(16), 'writeDict', '(id: ' + entry.id + ')', '(keys: ' + keysStr + ')', '(values: ' + valsStr + ')');
|
||||
}
|
||||
writeIntHeader(0xD, entry.entryKeys.length);
|
||||
entry.entryKeys.forEach(function(entry) {
|
||||
writeID(entry.id);
|
||||
});
|
||||
entry.entryValues.forEach(function(entry) {
|
||||
writeID(entry.id);
|
||||
});
|
||||
}
|
||||
|
||||
function writeNumber(entry) {
|
||||
if (debug) {
|
||||
console.log('0x' + buffer.size().toString(16), 'writeNumber', entry.value, ' (type: ' + entry.type + ')', '(id: ' + entry.id + ')');
|
||||
}
|
||||
|
||||
if (entry.type !== 'double' && parseFloat(entry.value.toFixed()) == entry.value) {
|
||||
if (entry.value < 0) {
|
||||
writeByte(0x13);
|
||||
writeBytes(entry.value, 8, true);
|
||||
} else if (entry.value <= 0xff) {
|
||||
writeByte(0x10);
|
||||
writeBytes(entry.value, 1);
|
||||
} else if (entry.value <= 0xffff) {
|
||||
writeByte(0x11);
|
||||
writeBytes(entry.value, 2);
|
||||
} else if (entry.value <= 0xffffffff) {
|
||||
writeByte(0x12);
|
||||
writeBytes(entry.value, 4);
|
||||
} else {
|
||||
writeByte(0x14);
|
||||
writeBytes(entry.value, 8);
|
||||
}
|
||||
} else {
|
||||
writeByte(0x23);
|
||||
writeDouble(entry.value);
|
||||
}
|
||||
}
|
||||
|
||||
function writeUID(entry) {
|
||||
if (debug) {
|
||||
console.log('0x' + buffer.size().toString(16), 'writeUID', entry.value, ' (type: ' + entry.type + ')', '(id: ' + entry.id + ')');
|
||||
}
|
||||
|
||||
writeIntHeader(0x8, 0x0);
|
||||
writeID(entry.value);
|
||||
}
|
||||
|
||||
function writeArray(entry) {
|
||||
if (debug) {
|
||||
console.log('0x' + buffer.size().toString(16), 'writeArray (length: ' + entry.entries.length + ')', '(id: ' + entry.id + ')');
|
||||
}
|
||||
writeIntHeader(0xA, entry.entries.length);
|
||||
entry.entries.forEach(function(e) {
|
||||
writeID(e.id);
|
||||
});
|
||||
}
|
||||
|
||||
function writeBoolean(entry) {
|
||||
if (debug) {
|
||||
console.log('0x' + buffer.size().toString(16), 'writeBoolean', entry.value, '(id: ' + entry.id + ')');
|
||||
}
|
||||
writeByte(entry.value ? 0x09 : 0x08);
|
||||
}
|
||||
|
||||
function writeString(entry) {
|
||||
if (debug) {
|
||||
console.log('0x' + buffer.size().toString(16), 'writeString', entry.value, '(id: ' + entry.id + ')');
|
||||
}
|
||||
if (entry.type === 'string-utf16' || mustBeUtf16(entry.value)) {
|
||||
var utf16 = new Buffer(entry.value, 'ucs2');
|
||||
writeIntHeader(0x6, utf16.length / 2);
|
||||
// needs to be big endian so swap the bytes
|
||||
for (var i = 0; i < utf16.length; i += 2) {
|
||||
var t = utf16[i + 0];
|
||||
utf16[i + 0] = utf16[i + 1];
|
||||
utf16[i + 1] = t;
|
||||
}
|
||||
buffer.write(utf16);
|
||||
} else {
|
||||
var utf8 = new Buffer(entry.value, 'ascii');
|
||||
writeIntHeader(0x5, utf8.length);
|
||||
buffer.write(utf8);
|
||||
}
|
||||
}
|
||||
|
||||
function writeData(entry) {
|
||||
if (debug) {
|
||||
console.log('0x' + buffer.size().toString(16), 'writeData', entry.value, '(id: ' + entry.id + ')');
|
||||
}
|
||||
writeIntHeader(0x4, entry.value.length);
|
||||
buffer.write(entry.value);
|
||||
}
|
||||
|
||||
function writeLong(l) {
|
||||
writeBytes(l, 8);
|
||||
}
|
||||
|
||||
function writeByte(b) {
|
||||
buffer.write(new Buffer([b]));
|
||||
}
|
||||
|
||||
function writeDouble(v) {
|
||||
var buf = new Buffer(8);
|
||||
buf.writeDoubleBE(v, 0);
|
||||
buffer.write(buf);
|
||||
}
|
||||
|
||||
function writeIntHeader(kind, value) {
|
||||
if (value < 15) {
|
||||
writeByte((kind << 4) + value);
|
||||
} else if (value < 256) {
|
||||
writeByte((kind << 4) + 15);
|
||||
writeByte(0x10);
|
||||
writeBytes(value, 1);
|
||||
} else if (value < 65536) {
|
||||
writeByte((kind << 4) + 15);
|
||||
writeByte(0x11);
|
||||
writeBytes(value, 2);
|
||||
} else {
|
||||
writeByte((kind << 4) + 15);
|
||||
writeByte(0x12);
|
||||
writeBytes(value, 4);
|
||||
}
|
||||
}
|
||||
|
||||
function writeID(id) {
|
||||
writeBytes(id, idSizeInBytes);
|
||||
}
|
||||
|
||||
function writeBytes(value, bytes, is_signedint) {
|
||||
// write low-order bytes big-endian style
|
||||
var buf = new Buffer(bytes);
|
||||
var z = 0;
|
||||
|
||||
// javascript doesn't handle large numbers
|
||||
if(!is_signedint) {
|
||||
while (bytes > 4) {
|
||||
buf[z++] = 0;
|
||||
bytes--;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = bytes - 1; i >= 0; i--) {
|
||||
buf[z++] = value >> (8 * i);
|
||||
}
|
||||
buffer.write(buf);
|
||||
}
|
||||
|
||||
function mustBeUtf16(string) {
|
||||
return Buffer.byteLength(string, 'utf8') != string.length;
|
||||
}
|
||||
};
|
||||
|
||||
function toEntries(dicts) {
|
||||
if (dicts.bplistOverride) {
|
||||
return [dicts];
|
||||
}
|
||||
|
||||
if (dicts instanceof Array) {
|
||||
return toEntriesArray(dicts);
|
||||
} else if (dicts instanceof Buffer) {
|
||||
return [
|
||||
{
|
||||
type: 'data',
|
||||
value: dicts
|
||||
}
|
||||
];
|
||||
} else if (dicts instanceof Real) {
|
||||
return [
|
||||
{
|
||||
type: 'double',
|
||||
value: dicts.value
|
||||
}
|
||||
];
|
||||
} else if (typeof(dicts) === 'object') {
|
||||
if (dicts instanceof Date) {
|
||||
return [
|
||||
{
|
||||
type: 'date',
|
||||
value: dicts
|
||||
}
|
||||
]
|
||||
} else if (Object.keys(dicts).length == 1 && typeof(dicts.UID) === 'number') {
|
||||
return [
|
||||
{
|
||||
type: 'UID',
|
||||
value: dicts.UID
|
||||
}
|
||||
]
|
||||
} else {
|
||||
return toEntriesObject(dicts);
|
||||
}
|
||||
} else if (typeof(dicts) === 'string') {
|
||||
return [
|
||||
{
|
||||
type: 'string',
|
||||
value: dicts
|
||||
}
|
||||
];
|
||||
} else if (typeof(dicts) === 'number') {
|
||||
return [
|
||||
{
|
||||
type: 'number',
|
||||
value: dicts
|
||||
}
|
||||
];
|
||||
} else if (typeof(dicts) === 'boolean') {
|
||||
return [
|
||||
{
|
||||
type: 'boolean',
|
||||
value: dicts
|
||||
}
|
||||
];
|
||||
} else if (typeof(dicts) === 'bigint') {
|
||||
return [
|
||||
{
|
||||
type: 'number',
|
||||
value: Number(BigInt.asIntN(32, dicts))
|
||||
}
|
||||
];
|
||||
} else {
|
||||
throw new Error('unhandled entry: ' + dicts);
|
||||
}
|
||||
}
|
||||
|
||||
function toEntriesArray(arr) {
|
||||
if (debug) {
|
||||
console.log('toEntriesArray');
|
||||
}
|
||||
var results = [
|
||||
{
|
||||
type: 'array',
|
||||
entries: []
|
||||
}
|
||||
];
|
||||
arr.forEach(function(v) {
|
||||
var entry = toEntries(v);
|
||||
results[0].entries.push(entry[0]);
|
||||
results = results.concat(entry);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
function toEntriesObject(dict) {
|
||||
if (debug) {
|
||||
console.log('toEntriesObject');
|
||||
}
|
||||
var results = [
|
||||
{
|
||||
type: 'dict',
|
||||
entryKeys: [],
|
||||
entryValues: []
|
||||
}
|
||||
];
|
||||
Object.keys(dict).forEach(function(key) {
|
||||
var entryKey = toEntries(key);
|
||||
results[0].entryKeys.push(entryKey[0]);
|
||||
results = results.concat(entryKey[0]);
|
||||
});
|
||||
Object.keys(dict).forEach(function(key) {
|
||||
var entryValue = toEntries(dict[key]);
|
||||
results[0].entryValues.push(entryValue[0]);
|
||||
results = results.concat(entryValue);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
function computeOffsetSizeInBytes(maxOffset) {
|
||||
if (maxOffset < 256) {
|
||||
return 1;
|
||||
}
|
||||
if (maxOffset < 65536) {
|
||||
return 2;
|
||||
}
|
||||
if (maxOffset < 4294967296) {
|
||||
return 4;
|
||||
}
|
||||
return 8;
|
||||
}
|
||||
|
||||
function computeIdSizeInBytes(numberOfIds) {
|
||||
if (numberOfIds < 256) {
|
||||
return 1;
|
||||
}
|
||||
if (numberOfIds < 65536) {
|
||||
return 2;
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
|
||||
module.exports.Real = Real;
|
Reference in New Issue
Block a user