public void Dispose() { if (!Links.Contains(this)) { return; } Links.Remove(this); if (Links.Count > 0) { return; } // Last link - dispose. h_GBSIOInit.Undo(); h_GBSIOInit.Free(); h_GBSIOInit = null; h_GBSIODeinit.Undo(); h_GBSIODeinit.Free(); h_GBSIODeinit = null; h_mTimingInit.Undo(); h_mTimingInit.Free(); h_mTimingInit = null; h_mCoreConfigLoadDefaults.Undo(); h_mCoreConfigLoadDefaults.Free(); h_mCoreConfigLoadDefaults = null; h_mSDLAttachPlayer.Undo(); h_mSDLAttachPlayer.Free(); h_mSDLAttachPlayer = null; Timing = (mTiming *)IntPtr.Zero; SIO = (GBSIO *)IntPtr.Zero; Marshal.FreeHGlobal((IntPtr)Driver); Driver = (GBSIODriver *)IntPtr.Zero; Marshal.FreeHGlobal((IntPtr)QueueCheckEvent->name); Marshal.FreeHGlobal((IntPtr)QueueCheckEvent); QueueCheckEvent = (mTimingEvent *)IntPtr.Zero; }
private static byte DriverWriteSC(GBSIODriver *driver, byte value) { // 0x80: Transfer enabled // 0x01: Shift Clock, must be "external" (0) if ((value & 0x80) == 0x80 && (value & 0x01) != 0x01) { // Game waiting for transfer from outside - dequeue or kick off QueueCheck loop. if (!Dequeue()) { // We haven't dequeued anything - schedule QueueCheck. mTimingSchedule(Timing, QueueCheckEvent, SyncWait); } } else { // Unexpected SC Console.WriteLine($"DRIVR SC 0x{value.ToString("X2")}"); } // Note: Value currently unused by GBSIOWriteSC, but python binding returns input. return(value); }
public MGBALink() { if (Links.Count == 0) { // First link - setup. IntPtr libmgba = DynamicDll.OpenLibrary("libmgba.dll"); IntPtr libmgbasdl = DynamicDll.OpenLibrary("libmgba-sdl.dll"); // Hook GBSIOInit to grab a reference to GBSIO. h_GBSIOInit = new NativeDetour( libmgba.GetFunction("GBSIOInit"), typeof(MGBALink).GetMethod("GBSIOInit", BindingFlags.NonPublic | BindingFlags.Static) ); orig_GBSIOInit = h_GBSIOInit.GenerateTrampoline <d_GBSIOInit>(); // Hook GBSIODeinit to properly dispose everything. h_GBSIODeinit = new NativeDetour( libmgba.GetFunction("GBSIODeinit"), typeof(MGBALink).GetMethod("GBSIODeinit", BindingFlags.NonPublic | BindingFlags.Static) ); orig_GBSIODeinit = h_GBSIODeinit.GenerateTrampoline <d_GBSIODeinit>(); // Hook mTimingInit (called via non-exported GBInit) to set up any timing-related stuff and the driver. h_mTimingInit = new NativeDetour( libmgba.GetFunction("mTimingInit"), typeof(MGBALink).GetMethod("mTimingInit", BindingFlags.NonPublic | BindingFlags.Static) ); orig_mTimingInit = h_mTimingInit.GenerateTrampoline <d_mTimingInit>(); // Hook mCoreConfigLoadDefaults to change the configs before loading them. h_mCoreConfigLoadDefaults = new NativeDetour( libmgba.GetFunction("mCoreConfigLoadDefaults"), typeof(MGBALink).GetMethod("mCoreConfigLoadDefaults", BindingFlags.NonPublic | BindingFlags.Static) ); orig_mCoreConfigLoadDefaults = h_mCoreConfigLoadDefaults.GenerateTrampoline <d_mCoreConfigLoadDefaults>(); // Hook mSDLAttachPlayer to hook the renderer's runloop. // This is required to fix any managed runtime <-> unmanaged state bugs. h_mSDLAttachPlayer = new NativeDetour( libmgbasdl.GetFunction("mSDLAttachPlayer"), typeof(MGBALink).GetMethod("mSDLAttachPlayer", BindingFlags.NonPublic | BindingFlags.Static) ); orig_mSDLAttachPlayer = h_mSDLAttachPlayer.GenerateTrampoline <d_mSDLAttachPlayer>(); // Hook mSDLInitAudio to force our own sample rate. h_mSDLInitAudio = new NativeDetour( libmgbasdl.GetFunction("mSDLInitAudio"), typeof(MGBALink).GetMethod("mSDLInitAudio", BindingFlags.NonPublic | BindingFlags.Static) ); orig_mSDLInitAudio = h_mSDLInitAudio.GenerateTrampoline <d_mSDLInitAudio>(); // Setup the custom GBSIODriver, responsible for syncing dequeues. Driver = (GBSIODriver *)Marshal.AllocHGlobal(Marshal.SizeOf(typeof(GBSIODriver))); // Slow but functional zeroing. for (int i = 0; i < sizeof(mTimingEvent); i++) { Driver->p = (GBSIO *)IntPtr.Zero; } Driver->init = PinnedPtr <GBSIODriver.d_init>(DriverInit); Driver->deinit = PinnedPtr <GBSIODriver.d_deinit>(DriverDeinit); Driver->writeSB = PinnedPtr <GBSIODriver.d_writeSB>(DriverWriteSB); Driver->writeSC = PinnedPtr <GBSIODriver.d_writeSC>(DriverWriteSC); // Setup the queue check event. QueueCheckEvent = (mTimingEvent *)Marshal.AllocHGlobal(sizeof(mTimingEvent)); // Slow but functional zeroing. for (int i = 0; i < sizeof(mTimingEvent); i++) { *((byte *)((IntPtr)QueueCheckEvent + i)) = 0x00; } QueueCheckEvent->context = (void *)IntPtr.Zero; QueueCheckEvent->name = (byte *)Marshal.StringToHGlobalAnsi("MidiToGBG Queue Check"); QueueCheckEvent->callback = PinnedPtr <mTimingEvent.d_callback>(QueueCheck); QueueCheckEvent->priority = 0x30; } Links.Add(this); }
private static void DriverWriteSB(GBSIODriver *driver, byte value) { Console.WriteLine($"DRIVR SB 0x{value.ToString("X2")}"); }
private static void DriverDeinit(GBSIODriver *driver) { Console.WriteLine("DRIVR -"); }
private static bool DriverInit(GBSIODriver *driver) { Console.WriteLine("DRIVR +"); return(true); }
public static void GBSIOSetDriver(GBSIO *sio, GBSIODriver *driver) => INTERNAL_GBSIOSetDriver((IntPtr)sio, (IntPtr)driver);