private void renderToolStripMenuItem_Click(object sender, EventArgs e) { var Node = GetSelectedFile(); if (Node != null) { if (IsValidNode(Node)) { Log.Write("{0}: Rendering {1}.sav", GetType().Name, Node.Text); using (var FS = File.OpenRead(GetName(Node))) { SaveFile F; try { F = SaveFile.Open(FS); } catch (Exception ex) { Log.Write(new Exception("Attempted to render invalid file", ex)); InvalidMessage(); return; } PreviewFile(F); } } else { InvalidMessage(); } } }
public void OpenFile(string SaveFileName) { Log.Write("{0}: Loading {1}", GetType().Name, SaveFileName); try { using (var FS = File.OpenRead(SaveFileName)) { F = SaveFile.Open(FS); if (F == null) { throw new InvalidDataException("We are unable to load your save file. It looks invalid."); } FileName = SaveFileName; HasChange = false; NameChanged = false; if (S.ShowLimited) { S.ShowLimited = false; MessageBox.Show(@"This is still in development and functionality is limited. If something breaks, please open an issue on GitHub so we can fix it.", "Limited Functionality", MessageBoxButtons.OK, MessageBoxIcon.Information); } RedrawMap(); Log.Write("{0}: File loaded. {1} entries total", GetType().Name, F.Entries.Count); } } catch (Exception ex) { Log.Write(new Exception("Unable to load the save file", ex)); Tools.E($"Unable to load the specified file\r\n{ex.Message}", "File read error"); } }
private void renderToolStripMenuItem_Click(object sender, EventArgs e) { var Node = GetSelectedFile(); if (Node != null) { if (IsValidNode(Node)) { Log.Write("{0}: Rendering {1}.sav", GetType().Name, Node.Text); using (var FS = File.OpenRead(GetName(Node))) { SaveFile F; try { F = SaveFile.Open(FS); } catch (Exception ex) { Log.Write(new Exception("Attempted to render invalid file", ex)); InvalidMessage(); return; } using (var BMP = MapRender.RenderFile(F)) { using (var frm = new Form()) { frm.Text = "Preview of " + Node.Text; frm.ShowIcon = frm.ShowInTaskbar = false; frm.WindowState = FormWindowState.Maximized; frm.BackgroundImageLayout = ImageLayout.Zoom; frm.BackgroundImage = BMP; Tools.SetupEscHandler(frm); frm.ShowDialog(); } } } } else { InvalidMessage(); } } }
private void renameToolStripMenuItem_Click(object sender, EventArgs e) { var Node = GetSelectedFile(); if (Node != null) { FileStream FS = null; SaveFile F = null; try { FS = File.OpenRead(GetName(Node)); } catch (Exception ex) { Log.Write(new Exception("Unable to rename the file", ex)); Tools.E($"Unable to rename the file.\r\n{ex.Message}", "File rename"); return; } using (FS) { F = SaveFile.Open(FS); } if (F != null) { using (var Ren = new frmRename(F.SessionName, Node.Text)) { if (Ren.ShowDialog() == DialogResult.OK) { var NewName = Path.Combine(Path.GetDirectoryName(GetName(Node)), Ren.RenameFileName + ".sav"); //Show rename dialog if the file itself is renamed and the destination exists already if ( Node.Text == Ren.RenameFileName || !File.Exists(NewName) || MessageBox.Show($"There is already a file named {Ren.RenameFileName}.sav. Overwrite this file?", "Confirm overwrite", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button2) == DialogResult.Yes) { F.SessionName = Ren.RenameSessionName; try { FS = File.Create(NewName); } catch (Exception ex) { Log.Write(new Exception("Unable to rename the file", ex)); FS = null; Tools.E($"Unable to rename the file.\r\n{ex.Message}", "File rename"); } if (FS != null) { using (FS) { F.Export(FS); try { File.Delete(GetName(Node)); } catch (Exception ex) { Log.Write(new Exception("Unable to delete the old copy", ex)); Tools.E($"File renamed, but we are unable to delete the old copy. Please do so manually.\r\n{ex.Message}", "Unable to rename"); } InitFiles(); } } } } } } else { InvalidMessage(); } } }
private void btnImport_Click(object sender, EventArgs e) { if (OFD.ShowDialog() == DialogResult.OK) { FileStream FS; try { FS = File.OpenRead(OFD.FileName); } catch (Exception ex) { Log.Write(new Exception($"Unable to import {OFD.FileName}", ex)); Tools.E($"Unable to open the selected file.\r\n{ex.Message}", "Import error"); return; } using (FS) { var NewName = Path.GetFileName(OFD.FileName); if (NewName.ToLower().Contains(".sav")) { //Make .sav the last part of the name NewName = NewName.Substring(0, NewName.ToLower().LastIndexOf(".sav")) + ".sav"; if (NewName == ".sav") { NewName = "Import.sav"; } } else { //Add .sav extension because it's not there NewName += ".sav"; } //Make name unique int i = 1; while (File.Exists(Path.Combine(Program.SaveDirectory, NewName))) { NewName = NewName.Substring(0, NewName.Length - 4) + $"_{i++}.sav"; } //Check if selected file is actually a (somewhat) valid save game var F = SaveFile.Open(FS); if (F == null) { NewName = Path.Combine(Program.SaveDirectory, NewName); if (MessageBox.Show("This file doesn't looks like it's a save file. Import anyways?", "Incompatible file", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == DialogResult.Yes) { //User decided to copy anyway. Decompress the file now as needed FS.Seek(0, SeekOrigin.Begin); if (Tools.IsGzFile(FS)) { Log.Write("{0}: looks compressed: {1}", GetType().Name, OFD.FileName); try { using (var GZS = new GZipStream(FS, CompressionMode.Decompress)) { //Try to manually decompress a few bytes before fully copying. //This will throw an exception for files that are not decompressable before the output file is created byte[] Data = new byte[100]; int R = GZS.Read(Data, 0, Data.Length); using (var OUT = File.Create(NewName)) { OUT.Write(Data, 0, R); GZS.CopyTo(OUT); } } InitFiles(); FeatureReport.Used(FeatureReport.Feature.RestoreBackup); } catch (Exception ex) { Log.Write(new Exception($"Unable to decompress {OFD.FileName}", ex)); Tools.E($"File looks compressed, but we are unable to decompress it.\r\n{ex.Message}", "Decompression error"); } } else { Log.Write("{0}: Importing uncompressed {1}", GetType().Name, OFD.FileName); //File is not compressed. Just copy as-is using (var OUT = File.Create(NewName)) { FS.CopyTo(OUT); InitFiles(); } } } } else { //Supply the NewName path to have a name that's not a conflict by default using (var Ren = new frmRename(F.SessionName, Path.GetFileNameWithoutExtension(NewName))) { if (Ren.ShowDialog() == DialogResult.OK) { NewName = Path.Combine(Program.SaveDirectory, Ren.RenameFileName + ".sav"); if (!File.Exists(NewName) || MessageBox.Show($"There is already a file named {Ren.RenameFileName}.sav. Overwrite this file?", "Confirm overwrite", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button2) == DialogResult.Yes) { F.SessionName = Ren.RenameSessionName; using (var OUT = File.Create(NewName)) { F.Export(OUT); } InitFiles(); } else { Log.Write("{0}: import destination exists: User cancelled", GetType().Name); } } } } } } }
private void InitFiles() { tvFiles.Nodes.Clear(); var Sessions = new Dictionary <string, TreeNode>(); Thread T = new Thread(delegate() { foreach (var FileName in Directory.GetFiles(Program.SaveDirectory, "*.sav", SearchOption.AllDirectories)) { if (Disposing || IsDisposed) { return; } SaveFile F = null; try { using (var FS = File.OpenRead(FileName)) { F = SaveFile.Open(FS); } } catch (Exception ex) { Log.Write(new Exception($"Invalid or locked save file: {FileName}", ex)); } if (F != null) { //File is valid game file TreeNode Node = null; var SessionName = string.IsNullOrWhiteSpace(F.SessionName) ? "<no name>" : F.SessionName; if (Sessions.ContainsKey(SessionName)) { Node = Sessions[SessionName]; } else { Invoke((MethodInvoker) delegate() { Node = tvFiles.Nodes.Add(SessionName); Sessions.Add(SessionName, Node); }); } Invoke((MethodInvoker) delegate() { var AddedNode = Node.Nodes.Add(Path.GetFileNameWithoutExtension(FileName)); AddedNode.Tag = new LocalFileView(FileName, true); Node.ExpandAll(); }); } else { //File is invalid game file TreeNode InvalidNode = null; if (Sessions.ContainsKey("")) { InvalidNode = Sessions[""]; } else { Sessions[""] = InvalidNode = new TreeNode("INVALID"); InvalidNode.ForeColor = Color.Red; InvalidNode.BackColor = Color.Yellow; } var Invalid = InvalidNode.Nodes.Add(Path.GetFileNameWithoutExtension(FileName)); Invalid.ForeColor = Color.Red; Invalid.Tag = new LocalFileView(FileName, false); } } //Add invalid nodes on the bottom if (Sessions.ContainsKey("")) { Log.Write("{0}: At least one invalid save file was found", GetType().Name); Invoke((MethodInvoker) delegate() { if (Sessions[""].TreeView == null) { tvFiles.Nodes.Add(Sessions[""]); Sessions[""].ExpandAll(); } }); } }); T.Start(); }
private static void Test() { const string WHITELIST = "/Buildable/|ResourceNode|ResourceDeposit"; const string PROTECTED = "MamIntegrated|HubTerminal|WorkBenchIntegrated|StorageIntegrated|GeneratorIntegratedBiomass"; var WLItems = WHITELIST.Split('|'); var ProtectedItems = PROTECTED.Split('|'); var ZeroPos = new Vector3(); //Note to testers: Do not close the stream early. This protects you from overwriting the save file. using (var FS = File.OpenRead(Path.Combine(SaveDirectory, "Reset.sav"))) { var F = SaveFile.Open(FS); var R = new Random(); foreach (var E in F.Entries) { if (E.EntryType == ObjectTypes.OBJECT_TYPE.OBJECT) { var o = (ObjectTypes.GameObject)E.ObjectData; if ( !o.ObjectPosition.Equals(ZeroPos) && WLItems.Any(n => o.Name.Contains(n)) && !ProtectedItems.Any(n => o.Name.Contains(n))) { var newPos = Tools.TranslateToMap(new System.Drawing.PointF((float)R.NextDouble(), (float)R.NextDouble())); newPos.Z = 18000; o.ObjectPosition = newPos; } } } //Show all entries Console.Error.WriteLine(string.Join("\r\n", F.Entries.OrderBy(m => m.Properties.Length).Select(m => m.ObjectData.Name).Distinct())); /* Test code to "align" things * var o = (ObjectTypes.GameObject)e.ObjectData; * o.ObjectPosition.X = (int)o.ObjectPosition.X; * o.ObjectPosition.Y = (int)o.ObjectPosition.Y; * o.ObjectPosition.Z = (int)o.ObjectPosition.Z; * * o.ObjectPosition.X -= o.ObjectPosition.X % 10; * o.ObjectPosition.Y -= o.ObjectPosition.Y % 10; * o.ObjectPosition.Z -= o.ObjectPosition.Z % 10; * //o.ObjectRotation.W = o.ObjectRotation.X = o.ObjectRotation.Y = o.ObjectRotation.Z = 0; * //Console.Error.WriteLine("{0} {1}", o.ObjectPosition, o.ObjectRotation); * //*/ /* * //Duplicator test * var BaseF = (SaveFileEntry)F.Entries.First(m => m.ObjectData.Name == "/Game/FactoryGame/Buildable/Building/Foundation/Build_Foundation_8x2_01.Build_Foundation_8x2_01_C").Clone(); * var BaseObject = (ObjectTypes.GameObject)BaseF.ObjectData; * var BaseName = BaseObject.InternalName; * BaseName = BaseName.Substring(0, BaseName.Length - 1); * BaseObject.ObjectPosition.X = -324000; * BaseObject.ObjectPosition.Y = -374000; * BaseObject.ObjectPosition.Z = 18000; * * int ctr = F.Entries.Count(m => m.ObjectData.Name == "/Game/FactoryGame/Buildable/Building/Foundation/Build_Foundation_8x2_01.Build_Foundation_8x2_01_C"); * for (var x = 0; x < 940; x += 5) * { * for (var y = 0; y < 940; y += 5) * { * var Copy = (SaveFileEntry)BaseF.Clone(); * var o = (ObjectTypes.GameObject)Copy.ObjectData; * o.InternalName = BaseName + (ctr++).ToString(); * o.ObjectPosition.X += x * 800; * o.ObjectPosition.Y += y * 800; * F.Entries.Add(Copy); * } * Console.Error.WriteLine(x); * } * Console.Error.WriteLine("Placed {0} foundations", ctr); * //*/ using (var FSOut = File.Create(Path.Combine(Environment.ExpandEnvironmentVariables(SAVEDIR), "Test-Edited.sav"))) { F.Export(FSOut); } } }
private static int HandleArguments(string[] args) { var Ops = new List <Modes>(); string Filename = null; string SessionName = null; if (args.Contains("/?")) { Help(); return(RET.HELP); } for (var i = 0; i < args.Length; i++) { var arg = args[i]; Log.Write("{0}: processing argument: {1}", nameof(HandleArguments), arg); switch (arg.ToLower()) { case "/verify": Ops.Add(Modes.Verify); break; case "/list": Ops.Add(Modes.List); break; case "/pack": Ops.Add(Modes.Pack); break; case "/render": Ops.Add(Modes.Render); break; case "/info": Ops.Add(Modes.Info); break; case "/rename": Ops.Add(Modes.RenameSession); if (i < args.Length - 1) { SessionName = args[++i]; } else { Log.Write("{0}: /rename requires a new session name", nameof(HandleArguments)); Console.WriteLine("/rename requires a new session name"); return(RET.ARG); } break; default: if (string.IsNullOrEmpty(Filename)) { Filename = arg; } else { Log.Write("{0}: unknown argument: {1}", nameof(HandleArguments), arg); Console.WriteLine("Unknown argument: {0}", arg); return(RET.ARG); } break; } } if (Ops.Count == 0 && args.Length < 2) { return(RET.UI); } Tools.AllocConsole(); if (string.IsNullOrEmpty(Filename)) { Log.Write("{0}: No file name argument supplied", nameof(HandleArguments)); Console.WriteLine("No file name argument supplied"); return(RET.ARG); } Ops = Ops.Distinct().ToList(); var Changes = false; var IsGz = false; SaveFile SF = null; FileStream FS = null; try { FS = File.Open(Filename, FileMode.Open, FileAccess.ReadWrite, FileShare.Read); } catch (Exception ex) { Console.WriteLine("Unable to open input file"); Log.Write("{0}: Unable to open input file", nameof(HandleArguments)); Log.Write(ex); return(RET.IO); } using (FS) { IsGz = Tools.IsGzFile(FS); try { SF = SaveFile.Open(FS); if (SF == null) { throw new InvalidDataException("The file is not a valid satisfactory save game"); } } catch (Exception ex) { Console.WriteLine("Invalid game file"); Log.Write("{0}: Invalid game file", nameof(HandleArguments)); Log.Write(ex); return(RET.IO); } if (Ops.Contains(Modes.Verify)) { Log.Write("{0}: Verifying file", nameof(HandleArguments)); //Verify does nothing on its own Check(SF.PlayTime.Ticks >= 0, "Positive play time"); Check(Tools.IsMatch(SF.SessionName, @"^[\w\.\- ]{1,31}$"), "Non-Problematic Session Name"); Check(SF.LevelType == SaveFile.DEFAULT_LEVEL_TYPE, $"Level type is '{SaveFile.DEFAULT_LEVEL_TYPE}'"); Check(SF.SaveDate.ToUniversalTime() < DateTime.UtcNow, "Date in past"); foreach (var key in "sessionName Visibility startloc".Split(' ')) { Check(SF.Properties.ContainsKey(key), $"Header contains field '{key}'"); } //Don't double error with the previous one if (SF.Properties.ContainsKey("sessionName")) { Check(SF.Properties["sessionName"] == SF.SessionName, "Both session names match"); } else { Console.WriteLine("[SKIP]: Both session names match"); } } if (Ops.Contains(Modes.List)) { Log.Write("{0}: Printing item list", nameof(HandleArguments)); foreach (var e in SF.Entries.GroupBy(m => m.ObjectData.Name).OrderBy(m => m.Key)) { Console.WriteLine("{1}\t{0}", e.Key, e.Count()); } } if (Ops.Contains(Modes.Render)) { var ImgFile = Path.ChangeExtension(Filename, ".png"); Log.Write("{0}: Rendering map as original size to {1}", nameof(HandleArguments), ImgFile); Console.WriteLine("Initializing image..."); MapRender.Init(-1, -1); Console.WriteLine("Rendering file..."); using (var IMG = MapRender.RenderFile(SF, 8.192)) { Console.WriteLine("Saving image..."); IMG.Save(ImgFile); } } if (Ops.Contains(Modes.Info)) { Log.Write("{0}: Showing game info", nameof(HandleArguments)); Console.WriteLine("Save File Size:\t{0}", FS.Position); Console.WriteLine("Build Version:\t{0}", SF.BuildVersion); Console.WriteLine("Save Version:\t{0}", SF.SaveVersion); Console.WriteLine("Header Version:\t{0}", SF.SaveHeaderVersion); Console.WriteLine("Session Name:\t{0}", SF.SessionName); Console.WriteLine("Save Date:\t{0}", SF.SaveDate); Console.WriteLine("Play Time:\t{0}", SF.PlayTime); foreach (var KV in SF.Properties) { Console.WriteLine("{0}:\t{1}", KV.Key, KV.Value); } Console.WriteLine("Object Count:\t{0}", SF.Entries.Count); Console.WriteLine("String List:\t{0}", SF.StringList.Count); } if (Ops.Contains(Modes.RenameSession)) { Log.Write("{0}: Renaming session to {1}", nameof(HandleArguments), SessionName); SF.SetSessionName(SessionName); Changes = true; } if (Ops.Contains(Modes.Pack)) { string NewFile; FileStream OUT; FS.Seek(0, SeekOrigin.Begin); if (IsGz) { if (Filename.ToLower().EndsWith(".sav.gz")) { NewFile = Path.ChangeExtension(Filename, null); } else { NewFile = Path.ChangeExtension(Filename, ".sav"); } } else { NewFile = Filename + ".gz"; } try { OUT = File.Create(NewFile); } catch (Exception ex) { Console.WriteLine("Can't create output file"); Log.Write("{0}: Can't create {1}", nameof(HandleArguments), NewFile); Log.Write(ex); return(RET.IO); } Log.Write("{0}: {1} file", nameof(HandleArguments), IsGz ? "Compressing" : "Decompressing"); using (OUT) { if (IsGz) { Compression.DecompressStream(FS, OUT); } else { Compression.CompressStream(FS, OUT); } } Log.Write("{0}: {1} file OK", nameof(HandleArguments), IsGz ? "Compressing" : "Decompressing"); } if (Changes) { Log.Write("{0}: Writing back changes", nameof(HandleArguments)); FS.Seek(0, SeekOrigin.Begin); SF.Export(FS); FS.Flush(); FS.SetLength(FS.Position); } } return(RET.SUCCESS); }