程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> J2ME >> J2me技巧—跟我學制作Pak文件

J2me技巧—跟我學制作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便可看到輸出的信息

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved