package test.display;
import java.io.BufferedOutputStream;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.imgfmt.app.BufferedImgFileReader;
import uk.me.parabola.imgfmt.app.ImgFileReader;
import uk.me.parabola.imgfmt.app.lbl.LBLFileReader;
import uk.me.parabola.imgfmt.app.trergn.Subdivision;
import uk.me.parabola.imgfmt.app.trergn.TREFileReader;
import uk.me.parabola.imgfmt.app.trergn.Zoom;
import uk.me.parabola.imgfmt.fs.DirectoryEntry;
import uk.me.parabola.imgfmt.fs.FileSystem;
import uk.me.parabola.imgfmt.fs.ImgChannel;
import uk.me.parabola.imgfmt.sys.FileImgChannel;
import uk.me.parabola.imgfmt.sys.ImgFS;
import test.elements.Line;
import test.files.NetFile;
import test.files.Nod2Record;
import test.files.NodFile;
import test.files.RgnFile;
import test.files.RoadData;
import test.files.RouteNode;
import test.files.Segment;
/**
* Common code for files that have the 'common header' in a .img file.
*/
public abstract class CommonDisplay
{
public static final int COMMON_HEADER_LEN =
21;
// You will always have a reader.
protected ImgFileReader reader
;
// You may not have a filesystem.
private FileSystem fs
;
protected PrintStream outStream =
new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.
out)));
private int headerLen
;
protected long filelen
;
// The sections in the file, if any.
protected SectList sections =
new SectList
();
protected LBLFileReader lbl
;
protected TREFileReader tre
;
protected RgnFile rgn
;
protected NetFile net
;
protected NodFile nod
;
protected int citySize
;
protected int zipSize
;
protected abstract void print
();
protected void readCommonHeader
() {
Displayer d =
new Displayer
(reader
);
d.
setTitle("Common Header");
headerLen = d.
charValue("Header length %d");
d.
stringValue(10,
"File type %s");
d.
byteValue("???");
d.
byteValue("Set if locked");
DisplayItem item = d.
rawItem(7);
Date date = Utils.
makeCreationTime(item.
getBytes());
DateFormat df =
new SimpleDateFormat("HH:mm:ss d MMM yyyy");
item.
addText(df.
format(date
));
d.
print(outStream
);
}
protected void readHeaderLen
() {
reader.
position(0);
headerLen = reader.
getChar();
reader.
position(COMMON_HEADER_LEN
);
}
/**
* Read the common combination offset + size (+ record size) in file headers.
* Utility function for subclasses.
*
* @param d The {@link Displayer} to use.
* @param name The name of the sub-file.
* @param number The section number.
* @param hasRecSize There is a record size to be read.
* @param hasMagic Has an 4 byte integer value following the header information. Otherwise
* false and any extra values must be read explicitly.
* @return The section information. It has also been saved for retrieval with getSection.
*/
protected Section readSection
(Displayer d,
String name,
int number,
boolean hasRecSize,
boolean hasMagic
) {
assert number
!=
0;
d.
gap();
int start = d.
intValue(name +
" at offset %#08x");
int len = d.
intValue(name +
" length %d");
Section section =
new Section
(name, start, len
);
d.
item().
addText("End of section %08x, len %#08x", start + len, len
);
if (hasRecSize
) {
int recordSize = d.
charValue(name +
" record size %02x");
if (recordSize
!=
0) {
//assert len % recordSize == 0 : "sect" + number + ", len=" + len + ", recsize=" + recordSize;
d.
item().
addText("Number of records %d", len / recordSize
);
if (len
% recordSize
!=
0)
d.
item().
addText("FRACTIONAL RECORD");
}
section.
setRecordSize(recordSize
);
}
if (hasMagic
) {
int magic = d.
intValue(name +
" header flags %04x");
section.
setMagic(magic
);
}
while (sections.
size() < number-
1)
sections.
add(null);
sections.
add(number-
1, section
);
return section
;
}
protected Section getSection
(int n
) {
assert n
!=
0;
return sections.
get(n-
1);
}
protected int numberOfSections
() {
return sections.
size();
}
/**
* This is used when you want to open a plain file.
* @param name The name of the file to open.
*/
protected void display
(String name
) {
RandomAccessFile raf =
null;
try {
raf =
new RandomAccessFile(name,
"r");
filelen = raf.
length();
ImgChannel chan =
new FileImgChannel
(raf.
getChannel());
this.
reader =
new BufferedImgFileReader
(chan
);
print
();
outStream.
flush();
} catch (FileNotFoundException e
) {
System.
err.
println("Could not open file: " + name
);
} catch (IOException e
) {
System.
err.
println("Could not get file size or read file " + name
);
} finally {
if (raf
!=
null) {
try {
raf.
close();
} catch (IOException e
) {
// ok
}
}
}
}
/**
* This is used to open a file within an img file.
*
* @param name The name of the .img file.
* @param fileExt The file to get. It is found by extension.
*/
protected void display
(String name,
String fileExt
) {
if (name.
toLowerCase().
endsWith(fileExt.
toLowerCase())) {
display
(name
);
return;
}
try {
fs = ImgFS.
openFs(name
);
ImgChannel chan = findFile
(fileExt
);
this.
reader =
new BufferedImgFileReader
(chan
);
print
();
outStream.
flush();
} catch (FileNotFoundException e
) {
System.
err.
println("Could not open file: " + name
);
} finally {
if (fs
!=
null) {
fs.
close();
}
}
}
protected ImgChannel findFile
(String fileExt
) throws FileNotFoundException {
if (fs ==
null)
throw new FileNotFoundException("Not an img file");
List<DirectoryEntry
> entries = fs.
list();
ImgChannel chan =
null;
for (DirectoryEntry ent : entries
) {
if (ent.
getExt().
equals(fileExt
)) {
filelen = ent.
getSize();
chan = fs.
open(ent.
getFullName(),
"r");
}
}
if (chan ==
null)
throw new FileNotFoundException("No file with " + fileExt +
" extension");
return chan
;
}
protected void openLbl
() {
if (lbl
!=
null)
return;
try {
ImgChannel chan = findFile
("LBL");
lbl =
new LBLFileReader
(chan
);
int numCities = lbl.
getCities().
size();
if (numCities
> 255)
citySize =
2;
int numZips = lbl.
getZips().
size();
if (numZips
> 255)
zipSize =
2;
} catch (FileNotFoundException e
) {
System.
err.
println("Could not open LBL file");
}
}
protected String fetchLabel
(int laboff
) {
if (lbl ==
null)
return "";
return lbl.
fetchLabel(laboff
).
getText();
}
protected void openTre
() {
if (tre
!=
null)
return;
try {
ImgChannel chan = findFile
("TRE");
tre =
new TREFileReader
(chan
);
} catch (FileNotFoundException e
) {
System.
err.
println("Could not open TRE file");
}
}
protected void openRgn
() {
if (rgn
!=
null)
return;
if (lbl ==
null)
openLbl
();
if (net ==
null)
openNet
();
try {
ImgChannel chan = findFile
("RGN");
rgn =
new RgnFile
(chan
);
rgn.
setLblFile(lbl
);
rgn.
setNetFile(net
);
} catch (FileNotFoundException e
) {
System.
err.
println("Could not open RGN file");
}
}
protected void openNet
() {
if (net
!=
null)
return;
if (lbl ==
null)
openLbl
();
try {
ImgChannel chan = findFile
("NET");
net =
new NetFile
(new BufferedImgFileReader
(chan
));
net.
setLableFile(lbl
);
} catch (FileNotFoundException e
) {
System.
err.
println("Could not open NET file");
}
}
protected void openNod
() {
if (nod
!=
null)
return;
try {
ImgChannel chan = findFile
("NOD");
nod =
new NodFile
(new BufferedImgFileReader
(chan
));
} catch (FileNotFoundException e
) {
System.
err.
println("Could not open NOD file");
}
}
protected void initRoads
() {
if (nod ==
null)
openNod
();
Zoom
[] levels = tre.
getMapLevels();
Subdivision
[] subdivisions = tre.
subdivForLevel(levels
[levels.
length -
1].
getLevel());
Map<Integer,
Line> lines =
new HashMap<>();
Set<Integer> netOffsets =
new HashSet<>();
// Fetch all lines and save all net offsets found.
for (Subdivision sub : subdivisions
) {
for (Line line : rgn.
linesForSubdiv(sub
)) {
int divNum = line.
getDivAndNum();
lines.
put(divNum, line
);
if (line.
hasNet()) {
netOffsets.
add(line.
getNetOffset());
}
}
}
// Go through all net offsets and set the line information into the road data.
for (int noff : netOffsets
) {
RoadData road = net.
getRoad(noff
);
for (Segment seg : road.
getSegments()) {
int divNum = seg.
getDivAndNum();
Line line = lines.
get(divNum
);
assert line
!=
null;
seg.
setLine(line
);
}
int offsetNod2 = road.
getOffsetNod2();
Nod2Record netInfo = nod.
getNod2(offsetNod2
);
netInfo.
setRoadData(road
);
road.
setNod2(netInfo
);
RouteNode node = netInfo.
getNode();
if (node ==
null) {
System.
out.
printf("Could not find node for road %x nod2=%x\n",
noff,
offsetNod2
);
continue;
}
node.
setRoad(road
);
}
}
protected void setOutStream
(PrintStream outStream
) {
this.
outStream = outStream
;
}
protected int getHeaderLen
() {
return headerLen
;
}
protected void analyze
(PrintStream outStream
) {
sections.
analyze(outStream
);
}
protected void addSection
(Section section
) {
sections.
add(section
);
}
}