LINUX.ORG.RU

Kaitai Struct 0.6

 , , ,


5

3

Вышла новая версия Kaitai Struct — языка спецификации произвольных бинарных форматов файлов, пакетов, протоколов и т. д.

Основная идея проекта в том, что формат бинарного файла описывается один раз на языке .ksy, после чего файлы такого формата можно рассматривать в визуализаторах, получая представление о том, каким байтам соответствуют какие значения элементов формата, сгенерировать человекочитаемую диаграмму формата, а самое главное — сгенерировать готовую библиотеку парсинга такого формата на одном из 8 поддерживаемых целевых языков: C++, C#, Java, JavaScript, Perl, PHP, Python, Ruby.

В новой версии стоит отметить следующие улучшения:

  • поддержка побитового чтения (в том числе для парсинга битовых полей, битовых потоков и т.д.) - type: bXX теперь позволит прочитать XX бит как число, type: b1 прочитает один бит и представит его как boolean
  • масса возможностей добавить метаинформацию о формате в .ksy: ключ doc на уровне типов, а также ключи title, license, ks-version в meta
  • поддержка нестандартных ключей а ля CSS, с минусом в начале; активно используется в Web IDE для задания опций отображения (-webide-representation) и т. д.
  • массивные изменения движка вывода типов: enum теперь тоже ресолвится по единым правилам, даже в языках, где таковой поддержки нет нативно (Python, PHP, Perl, JavaScript и т. д.)
  • идентификатор id для атрибутов последовательности теперь опционален; если его не задать, будет автоматически присвоен уникальный числовой идентификатор, что удобно для быстрого разбора неизвестных полей в форматах
  • поддержка подключения внешних типов (если задать type: foo и foo не определен в текущем файле, будет сгенерирован корректный import / include в предположении, что тип объявлен во внешнем файле)
  • возможность писать целочисленные литералы с разделителями (123_456_789 или 0b0101_1111), а также преобразовывать числа в строки с помощью метода to_s
  • исправление ошибок, оптимизация генерируемого кода

Релиз приурочен к проходящей в эти выходные конференции FOSDEM 2017. 5 февраля будет представлен доклад о парсинге бинарных media-форматов с помощью Kaitai Struct в рамках Open Media devroom. Для интересующихся, но не имеющих возможности посетить доклад лично, организована онлайн-трансляция видео.

>>> Подробности

★★

Проверено: jollheef ()
Последнее исправление: sudopacman (всего исправлений: 4)

Ответ на: комментарий от MATPOCKUH

«Читалка» представляет собой систему связанных классов, которые умеют в свои экземпляры раскладывать зачитанные данные. Кроме того, никуда не девается система потоков/подпотоков, из которых можно получать данные в исходном виде (грубо говоря, для Python - bytearray). Это вам поможет реализовать писалку?

GreyCat ★★
() автор топика
Ответ на: комментарий от anonymous

Ищи видео с конфы. Али гуглом пользоваццо ниазилил? :)

Linfan ★★★★★
()
Ответ на: комментарий от anonymous

И даже более того - код парсера CDR взят изначально из CDR Explorer'а. См. хистори репозитория OLEtoy. Ессно текущий код в мастере сильно отличается, поскольку переработан за 5 лет, но факт остается фактом.

Linfan ★★★★★
()
Ответ на: комментарий от segfault

Руки надо просто выпрямлять.

Только вот почему-то именно в процессе выпрямления обычно приходит осознание, что кастить бинарные куски из файла в struct - плохая идея. И очередной человек начинает изобретать библиотечку функций и код вида:

a = read_int32_le();
b = read_int16_le();
c = read_strz();
GreyCat ★★
() автор топика

Мне нужно парсить некие цифровые сигналы из радиоэфира. Имеющаяся приёмная аппаратура обеспечивает возможность чтения сырого битового потока после демодулятора, скажем через пайп/сокет... Скажите, планируется ли в Kaitai Struct поддержка генерации парсеров на Си? (Генерация готовых диссекторов для Wireshark - это было бы просто невообразимо круто!)

