bsnes is a dll once more. this is experimental--it may not work well, so I need to gather data. But I think it might work fine.
This commit is contained in:
parent
1256419cc1
commit
817f4360d3
|
@ -28,13 +28,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||||
|
|
||||||
//speedups to deploy later:
|
//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 - 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)
|
//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;
|
string InstanceName;
|
||||||
Process process;
|
|
||||||
System.Threading.EventWaitHandle watchdogEvent;
|
|
||||||
NamedPipeServerStream pipe;
|
NamedPipeServerStream pipe;
|
||||||
BinaryWriter bwPipe;
|
BinaryWriter bwPipe;
|
||||||
BinaryReader brPipe;
|
BinaryReader brPipe;
|
||||||
|
@ -49,46 +48,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||||
[DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
|
[DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
|
||||||
public static unsafe extern void* CopyMemory(void* dest, void* src, ulong count);
|
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<string> okExes = new HashSet<string>();
|
|
||||||
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();
|
InstanceName = "libsneshawk_" + Guid.NewGuid().ToString();
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
//use this to get a debug console with libsnes output
|
|
||||||
InstanceName = "console-" + InstanceName;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
var pipeName = InstanceName;
|
var pipeName = InstanceName;
|
||||||
|
|
||||||
mmf = MemoryMappedFile.CreateNew(pipeName, 1024 * 1024);
|
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);
|
pipe = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.None, 1024 * 1024, 1024);
|
||||||
|
|
||||||
//slim chance this might be useful sometimes:
|
instanceDll = new InstanceDll(dllPath);
|
||||||
//http://stackoverflow.com/questions/2590334/creating-a-cross-process-eventwaithandle
|
var dllinit = (DllInit)Marshal.GetDelegateForFunctionPointer(instanceDll.GetProcAddress("DllInit"), typeof(DllInit));
|
||||||
//create an event for the child process to monitor with a watchdog, to make sure it terminates when the emuhawk process terminates.
|
dllinit(pipeName);
|
||||||
//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();
|
|
||||||
|
|
||||||
//TODO - start a thread to wait for process to exit and gracefully handle errors? how about the pipe?
|
//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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
watchdogEvent.Dispose();
|
WritePipeMessage(eMessage.eMessage_Shutdown);
|
||||||
process.Kill();
|
WaitForCompletion();
|
||||||
process.Dispose();
|
instanceDll.Dispose();
|
||||||
process = null;
|
|
||||||
pipe.Dispose();
|
pipe.Dispose();
|
||||||
mmva.Dispose();
|
mmva.Dispose();
|
||||||
mmf.Dispose();
|
mmf.Dispose();
|
||||||
|
|
|
@ -14,6 +14,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||||
eMessage_BeginBufferIO,
|
eMessage_BeginBufferIO,
|
||||||
eMessage_EndBufferIO,
|
eMessage_EndBufferIO,
|
||||||
eMessage_ResumeAfterBRK,
|
eMessage_ResumeAfterBRK,
|
||||||
|
eMessage_Shutdown,
|
||||||
|
|
||||||
eMessage_QUERY_library_id,
|
eMessage_QUERY_library_id,
|
||||||
eMessage_QUERY_library_revision_major,
|
eMessage_QUERY_library_revision_major,
|
||||||
|
|
|
@ -57,7 +57,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||||
this.SyncSettings = (SnesSyncSettings)SyncSettings ?? new SnesSyncSettings();
|
this.SyncSettings = (SnesSyncSettings)SyncSettings ?? new SnesSyncSettings();
|
||||||
|
|
||||||
api = new LibsnesApi(GetExePath());
|
api = new LibsnesApi(GetExePath());
|
||||||
api.CMD_init();
|
|
||||||
api.ReadHook = ReadHook;
|
api.ReadHook = ReadHook;
|
||||||
api.ExecHook = ExecHook;
|
api.ExecHook = ExecHook;
|
||||||
api.WriteHook = WriteHook;
|
api.WriteHook = WriteHook;
|
||||||
|
@ -384,7 +383,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||||
// if (Win32.Is64BitOperatingSystem)
|
// if (Win32.Is64BitOperatingSystem)
|
||||||
// bits = "64";
|
// bits = "64";
|
||||||
|
|
||||||
var exename = "libsneshawk-" + bits + "-" + CurrentProfile.ToLower() + ".exe";
|
var exename = "libsneshawk-" + bits + "-" + CurrentProfile.ToLower() + ".dll";
|
||||||
|
|
||||||
string exePath = Path.Combine(CoreComm.CoreFileProvider.DllPath(), exename);
|
string exePath = Path.Combine(CoreComm.CoreFileProvider.DllPath(), exename);
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ enum eMessage : int32
|
||||||
eMessage_BeginBufferIO,
|
eMessage_BeginBufferIO,
|
||||||
eMessage_EndBufferIO,
|
eMessage_EndBufferIO,
|
||||||
eMessage_ResumeAfterBRK, //resumes execution of the core, after a BRK. no change to current CMD
|
eMessage_ResumeAfterBRK, //resumes execution of the core, after a BRK. no change to current CMD
|
||||||
|
eMessage_Shutdown,
|
||||||
|
|
||||||
eMessage_QUERY_library_id,
|
eMessage_QUERY_library_id,
|
||||||
eMessage_QUERY_library_revision_major,
|
eMessage_QUERY_library_revision_major,
|
||||||
|
@ -344,50 +345,9 @@ public:
|
||||||
}
|
}
|
||||||
}; //class IPCRingBuffer
|
}; //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 bool bufio = false;
|
||||||
static IPCRingBuffer *rbuf = NULL, *wbuf = NULL;
|
static IPCRingBuffer *rbuf = NULL, *wbuf = NULL;
|
||||||
|
|
||||||
Watchdog s_Watchdog;
|
|
||||||
HANDLE hPipe, hMapFile, hEvent;
|
HANDLE hPipe, hMapFile, hEvent;
|
||||||
void* hMapFilePtr;
|
void* hMapFilePtr;
|
||||||
static bool running = false;
|
static bool running = false;
|
||||||
|
@ -640,6 +600,8 @@ void* implementation_snes_allocSharedMemory()
|
||||||
memHandleTable[ptr] = smb;
|
memHandleTable[ptr] = smb;
|
||||||
|
|
||||||
s_EmulationControl.cb_allocSharedMemory_params.result = ptr;
|
s_EmulationControl.cb_allocSharedMemory_params.result = ptr;
|
||||||
|
|
||||||
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* snes_allocSharedMemory(const char* memtype, size_t amt)
|
void* snes_allocSharedMemory(const char* memtype, size_t amt)
|
||||||
|
@ -961,7 +923,7 @@ void Handle_SIG_audio_flush()
|
||||||
audiobuffer_idx = 0;
|
audiobuffer_idx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunControlMessageLoop()
|
void MessageLoop()
|
||||||
{
|
{
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
|
@ -1087,6 +1049,10 @@ HANDLEMESSAGES:
|
||||||
{
|
{
|
||||||
case eMessage_BRK_Complete:
|
case eMessage_BRK_Complete:
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case eMessage_Shutdown:
|
||||||
|
//terminate this dll process
|
||||||
|
return;
|
||||||
|
|
||||||
case eMessage_SetBuffer:
|
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()
|
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)
|
return TRUE;
|
||||||
{
|
}
|
||||||
printf("This program is run from the libsneshawk emulator core. It is useless to you directly.");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!strcmp(argv[1],"Bongizong"))
|
extern "C" dllexport bool __cdecl DllInit(const char* ipcname)
|
||||||
{
|
{
|
||||||
fprintf(stderr,"Honga Wongkong");
|
printf("NEW INSTANCE: %08X\n", &s_EmulationControl);
|
||||||
exit(0x16817);
|
|
||||||
}
|
|
||||||
|
|
||||||
char pipename[256];
|
char pipename[256];
|
||||||
char eventname[256];
|
char eventname[256];
|
||||||
sprintf(pipename, "\\\\.\\Pipe\\%s",argv[1]);
|
sprintf(pipename, "\\\\.\\Pipe\\%s",ipcname);
|
||||||
sprintf(eventname, "%s-event",argv[1]);
|
sprintf(eventname, "%s-event", ipcname);
|
||||||
|
|
||||||
if(!strncmp(argv[1],"console",7))
|
|
||||||
{
|
|
||||||
OpenConsole();
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("pipe: %s\n",pipename);
|
printf("pipe: %s\n",pipename);
|
||||||
printf("event: %s\n",eventname);
|
printf("event: %s\n",eventname);
|
||||||
|
|
||||||
s_Watchdog.Start(eventname);
|
|
||||||
|
|
||||||
hPipe = CreateFile(pipename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
hPipe = CreateFile(pipename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||||
|
|
||||||
if(hPipe == INVALID_HANDLE_VALUE)
|
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)
|
if(hMapFile == INVALID_HANDLE_VALUE)
|
||||||
return 1;
|
return false;
|
||||||
|
|
||||||
hMapFilePtr = MapViewOfFile(hMapFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
|
hMapFilePtr = MapViewOfFile(hMapFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
|
||||||
|
|
||||||
|
@ -1284,26 +1247,12 @@ int xmain(int argc, char** argv)
|
||||||
running = true;
|
running = true;
|
||||||
printf("running\n");
|
printf("running\n");
|
||||||
|
|
||||||
RunControlMessageLoop();
|
DWORD tid;
|
||||||
|
CreateThread(nullptr, 0, &ThreadProc, nullptr, 0, &tid);
|
||||||
return 0;
|
|
||||||
|
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()
|
void pwrap_init()
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,29 +22,30 @@
|
||||||
<ProjectGuid>{488B77AD-58DF-4E01-9329-67B20D486860}</ProjectGuid>
|
<ProjectGuid>{488B77AD-58DF-4E01-9329-67B20D486860}</ProjectGuid>
|
||||||
<RootNamespace>libsnes</RootNamespace>
|
<RootNamespace>libsnes</RootNamespace>
|
||||||
<TargetPlatformVersion>8.1</TargetPlatformVersion>
|
<TargetPlatformVersion>8.1</TargetPlatformVersion>
|
||||||
|
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Performance|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Performance|Win32'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
<PlatformToolset>v140_xp</PlatformToolset>
|
<PlatformToolset>v140_xp</PlatformToolset>
|
||||||
<CharacterSet>NotSet</CharacterSet>
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Compatibility|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Compatibility|Win32'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
<PlatformToolset>v140_xp</PlatformToolset>
|
<PlatformToolset>v140_xp</PlatformToolset>
|
||||||
<CharacterSet>NotSet</CharacterSet>
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-Performance|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-Performance|Win32'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
<PlatformToolset>v140_xp</PlatformToolset>
|
<PlatformToolset>v140_xp</PlatformToolset>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
<CharacterSet>NotSet</CharacterSet>
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-Compatibility|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-Compatibility|Win32'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
<PlatformToolset>v140_xp</PlatformToolset>
|
<PlatformToolset>v140_xp</PlatformToolset>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue