News:

Happy New Year! Yes, the current one, not a previous one; this is a new post, we swear!

Main Menu

[Java] Loading and useing BNI files.

Started by Hdx, May 01, 2007, 11:30:29 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Hdx

/*
* 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
http://img140.exs.cx/img140/6720/hdxnew6lb.gif
09/08/05 - Clan SBs @ USEast
[19:59:04.000] <DeadHelp> We don't like customers.
[19:59:05.922] <DeadHelp> They're assholes
[19:59:08.094] <DeadHelp> And they're never right.

iago

Nice, I wish I was still working on a bot! :)

Joe

* Joe[x86/64] tacks private class Buffer on the end. ^_^
Quote from: Camel on June 09, 2009, 04:12:23 PMI'd personally do as Joe suggests

Quote from: AntiVirus on October 19, 2010, 02:36:52 PM
You might be right about that, Joe.


MyndFyre

Who found you the TGA parsing logic?  HRM?
Quote from: Joe on January 23, 2011, 11:47:54 PM
I have a programming folder, and I have nothing of value there

Running with Code has a new home!

Quote from: Rule on May 26, 2009, 02:02:12 PMOur species really annoys me.

Hdx

Google did.
I will also be adding support for non-10 tga format. But thats later this week when i get some free time.
~Hdx
http://img140.exs.cx/img140/6720/hdxnew6lb.gif
09/08/05 - Clan SBs @ USEast
[19:59:04.000] <DeadHelp> We don't like customers.
[19:59:05.922] <DeadHelp> They're assholes
[19:59:08.094] <DeadHelp> And they're never right.