#include "os.h"
#include "interpret.h"
#include "object.h"
#include "lnode.h"
#include "sent.h"
#include "config.h"
/* main.c */
extern int d_flag;
extern void *xalloc(int size);
extern char *string_copy(char *str);
extern void debug_message(char *a, ...);
extern char *escape_path (char *str);
/* comm1.c */
extern void add_message(char *fmt, ...);
/* simulate.c */
extern struct value *find_value(struct lnode_variable *p);
extern void fatal(char *fmt, ...);
extern void error(char *fmt, ...);
extern void free_sentence(struct sentence *p);
extern struct lnode_var_def *find_status(char *str, int must_find);
/* interpret.c */
extern int count_value_ref(struct object *ob);
extern struct value *apply(char *fun, struct object *ob, struct value *arg);
/* swap.c */
extern void remove_swap_file(struct object *ob);
int tot_alloc_object;
/* object.c */
void save_object(struct object *ob, char *file);
int restore_object(struct object *ob, char *file);
struct object *find_living_object(char *name);
struct object *find_player(char *name);
void tell_npc(struct object *ob, char *str);
void tell_object(struct object *ob, char *str);
void free_object(struct object *ob, char *from);
void add_ref(struct object *ob, char *from);
struct object *get_empty_object(void);
void dump_all_objects(void);
static void verify(char *str, char *where);
static void replace_newline (char *str);
static void restore_newline (char *str);
/*
* Replace newlines in a string with a carriage return, to make the string
* writeable on one line.
*/
static void replace_newline (char *str)
{
for (; *str; str++) {
if (str[0] == '\n')
str[0] = '\r';
}
}
/*
* Replace carriage return in a string with newlines.
*/
static void restore_newline (char *str)
{
for (; *str; str++) {
if (str[0] == '\r')
str[0] = '\n';
}
}
void save_object (struct object *ob, char *file)
{
char *name;
int len;
FILE *f;
struct lnode_var_def *p;
if (strchr (file, '.') || file[0] == '/' || file[0] == '\\' || file[0] == ' ')
error ("Illegal file name to save_object(%s).\n", file);
if (strncmp (current_object->name, "obj/", 4) != 0 &&
strncmp (current_object->name, "room/", 5) != 0)
error ("Illegal use of save_object()\n");
len = strlen (file);
name = xalloc (len + 3);
(void) strcpy (name, file);
(void) strcat (name, ".o");
escape_path (name);
f = fopen (name, "w");
if (f == 0) {
error ("Could not open %s for a save.\n", name);
#ifdef DO_ABORT
WIN32CLEANUP
abort ();
#else
return;
#endif
}
for (p = ob->status; p; p = p->next) {
struct value *v = &ob->variables[p->num_var];
char *new_string;
if (v->type == T_NUMBER) {
(void) fprintf (f, "%s %d\n", p->name, v->u.number);
} else if (v->type == T_STRING) {
new_string = string_copy (v->u.string);
replace_newline (new_string);
(void) fprintf (f, "%s \"%s\"\n", p->name, new_string);
free (new_string);
}
}
free (name);
(void) fclose (f);
}
int restore_object (struct object *ob, char *file)
{
char *name, var[100], *val, *buff, *space;
int len;
FILE *f;
struct lnode_var_def *p;
struct object *save = current_object;
struct stat st;
if (strchr (file, '.') || file[0] == '/' || file[0] == '\\' || file[0] == ' ')
error ("Illegal file name to restore_object(%s).\n", file);
len = strlen (file);
name = xalloc (len + 3);
(void) strcpy (name, file);
if (name[len - 2] == '.' && name[len - 1] == 'c')
name[len - 1] = 'o';
else
(void) strcat (name, ".o");
escape_path (name);
f = fopen (name, "r");
if (f == 0)
return 0;
if (fstat (fileno (f), &st) == -1) {
perror (name);
return 0;
}
if (st.st_size == 0)
return 0;
val = xalloc (st.st_size + 1);
buff = xalloc (st.st_size + 1);
current_object = ob;
while (1) {
struct value *v;
if (fgets (buff, st.st_size, f) == 0)
break;
/* Remember that we have a newline at end of buff ! */
space = strchr (buff, ' ');
if (space == 0)
fatal ("Illegal format when restore %s.\n", name);
(void) strncpy (var, buff, space - buff);
var[space - buff] = '\0';
(void) strcpy (val, space + 1);
p = find_status (var, 0);
if (p == 0)
continue;
v = &ob->variables[p->num_var];
if (val[0] == '"') {
v->type = T_STRING;
v->u.string = xalloc (strlen (val) - 2);
(void) strncpy (v->u.string, val + 1, strlen (val) - 3);
v->u.string[strlen (val) - 3] = '\0';
restore_newline (v->u.string);
continue;
}
v->type = T_NUMBER;
v->u.number = atoi (val);
}
current_object = save;
if (d_flag)
debug_message ("Object %s restored from %s.\n", ob->name, name);
free (name);
free (buff);
free (val);
(void) fclose (f);
return 1;
}
/*
* Search for a living object which answers to the id NAME.
* If there are many living objects, this algorithm can become quite
* heavy. It should be hashed in the future !
*/
struct object *find_living_object (char *name)
{
struct object *ob;
struct value *ret;
struct value thing;
for (ob = obj_list; ob; ob = ob->next_all) {
if (!ob->enable_commands || ob->super == 0)
continue;
thing.type = T_STRING, thing.u.string = name;
ret = apply ("id", ob, &thing);
if (ret == 0 || (ret->type == T_NUMBER && ret->u.number == 0))
continue;
return ob;
}
return 0;
}
/*
* Search for a player object which answers to the id NAME.
* Like find_living_object(), this should really be hashed.
*/
struct object *find_player (char *name)
{
struct object *ob;
struct value *ret;
struct value thing;
for (ob = obj_list; ob; ob = ob->next_all) {
if (!ob->enable_commands || ob->super == 0)
continue;
thing.type = T_STRING, thing.u.string = name;
/* Is this a player? */
if (apply ("is_player", ob, &const0))
/* Yep... but is it the right one? */
ret = apply ("id", ob, &thing);
else
/* Nope... try again. */
continue;
if (ret == 0 || (ret->type == T_NUMBER && ret->u.number == 0))
continue;
return ob;
}
return 0;
}
void tell_npc (struct object *ob, char *str)
{
struct value thing;
thing.type = T_STRING;
thing.u.string = str;
(void) apply ("catch_tell", ob, &thing);
}
/*
* Send a message to an object.
* If it is an interactive object, it will go to his
* screen. Otherwise, it will go to a local function
* catch_tell() in that object. This enables communications
* between players and NPC's, and between other NPC's.
*/
void tell_object (struct object *ob, char *str)
{
struct object *save_command_giver;
if (ob->interactive) {
save_command_giver = command_giver;
command_giver = ob;
add_message ("%s", str);
command_giver = save_command_giver;
return;
}
tell_npc (ob, str);
}
void free_object (struct object *ob, char *from)
{
struct sentence *s;
ob->ref--;
if (d_flag)
debug_message ("Subtr ref to ob %s: %d (%s)\n", ob->name, ob->ref, from);
if (ob->ref > 0)
return;
if (!ob->destructed) {
/*
* This should never happen, but it does. I had to do this
* kludge, or the game would crash.
* When this bug is found, fatal() should be called if this would
* happen again.
*/
debug_message ("Remove a not destructed object.\n");
fprintf (stderr, "Remove a not destructed object.\n");
ob->ref++;
return;
}
ob->reset = 0;
if (ob->interactive)
fatal ("Tried to free an interactive object.\n");
/*
* If the program is freed, then we can also free the variable
* deklarations.
*/
if (ob->prog) {
if (free_prog ((struct lnode_def *) (ob->prog))) {
free_sub_part ((struct lnode *) ob->status, 1);
ob->status = 0;
}
}
ob->prog = 0;
if (ob->swap_num)
remove_swap_file (ob);
for (s = ob->sent; s;) {
struct sentence *next;
next = s->next;
free_sentence (s);
s = next;
}
if (ob->name) {
if (d_flag)
debug_message ("Free object %s\n", ob->name);
free (ob->name);
ob->name = 0;
}
if (ob->variables) {
free (ob->variables);
ob->variables = 0;
}
tot_alloc_object--;
free (ob);
}
void add_ref (struct object *ob, char *from)
{
ob->ref++;
if (d_flag)
debug_message ("Add reference to object %s: %d (%s)\n", ob->name,
ob->ref, from);
}
struct object *get_empty_object (void)
{
struct object *ob;
tot_alloc_object++;
ob = (struct object *) xalloc (sizeof (struct object));
ob->ref = 1;
ob->swapped = 0;
ob->not_touched = 0;
ob->swap_num = 0;
ob->ed_buffer = 0;
ob->next_inv = 0;
ob->contains = 0;
ob->reset = 0;
ob->interactive = 0;
ob->enable_commands = 0;
ob->total_light = 0;
ob->sent = 0;
ob->cloned = 0;
ob->variables = 0;
ob->super = 0;
ob->enable_heart_beat = 0;
ob->wl = 0;
ob->destructed = 0;
return ob;
}
void dump_all_objects (void)
{
struct object *ob;
FILE *d;
d = fopen ("OBJ_DUMP", "w");
if (!d) {
add_message ("Couldn't open dump file.\n");
return;
}
add_message ("Dumping data to 'OBJ_DUMP'... ");
for (ob = obj_list; ob; ob = ob->next_all) {
if (!ob->name)
fprintf (d, "<NULL> (0x%x) ", (unsigned int) ob);
else
fprintf (d, "%s (0x%x) ", ob->name, (unsigned int) ob);
if (ob->super) {
if (ob->super->name)
fprintf (d, "Super %s (0x%x)\n", ob->super->name, (unsigned int) ob->super);
else
fprintf (d, "Super <NULL> (0x%x)\n", (unsigned int) ob->super);
} else
fprintf (d, "\n");
fprintf (d,
"\tRef %2d Rst %d Enbl_cmd %d Clnd %d Hrt_bt %d lang ref %d swp %d\n",
ob->ref, ob->reset, ob->enable_commands, ob->cloned,
// ob->enable_heart_beat, (struct lnode_def *) (ob->prog)->num_ref,
ob->enable_heart_beat, ob->prog->num_ref,
ob->swapped);
}
fclose (d);
add_message ("DONE\n");
}
/*
* This one is used for extreme emergency.
*/
static void verify (char *str, char *where)
{
static struct object *harry;
struct object *ob;
int num_ref, i;
if (!harry)
harry = find_living_object (str);
if (!harry)
return;
if (harry->ref == 0) {
debug_message ("%20s verify %s ref 0\n", where, str);
harry = 0;
}
num_ref = 0;
for (ob = obj_list; ob; ob = ob->next_all) {
if (ob->num_variables == 0)
continue;
for (i = 0; i < ob->num_variables; i++) {
if (ob->variables[i].type == T_OBJECT && ob->variables[i].u.ob == harry) {
num_ref++;
debug_message ("var in '%s'\n", ob->name);
}
}
}
i = count_value_ref (harry);
debug_message ("%20s: num ref %2d : %2d+%2d = %2d\n", where, harry->ref,
num_ref, i, i + num_ref);
}