diff --git a/ElectronicObserver/Data/KCDatabase.cs b/ElectronicObserver/Data/KCDatabase.cs index 18318ef19..09306bdb2 100644 --- a/ElectronicObserver/Data/KCDatabase.cs +++ b/ElectronicObserver/Data/KCDatabase.cs @@ -1,4 +1,5 @@ -using ElectronicObserver.Data.Battle; +using DynaJson; +using ElectronicObserver.Data.Battle; using ElectronicObserver.Data.Quest; using ElectronicObserver.Data.Translation; using ElectronicObserver.Data.TsunDbSubmission; @@ -206,8 +207,41 @@ private KCDatabase() TsunDbSubmission = new TsunDbSubmissionManager(); FleetPreset = new FleetPresetManager(); Translation = new DataAndTranslationManager(); + +#if DEBUG + // data needed for loading old event battles via local api loader + // the values don't really matter, they just need to exist + for (int i = 56; i <= 59; i++) + { + AddWorld(i); + } +#endif } + private void AddWorld(int world) + { + string json = $$"""{"api_id":{{world}},"api_name":"","api_type":1}"""; + dynamic elem = JsonObject.Parse(json); + + MapAreaData item = new(); + item.LoadFromResponse("api_start2/getData", elem); + MapArea.Add(item); + + for (int i = 1; i <= 7; i++) + { + AddMap(world, i); + } + } + + private void AddMap(int world, int map) + { + string json = $$"""{"api_id":{{world}}{{map}},"api_maparea_id":{{world}},"api_no":{{map}},"api_name":"","api_level":6,"api_opetext":"","api_infotext":"","api_item":[32,34,0,0],"api_max_maphp":300,"api_required_defeat_count":null,"api_sally_flag":[1,0,0]}"""; + dynamic elem = JsonObject.Parse(json); + + MapInfoData item = new(); + item.LoadFromResponse("api_start2/getData", elem); + MapInfo.Add(item); + } public void Load() { diff --git a/ElectronicObserver/Window/Dialog/DialogLocalAPILoader2.cs b/ElectronicObserver/Window/Dialog/DialogLocalAPILoader2.cs index c9c768641..e22ff22c5 100644 --- a/ElectronicObserver/Window/Dialog/DialogLocalAPILoader2.cs +++ b/ElectronicObserver/Window/Dialog/DialogLocalAPILoader2.cs @@ -4,15 +4,31 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Windows.Forms; +using ElectronicObserver.Database; +using ElectronicObserver.Database.KancolleApi; +using ElectronicObserver.Database.Sortie; +using ElectronicObserver.KancolleApi.Types; +using ElectronicObserver.KancolleApi.Types.ApiPort.Port; +using ElectronicObserver.KancolleApi.Types.Models; using ElectronicObserver.Observer; +using ElectronicObserver.Utility; using ElectronicObserver.ViewModels; +using Microsoft.EntityFrameworkCore; namespace ElectronicObserver.Window.Dialog; public partial class DialogLocalAPILoader2 : Form { + private JsonSerializerOptions JsonSerializerOptions { get; } = new() + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + }; + private SortieRecord? SortieRecord { get; } + private List ApiFilesBeforeSortie { get; set; } = []; private string CurrentPath { get; set; } @@ -24,6 +40,11 @@ public DialogLocalAPILoader2() Translate(); } + public DialogLocalAPILoader2(SortieRecord sortieRecord) : this() + { + SortieRecord = sortieRecord; + } + public void Translate() { APIView_FileName.HeaderText = LocalAPILoader2Resources.APIView_FileName; @@ -44,7 +65,14 @@ public void Translate() private void DialogLocalAPILoader2_Load(object sender, EventArgs e) { - LoadFiles(Utility.Configuration.Config.Connection.SaveDataPath); + if (SortieRecord is null) + { + LoadFiles(Utility.Configuration.Config.Connection.SaveDataPath); + } + else + { + LoadSortieRecordFiles(SortieRecord); + } } @@ -101,14 +129,19 @@ private void ViewMenu_Delete_Click(object sender, EventArgs e) private void ButtonExecuteNext_Click(object sender, EventArgs e) { - if (APIView.SelectedRows.Count == 1) { - - var row = APIView.SelectedRows[0]; + DataGridViewRow row = APIView.SelectedRows[0]; int index = APIView.SelectedRows[0].Index; - ExecuteAPI((string)row.Cells[APIView_FileName.Index].Value); + if (SortieRecord is null) + { + ExecuteAPI((string)row.Cells[APIView_FileName.Index].Value); + } + else + { + ExecuteSortieRecordApi((string)row.Cells[APIView_FileName.Index].Value); + } APIView.ClearSelection(); if (index < APIView.Rows.Count - 1) @@ -154,6 +187,45 @@ private void LoadFiles(string path) } + private void LoadSortieRecordFiles(SortieRecord sortieRecord) + { + if (sortieRecord.ApiFiles.Count is 0) return; + + APIView.Rows.Clear(); + + List rows = []; + + int firstSortieApiFileId = sortieRecord.ApiFiles.MinBy(f => f.Id)!.Id; + + ElectronicObserverContext db = new(); + db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; + + int lastPortFileIdBeforeSortieId = db.ApiFiles + .Where(f => f.Id < firstSortieApiFileId) + .Where(f => f.Name == "api_port/port") + .OrderByDescending(f => f.Id) + .First() + .Id; + + ApiFilesBeforeSortie = db.ApiFiles + .Where(f => f.Id < firstSortieApiFileId) + .Where(f => f.Id >= lastPortFileIdBeforeSortieId) + .ToList(); + + foreach (string file in ApiFilesBeforeSortie.Concat(sortieRecord.ApiFiles).Select(f => $"{f.Id} {f.ApiFileType} {f.Name}")) + { + DataGridViewRow row = new(); + row.CreateCells(APIView); + + row.SetValues(file); + rows.Add(row); + } + + APIView.Rows.AddRange([.. rows]); + APIView.Sort(APIView_FileName, ListSortDirection.Ascending); + + } + //filename format: yyyyMMdd_hhmmssff[Q|S]@apipath@apiname.json private string GetAPIName(string fileName) @@ -211,15 +283,85 @@ private void ExecuteAPI(string filename) } + private void ExecuteSortieRecordApi(string filename) + { + Debug.Assert(SortieRecord is not null); + + string[] values = filename.Split(" "); + + int recordId = int.Parse(values[0]); + ApiFileType apiFileType = Enum.Parse(values[1]); + string apiName = values[2]; + + if (!APIObserver.Instance.APIList.ContainsKey(apiName)) return; + + ApiFile apiFile = ApiFilesBeforeSortie.Concat(SortieRecord.ApiFiles).First(f => f.Id == recordId); + if (apiFile.Name is "api_port/port") + { + string? savedPortResponseJson = LoadApiResponse("api_port/port"); + + if (savedPortResponseJson is not null) + { + ApiResponse savedPortResponse = JsonSerializer + .Deserialize>(savedPortResponseJson[7..]) + ?? throw new NotImplementedException(); + + ApiResponse apiFilePortResponse = JsonSerializer + .Deserialize>(apiFile.Content) + ?? throw new NotImplementedException(); + + apiFilePortResponse.ApiData.ApiShip = savedPortResponse.ApiData.ApiShip; + apiFilePortResponse.ApiData.ApiLog = savedPortResponse.ApiData.ApiLog; + apiFilePortResponse.ApiData.ApiNdock = savedPortResponse.ApiData.ApiNdock; + + apiFilePortResponse.ApiData.ApiDeckPort = SortieRecord.FleetData.Fleets + .Select((f, i) => f switch + { + null => null, + + _ => new FleetDataDto + { + ApiId = i + 1, + ApiShip = f.Ships.Select(s => s.DropId ?? 0).ToList(), + ApiMission = [0, 0, 0, 0], + }, + }) + .OfType() + .ToList(); + + apiFile.Content = JsonSerializer.Serialize(apiFilePortResponse, JsonSerializerOptions); + } + } + + switch (apiFileType) + { + case ApiFileType.Request: + Dictionary queryParameters = JsonSerializer + .Deserialize>(apiFile.Content) + ?? throw new NotImplementedException(); + + string queryString = string.Join("&", queryParameters.Select(kvp => $"{kvp.Key}={kvp.Value}")); + APIObserver.Instance.LoadRequest("/kcsapi/" + apiName, queryString); + break; + + case ApiFileType.Response: + APIObserver.Instance.LoadResponse("/kcsapi/" + apiName, $"svdata={apiFile.Content}"); + break; + } + } private void APICaller_DoWork(object sender, DoWorkEventArgs e) { + if (e.Argument is not IEnumerable files) return; - var files = e.Argument as IOrderedEnumerable; - var act = new Action(ExecuteAPI); + Action act = SortieRecord switch + { + null => ExecuteAPI, + _ => ExecuteSortieRecordApi, + }; - foreach (var file in files) + foreach (string? file in files) { Invoke(act, file); System.Threading.Thread.Sleep(10); //ゆるして @@ -375,7 +517,7 @@ private void APIView_CellMouseDoubleClick(object sender, DataGridViewCellMouseEv { try { - ProcessStartInfo psi = new ProcessStartInfo + ProcessStartInfo psi = new() { FileName = CurrentPath + "\\" + APIView.SelectedCells.OfType().First().Value, UseShellExecute = true @@ -387,4 +529,18 @@ private void APIView_CellMouseDoubleClick(object sender, DataGridViewCellMouseEv Utility.Logger.Add(1, string.Format(LocalAPILoader2Resources.FailedToStart, ex.GetType().Name, ex.Message)); } } + + private static string? LoadApiResponse(string apiName) + { + Configuration.ConfigurationData config = Configuration.Config; + + if (string.IsNullOrEmpty(config.Connection.SaveDataPath)) return null; + if (!Directory.Exists(config.Connection.SaveDataPath)) return null; + + string filePath = Path.Combine(config.Connection.SaveDataPath, "kcsapi", apiName); + + if (!File.Exists(filePath)) return null; + + return File.ReadAllText(filePath); + } } diff --git a/ElectronicObserver/Window/Tools/SortieRecordViewer/SortieRecordViewerViewModel.cs b/ElectronicObserver/Window/Tools/SortieRecordViewer/SortieRecordViewerViewModel.cs index c15867145..a0b8ab74f 100644 --- a/ElectronicObserver/Window/Tools/SortieRecordViewer/SortieRecordViewerViewModel.cs +++ b/ElectronicObserver/Window/Tools/SortieRecordViewer/SortieRecordViewerViewModel.cs @@ -23,6 +23,7 @@ using ElectronicObserver.Database.DataMigration; using ElectronicObserver.Services; using ElectronicObserver.Utility; +using ElectronicObserver.Window.Dialog; using ElectronicObserver.Window.Tools.SortieRecordViewer.DataExport; using ElectronicObserver.Window.Tools.SortieRecordViewer.SortieCostViewer; using Microsoft.EntityFrameworkCore; @@ -77,6 +78,13 @@ public partial class SortieRecordViewerViewModel : WindowViewModelBase private SortieCostConfigurationViewModel SortieCostConfiguration { get; } = new(); + public bool IsDebug => +#if DEBUG + true; +#else + false; +#endif + public SortieRecordViewerViewModel() { ToolService = Ioc.Default.GetRequiredService(); @@ -471,4 +479,15 @@ private void OpenSortieCost() new SortieCostViewerWindow(sortieCost).Show(); } + + [RelayCommand] + private async Task OpenLocalApiLoader() + { + if (!IsDebug) return; + if (SelectedSortie is null) return; + + await SelectedSortie.Model.EnsureApiFilesLoaded(Db); + + new DialogLocalAPILoader2(SelectedSortie.Model).Show(); + } } diff --git a/ElectronicObserver/Window/Tools/SortieRecordViewer/SortieRecordViewerWindow.xaml b/ElectronicObserver/Window/Tools/SortieRecordViewer/SortieRecordViewerWindow.xaml index 3bbfbd735..c841210b6 100644 --- a/ElectronicObserver/Window/Tools/SortieRecordViewer/SortieRecordViewerWindow.xaml +++ b/ElectronicObserver/Window/Tools/SortieRecordViewer/SortieRecordViewerWindow.xaml @@ -194,6 +194,11 @@ +