├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── SatisfactorySaveEditor.sln
└── SatisfactorySaveEditor
├── App.config
├── Audio
├── WAVHeader.cs
└── WaveFinder.cs
├── Changelog
├── v0.1.txt
├── v0.2.txt
├── v1.2.txt
├── v1.3.txt
├── v1.4.txt
├── v1.5.txt
├── v1.6.txt
├── v1.7.txt
├── v1.8.txt
├── v1.9.txt
├── v2.0.txt
├── v2.1.txt
├── v2.2.txt
├── v2.3.txt
├── v3.0.txt
├── v3.1.txt
└── v3.2.txt
├── Compression.cs
├── ErrorHandler
├── ErrorHandler.cs
├── frmErrorHandler.cs
├── frmErrorHandler.designer.cs
└── frmErrorHandler.resx
├── FeatureReport.cs
├── Help
├── Welcome.txt
├── _no.txt
├── frmApiRegister.txt
├── frmAudioExtract.txt
├── frmChangeLog.txt
├── frmCloudEdit.txt
├── frmCounter.txt
├── frmDeleter.txt
├── frmDuplicator.txt
├── frmElementList.txt
├── frmExport.txt
├── frmHeaderEditor.txt
├── frmHelp.txt
├── frmMain.txt
├── frmManager.txt
├── frmRegionDeleter.txt
├── frmRename.txt
└── frmSettings.txt
├── Images
├── Height.png
├── Icons
│ ├── audio.ico
│ ├── copy.ico
│ ├── delete.ico
│ ├── edit.ico
│ ├── help.ico
│ ├── network.ico
│ ├── rename.ico
│ ├── search.ico
│ └── settings.ico
├── Map.png
└── MapRender.cs
├── Log.cs
├── Program.cs
├── Properties
└── AssemblyInfo.cs
├── PropertyValues
├── ArrayProperty.cs
├── IPropertyValue.cs
├── IntProperty.cs
├── Inventory.cs
├── InventoryItem.cs
├── NoneProperty.cs
├── Property.cs
└── StructProperty.cs
├── QuickPlay.cs
├── SMR
├── API.cs
├── HTTP.cs
├── Responses.cs
└── Tools.cs
├── SatisfactorySaveEditor.csproj
├── SaveFileComponents
├── CompressedHeader.cs
├── ObjectTypes
│ ├── GameBaseObject.cs
│ ├── GameObject.cs
│ ├── GameScript.cs
│ └── Types.cs
├── PropertyString.cs
├── SaveFile.cs
├── SaveFileEntry.cs
├── SaveFileHelper.cs
├── Vector3.cs
└── Vector4.cs
├── Settings.cs
├── ShortName.cs
├── Tools.cs
├── UI
├── frmApiRegister.Designer.cs
├── frmApiRegister.cs
├── frmApiRegister.resx
├── frmAudioExtract.Designer.cs
├── frmAudioExtract.cs
├── frmAudioExtract.resx
├── frmChangeLog.Designer.cs
├── frmChangeLog.cs
├── frmChangeLog.resx
├── frmCloudEdit.Designer.cs
├── frmCloudEdit.cs
├── frmCloudEdit.resx
├── frmCounter.Designer.cs
├── frmCounter.cs
├── frmCounter.resx
├── frmDeleter.Designer.cs
├── frmDeleter.cs
├── frmDeleter.resx
├── frmDuplicator.Designer.cs
├── frmDuplicator.cs
├── frmDuplicator.resx
├── frmElementList.Designer.cs
├── frmElementList.cs
├── frmElementList.resx
├── frmExport.Designer.cs
├── frmExport.cs
├── frmExport.resx
├── frmHeaderEditor.Designer.cs
├── frmHeaderEditor.cs
├── frmHeaderEditor.resx
├── frmHelp.Designer.cs
├── frmHelp.cs
├── frmHelp.resx
├── frmMain.Designer.cs
├── frmMain.cs
├── frmMain.resx
├── frmManager.Designer.cs
├── frmManager.cs
├── frmManager.resx
├── frmRegionDeleter.Designer.cs
├── frmRegionDeleter.cs
├── frmRegionDeleter.resx
├── frmRename.Designer.cs
├── frmRename.cs
├── frmRename.resx
├── frmSettings.Designer.cs
├── frmSettings.cs
└── frmSettings.resx
├── UpdateHandler.cs
└── packages.config
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | [Xx]64/
19 | [Xx]86/
20 | [Bb]uild/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 |
85 | # Visual Studio profiler
86 | *.psess
87 | *.vsp
88 | *.vspx
89 | *.sap
90 |
91 | # TFS 2012 Local Workspace
92 | $tf/
93 |
94 | # Guidance Automation Toolkit
95 | *.gpState
96 |
97 | # ReSharper is a .NET coding add-in
98 | _ReSharper*/
99 | *.[Rr]e[Ss]harper
100 | *.DotSettings.user
101 |
102 | # JustCode is a .NET coding add-in
103 | .JustCode
104 |
105 | # TeamCity is a build add-in
106 | _TeamCity*
107 |
108 | # DotCover is a Code Coverage Tool
109 | *.dotCover
110 |
111 | # NCrunch
112 | _NCrunch_*
113 | .*crunch*.local.xml
114 | nCrunchTemp_*
115 |
116 | # MightyMoose
117 | *.mm.*
118 | AutoTest.Net/
119 |
120 | # Web workbench (sass)
121 | .sass-cache/
122 |
123 | # Installshield output folder
124 | [Ee]xpress/
125 |
126 | # DocProject is a documentation generator add-in
127 | DocProject/buildhelp/
128 | DocProject/Help/*.HxT
129 | DocProject/Help/*.HxC
130 | DocProject/Help/*.hhc
131 | DocProject/Help/*.hhk
132 | DocProject/Help/*.hhp
133 | DocProject/Help/Html2
134 | DocProject/Help/html
135 |
136 | # Click-Once directory
137 | publish/
138 |
139 | # Publish Web Output
140 | *.[Pp]ublish.xml
141 | *.azurePubxml
142 |
143 | # TODO: Un-comment the next line if you do not want to checkin
144 | # your web deploy settings because they may include unencrypted
145 | # passwords
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # NuGet Packages
150 | *.nupkg
151 | # The packages folder can be ignored because of Package Restore
152 | **/packages/*
153 | # except build/, which is used as an MSBuild target.
154 | !**/packages/build/
155 | # Uncomment if necessary however generally it will be regenerated when needed
156 | #!**/packages/repositories.config
157 | # NuGet v3's project.json files produces more ignoreable files
158 | *.nuget.props
159 | *.nuget.targets
160 |
161 | # Microsoft Azure Build Output
162 | csx/
163 | *.build.csdef
164 |
165 | # Microsoft Azure Emulator
166 | ecf/
167 | rcf/
168 |
169 | # Windows Store app package directory
170 | AppPackages/
171 | BundleArtifacts/
172 |
173 | # Visual Studio cache files
174 | # files ending in .cache can be ignored
175 | *.[Cc]ache
176 | # but keep track of directories ending in .cache
177 | !*.[Cc]ache/
178 |
179 | # Others
180 | ClientBin/
181 | [Ss]tyle[Cc]op.*
182 | ~$*
183 | *~
184 | *.dbmdl
185 | *.dbproj.schemaview
186 | *.pfx
187 | *.publishsettings
188 | node_modules/
189 | orleans.codegen.cs
190 |
191 | # RIA/Silverlight projects
192 | Generated_Code/
193 |
194 | # Backup & report files from converting an old project file
195 | # to a newer Visual Studio version. Backup files are not needed,
196 | # because we have git ;-)
197 | _UpgradeReport_Files/
198 | Backup*/
199 | UpgradeLog*.XML
200 | UpgradeLog*.htm
201 |
202 | # SQL Server files
203 | *.mdf
204 | *.ldf
205 |
206 | # Business Intelligence projects
207 | *.rdl.data
208 | *.bim.layout
209 | *.bim_*.settings
210 |
211 | # Microsoft Fakes
212 | FakesAssemblies/
213 |
214 | # GhostDoc plugin setting file
215 | *.GhostDoc.xml
216 |
217 | # Node.js Tools for Visual Studio
218 | .ntvs_analysis.dat
219 |
220 | # Visual Studio 6 build log
221 | *.plg
222 |
223 | # Visual Studio 6 workspace options file
224 | *.opt
225 |
226 | # Visual Studio LightSwitch build output
227 | **/*.HTMLClient/GeneratedArtifacts
228 | **/*.DesktopClient/GeneratedArtifacts
229 | **/*.DesktopClient/ModelManifest.xml
230 | **/*.Server/GeneratedArtifacts
231 | **/*.Server/ModelManifest.xml
232 | _Pvt_Extensions
233 |
234 | # LightSwitch generated files
235 | GeneratedArtifacts/
236 | ModelManifest.xml
237 |
238 | # Paket dependency manager
239 | .paket/paket.exe
240 |
241 | # FAKE - F# Make
242 | .fake/
243 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 AyrA
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SatisfactorySaveEditor", "SatisfactorySaveEditor\SatisfactorySaveEditor.csproj", "{5A95AFC0-2278-42AB-B18F-BEE58E642337}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {5A95AFC0-2278-42AB-B18F-BEE58E642337}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {5A95AFC0-2278-42AB-B18F-BEE58E642337}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {5A95AFC0-2278-42AB-B18F-BEE58E642337}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {5A95AFC0-2278-42AB-B18F-BEE58E642337}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Audio/WAVHeader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 |
5 | namespace SatisfactorySaveEditor.Audio
6 | {
7 | public class WAVHeader
8 | {
9 | public uint RIFFSize
10 | { get; set; }
11 | public ushort AudioFormat
12 | { get; set; }
13 | public ushort ChannelCount
14 | { get; set; }
15 | public uint SampleRate
16 | { get; set; }
17 | public uint ByteRate
18 | { get; set; }
19 | public ushort BlockAlign
20 | { get; set; }
21 | public ushort BitsPerSample
22 | { get; set; }
23 | public uint DataSize
24 | { get; set; }
25 | public long DataOffset
26 | { get; set; }
27 |
28 | public TimeSpan Duration
29 | {
30 | get
31 | {
32 | if (ChannelCount == 0 || BitsPerSample == 0)
33 | {
34 | return TimeSpan.FromSeconds(0);
35 | }
36 | var BytesPerSecond = SampleRate * BitsPerSample * ChannelCount / 8;
37 | return TimeSpan.FromSeconds(DataSize / BytesPerSecond);
38 | }
39 | }
40 |
41 | public WAVHeader(Stream Input)
42 | {
43 | var DE = Encoding.Default;
44 | using (var BR = new BinaryReader(Input, DE, true))
45 | {
46 | if (DE.GetString(BR.ReadBytes(4)) != "RIFF")
47 | {
48 | throw new InvalidDataException("Wave file must start with 'RIFF'");
49 | }
50 | RIFFSize = BR.ReadUInt32();
51 | if (DE.GetString(BR.ReadBytes(4)) != "WAVE")
52 | {
53 | throw new InvalidDataException("'WAVE' chunk expected but not there");
54 | }
55 | if (DE.GetString(BR.ReadBytes(4)) != "fmt ")
56 | {
57 | throw new InvalidDataException("Format chunk expected but not there");
58 | }
59 | var Chunksize = BR.ReadUInt32();
60 | AudioFormat = BR.ReadUInt16();
61 | ChannelCount = BR.ReadUInt16();
62 | SampleRate = BR.ReadUInt32();
63 | ByteRate = BR.ReadUInt32();
64 | BlockAlign = BR.ReadUInt16();
65 | BitsPerSample = BR.ReadUInt16();
66 | //Discard rest of chunk
67 | BR.ReadBytes((int)(Chunksize - 16));
68 | while (DE.GetString(BR.ReadBytes(4)) != "data")
69 | {
70 | var Length = BR.ReadUInt32();
71 | BR.ReadBytes((int)Length);
72 | }
73 | DataSize = BR.ReadUInt32();
74 | DataOffset = Input.Position;
75 | }
76 | }
77 |
78 | public void Write(Stream Output)
79 | {
80 | var DE = Encoding.Default;
81 | using (var BW = new BinaryWriter(Output, DE, true))
82 | {
83 | BW.Write(DE.GetBytes("RIFF"));
84 | BW.Write((uint)(DataOffset + DataSize));
85 | BW.Write(DE.GetBytes("WAVEfmt "));
86 | BW.Write(16);
87 | BW.Write(AudioFormat);
88 | BW.Write(ChannelCount);
89 | BW.Write(SampleRate);
90 | BW.Write(ByteRate);
91 | BW.Write(BlockAlign);
92 | BW.Write(BitsPerSample);
93 | BW.Write(DE.GetBytes("data"));
94 | BW.Write(DataSize);
95 | BW.Flush();
96 | }
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Audio/WaveFinder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading;
7 |
8 | namespace SatisfactorySaveEditor.Audio
9 | {
10 | public static class WaveFinder
11 | {
12 | private static Thread T;
13 | private static volatile bool Cont;
14 | private static List Files;
15 |
16 | public static WaveFileInfo[] WaveFiles
17 | {
18 | get
19 | {
20 | return Files == null ? null : Files.ToArray();
21 | }
22 | }
23 |
24 | public static bool IsScanning
25 | {
26 | get
27 | {
28 | return T != null && T.IsAlive;
29 | }
30 | }
31 |
32 | public static void StopSearch()
33 | {
34 | if (T != null)
35 | {
36 | Cont = false;
37 | T.Join();
38 | }
39 | }
40 |
41 | public static void FindAudio(Stream Input)
42 | {
43 | if (T != null)
44 | {
45 | throw new InvalidOperationException("Can't start a secondary scan process");
46 | }
47 | if (Input == null)
48 | {
49 | throw new ArgumentNullException(nameof(Input));
50 | }
51 | if (!Input.CanSeek)
52 | {
53 | throw new ArgumentException("Stream must be seekable", nameof(Input));
54 | }
55 | T = new Thread(Scanner);
56 | T.Priority = ThreadPriority.BelowNormal;
57 | T.IsBackground = true;
58 | T.Start(Input);
59 | }
60 |
61 | private static void Scanner(object o)
62 | {
63 | var S = (Stream)o;
64 | Cont = true;
65 | Files = new List();
66 | var buffer = new byte[100 * 1000 * 1000];
67 | var SEQ = Encoding.Default.GetBytes("WAVEfmt ");
68 | #if DEBUG
69 | //For debug only.
70 | //Right now the audio files seem to be towards the end of the file.
71 | //To speed up debugging, we skip the first half of the file.
72 | S.Seek(S.Length / 2, SeekOrigin.Begin);
73 | #endif
74 | while (Cont)
75 | {
76 | if (S.Position > 0)
77 | {
78 | S.Seek(-SEQ.Length, SeekOrigin.Current);
79 | }
80 | var StartPos = S.Position;
81 | int count = S.Read(buffer, 0, buffer.Length);
82 | Cont = count == buffer.Length;
83 | for (var i = 0; i < count - SEQ.Length; i++)
84 | {
85 | //Don't bother doing anything if the first character is no match
86 | if (SEQ[0] == buffer[i])
87 | {
88 | for (var j = 0; j < SEQ.Length; j++)
89 | {
90 | if (SEQ[j] != buffer[i + j])
91 | {
92 | break;
93 | }
94 | else if (j == SEQ.Length - 1)
95 | {
96 | Log.Write("WaveFinder: Found audio at {0}", S.Position - count + i);
97 | var LastPos = S.Position;
98 | //Found wave chunk, go to start of it
99 | S.Seek(StartPos + i - 8, SeekOrigin.Begin);
100 | var Info = new WaveFileInfo()
101 | {
102 | FilePosition = StartPos + i - 8,
103 | Header = new WAVHeader(S)
104 | };
105 | //This is always 1 for some reason in Satisfactory
106 | Log.Write("WaveFinder: Audio channel count: {0}", Info.Header.ChannelCount);
107 | //Info.Header.ChannelCount = 2;
108 |
109 | S.Seek(Info.Header.DataOffset, SeekOrigin.Begin);
110 | if (IsOgg(S))
111 | {
112 | Info.Type = WaveFileType.OGG;
113 | }
114 | else if (Info.Header.SampleRate == 44100 && Info.Header.BitsPerSample == 16)
115 | {
116 | Info.Type = WaveFileType.PCM;
117 | }
118 | else
119 | {
120 | Info.Type = WaveFileType.Invalid;
121 | }
122 | //Remove overlapping section
123 | //This means the Wave file was inside another wave file
124 | if (Overlaps(Info, Files.LastOrDefault()))
125 | {
126 | Log.Write("WaveFinder: Removed overlapping WAV segment at NEW={0}; OLD={1}", Info.FilePosition, Files.Last().FilePosition);
127 | Files.RemoveAt(Files.Count - 1);
128 | }
129 | Files.Add(Info);
130 | //Restore stream position
131 | S.Seek(LastPos, SeekOrigin.Begin);
132 | }
133 | }
134 | }
135 | }
136 | }
137 | T = null;
138 | }
139 |
140 | private static bool Overlaps(WaveFileInfo A, WaveFileInfo B)
141 | {
142 | if (A == null || B == null)
143 | {
144 | return false;
145 | }
146 | //End of A
147 | var AE = A.FilePosition + A.Header.DataSize + (A.Header.DataOffset - A.FilePosition);
148 | //End of B
149 | var BE = B.FilePosition + B.Header.DataSize + (B.Header.DataOffset - B.FilePosition);
150 | //A is entirely before B or B is entirely before A
151 | if (AE <= B.FilePosition || BE <= A.FilePosition)
152 | {
153 | return false;
154 | }
155 |
156 | //A starts inside B or B starts inside A
157 | if ((A.FilePosition > B.FilePosition && A.FilePosition < BE) || (B.FilePosition > A.FilePosition && B.FilePosition < AE))
158 | {
159 | return true;
160 | }
161 |
162 | //A ends inside B or B ends inside A
163 | if ((AE > B.FilePosition && AE < BE) || (BE > A.FilePosition && BE < AE))
164 | {
165 | return true;
166 | }
167 |
168 | throw new NotImplementedException("");
169 | }
170 |
171 | private static bool IsOgg(Stream S)
172 | {
173 | byte[] Data = new byte[4];
174 | S.Seek(-S.Read(Data, 0, Data.Length), SeekOrigin.Current);
175 | return Data[0] == 'O' && Data[1] == 'g' && Data[2] == 'g' && Data[3] == 'S';
176 | }
177 | }
178 |
179 | public class WaveFileInfo
180 | {
181 | public long FilePosition;
182 | public WaveFileType Type;
183 | public WAVHeader Header;
184 | }
185 |
186 | public enum WaveFileType
187 | {
188 | PCM,
189 | OGG,
190 | Invalid
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Changelog/v0.1.txt:
--------------------------------------------------------------------------------
1 | Version 0.1
2 |
3 | Released: 2019-04-21 23:10:11 UTC
4 |
5 | First public release of the editor containing a few quick action commands.
6 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Changelog/v0.2.txt:
--------------------------------------------------------------------------------
1 | Version 0.2
2 |
3 | Released: 2019-05-18 19:10:41 UTC
4 |
5 | - There's now a resize option for your lizard doggos
6 | - You can now remove friendly and hostile mobs
7 | - You can restore the slugs
8 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Changelog/v1.2.txt:
--------------------------------------------------------------------------------
1 | Version 1.2
2 |
3 | Released: 2019-06-12 18:55:08 UTC
4 |
5 | You can now duplicate and delete entries from the file.
6 | Be aware that it will not automatically update related/dependent entries in the file.
7 | For example deleting a container this way will not get rid of the inventory entry of said container.
8 |
9 | The UI should also work consistently now.
10 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Changelog/v1.3.txt:
--------------------------------------------------------------------------------
1 | Version 1.3
2 |
3 | Released: 2019-06-30 11:55:18 UTC
4 |
5 | UI overhaul
6 |
7 | The UI has been changed to add new features.
8 |
9 | Shortcuts
10 |
11 | The File menu now has commonly accepted shortcuts.
12 | For example CTRL+O to open or F5 to refresh the map.
13 |
14 | Settings
15 |
16 | A settings class has been added to remember settings.
17 | There are no user changeable settings as of now,
18 | but it already remembers which alert boxes it has shown to you.
19 |
20 | Short names
21 |
22 | Much shorter names are now displayed in drop down fields.
23 | The ordering is still alphabetical
24 |
25 | Map
26 |
27 | There is now a map in the main window that renders a crude list of items on your map.
28 | It shows nodes, natural items and player generated items as well as the player position(s).
29 |
30 | The map is automatically exported to the save file location each time you reload it using [F5] or the menu.
31 |
32 | Note: I don't know the actual ingame map coordinates exactly.
33 | The map you will see is only an approximation of the real position but close enough to give you a general overview
34 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Changelog/v1.4.txt:
--------------------------------------------------------------------------------
1 | Version 1.4
2 |
3 | Released: 2019-07-06 02:36:30 UTC
4 |
5 | Export/Import
6 |
7 | Allows you to copy entries from one save file to another.
8 | This is facilitated via XML code that is placed in the clipboard.
9 | The code can also be copied from or pasted into any text editor.
10 | This allows you to save individual objects to disk.
11 |
12 | Map item finder
13 |
14 | Item select boxes now have an M button next to them to locate the item(s) in the main window map.
15 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Changelog/v1.5.txt:
--------------------------------------------------------------------------------
1 | Version 1.5
2 |
3 | Released: 2019-07-20 00:04:00 UTC
4 |
5 | New Fature: Save File Manager
6 |
7 | The save file manager allows you to easily manage all your satisfactory save files:
8 |
9 | - Show all files in a tree structure, sorted by session name
10 | - Open file by double clicking or [ENTER] key
11 | - Rename file without opening it in the editor
12 | - Delete unwanted saves
13 | - Rendering map preview of a save file
14 | - Duplicator to easily experiment with files
15 | - Backup and import save files. This supports compressed saves
16 |
17 | The save file manager shows corrupted save files in a different color.
18 | Most options are unavailable for those.
19 |
20 | Compression
21 |
22 | Save files are compressed using gzip.
23 | You can use tools like 7-zip to open a compressed save file and decompress it.
24 | Compression saves 90% of space on average due to the high verbosity of the save file.
25 |
26 | Note: You can't directly load compressed save files into satisfactory.
27 | Import using the button in the save file manager or decompress manually.
28 | The save file importer automatically detects if decompression is necessary or not.
29 |
30 | New Feature: Clear String List
31 |
32 | The "Clear String List" option removes the list of items considered removed from the map.
33 | This is useful if you used the deleter form to reset map features, for example the poison plants.
34 |
35 | New Feature: Help
36 |
37 | Pressing [F1] at any time opens a help window with relevant information to the current location you are at.
38 | You can also open the main window help using the "Help" menu option
39 |
40 | New Feature: Updater
41 |
42 | You can now update the client.
43 | This is a real updater.
44 | It will not just open a website or only download a file but actually updates your version.
45 | You can either update manually through the red main menu item that is shown when an update is available,
46 | or you can wait and the application automatically updates during the next launch.
47 |
48 | Map image
49 |
50 | - Foundations are now grey
51 | - Walkways are now white
52 |
53 | Bug fixes
54 |
55 | - Only editing the header is now detected as a change
56 | - Removing the last item in the deleter list no longer crashes
57 | - Saves with empty session names are now supported
58 |
59 | Other changes
60 |
61 | Project structure now in order
62 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Changelog/v1.6.txt:
--------------------------------------------------------------------------------
1 | Version 1.6
2 |
3 | Released: 2019-07-21 14:50:26 UTC
4 |
5 | New Feature: Region based deleter
6 |
7 | The region deleter allows you to remove entries based on map location.
8 |
9 | Usage is quite simple:
10 |
11 | Load a save file and open the region deleter (Menu: General Actions).
12 | Repeatedly click on the map to set coordinates of a polygon.
13 | The polygon is rendered after the 3rd click.
14 |
15 | After you marked the region you want to clear, use the [DEL] key or the menu.
16 | This will show a list of all objects about to be removed and those being ignored.
17 | Using the [DEL] key you can move selected entries between the two lists.
18 |
19 | Other changes
20 |
21 | - Forms can now be closed using the [ESC] key
22 | - Updater no longer fires in debug mode.
23 | - Reorganizing "General Actions" menu to combine the two delete options.
24 | - Added icons
25 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Changelog/v1.7.txt:
--------------------------------------------------------------------------------
1 | Version 1.7
2 |
3 | Released: 2019-07-23 18:50:54 UTC
4 |
5 | This version adds a logger that makes debugging issues easier.
6 | By default the file is placed in the directory the executable is in and it's named lastrun.log
7 |
8 | It's overwritten each time you run the application again.
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Changelog/v1.8.txt:
--------------------------------------------------------------------------------
1 | Version 1.8
2 |
3 | Released: 2019-07-24 02:20:15 UTC
4 |
5 | This update fixes the error handling:
6 |
7 | - Proper message is now written to the log files
8 | - Better error message is generated for invalid save files.
9 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Changelog/v1.9.txt:
--------------------------------------------------------------------------------
1 | Version 1.9
2 |
3 | Released: 2019-07-27 19:36:57 UTC
4 |
5 | The primary function of this update is to allow the user to automatically report errors.
6 | If the user wants to, it uploads the log,
7 | error message and current save file to our server for later analysis.
8 |
9 | More Features:
10 |
11 | - Windows that pop up are now placed more predictable
12 | - Some dialog windows were enlarged to avoid clipping of controls in Windows 8 and later.
13 | - Object counter. Shows types and numbers of all things in your save file.
14 | - Protect hub from being deleted by default in map area delete mode.
15 | - "M" button in object deleter now shows the correct item (was off by one)
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Changelog/v2.0.txt:
--------------------------------------------------------------------------------
1 | Version 2.0
2 |
3 | Released: 2019-08-01 23:22:58 UTC
4 |
5 | This version brings you the Audio Extracter.
6 | It allows you to extract music and more from the game automatically.
7 | Check its help by pressing [F1] when the form is open.
8 |
9 | This version requires QuickPlay.exe.
10 | If the editor has trouble downloading it, you can do so manually here:
11 | https://cable.ayra.ch/satisfactory/QuickPlay.exe
12 |
13 | Other changes
14 |
15 | - The editor is now configured to prefer 64 bit instructions.
16 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Changelog/v2.1.txt:
--------------------------------------------------------------------------------
1 | Version 2.1
2 |
3 | Released: 2019-08-05 19:02:17 UTC
4 |
5 | Visible changes:
6 |
7 | - Fixed button layout in a few windows (nicer alignment, tab order, resizing)
8 | - Updated the very outdated help from the main window to reflect the latest few changes to the menus
9 | - New "Settings" option under the "More" item
10 | - Adding and updating tooltip texts to all main window menu items
11 | - "Help" menu can open the editor website now
12 | - Changelog window
13 |
14 | Invisible changes:
15 |
16 | - QuickPlay downloader is now its own component
17 | - QuickPlay downloader retries failed downloads a few times
18 | - Manual update system
19 | - Commented many functions that were lacking them
20 | - Added more utility functions to the Tools class
21 | - Added more log lines
22 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Changelog/v2.2.txt:
--------------------------------------------------------------------------------
1 | Version 2.2
2 |
3 | Released: 2019-08-14 01:19:53 UTC
4 |
5 | Visible changes:
6 |
7 | To further improve the application, anonymous usage statistics will be sent to the server from now on.
8 | You can view all submitted data as well as disable statistics in the settings window.
9 |
10 | Invisible changes:
11 |
12 | - Simple Usage Logger
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Changelog/v2.3.txt:
--------------------------------------------------------------------------------
1 | Version 2.3
2 |
3 | Released: 2019-08-29 21:40:21 UTC
4 |
5 | Visible changes:
6 |
7 | - Command line mode (see help of main window)
8 | - The session name change works properly now
9 | - Map coordinates have been refined to render positions more accurate
10 |
11 | Invisible changes:
12 |
13 | - More robust handling of rendering errors
14 | - Logger logs crashes of the rendering engine now
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Changelog/v3.0.txt:
--------------------------------------------------------------------------------
1 | Version 3.0
2 |
3 | Released: 2019-09-13 20:47:15 UTC
4 |
5 | Visible changes:
6 |
7 | - Full SMR API integration into the file manager
8 | - Redesign of the settings window
9 |
10 | Invisible changes:
11 |
12 | - Usage reporter has been adapter to support the new SMR API
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Changelog/v3.1.txt:
--------------------------------------------------------------------------------
1 | Version 3.1
2 |
3 | Released: 2019-09-27 16:05:11 UTC
4 |
5 | Invisible changes:
6 |
7 | - Support for new save file type
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Changelog/v3.2.txt:
--------------------------------------------------------------------------------
1 | Version 3.2
2 |
3 | Latest Release
4 |
5 | Invisible changes:
6 |
7 | - Temporarily downgrade save file until I figure out why it won't load anymore.
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Compression.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.IO.Compression;
4 |
5 | namespace SatisfactorySaveEditor
6 | {
7 | ///
8 | /// Provides Easy access to gzip compression
9 | ///
10 | public static class Compression
11 | {
12 | ///
13 | /// Copies a file to a new location and compresses it
14 | ///
15 | /// Original file
16 | /// New file
17 | /// true, if copied and compressed sucessfully
18 | ///
19 | /// The user must clean up the file referenced by if the function fails.
20 | ///
21 | public static bool CompressFile(string InName, string OutName)
22 | {
23 | Log.Write("Compressing \"{0}\" --> \"{1}\"", InName, OutName);
24 | try
25 | {
26 | using (var IN = File.OpenRead(InName))
27 | {
28 | using (var OUT = File.Create(OutName))
29 | {
30 | using (var GZS = new GZipStream(OUT, CompressionLevel.Optimal))
31 | {
32 | IN.CopyTo(GZS);
33 | }
34 | }
35 | }
36 | }
37 | catch (Exception ex)
38 | {
39 | Log.Write(ex);
40 | return false;
41 | }
42 | return true;
43 | }
44 |
45 | ///
46 | /// Compresses a byte array into gzip
47 | ///
48 | /// byte array
49 | /// Compressed data
50 | public static byte[] CompressData(byte[] In)
51 | {
52 | return CompressData(In, 0, In.Length);
53 | }
54 |
55 | ///
56 | /// Compresses a byte array into gzip
57 | ///
58 | /// byte array
59 | /// array offset
60 | /// byte count
61 | /// Compressed data
62 | public static byte[] CompressData(byte[] In, int Index, int Count)
63 | {
64 | using (var MS = new MemoryStream())
65 | {
66 | using (var GZS = new GZipStream(MS, CompressionLevel.Optimal))
67 | {
68 | GZS.Write(In, Index, Count);
69 | }
70 | return MS.ToArray();
71 | }
72 | }
73 |
74 | ///
75 | /// Compresses a stream
76 | ///
77 | /// Input stream
78 | /// Output stream
79 | /// is left open after the operation
80 | public static void CompressStream(Stream In, Stream Out)
81 | {
82 | using (var GZS = new GZipStream(Out, CompressionLevel.Optimal, true))
83 | {
84 | In.CopyTo(GZS);
85 | }
86 | }
87 |
88 | ///
89 | /// Copies a file to a new location and decompresses it
90 | ///
91 | /// Original file
92 | /// New file
93 | /// true, if copied and decompressed sucessfully
94 | ///
95 | /// The user must clean up the file referenced by if the function fails.
96 | ///
97 | public static bool DecompressFile(string InName, string OutName)
98 | {
99 | Log.Write("Decompressing \"{0}\" --> \"{1}\"", InName, OutName);
100 | try
101 | {
102 | using (var IN = File.OpenRead(InName))
103 | {
104 | using (var OUT = File.Create(OutName))
105 | {
106 | using (var GZS = new GZipStream(IN, CompressionMode.Decompress))
107 | {
108 | GZS.CopyTo(OUT);
109 | }
110 | }
111 | }
112 | }
113 | catch (Exception ex)
114 | {
115 | Log.Write(ex);
116 | return false;
117 | }
118 | return true;
119 | }
120 |
121 | ///
122 | /// Decompresses a byte array from gzip
123 | ///
124 | /// Compressed byte array
125 | /// array offset
126 | /// byte count
127 | /// Decompressed data
128 | public static byte[] DecompressData(byte[] In)
129 | {
130 | return DecompressData(In, 0, In.Length);
131 | }
132 |
133 | ///
134 | /// Decompresses a byte array from gzip
135 | ///
136 | /// Compressed byte array
137 | /// array offset
138 | /// byte count
139 | /// Decompressed data
140 | public static byte[] DecompressData(byte[] In, int Index, int Count)
141 | {
142 | using (var MS = new MemoryStream(In, Index, Count))
143 | {
144 | using (var OUT = new MemoryStream())
145 | {
146 | using (var GZS = new GZipStream(MS, CompressionMode.Decompress))
147 | {
148 | GZS.CopyTo(OUT);
149 | }
150 | return OUT.ToArray();
151 | }
152 | }
153 | }
154 |
155 | ///
156 | /// Decompresses a stream
157 | ///
158 | /// Input stream
159 | /// Output stream
160 | /// is left open after the operation
161 | public static void DecompressStream(Stream In, Stream Out)
162 | {
163 | using (var GZS = new GZipStream(In, CompressionMode.Decompress, true))
164 | {
165 | GZS.CopyTo(Out);
166 | }
167 | }
168 |
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/ErrorHandler/frmErrorHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 |
4 | namespace SatisfactorySaveEditor
5 | {
6 | internal partial class frmErrorHandler : Form
7 | {
8 | private bool reported;
9 | private ErrorHandler Handler;
10 | private Exception SourceException;
11 |
12 | public frmErrorHandler(Exception E, ErrorHandler EH)
13 | {
14 | InitializeComponent();
15 | reported = false;
16 | Handler = EH;
17 | SourceException = E;
18 | tbReport.Text = ErrorHandler.generateReport(SourceException);
19 | }
20 |
21 | private void btnClose_Click(object sender, EventArgs e)
22 | {
23 | if (!reported)
24 | {
25 | switch (MessageBox.Show("You have not yet reported the error. This makes it hard to improve the application. Do you want to report it before terminating the application?", "Unsent report", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question))
26 | {
27 | case DialogResult.No:
28 | Close();
29 | break;
30 | case DialogResult.Yes:
31 | if (report())
32 | {
33 | Close();
34 | }
35 | break;
36 | default:
37 | break;
38 | }
39 | }
40 | else
41 | {
42 | Close();
43 | }
44 | }
45 |
46 | private bool report()
47 | {
48 | if (reported)
49 | {
50 | MessageBox.Show("You have already reported the error", "Error reporting", MessageBoxButtons.OK, MessageBoxIcon.Information);
51 | }
52 | else
53 | {
54 | if (Handler.raise(SourceException))
55 | {
56 | reported = true;
57 | }
58 | }
59 | return reported;
60 | }
61 |
62 | private void btnReport_Click(object sender, EventArgs e)
63 | {
64 | var sent = report();
65 | btnReport.Enabled = !sent;
66 | if (!sent)
67 | {
68 | Tools.E($"Unable to send error report.\r\nDetails: {Handler.LastReportResult}", "Errror Report");
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/ErrorHandler/frmErrorHandler.designer.cs:
--------------------------------------------------------------------------------
1 | namespace SatisfactorySaveEditor
2 | {
3 | internal partial class frmErrorHandler
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.components = new System.ComponentModel.Container();
32 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(frmErrorHandler));
33 | this.btnClose = new System.Windows.Forms.Button();
34 | this.lblTitle = new System.Windows.Forms.Label();
35 | this.tbReport = new System.Windows.Forms.TextBox();
36 | this.btnReport = new System.Windows.Forms.Button();
37 | this.ttMain = new System.Windows.Forms.ToolTip(this.components);
38 | this.SuspendLayout();
39 | //
40 | // btnClose
41 | //
42 | this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
43 | this.btnClose.DialogResult = System.Windows.Forms.DialogResult.Cancel;
44 | this.btnClose.Location = new System.Drawing.Point(316, 335);
45 | this.btnClose.Name = "btnClose";
46 | this.btnClose.Size = new System.Drawing.Size(75, 23);
47 | this.btnClose.TabIndex = 2;
48 | this.btnClose.Text = "&Close";
49 | this.ttMain.SetToolTip(this.btnClose, "Close this message and terminate the Application");
50 | this.btnClose.UseVisualStyleBackColor = true;
51 | this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
52 | //
53 | // lblTitle
54 | //
55 | this.lblTitle.AutoSize = true;
56 | this.lblTitle.Font = new System.Drawing.Font("Microsoft Sans Serif", 22F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
57 | this.lblTitle.Location = new System.Drawing.Point(12, 9);
58 | this.lblTitle.Name = "lblTitle";
59 | this.lblTitle.Size = new System.Drawing.Size(401, 36);
60 | this.lblTitle.TabIndex = 0;
61 | this.lblTitle.Text = "An unexpected error occured";
62 | //
63 | // tbReport
64 | //
65 | this.tbReport.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
66 | | System.Windows.Forms.AnchorStyles.Left)
67 | | System.Windows.Forms.AnchorStyles.Right)));
68 | this.tbReport.BackColor = System.Drawing.SystemColors.Window;
69 | this.tbReport.Font = new System.Drawing.Font("Courier New", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
70 | this.tbReport.Location = new System.Drawing.Point(18, 63);
71 | this.tbReport.Multiline = true;
72 | this.tbReport.Name = "tbReport";
73 | this.tbReport.ReadOnly = true;
74 | this.tbReport.ScrollBars = System.Windows.Forms.ScrollBars.Both;
75 | this.tbReport.Size = new System.Drawing.Size(454, 258);
76 | this.tbReport.TabIndex = 1;
77 | //
78 | // btnReport
79 | //
80 | this.btnReport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
81 | this.btnReport.Location = new System.Drawing.Point(397, 335);
82 | this.btnReport.Name = "btnReport";
83 | this.btnReport.Size = new System.Drawing.Size(75, 23);
84 | this.btnReport.TabIndex = 3;
85 | this.btnReport.Text = "&Send";
86 | this.ttMain.SetToolTip(this.btnReport, "Try to report the error automatically");
87 | this.btnReport.UseVisualStyleBackColor = true;
88 | this.btnReport.Click += new System.EventHandler(this.btnReport_Click);
89 | //
90 | // frmErrorHandler
91 | //
92 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
93 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
94 | this.ClientSize = new System.Drawing.Size(492, 373);
95 | this.Controls.Add(this.tbReport);
96 | this.Controls.Add(this.lblTitle);
97 | this.Controls.Add(this.btnReport);
98 | this.Controls.Add(this.btnClose);
99 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
100 | this.MinimumSize = new System.Drawing.Size(500, 400);
101 | this.Name = "frmErrorHandler";
102 | this.Text = "Unexpected error occured";
103 | this.ResumeLayout(false);
104 | this.PerformLayout();
105 |
106 | }
107 |
108 | #endregion
109 |
110 | private System.Windows.Forms.Button btnClose;
111 | private System.Windows.Forms.Label lblTitle;
112 | private System.Windows.Forms.TextBox tbReport;
113 | private System.Windows.Forms.Button btnReport;
114 | private System.Windows.Forms.ToolTip ttMain;
115 | }
116 | }
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/FeatureReport.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace SatisfactorySaveEditor
10 | {
11 | public static class FeatureReport
12 | {
13 | public const string REPORT_URL = "https://cable.ayra.ch/satisfactory/report/";
14 |
15 | public enum Feature : int
16 | {
17 | OpenFile,
18 | OpenByCommandLine,
19 | Manager,
20 | SaveFile,
21 | QuickAction,
22 | DoBackup,
23 | RestoreBackup,
24 | EditHeader,
25 | CountItems,
26 | DuplicateItems,
27 | DeleteByType,
28 | DeleteByArea,
29 | XmlExport,
30 | XmlImport,
31 | ClearStringList,
32 | ExtractAudio,
33 | ChangeSettings,
34 | DisableReport,
35 | HelpRequest,
36 | ViewChanges,
37 | ApiAction,
38 | ApiRegisterAuto,
39 | ///
40 | /// Application is running in debug mode
41 | ///
42 | DebugMode,
43 | ///
44 | /// Application was terminated due to an unhandled exception
45 | ///
46 | TerminateByError,
47 | ///
48 | /// The image renderer crashed
49 | ///
50 | RendererCrash,
51 | }
52 |
53 | public static Guid Id
54 | { get; set; }
55 |
56 | private static List UsedFeatures;
57 |
58 | static FeatureReport()
59 | {
60 | UsedFeatures = new List();
61 | Id = Guid.NewGuid();
62 | }
63 |
64 | public static void Used(Feature F)
65 | {
66 | UsedFeatures.Add(F);
67 | }
68 |
69 | public static void Report()
70 | {
71 | //Don't bother reporting if it doesn't works
72 | if (Id == Guid.Empty)
73 | {
74 | Log.Write("{0}: Reporting Failed. ID not set", nameof(FeatureReport));
75 | return;
76 | }
77 | //Prevent multiple reports from stacking up
78 | lock (UsedFeatures)
79 | {
80 | //Don't bother reporting if nothing is there to report
81 | if (UsedFeatures.Count > 0)
82 | {
83 | var BaseFields = new string[] {
84 | $"id={Id}",
85 | $"version={Tools.CurrentVersion}"
86 | };
87 | WebResponse Res = null;
88 | var Req = WebRequest.CreateHttp(REPORT_URL);
89 | Req.UserAgent = UpdateHandler.UserAgent;
90 | Req.Method = "POST";
91 | Req.ContentType = "application/x-www-form-urlencoded";
92 | try
93 | {
94 | var Features = Encoding.UTF8.GetBytes(string.Join("&", UsedFeatures.Distinct().Select(m => $"feature[]={m}").Concat(BaseFields)));
95 | Req.ContentLength = Features.Length;
96 | using (var S = Req.GetRequestStream())
97 | {
98 | S.Write(Features, 0, Features.Length);
99 | S.Close();
100 | }
101 | Res = Req.GetResponse();
102 | }
103 | catch (WebException ex)
104 | {
105 | try
106 | {
107 | using (Res = ex.Response)
108 | {
109 | using (var SR = new StreamReader(Res.GetResponseStream()))
110 | {
111 | #if DEBUG
112 | Tools.E(SR.ReadToEnd(), "DEBUG Report Error");
113 | #else
114 | Log.Write("{0}: Reporting failed. Data: {1}", nameof(FeatureReport), SR.ReadToEnd());
115 | #endif
116 | }
117 | }
118 | }
119 | catch
120 | {
121 | Log.Write("{0}: Reporting failed (unable to provide a reason, response is null)", nameof(FeatureReport));
122 | }
123 | return;
124 | }
125 | catch (Exception ex)
126 | {
127 | Log.Write("{0}: Reporting failed (unable to provide a reason, not a WebException)", nameof(FeatureReport));
128 | Log.Write(ex);
129 | }
130 | //Report OK. Clear list and log message
131 | UsedFeatures.Clear();
132 | using (Res)
133 | {
134 | using (var SR = new StreamReader(Res.GetResponseStream()))
135 | {
136 | Log.Write("{0}: Reporting OK. Data: {1}", nameof(FeatureReport), SR.ReadToEnd());
137 | }
138 | }
139 | }
140 | }
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/Welcome.txt:
--------------------------------------------------------------------------------
1 | Satisfactory Save Editor
2 |
3 | Welcome to the Satisfactory Save File Editor.
4 | This application allows you to make generic changes to your save files.
5 | If you are stuck at any point, press [F1] to bring up this window again.
6 |
7 | Getting Help:
8 |
9 | If you encounter any problems, contact /u/AyrA_ch on reddit.
10 |
11 | Reporting Bugs:
12 |
13 | If you believe you found a bug, you can contact me on reddit,
14 | or if you know how, open an issue on Github:
15 | https://github.com/AyrA/SatisfactorySaveEditor/issues
16 |
17 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/_no.txt:
--------------------------------------------------------------------------------
1 | There is no help available for this item right now.
2 |
3 | If you believe this is an error,
4 | you can open an issue at https://github.com/AyrA/SatisfactorySaveEditor/issues
5 |
6 | The missing file is {0}
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/frmApiRegister.txt:
--------------------------------------------------------------------------------
1 | Satisfactory Save Editor API Registration Form
2 |
3 | Here you can register your API key to manage your save files in the cloud.
4 |
5 | You can either enter the key manually or click the button to get the key from the API automatically.
6 |
7 | Clearing the key from the text box will disable the API again.
8 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/frmAudioExtract.txt:
--------------------------------------------------------------------------------
1 | Satisfactory Save Editor Audio Extracter
2 |
3 | This form helps you in extracting audio files from your satisfactory game.
4 | Files included are:
5 | - Music
6 | - Ambient noise
7 | - A.D.A messages
8 |
9 | The files have an invalid header and are fixed up properly to play in any standard player.
10 | Most files are RIFF WAVE, A.D.A messages are OGG.
11 |
12 | WAV Format: PCM, 16 bit, LE, 1ch or 2ch(Mono or Stereo)
13 | OGG Format: Opus, VBR, 1ch(Mono), Converter arguments: --vbr --padding 0 --bitrate 64 --comp 10 --serial 0 --framesize 20
14 |
15 | Thanks to the uncompressed wav files you will find that the music sounds great.
16 | If a WAV file is Mono or Stereo depends on the type of sound.
17 | Music and "global" effects are usually Stereo.
18 | Ambient noise that is location sensitive is usually Mono.
19 |
20 | File name Format
21 | ================
22 | The files don't have names on their own in the game file, so they are numbered from 0001 onwards.
23 | WAV and OGG share the same number pool.
24 |
25 | Game Required
26 | =============
27 | You need to have the game installed.
28 | Doesn't matters if it's the weekend demo, early access or experimental version.
29 | The experimental version tends to give most files.
30 |
31 | Finding the file
32 | ================
33 | We try to find the required file for you when you open the form.
34 | This works if the game is installed in the program files folder only.
35 | It prefers the experimental over the early access version.
36 |
37 | Manual file search
38 | ==================
39 | If it's not possible to find the file automatically or you want to try a different file,
40 | you can search for it manually.
41 | You need to specify the "FactoryGame-WindowsNoEditor.pak" file.
42 | It's inside the "FactoryGame\Content\Paks" folder of your satisfactory installation.
43 |
44 | WARNING! You can select files from other games.
45 | This is likely not going to extract the files properly unless they are unencrypted.
46 | For wav files, they will be unusable unless they are in the format specified above
47 | because the header is rewritten.
48 |
49 | This application is open source.
50 | You can of course just take out the wav file scanner part and adapt it to support your game.
51 |
52 | Automatic file search
53 | =====================
54 | As an alternative to a manual search, the automatic search is offered.
55 | This will scan all hard drives (it will not scan network drives or removable drives).
56 | The scan is aborted as soon as a game file is found.
57 |
58 | Audio extraction
59 | ================
60 | Audio extraction is done in two steps.
61 | First, we scan the entire game file for audio streams.
62 | This is a time consuming process because the file is 15 GB in size.
63 | Note: Because we try to process the input file at maximum speed,
64 | you will find that your computer will operate sluggish until this process is complete.
65 |
66 | The second step is filtering and extraction of the actual files.
67 | We remove files that are invalid and export all others, ordered by type, then size descending.
68 | This means you end up with the background music first and the A.D.A. messages last.
69 |
70 | Player
71 | ======
72 | This application features a very crude audio player that's displayed when at least one file has been found.
73 | It's meant to preview the files.
74 | For proper playback, add the files in the "Audio" directory to your audio player.
75 |
76 | Note: This application uses QuickPlay.exe to play the audio files.
77 | It will automatically be downloaded if it doesn't exists.
78 |
79 | File protection
80 | ===============
81 | Files will not be overwritten.
82 | Delete the "Audio" folder if you want to search for audio files again.
83 | This is useful after a game update.
84 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/frmChangeLog.txt:
--------------------------------------------------------------------------------
1 | Satisfactory Save Editor Changelog
2 |
3 | This form simply shows all changes that were made in a specific version
4 |
5 | It pops up automatically after an update
6 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/frmCloudEdit.txt:
--------------------------------------------------------------------------------
1 | Satisfactory Save Editor Cloud File Editor
2 |
3 | This window allows you to edit the properties of maps in your cloud account.
4 |
5 | Conflicting categories are automatically disabled.
6 | If you can't select a checkbox, it's because another category is mutually exclusive to the disabled one.
7 |
8 | Enabled category checkboxes have a tooltip on mouse hover that describes the category.
9 |
10 | You can also change the hidden id.
11 | Note that this change is in effect immediately, you don't need to save other changes.
12 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/frmCounter.txt:
--------------------------------------------------------------------------------
1 | Satisfactory Save Editor Object Counter
2 |
3 | This form counts all objects in your save file.
4 |
5 | You can select multiple entries with the mouse by holding down [SHIFT] or [CTRL].
6 |
7 | Pressing [ENTER] renders the selected items on the main window map.
8 | Double clicking on an entry renders that item only.
9 |
10 | Entries with a "disabled" background color don't have coordinates, or they are unset.
11 | The button on the bottom of the window allows you to show and hide those items.
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/frmDeleter.txt:
--------------------------------------------------------------------------------
1 | Satisfactory Save Editor Object Deleter
2 |
3 | Here you can delete any entry from the save file.
4 | Deleting objects will not give you the resources for them.
5 |
6 | Select the object to delete from the list and then chose the range you want to delete.
7 |
8 | The [M] button will render your selection on the main window map.
9 |
10 | Tip: After you are done deleting and are back to the main window, press [F5] to refresh the map.
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/frmDuplicator.txt:
--------------------------------------------------------------------------------
1 | Satisfactory Save Editor Item Duplicator
2 |
3 | You can clone any kind of entry with this window,
4 | including those you almost certainly should not.
5 |
6 | You can optionally apply an offset to the copied item.
7 | When cloning multiple times, the offset is calculated from the previously placed object,
8 | essentially creating a chain of them.
9 |
10 | Be aware that map coordinate units are approximately centimeters.
11 | An 8x2 foundation is therefore X=800, Y=800, Z=200
12 |
13 | The offset is pretty much mandatory when cloning an object lots of times to avoid significant lag.
14 |
15 | The [M] button will render your selection (without potential duplicates) on the main window map.
16 |
17 | Tip: After you are done duplicating and are back to the main window, press [F5] to refresh the map.
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/frmElementList.txt:
--------------------------------------------------------------------------------
1 | Element Process List
2 |
3 | This form shows the item type and count that are to be processed.
4 |
5 | The top list contains items that are to be processed.
6 | The bottom list contains items to skip.
7 |
8 | Select Items and use the [DEL] key to move it to the other list.
9 |
10 | Use the "Confirm" button to confirm the changes.
11 | To do nothing, move all items to the bottom list and then click "Confirm".
12 | Closing the form using the [X] control will also discard the changes.
13 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/frmExport.txt:
--------------------------------------------------------------------------------
1 | Satisfactory Save Editor Object Exporter
2 |
3 | Here you can export and import individual items or groups of them.
4 |
5 | Data format:
6 |
7 | The exporter uses XML as underlying data format.
8 | If you feel confident enough, you can edit it in a text editor before importing again.
9 |
10 | Exporting:
11 |
12 | Select the item, offset and count you want to export, then click the "Export" button.
13 | The exported content is placed as text in your clipboard.
14 | Feel free to either import it into another save file (see "Import" below) or save it in a text editor of your choice.
15 |
16 | Note: Exports ARE NOT cumulative. Exporting will erase previously exported content from the clipboard.
17 |
18 | Import:
19 |
20 | Click the "Import" button to import whatever entries are in your clipboard.
21 | Optionally apply one of these options:
22 |
23 | "Remove identical existing entries" deletes all entries of the same types you try to import.
24 | For example when you import foundations, it will delete all foundations in the file before importing yours.
25 | This option is most useful if you want to import entries that are not supposed to exist multiple times.
26 |
27 | "Fix internal names" changes the 'InternalName' property of imported items when needed to avoid duplicates.
28 | This is necessary because this name is supposed to be unique. It is therefore a good idea to leave enabled.
29 |
30 | Note: This will not change references that no longer point to a valid name because it was changed.
31 | In most cases, this is not a problem. It will cause some objects to not work properly,
32 | for example storage containers.
33 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/frmHeaderEditor.txt:
--------------------------------------------------------------------------------
1 | Satisfactory Save Editor Header Editor
2 |
3 | The header editor allows you to edit basic header properties that don't directly affect the progress of the file.
4 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/frmHelp.txt:
--------------------------------------------------------------------------------
1 | Satisfactory Save Editor Help Form
2 |
3 | This is the help window.
4 | It is shown whenever you press [F1].
5 | You can continue to work on your save file while this window is being shown.
6 |
7 | Pressing [F1] at any time will show the help content relevant to the currently open form.
8 | You do not need to close the help window for the new text to show up.
9 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/frmMain.txt:
--------------------------------------------------------------------------------
1 | Satisfactory Save Editor Main Form
2 |
3 | This is the window where you do most of your changes.
4 | The main area of the window is used to show a map of your save file.
5 | An image of the map is saved in the same location your current save game is at.
6 |
7 | Automatic Backups
8 | =================
9 | This editor will automatically create a single backup of any file you save,
10 | provided that no such backup already exists.
11 | To create multiple backups, use the Save File Manager [CTRL]+[M].
12 |
13 | File Menu
14 | =========
15 |
16 | This provides basic File operations
17 | and has the usual commands (Load, Save, Exit, ...) you find in this menu in most applications.
18 |
19 | Quick Actions Menu
20 | ==================
21 |
22 | This provides pre-made bulk actions for your save file.
23 | They are mostly made of features that clean up the map and undo player made changes.
24 |
25 | General Actions Menu
26 | ====================
27 |
28 | This provides general editing capabilities of the save file.
29 |
30 | More Menu
31 | =========
32 |
33 | This provides actions not related to save file editing.
34 | All options in this menu are going to work without a loaded save file
35 |
36 | Help Menu
37 | =========
38 |
39 | Provides help and information.
40 | This menu also contains a manual update check option for when you disable automatic update checks.
41 |
42 | Update Available Menu
43 | =====================
44 |
45 | This red menu item is only shown when an update has been downloaded sucessfully and is ready for installation.
46 | Clicking this is optional. If you don't, the update is installed during the next launch.
47 |
48 | Command Line
49 | ============
50 |
51 | Basic operations are available as command line mode now.
52 | This mode is invoked when at least one "switch" is provided.
53 | If only a file name is provided, it will open that file in the normal user interface mode.
54 | Be aware that this application is not a true command line application.
55 | It will spawn its own command line window and always waits for a user key press at the end.
56 | You can combine multiple arguments.
57 |
58 | Arguments: [/verify] [/list] [/pack] [/render] [/info] [/rename ] [SaveFile]
59 |
60 | /verify - Reads the entire file and verifies basic constraints
61 | /list - Lists all entries and counts them
62 | /pack - Compresses file if uncompressed, or uncompresses if compressed.
63 | /rename - Renames the session
64 | /render - Renders a PNG image of the map into the same directory (replaces .sav with .png)
65 | /info - Print some basic information
66 | SaveFile - File to open. Required argument if switches are used
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/frmManager.txt:
--------------------------------------------------------------------------------
1 | Satisfactory Save Editor manager
2 |
3 | Here you can manage all your save games, locally and in the cloud.
4 |
5 | Most options are available via the context menu on the local and the cloud side.
6 | Keyboard (local side): [ENTER] will open a file and [DEL] will delete a file.
7 | Keyboard (cloud side): [ENTER] will preview a file and [DEL] will delete a file.
8 | For import/restore, press the button at the bottom.
9 |
10 | Context menu descriptions (local side):
11 |
12 | Open:
13 | This opens a save file in the current save file editor instance.
14 | You will lose any unsaved progress in a currently opened file.
15 |
16 | Render:
17 | This shows a preview image of the map
18 |
19 | Upload:
20 | This uploads the file to the cloud.
21 |
22 | Rename:
23 | This renames a save file and/or session name.
24 |
25 | Backup:
26 | Creates a compressed backup in a location you can chose from
27 |
28 | Duplicate:
29 | Creates an exact copy of the selected file with a new name.
30 |
31 | Delete:
32 | This deletes a save file after confirmation is given.
33 | Will not delete copies in the cloud.
34 |
35 | Context menu descriptions (remote side):
36 |
37 | Download:
38 | Downloads the map to your local map collection
39 |
40 | Preview:
41 | This shows a preview image of the map
42 |
43 | Copy Hidden Id:
44 | Copies the hidden id, which can be used to install maps that are not public.
45 |
46 | Import Id:
47 | This imports a map by id.
48 | You can use the real or hidden id for this.
49 |
50 | Edit:
51 | Edits properties of this map (name, description, etc)
52 |
53 | Delete:
54 | Deletes the map from the cloud. Will not delete local copies.
55 |
56 | Format for backups:
57 | Backup files are gzip compressed to save on space.
58 | On average, the result will be less than 10% of the original save file size
59 |
60 | Importing and restoring maps:
61 | The import function will autodetect if save files are compressed or not.
62 |
63 | Invalid files:
64 | Invalid files are given in red color.
65 | Most options are unavailable for these.
66 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/frmRegionDeleter.txt:
--------------------------------------------------------------------------------
1 | Satisfactory Save Editor Region Deleter
2 |
3 | This window allows you to delete all objects inside a user selected region.
4 |
5 | Click on the map to set points of a polygon.
6 | To delete the last point, use the right mouse button.
7 |
8 | The objects inside the selected region will be destroyed.
9 | You will not be given back any items in the process.
10 | This includes but is not limited to:
11 | - Items needed to craft the object
12 | - Items in association with the object (items on belts, storage content, etc)
13 | - Power Shards
14 |
15 | Menu Options:
16 |
17 | New Selection (CTRL+N)
18 | This will erase all points and begins selecting from the start
19 |
20 | Delete Objects... (DEL)
21 | Shows a list of objects inside the region and offers to delete them.
22 |
23 | Close (CTRL+Q)
24 | Closes the window.
25 |
26 | Tip: Keep the window maximized for maximum accuracy.
27 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/frmRename.txt:
--------------------------------------------------------------------------------
1 | Satisfactory Save Editor Rename
2 |
3 | Here you can rename the session name and file name of a save file.
4 | Session name and File name must be valid file names.
5 | This means, these characters are forbidden:
6 |
7 | \ / " : ? * < > |
8 |
9 | Additionally forbidden are some special unicode characters which you are very unlikely to type and the "NULL" character.
10 | The "OK" button will become unavailable immediately if any invalid characters are entered or a box is empty.
11 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Help/frmSettings.txt:
--------------------------------------------------------------------------------
1 | Satisfactory Save Editor Settings
2 |
3 | Here you can change various settings.
4 | They are saved immediately when you make a change.
5 |
6 | Dialog Boxes
7 | ============
8 |
9 | In this section you can mark all hint dialogs as read or unread.
10 |
11 | Update Settings
12 | ===============
13 |
14 | In this section you can change update related settings.
15 |
16 | You can turn automatic updates on or off.
17 | If they are on, we check for updates once at most every 24 hours.
18 | If you turn them off, you get a manual "Check for Updates" in the "Help" main menu.
19 |
20 | After a successful update the change log is shown to give you a quick info of the changes.
21 | You can turn this off using the second checkbox in this section.
22 | You can still at any time bring up the changelog window in the "Help" main menu
23 |
24 | How Automatic Updates Work
25 | ==========================
26 |
27 | Automatic updates are only checked for when the editor is running.
28 | We do not install and run background applications.
29 | This would go against the portable nature of this application.
30 |
31 | During the update, no personal or statistical information is sent to our server.
32 | The update checker gets a document from our server that contains the latest version and a download link.
33 | If your version is older, the file is downloaded.
34 | After a successful download, you get the red "Update Available" main menu item.
35 | You can click this item to install the update immediately, or you can ignore it,
36 | in which case the update is installed during the next start.
37 | Installation takes about half a second as of now.
38 |
39 | Usage Statistics
40 | ================
41 |
42 | This application collects and submits very limited usage statistics.
43 | The statistics are reported once you close the application or if it gets terminated due to an error.
44 |
45 | What is included:
46 | -----------------
47 |
48 | - Current version number
49 | - Randomly generated Id
50 | - List of features you used
51 |
52 | What is NEVER included:
53 | -----------------------
54 |
55 | - Anything related to your satisfactory installation
56 | - Your save games
57 | - Username
58 | - IP address*
59 | - Operating system information (Type, environment, language, etc)
60 | - Information about other installed software
61 |
62 | *) Your IP address will appear in the regular server logs of cable.ayra.ch as does every IP of every visitor.
63 | This is normal and practiced by almost all web servers world wide.
64 | The address WILL NOT be collected from the logs and tied to the Id.
65 |
66 | What we do:
67 | -----------
68 |
69 | - Analyze your statistics to focus development on the most used features.
70 | - Build and display anonymous statistics for the public to see.
71 |
72 | What we NEVER do:
73 | -----------------
74 |
75 | - Share your statistics with 3rd parties.
76 | - Sell your statistics.
77 |
78 | Your Usage Statistics Options
79 | =============================
80 |
81 | You can decide to change the Id each time you launch the application.
82 | This will add a layer of anonymity but it prevents you from viewing your own statistics.
83 |
84 | You can also completely disable usage statistics reporting.
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Images/Height.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AyrA/SatisfactorySaveEditor/1c7d5f8b7658541e7e979e5605717da068407140/SatisfactorySaveEditor/Images/Height.png
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Images/Icons/audio.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AyrA/SatisfactorySaveEditor/1c7d5f8b7658541e7e979e5605717da068407140/SatisfactorySaveEditor/Images/Icons/audio.ico
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Images/Icons/copy.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AyrA/SatisfactorySaveEditor/1c7d5f8b7658541e7e979e5605717da068407140/SatisfactorySaveEditor/Images/Icons/copy.ico
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Images/Icons/delete.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AyrA/SatisfactorySaveEditor/1c7d5f8b7658541e7e979e5605717da068407140/SatisfactorySaveEditor/Images/Icons/delete.ico
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Images/Icons/edit.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AyrA/SatisfactorySaveEditor/1c7d5f8b7658541e7e979e5605717da068407140/SatisfactorySaveEditor/Images/Icons/edit.ico
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Images/Icons/help.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AyrA/SatisfactorySaveEditor/1c7d5f8b7658541e7e979e5605717da068407140/SatisfactorySaveEditor/Images/Icons/help.ico
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Images/Icons/network.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AyrA/SatisfactorySaveEditor/1c7d5f8b7658541e7e979e5605717da068407140/SatisfactorySaveEditor/Images/Icons/network.ico
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Images/Icons/rename.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AyrA/SatisfactorySaveEditor/1c7d5f8b7658541e7e979e5605717da068407140/SatisfactorySaveEditor/Images/Icons/rename.ico
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Images/Icons/search.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AyrA/SatisfactorySaveEditor/1c7d5f8b7658541e7e979e5605717da068407140/SatisfactorySaveEditor/Images/Icons/search.ico
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Images/Icons/settings.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AyrA/SatisfactorySaveEditor/1c7d5f8b7658541e7e979e5605717da068407140/SatisfactorySaveEditor/Images/Icons/settings.ico
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Images/Map.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AyrA/SatisfactorySaveEditor/1c7d5f8b7658541e7e979e5605717da068407140/SatisfactorySaveEditor/Images/Map.png
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Log.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Runtime.InteropServices;
6 | using System.Threading;
7 |
8 | namespace SatisfactorySaveEditor
9 | {
10 | public static class Log
11 | {
12 | [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
13 | private static extern void OutputDebugString(string message);
14 |
15 | private static TextWriter Logger;
16 | private static Stopwatch SW;
17 | private static List LogMessages;
18 |
19 | public static string Logfile
20 | { get; private set; }
21 |
22 | static Log()
23 | {
24 | LogMessages = new List();
25 | SW = new Stopwatch();
26 | SW.Start();
27 | using (var P = Process.GetCurrentProcess())
28 | {
29 | try
30 | {
31 | //Try logging into application directory first
32 | Logger = File.CreateText(Logfile = Path.Combine(Path.GetDirectoryName(P.MainModule.FileName), "lastrun.log"));
33 | }
34 | catch
35 | {
36 | try
37 | {
38 | //Try temporary file on errors
39 | var F = Path.GetTempFileName();
40 | File.Delete(F);
41 | Path.ChangeExtension(F, ".log");
42 | Logger = File.CreateText(Logfile = F);
43 | }
44 | catch
45 | {
46 | //Give up
47 | Logfile = "";
48 | Logger = TextWriter.Null;
49 | }
50 | }
51 | }
52 | var DT = DateTime.UtcNow;
53 | Write("{0} {1}: Logger initialized", DT.ToLongDateString(), DT.ToLongTimeString());
54 | //Flush logger periodically
55 | Thread T = new Thread(delegate ()
56 | {
57 | while (Logger != null && Logger != TextWriter.Null)
58 | {
59 | Thread.Sleep(5000);
60 | lock (Logger)
61 | {
62 | Logger.Flush();
63 | }
64 | }
65 | });
66 | T.IsBackground = true;
67 | T.Start();
68 | }
69 |
70 | public static string[] GetMessages()
71 | {
72 | lock (Logger)
73 | {
74 | return LogMessages.ToArray();
75 | }
76 | }
77 |
78 | public static void Close()
79 | {
80 | lock (Logger)
81 | {
82 | if (Logger != TextWriter.Null)
83 | {
84 | var DT = DateTime.UtcNow;
85 | Write("{0} {1}: Logger ended", DT.ToLongDateString(), DT.ToLongTimeString());
86 | Logger.Flush();
87 | Logger.Close();
88 | Logger.Dispose();
89 | Logger = TextWriter.Null;
90 | }
91 | }
92 | }
93 |
94 | public static void Write(string Text)
95 | {
96 | var Msg = string.Format("[{0,9}] {1}", SW.ElapsedMilliseconds, Text);
97 | lock (Logger)
98 | {
99 | if (Program.DEBUG)
100 | {
101 | Debug.WriteLine(Msg);
102 | OutputDebugString(Msg);
103 | }
104 | Logger.WriteLine(Msg);
105 | LogMessages.Add(Msg);
106 | if (LogMessages.Count > 1000)
107 | {
108 | LogMessages.RemoveRange(1, LogMessages.Count - 999);
109 | LogMessages[0] = "";
110 | }
111 | }
112 | }
113 |
114 | public static void Write(string Text, params object[] Args)
115 | {
116 | Write(string.Format(Text, Args));
117 | }
118 |
119 | public static void Write(Exception ex, bool IsRoot = true)
120 | {
121 | if (ex == null)
122 | {
123 | return;
124 | }
125 | if (IsRoot)
126 | {
127 | Write("=== START: {0} handler ===", ex.GetType().FullName);
128 | }
129 | Write("Error: {0}", ex.Message);
130 | Write("Stack: {0}", ex.StackTrace);
131 | if (ex.Data != null && ex.Data.Count > 0)
132 | {
133 | foreach (var Entry in ex.Data)
134 | {
135 | Write("Data: {0}", Entry);
136 | }
137 | }
138 | if (ex is AggregateException)
139 | {
140 | foreach (var E in ((AggregateException)ex).InnerExceptions)
141 | {
142 | Write(E, false);
143 | }
144 | }
145 | else
146 | {
147 | Write(ex.InnerException, false);
148 | }
149 | if (IsRoot)
150 | {
151 | Write("=== END: {0} handler ===", ex.GetType().FullName);
152 | lock (Logger)
153 | {
154 | //Always flush on exceptions
155 | Logger.Flush();
156 | }
157 | }
158 | }
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("SatisfactorySaveEditor")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("AyrA.ch")]
11 | [assembly: AssemblyProduct("SatisfactorySaveEditor")]
12 | [assembly: AssemblyCopyright("Copyright © AyrA.ch 2019")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("5a95afc0-2278-42ab-b18f-bee58e642337")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("3.2.0.0")]
35 | [assembly: AssemblyFileVersion("3.2.0.0")]
36 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/PropertyValues/ArrayProperty.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 |
5 | namespace SatisfactorySaveEditor.PropertyValues
6 | {
7 | public class ArrayProperty : IPropertyValue
8 | {
9 | public List Properties;
10 |
11 | public ArrayProperty(BinaryReader BR)
12 | {
13 | Properties = new List();
14 | var Bytes = BR.ReadInt64();
15 | var Pos = BR.BaseStream.Position;
16 | while (BR.BaseStream.Position < Pos + Bytes)
17 | {
18 | Properties.Add(new Property(BR));
19 | }
20 | }
21 |
22 | public void Export(BinaryWriter BW)
23 | {
24 | throw new NotImplementedException();
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/PropertyValues/IPropertyValue.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace SatisfactorySaveEditor.PropertyValues
4 | {
5 | public interface IPropertyValue
6 | {
7 | void Export(BinaryWriter BW);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/PropertyValues/IntProperty.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace SatisfactorySaveEditor.PropertyValues
4 | {
5 | class IntProperty : IPropertyValue
6 | {
7 | public int UnknownInt { get; private set; }
8 | public int Value { get; private set; }
9 |
10 | public IntProperty(BinaryReader BR)
11 | {
12 | //Always 4
13 | BR.ReadInt32();
14 | UnknownInt = BR.ReadInt32();
15 | BR.ReadByte();
16 | Value = BR.ReadInt32();
17 | }
18 |
19 | public void Export(BinaryWriter BW)
20 | {
21 | BW.WriteIntString("IntProperty");
22 | BW.Write(4);
23 | BW.Write(UnknownInt);
24 | BW.Write((byte)0);
25 | BW.Write(Value);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/PropertyValues/Inventory.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 |
4 | namespace SatisfactorySaveEditor.PropertyValues
5 | {
6 | public class Inventory
7 | {
8 | public List Items;
9 | public Inventory(byte[] Data)
10 | {
11 | Items = new List();
12 |
13 | using (var MS = new MemoryStream(Data, false))
14 | {
15 | using (var BR = new BinaryReader(MS))
16 | {
17 | var Name = BR.ReadIntString();
18 | var ValueType = BR.ReadIntString();
19 | //Discard unknown bytes for now. Example was 5420, probably byte count
20 | BR.ReadBytes(8);
21 | //"StructProperty"
22 | BR.ReadIntString();
23 | //??
24 | BR.ReadBytes(5);
25 | //"InventoryStacks"
26 | BR.ReadIntString();
27 | //"StructProperty"
28 | BR.ReadIntString();
29 | //??
30 | BR.ReadBytes(8);
31 | //"InventoryStack"
32 | BR.ReadIntString();
33 | //?? All are Zero
34 | BR.ReadBytes(17);
35 |
36 | //This likely loops
37 |
38 | //"Item"
39 | BR.ReadIntString();
40 | //"StructProperty"
41 | BR.ReadIntString();
42 | //??
43 | BR.ReadBytes(8);
44 |
45 | //"InventoryItem"
46 | BR.ReadIntString();
47 | //?? All are Zero
48 | BR.ReadBytes(21);
49 |
50 | var Item = new InventoryItem(BR);
51 |
52 | }
53 | }
54 | }
55 |
56 | public Inventory()
57 | {
58 |
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/PropertyValues/InventoryItem.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace SatisfactorySaveEditor.PropertyValues
4 | {
5 | public class InventoryItem
6 | {
7 | public int Count { get; set; }
8 | public string Name { get; set; }
9 | public string Persistence1 { get; set; }
10 | public string Persistence2 { get; set; }
11 |
12 | public InventoryItem(BinaryReader BR)
13 | {
14 | //name of item
15 | Name = BR.ReadIntString();
16 | //"PersistentLevel" type string
17 | Persistence1 = BR.ReadIntString();
18 | //"PersistentLevel" type string (Again)
19 | Persistence2 = BR.ReadIntString();
20 |
21 | //"NumItems"
22 | BR.ReadIntString();
23 | //"IntProperty"
24 | BR.ReadIntString();
25 |
26 | //Discard. 04 00 00 00 00 00 00 00 00
27 | BR.ReadBytes(BR.ReadInt32());
28 | BR.ReadByte();
29 |
30 | Count = BR.ReadInt32();
31 | //"None"
32 | BR.ReadIntString();
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/PropertyValues/NoneProperty.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace SatisfactorySaveEditor.PropertyValues
4 | {
5 | public class NoneProperty : IPropertyValue
6 | {
7 | public NoneProperty(BinaryReader BR)
8 | {
9 | //This property has no values
10 | }
11 |
12 | public void Export(BinaryWriter BW)
13 | {
14 | BW.WriteIntString("None");
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/PropertyValues/Property.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace SatisfactorySaveEditor.PropertyValues
5 | {
6 | public class Property : IPropertyValue
7 | {
8 | public string Name { get; set; }
9 |
10 | public IPropertyValue Value { get; set; }
11 |
12 | public Property(BinaryReader BR)
13 | {
14 | var ValueType = BR.ReadIntString();
15 | switch (ValueType)
16 | {
17 | case "ArrayProperty":
18 | Value = new ArrayProperty(BR);
19 | break;
20 | case "StructProperty":
21 | Value = new StructProperty(BR);
22 | break;
23 | case "None":
24 | Value = new NoneProperty(BR);
25 | break;
26 | default:
27 | throw new NotImplementedException();
28 | }
29 | }
30 |
31 | public void Export(BinaryWriter BW)
32 | {
33 | throw new NotImplementedException();
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/PropertyValues/StructProperty.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 |
5 | namespace SatisfactorySaveEditor.PropertyValues
6 | {
7 | public class StructProperty : IPropertyValue
8 | {
9 | public Dictionary Properties { get; set; }
10 |
11 | public byte[] Sequence { get; set; }
12 |
13 | public StructProperty(BinaryReader BR)
14 | {
15 | Properties = new Dictionary();
16 | //Sequence = BR.ReadBytes(5); //This number is dynamic. No Idea how to read
17 | while (true)
18 | {
19 | var Name = BR.ReadIntString();
20 | //Empty property name means we have them all now.
21 | if (Name == string.Empty)
22 | {
23 | break;
24 | }
25 | Properties.Add(Name, new Property(BR));
26 | }
27 | }
28 |
29 | public void Export(BinaryWriter BW)
30 | {
31 | throw new NotImplementedException();
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/QuickPlay.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.IO;
4 | using System.Net;
5 | using System.Threading;
6 |
7 | namespace SatisfactorySaveEditor
8 | {
9 | ///
10 | /// Provides a QuickPlay obtain and update mechanism
11 | ///
12 | public static class QuickPlay
13 | {
14 | ///
15 | /// Expected QuickPlay hash
16 | ///
17 | private const string QP_HASH = "888178F6B888A295DB8269CF12B8803BAF2065B6";
18 | ///
19 | /// QuickPlay download URL
20 | ///
21 | private const string QP_URL = @"https://cable.ayra.ch/satisfactory/QuickPlay.exe";
22 | ///
23 | /// Default number of retries
24 | ///
25 | private const int DEFAULT_RETRIES = 5;
26 |
27 | ///
28 | /// Gets if QuickPlay is available
29 | ///
30 | public static bool HasQuickPlay
31 | { get; private set; }
32 |
33 | ///
34 | /// Number of Retries to download so far
35 | ///
36 | public static int QuickPlayTries
37 | { get; private set; }
38 |
39 | ///
40 | /// Gets or sets the maximum number of download retries
41 | ///
42 | public static int MaximumRetries
43 | { get; set; }
44 |
45 | ///
46 | /// Gets the Path+Name of QuickPlay
47 | ///
48 | public static string QuickPlayPath
49 | {
50 | get
51 | {
52 | return Path.Combine(UpdateHandler.ExecutablePath, "QuickPlay.exe");
53 | }
54 | }
55 |
56 | static QuickPlay()
57 | {
58 | MaximumRetries = DEFAULT_RETRIES;
59 | }
60 |
61 | public static void ResetQuickPlay()
62 | {
63 | try
64 | {
65 | File.Delete(QuickPlayPath);
66 | }
67 | catch (Exception ex)
68 | {
69 | Log.Write(new Exception("Unable to reset QuickPlay because we can't delete the file.", ex));
70 | }
71 | HasQuickPlay = File.Exists(QuickPlayPath);
72 | if (!HasQuickPlay)
73 | {
74 | QuickPlayTries = 0;
75 | }
76 | var F = Tools.GetForm();
77 | if(F!=null)
78 | {
79 | F.QPComplete(new Exception("QuickPlay was reset"));
80 | }
81 | }
82 |
83 | ///
84 | /// Attempts to obtain QuickPlay
85 | ///
86 | public static void CheckQuickPlay()
87 | {
88 | if (!File.Exists(QuickPlayPath))
89 | {
90 | ++QuickPlayTries;
91 | HasQuickPlay = false;
92 | var F = Tools.GetForm();
93 | if(F!=null)
94 | {
95 | F.QPProgress(0);
96 | }
97 | WebClient WC = new WebClient();
98 | WC.Headers.Add("User-Agent", UpdateHandler.UserAgent);
99 | WC.DownloadProgressChanged += WC_DownloadProgressChanged;
100 | WC.DownloadFileCompleted += WC_DownloadFileCompleted;
101 | try
102 | {
103 | WC.DownloadFileAsync(new Uri(QP_URL), QuickPlayPath);
104 | }
105 | catch (Exception ex)
106 | {
107 | Log.Write(new Exception("Unable to 'DownloadFileAsync' QuickPlay", ex));
108 | HasQuickPlay = false;
109 | }
110 | }
111 | else
112 | {
113 | if (!IsQuickPlayValid())
114 | {
115 | HasQuickPlay = false;
116 | //Hash invalid. Get new copy
117 | //But not immediately
118 | Thread.Sleep(1000);
119 | try
120 | {
121 | File.Delete(QuickPlayPath);
122 | CheckQuickPlay();
123 | }
124 | catch (Exception ex)
125 | {
126 | Log.Write(new Exception("Unable to retry QuickPlay Download", ex));
127 | }
128 | }
129 | else
130 | {
131 | HasQuickPlay = true;
132 | }
133 | }
134 | }
135 |
136 | private static bool IsQuickPlayValid()
137 | {
138 | if (File.Exists(QuickPlayPath))
139 | {
140 | try
141 | {
142 | using (var FS = File.OpenRead(QuickPlayPath))
143 | {
144 | return Tools.GetHash(FS) == QP_HASH;
145 | }
146 | }
147 | catch(Exception ex)
148 | {
149 | Log.Write(new Exception("Unable to test QuickPlay Hash", ex));
150 | }
151 | }
152 | return false;
153 | }
154 |
155 | private static void WC_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
156 | {
157 | var WC = (WebClient)sender;
158 | var F = Tools.GetForm();
159 | if (F != null && e.Cancelled)
160 | {
161 | F.QPComplete(new Exception("The user cancelled the operation"));
162 | }
163 | //Check QuickPlay after an update but not too often to avoid infinite loops
164 | if (QuickPlayTries < MaximumRetries)
165 | {
166 | if (F != null)
167 | {
168 | F.QPComplete(e.Error);
169 | }
170 | //On some network errors, this is fired immediately, so we wait for a second.
171 | Thread.Sleep(1000);
172 | CheckQuickPlay();
173 | }
174 | else
175 | {
176 | if (F != null)
177 | {
178 | F.QPComplete(new Exception($"QuickPlay download failed after {MaximumRetries} attempts"));
179 | }
180 | }
181 | WC.Dispose();
182 | }
183 |
184 | private static void WC_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
185 | {
186 | if (e.TotalBytesToReceive > e.BytesReceived)
187 | {
188 | var F = Tools.GetForm();
189 | if (F != null)
190 | {
191 | var Perc = (int)(e.BytesReceived * 100 / e.TotalBytesToReceive);
192 | F.QPProgress(Perc);
193 | }
194 | }
195 | }
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/SMR/Tools.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 | using System.Xml.Serialization;
4 |
5 | namespace SMRAPI
6 | {
7 | ///
8 | /// Tools to make our life easier
9 | ///
10 | internal static class Tools
11 | {
12 | ///
13 | /// Serialize an object as XML
14 | ///
15 | /// object
16 | /// XML string
17 | internal static string ToXml(this object o)
18 | {
19 | XmlSerializer S = new XmlSerializer(o.GetType());
20 | using (var MS = new MemoryStream())
21 | {
22 | S.Serialize(MS, o);
23 | return Encoding.UTF8.GetString(MS.ToArray());
24 | }
25 | }
26 |
27 | ///
28 | /// Deserialize an XML string to an object
29 | ///
30 | /// Object type
31 | /// XML string
32 | /// Deserialized object
33 | internal static T FromXml(this string s)
34 | {
35 | XmlSerializer S = new XmlSerializer(typeof(T));
36 | using (var SR = new StringReader(s))
37 | {
38 | return (T)S.Deserialize(SR);
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/SaveFileComponents/CompressedHeader.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 |
4 | namespace SatisfactorySaveEditor
5 | {
6 | public class CompressedHeader
7 | {
8 | ///
9 | /// The default chunk size utilized by the engine
10 | ///
11 | public const ulong DEFAULT_CHUNK_SIZE = 131072;
12 | ///
13 | /// Some kind of magic number
14 | ///
15 | public const ulong DEFAULT_FILE_TAG = 2653586369;
16 |
17 | ///
18 | /// File tag
19 | ///
20 | public ulong FileTag;
21 | ///
22 | /// Maximum supported chunk size
23 | ///
24 | public ulong MaxChunkSize;
25 | ///
26 | /// Length of compressed byte data
27 | ///
28 | public ulong CompressedLength;
29 | ///
30 | /// Length of decompressed byte data
31 | ///
32 | public ulong DecompressedLength;
33 |
34 | public CompressedHeader()
35 | {
36 | FileTag = DEFAULT_FILE_TAG;
37 | MaxChunkSize = DEFAULT_CHUNK_SIZE;
38 | CompressedLength = 0;
39 | DecompressedLength = 0;
40 | }
41 |
42 | public CompressedHeader(Stream Input)
43 | {
44 | using (var BR = new BinaryReader(Input, Encoding.Default, true))
45 | {
46 | FileTag = BR.ReadUInt64();
47 | MaxChunkSize = BR.ReadUInt64();
48 | CompressedLength = BR.ReadUInt64();
49 | DecompressedLength = BR.ReadUInt64();
50 | //Discard duplicate values
51 | CompressedLength = BR.ReadUInt64();
52 | DecompressedLength = BR.ReadUInt64();
53 | }
54 | }
55 |
56 | public void WriteHeader(BinaryWriter BW)
57 | {
58 | BW.Write(FileTag);
59 | BW.Write(MaxChunkSize);
60 | BW.Write(CompressedLength);
61 | BW.Write(DecompressedLength);
62 | BW.Write(CompressedLength);
63 | BW.Write(DecompressedLength);
64 | }
65 |
66 | public void WriteHeader(Stream S)
67 | {
68 | using (var BW = new BinaryWriter(S, Encoding.Default, true))
69 | {
70 | WriteHeader(BW);
71 | }
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/SaveFileComponents/ObjectTypes/GameBaseObject.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace SatisfactorySaveEditor.ObjectTypes
5 | {
6 | ///
7 | /// Serves as the base for object entries
8 | ///
9 | [Serializable]
10 | public class GameBaseObject : ICloneable
11 | {
12 | ///
13 | /// Reads the base values present in every object
14 | ///
15 | /// Open Reader
16 | internal void Fill(BinaryReader BR)
17 | {
18 | Name = BR.ReadIntString();
19 | LevelType = BR.ReadIntString();
20 | InternalName = BR.ReadIntString();
21 | }
22 |
23 | ///
24 | /// Type of object. See
25 | ///
26 | /// Maybe an enum would be better
27 | public int ObjectType { get; set; }
28 | ///
29 | /// Some Path-like internal name, probably path inside the game data file
30 | ///
31 | public string InternalName { get; set; }
32 | ///
33 | /// Level type entry. Almost always "Persistent_Level"
34 | ///
35 | public string LevelType { get; set; }
36 | ///
37 | /// Property Name
38 | ///
39 | public string Name { get; set; }
40 |
41 | ///
42 | /// Exports the base data to a stream
43 | ///
44 | /// Open Writer
45 | /// Do not call directly. Call on the child class
46 | public virtual void Export(BinaryWriter BW)
47 | {
48 | BW.WriteIntString(Name);
49 | BW.WriteIntString(LevelType);
50 | BW.WriteIntString(InternalName);
51 | }
52 |
53 | ///
54 | /// Clones the object
55 | ///
56 | /// The base object can't be cloned. Children of this type have to implement it
57 | /// -
58 | public virtual object Clone()
59 | {
60 | throw new NotImplementedException("BaseObject can't be cloned");
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/SaveFileComponents/ObjectTypes/GameObject.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace SatisfactorySaveEditor.ObjectTypes
5 | {
6 | ///
7 | /// "Constructable" Game object
8 | ///
9 | [Serializable]
10 | public class GameObject : GameBaseObject
11 | {
12 | ///
13 | /// Object rotation
14 | ///
15 | public Vector4 ObjectRotation { get; set; }
16 | ///
17 | /// Object position
18 | ///
19 | public Vector3 ObjectPosition { get; set; }
20 | ///
21 | /// Object scale
22 | ///
23 | public Vector3 ObjectScale { get; set; }
24 | ///
25 | /// Unknown integer at header end, always 1
26 | ///
27 | public int UnknownHeaderEnd { get; set; }
28 | ///
29 | /// Unknown integer at header start, always 1
30 | ///
31 | public int UnknownInt { get; set; }
32 |
33 | ///
34 | /// Creates new game object
35 | ///
36 | /// Open Reader
37 | public GameObject(BinaryReader BR)
38 | {
39 | Fill(BR);
40 | UnknownInt = BR.ReadInt32(); //Discard? always 1, maybe object type again
41 |
42 | ObjectRotation = new Vector4(BR);
43 | ObjectPosition = new Vector3(BR);
44 | ObjectScale = new Vector3(BR);
45 | UnknownHeaderEnd = BR.ReadInt32();
46 |
47 | ObjectType = OBJECT_TYPE.OBJECT;
48 | }
49 |
50 | ///
51 | /// Creates an empty game object
52 | ///
53 | public GameObject()
54 | {
55 |
56 | }
57 |
58 | ///
59 | /// Exports this game object to file
60 | ///
61 | /// Open Writer
62 | public override void Export(BinaryWriter BW)
63 | {
64 | base.Export(BW);
65 | BW.Write(UnknownInt);
66 | ObjectRotation.Export(BW);
67 | ObjectPosition.Export(BW);
68 | ObjectScale.Export(BW);
69 | BW.Write(UnknownHeaderEnd);
70 | }
71 |
72 | ///
73 | /// Clones this instance
74 | ///
75 | /// Copy
76 | public override object Clone()
77 | {
78 | var Copy = (GameObject)MemberwiseClone();
79 | Copy.ObjectPosition = (Vector3)Copy.ObjectPosition.Clone();
80 | Copy.ObjectRotation = (Vector4)Copy.ObjectRotation.Clone();
81 | Copy.ObjectScale = (Vector3)Copy.ObjectScale.Clone();
82 | return Copy;
83 | }
84 | }
85 | }
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/SaveFileComponents/ObjectTypes/GameScript.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace SatisfactorySaveEditor.ObjectTypes
5 | {
6 | ///
7 | /// Script object entry
8 | ///
9 | [Serializable]
10 | public class GameScript : GameBaseObject
11 | {
12 | ///
13 | /// Name of script
14 | ///
15 | public string ScriptName { get; set; }
16 |
17 | ///
18 | /// Initializes agame script
19 | ///
20 | /// Open Reader
21 | public GameScript(BinaryReader BR)
22 | {
23 | Fill(BR);
24 | ScriptName = BR.ReadIntString();
25 |
26 | ObjectType = OBJECT_TYPE.SCRIPT;
27 | }
28 |
29 | ///
30 | /// Creates an empty script entry
31 | ///
32 | public GameScript()
33 | {
34 |
35 | }
36 |
37 | ///
38 | /// Exports script to stream
39 | ///
40 | /// Open Writer
41 | public override void Export(BinaryWriter BW)
42 | {
43 | base.Export(BW);
44 | BW.WriteIntString(ScriptName);
45 | }
46 |
47 | ///
48 | /// Clones this instance
49 | ///
50 | /// Copy
51 | public override object Clone()
52 | {
53 | return (GameScript)MemberwiseClone();
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/SaveFileComponents/ObjectTypes/Types.cs:
--------------------------------------------------------------------------------
1 | namespace SatisfactorySaveEditor.ObjectTypes
2 | {
3 | ///
4 | /// Contains known object types
5 | ///
6 | public struct OBJECT_TYPE
7 | {
8 | ///
9 | /// Object is a script
10 | ///
11 | public const int SCRIPT = 0;
12 | ///
13 | /// Object is a "constructable" entity
14 | ///
15 | public const int OBJECT = 1;
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/SaveFileComponents/PropertyString.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace SatisfactorySaveEditor
5 | {
6 | ///
7 | /// String from the property list.
8 | ///
9 | public class PropertyString : ICloneable
10 | {
11 | ///
12 | /// String Name
13 | ///
14 | /// Names can be duplicate, so don't use them as key
15 | public string Name { get; private set; }
16 | ///
17 | /// String Value
18 | ///
19 | /// Values seem to be unique as they seem to reference objects on the map
20 | public string Value { get; private set; }
21 |
22 | ///
23 | /// Reads a string from the property list
24 | ///
25 | /// Open Reader
26 | public PropertyString(BinaryReader BR)
27 | {
28 | Name = BR.ReadIntString();
29 | Value = BR.ReadIntString();
30 | }
31 |
32 | ///
33 | /// Initializes a new property string instance
34 | ///
35 | /// String name
36 | /// String value
37 | public PropertyString(string Name,string Value)
38 | {
39 | this.Name = Name;
40 | this.Value = Value;
41 | }
42 |
43 | ///
44 | /// Writes this string to a stream
45 | ///
46 | /// Open Writer
47 | public void Export(BinaryWriter BW)
48 | {
49 | BW.WriteIntString(Name);
50 | BW.WriteIntString(Value);
51 | }
52 |
53 | ///
54 | /// Creates an independent copy of this instance
55 | ///
56 | /// new
57 | public object Clone()
58 | {
59 | return new PropertyString(string.Copy(Name), string.Copy(Value));
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/SaveFileComponents/SaveFileEntry.cs:
--------------------------------------------------------------------------------
1 | using SatisfactorySaveEditor.ObjectTypes;
2 | using System;
3 | using System.IO;
4 | using System.Xml.Serialization;
5 |
6 | namespace SatisfactorySaveEditor
7 | {
8 | ///
9 | /// Generic entry in the save file
10 | ///
11 | [XmlInclude(typeof(GameObject)),XmlInclude(typeof(GameScript))]
12 | [Serializable]
13 | public class SaveFileEntry : ICloneable
14 | {
15 | ///
16 | /// Entry specific data
17 | ///
18 | public GameBaseObject ObjectData
19 | { get; set; }
20 |
21 | ///
22 | /// Type of entry
23 | ///
24 | /// See
25 | public int EntryType
26 | { get; set; }
27 |
28 | ///
29 | /// Entry specific properties
30 | ///
31 | public byte[] Properties
32 | { get; set; }
33 |
34 | ///
35 | /// Creates an empty save file entry
36 | ///
37 | public SaveFileEntry()
38 | {
39 | Properties = new byte[0];
40 | }
41 |
42 | ///
43 | /// Reads a new entry from the stream
44 | ///
45 | /// Open Reader
46 | public SaveFileEntry(BinaryReader BR)
47 | {
48 | EntryType = BR.ReadInt32();
49 | switch (EntryType)
50 | {
51 | case OBJECT_TYPE.SCRIPT:
52 | ObjectData = new GameScript(BR);
53 | break;
54 | case OBJECT_TYPE.OBJECT:
55 | ObjectData = new GameObject(BR);
56 | break;
57 | default:
58 | throw new NotImplementedException($"Unknown Entry TYPE={EntryType} at POS={BR.BaseStream.Position - 4}");
59 | }
60 |
61 | }
62 |
63 | ///
64 | /// Export Entry to stream
65 | ///
66 | ///
67 | /// Not the as this is done later
68 | public void Export(BinaryWriter BW)
69 | {
70 | BW.Write(EntryType);
71 | ObjectData.Export(BW);
72 | }
73 |
74 | ///
75 | /// Copies this instance
76 | ///
77 | /// Fully independent clone of this instance
78 | ///
79 | /// This will not in any way change any properties,
80 | /// including those supposed to be unique
81 | ///
82 | public object Clone()
83 | {
84 | var Copy = (SaveFileEntry)MemberwiseClone();
85 | //Make sure the properties are only a copy and not a reference
86 | Copy.Properties = (byte[])Copy.Properties.Clone();
87 | Copy.ObjectData = (GameBaseObject)Copy.ObjectData.Clone();
88 | return Copy;
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/SaveFileComponents/Vector3.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace SatisfactorySaveEditor
5 | {
6 | ///
7 | /// Map coordinates for 3 dimensions
8 | ///
9 | [Serializable]
10 | public class Vector3 : ICloneable
11 | {
12 | ///
13 | /// X position
14 | ///
15 | public float X
16 | { get; set; }
17 | ///
18 | /// Y position
19 | ///
20 | public float Y
21 | { get; set; }
22 | ///
23 | /// Z position
24 | ///
25 | /// This will be the height if this instance represents map coordinates
26 | public float Z
27 | { get; set; }
28 |
29 | ///
30 | /// Reads positons
31 | ///
32 | /// Open Reader
33 | public Vector3(BinaryReader BR)
34 | {
35 | X = BR.ReadSingle();
36 | Y = BR.ReadSingle();
37 | Z = BR.ReadSingle();
38 | }
39 |
40 | ///
41 | /// Creates an empty instance for deserialization
42 | ///
43 | public Vector3() : this(0, 0, 0)
44 | {
45 | }
46 |
47 | ///
48 | /// Creates an instance with the given points
49 | ///
50 | /// X
51 | /// Y
52 | /// Z
53 | public Vector3(float x = 0f, float y = 0f, float z = 0f)
54 | {
55 | X = x;
56 | Y = y;
57 | Z = z;
58 | }
59 |
60 | ///
61 | /// Show nice coordinates
62 | ///
63 | /// Coordinates
64 | public override string ToString()
65 | {
66 | return string.Format("X={0} Y={1} Z={2}", Math.Round(X, 3), Math.Round(Y, 3), Math.Round(Z, 3));
67 | }
68 |
69 | ///
70 | /// Writes position to a stream
71 | ///
72 | /// Open Writer
73 | public void Export(BinaryWriter BW)
74 | {
75 | BW.Write(X);
76 | BW.Write(Y);
77 | BW.Write(Z);
78 | }
79 |
80 | ///
81 | /// Creates a copy of this instance
82 | ///
83 | /// Copy
84 | public object Clone()
85 | {
86 | return new Vector3(X, Y, Z);
87 | }
88 |
89 | ///
90 | /// Checks if the given Vector has the same coordinates
91 | ///
92 | /// Vector
93 | /// true if same coordinates, false if other coordinates or not same vector type
94 | public override bool Equals(object obj)
95 | {
96 | if (obj == null)
97 | {
98 | return false;
99 | }
100 | if (obj is Vector3)
101 | {
102 | var o = (Vector3)obj;
103 | return o.X == X && o.Y == Y && o.Z == Z;
104 | }
105 | return false;
106 | }
107 |
108 | public override int GetHashCode()
109 | {
110 | return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode();
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/SaveFileComponents/Vector4.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace SatisfactorySaveEditor
5 | {
6 | ///
7 | /// Coordinates for 4 dimensions
8 | ///
9 | [Serializable]
10 | public class Vector4 : ICloneable
11 | {
12 | ///
13 | /// X position
14 | ///
15 | public float X
16 | { get; set; }
17 | ///
18 | /// Y position
19 | ///
20 | public float Y
21 | { get; set; }
22 | ///
23 | /// Z position
24 | ///
25 | public float Z
26 | { get; set; }
27 | ///
28 | /// W position
29 | ///
30 | public float W
31 | { get; set; }
32 |
33 | ///
34 | /// Reads positons
35 | ///
36 | /// Open Reader
37 | public Vector4(BinaryReader BR)
38 | {
39 | X = BR.ReadSingle();
40 | Y = BR.ReadSingle();
41 | Z = BR.ReadSingle();
42 | W = BR.ReadSingle();
43 | }
44 |
45 | ///
46 | /// Creates an empty instance for deserialization
47 | ///
48 | public Vector4() : this(0, 0, 0, 0)
49 | {
50 | }
51 |
52 | ///
53 | /// Creates an instance with the given points
54 | ///
55 | /// X
56 | /// Y
57 | /// Z
58 | /// W
59 | public Vector4(float x = 0f, float y = 0f, float z = 0f, float w = 0f)
60 | {
61 | X = x;
62 | Y = y;
63 | Z = z;
64 | W = w;
65 | }
66 |
67 |
68 | ///
69 | /// Show nice coordinates
70 | ///
71 | /// Coordinates
72 | public override string ToString()
73 | {
74 | return string.Format("X={0} Y={1} Z={2} W={2}", Math.Round(X, 3), Math.Round(Y, 3), Math.Round(Z, 3), Math.Round(W, 3));
75 | }
76 |
77 | ///
78 | /// Writes position to a stream
79 | ///
80 | /// Open Writer
81 | public void Export(BinaryWriter BW)
82 | {
83 | BW.Write(X);
84 | BW.Write(Y);
85 | BW.Write(Z);
86 | BW.Write(W);
87 | }
88 |
89 | ///
90 | /// Creates a copy of this instance
91 | ///
92 | /// Copy
93 | public object Clone()
94 | {
95 | return new Vector4(X, Y, Z, W);
96 | }
97 |
98 | ///
99 | /// Checks if the given Vector has the same coordinates
100 | ///
101 | /// Vector
102 | /// true if same coordinates, false if other coordinates or not same vector type
103 | public override bool Equals(object obj)
104 | {
105 | if (obj == null)
106 | {
107 | return false;
108 | }
109 | if (obj is Vector4)
110 | {
111 | var o = (Vector4)obj;
112 | return o.W == W && o.X == X && o.Y == Y && o.Z == Z;
113 | }
114 | return false;
115 | }
116 |
117 | public override int GetHashCode()
118 | {
119 | return W.GetHashCode() ^ X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode();
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/Settings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Xml.Serialization;
4 |
5 | namespace SatisfactorySaveEditor
6 | {
7 | ///
8 | /// Provides application settings
9 | ///
10 | [Serializable]
11 | public class Settings
12 | {
13 | ///
14 | /// Automatically starts the manager on startup
15 | ///
16 | public bool AutostartManager
17 | { get; set; }
18 | ///
19 | /// Show the resize hint
20 | ///
21 | public bool ShowResizeHint
22 | { get; set; }
23 | ///
24 | /// Show the general editor limitations
25 | ///
26 | public bool ShowLimited
27 | { get; set; }
28 | ///
29 | /// Show the duplication problems hint
30 | ///
31 | public bool ShowDuplicationHint
32 | { get; set; }
33 | ///
34 | /// Show the item removal hint
35 | ///
36 | public bool ShowDeletionHint
37 | { get; set; }
38 | ///
39 | /// Shows a one time welcome message
40 | ///
41 | public bool ShowWelcomeMessage
42 | { get; set; }
43 | ///
44 | /// Shows a hint about the range deleter
45 | ///
46 | public bool ShowRangeDeleterHint
47 | { get; set; }
48 |
49 | ///
50 | /// Performs automatic update checks
51 | ///
52 | public bool AutoUpdate
53 | { get; set; }
54 |
55 | ///
56 | /// Show the changelog automatically after an update
57 | ///
58 | public bool ShowChangelog
59 | { get; set; }
60 |
61 | ///
62 | /// Uses a random Id for each time the application is launched
63 | ///
64 | public bool UseRandomId
65 | { get; set; }
66 |
67 | ///
68 | /// Disables usage reporting completely
69 | ///
70 | public bool DisableUsageReport
71 | { get; set; }
72 |
73 | ///
74 | /// Id used for usage report
75 | ///
76 | public Guid ReportId
77 | { get; set; }
78 |
79 | ///
80 | /// Key for the SMR API
81 | ///
82 | /// See https://cable.ayra.ch/satiafactory/maps/help
83 | public Guid ApiKey
84 | { get; set; }
85 |
86 | ///
87 | /// Last time an update check was performed
88 | ///
89 | public DateTime LastUpdateCheck
90 | { get; set; }
91 |
92 | ///
93 | /// The last version where the changelog was shown
94 | ///
95 | public string LastVersionLogShown
96 | { get; set; }
97 |
98 | ///
99 | /// Initializes default settings
100 | ///
101 | public Settings()
102 | {
103 | LastVersionLogShown = "0.0.0.0";
104 | LastUpdateCheck = DateTime.MinValue;
105 | //Default message status
106 | MarkDialogsRead(false);
107 | //Update settings
108 | AutoUpdate = true;
109 | //Changelog settings
110 | ShowChangelog = true;
111 | //Report settings
112 | ReportId = Guid.NewGuid();
113 | UseRandomId = false;
114 | DisableUsageReport = false;
115 | //UI Settings
116 | AutostartManager = true;
117 | }
118 |
119 | ///
120 | /// Marks all dialogs as read or unread
121 | ///
122 | /// Mark dialogs as read
123 | public void MarkDialogsRead(bool MarkAsRead)
124 | {
125 | ShowResizeHint =
126 | ShowLimited =
127 | ShowDuplicationHint =
128 | ShowDeletionHint =
129 | ShowWelcomeMessage =
130 | ShowRangeDeleterHint =
131 | !MarkAsRead;
132 |
133 | }
134 |
135 | ///
136 | /// Loads settings from a string
137 | ///
138 | /// Swettings string
139 | /// Settings instance
140 | public static Settings Load(string Content)
141 | {
142 | Log.Write("Settings: Loading settings");
143 | var S = new XmlSerializer(typeof(Settings));
144 | using (var SR = new StringReader(Content))
145 | {
146 | return (Settings)S.Deserialize(SR);
147 | }
148 | }
149 |
150 | ///
151 | /// Saves settings to a string
152 | ///
153 | /// Settings string
154 | public string Save()
155 | {
156 | Log.Write("Settings: Saving settings");
157 | var S = new XmlSerializer(typeof(Settings));
158 | using (var SW = new StringWriter())
159 | {
160 | S.Serialize(SW, this);
161 | return SW.ToString();
162 | }
163 | }
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/ShortName.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 |
4 | namespace SatisfactorySaveEditor
5 | {
6 | ///
7 | /// Provides a short name for save file entries to make the UI look nicer
8 | ///
9 | public class ShortName : IComparable
10 | {
11 | ///
12 | /// Full entry name
13 | ///
14 | public string Long
15 | { get; private set; }
16 |
17 | ///
18 | /// Short entry name
19 | ///
20 | public string Short
21 | { get; private set; }
22 |
23 | ///
24 | /// Use
25 | /// in
26 | /// instead of
27 | ///
28 | public bool UseLong
29 | { get; set; }
30 |
31 | ///
32 | /// Use
33 | /// in
34 | /// instead of
35 | ///
36 | public bool SortLong
37 | { get; set; }
38 |
39 | ///
40 | /// Initializes a short name instance from a long name instance
41 | ///
42 | /// Long name
43 | public ShortName(string LongName)
44 | {
45 | Long = LongName;
46 | //Default short name is last segment of long name without very short parts
47 | Short = string.Join(" ", LongName.Split('/', '.').Last().Split('_').Where(m => m.Length > 2));
48 |
49 | //TODO: Provide better short names
50 | SortLong = UseLong = false;
51 | }
52 |
53 | ///
54 | /// Gets the string of this instance
55 | ///
56 | /// string
57 | /// Use for configuration of this value
58 | public override string ToString()
59 | {
60 | return UseLong ? Long : Short;
61 | }
62 |
63 | ///
64 | /// Compares this object to another for sorting purposes
65 | ///
66 | /// Other object
67 | /// Sort value
68 | /// See for configuration of this value
69 | public int CompareTo(object obj)
70 | {
71 | if (obj == null)
72 | {
73 | throw new ArgumentNullException();
74 | }
75 | if (obj.GetType() != typeof(ShortName))
76 | {
77 | throw new ArgumentException("Expecting argument of type ShortName");
78 | }
79 | var o = (ShortName)obj;
80 |
81 | return SortLong ? Long.CompareTo(o.Long) : Short.CompareTo(o.Short);
82 | }
83 |
84 | public override bool Equals(object obj)
85 | {
86 | if (obj == null)
87 | {
88 | return false;
89 | }
90 | if (obj is ShortName)
91 | {
92 | return Short == ((ShortName)obj).Short;
93 | }
94 | return false;
95 | }
96 |
97 | public override int GetHashCode()
98 | {
99 | return Long.GetHashCode();
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/UI/frmApiRegister.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace SatisfactorySaveEditor
2 | {
3 | partial class frmApiRegister
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(frmApiRegister));
32 | this.btnRegister = new System.Windows.Forms.Button();
33 | this.btnOK = new System.Windows.Forms.Button();
34 | this.btnCancel = new System.Windows.Forms.Button();
35 | this.tbKey = new System.Windows.Forms.TextBox();
36 | this.label1 = new System.Windows.Forms.Label();
37 | this.SuspendLayout();
38 | //
39 | // btnRegister
40 | //
41 | this.btnRegister.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
42 | | System.Windows.Forms.AnchorStyles.Right)));
43 | this.btnRegister.Location = new System.Drawing.Point(12, 57);
44 | this.btnRegister.Name = "btnRegister";
45 | this.btnRegister.Size = new System.Drawing.Size(298, 23);
46 | this.btnRegister.TabIndex = 2;
47 | this.btnRegister.Text = "&Automatically Register";
48 | this.btnRegister.UseVisualStyleBackColor = true;
49 | this.btnRegister.Click += new System.EventHandler(this.btnRegister_Click);
50 | //
51 | // btnOK
52 | //
53 | this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
54 | this.btnOK.Location = new System.Drawing.Point(154, 88);
55 | this.btnOK.Name = "btnOK";
56 | this.btnOK.Size = new System.Drawing.Size(75, 23);
57 | this.btnOK.TabIndex = 3;
58 | this.btnOK.Text = "&OK";
59 | this.btnOK.UseVisualStyleBackColor = true;
60 | this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
61 | //
62 | // btnCancel
63 | //
64 | this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
65 | this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
66 | this.btnCancel.Location = new System.Drawing.Point(235, 88);
67 | this.btnCancel.Name = "btnCancel";
68 | this.btnCancel.Size = new System.Drawing.Size(75, 23);
69 | this.btnCancel.TabIndex = 4;
70 | this.btnCancel.Text = "&Cancel";
71 | this.btnCancel.UseVisualStyleBackColor = true;
72 | //
73 | // tbKey
74 | //
75 | this.tbKey.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
76 | | System.Windows.Forms.AnchorStyles.Right)));
77 | this.tbKey.Location = new System.Drawing.Point(12, 28);
78 | this.tbKey.Name = "tbKey";
79 | this.tbKey.Size = new System.Drawing.Size(298, 20);
80 | this.tbKey.TabIndex = 1;
81 | //
82 | // label1
83 | //
84 | this.label1.AutoSize = true;
85 | this.label1.Location = new System.Drawing.Point(9, 9);
86 | this.label1.Name = "label1";
87 | this.label1.Size = new System.Drawing.Size(299, 13);
88 | this.label1.TabIndex = 0;
89 | this.label1.Text = "Enter your API key or click the button to obtain it automatically";
90 | //
91 | // frmApiRegister
92 | //
93 | this.AcceptButton = this.btnOK;
94 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
95 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
96 | this.CancelButton = this.btnCancel;
97 | this.ClientSize = new System.Drawing.Size(322, 123);
98 | this.Controls.Add(this.label1);
99 | this.Controls.Add(this.tbKey);
100 | this.Controls.Add(this.btnCancel);
101 | this.Controls.Add(this.btnOK);
102 | this.Controls.Add(this.btnRegister);
103 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
104 | this.MaximizeBox = false;
105 | this.MinimizeBox = false;
106 | this.MinimumSize = new System.Drawing.Size(330, 150);
107 | this.Name = "frmApiRegister";
108 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
109 | this.Text = "Register API";
110 | this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.frmApiRegister_FormClosed);
111 | this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.frmApiRegister_HelpRequested);
112 | this.ResumeLayout(false);
113 | this.PerformLayout();
114 |
115 | }
116 |
117 | #endregion
118 |
119 | private System.Windows.Forms.Button btnRegister;
120 | private System.Windows.Forms.Button btnOK;
121 | private System.Windows.Forms.Button btnCancel;
122 | private System.Windows.Forms.TextBox tbKey;
123 | private System.Windows.Forms.Label label1;
124 | }
125 | }
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/UI/frmApiRegister.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Drawing;
3 | using System.Windows.Forms;
4 |
5 | namespace SatisfactorySaveEditor
6 | {
7 | public partial class frmApiRegister : Form
8 | {
9 | private Settings S;
10 | private SMRAPI.HTTP Server = null;
11 |
12 | public frmApiRegister(Settings CurrentSettings)
13 | {
14 | S = CurrentSettings;
15 | InitializeComponent();
16 | MaximumSize = new Size(int.MaxValue, MinimumSize.Height);
17 | if (S.ApiKey != Guid.Empty)
18 | {
19 | tbKey.Text = S.ApiKey.ToString();
20 | }
21 | Tools.SetupKeyHandlers(this);
22 | }
23 |
24 | private void btnRegister_Click(object sender, EventArgs e)
25 | {
26 | if (Server == null)
27 | {
28 | int Port = Tools.GetRandom(5000, 50000);
29 | Server = new SMRAPI.HTTP(Port);
30 | Log.Write("{0}: Starting HTTP server on port {1}", GetType().Name, Port);
31 | Server.Start();
32 | Server.ApiKeyEvent += delegate (object source, Guid Key)
33 | {
34 | Invoke((MethodInvoker)delegate
35 | {
36 | if (Key == Guid.Empty)
37 | {
38 | tbKey.Text = string.Empty;
39 | }
40 | else
41 | {
42 | tbKey.Text = Key.ToString();
43 | }
44 | });
45 | };
46 | }
47 | Server.OpenBrowser();
48 | }
49 |
50 | private void btnOK_Click(object sender, EventArgs e)
51 | {
52 | Guid TempKey;
53 | DialogResult = DialogResult.OK;
54 | if (string.IsNullOrEmpty(tbKey.Text))
55 | {
56 | S.ApiKey = Guid.Empty;
57 | }
58 | else if (!Guid.TryParse(tbKey.Text, out TempKey))
59 | {
60 | Log.Write($"Invalid Api Key in Text box: {tbKey.Text}");
61 | Tools.E("The entered API key is invalid. Please double check or use the button to obtain it automatically", Text);
62 | DialogResult = DialogResult.None;
63 | }
64 | else
65 | {
66 | var OldApiKey = SMRAPI.API.ApiKey;
67 | try
68 | {
69 | SMRAPI.API.ApiKey = TempKey;
70 | var I = SMRAPI.API.Info();
71 | if (I.success)
72 | {
73 | S.ApiKey = TempKey;
74 | }
75 | else
76 | {
77 | throw new Exception(I.msg);
78 | }
79 | }
80 | catch (Exception ex)
81 | {
82 | SMRAPI.API.ApiKey = OldApiKey;
83 | Tools.E("Error checking key:\r\n" + ex.Message, ex.GetType().Name);
84 | DialogResult = DialogResult.None;
85 | }
86 | }
87 | }
88 |
89 | private void frmApiRegister_FormClosed(object sender, FormClosedEventArgs e)
90 | {
91 | if (Server != null)
92 | {
93 | Log.Write("{0}: Stopping HTTP server", GetType().Name);
94 | Server.Dispose();
95 | Server = null;
96 | }
97 | }
98 |
99 | private void frmApiRegister_HelpRequested(object sender, HelpEventArgs hlpevent)
100 | {
101 | Tools.ShowHelp(GetType().Name);
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/UI/frmChangeLog.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace SatisfactorySaveEditor
2 | {
3 | partial class frmChangeLog
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(frmChangeLog));
32 | this.cbVersion = new System.Windows.Forms.ComboBox();
33 | this.tbVersion = new System.Windows.Forms.TextBox();
34 | this.SuspendLayout();
35 | //
36 | // cbVersion
37 | //
38 | this.cbVersion.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
39 | | System.Windows.Forms.AnchorStyles.Right)));
40 | this.cbVersion.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
41 | this.cbVersion.FormattingEnabled = true;
42 | this.cbVersion.Location = new System.Drawing.Point(12, 12);
43 | this.cbVersion.Name = "cbVersion";
44 | this.cbVersion.Size = new System.Drawing.Size(568, 21);
45 | this.cbVersion.TabIndex = 0;
46 | this.cbVersion.SelectedIndexChanged += new System.EventHandler(this.cbVersion_SelectedIndexChanged);
47 | //
48 | // tbVersion
49 | //
50 | this.tbVersion.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
51 | | System.Windows.Forms.AnchorStyles.Left)
52 | | System.Windows.Forms.AnchorStyles.Right)));
53 | this.tbVersion.BackColor = System.Drawing.SystemColors.Window;
54 | this.tbVersion.Location = new System.Drawing.Point(12, 39);
55 | this.tbVersion.Multiline = true;
56 | this.tbVersion.Name = "tbVersion";
57 | this.tbVersion.ReadOnly = true;
58 | this.tbVersion.ScrollBars = System.Windows.Forms.ScrollBars.Both;
59 | this.tbVersion.Size = new System.Drawing.Size(568, 522);
60 | this.tbVersion.TabIndex = 1;
61 | //
62 | // frmChangeLog
63 | //
64 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
65 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
66 | this.ClientSize = new System.Drawing.Size(592, 573);
67 | this.Controls.Add(this.tbVersion);
68 | this.Controls.Add(this.cbVersion);
69 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
70 | this.Name = "frmChangeLog";
71 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
72 | this.Text = "Change Log";
73 | this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.frmChangeLog_HelpRequested);
74 | this.ResumeLayout(false);
75 | this.PerformLayout();
76 |
77 | }
78 |
79 | #endregion
80 |
81 | private System.Windows.Forms.ComboBox cbVersion;
82 | private System.Windows.Forms.TextBox tbVersion;
83 | }
84 | }
--------------------------------------------------------------------------------
/SatisfactorySaveEditor/UI/frmChangeLog.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 | using System.Linq;
4 | using System.Windows.Forms;
5 |
6 | namespace SatisfactorySaveEditor
7 | {
8 | public partial class frmChangeLog : Form
9 | {
10 | public frmChangeLog()
11 | {
12 | InitializeComponent();
13 | cbVersion.Items.AddRange(Tools.GetVersionFiles().OrderByDescending(m => m).Cast