~ppsspp/ppsspp/ppsspp_1.3.0

« back to all changes in this revision

Viewing changes to Core/FileSystems/VirtualDiscFileSystem.cpp

  • Committer: Sérgio Benjamim
  • Date: 2017-01-02 00:12:05 UTC
  • Revision ID: sergio_br2@yahoo.com.br-20170102001205-cxbta9za203nmjwm
1.3.0 source (from ppsspp_1.3.0-r160.p5.l1762.a165.t83~56~ubuntu16.04.1.tar.xz).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) 2012- PPSSPP Project.
 
2
 
 
3
// This program is free software: you can redistribute it and/or modify
 
4
// it under the terms of the GNU General Public License as published by
 
5
// the Free Software Foundation, version 2.0 or later versions.
 
6
 
 
7
// This program is distributed in the hope that it will be useful,
 
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
10
// GNU General Public License 2.0 for more details.
 
11
 
 
12
// A copy of the GPL 2.0 should have been included with the program.
 
13
// If not, see http://www.gnu.org/licenses/
 
14
 
 
15
// Official git repository and contact information can be found at
 
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
 
17
 
 
18
#include "Common/FileUtil.h"
 
19
#include "Common/StringUtils.h"
 
20
#include "Common/ChunkFile.h"
 
21
#include "Core/FileSystems/VirtualDiscFileSystem.h"
 
22
#include "Core/FileSystems/ISOFileSystem.h"
 
23
#include "Core/HLE/sceKernel.h"
 
24
#include "Core/Reporting.h"
 
25
#include "file/zip_read.h"
 
26
#include "util/text/utf8.h"
 
27
 
 
28
#ifdef _WIN32
 
29
#include "Common/CommonWindows.h"
 
30
#include <sys/stat.h>
 
31
#else
 
32
#include <dirent.h>
 
33
#include <unistd.h>
 
34
#include <sys/stat.h>
 
35
#include <ctype.h>
 
36
#include <dlfcn.h>
 
37
#endif
 
38
 
 
39
const std::string INDEX_FILENAME = ".ppsspp-index.lst";
 
40
 
 
41
VirtualDiscFileSystem::VirtualDiscFileSystem(IHandleAllocator *_hAlloc, std::string _basePath)
 
42
        : basePath(_basePath),currentBlockIndex(0) {
 
43
 
 
44
#ifdef _WIN32
 
45
                if (!endsWith(basePath, "\\"))
 
46
                        basePath = basePath + "\\";
 
47
#else
 
48
                if (!endsWith(basePath, "/"))
 
49
                        basePath = basePath + "/";
 
50
#endif
 
51
 
 
52
        hAlloc = _hAlloc;
 
53
        LoadFileListIndex();
 
54
}
 
55
 
 
56
VirtualDiscFileSystem::~VirtualDiscFileSystem() {
 
57
        for (auto iter = entries.begin(), end = entries.end(); iter != end; ++iter) {
 
58
                if (iter->second.type != VFILETYPE_ISO) {
 
59
                        iter->second.Close();
 
60
                }
 
61
        }
 
62
        for (auto iter = handlers.begin(), end = handlers.end(); iter != end; ++iter) {
 
63
                delete iter->second;
 
64
        }
 
65
}
 
