Skip to content

Commit 06aac19

Browse files
committed
Add LE1 DLC to TOC, fix bugs, update to 2.2
1 parent 5f51e74 commit 06aac19

4 files changed

Lines changed: 412 additions & 41 deletions

File tree

AutoTOC/AutoTOC.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
<Reference Include="System.Xml" />
6464
</ItemGroup>
6565
<ItemGroup>
66+
<Compile Include="DuplicatingIni.cs" />
6667
<Compile Include="Program.cs" />
6768
<Compile Include="Properties\AssemblyInfo.cs" />
6869
<Compile Include="StreamIO.cs" />

AutoTOC/DuplicatingIni.cs

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Globalization;
5+
using System.IO;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
10+
namespace AutoTOC
11+
{
12+
13+
/*
14+
* This is modified code from the LegendaryExplorerCore codebase, which was in turn taken from ALOT Installer V4
15+
* Taken from LEC on 2021/7/17
16+
*/
17+
18+
public class DuplicatingIni
19+
{
20+
public Section this[string sectionName]
21+
{
22+
get
23+
{
24+
var existingSection = Sections.FirstOrDefault(x => x.Header == sectionName);
25+
if (existingSection != null) return existingSection;
26+
var ns = new Section()
27+
{
28+
Header = sectionName
29+
};
30+
Sections.Add(ns);
31+
return ns;
32+
}
33+
set
34+
{
35+
var sectionToReplace = Sections.FirstOrDefault(x => x.Header == sectionName);
36+
if (sectionToReplace != null)
37+
{
38+
Sections.Remove(sectionToReplace);
39+
}
40+
Sections.Add(value);
41+
}
42+
}
43+
44+
public List<Section> Sections = new List<Section>();
45+
46+
public IniEntry GetValue(string sectionname, string key)
47+
{
48+
var section = GetSection(sectionname);
49+
return section?.GetValue(key);
50+
}
51+
52+
public Section GetSection(string sectionname)
53+
{
54+
return Sections.FirstOrDefault(x => x.Header.Equals(sectionname, StringComparison.InvariantCultureIgnoreCase));
55+
}
56+
57+
public Section GetOrAddSection(string sectionname)
58+
{
59+
var s = GetSection(sectionname);
60+
if (s != null) return s;
61+
s = new Section() { Header = sectionname };
62+
Sections.Add(s);
63+
return s;
64+
}
65+
66+
public Section GetSection(Section section)
67+
{
68+
return Sections.FirstOrDefault(x => x.Header.Equals(section.Header, StringComparison.InvariantCultureIgnoreCase));
69+
}
70+
71+
/// <summary>
72+
/// Loads an ini file from disk
73+
/// </summary>
74+
/// <param name="iniFile"></param>
75+
/// <returns></returns>
76+
public static DuplicatingIni LoadIni(string iniFile)
77+
{
78+
return ParseIni(File.ReadAllText(iniFile));
79+
}
80+
81+
public static DuplicatingIni ParseIni(string iniText)
82+
{
83+
DuplicatingIni di = new DuplicatingIni();
84+
var splits = iniText.Split('\n');
85+
Section currentSection = null;
86+
foreach (var line in splits)
87+
{
88+
string trimmed = line.Trim();
89+
if (string.IsNullOrWhiteSpace(trimmed)) continue; //blank line
90+
if (trimmed.StartsWith("[") && trimmed.EndsWith("]"))
91+
{
92+
//New section
93+
currentSection = new Section()
94+
{
95+
Header = trimmed.Trim('[', ']')
96+
};
97+
di.Sections.Add(currentSection);
98+
}
99+
else if (currentSection == null)
100+
{
101+
continue; //this parser only supports section items
102+
}
103+
else
104+
{
105+
currentSection.Entries.Add(new IniEntry(trimmed));
106+
}
107+
}
108+
return di;
109+
}
110+
111+
112+
/// <summary>
113+
/// Converts this DuplicatingIni object into an ini file as a string.
114+
/// </summary>
115+
/// <returns></returns>
116+
public override string ToString()
117+
{
118+
StringBuilder sb = new StringBuilder();
119+
bool isFirst = true;
120+
foreach (var section in Sections)
121+
{
122+
if (!section.Entries.Any())
123+
{
124+
continue; //Do not write out empty sections.
125+
}
126+
if (isFirst)
127+
{
128+
isFirst = false;
129+
}
130+
else
131+
{
132+
sb.Append("\n");
133+
}
134+
sb.Append($"[{section.Header}]");
135+
sb.Append("\n"); //AppendLine does \r\n which we don't want.
136+
foreach (var line in section.Entries)
137+
{
138+
if (line.HasValue)
139+
{
140+
sb.Append($"{line.Key}={line.Value}");
141+
sb.Append("\n"); //AppendLine does \r\n which we don't want.
142+
}
143+
else
144+
{
145+
sb.Append(line.RawText);
146+
sb.Append("\n"); //AppendLine does \r\n which we don't want.
147+
}
148+
}
149+
}
150+
151+
return sb.ToString();
152+
}
153+
154+
/// <summary>
155+
/// Writes this ini file out to a file using the ToString() method
156+
/// </summary>
157+
/// <param name="filePath"></param>
158+
/// <param name="encoding"></param>
159+
public void WriteToFile(string filePath, Encoding encoding = null)
160+
{
161+
WriteToFile(filePath, ToString(), encoding);
162+
}
163+
164+
/// <summary>
165+
/// Writes a specified ini string out to a file
166+
/// </summary>
167+
/// <param name="filePath"></param>
168+
/// <param name="iniString"></param>
169+
/// <param name="encoding"></param>
170+
public void WriteToFile(string filePath, string iniString, Encoding encoding = null)
171+
{
172+
if(encoding == null) encoding = Encoding.UTF8;
173+
174+
FileStream fs = File.Open(filePath, FileMode.Create, FileAccess.Write);
175+
StreamWriter sr = new StreamWriter(fs, encoding);
176+
sr.Write(iniString);
177+
}
178+
179+
[DebuggerDisplay("Ini Section [{Header}] with {Entries.Count} entries")]
180+
public class Section
181+
{
182+
public string Header;
183+
public List<IniEntry> Entries = new List<IniEntry>();
184+
185+
public IniEntry GetValue(string key)
186+
{
187+
return Entries.FirstOrDefault(x => x.Key != null && x.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase));
188+
}
189+
190+
public IniEntry this[string keyname]
191+
{
192+
get
193+
{
194+
var firstExistingEntry = Entries.FirstOrDefault(x => x.Key == keyname);
195+
if (firstExistingEntry != null) return firstExistingEntry;
196+
197+
var ne = new IniEntry(keyname, "");
198+
Entries.Add(ne);
199+
return ne;
200+
}
201+
set
202+
{
203+
var keyToReplace = Entries.FirstOrDefault(x => x.Key == keyname);
204+
if (keyToReplace != null)
205+
{
206+
Entries.Remove(keyToReplace);
207+
}
208+
Entries.Add(value);
209+
}
210+
}
211+
212+
public void SetSingleEntry(string key, string value)
213+
{
214+
RemoveAllNamedEntries(key);
215+
Entries.Add(new IniEntry(key, value));
216+
}
217+
218+
public void SetSingleEntry(string key, int value)
219+
{
220+
RemoveAllNamedEntries(key);
221+
Entries.Add(new IniEntry(key, value.ToString()));
222+
}
223+
224+
public void SetSingleEntry(string key, float value)
225+
{
226+
RemoveAllNamedEntries(key);
227+
Entries.Add(new IniEntry(key, value.ToString(CultureInfo.InvariantCulture)));
228+
}
229+
230+
/// <summary>
231+
/// Removes all entries from this section with the specified name. If the name is not specified, all entries are removed.
232+
/// </summary>
233+
/// <param name="name"></param>
234+
public void RemoveAllNamedEntries(string name = null)
235+
{
236+
if (name != null)
237+
{
238+
Entries.RemoveAll(x => x.Key == name);
239+
}
240+
else
241+
{
242+
Entries.Clear();
243+
}
244+
}
245+
}
246+
247+
[DebuggerDisplay("IniEntry {Key} = {Value}")]
248+
249+
public class IniEntry
250+
{
251+
public string RawText;
252+
253+
public bool HasValue => Key != null && Value != null;
254+
255+
public IniEntry(string line)
256+
{
257+
RawText = line;
258+
Key = KeyPair.Key;
259+
Value = KeyPair.Value;
260+
}
261+
public IniEntry(string key, string value)
262+
{
263+
RawText = $"{key}={value}";
264+
Key = KeyPair.Key;
265+
Value = KeyPair.Value;
266+
}
267+
268+
public string Key { get; set; }
269+
270+
public string Value { get; set; }
271+
272+
public KeyValuePair<string, string> KeyPair
273+
{
274+
get
275+
{
276+
var separator = RawText.IndexOf('=');
277+
if (separator > 0)
278+
{
279+
string key = RawText.Substring(0, separator).Trim();
280+
string value = RawText.Substring(separator + 1).Trim();
281+
return new KeyValuePair<string, string>(key, value);
282+
}
283+
return new KeyValuePair<string, string>(null, null);
284+
}
285+
}
286+
}
287+
}
288+
}

