JMir——Java版熱血傳奇2之資源文件與地圖

  我雖然是90後,可是也很喜歡熱血傳奇2(如下簡稱「傳奇」)這款遊戲。java

  進入程序員行業後本身也對傳奇客戶端實現有所研究,如今將個人一些研究結果展現出來,若是你們有興趣的話不妨與我交流。git

  

  項目我託管到codeplex上了,使用GPLv2開源協議。你們能夠checkout代碼出來看。程序員

  我如今將地圖加載出來了,算是達到了里程碑1吧。github

  

  若是要將傳奇的地圖和資源文件詳細解析可能我得寫上幾萬字,不過我如今愈來愈懶了,就只將讀取wix、wil、map文件的方法和它們的解析貼出來吧。編程

  準備工做:數組

    熱血傳奇十週年客戶端網絡

    JDK7數據結構

    Eclipsedom

  

  注意:編程語言

    閱讀此篇文章後您將不須要再到網絡上搜索傳奇資源文件和地圖文件解析,由於個人隨筆絕對是最全最完整最詳細的!但這可能須要您花費一些耐心。

  

  第一部分——地圖:

    第一節——描述:

      Q: Tile是什麼?

      A: Tile在中文是「瓷磚」、「塊」的意思,具體到傳奇地圖中就是48*32屏幕像素大小的矩形區域。單個傳奇地圖就是由多個Tile構成的。

      Q: map格式文件究竟存放了哪些信息?

      A: map格式文件保存了一個完成地圖的全部信息,可是對於當前Tile的圖片只是保存了一個索引而不是把圖片色彩數據保存下來。

      Q: map格式文件怎樣讀取?

      A: 對於文件讀取以及對應到Java語言中的數據類型和數據結構咱們要從兩方面考慮。

        一是map的數據內容:

          map文件分爲兩部分。一個文件頭標識了當前地圖的高度、寬度等重要信息;剩餘部分則是多個Tile的詳細信息

        二是map格式文件是由Object-Pascal(如下簡稱Delphi)語言序列化而成的,咱們首先須要瞭解從Delphi序列化的數據到Java反序列化須要進行的操做。

      以上內容代表了地圖的信息,熱血傳奇中地圖由Tile構成,每一個Tile對應48*32屏幕像素大小。

      .map文件則保存了地圖的寬度、高度以及每一個Tile的詳細信息。

    第二節——對應:

      .map文件若是對應到編程語言中數據結構的話在Delphi中以下(文件頭):

1 TMapHeader = packed record 
2  wWidth: Word; 
3  wHeight :Word; 
4  sTitle :String[16]; 
5  UpdateDate :TDateTime; 
6  Reserved :array[0..22] of Char; 

      (Tile,兩種均可以):

 1 type 
 2  TMapInfo = packed record 
 3  wBkImg :Word; 
 4  wMidImg :Word; 
 5  wFrImg :Word; 
 6  btDoorIndex :Byte; 
 7  btDoorOffset :Byte; 
 8  btAniFrame :Byte; 
 9  btAniTick :Byte; 
