Предыдущие статьи о реверсе данных автомобильных навигаторов Siemens VDO Dayton CARMiN
01 Сшей красное с красным, желтое с желтым, белое с белым. Наверняка будет хорошо
02. Я уже даже не вижу код. Я вижу блондинку, брюнетку и рыжую
Блоки 0xA и 0xB
Получив в прошлой статье полную раскладку по значениям чисел и цифр типа блока 0xA, информации по странам, естественным следующим шагом будет попытка провести разбор типа блока 0x0С, информации по городам.
Напомню карту расположения информации в блоке стран 0xA.
Со смещения 0 по 0x30 - заголовок блока и TOC - table of contents, список LIST на полные списки различной информации в блоке. Сразу после заголовка - LIST на главную часть, brief, список записей по каждой из стран с краткой основной информацией. (Выделено желтым)
Со смещения 0x30 - 8-байтовые записи brief каждой из стран. (Красненькое)
Затем - по каждой стране расширенная информация (Фиолетовый)
Следующая часть - категории интересных мест, известность которых позволяет относить их к уровню достопримечательностей страны. Вернее, далекие ссылки (FAR_LIST) на списки points of interests, POI. (Синенькое)
Завершающая часть - список локальных названий (на местных языках) стран. Английские наименования (и, возможно и на других языках, зависящих от настроек навигатора) с высокой долей вероятности определяются по кодам стран, список кодов en_ENG_COUNTRY_NAME - не найден в открытом доступе, и восстановить его можно только аналитически в части стран, представленных в наличных данных.
//{en_PLACE_CATEGORY 0xA block, ADDINFO_0xA struct
typedef enum <ushort>{
Sites_of_interest = 0x14, // manneken pis ect
Museum = 0x20, //musee d'art moderne, musee de l'armee, musee des sciences Naturelles
Sport = 0x23, //automotodrom brno, o2 arena, ski areal jasna, o2 arena
Architecture = 0x25,//
Fun_park = 0x26, //boudewijn seapark, bruparck
Nature_park = 0x27, //het zwin, nationale plantentuin
UN_United_Nations = 0x28,
City = 0x30, //russian map
Aeroport = 0x3a, //brussel nationaal, brussels airport; , luchthaven brussel
Seaport = 0x35, // oostende ramsgate (tonnel), zeebrugge (port), need mode exmpls
Border_point = 0x34, //
Winery = 0x39 // lanson caves, champagne krug, moet et chandon, ruinart caves
}en_PLACE_CATEGORY <bgcolor=cDkGreen, fgcolor=cAqua>;;
Информация по каждой стране "собирается" из секций блока следующим образом:
BRIF_0xA - краткая информация, с ссылкой PTR на MORE_INFO_0xA.
MORE_INFO_0xA - дополнительная информация о стране, с набором ссылок FAR_LIST на города страны. Точнее, на список элементов CH_IDX блока типа BT_0x0B_0x0D_0x0F_0x11, которые ссылаются (иногда через несколько итераций внутри BT_0x0B_0x0D_0x0F_0x11, "набирая" последовательность необходимых для однозначного получения списка букв) на города, элементы блоков типа 0xC. Кроме этого, содержит ссылку на набор категорий (LIST) точек интереса (POI) масштаба страны,
ADDINFO_0xA, который правильнее назвать POI_CATEGORY. POI_CATEGORY - enum en_PLACE_CATEGORY категории POI, FAR_LIST на список элементов CH_IDX блока типа BT_0x0B_0x0D_0x0F_0x11, указывающих на элементы внутри блока типа 0x10, и указатель на начало строковых данных блока (выглядит бесполезно).
В паре мест используются указатели на списки внутри BT_0x0B_0x0D_0x0F_0x11, элементы CH_IDX, которые или рекурсивно содержат списки CH_IDX, или ведут "наружу", на информацию в других типах. Если изменить выводимую в Variables строку в функции Read_CH_IDX так, что копипаст этой информации в темплейт описывал вызов адресуемого блока и относительного смещения, то трудоемкость анализа значительно снизится.
//{BT_0x0B_0x0D_0x0F_0x11;
//{CH_IDX
struct CH_IDX;
typedef struct{
BL_ADDR bl_postaddr;
char ch <fgcolor=cYellow, bgcolor=cDkGreen>; // char
local en_BL_TYPE en_curr_bl_type <format=hex, hidden=false> = head.type; // current bl type
// is_ptr_out - boolean
if(bl_postaddr.type == (en_curr_bl_type-1) ){ // outer link
ubyte is_ptr_out <bgcolor=cLtBlue, fgcolor=cYellow,hidden=true>;
}else{ // innler link
ubyte is_ptr_out <bgcolor=cLtBlue, fgcolor=cBlue, hidden=true>;
}
// next for LIST far_away, have use size and offset - from bl_type_0c
local uint size <format=hex, hidden=true> = bl_postaddr.size *0x800; // size in blocks, * 0x800
local uint offset <format=hex, hidden=true> = bl_postaddr.offset;
LIST pl_postaddr <optimize=false>;
CONST_S align_s(0);
if(align_s.value) // тут ноль не ноль
Printf("Warn, %X align_s = %i( %X )\n",
FTell(), // offset where happened
align_s.value, align_s.value);
//BAD WAY - MAY NOT ENOUGHT MEMORY FOR BIG BLOCKS
if(!is_ptr_out){
// jmp and recursive declare children struct
local uint return_addr <hidden=true> = FTell();
FSeek(pl_postaddr.offset);
CH_IDX childs[pl_postaddr.cnt]<optimize=false>;
FSeek(return_addr);
}
}CH_IDX <read = Read_CH_IDX>;
string Read_CH_IDX(CH_IDX &a){
local string brif_str;
local uchar MAX_CNT_STR_getPStrList = 5;
if(a.is_ptr_out){
// get str list of brif strnames
SPrintf(brif_str, " block(0x%06X); //of:%X '%c' [%i]>%02x(%s)>%s",
a.bl_postaddr.raw, a.pl_postaddr.offset,
a.ch, a.pl_postaddr.cnt,
a.bl_postaddr.type, EnumToString(a.bl_postaddr.type),
getPStrList(a.bl_postaddr.offset,
a.pl_postaddr, MAX_CNT_STR_getPStrList) );
}else{ //if(!is_ptr_out){
// get chars list
SPrintf(brif_str, "%c [%i]>%s", a.ch,
a.pl_postaddr.cnt, getChList(a.pl_postaddr));
} //if(is_ptr_out)
return brif_str;
}
//}CH_IDX
// BT_0x0B_0x0D_0x0F_0x11 chars - and index to ge names or char set
typedef struct{
BL_HEAD head; // заголовок
local ushort size <format=hex, hidden=true> = head.addr.size * 0x800; // size of this block
local uint offset < hidden=true> = head.addr.offset; // absolute block offset
LIST pl_data;
CH_IDX char_of[pl_data.cnt] <optimize=false>;
byte after_parsed_block_info <bgcolor=cPurple, fgcolor=cWhite>;
}BT_0x0B_0x0D_0x0F_0x11;
//}BT_0x0B_0x0D_0x0F_0x11;
Кроме этого, можно вообще не задействовать описание блока BT_0x0B_0x0D_0x0F_0x11 в темплейте, вместо этого прописывая декларацию его структур-элементов в блоке 0xA в тех местах, где на них ссылаются.
В MORE_INFO_0xA тоже есть FAR_LIST idx_ch_cityes? Так тут же прописать массив структур CH_IDX, что позволит прямо в этом месте получать все города страны в человекопознаваемом виде.
//{BT_0x0A
//POI_CATEGORY - additional data - part of MORE_INFO_0xA
typedef struct{
local ushort size <format=hex, hidden=true> = head.addr.size * 0x800; // size of this block
local uint offset <hidden=true> = head.addr.offset; // absolute block offset
FAR_LIST POIs; //type 0xF, streets ch_idx
if(POIs.pl_data.cnt){
local uint ret_str_here <hidden=true> = FTell();
FSeek(POIs.pl_data.offset); // jmp to streets ch_idx
CH_IDX name[POIs.pl_data.cnt] <optimize=false>;
FSeek(ret_str_here);
}
en_PLACE_CATEGORY en_cat_places; //
PTR unkn_str_begin;
}POI_CATEGORY<read=Read_POI_CATEGORY>;
string Read_POI_CATEGORY(POI_CATEGORY &a){
string s;
SPrintf(s,"%02Xh %s: [%i]: >%s block(0x%X);//off:%X",
a.en_cat_places, EnumToString(a.en_cat_places),
a.POIs.pl_data.cnt, getChList(a.POIs.pl_data),
a.POIs.far_block.raw,
a.POIs.pl_data.offset
);
return s;
}//}ADDINFO_0xA == POI_CATEGORY
//MORE_INFO_0xA; - call from BRIF_0xA
typedef struct{
local ushort size <format=hex, hidden=true> = head.addr.size * 0x800; // size of this block
local uint offset < hidden=true> = head.addr.offset; // absolute block offset
FAR_LIST idx_ch_cityes; // ch_idx with country cityes
//make list ch
// try make citylist from ch_idx values
// TOOooooo slow - 5-10 seconds...
if(idx_ch_cityes.pl_data.cnt){ // count>0
//struct{
local uint ret_city_here <hidden=true> = FTell();
FSeek(idx_ch_cityes.pl_data.offset);
CH_IDX city[idx_ch_cityes.pl_data.cnt]<optimize=false>; // ch_idx type data elements
FSeek(ret_city_here);
//}
}
CONST_I dec_11(0x0B) <hidden=true>;
CONST_I dec_22(0x16) <hidden=true>;
CONST_I dec_33(0x21) <hidden=true>;
CONST_I dec_44(0x2C) <hidden=true>;
LIST pl_addinfo;
CONST_I hex01f4012c(0x01f4012c); // dec 32768300
CONST_I hex03e801f4(0x03e801f4); // dec 65536500
ushort is_island <bgcolor=cLtBlue>;
if((is_island & ~1)) { // !=0, !=1
Printf (" is_island = %i\n", is_island); // !=0, !=1
FSeek(FTell()-2); ushort is_island <bgcolor=cRed, fgcolor=cAqua>;
}
ushort is_EU <bgcolor=cLtBlue>;
if(is_EU & ~1){ // !=0, !=1
Printf (" is_EU = %i\n", is_EU);
FSeek(FTell()-2); ushort is_EU <bgcolor=cRed, fgcolor=cAqua>;
}
en_ENG_COUNTRY_NAME en_eng_strname<fgcolor=cDkGreen, bgcolor=cGreen>;
CONST_S aligment(0);
if(IS_OFICIAL_MAP){
CONST_S zero2(0);
//https://ru.wikipedia.org/wiki/ISO_3166-1
string alpha_2_ISO3166_1 <bgcolor=cLtGreen, fgcolor=cDkYellow>;
CONST_B aligment_b(0);
string const_triple_defice <bgcolor=cLtGreen,fgcolor=cDkYellow,hidden=true>;
CONST_S aligment_s(0);
}
// call ADDINFO_0xA
if(pl_addinfo.cnt){ // if pl_addinfo != 0
local uint return_here <hidden=true> = FTell();
FSeek(pl_addinfo.offset);
POI_CATEGORY country_POI[pl_addinfo.cnt] <optimize=false>;
FSeek(return_here);
}
}MORE_INFO_0xA<read=Read_MORE_INFO_0xA>;
string Read_MORE_INFO_0xA(MORE_INFO_0xA &a){
local string s;
SPrintf(s, "%s",EnumToString(a.en_eng_strname));
if(exists(a.alpha_2_ISO3166_1)) SPrintf(s, "%s, '%s'", s, a.alpha_2_ISO3166_1);
if(a.is_EU) SPrintf(s, "%s, EU", s);
if(a.is_island) SPrintf(s, "%s, island", s); /// Iseland in rus map - not iseland))
SPrintf(s,"%s . add_cnt:[%i]", s, a.pl_addinfo.cnt);
return s;
}
//}MORE_INFO_0xA;
//{BRIF_0xA; main data 0xA - BRIF_0xA
typedef struct{
local ushort size <format=hex, hidden=true> = head.addr.size * 0x800; // size of this block
local uint offset <hidden=true> = head.addr.offset; // absolute block offset
PSTR pstr_name; // ptr to zero-ended str
en_TYPE_ADDR is_synonym;// !!! carindb_rus.0xA.osterreich = 1
en_LANG en_lang;// language code
CONST_S zero(0); // ushort allways 0
PTR p_moreinfo<hidden=true>; // ptr to item of LIST pl_all_moreinfo;
// jump to MORE_INFO
local uint return_here <hidden=true> = FTell();
FSeek(p_moreinfo.ptr + offset);
MORE_INFO_0xA more_info;
FSeek(return_here);
}BRIF_0xA<read=Read_BRIF_0xA>;
string Read_BRIF_0xA(BRIF_0xA &a){
local string s;
SPrintf(s, "%s: `%s`. Lang: %s, add_cnt:[%i]",
EnumToString(a.more_info.en_eng_strname),
a.pstr_name.str, EnumToString(a.en_lang),
a.more_info.pl_addinfo.cnt
);
return s;
}
//}BRIF_0xA;
typedef struct{
BL_HEAD head; // заголовок
// size of this block
local ushort size <format=hex, hidden=true> = head.addr.size * 0x800;
// absolute block offset
local uint offset <hidden=true> = head.addr.offset;
LIST pl_all_countries; // brief geo info
LIST pl_all_moreinfo; // more info, ptrs from briefs
//pl_all_POIs - outer links to ch_idx file 0x11 type
CONST_I zero(0); LIST pl_all_POIs; CONST_I zero(0); CONST_I zero(0);
CONST_I zero(0); CONST_I zero(0); CONST_I zero(0); CONST_I zero(0);
BRIF_0xA country[pl_all_countries.cnt] <optimize=false>; // main data
/*
MORE_INFO_0xA more_info[pl_all_moreinfo.cnt] <optimize=false>;
NO NEED MADE THIS ARRAY - ALL ITEMS WILL BE CREATD FROM brief_geo ptrs
FSeek(pl_all_POIs.offset); // pl_all_addinfo - byte after more_info
COUNTRY_POI_0xA addinfo[pl_all_POIs.cnt] <optimize=false>;
NO NEED MADE THIS ARRAY - ALL ITEMS WILL BE CREATD FROM brief_geo ptrs
*/
byte after_parsed_block_info <bgcolor=cPurple, fgcolor=cWhite>;
}BT_0x0A;
//}BT_0x0A
Представление блока 0xA теперь включает в себя информацию от адресуемых элементов блока 0xD, при этом сам этот блок объявлять нет необходимости.
Чтобы прописать блок 0xC с информацией о конкретном городе достаточно раскрыть список городов конкретной страны, найти необходимый и скопировать информацию из колонки Variables в темплейт.
Анализируемая структура блока типа 0xC, информации о городах, сходна с 0xA. Описываю очевидные поля структуры, добавляю её тип в функцию block() и перечисление типов блоков.
Блок 0xC, заголовок, brief info
Практически то же самое, но некоторые нулевые 32битные константы в блоке типа 0xC обретают значения:
typedef struct{
BL_HEAD head; // заголовок
local ushort size <format=hex, hidden=true> = head.addr.size * 0x800; // size of this block
local uint offset <hidden=true> = head.addr.offset; // absolute block offset
LIST pl_all_cityes; // brief geo info
LIST pl_all_moreinfo;// more info, ptrs from briefs
// next eight uints seems like in hex from rel offset 10h
//0xA: CONST_I zero(0); LIST pl_all_POIs; CONST_I zero(0); CONST_I zero(0);
//0xA: CONST_I zero(0); CONST_I zero(0); CONST_I zero(0); CONST_I zero(0);
CONST_I zero(0); LIST pl_all_cat_pois; CONST_I zero(0); LIST pl_new_list_ofic;
CONST_I zero(0); LIST pl_new_list_ru; BL_ADDR next_0xC; BL_ADDR prev_0xC;
BRIF_0xC city[pl_all_cityes.cnt] <optimize=false>; // main data
}BT_0x0C;
//}BT_0x0C;
Последние 2 uint заголовка - адреса блоков типа 0xC, идущие "после" текущего, и "до". Пришлось переделать структуру BL_ADDR - теперь значение 0 не является ошибкой (оплачено возможностью вызвать block(0) как описание блока, начинающегося с нулевого абсолютного смещения).
// }BL_ADDR Структура адреса
typedef struct{
BitfieldDisablePadding(); //биты - как сплошная последовательность
unsigned int addr : 24 <bgcolor=0x00a0f0, fgcolor=cYellow>; //первые 3 байта - адрес, offset/0x800
unsigned short size : 8 <bgcolor=0x00a0f0, fgcolor=cDkAqua>; //последний байт - размер
local DWORD raw <hidden=true> = addr <<8 | size; //addr и size в виде 32-битном
local DWORD offset = addr <<11; // расчетное смещение от начала hex addr*800h
local uchar is_valid <hidden=true>; // Признак валидности структуры, default-0
local en_BL_TYPE type=0xFF; // Тип блока который начинается по этому адресу
if( raw ){ // 00 00 00 00 - too valid, but should not be checked, it place for BL_ADDR
if ( offset < FileSize() ) // Если offset внутри hex файла
is_valid = ( ReadUInt(offset) == raw ); // Прочитать реальное значение по offset адресу
if( !is_valid ){ // Если по рассчитанному смещению лежит иное значение
FSeek(FTell()-4); // откатить курсор назад на начало BL_ADDR
DWORD non_valid_bl_addr<bgcolor=cRed, fgcolor=cLtGray>; // paint it in red
}else{
// получить тип блока
type = ReadUByte(offset+5); // Через байта после BL_ADDR - type
}
}else{
is_valid = 1;// 00 00 00 00 - too valid
}
}BL_ADDR < read = Read_BL_ADDR>;
С новыми LIST pl_new_list_ofic (которых нет в русской версии) и LIST pl_new_list_ru (которых нет в официальных carindb) будем разбираться дальше.
Следующей секцией в типе 0xA шли восьмибитные структуры краткой информации brief по каждой из стран. Размер и значение этих структур в блоках типа 0xC не изменилось. Разве что в brief бывшая нулевая константа в 0xA так же обрела значение, ссылку на строку, PSTR pstr_region, несущую информацию о локальном наименовании региона, в котором находится город.
//{BRIF_0xC - main data, cityes
typedef struct{
local ushort size <format=hex, hidden=true> = head.addr.size * 0x800; // size of this block
local uint offset <hidden=true> = head.addr.offset; // absolute block offset
PSTR pstr_name; // ptr to zero-ended str
ubyte place_bitemask<bgcolor=cDkGreen, fgcolor=cYellow>;// 0xA={0,1}, 0xC={0,1,2,10,12}
en_LANG en_lang; // language code
PSTR pstr_region; // ushort always 0 in 0xA
PTR p_moreinfo <hidden=true>; // ptr to item of LIST pl_all_moreinfo;
// jump to MORE_INFO
local uint retur_here <hidden=true> = FTell();
FSeek(p_moreinfo.ptr + offset);
//MORE_INFO_0xC more_info;
FSeek(retur_here);
}BRIF_0xC<read=Read_BRIF_0xC>;
string Read_BRIF_0xC(BRIF_0xC &a){
local string s;
SPrintf(s, "%02X %s (%s) . Lang: %s",
a.place_bitemask,
a.pstr_name.str, a.pstr_region.str,
EnumToString(a.en_lang)
);
return s;
}//}BRIF_0xC
Интересной оказалась ex- CONST_B is_synonim, которая уж точно не константа. В 0xA все значения - 0, кроме одного раза, Австрии и её синонима. В 0xC известные варианты {0, 1, 2h, 10h, 12h} Не битовая ли маска, кстати?
Называю ubyte place_bitemask и пытаюсь отыскать закономерности.
00h относится к районам, областям, муниципалитетам, городам
10h - деревни, районы городов, минимальные части населений
02h - коммуны, 5й уровень самоуправления в Бельгии и Франции
12h - коммуны, субмуниципалитеты
Для окончательных выводов данных маловато, продолжаем наблюдение.
Блок 0xC, more info
Структура MORE_INFO_0xС, которая описывает дополнительную информацию о городе, от аналогичной в 0xA отличается значительно.
Для анализа возьму живописный курортный Хель — город в Польше, входит в Поморское воеводство, Пуцкий повят. Расположен на одноимённой косе. Занимает площадь 21,72 км². Население составляет 3480 человек. 54°36′42″ с. ш. 18°48′29″ в. д. Отдыхал с семьёй там пару недель, и знакомство с местностью может стать полезным для понимания данных.
В carindb_ee последовательно раскрываю Poland, moreinfo, city[7] == h [8]> a e i l o r u y
, дочерний элемент struct CH_IDX childs[1]
которого имеет значение block(0x0C1108); //of:608C78 'e' [38]>0c()>, hebdow, hecznarowice, hedwizyn, hejdyk, hel
.
Копипаст значения в текст template, применение его <F5>
, и в фокусе внимания информация блока 0xC, идущая после brif.
Размер данных будущей структуры MORE_INFO_0xC наглядно дается ссылками из BRIF_0xС на начала экземпляров.
Очевидно, что первые 8 байт выглядят как FAR_LIST, затем 4 байта еще один LIST, затем 4 байта неясного назначения (может, BL_ADDR?), 2 байта - вообще непонятно, наконец, 6 байт, равных 0. Для первичного описания структуры достаточно.
//{MORE_INFO_0xC;
typedef struct{
local ushort size <format=hex, hidden=true> = head.addr.size * 0x800; // size of this block
local uint offset <hidden=true> = head.addr.offset; // absolute block offset
FAR_LIST unk_1;
LIST unk_2;
BL_ADDR unk_3;
ushort _may_be_ptr<bgcolor=cRed>;
CONST_S zero(0);
CONST_I zero(0);
}MORE_INFO_0xC;
//}MORE_INFO_0xC;
unk_1 - список FAR_LIST в блоке CH_idx_F типа BT_0x0B_0x0D_0x0F_0x11, как в странах был список на города, так в городах - список на дороги-улицы, переименовать в ch_roads и добавить определение данных из индексного блока
CH_IDX road[ch_roads.pl_data.cnt]
.unk_2 - список LIST на категории POI, точек интереса, в конкретном городе. Ссылается на структуру определенно совпадающую с POI_CATEGORY из блока типа 0xA, не буду выдумывать новые сущности, использую прямо её же.
unk_3 - ссылка на один блок типа 0x00, не знаю пока что о нем ничего.
may_be_ptr - не указатель, т.к. указывает у разных городов на совершенно разные разделы данных в блоке. Предположу, что это уникальный ID города.
-
Константы и есть константы.... Стоп, на русской карте 64битная константа - иногда LIST, ведущий на непонятные данные, определение данных ставлю в зависимость от официальности карты.
Блок 0xC, категории POI
Получил у каждого города "внутри" список строковых названий улиц, и списки POI, разбитые по категориям. Некоторые категории уже известны по странам ( Interesting, Museum, Sport, Architecture, Nature_park).
Категория 0x0C - stacja paliw - прямым польским по-белому: автозаправка, в en_PLACE_CATEGORY добавляю
Gas_station = 0xC
Категория 0x0E -
biala foka, strzezony, ul. adm. steyera w., ul. boczna, ul. kuracyjna, ul. lesna, ul. szkolna
. Помню, на местности повеселила креативность - назвать автостоянку "Белый тюлень" (biala foka). А strzezony и переводится как "охраняемая парковка", значит, категория Parking = 0x0EКатегория 0x14 - уже известна по блоку стран, интересные места, не попадающие под другие категории, Interesting. Если вы подумали, что
focarium
- это тюлений зоопарк, то вы не ошиблись,hel cypel plaza
- купальный пляж в Хеле,latarnia morska hel
- если знать, что latarnia по-польски это маяк (а прожив в Хеле неделю вы не сможете не получить этого знания), то и эта достопримечательность понятна.Категории 0x1E и 0x17, обе содержат элементы, относящиеся к банкам. В одной категории один элемент, в другой - два. Отделение банка в Хеле одно, а банкоматов - сам видел. Так что вывод - где Bank, где ATM очевиден.
И так далее...
Для "восстановления" значения категории по названиям POI удобно, когда язык хотя бы близок к родному. И отлично, когда описание идет по спискам значений, местонахождение которых хоть чуть знакомо на местности. В Хеле 19 категорий POI - солидное число, объяснимое туристическо-курортной ориентацией городка. Некоторые категории распознаны с недостаточной долей уверенности. Категория Hospital = 0x2d
- пара медицинских заведений: "115 szpital wojskowy z przychodnia" и "przych. lek. samodzielny nzoz". А так же __city_railst_ =0x33
- в представителях только "Hel", как и в типе 0x30, и может быть, как железнодорожной станцией, так и автовокзалом.
В carindb_rus вообще у всех городов, в том числе и у польского Хеля, в наличии единственная категория POI (City = 0x30), с единственным представителем - этим самым городом.
Возвращаюсь в block countries, 0xA, и нахожу блок с еще одним немного знакомым польским городом: Ольштыном: block(0x0DE006); //of:6F0030 ''' [1]>0c(CITY)>, ol'shtyn
Аналогично просматриваю категории POI, и по именам внутри назначаю новым неизвестным категориям читабельное значение в enum.
29h : [1]: > w block(0x1550408); //off:AA841A8
block(0x1BA0208); //of:DD012C0 'w' [2]>10()>, wojewodzki szpital spec pogotowie, wojewodzki szpital specjalistyczny
Большая поликлиника, воеводство - крупнейшая административная единица Польши. А представителей типа POI 2Dh - 12 штук. Похоже, правильно когда Hospital=29h, а бывший госпиталь переименовать в Clinic=2Dh
2Ah : [2]: > k p block(0x1550408); //off:AA841B4
block(0x1BA0208); //of:DD012D0 'k' [6]>10()>, komenda miejska policji, komenda miejska policji, komenda miejska policji, komenda policji rewir dzielnicowych, komenda wojewodzka policji
block(0x1BA0208); //of:DD01300 'p' [1]>10()>, posterunek policji
Без сомнений, Police = 0x2A
33h __city_railst_: [2]: > g o block(0x1550408); //off:AA84484
block(0x1BA0A08); //of:DD05508 'g' [1]>10()>, gutkowo
block(0x1BA0A08); //of:DD05510 'o' [2]>10()>, olsztyn glowny, olsztyn zachodni
Olsztyn Gutkowo - есть такая ж/д станция, как и станции Ольштын Центральный и Ольштын Западный, подсказывает гугл. RailStation=0x33
Вот так, раскрывая с гуглом содержимое категорий, пополняю перечисление. После Хеля, Ольштына и Варшавы оно принимает вид:
//{en_PLACE_CATEGORY 0xA 0xC blocks
typedef enum <ushort>{
CarRepair = 0xB,
GasStation = 0xC,
CarRent = 0xD,
Parking = 0xE,
ParkAndRide = 0xF, // park and ride, intercept parking, parkuj i jedz metro marymont
RestingPlace= 0x10, // mop olesnica mala, mop jonas polnoc, mop jonas poludnie
Intresting = 0x14,
Hotel = 0x15,
Restaurant = 0x16,
Bank = 0x17,
Culture = 0x18,
Library = 0x19,
Court = 0x1a,
Embassy = 0x1D,
BankomatATM = 0x1E,
Tourist_info= 0x1F,
Museum = 0x20, //musee d'art moderne, musee de l'armee, musee des sciences Naturelles
Theater = 0x21,
Sport = 0x23, //automotodrom brno, o2 arena, ski areal jasna, o2 arena
Church = 0x24,
Architecture = 0x25, //
Fun_park = 0x26, //boudewijn seapark, bruparck
Nature_park = 0x27, //het zwin, nationale plantentuin
UN_United_Nations = 0x28,
Hospital = 0x29,
Police = 0x2A,
Goverment = 0x2B,
Post = 0x2C,
Clinic = 0x2D,
Aphoteca = 0x2E,
Shop = 0x2F, // supermarket?
City = 0x30, //russian map
Cinema = 0x31,
__golf_club = 0x32, //first warsaw golf country club
RailStation = 0x33,
Border_point= 0x34, //
Seaport = 0x35, // oostende ramsgate (tonnel), zeebrugge (port), need mode exmpls
BusStation = 0x36,
Pier = 0x37, // przystan kortowska-> Piers, Dock
Shcool = 0x38,
Winery = 0x39, // lanson caves, champagne krug, moet et chandon, ruinart caves
Aeroport = 0x3a, //brussel nationaal, brussels airport; , luchthaven brussel
__bmw_motorbike_service = 0x3B,
Business = 0x3D // Olsztyn, Poland https://en.wikipedia.org/wiki/Michelin_Polska
}en_PLACE_CATEGORY <format=hex, bgcolor=cDkGreen, fgcolor=cAqua>;
Явно есть еще пробелы в последовательности, но это все варианты из блоков типа 0xA и 0xC всех трех вариантов carindb.
Блок 0xC, необязательная характеристика категории POI
Структура POI_CATEGORY из блока типа 0xA несла в себе неиспользуемый в 0xA элемент PTR unkn_str_begin, абсолютно во всех имеющихся примерах указывавший на константное место - начало первой строки.
//POI_CATEGORY - additional data - part of MORE_INFO_0xA
typedef struct{
FAR_LIST POIs; //type 0xF, streets ch_idx
en_PLACE_CATEGORY en_cat_places; //
PTR unkn_str_begin;
}POI_CATEGORY
В блоках тип 0xC переменная reference_addr_start, заменившая unkn_str_begin, у некоторых категорий POI указывает на структуру {FAR_LIST, PSTR, CONST_S(0)}, из области pl_new_list_ofic заголовка блока. Тип PTR не несет количеств, только указатель на первую. Причем, судя по голубым "пятнашкам", для различных категорий POI число этих структур - неодинаковое.
После перечисления всех POI_CATEGORY из LIST pl_all_cat_pois в заголовке, внезапно в хвосте нахожу еще одну, пустую POI_CATEGORY, содержащую только значение reference_addr_start (или unkn_str_begin).
И этот экземпляр подсказывает, как определяется число элементов новой структуры, на которые есть ссылка на начальный адрес, но нет количества или адреса окончания.
Навигатор, "заглядывает вперёд", в следующий экземпляр POI_CATEGORY, беря оттуда значение reference_addr_start - использует его, как значение, до которого размещаются данные текущей структуры.
Меняю с учетом вышесказанного POI_CATEGORY, которая теперь будет отображать для категорий POI новые структуры POI_REFERENCE.
//{part BT_0x0C, use in POI_CATEGORY
typedef struct{
local ushort size <format=hex, hidden=true> = head.addr.size * 0x800; // size of this block
local uint offset <hidden=true> = head.addr.offset; // absolute block offset
FAR_LIST ch_idx_11;
local uint ret_str_here <hidden=true> = FTell();
FSeek(ch_idx_11.pl_data.offset); // jmp to streets ch_idx
CH_IDX POI_reference[ch_idx_11.pl_data.cnt] <optimize=false>;
FSeek(ret_str_here);
PSTR name<fgcolor=cRed>;
CONST_S zero(0);
}POI_REFERENCE <read=Read_POI_REFERENCE>;
string Read_POI_REFERENCE(POI_REFERENCE &a){
string s;
SPrintf(s, "%s: [%i] %s(%X)",
a.name.str, a.ch_idx_11.pl_data.cnt,
EnumToString(a.ch_idx_11.far_block.type),
a.ch_idx_11.far_block.type
);
return s;
}
//{part BT_0x0C
//{BT_0x0A
//POI_CATEGORY - additional data - part of MORE_INFO_0xA , used in 0xC too
typedef struct{
local ushort size <format=hex, hidden=true> = head.addr.size * 0x800; // size of this block
local uint offset <hidden=true> = head.addr.offset; // absolute block offset
FAR_LIST POIs; //type 0xF, streets ch_idx
if(POIs.pl_data.cnt){
local uint ret_str_here <hidden=true> = FTell();
FSeek(POIs.pl_data.offset); // jmp to streets ch_idx
CH_IDX POI_name[POIs.pl_data.cnt] <optimize=false>;
FSeek(ret_str_here);
}
en_PLACE_CATEGORY en_cat_places; //
PTR reference_addr_start;
local PTR reference_addr_till;
reference_addr_till.ptr = ReadUShort(FTell()+10);
// BL_0xX - BRIF - MORE_INFO - POI_CATEGORY
// in 0xA unkn_str_begin - pointer to firts str in all items
// on 0xC reference_addr_start - pointer to item pl_new_list_ofic array
// {only in oficial 0xC will work
if( exists( parentof(this).pl_category_POI_city) && IS_OFICIAL_MAP ){
// in carindb_rus pointed to begin pl_new_list_ru array
if ( ( (reference_addr_till.ptr - reference_addr_start.ptr) / 8 ) > 0 ){
local uint ret_poi_here <hidden=true> = FTell();
FSeek(offset + reference_addr_start.ptr);
POI_REFERENCE POI_reference[ (reference_addr_till.ptr - reference_addr_start.ptr) / 8] <optimize=false>;
FSeek(ret_poi_here);
}
}
// }only in oficial 0xC will work
// {debug - print unknown en_PLACE_CATEGORY:
if(Strlen(EnumToString(en_cat_places) ) == 0){
Printf("POI_CATEGORY %X cat:%X - unknown en_PLACE_CATEGORY\n",
FTell(), en_cat_places);
}
// }debug - print unknown en_PLACE_CATEGORY:
}POI_CATEGORY<read=Read_POI_CATEGORY>;
string Read_POI_CATEGORY(POI_CATEGORY &a){
string s;
SPrintf(s,"%02Xh %s: [%i]: >%s block(0x%X); //off:%X",
a.en_cat_places, EnumToString(a.en_cat_places),
a.POIs.pl_data.cnt, getChList(a.POIs.pl_data),
a.POIs.far_block.raw,
a.POIs.pl_data.offset
);
return s;
}//}POI_CATEGORY == ex ADDINFO_0xA used in 0xA, 0xC
Назначение их не ясно однозначно. В Хеле, например, они есть только у 2 категорий:
Банки
Банкоматы
Я предположил бы, что это перечень альтернатив, ближайших к описываемым POI (адреса блоков типа 0x10 - разные). Но в разных блоках может быть и одинаковая информация по этим точкам.
Пока не будет понимания структуры блока типа 0x10 минимум до уровня 0xC - дать ответ "что это было" не представляется возможным.
Резюмируя
Ссылки "наружу" данных блока 0xC и вложенность структур представления.
BRIF_0xC city[pl_all_cityes.cnt]
MORE_INFO_0xC
CH_IDX road[ch_roads.pl_data.cnt] // -> CH_idx_f -> Eh
POI_CATEGORY city_POI[pl_category_POI_city.cnt] // -> CH_idx_11 -> 10h
POI_REFERENCE[(reference_addr_till.ptr - reference_addr_start.ptr) / 8 ]
CH_IDX POI_reference[ch_idx_11.pl_data.cnt] // -> CH_idx_11 -> 10h
Тезисно главное.
Блок типа 0xC содержит информацию о городах. Кстати, в блоке сведений нет сведений, к какой стране относится город. "Город" - условность, это может быть и район или территориально-административная единица страны.
Заголовок блока 0xC равен по размеру заголовку блока стран 0xA (30h), однако дополнен значащими полями на месте нулевых констант: адресами предыдущего и следующего блоков городов, указатель на все категории точек интереса (POI), указатель на все "дополнительные" точки интереса POI_REFERENCE (предположительно ближайшие аналогичные, или ориентиры для поиска)
Краткая информация - BRIF - наименование города на местном языке (вот не всегда, есть и английские варианты), наименование административной территории, к которой относится город, язык (не без смешных ошибок, в Ольштыне, например, по мнению картографов, говорят по-чешски, а не по-польски) и признак, возможно ли отнесение к этому городу/поселению какой-то части, или данный населенный пункт - минимальная единица. Структурно соответствует аналогичной в 0xA, но с ссылкой на строку региона на месте константы.
Расширенная информация - MORE_INFO - содержит указатель на список улиц, дорог города или территории (через тип CH_idx_f на тип Eh), указатель на внутриблоковый список категорий POI этого города, адрес блока типа 00(block_type_00), ID города (под вопросом). В русском варианте карты - указатель на список значений размером 1 байт, с возможными значениями 0, 1 и 2. Для чего эти структуры - неясно.
В русском варианте карты (а там впихнули 42, СТРАНЫ, Карл!) список дорог/улиц у городов крайне скудный (Санкт-Петербург - 81 улица на многомиллионный город; Калининград - 17 улиц и 11 автодорог; Хель - 1 дорога, против 58 в "официальной"; Белгород - 2 дороги, они же улицы {p185, p186}, вся Белгородская область - аж 9 дорог {e105, m2, p185, p186, p187, p188, p189}; белорусский Брест - город, не область - 5 улиц: {e30, e581, m1, p17, p83})
Категории POI - структура POI_CATEGORY, по данным совпадающая с такой же в типе 0xA, но в официальной карте указатель структуры не константа в пределах блока, а ссылка на дополнительные списки POI POI_REFERENCE. Списки POI конкретной категории даны через блоки типа CH_idx_11 на блоки типа 10h, как в POI_REFERENCE, так и в POI_CATEGORY.
В русской карте единственный вариант категории POI у городов - City=30h, с единственным представителем у каждого города - самим этим городом.
Значительно расширен список возможных значений en_PLACE_CATEGORY - кодификации мест интереса.
Важно: впервые встретил и определил новый механизм указания количества адресуемых структур, когда есть указатель на начало, а из следующего экземпляра структуры берется её указатель на начало, как указатель на окончание данных текущей. Признак: после всех экземпляров структур (в данном случае POI_CATEGORY) есть дополнительная хвостовая "пустая" структура, с единственно заполненным полем указателя. Используется в блоке типа 0xC для определения количества экземпляров списка POI_REFERENCE, присутствующего не у всех категорий POI.
Продолжение анализа - в следующей статье.