private void A_PatchGame(object sender, EventArgs e) { if (Process.GetProcessesByName("ScrapMechanic.exe").Length != 0) { MessageBox.Show("You cannot install mods while the game is running. Please close it first.", "Please close Scrap Mechanic", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } DialogResult box = MessageBox.Show("Have you verified the game cache?\n\nThis is necessary for the patching to work. Your game might stop working if you skipped the previous step.", "Are you sure?", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); if (box == DialogResult.Yes) { // Check for dependency issues foreach (ModInfo i in this.Mods) { foreach (string dependency in i.Dependencies.Where((d) => !ModIDs.Contains(d))) { try { // Show error string[] dependencyInfo = ModInfo.ParseModID(dependency); MessageBox.Show("Error: Mod " + i.ModName + " requires you have the mod " + dependencyInfo[1] + " by " + dependencyInfo[0], "Mod dependency error"); return; } catch { } } } Dictionary <ModInfo, ZipFile> modZipFiles = new Dictionary <ModInfo, ZipFile>(); try { pbPatch.Value = 5; // Load reader GameReader reader = new GameReader(ScrapMechanicFolder); // 1. Load physics materials // 2. Load blocks // 3. Load rotation sets // 4. Load shapesets // 5. Load inventory item descriptions // 6. Load IconMap images Dictionary <string, Material> materials = reader.ReadPhysicsMaterials(); pbPatch.Value = 6; Dictionary <Guid, Block> blocks = reader.ReadBlocks(materials); pbPatch.Value = 7; Dictionary <string, RotationSet> rotationSets = reader.ReadRotationSets(); pbPatch.Value = 8; Dictionary <string, ShapeSet> shapeSets = reader.ReadShapesets(rotationSets, materials); pbPatch.Value = 9; Dictionary <Guid, InventoryItemDescription> invItemDescs = reader.ReadInventoryItemDescriptions(); pbPatch.Value = 10; Dictionary <Guid, Image> icons = reader.ReadIconMap(); pbPatch.Value = 11; Dictionary <string, byte[]> resources = new Dictionary <string, byte[]>(); List <string> addedMaterials = new List <string>(); List <Guid> addedBlocks = new List <Guid>(); List <string> addedRotationSets = new List <string>(); List <Guid> addedInvItemDescs = new List <Guid>(); List <Guid> addedIcons = new List <Guid>(); // 7. Load for all mods mods those things in same order string physicsMaterialsPath = "Data/Objects/Database/physicsmaterials.json"; string blocksPath = "Data/Objects/Database/basicmaterials.json"; string shapeSetsListPath = "Data/Objects/Database/shapesets.json"; string rotationSetsPath = "Data/Objects/Database/rotationsets.json"; string inventoryItemDescriptionsPath = "Data/Gui/InventoryItemDescriptions.json"; foreach (ModInfo i in this.Mods) { modZipFiles[i] = ZipFile.Read(i.FileName); } pbPatch.Value = 15; // Read physics materials foreach (ZipFile zip in modZipFiles.Values) { // If the mod offers new Physics Materials if (zip.ContainsEntry(physicsMaterialsPath)) { // Read and parse the physics materials Dictionary <string, Material> thisModMaterials = Material.ReadFromJson(Zip.ReadText(zip[physicsMaterialsPath])); // Loop over each material foreach (KeyValuePair <string, Material> kvp in thisModMaterials) { // If there is already a material with this name if (!addedMaterials.Contains(kvp.Key)) { // Add the material to the material dictionary, and make sure it won't be overwritten materials[kvp.Key] = kvp.Value; addedMaterials.Add(kvp.Key); } } } } pbPatch.Value = 20; // Read rotation sets foreach (ZipFile zip in modZipFiles.Values) { // If the mod offers new rotation sets if (zip.ContainsEntry(rotationSetsPath)) { // Read and parse the rotation sets Dictionary <string, RotationSet> thisModRotationSets = RotationSet.ReadFromJson(Zip.ReadText(zip[rotationSetsPath])); // Loop over each rotation set foreach (KeyValuePair <string, RotationSet> kvp in thisModRotationSets) { // If there is already a rotation set with this name if (!addedRotationSets.Contains(kvp.Key)) { // Add the rotation set to the rotation set dictionary, and make sure it won't be overwritten rotationSets[kvp.Key] = kvp.Value; addedRotationSets.Add(kvp.Key); } } } } pbPatch.Value = 25; // Read blocks foreach (ZipFile zip in modZipFiles.Values) { // If the mod offers new rotation sets if (zip.ContainsEntry(blocksPath)) { // Read and parse the blocks Dictionary <Guid, Block> thisModBlocks = Block.ReadFromJson(Zip.ReadText(zip[blocksPath]), materials); // Loop over each block foreach (KeyValuePair <Guid, Block> kvp in thisModBlocks) { // If there is already a block with this name if (!addedBlocks.Contains(kvp.Key)) { // Add the block to the rotation set dictionary, and make sure it won't be overwritten blocks[kvp.Key] = kvp.Value; addedBlocks.Add(kvp.Key); } } } } pbPatch.Value = 30; // Load ShapeSets foreach (ZipFile zipFile in modZipFiles.Values) { if (zipFile.ContainsEntry(shapeSetsListPath)) { // Load the shape set list json to a dictionary string sslJson = Zip.ReadText(zipFile[shapeSetsListPath]); IDictionary <string, object> sslDict = JsonConvert.DeserializeObject <ExpandoObject>(sslJson); // See if it has a shape set list if (sslDict.ContainsKey("shapeSetList")) { // Loop over the shape set list foreach (object lObj in (List <object>)sslDict["shapeSetList"]) { string shapeSetName = lObj.ToString(); if (!shapeSets.ContainsKey(shapeSetName)) { if (zipFile.ContainsEntry("Data/Objects/Database/ShapeSets/" + shapeSetName)) { string shapeSetJson = Zip.ReadText(zipFile["Data/Objects/Database/ShapeSets/" + shapeSetName]); shapeSets[shapeSetName] = new ShapeSet(shapeSetName, shapeSetJson, rotationSets, materials); } } else { throw new Exception("There is already a ShapeSet named " + shapeSetName + ".\n\nIf you are the mod maker: To prevent name collisions, prefix your shapesets with your username, and give every ShapeSet in your mods a different name."); } } } } } pbPatch.Value = 35; // Load inventory items foreach (ZipFile zip in modZipFiles.Values) { // If the mod has an Inventory Item Descriptions file if (zip.ContainsEntry(inventoryItemDescriptionsPath)) { // Load the description items Dictionary <Guid, InventoryItemDescription> descs = InventoryItemDescription.ReadFromJson(Zip.ReadText(zip[inventoryItemDescriptionsPath])); foreach (KeyValuePair <Guid, InventoryItemDescription> kvp in descs) { // If no (custom) description for this item exists yet; add one and make sure it won't be overwritten if (!addedInvItemDescs.Contains(kvp.Key)) { addedInvItemDescs.Add(kvp.Key); invItemDescs[kvp.Key] = kvp.Value; } } } } pbPatch.Value = 40; // Load inventory icons foreach (ZipFile zip in modZipFiles.Values) { // If this mod both has a icon information file ... if (zip.ContainsEntry("Data/Gui/IconMap.json")) { // ... and an actual icon map if (zip.ContainsEntry("Data/Gui/IconMap.png")) { // Load all the points into the iconPoints dictionary IDictionary <string, object> imDict = JsonConvert.DeserializeObject <ExpandoObject>(Zip.ReadText(zip["Data/Gui/IconMap.json"])); Dictionary <Guid, Point> iconPoints = new Dictionary <Guid, Point>(); foreach (KeyValuePair <string, object> kvp in imDict) { Guid itemUuid; if (Guid.TryParse(kvp.Key.ToString(), out itemUuid)) { IDictionary <string, object> objDict = kvp.Value as IDictionary <string, object>; if (objDict.ContainsKey("x") && objDict.ContainsKey("y")) { int x, y; if (int.TryParse(objDict["x"].ToString(), out x)) { if (int.TryParse(objDict["y"].ToString(), out y)) { iconPoints.Add(itemUuid, new Point(x, y)); } } } } } // Open the icon map picture using (CrcCalculatorStream stream = zip["Data/Gui/IconMap.png"].OpenReader()) { try { using (Image iconMapImage = Image.FromStream(stream)) { using (Bitmap iconMapImg = new Bitmap(iconMapImage)) { // And for every item without a (custom) icon the icon image; and make sure it won't be overwritten by a lower mod foreach (KeyValuePair <Guid, Point> kvp in iconPoints.Where((p) => !addedIcons.Contains(p.Key))) { int x = kvp.Value.X, y = kvp.Value.Y; Rectangle destination = new Rectangle(x, y, Math.Min(80, iconMapImg.Width - x), Math.Min(80, iconMapImg.Height - y)); Bitmap newIcon = iconMapImg.Clone(destination, iconMapImg.PixelFormat); icons.Add(kvp.Key, newIcon); addedIcons.Add(kvp.Key); } } } } catch (Exception ex) { throw new Exception(ex.Message + " Image data invalid."); } } } else { throw new Exception("Mod provides IconMap.json but no IconMap.png"); } } } pbPatch.Value = 45; // Load resources foreach (KeyValuePair <ModInfo, ZipFile> pair in modZipFiles) { foreach (ZipEntry entry in pair.Value.Entries) { // For every entry that is not a shapeset if (entry.FileName.StartsWith("Data/") && !entry.FileName.Contains("/ShapeSets/") && !entry.FileName.EndsWith("/")) { switch (entry.FileName) { // and also is not one of the files already read by case "Data/Objects/Database/physicsmaterials.json": case "Data/Objects/Database/basicmaterials.json": case "Data/Objects/Database/shapesets.json": case "Data/Objects/Database/rotationsets.json": case "Data/Gui/InventoryItemDescriptions.json": case "Data/Gui/IconMap.json": break; default: // Find file name for in Scrap Mechanic/Data string dataFileName = entry.FileName.Substring(5); // Read bytes from resource file if no higher mod adds this resource as well if (!resources.ContainsKey(dataFileName)) { using (BinaryReader r = new BinaryReader(entry.OpenReader())) resources[dataFileName] = r.ReadBytes((int)entry.UncompressedSize); } break; } } } } pbPatch.Value = 50; // Write everything to the game directory GameWriter writer = new GameWriter(Properties.Settings.Default.ScrapMechanicFolder); writer.WritePhysicsMaterials(materials); pbPatch.Value = 60; writer.WriteRotationSets(rotationSets); pbPatch.Value = 70; writer.WriteInventoryItemDescriptions(invItemDescs); pbPatch.Value = 80; writer.WriteBlocks(blocks); pbPatch.Value = 85; writer.WriteResources(resources); pbPatch.Value = 90; writer.WriteIcons(icons); pbPatch.Value = 95; writer.WriteShapesets(shapeSets); pbPatch.Value = 100; DialogResult r2 = MessageBox.Show("All " + this.Mods.Count + " mods are now installed. Do you want to start Scrap Mechanic to test it?", "Mods installed", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); if (r2 == System.Windows.Forms.DialogResult.Yes) { Process.Start("steam://rungameid/387990"); } } catch (Exception ex) { MessageBox.Show("An exception occurred while loading the mods.\n\n" + ex.Message + "\n\n" + ex.Source + "\n\n" + ex.StackTrace, "An error occurred", MessageBoxButtons.OK, MessageBoxIcon.Error); Console.WriteLine(ex.Message + " on " + ex.Source); } finally { foreach (ZipFile f in modZipFiles.Values) { f.Dispose(); } btnPatchGame.Enabled = false; } } }
public Dictionary <string, RotationSet> ReadRotationSets() { string rotationSetsJson = File.ReadAllText(Path.Combine(this.GameDir, "Data", "Objects", "Database", "rotationsets.json")); return(RotationSet.ReadFromJson(rotationSetsJson)); }