Или в моём случае проще воспользоваться чем-то типа wsgd.free.fr? Или писать на каждый протокол свой диссектор с нуля?

anonymous
()
Ответ на: комментарий от anonymous

enum теперь тоже ресолвится по единым правилам, даже в языках, где таковой поддержки нет нативно (Python, PHP, Perl, JavaScript и т. д.)

Но в Питоне есть enum: https://docs.python.org/3/library/enum.html.

Речь не про поддержку enum'ов, а про то, чтобы можно было имея такую иерархию классов:

class Foo {
    class Bar {
       // вот тут воспользоваться enum'ом Foo.Baz, назвав его просто "Baz"
    }
    enum Baz {
    }
}

В C++, Java и Ruby так можно делать, язык сам все отресолвит. В Python нужно выписывать полный путь до такого enum'а.

GreyCat ★★
() автор топика
Ответ на: комментарий от anonymous

Скажите, планируется ли в Kaitai Struct поддержка генерации парсеров на Си?

Планируется. Если готовы обсудить, как вам удобнее будет получать результаты - велком, можно попробовать сделать.

(Генерация готовых диссекторов для Wireshark - это было бы просто невообразимо круто!)

Экспериментальная поддержка есть - пробуйте.

Или в моём случае проще воспользоваться чем-то типа wsgd.free.fr? Или писать на каждый протокол свой диссектор с нуля?

Вы определитесь для начала, что хочется. Wireshark сам по себе, на самом деле, как самоцель - идея так себе. Он хорош, когда у тебя вокруг бегает 100500 вариантов сетевого трафика, ты именно используешь активно его как инструмент анализа всей сети в целом, и тебе надо написать 100501-ый диссектор для него. Когда же у вас ничего из этого нет и нужно работать с форматами, абсолютно не имеющими отношения к стандартным, понимаемым Wireshark — зачем сам по себе Wireshark?

Ну и основной вопрос, в который все упирается, конечно — нужно ли API потом с этим работать программно или просто глазками посмотреть один раз?

GreyCat ★★
() автор топика
Ответ на: комментарий от segfault

Ну кто-то изобретает, а кто-то использует готовые парсилки-композилки BER.

А кому-то надо парсить уже заданный формат, который ни под какие стандарты BER, DER, Protobuf, Thrift, BSON и т.д. не подходят.

GreyCat ★★
() автор топика
Ответ на: комментарий от GreyCat

Добрый день. Kaitai Struct оказался очень вовремя, поскольку я давненько подумывал распарсить некоторые закрытые файлы.

Если вам не сложно - помогите понять принципиально важный вопрос.
Предисловие: Пытаюсь раскрутить один мало известный формат. На неизвестном китайском сайте нашел документацию по нему, но не знаю, та ли это версия документации и соответствует ли она моим данным. В целом пока-что похоже соответствует, но есть одна мысль, которая не дает мне покоя.

В соответствии с документацией есть 2 типа данных:

  • Natural number Type (N). Value range: Represents a range of 0 to maximum value that is positive and determined by the field length. (Example: The natural number type with a two-byte field can represent from 0 to 65535. 0 is assigned to «Null»)
  • Integer Type (I). Representation method: Uses the two's-complement form (the first bit means a plus or minus sign). Value range: Represents a range between the maximum negative value and maximum positive value that are determined by the field length.

Предположим, что у меня есть данные, представленные 2 байтами. Так вот, если тип данны (N) - похоже, что он представляется как u2be, а если (I) то s2le. Меня ОЧЕНЬ смущает little-endian во втором варианте. С одной стороны s2be наверняка дает не правильный результат (см картинку), с другой стороны- почему во всем файле используется big-endian, а вот для сигнед - используется little-endian?

P.S. Ни разу до этого не занимался реверс инженерингом. Если спрашиваю глупости- не пинайте, пожалуйста.

disee ★★★
()
Ответ на: комментарий от disee

