#pragma once #include #include #include #include #include #include #include "appfat.h" #include "engine/assets.hpp" #include "headless_mode.hpp" #include "mpq/mpq_common.hpp" #include "utils/static_vector.hpp" #include "utils/str_cat.hpp" namespace devilution { template tl::expected LoadFileInMemWithStatus(const char *path, T *data) { size_t size; AssetHandle handle = OpenAsset(path, size); if (!handle.ok()) { if (HeadlessMode) return {}; return tl::make_unexpected(FailedToOpenFileErrorMessage(path, handle.error())); } if ((size % sizeof(T)) != 0) { return tl::make_unexpected(StrCat("File size does not align with type\n", path)); } if (!handle.read(data, size)) { return tl::make_unexpected("handle.read failed"); } return {}; } template void LoadFileInMem(const char *path, T *data) { const tl::expected result = LoadFileInMemWithStatus(path, data); if (!result.has_value()) app_fatal(result.error()); } template tl::expected LoadFileInMemWithStatus(const char *path, T *data, std::size_t count) { AssetHandle handle = OpenAsset(path); if (!handle.ok()) { if (HeadlessMode) return {}; return tl::make_unexpected(FailedToOpenFileErrorMessage(path, handle.error())); } if (!handle.read(data, count * sizeof(T))) { return tl::make_unexpected("handle.read failed"); } return {}; } template void LoadFileInMem(const char *path, T *data, std::size_t count) { tl::expected result = LoadFileInMemWithStatus(path, data, count); if (!result.has_value()) app_fatal(result.error()); } template bool LoadOptionalFileInMem(const char *path, T *data, std::size_t count) { AssetHandle handle = OpenAsset(path); return handle.ok() && handle.read(data, count * sizeof(T)); } template tl::expected LoadFileInMemWithStatus(const char *path, std::array &data) { return LoadFileInMemWithStatus(path, data.data(), N); } template void LoadFileInMem(const char *path, std::array &data) { LoadFileInMem(path, data.data(), N); } template tl::expected, std::string> LoadFileInMemWithStatus(const char *path, std::size_t *numRead = nullptr) { size_t size; AssetHandle handle = OpenAsset(path, size); if (!handle.ok()) { if (HeadlessMode) return {}; return tl::make_unexpected(FailedToOpenFileErrorMessage(path, handle.error())); } if ((size % sizeof(T)) != 0) { return tl::make_unexpected(StrCat("File size does not align with type\n", path)); } if (numRead != nullptr) *numRead = size / sizeof(T); std::unique_ptr buf { new T[size / sizeof(T)] }; if (!handle.read(buf.get(), size)) { return tl::make_unexpected("handle.read failed"); } return { std::move(buf) }; } /** * @brief Load a file in to a buffer * @param path Path of file * @param numRead Number of T elements read * @return Buffer with content of file */ template std::unique_ptr LoadFileInMem(const char *path, std::size_t *numRead = nullptr) { tl::expected, std::string> result = LoadFileInMemWithStatus(path, numRead); if (!result.has_value()) app_fatal(result.error()); return std::move(result).value(); } /** * @brief Reads multiple files into a single buffer * * @tparam MaxFiles maximum number of files */ template struct MultiFileLoader { struct DefaultFilterFn { bool operator()(size_t i) const { return true; } }; /** * @param numFiles number of files to read * @param pathFn a function that returns the path for the given index * @param outOffsets a buffer index for the start of each file will be written here, then the total file size at the end. * @param filterFn a function that returns whether to load a file for the given index * @return std::unique_ptr the buffer with all the files */ template [[nodiscard]] std::unique_ptr operator()(size_t numFiles, PathFn &&pathFn, uint32_t *outOffsets, FilterFn filterFn = DefaultFilterFn {}) { StaticVector, MaxFiles> paths; StaticVector files; StaticVector sizes; size_t totalSize = 0; for (size_t i = 0, j = 0; i < numFiles; ++i) { if (!filterFn(i)) continue; { const char *path = pathFn(i); paths.emplace_back(); memcpy(paths.back().data(), path, strlen(path) + 1); } const char *path = paths.back().data(); files.emplace_back(FindAsset(path)); if (!ValidatAssetRef(path, files.back())) return nullptr; const size_t size = files.back().size(); sizes.emplace_back(static_cast(size)); outOffsets[j] = static_cast(totalSize); totalSize += size; ++j; } outOffsets[files.size()] = static_cast(totalSize); std::unique_ptr buf { new std::byte[totalSize] }; for (size_t i = 0, j = 0; i < numFiles; ++i) { if (!filterFn(i)) continue; AssetHandle handle = OpenAsset(std::move(files[j])); if (!handle.ok() || !handle.read(&buf[outOffsets[j]], sizes[j])) { FailedToOpenFileError(paths[j].data(), handle.error()); } ++j; } return buf; } }; } // namespace devilution