Пример #1
0
/**
 * \brief __newindex function of the environment of the savegame file.
 *
 * This special __newindex function catches declaration of global variables
 * to store them into the savegame.
 *
 * \param l The Lua context that is calling this function.
 * \return Number of values to return to Lua.
 */
int Savegame::l_newindex(lua_State* l) {

  return LuaTools::exception_boundary_handle(l, [&] {
    lua_getfield(l, LUA_REGISTRYINDEX, "savegame");
    Savegame* savegame = static_cast<Savegame*>(lua_touserdata(l, -1));
    lua_pop(l, 1);

    const std::string& key = LuaTools::check_string(l, 2);

    switch (lua_type(l, 3)) {

    case LUA_TBOOLEAN:
      savegame->set_boolean(key, lua_toboolean(l, 3));
      break;

    case LUA_TNUMBER:
      savegame->set_integer(key, (int) lua_tointeger(l, 3));
      break;

    case LUA_TSTRING:
      savegame->set_string(key, lua_tostring(l, 3));
      break;

    default:
      LuaTools::type_error(l, 3, "string, number or boolean");
    }

    return 0;
  });
}
Пример #2
0
void
Levelset::refresh()
{
  for(std::vector<Level*>::iterator i = levels.begin(); i != levels.end(); ++i)
    {
      Savegame* savegame = SavegameManager::instance()->get((*i)->resname);

      if (savegame)
        {
          (*i)->accessible = (savegame->get_status() != Savegame::NONE);
          (*i)->finished   = (savegame->get_status() == Savegame::FINISHED);
        }
    }

  if (!levels.empty())
    {
      levels[0]->accessible = true; 
      for(std::vector<Level*>::size_type i = 0; i < levels.size()-1; ++i)
        if (levels[i]->finished)
          levels[i+1]->accessible = true;
    }

  completion = 0;
  for(std::vector<Level*>::iterator i = levels.begin(); i != levels.end(); ++i)
    if ((*i)->finished)
      completion += 1;
  completion = Math::clamp(0, completion * 100 / int(levels.size()), 100);
}
Пример #3
0
void
SavegameManager::store(Savegame& arg_savegame)
{
  Savegame* savegame = new Savegame(arg_savegame);
  SavegameTable::iterator i = find(savegame->get_filename());
  if (i == savegames.end())
  { // don't know anything about the savegame
    savegames.push_back(savegame);
  }
  else
  { // already have such a savegame
    if ((*i)->get_status() == Savegame::FINISHED
        && savegame->get_status() == Savegame::ACCESSIBLE)
    { // saved savegame is better then new game
      delete savegame;
    }
    else
    { // new game is better or equal, save it
      delete *i;
      *i = savegame;
    }
  }

  flush();
}
Пример #4
0
/**
 * \brief __newindex function of the environment of the savegame file.
 *
 * This special __newindex function catches declaration of global variables
 * to store them into the savegame.
 *
 * \param l The Lua context that is calling this function.
 * \return Number of values to return to Lua.
 */
