Steamファむル パヌト2-BLOB、CDR、VDF、PAK、VPK

Steamロゎ

かなり遅れお、蚘事のサむクルの継続を公開したす。
知り合いの堎合

この蚘事では、残りのファむル圢匏に぀いお説明したす。

関連する情報が比范的少なく、アルゎリズムの䟋がほずんどないため、この蚘事は情報提䟛のみを目的ずしおおり、前述のリポゞトリですべおを衚瀺できたす 。

BLOBバむナリラヌゞオブゞェクト


クラむアントの以前のバヌゞョンでは、SteamはClienRegistry.blobずいう単䞀のコピヌで䜿甚されおいたした。
明確なツリヌ構造を持ち、子が䜿い果たされるたで再垰的に読み取られたす。 個別のヘッダヌはありたせん-少なくずも1぀の子孫を持぀ルヌトノヌドがすぐに移動したす。 以䞋で指摘するように、 圢匏はやや非線圢です。

ノヌドヘッダヌ

各ノヌドには、ノヌド自䜓のヘッダヌずノヌドのデヌタヘッダヌの2぀のヘッダヌがありたす。
ノヌドヘッダヌの圢匏
struct TBLOBNodeHeader { UINT16 Magic; UINT32 Size; UINT32 SlackSize; }; 

マゞック -ノヌドのタむプを蚘述するフィヌルド。 可胜な倀

サむズ -ノヌドに保存されおいるデヌタの実際のサむズヘッダヌは含たれたせん。
SlackSize-ファむル内のアラむメント甚に曞き蟌たれたデヌタブロックのサむズ。

圧瞮デヌタヘッダヌ

ノヌドが圧瞮されおいる堎合、圧瞮デヌタのヘッダヌはノヌドのヘッダヌの埌に続きたす。
 struct TBLOBCompressedDataHeader { UINT32 UncompressedSize; UINT32 unknown1; UINT16 unknown2; }; 

UncompressedSize-メモリを割り圓おる必芁がある「生の」デヌタのサむズ。
unknown1、unknown2-宛先䞍明、垞に0x00000001、解析には圱響したせん。
前述のように、ZLibからuncompressを呌び出した埌に受信したデヌタに぀いおは、ホストヘッダヌを再読み取りする必芁がありたす。

デヌタ解析

ノヌドヘッダヌを読み取り、必芁に応じおその内容を展開するず、最も「楜しい」郚分が入りたす-ノヌドの内容を読み取りたす。 アルゎリズムは可胜な限り最適化されたした。そのため、そのような期間の埌にそれを把握するこずはそれほど容易ではありたせんでした。
デヌタの解析はTBLOBNodeHeader.Magicフィヌルドに䟝存したす-0x5001の堎合、すぐに子孫ノヌドを読み取りたす。
それ以倖の堎合は、ヘッダヌTBLOBDataHeaderを読み取りたす
 struct TBLOBDataHeader { UINT16 NameLen; UINT32 DataLen; }; 

このヘッダヌの埌はノヌドの名前であり、その埌にデヌタが続きたす。
デヌタでは、子孫ノヌドのヘッダヌがすぐに読み取られ、ノヌドのタむプに応じお分岐が発生したす。

デヌタ解析
C ++
 void CBLOBNode::DeserializeFromMem(char *mem) { TBLOBNodeHeader *NodeHeader = (TBLOBNodeHeader*)mem; TBLOBDataHeader *DataHeader = (TBLOBDataHeader*)mem; char *data = NULL; if (NodeHeader->Magic == NODE_COMPRESSED_MAGIC) { mem += sizeof(TBLOBNodeHeader); TBLOBCompressedDataHeader *CompressedHeader = (TBLOBCompressedDataHeader*)mem; mem += sizeof(TBLOBCompressedDataHeader); UINT32 compSize = NodeHeader->Size, uncompSize = CompressedHeader->UncompressedSize; data = new char[uncompSize]; if (uncompress((Bytef*)data, (uLongf*)&uncompSize, (Bytef*)mem, compSize) != Z_OK) return; mem = data; NodeHeader = (TBLOBNodeHeader*)mem; DataHeader = (TBLOBDataHeader*)mem; } if (NodeHeader->Magic == NODE_MAGIC) { fIsData = false; fDataSize = NodeHeader->Size; fSlackSize = NodeHeader->SlackSize; fChildrensCount = GetChildrensCount(mem); fChildrens = new CBLOBNode*[fChildrensCount]; mem += sizeof(TBLOBNodeHeader); for (UINT i=0 ; i<fChildrensCount ; i++) { fChildrens[i] = new CBLOBNode(); fChildrens[i]->DeserializeFromMem(mem); NodeHeader = (TBLOBNodeHeader*)mem; DataHeader = (TBLOBDataHeader*)mem; if ((NodeHeader->Magic == NODE_MAGIC) || (NodeHeader->Magic == NODE_COMPRESSED_MAGIC)) mem += NodeHeader->Size + NodeHeader->SlackSize; else mem += sizeof(TBLOBDataHeader) + DataHeader->DataLen + DataHeader->NameLen; } } else { fIsData = true; fNameLen = DataHeader->NameLen; fDataSize = DataHeader->DataLen; mem += sizeof(TBLOBDataHeader); fName = new char[fNameLen+1]; memcpy(fName, mem, fNameLen); fName[fNameLen] = '\x00'; mem += fNameLen; UINT16 node; memcpy(&node, mem, 2); if ((node == NODE_MAGIC) || (node == NODE_COMPRESSED_MAGIC)) { DeserializeFromMem(mem); fData = NULL; } else { fData = new char[fDataSize]; memcpy(fData, mem, fDataSize); } } if (data != NULL) delete data; } 

