jParser: анализ двоичных файлов работает просто

в 6:57, , рубрики: javascript, jParser, node.js, анализ данных

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 — объект с описанием всех структур данных.

Примеры

Обычная структура Си

Вы можете описывать Си-подобные структуры данных. Это джаваскриптовый объект, в котором ключи являются именами данных, а их значения — типами.

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. Он показывает, как решаются многие типичные задачи, возникающие при разборе двоичных данных.

  • ico.js — структура данных для jParser.
     
  • ico.json — итог анализа.

Распаковщик Tar. Это простой пример разбора двоичного файла внутри браузера.

  • tar.html — структура jParser.

Средство показа моделей World of Warcraft. Использует jParser для чтения двоичного кода трёхмерной модели, затем WebGL для её отображения.

  • m2.js — структура jParser.
     
  • model.json — итог анализа.

[скриншот]

Внутренние файлы Diablo 3

 

Автор: Mithgol


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js