66
 
 
67
void VirtualDiscFileSystem::LoadFileListIndex() {
 
68
        const std::string filename = basePath + INDEX_FILENAME;
 
69
        if (!File::Exists(filename)) {
 
70
                return;
 
71
        }
 
72
 
 
73
        std::ifstream in;
 
74
        in.open(filename.c_str(), std::ios::in);
 
75
        if (in.fail()) {
 
76
                return;
 
77
        }
 
78
 
 
79
        std::string buf;
 
80
        static const int MAX_LINE_SIZE = 1024;
 
81
        while (!in.eof()) {
 
82
                buf.resize(MAX_LINE_SIZE, '\0');
 
83
                in.getline(&buf[0], MAX_LINE_SIZE);
 
84
 
 
85
                std::string line = buf.data();
 
86
 
 
87
                // Ignore any UTF-8 BOM.
 
88
                if (line.substr(0, 3) == "\xEF\xBB\xBF") {
 
89
                        line = line.substr(3);
 
90
                }
 
91
 
 
92
                if (line.empty() || line[0] == ';') {
 
93
                        continue;
 
94
                }
 
95
 
 
96
                FileListEntry entry = {""};
 
97
 
 
98
                // Syntax: HEXPOS filename or HEXPOS filename:handler
 
99
                size_t filename_pos = line.find(' ');
 
100
                if (filename_pos == line.npos) {
 
101
                        ERROR_LOG(FILESYS, "Unexpected line in %s: %s", INDEX_FILENAME.c_str(), line.c_str());
 
102
                        continue;
 
103
                }
 
104
 
 
105
                filename_pos++;
 
106
                // Strip any slash prefix.
 
107
                while (filename_pos < line.length() && line[filename_pos] == '/') {
 
108
                        filename_pos++;
 
109
                }
 
110
 
 
111
                // Check if there's a handler specified.
 
112
                size_t handler_pos = line.find(':', filename_pos);
 
113
                if (handler_pos != line.npos) {
 
114
                        entry.fileName = line.substr(filename_pos, handler_pos - filename_pos);
 
115
 
 
116
                        std::string handler = line.substr(handler_pos + 1);
 
117
                        size_t trunc = handler.find_last_not_of("\r\n");
 
118
                        if (trunc != handler.npos && trunc != handler.size())
 
119
                                handler.resize(trunc + 1);
 
120
 
 
121
                        if (handlers.find(handler) == handlers.end())
 
122
                                handlers[handler] = new Handler(handler.c_str(), this);
 
123
                        if (handlers[handler]->IsValid())
 
124
                                entry.handler = handlers[handler];
 
125
                } else {
 
126
                        entry.fileName = line.substr(filename_pos);
 
127
                }
 
128
                size_t trunc = entry.fileName.find_last_not_of("\r\n");
 
129
                if (trunc != entry.fileName.npos && trunc != entry.fileName.size())
 
130
                        entry.fileName.resize(trunc + 1);
 
131
 
 
132
                entry.firstBlock = strtol(line.c_str(), NULL, 16);
 
133
                if (entry.handler != NULL && entry.handler->IsValid()) {
 
134
                        HandlerFileHandle temp = entry.handler;
 
135
                        if (temp.Open(basePath, entry.fileName, FILEACCESS_READ)) {
 
136
                                entry.totalSize = (u32)temp.Seek(0, FILEMOVE_END);
 
137
                                temp.Close();
 
138
                        } else {
 
139
                                ERROR_LOG(FILESYS, "Unable to open virtual file: %s", entry.fileName.c_str());
 
140
                        }
 
141
                } else {
 
142
                        entry.totalSize = File::GetFileSize(GetLocalPath(entry.fileName));
 
143
                }
 
144
 
 
145
                // Try to keep currentBlockIndex sane, in case there are other files.
 
146
                u32 nextBlock = entry.firstBlock + (entry.totalSize + 2047) / 2048;
 
147
                if (nextBlock > currentBlockIndex) {
 
148
                        currentBlockIndex = nextBlock;
 
149
                }
 
150
 
 
151
                fileList.push_back(entry);
 
152
        }
 
153
 
 
154
        in.close();
 
155
}
 
156
 
 
157
void VirtualDiscFileSystem::DoState(PointerWrap &p)
 
158
{
 
159
        auto s = p.Section("VirtualDiscFileSystem", 1, 2);
 
160
        if (!s)
 
161
                return;
 
162
 
 
163
        int fileListSize = (int)fileList.size();
 
164
        int entryCount = (int)entries.size();
 
165
 
 
166
        p.Do(fileListSize);
 
167
        p.Do(entryCount);
 
168
        p.Do(currentBlockIndex);
 
169
 
 
170
        FileListEntry dummy = {""};
 
171
        fileList.resize(fileListSize, dummy);
 
172
 
 
173
        for (int i = 0; i < fileListSize; i++)
 
174
        {
 
175
                p.Do(fileList[i].fileName);
 
176
                p.Do(fileList[i].firstBlock);
 
177
                p.Do(fileList[i].totalSize);
 
178
        }
 
179
 
 
180
        if (p.mode == p.MODE_READ)
 
181
        {
 
182
                entries.clear();
 
183
 
 
184
                for (int i = 0; i < entryCount; i++)
 
185
                {
 
186
                        u32 fd = 0;
 
187
                        OpenFileEntry of;
 
188
 
 
189
                        p.Do(fd);
 
190
                        p.Do(of.fileIndex);
 
191
                        p.Do(of.type);
 
192
                        p.Do(of.curOffset);
 
193
                        p.Do(of.startOffset);
 
194
                        p.Do(of.size);
 
195
 
 
196
                        // open file
 
197
                        if (of.type != VFILETYPE_ISO) {
 
198
                                if (fileList[of.fileIndex].handler != NULL) {
 
199
                                        of.handler = fileList[of.fileIndex].handler;
 
200
                                }
 
201
 
 
202
                                bool success = of.Open(basePath, fileList[of.fileIndex].fileName, FILEACCESS_READ);
 
203
                                if (!success) {
 
204
                                        ERROR_LOG(FILESYS, "Failed to create file handle for %s.", fileList[of.fileIndex].fileName.c_str());
 
205
                                } else {
 
206
                                        if (of.type == VFILETYPE_LBN) {
 
207
                                                of.Seek(of.curOffset + of.startOffset, FILEMOVE_BEGIN);
 
208
                                        } else {
 
209
                                                of.Seek(of.curOffset, FILEMOVE_BEGIN);
 
210
                                        }
 
211
                                }
 
212
                        }
 
213
 
 
214
                        entries[fd] = of;
 
215
                }
 
216
        } else {
 
217
                for (EntryMap::iterator it = entries.begin(), end = entries.end(); it != end; ++it)
 
218
                {
 
219
                        OpenFileEntry &of = it->second;
 
220
 
 
221
                        p.Do(it->first);
 
222
                        p.Do(of.fileIndex);
 
223
                        p.Do(of.type);
 
224
                        p.Do(of.curOffset);
 
225
                        p.Do(of.startOffset);
 
226
                        p.Do(of.size);
 
227
                }
 
228
        }
 
229
 
 
230
        if (s >= 2) {
 
231
                p.Do(lastReadBlock_);
 
232
        } else {
 
233
                lastReadBlock_ = 0;
 
234
        }
 
235
 
 
236
        // We don't savestate handlers (loaded on fs load), but if they change, it may not load properly.
 
237
}
 
