z_mirror/fmm

git clone https://git.raiguard.me/z_mirror/fmm

Caleb HeuerMicro3c2b7f3

predep-parsing
3.7 KiB195 linesraw
1package main
2
3import (
4	"archive/zip"
5	"encoding/json"
6	"errors"
7	"fmt"
8	"io/fs"
9	"io/ioutil"
10	"os"
11	"path"
12	"sort"
13	"strings"
14)
15
16type Dir struct {
17	Files ModFiles
18	List  ModList
19	Path  string
20}
21
22func newDir(dirPath string) (*Dir, error) {
23	dir, err := os.ReadDir(dirPath)
24	if err != nil {
25		return nil, err
26	}
27
28	list, err := newModList(path.Join(dirPath, "mod-list.json"))
29	if err != nil {
30		// TODO: Create a json?
31		return nil, err
32	}
33
34	var files ModFiles
35	for _, file := range dir {
36		name := file.Name()
37		if name == "mod-list.json" || name == "mod-settings.dat" {
38			continue
39		}
40		var ident ModIdent
41		fileType := file.Type()
42		var deps *[]Dependency
43		if fileType.IsDir() || fileType&fs.ModeSymlink > 0 {
44			infoJson, err := parseInfoJson(path.Join(dirPath, name, "info.json"))
45			if err != nil {
46				errorln(err)
47				continue
48			}
49			ident.Name = infoJson.Name
50			ident.Version = &infoJson.Version // TODO: Will this preserve InfoJson forever?
51			deps = &infoJson.Dependencies
52		} else {
53			ident = newModIdent(name)
54		}
55		files = append(files, ModFile{
56			dependencies: deps,
57			Ident:        ident,
58			Path:         path.Join(dirPath, name),
59			Type:         fileType,
60		})
61	}
62
63	// Sort files so we can reliably get the newest version
64	sort.Sort(files)
65
66	return &Dir{
67		Files: files,
68		List:  *list,
69		Path:  dirPath,
70	}, nil
71}
72
73func (d *Dir) Find(mod Dependency) (file *ModFile, entry *ModListMod, err error) {
74	// Iterate in reverse to get the newest version first
75	for i := len(d.Files) - 1; i >= 0; i-- {
76		thisfile := &d.Files[i]
77		if thisfile.Ident.Name != mod.Ident.Name {
78			continue
79		}
80		if mod.Test(&thisfile.Ident) {
81			file = thisfile
82			break
83		}
84	}
85	if file == nil {
86		return nil, nil, errors.New(fmt.Sprintf("%s was not found in the mods directory", mod.Ident.toString()))
87	}
88
89	for i := range d.List.Mods {
90		thisentry := &d.List.Mods[i]
91		if thisentry.Name == mod.Ident.Name {
92			entry = thisentry
93			break
94		}
95	}
96
97	if entry == nil {
98		entry = d.List.add(mod.Ident.Name)
99	}
100
101	return file, entry, nil
102}
103
104func (d *Dir) save() {
105	err := d.List.save()
106	if err != nil {
107		abort(err)
108	}
109}
110
111// Wrapper type with implementations for sorting
112type ModFiles []ModFile
113
114func (f ModFiles) Len() int {
115	return len(f)
116}
117func (f ModFiles) Swap(i, j int) {
118	f[i], f[j] = f[j], f[i]
119}
120func (f ModFiles) Less(i, j int) bool {
121	modi, modj := f[i].Ident, &f[j].Ident
122	if modi.Name != modj.Name {
123		return modi.Name < modj.Name
124	}
125	return modi.Version.cmp(modj.Version) == VersionLt
126}
127
128type ModFile struct {
129	dependencies *[]Dependency
130
131	Ident ModIdent
132	Path  string
133	Type  fs.FileMode
134}
135
136func (f *ModFile) Dependencies() (*[]Dependency, error) {
137	if f.dependencies != nil {
138		return f.dependencies, nil
139	}
140
141	if !f.Type.IsRegular() {
142		return nil, errors.New("Failed to get dependencies for unzipped mod")
143	}
144
145	r, err := zip.OpenReader(f.Path)
146	if err != nil {
147		return nil, err
148	}
149
150	for _, file := range r.File {
151		// TODO: Use a regex to get the right one
152		if !strings.Contains(file.Name, "info.json") {
153			continue
154		}
155		rc, err := file.Open()
156		if err != nil {
157			return nil, err
158		}
159		defer rc.Close()
160		content, err := ioutil.ReadAll(rc)
161		if err != nil {
162			return nil, err
163		}
164
165		var unmarshaled InfoJson
166		err = json.Unmarshal(content, &unmarshaled)
167		if err != nil {
168			return nil, err
169		}
170		f.dependencies = &unmarshaled.Dependencies
171	}
172
173	return f.dependencies, nil
174}
175
176type InfoJson struct {
177	Dependencies []Dependency `json:"dependencies"`
178	Name         string       `json:"name"`
179	Version      Version      `json:"version"`
180}
181
182func parseInfoJson(path string) (*InfoJson, error) {
183	bytes, err := os.ReadFile(path)
184	if err != nil {
185		return nil, err
186	}
187
188	var unmarshaled InfoJson
189	err = json.Unmarshal(bytes, &unmarshaled)
190	if err != nil {
191		return nil, err
192	}
193
194	return &unmarshaled, nil
195}