Skip to content

Commit 25ac5e0

Browse files
authored
Merge branch 'openrails:master' into size_centering
2 parents 9426e58 + 0d2de70 commit 25ac5e0

File tree

68 files changed

+15423
-1984
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+15423
-1984
lines changed

Source/Contrib/ContentManager/ContentManagerGUI.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,8 @@ void searchBox_TextChanged(object sender, EventArgs e)
286286
finds.Add(new SearchResult(content, path));
287287

288288
foreach (var child in ((ContentType[])Enum.GetValues(typeof(ContentType))).SelectMany(ct => content.Get(ct)))
289-
pending.Add(path + " / " + child.Name, child);
289+
if (!pending.ContainsKey(path + " / " + child.Name))
290+
pending.Add(path + " / " + child.Name, child);
290291

291292
foreach (var child in ContentLink.Matches(ContentInfo.GetText(content)).Cast<Match>().Select(linkMatch => content.Get(linkMatch.Groups[1].Value, (ContentType)Enum.Parse(typeof(ContentType), linkMatch.Groups[2].Value))).Where(linkContent => linkContent != null))
292293
if (!pending.ContainsKey(path + " -> " + child.Name))
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
// COPYRIGHT 2025 by the Open Rails project.
2+
//
3+
// This file is part of Open Rails.
4+
//
5+
// Open Rails is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// Open Rails is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with Open Rails. If not, see <http://www.gnu.org/licenses/>.
17+
18+
// Export the Settings in the Registry into an INI file, or vice versa.
19+
// In order to support multiple installations with different settings,
20+
// OpenRails supports using an INI file for the settings (instead of
21+
// the registry entries that are shared by all installations).
22+
// OpenRails creates a default INI file when the file exists, but has no
23+
// settings. This tool exports the current settings from the registry to
24+
// the INI file. It also allows the reverse.
25+
26+
// Important: UpdateSettings, part of UpdateManager has its own file,
27+
// Updater.ini.
28+
//
29+
// FUTURE: New settings classes that are not part of UserSettings need to
30+
// be added to the exporter. See FUTURE tag below.
31+
32+
using System;
33+
using System.Collections.Generic;
34+
using System.IO;
35+
using System.Linq;
36+
using Microsoft.Win32;
37+
using ORTS.Common;
38+
using ORTS.Settings;
39+
40+
namespace ORTS.SettingsExporter
41+
{
42+
class Program
43+
{
44+
static int Main(string[] args)
45+
{
46+
string fromArg = null; string toArg = null;
47+
48+
#region Parse Args
49+
// parse arguments
50+
foreach (string arg in args)
51+
{
52+
if (arg.Equals("/h") || arg.Equals("/help")) { ShowHelp(); Environment.Exit(1); }
53+
else if (arg.StartsWith("/")) { Console.WriteLine("ERROR: Invalid option {0}.", arg); ShowHelp(); Environment.Exit(1); }
54+
else if (fromArg == null) { fromArg = arg; }
55+
else if (toArg == null) { toArg = arg; }
56+
else { Console.WriteLine("ERROR: extraneous argument {0}.", arg); ShowHelp(); Environment.Exit(1); }
57+
}
58+
if (String.IsNullOrEmpty(fromArg) || String.IsNullOrEmpty(toArg))
59+
{ Console.WriteLine("ERROR: Missing from or to argument."); ShowHelp(); Environment.Exit(1); }
60+
else if (fromArg.Equals(toArg))
61+
{ Console.WriteLine("ERROR: From {0} and to {1} must not be the same.", fromArg, toArg); ShowHelp(); Environment.Exit(1); }
62+
#endregion
63+
64+
string loadFilePath = null; string loadRegistryKey = null;
65+
66+
#region Determine From
67+
// determine where to load from
68+
if (fromArg.Equals("INI")) { loadFilePath = SettingsBase.DefaultSettingsFileName; }
69+
else if (fromArg.Equals("REG")) { loadRegistryKey = SettingsBase.DefaultRegistryKey; }
70+
else if (fromArg.Contains(".ini")) { loadFilePath = fromArg; }
71+
else { loadRegistryKey = fromArg; }
72+
73+
// check that source exists
74+
if (!String.IsNullOrEmpty(loadFilePath))
75+
{
76+
var iniFilePath = Path.Combine(ApplicationInfo.ProcessDirectory, loadFilePath);
77+
if (!File.Exists(iniFilePath))
78+
{
79+
Console.WriteLine("ERROR: INI file {0} to export from does not exist.", iniFilePath);
80+
Environment.Exit(1);
81+
}
82+
}
83+
else if (!String.IsNullOrEmpty(loadRegistryKey))
84+
{
85+
using (var regKey = Registry.CurrentUser.OpenSubKey(loadRegistryKey))
86+
{
87+
if (regKey == null)
88+
{
89+
Console.WriteLine("ERROR: Reg key {0} to export from does not exist.", loadRegistryKey);
90+
Environment.Exit(1);
91+
}
92+
}
93+
}
94+
else
95+
{
96+
Console.WriteLine("ERROR: No source to export from found");
97+
Environment.Exit(1);
98+
}
99+
#endregion
100+
101+
// instantiate the static part of SettingsBase
102+
UserSettings userSettings;
103+
104+
// then override
105+
SettingsBase.OverrideSettingsLocations(loadFilePath, loadRegistryKey);
106+
107+
Console.WriteLine("Info: Loading from {0}.", SettingsBase.RegistryKey + SettingsBase.SettingsFilePath);
108+
109+
// load the user settings and its sub-settings from the default location
110+
IEnumerable<string> options = Enumerable.Empty<string>();
111+
userSettings = new UserSettings(options);
112+
var updateState = new UpdateState();
113+
// FUTURE: add here when a new settings class is added (that is not handled by UserSettings)
114+
115+
Console.WriteLine("Info: Successfully loaded settings from {0}.", userSettings.GetSettingsStoreName());
116+
117+
string saveFilePath = null; string saveRegistryKey = null;
118+
119+
#region Determine To
120+
// determine where to save to
121+
if (toArg.Equals("INI")) { saveFilePath = SettingsBase.DefaultSettingsFileName; }
122+
else if (toArg.Equals("REG")) { saveRegistryKey = SettingsBase.DefaultRegistryKey; }
123+
else if (toArg.Contains(".ini"))
124+
{
125+
// save to a custom ini file, do some basic verification
126+
saveFilePath = toArg;
127+
var dir = Path.GetDirectoryName(Path.Combine(ApplicationInfo.ProcessDirectory, saveFilePath));
128+
if (!Directory.Exists(dir)) { Console.WriteLine("ERROR: Directory {0} to save to does not exist.", dir); Environment.Exit(1); }
129+
}
130+
else if (toArg.StartsWith("SOFTWARE")) { saveRegistryKey = toArg; }
131+
else
132+
{
133+
Console.WriteLine("ERROR: Invalid destination {0}.", toArg);
134+
Console.WriteLine("ERROR: Registry key must start with \"SOFTWARE\", or INI path must include \".ini\".");
135+
Environment.Exit(1);
136+
}
137+
#endregion
138+
139+
#region Backup
140+
if (!String.IsNullOrEmpty(saveFilePath))
141+
{
142+
// backup the file if it already exists
143+
string settingsFilePath = Path.Combine(ApplicationInfo.ProcessDirectory, saveFilePath);
144+
string backupFilePath = Path.Combine(ApplicationInfo.ProcessDirectory, saveFilePath + ".bak");
145+
if (File.Exists(settingsFilePath))
146+
{
147+
File.Delete(backupFilePath);
148+
File.Move(settingsFilePath, backupFilePath);
149+
Console.WriteLine("Info: Backed up existing INI file as {0}.", backupFilePath);
150+
}
151+
152+
// create an empty file (required by SettingsStore)
153+
using (File.Create(settingsFilePath)) { };
154+
155+
}
156+
else if (!String.IsNullOrEmpty(saveRegistryKey))
157+
{
158+
// backup the registry key if it already exists
159+
using (var regKey = Registry.CurrentUser.OpenSubKey(saveRegistryKey))
160+
{
161+
if (regKey != null)
162+
{
163+
using (var backupRegKey = Registry.CurrentUser.CreateSubKey(saveRegistryKey + "-Backup"))
164+
{
165+
CopyRegistryKey(regKey, backupRegKey);
166+
Console.WriteLine("Info: Backed up existing Registry key as {0}.", backupRegKey.Name);
167+
}
168+
}
169+
}
170+
}
171+
else
172+
{
173+
Console.WriteLine("ERROR: No destination to export to.");
174+
Environment.Exit(1);
175+
}
176+
#endregion
177+
178+
// change the settings store
179+
userSettings.ChangeSettingsStore(saveFilePath, saveRegistryKey, null); // section is defined in SettingsStoreLocalIni
180+
updateState.ChangeSettingsStore(saveFilePath, saveRegistryKey, UpdateState.SectionName);
181+
// FUTURE: add here when a new settings class is added (that is not handled by UserSettings)
182+
183+
Console.WriteLine("Info: Saving to {0}.", userSettings.GetSettingsStoreName());
184+
185+
// save the settings
186+
userSettings.Save();
187+
updateState.Save();
188+
// FUTURE: add here when a new settings class is added (that is not handled by UserSettings)
189+
190+
Console.WriteLine("Info: Successfully saved to {0}.", userSettings.GetSettingsStoreName());
191+
192+
if (toArg.Equals("REG") || toArg.Equals(SettingsBase.DefaultRegistryKey))
193+
{
194+
Console.WriteLine();
195+
Console.WriteLine("To use the settings in the registry, manually delete the INI file in the OpenRails folder.");
196+
Console.WriteLine(" eg: {0}", Path.Combine(ApplicationInfo.ProcessDirectory, SettingsBase.DefaultSettingsFileName));
197+
}
198+
199+
return 0;
200+
}
201+
202+
static void ShowHelp()
203+
{
204+
string cmd = Path.GetFileNameWithoutExtension(ApplicationInfo.ProcessFile);
205+
Console.WriteLine();
206+
Console.WriteLine("Usage: {0} <from> <to>", cmd);
207+
Console.WriteLine(" <from> Specify the source to load settings from. It may be:");
208+
Console.WriteLine(" INI : use the default INI file {0}.", SettingsBase.DefaultSettingsFileName);
209+
Console.WriteLine(" REG : use the default registry key {0}.", SettingsBase.DefaultRegistryKey);
210+
Console.WriteLine(" path : a specific INI file, relative to the OpenRails main folder. Must include \".ini\".");
211+
Console.WriteLine(" key : a specific registry key, relative to the HKEY_CURRENT_USER key. Must start with \"SOFTWARE\".");
212+
Console.WriteLine(" <to> Specify the destination to save the settings to. Similar to <from>.");
213+
Console.WriteLine(" /h, /help Show this help.");
214+
Console.WriteLine();
215+
Console.WriteLine("Important: Close all OpenRails instances before exporting the settings. Otherwise the exported settings may be stale.");
216+
Console.WriteLine();
217+
Console.WriteLine("This utility reads the Settings (Options) from one location, and exports them to another location.");
218+
Console.WriteLine("It creates a backup of any settings that will be overwritten. Example:");
219+
Console.WriteLine(" <installfolder>\\OpenRails.ini.bak");
220+
Console.WriteLine(" HKEY_CURRENT_USER\\SOFTWARE\\OpenRails\\ORTS-Backup");
221+
Console.WriteLine("This utility is intended to:");
222+
Console.WriteLine("- Create an INI file that has the same settings as what is in the registry.");
223+
Console.WriteLine(" Example: {0} REG INI", cmd);
224+
Console.WriteLine("- Copy the settings from an INI file back into the registry, so that other installations");
225+
Console.WriteLine(" use the same settings.");
226+
Console.WriteLine(" Example: {0} INI REG", cmd);
227+
Console.WriteLine("- Backup the settings, before making temporary changes (and restore afterwards).");
228+
Console.WriteLine(" Example: {0} REG SOFTWARE\\OpenRails-Saved\\ORTS", cmd);
229+
Console.WriteLine();
230+
}
231+
232+
/// <summary>
233+
/// Recursively copy the values of a registry key to another registry key.
234+
/// </summary>
235+
/// <param name="fromKey">the key to copy from; must exist</param>
236+
/// <param name="toKey">the key to copy to; should not exist</param>
237+
static void CopyRegistryKey(RegistryKey fromKey, RegistryKey toKey)
238+
{
239+
// copy the values
240+
foreach (var name in fromKey.GetValueNames())
241+
{
242+
toKey.SetValue(name, fromKey.GetValue(name), fromKey.GetValueKind(name));
243+
}
244+
245+
// copy the subkeys
246+
foreach (var name in fromKey.GetSubKeyNames())
247+
{
248+
using (var fromSubKey = fromKey.OpenSubKey(name))
249+
{
250+
var toSubKey = toKey.CreateSubKey(name);
251+
CopyRegistryKey(fromSubKey, toSubKey);
252+
}
253+
}
254+
}
255+
}
256+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework Condition="'$(BuildDotNet)' == 'true'">net6-windows</TargetFramework>
4+
<TargetFramework Condition="'$(TargetFramework)' == ''">net472</TargetFramework>
5+
<OutputType>Exe</OutputType>
6+
<RootNamespace>Orts.SettingsExporter</RootNamespace>
7+
<AssemblyName>Contrib.SettingsExporter</AssemblyName>
8+
<ApplicationIcon>..\..\ORTS.ico</ApplicationIcon>
9+
<IsPublishable>False</IsPublishable>
10+
<AssemblyTitle>Open Rails Settings Exporter (Contributed)</AssemblyTitle>
11+
<Description>Export ORTS Settings from the Registry to the INI file, or vice versa.</Description>
12+
<Company>Open Rails</Company>
13+
<Product>Open Rails</Product>
14+
<Copyright>Copyright © 2009 - 2025</Copyright>
15+
<PackageLicenseExpression>GPL-3.0-or-later</PackageLicenseExpression>
16+
</PropertyGroup>
17+
<ItemGroup>
18+
<ProjectReference Include="..\..\ORTS.Common/ORTS.Common.csproj" />
19+
<ProjectReference Include="..\..\ORTS.Settings/ORTS.Settings.csproj" />
20+
</ItemGroup>
21+
<ItemGroup>
22+
<PackageReference Include="Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers" Version="0.4.355802">
23+
<PrivateAssets>all</PrivateAssets>
24+
</PackageReference>
25+
</ItemGroup>
26+
</Project>

Source/Contrib/TrackViewer/Drawing/DrawTrackDB.cs

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -107,37 +107,14 @@ public RouteData(string routePath, MessageDelegate messageDelegate)
107107
//sigcfgFile = null; // default initialization
108108
}
109109

