Exemplo n.º 1
0
        public Gameboy(CoreComm comm, GameInfo game, byte[] file, object Settings, object SyncSettings, bool deterministic)
        {
            var ser = new BasicServiceProvider(this);

            ser.Register <IDisassemblable>(new GBDisassembler());
            ServiceProvider = ser;
            Tracer          = new TraceBuffer
            {
                Header = "Z80: PC, opcode, registers (A, B, C, D, E, F, H, L, LY, SP, CY)"
            };
            ser.Register <ITraceable>(Tracer);
            InitMemoryCallbacks();
            CoreComm = comm;

            comm.VsyncNum            = 262144;
            comm.VsyncDen            = 4389;
            comm.RomStatusAnnotation = null;
            comm.RomStatusDetails    = null;
            comm.NominalWidth        = 160;
            comm.NominalHeight       = 144;

            ThrowExceptionForBadRom(file);
            BoardName = MapperName(file);

            DeterministicEmulation = deterministic;

            GambatteState = LibGambatte.gambatte_create();

            if (GambatteState == IntPtr.Zero)
            {
                throw new InvalidOperationException("gambatte_create() returned null???");
            }

            try
            {
                this._syncSettings = (GambatteSyncSettings)SyncSettings ?? new GambatteSyncSettings();
                // copy over non-loadflag syncsettings now; they won't take effect if changed later
                zerotime      = (uint)this._syncSettings.RTCInitialTime;
                real_rtc_time = DeterministicEmulation ? false : this._syncSettings.RealTimeRTC;

                LibGambatte.LoadFlags flags = 0;

                if (this._syncSettings.ForceDMG)
                {
                    flags |= LibGambatte.LoadFlags.FORCE_DMG;
                }
                if (this._syncSettings.GBACGB)
                {
                    flags |= LibGambatte.LoadFlags.GBA_CGB;
                }
                if (this._syncSettings.MulticartCompat)
                {
                    flags |= LibGambatte.LoadFlags.MULTICART_COMPAT;
                }

                if (LibGambatte.gambatte_load(GambatteState, file, (uint)file.Length, GetCurrentTime(), flags) != 0)
                {
                    throw new InvalidOperationException("gambatte_load() returned non-zero (is this not a gb or gbc rom?)");
                }

                // set real default colors (before anyone mucks with them at all)
                PutSettings((GambatteSettings)Settings ?? new GambatteSettings());

                InitSound();

                Frame      = 0;
                LagCount   = 0;
                IsLagFrame = false;

                InputCallback = new LibGambatte.InputGetter(ControllerCallback);

                LibGambatte.gambatte_setinputgetter(GambatteState, InputCallback);

                InitMemoryDomains();

                CoreComm.RomStatusDetails = string.Format("{0}\r\nSHA1:{1}\r\nMD5:{2}\r\n",
                                                          game.Name,
                                                          file.HashSHA1(),
                                                          file.HashMD5());

                {
                    byte[] buff = new byte[32];
                    LibGambatte.gambatte_romtitle(GambatteState, buff);
                    string romname = System.Text.Encoding.ASCII.GetString(buff);
                    Console.WriteLine("Core reported rom name: {0}", romname);
                }

                TimeCallback = new LibGambatte.RTCCallback(GetCurrentTime);
                LibGambatte.gambatte_setrtccallback(GambatteState, TimeCallback);

                CDCallback = new LibGambatte.CDCallback(CDCallbackProc);

                NewSaveCoreSetBuff();
            }
            catch
            {
                Dispose();
                throw;
            }
        }
Exemplo n.º 2
0
        public Gameboy(CoreComm comm, GameInfo game, byte[] file, object settings, object syncSettings, bool deterministic)
        {
            var ser = new BasicServiceProvider(this);

            ser.Register <IDisassemblable>(new GBDisassembler());
            ServiceProvider = ser;
            Tracer          = new TraceBuffer
            {
                Header = "Z80: PC, opcode, registers (A, B, C, D, E, F, H, L, LY, SP, CY)"
            };
            ser.Register <ITraceable>(Tracer);
            InitMemoryCallbacks();
            CoreComm = comm;

            comm.RomStatusAnnotation = null;
            comm.RomStatusDetails    = null;
            comm.NominalWidth        = 160;
            comm.NominalHeight       = 144;

            ThrowExceptionForBadRom(file);
            BoardName = MapperName(file);

            DeterministicEmulation = deterministic;

            GambatteState = LibGambatte.gambatte_create();

            if (GambatteState == IntPtr.Zero)
            {
                throw new InvalidOperationException("gambatte_create() returned null???");
            }

            Console.WriteLine(game.System);

            byte[] BiosRom;

            if (game.System == "GB")
            {
                BiosRom = comm.CoreFileProvider.GetFirmware("GB", "World", false);
            }
            else
            {
                BiosRom = comm.CoreFileProvider.GetFirmware("GBC", "World", false);
            }

            int bios_length = BiosRom == null ? 0 : BiosRom.Length;

            try
            {
                _syncSettings = (GambatteSyncSettings)syncSettings ?? new GambatteSyncSettings();

                // copy over non-loadflag syncsettings now; they won't take effect if changed later
                zerotime = (uint)_syncSettings.RTCInitialTime;

                real_rtc_time = !DeterministicEmulation && _syncSettings.RealTimeRTC;

                LibGambatte.LoadFlags flags = 0;

                switch (_syncSettings.ConsoleMode)
                {
                case GambatteSyncSettings.ConsoleModeType.GB:
                    flags |= LibGambatte.LoadFlags.FORCE_DMG;
                    // we need to change the BIOS to GB bios
                    if (game.System == "GBC")
                    {
                        BiosRom     = comm.CoreFileProvider.GetFirmware("GB", "World", false);
                        bios_length = BiosRom.Length;
                    }
                    break;

                case GambatteSyncSettings.ConsoleModeType.GBC:
                    BiosRom     = comm.CoreFileProvider.GetFirmware("GBC", "World", false);
                    bios_length = BiosRom.Length;
                    break;

                default:
                    if (game.System == "GB")
                    {
                        flags |= LibGambatte.LoadFlags.FORCE_DMG;
                    }
                    break;
                }

                if (_syncSettings.EnableBIOS && BiosRom == null)
                {
                    throw new MissingFirmwareException("Boot Rom not found");
                }

                // to disable BIOS loading into gambatte, just set bios_length to 0
                if (!_syncSettings.EnableBIOS)
                {
                    bios_length = 0;
                }

                if (_syncSettings.GBACGB)
                {
                    flags |= LibGambatte.LoadFlags.GBA_CGB;
                }

                if (_syncSettings.MulticartCompat)
                {
                    flags |= LibGambatte.LoadFlags.MULTICART_COMPAT;
                }

                if (LibGambatte.gambatte_load(GambatteState, file, (uint)file.Length, BiosRom, (uint)bios_length, GetCurrentTime(), flags) != 0)
                {
                    throw new InvalidOperationException("gambatte_load() returned non-zero (is this not a gb or gbc rom?)");
                }

                // set real default colors (before anyone mucks with them at all)
                PutSettings((GambatteSettings)settings ?? new GambatteSettings());

                InitSound();

                Frame      = 0;
                LagCount   = 0;
                IsLagFrame = false;

                InputCallback = new LibGambatte.InputGetter(ControllerCallback);

                LibGambatte.gambatte_setinputgetter(GambatteState, InputCallback);

                InitMemoryDomains();

                CoreComm.RomStatusDetails = $"{game.Name}\r\nSHA1:{file.HashSHA1()}\r\nMD5:{file.HashMD5()}\r\n";

                byte[] buff = new byte[32];
                LibGambatte.gambatte_romtitle(GambatteState, buff);
                string romname = System.Text.Encoding.ASCII.GetString(buff);
                Console.WriteLine("Core reported rom name: {0}", romname);

                TimeCallback = new LibGambatte.RTCCallback(GetCurrentTime);
                LibGambatte.gambatte_setrtccallback(GambatteState, TimeCallback);

                _cdCallback = new LibGambatte.CDCallback(CDCallbackProc);

                NewSaveCoreSetBuff();
            }
            catch
            {
                Dispose();
                throw;
            }
        }
