Rev 157 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* Copyright (C) 2007 Steve Ratcliffe
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 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.
*
*
* Author: Steve Ratcliffe
* Create date: Dec 16, 2007
*/
package test.display;
import java.util.Arrays;
import java.util.Formatter;
/**
* Standalone program to learn how to calculate the values at 9a in
* the TRE header.
*
* @author Steve Ratcliffe
*/
public class TreCalc
extends CommonDisplay
{
protected void print
() {
readCommonHeader
();
readHeader
();
}
public static void main
(String[] args
) {
if (args.
length < 1) {
System.
err.
println("Usage: tredisplay <filename>");
System.
exit(1);
}
String name = args
[0];
CommonDisplay nd =
new TreCalc
();
nd.
display(name,
"TRE");
}
void readHeader
() {
if (getHeaderLen
() < 188)
return;
Displayer d =
new Displayer
(reader
);
d.
setTitle("TRE Header");
// Print the map id out in various ways
DisplayItem item = d.
item();
reader.
position(0x74
);
int mapId = reader.
getInt();
item.
setStartPos(reader.
position());
item.
setBytes(mapId
);
item.
addText("Map ID %08d", mapId
)
.
addText("Map ID 0x%08x", mapId
);
// Print the actual values of the four words starting at 9A
reader.
position(0x9a
);
int u1 = d.
intValue("9A actual: %08x");
int u2 = d.
intValue("9E actual: %08x");
int u3 = d.
intValue("A2 actual: %08x");
int u4 = d.
intValue("A6 actual: %08x");
CalcValues actual =
new CalcValues
();
actual.
setVal(1, u1
);
actual.
setVal(2, u2
);
actual.
setVal(3, u3
);
actual.
setVal(4, u4
);
// item.addText("u1 %s", actual.getAsString(1));
// Now attempt to calculate the values.
CalcValues vals =
new CalcValues
();
vals.
setMapId(mapId
);
// The third an fourth values are the same and built from the map id
int offset = lookupOffset
(mapId, u3
); // not known how to calculate offset
int val = calcThird
(mapId, offset
);
vals.
setValRev(3, val
);
vals.
setValRev(4, val
);
calcSecond
(vals
);
calcFirst
(vals
);
// Print out the calcualted values
item = d.
item();
item.
addText("Calculated values (with off %d)", offset
);
item.
addText("U1 calc : %s", vals.
getAsString(1));
item.
addText("U2 calc : %s", vals.
getAsString(2));
item.
addText("U3 calc : %08x", vals.
get(3));
item.
addText("U4 calc : %08x", vals.
get(4));
// Diff the calculated verses actual
diffValues
(d, vals, actual
);
d.
gap();
item = d.
item();
int l1 = actual.
get(2,
2);
int l2 = actual.
get(2,
3);
item.
addText("Last val: %1x %1x", l1, l2
);
item.
addText("diff is %d", l2 - l1
);
item.
addText(" unoff: %x %x",
(l1 - offset
) & 0xf,
(l2 - offset
) & 0xf
);
// hmmm, looks like the un-offset value is not much dependant on the low
// bytes of the map id.
d.
print(outStream
);
}
private void diffValues
(Displayer d, CalcValues calcValues, CalcValues actual
) {
for (int n =
1; n
<=
4; n++
) {
byte[] arr1 = calcValues.
getArray(n
);
byte[] arr2 = actual.
getArray(n
);
for (int i =
0; i
< 8; i++
) {
byte bcalc = arr1
[i
];
if (bcalc
< 0)
continue;
byte bactual = arr2
[i
];
if ((bcalc
& 0xf
) !=
(bactual
& 0xf
)) {
DisplayItem item = d.
item();
item.
addText("MISMATCH at U%d n%d", n, i
);
}
}
}
}
private void calcFirst
(CalcValues vals
) {
byte[] arr = vals.
getArray(1);
// The first bytes are sometimes the low bytes of the mapId with
// the offset added.
arr
[0] =
(byte) (vals.
getMapIdNibble(3) + vals.
getOffset());
arr
[1] =
(byte) (vals.
getMapIdNibble(2) + vals.
getOffset());
arr
[2] =
(byte) (vals.
getMapIdNibble(1) + vals.
getOffset());
arr
[3] =
(byte) (vals.
getMapIdNibble(0) + vals.
getOffset());
// Adding the mapped value from the top bytes...
// Nibbles are reversed here.
arr
[0] += mapIdCodeTable
[vals.
getMapIdNibble(6)];
arr
[1] += mapIdCodeTable
[vals.
getMapIdNibble(7)];
arr
[2] += mapIdCodeTable
[vals.
getMapIdNibble(4)];
arr
[3] += mapIdCodeTable
[vals.
getMapIdNibble(5)];
// The following are copies of U3
arr
[4] =
(byte) vals.
get(3,
4);
arr
[5] =
(byte) vals.
get(3,
5);
arr
[6] =
(byte) vals.
get(3,
6);
// seven is always(?) one more
arr
[7] =
(byte) (vals.
get(3,
7) +
1);
}
/**
* Calculate the second value U2.
* @param vals The existing values that have been calculated and where any
* values we calculate here will be placed.
*/
private void calcSecond
(CalcValues vals
) {
byte[] arr = vals.
getArray(2);
// The first byte is copied from the first byte of U3
arr
[0] =
(byte) vals.
get(3,
0);
arr
[1] =
(byte) vals.
get(3,
1);
// This byte appears to come from the corresponding byte
// of the map id subtracting a constant (modulo 16).
// The numbers to add come from the header length?
int h1 = getHeaderLen
() >> 4;
int h2 = getHeaderLen
();
arr
[2] =
(byte) ((vals.
get(3,
2) + h1
) & 0xf
);
arr
[3] =
(byte) ((vals.
get(3,
3) + h2
) & 0xf
);
// The following are the sum of individual nibbles in U3 and the
// corresponding nibble in the top half of mapId.
arr
[4] =
(byte) (vals.
get(3,
4) + vals.
getMapIdNibble(7));
arr
[5] =
(byte) (vals.
get(3,
5) + vals.
getMapIdNibble(6));
arr
[6] =
(byte) (vals.
get(3,
6) + vals.
getMapIdNibble(5));
arr
[7] =
(byte) (vals.
get(3,
7) + vals.
getMapIdNibble(4));
}
/**
* We don't know how the calculate the offset value, so look it up
* by getting the actual value of one of the bytes and subtracting
* the value we would calculate without the offset.
* @param mapId The map number.
*
* So this is cheating as we are looking at the 'answer' but it is only
* one nibble which allows us to calculate over 16 nibbles.
*
* @param a2 The value at 0xa2 in the file.
* @return The offset value that was used.
*/
private int lookupOffset
(int mapId,
int a2
) {
int topMapId = mapIdCodeTable
[(mapId
>>> 28) & 0xf
];
return (((a2
>>> 24)&0xf
) - topMapId
);
}
/**
* Calculate the third (and fourth as it is the same) value.
*
* We take the nibbles from the mapId as if we had printed the number out in hex
* and started at the most significant nibble and work to the least. Each nibble is
* looked up in a table and an offset is added. The values are the combined into
* an integer from the least significant nibble upward. This means that the
* nibbles are reversed, but also swapped in pairs.
*
* @param mapId The map number.
* @return The third word of the values.
*/
private int calcThird
(int mapId,
int offset
) {
NibbleInt in =
new NibbleInt
(mapId
);
NibbleInt out =
new NibbleInt
(0);
for (int i =
0; i
< 8; i++
) {
int nib = in.
extractNibble(i
);
int n = mapIdCodeTable
[nib
] + offset
;
out.
setNibble((7-
(i^
1)), n
);
}
return out.
intValue();
}
private final int[] mapIdCodeTable =
{
0,
1, 0xf,
5,
0xd,
4,
7,
6,
0xb,
9, 0xe,
8,
2, 0xa, 0xc,
3
};
private static class NibbleInt
{
private int in
;
private NibbleInt
(int in
) {
this.
in = in
;
}
/**
* Return a nibble from the integer. Counted little endian, 0 is the
* least significant nibble.
* @param n The nibble to extract. 0 is least significant.
*/
public int extractNibble
(int n
) {
return (in
>>> (n
* 4)) & 0xf
;
}
public void setNibble
(int n,
int val
) {
in |=
(val
& 0xf
) << (n
* 4);
}
public int intValue
() {
return in
;
}
public void set
(byte[] arr
) {
for (int i =
0; i
< 8; i++
)
setNibble
(7-i, arr
[i
]);
}
}
private class CalcValues
{
private int offset
;
private final byte[][] values =
new byte[4][];
private NibbleInt mapId
;
private final int[] offsetMap =
{
6,
7,
5,
11,
3,
10,
13,
12,
1,
15,
4,
14,
8,
0,
2,
9
};
private CalcValues
() {
for (int i =
0; i
< 4; i++
) {
values
[i
] =
new byte[8];
Arrays.
fill(values
[i
],
(byte) 0xff
);
}
}
public void setValRev
(int n,
int val
) {
NibbleInt nint =
new NibbleInt
(val
);
byte[] arr = values
[n -
1];
for (int i =
0; i
< 8; i++
) {
arr
[i
] =
(byte) (nint.
extractNibble(i
) & 0xf
);
}
}
public void setVal
(int n,
int val
) {
NibbleInt nint =
new NibbleInt
(val
);
byte[] arr = values
[n -
1];
for (int i =
0; i
< 8; i++
) {
arr
[i
] =
(byte) (nint.
extractNibble(7-i
) & 0xf
);
}
}
public int get
(int n
) {
byte[] arr = values
[n -
1];
NibbleInt nint =
new NibbleInt
(0);
nint.
set(arr
);
return nint.
intValue();
}
public String getAsString
(int n
) {
byte[] arr = values
[n -
1];
StringBuffer sb =
new StringBuffer();
Formatter fmt =
new Formatter(sb
);
for (byte b : arr
) {
if (b
< 0)
sb.
append('x');
else
fmt.
format("%x", b
& 0xf
);
}
return sb.
toString();
}
public int get
(int n,
int i
) {
byte[] arr = values
[n -
1];
return arr
[i
] & 0xf
;
}
public byte[] getArray
(int n
) {
return values
[n -
1];
}
public int getOffset
() {
return offset
;
}
public void setMapId
(int mapId
) {
NibbleInt nint =
new NibbleInt
(mapId
);
// To get the offset value we add up all the even nibbles of the map
// number and transform via a table.
this.
mapId = nint
;
int rawoff = nint.
extractNibble(0) + nint.
extractNibble(2)
+ nint.
extractNibble(4) + nint.
extractNibble(6);
this.
offset = offsetMap
[rawoff
& 0xf
];
}
public int getMapIdNibble
(int n
) {
return mapId.
extractNibble(n
);
}
}
}