Skip to content

Commit 523ef9a

Browse files
committed
Added buttons to copy orig. part cfg file, and to open CFGs (original and in-game) in external editor
1 parent ad8ade7 commit 523ef9a

5 files changed

Lines changed: 271 additions & 78 deletions

File tree

GameData/PartInfoInPAW/Localization/en-us.cfg

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,21 @@ Localization
1717
#LOC_PartInfoInPAW_Info_Title = Info
1818
#LOC_PartInfoInPAW_CopyPartName_Action = Copy part name
1919
#LOC_PartInfoInPAW_CopyPartNode_Action = Copy part CFG node
20+
#LOC_PartInfoInPAW_CopyOrigPartNode_Action = Copy orig. CFG file
21+
#LOC_PartInfoInPAW_OpenPartCFGInEditor_Action = Open CFG node in editor
22+
#LOC_PartInfoInPAW_OpenOrigPartCFGInEditor_Action = Open orig. file in editor
2023
#LOC_PartInfoInPAW_Kg_Unit = kg
2124
#LOC_PartInfoInPAW_T_Unit = t
2225
#LOC_PartInfoInPAW_PartModuleInfo = <b>Part name:</b> <<1>><br>\n<b>Path to part CFG-file:</b> <<2>>
23-
#LOC_PartInfoInPAW_CopyToClipboardMsg_PartID_Success = <color=#CCFF00>Part <<1>> : ID successfully copied to clipboard</color>
24-
#LOC_PartInfoInPAW_CopyToClipboardMsg_PartCFG_Success = <color=#CCFF00>Part <<1>> : CFG successfully copied to clipboard</color>
25-
#LOC_PartInfoInPAW_CopyToClipboardMsg_PartCFG_Failure = <color=#FF8000>Error: could not copy part CFG (part <<1>>)!</color>
26+
#LOC_PartInfoInPAW_CopyToClipboard_PartID_SuccessMsg = <color=#CCFF00>Part <<1>> : ID successfully copied to clipboard</color>
27+
#LOC_PartInfoInPAW_CopyToClipboard_PartID_FailureMsg = <color=#FF8000>Error: could not copy part ID (part <<1>>)!</color>
28+
#LOC_PartInfoInPAW_GetNode_PartCFG_FailureMsg = <color=#FF8000>Error: could not get part CFG (part <<1>>)!</color>
29+
#LOC_PartInfoInPAW_CopyToClipboard_PartCFG_SuccessMsg = <color=#CCFF00>Part <<1>> : CFG successfully copied to clipboard</color>
30+
#LOC_PartInfoInPAW_CopyToClipboard_PartCFG_FailureMsg = <color=#FF8000>Error: could not copy part CFG (part <<1>>)!</color>
31+
#LOC_PartInfoInPAW_OpenFile_PartOrigFile_FailureMsg = <color=#FF8000>Error: could not read original part CFG file <<1>>!</color>
32+
#LOC_PartInfoInPAW_CopyToClipboard_PartOrigFile_SuccessMsg = <color=#CCFF00>Part <<1>> : original CFG file successfully copied to clipboard</color>
33+
#LOC_PartInfoInPAW_CopyToClipboard_PartOrigFile_FailureMsg = <color=#FF8000>Error: could not copy original CFG file (part <<1>>)!</color>
34+
#LOC_PartInfoInPAW_CantWriteToTempFile_PartCFG_FailureMsg = <color=#FF8000>Error: could not write part CFG to temporary file <<1>>!</color>
35+
#LOC_PartInfoInPAW_CantOpenFile_FailureMsg = <color=#FF8000>Error: could not open file <<1>> in default editor!</color>
2636
}
2737
}