Exemplo n.º 3
0
        public Gameboy(CoreComm comm, GameInfo game, byte[] file, Gameboy.GambatteSettings settings, Gameboy.GambatteSyncSettings syncSettings, bool deterministic)
        {
            var ser = new BasicServiceProvider(this);

            ser.Register <IDisassemblable>(new GBDisassembler());
            ServiceProvider = ser;
            Tracer          = new TraceBuffer
            {
                Header = "Z80: PC, opcode, registers (A, B, C, D, E, F, H, L, LY, SP, CY)"
            };
            ser.Register <ITraceable>(Tracer);
            InitMemoryCallbacks();

            ThrowExceptionForBadRom(file);
            BoardName = MapperName(file);

            DeterministicEmulation = deterministic;

            GambatteState = LibGambatte.gambatte_create();

            if (GambatteState == IntPtr.Zero)
            {
                throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_create)}() returned null???");
            }

            Console.WriteLine(game.System);

            try
            {
                _syncSettings = (GambatteSyncSettings)syncSettings ?? new GambatteSyncSettings();

                LibGambatte.LoadFlags flags = 0;

                switch (_syncSettings.ConsoleMode)
                {
                case GambatteSyncSettings.ConsoleModeType.GB:
                    flags |= LibGambatte.LoadFlags.FORCE_DMG;
                    break;

                case GambatteSyncSettings.ConsoleModeType.GBC:
                    break;

                default:
                    if (game.System == "GB")
                    {
                        flags |= LibGambatte.LoadFlags.FORCE_DMG;
                    }
                    break;
                }

                if (_syncSettings.GBACGB)
                {
                    flags |= LibGambatte.LoadFlags.GBA_CGB;
                }

                if (_syncSettings.MulticartCompat)
                {
                    flags |= LibGambatte.LoadFlags.MULTICART_COMPAT;
                }

                if (LibGambatte.gambatte_load(GambatteState, file, (uint)file.Length, flags) != 0)
                {
                    throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_load)}() returned non-zero (is this not a gb or gbc rom?)");
                }

                byte[] bios;
                string biosName;
                if ((flags & LibGambatte.LoadFlags.FORCE_DMG) == LibGambatte.LoadFlags.FORCE_DMG)
                {
                    biosName = "GB";
                    IsCgb    = false;
                }
                else
                {
                    // TODO: Fix AGB bios mode stuff
                    // biosName = _syncSettings.GBACGB ? "AGB" : "GBC";
                    biosName = "GBC";
                    IsCgb    = true;
                }

                if (_syncSettings.EnableBIOS)
                {
                    bios = comm.CoreFileProvider.GetFirmware(biosName, "World", true, "BIOS Not Found, Cannot Load.  Change SyncSettings to run without BIOS.");
                }
                else
                {
                    Lazy <byte[]> builtinBios;
                    switch (biosName)
                    {
                    case "GB":
                        builtinBios = Resources.SameboyDmgBoot;
                        break;

                    case "GBC":
                        builtinBios = Resources.SameboyCgbBoot;
                        break;

                    // TODO: This doesn't work; locks up before leaving the bios screen
                    // case "AGB":
                    //  builtinBios = Resources.SameboyAgbBoot;
                    //  break;
                    default:
                        throw new Exception("Internal GB Error (BIOS??)");
                    }
                    bios = BizHawk.Common.Util.DecompressGzipFile(new MemoryStream(builtinBios.Value, false));
                }

                if (LibGambatte.gambatte_loadbios(GambatteState, bios, (uint)bios.Length) != 0)
                {
                    throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbios)}() returned non-zero (bios error)");
                }

                // set real default colors (before anyone mucks with them at all)
                PutSettings((GambatteSettings)settings ?? new GambatteSettings());

                InitSound();

                Frame      = 0;
                LagCount   = 0;
                IsLagFrame = false;

                InputCallback = new LibGambatte.InputGetter(ControllerCallback);

                LibGambatte.gambatte_setinputgetter(GambatteState, InputCallback);

                InitMemoryDomains();

                RomDetails = $"{game.Name}\r\nSHA1:{file.HashSHA1()}\r\nMD5:{file.HashMD5()}\r\n";

                byte[] buff = new byte[32];
                LibGambatte.gambatte_romtitle(GambatteState, buff);
                string romname = System.Text.Encoding.ASCII.GetString(buff);
                Console.WriteLine("Core reported rom name: {0}", romname);

                if (!DeterministicEmulation && _syncSettings.RealTimeRTC)
                {
                    LibGambatte.gambatte_settimemode(GambatteState, false);
                }
                LibGambatte.gambatte_setrtcdivisoroffset(GambatteState, _syncSettings.RTCDivisorOffset);

                _cdCallback = new LibGambatte.CDCallback(CDCallbackProc);

                NewSaveCoreSetBuff();
            }
            catch
            {
                Dispose();
                throw;
            }
        }
