Compare commits

...
Sign in to create a new pull request.

20 commits

Author SHA1 Message Date
c313aafd8a WIP empdump, %a 2008-02-26 08:18:59 +01:00
add345f77f WIP empdump, fill in stuff not in xdump 2008-02-26 08:18:58 +01:00
4ef3574427 WIP empdump, can't shrink tables 2008-02-26 08:18:58 +01:00
b9c725224e Make xdump code available for future use outside the server
Move the bits useful there from src/lib/commands/xdump.c to new
include/xdump.h and src/lib/common/xdump.c.
2008-02-26 08:09:52 +01:00
d83dd2f5e4 Prepare some xdump code for future use outside the server
We don't have player->cnum, player->god and pr() there.  Pass
parameters instead.
2008-02-26 08:09:52 +01:00
3f15ea4bea Document tables EF_COMM, EF_TRADE and EF_COUNTRY have an owner
xdvisible() relies on that.

Set EFF_OWNER in the table definitions.  EFF_OWNER is not actually
used right now, so this doesn't fix anything broken.
2008-02-26 07:53:21 +01:00
1aa3cdb0ca Simplify unit_put() 2008-02-25 21:50:25 +01:00
e7e61687f4 Use get_empobj() instead of get_empobjp() in unit_map()
The get_FOOp() macros are generally avoided outside the update,
because direct access to the sector cache needs synchronization to be
safe.  unit_map() didn't access the cache directly until it was
converted from get_ship() & friends to get_empobjp() in commit
fec9878c.  Switching to get_empobj() reverts the change to direct
access while keeping the simplification.
2008-02-25 21:50:25 +01:00
9a1fef87fe Define common get/put macros for empobj
This turns existing functions get_empobjp() and put_empobj() into
equivalent macros.
2008-02-25 21:50:25 +01:00
5b7a6af94e Fix get_empobjp() not to reject EF_SECTOR
There's no technical reason for rejecting sector access by id.  It's
unusual, but not wrong.

Also remove the superfluous test for EF_BAD; ef_ptr() covers that.
2008-02-25 21:50:25 +01:00
e63f38833b Fix put_empobj() for types other than units
put_empobj() used struct empobj member uid, which is valid only for
units.  Existing users pass only units, fortunately.  Fix by making it
take type and uid parameters.
2008-02-25 21:50:25 +01:00
3117ef9c7a Give unit_map() internal linkage 2008-02-25 21:50:25 +01:00
2dea1b914d New realm selector timestamp
The timestamp always existed, but the selector was missing.  It's
needed for incremental xdumps.
2008-02-25 21:50:25 +01:00
38ead5e199 Clean up ugly line break 2008-02-25 21:50:24 +01:00
5a1cfeba95 Fix test for water in explore
This led to a bogus message when an interactive explore moved onto a
bridge and got prompted, the bridge was destroyed, and the player
stopped the explore "on the water".
2008-02-25 21:50:24 +01:00
fc807a4c0a Make sector types signed
get_empobj_chr() and emp_obj_chr_name() access struct sctstr member
sct_type through struct empobj member type.  This is technically
non-portable, because the two differ in signedness.  It was also
undocumented.  Fix by making sct_type signed.  sct_newtype as well,
for consistency.

map_char() uses unsigned char for a sector type argument.  Change that
to int.  Matches how this is done elsewhere.
2008-02-25 21:50:21 +01:00
f8a35dda1e Make xundump capable of extending tables
Use ef_ensure_space() in getobj().  This also makes sure objects are
properly initialized before undumping writes to them.

Clean up how sentinels are appended: instead of keeping its slot
reserved while undumping, keep it in the table, and strip it off when
done.
2008-02-24 08:40:08 +01:00
d4ac7d94b2 Generalize ef_extend() to non-file-backed tables
ef_extend() extended the file bypassing the cache, which screws up the
cache if it's EFF_MEM.  It fixed that by closing and reopening the
table.  Cheesy, and worked only for file-backed tables.

Rewrite ef_extend() to remap the cache properly for EFF_MEM.  While
there, simplify the !EFF_MEM case: steal a cache slot instead of
allocating a buffer.

Factor cache mapping out of ef_open() and ef_extend() into new
ef_remap_cache().
2008-02-24 08:40:05 +01:00
642c11eb64 Replace EFF_RDONLY by EFF_PRIVATE
Read-only was a bit of a misnomer: you could write to the table by
obtaining a pointer into it from ef_ptr(), you just couldn't write to
the backing file.

Semantic changes:

* ef_flush() is now allowed when the table is file-backed or privately
  mapped.  Before, it had to be file-backed.  Flushing a privately
  mapped table does nothing, just like flushing a read-only table did.

* ef_write() is now allowed when the table is file-backed or fully
  cached.  Before, it had to be file-backed and not read-only.
  Writing to a privately mapped file-backed table doesn't write to the
  file.

* ef_extend() is not implemented for privately mapped tables, just
  like it wasn't implemented for read-only tables.
2008-02-21 21:05:16 +01:00
53bcf04664 Remove obsolete comment
Obsolete since ef_open() locks the file (commit de124108).
2008-02-19 20:27:01 +01:00
24 changed files with 1142 additions and 362 deletions

View file

@ -104,7 +104,7 @@ deps := $(obj:.o=.d)
# Library archives: # Library archives:
libs := $(addprefix lib/, libcommon.a libas.a libgen.a libglobal.a) libs := $(addprefix lib/, libcommon.a libas.a libgen.a libglobal.a)
# Programs: # Programs:
util := $(addprefix src/util/, $(addsuffix $(EXEEXT), empsched fairland files pconfig)) util := $(addprefix src/util/, $(addsuffix $(EXEEXT), empdump empsched fairland files pconfig))
client := src/client/empire$(EXEEXT) client := src/client/empire$(EXEEXT)
server := src/server/emp_server$(EXEEXT) server := src/server/emp_server$(EXEEXT)
# Info subjects: # Info subjects:

View file

