144 lines
4.6 KiB
C++
144 lines
4.6 KiB
C++
/*
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
Copyright (C) 1997 Greg Lewis
|
|
|
|
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
See file, 'COPYING', for details.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <filesystem>
|
|
#include <optional>
|
|
#include <vector>
|
|
|
|
namespace fs
|
|
{
|
|
using namespace std::filesystem;
|
|
|
|
using data = std::optional<std::vector<uint8_t>>;
|
|
|
|
struct archive_like
|
|
{
|
|
path pathname;
|
|
|
|
bool external;
|
|
|
|
inline archive_like(const path &pathname, bool external) : pathname(pathname), external(external) { }
|
|
|
|
virtual bool contains(const path &filename) = 0;
|
|
|
|
virtual data load(const path &filename) = 0;
|
|
};
|
|
|
|
// clear all initialized/loaded data from fs
|
|
void clear();
|
|
|
|
// add the specified archive to the search path. must be the full
|
|
// path to the archive. Archives can be directories or archive-like
|
|
// files. Returns the archive if it already exists, the new
|
|
// archive added if one was added, or nullptr on error.
|
|
// `external` is a stored hint as to if the caller should consider
|
|
// the actual texture data embeddable.
|
|
std::shared_ptr<archive_like> addArchive(const path &p, bool external = false);
|
|
|
|
struct resolve_result
|
|
{
|
|
std::shared_ptr<archive_like> archive;
|
|
path filename;
|
|
|
|
inline explicit operator bool() const { return (bool)archive; }
|
|
};
|
|
|
|
// attempt to resolve the specified file.
|
|
// this will attempt resolves in the following order
|
|
// given the path "maps/start.map":
|
|
// - absolute path match (ie, "maps/start.map")
|
|
// - relative path match to current working dir (ie, "c:/eric-tools/bin/maps/start.map")
|
|
// - direct archive load (only used if path has a valid archive string in it; ie, "c:/quake/id1/pak0.pak/start.map")
|
|
// - registered directories in reverse order (ie, "c:/quake/mod/maps/start.map", "c:/quake/id1/maps/start.map")
|
|
// - registered archives in reverse order (ie, "c:/quake/pak1/maps/start.map", "c:/quake/pak0/maps/start.map")
|
|
// returns the archive that it is contained in, and the filename.
|
|
// the filename is only different from p if p is an archive path.
|
|
resolve_result where(const path &p, bool prefer_loose = false);
|
|
|
|
// attempt to load the specified resolve result.
|
|
data load(const resolve_result &pos);
|
|
|
|
// attempt to load the specified file from the specified path.
|
|
// shortcut to load(where(p))
|
|
inline data load(const path &p, bool prefer_loose = false)
|
|
{
|
|
return load(where(p, prefer_loose));
|
|
}
|
|
|
|
struct archive_components
|
|
{
|
|
path archive, filename;
|
|
|
|
inline operator bool() { return archive.has_relative_path(); }
|
|
};
|
|
|
|
// Splits an archive load path (ie, "C:/pak0.pak/file/path") into two components ("C:/pak0.pak", "file/path").
|
|
inline archive_components splitArchivePath(const path &source)
|
|
{
|
|
// check direct archive loading
|
|
// this is a bit complex, but we check the whole
|
|
// path to see if any piece of it that isn't
|
|
// the last piece matches a file
|
|
for (path archive = source.parent_path(); archive.has_relative_path(); archive = archive.parent_path()) {
|
|
if (is_regular_file(archive)) {
|
|
return {archive, source.lexically_relative(archive)};
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
// Quick helper to get the path this file would be in
|
|
// if it wasn't in a pak
|
|
inline path resolveArchivePath(const path &source)
|
|
{
|
|
if (auto paths = splitArchivePath(source)) {
|
|
return paths.archive.parent_path() / paths.filename;
|
|
}
|
|
|
|
return source;
|
|
}
|
|
}; // namespace fs
|
|
|
|
// Returns the path itself if it has an extension already, otherwise
|
|
// returns the path with extension replaced with `extension`.
|
|
inline fs::path DefaultExtension(const fs::path &path, const fs::path &extension)
|
|
{
|
|
if (path.has_extension())
|
|
return path;
|
|
|
|
return fs::path(path).replace_extension(extension);
|
|
}
|
|
|
|
#include <fmt/format.h>
|
|
|
|
// TODO: no wchar_t support in this version apparently
|
|
template<>
|
|
struct fmt::formatter<fs::path> : formatter<std::string>
|
|
{
|
|
template<typename FormatContext>
|
|
auto format(const fs::path &p, FormatContext &ctx)
|
|
{
|
|
return formatter<std::string>::format(p.string(), ctx);
|
|
}
|
|
}; |