[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