Ejemplo n.º 1
0
struct board *load_board_allocate(FILE *fp, int savegame,
 int file_version, int world_version)
{
  struct board *cur_board = cmalloc(sizeof(struct board));
  int board_size, board_location, last_location;
  enum val_result result;

  board_size = fgetd(fp);

  // Skip deleted boards
  if(!board_size)
  {
    fseek(fp, 4, SEEK_CUR);
    goto err_out;
  }

  board_location = fgetd(fp);
  last_location = ftell(fp);

  if(fseek(fp, board_location, SEEK_SET))
  {
    val_error(WORLD_BOARD_MISSING, board_location);
    goto err_out;
  }

  cur_board->world_version = world_version;
  result = load_board_direct(cur_board, fp, board_size, savegame, file_version);

  if(result != VAL_SUCCESS)
    create_blank_board(cur_board);

  fseek(fp, last_location, SEEK_SET);
  return cur_board;

err_out:
  free(cur_board);
  return NULL;
}
Ejemplo n.º 2
0
__editor_maybe_static int load_board_direct(struct board *cur_board,
 FILE *fp, int data_size, int savegame, int version)
{
  int num_robots, num_scrolls, num_sensors, num_robots_active;
  int overlay_mode, size, board_width, board_height, i;
  int viewport_x, viewport_y, viewport_width, viewport_height;
  int truncated = 0;

  struct robot *cur_robot;
  struct scroll *cur_scroll;
  struct sensor *cur_sensor;

  char *test_buffer;

  int board_location = ftell(fp);

  cur_board->num_robots = 0;
  cur_board->num_robots_allocated = 0;
  cur_board->num_robots_active = 0;
  cur_board->num_scrolls = 0;
  cur_board->num_scrolls_allocated = 0;
  cur_board->num_sensors = 0;
  cur_board->num_sensors_allocated = 0;
  cur_board->robot_list = NULL;
  cur_board->robot_list_name_sorted = NULL;
  cur_board->sensor_list = NULL;
  cur_board->scroll_list = NULL;

  // Initialize some fields that may no longer be loaded
  // from the board file itself..

  cur_board->last_key = '?';
  cur_board->num_input = 0;
  cur_board->input_size = 0;
  cur_board->input_string[0] = 0;
  cur_board->player_last_dir = 0x10;
  cur_board->bottom_mesg[0] = 0;
  cur_board->b_mesg_timer = 0;
  cur_board->lazwall_start = 7;
  cur_board->b_mesg_row = 24;
  cur_board->b_mesg_col = -1;
  cur_board->scroll_x = 0;
  cur_board->scroll_y = 0;
  cur_board->locked_x = -1;
  cur_board->locked_y = -1;
  cur_board->volume = 255;
  cur_board->volume_inc = 0;
  cur_board->volume_target = 255;

  // board_mode, unused
  if(fgetc(fp) == EOF)
  {
    val_error(WORLD_BOARD_MISSING, board_location);
    return VAL_MISSING;
  }

  overlay_mode = fgetc(fp);

  if(!overlay_mode)
  {
    int overlay_width;
    int overlay_height;

    overlay_mode = fgetc(fp);
    overlay_width = fgetw(fp);
    overlay_height = fgetw(fp);

    size = overlay_width * overlay_height;

    if((size < 1) || (size > MAX_BOARD_SIZE))
      goto err_invalid;

    cur_board->overlay = cmalloc(size);
    cur_board->overlay_color = cmalloc(size);

    if(load_RLE2_plane(cur_board->overlay, fp, size))
      goto err_freeoverlay;

    test_buffer = cmalloc(1024);
    free(test_buffer);

    // Skip sizes
    if(fseek(fp, 4, SEEK_CUR) ||
     load_RLE2_plane(cur_board->overlay_color, fp, size))
      goto err_freeoverlay;

    test_buffer = cmalloc(1024);
    free(test_buffer);
  }
  else
  {
    overlay_mode = 0;
    // Undo that last get
    fseek(fp, -1, SEEK_CUR);
  }

  cur_board->overlay_mode = overlay_mode;

  board_width = fgetw(fp);
  board_height = fgetw(fp);
  cur_board->board_width = board_width;
  cur_board->board_height = board_height;

  size = board_width * board_height;

  if((size < 1) || (size > MAX_BOARD_SIZE))
    goto err_freeoverlay;

  cur_board->level_id = cmalloc(size);
  cur_board->level_color = cmalloc(size);
  cur_board->level_param = cmalloc(size);
  cur_board->level_under_id = cmalloc(size);
  cur_board->level_under_color = cmalloc(size);
  cur_board->level_under_param = cmalloc(size);

  if(load_RLE2_plane(cur_board->level_id, fp, size))
    goto err_freeboard;

  if(fseek(fp, 4, SEEK_CUR) ||
   load_RLE2_plane(cur_board->level_color, fp, size))
    goto err_freeboard;

  if(fseek(fp, 4, SEEK_CUR) ||
   load_RLE2_plane(cur_board->level_param, fp, size))
    goto err_freeboard;

  if(fseek(fp, 4, SEEK_CUR) ||
   load_RLE2_plane(cur_board->level_under_id, fp, size))
    goto err_freeboard;

  if(fseek(fp, 4, SEEK_CUR) ||
   load_RLE2_plane(cur_board->level_under_color, fp, size))
    goto err_freeboard;

  if(fseek(fp, 4, SEEK_CUR) ||
   load_RLE2_plane(cur_board->level_under_param, fp, size))
    goto err_freeboard;

  // Load board parameters

  if(version < 0x0253)
  {
    fread(cur_board->mod_playing, LEGACY_MOD_FILENAME_MAX, 1, fp);
    cur_board->mod_playing[LEGACY_MOD_FILENAME_MAX] = 0;
  }
  else
  {
    size_t len = fgetw(fp);
    if(len >= MAX_PATH)
      len = MAX_PATH - 1;

    fread(cur_board->mod_playing, len, 1, fp);
    cur_board->mod_playing[len] = 0;
  }

  viewport_x = fgetc(fp);
  viewport_y = fgetc(fp);
  viewport_width = fgetc(fp);
  viewport_height = fgetc(fp);

  if(
   (viewport_x < 0) || (viewport_x > 79) ||
   (viewport_y < 0) || (viewport_y > 24) ||
   (viewport_width < 1) || (viewport_width > 80) ||
   (viewport_height < 1) || (viewport_height > 25))
    goto err_invalid;

  cur_board->viewport_x = viewport_x;
  cur_board->viewport_y = viewport_y;
  cur_board->viewport_width = viewport_width;
  cur_board->viewport_height = viewport_height;
  cur_board->can_shoot = fgetc(fp);
  cur_board->can_bomb = fgetc(fp);
  cur_board->fire_burn_brown = fgetc(fp);
  cur_board->fire_burn_space = fgetc(fp);
  cur_board->fire_burn_fakes = fgetc(fp);
  cur_board->fire_burn_trees = fgetc(fp);
  cur_board->explosions_leave = fgetc(fp);
  cur_board->save_mode = fgetc(fp);
  cur_board->forest_becomes = fgetc(fp);
  cur_board->collect_bombs = fgetc(fp);
  cur_board->fire_burns = fgetc(fp);

  for(i = 0; i < 4; i++)
  {
    cur_board->board_dir[i] = fgetc(fp);
  }

  cur_board->restart_if_zapped = fgetc(fp);
  cur_board->time_limit = fgetw(fp);

  if(version < 0x0253)
  {
    cur_board->last_key = fgetc(fp);
    cur_board->num_input = fgetw(fp);
    cur_board->input_size = fgetc(fp);

    fread(cur_board->input_string, LEGACY_INPUT_STRING_MAX + 1, 1, fp);
    cur_board->input_string[LEGACY_INPUT_STRING_MAX] = 0;

    cur_board->player_last_dir = fgetc(fp);

    fread(cur_board->bottom_mesg, LEGACY_BOTTOM_MESG_MAX + 1, 1, fp);
    cur_board->bottom_mesg[LEGACY_BOTTOM_MESG_MAX] = 0;

    cur_board->b_mesg_timer = fgetc(fp);
    cur_board->lazwall_start = fgetc(fp);
    cur_board->b_mesg_row = fgetc(fp);
    cur_board->b_mesg_col = (signed char)fgetc(fp);
    cur_board->scroll_x = (signed short)fgetw(fp);
    cur_board->scroll_y = (signed short)fgetw(fp);
    cur_board->locked_x = (signed short)fgetw(fp);
    cur_board->locked_y = (signed short)fgetw(fp);
  }
  else if(savegame)
  {
    size_t len;

    cur_board->last_key = fgetc(fp);
    cur_board->num_input = fgetw(fp);
    cur_board->input_size = fgetw(fp);

    len = fgetw(fp);
    if(len >= ROBOT_MAX_TR)
      len = ROBOT_MAX_TR - 1;

    fread(cur_board->input_string, len, 1, fp);
    cur_board->input_string[len] = 0;

    cur_board->player_last_dir = fgetc(fp);

    len = fgetw(fp);
    if(len >= ROBOT_MAX_TR)
      len = ROBOT_MAX_TR - 1;

    fread(cur_board->bottom_mesg, len, 1, fp);
    cur_board->bottom_mesg[len] = 0;

    cur_board->b_mesg_timer = fgetc(fp);
    cur_board->lazwall_start = fgetc(fp);
    cur_board->b_mesg_row = fgetc(fp);
    cur_board->b_mesg_col = (signed char)fgetc(fp);
    cur_board->scroll_x = (signed short)fgetw(fp);
    cur_board->scroll_y = (signed short)fgetw(fp);
    cur_board->locked_x = (signed short)fgetw(fp);
    cur_board->locked_y = (signed short)fgetw(fp);
  }

  cur_board->player_ns_locked = fgetc(fp);
  cur_board->player_ew_locked = fgetc(fp);
  cur_board->player_attack_locked = fgetc(fp);

  if(version < 0x0253 || savegame)
  {
    cur_board->volume = fgetc(fp);
    cur_board->volume_inc = fgetc(fp);
    cur_board->volume_target = fgetc(fp);
  }


  /***************/
  /* Load robots */
  /***************/
  num_robots = fgetc(fp);
  num_robots_active = 0;

  if(num_robots == EOF)
    truncated = 1;

  // EOF/crazy value check
  if((num_robots < 0) || (num_robots > 255) || (num_robots > size))
    goto board_scan;

  cur_board->robot_list = ccalloc(num_robots + 1, sizeof(struct robot *));
  // Also allocate for name sorted list
  cur_board->robot_list_name_sorted =
   ccalloc(num_robots, sizeof(struct robot *));

  // Any null objects being placed will later be optimized out

  if(num_robots)
  {
    for(i = 1; i <= num_robots; i++)
    {
      // Make sure there's robots to load here
      int length_check = fgetw(fp);
      fseek(fp, -2, SEEK_CUR);
      if(length_check < 0)
      {
        // Send off the error and then tell validation to shut up for now
        val_error(WORLD_ROBOT_MISSING, ftell(fp));
        set_validation_suppression(1);
        truncated = 1;
      }

      cur_robot = load_robot_allocate(fp, savegame, version);
      if(cur_robot->used)
      {
        cur_board->robot_list[i] = cur_robot;
        cur_board->robot_list_name_sorted[num_robots_active] = cur_robot;
        num_robots_active++;
      }
      else
      {
        // We don't need no null robot
        clear_robot(cur_robot);
        cur_board->robot_list[i] = NULL;
      }
    }
  }

  set_validation_suppression(-1);

  if(num_robots_active > 0)
  {
    if(num_robots_active != num_robots)
    {
      cur_board->robot_list_name_sorted =
       crealloc(cur_board->robot_list_name_sorted,
       sizeof(struct robot *) * num_robots_active);
    }
    qsort(cur_board->robot_list_name_sorted, num_robots_active,
     sizeof(struct robot *), cmp_robots);
  }
  else
  {
    free(cur_board->robot_list_name_sorted);
    cur_board->robot_list_name_sorted = NULL;
  }

  cur_board->num_robots = num_robots;
  cur_board->num_robots_allocated = num_robots;
  cur_board->num_robots_active = num_robots_active;


  /****************/
  /* Load scrolls */
  /****************/
  num_scrolls = fgetc(fp);

  if(num_scrolls == EOF)
    truncated = 1;

  if((num_scrolls < 0) || (num_scrolls > 255) || (num_robots + num_scrolls > size))
    goto board_scan;

  cur_board->scroll_list = ccalloc(num_scrolls + 1, sizeof(struct scroll *));

  if(num_scrolls)
  {
    for(i = 1; i <= num_scrolls; i++)
    {
      cur_scroll = load_scroll_allocate(fp);
      if(cur_scroll->used)
        cur_board->scroll_list[i] = cur_scroll;
      else
        clear_scroll(cur_scroll);
    }
  }

  cur_board->num_scrolls = num_scrolls;
  cur_board->num_scrolls_allocated = num_scrolls;


  /****************/
  /* Load sensors */
  /****************/
  num_sensors = fgetc(fp);

  if(num_sensors == EOF)
    truncated = 1;

  if((num_sensors < 0) || (num_sensors > 255) ||
   (num_scrolls + num_sensors + num_robots > size))
    goto board_scan;

  cur_board->sensor_list = ccalloc(num_sensors + 1, sizeof(struct sensor *));

  if(num_sensors)
  {
    for(i = 1; i <= num_sensors; i++)
    {
      cur_sensor = load_sensor_allocate(fp);
      if(cur_sensor->used)
        cur_board->sensor_list[i] = cur_sensor;
      else
        clear_sensor(cur_sensor);
    }
  }

  cur_board->num_sensors = num_sensors;
  cur_board->num_sensors_allocated = num_sensors;


board_scan:
  // Now do a board scan to make sure there aren't more than the data told us.
  {
    int robot_count = 0, scroll_count = 0, sensor_count = 0;
    char err_mesg[80] = { 0 };

    for(i = 0; i < (board_width * board_height); i++)
    {
      if(cur_board->level_id[i] > 127)
        cur_board->level_id[i] = CUSTOM_BLOCK;

      if(cur_board->level_under_id[i] > 127)
        cur_board->level_under_id[i] = CUSTOM_FLOOR;

      switch(cur_board->level_id[i])
      {
        case ROBOT:
        case ROBOT_PUSHABLE:
        {
          robot_count++;
          if(robot_count > cur_board->num_robots)
          {
            cur_board->level_id[i] = CUSTOM_BLOCK;
            cur_board->level_param[i] = 'R';
            cur_board->level_color[i] = 0xCF;
          }
          break;
        }
        case SIGN:
        case SCROLL:
        {
          scroll_count++;
          if(scroll_count > cur_board->num_scrolls)
          {
            cur_board->level_id[i] = CUSTOM_BLOCK;
            cur_board->level_param[i] = 'S';
            cur_board->level_color[i] = 0xCF;
          }
        }
        case SENSOR:
        {
          // Wait, I forgot.  Nobody cares about sensors.
          //sensor_count++;
          if(sensor_count > cur_board->num_sensors)
          {
            cur_board->level_id[i] = CUSTOM_FLOOR;
            cur_board->level_param[i] = 'S';
            cur_board->level_color[i] = 0xDF;
          }
        }
      }
    }
    if(robot_count > cur_board->num_robots)
    {
      snprintf(err_mesg, 80, "Board @ %Xh: found %i robots; expected %i",
       board_location, robot_count, cur_board->num_robots);
      error(err_mesg, 1, 8, 0);
    }
    if(scroll_count > cur_board->num_scrolls)
    {
      snprintf(err_mesg, 80, "Board @ %Xh: found %i scrolls/signs; expected %i",
       board_location, scroll_count, cur_board->num_scrolls);
      error(err_mesg, 1, 8, 0);
    }
    // This won't be reached but I'll leave it anyway.
    if(sensor_count > cur_board->num_sensors)
    {
      snprintf(err_mesg, 80, "Board @ %Xh: found %i sensors; expected %i",
       board_location, sensor_count, cur_board->num_sensors);
      error(err_mesg, 1, 8, 0);
    }
    if(err_mesg[0])
      error("Any extra robots/scrolls/signs were replaced", 1, 8, 0);

  }

  if(truncated == 1)
    val_error(WORLD_BOARD_TRUNCATED_SAFE, board_location);

  return VAL_SUCCESS;

err_freeboard:
  free(cur_board->level_id);
  free(cur_board->level_color);
  free(cur_board->level_param);
  free(cur_board->level_under_id);
  free(cur_board->level_under_color);
  free(cur_board->level_under_param);

err_freeoverlay:
  if(overlay_mode)
  {
    free(cur_board->overlay);
    free(cur_board->overlay_color);
  }

err_invalid:
  val_error(WORLD_BOARD_CORRUPT, board_location);
  return VAL_INVALID;
}
Ejemplo n.º 3
0
/* This is a lot like try_load_world but much more thorough, and doesn't
 * pass data through or leave a file open.  This needs to be done before
 * any data is ever loaded, so that Megazeux can cleanly abort if there
 * is an issue.
 */
