diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs index a808455764..0faf37d3dc 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs @@ -28,13 +28,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES //speedups to deploy later: //todo - convey rom data faster than pipe blob (use shared memory) (WARNING: right now our general purpose shared memory is only 1MB. maybe wait until ring buffer IPC) - //todo - collapse input messages to one IPC operation. right now theresl ike 30 of them + //todo - collapse input messages to one IPC operation. right now theres like 30 of them //todo - collect all memory block names whenever a memory block is alloc/dealloced. that way we avoid the overhead when using them for gui stuff (gfx debugger, hex editor) - + + InstanceDll instanceDll; string InstanceName; - Process process; - System.Threading.EventWaitHandle watchdogEvent; NamedPipeServerStream pipe; BinaryWriter bwPipe; BinaryReader brPipe; @@ -49,46 +48,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] public static unsafe extern void* CopyMemory(void* dest, void* src, ulong count); - static bool DryRun(string exePath) + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate void DllInit(string ipcname); + + public LibsnesApi(string dllPath) { - ProcessStartInfo oInfo = new ProcessStartInfo(exePath, "Bongizong"); - oInfo.WorkingDirectory = Path.GetDirectoryName(exePath); - oInfo.UseShellExecute = false; - oInfo.CreateNoWindow = true; - oInfo.RedirectStandardOutput = true; - oInfo.RedirectStandardError = true; - - Process proc = System.Diagnostics.Process.Start(oInfo); - string result = proc.StandardError.ReadToEnd(); - proc.WaitForExit(); - - //yongou chonganong nongo tong rongeadong - //pongigong chong hongi nonge songe - if (result == "Honga Wongkong" && proc.ExitCode == 0x16817) - return true; - - return false; - } - - static HashSet okExes = new HashSet(); - public LibsnesApi(string exePath) - { - //make sure we've checked this exe for OKness.. the dry run should keep us from freezing up or crashing weirdly if the external process isnt correct - if (!okExes.Contains(exePath)) - { - bool ok = DryRun(exePath); - if (!ok) - throw new InvalidOperationException(string.Format("Couldn't launch {0} to run SNES core. Not sure why this would have happened. Try redownloading BizHawk first.", Path.GetFileName(exePath))); - okExes.Add(exePath); - } - InstanceName = "libsneshawk_" + Guid.NewGuid().ToString(); -#if DEBUG - //use this to get a debug console with libsnes output - InstanceName = "console-" + InstanceName; -#endif - var pipeName = InstanceName; mmf = MemoryMappedFile.CreateNew(pipeName, 1024 * 1024); @@ -97,19 +63,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES pipe = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.None, 1024 * 1024, 1024); - //slim chance this might be useful sometimes: - //http://stackoverflow.com/questions/2590334/creating-a-cross-process-eventwaithandle - //create an event for the child process to monitor with a watchdog, to make sure it terminates when the emuhawk process terminates. - //NOTE: this is alarming! for some reason .net releases this event when it gets finalized, instead of when i (dont) dispose it. - bool createdNew; - watchdogEvent = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.AutoReset, InstanceName + "-event", out createdNew); - - process = new Process(); - process.StartInfo.WorkingDirectory = Path.GetDirectoryName(exePath); - process.StartInfo.FileName = exePath; - process.StartInfo.Arguments = pipeName; - process.StartInfo.ErrorDialog = true; - process.Start(); + instanceDll = new InstanceDll(dllPath); + var dllinit = (DllInit)Marshal.GetDelegateForFunctionPointer(instanceDll.GetProcAddress("DllInit"), typeof(DllInit)); + dllinit(pipeName); //TODO - start a thread to wait for process to exit and gracefully handle errors? how about the pipe? @@ -142,10 +98,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES public void Dispose() { - watchdogEvent.Dispose(); - process.Kill(); - process.Dispose(); - process = null; + WritePipeMessage(eMessage.eMessage_Shutdown); + WaitForCompletion(); + instanceDll.Dispose(); + pipe.Dispose(); mmva.Dispose(); mmf.Dispose(); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_Enums.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_Enums.cs index 640ba12c10..a1d30bef5c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_Enums.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi_Enums.cs @@ -14,6 +14,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES eMessage_BeginBufferIO, eMessage_EndBufferIO, eMessage_ResumeAfterBRK, + eMessage_Shutdown, eMessage_QUERY_library_id, eMessage_QUERY_library_revision_major, diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs index 534d2c8ae8..0078b9c861 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs @@ -57,7 +57,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES this.SyncSettings = (SnesSyncSettings)SyncSettings ?? new SnesSyncSettings(); api = new LibsnesApi(GetExePath()); - api.CMD_init(); api.ReadHook = ReadHook; api.ExecHook = ExecHook; api.WriteHook = WriteHook; @@ -384,7 +383,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES // if (Win32.Is64BitOperatingSystem) // bits = "64"; - var exename = "libsneshawk-" + bits + "-" + CurrentProfile.ToLower() + ".exe"; + var exename = "libsneshawk-" + bits + "-" + CurrentProfile.ToLower() + ".dll"; string exePath = Path.Combine(CoreComm.CoreFileProvider.DllPath(), exename); diff --git a/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp b/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp index 04b06cce77..41554e3625 100644 --- a/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp +++ b/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp @@ -46,6 +46,7 @@ enum eMessage : int32 eMessage_BeginBufferIO, eMessage_EndBufferIO, eMessage_ResumeAfterBRK, //resumes execution of the core, after a BRK. no change to current CMD + eMessage_Shutdown, eMessage_QUERY_library_id, eMessage_QUERY_library_revision_major, @@ -344,50 +345,9 @@ public: } }; //class IPCRingBuffer - -class Watchdog -{ -public: - void Start(const char* _eventName) - { - HANDLE thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadProc, this, 0, NULL); - SetThreadPriority(thread,THREAD_PRIORITY_LOWEST); - eventName = _eventName; - } - -private: - - std::string eventName; - - - static DWORD ThreadProc(LPVOID lpParam) - { - Watchdog* w = (Watchdog*)lpParam; - for(;;) - { - //only check once per second - Sleep(1000); - - //try opening the handle. if its gone, the process is gone - HANDLE hEvent = OpenEvent(SYNCHRONIZE | EVENT_ALL_ACCESS, FALSE, w->eventName.c_str()); - - //printf("event handle: %08X (%d)\n",hEvent,hEvent?0:GetLastError()); //debugging - - //handle was gone, terminate process - if(hEvent == 0) - { - TerminateProcess(INVALID_HANDLE_VALUE,0); - } - - CloseHandle(hEvent); - } - } -}; //class Watchdog - static bool bufio = false; static IPCRingBuffer *rbuf = NULL, *wbuf = NULL; -Watchdog s_Watchdog; HANDLE hPipe, hMapFile, hEvent; void* hMapFilePtr; static bool running = false; @@ -640,6 +600,8 @@ void* implementation_snes_allocSharedMemory() memHandleTable[ptr] = smb; s_EmulationControl.cb_allocSharedMemory_params.result = ptr; + + return ptr; } void* snes_allocSharedMemory(const char* memtype, size_t amt) @@ -961,7 +923,7 @@ void Handle_SIG_audio_flush() audiobuffer_idx = 0; } -void RunControlMessageLoop() +void MessageLoop() { for(;;) { @@ -1087,6 +1049,10 @@ HANDLEMESSAGES: { case eMessage_BRK_Complete: return; + + case eMessage_Shutdown: + //terminate this dll process + return; case eMessage_SetBuffer: { @@ -1112,6 +1078,15 @@ HANDLEMESSAGES: } } +static DWORD WINAPI ThreadProc(_In_ LPVOID lpParameter) +{ + MessageLoop(); + //send a message to the other thread to synchronize the shutdown of this thread + //after that message is received, this thread (and the whole dll instance) is dead. + WritePipe(eMessage_BRK_Complete); + return 0; +} + void OpenConsole() { @@ -1237,43 +1212,31 @@ void emuthread() } } -int xmain(int argc, char** argv) +BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved) { - if(argc != 2) - { - printf("This program is run from the libsneshawk emulator core. It is useless to you directly."); - exit(1); - } + return TRUE; +} - if(!strcmp(argv[1],"Bongizong")) - { - fprintf(stderr,"Honga Wongkong"); - exit(0x16817); - } +extern "C" dllexport bool __cdecl DllInit(const char* ipcname) +{ + printf("NEW INSTANCE: %08X\n", &s_EmulationControl); char pipename[256]; char eventname[256]; - sprintf(pipename, "\\\\.\\Pipe\\%s",argv[1]); - sprintf(eventname, "%s-event",argv[1]); - - if(!strncmp(argv[1],"console",7)) - { - OpenConsole(); - } + sprintf(pipename, "\\\\.\\Pipe\\%s",ipcname); + sprintf(eventname, "%s-event", ipcname); printf("pipe: %s\n",pipename); printf("event: %s\n",eventname); - s_Watchdog.Start(eventname); - hPipe = CreateFile(pipename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if(hPipe == INVALID_HANDLE_VALUE) - return 1; + return false; - hMapFile = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, argv[1]); + hMapFile = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, ipcname); if(hMapFile == INVALID_HANDLE_VALUE) - return 1; + return false; hMapFilePtr = MapViewOfFile(hMapFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); @@ -1284,26 +1247,12 @@ int xmain(int argc, char** argv) running = true; printf("running\n"); - RunControlMessageLoop(); - - return 0; + DWORD tid; + CreateThread(nullptr, 0, &ThreadProc, nullptr, 0, &tid); + + return true; } -int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) -{ - int argc = __argc; - char** argv = __argv; - if(argc != 2) - { - if(IDOK == MessageBox(0,"This program is run from the libsneshawk emulator core. It is useless to you directly. But if you're really, that curious, click OK.","Whatfor my daddy-o",MB_OKCANCEL)) - { - ShellExecute(0,"open","http://www.youtube.com/watch?v=boanuwUMNNQ#t=98s",NULL,NULL,SW_SHOWNORMAL); - } - exit(1); - - } - xmain(argc,argv); -} void pwrap_init() { diff --git a/libsnes/vs2015/libsnes.vcxproj b/libsnes/vs2015/libsnes.vcxproj index a87cff74d2..fc4d162fd9 100644 --- a/libsnes/vs2015/libsnes.vcxproj +++ b/libsnes/vs2015/libsnes.vcxproj @@ -22,29 +22,30 @@ {488B77AD-58DF-4E01-9329-67B20D486860} libsnes 8.1 + 8.1 - Application + DynamicLibrary true v140_xp NotSet - Application + DynamicLibrary true v140_xp NotSet - Application + DynamicLibrary false v140_xp true NotSet - Application + DynamicLibrary false v140_xp true diff --git a/output/dll/libsneshawk-32-compatibility.dll b/output/dll/libsneshawk-32-compatibility.dll new file mode 100644 index 0000000000..7e91cbd937 Binary files /dev/null and b/output/dll/libsneshawk-32-compatibility.dll differ diff --git a/output/dll/libsneshawk-32-compatibility.exe b/output/dll/libsneshawk-32-compatibility.exe deleted file mode 100644 index d211acd567..0000000000 Binary files a/output/dll/libsneshawk-32-compatibility.exe and /dev/null differ diff --git a/output/dll/libsneshawk-32-performance.dll b/output/dll/libsneshawk-32-performance.dll new file mode 100644 index 0000000000..29758965e9 Binary files /dev/null and b/output/dll/libsneshawk-32-performance.dll differ diff --git a/output/dll/libsneshawk-32-performance.exe b/output/dll/libsneshawk-32-performance.exe deleted file mode 100644 index 8134510e54..0000000000 Binary files a/output/dll/libsneshawk-32-performance.exe and /dev/null differ