From 89cfed4300fc80d5b25f855cb450d670ca5f902b Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 28 Sep 2023 02:41:00 -0400 Subject: [PATCH] refactor: update for api 9 --- csharp/RemotePartyFinder/Gatherer.cs | 93 ++- csharp/RemotePartyFinder/Plugin.cs | 34 +- csharp/RemotePartyFinder/UploadableListing.cs | 122 +-- csharp/SourceGenerator/AutoTranslate.cs | 148 ++-- csharp/SourceGenerator/Program.cs | 766 +++++++++--------- csharp/SourceGenerator/SeStringExtensions.cs | 56 +- 6 files changed, 609 insertions(+), 610 deletions(-) diff --git a/csharp/RemotePartyFinder/Gatherer.cs b/csharp/RemotePartyFinder/Gatherer.cs index 42504a9..df19ba1 100644 --- a/csharp/RemotePartyFinder/Gatherer.cs +++ b/csharp/RemotePartyFinder/Gatherer.cs @@ -6,68 +6,67 @@ using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; -using Dalamud.Game; using Dalamud.Game.Gui.PartyFinder.Types; -using Dalamud.Logging; +using Dalamud.Plugin.Services; using Newtonsoft.Json; -namespace RemotePartyFinder { - internal class Gatherer : IDisposable { - #if DEBUG +namespace RemotePartyFinder; + +internal class Gatherer : IDisposable { + #if DEBUG private const string UploadUrl = "http://192.168.174.240:7878/contribute/multiple"; - #elif RELEASE - private const string UploadUrl = "https://xivpf.com/contribute/multiple"; - #endif + #elif RELEASE + private const string UploadUrl = "https://xivpf.com/contribute/multiple"; + #endif - private Plugin Plugin { get; } + private Plugin Plugin { get; } - private ConcurrentDictionary> Batches { get; } = new(); - private Stopwatch UploadTimer { get; } = new(); - private HttpClient Client { get; } = new(); + private ConcurrentDictionary> Batches { get; } = new(); + private Stopwatch UploadTimer { get; } = new(); + private HttpClient Client { get; } = new(); - internal Gatherer(Plugin plugin) { - this.Plugin = plugin; + internal Gatherer(Plugin plugin) { + this.Plugin = plugin; - this.UploadTimer.Start(); + this.UploadTimer.Start(); - this.Plugin.PartyFinderGui.ReceiveListing += this.OnListing; - this.Plugin.Framework.Update += this.OnUpdate; + this.Plugin.PartyFinderGui.ReceiveListing += this.OnListing; + this.Plugin.Framework.Update += this.OnUpdate; + } + + public void Dispose() { + this.Plugin.Framework.Update -= this.OnUpdate; + this.Plugin.PartyFinderGui.ReceiveListing -= this.OnListing; + } + + private void OnListing(PartyFinderListing listing, PartyFinderListingEventArgs args) { + if (!this.Batches.ContainsKey(args.BatchNumber)) { + this.Batches[args.BatchNumber] = new List(); } - public void Dispose() { - this.Plugin.Framework.Update -= this.OnUpdate; - this.Plugin.PartyFinderGui.ReceiveListing -= this.OnListing; + this.Batches[args.BatchNumber].Add(listing); + } + + private void OnUpdate(IFramework framework1) { + if (this.UploadTimer.Elapsed < TimeSpan.FromSeconds(10)) { + return; } - private void OnListing(PartyFinderListing listing, PartyFinderListingEventArgs args) { - if (!this.Batches.ContainsKey(args.BatchNumber)) { - this.Batches[args.BatchNumber] = new List(); - } + this.UploadTimer.Restart(); - this.Batches[args.BatchNumber].Add(listing); - } - - private void OnUpdate(Framework framework) { - if (this.UploadTimer.Elapsed < TimeSpan.FromSeconds(10)) { - return; - } - - this.UploadTimer.Restart(); - - foreach (var (batch, listings) in this.Batches.ToList()) { - this.Batches.Remove(batch, out _); - Task.Run(async () => { - var uploadable = listings - .Select(listing => new UploadableListing(listing)) - .ToList(); - var json = JsonConvert.SerializeObject(uploadable); - var resp = await this.Client.PostAsync(UploadUrl, new StringContent(json) { - Headers = { ContentType = MediaTypeHeaderValue.Parse("application/json") }, - }); - var output = await resp.Content.ReadAsStringAsync(); - PluginLog.Log(output); + foreach (var (batch, listings) in this.Batches.ToList()) { + this.Batches.Remove(batch, out _); + Task.Run(async () => { + var uploadable = listings + .Select(listing => new UploadableListing(listing)) + .ToList(); + var json = JsonConvert.SerializeObject(uploadable); + var resp = await this.Client.PostAsync(UploadUrl, new StringContent(json) { + Headers = { ContentType = MediaTypeHeaderValue.Parse("application/json") }, }); - } + var output = await resp.Content.ReadAsStringAsync(); + Plugin.Log.Info(output); + }); } } } diff --git a/csharp/RemotePartyFinder/Plugin.cs b/csharp/RemotePartyFinder/Plugin.cs index 5e363db..d422267 100644 --- a/csharp/RemotePartyFinder/Plugin.cs +++ b/csharp/RemotePartyFinder/Plugin.cs @@ -1,26 +1,26 @@ -using Dalamud.Game; -using Dalamud.Game.Gui.PartyFinder; -using Dalamud.IoC; +using Dalamud.IoC; using Dalamud.Plugin; +using Dalamud.Plugin.Services; -namespace RemotePartyFinder { - public class Plugin : IDalamudPlugin { - public string Name => "Remote Party Finder"; +namespace RemotePartyFinder; - [PluginService] - internal Framework Framework { get; private init; } +public class Plugin : IDalamudPlugin { + [PluginService] + internal static IPluginLog Log { get; private set; } - [PluginService] - internal PartyFinderGui PartyFinderGui { get; private init; } + [PluginService] + internal IFramework Framework { get; private init; } - private Gatherer Gatherer { get; } + [PluginService] + internal IPartyFinderGui PartyFinderGui { get; private init; } - public Plugin() { - this.Gatherer = new Gatherer(this); - } + private Gatherer Gatherer { get; } - public void Dispose() { - this.Gatherer.Dispose(); - } + public Plugin() { + this.Gatherer = new Gatherer(this); + } + + public void Dispose() { + this.Gatherer.Dispose(); } } diff --git a/csharp/RemotePartyFinder/UploadableListing.cs b/csharp/RemotePartyFinder/UploadableListing.cs index 3e30629..dcaae8b 100644 --- a/csharp/RemotePartyFinder/UploadableListing.cs +++ b/csharp/RemotePartyFinder/UploadableListing.cs @@ -5,68 +5,68 @@ using Dalamud.Game.Gui.PartyFinder.Types; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; -namespace RemotePartyFinder { - [Serializable] - [JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - internal class UploadableListing { - public uint Id { get; } - public uint ContentIdLower { get; } - public byte[] Name { get; } - public byte[] Description { get; } - public ushort CreatedWorld { get; } - public ushort HomeWorld { get; } - public ushort CurrentWorld { get; } - public DutyCategory Category { get; } - public ushort Duty { get; } - public DutyType DutyType { get; } - public bool BeginnersWelcome { get; } - public ushort SecondsRemaining { get; } - public ushort MinItemLevel { get; } - public byte NumParties { get; } - public byte SlotsAvailable { get; } - public uint LastServerRestart { get; } - public ObjectiveFlags Objective { get; } - public ConditionFlags Conditions { get; } - public DutyFinderSettingsFlags DutyFinderSettings { get; } - public LootRuleFlags LootRules { get; } - public SearchAreaFlags SearchArea { get; } - public List Slots { get; } - public List JobsPresent { get; } +namespace RemotePartyFinder; - internal UploadableListing(PartyFinderListing listing) { - this.Id = listing.Id; - this.ContentIdLower = listing.ContentIdLower; - this.Name = listing.Name.Encode(); - this.Description = listing.Description.Encode(); - this.CreatedWorld = (ushort) listing.World.Value.RowId; - this.HomeWorld = (ushort) listing.HomeWorld.Value.RowId; - this.CurrentWorld = (ushort) listing.CurrentWorld.Value.RowId; - this.Category = listing.Category; - this.Duty = listing.RawDuty; - this.DutyType = listing.DutyType; - this.BeginnersWelcome = listing.BeginnersWelcome; - this.SecondsRemaining = listing.SecondsRemaining; - this.MinItemLevel = listing.MinimumItemLevel; - this.NumParties = listing.Parties; - this.SlotsAvailable = listing.SlotsAvailable; - this.LastServerRestart = listing.LastPatchHotfixTimestamp; - this.Objective = listing.Objective; - this.Conditions = listing.Conditions; - this.DutyFinderSettings = listing.DutyFinderSettings; - this.LootRules = listing.LootRules; - this.SearchArea = listing.SearchArea; - this.Slots = listing.Slots.Select(slot => new UploadableSlot(slot)).ToList(); - this.JobsPresent = listing.RawJobsPresent.ToList(); - } - } +[Serializable] +[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +internal class UploadableListing { + public uint Id { get; } + public uint ContentIdLower { get; } + public byte[] Name { get; } + public byte[] Description { get; } + public ushort CreatedWorld { get; } + public ushort HomeWorld { get; } + public ushort CurrentWorld { get; } + public DutyCategory Category { get; } + public ushort Duty { get; } + public DutyType DutyType { get; } + public bool BeginnersWelcome { get; } + public ushort SecondsRemaining { get; } + public ushort MinItemLevel { get; } + public byte NumParties { get; } + public byte SlotsAvailable { get; } + public uint LastServerRestart { get; } + public ObjectiveFlags Objective { get; } + public ConditionFlags Conditions { get; } + public DutyFinderSettingsFlags DutyFinderSettings { get; } + public LootRuleFlags LootRules { get; } + public SearchAreaFlags SearchArea { get; } + public List Slots { get; } + public List JobsPresent { get; } - [Serializable] - [JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - internal class UploadableSlot { - public JobFlags Accepting { get; } - - internal UploadableSlot(PartyFinderSlot slot) { - this.Accepting = slot.Accepting.Aggregate((JobFlags) 0, (agg, flag) => agg | flag); - } + internal UploadableListing(PartyFinderListing listing) { + this.Id = listing.Id; + this.ContentIdLower = listing.ContentIdLower; + this.Name = listing.Name.Encode(); + this.Description = listing.Description.Encode(); + this.CreatedWorld = (ushort) listing.World.Value.RowId; + this.HomeWorld = (ushort) listing.HomeWorld.Value.RowId; + this.CurrentWorld = (ushort) listing.CurrentWorld.Value.RowId; + this.Category = listing.Category; + this.Duty = listing.RawDuty; + this.DutyType = listing.DutyType; + this.BeginnersWelcome = listing.BeginnersWelcome; + this.SecondsRemaining = listing.SecondsRemaining; + this.MinItemLevel = listing.MinimumItemLevel; + this.NumParties = listing.Parties; + this.SlotsAvailable = listing.SlotsAvailable; + this.LastServerRestart = listing.LastPatchHotfixTimestamp; + this.Objective = listing.Objective; + this.Conditions = listing.Conditions; + this.DutyFinderSettings = listing.DutyFinderSettings; + this.LootRules = listing.LootRules; + this.SearchArea = listing.SearchArea; + this.Slots = listing.Slots.Select(slot => new UploadableSlot(slot)).ToList(); + this.JobsPresent = listing.RawJobsPresent.ToList(); } } + +[Serializable] +[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +internal class UploadableSlot { + public JobFlags Accepting { get; } + + internal UploadableSlot(PartyFinderSlot slot) { + this.Accepting = slot.Accepting.Aggregate((JobFlags) 0, (agg, flag) => agg | flag); + } +} \ No newline at end of file diff --git a/csharp/SourceGenerator/AutoTranslate.cs b/csharp/SourceGenerator/AutoTranslate.cs index 1d38dba..096a8e5 100644 --- a/csharp/SourceGenerator/AutoTranslate.cs +++ b/csharp/SourceGenerator/AutoTranslate.cs @@ -3,83 +3,83 @@ using Pidgin; using static Pidgin.Parser; using static Pidgin.Parser; -namespace SourceGenerator { - internal static class AutoTranslate { - internal static Parser> selector)> Parser() { - var sheetName = Any - .AtLeastOnceUntil(Lookahead(Char('[').IgnoreResult().Or(End))) - .Select(string.Concat) - .Labelled("sheetName"); +namespace SourceGenerator; - var numPair = Map( - (first, second) => (ISelectorPart) new IndexRange( - uint.Parse(string.Concat(first)), - uint.Parse(string.Concat(second)) - ), - Digit.AtLeastOnce().Before(Char('-')), - Digit.AtLeastOnce() - ) - .Labelled("numPair"); - var singleRow = Digit - .AtLeastOnce() - .Select(string.Concat) - .Select(num => (ISelectorPart) new SingleRow(uint.Parse(num))); - var column = String("col-") - .Then(Digit.AtLeastOnce()) - .Select(string.Concat) - .Select(num => (ISelectorPart) new ColumnSpecifier(uint.Parse(num))); - var noun = String("noun") - .Select(_ => (ISelectorPart) new NounMarker()); +internal static class AutoTranslate { + internal static Parser> selector)> Parser() { + var sheetName = Any + .AtLeastOnceUntil(Lookahead(Char('[').IgnoreResult().Or(End))) + .Select(string.Concat) + .Labelled("sheetName"); - var selectorItems = OneOf( - Try(numPair), - singleRow, - column, - noun - ) - .Separated(Char(',')) - .Labelled("selectorItems"); - var selector = selectorItems - .Between(Char('['), Char(']')) - .Labelled("selector"); + var numPair = Map( + (first, second) => (ISelectorPart) new IndexRange( + uint.Parse(string.Concat(first)), + uint.Parse(string.Concat(second)) + ), + Digit.AtLeastOnce().Before(Char('-')), + Digit.AtLeastOnce() + ) + .Labelled("numPair"); + var singleRow = Digit + .AtLeastOnce() + .Select(string.Concat) + .Select(num => (ISelectorPart) new SingleRow(uint.Parse(num))); + var column = String("col-") + .Then(Digit.AtLeastOnce()) + .Select(string.Concat) + .Select(num => (ISelectorPart) new ColumnSpecifier(uint.Parse(num))); + var noun = String("noun") + .Select(_ => (ISelectorPart) new NounMarker()); - return Map( - (name, selector) => (name, selector), - sheetName, - selector.Optional() - ); - } - } + var selectorItems = OneOf( + Try(numPair), + singleRow, + column, + noun + ) + .Separated(Char(',')) + .Labelled("selectorItems"); + var selector = selectorItems + .Between(Char('['), Char(']')) + .Labelled("selector"); - internal interface ISelectorPart { - } - - internal class SingleRow : ISelectorPart { - public uint Row { get; } - - public SingleRow(uint row) { - this.Row = row; - } - } - - internal class IndexRange : ISelectorPart { - public uint Start { get; } - public uint End { get; } - - public IndexRange(uint start, uint end) { - this.Start = start; - this.End = end; - } - } - - internal class NounMarker : ISelectorPart { - } - - internal class ColumnSpecifier : ISelectorPart { - public uint Column { get; } - - public ColumnSpecifier(uint column) { - this.Column = column; - } + return Map( + (name, selector) => (name, selector), + sheetName, + selector.Optional() + ); } } + +internal interface ISelectorPart { +} + +internal class SingleRow : ISelectorPart { + public uint Row { get; } + + public SingleRow(uint row) { + this.Row = row; + } +} + +internal class IndexRange : ISelectorPart { + public uint Start { get; } + public uint End { get; } + + public IndexRange(uint start, uint end) { + this.Start = start; + this.End = end; + } +} + +internal class NounMarker : ISelectorPart { +} + +internal class ColumnSpecifier : ISelectorPart { + public uint Column { get; } + + public ColumnSpecifier(uint column) { + this.Column = column; + } +} \ No newline at end of file diff --git a/csharp/SourceGenerator/Program.cs b/csharp/SourceGenerator/Program.cs index 1ea8bcb..4f2d8c0 100644 --- a/csharp/SourceGenerator/Program.cs +++ b/csharp/SourceGenerator/Program.cs @@ -10,452 +10,452 @@ using Lumina.Excel.GeneratedSheets; using Lumina.Text; using Pidgin; -namespace SourceGenerator { - internal class Program { - private static void Main(string[] args) { - var data = new Dictionary(4); - foreach (var lang in Languages.Keys) { - data[lang] = new GameData(args[0], new LuminaOptions { - PanicOnSheetChecksumMismatch = false, - DefaultExcelLanguage = lang, - }); - } +namespace SourceGenerator; - var prog = new Program(data); - - File.WriteAllText(Path.Join(args[1], "duties.rs"), prog.GenerateDuties()); - File.WriteAllText(Path.Join(args[1], "jobs.rs"), prog.GenerateJobs()); - File.WriteAllText(Path.Join(args[1], "roulettes.rs"), prog.GenerateRoulettes()); - File.WriteAllText(Path.Join(args[1], "worlds.rs"), prog.GenerateWorlds()); - File.WriteAllText(Path.Join(args[1], "territory_names.rs"), prog.GenerateTerritoryNames()); - File.WriteAllText(Path.Join(args[1], "auto_translate.rs"), prog.GenerateAutoTranslate()); - File.WriteAllText(Path.Join(args[1], "treasure_maps.rs"), prog.GenerateTreasureMaps()); +internal class Program { + private static void Main(string[] args) { + var data = new Dictionary(4); + foreach (var lang in Languages.Keys) { + data[lang] = new GameData(args[0], new LuminaOptions { + PanicOnSheetChecksumMismatch = false, + DefaultExcelLanguage = lang, + }); } - private Dictionary Data { get; } + var prog = new Program(data); - private Program(Dictionary data) { - this.Data = data; + File.WriteAllText(Path.Join(args[1], "duties.rs"), prog.GenerateDuties()); + File.WriteAllText(Path.Join(args[1], "jobs.rs"), prog.GenerateJobs()); + File.WriteAllText(Path.Join(args[1], "roulettes.rs"), prog.GenerateRoulettes()); + File.WriteAllText(Path.Join(args[1], "worlds.rs"), prog.GenerateWorlds()); + File.WriteAllText(Path.Join(args[1], "territory_names.rs"), prog.GenerateTerritoryNames()); + File.WriteAllText(Path.Join(args[1], "auto_translate.rs"), prog.GenerateAutoTranslate()); + File.WriteAllText(Path.Join(args[1], "treasure_maps.rs"), prog.GenerateTreasureMaps()); + } + + private Dictionary Data { get; } + + private Program(Dictionary data) { + this.Data = data; + } + + private static StringBuilder DefaultHeader(bool localisedText = false) { + var sb = new StringBuilder("use std::collections::HashMap;\n"); + + if (localisedText) { + sb.Append("use super::LocalisedText;\n"); } - private static StringBuilder DefaultHeader(bool localisedText = false) { - var sb = new StringBuilder("use std::collections::HashMap;\n"); + return sb; + } - if (localisedText) { - sb.Append("use super::LocalisedText;\n"); - } + private static readonly Dictionary Languages = new() { + [Language.English] = "en", + [Language.Japanese] = "ja", + [Language.German] = "de", + [Language.French] = "fr", + }; - return sb; + private string? GetLocalisedStruct(uint rowId, Func nameFunc, uint indent = 0, bool capitalise = false) where T : ExcelRow { + var def = this.Data[Language.English].GetExcelSheet()!.GetRow(rowId)!; + var defName = nameFunc(def)?.TextValue(); + if (string.IsNullOrEmpty(defName)) { + return null; } - private static readonly Dictionary Languages = new() { - [Language.English] = "en", - [Language.Japanese] = "ja", - [Language.German] = "de", - [Language.French] = "fr", - }; + var sb = new StringBuilder(); - private string? GetLocalisedStruct(uint rowId, Func nameFunc, uint indent = 0, bool capitalise = false) where T : ExcelRow { - var def = this.Data[Language.English].GetExcelSheet()!.GetRow(rowId)!; - var defName = nameFunc(def)?.TextValue(); - if (string.IsNullOrEmpty(defName)) { - return null; + sb.Append("LocalisedText {\n"); + + foreach (var (language, key) in Languages) { + var row = this.Data[language].GetExcelSheet(language)?.GetRow(rowId); + var name = row == null + ? defName + : nameFunc(row)?.TextValue().Replace("\"", "\\\""); + name ??= defName; + if (capitalise) { + name = name[..1].ToUpperInvariant() + name[1..]; } - var sb = new StringBuilder(); - - sb.Append("LocalisedText {\n"); - - foreach (var (language, key) in Languages) { - var row = this.Data[language].GetExcelSheet(language)?.GetRow(rowId); - var name = row == null - ? defName - : nameFunc(row)?.TextValue().Replace("\"", "\\\""); - name ??= defName; - if (capitalise) { - name = name[..1].ToUpperInvariant() + name[1..]; - } - - for (var i = 0; i < indent + 4; i++) { - sb.Append(' '); - } - - sb.Append($"{key}: \"{name}\",\n"); - } - - for (var i = 0; i < indent; i++) { + for (var i = 0; i < indent + 4; i++) { sb.Append(' '); } - sb.Append('}'); - - return sb.ToString(); + sb.Append($"{key}: \"{name}\",\n"); } - private string GenerateDuties() { - var sb = DefaultHeader(true); - sb.Append('\n'); - - sb.Append("#[derive(Debug)]\n"); - sb.Append("pub struct DutyInfo {\n"); - sb.Append(" pub name: LocalisedText,\n"); - sb.Append(" pub high_end: bool,\n"); - sb.Append(" pub content_kind: ContentKind,\n"); - sb.Append("}\n\n"); - - sb.Append("#[derive(Debug, Clone, Copy)]\n"); - sb.Append("#[allow(unused)]\n"); - sb.Append("#[repr(u32)]\n"); - sb.Append("pub enum ContentKind {\n"); - foreach (var kind in this.Data[Language.English].GetExcelSheet()!) { - var name = kind.Name.TextValue().Replace(" ", "").Replace("&", ""); - if (name.Length > 0) { - sb.Append($" {name} = {kind.RowId},\n"); - } - } - - sb.Append(" Other(u32),\n"); - sb.Append("}\n\n"); - - sb.Append("impl ContentKind {\n"); - - sb.Append(" fn from_u32(kind: u32) -> Self {\n"); - sb.Append(" match kind {\n"); - foreach (var kind in this.Data[Language.English].GetExcelSheet()!) { - var name = kind.Name.TextValue().Replace(" ", "").Replace("&", ""); - if (name.Length > 0) { - sb.Append($" {kind.RowId} => Self::{name},\n"); - } - } - - sb.Append(" x => Self::Other(x),\n"); - sb.Append(" }\n"); - sb.Append(" }\n\n"); - - sb.Append(" pub fn as_u32(self) -> u32 {\n"); - sb.Append(" match self {\n"); - foreach (var kind in this.Data[Language.English].GetExcelSheet()!) { - var name = kind.Name.TextValue().Replace(" ", "").Replace("&", ""); - if (name.Length > 0) { - sb.Append($" Self::{name} => {kind.RowId},\n"); - } - } - - sb.Append(" Self::Other(x) => x,\n"); - sb.Append(" }\n"); - sb.Append(" }\n"); - - sb.Append("}\n\n"); - - sb.Append("lazy_static::lazy_static! {\n"); - sb.Append(" pub static ref DUTIES: HashMap = maplit::hashmap! {\n"); - - foreach (var cfc in this.Data[Language.English].GetExcelSheet()!) { - if (cfc.RowId == 0) { - continue; - } - - var name = this.GetLocalisedStruct(cfc.RowId, row => row.Name, 12, true); - if (name == null) { - continue; - } - - var highEnd = cfc.HighEndDuty ? "true" : "false"; - var contentType = cfc.ContentType.Value; - var contentKind = contentType?.Name?.TextValue().Replace(" ", "").Replace("&", ""); - if (string.IsNullOrEmpty(contentKind)) { - contentKind = $"Other({contentType?.RowId ?? 0})"; - } - - sb.Append($" {cfc.RowId} => DutyInfo {{\n"); - sb.Append($" name: {name},\n"); - sb.Append($" high_end: {highEnd},\n"); - sb.Append($" content_kind: ContentKind::{contentKind},\n"); - sb.Append(" },\n"); - } - - sb.Append(" };\n"); - sb.Append("}\n"); - - return sb.ToString(); + for (var i = 0; i < indent; i++) { + sb.Append(' '); } - private string GenerateJobs() { - var sb = DefaultHeader(); - sb.Append("use ffxiv_types::jobs::{ClassJob, Class, Job, NonCombatJob};\n\n"); - sb.Append("lazy_static::lazy_static! {\n"); - sb.Append(" pub static ref JOBS: HashMap = maplit::hashmap! {\n"); + sb.Append('}'); - foreach (var cj in this.Data[Language.English].GetExcelSheet()!) { - if (cj.RowId == 0) { - continue; - } + return sb.ToString(); + } - var name = cj.NameEnglish.TextValue().Replace(" ", ""); - if (name.Length <= 0) { - continue; - } + private string GenerateDuties() { + var sb = DefaultHeader(true); + sb.Append('\n'); - var isCombat = cj.Role != 0; - var isClass = cj.JobIndex == 0; + sb.Append("#[derive(Debug)]\n"); + sb.Append("pub struct DutyInfo {\n"); + sb.Append(" pub name: LocalisedText,\n"); + sb.Append(" pub high_end: bool,\n"); + sb.Append(" pub content_kind: ContentKind,\n"); + sb.Append("}\n\n"); - string value; - if (isCombat) { - value = isClass - ? $"ClassJob::Class(Class::{name})" - : $"ClassJob::Job(Job::{name})"; - } else { - value = $"ClassJob::NonCombat(NonCombatJob::{name})"; - } - - sb.Append($" {cj.RowId} => {value},\n"); + sb.Append("#[derive(Debug, Clone, Copy)]\n"); + sb.Append("#[allow(unused)]\n"); + sb.Append("#[repr(u32)]\n"); + sb.Append("pub enum ContentKind {\n"); + foreach (var kind in this.Data[Language.English].GetExcelSheet()!) { + var name = kind.Name.TextValue().Replace(" ", "").Replace("&", ""); + if (name.Length > 0) { + sb.Append($" {name} = {kind.RowId},\n"); } - - sb.Append(" };\n"); - sb.Append("}\n"); - - return sb.ToString(); } - private string GenerateRoulettes() { - var sb = DefaultHeader(true); - sb.Append('\n'); - sb.Append("#[derive(Debug)]\n"); - sb.Append("pub struct RouletteInfo {\n"); - sb.Append(" pub name: LocalisedText,\n"); - sb.Append(" pub pvp: bool,\n"); - sb.Append("}\n\n"); + sb.Append(" Other(u32),\n"); + sb.Append("}\n\n"); - sb.Append("lazy_static::lazy_static! {\n"); - sb.Append(" pub static ref ROULETTES: HashMap = maplit::hashmap! {\n"); + sb.Append("impl ContentKind {\n"); - foreach (var cr in this.Data[Language.English].GetExcelSheet()!) { - if (cr.RowId == 0) { - continue; - } - - var name = this.GetLocalisedStruct(cr.RowId, row => row.Name, 12); - if (name == null) { - continue; - } - - var pvp = cr.IsPvP - ? "true" - : "false"; - - sb.Append($" {cr.RowId} => RouletteInfo {{\n"); - sb.Append($" name: {name},\n"); - sb.Append($" pvp: {pvp},\n"); - sb.Append(" },\n"); + sb.Append(" fn from_u32(kind: u32) -> Self {\n"); + sb.Append(" match kind {\n"); + foreach (var kind in this.Data[Language.English].GetExcelSheet()!) { + var name = kind.Name.TextValue().Replace(" ", "").Replace("&", ""); + if (name.Length > 0) { + sb.Append($" {kind.RowId} => Self::{name},\n"); } - - sb.Append(" };\n"); - sb.Append("}\n"); - - return sb.ToString(); } - private string GenerateWorlds() { - var sb = DefaultHeader(); - sb.Append("use ffxiv_types::World;\n\n"); - sb.Append("lazy_static::lazy_static! {\n"); - sb.Append(" pub static ref WORLDS: HashMap = maplit::hashmap! {\n"); + sb.Append(" x => Self::Other(x),\n"); + sb.Append(" }\n"); + sb.Append(" }\n\n"); - foreach (var world in this.Data[Language.English].GetExcelSheet()!) { - if (world.RowId == 0 || !world.IsPublic || world.UserType == 0 || world.DataCenter.Row == 0) { - continue; - } - - var name = world.Name.TextValue(); - if (name.Length <= 0) { - continue; - } - - sb.Append($" {world.RowId} => World::{name},\n"); + sb.Append(" pub fn as_u32(self) -> u32 {\n"); + sb.Append(" match self {\n"); + foreach (var kind in this.Data[Language.English].GetExcelSheet()!) { + var name = kind.Name.TextValue().Replace(" ", "").Replace("&", ""); + if (name.Length > 0) { + sb.Append($" Self::{name} => {kind.RowId},\n"); } - - sb.Append(" };\n"); - sb.Append("}\n"); - - return sb.ToString(); } - private string GenerateTerritoryNames() { - var sb = DefaultHeader(true); - sb.Append("\nlazy_static::lazy_static! {\n"); - sb.Append(" pub static ref TERRITORY_NAMES: HashMap = maplit::hashmap! {\n"); + sb.Append(" Self::Other(x) => x,\n"); + sb.Append(" }\n"); + sb.Append(" }\n"); - foreach (var tt in this.Data[Language.English].GetExcelSheet()!) { - if (tt.RowId == 0 || tt.PlaceName.Row == 0) { - continue; - } + sb.Append("}\n\n"); - var name = this.GetLocalisedStruct( - tt.RowId, - row => row.PlaceName.Value!.Name, - 8 - ); - if (name == null) { - continue; - } + sb.Append("lazy_static::lazy_static! {\n"); + sb.Append(" pub static ref DUTIES: HashMap = maplit::hashmap! {\n"); - sb.Append($" {tt.RowId} => {name},\n"); + foreach (var cfc in this.Data[Language.English].GetExcelSheet()!) { + if (cfc.RowId == 0) { + continue; } - sb.Append(" };\n"); - sb.Append("}\n"); + var name = this.GetLocalisedStruct(cfc.RowId, row => row.Name, 12, true); + if (name == null) { + continue; + } - return sb.ToString(); + var highEnd = cfc.HighEndDuty ? "true" : "false"; + var contentType = cfc.ContentType.Value; + var contentKind = contentType?.Name?.TextValue().Replace(" ", "").Replace("&", ""); + if (string.IsNullOrEmpty(contentKind)) { + contentKind = $"Other({contentType?.RowId ?? 0})"; + } + + sb.Append($" {cfc.RowId} => DutyInfo {{\n"); + sb.Append($" name: {name},\n"); + sb.Append($" high_end: {highEnd},\n"); + sb.Append($" content_kind: ContentKind::{contentKind},\n"); + sb.Append(" },\n"); } - private string GenerateAutoTranslate() { - var sb = DefaultHeader(true); - sb.Append("\nlazy_static::lazy_static! {\n"); - sb.Append(" pub static ref AUTO_TRANSLATE: HashMap<(u32, u32), LocalisedText> = maplit::hashmap! {\n"); + sb.Append(" };\n"); + sb.Append("}\n"); - var parser = AutoTranslate.Parser(); - foreach (var row in this.Data[Language.English].GetExcelSheet()!) { - var lookup = row.LookupTable.TextValue(); - if (lookup is not ("" or "@")) { - var (sheetName, selector) = parser.ParseOrThrow(lookup); - var sheetType = typeof(Completion) - .Assembly - .GetType($"Lumina.Excel.GeneratedSheets.{sheetName}")!; - var getSheet = this.Data[Language.English] - .GetType() - .GetMethod("GetExcelSheet", Type.EmptyTypes)! - .MakeGenericMethod(sheetType); - var sheets = this.Data.ToDictionary( - pair => pair.Key, - pair => { - var sheet = (ExcelSheetImpl) getSheet.Invoke(pair.Value, null)!; - return (sheet, sheet.GetRowParsers().ToArray()); - }); + return sb.ToString(); + } - var columns = new List(); - var rows = new List(); - if (selector.HasValue) { - columns.Clear(); - rows.Clear(); + private string GenerateJobs() { + var sb = DefaultHeader(); + sb.Append("use ffxiv_types::jobs::{ClassJob, Class, Job, NonCombatJob};\n\n"); + sb.Append("lazy_static::lazy_static! {\n"); + sb.Append(" pub static ref JOBS: HashMap = maplit::hashmap! {\n"); - foreach (var part in selector.Value) { - switch (part) { - case IndexRange range: { - var start = (int) range.Start; - var end = (int) (range.End + 1); - rows.Add(start..end); - break; - } - case SingleRow single: { - var idx = (int) single.Row; - rows.Add(idx..(idx + 1)); - break; - } - case ColumnSpecifier col: - columns.Add((int) col.Column); - break; + foreach (var cj in this.Data[Language.English].GetExcelSheet()!) { + if (cj.RowId == 0) { + continue; + } + + var name = cj.NameEnglish.TextValue().Replace(" ", ""); + if (name.Length <= 0) { + continue; + } + + var isCombat = cj.Role != 0; + var isClass = cj.JobIndex == 0; + + string value; + if (isCombat) { + value = isClass + ? $"ClassJob::Class(Class::{name})" + : $"ClassJob::Job(Job::{name})"; + } else { + value = $"ClassJob::NonCombat(NonCombatJob::{name})"; + } + + sb.Append($" {cj.RowId} => {value},\n"); + } + + sb.Append(" };\n"); + sb.Append("}\n"); + + return sb.ToString(); + } + + private string GenerateRoulettes() { + var sb = DefaultHeader(true); + sb.Append('\n'); + sb.Append("#[derive(Debug)]\n"); + sb.Append("pub struct RouletteInfo {\n"); + sb.Append(" pub name: LocalisedText,\n"); + sb.Append(" pub pvp: bool,\n"); + sb.Append("}\n\n"); + + sb.Append("lazy_static::lazy_static! {\n"); + sb.Append(" pub static ref ROULETTES: HashMap = maplit::hashmap! {\n"); + + foreach (var cr in this.Data[Language.English].GetExcelSheet()!) { + if (cr.RowId == 0) { + continue; + } + + var name = this.GetLocalisedStruct(cr.RowId, row => row.Name, 12); + if (name == null) { + continue; + } + + var pvp = cr.IsPvP + ? "true" + : "false"; + + sb.Append($" {cr.RowId} => RouletteInfo {{\n"); + sb.Append($" name: {name},\n"); + sb.Append($" pvp: {pvp},\n"); + sb.Append(" },\n"); + } + + sb.Append(" };\n"); + sb.Append("}\n"); + + return sb.ToString(); + } + + private string GenerateWorlds() { + var sb = DefaultHeader(); + sb.Append("use ffxiv_types::World;\n\n"); + sb.Append("lazy_static::lazy_static! {\n"); + sb.Append(" pub static ref WORLDS: HashMap = maplit::hashmap! {\n"); + + foreach (var world in this.Data[Language.English].GetExcelSheet()!) { + if (world.RowId == 0 || !world.IsPublic || world.UserType == 0 || world.DataCenter.Row == 0) { + continue; + } + + var name = world.Name.TextValue(); + if (name.Length <= 0) { + continue; + } + + sb.Append($" {world.RowId} => World::{name},\n"); + } + + sb.Append(" };\n"); + sb.Append("}\n"); + + return sb.ToString(); + } + + private string GenerateTerritoryNames() { + var sb = DefaultHeader(true); + sb.Append("\nlazy_static::lazy_static! {\n"); + sb.Append(" pub static ref TERRITORY_NAMES: HashMap = maplit::hashmap! {\n"); + + foreach (var tt in this.Data[Language.English].GetExcelSheet()!) { + if (tt.RowId == 0 || tt.PlaceName.Row == 0) { + continue; + } + + var name = this.GetLocalisedStruct( + tt.RowId, + row => row.PlaceName.Value!.Name, + 8 + ); + if (name == null) { + continue; + } + + sb.Append($" {tt.RowId} => {name},\n"); + } + + sb.Append(" };\n"); + sb.Append("}\n"); + + return sb.ToString(); + } + + private string GenerateAutoTranslate() { + var sb = DefaultHeader(true); + sb.Append("\nlazy_static::lazy_static! {\n"); + sb.Append(" pub static ref AUTO_TRANSLATE: HashMap<(u32, u32), LocalisedText> = maplit::hashmap! {\n"); + + var parser = AutoTranslate.Parser(); + foreach (var row in this.Data[Language.English].GetExcelSheet()!) { + var lookup = row.LookupTable.TextValue(); + if (lookup is not ("" or "@")) { + var (sheetName, selector) = parser.ParseOrThrow(lookup); + var sheetType = typeof(Completion) + .Assembly + .GetType($"Lumina.Excel.GeneratedSheets.{sheetName}")!; + var getSheet = this.Data[Language.English] + .GetType() + .GetMethod("GetExcelSheet", Type.EmptyTypes)! + .MakeGenericMethod(sheetType); + var sheets = this.Data.ToDictionary( + pair => pair.Key, + pair => { + var sheet = (ExcelSheetImpl) getSheet.Invoke(pair.Value, null)!; + return (sheet, sheet.GetRowParsers().ToArray()); + }); + + var columns = new List(); + var rows = new List(); + if (selector.HasValue) { + columns.Clear(); + rows.Clear(); + + foreach (var part in selector.Value) { + switch (part) { + case IndexRange range: { + var start = (int) range.Start; + var end = (int) (range.End + 1); + rows.Add(start..end); + break; } + case SingleRow single: { + var idx = (int) single.Row; + rows.Add(idx..(idx + 1)); + break; + } + case ColumnSpecifier col: + columns.Add((int) col.Column); + break; } } + } - if (columns.Count == 0) { - columns.Add(0); - } + if (columns.Count == 0) { + columns.Add(0); + } - if (rows.Count == 0) { - rows.Add(..); - } + if (rows.Count == 0) { + rows.Add(..); + } - var builder = new StringBuilder(); - foreach (var range in rows) { - var validRows = sheets[Language.English] - .Item2 - .Select(parser => parser.RowId) - .ToArray(); - for (var i = range.Start.Value; i < range.End.Value; i++) { - if (!validRows.Contains((uint) i)) { - continue; - } + var builder = new StringBuilder(); + foreach (var range in rows) { + var validRows = sheets[Language.English] + .Item2 + .Select(parser => parser.RowId) + .ToArray(); + for (var i = range.Start.Value; i < range.End.Value; i++) { + if (!validRows.Contains((uint) i)) { + continue; + } - builder.Clear(); + builder.Clear(); - builder.Append($" ({row.Group}, {i}) => LocalisedText {{\n"); + builder.Append($" ({row.Group}, {i}) => LocalisedText {{\n"); - var lines = 0; - foreach (var (lang, (_, parsers)) in sheets) { - // take the first column that works - foreach (var col in columns) { - var rowParser = parsers.FirstOrDefault(parser => parser.RowId == i); - if (rowParser != null) { - var name = rowParser.ReadColumn(col)!; - var text = name.TextValue().Replace("\"", "\\\""); - if (text.Length > 0) { - builder.Append($" {Languages[lang]}: \"{text}\",\n"); - lines += 1; - break; - } + var lines = 0; + foreach (var (lang, (_, parsers)) in sheets) { + // take the first column that works + foreach (var col in columns) { + var rowParser = parsers.FirstOrDefault(parser => parser.RowId == i); + if (rowParser != null) { + var name = rowParser.ReadColumn(col)!; + var text = name.TextValue().Replace("\"", "\\\""); + if (text.Length > 0) { + builder.Append($" {Languages[lang]}: \"{text}\",\n"); + lines += 1; + break; } } } - - builder.Append(" },\n"); - - if (lines != 4) { - continue; - } - - sb.Append(builder); } - } - } else { - var text = this.GetLocalisedStruct(row.RowId, row => row.Text, 8); - if (text != null) { - sb.Append($" ({row.Group}, {row.RowId}) => {text},\n"); + + builder.Append(" },\n"); + + if (lines != 4) { + continue; + } + + sb.Append(builder); } } - } - - sb.Append(" };\n"); - sb.Append("}\n"); - - return sb.ToString(); - } - - private string GenerateTreasureMaps() { - var sb = DefaultHeader(true); - sb.Append("\nlazy_static::lazy_static! {\n"); - sb.Append(" pub static ref TREASURE_MAPS: HashMap = maplit::hashmap! {\n"); - sb.Append(" 0 => LocalisedText {\n"); - sb.Append(" en: \"All Levels\",\n"); - sb.Append(" ja: \"レベルを指定しない\",\n"); - sb.Append(" de: \"Jede Stufe\",\n"); - sb.Append(" fr: \"Tous niveaux\",\n"); - sb.Append(" },\n"); - - var i = 1; - foreach (var row in this.Data[Language.English].GetExcelSheet()!) { - // IS THIS RIGHT? - if (row.TreasureHuntTexture != 0) { - continue; - } - - SeString? GetMapName(TreasureHuntRank thr) { - var name = thr.KeyItemName.Value?.Name; - return string.IsNullOrEmpty(name?.TextValue()) - ? thr.ItemName.Value?.Name - : name; - } - - var name = this.GetLocalisedStruct(row.RowId, GetMapName, 8); - if (!string.IsNullOrEmpty(name)) { - sb.Append($" {i++} => {name},\n"); + } else { + var text = this.GetLocalisedStruct(row.RowId, row => row.Text, 8); + if (text != null) { + sb.Append($" ({row.Group}, {row.RowId}) => {text},\n"); } } - - sb.Append(" };\n"); - sb.Append("}\n"); - - return sb.ToString(); } + + sb.Append(" };\n"); + sb.Append("}\n"); + + return sb.ToString(); } -} + + private string GenerateTreasureMaps() { + var sb = DefaultHeader(true); + sb.Append("\nlazy_static::lazy_static! {\n"); + sb.Append(" pub static ref TREASURE_MAPS: HashMap = maplit::hashmap! {\n"); + sb.Append(" 0 => LocalisedText {\n"); + sb.Append(" en: \"All Levels\",\n"); + sb.Append(" ja: \"レベルを指定しない\",\n"); + sb.Append(" de: \"Jede Stufe\",\n"); + sb.Append(" fr: \"Tous niveaux\",\n"); + sb.Append(" },\n"); + + var i = 1; + foreach (var row in this.Data[Language.English].GetExcelSheet()!) { + // IS THIS RIGHT? + if (row.TreasureHuntTexture != 0) { + continue; + } + + SeString? GetMapName(TreasureHuntRank thr) { + var name = thr.KeyItemName.Value?.Name; + return string.IsNullOrEmpty(name?.TextValue()) + ? thr.ItemName.Value?.Name + : name; + } + + var name = this.GetLocalisedStruct(row.RowId, GetMapName, 8); + if (!string.IsNullOrEmpty(name)) { + sb.Append($" {i++} => {name},\n"); + } + } + + sb.Append(" };\n"); + sb.Append("}\n"); + + return sb.ToString(); + } +} \ No newline at end of file diff --git a/csharp/SourceGenerator/SeStringExtensions.cs b/csharp/SourceGenerator/SeStringExtensions.cs index b63c956..1c74d22 100644 --- a/csharp/SourceGenerator/SeStringExtensions.cs +++ b/csharp/SourceGenerator/SeStringExtensions.cs @@ -2,36 +2,36 @@ using Lumina.Text; using Lumina.Text.Payloads; -namespace SourceGenerator { - internal static class SeStringExtensions { - internal static string TextValue(this SeString str) { - var payloads = str.Payloads - .Select(p => { - if (p is TextPayload text) { - return p.Data[0] == 0x03 - ? text.RawString[1..] - : text.RawString; - } +namespace SourceGenerator; - if (p.Data.Length <= 1) { - return ""; - } - - if (p.Data[1] == 0x1F) { - return "-"; - } - - if (p.Data.Length > 2 && p.Data[1] == 0x20) { - var value = p.Data.Length > 4 - ? p.Data[3] - 1 - : p.Data[2]; - return ((char) (48 + value)).ToString(); - } +internal static class SeStringExtensions { + internal static string TextValue(this SeString str) { + var payloads = str.Payloads + .Select(p => { + if (p is TextPayload text) { + return p.Data[0] == 0x03 + ? text.RawString[1..] + : text.RawString; + } + if (p.Data.Length <= 1) { return ""; - }); + } - return string.Join("", payloads); - } + if (p.Data[1] == 0x1F) { + return "-"; + } + + if (p.Data.Length > 2 && p.Data[1] == 0x20) { + var value = p.Data.Length > 4 + ? p.Data[3] - 1 + : p.Data[2]; + return ((char) (48 + value)).ToString(); + } + + return ""; + }); + + return string.Join("", payloads); } -} +} \ No newline at end of file