# gamelog.cpp

Go to the documentation of this file.

    00001 /* $Id: gamelog.cpp 16273 2009-05-10 21:33:55Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "openttd.h"
    00007 #include "saveload/saveload.h"
    00008 #include "core/alloc_func.hpp"
    00009 #include "variables.h"
    00010 #include "string_func.h"
    00011 #include "settings_type.h"
    00012 #include "gamelog.h"
    00013 #include "gamelog_internal.h"
    00014 #include "console_func.h"
    00015 #include "debug.h"
    00016 #include "rev.h"
    00017
    00018 #include <stdarg.h>
    00019
    00020 extern const uint16 SAVEGAME_VERSION;
    00021
    00022 extern SavegameType _savegame_type;
    00023
    00024 extern uint32 _ttdp_version;
    00025 extern uint16 _sl_version;
    00026 extern byte   _sl_minor_version;
    00027
    00028
    00029 static GamelogActionType _gamelog_action_type = GLAT_NONE;
    00030
    00031 LoggedAction *_gamelog_action = NULL;
    00032 uint _gamelog_actions         = 0;
    00033 static LoggedAction *_current_action = NULL;
    00034
    00035
    00040 void GamelogStartAction(GamelogActionType at)
    00041 {
    00042   assert(_gamelog_action_type == GLAT_NONE); // do not allow starting new action without stopping the previous first
    00043   _gamelog_action_type = at;
    00044 }
    00045
    00048 void GamelogStopAction()
    00049 {
    00050   assert(_gamelog_action_type != GLAT_NONE); // nobody should try to stop if there is no action in progress
    00051
    00052   bool print = _current_action != NULL;
    00053
    00054   _current_action = NULL;
    00055   _gamelog_action_type = GLAT_NONE;
    00056
    00057   if (print) GamelogPrintDebug(5);
    00058 }
    00059
    00062 void GamelogReset()
    00063 {
    00064   assert(_gamelog_action_type == GLAT_NONE);
    00065
    00066   for (uint i = 0; i < _gamelog_actions; i++) {
    00067     const LoggedAction *la = &_gamelog_action[i];
    00068     for (uint j = 0; j < la->changes; j++) {
    00069       const LoggedChange *lc = &la->change[j];
    00070       if (lc->ct == GLCT_SETTING) free(lc->setting.name);
    00071     }
    00072     free(la->change);
    00073   }
    00074
    00075   free(_gamelog_action);
    00076
    00077   _gamelog_action  = NULL;
    00078   _gamelog_actions = 0;
    00079   _current_action  = NULL;
    00080 }
    00081
    00082 enum {
    00083   GAMELOG_BUF_LEN = 1024
    00084 };
    00085
    00086 static int _dbgofs = 0;
    00087
    00088 static void AddDebugText(char *buf, const char *s, ...) WARN_FORMAT(2, 3);
    00089
    00090 static void AddDebugText(char *buf, const char *s, ...)
    00091 {
    00092   if (GAMELOG_BUF_LEN <= _dbgofs) return;
    00093
    00094   va_list va;
    00095
    00096   va_start(va, s);
    00097   _dbgofs += vsnprintf(buf + _dbgofs, GAMELOG_BUF_LEN - _dbgofs, s, va);
    00098   va_end(va);
    00099 }
    00100
    00101
    00105 static void PrintGrfFilename(char *buf, uint grfid)
    00106 {
    00107   const GRFConfig *gc = FindGRFConfig(grfid);
    00108
    00109   if (gc == NULL) return;
    00110
    00111   AddDebugText(buf, ", filename: %s", gc->filename);
    00112 }
    00113
    00118 static void PrintGrfInfo(char *buf, uint grfid, const uint8 *md5sum)
    00119 {
    00120   char txt[40];
    00121
    00122   md5sumToString(txt, lastof(txt), md5sum);
    00123
    00124   AddDebugText(buf, "GRF ID %08X, checksum %s", BSWAP32(grfid), txt);
    00125
    00126   PrintGrfFilename(buf, grfid);
    00127
    00128   return;
    00129 }
    00130
    00131
    00133 static const char *la_text[] = {
    00134   "new game started",
    00135   "game loaded",
    00136   "GRF config changed",
    00137   "cheat was used",
    00138   "settings changed",
    00139   "GRF bug triggered",
    00140   "emergency savegame",
    00141 };
    00142
    00143 assert_compile(lengthof(la_text) == GLAT_END);
    00144
    00145
    00147 void GamelogPrint(GamelogPrintProc *proc)
    00148 {
    00149   char buf[GAMELOG_BUF_LEN];
    00150
    00151   proc("---- gamelog start ----");
    00152
    00153   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
    00154
    00155   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
    00156     assert((uint)la->at < GLAT_END);
    00157
    00158     snprintf(buf, GAMELOG_BUF_LEN, "Tick %u: %s", (uint)la->tick, la_text[(uint)la->at]);
    00159     proc(buf);
    00160
    00161     const LoggedChange *lcend = &la->change[la->changes];
    00162
    00163     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
    00164       _dbgofs = 0;
    00165       AddDebugText(buf, "     ");
    00166
    00167       switch (lc->ct) {
    00168         default: NOT_REACHED();
    00169         case GLCT_MODE:
    00170           AddDebugText(buf, "New game mode: %u landscape: %u",
    00171             (uint)lc->mode.mode, (uint)lc->mode.landscape);
    00172           break;
    00173
    00174         case GLCT_REVISION:
    00175           AddDebugText(buf, "Revision text changed to %s, savegame version %u, ",
    00176             lc->revision.text, lc->revision.slver);
    00177
    00178           switch (lc->revision.modified) {
    00179             case 0: AddDebugText(buf, "not "); break;
    00180             case 1: AddDebugText(buf, "maybe "); break;
    00181             default: break;
    00182           }
    00183
    00184           AddDebugText(buf, "modified, _openttd_newgrf_version = 0x%08x", lc->revision.newgrf);
    00185           break;
    00186
    00187         case GLCT_OLDVER:
    00188           AddDebugText(buf, "Conversion from ");
    00189           switch (lc->oldver.type) {
    00190             default: NOT_REACHED();
    00191             case SGT_OTTD:
    00192               AddDebugText(buf, "OTTD savegame without gamelog: version %u, %u",
    00193                 GB(lc->oldver.version, 8, 16), GB(lc->oldver.version, 0, 8));
    00194               break;
    00195
    00196             case SGT_TTO:
    00197               AddDebugText(buf, "TTO savegame");
    00198               break;
    00199
    00200             case SGT_TTD:
    00201               AddDebugText(buf, "TTD savegame");
    00202               break;
    00203
    00204             case SGT_TTDP1:
    00205             case SGT_TTDP2:
    00206               AddDebugText(buf, "TTDP savegame, %s format",
    00207                 lc->oldver.type == SGT_TTDP1 ? "old" : "new");
    00208               if (lc->oldver.version != 0) {
    00209                 AddDebugText(buf, ", TTDP version %u.%u.%u.%u",
    00210                   GB(lc->oldver.version, 24, 8), GB(lc->oldver.version, 20, 4),
    00211                   GB(lc->oldver.version, 16, 4), GB(lc->oldver.version, 0, 16));
    00212               }
    00213               break;
    00214           }
    00215           break;
    00216
    00217         case GLCT_SETTING:
    00218           AddDebugText(buf, "Setting changed: %s : %d -> %d", lc->setting.name, lc->setting.oldval, lc->setting.newval);
    00219           break;
    00220
    00221         case GLCT_GRFADD:
    00222           AddDebugText(buf, "Added NewGRF: ");
    00223           PrintGrfInfo(buf, lc->grfadd.grfid, lc->grfadd.md5sum);
    00224           break;
    00225
    00226         case GLCT_GRFREM:
    00227           AddDebugText(buf, "Removed NewGRF: %08X", BSWAP32(lc->grfrem.grfid));
    00228           PrintGrfFilename(buf, lc->grfrem.grfid);
    00229           break;
    00230
    00231         case GLCT_GRFCOMPAT:
    00232           AddDebugText(buf, "Compatible NewGRF loaded: ");
    00233           PrintGrfInfo(buf, lc->grfcompat.grfid, lc->grfcompat.md5sum);
    00234           break;
    00235
    00236         case GLCT_GRFPARAM:
    00237           AddDebugText(buf, "GRF parameter changed: %08X", BSWAP32(lc->grfparam.grfid));
    00238           PrintGrfFilename(buf, lc->grfparam.grfid);
    00239           break;
    00240
    00241         case GLCT_GRFMOVE:
    00242           AddDebugText(buf, "GRF order changed: %08X moved %d places %s",
    00243             BSWAP32(lc->grfmove.grfid), abs(lc->grfmove.offset), lc->grfmove.offset >= 0 ? "down" : "up" );
    00244           PrintGrfFilename(buf, lc->grfmove.grfid);
    00245           break;
    00246
    00247         case GLCT_GRFBUG:
    00248           switch (lc->grfbug.bug) {
    00249             default: NOT_REACHED();
    00250             case GBUG_VEH_LENGTH:
    00251               AddDebugText(buf, "Rail vehicle changes length outside a depot: GRF ID %08X, internal ID 0x%X", BSWAP32(lc->grfbug.grfid), (uint)lc->grfbug.data);
    00252               PrintGrfFilename(buf, lc->grfbug.grfid);
    00253               break;
    00254           }
    00255
    00256         case GLCT_EMERGENCY:
    00257           break;
    00258       }
    00259
    00260       proc(buf);
    00261     }
    00262   }
    00263
    00264   proc("---- gamelog end ----");
    00265 }
    00266
    00267
    00268 static void GamelogPrintConsoleProc(const char *s)
    00269 {
    00270   IConsolePrint(CC_WARNING, s);
    00271 }
    00272
    00273 void GamelogPrintConsole()
    00274 {
    00275   GamelogPrint(&GamelogPrintConsoleProc);
    00276 }
    00277
    00278 static int _gamelog_print_level = 0;
    00279
    00280 static void GamelogPrintDebugProc(const char *s)
    00281 {
    00282   DEBUG(gamelog, _gamelog_print_level, "%s", s);
    00283 }
    00284
    00285
    00291 void GamelogPrintDebug(int level)
    00292 {
    00293   _gamelog_print_level = level;
    00294   GamelogPrint(&GamelogPrintDebugProc);
    00295 }
    00296
    00297
    00303 static LoggedChange *GamelogChange(GamelogChangeType ct)
    00304 {
    00305   if (_current_action == NULL) {
    00306     if (_gamelog_action_type == GLAT_NONE) return NULL;
    00307
    00308     _gamelog_action  = ReallocT(_gamelog_action, _gamelog_actions + 1);
    00309     _current_action  = &_gamelog_action[_gamelog_actions++];
    00310
    00311     _current_action->at      = _gamelog_action_type;
    00312     _current_action->tick    = _tick_counter;
    00313     _current_action->change  = NULL;
    00314     _current_action->changes = 0;
    00315   }
    00316
    00317   _current_action->change = ReallocT(_current_action->change, _current_action->changes + 1);
    00318
    00319   LoggedChange *lc = &_current_action->change[_current_action->changes++];
    00320   lc->ct = ct;
    00321
    00322   return lc;
    00323 }
    00324
    00325
    00328 void GamelogEmergency()
    00329 {
    00330   assert(_gamelog_action_type == GLAT_EMERGENCY);
    00331   GamelogChange(GLCT_EMERGENCY);
    00332 }
    00333
    00336 bool GamelogTestEmergency()
    00337 {
    00338   const LoggedChange *emergency = NULL;
    00339
    00340   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
    00341   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
    00342     const LoggedChange *lcend = &la->change[la->changes];
    00343     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
    00344       if (lc->ct == GLCT_EMERGENCY) emergency = lc;
    00345     }
    00346   }
    00347
    00348   return (emergency != NULL);
    00349 }
    00350
    00354 void GamelogRevision()
    00355 {
    00356   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
    00357
    00358   LoggedChange *lc = GamelogChange(GLCT_REVISION);
    00359   if (lc == NULL) return;
    00360
    00361   memset(lc->revision.text, 0, sizeof(lc->revision.text));
    00362   strecpy(lc->revision.text, _openttd_revision, lastof(lc->revision.text));
    00363   lc->revision.slver = SAVEGAME_VERSION;
    00364   lc->revision.modified = _openttd_revision_modified;
    00365   lc->revision.newgrf = _openttd_newgrf_version;
    00366 }
    00367
    00370 void GamelogMode()
    00371 {
    00372   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_CHEAT);
    00373
    00374   LoggedChange *lc = GamelogChange(GLCT_MODE);
    00375   if (lc == NULL) return;
    00376
    00377   lc->mode.mode      = _game_mode;
    00378   lc->mode.landscape = _settings_game.game_creation.landscape;
    00379 }
    00380
    00383 void GamelogOldver()
    00384 {
    00385   assert(_gamelog_action_type == GLAT_LOAD);
    00386
    00387   LoggedChange *lc = GamelogChange(GLCT_OLDVER);
    00388   if (lc == NULL) return;
    00389
    00390   lc->oldver.type = _savegame_type;
    00391   lc->oldver.version = (_savegame_type == SGT_OTTD ? ((uint32)_sl_version << 8 | _sl_minor_version) : _ttdp_version);
    00392 }
    00393
    00399 void GamelogSetting(const char *name, int32 oldval, int32 newval)
    00400 {
    00401   assert(_gamelog_action_type == GLAT_SETTING);
    00402
    00403   LoggedChange *lc = GamelogChange(GLCT_SETTING);
    00404   if (lc == NULL) return;
    00405
    00406   lc->setting.name = strdup(name);
    00407   lc->setting.oldval = oldval;
    00408   lc->setting.newval = newval;
    00409 }
    00410
    00411
    00415 void GamelogTestRevision()
    00416 {
    00417   const LoggedChange *rev = NULL;
    00418
    00419   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
    00420   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
    00421     const LoggedChange *lcend = &la->change[la->changes];
    00422     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
    00423       if (lc->ct == GLCT_REVISION) rev = lc;
    00424     }
    00425   }
    00426
    00427   if (rev == NULL || strcmp(rev->revision.text, _openttd_revision) != 0 ||
    00428       rev->revision.modified != _openttd_revision_modified ||
    00429       rev->revision.newgrf != _openttd_newgrf_version) {
    00430     GamelogRevision();
    00431   }
    00432 }
    00433
    00437 void GamelogTestMode()
    00438 {
    00439   const LoggedChange *mode = NULL;
    00440
    00441   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
    00442   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
    00443     const LoggedChange *lcend = &la->change[la->changes];
    00444     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
    00445       if (lc->ct == GLCT_MODE) mode = lc;
    00446     }
    00447   }
    00448
    00449   if (mode == NULL || mode->mode.mode != _game_mode || mode->mode.landscape != _settings_game.game_creation.landscape) GamelogMode();
    00450 }
    00451
    00452
    00458 static void GamelogGRFBug(uint32 grfid, byte bug, uint64 data)
    00459 {
    00460   assert(_gamelog_action_type == GLAT_GRFBUG);
    00461
    00462   LoggedChange *lc = GamelogChange(GLCT_GRFBUG);
    00463   if (lc == NULL) return;
    00464
    00465   lc->grfbug.data  = data;
    00466   lc->grfbug.grfid = grfid;
    00467   lc->grfbug.bug   = bug;
    00468 }
    00469
    00477 bool GamelogGRFBugReverse(uint32 grfid, uint16 internal_id)
    00478 {
    00479   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
    00480   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
    00481     const LoggedChange *lcend = &la->change[la->changes];
    00482     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
    00483       if (lc->ct == GLCT_GRFBUG && lc->grfbug.grfid == grfid &&
    00484           lc->grfbug.bug == GBUG_VEH_LENGTH && lc->grfbug.data == internal_id) {
    00485         return false;
    00486       }
    00487     }
    00488   }
    00489
    00490   GamelogStartAction(GLAT_GRFBUG);
    00491   GamelogGRFBug(grfid, GBUG_VEH_LENGTH, internal_id);
    00492   GamelogStopAction();
    00493
    00494   return true;
    00495 }
    00496
    00497
    00502 static inline bool IsLoggableGrfConfig(const GRFConfig *g)
    00503 {
    00504   return !HasBit(g->flags, GCF_STATIC) && g->status != GCS_NOT_FOUND;
    00505 }
    00506
    00510 void GamelogGRFRemove(uint32 grfid)
    00511 {
    00512   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
    00513
    00514   LoggedChange *lc = GamelogChange(GLCT_GRFREM);
    00515   if (lc == NULL) return;
    00516
    00517   lc->grfrem.grfid = grfid;
    00518 }
    00519
    00523 void GamelogGRFAdd(const GRFConfig *newg)
    00524 {
    00525   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_GRF);
    00526
    00527   if (!IsLoggableGrfConfig(newg)) return;
    00528
    00529   LoggedChange *lc = GamelogChange(GLCT_GRFADD);
    00530   if (lc == NULL) return;
    00531
    00532   memcpy(&lc->grfadd, newg, sizeof(GRFIdentifier));
    00533 }
    00534
    00539 void GamelogGRFCompatible(const GRFIdentifier *newg)
    00540 {
    00541   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
    00542
    00543   LoggedChange *lc = GamelogChange(GLCT_GRFCOMPAT);
    00544   if (lc == NULL) return;
    00545
    00546   memcpy(&lc->grfcompat, newg, sizeof(GRFIdentifier));
    00547 }
    00548
    00553 static void GamelogGRFMove(uint32 grfid, int32 offset)
    00554 {
    00555   assert(_gamelog_action_type == GLAT_GRF);
    00556
    00557   LoggedChange *lc = GamelogChange(GLCT_GRFMOVE);
    00558   if (lc == NULL) return;
    00559
    00560   lc->grfmove.grfid  = grfid;
    00561   lc->grfmove.offset = offset;
    00562 }
    00563
    00568 static void GamelogGRFParameters(uint32 grfid)
    00569 {
    00570   assert(_gamelog_action_type == GLAT_GRF);
    00571
    00572   LoggedChange *lc = GamelogChange(GLCT_GRFPARAM);
    00573   if (lc == NULL) return;
    00574
    00575   lc->grfparam.grfid = grfid;
    00576 }
    00577
    00582 void GamelogGRFAddList(const GRFConfig *newg)
    00583 {
    00584   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
    00585
    00586   for (; newg != NULL; newg = newg->next) {
    00587     GamelogGRFAdd(newg);
    00588   }
    00589 }
    00590
    00592 struct GRFList {
    00593   uint n;
    00594   const GRFConfig *grf[VARARRAY_SIZE];
    00595 };
    00596
    00600 static GRFList *GenerateGRFList(const GRFConfig *grfc)
    00601 {
    00602   uint n = 0;
    00603   for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
    00604     if (IsLoggableGrfConfig(g)) n++;
    00605   }
    00606
    00607   GRFList *list = (GRFList*)MallocT<byte>(sizeof(GRFList) + n * sizeof(GRFConfig*));
    00608
    00609   list->n = 0;
    00610   for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
    00611     if (IsLoggableGrfConfig(g)) list->grf[list->n++] = g;
    00612   }
    00613
    00614   return list;
    00615 }
    00616
    00621 void GamelogGRFUpdate(const GRFConfig *oldc, const GRFConfig *newc)
    00622 {
    00623   GRFList *ol = GenerateGRFList(oldc);
    00624   GRFList *nl = GenerateGRFList(newc);
    00625
    00626   uint o = 0, n = 0;
    00627
    00628   while (o < ol->n && n < nl->n) {
    00629     const GRFConfig *og = ol->grf[o];
    00630     const GRFConfig *ng = nl->grf[n];
    00631
    00632     if (og->grfid != ng->grfid) {
    00633       uint oi, ni;
    00634       for (oi = 0; oi < ol->n; oi++) {
    00635         if (ol->grf[oi]->grfid == nl->grf[n]->grfid) break;
    00636       }
    00637       if (oi < o) {
    00638         /* GRF was moved, this change has been logged already */
    00639         n++;
    00640         continue;
    00641       }
    00642       if (oi == ol->n) {
    00643         /* GRF couldn't be found in the OLD list, GRF was ADDED */
    00644         GamelogGRFAdd(nl->grf[n++]);
    00645         continue;
    00646       }
    00647       for (ni = 0; ni < nl->n; ni++) {
    00648         if (nl->grf[ni]->grfid == ol->grf[o]->grfid) break;
    00649       }
    00650       if (ni < n) {
    00651         /* GRF was moved, this change has been logged already */
    00652         o++;
    00653         continue;
    00654       }
    00655       if (ni == nl->n) {
    00656         /* GRF couldn't be found in the NEW list, GRF was REMOVED */
    00657         GamelogGRFRemove(ol->grf[o++]->grfid);
    00658         continue;
    00659       }
    00660
    00661       /* o < oi < ol->n
    00662        * n < ni < nl->n */
    00663       assert(ni > n && ni < nl->n);
    00664       assert(oi > o && oi < ol->n);
    00665
    00666       ni -= n; // number of GRFs it was moved downwards
    00667       oi -= o; // number of GRFs it was moved upwards
    00668
    00669       if (ni >= oi) { // prefer the one that is moved further
    00670         /* GRF was moved down */
    00671         GamelogGRFMove(ol->grf[o++]->grfid, ni);
    00672       } else {
    00673         GamelogGRFMove(nl->grf[n++]->grfid, -(int)oi);
    00674       }
    00675     } else {
    00676       if (memcmp(og->md5sum, ng->md5sum, sizeof(og->md5sum)) != 0) {
    00677         /* md5sum changed, probably loading 'compatible' GRF */
    00678         GamelogGRFCompatible(nl->grf[n]);
    00679       }
    00680
    00681       if (og->num_params != ng->num_params || memcmp(og->param, ng->param, og->num_params * sizeof(og->param[0])) != 0) {
    00682         GamelogGRFParameters(ol->grf[o]->grfid);
    00683       }
    00684
    00685       o++;
    00686       n++;
    00687     }
    00688   }
    00689
    00690   while (o < ol->n) GamelogGRFRemove(ol->grf[o++]->grfid); // remaining GRFs were removed ...
    00691   while (n < nl->n) GamelogGRFAdd   (nl->grf[n++]);    // ... or added
    00692
    00693   free(ol);
    00694   free(nl);
    00695 }
    00696
    00702 void GamelogGetOriginalGRFMD5Checksum(uint32 grfid, byte *md5sum)
    00703 {
    00704   const LoggedAction *la = &_gamelog_action[_gamelog_actions - 1];
    00705   /* There should always be a "start game" action */
    00706   assert(_gamelog_actions > 0);
    00707
    00708   do {
    00709     const LoggedChange *lc = &la->change[la->changes - 1];
    00710     /* There should always be at least one change per action */
    00711     assert(la->changes > 0);
    00712
    00713     do {
    00714       if (lc->ct == GLCT_GRFADD && lc->grfadd.grfid == grfid) {
    00715         memcpy(md5sum, lc->grfadd.md5sum, sizeof(lc->grfadd.md5sum));
    00716         return;
    00717       }
    00718     } while (lc-- != la->change);
    00719   } while (la-- != _gamelog_action);
    00720
    00721   NOT_REACHED();
    00722 }