110-
// read the activity location events and store them in the TrackDB.TrItemTable
110+
// read the activity location events and add them to the TrackDB.TrItemTable
111111

112112
ActivityNames.Clear();
113113
var directory = System.IO.Path.Combine(routePath, "ACTIVITIES");
114114
if (System.IO.Directory.Exists(directory))
115115
{
116-
// counting
117-
int cnt = 0;
118-
119-
foreach (var file in Directory.GetFiles(directory, "*.act"))
120-
{
121-
try
122-
{
123-
var activityFile = new ActivityFile(file);
124-
Events events = activityFile.Tr_Activity.Tr_Activity_File.Events;
125-
if (events != null)
126-
{
127-
for (int i = 0; i < events.EventList.Count; i++)
128-
{
129-
if (events.EventList[i].GetType() == typeof(EventCategoryLocation))
130-
{
131-
cnt++;
132-
}
133-
}
134-
}
135-
}
136-
catch { }
137-
}
138-
139-
// adding
140-
uint index = 0;
116+
int index = TrackDB.TrItemTable.Length;
117+
List<TrItem> eventItems = new List<TrItem>();
141118
foreach (var file in Directory.GetFiles(directory, "*.act"))
142119
{
143120
try
@@ -157,8 +134,8 @@ public RouteData(string routePath, MessageDelegate messageDelegate)
157134
eventCategoryLocation.Outcomes.DisplayMessage,
158135
eventCategoryLocation.TileX, eventCategoryLocation.TileZ,
159136
eventCategoryLocation.X, 0, eventCategoryLocation.Z,
160-
index);
161-
TrackDB.TrItemTable[index] = eventItem;
137+
(uint)index);
138+
eventItems.Add(eventItem);
162139
index++;
163140
found = true;
164141
}
@@ -168,9 +145,18 @@ public RouteData(string routePath, MessageDelegate messageDelegate)
168145
ActivityNames.Add(activityFile.Tr_Activity.Tr_Activity_Header.Name);
169146
}
170147
}
171-
catch { }
148+
catch { /* just ignore activity files with problems */ }
172149
}
173150