@ -174,9 +174,10 @@ precisely specified. We use EBNF (ISO 14977) for syntax, except we
use '-' in meta-identifiers and omit the concatenation symbol ','. use '-' in meta-identifiers and omit the concatenation symbol ','.
table = header { record } footer ; table = header { record } footer ;
header = "XDUMP" space [ "meta" space ] name space timestamp newline ; header = "XDUMP" space [ "meta" space ]
name = name-chr { name-chr } ; identifier space timestamp newline ;
name-chr = ? ASCII characters 33..126 ? ; identifier = id-chr { id-chr } ;
id-char = ? ASCII characters 33..126 except '"#()<>=' ? ;
timestamp = intnum ; timestamp = intnum ;
footer = "/" number newline ; footer = "/" number newline ;
record = [ fields ] newline ; record = [ fields ] newline ;
@ -211,7 +212,7 @@ Notes:
Semantics: Semantics:
* The table name in the header is one of the names in xdump table. * The table identifier in the header is one of the names in xdump table.
* The timestamp increases monotonically. It has a noticeable * The timestamp increases monotonically. It has a noticeable
granularity: game state may change between an xdump and the next granularity: game state may change between an xdump and the next
@ -524,7 +525,7 @@ EBNF changes:
* Header and footer: * Header and footer:
header = "config" name newline colhdr newline ; header = "config" identifier newline colhdr newline ;
colhdr = { identifier [ "(" ( intnum | identifier ) ")" ] } [ "..." ] ; colhdr = { identifier [ "(" ( intnum | identifier ) ")" ] } [ "..." ] ;
footer = "/config" newline ; footer = "/config" newline ;
@ -542,7 +543,7 @@ EBNF changes:
update. update.
- The column header is due to the self-containedness requirement. - The column header is due to the self-containedness requirement.
It contains just the essential bit of meta-data: the column name. It contains just the essential bit of meta-data: the column names.
* Symbolic fields: * Symbolic fields:
@ -662,7 +663,7 @@ and section `Objectives':
enemy ships, nations). enemy ships, nations).
(4) Bandwidth will be minimized (i.e. the format will be as (4) Bandwidth will be minimized (i.e. the format will be as
concise as possible) while remaining human readable (i.e. no concise as possible) while remaining human-readable (i.e. no
binary messages). [Note that data compression may be added at a later binary messages). [Note that data compression may be added at a later
date, but if it is added, it will be added on a separate port to date, but if it is added, it will be added on a separate port to
maintain backwards compatability.] maintain backwards compatability.]
@ -735,14 +736,14 @@ f. The data to sync is not readily available the server.
limited view on certain parts of server game state on certain limited view on certain parts of server game state on certain
events. events.
To be complete, a machine readable protocol must disclose as much To be complete, a machine-readable protocol must disclose as much
information as the human readable output. Tracking server game information as the human-readable output. Tracking server game
state changes cannot do that alone. For instance, lookout tells state changes cannot do that alone. For instance, lookout tells
you ship#, owner and location. That event does not trigger any you ship#, owner and location. That event does not trigger any
state change on the server! state change on the server!
To be correct, a machine readable protocol must disclose no more To be correct, a machine-readable protocol must disclose no more
information than the human readable output. When you observe a information than the human-readable output. When you observe a
server game state change, you can only guess what event triggered server game state change, you can only guess what event triggered
it, and what it disclosed to which player. You're stuck with it, and what it disclosed to which player. You're stuck with
conservative assumptions. That's the death knell for completeness. conservative assumptions. That's the death knell for completeness.
@ -761,7 +762,7 @@ a shortcut, and it didn't work. That doesn't mean there's no way at
all. I believe the only way to get this done right is by tracking all. I believe the only way to get this done right is by tracking
*events*. Whenever something is printed to a player, be it live *events*. Whenever something is printed to a player, be it live
connection or telegram, we need to transmit precisely the same connection or telegram, we need to transmit precisely the same
information in machine readable form. Much more work. information in machine-readable form. Much more work.
xdump shares valuable ideas with C_SYNC, e.g. using selector xdump shares valuable ideas with C_SYNC, e.g. using selector
meta-data. It is, however, much more modest in scope. We're pretty meta-data. It is, however, much more modest in scope. We're pretty

View file

@ -41,9 +41,11 @@
#include "item.h" #include "item.h"
struct comstr { struct comstr {
/* initial part must match struct empobj */
short ef_type; short ef_type;
natid com_owner; natid com_owner;
short com_uid; short com_uid;
/* end of part matching struct empobj */
i_type com_type; i_type com_type;
int com_amount; int com_amount;
float com_price; float com_price;

View file

@ -56,7 +56,8 @@ struct empobj {
short uid; short uid;
coord x; /* is valid if EFF_XY is set in table def. */ coord x; /* is valid if EFF_XY is set in table def. */
coord y; /* is valid if EFF_XY is set in table def. */ coord y; /* is valid if EFF_XY is set in table def. */
signed char type; signed char type; /* is valid for sectors and units */
/* remaining are valid for units */
signed char effic; signed char effic;
signed char mobil; signed char mobil;
unsigned char off; unsigned char off;
@ -88,9 +89,11 @@ union empobj_storage {
struct empobj_chr; struct empobj_chr;
#define get_empobj(type, n, p) ef_read((type), (n), (p))
#define put_empobj(type, n, p) ef_write((type), (n), (p))
#define get_empobjp(type, n) ((struct empobj *)ef_ptr((type), (n)))
extern char *obj_nameof(struct empobj *gp); extern char *obj_nameof(struct empobj *gp);
extern struct empobj *get_empobjp(int type, int id);
extern int put_empobj(struct empobj *gp);
extern struct empobj_chr *get_empobj_chr(struct empobj *gp); extern struct empobj_chr *get_empobj_chr(struct empobj *gp);
extern char *emp_obj_chr_name(struct empobj *gp); extern char *emp_obj_chr_name(struct empobj *gp);
extern int get_empobj_mob_max(int type); extern int get_empobj_mob_max(int type);

View file

@ -57,7 +57,7 @@ struct empfile {
int cids; /* # entries in cache */ int cids; /* # entries in cache */
int fids; /* # entries in table */ int fids; /* # entries in table */
int fd; /* file descriptor, -1 if not open */ int fd; /* file descriptor, -1 if not open */
/* flags bits EFF_RDONLY, EFF_CUSTOM also vary */ /* flags bits EFF_PRIVATE, EFF_CUSTOM also vary */
/* User callbacks */ /* User callbacks */
void (*init)(int, void *); /* called after entry creation, unless null */ void (*init)(int, void *); /* called after entry creation, unless null */
@ -86,8 +86,8 @@ struct empfile {
/* Flags set when table contents is mapped */ /* Flags set when table contents is mapped */
/* Table is entirely in memory */ /* Table is entirely in memory */
#define EFF_MEM bit(8) #define EFF_MEM bit(8)
/* Table is read-only */ /* Table is privately mapped: changes don't affect the underlying file */
#define EFF_RDONLY bit(9) #define EFF_PRIVATE bit(9)
/* Table is customized (configuration tables only) */ /* Table is customized (configuration tables only) */
#define EFF_CUSTOM bit(10) #define EFF_CUSTOM bit(10)
/* Transient flags, only occur in argument of ef_open() */ /* Transient flags, only occur in argument of ef_open() */
@ -97,7 +97,8 @@ struct empfile {
/* /*
* Empire `file types' * Empire `file types'
* These are really table IDs. Some tables are backed by files, some * These are really table IDs. Some tables are backed by files, some
* are compiled into the server. * are compiled into the server, some initialized from configuration
* files.
*/ */
enum { enum {
/* Error value */ /* Error value */

View file

@ -315,8 +315,8 @@ extern int sct_typematch(char *);
extern int demand_update_want(int *, int *, int); extern int demand_update_want(int *, int *, int);
extern int demand_check(void); extern int demand_check(void);
extern int demandupdatecheck(void); extern int demandupdatecheck(void);
/* xundump.c */ /* xdump.c xundump.c */
extern int xundump(FILE *, char *, int *, int); /* in xdump.h */
/* /*
* src/lib/gen/ *.c * src/lib/gen/ *.c
@ -480,7 +480,6 @@ extern int islist(char *);
/* maps.c */ /* maps.c */
extern int do_map(int bmap, int unit_type, char *arg1, char *arg2); extern int do_map(int bmap, int unit_type, char *arg1, char *arg2);
extern int draw_map(int, char, int, struct nstr_sect *); extern int draw_map(int, char, int, struct nstr_sect *);
extern int unit_map(int, int, struct nstr_sect *, char *);
extern int display_region_map(int bmap, int unit_type, coord curx, extern int display_region_map(int bmap, int unit_type, coord curx,
coord cury, char *arg); coord cury, char *arg);
extern int bmaps_intersect(natid, natid); extern int bmaps_intersect(natid, natid);

View file

@ -48,8 +48,8 @@ struct sctstr {
short sct_elev; /* elevation/depth */ short sct_elev; /* elevation/depth */
coord sct_x; /* x coord of sector */ coord sct_x; /* x coord of sector */
coord sct_y; /* y coord of sector */ coord sct_y; /* y coord of sector */
signed char sct_type; /* sector type */
/* end of part matching struct empobj */ /* end of part matching struct empobj */
unsigned char sct_type; /* sector type */
unsigned char sct_effic; /* 0% to 100% */ unsigned char sct_effic; /* 0% to 100% */
short sct_mobil; /* mobility units */ short sct_mobil; /* mobility units */
unsigned char sct_loyal; /* updates until civilans "converted" */ unsigned char sct_loyal; /* updates until civilans "converted" */
@ -65,7 +65,7 @@ struct sctstr {
short sct_fill; /* gunk */ short sct_fill; /* gunk */
unsigned char sct_work; /* pct of civ actually working */ unsigned char sct_work; /* pct of civ actually working */
unsigned char sct_coastal; /* is this a coastal sector? */ unsigned char sct_coastal; /* is this a coastal sector? */
unsigned char sct_newtype; /* for changing designations */ signed char sct_newtype; /* for changing designations */
unsigned char sct_min; /* ease of mining ore */ unsigned char sct_min; /* ease of mining ore */
unsigned char sct_gmin; /* amount of gold ore */ unsigned char sct_gmin; /* amount of gold ore */
unsigned char sct_fertil; /* fertility of soil */ unsigned char sct_fertil; /* fertility of soil */

View file

@ -41,9 +41,11 @@
#include "types.h" #include "types.h"
struct trdstr { struct trdstr {
/* initial part must match struct empobj */
short ef_type; short ef_type;
natid trd_owner; natid trd_owner;
short trd_uid; short trd_uid;
/* end of part matching struct empobj */
short trd_type; short trd_type;
short trd_unitid; short trd_unitid;
long trd_price; long trd_price;

59
include/xdump.h Normal file
View file

@ -0,0 +1,59 @@
/*
* Empire - A multi-player, client/server Internet based war game.
* Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---
*
* See files README, COPYING and CREDITS in the root of the source
* tree for related information and legal notices. It is expected
* that future projects/authors will amend these files as needed.
*
* ---
*
* xdump.h: Extended dumps
*
* Known contributors to this file:
* Markus Armbruster, 2008
*/
#ifndef XDUMP_H
#define XDUMP_H
#include <stdio.h>
#include "nsc.h"
#include "types.h"
/* xdump descriptor */
struct xdstr {
natid cnum; /* dump for this country */
int divine; /* is this a deity dump? */
int human; /* dump human-readable format */
void (*pr)(char *fmt, ...); /* callback for printing dump */
};
struct xdstr *xdinit(struct xdstr *, natid, int, void (*)(char *, ...));
extern void xdhdr(struct xdstr *, char *, int);
extern void xdcolhdr(struct xdstr *, struct castr[]);
extern void xdflds(struct xdstr *, struct castr[], void *);
extern struct valstr *xdeval(struct valstr *, struct xdstr *, nsc_type, void *, ptrdiff_t, int, int);
extern char *xdprval(struct xdstr *, struct valstr *, char *);
extern void xdftr(struct xdstr *, int);
extern int xdmeta(struct xdstr *, int);
extern int xundump(FILE *, char *, int *, int);
#endif

57
man/empdump.6 Normal file
View file

@ -0,0 +1,57 @@
.TH EMPDUMP 6
.SH NAME
empdump \- Export/import Empire game state
.SH SYNOPSIS
.B empdump
[
.B \-mtxhv
]
[
.BI \-e " configfile"
]
[
.I dump-file
]
.br
.SH DESCRIPTION
.B empdump
imports and exports game state as plain text.
.SH OPTIONS
.TP
.BI \-e " configfile"
Use game configuration in \fIconfigfile\fR.
.TP
.B \-h
Help. Print brief usage information and exit.
.TP
.TP
.B \-m
Use machine-readable format for export. Import always recognizes both
machine-readable and human-readable format.
.TP
.B \-t
Test import, don't update game state.
.TP
.B \-v
Print version information and exit.
.TP
.B \-x
Export game state to standard output.
.SH OPERANDS
.TP
.I dump-file
The file to import.
.SH "LIMITATIONS"
.B empdump
can't export player bmaps, power report, telegrams, announcements,
message of the day, no-login message and log files. Exported
floating-point values may be inexact. Importing an exported game
state may not result in identical data files; besides the loss of
floating-point precision just mentioned, coordinates are normalized,
and characters beyond a string's terminating zero in a character array
may be lost.
FIXME can't change file size, fix or document here
.SH "SEE ALSO"
\fIemp_server\fR(6).
.SH AUTHOR
Markus Armbruster <armbru@pond.sub.org>

View file

@ -196,7 +196,7 @@ explore(void)
putsect(&sect); putsect(&sect);
return RET_FAIL; return RET_FAIL;
} }
if (chksect.sct_type == '.') { if (chksect.sct_type == SCT_WATER) {
pr("Bridge disappeared!\n"); pr("Bridge disappeared!\n");
getsect(start.sct_x, start.sct_y, &start); getsect(start.sct_x, start.sct_y, &start);
start.sct_flags &= ~MOVE_IN_PROGRESS; start.sct_flags &= ~MOVE_IN_PROGRESS;

View file

@ -321,7 +321,7 @@ mission(void)
gp->mission = mission; gp->mission = mission;
gp->opx = x; gp->opx = x;
gp->opy = y; gp->opy = y;
put_empobj(gp); put_empobj(type, gp->uid, gp);
} }
if (num == 0) { if (num == 0) {
pr("No %s%s\n", ef_nameof(type), splur(num)); pr("No %s%s\n", ef_nameof(type), splur(num));

View file

@ -74,8 +74,7 @@ nati(void)
mil = sect.sct_item[I_MILIT]; mil = sect.sct_item[I_MILIT];
pr("%d%% eff %s at %s has %d civilian%s & %d military\n", pr("%d%% eff %s at %s has %d civilian%s & %d military\n",
sect.sct_effic, sect.sct_effic,
(sect.sct_type == sect.sct_type == SCT_CAPIT ? "capital" : "mountain capital",
SCT_CAPIT ? "capital" : "mountain capital"),
xyas(sect.sct_x, sect.sct_y, cnum), civ, splur(civ), xyas(sect.sct_x, sect.sct_y, cnum), civ, splur(civ),
mil); mil);
} }

View file

@ -28,168 +28,17 @@
* xdump.c: Extended dump * xdump.c: Extended dump
* *
* Known contributors to this file: * Known contributors to this file:
* Markus Armbruster, 2004-2007 * Markus Armbruster, 2004-2008
*/ */
/*
* Dump everything under the sun
*
* Static game data (configuration):
* - Item characteristics: ichr[]
* - Land unit characteristics: lchr[]
* - Nuke characteristics: nchr[]
* - Plane characteristics: plchr[]
* - Product characteristics: pchr[]
* - Sector designation characteristics: dchr[]
* - Sector infrastructure characteristics: intrchr[]
* - Ship characteristics: mchr[]
* - News item characteristics: rpt[]
* - News page headings: page_headings[]
* - Commands: player_coms[] (TODO)
* - Update schedule: update_time[] (not really static)
* - Configuration: configkeys[]
*
* Dynamic game data:
* - Sectors: EF_SECTOR (superseding dump)
* - Land units: EF_LAND (superseding ldump)
* - Lost: EF_LOST (superseding lost)
* - Nukes: EF_NUKE (superseding ndump)
* - Planes: EF_PLANE (superseding pdump)
* - Ships: EF_SHIP (superseding sdump)
* - News: EF_NEWS
* - Treaties: EF_TREATY
* - Power: EF_POWER (TODO)
* - Nations: EF_NATION
* - Loans: EF_LOAN
* - Map: EF_MAP (TODO)
* - Bmap: EF_BMAP (TODO)
* - Market: EF_COMM
*/
/*
* See doc/xdump for motivation, syntax, semantics, and rationale.
* Make sure to keep it up-to-date!
*/
/* TODO don't dump stuff that's useless due to options */
#include <config.h> #include <config.h>
#include <ctype.h> #include <ctype.h>
#include <stddef.h>
#include "commands.h" #include "commands.h"
#include "empobj.h" #include "empobj.h"
#include "match.h"
#include "news.h"
#include "optlist.h" #include "optlist.h"
#include "treaty.h"
#include "version.h" #include "version.h"
#include "xdump.h"
/*
* Evaluate a attribute of an object into VAL, return VAL.
* TYPE is the attribute's type.
* PTR points to the context object.
* The attribute is stored there at offset OFF + IDX * S, where S is
* its size.
* LEN is the #array elements if it is an array, else zero.
*/
static struct valstr *
xdeval(struct valstr *val,
nsc_type type, void *ptr, ptrdiff_t off, int idx, int len)
{
val->val_type = type;
val->val_cat = NSC_OFF;
val->val_as.sym.off = off;
val->val_as.sym.len = len;
val->val_as.sym.idx = idx;
nstr_exec_val(val, player->cnum, ptr, NSC_NOTYPE);
return val; /* FIXME nstr_exec_val() should return VAL */
}
/* Dump VAL prefixed with SEP, return " ". */
static char *
xdprval(struct valstr *val, char *sep)
{
unsigned char *s, *e, *l;
switch (val->val_type) {
case NSC_LONG:
pr("%s%ld", sep, val->val_as.lng);
break;
case NSC_DOUBLE:
pr("%s%#g", sep, val->val_as.dbl);
break;
case NSC_STRING:
s = (unsigned char *)val->val_as.str.base;
if (s) {
pr("%s\"", sep);
l = s + val->val_as.str.maxsz;
/* FIXME maxsz == INT_MAX ! */
for (;;) {
for (e = s;
e < l && *e != '"' && *e != '\\' && isgraph(*e);
++e)
;
pr("%.*s", (int)(e-s), s);
if (e < l && *e)
pr("\\%03o", *e++);
else
break;
s = e;
}
pr("\"");
} else
pr("%snil", sep);
break;
default:
CANT_REACH();
pr("0");
}
return " ";
}
/*
* Dump field values of a context object.
* CA[] describes fields.
* PTR points to context object.
*/
static void
xdflds(struct castr ca[], void *ptr)
{
int i, j, n;
struct valstr val;
char *sep = "";
for (i = 0; ca[i].ca_name; ++i) {
if (ca[i].ca_flags & NSC_DEITY && !player->god)
continue;
if (ca[i].ca_flags & NSC_EXTRA)
continue;
n = ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0;
j = 0;
do {
xdeval(&val, ca[i].ca_type, ptr, ca[i].ca_off, j, ca[i].ca_len);
sep = xdprval(&val, sep);
} while (++j < n);
}
}
/*
* Dump header for dump NAME.
* If META, it's for the meta-data dump rather than the data dump.
*/
static void
xdhdr(char *name, int meta)
{
pr("XDUMP %s%s %ld\n", meta ? "meta " : "", name, (long)time(NULL));
}
/* Dump footer for a dump that dumped N objects. */
static void
xdftr(int n)
{
pr("/%d\n", n);
}
/* /*
* Is object P of type TYPE visible to the player? * Is object P of type TYPE visible to the player?
@ -265,11 +114,11 @@ xdvisible(int type, void *p)
} }
/* /*
* Dump items of type TYPE selected by ARG. * Dump items of type TYPE selected by ARG to XD.
* Return RET_OK on success, RET_SYN on error. * Return RET_OK on success, RET_SYN on error.
*/ */
static int static int
xditem(int type, char *arg) xditem(struct xdstr *xd, int type, char *arg)
{ {
struct castr *ca; struct castr *ca;
struct nstr_item ni; struct nstr_item ni;
@ -283,59 +132,29 @@ xditem(int type, char *arg)
if (!snxtitem(&ni, type, arg)) if (!snxtitem(&ni, type, arg))
return RET_SYN; return RET_SYN;
xdhdr(ef_nameof(type), 0); xdhdr(xd, ef_nameof(type), 0);
n = 0; n = 0;
while (nxtitem(&ni, buf)) { while (nxtitem(&ni, buf)) {
if (!xdvisible(type, buf)) if (!xdvisible(type, buf))
continue; continue;
++n; ++n;
xdflds(ca, buf); xdflds(xd, ca, buf);
pr("\n"); xd->pr("\n");
} }
xdftr(n); xdftr(xd, n);
return RET_OK; return RET_OK;
} }
/* /*
* Dump meta-data for items of type TYPE. * Dump configkeys[] to XD.
* If META, dump meta-data rather than data.
* Return RET_OK. * Return RET_OK.
*/ */
static int static int
xdmeta(int type) xdver(struct xdstr *xd, int meta)
{
struct castr *ca = ef_cadef(type);
int i;
int n = 0;
if (!ca)
return RET_SYN;
xdhdr(ef_nameof(type), 1);
for (i = 0; ca[i].ca_name; i++) {
if (ca[i].ca_flags & NSC_DEITY && !player->god)
continue;
if (ca[i].ca_flags & NSC_EXTRA)
continue;
xdflds(mdchr_ca, &ca[i]);
pr("\n");
n++;
}
xdftr(n);
return RET_OK;
}
/*
* Dump configkeys[], return RET_OK.
* If META, dump meta-data rather than data.
*/
static int
xdver(int meta)
{ {
static struct castr vers_ca = { static struct castr vers_ca = {
NSC_STRINGY, 0, sizeof(PACKAGE_STRING), 0, "version", EF_BAD NSC_STRINGY, 0, sizeof(PACKAGE_STRING), 0, "version", EF_BAD
@ -346,11 +165,11 @@ xdver(int meta)
struct castr ca; struct castr ca;
struct valstr val; struct valstr val;
xdhdr("version", meta); xdhdr(xd, "version", meta);
if (meta) { if (meta) {
n = 0; n = 0;
xdflds(mdchr_ca, &vers_ca); xdflds(xd, mdchr_ca, &vers_ca);
pr("\n"); pr("\n");
n++; n++;
for (kp = configkeys; kp->km_key; ++kp) { for (kp = configkeys; kp->km_key; ++kp) {
@ -361,39 +180,40 @@ xdver(int meta)
ca.ca_off = 0; ca.ca_off = 0;
ca.ca_name = kp->km_key; ca.ca_name = kp->km_key;
ca.ca_table = EF_BAD; ca.ca_table = EF_BAD;
xdflds(mdchr_ca, &ca); xdflds(xd, mdchr_ca, &ca);
pr("\n"); pr("\n");
n++; n++;
} }
} }
xdftr(n); xdftr(xd, n);
return RET_OK; return RET_OK;
} }
xdeval(&val, vers_ca.ca_type, version, vers_ca.ca_off, 0, vers_ca.ca_len); xdeval(&val, xd,
sep = xdprval(&val, ""); vers_ca.ca_type, version, vers_ca.ca_off, 0, vers_ca.ca_len);
sep = xdprval(xd, &val, "");
for (kp = configkeys; kp->km_key; ++kp) { for (kp = configkeys; kp->km_key; ++kp) {
if (kp->km_type != NSC_NOTYPE && !(kp->km_flags & KM_INTERNAL)) { if (kp->km_type != NSC_NOTYPE && !(kp->km_flags & KM_INTERNAL)) {
xdeval(&val, kp->km_type, kp->km_data, 0, 0, 0); xdeval(&val, xd, kp->km_type, kp->km_data, 0, 0, 0);
sep = xdprval(&val, sep); sep = xdprval(xd, &val, sep);
} }
} }
pr("\n"); pr("\n");
xdftr(1); xdftr(xd, 1);
return RET_OK; return RET_OK;
} }
/* Experimental extended dump command */
int int
xdump(void) xdump(void)
{ {
char *p; char *p;
char buf[1024]; char buf[1024];
struct xdstr xd;
struct natstr *natp;
int type; int type;
int meta = 0; int meta = 0;
struct natstr *natp;
p = getstarg(player->argp[1], "Table name, or meta? ", buf); p = getstarg(player->argp[1], "Table name, or meta? ", buf);
if (p && strcmp(p, "meta") == 0) { if (p && strcmp(p, "meta") == 0) {
@ -403,19 +223,20 @@ xdump(void)
if (!p || !*p) if (!p || !*p)
return RET_SYN; return RET_SYN;
xdinit(&xd, player->cnum, 0, pr);
natp = getnatp(player->cnum); natp = getnatp(player->cnum);
type = isdigit(p[0]) ? atoi(p) : ef_byname(p); type = isdigit(p[0]) ? atoi(p) : ef_byname(p);
if (type >= 0 && type < EF_MAX) { if (type >= 0 && type < EF_MAX) {
if (meta) if (meta)
return xdmeta(type); return xdmeta(&xd, type);
else if ((EF_IS_GAME_STATE(type) || EF_IS_VIEW(type)) else if ((EF_IS_GAME_STATE(type) || EF_IS_VIEW(type))
&& !(natp->nat_stat == STAT_ACTIVE || player->god)) { && !(natp->nat_stat == STAT_ACTIVE || player->god)) {
pr("Access to table %s denied\n", ef_nameof(type)); pr("Access to table %s denied\n", ef_nameof(type));
return RET_FAIL; return RET_FAIL;
} else } else
return xditem(type, player->argp[2]); return xditem(&xd, type, player->argp[2]);
} else if (!strncmp(p, "ver", strlen(p))) { } else if (!strncmp(p, "ver", strlen(p))) {
return xdver(meta); return xdver(&xd, meta);
} }
return RET_SYN; return RET_SYN;

View file

@ -39,6 +39,7 @@
#include "optlist.h" #include "optlist.h"
#include "product.h" #include "product.h"
#include "prototypes.h" #include "prototypes.h"
#include "xdump.h"
static int read_custom_table_file(char *); static int read_custom_table_file(char *);

View file

@ -46,8 +46,10 @@
#include "nsc.h" #include "nsc.h"
#include "prototypes.h" #include "prototypes.h"
static int ef_remap_cache(struct empfile *, int);
static int fillcache(struct empfile *, int); static int fillcache(struct empfile *, int);
static int do_write(struct empfile *, void *, int, int); static int do_write(struct empfile *, void *, int, int);
static void ef_blank(struct empfile *, void *, int, int);
/* /*
* Open the file-backed table TYPE (EF_SECTOR, ...). * Open the file-backed table TYPE (EF_SECTOR, ...).
@ -61,7 +63,7 @@ ef_open(int type, int how)
{ {
struct empfile *ep; struct empfile *ep;
struct flock lock; struct flock lock;
int oflags, fd, fsiz, size; int oflags, fd, fsiz, nslots;
if (ef_check(type) < 0) if (ef_check(type) < 0)
return 0; return 0;
@ -73,7 +75,7 @@ ef_open(int type, int how)
if (CANT_HAPPEN(ep->fd >= 0)) if (CANT_HAPPEN(ep->fd >= 0))
return 0; return 0;
oflags = O_RDWR; oflags = O_RDWR;
if (how & EFF_RDONLY) if (how & EFF_PRIVATE)
oflags = O_RDONLY; oflags = O_RDONLY;
if (how & EFF_CREATE) if (how & EFF_CREATE)
oflags |= O_CREAT | O_TRUNC; oflags |= O_CREAT | O_TRUNC;
@ -85,7 +87,7 @@ ef_open(int type, int how)
return 0; return 0;
} }
lock.l_type = how & EFF_RDONLY ? F_RDLCK : F_WRLCK; lock.l_type = how & EFF_PRIVATE ? F_RDLCK : F_WRLCK;
lock.l_whence = SEEK_SET; lock.l_whence = SEEK_SET;
lock.l_start = lock.l_len = 0; lock.l_start = lock.l_len = 0;
if (fcntl(fd, F_SETLK, &lock) == -1) { if (fcntl(fd, F_SETLK, &lock) == -1) {
@ -116,18 +118,13 @@ ef_open(int type, int how)
} }
} }
} else { } else {
if (how & EFF_MEM)
ep->csize = ep->fids;
else
ep->csize = blksize(fd) / ep->size;
/* 0 could lead to null cache, which confuses assertions */
if (!ep->csize)
ep->csize++;
size = ep->csize * ep->size;
if (CANT_HAPPEN(ep->cache)) if (CANT_HAPPEN(ep->cache))
free(ep->cache); free(ep->cache);
ep->cache = malloc(size); if (how & EFF_MEM)
if (ep->cache == NULL) { nslots = ep->fids;
else
nslots = blksize(fd) / ep->size;
if (!ef_remap_cache(ep, nslots)) {
logerror("Can't open %s: out of memory", ep->file); logerror("Can't open %s: out of memory", ep->file);
close(fd); close(fd);
return 0; return 0;
@ -148,11 +145,31 @@ ef_open(int type, int how)
} }
} }
/* return 1;
* Could close fd if both EFF_RDONLY and EFF_MEM, but that doesn't }
* happen, so don't bother.
*/
static int
ef_remap_cache(struct empfile *ep, int nslots)
{
void *cache;
if (CANT_HAPPEN(ep->flags & EFF_STATIC))
return 0;
if (CANT_HAPPEN(nslots < 0))
nslots = 0;
/*
* Avoid zero slots, because that can lead to null cache, which
* would be interpreted as unmapped cache.
*/
if (nslots == 0)
nslots++;
cache = realloc(ep->cache, nslots * ep->size);
if (!cache)
return 0;
ep->cache = cache;
ep->csize = nslots;
return 1; return 1;
} }
@ -182,7 +199,8 @@ ef_close(int type)
} }
/* /*
* Flush file-backed table TYPE (EF_SECTOR, ...) to disk. * Flush table TYPE (EF_SECTOR, ...) to disk.
* Does nothing if the table is privately mapped.
* Return non-zero on success, zero on failure. * Return non-zero on success, zero on failure.
*/ */
int int
@ -193,6 +211,8 @@ ef_flush(int type)
if (ef_check(type) < 0) if (ef_check(type) < 0)
return 0; return 0;
ep = &empfile[type]; ep = &empfile[type];
if (ep->flags & EFF_PRIVATE)
return 1; /* nothing to do */
if (CANT_HAPPEN(ep->fd < 0)) if (CANT_HAPPEN(ep->fd < 0))
return 0; return 0;
/* /*
@ -201,7 +221,7 @@ ef_flush(int type)
* allowed only with EFF_MEM. Assume the whole cash is dirty * allowed only with EFF_MEM. Assume the whole cash is dirty
* then. * then.
*/ */
if (!(ep->flags & EFF_RDONLY) && (ep->flags & EFF_MEM)) if (ep->flags & EFF_MEM)
return do_write(ep, ep->cache, ep->baseid, ep->cids) >= 0; return do_write(ep, ep->cache, ep->baseid, ep->cids) >= 0;
return 1; return 1;
@ -316,7 +336,8 @@ do_write(struct empfile *ep, void *buf, int id, int count)
int n, ret; int n, ret;
char *p; char *p;
if (CANT_HAPPEN(ep->fd < 0 || id < 0 || count < 0)) if (CANT_HAPPEN(ep->fd < 0 || (ep->flags & EFF_PRIVATE)
|| id < 0 || count < 0))
return -1; return -1;
if (lseek(ep->fd, id * ep->size, SEEK_SET) == (off_t)-1) { if (lseek(ep->fd, id * ep->size, SEEK_SET) == (off_t)-1) {
@ -344,9 +365,10 @@ do_write(struct empfile *ep, void *buf, int id, int count)
} }
/* /*
* Write element ID into file-backed table TYPE from buffer FROM. * Write element ID into table TYPE from buffer FROM.
* FIXME pass buffer size! * FIXME pass buffer size!
* Write through cache straight to disk. * If table is file-backed and not privately mapped, write through
* cache straight to disk.
* Cannot write beyond the end of fully cached table (flags & EFF_MEM). * Cannot write beyond the end of fully cached table (flags & EFF_MEM).
* Can write at the end of partially cached table. * Can write at the end of partially cached table.
* Return non-zero on success, zero on failure. * Return non-zero on success, zero on failure.
@ -360,12 +382,16 @@ ef_write(int type, int id, void *from)
if (ef_check(type) < 0) if (ef_check(type) < 0)
return 0; return 0;
ep = &empfile[type]; ep = &empfile[type];
if (CANT_HAPPEN((ep->flags & (EFF_MEM | EFF_PRIVATE)) == EFF_PRIVATE))
return 0;
if (ep->prewrite) if (ep->prewrite)
ep->prewrite(id, from); ep->prewrite(id, from);
if (CANT_HAPPEN((ep->flags & EFF_MEM) ? id >= ep->fids : id > ep->fids)) if (CANT_HAPPEN((ep->flags & EFF_MEM) ? id >= ep->fids : id > ep->fids))
return 0; /* not implemented */ return 0; /* not implemented */
if (!(ep->flags & EFF_PRIVATE)) {
if (do_write(ep, from, id, 1) < 0) if (do_write(ep, from, id, 1) < 0)
return 0; return 0;
}
if (id >= ep->baseid && id < ep->baseid + ep->cids) { if (id >= ep->baseid && id < ep->baseid + ep->cids) {
/* update the cache if necessary */ /* update the cache if necessary */
to = ep->cache + (id - ep->baseid) * ep->size; to = ep->cache + (id - ep->baseid) * ep->size;
@ -380,44 +406,66 @@ ef_write(int type, int id, void *from)
} }
/* /*
* Extend the file-backed table TYPE by COUNT elements. * Extend table TYPE by COUNT elements.
* Any pointers obtained from ef_ptr() become invalid.
* Return non-zero on success, zero on failure. * Return non-zero on success, zero on failure.
*/ */
int int
ef_extend(int type, int count) ef_extend(int type, int count)
{ {
struct empfile *ep; struct empfile *ep;
void *tmpobj; char *p;
int id, i, how; int i, id;
if (ef_check(type) < 0) if (ef_check(type) < 0)
return 0; return 0;
ep = &empfile[type]; ep = &empfile[type];
if (CANT_HAPPEN(ep->fd < 0 || count < 0)) if (CANT_HAPPEN(count < 0))
return 0; return 0;
tmpobj = calloc(1, ep->size);
id = ep->fids; id = ep->fids;
for (i = 0; i < count; i++) {
if (ep->init)
ep->init(id + i, tmpobj);
if (do_write(ep, tmpobj, id + i, 1) < 0)
break;
}
free(tmpobj);
if (ep->flags & EFF_MEM) { if (ep->flags & EFF_MEM) {
/* FIXME lazy bastards... do this right */ if (id + count > ep->csize) {
/* XXX this will cause problems if there are ef_ptrs (to the if (ep->flags & EFF_STATIC)
* old allocated structure) active when we do the re-open */ return 0;
how = ep->flags & ~EFF_IMMUTABLE; if (!ef_remap_cache(ep, id + count))
ef_close(type); return 0;
ef_open(type, how);
} else {
ep->fids += i;
} }
p = ep->cache + id * ep->size;
ef_blank(ep, p, id, count);
if (!(ep->flags & EFF_PRIVATE)) {
if (do_write(ep, p, id, count) < 0)
return 0;
}
ep->cids += count;
} else {
/* need a buffer, steal last cache slot */
if (ep->cids == ep->csize)
ep->cids--;
p = ep->cache + ep->cids * ep->size;
for (i = 0; i < count; i++) {
ef_blank(ep, p, id + i, 1);
if (do_write(ep, p, id + i, 1) < 0)
return 0;
}
}
ep->fids += count;
return 1;
}
return i == count; /*
* Initialize COUNT elements of EP in BUF, starting with element ID.
*/
static void
ef_blank(struct empfile *ep, void *buf, int id, int count)
{
int i;
memset(buf, 0, count * ep->size);
if (ep->init) {
for (i = 0; i < count; i++)
ep->init(id + i, (char *)buf + i * ep->size);
}
} }
struct castr * struct castr *

351
src/lib/common/xdump.c Normal file
View file

@ -0,0 +1,351 @@
/*
* Empire - A multi-player, client/server Internet based war game.
* Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---
*
* See files README, COPYING and CREDITS in the root of the source
* tree for related information and legal notices. It is expected
* that future projects/authors will amend these files as needed.
*
* ---
*
* xdump.c: Extended dumps
*
* Known contributors to this file:
* Markus Armbruster, 2008
*/
/*
* Dump everything under the sun
*
* Static game data (configuration):
* - Item characteristics: ichr[]
* - Land unit characteristics: lchr[]
* - Nuke characteristics: nchr[]
* - Plane characteristics: plchr[]
* - Product characteristics: pchr[]
* - Sector designation characteristics: dchr[]
* - Sector infrastructure characteristics: intrchr[]
* - Ship characteristics: mchr[]
* - News item characteristics: rpt[]
* - News page headings: page_headings[]
* - Commands: player_coms[] (TODO)
* - Update schedule: update_time[] (not really static)
* - Configuration: configkeys[]
*
* Dynamic game data:
* - Sectors: EF_SECTOR (superseding dump)
* - Land units: EF_LAND (superseding ldump)
* - Lost: EF_LOST (superseding lost)
* - Nukes: EF_NUKE (superseding ndump)
* - Planes: EF_PLANE (superseding pdump)
* - Ships: EF_SHIP (superseding sdump)
* - News: EF_NEWS
* - Treaties: EF_TREATY
* - Power: EF_POWER (TODO)
* - Nations: EF_NATION
* - Loans: EF_LOAN
* - Map: EF_MAP (TODO)
* - Bmap: EF_BMAP (TODO)
* - Market: EF_COMM
*/
/*
* See doc/xdump for motivation, syntax, semantics, and rationale.
* Make sure to keep it up-to-date!
*/
/* TODO don't dump stuff that's useless due to options */
#include <config.h>
#include <ctype.h>
#include <limits.h>
#include "file.h"
#include "nat.h"
#include "xdump.h"
/*
* Initialize XD to dump for country CNUM.
* If HUMAN, dump in human-readable format.
* Dump is to be delivered through callback PR.
* Return XD.
*/
struct xdstr *
xdinit(struct xdstr *xd, natid cnum, int human, void (*pr)(char *fmt, ...))
{
xd->cnum = cnum;
xd->divine = getnatp(cnum)->nat_stat == STAT_GOD;
xd->human = human;
xd->pr = pr;
return xd;
}
/*
* Evaluate a attribute of an object into VAL, return VAL.
* XD is the xdump descriptor.
* TYPE is the attribute's type.
* PTR points to the context object.
* The attribute is stored there at offset OFF + IDX * S, where S is
* its size.
* LEN is the #array elements if it is an array, else zero.
*/
struct valstr *
xdeval(struct valstr *val, struct xdstr *xd,
nsc_type type, void *ptr, ptrdiff_t off, int idx, int len)
{
val->val_type = type;
val->val_cat = NSC_OFF;
val->val_as.sym.off = off;
val->val_as.sym.len = len;
val->val_as.sym.idx = idx;
nstr_exec_val(val, xd->cnum, ptr, NSC_NOTYPE);
return val; /* FIXME nstr_exec_val() should return VAL */
}
/*
* Dump string STR to XD with funny characters escaped.
* Dump at most N characters.
*/
static void
xdpresc(struct xdstr *xd, char *str, size_t n)
{
unsigned char *s, *e, *l;
s = (unsigned char *)str;
l = s + n;
for (;;) {
for (e = s;
e < l && *e != '"' && *e != '\\' && isgraph(*e);
++e)
;
xd->pr("%.*s", (int)(e-s), s);
if (e < l && *e)
xd->pr("\\%03o", *e++);
else
break;
s = e;
}
}
/* Dump VAL in machine readable format, prefixed with SEP, return " ". */
static char *
xdprval_nosym(struct xdstr *xd, struct valstr *val, char *sep)
{
switch (val->val_type) {
case NSC_LONG:
xd->pr("%s%ld", sep, val->val_as.lng);
break;
case NSC_DOUBLE:
#if 1 /* TODO have %a */
xd->pr("%s%a", sep, val->val_as.dbl);
#else
xd->pr("%s%#g", sep, val->val_as.dbl);
#endif
break;
case NSC_STRING:
if (val->val_as.str.base) {
xd->pr("%s\"", sep);
xdpresc(xd, val->val_as.str.base, val->val_as.str.maxsz);
xd->pr("\"");
} else
xd->pr("%snil", sep);
break;
default:
CANT_REACH();
xd->pr("0");
}
return " ";
}
/*
* Dump symbol with value KEY from symbol table TYPE to XD.
* Prefix with SEP, return " ".
*/
static char *
xdprsym(struct xdstr *xd, int key, int type, char *sep)
{
char *sym = symbol_by_value(key, ef_ptr(type, 0));
if (CANT_HAPPEN(!sym))
xd->pr("%s%ld", sep, key);
else {
xd->pr("%s", sep);
xdpresc(xd, sym, INT_MAX);
}
return " ";
}
/*
* Dump VAL prefixed with SEP, return " ".
* CA describes the field from which the value was fetched.
*/
static char *
xdprval_sym(struct xdstr *xd, struct valstr *val, struct castr *ca, char *sep)
{
unsigned long bit;
struct castr *ca_sym;
char *sym;
if (xd->human && val->val_type == NSC_LONG && ca->ca_table != EF_BAD) {
ca_sym = ef_cadef(ca->ca_table);
if (ca_sym != symbol_ca)
;
else if (ca->ca_flags & NSC_BITS) {
xd->pr("%s(", sep);
sep = "";
for (bit = 1; bit; bit <<= 1) {
if (bit & val->val_as.lng)
sep = xdprsym(xd, bit, ca->ca_table, sep);
}
xd->pr(")");
return " ";
} else
return xdprsym(xd, val->val_as.lng, ca->ca_table, sep);
}
return xdprval_nosym(xd, val, sep);
}
/*
* Dump VAL prefixed with SEP, return " ".
* XD must not be human-readable.
*/
char *
xdprval(struct xdstr *xd, struct valstr *val, char *sep)
{
CANT_HAPPEN(xd->human);
return xdprval_nosym(xd, val, sep);
}
/*
* Dump field values of a context object to XD.
* CA[] describes fields.
* PTR points to context object.
*/
void
xdflds(struct xdstr *xd, struct castr ca[], void *ptr)
{
int i, j, n;
struct valstr val;
char *sep = "";
for (i = 0; ca[i].ca_name; ++i) {
if (ca[i].ca_flags & NSC_DEITY && !xd->divine)
continue;
if (ca[i].ca_flags & NSC_EXTRA)
continue;
n = ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0;
j = 0;
do {
xdeval(&val, xd,
ca[i].ca_type, ptr, ca[i].ca_off, j, ca[i].ca_len);
sep = xdprval_sym(xd, &val, &ca[i], sep);
} while (++j < n);
}
}
/*
* Dump header for dump NAME to XD.
* If META, it's for the meta-data dump rather than the data dump.
*/
void
xdhdr(struct xdstr *xd, char *name, int meta)
{
if (xd->human) {
xd->pr("config %s\n", name);
} else
xd->pr("XDUMP %s%s %ld\n",
meta ? "meta " : "", name, (long)time(NULL));
}
/*
* Dump column header to XD.
* CA[] describes fields.
* Does nothing unless XD is human-readable.
*/
void
xdcolhdr(struct xdstr *xd, struct castr ca[])
{
int i, j, n;
char *sep = "";
if (!xd->human)
return;
for (i = 0; ca[i].ca_name; ++i) {
if (ca[i].ca_flags & NSC_DEITY && !xd->divine)
continue;
if (ca[i].ca_flags & NSC_EXTRA)
continue;
n = ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0;
if (n) {
for (j = 0; j < n; j++) {
xd->pr("%s%s(%d)",sep, ca[i].ca_name, j);
sep = " ";
}
} else {
xd->pr("%s%s", sep, ca[i].ca_name);
sep = " ";
}
}
xd->pr("\n");
}
/* Dump footer for a dump that dumped N objects to XD. */
void
xdftr(struct xdstr *xd, int n)
{
if (xd->human)
xd->pr("/config\n");
else
xd->pr("/%d\n", n);
}
/*
* Dump meta-data for items of type TYPE to XD.
* Return RET_OK.
*/
int
xdmeta(struct xdstr *xd, int type)
{
struct castr *ca = ef_cadef(type);
int i;
int n = 0;
if (!ca)
return RET_SYN;
xdhdr(xd, ef_nameof(type), 1);
xdcolhdr(xd, ca);
for (i = 0; ca[i].ca_name; i++) {
if (ca[i].ca_flags & NSC_DEITY && !xd->divine)
continue;
if (ca[i].ca_flags & NSC_EXTRA)
continue;
xdflds(xd, mdchr_ca, &ca[i]);
xd->pr("\n");
n++;
}
xdftr(xd, n);
return RET_OK;
}

View file

@ -56,12 +56,14 @@
#include <ctype.h> #include <ctype.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h> #include <time.h>
#include "file.h" #include "file.h"
#include "match.h" #include "match.h"
#include "nsc.h" #include "nsc.h"
#include "optlist.h" #include "optlist.h"
#include "prototypes.h" #include "xdump.h"
static char *fname; /* Name of file being read */ static char *fname; /* Name of file being read */
static int lineno; /* Current line number */ static int lineno; /* Current line number */
@ -130,21 +132,6 @@ skipfs(FILE *fp)
return ch; return ch;
} }
/*
* Read an identifier from FP into BUF.
* BUF must have space for 1024 characters.
* Return number of characters read on success, -1 on failure.
*/
static int
getid(FILE *fp, char *buf)
{
int n;
if (fscanf(fp, "%1023[^#()<>=#\" \t\n]%n", buf, &n) != 1
|| !isalpha(buf[0]))
return -1;
return n;
}
/* /*
* Decode escape sequences in BUF. * Decode escape sequences in BUF.
* Return BUF on success, null pointer on failure. * Return BUF on success, null pointer on failure.
@ -170,6 +157,22 @@ xuesc(char *buf)
return buf; return buf;
} }
/*
* Read an identifier from FP into BUF.
* BUF must have space for 1024 characters.
* Return number of characters read on success, -1 on failure.
*/
static int
getid(FILE *fp, char *buf)
{
int n;
if (fscanf(fp, "%1023[^\"#()<>= \t\n]%n", buf, &n) != 1
|| !isalpha(buf[0]))
return -1;
xuesc(buf);
return n;
}
/* /*
* Try to read a field name from FP. * Try to read a field name from FP.
* I is the field number, counting from zero. * I is the field number, counting from zero.
@ -479,17 +482,20 @@ getobj(void)
int need_sentinel = !EF_IS_GAME_STATE(cur_type); int need_sentinel = !EF_IS_GAME_STATE(cur_type);
if (!cur_obj) { if (!cur_obj) {
cur_obj_is_blank = cur_id >= ep->fids; cur_obj_is_blank = cur_id >= ep->fids - !!need_sentinel;
if (cur_obj_is_blank) { if (cur_obj_is_blank) {
/* TODO grow cache (and possibly file) unless EFF_STATIC */ if (ef_ensure_space(cur_type, cur_id + !!need_sentinel, 1))
if (cur_id < ep->csize - !!need_sentinel)
ep->cids = ep->fids = cur_id + 1;
/* else: ef_ptr() will fail */
}
cur_obj = ef_ptr(cur_type, cur_id); cur_obj = ef_ptr(cur_type, cur_id);
/* FIXME diagnose out of dynamic memory vs. static table full */
if (!cur_obj) if (!cur_obj)
gripe("Can't put ID %d into table %s, it holds only 0..%d.", gripe("Can't put ID %d into table %s, it holds only 0..%d.",
cur_id, ep->name, ep->fids - 1); cur_id, ep->name, ep->fids - !!need_sentinel - 1);
} else
cur_obj = ef_ptr(cur_type, cur_id);
/* TODO find a less ugly solution */
if (EF_IS_GAME_STATE(cur_type) && cur_type != EF_POWER &&
cur_type != EF_MAP && cur_type != EF_BMAP)
*((short *)cur_obj) = cur_type;
} }
return cur_obj; return cur_obj;
@ -958,8 +964,12 @@ xubody(FILE *fp)
{ {
struct empfile *ep = &empfile[cur_type]; struct empfile *ep = &empfile[cur_type];
int need_sentinel = !EF_IS_GAME_STATE(cur_type); int need_sentinel = !EF_IS_GAME_STATE(cur_type);
int old_maxid = ep->fids;
int i, maxid, ch; int i, maxid, ch;
if (old_maxid == 0 && need_sentinel)
ef_ensure_space(cur_type, 0, 1);
maxid = 0; maxid = 0;
for (i = 0;; ++i) { for (i = 0;; ++i) {
while ((ch = skipfs(fp)) == '\n') while ((ch = skipfs(fp)) == '\n')
@ -974,6 +984,12 @@ xubody(FILE *fp)
maxid = MAX(maxid, cur_id + 1); maxid = MAX(maxid, cur_id + 1);
} }
if (maxid >= old_maxid && need_sentinel) {
/* appended a sentinel, strip it off */
ep->fids--;
ep->cids--;
}
if (CANT_HAPPEN(maxid > ep->fids)) if (CANT_HAPPEN(maxid > ep->fids))
maxid = ep->fids; maxid = ep->fids;
if (maxid < ep->fids) { if (maxid < ep->fids) {
@ -988,11 +1004,5 @@ xubody(FILE *fp)
ef_nameof(cur_type), ep->fids, maxid); ef_nameof(cur_type), ep->fids, maxid);
} }
if (need_sentinel) {
if (CANT_HAPPEN(maxid >= ep->csize))
return gripe("No space for sentinel");
memset(ep->cache + ep->size * maxid, 0, ep->size);
}
return i; return i;
} }

