mirror of
				https://github.com/packwiz/packwiz.git
				synced 2025-11-04 04:04:31 +01:00 
			
		
		
		
	Implement Modrinth pack exporting (fixes #34)
This commit is contained in:
		
							
								
								
									
										37
									
								
								core/hash.go
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								core/hash.go
									
									
									
									
									
								
							@@ -5,23 +5,44 @@ import (
 | 
			
		||||
	"crypto/sha1"
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
	"crypto/sha512"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/packwiz/packwiz/curseforge/murmur2"
 | 
			
		||||
	"hash"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetHashImpl gets an implementation of hash.Hash for the given hash type string
 | 
			
		||||
func GetHashImpl(hashType string) (hash.Hash, error) {
 | 
			
		||||
func GetHashImpl(hashType string) (hash.Hash, HashStringer, error) {
 | 
			
		||||
	switch strings.ToLower(hashType) {
 | 
			
		||||
	case "sha1":
 | 
			
		||||
		return sha1.New(), nil
 | 
			
		||||
		return sha1.New(), hexStringer{}, nil
 | 
			
		||||
	case "sha256":
 | 
			
		||||
		return sha256.New(), nil
 | 
			
		||||
		return sha256.New(), hexStringer{}, nil
 | 
			
		||||
	case "sha512":
 | 
			
		||||
		return sha512.New(), nil
 | 
			
		||||
		return sha512.New(), hexStringer{}, nil
 | 
			
		||||
	case "md5":
 | 
			
		||||
		return md5.New(), nil
 | 
			
		||||
		return md5.New(), hexStringer{}, nil
 | 
			
		||||
	case "murmur2":
 | 
			
		||||
		return murmur2.New(), numberStringer{}, nil
 | 
			
		||||
	}
 | 
			
		||||
	// TODO: implement murmur2
 | 
			
		||||
	return nil, errors.New("hash implementation not found")
 | 
			
		||||
	return nil, nil, fmt.Errorf("hash implementation %s not found", hashType)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type HashStringer interface {
 | 
			
		||||
	HashToString([]byte) string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type hexStringer struct{}
 | 
			
		||||
 | 
			
		||||
func (hexStringer) HashToString(data []byte) string {
 | 
			
		||||
	return hex.EncodeToString(data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type numberStringer struct{}
 | 
			
		||||
 | 
			
		||||
func (numberStringer) HashToString(data []byte) string {
 | 
			
		||||
	return strconv.FormatUint(uint64(binary.BigEndian.Uint32(data)), 10)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
package core
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
@@ -133,7 +132,7 @@ func (in *Index) updateFile(path string) error {
 | 
			
		||||
		// Hash usage strategy (may change):
 | 
			
		||||
		// Just use SHA256, overwrite existing hash regardless of what it is
 | 
			
		||||
		// May update later to continue using the same hash that was already being used
 | 
			
		||||
		h, err := GetHashImpl("sha256")
 | 
			
		||||
		h, stringer, err := GetHashImpl("sha256")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			_ = f.Close()
 | 
			
		||||
			return err
 | 
			
		||||
@@ -146,7 +145,7 @@ func (in *Index) updateFile(path string) error {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		hashString = hex.EncodeToString(h.Sum(nil))
 | 
			
		||||
		hashString = stringer.HashToString(h.Sum(nil))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mod := false
 | 
			
		||||
@@ -348,7 +347,7 @@ func (in Index) SaveFile(f IndexFile, dest io.Writer) error {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	h, err := GetHashImpl(hashFormat)
 | 
			
		||||
	h, stringer, err := GetHashImpl(hashFormat)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -359,7 +358,7 @@ func (in Index) SaveFile(f IndexFile, dest io.Writer) error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	calculatedHash := hex.EncodeToString(h.Sum(nil))
 | 
			
		||||
	calculatedHash := stringer.HashToString(h.Sum(nil))
 | 
			
		||||
	if calculatedHash != f.Hash && !viper.GetBool("no-internal-hashes") {
 | 
			
		||||
		return errors.New("hash of saved file is invalid")
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
package core
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
@@ -90,7 +89,7 @@ func (m Mod) Write() (string, string, error) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	h, err := GetHashImpl("sha256")
 | 
			
		||||
	h, stringer, err := GetHashImpl("sha256")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		_ = f.Close()
 | 
			
		||||
		return "", "", err
 | 
			
		||||
@@ -101,7 +100,7 @@ func (m Mod) Write() (string, string, error) {
 | 
			
		||||
	// Disable indentation
 | 
			
		||||
	enc.Indent = ""
 | 
			
		||||
	err = enc.Encode(m)
 | 
			
		||||
	hashString := hex.EncodeToString(h.Sum(nil))
 | 
			
		||||
	hashString := stringer.HashToString(h.Sum(nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		_ = f.Close()
 | 
			
		||||
		return "sha256", hashString, err
 | 
			
		||||
@@ -135,7 +134,7 @@ func (m Mod) DownloadFile(dest io.Writer) error {
 | 
			
		||||
		_ = resp.Body.Close()
 | 
			
		||||
		return errors.New("invalid status code " + strconv.Itoa(resp.StatusCode))
 | 
			
		||||
	}
 | 
			
		||||
	h, err := GetHashImpl(m.Download.HashFormat)
 | 
			
		||||
	h, stringer, err := GetHashImpl(m.Download.HashFormat)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -146,7 +145,7 @@ func (m Mod) DownloadFile(dest io.Writer) error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	calculatedHash := hex.EncodeToString(h.Sum(nil))
 | 
			
		||||
	calculatedHash := stringer.HashToString(h.Sum(nil))
 | 
			
		||||
 | 
			
		||||
	// Check if the hash of the downloaded file matches the expected hash.
 | 
			
		||||
	if calculatedHash != m.Download.Hash {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
package core
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
@@ -116,7 +115,7 @@ func (pack *Pack) UpdateIndexHash() error {
 | 
			
		||||
	// Hash usage strategy (may change):
 | 
			
		||||
	// Just use SHA256, overwrite existing hash regardless of what it is
 | 
			
		||||
	// May update later to continue using the same hash that was already being used
 | 
			
		||||
	h, err := GetHashImpl("sha256")
 | 
			
		||||
	h, stringer, err := GetHashImpl("sha256")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		_ = f.Close()
 | 
			
		||||
		return err
 | 
			
		||||
@@ -125,7 +124,7 @@ func (pack *Pack) UpdateIndexHash() error {
 | 
			
		||||
		_ = f.Close()
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	hashString := hex.EncodeToString(h.Sum(nil))
 | 
			
		||||
	hashString := stringer.HashToString(h.Sum(nil))
 | 
			
		||||
 | 
			
		||||
	pack.Index.HashFormat = "sha256"
 | 
			
		||||
	pack.Index.Hash = hashString
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										54
									
								
								curseforge/murmur2/hash.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								curseforge/murmur2/hash.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
package murmur2
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"github.com/aviddiviner/go-murmur"
 | 
			
		||||
	"hash"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func New() hash.Hash32 {
 | 
			
		||||
	return &Murmur2CF{buf: make([]byte, 0)}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Murmur2CF struct {
 | 
			
		||||
	// Can't be done incrementally, since it is seeded with the length of the input!
 | 
			
		||||
	buf []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *Murmur2CF) Write(p []byte) (n int, err error) {
 | 
			
		||||
	for _, b := range p {
 | 
			
		||||
		if !isWhitespaceCharacter(b) {
 | 
			
		||||
			m.buf = append(m.buf, b)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return len(p), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CF modification: strips whitespace characters
 | 
			
		||||
func isWhitespaceCharacter(b byte) bool {
 | 
			
		||||
	return b == 9 || b == 10 || b == 13 || b == 32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *Murmur2CF) Sum(b []byte) []byte {
 | 
			
		||||
	if b == nil {
 | 
			
		||||
		b = make([]byte, 4)
 | 
			
		||||
	}
 | 
			
		||||
	binary.BigEndian.PutUint32(b, murmur.MurmurHash2(m.buf, 1))
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *Murmur2CF) Reset() {
 | 
			
		||||
	m.buf = make([]byte, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *Murmur2CF) Size() int {
 | 
			
		||||
	return 4
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *Murmur2CF) BlockSize() int {
 | 
			
		||||
	return 4
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *Murmur2CF) Sum32() uint32 {
 | 
			
		||||
	return binary.BigEndian.Uint32(m.Sum(nil))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										243
									
								
								modrinth/export.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								modrinth/export.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,243 @@
 | 
			
		||||
package modrinth
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"archive/zip"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/spf13/viper"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/packwiz/packwiz/core"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// exportCmd represents the export command
 | 
			
		||||
var exportCmd = &cobra.Command{
 | 
			
		||||
	Use:   "export",
 | 
			
		||||
	Short: "Export the current modpack into a .mrpack for Modrinth",
 | 
			
		||||
	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)
 | 
			
		||||
		}
 | 
			
		||||
		// Do a refresh to ensure files are up to date
 | 
			
		||||
		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
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// TODO: should index just expose indexPath itself, through a function?
 | 
			
		||||
		indexPath := filepath.Join(filepath.Dir(viper.GetString("pack-file")), filepath.FromSlash(pack.Index.File))
 | 
			
		||||
 | 
			
		||||
		mods := loadMods(index)
 | 
			
		||||
 | 
			
		||||
		var fileName = pack.GetPackName() + ".mrpack"
 | 
			
		||||
		expFile, err := os.Create(fileName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Printf("Failed to create zip: %s\n", err.Error())
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		exp := zip.NewWriter(expFile)
 | 
			
		||||
 | 
			
		||||
		// Add an overrides folder even if there are no files to go in it
 | 
			
		||||
		_, err = exp.Create("overrides/")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Printf("Failed to add overrides folder: %s\n", err.Error())
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// TODO: cache these (ideally with changes to pack format)
 | 
			
		||||
		fmt.Println("Retrieving SHA1 hashes for external mods...")
 | 
			
		||||
		sha1Hashes := make([]string, len(mods))
 | 
			
		||||
		for i, mod := range mods {
 | 
			
		||||
			if mod.Download.HashFormat == "sha1" {
 | 
			
		||||
				sha1Hashes[i] = mod.Download.Hash
 | 
			
		||||
			} else {
 | 
			
		||||
				// Hash format for this mod isn't SHA1 - and the Modrinth pack format requires it; so get it by downloading the file
 | 
			
		||||
				h, stringer, err := core.GetHashImpl("sha1")
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					panic("Failed to get sha1 hash implementation")
 | 
			
		||||
				}
 | 
			
		||||
				err = mod.DownloadFile(h)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					fmt.Printf("Error downloading mod file %s: %s\n", mod.Download.URL, err.Error())
 | 
			
		||||
					// TODO: exit(1)?
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				sha1Hashes[i] = stringer.HashToString(h.Sum(nil))
 | 
			
		||||
				fmt.Printf("Retrieved SHA1 hash for %s successfully\n", mod.Download.URL)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		manifestFile, err := exp.Create("modrinth.index.json")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			_ = exp.Close()
 | 
			
		||||
			_ = expFile.Close()
 | 
			
		||||
			fmt.Println("Error creating manifest: " + err.Error())
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		manifestFiles := make([]PackFile, len(mods))
 | 
			
		||||
		for i, mod := range mods {
 | 
			
		||||
			pathForward, err := filepath.Rel(filepath.Dir(indexPath), mod.GetDestFilePath())
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				fmt.Printf("Error resolving mod file: %s\n", err.Error())
 | 
			
		||||
				// TODO: exit(1)?
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			path := filepath.ToSlash(pathForward)
 | 
			
		||||
 | 
			
		||||
			hashes := make(map[string]string)
 | 
			
		||||
			hashes["sha1"] = sha1Hashes[i]
 | 
			
		||||
 | 
			
		||||
			// Create env options based on configured optional/side
 | 
			
		||||
			var envInstalled string
 | 
			
		||||
			if mod.Option != nil && mod.Option.Optional {
 | 
			
		||||
				envInstalled = "optional"
 | 
			
		||||
			} else {
 | 
			
		||||
				envInstalled = "required"
 | 
			
		||||
			}
 | 
			
		||||
			var clientEnv, serverEnv string
 | 
			
		||||
			if mod.Side == core.UniversalSide {
 | 
			
		||||
				clientEnv = envInstalled
 | 
			
		||||
				serverEnv = envInstalled
 | 
			
		||||
			} else if mod.Side == core.ClientSide {
 | 
			
		||||
				clientEnv = envInstalled
 | 
			
		||||
				serverEnv = "unsupported"
 | 
			
		||||
			} else if mod.Side == core.ServerSide {
 | 
			
		||||
				clientEnv = "unsupported"
 | 
			
		||||
				serverEnv = envInstalled
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			manifestFiles[i] = PackFile{
 | 
			
		||||
				Path:   path,
 | 
			
		||||
				Hashes: hashes,
 | 
			
		||||
				Env: &struct {
 | 
			
		||||
					Client string `json:"client"`
 | 
			
		||||
					Server string `json:"server"`
 | 
			
		||||
				}{Client: clientEnv, Server: serverEnv},
 | 
			
		||||
				Downloads: []string{mod.Download.URL},
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		dependencies := make(map[string]string)
 | 
			
		||||
		dependencies["minecraft"], err = pack.GetMCVersion()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			_ = exp.Close()
 | 
			
		||||
			_ = expFile.Close()
 | 
			
		||||
			fmt.Println("Error creating manifest: " + err.Error())
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		if fabricVersion, ok := pack.Versions["fabric"]; ok {
 | 
			
		||||
			dependencies["fabric-loader"] = fabricVersion
 | 
			
		||||
		} else if forgeVersion, ok := pack.Versions["forge"]; ok {
 | 
			
		||||
			dependencies["forge"] = forgeVersion
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		manifest := Pack{
 | 
			
		||||
			FormatVersion: 1,
 | 
			
		||||
			Game:          "minecraft",
 | 
			
		||||
			VersionID:     pack.Version,
 | 
			
		||||
			Name:          pack.Name,
 | 
			
		||||
			Files:         manifestFiles,
 | 
			
		||||
			Dependencies:  dependencies,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		w := json.NewEncoder(manifestFile)
 | 
			
		||||
		w.SetIndent("", "    ") // Documentation uses 4 spaces
 | 
			
		||||
		err = w.Encode(manifest)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			_ = exp.Close()
 | 
			
		||||
			_ = expFile.Close()
 | 
			
		||||
			fmt.Println("Error writing manifest: " + err.Error())
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, v := range index.Files {
 | 
			
		||||
			if !v.MetaFile {
 | 
			
		||||
				// Save all non-metadata files into the zip
 | 
			
		||||
				path, err := filepath.Rel(filepath.Dir(indexPath), index.GetFilePath(v))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					fmt.Printf("Error resolving file: %s\n", err.Error())
 | 
			
		||||
					// TODO: exit(1)?
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				file, err := exp.Create(filepath.ToSlash(filepath.Join("overrides", path)))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					fmt.Printf("Error creating file: %s\n", err.Error())
 | 
			
		||||
					// TODO: exit(1)?
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				err = index.SaveFile(v, file)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					fmt.Printf("Error copying file: %s\n", err.Error())
 | 
			
		||||
					// TODO: exit(1)?
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		err = exp.Close()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("Error writing export file: " + err.Error())
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		err = expFile.Close()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("Error writing export file: " + err.Error())
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fmt.Println("Modpack exported to " + fileName)
 | 
			
		||||
		fmt.Println("Make sure you remove this file before running packwiz refresh, or add it to .packwizignore")
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func loadMods(index core.Index) []core.Mod {
 | 
			
		||||
	modPaths := index.GetAllMods()
 | 
			
		||||
	mods := make([]core.Mod, len(modPaths))
 | 
			
		||||
	i := 0
 | 
			
		||||
	fmt.Println("Reading mod files...")
 | 
			
		||||
	for _, v := range modPaths {
 | 
			
		||||
		modData, err := core.LoadMod(v)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Printf("Error reading mod file %s: %s\n", v, err.Error())
 | 
			
		||||
			// TODO: exit(1)?
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		mods[i] = modData
 | 
			
		||||
		i++
 | 
			
		||||
	}
 | 
			
		||||
	return mods[:i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	modrinthCmd.AddCommand(exportCmd)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								modrinth/pack.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								modrinth/pack.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
package modrinth
 | 
			
		||||
 | 
			
		||||
type Pack struct {
 | 
			
		||||
	FormatVersion int    `json:"formatVersion"`
 | 
			
		||||
	Game          string `json:"game"`
 | 
			
		||||
	VersionID     string `json:"versionId"`
 | 
			
		||||
	Name          string `json:"name"`
 | 
			
		||||
	// TODO: implement Summary
 | 
			
		||||
	// Summary       string `json:"summary"`
 | 
			
		||||
	Files        []PackFile        `json:"files"`
 | 
			
		||||
	Dependencies map[string]string `json:"dependencies"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type PackFile struct {
 | 
			
		||||
	Path   string            `json:"path"`
 | 
			
		||||
	Hashes map[string]string `json:"hashes"`
 | 
			
		||||
	Env    *struct {
 | 
			
		||||
		Client string `json:"client"`
 | 
			
		||||
		Server string `json:"server"`
 | 
			
		||||
	} `json:"env"`
 | 
			
		||||
	Downloads []string `json:"downloads"`
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user