only delete owned mods, mod enable/disable, do not re-extract
This commit is contained in:
parent
c32e6134b3
commit
1daf6e9610
6 changed files with 102 additions and 12 deletions
|
@ -329,12 +329,17 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan InstallUpdate) e
|
||||||
for _, entry := range dir {
|
for _, entry := range dir {
|
||||||
if entry.IsDir() {
|
if entry.IsDir() {
|
||||||
if _, ok := lockfile[entry.Name()]; !ok {
|
if _, ok := lockfile[entry.Name()]; !ok {
|
||||||
if err := os.RemoveAll(filepath.Join(modsDirectory, entry.Name())); err != nil {
|
modDir := filepath.Join(modsDirectory, entry.Name())
|
||||||
|
_, err := os.Stat(filepath.Join(modDir, ".smm"))
|
||||||
|
if err == nil {
|
||||||
|
log.Info().Str("mod_reference", entry.Name()).Msg("deleting mod")
|
||||||
|
if err := os.RemoveAll(modDir); err != nil {
|
||||||
return errors.Wrap(err, "failed to delete mod directory")
|
return errors.Wrap(err, "failed to delete mod directory")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
completed := 0
|
completed := 0
|
||||||
for modReference, version := range lockfile {
|
for modReference, version := range lockfile {
|
||||||
|
@ -384,7 +389,7 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan InstallUpdate) e
|
||||||
downloading = false
|
downloading = false
|
||||||
|
|
||||||
log.Info().Str("mod_reference", modReference).Str("version", version.Version).Str("link", version.Link).Msg("extracting mod")
|
log.Info().Str("mod_reference", modReference).Str("version", version.Version).Str("link", version.Link).Msg("extracting mod")
|
||||||
if err := utils.ExtractMod(reader, size, filepath.Join(modsDirectory, modReference), genericUpdates); err != nil {
|
if err := utils.ExtractMod(reader, size, filepath.Join(modsDirectory, modReference), version.Hash, genericUpdates); err != nil {
|
||||||
return errors.Wrap(err, "could not extract "+modReference)
|
return errors.Wrap(err, "could not extract "+modReference)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -296,3 +296,30 @@ func (p *Profile) Resolve(resolver DependencyResolver, lockFile *LockFile, gameV
|
||||||
|
|
||||||
return resultLockfile, nil
|
return resultLockfile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Profile) IsModEnabled(reference string) bool {
|
||||||
|
if p.Mods == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if mod, ok := p.Mods[reference]; ok {
|
||||||
|
return mod.Enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Profile) SetModEnabled(reference string, enabled bool) {
|
||||||
|
if p.Mods == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := p.Mods[reference]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Mods[reference] = ProfileMod{
|
||||||
|
Version: p.Mods[reference].Version,
|
||||||
|
Enabled: enabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -144,7 +144,7 @@ func (m apply) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
case KeyEnter:
|
case KeyEnter:
|
||||||
if m.status.done {
|
if m.status.done {
|
||||||
if m.parent != nil {
|
if m.parent != nil {
|
||||||
return m.parent, nil
|
return m.parent, m.parent.Init()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
|
|
|
@ -44,6 +44,24 @@ func NewModMenu(root components.RootModel, parent tea.Model, mod utils.Mod) tea.
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if root.GetCurrentProfile().IsModEnabled(mod.Reference) {
|
||||||
|
items = append(items, utils.SimpleItem[modMenu]{
|
||||||
|
ItemTitle: "Disable Mod",
|
||||||
|
Activate: func(msg tea.Msg, currentModel modMenu) (tea.Model, tea.Cmd) {
|
||||||
|
root.GetCurrentProfile().SetModEnabled(mod.Reference, false)
|
||||||
|
return currentModel.parent, currentModel.parent.Init()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
items = append(items, utils.SimpleItem[modMenu]{
|
||||||
|
ItemTitle: "Enable Mod",
|
||||||
|
Activate: func(msg tea.Msg, currentModel modMenu) (tea.Model, tea.Cmd) {
|
||||||
|
root.GetCurrentProfile().SetModEnabled(mod.Reference, true)
|
||||||
|
return currentModel.parent, currentModel.parent.Init()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
items = []list.Item{
|
items = []list.Item{
|
||||||
utils.SimpleItem[modMenu]{
|
utils.SimpleItem[modMenu]{
|
||||||
|
|
|
@ -510,47 +510,71 @@ func (c ModsListDelegate) Render(w io.Writer, m list.Model, index int, item list
|
||||||
}
|
}
|
||||||
|
|
||||||
isInstalled := false
|
isInstalled := false
|
||||||
|
isDisabled := false
|
||||||
if c.Context != nil {
|
if c.Context != nil {
|
||||||
profile := c.Context.Profiles.Profiles[c.Context.Profiles.SelectedProfile]
|
profile := c.Context.Profiles.Profiles[c.Context.Profiles.SelectedProfile]
|
||||||
if profile != nil {
|
if profile != nil {
|
||||||
if profile.HasMod(realItem.Extra.Mod_reference) {
|
if profile.HasMod(realItem.Extra.Mod_reference) {
|
||||||
isInstalled = true
|
isInstalled = true
|
||||||
|
isDisabled = !profile.IsModEnabled(realItem.Extra.Mod_reference)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if emptyFilter {
|
if emptyFilter {
|
||||||
if isInstalled {
|
if isInstalled {
|
||||||
|
if isDisabled {
|
||||||
|
title = lipgloss.NewStyle().Foreground(lipgloss.Color("220")).Render("✓ " + title)
|
||||||
|
} else {
|
||||||
title = lipgloss.NewStyle().Foreground(lipgloss.Color("40")).Render("✓ " + title)
|
title = lipgloss.NewStyle().Foreground(lipgloss.Color("40")).Render("✓ " + title)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
title = s.DimmedTitle.Render(title)
|
title = s.DimmedTitle.Render(title)
|
||||||
} else if isSelected && m.FilterState() != list.Filtering {
|
} else if isSelected && m.FilterState() != list.Filtering {
|
||||||
if isFiltered {
|
if isFiltered {
|
||||||
unmatched := s.SelectedTitle.Inline(true)
|
unmatched := s.SelectedTitle.Inline(true)
|
||||||
matched := unmatched.Copy().Inherit(s.FilterMatch)
|
matched := unmatched.Copy().Inherit(s.FilterMatch)
|
||||||
if isInstalled {
|
if isInstalled {
|
||||||
|
if isDisabled {
|
||||||
|
unmatched = unmatched.Foreground(lipgloss.Color("220"))
|
||||||
|
matched = matched.Foreground(lipgloss.Color("220"))
|
||||||
|
} else {
|
||||||
unmatched = unmatched.Foreground(lipgloss.Color("40"))
|
unmatched = unmatched.Foreground(lipgloss.Color("40"))
|
||||||
matched = matched.Foreground(lipgloss.Color("40"))
|
matched = matched.Foreground(lipgloss.Color("40"))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
title = lipgloss.StyleRunes(title, matchedRunes, matched, unmatched)
|
title = lipgloss.StyleRunes(title, matchedRunes, matched, unmatched)
|
||||||
}
|
}
|
||||||
if isInstalled {
|
if isInstalled {
|
||||||
|
if isDisabled {
|
||||||
|
title = lipgloss.NewStyle().Foreground(lipgloss.Color("220")).Render("✓ ") + title
|
||||||
|
} else {
|
||||||
title = lipgloss.NewStyle().Foreground(lipgloss.Color("40")).Render("✓ ") + title
|
title = lipgloss.NewStyle().Foreground(lipgloss.Color("40")).Render("✓ ") + title
|
||||||
}
|
}
|
||||||
|
}
|
||||||
title = s.SelectedTitle.Render(title)
|
title = s.SelectedTitle.Render(title)
|
||||||
} else {
|
} else {
|
||||||
if isFiltered {
|
if isFiltered {
|
||||||
unmatched := s.NormalTitle.Inline(true)
|
unmatched := s.NormalTitle.Inline(true)
|
||||||
matched := unmatched.Copy().Inherit(s.FilterMatch)
|
matched := unmatched.Copy().Inherit(s.FilterMatch)
|
||||||
if isInstalled {
|
if isInstalled {
|
||||||
|
if isDisabled {
|
||||||
|
unmatched = unmatched.Foreground(lipgloss.Color("220"))
|
||||||
|
matched = matched.Foreground(lipgloss.Color("220"))
|
||||||
|
} else {
|
||||||
unmatched = unmatched.Foreground(lipgloss.Color("40"))
|
unmatched = unmatched.Foreground(lipgloss.Color("40"))
|
||||||
matched = matched.Foreground(lipgloss.Color("40"))
|
matched = matched.Foreground(lipgloss.Color("40"))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
title = lipgloss.StyleRunes(title, matchedRunes, matched, unmatched)
|
title = lipgloss.StyleRunes(title, matchedRunes, matched, unmatched)
|
||||||
}
|
}
|
||||||
if isInstalled {
|
if isInstalled {
|
||||||
|
if isDisabled {
|
||||||
|
title = lipgloss.NewStyle().Foreground(lipgloss.Color("220")).Render("✓ ") + title
|
||||||
|
} else {
|
||||||
title = lipgloss.NewStyle().Foreground(lipgloss.Color("40")).Render("✓ ") + title
|
title = lipgloss.NewStyle().Foreground(lipgloss.Color("40")).Render("✓ ") + title
|
||||||
}
|
}
|
||||||
|
}
|
||||||
title = s.NormalTitle.Render(title)
|
title = s.NormalTitle.Render(title)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
18
utils/io.go
18
utils/io.go
|
@ -140,7 +140,19 @@ func SHA256Data(f io.Reader) (string, error) {
|
||||||
return hex.EncodeToString(h.Sum(nil)), nil
|
return hex.EncodeToString(h.Sum(nil)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExtractMod(f io.ReaderAt, size int64, location string, updates chan GenericUpdate) error {
|
func ExtractMod(f io.ReaderAt, size int64, location string, hash string, updates chan GenericUpdate) error {
|
||||||
|
hashFile := filepath.Join(location, ".smm")
|
||||||
|
hashBytes, err := os.ReadFile(hashFile)
|
||||||
|
if err != nil {
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return errors.Wrap(err, "failed to read .smm mod hash file")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if hash == string(hashBytes) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := os.MkdirAll(location, 0777); err != nil {
|
if err := os.MkdirAll(location, 0777); err != nil {
|
||||||
if !os.IsExist(err) {
|
if !os.IsExist(err) {
|
||||||
return errors.Wrap(err, "failed to create mod directory: "+location)
|
return errors.Wrap(err, "failed to create mod directory: "+location)
|
||||||
|
@ -181,6 +193,10 @@ func ExtractMod(f io.ReaderAt, size int64, location string, updates chan Generic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(hashFile, []byte(hash), 0777); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to write .smm mod hash file")
|
||||||
|
}
|
||||||
|
|
||||||
if updates != nil {
|
if updates != nil {
|
||||||
select {
|
select {
|
||||||
case updates <- GenericUpdate{Progress: 1}:
|
case updates <- GenericUpdate{Progress: 1}:
|
||||||
|
|
Loading…
Reference in a new issue