int Savegame::l_newindex(lua_State* l) {

  lua_getfield(l, LUA_REGISTRYINDEX, "savegame");
  Savegame* savegame = static_cast<Savegame*>(lua_touserdata(l, -1));
  lua_pop(l, 1);

  const std::string& key = luaL_checkstring(l, 2);

  switch (lua_type(l, 3)) {

    case LUA_TBOOLEAN:
      savegame->set_boolean(key, lua_toboolean(l, 3));
      break;

    case LUA_TNUMBER:
      savegame->set_integer(key, int(lua_tointeger(l, 3)));
      break;

    case LUA_TSTRING:
      savegame->set_string(key, lua_tostring(l, 3));
      break;

   default:
      luaL_typerror(l, 3, "string, number or boolean");
  }

  return 0;
}
Пример #5
0
bool
LevelDot::finished()
{
  Savegame* savegame = SavegameManager::instance()->get(plf.get_resname());
  if (savegame && savegame->get_status() == Savegame::FINISHED)
    return true;
  else
    return false;
}
Пример #6
0
bool
LevelDot::accessible()
{
  Savegame* savegame = SavegameManager::instance()->get(plf.get_resname());
  if (savegame && savegame->get_status() != Savegame::NONE)
    return true;
  else
    return false;
}
Пример #7
0
SavegameManager::SavegameManager(const std::string& arg_filename) :
  filename(System::get_userdir() + arg_filename),
  savegames()
{
  assert(instance_ == 0);
  instance_ = this;

  std::shared_ptr<lisp::Lisp> sexpr;

  try 
  {
    sexpr = lisp::Parser::parse(filename);
  }
  catch (const std::runtime_error& e) 
  {
    std::cerr << "SavegameManager: " << e.what() << std::endl;
    return;
  }

  if (!sexpr)
  {
    std::cerr << "SavegameManager: Couldn't find savegame file '" <<
      filename << "', starting with an empty one." << std::endl;
    return;
  }

  SExprFileReader reader(sexpr->get_list_elem(0));
  if (reader.get_name() != "pingus-savegame")
  {
    std::cerr << "Error: " << filename << ": not a (pingus-savegame) file" << std::endl;
    return;
  }

  const std::vector<FileReader>& sections = reader.get_sections();
  for(std::vector<FileReader>::const_iterator i = sections.begin();
      i != sections.end(); ++i)
  {
    Savegame* savegame = new Savegame(*i);
    SavegameTable::iterator j = find(savegame->get_filename());
    if (j != savegames.end())
    { // overwrite duplicates, shouldn't happen, but harmless
      std::cout << "SavegameManager: name collision: " << savegame->get_filename() << std::endl;
      delete *j;
      *j = savegame;
    }
    else
    {
      savegames.push_back(savegame);
    }
  }
}
Пример #8
0
void iwSaveLoad::RefreshTable()
{
    static bool loadedOnce = false;

    GetCtrl<ctrlTable>(0)->DeleteAllItems();

    std::vector<std::string> saveFiles = ListDir(GetFilePath(FILE_PATHS[85]), "sav");
    for(std::vector<std::string>::iterator it = saveFiles.begin(); it != saveFiles.end(); ++it)
    {
        Savegame save;

        // Datei öffnen
        if(!save.Load(*it, false, false))
        {
            // Show errors only first time this is loaded
            if(!loadedOnce)
            {
                LOG.write(_("Invalid Savegame %1%! Reason: %2%\n"))
                    % *it
                    % (save.GetLastErrorMsg().empty() ? _("Unknown") : save.GetLastErrorMsg());
            }
            continue;
        }

        // Zeitstring erstellen
        std::string dateStr = TIME.FormatTime("%d.%m.%Y - %H:%i", &save.save_time);

        // Dateiname noch rausextrahieren aus dem Pfad
        bfs::path path = *it;
        if(!path.has_filename())
            continue;
        bfs::path fileName = path.filename();

        // ".sav" am Ende weg
        RTTR_Assert(fileName.has_extension());
        fileName.replace_extension();

        std::string fileNameStr = cvWideStringToUTF8(fileName.wstring());
        std::string startGF = helpers::toString(save.start_gf);

        // Und das Zeug zur Tabelle hinzufügen
        GetCtrl<ctrlTable>(0)->AddRow(0, fileNameStr.c_str(), save.mapName.c_str(), dateStr.c_str(), startGF.c_str(), it->c_str());
    }

    // Nach Zeit Sortieren
    bool bFalse = false;
    GetCtrl<ctrlTable>(0)->SortRows(2, &bFalse);
    loadedOnce = true;
}
Пример #9
0
void
LevelDot::unlock()
{
  Savegame* savegame = SavegameManager::instance()->get(plf.get_resname());
  if (savegame == 0 || savegame->get_status() == Savegame::NONE)
  {
    Savegame savegame_(plf.get_resname(),
                       Savegame::ACCESSIBLE,
                       0,
                       0);
    SavegameManager::instance()->store(savegame_);
  }
  else
  {
  }
}
Пример #10
0
/**
 * @brief Creates a game.
 * @param solarus the application object
 * @param savegame the saved data of this game (the specified object will be copied and stored into the game)
 */