Спасибо за добрые слова :)

Боюсь, что ничего конкретного по делу не подскажу. Документация, которую вы показали - очень странная и ничего однозначного я из нее вынести не могу. Про endianness там - ни слова. Если у вас по результатам выходит, что действительно в одном случае be, а в другом - le, что же - значит, так и есть. Соглашусь, что действительно странно, но надо поднимать историю и копаться в том, откуда этот формат, что там за оригинальная машина, что за движок, где и как разрабатывался и т.д.

P.S. Не стоит писать «b8» просто так ;) Если надо прочитать один байт - пишите лучше «u1», это генерит сильно более простой и быстрый код.

P.P.S. Сделаете - может подумаете, чтобы нам в репозитарий такой формат добавить?

GreyCat ★★
() автор топика
Ответ на: комментарий от GreyCat

И вам спасибо за помощь. Давайте я вам расскажу увлекательную историю. Есть у меня автомобиль Volvo XC90 2005го года. И есть в нем штатная навигация. Навигация замечательная, и карты недавно обновляли, но есть в ней три существенных недостатка:

  • Карты распространяются на DVD дисках
  • Один диск стоит примерно 400$
  • Есть все страны, кроме русскоговорящих

Собственно с двумя первыми пунктами можно мириться, но с третим увы, но нет.

Я люблю нативное. На моем месте любой вминяемый человек запустил бы навигатор на телефоне и не парился. Чуть-чуть менее вменяемый человек - собрал бы простую железяку, которая бы реюзала штатный монитор и то же не парился бы. Но я же люблю нативное! Поэтому, ничего лучше как достать родной диск из машины и занести его домой я не придумал.

А далее начались танцы с бубнами, оказалось что диск просто так под офтопиком не читается, но отлично читается под линуксами. На диске я нашел много файлов с расширением .KWI . И это «KWI» очень плохо гуглится. По итогу я узнал, что формат называется KIWI и это магическое слово гуглится еще хуже. После танцев с бубном я таки нашел один сайт (кстати выше, оказывается, я вас обманул, сказав, что нашел документацию на китайском сайте. он не китайский. он японский), с которого я смог скачать документацию по формату. Распарсив часть файла ALLDATA.KWI я все таки понял, что версия подходит (так как в этом файле внутри нашлось место для стринга, с кодом версии).

Некоторые крупицы информации, которые я смог узнать о железе:

  • Система называется Volvo RTI MMM (есть MMM+ но это более новая версия, у меня просто МММ)
  • Поговаривают, что работает на Embedded Windows (пруфа нет, но возможно)
  • Предполагаю, что железяка поддерживает только ascii (это бы объяснило наличее всех регионов европы, кроме русскоговорящих стран)
  • Железка подключена к шине CAN и умеет читать и писать в нее.
  • У железки есть внутрянняя перезаписываемая память, поскольку она может обновлятся с диска

Мой план состоит в следующем:

  • Распарсит все файлы на диске, попутно хорошо вникнув как это работает
  • Зареюзать сгенерированные классы, что бы написать сериализатор формата (ох, как жаль что KS пока не умеет)
  • Взять из Open Street Map данные и сгенерить на их основе KWI, записать на диск
  • начать пользоваться навигацией (правда на английском языке)

Разговаривал в вольво клубе- все жаждут таких дисков для России,Беларуси,Украины. Некоторые готовы даже платить.
Касательно наработок: с удовольствием поделюсь своими результатами

disee ★★★
()
Ответ на: комментарий от GreyCat

Если вас не затруднит, вы бы не могли рассказать, как вы пользуетесь сочетанием ksv и ksc ? Можно ли редактировать *.ksy прямо в ksv?

До недавнего времени я использовал WebIDE, но при попытке прогрузить файл, размером 3+гб - браузер долго думает, а потом говорит что что-то пошло не так. Насколько я понял, для работы с объемными файлами стоит переходить в консоль. Установил, попробовал- работает.

