/*
* Copyright 2010-2016 OpenXcom Developers.
*
* This file is part of OpenXcom.
*
* OpenXcom is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenXcom is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenXcom. If not, see <http://www.gnu.org/licenses/>.
*/
#include "SaveConverter.h"
#include <yaml-cpp/yaml.h>
#include <SDL_endian.h>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <bitset>
#include "../Engine/Options.h"
#include "../Engine/CrossPlatform.h"
#include "../Engine/Exception.h"
#include "../Engine/Language.h"
#include "../Mod/Mod.h"
#include "../Mod/RuleItem.h"
#include "SavedGame.h"
#include "GameTime.h"
#include "Country.h"
#include "Region.h"
#include "Base.h"
#include "BaseFacility.h"
#include "ItemContainer.h"
#include "Ufo.h"
#include "Craft.h"
#include "AlienBase.h"
#include "Waypoint.h"
#include "MissionSite.h"
#include "CraftWeapon.h"
#include "Transfer.h"
#include "Vehicle.h"
#include "AlienStrategy.h"
#include "AlienMission.h"
#include "../Mod/RuleResearch.h"
#include "../Mod/RuleRegion.h"
#include "../Mod/ArticleDefinition.h"
#include "ResearchProject.h"
#include "../Mod/RuleManufacture.h"
#include "Production.h"
#include "../Mod/UfoTrajectory.h"
#include "../Engine/RNG.h"
#include "../Mod/RuleConverter.h"
#include "../Ufopaedia/Ufopaedia.h"
#include "../fmath.h"
namespace OpenXcom
{
enum TargetType { TARGET_NONE, TARGET_UFO, TARGET_CRAFT, TARGET_XBASE, TARGET_ABASE, TARGET_CRASH, TARGET_LANDED, TARGET_WAYPOINT, TARGET_TERROR, TARGET_PORT = 0x51, TARGET_ISLAND = 0x52, TARGET_SHIP = 0x53, TARGET_ARTEFACT = 0x54 };
const char *xcomAltitudes[] = { "STR_GROUND", "STR_VERY_LOW", "STR_LOW_UC", "STR_HIGH_UC", "STR_VERY_HIGH" };
const char *xcomStatus[] = { "STR_READY", "STR_OUT", "STR_REPAIRS", "STR_REFUELLING", "STR_REARMING" };
// Helper functions
template <typename T> T load(char* data) { return *(T*)data; }
template <> Uint16 load(char* data) { return SDL_SwapLE16(*(Uint16*)data); }
template <> Sint16 load(char* data) { return SDL_SwapLE16(*(Sint16*)data); }
template <> int load(char* data) { return SDL_SwapLE32(*(int*)data); }
template <> unsigned int load(char* data) { return SDL_SwapLE32(*(unsigned int*)data); }
template <> std::string load(char* data) { return data; }
char *SaveConverter::binaryBuffer(const std::string &filename, std::vector<char> &buffer) const
{
std::string s = _savePath + CrossPlatform::PATH_SEPARATOR + filename;
std::ifstream datFile(s.c_str(), std::ios::in | std::ios::binary);
if (!datFile)
{
throw Exception(filename + " not found");
}
buffer = std::vector<char>((std::istreambuf_iterator<char>(datFile)), (std::istreambuf_iterator<char>()));
datFile.close();
return &buffer[0];
}
/**
* Creates a new converter for the given save folder.
* @param save Number of the save folder GAME_#
* @param mod Mod to associate with this save.
*/
SaveConverter::SaveConverter(int save, Mod *mod) : _save(0), _mod(mod), _rules(mod->getConverter()), _year(0), _funds(0)
{
std::ostringstream ssFolder, ssPath;
ssFolder << "GAME_" << save;
ssPath << Options::getMasterUserFolder() << ssFolder.str();
_saveName = ssFolder.str();
_savePath = ssPath.str();
ssPath << "/SAVEINFO.DAT";
if (!CrossPlatform::fileExists(ssPath.str()))
{
throw Exception(_saveName + " is not a valid save folder");
}
}
SaveConverter::~SaveConverter()
{
}
/**
* Gets all the info of the saves found in the user folder.
* @param lang Loaded language.
* @param info Returned list of saves info.
*/
void SaveConverter::getList(Language *lang, SaveOriginal info[NUM_SAVES])
{
for (int i = 0; i < NUM_SAVES; ++i)
{
SaveOriginal save;
save.id = 0;
int id = i + 1;
std::ostringstream ss;
ss << Options::getMasterUserFolder() << "GAME_" << id << CrossPlatform::PATH_SEPARATOR << "SAVEINFO.DAT";
std::ifstream datFile(ss.str().c_str(), std::ios::in | std::ios::binary);
if (datFile)
{
std::vector<char> buffer((std::istreambuf_iterator<char>(datFile)), (std::istreambuf_iterator<char>()));
char *data = &buffer[0];
std::string name = load<std::string>(data + 0x02);
int year = load<Uint16>(data + 0x1C);
int month = load<Uint16>(data + 0x1E);
int day = load<Uint16>(data + 0x20);
int hour = load<Uint16>(data + 0x22);
int minute = load<Uint16>(data + 0x24);
bool tactical = load<char>(data + 0x26) != 0;
GameTime time = GameTime(0, day, month + 1, year, hour, minute, 0);
std::ostringstream ssDate, ssTime;
ssDate << time.getDayString(lang) << " " << lang->getString(time.getMonthString()) << " " << time.getYear();
ssTime << time.getHour() << ":" << std::setfill('0') << std::setw(2) << time.getMinute();
save.id = id;
save.name = name;
save.date = ssDate.str();
save.time = ssTime.str();
save.tactical = tactical;
datFile.close();
}
info[i] = save;
}
}
/**
* Converts an original X-COM save into an OpenXcom save.
* @return New OpenXcom save.
*/
SavedGame *SaveConverter::loadOriginal()
{
_save = new SavedGame();
// Load globe data
_save->getIncomes().clear();
for (size_t i = 0; i < _rules->getCountries().size(); ++i)
{
Country *country = new Country(_mod->getCountry(_rules->getCountries()[i], true));
country->getActivityAlien().clear();
country->getActivityXcom().clear();
country->getFunding().clear();
_save->getCountries()->push_back(country);
}
for (size_t i = 0; i < _rules->getRegions().size(); ++i)
{
Region *region = new Region(_mod->getRegion(_rules->getRegions()[i], true));
region->getActivityAlien().clear();
region->getActivityXcom().clear();
_save->getRegions()->push_back(region);
}
loadDatXcom();
loadDatAlien();
loadDatDiplom();
loadDatLease();
// Load graph data
_save->getExpenditures().clear();
_save->getMaintenances().clear();
_save->getFundsList().clear();
_save->getResearchScores().clear();
loadDatLIGlob();
loadDatUIGlob();
loadDatIGlob();
// Load alien data
loadDatZonal();
loadDatActs();
loadDatMissions();
// Load player data
loadDatLoc();
loadDatBase();
loadDatAStore();
loadDatCraft();
loadDatSoldier();
loadDatTransfer();
loadDatResearch();
loadDatUp();
loadDatProject();
loadDatBProd();
loadDatXBases();
return _save;
}
/**
* Corrects vectors of graph data.
* Original X-COM uses months as array indexes,
* while OpenXcom stores month data in a linear fashion.
* @param vector Vector of graph data.
* @param month Current month.
* @param year Has game gone longer than a year?
*/
template <typename T>
void SaveConverter::graphVector(std::vector<T> &vector, int month, bool year)
{
if (year)
{
std::vector<T> newVector;
int i = month;
do
{
newVector.push_back(vector[i]);
i = (i + 1) % vector.size();
}
while (i != month);
vector = newVector;
}
else
{
vector.resize(month);
}
}
/**
* Loads the IGLOB.DAT file.
* Contains game date, time and difficulty.
*/
void SaveConverter::loadDatIGlob()
{
std::vector<char> buffer;
char *data = binaryBuffer("IGLOB.DAT", buffer);
int month = load<int>(data + 0x00) + 1;
int weekday = load<int>(data + 0x04) + 1;
int day = load<int>(data + 0x08);
int hour = load<int>(data + 0x0C);
int minute = load<int>(data + 0x10);
int second = load<int>(data + 0x14);
_save->setTime(GameTime(weekday, day, month, _year, hour, minute, second));
// account for difficulty bug
if (buffer.size() > 0x3C)
{
int coefficient = load<int>(data + 0x3C);
for (size_t i = DIFF_BEGINNER; i <= DIFF_SUPERHUMAN; ++i)
{
if (coefficient == Mod::DIFFICULTY_COEFFICIENT[i])
{
_save->setDifficulty((GameDifficulty)i);
break;
}
}
}
// Fix up the months
size_t monthsPassed = month + (_year - _mod->getStartingTime().getYear()) * 12;
for (size_t i = 0; i < monthsPassed; ++i)
{
_save->addMonth();
}
graphVector(_save->getIncomes(), month, _year != _mod->getStartingTime().getYear());
graphVector(_save->getExpenditures(), month, _year != _mod->getStartingTime().getYear());
graphVector(_save->getMaintenances(), month, _year != _mod->getStartingTime().getYear());
graphVector(_save->getFundsList(), month, _year != _mod->getStartingTime().getYear());
graphVector(_save->getResearchScores(), month, _year != _mod->getStartingTime().getYear());
for (size_t i = 0; i < _rules->getCountries().size(); ++i)
{
Country *country = _save->getCountries()->at(i);
graphVector(country->getActivityAlien(), month, _year != _mod->getStartingTime().getYear());
graphVector(country->getActivityXcom(), month, _year != _mod->getStartingTime().getYear());
graphVector(country->getFunding(), month, _year != _mod->getStartingTime().getYear());
}
for (size_t i = 0; i < _rules->getRegions().size(); ++i)
{
Region *region = _save->getRegions()->at(i);
graphVector(region->getActivityAlien(), month, _year != _mod->getStartingTime().getYear());
graphVector(region->getActivityXcom(), month, _year != _mod->getStartingTime().getYear());
}
_save->getFundsList().back() = _funds;
}
/**
* Loads the LIGLOB.DAT file.
* Contains financial data.
*/
void SaveConverter::loadDatLIGlob()
{
std::vector<char> buffer;
char *data = binaryBuffer("LIGLOB.DAT", buffer);
const size_t MONTHS = 12;
for (size_t i = 0; i < MONTHS; ++i)
{
int expenditure = load<int>(data + 0x04 + i * sizeof(int));
int maintenance = load<int>(data + 0x34 + i * sizeof(int));
int balance = load<int>(data + 0x64 + i * sizeof(int));
_save->getExpenditures().push_back(expenditure);
_save->getMaintenances().push_back(maintenance);
_save->getFundsList().push_back(balance);
}
_funds = load<int>(data);
}
/**
* Loads the UIGLOB.DAT file.
* Contains Geoscape number IDs and scores.
*/
void SaveConverter::loadDatUIGlob()
{
std::vector<char> buffer;
char *data = binaryBuffer("UIGLOB.DAT", buffer);
std::map<std::string, int> ids;
for (size_t i = 0; i < _rules->getMarkers().size(); ++i)
{
ids[_rules->getMarkers()[i]] = load<Uint16>(data + i * sizeof(Uint16));
}
ids["STR_CRASH_SITE"] = ids["STR_LANDING_SITE"] = ids["STR_UFO"];
_year = load<Uint16>(data + 0x16);
const size_t MONTHS = 12;
for (size_t i = 0; i < MONTHS; ++i)
{
int score = load<Sint16>(data + 0x18 + i * sizeof(Sint16));
_save->getResearchScores().push_back(score);
}
// Loads the SITE.DAT file (TFTD only).
std::string s = _savePath + CrossPlatform::PATH_SEPARATOR + "SITE.DAT";
if (CrossPlatform::fileExists(s))
{
std::vector<char> sitebuffer;
char* sitedata = binaryBuffer("SITE.DAT", sitebuffer);
int generatedArtifactSiteMissions = load<Uint16>(sitedata + 0x24);
if (generatedArtifactSiteMissions > 0)
{
_save->getAlienStrategy().addMissionRun("artifacts", generatedArtifactSiteMissions);
int spawnedArtifactSites = generatedArtifactSiteMissions;
char siteTypeToBeSpawned = load<char>(sitedata + 0x26);
if (siteTypeToBeSpawned == 'T')
{
// before the first hour of the month, the mission was generated already, but the site has not spawned yet
spawnedArtifactSites--;
}
else
{
// after the first hour of the month
// or not an artifact site type
}
ids["STR_ARTIFACT_SITE"] = spawnedArtifactSites + 1; // OXC stores the ID of the next site, thus +1
}
}
_save->setAllIds(ids);
}
/**
* Loads the LEASE.DAT file.
* Contains globe camera settings.
*/
void SaveConverter::loadDatLease()
{
std::vector<char> buffer;
char *data = binaryBuffer("LEASE.DAT", buffer);
double lat = -Xcom2Rad(load<Sint16>(data + 0x00));
double lon = -Xcom2Rad(load<Sint16>(data + 0x06));
_save->setGlobeLongitude(lon);
_save->setGlobeLatitude(lat);
int zoom = load<Sint16>(data + 0x0C);
const int DISTANCE[] = { 90, 120, 180, 360, 450, 720 };
for (size_t i = 0; i < 6; ++i)
{
if (zoom == DISTANCE[i])
{
_save->setGlobeZoom(i);
break;
}
}
}
/**
* Loads the XCOM.DAT file.
* Contains X-COM graph info.
*/
void SaveConverter::loadDatXcom()
{
std::vector<char> buffer;
char *data = binaryBuffer("XCOM.DAT", buffer);
const size_t ENTRIES = _rules->getCountries().size() + _rules->getRegions().size();
const size_t MONTHS = 12;
for (size_t i = 0; i < ENTRIES * MONTHS; ++i)
{
int score = load<int>(data + i * sizeof(int));
size_t j = i % ENTRIES;
// country
if (j < _rules->getCountries().size())
{
_save->getCountries()->at(j)->getActivityXcom().push_back(score);
}
// region
else
{
j -= _rules->getCountries().size();
_save->getRegions()->at(j)->getActivityXcom().push_back(score);
}
}
}
/**
* Loads the ALIEN.DAT file.
* Contains Alien graph info.
*/
void SaveConverter::loadDatAlien()
{
std::vector<char> buffer;
char *data = binaryBuffer("ALIEN.DAT", buffer);
const size_t ENTRIES = _rules->getCountries().size() + _rules->getRegions().size();
const size_t MONTHS = 12;
for (size_t i = 0; i < ENTRIES * MONTHS; ++i)
{
int score = load<int>(data + i * sizeof(int));
size_t j = i % ENTRIES;
// country
if (j < _rules->getCountries().size())
{
_save->getCountries()->at(j)->getActivityAlien().push_back(score);
}
// region
else
{
j -= _rules->getCountries().size();
_save->getRegions()->at(j)->getActivityAlien().push_back(score);
}
}
}
/**
* Loads the DIPLOM.DAT file.
* Contains country status.
*/
void SaveConverter::loadDatDiplom()
{
std::vector<char> buffer;
char *data = binaryBuffer("DIPLOM.DAT", buffer);
const size_t MONTHS = 12;
std::vector<int64_t> income;
for (size_t i = 0; i < MONTHS; ++i)
{
income.push_back(0);
}
const size_t ENTRY_SIZE = 36;
for (size_t i = 0; i < _rules->getCountries().size(); ++i)
{
char *cdata = (data + i * ENTRY_SIZE);
Country *country = _save->getCountries()->at(i);
int satisfaction = load<Sint16>(cdata + 0x02);
for (size_t j = 0; j < MONTHS; ++j)
{
int funding = load<Sint16>(cdata + 0x04 + j * sizeof(Sint16));
funding *= 1000;
income[j] += funding;
country->getFunding().push_back(funding);
}
bool pact = satisfaction == 0;
bool newPact = load<Sint16>(cdata + 0x1E) != 0;
if (pact)
country->setPact();
if (newPact)
country->setNewPact();
}
_save->getIncomes() = income;
}
/**
* Loads the ZONAL.DAT file.
* Contains alien region chances.
*/
void SaveConverter::loadDatZonal()
{
std::vector<char> buffer;
char *data = binaryBuffer("ZONAL.DAT", buffer);
std::map<std::string, int> chances;
const size_t REGIONS = 12;
for (size_t i = 0; i < REGIONS; ++i)
{
chances[_rules->getRegions()[i]] = load<Uint8>(data + i);
}
YAML::Node node;
node["regions"] = chances;
_save->getAlienStrategy().load(node);
}
/**
* Loads the ACTS.DAT file.
* Contains alien mission chances.
*/
void SaveConverter::loadDatActs()
{
std::vector<char> buffer;
char *data = binaryBuffer("ACTS.DAT", buffer);
std::map< std::string, std::map<std::string, int> > chances;
const size_t REGIONS = 12;
const size_t MISSIONS = 7;
for (size_t i = 0; i < REGIONS * MISSIONS; ++i)
{
size_t mission = i % MISSIONS;
size_t region = i / MISSIONS;
chances[_rules->getRegions()[region]][_rules->getMissions()[mission]] = load<Uint8>(data + i);
}
YAML::Node node;
for (std::map< std::string, std::map<std::string, int> >::iterator i = chances.begin(); i != chances.end(); ++i)
{
YAML::Node subnode;
subnode["region"] = i->first;
subnode["missions"] = i->second;
node["possibleMissions"].push_back(subnode);
}
_save->getAlienStrategy().load(node);
}
/**
* Loads the MISSIONS.DAT file.
* Contains ongoing alien missions.
*/
void SaveConverter::loadDatMissions()
{
std::vector<char> buffer;
char *data = binaryBuffer("MISSIONS.DAT", buffer);
const size_t REGIONS = 12;
const size_t MISSIONS = 7;
const size_t ENTRY_SIZE = 8;
for (size_t i = 0; i < REGIONS * MISSIONS; ++i)
{
char *mdata = (data + i * ENTRY_SIZE);
int wave = load<Uint16>(mdata + 0x00);
if (wave != 0xFFFF)
{
int ufoCounter = load<Uint16>(mdata + 0x02);
int spawn = load<Uint16>(mdata + 0x04);
int race = load<Uint16>(mdata + 0x06);
int mission = i % MISSIONS;
int region = i / MISSIONS;
YAML::Node node;
AlienMission *m = new AlienMission(*_mod->getAlienMission(_rules->getMissions()[mission], true));
node["region"] = _rules->getRegions()[region];
node["race"] = _rules->getCrews()[race];
node["nextWave"] = wave;
node["nextUfoCounter"] = ufoCounter;
node["spawnCountdown"] = spawn * 30;
node["uniqueID"] = _save->getId("ALIEN_MISSIONS");
if (m->getRules().getObjective() == OBJECTIVE_SITE)
{
int missionZone = 3; // pick a city for terror missions
RuleRegion *rule = _mod->getRegion(_rules->getRegions()[region], true);
if (rule->getMissionZones().size() <= 3)
{
// try to account for TFTD's artefacts and such
missionZone = 0;
}
node["missionSiteZone"] = RNG::generate(0, rule->getMissionZones().at(missionZone).areas.size() - 1);
}
m->load(node, *_save);
_save->getAlienMissions().push_back(m);
_missions[std::make_pair(mission, region)] = m;
}
}
}
/**
* Loads the LOC.DAT file.
* Contains globe markers.
*/
void SaveConverter::loadDatLoc()
{
std::vector<char> buffer;
char *data = binaryBuffer("LOC.DAT", buffer);
const size_t ENTRIES = 50;
const size_t ENTRY_SIZE = buffer.size() / ENTRIES;
for (size_t i = 0; i < ENTRIES; ++i)
{
char *tdata = (data + i * ENTRY_SIZE);
TargetType type = (TargetType)load<Uint8>(tdata);
int dat = load<Uint8>(tdata + 0x01);
double lon = Xcom2Rad(load<Sint16>(tdata + 0x02));
double lat = Xcom2Rad(load<Sint16>(tdata + 0x04));
int timer = load<Sint16>(tdata + 0x06);
int id = load<Sint16>(tdata + 0x0A);
std::bitset<3> visibility(load<int>(tdata + 0x10));
bool detected = !visibility.test(0);
// can't declare variables in switches :(
Target *target = 0;
Ufo *ufo = 0;
Craft *craft = 0;
Base *xbase = 0;
AlienBase *abase = 0;
Waypoint *waypoint = 0;
MissionSite *mission = 0;
switch (type)
{
case TARGET_NONE:
target = 0;
break;
case TARGET_UFO:
case TARGET_CRASH:
case TARGET_LANDED:
ufo = new Ufo(_mod->getUfo(_rules->getUfos()[0], true));
ufo->setId(id);
ufo->setCrashId(id);
ufo->setLandId(id);
ufo->setSecondsRemaining(timer);
ufo->setDetected(detected);
target = ufo;
break;
case TARGET_CRAFT:
craft = new Craft(_mod->getCraft(_rules->getCrafts()[0], true), 0, id);
target = craft;
break;
case TARGET_XBASE:
xbase = new Base(_mod);
target = xbase;
break;
case TARGET_ABASE:
abase = new AlienBase(_mod->getDeployment("STR_ALIEN_BASE_ASSAULT", true));
abase->setId(id);
abase->setAlienRace(_rules->getCrews()[dat]);
abase->setDiscovered(detected);
_save->getAlienBases()->push_back(abase);
target = abase;
break;
case TARGET_WAYPOINT:
waypoint = new Waypoint();
waypoint->setId(id);
_save->getWaypoints()->push_back(waypoint);
target = waypoint;
break;
case TARGET_TERROR:
mission = new MissionSite(_mod->getAlienMission("STR_ALIEN_TERROR", true), _mod->getDeployment("STR_TERROR_MISSION", true));
break;
case TARGET_PORT:
mission = new MissionSite(_mod->getAlienMission("STR_ALIEN_SURFACE_ATTACK", true), _mod->getDeployment("STR_PORT_TERROR", true));
break;
case TARGET_ISLAND:
mission = new MissionSite(_mod->getAlienMission("STR_ALIEN_SURFACE_ATTACK", true), _mod->getDeployment("STR_ISLAND_TERROR", true));
break;
case TARGET_SHIP:
mission = new MissionSite(_mod->getAlienMission("STR_ALIEN_SHIP_ATTACK", true), _mod->getDeployment("STR_CARGO_SHIP_P1", true));
break;
case TARGET_ARTEFACT:
mission = new MissionSite(_mod->getAlienMission("STR_ALIEN_ARTIFACT", true), _mod->getDeployment("STR_ARTIFACT_SITE_P1", true));
break;
}
if (mission != 0)
{
mission->setId(id);
mission->setAlienRace(_rules->getCrews()[dat]);
mission->setSecondsRemaining(timer * 3600);
mission->setDetected(detected);
_save->getMissionSites()->push_back(mission);
target = mission;
}
if (target != 0)
{
target->setLongitude(lon);
target->setLatitude(lat);
}
_targets.push_back(target);
_targetDat.push_back(dat);
}
}
/**
* Loads the BASE.DAT file.
* Contains X-COM base contents.
*/
void SaveConverter::loadDatBase()
{
std::vector<char> buffer;
char *data = binaryBuffer("BASE.DAT", buffer);
const size_t BASES = 8;
const size_t BASE_SIZE = 6;
const size_t FACILITIES = BASE_SIZE * BASE_SIZE;
const size_t ENTRY_SIZE = buffer.size() / BASES;
std::vector<Base*> bases(BASES, 0);
for (size_t i = 0; i < _targets.size(); ++i)
{
Base *base = dynamic_cast<Base*>(_targets[i]);
if (base != 0)
{
int j = _targetDat[i];
char *bdata = (data + j * ENTRY_SIZE);
std::string name = load<std::string>(bdata);
// facilities
for (size_t k = 0; k < FACILITIES; ++k)
{
size_t facilityType = load<Uint8>(bdata + _rules->getOffset("BASE.DAT_FACILITIES") + k);
if (facilityType < _rules->getFacilities().size())
{
BaseFacility *facility = new BaseFacility(_mod->getBaseFacility(_rules->getFacilities()[facilityType], true), base);
int x = k % BASE_SIZE;
int y = k / BASE_SIZE;
int days = load<Uint8>(bdata + _rules->getOffset("BASE.DAT_FACILITIES") + FACILITIES + k);
facility->setX(x);
facility->setY(y);
facility->setBuildTime(days);
base->getFacilities()->push_back(facility);
}
}
int engineers = load<Uint8>(bdata + _rules->getOffset("BASE.DAT_ENGINEERS"));
int scientists = load<Uint8>(bdata + _rules->getOffset("BASE.DAT_SCIENTISTS"));
// items
for (size_t k = 0; k < _rules->getItems().size(); ++k)
{
int qty = load<Uint16>(bdata + _rules->getOffset("BASE.DAT_ITEMS") + k * 2);
if (qty != 0 && !_rules->getItems()[k].empty())
{
base->getStorageItems()->addItem(_rules->getItems()[k], qty);
}
}
base->setEngineers(engineers);
base->setScientists(scientists);
base->setName(name);
bases[j] = base;
}
}
for (std::vector<Base*>::iterator i = bases.begin(); i != bases.end(); ++i)
{
if (*i != 0)
{
_save->getBases()->push_back(*i);
}
}
}
/**
* Loads the ASTORE.DAT file.
* Contains X-COM alien containment.
*/
void SaveConverter::loadDatAStore()
{
std::vector<char> buffer;
char *data = binaryBuffer("ASTORE.DAT", buffer);
const size_t ENTRY_SIZE = 12;
const size_t ENTRIES = buffer.size() / ENTRY_SIZE;
for (size_t i = 0; i < ENTRIES; ++i)
{
char *adata = (data + i * ENTRY_SIZE);
int race = load<Uint8>(adata + 0x00);
std::string liveAlien;
if (race != 0)
{
int rank = load<Uint8>(adata + 0x01);
int base = load<Uint8>(adata + 0x02);
liveAlien = _rules->getAlienRaces()[race];
liveAlien += _rules->getAlienRanks()[rank];
if (base != 0xFF)
{
Base *b = dynamic_cast<Base*>(_targets[base]);
b->getStorageItems()->addItem(liveAlien);
}
}
_aliens.push_back(liveAlien);
}
}
/**
* Loads the TRANSFER.DAT file.
* Contains transfers to X-COM bases.
*/
void SaveConverter::loadDatTransfer()
{
std::vector<char> buffer;
char *data = binaryBuffer("TRANSFER.DAT", buffer);
const size_t ENTRY_SIZE = 8;
const size_t ENTRIES = buffer.size() / ENTRY_SIZE;
for (size_t i = 0; i < ENTRIES; ++i)
{
char *tdata = (data + i * ENTRY_SIZE);
int qty = load<Uint8>(tdata + 0x06);
if (qty != 0)
{
int baseSrc = load<Uint8>(tdata + 0x00);
int baseDest = load<Uint8>(tdata + 0x01);
Base *b = dynamic_cast<Base*>(_targets[baseDest]);
int hours = load<Uint8>(tdata + 0x02);
TransferType type = (TransferType)load<Uint8>(tdata + 0x03);
int dat = load<Uint8>(tdata + 0x04);
Transfer *transfer = new Transfer(hours);
switch (type)
{
case TRANSFER_CRAFT:
if (baseSrc == 255)
{
std::string newCraft = _rules->getCrafts()[dat];
transfer->setCraft(new Craft(_mod->getCraft(newCraft, true), b, _save->getId(newCraft)));
}
else
{
transfer->setCraft(dynamic_cast<Craft*>(_targets[dat]));
}
break;
case TRANSFER_SOLDIER:
transfer->setSoldier(_soldiers[dat]);
break;
case TRANSFER_SCIENTIST:
transfer->setScientists(qty);
break;
case TRANSFER_ENGINEER:
transfer->setEngineers(qty);
break;
default:
if (type == TRANSFER_ITEM)
transfer->setItems(_rules->getItems()[dat], qty);
else
transfer->setItems(_aliens[dat]);
break;
}
b->getTransfers()->push_back(transfer);
}
}
}
/**
* Loads the CRAFT.DAT file.
* Contains X-COM craft and Alien UFOs.
*/
void SaveConverter::loadDatCraft()
{
std::vector<char> buffer;
char *data = binaryBuffer("CRAFT.DAT", buffer);
const size_t ENTRY_SIZE = buffer.size() / _targets.size();
for (size_t i = 0; i < _targets.size(); ++i)
{
int j = _targetDat[i];
char *cdata = (data + j * ENTRY_SIZE);
int type = load<Uint8>(cdata);
if (type != 0xFF)
{
YAML::Node node;
Craft *craft = dynamic_cast<Craft*>(_targets[i]);
if (craft != 0)
{
craft->changeRules(_mod->getCraft(_rules->getCrafts()[type], true));
int lweapon = load<Uint8>(cdata + _rules->getOffset("CRAFT.DAT_LEFT_WEAPON"));
int lammo = load<Uint16>(cdata + _rules->getOffset("CRAFT.DAT_LEFT_AMMO"));
if (lweapon != 0xFF)
{
CraftWeapon *cw = new CraftWeapon(_mod->getCraftWeapon(_rules->getCraftWeapons()[lweapon], true), lammo);
craft->getWeapons()->at(0) = cw;
}
int flight = load<Uint8>(cdata + _rules->getOffset("CRAFT.DAT_FLIGHT"));
int rweapon = load<Uint8>(cdata + _rules->getOffset("CRAFT.DAT_RIGHT_WEAPON"));
int rammo = load<Uint8>(cdata + _rules->getOffset("CRAFT.DAT_RIGHT_AMMO"));
if (rweapon != 0xFF)
{
CraftWeapon *cw = new CraftWeapon(_mod->getCraftWeapon(_rules->getCraftWeapons()[rweapon], true), rammo);
craft->getWeapons()->at(1) = cw;
}
node["damage"] = (int)load<Uint16>(cdata + _rules->getOffset("CRAFT.DAT_DAMAGE"));
node["speed"] = (int)load<Uint16>(cdata + _rules->getOffset("CRAFT.DAT_SPEED"));
int dest = load<Uint16>(cdata + _rules->getOffset("CRAFT.DAT_DESTINATION"));
node["fuel"] = (int)load<Uint16>(cdata + _rules->getOffset("CRAFT.DAT_FUEL"));
int base = load<Uint16>(cdata + _rules->getOffset("CRAFT.DAT_BASE"));
node["status"] = xcomStatus[load<Uint16>(cdata + _rules->getOffset("CRAFT.DAT_STATUS"))];
// vehicles
const size_t VEHICLES = 5;
for (size_t k = 0; k < VEHICLES; ++k)
{
int qty = load<Uint8>(cdata + _rules->getOffset("CRAFT.DAT_ITEMS") + k);
for (int v = 0; v < qty; ++v)
{
RuleItem *rule = _mod->getItem(_rules->getItems()[k + 10], true);
craft->getVehicles()->push_back(new Vehicle(rule, rule->getClipSize(), 4));
}
}
// items
const size_t ITEMS = 50;
for (size_t k = VEHICLES; k < VEHICLES + ITEMS; ++k)
{
int qty = load<Uint8>(cdata + _rules->getOffset("CRAFT.DAT_ITEMS") + k);
if (qty != 0 && !_rules->getItems()[k + 10].empty())
{
craft->getItems()->addItem(_rules->getItems()[k + 10], qty);
}
}
std::bitset<7> state(load<int>(cdata + _rules->getOffset("CRAFT.DAT_STATE")));
node["lowFuel"] = state.test(1);
craft->load(node, _mod, _save);
if (flight != 0 && dest != 0xFFFF)
{
Target *t = _targets[dest];
craft->setDestination(t);
}
if (base != 0xFFFF)
{
Base *b = dynamic_cast<Base*>(_targets[base]);
craft->setBase(b, false);
b->getCrafts()->push_back(craft);
}
}
Ufo *ufo = dynamic_cast<Ufo*>(_targets[i]);
if (ufo != 0)
{
ufo->changeRules(_mod->getUfo(_rules->getUfos()[type - 5], true));
node["damage"] = (int)load<Uint16>(cdata + _rules->getOffset("CRAFT.DAT_DAMAGE"));
node["altitude"] = xcomAltitudes[load<Uint16>(cdata + _rules->getOffset("CRAFT.DAT_ALTITUDE"))];
node["speed"] = (int)load<Uint16>(cdata + _rules->getOffset("CRAFT.DAT_SPEED"));
node["dest"]["lon"] = Xcom2Rad(load<Sint16>(cdata + _rules->getOffset("CRAFT.DAT_DEST_LON")));
node["dest"]["lat"] = Xcom2Rad(load<Sint16>(cdata + _rules->getOffset("CRAFT.DAT_DEST_LAT")));
int mission = load<Uint16>(cdata + _rules->getOffset("CRAFT.DAT_MISSION"));
int region = load<Uint16>(cdata + _rules->getOffset("CRAFT.DAT_REGION"));
std::ostringstream trajectory;
AlienMission *m = _missions[std::make_pair(mission, region)];
if (m == 0)
{
YAML::Node subnode;
m = new AlienMission(*_mod->getAlienMission(_rules->getMissions()[mission], true));
subnode["region"] = _rules->getRegions()[region];
subnode["race"] = _rules->getCrews()[load<Uint16>(cdata + _rules->getOffset("CRAFT.DAT_RACE"))];
subnode["nextWave"] = 1;
subnode["nextUfoCounter"] = 0;
subnode["spawnCountdown"] = 1000;
subnode["uniqueID"] = _save->getId("ALIEN_MISSIONS");
m->load(subnode, *_save);
_save->getAlienMissions().push_back(m);
_missions[std::make_pair(mission, region)] = m;
if (mission == 6)
{
trajectory << UfoTrajectory::RETALIATION_ASSAULT_RUN;
}
}
node["mission"] = m->getId();
m->increaseLiveUfos();
if (trajectory.str().empty())
{
trajectory << "P" << load<Uint16>(cdata + _rules->getOffset("CRAFT.DAT_TRAJECTORY"));
}
node["trajectory"] = trajectory.str();
node["trajectoryPoint"] = (int)load<Uint16>(cdata + _rules->getOffset("CRAFT.DAT_TRAJECTORY_POINT"));
std::bitset<7> state(load<int>(cdata + _rules->getOffset("CRAFT.DAT_STATE")));
node["hyperDetected"] = state.test(6);
ufo->load(node, *_mod, *_save);
ufo->setSpeed(ufo->getSpeed());
if (ufo->getStatus() == Ufo::CRASHED)
{
ufo->setSecondsRemaining(ufo->getSecondsRemaining() * 3600);
}
else if (ufo->getStatus() == Ufo::LANDED)
{
ufo->setSecondsRemaining(ufo->getSecondsRemaining() * 5);
}
else
{
ufo->setSecondsRemaining(0);
}
_save->getUfos()->push_back(ufo);
}
}
}
}
/**
* Loads the SOLDIER.DAT file.
* Contains X-COM soldiers.
*/
void SaveConverter::loadDatSoldier()
{
std::vector<char> buffer;
char *data = binaryBuffer("SOLDIER.DAT", buffer);
const size_t SOLDIERS = 250;
const size_t ENTRY_SIZE = buffer.size() / SOLDIERS;
for (size_t i = 0; i < SOLDIERS; ++i)
{
char *sdata = (data + i * ENTRY_SIZE);
int rank = load<Uint16>(sdata + _rules->getOffset("SOLDIER.DAT_RANK"));
if (rank != 0xFFFF)
{
YAML::Node node;
int base = load<Uint16>(sdata + _rules->getOffset("SOLDIER.DAT_BASE"));
int craft = load<Uint16>(sdata + _rules->getOffset("SOLDIER.DAT_CRAFT"));
node["missions"] = (int)load<Sint16>(sdata + _rules->getOffset("SOLDIER.DAT_MISSIONS"));
node["kills"] = (int)load<Sint16>(sdata + _rules->getOffset("SOLDIER.DAT_KILLS"));
node["recovery"] = (int)load<Sint16>(sdata + _rules->getOffset("SOLDIER.DAT_RECOVERY"));
node["name"] = load<std::string>(sdata + _rules->getOffset("SOLDIER.DAT_NAME"));
node["rank"] = rank;
UnitStats initial;
initial.tu = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_INITIAL_TU"));
initial.health = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_INITIAL_HE"));
initial.stamina = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_INITIAL_STA"));
initial.reactions = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_INITIAL_RE"));
initial.strength = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_INITIAL_STR"));
initial.firing = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_INITIAL_FA"));
initial.throwing = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_INITIAL_TA"));
initial.melee = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_INITIAL_ME"));
initial.psiStrength = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_INITIAL_PST"));
initial.psiSkill = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_INITIAL_PSK"));
initial.bravery = 110 - (10 * load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_INITIAL_BR")));
node["initialStats"] = initial;
UnitStats current;
current.tu = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_IMPROVED_TU"));
current.health = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_IMPROVED_HE"));
current.stamina = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_IMPROVED_STA"));
current.reactions = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_IMPROVED_RE"));
current.strength = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_IMPROVED_STR"));
current.firing = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_IMPROVED_FA"));
current.throwing = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_IMPROVED_TA"));
current.melee = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_IMPROVED_ME"));
current.psiStrength = 0;
current.psiSkill = 0;
current.bravery = 10 * load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_IMPROVED_BR"));
current += initial;
node["currentStats"] = current;
int armor = load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_ARMOR"));
node["armor"] = _rules->getArmor()[armor];
node["improvement"] = (int)load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_PSI"));
node["psiTraining"] = (int)load<char>(sdata + _rules->getOffset("SOLDIER.DAT_PSILAB")) != 0;
node["gender"] = (int)load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_GENDER"));
node["look"] = (int)load<Uint8>(sdata + _rules->getOffset("SOLDIER.DAT_LOOK"));
node["id"] = _save->getId("STR_SOLDIER");
Soldier *soldier = new Soldier(_mod->getSoldier(_mod->getSoldiersList().front(), true), 0);
soldier->load(node, _mod, _save);
if (base != 0xFFFF)
{
Base *b = dynamic_cast<Base*>(_targets[base]);
b->getSoldiers()->push_back(soldier);
}
if (craft != 0xFFFF)
{
Craft *c = dynamic_cast<Craft*>(_targets[craft]);
soldier->setCraft(c);
}
_soldiers.push_back(soldier);
}
else
{
_soldiers.push_back(0);
}
}
}
/**
* Loads the RESEARCH.DAT file.
* Contains X-COM research progress.
*/
void SaveConverter::loadDatResearch()
{
std::vector<char> buffer;
char *data = binaryBuffer("RESEARCH.DAT", buffer);
const size_t ENTRY_SIZE = buffer.size() / _rules->getResearch().size();
for (size_t i = 0; i < _rules->getResearch().size(); ++i)
{
char *rdata = (data + i * ENTRY_SIZE);
if (!_rules->getResearch()[i].empty())
{
RuleResearch *research = _mod->getResearch(_rules->getResearch()[i]);
if (research != 0 && research->getCost() != 0)
{
bool discovered = load<Uint8>(rdata + 0x0A) != 0;
bool popped = load<Uint8>(rdata + 0x12) != 0;
if (discovered)
{
_save->addFinishedResearch(research, _mod, 0, false);
}
else if (popped)
{
_save->addPoppedResearch(research);
}
}
}
}
}
/**
* Loads the UP.DAT file.
* Contains X-COM Ufopaedia progress.
*/
void SaveConverter::loadDatUp()
{
std::vector<char> buffer;
char *data = binaryBuffer("UP.DAT", buffer);
const size_t ENTRY_SIZE = buffer.size() / _rules->getUfopaedia().size();
for (size_t i = 0; i < _rules->getUfopaedia().size(); ++i)
{
char *rdata = (data + i * ENTRY_SIZE);
ArticleDefinition *article = _mod->getUfopaediaArticle(_rules->getUfopaedia()[i]);
if (article != 0 && article->section != UFOPAEDIA_NOT_AVAILABLE)
{
bool discovered = load<Uint8>(rdata + 0x08) == 2;
if (discovered)
{
std::vector<std::string> requires = article->requires;
for (std::vector<std::string>::const_iterator r = requires.begin(); r != requires.end(); ++r)
{
RuleResearch *research = _mod->getResearch(*r);
if (research && research->getCost() == 0)
{
_save->addFinishedResearch(research, _mod, 0, false);
}
}
}
}
}
}
/**
* Loads the PROJECT.DAT file.
* Contains X-COM research projects.
*/
void SaveConverter::loadDatProject()
{
std::vector<char> buffer;
char *data = binaryBuffer("PROJECT.DAT", buffer);
const size_t ENTRIES = _rules->getResearch().size();
// days (Uint16) | scientists (Uint8)
const size_t ENTRY_SIZE = ENTRIES * (sizeof(Uint16) + sizeof(Uint8));
for (size_t i = 0; i < _save->getBases()->size(); ++i)
{
Base *base = _save->getBases()->at(i);
char *pdata = (data + i * ENTRY_SIZE);
Uint16 *arrRemaining = (Uint16*)pdata;
Uint8 *arrScientists = (Uint8*)(&arrRemaining[ENTRIES]);
for (size_t j = 0; j < _rules->getResearch().size(); ++j)
{
int remaining = load<Uint16>((char*)&arrRemaining[j]);
int scientists = load<Uint8>((char*)&arrScientists[j]);
if (remaining != 0 && !_rules->getResearch()[j].empty())
{
RuleResearch *research = _mod->getResearch(_rules->getResearch()[j]);
if (research != 0 && research->getCost() != 0)
{
ResearchProject *project = new ResearchProject(research, research->getCost());
project->setAssigned(scientists);
project->setSpent(project->getCost() - remaining);
base->addResearch(project);
base->setScientists(base->getScientists() - scientists);
}
}
}
}
}
/**
* Loads the BPROD.DAT file.
* Contains X-COM manufacture projects.
*/
void SaveConverter::loadDatBProd()
{
std::vector<char> buffer;
char *data = binaryBuffer("BPROD.DAT", buffer);
const size_t ENTRIES = _rules->getManufacture().size();
// hours (int) | engineers (Uint16) | quantity (Uint16)| produced (Uint16)
const size_t ENTRY_SIZE = ENTRIES * (sizeof(int) + 3 * sizeof(Uint16));
for (size_t i = 0; i < _save->getBases()->size(); ++i)
{
Base *base = _save->getBases()->at(i);
char *pdata = (data + i * ENTRY_SIZE);
int *arrRemaining = (int*)pdata;
Uint16 *arrEngineers = (Uint16*)(&arrRemaining[ENTRIES]);
Uint16 *arrTotal = (Uint16*)(&arrEngineers[ENTRIES]);
Uint16 *arrProduced = (Uint16*)(&arrTotal[ENTRIES]);
for (size_t j = 0; j < _rules->getManufacture().size(); ++j)
{
int remaining = load<int>((char*)&arrRemaining[j]);
int engineers = load<Uint16>((char*)&arrEngineers[j]);
int total = load<Uint16>((char*)&arrTotal[j]);
int produced = load<Uint16>((char*)&arrProduced[j]);
if (remaining != 0 && !_rules->getManufacture()[j].empty())
{
RuleManufacture *manufacture = _mod->getManufacture(_rules->getManufacture()[j]);
if (manufacture != 0)
{
Production *project = new Production(manufacture, total);
project->setAssignedEngineers(engineers);
project->setTimeSpent(produced * manufacture->getManufactureTime() + manufacture->getManufactureTime() - remaining);
base->addProduction(project);
base->setEngineers(base->getEngineers() - engineers);
}
}
}
}
}
/**
* Loads the XBASES.DAT file.
* Contains targeted X-COM bases.
*/
void SaveConverter::loadDatXBases()
{
std::vector<char> buffer;
char *data = binaryBuffer("XBASES.DAT", buffer);
const size_t REGIONS = 12;
for (size_t i = 0; i < REGIONS; ++i)
{
char *bdata = (data + i * 4);
bool detected = load<Uint16>(bdata + 0x00) != 0;
if (detected)
{
int loc = load<Uint16>(bdata + 0x02);
Base *base = dynamic_cast<Base*>(_targets[loc]);
if (base != 0)
{
base->setRetaliationTarget(true);
}
}
}
}
}
↑ V522 There might be dereferencing of a potential null pointer 'b'.
↑ V522 There might be dereferencing of a potential null pointer 'b'.
↑ V522 There might be dereferencing of a potential null pointer 'b'.
↑ V522 There might be dereferencing of a potential null pointer 'b'.
↑ V601 The bool type is implicitly cast to the class type.
↑ V601 The bool type is implicitly cast to the class type.
↑ V601 The bool type is implicitly cast to the class type.