package com.pointcliki.dizgruntled.rez;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import org.newdawn.slick.Image;
import org.newdawn.slick.geom.Vector2f;
import com.pointcliki.dizgruntled.GruntzGame;
import com.pointcliki.dizgruntled.Logic;
import com.pointcliki.dizgruntled.LogicFactory;
/**
* This class represents and parses a WapWorld level.
*
* @author Hugheth
* @since alpha 2.5
*/
public class MonolithWWD {
protected MonolithFile fFile;
protected Image fImage;
protected Vector2f fOffset;
protected int fWidth;
protected int fHeight;
protected int fWorld;
protected int[] fTiles;
protected Logic[] fLogics;
/**
* Create a new WWD representation
* @param file The monolith file that contains the WWD file's data
*/
public MonolithWWD(MonolithFile file) {
fFile = file;
// Get the world i.e. Rocky Roads
fWorld = file.data()[469] - 48;
// Get the uncompressed raw data to parse tiles and logics
byte[] s = raw();
// Get the width and height of the map, in tiles
fWidth = MonolithResource.readInteger(s, 96);
fHeight = MonolithResource.readInteger(s, 100);
/*
FileOutputStream w;
try {
w = new FileOutputStream("out.txt");
w.write(s);
w.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
*/
int n = 160;
// If we are in a map with a scrolling background, skip past the first
// tile group as this draws the background
if (MonolithResource.readByte(s, 16) == 66) {
int area2 = fWidth * fHeight;
fWidth = MonolithResource.readInteger(s, 256);
fHeight = MonolithResource.readInteger(s, 260);
n = 320 + area2 * 4;
}
// Get the area of the map
int area = fWidth * fHeight;
fTiles = new int[area];
// Read the tile data
for (int i = 0; i < area; i++) {
fTiles[i] = MonolithResource.readInteger(s, n);
n += 4;
}
// Store the logics
ArrayList<Logic> logics = new ArrayList<Logic>();
// Create a logic factory to handle the creation of in-game logics
LogicFactory factory = new LogicFactory();
int l = s.length;
// Skip to the start of the logics
n += 7;
// If we have a scrolling background we need to skip some more
if (MonolithResource.readInteger(s, n) == 1313818964) n += 5;
// Read logics
try {
while (n + 284 < l) {
// Data of the logic
int dataOffset = n;
// String lengths
int nameLength = MonolithResource.readInteger(s, n + 4);
int logicLength = MonolithResource.readInteger(s, n + 8);
int graphicLength = MonolithResource.readInteger(s, n + 12);
int aniLength = MonolithResource.readInteger(s, n + 16);
if (logicLength == 0) break;
// Skip to name / image / animation of the logic
n += 284;
// Get the logic, graphic file and animation/sound file if there is one
n += nameLength;
String logicRef = new String(Arrays.copyOfRange(s, n, n + logicLength));
n += logicLength;
String graphicRef = new String(Arrays.copyOfRange(s, n, n + graphicLength)).replace('_', '/');
n += graphicLength;
String aniRef = new String(Arrays.copyOfRange(s, n, n + aniLength)).replace('_', '/');
n += aniLength;
if (!logicRef.matches("[A-Za-z]+")) break;
// Map to correct world if a level reference
if (graphicRef.startsWith("LEVEL")) graphicRef = "AREA" + fWorld + "/IMAGEZ" + graphicRef.substring(5);
else if (graphicRef.length() > 0) graphicRef = "GAME/IMAGEZ" + graphicRef.substring(4);
if (aniRef.startsWith("LEVEL")) aniRef = "AREA" + fWorld + "/ANIZ" + aniRef.substring(5);
else if (aniRef.length() > 0) aniRef = "GAME/ANIZ" + aniRef.substring(4);
// For some reason, ambient files are sometimes incorrectly called "AREAXLOOP" rather than "AMBIENTXLOOP"
if (GruntzGame.resourceManager().rez().file(aniRef, "ani") == null) aniRef = aniRef.replace("AREA", "AMBIENT");
// Check that the string exists
Logic log = factory.createFromWWD(logicRef, graphicRef, aniRef, Arrays.copyOfRange(s, dataOffset, dataOffset + 284));
if (log != null) logics.add(log);
else if (!logicRef.equals("GiantRock")) System.err.println("Failed to find controller for logic " + logicRef);
}
} catch (Exception e) {
// Finish parsing errors if an exception occurs
}
fLogics = logics.toArray(new Logic[logics.size()]);
// Print out parse count
System.out.println("Loaded WWD with " + logics.size() + " logics");
}
/**
* This method decompresses the compressed part of the file
* that stores all the tile and logic information of the map.
*
* @return The decompressed data
*/
public byte[] raw() {
byte[] b = Arrays.copyOfRange(fFile.data(), 1524, fFile.data().length - 1);
// WWD uses the deflate algorithm, so use an inflater
Inflater d = new Inflater();
d.setInput(b, 0, b.length);
ByteArrayOutputStream output = new ByteArrayOutputStream(1024);
try {
int count = 1;
byte[] out = new byte[1024];
while (!d.finished() && count > 0) {
count = d.inflate(out);
output.write(out, 0, count);
}
output.close();
return output.toByteArray();
} catch (DataFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public int width() {
return fWidth;
}
public int height() {
return fHeight;
}
public int tile(int i) {
return fTiles[i];
}
public int world() {
return fWorld;
}
public Logic[] logics() {
return fLogics;
}
public static int readID(byte[] data) {
return MonolithResource.readInteger(data, 0);
}
public static Vector2f readPosition(byte[] data) {
return new Vector2f(MonolithResource.readInteger(data, 20), MonolithResource.readInteger(data, 24));
}
public static int readPowerup(byte[] data) {
return MonolithResource.readInteger(data, 60);
}
public static int readPoints(byte[] data) {
return MonolithResource.readInteger(data, 56);
}
public static int readSmarts(byte[] data) {
return MonolithResource.readInteger(data, 68);
}
public static int readSpeed(byte[] data) {
return MonolithResource.readInteger(data, 240);
}
public static int readRect(byte[] data, int n) {
return MonolithResource.readInteger(data, 76 + n * 4);
}
public static int readDamage(byte[] data) {
return MonolithResource.readInteger(data, 64);
}
public static int readHealth(byte[] data) {
return MonolithResource.readInteger(data, 72);
}
}