Skip to content

Commit 636b477

Browse files
committed
Add decode support for new binary VDF
The new Steam beta introduced a new `appinfo.vdf` version. This appinfo.vdf V29 introduces a new binary VDF format which does not include field keys in binary VDF segments as-is. Instead, each key is represented by a 32-bit integer which needs to be mapped to an actual string using a table are stored at the end of the `appinfo.vdf` file. This also means the binary VDF segments are no longer self-contained. Also see SteamDatabase/SteamAppinfo#56b1fec7f5ce6be961c3e44cf9baf117e363ad91 Refs ValvePython/steam#462
1 parent d762926 commit 636b477

File tree

1 file changed

+24
-5
lines changed

1 file changed

+24
-5
lines changed

Diff for: vdf/__init__.py

+24-5
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ class COLOR(BASE_INT):
295295
BIN_INT64 = b'\x0A'
296296
BIN_END_ALT = b'\x0B'
297297

298-
def binary_loads(b, mapper=dict, merge_duplicate_keys=True, alt_format=False, raise_on_remaining=True):
298+
def binary_loads(b, mapper=dict, merge_duplicate_keys=True, alt_format=False, field_names=None, raise_on_remaining=True):
299299
"""
300300
Deserialize ``b`` (``bytes`` containing a VDF in "binary form")
301301
to a Python object.
@@ -307,13 +307,17 @@ def binary_loads(b, mapper=dict, merge_duplicate_keys=True, alt_format=False, ra
307307
``merge_duplicate_keys`` when ``True`` will merge multiple KeyValue lists with the
308308
same key into one instead of overwriting. You can se this to ``False`` if you are
309309
using ``VDFDict`` and need to preserve the duplicates.
310+
311+
``field_names`` contains a list of field keys. Newer `appinfo.vdf` format
312+
stores this at the very end of the file, and it is needed to deserialize
313+
the binary VDF segments in that file
310314
"""
311315
if not isinstance(b, bytes):
312316
raise TypeError("Expected s to be bytes, got %s" % type(b))
313317

314-
return binary_load(BytesIO(b), mapper, merge_duplicate_keys, alt_format, raise_on_remaining)
318+
return binary_load(BytesIO(b), mapper, merge_duplicate_keys, alt_format, field_names, raise_on_remaining)
315319

316-
def binary_load(fp, mapper=dict, merge_duplicate_keys=True, alt_format=False, raise_on_remaining=False):
320+
def binary_load(fp, mapper=dict, merge_duplicate_keys=True, alt_format=False, field_names=None, raise_on_remaining=False):
317321
"""
318322
Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
319323
binary VDF) to a Python object.
@@ -325,7 +329,13 @@ def binary_load(fp, mapper=dict, merge_duplicate_keys=True, alt_format=False, ra
325329
``merge_duplicate_keys`` when ``True`` will merge multiple KeyValue lists with the
326330
same key into one instead of overwriting. You can se this to ``False`` if you are
327331
using ``VDFDict`` and need to preserve the duplicates.
332+
333+
``field_names`` contains a list of field keys. Newer `appinfo.vdf` format
334+
stores this at the very end of the file, and it is needed to deserialize
335+
the binary VDF segments in that file
328336
"""
337+
use_field_indices = bool(field_names)
338+
329339
if not hasattr(fp, 'read') or not hasattr(fp, 'tell') or not hasattr(fp, 'seek'):
330340
raise TypeError("Expected fp to be a file-like object with tell()/seek() and read() returning bytes")
331341
if not issubclass(mapper, Mapping):
@@ -337,7 +347,14 @@ def binary_load(fp, mapper=dict, merge_duplicate_keys=True, alt_format=False, ra
337347
int64 = struct.Struct('<q')
338348
float32 = struct.Struct('<f')
339349

340-
def read_string(fp, wide=False):
350+
def read_string(fp, wide=False, use_field_indices=False):
351+
if use_field_indices:
352+
# Newer appinfo.vdf has the list of field names in an out-of-band
353+
# table
354+
index = int32.unpack(fp.read(int32.size))[0]
355+
356+
return field_names[index]
357+
341358
buf, end = b'', -1
342359
offset = fp.tell()
343360

@@ -382,7 +399,9 @@ def read_string(fp, wide=False):
382399
continue
383400
break
384401

385-
key = read_string(fp)
402+
# If 'field_names' was provided, each key is an int32 value that
403+
# corresponds to an entry in the field name array.
404+
key = read_string(fp, use_field_indices=use_field_indices)
386405

387406
if t == BIN_NONE:
388407
if merge_duplicate_keys and key in stack[-1]:

0 commit comments

Comments
 (0)