diff --git a/.gitmodules b/.gitmodules
index 24edb462..0b5e2363 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -2,3 +2,7 @@
path = ArkSavegameToolkit
url = https://github.com/cadon/ArkSavegameToolkit.git
branch = master
+[submodule "AsaSavegameToolkit"]
+ path = AsaSavegameToolkit
+ url = git@github.com:Olfi01/AsaSavegameToolkit.git
+ branch = asb
diff --git a/ARKBreedingStats.sln b/ARKBreedingStats.sln
index cdb3de61..aca44ef2 100644
--- a/ARKBreedingStats.sln
+++ b/ARKBreedingStats.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.32002.261
+# Visual Studio Version 18
+VisualStudioVersion = 18.6.11806.211 stable
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ARKBreedingStats", "ARKBreedingStats\ARKBreedingStats.csproj", "{991563CE-6B2C-40AE-BC80-A14F090A4D26}"
ProjectSection(ProjectDependencies) = postProject
@@ -25,6 +25,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_meta", "_meta", "{5DAADC66
translations.txt = translations.txt
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsaSavegameToolkit", "AsaSavegameToolkit\src\AsaSavegameToolkit.csproj", "{DC4E684E-1A84-2D33-95CA-154FECA26A10}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -51,6 +53,10 @@ Global
{E7C8F9A2-D4B3-4C1E-9F2A-1A8B5C3D4E5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7C8F9A2-D4B3-4C1E-9F2A-1A8B5C3D4E5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7C8F9A2-D4B3-4C1E-9F2A-1A8B5C3D4E5F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DC4E684E-1A84-2D33-95CA-154FECA26A10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DC4E684E-1A84-2D33-95CA-154FECA26A10}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DC4E684E-1A84-2D33-95CA-154FECA26A10}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DC4E684E-1A84-2D33-95CA-154FECA26A10}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/ARKBreedingStats/ARKBreedingStats.csproj b/ARKBreedingStats/ARKBreedingStats.csproj
index 202df7ec..cc449068 100644
--- a/ARKBreedingStats/ARKBreedingStats.csproj
+++ b/ARKBreedingStats/ARKBreedingStats.csproj
@@ -39,6 +39,14 @@
+
+
+
+
+
+
+
+
diff --git a/ARKBreedingStats/Form1.importSave.cs b/ARKBreedingStats/Form1.importSave.cs
index d3a1a9d4..2eb77234 100644
--- a/ARKBreedingStats/Form1.importSave.cs
+++ b/ARKBreedingStats/Form1.importSave.cs
@@ -155,8 +155,7 @@ private async Task RunSavegameImport(string fileLocation, string conveni
}
catch (Exception ex)
{
- var noAsaSupportInfo = ex.Message.StartsWith("Found unknown Version 20819") ? "Importing save games from ARK: Survival Ascended (ASA) is not yet supported, currently only ARK: Survival Evolved (ASE) is supported for save file import.\n\n" : null;
- MessageBoxes.ExceptionMessageBox(ex, $"{noAsaSupportInfo}An error occurred while importing the file {fileLocation}.", "Save file import error");
+ MessageBoxes.ExceptionMessageBox(ex, $"An error occurred while importing the file {fileLocation}.", "Save file import error");
return string.Empty;
}
finally
diff --git a/ARKBreedingStats/ImportSavegame.cs b/ARKBreedingStats/ImportSavegame.cs
index 9f486c0c..3bb8f8c7 100644
--- a/ARKBreedingStats/ImportSavegame.cs
+++ b/ARKBreedingStats/ImportSavegame.cs
@@ -1,6 +1,7 @@
using ARKBreedingStats.Library;
using ARKBreedingStats.species;
using ARKBreedingStats.values;
+using AsaSavegameToolkit.Porcelain;
using SavegameToolkit;
using SavegameToolkit.Arrays;
using SavegameToolkit.Structs;
@@ -12,6 +13,16 @@
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
+using Creature = ARKBreedingStats.Library.Creature;
+using SavegameCreature = AsaSavegameToolkit.Porcelain.Creature;
+using GameObjectExtensions = SavegameToolkitAdditions.GameObjectExtensions;
+using AsaSavegameToolkit.Plumbing.Utilities;
+#if DEBUG
+using AsaSavegameToolkit.Plumbing.Records;
+#else
+using AsaSavegameToolkit.Plumbing.Properties;
+#endif
+using Microsoft.Extensions.Logging.Abstractions;
namespace ARKBreedingStats
{
@@ -26,6 +37,77 @@ private ImportSavegame(float gameTime)
}
public static async Task ImportCollectionFromSavegame(CreatureCollection creatureCollection, string filename, string serverName)
+ {
+ byte[] first16Bytes = new byte[16];
+ using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
+ {
+ fs.ReadExactly(first16Bytes, 0, 16);
+ }
+ string checkString = System.Text.ASCIIEncoding.ASCII.GetString(first16Bytes);
+ bool isAsaSavegame = checkString.Contains("SQLite format");
+ var creatures = await (isAsaSavegame ? ImportCollectionFromAsaSavegame(creatureCollection, filename, serverName) : ImportCollectionFromAseSavegame(creatureCollection, filename, serverName));
+
+ ArkName.ClearCache();
+
+ // if there are creatures with unknown species, check if the according mod-file is available
+ var unknownSpeciesCreatures = creatures.Where(c => c.Species == null).ToArray();
+
+ if (!unknownSpeciesCreatures.Any()
+ || Properties.Settings.Default.IgnoreUnknownBlueprintsOnSaveImport
+ || MessageBox.Show("The species of " + unknownSpeciesCreatures.Length + " creature" + (unknownSpeciesCreatures.Length != 1 ? "s" : "") + " is not recognized, probably because they are from a mod that is not loaded.\n"
+ + "The unrecognized species-classes are as follows, all the according creatures cannot be imported:\n\n" + string.Join("\n", unknownSpeciesCreatures.Select(c => c.name).Distinct().ToArray())
+ + "\n\nTo import the unrecognized creatures, you first need mod values-files, see Settings - Mod value manager… if the mod value is available\n\n"
+ + "Do you want to import the recognized creatures? If you click no, nothing is imported.",
+ "Unrecognized species while importing savegame", MessageBoxButtons.YesNo, MessageBoxIcon.Question
+ ) == DialogResult.Yes
+ )
+ {
+ ImportCollection(creatureCollection, creatures.Where(c => c.Species != null).ToList(), serverName);
+ }
+ }
+
+ private static async Task ImportCollectionFromAsaSavegame(CreatureCollection creatureCollection, string filename, string serverName)
+ {
+ var ignoreClasses = Values.V.IgnoreSpeciesClassesOnImport;
+ var importUnclaimedBabies = Properties.Settings.Default.SaveFileImportUnclaimedBabies;
+ var saveImportCryo = Properties.Settings.Default.SaveImportCryo;
+
+ var save = AsaSaveGame.ReadFrom(filename, logger: NullLogger.Instance);
+#if DEBUG
+ var gameTime = save.GameTime;
+#else
+ var gameTime = 0d; // wait for https://github.com/flintthatchwood/AsaSavegameToolkit/pull/3 to be merged and published
+#endif
+
+ var tamedCreatureObjects = save.TamedCreatures.Values.Where(o => (importUnclaimedBabies || !o.Record.IsUnclaimedBaby()) && !ignoreClasses.Contains(o.ClassName.Split('.').Last()));
+ if (saveImportCryo)
+ {
+ tamedCreatureObjects = tamedCreatureObjects.Concat(save.CryopoddedCreatures.Values.Where(o => !ignoreClasses.Contains(o.ClassName.Split('.').Last())));
+ }
+
+ if (!string.IsNullOrWhiteSpace(Properties.Settings.Default.ImportTribeNameFilter))
+ {
+ string[] filters = Properties.Settings.Default.ImportTribeNameFilter.Split(',')
+ .Select(s => s.Trim())
+ .Where(s => !string.IsNullOrEmpty(s))
+ .ToArray();
+
+ if (filters.Any())
+ {
+ tamedCreatureObjects = tamedCreatureObjects.Where(o =>
+ {
+ string tribeName = o.TribeName ?? string.Empty;
+ return filters.Any(filter => tribeName.Contains(filter));
+ });
+ }
+ }
+
+ ImportSavegame importSavegame = new ImportSavegame(Convert.ToSingle(gameTime));
+ int? wildLevelStep = creatureCollection.getWildLevelStep();
+ return tamedCreatureObjects.Select(o => importSavegame.ConvertCreature(o, wildLevelStep)).Where(c => c != null).ToArray();
+ }
+
+ private static async Task ImportCollectionFromAseSavegame(CreatureCollection creatureCollection, string filename, string serverName)
{
(GameObjectContainer gameObjectContainer, float gameTime) = await Task.Run(() => ReadSavegameFile(filename));
var ignoreClasses = Values.V.IgnoreSpeciesClassesOnImport;
@@ -56,25 +138,7 @@ public static async Task ImportCollectionFromSavegame(CreatureCollection creatur
ImportSavegame importSavegame = new ImportSavegame(gameTime);
int? wildLevelStep = creatureCollection.getWildLevelStep();
- var creatures = tamedCreatureObjects.Select(o => importSavegame.ConvertGameObject(o, wildLevelStep)).Where(c => c != null).ToArray();
-
- ArkName.ClearCache();
-
- // if there are creatures with unknown species, check if the according mod-file is available
- var unknownSpeciesCreatures = creatures.Where(c => c.Species == null).ToArray();
-
- if (!unknownSpeciesCreatures.Any()
- || Properties.Settings.Default.IgnoreUnknownBlueprintsOnSaveImport
- || MessageBox.Show("The species of " + unknownSpeciesCreatures.Length + " creature" + (unknownSpeciesCreatures.Length != 1 ? "s" : "") + " is not recognized, probably because they are from a mod that is not loaded.\n"
- + "The unrecognized species-classes are as follows, all the according creatures cannot be imported:\n\n" + string.Join("\n", unknownSpeciesCreatures.Select(c => c.name).Distinct().ToArray())
- + "\n\nTo import the unrecognized creatures, you first need mod values-files, see Settings - Mod value manager… if the mod value is available\n\n"
- + "Do you want to import the recognized creatures? If you click no, nothing is imported.",
- "Unrecognized species while importing savegame", MessageBoxButtons.YesNo, MessageBoxIcon.Question
- ) == DialogResult.Yes
- )
- {
- ImportCollection(creatureCollection, creatures.Where(c => c.Species != null).ToList(), serverName);
- }
+ return tamedCreatureObjects.Select(o => importSavegame.ConvertGameObject(o, wildLevelStep)).Where(c => c != null).ToArray();
}
private static (GameObjectContainer, float) ReadSavegameFile(string fileName)
@@ -254,5 +318,146 @@ private Creature ConvertGameObject(GameObject creatureObject, int? levelStep)
return creature;
}
+
+ private Creature ConvertCreature(SavegameCreature creatureObject, int? levelStep)
+ {
+ if (!Values.V.TryGetSpeciesByBlueprint(creatureObject.ClassName, out Species species))
+ {
+ // species is unknown, creature cannot be imported.
+ // use name-field to temporarily save the unknown classString to display in a messageBox
+ return new Creature { name = creatureObject.ClassName };
+ }
+
+ string imprinterName = creatureObject.ImprinterName;
+ string owner = string.IsNullOrWhiteSpace(imprinterName) ? creatureObject.TamerString : imprinterName;
+
+ int[] wildLevels = Enumerable.Repeat(-1, Stats.StatsCount).ToArray(); // -1 is unknown
+ int[] tamedLevels = new int[Stats.StatsCount];
+ int[] mutatedLevels = new int[Stats.StatsCount];
+
+ for (int i = 0; i < Stats.StatsCount; i++)
+ {
+ wildLevels[i] = creatureObject.WildLevels[i] ?? 0;
+ }
+ wildLevels[Stats.Torpidity] = (creatureObject.BaseLevel ?? 1) - 1; // torpor
+
+ for (int i = 0; i < Stats.StatsCount; i++)
+ {
+ tamedLevels[i] = creatureObject.TamedLevelsApplied[i] ?? 0;
+ mutatedLevels[i] = creatureObject.MutationLevelsApplied[i] ?? 0;
+ }
+
+ if (!creatureObject.Record.Properties.TryGet("TamedIneffectivenessModifier", out float ti))
+ {
+ ti = creatureObject.Record.Properties.Get("TameIneffectivenessModifier");
+ }
+ double te = 1f / (1 + ti);
+
+ Guid creatureGuid = creatureObject.Id;
+ long arkId = Utils.ConvertCreatureGuidToArkId(creatureGuid);
+ Creature creature = new Creature(species,
+ creatureObject.TamedName, owner, creatureObject.TribeName,
+ creatureObject.IsFemale ? Sex.Female : Sex.Male,
+ wildLevels, tamedLevels, mutatedLevels, te,
+ !string.IsNullOrWhiteSpace(creatureObject.ImprinterName),
+ creatureObject.ImprintQuality,
+ levelStep
+ )
+ {
+ imprinterName = creatureObject.ImprinterName,
+ guid = creatureGuid,
+ ArkId = arkId,
+ ArkIdImported = true,
+ ArkIdInGame = Utils.ConvertImportedArkIdToIngameVisualization(arkId),
+ domesticatedAt = DateTime.Now,
+ addedToLibrary = DateTime.Now,
+ mutationsMaternal = creatureObject.MutationsFemale,
+ mutationsPaternal = creatureObject.MutationsMale,
+ flags = (creatureObject.Record.Properties.Get("bNeutered") ? CreatureFlags.Neutered : CreatureFlags.None)
+ | (creatureObject.Record.Properties.Get("MutagenApplied") ? CreatureFlags.MutagenApplied : CreatureFlags.None)
+ };
+
+ // If it's a baby and still growing, work out growingUntil
+ float babyAge = creatureObject.BabyAge ?? 1;
+ if (babyAge < 1)
+ {
+ double maturationDuration = species.breeding?.maturationTimeAdjusted ?? 0;
+ float bornSecondsAgo = (float)maturationDuration * babyAge;
+ if (bornSecondsAgo < maturationDuration - 120) // there seems to be a slight offset of one of these saved values, so don't display a creature as being in cooldown if it is about to leave it in the next 2 minutes
+ creature.growingUntil = DateTime.Now.Add(TimeSpan.FromSeconds(maturationDuration - bornSecondsAgo));
+ }
+ else
+ {
+ double nextMatingPossible = creatureObject.Record.Properties.Get("NextAllowedMatingTime");
+ if (_gameTime < nextMatingPossible)
+ {
+ creature.cooldownUntil = DateTime.Now.Add(TimeSpan.FromSeconds(nextMatingPossible - _gameTime));
+ }
+ }
+
+ // Ancestor linking is done later after entire collection is formed - here we just set the guids
+#if DEBUG
+ IEnumerable femaleAncestors = creatureObject.Record.Properties.Get