You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
3.1 KiB
145 lines
3.1 KiB
#include "mpq/mpq_reader.hpp" |
|
|
|
#include <cstring> |
|
#include <utility> |
|
|
|
#include <mpqfs/mpqfs.h> |
|
|
|
#include "utils/file_util.h" |
|
|
|
namespace devilution { |
|
|
|
// Helper: NUL-terminate a string_view into a stack buffer. |
|
// Returns false if the name doesn't fit. |
|
static bool CopyToPathBuf(std::string_view sv, char *buf, size_t bufSize) |
|
{ |
|
if (sv.size() >= bufSize) return false; |
|
std::memcpy(buf, sv.data(), sv.size()); |
|
buf[sv.size()] = '\0'; |
|
return true; |
|
} |
|
|
|
MpqArchive::MpqArchive(std::string path, mpqfs_archive_t *archive) |
|
: path_(std::move(path)) |
|
, archive_(archive) |
|
{ |
|
} |
|
|
|
MpqArchive::MpqArchive(MpqArchive &&other) noexcept |
|
: path_(std::move(other.path_)) |
|
, archive_(other.archive_) |
|
{ |
|
other.archive_ = nullptr; |
|
} |
|
|
|
MpqArchive &MpqArchive::operator=(MpqArchive &&other) noexcept |
|
{ |
|
if (this != &other) { |
|
mpqfs_close(archive_); |
|
path_ = std::move(other.path_); |
|
archive_ = other.archive_; |
|
other.archive_ = nullptr; |
|
} |
|
return *this; |
|
} |
|
|
|
MpqArchive::~MpqArchive() |
|
{ |
|
mpqfs_close(archive_); |
|
} |
|
|
|
std::optional<MpqArchive> MpqArchive::Open(const char *path, int32_t &error) |
|
{ |
|
if (!FileExists(path)) { |
|
error = 0; |
|
return std::nullopt; |
|
} |
|
mpqfs_archive_t *handle = mpqfs_open(path); |
|
if (!handle) { |
|
error = -1; |
|
return std::nullopt; |
|
} |
|
error = 0; |
|
return MpqArchive(path, handle); |
|
} |
|
|
|
std::optional<MpqArchive> MpqArchive::Clone(int32_t &error) |
|
{ |
|
mpqfs_archive_t *clone = mpqfs_clone(archive_); |
|
if (!clone) { |
|
error = -1; |
|
return std::nullopt; |
|
} |
|
error = 0; |
|
return MpqArchive(path_, clone); |
|
} |
|
|
|
const char *MpqArchive::ErrorMessage() |
|
{ |
|
const char *msg = mpqfs_last_error(); |
|
return msg ? msg : "Unknown error"; |
|
} |
|
|
|
bool MpqArchive::HasFile(std::string_view filename) const |
|
{ |
|
char buf[256]; |
|
if (!CopyToPathBuf(filename, buf, sizeof(buf))) |
|
return false; |
|
return mpqfs_has_file(archive_, buf); |
|
} |
|
|
|
size_t MpqArchive::GetFileSize(std::string_view filename) const |
|
{ |
|
char buf[256]; |
|
if (!CopyToPathBuf(filename, buf, sizeof(buf))) |
|
return 0; |
|
return mpqfs_file_size(archive_, buf); |
|
} |
|
|
|
uint32_t MpqArchive::FindHash(std::string_view filename) const |
|
{ |
|
char buf[256]; |
|
if (!CopyToPathBuf(filename, buf, sizeof(buf))) |
|
return UINT32_MAX; |
|
return mpqfs_find_hash(archive_, buf); |
|
} |
|
|
|
bool MpqArchive::HasFileHash(uint32_t hash) const |
|
{ |
|
return mpqfs_has_file_hash(archive_, hash); |
|
} |
|
|
|
size_t MpqArchive::GetFileSizeFromHash(uint32_t hash) const |
|
{ |
|
return mpqfs_file_size_from_hash(archive_, hash); |
|
} |
|
|
|
std::unique_ptr<std::byte[]> MpqArchive::ReadFile( |
|
std::string_view filename, std::size_t &fileSize, int32_t &error) |
|
{ |
|
char buf[256]; |
|
if (!CopyToPathBuf(filename, buf, sizeof(buf))) { |
|
error = -1; |
|
return nullptr; |
|
} |
|
|
|
const size_t size = mpqfs_file_size(archive_, buf); |
|
if (size == 0) { |
|
error = -1; |
|
return nullptr; |
|
} |
|
|
|
auto result = std::make_unique<std::byte[]>(size); |
|
const size_t read = mpqfs_read_file_into(archive_, buf, |
|
result.get(), size); |
|
if (read == 0) { |
|
error = -1; |
|
return nullptr; |
|
} |
|
|
|
error = 0; |
|
fileSize = read; |
|
return result; |
|
} |
|
|
|
} // namespace devilution
|
|
|