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