From c68c83dffa2edbf854935effedeac0c4697d5db7 Mon Sep 17 00:00:00 2001 From: rubidium Date: Sat, 22 Sep 2007 12:59:43 +0000 Subject: [PATCH] (svn r11138) -Codechange: prepare some subsystems for persistent storage for NewGRFs. --- projects/openttd.vcproj | 6 ++ projects/openttd_vs80.vcproj | 8 ++ source.list | 2 + src/command.cpp | 5 + src/newgrf_spritegroup.cpp | 23 ++--- src/newgrf_spritegroup.h | 8 +- src/newgrf_storage.cpp | 27 ++++++ src/newgrf_storage.h | 176 +++++++++++++++++++++++++++++++++++ src/openttd.cpp | 3 + 9 files changed, 240 insertions(+), 18 deletions(-) create mode 100644 src/newgrf_storage.cpp create mode 100644 src/newgrf_storage.h diff --git a/projects/openttd.vcproj b/projects/openttd.vcproj index 0a7e6bb26d..e47fba85f9 100644 --- a/projects/openttd.vcproj +++ b/projects/openttd.vcproj @@ -570,6 +570,9 @@ + + @@ -1121,6 +1124,9 @@ + + diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index 2a79f1c916..70f1cd775a 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -991,6 +991,10 @@ RelativePath=".\..\src\newgrf_station.h" > + + @@ -1715,6 +1719,10 @@ RelativePath=".\..\src\newgrf_station.cpp" > + + diff --git a/source.list b/source.list index b2ecd8dee4..7c8ff51803 100644 --- a/source.list +++ b/source.list @@ -156,6 +156,7 @@ newgrf_industrytiles.h newgrf_sound.h newgrf_spritegroup.h newgrf_station.h +newgrf_storage.h newgrf_text.h newgrf_town.h newgrf_townname.h @@ -347,6 +348,7 @@ newgrf_industrytiles.cpp newgrf_sound.cpp newgrf_spritegroup.cpp newgrf_station.cpp +newgrf_storage.cpp newgrf_text.cpp newgrf_town.cpp newgrf_townname.cpp diff --git a/src/command.cpp b/src/command.cpp index ca7f950cf1..4370f9b190 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -15,6 +15,7 @@ #include "network/network.h" #include "variables.h" #include "genworld.h" +#include "newgrf_storage.h" const char *_cmd_text = NULL; @@ -573,6 +574,7 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, _docommand_recursive = 0; _cmd_text = NULL; + ClearStorageChanges(false); return false; } @@ -603,6 +605,7 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, if (_network_dedicated || (_network_server && pbck == PLAYER_SPECTATOR)) _local_player = pbck; _docommand_recursive = 0; _cmd_text = NULL; + ClearStorageChanges(false); return true; } #endif /* ENABLE_NETWORK */ @@ -643,6 +646,7 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, if (callback) callback(true, tile, p1, p2); _cmd_text = NULL; + ClearStorageChanges(true); return true; show_error: @@ -656,6 +660,7 @@ callb_err: if (callback) callback(false, tile, p1, p2); _cmd_text = NULL; + ClearStorageChanges(false); return false; } diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index 9dcc674d8b..bc71ca19f2 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -76,7 +76,7 @@ void InitializeSpriteGroupPool() _spritegroup_count = 0; } -uint32 _temp_store[0x110]; +TemporaryStorageArray _temp_store; static inline uint32 GetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available) @@ -98,7 +98,7 @@ static inline uint32 GetVariable(const ResolverObject *object, byte variable, by case 0x1C: return object->last_value; case 0x20: return _opt.landscape == LT_ARCTIC ? GetSnowLine() : 0xFF; - case 0x7D: return _temp_store[parameter]; + case 0x7D: return _temp_store.Get(parameter); /* Not a common variable, so evalute the feature specific variables */ default: return object->GetVariable(object, variable, parameter, available); @@ -137,9 +137,7 @@ static U EvalAdjustT(const DeterministicSpriteGroupAdjust *adjust, U last_value, case DSGA_OP_AND: return last_value & value; case DSGA_OP_OR: return last_value | value; case DSGA_OP_XOR: return last_value ^ value; - case DSGA_OP_STO: - if (value < lengthof(_temp_store)) _temp_store[value] = last_value; - return last_value; + case DSGA_OP_STO: _temp_store.Store(value, last_value); return last_value; case DSGA_OP_RST: return value; default: return value; } @@ -162,7 +160,7 @@ static inline const SpriteGroup *ResolveVariable(const SpriteGroup *group, Resol bool available = true; if (adjust->variable == 0x7E) { ResolverObject subobject = *object; - const SpriteGroup *subgroup = Resolve(adjust->subroutine, &subobject, false); + const SpriteGroup *subgroup = Resolve(adjust->subroutine, &subobject); if (subgroup == NULL || subgroup->type != SGT_CALLBACK) { value = CALLBACK_FAILED; } else { @@ -175,7 +173,7 @@ static inline const SpriteGroup *ResolveVariable(const SpriteGroup *group, Resol if (!available) { /* Unsupported property: skip further processing and return either * the group from the first range or the default group. */ - return Resolve(group->g.determ.num_ranges > 0 ? group->g.determ.ranges[0].group : group->g.determ.default_group, object, false); + return Resolve(group->g.determ.num_ranges > 0 ? group->g.determ.ranges[0].group : group->g.determ.default_group, object); } switch (group->g.determ.size) { @@ -216,11 +214,11 @@ static inline const SpriteGroup *ResolveVariable(const SpriteGroup *group, Resol for (i = 0; i < group->g.determ.num_ranges; i++) { if (group->g.determ.ranges[i].low <= value && value <= group->g.determ.ranges[i].high) { - return Resolve(group->g.determ.ranges[i].group, object, false); + return Resolve(group->g.determ.ranges[i].group, object); } } - return Resolve(group->g.determ.default_group, object, false); + return Resolve(group->g.determ.default_group, object); } @@ -254,19 +252,16 @@ static inline const SpriteGroup *ResolveRandom(const SpriteGroup *group, Resolve mask = (group->g.random.num_groups - 1) << group->g.random.lowest_randbit; index = (object->GetRandomBits(object) & mask) >> group->g.random.lowest_randbit; - return Resolve(group->g.random.groups[index], object, false); + return Resolve(group->g.random.groups[index], object); } /* ResolverObject (re)entry point */ -const SpriteGroup *Resolve(const SpriteGroup *group, ResolverObject *object, bool first_call) +const SpriteGroup *Resolve(const SpriteGroup *group, ResolverObject *object) { /* We're called even if there is no group, so quietly return nothing */ if (group == NULL) return NULL; - /* Zero the temporary storage to make sure there are no desyncs */ - if (first_call) memset(_temp_store, 0, sizeof(_temp_store)); - switch (group->type) { case SGT_REAL: return object->ResolveReal(object, group); case SGT_DETERMINISTIC: return ResolveVariable(group, object); diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index f1b17cbbec..50b75b849b 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -7,6 +7,7 @@ #include "town.h" #include "industry.h" +#include "newgrf_storage.h" /** * Gets the value of a so-called newgrf "register". @@ -16,9 +17,8 @@ */ static inline uint32 GetRegister(uint i) { - assert(i < 0x110); - extern uint32 _temp_store[0x110]; - return _temp_store[i]; + extern TemporaryStorageArray _temp_store; + return _temp_store.Get(i); } struct SpriteGroup; @@ -243,7 +243,7 @@ struct ResolverObject { /* Base sprite group resolver */ -const SpriteGroup *Resolve(const SpriteGroup *group, ResolverObject *object, bool first_call = true); +const SpriteGroup *Resolve(const SpriteGroup *group, ResolverObject *object); #endif /* NEWGRF_SPRITEGROUP_H */ diff --git a/src/newgrf_storage.cpp b/src/newgrf_storage.cpp new file mode 100644 index 0000000000..c5c06b2f9e --- /dev/null +++ b/src/newgrf_storage.cpp @@ -0,0 +1,27 @@ +/* $Id$ */ + +/** @file newgrf_storage.cpp Functionality related to the temporary and persistent storage arrays for NewGRFs. */ + +#include "stdafx.h" +#include "helpers.hpp" +#include "newgrf_storage.h" +#include + +/** The changed storage arrays */ +static std::set _changed_storage_arrays; + +void AddChangedStorage(BaseStorageArray *storage) +{ + _changed_storage_arrays.insert(storage); +} + +void ClearStorageChanges(bool keep_changes) +{ + /* Loop over all changes arrays */ + for (std::set::iterator it = _changed_storage_arrays.begin(); it != _changed_storage_arrays.end(); it++) { + (*it)->ClearChanges(keep_changes); + } + + /* And then clear that array */ + _changed_storage_arrays.clear(); +} diff --git a/src/newgrf_storage.h b/src/newgrf_storage.h new file mode 100644 index 0000000000..93aab09dc2 --- /dev/null +++ b/src/newgrf_storage.h @@ -0,0 +1,176 @@ +/* $Id$ */ + +/** @file newgrf_storage.h Functionality related to the temporary and persistent storage arrays for NewGRFs. */ + +#ifndef NEWGRF_STORAGE_H +#define NEWGRF_STORAGE_H + +/** + * Base class for all NewGRF storage arrays. Nothing fancy, only here + * so we have a generalised class to use. + */ +struct BaseStorageArray +{ + /** The needed destructor */ + virtual ~BaseStorageArray() {} + + /** + * Clear the changes made since the last ClearChanges. + * This can be done in two ways: + * - saving the changes permanently + * - reverting to the previous version + * @param keep_changes do we save or revert the changes since the last ClearChanges? + */ + virtual void ClearChanges(bool keep_changes) {} +}; + +/** + * Class for persistent storage of data. + * On ClearChanges that data is either reverted or saved. + * @param TYPE the type of variable to store. + * @param SIZE the size of the array. + */ +template +struct PersistentStorageArray : BaseStorageArray { + TYPE storage[SIZE]; ///< Memory to for the storage array + TYPE *prev_storage; ///< Memory to store "old" states so we can revert them on the performance of test cases for commands etc. + + /** Simply construct the array */ + PersistentStorageArray() : prev_storage(NULL) + { + memset(this->storage, 0, sizeof(this->storage)); + } + + /** And free all data related to it */ + ~PersistentStorageArray() + { + free(this->prev_storage); + } + + /** + * Stores some value at a given position. + * If there is no backup of the data that backup is made and then + * we write the data. + * @param pos the position to write at + * @param value the value to write + */ + void Store(uint pos, TYPE value) + { + /* Out of the scope of the array */ + if (pos >= SIZE) return; + + /* The value hasn't changed, so we pretend nothing happened. + * Saves a few cycles and such and it's pretty easy to check. */ + if (this->storage[pos] == value) return; + + /* We do not have made a backup; lets do so */ + if (this->prev_storage != NULL) { + this->prev_storage = MallocT(SIZE); + if (this->prev_storage == NULL) return; + + memcpy(this->prev_storage, this->storage, sizeof(this->storage)); + + /* We only need to register ourselves when we made the backup + * as that is the only time something will have changed */ + AddChangedStorage(this); + } + + this->storage[pos] = value; + } + + /** + * Gets the value from a given position. + * @param pos the position to get the data from + * @return the data from that position + */ + TYPE Get(uint pos) + { + /* Out of the scope of the array */ + if (pos >= SIZE) return 0; + + return this->storage[pos]; + } + + void ClearChanges(bool keep_changes) + { + assert(this->prev_storage != NULL); + + if (!keep_changes) { + memcpy(this->storage, this->prev_storage, sizeof(this->storage)); + } + free(this->prev_storage); + } +}; + + +/** + * Class for temporary storage of data. + * On ClearChanges that data is always zero-ed. + * @param TYPE the type of variable to store. + * @param SIZE the size of the array. + */ +template +struct TemporaryStorageArray : BaseStorageArray { + TYPE storage[SIZE]; ///< Memory to for the storage array + + /** Simply construct the array */ + TemporaryStorageArray() + { + memset(this->storage, 0, sizeof(this->storage)); + } + + /** + * Stores some value at a given position. + * @param pos the position to write at + * @param value the value to write + */ + void Store(uint pos, TYPE value) + { + /* Out of the scope of the array */ + if (pos >= SIZE) return; + + this->storage[pos] = value; + AddChangedStorage(this); + } + + /** + * Gets the value from a given position. + * @param pos the position to get the data from + * @return the data from that position + */ + TYPE Get(uint pos) + { + /* Out of the scope of the array */ + if (pos >= SIZE) return 0; + + return this->storage[pos]; + } + + void ClearChanges(bool keep_changes) + { + memset(this->storage, 0, sizeof(this->storage)); + } +}; + +/** + * Add the changed storage array to the list of changed arrays. + * This is done so we only have to revert/save the changed + * arrays, which saves quite a few clears, etc. after callbacks. + * @param storage the array that has changed + */ +void AddChangedStorage(BaseStorageArray *storage); + + +/** + * Clear the changes made since the last ClearStorageChanges. + * This is done for *all* storages that have been registered to with + * AddChangedStorage since the previous ClearStorageChanges. + * + * This can be done in two ways: + * - saving the changes permanently + * - reverting to the previous version + * @param keep_changes do we save or revert the changes since the last ClearChanges? + */ +void ClearStorageChanges(bool keep_changes); + +#endif /* NEWGRF_STORAGE_H */ diff --git a/src/openttd.cpp b/src/openttd.cpp index bed5dbdb05..4098c75474 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -61,6 +61,7 @@ #include "newgrf_config.h" #include "newgrf_house.h" #include "newgrf_commons.h" +#include "newgrf_storage.h" #include "player_face.h" #include "group.h" #include "blitter/factory.hpp" @@ -977,6 +978,8 @@ void SwitchMode(int new_mode) * That check is enforced in DoCommand. */ void StateGameLoop() { + ClearStorageChanges(false); + /* dont execute the state loop during pause */ if (_pause_game) return; if (IsGeneratingWorld()) return;