[Crossfire-wiki] [Crossfire DokuWiki] page added: user:mhoram:code:bwp
no-reply_wiki at metalforge.org
no-reply_wiki at metalforge.org
Tue Jan 16 21:40:48 CST 2007
A page in your DokuWiki was added or changed. Here are the details:
Date : 2007/01/16 21:40
User : mhoram
Edit Summary: created, can anyone help debug?
====== Build Wiki Pages ======
This is a program I'm working on to automate the building of pages for the wiki from the monster archetypes (and eventually other categories, perhaps). I'm still working on it, but I thought I'd put it here in case anyone wanted to help me fix a problem I'm having, or critique my code. (I'm still very much an amateur at writing C.)
The main problem I'm having right now is that a couple variables are getting clobbered; I assume by bad memory allocation. It happens at line 502: the variables *key[] and *val[] are fine before that call to join_with_comma(), but after it they are messed up. (Search for BEFORE and AFTER.) Since they aren't involved in that function at all, I assume that something in that function is causing an overflow. There's probably a way with gdb or some other tool to see what variable is using a particular memory address, but I don't know what it is.
Also, if anyone knows of a good C primer/tutorial online, please let me know.
===== Notes & Comments =====
===== Code =====
<code c>
/*
* bwp - build wiki pages
*
* This program will sort out all monster archetypes and print wiki pages
* for them, named 'a' through 'z'. It uses some *_template subroutines taken
* from Ryo's mapper.c. It should compile if installed in server/trunk/utils.
* Please direct all suggestions or corrections to aaron at baugher.biz (or
* Mhoram on #crossfire).
*
* Compile command: gcc -g -pg -O0 bwp.c -I../include ../common/libcross.a ../socket/libsocket.a -o bwp -lz -lcrypt -lm
*/
/*
CrossFire, A Multiplayer game for X-windows
Copyright (C) 2002-2006 Mark Wedel & Crossfire Development Team
Copyright (C) 1992 Frank Tore Johansen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
The authors can be reached via e-mail at crossfire-devel at real-time.com
*/
#define LO_NEWFILE 2
#define MAX_SIZE 64
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <global.h>
char* monster_page; /* A single wiki page of monsters */
char* monster_entry; /* A single monster entry */
char* monster_canuse_row; /* Can_use table row */
char* monster_protected_row; /* Protected table row */
char* monster_vulnerable_row; /* Vulnerable table row */
char* monster_special_row; /* Special table row */
char* monster_attack_row; /* Attack types table row */
char* monster_lore_row; /* Lore table row */
typedef struct string_array {
sint16 count;
sint16 longest;
char** item;
} String_Array;
/**
* This is a list of pointers that correspond to the FLAG_.. values.
* This is a simple 1:1 mapping - if FLAG_FRIENDLY is 15, then
* the 15'th element of this array should match that name.
* If an entry is NULL, that is a flag not to loaded/saved.
*
* Copied from common/loader.c; perhaps should be defined elsewhere?
*
*/
const char *const flag_names[NUM_FLAGS+1] = {
"alive", "wiz", NULL, NULL, "was_wiz", "applied", "unpaid",
"can_use_shield", "no_pick", "client_anim_sync", "client_anim_random", /* 10 */
"is_animated", NULL /* slow_move */,
NULL /* flying */, "monster", "friendly", "generator",
"is_thrown", "auto_apply", "treasure", "player sold", /* 20 */
"see_invisible", "can_roll", "overlay_floor",
"is_turnable", NULL /* walk_off */, NULL /* fly_on */,
NULL /*fly_off*/, "is_used_up", "identified", "reflecting", /* 30 */
"changing", "splitting", "hitback", "startequip",
"blocksview", "undead", "scared", "unaggressive",
"reflect_missile", "reflect_spell", /* 40 */
"no_magic", "no_fix_player", "is_lightable", "tear_down",
"run_away", NULL /*pass_thru */, NULL /*can_pass_thru*/,
"pick_up", "unique", "no_drop", /* 50 */
NULL /* wizcast*/, "can_cast_spell", "can_use_scroll", "can_use_range",
"can_use_bow", "can_use_armour", "can_use_weapon",
"can_use_ring", "has_ready_range", "has_ready_bow", /* 60 */
"xrays", NULL, "is_floor", "lifesave", "no_strength", "sleep",
"stand_still", "random_move", "only_attack", "confused", /* 70 */
"stealth", NULL, NULL, "cursed", "damned",
"see_anywhere", "known_magical", "known_cursed",
"can_use_skill", "been_applied", /* 80 */
"has_ready_scroll", "can_use_rod", NULL,
"can_use_horn", "make_invisible", "inv_locked", "is_wooded",
"is_hilly", "has_ready_skill", "has_ready_weapon", /* 90 */
"no_skill_ident", "is_blind", "can_see_in_dark", "is_cauldron",
"is_dust", "no_steal", "one_hit", NULL, "berserk", "neutral", /* 100 */
"no_attack", "no_damage", NULL, NULL, "activate_on_push",
"activate_on_release","is_water","use_content_on_gen",NULL,"is_buildable", /* 110 */
NULL, "blessed", "known_blessed"
};
/**
* Concatenates a string, and free concatenated string.
*
* @param source
* string to append to. Can be NULL.
* @param add
* string that is appened. Will be free()d after. Must not be NULL.
* @return
* new string that should be free()d by caller.
*/
static char* cat_template(char* source, char* add) {
if (!source)
return add;
source = realloc(source, strlen(source) + strlen(add) + 1);
strcat(source, add);
free(add);
return source;
}
/**
* Reads a file in memory.
*
* @param name
* file path to read.
* @param buffer
* where to store. Can be left uninitialized in case of errors.
* @return
* 1 if error, 0 else.
*/
static int read_template(const char* name, char** buffer) {
FILE* file;
size_t size;
struct stat info;
if (stat(name, &info)) {
printf("Couldn't stat template %s!\n", name);
return 1;
}
(*buffer) = calloc(1, info.st_size + 1);
if (!(*buffer)) {
printf("Template %s calloc failed!\n", name);
return 1;
}
if (info.st_size == 0) {
(*buffer)[0] = '\0';
return 0;
}
file = fopen(name, "rb");
if (!file) {
printf("Couldn't open template %s!\n", name);
free(*buffer);
return 1;
}
if (fread(*buffer, info.st_size, 1, file) != 1) {
printf("Couldn't read template %s!\n", name);
free(*buffer);
fclose(file);
return 1;
}
fclose(file);
return 0;
}
/**
* Processes a template.
*
* Variables in the form #VARIABLE# will be substituted for specified values.
*
* @param template
* template to process.
* @param vars
* variables to replace. Array must be NULL-terminated.
* @param values
* variables to replace by. Must be the same size as vars, no NULL element allowed.
* @return
* filled-in template, that must be free()d be caller. NULL if memory allocation error.
*
* @note
* returned string will be a memory block larger than required, for performance reasons.
*/
static char* do_template(const char* template, const char** vars, const char** values) {
int count = 0;
const char* sharp = template;
int maxlen = 0;
int var = 0;
char* result;
char* current_result;
const char* end;
while ((sharp = strchr(sharp, '#')) != NULL) {
sharp++;
count++;
}
if (!count)
return strdup(template);
if (count % 2) {
printf("Malformed template, mismatched #!\n");
return strdup(template);
}
while (vars[var] != NULL) {
if (strlen(values[var]) > maxlen)
maxlen = strlen(values[var]);
var++;
}
result = calloc(1, strlen(template) + maxlen * (count / 2) + 1);
if (!result)
return NULL;
current_result = result;
sharp = template;
while ((sharp = strchr(sharp, '#')) != NULL) {
end = strchr(sharp + 1, '#');
strncpy(current_result, template, sharp - template);
if (end == sharp+1) {
strcat(current_result, "#");
}
else {
current_result = current_result + strlen(current_result);
var = 0;
while (vars[var] != 0 && strncmp(vars[var], sharp + 1, end - sharp - 1))
var++;
if (vars[var] == 0)
printf("Wrong tag: %s\n", sharp);
else
strcpy(current_result, values[var]);
}
current_result = current_result + strlen(current_result);
sharp = end + 1;
template = sharp;
}
strcat(current_result, template);
return result;
}
/**** Mhoram's code starts here *****/
/**
* Sort values alphabetically
*
* Used by qsort to sort values alphabetically without regard to case
*
* @param a
* First value
*
* @param b
* Second value
*
*/
static int sortbyname(const void *a, const void *b){
char aa[MAX_SIZE];
char bb[MAX_SIZE];
int i;
strcpy(aa,a);
strcpy(bb,b);
for(i=0; i<sizeof(aa) && i < sizeof(bb); i++){
aa[i] = tolower(aa[i]);
bb[i] = tolower(bb[i]);
if(aa[i] != bb[i]){
return(aa[i]-bb[i]);
}
}
return 0;
}
/**
* Appends a string to another separated by a comma and space.
*
* Appends the second argument to the first argument
*
* @param old
* Old string
*
* @param new
* New string to append to old
*
*/
static const char* append_with_comma(const char* old, const char* new){
LOG(llevError, "%s + %s\n", old, new);
if( old == NULL ){
old = calloc(1, strlen(new)+1 );
} else {
old = realloc( old, strlen(old)+ strlen(new)+strlen(", ")+1 );
old = strncat( old, ", ", 2 );
}
old = strncat( old, new, strlen(new));
return(old);
}
void push(String_Array* array, const char* string){
sint16 i = array->count;
array->item[i] = calloc(1, strlen(string)+1);
strncpy(array->item[i], string, strlen(string));
if( strlen(array->item[i]) > array->longest ){
array->longest = strlen(array->item[i]);
}
fprintf(stderr, "Pushed array item %d: %s\n", array->count, array->item[i]);
array->count++;
}
/**
* Joins strings with a comma and space.
*
* Takes an array of strings and joins them togther with a comma and a space
* between each of them.
*
* @param String_Array
* Pointer to struct of type String_Array, containing strings to join
*
*/
const char* join_with_comma(String_Array* array){
char *newtext = "";
int i;
fprintf(stderr, "JOIN: %d %d %s\n", array->count, array->longest, array->item[0]);
newtext = calloc(1,1);
qsort(&array->item, array->count, array->longest, sortbyname);
for(i=0;i<array->count;i++ ){
if(i){
newtext = realloc( newtext, strlen(newtext) + strlen(", ") +1 );
newtext = strncat( newtext, ", ", 2 );
}
newtext = realloc( newtext, strlen(newtext) + strlen(array->item[i]) +1 );
fprintf(stderr, "Reallocated %d bytes for newtext\n", strlen(newtext) + strlen(array->item[i]) +1 );
newtext = strncat( newtext, array->item[i], strlen(array->item[i]));
}
fprintf(stderr, "JOINED: %s\n", newtext);
return newtext;
}
int main(int argc, char *argv[]) {
archetype *at;
int archnum=0;
int archmem=0;
char archnames[4000][MAX_SIZE];
int i;
char letter;
char last_letter;
char *wiki_page=NULL;
char *monster_entries=NULL;
FILE *fp;
const char *wikidir = "/tmp"; /* Should change this to come from command line? */
char wikifile[40];
init_globals();
init_library();
init_archetypes();
init_artifacts();
init_formulae();
init_readable();
init_gods();
/* Initialize templates */
if (read_template("templates/wiki/monster_page", &monster_page))
return;
if (read_template("templates/wiki/monster_page", &monster_page))
return;
if (read_template("templates/wiki/monster_entry", &monster_entry))
return;
if (read_template("templates/wiki/monster_canuse_row",
&monster_canuse_row))
return;
if (read_template("templates/wiki/monster_protected_row",
&monster_protected_row))
return;
if (read_template("templates/wiki/monster_vulnerable_row",
&monster_vulnerable_row))
return;
if (read_template("templates/wiki/monster_special_row",
&monster_special_row))
return;
if (read_template("templates/wiki/monster_attack_row",
&monster_attack_row))
return;
if (read_template("templates/wiki/monster_lore_row",
&monster_lore_row))
return;
/* Pick out the monster archetypes and sort them into an array */
for(at=first_archetype; at!=NULL; at=at->next){
if(QUERY_FLAG(&at->clone, FLAG_MONSTER) &&
QUERY_FLAG(&at->clone,FLAG_ALIVE)){
strcpy(archnames[archnum++], at->clone.name);
}
}
printf("Sorting...");
qsort(archnames, archnum, MAX_SIZE, sortbyname);
printf("done.\n");
for(i=0; i<archnum; i++) {
at=find_archetype_by_object_name(archnames[i]);
if(at){
letter = tolower(archnames[i][0]);
wikifile[0] = '\0';
LOG(llevInfo, "Doing archetype %s\n", archnames[i]);
snprintf(wikifile, sizeof(wikifile), "%s/%c", wikidir, letter);
printf("%s\n",wikifile);
if(letter != last_letter) {
/* add a monster entry */
char *canuse_row = "";
char *protected_row = "";
char *vulnerable_row = "";
char *special_row = "";
char *attack_row = "";
char *lore_row = "";
const char *key[] = {NULL, NULL, NULL, NULL, NULL, NULL};
const char *val[] = {NULL, NULL, NULL, NULL, NULL, NULL};
const int CANUSE_LENGTH = 16;
String_Array canuse;
String_Array resist;
String_Array vulner;
String_Array attack;
String_Array special;
/* Some flags that seemed useful; may need to add to this list.
* *special_names[] is used because some of the names in
* define.h are a bit awkward. Last one is negative to mark end.
*/
const sint8 special_flags[] = { 21, 93, 52, 38, 13, 32, 61, -1 };
const char *special_names[] = { "see invisible", "see in dark",
"spellcaster", "unaggressive",
"flying", "splitting", "x-ray vision"
};
int keycount = 0;
int j;
canuse.item = calloc(1,sizeof(const char*)*(CANUSE_LENGTH+1));
resist.item = calloc(1,sizeof(const char*)*(NROFATTACKS+1));
vulner.item = calloc(1,sizeof(const char*)*(NROFATTACKS+1));
attack.item = calloc(1,sizeof(const char*)*(NROFATTACKS+1));
special.item = calloc(1,sizeof(const char*)*(NROFATTACKS+1));
/* Do lore row */
if( at->clone.lore ) {
key[keycount] = "LORE";
key[keycount+1] = NULL;
val[keycount] = at->clone.lore;
keycount++;
lore_row = do_template(monster_lore_row, key, val);
}
/* Do canuse row */
canuse.count = 0;
keycount = 0;
for( j=1; j<= NUM_FLAGS; j++ ) {
if( QUERY_FLAG(&at->clone, j) &&
flag_names[j] &&
! strncmp(flag_names[j], "can_use_", 8) ) {
push(&canuse, flag_names[j]+8);
}
}
if( canuse.count ){
key[keycount] = "CANUSE";
key[keycount+1] = NULL;
val[keycount] = join_with_comma(&canuse);
canuse_row = do_template(monster_canuse_row, key, val);
}
/* Do protected/vulnerable rows */
resist.count = 0;
vulner.count = 0;
for( j=0; j<=NROFATTACKS; j++ ) {
if( at->clone.resist[j] && attacktype_desc[j] ){
char rowtext[32];
if( at->clone.resist[j] < 0 ){
sprintf(rowtext, "%s %i",
attacktype_desc[j], at->clone.resist[j]);
push(&vulner, rowtext);
} else {
sprintf(rowtext, "%s +%i",
attacktype_desc[j], at->clone.resist[j]);
push(&resist, rowtext);
}
}
}
keycount = 0;
if( resist.count ){
key[keycount] = "PROTECTED";
key[keycount+1] = NULL;
fprintf(stderr, "BEFORE: %s\n", key[keycount]);
val[keycount] = join_with_comma(&resist);
fprintf(stderr, "AFTER: %s\n", key[keycount]);
protected_row = do_template(monster_protected_row, key, val);
}
keycount = 0;
if( vulner.count ){
key[keycount] = "VULNERABLE";
key[keycount+1] = NULL;
val[keycount] = join_with_comma(&vulner);
vulnerable_row = do_template(monster_vulnerable_row, key, val);
}
/* Do attacktype row */
attack.count = 0;
keycount = 0;
val[keycount]=NULL;
for( j=0; j<=NROFATTACKS; j++ ) {
if( at->clone.attacktype & (1U << j)) {
push(&attack, attacktype_desc[j]);
}
}
if( attack.count ){
key[keycount] = "ATTACKS";
key[keycount+1] = NULL;
val[keycount] = join_with_comma(&attack);
attack_row = do_template(monster_attack_row, key, val);
}
/* Do special row */
special.count = 0;
keycount = 0;
val[keycount]=NULL;
for( j=0; special_flags[j] >= 0; j++ ) {
if( QUERY_FLAG(&at->clone, special_flags[j])){
push(&special, special_names[j]);
}
}
if( special.count ){
key[keycount] = "SPECIAL";
key[keycount+1] = NULL;
val[keycount] = join_with_comma(&special);
special_row = do_template(monster_special_row, key, val);
}
/* printf("%s%s%s%s%s\n",
canuse_row, protected_row, vulnerable_row, attack_row, special_row);
*/
if( i > 10 ){
exit(0); /* stop after test with ten monsters */
}
free(canuse.item);
free(resist.item);
free(vulner.item);
free(attack.item);
free(special.item);
} else {
}
/* fp = fopen(wikifile, "w");
if(! fp){
fprintf(stderr, "Unable to write to wiki file!\n");
exit(1);
}
fprintf(fp, "\n====== %s ======\n", at->clone.name);
fprintf(fp, "%d %d\n\n", at->tail_x, at->tail_y);
fclose(fp);
*/
} else {
printf("********* %d %s **********************\n",i, archnames[i]);
}
}
exit(0);
}
void set_map_timeout(void) {} /* doesn't need to do anything */
#include <global.h>
/* some plagarized code from apply.c--I needed just these two functions
without all the rest of the junk, so.... */
int auto_apply (object *op) {
object *tmp = NULL;
int i;
switch(op->type) {
case SHOP_FLOOR:
if (!HAS_RANDOM_ITEMS(op)) return 0;
do {
i=10; /* let's give it 10 tries */
while((tmp=generate_treasure(op->randomitems,op->stats.exp?
op->stats.exp:5))==NULL&&--i);
if(tmp==NULL)
return 0;
if(QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))
{
free_object(tmp);
tmp = NULL;
}
} while(!tmp);
tmp->x=op->x,tmp->y=op->y;
SET_FLAG(tmp,FLAG_UNPAID);
insert_ob_in_map(tmp,op->map,NULL,0);
CLEAR_FLAG(op,FLAG_AUTO_APPLY);
identify(tmp);
break;
case TREASURE:
if (HAS_RANDOM_ITEMS(op))
while ((op->stats.hp--)>0)
create_treasure(op->randomitems, op, GT_ENVIRONMENT,
op->stats.exp ? op->stats.exp :
op->map == NULL ? 14: op->map->difficulty,0);
remove_ob(op);
free_object(op);
break;
}
return tmp ? 1 : 0;
}
/* fix_auto_apply goes through the entire map (only the first time
* when an original map is loaded) and performs special actions for
* certain objects (most initialization of chests and creation of
* treasures and stuff). Calls auto_apply if appropriate.
*/
void fix_auto_apply(mapstruct *m) {
object *tmp,*above=NULL;
int x,y;
for(x=0;x<MAP_WIDTH(m);x++)
for(y=0;y<MAP_HEIGHT(m);y++)
for(tmp=get_map_ob(m,x,y);tmp!=NULL;tmp=above) {
above=tmp->above;
if(QUERY_FLAG(tmp,FLAG_AUTO_APPLY))
auto_apply(tmp);
else if(tmp->type==TREASURE) {
if (HAS_RANDOM_ITEMS(tmp))
while ((tmp->stats.hp--)>0)
create_treasure(tmp->randomitems, tmp, 0,
m->difficulty,0);
}
if(tmp && tmp->arch && tmp->type!=PLAYER && tmp->type!=TREASURE &&
tmp->randomitems){
if(tmp->type==CONTAINER) {
if (HAS_RANDOM_ITEMS(tmp))
while ((tmp->stats.hp--)>0)
create_treasure(tmp->randomitems, tmp, 0,
m->difficulty,0);
}
else if (HAS_RANDOM_ITEMS(tmp))
create_treasure(tmp->randomitems, tmp, GT_APPLY,
m->difficulty,0);
}
}
for(x=0;x<MAP_WIDTH(m);x++)
for(y=0;y<MAP_HEIGHT(m);y++)
for(tmp=get_map_ob(m,x,y);tmp!=NULL;tmp=tmp->above)
if (tmp->above
&& (tmp->type == TRIGGER_BUTTON || tmp->type == TRIGGER_PEDESTAL))
check_trigger(tmp,tmp->above);
}
/**
* Those are dummy functions defined to resolve all symboles.
* Added as part of glue cleaning.
* Ryo 2005-07-15
**/
void draw_ext_info(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *txt, const char *txt2){
fprintf(logfile, "%s\n", txt);
}
void draw_ext_info_format(
int flags, int pri, const object *pl, uint8 type,
uint8 subtype,
const char* new_format,
const char* old_format,
...){
va_list ap;
va_start(ap, old_format);
vfprintf(logfile, old_format, ap);
va_end(ap);
}
void ext_info_map(int color, const mapstruct *map, uint8 type, uint8 subtype, const char *str1, const char *str2){
fprintf(logfile, "ext_info_map: %s\n", str2);
}
void move_teleporter( object* ob){
}
void move_firewall( object* ob){
}
void move_duplicator( object* ob){
}
void move_marker( object* ob){
}
void move_creator( object* ob){
}
void emergency_save( int x ){
}
void clean_tmp_files( void ){
}
void esrv_send_item( object* ob, object* obx ){
}
void dragon_ability_gain( object* ob, int x, int y ){
}
void weather_effect( const char* c ){
}
void set_darkness_map( mapstruct* m){
}
void move_apply( object* ob, object* obt, object* obx ){
}
object* find_skill_by_number( object* ob, int x ){
return NULL;
}
void esrv_del_item(player *pl, int tag){
}
void esrv_update_spells(player *pl){
}
void monster_check_apply( object* ob, object* obt ){
}
void trap_adjust( object* ob, int x ){
}
int execute_event(object* op, int eventcode, object* activator, object* third, const char* message, int fix){
return 0;
}
int execute_global_event(int eventcode, ...){
return 0;
}
</code>
IP-Address : 206.71.197.56
Old Revision: none
New Revision: http://wiki.metalforge.net/doku.php/user:mhoram:code:bwp
--
This mail was generated by DokuWiki at
http://wiki.metalforge.net/
More information about the crossfire-wiki
mailing list