#include "engine/assets.hpp" #include #include #include #include "init.h" #include "mpq/mpq_sdl_rwops.hpp" #include "utils/file_util.h" #include "utils/log.hpp" #include "utils/paths.h" namespace devilution { namespace { bool OpenMpqFile(const char *filename, MpqArchive **archive, uint32_t *fileNumber) { const MpqArchive::FileHash fileHash = MpqArchive::CalculateFileHash(filename); const auto at = [=](std::optional &src) -> bool { if (src && src->GetFileNumber(fileHash, *fileNumber)) { *archive = &(*src); return true; } return false; }; return at(font_mpq) || at(lang_mpq) || at(devilutionx_mpq) || (gbIsHellfire && (at(hfvoice_mpq) || at(hfmusic_mpq) || at(hfbarb_mpq) || at(hfbard_mpq) || at(hfmonk_mpq) || at(hellfire_mpq))) || at(spawn_mpq) || at(diabdat_mpq); } } // namespace SDL_RWops *OpenAsset(const char *filename, bool threadsafe) { std::string relativePath = filename; #ifndef _WIN32 std::replace(relativePath.begin(), relativePath.end(), '\\', '/'); #endif if (relativePath[0] == '/') return SDL_RWFromFile(relativePath.c_str(), "rb"); // Files next to the MPQ archives override MPQ contents. SDL_RWops *rwops; if (paths::MpqDir()) { const std::string path = *paths::MpqDir() + relativePath; // Avoid spamming DEBUG messages if the file does not exist. if ((FileExists(path.c_str())) && (rwops = SDL_RWFromFile(path.c_str(), "rb")) != nullptr) { LogVerbose("Loaded MPQ file override: {}", path); return rwops; } } // Load from all the MPQ archives. MpqArchive *archive; uint32_t fileNumber; if (OpenMpqFile(filename, &archive, &fileNumber)) return SDL_RWops_FromMpqFile(*archive, fileNumber, filename, threadsafe); // Load from the `/assets` directory next to the devilutionx binary. const std::string path = paths::AssetsPath() + relativePath; if ((rwops = SDL_RWFromFile(path.c_str(), "rb")) != nullptr) return rwops; #ifdef __ANDROID__ // On Android, fall back to the APK's assets. // This is handled by SDL when we pass a relative path. if (!paths::AssetsPath().empty() && (rwops = SDL_RWFromFile(relativePath.c_str(), "rb"))) return rwops; #endif return nullptr; } } // namespace devilution