1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.IO;
5 using System.Xml;
6 using System.Xml.Serialization;
7 using System.Drawing;
8 using System.Linq;
9 using SharpCompress.Archives;
10 using SharpCompress.Common;
11 using SharpCompress.Readers;
12 using SharpCompress.Writers;
13 
14 namespace OpenBveApi.Packages
15 {
16 	/* ----------------------------------------
17 	 * TODO: This part of the API is unstable.
18 	 *       Modifications can be made at will.
19 	 * ---------------------------------------- */
20 
21 	/// <summary>Defines an OpenBVE Package</summary>
22 	[XmlType("Package")]
23 	public class Package
24 	{
25 		/// <summary>The package version</summary>
26 		[XmlIgnore]
27 		public Version PackageVersion;
28 		/// <summary>The on-disk file of the package (Used during creation)</summary>
29 		[XmlIgnore]
30 		public string FileName;
31 		/// <summary>The package version represented in string format</summary>
32 		[XmlElement(ElementName = "PackageVersion"), EditorBrowsable(EditorBrowsableState.Never), Browsable(false),Bindable(false)]
33 		public string Version
34 		{
35 			get
36 			{
37 				if (this.PackageVersion == null)
38 					return string.Empty;
39 				else
40 					return this.PackageVersion.ToString();
41 			}
42 			set
43 			{
44 				if (!String.IsNullOrEmpty(value))
45 					this.PackageVersion = new Version(value);
46 			}
47 		}
48 		/// <summary>The package name</summary>
49 		public string Name;
50 		/// <summary>The package author</summary>
51 		public string Author;
52 		/// <summary>The package website</summary>
53 		public string Website;
54 		/// <summary>The GUID for this package</summary>
55 		public string GUID;
56 		/// <summary>Stores the package type</summary>
57 		public PackageType PackageType;
58 		/// <summary>The file this package was installed from</summary>
59 		public string PackageFile;
60 		/// <summary>The package description</summary>
61 		public string Description;
62 		/// <summary>The image for this package</summary>
63 		[XmlIgnore]
64 		public Image PackageImage;
65 		/// <summary>The list of dependancies for this package</summary>
66 		public List<Package> Dependancies;
67 		/// <summary>The list of packages that this package reccomends you also install</summary>
68 		public List<Package> Reccomendations;
69 		/// <summary>The list of packages which depend on this package</summary>
70 		public List<string> DependantPackages;
71 		/*
72 		 * These values are used by dependancies
73 		 * They need to live in the base Package class to save creating another.....
74 		 */
75 		/// <summary>The minimum package version</summary>
76 		[XmlIgnore]
77 		public Version MinimumVersion;
78 		/// <summary>The minimum package version represented in string format</summary>
79 		[XmlElement(ElementName = "MinimumVersion"), Browsable(false), Bindable(false),EditorBrowsable(EditorBrowsableState.Never)]
80 		public string MinVersion
81 		{
82 			get
83 			{
84 				if (this.MinimumVersion == null)
85 					return string.Empty;
86 				else
87 					return this.MinimumVersion.ToString();
88 			}
89 			set
90 			{
91 				if (!String.IsNullOrEmpty(value))
92 					this.MinimumVersion = new Version(value);
93 			}
94 		}
95 		/// <summary>The maximum package version</summary>
96 		[XmlIgnore]
97 		public Version MaximumVersion;
98 		/// <summary>The maximum package version represented in string format</summary>
99 		[XmlElement(ElementName = "MaximumVersion")]
100 		[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
101 		public string MaxVersion
102 		{
103 			get
104 			{
105 				if (this.MaximumVersion == null)
106 					return string.Empty;
107 				else
108 					return this.MaximumVersion.ToString();
109 			}
110 			set
111 			{
112 				if (!String.IsNullOrEmpty(value))
113 					this.MaximumVersion = new Version(value);
114 			}
115 		}
116 		/// <summary>Creates a clone of the specified package</summary>
117 		/// <param name="packageToClone">The package to clone</param>
118 		/// <param name="dependancy">Whether this package is part of a dependancy list</param>
Package(Package packageToClone, bool dependancy)119 		public Package(Package packageToClone, bool dependancy)
120 		{
121 			Name = packageToClone.Name;
122 			Author = packageToClone.Author;
123 			GUID = packageToClone.GUID;
124 			Website = packageToClone.Website;
125 			PackageType = packageToClone.PackageType;
126 			Description = packageToClone.Description;
127 			Dependancies = packageToClone.Dependancies;
128 			Reccomendations = packageToClone.Reccomendations;
129 			Version = packageToClone.Version;
130 			/*
131 			 * If we are cloning a package, we can assume that the image will change, as these are only currently stored in archives TODO: Serialize to XML? Bad idea?
132 			 */
133 
134 		}
135 		/// <summary>Creates a new package</summary>
136 		/// An empty default constructor is required as we've also specified a non default constructor
Package()137 		public Package()
138 		{
139 		}
140 	}
141 
142 	/// <summary>This class is used by the XML serializer to provide a correctly readable structure</summary>
143 	[Browsable(false), Bindable(false), EditorBrowsable(EditorBrowsableState.Never), XmlType("openBVE")]
144 	public class SerializedPackage
145 	{
146 		/// <summary>The base package</summary>
147 		public Package Package;
148 	}
149 
150 	/// <summary>Provides the possible states of a version</summary>
151 	public enum VersionInformation
152 	{
153 		/// <summary>The version was not found in the database</summary>
154 		NotFound = 0,
155 		/// <summary>The version is a newer version than that currently installed</summary>
156 		NewerVersion = 1,
157 		/// <summary>The version is an older version than that currently installed</summary>
158 		OlderVersion = 2,
159 		/// <summary>The version is the same version as that currently installed</summary>
160 		SameVersion = 3,
161 	}
162 
163 	/// <summary>Defines the possible package types</summary>
164 	public enum PackageType
165 	{
166 		/// <summary>The type of package was not found/ undefined</summary>
167 		NotFound = 0,
168 		/// <summary>The package is a route</summary>
169 		Route = 1,
170 		/// <summary>The package is a train</summary>
171 		Train = 2,
172 		/// <summary>The package is a route, utility etc.</summary>
173 		Other = 3,
174 		/// <summary>The package contains imported Loksim3D content.</summary>
175 		Loksim3D = 4,
176 	}
177 
178 	/// <summary>Holds the properties of a file, used during creation of a package.</summary>
179 	public class PackageFile
180 	{
181 		/// <summary>The absolute on-disk path to the file.</summary>
182 		public string absolutePath;
183 		/// <summary>The relative path to the file.</summary>
184 		public string relativePath;
185 	}
186 
187 	/// <summary>Defines the types of compression a package file may use.</summary>
188 	public enum CompressionType
189 	{
190 		/// <summary>LZMA Zip compression</summary>
191 		Zip,
192 		/// <summary>G compression</summary>
193 		TarGZ,
194 		/// <summary>BZip2 compression</summary>
195 		BZ2
196 	}
197 
198 	/// <summary>The current operation being performed</summary>
199 	public enum PackageOperation
200 	{
201 		/// <summary>No current operation</summary>
202 		None,
203 		/// <summary>Creating a package</summary>
204 		Creating,
205 		/// <summary>Installing a package</summary>
206 		Installing,
207 		/// <summary>Uninstalling a package</summary>
208 		Uninstalling,
209 		/// <summary>
210 		/// Reading the information from a package
211 		/// </summary>
212 		Reading
213 	}
214 
215 
216 	/// <summary>Provides functions for manipulating OpenBVE packages</summary>
217 	public static partial class Manipulation
218 	{
219 		/// <summary>This extracts a package, and returns the list of extracted files</summary>
220 		/// <param name="currentPackage">The package to extract</param>
221 		/// <param name="extractionDirectory">The directory to extract to</param>
222 		/// <param name="databaseFolder">The root package database folder</param>
223 		/// <param name="packageFiles">Returns via 'ref' a string containing a list of files in the package (Used to update the dialog)</param>
ExtractPackage(Package currentPackage, string extractionDirectory, string databaseFolder, ref string packageFiles)224 		public static void ExtractPackage(Package currentPackage, string extractionDirectory, string databaseFolder, ref string packageFiles)
225 		{
226 			int i = 0;
227 			int j = 0;
228 			string fp = String.Empty;
229 			try
230 			{
231 				using (Stream stream = File.OpenRead(currentPackage.PackageFile))
232 				{
233 
234 					var reader = ArchiveFactory.Open(stream);
235 					List<string> PackageFiles = new List<string>();
236 					j = reader.Entries.Count();
237 					foreach (var archiveEntry in reader.Entries)
238 					{
239 						fp = archiveEntry.Key;
240 						if (filesToSkip.Contains(archiveEntry.Key.ToLowerInvariant()))
241 						{
242 							//Skip package information files etc.
243 						}
244 						else if (archiveEntry.Size == 0)
245 						{
246 							//Skip zero-byte files
247 						}
248 						else
249 						{
250 							//Extract everything else, preserving directory structure
251 							archiveEntry.WriteToDirectory(extractionDirectory, new ExtractionOptions { ExtractFullPath = true, Overwrite = true });
252 							//We don't want to add directories to the list of files
253 							if (!archiveEntry.IsDirectory)
254 							{
255 								PackageFiles.Add(OpenBveApi.Path.CombineFile(extractionDirectory, archiveEntry.Key));
256 							}
257 						}
258 						i++;
259 						OnProgressChanged(null, new ProgressReport((int) ((double) i / j * 100), archiveEntry.Key));
260 					}
261 
262 					string Text = "";
263 					foreach (var FileName in PackageFiles)
264 					{
265 						Text += FileName + "\r\n";
266 					}
267 					packageFiles = Text;
268 					//Write out the package file list
269 					var fileListDirectory = OpenBveApi.Path.CombineDirectory(databaseFolder, "Installed");
270 					if (!Directory.Exists(fileListDirectory))
271 					{
272 						Directory.CreateDirectory(fileListDirectory);
273 					}
274 					var fileList = OpenBveApi.Path.CombineFile(fileListDirectory, currentPackage.GUID.ToUpper() + ".xml");
275 					using (StreamWriter sw = new StreamWriter(fileList))
276 					{
277 						XmlSerializer listWriter = new XmlSerializer(typeof(List<string>));
278 						listWriter.Serialize(sw, PackageFiles);
279 					}
280 				}
281 
282 			}
283 			catch (Exception ex)
284 			{
285 				OnProblemReport(null, new ProblemReport((int)((double)i / j * 100), fp, ex));
286 			}
287 			OnCompletion(null, new CompletionReport(PackageOperation.Installing));
288 		}
289 
290 		/// <summary>Creates a new packaged archive</summary>
291 		/// <param name="currentPackage">The package data we wish to compress into an archive</param>
292 		/// <param name="compressionType">The compression type to use for this archive</param>
293 		/// <param name="packageFile">The filename to save the package as</param>
294 		/// <param name="packageImage">The path to the image for this package, if applicable</param>
295 		/// <param name="packageFiles">The list of files to save within the package</param>
CreatePackage(Package currentPackage, CompressionType compressionType, string packageFile, string packageImage, List<PackageFile> packageFiles)296 		public static void CreatePackage(Package currentPackage, CompressionType compressionType, string packageFile, string packageImage, List<PackageFile> packageFiles)
297 		{
298 			int cf = 0;
299 
300 			string fp = String.Empty;
301 			try
302 			{
303 				using (var zip = File.OpenWrite(packageFile))
304 				{
305 					SharpCompress.Common.ArchiveType type;
306 					SharpCompress.Common.CompressionType compression;
307 					switch (compressionType)
308 					{
309 						case CompressionType.Zip:
310 							type = ArchiveType.Zip;
311 							compression = SharpCompress.Common.CompressionType.LZMA;
312 							break;
313 						case CompressionType.BZ2:
314 							type = ArchiveType.Zip;
315 							compression = SharpCompress.Common.CompressionType.BZip2;
316 							break;
317 						case CompressionType.TarGZ:
318 							type = ArchiveType.Tar;
319 							compression = SharpCompress.Common.CompressionType.GZip;
320 							break;
321 						default:
322 							type = ArchiveType.Zip;
323 							compression = SharpCompress.Common.CompressionType.LZMA;
324 							break;
325 					}
326 					using (var zipWriter = WriterFactory.Open(zip, type, compression))
327 					{
328 						if (packageFiles != null && packageFiles.Count > 0)
329 						{
330 							for (int fileToAdd = 0; fileToAdd < packageFiles.Count; fileToAdd++)
331 							{
332 								cf = fileToAdd;
333 								PackageFile currentFile = packageFiles[fileToAdd];
334 								fp = currentFile.absolutePath;
335 								if (currentFile.absolutePath.EndsWith("thumbs.db", StringComparison.InvariantCultureIgnoreCase))
336 								{
337 									//Skip thumbs.db files, as they're often locked when creating or extracting
338 									//Pointless too.....
339 									continue;
340 								}
341 								if (new FileInfo(currentFile.absolutePath).Length == 0)
342 								{
343 									//Don't archive zero-byte files, as Sharpcompress doesn't like them.....
344 									continue;
345 								}
346 								//Add file to archive
347 								zipWriter.Write(currentFile.relativePath, currentFile.absolutePath);
348 								OnProgressChanged(null,
349 									new ProgressReport((int) ((double) fileToAdd/packageFiles.Count*100), currentFile.absolutePath));
350 							}
351 						}
352 						//Create temp directory and XML file
353 						var tempXML = System.IO.Path.GetTempPath() + System.IO.Path.GetRandomFileName() + "package.xml";
354 						string tempPath = System.IO.Path.GetDirectoryName(tempXML);
355 						if (tempPath == null)
356 						{
357 							throw new Exception("Unable to create the temporary directory for package compression.");
358 						}
359 						Directory.CreateDirectory(tempPath);
360 						using (StreamWriter sw = new StreamWriter(tempXML))
361 						{
362 							//TODO: Let's see if we can get the serializer working everywhere in the solution.....
363 							//Haven't checked whether these are read by the reader yet.
364 							XmlSerializer listWriter = new XmlSerializer(typeof(SerializedPackage));
365 							listWriter.Serialize(sw, new SerializedPackage {Package = currentPackage});
366 						}
367 						//Write out XML
368 						zipWriter.Write("Package.xml", tempXML);
369 						//Write out image
370 						if (System.IO.File.Exists(packageImage))
371 						{
372 							zipWriter.Write("Package.png", packageImage);
373 						}
374 					}
375 				}
376 			}
377 			catch (Exception ex)
378 			{
379 				OnProblemReport(null, new ProblemReport((int)((double)cf / packageFiles.Count * 100), fp, ex));
380 			}
381 			OnCompletion(null, new CompletionReport(PackageOperation.Creating));
382 		}
383 
384 		/// <summary>Uninstalls a package</summary>
385 		/// <param name="currentPackage">The package to uninstall</param>
386 		/// <param name="databaseFolder">The package database folder</param>
387 		/// <param name="PackageFiles">Returns via 'ref' a list of files uninstalled</param>
388 		/// <returns>True if uninstall succeeded with no errors, false otherwise</returns>
UninstallPackage(Package currentPackage, string databaseFolder, ref string PackageFiles)389 		public static bool UninstallPackage(Package currentPackage, string databaseFolder, ref string PackageFiles)
390 		{
391 			var fileList = OpenBveApi.Path.CombineFile(OpenBveApi.Path.CombineDirectory(databaseFolder, "Installed"), currentPackage.GUID.ToUpper() + ".xml");
392 			if (!File.Exists(fileList))
393 			{
394 				PackageFiles = null;
395 				//The list of files installed by this package is missing
396 				return false;
397 			}
398 			XmlSerializer listReader = new XmlSerializer(typeof(List<string>));
399 			List<string> filesToDelete;
400 			using (FileStream readFileStream = new FileStream(fileList, FileMode.Open, FileAccess.Read, FileShare.Read))
401 			{
402 				filesToDelete = (List<string>)listReader.Deserialize(readFileStream);
403 			}
404 			File.Delete(fileList);
405 			bool noErrors = true;
406 			int errorCount = 0;
407 			int deletionCount = 0;
408 			string Result = "";
409 			foreach (var String in filesToDelete)
410 			{
411 				try
412 				{
413 					File.Delete(String);
414 					Result += String + " deleted successfully. \r\n ";
415 					deletionCount++;
416 				}
417 				catch (Exception ex)
418 				{
419 					//We have caught an error....
420 					//Set the return type to false, and add the exception to the results string
421 					noErrors = false;
422 					Result += String + "\r\n";
423 					Result += ex.Message + "\r\n";
424 					errorCount++;
425 				}
426 			}
427 			//Set the final results string to display
428 			PackageFiles = deletionCount + " files deleted successfully. \r\n" + errorCount + " errors were encountered. \r\n \r\n \r\n" + Result;
429 			OnCompletion(null, new CompletionReport(PackageOperation.Uninstalling));
430 			return noErrors;
431 		}
432 
433 		/// <summary>Reads the information of the selected package</summary>
ReadPackage(string packageFile)434 		public static Package ReadPackage(string packageFile)
435 		{
436 			bool InfoFound = false;
437 			string ImageFile = "package.png";
438 			//Create a random temp directory
439 			string TempDirectory = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName());
440 
441 			Package currentPackage = new Package();
442 			Directory.CreateDirectory(TempDirectory);
443 			//Load the selected package file into a stream
444 			reset:
445 			using (Stream stream = File.OpenRead(packageFile))
446 			{
447 				try
448 				{
449 					var reader = ReaderFactory.Open(stream);
450 					while (reader.MoveToNextEntry())
451 					{
452 
453 						//Search for the package.xml file- This must be located in the archive root
454 						if (reader.Entry.Key.ToLowerInvariant() == "package.xml" && !InfoFound)
455 						{
456 							reader.WriteEntryToDirectory(TempDirectory, new ExtractionOptions { ExtractFullPath = true, Overwrite = true });
457 							//Load the XML file
458 							InfoFound = true;
459 							XmlSerializer listReader = new XmlSerializer(typeof(SerializedPackage));
460 							SerializedPackage newPackage =
461 								(SerializedPackage) listReader.Deserialize(
462 									XmlReader.Create(OpenBveApi.Path.CombineFile(TempDirectory, "package.xml")));
463 							currentPackage = newPackage.Package;
464 						}
465 						if (reader.Entry.Key.ToLowerInvariant() == "packageinfo.xml" &&
466 						    packageFile.ToLowerInvariant().EndsWith(".l3dpack") && !InfoFound)
467 						{
468 							reader.WriteEntryToDirectory(TempDirectory, new ExtractionOptions { ExtractFullPath = true, Overwrite = true });
469 							//Load the XML file
470 							try
471 							{
472 								XmlDocument xml = new XmlDocument();
473 								xml.Load(OpenBveApi.Path.CombineFile(TempDirectory, "packageinfo.xml"));
474 								currentPackage =
475 									LoksimPackage.Parse(xml, System.IO.Path.GetFileNameWithoutExtension(packageFile), ref ImageFile);
476 								InfoFound = true;
477 								//Yuck...
478 								//We need to reset our streamreader, but Sharpcompress doesn't allow this, so just hit goto.....
479 								stream.Seek(0, 0);
480 								goto reset;
481 							}
482 							catch
483 							{
484 								return null;
485 							}
486 						}
487 						if (reader.Entry.Key.ToLowerInvariant() == ImageFile)
488 						{
489 							//Extract the package.png to the uniquely assigned temp directory
490 							reader.WriteEntryToDirectory(TempDirectory, new ExtractionOptions { ExtractFullPath = true, Overwrite = true });
491 							try
492 							{
493 								currentPackage.PackageImage = Image.FromFile(Path.CombineFile(TempDirectory, ImageFile));
494 							}
495 							catch
496 							{
497 								//Image loading failed
498 								currentPackage.PackageImage = null;
499 							}
500 						}
501 						/*
502 						 * May have to change to plaintext-
503 						 * No way of easily storing a RTF object....
504 						 *
505 						 */
506 						if (reader.Entry.Key.ToLowerInvariant() == "package.rtf")
507 						{
508 							//Extract the package.rtf description file to the uniquely assigned temp directory
509 							reader.WriteEntryToDirectory(TempDirectory, new ExtractionOptions { ExtractFullPath = true, Overwrite = true });
510 							//PackageDescription.LoadFile(OpenBveApi.Path.CombineFile(TempDirectory, "package.rtf"));
511 						}
512 
513 					}
514 				}
515 				catch
516 				{
517 					//The ReaderFactory threw a wobbly
518 					//Most likely cause is that this file is not an archive
519 					return null;
520 				}
521 			}
522 			if (!InfoFound)
523 			{
524 				//No info found, return null.....
525 				return null;
526 			}
527 			//Read the info
528 
529 			if (currentPackage.Equals(new Package()))
530 			{
531 				//Somewhat hacky way to quickly check if all elements are null....
532 				return null;
533 			}
534 			currentPackage.PackageFile = packageFile;
535 			OnCompletion(null, new CompletionReport(PackageOperation.Reading));
536 			return currentPackage;
537 		}
538 
539 		/*
540 		 * Events
541 		 */
542 
543 		/// <summary>Reports the current progress of a package installation or uninstallation</summary>
544 		public static event EventHandler<ProgressReport> ProgressChanged;
545 
546 		/// <summary>This is called whenever the progress changes</summary>
OnProgressChanged(object sender, ProgressReport progressReport)547 		public static void OnProgressChanged(object sender, ProgressReport progressReport)
548 		{
549 			if (ProgressChanged != null)
550 			{
551 				ProgressChanged(null, progressReport);
552 			}
553 		}
554 
555 		/// <summary>Reports the current progress of a package installation or uninstallation</summary>
556 		public static event EventHandler<ProblemReport> ProblemReport;
557 
558 		/// <summary>This is called whenever the progress changes</summary>
OnProblemReport(object sender, ProblemReport problemReport)559 		public static void OnProblemReport(object sender, ProblemReport problemReport)
560 		{
561 			if (ProblemReport != null)
562 			{
563 				ProblemReport(null, problemReport);
564 			}
565 		}
566 
567 		/// <summary>Reports the current progress of a package installation or uninstallation</summary>
568 		public static event EventHandler<CompletionReport> OperationCompleted;
569 
570 		/// <summary>This is called whenever the progress changes</summary>
OnCompletion(object sender, CompletionReport completionReport)571 		public static void OnCompletion(object sender, CompletionReport completionReport)
572 		{
573 			if (OperationCompleted != null)
574 			{
575 				OperationCompleted(null, completionReport);
576 			}
577 		}
578 
579 	}
580 
581 	/// <summary>Defines a progress report</summary>
582 	public class ProgressReport : EventArgs
583 	{
584 		/// <summary>The current progress percentage</summary>
585 		public int Progress { get; set; }
586 		/// <summary>The file currently being processed</summary>
587 		public string CurrentFile { get; set; }
588 		/// <summary>The progress report</summary>
ProgressReport(int progress, string file)589 		public ProgressReport(int progress, string file)
590 		{
591 			Progress = progress;
592 			CurrentFile = file;
593 		}
594 	}
595 
596 	/// <summary>Defines a progress report</summary>
597 	public class ProblemReport : EventArgs
598 	{
599 		/// <summary>The current progress percentage</summary>
600 		public int Progress { get; set; }
601 		/// <summary>The file currently being processed</summary>
602 		public string CurrentFile { get; set; }
603 
604 		/// <summary>The file currently being processed</summary>
605 		public Exception Exception { get; set; }
606 		/// <summary>The progress report</summary>
ProblemReport(int progress, string file, Exception ex)607 		public ProblemReport(int progress, string file, Exception ex)
608 		{
609 			Progress = progress;
610 			CurrentFile = file;
611 			Exception = ex;
612 		}
613 	}
614 
615 	/// <summary>Defines a completion report</summary>
616 	public class CompletionReport : EventArgs
617 	{
618 		/// <summary>The operation which has completed</summary>
619 		public PackageOperation Operation { get; set; }
620 		/// <summary>The completion report</summary>
CompletionReport(PackageOperation operation)621 		public CompletionReport(PackageOperation operation)
622 		{
623 			Operation = operation;
624 		}
625 	}
626 
627 	/// <summary>Provides information functions for OpenBVE packages</summary>
628 	public static class Information
629 	{
630 
631 		/// <summary>Checks to see if this package is currently installed, and if so whether there is another version installed</summary>
632 		/// <param name="currentPackage">The package to check</param>
633 		/// <param name="packageList">The list of currently installed packages</param>
634 		/// <param name="oldPackage">Returns via 'ref' the current package installed</param>
635 		/// <returns>Whether the package to check is installed, and if so whether it is an older, newer or identical version</returns>
CheckVersion(Package currentPackage, List<Package> packageList, ref Package oldPackage)636 		public static VersionInformation CheckVersion(Package currentPackage, List<Package> packageList, ref Package oldPackage)
637 		{
638 			if (packageList == null)
639 			{
640 				//List is null, so we can't possibly be in it
641 				return VersionInformation.NotFound;
642 			}
643 			foreach (var Package in packageList)
644 			{
645 				//Check GUID
646 				if (currentPackage.GUID == Package.GUID)
647 				{
648 					oldPackage = currentPackage;
649 					//GUID found, check versions
650 					if (currentPackage.PackageVersion == Package.PackageVersion)
651 					{
652 						//The versions are the same
653 						return VersionInformation.SameVersion;
654 					}
655 					if (currentPackage.PackageVersion > Package.PackageVersion)
656 					{
657 						//This is an older version, so update the ref with the found version number
658 						return VersionInformation.OlderVersion;
659 					}
660 					if (currentPackage.PackageVersion < Package.PackageVersion)
661 					{
662 						//This is a newer version, but it's good manners to point out that this will be replacing an older version
663 						return VersionInformation.NewerVersion;
664 					}
665 
666 				}
667 			}
668 			//We didn't find our package as currently installed
669 			return VersionInformation.NotFound;
670 		}
671 
672 
673 
674 		/// <summary>Checks to see if upgrading or downgrading this package will break any dependancies</summary>
UpgradeDowngradeDependancies(Package currentPackage, List<Package> installedRoutes, List<Package> installedTrains)675 		public static List<Package> UpgradeDowngradeDependancies(Package currentPackage, List<Package> installedRoutes, List<Package> installedTrains)
676 		{
677 			List<Package> Dependancies = new List<Package>();
678 			if (installedRoutes != null)
679 			{
680 				foreach (Package routePackage in installedRoutes)
681 				{
682 					//Itinerate through the routes list
683 					foreach (Package Package in routePackage.Dependancies)
684 					{
685 						if (Package.GUID == currentPackage.GUID)
686 						{
687 							if (Package.MinimumVersion > currentPackage.PackageVersion ||
688 								Package.MaximumVersion < currentPackage.PackageVersion)
689 							{
690 								Dependancies.Add(Package);
691 							}
692 						}
693 					}
694 
695 				}
696 			}
697 			if (installedTrains != null)
698 			{
699 				foreach (Package trainPackage in installedTrains)
700 				{
701 					//Itinerate through the routes list
702 					foreach (Package Package in trainPackage.Dependancies)
703 					{
704 						if (Package.GUID == currentPackage.GUID)
705 						{
706 							if (Package.MinimumVersion > currentPackage.PackageVersion ||
707 								Package.MaximumVersion < currentPackage.PackageVersion)
708 							{
709 								Dependancies.Add(Package);
710 							}
711 						}
712 					}
713 
714 				}
715 			}
716 			if (Dependancies.Count == 0)
717 			{
718 				//Return null if there are no unmet dependancies
719 				return null;
720 			}
721 			return Dependancies;
722 		}
723 	}
724 }
725