SWORD25: Reimplemented package manager for ScummVM using Common::Archive

svn-id: r53194
This commit is contained in:
Paul Gilbert 2010-07-31 13:02:58 +00:00 committed by Eugene Sandulenko
parent bed623b11e
commit ea29e41b86
7 changed files with 482 additions and 580 deletions

View File

@ -68,7 +68,7 @@ MODULE_OBJS := \
math/walkregion.o \
package/packagemanager.o \
package/packagemanager_script.o \
package/physfspackagemanager.o \
package/scummvmpackagemanager.o \
script/luabindhelper.o \
script/luacallback.o \
script/luascript.o \

View File

@ -42,10 +42,13 @@
// -----------------------------------------------------------------------------
BS_PackageManager::BS_PackageManager(BS_Kernel * pKernel) : BS_Service(pKernel)
{
namespace Sword25 {
BS_PackageManager::BS_PackageManager(BS_Kernel *pKernel) : BS_Service(pKernel) {
if (!_RegisterScriptBindings())
BS_LOG_ERRORLN("Script bindings could not be registered.");
else
BS_LOGLN("Script bindings registered.");
}
}
} // End of namespace Sword25

View File

@ -118,14 +118,14 @@ public:
* @param FileName The filename of the package to mount
* @param MountPosition The directory name under which the package should be mounted
* @return Returns true if the mount was successful, otherwise false.
*/
*/
virtual bool LoadPackage(const Common::String &FileName, const Common::String &MountPosition) = 0;
/**
* Mounts the contents of a directory in the specified directory in the virtual directory tree.
* @param The name of the directory to mount
* @param MountPosition The directory name under which the package should be mounted
* @return Returns true if the mount was successful, otherwise false.
*/
*/
virtual bool LoadDirectoryAsPackage(const Common::String &DirectoryName, const Common::String &MountPosition) = 0;
/**
* Downloads a file from the virtual directory tree
@ -190,7 +190,7 @@ public:
* Determines whether a file exists
* @param FileName The filename
* @return Returns true if the file exists, otherwise false.
*/
*/
virtual bool FileExists(const Common::String & FileName) = 0;
private:

View File

