/src/libe-book/src/lib/SoftBookParser.cpp
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* |
3 | | * This file is part of the libe-book project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | */ |
9 | | |
10 | | #include <cassert> |
11 | | #include <deque> |
12 | | |
13 | | #include <librevenge-stream/librevenge-stream.h> |
14 | | |
15 | | #include "libebook_utils.h" |
16 | | #include "EBOOKStreamView.h" |
17 | | #include "SoftBookLZSSStream.h" |
18 | | #include "SoftBookParser.h" |
19 | | #include "SoftBookResourceDir.h" |
20 | | #include "SoftBookText.h" |
21 | | |
22 | | using std::shared_ptr; |
23 | | |
24 | | namespace libebook |
25 | | { |
26 | | |
27 | | namespace |
28 | | { |
29 | | |
30 | | static const char *const SOFTBOOK_TEXT_FILE_NAME = " "; |
31 | | |
32 | | } |
33 | | |
34 | | namespace |
35 | | { |
36 | | |
37 | | struct SoftBookParserException {}; |
38 | | |
39 | | #ifdef UNUSED |
40 | | void skipStandardHeader(std::shared_ptr<librevenge::RVNGInputStream> stream) |
41 | | { |
42 | | assert(bool(stream)); |
43 | | skip(stream.get(), 32); |
44 | | } |
45 | | #endif |
46 | | |
47 | | SoftBookLZSSStream::Configuration makeLZSSConfiguration(const unsigned offsetBits, const unsigned lengthBits, const unsigned uncompressedLength) |
48 | 0 | { |
49 | 0 | SoftBookLZSSStream::Configuration configuration; |
50 | |
|
51 | 0 | configuration.offsetBits = offsetBits; |
52 | 0 | configuration.lengthBits = lengthBits; |
53 | 0 | configuration.uncompressedLength = uncompressedLength; |
54 | 0 | configuration.fillPos = 1; |
55 | 0 | configuration.allowOverflow = false; |
56 | 0 | configuration.bigEndian = true; |
57 | |
|
58 | 0 | return configuration; |
59 | 0 | } |
60 | | |
61 | | } |
62 | | |
63 | | namespace |
64 | | { |
65 | | |
66 | | class Resource |
67 | | { |
68 | | struct ResourceIndexEntry |
69 | | { |
70 | | ResourceIndexEntry(); |
71 | | |
72 | | unsigned id; |
73 | | unsigned length; |
74 | | unsigned offset; |
75 | | }; |
76 | | |
77 | | typedef std::deque<ResourceIndexEntry> ResourceIndex_t; |
78 | | |
79 | | public: |
80 | | enum IndexMode |
81 | | { |
82 | | INDEX_MODE_NORMAL, |
83 | | INDEX_MODE_WORKAROUND |
84 | | }; |
85 | | |
86 | | public: |
87 | | Resource(const char *name, shared_ptr<librevenge::RVNGInputStream> stream, const SoftBookHeader &header); |
88 | | |
89 | | unsigned getRecordCount() const; |
90 | | |
91 | | unsigned getRecordId(unsigned n) const; |
92 | | |
93 | | shared_ptr<librevenge::RVNGInputStream> getRecord(unsigned n) const; |
94 | | |
95 | | private: |
96 | | void readResourceIndex(IndexMode mode); |
97 | | |
98 | | private: |
99 | | const shared_ptr<librevenge::RVNGInputStream> m_stream; |
100 | | long m_start; |
101 | | ResourceIndex_t m_resourceIndex; |
102 | | }; |
103 | | |
104 | | Resource::ResourceIndexEntry::ResourceIndexEntry() |
105 | 0 | : id(0) |
106 | 0 | , length(0) |
107 | 0 | , offset(0) |
108 | 0 | { |
109 | 0 | } |
110 | | |
111 | | Resource::Resource(const char *const name, const shared_ptr<librevenge::RVNGInputStream> stream, const SoftBookHeader &header) |
112 | 0 | : m_stream(stream) |
113 | 0 | , m_start(stream->tell()) |
114 | 0 | , m_resourceIndex() |
115 | 0 | { |
116 | 0 | const unsigned version = readU16(stream, true); |
117 | 0 | if (1 != version) |
118 | 0 | { |
119 | 0 | EBOOK_DEBUG_MSG(("version of resource %s is not 1\n", name)); |
120 | 0 | } |
121 | |
|
122 | 0 | const char *const type = reinterpret_cast<const char *>(readNBytes(stream, 4)); |
123 | 0 | if (!std::equal(name, name + 4, type)) |
124 | 0 | { |
125 | 0 | EBOOK_DEBUG_MSG(("type of resource does not match: %s expected, %s got\n", name, type)); |
126 | 0 | } |
127 | |
|
128 | 0 | readResourceIndex((SOFTBOOK_COLOR_MODE_GRAYSCALE == header.getColorMode()) ? INDEX_MODE_WORKAROUND : INDEX_MODE_NORMAL); |
129 | 0 | } |
130 | | |
131 | | unsigned Resource::getRecordCount() const |
132 | 0 | { |
133 | 0 | return (unsigned) m_resourceIndex.size(); |
134 | 0 | } |
135 | | |
136 | | unsigned Resource::getRecordId(const unsigned n) const |
137 | 0 | { |
138 | 0 | return (n < m_resourceIndex.size()) ? m_resourceIndex[n].id : 0; |
139 | 0 | } |
140 | | |
141 | | shared_ptr<librevenge::RVNGInputStream> Resource::getRecord(const unsigned n) const |
142 | 0 | { |
143 | 0 | shared_ptr<librevenge::RVNGInputStream> record; |
144 | |
|
145 | 0 | if (n < m_resourceIndex.size()) |
146 | 0 | { |
147 | 0 | const ResourceIndexEntry &entry = m_resourceIndex[n]; |
148 | 0 | const long start = m_start + (long) entry.offset; |
149 | 0 | record.reset(new EBOOKStreamView(m_stream.get(), start, start + (long) entry.length)); |
150 | 0 | } |
151 | |
|
152 | 0 | return record; |
153 | 0 | } |
154 | | |
155 | | void Resource::readResourceIndex(const IndexMode mode) |
156 | 0 | { |
157 | 0 | m_stream->seek(m_start + 0xa, librevenge::RVNG_SEEK_SET); |
158 | 0 | const unsigned offset = readU32(m_stream, true); |
159 | 0 | m_stream->seek(m_start + (long) offset, librevenge::RVNG_SEEK_SET); |
160 | |
|
161 | 0 | while (!m_stream->isEnd()) |
162 | 0 | { |
163 | 0 | ResourceIndexEntry entry; |
164 | 0 | if (INDEX_MODE_NORMAL == mode) |
165 | 0 | { |
166 | 0 | entry.id = readU16(m_stream, true); |
167 | 0 | entry.length = readU32(m_stream, true); |
168 | 0 | entry.offset = readU32(m_stream, true); |
169 | 0 | } |
170 | 0 | else |
171 | 0 | { |
172 | | // this is really LITTLE endian |
173 | 0 | entry.id = readU32(m_stream); |
174 | 0 | entry.length = readU32(m_stream); |
175 | 0 | entry.offset = readU32(m_stream); |
176 | 0 | } |
177 | 0 | skip(m_stream, 2); |
178 | 0 | m_resourceIndex.push_front(entry); |
179 | 0 | } |
180 | 0 | } |
181 | | |
182 | | } |
183 | | |
184 | | SoftBookParser::SoftBookParser(librevenge::RVNGInputStream *const input, librevenge::RVNGTextInterface *const document) |
185 | 975 | : m_header(input) |
186 | 975 | , m_input(input) |
187 | 975 | , m_collector(document) |
188 | 975 | , m_resources() |
189 | 975 | , m_text() |
190 | 975 | { |
191 | 975 | } |
192 | | |
193 | | bool SoftBookParser::parse() |
194 | 975 | { |
195 | 975 | SoftBookResourceDir resourceDir(m_input, m_header); |
196 | 975 | m_resources = resourceDir.getTypeStream(); |
197 | | |
198 | 975 | createTextStream(); |
199 | | |
200 | 975 | SoftBookText text(m_text.get(), &m_collector); |
201 | 975 | return text.parse(); |
202 | 975 | } |
203 | | |
204 | | void SoftBookParser::createTextStream() |
205 | 245 | { |
206 | 245 | assert(bool(m_resources)); |
207 | | |
208 | 245 | const shared_ptr<librevenge::RVNGInputStream> textStream = getFileStream(SOFTBOOK_TEXT_FILE_NAME); |
209 | | |
210 | 245 | if (m_header.getCompressed()) |
211 | 1 | { |
212 | 1 | const shared_ptr<librevenge::RVNGInputStream> compressionInfo = getFileStream("!!cm"); |
213 | 1 | const Resource cm("!!cm", compressionInfo, m_header); |
214 | 1 | unsigned windowSize = 14; |
215 | 1 | unsigned lookAheadSize = 3; |
216 | 1 | unsigned textLength = 0; |
217 | | |
218 | 1 | for (unsigned i = 0; i != cm.getRecordCount(); ++i) |
219 | 0 | { |
220 | 0 | const unsigned id = cm.getRecordId(i); |
221 | 0 | if (0x64 == id) |
222 | 0 | { |
223 | 0 | const shared_ptr<librevenge::RVNGInputStream> record = cm.getRecord(i); |
224 | 0 | skip(record, 6); |
225 | 0 | windowSize = readU16(record, true); |
226 | 0 | lookAheadSize = readU16(record, true); |
227 | 0 | break; |
228 | 0 | } |
229 | 0 | else if (0x65 == id) |
230 | 0 | { |
231 | 0 | const shared_ptr<librevenge::RVNGInputStream> record = cm.getRecord(i); |
232 | 0 | seek(record, getRemainingLength(record) - 10); |
233 | 0 | textLength = readU32(record, true); |
234 | 0 | } |
235 | 0 | else |
236 | 0 | { |
237 | 0 | EBOOK_DEBUG_MSG(("unknown resource ID %x\n", id)); |
238 | 0 | } |
239 | 0 | } |
240 | | |
241 | 1 | m_text.reset(new SoftBookLZSSStream(textStream.get(), makeLZSSConfiguration(windowSize, lookAheadSize, textLength))); |
242 | 1 | } |
243 | 244 | else |
244 | 244 | m_text = textStream; |
245 | 245 | } |
246 | | |
247 | | shared_ptr<librevenge::RVNGInputStream> SoftBookParser::getFileStream(const char *const name) const |
248 | 246 | { |
249 | 246 | const shared_ptr<librevenge::RVNGInputStream> stream(m_resources->getSubStreamByName(name)); |
250 | 246 | if (!stream) |
251 | 72 | { |
252 | 72 | EBOOK_DEBUG_MSG(("resource %s not found\n", name)); |
253 | 72 | throw SoftBookParserException(); |
254 | 72 | } |
255 | | |
256 | | // skip file header |
257 | | // TODO: check that the resource type matches name |
258 | 174 | skip(stream, 20); |
259 | | |
260 | 174 | return stream; |
261 | 246 | } |
262 | | |
263 | | } |
264 | | |
265 | | /* vim:set shiftwidth=2 softtabstop=2 expandtab: */ |