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
|
|
|
|
|
|
|
FileSystem *FileSystem::filesystem = 0;
|
|
|
|
|
|
|
|
FileSystem::FileSystem(bool noforced)
|
|
|
|
: noforcedvpaths(noforced), allowdataoverride(true) {
|
2019-12-07 15:22:28 -08:00
|
|
|
con->Print(MM_INFO, "Creating FileSystem...\n");
|
2019-12-01 10:19:47 -08:00
|
|
|
|
|
|
|
filesystem = this;
|
2019-12-05 22:59:28 -08:00
|
|
|
AddVirtualPath("@home", "");
|
2019-12-01 10:19:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
FileSystem::~FileSystem() {
|
2019-12-07 15:22:28 -08:00
|
|
|
con->Print(MM_INFO, "Destroying FileSystem...\n");
|
2019-12-01 10:19:47 -08:00
|
|
|
|
|
|
|
filesystem = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Open a streaming file as readable. Streamed (0 on failure)
|
|
|
|
IDataSource *FileSystem::ReadFile(const string &vfn, bool is_text) {
|
|
|
|
string filename = vfn;
|
|
|
|
|
|
|
|
IDataSource *data = checkBuiltinData(vfn, is_text);
|
|
|
|
|
|
|
|
// allow data-override?
|
2019-12-05 22:59:28 -08:00
|
|
|
if (!allowdataoverride && data)
|
|
|
|
return data;
|
2019-12-01 10:19:47 -08:00
|
|
|
|
2019-12-12 19:52:23 -08:00
|
|
|
Common::SeekableReadStream *readStream;
|
|
|
|
if (!rawOpen(readStream, filename))
|
2019-12-12 18:03:43 -08:00
|
|
|
return 0;
|
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)
|
|
|
|
ODataSource *FileSystem::WriteFile(const string &vfn, bool is_text) {
|
|
|
|
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))
|
2019-12-12 18:03:43 -08:00
|
|
|
return 0;
|
2019-12-01 10:19:47 -08:00
|
|
|
|
2019-12-12 19:52:23 -08:00
|
|
|
return new OFileDataSource(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();
|
2019-12-31 21:54:53 -10:00
|
|
|
if (f->open(Common::String::format("data/%s", name.substr(6).c_str()),
|
2019-12-27 18:58:06 -10:00
|
|
|
*Ultima8Engine::get_instance()->getDataArchive())) {
|
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);
|
|
|
|
Std::string saveFilename = Ultima8Engine::get_instance()->getSaveFilename(slotNumber);
|
2019-12-12 19:52:23 -08:00
|
|
|
|
|
|
|
in = g_system->getSavefileManager()->openForLoading(saveFilename);
|
2019-12-12 22:04:03 -08:00
|
|
|
return in != 0;
|
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);
|
|
|
|
Std::string saveFilename = Ultima8Engine::get_instance()->getSaveFilename(slotNumber);
|
2019-12-12 19:52:23 -08:00
|
|
|
|
|
|
|
out = g_system->getSavefileManager()->openForSaving(saveFilename, false);
|
2019-12-14 19:40:48 -08:00
|
|
|
return out != 0;
|
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)) {
|
2019-12-07 15:22:28 -08:00
|
|
|
con->Print_err(MM_MAJOR_WARN, "Illegal file access\n");
|
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) {
|
2019-12-07 15:22:28 -08:00
|
|
|
con->Printf_err(MM_MINOR_ERR,
|
2019-12-01 10:19:47 -08:00
|
|
|
"Error mounting virtual path \"%s\": \"..\" not allowed.\n",
|
|
|
|
vp.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finding Reserved Virtual Path Names
|
|
|
|
// memory path is reserved
|
|
|
|
if (vp == "@memory" || vp.substr(0, 8) == "@memory/") {
|
2019-12-07 15:22:28 -08:00
|
|
|
con->Printf_err(MM_MINOR_ERR,
|
2019-12-01 10:19:47 -08:00
|
|
|
"Error mounting virtual path \"%s\": %s\"@memory\" is a reserved virtual path name.\n",
|
|
|
|
vp.c_str());
|
|
|
|
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
|
2019-12-07 15:22:28 -08:00
|
|
|
con->Printf(MM_INFO, "virtual path \"%s\": %s\n", vp.c_str(), fullpath.c_str());
|
2019-12-01 10:19:47 -08:00
|
|
|
#endif
|
|
|
|
if (!(fullpath.substr(0, 8) == "@memory/")) {
|
|
|
|
if (!IsDir(fullpath)) {
|
|
|
|
if (!create) {
|
|
|
|
#ifdef DEBUG
|
2019-12-07 15:22:28 -08:00
|
|
|
con->Printf_err(MM_MINOR_WARN,
|
2019-12-01 10:19:47 -08:00
|
|
|
"Problem mounting virtual path \"%s\": directory not found: %s\n",
|
|
|
|
vp.c_str(), fullpath.c_str());
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
MkDir(fullpath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtualpaths[vp] = rp;
|
|
|
|
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-01-28 18:27:04 -08:00
|
|
|
Std::map<Common::String, string>::iterator i = virtualpaths.find(vp);
|
2019-12-01 10:19:47 -08:00
|
|
|
|
|
|
|
if (i == virtualpaths.end()) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
virtualpaths.erase(vp);
|
|
|
|
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-01-28 18:27:04 -08:00
|
|
|
Std::map<Common::String, MemoryFile *>::iterator mf = memoryfiles.find(vfn);
|
2019-12-01 10:19:47 -08:00
|
|
|
|
|
|
|
if (mf != memoryfiles.end())
|
|
|
|
return new IBufferDataSource(mf->_value->data,
|
|
|
|
mf->_value->len, is_text);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FileSystem::rewrite_virtual_path(string &vfn) {
|
|
|
|
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) {
|
|
|
|
// perr << vfn << ", " << vfn.substr(0, pos) << ", " << pos << Std::endl;
|
|
|
|
Std::map<Common::String, string>::iterator p = virtualpaths.find(
|
2019-12-01 10:19:47 -08:00
|
|
|
vfn.substr(0, pos));
|
|
|
|
|
|
|
|
if (p != virtualpaths.end()) {
|
|
|
|
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
|
|
|
|
if (noforcedvpaths) ret = true;
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the current users pentagram home path
|
|
|
|
*/
|
|
|
|
|
2020-01-28 18:27:04 -08:00
|
|
|
Std::string FileSystem::getHomePath() {
|
2019-12-05 18:36:22 -08:00
|
|
|
Common::FSNode gameDir = Ultima8Engine::get_instance()->getGameDirectory();
|
2019-12-01 10:19:47 -08:00
|
|
|
return gameDir.getPath();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // End of namespace Ultima8
|
2019-12-16 21:32:17 -08:00
|
|
|
} // End of namespace Ultima
|