Подскажите, пожалуйста, как добится юзабильности? Т.е. в браузере я вношу изменение и сразу же получаю результат, а при использовании vi+ksv получается мне нужно:

  • Внести изменения в *.ksy (+сохранить изменения)
  • Запустить что-то типа ksv myfile.bin myfile.ksy
  • Пролистать до структуры, где я вносил изменения
  • если что-то написал не правильно - повторить с начала

P.S. в разделе 3. Workflow overview как раз написано TODO :)

disee ★★★
()
Последнее исправление: disee (всего исправлений: 1)
Ответ на: комментарий от disee

Забавно. Я когда-то давно, лет 5 назад, примерно такую же процедуру проделывал с VAG'овскими картами. Навигация там тоже была, и даже Россия в ней была, но само качество карт было ниже плинтуса. Та же Москва состояла буквально из двух десятков основных шоссе и все.

Я тогда разобрал внешний контейнер, внутри оказалось несколько тысяч мелких файлов, судя по расширениям - там было штук 50 каких-то подформатов. Осознав объем работ, я тогда забил.

Предполагаю, что железяка поддерживает только ascii (это бы объяснило наличее всех регионов европы, кроме русскоговорящих стран)

Скорее какой-нибудь iso8859-1 или windows-1252.

Зареюзать сгенерированные классы, что бы написать сериализатор формата (ох, как жаль что KS пока не умеет)

Скорее всего скоро будет уметь в ближайшем будущем в ограниченном объеме.

Касательно наработок: с удовольствием поделюсь своими результатами

Ловлю на слове :)

GreyCat ★★
() автор топика
Ответ на: комментарий от disee

но при попытке прогрузить файл, размером 3+гб - браузер долго думает, а потом говорит что что-то пошло не так

Боюсь, что web IDE для такого размера файла будет требовать эдак 100-200 гигабайт RAM ;)

при использовании vi+ksv получается мне нужно:

Да, сейчас примерно так :( Меня все давно пинают сделать перезагрузку и поиск открытого до этого места в ksv, но все пока никак руки не дойдут.

Частично спасает то, что можно записать интересные подразделы в отдельные файлы (в ksv можно нажать «w», выделив нужную область и введя имя файла) и работать с меньшими файлами.

Вообще отдельно странно, что вам весь 3-гигабайтный файл целиком нужен. Обычно сам гигантский файл - это контейнер, а внутри какая-то своя структура.

P.S. в разделе 3. Workflow overview как раз написано TODO :)

Он на самом деле не про это, там надо описать совсем базовую вещь, что есть, мол, .ksy, его сначала нужно создать/скачать, затем с помощью ksc сделать из .ksy какой-нибудь .cpp или .py, а затем подключить к своей программе.

GreyCat ★★
() автор топика
Ответ на: комментарий от GreyCat

Скажите, а как вы обычно работаете с особо крупными файлами?

Конкретно в моем случае я пытаюсь распарсить файл PARCEL.KWI . Похоже это не контейнер. Судя по всему, в этом файле хранится хэдер с некоторой мета информацией (указание области карты; длинны массивов), а затем три огромных массива (парселов, описывающих все точки на карте и еще что-то).

Если бы без совета, я бы начал наверное с того, что отрезал пару первых метров файла (используя какой-нибудь dd) и разобрался с ними (т.е. однозначно распарсил бы хедер, и возможно разобрался со структурой записи в первом массиве), а дальше бы думал. Но хотелось бы услышать ваше мнение. Как бы вы поступили в подобной ситуации?

disee ★★★
()
Ответ на: комментарий от disee

Ну, вполне себе жизнеспособная идея. Надо смотреть по ситуации и в целом сильно зависит от того, что будет внутри. KS хорошо работает с большими файлами, с которыми хотя бы в теории можно работать в lazy-режиме, т.е. зачитывая куски по необходимости, а не грузить весь файл целиком структурами в память. Это, как правило, всякие древовидные штуки, файловые системы и т.п. вещи, в которых есть какой-то индекс и по нему находятся какие-то более мелкие куски.

