Compare commits
20 commits
master
...
empdump-v4
Author | SHA1 | Date | |
---|---|---|---|
c313aafd8a | |||
add345f77f | |||
4ef3574427 | |||
b9c725224e | |||
d83dd2f5e4 | |||
3f15ea4bea | |||
1aa3cdb0ca | |||
e7e61687f4 | |||
9a1fef87fe | |||
5b7a6af94e | |||
e63f38833b | |||
3117ef9c7a | |||
2dea1b914d | |||
38ead5e199 | |||
5a1cfeba95 | |||
fc807a4c0a | |||
f8a35dda1e | |||
d4ac7d94b2 | |||
642c11eb64 | |||
53bcf04664 |
24 changed files with 1142 additions and 362 deletions
2
Make.mk
2
Make.mk
|
@ -104,7 +104,7 @@ deps := $(obj:.o=.d)
|
|||
# Library archives:
|
||||
libs := $(addprefix lib/, libcommon.a libas.a libgen.a libglobal.a)
|
||||
# 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)
|
||||
server := src/server/emp_server$(EXEEXT)
|
||||
# Info subjects:
|
||||
|
|
25
doc/xdump
25
doc/xdump
|
@ -174,9 +174,10 @@ precisely specified. We use EBNF (ISO 14977) for syntax, except we
|
|||
use '-' in meta-identifiers and omit the concatenation symbol ','.
|
||||
|
||||
table = header { record } footer ;
|
||||
header = "XDUMP" space [ "meta" space ] name space timestamp newline ;
|
||||
name = name-chr { name-chr } ;
|
||||
name-chr = ? ASCII characters 33..126 ? ;
|
||||
header = "XDUMP" space [ "meta" space ]
|
||||
identifier space timestamp newline ;
|
||||
identifier = id-chr { id-chr } ;
|
||||
id-char = ? ASCII characters 33..126 except '"#()<>=' ? ;
|
||||
timestamp = intnum ;
|
||||
footer = "/" number newline ;
|
||||
record = [ fields ] newline ;
|
||||
|
@ -211,7 +212,7 @@ Notes:
|
|||
|
||||
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
|
||||
granularity: game state may change between an xdump and the next
|
||||
|
@ -524,7 +525,7 @@ EBNF changes:
|
|||
|
||||
* Header and footer:
|
||||
|
||||
header = "config" name newline colhdr newline ;
|
||||
header = "config" identifier newline colhdr newline ;
|
||||
colhdr = { identifier [ "(" ( intnum | identifier ) ")" ] } [ "..." ] ;
|
||||
footer = "/config" newline ;
|
||||
|
||||
|
@ -542,7 +543,7 @@ EBNF changes:
|
|||
update.
|
||||
|
||||
- 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:
|
||||
|
||||
|
@ -662,7 +663,7 @@ and section `Objectives':
|
|||
enemy ships, nations).
|
||||
|
||||
(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
|
||||
date, but if it is added, it will be added on a separate port to
|
||||
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
|
||||
events.
|
||||
|
||||
To be complete, a machine readable protocol must disclose as much
|
||||
information as the human readable output. Tracking server game
|
||||
To be complete, a machine-readable protocol must disclose as much
|
||||
information as the human-readable output. Tracking server game
|
||||
state changes cannot do that alone. For instance, lookout tells
|
||||
you ship#, owner and location. That event does not trigger any
|
||||
state change on the server!
|
||||
|
||||
To be correct, a machine readable protocol must disclose no more
|
||||
information than the human readable output. When you observe a
|
||||
To be correct, a machine-readable protocol must disclose no more
|
||||
information than the human-readable output. When you observe a
|
||||
server game state change, you can only guess what event triggered
|
||||
it, and what it disclosed to which player. You're stuck with
|
||||
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
|
||||
*events*. Whenever something is printed to a player, be it live
|
||||
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
|
||||
meta-data. It is, however, much more modest in scope. We're pretty
|
||||
|
|
|
@ -41,9 +41,11 @@
|
|||
#include "item.h"
|
||||
|
||||
struct comstr {
|
||||
/* initial part must match struct empobj */
|
||||
short ef_type;
|
||||
natid com_owner;
|
||||
short com_uid;
|
||||
/* end of part matching struct empobj */
|
||||
i_type com_type;
|
||||
int com_amount;
|
||||
float com_price;
|
||||
|
|
|
@ -56,7 +56,8 @@ struct empobj {
|
|||
short uid;
|
||||
coord x; /* 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 mobil;
|
||||
unsigned char off;
|
||||
|
@ -88,9 +89,11 @@ union empobj_storage {
|
|||
|
||||
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 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 char *emp_obj_chr_name(struct empobj *gp);
|
||||
extern int get_empobj_mob_max(int type);
|
||||
|
|
|
@ -57,7 +57,7 @@ struct empfile {
|
|||
int cids; /* # entries in cache */
|
||||
int fids; /* # entries in table */
|
||||
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 */
|
||||
void (*init)(int, void *); /* called after entry creation, unless null */
|
||||
|
@ -86,8 +86,8 @@ struct empfile {
|
|||
/* Flags set when table contents is mapped */
|
||||
/* Table is entirely in memory */
|
||||
#define EFF_MEM bit(8)
|
||||
/* Table is read-only */
|
||||
#define EFF_RDONLY bit(9)
|
||||
/* Table is privately mapped: changes don't affect the underlying file */
|
||||
#define EFF_PRIVATE bit(9)
|
||||
/* Table is customized (configuration tables only) */
|
||||
#define EFF_CUSTOM bit(10)
|
||||
/* Transient flags, only occur in argument of ef_open() */
|
||||
|
@ -97,7 +97,8 @@ struct empfile {
|
|||
/*
|
||||
* Empire `file types'
|
||||
* 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 {
|
||||
/* Error value */
|
||||
|
|
|
@ -315,8 +315,8 @@ extern int sct_typematch(char *);
|
|||
extern int demand_update_want(int *, int *, int);
|
||||
extern int demand_check(void);
|
||||
extern int demandupdatecheck(void);
|
||||
/* xundump.c */
|
||||
extern int xundump(FILE *, char *, int *, int);
|
||||
/* xdump.c xundump.c */
|
||||
/* in xdump.h */
|
||||
|
||||
/*
|
||||
* src/lib/gen/ *.c
|
||||
|
@ -480,7 +480,6 @@ extern int islist(char *);
|
|||
/* maps.c */
|
||||
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 unit_map(int, int, struct nstr_sect *, char *);
|
||||
extern int display_region_map(int bmap, int unit_type, coord curx,
|
||||
coord cury, char *arg);
|
||||
extern int bmaps_intersect(natid, natid);
|
||||
|
|
|
@ -48,8 +48,8 @@ struct sctstr {
|
|||
short sct_elev; /* elevation/depth */
|
||||
coord sct_x; /* x coord of sector */
|
||||
coord sct_y; /* y coord of sector */
|
||||
signed char sct_type; /* sector type */
|
||||
/* end of part matching struct empobj */
|
||||
unsigned char sct_type; /* sector type */
|
||||
unsigned char sct_effic; /* 0% to 100% */
|
||||
short sct_mobil; /* mobility units */
|
||||
unsigned char sct_loyal; /* updates until civilans "converted" */
|
||||
|
@ -65,7 +65,7 @@ struct sctstr {
|
|||
short sct_fill; /* gunk */
|
||||
unsigned char sct_work; /* pct of civ actually working */
|
||||
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_gmin; /* amount of gold ore */
|
||||
unsigned char sct_fertil; /* fertility of soil */
|
||||
|
|
|
@ -41,9 +41,11 @@
|
|||
#include "types.h"
|
||||
|
||||
struct trdstr {
|
||||
/* initial part must match struct empobj */
|
||||
short ef_type;
|
||||
natid trd_owner;
|
||||
short trd_uid;
|
||||
/* end of part matching struct empobj */
|
||||
short trd_type;
|
||||
short trd_unitid;
|
||||
long trd_price;
|
||||
|
|
59
include/xdump.h
Normal file
59
include/xdump.h
Normal 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
57
man/empdump.6
Normal 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>
|
|
@ -196,7 +196,7 @@ explore(void)
|
|||
putsect(§);
|
||||
return RET_FAIL;
|
||||
}
|
||||
if (chksect.sct_type == '.') {
|
||||
if (chksect.sct_type == SCT_WATER) {
|
||||
pr("Bridge disappeared!\n");
|
||||
getsect(start.sct_x, start.sct_y, &start);
|
||||
start.sct_flags &= ~MOVE_IN_PROGRESS;
|
||||
|
|
|
@ -321,7 +321,7 @@ mission(void)
|
|||
gp->mission = mission;
|
||||
gp->opx = x;
|
||||
gp->opy = y;
|
||||
put_empobj(gp);
|
||||
put_empobj(type, gp->uid, gp);
|
||||
}
|
||||
if (num == 0) {
|
||||
pr("No %s%s\n", ef_nameof(type), splur(num));
|
||||
|
|
|
@ -74,8 +74,7 @@ nati(void)
|
|||
mil = sect.sct_item[I_MILIT];
|
||||
pr("%d%% eff %s at %s has %d civilian%s & %d military\n",
|
||||
sect.sct_effic,
|
||||
(sect.sct_type ==
|
||||
SCT_CAPIT ? "capital" : "mountain capital"),
|
||||
sect.sct_type == SCT_CAPIT ? "capital" : "mountain capital",
|
||||
xyas(sect.sct_x, sect.sct_y, cnum), civ, splur(civ),
|
||||
mil);
|
||||
}
|
||||
|
|
|
@ -28,168 +28,17 @@
|
|||
* xdump.c: Extended dump
|
||||
*
|
||||
* 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 <ctype.h>
|
||||
#include <stddef.h>
|
||||
#include "commands.h"
|
||||
#include "empobj.h"
|
||||
#include "match.h"
|
||||
#include "news.h"
|
||||
#include "optlist.h"
|
||||
#include "treaty.h"
|
||||
#include "version.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);
|
||||
}
|
||||
#include "xdump.h"
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static int
|
||||
xditem(int type, char *arg)
|
||||
xditem(struct xdstr *xd, int type, char *arg)
|
||||
{
|
||||
struct castr *ca;
|
||||
struct nstr_item ni;
|
||||
|
@ -283,59 +132,29 @@ xditem(int type, char *arg)
|
|||
if (!snxtitem(&ni, type, arg))
|
||||
return RET_SYN;
|
||||
|
||||
xdhdr(ef_nameof(type), 0);
|
||||
xdhdr(xd, ef_nameof(type), 0);
|
||||
|
||||
n = 0;
|
||||
while (nxtitem(&ni, buf)) {
|
||||
if (!xdvisible(type, buf))
|
||||
continue;
|
||||
++n;
|
||||
xdflds(ca, buf);
|
||||
pr("\n");
|
||||
xdflds(xd, ca, buf);
|
||||
xd->pr("\n");
|
||||
}
|
||||
|
||||
xdftr(n);
|
||||
xdftr(xd, n);
|
||||
|
||||
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.
|
||||
*/
|
||||
static int
|
||||
xdmeta(int type)
|
||||
{
|
||||
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)
|
||||
xdver(struct xdstr *xd, int meta)
|
||||
{
|
||||
static struct castr vers_ca = {
|
||||
NSC_STRINGY, 0, sizeof(PACKAGE_STRING), 0, "version", EF_BAD
|
||||
|
@ -346,11 +165,11 @@ xdver(int meta)
|
|||
struct castr ca;
|
||||
struct valstr val;
|
||||
|
||||
xdhdr("version", meta);
|
||||
xdhdr(xd, "version", meta);
|
||||
|
||||
if (meta) {
|
||||
n = 0;
|
||||
xdflds(mdchr_ca, &vers_ca);
|
||||
xdflds(xd, mdchr_ca, &vers_ca);
|
||||
pr("\n");
|
||||
n++;
|
||||
for (kp = configkeys; kp->km_key; ++kp) {
|
||||
|
@ -361,39 +180,40 @@ xdver(int meta)
|
|||
ca.ca_off = 0;
|
||||
ca.ca_name = kp->km_key;
|
||||
ca.ca_table = EF_BAD;
|
||||
xdflds(mdchr_ca, &ca);
|
||||
xdflds(xd, mdchr_ca, &ca);
|
||||
pr("\n");
|
||||
n++;
|
||||
}
|
||||
}
|
||||
xdftr(n);
|
||||
xdftr(xd, n);
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
xdeval(&val, vers_ca.ca_type, version, vers_ca.ca_off, 0, vers_ca.ca_len);
|
||||
sep = xdprval(&val, "");
|
||||
xdeval(&val, xd,
|
||||
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) {
|
||||
if (kp->km_type != NSC_NOTYPE && !(kp->km_flags & KM_INTERNAL)) {
|
||||
xdeval(&val, kp->km_type, kp->km_data, 0, 0, 0);
|
||||
sep = xdprval(&val, sep);
|
||||
xdeval(&val, xd, kp->km_type, kp->km_data, 0, 0, 0);
|
||||
sep = xdprval(xd, &val, sep);
|
||||
}
|
||||
}
|
||||
pr("\n");
|
||||
|
||||
xdftr(1);
|
||||
xdftr(xd, 1);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
/* Experimental extended dump command */
|
||||
int
|
||||
xdump(void)
|
||||
{
|
||||
char *p;
|
||||
char buf[1024];
|
||||
struct xdstr xd;
|
||||
struct natstr *natp;
|
||||
int type;
|
||||
int meta = 0;
|
||||
struct natstr *natp;
|
||||
|
||||
p = getstarg(player->argp[1], "Table name, or meta? ", buf);
|
||||
if (p && strcmp(p, "meta") == 0) {
|
||||
|
@ -403,19 +223,20 @@ xdump(void)
|
|||
if (!p || !*p)
|
||||
return RET_SYN;
|
||||
|
||||
xdinit(&xd, player->cnum, 0, pr);
|
||||
natp = getnatp(player->cnum);
|
||||
type = isdigit(p[0]) ? atoi(p) : ef_byname(p);
|
||||
if (type >= 0 && type < EF_MAX) {
|
||||
if (meta)
|
||||
return xdmeta(type);
|
||||
return xdmeta(&xd, type);
|
||||
else if ((EF_IS_GAME_STATE(type) || EF_IS_VIEW(type))
|
||||
&& !(natp->nat_stat == STAT_ACTIVE || player->god)) {
|
||||
pr("Access to table %s denied\n", ef_nameof(type));
|
||||
return RET_FAIL;
|
||||
} else
|
||||
return xditem(type, player->argp[2]);
|
||||
return xditem(&xd, type, player->argp[2]);
|
||||
} else if (!strncmp(p, "ver", strlen(p))) {
|
||||
return xdver(meta);
|
||||
return xdver(&xd, meta);
|
||||
}
|
||||
|
||||
return RET_SYN;
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "optlist.h"
|
||||
#include "product.h"
|
||||
#include "prototypes.h"
|
||||
#include "xdump.h"
|
||||
|
||||
static int read_custom_table_file(char *);
|
||||
|
||||
|
|
|
@ -46,8 +46,10 @@
|
|||
#include "nsc.h"
|
||||
#include "prototypes.h"
|
||||
|
||||
static int ef_remap_cache(struct empfile *, int);
|
||||
static int fillcache(struct empfile *, 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, ...).
|
||||
|
@ -61,7 +63,7 @@ ef_open(int type, int how)
|
|||
{
|
||||
struct empfile *ep;
|
||||
struct flock lock;
|
||||
int oflags, fd, fsiz, size;
|
||||
int oflags, fd, fsiz, nslots;
|
||||
|
||||
if (ef_check(type) < 0)
|
||||
return 0;
|
||||
|
@ -73,7 +75,7 @@ ef_open(int type, int how)
|
|||
if (CANT_HAPPEN(ep->fd >= 0))
|
||||
return 0;
|
||||
oflags = O_RDWR;
|
||||
if (how & EFF_RDONLY)
|
||||
if (how & EFF_PRIVATE)
|
||||
oflags = O_RDONLY;
|
||||
if (how & EFF_CREATE)
|
||||
oflags |= O_CREAT | O_TRUNC;
|
||||
|
@ -85,7 +87,7 @@ ef_open(int type, int how)
|
|||
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_start = lock.l_len = 0;
|
||||
if (fcntl(fd, F_SETLK, &lock) == -1) {
|
||||
|
@ -116,18 +118,13 @@ ef_open(int type, int how)
|
|||
}
|
||||
}
|
||||
} 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))
|
||||
free(ep->cache);
|
||||
ep->cache = malloc(size);
|
||||
if (ep->cache == NULL) {
|
||||
if (how & EFF_MEM)
|
||||
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);
|
||||
close(fd);
|
||||
return 0;
|
||||
|
@ -148,11 +145,31 @@ ef_open(int type, int how)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Could close fd if both EFF_RDONLY and EFF_MEM, but that doesn't
|
||||
* happen, so don't bother.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
int
|
||||
|
@ -193,6 +211,8 @@ ef_flush(int type)
|
|||
if (ef_check(type) < 0)
|
||||
return 0;
|
||||
ep = &empfile[type];
|
||||
if (ep->flags & EFF_PRIVATE)
|
||||
return 1; /* nothing to do */
|
||||
if (CANT_HAPPEN(ep->fd < 0))
|
||||
return 0;
|
||||
/*
|
||||
|
@ -201,7 +221,7 @@ ef_flush(int type)
|
|||
* allowed only with EFF_MEM. Assume the whole cash is dirty
|
||||
* 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 1;
|
||||
|
@ -316,7 +336,8 @@ do_write(struct empfile *ep, void *buf, int id, int count)
|
|||
int n, ret;
|
||||
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;
|
||||
|
||||
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!
|
||||
* 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).
|
||||
* Can write at the end of partially cached table.
|
||||
* 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)
|
||||
return 0;
|
||||
ep = &empfile[type];
|
||||
if (CANT_HAPPEN((ep->flags & (EFF_MEM | EFF_PRIVATE)) == EFF_PRIVATE))
|
||||
return 0;
|
||||
if (ep->prewrite)
|
||||
ep->prewrite(id, from);
|
||||
if (CANT_HAPPEN((ep->flags & EFF_MEM) ? id >= ep->fids : id > ep->fids))
|
||||
return 0; /* not implemented */
|
||||
if (!(ep->flags & EFF_PRIVATE)) {
|
||||
if (do_write(ep, from, id, 1) < 0)
|
||||
return 0;
|
||||
}
|
||||
if (id >= ep->baseid && id < ep->baseid + ep->cids) {
|
||||
/* update the cache if necessary */
|
||||
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.
|
||||
*/
|
||||
int
|
||||
ef_extend(int type, int count)
|
||||
{
|
||||
struct empfile *ep;
|
||||
void *tmpobj;
|
||||
int id, i, how;
|
||||
char *p;
|
||||
int i, id;
|
||||
|
||||
if (ef_check(type) < 0)
|
||||
return 0;
|
||||
ep = &empfile[type];
|
||||
if (CANT_HAPPEN(ep->fd < 0 || count < 0))
|
||||
if (CANT_HAPPEN(count < 0))
|
||||
return 0;
|
||||
|
||||
tmpobj = calloc(1, ep->size);
|
||||
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) {
|
||||
/* FIXME lazy bastards... do this right */
|
||||
/* XXX this will cause problems if there are ef_ptrs (to the
|
||||
* old allocated structure) active when we do the re-open */
|
||||
how = ep->flags & ~EFF_IMMUTABLE;
|
||||
ef_close(type);
|
||||
ef_open(type, how);
|
||||
if (id + count > ep->csize) {
|
||||
if (ep->flags & EFF_STATIC)
|
||||
return 0;
|
||||
if (!ef_remap_cache(ep, id + count))
|
||||
return 0;
|
||||
}
|
||||
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 {
|
||||
ep->fids += i;
|
||||
/* 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 *
|
||||
|
|
351
src/lib/common/xdump.c
Normal file
351
src/lib/common/xdump.c
Normal 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;
|
||||
}
|
|
@ -56,12 +56,14 @@
|
|||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "file.h"
|
||||
#include "match.h"
|
||||
#include "nsc.h"
|
||||
#include "optlist.h"
|
||||
#include "prototypes.h"
|
||||
#include "xdump.h"
|
||||
|
||||
static char *fname; /* Name of file being read */
|
||||
static int lineno; /* Current line number */
|
||||
|
@ -130,21 +132,6 @@ skipfs(FILE *fp)
|
|||
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.
|
||||
* Return BUF on success, null pointer on failure.
|
||||
|
@ -170,6 +157,22 @@ xuesc(char *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.
|
||||
* I is the field number, counting from zero.
|
||||
|
@ -479,17 +482,20 @@ getobj(void)
|
|||
int need_sentinel = !EF_IS_GAME_STATE(cur_type);
|
||||
|
||||
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) {
|
||||
/* TODO grow cache (and possibly file) unless EFF_STATIC */
|
||||
if (cur_id < ep->csize - !!need_sentinel)
|
||||
ep->cids = ep->fids = cur_id + 1;
|
||||
/* else: ef_ptr() will fail */
|
||||
}
|
||||
if (ef_ensure_space(cur_type, cur_id + !!need_sentinel, 1))
|
||||
cur_obj = ef_ptr(cur_type, cur_id);
|
||||
/* FIXME diagnose out of dynamic memory vs. static table full */
|
||||
if (!cur_obj)
|
||||
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;
|
||||
|
@ -958,8 +964,12 @@ xubody(FILE *fp)
|
|||
{
|
||||
struct empfile *ep = &empfile[cur_type];
|
||||
int need_sentinel = !EF_IS_GAME_STATE(cur_type);
|
||||
int old_maxid = ep->fids;
|
||||
int i, maxid, ch;
|
||||
|
||||
if (old_maxid == 0 && need_sentinel)
|
||||
ef_ensure_space(cur_type, 0, 1);
|
||||
|
||||
maxid = 0;
|
||||
for (i = 0;; ++i) {
|
||||
while ((ch = skipfs(fp)) == '\n')
|
||||
|
@ -974,6 +984,12 @@ xubody(FILE *fp)
|
|||
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))
|
||||
maxid = ep->fids;
|
||||
if (maxid < ep->fids) {
|
||||
|
@ -988,11 +1004,5 @@ xubody(FILE *fp)
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
SZ((array)), 0, SZ((array)) - 1, SZ((array)) - 1, -1, NULL, NULL, NULL
|
||||
|
||||
/* 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[] = {
|
||||
/*
|
||||
|
@ -95,7 +95,7 @@ struct empfile empfile[] = {
|
|||
* bits of flags get their final value.
|
||||
* If flags & EFF_STATIC, the cache is mapped here, and members
|
||||
* 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.
|
||||
* Member fd is initialized to -1.
|
||||
* Members init, postread, prewrite get initialized to NULL, but
|
||||
|
@ -131,7 +131,7 @@ struct empfile empfile[] = {
|
|||
{EF_TREATY, "treaty", "treaty", treaty_ca,
|
||||
UNMAPPED_CACHE(struct trtstr, 0)},
|
||||
{EF_TRADE, "trade", "trade", trade_ca,
|
||||
UNMAPPED_CACHE(struct trdstr, 0)},
|
||||
UNMAPPED_CACHE(struct trdstr, EFF_OWNER)},
|
||||
{EF_POWER, "pow", "power", NULL,
|
||||
UNMAPPED_CACHE(struct powstr, 0)},
|
||||
{EF_NATION, "nat", "nation", nat_ca,
|
||||
|
@ -143,7 +143,7 @@ struct empfile empfile[] = {
|
|||
{EF_BMAP, "bmap", "bmap", NULL,
|
||||
0, 0, NULL, 0, 0, 0, 0, -1, NULL, NULL, NULL},
|
||||
{EF_COMM, "commodity", "commodity", commodity_ca,
|
||||
UNMAPPED_CACHE(struct comstr, 0)},
|
||||
UNMAPPED_CACHE(struct comstr, EFF_OWNER)},
|
||||
{EF_LOST, "lost", "lostitems", lost_ca,
|
||||
UNMAPPED_CACHE(struct loststr, EFF_OWNER)},
|
||||
{EF_REALM, "realm", "realms", realm_ca,
|
||||
|
@ -214,7 +214,8 @@ struct empfile empfile[] = {
|
|||
SYMTAB(EF_TREATY_FLAGS, "treaty-flags", treaty_flags),
|
||||
|
||||
/* 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 */
|
||||
{EF_BAD, NULL, NULL, NULL, 0, 0, NULL, 0, 0, 0, 0, -1, NULL, NULL, NULL},
|
||||
|
|
|
@ -108,7 +108,7 @@ struct castr sect_ca[] = {
|
|||
{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_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_SHORT, 0, 0, fldoff(sctstr, sct_mobil), "mobil", 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_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_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_gmin), "gold", 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_yl), "yl", 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}
|
||||
};
|
||||
|
||||
|
|
|
@ -57,30 +57,6 @@ obj_nameof(struct empobj *gp)
|
|||
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 *
|
||||
get_empobj_chr(struct empobj *gp)
|
||||
{
|
||||
|
|
|
@ -54,7 +54,8 @@
|
|||
#include "xy.h"
|
||||
|
||||
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
|
||||
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.
|
||||
*/
|
||||
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))
|
||||
return '?';
|
||||
|
@ -341,29 +342,30 @@ map_char(unsigned char type, natid own, int owner_or_god)
|
|||
return '?';
|
||||
}
|
||||
|
||||
int
|
||||
static int
|
||||
unit_map(int unit_type, int uid, struct nstr_sect *nsp, char *originp)
|
||||
{
|
||||
struct empobj *gp;
|
||||
union empobj_storage unit;
|
||||
struct range range;
|
||||
char *name;
|
||||
|
||||
gp = get_empobjp(unit_type, uid);
|
||||
if (!gp || (gp->own != player->cnum && !player->god) || gp->own == 0)
|
||||
if (!get_empobj(unit_type, uid, &unit))
|
||||
return RET_FAIL;
|
||||
if (!player->owner || unit.gen.own == 0)
|
||||
return RET_FAIL;
|
||||
|
||||
if (unit_type == EF_NUKE)
|
||||
*originp = 'n';
|
||||
else {
|
||||
if ((name = emp_obj_chr_name(gp)) == NULL)
|
||||
if ((name = emp_obj_chr_name(&unit.gen)) == NULL)
|
||||
return RET_FAIL;
|
||||
*originp = *name;
|
||||
}
|
||||
|
||||
range.lx = xnorm(gp->x - 10);
|
||||
range.hx = xnorm(gp->x + 11);
|
||||
range.ly = ynorm(gp->y - 5);
|
||||
range.hy = ynorm(gp->y + 6);
|
||||
range.lx = xnorm(unit.gen.x - 10);
|
||||
range.hx = xnorm(unit.gen.x + 11);
|
||||
range.ly = ynorm(unit.gen.y - 5);
|
||||
range.hy = ynorm(unit.gen.y + 6);
|
||||
xysize_range(&range);
|
||||
snxtsct_area(nsp, &range);
|
||||
return RET_OK;
|
||||
|
|
|
@ -102,23 +102,24 @@ unit_put(struct emp_qelem *list, natid actor)
|
|||
struct emp_qelem *qp;
|
||||
struct emp_qelem *newqp;
|
||||
struct ulist *ulp;
|
||||
struct empobj *unit;
|
||||
|
||||
qp = list->q_back;
|
||||
while (qp != list) {
|
||||
ulp = (struct ulist *)qp;
|
||||
unit = &ulp->unit.gen;
|
||||
if (actor) {
|
||||
mpr(actor, "%s stopped at %s\n", obj_nameof(&ulp->unit.gen),
|
||||
xyas(ulp->unit.gen.x, ulp->unit.gen.y,
|
||||
ulp->unit.gen.own));
|
||||
if (ulp->unit.ef_type == EF_LAND) {
|
||||
mpr(actor, "%s stopped at %s\n", obj_nameof(unit),
|
||||
xyas(unit->x, unit->y, unit->own));
|
||||
if (unit->ef_type == EF_LAND) {
|
||||
if (ulp->mobil < -127)
|
||||
ulp->mobil = -127;
|
||||
ulp->unit.land.lnd_mobil = ulp->mobil;
|
||||
unit->mobil = ulp->mobil;
|
||||
}
|
||||
}
|
||||
if (ulp->unit.ef_type == EF_SHIP)
|
||||
ulp->unit.ship.shp_mobil = (int)ulp->mobil;
|
||||
put_empobj(&ulp->unit.gen);
|
||||
if (unit->ef_type == EF_SHIP)
|
||||
unit->mobil = (int)ulp->mobil;
|
||||
put_empobj(unit->ef_type, unit->uid, unit);
|
||||
newqp = qp->q_back;
|
||||
emp_remque(qp);
|
||||
free(qp);
|
||||
|
|
444
src/util/empdump.c
Normal file
444
src/util/empdump.c
Normal 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);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue