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
FileEngine(const QString & filename)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
~FileEngine()21 FileEngine::~FileEngine()
22 {
23 close();
24 }
25
open(QIODevice::OpenMode openMode)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
close()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
flush()75 bool FileEngine::flush()
76 {
77 return PHYSFS_flush(m_handle) != 0;
78 }
79
size() const80 qint64 FileEngine::size() const
81 {
82 return m_size;
83 }
84
pos() const85 qint64 FileEngine::pos() const
86 {
87 return PHYSFS_tell(m_handle);
88 }
89
setSize(qint64 size)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
seek(qint64 pos)101 bool FileEngine::seek(qint64 pos)
102 {
103 bool ok = PHYSFS_seek(m_handle, pos) != 0;
104
105 return ok;
106 }
107
isSequential() const108 bool FileEngine::isSequential() const
109 {
110 return false;
111 }
112
remove()113 bool FileEngine::remove()
114 {
115 return PHYSFS_delete(m_fileName.toUtf8().constData()) != 0;
116 }
117
mkdir(const QString & dirName,bool createParentDirectories) const118 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
rmdir(const QString & dirName,bool recurseParentDirectories) const125 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
caseSensitive() const132 bool FileEngine::caseSensitive() const
133 {
134 return true;
135 }
136
isRelativePath() const137 bool FileEngine::isRelativePath() const
138 {
139 return false;
140 }
141
beginEntryList(QDir::Filters filters,const QStringList & filterNames)142 QAbstractFileEngineIterator * FileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames)
143 {
144 return new FileEngineIterator(filters, filterNames, entryList(filters, filterNames));
145 }
146
entryList(QDir::Filters filters,const QStringList & filterNames) const147 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
fileFlags(FileFlags type) const168 QAbstractFileEngine::FileFlags FileEngine::fileFlags(FileFlags type) const
169 {
170 return type & m_flags;
171 }
172
fileName(FileName file) const173 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
fileTime(FileTime time) const198 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
setFileName(const QString & file)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
atEnd() const241 bool FileEngine::atEnd() const
242 {
243 return PHYSFS_eof(m_handle) != 0;
244 }
245
read(char * data,qint64 maxlen)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
readLine(char * data,qint64 maxlen)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
write(const char * data,qint64 len)281 qint64 FileEngine::write(const char *data, qint64 len)
282 {
283 return PHYSFS_writeBytes(m_handle, data, len);
284 }
285
isOpened() const286 bool FileEngine::isOpened() const
287 {
288 return m_handle != NULL;
289 }
290
error() const291 QFile::FileError FileEngine::error() const
292 {
293 return QFile::UnspecifiedError;
294 }
295
errorString() const296 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
supportsExtension(Extension extension) const305 bool FileEngine::supportsExtension(Extension extension) const
306 {
307 return
308 (extension == QAbstractFileEngine::AtEndExtension)
309 || (extension == QAbstractFileEngine::FastReadLineExtension)
310 ;
311 }
312
313
FileEngineHandler(char * argv0)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
~FileEngineHandler()323 FileEngineHandler::~FileEngineHandler()
324 {
325 PHYSFS_deinit();
326 }
327
create(const QString & filename) const328 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
mount(const QString & path)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
mount(const QString & path,const QString & mountPoint)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
setWriteDir(const QString & path)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
mountPacks()354 void FileEngineHandler::mountPacks()
355 {
356 hedgewarsMountPackages();
357 }
358
errorStr()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
FileEngineIterator(QDir::Filters filters,const QStringList & nameFilters,const QStringList & entries)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
hasNext() const385 bool FileEngineIterator::hasNext() const
386 {
387 return m_index < m_entries.size() - 1;
388 }
389
next()390 QString FileEngineIterator::next()
391 {
392 if (!hasNext())
393 return QString();
394
395 ++m_index;
396 return currentFilePath();
397 }
398
currentFileName() const399 QString FileEngineIterator::currentFileName() const
400 {
401 return m_entries.at(m_index);
402 }
403