enum val_result validate_world_file(const char *filename,
 int savegame, int *end_of_global_offset, int decrypt_attempted)
{
  enum val_result result = VAL_SUCCESS;

  FILE *f;
  char magic[15];
  int num_boards;
  int board_name_offset;
  int v, i;

  /* TEST 1:  Make sure it's a readable file */
  if(!(f = val_fopen(filename)))
  {
    val_error(FILE_DOES_NOT_EXIST, 0);
    result = VAL_MISSING;
    goto err_out;
  }

  /* TEST 2:  Is it a save file? */
  if(savegame)
  {
    int screen_mode, num_counters, num_strings, len;

    if(fread(magic, 5, 1, f) != 1)
      goto err_invalid;

    v = save_magic(magic);
    if(!v)
      goto err_invalid;

    else if (v > WORLD_VERSION)
    {
      val_error(SAVE_VERSION_TOO_RECENT, v);
      result = VAL_VERSION;
      goto err_close;
    }
    else if (v < WORLD_VERSION)
    {
      val_error(SAVE_VERSION_OLD, v);
      result = VAL_VERSION;
      goto err_close;
    }

    /* TEST 3:  Check for truncation, savegame style, hope this
     * doesn't explode :erm:
     */
    if(
     fseek(f, 8, SEEK_SET) ||
     fseek(f, WORLD_BLOCK_1_SIZE, SEEK_CUR) ||
     fseek(f, 71, SEEK_CUR) ||
     fseek(f, (len = fgetw(f)), SEEK_CUR) ||
     (len < 0) ||
     fseek(f, WORLD_BLOCK_2_SIZE, SEEK_CUR) ||
     fseek(f, 24, SEEK_CUR))
    {
      debug("pre-counters\n");
      goto err_invalid;
    }

    //do counters - vvvvnnnn(name)
    num_counters = fgetd(f);
    if(num_counters < 0)
    {
      debug("counter num\n");
      goto err_invalid;
    }

    for(i = 0; i < num_counters; i++)
    {
      if(
       fseek(f, 4, SEEK_CUR) || //value
       fseek(f, (len = fgetd(f)), SEEK_CUR) ||
       (len < 0))
      {
        debug("counters\n");
        goto err_invalid;
      }
    }

    //do strings-   nnnnllll(name)(value)
    num_strings = fgetd(f);
    if(num_strings < 0)
    {
      debug("string num\n");
      goto err_invalid;
    }

    for(i = 0; i < num_strings; i++)
    {
      int name_length = fgetd(f);
      int value_length = fgetd(f);
      if(
       (name_length < 0) ||
       (value_length < 0) ||
       fseek(f, name_length, SEEK_CUR) ||
       fseek(f, value_length, SEEK_CUR))
      {
        debug("strings\n");
        goto err_invalid;
      }
    }

    if(
     fseek(f, 4612, SEEK_CUR) || //sprites
     fseek(f, 12, SEEK_CUR) || //misc
     fseek(f, fgetw(f), SEEK_CUR) || //fread_open
     fseek(f, 4, SEEK_CUR) || //fread_pos
     fseek(f, fgetw(f), SEEK_CUR) || //fwrite_open
     fseek(f, 4, SEEK_CUR)) //fwrite_pos
    {
      debug("post strings\n");
      goto err_invalid;
    }

    screen_mode = fgetw(f);
    if((screen_mode > 3) || (screen_mode > 1 &&
     fseek(f, 768, SEEK_CUR))) //smzx palette
    {
      debug("smzx palette\n");
      goto err_invalid;
    }

    if(
     fseek(f, 4, SEEK_CUR) || //commands
     ((len = fgetd(f)) < 0) || //vlayer size
     fseek(f, 4, SEEK_CUR) || // width & height
     fseek(f, len, SEEK_CUR) || //chars
     fseek(f, len, SEEK_CUR)) //colors
    {
      debug("vlayer\n");
      goto err_invalid;
    }

    /* now we should be at the global robot pointer! */
  }

  else /* !savegame */
  {
    int protection_method;

    /* TEST 3:  Check for truncation */
    if(fseek(f, WORLD_GLOBAL_OFFSET_OFFSET, SEEK_SET))
      goto err_invalid;

    fseek(f, BOARD_NAME_SIZE, SEEK_SET);

    /* TEST 4:  If we think it's locked, try to decrypt it. */
    protection_method = fgetc(f);
    if(protection_method > 0)
    {
      if(protection_method > 3)
        goto err_invalid;

      if(decrypt_attempted) // In the unlikely event that this will happen again
        goto err_invalid;

      val_error(WORLD_PASSWORD_PROTECTED, 0);

      if(!confirm(NULL, "Would you like to decrypt it?"))
      {
        result = VAL_NEED_UNLOCK;
        goto err_close;
      }
      else
      {
        val_error(WORLD_LOCKED, 0);
        result = VAL_ABORTED;
        goto err_close;
      }
    }

    /* TEST 5:  Make sure the magic is awwrightttt~ */
    fread(magic, 1, 3, f);

    v = world_magic(magic);
    if(v == 0)
      goto err_invalid;

    else if (v < 0x0205)
    {
      val_error(WORLD_FILE_VERSION_OLD, v);
      result = VAL_VERSION;
      goto err_close;
    }
    else if (v > WORLD_VERSION)
    {
      val_error(WORLD_FILE_VERSION_TOO_RECENT, v);
      result = VAL_VERSION;
      goto err_close;
    }

    /* TEST 6:  Attempt to eliminate invalid files by
     * testing the palette for impossible values.
     */
    fseek(f, WORLD_GLOBAL_OFFSET_OFFSET - 48, SEEK_SET);
    for(i = 0; i<48; i++)
    {
      int val = fgetc(f);
      if((val < 0) || (val > 63))
        goto err_invalid;
    }

    /* now we should be at the global robot pointer! */
  }

  /* TEST 7:  Either branch should be at the global robot pointer now.
   * Test for valid SFX structure, if applicable, and board information.
   */
  fseek(f, 4, SEEK_CUR);

  // Do the sfx
  num_boards = fgetc(f);
  if(num_boards == 0)
  {
    int sfx_size = fgetw(f);
    int sfx_off = ftell(f);

    for (i = 0; i < NUM_SFX; i++)
    {
      if(fseek(f, fgetc(f), SEEK_CUR))
        break;
    }

    if((i != NUM_SFX) || ((ftell(f) - sfx_off) != sfx_size))
      goto err_invalid;

    num_boards = fgetc(f);
  }
  if(num_boards == 0)
    goto err_invalid;

  board_name_offset = ftell(f);

  //Make sure board name and pointer data exists
  if(
   fseek(f, num_boards * BOARD_NAME_SIZE, SEEK_CUR) ||
   fseek(f, num_boards * 8, SEEK_CUR) ||
   ((ftell(f) - board_name_offset) != num_boards * (BOARD_NAME_SIZE + 8)))
    goto err_invalid;

  /* If any of the pointers are less than this pos we probably
   * aren't dealing with a valid world, but it's not our job
   * to figure that out right now, so we'll pass it back.
   */
  if(end_of_global_offset)
    *end_of_global_offset = ftell(f);

  //todo: maybe have a complete fail when N number of pointers fail?

  goto err_close;

err_invalid:
  result = VAL_INVALID;
  if(savegame)
    val_error(SAVE_FILE_INVALID, 0);
  else
    val_error(WORLD_FILE_INVALID, 0);
err_close:
  fclose(f);
err_out:
  return result;
}