[File] [PATCH] of Magdir/map for Garmin map (*.img *.vpm)
Jörg Jenderek
joerg.jen.der.ek at gmx.net
Thu Dec 27 22:48:42 UTC 2018
Hello,
some days ago i run file command version 5.35 on some Garmin map (*.img)
and voice files (*.vpm). All such inspected examples are misidentified
as "DOS/MBR boot sector".
According to page about Garmin ".img" format on Wikipedia such examples
are used by Garmin navigation devices use to store the maps. So i add
that URL and magic lines to Magdir/map.
>From there i found on SourceForge a PDF document imgformat-1.0.pdf with
title "Garmin IMG File Format". So i add that URL as reference.
Unfortunately the informations in that document dated about year 2005
are incomplete or sometimes wrong, because John Mechalas gets file
format information by reverse engineering.
First i look for a signature at end of 1st block.
0x1FE leshort =0xAA55
This looks similar to MBR boot sector handled by ./filesystems. That
explains the misidentification. So at least one additional test line is
needed. According to documentation look for valid signatures like
DSKIMG\0 or DSDIMG\0 by line:
>0x13 string =IMG\0
That was sufficient for my examples. So Garmin staff is identified and
now i call subroutine to display information about that file format by lines
>>0 use garmin-map
0 name garmin-map
>0 ubyte x Garmin
!:mime application/x-garmin-map
Display map description ( two parts, 0x20 padded) like
"Freizeitkarte_PYRENEES (Release 18.09)" in PYRENEES_en_gmapsupp.img by
lines
>0x49 string x %.20s
>0x65 string >\ \b%.31s
Some field are not interesting for "normal" users. So i add lines as
comments like for checksum:
#>0xF ubyte x \b, Checksum 0x%x
In some fields often standard values are used. So display that value
only if unusual values are used like for 8-char signature field by line:
>0x10 string !DSKIMG \b, signature "%.7s"
According to PDF document after the information inside header the
remaining 314 bytes should be nils. This is true for real maps, but
wrong for gmaptz.img and all *.vpm examples according to my own
experiments. So print non nil value by line:
>0x84 uquad !0 \b, at 0x84 0x%llx
The img format itself is a container for other data sub files. Depending
on sub files things are different. Inspired by DOS FAT file system meta
information about sub files is stored in a File Allocation table (FAT).
Each FAT block is 512 bytes in length and the physical block number of
FAT beginning is stored at offset 0x40 according to openstreetmap wiki.
This information can be displayed by a line like:
#>0x40 ubyte x \b, FAT at phy. block %u
By pointer expression goto block before FAT by line
>>(0x40.b*512) ubyte x
Then jump relative to next block. Now we are at 1st FAT entry and
inspect FAT entry metadata information by calling sub routine like:
>>>&511 use garmin-fat
It is not needed but for interest i also display the first four FAT
entries by 3 additional lines
>>>&1023 use garmin-fat
>>>&1535 use garmin-fat
>>>&2047 use garmin-fat
In the FAT entry the stored information makes only sense to be displayed
after some tests. Check if the entry is not sub part and flag for true
sub file is set. This is done by lines:
0 name garmin-fat
>0 ubyte x \b;
>0x10 uleshort =0
>>0 ubyte =1
Afterwards now show sub-file name like MAKEGMAP or 12345678 by line
>>>0x1 string x %.8s
Next comes the sub-file typ like RGN TRE MDR LBL. This is shown by line:
>>>0x9 string x \b.%.3s
This is the most interesting part, because each sub-file typ carries a
specific data. Some known file sub-types are:
• RGN. Map elements such as poly lines, polygons and points.
• LBL. Labels for map elements, city names, localities, etc.
• TRE. Map structure information that organizes the map elements into a
data tree.
Afterwards come the 32-bit block sequence numbers used by FAT entry.
This can be shown by line like
#>>>0x20 ubequad x \b, seq. 0x%16.16llx
To store big maps a trick is used, which is known for DOS experts. The
unit used for counting block is not the physical block size, but the
logical block size. So with a large logical block size like 16384 in
sample CARPATHIAN_en_gmapsupp.img it is possible to address many map
elements with details. The logical block size is stored in the header in
exponent fields E1 and E2. The logical block size is is calculated by
formula:
blocksize=2**(E1+E2)
Exponent E1 appears to be always be 0x09 which gives a minimum block
size of 512 bytes. So instead E1 and E2 show for usual block sizes the
values by lines:
>0x61 ubyte !0x09 \b, E1=%u
>>0x62 ubyte x \b, E2=%u
>0x61 ubyte =0x09 \b, blocksize
>>0x62 ubyte 0 512
>>0x62 ubyte 1 1024
>>0x62 ubyte 2 2048
>>0x62 ubyte 3 4096
>>0x62 ubyte 4 8192
>>0x62 ubyte 5 16384
>>0x62 default x
>>>0x62 ubyte x E2=%u
For most garmin map file name extension ".img" is used. But some maps
with extension ".vpm" contains garmin voice processing module (normally
handled by Magdir/audio and further container for WAV sounds handled by
Magdir/riff). My inspected VPM samples contain in first FAT entry the
name "DLLINFO TXT". So look for that name. If found it is a map with
voice part. If not then it is a normal IMG map. This is done in
subroutine by lines like:
0 name garmin-map
>0 ubyte x Garmin
>(0x40.b*512) ubyte x
>>&512 string =DLLINFO\ TXT map (Voice Processing)
!:ext vpm
>>&512 string !DLLINFO\ TXT map
!:ext img
After applying the above mentioned modifications by patch
file-5.35-map-img.diff then all inspected examples are now described by
Magdir/map like:
CARPATHIAN_en_gmapsupp.img:
Garmin map v0.00 Freizeitkarte_CARPATHIAN (Release 18.09)
updated 2018-09, created 2018-09-12 19:27:44,
blocksize 16384, at 0x74000 156 bytes "GARMIN TYP"
; MAKEGMAP.MPS, 13612 bytes
; next 0x0003
; MAKEGMAP.MPS, 13612 bytes
; 00008070.TYP, 38347 bytes
gmaptz.img:
Garmin map v11.02 Time Zone Map
updated 2013-01, created 2013-01-07 9:46:41,
blocksize 1024
; TIMEZONE.RGN, 573378 bytes
; flag 0
; flag 0
; flag 0
, at 0x84 0x503de
test-map.img:
Garmin map v0.00 TEST_MAP_ ,
updated 2018-12, created 2018-12-23 4:18:00,
blocksize 512, at 0xc00 125 bytes "GARMIN RGN"
; 19088743.RGN, 512 bytes
; next 0x0003
; 19088743.RGN, 512 bytes
; 19088743.TRE, 241 bytes
Deutsch__Yannick_D4481-00_0210.vpm:
Garmin map (Voice Processing) v2.10 yannick ,
updated 2014-09, created 2014-09-19 11:46:41,
blocksize 1024
; DLLINFO .TXT, 1034 bytes
; flag 0
; flag 0
; flag 0
, at 0x84 0x202de
English_British_Serena.vpm:
Garmin map (Voice Processing) v1.30 serena ,
updated 2013-05, created 2013-05-30 11:14:46,
blocksize 1024
; DLLINFO .TXT, 820 bytes
; flag 0
; flag 0
; flag 0
, at 0x84 0x202de
I hope my diff file can be applied in future version of file utility.
I tried to remove the misidentification, but i failed. In theory it
should be possible to distinguish "DOS/MBR boot sector", "DOS executable
(COM), boot code" and "Linux kernel x86 boot executable" by looking for
appearance or absence of characteristic patterns like:
>0x1FE leshort 0xAA55 \b, boot code
in Magdir/msdos or in Magdir/linux
514 string HdrS Linux kernel
or test lines
0x1FE leshort !0xAA55 NO x86 BOOT SIGNATURE
514 string !HdrS NO Linux kernel
Unfortunately if size of inspected samples is low enough ( for example
only 446 bytes) then the above lines and followers are never executed by
file command version 5.35 and no error message is displayed. Maybe
somebody is smart enough to fix this behavior. That would be nice.
With best wishes
Jörg Jenderek
--
Jörg Jenderek
-------------- next part --------------
--- file-5.35/magic/Magdir/map.old 2017-03-17 21:34:26 +0000
+++ file-5.35/magic/Magdir/map 2018-12-26 20:00:14 +0000
@@ -27,4 +27,155 @@
>>53 byte 10 \b (Totals)
+# Summary: Garmin map
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Garmin_.img
+# Reference: https://wiki.openstreetmap.org/wiki/OSM_Map_On_Garmin/IMG_File_Format
+# sourceforge.net/projects/garmin-img/files/IMG%20File%20Format/1.0/imgformat-1.0.pdf
+# GRR: similar to MBR boot sector handled by ./filesystems
+0x1FE leshort =0xAA55
+# look for valid map signature
+>0x13 string =IMG\0
+>>0 use garmin-map
+0 name garmin-map
+>0 ubyte x Garmin
+!:mime application/x-garmin-map
+# If non-zero, every byte of the entire .img file is to be XORed with this value
+>0 ubyte !0 \b, 0x%x XORed
+# goto block before FAT
+>(0x40.b*512) ubyte x
+# 1st fat name "DLLINFO TXT" only found for vpm
+>>&512 string =DLLINFO\ TXT map (Voice Processing)
+# there exist 2 other Garmin VPM formats; see ./audio
+!:ext vpm
+# Deutsch__Yannick_D4481-00_0210.vpm
+#>>>512 search/0x0116da60/s RIFF \b; with
+# determine type voice type by ./riff
+#>>>>&0 indirect x \b
+>>&512 string !DLLINFO\ TXT map
+!:ext img
+# 9 zeros
+>1 ubelong !0 \b, zeroes 0x%x
+# Map's version major
+>8 ubyte x v%u
+# Map's version minor
+>9 ubyte x \b.%.2u
+# Map description[20], 0x20 padded
+>0x49 string x %.20s
+# Map name, continued (0x20 padded, \0 terminated)
+>0x65 string >\ \b%.31s
+# Update year (+1900 for val >= 0x63, +2000 for val <= 0x62)
+>0xB ubyte x \b, updated
+>>0xB ubyte >0x62
+>>>0xB ubyte-100 x 20%.2u
+>>0xB ubyte <0x63
+>>>0xB ubyte x 20%.2u
+# Update month (0-11)
+>0xA ubyte x \b-%.2u
+# All zeroes
+>0xc uleshort !0 \b, zeroes 0x%x
+# Mapsource flag, 1 - file created by Mapsource, 0 - Garmin map visible in Basecamp and Homeport
+#>0xE ubyte !0 \b, Mapsource flag 0x%x
+>0xE ubyte 1 \b, Mapsource
+# Checksum, sum of all bytes modulo 256 should be 0
+#>0xF ubyte x \b, Checksum 0x%x
+# Signature: DSKIMG 0x00 or DSDIMG 0x00 for demo map
+>0x10 string !DSKIMG \b, signature "%.7s"
+# Creation year
+>0x39 uleshort x \b, created %u
+# Creation month (0-11)
+>0x3b ubyte x \b-%.2u
+# Creation day (1-31)
+>0x3c ubyte x \b-%.2u
+# Creation hour (0-23)
+>0x3d ubyte x %u
+# Creation minute (0-59)
+>0x3e ubyte x \b:%.2u
+# Creation second (0-59)
+>0x3f ubyte x \b:%.2u
+# Map file identifier like GARMIN\0
+>0x41 string !GARMIN \b, id "%.7s"
+# Block size exponent, E1; appears to always be 0x09; minimum block size 512 bytes
+>0x61 ubyte !0x09 \b, E1=%u
+# Block size exponent, E2 ; file blocksize=2**(E1+E2)
+>>0x62 ubyte x \b, E2=%u
+>0x61 ubyte =0x09 \b, blocksize
+>>0x62 ubyte 0 512
+>>0x62 ubyte 1 1024
+>>0x62 ubyte 2 2048
+>>0x62 ubyte 3 4096
+>>0x62 ubyte 4 8192
+>>0x62 ubyte 5 16384
+>>0x62 default x
+>>>0x62 ubyte x E2=%u
+# MBR signature
+>0x1FE leshort !0xAA55 \b, invalid MBR
+# 512 zeros
+>0x200 uquad !0 \b, zeroes 0x%llx
+# First sub-file offset (absolute); sometimes NO/UNKNOWN sub file!
+>0x40C ulelong >0 \b, at 0x%x
+# sub-file Header length
+#>>(0x40C.l) uleshort x \b, header len 0x%x
+>>(0x40C.l) uleshort x %u bytes
+# sub-file Type[10] like "GARMIN RGN" "GARMIN TRE", "GARMIN TYP", etc.
+>>(0x40C.l+2) ubyte >0x1F
+>>>(0x40C.l+2) ubyte <0xFF
+>>>>(0x40C.l+2) string x "%.10s"
+# 0x00 for most maps, 0x80 for locked maps (City Nav, City Select, etc.)
+>>>>(0x40C.l+13) ubyte >0 \b, locked 0x%x
+# Block sequence numbers like 0000 0100 0200 ... FFFF
+# >0x420 ubequad >0 \b, seq. 0x%16.16llx
+# >>0x428 ubequad >0 \b%16.16llx
+# >>>0x430 ubequad >0 \b%16.16llx
+# >>>>0x438 ubequad >0 \b%16.16llx
+# >>>>>0x440 ubequad >0 \b%16.16llx
+# >>>>>>0x448 ubequad >0 \b%16.16llx
+# >>>>>>>0x450 ubequad >0 \b%16.16llx
+# >>>>>>>>0x458 ubequad >0 \b%16.16llx
+# >>>>>>>>>0x460 ubequad >0 \b%16.16llx
+# >>>>>>>>>>0x468 ubequad >0 \b%16.16llx
+# >>>>>>>>>>>0x470 ubequad >0 \b%16.16llx
+# >>>>>>>>>>>>0x478 ubequad >0 \b%16.16llx
+# >>>>>>>>>>>>>0x480 ubequad >0 \b%16.16llx
+# >>>>>>>>>>>>>>0x488 ubequad >0 \b%16.16llx
+# >>>>>>>>>>>>>>>0x490 ubequad >0 \b%16.16llx
+# >>>>>>>>>>>>>>>>0x498 ubequad >0 \b%16.16llx
+# >>>>>>>>>>>>>>>>>0x4A0 ubequad >0 \b%16.16llx
+# >>>>>>>>>>>>>>>>>>0x4A8 ubequad >0 \b%16.16llx
+# look for end of FAT
+#>>0x420 search/512/s \xff\xff FAT END
+# Physical block number of FAT header
+#>0x40 ubyte x \b, FAT at phy. block %u
+>0x40 ubyte x
+>>(0x40.b*512) ubyte x
+# 1st FAT block
+>>>&511 use garmin-fat
+# 2nd FAT block
+>>>&1023 use garmin-fat
+# 3th FAT block
+>>>&1535 use garmin-fat
+# 4th FAT block
+>>>&2047 use garmin-fat
+# ... xth FAT block
+#
+# 314 zeros but not in vpm and also gmaptz.img
+>0x84 uquad !0 \b, at 0x84 0x%llx
+# display FileAllocationTable block entry in garmin map
+0 name garmin-fat
+>0 ubyte x \b;
+# sub file part; 0x0003 seems to be garbage
+>0x10 uleshort !0 next 0x%4.4x
+>0x10 uleshort =0
+# fat flag 0~dummy block 1~true sub file
+>>0 ubyte !1 flag %u
+>>0 ubyte =1
+# sub-file name like MAKEGMAP 12345678
+>>>0x1 string x %.8s
+# sub-file typ like RGN TRE MDR LBL
+>>>0x9 string x \b.%.3s
+# size of sub file
+>>>0xC ulelong x \b, %u bytes
+# 32-bit block sequence numbers
+#>>>0x20 ubequad x \b, seq. 0x%16.16llx
+
# TOM TOM GPS watches ttbin files:
# http://github.com/ryanbinns/ttwatch/tree/master/ttbin
More information about the File
mailing list