238
 
 
239
std::string VirtualDiscFileSystem::GetLocalPath(std::string localpath) {
 
240
        if (localpath.empty())
 
241
                return basePath;
 
242
 
 
243
        if (localpath[0] == '/')
 
244
                localpath.erase(0,1);
 
245
        //Convert slashes
 
246
#ifdef _WIN32
 
247
        for (size_t i = 0; i < localpath.size(); i++) {
 
248
                if (localpath[i] == '/')
 
249
                        localpath[i] = '\\';
 
250
        }
 
251
#endif
 
252
        return basePath + localpath;
 
253
}
 
254
 
 
255
int VirtualDiscFileSystem::getFileListIndex(std::string &fileName)
 
256
{
 
257
        std::string normalized;
 
258
        if (fileName.length() >= 1 && fileName[0] == '/') {
 
259
                normalized = fileName.substr(1);
 
260
        } else {
 
261
                normalized = fileName;
 
262
        }
 
263
 
 
264
        for (size_t i = 0; i < fileList.size(); i++)
 
265
        {
 
266
                if (fileList[i].fileName == normalized)
 
267
                        return (int)i;
 
268
        }
 
269
 
 
270
        // unknown file - add it
 
271
        std::string fullName = GetLocalPath(fileName);
 
272
        if (! File::Exists(fullName)) {
 
273
#if HOST_IS_CASE_SENSITIVE
 
274
                if (! FixPathCase(basePath,fileName, FPC_FILE_MUST_EXIST))
 
275
                        return -1;
 
276
                fullName = GetLocalPath(fileName);
 
277
 
 
278
                if (! File::Exists(fullName))
 
279
                        return -1;
 
280
#else
 
281
                return -1;
 
282
#endif
 
283
        }
 
284
 
 
285
        FileType type = File::IsDirectory(fullName) ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;
 
286
        if (type == FILETYPE_DIRECTORY)
 
287
                return -1;
 
288
 
 
289
        FileListEntry entry = {""};
 
290
        entry.fileName = normalized;
 
291
        entry.totalSize = File::GetFileSize(fullName);
 
292
        entry.firstBlock = currentBlockIndex;
 
293
        currentBlockIndex += (entry.totalSize+2047)/2048;
 
294
 
 
295
        fileList.push_back(entry);
 
296
 
 
297
        return (int)fileList.size()-1;
 
298
}
 
299
 
 
300
int VirtualDiscFileSystem::getFileListIndex(u32 accessBlock, u32 accessSize, bool blockMode)
 
301
{
 
302
        for (size_t i = 0; i < fileList.size(); i++)
 
303
        {
 
304
                if (fileList[i].firstBlock <= accessBlock)
 
305
                {
 
306
                        u32 sectorOffset = (accessBlock-fileList[i].firstBlock)*2048;
 
307
                        u32 totalFileSize = blockMode ? (fileList[i].totalSize+2047) & ~2047 : fileList[i].totalSize;
 
308
 
 
309
                        u32 endOffset = sectorOffset+accessSize;
 
310
                        if (endOffset <= totalFileSize)
 
311
                        {
 
312
                                return (int)i;
 
313
                        }
 
314
                }
 
315
        }
 
316
 
 
317
        return -1;
 
318
}
 
319
 
 
320
u32 VirtualDiscFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename)
 