Exemplo n.º 4
0
        public Gameboy(CoreComm comm, GameInfo game, byte[] file, Gameboy.GambatteSettings settings, Gameboy.GambatteSyncSettings syncSettings, bool deterministic)
        {
            var ser = new BasicServiceProvider(this);

            ser.Register <IDisassemblable>(_disassembler);
            ServiceProvider = ser;
            const string TRACE_HEADER = "LR35902: PC, opcode, registers (A, F, B, C, D, E, H, L, LY, SP, CY)";

            Tracer = new TraceBuffer(TRACE_HEADER);
            ser.Register <ITraceable>(Tracer);
            InitMemoryCallbacks();

            ThrowExceptionForBadRom(file);
            BoardName = MapperName(file);

            DeterministicEmulation = deterministic;

            GambatteState = LibGambatte.gambatte_create();

            if (GambatteState == IntPtr.Zero)
            {
                throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_create)}() returned null???");
            }

            Console.WriteLine(game.System);

            try
            {
                _syncSettings = syncSettings ?? new GambatteSyncSettings();

                LibGambatte.LoadFlags flags = LibGambatte.LoadFlags.READONLY_SAV;

                switch (_syncSettings.ConsoleMode)
                {
                case GambatteSyncSettings.ConsoleModeType.GB:
                    break;

                case GambatteSyncSettings.ConsoleModeType.GBC:
                    flags |= LibGambatte.LoadFlags.CGB_MODE;
                    break;

                case GambatteSyncSettings.ConsoleModeType.GBA:
                    flags |= LibGambatte.LoadFlags.CGB_MODE | LibGambatte.LoadFlags.GBA_FLAG;
                    break;

                default:
                    if (game.System == "GBC")
                    {
                        flags |= LibGambatte.LoadFlags.CGB_MODE;
                    }
                    break;
                }

                if (game.System == "SGB")
                {
                    flags &= ~(LibGambatte.LoadFlags.CGB_MODE | LibGambatte.LoadFlags.GBA_FLAG);
                    flags |= LibGambatte.LoadFlags.SGB_MODE;
                    IsSgb  = true;
                }

                if (_syncSettings.MulticartCompat)
                {
                    flags |= LibGambatte.LoadFlags.MULTICART_COMPAT;
                }

                byte[] bios;
                string biosSystemId;
                string biosId;

                IsCgb        = (flags & LibGambatte.LoadFlags.CGB_MODE) == LibGambatte.LoadFlags.CGB_MODE;
                biosSystemId = IsCgb ? "GBC" : "GB";
                biosId       = (_syncSettings.ConsoleMode == GambatteSyncSettings.ConsoleModeType.GBA) && !_syncSettings.PatchBIOS ? "AGB" : "World";

                if (IsSgb)
                {
                    biosId = "SGB2";
                }

                if (_syncSettings.EnableBIOS)
                {
                    bios = comm.CoreFileProvider.GetFirmwareOrThrow(new(biosSystemId, biosId), "BIOS Not Found, Cannot Load.  Change SyncSettings to run without BIOS.");
                    if (_syncSettings.PatchBIOS)
                    {
                        if (!IsCgb)
                        {
                            bios[0xFD] ^= 0xFE;                             // patch from dmg<->mgb, or sgb1<->sgb2
                        }
                        else if (_syncSettings.ConsoleMode == GambatteSyncSettings.ConsoleModeType.GBA)
                        {
                            // patch from cgb->agb re
                            bios[0xF3] ^= 0x03;
                            for (var i = 0xF5; i < 0xFB; i++)
                            {
                                bios[i] = bios[i + 1];
                            }
                            bios[0xFB] ^= 0x74;
                        }
                    }
                    if (LibGambatte.gambatte_loadbiosbuf(GambatteState, bios, (uint)bios.Length) != 0)
                    {
                        throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbiosbuf)}() returned non-zero (bios error)");
                    }
                }
                else
                {
                    if (DeterministicEmulation)                     // throw a warning if a movie is being recorded with the bios disabled
                    {
                        comm.ShowMessage("Detected disabled BIOS during movie recording. It is recommended to use a BIOS for movie recording. Change Sync Settings to run with a BIOS.");
                    }
                    flags |= LibGambatte.LoadFlags.NO_BIOS;
                }

                if (LibGambatte.gambatte_loadbuf(GambatteState, file, (uint)file.Length, flags) != 0)
                {
                    throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbuf)}() returned non-zero (is this not a gb or gbc rom?)");
                }

                if (IsSgb)
                {
                    ResetStallTicks = 128 * (2 << 14);
                }
                else if (_syncSettings.EnableBIOS && (_syncSettings.ConsoleMode is GambatteSyncSettings.ConsoleModeType.GBA))
                {
                    ResetStallTicks = 485808;                     // GBA takes 971616 cycles to switch to CGB mode; CGB CPU is inactive during this time.
                }
                else
                {
                    ResetStallTicks = 0;
                }

                // set real default colors (before anyone mucks with them at all)
                PutSettings((GambatteSettings)settings ?? new GambatteSettings());

                InitSound();

                Frame      = 0;
                LagCount   = 0;
                IsLagFrame = false;

                InputCallback = new LibGambatte.InputGetter(ControllerCallback);

                LibGambatte.gambatte_setinputgetter(GambatteState, InputCallback);

                InitMemoryDomains();

                RomDetails = $"{game.Name}\r\nSHA1:{file.HashSHA1()}\r\nMD5:{file.HashMD5()}\r\n";

                byte[] buff = new byte[32];
                LibGambatte.gambatte_romtitle(GambatteState, buff);
                string romname = System.Text.Encoding.ASCII.GetString(buff);
                Console.WriteLine("Core reported rom name: {0}", romname);

                if (!_syncSettings.EnableBIOS && IsCgb && IsCGBDMGMode())                 // without a bios, we need to set the palette for cgbdmg ourselves
                {
                    int[] cgbDmgColors = new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 };
                    if (file[0x14B] == 0x01 || (file[0x14B] == 0x33 && file[0x144] == '0' && file[0x145] == '1'))                     // Nintendo licencees get special palettes
                    {
                        cgbDmgColors = ColorsFromTitleHash(file);
                    }
                    ChangeDMGColors(cgbDmgColors);
                }

                if (!DeterministicEmulation && _syncSettings.RealTimeRTC)
                {
                    LibGambatte.gambatte_settimemode(GambatteState, false);
                }

                if (DeterministicEmulation)
                {
                    int[] rtcRegs = new int[11];
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh] = 0;
                    if (_syncSettings.InternalRTCOverflow)
                    {
                        rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh] |= 0x80;
                    }
                    if (_syncSettings.InternalRTCHalt)
                    {
                        rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh] |= 0x40;
                    }
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh]  |= _syncSettings.InternalRTCDays >> 8;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dl]   = _syncSettings.InternalRTCDays & 0xFF;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.H]    = (_syncSettings.InternalRTCHours < 0) ? (_syncSettings.InternalRTCHours + 0x20) : _syncSettings.InternalRTCHours;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.M]    = (_syncSettings.InternalRTCMinutes < 0) ? (_syncSettings.InternalRTCMinutes + 0x40) : _syncSettings.InternalRTCMinutes;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.S]    = (_syncSettings.InternalRTCSeconds < 0) ? (_syncSettings.InternalRTCSeconds + 0x40) : _syncSettings.InternalRTCSeconds;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.C]    = _syncSettings.InternalRTCCycles;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh_L] = 0;
                    if (_syncSettings.LatchedRTCOverflow)
                    {
                        rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh_L] |= 0x80;
                    }
                    if (_syncSettings.LatchedRTCHalt)
                    {
                        rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh_L] |= 0x40;
                    }
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh_L] |= _syncSettings.LatchedRTCDays >> 8;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dl_L]  = _syncSettings.LatchedRTCDays & 0xFF;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.H_L]   = _syncSettings.LatchedRTCHours;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.M_L]   = _syncSettings.LatchedRTCMinutes;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.S_L]   = _syncSettings.LatchedRTCSeconds;
                    LibGambatte.gambatte_setrtcregs(GambatteState, rtcRegs);
                }

                LibGambatte.gambatte_setrtcdivisoroffset(GambatteState, _syncSettings.RTCDivisorOffset);

                LibGambatte.gambatte_setcartbuspulluptime(GambatteState, _syncSettings.CartBusPullUpTime);

                _cdCallback = new LibGambatte.CDCallback(CDCallbackProc);

                ControllerDefinition = CreateControllerDefinition(IsSgb, _syncSettings.FrameLength is GambatteSyncSettings.FrameLengthType.UserDefinedFrames);

                NewSaveCoreSetBuff();
            }
            catch
            {
                Dispose();
                throw;
            }
        }
