diff --git a/Larkator.sln b/Larkator.sln index 0687951..f33b899 100644 --- a/Larkator.sln +++ b/Larkator.sln @@ -20,6 +20,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SavegameToolkit", "ArkSaveg EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SavegameToolkitAdditions", "ArkSavegameToolkit\SavegameToolkitAdditions\SavegameToolkitAdditions.csproj", "{CA24E3C7-3BEE-4774-977E-E9253352CFB2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapCalibrator", "MapCalibrator\MapCalibrator.csproj", "{AA6DF52F-AECA-465B-B45B-14D18DB7F411}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -112,6 +114,22 @@ Global {CA24E3C7-3BEE-4774-977E-E9253352CFB2}.Release|x64.Build.0 = Release|Any CPU {CA24E3C7-3BEE-4774-977E-E9253352CFB2}.Release|x86.ActiveCfg = Release|Any CPU {CA24E3C7-3BEE-4774-977E-E9253352CFB2}.Release|x86.Build.0 = Release|Any CPU + {AA6DF52F-AECA-465B-B45B-14D18DB7F411}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA6DF52F-AECA-465B-B45B-14D18DB7F411}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA6DF52F-AECA-465B-B45B-14D18DB7F411}.Debug|ARM.ActiveCfg = Debug|Any CPU + {AA6DF52F-AECA-465B-B45B-14D18DB7F411}.Debug|ARM.Build.0 = Debug|Any CPU + {AA6DF52F-AECA-465B-B45B-14D18DB7F411}.Debug|x64.ActiveCfg = Debug|Any CPU + {AA6DF52F-AECA-465B-B45B-14D18DB7F411}.Debug|x64.Build.0 = Debug|Any CPU + {AA6DF52F-AECA-465B-B45B-14D18DB7F411}.Debug|x86.ActiveCfg = Debug|Any CPU + {AA6DF52F-AECA-465B-B45B-14D18DB7F411}.Debug|x86.Build.0 = Debug|Any CPU + {AA6DF52F-AECA-465B-B45B-14D18DB7F411}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA6DF52F-AECA-465B-B45B-14D18DB7F411}.Release|Any CPU.Build.0 = Release|Any CPU + {AA6DF52F-AECA-465B-B45B-14D18DB7F411}.Release|ARM.ActiveCfg = Release|Any CPU + {AA6DF52F-AECA-465B-B45B-14D18DB7F411}.Release|ARM.Build.0 = Release|Any CPU + {AA6DF52F-AECA-465B-B45B-14D18DB7F411}.Release|x64.ActiveCfg = Release|Any CPU + {AA6DF52F-AECA-465B-B45B-14D18DB7F411}.Release|x64.Build.0 = Release|Any CPU + {AA6DF52F-AECA-465B-B45B-14D18DB7F411}.Release|x86.ActiveCfg = Release|Any CPU + {AA6DF52F-AECA-465B-B45B-14D18DB7F411}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/LarkatorGUI/ArkReader.cs b/LarkatorGUI/ArkReader.cs index 4e4d3b1..bfd4826 100644 --- a/LarkatorGUI/ArkReader.cs +++ b/LarkatorGUI/ArkReader.cs @@ -13,7 +13,7 @@ namespace LarkatorGUI { public class ArkReader { - public int MapSize { get; set; } = 8000; + public MapCalibration MapCalibration { get; set; } public Dictionary<string, List<Dino>> WildDinos { get; } = new Dictionary<string, List<Dino>>(); public Dictionary<string, List<Dino>> TamedDinos { get; } = new Dictionary<string, List<Dino>>(); @@ -22,10 +22,18 @@ public class ArkReader public List<string> WildSpecies { get; } = new List<string>(); public int NumberOfTamedSpecies { get => TamedSpecies.Count; } public int NumberOfWildSpecies { get => WildSpecies.Count; } - - private ArkData arkData; + + public void SetArkData(ArkData data) + { + arkData = data; + + // Create some easy to use mappings for better performance + classMap = arkData.Creatures.ToDictionary(c => c.Class, c => c.Name); + } private static readonly string[] RAFT_CLASSES = { "Raft_BP_C", "MotorRaft_BP_C", "Barge_BP_C" }; + private ArkData arkData; + private Dictionary<string, string> classMap; private static Task<(GameObjectContainer gameObjects, float gameTime)> ReadSavegameFile(string fileName) { @@ -64,6 +72,9 @@ public class ArkReader public async Task PerformConversion(string saveFile) { + if (MapCalibration == null) + throw new ArgumentNullException(nameof(MapCalibration), "Callibration required"); + // Clear previously loaded data TamedSpecies.Clear(); WildSpecies.Clear(); @@ -78,33 +89,41 @@ public async Task PerformConversion(string saveFile) var creatureObjects = gameObjectContainer .Where(o => o.IsCreature() && !o.IsUnclaimedBaby() && !RAFT_CLASSES.Contains(o.ClassString)) .ToList(); - - var tameObjects = creatureObjects.Where(o => !o.IsWild()).GroupBy(o => o.ClassString); - TamedSpecies.AddRange(tameObjects.Select(o => o.Key)); + + var tameObjects = creatureObjects.Where(o => !o.IsWild()).GroupBy(o => SpeciesName(o.ClassString)); + TamedSpecies.AddRange(tameObjects.Select(o => o.Key).Distinct()); foreach (var group in tameObjects) TamedDinos.Add(group.Key, group.Select(o => ConvertCreature(o)).ToList()); - var wildObjects = creatureObjects.Where(o => o.IsWild()).GroupBy(o => o.ClassString); - WildSpecies.AddRange(wildObjects.Select(o => o.Key)); + var wildObjects = creatureObjects.Where(o => o.IsWild()).GroupBy(o => SpeciesName(o.ClassString)); + WildSpecies.AddRange(wildObjects.Select(o => o.Key).Distinct()); foreach (var group in wildObjects) WildDinos.Add(group.Key, group.Select(o => ConvertCreature(o)).ToList()); - AllSpecies.AddRange(creatureObjects.Select(o => o.ClassString).Distinct()); + AllSpecies.AddRange(creatureObjects.Select(o => SpeciesName(o.ClassString)).Distinct()); + } + + private string SpeciesName(string className) + { + if (classMap.TryGetValue(className, out var output)) + return output; + + return className; } private Dino ConvertCreature(GameObject obj) { var dino = new Dino { - Type = obj.ClassString, + Type = SpeciesName(obj.ClassString), Female = obj.IsFemale(), Id = (ulong)obj.GetDinoId(), BaseLevel = obj.GetBaseLevel(), - Name = obj.GetPropertyValue("TamedName", defaultValue:""), + Name = obj.GetPropertyValue("TamedName", defaultValue: ""), Location = ConvertCoordsToLatLong(obj.Location), WildLevels = new StatPoints(), }; - + var status = obj.CharacterStatusComponent(); if (status != null) { @@ -127,22 +146,11 @@ private Position ConvertCoordsToLatLong(LocationData location) Y = location.Y, Z = location.Z, - Lat = 50 + location.Y / 8000, - Lon = 50 + location.X / 8000, + Lat = MapCalibration.LatOffset + location.Y / MapCalibration.LatDivisor, + Lon = MapCalibration.LonOffset + location.X / MapCalibration.LonDivisor, }; } - private bool IsConversionRequired() - { - return true; - - //var classFile = Path.Combine(outputDir, Properties.Resources.ClassesJson); - //if (!File.Exists(classFile)) return true; - //var arkTimestamp = File.GetLastWriteTimeUtc(Properties.Settings.Default.SaveFile); - //var convertTimestamp = File.GetLastWriteTimeUtc(classFile); - //return (arkTimestamp >= convertTimestamp); - } - private string GenerateNameVariant(string name, string cls) { var clsParts = cls.Split('_'); diff --git a/LarkatorGUI/CalibrationWindow.xaml b/LarkatorGUI/CalibrationWindow.xaml index 89d1ecd..616cd51 100644 --- a/LarkatorGUI/CalibrationWindow.xaml +++ b/LarkatorGUI/CalibrationWindow.xaml @@ -35,6 +35,18 @@ <TextBlock Text="Y1" Margin="8,2"/> <TextBox Text="{Binding Bounds.Y2, Mode=TwoWay}" Width="64"/> </StackPanel> + <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="4"> + <TextBlock Text="Lat +" Margin="8,2"/> + <TextBox Text="{Binding LatOffset, Mode=TwoWay}" Width="64"/> + <TextBlock Text="/" Margin="8,2"/> + <TextBox Text="{Binding LatDivisor, Mode=TwoWay}" Width="64"/> + </StackPanel> + <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="4"> + <TextBlock Text="Lon +" Margin="8,2"/> + <TextBox Text="{Binding LonOffset, Mode=TwoWay}" Width="64"/> + <TextBlock Text="/" Margin="8,2"/> + <TextBox Text="{Binding LonDivisor, Mode=TwoWay}" Width="64"/> + </StackPanel> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="4"> <TextBlock Text="ARK filename" Margin="8,2"/> <TextBox Text="{Binding Filename, Mode=TwoWay}" Width="100" TextChanged="Filename_TextChanged"/> @@ -43,17 +55,13 @@ </StackPanel> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="4"> <TextBlock Text="+X" Margin="8,2"/> - <TextBox Text="{Binding OffsetX}" Width="32"/> + <TextBox Text="{Binding PixelOffsetX}" Width="32"/> <TextBlock Text="+Y" Margin="8,2"/> - <TextBox Text="{Binding OffsetY}" Width="32"/> + <TextBox Text="{Binding PixelOffsetY}" Width="32"/> <TextBlock Text="*X" Margin="8,2"/> - <TextBox Text="{Binding ScaleX}" Width="64"/> + <TextBox Text="{Binding PixelScaleX}" Width="64"/> <TextBlock Text="*Y" Margin="8,2"/> - <TextBox Text="{Binding ScaleY}" Width="64"/> - </StackPanel> - <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="4"> - <TextBlock Text="Units" Margin="8,2"/> - <TextBox Text="{Binding Units, Mode=TwoWay}" Width="60" TextChanged="UpdateOutput_TextChanged"/> + <TextBox Text="{Binding PixelScaleY}" Width="64"/> </StackPanel> </StackPanel> <TextBox Grid.Row="1" Text="{Binding Output}" Margin="8" MinWidth="150" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" IsReadOnly="True"/> diff --git a/LarkatorGUI/CalibrationWindow.xaml.cs b/LarkatorGUI/CalibrationWindow.xaml.cs index 19b22f3..27a8de4 100644 --- a/LarkatorGUI/CalibrationWindow.xaml.cs +++ b/LarkatorGUI/CalibrationWindow.xaml.cs @@ -92,6 +92,12 @@ public ExampleCalibration() public class Calibration : DependencyObject { + public Calibration() + { + LatDivisor = LonDivisor = 8000; + LatOffset = LonOffset = 50; + } + public Bounds Bounds { get { return (Bounds)GetValue(BoundsProperty); } @@ -110,34 +116,52 @@ public string Image set { SetValue(ImageProperty, value); } } - public int Units + public double PixelOffsetX + { + get { return (double)GetValue(PixelOffsetXProperty); } + set { SetValue(PixelOffsetXProperty, value); } + } + + public double PixelOffsetY + { + get { return (double)GetValue(PixelOffsetYProperty); } + set { SetValue(PixelOffsetYProperty, value); } + } + + public double PixelScaleX { - get { return (int)GetValue(UnitsProperty); } - set { SetValue(UnitsProperty, value); } + get { return (double)GetValue(PixelScaleXProperty); } + set { SetValue(PixelScaleXProperty, value); } } - public double OffsetX + public double PixelScaleY { - get { return (double)GetValue(OffsetXProperty); } - set { SetValue(OffsetXProperty, value); } + get { return (double)GetValue(PixelScaleYProperty); } + set { SetValue(PixelScaleYProperty, value); } } - public double OffsetY + public double LatOffset { - get { return (double)GetValue(OffsetYProperty); } - set { SetValue(OffsetYProperty, value); } + get { return (double)GetValue(LatOffsetProperty); } + set { SetValue(LatOffsetProperty, value); } } - public double ScaleX + public double LonOffset { - get { return (double)GetValue(ScaleXProperty); } - set { SetValue(ScaleXProperty, value); } + get { return (double)GetValue(LonOffsetProperty); } + set { SetValue(LonOffsetProperty, value); } } - public double ScaleY + public double LatDivisor { - get { return (double)GetValue(ScaleYProperty); } - set { SetValue(ScaleYProperty, value); } + get { return (double)GetValue(LatDivisorProperty); } + set { SetValue(LatDivisorProperty, value); } + } + + public double LonDivisor + { + get { return (double)GetValue(LonDivisorProperty); } + set { SetValue(LonDivisorProperty, value); } } public string Output @@ -149,24 +173,33 @@ public string Output public static readonly DependencyProperty OutputProperty = DependencyProperty.Register("Output", typeof(string), typeof(Calibration), new PropertyMetadata("")); - public static readonly DependencyProperty ScaleYProperty = - DependencyProperty.Register("ScaleY", typeof(double), typeof(Calibration), new PropertyMetadata(0.0)); + public static readonly DependencyProperty LonDivisorProperty = + DependencyProperty.Register("LonDivisor", typeof(double), typeof(Calibration), new PropertyMetadata(0.0)); - public static readonly DependencyProperty ScaleXProperty = - DependencyProperty.Register("ScaleX", typeof(double), typeof(Calibration), new PropertyMetadata(0.0)); + public static readonly DependencyProperty LatDivisorProperty = + DependencyProperty.Register("LatDivisor", typeof(double), typeof(Calibration), new PropertyMetadata(0.0)); - public static readonly DependencyProperty OffsetYProperty = - DependencyProperty.Register("OffsetY", typeof(double), typeof(Calibration), new PropertyMetadata(0.0)); + public static readonly DependencyProperty LonOffsetProperty = + DependencyProperty.Register("LonOffset", typeof(double), typeof(Calibration), new PropertyMetadata(0.0)); - public static readonly DependencyProperty OffsetXProperty = - DependencyProperty.Register("OffsetX", typeof(double), typeof(Calibration), new PropertyMetadata(0.0)); + public static readonly DependencyProperty LatOffsetProperty = + DependencyProperty.Register("LatOffset", typeof(double), typeof(Calibration), new PropertyMetadata(0.0)); - public static readonly DependencyProperty ImageProperty = - DependencyProperty.Register("Image", typeof(string), typeof(Calibration), new PropertyMetadata("")); + public static readonly DependencyProperty PixelScaleYProperty = + DependencyProperty.Register("PixelScaleY", typeof(double), typeof(Calibration), new PropertyMetadata(0.0)); - public static readonly DependencyProperty UnitsProperty = - DependencyProperty.Register("Units", typeof(int), typeof(Calibration), new PropertyMetadata(0)); + public static readonly DependencyProperty PixelScaleXProperty = + DependencyProperty.Register("PixelScaleX", typeof(double), typeof(Calibration), new PropertyMetadata(0.0)); + public static readonly DependencyProperty PixelOffsetYProperty = + DependencyProperty.Register("PixelOffsetY", typeof(double), typeof(Calibration), new PropertyMetadata(0.0)); + + public static readonly DependencyProperty PixelOffsetXProperty = + DependencyProperty.Register("PixelOffsetX", typeof(double), typeof(Calibration), new PropertyMetadata(0.0)); + + public static readonly DependencyProperty ImageProperty = + DependencyProperty.Register("Image", typeof(string), typeof(Calibration), new PropertyMetadata("")); + public static readonly DependencyProperty FilenameProperty = DependencyProperty.Register("Filename", typeof(string), typeof(Calibration), new PropertyMetadata("")); @@ -182,11 +215,11 @@ public void Recalculate() var maxX = Math.Max(Bounds.X1, Bounds.X2); var maxY = Math.Max(Bounds.Y1, Bounds.Y2); - ScaleX = (maxX - minX) / 80.0; - ScaleY = (maxY - minY) / 80.0; + PixelScaleX = (maxX - minX) / 80.0; + PixelScaleY = (maxY - minY) / 80.0; - OffsetX = minX - ScaleX * 10; - OffsetY = minY - ScaleY * 10; + PixelOffsetX = minX - PixelScaleX * 10; + PixelOffsetY = minY - PixelScaleY * 10; Output = RecreateOutput(); } @@ -195,11 +228,14 @@ private string RecreateOutput() { return $"{{\n" + $" \"Filename\": \"{Filename}\",\n" + - $" \"Units\": {Units},\n" + - $" \"OffsetX\": {OffsetX},\n" + - $" \"OffsetY\": {OffsetY},\n" + - $" \"ScaleX\": {ScaleX},\n" + - $" \"ScaleY\": {ScaleY}\n" + + $" \"LatOffset\": {LatOffset},\n" + + $" \"LatDivisor\": {LatDivisor},\n" + + $" \"LonOffset\": {LonOffset},\n" + + $" \"LonDivisor\": {LonDivisor},\n" + + $" \"PixelOffsetX\": {PixelOffsetX},\n" + + $" \"PixelOffsetY\": {PixelOffsetY},\n" + + $" \"PixelScaleX\": {PixelScaleX},\n" + + $" \"PixelScaleY\": {PixelScaleY}\n" + $"}},"; } } diff --git a/LarkatorGUI/DirectoryEntryBox.xaml b/LarkatorGUI/DirectoryEntryBox.xaml deleted file mode 100644 index 919e438..0000000 --- a/LarkatorGUI/DirectoryEntryBox.xaml +++ /dev/null @@ -1,17 +0,0 @@ -<UserControl x:Class="LarkatorGUI.DirectoryEntryBox" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:local="clr-namespace:LarkatorGUI" - mc:Ignorable="d" d:DesignWidth="240" - Margin="0" Padding="0"> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="*"/> - <ColumnDefinition Width="Auto"/> - </Grid.ColumnDefinitions> - <TextBox Grid.Column="0" MaxLines="1" Text="{Binding Path=Value, Mode=TwoWay}" ToolTip="{Binding Tooltip}" HorizontalContentAlignment="Right"/> - <Button Grid.Column="1" Padding="8,2" Margin="2,0" Click="Browse_Click" ToolTip="Browse...">...</Button> - </Grid> -</UserControl> diff --git a/LarkatorGUI/DirectoryEntryBox.xaml.cs b/LarkatorGUI/DirectoryEntryBox.xaml.cs deleted file mode 100644 index 1ea935a..0000000 --- a/LarkatorGUI/DirectoryEntryBox.xaml.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Avalon.Windows.Dialogs; -using Microsoft.Win32; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace LarkatorGUI -{ - /// <summary> - /// Interaction logic for FileEntryBox.xaml - /// </summary> - public partial class DirectoryEntryBox : UserControl - { - public string Value - { - get { return (string)GetValue(ValueProperty); } - set { SetValue(ValueProperty, value); } - } - - public string Title - { - get { return (string)GetValue(TitleProperty); } - set { SetValue(TitleProperty, value); } - } - - public string Tooltip - { - get { return (string)GetValue(TooltipProperty); } - set { SetValue(TooltipProperty, value); } - } - - public static readonly DependencyProperty TooltipProperty = - DependencyProperty.Register("Tooltip", typeof(string), typeof(DirectoryEntryBox), new PropertyMetadata("Enter path to the directory")); - - public static readonly DependencyProperty TitleProperty = - DependencyProperty.Register("Title", typeof(string), typeof(DirectoryEntryBox), new PropertyMetadata("Select directory")); - - public static readonly DependencyProperty ValueProperty = - DependencyProperty.Register("Value", typeof(string), typeof(DirectoryEntryBox), new PropertyMetadata("")); - - public DirectoryEntryBox() - { - InitializeComponent(); - - DataContext = this; - } - - private void Browse_Click(object sender, RoutedEventArgs e) - { - var dialog = new FolderBrowserDialog() - { - RootType = RootType.Path, - ValidateResult = true, - Title = Title, - SelectedPath = Value, - }; - - var result = dialog.ShowDialog(); - if (result == true) - { - Value = dialog.SelectedPath; - } - } - } -} diff --git a/LarkatorGUI/DummyMainWindow.cs b/LarkatorGUI/DummyMainWindow.cs index 659ce1c..4b11d61 100644 --- a/LarkatorGUI/DummyMainWindow.cs +++ b/LarkatorGUI/DummyMainWindow.cs @@ -35,10 +35,10 @@ public class DummyMainWindow private MapCalibration calibration = new MapCalibration { Filename = "TheIsland", - OffsetX = 13.75, - OffsetY = 23.75, - ScaleX = 9.8875, - ScaleY = 9.625 + PixelOffsetX = 13.75, + PixelOffsetY = 23.75, + PixelScaleX = 9.8875, + PixelScaleY = 9.625 }; public DummyMainWindow() diff --git a/LarkatorGUI/LarkatorGUI.csproj b/LarkatorGUI/LarkatorGUI.csproj index 38e3c7d..3d353ea 100644 --- a/LarkatorGUI/LarkatorGUI.csproj +++ b/LarkatorGUI/LarkatorGUI.csproj @@ -117,9 +117,6 @@ <Compile Include="DinoViewModel.cs" /> <Compile Include="DummyMainWindow.cs" /> <Compile Include="ExternalToolsException.cs" /> - <Compile Include="DirectoryEntryBox.xaml.cs"> - <DependentUpon>DirectoryEntryBox.xaml</DependentUpon> - </Compile> <Compile Include="FileEntryBox.xaml.cs"> <DependentUpon>FileEntryBox.xaml</DependentUpon> </Compile> @@ -148,10 +145,6 @@ <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> - <Page Include="DirectoryEntryBox.xaml"> - <Generator>MSBuild:Compile</Generator> - <SubType>Designer</SubType> - </Page> <Page Include="FileEntryBox.xaml"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> diff --git a/LarkatorGUI/MainWindow.xaml.cs b/LarkatorGUI/MainWindow.xaml.cs index 7e4bb12..7bc52f6 100644 --- a/LarkatorGUI/MainWindow.xaml.cs +++ b/LarkatorGUI/MainWindow.xaml.cs @@ -2,13 +2,17 @@ using GongSolutions.Wpf.DragDrop; using Larkator.Common; using Newtonsoft.Json; +using SavegameToolkitAdditions; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Deployment.Application; +using System.Diagnostics; using System.IO; using System.Linq; +using System.Net; +using System.Net.Http; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -167,7 +171,6 @@ public MainWindow() }, DispatcherPriority.Loaded); LoadSavedSearches(); - EnsureOutputDirectory(); SetupFileWatcher(); var cmdThrowExceptionAndExit = new RoutedCommand(); @@ -222,6 +225,8 @@ private void DiscoverCalibration() var imgFilename = $"pack://application:,,,/imgs/map_{MapCalibration.Filename}.jpg"; MapImage = (new ImageSourceConverter()).ConvertFromString(imgFilename) as ImageSource; if (image != null) image.Source = MapImage; + + arkReader.MapCalibration = MapCalibration; } private void ValidateWindowPositionAndSize() @@ -241,19 +246,6 @@ private void ValidateWindowPositionAndSize() } } - private void EnsureOutputDirectory() - { - if (String.IsNullOrWhiteSpace(Properties.Settings.Default.OutputDir)) - { - Properties.Settings.Default.OutputDir = Path.Combine(Path.GetTempPath(), Properties.Resources.ProgramName); - if (!Directory.Exists(Properties.Settings.Default.OutputDir)) - { - Directory.CreateDirectory(Properties.Settings.Default.OutputDir); - Properties.Settings.Default.Save(); - } - } - } - private void NotifyArkChanged() { // Cause a fresh conversion of the new ark @@ -287,10 +279,91 @@ private async void ReloadTimer_Tick(object sender, EventArgs e) private async void Window_Loaded(object sender, RoutedEventArgs e) { - //await UpdateArkToolsData(); // Maybe fetch data file? + await UpdateArkToolsData(); await ReReadArk(); } + private async Task UpdateArkToolsData() + { + StatusText = "Fetching latest species data..."; + + try + { + var targetFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Properties.Resources.ProgramName); + var targetFile = Path.Combine(targetFolder, "ark-data.json"); + if (!Directory.Exists(targetFolder)) + Directory.CreateDirectory(targetFolder); + + var fetchOkay = await FetchArkData(targetFile); + var loadOkay = await LoadArkData(targetFile); + + if (!loadOkay) throw new ApplicationException("No species data available"); + if (fetchOkay) + StatusText = "Species data loaded"; + else + StatusText = "Using old species data - offline?"; + } + catch (Exception) + { + MessageBox.Show("Unable to fetch species data - Larkator cannot function"); + Environment.Exit(3); + } + } + + private async Task<bool> FetchArkData(string targetFile) + { + try + { + using (var client = new HttpClient()) + { + if (File.Exists(targetFile)) + client.DefaultRequestHeaders.IfModifiedSince = new FileInfo(targetFile).LastWriteTimeUtc; + + using (var response = await client.GetAsync(Properties.Resources.ArkDataURL)) + { + Debug.WriteLine("Response status = ", response.StatusCode); + + // Throw exception on failure + response.EnsureSuccessStatusCode(); + + Debug.WriteLine("Response was successful"); + + // Don't do anything if the file hasn't changed + if (response.StatusCode == HttpStatusCode.NotModified) + return true; + + // Write the data to file + using (var fileWriter = File.OpenWrite(targetFile)) + { + await response.Content.CopyToAsync(fileWriter); + } + } + } + + return true; + } + catch (Exception) + { + return false; + } + } + + private async Task<bool> LoadArkData(string targetFile) + { + return await Task.Run<bool>(() => + { + try + { + arkReader.SetArkData(ArkDataReader.ReadFromFile(targetFile)); + return true; + } + catch (Exception) + { + return false; + } + }); + } + private void Searches_SelectionChanged(object sender, SelectionChangedEventArgs e) { UpdateCurrentSearch(); @@ -729,7 +802,6 @@ private void ShowWildSearches() private void OnSettingsChanged() { DiscoverCalibration(); - EnsureOutputDirectory(); CheckIfArkChanged(); UpdateCurrentSearch(); diff --git a/LarkatorGUI/MapPositionConverter.cs b/LarkatorGUI/MapPositionConverter.cs index de95384..5bc9168 100644 --- a/LarkatorGUI/MapPositionConverter.cs +++ b/LarkatorGUI/MapPositionConverter.cs @@ -47,8 +47,8 @@ public static void OnChanged(DependencyObject d, DependencyPropertyChangedEventA var pos = GetPosition(d); if (cal == null || pos == null) return; - tx.X = pos.Lon * cal.ScaleX + cal.OffsetX; - tx.Y = pos.Lat * cal.ScaleY + cal.OffsetY; + tx.X = pos.Lon * cal.PixelScaleX + cal.PixelOffsetX; + tx.Y = pos.Lat * cal.PixelScaleY + cal.PixelOffsetY; } else { @@ -60,11 +60,17 @@ public static void OnChanged(DependencyObject d, DependencyPropertyChangedEventA public class MapCalibration { public string Filename { get; set; } + + public double PixelOffsetX { get; set; } + public double PixelOffsetY { get; set; } + + public double PixelScaleX { get; set; } + public double PixelScaleY { get; set; } - public double OffsetX { get; set; } - public double OffsetY { get; set; } + public double LatOffset { get; set; } + public double LonOffset { get; set; } - public double ScaleX { get; set; } - public double ScaleY { get; set; } + public double LatDivisor { get; set; } + public double LonDivisor { get; set; } } } diff --git a/LarkatorGUI/Properties/Resources.Designer.cs b/LarkatorGUI/Properties/Resources.Designer.cs index dffe194..ce83a1e 100644 --- a/LarkatorGUI/Properties/Resources.Designer.cs +++ b/LarkatorGUI/Properties/Resources.Designer.cs @@ -60,18 +60,29 @@ internal Resources() { } } + /// <summary> + /// Looks up a localized string similar to https://ark-data.seen-von-ragan.de/data/loc/ark_data.json. + /// </summary> + internal static string ArkDataURL { + get { + return ResourceManager.GetString("ArkDataURL", resourceCulture); + } + } + /// <summary> /// Looks up a localized string similar to [ /// /// { /// "Filename": "TheIsland", - /// "OffsetX": "17.25", - /// "OffsetY": "23.75", - /// "ScaleX": "9.575", - /// "ScaleY": "9.625" + /// "Units": 8000, + /// "OffsetX": 17.25, + /// "OffsetY": 23.75, + /// "ScaleX": 9.575, + /// "ScaleY": 9.625 /// }, /// { /// "Filename": "TheCenter", + /// "Units": 8000, /// "OffsetX": 14.0, /// "OffsetY": 23.75, /// "ScaleX": 9.9, @@ -79,14 +90,12 @@ internal Resources() { /// }, /// { /// "Filename": "Aberration", + /// "Units": 8000, /// "OffsetX": 15.125, /// "OffsetY": 19.0, /// "ScaleX": 9.8875, /// "ScaleY": 9.7 - /// }, - /// { - /// "Filename": "Ragnarok", - /// "OffsetX": 15 [rest of string was truncated]";. + /// } [rest of string was truncated]";. /// </summary> internal static string calibrationsJson { get { diff --git a/LarkatorGUI/Properties/Resources.resx b/LarkatorGUI/Properties/Resources.resx index e851a40..67c645f 100644 --- a/LarkatorGUI/Properties/Resources.resx +++ b/LarkatorGUI/Properties/Resources.resx @@ -124,4 +124,7 @@ <data name="ProgramName" xml:space="preserve"> <value>Larkator</value> </data> + <data name="ArkDataURL" xml:space="preserve"> + <value>https://ark-data.seen-von-ragan.de/data/loc/ark_data.json</value> + </data> </root> \ No newline at end of file diff --git a/LarkatorGUI/SettingsWindow.xaml b/LarkatorGUI/SettingsWindow.xaml index e18a7be..407a282 100644 --- a/LarkatorGUI/SettingsWindow.xaml +++ b/LarkatorGUI/SettingsWindow.xaml @@ -10,71 +10,83 @@ Title="Settings" Background="{DynamicResource WindowBackgroundBrush}"> <Window.Resources> <local:SettingsWindowModel x:Key="Model"/> + <Style TargetType="TextBlock"> + <Setter Property="Foreground" Value="AntiqueWhite"/> + </Style> </Window.Resources> <StackPanel> - <TabControl> - <TabItem Selector.IsSelected="True" Header="Paths & Files"> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="*"/> - </Grid.ColumnDefinitions> - <Grid.RowDefinitions> - <RowDefinition Height="1*"/> - <RowDefinition Height="1*"/> - <RowDefinition Height="1*"/> - <RowDefinition Height="1*"/> - <RowDefinition Height="1*"/> - <RowDefinition Height="1*"/> - </Grid.RowDefinitions> - <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Margin="0,0,4,0">ARK Tools (ark-tools.exe)</TextBlock> - <local:FileEntryBox Grid.Column="1" Grid.Row="0" Value="{Binding Source={StaticResource Model}, Path=Settings.ArkTools, Mode=TwoWay}" + <Grid Margin="2,4,0,16"> + <Grid.Resources> + <Style TargetType="Border"> + <Setter Property="Margin" Value="3"/> + </Style> + </Grid.Resources> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition Width="*"/> + </Grid.ColumnDefinitions> + <Grid.RowDefinitions> + <RowDefinition Height="1*"/> + <RowDefinition Height="1*"/> + <RowDefinition Height="1*"/> + <RowDefinition Height="1*"/> + <RowDefinition Height="1*"/> + <RowDefinition Height="1*"/> + </Grid.RowDefinitions> + + <Border Grid.Row="0" Grid.Column="0"> + <TextBlock VerticalAlignment="Center" Margin="0,0,4,0">ARK Tools (ark-tools.exe)</TextBlock> + </Border> + <Border Grid.Column="1" Grid.Row="0"> + <local:FileEntryBox Value="{Binding Source={StaticResource Model}, Path=Settings.ArkTools, Mode=TwoWay}" Filter="ARK Tools Executable|ark-tools.exe" DefaultExt="ark-tools.exe" Title="Locate ark-tools.exe"/> - <TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Margin="0,0,4,0">Save file (.ark)</TextBlock> - <local:FileEntryBox Grid.Column="1" Grid.Row="1" Value="{Binding Source={StaticResource Model}, Path=Settings.SaveFile, Mode=TwoWay}" + </Border> + + <Border Grid.Row="1" Grid.Column="0"> + <TextBlock VerticalAlignment="Center" Margin="0,0,4,0">Save file (.ark)</TextBlock> + </Border> + <Border Grid.Column="1" Grid.Row="1"> + <local:FileEntryBox Value="{Binding Source={StaticResource Model}, Path=Settings.SaveFile, Mode=TwoWay}" Filter="ARK Save File|*.ark" DefaultExt=".ark" Title="Locate saved ARK"/> - <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Margin="0,0,4,0">Output directory</TextBlock> - <DockPanel Grid.Column="1" Grid.Row="2"> - <Button DockPanel.Dock="Right" Content="Reset" Click="ResetTmp_Click"/> - <local:DirectoryEntryBox Value="{Binding Source={StaticResource Model}, Path=Settings.OutputDir, Mode=TwoWay}" - Title="Choose a directory for temporary output files"/> - </DockPanel> - </Grid> - </TabItem> - <TabItem Header="Misc"> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto"/> - <ColumnDefinition Width="*"/> - </Grid.ColumnDefinitions> - <Grid.RowDefinitions> - <RowDefinition Height="1*"/> - <RowDefinition Height="1*"/> - <RowDefinition Height="1*"/> - <RowDefinition Height="1*"/> - <RowDefinition Height="1*"/> - <RowDefinition Height="1*"/> - </Grid.RowDefinitions> - <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Margin="0,0,4,0">Max level</TextBlock> - <local:NumericEntryControl Grid.Row="0" Grid.Column="1" ToolTip="The maximum level of normal spawns in the map (only affects filtering)" + </Border> + + <Border Grid.Row="2" Grid.Column="0"> + <TextBlock VerticalAlignment="Center" Margin="0,0,4,0">Max level</TextBlock> + </Border> + <Border Grid.Row="2" Grid.Column="1"> + <local:NumericEntryControl ToolTip="The maximum level of normal spawns in the map (only affects filtering)" Value="{Binding Source={StaticResource Model}, Path=Settings.MaxLevel, Mode=TwoWay}" MaxWidth="90" HorizontalAlignment="Right" - MaxValue="1000" MinValue="1" Increment="1" LargeIncrement="5"/> - <TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Margin="0,0,4,0">Level step</TextBlock> - <local:NumericEntryControl Grid.Row="1" Grid.Column="1" ToolTip="The step between levels, typically 4 for 120 or 5 for 150 (only affects filter adjustments)" + MaxValue="1000" MinValue="1" Increment="1" LargeIncrement="5" Margin="0,0,2,0"/> + </Border> + + <Border Grid.Row="3" Grid.Column="0"> + <TextBlock VerticalAlignment="Center" Margin="0,0,4,0">Level step</TextBlock> + </Border> + <Border Grid.Row="3" Grid.Column="1"> + <local:NumericEntryControl ToolTip="The step between levels, typically 4 for 120 or 5 for 150 (only affects filter adjustments)" Value="{Binding Source={StaticResource Model}, Path=Settings.LevelStep, Mode=TwoWay}" MaxWidth="90" HorizontalAlignment="Right" - MaxValue="100" MinValue="1" Increment="1" LargeIncrement="1"/> - <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Margin="0,0,4,0">List font size</TextBlock> - <local:NumericEntryControl Grid.Row="2" Grid.Column="1" ToolTip="Font size of the search filters and results (requires a restart)" + MaxValue="100" MinValue="1" Increment="1" LargeIncrement="1" Margin="0,0,2,0"/> + </Border> + + <Border Grid.Row="4" Grid.Column="0"> + <TextBlock VerticalAlignment="Center" Margin="0,0,4,0">List font size</TextBlock> + </Border> + <Border Grid.Row="4" Grid.Column="1"> + <local:NumericEntryControl ToolTip="Font size of the search filters and results (requires a restart)" Value="{Binding Source={StaticResource Model}, Path=Settings.ListFontSize, Mode=TwoWay}" MaxWidth="90" HorizontalAlignment="Right" - MaxValue="17" MinValue="11" Increment="1" LargeIncrement="1"/> - <TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Margin="0,0,4,0">Conversion delay (ms)</TextBlock> - <local:NumericEntryControl Grid.Row="3" Grid.Column="1" ToolTip="The delay between detecting a change to the saved ark and running the conversion process (in milliseconds)" + MaxValue="17" MinValue="11" Increment="1" LargeIncrement="1" Margin="0,0,2,0"/> + </Border> + + <Border Grid.Row="5" Grid.Column="0"> + <TextBlock VerticalAlignment="Center" Margin="0,0,4,0">Conversion delay (ms)</TextBlock> + </Border> + <Border Grid.Row="5" Grid.Column="1"> + <local:NumericEntryControl ToolTip="The delay between detecting a change to the saved ark and running the conversion process (in milliseconds)" Value="{Binding Source={StaticResource Model}, Path=Settings.ConvertDelay, Mode=TwoWay}" MaxWidth="110" HorizontalAlignment="Right" - MaxValue="10000" MinValue="200" Increment="100" LargeIncrement="500"/> - </Grid> - </TabItem> - </TabControl> - <DockPanel Margin="2,6,2,2" LastChildFill="True"> + MaxValue="10000" MinValue="200" Increment="100" LargeIncrement="500" Margin="0,0,2,0"/> + </Border> + </Grid> + <DockPanel Margin="4,6,6,4" LastChildFill="True"> <TextBlock Text="Restore all defaults" DockPanel.Dock="Left" VerticalAlignment="Bottom" TextDecorations="Underline" MouseDown="Restore_MouseDown" FontSize="10"/> <StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> <Button Padding="8,2" IsDefault="True" Click="Apply_Click" Content="Apply" Margin="0,0,4,0"/> diff --git a/LarkatorGUI/calibrations.json b/LarkatorGUI/calibrations.json index 20ceede..bdb6bd5 100644 --- a/LarkatorGUI/calibrations.json +++ b/LarkatorGUI/calibrations.json @@ -1,51 +1,68 @@ [ - { "Filename": "TheIsland", - "Units": 8000, - "OffsetX": 17.25, - "OffsetY": 23.75, - "ScaleX": 9.575, - "ScaleY": 9.625 + "LatOffset": 50, + "LatDivisor": 8000, + "LonOffset": 50, + "LonDivisor": 8000, + "PixelOffsetX": 18.375, + "PixelOffsetY": 23.75, + "PixelScaleX": 9.55, + "PixelScaleY": 9.625 }, { "Filename": "TheCenter", - "Units": 8000, - "OffsetX": 14.0, - "OffsetY": 23.75, - "ScaleX": 9.9, - "ScaleY": 9.625 + "LatOffset": 30.30309, + "LatDivisor": 9584.15646605143, + "LonOffset": 55, + "LonDivisor": 9600.55903111401, + "PixelOffsetX": 18.875, + "PixelOffsetY": 50.75, + "PixelScaleX": 9.1, + "PixelScaleY": 9.1 }, { "Filename": "Aberration", - "Units": 8000, - "OffsetX": 15.125, - "OffsetY": 19.0, - "ScaleX": 9.8875, - "ScaleY": 9.7 + "LatOffset": 50, + "LatDivisor": 8000, + "LonOffset": 50, + "LonDivisor": 8000, + "PixelOffsetX": 15.125, + "PixelOffsetY": 19.0, + "PixelScaleX": 9.8875, + "PixelScaleY": 9.7 }, { "Filename": "Ragnarok", - "Units": 13100, - "OffsetX": 15.125, - "OffsetY": 18.875, - "ScaleX": 9.8875, - "ScaleY": 9.7125 + "LatOffset": 50, + "LatDivisor": 13100, + "LonOffset": 50, + "LonDivisor": 13100, + "PixelOffsetX": 15.25, + "PixelOffsetY": 19, + "PixelScaleX": 9.875, + "PixelScaleY": 9.7 }, { "Filename": "ScorchedEarth", - "Units": 8000, - "OffsetX": 13.875, - "OffsetY": 21.125, - "ScaleX": 9.9125, - "ScaleY": 9.6875 + "LatOffset": 50, + "LatDivisor": 8000, + "LonOffset": 50, + "LonDivisor": 8000, + "PixelOffsetX": 13.875, + "PixelOffsetY": 21.125, + "PixelScaleX": 9.9125, + "PixelScaleY": 9.6875 }, { "Filename": "Extinction", - "Units": 8000, - "OffsetX": 92.375, - "OffsetY": 86.125, - "ScaleX": 8.4625, - "ScaleY": 8.2875 + "LatOffset": 50, + "LatDivisor": 8000, + "LonOffset": 50, + "LonDivisor": 8000, + "PixelOffsetX": 92.375, + "PixelOffsetY": 86.125, + "PixelScaleX": 8.4625, + "PixelScaleY": 8.2875 } ] \ No newline at end of file diff --git a/MapCalibrator/App.config b/MapCalibrator/App.config new file mode 100644 index 0000000..016d28f --- /dev/null +++ b/MapCalibrator/App.config @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8" ?> +<configuration> + <startup> + <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7" /> + </startup> +</configuration> \ No newline at end of file diff --git a/MapCalibrator/MapCalibrator.csproj b/MapCalibrator/MapCalibrator.csproj new file mode 100644 index 0000000..b1356fc --- /dev/null +++ b/MapCalibrator/MapCalibrator.csproj @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{AA6DF52F-AECA-465B-B45B-14D18DB7F411}</ProjectGuid> + <OutputType>Exe</OutputType> + <RootNamespace>MapCalibrator</RootNamespace> + <AssemblyName>MapCalibrator</AssemblyName> + <TargetFrameworkVersion>v4.7</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> + <Deterministic>true</Deterministic> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <PlatformTarget>AnyCPU</PlatformTarget> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <LangVersion>latest</LangVersion> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <PlatformTarget>AnyCPU</PlatformTarget> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="Program.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <None Include="App.config" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\ArkSavegameToolkit\SavegameToolkitAdditions\SavegameToolkitAdditions.csproj"> + <Project>{ca24e3c7-3bee-4774-977e-e9253352cfb2}</Project> + <Name>SavegameToolkitAdditions</Name> + </ProjectReference> + <ProjectReference Include="..\ArkSavegameToolkit\SavegameToolkit\SavegameToolkit.csproj"> + <Project>{1a7b8e8c-7a2c-44ab-8077-59a5f1b2a7b5}</Project> + <Name>SavegameToolkit</Name> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <PackageReference Include="MathNet.Numerics"> + <Version>4.7.0</Version> + </PackageReference> + <PackageReference Include="Newtonsoft.Json"> + <Version>11.0.2</Version> + </PackageReference> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> +</Project> \ No newline at end of file diff --git a/MapCalibrator/Program.cs b/MapCalibrator/Program.cs new file mode 100644 index 0000000..293e1d3 --- /dev/null +++ b/MapCalibrator/Program.cs @@ -0,0 +1,118 @@ +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +using SavegameToolkit; +using SavegameToolkitAdditions; + +using MathNet.Numerics; + + +namespace MapCalibrator +{ + /// <summary> + /// Coordinate conversion discovery. + /// </summary> + /// <remarks> + /// Reads the given savegame and finds all storage containers with a name "Calibration: NN.N, NN.N", + /// where NN.N, NN.N is Lat and Lon from your GPS. + /// Calculates the best coordinate conversion values that matches all of the found containers. + /// </remarks> + class Program + { + static async Task Main(string[] args) + { + var savegameFile = args.FirstOrDefault(); + if (savegameFile == null) + { + Console.Error.WriteLine("Usage: MapCalibrator.exe <savegame>"); + Environment.Exit(1); + } + + var arkDataFile = Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Larkator"), "ark-data.json"); + var arkData = ArkDataReader.ReadFromFile(arkDataFile); + + (GameObjectContainer gameObjects, float gameTime) = await ReadSavegameFile(savegameFile); + + // Find any objects that have a relevant BoxName + var items = gameObjects + .Where(o => o.Parent == null && o.GetPropertyValue<string>("BoxName", defaultValue: "").StartsWith("Calibration:")) + .ToList(); + + // Extract XYZ location and calibration lat/lon + var inputs = items.Select(o => (o.Location, LatLon: LatLongFromName(o.GetPropertyValue<string>("BoxName")))).ToArray(); + + // Perform linear regression on the values for best fit, separately for X and Y + double[] xValues = inputs.Select(i => (double)i.Location.X).ToArray(); + double[] yValues = inputs.Select(i => (double)i.Location.Y).ToArray(); + double[] lonValues = inputs.Select(i => i.LatLon.Lon).ToArray(); + double[] latValues = inputs.Select(i => i.LatLon.Lat).ToArray(); + var (xOffset, xMult) = Fit.Line(xValues, lonValues); + var (yOffset, yMult) = Fit.Line(yValues, latValues); + var xCorr = GoodnessOfFit.RSquared(xValues.Select(x => xOffset + xMult * x), lonValues); + var yCorr = GoodnessOfFit.RSquared(yValues.Select(y => yOffset + yMult * y), latValues); + + Console.WriteLine($"X: {xOffset} + X/{1 / xMult} (corr {xCorr})"); + Console.WriteLine($"Y: {yOffset} + X/{1 / yMult} (corr {yCorr})"); + + Console.ReadLine(); + } + + private static LatLon LatLongFromName(string name) + { + var coordsString = name.Split(':').Select(s => s.Trim()).Skip(1).FirstOrDefault(); + var parts = coordsString.Split(',').Select(s => s.Trim()); + var values = parts.Select(s => double.Parse(s)).ToArray(); + var result = new LatLon(values[0], values[1]); + return result; + } + + private static Task<(GameObjectContainer gameObjects, float gameTime)> ReadSavegameFile(string fileName) + { + return Task.Run(() => + { + if (new FileInfo(fileName).Length > int.MaxValue) + throw new Exception("Input file is too large."); + + var arkSavegame = new ArkSavegame(); + + using (var stream = new MemoryStream(File.ReadAllBytes(fileName))) + using (var archive = new ArkArchive(stream)) + { + arkSavegame.ReadBinary(archive, ReadingOptions.Create() + .WithDataFiles(false) + .WithEmbeddedData(false) + .WithDataFilesObjectMap(false) + .WithObjectFilter(o => !o.IsItem && (o.Parent != null || o.Components.Any())) + .WithBuildComponentTree(true)); + } + + if (!arkSavegame.HibernationEntries.Any()) + return (arkSavegame, arkSavegame.GameTime); + + var combinedObjects = arkSavegame.Objects; + + foreach (var entry in arkSavegame.HibernationEntries) + { + var collector = new ObjectCollector(entry, 1); + combinedObjects.AddRange(collector.Remap(combinedObjects.Count)); + } + + return (new GameObjectContainer(combinedObjects), arkSavegame.GameTime); + }); + } + + public struct LatLon + { + public LatLon(double lat, double lon) : this() + { + Lat = lat; + Lon = lon; + } + + public double Lat { get; set; } + public double Lon { get; set; } + } + } +} diff --git a/MapCalibrator/Properties/AssemblyInfo.cs b/MapCalibrator/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..7dd94e9 --- /dev/null +++ b/MapCalibrator/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MapCalibrator")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MapCalibrator")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("aa6df52f-aeca-465b-b45b-14d18db7f411")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")]