// Near Infinity - An Infinity Engine Browser and Editor
// Copyright (C) 2001 - 2005 Jon Olav Hauglid
// See LICENSE.txt for license information
package org.infinity.resource.graphics;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.nio.ByteBuffer;
import org.infinity.resource.ResourceFactory;
import org.infinity.resource.key.ResourceEntry;
/**
* Handles new PVRZ-based TIS resources.
*/
public class TisV2Decoder extends TisDecoder
{
private static final int HeaderSize = 24; // Size of the TIS header
private ByteBuffer tisBuffer;
private int tileCount, tileSize;
private String pvrzNameBase;
private BufferedImage workingCanvas;
public TisV2Decoder(ResourceEntry tisEntry)
{
super(tisEntry);
init();
}
/**
* Returns the base filename of the PVRZ resource (without page suffix and extension).
* @return Base filename of the PVRZ resource related to this TIS resource.
*/
public String getPvrzFileBase()
{
return pvrzNameBase;
}
/**
* Returns the page index of the PVRZ resource containing the graphics data of the specified tile.
* @param tileIdx The tile index.
* @return A page index that can be used to determine the PVRZ resource that contains the graphics
* data of the specified tile. Returns -1 on error.
*/
public int getPvrzPage(int tileIdx)
{
int ofs = getTileOffset(tileIdx);
if (ofs > 0) {
return tisBuffer.getInt(ofs);
} else {
return -1;
}
}
/**
* Returns the full PVRZ resource filename that contains the graphics data of the specified tile.
* @param tileIdx The tile index
* @return Full PVRZ resource filename with page and extension. Returns an empty string on error.
*/
public String getPvrzFileName(int tileIdx)
{
int page = getPvrzPage(tileIdx);
if (page >= 0) {
return String.format("%1$s%2$02d.PVRZ", getPvrzFileBase(), page);
} else {
return "";
}
}
@Override
public void close()
{
PvrDecoder.flushCache();
tisBuffer = null;
tileCount = 0;
tileSize = 0;
pvrzNameBase = "";
if (workingCanvas != null) {
workingCanvas.flush();
workingCanvas = null;
}
}
@Override
public void reload()
{
init();
}
@Override
public ByteBuffer getResourceBuffer()
{
return tisBuffer;
}
@Override
public int getTileWidth()
{
return TileDimension;
}
@Override
public int getTileHeight()
{
return TileDimension;
}
@Override
public int getTileCount()
{
return tileCount;
}
@Override
public Image getTile(int tileIdx)
{
BufferedImage image = ColorConvert.createCompatibleImage(TileDimension, TileDimension, true);
renderTile(tileIdx, image);
return image;
}
@Override
public boolean getTile(int tileIdx, Image canvas)
{
return renderTile(tileIdx, canvas);
}
@Override
public int[] getTileData(int tileIdx)
{
int[] buffer = new int[TileDimension*TileDimension];
renderTile(tileIdx, buffer);
return buffer;
}
@Override
public boolean getTileData(int tileIdx, int[] buffer)
{
return renderTile(tileIdx, buffer);
}
private void init()
{
close();
if (getResourceEntry() != null) {
try {
int[] info = getResourceEntry().getResourceInfo();
if (info == null || info.length < 2) {
throw new Exception("Error reading TIS header");
}
tileCount = info[0];
if (tileCount <= 0) {
throw new Exception("Invalid tile count: " + tileCount);
}
tileSize = info[1];
if (tileSize != 12) {
throw new Exception("Invalid tile size: " + tileSize);
}
tisBuffer = getResourceEntry().getResourceBuffer();
String name = getResourceEntry().getResourceName();
int idx = name.lastIndexOf('.');
if (idx < 0) idx = name.length();
pvrzNameBase = getResourceEntry().getResourceName().substring(0, 1) +
getResourceEntry().getResourceName().substring(2, idx);
setType(Type.PVRZ);
workingCanvas = new BufferedImage(TileDimension, TileDimension, BufferedImage.TYPE_INT_ARGB);
} catch (Exception e) {
e.printStackTrace();
close();
}
}
}
// Returns and caches the PVRZ resource of the specified page
private PvrDecoder getPVR(int page)
{
try {
String name = String.format("%1$s%2$02d.PVRZ", pvrzNameBase, page);
ResourceEntry entry = ResourceFactory.getResourceEntry(name);
if (entry != null) {
return PvrDecoder.loadPvr(entry);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// Returns the start offset of the specified tile. Returns -1 on error.
private int getTileOffset(int tileIdx)
{
if (tileIdx >= 0 && tileIdx < getTileCount()) {
return HeaderSize + tileIdx*tileSize;
} else {
return -1;
}
}
// Paints the specified tile onto the canvas
private boolean renderTile(int tileIdx, Image canvas)
{
if (canvas != null && canvas.getWidth(null) >= TileDimension && canvas.getHeight(null) >= TileDimension) {
if (updateWorkingCanvas(tileIdx)) {
Graphics2D g = (Graphics2D)canvas.getGraphics();
try {
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
g.drawImage(workingCanvas, 0, 0, null);
} finally {
g.dispose();
g = null;
}
return true;
}
}
return false;
}
// Writes the specified tile data into the buffer
private boolean renderTile(int tileIdx, int[] buffer)
{
int size = TileDimension*TileDimension;
if (buffer != null && buffer.length >= size) {
if (updateWorkingCanvas(tileIdx)) {
int[] src = ((DataBufferInt)workingCanvas.getRaster().getDataBuffer()).getData();
System.arraycopy(src, 0, buffer, 0, size);
src = null;
return true;
}
}
return false;
}
// Draws the specified tile into the working canvas and returns the success state.
private boolean updateWorkingCanvas(int tileIdx)
{
int ofs = getTileOffset(tileIdx);
if (ofs > 0) {
int page = tisBuffer.getInt(ofs);
int x = tisBuffer.getInt(ofs+4);
int y = tisBuffer.getInt(ofs+8);
PvrDecoder decoder = getPVR(page);
if (decoder != null || page == -1) {
// removing old content
try {
if (page == -1) {
Graphics2D g = workingCanvas.createGraphics();
try {
g.setColor(Color.BLACK);
g.fillRect(0, 0, TileDimension, TileDimension);
} finally {
g.dispose();
g = null;
}
} else {
if (decoder != null) {
// drawing new content
decoder.decode(workingCanvas, x, y, TileDimension, TileDimension);
decoder = null;
}
}
return true;
} catch (Exception e) {
e.printStackTrace();
decoder = null;
}
}
}
return false;
}
}