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