/* * Copyright (C) 2004-2012 Geometer Plus * * 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 #include #include #include #include #include "PalmDocStream.h" #include "DocDecompressor.h" #include "HuffDecompressor.h" PalmDocStream::PalmDocStream(const ZLFile &file) : PalmDocLikeStream(file) { } PalmDocStream::~PalmDocStream() { close(); } bool PalmDocStream::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; } const unsigned short recordSize = nextOffset - currentOffset; switch(myCompressionVersion) { case 17480://'DH' // HuffCDic compression myBufferLength = myHuffDecompressorPtr->decompress(*myBase, myBuffer, recordSize, myMaxRecordSize); //if (myHuffDecompressorPtr->error()) { // myErrorCode = ERROR_UNKNOWN; //} break; case 2: // PalmDoc compression myBufferLength = DocDecompressor().decompress(*myBase, myBuffer, recordSize, myMaxRecordSize); break; case 1: // No compression myBufferLength = myBase->read(myBuffer, std::min(recordSize, myMaxRecordSize)); break; } myBufferOffset = 0; return true; } bool PalmDocStream::processZeroRecord() { // Uses with offset presetting to zero record offset value PdbUtil::readUnsignedShort(*myBase, myCompressionVersion); // myBase offset: ^ + 2 switch (myCompressionVersion) { case 1: case 2: case 17480: break; default: myErrorCode = ERROR_COMPRESSION; return false; } myBase->seek(2, false); // myBase offset: ^ + 4 PdbUtil::readUnsignedLongBE(*myBase, myTextLength); // myBase offset: ^ + 8 PdbUtil::readUnsignedShort(*myBase, myTextRecordNumber); // myBase offset: ^ + 10 unsigned short endSectionIndex = header().Offsets.size(); myMaxRecordIndex = std::min(myTextRecordNumber, (unsigned short)(endSectionIndex - 1)); //TODO Insert in this point error message about uncompatible records and numRecords from Header PdbUtil::readUnsignedShort(*myBase, myMaxRecordSize); // myBase offset: ^ + 12 if (myMaxRecordSize == 0) { myErrorCode = ERROR_UNKNOWN; return false; } /* std::cerr << "PalmDocStream::processRecord0():\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 << "PDB record[0][2..4] spare : " << mySpare << "\n"; std::cerr << "PDB record[0][4..8] text length : " << myTextLength << "\n"; std::cerr << "PDB record[0][8..10] text records : " << myTextRecords << "\n"; std::cerr << "PDB record[0][10..12] max record size: " << myMaxRecordSize << "\n"; */ if (header().Id == "BOOKMOBI") { unsigned short encrypted = 0; PdbUtil::readUnsignedShort(*myBase, encrypted); // myBase offset: ^ + 14 if (encrypted) { //Always = 2, if encrypted myErrorCode = ERROR_ENCRYPTION; return false; } } else { myBase->seek(2, false); } if (myCompressionVersion == 17480) { unsigned long mobiHeaderLength; unsigned long huffSectionIndex; unsigned long huffSectionNumber; unsigned short extraFlags; unsigned long initialOffset = header().Offsets[0]; // myBase offset: ^ myBase->seek(6, false); // myBase offset: ^ + 20 PdbUtil::readUnsignedLongBE(*myBase, mobiHeaderLength); // myBase offset: ^ + 24 myBase->seek(0x70 - 24, false); // myBase offset: ^ + 102 (0x70) PdbUtil::readUnsignedLongBE(*myBase, huffSectionIndex); // myBase offset: ^ + 106 (0x74) PdbUtil::readUnsignedLongBE(*myBase, huffSectionNumber); // myBase offset: ^ + 110 (0x78) if (mobiHeaderLength >= 244) { myBase->seek(0xF2 - 0x78, false); // myBase offset: ^ + 242 (0xF2) PdbUtil::readUnsignedShort(*myBase, extraFlags); // myBase offset: ^ + 244 (0xF4) } else { extraFlags = 0; } /* std::cerr << "mobi header length: " << mobiHeaderLength << "\n"; std::cerr << "Huff's start record : " << huffSectionIndex << " from " << endSectionIndex - 1 << "\n"; std::cerr << "Huff's records number: " << huffSectionNumber << "\n"; std::cerr << "Huff's extraFlags : " << extraFlags << "\n"; */ const unsigned long endHuffSectionIndex = huffSectionIndex + huffSectionNumber; if (endHuffSectionIndex > endSectionIndex || huffSectionNumber <= 1) { myErrorCode = ERROR_COMPRESSION; return false; } const unsigned long endHuffDataOffset = recordOffset(endHuffSectionIndex); std::vector::const_iterator beginHuffSectionOffsetIt = header().Offsets.begin() + huffSectionIndex; // point to first Huff section std::vector::const_iterator endHuffSectionOffsetIt = header().Offsets.begin() + endHuffSectionIndex; // point behind last Huff section myHuffDecompressorPtr = new HuffDecompressor(*myBase, beginHuffSectionOffsetIt, endHuffSectionOffsetIt, endHuffDataOffset, extraFlags); myBase->seek(initialOffset, true); // myBase offset: ^ + 14 } return true; } bool PalmDocStream::hasExtraSections() const { return myMaxRecordIndex < header().Offsets.size() - 1; } std::pair PalmDocStream::imageLocation(const PdbHeader &header, int index) const { index += myMaxRecordIndex + 1; int recordNumber = header.Offsets.size(); if (index > recordNumber - 1) { return std::make_pair(-1, -1); } else { int start = header.Offsets[index]; int end = (index < recordNumber - 1) ? header.Offsets[index + 1] : myBase->offset(); return std::make_pair(start, end - start); } } int PalmDocStream::firstImageLocationIndex(const std::string &fileName) { shared_ptr fileStream = ZLFile(fileName).inputStream(); if (fileStream.isNull() || !fileStream->open()) { return -1; } bool found = false; int index = 0; char bu[5] = { 0 }; std::pair firstImageLocation = imageLocation(header(), 0); fileStream->seek(firstImageLocation.first, false); while ((firstImageLocation.first > 0) && (firstImageLocation.second > 0)) { if (firstImageLocation.second > 4) { fileStream->read(bu, 4); static const char jpegStart[2] = { (char)0xFF, (char)0xd8 }; if (std::strncmp(bu, "BM", 2) == 0 || std::strncmp(bu, "GIF8", 4) == 0 || std::strncmp(bu, jpegStart, 2) == 0) { found = true; break; } fileStream->seek(firstImageLocation.second - 4, false); } else { fileStream->seek(firstImageLocation.second, false); } index++; firstImageLocation = imageLocation(header(), index); } fileStream->close(); return found ? index : -1; }