mirror of
				https://github.com/packwiz/packwiz.git
				synced 2025-10-31 02:34:31 +01:00 
			
		
		
		
	Add cf detect command (experimental but should mostly work)
This commit is contained in:
		
							
								
								
									
										148
									
								
								curseforge/detect.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								curseforge/detect.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | |||||||
|  | package curseforge | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"github.com/aviddiviner/go-murmur" | ||||||
|  | 	"github.com/comp500/packwiz/core" | ||||||
|  | 	"github.com/spf13/cobra" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // TODO: make all of this less bad and hardcoded | ||||||
|  |  | ||||||
|  | // detectCmd represents the detect command | ||||||
|  | var detectCmd = &cobra.Command{ | ||||||
|  | 	Use:   "detect", | ||||||
|  | 	Short: "Detect .jar files in the mods folder (experimental)", | ||||||
|  | 	Args:  cobra.NoArgs, | ||||||
|  | 	Run: func(cmd *cobra.Command, args []string) { | ||||||
|  | 		fmt.Println("Loading modpack...") | ||||||
|  | 		pack, err := core.LoadPack() | ||||||
|  | 		if err != nil { | ||||||
|  | 			fmt.Println(err) | ||||||
|  | 			os.Exit(1) | ||||||
|  | 		} | ||||||
|  | 		index, err := pack.LoadIndex() | ||||||
|  | 		if err != nil { | ||||||
|  | 			fmt.Println(err) | ||||||
|  | 			os.Exit(1) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Walk files in the mods folder | ||||||
|  | 		var hashes []int | ||||||
|  | 		modPaths := make(map[int]string) | ||||||
|  | 		err = filepath.Walk("mods", func(path string, info os.FileInfo, err error) error { | ||||||
|  | 			if info.IsDir() { | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
|  | 			if !strings.HasSuffix(path, ".jar") { | ||||||
|  | 				// TODO: make this less bad | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
|  | 			fmt.Println("Hashing " + path) | ||||||
|  | 			bytes, err := ioutil.ReadFile(path) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			hash := getByteArrayHash(bytes) | ||||||
|  | 			hashes = append(hashes, int(hash)) | ||||||
|  | 			modPaths[int(hash)] = path | ||||||
|  | 			return nil | ||||||
|  | 		}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			fmt.Println(err) | ||||||
|  | 			os.Exit(1) | ||||||
|  | 		} | ||||||
|  | 		fmt.Printf("Found %d files, submitting...\n", len(hashes)) | ||||||
|  |  | ||||||
|  | 		res, err := getFingerprintInfo(hashes) | ||||||
|  | 		if err != nil { | ||||||
|  | 			fmt.Println(err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fmt.Printf("Successfully matched %d files\n", len(res.ExactFingerprints)) | ||||||
|  | 		if len(res.PartialMatches) > 0 { | ||||||
|  | 			fmt.Println("The following fingerprints were partial and I don't know what to do!!!") | ||||||
|  | 			for _, v := range res.PartialMatches { | ||||||
|  | 				fmt.Printf("%s (%d)", modPaths[v], v) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if len(res.UnmatchedFingerprints) > 0 { | ||||||
|  | 			fmt.Printf("Failed to match the following %d files:\n", len(res.UnmatchedFingerprints)) | ||||||
|  | 			for _, v := range res.UnmatchedFingerprints { | ||||||
|  | 				fmt.Printf("%s (%d)\n", modPaths[v], v) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		fmt.Println("Installing...") | ||||||
|  | 		for _, v := range res.ExactMatches { | ||||||
|  | 			modInfoData, err := getModInfo(v.ID) | ||||||
|  | 			if err != nil { | ||||||
|  | 				fmt.Println(err) | ||||||
|  | 				os.Exit(1) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			err = createModFile(modInfoData, v.File, &index) | ||||||
|  | 			if err != nil { | ||||||
|  | 				fmt.Println(err) | ||||||
|  | 				os.Exit(1) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			path, ok := modPaths[v.File.Fingerprint] | ||||||
|  | 			if ok { | ||||||
|  | 				err = os.Remove(path) | ||||||
|  | 				if err != nil { | ||||||
|  | 					fmt.Println(err) | ||||||
|  | 					os.Exit(1) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		fmt.Println("Installation done") | ||||||
|  |  | ||||||
|  | 		err = index.Refresh() | ||||||
|  | 		if err != nil { | ||||||
|  | 			fmt.Println(err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		err = index.Write() | ||||||
|  | 		if err != nil { | ||||||
|  | 			fmt.Println(err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		err = pack.UpdateIndexHash() | ||||||
|  | 		if err != nil { | ||||||
|  | 			fmt.Println(err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		err = pack.Write() | ||||||
|  | 		if err != nil { | ||||||
|  | 			fmt.Println(err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	curseforgeCmd.AddCommand(detectCmd) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getByteArrayHash(bytes []byte) uint64 { | ||||||
|  | 	return uint64(murmur.MurmurHash2(computeNormalizedArray(bytes), 1)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func computeNormalizedArray(bytes []byte) []byte { | ||||||
|  | 	var newArray []byte | ||||||
|  | 	for _, b := range bytes { | ||||||
|  | 		if !isWhitespaceCharacter(b) { | ||||||
|  | 			newArray = append(newArray, b) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return newArray | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func isWhitespaceCharacter(b byte) bool { | ||||||
|  | 	return b == 9 || b == 10 || b == 13 || b == 32 | ||||||
|  | } | ||||||
| @@ -291,3 +291,49 @@ func getSearch(searchText string, gameVersion string) ([]modInfo, error) { | |||||||
|  |  | ||||||
| 	return infoRes, nil | 	return infoRes, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type addonFingerprintResponse struct { | ||||||
|  | 	IsCacheBuilt bool `json:"isCacheBuilt"` | ||||||
|  | 	ExactMatches []struct { | ||||||
|  | 		ID          int           `json:"id"` | ||||||
|  | 		File        modFileInfo   `json:"file"` | ||||||
|  | 		LatestFiles []modFileInfo `json:"latestFiles"` | ||||||
|  | 	} `json:"exactMatches"` | ||||||
|  | 	ExactFingerprints        []int    `json:"exactFingerprints"` | ||||||
|  | 	PartialMatches           []int    `json:"partialMatches"` | ||||||
|  | 	PartialMatchFingerprints struct{} `json:"partialMatchFingerprints"` | ||||||
|  | 	InstalledFingerprints    []int    `json:"installedFingerprints"` | ||||||
|  | 	UnmatchedFingerprints    []int    `json:"unmatchedFingerprints"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getFingerprintInfo(hashes []int) (addonFingerprintResponse, error) { | ||||||
|  | 	var infoRes addonFingerprintResponse | ||||||
|  | 	client := &http.Client{} | ||||||
|  |  | ||||||
|  | 	hashesData, err := json.Marshal(hashes) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return addonFingerprintResponse{}, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req, err := http.NewRequest("POST", "https://addons-ecs.forgesvc.net/api/v2/fingerprint", bytes.NewBuffer(hashesData)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return addonFingerprintResponse{}, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// TODO: make this configurable application-wide | ||||||
|  | 	req.Header.Set("User-Agent", "comp500/packwiz client") | ||||||
|  | 	req.Header.Set("Accept", "application/json") | ||||||
|  | 	req.Header.Set("Content-Type", "application/json") | ||||||
|  |  | ||||||
|  | 	resp, err := client.Do(req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return addonFingerprintResponse{}, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = json.NewDecoder(resp.Body).Decode(&infoRes) | ||||||
|  | 	if err != nil && err != io.EOF { | ||||||
|  | 		return addonFingerprintResponse{}, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return infoRes, nil | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							| @@ -2,6 +2,7 @@ module github.com/comp500/packwiz | |||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	github.com/BurntSushi/toml v0.3.1 | 	github.com/BurntSushi/toml v0.3.1 | ||||||
|  | 	github.com/aviddiviner/go-murmur v0.0.0-20150519214947-b9740d71e571 | ||||||
| 	github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect | 	github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect | ||||||
| 	github.com/daviddengcn/go-colortext v0.0.0-20180409174941-186a3d44e920 // indirect | 	github.com/daviddengcn/go-colortext v0.0.0-20180409174941-186a3d44e920 // indirect | ||||||
| 	github.com/denormal/go-gitignore v0.0.0-20180930084346-ae8ad1d07817 | 	github.com/denormal/go-gitignore v0.0.0-20180930084346-ae8ad1d07817 | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							| @@ -7,6 +7,8 @@ github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmx | |||||||
| github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= | ||||||
| github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= | ||||||
| github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= | ||||||
|  | github.com/aviddiviner/go-murmur v0.0.0-20150519214947-b9740d71e571 h1:seCdAEDyB0Hti/v1VajB7pAOIk9zmz/0/KE0D0oFqnc= | ||||||
|  | github.com/aviddiviner/go-murmur v0.0.0-20150519214947-b9740d71e571/go.mod h1:VzSzsYCY3W9xWYWD8T2GLDidWTe5rTZv+UdDMGhLfjg= | ||||||
| github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= | ||||||
| github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | ||||||
| github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user