View file

@ -85,7 +85,7 @@
SZ((array)), 0, SZ((array)) - 1, SZ((array)) - 1, -1, NULL, NULL, NULL SZ((array)), 0, SZ((array)) - 1, SZ((array)) - 1, -1, NULL, NULL, NULL
/* Common configuration table flags */ /* Common configuration table flags */
#define EFF_CFG (EFF_RDONLY | EFF_MEM | EFF_STATIC) #define EFF_CFG (EFF_PRIVATE | EFF_MEM | EFF_STATIC)
struct empfile empfile[] = { struct empfile empfile[] = {
/* /*
@ -95,7 +95,7 @@ struct empfile empfile[] = {
* bits of flags get their final value. * bits of flags get their final value.
* If flags & EFF_STATIC, the cache is mapped here, and members * If flags & EFF_STATIC, the cache is mapped here, and members
* cache, csize get their final value. * cache, csize get their final value.
* Members baseid, cids, fids and the EFF_MEM|EFF_RDONLY bits of * Members baseid, cids, fids and the EFF_MEM|EFF_PRIVATE bits of
* flags are initialized according the initial cache contents. * flags are initialized according the initial cache contents.
* Member fd is initialized to -1. * Member fd is initialized to -1.
* Members init, postread, prewrite get initialized to NULL, but * Members init, postread, prewrite get initialized to NULL, but
@ -131,7 +131,7 @@ struct empfile empfile[] = {
{EF_TREATY, "treaty", "treaty", treaty_ca, {EF_TREATY, "treaty", "treaty", treaty_ca,
UNMAPPED_CACHE(struct trtstr, 0)}, UNMAPPED_CACHE(struct trtstr, 0)},
{EF_TRADE, "trade", "trade", trade_ca, {EF_TRADE, "trade", "trade", trade_ca,
UNMAPPED_CACHE(struct trdstr, 0)}, UNMAPPED_CACHE(struct trdstr, EFF_OWNER)},
{EF_POWER, "pow", "power", NULL, {EF_POWER, "pow", "power", NULL,
UNMAPPED_CACHE(struct powstr, 0)}, UNMAPPED_CACHE(struct powstr, 0)},
{EF_NATION, "nat", "nation", nat_ca, {EF_NATION, "nat", "nation", nat_ca,
@ -143,7 +143,7 @@ struct empfile empfile[] = {
{EF_BMAP, "bmap", "bmap", NULL, {EF_BMAP, "bmap", "bmap", NULL,
0, 0, NULL, 0, 0, 0, 0, -1, NULL, NULL, NULL}, 0, 0, NULL, 0, 0, 0, 0, -1, NULL, NULL, NULL},
{EF_COMM, "commodity", "commodity", commodity_ca, {EF_COMM, "commodity", "commodity", commodity_ca,
UNMAPPED_CACHE(struct comstr, 0)}, UNMAPPED_CACHE(struct comstr, EFF_OWNER)},
{EF_LOST, "lost", "lostitems", lost_ca, {EF_LOST, "lost", "lostitems", lost_ca,
UNMAPPED_CACHE(struct loststr, EFF_OWNER)}, UNMAPPED_CACHE(struct loststr, EFF_OWNER)},
{EF_REALM, "realm", "realms", realm_ca, {EF_REALM, "realm", "realms", realm_ca,
@ -214,7 +214,8 @@ struct empfile empfile[] = {
SYMTAB(EF_TREATY_FLAGS, "treaty-flags", treaty_flags), SYMTAB(EF_TREATY_FLAGS, "treaty-flags", treaty_flags),
/* Views */ /* Views */
{EF_COUNTRY, "country", NULL, cou_ca, UNMAPPED_CACHE(struct natstr, 0)}, {EF_COUNTRY, "country", NULL, cou_ca,
UNMAPPED_CACHE(struct natstr, EFF_OWNER)},
/* Sentinel */ /* Sentinel */
{EF_BAD, NULL, NULL, NULL, 0, 0, NULL, 0, 0, 0, 0, -1, NULL, NULL, NULL}, {EF_BAD, NULL, NULL, NULL, 0, 0, NULL, 0, 0, 0, 0, -1, NULL, NULL, NULL},

View file

@ -108,7 +108,7 @@ struct castr sect_ca[] = {
{NSC_SHORT, NSC_DEITY, 0, fldoff(sctstr, sct_elev), "elev", EF_BAD}, {NSC_SHORT, NSC_DEITY, 0, fldoff(sctstr, sct_elev), "elev", EF_BAD},
{NSC_XCOORD, NSC_CONST, 0, fldoff(sctstr, sct_x), "xloc", EF_BAD}, {NSC_XCOORD, NSC_CONST, 0, fldoff(sctstr, sct_x), "xloc", EF_BAD},
{NSC_YCOORD, NSC_CONST, 0, fldoff(sctstr, sct_y), "yloc", EF_BAD}, {NSC_YCOORD, NSC_CONST, 0, fldoff(sctstr, sct_y), "yloc", EF_BAD},
{NSC_UCHAR, 0, 0, fldoff(sctstr, sct_type), "des", EF_SECTOR_CHR}, {NSC_CHAR, 0, 0, fldoff(sctstr, sct_type), "des", EF_SECTOR_CHR},
{NSC_UCHAR, 0, 0, fldoff(sctstr, sct_effic), "effic", EF_BAD}, {NSC_UCHAR, 0, 0, fldoff(sctstr, sct_effic), "effic", EF_BAD},
{NSC_SHORT, 0, 0, fldoff(sctstr, sct_mobil), "mobil", EF_BAD}, {NSC_SHORT, 0, 0, fldoff(sctstr, sct_mobil), "mobil", EF_BAD},
{NSC_UCHAR, NSC_DEITY, 0, fldoff(sctstr, sct_loyal), "loyal", EF_BAD}, {NSC_UCHAR, NSC_DEITY, 0, fldoff(sctstr, sct_loyal), "loyal", EF_BAD},
@ -123,7 +123,7 @@ struct castr sect_ca[] = {
{NSC_SHORT, 0, 0, fldoff(sctstr, sct_avail), "avail", EF_BAD}, {NSC_SHORT, 0, 0, fldoff(sctstr, sct_avail), "avail", EF_BAD},
{NSC_UCHAR, 0, 0, fldoff(sctstr, sct_work), "work", EF_BAD}, {NSC_UCHAR, 0, 0, fldoff(sctstr, sct_work), "work", EF_BAD},
{NSC_UCHAR, 0, 0, fldoff(sctstr, sct_coastal), "coastal", EF_BAD}, {NSC_UCHAR, 0, 0, fldoff(sctstr, sct_coastal), "coastal", EF_BAD},
{NSC_UCHAR, 0, 0, fldoff(sctstr, sct_newtype), "newdes", EF_SECTOR_CHR}, {NSC_CHAR, 0, 0, fldoff(sctstr, sct_newtype), "newdes", EF_SECTOR_CHR},
{NSC_UCHAR, 0, 0, fldoff(sctstr, sct_min), "min", EF_BAD}, {NSC_UCHAR, 0, 0, fldoff(sctstr, sct_min), "min", EF_BAD},
{NSC_UCHAR, 0, 0, fldoff(sctstr, sct_gmin), "gold", EF_BAD}, {NSC_UCHAR, 0, 0, fldoff(sctstr, sct_gmin), "gold", EF_BAD},
{NSC_UCHAR, 0, 0, fldoff(sctstr, sct_fertil), "fert", EF_BAD}, {NSC_UCHAR, 0, 0, fldoff(sctstr, sct_fertil), "fert", EF_BAD},
@ -542,6 +542,8 @@ struct castr realm_ca[] = {
{NSC_SHORT, 0, 0, fldoff(realmstr, r_xh), "xh", EF_BAD}, {NSC_SHORT, 0, 0, fldoff(realmstr, r_xh), "xh", EF_BAD},
{NSC_SHORT, 0, 0, fldoff(realmstr, r_yl), "yl", EF_BAD}, {NSC_SHORT, 0, 0, fldoff(realmstr, r_yl), "yl", EF_BAD},
{NSC_SHORT, 0, 0, fldoff(realmstr, r_yh), "yh", EF_BAD}, {NSC_SHORT, 0, 0, fldoff(realmstr, r_yh), "yh", EF_BAD},
{NSC_TIME, NSC_EXTRA, 0, fldoff(realmstr, r_timestamp), "timestamp",
EF_BAD},
{NSC_NOTYPE, 0, 0, 0, NULL, EF_BAD} {NSC_NOTYPE, 0, 0, 0, NULL, EF_BAD}
}; };

View file

@ -57,30 +57,6 @@ obj_nameof(struct empobj *gp)
return "The Beast #666"; return "The Beast #666";
} }
struct empobj *
get_empobjp(int type, int id)
{
if (CANT_HAPPEN(type == EF_SECTOR || type == EF_BAD))
return NULL;
return ef_ptr(type, id);
}
int
put_empobj(struct empobj *gp)
{
switch (gp->ef_type)
{
case EF_SECTOR:
return ef_write(gp->ef_type, sctoff(gp->x, gp->y), gp);
case EF_NATION:
case EF_BMAP:
case EF_MAP:
return ef_write(gp->ef_type, gp->own, gp);
default:
return ef_write(gp->ef_type, gp->uid, gp);
}
}
struct empobj_chr * struct empobj_chr *
get_empobj_chr(struct empobj *gp) get_empobj_chr(struct empobj *gp)
{ {

View file

@ -54,7 +54,8 @@
#include "xy.h" #include "xy.h"
static int bmnxtsct(struct nstr_sect *); static int bmnxtsct(struct nstr_sect *);
static char map_char(unsigned char type, natid own, int owner_or_god); static char map_char(int, natid, int);
static int unit_map(int, int, struct nstr_sect *, char *);
int int
do_map(int bmap, int unit_type, char *arg, char *map_flags_arg) do_map(int bmap, int unit_type, char *arg, char *map_flags_arg)
@ -330,7 +331,7 @@ bmnxtsct(struct nstr_sect *np)
* If OWNER_OR_GOD, the map is for the sector's owner or a deity. * If OWNER_OR_GOD, the map is for the sector's owner or a deity.
*/ */
static char static char
map_char(unsigned char type, natid own, int owner_or_god) map_char(int type, natid own, int owner_or_god)
{ {
if (CANT_HAPPEN(type > SCT_TYPE_MAX || !dchr[type].d_mnem)) if (CANT_HAPPEN(type > SCT_TYPE_MAX || !dchr[type].d_mnem))
return '?'; return '?';
@ -341,29 +342,30 @@ map_char(unsigned char type, natid own, int owner_or_god)
return '?'; return '?';
} }
int static int
unit_map(int unit_type, int uid, struct nstr_sect *nsp, char *originp) unit_map(int unit_type, int uid, struct nstr_sect *nsp, char *originp)
{ {
struct empobj *gp; union empobj_storage unit;
struct range range; struct range range;
char *name; char *name;
gp = get_empobjp(unit_type, uid); if (!get_empobj(unit_type, uid, &unit))
if (!gp || (gp->own != player->cnum && !player->god) || gp->own == 0) return RET_FAIL;
if (!player->owner || unit.gen.own == 0)
return RET_FAIL; return RET_FAIL;
if (unit_type == EF_NUKE) if (unit_type == EF_NUKE)
*originp = 'n'; *originp = 'n';
else { else {
if ((name = emp_obj_chr_name(gp)) == NULL) if ((name = emp_obj_chr_name(&unit.gen)) == NULL)
return RET_FAIL; return RET_FAIL;
*originp = *name; *originp = *name;
} }
range.lx = xnorm(gp->x - 10); range.lx = xnorm(unit.gen.x - 10);
range.hx = xnorm(gp->x + 11); range.hx = xnorm(unit.gen.x + 11);
range.ly = ynorm(gp->y - 5); range.ly = ynorm(unit.gen.y - 5);
range.hy = ynorm(gp->y + 6); range.hy = ynorm(unit.gen.y + 6);
xysize_range(&range); xysize_range(&range);
snxtsct_area(nsp, &range); snxtsct_area(nsp, &range);
return RET_OK; return RET_OK;

View file

@ -102,23 +102,24 @@ unit_put(struct emp_qelem *list, natid actor)
struct emp_qelem *qp; struct emp_qelem *qp;
struct emp_qelem *newqp; struct emp_qelem *newqp;
struct ulist *ulp; struct ulist *ulp;
struct empobj *unit;
qp = list->q_back; qp = list->q_back;
while (qp != list) { while (qp != list) {
ulp = (struct ulist *)qp; ulp = (struct ulist *)qp;
unit = &ulp->unit.gen;
if (actor) { if (actor) {
mpr(actor, "%s stopped at %s\n", obj_nameof(&ulp->unit.gen), mpr(actor, "%s stopped at %s\n", obj_nameof(unit),
xyas(ulp->unit.gen.x, ulp->unit.gen.y, xyas(unit->x, unit->y, unit->own));
ulp->unit.gen.own)); if (unit->ef_type == EF_LAND) {
if (ulp->unit.ef_type == EF_LAND) {
if (ulp->mobil < -127) if (ulp->mobil < -127)
ulp->mobil = -127; ulp->mobil = -127;
ulp->unit.land.lnd_mobil = ulp->mobil; unit->mobil = ulp->mobil;
} }
} }
if (ulp->unit.ef_type == EF_SHIP) if (unit->ef_type == EF_SHIP)
ulp->unit.ship.shp_mobil = (int)ulp->mobil; unit->mobil = (int)ulp->mobil;
put_empobj(&ulp->unit.gen); put_empobj(unit->ef_type, unit->uid, unit);
newqp = qp->q_back; newqp = qp->q_back;
emp_remque(qp); emp_remque(qp);
free(qp); free(qp);

444
src/util/empdump.c Normal file
View file

@ -0,0 +1,444 @@
/*
* Empire - A multi-player, client/server Internet based war game.
* Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---
*
* See files README, COPYING and CREDITS in the root of the source
* tree for related information and legal notices. It is expected
* that future projects/authors will amend these files as needed.
*
* ---
*
* empdump.c: Export/import Empire game state
*
* Known contributors to this file:
* Markus Armbruster, 2008
*/
#include <config.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "file.h"
#include "optlist.h"
#include "prototypes.h"
#include "version.h"
#include "xdump.h"
static void dump_table(int, int);
static void shp_fixup(void);
static void pln_fixup(void);
static void lnd_fixup(void);
static void nuk_fixup(void);
int
main(int argc, char *argv[])
{
char *config_file = NULL;
char *import = NULL;
int export = 0;
int private = 0;
int human = 1;
int opt, i, lineno, type;
FILE *impf;
int dirty[EF_MAX];
while ((opt = getopt(argc, argv, "e:mtxhv")) != EOF) {
switch (opt) {
case 'e':
config_file = optarg;
break;
case 'h':
printf("Usage: %s [OPTION]... [DUMP-FILE]\n"
" -e CONFIG-FILE configuration file\n"
" (default %s)\n"
" -m use machine-readable format\n"
" -t test import, don't update game state\n"
" -x export to standard output\n"
" -h display this help and exit\n"
" -v display version information and exit\n",
argv[0], dflt_econfig);
exit(0);
case 'm':
human = 0;
break;
case 't':
private = EFF_PRIVATE;
break;
case 'x':
export = 1;
break;
case 'v':
printf("%s\n\n%s", version, legal);
exit(0);
default:
fprintf(stderr, "Try -h for help.\n");
exit(1);
}
}
if (argv[optind])
import = argv[optind++];
if (import) {
impf = fopen(import, "r");
if (!impf) {
fprintf(stderr, "Cant open %s for reading (%s)\n",
import, strerror(errno));
exit(1);
}
} else
private = EFF_PRIVATE;
/* read configuration & initialize */
empfile_init();
if (emp_config(config_file) < 0)
exit(1);
empfile_fixup();
nsc_init();
if (read_builtin_tables() < 0)
exit(1);
if (read_custom_tables() < 0)
exit(1);
if (chdir(gamedir)) {
fprintf(stderr, "Can't chdir to %s (%s)\n",
gamedir, strerror(errno));
exit(1);
}
global_init();
for (i = 0; i < EF_MAX; i++) {
if (!EF_IS_GAME_STATE(i))
continue;
if (!ef_open(i, EFF_MEM | private))
exit(1);
}
/* import from IMPORT */
memset(dirty, 0, sizeof(dirty));
if (import) {
lineno = 1;
while ((type = xundump(impf, import, &lineno, EF_BAD)) >= 0)
dirty[type] = 1;
if (type == EF_BAD)
exit(1);
shp_fixup();
pln_fixup();
lnd_fixup();
nuk_fixup();
}
if (ef_verify() < 0)
exit(1);
/* export to stdout */
if (export) {
for (i = 0; i < EF_MAX; i++) {
if (!EF_IS_GAME_STATE(i))
continue;
dump_table(i, human);
}
}
/* write out imported data */
for (i = 0; i < EF_MAX; i++) {
if (!EF_IS_GAME_STATE(i))
continue;
if (!private && dirty[i]) {
if (!ef_close(i))
exit(1);
}
}
return 0;
}
static void
printf_wrapper(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
static void
dump_table(int type, int human)
{
struct xdstr xd;
struct castr *ca;
int i;
void *p;
ca = ef_cadef(type);
if (!ca)
return;
xdinit(&xd, 0, human, printf_wrapper);
xdhdr(&xd, ef_nameof(type), 0);
xdcolhdr(&xd, ca);
for (i = 0; (p = ef_ptr(type, i)); i++) {
xdflds(&xd, ca, p);
printf("\n");
}
xdftr(&xd, i);
}
/* FIXME remove need for this */
#include <math.h>
#include "ship.h"
#include "plane.h"
#include "land.h"
#include "nuke.h"
static int fit_plane_on_ship(struct plnstr *, struct shpstr *);
static int fit_plane_on_land(struct plnstr *, struct lndstr *);
static void
shp_fixup(void)
{
int i;
struct shpstr *sp;
for (i = 0; (sp = ef_ptr(EF_SHIP, i)); i++) {
if (!sp->shp_own)
continue;
shp_set_tech(sp, sp->shp_tech);
}
}
static void
pln_fixup(void)
{
int i;
struct plnstr *pp;
struct shpstr *csp;
struct lndstr *clp;
for (i = 0; (pp = ef_ptr(EF_PLANE, i)); i++) {
if (!pp->pln_own)
continue;
pln_set_tech(pp, pp->pln_tech);
csp = ef_ptr(EF_SHIP, pp->pln_ship);
clp = ef_ptr(EF_LAND, pp->pln_land);
if (csp)
fit_plane_on_ship(pp, csp);
else if (clp)
fit_plane_on_land(pp, clp);
}
}
static void
lnd_fixup(void)
{
int i;
struct lndstr *lp;
struct shpstr *csp;
struct lndstr *clp;
for (i = 0; (lp = ef_ptr(EF_LAND, i)); i++) {
if (!lp->lnd_own)
continue;
lnd_set_tech(lp, lp->lnd_tech);
csp = ef_ptr(EF_SHIP, lp->lnd_ship);
clp = ef_ptr(EF_LAND, lp->lnd_land);
if (csp)
csp->shp_nland++;
else if (clp)
clp->lnd_nland++;
}
}
static void
nuk_fixup(void)
{
int i;
struct nukstr *np;
struct plnstr *cpp;
for (i = 0; (np = ef_ptr(EF_NUKE, i)); i++) {
if (!np->nuk_own)
continue;
cpp = ef_ptr(EF_PLANE, np->nuk_plane);
if (cpp)
cpp->pln_nuketype = np->nuk_type;
}
}
/* FIXME don't copy this from src/lib/subs/???sub.c */
/*
* Set SP's tech to TLEV along with everything else that depends on it.
*/
void
shp_set_tech(struct shpstr *sp, int tlev)
{
struct mchrstr *mcp = mchr + sp->shp_type;
int tech_diff = tlev - mcp->m_tech;
if (CANT_HAPPEN(tech_diff < 0)) {
tlev -= tech_diff;
tech_diff = 0;
}
sp->shp_tech = tlev;
sp->shp_armor = (short)SHP_DEF(mcp->m_armor, tech_diff);
sp->shp_speed = (short)SHP_SPD(mcp->m_speed, tech_diff);
sp->shp_visib = (short)SHP_VIS(mcp->m_visib, tech_diff);
sp->shp_frnge = (short)SHP_RNG(mcp->m_frnge, tech_diff);
sp->shp_glim = (short)SHP_FIR(mcp->m_glim, tech_diff);
}
/*
* Fit a plane of PP's type on ship SP.
* Adjust SP's plane counters.
* Updating the plane accordingly is the caller's job.
* Return whether it fits.
*/
static int
fit_plane_on_ship(struct plnstr *pp, struct shpstr *sp)
{
struct plchrstr *pcp = plchr + pp->pln_type;
struct mchrstr *mcp = mchr + sp->shp_type;
int wanted;
if (pcp->pl_flags & P_K) {
/* chopper, try chopper slot first */
if (sp->shp_nchoppers < mcp->m_nchoppers)
return ++sp->shp_nchoppers;
/* else try plane slot */
wanted = M_FLY;
} else if (pcp->pl_flags & P_E) {
/* x-light, try x-light slot first */
if (sp->shp_nxlight < mcp->m_nxlight)
return ++sp->shp_nxlight;
/* else try plane slot */
wanted = M_MSL | M_FLY;
} else if (!(pcp->pl_flags & P_L)) {
/* not light, no go */
wanted = 0;
} else if (pcp->pl_flags & P_M) {
/* missile, use plane slot */
wanted = M_MSL | M_FLY;
} else {
/* fixed-wing plane, use plane slot */
wanted = M_FLY;
}
if ((mcp->m_flags & wanted) == 0)
return 0; /* ship not capable */
if (sp->shp_nplane < mcp->m_nplanes)
return ++sp->shp_nplane;
return 0;
}
/*
* Fit a plane of PP's type on land unit LP.
* Adjust LP's plane counters.
* Updating the plane accordingly is the caller's job.
* Return whether it fits.
*/
static int
fit_plane_on_land(struct plnstr *pp, struct lndstr *lp)
{
struct plchrstr *pcp = plchr + pp->pln_type;
struct lchrstr *lcp = lchr + lp->lnd_type;
if ((pcp->pl_flags & P_E) && lp->lnd_nxlight < lcp->l_nxlight)
return ++lp->lnd_nxlight;
return 0;
}
/*
* Set PP's tech to TLEV along with everything else that depends on it.
*/
void
pln_set_tech(struct plnstr *pp, int tlev)
{
struct plchrstr *pcp = plchr + pp->pln_type;
int tech_diff = tlev - pcp->pl_tech;
int limited_range = pp->pln_range < pp->pln_range_max;
if (CANT_HAPPEN(tech_diff < 0)) {
tlev -= tech_diff;
tech_diff = 0;
}
pp->pln_tech = tlev;
pp->pln_att = PLN_ATTDEF(pcp->pl_att, tech_diff);
pp->pln_def = PLN_ATTDEF(pcp->pl_def, tech_diff);
pp->pln_acc = PLN_ACC(pcp->pl_acc, tech_diff);
pp->pln_range_max = PLN_RAN(pcp->pl_range, tech_diff);
pp->pln_load = PLN_LOAD(pcp->pl_load, tech_diff);
if (!limited_range || pp->pln_range > pp->pln_range_max)
pp->pln_range = pp->pln_range_max;
}
/*
* Set LP's tech to TLEV along with everything else that depends on it.
*/
void
lnd_set_tech(struct lndstr *lp, int tlev)
{
struct lchrstr *lcp = lchr + lp->lnd_type;
int tech_diff = tlev - lcp->l_tech;
if (CANT_HAPPEN(tech_diff < 0)) {
tlev -= tech_diff;
tech_diff = 0;
}
lp->lnd_tech = tlev;
lp->lnd_att = (float)LND_ATTDEF(lcp->l_att, tech_diff);
lp->lnd_def = (float)LND_ATTDEF(lcp->l_def, tech_diff);
lp->lnd_vul = (int)LND_VUL(lcp->l_vul, tech_diff);
lp->lnd_spd = (int)LND_SPD(lcp->l_spd, tech_diff);
lp->lnd_vis = (int)LND_VIS(lcp->l_vis, tech_diff);
lp->lnd_spy = (int)LND_SPY(lcp->l_spy, tech_diff);
lp->lnd_rad = (int)LND_RAD(lcp->l_rad, tech_diff);
lp->lnd_frg = (int)LND_FRG(lcp->l_frg, tech_diff);
lp->lnd_acc = (int)LND_ACC(lcp->l_acc, tech_diff);
lp->lnd_dam = (int)LND_DAM(lcp->l_dam, tech_diff);
lp->lnd_ammo = (int)LND_AMM(lcp->l_ammo, tech_diff);
lp->lnd_aaf = (int)LND_AAF(lcp->l_aaf, tech_diff);
lp->lnd_fuelc = (int)LND_FC(lcp->l_fuelc, tech_diff);
lp->lnd_fuelu = (int)LND_FU(lcp->l_fuelu, tech_diff);
lp->lnd_maxlight = (int)LND_XPL(lcp->l_nxlight, tech_diff);
lp->lnd_maxland = (int)LND_MXL(lcp->l_nland, tech_diff);
}
/* FIXME don't copy this from src/lib/update/nat.c */
double
logx(double d, double base)
{
if (base == 1.0)
return d;
return log10(d) / log10(base);
}