aboutsummaryrefslogtreecommitdiff
path: root/dep/CascLib/src/CascStructs.h
blob: 42478c324f038b094d2fb9dba8ded4c0c12c8b7a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
/*****************************************************************************/
/* CascStructs.h                          Copyright (c) Ladislav Zezula 2019 */
/*---------------------------------------------------------------------------*/
/* On-disk structures for CASC storages                                      */
/*---------------------------------------------------------------------------*/
/*   Date    Ver   Who  Comment                                              */
/* --------  ----  ---  -------                                              */
/* 28.04.19  1.00  Lad  The first version of CascStructs.h                   */
/*****************************************************************************/

#ifndef __CASC_STRUCTS_H__
#define __CASC_STRUCTS_H__

//-----------------------------------------------------------------------------
// Common definitions

#define CASC_INDEX_COUNT          0x10              // Number of index files
#define CASC_CKEY_SIZE            0x10              // Size of the content key
#define CASC_EKEY_SIZE            0x09              // Size of the encoded key
#define CASC_MAX_DATA_FILES      0x100              // Maximum number of data files

//-----------------------------------------------------------------------------
// The index files structures

#define FILE_INDEX_PAGE_SIZE    0x200               // Number of bytes in one page of EKey items

// Structure describing the 32-bit block size and 32-bit Jenkins hash of the block
typedef struct _BLOCK_SIZE_AND_HASH
{
    DWORD cbBlockSize;
    DWORD dwBlockHash;

} BLOCK_SIZE_AND_HASH, *PBLOCK_SIZE_AND_HASH;

// Checksum describing a block in the index file (v2)
typedef struct _FILE_INDEX_GUARDED_BLOCK
{
    DWORD BlockSize;
    DWORD BlockHash;

} FILE_INDEX_GUARDED_BLOCK, *PFILE_INDEX_GUARDED_BLOCK;

// Structure of the header of the index files version 1
typedef struct _FILE_INDEX_HEADER_V1
{
    USHORT IndexVersion;                            // Must be 0x05
    BYTE  BucketIndex;                              // The bucket index of this file; should be the same as the first byte of the hex filename. 
    BYTE  align_3;
    DWORD field_4;
    ULONGLONG field_8;
    ULONGLONG SegmentSize;                          // Size of one data segment (aka data.### file)
    BYTE  EncodedSizeLength;                        // Length, in bytes, of the EncodedSize in the EKey entry
    BYTE  StorageOffsetLength;                      // Length, in bytes, of the StorageOffset field in the EKey entry
    BYTE  EKeyLength;                               // Length of the encoded key (bytes)
    BYTE  FileOffsetBits;                           // Number of bits of the archive file offset in StorageOffset field. Rest is data segment index
    DWORD EKeyCount1;
    DWORD EKeyCount2;
    DWORD KeysHash1;
    DWORD KeysHash2;
    DWORD HeaderHash;
} FILE_INDEX_HEADER_V1, *PFILE_INDEX_HEADER_V1;

typedef struct _FILE_INDEX_HEADER_V2
{
    USHORT IndexVersion;                            // Must be 0x07
    BYTE   BucketIndex;                             // The bucket index of this file; should be the same as the first byte of the hex filename. 
    BYTE   ExtraBytes;                              // Unknown; must be 0
    BYTE   EncodedSizeLength;                       // Length, in bytes, of the EncodedSize in the EKey entry
    BYTE   StorageOffsetLength;                     // Length, in bytes, of the StorageOffset field in the EKey entry
    BYTE   EKeyLength;                              // Length of the encoded key (bytes)
    BYTE   FileOffsetBits;                          // Number of bits of the archive file offset in StorageOffset field. Rest is data segment index
    ULONGLONG SegmentSize;                          // Size of one data segment (aka data.### file)

} FILE_INDEX_HEADER_V2, *PFILE_INDEX_HEADER_V2;

// The EKey entry from the ".idx" files. Note that the lengths are optional and controlled by the FILE_INDEX_HEADER_Vx
typedef struct _FILE_EKEY_ENTRY
{
    BYTE EKey[CASC_EKEY_SIZE];                      // The first 9 bytes of the encoded key
    BYTE FileOffsetBE[5];                           // Index of data file and offset within (big endian).
    BYTE EncodedSize[4];                            // Encoded size (big endian). This is the size of encoded header, all file frame headers and all file frames
} FILE_EKEY_ENTRY, *PFILE_EKEY_ENTRY;