Exemplo n.º 5
0
        public Gameboy(CoreComm comm, GameInfo game, byte[] file, Gameboy.GambatteSettings settings, Gameboy.GambatteSyncSettings syncSettings, bool deterministic)
        {
            var ser = new BasicServiceProvider(this);

            ser.Register <IDisassemblable>(new GBDisassembler());
            ServiceProvider = ser;
            Tracer          = new TraceBuffer
            {
                Header = "Z80: PC, opcode, registers (A, B, C, D, E, F, H, L, LY, SP, CY)"
            };
            ser.Register <ITraceable>(Tracer);
            InitMemoryCallbacks();

            ThrowExceptionForBadRom(file);
            BoardName = MapperName(file);

            DeterministicEmulation = deterministic;

            GambatteState = LibGambatte.gambatte_create();

            if (GambatteState == IntPtr.Zero)
            {
                throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_create)}() returned null???");
            }

            Console.WriteLine(game.System);

            try
            {
                _syncSettings = (GambatteSyncSettings)syncSettings ?? new GambatteSyncSettings();

                LibGambatte.LoadFlags flags = 0;

                switch (_syncSettings.ConsoleMode)
                {
                case GambatteSyncSettings.ConsoleModeType.GB:
                    flags |= LibGambatte.LoadFlags.FORCE_DMG;
                    break;

                case GambatteSyncSettings.ConsoleModeType.GBC:
                    break;

                case GambatteSyncSettings.ConsoleModeType.GBA:
                    flags |= LibGambatte.LoadFlags.GBA_CGB;
                    break;

                default:
                    if (game.System == "GB")
                    {
                        flags |= LibGambatte.LoadFlags.FORCE_DMG;
                    }
                    break;
                }

                if (_syncSettings.MulticartCompat)
                {
                    flags |= LibGambatte.LoadFlags.MULTICART_COMPAT;
                }

                if (LibGambatte.gambatte_load(GambatteState, file, (uint)file.Length, flags) != 0)
                {
                    throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_load)}() returned non-zero (is this not a gb or gbc rom?)");
                }

                byte[] bios;
                string biosSystemId;
                string biosId;
                if ((flags & LibGambatte.LoadFlags.FORCE_DMG) == LibGambatte.LoadFlags.FORCE_DMG)
                {
                    biosSystemId = "GB";
                    biosId       = "World";
                    IsCgb        = false;
                }
                else
                {
                    biosSystemId = "GBC";
                    biosId       = _syncSettings.ConsoleMode == GambatteSyncSettings.ConsoleModeType.GBA ? "AGB" : "World";
                    IsCgb        = true;
                }

                if (_syncSettings.EnableBIOS)
                {
                    try
                    {
                        bios = comm.CoreFileProvider.GetFirmware(biosSystemId, biosId, true, "BIOS Not Found, Cannot Load.  Change SyncSettings to run without BIOS.");
                    }
                    catch (MissingFirmwareException)
                    {
                        //If we're missing GBA firmware, create gambatte's reverse engineered firmware from a real GBC BIOS
                        if (_syncSettings.ConsoleMode == GambatteSyncSettings.ConsoleModeType.GBA)
                        {
                            bios = comm.CoreFileProvider.GetFirmware(biosSystemId, "World", true, "BIOS Not Found, Cannot Load.  Change SyncSettings to run without BIOS.");
                            byte[] agbOverride = { 0xFF, 0x00, 0xCD, 0x03, 0x35, 0xAA, 0x31, 0x90, 0x94, 0x00, 0x00, 0x00, 0x00 };
                            for (int i = 0xF3; i < 0x100; i++)
                            {
                                bios[i] = (byte)((agbOverride[i - 0xF3] + bios[i]) & 0xFF);
                            }
                            File.WriteAllBytes("Firmware/agb_gambatte.bin", bios);
                        }
                        else
                        {
                            bios = comm.CoreFileProvider.GetFirmware(biosSystemId, biosId, true, "BIOS Not Found, Cannot Load.  Change SyncSettings to run without BIOS.");
                        }
                    }
                }
                else
                {
                    var builtinBios = (biosSystemId, biosId) switch {
                        ("GB", "World") => Resources.FastDmgBoot,
                        ("GBC", "World") => Resources.FastCgbBoot,
                        ("GBC", "AGB") => Resources.FastAgbBoot,
                        (_, _) => throw new Exception("Internal GB Error (BIOS??)"),
                    };
                    bios = BizHawk.Common.Util.DecompressGzipFile(new MemoryStream(builtinBios.Value, false));
                }

                if (LibGambatte.gambatte_loadbios(GambatteState, bios, (uint)bios.Length) != 0)
                {
                    throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbios)}() returned non-zero (bios error)");
                }

                // set real default colors (before anyone mucks with them at all)
                PutSettings((GambatteSettings)settings ?? new GambatteSettings());

                InitSound();

                Frame      = 0;
                LagCount   = 0;
                IsLagFrame = false;

                InputCallback = new LibGambatte.InputGetter(ControllerCallback);

                LibGambatte.gambatte_setinputgetter(GambatteState, InputCallback);

                InitMemoryDomains();

                RomDetails = $"{game.Name}\r\nSHA1:{file.HashSHA1()}\r\nMD5:{file.HashMD5()}\r\n";

                byte[] buff = new byte[32];
                LibGambatte.gambatte_romtitle(GambatteState, buff);
                string romname = System.Text.Encoding.ASCII.GetString(buff);
                Console.WriteLine("Core reported rom name: {0}", romname);

                if (!DeterministicEmulation && _syncSettings.RealTimeRTC)
                {
                    LibGambatte.gambatte_settimemode(GambatteState, false);
                }
                LibGambatte.gambatte_setrtcdivisoroffset(GambatteState, _syncSettings.RTCDivisorOffset);

                _cdCallback = new LibGambatte.CDCallback(CDCallbackProc);

                NewSaveCoreSetBuff();
            }
