mirror of
https://github.com/AppleCommander/ShrinkItArchive.git
synced 2024-06-18 04:29:28 +00:00
Added ability to get the CRC-16 from a ByteSource and for the MasterHeaderBlock to validate its CRC-16.
This commit is contained in:
parent
6c241ea036
commit
4371c7a730
|
@ -13,6 +13,8 @@ import java.util.GregorianCalendar;
|
|||
*/
|
||||
public class ByteSource implements ByteConstants {
|
||||
private InputStream inputStream;
|
||||
private long bytesRead = 0;
|
||||
private CRC16 crc = new CRC16();
|
||||
|
||||
/**
|
||||
* Construct a ByteSource from an InputStream.
|
||||
|
@ -33,7 +35,12 @@ public class ByteSource implements ByteConstants {
|
|||
* Note that an unsigned byte needs to be returned in a larger container (ie, a short or int or long).
|
||||
*/
|
||||
public int read() throws IOException {
|
||||
return inputStream.read();
|
||||
int b = inputStream.read();
|
||||
if (b != -1) {
|
||||
crc.update(b);
|
||||
bytesRead++;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
/**
|
||||
* Get the next byte and fail if we are at EOF.
|
||||
|
@ -51,9 +58,11 @@ public class ByteSource implements ByteConstants {
|
|||
public byte[] readBytes(int bytes) throws IOException {
|
||||
byte[] data = new byte[bytes];
|
||||
int read = inputStream.read(data);
|
||||
bytesRead+= read;
|
||||
if (read < bytes) {
|
||||
throw new IOException("Requested " + bytes + " bytes, but " + read + " read");
|
||||
}
|
||||
crc.update(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -102,4 +111,24 @@ public class ByteSource implements ByteConstants {
|
|||
data[TIMEREC_HOUR], data[TIMEREC_MINUTE], data[TIMEREC_SECOND]);
|
||||
return gc.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the CRC-16 to $0000.
|
||||
*/
|
||||
public void resetCrc() {
|
||||
crc.reset();
|
||||
}
|
||||
/**
|
||||
* Get the current CRC-16 value.
|
||||
*/
|
||||
public long getCrcValue() {
|
||||
return crc.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Answer with the total number of bytes read.
|
||||
*/
|
||||
public long getTotalBytesRead() {
|
||||
return bytesRead;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import java.util.Date;
|
|||
public class MasterHeaderBlock {
|
||||
private static final int MASTER_HEADER_LENGTH = 48;
|
||||
private int masterCrc;
|
||||
private boolean validCrc;
|
||||
private long totalRecords;
|
||||
private Date archiveCreateWhen;
|
||||
private Date archiveModWhen;
|
||||
|
@ -25,15 +26,11 @@ public class MasterHeaderBlock {
|
|||
|
||||
/**
|
||||
* Create the Master Header Block, based on the ByteSource.
|
||||
* To avoid byte counting, we read in the fixed size header
|
||||
* and then work our way through the data. When we are done,
|
||||
* that data is thrown away, and we don't need to ensure
|
||||
* that we've read a consistent number of bytes.
|
||||
*/
|
||||
public MasterHeaderBlock(ByteSource bs) throws IOException {
|
||||
bs = new ByteSource(bs.readBytes(MASTER_HEADER_LENGTH));
|
||||
bs.checkNuFileId();
|
||||
masterCrc = bs.readWord();
|
||||
bs.resetCrc(); // CRC is computed from this point to the end of the header
|
||||
totalRecords = bs.readLong();
|
||||
archiveCreateWhen = bs.readDate();
|
||||
archiveModWhen = bs.readDate();
|
||||
|
@ -44,6 +41,11 @@ public class MasterHeaderBlock {
|
|||
} else {
|
||||
masterEof = -1;
|
||||
}
|
||||
// Read whatever remains of the fixed size header
|
||||
while (bs.getTotalBytesRead() < MASTER_HEADER_LENGTH) {
|
||||
bs.readByte();
|
||||
}
|
||||
validCrc = (masterCrc == bs.getCrcValue());
|
||||
}
|
||||
|
||||
// GENERATED CODE
|
||||
|
@ -84,4 +86,7 @@ public class MasterHeaderBlock {
|
|||
public void setMasterEof(long masterEof) {
|
||||
this.masterEof = masterEof;
|
||||
}
|
||||
public boolean isValidCrc() {
|
||||
return validCrc;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ public class ByteSourceTest extends TestCase {
|
|||
ByteSource bs = new ByteSource("a".getBytes());
|
||||
assertEquals('a', bs.read());
|
||||
assertEquals(-1, bs.read());
|
||||
assertEquals(1, bs.getTotalBytesRead());
|
||||
}
|
||||
public void testReadB() throws IOException {
|
||||
// Just to ensure we can get more than one byte...
|
||||
|
@ -27,6 +28,7 @@ public class ByteSourceTest extends TestCase {
|
|||
assertEquals('l', bs.read());
|
||||
assertEquals('o', bs.read());
|
||||
assertEquals(-1, bs.read());
|
||||
assertEquals(5, bs.getTotalBytesRead());
|
||||
}
|
||||
public void testReadBytesInt() throws IOException {
|
||||
// Ensure we read the requested data.
|
||||
|
@ -34,6 +36,7 @@ public class ByteSourceTest extends TestCase {
|
|||
assertEquals("Hello", new String(bs.readBytes(5)));
|
||||
assertEquals("World", new String(bs.readBytes(5)));
|
||||
assertEquals(-1, bs.read());
|
||||
assertEquals(10, bs.getTotalBytesRead());
|
||||
}
|
||||
public void textReadBytesIntError() {
|
||||
// Ensure that we fail appropriately
|
||||
|
@ -43,17 +46,20 @@ public class ByteSourceTest extends TestCase {
|
|||
fail();
|
||||
} catch (IOException ex) {
|
||||
assertTrue(true); // Expected
|
||||
assertEquals(2, bs.getTotalBytesRead());
|
||||
}
|
||||
}
|
||||
public void testCheckNuFileId() throws IOException {
|
||||
ByteSource bs = new ByteSource(new byte[] { 0x4e, (byte)0xf5, 0x46, (byte)0xe9, 0x6c, (byte)0xe5 });
|
||||
assertTrue(bs.checkNuFileId());
|
||||
assertEquals(6, bs.getTotalBytesRead());
|
||||
bs = new ByteSource("NotNuFile".getBytes());
|
||||
assertFalse(bs.checkNuFileId());
|
||||
}
|
||||
public void testCheckNuFxId() throws IOException {
|
||||
ByteSource bs = new ByteSource(new byte[] { 0x4e, (byte)0xf5, 0x46, (byte)0xd8 });
|
||||
assertTrue(bs.checkNuFxId());
|
||||
assertEquals(4, bs.getTotalBytesRead());
|
||||
bs = new ByteSource("NotNuFx".getBytes());
|
||||
assertFalse(bs.checkNuFxId());
|
||||
}
|
||||
|
@ -61,18 +67,22 @@ public class ByteSourceTest extends TestCase {
|
|||
ByteSource bs = new ByteSource(new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 });
|
||||
assertEquals(0x0201, bs.readWord());
|
||||
assertEquals(0x0403, bs.readWord());
|
||||
assertEquals(4, bs.getTotalBytesRead());
|
||||
}
|
||||
public void testReadWordHighBitSet() throws IOException {
|
||||
ByteSource bs = new ByteSource(new byte[] { (byte)0xff, (byte)0xff });
|
||||
assertEquals(0xffff, bs.readWord());
|
||||
assertEquals(2, bs.getTotalBytesRead());
|
||||
}
|
||||
public void testReadLong() throws IOException {
|
||||
ByteSource bs = new ByteSource(new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 });
|
||||
assertEquals(0x04030201, bs.readLong());
|
||||
assertEquals(4, bs.getTotalBytesRead());
|
||||
}
|
||||
public void testReadLongHighBitSet() throws IOException {
|
||||
ByteSource bs = new ByteSource(new byte[] { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff });
|
||||
assertEquals(0xffffffffL, bs.readLong());
|
||||
assertEquals(4, bs.getTotalBytesRead());
|
||||
}
|
||||
public void testReadDate() throws IOException {
|
||||
ByteSource bs = new ByteSource(new byte[] {
|
||||
|
@ -84,11 +94,13 @@ public class ByteSourceTest extends TestCase {
|
|||
assertEquals(new GregorianCalendar(1988, Calendar.OCTOBER, 22, 1, 10, 0).getTime(), bs.readDate());
|
||||
assertEquals(new GregorianCalendar(1988, Calendar.NOVEMBER, 17, 11, 16, 0).getTime(), bs.readDate());
|
||||
assertEquals(new GregorianCalendar(1988, Calendar.OCTOBER, 22, 13, 12, 0).getTime(), bs.readDate());
|
||||
assertEquals(24, bs.getTotalBytesRead());
|
||||
}
|
||||
public void testReadNullDate() throws IOException {
|
||||
ByteSource bs = new ByteSource(new byte[] {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // null date
|
||||
});
|
||||
assertNull(bs.readDate());
|
||||
assertEquals(8, bs.getTotalBytesRead());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import junit.framework.TestCase;
|
|||
* @author robgreene@users.sourceforge.net
|
||||
*/
|
||||
public class MasterHeaderBlockTest extends TestCase {
|
||||
public void test1() throws IOException {
|
||||
public void testWithValidCrc() throws IOException {
|
||||
ByteSource bs = new ByteSource(new byte[] {
|
||||
0x4e, (byte)0xf5, 0x46, (byte)0xe9, 0x6c, (byte)0xe5, (byte)0xdc, 0x1b,
|
||||
0x2d, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x14, 0x5f,
|
||||
|
@ -28,5 +28,19 @@ public class MasterHeaderBlockTest extends TestCase {
|
|||
assertEquals(new ByteSource(new byte[] {0x29, 0x0d, 0x14, 0x5f, 0x08, 0x07, 0x01, 0x04}).readDate(), b.getArchiveModWhen());
|
||||
assertEquals(0x01, b.getMasterVersion());
|
||||
assertEquals(0x1acae, b.getMasterEof());
|
||||
assertTrue(b.isValidCrc());
|
||||
}
|
||||
|
||||
public void testWithInvalidCrc() throws IOException {
|
||||
ByteSource bs = new ByteSource(new byte[] {
|
||||
0x4e, (byte)0xf5, 0x46, (byte)0xe9, 0x6c, (byte)0xe5, 0x00, 0x00, // <-- Bad CRC!
|
||||
0x2d, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x14, 0x5f,
|
||||
0x08, 0x07, 0x30, 0x04, 0x29, 0x0d, 0x14, 0x5f,
|
||||
0x08, 0x07, 0x01, 0x04, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0xae, (byte)0xac,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
});
|
||||
MasterHeaderBlock b = new MasterHeaderBlock(bs);
|
||||
assertFalse(b.isValidCrc());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,9 +33,9 @@ public class NuFileArchiveTest extends TestCase {
|
|||
MasterHeaderBlock m = a.getMasterHeaderBlock();
|
||||
System.out.printf("Master Header Block\n==================\n"
|
||||
+ "master_crc=$%x\ntotal_records=%d\narchive_create_when=%tc\narchive_mod_when=%tc\n"
|
||||
+ "master_version=%d\nmaster_eof=$%x\n\n",
|
||||
+ "master_version=%d\nmaster_eof=$%x\nisValidCrc = %b\n\n",
|
||||
m.getMasterCrc(), m.getTotalRecords(), m.getArchiveCreateWhen(), m.getArchiveModWhen(),
|
||||
m.getMasterVersion(), m.getMasterEof());
|
||||
m.getMasterVersion(), m.getMasterEof(), m.isValidCrc());
|
||||
for (HeaderBlock b : a.getHeaderBlocks()) {
|
||||
System.out.printf("\tHeader Block\n\t============\n");
|
||||
System.out.printf("\theader_crc=$%x\n\tattrib_count=%d\n\tversion_number=%d\n\ttotal_threads=%d\n\t"
|
||||
|
|
Loading…
Reference in New Issue
Block a user