/*
* Copyright (C) 2009.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
package test.display;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Standalone program to display an MDR file. There is no freely available
* knowledge of the file format.
*
* Can produce a massive file, so may take some time to write.
*
* @author Steve Ratcliffe
*/
@
SuppressWarnings({"UnusedDeclaration"})
public class MdrDisplay
extends CommonDisplay
{
private static final int NSECT =
41;
// Sections to be displayed
private final boolean[] wanted =
new boolean[NSECT
];
private int numberOfMaps
; // The number of maps
private boolean compressedStrings
;
private int cityPtrSize
;
private Charset charSet
;
private Map<Integer,
String> stringTable
;
private int start
;
private int record
;
protected void print
() {
readCommonHeader
();
printHeader
();
Section end =
new Section
("END OF FILE", filelen,
0);
addSection
(end
);
analyze
(outStream
);
/*
* Uses reflection to find a method to print a section. If
* none is found then a generic method is used to print the
* section
*/
for (int i =
1; i
< numberOfSections
(); i++
) {
if (!want
(i
))
continue;
try {
Class<MdrDisplay
> c = MdrDisplay.
class;
Method method = c.
getDeclaredMethod("printSect" + i
);
method.
invoke(this);
} catch (NoSuchMethodException e
) {
printSectUnknown
(i
);
} catch (InvocationTargetException e
) {
Throwable targetException = e.
getTargetException();
System.
out.
printf("Failed in section %d: %s\n", i, targetException
);
} catch (IllegalAccessException e
) {
e.
printStackTrace();
}
}
}
/**
* Prints an unknown section. If there is a record size then each
* record is printed separately.
*/
private void printSectUnknown
(int n
) {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR " + n +
" (unknown)");
Section s = getSection
(n
);
d.
setSectStart(s.
getStart());
reader.
position(s.
getStart());
if (s.
getRecordSize() ==
0)
d.
rawValue(s.
getLen());
else {
int recno =
0;
while (reader.
position() < s.
getEnd()) {
d.
rawValue(s.
getRecordSize(),
"record " + recno++
);
recordPrint
(d
);
}
}
d.
print(outStream
);
}
public void setStart
(int start
) {
this.
start = start
;
}
class MapInfo
{
int mapIndex
;
int mapid
;
int offset
;
int sublen
;
}
private void printSect1
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 1 (maps)");
Section s = getSection
(1);
d.
setSectStart(s.
getStart());
int recsize = s.
getRecordSize();
reader.
position(s.
getStart());
List<MapInfo
> maps =
new ArrayList<MapInfo
>();
int index =
1;
while (reader.
position() < s.
getEnd()) {
d.
item().
addText("Map %d", index
);
MapInfo mi =
new MapInfo
();
mi.
mapIndex = index++
;
mi.
mapid = d.
intValue("mapname %d");
if (recsize
> 4)
mi.
offset = d.
intValue("offset to subsection %x");
maps.
add(mi
);
}
MapInfo mi =
new MapInfo
();
mi.
offset =
(int) s.
getEnd();
maps.
add(mi
);
d.
print(outStream
);
printSect1Mapsect
(maps
);
}
/**
* Contains a set of pointers and sizes elsewhere in the file.
* The lengths are I guess a number of items as they
* need to be multiplied by a number to get the true length as
* calculated by looking at the start offsets.
*/
private void printSect1Mapsect
(List<MapInfo
> maps
) {
for (int n =
0; n
< maps.
size()-
1; n++
) {
MapInfo mi = maps.
get(n
);
int suboff = mi.
offset;
if (suboff ==
0)
continue;
mi.
sublen = maps.
get(n +
1).
offset - suboff
;
Mdr1SubFileDisplay subFileDisplay =
new Mdr1SubFileDisplay
(reader, outStream, mi
);
subFileDisplay.
print();
}
}
/**
* A list of POI types.
*/
private void printSect4
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 4 (types)");
Section s = getSection
(4);
int recsize = s.
getRecordSize();
d.
setSectStart(s.
getStart());
reader.
position(s.
getStart());
while (reader.
position() < s.
getEnd()) {
int record = reader.
get3();
DisplayItem item = d.
item();
item.
setBytes3(record
);
item.
addText("Type 0x%04x. Unknown 0x%x",
((record
>> 16) & 0xff
) +
((record
& 0xff
) << 8),
(record
>> 8) & 0xff
);
}
d.
print(outStream
);
}
/**
* A list of cities. It is alphabetically arranged.
*/
private void printSect5
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 5 (cities)");
Section s = getSection
(5);
int recsize = s.
getRecordSize();
d.
setSectStart(s.
getStart());
int cityPtrSize =
(s.
getMagic() & 0x3
) +
1;
boolean hasRegion =
(s.
getMagic() & 0x4
) !=
0;
boolean hasStr =
(s.
getMagic() & 0x8
) !=
0;
boolean has20 =
(s.
getMagic() & 0x100
) !=
0;
boolean has20offset =
(s.
getMagic() & 0x800
) !=
0;
int mdr20PointerSize =
0;
if (has20
)
mdr20PointerSize = getSection
(20).
getBytesForRecords();
if (has20offset
)
mdr20PointerSize = getSection
(20).
getBytesForSize();
boolean has28_29_offset =
(s.
getMagic() & 0x400
) !=
0;
int mdr28_29PointerSize =
0;
if (has28_29_offset
)
mdr28_29PointerSize = getSection
(28).
getBytesForRecords();
if (has28_29_offset
&& mdr20PointerSize ==
0)
mdr28_29PointerSize = getSection
(29).
getBytesForRecords();
//boolean has44 = (s.getMagic() & 0x2000) != 0;
//int mdr44PointerSize = getSection(44).getBytesForRecords();
reader.
position(s.
getStart());
int citynum =
0;
while (reader.
position() < s.
getEnd()) {
int start =
(int) reader.
position();
d.
gap();
d.
item().
addText("City index %d", ++citynum
);
printMapIndex
(d
);
putValue
(d,
"the %d city in the map", cityPtrSize
);
DisplayItem item = d.
int3Item();
item.
addText("offset in LBL 0x%06x", item.
getValue() & ~0x800000
);
if (hasRegion
) {
item = d.
charItem();
item.
addText("region %d", item.
getValue() & 0x3fff
);
}
if (hasStr
)
printTextLabel
(d
);
if (has20 || has20offset
)
d.
intValue(mdr20PointerSize,
"mdr20 %d");
if (has28_29_offset
)
d.
intValue(mdr28_29PointerSize,
"mdr28/29 %d");
int remain =
(int) (start + s.
getRecordSize() - reader.
position());
if (remain
> 0)
d.
rawValue(remain
);
recordPrint
(d
);
}
d.
print(outStream
);
}
private void recordPrint
(Displayer d
) {
d.
print((++record
< start
)? null: outStream
);
d.
setTitle(null);
}
protected void putValue
(Displayer d,
String text,
int n
) {
switch (n
) {
case 1: d.
byteValue(text
); break;
case 2: d.
charValue(text
); break;
case 3: d.
int3Value(text
); break;
case 4: d.
intValue(text
); break;
}
}
/**
* A list of zips. It is alphabetically arranged.
*/
private void printSect6
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 6 (ZIPs)");
Section s = getSection
(6);
int recsize = s.
getRecordSize();
d.
setSectStart(s.
getStart());
int zipPtrSize =
(s.
getMagic() & 0x3
) +
1;
d.
item().
addText("Zip size: "+zipPtrSize
);
boolean hasString =
(s.
getMagic() & 0x4
) !=
0;
reader.
position(s.
getStart());
int zipnum =
0;
while (reader.
position() < s.
getEnd()) {
d.
item().
addText("zip index %d", ++zipnum
);
printMapIndex
(d
);
putValue
(d,
"the %d zip in the map", zipPtrSize
);
if (hasString
)
printTextLabel
(d
);
}
d.
print(outStream
);
}
/**
* Contains an ordered list of pointers to street names.
*/
private void printSect7
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 7 (street names)");
Section s = getSection
(7);
int magic = s.
getMagic();
boolean hasStr =
(magic
& 0x01
) !=
0;
boolean hasUnk1 =
(magic
& 0x20
) !=
0;
//boolean hasUnk2 = (magic & 0x40) != 0;
//int unk2size = (magic >> 7) & 0x3;
// cpreview has something equivalent to:
int unk2size =
(magic
>> 6) & 0x3
;
d.
setSectStart(s.
getStart());
int recsize = s.
getRecordSize();
reader.
position(s.
getStart());
int count =
1;
while (reader.
position() < s.
getEnd()) {
DisplayItem item = d.
item();
item.
addText("Record %d", count++
);
long itemStart = reader.
position();
printMapIndex
(d
);
item = d.
int3Item();
int off = item.
getValue();
item.
addText("Pointer back into LBL: %06x", off
& ~0x800000
);
if ((off
& 0x800000
) ==
0)
item.
addText("Repeated name");
if (hasStr
)
printTextLabel
(d
);
if (hasUnk1
)
d.
byteValue("name offset %d");
d.
intValue(unk2size,
"unk2 %d");
d.
rawValue((int) (itemStart + recsize - reader.
position()));
d.
gap();
recordPrint
(d
);
}
d.
print(outStream
);
}
private void printSect8
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 8 (index into streets)");
print8_12
(d,
8);
}
private void printSect9
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 9 (Index into mdr10, grouped based on type)");
Section s = getSection
(9);
d.
setSectStart(s.
getStart());
int recsize = s.
getRecordSize();
reader.
position(s.
getStart());
while (reader.
position() < s.
getEnd()) {
d.
byteValue("group %d");
DisplayItem item = d.
item();
int c
;
if (recsize ==
3)
c = item.
setBytes(reader.
getChar());
else
c = item.
setBytes3(reader.
get3());
item.
addText("record in MDR10 %d", c
);
}
d.
print(outStream
);
}
/**
* POI types. Also has a pointer into MDR 11. Seems like a waste.
*/
private void printSect10
() {
// Looks variable length
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 10 (POI types)");
Section s = getSection
(10);
d.
setSectStart(s.
getStart());
reader.
position(s.
getStart());
int count =
1;
while (reader.
position() < s.
getEnd()) {
d.
byteValue("type 0x%x");
DisplayItem item = d.
item();
// Calculate record size by determining how many points there are in MDR11
int nPoints = getSection
(11).
getNumberOfRecords();
int c
;
boolean isRepeat =
true;
if (nPoints
< 0x80
) {
c = item.
setBytes(reader.
get()) & 0xff
;
if ((c
& 0x80
) !=
0) {
isRepeat =
false;
c
&= ~0x80
;
}
} else if (nPoints
< 0x8000
) {
c = item.
setBytes(reader.
getChar()) & 0xffff
;
if ((c
& 0x8000
) !=
0) {
isRepeat =
false;
c
&= ~0x8000
;
}
} else {
c = item.
setBytes3(reader.
get3());
if ((c
& 0x800000
) !=
0) {
isRepeat =
false;
c
&= ~0x800000
;
}
}
item.
addText("[%d] rec in MDR11 %d %s", count++, c, isRepeat
? "(repeated name)":
"");
}
d.
print(outStream
);
}
/**
* Show a city ref
*/
private void printSect11_City
(Displayer d
) {
// if this is a city there is a reference to it.
DisplayItem item = d.
item();
int mask
;
int c
;
switch (cityPtrSize
) {
case 3:
c = item.
setBytes3(reader.
get3());
mask = 0x800000
;
break;
default:
// There is a min size of 2
c = item.
setBytes(reader.
getChar());
mask = 0x8000
;
break;
}
if (c
> 0) {
if ((c
& mask
) ==
0)
item.
addText("Region %d (in map)", c
);
else
item.
addText("City %d (MDR5)", c
& ~mask
);
}
}
/**
* Handle section 11, mainlength 8
*/
private void printSect11_8
(Displayer d
) {
d.
rawValue(6);
printSect11_City
(d
);
}
/**
* These records contain the map the subdivision and the point (or line?) number
* and the offset in LBL. If it has an associated city (or is a city itself), there is
* a reference back into MDR 5.
*/
private void printSect11
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 11 (POIs)");
Section s = getSection
(11);
d.
setSectStart(s.
getStart());
int recsize = s.
getRecordSize() - mapIndexSize
();
d.
item().
addText("section flags %x", s.
getMagic());
reader.
position(s.
getStart());
int count =
1;
while (reader.
position() < s.
getEnd()) {
int startPos =
(int) reader.
position();
// records in sect 1 appear to point here
d.
gap();
d.
item().
addText("record %d", count++
);
// The first values are always present.
printMapIndex
(d
);
d.
byteValue("Point number %d");
d.
charValue("In subdiv %d");
d.
int3Value("LBL offset %06x");
if (recsize
>=
8)
printSect11_City
(d
);
if (recsize
> 10)
printTextLabel
(d
);
int remain =
(int) (s.
getRecordSize() -
(reader.
position() - startPos
));
if (remain
> 0)
d.
rawValue(remain
);
recordPrint
(d
);
}
d.
print(outStream
);
}
private void printSect12
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 12 (index into POI)");
print8_12
(d,
12);
}
private void print8_12
(Displayer d,
int sectNumber
) {
// Get the number of records in the previous section
Section s = getSection
(sectNumber-
1);
int sizePrev = s.
getBytesForRecords();
s = getSection
(sectNumber
);
d.
setSectStart(s.
getStart());
int recsize = s.
getRecordSize();
int flags = s.
getMagic();
reader.
position(s.
getStart());
while (reader.
position() < s.
getEnd()) {
long start = reader.
position();
d.
stringValue((flags
>>8),
"Prefix:%s");
d.
intValue(sizePrev,
"record %d");
d.
rawValue((int) (recsize -
(reader.
position() - start
)));
}
d.
print(outStream
);
}
/**
* This is an ordered list containing pointers into the text strings,
* probably for regions, arranged per map.
*/
private void printSect13
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 13 (regions)");
Section s = getSection
(13);
d.
setSectStart(s.
getStart());
reader.
position(s.
getStart());
int record =
0;
while (reader.
position() < s.
getEnd()) {
int start =
(int) reader.
position();
d.
item().
addText("Region %d", ++record
);
printMapIndex
(d
);
d.
charValue("number in map %d");
d.
charValue("country %x");
printTextLabel
(d
);
int remain =
(int) (start+s.
getRecordSize() - reader.
position());
if (remain
> 0)
d.
rawValue(remain
);
d.
gap();
}
d.
print(outStream
);
}
/**
* Countries for each map.
*/
private void printSect14
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 14 (country)");
Section s = getSection
(14);
d.
setSectStart(s.
getStart());
reader.
position(s.
getStart());
while (reader.
position() < s.
getEnd()) {
int start =
(int) reader.
position();
printMapIndex
(d
);
d.
charValue("Number in map %d");
// probably country...
printTextLabel
(d
);
int remain =
(int) ((start + s.
getRecordSize()) - reader.
position());
if (remain
> 0)
d.
rawValue(remain
);
}
d.
print(outStream
);
}
/**
* This is the actual text strings that are being indexed.
* There are two formats possible, this is the simple straightforward
* one. There is an alternate one that is compressed in some way.
*/
private void printSect15
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 15 (strings)");
Section s = getSection
(15);
long start = s.
getStart();
long end = s.
getEnd();
d.
setSectStart(start
);
reader.
position(start
);
if (compressedStrings
) {
// don't currently know this
d.
rawValue((int) (end - start
));
} else {
// If we have already read the strings then don't bother to do it all again.
// If you want to see the string table then just view it separately.
if (stringTable
!=
null) {
d.
item().
addText("Strings omitted, call with just 15 on the command line to see");
} else {
int count =
0;
while (reader.
position() < end
) {
d.
zstringValue("text: %s");
count++
;
}
d.
item().
addText("%d records", count
);
}
}
d.
print(outStream
);
}
/**
* MDR 17: String table
*
* My personal guess is, that this is a search table for
* sub strings starting with the given short string
*/
private void printSect17
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 17 (indexes based on initial sub strings)");
Section s = getSection
(17);
d.
setSectStart(s.
getStart());
reader.
position(s.
getStart());
while (reader.
position() < s.
getEnd()) {
int peek = 0xff
& (int) d.
byteValue("Peek");
DisplayItem item = d.
item();
int partlength =
0;
if ((peek
& 1) ==
1)
{
partlength = peek
;
partlength
>>=
1;
}
if ((peek
& 3) ==
2)
{
partlength = 0xff
& (int) reader.
get();
item.
setBytes((byte) partlength
);
partlength
<<=
8;
partlength += peek
;
partlength
>>=
2;
}
if ((peek
& 7) ==
4)
{
partlength = 0xffff
& (int)reader.
getChar();
item.
setBytes((char) partlength
);
partlength
<<=
8;
partlength += peek
;
partlength
>>=
3;
}
item.
addText("Length of following part: %d", partlength
);
peek = -
1;
if (partlength
> 2) {
item = d.
charItem();
peek = item.
getValue();
item.
addText("Some header: 0x%04x", peek
);
partlength -=
2;
}
// This can't be the whole story. Header flags from other sections help determine?
int strLen =
(peek
>> 8) +
1;
int indLen = peek
& 0xff
;
if (strLen ==
4)
indLen -= 0x10
;
indLen =
(indLen-
9)/
10 +
1;
item.
addText("str len %d, index len %d", strLen, indLen
);
partlength /= strLen + indLen
;
int record =
0;
for (int i =
0; i
< partlength
; i++
) {
record++
;
d.
stringValue(strLen,
String.
format("[%d] str: %%s", record
));
d.
intValue(indLen,
"Index? %d");
}
}
d.
print(outStream
);
}
/**
* MDR 18: Looks like a mapping to MDR 11 or MDR 19
*/
private void printSect18
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 18 (poi type index into 19)");
Section s = getSection
(18);
d.
setSectStart(s.
getStart());
int size19 = getSection
(19).
getBytesForRecords();
reader.
position(s.
getStart());
while (reader.
position() < s.
getEnd()) {
DisplayItem item = d.
charItem();
int type = item.
getValue();
item.
addText("Type: %02x/%02x",
(type
>>5) & 0xff, type
& 0x1f
);
d.
intValue(size19,
"Idx into MDR 19: %d");
}
d.
print(outStream
);
}
private void printSect19
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 19 (poi sorted by type)");
Section s = getSection
(19);
d.
setSectStart(s.
getStart());
int poiSize = getSection
(11).
getBytesForRecords();
reader.
position(s.
getStart());
int count =
1;
while (reader.
position() < s.
getEnd()) {
DisplayItem item = d.
intItem(poiSize
);
int val = item.
getValue();
item.
addText("record %d: poi index %d", count++, val
& 0x7fff
);
}
d.
print(outStream
);
}
private void printSect20
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 20 (streets by city)");
Section s = getSection
(20);
d.
setSectStart(s.
getStart());
int recsize = s.
getRecordSize();
reader.
position(s.
getStart());
int count =
1;
int record =
1;
int lastid = -
1;
long start
;
while ((start = reader.
position()) < s.
getEnd()) {
d.
item().
addText("Record %d", record++
);
if (recsize ==
8) {
d.
byteValue("u1 %d");
DisplayItem item = d.
int3Item();
int val = item.
getValue();
item.
addText("u2 %d%s", val
&0x7fffff,
(val
&0x800000
) !=
0? " F":
"");
d.
intValue("u3 %d");
} else if (recsize ==
5) {
d.
byteValue("map %d");
DisplayItem item = d.
int3Item();
int val = item.
getValue();
item.
addText("lbl 0x%06x%s", val
&0x7fffff,
(val
&0x800000
) !=
0? " F":
"");
d.
byteValue("flag %d");
} else {
DisplayItem item = d.
intItem(recsize
);
int v = item.
getValue();
// Print a message whenever the id is less than the previous one. Not completely
// foolproof, but mostly shows where the boundaries are. By counting the groups
// you can guess what is pointing inward and work from there.
int id = v
>> 1;
if (id
< lastid
)
item.
addText("# New group %d", count++
);
lastid = id
;
item.
addText("street %d", id
);
item.
addText("flag %b",
(v
& 1) ==
1);
}
printRemainder
(d, recsize, start
);
}
d.
print(outStream
);
}
private void printSect21
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 21 (streets by region)");
Section s = getSection
(21);
d.
setSectStart(s.
getStart());
int recsize = s.
getRecordSize();
reader.
position(s.
getStart());
int count =
1;
int record =
1;
int lastid = -
1;
long start
;
while ((start = reader.
position()) < s.
getEnd()) {
d.
item().
addText("Record %d", record++
);
DisplayItem item = d.
intItem(recsize
);
int v = item.
getValue();
int id = v
>> 1;
if (id
< lastid
)
item.
addText("# New group %d", count++
);
lastid = id
;
item.
addText("street %d", id
);
item.
addText("flag %b",
(v
& 1) ==
1);
printRemainder
(d, recsize, start
);
}
d.
print(outStream
);
}
private void printSect22
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 22 (streets by country)");
Section s = getSection
(22);
d.
setSectStart(s.
getStart());
int recsize = s.
getRecordSize();
reader.
position(s.
getStart());
int count =
1;
int record =
1;
int lastid = -
1;
long start
;
while ((start = reader.
position()) < s.
getEnd()) {
d.
item().
addText("Record %d", record++
);
DisplayItem item = d.
intItem(recsize
);
int v = item.
getValue();
int id = v
>> 1;
if (id
< lastid
)
item.
addText("# New group %d", count++
);
lastid = id
;
item.
addText("street %d", id
);
item.
addText("flag %b",
(v
& 1) ==
1);
printRemainder
(d, recsize, start
);
}
d.
print(outStream
);
}
private void printSect23
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 23 (sorted regions)");
Section s = getSection
(23);
d.
setSectStart(s.
getStart());
int recsize = s.
getRecordSize();
reader.
position(s.
getStart());
int count =
1;
int record =
1;
long start
;
while ((start = reader.
position()) < s.
getEnd()) {
d.
item().
addText("Record %d", record++
);
printMapIndex
(d
);
d.
charValue("region in tile %d");
d.
charValue("country in tile %d");
DisplayItem item = d.
int3Item();
int value = item.
getValue();
item.
addText("lbl offset %x", value
& 0x7fffff
);
printRemainder
(d, recsize, start
);
}
d.
print(outStream
);
}
private void printSect24
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 24 (sorted countries)");
Section s = getSection
(24);
d.
setSectStart(s.
getStart());
int recsize = s.
getRecordSize();
reader.
position(s.
getStart());
int count =
1;
int record =
1;
long start
;
while ((start = reader.
position()) < s.
getEnd()) {
d.
item().
addText("Record %d", record++
);
printMapIndex
(d
);
d.
charValue("country in tile %d");
DisplayItem item = d.
int3Item();
int value = item.
getValue();
item.
addText("lbl offset %x", value
& 0x7fffff
);
printRemainder
(d, recsize, start
);
}
d.
print(outStream
);
}
private void printSect25
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 25 (cities by country)");
Section s = getSection
(25);
d.
setSectStart(s.
getStart());
int recsize = s.
getRecordSize();
reader.
position(s.
getStart());
int count =
1;
int record =
1;
int lastid = -
1;
long start
;
while ((start = reader.
position()) < s.
getEnd()) {
d.
item().
addText("Record %d", record++
);
DisplayItem item = d.
intItem(recsize
);
int v = item.
getValue();
if (v
< lastid
)
item.
addText("# New group %d", count++
);
lastid = v
;
item.
addText("city %d", v
);
printRemainder
(d, recsize, start
);
}
d.
print(outStream
);
}
private void printSect26
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 26 (mdr28 ordered by country)");
Section s = getSection
(26);
d.
setSectStart(s.
getStart());
int recsize = s.
getRecordSize();
reader.
position(s.
getStart());
int count =
1;
int record =
1;
int lastid =
Integer.
MAX_VALUE;
long start
;
int[] max =
new int[1];
while ((start = reader.
position()) < s.
getEnd()) {
d.
item().
addText("Record %d", record++
);
DisplayItem item = d.
charItem();
int m = item.
getValue();
if (m
< lastid
)
d.
item().
addText("New group %d", count++
);
lastid = m
;
item.
addText("m28 record %d", m
);
if (m
> max
[0]) max
[0] = m
;
printRemainder
(d, recsize, start
);
}
for (int i : max
)
d.
item().
addText("max %d", i
);
d.
print(outStream
);
}
private void printSect27
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 27 (cities by region?)");
Section s = getSection
(27);
d.
setSectStart(s.
getStart());
int recsize = s.
getRecordSize();
reader.
position(s.
getStart());
int count =
1;
int record =
1;
int lastid = -
1;
long start
;
while ((start = reader.
position()) < s.
getEnd()) {
d.
item().
addText("Record %d", record++
);
DisplayItem item = d.
intItem(recsize
);
int v = item.
getValue();
if (v
< lastid
)
item.
addText("# New group %d", count++
);
lastid = v
;
item.
addText("city %d", v
);
printRemainder
(d, recsize, start
);
}
d.
print(outStream
);
}
private void printSect28
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 28 (region names with pointers)");
Section s = getSection
(28);
d.
setSectStart(s.
getStart());
int recsize = s.
getRecordSize();
int size21 = getSection
(21).
getBytesForRecords();
int size23 = getSection
(23).
getBytesForRecords();
int size27 = getSection
(27).
getBytesForRecords();
reader.
position(s.
getStart());
int count =
1;
int record =
1;
int lastid = -
1;
long start
;
int[] max =
new int[4];
while ((start = reader.
position()) < s.
getEnd()) {
d.
item().
addText("Record %d", record++
);
// These will not work on all maps, until the sizes are correct
int m = d.
intValue(size23,
"mdr23 record %d");
if (m
> max
[0]) max
[0] = m
;
printTextLabel
(d
);
m = d.
intValue(size21,
"mdr21 record %d");
if (m
> max
[2]) max
[2] = m
;
m = d.
intValue(size27,
"mdr27 record %d");
if (m
> max
[3]) max
[3] = m
;
printRemainder
(d, recsize, start
);
}
for (int m : max
)
d.
item().
addText("max %d", m
);
d.
print(outStream
);
}
private void printSect29
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR 29 (country names with pointers)");
Section s = getSection
(29);
int magic = s.
getMagic();
d.
setSectStart(s.
getStart());
int recsize = s.
getRecordSize();
boolean hasStr =
(magic
& 0x1
) !=
0; // guessed
boolean has17 =
(magic
& 0x30
) !=
0;
boolean has26 =
(magic
& 0x8
) !=
0;
int size24 = getSection
(24).
getBytesForRecords();
int size22 = getSection
(22).
getBytesForRecords();
int size25 = getSection
(25).
getBytesForRecords();
int size26 =
(has26
)? getSection
(26).
getBytesForRecords() :
0;
int size17 =
(has17
)? (magic
& 0x30
) >> 4 :
0;
reader.
position(s.
getStart());
int count =
1;
int record =
1;
int lastid = -
1;
long start
;
int[] max =
new int[5];
while ((start = reader.
position()) < s.
getEnd()) {
d.
item().
addText("Record %d", record++
);
int m = d.
intValue(size24,
"mdr24 record %d");
if (m
> max
[0]) max
[0] = m
;
if (hasStr
)
printTextLabel
(d
);
m = d.
intValue(size22,
"mdr22 record %d");
if (m
> max
[2]) max
[2] = m
;
m = d.
intValue(size25,
"mdr25 record %d");
if (m
> max
[3]) max
[3] = m
;
if (has26
) {
m = d.
intValue(size26,
"mdr26 record %d");
if (m
> max
[4]) max
[4] = m
;
}
if (has17
) {
m = d.
intValue(size17,
"mdr17 record %d");
if (m
> max
[4]) max
[4] = m
;
}
printRemainder
(d, recsize, start
);
}
for (int m : max
)
d.
item().
addText("max %d", m
);
d.
print(outStream
);
}
private void printRemainder
(Displayer d,
int recsize,
long start
) {
int rem =
(int) ((start + recsize
) - reader.
position());
if (rem
<=
4)
d.
intValue(rem,
"unk %d");
else
d.
rawValue(rem
);
recordPrint
(d
);
}
/**
* Get a string from the string section.
* @param d The displayer to use.
*/
private void printTextLabel
(Displayer d
) {
DisplayItem item = d.
item();
int off
;
if (getSection
(15).
getLen() > 0xffffff
)
off = item.
setBytes(reader.
getInt());
else
off = item.
setBytes3(reader.
get3());
String str = getTextString
(off
);
item.
addText("Pointer to string: %s", str
);
}
/**
* Get a string from the string section.
* @param off The offset in the text section.
* @return The resultant string.
*/
private String getTextString
(int off
) {
if (compressedStrings
)
return "";
if (stringTable ==
null)
readAllStrings
();
return stringTable.
get(off
);
}
private void readAllStrings
() {
if (compressedStrings
)
return; // we don't know how to do this yet.
stringTable =
new LinkedHashMap<Integer,
String>();
// Remember where we are
long rem = reader.
position();
Section textsect = getSection
(15);
long start = textsect.
getStart();
long end = textsect.
getEnd();
reader.
position(start
);
byte[] buffer =
new byte[1024];
int len =
0;
int offset =
0;
while (reader.
position() < end
) {
byte b = reader.
get();
if (b ==
0) {
String str =
new String(buffer,
0, len, charSet
);
stringTable.
put(offset, str
);
offset =
(int) (reader.
position() - start
);
len =
0;
} else {
buffer
[len++
] = b
;
}
}
// Return to our callers position
reader.
position(rem
);
}
private void printHeader
() {
reader.
position();
Displayer d =
new Displayer
(reader
);
d.
setTitle("MDR header");
int codePage = d.
charValue("codepage %d");
String s
;
if (codePage ==
0)
s =
"ascii";
else if (codePage ==
65001)
s =
"utf-8";
else
s =
"cp" + codePage
;
charSet =
Charset.
forName(s
);
d.
charValue("??? %d");
d.
charValue("??? %d");
d.
charValue("??? %d");
Section sect = readSection
(d,
"MDR 1",
1,
true,
true);
numberOfMaps = sect.
getNumberOfRecords();
boolean onDevice = sect.
getRecordSize() ==
4;
d.
item().
addText("Number of maps %d", numberOfMaps
);
d.
item().
addText("Device MDR %s", onDevice
);
readSection
(d,
"MDR 2",
2,
true,
true);
readSection
(d,
"MDR 3",
3,
true,
true);
readSection
(d,
"MDR 4",
4,
true,
true);
readSection
(d,
"MDR 5",
5,
true,
true);
readSection
(d,
"MDR 6",
6,
true,
true);
readSection
(d,
"MDR 7",
7,
true,
true);
readSection
(d,
"MDR 8",
8,
true,
true);
readSection
(d,
"MDR 9",
9,
true,
true);
readSection
(d,
"MDR 10",
10,
false,
true);
readSection
(d,
"MDR 11",
11,
true,
true);
readSection
(d,
"MDR 12",
12,
true,
true);
readSection
(d,
"MDR 13",
13,
true,
true);
readSection
(d,
"MDR 14",
14,
true,
true);
readSection
(d,
"MDR 15",
15,
false,
false);
int flag = d.
byteValue("String flag %x");
if (flag ==
1)
compressedStrings =
true;
readSection
(d,
"MDR 16",
16,
true,
true);
readSection
(d,
"MDR 17",
17,
false,
true);
readSection
(d,
"MDR 18",
18,
true,
true);
readSection
(d,
"MDR 19",
19,
true,
true);
if (getHeaderLen
() > 286) {
readSection
(d,
"MDR 20",
20,
true,
true);
readSection
(d,
"MDR 21",
21,
true,
true);
readSection
(d,
"MDR 22",
22,
true,
true);
readSection
(d,
"MDR 23",
23,
true,
true);
readSection
(d,
"MDR 24",
24,
true,
true);
readSection
(d,
"MDR 25",
25,
true,
true);
readSection
(d,
"MDR 26",
26,
true,
true);
readSection
(d,
"MDR 27",
27,
true,
true);
readSection
(d,
"MDR 28",
28,
true,
true);
readSection
(d,
"MDR 29",
29,
true,
true);
readSection
(d,
"MDR 30",
30,
true,
true);
readSection
(d,
"MDR 31",
31,
false,
false);
readSection
(d,
"MDR 32",
32,
true,
true);
readSection
(d,
"MDR 33",
33,
false,
false);
readSection
(d,
"MDR 34",
34,
true,
true);
readSection
(d,
"MDR 35",
35,
true,
true);
readSection
(d,
"MDR 36",
36,
true,
true);
readSection
(d,
"MDR 37",
37,
true,
true);
readSection
(d,
"MDR 38",
38,
true,
true);
readSection
(d,
"MDR 39",
39,
true,
true);
readSection
(d,
"MDR 40",
40,
true,
true);
}
if (getHeaderLen
() >=
620) {
d.
intValue("??? Offset: %08x");
d.
intValue("??? 0x%x");
d.
intValue("??? 0x%x");
d.
intValue("??? 0x%x");
d.
intValue("??? Offset: %08x");
d.
intValue("??? 0x%x");
d.
intValue("??? 0x%x");
d.
intValue("??? 0x%x");
d.
intValue("??? Offset: %08x");
d.
intValue("??? 0x%x");
d.
intValue("??? 0x%x");
d.
intValue("??? 0x%x");
d.
intValue("??? 0x%x");
}
// Print any remaining part
d.
rawValue((int) (getHeaderLen
() - reader.
position()));
// The city field in mdr11 appears to have a minimum of 2
int ncities = getSection
(5).
getNumberOfRecords();
if (ncities
> 0x7fff
)
cityPtrSize =
3;
else
cityPtrSize =
2;
d.
print(outStream
);
outStream.
flush();
}
/**
* If there are more maps than will fit into a single byte, then
* two bytes are used. This routine sorts all that out.
*/
private void printMapIndex
(Displayer d
) {
int mi
;
if (numberOfMaps
> 255)
mi = d.
charValue("%d map number");
else
mi = d.
byteValue("%d map number");
if (mi
> numberOfMaps
) {
d.
print(outStream
);
outStream.
flush();
assert false:
"not a map number " + mi
;
}
}
/** The size of a map index - one or two bytes */
private int mapIndexSize
() {
return numberOfMaps
> 255? 2:
1;
}
/**
* Do we want to print this section.
*/
private boolean want
(int n
) {
Section section = getSection
(n
);
if (section
!=
null && section.
getLen() > 0 && wanted
[n
])
return true;
else
return false;
}
/**
* Print an mdr file.
*
* The sections printed can be limited by adding a list of numbers
* after the filename.
*
* If the list of numbers is preceded by a '!' character then all
* sections except for the listed ones are printed.
*/
public static void main
(String[] args
) {
if (args.
length < 1) {
System.
err.
println("Usage: mdrdisplay <filename>");
System.
exit(1);
}
MdrDisplay md =
new MdrDisplay
();
List<String> argv =
Arrays.
asList(args
);
Iterator<String> it = argv.
iterator();
String name = it.
next();
int start =
0;
boolean negate =
false;
boolean hasNumber =
false;
while (it.
hasNext()) {
String s = it.
next();
if (s.
equals("--start")) {
start =
Integer.
parseInt(it.
next());
} else if (s.
equals("!")) {
Arrays.
fill(md.
wanted,
true);
negate =
true;
} else {
int n =
Integer.
parseInt(s
);
md.
wanted[n
] =
!negate
;
hasNumber =
true;
}
}
if (!hasNumber
)
Arrays.
fill(md.
wanted,
true);
md.
setStart(start
);
md.
display(name,
"MDR");
}
}