Batman: Arkham Asylum - Unhandled Exception

Batman: Arkham Asylum (Game Image)
Batman: Arkham Asylum is a 2009 action-adventure game developed by Rocksteady Studios and published by Eidos Interactive in conjunction with Warner Bros. Interactive Entertainment.

Like many people, I played this game back in the day and, for some reason, I decided that I wanted to play it again...
Things didn't go as planned.
I installed the game from my Steam account and the game installed successfully, without any errors or other mishaps. When I went to run it, though, I got a very ugly error message stating: "Unhandled exception has occurred in your application. (...) Value as either too large or too small for an Int32."
As this is a game that was (or is) played by several million people, I did a search for possible solutions and found a few:
So I decided to dig deeper.
First I took a good look at the erros message: it was a simple overflow error. So, somewhere in the starter was a value that was, as the message stated, too large or too small for an Int32.
An Int32 value range is from -2,147,483,648 to +2,147,483,647. Which is a pretty large range, but not even close to an Int64, whose range goes from -9,223,372,036,854,775,808 to +9,223,372,036,854,775,807.
But how to discover where and what value was blowing up the game I wanted to play?
Asking for the details of the error I was greeted by the usual .NET Framework unhandled exception stack trace:
See the end of this message for details on invoking 
just-in-time (JIT) debugging instead of this dialog box.

************** Exception Text **************
System.OverflowException: Value was either too large or too small for an Int32.
   at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
   at BmLauncher.SystemInfo.GetPropertyInt(ArrayList info, String propertyName, Int32 property_index)
   at BmLauncher.SystemInfo.GetPropertyIntBest(ArrayList info, String propertyName, Boolean select_highest)
   at BmLauncher.SystemInfo..ctor()
   at BmLauncher.Form1.Initialise()
   at BmLauncher.Form1.OnLoad(Object sender, EventArgs e)
   at System.Windows.Forms.Form.OnLoad(EventArgs e)
   at System.Windows.Forms.Form.OnCreateControl()
   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
   at System.Windows.Forms.Control.CreateControl()
   at System.Windows.Forms.Control.WmShowWindow(Message& m)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
   at System.Windows.Forms.ContainerControl.WndProc(Message& m)
   at System.Windows.Forms.Form.WmShowWindow(Message& m)
   at System.Windows.Forms.Form.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
Well, it was a .NET application, which means all the usual debugging methods can be done with it. So, I used JustDecompile by Telerik to open up the file and take a look at what was going on.
The error seemed to occur from a call at BmLauncher.SystemInfo.GetPropertyInt. But I couldn't see anything wrong with the code itself (and, face it, I wouldn't -- even if it was blowing up at run time).
The error was occurring during the initialization of the BmLauncher.SystemInfo class. Therefore, I took the whole decompiled class and brought it to the Visual Studio 2022 to debug it property.
The class compiled without errors (although there were a few warnings, after all we're talking about a code that is at least a decade old compiling on a modern compiler -- even if I set up the correct framework [2.0, for those who want to know]). And in executing it the error popped up almost immediately.
The aforementioned class, or at least the part of the code that is relevant for this post, is below:
public class SystemInfo
{
    // ...

    public SystemInfo()
    {
        // ...
        int num2 = 0;
        // ...
        this.vidInfo = this.GetInfo("Win32_VideoController", ref num2);
        // ...
        if (this.vidInfo != null)
        {
            this.vidTotalMem = this.GetPropertyIntBest(this.vidInfo, "AdapterRAM", true);
            this.gpuName = this.GetPropertyString(this.vidInfo, "Caption");
        }
        // ...
    }
    
    // ...

    public ArrayList GetInfo(string queryObject, ref int count)
    {
        int num = 0;
        ArrayList arrayLists = new ArrayList();
        count = 0;
        try
        {
            foreach (ManagementObject managementObject in (new ManagementObjectSearcher(string.Concat("SELECT * FROM ", queryObject))).Get())
            {
                count++;
                num++;
                foreach (PropertyData property in managementObject.Properties)
                {
                    arrayLists.Add(property);
                }
            }
        }
        catch (Exception exception)
        {
            MessageBox.Show(exception.ToString());
        }
        return arrayLists;
    }
    
    // ...

    public int GetPropertyInt(ArrayList info, string propertyName)
    {
        return this.GetPropertyInt(info, propertyName, 0);
    }

    public int GetPropertyInt(ArrayList info, string propertyName, int property_index)
    {
        int num;
        string propertyString = this.GetPropertyString(info, propertyName, property_index);
        if (propertyString != null)
        {
            try
            {
                num = int.Parse(propertyString);
            }
            catch (FormatException)
            {
                return -1;
            }
            return num;
        }
        return -1;
    }

    public int GetPropertyIntBest(ArrayList info, string propertyName, bool select_highest)
    {
        int num = 0;
        int num1 = 0;
        foreach (PropertyData propertyDatum in info)
        {
            int propertyInt = this.GetPropertyInt(info, propertyName, num1);
            if (num1 != 0)
            {
                if ((select_highest ? propertyInt <= num : propertyInt >= num))
                {
                    goto Label0;
                }
            }
            num = propertyInt;
        Label0:
            num1++;
        }
        return num;
    }

    public string GetPropertyString(ArrayList info, string propertyName)
    {
        return this.GetPropertyString(info, propertyName, 0);
    }

    public string GetPropertyString(ArrayList info, string propertyName, int property_index)
    {
        string str;
        int num = 0;
        IEnumerator enumerator = info.GetEnumerator();
        try
        {
            while (enumerator.MoveNext())
            {
                PropertyData current = (PropertyData)enumerator.Current;
                if (current.Name.ToUpper().CompareTo(propertyName.ToUpper()) != 0)
                {
                    continue;
                }
                if (num != property_index || current.Value == null)
                {
                    num++;
                }
                else
                {
                    str = current.Value.ToString();
                    return str;
                }
            }
            return null;
        }
        finally
        {
            IDisposable disposable = enumerator as IDisposable;
            if (disposable != null)
            {
                disposable.Dispose();
            }
        }
        return str;
    }
    
    // ...
}
If we follow the code above, there is a call to GetInfo, which in turn calls ManagementObjectSearcher that returns a list of information regarding the specified query. In this case about the Win32_VideoController.
Our culprit, in this little whodunnit, is the AdapterRAM value, which I imagine it to be the amount that the video card has. In my machine it returns the value "2210398208", because of the amount of RAM in my video interface.
Currently, I'm using the onboard video card -- an Intel HD 2000, if you want to know -- with about 2 GB of RAM. 
As we can see, 2,210,398,208 is larger than the maximum of 2,147,483,647 allowed by an Int32, provoking the overflow exception.
Unfortunately, practically, there is no workaround.
This code needs to be tweaked at the source to change from Int32 to Int64. Something I'm not sure  Rocksteady would be interested in doing.
I will see, as the GPU I'm using is onboard, if I can change the amount of memory dedicated to it. If I can and I can lower it below the Int32 threshold, this should do the trick.

Comments