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

#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