321
{
 
322
        OpenFileEntry entry;
 
323
        entry.curOffset = 0;
 
324
        entry.size = 0;
 
325
        entry.startOffset = 0;
 
326
 
 
327
        if (filename == "")
 
328
        {
 
329
                entry.type = VFILETYPE_ISO;
 
330
                entry.fileIndex = -1;
 
331
 
 
332
                u32 newHandle = hAlloc->GetNewHandle();
 
333
                entries[newHandle] = entry;
 
334
 
 
335
                return newHandle;
 
336
        }
 
337
 
 
338
        if (filename.compare(0,8,"/sce_lbn") == 0)
 
339
        {
 
340
                u32 sectorStart = 0xFFFFFFFF, readSize = 0xFFFFFFFF;
 
341
                parseLBN(filename, &sectorStart, &readSize);
 
342
 
 
343
                entry.type = VFILETYPE_LBN;
 
344
                entry.size = readSize;
 
345
 
 
346
                int fileIndex = getFileListIndex(sectorStart,readSize);
 
347
                if (fileIndex == -1)
 
348
                {
 
349
                        ERROR_LOG(FILESYS, "VirtualDiscFileSystem: sce_lbn used without calling fileinfo.");
 
350
                        return 0;
 
351
                }
 
352
                entry.fileIndex = (u32)fileIndex;
 
353
 
 
354
                entry.startOffset = (sectorStart-fileList[entry.fileIndex].firstBlock)*2048;
 
355
 
 
356
                // now we just need an actual file handle
 
357
                if (fileList[entry.fileIndex].handler != NULL) {
 
358
                        entry.handler = fileList[entry.fileIndex].handler;
 
359
                }
 
360
                bool success = entry.Open(basePath, fileList[entry.fileIndex].fileName, FILEACCESS_READ);
 
361
 
 
362
                if (!success) {
 
363
#ifdef _WIN32
 
364
                        ERROR_LOG(FILESYS, "VirtualDiscFileSystem::OpenFile: FAILED, %i", GetLastError());
 
365
#else
 
366
                        ERROR_LOG(FILESYS, "VirtualDiscFileSystem::OpenFile: FAILED");
 
367
#endif
 
368
                        return 0;
 
369
                }
 
370
 
 
371
                // seek to start
 
372
                entry.Seek(entry.startOffset, FILEMOVE_BEGIN);
 
373
 
 
374
                u32 newHandle = hAlloc->GetNewHandle();
 
375
                entries[newHandle] = entry;
 
376
 
 
377
                return newHandle;
 
378
        }
 
379
 
 
380
        entry.type = VFILETYPE_NORMAL;
 
381
        entry.fileIndex = getFileListIndex(filename);
 
382
 
 
383
        if (entry.fileIndex != (u32)-1 && fileList[entry.fileIndex].handler != NULL) {
 
384
                entry.handler = fileList[entry.fileIndex].handler;
 
385
        }
 
386
        bool success = entry.Open(basePath, filename, access);
 
387
 
 
388
        if (!success) {
 
389
#ifdef _WIN32
 
390
                ERROR_LOG(FILESYS, "VirtualDiscFileSystem::OpenFile: FAILED, %i - access = %i", GetLastError(), (int)access);
 
391
#else
 
392
                ERROR_LOG(FILESYS, "VirtualDiscFileSystem::OpenFile: FAILED, access = %i", (int)access);
 
393
#endif
 
394
                //wwwwaaaaahh!!
 
395
                return 0;
 
396
        } else {
 
397
                u32 newHandle = hAlloc->GetNewHandle();
 
398
                entries[newHandle] = entry;
 
399
 
 
400
                return newHandle;
 
401
        }
 
402
}
 
403
 
 
404
size_t VirtualDiscFileSystem::SeekFile(u32 handle, s32 position, FileMove type) {
 
405
        EntryMap::iterator iter = entries.find(handle);
 
406
        if (iter != entries.end()) {
 
407
                auto &entry = iter->second;
 
408
                switch (entry.type)
 
409
                {
 
410
                case VFILETYPE_NORMAL:
 
411
                        {
 
412
                                return entry.Seek(position, type);
 
413
                        }
 
414
                case VFILETYPE_LBN:
 
415
                        {
 
416
                                switch (type)
 
417
                                {
 
418
                                case FILEMOVE_BEGIN:    entry.curOffset = position;                     break;
 
419
                                case FILEMOVE_CURRENT:  entry.curOffset += position;                    break;
 
420
                                case FILEMOVE_END:      entry.curOffset = entry.size + position;        break;
 
421
                                }
 
422
 
 
423
                                u32 off = entry.startOffset + entry.curOffset;
 
424
                                entry.Seek(off, FILEMOVE_BEGIN);
 
425
                                return entry.curOffset;
 
426
                        }
 
427
                case VFILETYPE_ISO:
 
428
                        {
 
429
                                switch (type)
 
430
                                {
 
431
                                case FILEMOVE_BEGIN:    entry.curOffset = position;                     break;
 
432
                                case FILEMOVE_CURRENT:  entry.curOffset += position;                    break;
 
433
                                case FILEMOVE_END:      entry.curOffset = currentBlockIndex + position; break;
 
434
                                }
 
435
 
 
436
                                return entry.curOffset;
 
437
                        }
 
438
                }
 
439
                return 0;
 
440
        } else {
 
441
                //This shouldn't happen...
 
442
                ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Cannot seek in file that hasn't been opened: %08x", handle);
 
443
                return 0;
 
444
        }
 
445
}
 
