mtglist
diff src/mtg.c @ 0:0a45dfe93e66
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sun, 02 Nov 2014 11:07:45 +0200 |
parents | |
children | 7211fa8db425 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/mtg.c Sun Nov 02 11:07:45 2014 +0200 1.3 @@ -0,0 +1,290 @@ 1.4 +#include <stdio.h> 1.5 +#include <stdlib.h> 1.6 +#include <string.h> 1.7 +#include <ctype.h> 1.8 +#include <errno.h> 1.9 +#include "rbtree.h" 1.10 +#include "mtg.h" 1.11 + 1.12 +#define ED_FNAME "editions.cfg" 1.13 + 1.14 +static int load_editions(void); 1.15 +static unsigned int parse_color(const char *colstr, const char *notes); 1.16 +static int strcmp_lower(const char *a, const char *b); 1.17 +static void rbdel(struct rbnode *node, void *cls); 1.18 + 1.19 +static struct { 1.20 + enum mtg_card_type type; 1.21 + const char *name; 1.22 +} type_names[] = { 1.23 + {MTG_TYPE_LAND, "Land"}, 1.24 + {MTG_TYPE_CREATURE, "Creature"}, 1.25 + {MTG_TYPE_SORCERY, "Sorcery"}, 1.26 + {MTG_TYPE_INSTANT, "Instant"}, 1.27 + {MTG_TYPE_ENCHANTMENT, "Enchantment"}, 1.28 + {MTG_TYPE_ARTIFACT, "Artifact"}, 1.29 + {MTG_TYPE_PLANESWALKER, "Planeswalker"}, 1.30 + {MTG_TYPE_UNKNOWN, 0} 1.31 +}; 1.32 + 1.33 +static struct rbtree *cards; 1.34 +static struct rbtree *editions; 1.35 + 1.36 + 1.37 +int mtg_init(void) 1.38 +{ 1.39 + if(cards || editions) { 1.40 + fprintf(stderr, "mtg already initialized\n"); 1.41 + return -1; 1.42 + } 1.43 + 1.44 + if(!(cards = rb_create((rb_cmp_func_t)strcmp_lower))) { 1.45 + goto err; 1.46 + } 1.47 + rb_set_delete_func(cards, rbdel, 0); 1.48 + 1.49 + if(!(editions = rb_create((rb_cmp_func_t)strcmp_lower))) { 1.50 + goto err; 1.51 + } 1.52 + rb_set_delete_func(editions, rbdel, 0); 1.53 + 1.54 + if(load_editions() == -1) { 1.55 + goto err; 1.56 + } 1.57 + return 0; 1.58 + 1.59 +err: 1.60 + if(cards) rb_destroy(cards); 1.61 + if(editions) rb_destroy(editions); 1.62 + return -1; 1.63 +} 1.64 + 1.65 +void mtg_destroy(void) 1.66 +{ 1.67 + rb_free(cards); 1.68 + rb_free(editions); 1.69 + cards = editions = 0; 1.70 +} 1.71 + 1.72 +int mtg_load_cards(const char *fname) 1.73 +{ 1.74 + int i; 1.75 + FILE *fp; 1.76 + char buf[512]; 1.77 + 1.78 + if(!(fp = fopen(fname, "r"))) { 1.79 + fprintf(stderr, "failed to open magic list: %s: %s\n", fname, strerror(errno)); 1.80 + return -1; 1.81 + } 1.82 + 1.83 + while(fgets(buf, sizeof buf, fp)) { 1.84 + struct mtg_card *card; 1.85 + char *field[6]; 1.86 + 1.87 + for(i=0; i<6; i++) { 1.88 + field[i] = strtok(i ? 0 : buf, "|\n\r\t\v;"); 1.89 + } 1.90 + 1.91 + if(!(card = malloc(sizeof *card))) { 1.92 + continue; 1.93 + } 1.94 + if(!field[1] || !(card->name = strdup(field[1]))) { 1.95 + free(card); 1.96 + continue; 1.97 + } 1.98 + card->color = parse_color(field[0], field[5]); 1.99 + card->type = mtg_str_type(field[2]); 1.100 + card->edition = mtg_edition(field[4]); 1.101 + card->count = atoi(field[3]); 1.102 + 1.103 + rb_insert(cards, card->name, card); 1.104 + } 1.105 + 1.106 + fclose(fp); 1.107 + return 0; 1.108 +} 1.109 + 1.110 +void mtg_iter_begin(void) 1.111 +{ 1.112 + rb_begin(cards); 1.113 +} 1.114 + 1.115 +const struct mtg_card *mtg_iter_next(void) 1.116 +{ 1.117 + struct rbnode *node = rb_next(cards); 1.118 + return node ? rb_node_data(node) : 0; 1.119 +} 1.120 + 1.121 +#define ADDCOL(x) \ 1.122 + do { \ 1.123 + if(*str) { \ 1.124 + strcat(str, "/"); \ 1.125 + } \ 1.126 + strcat(str, x); \ 1.127 + } while(0) 1.128 + 1.129 +const char *mtg_color_str(unsigned int color) 1.130 +{ 1.131 + static char str[128]; 1.132 + str[0] = 0; 1.133 + 1.134 + if(color == MTG_COL_MULTI) { 1.135 + return "multicolor"; 1.136 + } 1.137 + 1.138 + if(color & MTG_COL_RED) { 1.139 + ADDCOL("red"); 1.140 + } 1.141 + if(color & MTG_COL_GREEN) { 1.142 + ADDCOL("green"); 1.143 + } 1.144 + if(color & MTG_COL_BLUE) { 1.145 + ADDCOL("blue"); 1.146 + } 1.147 + if(color & MTG_COL_BLACK) { 1.148 + ADDCOL("black"); 1.149 + } 1.150 + if(color & MTG_COL_WHITE) { 1.151 + ADDCOL("white"); 1.152 + } 1.153 + if(color & MTG_COL_ARTIFACT) { 1.154 + ADDCOL("artifact"); 1.155 + } 1.156 + return str[0] ? str : "colorless"; 1.157 +} 1.158 + 1.159 +const char *mtg_type_str(enum mtg_card_type type) 1.160 +{ 1.161 + int i; 1.162 + for(i=0; type_names[i].name; i++) { 1.163 + if(type_names[i].type == type) { 1.164 + return type_names[i].name; 1.165 + } 1.166 + } 1.167 + return MTG_TYPE_UNKNOWN; 1.168 +} 1.169 + 1.170 +enum mtg_card_type mtg_str_type(const char *s) 1.171 +{ 1.172 + int i; 1.173 + for(i=0; type_names[i].name; i++) { 1.174 + if(strcmp_lower(type_names[i].name, s) == 0) { 1.175 + return type_names[i].type; 1.176 + } 1.177 + } 1.178 + return MTG_TYPE_UNKNOWN; 1.179 +} 1.180 + 1.181 +const char *mtg_edition(const char *edcode) 1.182 +{ 1.183 + struct rbnode *node = rb_find(editions, (void*)edcode); 1.184 + if(!node) { 1.185 + return "unknown"; 1.186 + } 1.187 + return rb_node_data(node); 1.188 +} 1.189 + 1.190 +int mtg_is_multicolor(unsigned int color) 1.191 +{ 1.192 + int i, bits = 0; 1.193 + 1.194 + for(i=0; i<16; i++) { 1.195 + if(color & 1) ++bits; 1.196 + color >>= 1; 1.197 + } 1.198 + return bits > 1; 1.199 +} 1.200 + 1.201 +static void dbgprint(struct rbnode *node, void *cls) 1.202 +{ 1.203 + fprintf(stderr, "%s\n", (char*)rb_node_key(node)); 1.204 +} 1.205 + 1.206 +static int load_editions(void) 1.207 +{ 1.208 + FILE *fp; 1.209 + char buf[128]; 1.210 + 1.211 + if(!(fp = fopen(ED_FNAME, "r"))) { 1.212 + perror("failed to open editions config file: " ED_FNAME); 1.213 + return -1; 1.214 + } 1.215 + 1.216 + while(fgets(buf, sizeof buf, fp)) { 1.217 + char *code, *name; 1.218 + 1.219 + code = strtok(buf, "|\n\r"); 1.220 + if(!code) continue; 1.221 + 1.222 + name = strtok(0, "|\n\r"); 1.223 + if(!name) continue; 1.224 + 1.225 + rb_insert(editions, strdup(code), strdup(name)); 1.226 + } 1.227 + 1.228 + rb_foreach(editions, dbgprint, 0); 1.229 + 1.230 + fclose(fp); 1.231 + return 0; 1.232 +} 1.233 + 1.234 +static unsigned int parse_multicolor(const char *str) 1.235 +{ 1.236 + int i; 1.237 + unsigned int mask = 0; 1.238 + 1.239 + char *cstr = alloca(strlen(str) + 1); 1.240 + for(i=0; str[i]; i++) { 1.241 + cstr[i] = tolower(str[i]); 1.242 + } 1.243 + cstr[i] = 0; 1.244 + 1.245 + if(strstr(cstr, "red")) mask |= MTG_COL_RED; 1.246 + if(strstr(cstr, "green")) mask |= MTG_COL_GREEN; 1.247 + if(strstr(cstr, "blue")) mask |= MTG_COL_BLUE; 1.248 + if(strstr(cstr, "black")) mask |= MTG_COL_BLACK; 1.249 + if(strstr(cstr, "white")) mask |= MTG_COL_WHITE; 1.250 + if(strstr(cstr, "artifact")) mask |= MTG_COL_ARTIFACT; 1.251 + 1.252 + return mask; 1.253 +} 1.254 + 1.255 +static unsigned int parse_color(const char *colstr, const char *notes) 1.256 +{ 1.257 + int i; 1.258 + unsigned int res = 0; 1.259 + char *cstr = alloca(strlen(colstr) + 1); 1.260 + for(i=0; colstr[i]; i++) { 1.261 + cstr[i] = tolower(colstr[i]); 1.262 + } 1.263 + cstr[i] = 0; 1.264 + 1.265 + if((res = parse_multicolor(colstr))) { 1.266 + return res; 1.267 + } 1.268 + 1.269 + if(strcmp(cstr, "*") == 0) { 1.270 + if((res = parse_multicolor(notes))) { 1.271 + return res; 1.272 + } 1.273 + return MTG_COL_MULTI; 1.274 + } 1.275 + 1.276 + return MTG_COL_NONE; 1.277 +} 1.278 + 1.279 +static int strcmp_lower(const char *a, const char *b) 1.280 +{ 1.281 + return strcasecmp(a, b); 1.282 + while(*a && tolower(*a) == tolower(*b)) { 1.283 + ++a; 1.284 + ++b; 1.285 + } 1.286 + return tolower(*a) - tolower(*b); 1.287 +} 1.288 + 1.289 +static void rbdel(struct rbnode *node, void *cls) 1.290 +{ 1.291 + free(rb_node_key(node)); 1.292 + free(rb_node_data(node)); 1.293 +}