empiremud/cnf/
empiremud/doc/
empiremud/lib/boards/
empiremud/lib/etc/
empiremud/lib/misc/
empiremud/lib/plralias/F-J/
empiremud/lib/plralias/K-O/
empiremud/lib/plralias/P-T/
empiremud/lib/plralias/U-Z/
empiremud/lib/plrobjs/
empiremud/lib/plrobjs/F-J/
empiremud/lib/plrobjs/K-O/
empiremud/lib/plrobjs/P-T/
empiremud/lib/plrobjs/U-Z/
empiremud/lib/world/
empiremud/lib/world/mob/
empiremud/lib/world/obj/
empiremud/log/
/* ************************************************************************
*   File: objsave.c                                      EmpireMUD AD 1.0 *
*  Usage: loading/saving player objects for rent and crash-save           *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Code base by Paul Clarke.  EmpireMUD Project, a tbgMUD Production.     *
*  Based upon CircleMUD 3.0, beta patch level 17, by Jeremy Elson.        *
*                                                                         *
*  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */

/*
 * AutoEQ by Burkhard Knopf <burkhard.knopf@informatik.tu-clausthal.de>
 */

#include "conf.h"
#include "sysdep.h"


#include "structs.h"
#include "comm.h"
#include "handler.h"
#include "db.h"
#include "interpreter.h"
#include "utils.h"

/* these factors should be unique integers */
#define RENT_FACTOR 	1
#define CRYO_FACTOR 	4

#define LOC_INVENTORY	0
#define MAX_BAG_ROWS	5

extern struct player_index_element *player_table;
extern int top_of_p_table;
extern int obj_file_timeout;

/* Extern functions */
ACMD(do_tell);

/* local functions */
int Obj_to_store(Object obj, FILE * fl, int location);
void update_obj_file(void);
int gen_receptionist(Creature ch, Creature recep, int cmd, char *arg, int mode);
void Crash_restore_weight(Object obj);
void Crash_extract_objs(Object obj);
int Crash_is_unrentable(Object obj);
void Crash_extract_norents(Object obj);
void Crash_rentsave(Creature ch);


Object Obj_from_store(struct obj_file_elem object, int *location) {
	Object obj;
	int j;

	*location = 0;
	if (real_object(object.item_number) >= 0) {
		obj = read_object(object.item_number, VIRTUAL);
		*location = object.location;
		GET_OBJ_VAL(obj, 0) = object.value[0];
		GET_OBJ_VAL(obj, 1) = object.value[1];
		GET_OBJ_VAL(obj, 2) = object.value[2];
		GET_OBJ_EXTRA(obj) = object.extra_flags;
		GET_OBJ_WEIGHT(obj) = object.weight;
		GET_OBJ_TIMER(obj) = object.timer;
		obj->obj_flags.bitvector = object.bitvector;

		for (j = 0; j < MAX_OBJ_AFFECT; j++)
			obj->affected[j] = object.affected[j];

		return (obj);
		}
	else
		return (NULL);
	}


int Obj_to_store(Object obj, FILE * fl, int location)
{
  int j;
  struct obj_file_elem object;

  object.item_number = GET_OBJ_VNUM(obj);
  object.location = location;
  object.value[0] = GET_OBJ_VAL(obj, 0);
  object.value[1] = GET_OBJ_VAL(obj, 1);
  object.value[2] = GET_OBJ_VAL(obj, 2);
  object.extra_flags = GET_OBJ_EXTRA(obj);
  object.weight = GET_OBJ_WEIGHT(obj);
  object.timer = GET_OBJ_TIMER(obj);
  object.bitvector = obj->obj_flags.bitvector;
  for (j = 0; j < MAX_OBJ_AFFECT; j++)
    object.affected[j] = obj->affected[j];

  if (fwrite(&object, sizeof(struct obj_file_elem), 1, fl) < 1) {
    perror("SYSERR: error writing object in Obj_to_store");
    return (0);
  }
  return (1);
}

