/* 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); }