11using System ;
2+ using System . Buffers ;
23using System . Collections . Generic ;
34using System . Collections . ObjectModel ;
45using System . IO ;
6+ using System . Text ;
57using ValveKeyValue ;
68
79namespace SteamAppInfoParser
810{
911 class AppInfo
1012 {
13+ private const uint Magic29 = 0x07_56_44_29 ;
1114 private const uint Magic28 = 0x07_56_44_28 ;
1215 private const uint Magic = 0x07_56_44_27 ;
1316
@@ -34,13 +37,33 @@ public void Read(Stream input)
3437 using var reader = new BinaryReader ( input ) ;
3538 var magic = reader . ReadUInt32 ( ) ;
3639
37- if ( magic != Magic && magic != Magic28 )
40+ if ( magic != Magic && magic != Magic28 && magic != Magic29 )
3841 {
3942 throw new InvalidDataException ( $ "Unknown magic header: { magic : X} ") ;
4043 }
4144
4245 Universe = ( EUniverse ) reader . ReadUInt32 ( ) ;
4346
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+
4467 var deserializer = KVSerializer . Create ( KVSerializationFormat . KeyValues1Binary ) ;
4568
4669 do
@@ -52,7 +75,8 @@ public void Read(Stream input)
5275 break ;
5376 }
5477
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 ;
5680
5781 var app = new App
5882 {
@@ -64,20 +88,62 @@ public void Read(Stream input)
6488 ChangeNumber = reader . ReadUInt32 ( ) ,
6589 } ;
6690
67- if ( magic == Magic28 )
91+ if ( magic == Magic28 || magic == Magic29 )
6892 {
6993 app . BinaryDataHash = new ReadOnlyCollection < byte > ( reader . ReadBytes ( 20 ) ) ;
7094 }
7195
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+ }
73102
74103 Apps . Add ( app ) ;
75104 } while ( true ) ;
76105 }
77106
78- public static DateTime DateTimeFromUnixTime ( uint unixTime )
107+ private static DateTime DateTimeFromUnixTime ( uint unixTime )
79108 {
80109 return new DateTime ( 1970 , 1 , 1 , 0 , 0 , 0 , 0 , DateTimeKind . Utc ) . AddSeconds ( unixTime ) ;
81110 }
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+ }
82148 }
83149}
0 commit comments