AutoTOC/Program.cs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ static void GenerateTocFromGamedir(string gameDir, MEGame game)
7777
string baseDir = Path.Combine(gameDir, @"BIOGame\");
7878
string dlcDir = Path.Combine(baseDir, @"DLC\");
7979
List<string> folders = new List<string>();
80-
folders.Add(baseDir);
8180
if (game != MEGame.LE1)
8281
{
8382
if(Directory.Exists(dlcDir))
@@ -89,18 +88,34 @@ static void GenerateTocFromGamedir(string gameDir, MEGame game)
8988
Console.WriteLine("DLC folder not detected, TOCing basegame only...");
9089
}
9190
}
92-
Task.WhenAll(folders.Select(loc => TOCAsync(loc, game))).Wait();
91+
Task.WhenAll(folders.Select(loc => TOCDLCAsync(loc, game)).Prepend(TOCBasegameAsync(baseDir, game))).Wait();
9392
}
9493

95-
static Task TOCAsync(string tocLoc, MEGame game)
94+
static Task TOCBasegameAsync(string tocLoc, MEGame game)
9695
{
97-
return Task.Run(() => CreateTOC(tocLoc, game));
96+
return Task.Run(() =>
97+
{
98+
var TOC = TOCCreator.CreateBasegameTOCForDirectory(tocLoc, game);
99+
TOC.WriteToFile(Path.Combine(tocLoc, "PCConsoleTOC.bin"));
100+
});
101+
}
102+
103+
static Task TOCDLCAsync(string tocLoc, MEGame game)
104+
{
105+
return Task.Run(() => CreateDLCTOC(tocLoc, game));
98106
}
99107

100-
static void CreateTOC(string tocLoc, MEGame game)
108+
static void CreateDLCTOC(string tocLoc, MEGame game)
101109
{
102-
var TOC = TOCCreator.CreateTOCForDirectory(tocLoc, game);
103-
TOC.WriteToFile(Path.Combine(tocLoc, "PCConsoleTOC.bin"));
110+
try
111+
{
112+
var TOC = TOCCreator.CreateDLCTOCForDirectory(tocLoc, game);
113+
TOC.WriteToFile(Path.Combine(tocLoc, "PCConsoleTOC.bin"));
114+
}
115+
catch (Exception e)
116+
{
117+
Console.WriteLine($"No TOCable files in {tocLoc}, may just be packed DLC.");
118+
}
104119
}
105120

106121
static string[] ValidExecutables = { "MassEffect1.exe", "MassEffect2.exe", "MassEffect3.exe" };

0 commit comments

Comments
 (0)