Exemple #1
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;
}
Exemple #2
0
int save_world(struct world *mzx_world, const char *file, int savegame)
{
  int i, num_boards;
  int gl_rob_position, gl_rob_save_position;
  int board_offsets_position, board_begin_position, board_end_position;
  int board_size;
  unsigned int *size_offset_list;
  unsigned char *charset_mem;
  unsigned char r, g, b;
  struct board *cur_board;
  FILE *fp;

  int meter_target = 2 + mzx_world->num_boards, meter_curr = 0;

#ifdef CONFIG_DEBYTECODE
  if(!savegame)
  {
    fp = fopen(file, "rb");
    if(fp)
    {
      if(!fseek(fp, 0x1A, SEEK_SET))
      {
        char tmp[3];
        if(fread(tmp, 1, 3, fp) == 3)
        {
          // If it's not a 2.90 world, abort the save
          if(world_magic(tmp) < 0x025A)
          {
            error("Save would overwrite older world. Aborted.", 0, 1, 1337);
            goto exit_close;
          }
        }
      }
      fclose(fp);
    }
  }
#endif

  fp = fopen(file, "wb");
  if(!fp)
  {
    error("Error saving world", 1, 8, 0x0D01);
    return -1;
  }

  meter_initial_draw(meter_curr, meter_target, "Saving...");

  if(savegame)
  {
    // Write this MZX's version string
    fputs("MZS", fp);
    fputc((WORLD_VERSION >> 8) & 0xff, fp);
    fputc(WORLD_VERSION & 0xff, fp);

    // Write the version of the loaded world for this SAV
    fputw(mzx_world->version, fp);

    fputc(mzx_world->current_board_id, fp);
  }
  else
  {