1
0
Fork 0

Codechange: Place gamelog into its own class, along with internal data.

Data is now stored in vectors to avoid manual memory management and
passing lengths around.
pull/10757/head
Peter Nelson 2023-05-01 18:14:31 +01:00 committed by PeterN
parent 47a8d12f0e
commit 00bf42353a
18 changed files with 308 additions and 345 deletions

View File

@ -2116,7 +2116,7 @@ DEF_CONSOLE_CMD(ConListSettings)
DEF_CONSOLE_CMD(ConGamelogPrint) DEF_CONSOLE_CMD(ConGamelogPrint)
{ {
GamelogPrintConsole(); _gamelog.PrintConsole();
return true; return true;
} }

View File

@ -301,7 +301,7 @@ char *CrashLog::LogLibraries(char *buffer, const char *last) const
*/ */
char *CrashLog::LogGamelog(char *buffer, const char *last) const char *CrashLog::LogGamelog(char *buffer, const char *last) const
{ {
GamelogPrint([&buffer, last](const char *s) { _gamelog.Print([&buffer, last](const char *s) {
buffer += seprintf(buffer, last, "%s\n", s); buffer += seprintf(buffer, last, "%s\n", s);
}); });
return buffer + seprintf(buffer, last, "\n"); return buffer + seprintf(buffer, last, "\n");
@ -423,7 +423,7 @@ bool CrashLog::WriteSavegame(char *filename, const char *filename_last) const
if (!Map::IsInitialized()) return false; if (!Map::IsInitialized()) return false;
try { try {
GamelogEmergency(); _gamelog.Emergency();
this->CreateFileName(filename, filename_last, ".sav"); this->CreateFileName(filename, filename_last, ".sav");

View File

@ -13,6 +13,7 @@
#include "gfx_type.h" #include "gfx_type.h"
#include "company_base.h" #include "company_base.h"
#include "newgrf_config.h" #include "newgrf_config.h"
#include "gamelog.h"
#include "network/core/tcp_content_type.h" #include "network/core/tcp_content_type.h"
#include "timer/timer_game_calendar.h" #include "timer/timer_game_calendar.h"
@ -44,21 +45,11 @@ struct LoadCheckData {
GRFConfig *grfconfig; ///< NewGrf configuration from save. GRFConfig *grfconfig; ///< NewGrf configuration from save.
GRFListCompatibility grf_compatibility; ///< Summary state of NewGrfs, whether missing files or only compatible found. GRFListCompatibility grf_compatibility; ///< Summary state of NewGrfs, whether missing files or only compatible found.
struct LoggedAction *gamelog_action; ///< Gamelog actions Gamelog gamelog; ///< Gamelog actions
uint gamelog_actions; ///< Number of gamelog actions
LoadCheckData() : grfconfig(nullptr), LoadCheckData() : grfconfig(nullptr),
grf_compatibility(GLC_NOT_FOUND), gamelog_action(nullptr), gamelog_actions(0) grf_compatibility(GLC_NOT_FOUND)
{ {
this->Clear();
}
/**
* Don't leak memory at program exit
*/
~LoadCheckData()
{
this->Clear();
} }
/** /**

View File

@ -28,6 +28,7 @@
#include "gamelog.h" #include "gamelog.h"
#include "stringfilter_type.h" #include "stringfilter_type.h"
#include "misc_cmd.h" #include "misc_cmd.h"
#include "gamelog_internal.h"
#include "widgets/fios_widget.h" #include "widgets/fios_widget.h"
@ -41,7 +42,6 @@ LoadCheckData _load_check_data; ///< Data loaded from save during SL_LOAD_CHE
static bool _fios_path_changed; static bool _fios_path_changed;
static bool _savegame_sort_dirty; static bool _savegame_sort_dirty;
/** /**
* Reset read data. * Reset read data.
*/ */
@ -60,9 +60,7 @@ void LoadCheckData::Clear()
} }
companies.clear(); companies.clear();
GamelogFree(this->gamelog_action, this->gamelog_actions); this->gamelog.Reset();
this->gamelog_action = nullptr;
this->gamelog_actions = 0;
ClearGRFConfigList(&this->grfconfig); ClearGRFConfigList(&this->grfconfig);
} }

View File

@ -30,13 +30,23 @@ extern uint32 _ttdp_version; ///< version of TTDP savegame (if applicable
extern SaveLoadVersion _sl_version; ///< the major savegame version identifier extern SaveLoadVersion _sl_version; ///< the major savegame version identifier
extern byte _sl_minor_version; ///< the minor savegame version, DO NOT USE! extern byte _sl_minor_version; ///< the minor savegame version, DO NOT USE!
Gamelog _gamelog; ///< Gamelog instance
static GamelogActionType _gamelog_action_type = GLAT_NONE; ///< action to record if anything changes LoggedChange::~LoggedChange()
{
if (this->ct == GLCT_SETTING) free(this->setting.name);
}
LoggedAction *_gamelog_action = nullptr; ///< first logged action Gamelog::Gamelog()
uint _gamelog_actions = 0; ///< number of actions {
static LoggedAction *_current_action = nullptr; ///< current action we are logging, nullptr when there is no action active this->data = std::make_unique<GamelogInternalData>();
this->action_type = GLAT_NONE;
this->current_action = nullptr;
}
Gamelog::~Gamelog()
{
}
/** /**
* Return the revision string for the current client version, for use in gamelog. * Return the revision string for the current client version, for use in gamelog.
@ -67,60 +77,40 @@ static const char * GetGamelogRevisionString()
* Action is allocated only when there is at least one change * Action is allocated only when there is at least one change
* @param at type of action * @param at type of action
*/ */
void GamelogStartAction(GamelogActionType at) void Gamelog::StartAction(GamelogActionType at)
{ {
assert(_gamelog_action_type == GLAT_NONE); // do not allow starting new action without stopping the previous first assert(this->action_type == GLAT_NONE); // do not allow starting new action without stopping the previous first
_gamelog_action_type = at; this->action_type = at;
} }
/** /**
* Stops logging of any changes * Stops logging of any changes
*/ */
void GamelogStopAction() void Gamelog::StopAction()
{ {
assert(_gamelog_action_type != GLAT_NONE); // nobody should try to stop if there is no action in progress assert(this->action_type != GLAT_NONE); // nobody should try to stop if there is no action in progress
bool print = _current_action != nullptr; bool print = this->current_action != nullptr;
_current_action = nullptr; this->current_action = nullptr;
_gamelog_action_type = GLAT_NONE; this->action_type = GLAT_NONE;
if (print) GamelogPrintDebug(5); if (print) this->PrintDebug(5);
} }
void GamelogStopAnyAction() void Gamelog::StopAnyAction()
{ {
if (_gamelog_action_type != GLAT_NONE) GamelogStopAction(); if (this->action_type != GLAT_NONE) this->StopAction();
}
/**
* Frees the memory allocated by a gamelog
*/
void GamelogFree(LoggedAction *gamelog_action, uint gamelog_actions)
{
for (uint i = 0; i < gamelog_actions; i++) {
const LoggedAction *la = &gamelog_action[i];
for (uint j = 0; j < la->changes; j++) {
const LoggedChange *lc = &la->change[j];
if (lc->ct == GLCT_SETTING) free(lc->setting.name);
}
free(la->change);
}
free(gamelog_action);
} }
/** /**
* Resets and frees all memory allocated - used before loading or starting a new game * Resets and frees all memory allocated - used before loading or starting a new game
*/ */
void GamelogReset() void Gamelog::Reset()
{ {
assert(_gamelog_action_type == GLAT_NONE); assert(this->action_type == GLAT_NONE);
GamelogFree(_gamelog_action, _gamelog_actions); this->data->action.clear();
this->current_action = nullptr;
_gamelog_action = nullptr;
_gamelog_actions = 0;
_current_action = nullptr;
} }
/** /**
@ -190,56 +180,52 @@ typedef SmallMap<uint32, GRFPresence> GrfIDMapping;
* Prints active gamelog * Prints active gamelog
* @param proc the procedure to draw with * @param proc the procedure to draw with
*/ */
void GamelogPrint(std::function<void(const char*)> proc) void Gamelog::Print(std::function<void(const char*)> proc)
{ {
char buffer[1024]; char buffer[1024];
GrfIDMapping grf_names; GrfIDMapping grf_names;
proc("---- gamelog start ----"); proc("---- gamelog start ----");
const LoggedAction *laend = &_gamelog_action[_gamelog_actions]; for (const LoggedAction &la : this->data->action) {
assert((uint)la.at < GLAT_END);
for (const LoggedAction *la = _gamelog_action; la != laend; la++) { seprintf(buffer, lastof(buffer), "Tick %u: %s", (uint)la.tick, la_text[(uint)la.at]);
assert((uint)la->at < GLAT_END);
seprintf(buffer, lastof(buffer), "Tick %u: %s", (uint)la->tick, la_text[(uint)la->at]);
proc(buffer); proc(buffer);
const LoggedChange *lcend = &la->change[la->changes]; for (const LoggedChange &lc : la.change) {
for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
char *buf = buffer; char *buf = buffer;
switch (lc->ct) { switch (lc.ct) {
default: NOT_REACHED(); default: NOT_REACHED();
case GLCT_MODE: case GLCT_MODE:
/* Changing landscape, or going from scenario editor to game or back. */ /* Changing landscape, or going from scenario editor to game or back. */
buf += seprintf(buf, lastof(buffer), "New game mode: %u landscape: %u", buf += seprintf(buf, lastof(buffer), "New game mode: %u landscape: %u",
(uint)lc->mode.mode, (uint)lc->mode.landscape); (uint)lc.mode.mode, (uint)lc.mode.landscape);
break; break;
case GLCT_REVISION: case GLCT_REVISION:
/* The game was loaded in a diffferent version than before. */ /* The game was loaded in a diffferent version than before. */
buf += seprintf(buf, lastof(buffer), "Revision text changed to %s, savegame version %u, ", buf += seprintf(buf, lastof(buffer), "Revision text changed to %s, savegame version %u, ",
lc->revision.text, lc->revision.slver); lc.revision.text, lc.revision.slver);
switch (lc->revision.modified) { switch (lc.revision.modified) {
case 0: buf += seprintf(buf, lastof(buffer), "not "); break; case 0: buf += seprintf(buf, lastof(buffer), "not "); break;
case 1: buf += seprintf(buf, lastof(buffer), "maybe "); break; case 1: buf += seprintf(buf, lastof(buffer), "maybe "); break;
default: break; default: break;
} }
buf += seprintf(buf, lastof(buffer), "modified, _openttd_newgrf_version = 0x%08x", lc->revision.newgrf); buf += seprintf(buf, lastof(buffer), "modified, _openttd_newgrf_version = 0x%08x", lc.revision.newgrf);
break; break;
case GLCT_OLDVER: case GLCT_OLDVER:
/* The game was loaded from before 0.7.0-beta1. */ /* The game was loaded from before 0.7.0-beta1. */
buf += seprintf(buf, lastof(buffer), "Conversion from "); buf += seprintf(buf, lastof(buffer), "Conversion from ");
switch (lc->oldver.type) { switch (lc.oldver.type) {
default: NOT_REACHED(); default: NOT_REACHED();
case SGT_OTTD: case SGT_OTTD:
buf += seprintf(buf, lastof(buffer), "OTTD savegame without gamelog: version %u, %u", buf += seprintf(buf, lastof(buffer), "OTTD savegame without gamelog: version %u, %u",
GB(lc->oldver.version, 8, 16), GB(lc->oldver.version, 0, 8)); GB(lc.oldver.version, 8, 16), GB(lc.oldver.version, 0, 8));
break; break;
case SGT_TTO: case SGT_TTO:
@ -253,11 +239,11 @@ void GamelogPrint(std::function<void(const char*)> proc)
case SGT_TTDP1: case SGT_TTDP1:
case SGT_TTDP2: case SGT_TTDP2:
buf += seprintf(buf, lastof(buffer), "TTDP savegame, %s format", buf += seprintf(buf, lastof(buffer), "TTDP savegame, %s format",
lc->oldver.type == SGT_TTDP1 ? "old" : "new"); lc.oldver.type == SGT_TTDP1 ? "old" : "new");
if (lc->oldver.version != 0) { if (lc.oldver.version != 0) {
buf += seprintf(buf, lastof(buffer), ", TTDP version %u.%u.%u.%u", buf += seprintf(buf, lastof(buffer), ", TTDP version %u.%u.%u.%u",
GB(lc->oldver.version, 24, 8), GB(lc->oldver.version, 20, 4), GB(lc.oldver.version, 24, 8), GB(lc.oldver.version, 20, 4),
GB(lc->oldver.version, 16, 4), GB(lc->oldver.version, 0, 16)); GB(lc.oldver.version, 16, 4), GB(lc.oldver.version, 0, 16));
} }
break; break;
} }
@ -265,29 +251,29 @@ void GamelogPrint(std::function<void(const char*)> proc)
case GLCT_SETTING: case GLCT_SETTING:
/* A setting with the SF_NO_NETWORK flag got changed; these settings usually affect NewGRFs, such as road side or wagon speed limits. */ /* A setting with the SF_NO_NETWORK flag got changed; these settings usually affect NewGRFs, such as road side or wagon speed limits. */
buf += seprintf(buf, lastof(buffer), "Setting changed: %s : %d -> %d", lc->setting.name, lc->setting.oldval, lc->setting.newval); buf += seprintf(buf, lastof(buffer), "Setting changed: %s : %d -> %d", lc.setting.name, lc.setting.oldval, lc.setting.newval);
break; break;
case GLCT_GRFADD: { case GLCT_GRFADD: {
/* A NewGRF got added to the game, either at the start of the game (never an issue), or later on when it could be an issue. */ /* A NewGRF got added to the game, either at the start of the game (never an issue), or later on when it could be an issue. */
const GRFConfig *gc = FindGRFConfig(lc->grfadd.grfid, FGCM_EXACT, lc->grfadd.md5sum); const GRFConfig *gc = FindGRFConfig(lc.grfadd.grfid, FGCM_EXACT, lc.grfadd.md5sum);
buf += seprintf(buf, lastof(buffer), "Added NewGRF: "); buf += seprintf(buf, lastof(buffer), "Added NewGRF: ");
buf = PrintGrfInfo(buf, lastof(buffer), lc->grfadd.grfid, lc->grfadd.md5sum, gc); buf = PrintGrfInfo(buf, lastof(buffer), lc.grfadd.grfid, lc.grfadd.md5sum, gc);
GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid); GrfIDMapping::Pair *gm = grf_names.Find(lc.grfrem.grfid);
if (gm != grf_names.End() && !gm->second.was_missing) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was already added!"); if (gm != grf_names.End() && !gm->second.was_missing) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was already added!");
grf_names[lc->grfadd.grfid] = gc; grf_names[lc.grfadd.grfid] = gc;
break; break;
} }
case GLCT_GRFREM: { case GLCT_GRFREM: {
/* A NewGRF got removed from the game, either manually or by it missing when loading the game. */ /* A NewGRF got removed from the game, either manually or by it missing when loading the game. */
GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid); GrfIDMapping::Pair *gm = grf_names.Find(lc.grfrem.grfid);
buf += seprintf(buf, lastof(buffer), la->at == GLAT_LOAD ? "Missing NewGRF: " : "Removed NewGRF: "); buf += seprintf(buf, lastof(buffer), la.at == GLAT_LOAD ? "Missing NewGRF: " : "Removed NewGRF: ");
buf = PrintGrfInfo(buf, lastof(buffer), lc->grfrem.grfid, nullptr, gm != grf_names.End() ? gm->second.gc : nullptr); buf = PrintGrfInfo(buf, lastof(buffer), lc.grfrem.grfid, nullptr, gm != grf_names.End() ? gm->second.gc : nullptr);
if (gm == grf_names.End()) { if (gm == grf_names.End()) {
buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!"); buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!");
} else { } else {
if (la->at == GLAT_LOAD) { if (la.at == GLAT_LOAD) {
/* Missing grfs on load are not removed from the configuration */ /* Missing grfs on load are not removed from the configuration */
gm->second.was_missing = true; gm->second.was_missing = true;
} else { } else {
@ -299,40 +285,40 @@ void GamelogPrint(std::function<void(const char*)> proc)
case GLCT_GRFCOMPAT: { case GLCT_GRFCOMPAT: {
/* Another version of the same NewGRF got loaded. */ /* Another version of the same NewGRF got loaded. */
const GRFConfig *gc = FindGRFConfig(lc->grfadd.grfid, FGCM_EXACT, lc->grfadd.md5sum); const GRFConfig *gc = FindGRFConfig(lc.grfadd.grfid, FGCM_EXACT, lc.grfadd.md5sum);
buf += seprintf(buf, lastof(buffer), "Compatible NewGRF loaded: "); buf += seprintf(buf, lastof(buffer), "Compatible NewGRF loaded: ");
buf = PrintGrfInfo(buf, lastof(buffer), lc->grfcompat.grfid, lc->grfcompat.md5sum, gc); buf = PrintGrfInfo(buf, lastof(buffer), lc.grfcompat.grfid, lc.grfcompat.md5sum, gc);
if (!grf_names.Contains(lc->grfcompat.grfid)) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!"); if (!grf_names.Contains(lc.grfcompat.grfid)) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!");
grf_names[lc->grfcompat.grfid] = gc; grf_names[lc.grfcompat.grfid] = gc;
break; break;
} }
case GLCT_GRFPARAM: { case GLCT_GRFPARAM: {
/* A parameter of a NewGRF got changed after the game was started. */ /* A parameter of a NewGRF got changed after the game was started. */
GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid); GrfIDMapping::Pair *gm = grf_names.Find(lc.grfrem.grfid);
buf += seprintf(buf, lastof(buffer), "GRF parameter changed: "); buf += seprintf(buf, lastof(buffer), "GRF parameter changed: ");
buf = PrintGrfInfo(buf, lastof(buffer), lc->grfparam.grfid, nullptr, gm != grf_names.End() ? gm->second.gc : nullptr); buf = PrintGrfInfo(buf, lastof(buffer), lc.grfparam.grfid, nullptr, gm != grf_names.End() ? gm->second.gc : nullptr);
if (gm == grf_names.End()) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!"); if (gm == grf_names.End()) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!");
break; break;
} }
case GLCT_GRFMOVE: { case GLCT_GRFMOVE: {
/* The order of NewGRFs got changed, which might cause some other NewGRFs to behave differently. */ /* The order of NewGRFs got changed, which might cause some other NewGRFs to behave differently. */
GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid); GrfIDMapping::Pair *gm = grf_names.Find(lc.grfrem.grfid);
buf += seprintf(buf, lastof(buffer), "GRF order changed: %08X moved %d places %s", buf += seprintf(buf, lastof(buffer), "GRF order changed: %08X moved %d places %s",
BSWAP32(lc->grfmove.grfid), abs(lc->grfmove.offset), lc->grfmove.offset >= 0 ? "down" : "up" ); BSWAP32(lc.grfmove.grfid), abs(lc.grfmove.offset), lc.grfmove.offset >= 0 ? "down" : "up" );
buf = PrintGrfInfo(buf, lastof(buffer), lc->grfmove.grfid, nullptr, gm != grf_names.End() ? gm->second.gc : nullptr); buf = PrintGrfInfo(buf, lastof(buffer), lc.grfmove.grfid, nullptr, gm != grf_names.End() ? gm->second.gc : nullptr);
if (gm == grf_names.End()) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!"); if (gm == grf_names.End()) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!");
break; break;
} }
case GLCT_GRFBUG: { case GLCT_GRFBUG: {
/* A specific bug in a NewGRF, that could cause wide spread problems, has been noted during the execution of the game. */ /* A specific bug in a NewGRF, that could cause wide spread problems, has been noted during the execution of the game. */
GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid); GrfIDMapping::Pair *gm = grf_names.Find(lc.grfrem.grfid);
assert (lc->grfbug.bug == GBUG_VEH_LENGTH); assert(lc.grfbug.bug == GBUG_VEH_LENGTH);
buf += seprintf(buf, lastof(buffer), "Rail vehicle changes length outside a depot: GRF ID %08X, internal ID 0x%X", BSWAP32(lc->grfbug.grfid), (uint)lc->grfbug.data); buf += seprintf(buf, lastof(buffer), "Rail vehicle changes length outside a depot: GRF ID %08X, internal ID 0x%X", BSWAP32(lc.grfbug.grfid), (uint)lc.grfbug.data);
buf = PrintGrfInfo(buf, lastof(buffer), lc->grfbug.grfid, nullptr, gm != grf_names.End() ? gm->second.gc : nullptr); buf = PrintGrfInfo(buf, lastof(buffer), lc.grfbug.grfid, nullptr, gm != grf_names.End() ? gm->second.gc : nullptr);
if (gm == grf_names.End()) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!"); if (gm == grf_names.End()) buf += seprintf(buf, lastof(buffer), ". Gamelog inconsistency: GrfID was never added!");
break; break;
} }
@ -352,9 +338,9 @@ void GamelogPrint(std::function<void(const char*)> proc)
/** Print the gamelog data to the console. */ /** Print the gamelog data to the console. */
void GamelogPrintConsole() void Gamelog::PrintConsole()
{ {
GamelogPrint([](const char *s) { this->Print([](const char *s) {
IConsolePrint(CC_WARNING, s); IConsolePrint(CC_WARNING, s);
}); });
} }
@ -365,9 +351,9 @@ void GamelogPrintConsole()
* doesn't matter that much. At least it gives more uniform code... * doesn't matter that much. At least it gives more uniform code...
* @param level debug level we need to print stuff * @param level debug level we need to print stuff
*/ */
void GamelogPrintDebug(int level) void Gamelog::PrintDebug(int level)
{ {
GamelogPrint([level](const char *s) { this->Print([level](const char *s) {
Debug(gamelog, level, "{}", s); Debug(gamelog, level, "{}", s);
}); });
} }
@ -379,23 +365,17 @@ void GamelogPrintDebug(int level)
* @param ct type of change * @param ct type of change
* @return new LoggedChange, or nullptr if there is no action active * @return new LoggedChange, or nullptr if there is no action active
*/ */
static LoggedChange *GamelogChange(GamelogChangeType ct) LoggedChange *Gamelog::Change(GamelogChangeType ct)
{ {
if (_current_action == nullptr) { if (this->current_action == nullptr) {
if (_gamelog_action_type == GLAT_NONE) return nullptr; if (this->action_type == GLAT_NONE) return nullptr;
_gamelog_action = ReallocT(_gamelog_action, _gamelog_actions + 1); this->current_action = &this->data->action.emplace_back();
_current_action = &_gamelog_action[_gamelog_actions++]; this->current_action->at = this->action_type;
this->current_action->tick = TimerGameTick::counter;
_current_action->at = _gamelog_action_type;
_current_action->tick = TimerGameTick::counter;
_current_action->change = nullptr;
_current_action->changes = 0;
} }
_current_action->change = ReallocT(_current_action->change, _current_action->changes + 1); LoggedChange *lc = &this->current_action->change.emplace_back();
LoggedChange *lc = &_current_action->change[_current_action->changes++];
lc->ct = ct; lc->ct = ct;
return lc; return lc;
@ -405,41 +385,37 @@ static LoggedChange *GamelogChange(GamelogChangeType ct)
/** /**
* Logs a emergency savegame * Logs a emergency savegame
*/ */
void GamelogEmergency() void Gamelog::Emergency()
{ {
/* Terminate any active action */ /* Terminate any active action */
if (_gamelog_action_type != GLAT_NONE) GamelogStopAction(); if (this->action_type != GLAT_NONE) this->StopAction();
GamelogStartAction(GLAT_EMERGENCY); this->StartAction(GLAT_EMERGENCY);
GamelogChange(GLCT_EMERGENCY); this->Change(GLCT_EMERGENCY);
GamelogStopAction(); this->StopAction();
} }
/** /**
* Finds out if current game is a loaded emergency savegame. * Finds out if current game is a loaded emergency savegame.
*/ */
bool GamelogTestEmergency() bool Gamelog::TestEmergency()
{ {
const LoggedChange *emergency = nullptr; for (const LoggedAction &la : this->data->action) {
for (const LoggedChange &lc : la.change) {
const LoggedAction *laend = &_gamelog_action[_gamelog_actions]; if (lc.ct == GLCT_EMERGENCY) return true;
for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
const LoggedChange *lcend = &la->change[la->changes];
for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
if (lc->ct == GLCT_EMERGENCY) emergency = lc;
} }
} }
return (emergency != nullptr); return false;
} }
/** /**
* Logs a change in game revision * Logs a change in game revision
*/ */
void GamelogRevision() void Gamelog::Revision()
{ {
assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD); assert(this->action_type == GLAT_START || this->action_type == GLAT_LOAD);
LoggedChange *lc = GamelogChange(GLCT_REVISION); LoggedChange *lc = this->Change(GLCT_REVISION);
if (lc == nullptr) return; if (lc == nullptr) return;
memset(lc->revision.text, 0, sizeof(lc->revision.text)); memset(lc->revision.text, 0, sizeof(lc->revision.text));
@ -452,11 +428,11 @@ void GamelogRevision()
/** /**
* Logs a change in game mode (scenario editor or game) * Logs a change in game mode (scenario editor or game)
*/ */
void GamelogMode() void Gamelog::Mode()
{ {
assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_CHEAT); assert(this->action_type == GLAT_START || this->action_type == GLAT_LOAD || this->action_type == GLAT_CHEAT);
LoggedChange *lc = GamelogChange(GLCT_MODE); LoggedChange *lc = this->Change(GLCT_MODE);
if (lc == nullptr) return; if (lc == nullptr) return;
lc->mode.mode = _game_mode; lc->mode.mode = _game_mode;
@ -466,11 +442,11 @@ void GamelogMode()
/** /**
* Logs loading from savegame without gamelog * Logs loading from savegame without gamelog
*/ */
void GamelogOldver() void Gamelog::Oldver()
{ {
assert(_gamelog_action_type == GLAT_LOAD); assert(this->action_type == GLAT_LOAD);
LoggedChange *lc = GamelogChange(GLCT_OLDVER); LoggedChange *lc = this->Change(GLCT_OLDVER);
if (lc == nullptr) return; if (lc == nullptr) return;
lc->oldver.type = _savegame_type; lc->oldver.type = _savegame_type;
@ -483,11 +459,11 @@ void GamelogOldver()
* @param oldval old setting value * @param oldval old setting value
* @param newval new setting value * @param newval new setting value
*/ */
void GamelogSetting(const std::string &name, int32 oldval, int32 newval) void Gamelog::Setting(const std::string &name, int32 oldval, int32 newval)
{ {
assert(_gamelog_action_type == GLAT_SETTING); assert(this->action_type == GLAT_SETTING);
LoggedChange *lc = GamelogChange(GLCT_SETTING); LoggedChange *lc = this->Change(GLCT_SETTING);
if (lc == nullptr) return; if (lc == nullptr) return;
lc->setting.name = stredup(name.c_str()); lc->setting.name = stredup(name.c_str());
@ -500,22 +476,20 @@ void GamelogSetting(const std::string &name, int32 oldval, int32 newval)
* Finds out if current revision is different than last revision stored in the savegame. * Finds out if current revision is different than last revision stored in the savegame.
* Appends GLCT_REVISION when the revision string changed * Appends GLCT_REVISION when the revision string changed
*/ */
void GamelogTestRevision() void Gamelog::TestRevision()
{ {
const LoggedChange *rev = nullptr; const LoggedChange *rev = nullptr;
const LoggedAction *laend = &_gamelog_action[_gamelog_actions]; for (const LoggedAction &la : this->data->action) {
for (const LoggedAction *la = _gamelog_action; la != laend; la++) { for (const LoggedChange &lc : la.change) {
const LoggedChange *lcend = &la->change[la->changes]; if (lc.ct == GLCT_REVISION) rev = &lc;
for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
if (lc->ct == GLCT_REVISION) rev = lc;
} }
} }
if (rev == nullptr || strcmp(rev->revision.text, GetGamelogRevisionString()) != 0 || if (rev == nullptr || strcmp(rev->revision.text, GetGamelogRevisionString()) != 0 ||
rev->revision.modified != _openttd_revision_modified || rev->revision.modified != _openttd_revision_modified ||
rev->revision.newgrf != _openttd_newgrf_version) { rev->revision.newgrf != _openttd_newgrf_version) {
GamelogRevision(); this->Revision();
} }
} }
@ -523,19 +497,17 @@ void GamelogTestRevision()
* Finds last stored game mode or landscape. * Finds last stored game mode or landscape.
* Any change is logged * Any change is logged
*/ */
void GamelogTestMode() void Gamelog::TestMode()
{ {
const LoggedChange *mode = nullptr; const LoggedChange *mode = nullptr;
const LoggedAction *laend = &_gamelog_action[_gamelog_actions]; for (const LoggedAction &la : this->data->action) {
for (const LoggedAction *la = _gamelog_action; la != laend; la++) { for (const LoggedChange &lc : la.change) {
const LoggedChange *lcend = &la->change[la->changes]; if (lc.ct == GLCT_MODE) mode = &lc;
for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
if (lc->ct == GLCT_MODE) mode = lc;
} }
} }
if (mode == nullptr || mode->mode.mode != _game_mode || mode->mode.landscape != _settings_game.game_creation.landscape) GamelogMode(); if (mode == nullptr || mode->mode.mode != _game_mode || mode->mode.landscape != _settings_game.game_creation.landscape) this->Mode();
} }
@ -545,11 +517,11 @@ void GamelogTestMode()
* @param bug type of bug, @see enum GRFBugs * @param bug type of bug, @see enum GRFBugs
* @param data additional data * @param data additional data
*/ */
static void GamelogGRFBug(uint32 grfid, byte bug, uint64 data) void Gamelog::GRFBug(uint32 grfid, byte bug, uint64 data)
{ {
assert(_gamelog_action_type == GLAT_GRFBUG); assert(this->action_type == GLAT_GRFBUG);
LoggedChange *lc = GamelogChange(GLCT_GRFBUG); LoggedChange *lc = this->Change(GLCT_GRFBUG);
if (lc == nullptr) return; if (lc == nullptr) return;
lc->grfbug.data = data; lc->grfbug.data = data;
@ -566,22 +538,20 @@ static void GamelogGRFBug(uint32 grfid, byte bug, uint64 data)
* @param internal_id the internal ID of whatever's broken in the NewGRF * @param internal_id the internal ID of whatever's broken in the NewGRF
* @return true iff a unique record was done * @return true iff a unique record was done
*/ */
bool GamelogGRFBugReverse(uint32 grfid, uint16 internal_id) bool Gamelog::GRFBugReverse(uint32 grfid, uint16 internal_id)
{ {
const LoggedAction *laend = &_gamelog_action[_gamelog_actions]; for (const LoggedAction &la : this->data->action) {
for (const LoggedAction *la = _gamelog_action; la != laend; la++) { for (const LoggedChange &lc : la.change) {
const LoggedChange *lcend = &la->change[la->changes]; if (lc.ct == GLCT_GRFBUG && lc.grfbug.grfid == grfid &&
for (const LoggedChange *lc = la->change; lc != lcend; lc++) { lc.grfbug.bug == GBUG_VEH_LENGTH && lc.grfbug.data == internal_id) {
if (lc->ct == GLCT_GRFBUG && lc->grfbug.grfid == grfid &&
lc->grfbug.bug == GBUG_VEH_LENGTH && lc->grfbug.data == internal_id) {
return false; return false;
} }
} }
} }
GamelogStartAction(GLAT_GRFBUG); this->StartAction(GLAT_GRFBUG);
GamelogGRFBug(grfid, GBUG_VEH_LENGTH, internal_id); this->GRFBug(grfid, GBUG_VEH_LENGTH, internal_id);
GamelogStopAction(); this->StopAction();
return true; return true;
} }
@ -601,11 +571,11 @@ static inline bool IsLoggableGrfConfig(const GRFConfig *g)
* Logs removal of a GRF * Logs removal of a GRF
* @param grfid ID of removed GRF * @param grfid ID of removed GRF
*/ */
void GamelogGRFRemove(uint32 grfid) void Gamelog::GRFRemove(uint32 grfid)
{ {
assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF); assert(this->action_type == GLAT_LOAD || this->action_type == GLAT_GRF);
LoggedChange *lc = GamelogChange(GLCT_GRFREM); LoggedChange *lc = this->Change(GLCT_GRFREM);
if (lc == nullptr) return; if (lc == nullptr) return;
lc->grfrem.grfid = grfid; lc->grfrem.grfid = grfid;
@ -615,13 +585,13 @@ void GamelogGRFRemove(uint32 grfid)
* Logs adding of a GRF * Logs adding of a GRF
* @param newg added GRF * @param newg added GRF
*/ */
void GamelogGRFAdd(const GRFConfig *newg) void Gamelog::GRFAdd(const GRFConfig *newg)
{ {
assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_GRF); assert(this->action_type == GLAT_LOAD || this->action_type == GLAT_START || this->action_type == GLAT_GRF);
if (!IsLoggableGrfConfig(newg)) return; if (!IsLoggableGrfConfig(newg)) return;
LoggedChange *lc = GamelogChange(GLCT_GRFADD); LoggedChange *lc = this->Change(GLCT_GRFADD);
if (lc == nullptr) return; if (lc == nullptr) return;
lc->grfadd = newg->ident; lc->grfadd = newg->ident;
@ -632,11 +602,11 @@ void GamelogGRFAdd(const GRFConfig *newg)
* (the same ID, but different MD5 hash) * (the same ID, but different MD5 hash)
* @param newg new (updated) GRF * @param newg new (updated) GRF
*/ */
void GamelogGRFCompatible(const GRFIdentifier *newg) void Gamelog::GRFCompatible(const GRFIdentifier *newg)
{ {
assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF); assert(this->action_type == GLAT_LOAD || this->action_type == GLAT_GRF);
LoggedChange *lc = GamelogChange(GLCT_GRFCOMPAT); LoggedChange *lc = this->Change(GLCT_GRFCOMPAT);
if (lc == nullptr) return; if (lc == nullptr) return;
lc->grfcompat = *newg; lc->grfcompat = *newg;
@ -647,11 +617,11 @@ void GamelogGRFCompatible(const GRFIdentifier *newg)
* @param grfid GRF that is moved * @param grfid GRF that is moved
* @param offset how far it is moved, positive = moved down * @param offset how far it is moved, positive = moved down
*/ */
static void GamelogGRFMove(uint32 grfid, int32 offset) void Gamelog::GRFMove(uint32 grfid, int32 offset)
{ {
assert(_gamelog_action_type == GLAT_GRF); assert(this->action_type == GLAT_GRF);
LoggedChange *lc = GamelogChange(GLCT_GRFMOVE); LoggedChange *lc = this->Change(GLCT_GRFMOVE);
if (lc == nullptr) return; if (lc == nullptr) return;
lc->grfmove.grfid = grfid; lc->grfmove.grfid = grfid;
@ -663,11 +633,11 @@ static void GamelogGRFMove(uint32 grfid, int32 offset)
* Details about parameters changed are not stored * Details about parameters changed are not stored
* @param grfid ID of GRF to store * @param grfid ID of GRF to store
*/ */
static void GamelogGRFParameters(uint32 grfid) void Gamelog::GRFParameters(uint32 grfid)
{ {
assert(_gamelog_action_type == GLAT_GRF); assert(this->action_type == GLAT_GRF);
LoggedChange *lc = GamelogChange(GLCT_GRFPARAM); LoggedChange *lc = this->Change(GLCT_GRFPARAM);
if (lc == nullptr) return; if (lc == nullptr) return;
lc->grfparam.grfid = grfid; lc->grfparam.grfid = grfid;
@ -678,12 +648,12 @@ static void GamelogGRFParameters(uint32 grfid)
* Useful when old savegame is loaded or when new game is started * Useful when old savegame is loaded or when new game is started
* @param newg head of GRF linked list * @param newg head of GRF linked list
*/ */
void GamelogGRFAddList(const GRFConfig *newg) void Gamelog::GRFAddList(const GRFConfig *newg)
{ {
assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD); assert(this->action_type == GLAT_START || this->action_type == GLAT_LOAD);
for (; newg != nullptr; newg = newg->next) { for (; newg != nullptr; newg = newg->next) {
GamelogGRFAdd(newg); this->GRFAdd(newg);
} }
} }
@ -719,7 +689,7 @@ static GRFList *GenerateGRFList(const GRFConfig *grfc)
* @param oldc original GRF list * @param oldc original GRF list
* @param newc new GRF list * @param newc new GRF list
*/ */
void GamelogGRFUpdate(const GRFConfig *oldc, const GRFConfig *newc) void Gamelog::GRFUpdate(const GRFConfig *oldc, const GRFConfig *newc)
{ {
GRFList *ol = GenerateGRFList(oldc); GRFList *ol = GenerateGRFList(oldc);
GRFList *nl = GenerateGRFList(newc); GRFList *nl = GenerateGRFList(newc);
@ -742,7 +712,7 @@ void GamelogGRFUpdate(const GRFConfig *oldc, const GRFConfig *newc)
} }
if (oi == ol->n) { if (oi == ol->n) {
/* GRF couldn't be found in the OLD list, GRF was ADDED */ /* GRF couldn't be found in the OLD list, GRF was ADDED */
GamelogGRFAdd(nl->grf[n++]); this->GRFAdd(nl->grf[n++]);
continue; continue;
} }
for (ni = 0; ni < nl->n; ni++) { for (ni = 0; ni < nl->n; ni++) {
@ -755,7 +725,7 @@ void GamelogGRFUpdate(const GRFConfig *oldc, const GRFConfig *newc)
} }
if (ni == nl->n) { if (ni == nl->n) {
/* GRF couldn't be found in the NEW list, GRF was REMOVED */ /* GRF couldn't be found in the NEW list, GRF was REMOVED */
GamelogGRFRemove(ol->grf[o++]->ident.grfid); this->GRFRemove(ol->grf[o++]->ident.grfid);
continue; continue;
} }
@ -769,18 +739,18 @@ void GamelogGRFUpdate(const GRFConfig *oldc, const GRFConfig *newc)
if (ni >= oi) { // prefer the one that is moved further if (ni >= oi) { // prefer the one that is moved further
/* GRF was moved down */ /* GRF was moved down */
GamelogGRFMove(ol->grf[o++]->ident.grfid, ni); this->GRFMove(ol->grf[o++]->ident.grfid, ni);
} else { } else {
GamelogGRFMove(nl->grf[n++]->ident.grfid, -(int)oi); this->GRFMove(nl->grf[n++]->ident.grfid, -(int)oi);
} }
} else { } else {
if (memcmp(og->ident.md5sum, ng->ident.md5sum, sizeof(og->ident.md5sum)) != 0) { if (memcmp(og->ident.md5sum, ng->ident.md5sum, sizeof(og->ident.md5sum)) != 0) {
/* md5sum changed, probably loading 'compatible' GRF */ /* md5sum changed, probably loading 'compatible' GRF */
GamelogGRFCompatible(&nl->grf[n]->ident); this->GRFCompatible(&nl->grf[n]->ident);
} }
if (og->num_params != ng->num_params || memcmp(og->param, ng->param, og->num_params * sizeof(og->param[0])) != 0) { if (og->num_params != ng->num_params || memcmp(og->param, ng->param, og->num_params * sizeof(og->param[0])) != 0) {
GamelogGRFParameters(ol->grf[o]->ident.grfid); this->GRFParameters(ol->grf[o]->ident.grfid);
} }
o++; o++;
@ -788,8 +758,8 @@ void GamelogGRFUpdate(const GRFConfig *oldc, const GRFConfig *newc)
} }
} }
while (o < ol->n) GamelogGRFRemove(ol->grf[o++]->ident.grfid); // remaining GRFs were removed ... while (o < ol->n) this->GRFRemove(ol->grf[o++]->ident.grfid); // remaining GRFs were removed ...
while (n < nl->n) GamelogGRFAdd (nl->grf[n++]); // ... or added while (n < nl->n) this->GRFAdd (nl->grf[n++]); // ... or added
free(ol); free(ol);
free(nl); free(nl);
@ -797,24 +767,20 @@ void GamelogGRFUpdate(const GRFConfig *oldc, const GRFConfig *newc)
/** /**
* Get some basic information from the given gamelog. * Get some basic information from the given gamelog.
* @param gamelog_action Pointer to the gamelog to extract information from.
* @param gamelog_actions Number of actions in the given gamelog.
* @param[out] last_ottd_rev OpenTTD NewGRF version from the binary that saved the savegame last. * @param[out] last_ottd_rev OpenTTD NewGRF version from the binary that saved the savegame last.
* @param[out] ever_modified Max value of 'modified' from all binaries that ever saved this savegame. * @param[out] ever_modified Max value of 'modified' from all binaries that ever saved this savegame.
* @param[out] removed_newgrfs Set to true if any NewGRFs have been removed. * @param[out] removed_newgrfs Set to true if any NewGRFs have been removed.
*/ */
void GamelogInfo(LoggedAction *gamelog_action, uint gamelog_actions, uint32 *last_ottd_rev, byte *ever_modified, bool *removed_newgrfs) void Gamelog::Info(uint32 *last_ottd_rev, byte *ever_modified, bool *removed_newgrfs)
{ {
const LoggedAction *laend = &gamelog_action[gamelog_actions]; for (const LoggedAction &la : this->data->action) {
for (const LoggedAction *la = gamelog_action; la != laend; la++) { for (const LoggedChange &lc : la.change) {
const LoggedChange *lcend = &la->change[la->changes]; switch (lc.ct) {
for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
switch (lc->ct) {
default: break; default: break;
case GLCT_REVISION: case GLCT_REVISION:
*last_ottd_rev = lc->revision.newgrf; *last_ottd_rev = lc.revision.newgrf;
*ever_modified = std::max(*ever_modified, lc->revision.modified); *ever_modified = std::max(*ever_modified, lc.revision.modified);
break; break;
case GLCT_GRFREM: case GLCT_GRFREM:
@ -824,3 +790,21 @@ void GamelogInfo(LoggedAction *gamelog_action, uint gamelog_actions, uint32 *las
} }
} }
} }
/**
* Try to find the overridden GRF identifier of the given GRF.
* @param c the GRF to get the 'previous' version of.
* @return the GRF identifier or \a c if none could be found.
*/
const GRFIdentifier *Gamelog::GetOverriddenIdentifier(const GRFConfig *c)
{
assert(c != nullptr);
const LoggedAction &la = this->data->action.back();
if (la.at != GLAT_LOAD) return &c->ident;
for (const LoggedChange &lc : la.change) {
if (lc.ct == GLCT_GRFCOMPAT && lc.grfcompat.grfid == c->ident.grfid) return &lc.grfcompat;
}
return &c->ident;
}

View File

@ -26,36 +26,77 @@ enum GamelogActionType : uint8 {
GLAT_NONE = 0xFF, ///< No logging active; in savegames, end of list GLAT_NONE = 0xFF, ///< No logging active; in savegames, end of list
}; };
void GamelogStartAction(GamelogActionType at); /** Type of logged change */
void GamelogStopAction(); enum GamelogChangeType : uint8 {
void GamelogStopAnyAction(); GLCT_MODE, ///< Scenario editor x Game, different landscape
GLCT_REVISION, ///< Changed game revision string
GLCT_OLDVER, ///< Loaded from savegame without logged data
GLCT_SETTING, ///< Non-networksafe setting value changed
GLCT_GRFADD, ///< Removed GRF
GLCT_GRFREM, ///< Added GRF
GLCT_GRFCOMPAT, ///< Loading compatible GRF
GLCT_GRFPARAM, ///< GRF parameter changed
GLCT_GRFMOVE, ///< GRF order changed
GLCT_GRFBUG, ///< GRF bug triggered
GLCT_EMERGENCY, ///< Emergency savegame
GLCT_END, ///< So we know how many GLCTs are there
GLCT_NONE = 0xFF, ///< In savegames, end of list
};
void GamelogFree(struct LoggedAction *gamelog_action, uint gamelog_actions); struct LoggedChange;
void GamelogReset(); struct LoggedAction;
struct GamelogInternalData;
void GamelogPrint(std::function<void(const char *)> proc); class Gamelog {
void GamelogPrintDebug(int level); private:
void GamelogPrintConsole(); std::unique_ptr<GamelogInternalData> data;
GamelogActionType action_type;
struct LoggedAction *current_action;
void GamelogEmergency(); LoggedChange *Change(GamelogChangeType ct);
bool GamelogTestEmergency();
void GamelogRevision(); public:
void GamelogMode(); Gamelog();
void GamelogOldver(); ~Gamelog();
void GamelogSetting(const std::string &name, int32 oldval, int32 newval);
void GamelogGRFUpdate(const GRFConfig *oldg, const GRFConfig *newg); void StartAction(GamelogActionType at);
void GamelogGRFAddList(const GRFConfig *newg); void StopAction();
void GamelogGRFRemove(uint32 grfid); void StopAnyAction();
void GamelogGRFAdd(const GRFConfig *newg);
void GamelogGRFCompatible(const GRFIdentifier *newg);
void GamelogTestRevision(); void Reset();
void GamelogTestMode();
bool GamelogGRFBugReverse(uint32 grfid, uint16 internal_id); void Print(std::function<void(const char *)> proc);
void PrintDebug(int level);
void PrintConsole();
void GamelogInfo(struct LoggedAction *gamelog_action, uint gamelog_actions, uint32 *last_ottd_rev, byte *ever_modified, bool *removed_newgrfs); void Emergency();
bool TestEmergency();
void Revision();
void Mode();
void Oldver();
void Setting(const std::string &name, int32 oldval, int32 newval);
void GRFUpdate(const GRFConfig *oldg, const GRFConfig *newg);
void GRFAddList(const GRFConfig *newg);
void GRFRemove(uint32 grfid);
void GRFAdd(const GRFConfig *newg);
void GRFBug(uint32 grfid, byte bug, uint64 data);
bool GRFBugReverse(uint32 grfid, uint16 internal_id);
void GRFCompatible(const GRFIdentifier *newg);
void GRFMove(uint32 grfid, int32 offset);
void GRFParameters(uint32 grfid);
void TestRevision();
void TestMode();
void Info(uint32 *last_ottd_rev, byte *ever_modified, bool *removed_newgrfs);
const GRFIdentifier *GetOverriddenIdentifier(const GRFConfig *c);
/* Saveload handler for gamelog needs access to internal data. */
friend struct GLOGChunkHandler;
};
extern Gamelog _gamelog;
#endif /* GAMELOG_H */ #endif /* GAMELOG_H */

View File

@ -12,24 +12,6 @@
#include "gamelog.h" #include "gamelog.h"
/** Type of logged change */
enum GamelogChangeType : uint8 {
GLCT_MODE, ///< Scenario editor x Game, different landscape
GLCT_REVISION, ///< Changed game revision string
GLCT_OLDVER, ///< Loaded from savegame without logged data
GLCT_SETTING, ///< Non-networksafe setting value changed
GLCT_GRFADD, ///< Removed GRF
GLCT_GRFREM, ///< Added GRF
GLCT_GRFCOMPAT, ///< Loading compatible GRF
GLCT_GRFPARAM, ///< GRF parameter changed
GLCT_GRFMOVE, ///< GRF order changed
GLCT_GRFBUG, ///< GRF bug triggered
GLCT_EMERGENCY, ///< Emergency savegame
GLCT_END, ///< So we know how many GLCTs are there
GLCT_NONE = 0xFF, ///< In savegames, end of list
};
static const uint GAMELOG_REVISION_LENGTH = 15; static const uint GAMELOG_REVISION_LENGTH = 15;
/** Contains information about one logged change */ /** Contains information about one logged change */
@ -73,18 +55,20 @@ struct LoggedChange {
byte bug; ///< type of bug, @see enum GRFBugs byte bug; ///< type of bug, @see enum GRFBugs
} grfbug; } grfbug;
}; };
~LoggedChange();
}; };
/** Contains information about one logged action that caused at least one logged change */ /** Contains information about one logged action that caused at least one logged change */
struct LoggedAction { struct LoggedAction {
LoggedChange *change; ///< First logged change in this action std::vector<LoggedChange> change; ///< First logged change in this action
uint32 changes; ///< Number of changes in this action
GamelogActionType at; ///< Type of action GamelogActionType at; ///< Type of action
uint64 tick; ///< Tick when it happened uint64 tick; ///< Tick when it happened
}; };
extern LoggedAction *_gamelog_action; struct GamelogInternalData {
extern uint _gamelog_actions; std::vector<LoggedAction> action;
};
#endif /* GAMELOG_INTERNAL_H */ #endif /* GAMELOG_INTERNAL_H */

View File

@ -122,10 +122,10 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin
ResetObjectToPlace(); ResetObjectToPlace();
GamelogReset(); _gamelog.Reset();
GamelogStartAction(GLAT_START); _gamelog.StartAction(GLAT_START);
GamelogRevision(); _gamelog.Revision();
GamelogMode(); _gamelog.Mode();
GamelogGRFAddList(_grfconfig); _gamelog.GRFAddList(_grfconfig);
GamelogStopAction(); _gamelog.StopAction();
} }

View File

@ -1964,11 +1964,11 @@ static void NewGRFConfirmationCallback(Window *w, bool confirmed)
CloseWindowByClass(WC_TEXTFILE); CloseWindowByClass(WC_TEXTFILE);
NewGRFWindow *nw = dynamic_cast<NewGRFWindow*>(w); NewGRFWindow *nw = dynamic_cast<NewGRFWindow*>(w);
GamelogStartAction(GLAT_GRF); _gamelog.StartAction(GLAT_GRF);
GamelogGRFUpdate(_grfconfig, nw->actives); // log GRF changes _gamelog.GRFUpdate(_grfconfig, nw->actives); // log GRF changes
CopyGRFConfigList(nw->orig_list, nw->actives, false); CopyGRFConfigList(nw->orig_list, nw->actives, false);
ReloadNewGRFData(); ReloadNewGRFData();
GamelogStopAction(); _gamelog.StopAction();
/* Show new, updated list */ /* Show new, updated list */
GRFConfig *c; GRFConfig *c;

View File

@ -241,7 +241,7 @@ static void WriteSavegameInfo(const char *name)
byte ever_modified = 0; byte ever_modified = 0;
bool removed_newgrfs = false; bool removed_newgrfs = false;
GamelogInfo(_load_check_data.gamelog_action, _load_check_data.gamelog_actions, &last_ottd_rev, &ever_modified, &removed_newgrfs); _gamelog.Info(&last_ottd_rev, &ever_modified, &removed_newgrfs);
std::string message; std::string message;
message.reserve(1024); message.reserve(1024);
@ -311,7 +311,7 @@ static void ShutdownGame()
Game::Uninitialize(false); Game::Uninitialize(false);
/* Uninitialize variables that are allocated dynamically */ /* Uninitialize variables that are allocated dynamically */
GamelogReset(); _gamelog.Reset();
LinkGraphSchedule::Clear(); LinkGraphSchedule::Clear();
PoolBase::Clean(PT_ALL); PoolBase::Clean(PT_ALL);

View File

@ -227,7 +227,7 @@ void CDECL HandleCrash(int signum)
signal(*i, SIG_DFL); signal(*i, SIG_DFL);
} }
if (GamelogTestEmergency()) { if (_gamelog.TestEmergency()) {
ShowMacDialog("A serious fault condition occurred in the game. The game will shut down.", ShowMacDialog("A serious fault condition occurred in the game. The game will shut down.",
"As you loaded an emergency savegame no crash information will be generated.\n", "As you loaded an emergency savegame no crash information will be generated.\n",
"Quit"); "Quit");

View File

@ -155,7 +155,7 @@ static void CDECL HandleCrash(int signum)
signal(*i, SIG_DFL); signal(*i, SIG_DFL);
} }
if (GamelogTestEmergency()) { if (_gamelog.TestEmergency()) {
printf("A serious fault condition occurred in the game. The game will shut down.\n"); printf("A serious fault condition occurred in the game. The game will shut down.\n");
printf("As you loaded an emergency savegame no crash information will be generated.\n"); printf("As you loaded an emergency savegame no crash information will be generated.\n");
abort(); abort();

View File

@ -540,7 +540,7 @@ static LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep)
ExitProcess(2); ExitProcess(2);
} }
if (GamelogTestEmergency()) { if (_gamelog.TestEmergency()) {
static const wchar_t _emergency_crash[] = static const wchar_t _emergency_crash[] =
L"A serious fault condition occurred in the game. The game will shut down.\n" L"A serious fault condition occurred in the game. The game will shut down.\n"
L"As you loaded an emergency savegame no crash information will be generated.\n"; L"As you loaded an emergency savegame no crash information will be generated.\n";

View File

@ -340,24 +340,6 @@ static void ResetSignalHandlers()
signal(SIGFPE, _prev_fpe); signal(SIGFPE, _prev_fpe);
} }
/**
* Try to find the overridden GRF identifier of the given GRF.
* @param c the GRF to get the 'previous' version of.
* @return the GRF identifier or \a c if none could be found.
*/
static const GRFIdentifier *GetOverriddenIdentifier(const GRFConfig *c)
{
const LoggedAction *la = &_gamelog_action[_gamelog_actions - 1];
if (la->at != GLAT_LOAD) return &c->ident;
const LoggedChange *lcend = &la->change[la->changes];
for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
if (lc->ct == GLCT_GRFCOMPAT && lc->grfcompat.grfid == c->ident.grfid) return &lc->grfcompat;
}
return &c->ident;
}
/** Was the saveload crash because of missing NewGRFs? */ /** Was the saveload crash because of missing NewGRFs? */
static bool _saveload_crash_with_missing_newgrfs = false; static bool _saveload_crash_with_missing_newgrfs = false;
@ -405,7 +387,7 @@ static void CDECL HandleSavegameLoadCrash(int signum)
for (const GRFConfig *c = _grfconfig; c != nullptr; c = c->next) { for (const GRFConfig *c = _grfconfig; c != nullptr; c = c->next) {
if (HasBit(c->flags, GCF_COMPATIBLE)) { if (HasBit(c->flags, GCF_COMPATIBLE)) {
const GRFIdentifier *replaced = GetOverriddenIdentifier(c); const GRFIdentifier *replaced = _gamelog.GetOverriddenIdentifier(c);
char original_md5[40]; char original_md5[40];
char replaced_md5[40]; char replaced_md5[40];
md5sumToString(original_md5, lastof(original_md5), c->original_md5sum); md5sumToString(original_md5, lastof(original_md5), c->original_md5sum);
@ -591,10 +573,10 @@ bool AfterLoadGame()
/* The LFSR used in RunTileLoop iteration cannot have a zeroed state, make it non-zeroed. */ /* The LFSR used in RunTileLoop iteration cannot have a zeroed state, make it non-zeroed. */
if (_cur_tileloop_tile == 0) _cur_tileloop_tile = 1; if (_cur_tileloop_tile == 0) _cur_tileloop_tile = 1;
if (IsSavegameVersionBefore(SLV_98)) GamelogOldver(); if (IsSavegameVersionBefore(SLV_98)) _gamelog.Oldver();
GamelogTestRevision(); _gamelog.TestRevision();
GamelogTestMode(); _gamelog.TestMode();
RebuildTownKdtree(); RebuildTownKdtree();
RebuildStationKdtree(); RebuildStationKdtree();
@ -602,7 +584,7 @@ bool AfterLoadGame()
* that otherwise won't exist in the tree. */ * that otherwise won't exist in the tree. */
RebuildViewportKdtree(); RebuildViewportKdtree();
if (IsSavegameVersionBefore(SLV_98)) GamelogGRFAddList(_grfconfig); if (IsSavegameVersionBefore(SLV_98)) _gamelog.GRFAddList(_grfconfig);
if (IsSavegameVersionBefore(SLV_119)) { if (IsSavegameVersionBefore(SLV_119)) {
_pause_mode = (_pause_mode == 2) ? PM_PAUSED_NORMAL : PM_UNPAUSED; _pause_mode = (_pause_mode == 2) ? PM_PAUSED_NORMAL : PM_UNPAUSED;
@ -729,9 +711,9 @@ bool AfterLoadGame()
GRFListCompatibility gcf_res = IsGoodGRFConfigList(_grfconfig); GRFListCompatibility gcf_res = IsGoodGRFConfigList(_grfconfig);
for (GRFConfig *c = _grfconfig; c != nullptr; c = c->next) { for (GRFConfig *c = _grfconfig; c != nullptr; c = c->next) {
if (c->status == GCS_NOT_FOUND) { if (c->status == GCS_NOT_FOUND) {
GamelogGRFRemove(c->ident.grfid); _gamelog.GRFRemove(c->ident.grfid);
} else if (HasBit(c->flags, GCF_COMPATIBLE)) { } else if (HasBit(c->flags, GCF_COMPATIBLE)) {
GamelogGRFCompatible(&c->ident); _gamelog.GRFCompatible(&c->ident);
} }
} }
@ -3260,7 +3242,7 @@ bool AfterLoadGame()
AfterLoadCompanyStats(); AfterLoadCompanyStats();
AfterLoadStoryBook(); AfterLoadStoryBook();
GamelogPrintDebug(1); _gamelog.PrintDebug(1);
InitializeWindowsAndCaches(); InitializeWindowsAndCaches();
/* Restore the signals */ /* Restore the signals */

View File

@ -298,12 +298,11 @@ public:
void Save(LoggedAction *la) const override void Save(LoggedAction *la) const override
{ {
SlSetStructListLength(la->changes); SlSetStructListLength(la->change.size());
const LoggedChange *lcend = &la->change[la->changes]; for (auto &lc : la->change) {
for (LoggedChange *lc = la->change; lc != lcend; lc++) { assert((uint)lc.ct < GLCT_END);
assert((uint)lc->ct < GLCT_END); SlObject(&lc, this->GetDescription());
SlObject(lc, this->GetDescription());
} }
} }
@ -315,27 +314,20 @@ public:
if (type >= GLCT_END) SlErrorCorrupt("Invalid gamelog change type"); if (type >= GLCT_END) SlErrorCorrupt("Invalid gamelog change type");
GamelogChangeType ct = (GamelogChangeType)type; GamelogChangeType ct = (GamelogChangeType)type;
la->change = ReallocT(la->change, la->changes + 1); LoggedChange &lc = la->change.emplace_back();
lc.ct = ct;
LoggedChange *lc = &la->change[la->changes++]; SlObject(&lc, this->GetLoadDescription());
memset(lc, 0, sizeof(*lc));
lc->ct = ct;
SlObject(lc, this->GetLoadDescription());
} }
return; return;
} }
size_t length = SlGetStructListLength(UINT32_MAX); size_t length = SlGetStructListLength(UINT32_MAX);
la->change = ReallocT(la->change, length); la->change.reserve(length);
la->changes = (uint32)length;
for (size_t i = 0; i < length; i++) { for (size_t i = 0; i < length; i++) {
LoggedChange *lc = &la->change[i]; LoggedChange &lc = la->change.emplace_back();
memset(lc, 0, sizeof(*lc)); lc.ct = (GamelogChangeType)SlReadByte();
SlObject(&lc, this->GetLoadDescription());
lc->ct = (GamelogChangeType)SlReadByte();
SlObject(lc, this->GetLoadDescription());
} }
} }
@ -352,10 +344,9 @@ static const SaveLoad _gamelog_desc[] = {
struct GLOGChunkHandler : ChunkHandler { struct GLOGChunkHandler : ChunkHandler {
GLOGChunkHandler() : ChunkHandler('GLOG', CH_TABLE) {} GLOGChunkHandler() : ChunkHandler('GLOG', CH_TABLE) {}
void LoadCommon(LoggedAction *&gamelog_action, uint &gamelog_actions) const void LoadCommon(Gamelog &gamelog) const
{ {
assert(gamelog_action == nullptr); assert(gamelog.data->action.empty());
assert(gamelog_actions == 0);
const std::vector<SaveLoad> slt = SlCompatTableHeader(_gamelog_desc, _gamelog_sl_compat); const std::vector<SaveLoad> slt = SlCompatTableHeader(_gamelog_desc, _gamelog_sl_compat);
@ -364,22 +355,16 @@ struct GLOGChunkHandler : ChunkHandler {
while ((type = SlReadByte()) != GLAT_NONE) { while ((type = SlReadByte()) != GLAT_NONE) {
if (type >= GLAT_END) SlErrorCorrupt("Invalid gamelog action type"); if (type >= GLAT_END) SlErrorCorrupt("Invalid gamelog action type");
gamelog_action = ReallocT(gamelog_action, gamelog_actions + 1); LoggedAction &la = gamelog.data->action.emplace_back();
LoggedAction *la = &gamelog_action[gamelog_actions++]; la.at = (GamelogActionType)type;
memset(la, 0, sizeof(*la)); SlObject(&la, slt);
la->at = (GamelogActionType)type;
SlObject(la, slt);
} }
return; return;
} }
while (SlIterateArray() != -1) { while (SlIterateArray() != -1) {
gamelog_action = ReallocT(gamelog_action, gamelog_actions + 1); LoggedAction &la = gamelog.data->action.emplace_back();
LoggedAction *la = &gamelog_action[gamelog_actions++]; SlObject(&la, slt);
memset(la, 0, sizeof(*la));
SlObject(la, slt);
} }
} }
@ -387,23 +372,21 @@ struct GLOGChunkHandler : ChunkHandler {
{ {
SlTableHeader(_gamelog_desc); SlTableHeader(_gamelog_desc);
const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
uint i = 0; uint i = 0;
for (LoggedAction *la = _gamelog_action; la != laend; la++, i++) { for (LoggedAction &la : _gamelog.data->action) {
SlSetArrayIndex(i); SlSetArrayIndex(i++);
SlObject(la, _gamelog_desc); SlObject(&la, _gamelog_desc);
} }
} }
void Load() const override void Load() const override
{ {
this->LoadCommon(_gamelog_action, _gamelog_actions); this->LoadCommon(_gamelog);
} }
void LoadCheck(size_t) const override void LoadCheck(size_t) const override
{ {
this->LoadCommon(_load_check_data.gamelog_action, _load_check_data.gamelog_actions); this->LoadCommon(_load_check_data.gamelog);
} }
}; };

View File

@ -348,7 +348,7 @@ void NORETURN SlError(StringID string, const std::string &extra_msg)
if (_sl.action == SLA_LOAD || _sl.action == SLA_PTRS) SlNullPointers(); if (_sl.action == SLA_LOAD || _sl.action == SLA_PTRS) SlNullPointers();
/* Logging could be active. */ /* Logging could be active. */
GamelogStopAnyAction(); _gamelog.StopAnyAction();
throw std::exception(); throw std::exception();
} }
@ -3129,7 +3129,7 @@ static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check)
* confuse old games */ * confuse old games */
InitializeGame(256, 256, true, true); InitializeGame(256, 256, true, true);
GamelogReset(); _gamelog.Reset();
if (IsSavegameVersionBefore(SLV_4)) { if (IsSavegameVersionBefore(SLV_4)) {
/* /*
@ -3175,16 +3175,16 @@ static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check)
/* The only part from AfterLoadGame() we need */ /* The only part from AfterLoadGame() we need */
_load_check_data.grf_compatibility = IsGoodGRFConfigList(_load_check_data.grfconfig); _load_check_data.grf_compatibility = IsGoodGRFConfigList(_load_check_data.grfconfig);
} else { } else {
GamelogStartAction(GLAT_LOAD); _gamelog.StartAction(GLAT_LOAD);
/* After loading fix up savegame for any internal changes that /* After loading fix up savegame for any internal changes that
* might have occurred since then. If it fails, load back the old game. */ * might have occurred since then. If it fails, load back the old game. */
if (!AfterLoadGame()) { if (!AfterLoadGame()) {
GamelogStopAction(); _gamelog.StopAction();
return SL_REINIT; return SL_REINIT;
} }
GamelogStopAction(); _gamelog.StopAction();
} }
return SL_OK; return SL_OK;
@ -3237,16 +3237,16 @@ SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop,
* Note: this is done here because AfterLoadGame is also called * Note: this is done here because AfterLoadGame is also called
* for OTTD savegames which have their own NewGRF logic. */ * for OTTD savegames which have their own NewGRF logic. */
ClearGRFConfigList(&_grfconfig); ClearGRFConfigList(&_grfconfig);
GamelogReset(); _gamelog.Reset();
if (!LoadOldSaveGame(filename)) return SL_REINIT; if (!LoadOldSaveGame(filename)) return SL_REINIT;
_sl_version = SL_MIN_VERSION; _sl_version = SL_MIN_VERSION;
_sl_minor_version = 0; _sl_minor_version = 0;
GamelogStartAction(GLAT_LOAD); _gamelog.StartAction(GLAT_LOAD);
if (!AfterLoadGame()) { if (!AfterLoadGame()) {
GamelogStopAction(); _gamelog.StopAction();
return SL_REINIT; return SL_REINIT;
} }
GamelogStopAction(); _gamelog.StopAction();
return SL_OK; return SL_OK;
} }

View File

@ -1401,9 +1401,9 @@ void IntSettingDesc::ChangeValue(const void *object, int32 newval) const
if (this->post_callback != nullptr) this->post_callback(newval); if (this->post_callback != nullptr) this->post_callback(newval);
if (this->flags & SF_NO_NETWORK) { if (this->flags & SF_NO_NETWORK) {
GamelogStartAction(GLAT_SETTING); _gamelog.StartAction(GLAT_SETTING);
GamelogSetting(this->GetName(), oldval, newval); _gamelog.Setting(this->GetName(), oldval, newval);
GamelogStopAction(); _gamelog.StopAction();
} }
SetWindowClassesDirty(WC_GAME_OPTIONS); SetWindowClassesDirty(WC_GAME_OPTIONS);

View File

@ -337,7 +337,7 @@ void VehicleLengthChanged(const Vehicle *u)
const Engine *engine = u->GetEngine(); const Engine *engine = u->GetEngine();
uint32 grfid = engine->grf_prop.grffile->grfid; uint32 grfid = engine->grf_prop.grffile->grfid;
GRFConfig *grfconfig = GetGRFConfig(grfid); GRFConfig *grfconfig = GetGRFConfig(grfid);
if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) { if (_gamelog.GRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true); ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
} }
} }