saving extra cb info in savegames

This commit is contained in:
Pavel Stupnikov
2015-06-14 21:39:55 +03:00
parent 528ec650b4
commit ad67312e2d
17 changed files with 433 additions and 22 deletions

View File

@@ -1,4 +1,4 @@
; $Id: no_music.obm 27041 2014-10-25 12:35:48Z rubidium $
; $Id: no_music.obm 25205 2013-04-24 20:30:02Z frosch $
;
; This represents more or less nothingness
;

View File

@@ -1,4 +1,4 @@
; $Id: no_sound.obs 27103 2015-01-01 20:51:18Z rubidium $
; $Id: no_sound.obs 25205 2013-04-24 20:30:02Z frosch $
;
; This represents more or less nothingness
;

View File

@@ -1,4 +1,4 @@
; $Id: orig_dos.obg 27041 2014-10-25 12:35:48Z rubidium $
; $Id: orig_dos.obg 25205 2013-04-24 20:30:02Z frosch $
;
; This represents the original graphics as on the non-German Transport
; Tycoon Deluxe DOS CD.

View File

@@ -1,4 +1,4 @@
; $Id: orig_dos.obs 27041 2014-10-25 12:35:48Z rubidium $
; $Id: orig_dos.obs 25205 2013-04-24 20:30:02Z frosch $
;
; This represents the original sounds as on the Transport
; Tycoon Deluxe DOS CD.

View File

@@ -1,4 +1,4 @@
; $Id: orig_dos_de.obg 27041 2014-10-25 12:35:48Z rubidium $
; $Id: orig_dos_de.obg 25205 2013-04-24 20:30:02Z frosch $
;
; This represents the original graphics as on the German Transport
; Tycoon Deluxe DOS CD. It contains one broken sprite.

View File

@@ -1,4 +1,4 @@
; $Id: orig_win.obg 27041 2014-10-25 12:35:48Z rubidium $
; $Id: orig_win.obg 25205 2013-04-24 20:30:02Z frosch $
;
; This represents the original graphics as on the Transport
; Tycoon Deluxe for Windows CD.

View File

@@ -1,4 +1,4 @@
; $Id: orig_win.obm 27274 2015-05-08 17:45:13Z rubidium $
; $Id: orig_win.obm 25205 2013-04-24 20:30:02Z frosch $
;
; This represents the original music as on the Transport
; Tycoon Deluxe for Windows CD.

View File

@@ -1,4 +1,4 @@
; $Id: orig_win.obs 27041 2014-10-25 12:35:48Z rubidium $
; $Id: orig_win.obs 25205 2013-04-24 20:30:02Z frosch $
;
; This represents the original sounds as on the Transport
; Tycoon Deluxe for Windows CD.

View File

@@ -3,6 +3,7 @@ airport.cpp
animated_tile.cpp
articulated_vehicles.cpp
autoreplace.cpp
bitstream.cpp
bmp.cpp
cargoaction.cpp
cargomonitor.cpp
@@ -141,6 +142,7 @@ autoslope.h
base_media_base.h
base_media_func.h
base_station_base.h
bitstream.h
bmp.h
bridge.h
cargo_table_gui.h
@@ -608,6 +610,8 @@ saveload/animated_tile_sl.cpp
saveload/autoreplace_sl.cpp
saveload/cargomonitor_sl.cpp
saveload/cargopacket_sl.cpp
saveload/citymania_sl.cpp
saveload/citymania_sl.h
saveload/cheat_sl.cpp
saveload/company_sl.cpp
saveload/depot_sl.cpp

58
src/bitstream.cpp Normal file
View File

