Patch
Overwrite bytes in the clone's descriptorsPATCH stores byte overwrites for the descriptors the clone serves at enumeration. The set persists in NVS under the device's VID:PID, and the clone serves the copy taken at its last presentation.
real device --USB3--> HOST chip
|
| snapshot of every descriptor
v
NVS, per VID:PID ---> DEVICE chip copy the stored set, overwrite in place
[ stored set ] |
| clone checks, consistency checks
v
game PC <---USB1----- the clone patched, or unpatched when a check fails| Action | Value | Effect |
|---|---|---|
| store | 0 to 4 | add, overwrite or remove one patch in the stored set |
| APPLY | 0xFE | present the clone again with the stored set |
| CLEAR | 0xFF | erase the set; a clone serving patches is presented again without them |
The value is the first payload byte. Read the set back with QUERY(PATCHES) and one patch in full with QUERY(PATCH_ENTRY).
A stored set reaches the game PC only under OPTION(IMPERFECT); see the gate.
PATCH
Store, overwrite, or remove one patchA patch names a descriptor, an offset into it, and the bytes to write there. Opcode 0x1D.
PATCH 0x1D · payload 5 + n bytes
Fire-and-forget
| Offset | Field | Type | Notes |
|---|---|---|---|
| 0 | section | u8 | descriptor (table below) |
| 1 | cfg | u8 | configuration's position in capture order, 0 first |
| 2 | index | u8 | interface number for REPORT, string index for STRING |
| 3 | offset | u16 | first byte overwritten, counted from the descriptor's first byte; little-endian |
| 5 | bytes | u8[] | overwrite, 0 to 505 bytes, delimited by the frame LEN |
| Value | Name | Effect |
|---|---|---|
0 | DEVICE | the 18-byte device descriptor; cfg and index ignored |
1 | CONFIG | configuration cfg, every byte of its wTotalLength; index ignored |
2 | REPORT | the HID report descriptor of interface index in configuration cfg |
3 | STRING | string index, replaced whole; cfg and offset ignored |
4 | BOS | the BOS descriptor; cfg and index ignored |
A patch is stored under (section, cfg, index, offset) exactly as sent, ignored fields included, so send 0 in those.
| Sent | Effect |
|---|---|
| bytes at a new key | appended to the set |
| other bytes at a stored key | replaced and moved to the end of the set |
| the bytes already stored at that key | no change: the patch keeps its place and NVS is untouched |
| zero bytes at a stored key | removed |
| Refused when | Why |
|---|---|
| no device attached | the set is keyed on the attached device; a device the box refuses to clone still counts as attached |
| store payload under 5 bytes | the first five are the address |
section is 5 to 0xFD | five descriptor kinds, then APPLY and CLEAR |
bytes longer than 505 | the patch must fit its RESP(PATCH_ENTRY) read-back in one frame |
| a 17th key | 16 patches per device; RESP(PATCHES) sets FULL |
| the set's bytes would pass 1024 | one pool holds every patch's bytes; FULL is set, and an overwrite that does not fit leaves the old patch in place |
zero bytes at an unstored key | nothing to remove |
A change is written to NVS at once and reaches the game PC at the clone's next presentation. A refused frame has no reply: read RESP(PATCHES) for the patch, or for FULL.
Set bcdDevice to 0x0200: section DEVICE, offset = 12, bytes 00 02:
+--------+--------+--------+--------+--------+--------+ | A5 | 1D | 00 | 07 00 | 00 | 00 | +--------+--------+--------+--------+--------+--------+ | SOF | TYPE | SEQ | LEN | section| cfg | +--------+--------+--------+--------+--------+--------+ +--------+--------+--------+--------+ | 00 | 0C 00 | 00 02 | lo hi | +--------+--------+--------+--------+ | index | offset | bytes | CRC16 | +--------+--------+--------+--------+
Library binding: set_patch.
APPLY
Present the clone again with the stored setAPPLY is a PATCH frame with section 0xFE; the rest of the payload is ignored.
PATCH 0x1D · payload 1 byte
Fire-and-forget
| Offset | Field | Type | Notes |
|---|---|---|---|
| 0 | section | u8 | 0xFE |
When the stored set differs from the one the clone serves, the box presents the clone again with a copy of the stored set and runs the checks. An emptied set presents it unpatched.
| Refused when | Why |
|---|---|
| the stored set is the one the clone serves | it would change nothing and cost the game PC a re-enumeration |
| the stored set failed a check at its last presentation and is unchanged since | it would fail the same way; PATCHES b2 is set |
OPTION(IMPERFECT) off | the clone serves no patches without the opt-in |
| no device attached | the box rebuilds the clone from the snapshot taken at attach |
Present the stored set:
+--------+--------+--------+--------+--------+--------+ | A5 | 1D | 01 | 01 00 | FE | lo hi | +--------+--------+--------+--------+--------+--------+ | SOF | TYPE | SEQ | LEN | section| CRC16 | +--------+--------+--------+--------+--------+--------+
Library binding: apply_patch.
CLEAR
Erase the set and present the clone without itCLEAR is a PATCH frame with section 0xFF. It runs with the opt-in off and with the device unplugged.
PATCH 0x1D · payload 1 byte
Fire-and-forget
| Offset | Field | Type | Notes |
|---|---|---|---|
| 0 | section | u8 | 0xFF |
| State | Effect |
|---|---|
| the clone serves patches | the set and its NVS key are erased, and the clone is presented again unpatched, as APPLY presents it |
| the clone serves none | the set and its NVS key are erased; the clone is unchanged |
Either way RESP(PATCHES) then reads with REFUSED and FULL clear. With the device unplugged, CLEAR erases the key of the last device attached since boot, or nothing after a boot with none attached.
Erase the set:
+--------+--------+--------+--------+--------+--------+ | A5 | 1D | 02 | 01 00 | FF | lo hi | +--------+--------+--------+--------+--------+--------+ | SOF | TYPE | SEQ | LEN | section| CRC16 | +--------+--------+--------+--------+--------+--------+
Library binding: clear_patch.
Presentation
When the stored set reaches the game PCThe clone serves the set it was last presented with, every section included. A presentation copies the stored set, runs the checks and re-enumerates the clone on the game PC.
PATCH --> stored set NVS, read back by QUERY(PATCHES)
|
| presentation: attach, APPLY, CLEAR, opt-in toggle
v
served set what the game PC reads at enumeration| Event | Presents when | Serves |
|---|---|---|
| the device attaches | always | the stored set, under the opt-in |
| APPLY | the stored set differs from the served one | the stored set, or none when it is empty |
| CLEAR | the clone serves patches | none |
| the opt-in turned on | the stored set differs from the served one and is not refused | the stored set |
| the opt-in turned off | the clone serves patches | none |
RESP(PATCHES) lists the stored set; its flags say whether the clone serves it.
A presentation re-clones the device, as a replug does. It releases injection, locks, transforms, rewrite rules, the loaded clip and its triggers, and the CATCH table. The release moves the session count.
Sections
Per-section behaviourA patch writes over bytes the descriptor already has, except STRING, which replaces the string. One that would run past the descriptor's end is skipped whole, never truncated.
device descriptor 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 offset 12, 2 bytes xx xx lands offset 17, 2 bytes xx xx past the end: skipped
| Name | Behaviour |
|---|---|
| DEVICE | Writes the served identity, which QUERY(DEVICE_INFO) then reports. The set and every learned setting stay keyed on the real VID:PID. Byte 17, bNumConfigurations, is set to the captured configuration count after the patch lands. |
| CONFIG | Writes the served configuration, which the box then parses for its interfaces and endpoints, so a patched endpoint faces the same clone checks a native one does. |
| REPORT | Writes the report descriptor before the box parses it, so INJECT, LOCK and the emitted report follow the patched layout. |
| STRING | Serves the bytes as the whole string, any length up to 127, one UTF-16 code unit per byte; a 0x00 byte ends it. Index 0, the language list, is not patched. |
| BOS | Writes the BOS descriptor, on a device that has one. |
With two STRING patches on one index, the one listed first in RESP(PATCHES) is served. An overwrite moves a patch to the end of that list.
A CONFIG patch to bInterval is served as written, while the box keeps polling the device at the rate OPTION(EMIT) forces.
Checks
What a patched clone must passA set that fails a check the device passes without it is refused: the clone is presented unpatched and one LOG line names the check. A device that fails without the set is refused as any device is.
patched descriptors
|
+-- the clone checks every device faces
+-- framing: length and type fields
+-- bcdUSB against BOS
+-- wDescriptorLength against the report descriptor, every configuration
+-- wMaxPacketSize against the report, the configuration in force
|
+-- all hold --> clone served patched, PATCHES b0, HEALTH PATCH_ON
+-- one fails --> clone served unpatched, PATCHES b1 + b2, one LOG line| Refused when | Why |
|---|---|
a clone check fails, such as an endpoint wMaxPacketSize above 64 | the patched descriptors face the checks every device does |
a patch changes the device descriptor's bLength or bDescriptorType, or a configuration's or the BOS's bLength, bDescriptorType or wTotalLength | the box serves these as written: a larger length reads the host past the descriptor, a smaller one hides its tail |
bcdUSB 0x0201 or above with no BOS | a host requests the BOS of any 2.01 device |
a HID descriptor's wDescriptorLength differs from its served report descriptor, in any configuration | the game PC would ask for one length and get another |
an interrupt-IN endpoint of a HID interface, in the configuration in force, has a wMaxPacketSize below that interface's report | the report would not fit the packet the endpoint advertises |
The set stays stored with PATCHES b2 set until it changes, and APPLY skips it until then. Change or remove the failing patch and APPLY, or CLEAR the set.
Opt-in gate
Stored always, presented under the opt-inOPTION(IMPERFECT) gates whether the stored set reaches the clone.
| Item | Opt-in off |
|---|---|
PATCH | stored and written to NVS |
| APPLY | ignored |
| CLEAR | runs |
| a clone of the device | presented unpatched, with PATCHES b1 set while a set is stored |
A toggle presents the clone again when that changes the set it serves. When the clone needs the opt-in for anything else, the device chip reboots and re-clones, as the option describes.
While the clone serves a patched set, HEALTH sets PATCH_ON (0x0200) and QUERY(OPTIONS, 0) reports clone_imperfect = 1. A refused set sets neither.
Lifecycle
Stored configurationA set is stored per device, loaded when that device attaches, and kept until a removal, a CLEAR, or a RESET with the NVS flag erases it.
remove a PATCH with zero bytes at the patch's key CLEAR a PATCH with section 0xFF: the whole set RESET a RESET carrying the NVS flag: the whole store
| Event | Effect |
|---|---|
| the device detaches | the clone, PATCH_ON and REFUSED go; the set stays stored and readable until another device attaches |
| another device attaches | that device's set loads in its place, cloned or refused |