639b0b7f59
Add native IK-style capture processing, Artifact Inventory, explicit promotion and single-result review. Confirm the three live OCR corrections in the eval corpus and preserve extraction/value separation.
275 lines
10 KiB
C#
275 lines
10 KiB
C#
using System.Text.Json;
|
|
|
|
namespace GenshinAssistant.InputHelper;
|
|
|
|
internal static class IkInventoryLists
|
|
{
|
|
public static IkInventoryListStatus Load(string dataDir)
|
|
{
|
|
var dir = ResolveDirectory(dataDir);
|
|
var required = new[] { "artifacts.json", "weapons.json", "characters.json", "materials.json", "version.txt" };
|
|
var missing = required.Where(file => !File.Exists(Path.Combine(dir, file))).ToArray();
|
|
var status = new IkInventoryListStatus { Directory = dir, Missing = missing };
|
|
if (missing.Length > 0) return status;
|
|
|
|
status.Version = File.ReadAllText(Path.Combine(dir, "version.txt")).Trim();
|
|
status.ArtifactSets = CountJsonObjectProperties(Path.Combine(dir, "artifacts.json"));
|
|
status.ArtifactPieces = CountArtifactPieces(Path.Combine(dir, "artifacts.json"));
|
|
status.Weapons = CountJsonObjectProperties(Path.Combine(dir, "weapons.json"));
|
|
status.Characters = CountJsonObjectProperties(Path.Combine(dir, "characters.json"));
|
|
status.Materials = CountJsonObjectProperties(Path.Combine(dir, "materials.json"));
|
|
return status;
|
|
}
|
|
|
|
private static string ResolveDirectory(string dataDir)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(dataDir)) return dataDir;
|
|
|
|
var candidates = new List<string>();
|
|
var envDir = Environment.GetEnvironmentVariable("IK_INVENTORYLISTS_DIR");
|
|
if (!string.IsNullOrWhiteSpace(envDir)) candidates.Add(envDir);
|
|
|
|
AddDirectoryCandidates(candidates, AppContext.BaseDirectory);
|
|
AddDirectoryCandidates(candidates, Environment.CurrentDirectory);
|
|
|
|
var baseParent = Directory.GetParent(AppContext.BaseDirectory);
|
|
for (var depth = 0; depth < 6 && baseParent != null; depth++)
|
|
{
|
|
AddDirectoryCandidates(candidates, baseParent.FullName);
|
|
baseParent = baseParent.Parent;
|
|
}
|
|
|
|
return candidates.FirstOrDefault(IsCompleteInventoryListDirectory)
|
|
?? Path.Combine(AppContext.BaseDirectory, "inventorylists");
|
|
}
|
|
|
|
private static void AddDirectoryCandidates(List<string> candidates, string root)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(root)) return;
|
|
candidates.Add(Path.Combine(root, "inventorylists"));
|
|
candidates.Add(Path.Combine(root, "ik-inventorylists"));
|
|
candidates.Add(Path.Combine(root, "data", "ik-inventorylists"));
|
|
}
|
|
|
|
private static bool IsCompleteInventoryListDirectory(string dir)
|
|
{
|
|
return File.Exists(Path.Combine(dir, "artifacts.json"))
|
|
&& File.Exists(Path.Combine(dir, "weapons.json"))
|
|
&& File.Exists(Path.Combine(dir, "characters.json"))
|
|
&& File.Exists(Path.Combine(dir, "materials.json"))
|
|
&& File.Exists(Path.Combine(dir, "version.txt"));
|
|
}
|
|
|
|
public static object CatalogPayload(string dataDir)
|
|
{
|
|
var status = Load(dataDir);
|
|
if (!status.Valid)
|
|
{
|
|
return new
|
|
{
|
|
data = status.ToPayload(),
|
|
artifacts = Array.Empty<object>(),
|
|
weapons = Array.Empty<object>(),
|
|
characters = Array.Empty<object>(),
|
|
materials = Array.Empty<object>(),
|
|
};
|
|
}
|
|
|
|
return new
|
|
{
|
|
data = status.ToPayload(),
|
|
artifacts = LoadArtifactCatalog(Path.Combine(status.Directory, "artifacts.json")),
|
|
weapons = LoadStringMapCatalog(Path.Combine(status.Directory, "weapons.json")),
|
|
characters = LoadCharacterCatalog(Path.Combine(status.Directory, "characters.json")),
|
|
materials = LoadStringMapCatalog(Path.Combine(status.Directory, "materials.json")),
|
|
};
|
|
}
|
|
|
|
private static int CountJsonObjectProperties(string path)
|
|
{
|
|
using var doc = JsonDocument.Parse(File.ReadAllText(path));
|
|
return doc.RootElement.ValueKind == JsonValueKind.Object
|
|
? doc.RootElement.EnumerateObject().Count()
|
|
: 0;
|
|
}
|
|
|
|
private static int CountArtifactPieces(string path)
|
|
{
|
|
using var doc = JsonDocument.Parse(File.ReadAllText(path));
|
|
if (doc.RootElement.ValueKind != JsonValueKind.Object) return 0;
|
|
var count = 0;
|
|
foreach (var set in doc.RootElement.EnumerateObject())
|
|
{
|
|
if (!set.Value.TryGetProperty("artifacts", out var artifacts)) continue;
|
|
if (artifacts.ValueKind == JsonValueKind.Object) count += artifacts.EnumerateObject().Count();
|
|
}
|
|
return count;
|
|
}
|
|
|
|
private static List<object> LoadStringMapCatalog(string path)
|
|
{
|
|
using var doc = JsonDocument.Parse(File.ReadAllText(path));
|
|
var entries = new List<object>();
|
|
if (doc.RootElement.ValueKind != JsonValueKind.Object) return entries;
|
|
foreach (var entry in doc.RootElement.EnumerateObject())
|
|
{
|
|
entries.Add(new
|
|
{
|
|
normalizedName = entry.Name,
|
|
good = entry.Value.ValueKind == JsonValueKind.String ? entry.Value.GetString() ?? "" : "",
|
|
});
|
|
}
|
|
return entries;
|
|
}
|
|
|
|
private static List<object> LoadCharacterCatalog(string path)
|
|
{
|
|
using var doc = JsonDocument.Parse(File.ReadAllText(path));
|
|
var entries = new List<object>();
|
|
if (doc.RootElement.ValueKind != JsonValueKind.Object) return entries;
|
|
foreach (var entry in doc.RootElement.EnumerateObject())
|
|
{
|
|
var value = entry.Value;
|
|
entries.Add(new
|
|
{
|
|
normalizedName = entry.Name,
|
|
good = JsonString(value, "GOOD"),
|
|
element = JsonFirstString(value, "Element"),
|
|
weaponType = JsonFirstInt(value, "WeaponType"),
|
|
constellationName = JsonFirstString(value, "ConstellationName"),
|
|
});
|
|
}
|
|
return entries;
|
|
}
|
|
|
|
private static List<object> LoadArtifactCatalog(string path)
|
|
{
|
|
using var doc = JsonDocument.Parse(File.ReadAllText(path));
|
|
var entries = new List<object>();
|
|
if (doc.RootElement.ValueKind != JsonValueKind.Object) return entries;
|
|
foreach (var set in doc.RootElement.EnumerateObject())
|
|
{
|
|
var pieces = new List<object>();
|
|
if (set.Value.TryGetProperty("artifacts", out var artifacts) && artifacts.ValueKind == JsonValueKind.Object)
|
|
{
|
|
foreach (var piece in artifacts.EnumerateObject())
|
|
{
|
|
pieces.Add(new
|
|
{
|
|
slot = piece.Name,
|
|
artifactName = JsonString(piece.Value, "artifactName"),
|
|
good = JsonString(piece.Value, "GOOD"),
|
|
normalizedName = JsonString(piece.Value, "normalizedName"),
|
|
});
|
|
}
|
|
}
|
|
|
|
entries.Add(new
|
|
{
|
|
normalizedName = set.Name,
|
|
setName = JsonString(set.Value, "setName"),
|
|
good = JsonString(set.Value, "GOOD"),
|
|
pieces,
|
|
});
|
|
}
|
|
return entries;
|
|
}
|
|
|
|
private static string JsonString(JsonElement value, string propertyName)
|
|
=> value.TryGetProperty(propertyName, out var property) && property.ValueKind == JsonValueKind.String
|
|
? property.GetString() ?? ""
|
|
: "";
|
|
|
|
private static string JsonFirstString(JsonElement value, string propertyName)
|
|
{
|
|
if (!value.TryGetProperty(propertyName, out var property)) return "";
|
|
if (property.ValueKind == JsonValueKind.String) return property.GetString() ?? "";
|
|
if (property.ValueKind == JsonValueKind.Array && property.GetArrayLength() > 0)
|
|
{
|
|
var first = property[0];
|
|
return first.ValueKind == JsonValueKind.String ? first.GetString() ?? "" : "";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
private static int JsonFirstInt(JsonElement value, string propertyName)
|
|
{
|
|
if (!value.TryGetProperty(propertyName, out var property)) return -1;
|
|
if (property.ValueKind == JsonValueKind.Number && property.TryGetInt32(out var number)) return number;
|
|
if (property.ValueKind == JsonValueKind.Array && property.GetArrayLength() > 0)
|
|
{
|
|
var first = property[0];
|
|
return first.ValueKind == JsonValueKind.Number && first.TryGetInt32(out number) ? number : -1;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
internal sealed class IkInventoryListStatus
|
|
{
|
|
public string Directory { get; init; } = "";
|
|
public string Version { get; set; } = "";
|
|
public int ArtifactSets { get; set; }
|
|
public int ArtifactPieces { get; set; }
|
|
public int Weapons { get; set; }
|
|
public int Characters { get; set; }
|
|
public int Materials { get; set; }
|
|
public string[] Missing { get; init; } = Array.Empty<string>();
|
|
public bool Valid => Missing.Length == 0;
|
|
|
|
public object SupportedCategoriesPayload() => new
|
|
{
|
|
artifacts = ArtifactCategoryPayload("artifacts.json", ArtifactSets, ArtifactPieces),
|
|
weapons = SimpleCategoryPayload("weapons.json", Weapons),
|
|
characters = SimpleCategoryPayload("characters.json", Characters),
|
|
materials = SimpleCategoryPayload("materials.json", Materials),
|
|
};
|
|
|
|
private object ArtifactCategoryPayload(string file, int setCount, int pieceCount)
|
|
{
|
|
var catalogAvailable = Valid;
|
|
var nativeCaptureSupported = Valid;
|
|
return new
|
|
{
|
|
file,
|
|
setCount,
|
|
pieceCount,
|
|
supported = nativeCaptureSupported,
|
|
catalogAvailable,
|
|
nativeCaptureSupported,
|
|
scanStatus = nativeCaptureSupported ? "native_capture" : "missing_data",
|
|
};
|
|
}
|
|
|
|
private object SimpleCategoryPayload(string file, int count)
|
|
{
|
|
var catalogAvailable = Valid;
|
|
const bool nativeCaptureSupported = false;
|
|
return new
|
|
{
|
|
file,
|
|
count,
|
|
supported = nativeCaptureSupported,
|
|
catalogAvailable,
|
|
nativeCaptureSupported,
|
|
scanStatus = catalogAvailable ? "catalog_only" : "missing_data",
|
|
};
|
|
}
|
|
|
|
public object ToPayload() => new
|
|
{
|
|
directory = Directory,
|
|
version = Version,
|
|
artifactSets = ArtifactSets,
|
|
artifactPieces = ArtifactPieces,
|
|
weapons = Weapons,
|
|
characters = Characters,
|
|
materials = Materials,
|
|
totalEntries = ArtifactPieces + Weapons + Characters + Materials,
|
|
categories = SupportedCategoriesPayload(),
|
|
missing = Missing,
|
|
valid = Valid,
|
|
source = "InventoryKamera inventorylists",
|
|
};
|
|
}
|