//-----------------------------------------------------------------------------
// The archive index (md5.index) files structures
// https://wowdev.wiki/TACT#CDN_File_Organization

template <int CHKSUM_LENGTH>
struct FILE_INDEX_FOOTER
{
    BYTE TocHash[MD5_HASH_SIZE];                    // Client tries to read with 0x10, then backs off in size when smaller
    BYTE Version;                                   // Version of the index header
    BYTE Reserved[2];                               // Length, in bytes, of the file offset field
    BYTE PageSizeKB;                                // Length, in kilobytes, of the index page
    BYTE OffsetBytes;                               // Normally 4 for archive indices, 6 for group indices, and 0 for loose file indices
    BYTE SizeBytes;                                 // Normally 4
    BYTE EKeyLength;                                // Normally 16
    BYTE FooterHashBytes;                           // Normally 8, <= 0x10
    BYTE ElementCount[4];                           // BigEndian in _old_ versions (e.g. 18179)
    BYTE FooterHash[CHKSUM_LENGTH];
};

//-----------------------------------------------------------------------------
// The ENCODING manifest structures
//
// The ENCODING file is in the form of:
// * File header. Fixed length.
// * Encoding Specification (ESpec) in the string form. Length is stored in FILE_ENCODING_HEADER::ESpecBlockSize
// https://wowdev.wiki/CASC#Encoding
// https://wowdev.wiki/TACT#Encoding_table
//

#define FILE_MAGIC_ENCODING 0x4E45                  // 'EN'

// File header of the ENCODING manifest
typedef struct _FILE_ENCODING_HEADER
{
    USHORT Magic;                                   // FILE_MAGIC_ENCODING ('EN')
    BYTE Version;                                   // Expected to be 1 by CascLib
    BYTE CKeyLength;                                // The content key length in ENCODING file. Usually 0x10
    BYTE EKeyLength;                                // The encoded key length in ENCODING file. Usually 0x10
    BYTE CKeyPageSize[2];                           // Size of the CKey page, in KB (big-endian)
    BYTE EKeyPageSize[2];                           // Size of the EKey page, in KB (big-endian)
    BYTE CKeyPageCount[4];                          // Number of CKey pages in the table (big endian)
    BYTE EKeyPageCount[4];                          // Number of EKey pages in the table (big endian)
    BYTE field_11;                                  // Asserted to be zero by the agent
    BYTE ESpecBlockSize[4];                         // Size of the ESpec string block

} FILE_ENCODING_HEADER, *PFILE_ENCODING_HEADER;

// Page header of the ENCODING manifest
typedef struct _FILE_CKEY_PAGE
{
    BYTE FirstKey[MD5_HASH_SIZE];                   // The first CKey/EKey in the segment
    BYTE SegmentHash[MD5_HASH_SIZE];                // MD5 hash of the entire segment

} FILE_CKEY_PAGE, *PFILE_CKEY_PAGE;

// Single entry in the page
typedef struct _FILE_CKEY_ENTRY
{
    USHORT EKeyCount;                               // Number of EKeys
    BYTE ContentSize[4];                            // Content file size (big endian)
    BYTE CKey[CASC_CKEY_SIZE];                      // Content key. This is MD5 of the file content
    BYTE EKey[CASC_CKEY_SIZE];                      // Encoded key. This is (trimmed) MD5 hash of the file header, containing MD5 hashes of all the logical blocks of the file

} FILE_CKEY_ENTRY, *PFILE_CKEY_ENTRY;

typedef struct _FILE_ESPEC_ENTRY
{
    BYTE ESpecKey[MD5_HASH_SIZE];                   // The ESpec key of the file
    BYTE ESpecIndexBE[4];                           // Index of ESPEC entry, assuming zero-terminated strings (big endian)
    BYTE FileSizeBE[5];                             // Size of the encoded version of the file (big endian)

} FILE_ESPEC_ENTRY, *PFILE_ESPEC_ENTRY;

//-----------------------------------------------------------------------------
// The DOWNLOAD manifest structures
//
// See https://wowdev.wiki/TACT#Download_manifest
//

#define FILE_MAGIC_DOWNLOAD 0x4C44                  // 'DL'

