mtglist

annotate src/mtg.c @ 1:7211fa8db425

added filters
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 03 Nov 2014 01:20:40 +0200
parents 0a45dfe93e66
children
rev   line source
nuclear@0 1 #include <stdio.h>
nuclear@0 2 #include <stdlib.h>
nuclear@0 3 #include <string.h>
nuclear@0 4 #include <ctype.h>
nuclear@0 5 #include <errno.h>
nuclear@0 6 #include "rbtree.h"
nuclear@0 7 #include "mtg.h"
nuclear@0 8
nuclear@0 9 #define ED_FNAME "editions.cfg"
nuclear@0 10
nuclear@0 11 static int load_editions(void);
nuclear@0 12 static unsigned int parse_color(const char *colstr, const char *notes);
nuclear@0 13 static int strcmp_lower(const char *a, const char *b);
nuclear@0 14 static void rbdel(struct rbnode *node, void *cls);
nuclear@0 15
nuclear@0 16 static struct {
nuclear@0 17 enum mtg_card_type type;
nuclear@0 18 const char *name;
nuclear@0 19 } type_names[] = {
nuclear@0 20 {MTG_TYPE_LAND, "Land"},
nuclear@0 21 {MTG_TYPE_CREATURE, "Creature"},
nuclear@0 22 {MTG_TYPE_SORCERY, "Sorcery"},
nuclear@0 23 {MTG_TYPE_INSTANT, "Instant"},
nuclear@0 24 {MTG_TYPE_ENCHANTMENT, "Enchantment"},
nuclear@0 25 {MTG_TYPE_ARTIFACT, "Artifact"},
nuclear@0 26 {MTG_TYPE_PLANESWALKER, "Planeswalker"},
nuclear@0 27 {MTG_TYPE_UNKNOWN, 0}
nuclear@0 28 };
nuclear@0 29
nuclear@0 30 static struct rbtree *cards;
nuclear@0 31 static struct rbtree *editions;
nuclear@0 32
nuclear@0 33
nuclear@0 34 int mtg_init(void)
nuclear@0 35 {
nuclear@0 36 if(cards || editions) {
nuclear@0 37 fprintf(stderr, "mtg already initialized\n");
nuclear@0 38 return -1;
nuclear@0 39 }
nuclear@0 40
nuclear@0 41 if(!(cards = rb_create((rb_cmp_func_t)strcmp_lower))) {
nuclear@0 42 goto err;
nuclear@0 43 }
nuclear@0 44 rb_set_delete_func(cards, rbdel, 0);
nuclear@0 45
nuclear@0 46 if(!(editions = rb_create((rb_cmp_func_t)strcmp_lower))) {
nuclear@0 47 goto err;
nuclear@0 48 }
nuclear@0 49 rb_set_delete_func(editions, rbdel, 0);
nuclear@0 50
nuclear@0 51 if(load_editions() == -1) {
nuclear@0 52 goto err;
nuclear@0 53 }
nuclear@0 54 return 0;
nuclear@0 55
nuclear@0 56 err:
nuclear@0 57 if(cards) rb_destroy(cards);
nuclear@0 58 if(editions) rb_destroy(editions);
nuclear@0 59 return -1;
nuclear@0 60 }
nuclear@0 61
nuclear@0 62 void mtg_destroy(void)
nuclear@0 63 {
nuclear@0 64 rb_free(cards);
nuclear@0 65 rb_free(editions);
nuclear@0 66 cards = editions = 0;
nuclear@0 67 }
nuclear@0 68
nuclear@0 69 int mtg_load_cards(const char *fname)
nuclear@0 70 {
nuclear@0 71 int i;
nuclear@0 72 FILE *fp;
nuclear@0 73 char buf[512];
nuclear@0 74
nuclear@0 75 if(!(fp = fopen(fname, "r"))) {
nuclear@0 76 fprintf(stderr, "failed to open magic list: %s: %s\n", fname, strerror(errno));
nuclear@0 77 return -1;
nuclear@0 78 }
nuclear@0 79
nuclear@0 80 while(fgets(buf, sizeof buf, fp)) {
nuclear@0 81 struct mtg_card *card;
nuclear@0 82 char *field[6];
nuclear@0 83
nuclear@0 84 for(i=0; i<6; i++) {
nuclear@0 85 field[i] = strtok(i ? 0 : buf, "|\n\r\t\v;");
nuclear@0 86 }
nuclear@0 87
nuclear@0 88 if(!(card = malloc(sizeof *card))) {
nuclear@0 89 continue;
nuclear@0 90 }
nuclear@0 91 if(!field[1] || !(card->name = strdup(field[1]))) {
nuclear@0 92 free(card);
nuclear@0 93 continue;
nuclear@0 94 }
nuclear@0 95 card->color = parse_color(field[0], field[5]);
nuclear@0 96 card->type = mtg_str_type(field[2]);
nuclear@0 97 card->edition = mtg_edition(field[4]);
nuclear@0 98 card->count = atoi(field[3]);
nuclear@0 99
nuclear@0 100 rb_insert(cards, card->name, card);
nuclear@0 101 }
nuclear@0 102
nuclear@0 103 fclose(fp);
nuclear@0 104 return 0;
nuclear@0 105 }
nuclear@0 106
nuclear@0 107 void mtg_iter_begin(void)
nuclear@0 108 {
nuclear@0 109 rb_begin(cards);
nuclear@0 110 }
nuclear@0 111
nuclear@0 112 const struct mtg_card *mtg_iter_next(void)
nuclear@0 113 {
nuclear@0 114 struct rbnode *node = rb_next(cards);
nuclear@0 115 return node ? rb_node_data(node) : 0;
nuclear@0 116 }
nuclear@0 117
nuclear@0 118 #define ADDCOL(x) \
nuclear@0 119 do { \
nuclear@0 120 if(*str) { \
nuclear@0 121 strcat(str, "/"); \
nuclear@0 122 } \
nuclear@0 123 strcat(str, x); \
nuclear@0 124 } while(0)
nuclear@0 125
nuclear@0 126 const char *mtg_color_str(unsigned int color)
nuclear@0 127 {
nuclear@0 128 static char str[128];
nuclear@0 129 str[0] = 0;
nuclear@0 130
nuclear@0 131 if(color == MTG_COL_MULTI) {
nuclear@0 132 return "multicolor";
nuclear@0 133 }
nuclear@0 134
nuclear@0 135 if(color & MTG_COL_RED) {
nuclear@0 136 ADDCOL("red");
nuclear@0 137 }
nuclear@0 138 if(color & MTG_COL_GREEN) {
nuclear@0 139 ADDCOL("green");
nuclear@0 140 }
nuclear@0 141 if(color & MTG_COL_BLUE) {
nuclear@0 142 ADDCOL("blue");
nuclear@0 143 }
nuclear@0 144 if(color & MTG_COL_BLACK) {
nuclear@0 145 ADDCOL("black");
nuclear@0 146 }
nuclear@0 147 if(color & MTG_COL_WHITE) {
nuclear@0 148 ADDCOL("white");
nuclear@0 149 }
nuclear@0 150 if(color & MTG_COL_ARTIFACT) {
nuclear@0 151 ADDCOL("artifact");
nuclear@0 152 }
nuclear@0 153 return str[0] ? str : "colorless";
nuclear@0 154 }
nuclear@0 155
nuclear@0 156 const char *mtg_type_str(enum mtg_card_type type)
nuclear@0 157 {
nuclear@0 158 int i;
nuclear@0 159 for(i=0; type_names[i].name; i++) {
nuclear@0 160 if(type_names[i].type == type) {
nuclear@0 161 return type_names[i].name;
nuclear@0 162 }
nuclear@0 163 }
nuclear@0 164 return MTG_TYPE_UNKNOWN;
nuclear@0 165 }
nuclear@0 166
nuclear@0 167 enum mtg_card_type mtg_str_type(const char *s)
nuclear@0 168 {
nuclear@0 169 int i;
nuclear@0 170 for(i=0; type_names[i].name; i++) {
nuclear@0 171 if(strcmp_lower(type_names[i].name, s) == 0) {
nuclear@0 172 return type_names[i].type;
nuclear@0 173 }
nuclear@0 174 }
nuclear@0 175 return MTG_TYPE_UNKNOWN;
nuclear@0 176 }
nuclear@0 177
nuclear@0 178 const char *mtg_edition(const char *edcode)
nuclear@0 179 {
nuclear@0 180 struct rbnode *node = rb_find(editions, (void*)edcode);
nuclear@0 181 if(!node) {
nuclear@0 182 return "unknown";
nuclear@0 183 }
nuclear@0 184 return rb_node_data(node);
nuclear@0 185 }
nuclear@0 186
nuclear@0 187 int mtg_is_multicolor(unsigned int color)
nuclear@0 188 {
nuclear@0 189 int i, bits = 0;
nuclear@0 190
nuclear@0 191 for(i=0; i<16; i++) {
nuclear@0 192 if(color & 1) ++bits;
nuclear@0 193 color >>= 1;
nuclear@0 194 }
nuclear@0 195 return bits > 1;
nuclear@0 196 }
nuclear@0 197
nuclear@0 198 static int load_editions(void)
nuclear@0 199 {
nuclear@0 200 FILE *fp;
nuclear@0 201 char buf[128];
nuclear@0 202
nuclear@0 203 if(!(fp = fopen(ED_FNAME, "r"))) {
nuclear@0 204 perror("failed to open editions config file: " ED_FNAME);
nuclear@0 205 return -1;
nuclear@0 206 }
nuclear@0 207
nuclear@0 208 while(fgets(buf, sizeof buf, fp)) {
nuclear@0 209 char *code, *name;
nuclear@0 210
nuclear@0 211 code = strtok(buf, "|\n\r");
nuclear@0 212 if(!code) continue;
nuclear@0 213
nuclear@0 214 name = strtok(0, "|\n\r");
nuclear@0 215 if(!name) continue;
nuclear@0 216
nuclear@0 217 rb_insert(editions, strdup(code), strdup(name));
nuclear@0 218 }
nuclear@0 219
nuclear@0 220 fclose(fp);
nuclear@0 221 return 0;
nuclear@0 222 }
nuclear@0 223
nuclear@1 224 unsigned int mtg_parse_multicolor(const char *str)
nuclear@0 225 {
nuclear@0 226 int i;
nuclear@0 227 unsigned int mask = 0;
nuclear@0 228
nuclear@0 229 char *cstr = alloca(strlen(str) + 1);
nuclear@0 230 for(i=0; str[i]; i++) {
nuclear@0 231 cstr[i] = tolower(str[i]);
nuclear@0 232 }
nuclear@0 233 cstr[i] = 0;
nuclear@0 234
nuclear@0 235 if(strstr(cstr, "red")) mask |= MTG_COL_RED;
nuclear@0 236 if(strstr(cstr, "green")) mask |= MTG_COL_GREEN;
nuclear@0 237 if(strstr(cstr, "blue")) mask |= MTG_COL_BLUE;
nuclear@0 238 if(strstr(cstr, "black")) mask |= MTG_COL_BLACK;
nuclear@0 239 if(strstr(cstr, "white")) mask |= MTG_COL_WHITE;
nuclear@0 240 if(strstr(cstr, "artifact")) mask |= MTG_COL_ARTIFACT;
nuclear@0 241
nuclear@0 242 return mask;
nuclear@0 243 }
nuclear@0 244
nuclear@0 245 static unsigned int parse_color(const char *colstr, const char *notes)
nuclear@0 246 {
nuclear@0 247 int i;
nuclear@0 248 unsigned int res = 0;
nuclear@0 249 char *cstr = alloca(strlen(colstr) + 1);
nuclear@0 250 for(i=0; colstr[i]; i++) {
nuclear@0 251 cstr[i] = tolower(colstr[i]);
nuclear@0 252 }
nuclear@0 253 cstr[i] = 0;
nuclear@0 254
nuclear@1 255 if((res = mtg_parse_multicolor(colstr))) {
nuclear@0 256 return res;
nuclear@0 257 }
nuclear@0 258
nuclear@0 259 if(strcmp(cstr, "*") == 0) {
nuclear@1 260 if((res = mtg_parse_multicolor(notes))) {
nuclear@0 261 return res;
nuclear@0 262 }
nuclear@0 263 return MTG_COL_MULTI;
nuclear@0 264 }
nuclear@0 265
nuclear@0 266 return MTG_COL_NONE;
nuclear@0 267 }
nuclear@0 268
nuclear@0 269 static int strcmp_lower(const char *a, const char *b)
nuclear@0 270 {
nuclear@0 271 return strcasecmp(a, b);
nuclear@0 272 while(*a && tolower(*a) == tolower(*b)) {
nuclear@0 273 ++a;
nuclear@0 274 ++b;
nuclear@0 275 }
nuclear@0 276 return tolower(*a) - tolower(*b);
nuclear@0 277 }
nuclear@0 278
nuclear@0 279 static void rbdel(struct rbnode *node, void *cls)
nuclear@0 280 {
nuclear@0 281 free(rb_node_key(node));
nuclear@0 282 free(rb_node_data(node));
nuclear@0 283 }