diff --git a/MUNGA/SPOOLER.cpp b/MUNGA/SPOOLER.cpp index 888cc58..f101a12 100644 --- a/MUNGA/SPOOLER.cpp +++ b/MUNGA/SPOOLER.cpp @@ -200,6 +200,23 @@ SpoolRecorder::SpoolRecorder(): headerWritten(False), packetsRecorded(0) { + eggPath[0] = '\0'; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +void + SpoolRecorder::SetEggPath(const char *path) +{ + Check(this); + + if (path == NULL) + { + eggPath[0] = '\0'; + return; + } + strncpy(eggPath, path, sizeof(eggPath) - 1); + eggPath[sizeof(eggPath) - 1] = '\0'; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -364,6 +381,47 @@ void spool->SaveAs(filename); CopyFileA(filename, "last.spl", FALSE); + // + // The egg beside it, under the same stem. + // + // A spool is only half a recording: it says what moved, never the + // track it moved through, and playback will not start without the egg + // the race was run on. frontend.egg is rewritten by the next race set + // up on this machine, so a recording kept on its own quietly stops + // being playable as soon as somebody picks another track. Kept + // together they stay one artifact for as long as the folder does. + // + if (eggPath[0] != '\0') + { + char egg_copy[MAX_PATH]; + + strncpy(egg_copy, filename, sizeof(egg_copy) - 1); + egg_copy[sizeof(egg_copy) - 1] = '\0'; + + size_t length = strlen(egg_copy); + if (length > 4) + { + strcpy(egg_copy + length - 4, ".egg"); + if (CopyFileA(eggPath, egg_copy, FALSE)) + { + CopyFileA(eggPath, "last.egg", FALSE); + DEBUG_STREAM << "Record: kept the egg beside it as " + << egg_copy << "\n" << std::flush; + } + else + { + DEBUG_STREAM << "Record: could NOT copy the egg '" << eggPath + << "' - the spool will not replay without it\n" << std::flush; + } + } + } + else + { + DEBUG_STREAM << "Record: no egg path known, so none kept - this spool" + << " will need the matching egg supplied by hand to replay\n" + << std::flush; + } + DEBUG_STREAM << "Record: wrote " << filename << " - " << packetsRecorded << " packets, " << (written / 1024) << "KB" << (full ? " (truncated - buffer filled)" : "") diff --git a/MUNGA/SPOOLER.h b/MUNGA/SPOOLER.h index 6c3ade6..e418d64 100644 --- a/MUNGA/SPOOLER.h +++ b/MUNGA/SPOOLER.h @@ -83,7 +83,17 @@ public: void Record(const NetworkPacket *packet); - // Write SPOOLS\.spl and copy it to last.spl. + // Where the egg for this race lives, so it can be kept beside the + // recording. A spool records what MOVED and never the track it moved + // through, so the two are one artifact: without its egg a spool cannot + // be replayed, and frontend.egg is overwritten by the next race set up + // on this machine. Saving them together is what stops a recording + // going stale the moment somebody picks a different track. + void + SetEggPath(const char *path); + + // Write SPOOLS\.spl, the matching .egg beside it, and copy + // the spool to last.spl. void Save(); @@ -119,6 +129,7 @@ protected: Logical full; Logical headerWritten; int packetsRecorded; + char eggPath[260]; }; // The process-wide recorder, made on first use. diff --git a/MUNGA_L4/L4SPLR.cpp b/MUNGA_L4/L4SPLR.cpp index 54ecc9e..df356e5 100644 --- a/MUNGA_L4/L4SPLR.cpp +++ b/MUNGA_L4/L4SPLR.cpp @@ -215,11 +215,48 @@ L4PlaybackNetworkManager::L4PlaybackNetworkManager(): const char *egg_name = ((L4Application *) application)->GetEggNotationFileName(); + // + // Fall back to the egg saved with the recording. + // + // Every spool is written with its egg beside it under the same stem, + // and last.egg alongside last.spl, precisely so that a recording is one + // self-contained thing. Having to name the egg by hand invites naming + // the WRONG one - frontend.egg is rewritten by the next race set up on + // the machine, and a spool played against a different track would load + // happily and show nonsense. + // + static char found_egg[MAX_PATH]; + if (egg_name == NULL || strlen(egg_name) == 0) { - DEBUG_STREAM << "Playback: no egg given. A spool records the race but" - << " not the track it was run on, so -egg must name the one it" - << " was recorded with.\n" << std::flush; + CString spool_name = ((L4Application *) application)->GetSpoolFileName(); + const char *spool_text = + (!spool_name) ? "last.spl" : (const char *) spool_name; + + strncpy(found_egg, spool_text, sizeof(found_egg) - 1); + found_egg[sizeof(found_egg) - 1] = '\0'; + + size_t length = strlen(found_egg); + if (length > 4) + { + strcpy(found_egg + length - 4, ".egg"); + + FILE *probe = fopen(found_egg, "r"); + if (probe != NULL) + { + fclose(probe); + egg_name = found_egg; + DEBUG_STREAM << "Playback: using the egg saved with the" + << " recording, '" << found_egg << "'\n" << std::flush; + } + } + } + + if (egg_name == NULL || strlen(egg_name) == 0) + { + DEBUG_STREAM << "Playback: no egg. A spool records the race but not the" + << " track it was run on, and no egg was found beside the spool -" + << " name one with -egg.\n" << std::flush; return; } diff --git a/RP_L4/RPL4CONSOLE.cpp b/RP_L4/RPL4CONSOLE.cpp index 0deaf43..fe2b81f 100644 --- a/RP_L4/RPL4CONSOLE.cpp +++ b/RP_L4/RPL4CONSOLE.cpp @@ -6,6 +6,7 @@ #include "..\munga\appmgr.h" #include "..\munga\appmsg.h" #include "..\munga\console.h" +#include "..\munga\spooler.h" #include "..\rp\rpcnsl.h" #include "..\munga_l4\l4app.h" #include "..\munga_l4\l4net.h" @@ -669,6 +670,13 @@ Logical strncpy(gEggPath, egg_path, sizeof(gEggPath) - 1); gEggPath[sizeof(gEggPath) - 1] = '\0'; + // + // The recording needs this too. A spool says what moved; the egg says + // what it moved through, and the console is where the egg's name is + // actually known. + // + SpoolRecorder_Get()->SetEggPath(gEggPath); + // // The wire image of the egg: file newlines become NULs, exactly // what the arcade console sent (RPMission.ToEggFileMessages)