mirror of
https://github.com/doldecomp/melee.git
synced 2026-11-13 04:30:52 -10:00
151 lines
6.0 KiB
C
151 lines
6.0 KiB
C
/** @file Exercise the TARGET_PC game loader without validation callbacks. */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "fighters.h"
|
|
#include <melee/ft/types.h>
|
|
#include <sysdolphin/baselib/archive.h>
|
|
#include <sysdolphin/baselib/jobj.h>
|
|
|
|
#define CHECK(condition) \
|
|
do { \
|
|
if (!(condition)) { \
|
|
fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, #condition); \
|
|
abort(); \
|
|
} \
|
|
} while (0)
|
|
|
|
static uint32_t be32(const u8* p)
|
|
{
|
|
return (uint32_t) p[0] << 24 | (uint32_t) p[1] << 16 |
|
|
(uint32_t) p[2] << 8 | p[3];
|
|
}
|
|
|
|
static void put32(u8* p, uint32_t value)
|
|
{
|
|
p[0] = value >> 24;
|
|
p[1] = value >> 16;
|
|
p[2] = value >> 8;
|
|
p[3] = value;
|
|
}
|
|
|
|
static void test_dispatch(void)
|
|
{
|
|
u8 bytes[0x20 + 0x44 + 8 + 16 + 8 + 21] = { 0 };
|
|
put32(bytes, sizeof bytes);
|
|
put32(bytes + 4, 0x44);
|
|
put32(bytes + 8, 2);
|
|
put32(bytes + 12, 2);
|
|
put32(bytes + 16, 1);
|
|
u8* data = bytes + 0x20;
|
|
put32(data, UINT32_MAX); /* extern chain terminator */
|
|
put32(data + 0x14, 0x3FC00000); /* rotation.x = 1.5 */
|
|
put32(data + 0x44, 8); /* child points to offset zero */
|
|
put32(data + 0x48, 0x40); /* links points to offset zero */
|
|
put32(data + 0x54, 0x40); /* links public */
|
|
put32(data + 0x58, 6);
|
|
put32(data + 0x60, 12); /* external name */
|
|
memcpy(data + 0x64, "joint\0links\0external\0", 21);
|
|
struct {
|
|
uint64_t before;
|
|
HSD_Archive archive;
|
|
uint64_t after;
|
|
} guarded = { .before = 123, .after = 456 };
|
|
HSD_Archive* archive = &guarded.archive;
|
|
CHECK(HSD_ArchiveParse(archive, bytes, sizeof bytes) == 0);
|
|
CHECK(guarded.before == 123 && guarded.after == 456);
|
|
CHECK(archive->native_data != NULL);
|
|
CHECK(archive->data == data && archive->top_ptr == bytes);
|
|
CHECK(archive->header.data_size == 0x44);
|
|
CHECK(archive->public_info[1].offset == 0x40);
|
|
CHECK(strcmp(HSD_ArchiveGetExtern(archive, 0), "external") == 0);
|
|
CHECK(HSD_ArchiveGetExtern(archive, 1) == NULL);
|
|
char first[] = "first", second[] = "second";
|
|
HSD_ArchiveLocateExtern(archive, "external", first);
|
|
HSD_Joint* joint = HSD_ArchiveGetPublicAs(HSD_Joint, archive, "joint");
|
|
CHECK(joint && joint->child == joint && joint->rotation.x == 1.5f);
|
|
CHECK(joint->class_name == first);
|
|
HSD_Joint** links = HSD_ArchiveGetPublicAs(HSD_Joint*, archive, "links");
|
|
CHECK(links && *links == joint);
|
|
CHECK(HSD_ArchiveGetPublicAs(HSD_Joint, archive, "joint") == joint);
|
|
HSD_ArchiveLocateExtern(archive, "external", second);
|
|
CHECK(joint->class_name == second);
|
|
HSD_ArchiveLocateExtern(archive, "external", NULL);
|
|
CHECK(joint->class_name == NULL);
|
|
CHECK(HSD_ArchiveGetPublicAs(HSD_Joint, archive, "missing") == NULL);
|
|
CHECK(HSD_ArchiveGetPublicTyped(archive, "joint", "unknown") == NULL);
|
|
CHECK(be32(data) == UINT32_MAX); /* input stays serialized */
|
|
HSD_ArchiveReleaseNative(archive);
|
|
CHECK(archive->native_data == NULL && archive->top_ptr == NULL);
|
|
CHECK(archive->data == data); /* the caller still owns the input */
|
|
HSD_ArchiveReleaseNative(archive);
|
|
CHECK(HSD_ArchiveParse(archive, bytes, 8) == -1);
|
|
CHECK(guarded.before == 123 && guarded.after == 456);
|
|
HSD_ArchiveReleaseNative(archive);
|
|
|
|
/* A relocation view has no parsed native data. */
|
|
HSD_Archive view = { 0 };
|
|
CHECK(HSD_ArchiveGetPublicAs(HSD_Joint, &view, "joint") == NULL);
|
|
CHECK(HSD_ArchiveGetExtern(&view, 0) == NULL);
|
|
HSD_ArchiveLocateExtern(&view, "external", first);
|
|
HSD_ArchiveReleaseNative(&view);
|
|
}
|
|
|
|
static void test_fighters(const char* dir)
|
|
{
|
|
for (size_t i = 0; i < sizeof fighters / sizeof *fighters; i++) {
|
|
char path[2048];
|
|
snprintf(path, sizeof path, "%s/%s", dir, fighters[i].file);
|
|
FILE* file = fopen(path, "rb");
|
|
CHECK(file);
|
|
fseek(file, 0, SEEK_END);
|
|
size_t size = (size_t) ftell(file);
|
|
rewind(file);
|
|
u8* bytes = malloc(size);
|
|
CHECK(bytes && fread(bytes, 1, size, file) == size);
|
|
fclose(file);
|
|
HSD_Archive archive;
|
|
CHECK(HSD_ArchiveParse(&archive, bytes, size) == 0);
|
|
ftData* fighter =
|
|
HSD_ArchiveGetPublicAs(ftData, &archive, fighters[i].name);
|
|
CHECK(fighter && fighter->x0);
|
|
/* Read the original archive tables independently of the adapter. */
|
|
const u8* table = bytes + 0x20 + be32(bytes + 4) + 4 * be32(bytes + 8);
|
|
const char* names =
|
|
(const char*) table + 8 * (be32(bytes + 12) + be32(bytes + 16));
|
|
uint32_t root = UINT32_MAX;
|
|
for (uint32_t j = 0; j < be32(bytes + 12); j++) {
|
|
if (!strcmp(names + be32(table + 8 * j + 4), fighters[i].name)) {
|
|
root = be32(table + 8 * j);
|
|
}
|
|
}
|
|
CHECK(root != UINT32_MAX);
|
|
uint32_t bits = be32(bytes + 0x20 + be32(bytes + 0x20 + root));
|
|
float want;
|
|
memcpy(&want, &bits, sizeof want);
|
|
CHECK(fighter->x0->walk_accel_mul == want);
|
|
/* Mario's special attributes require the generated loader binding. */
|
|
if (!strcmp(fighters[i].name, "ftDataMario")) {
|
|
bits = be32(bytes + 0x20 + be32(bytes + 0x20 + root + 4));
|
|
memcpy(&want, &bits, sizeof want);
|
|
CHECK(fighter->ext_attr &&
|
|
fighter->ext_attr->mario.specials.vel_x_decay == want);
|
|
}
|
|
CHECK(HSD_ArchiveGetPublicAs(ftData, &archive, fighters[i].name) ==
|
|
fighter);
|
|
HSD_ArchiveReleaseNative(&archive);
|
|
free(bytes);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
CHECK(argc == 2);
|
|
test_dispatch();
|
|
test_fighters(argv[1]);
|
|
puts("ok: typed HSD dispatch, pointer tokens, cycles, externs and 27 "
|
|
"fighters");
|
|
return 0;
|
|
}
|