/* textdump.c */
#include "copyright.h"
#include "config.h"
#include <stdio.h>
#include <ctype.h>
#if defined(ALLOCA_H) && !defined(NO_ALLOCA)
#include <alloca.h>
#endif
#include "teeny.h"
#include "db.h"
#include "strlist.h"
/*
* Provides a simple text dump format for TeenyMUD. a la TinyMUD. This should
* be considered as more or less an adjunct component of the database proper,
* since it roots around directly with DB structures.
*
* Dump format:
*
* Preamble of lines starting with !'s. These are comments.
*
* A line starting with &, and followed by a number. This number is the total
* number of objects contained in the dump (including garbage)
*
* A sequence of objects. Real objects start with a line like #127 to indicate
* object number, and are followed by a sequence of lines, one per field,
* describing the object data. Garbage objects are coded by a single line
* like @1283.
*
* A line at the end **** END OF DUMP ****
*/
extern struct dsc **main_index;
extern int total_objects;
extern int actual_objects;
extern int garbage_count;
extern struct obj_data *lookup_obj();
#ifdef COMPRESS
extern char *uncompress();
#endif /* COMPRESS */
void getstring();
void getstrings();
void skip_line();
void getnumber();
void getlock();
void putnumber();
void putstrings();
void putlock();
static int convert_flags();
static void convert_name();
static void convert_gender();
int text_load(name)
char *name;
{
FILE *in;
char work[BUFFSIZ + 16];
int objcount, objnum, i;
int garbagecount;
int done;
int flags, creatednum, objflags;
int read_version = 0;
extern int slack;
if (name[strlen(name) - 1] == 'Z') {
sprintf(work, "uncompress < %s", name);
if ((in = popen(work, "r")) == NULL) {
warning("text_load", "could not open dump file");
return (-1);
}
} else {
if ((in = fopen(name, "r")) == NULL) {
warning("text_load", "could not open dump file");
return (-1);
}
}
/* Read and ignore the preamble */
do {
if (fgets(work, BUFFSIZ + 16, in) == NULL) {
warning("text_load", "bad preamble");
goto fail;
}
} while (work[0] == '!');
if (work[0] == '%') {
objcount = atoi(work + 1);
} else if (work[0] == '&') {
char *z;
objcount = atoi(work + 1);
for (z = work; *z && *z != ' '; z++);
if (!*z)
read_version = 100;
else {
z++;
read_version = atoi(z);
}
} else {
warning("text_load", "couldn't object count");
goto fail;
}
if (objcount <= 0) {
warning("text_load", "DB of size <= 0? No way..");
goto fail;
}
/* Set up a blank DB */
initialize_db(objcount + SLACK);
slack = objcount + SLACK;
/*
* We kludge the garbage objects a bit. We track them, and every time we
* find a garbage object, we count it. We don't touch garbage_objects,
* though, so the db code thinks there aren't any until the end when we
* tell it all of a sudden. This means the db code will keep numbers in
* synch for us.
*/
garbagecount = 0;
/* Do it to it. Loop away sucking in objects and building them */
done = 0;
for (i = 0; i < objcount && !done; i++) {
if (fgets(work, BUFFSIZ + 16, in) == NULL)
goto fail;
switch (work[0]) {
case '~': /* version number */
if (read_version > 99) {
warning("text_load", "found version number in new style db");
goto fail;
}
read_version = atoi(work + 1);
if (read_version > 4) {
warning("text_load", "input DB version not supported");
goto fail;
}
i--; /* a kludge to fix the for loop */
break;
case '#': /* Actual object */
objnum = atoi(work + 1);
if (objnum != i) {
warning("text_load", "input DB is out of synch");
goto fail;
}
if (fgets(work, BUFFSIZ + 16, in) == NULL)
goto fail;
flags = atoi(work);
if (read_version < 102)
flags = convert_flags(flags, read_version); /* convert 'em */
creatednum = create_obj(flags & TYPE_MASK);
if (creatednum != objnum) {
warning("text_load"
,"input db and internal db out of synch.");
goto fail;
}
/* Set up the object flags, being CAREFUL */
if (get_int_elt(i, FLAGS, &objflags) == -1)
goto fail;
flags = (flags & ~INTERNAL_FLAGS)
| (objflags & INTERNAL_FLAGS);
if (set_int_elt(i, FLAGS, flags) == -1)
goto fail;
/* Now suck in all the data elements */
if (read_version < 100) {
getstring(i, NAME, in);
if (isplayer(i)) {
char *z;
(void) get_str_elt(i, NAME, &z);
convert_name(i, z);
}
}
getnumber(i, NEXT, in);
getnumber(i, QUOTA, in);
getnumber(i, LOC, in);
getnumber(i, HOME, in);
getnumber(i, OWNER, in);
getnumber(i, CONTENTS, in);
getnumber(i, EXITS, in);
if (read_version > 99) {
getnumber(i, ROOMS, in);
}
if ((read_version == 2) || (read_version > 3)) {
getnumber(i, TIMESTAMP, in);
} else {
stamp(i);
}
getlock(i, LOCK, in);
if (read_version > 99) {
getlock(i, ELOCK, in);
getlock(i, DESTINATIONS, in);
}
if (read_version < 99) {
getstring(i, SUC, in);
getstring(i, OSUC, in);
getstring(i, FAIL, in);
getstring(i, OFAIL, in);
if (read_version > 2) {
getstring(i, DROP, in);
getstring(i, ODROP, in);
}
getstring(i, DESC, in);
if (fgets(work, BUFFSIZ + 16, in) == NULL)
goto fail;
convert_gender(i, work);
} else {
getstrings(i, in, read_version);
}
break;
case '@': /* Garbage object */
garbagecount++;
total_objects++; /* Fake out the DB */
main_index[i] = (struct dsc *) NULL;
break;
case '*': /* End of file */
done = 1;
break;
}
}
garbage_count = garbagecount;
/* total_objects is already correct */
(void) fclose(in);
return (0);
fail:
(void) fclose(in);
warning("text_load", "load failed");
return (-1);
}
/*
* Generic routines for reading junk in to an object.
*
*/
void getstring(obj, code, f)
int obj;
int code;
FILE *f;
{
char work[BUFFSIZ + 16];
char *p;
int ret;
if (fgets(work, BUFFSIZ + 16, f) == NULL) {
warning("get_string", "unexpected EOF in text_load");
return;
}
for (p = work; *p != '\n' && *p; p++);
*p = '\0';
if (p == work) {
ret = set_str_elt(obj, code, (char *) NULL);
} else {
ret = set_str_elt(obj, code, work);
}
if (ret == -1) {
warning("getstring", "error setting string elt in text_load");
}
}
void skip_line(f)
FILE *f;
{
char temp[BUFFSIZ + 16];
if (fgets(temp, BUFFSIZ + 16, f) == NULL) {
warning("skip_line", "unexpected EOF in text_load");
return;
}
}
void getnumber(obj, code, f)
int obj;
int code;
FILE *f;
{
char work[BUFFSIZ + 16];
int num;
if (fgets(work, BUFFSIZ + 16, f) == NULL) {
warning("getnumber", "unexpected EOF in text_load");
return;
}
if ((!isdigit(work[0])) && !(work[0] == '-' && isdigit(work[1]))) {
warning("getnumber", "bad integer read in text_load");
return;
}
num = atoi(work);
if (code == TIMESTAMP && (num < 32000000)) /* 16bit */
num *= 60;
if (set_int_elt(obj, code, num) == -1) {
warning("getnumber", "could not set int elt in text_load");
}
}
static void getlock(obj, elt, f)
int obj, elt;
FILE *f;
{
char work[BUFFSIZ + 16], *p;
int size, *lock, i;
if (fgets(work, BUFFSIZ + 16, f) == NULL) {
warning("get_string", "unexpected EOF in text_load");
return;
}
/* OK. Locks are slightly complex. We malloc memory HERE for it */
if (work[0] == '\n') {
if (set_lock_elt(obj, elt, (int *) NULL) == -1)
warning("getlock", "error setting lock elt in text_load");
return;
}
size = atoi(work);
if (size <= 0) {
warning("getlock", "bad lock field in text database");
return;
}
lock = (int *) ty_malloc(sizeof(int) * (size + 1), "getlock");
lock[0] = size;
p = work;
for (i = 1; i <= size; i++) {
while (!isspace(*p) && *p)
p++;
while (isspace(*p))
p++;
if (*p == '\0') {
warning("getlock", "bad lock field in text database");
return;
}
lock[i] = atoi(p);
}
if (set_lock_elt(obj, elt, lock) == -1)
warning("getlock", "error setting lock elt in text_load");
}
static void getstrings(obj, f, version)
int obj;
FILE *f;
int version;
{
char work[BUFFSIZ + 16];
int ret, code = 0;
char *p;
do {
if (fgets(work, BUFFSIZ + 16, f) == NULL) {
warning("getstrings", "unexpected EOF in text_load");
return;
}
for (p = work; *p && *p != '\n'; p++);
*p = '\0';
if (p != work && work[0] != '<') {
for (p = work; *p && *p != ':'; p++);
*p++ = '\0';
code = atoi(work);
if (code > 0) {
if (version < 103 && code == NAME && isplayer(obj)) {
convert_name(obj, p);
} else if (version < 104 && code == 19 /* GENDER */ ) {
convert_gender(obj, p);
} else
ret = set_str_elt(obj, code, p);
if (ret == -1) {
warning("getstrings", "error setting string elt in text_load");
}
} else {
warning("getstrings", "error getting code");
return;
}
}
} while (work[0] != '<');
}
/*
* Dump out the database in text format, with the specified offset.
*/
void text_dump(name, offset)
char *name;
{
FILE *out;
int obj;
struct dsc *thedsc;
struct obj_data *theobj;
char cmd[256];
if (offset < 1)
offset = 0;
if (name[strlen(name) - 1] == 'Z') {
sprintf(cmd, "compress > %s", name);
if ((out = popen(cmd, "w")) == NULL) {
warning("text_dump", "could open dump file");
return;
}
} else {
if ((out = fopen(name, "w")) == NULL) {
warning("text_dump", "could not open dump file");
return;
}
}
/* Write out a header. */
if (offset == 0) {
fprintf(out,
"!\n! %s textdump\n! Actual objects: %d\n!\n", version,
actual_objects);
fprintf(out, "&%d 105\n", total_objects);
} else {
fprintf(out,
"!\n! %s offset textdump\n! Actual objects: %d, Offset: %d\n!\n",
version, actual_objects, offset);
fprintf(out, "&%d 105\n", total_objects);
}
/* Now dump the DB. */
for (obj = 0; obj < total_objects; obj++) {
if (exists_object(obj)) {
/* Go get the object, and dump it */
thedsc = main_index[obj];
theobj = lookup_obj(obj);
if (theobj == NULL) { /* awe, fuck */
log_error("text_dump: couldn't load obj #%d! writing it as garbage.\n",
obj);
fprintf(out, "@%d\n", obj + offset);
} else {
fprintf(out, "#%d\n", obj + offset);
putnumber(DSC_FLAGS(thedsc), out);
putnumber((thedsc->list_next + offset), out);
putnumber(theobj->pennies, out); /* quota */
putnumber((theobj->loc + offset), out);
putnumber((thedsc->home_dropto + offset), out);
putnumber((DSC_OWNER(thedsc) + offset), out);
putnumber((theobj->contents + offset), out);
putnumber((theobj->exits + offset), out);
putnumber((theobj->rooms + offset), out);
putnumber(theobj->timestamp, out);
putlock(theobj->lock, out, offset);
putlock(theobj->elock, out, offset);
putlock(theobj->destinations, out, offset);
putstrings(obj, out);
cache_trim();
}
} else {
/* This object does not exist. Make a garbage entry */
fprintf(out, "@%d\n", obj + offset);
}
}
/* Write a trailer */
fputs("**** END OF DUMP ****\n", out);
(void) fclose(out);
}
/*
* General output routines for text_dump()
*/
void putnumber(num, f)
int num;
FILE *f;
{
(void) fprintf(f, "%d\n", num);
}
void putstrings(obj, f)
int obj;
FILE *f;
{
StringList *strs;
char *p;
for (strs = Strings; strs->code; strs++) {
if (get_str_elt(obj, strs->code, &p) == -1) {
log_error("text_dump: bad %s string on object %d. cleared.\n",
strs->name, obj);
p = NULL;
}
if (p != NULL)
fprintf(f, "%d:%s\n", strs->code, p);
}
fputs("<\n", f);
}
void putlock(lock, f, offset)
int *lock;
FILE *f;
int offset;
{
int count, i;
if (lock == NULL || lock[0] == 0) {
fputs("\n", f);
return;
}
count = lock[0]; /* How many FOLLOW */
(void) fprintf(f, "%d", count);
for (i = 1; count > 0; count--, i++) {
if ((offset == 0) || (lock[i] < 0))
(void) fprintf(f, " %d", lock[i]);
else
(void) fprintf(f, " %d", lock[i] + offset);
}
fputs("\n", f);
}
/* for converting from TeenyMUD 1.x to beyond */
static int convert_flags(oldflags, version)
int oldflags, version;
{
int newflags;
if (version < 99) { /* 1.0 - 1.2 */
if (oldflags & 0x0004)
newflags |= STICKY;
if (oldflags & 0x0008)
newflags |= WIZARD;
if (oldflags & 0x0020)
newflags |= LINK_OK;
if (oldflags & 0x0040)
newflags |= DARK;
if (oldflags & 0x0080)
newflags |= HAVEN;
if (oldflags & 0x0100)
newflags |= JUMP_OK;
if (oldflags & 0x0200)
newflags |= ABODE;
if (oldflags & 0x0400)
newflags |= BUILDER;
}
if (version < 101) {
switch (oldflags & 0x0003) {
case 0:
newflags = TYP_PLAYER;
break;
case 1:
newflags = TYP_THING;
break;
case 2:
newflags = TYP_ROOM;
break;
case 3:
newflags = TYP_EXIT;
break;
}
}
if (version < 102) {
newflags = (oldflags & 0x00000003);
if (oldflags & 0x00000004) {/* STICKY */
oldflags &= ~0x00000004;
oldflags |= STICKY;
}
newflags |= (oldflags & ~(0x00000003 | INTERNAL_FLAGS));
}
if (version < 105) { /* TEMPLE */
newflags = (oldflags & ~0x00000010);
}
return newflags;
}
/* convert an old player name */
static void convert_name(obj, str)
int obj;
char *str;
{
char *work, *p;
#ifdef NO_ALLOCA
work = (char *) ty_malloc(strlen(str) + 1, "convert_name");
#else
work = (char *) alloca(strlen(str) + 1);
#endif
(void) strcpy(work, str);
p = work;
while (*p && *p != ' ')
p++;
*p++ = 0;
if (set_str_elt(obj, NAME, work) == -1)
warning("convert_name", "couldn't set player name");
if (set_str_elt(obj, PASSWORD, crypt(p, CRYPT_KEY)) == -1)
warning("convert_name", "couldn't set player pword");
#ifdef NO_ALLOCA
ty_free(work);
#endif
}
static void convert_gender(obj, str)
int obj;
char *str;
{
int flags;
if (str == NULL || !isplayer(obj))
return;
if (get_int_elt(obj, FLAGS, &flags) == -1)
return;
switch (str[0]) {
case 'N':
case 'n':
case 'I':
case 'i':
flags |= GENDER_NEUTER;
break;
case 'F':
case 'f':
flags |= GENDER_FEMALE;
break;
case 'M':
case 'm':
flags |= GENDER_MALE;
break;
default:
return;
}
(void) set_int_elt(obj, FLAGS, flags);
}