/*
DemoHeader.java
(c) 2012-2014 Edward Swartz
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
*/
package v9t9.common.demos;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DemoHeader {
/** original Java format */
public static final byte[] DEMO_MAGIC_HEADER_V9t9 = { 'V','9','7','0' };
private byte[] magic;
// ASCIIZ string
private String machineModel;
// ASCIIZ string
private String description;
private long timestamp = System.currentTimeMillis();
private int timerRate = 100;
// as ID byte followed by ASCIIZ string;
// ID of 0 terminates list
private Map<Integer, String> bufferIdentifiers = new HashMap<Integer, String>();
// as ID byte followed by ASCIIZ string;
// ID of 0 terminates list
private Map<String, List<String>> streamIdentifiers = new HashMap<String, List<String>>();
/**
*
*/
public DemoHeader(byte[] magic) {
this.magic = magic;
}
public String getMachineModel() {
return machineModel;
}
public void setMachineModel(String machineModel) {
this.machineModel = machineModel;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public int getTimerRate() {
return timerRate;
}
public void setTimerRate(int timerRate) {
this.timerRate = timerRate;
}
public Map<Integer, String> getBufferIdentifierMap() {
return bufferIdentifiers;
}
public Map<String, List<String>> getStreamIdentifierMap() {
return streamIdentifiers;
}
public void read(InputStream is) throws IOException {
if (is.read() != 0x7f) {
throw new IOException("unexpected format: wanted 0x80 or 0x7f");
}
setMachineModel(readString(is));
// description
setDescription(readString(is));
// timestamp
long time = 0;
for (int i = 0; i < 8; i++) {
int byt = is.read();
if (byt < 0)
throw new EOFException();
time |= byt << (64 - i * 8 - 8);
}
setTimestamp(time);
// timer rate (ticks per sec)
int rate = is.read();
if (rate < 0)
throw new EOFException();
setTimerRate(rate);
// read TOC
readTOC(is);
}
private void readTOC(InputStream is) throws IOException {
int id;
while ((id = is.read()) != 0) {
if (id < 0)
throw new EOFException();
String idString = readString(is);
if (bufferIdentifiers.put(id, idString) != null) {
throw new IOException("ID " + id + " is registered more than once");
}
}
}
private String readString(InputStream is) throws IOException {
StringBuilder sb = new StringBuilder();
int ch;
while ((ch = is.read()) > 0) {
sb.append((char) ch);
}
if (ch != 0)
throw new EOFException();
return sb.toString();
}
public void write(OutputStream os) throws IOException {
// machine ID token
if (bufferIdentifiers.isEmpty())
os.write(0x7f);
os.write(0x7f);
// machine identifier
writeString(os, machineModel);
// description
writeString(os, description);
// timestamp
long time = getTimestamp();
for (int i = 0; i < 8; i++) {
os.write((int) (time >>> (64 - i * 8 - 8)));
}
// timer rate (ticks per sec)
os.write(getTimerRate());
// write TOC
for (Map.Entry<Integer, String> entry : bufferIdentifiers.entrySet()) {
if ((entry.getKey() & 0xff) != entry.getKey())
throw new IOException("invalid buffer identifier: " + entry.getKey());
os.write(entry.getKey());
writeString(os, entry.getValue());
}
os.write(0);
}
private void writeString(OutputStream os, String str) throws IOException {
if (str != null)
os.write(str.getBytes());
os.write(0);
}
/**
* Find code for identifier, or allocate one.
* @param identifier string
* @return code
* @throws IOException
*/
public int findOrAllocateIdentifier(String id) throws IOException {
int max = 1;
for (Map.Entry<Integer, String> ent : bufferIdentifiers.entrySet()) {
if (ent.getValue().equals(id)) {
return ent.getKey();
}
max = Math.max(max, ent.getKey() + 1);
}
if (max >= 256)
throw new IOException("no identifier space left for " + id);
bufferIdentifiers.put(max, id);
return max;
}
/**
* @return
*/
public byte[] getMagic() {
return magic;
}
/**
* Tell if the magic is one of the V9t9j formats
* @param magic
* @return
*/
public static boolean isV9t9jFormat(byte[] magic) {
return Arrays.equals(magic, DEMO_MAGIC_HEADER_V9t9);
}
}