Cleanup internals of comms APIs
This commit is contained in:
parent
d94ce80eaa
commit
86e82b97c1
|
@ -0,0 +1,97 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BizHawk.Client.Common
|
||||||
|
{
|
||||||
|
public sealed class HttpCommunication
|
||||||
|
{
|
||||||
|
private readonly HttpClient _client = new HttpClient();
|
||||||
|
|
||||||
|
private readonly Func<byte[]> _takeScreenshotCallback;
|
||||||
|
|
||||||
|
public int DefaultTimeout { get; set; } = 500;
|
||||||
|
|
||||||
|
public string GetUrl;
|
||||||
|
|
||||||
|
public string PostUrl;
|
||||||
|
|
||||||
|
public int Timeout { get; set; }
|
||||||
|
|
||||||
|
public HttpCommunication(Func<byte[]> takeScreenshotCallback, string getURL, string postURL)
|
||||||
|
{
|
||||||
|
_takeScreenshotCallback = takeScreenshotCallback;
|
||||||
|
GetUrl = getURL;
|
||||||
|
PostUrl = postURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ExecGet(string url = null) => Get(url ?? GetUrl).Result;
|
||||||
|
|
||||||
|
public string ExecPost(string url = null, string payload = "") => Post(
|
||||||
|
url ?? PostUrl,
|
||||||
|
new FormUrlEncodedContent(new Dictionary<string, string> { ["payload"] = payload })
|
||||||
|
).Result;
|
||||||
|
|
||||||
|
public async Task<string> Get(string url)
|
||||||
|
{
|
||||||
|
_client.DefaultRequestHeaders.ConnectionClose = false;
|
||||||
|
var response = await _client.GetAsync(url).ConfigureAwait(false);
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return await response.Content.ReadAsStringAsync();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> Post(string url, FormUrlEncodedContent content)
|
||||||
|
{
|
||||||
|
_client.DefaultRequestHeaders.ConnectionClose = true;
|
||||||
|
HttpResponseMessage response;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response = await _client.PostAsync(url, content).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return e.ToString();
|
||||||
|
|
||||||
|
}
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return await response.Content.ReadAsStringAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SendScreenshot(string url = null, string parameter = "screenshot")
|
||||||
|
{
|
||||||
|
var url1 = url ?? PostUrl;
|
||||||
|
var content = new FormUrlEncodedContent(new Dictionary<string, string> { [parameter] = Convert.ToBase64String(_takeScreenshotCallback()) });
|
||||||
|
Task<string> postResponse = null;
|
||||||
|
var trials = 5;
|
||||||
|
while (postResponse == null && trials > 0)
|
||||||
|
{
|
||||||
|
postResponse = Post(url1, content);
|
||||||
|
trials -= 1;
|
||||||
|
}
|
||||||
|
return postResponse?.Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTimeout(int timeout)
|
||||||
|
{
|
||||||
|
if (Timeout == 0 && timeout == 0)
|
||||||
|
{
|
||||||
|
Timeout = DefaultTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout != 0)
|
||||||
|
{
|
||||||
|
_client.Timeout = new TimeSpan(0, 0, 0, timeout / 1000, timeout % 1000);
|
||||||
|
Timeout = timeout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TestGet() => Get(GetUrl).Result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO.MemoryMappedFiles;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BizHawk.Client.Common
|
||||||
|
{
|
||||||
|
public sealed class MemoryMappedFiles
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, MemoryMappedFile> _mmfFiles = new Dictionary<string, MemoryMappedFile>();
|
||||||
|
|
||||||
|
private readonly Func<byte[]> _takeScreenshotCallback;
|
||||||
|
|
||||||
|
public string Filename;
|
||||||
|
|
||||||
|
public MemoryMappedFiles(Func<byte[]> takeScreenshotCallback, string filename)
|
||||||
|
{
|
||||||
|
_takeScreenshotCallback = takeScreenshotCallback;
|
||||||
|
Filename = filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ReadFromFile(string filename, int expectedSize)
|
||||||
|
{
|
||||||
|
using var viewAccessor = MemoryMappedFile.OpenExisting(filename).CreateViewAccessor();
|
||||||
|
var bytes = new byte[expectedSize];
|
||||||
|
viewAccessor.ReadArray(0, bytes, 0, expectedSize);
|
||||||
|
return Encoding.UTF8.GetString(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ScreenShotToFile() => WriteToFile(Filename, _takeScreenshotCallback());
|
||||||
|
|
||||||
|
public int WriteToFile(string filename, byte[] outputBytes)
|
||||||
|
{
|
||||||
|
int TryWrite(MemoryMappedFile m)
|
||||||
|
{
|
||||||
|
using var accessor = m.CreateViewAccessor(0, outputBytes.Length, MemoryMappedFileAccess.Write);
|
||||||
|
accessor.WriteArray(0, outputBytes, 0, outputBytes.Length);
|
||||||
|
return outputBytes.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_mmfFiles.TryGetValue(filename, out var mmfFile))
|
||||||
|
{
|
||||||
|
mmfFile = _mmfFiles[filename] = MemoryMappedFile.CreateOrOpen(filename, outputBytes.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return TryWrite(mmfFile);
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mmfFile.Dispose();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
//TODO are Dispose() implementations allowed to throw? does this one ever throw? --yoshi
|
||||||
|
}
|
||||||
|
return TryWrite(_mmfFiles[filename] = MemoryMappedFile.CreateOrOpen(filename, outputBytes.Length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BizHawk.Client.Common
|
||||||
|
{
|
||||||
|
public sealed class SocketServer
|
||||||
|
{
|
||||||
|
private IPEndPoint _remoteEp;
|
||||||
|
|
||||||
|
private Socket _soc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||||
|
|
||||||
|
private readonly Func<byte[]> _takeScreenshotCallback;
|
||||||
|
|
||||||
|
private (string HostIP, int Port) _targetAddr;
|
||||||
|
|
||||||
|
public bool Connected { get; private set; }
|
||||||
|
|
||||||
|
public string IP
|
||||||
|
{
|
||||||
|
get => _targetAddr.HostIP;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_targetAddr.HostIP = value;
|
||||||
|
Connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Port
|
||||||
|
{
|
||||||
|
get => _targetAddr.Port;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_targetAddr.Port = value;
|
||||||
|
Connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if true
|
||||||
|
private const int Retries = 10;
|
||||||
|
#else
|
||||||
|
public int Retries { get; set; } = 10;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public bool Successful { get; private set; }
|
||||||
|
|
||||||
|
public SocketServer(Func<byte[]> takeScreenshotCallback, string ip, int port)
|
||||||
|
{
|
||||||
|
_takeScreenshotCallback = takeScreenshotCallback;
|
||||||
|
_targetAddr = (ip, port);
|
||||||
|
Connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Connect()
|
||||||
|
{
|
||||||
|
_remoteEp = new IPEndPoint(IPAddress.Parse(_targetAddr.HostIP), _targetAddr.Port);
|
||||||
|
_soc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||||
|
_soc.Connect(_remoteEp);
|
||||||
|
Connected = true;
|
||||||
|
_soc.ReceiveTimeout = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetInfo() => $"{_targetAddr.HostIP}:{_targetAddr.Port}";
|
||||||
|
|
||||||
|
public string ReceiveMessage()
|
||||||
|
{
|
||||||
|
if (!Connected)
|
||||||
|
{
|
||||||
|
Connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp = "";
|
||||||
|
var receivedBytes = new byte[256];
|
||||||
|
var receivedLength = 1;
|
||||||
|
while (receivedLength > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
receivedLength = _soc.Receive(receivedBytes, receivedBytes.Length, 0);
|
||||||
|
resp += Encoding.ASCII.GetString(receivedBytes);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
receivedLength = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int SendBytes(byte[] sendBytes)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _soc.Send(sendBytes);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SendScreenshot(int waitingTime = 0)
|
||||||
|
{
|
||||||
|
var bmpBytes = _takeScreenshotCallback();
|
||||||
|
var sentBytes = 0;
|
||||||
|
var tries = 0;
|
||||||
|
while (sentBytes <= 0 && tries < Retries)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tries++;
|
||||||
|
sentBytes = SendBytes(bmpBytes);
|
||||||
|
}
|
||||||
|
catch (SocketException)
|
||||||
|
{
|
||||||
|
Connect();
|
||||||
|
sentBytes = 0;
|
||||||
|
}
|
||||||
|
if (sentBytes == -1)
|
||||||
|
{
|
||||||
|
Connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Successful = tries < Retries;
|
||||||
|
if (waitingTime == 0)
|
||||||
|
{
|
||||||
|
return Successful ? "Screenshot was sent" : "Screenshot could not be sent";
|
||||||
|
}
|
||||||
|
var resp = ReceiveMessage();
|
||||||
|
return resp == "" ? "Failed to get a response" : resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int SendString(string sendString)
|
||||||
|
{
|
||||||
|
var sentBytes = SendBytes(Encoding.ASCII.GetBytes(sendString));
|
||||||
|
Successful = sentBytes > 0;
|
||||||
|
return sentBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTimeout(int timeout) => _soc.ReceiveTimeout = timeout;
|
||||||
|
|
||||||
|
public void SocketConnected() => Connected = !_soc.Poll(1000, SelectMode.SelectRead) || _soc.Available != 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,15 +14,15 @@ namespace BizHawk.Client.EmuHawk
|
||||||
|
|
||||||
public string SocketServerResponse() => GlobalWin.socketServer.ReceiveMessage();
|
public string SocketServerResponse() => GlobalWin.socketServer.ReceiveMessage();
|
||||||
|
|
||||||
public bool SocketServerSuccessful() => GlobalWin.socketServer.Successful();
|
public bool SocketServerSuccessful() => GlobalWin.socketServer.Successful;
|
||||||
|
|
||||||
public void SocketServerSetTimeout(int timeout) => GlobalWin.socketServer.SetTimeout(timeout);
|
public void SocketServerSetTimeout(int timeout) => GlobalWin.socketServer.SetTimeout(timeout);
|
||||||
|
|
||||||
public void SocketServerSetIp(string ip) => GlobalWin.socketServer.Ip = ip;
|
public void SocketServerSetIp(string ip) => GlobalWin.socketServer.IP = ip;
|
||||||
|
|
||||||
public void SetSocketServerPort(int port) => GlobalWin.socketServer.Port = port;
|
public void SetSocketServerPort(int port) => GlobalWin.socketServer.Port = port;
|
||||||
|
|
||||||
public string SocketServerGetIp() => GlobalWin.socketServer.Ip;
|
public string SocketServerGetIp() => GlobalWin.socketServer.IP;
|
||||||
|
|
||||||
public int SocketServerGetPort() => GlobalWin.socketServer.Port;
|
public int SocketServerGetPort() => GlobalWin.socketServer.Port;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
|
using BizHawk.Client.Common;
|
||||||
|
|
||||||
namespace BizHawk.Client.EmuHawk
|
namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
public class ArgParser
|
public class ArgParser
|
||||||
|
@ -36,7 +38,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
public bool? audiosync = null;
|
public bool? audiosync = null;
|
||||||
|
|
||||||
/// <exception cref="ArgParserException"><c>--socket_ip</c> passed without specifying <c>--socket_port</c> or vice-versa</exception>
|
/// <exception cref="ArgParserException"><c>--socket_ip</c> passed without specifying <c>--socket_port</c> or vice-versa</exception>
|
||||||
public void ParseArguments(string[] args)
|
public void ParseArguments(string[] args, Func<byte[]> takeScreenshotCallback)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < args.Length; i++)
|
for (int i = 0; i < args.Length; i++)
|
||||||
{
|
{
|
||||||
|
@ -147,38 +149,23 @@ namespace BizHawk.Client.EmuHawk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//initialize HTTP communication
|
GlobalWin.httpCommunication = URL_get == null && URL_post == null
|
||||||
if (URL_get != null || URL_post != null)
|
? null // don't bother
|
||||||
|
: new HttpCommunication(takeScreenshotCallback, URL_get, URL_post);
|
||||||
|
GlobalWin.memoryMappedFiles = mmf_filename == null
|
||||||
|
? null // don't bother
|
||||||
|
: new MemoryMappedFiles(takeScreenshotCallback, mmf_filename);
|
||||||
|
if (socket_ip == null && socket_port <= 0)
|
||||||
{
|
{
|
||||||
GlobalWin.httpCommunication = new Communication.HttpCommunication();
|
GlobalWin.socketServer = null; // don't bother
|
||||||
if (URL_get != null)
|
|
||||||
{
|
|
||||||
GlobalWin.httpCommunication.GetUrl = URL_get;
|
|
||||||
}
|
|
||||||
if (URL_post != null)
|
|
||||||
{
|
|
||||||
GlobalWin.httpCommunication.PostUrl = URL_post;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else if (socket_ip == null || socket_port <= 0)
|
||||||
// initialize socket server
|
|
||||||
if (socket_ip != null && socket_port > 0)
|
|
||||||
{
|
|
||||||
GlobalWin.socketServer = new Communication.SocketServer();
|
|
||||||
GlobalWin.socketServer.SetIp(socket_ip, socket_port);
|
|
||||||
}
|
|
||||||
else if (socket_ip == null ^ socket_port == 0)
|
|
||||||
{
|
{
|
||||||
throw new ArgParserException("Socket server needs both --socket_ip and --socket_port. Socket server was not started");
|
throw new ArgParserException("Socket server needs both --socket_ip and --socket_port. Socket server was not started");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
//initialize mapped memory files
|
|
||||||
if (mmf_filename != null)
|
|
||||||
{
|
{
|
||||||
GlobalWin.memoryMappedFiles = new Communication.MemoryMappedFiles
|
GlobalWin.socketServer = new SocketServer(takeScreenshotCallback, socket_ip, socket_port);
|
||||||
{
|
|
||||||
Filename = mmf_filename
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,422 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Text;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.IO.MemoryMappedFiles;
|
|
||||||
using BizHawk.Bizware.BizwareGL;
|
|
||||||
using System.Drawing;
|
|
||||||
using BizHawk.Emulation.Common;
|
|
||||||
|
|
||||||
namespace BizHawk.Client.EmuHawk
|
|
||||||
{
|
|
||||||
public static class Communication
|
|
||||||
{
|
|
||||||
public class HttpCommunication
|
|
||||||
{
|
|
||||||
private static readonly HttpClient Client = new HttpClient();
|
|
||||||
public string PostUrl { get; set; } = null;
|
|
||||||
public string GetUrl { get; set; } = null;
|
|
||||||
private readonly ScreenShot _screenShot = new ScreenShot();
|
|
||||||
public int Timeout { get; set; }
|
|
||||||
public int DefaultTimeout { get; set; } = 500;
|
|
||||||
|
|
||||||
public void SetTimeout(int timeout)
|
|
||||||
{
|
|
||||||
if (Timeout == 0 && timeout == 0)
|
|
||||||
{
|
|
||||||
Timeout = DefaultTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timeout != 0)
|
|
||||||
{
|
|
||||||
Client.Timeout = new TimeSpan(0, 0, 0, timeout / 1000, timeout % 1000);
|
|
||||||
Timeout = timeout;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> Get(string url)
|
|
||||||
{
|
|
||||||
Client.DefaultRequestHeaders.ConnectionClose = false;
|
|
||||||
HttpResponseMessage response = await Client.GetAsync(url).ConfigureAwait(false);
|
|
||||||
if (response.IsSuccessStatusCode)
|
|
||||||
{
|
|
||||||
return await response.Content.ReadAsStringAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> Post(string url, FormUrlEncodedContent content)
|
|
||||||
{
|
|
||||||
Client.DefaultRequestHeaders.ConnectionClose = true;
|
|
||||||
HttpResponseMessage response;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
response = await Client.PostAsync(url, content).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return e.ToString();
|
|
||||||
|
|
||||||
}
|
|
||||||
if (!response.IsSuccessStatusCode)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return await response.Content.ReadAsStringAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string TestGet()
|
|
||||||
{
|
|
||||||
Task<string> getResponse = Get(GetUrl);
|
|
||||||
return getResponse.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string SendScreenshot(string url, string parameter)
|
|
||||||
{
|
|
||||||
int trials = 5;
|
|
||||||
var values = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ parameter, _screenShot.GetScreenShotAsString() }
|
|
||||||
};
|
|
||||||
FormUrlEncodedContent content = new FormUrlEncodedContent(values);
|
|
||||||
|
|
||||||
Task<string> postResponse = null;
|
|
||||||
while (postResponse == null && trials > 0)
|
|
||||||
{
|
|
||||||
postResponse = Post(url, content);
|
|
||||||
trials -= 1;
|
|
||||||
}
|
|
||||||
return postResponse.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string SendScreenshot()
|
|
||||||
{
|
|
||||||
return SendScreenshot(PostUrl, "screenshot");
|
|
||||||
}
|
|
||||||
|
|
||||||
public string SendScreenshot(string url)
|
|
||||||
{
|
|
||||||
return SendScreenshot(url, "screenshot");
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ExecGet(string url)
|
|
||||||
{
|
|
||||||
return Get(url).Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ExecGet()
|
|
||||||
{
|
|
||||||
return Get(GetUrl).Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ExecPost(string url, string payload)
|
|
||||||
{
|
|
||||||
var values = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
["payload"] = payload
|
|
||||||
};
|
|
||||||
FormUrlEncodedContent content = new FormUrlEncodedContent(values);
|
|
||||||
return Post(url, content).Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ExecPost(string payload)
|
|
||||||
{
|
|
||||||
var values = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
["payload"] = payload
|
|
||||||
};
|
|
||||||
FormUrlEncodedContent content = new FormUrlEncodedContent(values);
|
|
||||||
return Post(PostUrl, content).Result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SocketServer
|
|
||||||
{
|
|
||||||
string _ip;
|
|
||||||
public string Ip
|
|
||||||
{
|
|
||||||
get => _ip;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_ip = value;
|
|
||||||
_ipAdd = IPAddress.Parse(_ip);
|
|
||||||
Connect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int _port;
|
|
||||||
public int Port
|
|
||||||
{
|
|
||||||
get => _port;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_port = value;
|
|
||||||
Connect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Socket _soc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
|
||||||
IPAddress _ipAdd;
|
|
||||||
IPEndPoint _remoteEp;
|
|
||||||
IVideoProvider _currentVideoProvider;
|
|
||||||
public bool Connected { get; set; }
|
|
||||||
public bool Initialized { get; set; }
|
|
||||||
public int Retries { get; set; } = 10;
|
|
||||||
private bool _success; // indicates whether the last command was executed successfully
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
if (_currentVideoProvider == null)
|
|
||||||
{
|
|
||||||
_currentVideoProvider = GlobalWin.Emulator.AsVideoProviderOrDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
Initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Connect()
|
|
||||||
{
|
|
||||||
_remoteEp = new IPEndPoint(_ipAdd, _port);
|
|
||||||
_soc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
|
||||||
_soc.Connect(_remoteEp);
|
|
||||||
Connected = true;
|
|
||||||
_soc.ReceiveTimeout = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetIp(string ip, int port)
|
|
||||||
{
|
|
||||||
_ip = ip;
|
|
||||||
_port = port;
|
|
||||||
_ipAdd = IPAddress.Parse(_ip);
|
|
||||||
Connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetInfo()
|
|
||||||
{
|
|
||||||
return $"{_ip}:{_port}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetTimeout(int timeout)
|
|
||||||
{
|
|
||||||
_soc.ReceiveTimeout = timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SocketConnected()
|
|
||||||
{
|
|
||||||
bool part1 = _soc.Poll(1000, SelectMode.SelectRead);
|
|
||||||
bool part2 = (_soc.Available == 0);
|
|
||||||
Connected = !(part1 && part2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int SendString(string sendString)
|
|
||||||
{
|
|
||||||
int sentBytes = SendBytes(Encoding.ASCII.GetBytes(sendString));
|
|
||||||
_success = sentBytes > 0;
|
|
||||||
return sentBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int SendBytes(byte[] sendBytes)
|
|
||||||
{
|
|
||||||
int sentBytes;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
sentBytes = _soc.Send(sendBytes);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
sentBytes = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sentBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string SendScreenshot()
|
|
||||||
{
|
|
||||||
return SendScreenshot(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string SendScreenshot(int waitingTime)
|
|
||||||
{
|
|
||||||
if (!Initialized)
|
|
||||||
{
|
|
||||||
Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
var screenShot = new ScreenShot();
|
|
||||||
using (BitmapBuffer bb = screenShot.MakeScreenShotImage())
|
|
||||||
{
|
|
||||||
using var img = bb.ToSysdrawingBitmap();
|
|
||||||
byte[] bmpBytes = screenShot.ImageToByte(img);
|
|
||||||
int sentBytes = 0;
|
|
||||||
int tries = 0;
|
|
||||||
while (sentBytes <= 0 && tries < Retries)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
tries++;
|
|
||||||
sentBytes = SendBytes(bmpBytes);
|
|
||||||
}
|
|
||||||
catch (SocketException)
|
|
||||||
{
|
|
||||||
Connect();
|
|
||||||
sentBytes = 0;
|
|
||||||
}
|
|
||||||
if (sentBytes == -1)
|
|
||||||
{
|
|
||||||
Connect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_success = tries < Retries;
|
|
||||||
}
|
|
||||||
|
|
||||||
var resp = !_success
|
|
||||||
? "Screenshot could not be sent"
|
|
||||||
: "Screenshot was sent";
|
|
||||||
|
|
||||||
if (waitingTime == 0)
|
|
||||||
{
|
|
||||||
return resp;
|
|
||||||
}
|
|
||||||
|
|
||||||
resp = ReceiveMessage();
|
|
||||||
if (resp == "")
|
|
||||||
{
|
|
||||||
resp = "Failed to get a response";
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ReceiveMessage()
|
|
||||||
{
|
|
||||||
if (!Connected)
|
|
||||||
{
|
|
||||||
Connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
string resp = "";
|
|
||||||
byte[] receivedBytes = new byte[256];
|
|
||||||
int receivedLength = 1;
|
|
||||||
|
|
||||||
while (receivedLength > 0)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
receivedLength = _soc.Receive(receivedBytes, receivedBytes.Length, 0);
|
|
||||||
resp += Encoding.ASCII.GetString(receivedBytes);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
receivedLength = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return resp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Successful()
|
|
||||||
{
|
|
||||||
return _success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MemoryMappedFiles
|
|
||||||
{
|
|
||||||
private readonly Dictionary<string, MemoryMappedFile> _mmfFiles = new Dictionary<string, MemoryMappedFile>();
|
|
||||||
|
|
||||||
public string Filename { get; set; } = "BizhawkTemp_main";
|
|
||||||
|
|
||||||
public int ScreenShotToFile()
|
|
||||||
{
|
|
||||||
ScreenShot screenShot = new ScreenShot();
|
|
||||||
var bb = screenShot.MakeScreenShotImage();
|
|
||||||
var img = bb.ToSysdrawingBitmap();
|
|
||||||
byte[] bmpBytes = screenShot.ImageToByte(img);
|
|
||||||
return WriteToFile(@Filename, bmpBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int WriteToFile(string filename, byte[] outputBytes)
|
|
||||||
{
|
|
||||||
int bytesWritten = -1;
|
|
||||||
if (_mmfFiles.TryGetValue(filename, out var mmfFile) == false)
|
|
||||||
{
|
|
||||||
mmfFile = MemoryMappedFile.CreateOrOpen(filename, outputBytes.Length);
|
|
||||||
_mmfFiles[filename] = mmfFile;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using MemoryMappedViewAccessor accessor = mmfFile.CreateViewAccessor(0, outputBytes.Length, MemoryMappedFileAccess.Write);
|
|
||||||
accessor.WriteArray<byte>(0, outputBytes, 0, outputBytes.Length);
|
|
||||||
bytesWritten = outputBytes.Length;
|
|
||||||
}
|
|
||||||
catch (UnauthorizedAccessException)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
mmfFile.Dispose();
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
mmfFile = MemoryMappedFile.CreateOrOpen(filename, outputBytes.Length);
|
|
||||||
_mmfFiles[filename] = mmfFile;
|
|
||||||
using MemoryMappedViewAccessor accessor = mmfFile.CreateViewAccessor(0, outputBytes.Length, MemoryMappedFileAccess.Write);
|
|
||||||
accessor.WriteArray(0, outputBytes, 0, outputBytes.Length);
|
|
||||||
bytesWritten = outputBytes.Length;
|
|
||||||
}
|
|
||||||
return bytesWritten;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ReadFromFile(string filename, int expectedSize)
|
|
||||||
{
|
|
||||||
MemoryMappedFile mmfFile = MemoryMappedFile.OpenExisting(filename);
|
|
||||||
using MemoryMappedViewAccessor viewAccessor = mmfFile.CreateViewAccessor();
|
|
||||||
byte[] bytes = new byte[expectedSize];
|
|
||||||
viewAccessor.ReadArray(0, bytes, 0, bytes.Length);
|
|
||||||
string text = Encoding.UTF8.GetString(bytes);
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// makes all functionality for providing screenshots available
|
|
||||||
class ScreenShot
|
|
||||||
{
|
|
||||||
private IVideoProvider _currentVideoProvider;
|
|
||||||
private readonly ImageConverter _converter = new ImageConverter();
|
|
||||||
|
|
||||||
public BitmapBuffer MakeScreenShotImage()
|
|
||||||
{
|
|
||||||
if (_currentVideoProvider == null)
|
|
||||||
{
|
|
||||||
_currentVideoProvider = GlobalWin.Emulator.AsVideoProviderOrDefault();
|
|
||||||
}
|
|
||||||
return GlobalWin.DisplayManager.RenderVideoProvider(_currentVideoProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] ImageToByte(Image img)
|
|
||||||
{
|
|
||||||
return (byte[])_converter.ConvertTo(img, typeof(byte[]));
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ImageToString(Image img)
|
|
||||||
{
|
|
||||||
return Convert.ToBase64String(ImageToByte(img));
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetScreenShotAsString()
|
|
||||||
{
|
|
||||||
BitmapBuffer bb = MakeScreenShotImage();
|
|
||||||
byte[] imgBytes = ImageToByte(bb.ToSysdrawingBitmap());
|
|
||||||
return Convert.ToBase64String(imgBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -28,9 +28,9 @@ namespace BizHawk.Client.EmuHawk
|
||||||
public static GLManager GLManager;
|
public static GLManager GLManager;
|
||||||
|
|
||||||
public static int ExitCode;
|
public static int ExitCode;
|
||||||
public static Communication.HttpCommunication httpCommunication = null;
|
public static HttpCommunication httpCommunication = null;
|
||||||
public static Communication.SocketServer socketServer = null;
|
public static SocketServer socketServer = null;
|
||||||
public static Communication.MemoryMappedFiles memoryMappedFiles = null;
|
public static MemoryMappedFiles memoryMappedFiles = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to disable secondary throttling (e.g. vsync, audio) for unthrottled modes or when the primary (clock) throttle is taking over (e.g. during fast forward/rewind).
|
/// Used to disable secondary throttling (e.g. vsync, audio) for unthrottled modes or when the primary (clock) throttle is taking over (e.g. during fast forward/rewind).
|
||||||
|
|
|
@ -318,7 +318,10 @@ namespace BizHawk.Client.EmuHawk
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_argParser.ParseArguments(args);
|
_argParser.ParseArguments(
|
||||||
|
args,
|
||||||
|
() => (byte[]) new ImageConverter().ConvertTo(MakeScreenshotImage().ToSysdrawingBitmap(), typeof(byte[]))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
catch (ArgParserException e)
|
catch (ArgParserException e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -66,7 +66,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
[LuaMethod("socketServerSuccessful", "returns the status of the last Socket server action")]
|
[LuaMethod("socketServerSuccessful", "returns the status of the last Socket server action")]
|
||||||
public bool SocketServerSuccessful()
|
public bool SocketServerSuccessful()
|
||||||
{
|
{
|
||||||
return CheckSocketServer() && GlobalWin.socketServer.Successful();
|
return CheckSocketServer() && GlobalWin.socketServer.Successful;
|
||||||
}
|
}
|
||||||
|
|
||||||
[LuaMethod("socketServerSetTimeout", "sets the timeout in milliseconds for receiving messages")]
|
[LuaMethod("socketServerSetTimeout", "sets the timeout in milliseconds for receiving messages")]
|
||||||
|
@ -80,7 +80,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
public void SocketServerSetIp(string ip)
|
public void SocketServerSetIp(string ip)
|
||||||
{
|
{
|
||||||
CheckSocketServer();
|
CheckSocketServer();
|
||||||
GlobalWin.socketServer.Ip = ip;
|
GlobalWin.socketServer.IP = ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
[LuaMethod("socketServerSetPort", "sets the port of the Lua socket server")]
|
[LuaMethod("socketServerSetPort", "sets the port of the Lua socket server")]
|
||||||
|
@ -93,7 +93,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
[LuaMethod("socketServerGetIp", "returns the IP address of the Lua socket server")]
|
[LuaMethod("socketServerGetIp", "returns the IP address of the Lua socket server")]
|
||||||
public string SocketServerGetIp()
|
public string SocketServerGetIp()
|
||||||
{
|
{
|
||||||
return GlobalWin.socketServer?.Ip;
|
return GlobalWin.socketServer?.IP;
|
||||||
}
|
}
|
||||||
|
|
||||||
[LuaMethod("socketServerGetPort", "returns the port of the Lua socket server")]
|
[LuaMethod("socketServerGetPort", "returns the port of the Lua socket server")]
|
||||||
|
|
Loading…
Reference in New Issue