Rev 4093 |
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: 23-Sep-2007
*/
package uk.me.parabola.tdbfmt;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import uk.me.parabola.imgfmt.app.Area;
import uk.me.parabola.io.EndOfFileException;
import uk.me.parabola.io.StructuredInputStream;
import uk.me.parabola.log.Logger;
/**
* The TDB file. See the package documentation for more details.
*
* @author Steve Ratcliffe
*/
public class TdbFile
{
private static final Logger log =
Logger.
getLogger(TdbFile.
class);
public static final int TDB_V407 =
407;
// The version number of the TDB format
private int tdbVersion
;
// The blocks that go to make up the file.
private HeaderBlock headerBlock
;
private CopyrightBlock copyrightBlock =
new CopyrightBlock
();
private OverviewMapBlock overviewMapBlock
;
private final List<DetailMapBlock
> detailBlocks =
new ArrayList<>();
private final RBlock rblock =
new RBlock
();
private final TBlock tblock =
new TBlock
();
private String overviewDescription
;
private int codePage
;
public TdbFile
() {
}
public TdbFile
(int tdbVersion
) {
this.
tdbVersion = tdbVersion
;
}
/**
* Read in a TDB file from the disk.
*
* @param name The file name to load.
* @return A TdbFile instance.
* @throws IOException For problems reading the file.
*/
public static TdbFile read
(String name
) throws IOException {
TdbFile tdb =
new TdbFile
();
try (InputStream is =
new BufferedInputStream(new FileInputStream(name
))) {
tdb.
load(is
);
}
return tdb
;
}
public void setProductInfo
(int familyId,
int productId,
short productVersion,
String seriesName,
String familyName,
String overviewDescription,
byte enableProfile
)
{
headerBlock =
new HeaderBlock
(tdbVersion
);
headerBlock.
setFamilyId((short) familyId
);
headerBlock.
setProductId((short) productId
);
headerBlock.
setProductVersion(productVersion
);
headerBlock.
setSeriesName(seriesName
);
headerBlock.
setFamilyName(familyName
);
headerBlock.
setEnableProfile(enableProfile
);
this.
overviewDescription = overviewDescription
;
}
public void setCodePage
(int codePage
) {
this.
codePage = codePage
;
headerBlock.
setCodePage(codePage
);
}
/**
* Add a copyright segment to the file.
* @param msg The message to add.
*/
public void addCopyright
(String msg
) {
if (msg.
isEmpty())
return;
CopyrightSegment seg =
new CopyrightSegment
(CopyrightSegment.
CODE_COPYRIGHT_TEXT_STRING,
3, msg
);
copyrightBlock.
addSegment(seg
);
}
/**
* Set the overview information. Basically the overall size of the map
* set.
* @param bounds The bounds for the map.
*/
public void setOverview
(Area bounds,
String number
) {
overviewMapBlock =
new OverviewMapBlock
();
overviewMapBlock.
setArea(bounds
);
overviewMapBlock.
setMapName(number
);
overviewMapBlock.
setDescription(overviewDescription
);
}
/**
* Add a detail block. This describes and names one of the maps in the
* map set.
* @param detail The detail to add.
*/
public void addDetail
(DetailMapBlock detail
) {
detailBlocks.
add(detail
);
}
public void write
(String name
) throws IOException {
if (headerBlock ==
null || overviewMapBlock ==
null)
throw new IOException("Attempting to write file without being fully set up");
try (CheckedOutputStream stream =
new CheckedOutputStream(
new BufferedOutputStream(new FileOutputStream(name
)),
new CRC32()))
{
headerBlock.
writeTo(stream, codePage
);
copyrightBlock.
writeTo(stream, codePage
);
if (tdbVersion
>= TDB_V407
) {
rblock.
writeTo(stream, codePage
);
}
overviewMapBlock.
writeTo(stream, codePage
);
for (DetailMapBlock detail : detailBlocks
) {
detail.
writeTo(stream, codePage
);
}
if (tdbVersion
>= TDB_V407
) {
tblock.
setSum(stream.
getChecksum().
getValue());
tblock.
writeTo(stream, codePage
);
}
}
}
/**
* Load from the given file name.
*
* @param ds The stream to read from.
* @throws IOException For problems reading the file.
*/
private void load
(InputStream ds
) throws IOException {
boolean eof =
false;
while (!eof
) {
try {
readBlock
(ds
);
} catch (EndOfFileException ignore
) {
eof =
true;
}
}
}
/**
* The file is divided into blocks. This reads a single block.
*
* @param is The input stream.
* @throws IOException For problems reading the file.
*/
private void readBlock
(InputStream is
) throws IOException {
int blockType = is.
read();
if (blockType == -
1)
throw new EndOfFileException
();
int blockLength = readBlockLength
(is
);
if (blockLength == -
1)
throw new EndOfFileException
();
byte[] body =
new byte[blockLength
];
int n = is.
read(body
);
if (n
< 0)
throw new IOException("failed to read block");
StructuredInputStream ds =
new StructuredInputStream
(new ByteArrayInputStream(body
));
switch (blockType
) {
case HeaderBlock.
BLOCK_ID:
headerBlock =
new HeaderBlock
(ds
);
log.
info("header block seen", headerBlock
);
break;
case CopyrightBlock.
BLOCK_ID:
log.
info("copyright block");
copyrightBlock =
new CopyrightBlock
(ds
);
break;
case OverviewMapBlock.
BLOCK_ID:
overviewMapBlock =
new OverviewMapBlock
(ds
);
log.
info("overview block", overviewMapBlock
);
break;
case DetailMapBlock.
BLOCK_ID:
DetailMapBlock db =
new DetailMapBlock
(ds
);
log.
info("detail block", db
);
detailBlocks.
add(db
);
break;
default:
log.
warn("Unknown block in tdb file");
break;
}
}
private int readBlockLength
(InputStream is
) throws IOException {
int b1 = is.
read();
if (b1
< 0)
return -
1;
int b2 = is.
read();
if (b2
< 0)
return -
1;
return (b2
& 0xff
) << 8 | b1
& 0xff
;
}
public int getTdbVersion
() {
return headerBlock.
getTdbVersion();
}
}