comparison api/WarFoundryLoader.cs @ 233:a36a0e9cc05d

Re #228: Crash with missing abilityID * Separate out the actual loader implementation from the static "WarFoundryLoader" class * Add a setter method for the current loader * Create an abstract and default implementation of the Loader to reduce coupling and allow easier mocking/testing
author IBBoard <dev@ibboard.co.uk>
date Thu, 24 Dec 2009 19:45:39 +0000
parents c931684f9024
children
comparison
equal deleted inserted replaced
232:f5009a00a50d 233:a36a0e9cc05d
1 // This file (WarFoundryLoader.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard. 1 // This file (WarFoundryLoader.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
2 // 2 //
3 // The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license. 3 // The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.
4 4
5 using System; 5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using IBBoard.Collections;
9 using IBBoard.IO;
10 using IBBoard.Logging;
11 using IBBoard.WarFoundry.API.Factories;
12 using IBBoard.WarFoundry.API.Objects;
13 using ICSharpCode.SharpZipLib.Zip; 6 using ICSharpCode.SharpZipLib.Zip;
14 7
15 namespace IBBoard.WarFoundry.API 8 namespace IBBoard.WarFoundry.API
16 { 9 {
17 public class WarFoundryLoader 10 public class WarFoundryLoader
18 { 11 {
19 private static WarFoundryLoader loader; 12 private static AbstractWarFoundryLoader loader;
20 13
21 /// <summary> 14 /// <summary>
22 /// Gets the default <see cref="WarFoundryLoader"/> used to load WarFoundry data files. 15 /// Gets the default <see cref="WarFoundryLoader"/> used to load WarFoundry data files.
23 /// </summary> 16 /// </summary>
24 /// <returns> 17 /// <returns>
25 /// The default <see cref="WarFoundryLoader"/> 18 /// The default <see cref="WarFoundryLoader"/>
26 /// </returns> 19 /// </returns>
27 public static WarFoundryLoader GetDefault() 20 public static AbstractWarFoundryLoader GetDefault()
28 { 21 {
29 if (loader == null) 22 if (loader == null)
30 { 23 {
31 loader = new WarFoundryLoader(); 24 loader = new DefaultWarFoundryLoader();
32 } 25 }
33 26
34 return loader; 27 return loader;
35 } 28 }
36 29
37 private ICollection<DirectoryInfo> directories; 30 public static void SetDefault(AbstractWarFoundryLoader newLoader)
38 private ICollection<INativeWarFoundryFactory> factories; 31 {
39 private ICollection<INonNativeWarFoundryFactory> nonNativeFactories; 32 loader = newLoader;
40 private Dictionary<string, GameSystem> systemsTable; 33 }
41 private Dictionary<string, Dictionary<string, Dictionary<string, Race>>> racesTable; //Keys are: System, Race, SubRace
42 private Dictionary<IWarFoundryFactory, SimpleSet<IWarFoundryObject>> loadedObjects;
43 public delegate void FileLoadingCompleteDelegate(List<FileLoadFailure> failures);
44 public event MethodInvoker FileLoadingStarted;
45 public event FileLoadingCompleteDelegate FileLoadingFinished;
46 34
47 private WarFoundryLoader() 35 private WarFoundryLoader()
48 { 36 {
49 directories = new List<DirectoryInfo>(); 37 //Hide constructor
50 factories = new List<INativeWarFoundryFactory>();
51 nonNativeFactories = new List<INonNativeWarFoundryFactory>();
52 loadedObjects = new Dictionary<IWarFoundryFactory,SimpleSet<IWarFoundryObject>>();
53 }
54
55 /// <summary>
56 /// Adds a directory to the collection of directories that will be searched for WarFoundry data files.
57 /// </summary>
58 /// <param name="directory">
59 /// The <see cref="DirectoryInfo"/> to add to the list for searching for data files
60 /// </param>
61 public void AddLoadDirectory(DirectoryInfo directory)
62 {
63 if (!directories.Contains(directory))
64 {
65 directories.Add(directory);
66 }
67 }
68
69 /// <summary>
70 /// Removes a directory from the collection of directories that will be searched for WarFoundry data files.
71 /// </summary>
72 /// <param name="directory">
73 /// A <see cref="DirectoryInfo"/>
74 /// </param>
75 public void RemoveLoadDirectory(DirectoryInfo directory)
76 {
77 if (directories.Contains(directory))
78 {
79 directories.Remove(directory);
80 }
81 }
82
83 /// <summary>
84 /// Registers a <see cref="INativeWarFoundryFactory"/> as a factory that can parse native data files.
85 /// </summary>
86 /// <param name="factory">
87 /// The <see cref="INativeWarFoundryFactory"/> to register to parse native data files.
88 /// </param>
89 public void RegisterFactory(INativeWarFoundryFactory factory)
90 {
91 if (!factories.Contains(factory))
92 {
93 factories.Add(factory);
94 }
95 }
96
97 /// <summary>
98 /// Unregisters a <see cref="INativeWarFoundryFactory"/> so that it will no longer be used to try to parse native data files.
99 /// </summary>
100 /// <param name="factory">
101 /// The <see cref="INativeWarFoundryFactory"/> to remove from the collection of factories that are used to try to parse native data files.
102 /// </param>
103 public void UnregisterFactory(INativeWarFoundryFactory factory)
104 {
105 if (factories.Contains(factory))
106 {
107 factories.Remove(factory);
108 }
109 }
110
111 /// <summary>
112 /// Registers a <see cref="INonNativeWarFoundryFactory"/> so that it will be used to try to parse non-native data files from other applications.
113 /// </summary>
114 /// <param name="factory">
115 /// The <see cref="INonNativeWarFoundryFactory"/> to register to parse non-native data files.
116 /// </param>
117 public void RegisterNonNativeFactory(INonNativeWarFoundryFactory factory)
118 {
119 if (!nonNativeFactories.Contains(factory))
120 {
121 nonNativeFactories.Add(factory);
122 }
123 }
124
125 /// <summary>
126 /// Unregisters a <see cref="INonNativeWarFoundryFactory"/> so that it will no longer be used to try to parse non-native data files from other applications.
127 /// </summary>
128 /// <param name="factory">
129 /// The <see cref="INonNativeWarFoundryFactory"/> to remove from the collection of factories that are used to try to parse non-native data files.
130 /// </param>
131 public void UnregisterNonNativeFactory(INonNativeWarFoundryFactory factory)
132 {
133 if (nonNativeFactories.Contains(factory))
134 {
135 nonNativeFactories.Remove(factory);
136 }
137 }
138
139 /// <summary>
140 /// Loads all of the data files in the registered directories.
141 /// </summary>
142 /// <returns>
143 /// A <see cref="List"/> of <see cref="FileLoadFailure"/> for files that failed to load
144 /// </returns>
145 public List<FileLoadFailure> LoadFiles()
146 {
147 LogNotifier.Debug(GetType(), "Load files");
148 PrepareForFileLoad();
149 Dictionary<FileInfo, IWarFoundryFactory> loadableRaces = new Dictionary<FileInfo, IWarFoundryFactory>();
150 Dictionary<FileInfo, IWarFoundryFactory> loadableGameSystems = new Dictionary<FileInfo, IWarFoundryFactory>();
151 List<FileLoadFailure> failedLoads = FillLoadableFiles(loadableRaces, loadableGameSystems);
152 failedLoads.AddRange(LoadGameSystems(loadableGameSystems));
153 failedLoads.AddRange(LoadRaces(loadableRaces));
154 OnFileLoadingFinished(failedLoads);
155 return failedLoads;
156 }
157
158 private void OnFileLoadingFinished(List<FileLoadFailure> failures)
159 {
160 if (FileLoadingFinished!=null)
161 {
162 FileLoadingFinished(failures);
163 }
164 }
165
166 protected void PrepareForFileLoad()
167 {
168 //Just set up blank dictionaries for now - may try different and more complex handling in future
169 systemsTable = new Dictionary<string,GameSystem>();
170 racesTable = new Dictionary<string,Dictionary<string,Dictionary<string,Race>>>();
171 }
172
173 private List<FileLoadFailure> FillLoadableFiles(Dictionary<FileInfo, IWarFoundryFactory> loadableRaces, Dictionary<FileInfo, IWarFoundryFactory> loadableGameSystems)
174 {
175 List<FileLoadFailure> fails = new List<FileLoadFailure>();
176
177 foreach (DirectoryInfo directory in directories)
178 {
179 if (directory.Exists)
180 {
181 List<FileLoadFailure> directoryFails = FillLoadableFilesForDirectory(loadableRaces, loadableGameSystems, directory);
182 fails.AddRange(directoryFails);
183 }
184 else
185 {
186 LogNotifier.WarnFormat(GetType(), "Load for {0} failed because directory didn't exist", directory.FullName);
187 }
188 }
189
190 return fails;
191 }
192
193 private List<FileLoadFailure> FillLoadableFilesForDirectory(Dictionary<FileInfo, IWarFoundryFactory> loadableRaces, Dictionary<FileInfo, IWarFoundryFactory> loadableGameSystems, DirectoryInfo directory)
194 {
195 List<FileLoadFailure> fails = new List<FileLoadFailure>();
196 LogNotifier.Debug(GetType(), "Load from "+directory.FullName);
197
198 foreach (FileInfo file in directory.GetFiles())
199 {
200 IWarFoundryFactory factory = GetGameSystemLoadingFactoryForFile(file);
201
202 if (factory != null)
203 {
204 loadableGameSystems.Add(file, factory);
205 }
206 else
207 {
208 factory = GetRaceLoadingFactoryForFile(file);
209
210 if (factory!=null)
211 {
212 loadableRaces.Add(file, factory);
213 }
214 else
215 {
216 FileLoadFailure failure = new FileLoadFailure(file, "File not handled as a Race or Game System definition: {0}", "FileNotHandled");
217 fails.Add(failure);
218 LogNotifier.Info(GetType(), failure.Message);
219 }
220 }
221 }
222
223 return fails;
224 }
225
226 private IWarFoundryFactory GetGameSystemLoadingFactoryForFile(FileInfo file)
227 {
228 IWarFoundryFactory loadingFactory = null;
229
230 foreach (INonNativeWarFoundryFactory factory in nonNativeFactories)
231 {
232 if (factory.CanHandleFileAsGameSystem(file))
233 {
234 loadingFactory = factory;
235 break;
236 }
237 }
238
239 if (loadingFactory == null)
240 {
241 foreach (INativeWarFoundryFactory factory in factories)
242 {
243 if (factory.CanHandleFileAsGameSystem(file))
244 {
245 loadingFactory = factory;
246 break;
247 }
248 }
249 }
250
251 return loadingFactory;
252 }
253
254 private IWarFoundryFactory GetRaceLoadingFactoryForFile(FileInfo file)
255 {
256 IWarFoundryFactory loadingFactory = null;
257
258 foreach (INonNativeWarFoundryFactory factory in nonNativeFactories)
259 {
260 if (factory.CanHandleFileAsRace(file))
261 {
262 loadingFactory = factory;
263 break;
264 }
265 }
266
267 if (loadingFactory == null)
268 {
269 foreach (INativeWarFoundryFactory factory in factories)
270 {
271 if (factory.CanHandleFileAsRace(file))
272 {
273 loadingFactory = factory;
274 break;
275 }
276 }
277 }
278
279 return loadingFactory;
280 }
281
282 private List<FileLoadFailure> LoadGameSystems(Dictionary<FileInfo, IWarFoundryFactory> gameSystemFiles)
283 {
284 List<FileLoadFailure> fails = new List<FileLoadFailure>();
285
286
287 foreach (FileInfo file in gameSystemFiles.Keys)
288 {
289 FileLoadFailure failure = null;
290
291 try
292 {
293 bool loaded = LoadObject(file, gameSystemFiles[file]);
294
295 if (!loaded)
296 {
297 failure = new FileLoadFailure(file, "FileLoadFailed", "Failed to load {0} as GameSystem using {1}");
298 }
299 }
300 catch (Exception ex)
301 {
302 failure = new FileLoadFailure(file, null, ex.Message, null, ex);
303 }
304
305 if (failure!=null)
306 {
307 fails.Add(failure);
308 LogNotifier.Warn(GetType(), failure.Message, failure.Exception);
309 }
310 }
311
312 return fails;
313 }
314
315 private List<FileLoadFailure> LoadRaces(Dictionary<FileInfo, IWarFoundryFactory> raceFiles)
316 {
317 List<FileLoadFailure> fails = new List<FileLoadFailure>();
318
319 foreach (FileInfo file in raceFiles.Keys)
320 {
321 FileLoadFailure failure = null;
322
323 try
324 {
325 bool loaded = LoadObject(file, raceFiles[file]);
326
327 if (!loaded)
328 {
329 failure = new FileLoadFailure(file, "FileLoadFailed", "Failed to load {0} as Race using {1}");
330 }
331 }
332 catch (Exception ex)
333 {
334 failure = new FileLoadFailure(file, null, ex.Message, null, ex);
335 }
336
337 if (failure!=null)
338 {
339 fails.Add(failure);
340 LogNotifier.Warn(GetType(), failure.Message, failure.Exception);
341 }
342 }
343
344 return fails;
345 }
346
347 private bool LoadObject(FileInfo file, IWarFoundryFactory factory)
348 {
349 bool loaded = false;
350
351 LogNotifier.DebugFormat(GetType(), "Loading {0} using {1}", file.FullName, factory.GetType().Name);
352 ICollection<IWarFoundryObject> objects = factory.CreateObjectsFromFile(file);
353
354 if (objects.Count > 0)
355 {
356 AddLoadedObjects(objects, factory);
357 loaded = true;
358 }
359
360 return loaded;
361 }
362
363
364 /// <summary>
365 /// Loads a single file through the registered WarFoundryFactories, if a factory exists that supports the file format.
366 /// </summary>
367 /// <param name="file">
368 /// A <see cref="FileInfo"/> for the file to attempt to load
369 /// </param>
370 /// <returns>
371 /// An ICollection of IWarFoundryObjects loaded from <code>file</code>
372 /// </returns>
373 public ICollection<IWarFoundryObject> LoadFile(FileInfo file)
374 {
375 ICollection<IWarFoundryObject> objs = null;
376 IWarFoundryFactory loadFactory = null;
377
378 try
379 {
380 objs = LoadFileWithNonNativeFactories(file, out loadFactory);
381
382 if (objs == null)
383 {
384 objs = LoadFileWithNativeFactories(file, out loadFactory);
385 }
386 }
387 catch (InvalidFileException ex)
388 {
389 LogNotifier.Error(GetType(), file.FullName+" failed to load", ex);
390 }
391
392 if (objs!=null)
393 {
394 AddLoadedObjects(objs, loadFactory);
395 }
396 else
397 {
398 objs = new List<IWarFoundryObject>();
399 }
400
401 return objs;
402 }
403
404 private ICollection<IWarFoundryObject> LoadFileWithNonNativeFactories(FileInfo file, out IWarFoundryFactory loadFactory)
405 {
406 ICollection<IWarFoundryObject> objs = null;
407 loadFactory = null;
408
409 if (nonNativeFactories.Count > 0)
410 {
411 LogNotifier.Debug(GetType(), "Attempting to load "+file.FullName+" as a non-native file");
412
413 foreach (INonNativeWarFoundryFactory factory in nonNativeFactories)
414 {
415 bool canLoad = factory.CanHandleFileFormat(file);
416 LogNotifier.Debug(GetType(), "Load using "+factory.GetType().FullName+"? " + (canLoad ? "yes" : "no"));
417
418 if (canLoad)
419 {
420 objs = factory.CreateObjectsFromFile(file);
421
422 if (objs!=null)
423 {
424 loadFactory = factory;
425 break;
426 }
427 }
428 }
429 }
430
431 return objs;
432 }
433
434 private ICollection<IWarFoundryObject> LoadFileWithNativeFactories(FileInfo file, out IWarFoundryFactory loadFactory)
435 {
436 ICollection<IWarFoundryObject> objs = null;
437 loadFactory = null;
438
439 if (factories.Count > 0)
440 {
441 LogNotifier.Debug(GetType(), "Attempting to load "+file.FullName+" as native file");
442
443 foreach (INativeWarFoundryFactory factory in factories)
444 {
445 if (factory.CanHandleFileFormat(file))
446 {
447 objs = factory.CreateObjectsFromFile(file);
448
449 if (objs!=null)
450 {
451 loadFactory = factory;
452 break;
453 }
454 }
455 }
456 }
457
458 return objs;
459 }
460
461 private void AddLoadedObjects(ICollection<IWarFoundryObject> loadedObjs, IWarFoundryFactory factory)
462 {
463 SimpleSet<IWarFoundryObject> objs;
464 loadedObjects.TryGetValue(factory, out objs);
465
466 if (objs == null)
467 {
468 objs = new SimpleSet<IWarFoundryObject>();
469 loadedObjects.Add(factory, objs);
470 }
471
472 objs.AddRange(loadedObjs);
473 StoreObjects(loadedObjs);
474 }
475
476 private void StoreObjects(ICollection<IWarFoundryObject> loadedObjects)
477 {
478 foreach (IWarFoundryObject loadedObject in loadedObjects)
479 {
480 if (loadedObject is GameSystem)
481 {
482 StoreGameSystem((GameSystem)loadedObject);
483 }
484 else if (loadedObject is Race)
485 {
486 StoreRace((Race)loadedObject);
487 }
488 }
489 }
490
491 protected void StoreGameSystem(GameSystem system)
492 {
493 string sysid = system.ID.ToLower();
494
495 if (systemsTable.ContainsKey(sysid))
496 {
497 GameSystem existingSystem = systemsTable[sysid];
498
499 if (!system.Equals(existingSystem))
500 {
501 //TODO: Raise an event to say we got a different duplicate
502 //We can't just fail, because failing is for completely unhandled files, not for objects in a file
503 }
504 }
505 else
506 {
507 systemsTable.Add(sysid, (GameSystem)system);
508 }
509 }
510
511 protected void StoreRace(Race race)
512 {
513 Dictionary<string, Dictionary<string, Race>> systemRaces;
514
515 if (race.GameSystem == null)
516 {
517 throw new InvalidOperationException("Race cannot have null game system. Game system should be loaded before race.");
518 }
519
520 string systemID = race.GameSystem.ID.ToLower();
521 racesTable.TryGetValue(systemID, out systemRaces);
522
523 if (systemRaces==null)
524 {
525 systemRaces = new Dictionary<string,Dictionary<string,Race>>();
526 racesTable.Add(systemID, systemRaces);
527 }
528
529 Dictionary<string, Race> subRaces;
530 systemRaces.TryGetValue(race.ID.ToLower(), out subRaces);
531
532 if (subRaces==null)
533 {
534 subRaces = new Dictionary<string,Race>();
535 systemRaces.Add(race.ID.ToLower(), subRaces);
536 }
537
538 string subID = race.SubID.ToLower();
539
540 if (subRaces.ContainsKey(subID))
541 {
542 Race existingRace = subRaces[subID];
543
544 if (!race.Equals(existingRace))
545 {
546 //TODO: Raise an event to say we got a different duplicate
547 //We can't just fail, because failing is for completely unhandled files, not for objects in a file
548 }
549 }
550 else
551 {
552 subRaces.Add(race.SubID.ToLower(), race);
553 }
554 }
555
556 /// <summary>
557 /// Gets all <see cref="GameSystem"/>s that are currently available, determined by those that can be loaded with the current <see cref="IWarFoundryFactory"/>s.
558 /// </summary>
559 /// <returns>
560 /// An array of <see cref="GameSystem"/>s that are currently available.
561 /// </returns>
562 public GameSystem[] GetGameSystems()
563 {
564 if (systemsTable==null)
565 {
566 LoadFiles();
567 }
568
569 return DictionaryUtils.ToArray<string, GameSystem>(systemsTable);
570 }
571
572 /// <summary>
573 /// Gets a single <see cref="GameSystem"/> with a given ID.
574 /// </summary>
575 /// <param name="systemID">
576 /// The ID of the <see cref="GameSystem"/> to get, as a <see cref="System.String"/>.
577 /// </param>
578 /// <returns>
579 /// The <see cref="GameSystem"/> with the given ID, or <code>null</code> if one doesn't exist.
580 /// </returns>
581 public GameSystem GetGameSystem(string systemID)
582 {
583 if (systemsTable==null)
584 {
585 LoadFiles();
586 }
587
588 GameSystem system;
589 systemsTable.TryGetValue(systemID.ToLower(), out system);
590 return system;
591 }
592
593 /// <summary>
594 /// Removes a loaded <see cref="GameSystem"/>. Used when a GameSystem fails to complete loading
595 /// </summary>
596 /// <param name="system">The GameSystem to remove</param>
597 internal void RemoveGameSystem(GameSystem system)
598 {
599 systemsTable.Remove(system.ID.ToLower());
600 }
601
602 /// <summary>
603 /// Gets an array of the races for the specified <see cref="GameSystem"/>.
604 /// </summary>
605 /// <param name="system">
606 /// The <see cref="GameSystem"/> to get the available races for.
607 /// </param>
608 /// <returns>
609 /// An array of <see cref="Race"/>s for the <see cref="GameSystem"/>
610 /// </returns>
611 public Race[] GetRaces(GameSystem system)
612 {
613 return GetRaces(system.ID);
614 }
615
616 /// <summary>
617 /// Gets an array of the races for a game system by ID.
618 /// </summary>
619 /// <param name="systemID">
620 /// The <see cref="System.String"/> ID of the game system to get races for
621 /// </param>
622 /// <returns>
623 /// An array of <see cref="Race"/>s for the specified game system
624 /// </returns>
625 public Race[] GetRaces(string systemID)
626 {
627 if (racesTable==null)
628 {
629 LoadFiles();
630 }
631
632 systemID = systemID.ToLower();
633 Dictionary<string, Dictionary<string, Race>> system;
634 racesTable.TryGetValue(systemID, out system);
635
636 if (system==null)
637 {
638 return new Race[0];
639 }
640
641 int count = 0;
642
643 foreach (Dictionary<string, Race> racesDict in system.Values)
644 {
645 count+= racesDict.Count;
646 }
647
648 Race[] races = new Race[count];
649 int i = 0;
650
651 foreach (string raceID in system.Keys)
652 {
653 foreach (string raceSubId in system[raceID].Keys)
654 {
655 races[i++] = GetRace(systemID, raceID, raceSubId);
656 }
657 }
658
659 return races;
660 }
661
662 /// <summary>
663 /// Gets a single race for a given <see cref="GameSystem"/> by ID of the race.
664 /// </summary>
665 /// <param name="system">
666 /// The <see cref="GameSystem"/> that the race is part of.
667 /// </param>
668 /// <param name="raceID">
669 /// A <see cref="System.String"/> ID for the race to load.
670 /// </param>
671 /// <returns>
672 /// A <see cref="Race"/> with the specified ID from the <see cref="GameSystem"/>, or <code>null</code> if one doesn't exist.
673 /// </returns>
674 public Race GetRace(GameSystem system, string raceID)
675 {
676 return GetRace(system.ID, raceID);
677 }
678
679 /// <summary>
680 /// Gets a single race for a given game system by ID of the game system and race.
681 /// </summary>
682 /// <param name="systemID">
683 /// The <see cref="System.String"/> ID of the game system that the race is part of.
684 /// </param>
685 /// <param name="raceID">
686 /// The <see cref="System.String"/> ID for the race to load.
687 /// </param>
688 /// <returns>
689 /// A <see cref="Race"/> with the specified ID from the game system with the specified ID, or <code>null</code> if there is no race or game system with those IDs.
690 /// </returns>
691 public Race GetRace(string systemID, string raceID)
692 {
693 return GetRace(systemID, raceID, "");
694 }
695
696 /// <summary>
697 /// Gets a single race for a given <see cref="GameSystem"/> by the race's ID and sub-race ID.
698 /// </summary>
699 /// <param name="system">
700 /// The <see cref="GameSystem"/> that the race is part of.
701 /// </param>
702 /// <param name="raceID">
703 /// The <see cref="System.String"/> ID for the race to load.
704 /// </param>
705 /// <param name="raceSubID">
706 /// A <see cref="System.String"/>
707 /// </param>
708 /// <returns>
709 /// A <see cref="Race"/>
710 /// </returns>
711 public Race GetRace(GameSystem system, string raceID, string raceSubID)
712 {
713 return GetRace(system.ID, raceID, raceSubID);
714 }
715
716 /// <summary>
717 /// Gets a single race for a given game system by the game system's ID and the race's ID and sub-race ID.
718 /// </summary>
719 /// <param name="systemID">
720 /// The <see cref="System.String"/> ID of the game system that the race is part of.
721 /// </param>
722 /// <param name="raceID">
723 /// The <see cref="System.String"/> ID for the race to load.
724 /// </param>
725 /// <param name="raceSubID">
726 /// A <see cref="System.String"/>
727 /// </param>
728 /// <returns>
729 /// A <see cref="Race"/>
730 /// </returns>
731 public Race GetRace(string systemID, string raceID, string raceSubID)
732 {
733 if (racesTable==null)
734 {
735 LoadFiles();
736 }
737
738 Race race = null;
739
740 Dictionary<string, Race> subraces = GetRaceTable(systemID, raceID);
741
742 if (subraces != null)
743 {
744 subraces.TryGetValue(raceSubID.ToLower(), out race);
745 }
746
747 return race;
748 }
749
750 private Dictionary<string, Race> GetRaceTable(string systemID, string raceID)
751 {
752 Dictionary<string, Dictionary<string, Race>> races;
753 racesTable.TryGetValue(systemID.ToLower(), out races);
754 Dictionary<string, Race> subraces = null;
755
756 if (races != null)
757 {
758 races.TryGetValue(raceID.ToLower(), out subraces);
759 }
760
761 return subraces;
762 }
763
764 internal void RemoveRace(Race race)
765 {
766 Dictionary<string, Race> subraces = GetRaceTable(race.GameSystem.ID, race.ID);
767
768 if (subraces != null)
769 {
770 subraces.Remove(race.SubID.ToLower());
771 }
772 }
773
774 /// <summary>
775 /// Gets the IDs of all of the game systems currently available.
776 /// </summary>
777 /// <returns>
778 /// An array of <see cref="System.String"/>s representing the IDs of the game systems.
779 /// </returns>
780 public string[] GetGameSystemIDs()
781 {
782 if (systemsTable==null)
783 {
784 LoadFiles();
785 }
786
787 string[] keys = new string[systemsTable.Keys.Count];
788 int i = 0;
789
790 foreach (string key in systemsTable.Keys)
791 {
792 keys[i++] = key;
793 }
794
795 return keys;
796 }
797
798 /// <summary>
799 /// Gets the IDs of all of the races of a specified game system.
800 /// </summary>
801 /// <param name="system">
802 /// The <see cref="GameSystem"/> to get the available races for.
803 /// </param>
804 /// <returns>
805 /// An array of <see cref="System.String"/>s representing the IDs of the races of the specified game system.
806 /// </returns>
807 public string[] GetSystemRaceIDs(GameSystem system)
808 {
809 return GetSystemRaceIDs(system.ID);
810 }
811
812 /// <summary>
813 /// Gets the IDs of all of the races of a specified game system.
814 /// </summary>
815 /// <param name="systemID">
816 /// The <see cref="System.String"/> ID of the game system to get the available races for.
817 /// </param>
818 /// <returns>
819 /// An array of <see cref="System.String"/>s representing the IDs of the races of the specified game system.
820 /// </returns>
821 public string[] GetSystemRaceIDs(string systemID)
822 {
823 if (racesTable == null)
824 {
825 LoadFiles();
826 }
827
828 Dictionary<string, Dictionary<string, Race>> races = racesTable[systemID.ToLower()];
829
830 if (races==null)
831 {
832 return new string[0];
833 }
834 else
835 {
836 string[] keys = new string[races.Keys.Count];
837 int i = 0;
838
839 foreach (string key in races.Keys)
840 {
841 keys[i++] = key;
842 }
843
844 return keys;
845 }
846 }
847
848 public Army LoadArmy(FileInfo file)
849 {
850 IWarFoundryFactory factory = GetArmyLoadingFactoryForFile(file);
851 Army loadedArmy = null;
852
853 if (factory != null)
854 {
855 ICollection<IWarFoundryObject> objs = factory.CreateObjectsFromFile(file);
856
857 if (objs.Count == 1)
858 {
859 foreach (IWarFoundryObject obj in objs)
860 {
861 if (obj is Army)
862 {
863 loadedArmy = (Army) obj;
864 }
865 }
866 }
867 }
868
869 return loadedArmy;
870 }
871
872 private IWarFoundryFactory GetArmyLoadingFactoryForFile(FileInfo file)
873 {
874 IWarFoundryFactory loadingFactory = null;
875
876 foreach (INonNativeWarFoundryFactory factory in nonNativeFactories)
877 {
878 if (factory.CanHandleFileAsArmy(file))
879 {
880 loadingFactory = factory;
881 break;
882 }
883 }
884
885 if (loadingFactory == null)
886 {
887 foreach (INativeWarFoundryFactory factory in factories)
888 {
889 if (factory.CanHandleFileAsArmy(file))
890 {
891 loadingFactory = factory;
892 break;
893 }
894 }
895 }
896
897 return loadingFactory;
898 } 38 }
899 } 39 }
900 } 40 }