-
Notifications
You must be signed in to change notification settings - Fork 7
/
textures_struct.go
145 lines (126 loc) · 3.98 KB
/
textures_struct.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
package minecraft
import (
"crypto/md5"
"fmt"
"image"
"image/draw"
"io"
// If we work with PNGs we need this
_ "image/png"
"github.com/pkg/errors"
)
// Texture is our structure for the Cape/Skin structs and the functions for dealing with it
type Texture struct {
// texture image...
Image image.Image
// md5 hash of the texture image
Hash string
// Location we grabbed the texture from. Mojang/S3/Char
Source string
// 4-byte signature of the background matte for the texture
AlphaSig [4]uint8
// URL of the texture
URL string
// M is a pointer to the Minecraft struct that is then used for requests against the API
Mc *Minecraft
}
// CastToNRGBA takes image bytes and converts to NRGBA format if needed
func (t *Texture) CastToNRGBA(r io.Reader) error {
// Decode the skin
textureImg, format, err := image.Decode(r)
if err != nil {
return errors.Wrap(err, "unable to CastToNRGBA")
}
// Convert it to NRGBA if necessary
textureFinal := textureImg
if format != "NRGBA" {
bounds := textureImg.Bounds()
textureFinal = image.NewNRGBA(bounds)
draw.Draw(textureFinal.(draw.Image), bounds, textureImg, image.Pt(0, 0), draw.Src)
}
t.Image = textureFinal
return nil
}
// Decode takes the image bytes and turns it into our Texture struct
func (t *Texture) Decode(r io.Reader) error {
err := t.CastToNRGBA(r)
if err != nil {
return errors.WithStack(err)
}
// And md5 hash its pixels
hasher := md5.New()
hasher.Write(t.Image.(*image.NRGBA).Pix)
t.Hash = fmt.Sprintf("%x", hasher.Sum(nil))
// Create the alpha signature
img := t.Image.(*image.NRGBA)
t.AlphaSig = [...]uint8{
img.Pix[0],
img.Pix[1],
img.Pix[2],
img.Pix[3],
}
return nil
}
// Fetch performs the GET for the texture, doing any required conversion and saving our Image property
func (t *Texture) Fetch() error {
apiBody, err := t.Mc.apiRequest(t.URL)
if apiBody != nil {
defer apiBody.Close()
}
if err != nil {
return errors.Wrap(err, "unable to Fetch Texture")
}
err = t.Decode(apiBody)
if err != nil {
return errors.Wrap(err, "unable to Decode Texture")
}
return nil
}
// FetchWithTextureProperty takes a already decoded Texture Property and will request either Skin or Cape as instructed
func (t *Texture) FetchWithTextureProperty(profileTextureProperty SessionProfileTextureProperty, textureType string) error {
if textureType == "Skin" {
t.URL = profileTextureProperty.Textures.Skin.URL
} else if textureType == "Cape" {
t.URL = profileTextureProperty.Textures.Cape.URL
} else {
return errors.New("Unknown textureType")
}
if t.URL == "" {
return errors.Errorf("%s URL not present", textureType)
}
t.Source = "SessionProfile"
err := t.Fetch()
if err != nil {
return errors.Wrap(err, "FetchWithTextureProperty failed")
}
return nil
}
// FetchWithSessionProfile will decode the Texture Property for you and request the Skin or Cape as instructed
// If requesting both Skin and Cape, this would result in 2 x decoding - use FetchWithTextureProperty instead
func (t *Texture) FetchWithSessionProfile(sessionProfile SessionProfileResponse, textureType string) error {
profileTextureProperty, err := DecodeTextureProperty(sessionProfile)
if err != nil {
return errors.WithStack(err)
}
err = t.FetchWithTextureProperty(profileTextureProperty, textureType)
if err != nil {
return errors.Wrap(err, "FetchWithSessionProfile failed")
}
return nil
}
// FetchWithUsername takes a username and will then request from UsernameAPI as specified in the Minecraft struct
func (t *Texture) FetchWithUsername(username string, textureType string) error {
if textureType == "Skin" && t.Mc.UsernameAPI.SkinURL != "" {
t.URL = t.Mc.UsernameAPI.SkinURL + username + ".png"
} else if textureType == "Cape" && t.Mc.UsernameAPI.CapeURL != "" {
t.URL = t.Mc.UsernameAPI.CapeURL + username + ".png"
} else {
return errors.New("Unkown textureType or missing UsernameAPI lookup URL")
}
t.Source = "UsernameAPI"
err := t.Fetch()
if err != nil {
return errors.Wrap(err, "FetchWithUsername failed")
}
return nil
}