Add CityMania savegame data (a bit broken for now)
This commit is contained in:
@@ -1193,6 +1193,8 @@ sound/null_s.cpp
|
||||
thread.h
|
||||
|
||||
# CityMania
|
||||
citymania/cm_bitstream.hpp
|
||||
citymania/cm_bitstream.cpp
|
||||
citymania/cm_console_cmds.hpp
|
||||
citymania/cm_console_cmds.cpp
|
||||
citymania/cm_event.hpp
|
||||
@@ -1203,5 +1205,8 @@ citymania/cm_game.cpp
|
||||
citymania/cm_main.hpp
|
||||
citymania/cm_main.cpp
|
||||
citymania/cm_type.hpp
|
||||
citymania/cm_saveload.hpp
|
||||
citymania/cm_saveload.cpp
|
||||
citymania/cm_settings.hpp
|
||||
citymania/extensions/cmext_town.hpp
|
||||
citymania/extensions/cmext_company.hpp
|
||||
|
||||
87
src/citymania/cm_bitstream.cpp
Normal file
87
src/citymania/cm_bitstream.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
#include "../stdafx.h"
|
||||
|
||||
#include "cm_bitstream.hpp"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
namespace citymania {
|
||||
|
||||
// Returns 8 higher bits of s-bit integer x
|
||||
#define FIRST8(x, s) (((x) >> ((s) - 8)) & 0xFF)
|
||||
|
||||
void BitOStream::WriteBytes(uint32 value, int amount)
|
||||
{
|
||||
do {
|
||||
v = (v << 8) | FIRST8(value, amount << 3);
|
||||
f.push_back(FIRST8(v, c + 8));
|
||||
} while (--amount);
|
||||
}
|
||||
|
||||
void BitOStream::WriteBytes64(uint64 value, int amount)
|
||||
{
|
||||
do {
|
||||
v = (v << 8) | FIRST8(value, amount << 3);
|
||||
f.push_back(FIRST8(v, c + 8));
|
||||
} while (--amount);
|
||||
}
|
||||
|
||||
void BitOStream::WriteMoney(Money value) {
|
||||
this->WriteBytes64(value, 8);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reserves memory for encoded data. Use to avoid reallocation of vector.
|
||||
*/
|
||||
void BitOStream::Reserve(int bytes)
|
||||
{
|
||||
f.reserve(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns encoded data
|
||||
*/
|
||||
const u8vector &BitOStream::GetVector()
|
||||
{
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Size of encoded data in bytes
|
||||
*/
|
||||
uint BitOStream::GetByteSize() const
|
||||
{
|
||||
return f.size() + (c ? 1 : 0);
|
||||
}
|
||||
|
||||
BitIStreamUnexpectedEnd _bit_i_stream_unexpected_end;
|
||||
|
||||
uint32 BitIStream::ReadBytes(uint amount)
|
||||
{
|
||||
uint32 res = 0;
|
||||
while (amount--) {
|
||||
if (i >= f.size()) {
|
||||
throw _bit_i_stream_unexpected_end;
|
||||
}
|
||||
res = (res << 8) | f[i++];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint64 BitIStream::ReadBytes64(uint amount)
|
||||
{
|
||||
uint64 res = 0;
|
||||
while (amount--) {
|
||||
if (i >= f.size()) {
|
||||
throw _bit_i_stream_unexpected_end;
|
||||
}
|
||||
res = (res << 8) | f[i++];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Money BitIStream::ReadMoney() {
|
||||
return (Money)this->ReadBytes64(8);
|
||||
}
|
||||
|
||||
} // namespace citymania
|
||||
49
src/citymania/cm_bitstream.hpp
Normal file
49
src/citymania/cm_bitstream.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef BITSTREAM_H
|
||||
#define BITSTREAM_H
|
||||
|
||||
#include "../economy_type.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace citymania {
|
||||
|
||||
typedef std::vector<uint8> u8vector;
|
||||
|
||||
class BitOStream {
|
||||
protected:
|
||||
u8vector f;
|
||||
uint32 c;
|
||||
uint32 v;
|
||||
|
||||
public:
|
||||
BitOStream(): c(0), v(0) {}
|
||||
virtual ~BitOStream(){}
|
||||
void Reserve(int bytes);
|
||||
void WriteBytes(uint32 value, int amount);
|
||||
void WriteBytes64(uint64 value, int amount);
|
||||
void WriteMoney(Money value);
|
||||
const u8vector &GetVector();
|
||||
uint GetByteSize() const;
|
||||
};
|
||||
|
||||
class BitIStreamUnexpectedEnd: public std::exception {
|
||||
virtual const char* what() const throw() {
|
||||
return "Unexpected end of bit input stream";
|
||||
}
|
||||
};
|
||||
|
||||
class BitIStream {
|
||||
protected:
|
||||
u8vector &f;
|
||||
uint32 i;
|
||||
public:
|
||||
BitIStream(u8vector &data): f(data), i(0) {}
|
||||
virtual ~BitIStream(){}
|
||||
uint32 ReadBytes(uint amount);
|
||||
uint64 ReadBytes64(uint amount);
|
||||
Money ReadMoney();
|
||||
};
|
||||
|
||||
} // namespace citymania
|
||||
|
||||
#endif
|
||||
@@ -19,12 +19,11 @@ enum class TownGrowthTileState : uint8 {
|
||||
};
|
||||
|
||||
class Game {
|
||||
protected:
|
||||
public:
|
||||
typedef std::map<TileIndex, TownGrowthTileState> TownsGrowthTilesIndex;
|
||||
TownsGrowthTilesIndex towns_growth_tiles_last_month;
|
||||
TownsGrowthTilesIndex towns_growth_tiles;
|
||||
|
||||
public:
|
||||
event::Dispatcher events;
|
||||
|
||||
Game();
|
||||
|
||||
440
src/citymania/cm_saveload.cpp
Normal file
440
src/citymania/cm_saveload.cpp
Normal file
@@ -0,0 +1,440 @@
|
||||
#include "../stdafx.h"
|
||||
|
||||
#include "../company_base.h"
|
||||
#include "../date_func.h"
|
||||
#include "../debug.h"
|
||||
#include "../saveload/saveload.h"
|
||||
#include "../town.h"
|
||||
|
||||
#include "cm_bitstream.hpp"
|
||||
#include "cm_saveload.hpp"
|
||||
#include "cm_game.hpp"
|
||||
#include "cm_main.hpp"
|
||||
|
||||
namespace citymania {
|
||||
|
||||
static const uint SAVEGAME_DATA_FORMAT_VERSION = 2;
|
||||
static const uint CITYMANIA_GRFID = 0x534B0501U; // Luukland_Citybuilder grf id actually
|
||||
|
||||
|
||||
// copied form storage_sl.cpp
|
||||
static const SaveLoad _storage_desc[] = {
|
||||
SLE_CONDVAR(PersistentStorage, grfid, SLE_UINT32, SLV_6, SL_MAX_VERSION),
|
||||
SLE_CONDARR(PersistentStorage, storage, SLE_UINT32, 16, SLV_161, SLV_EXTEND_PERSISTENT_STORAGE),
|
||||
SLE_CONDARR(PersistentStorage, storage, SLE_UINT32, 256, SLV_EXTEND_PERSISTENT_STORAGE, SL_MAX_VERSION),
|
||||
SLE_END()
|
||||
};
|
||||
|
||||
|
||||
static void EncodeTowns(BitOStream &bs)
|
||||
{
|
||||
for (const Town *t : Town::Iterate()) {
|
||||
bs.WriteBytes(t->cm.growing_by_chance, 1);
|
||||
bs.WriteBytes(t->cm.houses_reconstructed_this_month, 2);
|
||||
bs.WriteBytes(t->cm.houses_reconstructed_last_month, 2);
|
||||
bs.WriteBytes(t->cm.houses_demolished_this_month, 2);
|
||||
bs.WriteBytes(t->cm.houses_demolished_last_month, 2);
|
||||
bs.WriteBytes(t->cm.hs_total, 4);
|
||||
bs.WriteBytes(t->cm.hs_total_prev, 2);
|
||||
bs.WriteBytes(t->cm.hs_last_month, 2);
|
||||
bs.WriteBytes(t->cm.cs_total, 4);
|
||||
bs.WriteBytes(t->cm.cs_total_prev, 2);
|
||||
bs.WriteBytes(t->cm.cs_last_month, 2);
|
||||
bs.WriteBytes(t->cm.hr_total, 4);
|
||||
bs.WriteBytes(t->cm.hr_total_prev, 2);
|
||||
bs.WriteBytes(t->cm.hr_last_month, 2);
|
||||
}
|
||||
}
|
||||
|
||||
static void DecodeTowns(BitIStream &bs)
|
||||
{
|
||||
for (Town *t : Town::Iterate()) {
|
||||
t->cm.growing_by_chance = bs.ReadBytes(1);
|
||||
t->cm.houses_reconstructed_this_month = bs.ReadBytes(2);
|
||||
t->cm.houses_reconstructed_last_month = bs.ReadBytes(2);
|
||||
t->cm.houses_demolished_this_month = bs.ReadBytes(2);
|
||||
t->cm.houses_demolished_last_month = bs.ReadBytes(2);
|
||||
t->cm.hs_total = bs.ReadBytes(2);
|
||||
t->cm.hs_total_prev = bs.ReadBytes(2);
|
||||
t->cm.hs_last_month = bs.ReadBytes(2);
|
||||
t->cm.cs_total = bs.ReadBytes(2);
|
||||
t->cm.cs_total_prev = bs.ReadBytes(2);
|
||||
t->cm.cs_last_month = bs.ReadBytes(2);
|
||||
t->cm.hr_total = bs.ReadBytes(2);
|
||||
t->cm.hr_total_prev = bs.ReadBytes(2);
|
||||
t->cm.hr_last_month = bs.ReadBytes(2);
|
||||
}
|
||||
}
|
||||
|
||||
static void EncodeTownsGrowthTiles(BitOStream &bs, Game::TownsGrowthTilesIndex &tiles)
|
||||
{
|
||||
bs.WriteBytes(tiles.size(), 4);
|
||||
for (Game::TownsGrowthTilesIndex::iterator p = tiles.begin();
|
||||
p != tiles.end(); p++) {
|
||||
bs.WriteBytes(p->first, 4);
|
||||
bs.WriteBytes((uint8)p->second, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void DecodeTownsGrowthTiles(BitIStream &bs, Game::TownsGrowthTilesIndex &tiles)
|
||||
{
|
||||
uint n = bs.ReadBytes(4);
|
||||
for (uint i = 0; i < n; i++) {
|
||||
TileIndex tile = bs.ReadBytes(4);
|
||||
TownGrowthTileState state = (TownGrowthTileState)bs.ReadBytes(1);
|
||||
tiles[tile] = state;
|
||||
}
|
||||
}
|
||||
|
||||
static void EncodeCompanies(BitOStream &bs)
|
||||
{
|
||||
for (const Company *c : Company::Iterate()) {
|
||||
bs.WriteBytes(c->cm.is_server, 1);
|
||||
bs.WriteBytes(c->cm.is_scored, 1);
|
||||
for (uint i = 0; i < NUM_CARGO; i++)
|
||||
bs.WriteMoney(c->cur_economy.cm.cargo_income[i]);
|
||||
for (uint j = 0; j < MAX_HISTORY_QUARTERS; j++)
|
||||
for (uint i = 0; i < NUM_CARGO; i++)
|
||||
bs.WriteMoney(c->old_economy[j].cm.cargo_income[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void DecodeCompanies(BitIStream &bs)
|
||||
{
|
||||
for (Company *c : Company::Iterate()) {
|
||||
c->cm.is_server = bs.ReadBytes(1);
|
||||
c->cm.is_scored = bs.ReadBytes(1);
|
||||
for (uint i = 0; i < NUM_CARGO; i++)
|
||||
c->cur_economy.cm.cargo_income[i] = bs.ReadMoney();
|
||||
for (uint j = 0; j < MAX_HISTORY_QUARTERS; j++)
|
||||
for (uint i = 0; i < NUM_CARGO; i++)
|
||||
c->old_economy[j].cm.cargo_income[i] = bs.ReadMoney();
|
||||
}
|
||||
}
|
||||
|
||||
void CBController_saveload_encode(BitOStream &bs) {
|
||||
bs.WriteBytes(0 /* version */, 2);
|
||||
// Controller::saveload_encode(bs);
|
||||
bs.WriteBytes(0, 1);
|
||||
bs.WriteBytes(0, 1);
|
||||
// bs.WriteBytes(_settings_client.gui.cb_distance_check, 1);
|
||||
bs.WriteBytes(0, 1);
|
||||
bs.WriteBytes(0, 1);
|
||||
bs.WriteBytes(0, 1);
|
||||
bs.WriteBytes(0, 1);
|
||||
bs.WriteBytes(0, 1);
|
||||
bs.WriteBytes(0, 1);
|
||||
std::vector<CargoID> cb_cargos;
|
||||
// for(CargoID cargo = 0; cargo < NUM_CARGO; cargo++) {
|
||||
// if (CB_GetReq(cargo) > 0)
|
||||
// cb_cargos.push_back(cargo);
|
||||
// }
|
||||
|
||||
// for (auto cargo_id : cb_cargos) {
|
||||
// // bs.WriteBytes(req.cargo_id, 1);
|
||||
// // bs.WriteBytes(req.amount, 4);
|
||||
// // bs.WriteBytes(req.from, 4);
|
||||
// // bs.WriteBytes(req.decay, 1);
|
||||
// bs.WriteBytes(cargo_id, 1);
|
||||
// bs.WriteBytes(CB_GetReq(cargo_id), 4);
|
||||
// bs.WriteBytes(CB_GetFrom(cargo_id), 4);
|
||||
// bs.WriteBytes(CB_GetDecay(cargo_id), 1);
|
||||
// }
|
||||
// // uint16 cb_towns = 0;
|
||||
// // ForEachCBTown([this, &cb_towns](Town *, Company *) { cb_towns++; });
|
||||
// bs.WriteBytes(Town::GetNumItems(), 2);
|
||||
// for (Town *t : Town::Iterate()) {
|
||||
// auto &tcb = t->cb;
|
||||
// bs.WriteBytes(t->index, 2);
|
||||
// bs.WriteBytes(tcb.pax_delivered, 4);
|
||||
// bs.WriteBytes(tcb.mail_delivered, 4);
|
||||
// bs.WriteBytes(tcb.pax_delivered_last_month, 4);
|
||||
// bs.WriteBytes(tcb.mail_delivered_last_month, 4);
|
||||
// bs.WriteBytes((uint8)tcb.growth_state, 1);
|
||||
// bs.WriteBytes(tcb.shrink_effeciency, 1);
|
||||
// bs.WriteBytes(tcb.shrink_rate, 2);
|
||||
// bs.WriteBytes(tcb.shrink_counter, 2);
|
||||
// for (auto cargo_id : cb_cargos) {
|
||||
// bs.WriteBytes(tcb.stored[cargo_id], 4);
|
||||
// bs.WriteBytes(tcb.delivered[cargo_id], 4);
|
||||
// bs.WriteBytes(tcb.required[cargo_id], 4);
|
||||
// bs.WriteBytes(tcb.delivered_last_month[cargo_id], 4);
|
||||
// bs.WriteBytes(tcb.required_last_month[cargo_id], 4);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
bool CBController_saveload_decode(BitIStream &bs) {
|
||||
auto version = bs.ReadBytes(2);
|
||||
// if (version != 0)
|
||||
// ConsoleError("Save uses incompatible CB data version {}", version);
|
||||
// ConsoleError("Server is not supposed to load CB games");
|
||||
// if (!Controller::saveload_decode(bs))
|
||||
// return false;
|
||||
bs.ReadBytes(1); /* _settings_game.citymania.cb.requirements_type */
|
||||
bs.ReadBytes(1); /* _settings_game.citymania.cb.acceptance_range */
|
||||
// _settings_client.gui.cb_distance_check = bs.ReadBytes(1); /* _settings_game.citymania.cb.acceptance_range */
|
||||
bs.ReadBytes(1); /* _settings_game.citymania.cb.storage_size */
|
||||
bs.ReadBytes(1); /* _settings_game.citymania.cb.town_protection_range */
|
||||
bs.ReadBytes(2); /* _settings_game.citymania.cb.claim_max_houses */
|
||||
bs.ReadBytes(1); /* _settings_game.citymania.cb.smooth_growth_rate */
|
||||
bs.ReadBytes(1); /* _settings_game.citymania.cb.allow_negative_growth */
|
||||
// _settings_game.citymania.cb.requirements.clear();
|
||||
auto n_cargos = bs.ReadBytes(1);
|
||||
std::vector<CargoID> cb_cargos;
|
||||
for (uint i = 0; i < n_cargos; i++) {
|
||||
auto cargo_id = (CargoID)bs.ReadBytes(1);
|
||||
if (cargo_id >= NUM_CARGO) {
|
||||
DEBUG(sl, 0, "Invalid CargoID in CB towns cargo data (%u)", cargo_id);
|
||||
// ConsoleError("Invalid CargoID in CB towns cargo data ({})", cargo_id);
|
||||
return false;
|
||||
}
|
||||
cb_cargos.push_back(cargo_id);
|
||||
auto required = bs.ReadBytes(4);
|
||||
auto from = bs.ReadBytes(4);
|
||||
auto decay = bs.ReadBytes(1);
|
||||
// CB_SetRequirements(cargo_id, required, from, decay);
|
||||
// _settings_game.citymania.cb.requirements.push_back(CBRequirement(cargo_id, from, required, decay, i, ""));
|
||||
}
|
||||
|
||||
auto n_towns = bs.ReadBytes(2);
|
||||
for (uint i = 0; i < n_towns; i++) {
|
||||
auto town_id = bs.ReadBytes(2);
|
||||
auto t = Town::GetIfValid(town_id);
|
||||
if (!t) {
|
||||
DEBUG(sl, 0, "Invalid TownID in CB towns cargo data (%u)", town_id);
|
||||
// ConsoleError("Invalid TownID in CB towns cargo data ({})", town_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
// auto &tcb = t->cb;
|
||||
// tcb.pax_delivered = bs.ReadBytes(4);
|
||||
// tcb.mail_delivered = bs.ReadBytes(4);
|
||||
// tcb.pax_delivered_last_month = bs.ReadBytes(4);
|
||||
// tcb.mail_delivered_last_month = bs.ReadBytes(4);
|
||||
// tcb.growth_state = (TownGrowthState)bs.ReadBytes(1);
|
||||
// tcb.shrink_effeciency = bs.ReadBytes(1);
|
||||
// tcb.shrink_rate = bs.ReadBytes(2);
|
||||
// tcb.shrink_counter = bs.ReadBytes(2);
|
||||
// for (auto cargo_id : cb_cargos) {
|
||||
// tcb.stored[cargo_id] = bs.ReadBytes(4);
|
||||
// tcb.delivered[cargo_id] = bs.ReadBytes(4);
|
||||
// tcb.required[cargo_id] = bs.ReadBytes(4);
|
||||
// tcb.delivered_last_month[cargo_id] = bs.ReadBytes(4);
|
||||
// tcb.required_last_month[cargo_id] = bs.ReadBytes(4);
|
||||
// }
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8 _controller_type = 0;
|
||||
uint8 _game_type = 0;
|
||||
uint16 _last_client_version = 1512;
|
||||
|
||||
static u8vector EncodeData() {
|
||||
BitOStream bs;
|
||||
bs.Reserve(1000);
|
||||
bs.WriteBytes(SAVEGAME_DATA_FORMAT_VERSION, 2);
|
||||
bs.WriteBytes(_last_client_version, 2);
|
||||
bs.WriteBytes(_controller_type, 1);
|
||||
bs.WriteBytes(_date, 4); // Just in case we'll need to detect that game
|
||||
bs.WriteBytes(_date_fract, 1); // was saved by unmodified client
|
||||
bs.WriteBytes(_game_type, 1);
|
||||
bs.WriteBytes(0, 3); // Reserved
|
||||
bs.WriteBytes(0, 4); // Reserved
|
||||
|
||||
EncodeCompanies(bs);
|
||||
EncodeTowns(bs);
|
||||
EncodeTownsGrowthTiles(bs, _game->towns_growth_tiles);
|
||||
EncodeTownsGrowthTiles(bs, _game->towns_growth_tiles_last_month);
|
||||
if (_controller_type == 4)
|
||||
CBController_saveload_encode(bs);
|
||||
|
||||
return bs.GetVector();
|
||||
}
|
||||
|
||||
static void DecodeDataV2(BitIStream &bs) {
|
||||
DecodeCompanies(bs);
|
||||
DecodeTowns(bs);
|
||||
DecodeTownsGrowthTiles(bs, _game->towns_growth_tiles);
|
||||
DecodeTownsGrowthTiles(bs, _game->towns_growth_tiles_last_month);
|
||||
if (_controller_type == 4) CBController_saveload_decode(bs);
|
||||
}
|
||||
|
||||
static void DecodeTownsCargoV1(BitIStream &bs)
|
||||
{
|
||||
bs.ReadBytes(1);bs.ReadBytes(1);
|
||||
// CB_SetStorage(bs.ReadBytes(1));
|
||||
// _settings_client.gui.cb_distance_check = bs.ReadBytes(1);
|
||||
uint n_cb_cargos = bs.ReadBytes(1);
|
||||
// CB_ResetRequirements();
|
||||
std::vector<CargoID> cb_cargos;
|
||||
for (uint i = 0; i < n_cb_cargos; i++) {
|
||||
uint cargo = bs.ReadBytes(1);
|
||||
if (cargo >= NUM_CARGO) {
|
||||
DEBUG(sl, 0, "Invalid CargoID in CB towns cargo data (%u)", cargo);
|
||||
return;
|
||||
}
|
||||
cb_cargos.push_back(cargo);
|
||||
uint req = bs.ReadBytes(4);
|
||||
uint from = bs.ReadBytes(4);
|
||||
uint decay = bs.ReadBytes(1);
|
||||
// CB_SetRequirements(cargo, req, from, decay);
|
||||
}
|
||||
Town *t;
|
||||
uint n_affected_towns = bs.ReadBytes(2);
|
||||
for (uint i = 0; i < n_affected_towns; i++) {
|
||||
uint town_id = bs.ReadBytes(2);
|
||||
t = Town::GetIfValid(town_id);
|
||||
if (!t) {
|
||||
DEBUG(sl, 0, "Invalid TownID in CB towns cargo data (%u)", town_id);
|
||||
return;
|
||||
}
|
||||
// auto &tcb = t->cb;
|
||||
// for (auto cargo_id : cb_cargos) {
|
||||
// tcb.stored[cargo_id] = bs.ReadBytes(4);
|
||||
// tcb.delivered_last_month[cargo_id] = bs.ReadBytes(4);
|
||||
// tcb.delivered[cargo_id] = bs.ReadBytes(4);
|
||||
// bs.ReadBytes(1); /* delivered enough */
|
||||
|
||||
// tcb.required[cargo_id] = 0;
|
||||
// tcb.required_last_month[cargo_id] = 0;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
static void DecodeDataV1(BitIStream &bs) {
|
||||
if (_controller_type != 0) DecodeTownsCargoV1(bs);
|
||||
for (Town *t : Town::Iterate()) {
|
||||
t->cm.growing_by_chance = bs.ReadBytes(1);
|
||||
t->cm.houses_reconstructed_this_month = bs.ReadBytes(2);
|
||||
t->cm.houses_reconstructed_last_month = bs.ReadBytes(2);
|
||||
t->cm.houses_demolished_this_month = bs.ReadBytes(2);
|
||||
t->cm.houses_demolished_last_month = bs.ReadBytes(2);
|
||||
}
|
||||
|
||||
Town *t;
|
||||
uint n_affected_towns = bs.ReadBytes(2);
|
||||
for (uint i = 0; i < n_affected_towns; i++) {
|
||||
uint town_id = bs.ReadBytes(2);
|
||||
t = Town::Get(town_id);
|
||||
if (!t) {
|
||||
DEBUG(sl, 0, "Invalid TownID in CB towns layout errors (%u)", town_id);
|
||||
continue;
|
||||
}
|
||||
t->cm.hs_total = bs.ReadBytes(2);
|
||||
t->cm.hs_total_prev = bs.ReadBytes(2);
|
||||
t->cm.hs_last_month = bs.ReadBytes(2);
|
||||
t->cm.cs_total = bs.ReadBytes(2);
|
||||
t->cm.cs_total_prev = bs.ReadBytes(2);
|
||||
t->cm.cs_last_month = bs.ReadBytes(2);
|
||||
t->cm.hr_total = bs.ReadBytes(2);
|
||||
t->cm.hr_total_prev = bs.ReadBytes(2);
|
||||
t->cm.hr_last_month = bs.ReadBytes(2);
|
||||
}
|
||||
DecodeTownsGrowthTiles(bs, _game->towns_growth_tiles);
|
||||
DecodeTownsGrowthTiles(bs, _game->towns_growth_tiles_last_month);
|
||||
}
|
||||
|
||||
static void DecodeData(u8vector &data) {
|
||||
if (data.size() == 0) {
|
||||
DEBUG(sl, 2, "No CityMania save data");
|
||||
return;
|
||||
}
|
||||
DEBUG(sl, 2, "CityMania save data takes %lu bytes", data.size());
|
||||
BitIStream bs(data);
|
||||
try {
|
||||
uint version = bs.ReadBytes(2);
|
||||
if (version > SAVEGAME_DATA_FORMAT_VERSION) {
|
||||
DEBUG(sl, 0, "Savegame was made with newer version of client, extra save data was not loaded");
|
||||
return;
|
||||
}
|
||||
DEBUG(sl, 2, "CityMania savegame data version %u", version);
|
||||
_last_client_version = bs.ReadBytes(2);
|
||||
_controller_type = bs.ReadBytes(1);
|
||||
if (version <= 1) _controller_type = (_controller_type ? 4 : 0);
|
||||
int32 date = bs.ReadBytes(4);
|
||||
uint32 date_fract = bs.ReadBytes(1);
|
||||
if (date != _date || date_fract != _date_fract) {
|
||||
DEBUG(sl, 0, "Savegame was run in unmodified client, extra save data "
|
||||
"preserved, but may not be accurate");
|
||||
}
|
||||
_game_type = bs.ReadBytes(1);
|
||||
bs.ReadBytes(3); // reserved
|
||||
bs.ReadBytes(4); // reserved
|
||||
if (version == 1) DecodeDataV1(bs);
|
||||
else DecodeDataV2(bs);
|
||||
}
|
||||
catch (BitIStreamUnexpectedEnd &e) {
|
||||
DEBUG(sl, 0, "Invalid CityMania save data");
|
||||
}
|
||||
}
|
||||
|
||||
void Save_PSAC() {
|
||||
/* Write the industries */
|
||||
for (PersistentStorage *ps : PersistentStorage::Iterate()) {
|
||||
if (ps->grfid == CITYMANIA_GRFID) {
|
||||
continue;
|
||||
}
|
||||
ps->ClearChanges();
|
||||
SlSetArrayIndex(ps->index);
|
||||
SlObject(ps, _storage_desc);
|
||||
}
|
||||
|
||||
uint32 grfid = CITYMANIA_GRFID;
|
||||
u8vector data = EncodeData();
|
||||
int n_chunks = (data.size() + 1023) / 1024;
|
||||
data.resize(n_chunks * 1024);
|
||||
DEBUG(sl, 2, "Citybuilder data takes %u bytes", (uint)data.size());
|
||||
uint8 *ptr = &data[0];
|
||||
SaveLoadGlobVarList _desc[] = {
|
||||
SLEG_CONDVAR(grfid, SLE_UINT32, SLV_6, SL_MAX_VERSION),
|
||||
SLEG_CONDARR(*ptr, SLE_UINT32, 256, SLV_EXTEND_PERSISTENT_STORAGE, SL_MAX_VERSION),
|
||||
SLEG_END()
|
||||
};
|
||||
|
||||
uint index = 0;
|
||||
for (PersistentStorage *ps : PersistentStorage::Iterate()) {
|
||||
if (ps->grfid != CITYMANIA_GRFID)
|
||||
index = max(index, ps->index + 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < n_chunks; i++, ptr += 1024) {
|
||||
_desc[1].address = (void *)ptr;
|
||||
SlSetArrayIndex(index + i);
|
||||
SlGlobList(_desc);
|
||||
}
|
||||
}
|
||||
|
||||
void Load_PSAC()
|
||||
{
|
||||
int index;
|
||||
|
||||
/*
|
||||
CITYMANIA_GRFID is used to hide extra data in persitant storages.
|
||||
To save a bit of memory we only keep at most one PS with this
|
||||
grfid and it is later discarded on save.
|
||||
*/
|
||||
PersistentStorage *ps = NULL;
|
||||
u8vector cmdata;
|
||||
uint chunk_size = IsSavegameVersionBefore(SLV_EXTEND_PERSISTENT_STORAGE) ? 64 : 1024;
|
||||
|
||||
while ((index = SlIterateArray()) != -1) {
|
||||
if (ps == NULL) {
|
||||
assert(PersistentStorage::CanAllocateItem());
|
||||
ps = new (index) PersistentStorage(0, 0, 0);
|
||||
}
|
||||
SlObject(ps, _storage_desc);
|
||||
|
||||
if (ps->grfid == CITYMANIA_GRFID) {
|
||||
uint8 *data = (uint8 *)(ps->storage);
|
||||
cmdata.insert(cmdata.end(), data, data + chunk_size);
|
||||
} else {
|
||||
ps = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
DecodeData(cmdata);
|
||||
}
|
||||
|
||||
} // namespace citymania
|
||||
11
src/citymania/cm_saveload.hpp
Normal file
11
src/citymania/cm_saveload.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef CITYMANIA_SAVELOAD_HPP
|
||||
#define CITYMANIA_SAVELOAD_HPP
|
||||
|
||||
namespace citymania {
|
||||
|
||||
void Save_PSAC();
|
||||
void Load_PSAC();
|
||||
|
||||
} // namespace citymania
|
||||
|
||||
#endif
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../newgrf_storage.h"
|
||||
#include "../citymania/cm_saveload.hpp"
|
||||
#include "saveload.h"
|
||||
|
||||
#include "../safeguards.h"
|
||||
@@ -46,5 +47,5 @@ static void Save_PSAC()
|
||||
|
||||
/** Chunk handler for persistent storages. */
|
||||
extern const ChunkHandler _persistent_storage_chunk_handlers[] = {
|
||||
{ 'PSAC', Save_PSAC, Load_PSAC, nullptr, nullptr, CH_ARRAY | CH_LAST},
|
||||
{ 'PSAC', citymania::Save_PSAC, citymania::Load_PSAC, nullptr, nullptr, CH_ARRAY | CH_LAST},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user