03. С прозрачными воротами и яркою звездой

от автора

Предыдущие статьи о реверсе данных автомобильных навигаторов Siemens VDO Dayton CARMiN

Блоки 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.

Продолжение анализа — в следующей статье.


ссылка на оригинал статьи https://habr.com/ru/post/599661/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *