public MagicManager(Process process, int[] romPtrBaseSuggestions, int[] ramPtrBaseSuggestions, int offset)
        {
            GC.Collect();
            this.process = process;

            bool isRomFound = false;
            bool isRamFound = false;

            foreach (uint romPtrBaseSuggestion in romPtrBaseSuggestions)
            {
                romPtrBase = romPtrBaseSuggestion;
                if (IsRomBaseValid())
                {
                    isRomFound = true;
                    break;
                }
            }

            foreach (uint ramPtrBaseSuggestion in ramPtrBaseSuggestions)
            {
                ramPtrBase = ramPtrBaseSuggestion;
                if (IsRamBaseValid())
                {
                    isRamFound = true;
                    break;
                }
            }

            long MaxAddress = 0xffffffff;
            long address    = 0;

            do
            {
                if (isRomFound && isRamFound)
                {
                    break;
                }

                MEMORY_BASIC_INFORMATION m;
                int result = VirtualQueryEx(process.Handle, new UIntPtr((uint)address), out m, (uint)Marshal.SizeOf(typeof(MEMORY_BASIC_INFORMATION)));
                if (address == (long)m.BaseAddress + (long)m.RegionSize || result == 0)
                {
                    break;
                }

                if (m.AllocationProtect != 0)
                {
                    bool readSuccess = process.ReadValue(new IntPtr(address + offset), out uint value);
                    if (readSuccess)
                    {
                        if (!isRamFound && ((value & ramMagicMask) == ramMagic))
                        {
                            ramPtrBase = (uint)(address + offset);
                            isRamFound = true;
                        }

                        if (!isRomFound && value == romMagic)
                        {
                            romPtrBase = (uint)(address + offset);
                            isRomFound = true;
                        }
                    }
                }

                address = (long)m.BaseAddress + (long)m.RegionSize;
            }while (address <= MaxAddress);

            if (!isRomFound || !isRamFound)
            {
                throw new ArgumentException("Failed to find rom and ram!");
            }

            uint[] mem;
            {
                byte[] bytes = process.ReadBytes(new IntPtr(ramPtrBase), 0x400000);
                int    size  = bytes.Count() / 4;
                mem = new uint[size];
                for (int idx = 0; idx < size; idx++)
                {
                    mem[idx] = BitConverter.ToUInt32(bytes, 4 * idx);
                }
            }

            DecompManager dm = new DecompManager(mem);

            if (!dm.gSaveBuffer.HasValue)
            {
                throw new ArgumentException("Failed to gSaveBuffer!");
            }

            saveBufferOffset   = dm.gSaveBuffer.Value & 0xffffff;
            saveFileSize       = dm.gSaveFileSize.Value;
            verificationBytes  = dm.VerificationBytes;
            verificationOffset = dm.VerificationOffset.Value;

            isDecomp = saveBufferOffset != 0x207700; // TODO: This is inaccurate
        }
        public MagicManager(Process process, long[] romPtrBaseSuggestions, long[] ramPtrBaseSuggestions, int offset, bool exScan)
        {
            GC.Collect();
            this.process = process;

            bool isRomFound = false;
            bool isRamFound = false;

            foreach (uint romPtrBaseSuggestion in romPtrBaseSuggestions)
            {
                romPtrBase = romPtrBaseSuggestion;
                if (IsRomBaseValid())
                {
                    isRomFound = true;
                    break;
                }
            }

            foreach (uint ramPtrBaseSuggestion in ramPtrBaseSuggestions)
            {
                ramPtrBase = ramPtrBaseSuggestion;
                if (IsRamBaseValid())
                {
                    isRamFound = true;
                    break;
                }
            }

            ulong parallelStart = 0;
            ulong parallelEnd   = 0;

            foreach (ProcessModule module in process.Modules)
            {
                if (module.ModuleName.Contains("parallel_n64"))
                {
                    parallelStart = (ulong)module.BaseAddress;
                    parallelEnd   = parallelStart + (ulong)module.ModuleMemorySize;
                }
            }

            ulong MaxAddress = process.Is64Bit() ? 0x800000000000U : 0xffffffffU;
            ulong address    = 0;

            do
            {
                if (isRomFound && isRamFound)
                {
                    break;
                }

                MEMORY_BASIC_INFORMATION m;
                int result = VirtualQueryEx(process.Handle, new UIntPtr(address), out m, (uint)Marshal.SizeOf(typeof(MEMORY_BASIC_INFORMATION)));
                if (address == (ulong)m.BaseAddress + (ulong)m.RegionSize || result == 0)
                {
                    break;
                }

                AllocationProtect prot = (AllocationProtect)(m.Protect & 0xff);
                if (prot == AllocationProtect.PAGE_EXECUTE_READWRITE ||
                    prot == AllocationProtect.PAGE_EXECUTE_WRITECOPY ||
                    prot == AllocationProtect.PAGE_READWRITE ||
                    prot == AllocationProtect.PAGE_WRITECOPY ||
                    prot == AllocationProtect.PAGE_READONLY)
                {
                    uint value;
                    bool readSuccess = process.ReadValue(new IntPtr((long)(address + (ulong)offset)), out value);
                    if (readSuccess)
                    {
                        if (!isRamFound && ((value & ramMagicMask) == ramMagic))
                        {
                            ramPtrBase = address + (ulong)offset;
                            isRamFound = true;
                        }

                        if (!isRomFound && value == romMagic)
                        {
                            romPtrBase = address + (ulong)offset;
                            isRomFound = true;
                        }
                    }

                    // scan only large regions - we want to find g_rdram
                    ulong regionSize = (ulong)m.RegionSize;
                    if (parallelStart <= address && address <= parallelEnd && regionSize >= 0x800000)
                    {
                        // g_rdram is aligned to 0x1000
                        ulong maxCnt = (ulong)m.RegionSize / 0x1000;
                        for (ulong num = 0; num < maxCnt; num++)
                        {
                            readSuccess = process.ReadValue(new IntPtr((long)(address + num * 0x1000)), out value);
                            if (readSuccess)
                            {
                                if (!isRamFound && ((value & ramMagicMask) == ramMagic))
                                {
                                    ramPtrBase = address + num * 0x1000;
                                    isRamFound = true;
                                }
                            }

                            if (isRamFound)
                            {
                                break;
                            }
                        }
                    }
                }

                address = (ulong)m.BaseAddress + (ulong)m.RegionSize;
            }while (address <= MaxAddress);

            if (!isRomFound || !isRamFound)
            {
                throw new ArgumentException("Failed to find rom and ram!");
            }

            uint[] mem;
            {
                byte[] bytes = process.ReadBytes(new IntPtr((long)ramPtrBase), 0x400000);
                int    size  = bytes.Count() / 4;
                mem = new uint[size];
                for (int idx = 0; idx < size; idx++)
                {
                    mem[idx] = BitConverter.ToUInt32(bytes, 4 * idx);
                }
            }

            DecompManager dm = new DecompManager(mem);

            if (!dm.gSaveBuffer.HasValue)
            {
                throw new ArgumentException("Failed to gSaveBuffer!");
            }

            saveBufferOffset   = dm.gSaveBuffer.Value & 0xffffff;
            saveFileSize       = dm.gSaveFileSize.Value;
            verificationBytes  = dm.VerificationBytes;
            verificationOffset = dm.VerificationOffset.Value;

            isDecomp = saveBufferOffset != 0x207700; // TODO: This is inaccurate
        }