GameData/PartInfoInPAW/Localization/ru.cfg

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,21 @@ Localization
1717
#LOC_PartInfoInPAW_Info_Title = Информация
1818
#LOC_PartInfoInPAW_CopyPartName_Action = Скопировать имя детали
1919
#LOC_PartInfoInPAW_CopyPartNode_Action = Скопировать конфиг детали
20+
#LOC_PartInfoInPAW_CopyOrigPartNode_Action = Скопировать ориг. файл конфига
21+
#LOC_PartInfoInPAW_OpenPartCFGInEditor_Action = Открыть конфиг в редакторе
22+
#LOC_PartInfoInPAW_OpenOrigPartCFGInEditor_Action = Открыть ориг. файл в редакторе
2023
#LOC_PartInfoInPAW_Kg_Unit = кг
2124
#LOC_PartInfoInPAW_T_Unit = т
2225
#LOC_PartInfoInPAW_PartModuleInfo = <b>Имя (ID) детали:</b> <<1>><br>\n<b>Путь к CFG-файлу детали:</b> <<2>>
23-
#LOC_PartInfoInPAW_CopyToClipboardMsg_PartID_Success = <color=#CCFF00>Деталь <<1>> : ID успешно скопировано</color>
24-
#LOC_PartInfoInPAW_CopyToClipboardMsg_PartCFG_Success = <color=#CCFF00>Деталь <<1>> : конфиг успешно скопирован</color>
25-
#LOC_PartInfoInPAW_CopyToClipboardMsg_PartCFG_Failure = <color=#FF8000>Ошибка: не удалось скопировать конфиг детали <<1>>!</color>
26+
#LOC_PartInfoInPAW_CopyToClipboard_PartID_SuccessMsg = <color=#CCFF00>Деталь <<1>> : ID успешно скопировано</color>
27+
#LOC_PartInfoInPAW_CopyToClipboard_PartID_FailureMsg = <color=#FF8000>Ошибка: не удалось скопировать ID детали <<1>>!</color>
28+
#LOC_PartInfoInPAW_GetNode_PartCFG_FailureMsg = <color=#FF8000>Ошибка: не удалось получить конфиг детали <<1>>!</color>
29+
#LOC_PartInfoInPAW_CopyToClipboard_PartCFG_SuccessMsg = <color=#CCFF00>Деталь <<1>> : конфиг успешно скопирован</color>
30+
#LOC_PartInfoInPAW_CopyToClipboard_PartCFG_FailureMsg = <color=#FF8000>Ошибка: не удалось скопировать конфиг детали <<1>>!</color>
31+
#LOC_PartInfoInPAW_OpenFile_PartOrigFile_FailureMsg = <color=#FF8000>Ошибка: не удалось открыть базовый CFG файл детали <<1>>!</color>
32+
#LOC_PartInfoInPAW_CopyToClipboard_PartOrigFile_SuccessMsg = <color=#CCFF00>Деталь <<1>> : базовый CFG файл успешно скопирован</color>
33+
#LOC_PartInfoInPAW_CopyToClipboard_PartOrigFile_FailureMsg = <color=#FF8000>Ошибка: не удалось скопировать базовый CFG файл детали <<1>>!</color>
34+
#LOC_PartInfoInPAW_CantWriteToTempFile_PartCFG_FailureMsg = <color=#FF8000>Ошибка: не удалось записать конфиг детали во временный файл <<1>>!</color>
35+
#LOC_PartInfoInPAW_CantOpenFile_FailureMsg = <color=#FF8000>Ошибка: не удалось открыть файл <<1>> в редакторе по умолчанию!</color>
2636
}
2737
}

src/Modules/PartInfoInPAW.cs

Lines changed: 155 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@
22
using System;
33
using System.Collections.Generic;
44
using System.Linq;
5+
using System.IO;
6+
using System.Text;
7+
using System.Diagnostics;
58
using UnityEngine;
69

