Author Topic: [Java] Loading and useing BNI files.  (Read 4581 times)

0 Members and 1 Guest are viewing this topic.

Offline Hdx

  • The Hdx!
  • Full Member
  • ***
  • Posts: 311
  • <3 Java/Cpp/VB/QB
    • View Profile
[Java] Loading and useing BNI files.
« on: May 01, 2007, 11:30:29 pm »
Code: [Select]
/*
 * 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.

Offline iago

  • Leader
  • Administrator
  • Hero Member
  • *****
  • Posts: 17914
  • Fnord.
    • View Profile
    • SkullSecurity
Re: [Java] Loading and useing BNI files.
« Reply #1 on: May 01, 2007, 11:59:30 pm »
Nice, I wish I was still working on a bot! :)

Offline Joe

  • B&
  • x86
  • Hero Member
  • *****
  • Posts: 10319
  • In Soviet Russia, text read you!
    • View Profile
    • Github
Re: [Java] Loading and useing BNI files.
« Reply #2 on: May 02, 2007, 12:18:44 am »
* Joe[x86/64] tacks private class Buffer on the end. ^_^
I'd personally do as Joe suggests

You might be right about that, Joe.


Offline MyndFyre

  • Boticulator Extraordinaire
  • x86
  • Hero Member
  • *****
  • Posts: 4540
  • The wait is over.
    • View Profile
    • JinxBot :: the evolution in boticulation
Re: [Java] Loading and useing BNI files.
« Reply #3 on: May 02, 2007, 12:08:01 pm »
Who found you the TGA parsing logic?  HRM?
I have a programming folder, and I have nothing of value there

Running with Code has a new home!

Our species really annoys me.

Offline Hdx

  • The Hdx!
  • Full Member
  • ***
  • Posts: 311
  • <3 Java/Cpp/VB/QB
    • View Profile
Re: [Java] Loading and useing BNI files.
« Reply #4 on: May 02, 2007, 09:09:18 pm »
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.