jParser делает простым чтение структур данных из двоичных файлов джаваскриптом.
- Вы один раз описываете структуру, анализ её происходит автоматически.
- Процесс анализа данных может быть расширен самописными функциями. Чем упрощается разбор нетривиальных файлов.
- jParser действует и во браузере, и в NodeJS, потому что работает на основе jDataView.
API
Элементарные структуры:
- Целые числа без знака: uint8, uint16, uint32
- Со знаком: int8, int16, int32
- Дробные с плавающей точкой: float32, float64
- Строковые: char, string(len)
- Массив: array(type, len)
- Положение: tell, skip(len), seek(pos), seek(pos, func)
Методы jParser:
- parse(value) — запускает анализ, может использоваться рекурсивно. Поведение зависит от типа аргумента:
- функция: вызывает функцию;
- строка: разыменование (по указанному имени берётся значение из структуры);
- массив: вызов функции, имя которой — первый элемент массива, а последующие элементы служат аргументами;
- объект: возвращает объект с теми же именами полей, но проанализированными значениями.
- функция: вызывает функцию;
- tell() — возвращает текущее положение (смещение) в анализируемом файле.
- skip(count) — передвинуться в файле на count байтов вперёд (пропустить их).
- seek(position) — перейти к указанному смещению в анализируемом файле.
- seek(position, callback) — перейти к указанному, там выполнить callback(), затем возвратиться к прежнему положению.
- current — Текущий объект, подвергаемый анализу. Используйте для доступа к результатам, ужé достигнутым во время анализа.
Конструктор jParser:
- new jParser(data, structure)
- data — это jDataView с данными, подвергаемыми анализу. Можете передать строку байтов (String), можете передать ArrayBuffer или буфер Node.js — эти типы данных преобразуются в jDataView автоматически.
- structure — объект с описанием всех структур данных.
- data — это jDataView с данными, подвергаемыми анализу. Можете передать строку байтов (String), можете передать ArrayBuffer или буфер Node.js — эти типы данных преобразуются в jDataView автоматически.
Примеры
Обычная структура Си
Вы можете описывать Си-подобные структуры данных. Это джаваскриптовый объект, в котором ключи являются именами данных, а их значения — типами.
var parser = new jParser(file, {
header: {
fileId: 'int32',
recordIndex: 'int32',
hash: ['array', 'uint32', 4],
fileName: ['string', 256],
}
});
parser.parse('header');
// {
// fileId: 42,
// recordIndex: 6002,
// hash: [4237894687, 3491173757, 3626834111, 2631772842],
// fileName: ".\Resources\Excel\Items_Weapons.xls"
// }
Ссылки
Структуры могут содержать другие структуры. Используйте имя структуры в виде строки, чтобы сослаться на её описание. Нижеследующий пример является частью структуры файла, содержащего модель для World of Warcraft:
nofs: {
count: 'uint32',
offset: 'uint32'
},
animationBlock: {
interpolationType: 'uint16',
globalSequenceID: 'int16',
timestamps: 'nofs',
keyFrame: 'nofs'
},
uvAnimation: {
translation: 'animationBlock',
rotation: 'animationBlock',
scaling: 'animationBlock'
}
Функции-помощники
Вам будет несложно определять новые элементарные типы. Вы можете использовать существующие виды конструкций, такие как объекты (float3 в нижеследующем примере) или массивы (float4 в примере). Если же вы желаете определить более сложный тип, то всегда есть возможность определить новую функцию, полагающуюся на метод this.parse для первичного анализа (hex32 и string0 в нижеследующем примере):
float3: {
x: 'float32',
y: 'float32',
z: 'float32'
},
float4: ['array', 'float32', 4],
hex32: function () {
return '0x' + this.parse('uint32').toString(16);
},
string0: function (length) {
return this.parse(['string', length]).replace(/+$/g, '');
}
Обратные связи
Если размер массива заранее не известен, можете поместить на это место в структуре функцию, возвращающую целое число. В этой функции вы можете через this.current обратиться к объекту, в настоящее время подвергающемуся анализу, считывать ужé полученные поля его.
image: {
width: 'uint8',
height: 'uint8',
pixels: [
'array',
['array', 'rgba', function () { return this.current.width; }],
function () { return this.current.height; }
]
}
Развитый анализ данных
Приятнейшая особенность jParser заключается в том, что сложные алгоритмы обработки можно выразить функциями внутри описания структуры данных. Это позволяет анализировать сложные файлы, не отделяя описание структуры от кода её обработчиков.
entryHeader: {
start: 'int32',
count: 'int32'
},
entry: function (type) {
var that = this;
var header = this.parse('entryHeader');
var res = [];
this.seek(header.start, function () {
for (var i = 0; i < header.count; ++i) {
res.push(that.parse(type));
}
});
return res;
},
name: {
language: 'int32',
text: ['string', 256]
},
file: {
names: ['entry', 'name']
}
Начало работы
На движке NodeJS
Просто используйте npm для установки jParser — и вы готовы :-)
npm install jParser
var fs = require('fs');
var jParser = require('jParser');
fs.readFile('file.bin', function (err, data) {
var parser = new jParser(data, {
magic: ['array', 'uint8', 4]
});
console.log(parser.parse('magic'));
});
Во браузере
Я пропатчил jQuery, обеспечив скачивание двоичных файлов в наилучшем двоичном формате. Подключите этот пропатченный код jQuery, а также jDataView и jParser — и вы готовы :-)
<script src="https://raw.github.com/vjeux/jDataView/master/jquery/jquery-1.7.1-binary-ajax.js"></script>
<script src="https://raw.github.com/vjeux/jDataView/master/src/jdataview.js"></script>
<script src="https://raw.github.com/vjeux/jParser/master/src/jparser.js"></script>
<script>
$.get('file.bin', function (data) {
var parser = new jParser(data, {
magic: ['array', 'uint8', 4]
});
console.log(parser.parse('magic'));
}, 'dataview');
</script>
Предосторожности
Этот иструмент в своей работе полагается на недокументированную особенность JavaScript: цикл по полям объекта работает в том порядке, в каком они указывались. Имейте в виду, что в Chrome и в Opera это неявное правило не работает для полей с цифровыми именами.
Следуйте вот каким двум правилам, чтобы эта библиотека работала во всех существующих реализациях языка JavaScript:
- Не начинайте имя поля цифрою в описании структуры данных.
- Не переопределяйте поле с однажды заданным именем, не помещайте в тот же объект другое одноимённое поле.
Демо
Анализ ICO. Это простой пример анализа двоичного файла на движке NodeJS. Он показывает, как решаются многие типичные задачи, возникающие при разборе двоичных данных.
Распаковщик Tar. Это простой пример разбора двоичного файла внутри браузера.
- tar.html — структура jParser.
Средство показа моделей World of Warcraft. Использует jParser для чтения двоичного кода трёхмерной модели, затем WebGL для её отображения.
- m2.js — структура jParser.
- model.json — итог анализа.
Внутренние файлы Diablo 3
- convert.coffee — структура для jParser. Кофескриптом записывать структуру файла ещё проще.
- Примеры обработанных файлов:
Автор: Mithgol