Skip to content

Commit 7cfc98a

Browse files
committed
refactor: Make youngest failed save store full version number
1 parent 221cd2b commit 7cfc98a

File tree

4 files changed

+85
-129
lines changed

4 files changed

+85
-129
lines changed

Source/Menu/ResumeForm.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public class Save
9090
public bool IsMultiplayer { get; private set; }
9191
public bool DbfEval { get; private set; } //Debrief Eval
9292

93-
public Save(string fileName, string currentBuild, int youngestFailedToResume)
93+
public Save(string fileName, string youngestVersionFailedToRestore)
9494
{
9595
File = fileName;
9696
System.Threading.Thread.Sleep(10);
@@ -101,7 +101,7 @@ public Save(string fileName, string currentBuild, int youngestFailedToResume)
101101
var version = inf.ReadString().Replace("\0", ""); // e.g. "0.9.0.1648" or "X1321" or "" (if compiled locally)
102102
var build = inf.ReadString().Replace("\0", ""); // e.g. 0.0.5223.24629 (2014-04-20 13:40:58Z)
103103
var versionOrBuild = version.Length > 0 ? version : build;
104-
var valid = VersionInfo.GetValidity(version, build, youngestFailedToResume);
104+
var valid = VersionInfo.GetValidity(version, build, youngestVersionFailedToRestore);
105105
// Read in multiplayer flag/ route/activity/path/player data.
106106
// Done so even if not elegant to be compatible with existing save files
107107
var routeNameOrMultipl = inf.ReadString();
@@ -213,7 +213,6 @@ void LoadSaves()
213213
{
214214
var saves = new List<Save>();
215215
var directory = UserSettings.UserDataFolder;
216-
var build = VersionInfo.Build.Contains(" ") ? VersionInfo.Build.Substring(VersionInfo.Build.IndexOf(" ") + 1) : null;
217216
var prefix = String.Empty;
218217

219218
if (SelectedAction == MainForm.UserAction.SinglePlayerTimetableGame)
@@ -244,7 +243,7 @@ void LoadSaves()
244243
// SavePacks are all in the same folder and activities may have the same name
245244
// (e.g. Short Passenger Run shrtpass.act) but belong to a different route,
246245
// so pick only the activities for the current route.
247-
var save = new Save(saveFile, build, Settings.YoungestFailedToRestore);
246+
var save = new Save(saveFile, Settings.YoungestVersionFailedToRestore);
248247
if (save.RouteName == Route.Name)
249248
{
250249
if (!save.IsMultiplayer ^ Multiplayer)
@@ -447,11 +446,10 @@ void buttonDeleteInvalid_Click(object sender, EventArgs e)
447446
var directory = UserSettings.UserDataFolder;
448447
if (Directory.Exists(directory))
449448
{
450-
var build = VersionInfo.Build.Contains(" ") ? VersionInfo.Build.Substring(VersionInfo.Build.IndexOf(" ") + 1) : null;
451449
var deletes = 0;
452450
foreach (var saveFile in Directory.GetFiles(directory, "*.save"))
453451
{
454-
var save = new Save(saveFile, build, Settings.YoungestFailedToRestore);
452+
var save = new Save(saveFile, Settings.YoungestVersionFailedToRestore);
455453
if (save.Valid == false)
456454
{
457455
foreach (var fileName in new[] {

Source/ORTS.Common/VersionInfo.cs

Lines changed: 65 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -29,76 +29,42 @@ namespace ORTS.Common
2929
public static class VersionInfo
3030
{
3131
static readonly string ApplicationPath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
32-
// GetRevision() must come before GetVersion()
33-
/// <summary>Revision number, e.g. Release: "1648", experimental: "1649", local: ""</summary>
34-
public static readonly string Revision = GetRevision("Revision.txt");
35-
/// <summary>Full version number, e.g. Release: "0.9.0.1648", experimental: "X.1649", local: ""</summary>
36-
public static readonly string Version = GetVersion("Version.txt");
37-
/// <summary>Full build number, e.g. "0.0.5223.24629 (2014-04-20 13:40:58Z)"</summary>
38-
public static readonly string Build = GetBuild("ORTS.Common.dll", "OpenRails.exe", "Menu.exe", "RunActivity.exe");
32+
33+
/// <summary>Full version, e.g. stable: "1.4", testing: "T1.4-1-g1234567", unstable: "U2021.01.01-0000", local: ""</summary>
34+
public static readonly string Version = GetVersion("OpenRails.exe");
35+
36+
/// <summary>Full build, e.g. "0.0.5223.24629 (2014-04-20 13:40:58Z)"</summary>
37+
public static readonly string Build = GetBuild("OpenRails.exe");
38+
3939
/// <summary>Version, but if "", returns Build</summary>
4040
public static readonly string VersionOrBuild = GetVersionOrBuild();
4141

42-
static string GetRevision(string fileName)
43-
{
44-
try
45-
{
46-
using (var f = new StreamReader(Path.Combine(ApplicationPath, fileName)))
47-
{
48-
var revision = f.ReadLine().Trim();
49-
if (revision.StartsWith("$Revision:") && revision.EndsWith("$"))
50-
{
51-
if (!revision.Contains(" 000 "))
52-
return revision.Substring(10, revision.Length - 11).Trim();
53-
}
54-
else
55-
{
56-
return revision;
57-
}
58-
}
59-
}
60-
catch
61-
{
62-
}
63-
return "";
64-
}
65-
6642
static string GetVersion(string fileName)
6743
{
6844
try
6945
{
70-
using (var f = new StreamReader(Path.Combine(ApplicationPath, fileName)))
71-
{
72-
var version = f.ReadLine().Trim();
73-
if (!String.IsNullOrEmpty(Revision))
74-
return version + "-" + Revision;
75-
}
46+
var version = FileVersionInfo.GetVersionInfo(Path.Combine(ApplicationPath, fileName));
47+
if (version.ProductVersion != version.FileVersion)
48+
return version.ProductVersion;
7649
}
7750
catch
7851
{
7952
}
8053
return "";
8154
}
8255

83-
static string GetBuild(params string[] fileNames)
56+
static string GetBuild(string fileName)
8457
{
8558
var builds = new Dictionary<TimeSpan, string>();
86-
foreach (var fileName in fileNames)
59+
try
8760
{
88-
try
89-
{
90-
var version = FileVersionInfo.GetVersionInfo(Path.Combine(ApplicationPath, fileName));
91-
builds.Add(new TimeSpan(version.ProductBuildPart, 0, 0, version.ProductPrivatePart * 2), version.ProductVersion);
92-
}
93-
catch
94-
{
95-
}
61+
var version = FileVersionInfo.GetVersionInfo(Path.Combine(ApplicationPath, fileName));
62+
var datetime = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);
63+
var timespan = new TimeSpan(version.FileBuildPart, 0, 0, version.FilePrivatePart * 2);
64+
return String.Format("{0} ({1:u})", version.FileVersion, datetime + timespan);
9665
}
97-
if (builds.Count > 0)
66+
catch
9867
{
99-
var datetime = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);
100-
var timespan = builds.Keys.OrderBy(ts => ts).Last();
101-
return String.Format("{0} ({1:u})", builds[timespan], datetime + timespan);
10268
}
10369
return "";
10470
}
@@ -109,70 +75,62 @@ static string GetVersionOrBuild()
10975
}
11076

11177
/// <summary>
112-
/// Find whether a requested version and build are valid for this build
78+
/// Compares a version and build with the youngest version which failed to restore, to see if that version/build is likely to restore successfully
11379
/// </summary>
114-
/// <param name="version">version to test again</param>
115-
/// <param name="build">build to test again</param>
116-
/// <param name="youngestFailedToResume">youngest build that failed to resume</param>
80+
/// <param name="version">version to test</param>
81+
/// <param name="build">build to test</param>
82+
/// <param name="youngestVersionFailedToRestore">youngest version that failed to restore</param>
11783
/// <returns>true or false when able to determine validity, null otherwise</returns>
118-
public static bool? GetValidity(string version, string build, int youngestFailedToResume)
84+
public static bool? GetValidity(string version, string build, string youngestVersionFailedToRestore)
11985
{
120-
int revision = GetRevisionFromVersion(version);
121-
int programRevision = 0;
122-
try // as Convert.ToInt32() can fail and version may be ""
123-
{
124-
programRevision = Convert.ToInt32(VersionInfo.Revision);
125-
}
126-
catch { } // ignore errors
127-
//MessageBox.Show(String.Format("VersionInfo.Build = {0}, build = {1}, version = {2}, youngestFailedToResume = {3}", VersionInfo.Build, build, Version, youngestFailedToResume));
128-
if (revision != 0) // compiled remotely by Open Rails
129-
{
130-
if (revision == programRevision)
131-
{
132-
return true;
133-
}
134-
else
135-
{
136-
if (revision > youngestFailedToResume // 1. Normal situation
137-
|| programRevision < youngestFailedToResume) // 2. If an old version of OR is used, then attempt to load Saves
138-
// which would be blocked by the current version of OR
139-
{
140-
return null;
141-
}
142-
}
143-
}
144-
else // compiled locally
145-
{
146-
if (build.EndsWith(VersionInfo.Build))
147-
{
148-
return true;
149-
}
150-
else
151-
{
152-
return null;
153-
}
154-
}
155-
return false; // default validity
86+
// Validity rules:
87+
// - Same version and build --> yes
88+
// - Same non-empty version --> yes
89+
// - Unable to parse save or program version --> maybe
90+
// - Save version > setting --> maybe
91+
// - Program version < setting --> maybe
92+
// - Default --> no
93+
94+
if (Version == version && Build == build) return true;
95+
if (Version.Length > 0 && version.Length > 0 && Version == version) return true;
96+
var saveVersion = ParseVersion(version);
97+
var programVersion = ParseVersion(Version);
98+
var settingVersion = ParseVersion(youngestVersionFailedToRestore);
99+
if (saveVersion.Major == 0 || programVersion.Major == 0) return null;
100+
if (saveVersion > settingVersion) return null;
101+
if (programVersion < settingVersion) return null;
102+
return false;
156103
}
157104

158105
/// <summary>
159-
/// Find the revision number (e.g. 1648) from the full version (e.g. 0.9.0.1648 or X.1648 or X1648)
106+
/// Converts many possible Open Rails versions into a standard Version struct
160107
/// </summary>
161-
/// <param name="version">full version</param>
162-
public static int GetRevisionFromVersion(string fullVersion)
108+
/// <param name="version">text version to parse</param>
109+
public static Version ParseVersion(string version)
163110
{
164-
var versionParts = fullVersion.Split('.');
165-
var revision = 0;
166-
try
167-
{
168-
var version = versionParts[versionParts.Length - 1];
169-
if (version.StartsWith("X"))
170-
version = version.Substring(1);
171-
// Might throw an error if it isn't a number like we expect.
172-
revision = Convert.ToInt32(version);
173-
}
174-
catch { }
175-
return revision;
111+
// Version numbers which we do parse:
112+
// - 0.9.0.1648 --> 0.9
113+
// - 1.3.1.4328 --> 1.3.1
114+
// - T1.3.1-241-g6ff150c21 --> 1.3.1.241
115+
// - X1.3.1-370-g7df5318c2 --> 1.3.1.370
116+
// - 1.4 --> 1.4
117+
// - 1.4-rc1 --> 1.4
118+
// - T1.4-2-g7db094316 --> 1.4.0.2
119+
// Version numbers which we do NOT parse:
120+
// - U2019.07.25-2200
121+
// - U2021.06.25-0406
122+
// - X.1648
123+
// - X1648
124+
125+
if (version.StartsWith("T") || version.StartsWith("X")) version = version.Substring(1);
126+
127+
var versionParts = version.Split('-');
128+
if (!System.Version.TryParse(versionParts[0], out var parsedVersion)) return new Version();
129+
130+
var commits = 0;
131+
if (versionParts.Length > 1) int.TryParse(versionParts[1], out commits);
132+
// parsedVersion.Build will be -1 if the version only has major and minor, but we need the build number >= 0 here
133+
return new Version(parsedVersion.Major, parsedVersion.Minor, Math.Max(0, parsedVersion.Build), commits);
176134
}
177135
}
178136
}

Source/ORTS.Settings/UserSettings.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,8 @@ public enum Menu_SelectionIndex
329329
public string AvatarURL { get; set; }
330330
[Default(false)]
331331
public bool ShowAvatar { get; set; }
332-
[Default(0)] // Do not offer to restore/resume any saves this age or older. Updated whenever a younger save fails to restore.
333-
public int YoungestFailedToRestore { get; set; }
332+
[Default("0.0")] // Do not offer to restore/resume any saves this version or older. Updated whenever a younger save fails to restore.
333+
public string YoungestVersionFailedToRestore { get; set; }
334334

335335
// Internal settings:
336336
[Default(false)]

Source/RunActivity/Viewer3D/Processes/GameStateRunActivity.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -420,14 +420,12 @@ void Resume(UserSettings settings, string[] args)
420420

421421
// First use the .save file to check the validity and extract the route and activity.
422422
var saveFile = GetSaveFile(args);
423-
var versionOrBuild = "";
424-
var saveRevision = 0;
423+
var versionAndBuild = new[] { "", "", "" };
425424
using (BinaryReader inf = new BinaryReader(new FileStream(saveFile, FileMode.Open, FileAccess.Read)))
426425
{
427426
try // Because Restore() methods may try to read beyond the end of an out of date file.
428427
{
429-
versionOrBuild = GetValidSaveVersionOrBuild(settings, saveFile, inf);
430-
saveRevision = VersionInfo.GetRevisionFromVersion(versionOrBuild);
428+
versionAndBuild = GetValidSaveVersionAndBuild(settings, saveFile, inf);
431429

432430
var values = GetSavedValues(inf);
433431
Acttype = values.acttype;
@@ -486,23 +484,25 @@ void Resume(UserSettings settings, string[] args)
486484
}
487485
catch (Exception error)
488486
{
489-
if (versionOrBuild == VersionInfo.VersionOrBuild)
487+
if (versionAndBuild[2] == VersionInfo.VersionOrBuild)
490488
{
491489
// If the save version is the same as the program version, we can't be an incompatible save - it's just a bug.
492490
throw;
493491
}
494492
else
495493
{
496-
if (saveRevision > settings.YoungestFailedToRestore)
494+
var parsedSaveVersion = VersionInfo.ParseVersion(versionAndBuild[0]);
495+
var parsedSettingVersion = VersionInfo.ParseVersion(settings.YoungestVersionFailedToRestore);
496+
if (parsedSaveVersion > parsedSettingVersion)
497497
{
498-
settings.YoungestFailedToRestore = saveRevision;
499-
settings.Save("YoungestFailedToRestore");
500-
Trace.TraceInformation("YoungestFailedToRestore set to Save's revision: {0}", saveRevision);
498+
settings.YoungestVersionFailedToRestore = versionAndBuild[0];
499+
settings.Save("YoungestVersionFailedToRestore");
500+
Trace.TraceInformation("YoungestVersionFailedToRestore set to Save version: {0}", versionAndBuild[0]);
501501
}
502502
// Rethrow the existing error if it is already an IncompatibleSaveException.
503503
if (error is IncompatibleSaveException)
504504
throw;
505-
throw new IncompatibleSaveException(saveFile, versionOrBuild, error);
505+
throw new IncompatibleSaveException(saveFile, versionAndBuild[2], error);
506506
}
507507
}
508508

@@ -610,7 +610,7 @@ void ReplayFromSave(UserSettings settings, string[] args)
610610
// Resume from previous SaveFile and then replay
611611
using (var inf = new BinaryReader(new FileStream(previousSaveFile, FileMode.Open, FileAccess.Read)))
612612
{
613-
GetValidSaveVersionOrBuild(settings, saveFile, inf);
613+
GetValidSaveVersionAndBuild(settings, saveFile, inf);
614614

615615
var values = GetSavedValues(inf);
616616
InitSimulator(settings, values.args, "Resume", values.acttype);
@@ -629,12 +629,12 @@ void ReplayFromSave(UserSettings settings, string[] args)
629629
Game.ReplaceState(new GameStateViewer3D(Viewer));
630630
}
631631

632-
static string GetValidSaveVersionOrBuild(UserSettings settings, string saveFile, BinaryReader inf)
632+
static string[] GetValidSaveVersionAndBuild(UserSettings settings, string saveFile, BinaryReader inf)
633633
{
634634
var version = inf.ReadString().Replace("\0", ""); // e.g. "0.9.0.1648" or "X1321" or "" (if compiled locally)
635635
var build = inf.ReadString().Replace("\0", ""); // e.g. 0.0.5223.24629 (2014-04-20 13:40:58Z)
636636
var versionOrBuild = version.Length > 0 ? version : build;
637-
var valid = VersionInfo.GetValidity(version, build, settings.YoungestFailedToRestore);
637+
var valid = VersionInfo.GetValidity(version, build, settings.YoungestVersionFailedToRestore);
638638
if (valid == false) // This is usually detected in ResumeForm.cs but a Resume can also be launched from the command line.
639639
throw new IncompatibleSaveException(saveFile, versionOrBuild);
640640
if (valid == null)
@@ -645,7 +645,7 @@ static string GetValidSaveVersionOrBuild(UserSettings settings, string saveFile,
645645
+ "Please do not report any problems that may result.\n",
646646
Application.ProductName, versionOrBuild, VersionInfo.VersionOrBuild);
647647
}
648-
return versionOrBuild;
648+
return new[] { version, build, versionOrBuild };
649649
}
650650

651651
/// <summary>

0 commit comments

Comments
 (0)