/* 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 * */ #include "common/archive.h" #include "common/config-manager.h" #include "common/savefile.h" #include "common/str-array.h" #include "common/system.h" #include "common/unzip.h" #include "sword25/sword25.h" // for kDebugScript #include "sword25/kernel/filesystemutil.h" #include "sword25/package/packagemanager.h" namespace Sword25 { const char PATH_SEPARATOR = '/'; static Common::String normalizePath(const Common::String &path, const Common::String ¤tDirectory) { Common::String wholePath = (path.size() >= 1 && path[0] == PATH_SEPARATOR) ? path : currentDirectory + PATH_SEPARATOR + path; if (wholePath.size() == 0) { // The path list has no elements, therefore the root directory is returned return Common::String(PATH_SEPARATOR); } return Common::normalizePath(wholePath, PATH_SEPARATOR); } PackageManager::PackageManager(Kernel *pKernel) : Service(pKernel), _currentDirectory(PATH_SEPARATOR), _rootFolder(ConfMan.get("path")) { if (!registerScriptBindings()) error("Script bindings could not be registered."); else debugC(kDebugScript, "Script bindings registered."); } PackageManager::~PackageManager() { // Free the package list Common::List::iterator i; for (i = _archiveList.begin(); i != _archiveList.end(); ++i) delete *i; } /** * Scans through the archive list for a specified file */ Common::ArchiveMemberPtr PackageManager::getArchiveMember(const Common::String &fileName) { // Loop through checking each archive Common::List::iterator i; for (i = _archiveList.begin(); i != _archiveList.end(); ++i) { if (!fileName.hasPrefix((*i)->_mountPath)) { // The mount path is in different subtree. Skipping continue; } // Look into the archive for the desired file Common::Archive *archiveFolder = (*i)->archive; // Construct relative path Common::String resPath(&fileName.c_str()[(*i)->_mountPath.size()]); if (archiveFolder->hasFile(resPath)) { return archiveFolder->getMember(resPath); } } return Common::ArchiveMemberPtr(); } bool PackageManager::loadPackage(const Common::String &fileName, const Common::String &mountPosition) { debug(3, "loadPackage(%s, %s)", fileName.c_str(), mountPosition.c_str()); Common::Archive *zipFile = Common::makeZipArchive(fileName); if (zipFile == NULL) { error("Unable to mount file \"%s\" to \"%s\"", fileName.c_str(), mountPosition.c_str()); return false; } else { debugC(kDebugResource, "Package '%s' mounted as '%s'.", fileName.c_str(), mountPosition.c_str()); Common::ArchiveMemberList files; zipFile->listMembers(files); debug(3, "Capacity %d", files.size()); for (Common::ArchiveMemberList::iterator it = files.begin(); it != files.end(); ++it) debug(3, "%s", (*it)->getName().c_str()); _archiveList.push_front(new ArchiveEntry(zipFile, mountPosition)); return true; } } bool PackageManager::loadDirectoryAsPackage(const Common::String &directoryName, const Common::String &mountPosition) { Common::FSNode directory(directoryName); Common::Archive *folderArchive = new Common::FSDirectory(directory, 6); if (!directory.exists() || (folderArchive == NULL)) { error("Unable to mount directory \"%s\" to \"%s\".", directoryName.c_str(), mountPosition.c_str()); return false; } else { debugC(kDebugResource, "Directory '%s' mounted as '%s'.", directoryName.c_str(), mountPosition.c_str()); Common::ArchiveMemberList files; folderArchive->listMembers(files); debug(0, "Capacity %d", files.size()); _archiveList.push_front(new ArchiveEntry(folderArchive, mountPosition)); return true; } } byte *PackageManager::getFile(const Common::String &fileName, uint *fileSizePtr) { const Common::String B25S_EXTENSION(".b25s"); Common::SeekableReadStream *in; if (fileName.hasSuffix(B25S_EXTENSION)) { // Savegame loading logic Common::SaveFileManager *sfm = g_system->getSavefileManager(); Common::InSaveFile *file = sfm->openForLoading( FileSystemUtil::getPathFilename(fileName)); if (!file) { error("Could not load savegame \"%s\".", fileName.c_str()); return 0; } if (fileSizePtr) *fileSizePtr = file->size(); byte *buffer = new byte[file->size()]; file->read(buffer, file->size()); delete file; return buffer; } Common::ArchiveMemberPtr fileNode = getArchiveMember(normalizePath(fileName, _currentDirectory)); if (!fileNode) return 0; if (!(in = fileNode->createReadStream())) return 0; // If the filesize is desired, then output the size if (fileSizePtr) *fileSizePtr = in->size(); // Read the file byte *buffer = new byte[in->size()]; int bytesRead = in->read(buffer, in->size()); delete in; if (!bytesRead) { delete[] buffer; return NULL; } return buffer; } Common::SeekableReadStream *PackageManager::getStream(const Common::String &fileName) { Common::SeekableReadStream *in; Common::ArchiveMemberPtr fileNode = getArchiveMember(normalizePath(fileName, _currentDirectory)); if (!fileNode) return 0; if (!(in = fileNode->createReadStream())) return 0; return in; } bool PackageManager::changeDirectory(const Common::String &directory) { // Get the path elements for the file _currentDirectory = normalizePath(directory, _currentDirectory); return true; } Common::String PackageManager::getAbsolutePath(const Common::String &fileName) { return normalizePath(fileName, _currentDirectory); } bool PackageManager::fileExists(const Common::String &fileName) { // FIXME: The current Zip implementation doesn't support getting a folder entry, which is needed for detecting // the English voick pack if (fileName == "/speech/en") { // To get around this, change to detecting one of the files in the folder return getArchiveMember(normalizePath(fileName + "/APO0001.ogg", _currentDirectory)); } Common::ArchiveMemberPtr fileNode = getArchiveMember(normalizePath(fileName, _currentDirectory)); return fileNode; } int PackageManager::doSearch(Common::ArchiveMemberList &list, const Common::String &filter, const Common::String &path, uint typeFilter) { Common::String normalizedFilter = normalizePath(filter, _currentDirectory); int num = 0; if (path.size() > 0) warning("STUB: PackageManager::doSearch(<%s>, <%s>, %d)", filter.c_str(), path.c_str(), typeFilter); // Loop through checking each archive Common::List::iterator i; for (i = _archiveList.begin(); i != _archiveList.end(); ++i) { Common::ArchiveMemberList memberList; if (!normalizedFilter.hasPrefix((*i)->_mountPath)) { // The mount path is in different subtree. Skipping continue; } // Construct relative path Common::String resFilter(&normalizedFilter.c_str()[(*i)->_mountPath.size()]); if ((*i)->archive->listMatchingMembers(memberList, resFilter) == 0) continue; // Create a list of the matching names for (Common::ArchiveMemberList::iterator it = memberList.begin(); it != memberList.end(); ++it) { if (((typeFilter & PackageManager::FT_DIRECTORY) && (*it)->getName().hasSuffix("/")) || ((typeFilter & PackageManager::FT_FILE) && !(*it)->getName().hasSuffix("/"))) { // Do not add duplicate files bool found = false; for (Common::ArchiveMemberList::iterator it1 = list.begin(); it1 != list.end(); ++it1) { if ((*it1)->getName() == (*it)->getName()) { found = true; break; } } if (!found) list.push_back(*it); num++; } } } return num; } } // End of namespace Sword25