1
1
using System ;
2
+ using System . Buffers ;
2
3
using System . Collections . Generic ;
3
4
using System . Collections . ObjectModel ;
4
5
using System . IO ;
6
+ using System . Text ;
5
7
using ValveKeyValue ;
6
8
7
9
namespace SteamAppInfoParser
8
10
{
9
11
class AppInfo
10
12
{
13
+ private const uint Magic29 = 0x07_56_44_29 ;
11
14
private const uint Magic28 = 0x07_56_44_28 ;
12
15
private const uint Magic = 0x07_56_44_27 ;
13
16
@@ -34,13 +37,33 @@ public void Read(Stream input)
34
37
using var reader = new BinaryReader ( input ) ;
35
38
var magic = reader . ReadUInt32 ( ) ;
36
39
37
- if ( magic != Magic && magic != Magic28 )
40
+ if ( magic != Magic && magic != Magic28 && magic != Magic29 )
38
41
{
39
42
throw new InvalidDataException ( $ "Unknown magic header: { magic : X} ") ;
40
43
}
41
44
42
45
Universe = ( EUniverse ) reader . ReadUInt32 ( ) ;
43
46
47
+ var options = new KVSerializerOptions ( ) ;
48
+
49
+ if ( magic == Magic29 )
50
+ {
51
+ var stringTableOffset = reader . ReadInt64 ( ) ;
52
+ var offset = reader . BaseStream . Position ;
53
+ reader . BaseStream . Position = stringTableOffset ;
54
+ var stringCount = reader . ReadUInt32 ( ) ;
55
+ var stringPool = new string [ stringCount ] ;
56
+
57
+ for ( var i = 0 ; i < stringCount ; i ++ )
58
+ {
59
+ stringPool [ i ] = ReadNullTermUtf8String ( reader . BaseStream ) ;
60
+ }
61
+
62
+ reader . BaseStream . Position = offset ;
63
+
64
+ options . StringPool = stringPool ;
65
+ }
66
+
44
67
var deserializer = KVSerializer . Create ( KVSerializationFormat . KeyValues1Binary ) ;
45
68
46
69
do
@@ -52,7 +75,8 @@ public void Read(Stream input)
52
75
break ;
53
76
}
54
77
55
- reader . ReadUInt32 ( ) ; // size until end of Data
78
+ var size = reader . ReadUInt32 ( ) ; // size until end of Data
79
+ var end = reader . BaseStream . Position + size ;
56
80
57
81
var app = new App
58
82
{
@@ -64,20 +88,62 @@ public void Read(Stream input)
64
88
ChangeNumber = reader . ReadUInt32 ( ) ,
65
89
} ;
66
90
67
- if ( magic == Magic28 )
91
+ if ( magic == Magic28 || magic == Magic29 )
68
92
{
69
93
app . BinaryDataHash = new ReadOnlyCollection < byte > ( reader . ReadBytes ( 20 ) ) ;
70
94
}
71
95
72
- app . Data = deserializer . Deserialize ( input ) ;
96
+ app . Data = deserializer . Deserialize ( input , options ) ;
97
+
98
+ if ( reader . BaseStream . Position != end )
99
+ {
100
+ throw new InvalidDataException ( ) ;
101
+ }
73
102
74
103
Apps . Add ( app ) ;
75
104
} while ( true ) ;
76
105
}
77
106
78
- public static DateTime DateTimeFromUnixTime ( uint unixTime )
107
+ private static DateTime DateTimeFromUnixTime ( uint unixTime )
79
108
{
80
109
return new DateTime ( 1970 , 1 , 1 , 0 , 0 , 0 , 0 , DateTimeKind . Utc ) . AddSeconds ( unixTime ) ;
81
110
}
111
+
112
+ private static string ReadNullTermUtf8String ( Stream stream )
113
+ {
114
+ var buffer = ArrayPool < byte > . Shared . Rent ( 32 ) ;
115
+
116
+ try
117
+ {
118
+ var position = 0 ;
119
+
120
+ do
121
+ {
122
+ var b = stream . ReadByte ( ) ;
123
+
124
+ if ( b <= 0 ) // null byte or stream ended
125
+ {
126
+ break ;
127
+ }
128
+
129
+ if ( position >= buffer . Length )
130
+ {
131
+ var newBuffer = ArrayPool < byte > . Shared . Rent ( buffer . Length * 2 ) ;
132
+ Buffer . BlockCopy ( buffer , 0 , newBuffer , 0 , buffer . Length ) ;
133
+ ArrayPool < byte > . Shared . Return ( buffer ) ;
134
+ buffer = newBuffer ;
135
+ }
136
+
137
+ buffer [ position ++ ] = ( byte ) b ;
138
+ }
139
+ while ( true ) ;
140
+
141
+ return Encoding . UTF8 . GetString ( buffer [ ..position ] ) ;
142
+ }
143
+ finally
144
+ {
145
+ ArrayPool < byte > . Shared . Return ( buffer ) ;
146
+ }
147
+ }
82
148
}
83
149
}
0 commit comments