From e68c00d7e00a330be5ca6cf249ae3476a06bfa2f Mon Sep 17 00:00:00 2001 From: Paweł Redman Date: Tue, 20 Dec 2016 10:48:53 +0100 Subject: Refactor mapcat.c to get rid of globals. --- src/common.h | 46 +++++++- src/elist.h | 14 ++- src/main.c | 34 +++++- src/mapcat.c | 334 ++++++++++++++++++++++++++++++++--------------------------- 4 files changed, 267 insertions(+), 161 deletions(-) diff --git a/src/common.h b/src/common.h index 8c6f43f..7420485 100644 --- a/src/common.h +++ b/src/common.h @@ -33,6 +33,8 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #define PROGRAM_NAME "mapcat" #define PROGRAM_VERSION "0.1.0" +#define MAPCAT_DISCARD_SHADER "common/discard" + // common.c typedef struct { @@ -81,6 +83,44 @@ int lexer_get_floats(lexer_state_t *ls, float *out, size_t count); // mapcat.c -int mapcat_load(const char *path); -int mapcat_save(const char *path); -void mapcat_free(void); +typedef struct { + float def[9]; + char *shader; + float texmap[8]; + elist_header_t list; +} brush_face_t; + +typedef struct { + brush_face_t *faces; + elist_header_t list; +} brush_t; + +typedef struct { + char *key; + char *value; + elist_header_t list; +} entity_key_t; + +typedef struct { + char *classname; + brush_t *brushes; + entity_key_t *keys; + + elist_header_t list; +} entity_t; + +typedef struct { + entity_t *worldspawn; + entity_t *entities; + + // note: num_entities doesn't include the worldspawn + size_t num_entities, num_discarded_entities; + size_t num_brushes, num_discarded_brushes; +} map_t; + +void map_init(map_t *map); +void map_free(map_t *map); +int map_read(map_t *map, const char *path); +int map_write(const map_t *map, const char *path); +int map_merge(map_t *master, map_t *slave); +void map_print_stats(const char *path, const map_t *map); diff --git a/src/elist.h b/src/elist.h index da6d172..84b2a72 100644 --- a/src/elist.h +++ b/src/elist.h @@ -27,6 +27,11 @@ static inline elist_header_t *elist_header(void *entry, size_t offs) return (elist_header_t*)((char*)entry + offs); } +static inline const elist_header_t *elist_cheader(const void *entry, size_t offs) +{ + return (const elist_header_t*)((const char*)entry + offs); +} + static inline void elist_append_real(void **head, void *entry, size_t offs) { elist_header_t *head_header, *entry_header; @@ -56,9 +61,15 @@ elist_append_real((void**)(head), (entry), offsetin((entry), member)) #define elist_next(entry, member) \ (elist_header((entry), offsetin((entry), member))->next) +#define elist_cnext(entry, member) \ +(elist_cheader((entry), offsetin((entry), member))->next) + #define elist_for(iter, head, member) \ for ((iter) = (head); (iter); (iter) = elist_next((iter), member)) +#define elist_cfor(iter, head, member) \ +for ((iter) = (head); (iter); (iter) = elist_cnext((iter), member)) + static inline void elist_unlink_real(void **head, void *entry, size_t offs) { elist_header_t *entry_header; @@ -101,9 +112,10 @@ static inline void elist_append_list_real(void **head1, void *head2, size_t offs if (!head2) return; - head1_header = elist_header(head1, offs); + head1_header = elist_header(*head1, offs); head2_header = elist_header(head2, offs); last1_header = elist_header(head1_header->prev, offs); + head1_header->prev = head2_header->prev; last1_header->next = head2; head2_header->prev = head1_header->prev; diff --git a/src/main.c b/src/main.c index d34f00e..93cc0b0 100644 --- a/src/main.c +++ b/src/main.c @@ -47,6 +47,7 @@ int main(int argc, char **argv) int rv = 1, i; input_file_t *inputs = NULL, *input, *next; char *output = NULL; + map_t map; for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-v")) { @@ -92,17 +93,40 @@ int main(int argc, char **argv) goto out; } - elist_for (input, inputs, list) - if (mapcat_load(input->path)) + map_init(&map); + + elist_for (input, inputs, list) { + map_t part; + + map_init(&part); + + if (map_read(&part, input->path)) { + error("error: couldn't read %s\n", input->path); + goto out; + } + + map_print_stats(input->path, &part); + + if (map_merge(&map, &part)) { + error("error: couldn't merge %s into %s\n", + input->path, output); + map_free(&map); + map_free(&part); goto out; + } + } - if (mapcat_save(output)) + map_print_stats(output, &map); + + if (map_write(&map, output)) { + error("error: couldn't write %s\n", output); + map_free(&map); goto out; + } + map_free(&map); rv = 0; out: - mapcat_free(); - for (input = inputs; input; input = next) { next = elist_next(input, list); free(input); diff --git a/src/mapcat.c b/src/mapcat.c index 6420cf5..d9bff86 100644 --- a/src/mapcat.c +++ b/src/mapcat.c @@ -19,34 +19,56 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #define DEBUG #include "common.h" -typedef struct { - float def[9]; - char *shader; - float texmap[8]; - elist_header_t list; -} brush_face_t; - -typedef struct { - brush_face_t *faces; - elist_header_t list; -} brush_t; - -typedef struct { - char *key; - char *value; - elist_header_t list; -} entity_key_t; - -typedef struct { - char *classname; - brush_t *brushes; - entity_key_t *keys; - - elist_header_t list; -} entity_t; - -entity_t *worldspawn; -entity_t *entities; +// +// freeing +// + +static void free_entity_keys(entity_t *entity) +{ + entity_key_t *key, *next; + + for (key = entity->keys; key; key = next) { + next = elist_next(key, list); + + free(key->key); + free(key->value); + free(key); + } +} + +static void free_brush(brush_t *brush) +{ + brush_face_t *face, *face_next; + + for (face = brush->faces; face; face = face_next) { + face_next = elist_next(face, list); + + free(face->shader); + free(face); + } + + free(brush); +} + +// this frees an entity_t and its children +static void free_entity(entity_t *entity) +{ + brush_t *brush, *next; + + free_entity_keys(entity); + free(entity->classname); + + for (brush = entity->brushes; brush; brush = next) { + next = elist_next(brush, list); + free_brush(brush); + } + + free(entity); +} + +// +// reading +// static int read_entity_key(lexer_state_t *ls, entity_t *entity) { @@ -174,79 +196,18 @@ static int read_brush_faces(lexer_state_t *ls, brush_t *brush) return 0; } -static void free_entity_keys(entity_t *entity) -{ - entity_key_t *key, *next; - - for (key = entity->keys; key; key = next) { - next = elist_next(key, list); - - free(key->key); - free(key->value); - free(key); - } -} - -static void free_brush(brush_t *brush) -{ - brush_face_t *face, *face_next; - - for (face = brush->faces; face; face = face_next) { - face_next = elist_next(face, list); - - free(face->shader); - free(face); - } - - free(brush); -} - -// this frees an entity_t and its children -// note: this function does NOT unlink the entity from the global list -static void free_entity(entity_t *entity) -{ - brush_t *brush, *next; - - free_entity_keys(entity); - free(entity->classname); - - for (brush = entity->brushes; brush; brush = next) { - next = elist_next(brush, list); - free_brush(brush); - } - - free(entity); -} - -static void merge_into_worldspawn(entity_t *entity) -{ - // the first worldspawn becomes the output's worldspawn. - // other worldspawn's brushes are appended to it - if (!worldspawn) { - worldspawn = entity; - return; - } - - // keep only the original key-value pairs - free_entity_keys(entity); - - elist_append_list(worldspawn->brushes, entity->brushes, list); - - free(entity); -} - static bool brush_discard(brush_t *brush) { brush_face_t *face; elist_for(face, brush->faces, list) - if (!strcmp(face->shader, "mapcat_discard")) + if (!strcmp(face->shader, MAPCAT_DISCARD_SHADER)) return true; return false; } -static int read_entity(lexer_state_t *ls) +static int read_entity(lexer_state_t *ls, map_t *map) { entity_t *entity; @@ -311,18 +272,26 @@ static int read_entity(lexer_state_t *ls) goto error; } - if (brush_discard(brush)) + if (brush_discard(brush)) { free_brush(brush); - else + map->num_discarded_brushes++; + } else { elist_append(&entity->brushes, brush, list); + map->num_brushes++; + } } no_brushes: + if (!strcmp(entity->classname, "worldspawn")) { + if (map->worldspawn) { + lexer_perror(ls, "this entity is a worldspawn, but a " + "worldspawn was already read earlier"); + goto error; + } - if (entity->classname && !strcmp(entity->classname, "worldspawn")) - merge_into_worldspawn(entity); - else - elist_append(&entities, entity, list); + map->worldspawn = entity; + } else + elist_append(&map->entities, entity, list); return 0; error: @@ -330,45 +299,15 @@ error: return 1; } +// +// writing +// -int mapcat_load(const char *path) -{ - int rv = 1; - lexer_state_t lexer; - vstr_t token; - - vstr_init(&token); - - if (lexer_open(&lexer, path, &token)) { - perror(path); - return 1; - } - - while (1) { - int ret; - - ret = lexer_assert_or_eof(&lexer, "{", - "the beginning of an entity"); - if (ret == -1) - break; - if (ret > 0) - goto out; - - if (read_entity(&lexer)) - goto out; - } - - rv = 0; -out: - vstr_free(&token); - return rv; -} - -static int write_brush(FILE *fp, brush_t *brush) +static int write_brush(FILE *fp, const brush_t *brush) { - brush_face_t *face; + const brush_face_t *face; - elist_for(face, brush->faces, list) { + elist_cfor(face, brush->faces, list) { size_t i; for (i = 0; i < 9; i += 3) { @@ -397,18 +336,19 @@ static int write_brush(FILE *fp, brush_t *brush) return 0; } -static int write_entity(FILE *fp, entity_t *entity) +static int write_entity(FILE *fp, const entity_t *entity) { - entity_key_t *key; - brush_t *brush; + const entity_key_t *key; + const brush_t *brush; size_t brush_counter = 0; - fprintf(fp, "\"classname\" \"%s\"\n", entity->classname); + if (entity->classname) + fprintf(fp, "\"classname\" \"%s\"\n", entity->classname); - elist_for(key, entity->keys, list) + elist_cfor(key, entity->keys, list) fprintf(fp, "\"%s\" \"%s\"\n", key->key, key->value); - elist_for(brush, entity->brushes, list) { + elist_cfor(brush, entity->brushes, list) { fprintf(fp, "// brush %zu\n{\n", brush_counter); write_brush(fp, brush); fprintf(fp, "}\n"); @@ -421,11 +361,70 @@ static int write_entity(FILE *fp, entity_t *entity) return 0; } -int mapcat_save(const char *path) +// +// entry points +// + +void map_init(map_t *map) +{ + memset(map, 0, sizeof(*map)); +} + +void map_free(map_t *map) +{ + entity_t *entity, *next; + + if (map->worldspawn) + free_entity(map->worldspawn); + + for (entity = map->entities; entity; entity = next) { + next = elist_next(entity, list); + free_entity(entity); + } +} + +int map_read(map_t *map, const char *path) +{ + int rv = 1; + lexer_state_t lexer; + vstr_t token; + + vstr_init(&token); + + if (lexer_open(&lexer, path, &token)) { + perror(path); + return 1; + } + + while (1) { + int ret; + + ret = lexer_assert_or_eof(&lexer, "{", + "the beginning of an entity"); + if (ret == -1) + break; + if (ret > 0) + goto out; + + if (read_entity(&lexer, map)) + goto out; + } + + rv = 0; +out: + vstr_free(&token); + + if (rv) + map_free(map); + + return rv; +} + +int map_write(const map_t *map, const char *path) { int rv = 1; FILE *fp; - entity_t *entity; + const entity_t *entity; size_t entity_counter = 1; // worldspawn is #0 fp = fopen(path, "w"); @@ -434,16 +433,16 @@ int mapcat_save(const char *path) goto out; } - if (!worldspawn) { + if (!map->worldspawn) { fprintf(stderr, "error: worldspawn is missing\n"); goto out; } fprintf(fp, "// entity 0\n{\n"); - write_entity(fp, worldspawn); + write_entity(fp, map->worldspawn); fprintf(fp, "}\n"); - elist_for(entity, entities, list) { + elist_cfor(entity, map->entities, list) { fprintf(fp, "// entity %zu\n{\n", entity_counter); if (write_entity(fp, entity)) { @@ -469,15 +468,46 @@ out: return rv; } -void mapcat_free(void) +//RETURN VALUE +// always 0 (this function cannot fail (yet)) +// slave is left in invalid state after this function returns, do not use it +int map_merge(map_t *master, map_t *slave) { - entity_t *entity, *next; + if (!master->worldspawn) { + // the first worldspawn is kept intact + master->worldspawn = slave->worldspawn; + } else if (slave->worldspawn) { + // worldspawns of subsequent maps are discarded, except their + // brushes, which are appended to the master's worldspawn + elist_append_list(&master->worldspawn->brushes, + slave->worldspawn->brushes, list); + + free_entity_keys(slave->worldspawn); + free(slave->worldspawn->classname); + free(slave->worldspawn); + } - if (worldspawn) - free_entity(worldspawn); + // entities are always kept intact + elist_append_list(&master->entities, slave->entities, list); - for (entity = entities; entity; entity = next) { - next = elist_next(entity, list); - free_entity(entity); - } + master->num_entities += slave->num_entities; + master->num_discarded_entities += slave->num_discarded_entities; + master->num_brushes += slave->num_brushes; + master->num_discarded_brushes += slave->num_discarded_brushes; + + return 0; +} + +void map_print_stats(const char *path, const map_t *map) +{ + size_t ents_with_worldspawn; + + ents_with_worldspawn = map->num_entities + (map->worldspawn ? 1 : 0); + + printf("%s: ", path); + printf("%zu entit%s (%zu discarded), %zu brush%s (%zu discarded)\n", + ents_with_worldspawn, (ents_with_worldspawn == 1 ? "y" : "ies"), + map->num_discarded_entities, + map->num_brushes, (map->num_brushes == 1 ? "" : "es"), + map->num_discarded_brushes); } -- cgit