void auto_equip(Creature ch, Object obj, int *location) {
	int j;

	/* Lots of checks... */
	if (*location > 0) {	/* Was wearing it. */
		switch (j = (*location - 1)) {
			case WEAR_FINGER_R:
			case WEAR_FINGER_L:		if (!CAN_WEAR(obj, ITEM_WEAR_FINGER))		*location = LOC_INVENTORY;		break;
			case WEAR_NECK:			if (!CAN_WEAR(obj, ITEM_WEAR_NECK))			*location = LOC_INVENTORY;		break;
			case WEAR_QUIVER:		if (!CAN_WEAR(obj, ITEM_WEAR_QUIVER))		*location = LOC_INVENTORY;		break;
			case WEAR_EARS:			if (!CAN_WEAR(obj, ITEM_WEAR_EARS))			*location = LOC_INVENTORY;		break;
			case WEAR_BODY:			if (!CAN_WEAR(obj, ITEM_WEAR_BODY))			*location = LOC_INVENTORY;		break;
			case WEAR_ARMOR:		if (!CAN_WEAR(obj, ITEM_WEAR_ARMOR))		*location = LOC_INVENTORY;		break;
			case WEAR_HEAD:			if (!CAN_WEAR(obj, ITEM_WEAR_HEAD))			*location = LOC_INVENTORY;		break;
			case WEAR_LEGS:			if (!CAN_WEAR(obj, ITEM_WEAR_LEGS))			*location = LOC_INVENTORY;		break;
			case WEAR_FEET:			if (!CAN_WEAR(obj, ITEM_WEAR_FEET))			*location = LOC_INVENTORY;		break;
			case WEAR_HANDS:		if (!CAN_WEAR(obj, ITEM_WEAR_HANDS))		*location = LOC_INVENTORY;		break;
			case WEAR_ARMS:			if (!CAN_WEAR(obj, ITEM_WEAR_ARMS))			*location = LOC_INVENTORY;		break;
			case WEAR_ABOUT:		if (!CAN_WEAR(obj, ITEM_WEAR_ABOUT))		*location = LOC_INVENTORY;		break;
			case WEAR_WAIST:		if (!CAN_WEAR(obj, ITEM_WEAR_WAIST))		*location = LOC_INVENTORY;		break;
			case WEAR_SHEATH_1:		if (!CAN_WEAR(obj, ITEM_WEAR_SHEATH))		*location = LOC_INVENTORY;		break;
			case WEAR_SHEATH_2:		if (!CAN_WEAR(obj, ITEM_WEAR_SHEATH))		*location = LOC_INVENTORY;		break;
			case WEAR_SHEATH_3:		if (!CAN_WEAR(obj, ITEM_WEAR_SHEATH))		*location = LOC_INVENTORY;		break;
			case WEAR_IN_SHEATH_1:	if (!CAN_WEAR(obj, ITEM_WEAR_WIELD))		*location = LOC_INVENTORY;		break;
			case WEAR_IN_SHEATH_2:	if (!CAN_WEAR(obj, ITEM_WEAR_WIELD))		*location = LOC_INVENTORY;		break;
			case WEAR_IN_SHEATH_3:	if (!CAN_WEAR(obj, ITEM_WEAR_WIELD))		*location = LOC_INVENTORY;		break;
			case WEAR_WIELD:		if (!CAN_WEAR(obj, ITEM_WEAR_WIELD))		*location = LOC_INVENTORY;		break;
			case WEAR_HOLD:			if (!CAN_WEAR(obj, ITEM_WEAR_HOLD))			*location = LOC_INVENTORY;		break;
			default:				*location = LOC_INVENTORY;
			}

		if (*location > 0) {	    /* Wearable. */
			if (!GET_EQ(ch,j)) {
				equip_char(ch, obj, j);
				}
			else {	/* Oops, saved a player with double equipment? */
				syslog(0, TRUE, "SYSERR: autoeq: '%s' already equipped in position %d.", GET_NAME(ch), *location);
				*location = LOC_INVENTORY;
				}
			}
		}
	if (*location <= 0)	/* Inventory */
		obj_to_char(obj, ch);
	}


