posts - 495,comments - 227,trackbacks - 0
            序言:
            
            由于前些時間,一些matrixer常問關于j2me中使用Pak文件的問題。本人雖學藝不深,但滿懷熱心的做了一番探索,現將制作Pak文件的看法和方法公布出來,大家多多提意見。
            
            一、什么是Pak文件:
            
            Pak文件就是將多個文件打包為一個單獨文件,在這個文件中保存著多個文件的數據,當然還有一些描述文件結構的數據。所以將“Pak”作為文件的后綴是一種常規的用法,大家可以自定義其它的文件后綴。
            
            二、為什么使用Pak文件:
            
            由于MIDP對發布安裝的j2me程序大小進行了限制,所以縮小發布程序就意味著能夠提供更多的程序或者內容(如圖片、音樂)給用戶。而通過研究發現zip/jar算法對大文件的壓縮率高于對等量的多個小文件的壓縮率。
            
            當然還有其它方法,這里簡單做一下討論比如使用混淆器ProGuard的“-overloadaggressively”選項使jar文件縮小,但也會導致一些錯誤,因為這種方法生成jar中的class符合java byte code標準,但是與java語法相悖,嚴重的可能造成一些jre對Object的序列化錯誤。
            
            所以使用Pak方法將程序中要用到的資源(圖片、音樂、文本)組合為單一文件是一個安全有效的方法。而且對于一些商用程序,完全可以在pak文件中對文件數據進行加密,很好的保護了作者和公司的權益。本人的sample中使用了簡單的“加減法”加密,對于手機這類設備來講是一個效率較高的選擇。
            
            三、Pak文件的結構:
            
            大家可以自己設計Pak文件結構,本人這里只是拋磚引玉的作個sample。下面就是本人設計的Pak文件結構:
            
            PAK File Header:Pak文件的頭部
            
            * 簽名:6字節char數組 * 版本號:32位float * 文件table數量:32位整數 * 密碼行為:8位字節 * 密碼:8位字節 * 文件唯一ID:10字節char數組 * 保留位:32位整數(4字節)
            
            File Table:Pak文件中包含文件的列表,在一個Pak文件中一個被包含的文件對應一個File Table。
            
            * 文件名:30字節char數組 * 文件大小:32位整型 * 文件在pak文件中的位移:32位整數
            
            Concatenated File Data:按File Table的順序連接在一起的文件數據。
            * 文件數據
            
            四、程序框架:
            
            說明:由于Pak文件的制作和使用分別要使用兩個java應用領域:j2se和j2me,所以本人將PakUtil類制作了2個版本(j2se和j2me)。
            
            程序框架如下:
            1。PakHeader類,定義了Pak文件頭。
            2。PakFileTable類,定義Pak文件table。
            3。PakUtil類(j2se版),具備兩個功能:將多個png圖片合成一個Pak文件,并使用簡單的加減加密法對其進行加密;從Pak文件中取出png圖片,構造byte數組(可以用來構造Image對象)或者寫為文件。
            PakUtil類(j2me版),具備的功能:從Pak文件中取出png圖片,構造byte數組(可以用來構造Image對象)。
            
            五、PakHeader和PakFileTable類:
            
            PakHeader.java:
            package cn.org.matrix.gmatrix.gameLab.util.pak;/** * Pak文件頭: * 結構: *
            簽名:6字節char數組 *  版本號:32位float *
            文件table數量:32位整數 *
            密碼行為:8位字節 *  密碼:8位字節 *
            文件唯一ID:10字節char數組 *
            保留位:32位整數(4字節) * @author cleverpig * */class PakHeader {
            //定義文件唯一ID長度
            public static final int UNIQUEID_LENGTH=10;
            //定義文件簽名長度
            public static final int SIGNATURE_LENGTH=6;
            //定義加法運算
            public static final int ADDITION_CIPHERACTION=0;
            //定義減法運算
            public static final int SUBTRACT_CIHOERACTION=1;
            //文件簽名
            private char[] signature=new char[SIGNATURE_LENGTH];
            //版本號
            private float version=0f;
            //文件table數量
            private long numFileTableEntries=0;
            //密碼使用方法:在原數據上進行加法還是減法
            private byte cipherAction=ADDITION_CIPHERACTION;
            //密碼值
            private byte cipherValue=0x00;
            //唯一ID
            private char[] uniqueID=new char[UNIQUEID_LENGTH];
            //保留的4字節
            private long reserved=0;
            public PakHeader(){
            }
            /**
            * 構造方法
            * @param signature 簽名
            * @param version 版本
            * @param numFileTableEntries 文件table數量
            * @param cipherAction 密碼使用方法
            * @param cipherValue 密碼值
            * @param uniqueID 唯一ID
            * @param reserved 保留的2字節
            */
            public PakHeader(char[] signature,float version,
            long numFileTableEntries,byte cipherAction,
            byte cipherValue,char[] uniqueID,long reserved){
            for(int i=0;i<SIGNATURE_LENGTH;this.signature[i]=signature[i],i++)
            ;
            this.version=version;
            this.cipherAction=cipherAction;
            this.numFileTableEntries=numFileTableEntries;
            this.cipherValue=cipherValue;
            for(int i=0;i<UNIQUEID_LENGTH;this.uniqueID[i]=uniqueID[i],i++);
            this.reserved=reserved;
            }        
            public byte getCipherValue() {
            return cipherValue;    
            }
            public void setCipherValue(byte cipherValue) {
            this.cipherValue = cipherValue;
            }
            public long getNumFileTableEntries() {
            return numFileTableEntries;
            }
            public void setNumFileTableEntries(long numFileTableEntries) {
            this.numFileTableEntries = numFileTableEntries;
            }
            public long getReserved() {
            return reserved;
            }
            public void setReserved(long reserved) {
            this.reserved = reserved;
            }
            public char[] getUniqueID() {
            return uniqueID;
            }
            public void setUniqueID(char[] uniqueID) {
            for(int i=0;i<UNIQUEID_LENGTH;this.uniqueID[i]=uniqueID[i],i++)
            ;    
            }    
            public float getVersion() {
            return version;
            }    
            public void setVersion(float version) {
            this.version = version;
            }
            public byte getCipherAction() {
            return cipherAction;
            }
            public void setCipherAction(byte cipherAction) {
            this.cipherAction = cipherAction;
            }
            public char[] getSignature() {
            return signature;
            }
            public void setSignature(char[] signature) {
            for(int i=0;i<SIGNATURE_LENGTH;this.signature[i] = signature[i],i++)
            ;    
            }
            /**
            * 返回PakHeader的大小
            * @return 返回PakHeader的大小
            */    
            public static int size(){
            return SIGNATURE_LENGTH+4+4+1+1+UNIQUEID_LENGTH+4;
            }
            public String toString(){
            String result="";
            result+="\t簽名:"+new String(this.signature).trim()
            +"\t版本號:"+this.version
            +"\t文件table數量:"+this.numFileTableEntries
            +"\t密碼行為:" +this.cipherAction
            +"\t密碼:"+this.cipherValue            
            +"\t文件唯一ID:"+new String(this.uniqueID).trim()            +"\t保留位:"+this.reserved;        
            return result;    
            }}
            
            PakFileTable.java
            package cn.org.matrix.gmatrix.gameLab.util.pak;/** * Pak文件table類 * 文件table結構: *
            文件名:30字節char數組 *
            文件大小:32位整型 *
            文件在pak文件中的位移:32位整數 * @author cleverpig * */class PakFileTable {
            public static final int FILENAME_LENGTH=30;
            //文件名
            private char[] fileName=new char[FILENAME_LENGTH];
            //文件大小
            private long fileSize=0L;
            //文件在pak文件中的位移
            private long offSet=0L;
            public PakFileTable(){
            }
            /**
            * 構造方法
            * @param fileName 文件名
            * @param fileSize 文件大小
            * @param offSet 文件在Pak文件中的位移
            */
            public PakFileTable(char[] fileName,
            long fileSize,long offSet){
            for(int i=0;i<FILENAME_LENGTH;this.fileName[i]=fileName[i],i++)
            ;        this.fileSize=fileSize;
            this.offSet=offSet;
            }
            public char[] getFileName() {
            return fileName;
            }
            public void setFileName(char[] fileName) {
            for(int i=0;i<fileName.length;this.fileName[i]=fileName[i],i++)
            ;    
            }
            public long getFileSize() {
            return fileSize;
            }
            public void setFileSize(long fileSize) {
            this.fileSize = fileSize;
            }
            public long getOffSet() {
            return offSet;
            }
            public void setOffSet(long offSet) {
            this.offSet = offSet;
            }
            /**
            * 返回文件Table的大小
            * @return 返回文件Table的大小
            */    
            public static int size(){
            return FILENAME_LENGTH+4+4;
            }
            public String toString(){
            return "\t文件名:"+new String(this.fileName).trim()
            +"\t文件大小:"+this.fileSize
            +"\t文件位移:"+this.offSet;
            }}
            
            六、PakUtil類(j2se版):
            
            PakUtil.java
            package cn.org.matrix.gmatrix.gameLab.util.pak;import java.io.*;
            import java.util.Vector;
            /** * Pak工具類 * 功能:
            *1.將多個png圖片合成一個Pak文件,并使用簡單的加減加密法對其進行加密;
            * 2.從Pak文件中取出png圖片,構造byte數組(可以用來構造Image對象)或者寫為文件 * @author cleverpig * */public class PakUtil {
            public PakUtil(){
            }
            /**
            * 返回文件長度
            * @param filePath 文件路徑
            * @return 文件長度
            */
            private long getFileSize(String filePath){
            File file=new File(filePath);
            return file.length();
            }
            /**
            * 返回文件名
            * @param filePath 文件路徑
            * @return 文件名
            */
            
            private String getFileName(String filePath){
            File file=new File(filePath);
            return file.getName();
            }
            /**
            * 計算文件位移的起始點
            * @return 文件位移的起始點
            */
            private long workOutOffsetStart(PakHeader header){
            //計算出文件頭+文件table的長度
            return PakHeader.size()+header.getNumFileTableEntries()*PakFileTable.size();
            }
            /**
            * 計算文件位移
            * @param fileIndex 文件序號
            * @param lastFileOffset 上一個文件位移
            * @return 文件在pak文件中的位移
            */
            private long workOutNextOffset(long sourceFileSize,long lastFileOffset){
            return lastFileOffset+sourceFileSize;    
            }
            /**
            * 生成文件table
            * @param sourceFileName 源文件名
            * @param sourceFileSize 源文件長度
            * @param currentFileOffset 當前文件位移
            * @return 生成的PakFileTable對象
            */    
            private PakFileTable generateFileTable(String sourceFileName,
            long sourceFileSize,long currentFileOffset){
            PakFileTable ft=new PakFileTable();
            ft.setFileName(sourceFileName.toCharArray());
            ft.setFileSize(sourceFileSize);
            ft.setOffSet(currentFileOffset);
            return ft;
            }
            /**
            * 將char字符數組寫入到DataOutputStream中
            * @param toWriteCharArray 被寫入的char數組
            * @param dos DataOutputStream
            * @throws Exception
            */    
            private void writeCharArray(char[] toWriteCharArray,DataOutputStream dos) throws Exception{
            for(int i=0;i<toWriteCharArray.length;dos.writeChar(toWriteCharArray[i]),i++);
            }        
            /**
            * 使用文件頭中的密碼對數據進行加密
            * @param buff 被加密的數據
            * @param buffLength 數據的長度
            * @param header 文件頭
            */
            private void encryptBuff(byte[] buff,int buffLength,PakHeader header){
            for(int i=0;i<buffLength;i++){
            switch(header.getCipherAction()){
            case PakHeader.ADDITION_CIPHERACTION:
            buff[i]+=header.getCipherValue();
            break;
            case PakHeader.SUBTRACT_CIHOERACTION:
            buff[i]-=header.getCipherValue();
            break;
            }
            }
            }
            /**
            * 使用文件頭中的密碼對數據進行解密
            * @param buff 被解密的數據
            * @param buffLength 數據的長度
            * @param header 文件頭
            */
            private void decryptBuff(byte[] buff,int buffLength,PakHeader header){
            for(int i=0;i<buffLength;i++){
            switch(header.getCipherAction()){
            case PakHeader.ADDITION_CIPHERACTION:
            buff[i]-=header.getCipherValue();
            break;
            case PakHeader.SUBTRACT_CIHOERACTION:
            buff[i]+=header.getCipherValue();
            break;
            }
            }
            }
            /**
            * 制作Pak文件
            * @param sourceFilePath 源文件路徑數組
            * @param destinateFilePath 目的文件路徑(Pak文件)
            * @param cipherAction 密碼行為
            * @param cipherValue 密碼
            * @throws Exception
            */
            public void makePakFile(String[] sourceFilePath,
            String destinateFilePath,PakHeader header) throws Exception{
            PakFileTable[] fileTable=new PakFileTable[sourceFilePath.length];
            //計算文件位移起始點
            long fileOffset=workOutOffsetStart(header);
            //逐個建立文件table
            for(int i=0;i<sourceFilePath.length;i++){
            String sourceFileName=getFileName(sourceFilePath[i]);
            long sourceFileSize=getFileSize(sourceFilePath[i]);
            PakFileTable ft=generateFileTable(sourceFileName,sourceFileSize,fileOffset);
            //計算下一個文件位移
            fileOffset=workOutNextOffset(sourceFileSize,fileOffset);
            fileTable[i]=ft;
            }
            //寫入文件頭
            File wFile=new File(destinateFilePath);
            FileOutputStream fos=new FileOutputStream(wFile);
            DataOutputStream dos=new DataOutputStream(fos);
            writeCharArray(header.getSignature(),dos);
            dos.writeFloat(header.getVersion());
            dos.writeLong(header.getNumFileTableEntries());
            dos.writeByte(header.getCipherAction());
            dos.writeByte(header.getCipherValue());
            writeCharArray(header.getUniqueID(),dos);
            dos.writeLong(header.getReserved());
            //寫入文件table
            for(int i=0;i<fileTable.length;i++){
            writeCharArray(fileTable[i].getFileName(),dos);
            dos.writeLong(fileTable[i].getFileSize());
            dos.writeLong(fileTable[i].getOffSet());
            }
            //寫入文件數據
            for(int i=0;i<fileTable.length;i++){
            File ftFile=new File(sourceFilePath[i]);
            FileInputStream ftFis=new FileInputStream(ftFile);
            DataInputStream ftDis=new DataInputStream(ftFis);
            byte[] buff=new byte[256];
            int readLength=0;
            while((readLength=ftDis.read(buff))!=-1){
            encryptBuff(buff,readLength,header);
            dos.write(buff,0,readLength);
            }
            ftDis.close();
            ftFis.close();
            }
            dos.close();
            }
            /**
            * 從DataInputStream讀取char數組     * @param dis DataInputStream
            * @param readLength 讀取長度
            * @return char數組
            * @throws Exception
            */
            private char[] readCharArray(DataInputStream dis,int readLength) throws Exception{
            char[] readCharArray=new char[readLength];
            for(int i=0;i<readLength;i++){
            readCharArray[i]=dis.readChar();
            }
            return readCharArray;
            }
            /**
            * 從PAK文件中讀取文件頭
            * @param dis DataInputStream
            * @return PakHeader
            * @throws Exception
            */
            private PakHeader readHeader(DataInputStream dis) throws Exception{
            PakHeader header=new PakHeader();
            char[] signature=readCharArray(dis,PakHeader.SIGNATURE_LENGTH);
            header.setSignature(signature);
            header.setVersion(dis.readFloat());
            header.setNumFileTableEntries(dis.readLong());
            header.setCipherAction(dis.readByte());
            header.setCipherValue(dis.readByte());
            char[] uniqueID=readCharArray(dis,PakHeader.UNIQUEID_LENGTH);
            header.setUniqueID(uniqueID);
            header.setReserved(dis.readLong());
            return header;
            }
            /**
            * 讀取所有的文件table
            * @param dis DataInputStream
            * @param fileTableNumber 文件表總數
            * @return 文件table數組
            * @throws Exception
            */
            private PakFileTable[] readFileTable(DataInputStream dis,int fileTableNumber) throws Exception{
            PakFileTable[] fileTable=new PakFileTable[fileTableNumber];
            for(int i=0;i<fileTableNumber;i++){
            PakFileTable ft=new PakFileTable();
            ft.setFileName(readCharArray(dis,PakFileTable.FILENAME_LENGTH));
            ft.setFileSize(dis.readLong());
            ft.setOffSet(dis.readLong());
            fileTable[i]=ft;
            }        
            return fileTable;
            }
            /**
            * 從pak文件讀取文件到byte數組
            * @param dis DataInputStream
            * @param fileTable PakFileTable
            * @return byte數組
            * @throws Exception
            */
            private byte[] readFileFromPak(DataInputStream dis,PakHeader header,PakFileTable fileTable) throws Exception{
            dis.skip(fileTable.getOffSet()-workOutOffsetStart(header));
            //
            int fileLength=(int)fileTable.getFileSize();
            byte[] fileBuff=new byte[fileLength];
            int readLength=dis.read(fileBuff,0,fileLength);
            if (readLength<fileLength){
            System.out.println("讀取數據長度不正確");
            return null;
            }
            else{
            decryptBuff(fileBuff,readLength,header);
            return fileBuff;
            }
            }
            /**
            * 將buffer中的內容寫入到文件
            * @param fileBuff 保存文件內容的buffer
            * @param fileName 文件名
            * @param extractDir 文件導出目錄
            * @throws Exception
            */
            private void writeFileFromByteBuffer(byte[] fileBuff,String fileName,String extractDir) throws Exception{
            String extractFilePath=extractDir+fileName;
            File wFile=new File(extractFilePath);
            FileOutputStream fos=new FileOutputStream(wFile);
            DataOutputStream dos=new DataOutputStream(fos);
            dos.write(fileBuff);
            dos.close();
            fos.close();
            }
            /**
            * 從pak文件中取出指定的文件到byte數組,如果需要的話可以將byte數組寫為文件
            * @param pakFilePath pak文件路徑
            * @param extractFileName pak文件中將要被取出的文件名
            * @param writeFile 是否需要將byte數組寫為文件
            * @param extractDir 如果需要的話可以將byte數組寫為文件,extractDir為取出數據被寫的目錄文件
            * @return byte數組
            * @throws Exception
            */
            public byte[] extractFileFromPak(String pakFilePath,
            String extractFileName,boolean writeFile,String extractDir) throws Exception{
            File rFile=new File(pakFilePath);
            FileInputStream fis=new FileInputStream(rFile);
            DataInputStream dis=new DataInputStream(fis);
            PakHeader header=readHeader(dis);
            PakFileTable[] fileTable=readFileTable(dis,(int)header.getNumFileTableEntries());
            boolean find=false;        int fileIndex=0;
            for(int i=0;i<fileTable.length;i++){
            String fileName=new String(fileTable[i].getFileName()).trim();
            if (fileName.equals(extractFileName)){
            find=true;
            fileIndex=i;
            break;
            }
            }
            if (find==false){
            System.out.println("沒有找到指定的文件");
            return null;        
            }      
            else{
            byte[] buff=readFileFromPak(dis,header,fileTable[fileIndex]);
            if (writeFile){
            writeFileFromByteBuffer(buff,extractFileName,extractDir);
            }            
            else{
            dis.close();
            fis.close();
            }
            return buff;
            }
            }
            /**
            * 從pak文件中取出指定的Pak文件的信息
            * @param pakFilePath pak文件路徑
            * @return 裝載文件頭和文件table數組的Vector
            * @throws Exception
            */
            public Vector showPakFileInfo(String pakFilePath) throws Exception{
            File rFile=new File(pakFilePath);
            FileInputStream fis=new FileInputStream(rFile);
            DataInputStream dis=new DataInputStream(fis);
            PakHeader header=readHeader(dis);
            PakFileTable[] fileTable=readFileTable(dis,(int)header.getNumFileTableEntries());        Vector result=new Vector();
            result.add(header);
            result.add(fileTable);
            return result;
            }
            public static void main(String[] argv) throws Exception{
            PakUtil pu=new PakUtil();
            //構造文件頭
            char[] signature=new char[PakHeader.SIGNATURE_LENGTH];
            signature=new String("012345").toCharArray();
            char[] uniqueID=new char[PakHeader.UNIQUEID_LENGTH];
            uniqueID=new String("0123456789").toCharArray();
            PakHeader header=new PakHeader();
            header.setSignature(signature);
            header.setNumFileTableEntries(3);
            header.setCipherAction((byte)PakHeader.ADDITION_CIPHERACTION);
            header.setCipherValue((byte)0x0f);
            header.setUniqueID(uniqueID);
            header.setVersion(1.0f);
            header.setReserved(0L);
            String[] filePathArray={"F:\\eclipse3.1RC3\\workspace\\gmatrixProject_j2se\\testFiles\\apple.png",
            "F:\\eclipse3.1RC3\\workspace\\gmatrixProject_j2se\\testFiles\\cushaw.png",
            "F:\\eclipse3.1RC3\\workspace\\gmatrixProject_j2se\\testFiles\\flash.png"};
            String extractFilePath="F:\\eclipse3.1RC3\\workspace\\gmatrixProject_j2se\\testFiles\\test.pak";
            //制作Pak文件
            System.out.println("制作Pak文件...");
            pu.makePakFile(filePathArray,extractFilePath,header);
            System.out.println("制作Pak文件完成");
            //從Pak文件中取出所有的圖片文件
            Vector pakInfo=pu.showPakFileInfo(extractFilePath);
            header=(PakHeader)pakInfo.elementAt(0);
            System.out.println("Pak文件信息:");
            System.out.println("文件頭:");
            System.out.println(header);
            PakFileTable[] fileTable=(PakFileTable[])pakInfo.elementAt(1);
            for(int i=0;i<fileTable.length;i++){
            System.out.println("文件table["+i+"]:");
            System.out.println(fileTable[i]);
            }
            String restoreDir="F:\\eclipse3.1RC3\\workspace\\gmatrixProject_j2se\\testFiles\\extract\\";
            String restoreFileName=null;
            byte[] fileBuff=null;
            for(int i=0;i<fileTable.length;i++){
            restoreFileName=new String(fileTable[i].getFileName()).trim();
            System.out.println("從Pak文件中取出"+restoreFileName+"文件...");
            fileBuff=pu.extractFileFromPak(extractFilePath,restoreFileName,true,restoreDir);
            System.out.println("從Pak文件中取出"+restoreFileName+"文件保存在"+restoreDir+"目錄");
            }
            }}
            
            七、PakUtil類(j2me版):
            
            PakUtil.java
            package cn.org.matrix.gmatrix.gameLab.util.pak;
            import java.io.*;
            import java.util.Vector;
            /** * Pak工具類 * 功能: * 從Pak文件中取出png圖片,構造byte數組(可以用來構造Image對象) * @author cleverpig * */public class PakUtil {
            public PakUtil(){
            }
            /**
            * 計算文件位移的起始點
            * @return 文件位移的起始點
            */
            private long workOutOffsetStart(PakHeader header){
            //計算出文件頭+文件table的長度
            return PakHeader.size()+header.getNumFileTableEntries()*PakFileTable.size();
            }
            /**
            * 從DataInputStream讀取char數組
            * @param dis DataInputStream
            * @param readLength 讀取長度
            * @return char數組
            * @throws Exception
            */
            private char[] readCharArray(DataInputStream dis,int readLength) throws Exception{
            char[] readCharArray=new char[readLength];
            for(int i=0;i<readLength;i++){
            readCharArray[i]=dis.readChar();
            }
            return readCharArray;
            }
            /**
            * 從PAK文件中讀取文件頭
            * @param dis DataInputStream
            * @return PakHeader
            * @throws Exception
            */
            private PakHeader readHeader(DataInputStream dis) throws Exception{
            PakHeader header=new PakHeader();
            char[] signature=readCharArray(dis,PakHeader.SIGNATURE_LENGTH);
            header.setSignature(signature);
            header.setVersion(dis.readFloat());
            header.setNumFileTableEntries(dis.readLong());
            header.setCipherAction(dis.readByte());
            header.setCipherValue(dis.readByte());
            char[] uniqueID=readCharArray(dis,PakHeader.UNIQUEID_LENGTH);
            header.setUniqueID(uniqueID);
            header.setReserved(dis.readLong());
            return header;
            }
            /**
            * 讀取所有的文件table
            * @param dis DataInputStream
            * @param fileTableNumber 文件表總數
            * @return 文件table數組
            * @throws Exception
            */
            private PakFileTable[] readFileTable(DataInputStream dis,int fileTableNumber) throws Exception{
            PakFileTable[] fileTable=new PakFileTable[fileTableNumber];
            for(int i=0;i<fileTableNumber;i++){
            PakFileTable ft=new PakFileTable();
            ft.setFileName(readCharArray(dis,PakFileTable.FILENAME_LENGTH));
            ft.setFileSize(dis.readLong());
            ft.setOffSet(dis.readLong());
            fileTable[i]=ft;
            }
            return fileTable;
            }
            /**
            * 從pak文件讀取文件到byte數組
            * @param dis DataInputStream
            * @param fileTable PakFileTable
            * @return byte數組
            * @throws Exception
            */    
            private byte[] readFileFromPak(DataInputStream dis,PakHeader header,PakFileTable fileTable) throws Exception{
            dis.skip(fileTable.getOffSet()-workOutOffsetStart(header));
            //
            int fileLength=(int)fileTable.getFileSize();
            byte[] fileBuff=new byte[fileLength];
            int readLength=dis.read(fileBuff,0,fileLength);
            if (readLength<fileLength){
            System.out.println("讀取數據長度不正確");
            return null;
            }
            else{
            decryptBuff(fileBuff,readLength,header);
            }        
            return fileBuff;
            }
            /**
            * 使用文件頭中的密碼對數據進行解密
            * @param buff 被解密的數據
            * @param buffLength 數據的長度
            * @param header 文件頭
            */
            private void decryptBuff(byte[] buff,int buffLength,PakHeader header){
            for(int i=0;i<buffLength;i++){
            switch(header.getCipherAction()){
            case PakHeader.ADDITION_CIPHERACTION:
            buff[i]-=header.getCipherValue();
            break;
            case PakHeader.SUBTRACT_CIHOERACTION:
            buff[i]+=header.getCipherValue();
            break;
            }
            }
            }
            /**
            * 從pak文件中取出指定的文件到byte數組
            * @param pakResourceURL pak文件的資源路徑
            * @param extractResourceName pak文件中將要被取出的文件名
            * @return byte數組    
            * @throws Exception
            */    
            public byte[] extractResourceFromPak(String pakResourceURL
            ,String extractResourceName) throws Exception{
            InputStream is=this.getClass().getResourceAsStream(pakResourceURL);
            DataInputStream dis=new DataInputStream(is);
            PakHeader header=readHeader(dis);//
            System.out.println("文件頭:");//
            System.out.println(header);
            PakFileTable[] fileTable=readFileTable(dis,(int)header.getNumFileTableEntries());//
            for(int i=0;i<fileTable.length;i++){//
            System.out.println("文件table["+i+"]:");//
            System.out.println(fileTable[i]);//
            }
            boolean find=false;
            int fileIndex=0;
            for(int i=0;i<fileTable.length;i++){
            String fileName=new String(fileTable[i].getFileName()).trim();
            if (fileName.equals(extractResourceName)){
            find=true;
            fileIndex=i;
            break;
            }
            }
            if (find==false){
            System.out.println("沒有找到指定的文件");
            return null;
            }
            else{
            byte[] buff=readFileFromPak(dis,header,fileTable[fileIndex]);
            return buff;
            }
            }
            /**
            * 從pak文件中取出指定的Pak文件的信息
            * @param pakResourcePath pak文件資源路徑
            * @return 裝載文件頭和文件table數組的Vector
            * @throws Exception
            */
            public Vector showPakFileInfo(String pakResourcePath) throws Exception{
            InputStream is=this.getClass().getResourceAsStream(pakResourcePath);
            DataInputStream dis=new DataInputStream(is);
            PakHeader header=readHeader(dis);
            PakFileTable[] fileTable=readFileTable(dis,(int)header.getNumFileTableEntries());
            Vector result=new Vector();
            result.addElement(header);
            result.addElement(fileTable);
            return result;
            }
            public static void main(String[] argv) throws Exception{
            PakUtil pu=new PakUtil();
            String extractResourcePath="/test.pak";
            //從Pak文件中取出所有的圖片文件
            Vector pakInfo=pu.showPakFileInfo(extractResourcePath);
            PakHeader header=(PakHeader)pakInfo.elementAt(0);
            System.out.println("Pak文件信息:");
            System.out.println("文件頭:");
            System.out.println(header);
            PakFileTable[] fileTable=(PakFileTable[])pakInfo.elementAt(1);
            for(int i=0;i<fileTable.length;i++){
            System.out.println("文件table["+i+"]:");
            System.out.println(fileTable[i]);
            }
            String restoreFileName=null;       
            byte[] fileBuff=null;
            for(int i=0;i<fileTable.length;i++){
            restoreFileName=new String(fileTable[i].getFileName()).trim();
            System.out.println("從Pak文件中取出"+restoreFileName+"文件數據...");
            fileBuff=pu.extractResourceFromPak(extractResourcePath,restoreFileName);
            System.out.println("從Pak文件中取出"+restoreFileName+"文件數據完成");
            }   
             }}
            
            八、源代碼使用簡介:
            
            Pak過程:j2se版的PakUtil將testFiles目錄中的三個png文件Pak成為test.pak文件。
            UnPak過程:j2se版的PakUtil將testFiles目錄中test.pak文件釋放到testFiles\extract目錄下;j2me版的PakUtil從res目錄中的test.pak文件讀取出其中所包含的3個png文件數據并裝入到byte數據,用來構造Image對象,大家請運行PakUtilTestMIDlet.java便可看到輸出的信息。
          posted on 2007-02-13 17:23 SIMONE 閱讀(441) 評論(0)  編輯  收藏 所屬分類: 收藏
          主站蜘蛛池模板: 封丘县| 葵青区| 大庆市| 丽江市| 上犹县| 两当县| 康马县| 宿松县| 景洪市| 班戈县| 安龙县| 兴海县| 财经| 高碑店市| 瑞金市| 芜湖县| 九江县| 原平市| 武夷山市| 临江市| 彰化县| 瑞丽市| 伽师县| 安乡县| 天台县| 六枝特区| 黄浦区| 嘉义市| 华池县| 三河市| 盐源县| 游戏| 连江县| 新乡市| 隆子县| 三穗县| 兴海县| 裕民县| 乌恰县| 绥德县| 铜梁县|