fix: the emulated Steam Controller reports the firmware build Steam ships (#1323)

* fix: the emulated Steam Controller reports the firmware build Steam ships

* fix Triton firmware lookup for Steam root binary
This commit is contained in:
Kurt Himebauch authored and GitHub committed 2026-10-22 11:43:05 -04:00
1 parent 3a09e5698b
commit 701e70bd3e
1 file changed
+33 -1
+33 -1
View File
@@ -2650,6 +2650,38 @@ __attribute__((visibility("hidden"))) static int triton_get_feature(DeckHidraw &
static constexpr uint32_t TRITON_HWID = 72;
static constexpr uint32_t TRITON_FW_TIMESTAMP = 0x6AC686B3;
// Steam compares the controller's firmware with TRITON_FW_TS in its bundled hardwareupdater.cfg (and blocks
// with a required update below MUST_UPDATE_TRITON_FW_TS), so the emulated pad reports at least the build
// Steam ships.
static uint32_t triton_fw_timestamp() {
static const uint32_t timestamp = [] {
std::vector<std::string> roots;
char exe[PATH_MAX];
ssize_t n = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
if (n > 0) {
std::string path(exe, static_cast<size_t>(n));
size_t bin = path.rfind('/');
size_t root = bin == std::string::npos || bin == 0 ? std::string::npos : path.rfind('/', bin - 1);
if (bin != std::string::npos && bin > 0) roots.push_back(path.substr(0, bin));
if (root != std::string::npos) roots.push_back(path.substr(0, root));
}
if (const char *home = getenv("HOME")) roots.push_back(std::string(home) + "/.local/share/Steam");
uint32_t newest = TRITON_FW_TIMESTAMP;
for (const std::string &root : roots) {
std::ifstream cfg(root + "/bin/hardwareupdater/hardwareupdater.cfg");
std::string line;
while (std::getline(cfg, line)) {
if (line.rfind("TRITON_FW_TS:", 0) != 0) continue;
uint32_t shipped = static_cast<uint32_t>(strtoul(line.c_str() + 13, nullptr, 16));
if (shipped > newest) newest = shipped;
}
}
if (newest != TRITON_FW_TIMESTAMP) Logger::log("triton: reporting firmware %08x, the build Steam ships\n", newest);
return newest;
}();
return timestamp;
}
__attribute__((visibility("hidden"))) static int triton_pad_feature(DeckHidraw &triton, uint8_t *buf, size_t size, bool set) {
if (!buf || size < 2) {
errno = EINVAL;
@@ -2670,7 +2702,7 @@ __attribute__((visibility("hidden"))) static int triton_pad_feature(DeckHidraw &
switch (triton.pending_feature) {
case DECK_GET_ATTRIBUTES_VALUES: {
static const uint8_t kIds[] = {0x01, 0x02, 0x04, 0x09, 0x0a, 0x0b};
const uint32_t values[] = {static_cast<uint32_t>(TRITON_WIRED_PRODUCT), 0, TRITON_FW_TIMESTAMP, TRITON_HWID, 0, 4000};
const uint32_t values[] = {static_cast<uint32_t>(TRITON_WIRED_PRODUCT), 0, triton_fw_timestamp(), TRITON_HWID, 0, 4000};
reply[2] = sizeof(kIds) * 5;
for (size_t i = 0; i < sizeof(kIds); i++) {
reply[3 + i * 5] = kIds[i];