Грубо говоря, если попытаться загрузить 3-гигабайтный файл с помощью форматом а ля:

seq:
  - id: foo
    type: u4
    repeat: eos

(т.е. массив 4-байтовых целых, «отсюда и до обеда») - то оно предсказуемо будет печально. Весь этот массив разом будет зачитан в память и займет от 3*n GB, где n будет зависеть от целевого языка (n~1 для C++ или, скажем, эдак n~3..4 для Java). Даже без визуализации (которая тоже жрет в любом случае много и работает дико медленно по сравнению с парсингом) - это тяжко.

Поэтому, да, придумывать какие-то маневры и смотреть по ситуации. Как чуть более элегантный вариант:

meta:
  id: parcel_kwi
seq:
  - id: fragment
    size: 1 * 1024 * 1024
    type: real_parcel_kwi
types:
  real_parcel_kwi:
    # тут начинать описывать реальный формат

Т.е. создать с самого начала ограниченный подпоток размером в 1 мегабайт и давать парсеру работать только с ним.

GreyCat ★★
() автор топика
Ответ на: комментарий от GreyCat

Хех, забавно. Я именно с таким подходом и стараюсь делать все. Мне показалось логичным, что основной seq нужно держать чистым, а вся работа должно проходить через описание типов. Например для ALLDATA.KWI meta и seq выглядят следующим образом:

meta:
  id: alldata_kwi
  file-extension: KWI

seq:
  - id: data_volume
    type: type_data_volume
    size: 2048
    
  - id: sequence_of_management_header_tables
    type: type_sequence_of_management_header_tables
    size: 2048
а далее уже вся магия происходит после тега types.

Кстати, по поводу больших файлов. Может имеет смысл как-то поколдовать с инстансами? Меня привлекло в документации то, что:

Another very important difference between seq attribute and instances attribute is that instances are lazy by default. What does it mean? Unless someone would call that body getter method programmatically, no actual parsing of body would be done. This is super useful for parsing larger files, such as images of filesystems. It is impractical for a filesystem user to load all the filesystem data into memory at once: one usually finds a file by its name (traversing file index somehow), and then can access file’s body right away. If that’s the first time this file is being accessed, body will be loaded (and parsed) into RAM. Second and all subsequent times will just return a cached copy from the RAM, avoiding any unnecessary re-loading / re-parsing, thus conserving both RAM and CPU time.

И еще появился такой вопрос: при описании нескольких разных файлов у меня встречаются одинаковые кастомные types. Может быть существует какой-нибудь механизм, что бы все кастомные типы вынести в отдельный файл, а потом просто импортировать их по необходимости?

disee ★★★
()
Ответ на: комментарий от GreyCat

Я понимаю, что перекладывать с больной головы на здоровую - не лучшая практика, но вы бы не могли, пожалуйста, проконсультировать еще по одному вопросу:

у меня есть некая структура:

  type_level_management_record:
    seq:
      - id: main_body
        size: 40

      - id: expansion_field
        type: type_level_management_record_expansion_field
        
      - id: adjustment_field
        size: _parent.size_of_level_management_record*2 -42 -expansion_field.num_of_road_background_name_display_classes.num_of_road_display_classes*2-expansion_field.num_of_road_background_name_display_classes.num_of_background_display_classes*2-expansion_field.num_of_road_background_name_display_classes.num_of_name_display_classes*2
        doc: OMG