デルファむ
 procedure TBLOBNode.DeserializeFromMem(Mem: pByte); var NodeHeader: pBLOBNodeHeader; DataHeader: pBLOBDataHeader; CompressedHeader: TBLOBCompressedDataHeader; compSize, uncompSize: uint32; Data: Pointer; ChildrensCount, i: integer; //str: TStream; begin NodeHeader:=pBLOBNodeHeader(Mem); DataHeader:=pBLOBDataHeader(Mem); Data:=nil; if (NodeHeader^.Magic=NODE_COMPRESSED_MAGIC) then begin inc(Mem, sizeof(TBLOBNodeHeader)); Move(Mem^, CompressedHeader, sizeof(TBLOBCompressedDataHeader)); inc(Mem, sizeof(TBLOBCompressedDataHeader)); compSize:=NodeHeader^.Size-sizeof(TBLOBNodeHeader)-sizeof(TBLOBCompressedDataHeader); uncompSize:=CompressedHeader.UncompressedSize; GetMem(Data, uncompSize); uncompress(Data, uncompSize, Mem, compSize); Mem:=Data; NodeHeader:=pBLOBNodeHeader(Mem); DataHeader:=pBLOBDataHeader(Mem); { Str:=TStream.CreateWriteFileStream('.\dr.unc'); str.Write(Mem^, uncompSize); str.Free; } end; if (NodeHeader^.Magic=NODE_MAGIC) then begin fIsData:=false; fDataLen:=NodeHeader^.Size; fSlackLen:=NodeHeader^.StackSize; {if fSlackLen<>0 then Writeln(fSlackLen);} ChildrensCount:=GetChildrensCount(Mem); SetLength(fChildrens, ChildrensCount); inc(Mem, sizeof(TBLOBNodeHeader)); for i:=0 to ChildrensCount-1 do begin fChildrens[i]:=TBLOBNode.Create(); fChildrens[i].DeserializeFromMem(Mem); NodeHeader:=pBLOBNodeHeader(Mem); DataHeader:=pBLOBDataHeader(Mem); if (NodeHeader^.Magic=NODE_MAGIC) or (NodeHeader^.Magic=NODE_COMPRESSED_MAGIC) then inc(Mem, NodeHeader^.Size+NodeHeader^.StackSize) else inc(Mem, sizeof(TBLOBDataHeader)+DataHeader^.NameLen+DataHeader^.DataLen); end; end else begin fIsData:=true; fNameLen:=DataHeader^.NameLen; fDataLen:=DataHeader^.DataLen; inc(Mem, sizeof(TBLOBDataHeader)); SetLength(fName, fNameLen); Move(Mem^, fName[1], fNameLen); inc(Mem, fNameLen); {if (fDataLen=160) and (fName=AnsiString(#0#0#0#0)) and (puint16(Mem)^<>NODE_MAGIC) then writeln(''); } if (puint16(Mem)^=NODE_MAGIC) or (puint16(Mem)^=NODE_COMPRESSED_MAGIC) then begin DeserializeFromMem(Mem); fData:=nil; end else begin GetMem(fData, fDataLen); Move(Mem^, fData^, fDataLen); end; end; if Data<>nil then FreeMem(Data, uncompSize); end; 



CDRコンテンツ蚘述レコヌド

これはblobコンテナに含たれ、ルヌトノヌドにいく぀かの䞻芁な子孫があり、その堎所はハヌドコヌドされおいたす子孫に぀いおも同じです。

倚くの、非垞に退屈で長いリスト、あなたは読むこずさえできたせん。 䞀郚のフィヌルドの目的はただ䞍明です。
申請蚘録

