Mercurial > repos > IBDev-IBBoard.WarFoundry.API
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 } |