/*
* JaamSim Discrete Event Simulation
* Copyright (C) 2013 Ausenco Engineering Canada Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jaamsim.MeshFiles;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.zip.CRC32;
public class BlockReader {
private static final boolean CHECK_PAYLOAD_CRC = false;
public static final MeshData parse(URI asset) throws Exception {
InputStream inStream = asset.toURL().openStream();
DataBlock block = readBlock(inStream);
return new MeshData(false, block, asset.toURL());
}
public static DataBlock readBlock(InputStream in) {
try {
byte[] readBuffer = new byte[128];
// Read the header
readLoop(in, readBuffer, 0, 4);
for (int i = 0; i < 4; ++i) {
if (readBuffer[i] != BlockUtils.header[i])
throw new DataBlock.Error("Missing block header");
}
// Read the header CRC
readLoop(in, readBuffer, 0, 4);
int headerValue = BlockUtils.intFromBytes(readBuffer, 0);
CRC32 headerCRC = new CRC32();
// Read until a null byte, or max 128
int stringSize = 0;
while (stringSize < 128) {
byte b = (byte)in.read();
headerCRC.update(b);
readBuffer[stringSize] = b;
if (b == 0)
break;
++stringSize;
}
if (stringSize == 128) {
throw new DataBlock.Error("No null terminator for block name");
}
String blockName = new String(readBuffer, 0, stringSize, "UTF-8");
// Read the number of children
readLoop(in, readBuffer, 0, 4);
int numChildren = BlockUtils.intFromBytes(readBuffer, 0);
headerCRC.update(readBuffer, 0, 4);
// Read the block size
readLoop(in, readBuffer, 0, 8);
long payloadSize = BlockUtils.longFromBytes(readBuffer, 0);
headerCRC.update(readBuffer, 0, 8);
// check the header Adds up
if ((int)headerCRC.getValue() != headerValue) {
throw new DataBlock.Error("Header CRC mismatch");
}
ArrayList<DataBlock> children = new ArrayList<>();
BlockUtils.CRCInputStream wrappedIn = new BlockUtils.CRCInputStream(in, CHECK_PAYLOAD_CRC);
for (int i = 0; i < numChildren; ++i) {
children.add(readBlock(wrappedIn));
}
// Now read the remainder of the payload
long remainingBytes = payloadSize - wrappedIn.getBytesRead();
if (remainingBytes > Integer.MAX_VALUE) throw new DataBlock.Error("Block is too big and broke java");
byte[] data = new byte[(int)remainingBytes];
readLoop(in, data, 0, (int)remainingBytes);
// Check the CRC and footer
readLoop(in, readBuffer, 0, 4);
if (CHECK_PAYLOAD_CRC) {
int payloadValue = BlockUtils.intFromBytes(readBuffer, 0);
if (payloadValue != (int)wrappedIn.getCRC())
throw new DataBlock.Error("Block payload CRC mismatch");
}
// Finally read the footer
readLoop(in, readBuffer, 0, 4);
for (int i = 0; i < 4; ++i) {
if (readBuffer[i] != BlockUtils.footer[i])
throw new DataBlock.Error("Missing block header");
}
// Everything checks out here, return the block
return new DataBlock(blockName, data, children);
} catch (Exception e) {
throw new DataBlock.Error(e.getMessage());
}
}
/**
* Wrapper around InputStream.read() that keeps reading until the requested amount is found, or EOF
* Throws on error
*/
private static void readLoop(InputStream in, byte[] buffer, int offset, int size) throws IOException {
int total = 0;
int bytesRead;
while (total < size) {
bytesRead = in.read(buffer, total, size - total);
if (bytesRead == -1)
throw new DataBlock.Error("Unexpected End of stream");
total += bytesRead;
}
}
}