Exemplo n.º 6
0
        public Gameboy(CoreComm comm, GameInfo game, byte[] file, Gameboy.GambatteSettings settings, Gameboy.GambatteSyncSettings syncSettings, bool deterministic)
        {
            var ser = new BasicServiceProvider(this);

            ser.Register <IDisassemblable>(_disassembler);
            ServiceProvider = ser;
            Tracer          = new TraceBuffer
            {
                Header = "Z80: PC, opcode, registers (A, B, C, D, E, F, H, L, LY, SP, CY)"
            };
            ser.Register <ITraceable>(Tracer);
            InitMemoryCallbacks();

            ThrowExceptionForBadRom(file);
            BoardName = MapperName(file);

            DeterministicEmulation = deterministic;

            GambatteState = LibGambatte.gambatte_create();

            if (GambatteState == IntPtr.Zero)
            {
                throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_create)}() returned null???");
            }

            Console.WriteLine(game.System);

            try
            {
                _syncSettings = (GambatteSyncSettings)syncSettings ?? new GambatteSyncSettings();

                LibGambatte.LoadFlags flags = 0;

                switch (_syncSettings.ConsoleMode)
                {
                case GambatteSyncSettings.ConsoleModeType.GB:
                    break;

                case GambatteSyncSettings.ConsoleModeType.GBC:
                    flags |= LibGambatte.LoadFlags.CGB_MODE;
                    break;

                case GambatteSyncSettings.ConsoleModeType.GBA:
                    flags |= LibGambatte.LoadFlags.CGB_MODE | LibGambatte.LoadFlags.GBA_FLAG;
                    break;

                default:
                    if (game.System == "GBC")
                    {
                        flags |= LibGambatte.LoadFlags.CGB_MODE;
                    }
                    break;
                }

                if (_syncSettings.MulticartCompat)
                {
                    flags |= LibGambatte.LoadFlags.MULTICART_COMPAT;
                }

                if (LibGambatte.gambatte_load(GambatteState, file, (uint)file.Length, flags) != 0)
                {
                    throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_load)}() returned non-zero (is this not a gb or gbc rom?)");
                }

                byte[] bios;
                string biosSystemId;
                string biosId;
                if ((flags & LibGambatte.LoadFlags.CGB_MODE) == LibGambatte.LoadFlags.CGB_MODE)
                {
                    biosSystemId = "GBC";
                    biosId       = _syncSettings.ConsoleMode == GambatteSyncSettings.ConsoleModeType.GBA ? "AGB" : "World";
                    IsCgb        = true;
                }
                else
                {
                    biosSystemId = "GB";
                    biosId       = "World";
                    IsCgb        = false;
                }

                if (_syncSettings.EnableBIOS)
                {
                    bios = comm.CoreFileProvider.GetFirmware(biosSystemId, biosId, true, "BIOS Not Found, Cannot Load.  Change SyncSettings to run without BIOS.");
                }
                else
                {
                    var builtinBios = (biosSystemId, biosId) switch {
                        ("GB", "World") => Resources.FastDmgBoot,
                        ("GBC", "World") => Resources.FastCgbBoot,
                        ("GBC", "AGB") => Resources.FastAgbBoot,
                        (_, _) => throw new Exception("Internal GB Error (BIOS??)"),
                    };
                    bios = BizHawk.Common.Util.DecompressGzipFile(new MemoryStream(builtinBios.Value, false));
                }

                if (LibGambatte.gambatte_loadbios(GambatteState, bios, (uint)bios.Length) != 0)
                {
                    throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbios)}() returned non-zero (bios error)");
                }

                // set real default colors (before anyone mucks with them at all)
                PutSettings((GambatteSettings)settings ?? new GambatteSettings());

                InitSound();

                Frame      = 0;
                LagCount   = 0;
                IsLagFrame = false;

                InputCallback = new LibGambatte.InputGetter(ControllerCallback);

                LibGambatte.gambatte_setinputgetter(GambatteState, InputCallback);

                InitMemoryDomains();

                RomDetails = $"{game.Name}\r\nSHA1:{file.HashSHA1()}\r\nMD5:{file.HashMD5()}\r\n";

                byte[] buff = new byte[32];
                LibGambatte.gambatte_romtitle(GambatteState, buff);
                string romname = System.Text.Encoding.ASCII.GetString(buff);
                Console.WriteLine("Core reported rom name: {0}", romname);

                if (!DeterministicEmulation && _syncSettings.RealTimeRTC)
                {
                    LibGambatte.gambatte_settimemode(GambatteState, false);
                }

                if (DeterministicEmulation)
                {
                    int[] rtcRegs = new int[11];
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh] = 0;
                    if (_syncSettings.InternalRTCOverflow)
                    {
                        rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh] |= 0x80;
                    }
                    if (_syncSettings.InternalRTCHalt)
                    {
                        rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh] |= 0x40;
                    }
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh]  |= _syncSettings.InternalRTCDays >> 8;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dl]   = _syncSettings.InternalRTCDays & 0xFF;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.H]    = (_syncSettings.InternalRTCHours < 0) ? (_syncSettings.InternalRTCHours + 0x20) : _syncSettings.InternalRTCHours;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.M]    = (_syncSettings.InternalRTCMinutes < 0) ? (_syncSettings.InternalRTCMinutes + 0x40) : _syncSettings.InternalRTCMinutes;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.S]    = (_syncSettings.InternalRTCSeconds < 0) ? (_syncSettings.InternalRTCSeconds + 0x40) : _syncSettings.InternalRTCSeconds;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.C]    = _syncSettings.InternalRTCCycles;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh_L] = 0;
                    if (_syncSettings.LatchedRTCOverflow)
                    {
                        rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh_L] |= 0x80;
                    }
                    if (_syncSettings.LatchedRTCHalt)
                    {
                        rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh_L] |= 0x40;
                    }
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh_L] |= _syncSettings.LatchedRTCDays >> 8;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dl_L]  = _syncSettings.LatchedRTCDays & 0xFF;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.H_L]   = _syncSettings.LatchedRTCHours;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.M_L]   = _syncSettings.LatchedRTCMinutes;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.S_L]   = _syncSettings.LatchedRTCSeconds;
                    LibGambatte.gambatte_setrtcregs(GambatteState, rtcRegs);
                }

                LibGambatte.gambatte_setrtcdivisoroffset(GambatteState, _syncSettings.RTCDivisorOffset);

                _cdCallback = new LibGambatte.CDCallback(CDCallbackProc);

                NewSaveCoreSetBuff();
            }