@ -1,497 +0,0 @@
/* 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.
*
* $URL$
* $Id$
*
*/
/*
* This code is based on Broken Sword 2.5 engine
*
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
*
* Licensed under GNU GPL v2
*
*/
// -----------------------------------------------------------------------------
// Includes
// -----------------------------------------------------------------------------
#include "sword25/package/physfspackagemanager.h"
#include "sword25/kernel/memlog_off.h"
#include <vector>
#include <sstream>
#include "sword25/kernel/memlog_on.h"
using namespace std;
// -----------------------------------------------------------------------------
#define BS_LOG_PREFIX "PHYSFSPACKAGEMANAGER"
// -----------------------------------------------------------------------------
// Hilfsfunktionen
// -----------------------------------------------------------------------------
namespace
{
const char * SafeGetLastError()
{
const char * ErrorMessage = PHYSFS_getLastError();
return ErrorMessage ? ErrorMessage : "unknown";
}
// -------------------------------------------------------------------------
void LogPhysfsError(const char * FunctionName)
{
BS_LOG_ERRORLN("%s() failed. Reason: %s.", FunctionName, SafeGetLastError());
}
// -------------------------------------------------------------------------
void LogPhysfsError(const char * FunctionName, const char * FileName)
{
BS_LOG_ERRORLN("%s() on file \"%s\" failed. Reason: %s.", FunctionName, FileName, SafeGetLastError());
}
// -------------------------------------------------------------------------
const char PATH_SEPARATOR = '/';
const char NAVIGATION_CHARACTER = '.';
// -------------------------------------------------------------------------
std::string RemoveRedundantPathSeparators(const std::string & Path)
{
std::string Result;
// Über alle Zeichen des Eingabepfades iterieren.
std::string::const_iterator It = Path.begin();
while (It != Path.end())
{
if (*It == PATH_SEPARATOR)
{
// Verzeichnistrenner gefunden.
// Folgen von Verzeichnistrennern überspringen.
while (It != Path.end() && *It == PATH_SEPARATOR) ++It;
// Einzelnen Verzeichnistrenner ausgeben, nur am Ende des Pfades wird kein Verzeichnistrenner mehr ausgegeben.
if (It != Path.end()) Result.push_back(PATH_SEPARATOR);
}
else
{
// Normales Zeichen gefunden, wird unverändert ausgegeben.
Result.push_back(*It);
++It;
}
}
return Result;
}
// ---------------------------------------------------------------------
struct PathElement
{
public:
PathElement(std::string::const_iterator Begin, std::string::const_iterator End) : m_Begin(Begin), m_End(End) {}
std::string::const_iterator GetBegin() const { return m_Begin; }
std::string::const_iterator GetEnd() const { return m_End; }
private:
std::string::const_iterator m_Begin;
std::string::const_iterator m_End;
};
// -------------------------------------------------------------------------
std::string NormalizePath(const std::string & Path, const std::string & CurrentDirectory)
{
// Feststellen, ob der Pfad absolut (beginnt mit /) oder relativ ist und im relativen Fall dem Gesamtpfad das aktuelle Verzeichnis
// voranstellen.
std::string WholePath = (Path.size() >= 1 && Path[0] == PATH_SEPARATOR) ? "" : CurrentDirectory + PATH_SEPARATOR;
// Alle gedoppelten und nachfolgende Verzeichnistrenner aus dem übergebenen Pfad entfernen und den Gesamtpfad zusammensetzen.
// CurrentDirectory wird nicht auf diese Weise gesäubert. Es wird vorrausgesetzt, dass CurrentDirectory keine überflüssigen
// Verzeichnistrenner beinhaltet.
WholePath += RemoveRedundantPathSeparators(Path);
// Gesamtpfad parsen und in Einzelelemente aufteilen. Dabei werden Vorkommen von ".." und "." korrekt behandelt.
vector<PathElement> PathElements;
size_t SeparatorPos = 0;
while (SeparatorPos < WholePath.size())
{
// Nächsten Verzeichnistrenner finden.
size_t NextSeparatorPos = WholePath.find(PATH_SEPARATOR, SeparatorPos + 1);
if (NextSeparatorPos == std::string::npos) NextSeparatorPos = WholePath.size();
// Anfang und Ende vom Pfadelement berechnen.
std::string::const_iterator ElementBegin = WholePath.begin() + SeparatorPos + 1;
std::string::const_iterator ElementEnd = WholePath.begin() + NextSeparatorPos;
if (ElementEnd - ElementBegin == 2 &&
ElementBegin[0] == NAVIGATION_CHARACTER &&
ElementBegin[1] == NAVIGATION_CHARACTER)
{
// Element ist "..", daher wird das vorangegangene Pfadelement aus dem vector entfernt.
if (PathElements.size()) PathElements.pop_back();
}
else if (ElementEnd - ElementBegin == 1 &&
ElementBegin[0] == NAVIGATION_CHARACTER)
{
// Element ist ".", wir tun gar nichts.
}
else
{
// Normales Element in den vector einfügen.
PathElements.push_back(PathElement(WholePath.begin() + SeparatorPos + 1, WholePath.begin() + NextSeparatorPos));
}
SeparatorPos = NextSeparatorPos;
}
if (PathElements.size())
{
// Die einzelnen Pfadelemente werden durch Verzeichnistrenner getrennt aneinandergesetzt.
// Der so entstandene String wird als Ergebnis zurückgegeben.
ostringstream PathBuilder;
vector<PathElement>::const_iterator It = PathElements.begin();
while (It != PathElements.end())
{
PathBuilder << PATH_SEPARATOR << std::string(It->GetBegin(), It->GetEnd());
++It;
}
return PathBuilder.str();
}
else
{
// Nach dem Parsen sind keine Pfadelemente mehr übrig geblieben, daher wird des Root-Verzeichnis zurückgegeben.
return std::string(1, PATH_SEPARATOR);
}
}
// -------------------------------------------------------------------------
// RAII-Klasse für PHYSFS-Filehandles.
// -------------------------------------------------------------------------
class PhysfsHandleHolder
{
public:
PhysfsHandleHolder(PHYSFS_File * Handle) : m_Handle(Handle) {}
~PhysfsHandleHolder()
{
if (m_Handle)
{
if (!PHYSFS_close(m_Handle)) LogPhysfsError("PHYSFS_close");
}
}
PHYSFS_File * Get() { return m_Handle; }
PHYSFS_File * Release()
{
PHYSFS_File * Result = m_Handle;
m_Handle = 0;
return Result;
}
private:
PHYSFS_File * m_Handle;
};
// -------------------------------------------------------------------------
// RAII-Klasse für PHYSFS-Listen.
// -------------------------------------------------------------------------
template<typename T>
class PhysfsListHolder
{
public:
PhysfsListHolder(T List) : m_List(List) {};
~PhysfsListHolder() { if (m_List) PHYSFS_freeList(m_List); }
T Get() { return m_List; }
private:
T m_List;
};
// -------------------------------------------------------------------------
PHYSFS_File * OpenFileAndGetSize(const std::string & FileName, const std::string & CurrentDirectory, unsigned int & FileSize)
{
// Datei öffnen.
PhysfsHandleHolder Handle(PHYSFS_openRead(NormalizePath(FileName, CurrentDirectory).c_str()));
if (!Handle.Get())
{
LogPhysfsError("PHYSFS_openRead", FileName.c_str());
return 0;
}
// Dateigröße bestimmen.
PHYSFS_sint64 LongFileSize = PHYSFS_fileLength(Handle.Get());
if (LongFileSize == -1)
{
BS_LOG_ERRORLN("Unable to determine filelength on PhysicsFS file \"%s\".", FileName.c_str());
return 0;
}
if (LongFileSize >= UINT_MAX)
{
BS_LOG_ERRORLN("File \"%s\" is too big.", FileName.c_str());
return 0;
}
// Rückgabewerte setzen.
FileSize = static_cast<unsigned int>(LongFileSize);
return Handle.Release();
}
}
// -----------------------------------------------------------------------------
// Konstruktion / Destruktion
// -----------------------------------------------------------------------------
BS_PhysfsPackageManager::BS_PhysfsPackageManager(BS_Kernel * KernelPtr) :
BS_PackageManager(KernelPtr),
m_CurrentDirectory(1, PATH_SEPARATOR)
{
if (!PHYSFS_init(0)) LogPhysfsError("PHYSFS_init");
}
// -----------------------------------------------------------------------------
BS_PhysfsPackageManager::~BS_PhysfsPackageManager()
{
if (!PHYSFS_deinit()) LogPhysfsError("PHYSFS_deinit");
}
// -----------------------------------------------------------------------------
BS_Service * BS_PhysfsPackageManager_CreateObject(BS_Kernel * KernelPtr) { return new BS_PhysfsPackageManager(KernelPtr); }
// -----------------------------------------------------------------------------
bool BS_PhysfsPackageManager::LoadPackage(const std::string & FileName, const std::string & MountPosition)
{
if (!PHYSFS_mount(FileName.c_str(), NormalizePath(MountPosition, m_CurrentDirectory).c_str(), 0))
{
BS_LOG_ERRORLN("Unable to mount file \"%s\" to \"%s\". Reason: %s.", FileName.c_str(), MountPosition.c_str(), SafeGetLastError());
return false;
}
else
{
BS_LOGLN("Package '%s' mounted as '%s'.", FileName.c_str(), MountPosition.c_str());
return true;
}
}
// -----------------------------------------------------------------------------
bool BS_PhysfsPackageManager::LoadDirectoryAsPackage(const std::string & DirectoryName, const std::string & MountPosition)
{
if (!PHYSFS_mount(DirectoryName.c_str(), NormalizePath(MountPosition, m_CurrentDirectory).c_str(), 0))
{
BS_LOG_ERRORLN("Unable to mount directory \"%s\" to \"%s\". Reason: %s.", DirectoryName.c_str(), MountPosition.c_str(), SafeGetLastError());
return false;
}
else
{
BS_LOGLN("Directory '%s' mounted as '%s'.", DirectoryName.c_str(), MountPosition.c_str());
return true;
}
}
// -----------------------------------------------------------------------------
void * BS_PhysfsPackageManager::GetFile(const std::string & FileName, unsigned int * FileSizePtr)
{
// Datei öffnen und deren Größe bestimmen.
unsigned int FileSize;
PhysfsHandleHolder Handle(OpenFileAndGetSize(FileName, m_CurrentDirectory, FileSize));
if (!Handle.Get()) return 0;
// Falls gewünscht, die Größe der Datei zurückgeben.
if (FileSizePtr) *FileSizePtr = FileSize;
// Datei einlesen.
char * Buffer = new char[FileSize];
if (PHYSFS_read(Handle.Get(), Buffer, 1, FileSize) <= 0)
{
LogPhysfsError("PHYSFS_read", FileName.c_str());
delete [] Buffer;
return 0;
}
return Buffer;
}
// -----------------------------------------------------------------------------
std::string BS_PhysfsPackageManager::GetCurrentDirectory()
{
return m_CurrentDirectory;
}
// -----------------------------------------------------------------------------
bool BS_PhysfsPackageManager::ChangeDirectory(const std::string & Directory)
{
// Pfad normalisieren.
std::string CleanedDirectory = NormalizePath(Directory, m_CurrentDirectory);
// Interne Variable setzen, wenn das Verzeichnis tatsächlich existiert oder das Wurzelverzeichnis ist.
if (CleanedDirectory == std::string(1, PATH_SEPARATOR) || PHYSFS_isDirectory(CleanedDirectory.c_str()))
{
m_CurrentDirectory = CleanedDirectory;
return true;
}
// Fehler ausgeben, wenn das Verzeichnis nicht existiert.
else
{
BS_LOG_ERRORLN("Tried to change to non-existing directory \"%s\". Call is ignored", Directory.c_str());
return false;
}
}
// -----------------------------------------------------------------------------
std::string BS_PhysfsPackageManager::GetAbsolutePath(const std::string & FileName)
{
return NormalizePath(FileName, m_CurrentDirectory);
}
// -----------------------------------------------------------------------------
unsigned int BS_PhysfsPackageManager::GetFileSize(const std::string & FileName)
{
// Datei öffnen und deren Größe bestimmen.
unsigned int FileSize;
PhysfsHandleHolder Handle(OpenFileAndGetSize(FileName, m_CurrentDirectory, FileSize));
if (!Handle.Get()) return 0xffffffff;
// Größe der Datei zurückgeben.
return FileSize;
}
// -----------------------------------------------------------------------------
unsigned int BS_PhysfsPackageManager::GetFileType(const std::string & FileName)
{
std::string NormalizedPath = NormalizePath(FileName, m_CurrentDirectory);
if (PHYSFS_exists(NormalizedPath.c_str()))
{
return PHYSFS_isDirectory(NormalizedPath.c_str()) ? BS_PackageManager::FT_DIRECTORY : BS_PackageManager::FT_FILE;
}
else
{
BS_LOG_ERRORLN("Cannot determine type of non-existant file \"%s\".", NormalizedPath.c_str());
return 0;
}
}
// -----------------------------------------------------------------------------
bool BS_PhysfsPackageManager::FileExists(const std::string & FileName)
{
std::string NormalizedPath = NormalizePath(FileName, m_CurrentDirectory);
return PHYSFS_exists(NormalizedPath.c_str()) != 0;
}
// -----------------------------------------------------------------------------
// Dateien suchen
// -----------------------------------------------------------------------------
class PhysfsFileSearch : public BS_PackageManager::FileSearch
{
public:
// Path muss normalisiert sein.
PhysfsFileSearch(BS_PackageManager & PackageManager, const vector<std::string> & FoundFiles) :
m_PackageManager(PackageManager),
m_FoundFiles(FoundFiles),
m_FoundFilesIt(m_FoundFiles.begin())
{
}
virtual std::string GetCurFileName()
{
return *m_FoundFilesIt;
}
virtual unsigned int GetCurFileType()
{
return m_PackageManager.GetFileType(*m_FoundFilesIt);
}
virtual unsigned int GetCurFileSize()
{
return m_PackageManager.GetFileSize(*m_FoundFilesIt);
}
virtual bool NextFile()
{
++m_FoundFilesIt;
return m_FoundFilesIt != m_FoundFiles.end();
}
BS_PackageManager & m_PackageManager;
vector<std::string> m_FoundFiles;
vector<std::string>::const_iterator m_FoundFilesIt;
};
// -----------------------------------------------------------------------------
BS_PackageManager::FileSearch * BS_PhysfsPackageManager::CreateSearch(const std::string& Filter, const std::string& Path, unsigned int TypeFilter)
{
std::string NormalizedPath = NormalizePath(Path, m_CurrentDirectory);
// Nach Wildcards gefilterte Ergebnisliste erstellen.
PhysfsListHolder<char **> FilesPtr(PHYSFSEXT_enumerateFilesWildcard(NormalizedPath.c_str(), Filter.c_str(), 1));
// Diese Liste muss nun wiederum nach den gewünschten Dateitype gefiltert werden. Das Ergebnis wird in einem vector gespeichert, der dann
// einem PhysfsFileSearch-Objekt übergeben wird.
vector<std::string> FoundFiles;
for (char ** CurFilePtr = FilesPtr.Get(); *CurFilePtr != 0; ++CurFilePtr)
{
// Vollständigen Pfad zur gefunden Datei konstruieren.
std::string FullFilePath = NormalizedPath + std::string(1, PATH_SEPARATOR) + *CurFilePtr;
// Feststellen, ob der Dateityp erwünscht ist und nur dann den Dateinamen dem Ergebnisvektor hinzufügen.
unsigned int FileType = GetFileType(FullFilePath);
if (FileType & TypeFilter) FoundFiles.push_back(FullFilePath);
}
// Falls überhaupt eine Datei gefunden wurde, wird ein FileSearch-Objekt zurückgegeben mit dem über die gefundenen Dateien iteriert werden kann.
// Anderenfalls wird 0 zurückgegeben.
if (FoundFiles.size())
{
return new PhysfsFileSearch(*this, FoundFiles);
}
else
{
return 0;
}
}