10  btArea :Byte; 
11  btLight :Byte; 
12 
13 type 
14  TMapInfo = packed record 
15  wBigTileImg :Word; 
16  wSmTileImg :Word; 
17  wObjImg :Word; 
18  btDoorIndex :Byte; 
19  btDoorOffset :Byte; 
20  btAniFrame :Byte; 
21  btAniTick :Byte; 
22  btObjFile :Byte; 
23  btLight :Byte; 
MapTile

      每一個.map文件若是在Delphi中就成了一個TMapHeader加wWidth*wHeight個MapTile。

      (對於每一個字段佔用的字節數請查看下面Java代碼中註釋)

  

      因爲咱們是使用Java語言描述熱血傳奇地圖,因此我針對上述兩個數據結構使用Java語言進行了描述:

  1 package org.coderecord.jmir.entt.internal;
  2 
  3 import java.util.Date;
  4 
  5 /**
  6  * 熱血傳奇2地圖文件頭
  7  * <p>
  8  * 針對*.map文件的數據結構使用Java語言描述
  9  * <br>
 10  * 地圖文件頭爲52字節,在Pascal中定義爲
 11  * <br>
 12  * &nbsp;TMapHeader = packed record
 13  * <br>
 14  * &emsp;wWidth:&emsp;Word;
 15  * <br>
 16  * &emsp;wHeight&emsp;:Word;
 17  * <br>
 18  * &emsp;sTitle&emsp;:String[16];
 19  * <br>
 20  * &emsp;UpdateDate&emsp;:TDateTime;
 21  * <br>
 22  * &emsp;Reserved&emsp;:array[0..22] of Char;
 23  * </p>
 24  * <p>
 25  * <b>wWidth</b> 表示地圖寬度(佔用兩個字節,至關於Java語言short;通常不超過1000)
 26  * <br>
 27  * <b>wHeight</b> 表示地圖高度(佔用兩個字節,至關於Java於洋short;通常不超過1000)
 28  * <br>
 29  * <b>sTitle</b> 標題,靜態單字符串(佔用17個字節,首字節爲字符串已使用的長度即已存放的字符數,通常爲「Legend of mir」)
 30  * <br>
 31  * <b>UpdateDate</b> 地圖最後更新時間(佔用8個字節,爲TDateTime類型,可以使用{@link org.coderecord.jmir.kits.Pascal#readDate(byte[], int, boolean) readDate} 轉換爲java.util.Date)
 32  * <br>
 33  * <b>Reserved</b> 保留字符,固定爲23字節
 34  * </p>
 35  * 
 36  * @author ShawRyan
 37  *
 38  */
 39 public class MapHeader {
 40 
 41     /** 地圖寬度(橫向長度) */
 42     private short width;
 43     /** 地圖高度(縱向長度) */
 44     private short height;
 45     /** 標題 */
 46     private String title;
 47     /** 更新日期 */
 48     private Date updateDate;
 49     /** 保留字符 */
 50     private char[] reserved;
 51     
 52     /** 默認構造函數 */
 53     public MapHeader() {}
 54     /** 帶所有參數的構造函數 */
 55     public MapHeader(short width, short height, String title, Date updateDate, char[] reserved) {
 56         this.width = width;
 57         this.height = height;
 58         this.title = title;
 59         this.updateDate = updateDate;
 60         this.reserved = reserved;
 61     }
 62     /** 使用已有對象構造實例 */
 63     public MapHeader(MapHeader mapHeader) {
 64         this.width = mapHeader.getWidth();
 65         this.height = mapHeader.getHeight();
 66         this.title = mapHeader.getTitle();
 67         this.updateDate = mapHeader.getUpdateDate();
 68         this.reserved = mapHeader.getReserved();
 69     }
 70     
 71     /** 獲取地圖寬度(橫向長度) */
 72     public short getWidth() {
 73         return width;
 74     }
 75     /** 設置地圖寬度(橫向長度) */
 76     public void setWidth(short width) {
 77         this.width = width;
 78     }
 79     /** 獲取地圖高度(縱向長度) */
 80     public short getHeight() {
 81         return height;
 82     }
 83     /** 設置地圖高度(縱向長度) */
 84     public void setHeight(short height) {
 85         this.height = height;
 86     }
 87     /** 獲取標題 */
 88     public String getTitle() {
 89         return title;
 90     }
 91     /** 設置標題 */
 92     public void setTitle(String title) {
 93         this.title = title;
 94     }
 95     /** 獲取更新時間 */
 96     public Date getUpdateDate() {
 97         return updateDate;
 98     }
 99     /** 設置更新時間 */
100     public void setUpdateDate(Date updateDate) {
101         this.updateDate = updateDate;
102     }
103     /** 獲取保留字符 */
104     public char[] getReserved() {
105         return reserved;
106     }
107     /** 設置保留字符 */
108     public void setReserved(char[] reserved) {
109         this.reserved = reserved;
110     }
111 }
MapHeader

      (Tile我使用了兩種描述方式,後一種用於生產環境更加優秀):

  1 package org.coderecord.jmir.entt.internal;
  2 
  3 /**
  4  * 熱血傳奇2地圖「塊」
  5  * <br>
  6  * 即 「<b>邏輯座標</b>」點(人物/NPC等放置須要佔用一個邏輯座標點)
  7  * <br>
  8  * 須要注意的是邏輯座標和屏幕座標是不同的,屏幕座標通常爲像素值,根據顯示器分辨率設置而有所不一樣
  9  * <br>
 10  * 熱血傳奇2中一個邏輯座標點(地圖塊)須要佔用 48 * 32 屏幕座標大小
 11  * <br>
 12  * 每一個地圖塊爲2層結構,包括‘地’和‘空’
 13  * 例如樹葉投影下的地圖塊就是2層,包括地表及物體(若有突起石頭的地面或有水流的地面)和樹葉
 14  * <p>
 15  * 在Pascal語言中使用如下數據結構對地圖塊進行描述和存儲(兩種)
 16  * <br>
 17  * type
 18  * <br>
 19  * &nbsp;TMapInfo = packed record
 20  * <br>
 21  * &emsp;wBkImg&emsp;:Word;
 22  * <br>
 23  * &emsp;wMidImg&emsp;:Word;
 24  * <br>
 25  * &emsp;wFrImg&emsp;:Word;
 26  * <br>
 27  * &emsp;btDoorIndex&emsp;:Byte;
 28  * <br>
 29  * &emsp;btDoorOffset&emsp;:Byte;
 30  * <br>
 31  * &emsp;btAniFrame&emsp;:Byte;
 32  * <br>
 33  * &emsp;btAniTick&emsp;:Byte;
 34  * <br>
 35  * &emsp;btArea&emsp;:Byte;
 36  * <br>
 37  * &emsp;btLight&emsp;:Byte;
 38  * </p>
 39  * <p>
 40  * type
 41  * <br>
 42  * &nbsp;TMapInfo = packed record
 43  * <br>
 44  * &emsp;wBigTileImg&emsp;:Word;
 45  * <br>
 46  * &emsp;wSmTileImg&emsp;:Word;
 47  * <br>
 48  * &emsp;wObjImg&emsp;:Word;
 49  * <br>
 50  * &emsp;btDoorIndex&emsp;:Byte;
 51  * <br>
 52  * &emsp;btDoorOffset&emsp;:Byte;
 53  * <br>
 54  * &emsp;btAniFrame&emsp;:Byte;
 55  * <br>
 56  * &emsp;btAniTick&emsp;:Byte;
 57  * <br>
 58  * &emsp;btObjFile&emsp;:Byte;
 59  * <br>
 60  * &emsp;btLight&emsp;:Byte;
 61  * </p>
 62  * <p>
 63  * <b>wBkImg</b>或<b>wBigTileImg</b> 表示地圖地表圖片,若是最高位爲1則表示不能經過(或站立),如河水型地表等。在判斷是否能夠飛過(從空中經過)時則不須要考慮
 64  * <br>
 65  * <b>wMidImg</b>或<b>wSmTileImg</b> 表示地圖可視物體圖片(有時被稱爲可視數據/中間層/小地圖塊/地圖補充背景等等),若是wBkImg(或wBigTileImg)沒有鋪滿則使用此地圖塊進行鋪墊。最高位不做爲判斷依據,不過圖片索引通常小於0x8000,即最高位通常爲0。例如在某地圖中第一個地圖塊的wBkImg(或wBigTileImg)大小爲96 * 64,則表明該地圖左上角4個塊兒的地表都不爲空,此時緊鄰的三個地圖塊均可以不用設置wBkImg(或wBigTileImg)和wMidImg(或wSmTileImg);若是某個地圖塊的沒有被其餘塊兒的wBkImg(或wBigTileImg)鋪滿,本身也沒有wBkImg(或wBigTileImg),那麼它就須要一個wMidImg(或wSmTileImg)進行鋪墊。值得一提的是並不必定在有了wMidImg(或wBigTileImg)後就不須要繪製此層圖片了
 66  * <br>
 67  * <b>wFrImg</b>或<b>wObjImg</b> 表示表層圖片(對象),即空中遮擋物,如植物或建築物,若是最高位爲1則表示不能經過(或站立)。在判斷是否可飛過(從空中經過)時須要做爲惟一條件判斷,在判斷是否能夠徒步經過或站立時須要聯合wBkImg進行判斷
 68  * <br>
 69  * <b>總的來講,地圖通常爲兩層(只是針對上面的三個屬性,下方的也屬於地圖部分,不過先不歸入考慮),包括背景層與對象層,背景層爲wBkImg(或wBigTileImg)和wMidImg(或wSmTileImg)的集合,通常來講wBkImg就能搞定,也有時候須要二者都有;Spirit(人物/怪物/NPC/掉落物品等)在兩層中間;索引從1開始,因此在從資源中真正取圖片時應該減1(適用於全部資源索引);索引通常最高位爲0,爲1通常表示特殊狀況(在Java語言中能夠理解爲大於0,由於首位爲1表示負數)</b>
 70  * <br>
 71  * <b>btDoorIndex</b> 門索引,最高位爲1表示有門,爲0表示沒有門。
 72  * <br>
 73  * <b>btDoorOffset</b> 門偏移,最高位爲1表示門打開了,爲0表示門爲關閉狀態
 74  * <br>
 75  * <b>btAniFrame</b> 幀數,指示當前地圖塊動態內容由多少張靜態圖片輪詢播放,須要和btAniTick一塊兒起做用;若是最高位爲1(即值大於0x80,或者在Java中爲小於0的數值)則表示有動態內容
 76  * <br>
 77  * <b>btAniTick</b> 跳幀數,指示當前地圖塊動態內容應該每隔多少幀變換當前顯示的靜態圖片,須要和btAniFrame一塊兒做用
 78  * <br>
 79  * <b>btAniFrame和btAniTick做用時表達式以下index = (gAniCount % (btAniFrame * (1 + btAniTick))) / (1 + btAniTick)
 80  * <br>
 81  * &emsp;其中gAniCount是當前畫面幀是第幾幀,它會在每次繪製遊戲界面時累加,它能夠有最大值,超過能夠置0;index是相對當前objImgIdx的偏移量,好比當前對象層圖片索引爲1,而AniFrame爲10,則表示從1到11這10副圖片應該做爲一動態內容播放(有待考證)
 82  * </b>
 83  * <br>
 84  * <b>btArea</b>或<b>btObjFile</b> 表示當前wFrImg(或wObjImg)和動態內容構成圖片來自哪一個Object資源文件,具體爲Object{btArea}.wil中,若是btArea爲0則是Objects.wil
 85  * <br>
 86  * <b>btLight</b> 亮度,通常爲0/1/4
 87  * </p>
 88  * @author ShawRyan
 89  *
 90  */
 91 public class MapTile {
 92     
 93     /** 背景圖索引 */
 94     private short bngImgIdx;
 95     /** 補充背景圖索引 */
 96     private short midImgIdx;
 97     /** 對象圖索引 */
 98     private short objImgIdx;
 99     /** 門索引 */
100     private byte doorIdx;
101     /** 門偏移 */
102     private byte doorOffset;
103     /** 動畫幀數 */
104     private byte aniFrame;
105     /** 動畫跳幀數 */
106     private byte aniTick;
107     /** 資源文件索引 */
108     private byte objFileIdx;
109     /** 亮度 */
110     private byte light;
111 
112     /** 默認構造函數 */
113     public MapTile() { }
114     /** 使用已有對象構造實例 */
115     public MapTile(MapTile mapTile) {
116         this.bngImgIdx = mapTile.bngImgIdx;
117         this.midImgIdx = mapTile.midImgIdx;
118         this.objImgIdx = mapTile.objImgIdx;
119         this.doorIdx = mapTile.doorIdx;
120         this.doorOffset = mapTile.doorOffset;
121         this.aniFrame = mapTile.aniFrame;
122         this.aniTick = mapTile.aniTick;
123         this.objFileIdx = mapTile.objFileIdx;
124         this.light = mapTile.light;
125     }
126     /** 帶所有參數的構造函數 */
127     public MapTile(short bngImgIdx, short midImgIdx, short objImgIdx, byte doorIdx, byte doorOffset, byte aniFrame, byte aniTick, byte objFileIdx, byte light) {
128         this.bngImgIdx = bngImgIdx;
129         this.midImgIdx = midImgIdx;
130         this.objImgIdx = objImgIdx;
131         this.doorIdx = doorIdx;
132         this.doorOffset = doorOffset;
133         this.aniFrame = aniFrame;
134         this.aniTick = aniTick;
135         this.objFileIdx = objFileIdx;
136         this.light = light;
137     }
138     
139     /** 獲取背景圖索引 */
140     public short getBngImgIdx() {
141         return bngImgIdx;
142     }
143     /** 設置背景圖索引 */
144     public void setBngImgIdx(short bngImgIdx) {
145         this.bngImgIdx = bngImgIdx;
146     }
147     /** 獲取補充圖索引 */
148     public short getMidImgIdx() {
149         return midImgIdx;
150     }
151     /** 設置補充圖索引 */
152     public void setMidImgIdx(short midImgIdx) {
153         this.midImgIdx = midImgIdx;
154     }
155     /** 獲取對象圖索引 */
156     public short getObjImgIdx() {
157         return objImgIdx;
158     }
159     /** 設置對象圖索引 */
160     public void setObjImgIdx(short objImgIdx) {
161         this.objImgIdx = objImgIdx;
162     }
163     /** 獲取門索引 */
164     public byte getDoorIdx() {
165         return doorIdx;
166     }
167     /** 設置門索引 */
168     public void setDoorIdx(byte doorIdx) {
169         this.doorIdx = doorIdx;
170     }
171     /** 獲取門偏移 */
172     public byte getDoorOffset() {
173         return doorOffset;
174     }
175     /** 設置門偏移 */
176     public void setDoorOffset(byte doorOffset) {
177         this.doorOffset = doorOffset;
178     }
179     /** 獲取動畫幀數 */
180     public byte getAniFrame() {
181         return aniFrame;
182     }
183     /** 設置動畫幀數 */
184     public void setAniFrame(byte aniFrame) {
185         this.aniFrame = aniFrame;
186     }
187     /** 獲取動畫跳幀數 */
188     public byte getAniTick() {
189         return aniTick;
190     }
191     /** 設置動畫跳幀數 */
192     public void setAniTick(byte aniTick) {
193         this.aniTick = aniTick;
194     }
195     /** 獲取資源文件索引 */
196     public byte getObjFileIdx() {
197         return objFileIdx;
198     }
199     /** 設置資源文件索引 */
200     public void setObjFileIdx(byte objFileIdx) {
201         this.objFileIdx = objFileIdx;
202     }
203     /** 獲取亮度 */
204     public byte getLight() {
205         return light;
206     }
207     /** 設置亮度 */
208     public void setLight(byte light) {
209         this.light = light;
210     }
211 }
MapTile
  1 package org.coderecord.jmir.entt.internal;
  2 
  3 import org.coderecord.jmir.scn.DrawSupport;
  4 
  5 /**
  6  * MapTile方便程序邏輯的另類解讀方式
  7  * 
  8  * @author ShawRyan
  9  *
 10  */
 11 public class MapTileInfo {
 12     /** 背景圖索引 */
 13     private short bngImgIdx;
 14     /** 是否有背景圖(在熱血傳奇2地圖中,背景圖大小爲4個地圖塊,具體到繪製地圖時則表如今只有橫縱座標都爲雙數時才繪製),見{@link DrawSupport#drawMap(int, int, org.coderecord.jmir.entt.Map, int, int) drawMap} */
 15     private boolean hasBng;
 16     /** 是否可行走(站立) */
 17     private boolean canWalk;
 18     /** 補充背景圖索引 */
 19     private short midImgIdx;
 20     /** 是否有補充圖 */
 21     private boolean hasMid;
 22     /** 對象圖索引 */
 23     private short objImgIdx;
 24     /** 是否有對象圖 */
 25     private boolean hasObj;
 26     /** 是否能夠飛越 */
 27     private boolean canFly;
 28     /** 門索引 */
 29     private byte doorIdx;
 30     /** 是否有門 */
 31     private boolean hasDoor;
 32     /** 門偏移 */
 33     private byte doorOffset;
 34     /** 門是否開啓 */
 35     private boolean doorOpen;
 36     /** 動畫幀數 */
 37     private byte aniFrame;
 38     /** 是否有動畫 */
 39     private boolean hasAni;
 40     /** 動畫跳幀數 */
 41     private byte aniTick;
 42     /** 資源文件索引 */
 43     private byte objFileIdx;
 44     /** 亮度 */
 45     private byte light;
 46     
 47     /** 無參構造函數 */
 48     public MapTileInfo() { }
 49     /** 帶所有參數的構造函數 */
 50     public MapTileInfo(short bngImgIdx, boolean hasBng, boolean canWalk, short midImgIdx, boolean hasMid, short objImgIdx, boolean hasObj, boolean canFly, byte doorIdx, boolean hasDoor, byte doorOffset, boolean doorOpen, byte aniFrame, boolean hasAni, byte aniTick, byte objFileIdx, byte light) {
 51         this.bngImgIdx = bngImgIdx;
 52         this.hasBng = hasBng;
 53         this.canWalk = canWalk;
 54         this.midImgIdx = midImgIdx;
 55         this.hasMid = hasMid;
 56         this.objImgIdx = objImgIdx;
 57         this.hasObj = hasObj;
 58         this.canFly = canFly;
 59         this.doorIdx = doorIdx;
 60         this.hasDoor = hasDoor;
 61         this.doorOffset = doorOffset;
 62         this.doorOpen = doorOpen;
 63         this.aniFrame = aniFrame;
 64         this.hasAni = hasAni;
 65         this.aniTick = aniTick;
 66         this.objFileIdx = objFileIdx;
 67         this.light = light;
 68     }
 69     /** 基於已有實體構造對象 */
 70     public MapTileInfo(MapTileInfo mapTileInfo) {
 71         this.bngImgIdx = mapTileInfo.bngImgIdx;
 72         this.hasBng = mapTileInfo.hasBng;
 73         this.canWalk = mapTileInfo.canWalk;
 74         this.midImgIdx = mapTileInfo.midImgIdx;
 75         this.hasMid = mapTileInfo.hasMid;
 76         this.objImgIdx = mapTileInfo.objImgIdx;
 77         this.hasObj = mapTileInfo.hasObj;
 78         this.canFly = mapTileInfo.canFly;
 79         this.doorIdx = mapTileInfo.doorIdx;
 80         this.hasDoor = mapTileInfo.hasDoor;
 81         this.doorOffset = mapTileInfo.doorOffset;
 82         this.doorOpen = mapTileInfo.doorOpen;
 83         this.aniFrame = mapTileInfo.aniFrame;
 84         this.hasAni = mapTileInfo.hasAni;
 85         this.aniTick = mapTileInfo.aniTick;
 86         this.objFileIdx = mapTileInfo.objFileIdx;
 87         this.light = mapTileInfo.light;
 88     }
 89 
 90     /** 獲取背景圖索引 */
 91     public short getBngImgIdx() {
 92         return bngImgIdx;
 93     }
 94     /** 設置背景圖索引 */
 95     public void setBngImgIdx(short bngImgIdx) {
 96         this.bngImgIdx = bngImgIdx;
 97     }
 98     /** 獲取該地圖塊是否有背景圖 */
 99     public boolean isHasBng() {
100         return hasBng;
101     }
102     /** 設置該地圖塊是否有背景圖 */
103     public void setHasBng(boolean hasBng) {
104         this.hasBng = hasBng;
105     }
106     /** 獲取該地圖塊是否能夠站立或走過 */
107     public boolean isCanWalk() {
108         return canWalk;
109     }
110     /** 設置該地圖塊是否能夠站立或走過 */
111     public void setCanWalk(boolean canWalk) {
112         this.canWalk = canWalk;
113     }
114     /** 獲取補充圖索引 */
115     public short getMidImgIdx() {
116         return midImgIdx;
117     }
118     /** 設置補充圖索引 */
119     public void setMidImgIdx(short midImgIdx) {
120         this.midImgIdx = midImgIdx;
121     }
122     /** 獲取該地圖塊是否有補充圖 */
123     public boolean isHasMid() {
124         return hasMid;
125     }
126     /** 設置該地圖塊是否有補充圖 */
127     public void setHasMid(boolean hasMid) {
128         this.hasMid = hasMid;
129     }
130     /** 獲取對象圖索引 */
131     public short getObjImgIdx() {
132         return objImgIdx;
133     }
134     /** 設置對象圖索引 */
135     public void setObjImgIdx(short objImgIdx) {
136         this.objImgIdx = objImgIdx;
137     }
138     /** 獲取該地圖塊是否有對象圖 */
139     public boolean isHasObj() {
140         return hasObj;
141     }
142     /** 設置該地圖塊是否有對象圖 */
143     public void setHasObj(boolean hasObj) {
144         this.hasObj = hasObj;
145     }
146     /** 獲取該地圖塊是否能夠飛越 */
147     public boolean isCanFly() {
148         return canFly;
149     }
150     /** 設置該地圖塊是否能夠飛越 */
151     public void setCanFly(boolean canFly) {
152         this.canFly = canFly;
153     }
154     /** 獲取門索引 */
155     public byte getDoorIdx() {
156         return doorIdx;
157     }
158     /** 設置門索引 */
159     public void setDoorIdx(byte doorIdx) {
160         this.doorIdx = doorIdx;
161     }
162     /** 獲取該地圖塊是否有門 */
163     public boolean isHasDoor() {
164         return hasDoor;
165     }
166     /** 設置該地圖塊是否有門 */
167     public void setHasDoor(boolean hasDoor) {
168         this.hasDoor = hasDoor;
169     }
170     /** 獲取門偏移 */
171     public byte getDoorOffset() {
172         return doorOffset;
173     }
174     /** 設置門偏移 */
175     public void setDoorOffset(byte doorOffset) {
176         this.doorOffset = doorOffset;
177     }
178     /** 獲取該地圖塊門是否打開 */
179     public boolean isDoorOpen() {
180         return doorOpen;
181     }
182     /** 設置該地圖塊門是否打開 */
183     public void setDoorOpen(boolean doorOpen) {
184         this.doorOpen = doorOpen;
185     }
186     /** 獲取動畫幀數 */
187     public byte getAniFrame() {
188         return aniFrame;
189     }
190     /** 設置動畫幀數 */
191     public void setAniFrame(byte aniFrame) {
192         this.aniFrame = aniFrame;
193     }
194     /** 獲取該地圖塊是否有動畫 */
195     public boolean isHasAni() {
196         return hasAni;
197     }
198     /** 設置該地圖塊是否有動畫 */
199     public void setHasAni(boolean hasAni) {
200         this.hasAni = hasAni;
201     }
202     /** 獲取動畫跳幀數 */
203     public byte getAniTick() {
204         return aniTick;
205     }
206     /** 設置動畫跳幀數 */
207     public void setAniTick(byte aniTick) {
208         this.aniTick = aniTick;
209     }
210     /** 獲取資源文件索引 */
211     public byte getObjFileIdx() {
212         return objFileIdx;
213     }
214     /** 設置資源文件索引 */
215     public void setObjFileIdx(byte objFileIdx) {
216         this.objFileIdx = objFileIdx;
217     }
218     /** 獲取亮度 */
219     public byte getLight() {
220         return light;
221     }
222     /** 設置亮度 */
223     public void setLight(byte light) {
224         this.light = light;
225     }
226 }
MapTileInfo

      對於讀取物理文件到產生對象,我使用一些工具方法,這裏主要是高低位的問題,還有就是Delphi中字符串和時間日期到Java的不一樣處理。

    第三節——讀取:

      我對.map文件讀取數據生成對象的過程以下(工具方法請查閱源碼,就不在此貼出了):

 1 /**
 2      * 經過字節數組反序列化地圖文件頭數據
 3      * 
 4      * @param bytes
 5      *     數據(文件中直接讀取,未通過任何處理的字節數組)
 6      * @return
 7      *     地圖文件頭信息
 8      */
 9     public static MapHeader readMapHeader(byte[] bytes) {
10         MapHeader res = new MapHeader();
11         res.setWidth(Common.readShort(bytes, 0, true));
12         res.setHeight(Common.readShort(bytes, 2, true));
13         res.setTitle(readStaticSingleString(bytes, 4));
14         res.setUpdateDate(readDate(bytes, 21, true));
15         res.setReserved(readChars(bytes, 29, 23));
16         return res;
17     }

 

 1 /**
 2      * 經過字節數組反序列化地圖邏輯座標塊兒數據
 3      * 
 4      * @param bytes
 5      *     數據(文件中直接讀取,未通過任何處理的字節數組)
 6      * @return
 7      *     地圖邏輯座標塊兒信息
 8      */
 9     public static MapTile readMapTile(byte[] bytes) {
10         MapTile res = new MapTile();
11         res.setBngImgIdx(Common.readShort(bytes, 0, true));
12         res.setMidImgIdx(Common.readShort(bytes, 2, true));
13         res.setObjImgIdx(Common.readShort(bytes, 4, true));
14         res.setDoorIdx(bytes[6]);
15         res.setDoorOffset(bytes[7]);
16         res.setAniFrame(bytes[8]);
17         res.setAniTick(bytes[9]);
18         res.setObjFileIdx(bytes[10]);
19         res.setLight(bytes[11]);
20         return res;
21     }
22     
23     /**
24      * 經過字節數組反序列化地圖邏輯座標塊信息
25      * 
26      * @param bytes
27      *     數據(文件中直接讀取,未通過任何處理的字節數組)
28      * @return
29      *     地圖邏輯座標塊兒信息
30      */
31     public static MapTileInfo readMapTileInfo(byte[] bytes) {
32         MapTileInfo res = new MapTileInfo();
33         // 讀取背景
34         short bng = Common.readShort(bytes, 0, true);
35         // 讀取中間層
36         short mid = Common.readShort(bytes, 2, true);
37         // 讀取對象層
38         short obj = Common.readShort(bytes, 4, true);
39         // 設置背景
40         if((bng & 0b0111_1111_1111_1111) > 0) {
41             res.setBngImgIdx((short) (bng & 0b0111_1111_1111_1111));
42             res.setHasBng(true);
43         }
44         // 設置中間層
45         if((mid & 0b0111_1111_1111_1111) > 0) {
46             res.setMidImgIdx((short) (mid & 0b0111_1111_1111_1111));
47             res.setHasMid(true);
48         }
49         // 設置對象層
50         if((obj & 0b0111_1111_1111_1111) > 0) {
51             res.setObjImgIdx((short) (obj & 0b0111_1111_1111_1111));
52             res.setHasObj(true);
53         }
54         // 設置是否可站立
55         res.setCanWalk(!Common.is1AtTopDigit(bng) && !Common.is1AtTopDigit(obj));
56         // 設置是否可飛行
57         res.setCanFly(!Common.is1AtTopDigit(obj));
58                 
59         res.setDoorOffset(bytes[7]);
60         if(Common.is1AtTopDigit(bytes[7])) res.setDoorOpen(true);
61         res.setAniFrame(bytes[8]);
62         if(Common.is1AtTopDigit(bytes[8])) {
63             res.setAniFrame((byte) (bytes[8] & 0x7F));
64             res.setHasAni(true);
65         }
66         res.setAniTick(bytes[9]);
67         res.setObjFileIdx(bytes[10]);
68         res.setLight(bytes[11]);
69         return res;
70     }

  第二部分——資源文件:

    第一節——描述:

      Q: wix和wil文件分別是什麼?

      A: wix全名爲「Wemade Image Index」,顧名思義是圖片庫索引;wil全名爲「Wemade Image Lib」,意爲圖片庫。wix文件中存儲了對應圖片庫的基本信息,包括圖片數量、每一個圖片色彩數據起始位置;wil文件則存儲了包括圖片調色板、圖片色彩數據在內的全部圖片信息。

      Q: wix和wil須要對應起來用嗎?

      A: 其實在生產環境中只須要使用wil就能夠了,它包含了程序所需全部信息,網絡上有人說必須使用wix來尋找每一個圖片色彩數據起始位置的說法是錯誤的,不過結合起來使用能最大限度避免錯誤,若是不服,請聯繫我!

      Q: wix文件結構和wil文件結構?

      A: wix文件由標題、圖片數和圖片色彩數據起始位置(對應wil中索引)的數組構成;wil文件由文件頭和多個圖片數據構成,文件頭內容相對固定,每一個圖片色彩數據長度都不盡相同。

    第二節——對應:

      wix文件:

  

        Delphi語言描述(起始位置數組沒有加上):

