z_mirror/fmm
git clone https://git.raiguard.me/z_mirror/fmm
3c2b7f3
predep-parsing
1package main 2 3import ( 4"archive/zip" 5"encoding/json" 6"errors" 7"fmt" 8"io/fs" 9"io/ioutil" 10"os" 11"path" 12"sort" 13"strings" 14) 15 16type Dir struct { 17Files ModFiles 18List ModList 19Path string 20} 21 22func newDir (dirPath string ) (* Dir ,error ) { 23dir ,err := os .ReadDir (dirPath ) 24if err != nil { 25return nil ,err 26 } 27 28list ,err := newModList (path .Join (dirPath ,"mod-list.json" )) 29if err != nil { 30// TODO: Create a json? 31return nil ,err 32 } 33 34var files ModFiles 35for _ ,file := range dir { 36name := file .Name () 37if name == "mod-list.json" || name == "mod-settings.dat" { 38continue 39 } 40var ident ModIdent 41fileType := file .Type () 42var deps * []Dependency 43if fileType .IsDir ()|| fileType & fs .ModeSymlink > 0 { 44infoJson ,err := parseInfoJson (path .Join (dirPath ,name ,"info.json" )) 45if err != nil { 46errorln (err ) 47continue 48 } 49ident .Name = infoJson .Name 50ident .Version = & infoJson .Version // TODO: Will this preserve InfoJson forever? 51deps = & infoJson .Dependencies 52 }else { 53ident = newModIdent (name ) 54 } 55files = append (files ,ModFile { 56dependencies :deps , 57Ident :ident , 58Path :path .Join (dirPath ,name ), 59Type :fileType , 60 }) 61 } 62 63// Sort files so we can reliably get the newest version 64sort .Sort (files ) 65 66return & Dir { 67Files :files , 68List :* list , 69Path :dirPath , 70 },nil 71} 72 73func (d * Dir )Find (mod Dependency ) (file * ModFile ,entry * ModListMod ,err error ) { 74// Iterate in reverse to get the newest version first 75for i := len (d .Files )- 1 ;i >= 0 ;i -- { 76thisfile := & d .Files [i ] 77if thisfile .Ident .Name != mod .Ident .Name { 78continue 79 } 80if mod .Test (& thisfile .Ident ) { 81file = thisfile 82break 83 } 84 } 85if file == nil { 86return nil ,nil ,errors .New (fmt .Sprintf ("%s was not found in the mods directory" ,mod .Ident .toString ())) 87 } 88 89for i := range d .List .Mods { 90thisentry := & d .List .Mods [i ] 91if thisentry .Name == mod .Ident .Name { 92entry = thisentry 93break 94 } 95 } 96 97if entry == nil { 98entry = d .List .add (mod .Ident .Name ) 99 } 100 101return file ,entry ,nil 102} 103 104func (d * Dir )save () { 105err := d .List .save () 106if err != nil { 107abort (err ) 108 } 109} 110 111// Wrapper type with implementations for sorting 112type ModFiles []ModFile 113 114func (f ModFiles )Len ()int { 115return len (f ) 116} 117func (f ModFiles )Swap (i ,j int ) { 118f [i ],f [j ]= f [j ],f [i ] 119} 120func (f ModFiles )Less (i ,j int )bool { 121modi ,modj := f [i ].Ident ,& f [j ].Ident 122if modi .Name != modj .Name { 123return modi .Name < modj .Name 124 } 125return modi .Version .cmp (modj .Version )== VersionLt 126} 127 128type ModFile struct { 129dependencies * []Dependency 130 131Ident ModIdent 132Path string 133Type fs.FileMode 134} 135 136func (f * ModFile )Dependencies () (* []Dependency ,error ) { 137if f .dependencies != nil { 138return f .dependencies ,nil 139 } 140 141if ! f .Type .IsRegular () { 142return nil ,errors .New ("Failed to get dependencies for unzipped mod" ) 143 } 144 145r ,err := zip .OpenReader (f .Path ) 146if err != nil { 147return nil ,err 148 } 149 150for _ ,file := range r .File { 151// TODO: Use a regex to get the right one 152if ! strings .Contains (file .Name ,"info.json" ) { 153continue 154 } 155rc ,err := file .Open () 156if err != nil { 157return nil ,err 158 } 159defer rc .Close () 160content ,err := ioutil .ReadAll (rc ) 161if err != nil { 162return nil ,err 163 } 164 165var unmarshaled InfoJson 166err = json .Unmarshal (content ,& unmarshaled ) 167if err != nil { 168return nil ,err 169 } 170f .dependencies = & unmarshaled .Dependencies 171 } 172 173return f .dependencies ,nil 174} 175 176type InfoJson struct { 177Dependencies []Dependency `json:"dependencies"` 178Name string `json:"name"` 179Version Version `json:"version"` 180} 181 182func parseInfoJson (path string ) (* InfoJson ,error ) { 183bytes ,err := os .ReadFile (path ) 184if err != nil { 185return nil ,err 186 } 187 188var unmarshaled InfoJson 189err = json .Unmarshal (bytes ,& unmarshaled ) 190if err != nil { 191return nil ,err 192 } 193 194return & unmarshaled ,nil 195}