Exemplo n.º 7
0
        public Gameboy(CoreComm comm, GameInfo game, byte[] file, GambatteSettings settings, GambatteSyncSettings syncSettings, bool deterministic)
        {
            var ser = new BasicServiceProvider(this);

            ser.Register <IDisassemblable>(_disassembler);
            ServiceProvider = ser;
            const string TRACE_HEADER = "LR35902: PC, opcode, registers (A, F, B, C, D, E, H, L, LY, SP, CY)";

            Tracer = new TraceBuffer(TRACE_HEADER);
            ser.Register <ITraceable>(Tracer);
            InitMemoryCallbacks();

            DeterministicEmulation = deterministic;

            GambatteState = LibGambatte.gambatte_create();

            if (GambatteState == IntPtr.Zero)
            {
                throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_create)}() returned null???");
            }

            Console.WriteLine(game.System);

            try
            {
                _syncSettings = syncSettings ?? new GambatteSyncSettings();

                LibGambatte.LoadFlags flags = LibGambatte.LoadFlags.READONLY_SAV;

                switch (_syncSettings.ConsoleMode)
                {
                case GambatteSyncSettings.ConsoleModeType.GB:
                    break;

                case GambatteSyncSettings.ConsoleModeType.GBC:
                    flags |= LibGambatte.LoadFlags.CGB_MODE;
                    break;

                case GambatteSyncSettings.ConsoleModeType.GBA:
                    flags |= LibGambatte.LoadFlags.CGB_MODE | LibGambatte.LoadFlags.GBA_FLAG;
                    break;

                default:
                    if (game.System == VSystemID.Raw.GBC)
                    {
                        flags |= LibGambatte.LoadFlags.CGB_MODE;
                    }
                    break;
                }

                if (game.System == VSystemID.Raw.SGB)
                {
                    flags &= ~(LibGambatte.LoadFlags.CGB_MODE | LibGambatte.LoadFlags.GBA_FLAG);
                    flags |= LibGambatte.LoadFlags.SGB_MODE;
                    IsSgb  = true;
                }

                IsCgb = (flags & LibGambatte.LoadFlags.CGB_MODE) == LibGambatte.LoadFlags.CGB_MODE;
                if (_syncSettings.EnableBIOS)
                {
                    FirmwareID fwid = new(
                        IsCgb ? "GBC" : "GB",
                        IsSgb
                                                        ? "SGB2"
                                                        : _syncSettings.ConsoleMode is GambatteSyncSettings.ConsoleModeType.GBA
                                                                ? "AGB"
                                                                : "World");
                    var bios = comm.CoreFileProvider.GetFirmwareOrThrow(fwid, "BIOS Not Found, Cannot Load.  Change SyncSettings to run without BIOS.");                     // https://github.com/TASEmulators/BizHawk/issues/2832 tho
                    if (LibGambatte.gambatte_loadbiosbuf(GambatteState, bios, (uint)bios.Length) != 0)
                    {
                        throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbiosbuf)}() returned non-zero (bios error)");
                    }
                }
                else
                {
                    if (DeterministicEmulation)                     // throw a warning if a movie is being recorded with the bios disabled
                    {
                        comm.ShowMessage("Detected disabled BIOS during movie recording. It is recommended to use a BIOS for movie recording. Change Sync Settings to run with a BIOS.");
                    }
                    flags |= LibGambatte.LoadFlags.NO_BIOS;
                }

                if (LibGambatte.gambatte_loadbuf(GambatteState, file, (uint)file.Length, flags) != 0)
                {
                    throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbuf)}() returned non-zero (is this not a gb or gbc rom?)");
                }

                if (IsSgb)
                {
                    ResetStallTicks = 128 * (2 << 14);
                }
                else if (_syncSettings.EnableBIOS && (_syncSettings.ConsoleMode is GambatteSyncSettings.ConsoleModeType.GBA))
                {
                    ResetStallTicks = 485808;                     // GBA takes 971616 cycles to switch to CGB mode; CGB CPU is inactive during this time.
                }
                else
                {
                    ResetStallTicks = 0;
                }

                // set real default colors (before anyone mucks with them at all)
                PutSettings(settings ?? new GambatteSettings());

                InitSound();

                Frame      = 0;
                LagCount   = 0;
                IsLagFrame = false;

                InputCallback = new LibGambatte.InputGetter(ControllerCallback);

                LibGambatte.gambatte_setinputgetter(GambatteState, InputCallback, IntPtr.Zero);

                InitMemoryDomains();

                byte[] mbcBuf           = new byte[32];
                uint   rambanks         = 0;
                uint   rombanks         = 0;
                uint   crc              = 0;
                uint   headerchecksumok = 0;
                LibGambatte.gambatte_pakinfo(GambatteState, mbcBuf, ref rambanks, ref rombanks, ref crc, ref headerchecksumok);

                byte[] romNameBuf = new byte[32];
                LibGambatte.gambatte_romtitle(GambatteState, romNameBuf);
                string romname = Encoding.ASCII.GetString(romNameBuf);

                RomDetails = $"{game.Name}\r\n{SHA1Checksum.ComputePrefixedHex(file)}\r\n{MD5Checksum.ComputePrefixedHex(file)}\r\n\r\n";
                BoardName  = Encoding.ASCII.GetString(mbcBuf);

                RomDetails += $"Core reported Header Name: {romname}\r\n";
                RomDetails += $"Core reported RAM Banks: {rambanks}\r\n";
                RomDetails += $"Core reported ROM Banks: {rombanks}\r\n";
                RomDetails += $"Core reported CRC32: {crc:X8}\r\n";
                RomDetails += $"Core reported Header Checksum Status: {(headerchecksumok != 0 ? "OK" : "BAD")}\r\n";

                if (_syncSettings.EnableBIOS && headerchecksumok == 0)
                {
                    comm.ShowMessage("Core reports the header checksum is bad. This ROM will not boot with the official BIOS.\n" +
                                     "Disable BIOS in sync settings to boot this game");
                }

                if (!_syncSettings.EnableBIOS && IsCgb && IsCGBDMGMode())                 // without a bios, we need to set the palette for cgbdmg ourselves
                {
                    int[] cgbDmgColors = new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 };
                    if (file[0x14B] == 0x01 || (file[0x14B] == 0x33 && file[0x144] == '0' && file[0x145] == '1'))                     // Nintendo licencees get special palettes
                    {
                        cgbDmgColors = ColorsFromTitleHash(file);
                    }
                    ChangeDMGColors(cgbDmgColors);
                }

                if (!DeterministicEmulation && _syncSettings.RealTimeRTC)
                {
                    LibGambatte.gambatte_settimemode(GambatteState, false);
                }

                if (DeterministicEmulation)
                {
                    ulong dividers = _syncSettings.InitialTime * (0x400000UL + (ulong)_syncSettings.RTCDivisorOffset) / 2UL;
                    LibGambatte.gambatte_settime(GambatteState, dividers);
                }

                LibGambatte.gambatte_setrtcdivisoroffset(GambatteState, _syncSettings.RTCDivisorOffset);

                LibGambatte.gambatte_setcartbuspulluptime(GambatteState, (uint)_syncSettings.CartBusPullUpTime);

                _cdCallback = new LibGambatte.CDCallback(CDCallbackProc);

                ControllerDefinition = CreateControllerDefinition(sgb: IsSgb, sub: _syncSettings.FrameLength is GambatteSyncSettings.FrameLengthType.UserDefinedFrames, tilt: false);

                NewSaveCoreSetBuff();
            }
            catch
            {
                Dispose();
                throw;
            }
        }