type_level_management_record_expansion_field:
    seq:
      - id: num_of_road_background_name_display_classes
        type: type_num_of_road_backgroun_name_display_classes
        size: 2
        doc: >
          Number of Road Display Classes plus Number of Background
          Display Classes plus Number of Name Display Classes
  
      - id: a_sequence_of_display_class_information_items_for_road
        type: u2be
        repeat: expr
        repeat-expr: num_of_road_background_name_display_classes.num_of_road_display_classes
        doc: >
          A Sequence of Display Class Information Items (for Road
          Data Frame)
      
      - id: a_sequence_of_display_class_information_items_for_background
        type: u2be
        repeat: expr
        repeat-expr: num_of_road_background_name_display_classes.num_of_background_display_classes
        doc: >
          A Sequence of Display Class Information Items (for
          Background Data Frame)
          
      - id: a_sequence_of_display_class_information_items_for_name
        type: u2be
        repeat: expr
        repeat-expr: num_of_road_background_name_display_classes.num_of_name_display_classes
        doc: >
          A Sequence of Display Class Information Items (for Name
          Data Frame)

type_num_of_road_backgroun_name_display_classes:
    seq:
      - id: reserved
        type: b2
        
      - id: num_of_road_display_classes
        type: b4
        
      - id: num_of_background_display_classes
        type: b5
        
      - id: num_of_name_display_classes
        type: b5
И собственно, как можно догадаться, у меня есть некоторая проблема с вычислением размера для adjustment_field В родительском классе у меня есть запись «size_of_level_management_record» которая, как из названия видно, хранит информацию о длине level_management_record (только не байтах, а в словах, т.е. по 2 байта). А собственно в самой level_management_record в упращенном варианте, представленном здесь, есть 3 основные саб-класса main_body (длина 40 байтов), expansion_field (переменной длины) и не любимый adjustment_field, который считается как **size_of_level_management_record** - (длина main_body + длина expansion_field).

И собственно вот тут у меня возникла проблема. Как получить длину expansion_field (т.е. поля с переменной длиной)? Сейчас, как воркэранд я ее посчитал «ручками». Предполагаю, что это должно решаться через _io, но к сожалению так и не понял, как этим пользоваться.

Подскажите, пожалуйста.

P.S. неким органом чувствую, что сильно плаваю в терминалогии, аля class/sub-class/stream/etc. Не бейте ногами, пожалуйста

disee ★★★
()
Ответ на: комментарий от disee

Может имеет смысл как-то поколдовать с инстансами?

В 95% случаев это работает так: есть какой-то индекс, скажем, «имя файла -> (позиция, размер)». Разумеется, элемент такого индекса оформляется, как отдельный тип, который будет включен где-то, как повторяющийся. При парсинге это зачитает этот индекс в память целиком. А вот дальше следует ход конем:

# вот это все уже было и зачитывается в память всегда, т.к. и само
# seq, и вызывается скорее всего из других seq
seq:
  - id: file_name
    type: str
    size: 16
  - id: file_pos
    type: u4
  - id: file_size
    type: u4
instances:
  body:
    value:
      pos: file_pos
      size: file_size
      # и даже можно "type: XXX" указать при желании

После этого можно получить доступ к самому файлу, пройдя по индексу, найдя там нужную запись и тупо обратившись ".body" к ней. При этом индекс читается всегда, а вот чтение самого файла будет произведено только при обращении.

Есть ли у вас возможность делать такой же трюк на ваших данных - не знаю. Вообще, с точки зрения «не зачитывать все в память» надо себя спросить - как с этим работает родное приложение? Например, если есть какой-то гигантский массив - как можно получать доступ к его элементу N, не зачитывая весь массив в память?

GreyCat ★★
() автор топика
Ответ на: комментарий от disee

И еще появился такой вопрос: при описании нескольких разных файлов у меня встречаются одинаковые кастомные types. Может быть существует какой-нибудь механизм, что бы все кастомные типы вынести в отдельный файл, а потом просто импортировать их по необходимости?

Прямо сейчас можно делать это вообще без импорта. То есть просто два файла, в одном:

meta:
  id: foo
seq:
  - id: element
    type: bar

а описание самого bar - в другом:

meta:
  id: bar
seq:
  - id: baz
    type: u1
# ...

Если оба файла подключатся в проект - все будет работать. Это так называемые «opaque types» - когда в файле foo происходит обращение к неизвестному типу «bar» - компилятор просто считает, что оно, наверное, описано в другом файле, и генерирует обращение к нему по имени.

