mtglist

view 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 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 void dbgprint(struct rbnode *node, void *cls)
199 {
200 fprintf(stderr, "%s\n", (char*)rb_node_key(node));
201 }
203 static int load_editions(void)
204 {
205 FILE *fp;
206 char buf[128];
208 if(!(fp = fopen(ED_FNAME, "r"))) {
209 perror("failed to open editions config file: " ED_FNAME);
210 return -1;
211 }
213 while(fgets(buf, sizeof buf, fp)) {
214 char *code, *name;
216 code = strtok(buf, "|\n\r");
217 if(!code) continue;
219 name = strtok(0, "|\n\r");
220 if(!name) continue;
222 rb_insert(editions, strdup(code), strdup(name));
223 }
225 rb_foreach(editions, dbgprint, 0);
227 fclose(fp);
228 return 0;
229 }
231 static unsigned int parse_multicolor(const char *str)
232 {
233 int i;
234 unsigned int mask = 0;
236 char *cstr = alloca(strlen(str) + 1);
237 for(i=0; str[i]; i++) {
238 cstr[i] = tolower(str[i]);
239 }
240 cstr[i] = 0;
242 if(strstr(cstr, "red")) mask |= MTG_COL_RED;
243 if(strstr(cstr, "green")) mask |= MTG_COL_GREEN;
244 if(strstr(cstr, "blue")) mask |= MTG_COL_BLUE;
245 if(strstr(cstr, "black")) mask |= MTG_COL_BLACK;
246 if(strstr(cstr, "white")) mask |= MTG_COL_WHITE;
247 if(strstr(cstr, "artifact")) mask |= MTG_COL_ARTIFACT;
249 return mask;
250 }
252 static unsigned int parse_color(const char *colstr, const char *notes)
253 {
254 int i;
255 unsigned int res = 0;
256 char *cstr = alloca(strlen(colstr) + 1);
257 for(i=0; colstr[i]; i++) {
258 cstr[i] = tolower(colstr[i]);
259 }
260 cstr[i] = 0;
262 if((res = parse_multicolor(colstr))) {
263 return res;
264 }
266 if(strcmp(cstr, "*") == 0) {
267 if((res = parse_multicolor(notes))) {
268 return res;
269 }
270 return MTG_COL_MULTI;
271 }
273 return MTG_COL_NONE;
274 }
276 static int strcmp_lower(const char *a, const char *b)
277 {
278 return strcasecmp(a, b);
279 while(*a && tolower(*a) == tolower(*b)) {
280 ++a;
281 ++b;
282 }
283 return tolower(*a) - tolower(*b);
284 }
286 static void rbdel(struct rbnode *node, void *cls)
287 {
288 free(rb_node_key(node));
289 free(rb_node_data(node));
290 }