Статья является продолжением Реверс черного тессеракта. , без прочтения которой, единственный вопрос, на который именно тут дается ответ - "почему на КДПВ этого цикла статей везде картинки авторства Евгения Тихоновича Мигунова?". Отвечаю. Потому, что по странному совпадению, всеми книгами, которые он иллюстрировал, в детстве я зачитывался. Как художник, на мой взгляд, он великолепен, и выбранные цитаты его творчества, на мой взгляд, очень хорошо ложатся на темы конкретных статей. Единственное "но", его подходящие иллюстрации книг не всегда соответствуют строгому альбомному видению КДПВ Хабра. Посему, необрезанный оригинал я всё же приведу и под катом. В первой части файл carindb нарезали на части, следующий шаг - выбрать какой-то из типов блоков и посмотреть его попристальнее. Пока нет структуры данных, не выйдет построить семантику. Первичная задача: получить понимание, какие структуры образуют данные, какие типы у них, как соотносятся данные внутри между собой, угадать стандартные структуры, разметить, выбрав хоть какой то тип блока.  А потом - наверняка будет хорошо (с). Напомню карту блоков из прошлой статьи, надо чем-то руководствоваться при выборе препарируемого типа. Например, возьму тип 0x0D - и от начала недалёк (смещение 0x3000), и всего три по количеству - не сильно много, но есть рядом разные представители того же типа для проверки гипотез разметки. Поехали. В темплейте прыгаю на адрес 0x3000, и объявляю переменную block_0x0D типа структуры BT_0x0D, объявленной выше.  После указанного стрелочкой DWORD, заметьте, каждые 3 DWORD начинается явное повторение сходных значений. На картинке выше синим выделил второе из повторений, перед ним и после - явно что-то очень похожее. Добавляю в структуру BT_0x0D переменную UINT unknown с зеленым фоном, и после неё массив из 2042 структур BT_0x0D__triple, которая состоит из 3-х UINT разных цветов - удобно на глаз анализировать соответствие соседей. Количество 2024 подобрал, визуально контролируя окончание блока. (Во вкладке Variables раскрываю struct BT_0x0D block_0x0D, раскрываю дочернюю struct BL_HEAD head, кликаю на строку uchar here_last_byte - и фокус в окне hex перемещается на самый конец данного блока. Меняю число элементов массива, нажимаю F5, применить темплейт, если перебор - закрашиваются hex после конца, уменьшить число, и пока не закрасятся значащие байты.) Структуре BT_0x0D добавляю локальную переменную size - расчетный размер блока в байтах. Значение 2042 в шестнадцатеричной форме выглядит, как 0x7FA. А следующий сразу за head UINT unknown с зеленым фоном = 0x0c07fa. Причем, и массив array[0x7FA] начинается со смещения 0x0c от начала блока. Пара значений: указатель на массив и количество элементов массива за указателем явно неспроста рядом. Новый тип данных в "inc_common.bt" PTR - указатель на данные внутри блока. Структура ищет у родительской переменную с размером структурыexists(parentof(this).size), и если её значение больше size, цвет фона - красный и запись в лог о несоответствии значения возможному. Хоть какая-то проверка. Объявляет ushort ptr <format=hex> - смещение относительно начала блока. Если ptr ненулевой, и, если у родителя есть переменная с именем offset, по рассчитанному абсолютному адресу оставляет метку - байтик here на голубеньком фоне, кликая на него в Variables - в окне hex отобразится это место. Read_PTR - отображается во вкладке Variables в колонке Values. Новый тип данных в "inc_common.bt" LIST - список указатель на начало массива и количество элементов. Локальные offset, size транзитом идут для следующей PTR ptr, но потом offset этой структуры И вот теперь в структуре BT_0x0D переменную UINT unknown с зеленым фоном заменяю на LIST p_data, правя количество элементов BT_0x0D__triple main_array[cnt] на p_data.cnt, массиву добавлю атрибут optimize=false - иначе 010Editor схитрит, всем расчетным элементам присваивая значения первого. И в структуре парсинга блока BT_0x0D последним добавлю яркий byte after_parsed_block_info - и визуально в окне hex видно, где окончился парсинг, и прыгнуть на неё выбором в Variables можно. В самой структуре BT_0x0D__triple тоже изменения, при сохраненном размере: Первый UINT unknown1 в структуре - BL_ADDR, как адреса блоков они все валидны. Сразу за адресом, похоже, char ch, его значения лежат в промежутке, что и ASCII кода символов от '0' до 'z'. За ним еще один char seems_always_eq_1 - вставлю проверку, а правда ли всегда seems_always_eq_1 == 1 UINT unknown3 остается по-прежнему Завершает внутренности структуры 16битный aligment: явно данные выравнивают по границе 32бита. Как видите, в консоли, во вкладке Output, - множество намеков, что seems_always_eq_1 - вовсе не всегда равна 1, бывает и 0. Поправляю условие проверки  if((seems_always_eq_1 != 1)&&(seems_always_eq_1 != 0) ) , после чего парсинг проходит без замечаний в логе. Значение always_zero, похоже, действительно предназначена для выравнивания данных по границе 32 бита. Добавляю проверку в BT_0x0D__triple: BL_ADDR bl_type_0c действительно ссылается всегда на тип 0x0C?  Нет, не всегда, говорит консоль. Чтобы визуально выделить те элементы, где тип блока bl_type_0c не 0xC, а 0xD , выделяю ярким нераскрашенный seems_always_eq_1. Результат неожиданнен: в блоке типа 0xD в BT_0x0D__triple seems_always_eq_1 всегда ==1, если bl_type_0c ведёт "наружу", является типом 0xC, и всегда ==0, если bl_type_0c.type == 0xD, то есть на тот же тип. Является bool - ссылка "наружу", или "внутрь", самое имя ей is_ptr_out. Но, если часть ссылок ведёт внутрь, то на какие именно значения они указывают? Переписываю функцию, для отображения в Variables значения BT_0x0D__triple. Если is_ptr_out ==0, то в цикле "собираются" far_away.cnt символов ch из BT_0x0D__triple, начиная с адреса far_away.offset. Дерево? Все данные блока типа 0x0D размечены, но когда в руки попадает ухватистый молоток, то трудно остановиться и не продолжать этим молотком забивать всё, что попадает под руку и хоть чуть-чуть похоже на гвоздь. С минимальными изменениями, ранее сохраненный темплейт "util_vdo_stat.bt" выводит все варианты типов блоков в виде вызовов функции block(). Копипащу в основной темплейт, применяю, получаю в Variables представителей всех типов блоков. Визуально пытаюсь найти сходство сырых данных разных типов с данными в блоке 0x0D, похожие пытаюсь распарсить структурой BT_0x0D. Понимаю, что структуру блока BT_0x0D правильно называть BT_0x0B_0x0D_0x0F_0x11: для этих типов блоков подходит один и тот же темплейт (его надо только подправить, чтобы лог был не такой грязный от сообщений, что тип не 0xD и не 0xC). Из блока типа 0xB "внешние" "ссылки" ведут в 0xA. Из блока 0xD ссылки ведут в 0xC. Из 0x0F в 0x0E. Из 0x11 в 0x10. Везде получается ссылки ведут или в тот же тип блока, из которого вызываются, (по сути ссылаясь на массивы того же типа данных BT_0x0D__triple), или в тип блока на единицу меньший. Редактирую BT_0x0D__triple. Добавляю перед описанием структуры BT_0x0D__triple её forward declaration для рекурсивного описания. Переименовываю BL_ADDR bl_type_0c -> bl_more_info Добавляю local en_BL_TYPE en_curr_bl_type = head.type; текущий блок Проверка раскараски фона is_ptr_out меняется if(bl_more_info.type == (en_curr_bl_type-1) )1) Если !is_ptr_out, т.е. ссылка идет "внутрь" - создаётся массив дочерних структур BT_0x0D__triple (рекурсия, у дочерних так же создадутся дочерние и т.д. и т.п.) А ведь я видел нечто похожее в автонавигаторе. Для ввода адреса пункта назначения необходимо последовательно ввести в четыре поля - страна, город, улица, дом (на экране они называются Country, City, Road, Number) строковые значения. Буквы для ввода последовательно выбираются на всплывающей при редактировании поля экранной клавиатуре энкодером, причем на этой QWERTY-клавиатуре доступны к выбору не все буквы, а лишь некоторые. Перемещение фокуса по буквам клавиатуры - вращением энкодера, выбор буквы - нажатием того же энкодера. Вначале доступно к заполнению только поле страна, на остальные перейти нельзя. Для официального диска Восточной Европы при заполнении страны на клавиатуре активны и доступны буквы B, C, E, L, M, P, R, S, остальные неактивны. При выборе L (энкодер с нажатием) появляется меню выбора, LATVIJA или LIETUVA. А если выбрать P - поле страна сразу заполняется значением POLAND и фокус переходит на поле город, заполнение которого происходит аналогичным способом: последовательным вводом букв из предложенных на клавиатуре. Набрать можно только те города, которые расположены в Польше. После курсор уходит на поле улица, которая заполняется так же, невозможно для Poland, Olsztyn набрать название улицы, которой в Ольштыне нет. Нагляднее описано в мануале на VDO VDO DAYTON PN 6000 на 18 странице, модель, картинка другие, но логика и данные те самые. Предварительная гипотеза - а не функционал ли ввода адреса и обеспечивают рассматриваемые типы? По количеству блоков, по последовательности расположения в carindb можно тогда предположить, что 0xB - выбор букв для страны, тогда 0xA - страна. Тогда следующая пара 0xD и 0xC - город, 0xF-0xE - улица, 0x11-0x10 - дом. Результаты статьи суммарно: Структура PTR, псевдо-интеллектуальный указатель внутри блока Структура LIST - указатель на массив, с количеством элементов Рекурсивная структура с неэстетичным временным названием BT_0x0D__triple, описывающая единицу информации в блоках 0x0B, 0x0D, 0x0F, 0x11 Найдены блоки, которые описываются одной и той же структурой. Структура BT_0x0B_0x0D_0x0F_0x11 полностью размечает всё содержимое блоков с типами  0x0B, 0x0D, 0x0F, 0x11 Рекурсивные структуры данных 010Editor Гипотеза о ~существовании обитаемых миров~ функциональном назначении распарсенных данных Данные не только BigEndian, но еще выравниваются по границе 32 бита. Темплейт работает и на официальных картах carindb_ee carindb_bnl (с учетом, что там блоки 0x0B, 0x0D, 0x0F, 0x11 находятся по другим адресам) Чтобы уйти от привязки к "волшебным цифрам" отступа при вызове функции block(), напишу в инклюд "inc_block.bt" функцию FindBlockByType(en_BL_TYPE type), возвращающую адрес типа BL_ADDR первого попавшегося блока с подходящим типом. Нельзя возвращать смещение, потому что при ненахождении функция возвращает 0. В основном темплейте теперь для объявления блока необходимого типа на любых исходных carindb достаточно такой конструкции: block(FindBlockByType(0x0B)); // 0B 1      BT_0x0B_0x0D_0x0F_0x11  Дальнейший анализ - в следующей статье.