1077 lines
33 KiB
C
1077 lines
33 KiB
C
/**
|
|
* @file
|
|
* This small program will extract information from Gridarta's types.xml file to generate documentation about types and fields.
|
|
* Files are placed in developer's documentation subdirs by default.
|
|
*
|
|
* To build: <pre>gcc -g -pg -O0 -Wall -pedantic gridarta-types-convert.c -I../include -o gridarta-types-convert</pre>
|
|
* To run: <pre>./gridarta-types-convert ../../gridarta/crossfire/resource/conf/types.xml</pre>
|
|
* (adjust the path according to your setup)
|
|
*
|
|
* Note that someone wishing to tweak this program should know the format of Gridarta's types.xml.
|
|
*
|
|
* This program isn't terribly robust, there could be more conditions, and it just doesn't free memory.
|
|
* May be fixed someday, but since it's a "run and exit", not high priority.
|
|
*
|
|
* This program can be modified and altered at will, as long as it's for the Crossfire project. Else don't touch it :)
|
|
*
|
|
* Note that "attribute" is used for "field in a object/living structure".
|
|
*
|
|
* @author
|
|
* Nicolas Weeger / Ryo_ on IRC
|
|
* @date 2008-01-06
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "define.h"
|
|
|
|
const char *destination_dir = "../doc/Developers"; /**< Root destination dir. */
|
|
const char *field_dir = "fields"; /**< Where the files about the fields will be stored. */
|
|
const char *type_dir = "types"; /**< Where the files about types will be stored. */
|
|
|
|
/** One attribute in a type. */
|
|
typedef struct {
|
|
char *field;
|
|
char *name;
|
|
char *description;
|
|
} type_attribute;
|
|
|
|
/** One object type. */
|
|
typedef struct {
|
|
int number;
|
|
char *name;
|
|
char *description;
|
|
char *use;
|
|
type_attribute **attributes;
|
|
int attribute_count;
|
|
char **required;
|
|
int require_count;
|
|
} type_definition;
|
|
|
|
/** Defined types. */
|
|
type_definition **types = NULL;
|
|
|
|
int type_count = 0;
|
|
|
|
/** Definitions all types have by default. */
|
|
type_definition *default_type = NULL;
|
|
|
|
/** Dummy object type that non defined objects use. */
|
|
type_definition *fallback_type = NULL;
|
|
|
|
/** One list of fields to ignore. */
|
|
typedef struct {
|
|
char *name;
|
|
int count;
|
|
char **fields;
|
|
} ignore_list;
|
|
|
|
ignore_list **lists = NULL;
|
|
int list_count = 0;
|
|
|
|
/** One type for an attribute. */
|
|
typedef struct {
|
|
char **type;
|
|
int *number;
|
|
int count;
|
|
char *description;
|
|
} attribute_type;
|
|
|
|
/** One attribute. */
|
|
typedef struct {
|
|
char *field;
|
|
attribute_type **types;
|
|
int type_count;
|
|
} attribute_definition;
|
|
|
|
attribute_definition **attributes = NULL;
|
|
int attribute_count = 0;
|
|
|
|
/** One flag. */
|
|
typedef struct {
|
|
const char *field;
|
|
const char *code_name;
|
|
} flag_definition;
|
|
|
|
/** Flag mapping. */
|
|
static const flag_definition flags[] = {
|
|
{ "alive", "FLAG_ALIVE" },
|
|
{ "wiz", "FLAG_WIZ" },
|
|
{ "was_wiz", "FLAG_WAS_WIZ" },
|
|
{ "applied", "FLAG_APPLIED" },
|
|
{ "unpaid", "FLAG_UNPAID" },
|
|
{ "can_use_shield", "FLAG_USE_SHIELD" },
|
|
{ "no_pick", "FLAG_NO_PICK" },
|
|
{ "client_anim_sync", "FLAG_CLIENT_ANIM_SYNC" },
|
|
{ "client_anim_random", "FLAG_CLIENT_ANIM_RANDOM" },
|
|
{ "is_animated", "FLAG_ANIMATE" },
|
|
{ "monster", "FLAG_MONSTER" },
|
|
{ "friendly", "FLAG_FRIENDLY" },
|
|
{ "generator", "FLAG_GENERATOR" },
|
|
{ "is_thrown", "FLAG_IS_THROWN" },
|
|
{ "auto_apply", "FLAG_AUTO_APPLY" },
|
|
{ "treasure", "FLAG_TREASURE" },
|
|
{ "player sold", "FLAG_PLAYER_SOLD" },
|
|
{ "see_invisible", "FLAG_SEE_INVISIBLE" },
|
|
{ "can_roll", "FLAG_CAN_ROLL" },
|
|
{ "overlay_floor", "FLAG_OVERLAY_FLOOR" },
|
|
{ "is_turnable", "FLAG_IS_TURNABLE" },
|
|
{ "is_used_up", "FLAG_IS_USED_UP" },
|
|
{ "identified", "FLAG_IDENTIFIED" },
|
|
{ "reflecting", "FLAG_REFLECTING" },
|
|
{ "changing", "FLAG_CHANGING" },
|
|
{ "splitting", "FLAG_SPLITTING" },
|
|
{ "hitback", "FLAG_HITBACK" },
|
|
{ "startequip", "FLAG_STARTEQUIP" },
|
|
{ "blocksview", "FLAG_BLOCKSVIEW" },
|
|
{ "undead", "FLAG_UNDEAD" },
|
|
{ "scared", "FLAG_SCARED" },
|
|
{ "unaggressive", "FLAG_UNAGGRESSIVE" },
|
|
{ "reflect_missile", "FLAG_REFL_MISSILE" },
|
|
{ "reflect_spell", "FLAG_REFL_SPELL" },
|
|
{ "no_magic", "FLAG_NO_MAGIC" },
|
|
{ "no_fix_player", "FLAG_NO_FIX_PLAYER" },
|
|
{ "is_lightable", "FLAG_IS_LIGHTABLE" },
|
|
{ "tear_down", "FLAG_TEAR_DOWN" },
|
|
{ "run_away", "FLAG_RUN_AWAY" },
|
|
{ "unique", "FLAG_UNIQUE" },
|
|
{ "no_drop", "FLAG_NO_DROP" },
|
|
{ "can_cast_spell", "FLAG_CAST_SPELL" },
|
|
{ "can_use_scroll", "FLAG_USE_SCROLL" },
|
|
{ "can_use_range", "FLAG_USE_RANGE" },
|
|
{ "can_use_bow", "FLAG_USE_BOW" },
|
|
{ "can_use_armour", "FLAG_USE_ARMOUR" },
|
|
{ "can_use_weapon", "FLAG_USE_WEAPON" },
|
|
{ "can_use_ring", "FLAG_USE_RING" },
|
|
{ "can_use_earring", "FLAG_USE_EARRING" },
|
|
{ "has_ready_range", "FLAG_READY_RANGE" },
|
|
{ "has_ready_bow", "FLAG_READY_BOW" },
|
|
{ "xrays", "FLAG_XRAYS" },
|
|
{ "is_floor", "FLAG_IS_FLOOR" },
|
|
{ "lifesave", "FLAG_LIFESAVE" },
|
|
{ "no_strength", "FLAG_NO_STRENGTH" },
|
|
{ "sleep", "FLAG_SLEEP" },
|
|
{ "stand_still", "FLAG_STAND_STILL" },
|
|
{ "random_movement", "FLAG_RANDOM_MOVE" },
|
|
{ "only_attack", "FLAG_ONLY_ATTACK" },
|
|
{ "confused", "FLAG_CONFUSED" },
|
|
{ "stealth", "FLAG_STEALTH" },
|
|
{ "cursed", "FLAG_CURSED" },
|
|
{ "damned", "FLAG_DAMNED" },
|
|
{ "see_anywhere", "FLAG_SEE_ANYWHERE" },
|
|
{ "known_magical", "FLAG_KNOWN_MAGICAL" },
|
|
{ "known_cursed", "FLAG_KNOWN_CURSED" },
|
|
{ "can_use_skill", "FLAG_CAN_USE_SKILL" },
|
|
{ "been_applied", "FLAG_BEEN_APPLIED" },
|
|
{ "has_ready_scroll", "FLAG_READY_SCROLL" },
|
|
{ "can_use_rod", "FLAG_USE_ROD" },
|
|
{ "can_use_horn", "FLAG_USE_HORN" },
|
|
{ "make_invisible", "FLAG_MAKE_INVIS" },
|
|
{ "inv_locked", "FLAG_INV_LOCKED" },
|
|
{ "is_wooded", "FLAG_IS_WOODED" },
|
|
{ "is_hilly", "FLAG_IS_HILLY" },
|
|
{ "has_ready_skill", "FLAG_READY_SKILL" },
|
|
{ "has_ready_weapon", "FLAG_READY_WEAPON" },
|
|
{ "no_skill_ident", "FLAG_NO_SKILL_IDENT" },
|
|
{ "is_blind", "FLAG_BLIND" },
|
|
{ "can_see_in_dark", "FLAG_SEE_IN_DARK" },
|
|
{ "is_cauldron", "FLAG_IS_CAULDRON" },
|
|
{ "no_steal", "FLAG_NO_STEAL" },
|
|
{ "one_hit", "FLAG_ONE_HIT" },
|
|
{ "berserk", "FLAG_BERSERK" },
|
|
{ "neutral", "FLAG_NEUTRAL" },
|
|
{ "no_attack", "FLAG_NO_ATTACK" },
|
|
{ "no_damage", "FLAG_NO_DAMAGE" },
|
|
{ "activate_on_push", "FLAG_ACTIVATE_ON_PUSH" },
|
|
{ "activate_on_release", "FLAG_ACTIVATE_ON_RELEASE" },
|
|
{ "is_water", "FLAG_IS_WATER" },
|
|
{ "use_content_on_gen", "FLAG_CONTENT_ON_GEN" },
|
|
{ "is_buildable", "FLAG_IS_BUILDABLE" },
|
|
{ "blessed", "FLAG_BLESSED" },
|
|
{ "known_blessed", "FLAG_KNOWN_BLESSED" },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
/** Return flag if exists, NULL else. */
|
|
const flag_definition *find_flag(const char *name) {
|
|
int flag;
|
|
|
|
for (flag = 0; flags[flag].field; flag++)
|
|
if (!strcmp(flags[flag].field, name))
|
|
return &flags[flag];
|
|
return NULL;
|
|
}
|
|
|
|
typedef struct {
|
|
const char *code_name;
|
|
int value;
|
|
} type_name;
|
|
|
|
static type_name type_names[] = {
|
|
{ "PLAYER", PLAYER },
|
|
{ "TRANSPORT", TRANSPORT },
|
|
{ "ROD", ROD },
|
|
{ "TREASURE", TREASURE },
|
|
{ "POTION", POTION },
|
|
{ "FOOD", FOOD },
|
|
{ "POISON", POISON },
|
|
{ "BOOK", BOOK },
|
|
{ "CLOCK", CLOCK },
|
|
{ "ARROW", ARROW },
|
|
{ "BOW", BOW },
|
|
{ "WEAPON", WEAPON },
|
|
{ "ARMOUR", ARMOUR },
|
|
{ "PEDESTAL", PEDESTAL },
|
|
{ "ALTAR", ALTAR },
|
|
{ "LOCKED_DOOR", LOCKED_DOOR },
|
|
{ "SPECIAL_KEY", SPECIAL_KEY },
|
|
{ "MAP", MAP },
|
|
{ "DOOR", DOOR },
|
|
{ "KEY", KEY },
|
|
{ "TIMED_GATE", TIMED_GATE },
|
|
{ "TRIGGER", TRIGGER },
|
|
{ "GRIMREAPER", GRIMREAPER },
|
|
{ "MAGIC_EAR", MAGIC_EAR },
|
|
{ "TRIGGER_BUTTON", TRIGGER_BUTTON },
|
|
{ "TRIGGER_ALTAR", TRIGGER_ALTAR },
|
|
{ "TRIGGER_PEDESTAL", TRIGGER_PEDESTAL },
|
|
{ "SHIELD", SHIELD },
|
|
{ "HELMET", HELMET },
|
|
{ "HORN", HORN },
|
|
{ "MONEY", MONEY },
|
|
{ "CLASS", CLASS },
|
|
{ "AMULET", AMULET },
|
|
{ "PLAYERMOVER", PLAYERMOVER },
|
|
{ "TELEPORTER", TELEPORTER },
|
|
{ "CREATOR", CREATOR },
|
|
{ "SKILL", SKILL },
|
|
{ "EXPERIENCE", EXPERIENCE },
|
|
{ "EARTHWALL", EARTHWALL },
|
|
{ "GOLEM", GOLEM },
|
|
{ "THROWN_OBJ", THROWN_OBJ },
|
|
{ "BLINDNESS", BLINDNESS },
|
|
{ "GOD", GOD },
|
|
{ "DETECTOR", DETECTOR },
|
|
{ "TRIGGER_MARKER", TRIGGER_MARKER },
|
|
{ "DEAD_OBJECT", DEAD_OBJECT },
|
|
{ "DRINK", DRINK },
|
|
{ "MARKER", MARKER },
|
|
{ "HOLY_ALTAR", HOLY_ALTAR },
|
|
{ "PLAYER_CHANGER", PLAYER_CHANGER },
|
|
{ "BATTLEGROUND", BATTLEGROUND },
|
|
{ "PEACEMAKER", PEACEMAKER },
|
|
{ "GEM", GEM },
|
|
{ "FIREWALL", FIREWALL },
|
|
{ "CHECK_INV", CHECK_INV },
|
|
{ "MOOD_FLOOR", MOOD_FLOOR },
|
|
{ "EXIT", EXIT },
|
|
{ "ENCOUNTER", ENCOUNTER },
|
|
{ "SHOP_FLOOR", SHOP_FLOOR },
|
|
{ "SHOP_MAT", SHOP_MAT },
|
|
{ "RING", RING },
|
|
{ "EARRING", EARRING },
|
|
{ "FLOOR", FLOOR },
|
|
{ "FLESH", FLESH },
|
|
{ "INORGANIC", INORGANIC },
|
|
{ "SKILL_TOOL", SKILL_TOOL },
|
|
{ "LIGHTER", LIGHTER },
|
|
{ "WALL", WALL },
|
|
{ "MISC_OBJECT", MISC_OBJECT },
|
|
{ "MONSTER", MONSTER },
|
|
{ "LAMP", LAMP },
|
|
{ "DUPLICATOR", DUPLICATOR },
|
|
{ "SPELLBOOK", SPELLBOOK },
|
|
{ "CLOAK", CLOAK },
|
|
{ "SPINNER", SPINNER },
|
|
{ "GATE", GATE },
|
|
{ "BUTTON", BUTTON },
|
|
{ "CF_HANDLE", CF_HANDLE },
|
|
{ "HOLE", HOLE },
|
|
{ "TRAPDOOR", TRAPDOOR },
|
|
{ "SIGN", SIGN },
|
|
{ "BOOTS", BOOTS },
|
|
{ "GLOVES", GLOVES },
|
|
{ "SPELL", SPELL },
|
|
{ "SPELL_EFFECT", SPELL_EFFECT },
|
|
{ "CONVERTER", CONVERTER },
|
|
{ "BRACERS", BRACERS },
|
|
{ "POISONING", POISONING },
|
|
{ "SAVEBED", SAVEBED },
|
|
{ "WAND", WAND },
|
|
{ "SCROLL", SCROLL },
|
|
{ "DIRECTOR", DIRECTOR },
|
|
{ "GIRDLE", GIRDLE },
|
|
{ "FORCE", FORCE },
|
|
{ "POTION_EFFECT", POTION_EFFECT },
|
|
{ "EVENT_CONNECTOR", EVENT_CONNECTOR },
|
|
{ "CLOSE_CON", CLOSE_CON },
|
|
{ "CONTAINER", CONTAINER },
|
|
{ "ARMOUR_IMPROVER", ARMOUR_IMPROVER },
|
|
{ "WEAPON_IMPROVER", WEAPON_IMPROVER },
|
|
{ "SKILLSCROLL", SKILLSCROLL },
|
|
{ "DEEP_SWAMP", DEEP_SWAMP },
|
|
{ "IDENTIFY_ALTAR", IDENTIFY_ALTAR },
|
|
{ "SHOP_INVENTORY", SHOP_INVENTORY },
|
|
{ "RUNE", RUNE },
|
|
{ "TRAP", TRAP },
|
|
{ "POWER_CRYSTAL", POWER_CRYSTAL },
|
|
{ "CORPSE", CORPSE },
|
|
{ "DISEASE", DISEASE },
|
|
{ "SYMPTOM", SYMPTOM },
|
|
{ "BUILDER", BUILDER },
|
|
{ "MATERIAL", MATERIAL },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
type_attribute *duplicate_attribute(type_attribute *attr) {
|
|
type_attribute *ret = calloc(1, sizeof(type_attribute));
|
|
ret->field = strdup(attr->field);
|
|
ret->name = strdup(attr->name);
|
|
ret->description = strdup(attr->description);
|
|
return ret;
|
|
}
|
|
|
|
void free_attribute(type_attribute *attr) {
|
|
free(attr->field);
|
|
free(attr->name);
|
|
free(attr->description);
|
|
free(attr);
|
|
}
|
|
|
|
/**
|
|
* Gets the attribute for the specified type. If it doesn't exist, create it.
|
|
* If the attribute is already defined, return the existing one, after cleaning its fields if clean is set.
|
|
*/
|
|
type_attribute *get_attribute_for_type(type_definition *type, const char *attribute, int clean) {
|
|
type_attribute *ret;
|
|
int test;
|
|
|
|
for (test = 0; test < type->attribute_count; test++) {
|
|
if (!strcmp(type->attributes[test]->field, attribute)) {
|
|
ret = type->attributes[test];
|
|
if (clean) {
|
|
free(ret->name);
|
|
ret->name = NULL;
|
|
free(ret->description);
|
|
ret->description = NULL;
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
ret = calloc(1, sizeof(type_attribute));
|
|
ret->field = strdup(attribute);
|
|
|
|
type->attribute_count++;
|
|
type->attributes = realloc(type->attributes, type->attribute_count*sizeof(type_attribute *));
|
|
type->attributes[type->attribute_count-1] = ret;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void copy_attributes(const type_definition *source, type_definition *type) {
|
|
int attr;
|
|
type_attribute *add;
|
|
|
|
assert(source);
|
|
if (source->attribute_count == 0)
|
|
return;
|
|
|
|
for (attr = 0; attr < source->attribute_count; attr++) {
|
|
add = get_attribute_for_type(type, source->attributes[attr]->field, 1);
|
|
add->name = strdup(source->attributes[attr]->name);
|
|
if (source->attributes[attr]->description)
|
|
add->description = strdup(source->attributes[attr]->description);
|
|
}
|
|
}
|
|
|
|
void copy_default_attributes(type_definition *type) {
|
|
if (!default_type)
|
|
return;
|
|
copy_attributes(default_type, type);
|
|
}
|
|
|
|
/**
|
|
* Returns a new type_definition having the default attributes.
|
|
*/
|
|
type_definition *get_type_definition(void) {
|
|
type_definition *ret = calloc(1, sizeof(type_definition));
|
|
|
|
ret->attribute_count = 0;
|
|
ret->attributes = NULL;
|
|
assert(ret->description == NULL);
|
|
|
|
if (default_type)
|
|
copy_default_attributes(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Used for type import.
|
|
*/
|
|
type_definition *find_type_definition(const char *name) {
|
|
int type;
|
|
|
|
for (type = 0; type < type_count; type++) {
|
|
if (!strcmp(types[type]->name, name))
|
|
return types[type];
|
|
}
|
|
printf("type not found: %s\n", name);
|
|
return NULL;
|
|
}
|
|
|
|
/** To sort attributes. */
|
|
int sort_type_attribute(const void *a, const void *b) {
|
|
const type_attribute **la = (const type_attribute **)a;
|
|
const type_attribute **lb = (const type_attribute **)b;
|
|
|
|
return strcmp((*la)->name, (*lb)->name);
|
|
}
|
|
|
|
ignore_list *find_ignore_list(const char *name) {
|
|
int list;
|
|
|
|
for (list = 0; list < list_count; list++) {
|
|
if (strcmp(lists[list]->name, name) == 0)
|
|
return lists[list];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @todo remove spaces at line start/end.
|
|
*/
|
|
char *read_line(char *buffer, int size, FILE *file) {
|
|
return fgets(buffer, 200, file);
|
|
}
|
|
|
|
/** Remove an attribute from the type. */
|
|
void ignore_attribute(type_definition *type, const char *attribute) {
|
|
int find;
|
|
|
|
for (find = 0; find < type->attribute_count; find++) {
|
|
if (!strcmp(attribute, type->attributes[find]->field)) {
|
|
/*printf("rem %s from %s\n", list->fields[attr], type->name);*/
|
|
free_attribute(type->attributes[find]);
|
|
if (find < type->attribute_count-1)
|
|
type->attributes[find] = type->attributes[type->attribute_count-1];
|
|
type->attribute_count--;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Remove all attributes in the specified list from the type. */
|
|
void ignore_attributes(type_definition *type, ignore_list *list) {
|
|
int attr;
|
|
|
|
if (!list) {
|
|
printf("empty ignore list?\n");
|
|
return;
|
|
}
|
|
|
|
for (attr = 0; attr < list->count; attr++) {
|
|
ignore_attribute(type, list->fields[attr]);
|
|
}
|
|
}
|
|
|
|
/** Add a required parameter to the specified type. buf is the line read from the file, non processed. */
|
|
void add_required_parameter(type_definition *type, const char *buf) {
|
|
char *sn, *en, *sv, *ev;
|
|
char value[200], name[200], temp[200];
|
|
const flag_definition *flag;
|
|
|
|
if (type == fallback_type)
|
|
/* the "Misc" type has dummy requirements, don't take that into account. */
|
|
return;
|
|
|
|
sn = strstr(buf, "arch");
|
|
if (!sn)
|
|
return;
|
|
sn = strchr(sn, '"');
|
|
en = strchr(sn+1, '"');
|
|
sv = strstr(buf, "value");
|
|
sv = strchr(sv, '"');
|
|
ev = strchr(sv+1, '"');
|
|
|
|
name[en-sn-1] = '\0';
|
|
strncpy(name, sn+1, en-sn-1);
|
|
value[ev-sv-1] = '\0';
|
|
strncpy(value, sv+1, ev-sv-1);
|
|
|
|
type->require_count++;
|
|
type->required = realloc(type->required, type->require_count*sizeof(char *));
|
|
|
|
flag = find_flag(name);
|
|
if (flag)
|
|
snprintf(temp, 200, "@ref %s %s", flag->code_name, strcmp(value, "0") ? "set" : "unset");
|
|
else
|
|
snprintf(temp, 200, "@ref object::%s = %s", name, value);
|
|
type->required[type->require_count-1] = strdup(temp);
|
|
}
|
|
|
|
/** Read all lines related to a type, stop when "block_end" is found on a line. */
|
|
void read_type(type_definition *type, FILE *file, const char *block_end) {
|
|
char buf[200], tmp[200];
|
|
char *find, *end;
|
|
type_attribute *attr;
|
|
|
|
while (read_line(buf, 200, file)) {
|
|
if (strstr(buf, block_end) != NULL) {
|
|
if (type->attribute_count)
|
|
qsort(type->attributes, type->attribute_count, sizeof(type_attribute *), sort_type_attribute);
|
|
return;
|
|
}
|
|
if (strstr(buf, "<description>") != NULL) {
|
|
while (read_line(buf, 200, file)) {
|
|
if (strstr(buf, "</description>") != NULL)
|
|
break;
|
|
|
|
if (type->description) {
|
|
type->description = realloc(type->description, strlen(type->description)+strlen(buf)+1);
|
|
strcat(type->description, buf);
|
|
}
|
|
else
|
|
type->description = strdup(buf);
|
|
}
|
|
find = strstr(type->description, "]]>");
|
|
if (find)
|
|
type->description[find-type->description] = '\0';
|
|
while (type->description[strlen(type->description)-1] == '\n')
|
|
type->description[strlen(type->description)-1] = '\0';
|
|
/*printf(" => desc = %s\n", type->description);*/
|
|
}
|
|
|
|
if (strstr(buf, "<ignore_list") != NULL) {
|
|
find = strstr(buf, "name=");
|
|
if (!find)
|
|
return;
|
|
find = strchr(find+1, '"');
|
|
if (!find)
|
|
return;
|
|
end = strchr(find+1, '"');
|
|
if (!end)
|
|
return;
|
|
tmp[end-find-1] = '\0';
|
|
strncpy(tmp, find+1, end-find-1);
|
|
ignore_attributes(type, find_ignore_list(tmp));
|
|
}
|
|
|
|
if (strstr(buf, "<ignore>") != NULL) {
|
|
while (read_line(buf, 200, file)) {
|
|
if (strstr(buf, "</ignore>") != NULL)
|
|
break;
|
|
find = strstr(buf, "arch=");
|
|
if (!find)
|
|
continue;
|
|
find = strchr(find+1, '"');
|
|
if (!find)
|
|
continue;
|
|
end = strchr(find+1, '"');
|
|
if (!end)
|
|
continue;
|
|
tmp[end-find-1] = '\0';
|
|
strncpy(tmp, find+1, end-find-1);
|
|
ignore_attribute(type, tmp);
|
|
}
|
|
}
|
|
|
|
if (strstr(buf, "<required>") != NULL) {
|
|
while (read_line(buf, 200, file)) {
|
|
if (strstr(buf, "</required>") != NULL)
|
|
break;
|
|
add_required_parameter(type, buf);
|
|
}
|
|
}
|
|
|
|
if (strstr(buf, "<import_type") != NULL) {
|
|
type_definition *import;
|
|
|
|
find = strstr(buf, "name=");
|
|
if (!find)
|
|
return;
|
|
find = strchr(find+1, '"');
|
|
if (!find)
|
|
return;
|
|
end = strchr(find+1, '"');
|
|
if (!end)
|
|
return;
|
|
tmp[end-find-1] = '\0';
|
|
strncpy(tmp, find+1, end-find-1);
|
|
import = find_type_definition(tmp);
|
|
if (import) {
|
|
/*printf("%s import %s\n", type->name, tmp);*/
|
|
copy_attributes(import, type);
|
|
}
|
|
else
|
|
printf("%s: import %s not found\n", type->name, tmp);
|
|
}
|
|
|
|
if (strstr(buf, "<attribute") != NULL) {
|
|
if (strstr(buf, "/>") != NULL)
|
|
continue;
|
|
find = strstr(buf, "arch");
|
|
if (!find)
|
|
continue;
|
|
find = strchr(find, '"');
|
|
end = strchr(find+1, '"');
|
|
if (end == find+1)
|
|
/* empty arch, meaning inventory or such, ignore. */
|
|
continue;
|
|
|
|
tmp[end-find-1] = '\0';
|
|
strncpy(tmp, find+1, end-find-1);
|
|
/*printf(" => attr %s\n", tmp);*/
|
|
|
|
attr = get_attribute_for_type(type, tmp, 1);
|
|
|
|
find = strstr(buf, "editor");
|
|
find = strchr(find, '"');
|
|
end = strchr(find+1, '"');
|
|
tmp[end-find-1] = '\0';
|
|
strncpy(tmp, find+1, end-find-1);
|
|
attr->name = strdup(tmp);
|
|
|
|
while (read_line(buf, 200, file)) {
|
|
if (strstr(buf, "</attribute>") != NULL)
|
|
break;
|
|
if (attr->description) {
|
|
attr->description = realloc(attr->description, strlen(attr->description)+strlen(buf)+1);
|
|
strcat(attr->description, buf);
|
|
}
|
|
else
|
|
attr->description = strdup(buf);
|
|
}
|
|
if (attr->description)
|
|
while (attr->description[strlen(attr->description)-1] == '\n')
|
|
attr->description[strlen(attr->description)-1] = '\0';
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
void dump_type(type_definition *type) {
|
|
int attr;
|
|
|
|
printf("type: %s [%d]\n", type->name, type->number);
|
|
printf(" attributes:\n");
|
|
for (attr = 0; attr < type->attribute_count; attr++) {
|
|
printf(" %30s: %s\n", type->attributes[attr]->field, type->attributes[attr]->name);
|
|
printf(" %s\n", type->attributes[attr]->description);
|
|
}
|
|
}
|
|
|
|
void dump_types(void) {
|
|
int t;
|
|
type_definition *type;
|
|
|
|
for (t = 0; t < type_count; t++) {
|
|
type = types[t];
|
|
dump_type(type);
|
|
}
|
|
}
|
|
|
|
/** Get an attribute, create it if it doesn't exist yet. */
|
|
attribute_definition *get_attribute(const char *name) {
|
|
int attr;
|
|
attribute_definition *ret;
|
|
|
|
for (attr = 0; attr < attribute_count; attr++) {
|
|
if (!strcmp(attributes[attr]->field, name))
|
|
return attributes[attr];
|
|
}
|
|
|
|
ret = calloc(1, sizeof(attribute_definition));
|
|
attribute_count++;
|
|
attributes = realloc(attributes, attribute_count*sizeof(attribute_definition *));
|
|
attributes[attribute_count-1] = ret;
|
|
|
|
ret->field = strdup(name);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/** Gets a type description for specified attribute, create it if doesn't exist. */
|
|
attribute_type *get_description_for_attribute(attribute_definition *attribute, const char *description) {
|
|
int desc;
|
|
attribute_type *add;
|
|
|
|
for (desc = 0; desc < attribute->type_count; desc++) {
|
|
if (!description && !attribute->types[desc]->description)
|
|
return attribute->types[desc];
|
|
if (description && attribute->types[desc]->description && !strcmp(description, attribute->types[desc]->description))
|
|
return attribute->types[desc];
|
|
}
|
|
|
|
add = calloc(1, sizeof(attribute_type));
|
|
attribute->type_count++;
|
|
attribute->types = realloc(attribute->types, attribute->type_count*sizeof(attribute_type));
|
|
attribute->types[attribute->type_count-1] = add;
|
|
|
|
if (description)
|
|
add->description = strdup(description);
|
|
|
|
return add;
|
|
}
|
|
|
|
void add_type_to_attribute(attribute_definition *attribute, type_definition *type, int attr) {
|
|
attribute_type *att;
|
|
|
|
att = get_description_for_attribute(attribute, type->attributes[attr]->description);
|
|
att->count++;
|
|
att->type = realloc(att->type, att->count*sizeof(const char *));
|
|
att->number = realloc(att->number, att->count*sizeof(int));
|
|
att->type[att->count-1] = strdup(type->name);
|
|
att->number[att->count-1] = type->number;
|
|
}
|
|
|
|
/** Read the contents of a <code>\<ignore_list\></code> tag. */
|
|
void read_ignore_list(const char *name, FILE *file) {
|
|
char buf[200], tmp[200];
|
|
char *start, *end;
|
|
ignore_list *list;
|
|
|
|
/*printf("il %s:", name);*/
|
|
list = calloc(1, sizeof(ignore_list));
|
|
list_count++;
|
|
lists = realloc(lists, list_count*sizeof(ignore_list *));
|
|
lists[list_count-1] = list;
|
|
list->name = strdup(name);
|
|
|
|
while (read_line(buf, 200, file)) {
|
|
if (strstr(buf, "</ignore_list>") != NULL) {
|
|
/*printf("\n");*/
|
|
return;
|
|
}
|
|
start = strstr(buf, "arch=");
|
|
if (!start)
|
|
continue;
|
|
start = strchr(start+1, '"');
|
|
if (!start)
|
|
continue;
|
|
end = strchr(start+1, '"');
|
|
if (!end)
|
|
continue;
|
|
|
|
tmp[end-start-1] = '\0';
|
|
strncpy(tmp, start+1, end-start-1);
|
|
/*printf(" %s", tmp);*/
|
|
|
|
list->count++;
|
|
list->fields = realloc(list->fields, list->count*sizeof(char *));
|
|
list->fields[list->count-1] = strdup(tmp);
|
|
}
|
|
}
|
|
|
|
void dump_ignore_lists(void) {
|
|
int list, field;
|
|
|
|
printf("ignore lists:\n");
|
|
for (list = 0; list < list_count; list++) {
|
|
printf(" %s:", lists[list]->name);
|
|
for (field = 0; field < lists[list]->count; field++)
|
|
printf(" %s", lists[list]->fields[field]);
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
/** Fields part of the living structure. */
|
|
static const char *in_living[] = {
|
|
"Str",
|
|
"Dex",
|
|
"Con",
|
|
"Wis",
|
|
"Cha",
|
|
"Int",
|
|
"Pow",
|
|
"wc",
|
|
"ac",
|
|
"hp",
|
|
"maxhp",
|
|
"sp",
|
|
"maxsp",
|
|
"grace",
|
|
"maxgrace",
|
|
"exp",
|
|
"food",
|
|
"dam",
|
|
"luck",
|
|
NULL
|
|
};
|
|
|
|
/** Custom attributes we know about, to point to the right page. */
|
|
static const char *custom_attributes[] = {
|
|
/* transports */
|
|
"weight_speed_ratio",
|
|
"base_speed",
|
|
"passenger_limit",
|
|
"face_full",
|
|
"anim_full",
|
|
NULL
|
|
};
|
|
|
|
int is_custom_attribute(const char *attribute) {
|
|
int val;
|
|
|
|
for (val = 0; custom_attributes[val] != NULL; val++) {
|
|
if (!strcmp(custom_attributes[val], attribute)) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/** Write the part to the right of a \@ref for the specified attribute. */
|
|
void write_attribute_reference(const char *attribute, FILE *file) {
|
|
const flag_definition *flag = find_flag(attribute);
|
|
int val;
|
|
|
|
if (flag) {
|
|
fprintf(file, "%s", flag->code_name);
|
|
return;
|
|
}
|
|
for (val = 0; in_living[val] != NULL; val++) {
|
|
if (!strcmp(in_living[val], attribute)) {
|
|
fprintf(file, "liv::%s", attribute);
|
|
return;
|
|
}
|
|
}
|
|
if (is_custom_attribute(attribute)) {
|
|
fprintf(file, "page_custom_attributes \"%s\"", attribute);
|
|
return;
|
|
}
|
|
if (strstr(attribute, "resist_")) {
|
|
fprintf(file, "obj::resist");
|
|
return;
|
|
}
|
|
if (!strcmp(attribute, "connected")) {
|
|
fprintf(file, "page_connected \"connection value\"");
|
|
return;
|
|
}
|
|
fprintf(file, "obj::%s", attribute);
|
|
}
|
|
|
|
/** Write a type definition file. */
|
|
void write_type_file(type_definition *type) {
|
|
FILE *file;
|
|
char buf[200];
|
|
int attr, req;
|
|
|
|
snprintf(buf, 200, "%s/%s/type_%d.dox", destination_dir, type_dir, type->number);
|
|
file = fopen(buf, "w+");
|
|
|
|
fprintf(file, "/**\n");
|
|
|
|
/* auto-generate documentation for the type, so no need to change define.h */
|
|
if (type->number > 0) {
|
|
for (req = 0; type_names[req].code_name != NULL; req++) {
|
|
if (type_names[req].value == type->number) {
|
|
fprintf(file, "@var %s\nSee @ref page_type_%d\n*/\n\n/**\n", type_names[req].qcode_name, type->number);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
fprintf(file, "@page page_type_%d %s\n\n", type->number, type->name);
|
|
fprintf(file, "\n@section Description\n");
|
|
fprintf(file, "%s\n\n", type->description);
|
|
if (type != fallback_type) {
|
|
fprintf(file, "\n\nType defined by:\n");
|
|
if (type->number && type->number < OBJECT_TYPE_MAX)
|
|
fprintf(file, "- @ref object::type = %d\n", type->number);
|
|
for (req = 0; req < type->require_count; req++)
|
|
fprintf(file, "- %s\n", type->required[req]);
|
|
}
|
|
|
|
fprintf(file, "\n\n@section Attributes\n\n");
|
|
fprintf(file, "<table>\n\t<tr>\n\t\t<th>Attribute</th>\n\t\t<th>Field</th>\n\t\t<th>Description</th>\n\t</tr>\n");
|
|
for (attr = 0; attr < type->attribute_count; attr++) {
|
|
fprintf(file, "\t<tr>\n\t\t<td>%s</td>\n\t\t<td>@ref ", type->attributes[attr]->name);
|
|
write_attribute_reference(type->attributes[attr]->field, file);
|
|
fprintf(file, "</td>\n\t\t<td>%s\n\t\t</td>\n\t</tr>\n", type->attributes[attr]->description ? type->attributes[attr]->description : "(no description)");
|
|
}
|
|
|
|
fprintf(file, "</table>\n*/\n");
|
|
|
|
fclose(file);
|
|
}
|
|
|
|
/** Write index of all types. */
|
|
void write_type_index(void) {
|
|
FILE *index;
|
|
int type;
|
|
char buf[200];
|
|
|
|
snprintf(buf, 200, "%s/%s/types.dox", destination_dir, type_dir);
|
|
index = fopen(buf, "w+");
|
|
fprintf(index, "/**\n@page type_index Type index\n");
|
|
|
|
fprintf(index, "Types not listed here have the attributes defined in @ref page_type_0 \"this page\".\n\n");
|
|
|
|
for (type = 0; type < type_count; type++) {
|
|
fprintf(index, "-@ref page_type_%d \"%s\"\n", types[type]->number, types[type]->name);
|
|
}
|
|
|
|
fprintf(index, "*/\n");
|
|
|
|
fclose(index);
|
|
}
|
|
|
|
/** Write the description of a field. */
|
|
void write_attribute_file(attribute_definition *attribute) {
|
|
FILE *file;
|
|
char buf[200];
|
|
int type, desc;
|
|
const char *end;
|
|
|
|
snprintf(buf, 200, "%s/%s/field_%s.dox", destination_dir, field_dir, attribute->field);
|
|
file = fopen(buf, "w+");
|
|
|
|
fprintf(file, "/**\n@fn ");
|
|
write_attribute_reference(attribute->field, file);
|
|
|
|
/* resistances are special, they'll be merged in the obj::resist paragraph, so specify the name. */
|
|
if (strstr(attribute->field, "resist_"))
|
|
fprintf(file, "\n@section %s %s resistance\n", attribute->field, attribute->field+7);
|
|
else
|
|
fprintf(file, "\n@section Use\n");
|
|
|
|
fprintf(file, "<table>\n\t<tr>\n\t\t<th>Type(s)</th>\n\t\t<th>Description</th>\n\t</tr>");
|
|
|
|
for (desc = 0; desc < attribute->type_count; desc++) {
|
|
assert(attribute->types[desc]->count > 0);
|
|
|
|
fprintf(file, "\t<tr>\n\t\t<td>\n");
|
|
|
|
for (type = 0; type < attribute->types[desc]->count; type++) {
|
|
if (type < attribute->types[desc]->count-1)
|
|
end = ", ";
|
|
else
|
|
end = "\n";
|
|
fprintf(file, "@ref page_type_%d%s", attribute->types[desc]->number[type], end);
|
|
}
|
|
fprintf(file, "\t\t</td><td>%s</td>\n\t</tr>\n", attribute->types[desc]->description ? attribute->types[desc]->description : "(no description)");
|
|
}
|
|
|
|
fprintf(file, "\n*/\n");
|
|
|
|
fclose(file);
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
FILE *xml;
|
|
int number, attr, dummy;
|
|
char buf[200], tmp[200];
|
|
char *start, *end;
|
|
type_definition *type;
|
|
|
|
if (argc < 2) {
|
|
printf("Syntax: %s /path/to/Gridarta/types.xml\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
/* dummy type number for special types. */
|
|
dummy = OBJECT_TYPE_MAX+50;
|
|
|
|
xml = fopen(argv[1], "r");
|
|
while (read_line(buf, 200, xml) != NULL) {
|
|
if (buf[0] == '#')
|
|
continue;
|
|
if (strstr(buf, "<default_type>")) {
|
|
default_type = get_type_definition();
|
|
default_type->name = strdup("(default type)");
|
|
read_type(default_type, xml, "</default_type>");
|
|
continue;
|
|
}
|
|
|
|
if (strstr(buf, "<ignore_list") != NULL) {
|
|
start = strstr(buf, "name=");
|
|
start = strchr(start+1, '"');
|
|
end = strchr(start+1, '"');
|
|
tmp[end-start-1] = '\0';
|
|
strncpy(tmp, start+1, end-start-1);
|
|
read_ignore_list(tmp, xml);
|
|
continue;
|
|
}
|
|
|
|
start = strstr(buf, "<type number");
|
|
if (start) {
|
|
start = strchr(start, '"');
|
|
/*if (!start)
|
|
break;*/
|
|
end = strchr(start+1, '"');
|
|
/*if (!end)
|
|
break;*/
|
|
tmp[end-start-1] = '\0';
|
|
strncpy(tmp, start+1, end-start-1);
|
|
/*printf("type %s ", tmp);*/
|
|
|
|
number = atoi(tmp);
|
|
start = strstr(end, "name=");
|
|
start = strchr(start, '"');
|
|
end = strchr(start+1, '"');
|
|
tmp[end-start-1] = '\0';
|
|
strncpy(tmp, start+1, end-start-1);
|
|
|
|
if (!strcmp(tmp, "Misc")) {
|
|
fallback_type = get_type_definition();
|
|
type = fallback_type;
|
|
}
|
|
else {
|
|
if (number == 0)
|
|
number = dummy++;
|
|
type = get_type_definition();
|
|
type_count++;
|
|
types = realloc(types, type_count*sizeof(type_definition *));
|
|
types[type_count-1] = type;
|
|
}
|
|
|
|
#if 0
|
|
if (!number || number >= OBJECT_TYPE_MAX || types[number] != NULL) {
|
|
/*printf("=> skip\n");*/
|
|
while (read_line(buf, 200, xml) != NULL && strstr(buf, "</type>") == NULL)
|
|
/*printf(" => skip %s\n", buf)*/;
|
|
/*printf(" => end of skip\n");*/
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
type->number = number;
|
|
|
|
/*printf("nom %s\n", tmp);*/
|
|
type->name = strdup(tmp);
|
|
|
|
read_type(type, xml, "</type>");
|
|
}
|
|
}
|
|
|
|
if (fallback_type->description)
|
|
free(fallback_type->description);
|
|
fallback_type->description = strdup("This type regroups all types who don't have a specific definition.");
|
|
|
|
for (number = 0; number < type_count; number++) {
|
|
for (attr = 0; attr < types[number]->attribute_count; attr++)
|
|
add_type_to_attribute(get_attribute(types[number]->attributes[attr]->field), types[number], attr);
|
|
}
|
|
|
|
/* dump_types();*/
|
|
/* dump_type(default_type);*/
|
|
/* dump_ignore_lists();*/
|
|
|
|
write_type_index();
|
|
for (number = 0; number < type_count; number++)
|
|
write_type_file(types[number]);
|
|
write_type_file(fallback_type);
|
|
|
|
for (attr = 0; attr < attribute_count; attr++)
|
|
if (!is_custom_attribute(attributes[attr]->field))
|
|
write_attribute_file(attributes[attr]);
|
|
|
|
fclose(xml);
|
|
free(types);
|
|
return 0;
|
|
}
|