using System; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Threading; using Windows.Win32; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace AMNet.Server; public static class DllMain { private const string WebAddress = "http://card.ppc.moe"; // instance metrics internal static long LastPollTime = -1; internal static long ServerStartedAt; static DllMain() { PInvoke.AllocConsole(); App = WebServer.BuildServer(Config.ReadKey(Config.IOSection, "serverAddress", 1024, "http://+:6070").Split(';')); } private static WebApplication App { get; } [UnmanagedCallersOnly(EntryPoint = "aime_io_get_api_version")] public static ushort GetApiVersion() => 0x0100; [UnmanagedCallersOnly(EntryPoint = "aime_io_init")] public static int Init() { CancellationTokenRegistration? cancellationRegistration = null; cancellationRegistration = App.Lifetime.ApplicationStarted.Register(() => { App.Logger.LogInformation("AMNet Server ({gameId}) started successfully.", Config.GameId); App.Logger.LogInformation("Visit {addr} from a mobile device on the same network to get started.", WebAddress); // ReSharper disable once AccessToModifiedClosure cancellationRegistration?.Dispose(); }); Volatile.Write(ref ServerStartedAt, Environment.TickCount64); App.RunAsync(); return 0; } [UnmanagedCallersOnly(EntryPoint = "aime_io_nfc_poll")] public static int NfcPoll(byte unitNo) { Volatile.Write(ref LastPollTime, Environment.TickCount64); if (!Config.EnableAimeTxt) { return 1; } // check keyboard input for the enter key being held down (0x0D) var enterPressed = (PInvoke.GetAsyncKeyState(0x0D) & 0x8000) != 0; if (!enterPressed) { return 0; } try { using var aimeTxtReader = new StreamReader(File.OpenRead(Config.AimeTxtPath), Encoding.UTF8); var fileName = Path.GetFileName(Config.AimeTxtPath); var cardResult = App.Services.GetRequiredService().SetCard(aimeTxtReader.ReadLine()); if (cardResult == null) { App.Logger.LogWarning("Failed to read card from {file}: invalid format", fileName); } else { App.Logger.LogInformation("Card loaded from {fileName}", fileName); } } catch (IOException e) { App.Logger.LogWarning("Failed to read '{path}': {message}", Path.GetFullPath(Config.AimeTxtPath), e.Message); } return 0; } [UnmanagedCallersOnly(EntryPoint = "aime_io_nfc_get_aime_id")] public static int GetAimeId(byte unitNo, IntPtr luid, nint luidSize) { if (unitNo != 0) { return 1; } var card = App.Services.GetRequiredService().TakeActiveCard(); if (card?.Expired != false) { return 1; } try { Marshal.Copy(card.Value, 0, luid, (int)luidSize); return 0; } finally { App.Logger.LogInformation("Card read in: {cardId}", card); } } [UnmanagedCallersOnly(EntryPoint = "aime_io_nfc_get_felica_id")] public static unsafe int GetFelicaId(byte unitNo, ulong* idm) => 1; // felica not supported [UnmanagedCallersOnly(EntryPoint = "aime_io_led_set_color")] public static void SetLedColour(byte unitNo, byte r, byte g, byte b) { // do nothing } }