View File

@ -1,76 +0,0 @@
/* 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.
*
* $URL$
* $Id$
*
*/
/*
* This code is based on Broken Sword 2.5 engine
*
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
*
* Licensed under GNU GPL v2
*
*/
#ifndef SWORD25_PHYSFS_PACKAGE_MANAGER_H
#define SWORD25_PHYSFS_PACKAGE_MANAGER_H
// -----------------------------------------------------------------------------
// Includes
// -----------------------------------------------------------------------------
#include "sword25/kernel/common.h"
#include "sword25/package/packagemanager.h"
// -----------------------------------------------------------------------------
// Forward declarations
// -----------------------------------------------------------------------------
class BS_Kernel;
// -----------------------------------------------------------------------------
// Klassendefinition
// -----------------------------------------------------------------------------
class BS_PhysfsPackageManager : public BS_PackageManager
{
public:
BS_PhysfsPackageManager(BS_Kernel * KernelPtr);
virtual ~BS_PhysfsPackageManager();
virtual bool LoadPackage(const std::string & FileName, const std::string& MountPosition);
virtual bool LoadDirectoryAsPackage(const std::string & DirectoryName, const std::string& MountPosition);
virtual void* GetFile(const std::string& FileName, unsigned int * FileSizePtr = 0);
virtual std::string GetCurrentDirectory();
virtual bool ChangeDirectory(const std::string & Directory);
virtual std::string GetAbsolutePath(const std::string & FileName);
virtual FileSearch* CreateSearch(const std::string & Filter, const std::string& Path, unsigned int TypeFilter = FT_DIRECTORY | FT_FILE);
virtual unsigned int GetFileSize(const std::string & FileName);
virtual unsigned int GetFileType(const std::string & FileName);
virtual bool FileExists(const std::string & FileName);
private:
std::string m_CurrentDirectory;
};
#endif

