summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common.h46
-rw-r--r--src/elist.h14
-rw-r--r--src/main.c34
-rw-r--r--src/mapcat.c334
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);
}