package main import ( "archive/zip" "encoding/json" "io/ioutil" "log" "os" "regexp" "github.com/pelletier/go-toml/v2" ) // Fabric/Quilt Mod Json type FabricModJson struct { ID string `json:"id"` Version string `json:"version"` Name string `json:"name"` } type QuiltModJson struct { QuiltLoader struct { ID string `json:"id"` Version string `json:"version"` Metadata struct { Name string `json:"name"` } `json:"metadata"` } `json:"quilt_loader"` } type ForgeModToml struct { Mods []struct { ModID string `toml:"modId"` Version string `toml:"version"` DisplayName string `toml:"displayName"` } `toml:"mods"` } var ( fabricMods []FabricModJson quiltMods []QuiltModJson forgeMods []ForgeModToml cleanPattern = regexp.MustCompile(`[<>;:\"|?*]`) ) // deduplicate mod jars based off the name of the jar // if the jar name is the same, then we will only keep the one with the highest version // or the latest modified date func main() { // get list of jars in ./mods files, err := ioutil.ReadDir("./mods") if err != nil { log.Fatalln("[READ DIR]", err) } // for modjars, for _, file := range files { // read the mod.json file and return the name, version, and filename name, version, loader := readMod("./mods/" + file.Name()) name = cleanPattern.ReplaceAllString(name, "") if loader != "" { err := os.Rename("./mods/"+file.Name(), "./mods/"+name+"-"+version+".jar") if err != nil { log.Fatalln("[RENAME MOD]", err) } else { log.Println("[RENAME MOD]", file.Name(), ">>", name+"-"+version+".jar") } } else { log.Println("Unsupported Loader", file.Name()) } } for _, mod := range fabricMods { // check if there is a duplicate in the list for _, mod2 := range fabricMods { if mod.Name == mod2.Name && mod.Version != mod2.Version { // if there is a duplicate, delete the one with the lower semver version if mod.Version < mod2.Version { name := cleanPattern.ReplaceAllString(mod.Name, "") err := os.Remove("./mods/" + name + "-" + mod.Version + ".jar") if err != nil { log.Fatalln("[REMOVE MOD]", err) } } } } } } // read the mod.json file and return the name, version, and loader func readMod(filename string) (string, string, string) { var fabricJson FabricModJson var quiltJson QuiltModJson var forgeMod ForgeModToml var loader string zipReader, err := zip.OpenReader(filename) if err != nil { log.Fatalln("[READ ZIP]", err) } defer zipReader.Close() for _, file := range zipReader.File { contents, _ := file.Open() if file.Name == "fabric.mod.json" { loader = "fabric" // add to fabric list fabricJsonErr := json.NewDecoder(contents).Decode(&fabricJson) if fabricJsonErr != nil { log.Println("[DECODE ERROR]", filename) log.Fatalln("[DECODE FABRIC MOD]", fabricJsonErr) } break } if file.Name == "quilt.mod.json" { loader = "quilt" quiltJsonErr := json.NewDecoder(contents).Decode(&quiltJson) if quiltJsonErr != nil { log.Println(filename) log.Println("[DECODE ERROR]", filename) log.Fatalln("[DECODE QUILT MOD]", quiltJsonErr) } break } if file.Name == "META-INF/mods.toml" { loader = "forge" //add to forge list forgeTomlErr := toml.NewDecoder(contents).Decode(&forgeMod) if forgeTomlErr != nil { log.Println("[DECODE ERROR]", filename) log.Fatalln("[DECODE FORGE MOD]", forgeTomlErr) } break } } if loader == "fabric" { fabricMods = append(fabricMods, fabricJson) return fabricJson.Name, fabricJson.Version, loader } if loader == "quilt" { quiltMods = append(quiltMods, quiltJson) return quiltJson.QuiltLoader.Metadata.Name, quiltJson.QuiltLoader.Version, loader } if loader == "forge" { forgeMods = append(forgeMods, forgeMod) return forgeMod.Mods[0].DisplayName, forgeMod.Mods[0].Version, loader } return "", "", "" }