summaryrefslogtreecommitdiffstats
path: root/fbreader/src/formats/pdb/EReaderStream.cpp
diff options
context:
space:
mode:
authorMichele Calgaro <[email protected]>2024-05-11 21:28:48 +0900
committerMichele Calgaro <[email protected]>2024-05-11 21:28:48 +0900
commit2462d03f322261bd616721c2b2065c4004b36c9c (patch)
tree239947a0737bb8386703a1497f12c09aebd3080a /fbreader/src/formats/pdb/EReaderStream.cpp
downloadtde-ebook-reader-2462d03f322261bd616721c2b2065c4004b36c9c.tar.gz
tde-ebook-reader-2462d03f322261bd616721c2b2065c4004b36c9c.zip
Initial import (as is) from Debian Snapshot's 'fbreader' source code (https://snapshot.debian.org/package/fbreader/0.99.4%2Bdfsg-6).
The Debian code is provided under GPL2 license. Signed-off-by: Michele Calgaro <[email protected]>
Diffstat (limited to 'fbreader/src/formats/pdb/EReaderStream.cpp')
-rw-r--r--fbreader/src/formats/pdb/EReaderStream.cpp289
1 files changed, 289 insertions, 0 deletions
diff --git a/fbreader/src/formats/pdb/EReaderStream.cpp b/fbreader/src/formats/pdb/EReaderStream.cpp
new file mode 100644
index 0000000..9775773
--- /dev/null
+++ b/fbreader/src/formats/pdb/EReaderStream.cpp
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2004-2012 Geometer Plus <[email protected]>
+ *
+ * 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 2 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <algorithm>
+#include <cctype>
+
+#include <ZLFile.h>
+#include <ZLResource.h>
+#include <ZLZDecompressor.h>
+
+#include "EReaderStream.h"
+#include "DocDecompressor.h"
+
+
+EReaderStream::EReaderStream(const ZLFile &file) : PalmDocLikeStream(file) {
+ myDestination = TEXT;
+}
+
+EReaderStream::~EReaderStream() {
+ close();
+}
+
+bool EReaderStream::switchStreamDestination(StreamDestination destination, const std::string& id) {
+ bool result = true;
+ switch(destination) {
+ case TEXT:
+ myDestination = TEXT;
+ myRecordIndex = 1;
+ break;
+ case FOOTNOTE:
+ std::map<std::string, unsigned short>::const_iterator footnoteIt = myFootnotes.find(id);
+ if (footnoteIt != myFootnotes.end()) {
+ myDestination = FOOTNOTE;
+ myRecordIndex = footnoteIt->second;
+ } else {
+ result = false;
+ }
+ break;
+ }
+ return result;
+}
+
+bool EReaderStream::fillBuffer() {
+ if (myDestination == TEXT) {
+ return PalmDocLikeStream::fillBuffer();
+ } else {
+ while (myBufferOffset == myBufferLength) {
+ if (!processRecord()) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+bool EReaderStream::processRecord() {
+ const std::size_t currentOffset = recordOffset(myRecordIndex);
+ if (currentOffset < myBase->offset()) {
+ return false;
+ }
+ myBase->seek(currentOffset, true);
+ const std::size_t nextOffset = recordOffset(myRecordIndex + 1);
+ if (nextOffset < currentOffset) {
+ return false;
+ }
+
+ unsigned short myCompressedSize = nextOffset - currentOffset;
+
+ switch (myCompressionVersion) {
+ case 10: // Inflate compression
+ myBase->seek(2, false);
+ myBufferLength = ZLZDecompressor(myCompressedSize - 2).decompress(*myBase, myBuffer, myMaxRecordSize);
+ break;
+ case 2: // PalmDoc compression
+ myBufferLength = DocDecompressor().decompress(*myBase, myBuffer, myCompressedSize, myMaxRecordSize);
+ break;
+ }
+ clearBuffer('\0');
+ myBufferOffset = 0;
+ return true;
+}
+
+bool EReaderStream::processZeroRecord() {
+ // Use it with offset presetting to zero record offset value
+ PdbUtil::readUnsignedShort(*myBase, myCompressionVersion); // myBase offset: ^ + 2
+ if (myCompressionVersion > 255) {
+ myErrorCode = ERROR_ENCRYPTION;
+ return false;
+ } else {
+ switch (myCompressionVersion) {
+ case 2:
+ case 10:
+ break;
+ default:
+ myErrorCode = ERROR_COMPRESSION;
+ return false;
+ }
+ }
+ myBase->seek(10, false); // myBase offset: ^ + 12
+ PdbUtil::readUnsignedShort(*myBase, myNonTextOffset); // myBase offset: ^ + 14
+ PdbUtil::readUnsignedShort(*myBase, myNonTextOffsetReserved); // myBase offset: ^ + 16
+ myBase->seek(12, false); // myBase offset: ^ + 28
+ PdbUtil::readUnsignedShort(*myBase, myFootnoteRecords); // myBase offset: ^ + 30
+ PdbUtil::readUnsignedShort(*myBase, mySidebarRecords); // myBase offset: ^ + 32
+ PdbUtil::readUnsignedShort(*myBase, myBookmarksOffset); // myBase offset: ^ + 34
+ myBase->seek(2, false); // myBase offset: ^ + 36
+ PdbUtil::readUnsignedShort(*myBase, myNonTextOffsetExtraReserved); // myBase offset: ^ + 38
+ myBase->seek(2, false); // myBase offset: ^ + 40
+ PdbUtil::readUnsignedShort(*myBase, myImagedataOffset); // myBase offset: ^ + 42
+ PdbUtil::readUnsignedShort(*myBase, myImagedataOffsetReserved); // myBase offset: ^ + 44
+ PdbUtil::readUnsignedShort(*myBase, myMetadataOffset); // myBase offset: ^ + 46
+ PdbUtil::readUnsignedShort(*myBase, myMetadataOffsetReserved); // myBase offset: ^ + 48
+ PdbUtil::readUnsignedShort(*myBase, myFootnoteOffset); // myBase offset: ^ + 50
+ PdbUtil::readUnsignedShort(*myBase, mySidebarOffset); // myBase offset: ^ + 52
+ PdbUtil::readUnsignedShort(*myBase, myLastdataOffset); // myBase offset: ^ + 54
+
+ unsigned short endSectionIndex = header().Offsets.size();
+ myMaxRecordIndex = std::min((unsigned short) (myNonTextOffset - 1), (unsigned short) (endSectionIndex - 1));
+
+ myMaxRecordSize = 65535; // Maximum size of addressable space in PalmOS
+ // not more than 8192 bytes happens in the tested examples
+
+ if (myFootnoteRecords) {
+ bool isSuccess = processFootnoteIdsRecord();
+ if (!isSuccess) {
+ //TODO take in account returned bool value
+ //false if wrong footnotes amount anounced in zero record
+ //or corrupted or wrong footnote ids record
+ }
+ }
+
+ if (myImagedataOffset != myMetadataOffset) {
+ bool isSuccess = processImageHeaders();
+ if (!isSuccess) {
+ //TODO take in account returned bool value
+ //false if one of image record is corrupted
+ }
+ }
+
+ myBase->seek(header().Offsets[1], true);
+
+ /*
+ std::cerr << "EReaderStream::processZeroRecord():\n";
+ std::cerr << "PDB header indentificator : " << header().Id << "\n";
+ std::cerr << "PDB file system: sizeof opened : " << myBaseSize << "\n";
+ std::cerr << "PDB header/record[0] max index : " << myMaxRecordIndex << "\n";
+ std::cerr << "PDB record[0][0..2] compression : " << myCompressionVersion << "\n";
+ std::cerr << "EReader record[0] myNonTextOffset : " << myNonTextOffset << std::endl;
+ std::cerr << "EReader record[0] myNonTextOffset2 : " << myNonTextOffsetReserved << std::endl;
+ std::cerr << "EReader record[0] myFootnoteRecords : " << myFootnoteRecords << std::endl;
+ std::cerr << "EReader record[0] mySidebarRecords : " << mySidebarRecords << std::endl;
+ std::cerr << "EReader record[0] myBookmarksOffset : " << myBookmarksOffset << std::endl;
+ std::cerr << "EReader record[0] myNonTextOffset3 : " << myNonTextOffsetExtraReserved << std::endl;
+ std::cerr << "EReader record[0] myImagedataOffset : " << myImagedataOffset << std::endl;
+ std::cerr << "EReader record[0] myImagedataOffset2 : " << myImagedataOffsetReserved << std::endl;
+ std::cerr << "EReader record[0] myMetadataOffset : " << myMetadataOffset << std::endl;
+ std::cerr << "EReader record[0] myMetadataOffset2 : " << myMetadataOffsetReserved << std::endl;
+ std::cerr << "EReader record[0] myFootnoteOffset : " << myFootnoteOffset << std::endl;
+ std::cerr << "EReader record[0] mySidebarOffset : " << mySidebarOffset << std::endl;
+ std::cerr << "EReader record[0] myLastdataOffset : " << myLastdataOffset << std::endl;
+ std::cerr << "PDB header lastSectionIndex : " << endSectionIndex - 1 << "\n";
+ */
+ return true;
+}
+
+void EReaderStream::clearBuffer(unsigned char symbol) {
+ myBufferLength = std::remove(myBuffer, myBuffer + myBufferLength, symbol) - myBuffer;
+}
+
+bool EReaderStream::processFootnoteIdsRecord() {
+ char* footnoteIdBuffer = new char[myMaxRecordSize];
+ myBase->seek(header().Offsets[myFootnoteOffset], true);
+ const std::size_t currentOffset = recordOffset(myFootnoteOffset);
+ const std::size_t nextOffset = recordOffset(myFootnoteOffset + 1);
+ const std::size_t length = nextOffset - currentOffset;
+ myBase->read(footnoteIdBuffer, length);
+ std::string footnoteIdStr(footnoteIdBuffer, length);
+ unsigned short footnoteIndex = myFootnoteOffset + 1;
+ while (!footnoteIdStr.empty() && (footnoteIndex < myLastdataOffset)) {
+ std::string id = findFootnoteId(footnoteIdStr);
+ if (!id.empty()) {
+ myFootnotes[id] = footnoteIndex;
+ ++footnoteIndex;
+ }
+ }
+ delete[] footnoteIdBuffer;
+ return (myFootnoteRecords - 1 == (unsigned short)myFootnotes.size());
+}
+
+std::string EReaderStream::findFootnoteId(std::string &footnoteIdStr) const {
+ std::string resultStr;
+ if (!footnoteIdStr.empty()) {
+ std::size_t counter = 0;
+ for (; counter < footnoteIdStr.length(); ++counter) {
+ if (std::isalnum(footnoteIdStr[counter])) {
+ break;
+ }
+ }
+ const std::size_t startIdIndex = counter;
+ for (; counter < footnoteIdStr.length(); ++counter) {
+ if (footnoteIdStr[counter] == '\0') {
+ break;
+ }
+ }
+ const std::size_t endIdIndex = counter;
+ resultStr = footnoteIdStr.substr(startIdIndex, endIdIndex - startIdIndex);
+ footnoteIdStr = footnoteIdStr.substr(endIdIndex);
+ }
+ return resultStr;
+}
+
+const std::map<std::string, unsigned short>& EReaderStream::footnotes() const {
+ return myFootnotes;
+}
+
+bool EReaderStream::processImageHeaders() {
+ unsigned short recordIndex = myImagedataOffset;
+ bool result = true;
+ myBase->seek(header().Offsets[recordIndex], true);
+ while (recordIndex < myMetadataOffset && recordIndex < myLastdataOffset) {
+ result = result && addImageInfo(recordIndex);
+ ++recordIndex;
+ }
+ return result;
+}
+
+bool EReaderStream::addImageInfo(const unsigned short recordIndex) {
+ const std::size_t bufferLength = 128;
+ char *buffer = new char[bufferLength]; //TODO may be it's needed here more bytes
+ ImageInfo image;
+ const std::size_t currentOffset = recordOffset(recordIndex);
+ const std::size_t nextOffset = recordOffset(recordIndex + 1);
+
+ myBase->read(buffer, bufferLength);
+ std::string header(buffer, bufferLength);
+ delete[] buffer;
+
+ image.Offset = currentOffset + header.find("\x89PNG"); //TODO treat situation when there isn't PNG in first 128 bytes
+ image.Size = nextOffset - image.Offset;
+ const int endType = header.find(' ');
+ image.Type = ZLMimeType::get(header.substr(0, endType));
+ header = header.substr(endType + 1);
+ const int endId = header.find('\0');
+ const std::string id = header.substr(0, endId);
+ myBase->seek(nextOffset - currentOffset - bufferLength, false);
+ if (id.empty()) {
+ return false;
+ }
+ myImages[id] = image;
+ return true;
+}
+
+
+/*bool EReaderStream::hasExtraSections() const {
+ return false;
+ //return myMaxRecordIndex < header().Offsets.size() - 1;
+}*/
+
+EReaderStream::ImageInfo EReaderStream::imageLocation(const std::string& id) {
+ if (myImagedataOffset != myMetadataOffset && myImages.empty()) {
+ processImageHeaders();
+ }
+ const std::map<std::string, ImageInfo>::const_iterator it = myImages.find(id);
+ if (it != myImages.end()) {
+ return it->second;
+ } else {
+ return ImageInfo();
+ }
+}
+
+const std::map<std::string, EReaderStream::ImageInfo>& EReaderStream::images() const {
+ return myImages;
+}