this is a extra element for clear the floated element
J2me技术——跟我学制作Pak文件
  • 12/31
  • 2008
无线开发 | Java 2058 次查看
  序言:

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

更多相关内容