Game::Game(Solarus &solarus, Savegame &savegame):

  Screen(solarus),
  savegame(savegame),
  pause_key_available(true),
  pause_menu(NULL), 
  gameover_sequence(NULL),
  reseting(false),
  restarting(false),
  keys_effect(NULL),
  current_map(NULL),
  next_map(NULL),
  previous_map_surface(NULL),
  transition_style(Transition::IMMEDIATE),
  transition(NULL),
  dungeon(NULL),
  crystal_switch_state(false),
  hud(NULL),
  hud_enabled(true),
  dialog_box(NULL) {

  // notify objects
  get_equipment().set_game(*this);
  solarus.get_debug_keys().set_game(this);

  // initialize members
  controls = new GameControls(*this);
  dialog_box = new DialogBox(*this);
  hero = new Hero(get_equipment());
  keys_effect = new KeysEffect();
  hud = new HUD(*this);

  // launch the starting map
  set_current_map(savegame.get_integer(Savegame::STARTING_MAP), "", Transition::FADE);
}
Пример #11
0
/**
 * \brief Implementation of sol.game.load().
 * \param l The Lua context that is calling this function.
 * \return Number of values to return to Lua.
 */
int LuaContext::game_api_load(lua_State* l) {

  const std::string& file_name = luaL_checkstring(l, 1);

  if (FileTools::get_quest_write_dir().empty()) {
    error(l, "Cannot load savegame: no write directory was specified in quest.dat");
  }

  Savegame* savegame = new Savegame(get_lua_context(l).get_main_loop(), file_name);

  RefCountable::ref(savegame);
  savegame->get_equipment().load_items();

  push_game(l, *savegame);
  RefCountable::unref(savegame);
  return 1;
}
Пример #12
0
unsigned int BasicMonster::save(Savegame& sg)
{
	unsigned int id;
	if (sg.saved(this, &id)) return id;
	SaveBlock store("BasicMonster", id);
	storeAll(sg, store);
	sg << store;
	return id;
}
Пример #13
0
// for Savegame
bool Storage::createSavegame(int grp, int lvl, int timespent, int cols_left, int cols_over, QStringList used, QStringList marked)
{
    //qDebug() << "createSavegame";
    if (! m_db.isOpen()) return false;

    bool ret = false;

    QDateTime now = QDateTime::currentDateTime();

    Savegame *m = new Savegame;
    m->created = now.toTime_t();
    m->updated = now.toTime_t();
    m->grp = grp;
    m->lvl = lvl;
    m->timespent = timespent;
    m->cols_left = cols_left;
    m->cols_over = cols_over;
    m->setUsedCells(used);
    m->setMarkedCells(marked);

    QSqlQuery q;
    ret = q.prepare("INSERT INTO sg (created, updated, grp, lvl, timespent, cleft, cover, cused, cmarked) "
        "VALUES (:created, :updated, :grp, :lvl, :timespent, :cleft, :cover, :cused, :cmarked)");

    if (ret) {
        q.bindValue(":created", m->created);
        q.bindValue(":updated", m->updated);
        q.bindValue(":grp", m->grp);
        q.bindValue(":lvl", m->lvl);
        q.bindValue(":timespent", m->timespent);
        q.bindValue(":cleft", m->cols_left);
        q.bindValue(":cover", m->cols_over);
        q.bindValue(":cused", m->cells_used);
        q.bindValue(":cmarked", m->cells_marked);

        ret = q.exec();
    }

    if (ret) m->id = q.lastInsertId().toInt();

    //qDebug() << "created with id" << m->id;

    return ret;
}
Пример #14
0
unsigned int Object::save(Savegame& sg)
{
	unsigned int id;
	if (sg.saved(this,&id)) return id;
	SaveBlock store("Object", id);
	store ("type", (int) type) ("name", name) ("formatFlags", formatFlags);
	store ("symbol", sym) ("color", color) ("visible", visible);
	sg << store;
	return id;
}
Пример #15
0
unsigned int Tool::save(Savegame& sg)
{
	unsigned int id;
	if (sg.saved(this,&id)) return id;
	SaveBlock store("Tool", id);
	store ("name", name) ("symbol", sym) ("color", color);
	store ("amount", amount) ("weight", weight) ("active", active);
	store ("tool", tool);
	sg << store;
	return id;
}
Пример #16
0
void
LevelDot::draw(DrawingContext& gc)
{
  Vector2i mpos
    = gc.screen_to_world(Vector2i(Input::Controller::current()->get_pointer(Input::STANDARD_POINTER)->get_pos()));

  float x = static_cast<float>(mpos.x) - pos.x;
  float y = static_cast<float>(mpos.y) - pos.y;

  bool highlight = false;

  if (Math::sqrt(x*x + y*y) < 30.0f)
    highlight = true;

  Savegame* savegame = SavegameManager::instance()->get(plf.get_resname());
  if (savegame
      && (savegame->get_status() == Savegame::FINISHED
          || savegame->get_status() == Savegame::ACCESSIBLE))
  {
    if (savegame->get_status() == Savegame::FINISHED)
      if (highlight)
      {
        gc.draw (highlight_green_dot_sur, pos);
      }
      else
      {
        gc.draw (green_dot_sur, pos);
      }
    else
      if (highlight)
        gc.draw (highlight_red_dot_sur, pos);
      else
        gc.draw (red_dot_sur, pos);
  }
  else
  {
    gc.draw (inaccessible_dot_sur, pos);
  }
}
Пример #17
0
void iwSaveLoad::RefreshTable()
{
    static bool loadedOnce = false;

    GetCtrl<ctrlTable>(0)->DeleteAllItems();

    std::vector<std::string> saveFiles = ListDir(RTTRCONFIG.ExpandPath(FILE_PATHS[85]), "sav");
    for(auto& saveFile : saveFiles)
    {
        Savegame save;

        // Datei öffnen
        if(!save.Load(saveFile, false, false))
        {
            // Show errors only first time this is loaded
            if(!loadedOnce)
            {
                LOG.write(_("Invalid Savegame %1%! Reason: %2%\n")) % saveFile
                  % (save.GetLastErrorMsg().empty() ? _("Unknown") : save.GetLastErrorMsg());
            }
            continue;
        }

        // Zeitstring erstellen
        std::string dateStr = s25util::Time::FormatTime("%d.%m.%Y - %H:%i", save.GetSaveTime());

        // Dateiname noch rausextrahieren aus dem Pfad
        bfs::path path = saveFile;
        if(!path.has_filename())
            continue;
        // Just filename w/o extension
        bfs::path fileName = path.stem();

        std::string startGF = helpers::toString(save.start_gf);

        // Und das Zeug zur Tabelle hinzufügen
        GetCtrl<ctrlTable>(0)->AddRow(0, fileName.string().c_str(), save.GetMapName().c_str(), dateStr.c_str(), startGF.c_str(),
                                      saveFile.c_str());
    }

    // Nach Zeit Sortieren
    bool bFalse = false;
    GetCtrl<ctrlTable>(0)->SortRows(2, &bFalse);
    loadedOnce = true;
}
Пример #18
0
/**
 * @brief Deletes a save file.
 * @param save_number number of the savegame to delete (0 to 2)
 */
