using System.Text; using MessagePack; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.Extensions.Primitives; using Newtonsoft.Json; using SVSim.Database.Models; using SVSim.EmulatedEntrypoint.Constants; using SVSim.EmulatedEntrypoint.Extensions; using SVSim.EmulatedEntrypoint.Models.Dtos; using SVSim.EmulatedEntrypoint.Models.Dtos.Internal; using SVSim.EmulatedEntrypoint.Security; using SVSim.EmulatedEntrypoint.Services; namespace SVSim.EmulatedEntrypoint.Middlewares; /// /// Translates incoming requests and outgoing responses from the Shadowverse client into the messagepack format. /// public class ShadowverseTranslationMiddleware : IMiddleware { private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider; private readonly ShadowverseSessionService _sessionService; public ShadowverseTranslationMiddleware(IActionDescriptorCollectionProvider actionDescriptorCollectionProvider, ShadowverseSessionService sessionService) { _actionDescriptorCollectionProvider = actionDescriptorCollectionProvider; _sessionService = sessionService; } public async Task InvokeAsync(HttpContext context, RequestDelegate next) { bool isUnity = context.Request.Headers.UserAgent.Any(agent => agent?.Contains("UnityPlayer") ?? false); string path = context.Request.Path; ActionDescriptor? endpointDescriptor = _actionDescriptorCollectionProvider.ActionDescriptors.Items.FirstOrDefault(ad => $"/{ad.AttributeRouteInfo.Template}".Equals(path, StringComparison.InvariantCultureIgnoreCase)); if (!isUnity || endpointDescriptor == null) { await next.Invoke(context); return; } // Replace response body stream to re-access it. using MemoryStream tempResponseBody = new MemoryStream(); Stream originalResponsebody = context.Response.Body; context.Response.Body = tempResponseBody; // Pull out the request bytes into a stream using MemoryStream requestBytesStream = new MemoryStream(); await context.Request.Body.CopyToAsync(requestBytesStream); byte[] requestBytes = requestBytesStream.ToArray(); // Get encryption values for this request string sid = context.Request.Headers[NetworkConstants.SessionIdHeaderName]; string udid = _sessionService.GetUdidFromSessionId(sid).GetValueOrDefault().ToString(); // Decrypt incoming data. requestBytes = Encryption.Decrypt(requestBytes, udid); object? data = MessagePackSerializer.Deserialize(endpointDescriptor.Parameters.FirstOrDefault().ParameterType, requestBytes); string json = JsonConvert.SerializeObject(data); StringContent newStream = new StringContent(json, Encoding.UTF8, "application/json"); context.Request.Body = newStream.ReadAsStream(); context.Request.Headers.ContentType = new StringValues("application/json"); await next.Invoke(context); Viewer? viewer = context.GetViewer(); // Grab the response object Type responseType = ((ControllerActionDescriptor)endpointDescriptor).MethodInfo.ReturnType; if (responseType.IsGenericType && responseType.GetGenericTypeDefinition() == typeof(Task<>)) { responseType = responseType.GetGenericArguments()[0]; } using MemoryStream responseBytesStream = new MemoryStream(); context.Response.Body.Seek(0, SeekOrigin.Begin); await context.Response.Body.CopyToAsync(responseBytesStream); byte[] responseBytes = responseBytesStream.ToArray(); object? responseData = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(responseBytes), responseType); // Wrap the response in a datawrapper DataWrapper wrappedResponseData = new DataWrapper { Data = responseData, DataHeaders = new DataHeaders { Servertime = DateTime.UtcNow.Ticks, Sid = context.Request.Headers[NetworkConstants.SessionIdHeaderName].FirstOrDefault(), // TODO error handling ResultCode = 1, ShortUdid = viewer.ShortUdid, ViewerId = viewer.Id } }; // Convert the response into a messagepack, encrypt it byte[] packedData = MessagePackSerializer.Serialize(wrappedResponseData); packedData = Encryption.Encrypt(packedData, udid); await originalResponsebody.WriteAsync(Encoding.UTF8.GetBytes(Convert.ToBase64String(packedData))); context.Response.Body = originalResponsebody; } }