/
jar.go
149 lines (123 loc) · 3.02 KB
/
jar.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
package jar
import (
"archive/zip"
"bytes"
"context"
"fmt"
coremdl2 "github.com/Senth/mcman-lib/coremdl"
"io"
)
type Jar interface {
GetMod(ctx context.Context, data []byte) (*coremdl2.JarInfo, error)
}
type parser interface {
Parse(data []byte) (*coremdl2.JarInfo, error)
}
type jarImpl struct {
forge parser
fabric parser
manifest manifestReader
}
func NewJar() Jar {
return &jarImpl{
forge: newForgeParser(),
fabric: newFabricParser(),
manifest: manifestReader{},
}
}
func (j jarImpl) GetMod(ctx context.Context, data []byte) (*coremdl2.JarInfo, error) {
// Read zip/jar
zipReader, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
if err != nil {
return nil, err
}
// Get zip mod file information
file, modLoader, err := j.getZippedFile(zipReader)
if err != nil {
return nil, err
}
// Open zip file
fileReader, err := file.Open()
if err != nil {
return nil, err
}
defer func(fileReader io.ReadCloser) {
_ = fileReader.Close()
}(fileReader)
buf := new(bytes.Buffer)
_, err = buf.ReadFrom(fileReader)
if err != nil {
return nil, err
}
// Parse json with mod information
info, err := j.parseJar(ctx, modLoader, buf.Bytes())
if err != nil {
return nil, err
}
// Fix version number if ${file.jarVersion} is used
if info.VersionNumber == "${file.jarVersion}" {
info.VersionNumber, err = j.getJARVersion(zipReader)
if err != nil {
return nil, err
}
}
return info, err
}
func (j jarImpl) parseJar(ctx context.Context, modLoader coremdl2.ModLoader, data []byte) (*coremdl2.JarInfo, error) {
// Parse json with mod information
switch modLoader {
case coremdl2.ModLoaderForge:
return j.forge.Parse(data)
case coremdl2.ModLoaderFabric:
return j.fabric.Parse(data)
default:
return nil, fmt.Errorf("unknown mod loader %s", modLoader)
}
}
func (j jarImpl) getZippedFile(r *zip.Reader) (*zip.File, coremdl2.ModLoader, error) {
for _, f := range r.File {
switch f.Name {
case "fabric.mod.json":
return f, coremdl2.ModLoaderFabric, nil
case "META-INF/mods.toml":
return f, coremdl2.ModLoaderForge, nil
}
}
return nil, coremdl2.ModLoaderNone, fmt.Errorf("could not find mod file in jar")
}
func (j jarImpl) getJARVersion(r *zip.Reader) (string, error) {
var file *zip.File
for _, f := range r.File {
if f.Name == "META-INF/MANIFEST.MF" {
file = f
break
}
}
if file == nil {
return "", fmt.Errorf("could not find manifest file in jar")
}
// Open zip file
fileReader, err := file.Open()
if err != nil {
return "", err
}
defer func(fileReader io.ReadCloser) {
err = fileReader.Close()
}(fileReader)
// Parse manifest file
manifest, err := j.manifest.Parse(fileReader)
if err != nil {
return "", err
}
// Use implementation version if available
version := manifest["Implementation-Version"]
if version != "" {
return version, err
}
// Use specification version if available
version = manifest["Specification-Version"]
if version == "" {
return "", fmt.Errorf("could not find version in manifest")
}
return version, err
}