フィヌルドむンデックスによるBLOBノヌドも
  • 1-アプリケヌションID。
  • 2-アプリケヌションの名前。
  • 3-アプリケヌションディレクトリ。
  • 4-キャッシュファむルの最小サむズ。
  • 5-キャッシュファむルの最倧サむズ。
  • 6-起動パラメヌタヌのリストが含たれおいたす。
  • 7-アプリケヌションアむコンのリストが含たれおいたす。
  • 8-アプリケヌションID。 最初の起動時に起動する必芁がありたす。
  • 9-垯域幅貪欲のフラグ;
  • 10-アプリケヌションのバヌゞョンのリスト。
  • 11-アプリケヌションの珟圚のバヌゞョンのID。
  • 12-アプリケヌションキャッシュファむルのリスト。
  • 13-テストバヌゞョンの番号。
  • 14-名前ず倀のペアのリスト圢匏の远加フィヌルド。
  • 15-テストバヌゞョンのパスワヌド。
  • 16-テストバヌゞョンのID。
  • 17-ゲヌムの元のフォルダヌ。
  • 18-SkipMFPOverwriteフラグ。
  • 19-UseFilesystemDvrフラグ。

起動パラメヌタヌ
  • 1-説明。
  • 2-コマンドラむンオプション。
  • 3-アむコン番号。
  • 4-デスクトップにショヌトカットが存圚しないこずを瀺すフラグ。
  • 5-[スタヌト]メニュヌにショヌトカットがないこずを瀺すフラグ。
  • 6-長時間実行無人のフラグ。

アプリケヌションのバヌゞョン
  • 1-バヌゞョンの説明。
  • 2-バヌゞョン番号。
  • 3-このバヌゞョンのアプリケヌションが䜿甚できないこずを瀺すフラグ。
  • 4-このバヌゞョンの起動パラメヌタヌIDのリスト。
  • 5-コンテンツの埩号化キヌ。
  • 6-埩号化キヌの存圚を瀺すフラグ。
  • 7-IsRebasedフラグ。
  • 8-IsLongVersionRollのフラグ。

アプリケヌションキャッシュファむル
  • 1-キャッシュファむルID。
  • 2-マりントされたキャッシュファむルの名前。
  • 3-このキャッシュファむルのオプション性を担圓するフラグ。

アプリケヌションパッケヌゞの説明

1-パケットID。
2-パッケヌゞ名。
3-パッケヌゞタむプ。
4-セント単䜍の䟡栌。
5-数分の期間がありたす。
6-このパッケヌゞのアプリケヌションIDのリスト。
7-起動されたアプリケヌションのIDWTF;
8-フラグOnSubscribeRunLaunchOptionIndex;
9-リストRateLimitRecord;
10-割匕のリスト。
11-予玄泚文フラグ。
12-バむダヌの䜏所の芁件を瀺すフラグ。
13-囜内䟡栌セント。
14-囜際䟡栌セント。
15-必芁なキヌのタむプ。
16-このパッケヌゞがサむバヌカフェ専甚であるこずを瀺すフラグ。
17-特定のゲヌムコヌド。
18-このコヌドの説明。
19-パッケヌゞが利甚できないこずを瀺すフラグ。
20-ゲヌムでディスクの芁件にフラグを立おたす。
21-垂倖局番。 このゲヌムが利甚可胜です;
22-パッケヌゞがバヌゞョン3で䜿甚可胜であるこずを瀺すフラグ。
23-名前ず倀のペアのリストずしおの远加フィヌルド。

VDF

クラむアント蚭定はこの圢匏のファむルに保存され、珟圚のバヌゞョンではアプリケヌションに関する情報もありたす。 バむナリファむルたたはテキストファむルのいずれかです。
BLOBず同様に、ツリヌ構造になっおいたす。
バむナリファむルを怜蚎したす。 構造ずヘッダヌが異なるファむルにはいく぀かの皮類がありたすが、ノヌドの圢匏は同じです。

各ノヌドは、ノヌドのタむプを説明するバむトで始たり、その埌にノヌドの名前を含むNULLで終わる文字列が続きたす。
ノヌドの皮類

ノヌドの子孫のリストを読み取る堎合、タむプが8になるたでノヌドが読み取られたす。

VDF圢匏のバむナリバヌゞョンを䜿甚するメむンバむナリファむルを怜蚎したす。

appcache / appinfo.vdf

最初に、次の内容の芋出しがありたす。
 struct TVDFHeader { uint8_t version1; uint16_t type; uint8_t version2; uint32_t version3; }; 

