jar-dedupe/main.go
2023-04-16 17:36:18 -07:00

146 lines
3.8 KiB
Go

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 "", "", ""
}