From ff95aeafbf3a7ade353838a4e2c7fa88727c9d3d Mon Sep 17 00:00:00 2001 From: William Miles Date: Thu, 8 Sep 2022 12:23:16 +1000 Subject: [PATCH] A lot of formatting and an untested implementation of espr_create_tree. --- NavmeshList/main.c | 4 +- espReader/ESPReader.h | 15 +- espReader/Reader.c | 837 +++++++++++++++++++++++++----------------- mph_gen/main.c | 2 +- 4 files changed, 507 insertions(+), 351 deletions(-) diff --git a/NavmeshList/main.c b/NavmeshList/main.c index ddedd9b..94c9d96 100644 --- a/NavmeshList/main.c +++ b/NavmeshList/main.c @@ -34,13 +34,13 @@ int main(void) { if (!decompressed) return errno; - espr_print(buffer, size); + // espr_print(buffer, size); espr_decompress(buffer, size, decompressed, stats.decompressed_size); free(buffer); - // espr_print(decompressed, stats.decompressed_size); + espr_print(decompressed, stats.decompressed_size); free(decompressed); diff --git a/espReader/ESPReader.h b/espReader/ESPReader.h index 086b11e..77e2d04 100644 --- a/espReader/ESPReader.h +++ b/espReader/ESPReader.h @@ -109,6 +109,10 @@ extern "C" { char _pad[4]; }; + struct sized_buf { + char *data; + size_t size; + }; // // === ENUMS === @@ -172,7 +176,7 @@ extern "C" { Group *group; Record *record; } header; - char *const data; + char *data; enum node_type type; uint32_t _pad; }; @@ -196,8 +200,8 @@ extern "C" { * will be passed to pre and post when they are called. */ struct walker_callbacks { - void (*pre)(Node n, void *data, void **carry_out); - void (*post)(Node n, void *data, void **carry_in); + void (*pre)(Node n, void *data, void **carry_out, void *from_parent, void **to_children); + void (*post)(Node n, void *data, void *carry_in); void *data; }; @@ -227,7 +231,8 @@ extern "C" { struct meta_node { Node n; MetaNode *parent; - MetaNode *child; + MetaNode *first_child; + MetaNode *last_child; MetaNode *prev; MetaNode *next; }; @@ -349,7 +354,7 @@ extern "C" { */ void espr_decompress(char *data, size_t size, char *buf, size_t buf_size); - + MetaNode *espr_create_tree(struct sized_buf in, struct sized_buf tree); // End C++ guard #ifdef __cplusplus diff --git a/espReader/Reader.c b/espReader/Reader.c index d51cae9..fbbeec5 100644 --- a/espReader/Reader.c +++ b/espReader/Reader.c @@ -10,6 +10,8 @@ #include #include #include +#include + #include #include "zlib.h" @@ -36,9 +38,14 @@ const int year_offset = 9; void asserts(void); // Tree walkers -char *walk_concat(char *data, size_t size, struct walker_callbacks cb); -char *walk_group(char *data, struct walker_callbacks cb); -char *walk_record(char *data, struct walker_callbacks cb); +char *walk_concat( + char *data, + size_t size, + struct walker_callbacks cb, + void *from_parent +); +char *walk_group(char *data, struct walker_callbacks cb, void *from_parent); +char *walk_record(char *data, struct walker_callbacks cb, void *from_parent); // Header printers void print_group_header(Group *header); @@ -55,39 +62,74 @@ void sb_write(int fp, struct str_buf sb_pre, struct str_buf sb_post); // Utilities Timestamp convert_ts(uint16_t ts); -void print_cb(Node n, void *data, void **carry_out); -void stats_cb(Node n, void *data, void **carry_out); -void decompress_pre(Node n, void *data, void **carry_out); -void decompress_post(Node n, void *data, void **carry_in); + +// Callbacks +void print_cb( + Node n, + void *data, + void **carry_out, + void *from_parent, + void **to_children +); +void stats_cb( + Node n, + void *data, + void **carry_out, + void *from_parent, + void **to_children +); +void decompress_pre( + Node n, + void *decom_ptr, + void **carry_out, + void *from_parent, + void **to_children +); +void decompress_post( + Node n, + void *data, + void **carry_in +); +void create_tree_cb( + Node n, + void *data, + void **carry_out, + void *from_parent, + void **to_children +); // // === FUNCTIONS === // -void asserts(void) { - // binary overlay size checks - assert(sizeof(Record) == 24); // Record struct incorrect size - assert(sizeof(Group) == 24); // Group struct incorrect size - assert(sizeof((Group) { 0 }.label) == 4); // Label union incorrect size - assert(sizeof(Field) == 6); // Field struct incorrect size +void asserts(void) +{ + // binary overlay size checks + assert(sizeof(Record) == 24); // Record struct incorrect size + assert(sizeof(Group) == 24); // Group struct incorrect size + assert(sizeof((Group) { 0 }.label) == 4); // Label union incorrect size + assert(sizeof(Field) == 6); // Field struct incorrect size - // zlib compatability - assert(sizeof(uLongf) == sizeof(uint32_t)); - assert(sizeof(Bytef) == sizeof(char)); + assert(sizeof(MetaNode) == 64); // 1 cache line + + // zlib compatability + assert(sizeof(uLongf) == sizeof(uint32_t)); + assert(sizeof(Bytef) == sizeof(char)); } -void espr_walk(char *data, size_t size, struct walker_callbacks cb) { - // check assertions that cannot be checked at compile time - asserts(); +void espr_walk(char *data, size_t size, struct walker_callbacks cb) +{ + // check assertions that cannot be checked at compile time + asserts(); - char *data_start = data; + char *data_start = data; - // check that we are at the start of the file - const Type4 type = *(const Type4 *)data; - assert(type.uint == rt[TES4]); + // check that we are at the start of the file + const Type4 type = *(const Type4 *)data; + assert(type.uint == rt[TES4]); - data = walk_concat(data, size, cb); - assert(data == data_start + size); + data = walk_concat(data, size, cb, NULL); + assert(data == data_start + size); } /* Unknown data will be some concatenation of groups and records. @@ -95,431 +137,540 @@ void espr_walk(char *data, size_t size, struct walker_callbacks cb) { * `walk_concat` will call the appropriate walking function * for each segment of unknown data in this concatenation. */ -char *walk_concat(char *data, size_t size, struct walker_callbacks cb) { - const char *end = data + size; - while (data != end) { - assert(data < end); +char *walk_concat( + char *data, + size_t size, + struct walker_callbacks cb, + void *from_parent +) { + const char *end = data + size; + while (data != end) { + assert(data < end); - const Type4 *type = (Type4 *)data; + const Type4 *type = (Type4 *)data; - // check valid type - assert(rt[rt_hash(type->uint)] == type->uint); + // check valid type + assert(rt[rt_hash(type->uint)] == type->uint); - // only need to distinguish between groups and records - if (type->uint == rt[GRUP]) - data = walk_group(data, cb); - else - data = walk_record(data, cb); - } - return data; + // only need to distinguish between groups and records + if (type->uint == rt[GRUP]) + data = walk_group(data, cb, from_parent); + else + data = walk_record(data, cb, from_parent); + } + return data; } -/* Walk a group record. Group records are containers for any other type of +/* Walk a group record. Group records are containers for any other type of * record, including other group records. * - * This function will also call `cb` with the node constructed from this group + * This function will also call `cb` with the node constructed from this group * record. */ -char *walk_group(char *data, struct walker_callbacks cb) { - Group *const header = (Group *const)data; +char *walk_group(char *data, struct walker_callbacks cb, void *from_parent) +{ + Group *const header = (Group *const)data; - // The size in the group header includes the size of the header - char *data_start = data + sizeof(Group); - char *data_end = data + header->size; - size_t data_size = data_end - data_start; + // The size in the group header includes the size of the header + char *data_start = data + sizeof(Group); + char *data_end = data + header->size; + size_t data_size = data_end - data_start; - Node n = { .header.group = header, .data = data_start, .type = NT_GROUP }; - void *carry; + Node n = { + .header.group = header, + .data = data_start, + .type = NT_GROUP + }; + void *carry = NULL; + void *to_children = NULL; - // Pre-walk callback - if (cb.pre) - cb.pre(n, cb.data, &carry); + // Pre-walk callback + if (cb.pre) + cb.pre(n, cb.data, &carry, from_parent, &to_children); - // Walk through the concatenation of data inside the group. - data = walk_concat(data_start, data_size, cb); - assert(data == data_end); + // Walk through the concatenation of data inside the group. + data = walk_concat(data_start, data_size, cb, to_children); + assert(data == data_end); - // Post-walk callback - if (cb.post) - cb.post(n, cb.data, &carry); + // Post-walk callback + if (cb.post) + cb.post(n, cb.data, carry); - return data; + return data; } -char *walk_record(char *data, struct walker_callbacks cb) { - Record *header = (Record *)data; - assert(header->type.uint != rt[GRUP]); +char *walk_record(char *data, struct walker_callbacks cb, void *from_parent) +{ + Record *header = (Record *)data; + assert(header->type.uint != rt[GRUP]); - char *data_start = data + sizeof(Record); + char *data_start = data + sizeof(Record); - Node n = { .header.record = header, .data = data_start, .type = NT_RECORD }; - void *carry; + Node n = { + .header.record = header, + .data = data_start, + .type = NT_RECORD + }; + void *carry = NULL; + void *to_children = NULL; - /* Pre and post walk callbacks make less sense for record walking as records - * are leaf-ish, will still call both here for now as field walking may be - * added in the future. - */ + /* Pre and post walk callbacks make less sense for record walking as + * records are leaf-ish, will still call both here for now as field + * walking may be added in the future. + */ - // Pre-walk callback - if (cb.pre) - cb.pre(n, cb.data, &carry); + // Pre-walk callback + if (cb.pre) + cb.pre(n, cb.data, &carry, from_parent, &to_children); - // Update data ptr based on record size. - data += sizeof(Record) + header->size; + // Update data ptr based on record size. + data += sizeof(Record) + header->size; - // Post-walk callback - if (cb.post) - cb.post(n, cb.data, &carry); + // Post-walk callback + if (cb.post) + cb.post(n, cb.data, carry); - return data; + return data; } -void espr_print(char *data, size_t size) { - struct walker_callbacks cb = { .pre = print_cb }; - espr_walk(data, size, cb); +void espr_print(char *data, size_t size) +{ + struct walker_callbacks cb = { .pre = print_cb }; + espr_walk(data, size, cb); } -void print_cb(Node n, void *data, void **carry_out) { - (void)data; - (void)carry_out; - switch (n.type) { +void print_cb( + Node n, void *data, + void **carry_out, + void *from_parent, + void **to_children +) { + (void)data; + (void)carry_out; + (void)from_parent; + (void)to_children; + + switch (n.type) { case NT_GROUP: - print_group_header(n.header.group); - break; + print_group_header(n.header.group); + break; case NT_RECORD: - print_record_header(n.header.record); - break; + print_record_header(n.header.record); + break; default: - assert(false); // invalid node type - } + assert(false); // invalid node type + } } -struct esp_stats espr_stats(char *data, size_t size) { - struct esp_stats stats = { 0 }; - struct walker_callbacks cb = { .pre = stats_cb, .data = &stats }; - espr_walk(data, size, cb); - return stats; +struct esp_stats espr_stats(char *data, size_t size) +{ + struct esp_stats stats = { 0 }; + struct walker_callbacks cb = { .pre = stats_cb, .data = &stats }; + espr_walk(data, size, cb); + return stats; } /* Tallies up the group and record count. Calculates uncompressed size; groups - * only need their header size tallied as their data size will be handled by - * further walking of the tree. + * only need their header size tallied as their data size will be handled by + * further walking of the tree. */ -void stats_cb(Node n, void *data, void **carry_out) { - (void)carry_out; - struct esp_stats *stats = data; - switch (n.type) { +void stats_cb( + Node n, + void *data, + void **carry_out, + void *from_parent, + void **to_children +) { + (void)carry_out; + (void)from_parent; + (void)to_children; + + struct esp_stats *stats = data; + switch (n.type) { case NT_GROUP: - stats->group_count++; - stats->decompressed_size += sizeof(Group); - break; + stats->group_count++; + stats->decompressed_size += sizeof(Group); + break; case NT_RECORD: - stats->record_count++; - stats->decompressed_size += sizeof(Record); - if (n.header.record->flags & COMPRESSED_FLAG) { - // uncompressed size is stored in the first 4 bytes of data - stats->decompressed_size += *((uint32_t *)n.data); - } else { - stats->decompressed_size += n.header.record->size; - } - break; + stats->record_count++; + stats->decompressed_size += sizeof(Record); + if (n.header.record->flags & COMPRESSED_FLAG) { + // uncompressed size is stored in the first 4 bytes of + // data + stats->decompressed_size += *((uint32_t *)n.data); + } else { + stats->decompressed_size += n.header.record->size; + } + break; default: - assert(false); // invalid node type - } + assert(false); // invalid node type + } } struct decom { - char *buf; - size_t remaining; + char *buf; + size_t remaining; }; -void espr_decompress(char *data, size_t size, char *buf, size_t buf_size) { - struct decom s = { .buf = buf, .remaining = buf_size }; - struct walker_callbacks cb = - { .pre = decompress_pre, .post = decompress_post, .data = &s }; - espr_walk(data, size, cb); +void espr_decompress(char *data, size_t size, char *buf, size_t buf_size) +{ + struct decom s = { .buf = buf, .remaining = buf_size }; + struct walker_callbacks cb = { + .pre = decompress_pre, + .post = decompress_post, + .data = &s + }; + espr_walk(data, size, cb); } /* Handles the copying of groups and records, and the decompression of * compressed record data. - * + * * For groups it copies only the header as group data will be handled by further * walking. The destination prior to copying will also be saved to carry_out for - * groups so that decompress_post can correctly update the size of the copied + * groups so that decompress_post can correctly update the size of the copied * group. - * + * * For uncompressed records it simply copies the entirety of the record to the * destination. For compressed records in copies the header first and then * directly decompresses the compressed record into the destination. */ -void decompress_pre(Node n, void *decom_ptr, void **carry_out) { - struct decom *d = decom_ptr; +void decompress_pre( + Node n, + void *decom_ptr, + void **carry_out, + void *from_parent, + void **to_children +) { + (void)from_parent; + (void)to_children; - switch (n.type) { + struct decom *d = decom_ptr; + + switch (n.type) { case NT_RECORD: - // compressed record - if (n.header.record->flags & COMPRESSED_FLAG) { - // copy header - memcpy(d->buf, n.header.record, sizeof(Record)); + // compressed record + if (n.header.record->flags & COMPRESSED_FLAG) { + // copy header + memcpy(d->buf, n.header.record, sizeof(Record)); - // copied header reference - Record *header = (Record *)d->buf; + // copied header reference + Record *header = (Record *)d->buf; - // update decom struct - d->remaining -= sizeof(Record); - d->buf += sizeof(Record); + // update decom struct + d->remaining -= sizeof(Record); + d->buf += sizeof(Record); - // decompress directly into buffer - // first 4 bytes are the decompressed size - const uint32_t dc_size = *((uint32_t *)n.data); - uint32_t to_copy = dc_size; - uint32_t cur_size = n.header.record->size - sizeof(uint32_t); - char *data_start = n.data + sizeof(uint32_t); - int ret = uncompress( - (Bytef *)d->buf, - (uLongf *)&to_copy, - (Bytef *)data_start, - (uLong)cur_size - ); - assert(ret == Z_OK); - assert(to_copy == dc_size); + // decompress directly into buffer + // first 4 bytes are the decompressed size + const uint32_t dc_size = *((uint32_t *)n.data); + uint32_t to_copy = dc_size; + uint32_t cur_size = + n.header.record->size - sizeof(uint32_t); + char *data_start = n.data + sizeof(uint32_t); + int ret = uncompress( + (Bytef *)d->buf, + (uLongf *)&to_copy, + (Bytef *)data_start, + (uLong)cur_size + ); + assert(ret == Z_OK); + assert(to_copy == dc_size); - // update decom struct - d->remaining -= dc_size; - d->buf += dc_size; + // update decom struct + d->remaining -= dc_size; + d->buf += dc_size; - // update header data size - header->size = dc_size; + // update header data size + header->size = dc_size; - // unset compressed flag - header->flags &= ~COMPRESSED_FLAG; - } - else { - // copy record - size_t record_size = sizeof(Record) + n.header.record->size; - memcpy(d->buf, n.header.record, record_size); + // unset compressed flag + header->flags &= ~COMPRESSED_FLAG; + } else { + // copy record + size_t record_size = sizeof(Record) + n.header.record->size; + memcpy(d->buf, n.header.record, record_size); + + // update decom + d->remaining -= record_size; + d->buf += record_size; + } + break; + case NT_GROUP: + // copy header, contents will be copied while walking + memcpy(d->buf, n.header.group, sizeof(Group)); + + // save copied header location for post-walk group size recalc + *carry_out = (void *)d->buf; // update decom - d->remaining -= record_size; - d->buf += record_size; - } - break; - case NT_GROUP: - // copy header, contents will be copied while walking - memcpy(d->buf, n.header.group, sizeof(Group)); + d->buf += sizeof(Group); + d->remaining -= sizeof(Group); - // save copied header location for post-walk group size recalc - *carry_out = (void *)d->buf; - - // update decom - d->buf += sizeof(Group); - d->remaining -= sizeof(Group); - - break; + break; default: - assert(false); // invalid node type - } + assert(false); // invalid node type + } } -/* Handles recalculating group size after decompression. The location of the +/* Handles recalculating group size after decompression. The location of the * the group's copied header will be passed in in carry_in and can be used both * to access the copied group header and calculate the new size of the group - * based on the difference between the current destination pointer and the + * based on the difference between the current destination pointer and the * group header pointer. */ -void decompress_post(Node n, void *decom_ptr, void **carry_in) { - struct decom *d = decom_ptr; +void decompress_post(Node n, void *decom_ptr, void **carry_in) +{ + struct decom *d = decom_ptr; - // only need to handle group resize - if (n.type == NT_GROUP) { - Group *g = (Group *)(*carry_in); - uint32_t new_size = (uint32_t)((char *)d->buf - (char *)g); - g->size = new_size; - } + // only need to handle group resize + if (n.type == NT_GROUP) { + Group *g = (Group *)(*carry_in); + uint32_t new_size = (uint32_t)((char *)d->buf - (char *)g); + g->size = new_size; + } } -void print_group_header(Group *header) { - assert(header->type < GTS_SIZE); - // Guess at enough with significant margin - char buf[1024] = { 0 }; - const struct str_buf sb_pre = { .buf = buf, .size = sizeof(buf) }; - struct str_buf sb = sb_pre; +void print_group_header(Group *header) +{ + assert(header->type < GTS_SIZE); - // literals - struct str_lit - l1 = STR_LIT("--- HEADER: GROUP ---"), - l2 = STR_LIT("\nType: "), - l3 = STR_LIT("\nSize: "), - l4 = STR_LIT("\nLabel: "), - l5 = STR_LIT("\nGroup type: "), - l6 = STR_LIT("\nTimestamp: "), - l7 = STR_LIT("\nVersion Control Info: "), - l8 = STR_LIT("\nUnknown: "), - l9 = STR_LIT("\n"); + // Guess at enough with significant margin + char buf[1024] = { 0 }; + const struct str_buf sb_pre = { .buf = buf, .size = sizeof(buf) }; + struct str_buf sb = sb_pre; - struct str_lit gt = group_type_strings[header->type]; + // literals + struct str_lit + l1 = STR_LIT("--- HEADER: GROUP ---"), + l2 = STR_LIT("\nType: "), + l3 = STR_LIT("\nSize: "), + l4 = STR_LIT("\nLabel: "), + l5 = STR_LIT("\nGroup type: "), + l6 = STR_LIT("\nTimestamp: "), + l7 = STR_LIT("\nVersion Control Info: "), + l8 = STR_LIT("\nUnknown: "), + l9 = STR_LIT("\n"); - // construct output - litcopy(&sb, l1); - litcopy(&sb, l2); type_str(&sb, header->grup); - litcopy(&sb, l3); num_str(&sb, header->size, 10); - litcopy(&sb, l4); group_label_str(&sb, header); - litcopy(&sb, l5); litcopy(&sb, gt); - litcopy(&sb, l6); timestamp_str(&sb, header->timestamp); - litcopy(&sb, l7); num_str(&sb, header->vcinfo, 16); - litcopy(&sb, l8); num_str(&sb, header->unknown, 16); - litcopy(&sb, l9); + struct str_lit gt = group_type_strings[header->type]; - sb_write(STDOUT_FILENO, sb_pre, sb); + // construct output + litcopy(&sb, l1); + litcopy(&sb, l2); type_str(&sb, header->grup); + litcopy(&sb, l3); num_str(&sb, header->size, 10); + litcopy(&sb, l4); group_label_str(&sb, header); + litcopy(&sb, l5); litcopy(&sb, gt); + litcopy(&sb, l6); timestamp_str(&sb, header->timestamp); + litcopy(&sb, l7); num_str(&sb, header->vcinfo, 16); + litcopy(&sb, l8); num_str(&sb, header->unknown, 16); + litcopy(&sb, l9); + + sb_write(STDOUT_FILENO, sb_pre, sb); } -void litcopy(struct str_buf *sb, struct str_lit lit) { - assert(sb->size >= lit.size); - memcpy(sb->buf, lit.lit, lit.size); - sb->size -= lit.size; - sb->buf += lit.size; +void litcopy(struct str_buf *sb, struct str_lit lit) +{ + assert(sb->size >= lit.size); + memcpy(sb->buf, lit.lit, lit.size); + sb->size -= lit.size; + sb->buf += lit.size; } -void num_str(struct str_buf *sb, unsigned long num, int radix) { - errno_t ret = _ultoa_s(num, sb->buf, sb->size, radix); - assert(ret == 0); - int len = (int)strlen(sb->buf); - sb->size -= len; - sb->buf += len; +void num_str(struct str_buf *sb, unsigned long num, int radix) +{ + errno_t ret = _ultoa_s(num, sb->buf, sb->size, radix); + assert(ret == 0); + int len = (int)strlen(sb->buf); + sb->size -= len; + sb->buf += len; } -void type_str(struct str_buf *sb, Type4 type) { - assert(sb->size >= 4); - for (size_t i = 0; i != 4; i++) - sb->buf[i] = type.bytes[i]; - sb->buf += 4; - sb->size -= 4; +void type_str(struct str_buf *sb, Type4 type) +{ + assert(sb->size >= 4); + for (size_t i = 0; i != 4; i++) + sb->buf[i] = type.bytes[i]; + sb->buf += 4; + sb->size -= 4; } -void group_label_str(struct str_buf *sb, Group *header) { - switch (header->type) { +void group_label_str(struct str_buf *sb, Group *header) +{ + switch (header->type) { case GT_TOP: - type_str(sb, header->label.type); - break; + type_str(sb, header->label.type); + break; case GT_INTERIOR_CELL_BLOCK: case GT_INTERIOR_CELL_SUBBLOCK: - num_str(sb, header->label.number, 10); - break; + num_str(sb, header->label.number, 10); + break; case GT_EXTERIOR_CELL_BLOCK: case GT_EXTERIOR_CELL_SUBBLOCK: - litcopy(sb, LIT("X: ")); num_str(sb, header->label.coord[1], 10); - litcopy(sb, LIT("Y: ")); num_str(sb, header->label.coord[0], 10); - break; + uint16_t x = header->label.coord[1], y = header->label.coord[0]; + litcopy(sb, LIT("X: ")); num_str(sb, x, 10); + litcopy(sb, LIT("Y: ")); num_str(sb, y, 10); + break; case GT_WORLD_CHILDREN: case GT_CELL_CHILDREN: case GT_TOPIC_CHILDREN: case GT_CELL_PERSISTENT_CHILDREN: case GT_CELL_TEMPORARY_CHILDREN: - litcopy(sb, LIT("FormID[")); - num_str(sb, header->label.formid, 16); - litcopy(sb, LIT("]")); - break; + litcopy(sb, LIT("FormID[")); + num_str(sb, header->label.formid, 16); + litcopy(sb, LIT("]")); + break; default: - assert(false); // invalid group type - } -} - -void timestamp_str(struct str_buf *sb, uint16_t timestamp) { - Timestamp ts = convert_ts(timestamp); - litcopy(sb, LIT("20x")); num_str(sb, ts.year, 10); litcopy(sb, LIT("-")); - num_str(sb, ts.month, 10); litcopy(sb, LIT("-")); num_str(sb, ts.day, 10); -} - -void sb_write(int fp, struct str_buf sb_pre, struct str_buf sb_post) { - int size = sb_pre.size - sb_post.size; - assert(size >= 0); - int ret = _write(fp, sb_pre.buf, size); - assert(ret == size); -} - -void print_record_header(Record *header) { - char buf[1024] = { 0 }; - const struct str_buf sb_pre = { .buf = buf, .size = sizeof(buf) }; - struct str_buf sb = sb_pre; - - const struct str_lit - l1 = LIT("--- HEADER: RECORD ---"), - l2 = LIT("\nType: "), - l3 = LIT("\nFlags: "), - l4 = LIT("\nFormID: "), - l5 = LIT("\nTimestamp: "), - l6 = LIT("\nVersion Control Info: "), - l7 = LIT("\nVersion: "), - l8 = LIT("\nUnknown: "), - l9 = LIT("\n"); - - litcopy(&sb, l1); - litcopy(&sb, l2); type_str(&sb, header->type); - litcopy(&sb, l3); record_flags_str(&sb, header); - litcopy(&sb, l4); num_str(&sb, header->formid, 16); - litcopy(&sb, l5); timestamp_str(&sb, header->timestamp); - litcopy(&sb, l6); num_str(&sb, header->vcinfo, 16); - litcopy(&sb, l7); num_str(&sb, header->version, 10); - litcopy(&sb, l8); num_str(&sb, header->unknown, 16); - litcopy(&sb, l9); - - sb_write(STDOUT_FILENO, sb_pre, sb); -} - -void record_flags_str(struct str_buf *sb, Record *header) { - uint32_t flags = header->flags; - const uint32_t type = header->type.uint; - - // print flags - if (type == rt[REFR]) { - // TODO - // REFR requires FormID lookup - flags = 0; - } - else { - rfs_inner *const flag_lut = rfs[rt_hash(type)]; - if (flag_lut) { - while (flags != 0) { - // will always be >= 0 as flags is not 0 - int highest = 31 - __lzcnt(flags); - assert(highest >= 0); - const struct str_lit lit = (*flag_lut)[highest]; - if (lit.lit) { - litcopy(sb, LIT("\n - ")); litcopy(sb, lit); - flags -= ((uint32_t)1) << highest; - } - else - break; - } + assert(false); // invalid group type } - } - - // slow path - if (flags != 0) { - printf("\n\nOriginal flags: %08x\n", header->flags); - printf("Unhandled flags: %08x\n", flags); - assert(false); // unhandled flags - } } -/* Converts the bit-packed/encoded timestamp used in esp/esm files into day, - * month and year. See UESP for further explanation. - * +void timestamp_str(struct str_buf *sb, uint16_t timestamp) +{ + Timestamp ts = convert_ts(timestamp); + litcopy(sb, LIT("20x")); + num_str(sb, ts.year, 10); + litcopy(sb, LIT("-")); + num_str(sb, ts.month, 10); + litcopy(sb, LIT("-")); + num_str(sb, ts.day, 10); +} + +void sb_write(int fp, struct str_buf sb_pre, struct str_buf sb_post) +{ + int size = sb_pre.size - sb_post.size; + assert(size >= 0); + int ret = _write(fp, sb_pre.buf, size); + assert(ret == size); +} + +void print_record_header(Record *header) +{ + char buf[1024] = { 0 }; + const struct str_buf sb_pre = { .buf = buf, .size = sizeof(buf) }; + struct str_buf sb = sb_pre; + + const struct str_lit + l1 = LIT("--- HEADER: RECORD ---"), + l2 = LIT("\nType: "), + l3 = LIT("\nFlags: "), + l4 = LIT("\nFormID: "), + l5 = LIT("\nTimestamp: "), + l6 = LIT("\nVersion Control Info: "), + l7 = LIT("\nVersion: "), + l8 = LIT("\nUnknown: "), + l9 = LIT("\n"); + + litcopy(&sb, l1); + litcopy(&sb, l2); type_str(&sb, header->type); + litcopy(&sb, l3); record_flags_str(&sb, header); + litcopy(&sb, l4); num_str(&sb, header->formid, 16); + litcopy(&sb, l5); timestamp_str(&sb, header->timestamp); + litcopy(&sb, l6); num_str(&sb, header->vcinfo, 16); + litcopy(&sb, l7); num_str(&sb, header->version, 10); + litcopy(&sb, l8); num_str(&sb, header->unknown, 16); + litcopy(&sb, l9); + + sb_write(STDOUT_FILENO, sb_pre, sb); +} + +void record_flags_str(struct str_buf *sb, Record *header) +{ + uint32_t flags = header->flags; + const uint32_t type = header->type.uint; + + // print flags + if (type == rt[REFR]) { + // TODO + // REFR requires FormID lookup + flags = 0; + } else { + rfs_inner *const flag_lut = rfs[rt_hash(type)]; + if (flag_lut) { + while (flags != 0) { + // get next flag, from lowest bit to highest + // will always be >= 0 as flags is not 0 + int lowest = _tzcnt_u32(flags); + assert(lowest < 32); + + // get flag string + const struct str_lit lit = (*flag_lut)[lowest]; + + // not a valid flag + if (!lit.lit) + break; + + // copy flag string + litcopy(sb, LIT("\n - ")); litcopy(sb, lit); + // remove flag from to be processed + flags ^= ((uint32_t)1) << lowest; + } + } + } + + // slow path + if (flags != 0) { + printf("\n\nOriginal flags: %08x\n", header->flags); + printf("Unhandled flags: %08x\n", flags); + assert(false); // unhandled flags + } +} + +/* Converts the bit-packed/encoded timestamp used in esp/esm files into day, + * month and year. See UESP for further explanation. + * * This currently handles the timestamp format used in Skyrim.esm, but newer * files apparently use a different format. This will need to be handled later. */ -Timestamp convert_ts(uint16_t ts) { - /* - const uint8_t day = (uint8_t)(ts & day_mask); - const uint8_t month = (uint8_t)((ts >> month_offset) & month_mask); - const uint16_t year = (ts >> year_offset) & year_mask; - */ +Timestamp convert_ts(uint16_t ts) +{ + /* + const uint8_t day = (uint8_t)(ts & day_mask); + const uint8_t month = (uint8_t)((ts >> month_offset) & month_mask); + const uint16_t year = (ts >> year_offset) & year_mask; + */ - const uint8_t day = ts & 0xff; - const uint8_t hb = (ts >> 8) & 0xff; - const uint8_t month = ((hb - 1) % 12) + 1; - const uint8_t year = ((hb - 1) / 12 + 3) % 10; + const uint8_t day = ts & 0xff; + const uint8_t hb = (ts >> 8) & 0xff; + const uint8_t month = ((hb - 1) % 12) + 1; + const uint8_t year = ((hb - 1) / 12 + 3) % 10; - return (Timestamp) { year, month, day }; + return (Timestamp) { year, month, day }; +} + +MetaNode *espr_create_tree(struct sized_buf in, struct sized_buf tree) +{ + const struct sized_buf tree_pre = tree; + struct walker_callbacks cb = { .pre = create_tree_cb, .data = &tree }; + espr_walk(in.data, in.size, cb); + return (MetaNode *)tree_pre.data; +} + +void create_tree_cb( + const Node n, + void *data, + void **carry_out, + void *from_parent, + void **to_children +) { + (void)carry_out; + + // add new metanode to tree + struct sized_buf *tree = data; + assert(tree->size >= sizeof(MetaNode)); + MetaNode *m = (MetaNode *)tree->data; + tree->data += sizeof(MetaNode); + tree->size -= sizeof(MetaNode); + + MetaNode *p = from_parent; + + // construct new node + m->n = n; + m->parent = p; + m->prev = p->last_child; + p->last_child->next = m; + p->last_child = m; + + // send self to children + *to_children = m; } diff --git a/mph_gen/main.c b/mph_gen/main.c index 9ae1d53..409a94c 100644 --- a/mph_gen/main.c +++ b/mph_gen/main.c @@ -24,7 +24,7 @@ bool buf[NUM]; -int main() { +int main(void) { uint32_t seed = 1; bool clash; size_t max = 0;