// File header of the DOWNLOAD manifest
typedef struct _FILE_DOWNLOAD_HEADER
{
    USHORT Magic;                                   // FILE_MAGIC_DOWNLOAD ('DL')
    BYTE Version;                                   // Expected to be 1 by CascLib
    BYTE EKeyLength;                                // The content key length in DOWNLOAD file. Expected to be 0x10
    BYTE EntryHasChecksum;                          // If nonzero, then the entry has checksum.
    BYTE EntryCount[4];                             // Number of entries (big-endian)
    BYTE TagCount[2];                               // Number of tag entries (big endian)

    // Version 2 or newer
    BYTE FlagByteSize;                              // Number of flag bytes

    // Verion 3 or newer
    BYTE BasePriority;
    BYTE Unknown2[3];

} FILE_DOWNLOAD_HEADER, *PFILE_DOWNLOAD_HEADER;

typedef struct _FILE_DOWNLOAD_ENTRY
{
    BYTE EKey[MD5_HASH_SIZE];                       // Encoding key (variable length)
    BYTE FileSize[5];                               // File size
    BYTE Priority;

    // DWORD Checksum [optional]
    // BYTE Flags;

} FILE_DOWNLOAD_ENTRY, *PFILE_DOWNLOAD_ENTRY;

//-----------------------------------------------------------------------------
// The INSTALL manifest structures
//
// See https://wowdev.wiki/TACT#Install_manifest
//

#define FILE_MAGIC_INSTALL 0x4E49                   // 'IN'

// File header of the INSTALL manifest
typedef struct _FILE_INSTALL_HEADER
{
    USHORT Magic;                                   // FILE_MAGIC_INSTALL ('DL')
    BYTE Version;                                   // Expected to be 1 by CascLib
    BYTE EKeyLength;                                // The content key length in INSTALL file. Expected to be 0x10
    BYTE TagCount[2];                               // Number of tag entries (big endian)
    BYTE EntryCount[4];                             // Number of entries (big-endian)

} FILE_INSTALL_HEADER, *PFILE_INSTALL_HEADER;

//-----------------------------------------------------------------------------
// Data file structures

#define BLTE_HEADER_SIGNATURE   0x45544C42          // 'BLTE' header in the data files
#define BLTE_HEADER_DELTA       0x1E                // Distance of BLTE header from begin of the header area
#define MAX_ENCODED_HEADER      0x1000              // Starting size for the frame headers

typedef struct _BLTE_HEADER
{
    BYTE  Signature[4];                             // Must be "BLTE"
    BYTE  HeaderSize[4];                            // Header size in bytes (big endian)
    BYTE  MustBe0F;                                 // Must be 0x0F. Optional, only if HeaderSize != 0
    BYTE  FrameCount[3];                            // Frame count (big endian). Optional, only if HeaderSize != 0
} BLTE_HEADER, *PBLTE_HEADER;

typedef struct _BLTE_ENCODED_HEADER
{
    // Header span.
    ENCODED_KEY EKey;                               // Encoded key of the data beginning with "BLTE" (byte-reversed)
    DWORD EncodedSize;                              // Encoded size of the data data beginning with "BLTE" (little endian)
    BYTE  field_14;                                 // Seems to be 1 if the header span has no data
    BYTE  field_15;                                 // Hardcoded to zero (Agent.exe 2.15.0.6296: 01370000->0148E2AA)
    BYTE  JenkinsHash[4];                           // Jenkins hash (hashlittle2) of the preceding fields (EKey + EncodedSize + field_14 + field_15) (little endian)
    BYTE  Checksum[4];                              // Checksum of the previous part. See "VerifyHeaderSpan()" for more information.

    // BLTE header. Always present.
    BYTE  Signature[4];                             // Must be "BLTE"
    BYTE  HeaderSize[4];                            // Header size in bytes (big endian)
    BYTE  MustBe0F;                                 // Must be 0x0F. Optional, only if HeaderSize != 0
    BYTE  FrameCount[3];                            // Frame count (big endian). Optional, only if HeaderSize != 0
} BLTE_ENCODED_HEADER, *PBLTE_ENCODED_HEADER;

typedef struct _BLTE_FRAME
{
    BYTE EncodedSize[4];                            // Encoded frame size (big endian)
    BYTE ContentSize[4];                            // Content frame size (big endian)
    CONTENT_KEY FrameHash;                          // Hash of the encoded frame

} BLTE_FRAME, *PBLTE_FRAME;

#endif  // __CASC_STRUCTS_H__