446
 
 
447
size_t VirtualDiscFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) {
 
448
        int ignored;
 
449
        return ReadFile(handle, pointer, size, ignored);
 
450
}
 
451
 
 
452
size_t VirtualDiscFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) {
 
453
        EntryMap::iterator iter = entries.find(handle);
 
454
        if (iter != entries.end()) {
 
455
                if (size < 0) {
 
456
                        ERROR_LOG_REPORT(FILESYS, "Invalid read for %lld bytes from virtual umd", size);
 
457
                        return 0;
 
458
                }
 
459
 
 
460
                // it's the whole iso... it could reference any of the files on the disc.
 
461
                // For now let's just open and close the files on demand. Can certainly be done
 
462
                // better though
 
463
                if (iter->second.type == VFILETYPE_ISO)
 
464
                {
 
465
                        int fileIndex = getFileListIndex(iter->second.curOffset,size*2048,true);
 
466
                        if (fileIndex == -1)
 
467
                        {
 
468
                                ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Reading from unknown address in %08x at %08llx", handle, iter->second.curOffset);
 
469
                                return 0;
 
470
                        }
 
471
 
 
472
                        OpenFileEntry temp;
 
473
                        if (fileList[fileIndex].handler != NULL) {
 
474
                                temp.handler = fileList[fileIndex].handler;
 
475
                        }
 
476
                        bool success = temp.Open(basePath, fileList[fileIndex].fileName, FILEACCESS_READ);
 
477
 
 
478
                        if (!success)
 
479
                        {
 
480
                                ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Error opening file %s", fileList[fileIndex].fileName.c_str());
 
481
                                return 0;
 
482
                        }
 
483
 
 
484
                        u32 startOffset = (iter->second.curOffset-fileList[fileIndex].firstBlock)*2048;
 
485
                        size_t bytesRead;
 
486
 
 
487
                        temp.Seek(startOffset, FILEMOVE_BEGIN);
 
488
 
 
489
                        u32 remainingSize = fileList[fileIndex].totalSize-startOffset;
 
490
                        if (remainingSize < size * 2048)
 
491
                        {
 
492
                                // the file doesn't fill the whole last sector
 
493
                                // read what's there and zero fill the rest like on a real disc
 
494
                                bytesRead = temp.Read(pointer, remainingSize);
 
495
                                memset(&pointer[bytesRead], 0, size * 2048 - bytesRead);
 
496
                        } else {
 
497
                                bytesRead = temp.Read(pointer, size * 2048);
 
498
                        }
 
499
 
 
500
                        temp.Close();
 
501
 
 
502
                        iter->second.curOffset += size;
 
503
                        // TODO: This probably isn't enough...
 
504
                        if (abs((int)lastReadBlock_ - (int)iter->second.curOffset) > 100) {
 
505
                                // This is an estimate, sometimes it takes 1+ seconds, but it definitely takes time.
 
506
                                usec = 100000;
 
507
                        }
 
508
                        lastReadBlock_ = iter->second.curOffset;
 
509
                        return size;
 
510
                }
 
511
 
 
512
                if (iter->second.type == VFILETYPE_LBN && iter->second.curOffset + size > iter->second.size) {
 
513
                        // Clamp to the remaining size, but read what we can.
 
514
                        const s64 newSize = iter->second.size - iter->second.curOffset;
 
515
                        WARN_LOG(FILESYS, "VirtualDiscFileSystem: Reading beyond end of file, clamping size %lld to %lld", size, newSize);
 
516
                        size = newSize;
 
517
                }
 
518
 
 
519
                size_t bytesRead = iter->second.Read(pointer, size);
 
520
                iter->second.curOffset += bytesRead;
 
521
                return bytesRead;
 
522
        } else {
 
523
                //This shouldn't happen...
 
524
                ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Cannot read file that hasn't been opened: %08x", handle);
 
525
                return 0;
 
526
        }
 
527
}
 
528
 
 
529
void VirtualDiscFileSystem::CloseFile(u32 handle) {
 
530
        EntryMap::iterator iter = entries.find(handle);
 
531
        if (iter != entries.end()) {
 
532
                hAlloc->FreeHandle(handle);
 
533
                iter->second.Close();
 
534
                entries.erase(iter);
 
535
        } else {
 
536
                //This shouldn't happen...
 
537
                ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Cannot close file that hasn't been opened: %08x", handle);
 
538
        }
 
539
}
 
540
 
 
541
bool VirtualDiscFileSystem::OwnsHandle(u32 handle) {
 
542
        EntryMap::iterator iter = entries.find(handle);
 
543
        return (iter != entries.end());
 
544
}
 
545
 
 
546
int VirtualDiscFileSystem::Ioctl(u32 handle, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen, int &usec) {
 
547
        // TODO: How to support these?
 
548
        return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED;
 
549
}
 
550
 
 
551
int VirtualDiscFileSystem::DevType(u32 handle) {
 
552
        EntryMap::iterator iter = entries.find(handle);
 
553
        return iter->second.type == VFILETYPE_ISO ? PSP_DEV_TYPE_BLOCK : PSP_DEV_TYPE_FILE;
 
554
}
 
555
 
 
556
PSPFileInfo VirtualDiscFileSystem::GetFileInfo(std::string filename) {
 
557
        PSPFileInfo x;
 
558
        x.name = filename;
 
559
        x.access = FILEACCESS_READ;
 
560
 
 
561
        if (filename.compare(0,8,"/sce_lbn") == 0) {
 
562
                u32 sectorStart = 0xFFFFFFFF, readSize = 0xFFFFFFFF;
 
563
                parseLBN(filename, &sectorStart, &readSize);
 
564
 
 
565
                PSPFileInfo fileInfo;
 
566
                fileInfo.name = filename;
 
567
                fileInfo.exists = true;
 
568
                fileInfo.size = readSize;
 
569
                fileInfo.startSector = sectorStart;
 
570
                fileInfo.isOnSectorSystem = true;
 
571
                fileInfo.numSectors = (readSize + 2047) / 2048;
 
572
                return fileInfo;
 
573
        }
 
574
 
 
575
        int fileIndex = getFileListIndex(filename);
 
576
        if (fileIndex != -1 && fileList[fileIndex].handler != NULL) {
 
577
                x.type = FILETYPE_NORMAL;
 
578
                x.isOnSectorSystem = true;
 
579
                x.startSector = fileList[fileIndex].firstBlock;
 
580
 
 
581
                HandlerFileHandle temp = fileList[fileIndex].handler;
 
582
                if (temp.Open(basePath, filename, FILEACCESS_READ)) {
 
583
                        x.exists = true;
 
584
                        x.size = temp.Seek(0, FILEMOVE_END);
 
585
                        temp.Close();
 
586
                }
 
587
 
 
588
                // TODO: Probably should include dates or something...
 
589
                return x;
 
590
        }
 
591
 
 
592
        std::string fullName = GetLocalPath(filename);
 
593
        if (! File::Exists(fullName)) {
 
594
#if HOST_IS_CASE_SENSITIVE
 
595
                if (! FixPathCase(basePath,filename, FPC_FILE_MUST_EXIST))
 
596
                        return x;
 
597
                fullName = GetLocalPath(filename);
 
598
 
 
599
                if (! File::Exists(fullName))
 
600
                        return x;
 
601
#else
 
602
                return x;
 
603
#endif
 
604
        }
 
605
 
 
606
        x.type = File::IsDirectory(fullName) ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;
 
607
        x.exists = true;
 
608
        if (fileIndex != -1) {
 
609
                x.isOnSectorSystem = true;
 
610
                x.startSector = fileList[fileIndex].firstBlock;
 
611
        }
 
612
 
 
613
        if (x.type != FILETYPE_DIRECTORY) {
 
614
                File::FileDetails details;
 
615
                if (!File::GetFileDetails(fullName, &details)) {
 
616
                        ERROR_LOG(FILESYS, "DirectoryFileSystem::GetFileInfo: GetFileDetails failed: %s", fullName.c_str());
 
617
                        x.size = 0;
 
618
                        x.access = 0;
 
619
                        memset(&x.atime, 0, sizeof(x.atime));
 
620
                        memset(&x.ctime, 0, sizeof(x.ctime));
 
621
                        memset(&x.mtime, 0, sizeof(x.mtime));
 
622
                } else {
 
623
                        x.size = details.size;
 
624
                        x.access = details.access;
 
625
                        time_t atime = details.atime;
 
626
                        time_t ctime = details.ctime;
 
627
                        time_t mtime = details.mtime;
 
628
 
 
629
                        localtime_r((time_t*)&atime, &x.atime);
 
630
                        localtime_r((time_t*)&ctime, &x.ctime);
 
631
                        localtime_r((time_t*)&mtime, &x.mtime);
 
632
                }
 
633
 
 
634
                x.startSector = fileList[fileIndex].firstBlock;
 
635
                x.numSectors = (x.size+2047)/2048;
 
636
        }
 
637
 
 
638
        return x;
 
639
}
 
640
 
 
641
bool VirtualDiscFileSystem::GetHostPath(const std::string &inpath, std::string &outpath)
 
642
{
 
643
        ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Retrieving host path");
 
644
        return false;
 
645
}
 
646
 
 
647
#ifdef _WIN32
 
648
#define FILETIME_FROM_UNIX_EPOCH_US 11644473600000000ULL
 
