-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathProgram.cs
137 lines (117 loc) · 4.13 KB
/
Program.cs
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
// See https://aka.ms/new-console-template for more information
using System.Diagnostics;
using System.Globalization;
using System.IO.Compression;
using System.Net.Http.Headers;
using System.Text.Json;
using CsvHelper;
using CsvHelper.Configuration;
using Lumina;
using PathMapper;
if (args.Length != 4) {
Console.WriteLine("args: <path to sqpack> <path to rl2 csv> <path to tc bnpc data> <output json path>");
return;
}
var gamePath = args[0];
var pathsCsv = args[1];
var bnpcJson = args[2];
var outputPath = args[3];
using var client = new HttpClient();
async Task<string[]> GetPaths(HttpClient client) {
Console.WriteLine("downloading paths");
await using var pathsStream = await client.GetStreamAsync("https://rl2.perchbird.dev/download/export/CurrentPathList.gz");
await using var gzip = new GZipStream(pathsStream, CompressionMode.Decompress);
using var reader = new StreamReader(gzip);
var paths = await reader.ReadToEndAsync();
return paths
.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries)
.OrderBy(path => path)
.ToArray();
}
BNpcContainer bnpcs;
if (bnpcJson == "auto") {
Console.WriteLine("downloading bnpc info");
var message = new HttpRequestMessage(HttpMethod.Post, "https://api.ffxivteamcraft.com/gubal");
message.Content = new StringContent(JsonSerializer.Serialize(new {
query = "query { bnpc { bnpcBase, bnpcName } }",
}));
message.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var json = await (await client.SendAsync(message)).Content.ReadAsStringAsync();
bnpcs = JsonSerializer.Deserialize<GraphqlContainer<BNpcContainer>>(json)!.data;
} else {
await using var bnpcFile = File.OpenRead(bnpcJson);
bnpcs = (await JsonSerializer.DeserializeAsync<BNpcContainer>(bnpcFile))!;
}
var luminaSettings = new LuminaOptions {
PanicOnSheetChecksumMismatch = false,
};
var gameData = new GameData(gamePath, luminaSettings);
var identifier = new ObjectIdentification(gameData, new GamePathParser(gameData), bnpcs);
string[] allPaths;
if (pathsCsv == "auto") {
allPaths = await GetPaths(client);
} else {
await using var file = File.Open(pathsCsv, FileMode.Open);
using var csv = new CsvReader(new StreamReader(file), new CsvConfiguration(CultureInfo.InvariantCulture) {
HasHeaderRecord = true,
});
allPaths = csv.GetRecords<Record>()
.Select(record => record.path)
.OrderBy(path => path)
.ToArray();
}
// change this to something like
// {
// "mdl": {
// "_comment1": "mdl uses two matchers: primary id and secondary id",
// "_comment2": "358 is primary id, 1 is secondary id",
//
// "358": {
// "1": [
// "Some Object Name"
// ]
// }
// },
// "tex": { ... }
// }
Console.WriteLine($"[{0:000.00}s] {0:N2}% - {0:N0}/{allPaths.Length:N0}");
var affects = new Dictionary<string, List<string>>();
var stopwatch = new Stopwatch();
stopwatch.Start();
var lastSeconds = 0u;
for (var i = 0; i < allPaths.Length; i++) {
var elapsed = (uint) stopwatch.Elapsed.TotalSeconds;
if (elapsed > lastSeconds + 2) {
Console.WriteLine($"[{stopwatch.Elapsed.TotalSeconds:000.00}s] {(float) i / allPaths.Length * 100:N2}% - {i:N0}/{allPaths.Length:N0}");
lastSeconds = elapsed;
}
var path = allPaths[i];
foreach (var (key, _) in identifier.Identify(path)) {
if (affects.TryGetValue(path, out var affected)) {
affected.Add(key);
} else {
affects[path] = new List<string> { key };
}
}
}
Console.WriteLine($"processing took {stopwatch.Elapsed}");
await using var output = File.Create(outputPath);
await JsonSerializer.SerializeAsync(output, affects);
public class Record {
public int hash { get; set; }
public int index { get; set; }
public string path { get; set; }
}
[Serializable]
public class GraphqlContainer<T> {
public T data { get; set; }
}
[Serializable]
public class BNpcContainer {
public List<BNpcMapEntry> bnpc { get; set; }
}
[Serializable]
public class BNpcMapEntry {
public uint bnpcBase { get; set; }
public uint bnpcName { get; set; }
}