2019-12-01 10:19:47 -08:00
|
|
|
/* ScummVM - Graphic Adventure Engine
|
|
|
|
*
|
|
|
|
* ScummVM is the legal property of its developers, whose names
|
|
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
|
|
* file distributed with this source distribution.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2019-12-11 22:38:03 -08:00
|
|
|
#include "ultima/ultima8/misc/pent_include.h"
|
|
|
|
#include "ultima/ultima8/filesys/file_system.h"
|
2019-12-16 22:00:28 -08:00
|
|
|
#include "ultima/shared/std/string.h"
|
2019-12-11 22:38:03 -08:00
|
|
|
#include "ultima/ultima8/ultima8.h"
|
2019-12-12 19:52:23 -08:00
|
|
|
#include "common/system.h"
|
2019-12-12 22:04:03 -08:00
|
|
|
#include "common/memstream.h"
|
2019-12-12 19:52:23 -08:00
|
|
|
#include "common/savefile.h"
|
2019-12-01 10:19:47 -08:00
|
|
|
|
2019-12-16 21:32:17 -08:00
|
|
|
namespace Ultima {
|
2019-12-01 10:19:47 -08:00
|
|
|
namespace Ultima8 {
|
|
|
|
|
2020-01-28 18:27:04 -08:00
|
|
|
using Std::string;
|
2019-12-01 10:19:47 -08:00
|
|
|
|
2020-03-14 05:07:43 +09:00
|
|
|
FileSystem *FileSystem::_fileSystem = nullptr;
|
2019-12-01 10:19:47 -08:00
|
|
|
|
|
|
|
FileSystem::FileSystem(bool noforced)
|
2020-02-15 18:37:57 -08:00
|
|
|
: _noForcedVPaths(noforced), _allowDataOverride(true) {
|
2020-02-22 19:51:44 -08:00
|
|
|
debugN(MM_INFO, "Creating FileSystem...\n");
|
2019-12-01 10:19:47 -08:00
|
|
|
|
2020-02-15 18:37:57 -08:00
|
|
|
_fileSystem = this;
|
2019-12-05 22:59:28 -08:00
|
|
|
AddVirtualPath("@home", "");
|
2019-12-01 10:19:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
FileSystem::~FileSystem() {
|
2020-02-22 19:51:44 -08:00
|
|
|
debugN(MM_INFO, "Destroying FileSystem...\n");
|
2019-12-01 10:19:47 -08:00
|
|
|
|
2020-03-14 05:07:43 +09:00
|
|
|
_fileSystem = nullptr;
|
2019-12-01 10:19:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Open a streaming file as readable. Streamed (0 on failure)
|
|
|
|
IDataSource *FileSystem::ReadFile(const string &vfn, bool is_text) {
|
|
|
|
IDataSource *data = checkBuiltinData(vfn, is_text);
|
|
|
|
|
|
|
|
// allow data-override?
|
2020-02-15 18:37:57 -08:00
|
|
|
if (!_allowDataOverride && data)
|
2019-12-05 22:59:28 -08:00
|
|
|
return data;
|
2019-12-01 10:19:47 -08:00
|
|
|
|
2020-05-22 21:30:44 +09:00
|
|
|
if (data)
|
|
|
|
delete data;
|
|
|
|
|
2019-12-12 19:52:23 -08:00
|
|
|
Common::SeekableReadStream *readStream;
|
2020-04-01 18:42:16 +09:00
|
|
|
if (!rawOpen(readStream, vfn))
|
2020-03-14 05:07:43 +09:00
|
|
|
return nullptr;
|
2019-12-01 10:19:47 -08:00
|
|
|
|
2019-12-12 19:52:23 -08:00
|
|
|
return new IFileDataSource(readStream);
|
2019-12-01 10:19:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Open a streaming file as writeable. Streamed (0 on failure)
|
2020-04-14 18:21:43 +09:00
|
|
|
Common::WriteStream *FileSystem::WriteFile(const string &vfn, bool is_text) {
|
2019-12-01 10:19:47 -08:00
|
|
|
string filename = vfn;
|
2019-12-12 19:52:23 -08:00
|
|
|
Common::WriteStream *writeStream;
|
2019-12-01 10:19:47 -08:00
|
|
|
|
2019-12-12 19:52:23 -08:00
|
|
|
if (!rawOpen(writeStream, filename))
|
2020-03-14 05:07:43 +09:00
|
|
|
return nullptr;
|
2019-12-01 10:19:47 -08:00
|
|
|
|
2020-04-14 18:21:43 +09:00
|
|
|
return writeStream;
|
2019-12-01 10:19:47 -08:00
|
|
|
}
|
|
|
|
|
2019-12-12 19:52:23 -08:00
|
|
|
bool FileSystem::rawOpen(Common::SeekableReadStream *&in, const string &fname) {
|
2019-12-05 22:59:28 -08:00
|
|
|
string name = fname;
|
2019-12-12 19:52:23 -08:00
|
|
|
Common::File *f;
|
2019-12-05 22:59:28 -08:00
|
|
|
|
2019-12-12 19:52:23 -08:00
|
|
|
// Handle reading files from the ultima.dat data
|
2019-12-05 22:59:28 -08:00
|
|
|
if (name.hasPrefix("@data/")) {
|
2019-12-12 19:52:23 -08:00
|
|
|
// It's a file specifically from the ultima.dat file
|
|
|
|
f = new Common::File();
|
2020-03-14 17:25:12 -07:00
|
|
|
if (f->open(Common::String::format("data/%s", name.substr(6).c_str()))) {
|
2019-12-12 19:52:23 -08:00
|
|
|
in = f;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
f->close();
|
|
|
|
delete f;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle opening savegames
|
|
|
|
if (name.hasPrefix("@save/")) {
|
2020-01-28 18:27:04 -08:00
|
|
|
int slotNumber = Std::atoi(name.c_str() + 6);
|
2020-02-09 17:10:39 -08:00
|
|
|
Std::string saveFilename = Ultima8Engine::get_instance()->getSaveStateName(slotNumber);
|
2019-12-12 19:52:23 -08:00
|
|
|
|
|
|
|
in = g_system->getSavefileManager()->openForLoading(saveFilename);
|
2020-03-14 05:07:43 +09:00
|
|
|
return in != nullptr;
|
2019-12-05 22:59:28 -08:00
|
|
|
}
|
2019-12-12 19:52:23 -08:00
|
|
|
|
2019-12-05 22:59:28 -08:00
|
|
|
if (!rewrite_virtual_path(name))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
switch_slashes(name);
|
|
|
|
|
|
|
|
int uppercasecount = 0;
|
2019-12-12 19:52:23 -08:00
|
|
|
f = new Common::File();
|
2019-12-05 22:59:28 -08:00
|
|
|
do {
|
2019-12-12 19:52:23 -08:00
|
|
|
if (f->open(name)) {
|
|
|
|
in = f;
|
2019-12-05 22:59:28 -08:00
|
|
|
return true;
|
2019-12-12 19:52:23 -08:00
|
|
|
}
|
2019-12-05 22:59:28 -08:00
|
|
|
} while (base_to_uppercase(name, ++uppercasecount));
|
|
|
|
|
2019-12-12 19:52:23 -08:00
|
|
|
// file not found
|
|
|
|
delete f;
|
2019-12-05 22:59:28 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-12 19:52:23 -08:00
|
|
|
bool FileSystem::rawOpen(Common::WriteStream *&out, const string &fname) {
|
2019-12-05 22:59:28 -08:00
|
|
|
string name = fname;
|
2019-12-12 19:52:23 -08:00
|
|
|
switch_slashes(name);
|
|
|
|
|
|
|
|
if (name.hasPrefix("@save/")) {
|
2020-01-28 18:27:04 -08:00
|
|
|
int slotNumber = Std::atoi(name.c_str() + 6);
|
2020-02-09 17:10:39 -08:00
|
|
|
Std::string saveFilename = Ultima8Engine::get_instance()->getSaveStateName(slotNumber);
|
2019-12-12 19:52:23 -08:00
|
|
|
|
|
|
|
out = g_system->getSavefileManager()->openForSaving(saveFilename, false);
|
2020-03-14 05:07:43 +09:00
|
|
|
return out != nullptr;
|
2019-12-12 19:52:23 -08:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
2019-12-05 22:59:28 -08:00
|
|
|
if (!rewrite_virtual_path(name)) {
|
2020-02-22 19:51:44 -08:00
|
|
|
warning("Illegal file access");
|
2019-12-05 22:59:28 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int uppercasecount = 0;
|
|
|
|
do {
|
|
|
|
if (out.open(name))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} while (base_to_uppercase(name, ++uppercasecount));
|
|
|
|
|
|
|
|
// file not found
|
|
|
|
return false;
|
2019-12-12 19:52:23 -08:00
|
|
|
#endif
|
2019-12-05 22:59:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-01 10:19:47 -08:00
|
|
|
void FileSystem::switch_slashes(string &name) {
|
|
|
|
for (string::iterator X = name.begin(); X != name.end(); ++X) {
|
2019-12-07 17:33:40 -08:00
|
|
|
if (*X == '\\')
|
|
|
|
*X = '/';
|
2019-12-01 10:19:47 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert just the last 'count' parts of a filename to uppercase.
|
|
|
|
* returns false if there are less than 'count' parts
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool FileSystem::base_to_uppercase(string &str, int count) {
|
|
|
|
if (count <= 0) return true;
|
|
|
|
|
|
|
|
int todo = count;
|
|
|
|
// Go backwards.
|
2019-12-05 20:54:58 -08:00
|
|
|
string::reverse_iterator X;
|
2019-12-05 22:59:28 -08:00
|
|
|
for (X = str.rbegin(); X != str.rend(); ++X) {
|
2019-12-01 10:19:47 -08:00
|
|
|
// Stop at separator.
|
|
|
|
if (*X == '/' || *X == '\\' || *X == ':')
|
|
|
|
todo--;
|
|
|
|
if (todo <= 0)
|
|
|
|
break;
|
|
|
|
|
2020-02-01 17:48:37 -08:00
|
|
|
*X = static_cast<char>(Std::toUpper(*X));
|
2019-12-01 10:19:47 -08:00
|
|
|
}
|
2019-12-05 20:54:58 -08:00
|
|
|
if (X == str.rend())
|
2019-12-01 10:19:47 -08:00
|
|
|
todo--; // start of pathname counts as separator too
|
|
|
|
|
|
|
|
// false if it didn't reach 'count' parts
|
|
|
|
return (todo <= 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FileSystem::AddVirtualPath(const string &vpath, const string &realpath, const bool create) {
|
|
|
|
string vp = vpath, rp = realpath;
|
|
|
|
|
|
|
|
// remove trailing slash
|
|
|
|
if (vp.rfind('/') == vp.size() - 1)
|
|
|
|
vp.erase(vp.rfind('/'));
|
|
|
|
|
|
|
|
if (rp.rfind('/') == rp.size() - 1)
|
|
|
|
rp.erase(rp.rfind('/'));
|
|
|
|
|
|
|
|
if (rp.find("..") != string::npos) {
|
2020-02-22 19:51:44 -08:00
|
|
|
warning("Error mounting virtual path \"%s\": \"..\" not allowed", vp.c_str());
|
2019-12-01 10:19:47 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finding Reserved Virtual Path Names
|
|
|
|
// memory path is reserved
|
|
|
|
if (vp == "@memory" || vp.substr(0, 8) == "@memory/") {
|
2020-02-23 10:59:17 -08:00
|
|
|
warning("Error mounting virtual path \"%s\": \"@memory\" is a reserved virtual path name",
|
|
|
|
vp.c_str());
|
2019-12-01 10:19:47 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
string fullpath = rp;
|
|
|
|
rewrite_virtual_path(fullpath);
|
|
|
|
// When mounting a memory file, it wont exist, so don't attempt to create the dir
|
|
|
|
#ifdef DEBUG
|
2020-02-22 19:51:44 -08:00
|
|
|
debugN(MM_INFO, "virtual path \"%s\": %s\n", vp.c_str(), fullpath.c_str());
|
2019-12-01 10:19:47 -08:00
|
|
|
#endif
|
2020-10-20 21:34:50 +09:00
|
|
|
if (!(fullpath.substr(0, 8) == "@memory/") && rp.length()) {
|
2019-12-01 10:19:47 -08:00
|
|
|
if (!IsDir(fullpath)) {
|
|
|
|
if (!create) {
|
|
|
|
#ifdef DEBUG
|
2020-02-22 19:51:44 -08:00
|
|
|
warning("Problem mounting virtual path \"%s\": directory not found: %s",
|
|
|
|
vp.c_str(), fullpath.c_str());
|
2019-12-01 10:19:47 -08:00
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
MkDir(fullpath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-15 18:37:57 -08:00
|
|
|
_virtualPaths[vp] = rp;
|
2019-12-01 10:19:47 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FileSystem::RemoveVirtualPath(const string &vpath) {
|
|
|
|
string vp = vpath;
|
|
|
|
|
|
|
|
// remove trailing slash
|
|
|
|
if (vp.rfind('/') == vp.size() - 1)
|
|
|
|
vp.erase(vp.rfind('/'));
|
|
|
|
|
2020-02-15 18:37:57 -08:00
|
|
|
Std::map<Common::String, string>::iterator i = _virtualPaths.find(vp);
|
2019-12-01 10:19:47 -08:00
|
|
|
|
2020-02-15 18:37:57 -08:00
|
|
|
if (i == _virtualPaths.end()) {
|
2019-12-01 10:19:47 -08:00
|
|
|
return false;
|
|
|
|
} else {
|
2020-02-15 18:37:57 -08:00
|
|
|
_virtualPaths.erase(vp);
|
2019-12-01 10:19:47 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-28 18:27:04 -08:00
|
|
|
IDataSource *FileSystem::checkBuiltinData(const Std::string &vfn, bool is_text) {
|
2019-12-01 10:19:47 -08:00
|
|
|
// Is it a Memory file?
|
2020-03-24 09:24:08 +09:00
|
|
|
Std::map<Common::String, MemoryFile *>::const_iterator mf = _memoryFiles.find(vfn);
|
2019-12-01 10:19:47 -08:00
|
|
|
|
2020-02-24 21:14:17 -08:00
|
|
|
if (mf != _memoryFiles.end())
|
2020-02-15 18:37:57 -08:00
|
|
|
return new IBufferDataSource(mf->_value->_data,
|
|
|
|
mf->_value->_len, is_text);
|
2019-12-01 10:19:47 -08:00
|
|
|
|
2020-03-14 05:07:43 +09:00
|
|
|
return nullptr;
|
2019-12-01 10:19:47 -08:00
|
|
|
}
|
|
|
|
|
2020-03-23 22:33:28 +09:00
|
|
|
bool FileSystem::rewrite_virtual_path(string &vfn) const {
|
2019-12-01 10:19:47 -08:00
|
|
|
bool ret = false;
|
|
|
|
string::size_type pos = vfn.size();
|
|
|
|
|
2020-01-28 18:27:04 -08:00
|
|
|
while ((pos = vfn.rfind('/', pos)) != Std::string::npos) {
|
2020-10-20 21:34:50 +09:00
|
|
|
// perr << vfn << ", '" << vfn.substr(0, pos) << "', " << pos << Std::endl;
|
2020-03-23 22:38:35 +09:00
|
|
|
Std::map<Common::String, string>::const_iterator p = _virtualPaths.find(
|
2019-12-01 10:19:47 -08:00
|
|
|
vfn.substr(0, pos));
|
|
|
|
|
2020-02-15 18:37:57 -08:00
|
|
|
if (p != _virtualPaths.end()) {
|
2019-12-01 10:19:47 -08:00
|
|
|
ret = true;
|
|
|
|
// rewrite first part of path
|
2019-12-05 22:59:28 -08:00
|
|
|
vfn = p->_value + vfn.substr(pos + 1);
|
2019-12-01 10:19:47 -08:00
|
|
|
pos = string::npos;
|
|
|
|
} else {
|
|
|
|
if (pos == 0)
|
|
|
|
break;
|
|
|
|
--pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We will allow all paths to work
|
2020-02-15 18:37:57 -08:00
|
|
|
if (_noForcedVPaths) ret = true;
|
2019-12-01 10:19:47 -08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool FileSystem::IsDir(const string &path) {
|
|
|
|
Common::FSNode node(path);
|
|
|
|
return node.isDirectory();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a directory
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool FileSystem::MkDir(const string &path) {
|
|
|
|
Common::FSNode newDir(path);
|
|
|
|
return newDir.createDirectory();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // End of namespace Ultima8
|
2019-12-16 21:32:17 -08:00
|
|
|
} // End of namespace Ultima
|