Ralf Kleineisel wrote:
On 01/13/2010 09:14 PM, Simon Eugster wrote:I thought it would be enough to just search for the map ID in a .img file and replace it by a new one. Well, after some research I found out that this is not true.http://svn.parabola.me.uk/mkgmap/trunk/src/uk/me/parabola/imgfmt/app/trergn/MapValues.java I tried to check out TreCalc listed there, but it would not compile ...Would it be possible to change mkgmap that way that it supports in-file replacing of the map ID (without regenerating all stuff)?Wgmaptool can do this.
Well ... Now my Python script can do it as well. I've mainly copied Steve's code (thank you!) and translated it to Python.It took some time (as you can see when you look when I sent this mail) to get everything right ... but now it is working :)
$ python change-mapID.py 1000.img 12343210 Old map ID at fe00: 0x000003e8 = 1000 Header length: 188 Values: e1c63e9b ee9a3e9a eeee3e9a eeee3e9a (original) Values: 57c264ef 00d4649a 002864ee 002864ee (calculated) Have fun :) Simon
#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (c) 2009, Simon A. Eugster <[email protected]> # License: GNU General Public License, see http://www.gnu.org/licenses/gpl.html # hex to bin: 'ab'.decode('hex') -> '\xab' # bin to hex: '%02x' % ord('\xab') # hex to dec: int('ab', 16) -> 171 # dec to hex: '%x' % (171) from __future__ import with_statement import os # Path manipulation import mmap # Binary Editing def s(c) : return str(ord(c)).zfill(2) def hex2dec(s) : return int(s, 16) def bin2dec(b) : return int(b.encode('hex'), 16) def bin2hex(b) : return b.encode('hex') class GarminImg : def __init__(self, filename) : self.filename = os.path.abspath(filename) offsetMapID = 0x74 offsetMapValues = 0x9a def binWord(self, id) : return ('%08x' % (int(id))).decode('hex')[::-1] def updateID(self, toID) : with open(self.filename, 'r+b') as f : map = mmap.mmap(f.fileno(), 0) # 0: Read whole file pos = map.find('GARMIN TRE') & 0xffffffffffffff00 # 14*f,2*0 l = pos+GarminImg.offsetMapID id = map[l : l+4][::-1] # Need to reverse. print('Old map ID at %x: 0x%08x = %s' % (pos, hex2dec(id.encode('hex')), hex2dec(id.encode('hex')))) map[l : l+4] = self.binWord(toID) headerLength = bin2dec(map[pos]) print('Header length: %d' % (headerLength)) l = pos+GarminImg.offsetMapValues values = [map[l+4*i : l+4*(i+1)][::-1] for i in range(4)] print('Values: %s %s %s %s (original)' % (bin2hex(values[0]), bin2hex(values[1]), bin2hex(values[2]), bin2hex(values[3]))) mv = MapValues(toID, headerLength) mv.calculate() print('Values: %08x %08x %08x %08x (calculated)' % (mv.value(0), mv.value(1), mv.value(2), mv.value(3))) for i in range(4) : map[l+4*i : l+4*(i+1)] = self.binWord(mv.value(i)) def rename(self, toID) : try : int(toID) self.ID = str(toID) if len(self.ID) < 8 : self.ID = self.ID.zfill(8) if len(self.ID) == 8 : pos = 0x600 # Starting position. renamed = [] count = 0 oldid = '' with open(self.filename, 'r+b') as f : map = mmap.mmap(f.fileno(), 0) # 0: Read whole file oldid = map[pos+1:pos+9] while not (map[pos] == '\x00') : # Header ends if 00 is read there. renamed.append(pos) count += 1 # Change the ID map[pos+1:pos+9] = self.ID # Position of next entry: Offset of 0x200 pos += 0x200 #all = '' #for r in renamed : # all += hex(r) + ' ' #print('Renamed positions: %s\nTotal: %s' % (all, count)) self.updateID(toID) return (count, renamed, oldid) else : print('Wrong length (%s) of ID %s.' % (len(self.ID), toID)) except ValueError : print('Wrong ID: %s (needs to be 8 digits)' % toID) return None class MapValues : # This is the Python copy of this file: # http://svn.parabola.me.uk/mkgmap/trunk/src/uk/me/parabola/imgfmt/app/trergn/MapValues.java # which has been written by Steve Ratcliffe (including comments). def __init__(self, mapId, headerLength) : self.mapId = mapId self.length = headerLength self.values = [range(8), range(8), range(8), range(8)] # Converts the digits in the map id to the values seen in this section. mapIdCodeTable = [ 0x0, 0x1, 0xf, 0x5, 0xd, 0x4, 0x7, 0x6, 0xb, 0x9, 0xe, 0x8, 0x2, 0xa, 0xc, 0x3 ] # Used to work out the required offset that is applied to all the # digits of the values. offsetMap = [ 6, 7, 5, 11, 3, 10, 13, 12, 1, 15, 4, 14, 8, 0, 2, 9 ] def value(self, n) : """There are four values. Get value n. @param n Get value n, starting at 0 up to four.""" out = self.values[n] res = 0 for i in range(8) : res |= ((out[i] & 0xf) << (4 * (7 - i))) return res def calculate(self) : # Done in this order because the first and second depend on things # we have already calculated in three. self.calcThird() self.calcFourth() self.calcFirst() self.calcSecond() self.addOffset() def addOffset(self) : """Add an offset to all previously calculated values.""" # To get the offset value we add up all the even nibbles of the map # number and transform via a table. n = self.mapIdDigit(1) + self.mapIdDigit(3) + self.mapIdDigit(5) + self.mapIdDigit(7) offset = MapValues.offsetMap[n & 0xf] for i in range(4) : for j in range(8) : self.values[i][j] += offset def calcFirst(self) : """This value is made from the third value, combined with the raw map id values.""" out = self.values[0] v3 = self.values[3] # First bytes are the low bytes of the mapId, with the corresponding # value from value[3] added. out[0] = self.mapIdDigit(4) + v3[0] out[1] = self.mapIdDigit(5) + v3[1] out[2] = self.mapIdDigit(6) + v3[2] out[3] = self.mapIdDigit(7) + v3[3] # Copies of v3 out[4] = v3[4] out[5] = v3[5] out[6] = v3[6] #Always (?) one more. The one likely comes from some other # part of the header, but we don't know if or where. out[7] = v3[7] + 1 def calcSecond(self) : """This is made from various parts of the third value and the raw digits from the map id. There are two digits where the header length digits are used (or that could be a coincidence, but it holds up well so far).""" out = self.values[1] v3 = self.values[3] # Just same as in v3 out[0] = v3[0] out[1] = v3[1] h1 = self.length >> 4 h2 = self.length out[2] = (v3[2] + h1) & 0xf out[3] = (v3[3] + h2) & 0xf # The following are the sum of individual nibbles in U3 and the # corresponding nibble in the top half of mapId. out[4] = v3[4] + self.mapIdDigit(0) out[5] = v3[5] + self.mapIdDigit(1) out[6] = v3[6] + self.mapIdDigit(2) out[7] = v3[7] + self.mapIdDigit(3) def calcThird(self) : """This is made of the hex digits of the map id in a given order translated according to a given table of values.""" out = self.values[2] for i in range(8) : n = self.mapIdDigit(i) out[(i ^ 1)] = MapValues.mapIdCodeTable[n] def calcFourth(self) : """This is just a copy of the third value.""" out = self.values[3] v2 = self.values[2] for i in range(8) : out[i] = v2[i] def mapIdDigit(self, i) : """Extract the given nibble of the map id. 0 is the highest four bits. @param i The nibble number, 0 most significant, 7 the least. @return The given nibble of the map id.""" return int(('%08x' % (int(self.mapId)))[i], 16)
import sys
from libGarminImg import GarminImg
if len(sys.argv) < 3 :
print('Usage: python %s mapfile mapid' % (sys.argv[0]))
sys.exit()
img = GarminImg(sys.argv[1])
img.rename(sys.argv[2])
_______________________________________________ mkgmap-dev mailing list [email protected] http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev
