/** * <This class for resolve UMD file.> * Copyright (C) <2009> <mingkg21,ACC http://androidos.cc/dev> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ package org.geometerplus.fbreader.formats.umd; import java.io.BufferedWriter; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.zip.Inflater; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; /** * @author mingkg21 * Date: 2009-4-8 */ public class UMDFile { /** UMD file format */ public static final long UMD_FORMAT = 0xde9a9b89L; /** UMD file type: text and picture */ public static final byte UMD_BOOK_TYPE_TEXT = 1; public static final byte UMD_BOOK_TYPE_PICTURE = 2; public int contentLength; private long additionalCheck; /** UMD file info, eg: book title, author, publisher */ public BookInfo bookInfo; public int[] chapOff; private ArrayList<String> chapters; private ArrayList<Content> contentArr; private long currentPoint; // private InputStream is; private String bookPath; public UMDFile() { bookInfo = new BookInfo(); this.chapters = new ArrayList<String>(); this.contentArr = new ArrayList<Content>(); } private long readUInt32(DataInputStream dis) throws Exception{ return IntergerUtil.int2long(IntergerUtil.getInt(readBytes(dis, 4))); } private int readInt32(DataInputStream dis) throws Exception{ return IntergerUtil.getInt(readBytes(dis, 4)); } private short readInt16(DataInputStream dis) throws Exception{ return IntergerUtil.getShort(readBytes(dis, 2)); } private byte[] readBytes(DataInputStream dis, int num) throws Exception{ if(num <= 0 || null == dis){ return null; } byte[] value = new byte[num]; long temp = dis.read(value); currentPoint += temp; return value; } private void skipBytes(DataInputStream dis, int num) throws Exception{ if(num <= 0 || null == dis){ return ; } // byte[] value = new byte[num]; long temp = dis.skipBytes(num); currentPoint += temp; // return value; } private int readBytes(DataInputStream dis, byte[] bytes) throws Exception{ currentPoint += bytes.length; return dis.read(bytes); } private byte readByte(DataInputStream dis) throws Exception{ currentPoint += 1; return dis.readByte(); } private String readString(DataInputStream dis, byte length) throws Exception{ return new String(IntergerUtil.getReverseBytes(readBytes(dis, length)), "UNICODE"); } /** get the cover image of the UMD file * @return */ public Bitmap getCoverImage(){ if(bookInfo == null){ return null; } if(bookInfo.cover == null){ return null; } Bitmap bitmap = BitmapFactory.decodeByteArray(bookInfo.cover, 0, bookInfo.cover.length); return bitmap; } // public Drawable getDrawable(int index){ // byte[] bytes = getContentBytes(index); // if(bytes == null){ // return null; // } // Drawable drawable = Drawable // } /** if the type of the UMD file is picture, then content is picture. get the picture from content * @param index * @return */ public Bitmap getBitmap(int index){ byte[] bytes = getContentBytes(index); if(bytes == null){ return null; } return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); } public int getContentSize(){ if(contentArr == null){ return 0; } return contentArr.size(); } public int getChapterSize(){ if(chapters == null){ return 0; } return chapters.size(); } public ArrayList<String> getChapters(){ return chapters; } public String getContentText(int index){ String tempStr = null; try { byte[] newBytes = new byte[0x8000]; Inflater inflater1 = new Inflater(); inflater1.setInput(getContentBytes(index)); inflater1.inflate(newBytes); byte[] strBytes = new byte[inflater1.getTotalOut()]; System.arraycopy(newBytes, 0, strBytes, 0, Math.min(strBytes.length, inflater1.getTotalOut())); tempStr = new String(IntergerUtil.getReverseBytes(strBytes), "UNICODE").replace("\u2029", "\n"); } catch (Exception e) { // e.printStackTrace(); } return tempStr; } /** if the type of the UMD file is text, * @param context */ public void writeFile(Context context){ try { InputStream is = new DataInputStream(new FileInputStream(bookPath)); long skipNum = 0; long nn = 0; int tempI = 0; byte[] copyByte = null; int copyLength = 0; for(int j = 0; j < this.chapOff.length; ++j){ int start = this.chapOff[j]; int strByteL = 0; if(j < this.chapOff.length - 1){ strByteL = this.chapOff[j+1] - start; }else{ strByteL = this.contentLength - start; } int tempL = 0; byte[] strBytes = new byte[strByteL]; if(copyByte != null){ tempL = copyByte.length - copyLength; System.arraycopy(copyByte, copyLength, strBytes, 0, tempL); } for(int i = tempI; i < contentArr.size(); ++i){ long index = contentArr.get(i).getIndex(); skipNum = index - nn; is.skip(skipNum); int length = (int) contentArr.get(i).getLength(); nn = index + length; byte[] bytes = new byte[length]; is.read(bytes); byte[] newBytes = new byte[0x8000]; Inflater inflater1 = new Inflater(); inflater1.setInput(bytes); inflater1.inflate(newBytes); if(tempL < strBytes.length){ copyLength = Math.min(strBytes.length - tempL, inflater1.getTotalOut()); System.arraycopy(newBytes, 0, strBytes, tempL, copyLength); tempL += inflater1.getTotalOut(); if(tempL >= strBytes.length){ copyByte = newBytes; tempI = i + 1; break; } } } String content = new String(IntergerUtil.getReverseBytes(strBytes), "UNICODE").replace("\u2029", "\n"); // file path: /sdcard/cr_temp/crbookXX.txt String folderPath = "/sdcard/cr_temp"; File cr_temp = new File(folderPath); if(!cr_temp.exists()){ cr_temp.mkdir(); } StringBuffer sb = new StringBuffer(); sb.append(folderPath); sb.append("/crbook"); sb.append(j); sb.append(".txt"); // String fileName = "/sdcard/tempfile" + j + ".txt"; FileOutputStream fos = new FileOutputStream(sb.toString()); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos)); bw.write(content); bw.flush(); bw.close(); fos.close(); } is.close(); } catch (Exception e) { // e.printStackTrace(); } } public byte[] getContentBytes(int index){ byte[] bytes = null; InputStream is = null; try{ is = new DataInputStream(new FileInputStream(bookPath)); long skipLength= contentArr.get(index).getIndex(); is.skip(skipLength); int length = (int) contentArr.get(index).getLength(); bytes = new byte[length]; is.read(bytes); }catch(Exception e){ e.printStackTrace(); }finally{ if(is != null){ try { is.close(); } catch (IOException e) { // e.printStackTrace(); } } } return bytes; } public boolean readmeta(String umdFile){ bookPath = umdFile; InputStream is = null; try { is = new DataInputStream(new FileInputStream(bookPath)); return readmeta(is); } catch (Exception e) { // e.printStackTrace(); }finally{ if(is != null){ try { is.close(); } catch (IOException e) { // e.printStackTrace(); } } } return false; } public boolean readmeta(InputStream is){ try { DataInputStream dis = new DataInputStream(is); long header; //check UMD format header = readUInt32(dis); if(UMD_FORMAT != header){ return false; // throw new Exception("invalid file format\n"); } byte[] symbol = new byte[1]; // int eof = dis.read(symbol); int eof = readBytes(dis, symbol); while(eof != -1 && 0x23 == symbol[0]){//0x23 '#' short id = readInt16(dis); // byte num3 = dis.readByte(); // byte length = (byte) (dis.readByte() - 5); byte num3 = readByte(dis); byte length = (byte) (readByte(dis) - 5); readSection(id, num3, length, dis); // eof = dis.read(symbol); if(id==4){ return true;//读到vender就退出 } eof = readBytes(dis, symbol); if((0xf1 == id) || (10 == id)){ id = 0x84; } while(eof != -1 && '$' == symbol[0]){ long num5 = readUInt32(dis); long num6 = readUInt32(dis) - 9; readAdditional(id, num5, num6, dis); // eof = dis.read(symbol); eof = readBytes(dis, symbol); } } dis.close(); // is.close(); } catch (Exception e) { // System.out.println("read errs " + e); e.printStackTrace(); return false; } return true; } public boolean readcover(String umdFile ,long size){ bookPath = umdFile; InputStream is = null; try { is = new DataInputStream(new FileInputStream(bookPath)); return readcover(is,size); } catch (Exception e) { // e.printStackTrace(); }finally{ if(is != null){ try { is.close(); } catch (IOException e) { // e.printStackTrace(); } } } return false; } public boolean readcover(InputStream is,long size){ try { DataInputStream dis = new DataInputStream(is); long header; //check UMD format header = readUInt32(dis); if(UMD_FORMAT != header){ return false; // throw new Exception("invalid file format\n"); } byte[] symbol = new byte[1]; // int eof = dis.read(symbol); int eof = readBytes(dis, symbol); while(eof != -1 && 0x23 == symbol[0]){//0x23 '#' short id = readInt16(dis); // byte num3 = dis.readByte(); // byte length = (byte) (dis.readByte() - 5); byte num3 = readByte(dis); byte length = (byte) (readByte(dis) - 5); readSection(id, num3, length, dis); // eof = dis.read(symbol); eof = readBytes(dis, symbol); if(id!=130){//不是 封面 id = 0xff;//下面执行默认的 就是跳过 } while(eof != -1 && '$' == symbol[0]){ long num5 = readUInt32(dis); long num6 = readUInt32(dis) - 9; readAdditional(id, num5, num6, dis); if(id==130){//读到封面就ok return true; } eof = readBytes(dis, symbol); } } dis.close(); // is.close(); } catch (Exception e) { // System.out.println("read errs " + e); e.printStackTrace(); return false; } return true; } public boolean read(String umdFile){ bookPath = umdFile; InputStream is = null; try { is = new DataInputStream(new FileInputStream(bookPath)); return read(is); } catch (Exception e) { // e.printStackTrace(); }finally{ if(is != null){ try { is.close(); } catch (IOException e) { // e.printStackTrace(); } } } return false; } /** parse the UMD file and read the content of UMD file * @param is */ public boolean read(InputStream is){ try { DataInputStream dis = new DataInputStream(is); long header; //check UMD format header = readUInt32(dis); if(UMD_FORMAT != header){ return false; // throw new Exception("invalid file format\n"); } byte[] symbol = new byte[1]; // int eof = dis.read(symbol); int eof = readBytes(dis, symbol); while(eof != -1 && 0x23 == symbol[0]){//0x23 '#' short id = readInt16(dis); // byte num3 = dis.readByte(); // byte length = (byte) (dis.readByte() - 5); byte num3 = readByte(dis); byte length = (byte) (readByte(dis) - 5); readSection(id, num3, length, dis); // System.out.println("---hym umd- section:"+id); // eof = dis.read(symbol); eof = readBytes(dis, symbol); // dis. if((0xf1 == id) || (10 == id)){ id = 0x84; } while(eof != -1 && '$' == symbol[0]){ long num5 = readUInt32(dis); long num6 = readUInt32(dis) - 9; readAdditional(id, num5, num6, dis); // System.out.println("---hym umd- Additional:"+id); // eof = dis.read(symbol); eof = readBytes(dis, symbol); } } dis.close(); // is.close(); } catch (Exception e) { // System.out.println("read errs " + e); e.printStackTrace(); return false; } return true; } /** read the content * @param id * @param check * @param length * @param dis * @throws Exception */ private void readAdditional(short id, long check, long length, DataInputStream dis) throws Exception{ int num1; int num2; byte[] buffer1; switch(id){ case 0x0E://only picture if(UMD_BOOK_TYPE_PICTURE == bookInfo.type){ this.contentArr.add(new Content(currentPoint, length)); } readBytes(dis, (int) length); return; case 0x0F://only text return; case 0x81: readBytes(dis, (int) length); return; case 130://cover image this.bookInfo.cover = readBytes(dis, (int) length); // System.out.println("----hym umd cover size:"+length); return; case 0x83://each chapter length int chapOffLen = (int) (length / 4); this.chapOff = new int[chapOffLen]; num1 = 0; while(num1 < chapOffLen){ this.chapOff[num1] = readInt32(dis); num1++; } return; case 0x84: //read the content 内容 if(this.additionalCheck != check){ this.contentArr.add(new Content(currentPoint, length)); readBytes(dis, (int) length); return; } num2 = 0; buffer1 = readBytes(dis, (int) length); if(null == buffer1){ return; } //read the chapter's name while(num2 < buffer1.length){ byte num3 = buffer1[num2]; byte[] temp = new byte[num3]; ++num2; for(int i = 0; i < num3; ++i){ temp[i] = buffer1[i+num2]; } this.chapters.add(new String(IntergerUtil.getReverseBytes(temp), "UNICODE")); num2 += num3; } return; default: skipBytes(dis, (int) length); // dis.skipBytes((int) length); return; } } private void readSection(short id, byte b, byte length, DataInputStream dis) throws Exception{ switch(id){ case 1://type this.bookInfo.type = readByte(dis); this.bookInfo.pgkSeed = readInt16(dis); return; case 2://title this.bookInfo.title = readString(dis, length); return; case 3://author this.bookInfo.author = readString(dis, length); return; case 4://year this.bookInfo.year = readString(dis, length); return; case 5://month this.bookInfo.month = readString(dis, length); return; case 6://day this.bookInfo.day = readString(dis, length); return; case 7://gender this.bookInfo.gender = readString(dis, length); return; case 8://publisher this.bookInfo.publisher = readString(dis, length); return; case 9://vendor this.bookInfo.vendor = readString(dis, length); return; case 10: this.bookInfo.cid = readInt32(dis); return; case 11:// this.contentLength = readInt32(dis); return; case 12: readUInt32(dis); return; case 0x81: case 0x83: case 0x84: this.additionalCheck = readUInt32(dis); return; case 0x0E: readByte(dis); return; case 0x0F: readByte(dis); return; case 130: readByte(dis); this.additionalCheck = readUInt32(dis); return; } skipBytes(dis, length); } }