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
};
}
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
};
}