Пример #1
0
void frame_set_var(unsigned frame, int var, zword val)
{
  if(var == 0 || var > 0x10) {
    n_show_error(E_STACK, "variable not writable from arbitrary frame", var);
    return;
  }

  if(var > stack_frames[frame].num_locals)
    n_show_error(E_STACK, "writing nonexistant local", var);

  stack_stack[stack_frames[frame].stack_stack_start + (var - 1)] = val;;
}
Пример #2
0
zword frame_get_var(unsigned frame, int var)
{
  if(var == 0 || var > 0x10) {
    n_show_error(E_STACK, "variable not readable from arbitrary frame", var);
    return 0;
  }

  if(var > stack_frames[frame].num_locals)
    n_show_error(E_STACK, "reading nonexistant local", var);

  return stack_stack[stack_frames[frame].stack_stack_start + (var - 1)];
}
Пример #3
0
void mop_call(zword dest, unsigned num_args, zword *args, int result_var)
{
  unsigned i;
  offset destPC;
  unsigned num_local;

  if(dest == 0) {
    check_set_var(result_var, 0);
    return;
  }

  destPC = UNPACKR(dest);

  /*  printf("call %x -> %x\n", oldPC, destPC); */
  
#ifndef FAST
  if(destPC > game_size) {
    n_show_error(E_INSTR, "call past end of story", dest);
    check_set_var(result_var, 0);
    return;
  }
#endif
  
  num_local = HIBYTE(destPC); /* first byte is # of local variables */

#ifndef FAST
  if(num_local > 15) {
    n_show_error(E_INSTR, "call to non-function (initial byte > 15)", dest);
    check_set_var(result_var, 0);
    return;
  }
#endif

  destPC++;     /* Go on past the variable count */

  if(zversion < 5) {
    for(i = num_args; i < num_local; i++)
      args[i] = HIWORD(destPC + i * ZWORD_SIZE);  /* Load default locals */
    destPC += num_local * ZWORD_SIZE;
  } else {
    for(i = num_args; i < num_local; i++)
      args[i] = 0;                                /* locals default to zero */
  }

  add_stack_frame(PC, num_local, args, num_args, result_var);

  PC = destPC;

  /*n_show_debug(E_DEBUG, "function starting", dest);*/
}
Пример #4
0
void op_throw(void)
{
#ifndef FAST
  if(operand[1] > frame_count) {
    n_show_error(E_STACK, "attempting to throw away non-existent frames", operand[1]);
    return;
  }
#endif
  if(operand[1] != 0) {
    frame_count = operand[1];
    mop_func_return(operand[0]);
  } else {
    n_show_error(E_STACK, "attempting to throw away initial frame", operand[0]);
  }
}
Пример #5
0
N_INLINE static void take_branch(zbyte branch)
{
  int o = branch & b00111111;

  if(!(branch & b01000000)) {/* Bit 6 clear means 'branch occupies two bytes'*/
    o = (o << 8) + HIBYTE(PC);
    PC++;
    if(branch & b00100000)
      o = -((1 << 14) - o);
  }

  if(o == 0)
    mop_func_return(0);
  else if(o == 1)
    mop_func_return(1);
  else
    PC += o - 2;

#ifndef FAST
  if(PC > game_size) {
    n_show_error(E_INSTR, "attempt to conditionally jump outside of story", o - 2);
    PC -= o - 2;
    return;
  }
#endif
  
  /*  printf("cjmp %x -> %x\n", oldPC, PC); */
}
Пример #6
0
/* Returns a direction it wants you to explore in.  Take the direction and
   call automap_unexplore, which'll take you back to the @read.
   If it returns NULL, we're finished; get player input */
const char *automap_explore(void)
{
  if(automap_explored) {
    n_show_error(E_SAVE, "tried to explore when we just did so", automap_explored);
    return NULL;
  }

  if(!loc_exp)
    return NULL;

  if(automap_dir == NUM_EXITS) {
    fast_saveundo();
    automap_location = evaluate_expression(loc_exp, stack_get_depth()).v;    
    automap_dir = 0;
  } else {
    automap_dir++;
    if(automap_dir == NUM_EXITS) {
      loc_node *r = room_find(automap_location, TRUE);
      r->found = TRUE;
      automap_calc_exits(r, 0);
      allow_saveundo = TRUE;
      allow_output = TRUE;
      return NULL;
    }    
  }

  allow_saveundo = FALSE;
  allow_output = FALSE;
  automap_explored = TRUE;

  return dirways[automap_dir].name;
}
Пример #7
0
void z_tokenise(const char *text, int length, zword parse_dest,
		zword dictionarytable, BOOL write_unrecognized)
{
  zword separatortable;
  zword numparsedloc;
  int numseparators;
  int maxwords, parsed_words;

  if(parse_dest > dynamic_size || parse_dest < 64) {
    n_show_error(E_OUTPUT, "parse table in invalid location", parse_dest);
    return;
  }
  
  numseparators = LOBYTE(dictionarytable);
  separatortable = dictionarytable + 1;
  dictionarytable += numseparators + 1;

  maxwords = LOBYTE(parse_dest);
  numparsedloc = parse_dest + 1;
  parse_dest+=2;

  if(maxwords == 0)
    n_show_warn(E_OUTPUT, "small parse size", maxwords);

  parsed_words = tokenise(dictionarytable, text, length,
			  &parse_dest, maxwords, separatortable, numseparators,
			  write_unrecognized);
  
  LOBYTEwrite(numparsedloc, parsed_words);
}
Пример #8
0
void op_save5(void)
{
  unsigned i;
  char filename[256];
  unsigned length;
  strid_t file = NULL;
  offset end;
  switch(numoperands) {
  case 0:
    op_save4();
    return;
  case 1: 
    n_show_error(E_INSTR, "call save with bad number of operands", numoperands);
    mop_store_result(0);
    return;
  case 2:
    file = n_file_prompt(fileusage_Data | fileusage_BinaryMode,
			 filemode_Write);
    break;
  default:
    length = LOBYTE(operand[2]);
    if(length > 13)
      n_show_port(E_INSTR, "save with filename > 13 characters", length);
    for(i = 0; i < length; i++)
      filename[i] = glk_char_to_upper(LOBYTE(operand[2] + 1 + i));
    filename[length] = 0;
    file = n_file_name(fileusage_Data | fileusage_BinaryMode,
		       filemode_Write, filename);
    break;
  }
  if(!file) {
    mop_store_result(0);
    return;
  }
  end = ((offset) operand[0]) + operand[1];
  if(end > 65535 || end > total_size) {
    n_show_error(E_MEMORY, "attempt to save data out of range", end);
    mop_store_result(0);
    return;
  }

  w_glk_put_buffer_stream(file, (char *) z_memory + operand[0], operand[1]);
  glk_stream_close(file, NULL);

  mop_store_result(1);
}
Пример #9
0
static zword stack_pop(void)
{
#ifndef FAST
  if(stack_pointer <= stack_min) {
    n_show_error(E_STACK, "underflow - excessive popping", stack_pointer);
    return 0;
  }
#endif
  return stack_stack[--stack_pointer];
}
Пример #10
0
static automap_path *automap_find_path(loc_node *location, loc_node *dest,
				       BOOL by_walking)
{
  automap_path *path = NULL;
  automap_path *rev;
  automap_path newnode;
  loc_node *p;

  /* Find the distances of all nodes from dest */
  n_hash_enumerate(&rooms, make_distant);
  automap_calc_distances(dest, 0, by_walking);

  /* If dest isn't reachable, location's distance will still be infinite */
  if(location->dist == INFINITY)
    return NULL;

  /* At each step, go toward a nearer node 'till we're there */
  p = location;
  while(p != dest) {
    unsigned i;
    unsigned best_dir;
    glui32 best_dist = INFINITY;
    loc_node *best_node = NULL;
    unsigned maxdir = by_walking ? NUM_EXITS : NUM_DIRS;
    for(i = 0; i < maxdir; i++) {
      loc_node *thisdest;
      if(by_walking)
	thisdest = p->exits[i];
      else
	thisdest = automap_edge_follow(p, i);
      
      if(thisdest && thisdest->dist < best_dist) {
	best_dir = i;
	best_dist = thisdest->dist;
	best_node = thisdest;
      }
    }
    if(!best_node) {
      n_show_error(E_SYSTEM, "couldn't find path there", 0);
      return NULL;
    }
    newnode.loc = p;
    newnode.dir = best_dir;
    LEadd(path, newnode);
    p = best_node;
  }

  rev = NULL;
  while(path) {
    LEadd(rev, *path);
    LEremove(path);
  }

  return rev;
}
Пример #11
0
static loc_node *automap_edge_follow(loc_node *location, int dir)
{
  if(location->outgoing[dir] == NULL)
    return NULL;
  
  if(location->outgoing[dir]->dest[0] == location)
    return location->outgoing[dir]->dest[1];
  if(location->outgoing[dir]->dest[1] == location)
    return location->outgoing[dir]->dest[0];
  
  n_show_error(E_SYSTEM, "edge isn't connected to what it should be", 0);
  return NULL;
}
Пример #12
0
void remove_stack_frame(void)
{
#ifndef FAST
  if(frame_count == 0) {
    n_show_error(E_STACK, "attempt to remove initial stack frame", 0);
    return;
  }
#endif
  stack_pointer = stack_frames[frame_count].stack_stack_start;
  frame_count--;
  stack_min = stack_frames[frame_count].stack_stack_start +
              stack_frames[frame_count].num_locals;
  local_vars = stack_stack + stack_frames[frame_count].stack_stack_start;
}
Пример #13
0
N_INLINE zword get_var(int var)
{
  if(var < 0x10) {
    if(var != 0) {
#ifndef FAST
      if(var > stack_frames[frame_count].num_locals)
	n_show_error(E_INSTR, "reading nonexistant local", var);
#endif
      return local_vars[var - 1];
    }
    return stack_pop();
  }
  return LOWORD(z_globaltable + (var - 0x10) * ZWORD_SIZE);
}
Пример #14
0
N_INLINE void set_var(int var, zword val)
{
  if(var < 0x10) {
    if(var != 0) {
#ifndef FAST
      if(var > stack_frames[frame_count].num_locals)
	n_show_error(E_INSTR, "setting nonexistant local", var);
#endif
      local_vars[var - 1] = val;
    } else {
      stack_push(val);
    }
  } else {
    LOWORDwrite(z_globaltable + (var - 0x10) * ZWORD_SIZE, val);
  }
}
Пример #15
0
/* Just like restoreundo, but the opposite ;) 
   The idea is to go to the @save_undo location, but return 0 instead of 2
   so the game thinks it just successfully saved the game. For games which
   don't contain @save_undo, jumps to right after the @read instruction. */