710
namespace PartInfoInPAW
811
{
912
public class ModulePartInfoInPAW : PartModule
1013
{
14+
#region GUI: Fields
15+
1116
[KSPField(isPersistant = true)]
1217
public bool showTWR = true;
1318

@@ -21,7 +26,7 @@ public class ModulePartInfoInPAW : PartModule
2126
public string partName = "";
2227

2328
[KSPField(isPersistant = false, guiActiveEditor = true, guiActive = false, guiName = "#LOC_PartInfoInPAW_PartMod_Title", groupName = "partInfo", groupDisplayName = "#LOC_PartInfoInPAW_PartInfo_GroupTitle")]
24-
public string partMod = "";
29+
public string partModName = "";
2530

2631
[KSPField(isPersistant = false, guiActiveEditor = false, guiActive = false, guiName = "#LOC_PartInfoInPAW_PartCFGPath_Title", groupName = "partInfo", groupDisplayName = "#LOC_PartInfoInPAW_PartInfo_GroupTitle")]
2732
public string partCFGPath = "";
@@ -50,33 +55,143 @@ public class ModulePartInfoInPAW : PartModule
5055
[KSPField(isPersistant = false, guiActiveEditor = true, guiActive = false, guiName = "#LOC_PartInfoInPAW_Info_Title", groupName = "engine2Info", groupDisplayName = "#LOC_PartInfoInPAW_Engine2Info_GroupTitle")]
5156
public string engine2Info = "";
5257

58+
#endregion GUI: Fields
59+
60+
#region GUI: Buttons
61+
5362
[KSPEvent(guiActive = false, guiActiveEditor = true, guiName = "#LOC_PartInfoInPAW_CopyPartName_Action", active = true, groupName = "partInfo", groupDisplayName = "#LOC_PartInfoInPAW_PartInfo_GroupTitle")]
5463
public void CopyPartName()
5564
{
56-
if (partName == "")
65+
try
5766
{
58-
partName = GetPartName();
67+
if (partName == "")
68+
{
69+
partName = GetPartName();
70+
}
71+
GUIUtility.systemCopyBuffer = partName;
72+
Utils.Log($"Part {part.partInfo.name} : ID copied to clipboard");
73+
Utils.OnScreenMsg(Localizer.Format("#LOC_PartInfoInPAW_CopyToClipboard_PartID_SuccessMsg", partName), 2.0f);
74+
}
75+
catch(Exception e)
76+
{
77+
Utils.LogError($"Part {part.partInfo.name} : failed to copy part ID to clipboard: " + e.Message);
78+
Utils.OnScreenMsg(Localizer.Format("#LOC_PartInfoInPAW_CopyToClipboard_PartID_FailureMsg", partName));
5979
}
60-
GUIUtility.systemCopyBuffer = partName;
61-
Debug.Log(String.Format($"[PartInfoInPAW] Part {part.partInfo.name} : ID copied to clipboard"));
62-
ScreenMessages.PostScreenMessage(new ScreenMessage(
63-
Localizer.Format("#LOC_PartInfoInPAW_CopyToClipboardMsg_PartID_Success", partName),
64-
2.0f, ScreenMessageStyle.UPPER_CENTER));
6580
}
6681

6782
[KSPEvent(guiActive = false, guiActiveEditor = true, guiName = "#LOC_PartInfoInPAW_CopyPartNode_Action", active = true, groupName = "partInfo", groupDisplayName = "#LOC_PartInfoInPAW_PartInfo_GroupTitle")]
6883
public void CopyPartConfigNode()
6984
{
70-
GUIUtility.systemCopyBuffer = GetConfigNodeText();
85+
string partCFG;
86+
try
87+
{
88+
partCFG = GetConfigNodeText();
89+
}
90+
catch (Exception e)
91+
{
92+
Utils.LogError(e.Message);
93+
Utils.OnScreenMsg(Localizer.Format("#LOC_PartInfoInPAW_GetNode_PartCFG_FailureMsg", partName));
94+
return;
95+
}
96+
try
97+
{
98+
GUIUtility.systemCopyBuffer = partCFG;
99+
Utils.Log($"Part {part.partInfo.name} : CFG node copied to clipboard");
100+
Utils.OnScreenMsg(Localizer.Format("#LOC_PartInfoInPAW_CopyToClipboard_PartCFG_SuccessMsg", partName), 2.0f);
101+
}
102+
catch (Exception e)
103+
{
104+
Utils.LogError($"Part {part.partInfo.name} : failed to copy CFG node to clipboard: " + e.Message);
105+
Utils.OnScreenMsg(Localizer.Format("#LOC_PartInfoInPAW_CopyToClipboard_PartCFG_FailureMsg", partName));
106+
}
71107
}
72108

109+
[KSPEvent(guiActive = false, guiActiveEditor = true, guiName = "#LOC_PartInfoInPAW_OpenPartCFGInEditor_Action", active = true, groupName = "partInfo", groupDisplayName = "#LOC_PartInfoInPAW_PartInfo_GroupTitle")]
110+
public void OpenPartCFGInEditor()
111+
{
112+
string partCFG;
113+
try
114+
{
115+
partCFG = GetConfigNodeText();
116+
}
117+
catch (Exception e)
118+
{
119+
Utils.LogError(e.Message);
120+
Utils.OnScreenMsg(Localizer.Format("#LOC_PartInfoInPAW_GetNode_PartCFG_FailureMsg", partName));
121+
return;
122+
}
123+
string fileName = partName;
124+
if (fileName == "")
125+
{
126+
fileName = GetPartName();
127+
}
128+
foreach (var c in Path.GetInvalidFileNameChars())
129+
{
130+
fileName = fileName.Replace(c, '-');
131+
}
132+
string filePath = Path.Combine(Path.GetTempPath(), Process.GetCurrentProcess().Id.ToString() + "_" + fileName + ".cfg");
133+
if (!File.Exists(filePath))
134+
{
135+
try
136+
{
137+
File.WriteAllText(filePath, partCFG, Encoding.UTF8);
138+
}
139+
catch (Exception e)
140+
{
141+
Utils.LogError($"Could not write part CFG to file {filePath}: " + e.Message);
142+
Utils.OnScreenMsg(Localizer.Format("#LOC_PartInfoInPAW_CantWriteToTempFile_PartCFG_FailureMsg", filePath));
143+
return;
144+
}
145+
}
146+
Utils.ShellOpenFile(filePath);
147+
}
148+
149+
[KSPEvent(guiActive = false, guiActiveEditor = true, guiName = "#LOC_PartInfoInPAW_CopyOrigPartNode_Action", active = true, groupName = "partInfo", groupDisplayName = "#LOC_PartInfoInPAW_PartInfo_GroupTitle")]
150+
public void CopyOrigPartConfigNode()
151+
{
152+
string origFilePath = Utils.GetPartFilePath(part);
153+
string origFileContent;
154+
try
155+
{
156+
origFileContent = File.ReadAllText(origFilePath);
157+
}
158+
catch (Exception e)
159+
{
160+
Utils.LogError($"Could not read part CFG file {origFilePath}: " + e.Message);
161+
Utils.OnScreenMsg(Localizer.Format("#LOC_PartInfoInPAW_OpenFile_PartOrigFile_FailureMsg", origFilePath));
162+
return;
163+
}
164+
try
165+
{
166+
GUIUtility.systemCopyBuffer = origFileContent;
167+
Utils.Log($"Part {part.partInfo.name} : CFG file {origFilePath.Replace('\\', '/')} copied to clipboard");
168+
Utils.OnScreenMsg(Localizer.Format("#LOC_PartInfoInPAW_CopyToClipboard_PartOrigFile_SuccessMsg", partName), 2.0f);
169+
}
170+
catch (Exception e)
171+
{
172+
Utils.LogError($"Part {part.partInfo.name} : failed to copy original CFG file to clipboard: " + e.Message);
173+
Utils.OnScreenMsg(Localizer.Format("#LOC_PartInfoInPAW_CopyToClipboard_PartOrigFile_FailureMsg", partName));
174+
}
175+
}
176+
177+
[KSPEvent(guiActive = false, guiActiveEditor = true, guiName = "#LOC_PartInfoInPAW_OpenOrigPartCFGInEditor_Action", active = true, groupName = "partInfo", groupDisplayName = "#LOC_PartInfoInPAW_PartInfo_GroupTitle")]
178+
public void OpenOrigPartCFGInEditor()
179+
{
180+
Utils.ShellOpenFile(Utils.GetPartFilePath(part));
181+
}
182+
183+
#endregion GUI: Buttons
184+
185+
73186
protected ModuleEngines engine1;
74187
protected ModuleEngines engine2;
75188

76-
public struct ModWithComplexFoldersStruct
189+
protected bool InfoUpdated = false;
190+
191+
public readonly struct ModWithComplexFoldersStruct
77192
{
78-
private string ModFolder;
79-
private int NestingLevel;
193+
private readonly string ModFolder;
194+
private readonly int NestingLevel;
80195

81196
public ModWithComplexFoldersStruct(string folder, int nestLevel = 2)
82197
{
@@ -113,16 +228,14 @@ public string BuildModName(string url)
113228
}
114229
}
115230

116-
protected List<ModWithComplexFoldersStruct> ModsWithComplexFoldersStruct = new List<ModWithComplexFoldersStruct>()
231+
protected static List<ModWithComplexFoldersStruct> ModsWithComplexFoldersStruct = new List<ModWithComplexFoldersStruct>()
117232
{
118233
new ModWithComplexFoldersStruct("SquadExpansion"),
119234
new ModWithComplexFoldersStruct("UmbraSpaceIndustries"),
120235
new ModWithComplexFoldersStruct("WildBlueIndustries"),
121236
new ModWithComplexFoldersStruct("Bluedog_DB", 3),
122237
};
123238

124-
protected bool InfoUpdated = false;
125-
126239
private void Start()
127240
{
128241
GameEvents.onEditorShipModified.Add(EditorShipModified);
@@ -161,9 +274,9 @@ private void UpdateInfo()
161274
{
162275
partName = GetPartName();
163276
}
164-
if (partMod == "")
277+
if (partModName == "")
165278
{
166-
partMod = GetPartMod();
279+
partModName = GetPartModName();
167280
}
168281
ModuleEngines[] engines;
169282
MultiModeEngine[] isMultimode;
@@ -177,13 +290,13 @@ private void UpdateInfo()
177290
{
178291
// Dry mass only
179292
Fields["partMass"].guiName = Localizer.Format("#LOC_PartInfoInPAW_PartDryMass_Title");
180-
partMass = FormatMass(dryMass);
293+
partMass = Utils.FormatMass(dryMass);
181294
}
182295
else
183296
{
184297
// Dry mass / wet mass
185298
Fields["partMass"].guiName = Localizer.Format("#LOC_PartInfoInPAW_PartDryWetMass_Title");
186-
partMass = FormatMass(dryMass) + " / " + FormatMass(wetMass);
299+
partMass = Utils.FormatMass(dryMass) + " / " + Utils.FormatMass(wetMass);
187300
}
188301

189302
partCost = part.partInfo.cost + part.GetModuleCosts(part.partInfo.cost);
@@ -234,20 +347,6 @@ private void UpdateInfo()
234347
InfoUpdated = true;
235348
}
236349

237-
private string FormatMass(float mass)
238-
{
239-
string result;
240-
if (mass < 1.0f)
241-
{
242-
result = (mass * 1000.0f).ToString("F0") + " " + Localizer.Format("#LOC_PartInfoInPAW_Kg_Unit");
243-
}
244-
else
245-
{
246-
result = mass.ToString("F3") + " " + Localizer.Format("#LOC_PartInfoInPAW_T_Unit");
247-
}
248-
return result;
249-
}
250-
251350
private string GetPartName()
252351
{
253352
string pName = "";
@@ -256,14 +355,14 @@ private string GetPartName()
256355
pName = GameDatabase.Instance.GetConfigs("PART").
257356
Single(c => part.partInfo.name.Replace('_', '.') == c.name.Replace('_', '.')).name;
258357
}
259-
catch (Exception)
358+
catch (Exception e)
260359
{
261-
Debug.LogError(String.Format($"[PartInfoInPAW] Couldn't get config value name for part {part.partInfo.name}"));
360+
Utils.LogError($"Couldn't get config value name for part {part.partInfo.name}: " + e.Message);
262361
}
263362
return pName;
264363
}
265364

266-
private string GetPartMod()
365+
private string GetPartModName()
267366
{
268367
string url = part.partInfo.partUrl;
269368
foreach (var mod in ModsWithComplexFoldersStruct)
@@ -278,49 +377,29 @@ private string GetPartMod()
278377

279378
private string GetConfigNodeText()
280379
{
281-
string node = "";
380+
ConfigNode cfg;
282381
try
283382
{
284-
ConfigNode cfg = GameDatabase.Instance.GetConfigNode(part.partInfo.partUrl);
285-
if (cfg == null)
286-
{
287-
cfg = part.partInfo.partConfig;
288-
}
289-
if (cfg != null)
290-
{
291-
node = cfg.ToString();
292-
if (cfg != null && !cfg.HasValue("name") && (partName != ""))
293-
{
294-
node = ReplaceFirstOccurrence(node, "{", "{" + $"{Environment.NewLine}\tname = {partName}");
295-
Debug.Log(String.Format($"[PartInfoInPAW] Part {part.partInfo.name} : CFG node copied to clipboard"));
296-
ScreenMessages.PostScreenMessage(new ScreenMessage(
297-
Localizer.Format("#LOC_PartInfoInPAW_CopyToClipboardMsg_PartCFG_Success", partName),
298-
2.0f, ScreenMessageStyle.UPPER_CENTER));
299-
}
300-
}
301-
else
383+
cfg = GameDatabase.Instance.GetConfigNode(part.partInfo.partUrl) ?? part.partInfo.partConfig;
384+
}
385+
catch (Exception e)
386+
{
387+
throw new Exception($"Couldn't get config node for part {part.partInfo.name}: " + e.Message);
388+
}
389+
string nodeText;
390+
if (cfg != null)
391+
{
392+
nodeText = cfg.ToString();
393+
if (cfg != null && !cfg.HasValue("name") && (partName != ""))
302394
{
303-
Debug.LogError(String.Format($"[PartInfoInPAW] Couldn't get config node for part {part.partInfo.name}"));
304-
ScreenMessages.PostScreenMessage(new ScreenMessage(
305-
Localizer.Format("#LOC_PartInfoInPAW_CopyToClipboardMsg_PartCFG_Failure", partName),
306-
3.0f, ScreenMessageStyle.UPPER_CENTER));
395+
nodeText = Utils.ReplaceFirstOccurrence(nodeText, "{", "{" + $"{Environment.NewLine}\tname = {partName}");
307396
}
308397
}
309-
catch (Exception)
398+
else
310399
{
311-
Debug.LogError(String.Format($"[PartInfoInPAW] Couldn't get config node for part {part.partInfo.name}"));
312-
ScreenMessages.PostScreenMessage(new ScreenMessage(
313-
Localizer.Format("#LOC_PartInfoInPAW_CopyToClipboardMsg_PartCFG_Failure", partName),
314-
3.0f, ScreenMessageStyle.UPPER_CENTER));
400+
throw new Exception($"Couldn't get config node for part {part.partInfo.name}: CFG node is null for some reason");
315401
}
316-
return node;
317-
}
318-
319-
public static string ReplaceFirstOccurrence(string Source, string Find, string Replace)
320-
{
321-
int Place = Source.IndexOf(Find);
322-
string result = Source.Remove(Place, Find.Length).Insert(Place, Replace);
323-
return result;
402+
return nodeText;
324403
}
325404

326405
public override string GetInfo()
@@ -342,10 +421,14 @@ public override string GetInfo()
342421
return Localizer.Format("#LOC_PartInfoInPAW_PartModuleInfo", partName, partURL);
343422
}
344423

424+
#region B9PartSwitch API
425+
345426
[KSPEvent]
346427
public void ModuleDataChanged(BaseEventDetails details)
347428
{
348429
InfoUpdated = false;
349430
}
431+
432+
#endregion B9PartSwitch API
350433
}
351434
}

0 commit comments

Comments
 (0)