View File

@ -0,0 +1,367 @@
/* 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.
*
* $URL$
* $Id$
*
*/
// -----------------------------------------------------------------------------
// Includes
// -----------------------------------------------------------------------------
#include "common/archive.h"
#include "common/config-manager.h"
#include "common/str-array.h"
#include "common/unzip.h"
#include "sword25/package/scummvmpackagemanager.h"
// -----------------------------------------------------------------------------
#define BS_LOG_PREFIX "SCUMMVMPACKAGEMANAGER"
namespace Sword25 {
const char PATH_SEPARATOR = '/';
const char NAVIGATION_CHARACTER = '.';
// -----------------------------------------------------------------------------
// Support functions and classes
// -----------------------------------------------------------------------------
static Common::String RemoveRedundantPathSeparators(const Common::String &Path) {
Common::String Result;
// Iterate over all the chracters of the input path
Common::String::const_iterator It = Path.begin();
while (It != Path.end()) {
if (*It == PATH_SEPARATOR) {
// Directory separater found
// Skip over directory separator(s)
while (It != Path.end() && *It == PATH_SEPARATOR) ++It;
// Unless it's the end of the path, add the separator
if (It != Path.end()) Result += PATH_SEPARATOR;
} else {
// Normal characters are copied over unchanged
Result += *It;
++It;
}
}
return Result;
}
// ---------------------------------------------------------------------
static PathElementArray SeparatePath(const Common::String &Path, const Common::String &CurrentDirectory) {
// Determine whether the path is absolute (begins with /) or relative, in which case it's added
// to the current directory
Common::String wholePath = (Path.size() >= 1 && Path[0] == PATH_SEPARATOR) ? "" : CurrentDirectory + PATH_SEPARATOR;
// Add in the provided path
wholePath += RemoveRedundantPathSeparators(Path);
// Parse the path and divide into it's components. This ensures that occurences of ".." and "."
// are handled correctly.
PathElementArray pathElements;
size_t separatorPos = 0;
while (separatorPos < wholePath.size()) {
// Find next directory separator
const char *p = strchr(wholePath.c_str() + separatorPos + 1, PATH_SEPARATOR);
size_t nextseparatorPos = (p == NULL) ? wholePath.size() : p - wholePath.c_str();
// Calculate the beginning and end of the path element
Common::String::const_iterator elementBegin = wholePath.begin() + separatorPos + 1;
Common::String::const_iterator elementEnd = wholePath.begin() + nextseparatorPos;
if (elementEnd - elementBegin == 2 &&
elementBegin[0] == NAVIGATION_CHARACTER &&
elementBegin[1] == NAVIGATION_CHARACTER) {
// element is "..", therefore the previous path element should be removed
if (pathElements.size()) pathElements.pop_back();
} else if (elementEnd - elementBegin == 1 &&
elementBegin[0] == NAVIGATION_CHARACTER) {
// element is ".", so we do nothing
} else {
// Normal elements get added to the list
pathElements.push_back(PathElement(wholePath.begin() + separatorPos + 1, wholePath.begin() + nextseparatorPos));
}
separatorPos = nextseparatorPos;
}
return pathElements;
}
static Common::String NormalizePath(const Common::String &Path, const Common::String &CurrentDirectory) {
// Get the path elements for the file
PathElementArray pathElements = SeparatePath(Path, CurrentDirectory);
if (pathElements.size()) {
// The individual path elements are fitted together, separated by a directory
// separator. The resulting string is returned as a result
Common::String Result;
PathElementArray::const_iterator It = pathElements.begin();
while (It != pathElements.end()) {
Result += PATH_SEPARATOR;
Result += Common::String(It->GetBegin(), It->GetEnd());
++It;
}
return Result;
} else {
// The path list has no elements, therefore the root directory is returned
return Common::String(PATH_SEPARATOR);
}
}
// -----------------------------------------------------------------------------
// Constructor / Destructor
// -----------------------------------------------------------------------------
BS_ScummVMPackageManager::BS_ScummVMPackageManager(BS_Kernel *KernelPtr) :
BS_PackageManager(KernelPtr),
_currentDirectory(PATH_SEPARATOR),
_rootFolder(ConfMan.get("path")) {
}
// -----------------------------------------------------------------------------
BS_ScummVMPackageManager::~BS_ScummVMPackageManager() {
}
// -----------------------------------------------------------------------------
BS_Service *BS_ScummVMPackageManager_CreateObject(BS_Kernel *KernelPtr) {
return new BS_ScummVMPackageManager(KernelPtr);
}
// -----------------------------------------------------------------------------
/**
* Scans through the archive list for a specified file
*/
Common::FSNode BS_ScummVMPackageManager::GetFSNode(const Common::String &FileName) {
// Get the path elements for the file
PathElementArray pathElements = SeparatePath(FileName, _currentDirectory);
// Loop through checking each archive
Common::List<ArchiveEntry>::iterator i;
for (i = _archiveList.begin(); i != _archiveList.end(); ++i) {
if (i->MountPath.size() > pathElements.size())
// The mount path has more subfolder depth than the search entry, so skip it
continue;
// Check the path against that of the archive
PathElementArray::iterator iPath = pathElements.begin();
PathElementArray::iterator iEntry = i->MountPath.begin();
for (; iEntry != i->MountPath.end(); ++iEntry, ++iPath) {
if (Common::String(iPath->GetBegin(), iPath->GetEnd()) ==
Common::String(iEntry->GetBegin(), iEntry->GetEnd()))
break;
}
if (iEntry == i->MountPath.end()) {
// Look into the archive for the desired file
// Common::Archive *archiveFolder = i->Archive;
// TODO: Loop through any folders in the archive
for (; iPath != pathElements.end(); ++iPath) {
}
// Return the found node
return Common::FSNode();
}
}
return Common::FSNode();
}
// -----------------------------------------------------------------------------
bool BS_ScummVMPackageManager::LoadPackage(const Common::String &FileName, const Common::String &MountPosition) {
// Get the path elements for the file
PathElementArray pathElements = SeparatePath(MountPosition, _currentDirectory);
Common::Archive *zipFile = Common::makeZipArchive(FileName);
if (zipFile == NULL) {
BS_LOG_ERRORLN("Unable to mount file \"%s\" to \"%s\"", FileName.c_str(), MountPosition.c_str());
return false;
} else {
BS_LOGLN("Package '%s' mounted as '%s'.", FileName.c_str(), MountPosition.c_str());
_archiveList.push_back(ArchiveEntry(zipFile, pathElements));
return true;
}
}
// -----------------------------------------------------------------------------
bool BS_ScummVMPackageManager::LoadDirectoryAsPackage(const Common::String &DirectoryName, const Common::String &MountPosition) {
// Get the path elements for the file
PathElementArray pathElements = SeparatePath(MountPosition, _currentDirectory);
Common::FSNode directory(DirectoryName);
Common::Archive *folderArchive = new Common::FSDirectory(directory);
if (!directory.exists() || (folderArchive == NULL)) {
BS_LOG_ERRORLN("Unable to mount directory \"%s\" to \"%s\".", DirectoryName.c_str(), MountPosition.c_str());
return false;
} else {
BS_LOGLN("Directory '%s' mounted as '%s'.", DirectoryName.c_str(), MountPosition.c_str());
_archiveList.push_front(ArchiveEntry(folderArchive, pathElements));
return true;
}
}
// -----------------------------------------------------------------------------
void *BS_ScummVMPackageManager::GetFile(const Common::String &FileName, unsigned int *FileSizePtr) {
Common::File f;
Common::FSNode fileNode = GetFSNode(FileName);
if (!fileNode.exists()) return 0;
if (!f.open(fileNode)) return 0;
// If the filesize is desired, then output the size
if (FileSizePtr) *FileSizePtr = f.size();
// Read the file
byte *buffer = new byte[f.size()];
if (!f.read(buffer, f.size())) return 0;
f.close();
return buffer;
}
// -----------------------------------------------------------------------------
Common::String BS_ScummVMPackageManager::GetCurrentDirectory() {
return _currentDirectory;
}
// -----------------------------------------------------------------------------
bool BS_ScummVMPackageManager::ChangeDirectory(const Common::String &Directory) {
// Get the path elements for the file
_currentDirectory = NormalizePath(Directory, _currentDirectory);
return true;
}
// -----------------------------------------------------------------------------
Common::String BS_ScummVMPackageManager::GetAbsolutePath(const Common::String &FileName) {
return NormalizePath(FileName, _currentDirectory);
}
// -----------------------------------------------------------------------------
unsigned int BS_ScummVMPackageManager::GetFileSize(const Common::String &FileName) {
Common::File f;
Common::FSNode fileNode = GetFSNode(FileName);
if (!fileNode.exists()) return 0;
if (!f.open(fileNode)) return 0;
uint32 fileSize = f.size();
f.close();
return fileSize;
}
// -----------------------------------------------------------------------------
unsigned int BS_ScummVMPackageManager::GetFileType(const Common::String &FileName) {
Common::File f;
Common::FSNode fileNode = GetFSNode(FileName);
if (!fileNode.exists()) return 0;
return fileNode.isDirectory() ? BS_PackageManager::FT_DIRECTORY : BS_PackageManager::FT_FILE;
}
// -----------------------------------------------------------------------------
bool BS_ScummVMPackageManager::FileExists(const Common::String &FileName) {
Common::FSNode fileNode = GetFSNode(FileName);
return fileNode.exists();
}
// -----------------------------------------------------------------------------
// File find
// -----------------------------------------------------------------------------
class ArchiveFileSearch : public BS_PackageManager::FileSearch {
public:
// Path must be normalised
ArchiveFileSearch(BS_PackageManager &PackageManager, const Common::StringArray &FoundFiles) :
_packageManager(PackageManager),
_foundFiles(FoundFiles),
_foundFilesIt(_foundFiles.begin()) {
}
virtual Common::String GetCurFileName() {
return *_foundFilesIt;
}
virtual unsigned int GetCurFileType() {
return _packageManager.GetFileType(*_foundFilesIt);
}
virtual unsigned int GetCurFileSize() {
return _packageManager.GetFileSize(*_foundFilesIt);
}
virtual bool NextFile() {
++_foundFilesIt;
return _foundFilesIt != _foundFiles.end();
}
BS_PackageManager & _packageManager;
Common::StringArray _foundFiles;
Common::StringArray::const_iterator _foundFilesIt;
};
// -----------------------------------------------------------------------------
BS_PackageManager::FileSearch *BS_ScummVMPackageManager::CreateSearch(
const Common::String &Filter, const Common::String &Path, unsigned int TypeFilter) {
Common::String NormalizedPath = NormalizePath(Path, _currentDirectory);
Common::FSNode folderNode = GetFSNode(Path);
if (!folderNode.exists() || !folderNode.isDirectory()) return NULL;
Common::Archive *folder = new Common::FSDirectory(folderNode);
Common::ArchiveMemberList memberList;
if (folder->listMatchingMembers(memberList, Filter) == 0)
return NULL;
// Create a list of the matching names
Common::StringArray nameList;
for (Common::ArchiveMemberList::iterator i = memberList.begin(); i != memberList.end(); ++i) {
nameList.push_back((*i)->getName());
}
// Return a ArchiveFileSearch object that encapsulates the name list
return new ArchiveFileSearch(*this, nameList);
}
} // End of namespace Sword25

