mtglist

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