WEB开发网
开发学院软件开发Java 跟我学制作Pak文件 阅读

跟我学制作Pak文件

 2007-12-23 12:30:52 来源:WEB开发网   
核心提示:版权声明:本文可以自由转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明作者:cleverpig(http://blog.matrix.org.cn/page/cleverpig)原文:http://www.matrix.org.cn/resource/article/43/43966_J2ME_Pak.

  版权声明:本文可以自由转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
作者:cleverpig(http://blog.matrix.org.cn/page/cleverpig)
原文:http://www.matrix.org.cn/resource/article/43/43966_J2ME_Pak.Html
关键字:pak,j2me,减肥


序言:

  由于前些时间,一些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便可看到输出的信息。

九、源代码下载:

j2se版源代码
Download File
j2sme版源代码 Download File 资源
·CleverPig的blog:http://blog.matrix.org.cn/page/cleverpig
·Matrix-Java开发者社区:http://www.matrix.org.cn/
·ProGuard
·j2me.org上的Topic: Give me all your tricks for minimizing jar file size

(出处:http://www.cncms.com)


Tags:跟我学 制作 Pak

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接