Abstract file access, implement zip reading

This commit is contained in:
comp500 2019-10-24 18:30:06 +01:00
parent 8956ec9bcb
commit c853f0ff07

View File

@ -1,6 +1,7 @@
package curseforge package curseforge
import ( import (
"archive/zip"
"bufio" "bufio"
"bytes" "bytes"
"encoding/json" "encoding/json"
@ -18,6 +19,8 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
) )
// TODO: this file is a mess, I need to refactor it
type importPackFile interface { type importPackFile interface {
Name() string Name() string
Open() (io.ReadCloser, error) Open() (io.ReadCloser, error)
@ -33,6 +36,13 @@ type importPackMetadata interface {
GetFiles() ([]importPackFile, error) GetFiles() ([]importPackFile, error)
} }
type importPackSource interface {
GetFile(path string) (importPackFile, error)
//TODO: was GetFileList(base string), is it needed?
GetFileList() ([]importPackFile, error)
GetPackFile() importPackFile
}
// importCmd represents the import command // importCmd represents the import command
var importCmd = &cobra.Command{ var importCmd = &cobra.Command{
Use: "import [modpack]", Use: "import [modpack]",
@ -108,43 +118,49 @@ var importCmd = &cobra.Command{
// Check if file is a zip // Check if file is a zip
if string(header) == "PK" { if string(header) == "PK" {
fmt.Println("it do be a zip doe") // Read the whole file (as bufio doesn't work for zips)
os.Exit(0) zipData, err := ioutil.ReadAll(buf)
} else {
// Read the whole file (as we are going to parse it multiple times)
fileData, err := ioutil.ReadAll(buf)
if err != nil { if err != nil {
fmt.Printf("Error reading file: %s\n", err) fmt.Printf("Error reading file: %s\n", err)
os.Exit(1) os.Exit(1)
} }
// Get zip size
// Determine what format the file is stat, err := f.Stat()
var jsonFile map[string]interface{}
err = json.Unmarshal(fileData, &jsonFile)
if err != nil { if err != nil {
fmt.Printf("Error parsing JSON: %s\n", err) fmt.Printf("Error reading file: %s\n", err)
os.Exit(1)
}
zr, err := zip.NewReader(bytes.NewReader(zipData), stat.Size())
if err != nil {
fmt.Printf("Error parsing zip: %s\n", err)
os.Exit(1) os.Exit(1)
} }
isManifest := false // Search the zip for minecraftinstance.json or manifest.json
if v, ok := jsonFile["manifestType"]; ok { var metaFile *zip.File
isManifest = v.(string) == "minecraftModpack" for _, v := range zr.File {
fileName := filepath.Base(v.Name)
if fileName == "minecraftinstance.json" || fileName == "manifest.json" {
metaFile = v
} }
if isManifest { }
fmt.Println("it do be a manifest doe")
os.Exit(0) if metaFile == nil {
fmt.Println("Can't find manifest.json or minecraftinstance.json, is this a valid pack?")
os.Exit(1)
}
packImport = readMetadata(zipPackSource{
MetaFile: metaFile,
Reader: zr,
})
} else { } else {
// Replace FileNameOnDisk with fileNameOnDisk packImport = readMetadata(diskPackSource{
fileData = bytes.ReplaceAll(fileData, []byte("FileNameOnDisk"), []byte("fileNameOnDisk")) MetaSource: buf,
packMeta := twitchInstalledPackMeta{} MetaName: inputFile, // TODO: is this always the correct file?
err = json.Unmarshal(fileData, &packMeta) BasePath: filepath.Dir(inputFile),
if err != nil { })
fmt.Printf("Error parsing JSON: %s\n", err)
os.Exit(1)
}
packMeta.srcFile = inputFile
packImport = packMeta
}
} }
} }
@ -373,6 +389,28 @@ func (f diskFile) Open() (io.ReadCloser, error) {
return os.Open(f.Path) return os.Open(f.Path)
} }
type zipReaderFile struct {
NameInternal string
*zip.File
}
func (f zipReaderFile) Name() string {
return f.NameInternal
}
type readerFile struct {
NameInternal string
Reader *io.ReadCloser
}
func (f readerFile) Name() string {
return f.NameInternal
}
func (f readerFile) Open() (io.ReadCloser, error) {
return *f.Reader, nil
}
func diskFilesFromPath(base string) ([]importPackFile, error) { func diskFilesFromPath(base string) ([]importPackFile, error) {
list := make([]importPackFile, 0) list := make([]importPackFile, 0)
err := filepath.Walk(base, func(path string, info os.FileInfo, err error) error { err := filepath.Walk(base, func(path string, info os.FileInfo, err error) error {
@ -395,6 +433,60 @@ func diskFilesFromPath(base string) ([]importPackFile, error) {
return list, nil return list, nil
} }
type diskPackSource struct {
MetaSource *bufio.Reader
MetaName string
BasePath string
}
func (s diskPackSource) GetFile(path string) (importPackFile, error) {
return diskFile{s.BasePath, path}, nil
}
func (s diskPackSource) GetFileList() ([]importPackFile, error) {
return diskFilesFromPath(s.BasePath)
}
func (s diskPackSource) GetPackFile() importPackFile {
rc := ioutil.NopCloser(s.MetaSource)
return readerFile{s.MetaName, &rc}
}
type zipPackSource struct {
MetaFile *zip.File
Reader *zip.Reader
cachedFileList []importPackFile
}
func (s zipPackSource) GetFile(path string) (importPackFile, error) {
if s.cachedFileList == nil {
s.cachedFileList = make([]importPackFile, len(s.Reader.File))
for i, v := range s.Reader.File {
s.cachedFileList[i] = zipReaderFile{v.Name, v}
}
}
for _, v := range s.cachedFileList {
if v.Name() == path {
return v, nil
}
}
return zipReaderFile{}, errors.New("file not found in zip")
}
func (s zipPackSource) GetFileList() ([]importPackFile, error) {
if s.cachedFileList == nil {
s.cachedFileList = make([]importPackFile, len(s.Reader.File))
for i, v := range s.Reader.File {
s.cachedFileList[i] = zipReaderFile{v.Name, v}
}
}
return s.cachedFileList, nil
}
func (s zipPackSource) GetPackFile() importPackFile {
return zipReaderFile{s.MetaFile.Name, s.MetaFile}
}
type twitchInstalledPackMeta struct { type twitchInstalledPackMeta struct {
NameInternal string `json:"name"` NameInternal string `json:"name"`
Path string `json:"installPath"` Path string `json:"installPath"`
@ -472,3 +564,51 @@ func (m twitchInstalledPackMeta) GetFiles() ([]importPackFile, error) {
} }
return list, nil return list, nil
} }
func readMetadata(s importPackSource) importPackMetadata {
var packImport importPackMetadata
metaFile := s.GetPackFile()
rdr, err := metaFile.Open()
if err != nil {
fmt.Printf("Error reading file: %s\n", err)
os.Exit(1)
}
// Read the whole file (as we are going to parse it multiple times)
fileData, err := ioutil.ReadAll(rdr)
if err != nil {
fmt.Printf("Error reading file: %s\n", err)
os.Exit(1)
}
// Determine what format the file is
var jsonFile map[string]interface{}
err = json.Unmarshal(fileData, &jsonFile)
if err != nil {
fmt.Printf("Error parsing JSON: %s\n", err)
os.Exit(1)
}
isManifest := false
if v, ok := jsonFile["manifestType"]; ok {
isManifest = v.(string) == "minecraftModpack"
}
if isManifest {
fmt.Println("it do be a manifest doe")
os.Exit(0)
// TODO: implement manifest parsing
} else {
// Replace FileNameOnDisk with fileNameOnDisk
fileData = bytes.ReplaceAll(fileData, []byte("FileNameOnDisk"), []byte("fileNameOnDisk"))
packMeta := twitchInstalledPackMeta{}
err = json.Unmarshal(fileData, &packMeta)
if err != nil {
fmt.Printf("Error parsing JSON: %s\n", err)
os.Exit(1)
}
packMeta.srcFile = metaFile.Name()
packImport = packMeta
}
return packImport
}