Crossfire Server, Trunk  1.75.0
hiscore.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
18 #ifndef _GNU_SOURCE
19 #define _GNU_SOURCE // strcasestr() is a GNU extension in string.h
20 #endif
21 
22 #include "global.h"
23 
24 #include <errno.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 
29 #include "sproto.h"
30 #include "output_file.h"
31 
35 struct score {
36  char name[BIG_NAME];
37  char title[BIG_NAME];
38  char killer[BIG_NAME];
39  int64_t exp;
41  int maxhp,
44  int position;
45 };
46 
50 struct score_table {
51  char fname[MAX_BUF];
54 };
55 
59 static score_table hiscore_tables[MAX_SKILLS + 1]; // One for each skill, plus one for overall
60 
71 static void put_score(const score *sc, char *buf, size_t size) {
72  snprintf(buf, size, "%s:%s:%" FMT64 ":%s:%s:%d:%d:%d", sc->name, sc->title, sc->exp, sc->killer, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
73 }
74 
81 static void hiscore_save(const score_table *table) {
82  FILE *fp;
83  OutputFile of;
84  size_t i;
85  char buf[MAX_BUF];
86 
87  fp = of_open(&of, table->fname);
88  if (fp == NULL)
89  return;
90 
91  for (i = 0; i < HIGHSCORE_LENGTH; i++) {
92  if (table->entry[i].name[0] == '\0')
93  break;
94 
95  put_score(&table->entry[i], buf, sizeof(buf));
96  fprintf(fp, "%s\n", buf);
97  }
98  of_close(&of);
99 }
100 
113 static int get_score(char *bp, score *sc) {
114  char *cp;
115  char *tmp[8];
116 
117  cp = strchr(bp, '\n');
118  if (cp != NULL)
119  *cp = '\0';
120 
121  if (split_string(bp, tmp, 8, ':') != 8)
122  return 0;
123 
124  strncpy(sc->name, tmp[0], BIG_NAME);
125  sc->name[BIG_NAME-1] = '\0';
126 
127  strncpy(sc->title, tmp[1], BIG_NAME);
128  sc->title[BIG_NAME-1] = '\0';
129 
130  sscanf(tmp[2], "%" FMT64, &sc->exp);
131 
132  strncpy(sc->killer, tmp[3], BIG_NAME);
133  sc->killer[BIG_NAME-1] = '\0';
134 
135  strncpy(sc->maplevel, tmp[4], BIG_NAME);
136  sc->maplevel[BIG_NAME-1] = '\0';
137 
138  sscanf(tmp[5], "%d", &sc->maxhp);
139 
140  sscanf(tmp[6], "%d", &sc->maxsp);
141 
142  sscanf(tmp[7], "%d", &sc->maxgrace);
143  return 1;
144 }
145 
158 static char *draw_one_high_score(const score *sc, char *buf, size_t size) {
159  const char *s1;
160  const char *s2;
161 
162  if (strcmp(sc->killer, "quit") == 0 || strcmp(sc->killer, "left") == 0) {
163  s1 = sc->killer;
164  s2 = "the game";
165  } else if (strcmp(sc->killer,"a dungeon collapse") == 0) {
166  s1 = "was last";
167  s2 = "seen";
168  } else {
169  s1 = "was killed by";
170  s2 = sc->killer;
171  }
172  snprintf(buf, size, "[fixed]%3d %10" FMT64 "[print] %s%s%s %s %s on map %s <%d><%d><%d>.",
173  sc->position, sc->exp, sc->name, sc->title[0]==',' ? "" : " ", sc->title, s1, s2, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
174  return buf;
175 }
176 
188 static void add_score(score_table *table, score *new_score, score *old_score) {
189  size_t i;
190 
191  new_score->position = HIGHSCORE_LENGTH+1;
192  memset(old_score, 0, sizeof(*old_score));
193  old_score->position = -1;
194 
195  /* find existing entry by name */
196  for (i = 0; i < HIGHSCORE_LENGTH; i++) {
197  if (table->entry[i].name[0] == '\0') {
198  table->entry[i] = *new_score;
199  table->entry[i].position = i+1;
200  break;
201  }
202  if (strcmp(new_score->name, table->entry[i].name) == 0) {
203  *old_score = table->entry[i];
204  if (table->entry[i].exp <= new_score->exp) {
205  table->entry[i] = *new_score;
206  table->entry[i].position = i+1;
207  }
208  break;
209  }
210  }
211 
212  if (i >= HIGHSCORE_LENGTH) {
213  /* entry for unknown name */
214 
215  if (new_score->exp < table->entry[i-1].exp) {
216  /* new exp is less than lowest hiscore entry => drop */
217  return;
218  }
219 
220  /* new exp is not less than lowest hiscore entry => add */
221  i--;
222  table->entry[i] = *new_score;
223  table->entry[i].position = i+1;
224  }
225 
226  /* move entry to correct position */
227  while (i > 0 && new_score->exp >= table->entry[i-1].exp) {
228  score tmp;
229 
230  tmp = table->entry[i-1];
231  table->entry[i-1] = table->entry[i];
232  table->entry[i] = tmp;
233 
234  table->entry[i-1].position = i;
235  table->entry[i].position = i+1;
236 
237  i--;
238  }
239 
240  new_score->position = table->entry[i].position;
241  hiscore_save(table);
242 }
243 
250 static void hiscore_load(score_table *table) {
251  FILE *fp;
252  size_t i;
253 
254  i = 0;
255 
256  fp = fopen(table->fname, "r");
257  if (fp == NULL) {
258  if (errno == ENOENT) {
259  LOG(llevDebug, "Highscore file %s does not exist\n", table->fname);
260  } else {
261  LOG(llevError, "Cannot open highscore file %s: %s\n", table->fname, strerror(errno));
262  }
263  } else {
264  LOG(llevDebug, "Reading highscore file %s\n", table->fname);
265  while (i < HIGHSCORE_LENGTH) {
266  char buf[MAX_BUF];
267 
268  if (fgets(buf, MAX_BUF, fp) == NULL) {
269  break;
270  }
271 
272  if (!get_score(buf, &table->entry[i]))
273  break;
274  table->entry[i].position = i+1;
275  i++;
276  }
277 
278  fclose(fp);
279  }
280 
281  while (i < HIGHSCORE_LENGTH) {
282  memset(&table->entry[i], 0, sizeof(table->entry[i]));
283  // This cannot be ++i due the right-to-left association of assignment.
284  table->entry[i].position = i + 1;
285  i++;
286  }
287 }
288 
296 void hiscore_init(void) {
297  char dirname[MAX_BUF];
298 
299  snprintf(dirname, sizeof(dirname), "%s/%s", settings.localdir, HIGHSCORE_DIR);
300 #ifdef WIN32
301  mkdir(dirname);
302 #else
303  mkdir(dirname,0755);
304 #endif
305  memset(hiscore_tables,0,sizeof(hiscore_tables));
306  for (int i =- 1; i < MAX_SKILLS; ++i) {
307  const char *name;
308  int subtype;
309 
310  /*
311  * This gets complicated because the skills are defined internally by the subtype in
312  * the skill object, but our list of skill names is in the order the skills are
313  * initialized in.
314  */
315  if (i == -1) {
316  name = "Overall";
317  subtype = 0;
318  } else {
319  name = skill_names[i];
320  if (!name || !*name) continue; // No such skill
321  subtype = get_skill_client_code(name) + 1;
322  }
323  snprintf(hiscore_tables[subtype].fname, sizeof(hiscore_tables[subtype].fname), "%s/%s/%s", settings.localdir, HIGHSCORE_DIR,name);
324  for ( char *c = hiscore_tables[subtype].fname; *c; ++c ) {
325  if (*c == ' ')
326  *c = '_'; /* avoid spaces in file names */
327  }
328  strncpy(hiscore_tables[subtype].skill_name, name, sizeof(hiscore_tables[subtype].skill_name));
329  hiscore_load(&hiscore_tables[subtype]);
330  }
331  /* Load legacy highscore file if new one was blank */
332  if (hiscore_tables[0].entry[0].exp == 0) {
333  snprintf(hiscore_tables[0].fname, sizeof(hiscore_tables[0].fname), "%s/%s", settings.localdir, OLD_HIGHSCORE);
335  }
336 }
337 
348 void hiscore_check(object *op, int quiet) {
349  score new_score;
350  score old_score;
351  char bufscore[MAX_BUF];
352  const char *message;
353 
354  if (op->stats.exp == 0 || !op->contr->name_changed)
355  return;
356 
357  if (QUERY_FLAG(op, FLAG_WAS_WIZ)) {
358  if (!quiet)
360  "Since you have been in wizard mode, "
361  "you can't enter the high-score list.");
362  return;
363  }
364  if (!op->stats.exp) {
365  if (!quiet)
367  "You don't deserve to save your character yet.");
368  return;
369  }
370 
371  PROFILE_BEGIN();
372  strncpy(new_score.name, op->name, BIG_NAME);
373  new_score.name[BIG_NAME-1] = '\0';
374  player_get_title(op->contr, new_score.title, sizeof(new_score.title));
375  strncpy(new_score.killer, op->contr->killer, BIG_NAME);
376  if (new_score.killer[0] == '\0') {
377  strcpy(new_score.killer, "a dungeon collapse");
378  }
379  new_score.killer[BIG_NAME-1] = '\0';
380  if (op->map == NULL) {
381  *new_score.maplevel = '\0';
382  } else {
383  strncpy(new_score.maplevel, op->map->name ? op->map->name : op->map->path, BIG_NAME-1);
384  new_score.maplevel[BIG_NAME-1] = '\0';
385  }
386  new_score.maxhp = (int)op->stats.maxhp;
387  new_score.maxsp = (int)op->stats.maxsp;
388  new_score.maxgrace = (int)op->stats.maxgrace;
389  FOR_INV_PREPARE(op, tmp) {
390  if (tmp->type != SKILL) continue;
391  if (!tmp->stats.exp) continue;
392  new_score.exp = tmp->stats.exp;
393  add_score(&hiscore_tables[get_skill_client_code(tmp->name) + 1], &new_score, &old_score);
394 #if 0
395  if (!quiet && new_score.exp > old_score.exp) {
397  "You improved your rating in %s: %" FMT64, tmp->name, new_score.exp);
398  if (old_score.position != -1)
400  draw_one_high_score(&old_score, bufscore, sizeof(bufscore)));
402  draw_one_high_score(&new_score, bufscore, sizeof(bufscore)));
403  }
404 #endif
405  } FOR_INV_FINISH();
406  new_score.exp = op->stats.exp;
407  add_score(&hiscore_tables[0], &new_score, &old_score); // overall
408  PROFILE_END(diff, LOG(llevDebug, "Wrote highscore files for %s (%ld ms)\n", op->name, diff/1000));
409 
410  /* Everything below here is just related to print messages
411  * to the player. If quiet is set, we can just return
412  * now.
413  */
414  if (quiet)
415  return;
416 
417  if (old_score.position == -1) {
418  if (new_score.position > HIGHSCORE_LENGTH)
419  message = "You didn't enter the highscore list:";
420  else
421  message = "You entered the highscore list:";
422  } else {
423  if (new_score.position > HIGHSCORE_LENGTH)
424  message = "You left the highscore list:";
425  else if (new_score.exp > old_score.exp)
426  message = "You beat your last score:";
427  else
428  message = "You didn't beat your last score:";
429  }
430 
432  if (old_score.position != -1)
434  draw_one_high_score(&old_score, bufscore, sizeof(bufscore)));
436  draw_one_high_score(&new_score, bufscore, sizeof(bufscore)));
437 }
438 
451 void hiscore_display(object *op, int max, const char *match) {
452  int printed_entries;
453  size_t j;
454  int skill_match = 0;
455  int skill_min,skill_max;
456  int len;
457 
458  /* check for per-skill instead of overall report */
459  if (strncmp(match, "-s", 2) == 0 ) {
460  match += 2;
461  if (*match == ':') {
462  ++match;
463  if (strchr(match,' ')) {
464  len = strchr(match, ' ') - match;
465  } else {
466  len = strlen(match);
467  }
468  for (int i = 1; i < MAX_SKILLS; ++i) {
469  if (strncmp(match,hiscore_tables[i].skill_name, len) == 0) {
470  skill_match = i;
471  break;
472  }
473  }
474  if (!skill_match) {
476  "Could not match '%.*s' to a skill", len, match);
477  return;
478  }
479  match += len;
480  }
481  else {
482  skill_match = -1; // flag to show all skills
483  if ( max < 100 && max > 10 ) max = 10; // Less output per skill
484  }
485  }
486  while (*match == ' ') ++match;
487 
488  skill_min = skill_max = skill_match;
489  if (skill_match == -1) {
490  skill_min = 1;
491  skill_max = MAX_SKILLS;
492  }
493 
494  /*
495  * Check all skills in skill_names[] order (which should be alphabetical)
496  */
497  for (int s = -1; s <= MAX_SKILLS; ++s) {
498  int skill = s + 1;
499 
500  if (skill < skill_min || skill > skill_max) continue;
501 
502  if (hiscore_tables[skill].skill_name[0] == 0) {
503  continue; // No such skill
504  }
505  if (hiscore_tables[skill].entry[0].exp == 0) {
506  continue; // No entries for this skill
507  }
508  if (skill == 0) {
510  "Overall high scores:");
511  } else {
513  "High scores for the skill [color=red]%s[/color]:", hiscore_tables[skill].skill_name);
514  }
516  "[fixed]Rank Score [print]Who <max hp><max sp><max grace>");
517 
518  printed_entries = 0;
519  for (j = 0; j < HIGHSCORE_LENGTH && hiscore_tables[skill].entry[j].name[0] != '\0' && printed_entries < max; j++) {
520  char scorebuf[MAX_BUF];
521 
522  if (*match != '\0'
523  && !strcasestr_local(hiscore_tables[skill].entry[j].name, match)
524  && !strcasestr_local(hiscore_tables[skill].entry[j].title, match))
525  continue;
526 
527  draw_one_high_score(&hiscore_tables[skill].entry[j], scorebuf, sizeof(scorebuf));
528  printed_entries++;
529 
530  if (op == NULL) {
531  LOG(llevDebug, "%s\n", scorebuf);
532  } else {
534  }
535  }
536  }
537 }
hiscore_load
static void hiscore_load(score_table *table)
Loads the hiscore_table from the highscore file.
Definition: hiscore.cpp:250
living::exp
int64_t exp
Experience.
Definition: living.h:47
draw_one_high_score
static char * draw_one_high_score(const score *sc, char *buf, size_t size)
Formats one score to display to a player.
Definition: hiscore.cpp:158
output_file.h
global.h
settings
struct Settings settings
Server settings.
Definition: init.cpp:139
living::maxhp
int16_t maxhp
Max hit points.
Definition: living.h:41
llevError
@ llevError
Error, serious thing.
Definition: logger.h:11
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:58
strcasestr_local
#define strcasestr_local
Definition: compat.h:28
of_close
int of_close(OutputFile *of)
Closes an output file.
Definition: output_file.cpp:61
of_open
FILE * of_open(OutputFile *of, const char *fname)
Opens an output file.
Definition: output_file.cpp:30
HIGHSCORE_LENGTH
#define HIGHSCORE_LENGTH
How many entries there are room for.
Definition: config.h:526
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
score::title
char title[BIG_NAME]
Title.
Definition: hiscore.cpp:37
get_skill_client_code
int get_skill_client_code(const char *skill_name)
Return the code of the skill for a client, the index in the skill_names array.
Definition: skill_util.cpp:116
c
static event_registration c
Definition: citylife.cpp:422
object::map
struct mapstruct * map
Pointer to the map in which this object is present.
Definition: object.h:305
add_score
static void add_score(score_table *table, score *new_score, score *old_score)
Adds the given score-structure to the high-score list, but only if it was good enough to deserve a pl...
Definition: hiscore.cpp:188
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
SKILL
@ SKILL
Also see SKILL_TOOL (74) below.
Definition: object.h:148
HIGHSCORE_DIR
#define HIGHSCORE_DIR
Definition: config.h:519
PROFILE_BEGIN
#define PROFILE_BEGIN(expr)
Definition: global.h:371
MSG_TYPE_COMMAND_ERROR
#define MSG_TYPE_COMMAND_ERROR
Bad syntax/can't use command.
Definition: newclient.h:532
mapstruct::path
char path[HUGE_BUF]
Filename of the map.
Definition: map.h:355
hiscore_tables
static score_table hiscore_tables[MAX_SKILLS+1]
The highscore table.
Definition: hiscore.cpp:59
MSG_TYPE_ADMIN_HISCORE
#define MSG_TYPE_ADMIN_HISCORE
Hiscore list.
Definition: newclient.h:501
buf
StringBuffer * buf
Definition: readable.cpp:1565
MSG_TYPE_COMMAND
#define MSG_TYPE_COMMAND
Responses to commands, eg, who.
Definition: newclient.h:407
name
Plugin animator file specs[Config] name
Definition: animfiles.txt:4
draw_ext_info
vs only yadda is in because all tags get reset on the next draw_ext_info In the second since it is all in one draw_ext_info
Definition: media-tags.txt:61
score::maxsp
int maxsp
Max sp when killed.
Definition: hiscore.cpp:42
PROFILE_END
#define PROFILE_END(var, expr)
Definition: global.h:376
score_table::entry
score entry[HIGHSCORE_LENGTH]
The entries in decreasing exp order.
Definition: hiscore.cpp:53
object::contr
struct player * contr
Pointer to the player which control this object.
Definition: object.h:284
FMT64
#define FMT64
Definition: compat.h:16
player::killer
char killer[BIG_NAME]
Who killed this player.
Definition: player.h:190
FLAG_WAS_WIZ
#define FLAG_WAS_WIZ
Player was once a wiz.
Definition: define.h:234
score::maxgrace
int maxgrace
Max grace when killed.
Definition: hiscore.cpp:43
sc
Player Stats effect how well a character can survie and interact inside the crossfire world This section discusses the various what they and how they effect the player s actions Also in this section are the stat modifiers that specific classes professions bring Player and sps the current and maximum the Current and Maximum The Current Sp can go somewhat negative When Sp is negative not all spells can be and a more negative Sp makes spell casting less likey to succeed can affect Damage and how the characters as well as how often the character can attack this affects the prices when buying and selling items if this drops the player will start losing hit points wd Cleric or Dwarf sm Elf wd Fireborn sc
Definition: stats.txt:96
split_string
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Splits a string delimited by passed in sep value into characters into an array of strings.
Definition: utils.cpp:473
score::maxhp
int maxhp
Max hp when killed.
Definition: hiscore.cpp:41
hiscore_save
static void hiscore_save(const score_table *table)
Saves the highscore_table into the highscore file.
Definition: hiscore.cpp:81
get_score
static int get_score(char *bp, score *sc)
The opposite of put_score(), get_score reads from the given buffer into a static score structure,...
Definition: hiscore.cpp:113
message
TIPS on SURVIVING Crossfire is populated with a wealth of different monsters These monsters can have varying immunities and attack types In some of them can be quite a bit smarter than others It will be important for new players to learn the abilities of different monsters and learn just how much it will take to kill them This section discusses how monsters can interact with players Most monsters in the game are out to mindlessly kill and destroy the players These monsters will help boost a player s after he kills them When fighting a large amount of monsters in a single attempt to find a narrower hallway so that you are not being attacked from all sides Charging into a room full of Beholders for instance would not be open the door and fight them one at a time For there are several maps designed for them Find these areas and clear them out All throughout these a player can find signs and books which they can read by stepping onto them and hitting A to apply the book sign These messages will help the player to learn the system One more always keep an eye on your food If your food drops to your character will soon so BE CAREFUL ! NPCs Non Player Character are special monsters which have intelligence Players may be able to interact with these monsters to help solve puzzles and find items of interest To speak with a monster you suspect to be a simply move to an adjacent square to them and push the double ie Enter your message
Definition: survival-guide.txt:34
skill_names
const char * skill_names[MAX_SKILLS]
Will contain a number-name mapping for skills, initialized by init_skills().
Definition: skill_util.cpp:59
title
Information on one title.
Definition: readable.cpp:108
FOR_INV_FINISH
#define FOR_INV_FINISH()
Finishes FOR_INV_PREPARE().
Definition: define.h:671
score_table::skill_name
char skill_name[MAX_BUF]
The name of the skill or "Overall".
Definition: hiscore.cpp:52
sproto.h
MAX_SKILLS
#define MAX_SKILLS
This is the maximum number of skills the game may handle.
Definition: skills.h:70
player::maplevel
char maplevel[MAX_BUF]
On which level is the player?
Definition: player.h:109
MAX_BUF
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
hiscore_display
void hiscore_display(object *op, int max, const char *match)
Displays the high score file.
Definition: hiscore.cpp:451
score::maplevel
char maplevel[BIG_NAME]
Killed on what level.
Definition: hiscore.cpp:40
score
The score structure is used when treating new high-scores.
Definition: hiscore.cpp:35
living::maxgrace
int16_t maxgrace
Maximum grace.
Definition: living.h:45
score_table
A highscore table.
Definition: hiscore.cpp:50
put_score
static void put_score(const score *sc, char *buf, size_t size)
Writes the given score structure to specified buffer.
Definition: hiscore.cpp:71
NDI_UNIQUE
#define NDI_UNIQUE
Print immediately, don't buffer.
Definition: newclient.h:265
object::name
sstring name
The name of the object, obviously...
Definition: object.h:319
living::maxsp
int16_t maxsp
Max spell points.
Definition: living.h:43
OLD_HIGHSCORE
#define OLD_HIGHSCORE
Definition: config.h:518
mapstruct::name
char * name
Name of map as given by its creator.
Definition: map.h:318
score::position
int position
Position in the highscore list.
Definition: hiscore.cpp:44
score::exp
int64_t exp
Experience.
Definition: hiscore.cpp:39
hiscore_init
void hiscore_init(void)
Initializes the module.
Definition: hiscore.cpp:296
score::killer
char killer[BIG_NAME]
Name (+ title) or "left".
Definition: hiscore.cpp:38
skill
skill
Definition: arch-handbook.txt:585
hiscore_check
void hiscore_check(object *op, int quiet)
Checks if player should enter the hiscore, and if so writes her into the list.
Definition: hiscore.cpp:348
score::name
char name[BIG_NAME]
Name.
Definition: hiscore.cpp:36
object::stats
living stats
Str, Con, Dex, etc.
Definition: object.h:378
player::title
char title[BIG_NAME]
Default title, like fighter, wizard, etc.
Definition: player.h:184
score_table::fname
char fname[MAX_BUF]
Filename of the backing file.
Definition: hiscore.cpp:51
BIG_NAME
#define BIG_NAME
Definition: define.h:42
MSG_TYPE_APPLY
#define MSG_TYPE_APPLY
Applying objects.
Definition: newclient.h:411
player::name_changed
uint32_t name_changed
If true, the player has set a name.
Definition: player.h:145
MSG_TYPE_APPLY_ERROR
#define MSG_TYPE_APPLY_ERROR
Definition: newclient.h:604
MSG_TYPE_ADMIN
#define MSG_TYPE_ADMIN
Definition: newclient.h:405
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Constructs a loop iterating over the inventory of an object.
Definition: define.h:664
llevDebug
@ llevDebug
Only for debugging purposes.
Definition: logger.h:13
OutputFile
Definition: output_file.h:41
player_get_title
void player_get_title(const player *pl, char *buf, size_t bufsize)
Returns the player's title.
Definition: player.cpp:232
Settings::localdir
const char * localdir
Read/write data files.
Definition: global.h:251