namespace XFESystem.NativeKernel; public unsafe struct UsbHidField { public uint Bit, Size, Count, Page, Minimum, Maximum, Report, Flags; } public unsafe struct UsbHidLayout { public UsbHidField* Fields; public uint Count, Capacity, Kind, ReportIds; } public unsafe struct UsbHidState { public ulong A, B, C, D; public byte Modifiers; public Ps2DecodeState Decoder; } // HID 1.11 short-item parser. Bounded input, fields and bit offsets; vendor, // absolute-pointer and oversized reports do not become fabricated input. // Shared directly with the host descriptor tests, without any hardware I/O. public static unsafe class UsbHid { public static bool Parse(byte* bytes, uint length, UsbHidLayout* layout) { if (bytes == null || length == 0 || length > 4096 || layout == null || layout->Fields == null) return false; layout->Count = layout->Kind = layout->ReportIds = 0; uint page = 0, size = 0, count = 0, report = 0, minimum = 0, maximum = 0, usageCount = 0; uint* usages = stackalloc uint[256]; uint* bits = stackalloc uint[256]; uint* globals = stackalloc uint[32]; for (uint i = 0; i < 256; i++) bits[i] = 0; uint depth = 0; for (uint p = 0; p < length;) { byte prefix = bytes[p++]; if (prefix == 0xfe) return false; // long/vendor items are outside the supported contract uint n = (uint)(prefix & 3); if (n == 3) n = 4; if (n > length - p) return false; uint value = 0; for (uint i = 0; i < n; i++) value |= (uint)bytes[p++] << (int)(8 * i); uint type = (uint)(prefix >> 2) & 3, tag = (uint)prefix >> 4; if (type == 1) { if (tag == 0) page = value; else if (tag == 7) { if (value > 32) return false; size = value; } else if (tag == 8) { if (value == 0 || value > 255) return false; report = value; layout->ReportIds = 1; } else if (tag == 9) { if (value > 256) return false; count = value; } else if (tag == 10) { if (depth >= 8) return false; globals[depth * 4] = page; globals[depth * 4 + 1] = size; globals[depth * 4 + 2] = count; globals[depth++ * 4 + 3] = report; } else if (tag == 11) { if (depth == 0) return false; depth--; page = globals[depth * 4]; size = globals[depth * 4 + 1]; count = globals[depth * 4 + 2]; report = globals[depth * 4 + 3]; } } else if (type == 2) { if (tag == 0) { if (usageCount == 256) return false; usages[usageCount++] = value; } else if (tag == 1) minimum = value; else if (tag == 2) maximum = value; } else if (type == 0) { if (tag == 8) { if (size == 0 || count == 0 || bits[report] > 32768 || size * count > 32768 - bits[report]) return false; uint start = bits[report]; bits[report] += size * count; if ((value & 1) == 0) { bool variable = (value & 2) != 0; if (page == 7 && !variable) { if (!Add(layout, start, size, count, page, minimum, maximum, report, value)) return false; layout->Kind |= 1; } else if (page == 7 || page == 9 || page == 1) { for (uint i = 0; i < count; i++) { uint usage = usageCount != 0 ? usages[i < usageCount ? i : usageCount - 1] : minimum + i; uint actualPage = usage > 65535 ? usage >> 16 : page; usage &= 65535; if (actualPage == 7 || actualPage == 9 || actualPage == 1 && usage is 0x30 or 0x31 or 0x38) { if (!Add(layout, start + i * size, size, 1, actualPage, usage, usage, report, value)) return false; if (actualPage == 7) layout->Kind |= 1; else if (actualPage == 9 || (value & 4) != 0) layout->Kind |= 2; } } } } } minimum = maximum = usageCount = 0; } } return depth == 0 && layout->Count != 0 && layout->Kind != 0; } static bool Add(UsbHidLayout* layout, uint bit, uint size, uint count, uint page, uint min, uint max, uint id, uint flags) { if (layout->Count >= layout->Capacity) return false; layout->Fields[layout->Count++] = new UsbHidField { Bit = bit, Size = size, Count = count, Page = page, Minimum = min, Maximum = max, Report = id, Flags = flags }; return true; } public static void BootKeyboard(UsbHidLayout* layout) { layout->Count = 0; layout->Kind = 1; layout->ReportIds = 0; for (uint i = 0; i < 8; i++) Add(layout, i, 1, 1, 7, 224 + i, 224 + i, 0, 2); Add(layout, 16, 8, 6, 7, 0, 255, 0, 0); } public static uint Bits(byte* data, uint bit, uint size) { uint value = 0; for (uint i = 0; i < size; i++) if ((data[(bit + i) / 8] & (1 << (int)((bit + i) & 7))) != 0) value |= 1u << (int)i; return value; } static int Signed(uint value, uint size) => size == 32 ? unchecked((int)value) : (int)(value << (int)(32 - size)) >> (int)(32 - size); static bool Has(ulong* set, uint key) => key < 256 && (set[key / 64] & (1UL << (int)(key & 63))) != 0; public static uint Decode(UsbHidLayout* layout, UsbHidState* state, byte* data, uint bytes, NativeInputReport* output, uint capacity) { if (layout == null || state == null || data == null || bytes == 0 || bytes > 4096 || output == null || capacity < 64) return 0; uint id = 0; if (layout->ReportIds != 0) { id = *data++; bytes--; } ulong* keys = stackalloc ulong[4]; for (uint i = 0; i < 4; i++) keys[i] = 0; bool keyboard = false, pointer = false, rollover = false; uint mods = 0, buttons = 0; int x = 0, y = 0, wheel = 0; for (uint i = 0; i < layout->Count; i++) { UsbHidField* f = &layout->Fields[i]; if (f->Report != id) continue; if (f->Bit + f->Size * f->Count > bytes * 8) return 0; // truncated input changes no held state if (f->Page == 7) keyboard = true; for (uint j = 0; j < f->Count; j++) { uint v = Bits(data, f->Bit + j * f->Size, f->Size); if (f->Page == 7) { uint usage = (f->Flags & 2) != 0 ? (v != 0 ? f->Minimum : 0) : v; if (usage is >= 224 and <= 231) mods |= 1u << (int)(usage - 224); else if (usage is >= 1 and <= 3) rollover = true; else if (usage is >= 4 and < 224) keys[usage / 64] |= 1UL << (int)(usage & 63); } else if (f->Page == 9 && f->Minimum is >= 1 and <= 5) { pointer = true; if (v != 0) buttons |= 1u << (int)(f->Minimum - 1); } else if (f->Page == 1 && (f->Flags & 4) != 0) { int signed = Signed(v, f->Size); pointer = true; if (f->Minimum == 0x30) x = signed; else if (f->Minimum == 0x31) y = signed; else if (f->Minimum == 0x38) wheel = signed; } } } uint made = 0; if (keyboard && !rollover) { // Modifier transitions precede ordinary keys, using the common // native set-1 ABI so Win/Alt+Tab work in both input backends. for (uint i = 0; i < 8; i++) if (((mods ^ state->Modifiers) & (1u << (int)i)) != 0) Emit(state, Scan(224 + i), (mods & (1u << (int)i)) == 0, output, ref made); ulong* prior = &state->A; for (uint key = 4; key < 224; key++) if (Has(prior, key) && !Has(keys, key)) Emit(state, Scan(key), true, output, ref made); for (uint key = 4; key < 224; key++) if (!Has(prior, key) && Has(keys, key)) Emit(state, Scan(key), false, output, ref made); for (uint i = 0; i < 4; i++) prior[i] = keys[i]; state->Modifiers = (byte)mods; } if (pointer && made < 64) output[made++] = new NativeInputReport { Kind = Ps2Decoder.Pointer, DeltaX = x, DeltaY = y, Buttons = buttons, Wheel = wheel, Modifiers = state->Decoder.Modifiers }; return made; } static void Emit(UsbHidState* state, uint scan, bool release, NativeInputReport* output, ref uint count) { if (scan == 0 || count >= 63) return; if ((scan & 0x100) != 0) Ps2Decoder.DecodeKeyboard(ref state->Decoder, 0xe0, &output[count]); byte value = (byte)((scan & 127) | (release ? 128u : 0u)); if (Ps2Decoder.DecodeKeyboard(ref state->Decoder, value, &output[count])) count++; } public static uint Scan(uint usage) => usage switch { 4=>0x1e,5=>0x30,6=>0x2e,7=>0x20,8=>0x12,9=>0x21,10=>0x22,11=>0x23,12=>0x17,13=>0x24,14=>0x25,15=>0x26, 16=>0x32,17=>0x31,18=>0x18,19=>0x19,20=>0x10,21=>0x13,22=>0x1f,23=>0x14,24=>0x16,25=>0x2f,26=>0x11,27=>0x2d,28=>0x15,29=>0x2c, >=30 and <=38=>usage-28,39=>0x0b,40=>0x1c,41=>1,42=>0xe,43=>0xf,44=>0x39,45=>0xc,46=>0xd,47=>0x1a,48=>0x1b,49=>0x2b, 50=>0x2b,51=>0x27,52=>0x28,53=>0x29,54=>0x33,55=>0x34,56=>0x35,57=>0x3a,>=58 and <=67=>usage+1,68=>0x57,69=>0x58, 70=>0x137,71=>0x46,73=>0x152,74=>0x147,75=>0x149,76=>0x153,77=>0x14f,78=>0x151,79=>0x14d,80=>0x14b,81=>0x150,82=>0x148, 83=>0x45,84=>0x135,85=>0x37,86=>0x4a,87=>0x4e,88=>0x11c,89=>0x4f,90=>0x50,91=>0x51,92=>0x4b,93=>0x4c,94=>0x4d,95=>0x47,96=>0x48,97=>0x49,98=>0x52,99=>0x53, 224=>0x1d,225=>0x2a,226=>0x38,227=>0x15b,228=>0x11d,229=>0x36,230=>0x138,231=>0x15c,_=>0 }; }