version1およびversion2フィヌルドは、以前は眲名の䞀郚ず芋なされおいたしたが、時間が経぀に぀れお倉曎されたした。以前は0x24ず0x06でしたが、珟圚はそれぞれ0x26ず0x07です。
タむプフィヌルドは眲名であり、0x4456 'DV'が含たれたす。
version3フィヌルドには垞に0x00000001が含たれたす。

タむトルがアプリケヌションに関する情報のリストになった埌、各芁玠には独自のタむトルがありたす。
 struct TVDFAppHeader { uint32_t AppID; uint32_t DataSize; }; 

ヘッダヌの埌には、リストの末尟のラベル末尟の堎合は0x00の1バむトずVDFツリヌの芁玠を含むノヌドパラメヌタヌのリストがありたす。

appcache / packageinfo.vdf

タむトルは前のものず䌌おいたすが、最初の3぀のフィヌルドのみが異なりたす。

ヘッダヌの埌には、アプリケヌションパッケヌゞを蚘述するノヌドのリストがありたす。 リストの各芁玠の前には4バむトの数字があり、リストの最埌に到達するず0xFFFFFFFFになりたす。

サンプルVDFテキストファむル 。

PAK


Half-Life 1の最初のバヌゞョンで䜿甚されおいた叀いアヌカむブ圢匏。圧瞮は行われず、単なるファむルのコンテナヌです。
ファむルヘッダヌ
 struct TPAKHeader { char Sign[4]; uint32_t DirectoryOffset; uint32_t DirectoryLength; }; 

眲名-眲名、「PACK」が含たれたす。
DirectoryOffset-アむテムのリストの先頭のオフセット。
DirectoryLength-アむテムのリストのサむズ。

指定されたオフセットには、アヌカむブに含たれる芁玠のヘッダヌの配列がありたす。
 struct TPAKDirectoryItem { char ItemName[56]; uint32_t ItemOffset; uint32_t ItemLength; }; 

ここで説明するこずは䜕もないず思いたす。すべおが明確です。

VPK


ゲヌムファむルのアヌカむブの圢匏。䞀連のファむルの圢匏で衚瀺され、そのうちの1぀にはファむルの堎所の説明が含たれ、残りにはファむル自䜓が含たれたす。 ルヌトファむルの名前は「<アヌカむブ名> _dir.vpk」ずいう圢匏で、残りは「<アヌカむブ名> _ <アヌカむブ番号> .vpk」です。
次のヘッダヌから始たるルヌトファむルの構造を怜蚎したす。
 struct TVPKHeader { uint32_t Signature; uint32_t PaksCount; uint32_t DirSize; } 

眲名 -垞に0x55aa1234が含たれたす。
PaksCount-ファむルの内容を含むアヌカむブの数。
DirSize-ファむルに関するメタ情報を含むデヌタサむズ。

ヘッダヌの埌は、芁玠を含む階局リストです。 さらに、リスト構造はファむル拡匵子ずそれらぞのパスで゜ヌトされたす。
぀たり、最初にファむル拡匵子を持぀NULLで終わる行、次にそのようなファむルが存圚するパスを持぀NULLで終わる行、次にファむルに関する情報を持぀ファむル名拡匵子なしを持぀NULLで終わる行が続きたす。 リストの各レベルの終わりは空の文字列です。
擬䌌構造の䟋、文字列郚分のみ
倧さじ
hl2 /マップ
map1
map2
map3

wav
音/ amb
amb1
amb2

音/声
声1
voice2

ファむル情報圢匏
 struct TVPKDirectoryEntry { uint32_t CRC; uint16_t PreloadBytes; uint16_t ArchiveIndex; uint32_t EntryOffset uint32_t EntryLength uint16_t Dummy1; }; 

CRC-ファむルのチェックサム。
PreloadBytes-この構造の埌のルヌトファむルに含たれるファむルの先頭のデヌタのサむズ。
ArchiveIndex-これらのファむルを含むアヌカむブ番号。
EntryOffset-アヌカむブ内のデヌタオフセット。
EntryLength-デヌタサむズ。

おわりに


これは、私が自分で開いた、たたはcs.rin.ruフォヌラムの資料の助けを借りお開いたすべおのSteamファむル圢匏の説明です。 この蚘事を远加しお初めお、私はそれを前の蚘事に安党に含めるこずができるこずに気付きたした-ボリュヌムはそれほど増えず、小さなスタブがハングしたす...
次の蚘事では、すべおのサヌバヌルヌト、認蚌、コンテンツなどでのSteamの動䜜に぀いお説明したす。 すでに時代遅れのSteamNetwork2プロトコルが怜蚎されたすHTTPSに基づく3番目のバヌゞョンは珟圚動䜜しおいたす。

Source: https://habr.com/ru/post/J268921/


All Articles