151+
// extend the track items array and append the event items
152+
if (eventItems.Count > 0)
153+
{
154+
int oldSize = TrackDB.TrItemTable.Length;
155+
Array.Resize<TrItem>(ref TrackDB.TrItemTable, index);
156+
int newSize = TrackDB.TrItemTable.Length;
157+
int eventSize = eventItems.Count;
158+
for (int toIdx = oldSize, fromIdx = 0; toIdx < newSize && fromIdx < eventSize; toIdx++, fromIdx++) { TrackDB.TrItemTable[toIdx] = eventItems[fromIdx]; }
159+
}
174160
}
175161
}
176162

Source/Contrib/TrackViewer/Editing/Charts/DrawPathChart.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ public class DrawPathChart
5454

5555
private bool ChartWindowIsOpen { get { return chartWindow.Visibility == Visibility.Visible; } }
5656

57+
// save window title from properies, so that the window's title can be changed when the selected path is changed
58+
private readonly String WindowTitle;
59+
5760
/// <summary>
5861
/// Constructor
5962
/// </summary>
@@ -64,6 +67,7 @@ public DrawPathChart()
6467
OnJsonSaveClick = OnJsonSave
6568
};
6669
TrackViewer.Localize(chartWindow);
70+
if (WindowTitle == null) WindowTitle = chartWindow.Title;
6771
}
6872

6973
/// <summary>
@@ -113,6 +117,7 @@ public void Open()
113117
{ // it path is broken, OnPathChanged performed a close
114118
return;
115119
}
120+
chartWindow.Title = String.Format("{0}: {1}", WindowTitle, pathEditor.CurrentTrainPath.PathName);
116121
chartWindow.Show();
117122

118123
}
@@ -160,6 +165,7 @@ private void OnPathChanged()
160165
return;
161166
}
162167
pathData.Update(trainpath);
168+
chartWindow.Title = String.Format("{0}: {1}", WindowTitle, pathEditor.CurrentTrainPath.PathName);
163169
chartWindow.Draw();
164170
}
165171

0 commit comments

Comments
 (0)