public static              PatternByte[] Parse(string pattern)
        {
            //check pattern
            if (pattern.Split(' ').Any(a => a.Length % 2 != 0))
            {
                throw new Exception("Bad pattern");
            }

            //TODO: first split by spaces, in case user passes "FF 3 23 0"
            string noSpaces = pattern.Replace(" ", string.Empty);

            if (noSpaces.Length % 2 != 0)
            {
                throw new Exception("Bad pattern");
            }

            int byteCount = noSpaces.Length / 2;
            var arr       = new PatternByte[byteCount];

            for (int i = 0; i < byteCount; i++)
            {
                arr[i] = byte.TryParse(noSpaces.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out byte b)
                    ? Normal(b)
                    : Wildcard;
            }

            return(arr);
        }
        /// <summary>
        /// Scans a memory section for a pattern.
        /// </summary>
        /// <param name="handle">A handle to the process</param>
        /// <param name="pattern">The pattern array to scan for</param>
        /// <param name="baseAddress">The address to start at</param>
        /// <param name="size">The size of the region to scan</param>
        /// <param name="result">The address, or <see cref="IntPtr.Zero"/> on failure</param>
        /// <returns>Whether the scan was successful or not</returns>
        public static bool Scan(IntPtr handle, PatternByte[] pattern, IntPtr baseAddress, int size, out IntPtr result)
        {
            //TODO: ready for 64-bit
            uint step = (uint)Math.Min(size, ScanStep);
            uint min  = (uint)baseAddress.ToInt32();
            uint max  = (uint)(min + size);

            byte[] buffer = new byte[step + pattern.Length - 1];

            //skip wildcards, since they would always match
            uint firstNonWildcard = PatternByte.FirstNonWildcardByte(pattern);

            var sw = new Stopwatch();

            sw.Start();
            for (uint i = min; i < max; i += step)
            {
                //read buffer
                //TODO: limit to not go outside region?
                Native.ReadProcessMemory(handle, (IntPtr)i, buffer, buffer.Length, out _);

                //loop through buffer
                for (uint j = 0; j < step; ++j)
                {
                    bool match = true;

                    //loop through pattern
                    for (uint k = firstNonWildcard; k < pattern.Length; ++k)
                    {
                        if (pattern[k].Match(buffer[j + k]))
                        {
                            continue;
                        }
                        match = false;
                        break;
                    }

                    if (match)
                    {
                        result = (IntPtr)(i + j);
                        sw.Stop();
                        Debug.WriteLine("Stopwatch sigscan: " + sw.Elapsed);
                        return(true);
                    }
                }
            }

            result = IntPtr.Zero;
            return(false);
        }