В ksv можно одновременно грузить много .ksy для таких случаев: ksv файл.bin файл1.ksy файл2.ksy ...

Разумеется, это не будет работать, если из файла foo нужно будет получить доступ к каким-то внутренностям bar, например, если будет желание обратиться к element.baz - компилятор не будет знать ничего о том, что в типе bar есть этот baz или нет, и какого он там типа. Над этим работаем, есть https://github.com/kaitai-io/kaitai_struct/issues/71

GreyCat ★★
() автор топика
Ответ на: комментарий от disee

Подскажите, пожалуйста.

У меня пока вопрос номер один - для чего это все нужно: «есть некоторая проблема с вычислением размера для adjustment_field» и «Как получить длину expansion_field»?

Из того, что я вижу - я бы сейчас делал во внешнем классе, у которго есть «size_of_level_management_record» вызов level_management_record как-то так:

- id: level_management_record
  type: type_level_management_record
  size: size_of_level_management_record * 2

Это создаст подпоток с фиксированной длиной ровно в столько, сколько нужно. Зачитать из него больше будет нельзя физически (вызовется exception), если зачитать меньше - то чтение дальнейших полей все равно пройдет нормально, незачитанный в type_level_management_record остаток корректно пропустится. Т.о. гарантируется, что level_management_record будет точно нужного размера.

Дальше, если по логике «adjustment_field» - это поле «отсюда и до состояния, чтобы запись level_management_record была размера size_of_level_management_record * 2» - это просто означает «отсюда и до конца потока». «Потоком» в данном случае (т.к. мы явно указали размер) будет подпоток нужной длины. Т.е. скорее всего можно будет сделать:

  - id: adjustment_field
    size: eos
    # type: XXX при необходимости тоже можно сделать

и все. Ничего считать вручную вроде бы при такой постановке задачи не нужно. А если adjustment_field - это вообще не осмысленное поле, а просто паддинг - то его даже заводить не надо, все само проскипается при задании «size: size_of_level_management_record * 2».

P.S. неким органом чувствую, что сильно плаваю в терминалогии, аля class/sub-class/stream/etc. Не бейте ногами, пожалуйста

Да она сама по себе, к сожалению, не очень устоявшаяся.

GreyCat ★★
() автор топика
Ответ на: комментарий от GreyCat

Вообще, с точки зрения «не зачитывать все в память» надо себя спросить - как с этим работает родное приложение? Например, если есть какой-то гигантский массив - как можно получать доступ к его элементу N, не зачитывая весь массив в память?

Если я правильно понимаю они сделали следующим образом: есть таблица my_table_1, в которой у каждого элемента этой таблицы, помимо прочих есть атрибут «offset_to_table_2», который указывает офсет от начала файла, до места, где начинается подтаблица table_2 для этой таблицы my_table_1. В свою очередь в таблице table_2 есть атрибут «offset_to_table_3».

Таким образом, полагаю, можно сказать, что у них последовательно хранятся индексы на таблицы с индексами на таблицы с данными.

Грубо говоря по первой таблице они быстро находят книжную полку, по второй таблице книгу на этой полке, а по третей страницу и текст на ней.

Прямо сейчас можно делать это вообще без импорта

Почему-то не получилось завести. Подскажите, пожалуйста, что я делаю не так? Пробовал разные комбинации- не работает :(

И сразу второй вопрос: а если в импортируемом типе имеются поля с enum'ами, их тоже объявлять в файле с типом? И возможно ли это в принципе?

У меня пока вопрос номер один - для чего это все нужно: «есть некоторая проблема с вычислением размера для adjustment_field» и «Как получить длину expansion_field»?

Что-то я тупонул, и сам не додумался до этого, хотя уже неоднократно такой подход использовал :)
Просто убрал adjustment_field ибо в нем все нули и указал размер родительского стрима

disee ★★★
()
Ответ на: комментарий от disee

Грубо говоря по первой таблице они быстро находят книжную полку, по второй таблице книгу на этой полке, а по третей страницу и текст на ней.

