-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathParser.linq
254 lines (201 loc) · 6.92 KB
/
Parser.linq
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
<Query Kind="Program">
<NuGetReference>Sprache</NuGetReference>
<Namespace>Sprache</Namespace>
</Query>
void Main()
{
//parameters
string currentUser = "dj"; //not case sensitive
var localSaveGameFolder = @"C:\Program Files (x86)\Steam\steamapps\common\Kerbal Space Program\saves\test";
var userSpecificFolder = Path.Combine(localSaveGameFolder, "userdata");
var userSpecificFile = Path.Combine(userSpecificFolder, currentUser + ".sfs");
var fileToSync = "persistent.sfs"; //also try quickssave
//setup
if (!Directory.Exists(userSpecificFolder)) Directory.CreateDirectory(userSpecificFolder);
var fileConverter = new KspFileReader(); //loads and saves the format that sfs is. can read other file types, like .craft
//load up my saved game
var saveGameData = fileConverter.ReadFile(Path.Combine(localSaveGameFolder, fileToSync)); //get data
var myUniverse = new Universe(saveGameData); //convert to "smart data" (understands the contents a bit)
//clean it
myUniverse.RemoveVessels(v => v.IsOwnedBy(currentUser));
//resave as the "clean" version under my usename
fileConverter.SaveFile(myUniverse.ToSaveGameObject(), userSpecificFile);
}
public class KspFileReader
{
Parser<SaveGameObject> parser;
public KspFileReader()
{
var newlineChars = Environment.NewLine.ToCharArray();
var identifierChars = new char[] { '_' };
var identifier = Parse.Char(c => char.IsLetterOrDigit(c) || identifierChars.Contains(c) ,"identifier character").Many().Token().Text();
var separator = Parse.String("= ");
var newline = Parse.String(Environment.NewLine).Text();
var value = Parse.Char(c => !newlineChars.Contains(c), "non-newline").Many().Text();
var beginBlock = Parse.Char('{').Token();
var endBlock = Parse.Char('}').Token();
var parameter = from i in identifier
from _ in separator
from v in value
from _2 in newline
select new SaveGameParameter() { Name = i, Value = v};
Parser<SaveGameObject> section = null;
section = from sectionName in identifier
from _2 in beginBlock
from parameters in parameter.Many().Select(x => x.ToList())
from sections in Parse.Ref(() => section).Many().Select(x => x.ToList())
from _3 in endBlock
select new SaveGameObject() { Name = sectionName, Parameters = parameters, SubObjects = sections };
parser = section;
}
public SaveGameObject ReadFile(string filename)
{
using (var file = new StreamReader(filename))
{
return parser.Parse(file.ReadToEnd());
}
}
public void SaveFile(SaveGameObject data, string filename)
{
using (var writer = new StreamWriter(filename))
{
data.WriteToStream(writer);
}
}
}
public class Universe
{
public IList<Vessel> Vessels { get; set; }
private SaveGameObject SaveGameObject { get; set; }
public Vessel ActiveVessel { get; set; }
public Universe(SaveGameObject saveGameObject)
{
SaveGameObject = saveGameObject;
Prepare();
}
public void ImportVessels(Universe otherUniverse)
{
foreach (var v in otherUniverse.Vessels)
{
Vessels.Add(v);
}
}
public void RemoveVessels(Predicate<Vessel> condition)
{
var vesselsToRemove = (
from v in Vessels
where condition(v)
select v).ToList();
vesselsToRemove.ForEach(v => Vessels.Remove(v));
}
public SaveGameObject ToSaveGameObject()
{
List<Crew> crewRoster = new List<Crew>();
//add each USED crew member to the list
foreach (var vessel in Vessels)
{
crewRoster.AddRange(vessel.Crew);
}
var flightState = (from fs in SaveGameObject.SubObjects where fs.Name == "FLIGHTSTATE"
select fs).Single();
flightState.SubObjects = new List<SaveGameObject>(); //only crew and vessels knows. just make a new collection
//save all crew members used
foreach (var crewMember in crewRoster)
{
flightState.SubObjects.Add(crewMember.ToSaveGameObject());
}
//save all ships (they fix their own crew indexes)
foreach (var vessel in Vessels)
{
flightState.SubObjects.Add(vessel.ToSaveGameObject(crewRoster));
}
return SaveGameObject;
}
private void Prepare()
{
var flightState = (from fs in SaveGameObject.SubObjects where fs.Name == "FLIGHTSTATE" select fs).Single();
//get all crew as loaded. needed for vessels
var crewList =
(from crew in flightState.SubObjects where crew.Name == "CREW"
select new Crew(crew))
.ToList();
//get all vessels
Vessels =
(from vessel in flightState.SubObjects where vessel.Name == "VESSEL"
select new Vessel(crewList, vessel))
.ToList();
var activeVesselIndex = int.Parse(flightState.Parameters.Where(p => p.Name == "activeVessel").Single().Value);
ActiveVessel = Vessels[activeVesselIndex];
}
}
public class Vessel
{
SaveGameObject o;
public IList<Crew> Crew { get; set; }
public Vessel(IList<Crew> crewList, SaveGameObject saveGameObject)
{
this.o = saveGameObject;
Crew = new List<Crew>();
//add used crewmembers to the list
( from part in o.SubObjects where part.Name == "PART"
from p in part.Parameters where p.Name == "crew"
select crewList[int.Parse(p.Value)]).ToList().ForEach(c => Crew.Add(c));
}
public bool IsOwnedBy(string name)
{
return o.Parameters.Where(p => p.Name == "name").Single().Value.ToUpper().StartsWith(name.ToUpper() + ":");
}
public SaveGameObject ToSaveGameObject(IList<Crew> crewList)
{
var crewParameters = (from part in o.SubObjects where part.Name == "PART"
from p in part.Parameters where p.Name == "crew"
select p).ToList();
for (int i = 0; i < Crew.Count; i++)
{
//set each one to the new index
crewParameters[i].Value = crewList.IndexOf(Crew[i]).ToString();
}
return o;
}
}
public class Crew
{
SaveGameObject o;
public Crew(SaveGameObject saveGameObject)
{
this.o = saveGameObject;
}
public SaveGameObject ToSaveGameObject()
{
return o;
}
}
public class SaveGameObject
{
public string Name { get; set; }
public IList<SaveGameParameter> Parameters { get; set; }
public IList<SaveGameObject> SubObjects { get; set; }
public void WriteToStream(StreamWriter writer, int level = 0)
{
var linePrefix = new string('\t', level);
var subLevel = level + 1;
var subLevelPrefix = new string('\t', subLevel);
writer.WriteLine("{0}{1}", linePrefix, Name);
writer.WriteLine("{0}{{", linePrefix);
foreach (var parameter in Parameters)
{
writer.WriteLine("{0}{1} = {2}", subLevelPrefix, parameter.Name, parameter.Value);
}
foreach (var subObject in SubObjects)
{
subObject.WriteToStream(writer, subLevel);
}
writer.WriteLine("{0}}}", linePrefix);
}
}
public class SaveGameParameter //used instead of tuple so i can do reference checks.
{
public string Name { get; set; }
public string Value { get; set; }
}
// Define other methods and classes here