int Crash_delete_file(char *name) {
	char filename[50];
	FILE *fl;

	if (!get_filename(name, filename, CRASH_FILE))
		return (0);
	if (!(fl = fopen(filename, "rb"))) {
		if (errno != ENOENT)	/* if it fails but NOT because of no file */
			log("SYSERR: deleting crash file %s (1): %s", filename, strerror(errno));
		return (0);
		}
	fclose(fl);

	/* if it fails, NOT because of no file */
	if (remove(filename) < 0 && errno != ENOENT)
		log("SYSERR: deleting crash file %s (2): %s", filename, strerror(errno));

	return (1);
	}


int Crash_delete_crashfile(Creature ch) {
	char fname[MAX_INPUT_LENGTH];
	struct rent_info rent;
	FILE *fl;

	if (!get_filename(GET_NAME(ch), fname, CRASH_FILE))
		return (0);
	if (!(fl = fopen(fname, "rb"))) {
		if (errno != ENOENT)	/* if it fails, NOT because of no file */
			log("SYSERR: checking for crash file %s (3): %s", fname, strerror(errno));
		return (0);
		}
	if (!feof(fl))
		fread(&rent, sizeof(struct rent_info), 1, fl);
	fclose(fl);

	if (rent.rentcode == RENT_CRASH)
		Crash_delete_file(GET_NAME(ch));

	return (1);
	}


int Crash_clean_file(char *name) {
	char fname[MAX_STRING_LENGTH], filetype[20];
	struct rent_info rent;
	FILE *fl;

	if (!get_filename(name, fname, CRASH_FILE))
		return (0);
	/*
	 * open for write so that permission problems will be flagged now, at boot
	 * time.
	 */
	if (!(fl = fopen(fname, "r+b"))) {
		if (errno != ENOENT)	/* if it fails, NOT because of no file */
			log("SYSERR: OPENING OBJECT FILE %s (4): %s", fname, strerror(errno));
		return (0);
		}
	if (!feof(fl))
		fread(&rent, sizeof(struct rent_info), 1, fl);
	fclose(fl);

	if (rent.time < time(0) - (obj_file_timeout * SECS_PER_REAL_DAY)) {
		Crash_delete_file(name);
		switch (rent.rentcode) {
			case RENT_CRASH:
				strcpy(filetype, "crash");
				break;
			case RENT_RENTED:
				strcpy(filetype, "rent");
				break;
			default:
				strcpy(filetype, "UNKNOWN!");
				break;
			}
		log("    Deleting %s's %s file.", name, filetype);
		return (1);
		}
	return (0);
	}


void update_obj_file(void) {
	int i;

	for (i = 0; i <= top_of_p_table; i++)
		if (*player_table[i].name)
			Crash_clean_file(player_table[i].name);
	}


void Crash_listrent(Creature ch, char *name) {
	FILE *fl;
	char fname[MAX_INPUT_LENGTH], buf[MAX_STRING_LENGTH];
	struct obj_file_elem object;
	Object obj;
	struct rent_info rent;

	if (!get_filename(name, fname, CRASH_FILE))
		return;
	if (!(fl = fopen(fname, "rb"))) {
		sprintf(buf, "%s has no rent file.\r\n", name);
		send_to_char(buf, ch);
		return;
		}
	sprintf(buf, "%s\r\n", fname);
	if (!feof(fl))
		fread(&rent, sizeof(struct rent_info), 1, fl);
	switch (rent.rentcode) {
		case RENT_RENTED:
			strcat(buf, "Rent\r\n");
			break;
		case RENT_CRASH:
			strcat(buf, "Crash\r\n");
			break;
		default:
			strcat(buf, "Undef\r\n");
			break;
		}
	while (!feof(fl)) {
		fread(&object, sizeof(struct obj_file_elem), 1, fl);
		if (ferror(fl)) {
			fclose(fl);
			return;
			}
		if (!feof(fl))
			if (real_object(object.item_number) > -1) {
				obj = read_object(object.item_number, VIRTUAL);
				sprintf(buf + strlen(buf), " [%5d] (%5dau) <%2d> %-20s\r\n", object.item_number, GET_OBJ_RENT(obj), object.location, obj->short_description);
				extract_obj(obj);
				if (strlen(buf) > MAX_STRING_LENGTH - 80) {
					strcat(buf, "** Excessive rent listing. **\r\n");
					break;
					}
				}
		}
	send_to_char(buf, ch);
	fclose(fl);
	}