BOOL restoreredo(void)
{
  strid_t stack;
  int i;
  glui32 wid, hei;
  stream_result_t poo;
  move_difference *p = movelist;

  if(!p || move_index <= 0)
    return FALSE;
  
  move_index--;
  for(i = 0; i < move_index; i++) {
    p=p->next;
    if(!p)
      return FALSE;
  }

  quetzal_undiff(prevstate, dynamic_size, p->delta, p->deltalength, TRUE);
  
  n_memcpy(z_memory, prevstate, dynamic_size);

  stack = glk_stream_open_memory((char *) p->stackchunk, p->stacklength,
				 filemode_Read, 0);

  quetzal_stack_restore(stack, p->stacklength);
  glk_stream_close(stack, &poo);

  if(poo.readcount != p->stacklength) {
    n_show_error(E_SAVE, "incorrect stack size assessment", poo.readcount);
    return FALSE;
  }

  if(p->PC_in_instruction) {
    PC = p->PC;
    mop_store_result(0);
    false_undo = FALSE;
  } else {
    PC = p->PC;
    false_undo = FALSE;
  }
  has_done_save_undo = TRUE;

  z_find_size(&wid, &hei);
  set_header(wid, hei);
  return TRUE;
}
Пример #16
0
/* Perform various sanity checks on the stack to make sure all is well in
   Denmark. */