@@ -0,0 +1,58 @@
#include "bitstream.h"
// 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);
}
/**
* 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;
}

41
src/bitstream.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef BITSTREAM_H
#define BITSTREAM_H
#include "stdafx.h"
#include <vector>
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);
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);
};
#endif

View File

@@ -0,0 +1,242 @@
#include "citymania_sl.h"
#include "../bitstream.h"
#include "../date_func.h"
#include "../debug.h"
#include "../town.h"
#define CM_DATA_FORMAT_VERSION 1
assert_compile(NUM_CARGO == 32);
static void CM_EncodeTownsExtraInfo(BitOStream &bs)
{
Town *t;
uint n_affected_towns = 0;
FOR_ALL_TOWNS(t) {
if (t->growing_by_chance || t->houses_reconstruction ||
t->houses_construction || t->houses_demolished)
n_affected_towns++;
}
bs.WriteBytes(n_affected_towns, 2);
FOR_ALL_TOWNS(t) {
if (t->growing_by_chance || t->houses_reconstruction ||
t->houses_construction || t->houses_demolished) {
bs.WriteBytes(t->index, 2);
bs.WriteBytes(t->growing_by_chance, 1);
bs.WriteBytes(t->houses_reconstruction, 2);
bs.WriteBytes(t->houses_demolished, 2);
}
}
}
static void CM_EncodeTownsGrowthTiles(BitOStream &bs, TownsGrowthTilesIndex &tiles)
{
bs.WriteBytes(tiles.size(), 4);
for (TownsGrowthTilesIndex::iterator p = tiles.begin();
p != tiles.end(); p++) {
bs.WriteBytes(p->first, 4);
bs.WriteBytes(p->second, 1);
}
}
static void CM_EncodeTownsLayoutErrors(BitOStream &bs)
{
Town *t;
uint n_affected_towns = 0;
FOR_ALL_TOWNS(t) {
if (t->cb_houses_removed || t->houses_skipped || t->cycles_skipped)
n_affected_towns++;
}
bs.WriteBytes(n_affected_towns, 2);
FOR_ALL_TOWNS(t) {
if (t->cb_houses_removed || t->houses_skipped || t->cycles_skipped) {
bs.WriteBytes(t->index, 2);
bs.WriteBytes(t->houses_skipped, 2);
bs.WriteBytes(t->houses_skipped_prev, 2);
bs.WriteBytes(t->houses_skipped_last_month, 2);
bs.WriteBytes(t->cycles_skipped, 2);
bs.WriteBytes(t->cycles_skipped_prev, 2);
bs.WriteBytes(t->cycles_skipped_last_month, 2);
bs.WriteBytes(t->cb_houses_removed, 2);
bs.WriteBytes(t->cb_houses_removed_prev, 2);
bs.WriteBytes(t->cb_houses_removed_last_month, 2);
}
}
CM_EncodeTownsGrowthTiles(bs, _towns_growth_tiles);
CM_EncodeTownsGrowthTiles(bs, _towns_growth_tiles_last_month);
}
static void CM_EncodeTownsCargo(BitOStream &bs)
{
uint n_cb_cargos = 0;
CargoID cb_cargos[NUM_CARGO];
for(CargoID cargo = 0; cargo < NUM_CARGO; cargo++) {
if (CB_GetReq(cargo) > 0)
cb_cargos[n_cb_cargos++] = cargo;
}
bs.WriteBytes(n_cb_cargos, 1);
for (uint i = 0; i < n_cb_cargos; i++) {
CargoID cargo = cb_cargos[i];
bs.WriteBytes(cargo, 1);
bs.WriteBytes(CB_GetReq(cargo), 4);
bs.WriteBytes(CB_GetFrom(cargo), 4);
bs.WriteBytes(CB_GetDecay(cargo), 1);
}
Town *t;
bs.WriteBytes(Town::GetNumItems(), 2);
FOR_ALL_TOWNS(t) {
bs.WriteBytes(t->index, 2);
for (uint i = 0; i < n_cb_cargos; i++) {
CargoID cargo = cb_cargos[i];
bs.WriteBytes(t->storage[cargo], 4);
bs.WriteBytes(t->act_cargo[cargo], 4);
bs.WriteBytes(t->new_act_cargo[cargo], 4);
bs.WriteBytes(t->delivered_enough[cargo], 1);
}
}
}
static void CM_DecodeTownsExtraInfo(BitIStream &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 CM extra towns info (%u)", town_id);
continue;
}
t->growing_by_chance = bs.ReadBytes(1);
t->houses_reconstruction = bs.ReadBytes(2);
t->houses_demolished = bs.ReadBytes(2);
}
}
static void CM_DecodeTownsGrowthTiles(BitIStream &bs, 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 CM_DecodeTownsLayoutErrors(BitIStream &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->houses_skipped = bs.ReadBytes(2);
t->houses_skipped_prev = bs.ReadBytes(2);
t->houses_skipped_last_month = bs.ReadBytes(2);
t->cycles_skipped = bs.ReadBytes(2);
t->cycles_skipped_prev = bs.ReadBytes(2);
t->cycles_skipped_last_month = bs.ReadBytes(2);
t->cb_houses_removed = bs.ReadBytes(2);
t->cb_houses_removed_prev = bs.ReadBytes(2);
t->cb_houses_removed_last_month = bs.ReadBytes(2);
}
CM_DecodeTownsGrowthTiles(bs, _towns_growth_tiles);
CM_DecodeTownsGrowthTiles(bs, _towns_growth_tiles_last_month);
}
static void CM_DecodeTownsCargo(BitIStream &bs)
{
uint n_cb_cargos = bs.ReadBytes(1);
CB_ResetRequirements();
CargoID cb_cargos[NUM_CARGO];
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[i] = 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::Get(town_id);
if (!t) {
DEBUG(sl, 0, "Invalid TownID in CB towns cargo data (%u)", town_id);
continue;
}
for (uint i = 0; i < n_cb_cargos; i++) {
CargoID cargo = cb_cargos[i];
t->storage[cargo] = bs.ReadBytes(4);
t->act_cargo[cargo] = bs.ReadBytes(4);
t->new_act_cargo[cargo] = bs.ReadBytes(4);
t->delivered_enough[cargo] = bs.ReadBytes(1);
}
}
}
u8vector CM_EncodeData()
{
BitOStream bs;
bs.Reserve(1000);
bs.WriteBytes(CM_DATA_FORMAT_VERSION, 2);
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(0, 4); // Reserved
bs.WriteBytes(0, 4); // Reserved
CM_EncodeTownsCargo(bs);
CM_EncodeTownsExtraInfo(bs);
CM_EncodeTownsLayoutErrors(bs);
// fill with 0 to fit in persistent storage chunks (16 * 4)
int mod = bs.GetByteSize() % 64;
if (mod) {
for (int i = 0; i < 64 - mod; i++)
bs.WriteBytes(0, 1);
}
DEBUG(sl, 2, "Citybuilder data takes %u bytes", bs.GetByteSize());
return bs.GetVector();
}
void CM_DecodeData(u8vector &data)
{
ResetTownsGrowthTiles();
if (data.size() == 0) {
CB_SetCB(false);
DEBUG(sl, 2, "No citybuilder data");
return;
}
DEBUG(sl, 2, "Citybuilder data takes %lu bytes", data.size());
CB_SetCB(true);
BitIStream bs(data);
try {
uint version = bs.ReadBytes(2);
if (version != CM_DATA_FORMAT_VERSION) {
DEBUG(sl, 0, "Savegame was made with different version of client, extra citybuilder data was not loaded");
return;
}
bs.ReadBytes(4); // _date
bs.ReadBytes(1); // _date_fract
bs.ReadBytes(4); // reserved
bs.ReadBytes(4); // reserved
CM_DecodeTownsCargo(bs);
CM_DecodeTownsExtraInfo(bs);
CM_DecodeTownsLayoutErrors(bs);
}
catch (BitIStreamUnexpectedEnd &e) {
DEBUG(sl, 0, "Invalid citybuilder data");
}
}

View File

@@ -0,0 +1,9 @@
#ifndef SAVELOAD_CITYMANIA_SL_H
#define SAVELOAD_CITYMANIA_SL_H
#include "../bitstream.h"
u8vector CM_EncodeData();
void CM_DecodeData(u8vector &data);
#endif

View File

@@ -11,10 +11,14 @@
#include "../stdafx.h"
#include "../newgrf_storage.h"
#include "citymania_sl.h"
#include "saveload.h"
#include "../safeguards.h"
// Luukland_Citybuilder grf id actually
#define CITYMANIA_GRFID 0x534B0501U
/** Description of the data to save and load in #PersistentStorage. */
static const SaveLoad _storage_desc[] = {
SLE_CONDVAR(PersistentStorage, grfid, SLE_UINT32, 6, SL_MAX_VERSION),
@@ -22,15 +26,62 @@ static const SaveLoad _storage_desc[] = {
SLE_END()
};
// static void hexdump(uint8 *data) {
// uint i;
// for (i = 0; i < 20; i++) {
// if (i) fprintf(stderr, " : ");
// fprintf(stderr, "%02x", data[i]);
// }
// fprintf(stderr, i >= 20 ? " ...\n" : "\n");
// }
/** Load persistent storage data. */
static 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;
while ((index = SlIterateArray()) != -1) {
assert(PersistentStorage::CanAllocateItem());
PersistentStorage *ps = new (index) PersistentStorage(0, 0, 0);
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 + 64);
} else {
ps = NULL;
}
}
CM_DecodeData(cmdata);
}
static void Save_CMDataAsPSAC() {
uint32 grfid = CITYMANIA_GRFID;
u8vector data = CM_EncodeData();
uint8 *ptr = &data[0];
SaveLoadGlobVarList _desc[] = {
SLEG_CONDVAR(grfid, SLE_UINT32, 6, SL_MAX_VERSION),
SLEG_CONDARR(*ptr, SLE_UINT32, 16, 161, SL_MAX_VERSION),
SLEG_END()
};
uint index = PersistentStorage::GetNumItems();
int n_chunks = data.size() / 64;
for (int i = 0; i < n_chunks; i++, ptr += 64) {
_desc[1].address = (void *)ptr;
SlSetArrayIndex(index + i);
SlGlobList(_desc);
}
}
@@ -38,13 +89,17 @@ static void Load_PSAC()
static void Save_PSAC()
{
PersistentStorage *ps;
/* Write the industries */
FOR_ALL_STORAGES(ps) {
if (ps->grfid == CITYMANIA_GRFID) {
continue;
}
ps->ClearChanges();
SlSetArrayIndex(ps->index);
SlObject(ps, _storage_desc);
}
Save_CMDataAsPSAC();
}
/** Chunk handler for persistent storages. */

