Complete remaining client gap features
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
#include "attribute_data.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
int failures = 0;
|
||||
|
||||
#define CHECK(condition, message) \
|
||||
do { \
|
||||
if (!(condition)) { \
|
||||
std::fprintf(stderr, "FAIL: %s (%s:%d)\n", (message), __FILE__, \
|
||||
__LINE__); \
|
||||
++failures; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
template <typename T>
|
||||
void append(std::vector<std::uint8_t> &bytes, const T &value) {
|
||||
const auto *p = reinterpret_cast<const std::uint8_t *>(&value);
|
||||
bytes.insert(bytes.end(), p, p + sizeof(T));
|
||||
}
|
||||
|
||||
void append_name(std::vector<std::uint8_t> &bytes, const char *name) {
|
||||
char fixed[32] = {};
|
||||
std::strncpy(fixed, name, sizeof(fixed));
|
||||
bytes.insert(bytes.end(), reinterpret_cast<std::uint8_t *>(fixed),
|
||||
reinterpret_cast<std::uint8_t *>(fixed) + sizeof(fixed));
|
||||
}
|
||||
|
||||
void append_collision(std::vector<std::uint8_t> &bytes, std::uint32_t type,
|
||||
const char *name, const float *position,
|
||||
const float *dimensions, const float *quaternion,
|
||||
std::size_t dimension_count) {
|
||||
append(bytes, type);
|
||||
append_name(bytes, name);
|
||||
for (std::size_t i = 0; i < 3; ++i)
|
||||
append(bytes, position[i]);
|
||||
for (std::size_t i = 0; i < dimension_count; ++i)
|
||||
append(bytes, dimensions[i]);
|
||||
for (std::size_t i = 0; i < 4; ++i)
|
||||
append(bytes, quaternion[i]);
|
||||
}
|
||||
|
||||
void test_synthetic() {
|
||||
std::vector<std::uint8_t> bytes;
|
||||
const char header[] = "AttributeData";
|
||||
bytes.insert(bytes.end(), header, header + sizeof(header));
|
||||
const std::uint32_t collisions = 3;
|
||||
const std::uint32_t heights = 1;
|
||||
append(bytes, collisions);
|
||||
append(bytes, heights);
|
||||
|
||||
const float p0[] = {10, 20, 30}, d0[] = {40, 50}, q0[] = {0, 0, 0, 1};
|
||||
append_collision(bytes, 0, "plane", p0, d0, q0, 2);
|
||||
const float p1[] = {-1, 2, -3}, d1[] = {7}, q1[] = {1, 2, 3, 4};
|
||||
append_collision(bytes, 2, "sphere", p1, d1, q1, 1);
|
||||
const float p2[] = {4, 5, 6}, d2[] = {8, 9, 10}, q2[] = {0, 0.5f, 0, 0.5f};
|
||||
append_collision(bytes, 5, "obb", p2, d2, q2, 3);
|
||||
|
||||
append_name(bytes, "height0");
|
||||
const std::uint32_t vertex_count = 3;
|
||||
append(bytes, vertex_count);
|
||||
const float vertices[] = {0, 0, 0, 100, 0, 0, 0, 100, 0};
|
||||
for (float value : vertices)
|
||||
append(bytes, value);
|
||||
|
||||
fmt::AttributeData parsed;
|
||||
std::string error;
|
||||
CHECK(fmt::parse_attribute_data(bytes.data(), bytes.size(), parsed, &error),
|
||||
"synthetic .mdatr parses");
|
||||
CHECK(parsed.collisions.size() == 3 && parsed.heights.size() == 1,
|
||||
"synthetic counts");
|
||||
CHECK(parsed.collisions[0].name == "plane" &&
|
||||
parsed.collisions[0].dimensions[1] == 50.0f,
|
||||
"plane fields");
|
||||
CHECK(parsed.collisions[1].type == 2 &&
|
||||
parsed.collisions[1].dimensions[0] == 7.0f &&
|
||||
parsed.collisions[1].quaternion[0] == 1.0f,
|
||||
"sphere fields");
|
||||
CHECK(parsed.collisions[2].name == "obb" &&
|
||||
parsed.collisions[2].dimensions[2] == 10.0f,
|
||||
"obb fields");
|
||||
CHECK(parsed.heights[0].name == "height0" &&
|
||||
parsed.heights[0].vertices.size() == 3 &&
|
||||
parsed.heights[0].vertices[1][0] == 100.0f,
|
||||
"height triangle fields");
|
||||
|
||||
auto truncated = bytes;
|
||||
truncated.pop_back();
|
||||
CHECK(!fmt::parse_attribute_data(truncated.data(), truncated.size(), parsed, &error),
|
||||
"truncated .mdatr rejected");
|
||||
auto trailing = bytes;
|
||||
trailing.push_back(0);
|
||||
CHECK(!fmt::parse_attribute_data(trailing.data(), trailing.size(), parsed, &error),
|
||||
"trailing .mdatr rejected");
|
||||
}
|
||||
|
||||
void test_real_asset() {
|
||||
const char *root = std::getenv("M2_ASSETS");
|
||||
if (!root || !*root)
|
||||
return;
|
||||
const std::string path = std::string(root) +
|
||||
"/metin2_patch_eu3/ymir work/zone/devilcave/devil_entrance1f.mdatr";
|
||||
fmt::AttributeData parsed;
|
||||
std::string error;
|
||||
CHECK(fmt::parse_attribute_data_file(path, parsed, &error),
|
||||
"real devil_entrance1f.mdatr parses");
|
||||
CHECK(parsed.collisions.size() == 9 && parsed.heights.empty(),
|
||||
"real .mdatr collision/height count");
|
||||
CHECK(parsed.collisions[0].type == 0 && parsed.collisions[0].name == "collision04",
|
||||
"real .mdatr first collision");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
test_synthetic();
|
||||
test_real_asset();
|
||||
if (failures == 0) {
|
||||
std::puts("PASS: formats_attribute_data_test");
|
||||
return 0;
|
||||
}
|
||||
std::fprintf(stderr, "%d check(s) failed\n", failures);
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user