mirror of
https://github.com/AxioDL/metaforce.git
synced 2025-05-13 21:11:21 +00:00
Existing code was using the size argument for the number of elements to write and vice versa. No behavior change, given this still results in the same number of bytes being copied. This just makes corrects their usages.
282 lines
8.0 KiB
C++
282 lines
8.0 KiB
C++
#include <cstdint>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
#include <logvisor/logvisor.hpp>
|
|
#include "tinyxml2/tinyxml2.h"
|
|
|
|
#ifndef _WIN32
|
|
#include <cstdlib>
|
|
#include <sys/stat.h>
|
|
#include <sys/file.h>
|
|
#include <sys/ioctl.h>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/statvfs.h>
|
|
#include <cerrno>
|
|
#else
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN 1
|
|
#endif
|
|
#ifndef NOMINMAX
|
|
#define NOMINMAX
|
|
#endif
|
|
#include <Windows.h>
|
|
#include <cwchar>
|
|
#define strncasecmp _strnicmp
|
|
#define strcasecmp _stricmp
|
|
#if UNICODE
|
|
#define IS_UCS2 1
|
|
#endif
|
|
#endif
|
|
|
|
namespace {
|
|
logvisor::Module Log("AssetNameParser");
|
|
|
|
// TODO: Clean this up
|
|
#undef bswap16
|
|
#undef bswap32
|
|
#undef bswap64
|
|
|
|
/* Type-sensitive byte swappers */
|
|
template <typename T>
|
|
constexpr T bswap16(T val) {
|
|
#if __GNUC__
|
|
return __builtin_bswap16(val);
|
|
#elif _WIN32
|
|
return _byteswap_ushort(val);
|
|
#else
|
|
return (val = (val << 8) | ((val >> 8) & 0xFF));
|
|
#endif
|
|
}
|
|
|
|
template <typename T>
|
|
constexpr T bswap32(T val) {
|
|
#if __GNUC__
|
|
return __builtin_bswap32(val);
|
|
#elif _WIN32
|
|
return _byteswap_ulong(val);
|
|
#else
|
|
val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16;
|
|
val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8;
|
|
return val;
|
|
#endif
|
|
}
|
|
|
|
template <typename T>
|
|
constexpr T bswap64(T val) {
|
|
#if __GNUC__
|
|
return __builtin_bswap64(val);
|
|
#elif _WIN32
|
|
return _byteswap_uint64(val);
|
|
#else
|
|
return ((val & 0xFF00000000000000ULL) >> 56) | ((val & 0x00FF000000000000ULL) >> 40) |
|
|
((val & 0x0000FF0000000000ULL) >> 24) | ((val & 0x000000FF00000000ULL) >> 8) |
|
|
((val & 0x00000000FF000000ULL) << 8) | ((val & 0x0000000000FF0000ULL) << 24) |
|
|
((val & 0x000000000000FF00ULL) << 40) | ((val & 0x00000000000000FFULL) << 56);
|
|
#endif
|
|
}
|
|
|
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
constexpr uint32_t SBig(uint32_t val) { return bswap32(val); }
|
|
constexpr uint64_t SBig(uint64_t val) { return bswap64(val); }
|
|
#ifndef SBIG
|
|
#define SBIG(q) (((q)&0x000000FF) << 24 | ((q)&0x0000FF00) << 8 | ((q)&0x00FF0000) >> 8 | ((q)&0xFF000000) >> 24)
|
|
#endif
|
|
|
|
#ifndef SLITTLE
|
|
#define SLITTLE(q) (q)
|
|
#endif
|
|
#else
|
|
#ifndef SLITTLE
|
|
#define SLITTLE(q) (((q)&0x000000FF) << 24 | ((q)&0x0000FF00) << 8 | ((q)&0x00FF0000) >> 8 | ((q)&0xFF000000) >> 24)
|
|
#endif
|
|
|
|
constexpr uint32_t SBig(uint32_t val) { return val; }
|
|
constexpr uint64_t SBig(uint64_t val) { return val; }
|
|
#ifndef SBIG
|
|
#define SBIG(q) (q)
|
|
#endif
|
|
#endif
|
|
|
|
class FourCC {
|
|
protected:
|
|
union {
|
|
char fcc[4];
|
|
uint32_t num;
|
|
};
|
|
|
|
public:
|
|
FourCC() /* Sentinel FourCC */
|
|
: num(0) {}
|
|
FourCC(const FourCC& other) { num = other.num; }
|
|
FourCC(const char* name) : num(*(uint32_t*)name) {}
|
|
FourCC(uint32_t n) : num(n) {}
|
|
bool operator==(const FourCC& other) const { return num == other.num; }
|
|
bool operator!=(const FourCC& other) const { return num != other.num; }
|
|
bool operator==(const char* other) const { return num == *(uint32_t*)other; }
|
|
bool operator!=(const char* other) const { return num != *(uint32_t*)other; }
|
|
bool operator==(int32_t other) const { return num == other; }
|
|
bool operator!=(int32_t other) const { return num != other; }
|
|
bool operator==(uint32_t other) const { return num == other; }
|
|
bool operator!=(uint32_t other) const { return num != other; }
|
|
std::string toString() const { return std::string(fcc, 4); }
|
|
uint32_t toUint32() const { return num; }
|
|
operator uint32_t() const { return num; }
|
|
};
|
|
|
|
struct SAsset {
|
|
FourCC type;
|
|
uint64_t id;
|
|
std::string name;
|
|
std::string dir;
|
|
};
|
|
|
|
enum class FileLockType { None = 0, Read, Write };
|
|
|
|
#if IS_UCS2
|
|
using SystemChar = wchar_t;
|
|
using SystemString = std::wstring;
|
|
#ifndef _SYS_STR
|
|
#define _SYS_STR(val) L##val
|
|
#endif
|
|
using Sstat = struct _stat;
|
|
#else
|
|
using SystemChar = char;
|
|
using SystemString = std::string;
|
|
#ifndef _SYS_STR
|
|
#define _SYS_STR(val) val
|
|
#endif
|
|
using Sstat = struct stat;
|
|
#endif
|
|
|
|
struct FILEDeleter {
|
|
void operator()(FILE* file) const { std::fclose(file); }
|
|
};
|
|
using FILEPtr = std::unique_ptr<FILE, FILEDeleter>;
|
|
|
|
FILEPtr Fopen(const SystemChar* path, const SystemChar* mode, FileLockType lock = FileLockType::None) {
|
|
#if IS_UCS2
|
|
FILEPtr fp{_wfopen(path, mode)};
|
|
if (!fp) {
|
|
return nullptr;
|
|
}
|
|
#else
|
|
FILEPtr fp{std::fopen(path, mode)};
|
|
if (!fp) {
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
|
|
if (lock != FileLockType::None) {
|
|
#if _WIN32
|
|
OVERLAPPED ov = {};
|
|
LockFileEx((HANDLE)(uintptr_t)_fileno(fp.get()), (lock == FileLockType::Write) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0,
|
|
1, &ov);
|
|
#else
|
|
if (flock(fileno(fp.get()), ((lock == FileLockType::Write) ? LOCK_EX : LOCK_SH) | LOCK_NB)) {
|
|
std::fprintf(stderr, "flock %s: %s", path, strerror(errno));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return fp;
|
|
}
|
|
} // Anonymous namespace
|
|
|
|
#if _WIN32
|
|
int wmain(int argc, const wchar_t* argv[])
|
|
#else
|
|
int main(int argc, const char* argv[])
|
|
#endif
|
|
{
|
|
logvisor::RegisterStandardExceptions();
|
|
logvisor::RegisterConsoleLogger();
|
|
if (argc < 3) {
|
|
Log.report(logvisor::Error, FMT_STRING(_SYS_STR("Usage: {} <input> <output>")), argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
SystemString inPath = argv[1];
|
|
SystemString outPath = argv[2];
|
|
|
|
tinyxml2::XMLDocument doc;
|
|
std::vector<SAsset> assets;
|
|
FILEPtr docF = Fopen(inPath.c_str(), _SYS_STR("rb"));
|
|
if (doc.LoadFile(docF.get()) == tinyxml2::XML_SUCCESS) {
|
|
const tinyxml2::XMLElement* elm = doc.RootElement();
|
|
if (strcmp(elm->Name(), "AssetNameMap") != 0) {
|
|
Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Invalid database supplied")));
|
|
return 1;
|
|
}
|
|
|
|
elm = elm->FirstChildElement("AssetNameMap");
|
|
if (elm == nullptr) {
|
|
Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Malformed AssetName database")));
|
|
return 1;
|
|
}
|
|
|
|
elm = elm->FirstChildElement("Asset");
|
|
|
|
while (elm != nullptr ) {
|
|
const tinyxml2::XMLElement* keyElm = elm->FirstChildElement("Key");
|
|
const tinyxml2::XMLElement* valueElm = elm->FirstChildElement("Value");
|
|
|
|
if (keyElm == nullptr || valueElm == nullptr) {
|
|
Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Malformed Asset entry, [Key,Value] required")));
|
|
return 0;
|
|
}
|
|
|
|
const tinyxml2::XMLElement* nameElm = valueElm->FirstChildElement("Name");
|
|
const tinyxml2::XMLElement* dirElm = valueElm->FirstChildElement("Directory");
|
|
const tinyxml2::XMLElement* typeElm = valueElm->FirstChildElement("Type");
|
|
const tinyxml2::XMLElement* autoGenNameElm = valueElm->FirstChildElement("AutoGenName");
|
|
const tinyxml2::XMLElement* autoGenDirElm = valueElm->FirstChildElement("AutoGenDir");
|
|
|
|
if (nameElm == nullptr || dirElm == nullptr || typeElm == nullptr) {
|
|
Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Malformed Value entry, [Name,Directory,Type] required")));
|
|
return 0;
|
|
}
|
|
assets.emplace_back();
|
|
bool autoGen = strncasecmp(autoGenNameElm->GetText(), "true", 4) == 0 && strncasecmp(autoGenDirElm->GetText(), "true", 4) == 0;
|
|
if (!autoGen) {
|
|
SAsset& asset = assets.back();
|
|
asset.type = typeElm->GetText();
|
|
asset.id = strtoull(keyElm->GetText(), nullptr, 16);
|
|
asset.name = nameElm->GetText();
|
|
asset.dir = dirElm->GetText();
|
|
}
|
|
elm = elm->NextSiblingElement("Asset");
|
|
}
|
|
|
|
FILEPtr f = Fopen(outPath.c_str(), _SYS_STR("wb"));
|
|
if (f == nullptr) {
|
|
Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Unable to open destination")));
|
|
return 0;
|
|
}
|
|
|
|
uint32_t assetCount = SBig(uint32_t(assets.size()));
|
|
FourCC sentinel(SBIG('AIDM'));
|
|
fwrite(&sentinel, sizeof(sentinel), 1, f.get());
|
|
fwrite(&assetCount, sizeof(assetCount), 1, f.get());
|
|
for (const SAsset& asset : assets) {
|
|
fwrite(&asset.type, sizeof(asset.type), 1, f.get());
|
|
uint64_t id = SBig(asset.id);
|
|
fwrite(&id, sizeof(id), 1, f.get());
|
|
uint32_t tmp = SBig(uint32_t(asset.name.length()));
|
|
fwrite(&tmp, sizeof(tmp), 1, f.get());
|
|
fwrite(asset.name.c_str(), 1, SBig(tmp), f.get());
|
|
tmp = SBig(uint32_t(asset.dir.length()));
|
|
fwrite(&tmp, sizeof(tmp), 1, f.get());
|
|
fwrite(asset.dir.c_str(), SBig(tmp), 1, f.get());
|
|
}
|
|
fflush(f.get());
|
|
return 0;
|
|
}
|
|
|
|
Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("failed to load")));
|
|
return 1;
|
|
}
|