View File

@@ -33,9 +33,6 @@ void RebuildTownCaches()
town->cache.population = 0;
town->cache.num_houses = 0;
town->cache.potential_pop = 0;
town->houses_construction = 0;
town->houses_reconstruction = 0;
town->houses_demolished = 0;
}
for (TileIndex t = 0; t < MapSize(); t++) {
@@ -117,7 +114,6 @@ void UpdateHousesAndTowns()
}
RebuildTownCaches();
ResetTownsGrowthTiles();
}
/** Save and load of towns. */

View File

@@ -256,6 +256,7 @@ bool CB_Enabled();
void CB_SetCB(bool cb);
void CB_SetStorage(uint storage);
void CB_SetRequirements(CargoID cargo, uint req, uint from, uint decay);
void CB_ResetRequirements();
uint CB_GetReq(CargoID cargo);
uint CB_GetFrom(CargoID cargo);
uint CB_GetDecay(CargoID cargo);
@@ -274,8 +275,9 @@ enum TownGrowthTileState {
TGTS_CB_HOUSE_REMOVED
};
extern std::map<TileIndex, TownGrowthTileState> _towns_growth_tiles_last_month;
extern std::map<TileIndex, TownGrowthTileState> _towns_growth_tiles;
typedef std::map<TileIndex, TownGrowthTileState> TownsGrowthTilesIndex;
extern TownsGrowthTilesIndex _towns_growth_tiles_last_month;
extern TownsGrowthTilesIndex _towns_growth_tiles;
void UpdateTownGrowthTile(TileIndex tile, TownGrowthTileState state);
void ResetTownsGrowthTiles();

