forked from polynite/splash
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathchunk.go
More file actions
140 lines (116 loc) Β· 2.93 KB
/
Copy pathchunk.go
File metadata and controls
140 lines (116 loc) Β· 2.93 KB
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
package main
import (
"encoding/binary"
"encoding/hex"
"fmt"
"io/ioutil"
"log"
"strconv"
"strings"
)
// Chunk defines a downloadable chunk
type Chunk struct {
GUID string
Hash string
Sha string
DataGroup int
FileSize int64
}
// ChunkPart defines a part of a specific chunk
type ChunkPart struct {
Offset uint32
Size uint32
}
// ChunkJob defines a job
type ChunkJob struct {
ID int
Chunk Chunk
Part ChunkPart
}
// ChunkJobResult defines a result
type ChunkJobResult struct {
Job ChunkJob
Reader ReadSeekCloser
}
// ChunkHeader defines the binary chunk header
type ChunkHeader struct {
Magic uint32 // 0xB1FE3AA2
Version uint32 // 2
HeaderSize uint32 // 3E
DataSizeCompressed uint32
GUID [16]byte
RollingHash uint64
StoredAs uint8 // 00 = plaintext, 01 = compressed, 02 = encrypted
SHAHash [20]byte
HashType uint8 // strangely 03
}
// GetURL builds a url
func (c *Chunk) GetURL(cloudURL string) string {
return fmt.Sprintf("%s/Builds/Fortnite/CloudDir/ChunksV3/%02d/%s_%s.chunk", cloudURL, c.DataGroup, c.Hash, c.GUID)
}
// Download fetches the chunk from the internet
func (c *Chunk) Download(cloudURL string) (data []byte, err error) {
// Make GET request
resp, err := httpClient.Get(c.GetURL(cloudURL))
if err != nil {
return
}
defer resp.Body.Close()
// Check response code
if resp.StatusCode/100 != 2 {
err = fmt.Errorf("invalid status code %d", resp.StatusCode)
return
}
// Read data
data, err = ioutil.ReadAll(resp.Body)
return
}
// NewChunk create a chunk object
func NewChunk(guid string, hash string, sha string, dataGroup string, fileSize string) Chunk {
dg, err := strconv.Atoi(dataGroup)
if err != nil {
log.Fatalf("Failed to convert datagroup %s: %v", dataGroup, err)
}
parsedHash := readPackedData(hash)
reverse(parsedHash)
return Chunk{
GUID: guid,
Hash: strings.ToUpper(hex.EncodeToString(parsedHash)),
Sha: sha,
DataGroup: dg,
FileSize: int64(readPackedUint32(fileSize)),
}
}
func NewChunkInt(guid string, hash string, sha string, dataGroup string, fileSize uint64) Chunk {
dg, err := strconv.Atoi(dataGroup)
if err != nil {
log.Fatalf("Failed to convert datagroup %s: %v", dataGroup, err)
}
return Chunk{
GUID: guid,
Hash: hash,
DataGroup: dg,
FileSize: int64(fileSize),
}
}
func readChunkHeader(r ReadSeekCloser) (ChunkHeader, error) {
// Initialize empty header
header := ChunkHeader{}
// Read header
err := binary.Read(r, binary.LittleEndian, &header)
return header, err
}
func readPackedData(packed string) []byte {
output := make([]byte, 0)
for i := 0; i < len(packed); i += 3 {
num, err := strconv.ParseUint(packed[i:i+3], 10, 16)
if err != nil {
return nil
}
output = append(output, byte(num))
}
return output
}
func readPackedUint32(packed string) uint32 {
return binary.LittleEndian.Uint32(readPackedData(packed))
}