internal void Initialize(AssetStore assetStore) { // We need to check if there is an existing MapCache.ini file and if yes, // load it so that we can figure out which entries are outdated. // Then we need to update these entries, update the file, and parse it // again. In order to do that we first load it in a temporary scope. assetStore.PushScope(); var mapCacheIniEntry = _contentManager.UserDataFileSystem.GetFile(MapCacheIniPath); if (mapCacheIniEntry != null) { _contentManager.LoadIniFile(mapCacheIniEntry); } // for each map, check if it was modified and the MapCache needs to be updated. var mapCacheEntries = new Dictionary <string, MapCache>(); foreach (var mapEntry in EnumerateMaps()) { var buildMapCache = false; var mapCache = assetStore.MapCaches.GetByName(mapEntry.FullFilePath); var fileInfo = new FileInfo(mapEntry.FullFilePath); if (mapCache == null) { // new map buildMapCache = true; } else { var timestamp = DateTime.FromFileTime((((long)mapCache.TimestampHi) << 32) | (uint)mapCache.TimestampLo); // TODO: Should we check the CRC here as well? // If yes, which implementation should we use? if (fileInfo.LastWriteTime != timestamp && fileInfo.Length != mapCache.FileSize) { // existing map modified buildMapCache = true; } } if (buildMapCache) { mapCache = BuildMapCache(mapEntry, fileInfo, assetStore); } mapCacheEntries.Add(mapEntry.FullFilePath, mapCache); } // Get rid of the old MapCaches, generate the file based on // the updated ones and load it again, this time for real. assetStore.PopScope(); var fullMapCacheIniPath = Path.Combine(_contentManager.UserDataFileSystem.RootDirectory, MapCacheIniPath); GenerateMapCacheIniFile(fullMapCacheIniPath, mapCacheEntries); mapCacheIniEntry = new FileSystemEntry( _contentManager.UserDataFileSystem, MapCacheIniPath, (uint)new FileInfo(fullMapCacheIniPath).Length, () => File.OpenRead(fullMapCacheIniPath)); _contentManager.UserDataFileSystem.Update(mapCacheIniEntry); _contentManager.LoadIniFile(mapCacheIniEntry); }
public void CanReadIniFiles() { var gameDefinitions = new[] { GameDefinition.FromGame(SageGame.CncGenerals), GameDefinition.FromGame(SageGame.CncGeneralsZeroHour), GameDefinition.FromGame(SageGame.Bfme), GameDefinition.FromGame(SageGame.Bfme2), GameDefinition.FromGame(SageGame.Bfme2Rotwk), }; using var graphicsDevice = GraphicsDeviceUtility.CreateGraphicsDevice(null, null); using var standardGraphicsResources = new StandardGraphicsResources(graphicsDevice); using var shaderSetStore = new ShaderSetStore(graphicsDevice, RenderPipeline.GameOutputDescription); using var shaderResources = new ShaderResourceManager(graphicsDevice, standardGraphicsResources, shaderSetStore); var graphicsLoadContext = new GraphicsLoadContext(graphicsDevice, standardGraphicsResources, shaderResources, shaderSetStore); foreach (var gameDefinition in gameDefinitions) { foreach (var installation in InstallationLocators.FindAllInstallations(gameDefinition)) { using var fileSystem = installation.CreateFileSystem(); var assetStore = new AssetStore( gameDefinition.Game, fileSystem, LanguageUtility.ReadCurrentLanguage(gameDefinition, fileSystem), graphicsDevice, standardGraphicsResources, shaderResources, shaderSetStore, gameDefinition.CreateAssetLoadStrategy()); assetStore.PushScope(); var dataContext = new IniDataContext(); void LoadIniFile(FileSystemEntry entry) { var parser = new IniParser( entry, assetStore, gameDefinition.Game, dataContext, LocaleSpecificEncoding); parser.ParseFile(); } switch (gameDefinition.Game) { case SageGame.Bfme: case SageGame.Bfme2: case SageGame.Bfme2Rotwk: LoadIniFile(fileSystem.GetFile(@"Data\INI\GameData.ini")); break; } foreach (var file in fileSystem.GetFilesInDirectory("", $"*.ini", SearchOption.AllDirectories)) { var filename = Path.GetFileName(file.FilePath).ToLowerInvariant(); switch (filename) { case "webpages.ini": // Don't care about this case "buttonsets.ini": // Doesn't seem to be used? case "scripts.ini": // Only needed by World Builder? case "commandmapdebug.ini": // Only applies to DEBUG and INTERNAL builds case "fxparticlesystemcustom.ini": // Don't know if this is used, it uses Emitter property not used elsewhere case "lightpoints.ini": // Don't know if this is used. //added in BFME and subsequent games case "optionregistry.ini": // Don't know if this is used case "localization.ini": // Don't know if we need this continue; case "credits.ini": if (gameDefinition.Game == SageGame.Bfme2Rotwk) //corrupted in rotwk (start of the block is commented out) { continue; } break; //mods specific //edain mod case "einstellungen.ini": case "einstellungendeakt.ini": case "einstellungenedain.ini": case "news.ini": continue; //unofficial patch 2.02 case "desktop.ini": //got into a big file somehow case "2.01.ini": case "disable timer.ini": case "enable timer.ini": case "old music.ini": continue; default: if (filename.StartsWith("2.02")) { continue; } break; } _output.WriteLine($"Reading file {file.FilePath}."); LoadIniFile(file); } } } }