casc-cdn 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +220 -0
- package/dist/LICENSE +21 -0
- package/dist/README.md +220 -0
- package/dist/index.d.ts +462 -0
- package/dist/index.js +1087 -0
- package/dist/package.json +45 -0
- package/package.json +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Bogdan Tretyakov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# casc-cdn
|
|
2
|
+
|
|
3
|
+
A modern TypeScript library for downloading and extracting files from Blizzard's CASC (Content Addressable Storage Container) archives via CDN.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **High-level API** - Simple, intuitive interface for downloading game files
|
|
8
|
+
- 📦 **CDN-based** - Downloads directly from Blizzard's CDN servers (no game installation required)
|
|
9
|
+
- 💾 **Built-in caching** - Automatic file caching to minimize network requests
|
|
10
|
+
- 🔍 **File search** - Search files by path with case-insensitive matching
|
|
11
|
+
- ⚡ **Batch downloads** - Optimized multi-file downloads grouped by archive
|
|
12
|
+
- 📝 **Full TypeScript** - Complete type definitions included
|
|
13
|
+
|
|
14
|
+
## Compatibility
|
|
15
|
+
|
|
16
|
+
✅ **Tested with Warcraft III: Reforged**
|
|
17
|
+
⚠️ **May work with other Blizzard games** (World of Warcraft, Diablo, etc.) but untested
|
|
18
|
+
|
|
19
|
+
## Limitations
|
|
20
|
+
|
|
21
|
+
- ❌ No support for patch files
|
|
22
|
+
- ❌ No support for encrypted files
|
|
23
|
+
- ❌ Archive-only (requires files to be in archives, not loose CDN files)
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install casc-cdn
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { CascClient, TactProduct } from 'casc-cdn';
|
|
35
|
+
|
|
36
|
+
// Initialize client for Warcraft III (EU region)
|
|
37
|
+
const client = await CascClient.init(TactProduct.WarcraftIII, 'eu');
|
|
38
|
+
|
|
39
|
+
// Search for a file by path
|
|
40
|
+
const entries = client.getEntryByPath('itemfunc.txt');
|
|
41
|
+
|
|
42
|
+
if (entries.length > 0) {
|
|
43
|
+
// Download the file
|
|
44
|
+
const fileBuffer = await client.getFile(entries[0].contentKey);
|
|
45
|
+
console.log(fileBuffer.toString('utf8'));
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## API Reference
|
|
50
|
+
|
|
51
|
+
### Initialization
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { CascClient, TactProduct, CascCache } from 'casc-cdn';
|
|
55
|
+
|
|
56
|
+
// Use default cache (node_modules/.cache/casc-cdn)
|
|
57
|
+
const client = await CascClient.init(TactProduct.WarcraftIII, 'eu');
|
|
58
|
+
|
|
59
|
+
// Use custom cache directory
|
|
60
|
+
const customCache = new CascCache('./my-cache');
|
|
61
|
+
const client = await CascClient.init(
|
|
62
|
+
TactProduct.WarcraftIII,
|
|
63
|
+
'us',
|
|
64
|
+
customCache,
|
|
65
|
+
);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Available Regions:** `'us'` | `'eu'` | `'kr'` | `'cn'`
|
|
69
|
+
|
|
70
|
+
**Available Products:**
|
|
71
|
+
|
|
72
|
+
- `TactProduct.WarcraftIII` - Warcraft III: Reforged
|
|
73
|
+
- Other products may work but are untested
|
|
74
|
+
|
|
75
|
+
### Searching Files
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
// Case-insensitive partial match
|
|
79
|
+
const entries = client.getEntryByPath('units.slk');
|
|
80
|
+
|
|
81
|
+
// Search returns an array of matching entries
|
|
82
|
+
entries.forEach((entry) => {
|
|
83
|
+
console.log(entry.contentKey); // File's content key (hash)
|
|
84
|
+
console.log(entry.localeFlags); // Locale information
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Downloading Files
|
|
89
|
+
|
|
90
|
+
#### Single File
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// Download by content key
|
|
94
|
+
const fileBuffer = await client.getFile(contentKey);
|
|
95
|
+
|
|
96
|
+
if (fileBuffer) {
|
|
97
|
+
// File successfully downloaded
|
|
98
|
+
console.log(fileBuffer.toString('utf8'));
|
|
99
|
+
} else {
|
|
100
|
+
// File not found
|
|
101
|
+
console.log('File not available');
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
#### Multiple Files (Batch)
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// Promise-based: Returns all files at once
|
|
109
|
+
const files = await client.getFiles([cKey1, cKey2, cKey3]);
|
|
110
|
+
console.log(files[cKey1].toString('utf8'));
|
|
111
|
+
|
|
112
|
+
// Callback-based: Process files as they download
|
|
113
|
+
client.getFiles([cKey1, cKey2, cKey3], (cKey, data) => {
|
|
114
|
+
console.log(`Downloaded ${cKey}: ${data.length} bytes`);
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Examples
|
|
119
|
+
|
|
120
|
+
### Example 1: Extract All Unit Data Files
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import { CascClient, TactProduct } from 'casc-cdn';
|
|
124
|
+
import fs from 'fs/promises';
|
|
125
|
+
|
|
126
|
+
const client = await CascClient.init(TactProduct.WarcraftIII, 'eu');
|
|
127
|
+
|
|
128
|
+
// Find all .slk files (unit data)
|
|
129
|
+
const slkFiles = client.getEntryByPath('.slk');
|
|
130
|
+
|
|
131
|
+
console.log(`Found ${slkFiles.length} SLK files`);
|
|
132
|
+
|
|
133
|
+
// Download all SLK files
|
|
134
|
+
const cKeys = slkFiles.map((entry) => entry.contentKey);
|
|
135
|
+
const files = await client.getFiles(cKeys);
|
|
136
|
+
|
|
137
|
+
// Save to disk
|
|
138
|
+
for (const [cKey, data] of Object.entries(files)) {
|
|
139
|
+
const entry = slkFiles.find((e) => e.contentKey === cKey);
|
|
140
|
+
const filename = entry?._normalizedPath?.split('/').pop() || cKey;
|
|
141
|
+
await fs.writeFile(`./output/${filename}`, data);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
console.log('All SLK files extracted!');
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Example 2: Search and Download Locale File
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { CascClient, TactProduct } from 'casc-cdn';
|
|
151
|
+
|
|
152
|
+
const client = await CascClient.init(TactProduct.WarcraftIII, 'eu');
|
|
153
|
+
|
|
154
|
+
// Search for item functions file
|
|
155
|
+
const entries = client.getEntryByPath('a01garithos01.flac');
|
|
156
|
+
|
|
157
|
+
const soundEntry = entries.find((e) => e.localeFlags.ruRU);
|
|
158
|
+
|
|
159
|
+
if (soundEntry) {
|
|
160
|
+
const fileData = await client.getFile(soundEntry.contentKey);
|
|
161
|
+
|
|
162
|
+
if (fileData) {
|
|
163
|
+
// Parse the file content
|
|
164
|
+
const content = fileData.toString('utf8');
|
|
165
|
+
const lines = content.split('\n');
|
|
166
|
+
|
|
167
|
+
console.log(`File has ${lines.length} lines`);
|
|
168
|
+
console.log('First 10 lines:');
|
|
169
|
+
console.log(lines.slice(0, 10).join('\n'));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Example 3: Custom Cache Location
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { CascClient, TactProduct, CascCache } from 'casc-cdn';
|
|
178
|
+
|
|
179
|
+
// Store cache in project directory
|
|
180
|
+
const cache = new CascCache('./casc-cache');
|
|
181
|
+
const client = await CascClient.init(TactProduct.WarcraftIII, 'eu', cache);
|
|
182
|
+
|
|
183
|
+
// Subsequent runs will use cached data
|
|
184
|
+
const entries = client.getEntryByPath('units.slk');
|
|
185
|
+
const fileData = await client.getFile(entries[0].contentKey);
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## How It Works
|
|
189
|
+
|
|
190
|
+
1. **Initialization**: Fetches CDN configuration, version info, and builds internal indices
|
|
191
|
+
2. **File Search**: Searches the root file for path-to-content-key mappings
|
|
192
|
+
3. **File Lookup**: Resolves content keys to encoded keys via encoding table
|
|
193
|
+
4. **Location Finding**: Locates files in archive indices
|
|
194
|
+
5. **Download**: Downloads archive chunks from CDN
|
|
195
|
+
6. **Decompression**: Decodes BLTE-compressed data
|
|
196
|
+
7. **Caching**: Stores downloaded data for future use
|
|
197
|
+
|
|
198
|
+
## Architecture
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
CascClient
|
|
202
|
+
├── BlizzardAPI - HTTP client for CDN requests
|
|
203
|
+
├── CascCache - File-based caching layer
|
|
204
|
+
├── EncodingTable - Maps content keys to encoded keys
|
|
205
|
+
├── IndexTable - Maps encoded keys to archive locations
|
|
206
|
+
└── RootFile - Maps file paths to content keys
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
MIT
|
|
212
|
+
|
|
213
|
+
## Contributing
|
|
214
|
+
|
|
215
|
+
Contributions are welcome! Please feel free to submit issues or pull requests.
|
|
216
|
+
|
|
217
|
+
## Acknowledgments
|
|
218
|
+
|
|
219
|
+
- Based on the [TACT protocol](https://wowdev.wiki/TACT) used by Blizzard Entertainment
|
|
220
|
+
- Inspired by various CASC implementations in the community
|
package/dist/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Bogdan Tretyakov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/README.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# casc-cdn
|
|
2
|
+
|
|
3
|
+
A modern TypeScript library for downloading and extracting files from Blizzard's CASC (Content Addressable Storage Container) archives via CDN.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **High-level API** - Simple, intuitive interface for downloading game files
|
|
8
|
+
- 📦 **CDN-based** - Downloads directly from Blizzard's CDN servers (no game installation required)
|
|
9
|
+
- 💾 **Built-in caching** - Automatic file caching to minimize network requests
|
|
10
|
+
- 🔍 **File search** - Search files by path with case-insensitive matching
|
|
11
|
+
- ⚡ **Batch downloads** - Optimized multi-file downloads grouped by archive
|
|
12
|
+
- 📝 **Full TypeScript** - Complete type definitions included
|
|
13
|
+
|
|
14
|
+
## Compatibility
|
|
15
|
+
|
|
16
|
+
✅ **Tested with Warcraft III: Reforged**
|
|
17
|
+
⚠️ **May work with other Blizzard games** (World of Warcraft, Diablo, etc.) but untested
|
|
18
|
+
|
|
19
|
+
## Limitations
|
|
20
|
+
|
|
21
|
+
- ❌ No support for patch files
|
|
22
|
+
- ❌ No support for encrypted files
|
|
23
|
+
- ❌ Archive-only (requires files to be in archives, not loose CDN files)
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install casc-cdn
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { CascClient, TactProduct } from 'casc-cdn';
|
|
35
|
+
|
|
36
|
+
// Initialize client for Warcraft III (EU region)
|
|
37
|
+
const client = await CascClient.init(TactProduct.WarcraftIII, 'eu');
|
|
38
|
+
|
|
39
|
+
// Search for a file by path
|
|
40
|
+
const entries = client.getEntryByPath('itemfunc.txt');
|
|
41
|
+
|
|
42
|
+
if (entries.length > 0) {
|
|
43
|
+
// Download the file
|
|
44
|
+
const fileBuffer = await client.getFile(entries[0].contentKey);
|
|
45
|
+
console.log(fileBuffer.toString('utf8'));
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## API Reference
|
|
50
|
+
|
|
51
|
+
### Initialization
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { CascClient, TactProduct, CascCache } from 'casc-cdn';
|
|
55
|
+
|
|
56
|
+
// Use default cache (node_modules/.cache/casc-cdn)
|
|
57
|
+
const client = await CascClient.init(TactProduct.WarcraftIII, 'eu');
|
|
58
|
+
|
|
59
|
+
// Use custom cache directory
|
|
60
|
+
const customCache = new CascCache('./my-cache');
|
|
61
|
+
const client = await CascClient.init(
|
|
62
|
+
TactProduct.WarcraftIII,
|
|
63
|
+
'us',
|
|
64
|
+
customCache,
|
|
65
|
+
);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Available Regions:** `'us'` | `'eu'` | `'kr'` | `'cn'`
|
|
69
|
+
|
|
70
|
+
**Available Products:**
|
|
71
|
+
|
|
72
|
+
- `TactProduct.WarcraftIII` - Warcraft III: Reforged
|
|
73
|
+
- Other products may work but are untested
|
|
74
|
+
|
|
75
|
+
### Searching Files
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
// Case-insensitive partial match
|
|
79
|
+
const entries = client.getEntryByPath('units.slk');
|
|
80
|
+
|
|
81
|
+
// Search returns an array of matching entries
|
|
82
|
+
entries.forEach((entry) => {
|
|
83
|
+
console.log(entry.contentKey); // File's content key (hash)
|
|
84
|
+
console.log(entry.localeFlags); // Locale information
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Downloading Files
|
|
89
|
+
|
|
90
|
+
#### Single File
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// Download by content key
|
|
94
|
+
const fileBuffer = await client.getFile(contentKey);
|
|
95
|
+
|
|
96
|
+
if (fileBuffer) {
|
|
97
|
+
// File successfully downloaded
|
|
98
|
+
console.log(fileBuffer.toString('utf8'));
|
|
99
|
+
} else {
|
|
100
|
+
// File not found
|
|
101
|
+
console.log('File not available');
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
#### Multiple Files (Batch)
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// Promise-based: Returns all files at once
|
|
109
|
+
const files = await client.getFiles([cKey1, cKey2, cKey3]);
|
|
110
|
+
console.log(files[cKey1].toString('utf8'));
|
|
111
|
+
|
|
112
|
+
// Callback-based: Process files as they download
|
|
113
|
+
client.getFiles([cKey1, cKey2, cKey3], (cKey, data) => {
|
|
114
|
+
console.log(`Downloaded ${cKey}: ${data.length} bytes`);
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Examples
|
|
119
|
+
|
|
120
|
+
### Example 1: Extract All Unit Data Files
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import { CascClient, TactProduct } from 'casc-cdn';
|
|
124
|
+
import fs from 'fs/promises';
|
|
125
|
+
|
|
126
|
+
const client = await CascClient.init(TactProduct.WarcraftIII, 'eu');
|
|
127
|
+
|
|
128
|
+
// Find all .slk files (unit data)
|
|
129
|
+
const slkFiles = client.getEntryByPath('.slk');
|
|
130
|
+
|
|
131
|
+
console.log(`Found ${slkFiles.length} SLK files`);
|
|
132
|
+
|
|
133
|
+
// Download all SLK files
|
|
134
|
+
const cKeys = slkFiles.map((entry) => entry.contentKey);
|
|
135
|
+
const files = await client.getFiles(cKeys);
|
|
136
|
+
|
|
137
|
+
// Save to disk
|
|
138
|
+
for (const [cKey, data] of Object.entries(files)) {
|
|
139
|
+
const entry = slkFiles.find((e) => e.contentKey === cKey);
|
|
140
|
+
const filename = entry?._normalizedPath?.split('/').pop() || cKey;
|
|
141
|
+
await fs.writeFile(`./output/${filename}`, data);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
console.log('All SLK files extracted!');
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Example 2: Search and Download Locale File
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { CascClient, TactProduct } from 'casc-cdn';
|
|
151
|
+
|
|
152
|
+
const client = await CascClient.init(TactProduct.WarcraftIII, 'eu');
|
|
153
|
+
|
|
154
|
+
// Search for item functions file
|
|
155
|
+
const entries = client.getEntryByPath('a01garithos01.flac');
|
|
156
|
+
|
|
157
|
+
const soundEntry = entries.find((e) => e.localeFlags.ruRU);
|
|
158
|
+
|
|
159
|
+
if (soundEntry) {
|
|
160
|
+
const fileData = await client.getFile(soundEntry.contentKey);
|
|
161
|
+
|
|
162
|
+
if (fileData) {
|
|
163
|
+
// Parse the file content
|
|
164
|
+
const content = fileData.toString('utf8');
|
|
165
|
+
const lines = content.split('\n');
|
|
166
|
+
|
|
167
|
+
console.log(`File has ${lines.length} lines`);
|
|
168
|
+
console.log('First 10 lines:');
|
|
169
|
+
console.log(lines.slice(0, 10).join('\n'));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Example 3: Custom Cache Location
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { CascClient, TactProduct, CascCache } from 'casc-cdn';
|
|
178
|
+
|
|
179
|
+
// Store cache in project directory
|
|
180
|
+
const cache = new CascCache('./casc-cache');
|
|
181
|
+
const client = await CascClient.init(TactProduct.WarcraftIII, 'eu', cache);
|
|
182
|
+
|
|
183
|
+
// Subsequent runs will use cached data
|
|
184
|
+
const entries = client.getEntryByPath('units.slk');
|
|
185
|
+
const fileData = await client.getFile(entries[0].contentKey);
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## How It Works
|
|
189
|
+
|
|
190
|
+
1. **Initialization**: Fetches CDN configuration, version info, and builds internal indices
|
|
191
|
+
2. **File Search**: Searches the root file for path-to-content-key mappings
|
|
192
|
+
3. **File Lookup**: Resolves content keys to encoded keys via encoding table
|
|
193
|
+
4. **Location Finding**: Locates files in archive indices
|
|
194
|
+
5. **Download**: Downloads archive chunks from CDN
|
|
195
|
+
6. **Decompression**: Decodes BLTE-compressed data
|
|
196
|
+
7. **Caching**: Stores downloaded data for future use
|
|
197
|
+
|
|
198
|
+
## Architecture
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
CascClient
|
|
202
|
+
├── BlizzardAPI - HTTP client for CDN requests
|
|
203
|
+
├── CascCache - File-based caching layer
|
|
204
|
+
├── EncodingTable - Maps content keys to encoded keys
|
|
205
|
+
├── IndexTable - Maps encoded keys to archive locations
|
|
206
|
+
└── RootFile - Maps file paths to content keys
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
MIT
|
|
212
|
+
|
|
213
|
+
## Contributing
|
|
214
|
+
|
|
215
|
+
Contributions are welcome! Please feel free to submit issues or pull requests.
|
|
216
|
+
|
|
217
|
+
## Acknowledgments
|
|
218
|
+
|
|
219
|
+
- Based on the [TACT protocol](https://wowdev.wiki/TACT) used by Blizzard Entertainment
|
|
220
|
+
- Inspired by various CASC implementations in the community
|