Skip to content

Commit

Permalink
Add appinfo v29
Browse files Browse the repository at this point in the history
  • Loading branch information
xPaw committed Jun 27, 2024
1 parent a8069e8 commit 56b1fec
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 8 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,29 @@ This is mostly intended as an example on how to read these files.

## appinfo.vdf
```
uint32 - MAGIC: 29 44 56 07
uint32 - UNIVERSE: 1
int64 - Offset to string table from start of the file
---- repeated app sections ----
uint32 - AppID
uint32 - size // until end of binary_vdf
uint32 - infoState // mostly 2, sometimes 1 (may indicate prerelease or no info)
uint32 - lastUpdated
uint64 - picsToken
20bytes - SHA1 // of text appinfo vdf, as seen in CMsgClientPICSProductInfoResponse.AppInfo.sha
uint32 - changeNumber
20bytes - SHA1 // of binary_vdf
variable - binary_vdf
---- end of section ----
uint32 - EOF: 0
---- offset to the string table ----
uint32 - Count of strings
null-term strings[count]
```

## appinfo.vdf (before june 2024)
```
uint32 - MAGIC: 28 44 56 07
uint32 - UNIVERSE: 1
---- repeated app sections ----
Expand Down
12 changes: 9 additions & 3 deletions SteamAppInfo.sln
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30011.22
# Visual Studio Version 17
VisualStudioVersion = 17.10.35013.160
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SteamAppInfoParser", "SteamAppInfoParser\SteamAppInfoParser.csproj", "{6B285D2E-0613-434D-86B5-A51CEA7D4445}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SteamAppInfoParser", "SteamAppInfoParser\SteamAppInfoParser.csproj", "{6B285D2E-0613-434D-86B5-A51CEA7D4445}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ValveKeyValue", "..\..\ValveResourceFormat\ValveKeyValue\ValveKeyValue\ValveKeyValue\ValveKeyValue.csproj", "{DA073F8F-2816-4F11-983E-8C0A18F290BB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -15,6 +17,10 @@ Global
{6B285D2E-0613-434D-86B5-A51CEA7D4445}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6B285D2E-0613-434D-86B5-A51CEA7D4445}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6B285D2E-0613-434D-86B5-A51CEA7D4445}.Release|Any CPU.Build.0 = Release|Any CPU
{DA073F8F-2816-4F11-983E-8C0A18F290BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DA073F8F-2816-4F11-983E-8C0A18F290BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA073F8F-2816-4F11-983E-8C0A18F290BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA073F8F-2816-4F11-983E-8C0A18F290BB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
76 changes: 71 additions & 5 deletions SteamAppInfoParser/AppInfo.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Text;
using ValveKeyValue;

namespace SteamAppInfoParser
{
class AppInfo
{
private const uint Magic29 = 0x07_56_44_29;
private const uint Magic28 = 0x07_56_44_28;
private const uint Magic = 0x07_56_44_27;

Expand All @@ -34,13 +37,33 @@ public void Read(Stream input)
using var reader = new BinaryReader(input);
var magic = reader.ReadUInt32();

if (magic != Magic && magic != Magic28)
if (magic != Magic && magic != Magic28 && magic != Magic29)
{
throw new InvalidDataException($"Unknown magic header: {magic:X}");
}

Universe = (EUniverse)reader.ReadUInt32();

var options = new KVSerializerOptions();

if (magic == Magic29)
{
var stringTableOffset = reader.ReadInt64();
var offset = reader.BaseStream.Position;
reader.BaseStream.Position = stringTableOffset;
var stringCount = reader.ReadUInt32();
var stringPool = new string[stringCount];

for (var i = 0; i < stringCount; i++)
{
stringPool[i] = ReadNullTermUtf8String(reader.BaseStream);
}

reader.BaseStream.Position = offset;

options.StringPool = stringPool;
}

var deserializer = KVSerializer.Create(KVSerializationFormat.KeyValues1Binary);

do
Expand All @@ -52,7 +75,8 @@ public void Read(Stream input)
break;
}

reader.ReadUInt32(); // size until end of Data
var size = reader.ReadUInt32(); // size until end of Data
var end = reader.BaseStream.Position + size;

var app = new App
{
Expand All @@ -64,20 +88,62 @@ public void Read(Stream input)
ChangeNumber = reader.ReadUInt32(),
};

if (magic == Magic28)
if (magic == Magic28 || magic == Magic29)
{
app.BinaryDataHash = new ReadOnlyCollection<byte>(reader.ReadBytes(20));
}

app.Data = deserializer.Deserialize(input);
app.Data = deserializer.Deserialize(input, options);

if (reader.BaseStream.Position != end)
{
throw new InvalidDataException();
}

Apps.Add(app);
} while (true);
}

public static DateTime DateTimeFromUnixTime(uint unixTime)
private static DateTime DateTimeFromUnixTime(uint unixTime)
{
return new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(unixTime);
}

private static string ReadNullTermUtf8String(Stream stream)
{
var buffer = ArrayPool<byte>.Shared.Rent(32);

try
{
var position = 0;

do
{
var b = stream.ReadByte();

if (b <= 0) // null byte or stream ended
{
break;
}

if (position >= buffer.Length)
{
var newBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length * 2);
Buffer.BlockCopy(buffer, 0, newBuffer, 0, buffer.Length);
ArrayPool<byte>.Shared.Return(buffer);
buffer = newBuffer;
}

buffer[position++] = (byte)b;
}
while (true);

return Encoding.UTF8.GetString(buffer[..position]);
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
}
}

0 comments on commit 56b1fec

Please sign in to comment.