Exemplo n.º 8
0
        public Gameboy(CoreComm comm, GameInfo game, byte[] file, object settings, object syncSettings, bool deterministic)
        {
            var ser = new BasicServiceProvider(this);

            ser.Register <IDisassemblable>(new GBDisassembler());
            ServiceProvider = ser;
            Tracer          = new TraceBuffer
            {
                Header = "Z80: PC, opcode, registers (A, B, C, D, E, F, H, L, LY, SP, CY)"
            };
            ser.Register <ITraceable>(Tracer);
            InitMemoryCallbacks();
            CoreComm = comm;

            comm.RomStatusAnnotation = null;
            comm.RomStatusDetails    = null;
            comm.NominalWidth        = 160;
            comm.NominalHeight       = 144;

            ThrowExceptionForBadRom(file);
            BoardName = MapperName(file);

            DeterministicEmulation = deterministic;

            GambatteState = LibGambatte.gambatte_create();

            if (GambatteState == IntPtr.Zero)
            {
                throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_create)}() returned null???");
            }

            Console.WriteLine(game.System);

            try
            {
                _syncSettings = (GambatteSyncSettings)syncSettings ?? new GambatteSyncSettings();

                LibGambatte.LoadFlags flags = 0;

                switch (_syncSettings.ConsoleMode)
                {
                case GambatteSyncSettings.ConsoleModeType.GB:
                    flags |= LibGambatte.LoadFlags.FORCE_DMG;
                    break;

                case GambatteSyncSettings.ConsoleModeType.GBC:
                    break;

                default:
                    if (game.System == "GB")
                    {
                        flags |= LibGambatte.LoadFlags.FORCE_DMG;
                    }
                    break;
                }

                if (_syncSettings.GBACGB)
                {
                    flags |= LibGambatte.LoadFlags.GBA_CGB;
                }

                if (_syncSettings.MulticartCompat)
                {
                    flags |= LibGambatte.LoadFlags.MULTICART_COMPAT;
                }

                if (LibGambatte.gambatte_load(GambatteState, file, (uint)file.Length, flags) != 0)
                {
                    throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_load)}() returned non-zero (is this not a gb or gbc rom?)");
                }

                byte[] Bios;
                if ((flags & LibGambatte.LoadFlags.FORCE_DMG) == LibGambatte.LoadFlags.FORCE_DMG)
                {
                    Bios  = comm.CoreFileProvider.GetFirmware("GB", "World", true, "BIOS Not Found, Cannot Load");
                    IsCgb = false;
                }
                else
                {
                    Bios  = comm.CoreFileProvider.GetFirmware("GBC", "World", true, "BIOS Not Found, Cannot Load");
                    IsCgb = true;
                }
                if (LibGambatte.gambatte_loadbios(GambatteState, Bios, (uint)Bios.Length) != 0)
                {
                    throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbios)}() returned non-zero (bios error)");
                }

                // set real default colors (before anyone mucks with them at all)
                PutSettings((GambatteSettings)settings ?? new GambatteSettings());

                InitSound();

                Frame      = 0;
                LagCount   = 0;
                IsLagFrame = false;

                InputCallback = new LibGambatte.InputGetter(ControllerCallback);

                LibGambatte.gambatte_setinputgetter(GambatteState, InputCallback);

                InitMemoryDomains();

                CoreComm.RomStatusDetails = $"{game.Name}\r\nSHA1:{file.HashSHA1()}\r\nMD5:{file.HashMD5()}\r\n";

                byte[] buff = new byte[32];
                LibGambatte.gambatte_romtitle(GambatteState, buff);
                string romname = System.Text.Encoding.ASCII.GetString(buff);
                Console.WriteLine("Core reported rom name: {0}", romname);

                if (!DeterministicEmulation && _syncSettings.RealTimeRTC)
                {
                    LibGambatte.gambatte_settimemode(GambatteState, false);
                }
                LibGambatte.gambatte_setrtcdivisoroffset(GambatteState, _syncSettings.RTCDivisorOffset);

                _cdCallback = new LibGambatte.CDCallback(CDCallbackProc);

                NewSaveCoreSetBuff();
            }
            catch
            {
                Dispose();
                throw;
            }
        }
