server/src/common/kernel/save.c

2059 lines
44 KiB
C
Raw Normal View History

2001-01-25 10:37:55 +01:00
/* vi: set ts=2:
*
* Eressea PB(E)M host Copyright (C) 1998-2003
2001-01-25 10:37:55 +01:00
* Christian Schlittchen (corwin@amber.kn-bremen.de)
* Katja Zedel (katze@felidae.kn-bremen.de)
* Henning Peters (faroul@beyond.kn-bremen.de)
* Enno Rehling (enno@eressea.de)
2001-01-25 10:37:55 +01:00
* Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
*
* based on:
*
* Atlantis v1.0 13 September 1993 Copyright 1993 by Russell Wallace
* Atlantis v1.7 Copyright 1996 by Alex Schr<EFBFBD>der
*
* This program may not be used, modified or distributed without
* prior permission by the authors of Eressea.
* This program may not be sold or used commercially without prior written
* permission from the authors.
*/
#include <config.h>
#include "eressea.h"
#include "save.h"
2004-06-21 18:45:27 +02:00
#include "alchemy.h"
#include "alliance.h"
2004-06-21 18:45:27 +02:00
#include "attrib.h"
#include "border.h"
#include "building.h"
2001-01-25 10:37:55 +01:00
#include "faction.h"
#include "group.h"
2001-01-25 10:37:55 +01:00
#include "item.h"
2004-06-21 18:45:27 +02:00
#include "karma.h"
#include "magic.h"
#include "message.h"
2007-08-05 14:26:46 +02:00
#include "move.h"
2004-06-21 18:45:27 +02:00
#include "objtypes.h"
#include "order.h"
#include "pathfinder.h"
#include "plane.h"
#include "race.h"
2001-01-25 10:37:55 +01:00
#include "region.h"
#include "resources.h"
2001-01-25 10:37:55 +01:00
#include "ship.h"
2004-06-21 18:45:27 +02:00
#include "skill.h"
#include "spell.h"
#include "terrain.h"
#include "terrainid.h" /* only for conversion code */
2004-06-21 18:45:27 +02:00
#include "unit.h"
2007-06-20 02:34:02 +02:00
#include "version.h"
2001-01-25 10:37:55 +01:00
/* attributes includes */
#include <attributes/key.h>
/* util includes */
#include <util/attrib.h>
#include <util/base36.h>
#include <util/bsdstring.h>
#include <util/event.h>
#include <util/filereader.h>
#include <util/goodies.h>
2007-06-20 02:34:02 +02:00
#include <util/lists.h>
2007-08-05 14:19:56 +02:00
#include <util/log.h>
#include <util/parser.h>
#include <util/resolve.h>
#include <util/sql.h>
#include <util/rand.h>
#include <util/rng.h>
#include <util/umlaut.h>
#include <util/unicode.h>
2001-01-25 10:37:55 +01:00
#include <libxml/encoding.h>
2001-01-25 10:37:55 +01:00
/* libc includes */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#define xisdigit(c) (((c) >= '0' && (c) <= '9') || (c) == '-')
2001-01-25 10:37:55 +01:00
#define ESCAPE_FIX
#define MAXORDERS 256
2005-07-28 15:20:36 +02:00
#define MAXPERSISTENT 128
2001-01-25 10:37:55 +01:00
/* exported symbols symbols */
2007-09-02 11:59:28 +02:00
const unsigned char utf8_bom[4] = { 0xef, 0xbb, 0xbf };
const char * xmlfile = "eressea.xml";
2001-01-25 10:37:55 +01:00
const char * g_datadir;
int firstx = 0, firsty = 0;
int enc_gamedata = 0;
/* local symbols */
static region * current_region;
2001-01-25 10:37:55 +01:00
char *
rns(FILE * f, char *c, size_t size)
{
char * s = c;
do {
*s = (char) getc(f);
} while (*s!='"');
for (;;) {
*s = (char) getc(f);
if (*s=='"') break;
if (s<c+size) ++s;
}
*s = 0;
return c;
2001-01-25 10:37:55 +01:00
}
extern unsigned int __at_hashkey(const char* s);
FILE *
cfopen(const char *filename, const char *mode)
{
FILE * F = fopen(filename, mode);
if (F == 0) {
perror(filename);
return NULL;
}
setvbuf(F, 0, _IOFBF, 32 * 1024); /* 32 kb buffer size */
return F;
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
/* Dummy-Funktion f<>r die Kompatibilit<69>t */
#define rid(F) ri36(F)
2001-01-25 10:37:55 +01:00
int
freadstr(FILE * F, int encoding, char * start, size_t size)
{
char * str = start;
boolean quote = false;
for (;;) {
int c = fgetc(F);
if (isspace(c)) {
if (str==start) {
continue;
}
if (!quote) {
*str = 0;
return (int)(str-start);
}
}
switch (c) {
case EOF:
return EOF;
case '"':
if (!quote && str!=start) {
log_error(("datafile contains a \" that isn't at the start of a string.\n"));
assert(!"datafile contains a \" that isn't at the start of a string.\n");
}
if (quote) {
*str = 0;
return (int)(str-start);
}
quote = true;
break;
case '\\':
c = fgetc(F);
switch (c) {
case EOF:
return EOF;
case 'n':
if ((size_t)(str-start+1)<size) {
*str++ = '\n';
}
break;
default:
if ((size_t)(str-start+1)<size) {
if (encoding == XML_CHAR_ENCODING_8859_1 && c&0x80) {
char inbuf = (char)c;
size_t inbytes = 1;
size_t outbytes = size-(str-start);
int ret = unicode_latin1_to_utf8((xmlChar *)str, &outbytes, (const xmlChar *)&inbuf, &inbytes);
if (ret>0) str+=ret;
else {
log_error(("input data was not iso-8859-1! assuming utf-8\n"));
encoding = XML_CHAR_ENCODING_ERROR;
*str++ = (char)c;
}
} else {
*str++ = (char)c;
}
}
}
break;
default:
if ((size_t)(str-start+1)<size) {
if (encoding == XML_CHAR_ENCODING_8859_1 && c&0x80) {
char inbuf = (char)c;
size_t inbytes = 1;
size_t outbytes = size-(str-start);
int ret = unicode_latin1_to_utf8((xmlChar *)str, &outbytes, (const xmlChar *)&inbuf, &inbytes);
if (ret>0) str+=ret;
else {
log_error(("input data was not iso-8859-1! assuming utf-8\n"));
encoding = XML_CHAR_ENCODING_ERROR;
*str++ = (char)c;
}
} else {
*str++ = (char)c;
}
}
}
}
}
/** writes a quoted string to the file
* no trailing space, since this is used to make the creport.
*/
int
fwritestr(FILE * F, const char * str)
{
int nwrite = 0;
fputc('\"', F);
while (*str) {
int c = (int)(unsigned char)*str++;
switch (c) {
case '"':
case '\\':
fputc('\\', F);
fputc(c, F);
nwrite+=2;
break;
case '\n':
fputc('\\', F);
fputc('n', F);
nwrite+=2;
break;
default:
fputc(c, F);
++nwrite;
}
}
fputc('\"', F);
return nwrite + 2;
}
static void
2007-08-18 16:54:35 +02:00
rds(FILE * F, char **ds)
2001-01-25 10:37:55 +01:00
{
static char buffer[DISPLAYSIZE + 1]; /*Platz f<>r null-char nicht vergessen!*/
char *s = &buffer[0];
int c = getc(F);
while (c != '"') {
if (c == EOF) {
*s = 0;
fprintf(stderr, "Die Datei bricht vorzeitig ab.\n");
abort();
}
c = getc(F);
}
c = getc(F);
while (c != '"') {
if (c == EOF) {
*s = 0;
fprintf(stderr, "Die Datei bricht vorzeitig ab.\n");
abort();
}
2005-06-17 20:12:58 +02:00
if (s - buffer < DISPLAYSIZE) {
*s++ = (char)c;
2005-06-17 20:12:58 +02:00
}
c = getc(F);
}
2001-01-25 10:37:55 +01:00
c = getc(F);
*s = 0;
if (ds) {
2005-06-17 20:12:58 +02:00
*ds = realloc(*ds, sizeof(char) * (strlen(buffer) + 1));
strcpy(*ds, buffer);
}
2001-01-25 10:37:55 +01:00
}
static void
2007-08-18 16:54:35 +02:00
xrds(FILE * F, char **ds, int encoding)
{
static char buffer[DISPLAYSIZE + 1]; /*Platz f<>r null-char nicht vergessen!*/
int len = freadstr(F, encoding, buffer, sizeof(buffer));
if (len>=0) {
if (ds) {
*ds = realloc(*ds, sizeof(char) * (len + 1));
strcpy(*ds, buffer);
}
}
}
2001-01-25 10:37:55 +01:00
#define rcf(F) (getc(F));
void
rsf(FILE * F, char *s, size_t len)
{
char * begin = s;
int c;
do {
c = getc(F);;
if (c == EOF) {
puts("Die Datei bricht vorzeitig ab.");
abort();
}
} while (c != '"');
for (;;) {
c = getc(F);
if (c == '"') break;
else if (c == EOF) {
puts("Die Datei bricht vorzeitig ab.");
abort();
}
if (s-begin<(int)len-1)
*s++ = (char)c;
}
*s = 0;
2001-01-25 10:37:55 +01:00
}
static void
rs(FILE * F, char *s)
{
boolean apos = false;
int c = getc(F);
while (isspace(c)) c = getc(F);
if (c=='"') {
apos = true;
c = getc(F);
}
for (;;) {
if (c=='"') {
c = getc(F);
break;
} else if (!apos && isspace(c)) break;
*s++ = (char)c;
c = getc(F);
}
*s = 0;
2001-01-25 10:37:55 +01:00
}
#define rss(F, buf, size) rs(F, buf) /* should check size but doesn't */
2001-01-25 10:37:55 +01:00
static int
ri(FILE * F)
{
int i = 0, vz = 1;
int c = getc(F);
while (!xisdigit(c)) {
if (c == EOF) {
puts("Die Datei bricht vorzeitig ab.");
abort();
}
c = getc(F);
}
while (xisdigit(c)) {
if (c == '-')
vz = -1;
else
i = 10 * i + (c - '0');
c = getc(F);
}
return i * vz;
2001-01-25 10:37:55 +01:00
}
static int
ri36(FILE * F)
{
char buf[16];
int i = 0;
int c = getc(F);
while (!isalnum(c)) c = getc(F);
while (isalnum(c)) {
if (i+1<sizeof(buf)) {
buf[i++]=(char)c;
}
c = getc(F);
}
buf[i]=0;
i = atoi36(buf);
return i;
2001-01-25 10:37:55 +01:00
}
#ifdef ENEMIES
static void
read_enemies(FILE * F, faction * f)
{
if (global.data_version<ENEMIES_VERSION) return;
f->enemies = NULL;
for (;;) {
char zText[32];
fscanf(F, "%s", zText);
if (strcmp(zText, "end")==0) break;
else {
int fno = atoi36(zText);
faction_list * flist = malloc(sizeof(faction_list));
flist->next = f->enemies;
f->enemies = flist;
ur_add((void*)fno, (void**)&flist->data, resolve_faction);
}
}
}
static void
write_enemies(FILE * F, const faction_list * flist)
{
while (flist) {
fprintf(F, "%s ", itoa36(flist->data->no));
}
fputs("end \n", F);
}
#endif
2001-01-25 10:37:55 +01:00
static unit *
unitorders(FILE * F, int enc, struct faction * f)
2001-01-25 10:37:55 +01:00
{
2004-06-21 18:45:27 +02:00
int i;
unit *u;
if (!f) return NULL;
i = getid();
u = findunitg(i, NULL);
2005-05-27 23:04:13 +02:00
if (u && u->race == new_race[RC_SPELL]) return NULL;
2004-06-21 18:45:27 +02:00
if (u && u->faction == f) {
order ** ordp;
if (quiet==0) {
printf(",_%4s_", itoa36(u->no));
fflush(stdout);
}
if (!fval(u, UFL_ORDERS)) {
/* alle wiederholbaren, langen befehle werden gesichert: */
fset(u, UFL_ORDERS);
u->old_orders = u->orders;
ordp = &u->old_orders;
while (*ordp) {
order * ord = *ordp;
if (!is_repeated(ord)) {
2005-05-01 13:38:06 +02:00
*ordp = ord->next;
2005-05-01 15:33:34 +02:00
ord->next = NULL;
free_order(ord);
} else {
ordp = &ord->next;
}
}
} else {
free_orders(&u->orders);
}
2004-06-21 18:45:27 +02:00
u->orders = 0;
ordp = &u->orders;
for (;;) {
const char * s;
2004-06-21 18:45:27 +02:00
/* Erst wenn wir sicher sind, dass kein Befehl
* eingegeben wurde, checken wir, ob nun eine neue
* Einheit oder ein neuer Spieler drankommt */
s = getbuf(F, enc);
if (s==NULL) break;
2004-06-21 18:45:27 +02:00
if (s[0]) {
2007-08-12 15:02:24 +02:00
const char * stok = s;
stok = parse_token(&stok);
if (stok) {
boolean quit = false;
switch (findparam(stok, u->faction->locale)) {
case P_UNIT:
case P_REGION:
case P_FACTION:
case P_NEXT:
case P_GAMENAME:
quit = true;
}
if (quit) break;
}
2004-06-21 18:45:27 +02:00
/* Nun wird der Befehl erzeut und eingeh<65>ngt */
*ordp = parse_order(s, u->faction->locale);
if (*ordp) ordp = &(*ordp)->next;
2004-06-21 18:45:27 +02:00
}
}
2001-01-25 10:37:55 +01:00
2004-06-21 18:45:27 +02:00
} else {
/* cmistake(?, buf, 160, MSG_EVENT); */
2004-06-21 18:45:27 +02:00
return NULL;
}
return u;
2001-01-25 10:37:55 +01:00
}
static faction *
factionorders(void)
2001-01-25 10:37:55 +01:00
{
faction * f = NULL;
int fid = getid();
f = findfaction(fid);
if (f!=NULL) {
const char * pass = getstrtoken();
if (quiet==0) {
printf(" %4s;", factionid(f));
fflush(stdout);
}
2007-08-05 14:19:56 +02:00
if (!checkpasswd(f, (const char *)pass, true)) {
log_warning(("Invalid password for faction %s\n", itoa36(fid)));
2007-03-25 22:00:14 +02:00
ADDMSG(&f->msgs, msg_message("wrongpasswd", "faction password",
f->no, pass));
return 0;
}
/* Die Partei hat sich zumindest gemeldet, so da<64> sie noch
* nicht als unt<EFBFBD>tig gilt */
2006-08-20 15:32:03 +02:00
/* TODO: +1 ist ein Workaround, weil turn erst in process_orders
* incrementiert wird. */
f->lastorders = global.data_turn+1;
} else {
log_warning(("orders for invalid faction %s\n", itoa36(fid)));
2006-08-20 15:32:03 +02:00
}
return f;
2001-01-25 10:37:55 +01:00
}
double
version(void)
{
return RELEASE_VERSION * 0.1;
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
2004-06-21 18:45:27 +02:00
static param_t
igetparam (const char *s, const struct locale *lang)
2004-06-21 18:45:27 +02:00
{
return findparam (igetstrtoken (s), lang);
}
2001-01-25 10:37:55 +01:00
int
readorders(const char *filename)
2001-01-25 10:37:55 +01:00
{
FILE * F = NULL;
const char *b;
int nfactions=0;
struct faction *f = NULL;
2001-01-25 10:37:55 +01:00
if (filename) F = cfopen(filename, "rt");
if (F==NULL) return 0;
2001-01-25 10:37:55 +01:00
puts(" - lese Befehlsdatei...\n");
2001-01-25 10:37:55 +01:00
/* TODO: recognize UTF8 BOM */
b = getbuf(F, enc_gamedata);
2001-01-25 10:37:55 +01:00
/* Auffinden der ersten Partei, und danach abarbeiten bis zur letzten
* Partei */
2001-01-25 10:37:55 +01:00
while (b) {
const struct locale * lang = f?f->locale:default_locale;
int p;
const char * s;
switch (igetparam(b, lang)) {
case P_LOCALE:
s = getstrtoken();
2004-05-09 12:18:09 +02:00
#undef LOCALE_CHANGE
#ifdef LOCALE_CHANGE
if (f && find_locale(s)) {
f->locale = find_locale(s);
}
#endif
2001-02-17 16:02:50 +01:00
b = getbuf(F, enc_gamedata);
break;
case P_GAMENAME:
case P_FACTION:
f = factionorders();
if (f) {
++nfactions;
}
b = getbuf(F, enc_gamedata);
break;
/* in factionorders wird nur eine zeile gelesen:
* diejenige mit dem passwort. Die befehle der units
* werden geloescht, und die Partei wird als aktiv
* vermerkt. */
case P_UNIT:
if (!f || !unitorders(F, enc_gamedata, f)) do {
b = getbuf(F, enc_gamedata);
if (!b) break;
p = igetparam(b, lang);
} while ((p != P_UNIT || !f) && p != P_FACTION && p != P_NEXT && p != P_GAMENAME);
break;
/* Falls in unitorders() abgebrochen wird, steht dort entweder eine neue
* Partei, eine neue Einheit oder das File-Ende. Das switch() wird erneut
* durchlaufen, und die entsprechende Funktion aufgerufen. Man darf buf
* auf alle F<EFBFBD>lle nicht <EFBFBD>berschreiben! Bei allen anderen Eintr<EFBFBD>gen hier
* mu<EFBFBD> buf erneut gef<EFBFBD>llt werden, da die betreffende Information in nur
* einer Zeile steht, und nun die n<EFBFBD>chste gelesen werden mu<EFBFBD>. */
case P_NEXT:
f = NULL;
b = getbuf(F, enc_gamedata);
break;
default:
b = getbuf(F, enc_gamedata);
break;
}
}
2001-01-25 10:37:55 +01:00
fclose(F);
puts("\n");
log_printf(" %d Befehlsdateien gelesen\n", nfactions);
return 0;
2001-01-25 10:37:55 +01:00
}
/* ------------------------------------------------------------- */
/* #define INNER_WORLD */
/* f<>rs debuggen nur den inneren Teil der Welt laden */
/* -9;-27;-1;-19;Sumpfloch */
int
inner_world(region * r)
{
static int xy[2] =
{18, -45};
static int size[2] =
{27, 27};
if (r->x >= xy[0] && r->x < xy[0] + size[0] && r->y >= xy[1] && r->y < xy[1] + size[1])
return 2;
if (r->x >= xy[0] - 9 && r->x < xy[0] + size[0] + 9 && r->y >= xy[1] - 9 && r->y < xy[1] + size[1] + 9)
return 1;
return 0;
2001-01-25 10:37:55 +01:00
}
int maxregions = -1;
int loadplane = 0;
boolean dirtyload = false;
2005-01-03 22:28:57 +01:00
boolean incomplete_data = false;
2001-01-25 10:37:55 +01:00
enum {
U_MAN,
U_UNDEAD,
U_ILLUSION,
U_FIREDRAGON,
U_DRAGON,
U_WYRM,
U_SPELL,
U_TAVERNE,
U_MONSTER,
U_BIRTHDAYDRAGON,
U_TREEMAN,
MAXTYPES
2001-01-25 10:37:55 +01:00
};
race_t
typus2race(unsigned char typus)
{
if (typus>0 && typus <=11) return (race_t)(typus-1);
return NORACE;
2001-01-25 10:37:55 +01:00
}
void
create_backup(char *file)
{
#ifdef HAVE_UNISTD_H
char bfile[MAX_PATH];
int c = 1;
if (access(file, R_OK) == 0) return;
do {
sprintf(bfile, "%s.backup%d", file, c);
c++;
} while(access(bfile, R_OK) == 0);
link(file, bfile);
2001-01-25 10:37:55 +01:00
#endif
}
const char *
2001-01-25 10:37:55 +01:00
datapath(void)
{
static char zText[MAX_PATH];
if (g_datadir) return g_datadir;
return strcat(strcpy(zText, basepath()), "/data");
2001-01-25 10:37:55 +01:00
}
2001-01-25 10:37:55 +01:00
void
read_items(FILE *F, item **ilist)
{
for (;;) {
char ibuf[32];
2003-12-14 11:10:30 +01:00
const item_type * itype;
rss(F, ibuf, sizeof(ibuf));
if (!strcmp("end", ibuf)) break;
itype = it_find(ibuf);
assert(itype!=NULL);
if (itype!=NULL) {
2007-08-12 15:02:24 +02:00
int i = ri(F);
if (i<=0) {
log_error(("data contains an entry with %d %s\n", i, itype->rtype->_name[1]));
} else {
i_change(ilist, itype, i);
}
}
}
2001-01-25 10:37:55 +01:00
}
static void
read_alliances(FILE * F)
{
char pbuf[32];
if (global.data_version<SAVEALLIANCE_VERSION) {
if (!AllianceRestricted() && !AllianceAuto()) return;
}
rss(F, pbuf, sizeof(pbuf));
while (strcmp(pbuf, "end")!=0) {
char aname[128];
rss(F, aname, sizeof(aname));
makealliance(atoi36(pbuf), aname);
rss(F, pbuf, sizeof(pbuf));
}
}
#define wc(F, c) putc(c, F);
#define wnl(F) putc('\n', F);
#define whs(F, s) fputs(s, F); putc(' ', F)
2001-01-25 10:37:55 +01:00
void
wsn(FILE * F, const char *s)
{
if (!s)
return;
while (*s)
wc(F, *s++);
2001-01-25 10:37:55 +01:00
}
void
ws(FILE * F, const char *s)
{
fputc('"', F);
wsn(F, s);
fputs("\" ", F);
2001-01-25 10:37:55 +01:00
}
static void
2001-01-25 10:37:55 +01:00
wi(FILE * F, int n)
{
fprintf(F, "%d ", n);
2001-01-25 10:37:55 +01:00
}
static void
wi36(FILE * F, int n)
2001-01-25 10:37:55 +01:00
{
fprintf(F, "%s ", itoa36(n));
2001-01-25 10:37:55 +01:00
}
void
write_alliances(FILE * F)
{
alliance * al = alliances;
while (al) {
wi36(F, al->id);
ws(F, al->name);
al = al->next;
wnl(F);
}
fprintf(F, "end ");
wnl(F);
}
2001-01-25 10:37:55 +01:00
void
write_items(FILE *F, item *ilist)
{
item * itm;
2007-08-12 15:02:24 +02:00
for (itm=ilist;itm;itm=itm->next) {
assert(itm->number>=0);
if (itm->number) {
fprintf(F, "%s %i ", resourcename(itm->type->rtype, 0), itm->number);
}
}
fputs("end ", F);
2001-01-25 10:37:55 +01:00
}
int
lastturn(void)
{
int turn = 0;
2001-01-25 10:37:55 +01:00
#ifdef HAVE_READDIR
DIR *data_dir = NULL;
struct dirent *entry = NULL;
const char * dir = datapath();
data_dir = opendir(dir);
if (data_dir != NULL) {
entry = readdir(data_dir);
}
if (data_dir != NULL && entry != NULL) {
turn = 0;
do {
int i = atoi(entry->d_name);
if (i > turn)
turn = i;
entry = readdir(data_dir);
} while (entry != NULL);
2001-01-25 10:37:55 +01:00
#ifdef HAVE_CLOSEDIR
closedir(data_dir);
2001-01-25 10:37:55 +01:00
#endif
}
2001-01-25 10:37:55 +01:00
#else
# error "requires dirent.h or an equivalent to compile!"
#endif
return turn;
2001-01-25 10:37:55 +01:00
}
2004-06-21 18:45:27 +02:00
void
2007-06-20 02:34:02 +02:00
fwriteorder(FILE * F, const struct order * ord, const struct locale * lang)
2004-06-21 18:45:27 +02:00
{
char obuf[1024];
write_order(ord, lang, obuf, sizeof(obuf));
if (obuf[0]) fwritestr(F, obuf);
2004-06-21 18:45:27 +02:00
}
unit *
readunit(FILE * F, int encoding)
{
skill_t sk;
unit * u;
int number, n, p;
order ** orderp;
char obuf[1024];
n = rid(F);
u = findunit(n);
if (u==NULL) {
u = calloc(sizeof(unit), 1);
u->no = n;
uhash(u);
} else {
while (u->attribs) a_remove(&u->attribs, u->attribs);
while (u->items) i_free(i_remove(&u->items, u->items));
free(u->skills);
u->skills = 0;
u->skill_size = 0;
u_setfaction(u, NULL);
}
{
int n = rid(F);
faction * f = findfaction(n);
if (f!=u->faction) u_setfaction(u, f);
}
xrds(F, &u->name, encoding);
if (lomem) rds(F, NULL);
else {
xrds(F, &u->display, encoding);
}
number = ri(F);
u->age = (short)ri(F);
if (global.data_version<NEWRACE_VERSION) {
u->race = new_race[(race_t)ri(F)];
u->irace = new_race[(race_t)ri(F)];
} else {
char * space;
char rname[32];
rss(F, rname, sizeof(rname));
space = strchr(rname, ' ');
2006-01-29 02:55:36 +01:00
if (space!=NULL) {
char * inc = space+1;
char * outc = space;
do {
while (*inc==' ') ++inc;
while (*inc) {
*outc++ = *inc++;
if (*inc==' ') break;
}
} while (*inc);
*outc = 0;
}
u->race = rc_find(rname);
assert(u->race);
rss(F, rname, sizeof(rname));
if (strlen(rname)) u->irace = rc_find(rname);
else u->irace = u->race;
}
if (u->race->describe) {
const char * rcdisp = u->race->describe(u, u->faction->locale);
if (u->display && rcdisp) {
/* see if the data file contains old descriptions */
if (strcmp(rcdisp, u->display)==0) {
free(u->display);
u->display = NULL;
}
}
}
if (u->faction == NULL) {
log_error(("unit %s has faction == NULL\n", unitname(u)));
u_setfaction(u, findfaction(MONSTER_FACTION));
set_number(u, 0);
}
if (count_unit(u)) u->faction->no_units++;
set_number(u, number);
u->building = findbuilding(rid(F));
u->ship = findship(rid(F));
if (global.data_version <= 73) {
if (ri(F)) {
fset(u, UFL_OWNER);
} else {
freset(u, UFL_OWNER);
}
}
setstatus(u, ri(F));
if (global.data_version <= 73) {
if (ri(F)) {
guard(u, GUARD_ALL);
} else {
guard(u, GUARD_NONE);
}
} else {
u->flags = ri(F) & ~UFL_DEBUG;
u->flags &= UFL_SAVEMASK;
}
/* Persistente Befehle einlesen */
free_orders(&u->orders);
freadstr(F, encoding, obuf, sizeof(obuf));
p = n = 0;
orderp = &u->orders;
while (obuf[0]) {
if (!lomem) {
order * ord = parse_order(obuf, u->faction->locale);
if (ord!=NULL) {
if (++n<MAXORDERS) {
if (!is_persistent(ord) || ++p<MAXPERSISTENT) {
*orderp = ord;
orderp = &ord->next;
ord = NULL;
} else if (p==MAXPERSISTENT) {
log_warning(("%s had %d or more persistent orders\n", unitname(u), MAXPERSISTENT));
}
} else if (n==MAXORDERS) {
log_warning(("%s had %d or more orders\n", unitname(u), MAXORDERS));
}
if (ord!=NULL) free_order(ord);
}
}
freadstr(F, encoding, obuf, sizeof(obuf));
}
if (global.data_version<NOLASTORDER_VERSION) {
order * ord;
freadstr(F, encoding, obuf, sizeof(obuf));
ord = parse_order(obuf, u->faction->locale);
if (ord!=NULL) {
#ifdef LASTORDER
set_order(&u->lastorder, ord);
#else
addlist(&u->orders, ord);
#endif
}
}
set_order(&u->thisorder, NULL);
2006-10-29 15:03:56 +01:00
assert(u->race);
if (global.data_version<NEWSKILL_VERSION) {
/* convert old data */
if (u->number) {
while ((sk = (skill_t) ri(F)) != NOSKILL) {
2006-10-29 15:03:56 +01:00
int days = ri(F) / u->number;
int lvl = level(days);
int weeks = lvl + 1 - (days - level_days(lvl))/30;
assert(weeks>0 && weeks<=lvl+1);
if (lvl) {
skill * sv = add_skill(u, sk);
sv->level = sv->old = (unsigned char)lvl;
sv->weeks = (unsigned char)weeks;
}
}
2006-10-29 15:03:56 +01:00
}
} else {
while ((sk = (skill_t) ri(F)) != NOSKILL) {
int level = ri(F);
int weeks = ri(F);
if (level) {
skill * sv = add_skill(u, sk);
sv->level = sv->old = (unsigned char)level;
sv->weeks = (unsigned char)weeks;
}
}
}
read_items(F, &u->items);
u->hp = ri(F);
if (u->hp < u->number) {
log_error(("Einheit %s hat %u Personen, und %u Trefferpunkte\n", itoa36(u->no),
u->number, u->hp));
u->hp=u->number;
}
a_read(F, &u->attribs);
return u;
}
void
writeunit(FILE * F, const unit * u)
{
order * ord;
int i, p = 0;
wi36(F, u->no);
wi36(F, u->faction->no);
ws(F, (const char *)u->name);
ws(F, u->display?(const char *)u->display:"");
wi(F, u->number);
wi(F, u->age);
ws(F, u->race->_name[0]);
ws(F, u->irace!=u->race?u->irace->_name[0]:"");
if (u->building)
wi36(F, u->building->no);
else
wi(F, 0);
if (u->ship) wi36(F, u->ship->no);
else wi36(F, 0);
wi(F, u->status);
wi(F, u->flags & UFL_SAVEMASK);
wnl(F);
#ifndef LASTORDER
for (ord = u->old_orders; ord; ord=ord->next) {
if (++p<MAXPERSISTENT) {
fwriteorder(F, ord, u->faction->locale);
fputc(' ', F);
} else {
log_error(("%s had %d or more persistent orders\n", unitname(u), MAXPERSISTENT));
break;
}
}
#endif
for (ord = u->orders; ord; ord=ord->next) {
if (u->old_orders && is_repeated(ord)) continue; /* has new defaults */
if (is_persistent(ord)) {
if (++p<MAXPERSISTENT) {
fwriteorder(F, ord, u->faction->locale);
fputc(' ', F);
} else {
log_error(("%s had %d or more persistent orders\n", unitname(u), MAXPERSISTENT));
break;
}
}
}
2004-07-03 21:16:17 +02:00
/* write an empty string to terminate the list */
fputs("\"\"", F);
wnl(F);
#if RELEASE_VERSION<NOLASTORDER_VERSION
2004-07-03 21:16:17 +02:00
/* the current default order */
fwriteorder(F, u->lastorder, u->faction->locale);
wnl(F);
#endif
assert(u->race);
2006-10-29 15:03:56 +01:00
for (i=0;i!=u->skill_size;++i) {
skill * sv = u->skills+i;
assert(sv->weeks<=sv->level*2+1);
if (sv->level>0) {
wi(F, sv->id);
wi(F, sv->level);
wi(F, sv->weeks);
}
}
wi(F, -1);
wnl(F);
write_items(F, u->items);
wnl(F);
if (u->hp == 0) {
log_error(("Einheit %s hat 0 Trefferpunkte\n", itoa36(u->no)));
((unit*)u)->hp = 1;
}
wi(F, u->hp);
wnl(F);
a_write(F, u->attribs);
wnl(F);
}
region *
readregion(FILE * F, int encoding, short x, short y)
{
region * r = findregion(x, y);
const terrain_type * terrain;
char token[32];
if (r==NULL) {
r = new_region(x, y);
} else {
current_region = r;
while (r->attribs) a_remove(&r->attribs, r->attribs);
if (r->land) {
free(r->land); /* mem leak */
r->land->demands = 0; /* mem leak */
}
while (r->resources) {
rawmaterial * rm = r->resources;
r->resources = rm->next;
free(rm);
}
r->land = 0;
}
if (lomem) rds(F, NULL);
else xrds(F, &r->display, encoding);
if (global.data_version < TERRAIN_VERSION) {
int ter = ri(F);
terrain = newterrain((terrain_t)ter);
} else {
char name[64];
rss(F, name, sizeof(name));
terrain = get_terrain(name);
if (terrain==NULL) {
log_error(("Unknown terrain '%s'\n", name));
assert(!"unknown terrain");
}
}
r->terrain = terrain;
r->flags = (char) ri(F);
r->age = (unsigned short) ri(F);
if (fval(r->terrain, LAND_REGION)) {
r->land = calloc(1, sizeof(land_region));
xrds(F, &r->land->name, encoding);
}
if (r->land) {
int i;
rawmaterial ** pres = &r->resources;
if(global.data_version < GROWTREE_VERSION) {
i = ri(F); rsettrees(r, 2, i);
} else {
i = ri(F);
if (i<0) {
log_error(("number of trees in %s is %d.\n",
regionname(r, NULL), i));
i=0;
}
rsettrees(r, 0, i);
i = ri(F);
if (i<0) {
log_error(("number of young trees in %s is %d.\n",
regionname(r, NULL), i));
i=0;
}
rsettrees(r, 1, i);
i = ri(F);
if (i<0) {
log_error(("number of seeds in %s is %d.\n",
regionname(r, NULL), i));
i=0;
}
rsettrees(r, 2, i);
}
i = ri(F); rsethorses(r, i);
assert(*pres==NULL);
for (;;) {
rawmaterial * res;
rss(F, token, sizeof(token));
if (strcmp(token, "end")==0) break;
res = malloc(sizeof(rawmaterial));
res->type = rmt_find(token);
if (res->type==NULL) {
log_error(("invalid resourcetype %s in data.\n", token));
}
assert(res->type!=NULL);
res->level = ri(F);
res->amount = ri(F);
res->flags = 0;
if(global.data_version >= RANDOMIZED_RESOURCES_VERSION) {
res->startlevel = ri(F);
res->base = ri(F);
res->divisor = ri(F);
} else {
int i;
for (i=0;r->terrain->production[i].type;++i) {
if (res->type->rtype == r->terrain->production[i].type) break;
}
res->base = dice_rand(r->terrain->production[i].base);
res->divisor = dice_rand(r->terrain->production[i].divisor);
res->startlevel = 1;
}
*pres = res;
pres=&res->next;
}
*pres = NULL;
rss(F, token, sizeof(token));
if (strcmp(token, "noherb") != 0) {
const resource_type * rtype = rt_find(token);
assert(rtype && rtype->itype && fval(rtype->itype, ITF_HERB));
rsetherbtype(r, rtype->itype);
} else {
rsetherbtype(r, NULL);
}
rsetherbs(r, (short)ri(F));
rsetpeasants(r, ri(F));
rsetmoney(r, ri(F));
}
assert(r->terrain!=NULL);
assert(rhorses(r) >= 0);
assert(rpeasants(r) >= 0);
assert(rmoney(r) >= 0);
if (r->land) {
for (;;) {
const struct item_type * itype;
rss(F, token, sizeof(token));
if (!strcmp(token, "end")) break;
itype = it_find(token);
assert(itype->rtype->ltype);
r_setdemand(r, itype->rtype->ltype, ri(F));
}
if (global.data_version>=REGIONITEMS_VERSION) {
read_items(F, &r->land->items);
}
}
a_read(F, &r->attribs);
return r;
}
void
writeregion(FILE * F, const region * r)
{
ws(F, r->display?(const char *)r->display:"");
ws(F, r->terrain->_name);
wi(F, r->flags & RF_SAVEMASK);
wi(F, r->age);
wnl(F);
if (fval(r->terrain, LAND_REGION)) {
const item_type *rht;
struct demand * demand;
rawmaterial * res = r->resources;
ws(F, (const char *)r->land->name);
assert(rtrees(r,0)>=0);
assert(rtrees(r,1)>=0);
assert(rtrees(r,2)>=0);
wi(F, rtrees(r,0));
wi(F, rtrees(r,1));
wi(F, rtrees(r,2));
wi(F, rhorses(r));
while (res) {
ws(F, res->type->name);
wi(F, res->level);
wi(F, res->amount);
wi(F, res->startlevel);
wi(F, res->base);
wi(F, res->divisor);
res = res->next;
}
fputs("end ", F);
rht = rherbtype(r);
if (rht) {
ws(F, resourcename(rht->rtype, 0));
} else {
ws(F, "noherb");
}
wi(F, rherbs(r));
wi(F, rpeasants(r));
wi(F, rmoney(r));
if (r->land) for (demand=r->land->demands; demand; demand=demand->next) {
ws(F, resourcename(demand->type->itype->rtype, 0));
wi(F, demand->value);
}
fputs("end \n", F);
#if RELEASE_VERSION>=REGIONITEMS_VERSION
write_items(F, r->land->items);
wnl(F);
#endif
}
a_write(F, r->attribs);
wnl(F);
}
static ally **
2002-09-02 22:36:12 +02:00
addally(const faction * f, ally ** sfp, int aid, int state)
{
struct faction * af = findfaction(aid);
ally * sf;
state &= ~HELP_OBSERVE;
magicskillboost -- implementiert. Kann nur einmal pro Partei verwendet werden, und nur von einem Magier. Müssen wir erklären. Amulett des wahren Sehens und Ring der Unsichtbarkeit geben lediglich einen Bonus auf Wahrnehmung (+2) und Tarnung (+4). -- das define war nicht aktiviert Halblinge haben Armbrustschiessen +0 (statt +1). -- spearates Rassenfile für WDW Es gibt spezielle Regeln für Allianzen, und der HELFE und KONTAKTIERE Befehl sind eingeschränkt. Konkret kann kein HELP_FIGHT HELP_GUARD HELP_FSTEALTH HELP_MONEY gegeben werden (die anderen sind erlaubt). -- mit #defeine ALLIES_ONLY definiert Jede Allianz kann bis zu 15 Magier und 15 Alchemisten haben. Jede einzelne Partei der Allianz kann dabei nicht mehr als 15/Anzahl_Parteien (aufgerundet) Magier bzw. Alchemisten haben, und die Gesamtsumme darf 15 nicht übersteigen. -- mit #define ALLIANCE_LIMITS gemacht. Die Startgeschenke (Personen, Silber, ...) werden pro Allianz, nicht pro Spieler verteilt. Größere Allianzen bekommen also weniger pro Spieler. -- Nochmal geändert: Die Allianzen kriegen jede 168 Personen zum Start, weil sich das gut aufteilen lässt. Das wird auf 28 Einheiten pro Partei gesplittet, jede Einheit hat eines der Talente, außer der Starteinheit, die hat den magicskillboost. Einige Skills kommen öfter vor als andere, das ist nicht einfach vermeidbar. Sollte aber auch wurscht sein, es geht primär darum, lehren zu können. Es gibt ein Einheitenlimit von 1000 Einheiten pro Allianz. -- die Regel sparen wir uns einfach mal.
2003-12-14 22:45:47 +01:00
#ifndef REGIONOWNERS
state &= ~HELP_TRAVEL;
2002-09-02 22:36:12 +02:00
#endif
2004-05-04 21:34:35 +02:00
if (state==0) return sfp;
magicskillboost -- implementiert. Kann nur einmal pro Partei verwendet werden, und nur von einem Magier. Müssen wir erklären. Amulett des wahren Sehens und Ring der Unsichtbarkeit geben lediglich einen Bonus auf Wahrnehmung (+2) und Tarnung (+4). -- das define war nicht aktiviert Halblinge haben Armbrustschiessen +0 (statt +1). -- spearates Rassenfile für WDW Es gibt spezielle Regeln für Allianzen, und der HELFE und KONTAKTIERE Befehl sind eingeschränkt. Konkret kann kein HELP_FIGHT HELP_GUARD HELP_FSTEALTH HELP_MONEY gegeben werden (die anderen sind erlaubt). -- mit #defeine ALLIES_ONLY definiert Jede Allianz kann bis zu 15 Magier und 15 Alchemisten haben. Jede einzelne Partei der Allianz kann dabei nicht mehr als 15/Anzahl_Parteien (aufgerundet) Magier bzw. Alchemisten haben, und die Gesamtsumme darf 15 nicht übersteigen. -- mit #define ALLIANCE_LIMITS gemacht. Die Startgeschenke (Personen, Silber, ...) werden pro Allianz, nicht pro Spieler verteilt. Größere Allianzen bekommen also weniger pro Spieler. -- Nochmal geändert: Die Allianzen kriegen jede 168 Personen zum Start, weil sich das gut aufteilen lässt. Das wird auf 28 Einheiten pro Partei gesplittet, jede Einheit hat eines der Talente, außer der Starteinheit, die hat den magicskillboost. Einige Skills kommen öfter vor als andere, das ist nicht einfach vermeidbar. Sollte aber auch wurscht sein, es geht primär darum, lehren zu können. Es gibt ein Einheitenlimit von 1000 Einheiten pro Allianz. -- die Regel sparen wir uns einfach mal.
2003-12-14 22:45:47 +01:00
sf = calloc(1, sizeof(ally));
sf->faction = af;
if (!sf->faction) {
variant id;
id.i = aid;
ur_add(id, (void**)&sf->faction, resolve_faction);
}
sf->status = state & HELP_ALL;
2002-09-02 22:36:12 +02:00
while (*sfp) sfp=&(*sfp)->next;
*sfp = sf;
2004-05-04 21:34:35 +02:00
return &sf->next;
2002-09-02 22:36:12 +02:00
}
/** Reads a faction from a file.
* This function requires no context, can be called in any state. The
* faction may not already exist, however.
*/
faction *
readfaction(FILE * F, int encoding)
{
ally **sfp;
int planes;
int i = rid(F);
faction * f = findfaction(i);
2004-08-22 11:54:39 +02:00
char * email = NULL;
char token[32];
if (f==NULL) {
f = (faction *) calloc(1, sizeof(faction));
f->no = i;
} else {
f->allies = NULL; /* mem leak */
while (f->attribs) a_remove(&f->attribs, f->attribs);
}
f->subscription = ri(F);
if (alliances!=NULL) {
int allianceid = rid(F);
if (allianceid!=0) f->alliance = findalliance(allianceid);
if (f->alliance) {
faction_list * flist = malloc(sizeof(faction_list));
flist->data = f;
flist->next = f->alliance->members;
f->alliance->members = flist;
}
}
xrds(F, &f->name, encoding);
xrds(F, &f->banner, encoding);
if (quiet==0) printf(" - Lese Partei %s (%s)\n", f->name, factionid(f));
rds(F, &email);
if (set_email(&f->email, email)!=0) {
log_warning(("Invalid email address for faction %s: %s\n", itoa36(f->no), email));
set_email(&f->email, "");
}
free(email);
rds(F, &f->passw);
if (global.data_version >= OVERRIDE_VERSION) {
rds(F, &f->override);
} else {
2007-08-05 14:19:56 +02:00
f->override = strdup(itoa36(rng_int()));
}
rss(F, token, sizeof(token));
f->locale = find_locale(token);
f->lastorders = ri(F);
f->age = ri(F);
if (global.data_version < NEWRACE_VERSION) {
race_t rc = (char) ri(F);
f->race = new_race[rc];
} else {
rss(F, token, sizeof(token));
f->race = rc_find(token);
assert(f->race);
}
f->magiegebiet = (magic_t)ri(F);
#ifdef KARMA_MODULE
f->karma = ri(F);
#else
/* ignore karma */
ri(F);
#endif /* KARMA_MODULE */
f->flags = ri(F);
a_read(F, &f->attribs);
if (global.data_version>=CLAIM_VERSION) {
read_items(F, &f->items);
}
for (;;) {
int level;
fscanf(F, "%s", token);
if (strcmp("end", token)==0) break;
fscanf(F, "%d ", &level);
}
planes = ri(F);
while(--planes >= 0) {
int id = ri(F);
short ux = (short)ri(F);
short uy = (short)ri(F);
set_ursprung(f, id, ux, uy);
}
f->newbies = 0;
i = f->options = ri(F);
if ((i & (want(O_REPORT)|want(O_COMPUTER)))==0 && f->no!=MONSTER_FACTION) {
/* Kein Report eingestellt, Fehler */
f->options = f->options | want(O_REPORT) | want(O_ZUGVORLAGE);
}
sfp = &f->allies;
if (global.data_version<ALLIANCES_VERSION) {
int p = ri(F);
while (--p >= 0) {
int aid = rid(F);
int state = ri(F);
sfp = addally(f, sfp, aid, state);
}
} else {
for (;;) {
rs(F, token);
if (strcmp(token, "end")==0) break;
else {
int aid = atoi36(token);
int state = ri(F);
sfp = addally(f, sfp, aid, state);
}
}
}
read_groups(F, f);
#ifdef ENEMIES
read_enemies(F, f);
#endif
return f;
}
void
writefaction(FILE * F, const faction * f)
{
ally *sf;
ursprung *ur;
wi36(F, f->no);
wi(F, f->subscription);
if (alliances!=NULL) {
if (f->alliance) wi36(F, f->alliance->id);
else wi36(F, 0);
}
2002-09-02 22:36:12 +02:00
ws(F, (const char *)f->name);
ws(F, (const char *)f->banner);
ws(F, f->email);
ws(F, (const char *)f->passw);
ws(F, (const char *)f->override);
ws(F, locale_name(f->locale));
wi(F, f->lastorders);
wi(F, f->age);
ws(F, f->race->_name[0]);
wnl(F);
wi(F, f->magiegebiet);
#ifdef KARMA_MODULE
wi(F, f->karma);
#else
wi(F, 0);
#endif /* KARMA_MODULE */
wi(F, f->flags&FFL_SAVEMASK);
a_write(F, f->attribs);
wnl(F);
#if RELEASE_VERSION>=CLAIM_VERSION
write_items(F, f->items);
wnl(F);
#endif
fputs("end ", F);
wnl(F);
wi(F, listlen(f->ursprung));
for(ur = f->ursprung;ur;ur=ur->next) {
wi(F, ur->id);
wi(F, ur->x);
wi(F, ur->y);
}
wnl(F);
wi(F, f->options & ~want(O_DEBUG));
wnl(F);
for (sf = f->allies; sf; sf = sf->next) {
int no = (sf->faction!=NULL)?sf->faction->no:0;
int status = alliedfaction(NULL, f, sf->faction, HELP_ALL);
if (status!=0) {
wi36(F, no);
wi(F, sf->status);
}
}
fprintf(F, "end ");
wnl(F);
write_groups(F, f->groups);
#ifdef ENEMIES
write_enemies(F, f->enemies);
#endif
2002-03-03 16:26:17 +01:00
}
int
readgame(const char * filename, int backup)
{
int i, n, p;
faction *f, **fp;
region *r;
building *b, **bp;
ship **shp;
unit *u;
FILE * F;
int rmax = maxregions;
char path[MAX_PATH];
char token[32];
2007-09-02 11:59:28 +02:00
int encoding = enc_gamedata;
const struct building_type * bt_lighthouse = bt_find("lighthouse");
sprintf(path, "%s/%s", datapath(), filename);
log_printf("- reading game data from %s\n", filename);
if (backup) create_backup(path);
F = cfopen(path, "r");
if (F==NULL) {
log_error(("Keine Spieldaten gefunden.\n"));
return -1;
}
2007-09-02 11:59:28 +02:00
/* recognize UTF8 BOM */
rss(F, token, sizeof(token));
if (memcmp(token, utf8_bom, 3)==0 && enc_gamedata!=XML_CHAR_ENCODING_UTF8) {
encoding = XML_CHAR_ENCODING_UTF8;
log_warning(("Found UTF-8 BOM, assuming unicode gamedata.\n"));
global.data_version = atoi(token+3);
} else {
2007-09-03 21:52:51 +02:00
if (encoding==XML_CHAR_ENCODING_NONE) {
encoding=XML_CHAR_ENCODING_8859_1;
log_warning(("No BOM, assuming 8859-1 gamedata.\n"));
}
2007-09-02 11:59:28 +02:00
global.data_version = atoi(token);
}
2007-09-03 21:52:51 +02:00
enc_gamedata = encoding;
assert(global.data_version>=MIN_VERSION || !"unsupported data format");
assert(global.data_version<=RELEASE_VERSION || !"unsupported data format");
assert(global.data_version >= GROWTREE_VERSION);
if(global.data_version >= SAVEXMLNAME_VERSION) {
char basefile[1024];
const char *basearg = "(null)";
rs(F, basefile);
assert(xmlfile != NULL);
basearg = strrchr(xmlfile, '/');
if (basearg==NULL) {
basearg = xmlfile;
} else {
++basearg;
}
if (strcmp(basearg, basefile)!=0) {
log_warning(("xmlfile mismatch: datafile contains %s, argument/default is %s\n", basefile, basearg));
printf("WARNING: any key to continue, Ctrl-C to stop\n");
getchar();
}
}
a_read(F, &global.attribs);
global.data_turn = turn = ri(F);
ri(F); /* max_unique_id = */
nextborder = ri(F);
/* Planes */
planes = NULL;
n = ri(F);
while(--n >= 0) {
plane *pl = calloc(1, sizeof(plane));
pl->id = ri(F);
2007-09-02 11:59:28 +02:00
xrds(F, &pl->name, encoding);
pl->minx = (short)ri(F);
pl->maxx = (short)ri(F);
pl->miny = (short)ri(F);
pl->maxy = (short)ri(F);
pl->flags = ri(F);
if (global.data_version>WATCHERS_VERSION) {
rss(F, token, sizeof(token));
while (strcmp(token, "end")!=0) {
watcher * w = calloc(sizeof(watcher),1);
variant fno;
fno.i = atoi36(token);
w->mode = (unsigned char)ri(F);
w->next = pl->watchers;
pl->watchers = w;
ur_add(fno, (void**)&w->faction, resolve_faction);
rss(F, token, sizeof(token));
}
}
a_read(F, &pl->attribs);
addlist(&planes, pl);
}
/* Read factions */
if (global.data_version>=ALLIANCES_VERSION) {
read_alliances(F);
}
n = ri(F);
if (quiet<2) printf(" - Einzulesende Parteien: %d\n", n);
fp = &factions;
while (*fp) fp=&(*fp)->next;
/* fflush (stdout); */
while (--n >= 0) {
2007-09-02 11:59:28 +02:00
faction * f = readfaction(F, encoding);
*fp = f;
fp = &f->next;
fhash(f);
}
*fp = 0;
/* Benutzte Faction-Ids */
i = ri(F);
while (i--) {
ri(F); /* used faction ids. ignore. */
}
/* Regionen */
n = ri(F);
if (rmax<0) rmax = n;
if (quiet<2) printf(" - Einzulesende Regionen: %d/%d\r", rmax, n);
2005-01-03 22:28:57 +01:00
if (loadplane || dirtyload || firstx || firsty || maxregions>=0) {
incomplete_data = true;
}
while (--n >= 0) {
unit **up;
boolean skip = false;
short x = (short)ri(F);
short y = (short)ri(F);
plane * pl = findplane(x, y);
if (firstx || firsty) {
if (x!=firstx || y!=firsty) {
skip = true;
} else {
firstx=0;
firsty=0;
if (rmax>0) rmax = min(n, rmax)-1;
}
}
if (loadplane && (!pl || pl->id!=loadplane)) skip = true;
if (rmax==0) {
if (dirtyload) break;
skip = true;
}
if (quiet<2 && (n & 0x3FF) == 0) { /* das spart extrem Zeit */
printf(" - Einzulesende Regionen: %d/%d ", rmax, n);
printf("* %d,%d \r", x, y);
}
if (skip) {
char * r;
#define SKIPSIZE 4096
char buffer[SKIPSIZE];
buffer[SKIPSIZE-1] = '@';
do {
r = fgets(buffer, sizeof(buffer), F); /* skip region */
if (r && buffer[SKIPSIZE-1]!='@') {
while (r && buffer[SKIPSIZE-1]!='@') {
/* our buffer was not big enough */
buffer[SKIPSIZE-1] = '@';
r = fgets(buffer, sizeof(buffer), F);
}
if (r) continue;
}
} while (r && buffer[0]!='\n');
continue;
}
--rmax;
2007-09-02 11:59:28 +02:00
r = readregion(F, encoding, x, y);
/* Burgen */
p = ri(F);
bp = &r->buildings;
while (--p >= 0) {
b = (building *) calloc(1, sizeof(building));
b->no = rid(F);
*bp = b;
bp = &b->next;
bhash(b);
2007-09-02 11:59:28 +02:00
xrds(F, &b->name, encoding);
if (lomem) rds(F, 0);
2007-09-02 11:59:28 +02:00
else xrds(F, &b->display, encoding);
b->size = ri(F);
rss(F, token, sizeof(token));
b->type = bt_find(token);
b->region = r;
a_read(F, &b->attribs);
if (b->type==bt_lighthouse) {
r->flags |= RF_LIGHTHOUSE;
}
}
/* Schiffe */
p = ri(F);
shp = &r->ships;
while (--p >= 0) {
ship * sh = (ship *) calloc(1, sizeof(ship));
sh->region = r;
sh->no = rid(F);
*shp = sh;
shp = &sh->next;
shash(sh);
2007-09-02 11:59:28 +02:00
xrds(F, &sh->name, encoding);
if (lomem) rds(F, NULL);
2007-09-02 11:59:28 +02:00
else xrds(F, &sh->display, encoding);
rss(F, token, sizeof(token));
sh->type = st_find(token);
if (sh->type==NULL) {
/* old datafiles */
sh->type = st_find((const char *)locale_string(default_locale, token));
}
assert(sh->type || !"ship_type not registered!");
sh->size = ri(F);
sh->damage = ri(F);
/* Attribute rekursiv einlesen */
sh->coast = (direction_t)ri(F);
a_read(F, &sh->attribs);
}
*shp = 0;
/* Einheiten */
p = ri(F);
up = &r->units;
while (--p >= 0) {
2007-09-02 11:59:28 +02:00
unit * u = readunit(F, encoding);
sc_mage * mage;
assert(u->region==NULL);
u->region = r;
2007-02-28 22:50:19 +01:00
if (u->flags&UFL_GUARD) fset(r, RF_GUARDED);
*up = u;
up = &u->next;
update_interval(u->faction, u->region);
mage = get_mage(u);
if (mage && mage->spellcount<0) {
mage->spellcount = 0;
updatespelllist(u);
}
}
}
if (quiet<2) printf("\n");
if (!dirtyload) {
read_borders(F);
}
fclose(F);
/* Unaufgeloeste Zeiger initialisieren */
if (quiet<2) printf("\n - Referenzen initialisieren...\n");
resolve();
for (r=regions;r;r=r->next) {
if (r->flags & RF_LIGHTHOUSE) {
building * b;
for (b=r->buildings;b;b=b->next) update_lighthouse(b);
}
}
if (quiet < 2) printf(" - Regionen initialisieren & verbinden...\n");
for (f = factions; f; f = f->next) {
for (u = f->units; u; u = u->nextF) {
if (u->number>0) {
f->alive = 1;
break;
}
}
}
if (findfaction(0)) {
findfaction(0)->alive = 1;
}
if (loadplane || maxregions>=0) {
remove_empty_factions(false);
}
return 0;
}
int
writegame(const char *filename, int quiet)
{
char *base;
int n;
faction *f;
region *r;
building *b;
ship *sh;
unit *u;
plane *pl;
FILE * F;
char path[MAX_PATH];
sprintf(path, "%s/%s", datapath(), filename);
#ifdef HAVE_UNISTD_H
if (access(path, R_OK) == 0) {
/* make sure we don't overwrite some hardlinkedfile */
unlink(path);
}
#endif
F = cfopen(path, "w");
if (F==NULL) {
return -1;
} else {
fwrite(utf8_bom, 1, 3, F);
}
if (!quiet)
printf("Schreibe die %d. Runde...\n", turn);
/* globale Variablen */
wi(F, RELEASE_VERSION);
wnl(F);
base = strrchr(xmlfile, '/');
if(base) {
ws(F, base+1);
} else {
ws(F, xmlfile);
}
wnl(F);
a_write(F, global.attribs);
wnl(F);
wi(F, turn);
wi(F, 0/*max_unique_id*/);
wi(F, nextborder);
/* Write planes */
wnl(F);
wi(F, listlen(planes));
wnl(F);
for(pl = planes; pl; pl=pl->next) {
watcher * w;
wi(F, pl->id);
ws(F, pl->name);
wi(F, pl->minx);
wi(F, pl->maxx);
wi(F, pl->miny);
wi(F, pl->maxy);
wi(F, pl->flags);
w = pl->watchers;
while (w) {
if (w->faction) {
wi36(F, w->faction->no);
wi(F, w->mode);
}
w = w->next;
}
fputs("end ", F);
a_write(F, pl->attribs);
wnl(F);
}
/* Write factions */
#if RELEASE_VERSION>=ALLIANCES_VERSION
write_alliances(F);
#endif
n = listlen(factions);
wi(F, n);
wnl(F);
if (quiet < 2) printf(" - Schreibe %d Parteien...\n",n);
for (f = factions; f; f = f->next) {
writefaction(F, f);
}
wi(F, 0); /* used faction ids. old stuff */
wnl(F);
/* Write regions */
n=listlen(regions);
wi(F, n);
wnl(F);
if (quiet<2) printf(" - Schreibe Regionen: %d \r", n);
for (r = regions; r; r = r->next, --n) {
/* plus leerzeile */
if (quiet<2 && (n%1024)==0) { /* das spart extrem Zeit */
printf(" - Schreibe Regionen: %d \r", n);
fflush(stdout);
}
wnl(F);
wi(F, r->x);
wi(F, r->y);
writeregion(F, r);
wi(F, listlen(r->buildings));
wnl(F);
for (b = r->buildings; b; b = b->next) {
wi36(F, b->no);
ws(F, b->name);
ws(F, b->display?b->display:"");
wi(F, b->size);
ws(F, b->type->_name);
wnl(F);
a_write(F, b->attribs);
wnl(F);
}
wi(F, listlen(r->ships));
wnl(F);
for (sh = r->ships; sh; sh = sh->next) {
assert(sh->region == r);
wi36(F, sh->no);
ws(F, (const char *)sh->name);
ws(F, sh->display?(const char *)sh->display:"");
ws(F, sh->type->name[0]);
wi(F, sh->size);
wi(F, sh->damage);
wi(F, sh->coast);
wnl(F);
a_write(F, sh->attribs);
wnl(F);
}
wi(F, listlen(r->units));
wnl(F);
for (u = r->units; u; u = u->next) {
writeunit(F, u);
}
}
wnl(F);
write_borders(F);
wnl(F);
fclose(F);
if (quiet<2) printf("\nOk.\n");
return 0;
}
int
a_readint(attrib * a, FILE * f)
{
/* assert(sizeof(int)==sizeof(a->data)); */
fscanf(f, "%d", &a->data.i);
return AT_READ_OK;
}
void
a_writeint(const attrib * a, FILE * f)
{
fprintf(f, "%d ", a->data.i);
}
int
a_readshorts(attrib * a, FILE * f)
{
if (global.data_version<ATTRIBREAD_VERSION) {
return a_readint(a, f);
}
fscanf(f, "%hd %hd", &a->data.sa[0], &a->data.sa[1]);
return AT_READ_OK;
}
void
a_writeshorts(const attrib * a, FILE * f)
{
fprintf(f, "%hd %hd ", a->data.sa[0], a->data.sa[1]);
}
int
a_readchars(attrib * a, FILE * f)
{
int i;
if (global.data_version<ATTRIBREAD_VERSION) {
return a_readint(a, f);
}
for (i=0;i!=4;++i) {
int c;
2006-02-12 10:22:37 +01:00
fscanf(f, "%d", &c);
a->data.ca[i] = (char)c;
}
return AT_READ_OK;
}
void
a_writechars(const attrib * a, FILE * f)
{
2006-02-12 10:22:37 +01:00
int i;
2006-02-12 10:42:52 +01:00
for (i=0;i!=4;++i) {
int c = a->data.ca[i];
2006-02-12 10:22:37 +01:00
fprintf(f, "%d ", c);
}
}
int
a_readvoid(attrib * a, FILE * f)
{
if (global.data_version<ATTRIBREAD_VERSION) {
return a_readint(a, f);
}
return AT_READ_OK;
}
void
a_writevoid(const attrib * a, FILE * f)
{
}
int
a_readstring(attrib * a, FILE * F)
{
char zText[4096];
freadstr(F, enc_gamedata, zText, sizeof(zText));
a->data.v = strdup(zText);
return AT_READ_OK;
}
void
a_writestring(const attrib * a, FILE * f)
{
assert(a->data.v);
fprintf(f, "\"%s\" ", (char*)a->data.v);
}
void
a_finalizestring(attrib * a)
{
free(a->data.v);
}