@@ -45,12 +45,20 @@ orderby ass.assembly.GetName().Version descending, ass.path ascending
4545 return true ;
4646 }
4747
48+ private static bool hasRun = false ;
49+
4850 internal void Awake ( )
4951 {
5052 try
5153 {
5254 if ( ! RunTypeElection ( typeof ( SaveGameFixer ) , "ModuleManager" ) )
5355 return ;
56+
57+ // Guard against multiple copies of the same DLL
58+ if ( hasRun )
59+ return ;
60+ hasRun = true ;
61+
5462 // So at this point we know we have won the election, and will be using the class versions as in this assembly.
5563
5664 UpdateSaves ( ) ;
@@ -68,15 +76,42 @@ internal void Awake()
6876 }
6977 #endregion
7078
71- #region Finding the part
79+ #region State
80+
81+ // Files and directories
7282 private string savesRoot ;
83+ private string backupDir = null ;
84+ private string logFile = null ;
85+
86+ // Bits and pieces for logging
87+ private StringBuilder backupLog = new StringBuilder ( ) ;
88+ private List < string > logContext = new List < string > ( ) ;
89+ private int logCtxCur = 0 ;
90+
91+ // Flags
92+ private bool logOnly = true ;
93+ private bool needsBackup = false ;
94+ private bool needsSave = false ;
95+ private bool partMissing = false ;
96+
97+ #endregion
98+
99+ #region Finding the part
100+
73101
74102 private void UpdateSaves ( )
75103 {
76104 savesRoot = Path . Combine ( Path . GetFullPath ( KSPUtil . ApplicationRootPath ) , "saves" + Path . DirectorySeparatorChar ) ;
77105
78106 foreach ( string saveDir in Directory . GetDirectories ( savesRoot ) )
79107 UpdateSaveDir ( saveDir ) ;
108+
109+ // Write the backup log if needed
110+ if ( ! logOnly && backupLog . Length > 0 )
111+ {
112+ CreateBackupDir ( ) ;
113+ File . AppendAllText ( logFile , backupLog . ToString ( ) ) ;
114+ }
80115 }
81116
82117
@@ -85,7 +120,7 @@ private void UpdateSaveDir(string saveDir)
85120 try
86121 {
87122 PushLogContext ( "Save Game: " + saveDir . Substring ( savesRoot . Length , saveDir . Length - savesRoot . Length ) ) ;
88-
123+
89124 char ds = Path . DirectorySeparatorChar ;
90125
91126 // .craft files
@@ -126,11 +161,21 @@ private void UpdateCraft(string vabCraft)
126161 PushLogContext ( "Craft file: " + vabCraft . Substring ( savesRoot . Length , vabCraft . Length - savesRoot . Length ) ) ;
127162 ConfigNode craft = ConfigNode . Load ( vabCraft ) ;
128163
129- bool needsBackup = false , needsSave = false ;
164+ needsBackup = false ; needsSave = false ; partMissing = false ;
165+
130166 foreach ( ConfigNode part in craft . GetNodes ( "PART" ) )
131- UpdatePart ( part , ref needsBackup , ref needsSave ) ;
167+ UpdatePart ( part ) ;
168+
169+ // If a part is missing don't do anything special. The game just locks the craft, it doesn't destory them.
170+ if ( partMissing )
171+ {
172+ WriteDebugMessage ( "Craft has mising parts in the VAB, the craft file will be locked." ) ;
173+ WriteDebugMessage ( "Delete the craft to get rid of this message." ) ;
174+ }
175+
176+ BackupAndReplace ( vabCraft , craft ) ;
177+
132178
133- BackupAndReplace ( vabCraft , craft , needsBackup , needsSave ) ;
134179 }
135180 finally
136181 {
@@ -141,18 +186,28 @@ private void UpdateCraft(string vabCraft)
141186 private void UpdateSFS ( string sfsFile )
142187 {
143188 ConfigNode sfs = ConfigNode . Load ( sfsFile ) ;
144-
145189 try
146190 {
147191 PushLogContext ( "Save file: " + sfsFile . Substring ( savesRoot . Length , sfsFile . Length - savesRoot . Length ) ) ;
148192
149- bool needsBackup = false , needsSave = false ;
193+ needsBackup = false ; needsSave = false ; partMissing = false ;
194+
150195 foreach ( ConfigNode game in sfs . GetNodes ( "GAME" ) )
151196 foreach ( ConfigNode flightState in game . GetNodes ( "FLIGHTSTATE" ) )
152197 foreach ( ConfigNode vessel in flightState . GetNodes ( "VESSEL" ) )
153- UpdateVessel ( vessel , ref needsBackup , ref needsSave ) ;
198+ UpdateVessel ( vessel ) ;
154199
155- BackupAndReplace ( sfsFile , sfs , needsBackup , needsSave ) ;
200+ // Backup if missing parts.
201+ // TODO: handle missing parts more gracefully, like missing modules are handled.
202+ if ( partMissing )
203+ {
204+ WriteLogMessage ( "Save game has vessels with missing parts. These vessels will be deleted on loading the save." ) ;
205+ WriteLogMessage ( "The persistence file has been backed up. Note that this will keep occuring every load until" ) ;
206+ WriteLogMessage ( "either the save game is loaded and the ships are destroyed, or the missing parts are not missing." ) ;
207+ needsBackup = true ;
208+ }
209+
210+ BackupAndReplace ( sfsFile , sfs ) ;
156211 }
157212 finally
158213 {
@@ -161,14 +216,14 @@ private void UpdateSFS(string sfsFile)
161216
162217 }
163218
164- private void UpdateVessel ( ConfigNode vessel , ref bool needsBackup , ref bool needsSave )
219+ private void UpdateVessel ( ConfigNode vessel )
165220 {
166221 try
167222 {
168223 PushLogContext ( "Vessel: " + vessel . GetValue ( "name" ) ) ;
169224
170225 foreach ( ConfigNode part in vessel . GetNodes ( "PART" ) )
171- UpdatePart ( part , ref needsBackup , ref needsSave ) ;
226+ UpdatePart ( part ) ;
172227 }
173228 finally
174229 {
@@ -177,7 +232,7 @@ private void UpdateVessel(ConfigNode vessel, ref bool needsBackup, ref bool need
177232 }
178233 #endregion
179234
180- private void UpdatePart ( ConfigNode part , ref bool needsBackup , ref bool needsSave )
235+ private void UpdatePart ( ConfigNode part )
181236 {
182237 // The modules saved with the part
183238 ConfigNode [ ] savedModules = part . GetNodes ( "MODULE" ) ;
@@ -214,8 +269,9 @@ private void UpdatePart(ConfigNode part, ref bool needsBackup, ref bool needsSav
214269
215270 if ( available == null )
216271 {
217- WriteLogMessage ( "Backup created - part \" " + partName + "\" has been deleted and ship will be destroyed." ) ;
218- needsBackup = true ;
272+ WriteLogMessage ( "Part \" " + partName + "\" has been deleted." ) ;
273+ partMissing = true ;
274+ return ;
219275 }
220276
221277 PartModuleList prefabModules = available . partPrefab . Modules ;
@@ -232,20 +288,7 @@ private void UpdatePart(ConfigNode part, ref bool needsBackup, ref bool needsSav
232288 return ;
233289 needUpdate : ;
234290 }
235-
236291 // Yes we do!
237- #if false
238- string prefabNames = "Prefab modules: " ;
239- for ( int i = 0 ; i < prefabModules . Count ; ++ i )
240- prefabNames += ( prefabModules [ i ] as PartModule ) . moduleName + "," ;
241- prefabNames = prefabNames . Substring ( 0 , prefabNames . Length - 1 ) ;
242-
243- Debug . Log ( "[SaveGameFixer] Fixing Part: " + partName + " in file: " + source + "\n " + prefabNames
244- + "\n Saved modules: " + string . Join ( "," , ( from s in savedModules select ( s == null ? "***" : s . GetValue ( "name" ) ) ) . ToArray ( ) )
245- + "\n Backup modules: " + string . Join ( "," , ( from s in backupModules select ( s == null ? "***" : s . GetValue ( "name" ) ) ) . ToArray ( ) )
246- //+ "\nConfig: \n" + part
247- ) ;
248- #endif
249292
250293 // Discard any backups that are already in saved modules
251294 for ( int i = 0 ; i < backupModules . Length ; ++ i )
@@ -373,11 +416,6 @@ private void UpdatePart(ConfigNode part, ref bool needsBackup, ref bool needsSav
373416
374417 #region Backups
375418
376- private List < string > logContext = new List < string > ( ) ;
377- private int logCtxCur = 0 ;
378- private string backupDir = null ;
379- private string logFile = null ;
380-
381419 private void PushLogContext ( string p )
382420 {
383421 logContext . Add ( p ) ;
@@ -397,29 +435,20 @@ private void WriteDebugMessage(string logMessage)
397435
398436 private void WriteLogMessage ( string logMessage , bool debugMsg = false )
399437 {
400- #if DEBUG
401- string dbg = debugMsg ? "[dbg]" : "[log]" ;
402-
403- #else
404- string dbg = string . Empty ;
405- #endif
406- CreateBackupDir ( ) ;
407-
408- StringBuilder sb = new StringBuilder ( ) ;
409-
410438 // Write any pending log headers
411439 string indent ;
412440 for ( ; logCtxCur < logContext . Count ; logCtxCur ++ )
413441 {
414- indent = new String ( ' ' , 4 * logCtxCur + dbg . Length ) ;
415- sb . Append ( indent ) . AppendLine ( logContext [ logCtxCur ] ) ;
416- Debug . Log ( "[SaveGameFixer]" + indent + logContext [ logCtxCur ] ) ;
442+ indent = new String ( ' ' , 4 * logCtxCur ) ;
443+ backupLog . Append ( ' ' ) . Append ( indent ) . AppendLine ( logContext [ logCtxCur ] ) ;
444+ Debug . Log ( indent + logContext [ logCtxCur ] ) ;
417445 }
418446 indent = new String ( ' ' , 4 * logCtxCur ) ;
419- sb . Append ( dbg ) . Append ( indent ) . AppendLine ( logMessage ) ;
420- Debug . Log ( "[SaveGameFixer]" + dbg + indent + logMessage ) ;
421-
422- File . AppendAllText ( logFile , sb . ToString ( ) ) ;
447+ backupLog . Append ( debugMsg ? ' ' : '*' ) . Append ( indent ) . AppendLine ( logMessage ) ;
448+ if ( debugMsg )
449+ Debug . Log ( indent + logMessage ) ;
450+ else
451+ Debug . LogWarning ( indent + logMessage ) ;
423452 }
424453
425454 private void CreateBackupDir ( )
@@ -432,11 +461,10 @@ private void CreateBackupDir()
432461 }
433462 }
434463
435- private void BackupAndReplace ( string file , ConfigNode config , bool needsBackup , bool needsSave )
464+ private void BackupAndReplace ( string file , ConfigNode config )
436465 {
437- #if ! DEBUG
466+
438467 if ( needsBackup )
439- #endif
440468 {
441469 CreateBackupDir ( ) ;
442470
@@ -446,13 +474,14 @@ private void BackupAndReplace(string file, ConfigNode config, bool needsBackup,
446474 Directory . CreateDirectory ( Path . GetDirectoryName ( backupTo ) ) ;
447475
448476 File . Copy ( file , backupTo ) ;
477+
478+ logOnly = false ;
449479 }
450480
451481 if ( needsSave )
452482 config . Save ( file ) ;
453483 }
454484
455-
456485 #endregion
457486 }
458487
0 commit comments