fire-eggs / CivOne

An open source implementation of Sid Meier's Civilization 1 using C# and .NET.
Creative Commons Zero v1.0 Universal
21 stars 4 forks source link

Better recovery with problem icons (was Creating world: `The given key '1699' was not present in the dictionary.`) #194

Open misterbeebee opened 2 years ago

misterbeebee commented 2 years ago

After launching the game and choosing nation, number of AI, etc, the app crashes with this exception.

1) I suspect it's something to do with failing to load the original Civ art assets, but I don't see where to input the path to assets -- I think the app prompted for that once in the past, and I don't know where to change it.

2) If there is a known cause for this error, it would be good if the error message gave useful advice..

The error is in SetIcon, trying to load data from LZW-compressed image file:

        protected void SetIcon(char page, int col, int row)
        {
            if (_iconCache[(int)Type] == null)
            {
--->                _iconCache[(int)Type] = Resources[$"ICONPG{page}"][col * 160, row * 62, 160, 60]
                    .ColourReplace((byte)(GFX256 ? 253 : 15), 0);
            }
            Icon = _iconCache[(int)Type];
        }
Unhandled Exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Collections.Generic.KeyNotFoundException:
The given key '1699' was not present in the dictionary.
   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at CivOne.IO.LZW.Decode(Byte[] input, Boolean clearEnd, Boolean flushDictionary, Int32 minBits, Int32 maxBits) in E:\proj\CivOneMaster\src\IO\LZW.cs:line 81
   at CivOne.Graphics.ImageFormats.PicFile.DecodePicture(Int32& index, UInt32 length) in E:\proj\CivOneMaster\src\Graphics\ImageFormats\PicFile.cs:line 124
   at CivOne.Graphics.ImageFormats.PicFile.ReadPictureX0(Int32& index) in E:\proj\CivOneMaster\src\Graphics\ImageFormats\PicFile.cs:line 138
   at CivOne.Graphics.ImageFormats.PicFile..ctor(String filename) in E:\proj\CivOneMaster\src\Graphics\ImageFormats\PicFile.cs:line 333
   at CivOne.Graphics.Resources.get_Item(String filename) in E:\proj\CivOneMaster\src\Graphics\Resources.cs:line 207
   at CivOne.Units.BaseUnit.SetIcon(Char page, Int32 col, Int32 row) in E:\proj\CivOneMaster\src\Units\BaseUnit.cs:line 862
   --- End of inner exception stack trace ---
   at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean wrapExceptions, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor)
   at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean wrapExceptions, Boolean skipCheckThis, Boolean fillCache)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic, Boolean wrapExceptions)
   at CivOne.Reflect.GetTypes[T]()+MoveNext() in E:\proj\CivOneMaster\src\Reflect.cs:line 73
   at System.Linq.OrderedEnumerable`1.TryGetFirst(Func`2 predicate, Boolean& found)
   at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Func`2 predicate, Boolean& found)
   at CivOne.SaveDataAdapter.GetUnitType(Byte typeId) in E:\proj\CivOneMaster\src\SaveDataAdapter.cs:line 27
   at System.Linq.Enumerable.SelectIPartitionIterator`2.PreallocatingToArray(Int32 count)
   at CivOne.SaveDataAdapter..ctor() in E:\proj\CivOneMaster\src\SaveDataAdapter.cs:line 330
   at CivOne.Common.get_AllowSaveGame() in E:\proj\CivOneMaster\src\Common.cs:line 355
   at CivOne.Screens.GamePlay..ctor() in E:\proj\CivOneMaster\src\Screens\GamePlay.cs:line 413
   at CivOne.Screens.NewGame.HasUpdate(UInt32 gameTick) in E:\proj\CivOneMaster\src\Screens\NewGame.cs:line 238
   at CivOne.RuntimeHandler.Update() in E:\proj\CivOneMaster\src\RuntimeHandler.cs:line 63
   at CivOne.RuntimeHandler.OnUpdate(Object sender, UpdateEventArgs args) in E:\proj\CivOneMaster\src\RuntimeHandler.cs:line 93
   at CivOne.GameWindow.Update(Object sender, EventArgs args) in E:\proj\CivOneMaster\runtime\sdl\src\GameWindow.cs:line 40
   at CivOne.SDL.Window.Run() in E:\proj\CivOneMaster\runtime\sdl\src\SDL\Window.cs:line 96
   at CivOne.Program.Main(String[] args) in E:\proj\CivOneMaster\runtime\sdl\src\Program.cs:line 89
misterbeebee commented 2 years ago

I found that the original Civ assets are copied/stored in

C:\Users\[username]\AppData\Local\CivOne

and the data there seems fine, so the problem isn't that the assets are just missing.

misterbeebee commented 2 years ago

I deleted C:\Users\[username]\AppData\Local\CivOne and re-ran the app. It prompted me to copy data files from original source, which I did, and now the app works.

So the problem must have been that the data got corrupted somehow.

So, the request here is to catch the exception at a reasonable place (maybe CivOne.Graphics.Resources.get_Item()), and offer troubleshooting advice: "Delete IRuntime.StorageDirectory and restart app"

misterbeebee commented 2 years ago

No, it's worse. The corruption happens every time I quit and restart the app.

So, if I delete AppData\Local\CivOne and launch the app, it prompts to copy data files, copies, and starts the game. The game plays fine, then I quit. I run the app again: the new-game options (# of players, etc), the Customize World screen, and the Load Saved Game screen, all work fine.

But the game crashes when it tries to show the game map main screen where I interact with the units and cities.

fire-eggs commented 2 years ago

So you're still having the same problem as per the original repo, issue #507 ? Does your workaround from that issue help this issue here?

misterbeebee commented 2 years ago

Ha! You know me better than I know myself. I forgot about #507... And I was better set up for debugging back in 2020.

I'm on my phone now, but I'm confident the same workaround from then on Mac will work now on Windows.

(I'd still leave the issue open for a usability enhancement for handling corrupt data files.)

misterbeebee commented 2 years ago

Oh, and it's also interesting that the bad ICONPGA.PIC file is corrupt in CivOne but not in original Civ under DOSBox.

I'm curious if I have a different ICONPGA.PIC from other people.

This is the file I have: https://drive.google.com/file/d/19uho8XdfgRxrE4Whpb7VXOZskmahFDml/view?usp=drivesdk

fire-eggs commented 2 years ago

(I'd still leave the issue open for a usability enhancement for handling corrupt data files.)

Absolutely.

Here's my ICONPGA.PIC file. There is a 5 byte difference, starting at byte 5016, between mine and yours.

ICONPGA.PIC.zip