using System.Globalization; namespace Recordingtest.Recorder; /// /// Translates Win32 virtual-key codes into human-readable strings for /// scenario yaml. See issue #11 — prior recorder emitted raw VK ints which /// broke replay. Rules: /// - modifiers (Ctrl/Shift/Alt/Win) are tracked separately and not emitted as standalone steps /// - printable keys (letters/digits/space) become single-character strings /// - other named keys become lowercase names (enter, tab, esc, f1..f12, arrows, ...) /// public static class KeyTranslator { public enum KeyCategory { Modifier, Printable, Named, } public readonly record struct Translated(KeyCategory Category, string Text, uint RawVk); // Convention chosen for issue #11: printable letters are emitted UPPERCASE // matching VK semantics (VK for 'A' is 0x41). Case handling is the player's // concern (Shift state is tracked separately as a modifier). public static Translated Translate(uint vk) { switch (vk) { case 0x10: // VK_SHIFT case 0xA0: // VK_LSHIFT case 0xA1: // VK_RSHIFT return new Translated(KeyCategory.Modifier, "shift", vk); case 0x11: // VK_CONTROL case 0xA2: // VK_LCONTROL case 0xA3: // VK_RCONTROL return new Translated(KeyCategory.Modifier, "ctrl", vk); case 0x12: // VK_MENU (Alt) case 0xA4: // VK_LMENU case 0xA5: // VK_RMENU return new Translated(KeyCategory.Modifier, "alt", vk); case 0x5B: // VK_LWIN case 0x5C: // VK_RWIN return new Translated(KeyCategory.Modifier, "win", vk); case 0x08: return new Translated(KeyCategory.Named, "backspace", vk); case 0x09: return new Translated(KeyCategory.Named, "tab", vk); case 0x0D: return new Translated(KeyCategory.Named, "enter", vk); case 0x1B: return new Translated(KeyCategory.Named, "escape", vk); case 0x20: return new Translated(KeyCategory.Printable, " ", vk); case 0x21: return new Translated(KeyCategory.Named, "pageup", vk); case 0x22: return new Translated(KeyCategory.Named, "pagedown", vk); case 0x23: return new Translated(KeyCategory.Named, "end", vk); case 0x24: return new Translated(KeyCategory.Named, "home", vk); case 0x25: return new Translated(KeyCategory.Named, "left", vk); case 0x26: return new Translated(KeyCategory.Named, "up", vk); case 0x27: return new Translated(KeyCategory.Named, "right", vk); case 0x28: return new Translated(KeyCategory.Named, "down", vk); case 0x2D: return new Translated(KeyCategory.Named, "insert", vk); case 0x2E: return new Translated(KeyCategory.Named, "delete", vk); } // Letters A-Z (VK 0x41..0x5A) if (vk >= 0x41 && vk <= 0x5A) { var c = (char)vk; return new Translated(KeyCategory.Printable, c.ToString(CultureInfo.InvariantCulture), vk); } // Top-row digits 0-9 (VK 0x30..0x39) if (vk >= 0x30 && vk <= 0x39) { var c = (char)vk; return new Translated(KeyCategory.Printable, c.ToString(CultureInfo.InvariantCulture), vk); } // Numpad digits (VK 0x60..0x69) if (vk >= 0x60 && vk <= 0x69) { var c = (char)('0' + (vk - 0x60)); return new Translated(KeyCategory.Printable, c.ToString(CultureInfo.InvariantCulture), vk); } // F1..F24 (VK 0x70..0x87) if (vk >= 0x70 && vk <= 0x87) { var n = (int)(vk - 0x70) + 1; return new Translated(KeyCategory.Named, "f" + n.ToString(CultureInfo.InvariantCulture), vk); } return new Translated(KeyCategory.Named, "vk" + vk.ToString(CultureInfo.InvariantCulture), vk); } }