mirror of
https://github.com/doldecomp/melee.git
synced 2026-11-10 17:10:37 -10:00
668 lines
21 KiB
C
668 lines
21 KiB
C
/**
|
|
* @file
|
|
* Generated readers for independent layouts, over an archive built here: each
|
|
* kind of field converted, pointers followed and shared, unions chosen by
|
|
* `DAT_IF`, and malformed archives refused.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <dat/test.h>
|
|
|
|
static int failures;
|
|
|
|
#define CHECK(cond) \
|
|
do { \
|
|
if (!(cond)) { \
|
|
printf("%s:%d: failed: %s\n", __FILE__, __LINE__, #cond); \
|
|
failures++; \
|
|
} \
|
|
} while (0)
|
|
|
|
/* --- The native types -----------------------------------------------------
|
|
*/
|
|
|
|
typedef struct Leaf {
|
|
int8_t a;
|
|
int16_t b;
|
|
int32_t c;
|
|
float f;
|
|
} Leaf;
|
|
|
|
union Choice {
|
|
Leaf* leaf;
|
|
struct Node* node;
|
|
};
|
|
|
|
typedef struct Node {
|
|
struct Node* next;
|
|
Leaf* leaf;
|
|
Leaf* many;
|
|
uint32_t n;
|
|
uint8_t* raw;
|
|
int32_t kind;
|
|
union Choice u;
|
|
unsigned flag : 3;
|
|
unsigned other : 5;
|
|
} Node;
|
|
|
|
typedef struct CountedLists {
|
|
uint32_t n;
|
|
Leaf* (*rows)[2];
|
|
} CountedLists;
|
|
|
|
typedef struct InlineLists {
|
|
uint32_t n;
|
|
Leaf* rows[2];
|
|
} InlineLists;
|
|
|
|
/* --- Their type indices and native declarations --------------------------
|
|
*/
|
|
|
|
enum {
|
|
T_S8 = 1,
|
|
T_S16,
|
|
T_S32,
|
|
T_F32,
|
|
T_LEAF,
|
|
T_LEAF_P,
|
|
T_NODE,
|
|
T_NODE_P,
|
|
T_U32,
|
|
T_U8,
|
|
T_U8_P,
|
|
T_CHOICE,
|
|
T_UINT,
|
|
T_INLINE_LEAVES,
|
|
T_INLINE_POINTERS,
|
|
T_FIXED_POINTERS,
|
|
T_LEAF_ARRAY,
|
|
T_POINTER_ARRAY,
|
|
T_FIXED_ARRAY,
|
|
T_COUNTED_LEAF_P,
|
|
T_COUNTED_ROW,
|
|
T_COUNTED_ROW_P,
|
|
T_COUNTED_LISTS,
|
|
T_INLINE_LISTS,
|
|
T_SCRIPT,
|
|
T_SCRIPT_ROW,
|
|
T_COUNT,
|
|
};
|
|
|
|
typedef struct InlineLeaves {
|
|
int32_t n;
|
|
Leaf entries[];
|
|
} InlineLeaves;
|
|
typedef struct InlinePointers {
|
|
int32_t n;
|
|
Leaf* entries[];
|
|
} InlinePointers;
|
|
typedef struct FixedPointers {
|
|
int32_t n;
|
|
Leaf* entries[2];
|
|
} FixedPointers;
|
|
union PlainChoice {
|
|
uint32_t small;
|
|
Leaf large;
|
|
};
|
|
#include "unit.h"
|
|
|
|
/* --- An archive -----------------------------------------------------------
|
|
*/
|
|
|
|
static void put32(unsigned char* p, uint32_t v)
|
|
{
|
|
p[0] = (unsigned char) (v >> 24);
|
|
p[1] = (unsigned char) (v >> 16);
|
|
p[2] = (unsigned char) (v >> 8);
|
|
p[3] = (unsigned char) v;
|
|
}
|
|
|
|
static void put16(unsigned char* p, uint16_t v)
|
|
{
|
|
p[0] = (unsigned char) (v >> 8);
|
|
p[1] = (unsigned char) v;
|
|
}
|
|
|
|
enum {
|
|
DATA = 0x80
|
|
};
|
|
|
|
static const uint32_t relocs[] = { 0x00, 0x04, 0x08, 0x10, 0x18, 0x20, 0x38 };
|
|
|
|
/// Node A at 0 (kind 0, its union a leaf), node B at 0x20 (kind 1, its
|
|
/// union node A), two leaves at 0x40, raw bytes at 0x70. B's raw pointer is
|
|
/// an extern.
|
|
static size_t build(unsigned char* f)
|
|
{
|
|
unsigned char* d = f + 0x20;
|
|
memset(f, 0, 0x200);
|
|
/* A */
|
|
put32(d + 0x00, 0x20);
|
|
put32(d + 0x04, 0x40);
|
|
put32(d + 0x08, 0x40);
|
|
put32(d + 0x0C, 2);
|
|
put32(d + 0x10, 0x70);
|
|
put32(d + 0x14, 0);
|
|
put32(d + 0x18, 0x40);
|
|
d[0x1C] = 5 << 5 | 17;
|
|
/* B */
|
|
put32(d + 0x20, 0x00);
|
|
put32(d + 0x24, 0xFFFFFFFF);
|
|
put32(d + 0x30, 0xFFFFFFFF);
|
|
put32(d + 0x34, 1);
|
|
put32(d + 0x38, 0x00);
|
|
/* Leaves */
|
|
d[0x40] = 0xFD;
|
|
put16(d + 0x42, 0xFFFE);
|
|
put32(d + 0x44, 0x12345678);
|
|
put32(d + 0x48, 0x3FC00000);
|
|
d[0x4C] = 7;
|
|
put16(d + 0x4E, 300);
|
|
put32(d + 0x50, 0xFFFFFFFF);
|
|
put32(d + 0x54, 0xC0000000);
|
|
memcpy(d + 0x70, "HELLO", 6);
|
|
/* Tables */
|
|
unsigned char* at = d + DATA;
|
|
for (size_t i = 0; i < sizeof relocs / sizeof *relocs; i++, at += 4) {
|
|
put32(at, relocs[i]);
|
|
}
|
|
/* One public, "root" at 0; one extern, "ext" at 0x30 */
|
|
put32(at, 0), put32(at + 4, 0), at += 8;
|
|
put32(at, 0x30), put32(at + 4, 5), at += 8;
|
|
memcpy(at, "root\0ext\0", 9);
|
|
at += 9;
|
|
size_t size = (size_t) (at - f);
|
|
put32(f, (uint32_t) size);
|
|
put32(f + 4, DATA);
|
|
put32(f + 8, sizeof relocs / sizeof *relocs);
|
|
put32(f + 12, 1);
|
|
put32(f + 16, 1);
|
|
return size;
|
|
}
|
|
|
|
static bool contains(const char* text, const char* line)
|
|
{
|
|
return strstr(text, line) != NULL;
|
|
}
|
|
|
|
static void test_walk(void)
|
|
{
|
|
unsigned char file[0x200];
|
|
size_t size = build(file);
|
|
const char* error = NULL;
|
|
DatArchive* a = dat_open(&schema, file, size, &error);
|
|
CHECK(a != NULL);
|
|
if (a == NULL) {
|
|
printf("dat_open: %s\n", error);
|
|
return;
|
|
}
|
|
Node* root = dat_public(a, "root", T_NODE);
|
|
CHECK(root != NULL);
|
|
if (root == NULL) {
|
|
return;
|
|
}
|
|
Node* b = root->next;
|
|
CHECK(b != NULL && b->next == root);
|
|
CHECK(root->kind == 0 && b->kind == 1);
|
|
/* A union chosen by its record's field */
|
|
CHECK(root->u.leaf == root->leaf);
|
|
CHECK(b->u.node == root);
|
|
/* Scalars sign-extended, floats by their bits */
|
|
CHECK(root->leaf->a == -3);
|
|
CHECK(root->leaf->b == -2);
|
|
CHECK(root->leaf->c == 0x12345678);
|
|
CHECK(root->leaf->f == 1.5f);
|
|
/* A counted array, its first element also an object of its own */
|
|
CHECK(root->n == 2);
|
|
CHECK(root->many != NULL);
|
|
CHECK(root->leaf == root->many);
|
|
if (root->many != NULL) {
|
|
CHECK(root->many[0].c == 0x12345678 && root->many[0].b == -2);
|
|
CHECK(root->many[1].a == 7 && root->many[1].b == 300);
|
|
CHECK(root->many[1].c == -1 && root->many[1].f == -2.0f);
|
|
}
|
|
/* Raw bytes stay where they are */
|
|
CHECK(root->raw == dat_raw(a, 0x70));
|
|
CHECK(root->raw != NULL && memcmp(root->raw, "HELLO", 6) == 0);
|
|
/* -1 stays -1; externs are null until linked */
|
|
CHECK(b->leaf == (Leaf*) (intptr_t) -1);
|
|
CHECK(b->raw == NULL);
|
|
CHECK(b->many == NULL && b->n == 0);
|
|
/* Bitfields, counted from the most significant bit */
|
|
CHECK(root->flag == 5 && root->other == 17);
|
|
CHECK(b->flag == 0 && b->other == 0);
|
|
CHECK(dat_verify(a, stdout) == 0);
|
|
/* A relocated pointer to a converted object cannot become NULL. */
|
|
Leaf* saved_leaf = root->leaf;
|
|
root->leaf = NULL;
|
|
CHECK(dat_verify(a, NULL) == 1);
|
|
root->leaf = saved_leaf;
|
|
CHECK(dat_verify(a, NULL) == 0);
|
|
|
|
FILE* out = tmpfile();
|
|
dat_trace(a, out, DAT_TRACE_ALL);
|
|
long n = ftell(out);
|
|
rewind(out);
|
|
char* text = calloc(1, (size_t) n + 1);
|
|
if (fread(text, 1, (size_t) n, out) != (size_t) n) {
|
|
CHECK(false);
|
|
}
|
|
fclose(out);
|
|
CHECK(contains(text, "object 0x0 7 Node\n"));
|
|
CHECK(contains(text, "object 0x20 7 Node\n"));
|
|
CHECK(contains(text, "object 0x40 5 Leaf\n"));
|
|
CHECK(contains(text, "object 0x70 10 u8\n"));
|
|
CHECK(contains(text, "choice 0x18 12 0\n"));
|
|
CHECK(contains(text, "choice 0x38 12 1\n"));
|
|
CHECK(contains(text, "pointer 0x30\n"));
|
|
CHECK(contains(text, "extent 0x40 0x58\n"));
|
|
CHECK(contains(text, "sentinels 1\n"));
|
|
CHECK(!contains(text, "issue"));
|
|
free(text);
|
|
dat_close(a);
|
|
}
|
|
|
|
static size_t build_inline(unsigned char* f, uint32_t count, bool pointers)
|
|
{
|
|
memset(f, 0, 0x200);
|
|
unsigned char* d = f + 0x20;
|
|
put32(d, count);
|
|
if (pointers) {
|
|
put32(d + 4, 0x40);
|
|
put32(d + 8, 0x4C);
|
|
/* Relocated data past the count must stay unwalked. */
|
|
put32(d + 12, 0x40);
|
|
}
|
|
unsigned char* leaves = d + (pointers ? 0x40 : 4);
|
|
leaves[0] = (unsigned char) -3;
|
|
put16(leaves + 2, (uint16_t) -2);
|
|
put32(leaves + 4, 123);
|
|
put32(leaves + 8, 0x3FC00000);
|
|
leaves[12] = 7;
|
|
put16(leaves + 14, 300);
|
|
put32(leaves + 16, 456);
|
|
put32(leaves + 20, 0xC0000000);
|
|
unsigned char* at = d + DATA;
|
|
if (pointers) {
|
|
put32(at, 4), put32(at + 4, 8), put32(at + 8, 12), at += 12;
|
|
}
|
|
put32(at, 0), put32(at + 4, 0), at += 8;
|
|
/* A public inside the array must not shorten its count. */
|
|
put32(at, pointers ? 8 : 16), put32(at + 4, 5), at += 8;
|
|
memcpy(at, "root\0interior\0", 14);
|
|
at += 14;
|
|
size_t size = (size_t) (at - f);
|
|
put32(f, (uint32_t) size);
|
|
put32(f + 4, DATA);
|
|
put32(f + 8, pointers ? 3 : 0);
|
|
put32(f + 12, 2);
|
|
return size;
|
|
}
|
|
|
|
static char* trace(DatArchive* a)
|
|
{
|
|
FILE* out = tmpfile();
|
|
dat_trace(a, out, DAT_TRACE_ALL);
|
|
long n = ftell(out);
|
|
rewind(out);
|
|
char* text = calloc(1, (size_t) n + 1);
|
|
CHECK(fread(text, 1, (size_t) n, out) == (size_t) n);
|
|
fclose(out);
|
|
return text;
|
|
}
|
|
|
|
static void test_inline_counts(void)
|
|
{
|
|
unsigned char file[0x200];
|
|
size_t size = build_inline(file, 2, false);
|
|
DatArchive* a = dat_open(&schema, file, size, NULL);
|
|
CHECK(a != NULL);
|
|
InlineLeaves* leaves = dat_public(a, "root", T_INLINE_LEAVES);
|
|
CHECK(leaves != NULL && leaves->n == 2);
|
|
if (leaves != NULL) {
|
|
CHECK(leaves->entries[0].b == -2 && leaves->entries[0].c == 123);
|
|
CHECK(leaves->entries[1].b == 300 && leaves->entries[1].c == 456);
|
|
CHECK(leaves->entries[1].f == -2.0f);
|
|
/* Verification must include the flexible array's last element. */
|
|
CHECK(dat_verify(a, NULL) == 0);
|
|
leaves->entries[1].c++;
|
|
CHECK(dat_verify(a, NULL) == 1);
|
|
leaves->entries[1].c--;
|
|
}
|
|
dat_close(a);
|
|
|
|
for (unsigned i = 0; i < 6; i++) {
|
|
static const uint32_t counts[] = { 2, 0, 32, UINT32_MAX, 1, 3 };
|
|
bool fixed = i >= 4;
|
|
uint32_t count = counts[i];
|
|
size = build_inline(file, count, true);
|
|
a = dat_open(&schema, file, size, NULL);
|
|
CHECK(a != NULL);
|
|
InlinePointers* pointers = dat_public(
|
|
a, "root", fixed ? T_FIXED_POINTERS : T_INLINE_POINTERS);
|
|
CHECK(pointers != NULL && pointers->n == (int32_t) count);
|
|
char* text = trace(a);
|
|
bool invalid = i == 2 || i == 3 || i == 5;
|
|
if (invalid) {
|
|
CHECK(contains(text, "issue out-of-bounds 0x4"));
|
|
CHECK(!contains(text, "pointer 0x4\n"));
|
|
} else {
|
|
CHECK(!contains(text, "issue"));
|
|
if (count > 0 && pointers != NULL) {
|
|
CHECK(pointers->entries[0]->c == 123);
|
|
CHECK(contains(text, "pointer 0x4\n"));
|
|
if (count == 2) {
|
|
CHECK(pointers->entries[1]->c == 456);
|
|
CHECK(contains(text, "pointer 0x8\n"));
|
|
} else {
|
|
CHECK(!contains(text, "pointer 0x8\n"));
|
|
}
|
|
} else {
|
|
CHECK(!contains(text, "pointer 0x"));
|
|
}
|
|
}
|
|
CHECK(!contains(text, "pointer 0xC\n"));
|
|
CHECK(dat_verify(a, NULL) == 0);
|
|
free(text);
|
|
dat_close(a);
|
|
}
|
|
}
|
|
|
|
static void test_array_typedef_counts(void)
|
|
{
|
|
for (unsigned i = 0; i < 4; i++) {
|
|
unsigned char file[0x200];
|
|
uint32_t count = i < 2 ? 2 : 0;
|
|
bool inline_row = i % 2 != 0;
|
|
size_t size = build_inline(file, count, true);
|
|
unsigned char* d = file + 0x20;
|
|
/* Two lists of two leaves; the last leaf of each must be read. */
|
|
memcpy(d + 0x58, d + 0x40, 24);
|
|
put32(d + 0x5C, 111);
|
|
put32(d + 0x68, 222);
|
|
if (!inline_row) {
|
|
put32(d + 4, 16);
|
|
put32(d + 8, 0);
|
|
put32(d + 12, 0);
|
|
put32(d + 16, 0x40);
|
|
put32(d + 20, 0x58);
|
|
put32(d + DATA + 4, 16);
|
|
put32(d + DATA + 8, 20);
|
|
} else {
|
|
put32(d + 8, 0x58);
|
|
}
|
|
DatArchive* a = dat_open(&schema, file, size, NULL);
|
|
CHECK(a != NULL);
|
|
Leaf** row = NULL;
|
|
if (!inline_row) {
|
|
CountedLists* lists = dat_public(a, "root", T_COUNTED_LISTS);
|
|
CHECK(lists != NULL && lists->rows != NULL);
|
|
if (lists != NULL && lists->rows != NULL) {
|
|
row = lists->rows[0];
|
|
}
|
|
} else {
|
|
InlineLists* lists = dat_public(a, "root", T_INLINE_LISTS);
|
|
CHECK(lists != NULL);
|
|
if (lists != NULL) {
|
|
row = lists->rows;
|
|
}
|
|
}
|
|
CHECK(row != NULL && row[0] != NULL && row[1] != NULL);
|
|
char* text = trace(a);
|
|
CHECK(!contains(text, "issue"));
|
|
CHECK(contains(text, "extent 0x40 0x58") == (count > 0));
|
|
CHECK(contains(text, "extent 0x58 0x70") == (count > 0));
|
|
if (count > 0 && row != NULL && row[0] != NULL && row[1] != NULL) {
|
|
/* An incomplete list should fail without an out-of-bounds
|
|
access, so guard its last element with the traced extent. */
|
|
if (contains(text, "extent 0x40 0x58") &&
|
|
contains(text, "extent 0x58 0x70"))
|
|
{
|
|
CHECK(row[0][1].c == 456);
|
|
CHECK(row[1][1].c == 222);
|
|
}
|
|
}
|
|
CHECK(dat_verify(a, NULL) == 0);
|
|
free(text);
|
|
dat_close(a);
|
|
}
|
|
}
|
|
|
|
/// Test a 0x7F argument, a two-argument command, and an unterminated script.
|
|
static void test_byte_scripts(void)
|
|
{
|
|
static const unsigned char scripts[] = {
|
|
0x80, 0x7F, 0x7F, 0, 0x01, 0xC0, 0x01, 0x02, 0x7F, 0, 0, 0, 0x01, 0x02,
|
|
};
|
|
unsigned char file[0x200];
|
|
memset(file, 0, sizeof file);
|
|
unsigned char* d = file + 0x20;
|
|
put32(d, 12), put32(d + 4, 16), put32(d + 8, 24);
|
|
memcpy(d + 12, scripts, sizeof scripts);
|
|
uint32_t data = 12 + sizeof scripts;
|
|
unsigned char* at = d + data;
|
|
put32(at, 0), put32(at + 4, 4), put32(at + 8, 8), at += 12;
|
|
put32(at, 0), put32(at + 4, 0), at += 8;
|
|
memcpy(at, "root", 5);
|
|
at += 5;
|
|
size_t size = (size_t) (at - file);
|
|
put32(file, (uint32_t) size);
|
|
put32(file + 4, data);
|
|
put32(file + 8, 3);
|
|
put32(file + 12, 1);
|
|
DatArchive* a = dat_open(&schema, file, size, NULL);
|
|
CHECK(a != NULL);
|
|
if (a == NULL) {
|
|
return;
|
|
}
|
|
uint8_t** row = dat_public(a, "root", T_SCRIPT_ROW);
|
|
CHECK(row != NULL);
|
|
if (row != NULL) {
|
|
/* Script pointers refer to unchanged archive bytes. */
|
|
CHECK(row[0] == dat_raw(a, 12) && row[2] == dat_raw(a, 24));
|
|
}
|
|
char* text = trace(a);
|
|
CHECK(contains(text, "extent 0xC 0xF\n"));
|
|
CHECK(contains(text, "extent 0x10 0x15\n"));
|
|
CHECK(!contains(text, "extent 0x18"));
|
|
CHECK(contains(text, "issue out-of-bounds 0x1A"));
|
|
CHECK(dat_verify(a, NULL) == 0);
|
|
free(text);
|
|
dat_close(a);
|
|
}
|
|
|
|
static void test_refuses(void)
|
|
{
|
|
unsigned char file[0x200];
|
|
size_t size = build(file);
|
|
const char* error = NULL;
|
|
/* The size in the header is the file's */
|
|
CHECK(dat_open(&schema, file, size - 1, &error) == NULL && error != NULL);
|
|
/* A relocation outside the data */
|
|
unsigned char bad[0x200];
|
|
memcpy(bad, file, size);
|
|
put32(bad + 0x20 + DATA, DATA - 2);
|
|
CHECK(dat_open(&schema, bad, size, &error) == NULL);
|
|
/* A public symbol's name outside the symbol table */
|
|
memcpy(bad, file, size);
|
|
put32(bad + 0x20 + DATA + 4 * 7 + 4, 0x100);
|
|
CHECK(dat_open(&schema, bad, size, &error) == NULL);
|
|
}
|
|
|
|
static void check_inline_count(const DatSchema* schema, uint32_t count,
|
|
uint32_t expected)
|
|
{
|
|
unsigned char file[0x200];
|
|
size_t size = build_inline(file, count, false);
|
|
DatArchive* a = dat_open(schema, file, size, NULL);
|
|
CHECK(a != NULL);
|
|
InlineLeaves* leaves = dat_public(a, "root", T_INLINE_LEAVES);
|
|
CHECK(leaves != NULL && leaves->n == (int32_t) count);
|
|
char* text = trace(a);
|
|
CHECK(!contains(text, "issue"));
|
|
CHECK(contains(text, "extent 0x10 0x1C") == (expected == 2));
|
|
if (expected != 0 && leaves != NULL) {
|
|
CHECK(leaves->entries[0].c == 123);
|
|
}
|
|
if (expected == 2 && leaves != NULL) {
|
|
CHECK(leaves->entries[1].c == 456);
|
|
}
|
|
CHECK(dat_verify(a, NULL) == 0);
|
|
free(text);
|
|
dat_close(a);
|
|
}
|
|
|
|
static void test_conditional_counts(void)
|
|
{
|
|
/* Inactive unresolved or undefined branches must stay unevaluated. */
|
|
const DatSchema* variants[] = {
|
|
&fixture_conditional0_schema, &fixture_conditional1_schema,
|
|
&fixture_conditional2_schema, &fixture_conditional3_schema,
|
|
&fixture_conditional4_schema, &fixture_conditional5_schema,
|
|
&fixture_conditional6_schema, &fixture_conditional7_schema
|
|
};
|
|
for (size_t i = 0; i < DAT_COUNTOF(variants); i++) {
|
|
check_inline_count(variants[i], 0, 0);
|
|
check_inline_count(variants[i], 2, 2);
|
|
}
|
|
/* Undefined active operations retain the default single element. */
|
|
check_inline_count(&fixture_invalid0_schema, 2, 1);
|
|
check_inline_count(&fixture_invalid1_schema, 2, 1);
|
|
}
|
|
|
|
static void test_union_member_binding(void)
|
|
{
|
|
const DatSchema local_schema = fixture_binding_schema;
|
|
unsigned char file[0x200];
|
|
size_t size = build(file);
|
|
DatArchive* a = dat_open(&local_schema, file, size, NULL);
|
|
CHECK(a != NULL);
|
|
Node* root = dat_public(a, "root", T_NODE);
|
|
CHECK(root != NULL && root->u.leaf != NULL);
|
|
char* text = trace(a);
|
|
CHECK(!contains(text, "issue"));
|
|
CHECK(contains(text, "extent 0x40 0x58"));
|
|
CHECK(dat_verify(a, NULL) == 0);
|
|
free(text);
|
|
dat_close(a);
|
|
}
|
|
|
|
static void test_packed(void)
|
|
{
|
|
unsigned char file[0x200];
|
|
size_t size = build(file);
|
|
size_t padded = (size + 31) & ~(size_t) 31;
|
|
unsigned char* two = calloc(1, padded + size);
|
|
memcpy(two, file, size);
|
|
memcpy(two + padded, file, size);
|
|
DatArchive* out[4];
|
|
size_t offsets[4];
|
|
int n =
|
|
dat_open_packed(&schema, two, padded + size, out, offsets, 4, NULL);
|
|
CHECK(n == 2);
|
|
if (n == 2) {
|
|
CHECK(offsets[0] == 0 && offsets[1] == padded);
|
|
Node* a = dat_public(out[1], "root", T_NODE);
|
|
CHECK(a != NULL && a->leaf != NULL && a->leaf->c == 0x12345678);
|
|
dat_close(out[0]);
|
|
dat_close(out[1]);
|
|
}
|
|
free(two);
|
|
}
|
|
|
|
static void test_tagged_plain_union_size(void)
|
|
{
|
|
for (unsigned i = 0; i < 9; i++) {
|
|
uint32_t kind = i % 3 == 2;
|
|
uint32_t offset = i >= 6 ? 4 : 0;
|
|
uint32_t data = (i % 3 == 0 ? 4 : 12) + offset;
|
|
bool foreign = i % 3 == 1;
|
|
unsigned char file[0x200] = { 0 };
|
|
unsigned char* d = file + 32;
|
|
put32(d, 4);
|
|
put32(d + offset, 123);
|
|
if (data - offset > 4) {
|
|
put32(d + offset + 4, 456);
|
|
put32(d + offset + 8, 0x3FC00000);
|
|
}
|
|
unsigned char* at = d + data;
|
|
if (offset) {
|
|
put32(at, 0);
|
|
at += 4;
|
|
}
|
|
if (foreign) {
|
|
put32(at, offset + 4);
|
|
at += 4;
|
|
}
|
|
put32(at, 0), put32(at + 4, 0), at += 8;
|
|
memcpy(at, "root", 5);
|
|
at += 5;
|
|
size_t size = (size_t) (at - file);
|
|
put32(file, (uint32_t) size);
|
|
put32(file + 4, data);
|
|
put32(file + 8, foreign + (offset != 0));
|
|
put32(file + 12, 1);
|
|
const DatScope bind = { DAT_NAME_kind, kind, NULL };
|
|
const DatRoot root = { .name = "root",
|
|
.type = offset ? T_COUNTED_LEAF_P : T_CHOICE,
|
|
.count_kind = i >= 3 && i < 6
|
|
? DAT_COUNT_EXACTLY
|
|
: DAT_COUNT_ONE,
|
|
.count = 1,
|
|
.env = &bind };
|
|
const DatFileRoots roots = { .file = "fixture",
|
|
.roots = &root,
|
|
.nroots = 1 };
|
|
const DatModule module = { .files = &roots, .nfiles = 1 };
|
|
DatSchema local_schema = fixture_plain_schema;
|
|
local_schema.modules = &module;
|
|
local_schema.nmodules = 1;
|
|
DatArchive* a = dat_open(&local_schema, file, size, NULL);
|
|
CHECK(a != NULL);
|
|
CHECK(dat_load_roots(a, "fixture", 0) == 1);
|
|
void* loaded = dat_public(a, "root", root.type);
|
|
CHECK(loaded != NULL);
|
|
union PlainChoice* value =
|
|
offset && loaded != NULL ? *(union PlainChoice**) loaded : loaded;
|
|
CHECK(value != NULL);
|
|
char* text = trace(a);
|
|
CHECK(!contains(text, "issue"));
|
|
CHECK(contains(
|
|
text, offset ? (kind ? "extent 0x4 0x10\n" : "extent 0x4 0x8\n")
|
|
: (kind ? "extent 0x0 0xC\n" : "extent 0x0 0x4\n")));
|
|
CHECK(dat_verify(a, NULL) == 0);
|
|
if (value != NULL) {
|
|
if (kind) {
|
|
CHECK(value->large.c == 456);
|
|
value->large.c++;
|
|
} else {
|
|
CHECK(value->small == 123);
|
|
value->small++;
|
|
}
|
|
CHECK(dat_verify(a, NULL) == 1);
|
|
}
|
|
free(text);
|
|
dat_close(a);
|
|
}
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
test_walk();
|
|
test_inline_counts();
|
|
test_array_typedef_counts();
|
|
test_conditional_counts();
|
|
test_union_member_binding();
|
|
test_byte_scripts();
|
|
test_tagged_plain_union_size();
|
|
test_refuses();
|
|
test_packed();
|
|
printf("%s (%zu-bit %s-endian)\n", failures ? "FAILED" : "ok",
|
|
sizeof(void*) * 8,
|
|
*(const unsigned char*) &(const uint16_t){ 1 } ? "little" : "big");
|
|
return failures ? 1 : 0;
|
|
}
|