-
Notifications
You must be signed in to change notification settings - Fork 0
/
highscore.go
187 lines (155 loc) · 4.68 KB
/
highscore.go
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
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/gob"
"errors"
"io"
"io/ioutil"
"log"
"os"
"strconv"
t "time"
)
const key = "cegthctrm.hysqrk.xrjnjhsqytdjpvj"
const highScoreFilename = "score.hsc"
const highscoreWindowTitle = "High scores"
const highScoreWindowWidth = 70
const highScoreWindowHeight = 14
const maxAmountOfTopHighScores = 10
// HighScore represents all of the single high-score entry components
type HighScore struct {
Timestamp t.Time
Score int
PlayerName string
}
// HighScores represents a slice of HighScore entries
type HighScores []HighScore
func init() {
gob.Register(HighScore{})
}
func (score *HighScore) String() string {
return score.Timestamp.Format(t.RFC1123) + "\t" +
score.PlayerName + "\t" +
strconv.Itoa(score.Score)
}
func (scores *HighScores) String() string {
content := ""
for _, score := range *scores {
content += score.String() + "\n"
}
return content
}
func serialize(scores *HighScores) ([]byte, error) {
buffer := bytes.Buffer{}
encoder := gob.NewEncoder(&buffer)
err := encoder.Encode(*scores)
if err != nil {
log.Panic("Error serialization high score:", err)
return nil, err
}
return buffer.Bytes(), nil
}
func deSerialize(file *os.File) (*HighScores, error) {
content, readingError := ioutil.ReadAll(file)
if readingError != nil {
log.Panic("Error reading contents from file:", readingError)
return nil, readingError
}
decrypted, decryptionError := decrypt(content)
if decryptionError != nil {
log.Panic("Error decrypting high score contents:", decryptionError)
return nil, decryptionError
}
score := new(HighScores)
decoder := gob.NewDecoder(bytes.NewReader(decrypted))
err := decoder.Decode(score)
if err != nil {
log.Panic("Error de-serializing high score:", err)
return nil, err
}
return score, nil
}
// SaveHighScore writes high score structure to file
func SaveHighScore(score *HighScore) {
currentScores, _ := LoadHighScore()
currentScores = append(currentScores, *score)
payload, serializeError := serialize(¤tScores)
if serializeError != nil {
log.Panic("Error saving high score to file:", serializeError)
return
}
encryptedPayload, encryptionError := encrypt(payload)
if encryptionError != nil {
log.Panic("Error encryption of the high score payload:", encryptionError)
}
saveError := ioutil.WriteFile(highScoreFilename, encryptedPayload, 0666)
if saveError != nil {
log.Panic("Error saving high score to file:", saveError)
return
}
log.Printf("High score successfully saved to file: %s", highScoreFilename)
}
// LoadHighScore reads high score structure from file
func LoadHighScore() (HighScores, error) {
log.Printf("Loading high score from file: %s", highScoreFilename)
file, readError := os.Open(highScoreFilename)
if readError != nil {
return nil, readError
}
scores, deSerializeError := deSerialize(file)
if deSerializeError != nil {
return nil, readError
}
return *scores, nil
}
func encrypt(payload []byte) ([]byte, error) {
keyBytes := []byte(key)
block, chipherCreationError := aes.NewCipher(keyBytes)
if chipherCreationError != nil {
log.Panic("Error creating cipher:", chipherCreationError)
return nil, chipherCreationError
}
encryptedPayload := make([]byte, aes.BlockSize+len(payload))
iv := encryptedPayload[:aes.BlockSize]
_, ivGenerationError := io.ReadFull(rand.Reader, iv)
if ivGenerationError != nil {
log.Panic("Error generating IV:", ivGenerationError)
return nil, ivGenerationError
}
encryptionStream := cipher.NewCFBEncrypter(block, iv)
encryptionStream.XORKeyStream(encryptedPayload[aes.BlockSize:], payload)
return encryptedPayload, nil
}
func decrypt(payload []byte) ([]byte, error) {
keyBytes := []byte(key)
block, chipherCreationError := aes.NewCipher(keyBytes)
if chipherCreationError != nil {
log.Panic("Error creating cipher:", chipherCreationError)
return nil, chipherCreationError
}
if len(payload) < aes.BlockSize {
errorMessage := "High score file is too short"
log.Panic("Decryption error:", errorMessage)
return nil, errors.New(errorMessage)
}
iv := payload[:aes.BlockSize]
encryptedPayload := payload[aes.BlockSize:]
decryptionStream := cipher.NewCFBDecrypter(block, iv)
decryptionStream.XORKeyStream(encryptedPayload, encryptedPayload)
return encryptedPayload, nil
}
// Len gets the amount of elements in HighScores type
func (scores HighScores) Len() int {
return len(scores)
}
// Swap swaps i-th and j-th elements in the HighScores type
func (scores HighScores) Swap(i, j int) {
scores[i], scores[j] = scores[j], scores[i]
}
// Less allows to sort high scores in descending score order
func (scores HighScores) Less(i, j int) bool {
return scores[i].Score > scores[j].Score
}