QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
QGCArchiveDeviceBase.cc
Go to the documentation of this file.
2#include "QGCFileHelper.h"
3#include "QGClibarchive.h"
5
6#include <archive.h>
7#include <archive_entry.h>
8
9#include <cstring>
10
11QGC_LOGGING_CATEGORY(QGCArchiveDeviceBaseLog, "Utilities.QGCArchiveDeviceBase")
12
13// ============================================================================
14// Constructors / Destructor
15// ============================================================================
16
17QGCArchiveDeviceBase::QGCArchiveDeviceBase(const QString &filePath, QObject *parent)
18 : QIODevice(parent)
19 , _filePath(filePath)
20 , _ownsSource(true)
21{
22}
23
24QGCArchiveDeviceBase::QGCArchiveDeviceBase(QIODevice *source, QObject *parent)
25 : QIODevice(parent)
26 , _sourceDevice(source)
27 , _ownsSource(false)
28{
29}
30
35
36// ============================================================================
37// QIODevice Interface
38// ============================================================================
39
41{
42 if (_archive) {
43 archive_read_free(_archive);
44 _archive = nullptr;
45 }
46
47 resetState();
48
49 if (_ownsSource) {
50 _ownedSource.reset();
51 _sourceDevice = nullptr;
52 }
53
54 QIODevice::close();
55}
56
58{
59 return _buffer.size() + QIODevice::bytesAvailable();
60}
61
63{
64 if (!_errorString.isEmpty()) {
65 return _errorString;
66 }
67 return QIODevice::errorString();
68}
69
70// ============================================================================
71// Protected QIODevice Methods
72// ============================================================================
73
74qint64 QGCArchiveDeviceBase::readData(char *data, qint64 maxSize)
75{
76 if (!isReadyToRead()) {
77 return -1;
78 }
79
80 if (_eof && _buffer.isEmpty()) {
81 return 0; // EOF
82 }
83
84 // Fill buffer if needed
85 while (_buffer.size() < maxSize && !_eof) {
86 if (!fillBuffer()) {
87 if (_buffer.isEmpty()) {
88 return _eof ? 0 : -1;
89 }
90 break;
91 }
92 }
93
94 // Return data from buffer
95 const qint64 bytesToCopy = qMin(static_cast<qint64>(_buffer.size()), maxSize);
96 if (bytesToCopy > 0) {
97 std::memcpy(data, _buffer.constData(), static_cast<size_t>(bytesToCopy));
98 _buffer.remove(0, static_cast<int>(bytesToCopy));
99 }
100
101 return bytesToCopy;
102}
103
104qint64 QGCArchiveDeviceBase::writeData(const char *, qint64)
105{
106 return -1; // Read-only device
107}
108
109// ============================================================================
110// Protected Implementation Methods
111// ============================================================================
112
114{
115 if (_filePath.isEmpty()) {
116 _errorString = QStringLiteral("File path is empty");
117 return false;
118 }
119
120 const bool isResource = QGCFileHelper::isQtResource(_filePath);
121
122 if (isResource) {
123 // Load Qt resource into memory
124 QFile resourceFile(_filePath);
125 if (!resourceFile.open(QIODevice::ReadOnly)) {
126 _errorString = QStringLiteral("Failed to open resource: ") + _filePath;
127 return false;
128 }
129 _resourceData = resourceFile.readAll();
130 resourceFile.close();
131
132 // Create QBuffer as source
133 auto buffer = std::make_unique<QBuffer>();
134 buffer->setData(_resourceData);
135 if (!buffer->open(QIODevice::ReadOnly)) {
136 _errorString = QStringLiteral("Failed to open buffer for resource");
137 return false;
138 }
139 _ownedSource = std::move(buffer);
141 } else {
142 auto file = std::make_unique<QFile>(_filePath);
143 if (!file->open(QIODevice::ReadOnly)) {
144 _errorString = QStringLiteral("Failed to open file: ") + _filePath;
145 return false;
146 }
147 _ownedSource = std::move(file);
149 }
150
151 return true;
152}
153
155{
156 if (!_archive) {
157 return;
158 }
159
160 archive_read_support_filter_all(_archive);
161
162 if (allFormats) {
163 archive_read_support_format_all(_archive);
164 } else {
165 archive_read_support_format_raw(_archive);
166 }
167}
168
170{
171 if (!_sourceDevice || !_sourceDevice->isOpen() || !_sourceDevice->isReadable()) {
172 _errorString = QStringLiteral("Source device is not ready");
173 return false;
174 }
175
176 // Enable seek callback for random-access devices (improves ZIP performance)
177 if (!_sourceDevice->isSequential()) {
178 archive_read_set_seek_callback(_archive, QGClibarchive::deviceSeekCallback);
179 }
180
181 const int result = archive_read_open2(_archive, _sourceDevice,
182 nullptr, // open callback
186
187 if (result != ARCHIVE_OK) {
188 _errorString = QString::fromUtf8(archive_error_string(_archive));
189 archive_read_free(_archive);
190 _archive = nullptr;
191 return false;
192 }
193
194 return true;
195}
196
198{
199 if (_eof || !_archive) {
200 return false;
201 }
202
203 char readBuffer[QGCFileHelper::kBufferSizeMax];
204 const la_ssize_t bytesRead = archive_read_data(_archive, readBuffer, sizeof(readBuffer));
205
206 if (bytesRead < 0) {
207 _errorString = QString::fromUtf8(archive_error_string(_archive));
208 qCWarning(QGCArchiveDeviceBaseLog) << "Read error:" << _errorString;
209 return false;
210 }
211
212 if (bytesRead == 0) {
213 _eof = true;
214 return false;
215 }
216
217 _buffer.append(readBuffer, static_cast<int>(bytesRead));
218 return true;
219}
220
222{
223 if (!_archive) {
224 return;
225 }
226
227 const char *fmt = archive_format_name(_archive);
228 _formatName = fmt ? QString::fromUtf8(fmt) : QString();
229
230 const char *flt = archive_filter_name(_archive, 0);
231 _filterName = flt ? QString::fromUtf8(flt) : QStringLiteral("none");
232}
233
235{
236 _buffer.clear();
237 _eof = false;
238 _formatName.clear();
239 _filterName.clear();
240}
Base class for QIODevice wrappers using libarchive.
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Private implementation details for QGCCompression.
Base class for QIODevice wrappers that use libarchive for decompression.
void captureFormatInfo()
Capture format info from the archive after first header read.
virtual bool isReadyToRead() const =0
qint64 readData(char *data, qint64 maxSize) override
std::unique_ptr< QIODevice > _ownedSource
QGCArchiveDeviceBase(const QString &filePath, QObject *parent=nullptr)
void configureArchiveFormats(bool allFormats)
virtual void resetState()
Reset all state (called by close())
qint64 writeData(const char *data, qint64 maxSize) override
qint64 bytesAvailable() const override
bool isQtResource(const QString &path)
constexpr size_t kBufferSizeMax
Maximum buffer size for I/O operations.
la_int64_t deviceSkipCallback(struct archive *, void *clientData, la_int64_t request)
la_ssize_t deviceReadCallback(struct archive *, void *clientData, const void **buffer)
la_int64_t deviceSeekCallback(struct archive *, void *clientData, la_int64_t offset, int whence)
int deviceCloseCallback(struct archive *, void *)