649
 
 
650
static void tmFromFiletime(tm &dest, FILETIME &src)
 
651
{
 
652
        u64 from_1601_us = (((u64) src.dwHighDateTime << 32ULL) + (u64) src.dwLowDateTime) / 10ULL;
 
653
        u64 from_1970_us = from_1601_us - FILETIME_FROM_UNIX_EPOCH_US;
 
654
 
 
655
        time_t t = (time_t) (from_1970_us / 1000000UL);
 
656
        localtime_r(&t, &dest);
 
657
}
 
658
#endif
 
659
 
 
660
std::vector<PSPFileInfo> VirtualDiscFileSystem::GetDirListing(std::string path)
 
661
{
 
662
        std::vector<PSPFileInfo> myVector;
 
663
#ifdef _WIN32
 
664
        WIN32_FIND_DATA findData;
 
665
        HANDLE hFind;
 
666
 
 
667
        // TODO: Handler files that are virtual might not be listed.
 
668
 
 
669
        std::string w32path = GetLocalPath(path) + "\\*.*";
 
670
 
 
671
        hFind = FindFirstFile(ConvertUTF8ToWString(w32path).c_str(), &findData);
 
672
 
 
673
        if (hFind == INVALID_HANDLE_VALUE) {
 
674
                return myVector; //the empty list
 
675
        }
 
676
 
 
677
        for (BOOL retval = 1; retval; retval = FindNextFile(hFind, &findData)) {
 
678
                if (!wcscmp(findData.cFileName, L"..") || !wcscmp(findData.cFileName, L".")) {
 
679
                        continue;
 
680
                }
 
681
 
 
682
                PSPFileInfo entry;
 
683
                if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
 
684
                        entry.type = FILETYPE_DIRECTORY;
 
685
                } else {
 
686
                        entry.type = FILETYPE_NORMAL;
 
687
                }
 
688
 
 
689
                entry.access = FILEACCESS_READ;
 
690
                entry.size = findData.nFileSizeLow | ((u64)findData.nFileSizeHigh<<32);
 
691
                entry.name = ConvertWStringToUTF8(findData.cFileName);
 
692
                tmFromFiletime(entry.atime, findData.ftLastAccessTime);
 
693
                tmFromFiletime(entry.ctime, findData.ftCreationTime);
 
694
                tmFromFiletime(entry.mtime, findData.ftLastWriteTime);
 
695
                entry.isOnSectorSystem = true;
 
696
 
 
697
                std::string fullRelativePath = path + "/" + entry.name;
 
698
                int fileIndex = getFileListIndex(fullRelativePath);
 
699
                if (fileIndex != -1)
 
700
                        entry.startSector = fileList[fileIndex].firstBlock;
 
701
                myVector.push_back(entry);
 
702
        }
 
703
        FindClose(hFind);
 
704
#else
 
705
        dirent *dirp;
 
706
        std::string localPath = GetLocalPath(path);
 
707
        DIR *dp = opendir(localPath.c_str());
 
708
 
 
709
#if HOST_IS_CASE_SENSITIVE
 
710
        if(dp == NULL && FixPathCase(basePath,path, FPC_FILE_MUST_EXIST)) {
 
711
                // May have failed due to case sensitivity, try again
 
712
                localPath = GetLocalPath(path);
 
713
                dp = opendir(localPath.c_str());
 
714
        }
 
715
#endif
 
716
 
 
717
        if (dp == NULL) {
 
718
                ERROR_LOG(FILESYS,"Error opening directory %s\n", path.c_str());
 
719
                return myVector;
 
720
        }
 
721
 
 
722
        while ((dirp = readdir(dp)) != NULL) {
 
723
                if (!strcmp(dirp->d_name, "..") || !strcmp(dirp->d_name, ".")) {
 
724
                        continue;
 
725
                }
 
726
 
 
727
                PSPFileInfo entry;
 
728
                struct stat s;
 
729
                std::string fullName = GetLocalPath(path) + "/"+dirp->d_name;
 
730
                stat(fullName.c_str(), &s);
 
731
                if (S_ISDIR(s.st_mode))
 
732
                        entry.type = FILETYPE_DIRECTORY;
 
733
                else
 
734
                        entry.type = FILETYPE_NORMAL;
 
735
                entry.access = s.st_mode & 0x1FF;
 
736
                entry.name = dirp->d_name;
 
737
                entry.size = s.st_size;
 
738
                localtime_r((time_t*)&s.st_atime,&entry.atime);
 
739
                localtime_r((time_t*)&s.st_ctime,&entry.ctime);
 
740
                localtime_r((time_t*)&s.st_mtime,&entry.mtime);
 
741
                entry.isOnSectorSystem = true;
 
742
 
 
743
                std::string fullRelativePath = path + "/" + entry.name;
 
744
                int fileIndex = getFileListIndex(fullRelativePath);
 
745
                if (fileIndex != -1)
 
746
                        entry.startSector = fileList[fileIndex].firstBlock;
 
747
                myVector.push_back(entry);
 
748
        }
 
