diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7ea741e..9bf43cd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,33 +17,3 @@ jobs: - uses: actions/checkout@v4 - name: Build Plugin uses: redstrate/build-dalamud-plugin@main - with: - solution-dir: dalamud - - build-webassembly: - name: "Build" - runs-on: ubuntu-latest - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name - - steps: - - uses: actions/checkout@v4 - - name: Add WebAssembly Target - run: | - rustup target add wasm32-unknown-unknown - - uses: actions/cache@v4 - id: cache-deps - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - name: Install wasm-pack - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - cargo install wasm-pack - - name: Build - run: | - ./scripts/build-web.sh diff --git a/dalamud/Auracite.sln b/Auracite.sln similarity index 100% rename from dalamud/Auracite.sln rename to Auracite.sln diff --git a/dalamud/Auracite/Auracite.csproj b/Auracite/Auracite.csproj similarity index 100% rename from dalamud/Auracite/Auracite.csproj rename to Auracite/Auracite.csproj diff --git a/Auracite/Auracite.json b/Auracite/Auracite.json new file mode 100644 index 0000000..15f45fd --- /dev/null +++ b/Auracite/Auracite.json @@ -0,0 +1,9 @@ +{ + "Author": "redstrate", + "Name": "Auracite", + "Punchline": "Archive your character in a portable, generic format", + "Description": "Archive your character in a portable, generic format", + "Tags": [], + "RepoUrl": "https://github.com/redstrate/Auracite", + "IconUrl": "https://raw.githubusercontent.com/redstrate/Auracite/refs/heads/main/images/icon.png" +} diff --git a/Auracite/IStep.cs b/Auracite/IStep.cs new file mode 100644 index 0000000..c948d0e --- /dev/null +++ b/Auracite/IStep.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using InteropGenerator.Runtime; +using Lumina.Excel; +using Lumina.Text.ReadOnly; + +namespace Auracite; + +public interface IStep : IDisposable +{ + public event CompletedDelegate Completed; + + void Run(); + + string StepName(); + string StepDescription(); + bool IsEnd() + { + return false; + } + bool NeedsUpdateEveryFrame() + { + return false; + } + + delegate void CompletedDelegate(); + + public static NameValue SaveNameValue(uint key, Func fieldSelector) where T : struct, IExcelRow { + var newValue = new NameValue(); + var row = Plugin.DataManager.GetExcelSheet()?.GetRow(key); + if (row != null) { + newValue.name = fieldSelector(row.Value).ToString(); + } + newValue.value = key; + + return newValue; + } + + public static unsafe List ConsumeBitArray(BitArray array) { + return new List(new ReadOnlySpan(array.Pointer, array.ByteLength).ToArray()); + } +} diff --git a/Auracite/Package.cs b/Auracite/Package.cs new file mode 100644 index 0000000..44af4b4 --- /dev/null +++ b/Auracite/Package.cs @@ -0,0 +1,197 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Auracite; + +[SuppressMessage("ReSharper", "InconsistentNaming")] +public class InventoryItem +{ + public int slot; + public uint quantity; + public uint id; + public ulong crafter_content_id; + public byte item_flags; + public ushort condition; + public ushort spiritbond_or_collectability; + public uint glamour_id; + public List materia; + public List materia_grades; + public List stains; +} + +[SuppressMessage("ReSharper", "InconsistentNaming")] +public class InventoryContainer +{ + public List items; +} + +[SuppressMessage("ReSharper", "InconsistentNaming")] +public class NameValue +{ + public string name; + public uint value; +} + +[SuppressMessage("ReSharper", "InconsistentNaming")] +public class DayMonthValue +{ + // TODO: add back datetime name once I can figure out how to calculate it + public int day; + public int month; +} + +[SuppressMessage("ReSharper", "InconsistentNaming")] +public class ClassJobLevel +{ + public string name; + public int level; + public int exp; + public uint value; +} + +[SuppressMessage("ReSharper", "InconsistentNaming")] +public class Appearance +{ + public int model_type; + public int height; + public int face_type; + public int hair_style; + public bool has_highlights; + public int skin_color; + public int eye_color; + public int hair_color; + public int hair_color2; + public int face_features; + public int face_features_color; + public int eyebrows; + public int eye_color2; + public int eye_shape; + public int nose_shape; + public int jaw_shape; + public int lip_style; + public int lip_color; + public int race_feature_size; + public int race_feature_type; + public int bust_size; + public int facepaint; + public int facepaint_color; +} + +[SuppressMessage("ReSharper", "InconsistentNaming")] +public class CharacterJson +{ + public string name; + public NameValue world; + public NameValue data_center; + public NameValue city_state; + public DayMonthValue nameday; + public NameValue guardian; + public NameValue gender; + public NameValue tribe; + public NameValue race; + public List classjob_levels = new List(); + public NameValue grand_company; + public List grand_company_ranks = new List(); // TODO: introduce as a NameValue + public NameValue title; + public string playtime; + public int voice; + + // adventurer plate + public string? plate_title; + public bool? plate_title_is_prefix; + public string? plate_class_job; + public int plate_class_job_level; + public string? search_comment; + + public bool is_battle_mentor; + public bool is_trade_mentor; + public bool is_novice; + public bool is_returner; + public short player_commendations; + + // Appearance + public Appearance appearance = new Appearance(); + + // inventory + public InventoryContainer inventory1; + public InventoryContainer inventory2; + public InventoryContainer inventory3; + public InventoryContainer inventory4; + + public InventoryContainer equipped; + + public InventoryContainer currency; + + public InventoryContainer armory_off_hand; + public InventoryContainer armory_head; + public InventoryContainer armory_body; + public InventoryContainer armory_hands; + public InventoryContainer armory_waist; + public InventoryContainer armory_legs; + public InventoryContainer armory_ear; + public InventoryContainer armory_neck; + public InventoryContainer armory_wrist; + public InventoryContainer armory_rings; + public InventoryContainer armory_soul_crystal; + public InventoryContainer armory_main_hand; + + // unlocks + public List unlocks; + public List seen_active_help; + public List minions; + public List mounts; + public List orchestrion_rolls; + public List cutscene_seen; + public List ornaments; + public List caught_fish; + public List caught_spearfish; + public List adventures; + public List triple_triad_cards; + public List glasses_styles; + public List chocobo_taxi_stands; + public List titles; + public List unlocked_companion_equip; + + // aether currents + public List comp_flg_set; + public List unlocked_aether_currents; + + // aetheryte + public List unlocked_aetherytes; + public int homepoint; + public List favorite_aetherytes; + public int free_aetheryte; + + // classjob + public int current_class; + public int first_class; + public int rested_exp; + + // content + public List unlocked_special_content; + public List unlocked_raids; + public List unlocked_dungeons; + public List unlocked_guildhests; + public List unlocked_trials; + public List unlocked_crystalline_conflicts; + public List unlocked_frontlines; + public List cleared_raids; + public List cleared_dungeons; + public List cleared_guildhests; + public List cleared_trials; + public List cleared_crystalline_conflicts; + public List cleared_frontlines; + public List cleared_masked_carnivale; + public List unlocked_misc_content; + public List cleared_misc_content; + + // quest + public List completed_quests; + + // volatile + public float position_x; + public float position_y; + public float position_z; + public float rotation; + public ushort zone_id; +} diff --git a/Auracite/Plugin.cs b/Auracite/Plugin.cs new file mode 100644 index 0000000..ea2be56 --- /dev/null +++ b/Auracite/Plugin.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using Dalamud.Game.Command; +using Dalamud.Interface.Windowing; +using Dalamud.IoC; +using Dalamud.Plugin; +using Dalamud.Plugin.Services; +using SixLabors.ImageSharp; + +namespace Auracite; + +public sealed class Plugin : IDalamudPlugin +{ + public static IStep? CurrentStep; + private readonly WindowSystem WindowSystem = new("Auracite"); + + private readonly List _steps = + [typeof(AppearanceStep), typeof(InventoryStep), typeof(MiscStep), typeof(PlaytimeStep), typeof(AdventurerPlateStep), typeof(TitleStep), typeof(EndStep)]; + + private int _stepIndex; + + private readonly StepWindow StepWindow; + + public static CharacterJson? package; + public static Image? portrait; + public static Image? base_plate; + public static Image? pattern_overlay; + public static Image? backing; + public static Image? top_border; + public static Image? bottom_border; + public static Image? portrait_frame; + public static Image? plate_frame; + public static Image? accent; + + public Plugin() + { + CommandManager.AddHandler("/auracite", new CommandInfo(OnAuraciteCommand) + { + HelpMessage = "Start the archival process." + }); + + StepWindow = new StepWindow(this); + WindowSystem.AddWindow(StepWindow); + + PluginInterface.UiBuilder.Draw += WindowSystem.Draw; + Framework.Update += CheckCurrentStep; + } + + [PluginService] internal static IClientState ClientState { get; private set; } = null!; + + [PluginService] internal static IObjectTable ObjectTable { get; private set; } = null!; + + [PluginService] internal static IDalamudPluginInterface PluginInterface { get; private set; } = null!; + + [PluginService] internal static IChatGui ChatGui { get; private set; } = null!; + + [PluginService] internal static ICommandManager CommandManager { get; private set; } = null!; + + [PluginService] internal static IDataManager DataManager { get; private set; } = null!; + + [PluginService] internal static IFramework Framework { get; private set; } = null!; + + public void Dispose() + { + CurrentStep?.Dispose(); + WindowSystem.RemoveAllWindows(); + } + + private void OnAuraciteCommand(string command, string arguments) + { + if (CurrentStep == null) + { + _stepIndex = -1; + package = new CharacterJson(); + NextStep(); + StepWindow.IsOpen = true; + } + } + + private void NextStep() + { + _stepIndex++; + if (_stepIndex >= _steps.Count) + { + CurrentStep?.Dispose(); + CurrentStep = null; + StepWindow.IsOpen = false; + return; + } + CurrentStep = (IStep)Activator.CreateInstance(_steps[_stepIndex])!; + CurrentStep.Completed += NextStep; + CurrentStep.Run(); + } + + public void Stop() + { + CurrentStep = null; + StepWindow.IsOpen = false; + package = null; + } + + private void CheckCurrentStep(IFramework framework) + { + if (CurrentStep != null && CurrentStep.NeedsUpdateEveryFrame()) { + CurrentStep.Run(); + } + } +} diff --git a/Auracite/StepWindow.cs b/Auracite/StepWindow.cs new file mode 100644 index 0000000..3fb0660 --- /dev/null +++ b/Auracite/StepWindow.cs @@ -0,0 +1,53 @@ +using System; +using Dalamud.Interface.Windowing; +using Dalamud.Bindings.ImGui; +using System.Diagnostics; + +namespace Auracite; + +public class StepWindow : Window, IDisposable +{ + private Plugin plugin; + + public StepWindow(Plugin plugin) : base("Auracite", ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoSavedSettings) + { + this.plugin = plugin; + this.ShowCloseButton = false; + } + + public void Dispose() + { + + } + + public override void Draw() + { + if (Plugin.CurrentStep != null) + { + if (Plugin.CurrentStep.IsEnd()) { + ImGui.Text("Archive created! Please download it below and keep it in a safe place."); + ImGui.Text("The plugin can be disabled once you're done using it."); + + if (ImGui.Button("Download")) + { + Process.Start(new ProcessStartInfo { FileName = "http://localhost:42073/download", UseShellExecute = true }); + } + ImGui.SameLine(); + if (ImGui.Button("Close")) + { + plugin.Stop(); + } + } else { + ImGui.Text($"Step: {Plugin.CurrentStep.StepName()}"); + ImGui.Separator(); + ImGui.Text(Plugin.CurrentStep.StepDescription()); + + ImGui.TextDisabled("This step requires manual user action."); + } + } + else + { + ImGui.Text("Auracite is not running."); + } + } +} diff --git a/dalamud/Auracite/AdventurerPlateStep.cs b/Auracite/Steps/AdventurerPlateStep.cs similarity index 80% rename from dalamud/Auracite/AdventurerPlateStep.cs rename to Auracite/Steps/AdventurerPlateStep.cs index c2f916e..4e24e14 100644 --- a/dalamud/Auracite/AdventurerPlateStep.cs +++ b/Auracite/Steps/AdventurerPlateStep.cs @@ -8,7 +8,6 @@ using SharpDX.Direct3D11; using SharpDX.DXGI; using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; namespace Auracite; @@ -34,15 +33,13 @@ public void Run() unsafe { var storage = AgentCharaCard.Instance()->Data; - var image = GetCurrentCharaViewImage(); - Plugin.package.portrait = image.ToBase64String(PngFormat.Instance); + var image = GetCurrentCharaViewImage();; var plateDesign = storage->PlateDesign; if (plateDesign.BasePlate != 0) { - Plugin.package.base_plate = GetImage(ResolveCardBase(plateDesign.BasePlate)) - .ToBase64String(PngFormat.Instance); + Plugin.base_plate = GetImage(ResolveCardBase(plateDesign.BasePlate)); } for (int i = 0; i < plateDesign.NumDecorations; i++) @@ -58,32 +55,27 @@ public void Run() { case AgentCharaCard.DecorationType.PatternOverlay: { - Plugin.package.pattern_overlay = GetImage(ResolveCardDecoration(rowIndex)) - .ToBase64String(PngFormat.Instance); + Plugin.pattern_overlay = GetImage(ResolveCardDecoration(rowIndex)); } break; case AgentCharaCard.DecorationType.Backing: { - Plugin.package.backing = GetImage(ResolveCardDecoration(rowIndex)) - .ToBase64String(PngFormat.Instance); + Plugin.backing = GetImage(ResolveCardDecoration(rowIndex)); } break; case AgentCharaCard.DecorationType.PortraitFrame: { - Plugin.package.portrait_frame = GetImage(ResolveCardDecoration(rowIndex)) - .ToBase64String(PngFormat.Instance); + Plugin.portrait_frame = GetImage(ResolveCardDecoration(rowIndex)); } break; case AgentCharaCard.DecorationType.PlateFrame: { - Plugin.package.plate_frame = GetImage(ResolveCardDecoration(rowIndex)) - .ToBase64String(PngFormat.Instance); + Plugin.plate_frame = GetImage(ResolveCardDecoration(rowIndex)); } break; case AgentCharaCard.DecorationType.Accent: { - Plugin.package.accent = GetImage(ResolveCardDecoration(rowIndex)) - .ToBase64String(PngFormat.Instance); + Plugin.accent = GetImage(ResolveCardDecoration(rowIndex)); } break; } @@ -91,14 +83,12 @@ public void Run() if (plateDesign.TopBorder != 0) { - Plugin.package.top_border = GetImage(ResolveCardHeaderTop(plateDesign.TopBorder)) - .ToBase64String(PngFormat.Instance); + Plugin.top_border = GetImage(ResolveCardHeaderTop(plateDesign.TopBorder)); } if (plateDesign.BottomBorder != 0) { - Plugin.package.bottom_border = GetImage(ResolveCardHeaderBottom(plateDesign.BottomBorder)) - .ToBase64String(PngFormat.Instance); + Plugin.bottom_border = GetImage(ResolveCardHeaderBottom(plateDesign.BottomBorder)); } Plugin.package.plate_title = Title?.Feminine.ToString(); // TODO: Support mascs @@ -209,4 +199,9 @@ public string ResolveCardBase(uint rowIndex) var row = Plugin.DataManager.GetExcelSheet()?.GetRow(rowIndex); return $"ui/icon/{row?.BottomImage.ToString().Substring(0, 3)}000/{row?.BottomImage}_hr1.tex"; } + + public bool NeedsUpdateEveryFrame() + { + return true; // So we can wait for the window to open. + } } diff --git a/Auracite/Steps/AppearanceStep.cs b/Auracite/Steps/AppearanceStep.cs new file mode 100644 index 0000000..0760147 --- /dev/null +++ b/Auracite/Steps/AppearanceStep.cs @@ -0,0 +1,53 @@ +using Dalamud.Game.ClientState.Objects.Enums; + +namespace Auracite; + +public class AppearanceStep : IStep +{ + public event IStep.CompletedDelegate? Completed; + + public void Run() + { + if (Plugin.ObjectTable.LocalPlayer != null) + { + Plugin.package.appearance.model_type = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.ModelType]; + Plugin.package.appearance.height = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.Height]; + Plugin.package.appearance.face_type = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.FaceType]; + Plugin.package.appearance.hair_style = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.HairStyle]; + Plugin.package.appearance.has_highlights = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.HasHighlights] == 1; + Plugin.package.appearance.skin_color = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.SkinColor]; + Plugin.package.appearance.eye_color = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.EyeColor]; + Plugin.package.appearance.hair_color = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.HairColor]; + Plugin.package.appearance.hair_color2 = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.HairColor2]; + Plugin.package.appearance.face_features = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.FaceFeatures]; + Plugin.package.appearance.face_features_color = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.FaceFeaturesColor]; + Plugin.package.appearance.eyebrows = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.Eyebrows]; + Plugin.package.appearance.eye_color2 = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.EyeColor2]; + Plugin.package.appearance.eye_shape = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.EyeShape]; + Plugin.package.appearance.nose_shape = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.NoseShape]; + Plugin.package.appearance.jaw_shape = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.JawShape]; + Plugin.package.appearance.lip_style = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.LipStyle]; + Plugin.package.appearance.lip_color = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.LipColor]; + Plugin.package.appearance.race_feature_size = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.RaceFeatureSize]; + Plugin.package.appearance.race_feature_type = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.RaceFeatureType]; + Plugin.package.appearance.bust_size = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.BustSize]; + Plugin.package.appearance.facepaint = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.Facepaint]; + Plugin.package.appearance.facepaint_color = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.FacepaintColor]; + } + Completed?.Invoke(); + } + + public string StepName() + { + return "Appearance"; + } + + public string StepDescription() + { + return "No user action required."; + } + + public void Dispose() + { + } +} diff --git a/Auracite/Steps/EndStep.cs b/Auracite/Steps/EndStep.cs new file mode 100644 index 0000000..6b1fa7e --- /dev/null +++ b/Auracite/Steps/EndStep.cs @@ -0,0 +1,113 @@ +using System.IO; +using System.IO.Compression; +using EmbedIO; +using EmbedIO.Routing; +using EmbedIO.WebApi; +using Newtonsoft.Json; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Png; + +namespace Auracite; + +public class EndStep : IStep +{ + public event IStep.CompletedDelegate? Completed; + + public void Run() + { + StartWebServer(); + } + + public void End() + { + Completed?.Invoke(); + } + + public string StepName() + { + return "Save Archive"; + } + + public string StepDescription() + { + return "Save your archived character ZIP."; + } + + private class Controller : WebApiController + { + private EndStep _endStep; + + public Controller(EndStep endStep) + { + _endStep = endStep; + } + + [Route(HttpVerbs.Get, "/download")] + public void GetPackage() + { + Response.Headers.Set(HttpHeaderNames.AccessControlAllowOrigin, "*"); + Response.ContentType = "application/zip"; + using var writer = HttpContext.OpenResponseStream(true); + + using (var archive = new ZipArchive(writer, ZipArchiveMode.Create, true)) + { + using (var entryStream = archive.CreateEntry("character.json").Open()) + { + using (var streamWriter = new StreamWriter(entryStream)) + { + streamWriter.Write(JsonConvert.SerializeObject(Plugin.package, Formatting.Indented)); + } + } + + WriteImage(archive, Plugin.accent, "accent.png"); + WriteImage(archive, Plugin.backing, "backing.png"); + WriteImage(archive, Plugin.base_plate, "base-plate.png"); + WriteImage(archive, Plugin.pattern_overlay, "pattern-overlay.png"); + WriteImage(archive, Plugin.plate_frame, "plate-frame.png"); + WriteImage(archive, Plugin.portrait, "plate-portrait.png"); + WriteImage(archive, Plugin.top_border, "top-border.png"); + WriteImage(archive, Plugin.bottom_border, "bottom-border.png"); + } + } + } + + private WebServer? _server; + + private void StartWebServer() + { + ShutdownWebServer(); + + _server = new WebServer(o => o + .WithUrlPrefix("http://localhost:42073/") + .WithMode(HttpListenerMode.EmbedIO)) + .WithWebApi("/", m => m.WithController(() => new Controller(this))); + _server.RunAsync(); + } + + private void ShutdownWebServer() + { + _server?.Dispose(); + _server = null; + } + + public void Dispose() + { + ShutdownWebServer(); + } + + public bool IsEnd() + { + return true; + } + + private static void WriteImage(ZipArchive archive, Image? image, string path) + { + if (image != null) + { + using (var entryStream = archive.CreateEntry(path).Open()) + { + image.Save(entryStream, PngFormat.Instance); + } + } + } +} diff --git a/dalamud/Auracite/InventoryStep.cs b/Auracite/Steps/InventoryStep.cs similarity index 77% rename from dalamud/Auracite/InventoryStep.cs rename to Auracite/Steps/InventoryStep.cs index 4a9c850..291a1b2 100644 --- a/dalamud/Auracite/InventoryStep.cs +++ b/Auracite/Steps/InventoryStep.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using FFXIVClientStructs.FFXIV.Client.Game; namespace Auracite; @@ -18,7 +19,7 @@ public void Run() Plugin.package.inventory3 = ProcessContainer(manager->GetInventoryContainer(InventoryType.Inventory3)); Plugin.package.inventory4 = ProcessContainer(manager->GetInventoryContainer(InventoryType.Inventory4)); - Plugin.package.equipped_items = ProcessContainer(manager->GetInventoryContainer(InventoryType.EquippedItems)); + Plugin.package.equipped = ProcessContainer(manager->GetInventoryContainer(InventoryType.EquippedItems)); Plugin.package.currency = ProcessContainer(manager->GetInventoryContainer(InventoryType.Currency)); @@ -39,9 +40,9 @@ public void Run() Completed?.Invoke(); } - private unsafe Auracite.Plugin.InventoryContainer ProcessContainer(FFXIVClientStructs.FFXIV.Client.Game.InventoryContainer *container) { - var serializedContainer = new Auracite.Plugin.InventoryContainer(); - serializedContainer.items = new System.Collections.Generic.List(); // TODO: lol + private unsafe Auracite.InventoryContainer ProcessContainer(FFXIVClientStructs.FFXIV.Client.Game.InventoryContainer *container) { + var serializedContainer = new Auracite.InventoryContainer(); + serializedContainer.items = new System.Collections.Generic.List(); // TODO: lol for (int i = 0; i < container->Size; i++) { var item = container->GetInventorySlot(i); @@ -50,12 +51,18 @@ private unsafe Auracite.Plugin.InventoryContainer ProcessContainer(FFXIVClientSt continue; } - var serializedItem = new Auracite.Plugin.InventoryItem(); - serializedItem.condition = item->GetCondition(); - serializedItem.id = item->GetBaseItemId(); - serializedItem.quantity = item->GetQuantity(); + var serializedItem = new Auracite.InventoryItem(); serializedItem.slot = item->GetSlot(); + serializedItem.quantity = item->GetQuantity(); + serializedItem.id = item->GetBaseItemId(); + serializedItem.crafter_content_id = item->GetCrafterContentId(); + serializedItem.item_flags = (byte)item->GetFlags(); + serializedItem.condition = item->GetCondition(); + serializedItem.spiritbond_or_collectability = item->GetSpiritbondOrCollectability(); serializedItem.glamour_id = item->GetGlamourId(); + serializedItem.materia = new List(item->Materia.ToArray()); + serializedItem.materia_grades = new List(item->MateriaGrades.ToArray()); + serializedItem.stains = new List(item->Stains.ToArray()); serializedContainer.items.Add(serializedItem); } diff --git a/Auracite/Steps/MiscStep.cs b/Auracite/Steps/MiscStep.cs new file mode 100644 index 0000000..21e1a7e --- /dev/null +++ b/Auracite/Steps/MiscStep.cs @@ -0,0 +1,146 @@ +using FFXIVClientStructs.FFXIV.Client.Game.UI; +using FFXIVClientStructs.FFXIV.Client.Game.Character; +using Lumina.Excel.Sheets; +using System.Collections.Generic; +using FFXIVClientStructs.FFXIV.Client.Game; + +namespace Auracite; + +public class MiscStep : IStep +{ + public event IStep.CompletedDelegate? Completed; + + public void Run() + { + unsafe + { + var playerState = PlayerState.Instance(); + var uiState = UIState.Instance(); + var questManager = QuestManager.Instance(); + + Plugin.package!.name = playerState->CharacterNameString; + Plugin.package!.world = IStep.SaveNameValue(Plugin.ObjectTable.LocalPlayer!.HomeWorld.RowId, world => world.Name); + Plugin.package!.data_center = IStep.SaveNameValue(Plugin.ObjectTable.LocalPlayer!.HomeWorld.Value.DataCenter.RowId, data_center => data_center.Name); + Plugin.package!.city_state = IStep.SaveNameValue(playerState->StartTown, town => town.Name); + Plugin.package!.nameday = new DayMonthValue(); + Plugin.package!.nameday.day = playerState->BirthDay; + Plugin.package!.nameday.month = playerState->BirthMonth; + Plugin.package!.guardian = IStep.SaveNameValue(playerState->GuardianDeity, guardian => guardian.Name); + Plugin.package!.gender = new NameValue(); + Plugin.package!.gender.value = playerState->Sex; + Plugin.package!.gender.name = playerState->Sex == 1 ? "Female" : "Male"; + Plugin.package!.tribe = IStep.SaveNameValue(playerState->Tribe, tribe => playerState->Sex == 1 ? tribe.Feminine : tribe.Masculine); + Plugin.package!.race = IStep.SaveNameValue(playerState->Race, race => playerState->Sex == 1 ? race.Feminine : race.Masculine); + var classJobSheet = Plugin.DataManager.GetExcelSheet()!; + for (int i = 0; i < playerState->ClassJobLevels.Length; i++) { + var classLevel = new ClassJobLevel(); + foreach (var row in classJobSheet) { + if (row.ExpArrayIndex == i) { + classLevel.name = row.NameEnglish.ToString()!; + classLevel.value = row.RowId; + break; + } + } + classLevel.level = playerState->ClassJobLevels[i]; + classLevel.exp = playerState->ClassJobExperience[i]; + + // Exclude currently unavailable jobs + if (classLevel.name != null) { + Plugin.package!.classjob_levels.Add(classLevel); + } + } + Plugin.package!.grand_company = IStep.SaveNameValue(playerState->GrandCompany, company => company.Name); + Plugin.package!.grand_company_ranks.Add(playerState->GCRankMaelstrom); + Plugin.package!.grand_company_ranks.Add(playerState->GCRankTwinAdders); + Plugin.package!.grand_company_ranks.Add(playerState->GCRankImmortalFlames); + + Plugin.package.is_battle_mentor = PlayerState.Instance()->IsBattleMentor(); + Plugin.package.is_trade_mentor = PlayerState.Instance()->IsTradeMentor(); + Plugin.package.is_novice = PlayerState.Instance()->IsNovice(); + Plugin.package.is_returner = PlayerState.Instance()->IsReturner(); + Plugin.package.player_commendations = PlayerState.Instance()->PlayerCommendations; + + var localPlayer = Plugin.ObjectTable.LocalPlayer; + if (localPlayer != null) + { + var gameObject = (Character*)localPlayer.Address; + Plugin.package.voice = gameObject->Vfx.VoiceId; + } + + // unlocks + Plugin.package.unlocks = IStep.ConsumeBitArray(uiState->UnlockLinksBitArray); + Plugin.package.seen_active_help = IStep.ConsumeBitArray(uiState->UnlockedHowTosBitArray); + Plugin.package.minions = IStep.ConsumeBitArray(uiState->UnlockedCompanionsBitArray); + Plugin.package.mounts = IStep.ConsumeBitArray(playerState->UnlockedMountsBitArray); + Plugin.package.orchestrion_rolls = IStep.ConsumeBitArray(playerState->UnlockedOrchestrionRollsBitArray); + Plugin.package.cutscene_seen = IStep.ConsumeBitArray(uiState->SeenCutscenesBitArray); + Plugin.package.ornaments = IStep.ConsumeBitArray(playerState->UnlockedOrnamentsBitArray); + Plugin.package.caught_fish = IStep.ConsumeBitArray(playerState->CaughtFishBitArray); + Plugin.package.caught_spearfish = IStep.ConsumeBitArray(playerState->CaughtSpearfishBitArray); + Plugin.package.adventures = IStep.ConsumeBitArray(playerState->CompletedAdventuresBitArray); + Plugin.package.triple_triad_cards = IStep.ConsumeBitArray(uiState->UnlockedTripleTriadCardsBitArray); + Plugin.package.glasses_styles = IStep.ConsumeBitArray(playerState->UnlockedGlassesStylesBitArray); + Plugin.package.chocobo_taxi_stands = IStep.ConsumeBitArray(uiState->UnlockedChocoboTaxiStandsBitArray); + Plugin.package.unlocked_companion_equip = new List(uiState->Buddy.CompanionInfo.BuddyEquipUnlockBitmask.ToArray()); + + // aether currents + Plugin.package.comp_flg_set = IStep.ConsumeBitArray(playerState->UnlockedAetherCurrentCompFlgSetsBitArray); + Plugin.package.unlocked_aether_currents = IStep.ConsumeBitArray(playerState->UnlockedAetherCurrentsBitArray); + + // aetherytes + Plugin.package.unlocked_aetherytes = IStep.ConsumeBitArray(uiState->UnlockedAetherytesBitArray); + Plugin.package.homepoint = playerState->HomeAetheryteId; + Plugin.package.favorite_aetherytes = new List(playerState->FavouriteAetherytes.ToArray()); + Plugin.package.free_aetheryte = playerState->FreeAetheryteId; + + // classjob + Plugin.package.current_class = playerState->CurrentClassJobId; + Plugin.package.first_class = playerState->FirstClass; + Plugin.package.rested_exp = (int)playerState->BaseRestedExperience; + + // content + Plugin.package.unlocked_special_content = IStep.ConsumeBitArray(playerState->UnlockedSpecialContentBitArray); + Plugin.package.unlocked_raids = IStep.ConsumeBitArray(playerState->UnlockedRaidsBitArray); + Plugin.package.unlocked_dungeons = IStep.ConsumeBitArray(playerState->UnlockedDungeonsBitArray); + Plugin.package.unlocked_guildhests = IStep.ConsumeBitArray(playerState->UnlockedGuildOrdersBitArray); + Plugin.package.unlocked_trials = IStep.ConsumeBitArray(playerState->UnlockedTrialsBitArray); + Plugin.package.unlocked_crystalline_conflicts = IStep.ConsumeBitArray(playerState->UnlockedCrystallineConflictsBitArray); + Plugin.package.unlocked_frontlines = IStep.ConsumeBitArray(playerState->UnlockedFrontlinesBitArray); + Plugin.package.cleared_raids = IStep.ConsumeBitArray(playerState->CompletedRaidsBitArray); + Plugin.package.cleared_dungeons = IStep.ConsumeBitArray(playerState->CompletedDungeonsBitArray); + Plugin.package.cleared_guildhests = IStep.ConsumeBitArray(playerState->CompletedGuildOrdersBitArray); + Plugin.package.cleared_trials = IStep.ConsumeBitArray(playerState->CompletedTrialsBitArray); + Plugin.package.cleared_crystalline_conflicts = IStep.ConsumeBitArray(playerState->CompletedCrystallineConflictsBitArray); + Plugin.package.cleared_frontlines = IStep.ConsumeBitArray(playerState->CompletedFrontlinesBitArray); + Plugin.package.cleared_masked_carnivale = IStep.ConsumeBitArray(playerState->CompletedMaskedCarnivaleBitArray); + Plugin.package.unlocked_misc_content = IStep.ConsumeBitArray(playerState->UnlockedMiscContentBitArray); + Plugin.package.cleared_misc_content = IStep.ConsumeBitArray(playerState->CompletedMiscContentBitArray); + + // quest + Plugin.package.completed_quests = IStep.ConsumeBitArray(questManager->CompletedQuestsBitArray); + + // volatile + Plugin.package.position_x = Plugin.ObjectTable.LocalPlayer.Position.X; + Plugin.package.position_y = Plugin.ObjectTable.LocalPlayer.Position.Y; + Plugin.package.position_z = Plugin.ObjectTable.LocalPlayer.Position.Z; + Plugin.package.rotation = Plugin.ObjectTable.LocalPlayer.Rotation; + Plugin.package.zone_id = Plugin.ClientState.TerritoryType; + } + + Completed?.Invoke(); + } + + public string StepName() + { + return "Misc Data"; + } + + public string StepDescription() + { + return "No user action required."; + } + + public void Dispose() + { + } +} diff --git a/dalamud/Auracite/PlaytimeStep.cs b/Auracite/Steps/PlaytimeStep.cs similarity index 100% rename from dalamud/Auracite/PlaytimeStep.cs rename to Auracite/Steps/PlaytimeStep.cs diff --git a/Auracite/Steps/TitleStep.cs b/Auracite/Steps/TitleStep.cs new file mode 100644 index 0000000..71d8e4e --- /dev/null +++ b/Auracite/Steps/TitleStep.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using FFXIVClientStructs.FFXIV.Client.Game.Character; +using FFXIVClientStructs.FFXIV.Client.Game.UI; +using Lumina.Excel.Sheets; + +namespace Auracite; + +public class TitleStep : IStep +{ + public TitleStep() + { + } + + public void Dispose() + { + } + + public event IStep.CompletedDelegate? Completed; + + public void Run() + { + unsafe { + var uiState = UIState.Instance(); + var playerState = PlayerState.Instance(); + if (uiState->TitleList.DataReceived) + { + Plugin.package!.titles = new List(uiState->TitleList.TitlesUnlockBitmask.ToArray()); + Plugin.package!.title = IStep.SaveNameValue(((Character*)Plugin.ObjectTable.LocalPlayer!.Address)->TitleId, title => playerState->Sex == 1 ? title.Feminine : title.Masculine); + + Completed?.Invoke(); + } + } + } + + public string StepName() + { + return "Titles"; + } + + public string StepDescription() + { + return "Open the title list."; + } + + public bool NeedsUpdateEveryFrame() + { + return true; // So we can wait for the title window to open. + } +} diff --git a/dalamud/Auracite/packages.lock.json b/Auracite/packages.lock.json similarity index 100% rename from dalamud/Auracite/packages.lock.json rename to Auracite/packages.lock.json diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 6b697e9..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,2348 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "array-init" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc" - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "auracite" -version = "0.1.0" -dependencies = [ - "ahash", - "base64", - "console_error_panic_hook", - "getrandom 0.3.4", - "minijinja", - "physis", - "regex", - "reqwest", - "scraper", - "serde", - "serde_json", - "tokio", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-time", - "zip", -] - -[[package]] -name = "aws-lc-rs" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9a7b350e3bb1767102698302bc37256cbd48422809984b98d292c40e2579aa9" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.37.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549" -dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", -] - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "binrw" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81419ff39e6ed10a92a7f125290859776ced35d9a08a665ae40b23e7ca702f30" -dependencies = [ - "array-init", - "binrw_derive", - "bytemuck", -] - -[[package]] -name = "binrw_derive" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "376404e55ec40d0d6f8b4b7df3f87b87954bd987f0cf9a7207ea3b6ea5c9add4" -dependencies = [ - "either", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "bitflags" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" - -[[package]] -name = "bumpalo" -version = "3.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" - -[[package]] -name = "bytemuck" -version = "1.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - -[[package]] -name = "cc" -version = "1.2.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "cmake" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" -dependencies = [ - "cc", -] - -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - -[[package]] -name = "cssparser" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa", - "phf", - "smallvec", -] - -[[package]] -name = "cssparser-macros" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "derive_more" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" -dependencies = [ - "proc-macro2", - "quote", - "rustc_version", - "syn", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "dtoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" - -[[package]] -name = "dtoa-short" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" -dependencies = [ - "dtoa", -] - -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - -[[package]] -name = "ego-tree" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2972feb8dffe7bc8c5463b1dacda1b0dfbed3710e50f977d965429692d74cd8" - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-sink" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "slab", -] - -[[package]] -name = "getopts" -version = "0.2.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "h2" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "half" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" -dependencies = [ - "cfg-if", - "crunchy", - "zerocopy", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - -[[package]] -name = "html5ever" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6452c4751a24e1b99c3260d505eaeee76a050573e61f30ac2c924ddc7236f01e" -dependencies = [ - "log", - "markup5ever", -] - -[[package]] -name = "http" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "hyper" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "pin-utils", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "system-configuration", - "tokio", - "tower-service", - "tracing", - "windows-registry", -] - -[[package]] -name = "icu_collections" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" - -[[package]] -name = "icu_properties" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" - -[[package]] -name = "icu_provider" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "ipnet" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" - -[[package]] -name = "iri-string" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "itoa" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" - -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dc6f6450b3f6d4ed5b16327f38fed626d375a886159ca555bd7822c0c3a5a6" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "libc" -version = "0.2.182" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" - -[[package]] -name = "libz-rs-sys" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c10501e7805cee23da17c7790e59df2870c0d4043ec6d03f67d31e2b53e77415" -dependencies = [ - "zlib-rs", -] - -[[package]] -name = "litemap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - -[[package]] -name = "markup5ever" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c3294c4d74d0742910f8c7b466f44dda9eb2d5742c1e430138df290a1e8451c" -dependencies = [ - "log", - "tendril", - "web_atoms", -] - -[[package]] -name = "memchr" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "minijinja" -version = "2.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c54f3bcc034dd74496b5ca929fd0b710186672d5ff0b0f255a9ceb259042ece" -dependencies = [ - "serde", -] - -[[package]] -name = "mio" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "phf" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" -dependencies = [ - "phf_macros", - "phf_shared", - "serde", -] - -[[package]] -name = "phf_codegen" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" -dependencies = [ - "fastrand", - "phf_shared", -] - -[[package]] -name = "phf_macros" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "phf_shared" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" -dependencies = [ - "siphasher", -] - -[[package]] -name = "physis" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51018fe6a91cd6292c89de282fa4426465080d56a0d62ae21628edc95af23a93" -dependencies = [ - "binrw", - "bitflags", - "half", - "libz-rs-sys", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "potential_utf" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" -dependencies = [ - "zerovec", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror 2.0.18", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" -dependencies = [ - "aws-lc-rs", - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - -[[package]] -name = "quote" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" - -[[package]] -name = "reqwest" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "mime", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "rustls-platform-verifier", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustls" -version = "0.23.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" -dependencies = [ - "aws-lc-rs", - "once_cell", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pki-types" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-platform-verifier" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" -dependencies = [ - "core-foundation 0.10.1", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" - -[[package]] -name = "rustls-webpki" -version = "0.103.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" -dependencies = [ - "aws-lc-rs", - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "scraper" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93cecd86d6259499c844440546d02f55f3e17bd286e529e48d1f9f67e92315cb" -dependencies = [ - "cssparser", - "ego-tree", - "getopts", - "html5ever", - "precomputed-hash", - "selectors", - "tendril", -] - -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags", - "core-foundation 0.10.1", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "selectors" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feef350c36147532e1b79ea5c1f3791373e61cbd9a6a2615413b3807bb164fb7" -dependencies = [ - "bitflags", - "cssparser", - "derive_more", - "log", - "new_debug_unreachable", - "phf", - "phf_codegen", - "precomputed-hash", - "rustc-hash", - "servo_arc", - "smallvec", -] - -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "servo_arc" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "siphasher" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "socket2" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" -dependencies = [ - "libc", - "windows-sys 0.60.2", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "string_cache" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" -dependencies = [ - "new_debug_unreachable", - "parking_lot", - "phf_shared", - "precomputed-hash", -] - -[[package]] -name = "string_cache_codegen" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", -] - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "system-configuration" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" -dependencies = [ - "bitflags", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tendril" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" -dependencies = [ - "futf", - "mac", - "utf-8", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl 2.0.18", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tinystr" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.49.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" -dependencies = [ - "bytes", - "libc", - "mio", - "pin-project-lite", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" -dependencies = [ - "bitflags", - "bytes", - "futures-util", - "http", - "http-body", - "iri-string", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "typed-path" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-width" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.2+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.113" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60722a937f594b7fde9adb894d7c092fc1bb6612897c46368d18e7a20208eff2" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a89f4650b770e4521aa6573724e2aed4704372151bd0de9d16a3bbabb87441a" -dependencies = [ - "cfg-if", - "futures-util", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.113" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac8c6395094b6b91c4af293f4c79371c163f9a6f56184d2c9a85f5a95f3950" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.113" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3fabce6159dc20728033842636887e4877688ae94382766e00b180abac9d60" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.113" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0e091bdb824da87dc01d967388880d017a0a9bc4f3bdc0d86ee9f9336e3bb5" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "web-sys" -version = "0.3.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705eceb4ce901230f8625bd1d665128056ccbe4b7408faa625eec1ba80f59a97" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web_atoms" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a9779e9f04d2ac1ce317aee707aa2f6b773afba7b931222bff6983843b1576" -dependencies = [ - "phf", - "phf_codegen", - "string_cache", - "string_cache_codegen", -] - -[[package]] -name = "webpki-root-certs" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" - -[[package]] -name = "writeable" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" - -[[package]] -name = "yoke" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.8.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" - -[[package]] -name = "zerotrie" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zip" -version = "8.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e499faf5c6b97a0d086f4a8733de6d47aee2252b8127962439d8d4311a73f72" -dependencies = [ - "crc32fast", - "indexmap", - "memchr", - "typed-path", -] - -[[package]] -name = "zlib-rs" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40990edd51aae2c2b6907af74ffb635029d5788228222c4bb811e9351c0caad3" - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 2edbf67..0000000 --- a/Cargo.toml +++ /dev/null @@ -1,65 +0,0 @@ -[package] -name = "auracite" -version = "0.1.0" -edition = "2024" -description = "Export your FFXIV character in portable, generic formats" -repository = "https://github.com/redstrate/Auracite" -license = "AGPL-3" - -[lib] -crate-type = ["cdylib", "rlib"] - -[features] -debug = ["dep:console_error_panic_hook"] - -[dependencies] -# Used to scrape the Lodestone HTML pages -scraper = { version = "0.25" } - -# Used to serialize the JSON data we export -serde = { version = "1.0", features = ["derive"], default-features = false } - -# Used to do some misc regex operations during scraping -regex = { version = "1.12", default-features = false, features = ["unicode-perl"] } - -# Used to generate the HTML page to easily preview your exported data -minijinja = { version = "2.16", features = ["serde"], default-features = false } - -# Download files -reqwest = { version = "0.13" } - -# Zip the character archive -zip = { version = "8.1", default-features = false } - -# Exporting propietary game data -physis = { version = "0.5" } - -# Encoding the character archive to base64 so the browser can download it and decoding the base64 images from the client -base64 = { version = "0.22", default-features = false } - -# Not used directly by us, but to disable the "std" feature and is used by the scraper crate. -ahash = { version = "0.8", default-features = false } - -[target.'cfg(target_family = "wasm")'.dependencies] -# Used to generate the WebAssembly version -wasm-bindgen = { version = "0.2", default-features = false } -wasm-bindgen-futures = { version = "0.4", default-features = false } - -# For async -tokio = { version = "1.49", features = ["rt", "macros"], default-features = false } - -serde_json = { version = "1.0", default-features = false, features = ["alloc"] } - -console_error_panic_hook = { version = "0.1", optional = true } - -# to fix some dependency that doesn't enable the feature -getrandom = { version = "0.3", features = ["wasm_js"] } - -# Because SystemTime doesn't work on Web -web-time = "1.1" - -[target.'cfg(not(target_family = "wasm"))'.dependencies] -# For async -tokio = { version = "1.49", features = ["rt", "rt-multi-thread", "macros"], default-features = false } - -serde_json = { version = "1.0", default-features = false, features = ["std"] } diff --git a/README.md b/README.md index 3bdf699..6d157ba 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,13 @@ # Auracite -Export your FFXIV character in portable, generic formats. This includes +Archive your FFXIV character in a portable, generic format. This includes data in machine-readable JSON (and can be imported by other programs -like [Kawari](https://github.com/redstrate/Kawari)) and a Lodestone-esque -HTML page which you can display in your browser. +like [Kawari](https://github.com/redstrate/Kawari).) ## Usage -Auracite runs inside your web browser, and can be accessed at [auracite.xiv.zone](https://auracite.xiv.zone/). - -## Building - -Use `wasm-pack` or the helper script `scripts/build-web.sh`. A folder called `pkg/` will be generated, and the HTML files live in `web/`. - -### Dalamud Mode - -Auracite can only collect so much data from the Lodestone, some data can only be collected when logged in. To do this, -we provide a Dalamud plugin to run alongside the tool. The plugin is currently available -[in my personal Dalamud repository](https://github.com/redstrate/DalamudPlugins). The plugin can be -safely removed if you're done using Auracite. +It works by running in Dalamud, and the plugin is currently available +[in my personal Dalamud repository](https://github.com/redstrate/DalamudPlugins). ## License diff --git a/dalamud/Auracite/AppearanceStep.cs b/dalamud/Auracite/AppearanceStep.cs deleted file mode 100644 index 136affd..0000000 --- a/dalamud/Auracite/AppearanceStep.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Dalamud.Game.ClientState.Objects.Enums; - -namespace Auracite; - -public class AppearanceStep : IStep -{ - public event IStep.CompletedDelegate? Completed; - - public void Run() - { - if (Plugin.ObjectTable.LocalPlayer != null) - { - Plugin.package.race = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.Race]; - Plugin.package.gender = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.Gender]; - Plugin.package.model_type = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.ModelType]; - Plugin.package.height = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.Height]; - Plugin.package.tribe = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.Tribe]; - Plugin.package.face_type = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.FaceType]; - Plugin.package.hair_style = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.HairStyle]; - Plugin.package.has_highlights = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.HasHighlights] == 1; - Plugin.package.skin_color = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.SkinColor]; - Plugin.package.eye_color = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.EyeColor]; - Plugin.package.hair_color = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.HairColor]; - Plugin.package.hair_color2 = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.HairColor2]; - Plugin.package.face_features = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.FaceFeatures]; - Plugin.package.face_features_color = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.FaceFeaturesColor]; - Plugin.package.eyebrows = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.Eyebrows]; - Plugin.package.eye_color2 = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.EyeColor2]; - Plugin.package.eye_shape = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.EyeShape]; - Plugin.package.nose_shape = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.NoseShape]; - Plugin.package.jaw_shape = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.JawShape]; - Plugin.package.lip_style = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.LipStyle]; - Plugin.package.lip_color = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.LipColor]; - Plugin.package.race_feature_size = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.RaceFeatureSize]; - Plugin.package.race_feature_type = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.RaceFeatureType]; - Plugin.package.bust_size = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.BustSize]; - Plugin.package.facepaint = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.Facepaint]; - Plugin.package.facepaint_color = Plugin.ObjectTable.LocalPlayer.Customize[(int)CustomizeIndex.FacepaintColor]; - } - Completed?.Invoke(); - } - - public string StepName() - { - return "Appearance"; - } - - public string StepDescription() - { - return "No user action required."; - } - - public void Dispose() - { - } -} diff --git a/dalamud/Auracite/Auracite.json b/dalamud/Auracite/Auracite.json deleted file mode 100644 index 2a06978..0000000 --- a/dalamud/Auracite/Auracite.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Author": "redstrate", - "Name": "Auracite", - "Punchline": "Export your FFXIV character in portable, generic formats", - "Description": "Export your FFXIV character in portable, generic formats.", - "Tags": [], - "RepoUrl": "https://github.com/redstrate/Auracite" -} diff --git a/dalamud/Auracite/EndStep.cs b/dalamud/Auracite/EndStep.cs deleted file mode 100644 index d4159e0..0000000 --- a/dalamud/Auracite/EndStep.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System.Text; -using EmbedIO; -using EmbedIO.Routing; -using EmbedIO.WebApi; -using Newtonsoft.Json; - -namespace Auracite; - -public class EndStep : IStep -{ - public event IStep.CompletedDelegate? Completed; - - public void Run() - { - StartWebServer(); - } - - public void End() - { - Completed?.Invoke(); - } - - public string StepName() - { - return "Waiting for Upload"; - } - - public string StepDescription() - { - return "Run Auracite to archive your character."; - } - - private class Controller : WebApiController - { - private EndStep _endStep; - - public Controller(EndStep endStep) - { - _endStep = endStep; - } - - [Route(HttpVerbs.Get, "/package")] - public void GetPackage() - { - Response.Headers.Set(HttpHeaderNames.AccessControlAllowOrigin, "*"); - Response.ContentType = MimeType.Json; - using var writer = HttpContext.OpenResponseText(Encoding.UTF8, true); - writer.Write(JsonConvert.SerializeObject(Plugin.package)); - } - - // TODO: Make this a POST request? - // This is needed since we don't know when the CORS handshake really stops. This really shouldn't be needed though. - [Route(HttpVerbs.Get, "/stop")] - public void Stop() - { - _endStep.End(); - } - } - - private WebServer? _server; - - private void StartWebServer() - { - ShutdownWebServer(); - - _server = new WebServer(o => o - .WithUrlPrefix("http://localhost:42072/") - .WithMode(HttpListenerMode.EmbedIO)) - .WithWebApi("/", m => m.WithController(() => new Controller(this))); - _server.RunAsync(); - } - - private void ShutdownWebServer() - { - _server?.Dispose(); - _server = null; - } - - public void Dispose() - { - ShutdownWebServer(); - } -} \ No newline at end of file diff --git a/dalamud/Auracite/IStep.cs b/dalamud/Auracite/IStep.cs deleted file mode 100644 index 2e6dc7d..0000000 --- a/dalamud/Auracite/IStep.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Auracite; - -public interface IStep : IDisposable -{ - public event CompletedDelegate Completed; - - void Run(); - - string StepName(); - string StepDescription(); - - delegate void CompletedDelegate(); -} \ No newline at end of file diff --git a/dalamud/Auracite/MiscStep.cs b/dalamud/Auracite/MiscStep.cs deleted file mode 100644 index 024b960..0000000 --- a/dalamud/Auracite/MiscStep.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using FFXIVClientStructs.FFXIV.Client.Game.UI; -using FFXIVClientStructs.FFXIV.Client.Game.Character; - -namespace Auracite; - -public class MiscStep : IStep -{ - public event IStep.CompletedDelegate? Completed; - - public void Run() - { - unsafe - { - Plugin.package.is_battle_mentor = PlayerState.Instance()->IsBattleMentor(); - Plugin.package.is_trade_mentor = PlayerState.Instance()->IsTradeMentor(); - Plugin.package.is_novice = PlayerState.Instance()->IsNovice(); - Plugin.package.is_returner = PlayerState.Instance()->IsReturner(); - Plugin.package.player_commendations = PlayerState.Instance()->PlayerCommendations; - Plugin.package.unlock_flags = new System.Collections.Generic.List<byte>(); // TODO: lol - Plugin.package.unlock_flags.AddRange(new ReadOnlySpan<byte>(UIState.Instance()->UnlockLinksBitArray.Pointer, UIState.Instance()->UnlockLinksBitArray.ByteLength).ToArray()); - Plugin.package.unlock_aetherytes = new System.Collections.Generic.List<byte>(); // TODO: lol - Plugin.package.unlock_aetherytes.AddRange(new ReadOnlySpan<byte>(UIState.Instance()->UnlockedAetherytesBitArray.Pointer, UIState.Instance()->UnlockedAetherytesBitArray.ByteLength).ToArray()); - - var localPlayer = Plugin.ObjectTable.LocalPlayer; - if (localPlayer != null) - { - var gameObject = (Character*)localPlayer.Address; - Plugin.package.voice = gameObject->Vfx.VoiceId; - } - } - - Completed?.Invoke(); - } - - public string StepName() - { - return "Misc Data"; - } - - public string StepDescription() - { - return "No user action required."; - } - - public void Dispose() - { - } -} diff --git a/dalamud/Auracite/Plugin.cs b/dalamud/Auracite/Plugin.cs deleted file mode 100644 index 06df774..0000000 --- a/dalamud/Auracite/Plugin.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using Dalamud.Game.Command; -using Dalamud.Interface.Windowing; -using Dalamud.IoC; -using Dalamud.Plugin; -using Dalamud.Plugin.Services; - -namespace Auracite; - -public sealed class Plugin : IDalamudPlugin -{ - public static IStep? CurrentStep; - private readonly WindowSystem WindowSystem = new("Auracite"); - - private readonly List<Type> _steps = - [typeof(AppearanceStep), typeof(InventoryStep), typeof(MiscStep), typeof(PlaytimeStep), typeof(AdventurerPlateStep), typeof(EndStep)]; - - private int _stepIndex; - - private readonly StepWindow StepWindow; - - [SuppressMessage("ReSharper", "InconsistentNaming")] - public class InventoryItem - { - public int slot; - public uint quantity; - public int condition; - public uint id; - public uint glamour_id; - } - - [SuppressMessage("ReSharper", "InconsistentNaming")] - public class InventoryContainer - { - public List<InventoryItem> items; - } - - [SuppressMessage("ReSharper", "InconsistentNaming")] - public class Package - { - public string? playtime; - public bool is_battle_mentor; - public bool is_trade_mentor; - public bool is_novice; - public bool is_returner; - public short player_commendations; - - // Appearance - public int race; - public int gender; - public int model_type; - public int height; - public int tribe; - public int face_type; - public int hair_style; - public bool has_highlights; - public int skin_color; - public int eye_color; - public int hair_color; - public int hair_color2; - public int face_features; - public int face_features_color; - public int eyebrows; - public int eye_color2; - public int eye_shape; - public int nose_shape; - public int jaw_shape; - public int lip_style; - public int lip_color; - public int race_feature_size; - public int race_feature_type; - public int bust_size; - public int facepaint; - public int facepaint_color; - public string? portrait; - public string? plate_title; - public bool? plate_title_is_prefix; - public string? plate_class_job; - public int plate_class_job_level; - public string? search_comment; - public string? base_plate; - public string? pattern_overlay; - public string? backing; - public string? top_border; - public string? bottom_border; - public string? portrait_frame; - public string? plate_frame; - public string? accent; - - // inventory - public InventoryContainer inventory1; - public InventoryContainer inventory2; - public InventoryContainer inventory3; - public InventoryContainer inventory4; - - public InventoryContainer equipped_items; - - public InventoryContainer currency; - - public InventoryContainer armory_off_hand; - public InventoryContainer armory_head; - public InventoryContainer armory_body; - public InventoryContainer armory_hands; - public InventoryContainer armory_waist; - public InventoryContainer armory_legs; - public InventoryContainer armory_ear; - public InventoryContainer armory_neck; - public InventoryContainer armory_wrist; - public InventoryContainer armory_rings; - public InventoryContainer armory_soul_crystal; - public InventoryContainer armory_main_hand; - - public int voice; - public List<byte> unlock_flags; - public List<byte> unlock_aetherytes; - } - - public static Package? package; - - public Plugin() - { - CommandManager.AddHandler("/auracite", new CommandInfo(OnAuraciteCommand) - { - HelpMessage = "Start the server." - }); - - StepWindow = new StepWindow(); - WindowSystem.AddWindow(StepWindow); - - PluginInterface.UiBuilder.Draw += WindowSystem.Draw; - } - - [PluginService] internal static IClientState ClientState { get; private set; } = null!; - - [PluginService] internal static IObjectTable ObjectTable { get; private set; } = null!; - - [PluginService] internal static IDalamudPluginInterface PluginInterface { get; private set; } = null!; - - [PluginService] internal static IChatGui ChatGui { get; private set; } = null!; - - [PluginService] internal static ICommandManager CommandManager { get; private set; } = null!; - - [PluginService] internal static IDataManager DataManager { get; private set; } = null!; - - public void Dispose() - { - CurrentStep?.Dispose(); - WindowSystem.RemoveAllWindows(); - } - - private void OnAuraciteCommand(string command, string arguments) - { - if (arguments == "begin" && CurrentStep == null) - { - _stepIndex = -1; - package = new Package(); - NextStep(); - StepWindow.IsOpen = true; - } - } - - private void NextStep() - { - _stepIndex++; - if (_stepIndex >= _steps.Count) - { - CurrentStep?.Dispose(); - CurrentStep = null; - StepWindow.IsOpen = false; - return; - } - CurrentStep = (IStep)Activator.CreateInstance(_steps[_stepIndex])!; - CurrentStep.Completed += NextStep; - CurrentStep.Run(); - } -} diff --git a/dalamud/Auracite/StepWindow.cs b/dalamud/Auracite/StepWindow.cs deleted file mode 100644 index 41bd942..0000000 --- a/dalamud/Auracite/StepWindow.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using Dalamud.Interface.Windowing; -using Dalamud.Bindings.ImGui; - -namespace Auracite; - -public class StepWindow() - : Window("Step Window"), IDisposable -{ - public void Dispose() - { - } - - public override void Draw() - { - if (Plugin.CurrentStep != null) - { - ImGui.Text(Plugin.CurrentStep.StepName()); - ImGui.Text(Plugin.CurrentStep.StepDescription()); - - ImGui.TextDisabled("Step requires manual user action."); - - if (ImGui.Button("Retry")) - { - Plugin.CurrentStep.Run(); - } - } - else - { - ImGui.Text("Auracite is not running."); - } - } -} diff --git a/images/icon.png b/images/icon.png new file mode 100644 index 0000000..cd79c53 Binary files /dev/null and b/images/icon.png differ diff --git a/scripts/build-web.sh b/scripts/build-web.sh deleted file mode 100755 index a220df5..0000000 --- a/scripts/build-web.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -RUSTFLAGS='--cfg getrandom_backend="wasm_js"' wasm-pack build --target web --release --no-pack --no-typescript diff --git a/scripts/copy-web-to-server.sh b/scripts/copy-web-to-server.sh deleted file mode 100755 index abb3a68..0000000 --- a/scripts/copy-web-to-server.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -rsync -e "ssh -p 38901 -o StrictHostKeyChecking=no" --recursive web/ ryne.moe:/srv/http/auracite.xiv.zone/ && -rsync -e "ssh -p 38901 -o StrictHostKeyChecking=no" --recursive pkg/ ryne.moe:/srv/http/auracite.xiv.zone/pkg/ diff --git a/src/data.rs b/src/data.rs deleted file mode 100644 index 7d7d3d0..0000000 --- a/src/data.rs +++ /dev/null @@ -1,172 +0,0 @@ -use serde::Serialize; - -use crate::{ - package::InventoryContainer, - value::{ - CityStateValue, ClassJobValue, GenderValue, GrandCompanyValue, GuardianValue, ItemValue, - NamedayValue, RaceValue, TribeValue, WorldValue, - }, -}; - -#[derive(Default, Serialize)] -pub struct Currencies { - pub gil: u32, -} - -#[derive(Default, Serialize)] -pub struct Appearance { - pub race: String, - pub gender: String, - pub model_type: i32, - pub height: i32, - pub tribe: String, - pub face_type: i32, - pub hair_style: i32, - pub has_highlights: bool, - pub skin_color: i32, - pub eye_color: i32, - pub hair_color: i32, - pub hair_color2: i32, - pub face_features: i32, - pub face_features_color: i32, - pub eyebrows: i32, - pub eye_color2: i32, - pub eye_shape: i32, - pub nose_shape: i32, - pub jaw_shape: i32, - pub lip_style: i32, - pub lip_color: i32, - pub race_feature_size: i32, - pub race_feature_type: i32, - pub bust_size: i32, - pub facepaint: i32, - pub facepaint_color: i32, -} - -#[derive(Default, Serialize)] -pub struct EquippedItems { - #[serde(skip_serializing_if = "Option::is_none")] - pub main_hand: Option<ItemValue>, - #[serde(skip_serializing_if = "Option::is_none")] - pub off_hand: Option<ItemValue>, - #[serde(skip_serializing_if = "Option::is_none")] - pub head: Option<ItemValue>, - #[serde(skip_serializing_if = "Option::is_none")] - pub body: Option<ItemValue>, - #[serde(skip_serializing_if = "Option::is_none")] - pub hands: Option<ItemValue>, - #[serde(skip_serializing_if = "Option::is_none")] - pub legs: Option<ItemValue>, - #[serde(skip_serializing_if = "Option::is_none")] - pub feet: Option<ItemValue>, - #[serde(skip_serializing_if = "Option::is_none")] - pub earrings: Option<ItemValue>, - #[serde(skip_serializing_if = "Option::is_none")] - pub necklace: Option<ItemValue>, - #[serde(skip_serializing_if = "Option::is_none")] - pub bracelets: Option<ItemValue>, - #[serde(skip_serializing_if = "Option::is_none")] - pub left_ring: Option<ItemValue>, - #[serde(skip_serializing_if = "Option::is_none")] - pub right_ring: Option<ItemValue>, - #[serde(skip_serializing_if = "Option::is_none")] - pub soul_crystal: Option<ItemValue>, -} - -#[derive(Default, Serialize)] -pub struct CharacterData { - pub name: String, - pub world: WorldValue, - pub data_center: String, - pub city_state: CityStateValue, - pub nameday: NamedayValue, - pub guardian: GuardianValue, - pub race: RaceValue, - pub gender: GenderValue, - pub tribe: TribeValue, - pub classjob_levels: Vec<ClassJobValue>, - pub grand_company: GrandCompanyValue, - #[serde(skip_serializing_if = "Option::is_none")] - pub free_company: Option<String>, - #[serde(skip_serializing_if = "Option::is_none")] - pub title: Option<String>, - pub equipped: EquippedItems, - - #[serde(skip_serializing_if = "Option::is_none")] - pub currencies: Option<Currencies>, - #[serde(skip_serializing_if = "Option::is_none")] - pub playtime: Option<String>, - #[serde(skip_serializing_if = "Option::is_none")] - pub appearance: Option<Appearance>, - #[serde(skip_serializing_if = "Option::is_none")] - pub is_battle_mentor: Option<bool>, - #[serde(skip_serializing_if = "Option::is_none")] - pub is_trade_mentor: Option<bool>, - #[serde(skip_serializing_if = "Option::is_none")] - pub is_novice: Option<bool>, - #[serde(skip_serializing_if = "Option::is_none")] - pub is_returner: Option<bool>, - #[serde(skip_serializing_if = "Option::is_none")] - pub player_commendations: Option<i32>, - #[serde(skip_serializing_if = "Option::is_none")] - pub plate_title: Option<String>, - #[serde(skip_serializing_if = "Option::is_none")] - pub plate_classjob: Option<String>, - #[serde(skip_serializing_if = "Option::is_none")] - pub plate_classjob_level: Option<i32>, - #[serde(skip_serializing_if = "Option::is_none")] - pub search_comment: Option<String>, - #[serde(skip_serializing_if = "Option::is_none")] - pub voice: Option<i32>, - - #[serde(skip)] - pub face_url: String, - #[serde(skip)] - pub portrait_url: String, - - // inventory - #[serde(skip_serializing_if = "Option::is_none")] - pub inventory1: Option<InventoryContainer>, - #[serde(skip_serializing_if = "Option::is_none")] - pub inventory2: Option<InventoryContainer>, - #[serde(skip_serializing_if = "Option::is_none")] - pub inventory3: Option<InventoryContainer>, - #[serde(skip_serializing_if = "Option::is_none")] - pub inventory4: Option<InventoryContainer>, - - #[serde(skip_serializing_if = "Option::is_none")] - pub equipped_items: Option<InventoryContainer>, - - #[serde(skip_serializing_if = "Option::is_none")] - pub currency: Option<InventoryContainer>, - - #[serde(skip_serializing_if = "Option::is_none")] - pub armory_off_hand: Option<InventoryContainer>, - #[serde(skip_serializing_if = "Option::is_none")] - pub armory_head: Option<InventoryContainer>, - #[serde(skip_serializing_if = "Option::is_none")] - pub armory_body: Option<InventoryContainer>, - #[serde(skip_serializing_if = "Option::is_none")] - pub armory_hands: Option<InventoryContainer>, - #[serde(skip_serializing_if = "Option::is_none")] - pub armory_waist: Option<InventoryContainer>, - #[serde(skip_serializing_if = "Option::is_none")] - pub armory_legs: Option<InventoryContainer>, - #[serde(skip_serializing_if = "Option::is_none")] - pub armory_ear: Option<InventoryContainer>, - #[serde(skip_serializing_if = "Option::is_none")] - pub armory_neck: Option<InventoryContainer>, - #[serde(skip_serializing_if = "Option::is_none")] - pub armory_wrist: Option<InventoryContainer>, - #[serde(skip_serializing_if = "Option::is_none")] - pub armory_rings: Option<InventoryContainer>, - #[serde(skip_serializing_if = "Option::is_none")] - pub armory_soul_crystal: Option<InventoryContainer>, - #[serde(skip_serializing_if = "Option::is_none")] - pub armory_main_hand: Option<InventoryContainer>, - - #[serde(skip_serializing_if = "Option::is_none")] - pub unlock_flags: Option<Vec<u8>>, - #[serde(skip_serializing_if = "Option::is_none")] - pub unlock_aetherytes: Option<Vec<u8>>, -} diff --git a/src/downloader.rs b/src/downloader.rs deleted file mode 100644 index 8fd4cad..0000000 --- a/src/downloader.rs +++ /dev/null @@ -1,21 +0,0 @@ -use reqwest::Url; - -pub async fn download(url: &Url) -> Result<Vec<u8>, reqwest::Error> { - let client; - - #[cfg(target_family = "wasm")] - { - client = reqwest::Client::builder(); - } - - #[cfg(not(target_family = "wasm"))] - { - client = reqwest::Client::builder().no_proxy(); // This fixes localhost connections... for some reason (https://github.com/seanmonstar/reqwest/issues/913) - } - - let client = client.build()?; - - let body = client.get(url.to_string()).send().await; - - Ok(body?.bytes().await?.to_vec()) -} diff --git a/src/html.rs b/src/html.rs deleted file mode 100644 index ff8532c..0000000 --- a/src/html.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::data::CharacterData; -use minijinja::{Environment, context}; - -/// Writes a visual HTML for `char_data` to `file_path`. -/// This vaguely represents Lodestone and designed to visually check your character data. -pub fn create_character_html(char_data: &CharacterData) -> String { - let mut env = Environment::new(); - env.add_template( - "character.html", - include_str!("../templates/character.html"), - ) - .unwrap(); - let template = env.get_template("character.html").unwrap(); - template - .render(context! { - name => char_data.name, - world => char_data.world.name, - data_center => char_data.data_center, - race => char_data.race.name, - subrace => char_data.tribe.name, - gender => char_data.gender.name, - nameday => char_data.nameday.value, - city_state => char_data.city_state.name - }) - .unwrap() -} - -/// Writes a visual HTML for `char_data` to `file_path`. -/// This vaguely represents Lodestone and designed to visually check your character data. -pub fn create_plate_html(char_data: &CharacterData) -> String { - let mut env = Environment::new(); - env.add_template("plate.html", include_str!("../templates/plate.html")) - .unwrap(); - let template = env.get_template("plate.html").unwrap(); - template - .render(context! { - name => char_data.name, - world => char_data.world.name, - data_center => char_data.data_center, - title => char_data.plate_title, - level => char_data.plate_classjob_level, - class => char_data.plate_classjob, - search_comment => char_data.search_comment, - }) - .unwrap() -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 2f722bd..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,433 +0,0 @@ -pub mod data; -pub mod downloader; -pub mod html; -pub mod package; -pub mod parser; -pub mod value; - -use crate::data::CharacterData; -use crate::downloader::download; -use crate::html::{create_character_html, create_plate_html}; -use crate::parser::parse_search; -use base64::prelude::*; -use data::Appearance; -use package::Package; -use physis::savedata::chardat; -use regex::Regex; -use reqwest::Url; -use std::io::Write; -#[cfg(target_family = "wasm")] -use wasm_bindgen::JsValue; -#[cfg(target_family = "wasm")] -use wasm_bindgen::prelude::wasm_bindgen; -use web_time::{SystemTime, UNIX_EPOCH}; -use zip::ZipWriter; -use zip::result::ZipError; -use zip::write::SimpleFileOptions; - -/// The main Lodestone domain -const LODESTONE_HOST: &str = "https://na.finalfantasyxiv.com"; - -/// The Lodestone proxy used in WebAssembly builds. Needed for CORS and cookie injection. -const LODESTONE_TUNNEL_HOST: &str = "https://lodestone-tunnel.ryne.moe"; - -/// The image domain. -const IMAGE_HOST: &str = "img2.finalfantasyxiv.com"; - -/// The image proxy used in WebAssembly builds. Needed for CORS. -const IMAGE_TUNNEL_HOST: &str = "img-tunnel.ryne.moe"; - -#[derive(Debug)] -pub enum ArchiveError { - DownloadFailed(String), - CharacterNotFound, - ParsingError, - CouldNotConnectToDalamud, - UnknownError, -} - -impl From<ZipError> for ArchiveError { - fn from(_: ZipError) -> Self { - ArchiveError::UnknownError - } -} - -impl From<std::io::Error> for ArchiveError { - fn from(_: std::io::Error) -> Self { - ArchiveError::UnknownError - } -} - -impl From<physis::Error> for ArchiveError { - fn from(_: physis::Error) -> Self { - ArchiveError::UnknownError - } -} - -#[cfg(target_family = "wasm")] -impl From<ArchiveError> for JsValue { - fn from(err: ArchiveError) -> Self { - match err { - // TODO: give JS the URL that failed to download - ArchiveError::DownloadFailed(_) => JsValue::from_str(&"download_failed".to_string()), - ArchiveError::CharacterNotFound => { - JsValue::from_str(&"character_not_found".to_string()) - } - ArchiveError::ParsingError => JsValue::from_str(&"parsing_error".to_string()), - ArchiveError::UnknownError => JsValue::from_str(&"unknown_error".to_string()), - ArchiveError::CouldNotConnectToDalamud => { - JsValue::from_str(&"could_not_connect_to_dalamud".to_string()) - } - } - } -} - -/// Searches for the character by their name. -pub async fn search_character(character_name: &str) -> Option<u64> { - let lodestone_host = if cfg!(target_family = "wasm") { - LODESTONE_TUNNEL_HOST - } else { - LODESTONE_HOST - }; - - let search_url = Url::parse_with_params( - &format!("{lodestone_host}/lodestone/character?"), - &[("q", character_name)], - ) - .map_err(|_| ArchiveError::UnknownError) - .ok()?; - let search_page = download(&search_url) - .await - .map_err(|_| ArchiveError::DownloadFailed(search_url.to_string())) - .ok()?; - let search_page = String::from_utf8(search_page) - .map_err(|_| ArchiveError::ParsingError) - .ok()?; - - let href = parse_search(&search_page); - if href.is_empty() { - return None; - } - - let re = Regex::new(r"\/lodestone\/character\/(\d+)").ok()?; - let captures = re.captures(&href)?; - - captures.get(1)?.as_str().parse().ok() -} - -/// Archives the character named `character_name` and gives a ZIP file as bytes that can be written to disk. -pub async fn archive_character(id: u64, use_dalamud: bool) -> Result<Vec<u8>, ArchiveError> { - let lodestone_host = if cfg!(target_family = "wasm") { - LODESTONE_TUNNEL_HOST - } else { - LODESTONE_HOST - }; - - let char_page_url = Url::parse(&format!("{lodestone_host}/lodestone/character/{id}/")) - .map_err(|_| ArchiveError::UnknownError)?; - let char_page = download(&char_page_url) - .await - .map_err(|_| ArchiveError::DownloadFailed(char_page_url.to_string()))?; - let char_page = String::from_utf8(char_page).map_err(|_| ArchiveError::ParsingError)?; - - let mut char_data = CharacterData::default(); - parser::parse_profile(&char_page, &mut char_data); - - let classjob_page_url = Url::parse(&format!( - "{lodestone_host}/lodestone/character/{id}/class_job/" - )) - .map_err(|_| ArchiveError::UnknownError)?; - let classjob_page = download(&classjob_page_url) - .await - .map_err(|_| ArchiveError::DownloadFailed(classjob_page_url.to_string()))?; - let char_page = String::from_utf8(classjob_page).map_err(|_| ArchiveError::ParsingError)?; - - parser::parse_classjob(&char_page, &mut char_data); - - let mut buf = Vec::new(); - let mut zip = ZipWriter::new(std::io::Cursor::new(&mut buf)); - - let options = SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored); - - if !char_data.portrait_url.is_empty() { - let portrait_url = if cfg!(target_family = "wasm") { - &char_data - .portrait_url - .replace(IMAGE_HOST, IMAGE_TUNNEL_HOST) - } else { - &char_data.portrait_url - }; - let portrait_url = Url::parse(portrait_url).map_err(|_| ArchiveError::UnknownError)?; - - let portrait = download(&portrait_url) - .await - .map_err(|_| ArchiveError::DownloadFailed(portrait_url.to_string()))?; - - zip.start_file("portrait.jpg", options)?; - zip.write_all(&portrait)?; - } - if !char_data.face_url.is_empty() { - let face_url = if cfg!(target_family = "wasm") { - &char_data.face_url.replace(IMAGE_HOST, IMAGE_TUNNEL_HOST) - } else { - &char_data.face_url - }; - let face_url = Url::parse(face_url).map_err(|_| ArchiveError::UnknownError)?; - - let face = download(&face_url) - .await - .map_err(|_| ArchiveError::DownloadFailed(face_url.to_string()))?; - - zip.start_file("face.jpg", options)?; - zip.write_all(&face)?; - } - - if use_dalamud { - let dalamud_url = - Url::parse("http://localhost:42072/package").map_err(|_| ArchiveError::UnknownError)?; - let package = download(&dalamud_url) - .await - .map_err(|_| ArchiveError::CouldNotConnectToDalamud)?; - let package = String::from_utf8(package).map_err(|_| ArchiveError::ParsingError)?; - // Remove BOM at the start - let package = package.trim_start_matches("\u{feff}"); - let package: Package = - serde_json::from_str(package.trim_start()).map_err(|_| ArchiveError::ParsingError)?; - - // appearance data - char_data.appearance = Some(Appearance { - race: char_data.race.name.clone(), - tribe: char_data.tribe.name.clone(), - gender: char_data.gender.name.clone(), - model_type: package.model_type, - height: package.height, - face_type: package.face_type, - hair_style: package.hair_style, - has_highlights: package.has_highlights, - skin_color: package.skin_color, - eye_color: package.eye_color, - hair_color: package.hair_color, - hair_color2: package.hair_color2, - face_features: package.face_features, - face_features_color: package.face_features_color, - eyebrows: package.eyebrows, - eye_color2: package.eye_color2, - eye_shape: package.eye_color2, - nose_shape: package.nose_shape, - jaw_shape: package.jaw_shape, - lip_style: package.lip_style, - lip_color: package.lip_color, - race_feature_size: package.race_feature_size, - race_feature_type: package.race_feature_type, - bust_size: package.bust_size, - facepaint: package.facepaint, - facepaint_color: package.facepaint_color, - }); - - char_data.inventory1 = Some(package.inventory1); - char_data.inventory2 = Some(package.inventory2); - char_data.inventory3 = Some(package.inventory3); - char_data.inventory4 = Some(package.inventory4); - - char_data.equipped_items = Some(package.equipped_items); - - char_data.currency = Some(package.currency); - - char_data.armory_off_hand = Some(package.armory_off_hand); - char_data.armory_head = Some(package.armory_head); - char_data.armory_body = Some(package.armory_body); - char_data.armory_hands = Some(package.armory_hands); - char_data.armory_waist = Some(package.armory_waist); - char_data.armory_legs = Some(package.armory_legs); - char_data.armory_ear = Some(package.armory_ear); - char_data.armory_neck = Some(package.armory_neck); - char_data.armory_wrist = Some(package.armory_wrist); - char_data.armory_rings = Some(package.armory_rings); - char_data.armory_soul_crystal = Some(package.armory_soul_crystal); - char_data.armory_main_hand = Some(package.armory_main_hand); - - char_data.playtime = Some(package.playtime.parse().unwrap()); - char_data.is_battle_mentor = Some(package.is_battle_mentor); - char_data.is_trade_mentor = Some(package.is_trade_mentor); - char_data.is_novice = Some(package.is_novice); - char_data.is_returner = Some(package.is_returner); - char_data.player_commendations = Some(package.player_commendations); // TODO: fetch from the lodestone? - char_data.plate_title = package.plate_title; - char_data.plate_classjob = package.plate_class_job; - char_data.plate_classjob_level = Some(package.plate_class_job_level); - char_data.search_comment = package.search_comment; - char_data.voice = Some(package.voice); - char_data.unlock_flags = Some(package.unlock_flags); - char_data.unlock_aetherytes = Some(package.unlock_aetherytes); - - if let Some(portrait) = package.portrait { - zip.start_file("plate-portrait.png", options)?; - zip.write_all( - &BASE64_STANDARD - .decode(portrait.trim_start_matches("data:image/png;base64,")) - .unwrap(), - )?; - } - - if let Some(base_plate) = package.base_plate { - zip.start_file("base-plate.png", options)?; - zip.write_all( - &BASE64_STANDARD - .decode(base_plate.trim_start_matches("data:image/png;base64,")) - .unwrap(), - )?; - } - - if let Some(pattern_overlay) = package.pattern_overlay { - zip.start_file("pattern-overlay.png", options)?; - zip.write_all( - &BASE64_STANDARD - .decode(pattern_overlay.trim_start_matches("data:image/png;base64,")) - .unwrap(), - )?; - } - - if let Some(backing) = package.backing { - zip.start_file("backing.png", options)?; - zip.write_all( - &BASE64_STANDARD - .decode(backing.trim_start_matches("data:image/png;base64,")) - .unwrap(), - )?; - } - - if let Some(top_border) = package.top_border { - zip.start_file("top-border.png", options)?; - zip.write_all( - &BASE64_STANDARD - .decode(top_border.trim_start_matches("data:image/png;base64,")) - .unwrap(), - )?; - } - - if let Some(bottom_border) = package.bottom_border { - zip.start_file("bottom-border.png", options)?; - zip.write_all( - &BASE64_STANDARD - .decode(bottom_border.trim_start_matches("data:image/png;base64,")) - .unwrap(), - )?; - } - - if let Some(portrait_frame) = package.portrait_frame { - zip.start_file("portrait-frame.png", options)?; - zip.write_all( - &BASE64_STANDARD - .decode(portrait_frame.trim_start_matches("data:image/png;base64,")) - .unwrap(), - )?; - } - - if let Some(plate_frame) = package.plate_frame { - zip.start_file("plate-frame.png", options)?; - zip.write_all( - &BASE64_STANDARD - .decode(plate_frame.trim_start_matches("data:image/png;base64,")) - .unwrap(), - )?; - } - - if let Some(accent) = package.accent { - zip.start_file("accent.png", options)?; - zip.write_all( - &BASE64_STANDARD - .decode(accent.trim_start_matches("data:image/png;base64,")) - .unwrap(), - )?; - } - - let timestamp: u32 = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("Failed to get UNIX timestamp!") - .as_secs() - .try_into() - .unwrap(); - - let char_dat = chardat::CharacterData { - version: 7, - customize: chardat::CustomizeData { - race: (package.race as u8).try_into()?, - gender: (package.gender as u8).try_into()?, - age: package.model_type as u8, - height: package.height as u8, - tribe: (package.tribe as u8).try_into()?, - face: package.face_type as u8, - hair: package.hair_style as u8, - enable_highlights: package.has_highlights, - skin_tone: package.skin_color as u8, - right_eye_color: package.eye_color as u8, - hair_tone: package.hair_color as u8, - highlights: package.hair_color2 as u8, - facial_features: package.face_features as u8, - facial_feature_color: package.face_features_color as u8, - eyebrows: package.eyebrows as u8, - left_eye_color: package.eye_color2 as u8, - eyes: package.eye_shape as u8, - nose: package.nose_shape as u8, - jaw: package.jaw_shape as u8, - mouth: package.lip_style as u8, - lips_tone_fur_pattern: package.lip_color as u8, - race_feature_size: package.race_feature_size as u8, - race_feature_type: package.race_feature_type as u8, - bust: package.bust_size as u8, - face_paint: package.facepaint as u8, - face_paint_color: package.facepaint_color as u8, - voice: package.voice as u8, - }, - timestamp, - comment: "Generated by Auracite".to_string(), - }; - - zip.start_file("FFXIV_CHARA_01.dat", options)?; - zip.write_all(&char_dat.write_to_buffer().unwrap())?; - - zip.start_file("plate.html", options)?; - zip.write_all(create_plate_html(&char_data).as_ref())?; - - // Stop the HTTP server - let stop_url = - Url::parse("http://localhost:42072/stop").map_err(|_| ArchiveError::UnknownError)?; - // I'm intentionally ignoring the message because it doesn't matter if it fails - and it usually does - let _ = download(&stop_url).await; - } - - zip.start_file("character.json", options)?; - zip.write_all(serde_json::to_string_pretty(&char_data).unwrap().as_ref())?; - - zip.start_file("character.html", options)?; - zip.write_all(create_character_html(&char_data).as_ref())?; - - zip.finish()?; - - Ok(buf) -} - -/// Archives the character `id` and converts the ZIP file to Base64. Useful for downloading via data URIs. -#[cfg(target_family = "wasm")] -#[wasm_bindgen] -pub async extern "C" fn archive_character_base64( - id: u64, - use_dalamud: bool, -) -> Result<String, ArchiveError> { - #[cfg(feature = "debug")] - console_error_panic_hook::set_once(); - - let buf: String = archive_character(id, use_dalamud) - .await - .map(|x| BASE64_STANDARD.encode(x))?; - return Ok(format!("data:application/octet-stream;charset=utf-16le;base64,{buf}").into()); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen] -pub async extern "C" fn search_for_character(name: &str) -> Option<u64> { - #[cfg(feature = "debug")] - console_error_panic_hook::set_once(); - - search_character(name).await -} diff --git a/src/package.rs b/src/package.rs deleted file mode 100644 index ac75b97..0000000 --- a/src/package.rs +++ /dev/null @@ -1,94 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Default, Deserialize, Serialize, Clone)] -pub struct InventoryItem { - pub slot: i32, - pub quantity: u32, - pub condition: i32, - pub id: u32, - pub glamour_id: u32, -} - -#[derive(Default, Deserialize, Serialize, Clone)] -pub struct InventoryContainer { - pub items: Vec<InventoryItem>, -} - -#[derive(Default, Deserialize, Clone)] -pub struct Package { - pub playtime: String, - pub is_battle_mentor: bool, - pub is_trade_mentor: bool, - pub is_novice: bool, - pub is_returner: bool, - pub player_commendations: i32, - pub portrait: Option<String>, - pub plate_title: Option<String>, - pub plate_title_is_prefix: Option<bool>, - pub plate_class_job: Option<String>, - pub plate_class_job_level: i32, - pub search_comment: Option<String>, - pub base_plate: Option<String>, - pub pattern_overlay: Option<String>, - pub backing: Option<String>, - pub top_border: Option<String>, - pub bottom_border: Option<String>, - pub portrait_frame: Option<String>, - pub plate_frame: Option<String>, - pub accent: Option<String>, - pub voice: i32, - - // Appearance - pub race: i32, - pub gender: i32, - pub model_type: i32, - pub height: i32, - pub tribe: i32, - pub face_type: i32, - pub hair_style: i32, - pub has_highlights: bool, - pub skin_color: i32, - pub eye_color: i32, - pub hair_color: i32, - pub hair_color2: i32, - pub face_features: i32, - pub face_features_color: i32, - pub eyebrows: i32, - pub eye_color2: i32, - pub eye_shape: i32, - pub nose_shape: i32, - pub jaw_shape: i32, - pub lip_style: i32, - pub lip_color: i32, - pub race_feature_size: i32, - pub race_feature_type: i32, - pub bust_size: i32, - pub facepaint: i32, - pub facepaint_color: i32, - - // inventory - pub inventory1: InventoryContainer, - pub inventory2: InventoryContainer, - pub inventory3: InventoryContainer, - pub inventory4: InventoryContainer, - - pub equipped_items: InventoryContainer, - - pub currency: InventoryContainer, - - pub armory_off_hand: InventoryContainer, - pub armory_head: InventoryContainer, - pub armory_body: InventoryContainer, - pub armory_hands: InventoryContainer, - pub armory_waist: InventoryContainer, - pub armory_legs: InventoryContainer, - pub armory_ear: InventoryContainer, - pub armory_neck: InventoryContainer, - pub armory_wrist: InventoryContainer, - pub armory_rings: InventoryContainer, - pub armory_soul_crystal: InventoryContainer, - pub armory_main_hand: InventoryContainer, - - pub unlock_flags: Vec<u8>, - pub unlock_aetherytes: Vec<u8>, -} diff --git a/src/parser.rs b/src/parser.rs deleted file mode 100644 index 98fa8f2..0000000 --- a/src/parser.rs +++ /dev/null @@ -1,244 +0,0 @@ -use crate::{ - data::CharacterData, - value::{ - CityStateValue, ClassJobValue, GenderValue, GuardianValue, ItemValue, NamedayValue, - RaceValue, TribeValue, WorldValue, - }, -}; -use regex::Regex; -use scraper::{Html, Selector}; - -const ENTRY_SELECTOR: &str = ".entry"; -const ENTRY_NAME_SELECTOR: &str = ".entry__name"; - -/// Parses the HTML from `data` and returns the relative Lodestone URL for the first search entry. -pub fn parse_search(data: &str) -> String { - let document = Html::parse_document(data); - let mut href = String::new(); - - for element in document.select(&Selector::parse(ENTRY_SELECTOR).unwrap()) { - if let Some(_) = element - .select(&Selector::parse(ENTRY_NAME_SELECTOR).unwrap()) - .next() - && let Some(block_name) = element - .select(&Selector::parse("a.entry__link").unwrap()) - .next() - { - href = block_name.attr("href").unwrap().parse().unwrap(); - } - } - - href -} - -const CHARACTER_NAME_SELECTOR: &str = ".frame__chara__name"; -const WORLD_DATA_CENTER_SELECTOR: &str = ".frame__chara__world"; -const CHARACTER_BLOCK_SELECTOR: &str = ".character-block__box"; -const CHARACTER_BLOCK_TITLE_SELECTOR: &str = ".character-block__title"; -const CHARACTER_BLOCK_NAME_SELECTOR: &str = ".character-block__name"; -const FACE_IMG_SELECTOR: &str = ".frame__chara__face > img"; -const PORTRAIT_IMG_SELECTOR: &str = ".character__detail__image > a > img"; -const NAMEDAY_SELECTOR: &str = ".character-block__birth"; -const FREE_COMPANY_SELECTOR: &str = ".character__freecompany__name > h4 > a"; -const TITLE_SELECTOR: &str = ".frame__chara__title"; - -/// Parses the HTML from `data` and returns `CharacterData`. The data may be incomplete. -pub fn parse_profile(data: &str, char_data: &mut CharacterData) { - let document = Html::parse_document(data); - - for element in document.select(&Selector::parse(CHARACTER_NAME_SELECTOR).unwrap()) { - char_data.name = element.inner_html(); - } - - if let Some(title) = document - .select(&Selector::parse(TITLE_SELECTOR).unwrap()) - .next() - { - char_data.title = Some(title.inner_html().as_str().to_string()); - } - - let world_re = Regex::new(r"(\w+)\s\[(\w+)\]").unwrap(); - for element in document.select(&Selector::parse(WORLD_DATA_CENTER_SELECTOR).unwrap()) { - let inner_html = element.inner_html(); - let captures = world_re.captures(&inner_html).unwrap(); - // TODO: use error - char_data.world = WorldValue::try_from(captures.get(1).unwrap().as_str()).unwrap(); - char_data.data_center = captures.get(2).unwrap().as_str().to_owned(); - } - - let block_re = Regex::new(r"([^<]+)<br>([^\/]+)\s\/\s(\W)").unwrap(); - let grand_re = Regex::new(r"([^\/]+)\s\/\s([^\/]+)").unwrap(); - for element in document.select(&Selector::parse(CHARACTER_BLOCK_SELECTOR).unwrap()) { - if let Some(block_title) = element - .select(&Selector::parse(CHARACTER_BLOCK_TITLE_SELECTOR).unwrap()) - .next() - { - let name = block_title.inner_html(); - if name == "Race/Clan/Gender" { - if let Some(block_name) = element - .select(&Selector::parse(CHARACTER_BLOCK_NAME_SELECTOR).unwrap()) - .next() - { - let inner_html = block_name.inner_html(); - let captures = block_re.captures(&inner_html).unwrap(); - - char_data.race = - RaceValue::try_from(captures.get(1).unwrap().as_str()).unwrap(); - char_data.tribe = - TribeValue::try_from(captures.get(2).unwrap().as_str()).unwrap(); - char_data.gender = - GenderValue::try_from(captures.get(3).unwrap().as_str()).unwrap(); - } - } else if name == "City-state" { - if let Some(block_name) = element - .select(&Selector::parse(CHARACTER_BLOCK_NAME_SELECTOR).unwrap()) - .next() - { - char_data.city_state = - CityStateValue::try_from(block_name.inner_html().as_str()).unwrap(); - } - } else if name == "Nameday" { - for element in element.select(&Selector::parse(NAMEDAY_SELECTOR).unwrap()) { - char_data.nameday = - NamedayValue::try_from(element.inner_html().as_str()).unwrap(); - } - - if let Some(block_name) = element - .select(&Selector::parse(CHARACTER_BLOCK_NAME_SELECTOR).unwrap()) - .next() - { - char_data.guardian = - GuardianValue::try_from(block_name.inner_html().as_str()).unwrap(); - } - } else if name == "Grand Company" - && let Some(block_name) = element - .select(&Selector::parse(CHARACTER_BLOCK_NAME_SELECTOR).unwrap()) - .next() - { - let inner_html = block_name.inner_html(); - let captures = grand_re.captures(&inner_html).unwrap(); - - char_data.grand_company.name = captures.get(1).unwrap().as_str().to_string(); - char_data.grand_company.rank = captures.get(2).unwrap().as_str().to_string(); - } - } - - if let Some(free_company) = element - .select(&Selector::parse(FREE_COMPANY_SELECTOR).unwrap()) - .next() - { - char_data.free_company = Some(free_company.inner_html().as_str().to_string()); - } - } - - for element in document.select(&Selector::parse(FACE_IMG_SELECTOR).unwrap()) { - char_data.face_url = element.attr("src").unwrap().parse().unwrap(); - } - - if let Some(element) = document - .select(&Selector::parse(PORTRAIT_IMG_SELECTOR).unwrap()) - .next() - { - char_data.portrait_url = element.attr("src").unwrap().parse().unwrap(); - } - - // TODO: support facewear - let item_slot_selectors = [ - ".icon-c--0", // Main Hand - ".icon-c--1", // Off hand - ".icon-c--2", // Head - ".icon-c--3", // Body - ".icon-c--4", // Hands - ".icon-c--6", // Legs - ".icon-c--7", // Feet - ".icon-c--8", // Earrings - ".icon-c--9", // Necklace - ".icon-c--10", // Bracelets - ".icon-c--11", // Left Ring - ".icon-c--12", // Right Ring - ".icon-c--13", // Soul Crystal - ]; - - for (i, selector) in item_slot_selectors.iter().enumerate() { - if let Some(slot) = document.select(&Selector::parse(selector).unwrap()).next() - && let Some(item) = slot.select(&Selector::parse(".db-tooltip").unwrap()).next() - { - let parsed_item = parse_item_tooltip(&item); - let slot = match i { - 0 => &mut char_data.equipped.main_hand, - 1 => &mut char_data.equipped.off_hand, - 2 => &mut char_data.equipped.head, - 3 => &mut char_data.equipped.body, - 4 => &mut char_data.equipped.hands, - 5 => &mut char_data.equipped.legs, - 6 => &mut char_data.equipped.feet, - 7 => &mut char_data.equipped.earrings, - 8 => &mut char_data.equipped.necklace, - 9 => &mut char_data.equipped.bracelets, - 10 => &mut char_data.equipped.left_ring, - 11 => &mut char_data.equipped.right_ring, - 12 => &mut char_data.equipped.soul_crystal, - _ => panic!("Unexpected slot!"), - }; - - *slot = parsed_item; - } - } -} - -const CLASSJOB_SELECTOR: &str = ".character__job > li"; -const CLASSJOB_LEVEL_SELECTOR: &str = ".character__job__level"; -const CLASSJOB_NAME_SELECTOR: &str = ".character__job__name"; -const CLASSJOB_EXP_SELECTOR: &str = ".character__job__exp"; - -/// Parses the HTML from `data` and returns `CharacterData`. The data may be incomplete. -pub fn parse_classjob(data: &str, char_data: &mut CharacterData) { - let document = Html::parse_document(data); - - for element in document.select(&Selector::parse(CLASSJOB_SELECTOR).unwrap()) { - let level = element - .select(&Selector::parse(CLASSJOB_LEVEL_SELECTOR).unwrap()) - .next() - .unwrap(); - let name = element - .select(&Selector::parse(CLASSJOB_NAME_SELECTOR).unwrap()) - .next() - .unwrap(); - let exp_element = element - .select(&Selector::parse(CLASSJOB_EXP_SELECTOR).unwrap()) - .next() - .unwrap(); - - let mut exp = None; - let mut max_exp = None; - if let Some((exp_text, max_exp_text)) = exp_element.inner_html().split_once(" / ") { - exp = exp_text.replace(",", "").parse().ok(); - max_exp = max_exp_text.replace(",", "").parse().ok(); - } - - // skip levels that are -, which means they don't even have the classjob - if let Ok(level) = level.inner_html().parse() { - let mut class_job_value = - ClassJobValue::try_from(name.inner_html().as_str()).unwrap_or_default(); - class_job_value.level = level; - class_job_value.exp = exp; - class_job_value.max_exp = max_exp; - char_data.classjob_levels.push(class_job_value); - } - } -} - -fn parse_item_tooltip(element: &scraper::ElementRef<'_>) -> Option<ItemValue> { - if let Some(slot) = element - .select(&Selector::parse(".db-tooltip__item__name").unwrap()) - .next() - { - let mut text: String = slot.text().collect(); - if text.contains("\u{e03c}") { - text = text.strip_suffix("\u{e03c}").unwrap().to_string(); - } - return Some(ItemValue { name: text }); - } - - None -} diff --git a/src/value.rs b/src/value.rs deleted file mode 100644 index 583d19c..0000000 --- a/src/value.rs +++ /dev/null @@ -1,382 +0,0 @@ -use regex::Regex; -use serde::Serialize; - -use crate::ArchiveError; - -// TODO: does it make sense to implement Default? -#[derive(Default, Serialize)] -pub struct WorldValue { - /// Name of the world. - pub name: String, - /// Internal ID of the world. - pub value: i32, -} - -impl TryFrom<&str> for WorldValue { - type Error = ArchiveError; - - fn try_from(name: &str) -> Result<Self, ArchiveError> { - let value = match name { - "Adamantoise" => 73, - "Cactuar" => 79, - "Faerie" => 54, - "Gilgamesh" => 63, - "Jenova" => 40, - "Midgardsormr" => 65, - "Sargatanas" => 99, - "Siren" => 57, - "Balmung" => 91, - "Brynhildr" => 34, - "Coeurl" => 74, - "Diabolos" => 62, - "Goblin" => 81, - "Malboro" => 75, - "Mateus" => 37, - "Zalera" => 41, - "Behemoth" => 78, - "Excalibur" => 93, - "Exodus" => 53, - "Famfrit" => 35, - "Hyperion" => 95, - "Lamia" => 55, - "Leviathan" => 64, - "Ultros" => 77, - "Halicarnassus" => 406, - "Maduin" => 407, - "Marilith" => 404, - "Seraph" => 405, - "Cuchulainn" => 408, - "Golem" => 411, - "Kraken" => 409, - "Rafflesia" => 410, - "Cerberus" => 80, - "Louisoix" => 83, - "Moogle" => 71, - "Omega" => 39, - "Phantom" => 401, - "Ragnarok" => 97, - "Sagittarius" => 400, - "Spriggan" => 85, - "Alpha" => 402, - "Lich" => 36, - "Odin" => 66, - "Phoenix" => 56, - "Raiden" => 403, - "Shiva" => 67, - "Twintania" => 33, - "Zodiark" => 42, - "Aegis" => 90, - "Atomos" => 68, - "Carbuncle" => 45, - "Garuda" => 58, - "Gungnir" => 94, - "Kujata" => 49, - "Tonberry" => 72, - "Typhon" => 50, - "Alexander" => 43, - "Bahamut" => 69, - "Durandal" => 92, - "Fenrir" => 46, - "Ifrit" => 59, - "Ridill" => 98, - "Tiamat" => 76, - "Ultima" => 51, - "Anima" => 44, - "Asura" => 23, - "Chocobo" => 70, - "Hades" => 47, - "Ixion" => 48, - "Masamune" => 96, - "Pandaemonium" => 28, - "Titan" => 61, - "Belias" => 24, - "Mandragora" => 82, - "Ramuh" => 60, - "Shinryu" => 29, - "Unicorn" => 30, - "Valefor" => 52, - "Yojimbo" => 31, - "Zeromus" => 32, - "Bismarck" => 22, - "Ravana" => 21, - "Sephirot" => 86, - "Sophia" => 87, - "Zurvan" => 88, - _ => return Err(ArchiveError::ParsingError), - }; - - Ok(Self { - name: name.to_string(), - value, - }) - } -} - -#[derive(Default, Serialize)] -pub struct CityStateValue { - /// Name of the city-state. - pub name: String, - /// Internal ID of the city-state. - pub value: i32, -} - -impl TryFrom<&str> for CityStateValue { - type Error = ArchiveError; - - fn try_from(name: &str) -> Result<Self, ArchiveError> { - let value = match name { - "Limsa Lominsa" => 1, - "Gridania" => 2, - "Ul'dah" => 3, - _ => return Err(ArchiveError::ParsingError), - }; - - Ok(Self { - name: name.to_string(), - value, - }) - } -} - -#[derive(Default, Serialize)] -pub struct GenderValue { - /// Name of the gender. - pub name: String, - /// Internal ID of the gender. - pub value: i32, -} - -impl TryFrom<&str> for GenderValue { - type Error = ArchiveError; - - fn try_from(name: &str) -> Result<Self, ArchiveError> { - let (value, name) = match name { - "♂" => (0, "Male"), - "♀" => (1, "Female"), - _ => return Err(ArchiveError::ParsingError), - }; - - Ok(Self { - name: name.to_string(), - value, - }) - } -} - -#[derive(Default, Serialize)] -pub struct RaceValue { - /// Name of the race. - pub name: String, - /// Internal ID of the race. - pub value: i32, -} - -impl TryFrom<&str> for RaceValue { - type Error = ArchiveError; - - fn try_from(name: &str) -> Result<Self, ArchiveError> { - let value = match name { - "Hyur" => 1, - "Elezen" => 2, - "Lalafell" => 3, - "Miqo'te" => 4, - "Roegadyn" => 5, - "Au Ra" => 6, - "Hrothgar" => 7, - "Viera" => 8, - _ => return Err(ArchiveError::ParsingError), - }; - - Ok(Self { - name: name.to_string(), - value, - }) - } -} - -#[derive(Default, Serialize)] -pub struct TribeValue { - /// Name of the tribe. - pub name: String, - /// Internal ID of the tribe. - pub value: i32, -} - -impl TryFrom<&str> for TribeValue { - type Error = ArchiveError; - - fn try_from(name: &str) -> Result<Self, ArchiveError> { - let value = match name { - "Midlander" => 1, - "Highlander" => 2, - "Wildwood" => 3, - "Duskwight" => 4, - "Plainsfolk" => 5, - "Dunesfolk" => 6, - "Seeker of the Sun" => 7, - "Keeper of the Moon" => 8, - "Sea Wolf" => 9, - "Hellsguard" => 10, - "Raen" => 11, - "Xaela" => 12, - "Hellions" => 13, - "The Lost" => 14, - "Rava" => 15, - "Veena" => 16, - _ => return Err(ArchiveError::ParsingError), - }; - - Ok(Self { - name: name.to_string(), - value, - }) - } -} - -#[derive(Default, Serialize)] -pub struct GuardianValue { - /// Name of the guardian. - pub name: String, - /// Internal ID of the guardian. - pub value: i32, -} - -impl TryFrom<&str> for GuardianValue { - type Error = ArchiveError; - - fn try_from(name: &str) -> Result<Self, ArchiveError> { - let value = match name { - "Halone, the Fury" => 1, - "Menphina, the Lover" => 2, - "Thaliak, the Scholar" => 3, - "Nymeia, the Spinner" => 4, - "Llymlaen, the Navigator" => 5, - "Oschon, the Wanderer" => 6, - "Byregot, the Builder" => 7, - "Rhalgr, the Destroyer" => 8, - "Azeyma, the Warden" => 9, - "Nald'thal, the Traders" => 10, - "Nophica, the Matron" => 11, - "Althyk, the Keeper" => 12, - _ => return Err(ArchiveError::ParsingError), - }; - - Ok(Self { - name: name.to_string(), - value, - }) - } -} - -#[derive(Default, Serialize)] -pub struct NamedayValue { - /// String represenation of your nameday. - pub value: String, - /// Day part of your nameday. - pub day: i32, - /// Month part of your nameday. - pub month: i32, -} - -impl TryFrom<&str> for NamedayValue { - type Error = ArchiveError; - - fn try_from(value: &str) -> Result<Self, ArchiveError> { - let re = Regex::new(r"(\d{1,2})[^\d]+(\d{1,2})").unwrap(); - let captures = re.captures(value).unwrap(); - - Ok(Self { - value: value.to_string(), - day: captures.get(1).unwrap().as_str().parse::<i32>().unwrap(), - month: captures.get(2).unwrap().as_str().parse::<i32>().unwrap(), - }) - } -} - -#[derive(Default, Serialize)] -pub struct ClassJobValue { - /// Name of the class or job. - pub name: String, - /// Level of the class or job. - pub level: i32, - /// The EXP of the job, can be None to indicate either: the job isn't unlocked or that they have max EXP. - #[serde(skip_serializing_if = "Option::is_none")] - pub exp: Option<i32>, - /// The maximum EXP of the job, can be None to indicate either: the job isn't unlocked or that they have max EXP. - #[serde(skip_serializing_if = "Option::is_none")] - pub max_exp: Option<i32>, - /// Internal ID of the class or job. - pub value: i32, -} - -impl TryFrom<&str> for ClassJobValue { - type Error = ArchiveError; - - fn try_from(name: &str) -> Result<Self, ArchiveError> { - let value = match name { - "Gladiator" => 1, - "Pugilist" => 2, - "Marauder" => 3, - "Lancer" => 4, - "Archer" => 5, - "Conjurer" => 6, - "Thaumaturge" => 7, - "Carpenter" => 8, - "Blacksmith" => 9, - "Armorer" => 10, - "Goldsmith" => 11, - "Leatherworker" => 12, - "Weaver" => 13, - "Alchemist" => 14, - "Culinarian" => 15, - "Miner" => 16, - "Botanist" => 17, - "Fisher" => 18, - "Paladin" => 19, - "Monk" => 20, - "Warrior" => 21, - "Dragoon" => 22, - "Bard" => 23, - "White Mage" => 24, - "Black Mage" => 25, - "Arcanist" => 26, - "Summoner" => 27, - "Scholar" => 28, - "Rogue" => 29, - "Ninja" => 30, - "Machinist" => 31, - "Dark Knight" => 32, - "Astrologian" => 33, - "Samurai" => 34, - "Red Mage" => 35, - "Blue Mage" => 36, - "Gunbreaker" => 37, - "Dancer" => 38, - "Reaper" => 39, - "Sage" => 40, - "Viper" => 41, - "Pictomancer" => 42, - _ => return Err(ArchiveError::ParsingError), - }; - - Ok(Self { - name: name.to_string(), - value, - ..Default::default() - }) - } -} - -#[derive(Default, Serialize)] -pub struct GrandCompanyValue { - /// Name of the grand company. - pub name: String, - /// Name of your rank in the grand company. - pub rank: String, -} - -#[derive(Default, Serialize)] -pub struct ItemValue { - /// Name of the item. - pub name: String, -} diff --git a/templates/character.html b/templates/character.html deleted file mode 100644 index a49ddd5..0000000 --- a/templates/character.html +++ /dev/null @@ -1,17 +0,0 @@ -<html> -<head> - <title>{{ name }} - - -

Name: {{ name }}

-

World/Data Center: {{ world }} [{{ data_center }}]

-

Race/Clan/Gender: {{ race }} {{ subrace }} {{ gender }}

-

Nameday: {{ nameday }}

-

City-state: {{ city_state }}

-Character Face -Character Portrait - - - \ No newline at end of file diff --git a/templates/plate.html b/templates/plate.html deleted file mode 100644 index b9c9667..0000000 --- a/templates/plate.html +++ /dev/null @@ -1,28 +0,0 @@ - - - {{ name }}'s Adventurer Plate - - -
- - - - - - - - -
-

{{ title }}

-

{{ name }}

-

{{ world }} [{{ data_center }}]

-

LEVEL {{ level }}

-

{{ class }}

-

{{ search_comment }}

-
-
- - - \ No newline at end of file diff --git a/web/auracite.ico b/web/auracite.ico deleted file mode 100644 index 972cf08..0000000 Binary files a/web/auracite.ico and /dev/null differ diff --git a/web/auracite.svg b/web/auracite.svg deleted file mode 100644 index 7a7b65f..0000000 --- a/web/auracite.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/web/index.html b/web/index.html deleted file mode 100644 index e1515b3..0000000 --- a/web/index.html +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - Auracite - - - - -
-
- -

Auracite

-

Auracite allows you to export your FFXIV character into portable, generic formats. Enter your character's name below, and optionally use the Dalamud plugin to collect even more data like your adventurer plate.

- -
- -
- -
- -
- - -
- -
-

-
-
-
-

Auracite uses the Lodestone for collecting data about your character. However, we can't connect to the - Lodestone directly. It uses a proxy which injects CORS compliance and browser compatibility cookies.

- -

The entire process happens locally, and does not give me any personally identifiable data about you. It only - collects what is already publicly available on the Lodestone, and optionally data from the game client.

- - How does this work? - -
-
-

Depending on how much information is collected, lots of things! A snapshot of your character, or perhaps processing it to graph data over time.

-

For a cooler example, I use Auracite in the FFXIV shrine on my website.

- - What can I use this for? - -
-
-

Auracite can only collect public Lodestone information, so if you have privacy settings preventing that then there's nothing we can do. You will have to switch your profile to public for Auracite to function properly.

- - Why doesn't it work/couldn't collect information? - -
-
-

Auracite can only collect as much data about your character as they make publicly available on the Lodestone.

-

To work around this, I created a Dalamud plugin to collect even more information. It's available in my personal - Dalamud plugin repository.

-

The plugin needs to start a local HTTP server in order to communicate with Auracite. To prevent this from running - all the time, you must type /auracite begin before clicking the "Download" button. Once the process - is complete, the server is shutdown automatically. It's always safe to disable the plugin when you're not using - Auracite.

-

The website connects to your game client locally, and it does not use my server to proxy any data. No data leaves - your device.

- - What is the "Connect to Dalamud Plugin" option? - -
- -
- -