Walk Steam's binary KV1 stream (a top-level "RP" struct around string KVs) and collect every string KV at any depth. Type 0x00 starts a struct, 0x01 a string KV, 0x08 ends a struct. String KVs are null-terminated key + null-terminated value.
| 600 | // starts a struct, 0x01 a string KV, 0x08 ends a struct. String KVs |
| 601 | // are null-terminated key + null-terminated value. |
| 602 | static void ExtractStringKVs(const uint8* data, uint32 size, |
| 603 | std::vector<std::pair<std::string, std::string>>& out) |
| 604 | { |
| 605 | uint32 pos = 0; |
| 606 | int depth = 0; |
| 607 | auto readCStr = [&](std::string& s) -> bool { |
| 608 | uint32 start = pos; |
| 609 | while (pos < size && data[pos] != 0) ++pos; |
| 610 | if (pos >= size) return false; |
| 611 | s.assign(reinterpret_cast<const char*>(data + start), pos - start); |
| 612 | ++pos; |
| 613 | return true; |
| 614 | }; |
| 615 | while (pos < size) { |
| 616 | uint8 type = data[pos++]; |
| 617 | if (type == 0x08) { |
| 618 | if (depth > 0) { --depth; continue; } |
| 619 | break; |
| 620 | } |
| 621 | if (type == 0x00) { |
| 622 | std::string name; |
| 623 | if (!readCStr(name)) return; |
| 624 | ++depth; |
| 625 | } else if (type == 0x01) { |
| 626 | std::string key, value; |
| 627 | if (!readCStr(key) || !readCStr(value)) return; |
| 628 | out.emplace_back(std::move(key), std::move(value)); |
| 629 | } else { |
| 630 | return; |
| 631 | } |
| 632 | } |
| 633 | } |
| 634 | |
| 635 | // Patch the self Friend entry with the appid (0 = stopped) and per-app |
| 636 | // KVs. Mask status_flags's RichPresence bit (0x1000) on appid + empty |