/*
* Author: Hdx
* Date: 05-01-07
*/
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
//iago's buffer class
import util.Buffer;
public class bni{
private ArrayList iconlist = new ArrayList();
public boolean loadBNI(String file){
Buffer fileData = new Buffer();
try{
/* Just read out the file, this will throw an error if the file is unreadble (Not found, locked, etc) */
FileInputStream fis = new FileInputStream(file);
int i = fis.read();
while(i != -1){
fileData.addByte((byte)i);
i = fis.read();
}
fis.close();
/* BNI Header format:
* (DWORD) HeaderLength (always 0x10) - Length of this BNI header
* (WORD) BNI Version (Only 0x01 is supported)
* (WORD) Unused (always 0x00)
* (DWORD) NumIcons - Number of icons
* (DWORD) DataStart - Length of the BNI file header, and start position of TGA file
*/
int headLen = fileData.removeDWord();
int ver = fileData.removeWord();
fileData.removeWord();
int imagecount = fileData.removeDWord();
int datastart = fileData.removeDWord();
/*
System.out.println("Header length: 0x" + Integer.toHexString(headLen));
System.out.println("BNI Version: 0x" + Integer.toHexString(ver));
System.out.println("Images: " + imagecount);
System.out.println("Start position: 0x" + Integer.toHexString(datastart));
*/
Icon[] icons = new Icon[imagecount];
/* Image info format:
* (DWORD) FlagValue - User's flags must match this value after a BitwiseAnd of the two for this icon to be valid
* (DWORD) IconWidth - Icon's width
* (DWORD) IconHeight - Icon's height
* (DWORD-List) Software - User's software must be one of these values for this icon to be valid. (Real games support up to 32 values for this field)
*/
for(int x = 0; x < imagecount; x++){
int flags = fileData.removeDWord();
int width = fileData.removeDWord();
int height = fileData.removeDWord();
int[] prods = new int[32];
int z = 0;
i = fileData.removeDWord();
while(i != 0){
prods[z++] = i;
i = fileData.removeDWord();
}
icons[x] = new Icon(flags, width, height, prods);
//System.out.println(icons[x].toString());
}
BufferedImage fullicons = tgaToImage(fileData); //This takes in a TGA compressed image and returns a BufferedImage to be split up.
if(fullicons == null) return false;
int currentX = 0; int currentY = 0;
for(int x = 0; x < icons.length; x++){
if(currentX + icons[x].getWidth() > ((Image)fullicons).getWidth(null)){ //If this image wont fit, move down (Possile bug if all images are not the same height)
currentX = 0;
currentY += icons[x].getHeight();
}
Image icon = fullicons.getSubimage(currentX, currentY, icons[x].getWidth(), icons[x].getHeight()); //Split em up yo!
icons[x].setIcon(icon);
iconlist.add(icon);
currentX += icons[x].getWidth();
}
return true;
}catch(Exception e){
e.printStackTrace();
}
return false;
}
/* GetIcon(Flags, Product)
* This will go through all the current icons you have loaded,
* and pick out the appopriate one for this information.
* Note:
* Order of selection is in the order they were loaded form file.
* This means, an icon takes presidence over any icon loaded after it.
* So load your custom bni first if you want it to override the real ones. :P
*
* This returns the icon in an Image object, If there is no suitible icon, null is returned.
*/
public Image getIcon(int flags, int product){
Object[] images = iconlist.toArray();
if(flags != 0){ //Check all icons that have flags 1st.
for(int x = 0; x < images.length; x++){
int iconflags = ((Icon)images[x]).getFlags();
if((flags & iconflags) == iconflags && (iconflags != 0)) return ((Icon)images[x]).getIcon();
}
}
for(int x = 0; x < images.length; x++){//Now if an icon was not found, check against each product list.
int[] iconproducts = ((Icon)images[x]).getProducts();
for(int i = 0; i < iconproducts.length; i++)
if(iconproducts[i] == product) return ((Icon)images[x]).getIcon();
}
return null;
}
/* This is a simple class to hold the data for each Icon we get. Pretty self explnitory */
private class Icon{
private int flags = 0;
private int width = 0;
private int height = 0;
private int[] prods = null;
private Image icon = null;
Icon(int f, int w, int h, int[] p){
flags = f;
width = w;
height = h;
prods = p;
}
public int getWidth(){ return width; }
public int getHeight(){ return height; }
public int getFlags(){ return flags; }
public int[] getProducts(){ return prods; }
public Image getIcon(){ return icon; }
public void setIcon(Image i){ this.icon = i; }
public String toString(){
StringBuffer buff = new StringBuffer();
buff.append("Flags: 0x");
buff.append(Integer.toHexString(flags));
buff.append(", Size: ");
buff.append(width);
buff.append("x");
buff.append(height);
buff.append(", Prods:");
for(int x = 0; x < prods.length; x++){
if(prods[x] != 0){
buff.append(" 0x");
buff.append(Integer.toHexString(prods[x]));
}
}
return buff.toString();
}
}
/* togToImage(Buffer)
* This will create a BufferedImage out of the raw TGA data.
* Currently, only supports format 10 in the TGA standards.
* http://local.wasp.uwa.edu.au/%7Epbourke/dataformats/tga/
*/
private BufferedImage tgaToImage(Buffer data){
try{
byte infoLen = data.removeByte();
byte colorMapType = data.removeByte();
byte imageType = data.removeByte();
short colorMapOrigin = data.removeWord();
short colorMapLength = data.removeWord();
byte colorMapEntrySize = data.removeByte();
short startX = data.removeWord();
short startY = data.removeWord();
short width = data.removeWord();
short height = data.removeWord();
byte depth = data.removeByte();
byte descriptor = data.removeByte();
for(int x = 1; x < infoLen; x++) data.removeByte();
if(colorMapType != 0){
while(colorMapEntrySize > 0){
data.removeByte();
colorMapEntrySize -= 4;
}
}
boolean topdown = ((descriptor & 0x20) == 0x20 ? true : false); //There are two ways the image could start, top left, or bottom left
/* System.out.println("Start: " + startX + "x" + startY + ", " +
"Size: " + width + "x" + height + ", " +
"Info Length: " + infoLen + ", " +
"Type: " + imageType + ", " +
"Depth: " + depth + ", " +
"Descriptor: " + descriptor + ", " +
"Color Map Type: " + colorMapType + ", " +
"Color map Orign: " + colorMapOrigin + ", " +
"Color Map Length: " + colorMapLength + ", " +
"Color Map Entry Size: " + colorMapEntrySize + ", " +
"Top Down: " + topdown);
*/
BufferedImage bimage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = bimage.getGraphics();
int pixlesPainted = 0;
int position = 0;
while(pixlesPainted < (width*height)){
int header = data.removeByte();
if(header < 0){ //Repeat the same value
header &= 127;
header++;
int blue = (data.removeByte()+256)%256;
int green = (data.removeByte()+256)%256;
int red = (data.removeByte()+256)%256;
g.setColor(new Color(red, green, blue));
for(int x = 0; x < header; x++){
int w = (int)(pixlesPainted % width);
int h = (int)(pixlesPainted / width);
if(topdown)
g.fillRect(w, h, 1, 1);
else
g.fillRect(w, height-h-1, 1, 1);
pixlesPainted++;
}
}else{//Raw pixle info
header &= 127;
header++;
for(int x = 0; x < header; x++){
int blue = (data.removeByte()+256)%256; // Stupid java signed bytes -.-
int green = (data.removeByte()+256)%256;
int red = (data.removeByte()+256)%256;
int w = (int)(pixlesPainted % width);
int h = (int)(pixlesPainted / width);
g.setColor(new Color(red, green, blue));
if(topdown)
g.fillRect(w, h, 1, 1);
else
g.fillRect(w, height-h-1, 1, 1);
pixlesPainted++;
}
}
}
return bimage;
}catch(IndexOutOfBoundsException e){
return null;
}catch(Exception e){
e.printStackTrace();
}
return null;
}
}
Comments?
There is one non-java dependency, And that would be iago's Buffer class.
~Hdx