From 9062fc5e657c68182be59a5d3370a1875284a14d Mon Sep 17 00:00:00 2001 From: ag2s20150909 Date: Fri, 2 Jul 2021 13:09:11 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0Umd=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 6 +- app/libs/umdlib-debug.aar | Bin 0 -> 18056 bytes .../io/legado/app/model/localBook/UmdFile.kt | 137 +++++++++++ .../java/me/ag2s/umdlib/domain/UmdBook.java | 81 +++++++ .../me/ag2s/umdlib/domain/UmdChapters.java | 215 +++++++++++++++++ .../java/me/ag2s/umdlib/domain/UmdCover.java | 96 ++++++++ .../java/me/ag2s/umdlib/domain/UmdEnd.java | 20 ++ .../java/me/ag2s/umdlib/domain/UmdHeader.java | 162 +++++++++++++ .../me/ag2s/umdlib/tool/StreamReader.java | 124 ++++++++++ .../java/me/ag2s/umdlib/tool/UmdUtils.java | 159 ++++++++++++ .../me/ag2s/umdlib/tool/WrapOutputStream.java | 91 +++++++ .../java/me/ag2s/umdlib/umd/UmdReader.java | 227 ++++++++++++++++++ 12 files changed, 1315 insertions(+), 3 deletions(-) create mode 100644 app/libs/umdlib-debug.aar create mode 100644 app/src/main/java/io/legado/app/model/localBook/UmdFile.kt create mode 100644 epublib/src/main/java/me/ag2s/umdlib/domain/UmdBook.java create mode 100644 epublib/src/main/java/me/ag2s/umdlib/domain/UmdChapters.java create mode 100644 epublib/src/main/java/me/ag2s/umdlib/domain/UmdCover.java create mode 100644 epublib/src/main/java/me/ag2s/umdlib/domain/UmdEnd.java create mode 100644 epublib/src/main/java/me/ag2s/umdlib/domain/UmdHeader.java create mode 100644 epublib/src/main/java/me/ag2s/umdlib/tool/StreamReader.java create mode 100644 epublib/src/main/java/me/ag2s/umdlib/tool/UmdUtils.java create mode 100644 epublib/src/main/java/me/ag2s/umdlib/tool/WrapOutputStream.java create mode 100644 epublib/src/main/java/me/ag2s/umdlib/umd/UmdReader.java diff --git a/app/build.gradle b/app/build.gradle index 0e8c4aa3d..1cf1e1e41 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -124,10 +124,10 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" //androidX - implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'androidx.core:core-ktx:1.3.2' + implementation 'androidx.appcompat:appcompat:1.3.0' + implementation 'androidx.core:core-ktx:1.5.0' implementation "androidx.activity:activity-ktx:1.2.3" - implementation "androidx.fragment:fragment-ktx:1.3.3" + implementation "androidx.fragment:fragment-ktx:1.3.5" implementation 'androidx.preference:preference-ktx:1.1.1' implementation "androidx.collection:collection-ktx:1.1.0" implementation 'androidx.constraintlayout:constraintlayout:2.0.4' diff --git a/app/libs/umdlib-debug.aar b/app/libs/umdlib-debug.aar new file mode 100644 index 0000000000000000000000000000000000000000..1f8be1b3baf7310655590eaa916de25cf9a1a397 GIT binary patch literal 18056 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 nK#dXQ>=Ja1=z)yTxWa{kAP?|n1@4z;U| { + return getEFile(book).getChapterList() + } + + @Synchronized + fun getContent(book: Book, chapter: BookChapter): String? { + return getEFile(book).getContent(chapter) + } + + @Synchronized + fun getImage( + book: Book, + href: String + ): InputStream? { + return getEFile(book).getImage(href) + } + + + @Synchronized + fun upBookInfo(book: Book) { + return getEFile(book).upBookInfo() + } + } + + + + private var umdBook: UmdBook? = null + get() { + if (field != null) { + return field + } + field = readUmd() + return field + } + + + + init { + try { + umdBook?.let { + if (book.coverUrl.isNullOrEmpty()) { + book.coverUrl = FileUtils.getPath( + appCtx.externalFilesDir, + "covers", + "${MD5Utils.md5Encode16(book.bookUrl)}.jpg" + ) + } + if (!File(book.coverUrl!!).exists()) { + FileUtils.writeBytes(book.coverUrl!!,it.cover.coverData) + + } + } + } catch (e: Exception) { + e.printStackTrace() + } + } + private fun readUmd(): UmdBook? { + val input= if (book.bookUrl.isContentScheme()) { + val uri = Uri.parse(book.bookUrl) + appCtx.contentResolver.openInputStream(uri) + } else { + File(book.bookUrl).inputStream() + } + return UmdReader().read(input) + } + + private fun upBookInfo() { + if(umdBook==null){ + eFile = null + book.intro = "书籍导入异常" + }else{ + val hd= umdBook!!.header + book.name=hd.title; + book.author=hd.author; + book.kind=hd.bookType; + } + } + private fun getContent(chapter: BookChapter): String? { + return umdBook?.chapters?.getContentString(chapter.index) + } + + private fun getChapterList(): ArrayList { + val chapterList = ArrayList() + umdBook?.chapters?.titles?.forEachIndexed { index, bytes -> + val title = umdBook!!.chapters.getTitle(index) + val chapter = BookChapter() + chapter.title=title; + chapter.index = index + chapter.bookUrl = book.bookUrl + chapter.url = index.toString(); + chapterList.add(chapter) + } + return chapterList + } + + private fun getImage(href: String): InputStream? { + TODO("Not yet implemented") + } + + +} \ No newline at end of file diff --git a/epublib/src/main/java/me/ag2s/umdlib/domain/UmdBook.java b/epublib/src/main/java/me/ag2s/umdlib/domain/UmdBook.java new file mode 100644 index 000000000..14f1f895d --- /dev/null +++ b/epublib/src/main/java/me/ag2s/umdlib/domain/UmdBook.java @@ -0,0 +1,81 @@ +package me.ag2s.umdlib.domain; + +import java.io.IOException; +import java.io.OutputStream; + +import me.ag2s.umdlib.tool.WrapOutputStream; + +public class UmdBook { + + public int getNum() { + return num; + } + + public void setNum(int num) { + this.num = num; + } + + private int num; + + + /** Header Part of UMD book */ + private UmdHeader header = new UmdHeader(); + /** + * Detail chapters Part of UMD book + * (include Titles & Contents of each chapter) + */ + private UmdChapters chapters = new UmdChapters(); + + /** Cover Part of UMD book (for example, and JPEG file) */ + private UmdCover cover = new UmdCover(); + + /** End Part of UMD book */ + private UmdEnd end = new UmdEnd(); + + /** + * Build the UMD file. + * @param os + * @throws IOException + */ + public void buildUmd(OutputStream os) throws IOException { + WrapOutputStream wos = new WrapOutputStream(os); + + header.buildHeader(wos); + chapters.buildChapters(wos); + cover.buildCover(wos); + end.buildEnd(wos); + } + + public UmdHeader getHeader() { + return header; + } + + public void setHeader(UmdHeader header) { + this.header = header; + } + + public UmdChapters getChapters() { + return chapters; + } + + public void setChapters(UmdChapters chapters) { + this.chapters = chapters; + } + + public UmdCover getCover() { + return cover; + } + + public void setCover(UmdCover cover) { + this.cover = cover; + } + + public UmdEnd getEnd() { + return end; + } + + public void setEnd(UmdEnd end) { + this.end = end; + } + +} diff --git a/epublib/src/main/java/me/ag2s/umdlib/domain/UmdChapters.java b/epublib/src/main/java/me/ag2s/umdlib/domain/UmdChapters.java new file mode 100644 index 000000000..b899e841f --- /dev/null +++ b/epublib/src/main/java/me/ag2s/umdlib/domain/UmdChapters.java @@ -0,0 +1,215 @@ +package me.ag2s.umdlib.domain; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.zip.DeflaterOutputStream; + +import me.ag2s.umdlib.tool.UmdUtils; +import me.ag2s.umdlib.tool.WrapOutputStream; + +/** + * It includes all titles and contents of each chapter in the UMD file. + * And the content has been compressed by zlib. + * + * @author Ray Liang (liangguanhui@qq.com) + * 2009-12-20 + */ +public class UmdChapters { + + private static final int DEFAULT_CHUNK_INIT_SIZE = 32768; + private int TotalContentLen; + + public List getTitles() { + return titles; + } + + private List titles = new ArrayList(); + public List contentLengths = new ArrayList(); + public ByteArrayOutputStream contents = new ByteArrayOutputStream(); + + public void addTitle(String s){ + titles.add(UmdUtils.stringToUnicodeBytes(s)); + } + public void addTitle(byte[] s){ + titles.add(s); + } + public void addContentLength(Integer integer){ + contentLengths.add(integer); + } + public int getContentLength(int index){ + return contentLengths.get(index); + } + + public byte[] getContent(int index) throws Exception { + int st=contentLengths.get(index); + byte[] b=contents.toByteArray(); + int end=index+1 chunkRbList = new ArrayList(); + + while(startPos < allContents.length) { + left = allContents.length - startPos; + len = DEFAULT_CHUNK_INIT_SIZE < left ? DEFAULT_CHUNK_INIT_SIZE : left; + + bos.reset(); + DeflaterOutputStream zos = new DeflaterOutputStream(bos); + zos.write(allContents, startPos, len); + zos.close(); + byte[] chunk = bos.toByteArray(); + + byte[] rb = UmdUtils.genRandomBytes(4); + wos.writeByte('$'); + wos.writeBytes(rb); // 4 random + chunkRbList.add(rb); + wos.writeInt(chunk.length + 9); + wos.write(chunk); + + // end of each chunk + wos.writeBytes('#', 0xF1, 0, 0, 0x15); + wos.write(zero16); + + startPos += len; + chunkCnt++; + } + + // end of all chunks + wos.writeBytes('#', 0x81, 0, 0x01, 0x09); + wos.writeBytes(0, 0, 0, 0); //random numbers + wos.write('$'); + wos.writeBytes(0, 0, 0, 0); //random numbers + wos.writeInt(chunkCnt * 4 + 9); + for (int i = chunkCnt - 1; i >= 0; i--) { + // random. They are as the same as random numbers in the begin of each chunk + // use desc order to output these random + wos.writeBytes(chunkRbList.get(i)); + } + } + + public void addChapter(String title, String content) { + titles.add(UmdUtils.stringToUnicodeBytes(title)); + byte[] b = UmdUtils.stringToUnicodeBytes(content); + contentLengths.add(b.length); + try { + contents.write(b); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void addFile(File f, String title) throws IOException { + byte[] temp = UmdUtils.readFile(f); + String s = new String(temp); + addChapter(title, s); + } + + public void addFile(File f) throws IOException { + String s = f.getName(); + int idx = s.lastIndexOf('.'); + if (idx >= 0) { + s = s.substring(0, idx); + } + addFile(f, s); + } + + public void clearChapters() { + titles.clear(); + contentLengths.clear(); + contents.reset(); + } + + public int getTotalContentLen() { + return TotalContentLen; + } + + public void setTotalContentLen(int totalContentLen) { + TotalContentLen = totalContentLen; + } +} diff --git a/epublib/src/main/java/me/ag2s/umdlib/domain/UmdCover.java b/epublib/src/main/java/me/ag2s/umdlib/domain/UmdCover.java new file mode 100644 index 000000000..b3c155300 --- /dev/null +++ b/epublib/src/main/java/me/ag2s/umdlib/domain/UmdCover.java @@ -0,0 +1,96 @@ +package me.ag2s.umdlib.domain; + + +import java.io.File; +import java.io.IOException; + +import me.ag2s.umdlib.tool.UmdUtils; +import me.ag2s.umdlib.tool.WrapOutputStream; + + +/** + * This is the cover part of the UMD file. + *

