Как стать автором
Обновить

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

Время на прочтение19 мин
Количество просмотров2.1K

Предыдущие статьи о реверсе данных автомобильных навигаторов 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.

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

Теги:
Хабы:
Всего голосов 8: ↑7 и ↓1+6
Комментарии1

Публикации