public static void Scan_Materials(InventorySection section, ref HashSet <Material> materials)
        {
            if (!materials.Contains(new Material("Mora", 0)))
            {
                materials.Add(new Material("Mora", ScanMora()));
            }

            int scrollCount = 0;

            Material material         = new Material(null, 0);
            Material previousMaterial = new Material(null, -1);

            List <Rectangle> rectangles;
            int page = 0;

            // Keep scanning while not repeating any items names
            while (true)
            {
                int rows, cols;
                // Find all items on the screen
                (rectangles, cols, rows) = GetPageOfItems(section, page);

                // Remove last row. Sometimes the bottom of a page of items is caught which results
                // in a faded quantity that can't be parsed. Removing slightly increases the number of pages that
                // need to be scrolled but it's fine.
                var r = rectangles.Take(rectangles.Count() - cols).ToList();

                foreach (var rectangle in r)
                {
                    // Select Material
                    Navigation.SetCursorPos(Navigation.GetPosition().Left + rectangle.Center().X, Navigation.GetPosition().Top + rectangle.Center().Y);
                    Navigation.Click();
                    Navigation.SystemRandomWait(Navigation.Speed.SelectNextInventoryItem);

                    material.name  = ScanMaterialName(section, out Bitmap nameplate);
                    material.count = 0;

                    // Check if new material has been found
                    if (materials.Contains(material))
                    {
                        goto LastPage;
                    }
                    else
                    {
                        if (!string.IsNullOrEmpty(material.name))
                        {
                            // Scan Material Number
                            material.count = ScanMaterialCount(rectangle, out Bitmap quantity);
                            if (material.count == 0)
                            {
                                UserInterface.AddError($"Failed to parse quantity for {material.name}");
                                quantity.Save($"./logging/materials/{material.name}_Quantity.png");
                            }
                            materials.Add(material);
                            UserInterface.ResetCharacterDisplay();
                            UserInterface.SetMaterial(nameplate, quantity, material.name, material.count);

                            previousMaterial.name = material.name;
                        }
                    }
                    nameplate.Dispose();
                    Navigation.Wait(150);
                }

                Navigation.SetCursorPos(Navigation.GetPosition().Left + r.Last().Center().X, Navigation.GetPosition().Top + r.Last().Center().Y);
                Navigation.Click();
                Navigation.Wait(150);
                // Scroll to next page
                for (int i = 0; i < rows - 1; i++)
                {
                    scrollCount++;

                    // scroll down
                    for (int k = 0; k < 10; k++)
                    {
                        Navigation.sim.Mouse.VerticalScroll(-1);
                        // skip a scroll
                        if ((k == 7) && ((scrollCount % 3) == 0))
                        {
                            k++;
                            if (scrollCount % 9 == 0)
                            {
                                if (scrollCount == 18)
                                {
                                    scrollCount = 0;
                                }
                                else
                                {
                                    Navigation.sim.Mouse.VerticalScroll(-1);
                                }
                            }
                        }
                        Navigation.SystemRandomWait(Navigation.Speed.InventoryScroll);
                    }
                }
                Navigation.SystemRandomWait(Navigation.Speed.Normal);
                ++page;
            }

LastPage:
            // scroll down as much as possible
            for (int i = 0; i < 20; i++)
            {
                Navigation.sim.Mouse.VerticalScroll(-1);
                Navigation.SystemRandomWait(Navigation.Speed.InventoryScroll);
            }

            Navigation.Wait(500);

            (rectangles, _, _) = GetPageOfItems(section, page);
            bool passby = true;

            for (int i = rectangles.Count - 1; i >= 0; i--)             // Click through but backwards to short-circuit after new materials
            {
                // Select Material
                Rectangle rectangle = rectangles[i];
                Navigation.SetCursorPos(Navigation.GetPosition().Left + rectangle.Center().X, Navigation.GetPosition().Top + rectangle.Center().Y);
                Navigation.Click();
                Navigation.SystemRandomWait(Navigation.Speed.SelectNextInventoryItem);

                material.name  = ScanMaterialName(section, out Bitmap nameplate);
                material.count = 0;

                if (materials.Contains(material) && passby)
                {
                    continue;
                }

                if (!materials.Contains(material))
                {
                    if (!string.IsNullOrEmpty(material.name))
                    {
                        // Scan Material Number
                        material.count = ScanMaterialCount(rectangle, out Bitmap quantity);
                        if (material.count == 0)
                        {
                            UserInterface.AddError($"Failed to parse quantity for {material.name}");
                            quantity.Save($"./logging/materials/{material.name}_Quantity.png");
                        }
                        materials.Add(material);
                        UserInterface.ResetCharacterDisplay();
                        UserInterface.SetMaterial(nameplate, quantity, material.name, material.count);
                        passby = false;                         // New material found so break on next old material
                        quantity.Dispose();
                    }
                }
                else
                {
                    nameplate.Dispose();
                    break;
                }
                Navigation.Wait(150);
            }
        }