int Crash_write_rentcode(Creature ch, FILE * fl, struct rent_info * rent) {
	if (fwrite(rent, sizeof(struct rent_info), 1, fl) < 1) {
		perror("SYSERR: writing rent code");
		return (0);
		}
	return (1);
	}



/*
 * Return values:
 *  0 - successful load, keep char in rent room.
 *  1 - load failure or load of crash items -- put char in temple.
 */
int Crash_load(Creature ch, int dolog) {
	FILE *fl;
	char fname[MAX_STRING_LENGTH];
	struct obj_file_elem object;
	struct rent_info rent;
	int orig_rent_code, num_objs = 0, j;
	/* AutoEQ addition. */
	Object obj, obj2, cont_row[MAX_BAG_ROWS];
	int location;

	/* Empty all of the container lists (you never know ...) */
	for (j = 0; j < MAX_BAG_ROWS; j++)
		cont_row[j] = NULL;

	if (!get_filename(GET_NAME(ch), fname, CRASH_FILE))
		return (1);
	if (!(fl = fopen(fname, "r+b"))) {
		if (errno != ENOENT) {	/* if it fails, NOT because of no file */
			log("SYSERR: READING OBJECT FILE %s (5): %s", fname, strerror(errno));
			send_to_char("\r\n********************* NOTICE *********************\r\n"
						 "There was a problem loading your objects from disk.\r\n"
						 "Contact a God for assistance.\r\n", ch);
			}
		if (dolog) {
			syslog(GET_INVIS_LEV(ch), TRUE, "%s entering game with no equipment.", GET_NAME(ch));
			if (GET_INVIS_LEV(ch) == 0)
				mortlog(ch, "%s has entered the game", PERS(ch, ch, 1));
			}
		return (1);
		}
	if (!feof(fl))
		fread(&rent, sizeof(struct rent_info), 1, fl);
	else {
		log("SYSERR: Crash_load: %s's rent file was empty!", GET_NAME(ch));
		return (1);
		}

	switch (orig_rent_code = rent.rentcode) {
		case RENT_RENTED:
			if (dolog)
				syslog(GET_INVIS_LEV(ch), TRUE, "%s un-renting and entering game.", GET_NAME(ch));
			break;
		case RENT_CRASH:
			if (dolog)
				syslog(GET_INVIS_LEV(ch), TRUE, "%s retrieving crash-saved items and entering game.", GET_NAME(ch));
			break;
		default:
			syslog(GET_INVIS_LEV(ch), TRUE, "SYSERR: %s entering game with undefined rent code %d.", GET_NAME(ch), rent.rentcode);
			break;
		}

	if (dolog && GET_INVIS_LEV(ch) == 0)
		mortlog(ch, "%s has entered the game", PERS(ch, ch, 1));

	while (!feof(fl)) {
		fread(&object, sizeof(struct obj_file_elem), 1, fl);
		if (ferror(fl)) {
			perror("SYSERR: Reading crash file: Crash_load");
			fclose(fl);
			return (1);
			}
		if (feof(fl))
			break;
		++num_objs;
		if ((obj = Obj_from_store(object, &location)) == NULL)
			continue;

		auto_equip(ch, obj, &location);
		/*
		 * What to do with a new loaded item:
		 *
		 * If there's a list with location less than 1 below this, then its
		 * container has disappeared from the file so we put the list back into
		 * the character's inventory. (Equipped items are 0 here.)
		 *
		 * If there's a list of contents with location of 1 below this, then we
		 * check if it is a container:
		 *   - Yes: Get it from the character, fill it, and give it back so we
		 *          have the correct weight.
		 *   -  No: The container is missing so we put everything back into the
		 *          character's inventory.
		 *
		 * For items with negative location, we check if there is already a list
		 * of contents with the same location.  If so, we put it there and if not,
		 * we start a new list.
		 *
		 * Since location for contents is < 0, the list indices are switched to
		 * non-negative.
		 *
		 * This looks ugly, but it works.
		 */
		if (location > 0) {		/* Equipped */
			for (j = MAX_BAG_ROWS - 1; j > 0; j--) {
				if (cont_row[j]) {	/* No container, back to inventory. */
					for (; cont_row[j]; cont_row[j] = obj2) {
						obj2 = cont_row[j]->next_content;
						obj_to_char(cont_row[j], ch);
						}
					cont_row[j] = NULL;
					}
				}
			if (cont_row[0]) {	/* Content list existing. */
				if (GET_OBJ_TYPE(obj) == ITEM_CONTAINER || IS_CORPSE(obj)) {
					/* Remove object, fill it, equip again. */
					obj = unequip_char(ch, location - 1);
					obj->contains = NULL;	/* Should be NULL anyway, but just in case. */
					for (; cont_row[0]; cont_row[0] = obj2) {
						obj2 = cont_row[0]->next_content;
						obj_to_obj(cont_row[0], obj);
						}
					equip_char(ch, obj, location - 1);
					}
				else {			/* Object isn't container, empty the list. */
					for (; cont_row[0]; cont_row[0] = obj2) {
						obj2 = cont_row[0]->next_content;
						obj_to_char(cont_row[0], ch);
						}
					cont_row[0] = NULL;
					}
				}
			}
		else {	/* location <= 0 */
			for (j = MAX_BAG_ROWS - 1; j > -location; j--) {
				if (cont_row[j]) {	/* No container, back to inventory. */
					for (; cont_row[j]; cont_row[j] = obj2) {
						obj2 = cont_row[j]->next_content;
						obj_to_char(cont_row[j], ch);
						}
					cont_row[j] = NULL;
					}
				}
			if (j == -location && cont_row[j]) {	/* Content list exists. */
				if (GET_OBJ_TYPE(obj) == ITEM_CONTAINER || IS_CORPSE(obj)) {
					/* Take the item, fill it, and give it back. */
					obj_from_char(obj);
					obj->contains = NULL;
					for (; cont_row[j]; cont_row[j] = obj2) {
						obj2 = cont_row[j]->next_content;
						obj_to_obj(cont_row[j], obj);
						}
					obj_to_char(obj, ch);	/* Add to inventory first. */
					}
				else {	/* Object isn't container, empty content list. */
					for (; cont_row[j]; cont_row[j] = obj2) {
						obj2 = cont_row[j]->next_content;
						obj_to_char(cont_row[j], ch);
						}
					cont_row[j] = NULL;
					}
				}
			if (location < 0 && location >= -MAX_BAG_ROWS) {
				/*
				 * Let the object be part of the content list but put it at the
				 * list's end.  Thus having the items in the same order as before
				 * the character rented.
				 */
				obj_from_char(obj);
				if ((obj2 = cont_row[-location - 1]) != NULL) {
					while (obj2->next_content)
						obj2 = obj2->next_content;
					obj2->next_content = obj;
					}
				else
					cont_row[-location - 1] = obj;
				}
			}
		}

	/* turn this into a crash file by re-writing the control block */
	rent.rentcode = RENT_CRASH;
	rent.time = time(0);
	rewind(fl);
	Crash_write_rentcode(ch, fl, &rent);

	fclose(fl);

	if (orig_rent_code == RENT_RENTED)
		return (0);
	else
		return (1);
	}


