Skip to content

API Hawk: Cannot load configuration file when attempting to load persisted configurations for external tools. #3337

@tommai78101

Description

@tommai78101

Summary

If the external tool is opened at least once, and the external tool uses the following C# codes below, in theory, the configuration file config.ini will include information of the external tool.

[ConfigPersist]
public GeneticAlgorithmBotSettings Settings { get; set; }
public class GeneticAlgorithmBotSettings {
	public RecentFiles RecentBotFiles { get; set; } = new RecentFiles();
	public bool TurboWhenBotting { get; set; } = true;
	public bool InvisibleEmulation { get; set; }
}

Below is the snippet of the config.ini:

  ...

  "CommonToolSettings": {
    "BizHawk.Client.EmuHawk.TAStudio": {
      "_wndx": 543,
      "_wndy": 55,
      "Width": 525,
      "Height": 615,
      "SaveWindowPosition": true,
      "TopMost": false,
      "FloatingWindow": true,
      "AutoLoad": false
    },
    "GeneticAlgorithmBot.GeneticAlgorithmBot": {
      "_wndx": 1091,
      "_wndy": 149,
      "Width": 723,
      "Height": 626,
      "SaveWindowPosition": true,
      "TopMost": false,
      "FloatingWindow": true,
      "AutoLoad": false
    }
  },
  "CustomToolSettings": {
    "BizHawk.Client.EmuHawk.TAStudio": {
      "Settings": {
        "$type": "BizHawk.Client.EmuHawk.TAStudio+TAStudioSettings, EmuHawk",
        "RecentTas": {
          "recentlist": [],
          "MAX_RECENT_FILES": 8,
          "AutoLoad": false,
          "Frozen": false
        },
        "AutoPause": true,
        "AutoRestoreLastPosition": false,
        "FollowCursor": true,
        "EmptyMarkers": false,
        "ScrollSpeed": 6,
        "FollowCursorAlwaysScroll": false,
        "FollowCursorScrollMethod": "near",
        "BranchCellHoverInterval": 1,
        "SeekingCutoffInterval": 2,
        "AutosaveInterval": 120000,
        "AutosaveAsBk2": false,
        "AutosaveAsBackupFile": false,
        "BackupPerFileSave": false,
        "SingleClickAxisEdit": false,
        "OldControlSchemeForBranches": false,
        "LoadBranchOnDoubleClick": true,
        "DenoteStatesWithIcons": false,
        "DenoteStatesWithBGColor": true,
        "DenoteMarkersWithIcons": false,
        "DenoteMarkersWithBGColor": true,
        "MainVerticalSplitDistance": 0,
        "BranchMarkerSplitDistance": 183,
        "BindMarkersToInput": false,
        "CopyIncludesFrameNo": false,
        "Palette": {
          "CurrentFrame_InputLog": "181, 231, 247",
          "GreenZone_FrameCol": "221, 255, 221",
          "GreenZone_InputLog": "210, 249, 211",
          "GreenZone_InputLog_Stated": "196, 247, 200",
          "GreenZone_InputLog_Invalidated": "224, 251, 224",
          "LagZone_FrameCol": "255, 220, 221",
          "LagZone_InputLog": "244, 218, 218",
          "LagZone_InputLog_Stated": "240, 208, 210",
          "LagZone_InputLog_Invalidated": "247, 229, 229",
          "Marker_FrameCol": "247, 255, 201",
          "AnalogEdit_Col": "144, 144, 112"
        }
      },
      "TasViewFont": "Arial, 8.25pt, style=Bold"
    },
    "GeneticAlgorithmBot.GeneticAlgorithmBot": {
      "Settings": {
        "$type": "GeneticAlgorithmBot.GeneticAlgorithmBotSettings, GeneticAlgorithmBot",
        "RecentBotFiles": {
          "recentlist": [],
          "MAX_RECENT_FILES": 8,
          "AutoLoad": false,
          "Frozen": false
        },
        "TurboWhenBotting": true,
        "InvisibleEmulation": false
      }
    }
  },

  ...

I suspect that the formatting of the JSON object for CustomToolSettings and CommonToolSettings both didn't expect there to be an external tool reference if the external tool is using [ConfigPersist] attribute in the code.

Repro

  1. Use any blank C# project that is set up to create an external tool for BizHawk.
  2. Declare a class type with the [ConfigPersist] attribute and create a class type with any properties (int, float, or even RecentFiles class type).
  3. Build the external tool.
  4. Load the external tool in EmuHawk just 1 time.
  5. Close the external tool window (Important step!)
  6. Close EmuHawk.
  7. Open EmuHawk again.
  8. Observe crash.

Output

Full stack trace below:

It appears your config file (config.ini) is corrupted; an exception was thrown while loading it.
On closing this warning, EmuHawk will delete your config file and generate a new one. You can go make a backup now if you'd like to look into diffs.
The caught exception was:
System.InvalidOperationException: Config Error ---> Newtonsoft.Json.JsonSerializationException: Error resolving type specified in JSON 'GeneticAlgorithmBot.GeneticAlgorithmBotSettings, GeneticAlgorithmBot'. Path 'CustomToolSettings['GeneticAlgorithmBot.GeneticAlgorithmBot'].Settings.$type', line 1573, position 87. ---> Newtonsoft.Json.JsonSerializationException: Could not load assembly 'GeneticAlgorithmBot'.
   at Newtonsoft.Json.Serialization.DefaultSerializationBinder.GetTypeFromTypeNameKey(StructMultiKey`2 typeNameKey)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Newtonsoft.Json.Serialization.DefaultSerializationBinder.BindToType(String assemblyName, String typeName)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolveTypeName(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, String qualifiedTypeName)
   --- End of inner exception stack trace ---
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolveTypeName(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, String qualifiedTypeName)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadMetadataProperties(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue, Object& newValue, String& id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateDictionary(IDictionary dictionary, JsonReader reader, JsonDictionaryContract contract, JsonProperty containerProperty, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateDictionary(IDictionary dictionary, JsonReader reader, JsonDictionaryContract contract, JsonProperty containerProperty, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at BizHawk.Client.Common.ConfigService.Load[T](String filepath) in E:\LargeGithubProjects\BizHawk\src\BizHawk.Client.Common\config\ConfigService.cs:line 97
   --- End of inner exception stack trace ---
   at BizHawk.Client.Common.ConfigService.Load[T](String filepath) in E:\LargeGithubProjects\BizHawk\src\BizHawk.Client.Common\config\ConfigService.cs:line 102
   at BizHawk.Client.EmuHawk.Program.SubMain(String[] args)

Host env.

  • BizHawk dev build at 070e703; Win10 Pro 21H1; Intel/NVIDIA

Metadata

Metadata

Assignees

Labels

App: EmuHawkRelating to EmuHawk frontendRepro: Affects 2.9 devNeeds to be re-triagedReproducible bugShould only be added to issues with a `Repro: Affects` label.re: APIHawkRelating to EmuHawk's public .NET API or to the creation of external tools

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions