/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* 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.badlogic.gdx.graphics.g3d.loaders.g3d.chunks;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.GdxRuntimeException;
import static com.badlogic.gdx.graphics.g3d.loaders.g3d.G3dConstants.*;
public class ChunkReader {
public static class Chunk {
int id;
Chunk parent;
Array<Chunk> children = new Array<Chunk>();
ByteArrayInputStream payload;
byte[] payloadBytes;
int offset = 0;
CountingDataInputStream in;
protected Chunk(int id, Chunk parent, byte[] bytes, int offset, int size) throws IOException {
this.id = id;
this.parent = parent;
this.payload = new ByteArrayInputStream(bytes, offset, size);
this.payloadBytes = bytes;
this.offset = offset;
this.in = new CountingDataInputStream(payload);
}
public int getId() {
return id;
}
public Chunk getParent() {
return parent;
}
public Array<Chunk> getChildren() {
return children;
}
public int readByte() {
try {
return in.readByte();
} catch (IOException e) {
throw new GdxRuntimeException("Couldn't read payload, " + e.getMessage(), e);
}
}
public short readShort() {
try {
return in.readShort();
} catch (IOException e) {
throw new GdxRuntimeException("Couldn't read payload, " + e.getMessage(), e);
}
}
public int readInt() {
try {
return in.readInt();
} catch (IOException e) {
throw new GdxRuntimeException("Couldn't read payload, " + e.getMessage(), e);
}
}
public long readLong() {
try {
return in.readLong();
} catch (IOException e) {
throw new GdxRuntimeException("Couldn't read payload, " + e.getMessage(), e);
}
}
public float readFloat() {
try {
return in.readFloat();
} catch (IOException e) {
throw new GdxRuntimeException("Couldn't read payload, " + e.getMessage(), e);
}
}
public double readDouble() {
try {
return in.readDouble();
} catch (IOException e) {
throw new GdxRuntimeException("Couldn't read payload, " + e.getMessage(), e);
}
}
public byte[] readBytes() {
try {
int len = in.readInt();
byte[] v = new byte[len];
for (int i = 0; i < len; i++) {
v[i] = in.readByte();
}
return v;
} catch (IOException e) {
throw new GdxRuntimeException("Couldn't read payload, " + e.getMessage(), e);
}
}
public short[] readShorts() {
try {
int len = in.readInt();
short[] v = new short[len];
for (int i = 0; i < len; i++) {
v[i] = in.readShort();
}
return v;
} catch (IOException e) {
throw new GdxRuntimeException("Couldn't read payload, " + e.getMessage(), e);
}
}
public int[] readInts() {
try {
int len = in.readInt();
int[] v = new int[len];
for (int i = 0; i < len; i++) {
v[i] = in.readInt();
}
return v;
} catch (IOException e) {
throw new GdxRuntimeException("Couldn't read payload, " + e.getMessage(), e);
}
}
public long[] readLongs() {
try {
int len = in.readInt();
long[] v = new long[len];
for (int i = 0; i < len; i++) {
v[i] = in.readLong();
}
return v;
} catch (IOException e) {
throw new GdxRuntimeException("Couldn't read payload, " + e.getMessage(), e);
}
}
public float[] readFloats() {
try {
int len = in.readInt();
float[] v = new float[len];
for (int i = 0; i < len; i++) {
v[i] = in.readFloat();
}
return v;
} catch (IOException e) {
throw new GdxRuntimeException("Couldn't read payload, " + e.getMessage(), e);
}
}
public double[] readDoubles() {
try {
int len = in.readInt();
double[] v = new double[len];
for (int i = 0; i < len; i++) {
v[i] = in.readDouble();
}
return v;
} catch (IOException e) {
throw new GdxRuntimeException("Couldn't read payload, " + e.getMessage(), e);
}
}
public String readString() {
try {
int len = in.readInt();
byte[] bytes = new byte[len];
in.readFully(bytes);
return new String(bytes, "UTF-8");
} catch (IOException e) {
throw new GdxRuntimeException("Couldn't read payload, " + e.getMessage(), e);
}
}
public Chunk getChild(int id) {
for (int i = 0; i < children.size; i++) {
Chunk child = children.get(i);
if (child.getId() == id)
return child;
}
return null;
}
public Chunk[] getChildren(int id) {
Array<Chunk> meshes = new Array<Chunk>(true, 16, Chunk.class);
for (int i = 0; i < children.size; i++) {
Chunk child = children.get(i);
if (child.getId() == id)
meshes.add(child);
}
meshes.shrink();
return meshes.items;
}
}
public static Chunk readChunks(InputStream in) throws IOException {
return loadChunks(in, 0);
}
private static Chunk loadChunks(InputStream in, int fileSize) throws IOException {
byte[] bytes = readStream(in, fileSize);
CountingDataInputStream din = new CountingDataInputStream(new ByteArrayInputStream(bytes));
return loadChunk(din, bytes);
}
private static Chunk loadChunk(CountingDataInputStream din, byte[] bytes) throws IOException {
int id = din.readInt();
int payloadSize = din.readInt();
int numChildren = din.readInt();
int offset = din.getReadBytes();
din.skipBytes(payloadSize);
Chunk chunk = new Chunk(id, null, bytes, offset, payloadSize);
for (int i = 0; i < numChildren; i++) {
Chunk child = loadChunk(din, bytes);
child.parent = chunk;
chunk.children.add(child);
}
return chunk;
}
private static byte[] readStream(InputStream in, int size) throws IOException {
if (size == 0) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
byte[] buffer = new byte[10 * 1024];
int readBytes = 0;
while ((readBytes = in.read(buffer)) != -1) {
bytes.write(buffer, 0, readBytes);
}
return bytes.toByteArray();
} else {
byte[] bytes = new byte[size];
DataInputStream din = new DataInputStream(in);
din.readFully(bytes);
return bytes;
}
}
/**
* Prints a textual representation of the given Chunk hierarchy
*
* @param chunk
* the hierarchy root {@link Chunk}
*/
public static void printChunks(Chunk chunk) {
printChunks(chunk, 0);
}
private static void printChunks(Chunk chunk, int level) {
String id = null;
String payload = null;
switch (chunk.getId()) {
case G3D_ROOT:
id = "G3D_ROOT";
break;
case VERSION_INFO:
id = "VERSION_INFO";
int major = chunk.readByte();
int minor = chunk.readByte();
payload = rep(" ", level + 1) + "major: " + major + ", minor: " + minor;
break;
case STILL_MODEL:
id = "STILL_MODEL";
int subMeshes = chunk.readInt();
payload = rep(" ", level + 1) + "#submeshes: " + subMeshes;
break;
case STILL_SUBMESH:
id = "STILL_SUBMESH";
payload = rep(" ", level + 1) + "name: " + chunk.readString() + ", primitive type: " + chunk.readInt();
break;
case VERTEX_ATTRIBUTE:
id = "VERTEX_ATTRIBUTE";
int usage = chunk.readInt();
int components = chunk.readInt();
String name = chunk.readString();
payload = rep(" ", level + 1) + "usage: " + usage + ", components: " + components + ", name: " + name;
break;
case VERTEX_ATTRIBUTES:
id = "VERTEX_ATTRIBUTES";
int numAttributes = chunk.readInt();
payload = rep(" ", level + 1) + "#attributes: " + numAttributes;
break;
case VERTEX_LIST:
id = "VERTEX_LIST";
int numVertices = chunk.readInt();
float[] vertices = chunk.readFloats();
payload = rep(" ", level + 1) + "#vertices: " + numVertices + ": "
+ Arrays.toString(vertices).substring(0, 400);
break;
case INDEX_LIST:
id = "INDEX_LIST";
int numIndices = chunk.readInt();
short[] indices = chunk.readShorts();
payload = rep(" ", level + 1) + "#indices: " + numIndices + ": "
+ Arrays.toString(indices).substring(0, 400);
break;
default:
id = "unknown [" + id + "]";
payload = rep(" ", level + 1) + "unknown";
break;
}
System.out.println(rep(" ", level) + id + " {");
if (payload != null)
System.out.println(payload);
for (Chunk child : chunk.getChildren()) {
printChunks(child, level + 1);
}
System.out.println(rep(" ", level) + "}");
}
private static String rep(String c, int n) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < n; i++)
buf.append(c);
return buf.toString();
}
}