int Crash_save(Object obj, FILE *fp, int location) {
	Object tmp;
	int result;

	if (obj) {
		Crash_save(obj->next_content, fp, location);
		Crash_save(obj->contains, fp, MIN(0, location) - 1);
		result = Obj_to_store(obj, fp, location);

		for (tmp = obj->in_obj; tmp; tmp = tmp->in_obj)
			GET_OBJ_WEIGHT(tmp) -= GET_OBJ_WEIGHT(obj);

		if (!result)
			return (0);
		}
	return (TRUE);
	}


void Crash_restore_weight(Object obj) {
	if (obj) {
		Crash_restore_weight(obj->contains);
		Crash_restore_weight(obj->next_content);
		if (obj->in_obj)
			GET_OBJ_WEIGHT(obj->in_obj) += GET_OBJ_WEIGHT(obj);
		}
	}

/*
 * Get !RENT items from equipment to inventory and
 * extract !RENT out of worn containers.
 */
void Crash_extract_norent_eq(Creature ch) {
	int j;

	for (j = 0; j < NUM_WEARS; j++) {
		if (GET_EQ(ch, j) == NULL)
			continue;

		if (Crash_is_unrentable(GET_EQ(ch, j)))
			obj_to_char(unequip_char(ch, j), ch);
		else
			Crash_extract_norents(GET_EQ(ch, j));
		}
	}