1 type 
2  TIndexHeader = record 
3  sTitle :String[40]; 
4  iIndexCount :Integer; 

        Java語言描述:

 1 package org.coderecord.jmir.entt;
 2 
 3 /**
 4  * WIX即「WEMADE IMAGE INDEX」
 5  * <br>
 6  * 意味圖片索引
 7  * <br>
 8  * 在熱血傳奇2中,圖片資源存儲方式通常爲一個wix文件和一個wil文件造成一組,負責保存一個內容或功能的圖片資源
 9  * <br>
10  * wix文件爲索引文件,經過它從wil文件中解析出圖片數據
11  * <br>
12  * 對於wix文件(頭)可以使用以下數據結構進行描述
13  * <br>
14  * type
15  * <br>
16  * &nbsp;TIndexHeader&nbsp;=&nbsp;record
17  * <br>
18  * &emsp;sTitle&emsp;:String[40];
19  * <br>
20  * &emsp;iIndexCount&emsp;:Integer;
21  * <br>
22  * <b>sTitle通常爲「INDX v1.0-WEMADE Entertainment inc.」,表示標題;iIndexCount表示對應wil(lib庫文件)中存儲的圖片數量;其實重點是此文件中前44個字節用處不大,從45字節開始纔是咱們關心的。</b>
23  * <p>
24  *     <b>注意:</b>爲何sTitle是String[40]而非String[43],若是是後者,則sTitle會佔用44字節,恰好是咱們所說的前44字節?其實Pascal中對record的packed修飾符有特殊處理,<b>不帶</b>packed表示「對齊」(對齊意味着能被4整除),如今sTitle爲String[40],加上其頭部修飾的一個字節佔共用41字節,並不能被4整除,因此編譯器會在後面加上3個字節來<b>「對齊」</b>,因此在這裏sTitle一共佔用44字節
25  * </p>
26  * 從第49字節開始則是iIndexCount個Integer類型數據,標識在對應wil文件中各個圖片數據對應位置(array of Integer),這裏的位置索引從0開始,指示在文件二進制數據的索引
27  * 
28  * @author ShawRyan
29  *
30  */
31 public class WIX {
32     /** 標題 */
33     private String title;
34     /** 資源數量 */
35     private int indexCount;
36     /** 資源數據起始位置 */
37     private int[] indexArray;
38     
39     /** 默認構造函數 */
40     public WIX() {}
41     /** 基於已有對象構造實例 */
42     public WIX(WIX wix) {
43         this.title = wix.title;
44         this.indexCount = wix.indexCount;
45         this.indexArray = wix.indexArray;
46     }
47     /** 使用所有參數構造實例 */
48     public WIX(String title, int indexCount, int[] indexArray) {
49         this.title = title;
50         this.indexCount = indexCount;
51         this.indexArray = indexArray;
52     }
53     
54     /** 獲取標題 */
55     public String getTitle() {
56         return title;
57     }
58     /** 設置標題 */
59     public void setTitle(String title) {
60         this.title = title;
61     }
62     /** 獲取資源數量 */
63     public int getIndexCount() {
64         return indexCount;
65     }
66     /** 設置資源數量 */
67     public void setIndexCount(int indexCount) {
68         this.indexCount = indexCount;
69     }
70     /** 獲取資源索引 */
71     public int[] getIndexArray() {
72         return indexArray;
73     }
74     /** 設置資源索引 */
75     public void setIndexArray(int[] indexArray) {
76         this.indexArray = indexArray;
77     }
78 }
Wix

      wil文件:

  

        Delphi語言描述(調色板未加上,調色板提及來比較麻煩):