749
        closedir(dp);
 
750
#endif
 
751
        return myVector;
 
752
}
 
753
 
 
754
size_t VirtualDiscFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size)
 
755
{
 
756
        ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Cannot write to file on virtual disc");
 
757
        return 0;
 
758
}
 
759
 
 
760
size_t VirtualDiscFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec)
 
761
{
 
762
        ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Cannot write to file on virtual disc");
 
763
        return 0;
 
764
}
 
765
 
 
766
bool VirtualDiscFileSystem::MkDir(const std::string &dirname)
 
767
{
 
768
        ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Cannot create directory on virtual disc");
 
769
        return false;
 
770
}
 
771
 
 
772
bool VirtualDiscFileSystem::RmDir(const std::string &dirname)
 
773
{
 
774
        ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Cannot remove directory on virtual disc");
 
775
        return false;
 
776
}
 
777
 
 
778
int VirtualDiscFileSystem::RenameFile(const std::string &from, const std::string &to)
 
779
{
 
780
        ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Cannot rename file on virtual disc");
 
781
        return -1;
 
782
}
 
783
 
 
784
bool VirtualDiscFileSystem::RemoveFile(const std::string &filename)
 
785
{
 
786
        ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Cannot remove file on virtual disc");
 
787
        return false;
 
788
}
 
789
 
 
790
void VirtualDiscFileSystem::HandlerLogger(void *arg, HandlerHandle handle, LogTypes::LOG_LEVELS level, const char *msg) {
 
791
        VirtualDiscFileSystem *sys = static_cast<VirtualDiscFileSystem *>(arg);
 
792
 
 
793
        // TODO: Probably could do this smarter / use a lookup.
 
794
        const char *filename = NULL;
 
795
        for (auto it = sys->entries.begin(), end = sys->entries.end(); it != end; ++it) {
 
796
                if (it->second.fileIndex != (u32)-1 && it->second.handler.handle == handle) {
 
797
                        filename = sys->fileList[it->second.fileIndex].fileName.c_str();
 
798
                        break;
 
799
                }
 
800
        }
 
801
 
 
802
        if (filename != NULL) {
 
803
                GENERIC_LOG(LogTypes::FILESYS, level, "%s: %s", filename, msg);
 
804
        } else {
 
805
                GENERIC_LOG(LogTypes::FILESYS, level, "%s", msg);
 
806
        }
 
807
}
 
808
 
 
809
VirtualDiscFileSystem::Handler::Handler(const char *filename, VirtualDiscFileSystem *const sys) {
 
810
#ifdef _WIN32
 
811
#define dlopen(name, ignore) (void *)LoadLibrary(ConvertUTF8ToWString(name).c_str())
 
812
#define dlsym(mod, name) GetProcAddress((HMODULE)mod, name)
 
813
#define dlclose(mod) FreeLibrary((HMODULE)mod)
 
814
#endif
 
815
 
 
816
        library = dlopen(filename, RTLD_LOCAL | RTLD_NOW);
 
817
        if (library != NULL) {
 
818
                Init = (InitFunc)dlsym(library, "Init");
 
819
                Shutdown = (ShutdownFunc)dlsym(library, "Shutdown");
 
820
                Open = (OpenFunc)dlsym(library, "Open");
 
821
                Seek = (SeekFunc)dlsym(library, "Seek");
 
822
                Read = (ReadFunc)dlsym(library, "Read");
 
823
                Close = (CloseFunc)dlsym(library, "Close");
 
824
 
 
825
                if (Init == NULL || Shutdown == NULL || Open == NULL || Seek == NULL || Read == NULL || Close == NULL) {
 
826
                        ERROR_LOG(FILESYS, "Unable to find all handler functions: %s", filename);
 
827
                        dlclose(library);
 
828
                        library = NULL;
 
829
                } else if (!Init(&HandlerLogger, sys)) {
 
830
                        ERROR_LOG(FILESYS, "Unable to initialize handler: %s", filename);
 
831
                        dlclose(library);
 
832
                        library = NULL;
 
833
                }
 
834
        } else {
 
835
                ERROR_LOG(FILESYS, "Unable to load handler: %s", filename);
 
836
        }
 
837
#ifdef _WIN32
 
838
#undef dlopen
 
839
#undef dlsym
 
840
#undef dlclose
 
841
#endif
 
842
}
 
843
 
 
844
VirtualDiscFileSystem::Handler::~Handler() {
 
845
        if (library != NULL) {
 
846
                Shutdown();
 
847
 
 
848
#ifdef _WIN32
 
849
                FreeLibrary((HMODULE)library);
 
850
#else
 
851
                dlclose(library);
 
852
#endif
 
853
        }
 
854
}