void Crash_extract_objs(Object obj) {
	if (obj) {
		Crash_extract_objs(obj->contains);
		Crash_extract_objs(obj->next_content);
		extract_obj(obj);
		}
	}


int Crash_is_unrentable(Object obj) {
	if (!obj)
		return (0);

	if (GET_OBJ_RNUM(obj) <= NOTHING)
		return (1);

	return (0);
	}


void Crash_extract_norents(Object obj) {
	if (obj) {
		Crash_extract_norents(obj->contains);
		Crash_extract_norents(obj->next_content);
		if (Crash_is_unrentable(obj))
			extract_obj(obj);
		}
	}


void Crash_crashsave(Creature ch) {
	char buf[MAX_INPUT_LENGTH];
	struct rent_info rent;
	int j;
	FILE *fp;

	if (IS_NPC(ch))
		return;

	if (!get_filename(GET_NAME(ch), buf, CRASH_FILE))
		return;
	if (!(fp = fopen(buf, "wb")))
		return;

	rent.rentcode = RENT_CRASH;
	rent.time = time(0);
	if (!Crash_write_rentcode(ch, fp, &rent)) {
		fclose(fp);
		return;
		}

	for (j = 0; j < NUM_WEARS; j++)
		if (GET_EQ(ch, j)) {
			if (!Crash_save(GET_EQ(ch, j), fp, j + 1)) {
				fclose(fp);
				return;
				}
			Crash_restore_weight(GET_EQ(ch, j));
			}

	if (!Crash_save(ch->carrying, fp, 0)) {
		fclose(fp);
		return;
		}
	Crash_restore_weight(ch->carrying);

	fclose(fp);
	}


void Crash_rentsave(Creature ch) {
	char buf[MAX_INPUT_LENGTH];
	struct rent_info rent;
	int j;
	FILE *fp;

	if (IS_NPC(ch))
		return;

	if (!get_filename(GET_NAME(ch), buf, CRASH_FILE))
		return;
	if (!(fp = fopen(buf, "wb")))
		return;

	Crash_extract_norent_eq(ch);
	Crash_extract_norents(ch->carrying);

	rent.rentcode = RENT_RENTED;
	rent.time = time(0);
	if (!Crash_write_rentcode(ch, fp, &rent)) {
		fclose(fp);
		return;
		}
	for (j = 0; j < NUM_WEARS; j++)
		if (GET_EQ(ch, j)) {
			if (!Crash_save(GET_EQ(ch,j), fp, j + 1)) {
				fclose(fp);
				return;
				}
			Crash_restore_weight(GET_EQ(ch, j));
			Crash_extract_objs(GET_EQ(ch, j));
			}
	if (!Crash_save(ch->carrying, fp, 0)) {
		fclose(fp);
		return;
		}
	fclose(fp);

	Crash_extract_objs(ch->carrying);
	}


void Crash_save_all(void) {
	void write_aliases(Creature ch);

	Descr d;

	for (d = descriptor_list; d; d = d->next) {
		if ((STATE(d) == CON_PLAYING) && !IS_NPC(d->character)) {
			Crash_crashsave(d->character);
			write_aliases(d->character);
			SAVE_CHAR(d->character);
			}
		}
	}


