|
1 /* borrowed from https://github.com/skhaz/qt-physfs-wrapper |
|
2 * TODO: add copyright header, determine license |
|
3 */ |
|
4 |
|
5 #include "FileEngine.h" |
|
6 #include "hwpacksmounter.h" |
|
7 |
|
8 |
|
9 const QString FileEngineHandler::scheme = "physfs:/"; |
|
10 |
|
11 FileEngine::FileEngine(const QString& filename) |
|
12 : m_handle(NULL) |
|
13 , m_size(0) |
|
14 , m_flags(0) |
|
15 , m_bufferSet(false) |
|
16 , m_readWrite(false) |
|
17 { |
|
18 setFileName(filename); |
|
19 } |
|
20 |
|
21 FileEngine::~FileEngine() |
|
22 { |
|
23 close(); |
|
24 } |
|
25 |
|
26 bool FileEngine::open(QIODevice::OpenMode openMode) |
|
27 { |
|
28 close(); |
|
29 |
|
30 if ((openMode & QIODevice::ReadWrite) == QIODevice::ReadWrite) { |
|
31 m_handle = PHYSFS_openAppend(m_fileName.toUtf8().constData()); |
|
32 if(m_handle) |
|
33 { |
|
34 m_readWrite = true; |
|
35 seek(0); |
|
36 } |
|
37 } |
|
38 |
|
39 else if (openMode & QIODevice::WriteOnly) { |
|
40 m_handle = PHYSFS_openWrite(m_fileName.toUtf8().constData()); |
|
41 m_flags = QAbstractFileEngine::WriteOwnerPerm | QAbstractFileEngine::WriteUserPerm | QAbstractFileEngine::FileType; |
|
42 } |
|
43 |
|
44 else if (openMode & QIODevice::ReadOnly) { |
|
45 m_handle = PHYSFS_openRead(m_fileName.toUtf8().constData()); |
|
46 } |
|
47 |
|
48 else if (openMode & QIODevice::Append) { |
|
49 m_handle = PHYSFS_openAppend(m_fileName.toUtf8().constData()); |
|
50 } |
|
51 |
|
52 else { |
|
53 qWarning("[PHYSFS] Bad file open mode: %d", (int)openMode); |
|
54 } |
|
55 |
|
56 if (!m_handle) { |
|
57 qWarning("%s", QString("[PHYSFS] Failed to open %1, reason: %2").arg(m_fileName).arg(FileEngineHandler::errorStr()).toLocal8Bit().constData()); |
|
58 return false; |
|
59 } |
|
60 |
|
61 return true; |
|
62 } |
|
63 |
|
64 bool FileEngine::close() |
|
65 { |
|
66 if (isOpened()) { |
|
67 int result = PHYSFS_close(m_handle); |
|
68 m_handle = NULL; |
|
69 return result != 0; |
|
70 } |
|
71 |
|
72 return true; |
|
73 } |
|
74 |
|
75 bool FileEngine::flush() |
|
76 { |
|
77 return PHYSFS_flush(m_handle) != 0; |
|
78 } |
|
79 |
|
80 qint64 FileEngine::size() const |
|
81 { |
|
82 return m_size; |
|
83 } |
|
84 |
|
85 qint64 FileEngine::pos() const |
|
86 { |
|
87 return PHYSFS_tell(m_handle); |
|
88 } |
|
89 |
|
90 bool FileEngine::setSize(qint64 size) |
|
91 { |
|
92 if(size == 0) |
|
93 { |
|
94 m_size = 0; |
|
95 return open(QIODevice::WriteOnly); |
|
96 } |
|
97 else |
|
98 return false; |
|
99 } |
|
100 |
|
101 bool FileEngine::seek(qint64 pos) |
|
102 { |
|
103 bool ok = PHYSFS_seek(m_handle, pos) != 0; |
|
104 |
|
105 return ok; |
|
106 } |
|
107 |
|
108 bool FileEngine::isSequential() const |
|
109 { |
|
110 return false; |
|
111 } |
|
112 |
|
113 bool FileEngine::remove() |
|
114 { |
|
115 return PHYSFS_delete(m_fileName.toUtf8().constData()) != 0; |
|
116 } |
|
117 |
|
118 bool FileEngine::mkdir(const QString &dirName, bool createParentDirectories) const |
|
119 { |
|
120 Q_UNUSED(createParentDirectories); |
|
121 |
|
122 return PHYSFS_mkdir(dirName.toUtf8().constData()) != 0; |
|
123 } |
|
124 |
|
125 bool FileEngine::rmdir(const QString &dirName, bool recurseParentDirectories) const |
|
126 { |
|
127 Q_UNUSED(recurseParentDirectories); |
|
128 |
|
129 return PHYSFS_delete(dirName.toUtf8().constData()) != 0; |
|
130 } |
|
131 |
|
132 bool FileEngine::caseSensitive() const |
|
133 { |
|
134 return true; |
|
135 } |
|
136 |
|
137 bool FileEngine::isRelativePath() const |
|
138 { |
|
139 return false; |
|
140 } |
|
141 |
|
142 QAbstractFileEngineIterator * FileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames) |
|
143 { |
|
144 return new FileEngineIterator(filters, filterNames, entryList(filters, filterNames)); |
|
145 } |
|
146 |
|
147 QStringList FileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const |
|
148 { |
|
149 Q_UNUSED(filters); |
|
150 |
|
151 QString file; |
|
152 QStringList result; |
|
153 char **files = PHYSFS_enumerateFiles(m_fileName.toUtf8().constData()); |
|
154 |
|
155 for (char **i = files; *i != NULL; i++) { |
|
156 file = QString::fromUtf8(*i); |
|
157 |
|
158 if (filterNames.isEmpty() || QDir::match(filterNames, file)) { |
|
159 result << file; |
|
160 } |
|
161 } |
|
162 |
|
163 PHYSFS_freeList(files); |
|
164 |
|
165 return result; |
|
166 } |
|
167 |
|
168 QAbstractFileEngine::FileFlags FileEngine::fileFlags(FileFlags type) const |
|
169 { |
|
170 return type & m_flags; |
|
171 } |
|
172 |
|
173 QString FileEngine::fileName(FileName file) const |
|
174 { |
|
175 switch(file) |
|
176 { |
|
177 case QAbstractFileEngine::AbsolutePathName: |
|
178 { |
|
179 QString s(PHYSFS_getWriteDir()); |
|
180 return s; |
|
181 } |
|
182 case QAbstractFileEngine::BaseName: |
|
183 { |
|
184 int l = m_fileName.lastIndexOf('/'); |
|
185 QString s = m_fileName.mid(l + 1); |
|
186 return s; |
|
187 } |
|
188 case QAbstractFileEngine::DefaultName: |
|
189 case QAbstractFileEngine::AbsoluteName: |
|
190 default: |
|
191 { |
|
192 QString s = "physfs:/" + m_fileName; |
|
193 return s; |
|
194 } |
|
195 } |
|
196 } |
|
197 |
|
198 QDateTime FileEngine::fileTime(FileTime time) const |
|
199 { |
|
200 switch (time) |
|
201 { |
|
202 case QAbstractFileEngine::ModificationTime: |
|
203 default: |
|
204 return m_date; |
|
205 break; |
|
206 }; |
|
207 } |
|
208 |
|
209 void FileEngine::setFileName(const QString &file) |
|
210 { |
|
211 if(file.startsWith(FileEngineHandler::scheme)) |
|
212 m_fileName = file.mid(FileEngineHandler::scheme.size()); |
|
213 else |
|
214 m_fileName = file; |
|
215 PHYSFS_Stat stat; |
|
216 if (PHYSFS_stat(m_fileName.toUtf8().constData(), &stat) != 0) { |
|
217 m_size = stat.filesize; |
|
218 m_date = QDateTime::fromTime_t(stat.modtime); |
|
219 // m_flags |= QAbstractFileEngine::WriteOwnerPerm; |
|
220 m_flags |= QAbstractFileEngine::ReadOwnerPerm; |
|
221 m_flags |= QAbstractFileEngine::ReadUserPerm; |
|
222 m_flags |= QAbstractFileEngine::ExistsFlag; |
|
223 m_flags |= QAbstractFileEngine::LocalDiskFlag; |
|
224 |
|
225 switch (stat.filetype) |
|
226 { |
|
227 case PHYSFS_FILETYPE_REGULAR: |
|
228 m_flags |= QAbstractFileEngine::FileType; |
|
229 break; |
|
230 case PHYSFS_FILETYPE_DIRECTORY: |
|
231 m_flags |= QAbstractFileEngine::DirectoryType; |
|
232 break; |
|
233 case PHYSFS_FILETYPE_SYMLINK: |
|
234 m_flags |= QAbstractFileEngine::LinkType; |
|
235 break; |
|
236 default: ; |
|
237 } |
|
238 } |
|
239 } |
|
240 |
|
241 bool FileEngine::atEnd() const |
|
242 { |
|
243 return PHYSFS_eof(m_handle) != 0; |
|
244 } |
|
245 |
|
246 qint64 FileEngine::read(char *data, qint64 maxlen) |
|
247 { |
|
248 if(m_readWrite) |
|
249 { |
|
250 if(pos() == 0) |
|
251 open(QIODevice::ReadOnly); |
|
252 else |
|
253 return -1; |
|
254 } |
|
255 |
|
256 qint64 len = PHYSFS_readBytes(m_handle, data, maxlen); |
|
257 return len; |
|
258 } |
|
259 |
|
260 qint64 FileEngine::readLine(char *data, qint64 maxlen) |
|
261 { |
|
262 if(!m_bufferSet) |
|
263 { |
|
264 PHYSFS_setBuffer(m_handle, 4096); |
|
265 m_bufferSet = true; |
|
266 } |
|
267 |
|
268 qint64 bytesRead = 0; |
|
269 while(PHYSFS_readBytes(m_handle, data, 1) |
|
270 && maxlen |
|
271 && (*data == '\n')) |
|
272 { |
|
273 ++data; |
|
274 --maxlen; |
|
275 ++bytesRead; |
|
276 } |
|
277 |
|
278 return bytesRead; |
|
279 } |
|
280 |
|
281 qint64 FileEngine::write(const char *data, qint64 len) |
|
282 { |
|
283 return PHYSFS_writeBytes(m_handle, data, len); |
|
284 } |
|
285 |
|
286 bool FileEngine::isOpened() const |
|
287 { |
|
288 return m_handle != NULL; |
|
289 } |
|
290 |
|
291 QFile::FileError FileEngine::error() const |
|
292 { |
|
293 return QFile::UnspecifiedError; |
|
294 } |
|
295 |
|
296 QString FileEngine::errorString() const |
|
297 { |
|
298 #if PHYSFS_VER_MAJOR >= 3 |
|
299 return PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()); |
|
300 #else |
|
301 return PHYSFS_getLastError(); |
|
302 #endif |
|
303 } |
|
304 |
|
305 bool FileEngine::supportsExtension(Extension extension) const |
|
306 { |
|
307 return |
|
308 (extension == QAbstractFileEngine::AtEndExtension) |
|
309 || (extension == QAbstractFileEngine::FastReadLineExtension) |
|
310 ; |
|
311 } |
|
312 |
|
313 |
|
314 FileEngineHandler::FileEngineHandler(char *argv0) |
|
315 { |
|
316 if (!PHYSFS_init(argv0)) |
|
317 { |
|
318 qCritical("PHYSFS initialization failed"); |
|
319 } |
|
320 qDebug("%s", QString("[PHYSFS] Init: %1").arg(errorStr()).toLocal8Bit().constData()); |
|
321 } |
|
322 |
|
323 FileEngineHandler::~FileEngineHandler() |
|
324 { |
|
325 PHYSFS_deinit(); |
|
326 } |
|
327 |
|
328 QAbstractFileEngine* FileEngineHandler::create(const QString &filename) const |
|
329 { |
|
330 if (filename.startsWith(scheme)) |
|
331 return new FileEngine(filename); |
|
332 else |
|
333 return NULL; |
|
334 } |
|
335 |
|
336 void FileEngineHandler::mount(const QString &path) |
|
337 { |
|
338 PHYSFS_mount(path.toUtf8().constData(), NULL, 0); |
|
339 qDebug("%s", QString("[PHYSFS] Mounting '%1' to '/': %2").arg(path).arg(errorStr()).toLocal8Bit().constData()); |
|
340 } |
|
341 |
|
342 void FileEngineHandler::mount(const QString & path, const QString & mountPoint) |
|
343 { |
|
344 PHYSFS_mount(path.toUtf8().constData(), mountPoint.toUtf8().constData(), 0); |
|
345 qDebug("%s", QString("[PHYSFS] Mounting '%1' to '%2': %3").arg(path).arg(mountPoint).arg(errorStr()).toLocal8Bit().data()); |
|
346 } |
|
347 |
|
348 void FileEngineHandler::setWriteDir(const QString &path) |
|
349 { |
|
350 PHYSFS_setWriteDir(path.toUtf8().constData()); |
|
351 qDebug("%s", QString("[PHYSFS] Setting write dir to '%1': %2").arg(path).arg(errorStr()).toLocal8Bit().data()); |
|
352 } |
|
353 |
|
354 void FileEngineHandler::mountPacks() |
|
355 { |
|
356 hedgewarsMountPackages(); |
|
357 } |
|
358 |
|
359 QString FileEngineHandler::errorStr() |
|
360 { |
|
361 QString s; |
|
362 #if PHYSFS_VER_MAJOR >= 3 |
|
363 s = QString::fromUtf8(PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); |
|
364 #else |
|
365 s = QString::fromUtf8(PHYSFS_getLastError()); |
|
366 #endif |
|
367 return s.isEmpty() ? "ok" : s; |
|
368 } |
|
369 |
|
370 |
|
371 FileEngineIterator::FileEngineIterator(QDir::Filters filters, const QStringList &nameFilters, const QStringList &entries) |
|
372 : QAbstractFileEngineIterator(filters, nameFilters) |
|
373 { |
|
374 m_entries = entries; |
|
375 |
|
376 /* heck.. docs are unclear on this |
|
377 * QDirIterator puts iterator before first entry |
|
378 * but QAbstractFileEngineIterator example puts iterator on first entry |
|
379 * though QDirIterator approach seems to be the right one |
|
380 */ |
|
381 |
|
382 m_index = -1; |
|
383 } |
|
384 |
|
385 bool FileEngineIterator::hasNext() const |
|
386 { |
|
387 return m_index < m_entries.size() - 1; |
|
388 } |
|
389 |
|
390 QString FileEngineIterator::next() |
|
391 { |
|
392 if (!hasNext()) |
|
393 return QString(); |
|
394 |
|
395 ++m_index; |
|
396 return currentFilePath(); |
|
397 } |
|
398 |
|
399 QString FileEngineIterator::currentFileName() const |
|
400 { |
|
401 return m_entries.at(m_index); |
|
402 } |