using System; using System.Globalization; using System.Linq; using System.Threading; namespace AMNet.Server; /// /// Represents a storage location to load/read cards from /// internal class CardPresenter { private readonly Lock _cardLock = new(); private StoredCard _currentCard; public record StoredCard(byte[] AccessCode, byte[] IDm, long ExpiresAt) { public bool Expired => Environment.TickCount64 >= ExpiresAt; public override string ToString() => FormatHexCode(AccessCode); } /// /// Returns the currently active card without clearing it /// public StoredCard PeekActiveCard() { lock (_cardLock) { return _currentCard; } } public StoredCard TakeActiveCard(bool? requireIDm = null) { lock (_cardLock) { // IDm and requireIDm are mutually exclusive if (_currentCard == null || (requireIDm.HasValue && (_currentCard.IDm != null) ^ requireIDm.Value)) { return null; } var card = _currentCard; _currentCard = null; return card; } } /// /// Sets the card /// /// The access code of the card to use /// The FeliCa card IDm, if a physical card was used /// How long the card should be presented to the game for in milliseconds /// Whether the card was set successfully public StoredCard SetCard(string accessCode, string idmHex, long validFor = 5000) { // ensure the matrix code is 20-digits long, otherwise pad with zeros var matrixCode = accessCode.Replace(" ", "").PadLeft(20, '0'); var bytes = new byte[10]; for (var i = 0; i < bytes.Length; i++) { var value = matrixCode.Substring(i * 2, 2); if (byte.TryParse(value, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var b)) { bytes[i] = b; } else { return null; } } var idmBytes = !string.IsNullOrWhiteSpace(idmHex) && idmHex.Length <= 16 && idmHex.All(char.IsAsciiHexDigit) ? Convert.FromHexString(idmHex.PadLeft(16, '0')) : null; var card = new StoredCard(bytes, idmBytes, Environment.TickCount64 + validFor); lock (_cardLock) { _currentCard = card; } return card; } /// /// Formats a byte array as a hex string, inserting a space every 2 characters /// public static string FormatHexCode(byte[] code) => string.Join(" ", code.Chunk(2).Select(Convert.ToHexString)); }