+ * NOTICE: if the "coverData" is empty, it will be skipped when building UMD file. + *

+ * There are 3 ways to load the image data: + *
    + *
  1. new constructor function of UmdCover.
  2. + *
  3. use UmdCover.load function.
  4. + *
  5. use UmdCover.initDefaultCover, it will generate a simple image with text.
  6. + *
+ * @author Ray Liang (liangguanhui@qq.com) + * 2009-12-20 + */ +public class UmdCover { + + private static int DEFAULT_COVER_WIDTH = 120; + private static int DEFAULT_COVER_HEIGHT = 160; + + private byte[] coverData; + + public UmdCover() { + } + + public UmdCover(byte[] coverData) { + this.coverData = coverData; + } + + public void load(File f) throws IOException { + this.coverData = UmdUtils.readFile(f); + } + + public void load(String fileName) throws IOException { + load(new File(fileName)); + } + + public void initDefaultCover(String title) throws IOException { +// BufferedImage img = new BufferedImage(DEFAULT_COVER_WIDTH, DEFAULT_COVER_HEIGHT, BufferedImage.TYPE_INT_RGB); +// Graphics g = img.getGraphics(); +// g.setColor(Color.BLACK); +// g.fillRect(0, 0, img.getWidth(), img.getHeight()); +// g.setColor(Color.WHITE); +// g.setFont(new Font("����", Font.PLAIN, 12)); +// +// FontMetrics fm = g.getFontMetrics(); +// int ascent = fm.getAscent(); +// int descent = fm.getDescent(); +// int strWidth = fm.stringWidth(title); +// int x = (img.getWidth() - strWidth) / 2; +// int y = (img.getHeight() - ascent - descent) / 2; +// g.drawString(title, x, y); +// g.dispose(); +// +// ByteArrayOutputStream baos = new ByteArrayOutputStream(); +// +// JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(baos); +// JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(img); +// param.setQuality(0.5f, false); +// encoder.setJPEGEncodeParam(param); +// encoder.encode(img); +// +// coverData = baos.toByteArray(); + } + + public void buildCover(WrapOutputStream wos) throws IOException { + if (coverData == null || coverData.length == 0) { + return; + } + wos.writeBytes('#', 0x82, 0, 0x01, 0x0A, 0x01); + byte[] rb = UmdUtils.genRandomBytes(4); + wos.writeBytes(rb); //random numbers + wos.write('$'); + wos.writeBytes(rb); //random numbers + wos.writeInt(coverData.length + 9); + wos.write(coverData); + } + + public byte[] getCoverData() { + return coverData; + } + + public void setCoverData(byte[] coverData) { + this.coverData = coverData; + } + +} diff --git a/epublib/src/main/java/me/ag2s/umdlib/domain/UmdEnd.java b/epublib/src/main/java/me/ag2s/umdlib/domain/UmdEnd.java new file mode 100644 index 000000000..129712f49 --- /dev/null +++ b/epublib/src/main/java/me/ag2s/umdlib/domain/UmdEnd.java @@ -0,0 +1,20 @@ +package me.ag2s.umdlib.domain; + +import java.io.IOException; + +import me.ag2s.umdlib.tool.WrapOutputStream; + +/** + * End part of UMD book, nothing to be special + * + * @author Ray Liang (liangguanhui@qq.com) + * 2009-12-20 + */ +public class UmdEnd { + + public void buildEnd(WrapOutputStream wos) throws IOException { + wos.writeBytes('#', 0x0C, 0, 0x01, 0x09); + wos.writeInt(wos.getWritten() + 4); + } + +} diff --git a/epublib/src/main/java/me/ag2s/umdlib/domain/UmdHeader.java b/epublib/src/main/java/me/ag2s/umdlib/domain/UmdHeader.java new file mode 100644 index 000000000..389ea388f --- /dev/null +++ b/epublib/src/main/java/me/ag2s/umdlib/domain/UmdHeader.java @@ -0,0 +1,162 @@ +package me.ag2s.umdlib.domain; + + +import java.io.IOException; + +import me.ag2s.umdlib.tool.UmdUtils; +import me.ag2s.umdlib.tool.WrapOutputStream; + +/** + * Header of UMD file. + * It includes a lot of properties of header. + * All the properties are String type. + * + * @author Ray Liang (liangguanhui@qq.com) + * 2009-12-20 + */ +public class UmdHeader { + public byte getUmdType() { + return umdType; + } + + public void setUmdType(byte umdType) { + this.umdType = umdType; + } + + private byte umdType; + private String title; + + private String author; + + private String year; + + private String month; + + private String day; + + private String bookType; + + private String bookMan; + + private String shopKeeper; + private final static byte B_type_umd = (byte) 0x01; + private final static byte B_type_title = (byte) 0x02; + private final static byte B_type_author = (byte) 0x03; + private final static byte B_type_year = (byte) 0x04; + private final static byte B_type_month = (byte) 0x05; + private final static byte B_type_day = (byte) 0x06; + private final static byte B_type_bookType = (byte) 0x07; + private final static byte B_type_bookMan = (byte) 0x08; + private final static byte B_type_shopKeeper = (byte) 0x09; + + public void buildHeader(WrapOutputStream wos) throws IOException { + wos.writeBytes(0x89, 0x9b, 0x9a, 0xde); // UMD file type flags + wos.writeByte('#'); + wos.writeBytes(0x01, 0x00, 0x00, 0x08); // Unknown + wos.writeByte(0x01); //0x01 is text type; while 0x02 is Image type. + wos.writeBytes(UmdUtils.genRandomBytes(2)); //random number + + // start properties output + buildType(wos, B_type_title, getTitle()); + buildType(wos, B_type_author, getAuthor()); + buildType(wos, B_type_year, getYear()); + buildType(wos, B_type_month, getMonth()); + buildType(wos, B_type_day, getDay()); + buildType(wos, B_type_bookType, getBookType()); + buildType(wos, B_type_bookMan, getBookMan()); + buildType(wos, B_type_shopKeeper, getShopKeeper()); + } + + public void buildType(WrapOutputStream wos, byte type, String content) throws IOException { + if (content == null || content.length() == 0) { + return; + } + + wos.writeBytes('#', type, 0, 0); + + byte[] temp = UmdUtils.stringToUnicodeBytes(content); + wos.writeByte(temp.length + 5); + wos.write(temp); + } + + + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public String getBookMan() { + return bookMan; + } + + public void setBookMan(String bookMan) { + this.bookMan = bookMan; + } + + public String getShopKeeper() { + return shopKeeper; + } + + public void setShopKeeper(String shopKeeper) { + this.shopKeeper = shopKeeper; + } + + public String getYear() { + return year; + } + + public void setYear(String year) { + this.year = year; + } + + public String getMonth() { + return month; + } + + public void setMonth(String month) { + this.month = month; + } + + public String getDay() { + return day; + } + + public void setDay(String day) { + this.day = day; + } + + public String getBookType() { + return bookType; + } + + public void setBookType(String bookType) { + this.bookType = bookType; + } + + @Override + public String toString() { + return "UmdHeader{" + + "umdType=" + umdType + + ", title='" + title + '\'' + + ", author='" + author + '\'' + + ", year='" + year + '\'' + + ", month='" + month + '\'' + + ", day='" + day + '\'' + + ", bookType='" + bookType + '\'' + + ", bookMan='" + bookMan + '\'' + + ", shopKeeper='" + shopKeeper + '\'' + + '}'; + } +} diff --git a/epublib/src/main/java/me/ag2s/umdlib/tool/StreamReader.java b/epublib/src/main/java/me/ag2s/umdlib/tool/StreamReader.java new file mode 100644 index 000000000..663e73f6b --- /dev/null +++ b/epublib/src/main/java/me/ag2s/umdlib/tool/StreamReader.java @@ -0,0 +1,124 @@ +package me.ag2s.umdlib.tool; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class StreamReader { + private InputStream is; + + public long getOffset() { + return offset; + } + + public void setOffset(long offset) { + this.offset = offset; + } + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } + + private long offset; + private long size; + + private void incCount(int value) { + int temp = (int) (offset + value); + if (temp < 0) { + temp = Integer.MAX_VALUE; + } + offset = temp; + } + public StreamReader(InputStream inputStream) throws IOException { + this.is=inputStream; + //this.size=inputStream.getChannel().size(); + } + + public short readUint8() throws IOException { + byte[] b=new byte[1]; + is.read(b); + incCount(1); + return (short) ((b[0] & 0xFF)); + + } + + public byte readByte() throws IOException { + byte[] b=new byte[1]; + is.read(b); + incCount(1); + return b[0]; + } + public byte[] readBytes(int len) throws IOException { + if (len<1){ + System.out.println(len); + throw new IllegalArgumentException("Length must > 0: " + len); + } + byte[] b=new byte[len]; + is.read(b); + incCount(len); + return b; + } + public String readHex(int len) throws IOException { + if (len<1){ + System.out.println(len); + throw new IllegalArgumentException("Length must > 0: " + len); + } + byte[] b=new byte[len]; + is.read(b); + incCount(len); + return UmdUtils.toHex(b); + } + + public short readShort() throws IOException { + byte[] b=new byte[2]; + is.read(b); + incCount(2); + short x = (short) (((b[0] & 0xFF) << 8) | ((b[1] & 0xFF) << 0)); + return x; + } + public short readShortLe() throws IOException { + byte[] b=new byte[2]; + is.read(b); + incCount(2); + short x = (short) (((b[1] & 0xFF) << 8) | ((b[0] & 0xFF) << 0)); + return x; + } + public int readInt() throws IOException { + byte[] b=new byte[4]; + is.read(b); + incCount(4); + int x = ((b[0] & 0xFF) << 24) | ((b[1] & 0xFF) << 16) | + ((b[2] & 0xFF) << 8) | ((b[3] & 0xFF) << 0); + return x; + } + public int readIntLe() throws IOException { + byte[] b=new byte[4]; + is.read(b); + incCount(4); + int x = ((b[3] & 0xFF) << 24) | ((b[2] & 0xFF) << 16) | + ((b[1] & 0xFF) << 8) | ((b[0] & 0xFF) << 0); + return x; + } + public void skip(int len) throws IOException { + readBytes(len); + } + + + public byte[] read(byte[] b) throws IOException { + is.read(b); + incCount(b.length); + return b; + } + + public byte[] read(byte[] b, int off, int len) throws IOException { + is.read(b, off, len); + incCount(len); + return b; + } + + +} diff --git a/epublib/src/main/java/me/ag2s/umdlib/tool/UmdUtils.java b/epublib/src/main/java/me/ag2s/umdlib/tool/UmdUtils.java new file mode 100644 index 000000000..2fa804d58 --- /dev/null +++ b/epublib/src/main/java/me/ag2s/umdlib/tool/UmdUtils.java @@ -0,0 +1,159 @@ + +package me.ag2s.umdlib.tool; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Random; +import java.util.zip.InflaterInputStream; + + +public class UmdUtils { + + private static final int EOF = -1; + private static final int BUFFER_SIZE = 8 * 1024; + + + /** + * 将字符串编码成Unicode形式的byte[] + * @param s 要编码的字符串 + * @return 编码好的byte[] + */ + public static byte[] stringToUnicodeBytes(String s) { + if (s == null) { + throw new NullPointerException(); + } + + int len = s.length(); + byte[] ret = new byte[len * 2]; + int a, b, c; + for (int i = 0; i < len; i++) { + c = s.charAt(i); + a = c >> 8; + b = c & 0xFF; + if (a < 0) { + a += 0xFF; + } + if (b < 0) { + b += 0xFF; + } + ret[i * 2] = (byte) b; + ret[i * 2 + 1] = (byte) a; + } + return ret; + } + + /** + * 将编码成Unicode形式的byte[]解码成原始字符串 + * @param bytes 编码成Unicode形式的byte[] + * @return 原始字符串 + */ + public static String unicodeBytesToString(byte[] bytes){ + char[] s=new char[bytes.length/2]; + StringBuilder sb=new StringBuilder(); + int a,b,c; + for(int i=0;i= 0) { + baos.write(ch); + } + baos.flush(); + return baos.toByteArray(); + } finally { + fis.close(); + } + } + + private static Random random = new Random(); + + public static byte[] genRandomBytes(int len) { + if (len <= 0) { + throw new IllegalArgumentException("Length must > 0: " + len); + } + byte[] ret = new byte[len]; + for (int i = 0; i < ret.length; i++) { + ret[i] = (byte) random.nextInt(256); + } + return ret; + } + +} diff --git a/epublib/src/main/java/me/ag2s/umdlib/tool/WrapOutputStream.java b/epublib/src/main/java/me/ag2s/umdlib/tool/WrapOutputStream.java new file mode 100644 index 000000000..80ec1982e --- /dev/null +++ b/epublib/src/main/java/me/ag2s/umdlib/tool/WrapOutputStream.java @@ -0,0 +1,91 @@ +package me.ag2s.umdlib.tool; + +import java.io.IOException; +import java.io.OutputStream; + +public class WrapOutputStream extends OutputStream { + + private OutputStream os; + private int written; + + public WrapOutputStream(OutputStream os) { + this.os = os; + } + + private void incCount(int value) { + int temp = written + value; + if (temp < 0) { + temp = Integer.MAX_VALUE; + } + written = temp; + } + + // it is different from the writeInt of DataOutputStream + public void writeInt(int v) throws IOException { + os.write((v >>> 0) & 0xFF); + os.write((v >>> 8) & 0xFF); + os.write((v >>> 16) & 0xFF); + os.write((v >>> 24) & 0xFF); + incCount(4); + } + + public void writeByte(byte b) throws IOException { + write(b); + } + + public void writeByte(int n) throws IOException { + write(n); + } + + public void writeBytes(byte ... bytes) throws IOException { + write(bytes); + } + + public void writeBytes(int ... vals) throws IOException { + for (int v : vals) { + write(v); + } + } + + public void write(byte[] b, int off, int len) throws IOException { + os.write(b, off, len); + incCount(len); + } + + public void write(byte[] b) throws IOException { + os.write(b); + incCount(b.length); + } + + public void write(int b) throws IOException { + os.write(b); + incCount(1); + } + + ///////////////////////////////////////////////// + + public void close() throws IOException { + os.close(); + } + + public void flush() throws IOException { + os.flush(); + } + + public boolean equals(Object obj) { + return os.equals(obj); + } + + public int hashCode() { + return os.hashCode(); + } + + public String toString() { + return os.toString(); + } + + public int getWritten() { + return written; + } + +} diff --git a/epublib/src/main/java/me/ag2s/umdlib/umd/UmdReader.java b/epublib/src/main/java/me/ag2s/umdlib/umd/UmdReader.java new file mode 100644 index 000000000..95da9a524 --- /dev/null +++ b/epublib/src/main/java/me/ag2s/umdlib/umd/UmdReader.java @@ -0,0 +1,227 @@ +package me.ag2s.umdlib.umd; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import me.ag2s.umdlib.domain.UmdBook; +import me.ag2s.umdlib.domain.UmdCover; +import me.ag2s.umdlib.domain.UmdHeader; +import me.ag2s.umdlib.tool.StreamReader; +import me.ag2s.umdlib.tool.UmdUtils; + +public class UmdReader { + UmdBook book; + InputStream inputStream; + int[] _ChaptersOff; + int _AdditionalCheckNumber; + int _TotalContentLen; + boolean end = false; + List _ZippedContentList = new ArrayList<>(); + +// public UmdReader(InputStream inputStream) { +// this.inputStream = inputStream; +// } + +// public UmdReader(File file) throws IOException { +// this.inputStream = new FileInputStream(file); +// } + + public UmdBook read(InputStream inputStream) throws Exception { + book = new UmdBook(); + StreamReader reader = new StreamReader(inputStream); + UmdHeader umdHeader = new UmdHeader(); + book.setHeader(umdHeader); + if (reader.readIntLe() != 0xde9a9b89) { + throw new IOException("Wrong header"); + } + short num1 = -1; + byte ch = reader.readByte(); + while (ch == 35) { + //int num2=reader.readByte(); + short segType = reader.readShortLe(); + byte segFlag = reader.readByte(); + short len = (short) (reader.readUint8() - 5); + + System.out.println("块标识:" + segType); + //short length1 = reader.readByte(); + ReadSection(segType, segFlag, len, reader, umdHeader); + + if ((int) segType == 241 || (int) segType == 10) { + segType = num1; + } + for (ch = reader.readByte(); ch == 36; ch = reader.readByte()) { + //int num3 = reader.readByte(); + System.out.println(ch); + int additionalCheckNumber = reader.readIntLe(); + int length2 = (reader.readIntLe() - 9); + ReadAdditionalSection(segType, additionalCheckNumber, length2, reader); + } + num1 = segType; + + } + //System.out.println(book.getHeader().toString()); + return book; + + } + + private void ReadAdditionalSection(short segType, int additionalCheckNumber, int length, StreamReader reader) throws Exception { + switch (segType) { + case 14: + //this._TotalImageList.Add((object) Image.FromStream((Stream) new MemoryStream(reader.ReadBytes((int) length)))); + break; + case 15: + //this._TotalImageList.Add((object) Image.FromStream((Stream) new MemoryStream(reader.ReadBytes((int) length)))); + break; + case 129: + reader.readBytes(length); + break; + case 130: + //byte[] covers = reader.readBytes(length); + book.setCover(new UmdCover(reader.readBytes(length))); + //this._Book.Cover = BitmapImage.FromStream((Stream) new MemoryStream(reader.ReadBytes((int) length))); + break; + case 131: + System.out.println(length / 4); + book.setNum(length / 4); + for (int i = 0; i < length / 4; ++i) { + book.getChapters().addContentLength(reader.readIntLe()); + } + break; + case 132: + //System.out.println(length/4); + System.out.println(_AdditionalCheckNumber); + System.out.println(additionalCheckNumber); + if (this._AdditionalCheckNumber != additionalCheckNumber) { + System.out.println(length); + book.getChapters().contents.write(UmdUtils.decompress(reader.readBytes(length))); + book.getChapters().contents.flush(); + //this._ZippedContentList.add(reader.readBytes(length)); + break; + } else { + for (int i = 0; i < book.getNum(); i++) { + short len = reader.readUint8(); + byte[] title = reader.readBytes(len); + //System.out.println(UmdUtils.unicodeBytesToString(title)); + book.getChapters().addTitle(title); + } + } + + + break; + default: + /*Console.WriteLine("未知内容"); + Console.WriteLine("Seg Type = " + (object) segType); + Console.WriteLine("Seg Len = " + (object) length); + Console.WriteLine("content = " + (object) reader.ReadBytes((int) length));*/ + break; + } + } + + public void ReadSection(short segType, byte segFlag, short length, StreamReader reader, UmdHeader header) throws IOException { + switch (segType) { + case 1://umd文件头 DCTS_CMD_ID_VERSION + header.setUmdType(reader.readByte()); + reader.readBytes(2);//Random 2 + System.out.println("UMD文件类型:" + header.getUmdType()); + break; + case 2://文件标题 DCTS_CMD_ID_TITLE + header.setTitle(UmdUtils.unicodeBytesToString(reader.readBytes(length))); + System.out.println("文件标题:" + header.getTitle()); + break; + case 3://作者 + header.setAuthor(UmdUtils.unicodeBytesToString(reader.readBytes(length))); + System.out.println("作者:" + header.getAuthor()); + break; + case 4://年 + header.setYear(UmdUtils.unicodeBytesToString(reader.readBytes(length))); + System.out.println("年:" + header.getYear()); + break; + case 5://月 + header.setMonth(UmdUtils.unicodeBytesToString(reader.readBytes(length))); + System.out.println("月:" + header.getMonth()); + break; + case 6://日 + header.setDay(UmdUtils.unicodeBytesToString(reader.readBytes(length))); + System.out.println("日:" + header.getDay()); + break; + case 7://小说类型 + header.setBookType(UmdUtils.unicodeBytesToString(reader.readBytes(length))); + System.out.println("小说类型:" + header.getBookType()); + break; + case 8://出版商 + header.setBookMan(UmdUtils.unicodeBytesToString(reader.readBytes(length))); + System.out.println("出版商:" + header.getBookMan()); + break; + case 9:// 零售商 + header.setShopKeeper(UmdUtils.unicodeBytesToString(reader.readBytes(length))); + System.out.println("零售商:" + header.getShopKeeper()); + break; + case 10://CONTENT ID + System.out.println("CONTENT ID:" + reader.readHex(length)); + break; + case 11: + //内容长度 DCTS_CMD_ID_FILE_LENGTH + _TotalContentLen = reader.readIntLe(); + book.getChapters().setTotalContentLen(_TotalContentLen); + System.out.println("内容长度:" + _TotalContentLen); + break; + case 12://UMD文件结束 + end = true; + int num2 = reader.readIntLe(); + System.out.println("整个文件长度" + num2); + break; + case 13: + break; + case 14: + int num3 = (int) reader.readByte(); + break; + case 15: + reader.readBytes(length); + break; + case 129://正文 + case 131://章节偏移 + _AdditionalCheckNumber = reader.readIntLe(); + System.out.println("章节偏移:" + _AdditionalCheckNumber); + break; + case 132://章节标题,正文 + _AdditionalCheckNumber = reader.readIntLe(); + System.out.println("章节标题,正文:" + _AdditionalCheckNumber); + break; + case 130://封面(jpg) + int num4 = (int) reader.readByte(); + _AdditionalCheckNumber = reader.readIntLe(); + break; + case 135://页面偏移(Page Offset) + reader.readUint8();//fontSize 一字节 字体大小 + reader.readUint8();//screenWidth 屏幕宽度 + reader.readBytes(4);//BlockRandom 指向一个页面偏移数据块 + break; + case 240://CDS KEY + break; + case 241://许可证(LICENCE KEY) + //System.out.println("整个文件长度" + length); + System.out.println("许可证(LICENCE KEY):" + reader.readHex(16)); + break; + default: + if (length > 0) { + byte[] numArray = reader.readBytes(length); + } + + + } + } + + + + + + + @Override + public String toString() { + return "UmdReader{" + + "book=" + book + + '}'; + } +} From 950afc36d7fe3d3ca31a44a62928f9e1d13778e4 Mon Sep 17 00:00:00 2001 From: ag2s20150909 Date: Fri, 2 Jul 2021 15:03:53 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0Umd=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/libs/umdlib-debug.aar | Bin 18056 -> 0 bytes .../java/io/legado/app/data/entities/Book.kt | 3 ++ .../main/java/io/legado/app/help/BookHelp.kt | 4 +-- .../legado/app/model/localBook/LocalBook.kt | 7 ++++- .../io/legado/app/model/localBook/UmdFile.kt | 9 +++--- .../app/ui/book/local/ImportBookActivity.kt | 2 ++ .../me/ag2s/umdlib/domain/UmdChapters.java | 18 +++-------- .../java/me/ag2s/umdlib/umd/UmdReader.java | 29 ++++++++---------- 8 files changed, 35 insertions(+), 37 deletions(-) delete mode 100644 app/libs/umdlib-debug.aar diff --git a/app/libs/umdlib-debug.aar b/app/libs/umdlib-debug.aar deleted file mode 100644 index 1f8be1b3baf7310655590eaa916de25cf9a1a397..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18056 zcmZ^JL$EGPu;h1a+qP}rWBVT4wr$(CZQHhO+qm<`tY#wKi>Rz#RBp0*m7T314FU=c z06;Z_`GmgIkSuUU5yjDFOM8l}#4k8DNO4X71c6$Nr^oJg#KWOx0 z(WXoA;ww_ws(+`eM`RlS9^zXCx1J2|V^I*UqP}@5o*wG}zRG^~LI$E}4vWugN#Ql! zFb{l~X6AQO`r=1*x$oIlvcRJ~PiDvx&2b}`Q7OGz@8VP#4V9ZWbq}UV#FxYr!?>isY{PHjZ+FPhtokNMM##kZcj;k`d_V4aOl|OV@ z$0nZzp?#ab*h{d{&)%&q3!T4#sPYGE&SR1OJxLeergKox`3jDY|3WPScva3Y5dr?} zpa8&v-`Mi{>}daBs@%xv-}%|uO92Bo#RkVsrO-2tqAhc6+R^}`SFVMA`l(gLpWu*BZZ#8p})OtS;FP zAHNzSkop~rAui3lHg%MsVc-e;!+?oHYh2D7xE3ca_(UWu;6bbfJ7SGG*dC`?{%6m3 zq;p%akJPj{qh5Bmy4W_CrNCwtV)Le@zzgcawT5*{8w)3Px0OgLRl~u?9LH^5Iwiiz zkR=5?J+B&|%&w8rY;rtJl;AN7XAc{o*&*0JVD$Cpn+bOEp{uP%AbFb(>(MqS&#jDz zB<9>EE$|xQ+Lks~)f3wfjCvEtrwWekN-o%4#&*F$Mw>TiowCwWmABm`ndYodf&45Y z68?CNR5qpa?v@ogjj3ah5emFiA6*mSO=>rrC@Nd7M}|5p!a8}pCf?8XVGly52O`W| zht0~LySBkgF~l_aZ(9aYXZT$sUEU1Z{RC9{&GA}KWBI2V=*6^l+2>Os zj@W9BOe(cmhz|uNlXBI8ZpnAy2RpSg#ZH0O{&?uXZs>gb zPCvK;2h`MH6vakU}KakSg??mAP_z)|y+yi-Yq6=YZr<{l>ea9Qn9cGiP>z4TsV^c8sMQ*4yg^zI<5Zm~)DAKm@?!Im)uZ zV`-}S#H3*rX7Rddrg?$%8{p%_BYP=3fMwUt?#mpw?jgX0)P#&x1eQ%1*>sWOY=6W(+YEJ;D9VSh925|w?zHBwnN z>s}r6H&2(|sLlglK5(uuwd1A7?f~dC!ku6Shq1V;s!Fva=fof1a zHMiY2O3e6wQ&>ZB^geqV+HaoXT})mvP(z5^WTwcJp!#Z8A^s@zhC*2Wf-KGUH>B

u<>sgMEDf^b(TC=7gf`t7L1`3*d+7m^)QUg>TIwb0Kr}6x_@s8J}y0*4(2?L=|R!KdH^5#qZW_%}8 z8PT?`aoxz|Hb}%!I?)zZ+WS*Ex2#8OR2MzH1^CxP?jP~HZ9FSE#lk>}l327H;b5+< z>dRrY$NPoZF0?lla&dIm%C@J5a$6}hrUw#PeTaM-@9jl{9t=|pS}t*wn=>T*p)6v| zIpbfe+YHR4Q`omWBSIab$nd%pZ$~&tp(YCOO5)Ck8eMcf}$QGj>!#&3bo= zK%aiCMUq8}dE;Bf#AJmg@sWyU+W;a69+|v(Gb4495~onoI)C+$enEA%Isb+R@cs>! zpsY+p>=kcFJx113+hz=h`%eb^*~y`OZv~%iT%yarV5Ca>{lo(!_k7^vv|+IM1SSvb zusDKUj8k4{${amY@ug#_EX&>uVw|E*^1AQxERl1Uj~x)Gk9N<-eZz*ysS3j1s@e^E z&-QZ{dU-*|j=zUT4iS7l0fDFxeaI0d6JQ$qw|Ih2^?E+Gdb2$f0~zy4)UR~p-W$MC zvs9Q`ysaymz*dRx**0Y?>n^TgX6&_BCvBgoF1*4p~k$H-CI?dzXQRT#8Q{Y zzL?+L%+flqB=w3-&?{hKfqnydj#o#$H~ap}W1s#bSo66)tWXiDlPM-E8>q?;H3vOS z29UR(YbdV$_})6H+&irviZ+9PWuE3KO{ocI*7YNbaO3Qe*G4FQ9h(XVgqGmYkHP;9 z?hjdr<+7T1+OnJlsH6qWga8HMr`LZdi@+egb*L_xsz2cNmb+=Kr+p7=>%zUD$R3th z@K3*qL;_zdX1;4r3mJ+?}FeY>urk3eoZqHykIQW6_Dw~;y8Cr65T-Z)8@53jbY=={d~9KP`zNNZ6EXD zt852=BxZAa&2zjhx&INxtk`|~9z+JcOFJ1ePuGcqr*AQ{FlyZ9iE;g_Kb{idMbEn9 zz|Shx!~Ct|F2?j|+Tlau`EPgF&UK3AKonC9Sw; zaeHPe&s?qViDae8KgCK@LB|No6Oj7XLPoeCl>CI;c}~CN$N*nK$VoD1+F$+RdoB^ z6aBdEwTtkrP&84q5^wSDi*m=0J9J5cL5AWn_R_OUq%ewQgUB?;UWQH=vPm0Gx1aaQJrXEqWaLvRV~@nGu$5)RTD;E47BMXkdPK;J z+sV|nV9FVhQ7|?STw?3U$n=YSs%v5m2*uOdlWGd1dbnWJr?QjLFNK8J$q~919L45rS(bK=CkErGQ1M5z+ zbZIGt#K!X}&`6>il;IpyDufxGh5#PpEkV5&(o(2s4r+7=d7slBgEXiE{8yI2s4k&D ziOWBC8B@V#H%bZSsB5ywiwB(?;XGyGbki_*s4a!GRTI!N+X>~U?N8(O!MOG$-|~9n z&hI4jW*^rFpv>^+s1{+hmPNTq=a;qdG~K)Nm5{WyQrzzSEbk8fOytAryrAmvhgzE8 z{i55Bs%T0W`oLW`YRxDtoBNb-AEsFFj|`1ql@RsIFlGxvH1yV(=_GtCZ!dT>8W$ggqW3F=l! zEy9#$tS5#lp|k3ZGvN1*VcHzI<$R2*gUv;{aL__95GNQw@D>V%yj=~`;0RDb8n29O zXU1I6o6598Y3GoKFNInF=PtNUK%>C;@=!%|HMHa1@#^AZ z7&d3~AB7s|W)a~LP`~&aeJA~c-CDIgg9RNmPygTdorkd_wiUJ3kf!Nl@ahuus;9n> zt#XL%bevQ2@Gkhp9q!>7xD#(PxQpsxw+;dd#@%0+y!rNANy{~Lr$piQ$!<+i7fSul zs-8d{<&g^h>I;n3W^H_VJ5H$qnFTlzlfcT*ah!oWP}VVKSV$e*;3VTy>Fx>mE$n$<*o8m4${2ln>R?0*g`&Oc$AFSFjh$$6nZ`39k#GsBHggDKRpZ#e! z_l+#*s~mhUVN{T&9ttEc^L58Ll>V!TU8fx+H%!Qg<@qz>dJnYoZMeVIe`xS6)sBhk zF&X~RP~~3Y&&j}2T9a!2Gd%=#wl4P_DR);QW~&lOqk zJvaYBx_L)a@YkaB!CAo0;N{Wp1GTT<_08gWUdj+g!6UggVbBhukzf3IOH3USk~KRM z)~&6aGg(GxI305_M|kUnyZ$Og?a=Xt;o`vR1_{vx3K3m;ZtV*Vl^iI~fzSa#Fy#>3 zeb2(Q7uti-V@0f5J_yLm>%=N@cC}DhOSM1^2b`jy|#Z8Gy{yfFyOc|6f%Unzj zDhU=zIiuuDmQ;8HEX1+XSK+!!vtagXU+eTn2oLQI-2R@U0tVx* z9b18dMqtBtl^#ZOH*hBHOA8jgd%$@*hfJ*Jqn%^3?)#s&x6&?fAh;pb}n6XrK z6rxtk6`cTrR>n0rXwD&^+&5wqgwG-FJi@=wnL_D?W6vTltGH~J$v zLze!NxzZemE_o+#8+X@HUJM-^$u#sQO56y1&4_8 zqf0Zd1W~4PK^FUt7Xu_E9V$-NMize30McIgLA3) z!%TgHxk!BJpu&^_WZ%k)FVgH5(l>8{m^uS-;3t~rFyNv#FTq-sUGFUkrZQG zH`Y+we3np^7BZEv73Gy1DDzLKvMw^m3K6S9K^~*bWi`)GN@AAQDea7vor`ErGtILq zbf@ino^vSJnKG=!wqAj$yqMH@5$jLm35>&rSVgoe3SQ}}?Wb&_h5<-dBsh+nkjbOq zj6R+TGr^@y_fg2a2V>BeD@irjt^k_|q7|fp?yiHEpaXt2G`iW0Lc%C(SQCsg#VWrV z^9tsoZ3-)8o+9Ek42#(9Fn2@oP@1lUZ&H4CGRc z`Aa%Z1Uk&U2{zLim_`pDn({=Hm%-r_*9DFihJS>&_pKq`U3S^;T|Hg3z`0z3Eu`^@ z`Bxplwvt$lZ{Mk+wCPMi$vr_{r6792y!6O3Dm*xQKl49cy(p~_LfoLh06`EC0O2pe zQ%+7*#8Ka%PGam*BBkTGZqx*7<&lAXHbeIZ@ux|4A9?S)&zso&{&%N=L z!Ey1Wxp4n&)P@t89*}d)0J*j1TyJ{R*)^%x)hd|JX{em6b?sdCn)Th+*%daRa?tYI z9+u}qM4WQRN0Jl@*7&ZlBV!cN`0gSm@^rIQ>r(^2>dPI?MK&(JE>nkmK$K8yKIZtdY&yHY8c>I`BZXxJ3X#-6Q3U5R-Z#df!8QI($4`abyq z*}*JKw?l2q?b5K+$~%opZPe))@8`lw%pz z{!AK8{gQNp$Iv02)F9aA5i?)hrw9|vB?XdWIrc+SuOU<-nF#4`h8Qst){-o(E3*A8 z4!Kpn7@8neOV(j`I@tNi-uwt8DM$|lf5V&qZvdwd-5uIILdcdN!$Mk`6*LkjE2tpyzYr-j z^N=TKXtUm-f5phpJc6lJPcYuca#6n(3Rs!<=9S6(h*pEa9C6zgY;xXhi&*Zch2QU87F9Hxacy@-e{Y+QOTJPd3iu9m-;je19sGr}#2x zK@VT1`L=m%CaLGxeWCk>==P{hy8VSM;A_!|Oli#iMng|(3CnF!?z7%0dIz>b(F-B! z7alU*_MQ%T?p7dMXsR0r2bFru#~+BcS{b)p2FS(TrW8F-Bis;Q$7x zsb`1yw2AyNxZE%alIfO zx(-5AoEXWxWmrL`NSI*#T`!Q`1F-gb+5*gxaqyf5Xj?IM2_&>MzX@U;npClOk*Nl8 z%cfucZ{4Hc_sQFwKwv-Yf0y|v07P$VsQ>M!LbNgnV+Ce%-8OFe?7#im;MLY?O*?&JB?uZ zczNbKGy1ZC^k+)6m?yTa3LA#4tJb5V>AE+NmOM4BMM!sRntzmm8ZQN$-PR9qBJAHI zP~x@LM`+Ln^xHZv>2k-?j_VQgX{Ylb0oD5-H7R z0ikbxojo&dPU6zz#Mi5>SFj^0)-?p3&q*vA@(FqJP^Q$x#%rjo{u047v5enVq_<*`{mQ9$|mW~MCRFfw&e9GlRx3xgafy8g*r&dunvE_Ld zEnOb!QO7P6I$atq=L{zqQ4MM78#56NJvQ-?TXrT}gCp_Cr9T04uN`RLdJeM`ER`DG_wcb!qwA}FMIv0^Z@yLJg`U%|&7<^H9ulw}y}ZBmW2s8lw0A2fBgbHe<`iduD@h6gZ|T_HQ;OuYDS3?}P-30*VTX7ECp`9{5c{LE4AJ zy0}5YvzWKeWpPt}4cR^J8e)Jos7V@@YmXu(XWrqzi2rN>Ppv{Z0=<>{GlVUMuYHYl2E+Xsz{VM9#0rYUm>E!hMt9kc|-4{(E z9~(k0dt9*$o}8C&<}|IynVCy=`8=!0zQIyvYDn%2pIDBWPH--DLc=h>sH$?hZ%jYS zfu`oG=2X!ktcC**`AXRyv8r;EH=j(S$D-R)!4N0EV7bMitOf94`?bAyes65yZ_&pR z+>VrDFtw6mm~575s$;CBl-HnHAe?BGajIriDwlD=aSN#DI_STrdc9?hv(R~B?Q5+i zVe4zH_S3wk!#+GYTW5>cxVFK*jqvTw_Z_tDY^kNmNfO*(%7!G&t=7_{fGa>dE)NMy zppmr=c26<;0Z0&BSQ>tc`6a$N^hm=!4US(II=TC^!{L7Y(UNX6y!HIG;M8g1>_8Hp z)(p7*pjvxFmT(kqKkjEALD8S3Vy>_t{?zYscN0%a$soDv?(=!I4Q8g{Q(pA&avAlI zNLO9oto2*@FUXVG#QBq8_IdD<( zJ$q3CYUtB6tQnxog%(98WogDT9I2_55F18`Hgxlt|28-t_AZrL9`{=cZ?Odtxlqm*fULEYKO^B~&h|gdIAP7Nl zIbryX1?=I(`0p73=0Z4efjogwhT*3dpmBPqU(fcTzvhUsDMKU(Imo94AmYrw(J+1w zeI@So_znE5*7Hq-fhNGdtBJm;^%2Bz-4Q_d_w_)8f8&U}nS}#vp56&Q9mw?&*8IpL z{>el_erN@OITrczl{3YZ%*Q2w8DIHB-a!A{eKiM+sb7qhAm3M@-?xO`Um?b6X}`yz zDIEBf5`OrJfkPd;@?2t`{~*54dchdkgmy#FFVwl&)wx$=b<}R+H0=xL*wAh0>=~Nr zkE(e_eZp^w?^M&RZyiH`20|GGlCw>C7EsJM7CG1n=7F_2~yHQtx?TuHJoR9<0qebElZyo*tU}45?nf7Kj~6> z&d)QM;r^Z-v-$3PewzwRyKz;F(0g z8$qV$*YP3C+J$!+LQXKR<2MUU(AbG%i>`zsNqE|v>UztBQ3~JxvBM^)J6W%%v8!WV zMhpMXl$brMEoHt%zoupt%hei`Q@35tgE@HYtxGhQ#Fv$T%E!Q8!G4Fg#$533>pjf~ z=4Cf=bXRh;%Vb^GpF5;YWSaaL&ZM*IfLAl&-wgz+Ie=V0ozQ)>%;i@|KGK&qqB6RT+^}`I7j;T2wTkpxG3zINtzRX&LO9B)*gloFg=tkogyCXPR%ag1}#`11|!Cb zVBiW09>y&yJvgBGvoPvH6{K7FPtd{jb$#5(@_PAeNRdl{UbkF8tVIkew~WLGjF~N0 zG_Y{tFZzTts%1dYs2(%5Z7LQega{OWLpK6y$TZXOUJ-Ptjx@gGI2JK{kyTFYZ=g^EqG>K~H;;ZAP>LrsOGGlP@=b4R zHbRhBxZh(sLP~GZZWW!6XgZ?Ar8)rTLdbpA4%}Kjd3ib0BnPs)|9AqCl9k>uuCpRB z*1nqXj9j+9|`pp^( zB9>9rYd(HW`xq`&9cPkTK8wm`Ic@)S$6=KfT3TAtPL(7tb2T~vbvbfxz=|*(vdSvn z?14S4Kpw_edb3qs6dQuLs|AWS#&AtihuF~0*GFcL!3z5@=8u?3v)?x~UY+SYu z)2d^0XlqNpQeqjN)s$G2gjDvRm2(xjTi7=}RM0eSe z%+e-;^4uwQRpiMx(YRQBP8BHW1npBW%09NQ!ZQue)7N(1hAB(p9rOdyZ016gN(oNABY1+`!a_3 z-CfVZz}M~SP2@=KTlp+fhz(|Vyn(c)+O+hdxtOtDlpiE_+zQg{rRvs~+o#v4rC*(Vb136rgcG2jtf!B{ zC~+`rjNpVE2c{pnu|DG*@2-sOqC$kY9l~^ry%PTYZAsNUB)D@L&C^M`*;_IfXrNjc zAvDB45M_BlxQ?)HM2HuSaC=a%JCx5I$&SVVH- z9X94^g=+8owCXzevJ<->O6o_ACGxe8{V^ZSNIbM^zElf)V5T=g4oYFd>J+M4KGin=IcP@5$WIZd(ozTM(K}6$t`o~)(mZP z{H(Dqo^2{4^S1UX+nY8KIFXVLX-&g}x84g){}<)Q?Dv9o))OO(#1cWLRo9iA*JO_8 zq${%zPhZXkusL7(QVTw1tofo%Ss!9RR!%!ZZ21!}CgmtH!<9oAFN=p=Xf95lS-}*Z zgW>lz?pcvlAn^?!qCx(QGeNkpgB~V%!58&6(Zap5@QySuE@G7&flHgzKnf zyh;islVOGfYL8{R8>68}l67)R7yYG>*MGaOS$1Cov?0z63Fc|gZk+KF?zGtX$8?yGZY)0h}Q`Y-Hw3KYf^ke3swS*hMj1jMa11zXMdZ2 z9G;}DvNcQy)m4F@#U1HHa+0YhHVUndadfUIeZnZX$_2{Pu^*X~uh^d)nTwO7I{ZW` z$bu%}7PU=OE9^c|(RD?J8|;LQQS!|=VGTohUQ&K+V91Kwvz^4IMfdg>Cj74YA`F`z z^(Y>5hnSYKHS&tWpZI}^my+lKO%e*HnnwkfWI^fv4iYk%3%cAR20f;^4F(&l5}E}o z3*S9Z9P4KLM?yeLb0enV_eyEswW(oHn?DPO*t}Iy7rD5g=euS z(^kMg`K#OE9UpdqYaI5&oHE{lQ8XRbb#MdkVE-m|*`q8@Ebk2)_i=v%yRvn&XHU!K zy|AT$(#vX4uG+r;Z^<7$;NL_r$h|L6S4gXQN5;Ot<=b!@J5~n|uVD|q1FQGG2c1mn zXO)T)d78vzA2I)aMnVV;o<-yw4qwQ@_o>U65->dPW*?~G?J;&vufhU4q-N=9%Y>~F ztFZcfSlXs{&-dk_`I=7-r-y3?Q%EGW8#E>sD{&AJU$x-MrpFvSgNkt9wY>8^(&P)) zmO&kuZmmuhHJrXc-!xg>QmhPlm~oP>&Vn*Y1vhF;1UZCqRE)k%MDA&uwalXY(iPAo zbJ>=kmzXJa%s6!NGT8U7VZi(x%C{m^K+#2es(GCd1uv}*q>&4s?xgI;npHpGJ?ujc zAx;cj?-Ds<=5%p4r|rzz?3$tR`1_SbM<{xvra_vBN6F&H1Zo7TCq8tOC#SUsDYn#3 zI3V6xb#a?z#0Jl_}gku5k8Axt{%_~p;o_`w@!8lu1sRLWY9`&LHm`j7Ga0Y5rdng#E4E}hg zD+9F05sS+JXQH(bOnN-pa5}ZX)N>j(j{8U_Z>H>lCwu> zBoJ7=y8W3tk4)3U80ST+&tZiqr}Rk2?yGxbj}AkzsQ3MlAj6qS=e5T7+{Cj2aXg80 zwNP$+gC`bO^KW|RmJyT&XQs}s{0%aa*6l|h=bzcx$2Af5`Xu3Dv<_kS1PAB0rVknQ zux%7mFpfO+39PRs(7_LWEE_dcaf*d)*}C-)X}y4PLpJX229{yOoxv!Rj(x&Fkk<5D zoz7OufWRxG4I#=cW7iM3ee6@C%F(Or<7jqdPK%}E$k{Qz^>o`C?ZR)isX?w$E{3S@ z-V(20O3xDYcF?Bkh@KIpOAw_+1wQw9G0~eQwb{O=@yDQw?5oODAa!+Cbx_|$Pm)T_i4qdu3}Heo{-~F*bb35)6e9-JAuEjH2I6Ikk%pwi}Bv zYp><#Uj^2|>Pg3ou9rWPCHbu+W*e_B1GI{r=SE5Nk+)vWE7I-g~7}Nk&`g_Z#v^R<6IPr47Usc2VXg zd6ntTaF*l`>p+K;qO(X+cS#7~-lP3iftG}^TxL+U+1;8lYIqW4BFsJM_!g} zo7)UKd|niQEa-6-MQEFa&mM{2H-B-sLhVLhz%++g*mAXKS=X+0+6dIP-i7Q0$Rj^d zZ9GoN+j;OD>Ysa2)C=Fgb(0pP?}Z$rCw|{8RD|yr(|^htB9S6S++8@IIRrRGFgW4r z|0L%l@%l!%&j(eBA0r*Bj1AN8A+zZVX2xyy>kS8MioD={K3Xrn4wt~xapN}^HNMV= zVr^VXFTJ)~n*X^MmkD#Jgzm_byNHc^mnB;uJ8E}Z?h@y&tJUtF9E5_b%Fp1I)wu!Z z=n(rbz{{@5oxlKLfyBJn&cUYwLv^Sb)vJz6qP z$#iq5j+J$fAuCd5A8HJ~rSlq=DCD#*@)IiMDoN-g^hw2@c<)|rn-){)BAJ4QORcta z^N@0{Y&d%Mlct-}bD^X)^8iF|-e+evdoIQCZ&U2?CEl7-Nitp5>l0b29f8>*bY8#5 z3cL6^sML}eL#C-N?6R$*rHfJJO!y`+?Ac*$E4EqUB`^X@-k!}B-KZ#-M|LC+$tZ1+ zkOr0r7E1AkQm1Ps7P}Y?@2o-F!0*}Qmxm>1L5v@c549EbyGGpFNzsV>U6~IwmXK)g zSS7tEui<%-8UI`(42Oo_1)|ATbiW;vet7hhDfBoqzf3*fQHv3bU&fJt|hxhq2UEKP>4kv=l((8jMDT& zuCu3};R|5zB^h{v{eBUH6MZH`O7>67-~xMPD#9g@{D7Ri`qM^+*ZJMZiF223=~?+g z{ATp$7&-S@Bdsxm?@sT5vbDfn36ZM`vao))(lWcR@C&82btCNRUm=b)Ol7#mNg`#i zTST3FmZ8L2-#-2dhj{jKbcWc*lHE|;@H3nM7z6*|Z1+=pLBWcR!$do8vck!tBz&K+l8cerU zIKBCXm{DF;U$11D^|zi$sFy(~hcO1XlFdI0_n$M*VRjN`$P#&TxLsnDMb0FTCY8hQf#6@2(DlnJ(gDCF5zV(y(IUdBed zSMMAw(XOgP3lBq&jm+i*VhR$Sd}{ZI%`|X=!#&LuqJ+~!{aAQtQIg@Vkafm#2u=Pk zuX1cXrHwo8Ix{n=gj5#7)3+hONHvhO=Q?v9MUrEGa0r@6a9Eaj$9#__bX#UvI607 z!TiriKL0br|1tZDNwjH-m5rCiht&FEv zgH%XXU_TW?kJK6`Pb)qSkq2__*N+!5tmb@|9XZ6x_R^3t(Y@vow!v<1G+DI4g~sXA zu|}3I=Q(rRaNBazbE@NVgYS_Ga`L(AyZ)7$T6At6Dz&c&YZdg8547>F**8@M(%pG} zbpc}Uyj{cM$nS9@LgLJClMivPR$kf*!vF4*uB+Ml`~tQP>zp65AGj07Xpd+Ex9=U` z%CE(T9oOKwVx5D;U^uK3b8aWru}G~;ItCT3$F_0$5$jw4>4ly`}lf;m(q&e#&8jxh&+PXhIkC;y`PARA}Hx`;PW%{f7&HLtkN&lm%? zuv95mnV2alk0($zv%K0D1H~AAXmMMp>WYSIPQ`yI0!D~2!(o{z>YI*GEDECvLKJkL zr>*W#rK(MNu-vXYp*m|Dv%la|Ts$1}|r z1;t?{6!JZH_1(;)PsE?`kjlNv7X{XHuK|>W#>T0N)&UT5{mmhFO|TrW#ZIcg$voYM zM~DTykn+<8nkU`bbqRQ0vNnI_{@u4PAW*r2ty1UJVQpCCVaV&GC=&O@{pjcrCSBje ztvA5KOF^nqPz%*zLC$DrDALz4~`=nz`O4K2V_;QtkX#U0C=W z-FY?WMH#5gXceC}Tboa{kiX+7Py>+`RoSa9t^CmB_T zCwtSDd@mXaG^c>jQWX3v$y%uI+!k`}B{ORYAzl>857EKPEQC2yd?!|A!;<(rI4SKH zbd=ID!A(V5{2ZWGCTN2~9!S(^8ugc*_ymue4CUH(LSg3G?oT0p=HiA+UfVpxoarR8F;0|e&DfSKKE3kUjD;W_ zD!R(LIn(xV_cOV|iFR501td;UiQ}!@F zFdH+{mN-G>I2vR%le_0}u8Ltrs*5#T=9nDUpmMNM4R3|PoGN~7jCOh19Lud-Kx8ef zF_H*-4;hW+vF)B$Gpl*?x4OlxV8w1e5u7BurX_D9SD^~rO0yR+8*gt-E-Ds{#t1Zr; zWm^fnNuRZ864X*;Owm}5XW6@xM$l|NBTh~vXh?C=1g$aIXg<^>J_~Pk*i${660+W^ ztW(f>)tc-%qj)HXo+n*3f|jz^;QuViS^}7}%2N%K#h7A5lJy5a!4Rr+jqXkC_tU2bweGSi!jA zPI+T_C^xu!wOm2Sy6KWHMdS`Q7wH}LRf^3b618HNgDX0}bSmgSDq&Yhj9k?UEka|l z@}4{f2Sj^8tGT7*$z&_*GC&(CM72mo5+Q`~86JJ%wksvnO4amd(tLt<#|0%qR(K7i-P6}0B-XhwZEgGq&UOaMZX=vI}-I|U3_N?Yn3^yxdPL=OZad`a6#!iKy z)Z|(P1&NCrCv{>@qK9UVJtj+~&s@-N+I+{IE`uGW##AsIIFc<4_WZY7^cP7;+z|@A z@pEQXNahH%kXyB`>rW*f-;jzOw!LRV!nPTc8q3?)G)sMc#-Hez>k9_8!(uK-oy~eT z{l@&zi9~(fPPpxc4NdmaxVT1no8-0%emfbYJDZ*dJ~9Pb?y9u+@}5C~Wt(HSVa1qZ z%?W#b#W*Se5!JfdO@DI(K9k%!s9C5$yY!(!x$QTlx-J-{ZJ-OsubA@&iFjQQol&Ma zxre@S9_Z_jkq0mS`M|CKBe5*4DKZGzDTFx%wvN;(#T77Wb}kN8I?I0qw2}C)E)WFR%J_9h{DdKKt8MA8QPt5j1G8}h4oOS zKf99S?1;6WP_HS=(TCZdzfhdKO^Eh{pt`&P!_OU|wE_#Tl>SrrbgE7p#1XiZagiMC zN2ts9Fg1R3><;XNq<*RS@YC5`@mo}Y$iz9!+FUMAxA+dx1wL2*>zu}pk7>i5_7+hR zYzV=e-C_uo`v@R{Qv02znZN$Tn|e5W%g3IJE>T(y@!gmDjI-Rk50xV~BHd&txJD>8 z_&hv69CJSEyderx=_`98Ho9c!J{?nEc3f0Q;)y5$yFY0WDUqIYckdcauRdEG!4 zA-|(cvHLuLpNF0e-$lHli{uJ^HSyuUq`<^2v~k6(HYZ+5_!>7Brd4YbXa#de0q2wO zO-kTGX2vV*oLp`L8Lqx?>RLnXq9%-+*TNERQx?I&E{>EfMc8C{OjEyQ?62Ut$5?iN z6o~gz;Gx+0wG$dyj`3a)9~#{AF=4kJJM1bxSFB*#8{B>}AnC}l(DJ@DU(2sLYgPTq zOs1vu?~^T~mVEUZwheKt>AK6=cD&>TA71?hac4WGl)8M*TU>y+ILh-Hk~i*(>hR z=N8p&jcWq4EW&EMQe@&cX-s++C_d%PWHpU#lO}y|u?^Nzv(?s#(*JbS*l5d^&h#bu zN;;cF9;;rN=3}K7rTWg(`?bKfXLi;4hdg;iyFEqro_723ifNi`@k;iNOEZP0`RExf zpH^Xf>g0;4+ty!l5-i^MZj z7p@O+wzfX-cYW>7ypqJvPsC?`+V&^v@V?V+H^cT8-+Oax*W5Rug*p50t@tzR@aLzq z?{w?U)3>(W|L39R$7lMUsjG`bZe=rs%-nPS_05a(&j(B1aars=`OEjSn{O@?djH=c z>eE-Pb8oHvi&lTFeZOwa`F&+t8gp{JqYrp3JHvjgwSL{4SprM$98dr2s(#$c!pqiG zUGHgW(#q*|H9IfOoxip9k@?zpDPbpUo}aS&sPUv`QddUi6rT-&9rregn!c;6`}0}+ ziv6AHq|4>2cTZ0)f8%}QWXUgu$F>hYOG|&-vgY54>-FEZ_g?$hv^B_gX{6BgF!6UC z*3~o0MJNAUwb|12Gk%x<)80mw?*IC0`m6rX z)oLt9wVy35n#SVyZg+0tlEPmnqG$HX=;yQB&fR12^to{Vo-Nt0YAbJlN>a(U<1N$P zf8pKtUvqPAAAHQpIls^5rv8pU(=+RT&6YO&e0Z5p?Di*@=N|v+Zst>7pLWt@yjMYkPTZ@zI3s4*S?>Oi7(_5A8m75 zFuP-4h*aVu=E5W~zvLF>TV63OmEx+MG1I&ARDK;&X_>gF>xf>kleuv1EXl-YIi*Qz zb1$BCmVfIqV|tRusT=CMcD^{w&r!VfY=>kI$A`33=9SNxoPrnZY@h3BgLd|ag-n=_ zD)8hDJs`FOVqez~M_o@pH~r-NT>V7I86)~hrI|S?`sqc9DLJY7iHSwJxv3?IDTyVC zdcb2%3Q~(oGE!0$~;q=zkI(O#$X45Oi51xAY=zQ|k_4M)7@pKVn2incZ zB*KjQloX%?z+g!uh(bOy1)omPnJF-^q)`XR1fQUSt_A%B6@->RAQQ`ZD(D8FpH_k} nK#dXQ>=Ja1=z)yTxWa{kAP?|n1@4z;U| { return if (book.isEpub()) { EpubFile.getChapterList(book) + }else if(book.isUmd()){ + UmdFile.getChapterList(book) } else { AnalyzeTxtFile().analyze(book) } @@ -36,6 +38,8 @@ object LocalBook { fun getContext(book: Book, chapter: BookChapter): String? { return if (book.isEpub()) { EpubFile.getContent(book, chapter) + }else if (book.isUmd()){ + UmdFile.getContent(book, chapter) } else { AnalyzeTxtFile.getContent(book, chapter) } @@ -121,13 +125,14 @@ object LocalBook { ) ) if (book.isEpub()) EpubFile.upBookInfo(book) + if (book.isUmd()) UmdFile.upBookInfo(book) appDb.bookDao.insert(book) return book } fun deleteBook(book: Book, deleteOriginal: Boolean) { kotlin.runCatching { - if (book.isLocalTxt()) { + if (book.isLocalTxt()||book.isUmd()) { val bookFile = FileUtils.getFile(cacheFolder, book.originName) bookFile.delete() } diff --git a/app/src/main/java/io/legado/app/model/localBook/UmdFile.kt b/app/src/main/java/io/legado/app/model/localBook/UmdFile.kt index bfdf3655f..394072085 100644 --- a/app/src/main/java/io/legado/app/model/localBook/UmdFile.kt +++ b/app/src/main/java/io/legado/app/model/localBook/UmdFile.kt @@ -1,6 +1,7 @@ package io.legado.app.model.localBook import android.net.Uri +import android.util.Log import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter @@ -21,12 +22,9 @@ class UmdFile(var book: Book) { @Synchronized private fun getEFile(book: Book): UmdFile { - //BookHelp.getEpubFile(book) if (eFile == null || eFile?.book?.bookUrl != book.bookUrl) { eFile = UmdFile(book) - //对于Epub文件默认不启用替换 - //book.setUseReplaceRule(false) return eFile!! } eFile?.book = book @@ -112,7 +110,7 @@ class UmdFile(var book: Book) { } } private fun getContent(chapter: BookChapter): String? { - return umdBook?.chapters?.getContentString(chapter.index) + return umdBook?.chapters?.getContentString(chapter.index) } private fun getChapterList(): ArrayList { @@ -124,8 +122,11 @@ class UmdFile(var book: Book) { chapter.index = index chapter.bookUrl = book.bookUrl chapter.url = index.toString(); + Log.d("UMD",chapter.url) chapterList.add(chapter) } + book.latestChapterTitle = chapterList.lastOrNull()?.title + book.totalChapterNum = chapterList.size return chapterList } diff --git a/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt index 0bb66218d..5d45d4795 100644 --- a/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt @@ -197,6 +197,7 @@ class ImportBookActivity : VMBaseActivity titles = new ArrayList(); - public List contentLengths = new ArrayList(); + private List titles = new ArrayList<>(); + public List contentLengths = new ArrayList<>(); public ByteArrayOutputStream contents = new ByteArrayOutputStream(); public void addTitle(String s){ @@ -44,7 +44,7 @@ public class UmdChapters { return contentLengths.get(index); } - public byte[] getContent(int index) throws Exception { + public byte[] getContent(int index) { int st=contentLengths.get(index); byte[] b=contents.toByteArray(); int end=index+1 _ZippedContentList = new ArrayList<>(); -// public UmdReader(InputStream inputStream) { -// this.inputStream = inputStream; -// } -// public UmdReader(File file) throws IOException { -// this.inputStream = new FileInputStream(file); -// } + public synchronized UmdBook read(InputStream inputStream) throws Exception { - public UmdBook read(InputStream inputStream) throws Exception { book = new UmdBook(); + this.inputStream=inputStream; StreamReader reader = new StreamReader(inputStream); UmdHeader umdHeader = new UmdHeader(); book.setHeader(umdHeader); @@ -61,7 +61,7 @@ public class UmdReader { num1 = segType; } - //System.out.println(book.getHeader().toString()); + System.out.println(book.getHeader().toString()); return book; } @@ -97,7 +97,6 @@ public class UmdReader { System.out.println(length); book.getChapters().contents.write(UmdUtils.decompress(reader.readBytes(length))); book.getChapters().contents.flush(); - //this._ZippedContentList.add(reader.readBytes(length)); break; } else { for (int i = 0; i < book.getNum(); i++) { @@ -214,10 +213,6 @@ public class UmdReader { } - - - - @Override public String toString() { return "UmdReader{" + From 2aeb7a421927d56e958a9c3e0262861cbd50fda2 Mon Sep 17 00:00:00 2001 From: ag2s20150909 Date: Fri, 2 Jul 2021 15:06:28 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0Umd=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/model/localBook/UmdFile.kt | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/io/legado/app/model/localBook/UmdFile.kt b/app/src/main/java/io/legado/app/model/localBook/UmdFile.kt index 394072085..de6c1586f 100644 --- a/app/src/main/java/io/legado/app/model/localBook/UmdFile.kt +++ b/app/src/main/java/io/legado/app/model/localBook/UmdFile.kt @@ -18,27 +18,27 @@ import java.util.ArrayList class UmdFile(var book: Book) { companion object { - private var eFile: UmdFile? = null + private var uFile: UmdFile? = null @Synchronized - private fun getEFile(book: Book): UmdFile { + private fun getUFile(book: Book): UmdFile { - if (eFile == null || eFile?.book?.bookUrl != book.bookUrl) { - eFile = UmdFile(book) - return eFile!! + if (uFile == null || uFile?.book?.bookUrl != book.bookUrl) { + uFile = UmdFile(book) + return uFile!! } - eFile?.book = book - return eFile!! + uFile?.book = book + return uFile!! } @Synchronized fun getChapterList(book: Book): ArrayList { - return getEFile(book).getChapterList() + return getUFile(book).getChapterList() } @Synchronized fun getContent(book: Book, chapter: BookChapter): String? { - return getEFile(book).getContent(chapter) + return getUFile(book).getContent(chapter) } @Synchronized @@ -46,13 +46,13 @@ class UmdFile(var book: Book) { book: Book, href: String ): InputStream? { - return getEFile(book).getImage(href) + return getUFile(book).getImage(href) } @Synchronized fun upBookInfo(book: Book) { - return getEFile(book).upBookInfo() + return getUFile(book).upBookInfo() } } @@ -100,7 +100,7 @@ class UmdFile(var book: Book) { private fun upBookInfo() { if(umdBook==null){ - eFile = null + uFile = null book.intro = "书籍导入异常" }else{ val hd= umdBook!!.header @@ -115,7 +115,7 @@ class UmdFile(var book: Book) { private fun getChapterList(): ArrayList { val chapterList = ArrayList() - umdBook?.chapters?.titles?.forEachIndexed { index, bytes -> + umdBook?.chapters?.titles?.forEachIndexed { index, _ -> val title = umdBook!!.chapters.getTitle(index) val chapter = BookChapter() chapter.title=title;