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.
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 *)