void SelectionMenuConfirmErase::delete_save_file(int save_number) {

  Savegame *savegame = menu->get_savegame(save_number);
  FileTools::data_file_delete(savegame->get_file_name().c_str());
  menu->reload_savegames();
}
Пример #19
0
void dskSinglePlayer::Msg_ButtonClick(const unsigned int ctrl_id)
{
    switch(ctrl_id)
    {
        case 3: // "Letztes Spiel fortsetzen"
        {
            std::vector<std::string> savFiles = ListDir(GetFilePath(FILE_PATHS[85]), "sav");

            bfs::path path;
            unser_time_t recent = 0;
            for(std::vector<std::string>::iterator it = savFiles.begin(); it != savFiles.end(); ++it)
            {
                Savegame save;

                // Datei öffnen
                if (!save.Load(*it, false, false))
                    continue;

                if (save.save_time > recent)
                {
                    recent = save.save_time;
                    path = *it;
                }
            }

            if (recent != 0)
            {
                // Dateiname noch rausextrahieren aus dem Pfad
                if(!path.has_filename())
                    return;
                bfs::path fileName = path.filename();

                // ".sav" am Ende weg
                RTTR_Assert(fileName.has_extension());
                fileName.replace_extension();

                // Server info
                CreateServerInfo csi;

                csi.gamename = fileName.string();
                csi.password = "******";
                csi.port = 3665;
                csi.type = ServerType::LOCAL;
                csi.ipv6 = false;
                csi.use_upnp = false;

                WINDOWMANAGER.Switch(new dskSelectMap(csi));

                if(GAMESERVER.TryToStart(csi, path.string(), MAPTYPE_SAVEGAME))
                    WINDOWMANAGER.ShowAfterSwitch(new iwPleaseWait);
                else
                    WINDOWMANAGER.Show(new iwMsgbox(_("Error"), _("The specified file couldn't be loaded!"), NULL, MSB_OK, MSB_EXCLAMATIONRED));
            }
            else
                WINDOWMANAGER.Show(new iwMsgbox(_("Error"), _("The specified file couldn't be loaded!"), NULL, MSB_OK, MSB_EXCLAMATIONRED));

        } break;
        case 4: // "Replay abspielen"
        {
            WINDOWMANAGER.Show(new iwPlayReplay);
        } break;
        case 5: // "Kampagne"
        {
            /// @todo Hier dann Auswahl zwischen Kampagne(n) und "Freies Spiel"
            WINDOWMANAGER.Show(new iwMsgbox(_("Not available"), _("Please use \'Unlimited Play\' to create a Singleplayer game."), this, MSB_OK, MSB_EXCLAMATIONGREEN));
        } break;
        case 6: // "Freies Spiel"
        {
            PrepareSinglePlayerServer();
        } break;
        case 7: // "Spiel laden"
        {
            PrepareLoadGame();
        } break;
        case 8: // "Zurück"
        {
            WINDOWMANAGER.Switch(new dskMainMenu);
        } break;
    }
}
Пример #20
0
void dskSinglePlayer::Msg_ButtonClick(const unsigned int ctrl_id)
{
    switch(ctrl_id)
    {
        case 3: // "Letztes Spiel fortsetzen"
        {
            std::list<std::string> liste;
            std::string tmp = GetFilePath(FILE_PATHS[85]);

            tmp += "*.sav";
            ListDir(tmp.c_str(), false, NULL, NULL, &liste);

            std::string path;
            unser_time_t recent = 0;
            for(std::list<std::string>::iterator it = liste.begin(); it != liste.end(); ++it)
            {
                Savegame save;

                // Datei öffnen
                if (!save.Load(*it, false, false))
                    continue;

                if (save.save_time > recent)
                {
                    recent = save.save_time;
                    path = *it;
                }
            }

            if (recent != 0)
            {
                // Dateiname noch rausextrahieren aus dem Pfad
                size_t pos = path.find_last_of('/');
                if(pos == std::string::npos)
                    return;
                std::string extracted_filename = path.substr(pos + 1);

                // ".sav" am Ende weg
                assert(extracted_filename.length() >= 4);
                extracted_filename.erase(extracted_filename.length() - 4);

                // Server info
                CreateServerInfo csi;

                csi.gamename = extracted_filename;
                csi.password = "******";
                csi.port = 3665;
                csi.type = NP_LOCAL;
                csi.ipv6 = false;
                csi.use_upnp = false;

                WindowManager::inst().Switch(new dskSelectMap(csi));

                if(GAMESERVER.TryToStart(csi, path, MAPTYPE_SAVEGAME))
                {
                    WindowManager::inst().Draw();
                    WindowManager::inst().Show(new iwPleaseWait);
                }
                else
                {
                    WindowManager::inst().Show(new iwMsgbox(_("Error"), _("The specified file couldn't be loaded!"), this, MSB_OK, MSB_EXCLAMATIONRED));
                }
            }
            else
            {
                WindowManager::inst().Show(new iwMsgbox(_("Error"), _("The specified file couldn't be loaded!"), this, MSB_OK, MSB_EXCLAMATIONRED));
            }

            liste.clear();
        } break;
        case 4: // "Replay abspielen"
        {
            WindowManager::inst().Show(new iwPlayReplay);
        } break;
        case 5: // "Kampagne"
        {
            /// @todo Hier dann Auswahl zwischen Kampagne(n) und "Freies Spiel"
            WindowManager::inst().Show(new iwMsgbox(_("Not available"), _("Please use \'Unlimited Play\' to create a Singleplayer game."), this, MSB_OK, MSB_EXCLAMATIONGREEN));
        } break;
        case 6: // "Freies Spiel"
        {
            PrepareSinglePlayerServer();
        } break;
        case 7: // "Spiel laden"
        {
            PrepareLoadGame();
        } break;
        case 8: // "Zurück"
        {
            WindowManager::inst().Switch(new dskMainMenu);
        } break;
    }
}
Пример #21
0
/**
 * \brief Converts this savegame v1 into a savegame v2.
 * \param savegame_v2 The savegame to fill.
 */