1 type 
2  TLibHeader = record 
3  sTitle :String[40]; 
4  iImageCount :Integer; 
5  iColorCount :Integer; 
6  iPaletteSize :Integer; 
1 type 
2  TImageInfo = record 
3  siWidth :SmallInt; 
4  siHeight :SmallInt; 
5  siPx :SmallInt; 
6  siPy :SmallInt; 
7  Bits :PByte; 

      Java語言描述:

 1 package org.coderecord.jmir.entt;
 2 
 3 import org.coderecord.jmir.entt.internal.ImageInfo;
 4 import org.coderecord.jmir.entt.internal.LibHeader;
 5 
 6 /**
 7  * WIL即「WEMADE IMAGE LIBRARY」
 8  * <br>
 9  * 意爲圖片庫
10  * <br>
11  * 存放多個圖片數據(包括像素點色彩)
12  * <br>
13  * WIL文件由頭部和多個圖片信息組成
14  * <br>
15  * 頭部可使用以下類型進行描述
16  * <br>
17  * type
18  * <br>
19  * &nbsp;TLibHeader&nbsp;=&nbsp;record
20  * <br>
21  * &emsp;sTitle&emsp;:String[40];
22  * <br>
23  * &emsp;iImageCount&emsp;:Integer;
24  * <br>
25  * &emsp;iColorCount&emsp;:Integer;
26  * <br>
27  * &emsp;iPaletteSize&emsp;:Integer;
28  * <br>
29  * <b>頭部共佔用56字節,sTitle佔用44字節,緣由參見{@link WIX}。其中sTitle爲標題,通常爲「ILIB v1.0-WEMADE Entertainment inc.」;iImageCount爲圖片數量;iColorCount表示色彩位深度,如256表示8bit位圖,65536表示16bit位圖(2的冪數);iPaletteSize表示調色板字節數</b>
30  * <br>
31  * 頭部後則是調色板(調色板請參照{@link org.coderecord.jmir.entt.internal.LibHeader#pallette LibHeader})和多個圖片數據,包括圖片寬高/座標和像素色彩,可使用下面的結構進行描述(不包括調色板,調色板其實也可放入header中,類型爲 array of Byte)
32  * <br>
33  * type
34  * <br>
35  * &nbsp;TImageInfo&nbsp;=&nbsp;record
36  * <br>
37  * &emsp;siWidth&emsp;:SmallInt;
38  * <br>
39  * &emsp;siHeight&emsp;:SmallInt;
40  * <br>
41  * &emsp;siPx&emsp;:SmallInt;
42  * <br>
43  * &emsp;siPy&emsp;:SmallInt;
44  * <br>
45  * &emsp;Bits&emsp;:PByte;
46  * <br>
47  * <b>siWidth</b> 圖片寬度
48  * <br>
49  * <b>siHeight</b> 圖片高度
50  * <br>
51  * <b>siPx</b> 圖片橫向偏移量
52  * <br>
53  * <b>siPy</b> 圖片縱向偏移量
54  * <br>
55  * <b>Bits</b> 圖片像素色彩值數組
56  * 
57  * @author ShawRyan
58  *
59  */
60 public class WIL {
61     
62     /** 文件頭 */
63     private LibHeader header;
64     /** 圖片數組 */
65     private ImageInfo[] images;
66     
67     /** 無參構造函數 */
68     public WIL() {}
69     /** 基於已有實例構造對象 */
70     public WIL(WIL wil) {
71         this.header = wil.header;
72         this.images = wil.images;
73     }
74     /** 全參構造函數 */
75     public WIL(LibHeader header, ImageInfo[] images) {
76         this.header = header;
77         this.images = images;
78     }
79     
80     /** 獲取頭部 */
81     public LibHeader getHeader() {
82         return header;
83     }
84     /** 設置頭部 */
85     public void setHeader(LibHeader header) {
86         this.header = header;
87     }
88     /** 獲取圖片 */
89     public ImageInfo[] getImages() {
90         return images;
91     }
92     /** 設置圖片 */
93     public void setImages(ImageInfo[] images) {
94         this.images = images;
95     }
96 }
Wil
  1 package org.coderecord.jmir.entt.internal;
  2 
  3 /** WIL文件頭 */
  4 public class LibHeader {
  5     /** 長度(字節數),不包括調色板大小 */
  6     public static final int BIT_LENGTH_WITHOUT_PATTELE = 56;
  7     
  8     /** 標題 */
  9     private String title;
 10     /** 圖片數量 */
 11     private int imageCount;
 12     /** 色深度 */
 13     private int colorCount;
 14     /** 調色板字節數 */
 15     private int paletteSize;
 16     /**
 17      * 調色板
 18      * <br>
 19      * 調色板使用是一組字節數組即二維字節數組,第二維總爲4字節,依次存儲藍、綠、紅、Alpha色彩值
 20      * <br>
 21      * <br>
 22      * 對於任意色彩而言,都應該使用24位來存儲,好比FF0000表示紅色
 23      * <br>
 24      * 24位色彩值也被稱爲真彩
 25      * <br>
 26      * Windows 中位圖有兩種格式:
 27      * <br>
 28      * &emsp;設備相關位圖 Device Depend Bitmap DDB
 29      * <br>
 30      * &emsp;設備無關位圖 Device Independ Bitmap DIB
 31      * <br>
 32      * 熱血傳奇使用DIB對圖片進行存儲
 33      * <br>
 34      * 色彩深度有時少於24位,有時是由於精度要求並不會那麼高,例如使用5或6個字節存儲的R/G/B值就已經夠用,此時可以使用16位顏色值存儲一個像素點的顏色
 35      * <br>
 36      * &emsp;有時甚至一幅圖的色彩不超過256種,此時就能夠將這256中顏色提取出來做爲一個調色板,而後像素點色彩值則存儲色彩值在這個調色板中的索引,這就是8位顏色值
 37      * <br>
 38      * &emsp;對於單色或16色(即便用1bit或4bit存儲色彩值的狀況不與考慮)
 39      */
 40     private int[] palette;
 41     
 42     /** 無參構造函數 */
 43     public LibHeader() {}
 44     /** 基於已有對象構造實例 */
 45     public LibHeader(LibHeader header) {
 46         this.title = header.title;
 47         this.imageCount = header.imageCount;
 48         this.colorCount = header.colorCount;
 49         this.paletteSize = header.paletteSize;
 50         this.palette = header.palette;
 51     }
 52     /** 帶所有參數的構造函數 */
 53     public LibHeader(String title, int imageCount, int colorCount, int paletteSize, int[] pallette) {
 54         this.title = title;
 55         this.imageCount = imageCount;
 56         this.colorCount = colorCount;
 57         this.paletteSize = paletteSize;
 58         this.palette = pallette;
 59     }
 60     
 61     /** 獲取標題 */
 62     public String getTitle() {
 63         return title;
 64     }
 65     /** 設置標題 */
 66     public void setTitle(String title) {
 67         this.title = title;
 68     }
 69     /** 獲取圖片數量 */
 70     public int getImageCount() {
 71         return imageCount;
 72     }
 73     /** 設置圖片數量 */
 74     public void setImageCount(int imageCount) {
 75         this.imageCount = imageCount;
 76     }
 77     /** 獲取色深 */
 78     public int getColorCount() {
 79         return colorCount;
 80     }
 81     /** 設置色深 */
 82     public void setColorCount(int colorCount) {
 83         this.colorCount = colorCount;
 84     }
 85     /** 獲取調色板大小 */
 86     public int getPaletteSize() {
 87         return paletteSize;
 88     }
 89     /** 設置調色板大小 */
 90     public void setPaletteSize(int paletteSize) {
 91         this.paletteSize = paletteSize;
 92     }
 93     /** 獲取調色板 */
 94     public int[] getPalette() {
 95         return palette;
 96     }
 97     /** 設置調色板 */
 98     public void setPalette(int[] pallette) {
 99         this.palette = pallette;
100     }
101 }
LibHeader
  1 package org.coderecord.jmir.entt.internal;
  2 
  3 /** 圖片信息 */
  4 public class ImageInfo {
  5     /** 圖片寬度 */
  6     private short width;
  7     /** 圖片高度 */
  8     private short height;
  9     /** 圖片橫向偏移量 */
 10     private short offsetX;
 11     /** 圖片縱向偏移量 */
 12     private short offsetY;
 13     /**
 14      * 圖片數據
 15      * <br>
 16      * 逐列存儲像素色彩值,對於4 * 4的圖片而言,其色彩數據以下(以字節爲單位)
 17      * <br>
 18      * <b>注意:</b>若是圖片寬度字節數不是4的倍數會有填充字節數,若是是讀取真正的圖片數據能夠不用考慮,但若是須要一次性讀取多張圖片則須要跳過填充的字節,參見{@link org.coderecord.jmir.kits.Pascal#fillPByteLineWidth(int, int) fillPByteLineWidth}
 19      * <br>
 20      * 8位
 21      * <br>
 22      * 12&emsp;8&emsp;4&emsp;0&emsp;
 23      * <br>
 24      * 13&emsp;9&emsp;5&emsp;1&emsp;
 25      * <br>
 26      * 14&emsp;10&emsp;6&emsp;2&emsp;
 27      * <br>
 28      * 15&emsp;11&emsp;7&emsp;3&emsp;
 29      * <br>
 30      * 16位
 31      * <br>
 32      * 24&25&emsp;16&17&emsp;8&9&emsp;0&1&emsp;
 33      * <br>
 34      * 26&27&emsp;18&19&emsp;10&11&emsp;2&3&emsp;
 35      * <br>
 36      * 28&29&emsp;20&21&emsp;12&13&emsp;4&5&emsp;
 37      * <br>
 38      * 30&31&emsp;22&23&emsp;14&15&emsp;6&7&emsp;
 39      */
 40     private byte[] pixels;
 41     
 42     /** 無參構造函數 */
 43     public ImageInfo() {}
 44     /** 基於已有對象構造實例 */
 45     public ImageInfo(ImageInfo imageInfo) {
 46         this.height = imageInfo.height;
 47         this.offsetX = imageInfo.offsetX;
 48         this.offsetY = imageInfo.offsetY;
 49         this.pixels = imageInfo.pixels;
 50         this.width = imageInfo.width;
 51     }
 52     /** 帶所有參數的構造函數 */
 53     public ImageInfo(short width, short height, short offsetX, short offsetY, byte[] pixels) {
 54         this.width = width;
 55         this.height = height;
 56         this.offsetX = offsetX;
 57         this.offsetY = offsetY;
 58         this.pixels = pixels;
 59     }
 60     
 61     /** 獲取圖片寬度 */
 62     public short getWidth() {
 63         return width;
 64     }
 65     /** 設置圖片高度 */
 66     public void setWidth(short width) {
 67         this.width = width;
 68     }
 69     /** 獲取圖片高度 */
 70     public short getHeight() {
 71         return height;
 72     }
 73     /** 設置圖片高度 */
 74     public void setHeight(short height) {
 75         this.height = height;
 76     }
 77     /** 獲取圖片橫線偏移量 */
 78     public short getOffsetX() {
 79         return offsetX;
 80     }
 81     /** 設置圖片橫向偏移量 */
 82     public void setOffsetX(short offsetX) {
 83         this.offsetX = offsetX;
 84     }
 85     /** 獲取圖片縱向偏移量 */
 86     public short getOffsetY() {
 87         return offsetY;
 88     }
 89     /** 設置圖片縱向偏移量 */
 90     public void setOffsetY(short offsetY) {
 91         this.offsetY = offsetY;
 92     }
 93     /** 獲取圖片二進制數據 */
 94     public byte[] getPixels() {
 95         return pixels;
 96     }
 97     /** 設置圖片二進制數據 */
 98     public void setPixels(byte[] pixels) {
 99         this.pixels = pixels;
100     }
101 }
ImageInfo

    第三節——讀取:

      咱們的目的在於使用wil中的圖片,在上圖其實能夠看到咱們只差一個每一個圖片色彩數據大小(??處),這個大小能夠本身計算獲得(涉及到Delphi位圖處理,信息量較大,不在此贅述),但咱們也能夠從wix中拿到,這樣比較方便,並且不會出錯。

 1 /**
 2      * 根據庫文件路徑和索引文件路徑讀取圖片庫
 3      * 
 4      * @param wilPath
 5      *     圖片庫文件路徑
 6      * @param wixPath
 7      *     圖片索引文件路徑
 8      * @return
 9      *     獲得的圖片庫文件路徑
10      */
11     public static WIL readWILFromFile(String wilPath, String wixPath) {
12         WIX wix = new WIX();
13         try(FileInputStream fis = new FileInputStream(wixPath)) {
14             wix.setTitle(Pascal.readStaticSingleString(fis, 0, 44));
15             wix.setIndexCount(Common.readInt(fis, 0, true));
16             int[] imageIndexs = new int[wix.getIndexCount()];
17             for(int i = 0; i < imageIndexs.length; ++i)
18                 imageIndexs[i] = Common.readInt(fis, 0, true);
19             wix.setIndexArray(imageIndexs);
20         } catch (FileNotFoundException e) {
21             throw new RuntimeException(e);
22         } catch (IOException e) {
23             throw new RuntimeException(e);
24         }
25         WIL res = new WIL();
26         try(FileInputStream fis = new FileInputStream(wilPath)) {
27             res.setHeader(Pascal.readLibHeader(fis));
28         } catch (FileNotFoundException e) {
29             throw new RuntimeException(e);
30         } catch (IOException e) {
31             throw new RuntimeException(e);
32         }
33         try(RandomAccessFile raf = new RandomAccessFile(wilPath, "r")){
34             ImageInfo[] images = new ImageInfo[res.getHeader().getImageCount()];
35             for(int i = 0; i < images.length; ++i)
36                 images[i] = Pascal.readImageInfo(raf, wix.getIndexArray()[i], Pascal.colorCountToBitCount(res.getHeader().getColorCount()));
37             res.setImages(images);
38         } catch (FileNotFoundException e) {
39             throw new RuntimeException(e);
40         } catch (IOException e) {
41             throw new RuntimeException(e);
42         }
43         return res;
44     }
readWil

    第四節——圖片處理:

      圖片數據須要通過轉換才能在界面上展現(針對8位的圖片):

      首先在讀取LibHeader時就須要作透明色處理:

 1 /**
 2      * 從流中讀取圖片庫文件頭
 3      * 
 4      * @param is
 5      *     數據流
 6      * @return
 7      *     文件頭
 8      * @throws IOException
 9      *     可能的流異常
10      */
11     public static LibHeader readLibHeader(InputStream is) throws IOException {
12         LibHeader res = new LibHeader();
13         res.setTitle(Pascal.readStaticSingleString(is, 0, 44));
14         res.setImageCount(Common.readInt(is, 0, true));
15         res.setColorCount(Common.readInt(is, 0, true));
16         res.setPaletteSize(Common.readInt(is, 0, true));
17         int[] palette = new int[res.getPaletteSize() / 4];
18         for(int i = 0; i < palette.length; ++i) {
19             byte[] byteArgb = new byte[4];
20             is.read(byteArgb);
21             // 最重要的一步,重設Alpha值
22             // 調色板的Alpha值都爲0,而實際上只有0x00000000(通常在調色板第一個色彩)纔是透明色,即熱血傳奇2圖片庫中8位色彩沒有不透明的純黑色
23             if(byteArgb[2] == 0 && byteArgb[1] == 0 && byteArgb[0] == 0)
24                 byteArgb[3] = 0;
25             else
26                 byteArgb[3] = (byte) 255;
27             
28             palette[i] = Common.readInt(byteArgb, 0, true);
29         }
30         
31         res.setPalette(palette);
32         return res;
33     }
readLibHeader

      在顯示時要記住圖片顏色數據的存儲是從右向左,從上往下的:

 1 /**
 2      * 從ImageInfo對象及調色板和色彩深度讀取圖片數據
 3      * 
 4      * @param imageInfo
 5      *     圖片原數據信息
 6      * @param palette
 7      *     調色板
 8      * @param bitCount
 9      *     色彩深度
10      * @return
11      */
12     public static BufferedImage readImage(ImageInfo imageInfo, int[] palette, int bitCount) {
13         BufferedImage res = null;        
14         if(bitCount == 8) {
15             res = new BufferedImage(imageInfo.getWidth(), imageInfo.getHeight(), BufferedImage.TYPE_INT_ARGB);
16 
17             int index = 0;
18             for(int h = 0; h < imageInfo.getHeight(); ++h)
19                 for(int w = 0; w < imageInfo.getWidth(); ++w)
20                     res.setRGB(w, res.getHeight() - 1 - h, palette[imageInfo.getPixels()[index++] & 0xff]);
21         } else if(bitCount == 16) {
22             // FIXME
23             res = new BufferedImage(imageInfo.getWidth(), imageInfo.getHeight(), BufferedImage.TYPE_USHORT_565_RGB);
24             int index = 0;
25             for(int h = 0; h < imageInfo.getHeight(); ++h)
26                 for(int w = 0; w < imageInfo.getWidth(); ++w, index += 2)
27                     res.setRGB(w, res.getHeight() - 1 - h, Common.readShort(imageInfo.getPixels(), index, true));
28         } else if(bitCount == 24) {
29             // FIXME
30             //res = new BufferedImage(imageInfo.getWidth(), imageInfo.getHeight(), BufferedImage.TYPE_INT_RGB);
31             //int index = 0;
32             //for(int h = 0; h < imageInfo.getHeight(); ++h)
33             //    for(int w = 0; w < imageInfo.getWidth(); ++w)
34             //        res.setRGB(w, h, Common.readInt(imageInfo.getPixels(), index++, true));
35         } else {
36             // FIXME
37             //res = new BufferedImage(imageInfo.getWidth(), imageInfo.getHeight(), BufferedImage.TYPE_INT_RGB);
38             //int index = 0;
39             //for(int h = 0; h < imageInfo.getHeight(); ++h)
40             //    for(int w = 0; w < imageInfo.getWidth(); ++w)
41             //        res.setRGB(w, h, Common.readInt(imageInfo.getPixels(), index++, true));
42         }
43         return res;
44     }
readImage

 

  說了這麼久,我本身都糊塗了。你們若是不清楚請下載源碼或基於Eclipse和JDK的項目進行查看。

 

  編輯於2015-01-25 21:06:33

    項目沒有繼續下去,不過我用lwjgl重寫了部分,實現了地圖加載和紋理讀取。你們能夠去新的項目地址checkout代碼。

 

  編輯於2017-08-16 13:53:40

    github上我上傳了一個輕便的庫,能夠一看https://github.com/jootnet/mir2.core

 

 歡迎您移步咱們的交流羣,無聊的時候你們一塊兒打發時間:Programmer Union

 或者經過QQ與我聯繫:點擊這裏給我發消息

 (最後編輯時間2014-03-16 15:43:23)

相關文章
相關標籤/搜索