View File

@ -0,0 +1,105 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#ifndef SWORD25_SCUMMVM_PACKAGE_MANAGER_H
#define SWORD25_SCUMMVM_PACKAGE_MANAGER_H
// -----------------------------------------------------------------------------
// Includes
// -----------------------------------------------------------------------------
#include "common/archive.h"
#include "common/array.h"
#include "common/fs.h"
#include "common/str.h"
#include "sword25/kernel/common.h"
#include "sword25/package/packagemanager.h"
namespace Sword25 {
// -----------------------------------------------------------------------------
// Forward declarations
// -----------------------------------------------------------------------------
class BS_Kernel;
// -----------------------------------------------------------------------------
// Class definitions
// -----------------------------------------------------------------------------
struct PathElement {
public:
PathElement() { }
PathElement(Common::String::const_iterator Begin, Common::String::const_iterator End) : m_Begin(Begin), m_End(End) {}
Common::String::const_iterator GetBegin() const { return m_Begin; }
Common::String::const_iterator GetEnd() const { return m_End; }
private:
Common::String::const_iterator m_Begin;
Common::String::const_iterator m_End;
};
typedef Common::Array<PathElement> PathElementArray;
class BS_ScummVMPackageManager : public BS_PackageManager {
private:
class ArchiveEntry {
public:
Common::Archive *Archive;
PathElementArray MountPath;
ArchiveEntry(Common::Archive *Archive_, const PathElementArray &MountPath_):
Archive(Archive_), MountPath(MountPath_) {
}
~ArchiveEntry() {
delete Archive;
}
};
Common::String _currentDirectory;
Common::FSNode _rootFolder;
Common::List<ArchiveEntry> _archiveList;
Common::FSNode GetFSNode(const Common::String &FileName);
public:
BS_ScummVMPackageManager(BS_Kernel * KernelPtr);
virtual ~BS_ScummVMPackageManager();
virtual bool LoadPackage(const Common::String &FileName, const Common::String &MountPosition);
virtual bool LoadDirectoryAsPackage(const Common::String &DirectoryName, const Common::String &MountPosition);
virtual void *GetFile(const Common::String &FileName, unsigned int *FileSizePtr = 0);
virtual Common::String GetCurrentDirectory();
virtual bool ChangeDirectory(const Common::String &Directory);
virtual Common::String GetAbsolutePath(const Common::String &FileName);
virtual FileSearch *CreateSearch(const Common::String &Filter, const Common::String &Path, unsigned int TypeFilter = FT_DIRECTORY | FT_FILE);
virtual unsigned int GetFileSize(const Common::String &FileName);
virtual unsigned int GetFileType(const Common::String &FileName);
virtual bool FileExists(const Common::String &FileName);
};
} // End of namespace Sword25
#endif