/*
 * objpacks are based ENTIRELY on EmpireMUD's standard object saving/loading
 * code, but with a little of my own personal finesse.  Some comments have
 * been removed because I think they're excessive, but I've made an attempt
 * not to remove any credits.  Just in case I do, this paragraph indicates
 * the original authors.  Don't be fooled, though.  This is my own genius
 * work.  - PC 10/13/00
 */

/* Objects in room */
int objpack_save_room(int rnum) {
	struct rent_info rent[1];
	FILE *fp;

	if (!world[rnum].contents)
		return 0;

	sprintf(buf, "%s%d.%s", LIB_OBJPACK, world[rnum].number, SUF_PACK);

	if (!(fp = fopen(buf, "wb")))
		return 0;

	rent[0].rentcode = RENT_CRASH;
	rent[0].time = time(0);

	if (fwrite(rent, sizeof(struct rent_info), 1, fp) < 1) {
		perror("SYSERR: writing rent code");
		fclose(fp);
		return 0;
		}

	if (!Crash_save(world[rnum].contents, fp, 0)) {
		fclose(fp);
		return 0;
		}
	Crash_restore_weight(world[rnum].contents);

	fclose(fp);

	return 1;
	}


void objpack_load_room(int rnum) {
	FILE *fl;
	char fname[MAX_STRING_LENGTH];
	struct obj_file_elem object;
	struct rent_info rent;
	int num_objs = 0, j;
	/* AutoEQ addition. */
	Object obj, obj2, cont_row[MAX_BAG_ROWS];
	int location;

	/* Empty all of the container lists (you never know ...) */
	for (j = 0; j < MAX_BAG_ROWS; j++)
		cont_row[j] = NULL;

	sprintf(fname, "%s%d.%s", LIB_OBJPACK, world[rnum].number, SUF_PACK);

	if (!(fl = fopen(fname, "r+b"))) {
		if (errno != ENOENT)
			log("SYSERR: READING OBJECT FILE %s (5): %s", fname, strerror(errno));
		return;
		}
	if (!feof(fl))
		fread(&rent, sizeof(struct rent_info), 1, fl);
	else {
		log("SYSERR: Crash_load: '%s' rent file was empty!", fname);
		return;
		}

	while (!feof(fl)) {
		fread(&object, sizeof(struct obj_file_elem), 1, fl);
		if (ferror(fl)) {
			perror("SYSERR: Reading crash file: Crash_load");
			fclose(fl);
			return;
			}
		if (feof(fl))
			break;
		++num_objs;
		if ((obj = Obj_from_store(object, &location)) == NULL)
			continue;

		if (location > 0)
			location = LOC_INVENTORY;		/* Not really an inventory, but same idea. */

		obj_to_room(obj, rnum);

		for (j = MAX_BAG_ROWS - 1; j > -location; j--) {
			if (cont_row[j]) {								/* No container, back to room. */
				for (; cont_row[j]; cont_row[j] = obj2) {
					obj2 = cont_row[j]->next_content;
					obj_to_room(cont_row[j], rnum);
					}
				cont_row[j] = NULL;
				}
			}
		if (j == -location && cont_row[j]) {					/* Content list exists. */
			if (GET_OBJ_TYPE(obj) == ITEM_CONTAINER || IS_CORPSE(obj)) {
				/* Take the item, fill it, and give it back. */
				obj_from_room(obj);
				obj->contains = NULL;
				for (; cont_row[j]; cont_row[j] = obj2) {
					obj2 = cont_row[j]->next_content;
					obj_to_obj(cont_row[j], obj);
					}
				obj_to_room(obj, rnum);							/* Add to room first. */
				}
			else {												/* Object isn't container, empty content list. */
				for (; cont_row[j]; cont_row[j] = obj2) {
					obj2 = cont_row[j]->next_content;
					obj_to_room(cont_row[j], rnum);
					}
				cont_row[j] = NULL;
				}
			}
		if (location < 0 && location >= -MAX_BAG_ROWS) {
			obj_from_room(obj);
			if ((obj2 = cont_row[-location - 1]) != NULL) {
				while (obj2->next_content)
					obj2 = obj2->next_content;
				obj2->next_content = obj;
				}
			else
				cont_row[-location - 1] = obj;
			}
		}

	fclose(fl);
	}