#pragma once #include #include #include #include #include #include #include "appfat.h" #include "diablo.h" #include "mpq/mpq_reader.hpp" #include "utils/file_util.h" #include "utils/str_cat.hpp" #include "utils/string_or_view.hpp" namespace devilution { #ifdef UNPACKED_MPQS struct AssetRef { static constexpr size_t PathBufSize = 4088; char path[PathBufSize]; [[nodiscard]] bool ok() const { return path[0] != '\0'; } // NOLINTNEXTLINE(readability-convert-member-functions-to-static) [[nodiscard]] const char *error() const { return "File not found"; } [[nodiscard]] size_t size() const { uintmax_t fileSize; if (!GetFileSize(path, &fileSize)) return 0; return fileSize; } }; struct AssetHandle { FILE *handle = nullptr; AssetHandle() = default; AssetHandle(FILE *handle) : handle(handle) { } AssetHandle(AssetHandle &&other) noexcept : handle(other.handle) { other.handle = nullptr; } AssetHandle &operator=(AssetHandle &&other) noexcept { handle = other.handle; other.handle = nullptr; return *this; } ~AssetHandle() { if (handle != nullptr) std::fclose(handle); } [[nodiscard]] bool ok() const { return handle != nullptr && std::ferror(handle) == 0; } bool read(void *buffer, size_t len) { return std::fread(buffer, len, 1, handle) == 1; } bool seek(long pos) { return std::fseek(handle, pos, SEEK_SET) == 0; } [[nodiscard]] const char *error() const { return std::strerror(errno); } }; #else struct AssetRef { // An MPQ file reference: MpqArchive *archive = nullptr; uint32_t fileNumber; std::string_view filename; // Alternatively, a direct SDL_RWops handle: SDL_RWops *directHandle = nullptr; AssetRef() = default; AssetRef(AssetRef &&other) noexcept : archive(other.archive) , fileNumber(other.fileNumber) , filename(other.filename) , directHandle(other.directHandle) { other.directHandle = nullptr; } AssetRef &operator=(AssetRef &&other) noexcept { if (directHandle != nullptr) SDL_RWclose(directHandle); archive = other.archive; fileNumber = other.fileNumber; filename = other.filename; directHandle = other.directHandle; other.directHandle = nullptr; return *this; } ~AssetRef() { if (directHandle != nullptr) SDL_RWclose(directHandle); } [[nodiscard]] bool ok() const { return directHandle != nullptr || archive != nullptr; } // NOLINTNEXTLINE(readability-convert-member-functions-to-static) [[nodiscard]] const char *error() const { return SDL_GetError(); } [[nodiscard]] size_t size() const { if (archive != nullptr) { int32_t error; return archive->GetUnpackedFileSize(fileNumber, error); } return SDL_RWsize(directHandle); } }; struct AssetHandle { SDL_RWops *handle = nullptr; AssetHandle() = default; explicit AssetHandle(SDL_RWops *handle) : handle(handle) { } AssetHandle(AssetHandle &&other) noexcept : handle(other.handle) { other.handle = nullptr; } AssetHandle &operator=(AssetHandle &&other) noexcept { if (handle != nullptr) { SDL_RWclose(handle); } handle = other.handle; other.handle = nullptr; return *this; } ~AssetHandle() { if (handle != nullptr) SDL_RWclose(handle); } [[nodiscard]] bool ok() const { return handle != nullptr; } bool read(void *buffer, size_t len) { return handle->read(handle, buffer, len, 1) == 1; } bool seek(long pos) { return handle->seek(handle, pos, RW_SEEK_SET) != -1; } [[nodiscard]] const char *error() const { return SDL_GetError(); } SDL_RWops *release() && { SDL_RWops *result = handle; handle = nullptr; return result; } }; #endif [[noreturn]] inline void FailedToOpenFileError(std::string_view path, std::string_view error) { app_fatal(StrCat("Failed to open file:\n", path, "\n\n", error)); } inline bool ValidatAssetRef(std::string_view path, const AssetRef &ref) { if (ref.ok()) return true; if (!HeadlessMode) { FailedToOpenFileError(path, ref.error()); } return false; } inline bool ValidateHandle(std::string_view path, const AssetHandle &handle) { if (handle.ok()) return true; if (!HeadlessMode) { FailedToOpenFileError(path, handle.error()); } return false; } AssetRef FindAsset(std::string_view filename); AssetHandle OpenAsset(AssetRef &&ref, bool threadsafe = false); AssetHandle OpenAsset(std::string_view filename, bool threadsafe = false); AssetHandle OpenAsset(std::string_view filename, size_t &fileSize, bool threadsafe = false); SDL_RWops *OpenAssetAsSdlRwOps(std::string_view filename, bool threadsafe = false); } // namespace devilution