Ну так это как раз идеальная ситуация именно для instances. «pos: offset_to_table_2» и вперед. И lazy будет как раз.

Почему-то не получилось завести. Подскажите, пожалуйста, что я делаю не так?

Попробуйте вызывать «ksv PARCEL_PART_1.KWI parcel_kwi.ksy type_sa.ksy»

Кстати, я только вчера закоммитил таки более полноценную поддержку импортов: https://github.com/kaitai-io/kaitai_struct/issues/71. Теперь можно писать в «meta/imports» список импортируемых типов и должно работать.

И сразу второй вопрос: а если в импортируемом типе имеются поля с enum'ами, их тоже объявлять в файле с типом? И возможно ли это в принципе?

Если будут какие-то обращения из первого файла во второй, *кроме* type: второй, то вся эта штука с opaque типами не будет работать. Собственно, ровно для решения этой проблемы и делался более полноценный механизм импортов в issue #71.

GreyCat ★★
() автор топика
Ответ на: комментарий от GreyCat

Похоже в моем случае инстансы таки не подойдут.
Честно говоря, я зашел в тупик. Вы бы не могли, пожалуйста, помочь найти мне решение проблемке, с которой я столкнулся.
Небольшой пример структуры данных:

+ number_of_block_set = 583 //число записей в таблице seq_of_block_set
+ seq_of_block_set
  + [-] 0
    + offset_to_block_management_table = 3413 // Валидный офсет
    + size_of_block_management_table = 3 // Валидный размер
  + [-] 1
    + offset_to_block_management_table = 4294967295 
    + size_of_block_management_table = 0 // Такую запись нужно игнорировать и не создавать для нее block_management_table
.....

+ seq_of_block_management_tables
  + [-] 0
    + seq_of_block_management_records // <--- Вот именно это и есть та самая management_table для которой выше указан офсет и размер.

Собственно тут начинается, мягко говоря, неприятность. В документации сказано, что

Block set management records have a one-to-one relationship with block management tables.

Что я понимаю как

Для каждого «Block set» есть «block_management_table» и что их количество совпадает.

Поэтому я использую number_of_block_set для указание длины не только seq_of_block_set, но и для указания длины «seq_of_block_management_tables». Как указано выше seq_of_block_management_tables это таблица, с таблицами «seq_of_block_management_records». Вот собственно тут у меня и проблема. Кол-во элементов в seq_of_block_management_records я не знаю, и я должен использовать замаплиную как 1-к-1 информацию из block_set, а именно size_of_block_management_table (т.е. я должен указать размер таблицы, а потом повторять до конца стрима).

И может показаться, что это П^C закончился, но НЕТ! Как показано в примере, size_of_block_management_table может быть равен 0, а это значит что эту таблицу в «seq_of_block_management_tables» создавать не надо (т.к. она нулевого размера) и перейти к следующей.

В текущей ситуация я не имею понятия как это можно сделать через Kaitai. Может вы что-нибудь посоветуете?

Заранее спасибо

disee ★★★
()
Ответ на: комментарий от disee

Не очень понял, зачем используются два seq: seq_of_block_set и seq_of_block_management_tables. Почему бы не сделать instance, который будет получать объект block_management_table именно там, где описаны его координаты? Т.е. что-то типа:

something_upper_level:
  seq:
    - id: number_of_block_set
      type: u4
    - id: block_sets
      type: block_set
      repeat: expr
      repeat-expr: number_of_block_set
block_set:
  seq:
    - id: offset # to_block_management_table
      type: u4
    - id: size # of_block_management_table
      type: u4
  instances:
    body:
      pos: offset
      size: size
      if: size != 0
      type: block_management_table

Тут же одновременно можно и сделать проверку на то, что инстансить block_management_table нужно только тогда, когда size - не 0, как вы и сказали.

Если я чего-то не так понял - уточняйте, пожалуйста, желательно с примерами ksy сразу ;)

GreyCat ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.