Exemplo n.º 9
0
        public Gameboy(CoreComm comm, GameInfo game, byte[] file, Gameboy.GambatteSettings settings, Gameboy.GambatteSyncSettings syncSettings, bool deterministic)
        {
            var ser = new BasicServiceProvider(this);

            ser.Register <IDisassemblable>(_disassembler);
            ServiceProvider = ser;
            Tracer          = new TraceBuffer
            {
                Header = "LR35902: PC, opcode, registers (A, F, B, C, D, E, H, L, LY, SP, CY)"
            };
            ser.Register <ITraceable>(Tracer);
            InitMemoryCallbacks();

            ThrowExceptionForBadRom(file);
            BoardName = MapperName(file);

            DeterministicEmulation = deterministic;

            GambatteState = LibGambatte.gambatte_create();

            if (GambatteState == IntPtr.Zero)
            {
                throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_create)}() returned null???");
            }

            Console.WriteLine(game.System);

            try
            {
                _syncSettings = (GambatteSyncSettings)syncSettings ?? new GambatteSyncSettings();

                LibGambatte.LoadFlags flags = 0;

                switch (_syncSettings.ConsoleMode)
                {
                case GambatteSyncSettings.ConsoleModeType.GB:
                    break;

                case GambatteSyncSettings.ConsoleModeType.GBC:
                    flags |= LibGambatte.LoadFlags.CGB_MODE;
                    break;

                case GambatteSyncSettings.ConsoleModeType.GBA:
                    flags |= LibGambatte.LoadFlags.CGB_MODE | LibGambatte.LoadFlags.GBA_FLAG;
                    break;

                default:
                    if (game.System == "GBC")
                    {
                        flags |= LibGambatte.LoadFlags.CGB_MODE;
                    }
                    break;
                }

                if (_syncSettings.MulticartCompat)
                {
                    flags |= LibGambatte.LoadFlags.MULTICART_COMPAT;
                }

                byte[] bios;
                string biosSystemId;
                string biosId;
                if ((flags & LibGambatte.LoadFlags.CGB_MODE) == LibGambatte.LoadFlags.CGB_MODE)
                {
                    biosSystemId = "GBC";
                    biosId       = _syncSettings.ConsoleMode == GambatteSyncSettings.ConsoleModeType.GBA ? "AGB" : "World";
                    IsCgb        = true;
                }
                else
                {
                    biosSystemId = "GB";
                    biosId       = "World";
                    IsCgb        = false;
                }

                if (_syncSettings.EnableBIOS)
                {
                    bios = comm.CoreFileProvider.GetFirmware(biosSystemId, biosId, true, "BIOS Not Found, Cannot Load.  Change SyncSettings to run without BIOS.");
                    if (LibGambatte.gambatte_loadbios(GambatteState, bios, (uint)bios.Length) != 0)
                    {
                        throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbios)}() returned non-zero (bios error)");
                    }
                }
                else
                {
                    flags |= LibGambatte.LoadFlags.NO_BIOS;
                }

                if (LibGambatte.gambatte_load(GambatteState, file, (uint)file.Length, flags) != 0)
                {
                    throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_load)}() returned non-zero (is this not a gb or gbc rom?)");
                }

                // set real default colors (before anyone mucks with them at all)
                PutSettings((GambatteSettings)settings ?? new GambatteSettings());

                InitSound();

                Frame      = 0;
                LagCount   = 0;
                IsLagFrame = false;

                InputCallback = new LibGambatte.InputGetter(ControllerCallback);

                LibGambatte.gambatte_setinputgetter(GambatteState, InputCallback);

                InitMemoryDomains();

                RomDetails = $"{game.Name}\r\nSHA1:{file.HashSHA1()}\r\nMD5:{file.HashMD5()}\r\n";

                byte[] buff = new byte[32];
                LibGambatte.gambatte_romtitle(GambatteState, buff);
                string romname = System.Text.Encoding.ASCII.GetString(buff);
                Console.WriteLine("Core reported rom name: {0}", romname);

                if (!_syncSettings.EnableBIOS && IsCgb && IsCGBDMGMode())                 // without a bios, we need to set the palette for cgbdmg ourselves
                {
                    int[] cgbDmgColors = new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 };
                    if (file[0x14B] == 0x01 || (file[0x14B] == 0x33 && file[0x144] == '0' && file[0x145] == '1'))                     // Nintendo licencees get special palettes
                    {
                        cgbDmgColors = ColorsFromTitleHash(file);
                    }
                    ChangeDMGColors(cgbDmgColors);
                }

                if (!DeterministicEmulation && _syncSettings.RealTimeRTC)
                {
                    LibGambatte.gambatte_settimemode(GambatteState, false);
                }

                if (DeterministicEmulation)
                {
                    int[] rtcRegs = new int[11];
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh] = 0;
                    if (_syncSettings.InternalRTCOverflow)
                    {
                        rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh] |= 0x80;
                    }
                    if (_syncSettings.InternalRTCHalt)
                    {
                        rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh] |= 0x40;
                    }
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh]  |= _syncSettings.InternalRTCDays >> 8;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dl]   = _syncSettings.InternalRTCDays & 0xFF;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.H]    = (_syncSettings.InternalRTCHours < 0) ? (_syncSettings.InternalRTCHours + 0x20) : _syncSettings.InternalRTCHours;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.M]    = (_syncSettings.InternalRTCMinutes < 0) ? (_syncSettings.InternalRTCMinutes + 0x40) : _syncSettings.InternalRTCMinutes;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.S]    = (_syncSettings.InternalRTCSeconds < 0) ? (_syncSettings.InternalRTCSeconds + 0x40) : _syncSettings.InternalRTCSeconds;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.C]    = _syncSettings.InternalRTCCycles;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh_L] = 0;
                    if (_syncSettings.LatchedRTCOverflow)
                    {
                        rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh_L] |= 0x80;
                    }
                    if (_syncSettings.LatchedRTCHalt)
                    {
                        rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh_L] |= 0x40;
                    }
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dh_L] |= _syncSettings.LatchedRTCDays >> 8;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.Dl_L]  = _syncSettings.LatchedRTCDays & 0xFF;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.H_L]   = _syncSettings.LatchedRTCHours;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.M_L]   = _syncSettings.LatchedRTCMinutes;
                    rtcRegs[(int)LibGambatte.RtcRegIndicies.S_L]   = _syncSettings.LatchedRTCSeconds;
                    LibGambatte.gambatte_setrtcregs(GambatteState, rtcRegs);
                }

                LibGambatte.gambatte_setrtcdivisoroffset(GambatteState, _syncSettings.RTCDivisorOffset);

                LibGambatte.gambatte_setcartbuspulluptime(GambatteState, _syncSettings.CartBusPullUpTime);

                _cdCallback = new LibGambatte.CDCallback(CDCallbackProc);

                NewSaveCoreSetBuff();
            }
            catch
            {
                Dispose();
                throw;
            }
        }