/src/libe-book/src/lib/PDBParser.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 <string> |
12 | | #include <vector> |
13 | | |
14 | | #include <librevenge-stream/librevenge-stream.h> |
15 | | |
16 | | #include "libebook_utils.h" |
17 | | #include "EBOOKStreamView.h" |
18 | | #include "PDBParser.h" |
19 | | |
20 | | using std::unique_ptr; |
21 | | |
22 | | namespace libebook |
23 | | { |
24 | | |
25 | | namespace |
26 | | { |
27 | | |
28 | | struct HeaderData |
29 | | { |
30 | | HeaderData(); |
31 | | |
32 | | std::string m_name; |
33 | | unsigned m_version; |
34 | | unsigned m_appInfoID; |
35 | | unsigned m_sortInfoID; |
36 | | unsigned m_type; |
37 | | unsigned m_creator; |
38 | | unsigned m_nextRecordListID; |
39 | | unsigned m_numberOfRecords; |
40 | | std::vector<unsigned> m_recordOffsets; |
41 | | }; |
42 | | |
43 | | } |
44 | | |
45 | | namespace |
46 | | { |
47 | | |
48 | | HeaderData::HeaderData() |
49 | 0 | : m_name() |
50 | 0 | , m_version(0) |
51 | 0 | , m_appInfoID(0) |
52 | 0 | , m_sortInfoID(0) |
53 | 0 | , m_type(0) |
54 | 0 | , m_creator(0) |
55 | 0 | , m_nextRecordListID(0) |
56 | 0 | , m_numberOfRecords(0) |
57 | 0 | , m_recordOffsets() |
58 | 0 | { |
59 | 0 | } |
60 | | |
61 | | } |
62 | | |
63 | | struct PDBParserImpl |
64 | | { |
65 | | PDBParserImpl(librevenge::RVNGInputStream *input, librevenge::RVNGTextInterface *document); |
66 | | |
67 | | HeaderData m_header; |
68 | | librevenge::RVNGInputStream *m_input; |
69 | | librevenge::RVNGTextInterface *m_document; |
70 | | |
71 | | private: |
72 | | // disable copying |
73 | | PDBParserImpl(const PDBParserImpl &other); |
74 | | PDBParserImpl &operator=(const PDBParserImpl &other); |
75 | | }; |
76 | | |
77 | | PDBParserImpl::PDBParserImpl(librevenge::RVNGInputStream *const input, librevenge::RVNGTextInterface *const document) |
78 | 0 | : m_header() |
79 | 0 | , m_input(input) |
80 | 0 | , m_document(document) |
81 | 0 | { |
82 | 0 | } |
83 | | |
84 | | PDBParser::PDBParser(librevenge::RVNGInputStream *const input, librevenge::RVNGTextInterface *const document, |
85 | | const unsigned type, const unsigned creator) |
86 | 0 | : m_impl(new PDBParserImpl(input, document)) |
87 | 0 | { |
88 | 0 | readHeader(); |
89 | |
|
90 | 0 | if ((type != m_impl->m_header.m_type) || (creator != m_impl->m_header.m_creator)) |
91 | 0 | throw UnsupportedFormat(); |
92 | | |
93 | 0 | if ((0 == m_impl->m_header.m_numberOfRecords) |
94 | 0 | || (m_impl->m_header.m_recordOffsets.size() != m_impl->m_header.m_numberOfRecords)) |
95 | 0 | throw UnsupportedFormat(); |
96 | 0 | } |
97 | | |
98 | | PDBParser::~PDBParser() |
99 | 0 | { |
100 | 0 | } |
101 | | |
102 | | bool PDBParser::parse() |
103 | 0 | { |
104 | 0 | if (m_impl->m_header.m_appInfoID) |
105 | 0 | { |
106 | | // TODO: implement me |
107 | 0 | } |
108 | 0 | if (m_impl->m_header.m_sortInfoID) |
109 | 0 | { |
110 | | // TODO: implement me |
111 | 0 | } |
112 | |
|
113 | 0 | { |
114 | 0 | std::unique_ptr<librevenge::RVNGInputStream> input(getRecordStream(0)); |
115 | 0 | readIndexRecord(input.get()); |
116 | 0 | } |
117 | |
|
118 | 0 | readDataRecords(); |
119 | |
|
120 | 0 | return true; |
121 | 0 | } |
122 | | |
123 | | librevenge::RVNGTextInterface *PDBParser::getDocument() const |
124 | 0 | { |
125 | 0 | return m_impl->m_document; |
126 | 0 | } |
127 | | |
128 | | const char *PDBParser::getName() const |
129 | 0 | { |
130 | 0 | return m_impl->m_header.m_name.c_str(); |
131 | 0 | } |
132 | | |
133 | | librevenge::RVNGInputStream *PDBParser::getAppInfoRecord() const |
134 | 0 | { |
135 | | // TODO: implement me |
136 | 0 | return nullptr; |
137 | 0 | } |
138 | | |
139 | | librevenge::RVNGInputStream *PDBParser::getIndexRecord() const |
140 | 0 | { |
141 | 0 | return getRecordStream(0); |
142 | 0 | } |
143 | | |
144 | | unsigned PDBParser::getDataRecordCount() const |
145 | 0 | { |
146 | 0 | return m_impl->m_header.m_numberOfRecords - 1; |
147 | 0 | } |
148 | | |
149 | | librevenge::RVNGInputStream *PDBParser::getDataRecord(unsigned n) const |
150 | 0 | { |
151 | 0 | return getRecordStream(n + 1); |
152 | 0 | } |
153 | | |
154 | | librevenge::RVNGInputStream *PDBParser::getDataRecords() const |
155 | 0 | { |
156 | 0 | const auto begin = (long) m_impl->m_header.m_recordOffsets[1]; |
157 | 0 | m_impl->m_input->seek(0, librevenge::RVNG_SEEK_END); |
158 | 0 | const long end = m_impl->m_input->tell(); |
159 | |
|
160 | 0 | return new EBOOKStreamView(m_impl->m_input, begin, end); |
161 | 0 | } |
162 | | |
163 | | librevenge::RVNGInputStream *PDBParser::getDataRecords(unsigned first, unsigned last) const |
164 | 0 | { |
165 | 0 | if (first >= last) |
166 | 0 | return nullptr; |
167 | 0 | if ((m_impl->m_header.m_numberOfRecords - 1) < last) |
168 | 0 | return nullptr; |
169 | | |
170 | 0 | const auto begin = (long) m_impl->m_header.m_recordOffsets[first + 1]; |
171 | 0 | long end = 0; |
172 | 0 | if ((m_impl->m_header.m_numberOfRecords - 1) == last) // ends with the last record |
173 | 0 | { |
174 | 0 | m_impl->m_input->seek(0, librevenge::RVNG_SEEK_END); |
175 | 0 | end = m_impl->m_input->tell(); |
176 | 0 | } |
177 | 0 | else |
178 | 0 | end = (long) m_impl->m_header.m_recordOffsets[last + 1]; |
179 | |
|
180 | 0 | return new EBOOKStreamView(m_impl->m_input, begin, end); |
181 | 0 | } |
182 | | |
183 | | void PDBParser::readDataRecords() |
184 | 0 | { |
185 | 0 | for (unsigned i = 1; i != m_impl->m_header.m_numberOfRecords; ++i) |
186 | 0 | { |
187 | 0 | unique_ptr<librevenge::RVNGInputStream> record(getRecordStream(i)); |
188 | 0 | readDataRecord(record.get(), m_impl->m_header.m_numberOfRecords - 1 == i); |
189 | 0 | } |
190 | 0 | } |
191 | | |
192 | | void PDBParser::readHeader() |
193 | 0 | { |
194 | 0 | m_impl->m_input->seek(0, librevenge::RVNG_SEEK_SET); |
195 | |
|
196 | 0 | char name[32]; |
197 | 0 | unsigned nameLen = 0; |
198 | 0 | while (nameLen != 32) |
199 | 0 | { |
200 | 0 | unsigned char c = readU8(m_impl->m_input); |
201 | 0 | name[nameLen++] = (char) c; |
202 | 0 | if (0 == c) |
203 | 0 | break; |
204 | 0 | } |
205 | | // We read the whole name, but there was no terminating 0. Either a |
206 | | // broken file or not a PDB file at all... |
207 | 0 | if (nameLen == 32) |
208 | 0 | --nameLen; |
209 | 0 | name[nameLen] = 0; |
210 | 0 | m_impl->m_header.m_name.assign(name, nameLen); |
211 | | |
212 | | // skip the unused bytes of name and ignore 2 bytes of attributes |
213 | 0 | m_impl->m_input->seek(34, librevenge::RVNG_SEEK_SET); |
214 | 0 | m_impl->m_header.m_version = readU16(m_impl->m_input, true); |
215 | | // ignore dates, modification number and IDs; 6*4 = 24 bytes |
216 | 0 | skip(m_impl->m_input, 24); |
217 | 0 | m_impl->m_header.m_type = readU32(m_impl->m_input, true); |
218 | 0 | m_impl->m_header.m_creator = readU32(m_impl->m_input, true); |
219 | 0 | skip(m_impl->m_input, 4); // skip uniqueIDseed |
220 | 0 | m_impl->m_header.m_nextRecordListID = readU32(m_impl->m_input, true); |
221 | 0 | assert(m_impl->m_input->tell() == 76); |
222 | 0 | m_impl->m_header.m_numberOfRecords = readU16(m_impl->m_input, true); |
223 | 0 | if (m_impl->m_header.m_numberOfRecords > getRemainingLength(m_impl->m_input) / 8) |
224 | 0 | m_impl->m_header.m_numberOfRecords = getRemainingLength(m_impl->m_input) / 8; |
225 | | |
226 | | // read records |
227 | 0 | for (unsigned i = 0; i != m_impl->m_header.m_numberOfRecords; ++i) |
228 | 0 | { |
229 | 0 | m_impl->m_header.m_recordOffsets.push_back(readU32(m_impl->m_input, true)); |
230 | 0 | skip(m_impl->m_input, 4); // skip the uninteresting remainder |
231 | 0 | } |
232 | 0 | } |
233 | | |
234 | | librevenge::RVNGInputStream *PDBParser::getRecordStream(const unsigned n) const |
235 | 0 | { |
236 | 0 | if (n >= m_impl->m_header.m_numberOfRecords) |
237 | 0 | return nullptr; |
238 | | |
239 | 0 | const auto begin = (long) m_impl->m_header.m_recordOffsets[n]; |
240 | 0 | long end = 0; |
241 | 0 | if ((m_impl->m_header.m_numberOfRecords - 1) == n) // the last record |
242 | 0 | { |
243 | 0 | m_impl->m_input->seek(0, librevenge::RVNG_SEEK_END); |
244 | 0 | end = m_impl->m_input->tell(); |
245 | 0 | } |
246 | 0 | else |
247 | 0 | end = (long) m_impl->m_header.m_recordOffsets[n + 1]; |
248 | |
|
249 | 0 | return new EBOOKStreamView(m_impl->m_input, begin, end); |
250 | 0 | } |
251 | | |
252 | | } |
253 | | |
254 | | /* vim:set shiftwidth=2 softtabstop=2 expandtab: */ |