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 +}