View File

@@ -61,8 +61,8 @@ uint days_in_month[] = {31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};//CB
void CB_UpdateTownStorage(Town *t); //CB
const Money NOVAPOLIS_COMPANY_MONEY_THRESHOLD = INT64_MAX >> 4;
std::map<TileIndex, TownGrowthTileState> _towns_growth_tiles_last_month;
std::map<TileIndex, TownGrowthTileState> _towns_growth_tiles;
TownsGrowthTilesIndex _towns_growth_tiles_last_month;
TownsGrowthTilesIndex _towns_growth_tiles;
TownID _new_town_id;
uint32 _town_cargoes_accepted; ///< Bitmap of all cargoes accepted by houses.
@@ -1683,15 +1683,14 @@ void UpdateTownMaxPass(Town *t)
}
//CB
bool CB_Enabled(){
return _cb_enabled;
}
void CB_SetCB(bool cb){
_cb_enabled = cb;
if(!_cb_enabled){
for(CargoID cargo = 0; cargo < NUM_CARGO; cargo++){
CB_SetRequirements(cargo, 0, 0, 0);
}
CB_ResetRequirements();
}
}
void CB_SetStorage(uint storage){
@@ -1702,6 +1701,11 @@ void CB_SetRequirements(CargoID cargo, uint req, uint from, uint decay){
CBFROM[cargo] = from;
CBDECAY[cargo] = decay;
}
void CB_ResetRequirements() {
for(CargoID cargo = 0; cargo < NUM_CARGO; cargo++){
CB_SetRequirements(cargo, 0, 0, 0);
}
}
uint CB_GetReq(CargoID cargo){
return CBREQ[cargo];
}