Within the last couple of days I made some progress with decoding the Geogrid-Viewer OVL binary file format. Geogrid-Viewer is part of various Top50 map products available in Germany and Austria. The most recent Top50 versions use file format version 5.0, which consists of ZIP compressed XML files and likely very easy to convert. But there are still a lot of older OVL files up to version 4.0 available on the internet. My main motivation were these version 2.0 files, that I wanted to convert into GPX.
To get more information about the OVL file format, it helped to observe Geogrid-Viewer with Procmon while reading OVL files. Fortunately the OVL files are read in pieces, which provides valuable information about the file structure. For tricky bits OllyDbg was helpful as well.
With the following file format "specifications" (there are still a lot of unknowns in there) I've improved the gpsbabel importer I wrote a year ago. I can now successfully parse all of the 153 random binary OVL samples I found on the internet. The patch will be provided to gpsbabel soon.
Update (2016-01-13): Corrected type1/type2 decoding in V3.0/V4.0
Update (2016-01-16): Patch has been merged into gpsbabel git
Update (2019-07-12): “Radtouren rund um Paderborn” has disappeared, link to web.archive.org instead.
Update (2020-01-08): Corrected subtype decoding in V2.0, add missing padding after magic bytes in V3.0 and V4.0
Here is a pseudo C code like structure definition of version 2.0 OVL files. Those files can be identified by looking at the first 23 bytes of the file, which contain the string "DOMGVCRD Ovlfile V2.0".
// Geogrid-Viewer OVL binary file format version 2.0
struct FILE {
struct HEADER {
char magic[23]; // "DOMGVCRD Ovlfile V2.0:\0"
; // usually 0x90 or 0x00
uint16 header_lenif (header_len > 4) {
;
uint16 i1;
uint16 i2char map_name[header_len - 4];
// the map_name field contains more information
// then just the null-terminated name, but details
// are unknown
}
}
struct RECORD[] {
;
uint16 entry_type// 0x02: text
// 0x03: line
// 0x04: area
// 0x05: rectangle
// 0x06: circle
// 0x07: triangle
// 0x09: bitmap
;
uint16 entry_group;
uint16 entry_zoom;
uint16 entry_subtypeif (subtype != 0x01) {
;
uint32 text_lenchar text[text_len];
}
union {
struct TEXT {
;
uint16 color;
uint16 size;
uint16 trans;
uint16 font;
uint16 angledouble lon;
double last;
;
uint16 text_lenchar text[text_len];
}
struct LINE_AREA {
;
uint16 color;
uint16 width;
uint16 type;
uint16 point_countstruct POINT {
double lon;
double lat;
}[point_count];
}
struct RECT_CIRC_TRI {
;
uint16 color;
uint16 prop1;
uint16 prop2;
uint16 angle;
uint16 stroke;
uint16 areadouble lon;
double lat;
}
struct BITMAP {
;
uint16 color;
uint16 prop1;
uint16 prop2;
uint16 prop3double lon;
double lat;
;
uint32 data_lenchar data[data_len];
}
}
}
}
Version 3.0 and 4.0 are a bit different as they allow multiple parts in one file that all start with the magic bytes "DOMGVCRD Ovlfile V3.0" or "DOMGVCRD Ovlfile V4.0". In addition there are two types of data sections. A "label" section that might contain group definitions or similar. And a "record" section that contain the real data, like tracks or other kind of geometric objects.
The header contains the number of "label" and "record" sections following the header (see label_count and record_count). These might be zero, which means the part does not contain any label or record sections. OVL files containing parts without label or record section or without both do actually exist.
struct FILE {
// A version 3.0/4.0 file might contain multiple parts all
// starting with DOMGCRD magic bytes and header
struct PART[] {
struct HEADER {
char magic[23];
// either "DOMGVCRD Ovlfile V3.0:\0"
// or "DOMGVCRD Ovlfile V4.0:\0"
char padding[8];
;
uint32 label_count;
uint32 record_count;
uint16 text_len[text_len];
uint16 text;
uint16 i1;
uint16 i2;
uint16 i3; // usually 0x90 or 0x00
uint16 header_len;
uint16 i4;
uint16 i5if (header_len > 4) {
;
uint16 i1;
uint16 i2char map_name[header_len - 4];
// the map_name field contains more information
// then just the null-terminated name, but details
// are unknown
}
}
struct LABEL[label_count] {
char label_header[8];
char label_number[14];
;
uint16 label_text_lenchar label_text[label_text_len];
;
uint16 label_flags1;
uint16 label_flags2};
struct RECORD[record_count] {
;
uint16 record_type// 0x02: text
// 0x03: line
// 0x04: area
// 0x05: rectangle
// 0x06: circle
// 0x07: triangle
// 0x09: bitmap
// 0x17: line
;
uint16 record_prop1;
uint16 record_prop2;
uint16 record_prop3;
uint16 record_prop4;
uint16 record_prop5;
uint16 record_prop6;
uint16 record_prop7;
uint16 record_prop8; // 0x0001=ZOOM, 0x0002=NOZOOM, 0x0800=ROUNDED, 0x10000=CLOSED
uint16 record_flags;
uint16 record_prop10;
uint16 record_text_lenchar record_text[record_text_len];
;
uint16 record_type1if (record_type1 != 1) {
;
uint32 record_object1_lenchar record_object1[record_object1_len];
}
;
uint16 record_type2if (record_type2 != 1) {
;
uint32 record_object2_lenchar record_object2[record_object2_len];
}
union {
struct TEXT {
;
uint16 text_prop1;
uint32 text_prop2;
uint16 text_prop3; // 0x80bbggrr
uint32 text_color; // 100-1100
uint16 text_size; // 1=transparent, 2=solid, 3-8=various patterns
uint16 text_back; // 1=Arial, 3=Courier, 4=Times, 10=Comic
uint16 text_font; // 100-460
uint16 text_angledouble lon;
double lat;
double unkown;
;
uint16 text_label_lenchar [text_label_len];
}
struct AREA_LINE {
;
uint16 line_prop1;
uint32 line_prop2; // 0x1e
uint16 line_prop3; // 0x80bbggrr
uint32 line_color; // 101-115
uint16 line_width; // 1=transparent, 2=solid, 3-8=various patterns
uint16 line_back;
uint16 line_countif (record_type == 0x04)
; // 1=solid, 2=dashed, 3=dotted, 4=dot-dash
uint16 line_stroke}
struct COORD[line_count] {
double lon;
double lat;
double unkown;
}
struct RECT_CIRC_TRI {
;
uint16 rct_prop1;
uint32 rct_prop2; // 0x1e
uint16 rct_prop3; // 0x80bbggrr
uint32 rct_color;
uint32 rct_width;
uint32 rct_height; // 1=solid, 2=dashed, 3=dotted, 4=dot-dash
uint16 rct_stroke; // 0-360
uint16 rct_angle; // 101-115
uint16 rct_lwidth; // 1=transparent, 2=solid, 3-8=various patterns
uint16 rct_backdouble lon;
double lat;
double unkown;
}
struct BITMAP {
;
uint16 bmp_prop1;
uint32 bmp_prop2; // 0x1e
uint16 bmp_prop3;
uint32 bmp_prop4;
uint32 bmp_width;
uint32 bmp_heightdouble lon;
double lat;
double unkown;
;
uint32 bmp_len; // 100-460
uint16 bmp_anglechar bmp_data[bmp_len];
}
}
}
}
}