diff --git a/Medusa.AppHost/Program.cs b/Medusa.AppHost/Program.cs index bf80235..3caffed 100644 --- a/Medusa.AppHost/Program.cs +++ b/Medusa.AppHost/Program.cs @@ -25,7 +25,9 @@ var medusaDB = postgres.AddDatabase(dbVolumeName); builder.AddProject("Medusa-core") .WithReference(cache) .WithReference(messenger) - .WithEnvironment("weburl", weburl); + .WithEnvironment("weburl", weburl) + .WithEnvironment("rabbitHostName", messenger) + .WaitOn(messenger); builder.AddProject("Medusa-web-server"); @@ -33,6 +35,8 @@ builder.AddProject("medusa-data") .WithReference(cache) .WithReference(messenger) .WithReference(medusaDB) - .WaitOn(postgres); + .WithEnvironment("rabbitHostName", messenger) + .WaitOn(postgres) + .WaitOn(messenger); builder.Build().Run(); diff --git a/Medusa.Core/Extensions/ApplicationBuilderExtensions.cs b/Medusa.Core/Extensions/ApplicationBuilderExtensions.cs index 199a189..2c19a4c 100644 --- a/Medusa.Core/Extensions/ApplicationBuilderExtensions.cs +++ b/Medusa.Core/Extensions/ApplicationBuilderExtensions.cs @@ -11,7 +11,8 @@ namespace Medusa.Core.Extensions var handlerService = app.ApplicationServices.GetRequiredService(); var assembly = Assembly.GetEntryAssembly() ?? throw new InvalidOperationException("Could not find entry assembly."); - var types = assembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(IHandler))); + var types = assembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(IHandler)) || + (t.IsSubclassOf(typeof(Handler)) && !t.IsAbstract)); handlerService.Handlers.AddRange(types); diff --git a/Medusa.Core/Handlers/Boot/GetServicesHandler.cs b/Medusa.Core/Handlers/Boot/GetServicesHandler.cs index 2b22385..f6dc848 100644 --- a/Medusa.Core/Handlers/Boot/GetServicesHandler.cs +++ b/Medusa.Core/Handlers/Boot/GetServicesHandler.cs @@ -5,8 +5,7 @@ using System.Xml.Linq; namespace Medusa.Core.Handlers.Boot { - [Handler("services", "get")] - public class GetServicesHandler(IServer server, ILogger logger, XDocument body) : IHandler + public class GetServicesHandler(IServer server, ILogger logger, XDocument body) : Handler { private readonly IServer _server = server; private readonly ILogger _logger = logger; @@ -14,7 +13,13 @@ namespace Medusa.Core.Handlers.Boot private const string CommonUrl = "http://127.0.0.1:8083/ea"; private string listeningAddress = "http://127.0.0.1:8083"; - public Task HandleAsync(string model) + public override void Configure() + { + Module("services"); + Method("get"); + } + + public override Task HandleAsync(string model) { var services = CreateCoreServicesElement(); diff --git a/Medusa.Core/Handlers/Common/Card/ConvCardnNumberSystem3Handler.cs b/Medusa.Core/Handlers/Common/Card/ConvCardnNumberSystem3Handler.cs new file mode 100644 index 0000000..dd52ac8 --- /dev/null +++ b/Medusa.Core/Handlers/Common/Card/ConvCardnNumberSystem3Handler.cs @@ -0,0 +1,39 @@ +using Medusa.Core.Attributes; +using Medusa.Core.Services; +using System.Xml.Linq; + +namespace Medusa.Core.Handlers.Common.Card +{ + [Handler("system_3", "convcardnumber")] + public class ConvCardnNumberSystem3(ICardService cardService, XDocument body) : IHandler + { + private readonly ICardService _cardService = cardService; + + public Task HandleAsync(string model) + { + var root = body.Root; + var systemElement = root.Element("system_3"); + var data = systemElement.Element("data"); + var cardIdElement = data.Element("card_id").Value; + var cardId = cardIdElement; + var cardNumber = _cardService.ConvertUidToKonamiId(cardId); + + var system = new XElement("system", + new XAttribute("status", "0"), + new XElement("result", + new XAttribute("__type", "s32"), + 0 + ), + new XElement("data", + new XElement("card_number", cardNumber))); + + var document = new XDocument( + new XElement("response", + system + ) + ); + + return Task.FromResult(document); + } + } +} diff --git a/Medusa.Core/Handlers/Handler.cs b/Medusa.Core/Handlers/Handler.cs new file mode 100644 index 0000000..eecbd50 --- /dev/null +++ b/Medusa.Core/Handlers/Handler.cs @@ -0,0 +1,26 @@ +using System.Xml.Linq; + +namespace Medusa.Core.Handlers +{ + public abstract class Handler + { + public string HandlerModule { get; set; } = ""; + public string HandlerMethod { get; set; } = ""; + + public virtual void Configure() { throw new NotImplementedException("Configure method not implemented."); } + + protected void Module(string module) + { + this.HandlerModule = module; + } + + protected void Method(string method) + { + this.HandlerMethod = method; + } + + public virtual XDocument Handle(string model) { throw new NotImplementedException("Handle method not implemented."); } + public virtual Task HandleAsync(string model) { throw new NotImplementedException("Handle method not implemented."); } + + } +} diff --git a/Medusa.Core/Handlers/M39/BootPCB24Handler.cs b/Medusa.Core/Handlers/M39/BootPCB24Handler.cs new file mode 100644 index 0000000..34d57a6 --- /dev/null +++ b/Medusa.Core/Handlers/M39/BootPCB24Handler.cs @@ -0,0 +1,29 @@ +using Medusa.Core.Attributes; +using Microsoft.AspNetCore.Routing.Patterns; +using System.Xml.Linq; + +namespace Medusa.Core.Handlers.M39 +{ + public class BootPCB24Handler: Handler + { + public override void Configure() + { + Module("pcb24"); + Method("boot"); + } + + public override XDocument Handle(string model) + { + var pcb24 = new XElement("pcb24", + new XAttribute("status", "0")); + + var document = new XDocument( + new XElement("response", + pcb24 + ) + ); + + return document; + } + } +} diff --git a/Medusa.Core/Handlers/MDX/MusicDataLoadPlayDate3Handler.cs b/Medusa.Core/Handlers/MDX/MusicDataLoadPlayDate3Handler.cs new file mode 100644 index 0000000..1f714bd --- /dev/null +++ b/Medusa.Core/Handlers/MDX/MusicDataLoadPlayDate3Handler.cs @@ -0,0 +1,17 @@ +using Medusa.Core.Attributes; +using System.Xml.Linq; + +namespace Medusa.Core.Handlers.MDX +{ + [Handler("playdata_3", "musicdata_load")] + public class MusicDataLoadPlayDate3Handler(XDocument body) : IHandler + { + private readonly XDocument _body = body; + + public Task HandleAsync(string model) + { + var document = new XDocument("Response"); + return Task.FromResult(document); + } + } +} diff --git a/Medusa.Core/Handlers/MDX/PlayerDataLoadPlayData3Handler.cs b/Medusa.Core/Handlers/MDX/PlayerDataLoadPlayData3Handler.cs new file mode 100644 index 0000000..dce564e --- /dev/null +++ b/Medusa.Core/Handlers/MDX/PlayerDataLoadPlayData3Handler.cs @@ -0,0 +1,17 @@ +using Medusa.Core.Attributes; +using System.Xml.Linq; + +namespace Medusa.Core.Handlers.MDX +{ + [Handler("playdata_3", "playerdata_load")] + public class PlayerDataLoadPlayData3Handler(XDocument body) : IHandler + { + private readonly XDocument _body = body; + + public Task HandleAsync(string model) + { + var document = new XDocument("Response"); + return Task.FromResult(document); + } + } +} diff --git a/Medusa.Core/Medusa.Core.csproj b/Medusa.Core/Medusa.Core.csproj index c3368b4..d46997c 100644 --- a/Medusa.Core/Medusa.Core.csproj +++ b/Medusa.Core/Medusa.Core.csproj @@ -12,6 +12,8 @@ + + @@ -19,9 +21,7 @@ - - diff --git a/Medusa.Core/Program.cs b/Medusa.Core/Program.cs index e956a40..14b4323 100644 --- a/Medusa.Core/Program.cs +++ b/Medusa.Core/Program.cs @@ -2,6 +2,8 @@ using FastEndpoints; using Medusa.Core.Extensions; using Medusa.Core.Middlewares; using Medusa.Core.Request; +using RabbitMQ.Client.Core.DependencyInjection; +using RabbitMQ.Client.Core.DependencyInjection.Configuration; using System.Text; Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); @@ -12,6 +14,20 @@ builder.Services.AddFastEndpoints(); builder.Services.AddHandlers(); +var uri = new Uri(Environment.GetEnvironmentVariable("rabbitHostName")); + +var userInfo = uri.UserInfo.Split(':'); +var rabbitMqConfiguration = new RabbitMqServiceOptions +{ + HostName = uri.Host, + Port = uri.Port, + UserName = userInfo[0], + Password = userInfo[1] +}; + +builder.Services.AddRabbitMqServices(rabbitMqConfiguration); + +builder.Services.AddRabbitMqProducer(rabbitMqConfiguration); builder.AddServiceDefaults(); diff --git a/Medusa.Core/Services/HandlerService.cs b/Medusa.Core/Services/HandlerService.cs index bb41cf8..e078d81 100644 --- a/Medusa.Core/Services/HandlerService.cs +++ b/Medusa.Core/Services/HandlerService.cs @@ -14,18 +14,27 @@ namespace Medusa.Core.Services public async Task Handle(string model, string module, string method, XDocument body) { - foreach (var handler in Handlers) + foreach(var handler in Handlers) { //Module and service are on the attribute var handlerAttribute = handler.CustomAttributes.FirstOrDefault(x => x.AttributeType == typeof(HandlerAttribute)); - if (handlerAttribute is null) + if(handlerAttribute is null) + { + var response = await HandleInheritanceClass(handler, model, module, method, body); + + if(response is not null) + { + return response; + } + continue; - + } + var attributeModule = handlerAttribute.ConstructorArguments[0].Value.ToString(); var attributeMethod = handlerAttribute.ConstructorArguments[1].Value.ToString(); - if (attributeModule == module && attributeMethod == method) + if(attributeModule == module && attributeMethod == method) { // Body param is optional so check for it bool requiresXDocumentConstructor = handler.GetConstructors() @@ -44,5 +53,63 @@ namespace Medusa.Core.Services return new XDocument(); } + + private async Task HandleInheritanceClass(Type handler, string model, string module, string method, XDocument body) + { + bool requiredXdocumentConstructor = handler.GetConstructors() + .Any(c => c.GetParameters().Any(p => p.ParameterType == typeof(XDocument))); + + Handler handlerInstance = requiredXdocumentConstructor + ? (Handler)ActivatorUtilities.CreateInstance(_serviceProvider, handler, body) + : (Handler)ActivatorUtilities.CreateInstance(_serviceProvider, handler); + + try + { + var configureMethod = handler.GetMethod("Configure"); + configureMethod?.Invoke(handlerInstance, null); + } + catch(NotImplementedException e) + { + _logger.LogError(e, "Configure method not implemented for {Handler}", handler.Name); + return null; + } + + var handlerModule = handlerInstance.HandlerModule; + var handlerMethod = handlerInstance.HandlerMethod; + + if(string.IsNullOrEmpty(handlerModule)) + { + _logger.LogError("Module not set for {Handler}", handler.Name); + return null; + } + + if(string.IsNullOrEmpty(handlerMethod)) + { + _logger.LogError("Method not set for {Handler}", handler.Name); + return null; + } + + if(handlerModule != module && handlerMethod != method) + return null; + + var handleMethod = handlerInstance.GetType().GetMethod("Handle", [typeof(string)]); + var handleAsyncMethod = handlerInstance.GetType().GetMethod("HandleAsync", [typeof(string)]); + + var response = new XDocument(); + + response = handleMethod?.DeclaringType != typeof(Handler) + ? handlerInstance.Handle(model) + : handleAsyncMethod?.DeclaringType != typeof(Handler) + ? await handlerInstance.HandleAsync(model) + : null; + + if(response is null) + { + _logger.LogError("Handle and HandleAsync not implemented for {Handler}", handler.Name); + return null; + } + + return response; + } } } diff --git a/Medusa.Core/Utils/LZ77.cs b/Medusa.Core/Utils/LZ77.cs index 329ccfa..c50c563 100644 --- a/Medusa.Core/Utils/LZ77.cs +++ b/Medusa.Core/Utils/LZ77.cs @@ -2,74 +2,95 @@ { public class LZ77 { - public static byte[] Decompress(byte[] compressedData) + public static byte[] Decompress(byte[] data) { - byte[] decompressedData = new byte[0x190000]; - int inputPosition = 0; - int outputPosition = 0; - byte bitmask = 0; - int bitCounter = 8; + // init a buffer, size comes from eamuemu + byte[] res = new byte[0x190000]; + // current data location + int p = 0; + // current output location + int r = 0; + // traceback location + int t = 0; + // bitmask location + int b = 8; // read next bitmask byte on start + byte mask = 0; while(true) { - if(bitCounter == 8) + // read bitmask on block end + if(b == 8) { - bitmask = compressedData[inputPosition++]; - bitCounter = 0; + mask = data[p]; + p += 1; + b = 0; } - - if((bitmask & 1) != 0) - { - decompressedData[outputPosition++] = compressedData[inputPosition++]; + // get mask for next byte + if((mask & 1) == 1) + { // not coded + // copy the byte from data to result + res[r] = data[p]; + r += 1; + p += 1; } else - { - int distance = compressedData[inputPosition]; - int count = compressedData[inputPosition + 1]; - + { // coded + // read descriptors + int distance = data[p]; + int count = data[p + 1]; + // EOF mark if(distance == 0 && count == 0) + { break; - - inputPosition += 2; - distance = (distance << 4) | (count >> 4); + } + p += 2; + // shift to correct location + distance <<= 4; + distance |= count >> 4; count = (count & 0x0F) + 3; - + // copy earlier result bytes to the end + t = r - distance; // initialize traceback location for(int i = 0; i < count; i++) { - decompressedData[outputPosition] = decompressedData[outputPosition - distance]; - outputPosition++; + res[r] = t < 0 ? (byte)0x00 : res[t]; + r += 1; + t += 1; } } - - bitmask >>= 1; - bitCounter++; + // shift mask + mask >>= 1; + b += 1; } - - var output = new byte[outputPosition]; - Array.Copy(decompressedData, output, outputPosition); + // r = result length + byte[] output = new byte[r]; + Array.Copy(res, output, r); return output; } public static byte[] CompressEmpty(byte[] data) { - int estimatedLength = data.Length + (data.Length / 8) + 3; - byte[] compressedData = new byte[estimatedLength]; - int outputPosition = 0; - + byte[] res = new byte[data.Length + data.Length / 8 + 3]; + int p = 0; for(int i = 0; i < data.Length; i++) { if(i % 8 == 0) { - compressedData[outputPosition++] = (byte)((data.Length - i < 8) ? (Math.Pow(2, data.Length - i) - 1) : 255); + if(data.Length - i < 8) + { + res[p] = (byte)(Math.Pow(2, data.Length - i) - 1); + } + else + { + res[p] = 255; + } + p += 1; } - - compressedData[outputPosition++] = data[i]; + res[p] = data[i]; + p += 1; } - - compressedData[outputPosition++] = 0; - compressedData[outputPosition] = 0; - - return compressedData; + res[p] = 0; + res[p + 1] = 0; + return res; } } } diff --git a/Medusa.Data/Medusa.Data.csproj b/Medusa.Data/Medusa.Data.csproj index 048dcd3..6a77c66 100644 --- a/Medusa.Data/Medusa.Data.csproj +++ b/Medusa.Data/Medusa.Data.csproj @@ -1,4 +1,4 @@ - + Exe @@ -19,6 +19,12 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + diff --git a/Medusa.Data/Program.cs b/Medusa.Data/Program.cs index 3751555..8fabae9 100644 --- a/Medusa.Data/Program.cs +++ b/Medusa.Data/Program.cs @@ -1,2 +1,10 @@ -// See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); + +using Microsoft.Extensions.Hosting; + +var builder = WebApplication.CreateBuilder(args); + +builder.AddServiceDefaults(); + +var app = builder.Build(); + +app.Run(); \ No newline at end of file diff --git a/Medusa.Data/Properties/launchSettings.json b/Medusa.Data/Properties/launchSettings.json new file mode 100644 index 0000000..46c8f7c --- /dev/null +++ b/Medusa.Data/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Medusa.Data": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:58881;http://localhost:58882" + } + } +} \ No newline at end of file