internal static List <string> GetTypesFromCacheOrManifest(VersionManifest manifest, string path) { if (typeCache.ContainsKey(path)) { return(typeCache[path]); } // get the type from the manifest var matchingEntries = manifest.FindAll(x => Path.GetFullPath(x.FilePath) == path); if (matchingEntries == null || matchingEntries.Count == 0) { return(null); } var types = new List <string>(); foreach (var existingEntry in matchingEntries) { types.Add(existingEntry.Type); } typeCache[path] = types; return(typeCache[path]); }
//Is it possible for this to ever return more than 1 type for a entry in the manifest? // Based on the output file the answer is yes. Prefab+Assetbundle | Texture2D+Sprite are examples //Can a manifest entry even have more than one type? // Multiple entries with the same ID could have different types in their entry public List <string> GetTypes(string id, VersionManifest manifest = null) { if (entries.ContainsKey(id)) { return(entries[id]); } if (manifest != null) { // get the types from the manifest var matchingEntries = manifest.FindAll(x => x.Id == id); if (matchingEntries == null || matchingEntries.Count == 0) { return(null); } var types = new List <string>(); foreach (var existingEntry in matchingEntries) { types.Add(existingEntry.Type); } entries[id] = types; return(entries[id]); } return(null); }
internal static List <string> GetTypesFromCacheOrManifest(VersionManifest manifest, string id) { var types = GetTypesFromCache(id); if (types != null) { return(types); } // get the types from the manifest var matchingEntries = manifest.FindAll(x => x.Id == id); if (matchingEntries == null || matchingEntries.Count == 0) { return(null); } types = new List <string>(); foreach (var existingEntry in matchingEntries) { types.Add(existingEntry.Type); } typeCache[id] = types; return(typeCache[id]); }
internal static void TryAddToVersionManifest(VersionManifest manifest) { if (!hasLoadedMods) { LoadMods(); } // there are no mods loaded, just return if (modLoadOrder == null || modLoadOrder.Count == 0) { return; } if (modEntries != null) { LogWithDate("Loading another manifest with already setup mod manifests."); foreach (var modEntry in modEntries) { AddModEntryToVersionManifest(manifest, modEntry); } LogWithDate("Done."); return; } modEntries = new List <ModDef.ManifestEntry>(); LogWithDate("Setting up mod manifests..."); var breakMyGame = File.Exists(Path.Combine(ModDirectory, "break.my.game")); if (breakMyGame) { var mddPath = Path.Combine(Path.Combine(StreamingAssetsDirectory, "MDD"), "MetadataDatabase.db"); var mddBackupPath = mddPath + ".orig"; Log($"\tBreak my game mode enabled! All new modded content (doesn't currently support merges) will be added to the DB."); if (!File.Exists(mddBackupPath)) { Log($"\t\tBacking up metadata database to {Path.GetFileName(mddBackupPath)}"); File.Copy(mddPath, mddBackupPath); } } var jsonMerges = new Dictionary <string, List <string> >(); foreach (var modName in modLoadOrder) { if (!ModManifest.ContainsKey(modName)) { continue; } Log($"\t{modName}:"); foreach (var modEntry in ModManifest[modName]) { // type being null means we have to figure out the type from the path (StreamingAssets) if (modEntry.Type == null) { // TODO: + 16 is a little bizzare looking, it's the length of the substring + 1 because we want to get rid of it and the \ var relPath = modEntry.Path.Substring(modEntry.Path.LastIndexOf("StreamingAssets", StringComparison.Ordinal) + 16); var fakeStreamingAssetsPath = Path.GetFullPath(Path.Combine(StreamingAssetsDirectory, relPath)); List <string> types; if (TypeCache.ContainsKey(fakeStreamingAssetsPath)) { types = TypeCache[fakeStreamingAssetsPath]; } else { // get the type from the manifest var matchingEntries = manifest.FindAll(x => Path.GetFullPath(x.FilePath) == fakeStreamingAssetsPath); if (matchingEntries == null || matchingEntries.Count == 0) { Log($"\t\tCould not find an existing VersionManifest entry for {modEntry.Id}. Is this supposed to be a new entry? Don't put new entries in StreamingAssets!"); continue; } types = new List <string>(); foreach (var existingEntry in matchingEntries) { types.Add(existingEntry.Type); } TypeCache[fakeStreamingAssetsPath] = types; } if (Path.GetExtension(modEntry.Path).ToLower() == ".json" && modEntry.ShouldMergeJSON) { if (!TypeCache.ContainsKey(fakeStreamingAssetsPath)) { Log($"\t\tUnable to determine type of {modEntry.Id}. Is there someone screwy with your this mod.json?"); continue; } if (!jsonMerges.ContainsKey(fakeStreamingAssetsPath)) { jsonMerges[fakeStreamingAssetsPath] = new List <string>(); } if (jsonMerges[fakeStreamingAssetsPath].Contains(modEntry.Path)) { continue; } // this assumes that .json can only have a single type modEntry.Type = TypeCache[fakeStreamingAssetsPath][0]; Log($"\t\tMerge => {modEntry.Id} ({modEntry.Type})"); jsonMerges[fakeStreamingAssetsPath].Add(modEntry.Path); continue; } foreach (var type in types) { var subModEntry = new ModDef.ManifestEntry(modEntry, modEntry.Path, modEntry.Id); subModEntry.Type = type; if (AddModEntryToVersionManifest(manifest, subModEntry, breakMyGame)) { modEntries.Add(modEntry); } } continue; } // non-streamingassets json merges if (Path.GetExtension(modEntry.Path).ToLower() == ".json" && modEntry.ShouldMergeJSON) { // have to find the original path for the manifest entry that we're merging onto var matchingEntry = manifest.Find(x => x.Id == modEntry.Id); if (matchingEntry == null) { Log($"\t\tCould not find an existing VersionManifest entry for {modEntry.Id}!"); continue; } if (!jsonMerges.ContainsKey(matchingEntry.FilePath)) { jsonMerges[matchingEntry.FilePath] = new List <string>(); } if (jsonMerges[matchingEntry.FilePath].Contains(modEntry.Path)) { continue; } // this assumes that .json can only have a single type modEntry.Type = matchingEntry.Type; Log($"\t\tMerge => {modEntry.Id} ({modEntry.Type})"); jsonMerges[matchingEntry.FilePath].Add(modEntry.Path); continue; } if (AddModEntryToVersionManifest(manifest, modEntry, breakMyGame)) { modEntries.Add(modEntry); } } } LogWithDate("Doing merges..."); foreach (var jsonMerge in jsonMerges) { var cachePath = JsonMergeCache.GetOrCreateCachedEntry(jsonMerge.Key, jsonMerge.Value); var cacheEntry = new ModDef.ManifestEntry(cachePath); cacheEntry.ShouldMergeJSON = false; cacheEntry.Type = TypeCache[jsonMerge.Key][0]; cacheEntry.Id = InferIDFromFileAndType(cachePath, cacheEntry.Type); if (AddModEntryToVersionManifest(manifest, cacheEntry, breakMyGame)) { modEntries.Add(cacheEntry); } } // write merge cache to disk JsonMergeCache.WriteCacheToDisk(Path.Combine(CacheDirectory, MERGE_CACHE_FILE_NAME)); // write type cache to disk File.WriteAllText(Path.Combine(CacheDirectory, TYPE_CACHE_FILE_NAME), JsonConvert.SerializeObject(TypeCache, Formatting.Indented)); LogWithDate("Done."); Log(""); }
internal static void AddModEntries(VersionManifest manifest) { if (!hasLoadedMods) { LoadMods(); } stopwatch.Start(); // there are no mods loaded, just return if (modLoadOrder == null || modLoadOrder.Count == 0) { return; } if (modEntries != null) { LogWithDate("Loading another manifest with already setup mod manifests."); foreach (var modEntry in modEntries) { AddModEntry(manifest, modEntry); } stopwatch.Stop(); Log(""); LogWithDate($"Done. Elapsed running time: {stopwatch.Elapsed.TotalSeconds} seconds\n"); return; } LogWithDate("Setting up mod manifests..."); var jsonMerges = new Dictionary <string, List <string> >(); modEntries = new List <ModDef.ManifestEntry>(); foreach (var modName in modLoadOrder) { if (!modManifest.ContainsKey(modName)) { continue; } Log($"\t{modName}:"); foreach (var modEntry in modManifest[modName]) { // type being null means we have to figure out the type from the path (StreamingAssets) if (modEntry.Type == null) { // TODO: + 16 is a little bizzare looking, it's the length of the substring + 1 because we want to get rid of it and the \ var relPath = modEntry.Path.Substring(modEntry.Path.LastIndexOf("StreamingAssets", StringComparison.Ordinal) + 16); var fakeStreamingAssetsPath = Path.GetFullPath(Path.Combine(StreamingAssetsDirectory, relPath)); List <string> types; if (typeCache.ContainsKey(fakeStreamingAssetsPath)) { types = typeCache[fakeStreamingAssetsPath]; } else { // get the type from the manifest var matchingEntries = manifest.FindAll(x => Path.GetFullPath(x.FilePath) == fakeStreamingAssetsPath); if (matchingEntries == null || matchingEntries.Count == 0) { Log($"\t\tCould not find an existing VersionManifest entry for {modEntry.Id}. Is this supposed to be a new entry? Don't put new entries in StreamingAssets!"); continue; } types = new List <string>(); foreach (var existingEntry in matchingEntries) { types.Add(existingEntry.Type); } typeCache[fakeStreamingAssetsPath] = types; } if (Path.GetExtension(modEntry.Path).ToLower() == ".json" && modEntry.ShouldMergeJSON) { if (!typeCache.ContainsKey(fakeStreamingAssetsPath)) { Log($"\t\tUnable to determine type of {modEntry.Id}. Is there someone screwy with your this mod.json?"); continue; } if (!jsonMerges.ContainsKey(fakeStreamingAssetsPath)) { jsonMerges[fakeStreamingAssetsPath] = new List <string>(); } if (jsonMerges[fakeStreamingAssetsPath].Contains(modEntry.Path)) { continue; } // this assumes that .json can only have a single type modEntry.Type = typeCache[fakeStreamingAssetsPath][0]; Log($"\t\tMerge => {modEntry.Id} ({modEntry.Type})"); jsonMerges[fakeStreamingAssetsPath].Add(modEntry.Path); continue; } foreach (var type in types) { var subModEntry = new ModDef.ManifestEntry(modEntry, modEntry.Path, modEntry.Id); subModEntry.Type = type; if (AddModEntry(manifest, subModEntry)) { modEntries.Add(subModEntry); } } continue; } // get "fake" entries that don't actually go into the game's VersionManifest // add videos to be loaded from an external path if (modEntry.Type == "Video") { var fileName = Path.GetFileName(modEntry.Path); if (fileName != null && File.Exists(modEntry.Path)) { Log($"\t\tVideo => {fileName}"); ModVideos.Add(fileName, modEntry.Path); } continue; } // non-streamingassets json merges if (Path.GetExtension(modEntry.Path)?.ToLower() == ".json" && modEntry.ShouldMergeJSON) { // have to find the original path for the manifest entry that we're merging onto var matchingEntry = manifest.Find(x => x.Id == modEntry.Id); if (matchingEntry == null) { Log($"\t\tCould not find an existing VersionManifest entry for {modEntry.Id}!"); continue; } if (!jsonMerges.ContainsKey(matchingEntry.FilePath)) { jsonMerges[matchingEntry.FilePath] = new List <string>(); } if (jsonMerges[matchingEntry.FilePath].Contains(modEntry.Path)) { continue; } // this assumes that .json can only have a single type modEntry.Type = matchingEntry.Type; if (!typeCache.ContainsKey(matchingEntry.FilePath)) { typeCache[matchingEntry.FilePath] = new List <string>(); typeCache[matchingEntry.FilePath].Add(modEntry.Type); } Log($"\t\tMerge => {modEntry.Id} ({modEntry.Type})"); jsonMerges[matchingEntry.FilePath].Add(modEntry.Path); continue; } if (AddModEntry(manifest, modEntry)) { modEntries.Add(modEntry); } } } // write type cache to disk WriteJsonFile(TypeCachePath, typeCache); // perform merges into cache LogWithDate("Doing merges..."); foreach (var jsonMerge in jsonMerges) { var cachePath = jsonMergeCache.GetOrCreateCachedEntry(jsonMerge.Key, jsonMerge.Value); // something went wrong (the parent json prob had errors) if (cachePath == null) { continue; } var cacheEntry = new ModDef.ManifestEntry(cachePath); cacheEntry.ShouldMergeJSON = false; cacheEntry.Type = typeCache[jsonMerge.Key][0]; cacheEntry.Id = InferIDFromFile(cachePath); if (AddModEntry(manifest, cacheEntry)) { modEntries.Add(cacheEntry); } } // write merge cache to disk jsonMergeCache.WriteCacheToDisk(Path.Combine(CacheDirectory, MERGE_CACHE_FILE_NAME)); LogWithDate("Adding to DB..."); // check if files removed from DB cache var rebuildDB = false; var replacementEntries = new List <VersionManifestEntry>(); var removeEntries = new List <string>(); foreach (var kvp in dbCache) { var path = kvp.Key; if (File.Exists(path)) { continue; } Log($"\tNeed to remove DB entry from file in path: {path}"); // file is missing, check if another entry exists with same filename in manifest var fileName = Path.GetFileName(path); var existingEntry = manifest.Find(x => Path.GetFileName(x.FilePath) == fileName); if (existingEntry == null) { Log("\t\tHave to rebuild DB, no existing entry in VersionManifest matches removed entry"); rebuildDB = true; break; } replacementEntries.Add(existingEntry); removeEntries.Add(path); } // add removed entries replacements to db if (!rebuildDB) { // remove old entries foreach (var removeEntry in removeEntries) { dbCache.Remove(removeEntry); } using (var metadataDatabase = new MetadataDatabase()) { foreach (var replacementEntry in replacementEntries) { if (AddModEntryToDB(metadataDatabase, Path.GetFullPath(replacementEntry.FilePath), replacementEntry.Type)) { Log($"\t\tReplaced DB entry with an existing entry in path: {Path.GetFullPath(replacementEntry.FilePath)}"); } } } } // if an entry has been removed and we cannot find a replacement, have to rebuild the mod db if (rebuildDB) { if (File.Exists(ModDBPath)) { File.Delete(ModDBPath); } File.Copy(Path.Combine(Path.Combine(StreamingAssetsDirectory, "MDD"), MDD_FILE_NAME), ModDBPath); dbCache = new Dictionary <string, DateTime>(); } // add needed files to db using (var metadataDatabase = new MetadataDatabase()) { foreach (var modEntry in modEntries) { if (modEntry.AddToDB && AddModEntryToDB(metadataDatabase, modEntry.Path, modEntry.Type)) { Log($"\tAdded/Updated {modEntry.Id} ({modEntry.Type})"); } } } // write db/type cache to disk WriteJsonFile(DBCachePath, dbCache); stopwatch.Stop(); Log(""); LogWithDate($"Done. Elapsed running time: {stopwatch.Elapsed.TotalSeconds} seconds\n"); }