Add more data to savegame

This commit is contained in:
dP
2020-07-09 10:49:39 +03:00
parent a671d46961
commit 27f5f91eae
5 changed files with 139 additions and 69 deletions

View File

@@ -2,6 +2,8 @@
#include "cm_bitstream.hpp"
#include "../safeguards.h"
namespace citymania {
// Returns 8 higher bits of s-bit integer x
@@ -23,6 +25,11 @@ void BitOStream::WriteBytes64(uint64 value, int amount)
} while (--amount);
}
void BitOStream::WriteMoney(Money value) {
this->WriteBytes64(value, 8);
}
/**
* Reserves memory for encoded data. Use to avoid reallocation of vector.
*/
@@ -61,4 +68,20 @@ uint32 BitIStream::ReadBytes(uint amount)
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

View File

@@ -1,6 +1,8 @@
#ifndef BITSTREAM_H
#define BITSTREAM_H
#include "../economy_type.h"
#include <vector>
namespace citymania {
@@ -19,6 +21,7 @@ public:
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;
};
@@ -37,6 +40,8 @@ public:
BitIStream(u8vector &data): f(data), i(0) {}
virtual ~BitIStream(){}
uint32 ReadBytes(uint amount);
uint64 ReadBytes64(uint amount);
Money ReadMoney();
};
} // namespace citymania

View File

@@ -1,5 +1,6 @@
#include "../stdafx.h"
#include "../company_base.h"
#include "../date_func.h"
#include "../debug.h"
#include "../saveload/saveload.h"
@@ -25,7 +26,7 @@ static const SaveLoad _storage_desc[] = {
};
static void EncodeTownsExtraInfo(BitOStream &bs)
static void EncodeTowns(BitOStream &bs)
{
for (const Town *t : Town::Iterate()) {
bs.WriteBytes(t->cm.growing_by_chance, 1);
@@ -33,9 +34,38 @@ static void EncodeTownsExtraInfo(BitOStream &bs)
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);
@@ -46,43 +76,6 @@ static void EncodeTownsGrowthTiles(BitOStream &bs, Game::TownsGrowthTilesIndex &
}
}
static void EncodeTownsLayoutErrors(BitOStream &bs)
{
uint n_affected_towns = 0;
for (const Town *t : Town::Iterate()) {
if (t->cm.hr_total || t->cm.hs_total || t->cm.cs_total)
n_affected_towns++;
}
bs.WriteBytes(n_affected_towns, 2);
for (const Town *t : Town::Iterate()) {
if (t->cm.hr_total || t->cm.hs_total || t->cm.cs_total) {
bs.WriteBytes(t->index, 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);
}
}
EncodeTownsGrowthTiles(bs, _game->towns_growth_tiles);
EncodeTownsGrowthTiles(bs, _game->towns_growth_tiles_last_month);
}
static void DecodeTownsExtraInfo(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);
}
}
static void DecodeTownsGrowthTiles(BitIStream &bs, Game::TownsGrowthTilesIndex &tiles)
{
uint n = bs.ReadBytes(4);
@@ -93,29 +86,30 @@ static void DecodeTownsGrowthTiles(BitIStream &bs, Game::TownsGrowthTilesIndex &
}
}
static void DecodeTownsLayoutErrors(BitIStream &bs)
static void EncodeCompanies(BitOStream &bs)
{
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);
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) {
@@ -247,14 +241,24 @@ static u8vector EncodeData() {
bs.WriteBytes(0, 3); // Reserved
bs.WriteBytes(0, 4); // Reserved
EncodeTownsExtraInfo(bs);
EncodeTownsLayoutErrors(bs);
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)
{
CB_SetStorage(bs.ReadBytes(1));
@@ -296,13 +300,43 @@ static void DecodeTownsCargoV1(BitIStream &bs)
}
}
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;
} else {
DEBUG(sl, 2, "Ignoring CityMania data in savegame");
return;
}
DEBUG(sl, 2, "CityMania save data takes %lu bytes", data.size());
BitIStream bs(data);
@@ -312,6 +346,7 @@ static void DecodeData(u8vector &data) {
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);
@@ -324,10 +359,8 @@ static void DecodeData(u8vector &data) {
_game_type = bs.ReadBytes(1);
bs.ReadBytes(3); // reserved
bs.ReadBytes(4); // reserved
if (version <= 1 && _controller_type != 0) DecodeTownsCargoV1(bs);
DecodeTownsExtraInfo(bs);
DecodeTownsLayoutErrors(bs);
if (version > 1 && _controller_type == 4) CBController_saveload_decode(bs);
if (version == 1) DecodeDataV1(bs);
else DecodeDataV2(bs);
}
catch (BitIStreamUnexpectedEnd &e) {
DEBUG(sl, 0, "Invalid CityMania save data");

View File

@@ -6,11 +6,18 @@
namespace citymania {
namespace ext {
class CompanyEconomyEntry {
public:
Money cargo_income[NUM_CARGO]; ///< Cargo income from each cargo type
};
class Company {
public:
bool is_server; ///< whether company is controlled by the server
bool is_scored; ///< whether company is eligible for scoring
};
} // namespace ext
} // namespace citymania

View File

@@ -100,6 +100,8 @@ struct CompanyProperties {
CompanyEconomyEntry old_economy[MAX_HISTORY_QUARTERS]; ///< Economic data of the company of the last #MAX_HISTORY_QUARTERS quarters.
byte num_valid_stat_ent; ///< Number of valid statistical entries in #old_economy.
citymania::ext::Company cm; ///< CityMania extra company data.
// TODO: Change some of these member variables to use relevant INVALID_xxx constants
CompanyProperties()
: name_2(0), name_1(0), name(nullptr), president_name_1(0), president_name_2(0), president_name(nullptr),