void SavegameConverterV1::convert_to_v2(Savegame& savegame_v2) {

  // 1. Built-in values.
  savegame_v2.set_string(Savegame::KEY_STARTING_POINT, get_string(STARTING_POINT));
  if (!get_string(STARTING_MAP).empty()) {
    savegame_v2.set_string(Savegame::KEY_STARTING_MAP, get_string(STARTING_MAP));
  }
  else {
    // Older v1 savegames used integers to identify maps.
    std::ostringstream oss;
    oss << get_integer(STARTING_MAP_INT);
    savegame_v2.set_string(Savegame::KEY_STARTING_MAP, oss.str());
  }
  savegame_v2.set_integer(Savegame::KEY_CURRENT_LIFE, get_integer(CURRENT_LIFE));
  savegame_v2.set_integer(Savegame::KEY_CURRENT_MONEY, get_integer(CURRENT_MONEY));
  savegame_v2.set_integer(Savegame::KEY_CURRENT_MAGIC, get_integer(CURRENT_MAGIC));
  savegame_v2.set_integer(Savegame::KEY_MAX_LIFE, get_integer(MAX_LIFE));
  savegame_v2.set_integer(Savegame::KEY_MAX_MONEY, get_integer(MAX_MONEY));
  savegame_v2.set_integer(Savegame::KEY_MAX_MAGIC, get_integer(MAX_MAGIC));
  savegame_v2.set_string(Savegame::KEY_ITEM_SLOT_1, get_string(ITEM_SLOT_0));
  savegame_v2.set_string(Savegame::KEY_ITEM_SLOT_2, get_string(ITEM_SLOT_1));

  savegame_v2.set_string(Savegame::KEY_KEYBOARD_ACTION, InputEvent::get_keyboard_key_name(
      InputEvent::KeyboardKey(get_integer(KEYBOARD_ACTION_KEY))));
  savegame_v2.set_string(Savegame::KEY_KEYBOARD_ATTACK, InputEvent::get_keyboard_key_name(
      InputEvent::KeyboardKey(get_integer(KEYBOARD_SWORD_KEY))));
  savegame_v2.set_string(Savegame::KEY_KEYBOARD_ITEM_1, InputEvent::get_keyboard_key_name(
      InputEvent::KeyboardKey(get_integer(KEYBOARD_ITEM_1_KEY))));
  savegame_v2.set_string(Savegame::KEY_KEYBOARD_ITEM_2, InputEvent::get_keyboard_key_name(
      InputEvent::KeyboardKey(get_integer(KEYBOARD_ITEM_2_KEY))));
  savegame_v2.set_string(Savegame::KEY_KEYBOARD_PAUSE, InputEvent::get_keyboard_key_name(
      InputEvent::KeyboardKey(get_integer(KEYBOARD_PAUSE_KEY))));
  savegame_v2.set_string(Savegame::KEY_KEYBOARD_RIGHT, InputEvent::get_keyboard_key_name(
      InputEvent::KeyboardKey(get_integer(KEYBOARD_RIGHT_KEY))));
  savegame_v2.set_string(Savegame::KEY_KEYBOARD_UP, InputEvent::get_keyboard_key_name(
      InputEvent::KeyboardKey(get_integer(KEYBOARD_UP_KEY))));
  savegame_v2.set_string(Savegame::KEY_KEYBOARD_LEFT, InputEvent::get_keyboard_key_name(
      InputEvent::KeyboardKey(get_integer(KEYBOARD_LEFT_KEY))));
  savegame_v2.set_string(Savegame::KEY_KEYBOARD_DOWN, InputEvent::get_keyboard_key_name(
      InputEvent::KeyboardKey(get_integer(KEYBOARD_DOWN_KEY))));
  savegame_v2.set_string(Savegame::KEY_JOYPAD_ACTION, get_string(JOYPAD_ACTION_KEY));
  savegame_v2.set_string(Savegame::KEY_JOYPAD_ATTACK, get_string(JOYPAD_SWORD_KEY));
  savegame_v2.set_string(Savegame::KEY_JOYPAD_ITEM_1, get_string(JOYPAD_ITEM_1_KEY));
  savegame_v2.set_string(Savegame::KEY_JOYPAD_ITEM_2, get_string(JOYPAD_ITEM_2_KEY));
  savegame_v2.set_string(Savegame::KEY_JOYPAD_PAUSE, get_string(JOYPAD_PAUSE_KEY));
  savegame_v2.set_string(Savegame::KEY_JOYPAD_RIGHT, get_string(JOYPAD_RIGHT_KEY));
  savegame_v2.set_string(Savegame::KEY_JOYPAD_UP, get_string(JOYPAD_UP_KEY));
  savegame_v2.set_string(Savegame::KEY_JOYPAD_LEFT, get_string(JOYPAD_LEFT_KEY));
  savegame_v2.set_string(Savegame::KEY_JOYPAD_DOWN, get_string(JOYPAD_DOWN_KEY));
  savegame_v2.set_integer(Savegame::KEY_ABILITY_RESISTANCE, get_integer(ABILITY_TUNIC));
  savegame_v2.set_integer(Savegame::KEY_ABILITY_SWORD, get_integer(ABILITY_SWORD));
  savegame_v2.set_integer(Savegame::KEY_ABILITY_SHIELD, get_integer(ABILITY_SHIELD));
  savegame_v2.set_integer(Savegame::KEY_ABILITY_LIFT, get_integer(ABILITY_LIFT));
  savegame_v2.set_integer(Savegame::KEY_ABILITY_SWIM, get_integer(ABILITY_SWIM));
  savegame_v2.set_integer(Savegame::KEY_ABILITY_SWORD_KNOWLEDGE, get_integer(ABILITY_SWORD_KNOWLEDGE));
  savegame_v2.set_integer(Savegame::KEY_ABILITY_DETECT_WEAK_WALLS, get_integer(ABILITY_DETECT_WEAK_WALLS));
  savegame_v2.set_integer(Savegame::KEY_ABILITY_GET_BACK_FROM_DEATH, get_integer(ABILITY_GET_BACK_FROM_DEATH));
  savegame_v2.set_integer(Savegame::KEY_ABILITY_RUN, get_integer(ABILITY_RUN));

  // 2. Values that used to be built-in in v1 and become pure data in v2.
  savegame_v2.set_string("player_name", get_string(PLAYER_NAME));
  savegame_v2.set_integer("pause_last_submenu", get_integer(PAUSE_LAST_SUBMENU) + 1);
  savegame_v2.set_integer("pause_inventory_last_item_index", get_integer(INVENTORY_LAST_ITEM_INDEX) + 1);

  for (int i = 0; i < 40; i++) {
    int index = 200 + i * 10;

    std::ostringstream oss;
    oss << "dungeon_" << (i + 1);
    const std::string& dungeon_number = oss.str();

    // Dungeon finished (integer replaced by a boolean).
    if (get_integer(index) > 0) {
      savegame_v2.set_boolean(dungeon_number + "_finished", true);
    }

    // Got the map? (integer replaced by a boolean).
    ++index;
    if (get_integer(index) > 0) {
      savegame_v2.set_boolean(dungeon_number + "_map", true);
    }

    // Got the compass? (integer replaced by a boolean).
    ++index;
    if (get_integer(index) > 0) {
      savegame_v2.set_boolean(dungeon_number + "_compass", true);
    }

    // Got the big key? (integer replaced by a boolean).
    ++index;
    if (get_integer(index) > 0) {
      savegame_v2.set_boolean(dungeon_number + "_big_key", true);
    }

    // Got the boss key? (integer replaced by a boolean).
    ++index;
    if (get_integer(index) > 0) {
      savegame_v2.set_boolean(dungeon_number + "_boss_key", true);
    }

    // Number of small keys.
    ++index;
    int nb_small_keys = get_integer(index);
    if (nb_small_keys > 0) {
      savegame_v2.set_integer(dungeon_number + "_small_keys", nb_small_keys);
    }
  }

  // 3. Custom values.
  int i;
  for (i = 32; i < 64; i++) {
    const std::string& value = get_string(i);
    if (!value.empty()) {
      std::ostringstream oss;
      oss << "s" << i;
      savegame_v2.set_string(oss.str(), value);
    }
  }

  for (i = 1024; i < 2048; i++) {
    int value = get_integer(i);
    if (value != 0) {
      std::ostringstream oss;
      oss << "i" << i;
      savegame_v2.set_integer(oss.str(), value);
    }
  }

  for (i = 0; i < 32768; i++) {
    bool value = get_boolean(i);
    if (value) {
      std::ostringstream oss;
      oss << "b" << i;
      savegame_v2.set_boolean(oss.str(), value);
    }
  }
}