mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
306 lines
9.2 KiB
C++
306 lines
9.2 KiB
C++
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "common/memstream.h"
|
|
#include "ags/lib/std/algorithm.h"
|
|
#include "ags/lib/std/utility.h"
|
|
#include "ags/shared/core/platform.h"
|
|
#include "ags/shared/core/asset_manager.h"
|
|
#include "ags/shared/util/directory.h"
|
|
#include "ags/shared/util/multi_file_lib.h"
|
|
#include "ags/shared/util/path.h"
|
|
#include "ags/shared/util/string_utils.h" // cbuf_to_string_and_free
|
|
|
|
namespace AGS3 {
|
|
namespace AGS {
|
|
namespace Shared {
|
|
|
|
inline static bool IsAssetLibDir(const AssetLibInfo *lib) {
|
|
return lib->BaseFileName.IsEmpty();
|
|
}
|
|
|
|
bool AssetManager::AssetLibEx::TestFilter(const String &filter) const {
|
|
return filter == "*" ||
|
|
(std::find(Filters.begin(), Filters.end(), filter) != Filters.end());
|
|
}
|
|
|
|
bool AssetManager::LibsByPriority::operator()(const AssetLibInfo *lib1, const AssetLibInfo *lib2) const {
|
|
const bool lib1dir = IsAssetLibDir(lib1);
|
|
const bool lib2dir = IsAssetLibDir(lib2);
|
|
if (lib1dir == lib2dir)
|
|
return false; // both are equal, none is less
|
|
if (Priority == kAssetPriorityLib)
|
|
return !lib1dir; // first element is less if it's library file
|
|
if (Priority == kAssetPriorityDir)
|
|
return lib1dir; // first element is less if it's directory
|
|
return false; // unknown priority, just fail
|
|
}
|
|
|
|
|
|
/* static */ bool AssetManager::IsDataFile(const String &data_file) {
|
|
Stream *in = File::OpenFileCI(data_file.GetCStr(), Shared::kFile_Open, Shared::kFile_Read);
|
|
if (in) {
|
|
MFLUtil::MFLError err = MFLUtil::TestIsMFL(in, true);
|
|
delete in;
|
|
return err == MFLUtil::kMFLNoError;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* static */ AssetError AssetManager::ReadDataFileTOC(const String &data_file, AssetLibInfo &lib) {
|
|
Stream *in = File::OpenFileCI(data_file.GetCStr(), Shared::kFile_Open, Shared::kFile_Read);
|
|
if (in) {
|
|
MFLUtil::MFLError err = MFLUtil::ReadHeader(lib, in);
|
|
delete in;
|
|
return (err != MFLUtil::kMFLNoError) ? kAssetErrLibParse : kAssetNoError;
|
|
}
|
|
return kAssetErrNoLibFile;
|
|
}
|
|
|
|
AssetManager::AssetManager() {
|
|
_libsByPriority.Priority = kAssetPriorityDir;
|
|
}
|
|
|
|
void AssetManager::SetSearchPriority(AssetSearchPriority priority) {
|
|
_libsByPriority.Priority = priority;
|
|
|
|
std::sort(_activeLibs.begin(), _activeLibs.end(), _libsByPriority);
|
|
}
|
|
|
|
AssetSearchPriority AssetManager::GetSearchPriority() const {
|
|
return _libsByPriority.Priority;
|
|
}
|
|
|
|
AssetError AssetManager::AddLibrary(const String &path, const AssetLibInfo **out_lib) {
|
|
return AddLibrary(path, "", out_lib);
|
|
}
|
|
|
|
AssetError AssetManager::AddLibrary(const String &path, const String &filters, const AssetLibInfo **out_lib) {
|
|
if (path.IsEmpty())
|
|
return kAssetErrNoLibFile;
|
|
|
|
for (const auto &lib : _libs) {
|
|
if (Path::ComparePaths(lib->BasePath, path) == 0) {
|
|
// already present, only assign new filters
|
|
lib->Filters = filters.Split(',');
|
|
if (out_lib)
|
|
*out_lib = lib;
|
|
return kAssetNoError;
|
|
}
|
|
}
|
|
|
|
AssetLibEx *lib;
|
|
AssetError err = RegisterAssetLib(path, lib);
|
|
if (err != kAssetNoError)
|
|
return err;
|
|
lib->Filters = filters.Split(',');
|
|
auto place = std::upper_bound(_activeLibs.begin(), _activeLibs.end(), lib, _libsByPriority);
|
|
_activeLibs.insert(place, lib);
|
|
if (out_lib)
|
|
*out_lib = lib;
|
|
return kAssetNoError;
|
|
}
|
|
|
|
void AssetManager::RemoveLibrary(const String &path) {
|
|
int idx = 0;
|
|
for (auto it = _libs.begin(); it != _libs.end(); ++it, ++idx) {
|
|
if (Path::ComparePaths((*it)->BasePath, path) == 0) {
|
|
_libs.remove_at(idx);
|
|
_activeLibs.remove(*it);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AssetManager::RemoveAllLibraries() {
|
|
for (uint i = 0; i < _libs.size(); ++i)
|
|
delete _libs[i];
|
|
|
|
_libs.clear();
|
|
_activeLibs.clear();
|
|
}
|
|
|
|
size_t AssetManager::GetLibraryCount() const {
|
|
return _libs.size();
|
|
}
|
|
|
|
const AssetLibInfo *AssetManager::GetLibraryInfo(size_t index) const {
|
|
return index < _libs.size() ? _libs[index] : nullptr;
|
|
}
|
|
|
|
bool AssetManager::DoesAssetExist(const String &asset_name, const String &filter) const {
|
|
for (const auto &lib : _activeLibs) {
|
|
if (!lib->TestFilter(filter))
|
|
continue; // filter does not match
|
|
|
|
if (IsAssetLibDir(lib)) {
|
|
String filename = File::FindFileCI(lib->BaseDir, asset_name);
|
|
if (!filename.IsEmpty() && File::IsFile(filename)) return true;
|
|
} else {
|
|
for (const auto &a : lib->AssetInfos) {
|
|
if (a.FileName.CompareNoCase(asset_name) == 0) return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void AssetManager::FindAssets(std::vector<String> &assets, const String &wildcard,
|
|
const String &filter) const {
|
|
String pattern = StrUtil::WildcardToRegex(wildcard);
|
|
|
|
for (const auto *lib : _activeLibs) {
|
|
auto match = std::find(lib->Filters.begin(), lib->Filters.end(), filter);
|
|
if (match == lib->Filters.end())
|
|
continue; // filter does not match
|
|
|
|
if (IsAssetLibDir(lib)) {
|
|
for (FindFile ff = FindFile::OpenFiles(lib->BaseDir, wildcard);
|
|
!ff.AtEnd(); ff.Next())
|
|
assets.push_back(ff.Current());
|
|
} else {
|
|
for (const auto &a : lib->AssetInfos) {
|
|
if (pattern == "*" || (*pattern.GetCStr() &&
|
|
Common::String(a.FileName.GetCStr()).hasSuffixIgnoreCase(pattern.GetCStr() + 1)))
|
|
assets.push_back(a.FileName);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort and remove duplicates
|
|
std::sort(assets.begin(), assets.end());
|
|
assets.erase(std::unique(assets.begin(), assets.end()), assets.end());
|
|
}
|
|
|
|
AssetError AssetManager::RegisterAssetLib(const String &path, AssetLibEx *&out_lib) {
|
|
// Test for a directory
|
|
std::unique_ptr<AssetLibEx> lib;
|
|
if (File::IsDirectory(path)) {
|
|
lib.reset(new AssetLibEx());
|
|
lib->BasePath = Path::MakeAbsolutePath(path);
|
|
lib->BaseDir = Path::GetDirectoryPath(lib->BasePath);
|
|
|
|
// TODO: maybe parse directory for the file reference? idk if needed
|
|
}
|
|
// ...else try open a data library
|
|
else {
|
|
Stream *in = File::OpenFileCI(path.GetCStr(), Shared::kFile_Open, Shared::kFile_Read);
|
|
if (!in)
|
|
return kAssetErrNoLibFile; // can't be opened, return error code
|
|
|
|
lib.reset(new AssetLibEx());
|
|
MFLUtil::MFLError mfl_err = MFLUtil::ReadHeader(*lib, in);
|
|
delete in;
|
|
|
|
if (mfl_err != MFLUtil::kMFLNoError)
|
|
return kAssetErrLibParse;
|
|
|
|
lib->BasePath = Path::MakeAbsolutePath(path);
|
|
lib->BaseDir = Path::GetDirectoryPath(lib->BasePath);
|
|
lib->BaseFileName = Path::GetFilename(lib->BasePath);
|
|
lib->LibFileNames[0] = lib->BaseFileName;
|
|
|
|
// Find out real library files in the current filesystem and save them
|
|
for (size_t i = 0; i < lib->LibFileNames.size(); ++i) {
|
|
lib->RealLibFiles.push_back(File::FindFileCI(lib->BaseDir, lib->LibFileNames[i]));
|
|
}
|
|
}
|
|
|
|
out_lib = lib.release();
|
|
_libs.push_back(out_lib);
|
|
|
|
return kAssetNoError;
|
|
}
|
|
|
|
Stream *AssetManager::OpenAsset(const String &asset_name, const String &filter) const {
|
|
for (const auto *lib : _activeLibs) {
|
|
if (!lib->TestFilter(filter)) continue; // filter does not match
|
|
|
|
Stream *s = nullptr;
|
|
if (IsAssetLibDir(lib))
|
|
s = OpenAssetFromDir(lib, asset_name);
|
|
else
|
|
s = OpenAssetFromLib(lib, asset_name);
|
|
if (s)
|
|
return s;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Stream *AssetManager::OpenAssetFromLib(const AssetLibEx *lib, const String &asset_name) const {
|
|
for (const auto &a : lib->AssetInfos) {
|
|
if (a.FileName.CompareNoCase(asset_name) == 0) {
|
|
String libfile = lib->RealLibFiles[a.LibUid];
|
|
if (libfile.IsEmpty())
|
|
return nullptr;
|
|
return File::OpenFile(libfile, a.Offset, a.Offset + a.Size);
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Stream *AssetManager::OpenAssetFromDir(const AssetLibEx *lib, const String &file_name) const {
|
|
String found_file = File::FindFileCI(lib->BaseDir, file_name);
|
|
if (found_file.IsEmpty())
|
|
return nullptr;
|
|
return File::OpenFileRead(found_file);
|
|
}
|
|
|
|
Stream *AssetManager::OpenAsset(const String &asset_name) const {
|
|
return OpenAsset(asset_name, "");
|
|
}
|
|
|
|
Common::SeekableReadStream *AssetManager::OpenAssetStream(const String &asset_name) const {
|
|
return OpenAssetStream(asset_name, "");
|
|
}
|
|
|
|
Common::SeekableReadStream *AssetManager::OpenAssetStream(const String &asset_name, const String &filter) const {
|
|
Stream *stream = OpenAsset(asset_name, filter);
|
|
if (!stream)
|
|
return nullptr;
|
|
|
|
// Get the contents of the asset
|
|
size_t assetSize = stream->GetLength();
|
|
byte *data = (byte *)malloc(assetSize);
|
|
stream->Read(data, assetSize);
|
|
delete stream;
|
|
|
|
return new Common::MemoryReadStream(data, assetSize, DisposeAfterUse::YES);
|
|
}
|
|
|
|
String GetAssetErrorText(AssetError err) {
|
|
switch (err) {
|
|
case kAssetNoError:
|
|
return "No error.";
|
|
case kAssetErrNoLibFile:
|
|
return "Asset library file not found or could not be opened.";
|
|
case kAssetErrLibParse:
|
|
return "Not an asset library or unsupported format.";
|
|
case kAssetErrNoManager:
|
|
return "Asset manager is not initialized.";
|
|
}
|
|
return "Unknown error.";
|
|
}
|
|
|
|
} // namespace Shared
|
|
} // namespace AGS
|
|
} // namespace AGS3
|