BOOL verify_stack(void)
{
  zword f;
  if(frame_count > frame_max) {
    n_show_error(E_STACK, "more frames than max", frame_count);
    return FALSE;
  }
  if(!stack_frames) {
    n_show_error(E_STACK, "no frames", 0);
    return FALSE;
  }
  for(f = 0; f < frame_count; f++) {
    if(stack_frames[f].stack_stack_start > stack_pointer) {
      n_show_error(E_STACK, "stack start after end", f);
      return FALSE;
    }
    if(stack_frames[f].return_PC > total_size) {
      n_show_error(E_STACK, "PC after end of game", f);
      return FALSE;
    }
    if(stack_frames[f].num_locals > 15) {
      n_show_error(E_STACK, "too many locals", f);
      return FALSE;
    }
    if(stack_frames[f].arguments > 7) {
      n_show_error(E_STACK, "too many arguments", f);
      return FALSE;
    }
    if(stack_frames[f].result_variable > 255) {
      n_show_error(E_STACK, "result variable too big", f);
      return FALSE;
    }
    if(stack_frames[f].result_variable < -2) {
      n_show_error(E_STACK, "unknown magic result variable", f);
      return FALSE;
    }
  }
  return TRUE;
}
Пример #17
0
/* Returns TRUE if it improved any */
static BOOL automap_increase_along_path(automap_path *path, int oldcount,
					loc_node *center, int effort)
{
  automap_path *p;
  int exploring;
  int explore_max = effort > 1;
  if(!effort)
    return FALSE;

  /* Takes two passes at trying to improve the situation.
     The first time (!exploring), it tries increasing the length of each
     edge along the path, observing the results and then undoing the increase.
     If it was able to improve anything, it returns with the best improvement.
     Otherwise it tries increasing the length of each edge and calling itself;
     If its child is able to improve things, then it returns with both
     lengthenings in effect. */
  
  for(exploring = 0; exploring <= explore_max; exploring++) {
    edge *best_edge = NULL;
    int best_count = oldcount;
    int smallest_new = 10000;
    
    for(p = path; p; p=p->next) {
      int newcount;
      edge *e = automap_get_edge(p->loc, p->dir);
      int old_min_length = e->min_length;
      int old_guess_length = e->guess_length;

      if(p->next && p->next->loc != automap_edge_follow(p->loc, p->dir))
	  n_show_error(E_SYSTEM, "path doesn't follow itself", 0);

      e->guess_length += 2;
      e->min_length = e->guess_length;
      
      if(!exploring) {
	newcount = automap_find_and_count_interference(center);
	if(newcount < best_count
	   || (newcount == best_count && newcount < oldcount
	       && e->min_length < smallest_new)) {
	  best_edge = e;
	  best_count = newcount;
	  smallest_new = e->min_length;
	}
      } else {
	if(automap_increase_along_path(p, oldcount, center, effort-1))
	  return TRUE;
      }
    
      e->min_length   = old_min_length;
      e->guess_length = old_guess_length;
    }

    if(!exploring && best_edge) {
      best_edge->guess_length += 2;
      best_edge->min_length = best_edge->guess_length;
      automap_find_and_count_interference(center);
      return TRUE;
    }
  }
  return FALSE;
}
Пример #18
0
void decode(void)
{
  unsigned optypes;
  int maxoperands;

  while(!exit_decoder) {

#ifdef ANDGLK
	extern int do_autosave;
	extern char AUTOSAVE_FILE[];
	extern offset stack_pointer;
	extern zword *local_vars;
	extern zword frame_count;
	offset save_pcp = PC;
	offset save_oldpcp = oldPC;
	offset save_sp = stack_pointer;
	zword* save_fp = local_vars;
	zword save_optypes = optypes;
	zword save_maxoperands = maxoperands;
	int save_frame_count = frame_count;
	zword save_zargs[8] = { operand[0], operand[1], operand[2], operand[3], 
							operand[4], operand[5], operand[6], operand[7] };
	int zarg_ix, save_zargc = 0; //zargc;
#endif

    zbyte opcode = HIBYTE(PC);
	//LOGD("state: PC:%x op:%x", (glui32)PC, (glui32)opcode);

    oldPC = PC;

#ifdef DEBUGGING
    if(do_check_watches)
      check_watches();
#endif

    PC++;

#ifndef NO_TICK
    glk_tick();  /* tick tock hickery dock the mouse ran up the clock */
#endif
    
    /* Top bits decide opcode/operand encoding */
    switch(opcode >> 4) {
    case 0: case 1:                         /* long 2OP */
      operand[0] = HIBYTE(PC);              /* small constant */
      operand[1] = HIBYTE(PC+1);            /* small constant */
      numoperands = 2; PC += 2;
      opcode += OFFSET_2OP - 0x00;
      break;


    case 2: case 3:                         /* long 2OP */
      operand[0] = HIBYTE(PC);              /* small constant */
      operand[1] = get_var(HIBYTE(PC+1));   /* variable */
      numoperands = 2; PC += 2;
      opcode += OFFSET_2OP - 0x20;
      break;


    case 4: case 5:                         /* long 2OP */
      operand[0] = get_var(HIBYTE(PC));     /* variable */
      operand[1] = HIBYTE(PC+1);            /* small constant */
      numoperands = 2; PC += 2;
      opcode += OFFSET_2OP - 0x40;
      break;


    case 6: case 7:                         /* long 2OP */
      operand[0] = get_var(HIBYTE(PC));     /* variable */
      operand[1] = get_var(HIBYTE(PC+1));   /* variable */
      numoperands = 2; PC += 2;
      opcode += OFFSET_2OP - 0x60;
      break;


    case 8:                                 /* short 1OP */
      operand[0] = HIWORD(PC);              /* large constant */
      numoperands = 1; PC += ZWORD_SIZE;
      opcode += OFFSET_1OP - 0x80;
      break;


    case 9:                                 /* short 1OP */
      operand[0] = HIBYTE(PC);              /* small constant */
      numoperands = 1; PC += 1;
      opcode += OFFSET_1OP - 0x90;
      break;


    case 10:                                /* short 1OP */
      operand[0] = get_var(HIBYTE(PC));     /* variable */
      numoperands = 1; PC += 1;
      opcode += OFFSET_1OP - 0xa0;
      break;


    case 11:                                /* short 0OP */
      if(opcode != 0xbe) {
	numoperands = 0;
	opcode += OFFSET_0OP - 0xb0;
	break;
      }
                                            /* EXTENDED */
      opcode  = HIBYTE(PC);   /* Get the extended opcode */
      optypes = HIBYTE(PC+1);
      PC += 2;

#ifndef FAST
      if(OFFSET_EXT + opcode > OFFSET_END) {
	n_show_error(E_INSTR, "unknown extended opcode", opcode);
	break;
      }
#endif
      
      for(numoperands = 0; numoperands < 4; numoperands++) {
	switch(optypes & (3 << 6)) {   /* Look at the highest two bits. */
	case 0 << 6:
	  operand[numoperands] = HIWORD(PC); PC+=ZWORD_SIZE; break;
	case 1 << 6:
	  operand[numoperands] = HIBYTE(PC); PC++; break;
	case 2 << 6:
	  operand[numoperands] = get_var(HIBYTE(PC)); PC++; break;
	default:
	  goto END_OF_EXTENDED;   /* inky says, "use the goto." */
	}
	optypes <<= 2;    /* Move the next two bits into position. */
      }
    END_OF_EXTENDED:
      opcode += OFFSET_EXT;
      break;

    case 12: case 13: case 14: case 15:     /* variable operand count */
      maxoperands = 4;
      optypes = ((unsigned) HIBYTE(PC)) << 8; /* Shift left so our loop will */
                                    /* be the same for both 4 and 8 operands */
      PC++;
  
      if(opcode == 0xec || opcode == 0xfa) {  /* call_vs2 and call_vn2 */
	maxoperands = 8;
	optypes |= HIBYTE(PC);  /* Fill the bottom 8 bits */
	PC++;
      }
      
      for(numoperands = 0; numoperands < maxoperands; numoperands++) {
	switch(optypes & (3 << 14)) {   /* Look at the highest two bits. */
	case 0 << 14:
	  operand[numoperands] = HIWORD(PC); PC+=ZWORD_SIZE; break;
	case 1 << 14:
	  operand[numoperands] = HIBYTE(PC); PC++; break;
	case 2 << 14:
	  operand[numoperands] = get_var(HIBYTE(PC)); PC++; break;
	default:
	  goto END_OF_VARIABLE;
	}
	optypes <<= 2;    /* Move the next two bits into position. */
      }
    END_OF_VARIABLE:
      opcode += OFFSET_2OP - 0xc0;
      break;
    }
    opcodetable[opcode]();

#ifdef ANDGLK
	if (do_autosave) {

	    PC = save_pcp;
	    oldPC = save_oldpcp;
	    stack_pointer = save_sp;
	    local_vars = save_fp;
	    numoperands = save_zargc;
	    frame_count = save_frame_count;
	    for (zarg_ix=0; zarg_ix<8; ++zarg_ix)
			operand[zarg_ix] = save_zargs[zarg_ix];
		optypes = save_optypes;
		maxoperands = save_maxoperands;

		savegame();

		do_autosave = 0;
		AUTOSAVE_FILE[0] = '\0';
	    continue;
	}       
#endif


  }
}
Пример #19
0
BOOL quetzal_stack_save(strid_t stream)
{
  unsigned frame_num = 0;

  if(zversion == 6)
    frame_num++;

  if(!verify_stack()) {
    n_show_error(E_SAVE, "stack did not pass verification before saving", 0);
    return FALSE;
  }

  /* We have to look one ahead to see how much stack space a frame uses; when
     we get to the last frame, there will be no next frame, so this won't work
     if there wasn't a frame there earlier with the correct info.  Add and
     remove a frame to make things happy */
  add_stack_frame(0, 0, NULL, 0, 0);
  remove_stack_frame();

  for(; frame_num <= frame_count; frame_num++) {
    unsigned n;
    int num_locals;
    unsigned stack_size;

    glui32 qframe[5];

    const unsigned char argarray[8] = {
      b00000000, b00000001, b00000011, b00000111,
      b00001111, b00011111, b00111111, b01111111
    };

    qframe[qreturnPC] = stack_frames[frame_num].return_PC;

    qframe[qvar]      = stack_frames[frame_num].result_variable;

    num_locals        = stack_frames[frame_num].num_locals;

    if(num_locals > 15) {
      n_show_error(E_SAVE, "num_locals too big", num_locals);
      return FALSE;
    }

    qframe[qflags] = num_locals;

    if(stack_frames[frame_num].result_variable == -1) {
      qframe[qflags] |= b00010000;
      qframe[qvar] = 0;
    }

    if(stack_frames[frame_num].arguments > 7) {
      n_show_error(E_SAVE, "too many function arguments", stack_frames[frame_num].arguments);
      return FALSE;
    }
    
    qframe[qargs] = argarray[stack_frames[frame_num].arguments];

    stack_size = (stack_frames[frame_num+1].stack_stack_start -
		  stack_frames[frame_num].stack_stack_start -
		  num_locals);

    qframe[qeval] = stack_size;
	             
    if(frame_num == 0) {
      qframe[qreturnPC] = 0;
      qframe[qflags] = 0;
      qframe[qvar] = 0;
      qframe[qargs] = 0;
    }

    emptystruct(stream, qstackframe, qframe);
    
    for(n = 0; n < num_locals + stack_size; n++) {
      unsigned char v[ZWORD_SIZE];
      zword z = stack_stack[stack_frames[frame_num].stack_stack_start + n];
      MSBencodeZ(v, z);
      w_glk_put_buffer_stream(stream, (char *) v, ZWORD_SIZE);
    }
  }
  return TRUE;
}
Пример #20
0
BOOL quetzal_stack_restore(strid_t stream, glui32 qsize)
{
  glui32 i = 0;
  int num_frames = 0;

  kill_stack();
  init_stack(1024, 128);
  
  while(i < qsize) {
    unsigned n;
    unsigned num_locals;
    zword locals[16];
    int num_args;
    int var;

    glui32 qframe[5];
    i += fillstruct(stream, qstackframe, qframe, NULL);

    if(qframe[qreturnPC] > total_size) {
      n_show_error(E_SAVE, "function return PC past end of memory",
		 qframe[qreturnPC]);
      return FALSE;
    }

    if((qframe[qflags] & b11100000) != 0) {
      n_show_error(E_SAVE, "expected top bits of flag to be zero", qframe[qflags]);
      return FALSE;
    }
    
    var = qframe[qvar];
    if(qframe[qflags] & b00010000)  /* from a call_n */
      var = -1;
    
    num_locals = qframe[qflags] & b00001111;

    if(num_locals > 15) {
      n_show_error(E_SAVE, "too many locals", num_locals);
      return FALSE;
    }
    
    num_args = 0;
    switch(qframe[qargs]) {
    default:
      n_show_error(E_SAVE, "invalid argument count", qframe[qargs]);
      return FALSE;
    case b01111111: num_args++;
    case b00111111: num_args++;
    case b00011111: num_args++;
    case b00001111: num_args++;
    case b00000111: num_args++;
    case b00000011: num_args++;
    case b00000001: num_args++;
    case b00000000: ;
    }
    
    for(n = 0; n < num_locals; n++) {
      unsigned char v[ZWORD_SIZE];
      glk_get_buffer_stream(stream, (char *) v, ZWORD_SIZE);
      locals[n] = MSBdecodeZ(v);
      i+=ZWORD_SIZE;
    }
    
    if(zversion != 6 && num_frames == 0)
      ;               /* dummy stack frame; don't really use it */
    else
      add_stack_frame(qframe[qreturnPC],
		      num_locals, locals,
		      num_args, var);
    
    for(n = 0; n < qframe[qeval]; n++) {
      unsigned char v[ZWORD_SIZE];
      glk_get_buffer_stream(stream, (char *) v, ZWORD_SIZE);
      stack_push(MSBdecodeZ(v));
      i += ZWORD_SIZE;
    }
    
    num_frames++;
  }
  if(!verify_stack()) {
    n_show_error(E_SAVE, "restored stack fails verification", 0);
    return FALSE;
  }
  return TRUE;
}
Пример #21
0
BOOL saveundo(BOOL in_instruction)
{
  move_difference newdiff;
  strid_t stack;
  stream_result_t poo;

  if(!allow_saveundo)
    return TRUE;

  /* In games which provide @save_undo, we will have already issued a faked
     saveundo before the first @save_undo hits, since there hadn't been any
     @save_undo before the first read line.  So when this happens, wipe the
     fake saveundo in favor of the real one */
  if(in_instruction && movelist && !movelist->next
     && !movelist->PC_in_instruction)
    init_undo();

    
  if(!quetzal_diff(z_memory, prevstate, dynamic_size, &newdiff.delta,
		   &newdiff.deltalength, TRUE))
    return FALSE;

#ifdef PARANOID
  {
    char *newmem = (char *) n_malloc(dynamic_size);
    n_memcpy(newmem, prevstate, dynamic_size);
    quetzal_undiff(newmem, dynamic_size, newdiff.delta,
		   newdiff.deltalength, TRUE);
    if(n_memcmp(z_memory, newmem, dynamic_size)) {
      n_show_error(E_SAVE, "save doesn't match itself", 0);
    }
    n_free(newmem);
  }
#endif
  
  newdiff.PC = PC;
  newdiff.oldPC = oldPC;
  
  newdiff.PC_in_instruction = in_instruction;
  newdiff.stacklength = get_quetzal_stack_size();
  newdiff.stackchunk = (zbyte *) n_malloc(newdiff.stacklength);
  stack = glk_stream_open_memory((char *) newdiff.stackchunk,
				 newdiff.stacklength, filemode_Write, 0);
  if(!stack) {
    n_free(newdiff.delta);
    n_free(newdiff.stackchunk);
    return FALSE;
  }
  if(!quetzal_stack_save(stack)) {
    glk_stream_close(stack, NULL);
    n_free(newdiff.delta);
    n_free(newdiff.stackchunk);
    return FALSE;
  }
  glk_stream_close(stack, &poo);
  if(poo.writecount != newdiff.stacklength) {
    n_show_error(E_SAVE, "incorrect stack size assessment", poo.writecount);
    n_free(newdiff.delta);
    n_free(newdiff.stackchunk);
    return FALSE;
  }

  while(move_index-- > 0) {
    n_free(movelist->delta);
    n_free(movelist->stackchunk);
    LEremove(movelist);
  }
  LEadd(movelist, newdiff);
  move_index++;
  n_memcpy(prevstate, z_memory, dynamic_size);

  has_done_save_undo = TRUE;
  return TRUE;
}