Kstars

binfilehelper.cpp
1/*
2 SPDX-FileCopyrightText: 2008 Akarsh Simha <akarshsimha@gmail.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "binfilehelper.h"
8
9#include "byteorder.h"
10#include "auxiliary/kspaths.h"
11
12#include <QStandardPaths>
13
14class BinFileHelper;
15
17{
18 init();
19}
20
22{
23 qDeleteAll(fields);
24 fields.clear();
25 if (fileHandle)
26 closeFile();
27}
28
29void BinFileHelper::init()
30{
31 if (fileHandle)
32 fclose(fileHandle);
33
34 fileHandle = nullptr;
35 indexUpdated = false;
36 FDUpdated = false;
37 RSUpdated = false;
38 preambleUpdated = false;
39 byteswap = false;
40 errnum = ERR_NULL;
41 recordCount = 0;
42 recordSize = 0;
43 nfields = 0;
44 indexSize = 0;
45 itableOffset = 0;
46 dataOffset = 0;
47 recordCount = 0;
48 versionNumber = 0;
49}
50
51void BinFileHelper::clearFields()
52{
53 qDeleteAll(fields);
54 fields.clear();
55}
56
58{
59 QString FilePath = KSPaths::locate(QStandardPaths::AppLocalDataLocation, fileName);
60 QByteArray b = FilePath.toLatin1();
61 const char *filepath = b.data();
62 FILE *f = fopen(filepath, "rb");
63 if (f)
64 {
65 fclose(f);
66 return true;
67 }
68 else
69 return false;
70}
71
72FILE *BinFileHelper::openFile(const QString &fileName)
73{
74 QString FilePath = KSPaths::locate(QStandardPaths::AppLocalDataLocation, fileName);
75 init();
76 QByteArray b = FilePath.toLatin1();
77 const char *filepath = b.data();
78
79 fileHandle = fopen(filepath, "rb");
80
81 if (!fileHandle)
82 {
83 errnum = ERR_FILEOPEN;
84 return nullptr;
85 }
86 return fileHandle;
87}
88
89enum BinFileHelper::Errors BinFileHelper::__readHeader()
90{
91 qint16 endian_id, i;
92 char ASCII_text[125];
93 int ret = 0;
94
95 // Read the preamble
96 if (!fileHandle)
97 return ERR_FILEOPEN;
98
99 rewind(fileHandle);
100
101 // Read the first 124 bytes of the binary file which contains a general text about the binary data.
102 // e.g. "KStars Star Data v1.0. To be read using the 32-bit StarData structure only"
103 ret = fread(ASCII_text, 124, 1, fileHandle); // cppcheck-suppress redundantAssignment
104 ASCII_text[124] = '\0';
105 headerText = ASCII_text;
106
107 // Find out endianess from reading "KS" 0x4B53 in the binary file which was encoded on a little endian machine
108 // Therefore, in the binary file it is written as 53 4B (little endian as least significant byte is stored first),
109 // and when read on a little endian machine then it results in 0x4B53 (least significant byte is stored first in memory),
110 // whereas a big endian machine would read it as 0x534B (most significant byte is stored first in memory).
111 ret = fread(&endian_id, 2, 1, fileHandle); // cppcheck-suppress redundantAssignment
112 if (endian_id != 0x4B53)
113 byteswap = 1;
114 else
115 byteswap = 0;
116
117 ret = fread(&versionNumber, 1, 1, fileHandle); // cppcheck-suppress redundantAssignment
118
119 preambleUpdated = true;
120 // Read the field descriptor table
121 ret = fread(&nfields, 2, 1, fileHandle); // cppcheck-suppress redundantAssignment
122 if (byteswap)
123 nfields = bswap_16(nfields);
124
125 qDeleteAll(fields);
126 fields.clear();
127 for (i = 0; i < nfields; ++i)
128 {
129 dataElement *de = new dataElement;
130
131 // Read 16 byte dataElement that describe each field (name[8], size[1], type[1], scale[4])
132 if (!fread(de, sizeof(dataElement), 1, fileHandle))
133 {
134 delete de;
135 qDeleteAll(fields);
136 fields.clear();
137 return ERR_FD_TRUNC;
138 }
139 if (byteswap)
140 de->scale = bswap_32(de->scale);
141 fields.append(de);
142 }
143
144 if (!RSUpdated)
145 {
146 recordSize = 0;
147 for (i = 0; i < fields.size(); ++i)
148 recordSize += fields[i]->size;
149 RSUpdated = true;
150 }
151
152 FDUpdated = true;
153
154 // Read the index table
155 ret = fread(&indexSize, 4, 1, fileHandle); // cppcheck-suppress redundantAssignment
156 if (byteswap)
157 indexSize = bswap_32(indexSize);
158
159 quint32 j;
160 quint32 ID;
161 quint32 offset;
162 quint32 prev_offset;
163 quint32 nrecs;
164 quint32 prev_nrecs;
165
166 // Find out current offset so far in the binary file (how many bytes we read so far)
167 itableOffset = ftell(fileHandle);
168
169 prev_offset = 0;
170 prev_nrecs = 0;
171 recordCount = 0;
172
173 indexCount.clear();
174 indexOffset.clear();
175
176 if (indexSize == 0)
177 {
178 errorMessage = QStringLiteral("Zero index size!");
179 return ERR_INDEX_TRUNC;
180 }
181
182 // We read each 12-byte index entry (ID[4], Offset[4] within file in bytes, nrec[4] # of Records).
183 // After reading all the indexes, we are (itableOffset + indexSize * 12) bytes within the file
184 // indexSize is usually the size of the HTM level (eg. HTM level 3 --> 512)
185 for (j = 0; j < indexSize; ++j)
186 {
187 if (!fread(&ID, 4, 1, fileHandle))
188 {
189 errorMessage = QStringLiteral("Table truncated before expected! Read i = %1 index entries so far").arg(QString::number(j));
190 return ERR_INDEX_TRUNC;
191 }
192
193 if (byteswap)
194 ID = bswap_32(ID);
195
196 if (ID >= indexSize)
197 {
198 errorMessage = QString::asprintf("ID %u is greater than the expected number of expected entries (%u)", ID, indexSize);
199 return ERR_INDEX_BADID;
200 }
201
202 if (ID != j)
203 {
204 errorMessage = QString::asprintf("Found ID %u, at the location where ID %u was expected", ID, j);
206 }
207
208 if (!fread(&offset, 4, 1, fileHandle))
209 {
210 errorMessage = QString::asprintf("Table truncated before expected! Read i = %d index entries so far", j);
211 return ERR_BADSEEK;
212 }
213
214 if (byteswap)
215 offset = bswap_32(offset);
216
217 if (!fread(&nrecs, 4, 1, fileHandle))
218 {
219 errorMessage = QString::asprintf("Table truncated before expected! Read i = %d index entries so far", j);
220 return ERR_BADSEEK;
221 }
222
223 if (byteswap)
224 nrecs = bswap_32(nrecs);
225
226 //if (prev_offset != 0 && prev_nrecs != (-prev_offset + offset) / recordSize)
227 if (prev_offset != 0 && prev_nrecs != (offset - prev_offset) / recordSize)
228 {
229 errorMessage = QString::asprintf("Expected %u = (%X - %x) / %x records, but found %u, in index entry %u",
230 (offset - prev_offset) / recordSize, offset, prev_offset, recordSize, prev_nrecs,
231 j - 1);
232 return ERR_INDEX_BADOFFSET;
233 }
234
235 indexOffset.append(offset);
236 indexCount.append(nrecs);
237
238 recordCount += nrecs;
239 prev_offset = offset;
240 prev_nrecs = nrecs;
241 }
242
243 dataOffset = ftell(fileHandle);
244
245 indexUpdated = true;
246
247 return ERR_NULL;
248}
249
251{
252 switch ((errnum = __readHeader()))
253 {
254 case ERR_NULL:
255 return true;
256 break;
257 case ERR_FILEOPEN:
258 return false;
259 break;
260 case ERR_FD_TRUNC:
261 clearFields();
262 break;
263 case ERR_INDEX_TRUNC:
264 case ERR_INDEX_BADID:
266 case ERR_BADSEEK:
268 {
269 indexOffset.clear();
270 indexCount.clear();
271 }
272 }
273 return false;
274}
275
277{
278 fclose(fileHandle);
279 fileHandle = nullptr;
280}
281
283{
284 int err = errnum;
285 errnum = ERR_NULL;
286 return err;
287}
288
290{
291 QString erm = errorMessage;
292 errorMessage = "";
293 return erm;
294}
295
296struct dataElement BinFileHelper::getField(const QString &fieldName) const
297{
298 dataElement de;
299
300 for (auto &field : fields)
301 {
302 if (field->name == fieldName)
303 {
304 de = *field;
305 return de;
306 }
307 }
308 return de; // Returns junk!
309}
310
311bool BinFileHelper::isField(const QString &fieldName) const
312{
313 for (auto &field : fields)
314 {
315 if (field->name == fieldName)
316 return true;
317 }
318 return false;
319}
320
321int BinFileHelper::unsigned_KDE_fseek(FILE *stream, quint32 offset, int whence)
322{
323 Q_ASSERT(stream);
324 int ret = 0;
325 if (offset <= ((quint32)1 << 31) - 1)
326 {
327 ret = fseek(stream, offset, whence);
328 }
329 else
330 {
331 // Do the fseek in two steps
332 ret = fseek(stream, ((quint32)1 << 31) - 1, whence);
333 if (!ret)
334 ret = fseek(stream, offset - ((quint32)1 << 31) + 1, SEEK_CUR);
335 }
336 return ret;
337}
This class provides utility functions to handle binary data files in the format prescribed by KStars.
static int unsigned_KDE_fseek(FILE *stream, quint32 offset, int whence)
Wrapper around fseek for large offsets.
static bool testFileExists(const QString &fileName)
Checks if a file exists.
void closeFile()
Close the binary data file.
bool readHeader()
Read the header and index table from the file and fill up the QVector s with the entries.
int getErrorNumber()
Get error number.
BinFileHelper()
Constructor.
~BinFileHelper()
Destructor.
Errors
An enum providing user-friendly names for errors encountered.
FILE * openFile(const QString &fileName)
WARNING: This function may not be compatible in other locales, because it calls QString::toAscii.
QString getError()
Get error string.
bool isField(const QString &FieldName) const
Check whether a field exists.
char * data()
QString arg(Args &&... args) const const
QString asprintf(const char *cformat,...)
QString number(double n, char format, int precision)
QByteArray toLatin1() const const
A structure describing a data field in the binary file.
qint32 scale
Field scale.
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:01 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.