protected T PreInit <T>(WaterboxOptions options, IEnumerable <Delegate> allExtraDelegates = null) where T : LibWaterboxCore { options.Path ??= CoreComm.CoreFileProvider.DllPath(); _exe = new WaterboxHost(options); var delegates = new Delegate[] { _inputCallback }.AsEnumerable(); if (allExtraDelegates != null) { delegates = delegates.Concat(allExtraDelegates); } using (_exe.EnterExit()) { var ret = BizInvoker.GetInvoker <T>(_exe, _exe, CallingConventionAdapters.MakeWaterbox(delegates, _exe)); _core = ret; return(ret); } }
public LibsnesApi(string dllPath, CoreComm comm, IEnumerable <Delegate> allCallbacks) { _exe = new WaterboxHost(new WaterboxOptions { Filename = "libsnes.wbx", Path = dllPath, SbrkHeapSizeKB = 4 * 1024, InvisibleHeapSizeKB = 8 * 1024, MmapHeapSizeKB = 32 * 1024, // TODO: see if we can safely make libco stacks smaller PlainHeapSizeKB = 2 * 1024, // TODO: wasn't there more in here? SealedHeapSizeKB = 128 * 1024, SkipCoreConsistencyCheck = comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck), SkipMemoryConsistencyCheck = comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck), }); using (_exe.EnterExit()) { // Marshal checks that function pointers passed to GetDelegateForFunctionPointer are // _currently_ valid when created, even though they don't need to be valid until // the delegate is later invoked. so GetInvoker needs to be acquired within a lock. _core = BizInvoker.GetInvoker <CoreImpl>(_exe, _exe, CallingConventionAdapters.MakeWaterbox(allCallbacks, _exe)); _comm = (CommStruct *)_core.DllInit().ToPointer(); } }
public GPGX(CoreLoadParameters <GPGXSettings, GPGXSyncSettings> lp) { LoadCallback = new LibGPGX.load_archive_cb(load_archive); _inputCallback = new LibGPGX.input_cb(input_callback); InitMemCallbacks(); // ExecCallback, ReadCallback, WriteCallback CDCallback = new LibGPGX.CDCallback(CDCallbackProc); cd_callback_handle = new LibGPGX.cd_read_cb(CDRead); ServiceProvider = new BasicServiceProvider(this); // this can influence some things internally (autodetect romtype, etc) string romextension = "GEN"; // three or six button? // http://www.sega-16.com/forum/showthread.php?4398-Forgotten-Worlds-giving-you-GAME-OVER-immediately-Fix-inside&highlight=forgotten%20worlds //hack, don't use if (lp.Roms.FirstOrDefault()?.RomData.Length > 32 * 1024 * 1024) { throw new InvalidOperationException("ROM too big! Did you try to load a CD as a ROM?"); } _elf = new WaterboxHost(new WaterboxOptions { Path = lp.Comm.CoreFileProvider.DllPath(), Filename = "gpgx.wbx", SbrkHeapSizeKB = 512, SealedHeapSizeKB = 4 * 1024, InvisibleHeapSizeKB = 4 * 1024, PlainHeapSizeKB = 34 * 1024, MmapHeapSizeKB = 1 * 1024, SkipCoreConsistencyCheck = lp.Comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck), SkipMemoryConsistencyCheck = lp.Comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck), }); var callingConventionAdapter = CallingConventionAdapters.MakeWaterbox(new Delegate[] { LoadCallback, _inputCallback, ExecCallback, ReadCallback, WriteCallback, CDCallback, cd_callback_handle, }, _elf); using (_elf.EnterExit()) { Core = BizInvoker.GetInvoker <LibGPGX>(_elf, _elf, callingConventionAdapter); _syncSettings = lp.SyncSettings ?? new GPGXSyncSettings(); _settings = lp.Settings ?? new GPGXSettings(); CoreComm = lp.Comm; _romfile = lp.Roms.FirstOrDefault()?.RomData; if (lp.Discs.Count > 0) { _cds = lp.Discs.Select(d => d.DiscData).ToArray(); _cdReaders = _cds.Select(c => new DiscSectorReader(c)).ToArray(); Core.gpgx_set_cdd_callback(cd_callback_handle); DriveLightEnabled = true; } LibGPGX.INPUT_SYSTEM system_a = SystemForSystem(_syncSettings.ControlTypeLeft); LibGPGX.INPUT_SYSTEM system_b = SystemForSystem(_syncSettings.ControlTypeRight); var initResult = Core.gpgx_init(romextension, LoadCallback, _syncSettings.GetNativeSettings(lp.Game)); if (!initResult) { throw new Exception($"{nameof(Core.gpgx_init)}() failed"); } { int fpsnum = 60; int fpsden = 1; Core.gpgx_get_fps(ref fpsnum, ref fpsden); VsyncNumerator = fpsnum; VsyncDenominator = fpsden; Region = VsyncNumerator / VsyncDenominator > 55 ? DisplayType.NTSC : DisplayType.PAL; } // when we call Seal, ANY pointer passed from managed code must be 0. // this is so the initial state is clean // the only two pointers set so far are LoadCallback, which the core zeroed itself, // and CdCallback Core.gpgx_set_cdd_callback(null); _elf.Seal(); Core.gpgx_set_cdd_callback(cd_callback_handle); SetControllerDefinition(); // pull the default video size from the core UpdateVideo(); SetMemoryDomains(); Core.gpgx_set_input_callback(_inputCallback); // process the non-init settings now PutSettings(_settings); KillMemCallbacks(); _tracer = new GPGXTraceBuffer(this, _memoryDomains, this); (ServiceProvider as BasicServiceProvider).Register <ITraceable>(_tracer); } _romfile = null; }
protected T DoInit <T>(GameInfo game, byte[] rom, Disc[] discs, string wbxFilename, string extension, bool deterministic, IDictionary <string, FirmwareID> firmwares = null) where T : LibNymaCore { _settingsQueryDelegate = SettingsQuery; _cdTocCallback = CDTOCCallback; _cdSectorCallback = CDSectorCallback; var filesToRemove = new List <string>(); var firmwareDelegate = new LibNymaCore.FrontendFirmwareNotify((name) => { if (firmwares != null && firmwares.TryGetValue(name, out var id)) { var data = CoreComm.CoreFileProvider.GetFirmwareOrThrow(id, "Firmware files are usually required and may stop your game from loading"); _exe.AddReadonlyFile(data, name); filesToRemove.Add(name); } else { throw new InvalidOperationException($"Core asked for firmware `{name}`, but that was not understood by the system"); } }); var t = PreInit <T>(new WaterboxOptions { Filename = wbxFilename, // WaterboxHost only saves parts of memory that have changed, so not much to be gained by making these precisely sized SbrkHeapSizeKB = 1024 * 16, SealedHeapSizeKB = 1024 * 48, InvisibleHeapSizeKB = 1024 * 48, PlainHeapSizeKB = 1024 * 48, MmapHeapSizeKB = 1024 * 48, SkipCoreConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck), SkipMemoryConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck), }, new Delegate[] { _settingsQueryDelegate, _cdTocCallback, _cdSectorCallback, firmwareDelegate }); _nyma = t; using (_exe.EnterExit()) { _nyma.PreInit(); _nyma.SetFrontendFirmwareNotify(firmwareDelegate); var portData = GetInputPortsData(); InitAllSettingsInfo(portData); _nyma.SetFrontendSettingQuery(_settingsQueryDelegate); if (discs?.Length > 0) { _disks = discs; _diskReaders = _disks.Select(d => new DiscSectorReader(d) { Policy = _diskPolicy }).ToArray(); _nyma.SetCDCallbacks(_cdTocCallback, _cdSectorCallback); var didInit = _nyma.InitCd(_disks.Length); if (!didInit) { throw new InvalidOperationException("Core rejected the CDs!"); } } else { var fn = game.FilesystemSafeName(); _exe.AddReadonlyFile(rom, fn); var didInit = _nyma.InitRom(new LibNymaCore.InitData { // TODO: Set these as some cores need them FileNameBase = "", FileNameExt = extension.Trim('.').ToLowerInvariant(), FileNameFull = fn }); if (!didInit) { throw new InvalidOperationException("Core rejected the rom!"); } _exe.RemoveReadonlyFile(fn); } foreach (var s in filesToRemove) { _exe.RemoveReadonlyFile(s); } // any later attempts to request a firmware will crash _nyma.SetFrontendFirmwareNotify(null); var info = *_nyma.GetSystemInfo(); _videoBuffer = new int[Math.Max(info.MaxWidth * info.MaxHeight, info.LcmWidth * info.LcmHeight)]; BufferWidth = info.NominalWidth; BufferHeight = info.NominalHeight; _mdfnNominalWidth = info.NominalWidth; _mdfnNominalHeight = info.NominalHeight; switch (info.VideoSystem) { // TODO: There seriously isn't any region besides these? case LibNymaCore.VideoSystem.PAL: case LibNymaCore.VideoSystem.SECAM: Region = DisplayType.PAL; break; case LibNymaCore.VideoSystem.PAL_M: Region = DisplayType.Dendy; // sort of... break; default: Region = DisplayType.NTSC; break; } VsyncNumerator = info.FpsFixed; VsyncDenominator = 1 << 24; _soundBuffer = new short[22050 * 2]; InitControls(portData, discs?.Length > 0, ref info); PostInit(); SettingsInfo.LayerNames = GetLayerData(); _settings.Normalize(SettingsInfo); _syncSettings.Normalize(SettingsInfo); _nyma.SetFrontendSettingQuery(_settingsQueryDelegate); if (_disks != null) { _nyma.SetCDCallbacks(_cdTocCallback, _cdSectorCallback); } PutSettings(_settings); DateTime RtcStart = DateTime.Parse("2010-01-01"); try { RtcStart = DateTime.Parse(SettingsQuery("nyma.rtcinitialtime")); } catch { Console.Error.WriteLine($"Couldn't parse DateTime \"{SettingsQuery("nyma.rtcinitialtime")}\""); } // Don't optimistically set deterministic, as some cores like faust can change this DeterministicEmulation = deterministic; // || SettingsQuery("nyma.rtcrealtime") == "0"; InitializeRtc(RtcStart); _frameThreadPtr = _nyma.GetFrameThreadProc(); if (_frameThreadPtr != IntPtr.Zero) { // This will probably be fine, right? TODO: Revisit // if (deterministic) // throw new InvalidOperationException("Internal error: Core set a frame thread proc in deterministic mode"); Console.WriteLine($"Setting up waterbox thread for {_frameThreadPtr}"); _frameThreadStart = CallingConventionAdapters.GetWaterboxUnsafeUnwrapped().GetDelegateForFunctionPointer <Action>(_frameThreadPtr); } } return(t); }
public static MemoryDomainAccessStub Create(IntPtr p, WaterboxHost host) { return(BizInvoker.GetInvoker <MemoryDomainAccessStub>( new StubResolver(p), host, CallingConventionAdapters.MakeWaterboxDepartureOnly(host))); }
public NDS(CoreLoadParameters <NDSSettings, NDSSyncSettings> lp) : base(lp.Comm, new Configuration { DefaultWidth = 256, DefaultHeight = 384, MaxWidth = 256, MaxHeight = 384, MaxSamples = 1024, DefaultFpsNumerator = 33513982, DefaultFpsDenominator = 560190, SystemId = VSystemID.Raw.NDS, }) { _syncSettings = lp.SyncSettings ?? new NDSSyncSettings(); _settings = lp.Settings ?? new NDSSettings(); IsDSi = _syncSettings.UseDSi; var roms = lp.Roms.Select(r => r.RomData).ToList(); if (roms.Count > (IsDSi ? 1 : 3)) { throw new InvalidOperationException("Wrong number of ROMs!"); } IsDSiWare = IsDSi && RomIsWare(roms[0]); bool gbacartpresent = roms.Count > 1; bool gbasrampresent = roms.Count == 3; InitMemoryCallbacks(); _tracecb = MakeTrace; _threadstartcb = ThreadStartCallback; _core = PreInit <LibMelonDS>(new WaterboxOptions { Filename = "melonDS.wbx", SbrkHeapSizeKB = 2 * 1024, SealedHeapSizeKB = 4, InvisibleHeapSizeKB = 4 * 1024, PlainHeapSizeKB = 4, MmapHeapSizeKB = 1024 * 1024, SkipCoreConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck), SkipMemoryConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck), }, new Delegate[] { _readcb, _writecb, _execcb, _tracecb, _threadstartcb }); var bios7 = IsDSi || _syncSettings.UseRealBIOS ? CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios7")) : null; var bios9 = IsDSi || _syncSettings.UseRealBIOS ? CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios9")) : null; var bios7i = IsDSi ? CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios7i")) : null; var bios9i = IsDSi ? CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios9i")) : null; var nand = IsDSi ? CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "nand")) : null; var fw = IsDSi ? CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "firmwarei")) : CoreComm.CoreFileProvider.GetFirmware(new("NDS", "firmware")); var tmd = IsDSi && IsDSiWare ? GetTMDData(roms[0]) : null; bool skipfw = _syncSettings.SkipFirmware || !_syncSettings.UseRealBIOS || fw == null; LibMelonDS.LoadFlags loadFlags = LibMelonDS.LoadFlags.NONE; if (_syncSettings.UseRealBIOS || IsDSi) { loadFlags |= LibMelonDS.LoadFlags.USE_REAL_BIOS; } if (skipfw && !IsDSi) { loadFlags |= LibMelonDS.LoadFlags.SKIP_FIRMWARE; } if (gbacartpresent) { loadFlags |= LibMelonDS.LoadFlags.GBA_CART_PRESENT; } if (_syncSettings.FirmwareOverride || lp.DeterministicEmulationRequested) { loadFlags |= LibMelonDS.LoadFlags.FIRMWARE_OVERRIDE; } if (IsDSi) { loadFlags |= LibMelonDS.LoadFlags.IS_DSI; } if (IsDSi && IsDSiWare) { loadFlags |= LibMelonDS.LoadFlags.LOAD_DSIWARE; } if (_syncSettings.ThreadedRendering) { loadFlags |= LibMelonDS.LoadFlags.THREADED_RENDERING; } var fwSettings = new LibMelonDS.FirmwareSettings(); var name = Encoding.UTF8.GetBytes(_syncSettings.FirmwareUsername); fwSettings.FirmwareUsernameLength = name.Length; fwSettings.FirmwareLanguage = _syncSettings.FirmwareLanguage; if (!IsDSi && _syncSettings.FirmwareStartUp == NDSSyncSettings.StartUp.AutoBoot) { fwSettings.FirmwareLanguage |= (NDSSyncSettings.Language) 0x40; } fwSettings.FirmwareBirthdayMonth = _syncSettings.FirmwareBirthdayMonth; fwSettings.FirmwareBirthdayDay = _syncSettings.FirmwareBirthdayDay; fwSettings.FirmwareFavouriteColour = _syncSettings.FirmwareFavouriteColour; var message = _syncSettings.FirmwareMessage.Length != 0 ? Encoding.UTF8.GetBytes(_syncSettings.FirmwareMessage) : new byte[1] { 0 }; fwSettings.FirmwareMessageLength = message.Length; var loadData = new LibMelonDS.LoadData { DsRomLength = roms[0].Length, GbaRomLength = gbacartpresent ? roms[1].Length : 0, GbaRamLength = gbasrampresent ? roms[2].Length : 0, NandLength = nand?.Length ?? 0, AudioBitrate = _settings.AudioBitrate, }; if (_syncSettings.UseRealBIOS || IsDSi) { _exe.AddReadonlyFile(bios7, "bios7.rom"); _exe.AddReadonlyFile(bios9, "bios9.rom"); } if (IsDSi) { _exe.AddReadonlyFile(bios7i, "bios7i.rom"); _exe.AddReadonlyFile(bios9i, "bios9i.rom"); if (IsDSiWare) { _exe.AddReadonlyFile(roms[0], "dsiware.rom"); } } if (fw != null) { if (IsDSi || NDSFirmware.MaybeWarnIfBadFw(fw, CoreComm)) // fw checks dont work on dsi firmware, don't bother { if (_syncSettings.FirmwareOverride || lp.DeterministicEmulationRequested) { NDSFirmware.SanitizeFw(fw); } } _exe.AddReadonlyFile(fw, IsDSi ? "firmwarei.bin" : "firmware.bin"); } unsafe { fixed(byte * dsRomPtr = roms[0], gbaRomPtr = gbacartpresent?roms[1] : null, gbaRamPtr = gbasrampresent?roms[2] : null, nandPtr = nand, tmdPtr = tmd, namePtr = name, messagePtr = message) { loadData.DsRomData = (IntPtr)dsRomPtr; loadData.GbaRomData = (IntPtr)gbaRomPtr; loadData.GbaRamData = (IntPtr)gbaRamPtr; loadData.NandData = (IntPtr)nandPtr; loadData.TmdData = (IntPtr)tmdPtr; fwSettings.FirmwareUsername = (IntPtr)namePtr; fwSettings.FirmwareMessage = (IntPtr)messagePtr; if (!_core.Init(loadFlags, ref loadData, ref fwSettings)) { throw new InvalidOperationException("Init returned false!"); } } } if (fw != null) { _exe.RemoveReadonlyFile(IsDSi ? "firmwarei.bin" : "firmware.bin"); } if (IsDSi && IsDSiWare) { _exe.RemoveReadonlyFile("dsiware.rom"); } PostInit(); ((MemoryDomainList)this.AsMemoryDomains()).SystemBus = new NDSSystemBus(this.AsMemoryDomains()["ARM9 System Bus"], this.AsMemoryDomains()["ARM7 System Bus"]); DeterministicEmulation = lp.DeterministicEmulationRequested || (!_syncSettings.UseRealTime); InitializeRtc(_syncSettings.InitialTime); _frameThreadPtr = _core.GetFrameThreadProc(); if (_frameThreadPtr != IntPtr.Zero) { Console.WriteLine($"Setting up waterbox thread for 0x{_frameThreadPtr:X16}"); _frameThreadStart = CallingConventionAdapters.GetWaterboxUnsafeUnwrapped().GetDelegateForFunctionPointer <Action>(_frameThreadPtr); _core.SetThreadStartCallback(_threadstartcb); } _resampler = new SpeexResampler(SpeexResampler.Quality.QUALITY_DEFAULT, 32768, 44100, 32768, 44100, null, this); _serviceProvider.Register <ISoundProvider>(_resampler); _disassembler = new NDSDisassembler(); _serviceProvider.Register <IDisassemblable>(_disassembler); const string TRACE_HEADER = "ARM9+ARM7: PC, opcode, registers (r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, Cy, CpuMode)"; Tracer = new TraceBuffer(TRACE_HEADER); _serviceProvider.Register(Tracer); }