From e9758598fef1eb7e82189026323cc9b36a48260d Mon Sep 17 00:00:00 2001 From: gravestench <> Date: Fri, 4 Jun 2021 07:32:32 -0700 Subject: [PATCH 01/40] minor edits to hsapp.Create and hsapp.Run() --- hsapp/app.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/hsapp/app.go b/hsapp/app.go index b7b4bc59..6f40710c 100644 --- a/hsapp/app.go +++ b/hsapp/app.go @@ -106,19 +106,15 @@ type App struct { // Create creates new app instance func Create() (*App, error) { - tl := hscommon.NewTextureLoader() result := &App{ Flags: &Flags{}, editors: make([]hscommon.EditorWindow, 0), editorConstructors: make(map[hsfiletypes.FileType]editorConstructor), - TextureLoader: tl, + TextureLoader: hscommon.NewTextureLoader(), + InputManager: hsinput.NewInputManager(), + abyssWrapper: abysswrapper.Create(), } - im := hsinput.NewInputManager() - result.InputManager = im - - result.abyssWrapper = abysswrapper.Create() - result.parseArgs() result.config = hsconfig.Load(*result.Flags.optionalConfigPath) @@ -158,7 +154,7 @@ func (a *App) Run() { defer a.Quit() // force-close and save everything (in case of crash) if a.config.LoggingToFile || *a.Flags.logFile != "" { - var path string = a.config.LogFilePath + var path = a.config.LogFilePath if *a.Flags.logFile != "" { path = *a.Flags.logFile } From bcc8036ea5484019ddf0aadd58f43e3f958ad5c6 Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 07:49:29 -0700 Subject: [PATCH 02/40] adding comment for why logging setup not in app.setup --- hsapp/app.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hsapp/app.go b/hsapp/app.go index 6f40710c..de80b940 100644 --- a/hsapp/app.go +++ b/hsapp/app.go @@ -153,6 +153,8 @@ func (a *App) Run() { defer a.Quit() // force-close and save everything (in case of crash) + // setting up the logging here, as opposed to inside of app.setup(), + // because of the deferred call to logfile.Close() if a.config.LoggingToFile || *a.Flags.logFile != "" { var path = a.config.LogFilePath if *a.Flags.logFile != "" { From 847352cab31fe4d29de8ab457cc9e1d2212b0caa Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 07:50:27 -0700 Subject: [PATCH 03/40] fixing typo --- hsapp/app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hsapp/app.go b/hsapp/app.go index de80b940..70e429fa 100644 --- a/hsapp/app.go +++ b/hsapp/app.go @@ -297,7 +297,7 @@ func (a *App) openEditor(path *hscommon.PathEntry) { a.editorManagerMutex.RUnlock() - // w, h = 0, because we're createing a new editor, + // w, h = 0, because we're creating a new editor, // width and height aren't saved, so we give 0 and // editors without AutoResize flag sets w, h to default a.createEditor(path, nil, editorWindowDefaultX, editorWindowDefaultY, 0, 0) From 3c586e0c4fdd5f0e2435e27c8b559dcda14bc38b Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 07:53:15 -0700 Subject: [PATCH 04/40] removed unnecessary nolint directive --- hsapp/app.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hsapp/app.go b/hsapp/app.go index 70e429fa..af1dd1ed 100644 --- a/hsapp/app.go +++ b/hsapp/app.go @@ -47,6 +47,7 @@ const ( consoleDefaultY = 500 samplesPerSecond = 22050 + sampleDuration = time.Second / 10 autoSaveTimer = 120 @@ -135,9 +136,9 @@ func (a *App) Run() { a.masterWindow.SetBgColor(color) sampleRate := beep.SampleRate(samplesPerSecond) + bufferSize := sampleRate.N(sampleDuration) - // nolint:gomnd // this is 0.1 of second - if err = speaker.Init(sampleRate, sampleRate.N(time.Second/10)); err != nil { + if err = speaker.Init(sampleRate, bufferSize); err != nil { log.Fatal(err) } From 5245356d958fed0e5f8d4a054849147b58a89463 Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 08:45:51 -0700 Subject: [PATCH 05/40] setup cleanup * moved most setup logic into `App.setup` * split `App.setup` into smaller steps * removed call to `log.Fatal`, instead bubble up the resulting error --- hsapp/app.go | 40 ++--------------- hsapp/setup.go | 116 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 104 insertions(+), 52 deletions(-) diff --git a/hsapp/app.go b/hsapp/app.go index af1dd1ed..c971610a 100644 --- a/hsapp/app.go +++ b/hsapp/app.go @@ -7,29 +7,22 @@ import ( "sync" "time" - "github.com/OpenDiablo2/HellSpawner/hswindow/hstoolwindow/hsconsole" - - "github.com/OpenDiablo2/HellSpawner/abysswrapper" - - "github.com/OpenDiablo2/HellSpawner/hsinput" - - "github.com/OpenDiablo2/HellSpawner/hscommon/hsfiletypes" - g "github.com/ianling/giu" "github.com/ianling/imgui-go" "github.com/OpenDiablo2/dialog" - "github.com/faiface/beep" - "github.com/faiface/beep/speaker" "github.com/go-gl/glfw/v3.3/glfw" + "github.com/OpenDiablo2/HellSpawner/abysswrapper" "github.com/OpenDiablo2/HellSpawner/hscommon" + "github.com/OpenDiablo2/HellSpawner/hscommon/hsfiletypes" "github.com/OpenDiablo2/HellSpawner/hscommon/hsproject" - "github.com/OpenDiablo2/HellSpawner/hscommon/hsutil" "github.com/OpenDiablo2/HellSpawner/hsconfig" + "github.com/OpenDiablo2/HellSpawner/hsinput" "github.com/OpenDiablo2/HellSpawner/hswindow/hsdialog/hsaboutdialog" "github.com/OpenDiablo2/HellSpawner/hswindow/hsdialog/hspreferencesdialog" "github.com/OpenDiablo2/HellSpawner/hswindow/hsdialog/hsprojectpropertiesdialog" + "github.com/OpenDiablo2/HellSpawner/hswindow/hstoolwindow/hsconsole" "github.com/OpenDiablo2/HellSpawner/hswindow/hstoolwindow/hsmpqexplorer" "github.com/OpenDiablo2/HellSpawner/hswindow/hstoolwindow/hsprojectexplorer" ) @@ -127,31 +120,6 @@ func Create() (*App, error) { func (a *App) Run() { var err error - color := a.config.BGColor - if bg := uint32(*a.Flags.bgColor); bg != hsconfig.DefaultBGColor { - color = hsutil.Color(bg) - } - - a.masterWindow = g.NewMasterWindow(baseWindowTitle, baseWindowW, baseWindowH, 0, a.setupFonts) - a.masterWindow.SetBgColor(color) - - sampleRate := beep.SampleRate(samplesPerSecond) - bufferSize := sampleRate.N(sampleDuration) - - if err = speaker.Init(sampleRate, bufferSize); err != nil { - log.Fatal(err) - } - - dialog.Init() - - // initialize auto-save timer - go func() { - time.Sleep(autoSaveTimer * time.Second) - a.Save() - }() - - a.TextureLoader.ProcessTextureLoadRequests() - defer a.Quit() // force-close and save everything (in case of crash) // setting up the logging here, as opposed to inside of app.setup(), diff --git a/hsapp/setup.go b/hsapp/setup.go index 91ed34e1..8eba4cb8 100644 --- a/hsapp/setup.go +++ b/hsapp/setup.go @@ -5,21 +5,25 @@ import ( "log" "time" - "github.com/OpenDiablo2/HellSpawner/hsassets" - "github.com/OpenDiablo2/HellSpawner/hswindow/hstoolwindow/hsconsole" + "github.com/OpenDiablo2/dialog" - "github.com/OpenDiablo2/HellSpawner/hswindow/hseditor/hsds1editor" + "github.com/faiface/beep" + "github.com/faiface/beep/speaker" g "github.com/ianling/giu" "github.com/ianling/imgui-go" + "github.com/OpenDiablo2/HellSpawner/hswindow/hseditor/hsds1editor" "github.com/OpenDiablo2/HellSpawner/hswindow/hseditor/hsdt1editor" "github.com/OpenDiablo2/HellSpawner/hswindow/hseditor/hsfonttableeditor" "github.com/OpenDiablo2/HellSpawner/hswindow/hseditor/hspalettemapeditor" "github.com/OpenDiablo2/HellSpawner/hswindow/hseditor/hsstringtableeditor" + "github.com/OpenDiablo2/HellSpawner/hsassets" "github.com/OpenDiablo2/HellSpawner/hscommon/hsenum" "github.com/OpenDiablo2/HellSpawner/hscommon/hsfiletypes" + "github.com/OpenDiablo2/HellSpawner/hscommon/hsutil" + "github.com/OpenDiablo2/HellSpawner/hsconfig" "github.com/OpenDiablo2/HellSpawner/hswindow/hsdialog/hsaboutdialog" "github.com/OpenDiablo2/HellSpawner/hswindow/hsdialog/hspreferencesdialog" "github.com/OpenDiablo2/HellSpawner/hswindow/hsdialog/hsprojectpropertiesdialog" @@ -31,14 +35,64 @@ import ( "github.com/OpenDiablo2/HellSpawner/hswindow/hseditor/hspaletteeditor" "github.com/OpenDiablo2/HellSpawner/hswindow/hseditor/hssoundeditor" "github.com/OpenDiablo2/HellSpawner/hswindow/hseditor/hstexteditor" + "github.com/OpenDiablo2/HellSpawner/hswindow/hstoolwindow/hsconsole" "github.com/OpenDiablo2/HellSpawner/hswindow/hstoolwindow/hsmpqexplorer" "github.com/OpenDiablo2/HellSpawner/hswindow/hstoolwindow/hsprojectexplorer" ) -func (a *App) setup() error { - var err error +func (a *App) setup() (err error) { + dialog.Init() + + a.setupConsole() + a.setupMasterWindow() + a.setupAutoSave() + a.registerGlobalKeyboardShortcuts() + a.registerEditors() + + err = a.setupAudio() + if err != nil { + return err + } + + err = a.setupMainMpqExplorer() + if err != nil { + return err + } + + err = a.setupProjectExplorer() + if err != nil { + return err + } + + err = a.setupDialogs() + if err != nil { + return err + } + + // we may have tried loading some textures already... + a.TextureLoader.ProcessTextureLoadRequests() + + return nil +} + +func (a *App) setupMasterWindow() { + color := a.config.BGColor + if bg := uint32(*a.Flags.bgColor); bg != hsconfig.DefaultBGColor { + color = hsutil.Color(bg) + } + + a.masterWindow = g.NewMasterWindow(baseWindowTitle, baseWindowW, baseWindowH, 0, a.setupFonts) + a.masterWindow.SetBgColor(color) +} + +func (a *App) setupAutoSave() { + go func() { + time.Sleep(autoSaveTimer * time.Second) + a.Save() + }() +} - // Register the editors +func (a *App) registerEditors() { a.editorConstructors[hsfiletypes.FileTypeText] = hstexteditor.Create a.editorConstructors[hsfiletypes.FileTypeAudio] = hssoundeditor.Create a.editorConstructors[hsfiletypes.FileTypePalette] = hspaletteeditor.Create @@ -52,18 +106,45 @@ func (a *App) setup() error { a.editorConstructors[hsfiletypes.FileTypeTBLStringTable] = hsstringtableeditor.Create a.editorConstructors[hsfiletypes.FileTypeTBLFontTable] = hsfonttableeditor.Create a.editorConstructors[hsfiletypes.FileTypeDS1] = hsds1editor.Create +} - // Register the tool windows - if a.mpqExplorer, err = hsmpqexplorer.Create(a.openEditor, a.config, mpqExplorerDefaultX, mpqExplorerDefaultY); err != nil { +func (a *App) setupMainMpqExplorer() error { + window, err := hsmpqexplorer.Create(a.openEditor, a.config, mpqExplorerDefaultX, mpqExplorerDefaultY) + if err != nil { return fmt.Errorf("error creating a MPQ explorer: %w", err) } - if a.projectExplorer, err = hsprojectexplorer.Create(a.TextureLoader, - a.openEditor, projectExplorerDefaultX, - projectExplorerDefaultY); err != nil { + a.mpqExplorer = window + + return nil +} + +func (a *App) setupProjectExplorer() error { + x, y := float32(projectExplorerDefaultX), float32(projectExplorerDefaultY) + window, err := hsprojectexplorer.Create(a.TextureLoader, + a.openEditor, x, y) + + if err != nil { return fmt.Errorf("error creating a project explorer: %w", err) } + a.projectExplorer = window + + return nil +} + +func (a *App) setupAudio() error { + sampleRate := beep.SampleRate(samplesPerSecond) + bufferSize := sampleRate.N(sampleDuration) + + if err := speaker.Init(sampleRate, bufferSize); err != nil { + return fmt.Errorf("could not initialize, %w", err) + } + + return nil +} + +func (a *App) setupConsole() { a.console = hsconsole.Create(a.fontFixed, consoleDefaultX, consoleDefaultY, a.logFile) log.SetFlags(log.Lshortfile) @@ -71,19 +152,22 @@ func (a *App) setup() error { t := time.Now() y, m, d := t.Date() - log.Printf(logFileSeparator, fmt.Sprintf("%d-%d-%d, %d:%d:%d", y, m, d, t.Hour(), t.Minute(), t.Second())) + line := fmt.Sprintf("%d-%d-%d, %d:%d:%d", y, m, d, t.Hour(), t.Minute(), t.Second()) + log.Printf(logFileSeparator, line) +} + +func (a *App) setupDialogs() error { // Register the dialogs - if a.aboutDialog, err = hsaboutdialog.Create(a.TextureLoader, a.diabloRegularFont, a.diabloBoldFont, a.fontFixedSmall); err != nil { + about, err := hsaboutdialog.Create(a.TextureLoader, a.diabloRegularFont, a.diabloBoldFont, a.fontFixedSmall) + if err != nil { return fmt.Errorf("error creating an about dialog: %w", err) } + a.aboutDialog = about a.projectPropertiesDialog = hsprojectpropertiesdialog.Create(a.TextureLoader, a.onProjectPropertiesChanged) a.preferencesDialog = hspreferencesdialog.Create(a.onPreferencesChanged, a.masterWindow.SetBgColor) - // Set up keyboard shortcuts - a.registerGlobalKeyboardShortcuts() - return nil } From a542d7eb537ab660a9772c2c038ffd72b6fe5c4f Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 09:51:26 -0700 Subject: [PATCH 06/40] hsapp.App now bubble up errors --- hsapp/app.go | 8 ++++---- main.go | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/hsapp/app.go b/hsapp/app.go index c971610a..cea6ec5e 100644 --- a/hsapp/app.go +++ b/hsapp/app.go @@ -117,9 +117,7 @@ func Create() (*App, error) { } // Run runs an app instance -func (a *App) Run() { - var err error - +func (a *App) Run() (err error) { defer a.Quit() // force-close and save everything (in case of crash) // setting up the logging here, as opposed to inside of app.setup(), @@ -144,7 +142,7 @@ func (a *App) Run() { } if err := a.setup(); err != nil { - log.Panic(err) + return err } if a.config.OpenMostRecentOnStartup && len(a.config.RecentProjects) > 0 { @@ -153,6 +151,8 @@ func (a *App) Run() { a.masterWindow.SetInputCallback(a.InputManager.HandleInput) a.masterWindow.Run(a.render) + + return nil } func (a *App) render() { diff --git a/main.go b/main.go index 4b0b5654..95731244 100644 --- a/main.go +++ b/main.go @@ -14,5 +14,7 @@ func main() { log.Fatal(err) } - app.Run() + if err := app.Run(); err != nil { + log.Fatal(err) + } } From 866fa0547d7564c2a2086e75c0a19c6d1bbf9554 Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 10:20:31 -0700 Subject: [PATCH 07/40] App.createEditor now logs errors to console --- hsapp/app.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/hsapp/app.go b/hsapp/app.go index cea6ec5e..49e5f54f 100644 --- a/hsapp/app.go +++ b/hsapp/app.go @@ -1,6 +1,7 @@ package hsapp import ( + "fmt" "log" "os" "path/filepath" @@ -210,34 +211,41 @@ func (a *App) render() { a.TextureLoader.ResumeLoadingTextures() } +func logErr(fmtErr string, err error) { + msg := fmt.Sprintf(fmtErr, err) + + log.Printf(msg) + dialog.Message(msg).Error() +} + func (a *App) createEditor(path *hscommon.PathEntry, state []byte, x, y, w, h float32) { data, err := path.GetFileBytes() if err != nil { - log.Printf("Could not load file: %v", err) - dialog.Message("Could not load file!").Error() + const fmtErr = "Could not load file: %v" + logErr(fmtErr, err) return } fileType, err := hsfiletypes.GetFileTypeFromExtension(filepath.Ext(path.FullPath), &data) if err != nil { - log.Printf("Error reading file type: %v", err) - dialog.Message("No file type is defined for this extension!").Error() + const fmtErr = "Error reading file type: %v" + logErr(fmtErr, err) return } if a.editorConstructors[fileType] == nil { - log.Printf("Error loading editor: %v", err) - dialog.Message("No editor is defined for this file type!").Error() + const fmtErr = "Error opening editor: %v" + logErr(fmtErr, err) return } editor, err := a.editorConstructors[fileType](a.config, a.TextureLoader, path, state, &data, x, y, a.project) if err != nil { - log.Printf("Error creating editor: %v", err) - dialog.Message("Error creating editor: %s", err).Error() + const fmtErr = "Error creating editor: %v" + logErr(fmtErr, err) return } From 98e8c74b2add03b0226acdc75af77fc0241a91db Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 12:30:03 -0700 Subject: [PATCH 08/40] changed default logfile permissions to 0o644 --- hsapp/app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hsapp/app.go b/hsapp/app.go index 49e5f54f..f93c8c6f 100644 --- a/hsapp/app.go +++ b/hsapp/app.go @@ -46,7 +46,7 @@ const ( autoSaveTimer = 120 logFileSeparator = "-----%v-----\n" - logFilePerms = 0o600 + logFilePerms = 0o644 ) const ( From 59f92c5f2fb84e831af96394329922f6c5719aae Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 12:30:57 -0700 Subject: [PATCH 09/40] removed unnecessary double nil-assign in playPuseButtonState.Dispose --- hswidget/custom_widgets.go | 1 - 1 file changed, 1 deletion(-) diff --git a/hswidget/custom_widgets.go b/hswidget/custom_widgets.go index 337cce2c..abf4407b 100644 --- a/hswidget/custom_widgets.go +++ b/hswidget/custom_widgets.go @@ -38,7 +38,6 @@ type playPauseButtonState struct { func (s *playPauseButtonState) Dispose() { s.playTexture = nil - s.playTexture = nil } // PlayPauseButtonWidget represents a play/pause button From c9c769e1804072075727e46a148b50c7cece25af Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 12:50:06 -0700 Subject: [PATCH 10/40] removed duplicate code, simplified PlayPauseButton.Build --- hsapp/app.go | 7 ++-- hswidget/custom_widgets.go | 71 ++++++++++++++++++++------------------ 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/hsapp/app.go b/hsapp/app.go index f93c8c6f..16e68613 100644 --- a/hsapp/app.go +++ b/hsapp/app.go @@ -213,8 +213,7 @@ func (a *App) render() { func logErr(fmtErr string, err error) { msg := fmt.Sprintf(fmtErr, err) - - log.Printf(msg) + log.Print(msg) dialog.Message(msg).Error() } @@ -222,6 +221,7 @@ func (a *App) createEditor(path *hscommon.PathEntry, state []byte, x, y, w, h fl data, err := path.GetFileBytes() if err != nil { const fmtErr = "Could not load file: %v" + logErr(fmtErr, err) return @@ -230,6 +230,7 @@ func (a *App) createEditor(path *hscommon.PathEntry, state []byte, x, y, w, h fl fileType, err := hsfiletypes.GetFileTypeFromExtension(filepath.Ext(path.FullPath), &data) if err != nil { const fmtErr = "Error reading file type: %v" + logErr(fmtErr, err) return @@ -237,6 +238,7 @@ func (a *App) createEditor(path *hscommon.PathEntry, state []byte, x, y, w, h fl if a.editorConstructors[fileType] == nil { const fmtErr = "Error opening editor: %v" + logErr(fmtErr, err) return @@ -245,6 +247,7 @@ func (a *App) createEditor(path *hscommon.PathEntry, state []byte, x, y, w, h fl editor, err := a.editorConstructors[fileType](a.config, a.TextureLoader, path, state, &data, x, y, a.project) if err != nil { const fmtErr = "Error creating editor: %v" + logErr(fmtErr, err) return diff --git a/hswidget/custom_widgets.go b/hswidget/custom_widgets.go index abf4407b..7565bda6 100644 --- a/hswidget/custom_widgets.go +++ b/hswidget/custom_widgets.go @@ -109,41 +109,46 @@ func (p *PlayPauseButtonWidget) Build() { }) giu.Context.SetState(stateID, state) - } else { - imgState := state.(*playPauseButtonState) - if !*p.isPlaying { - widget = MakeImageButton( - p.id+"Play", - int(p.width), int(p.height), - imgState.playTexture, - func() { - *p.isPlaying = true - if cb := p.onChange; cb != nil { - cb() - } - if cb := p.onPlayClicked; cb != nil { - cb() - } - }, - ) - } else { - widget = MakeImageButton( - p.id+"Pause", - int(p.width), int(p.height), - imgState.pauseTexture, - func() { - *p.isPlaying = false - if cb := p.onChange; cb != nil { - cb() - } - if cb := p.onPauseClicked; cb != nil { - cb() - } - }, - ) + + widget.Build() + + return + } + + imgState := state.(*playPauseButtonState) + + w, h := int(p.width), int(p.height) + + var id string + + var texture *giu.Texture + + var callback func() // callback + + setIsPlaying := func(b bool) { + *p.isPlaying = b + + if cb := p.onChange; cb != nil { + cb() + } + + if cb := p.onPlayClicked; cb != nil { + cb() } } + if !*p.isPlaying { + id = p.id + "Play" + texture = imgState.playTexture + callback = func() { setIsPlaying(true) } + } else { + id = p.id + "Pause" + texture = imgState.pauseTexture + callback = func() { setIsPlaying(false) } + } + + widget = MakeImageButton(id, w, h, texture, callback) + widget.Build() } @@ -188,7 +193,7 @@ func MakeInputInt(id string, width int32, output interface{}, optionalCB func()) // MakeCheckboxFromByte creates a checkbox using a byte as input/output func MakeCheckboxFromByte(id string, value *byte) *giu.CheckboxWidget { - v := (*value > 0) + v := *value > 0 return giu.Checkbox(id, &v).OnChange(func() { if v { From aaea8fd5b66ee66b61232087d038171ffdd955f8 Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 14:10:40 -0700 Subject: [PATCH 11/40] updated hsapp doc string to make sense --- hsapp/doc.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hsapp/doc.go b/hsapp/doc.go index 9706135c..242272f3 100644 --- a/hsapp/doc.go +++ b/hsapp/doc.go @@ -1,3 +1,2 @@ -// Package hsapp contains HellSpawner's -// app data +// Package hsapp contains HellSpawner's high-level application logic package hsapp From dd97ba69203d54c7ec49198d853360ed44bcbf00 Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 15:32:33 -0700 Subject: [PATCH 12/40] cleaned up app flag parsing * separated parsing logic for individual flags * changed bgColor flag type to string so that description was human-readable * bgColor flag supports both RGB and RGBA format, with or without "0x" prefix --- hsapp/flags.go | 64 +++++++++++++++++++++++++++++++++++++++++++------- hsapp/setup.go | 43 ++++++++++++++++++++++++++++----- 2 files changed, 93 insertions(+), 14 deletions(-) diff --git a/hsapp/flags.go b/hsapp/flags.go index a8679453..d7662efa 100644 --- a/hsapp/flags.go +++ b/hsapp/flags.go @@ -11,26 +11,74 @@ import ( // Flags specifies app flags type Flags struct { optionalConfigPath *string - bgColor *uint + bgColor *string logFile *string } func (a *App) parseArgs() { - configDescr := fmt.Sprintf("specify a custom config path. Default is: %s", hsconfig.GetConfigPath()) - a.Flags.optionalConfigPath = flag.String("config", "", configDescr) - a.Flags.bgColor = flag.Uint("bgColor", hsconfig.DefaultBGColor, "custom background color") - a.Flags.logFile = flag.String("log", "", "path to the output log file") - showHelp := flag.Bool("h", false, "Show help") + a.parseConfigArgs() + a.parseLogFileArgs() + a.parseBackgroundColorArgs() + + // help args need to be parsed last, so that all other args + // can be parsed before a possible os.Exit() invoked by `-h` or `--help` + // + // otherwise, other flags will not be printed in usage string! + a.parseHelpArgs() +} + +func (a *App) parseHelpArgs() { + const ( + short = "h" + long = "help" + fmtUsage = "usage: %s []\n\nFlags:\n" + ) + + // we will use a single variable for both short and long flags + var showHelp bool + + flag.BoolVar(&showHelp, long, false, "Show help") + flag.BoolVar(&showHelp, short, false, "Show help (shorthand)") flag.Usage = func() { - fmt.Printf("usage: %s []\n\nFlags:\n", os.Args[0]) + fmt.Printf(fmtUsage, os.Args[0]) flag.PrintDefaults() } flag.Parse() - if *showHelp { + if showHelp { flag.Usage() os.Exit(0) } } + +func (a *App) parseConfigArgs() { + const ( + name = "config" + defaultValue = "" + fmtDesc = "specify a custom config path.\nDefault is:\n\t%s" + ) + + desc := fmt.Sprintf(fmtDesc, hsconfig.GetConfigPath()) + a.Flags.optionalConfigPath = flag.String(name, defaultValue, desc) +} + +func (a *App) parseBackgroundColorArgs() { + const ( + name = "bgColor" + desc = "custom background color." + ) + + defaultValue := fmt.Sprintf("0x%x", hsconfig.DefaultBGColor) + a.Flags.bgColor = flag.String(name, defaultValue, desc) +} + +func (a *App) parseLogFileArgs() { + const ( + name = "log" + desc = "path to the output log file." + ) + + a.Flags.logFile = flag.String(name, "", desc) +} diff --git a/hsapp/setup.go b/hsapp/setup.go index 8eba4cb8..1fdd863a 100644 --- a/hsapp/setup.go +++ b/hsapp/setup.go @@ -2,7 +2,9 @@ package hsapp import ( "fmt" + "image/color" "log" + "strconv" "time" "github.com/OpenDiablo2/dialog" @@ -23,7 +25,6 @@ import ( "github.com/OpenDiablo2/HellSpawner/hscommon/hsenum" "github.com/OpenDiablo2/HellSpawner/hscommon/hsfiletypes" "github.com/OpenDiablo2/HellSpawner/hscommon/hsutil" - "github.com/OpenDiablo2/HellSpawner/hsconfig" "github.com/OpenDiablo2/HellSpawner/hswindow/hsdialog/hsaboutdialog" "github.com/OpenDiablo2/HellSpawner/hswindow/hsdialog/hspreferencesdialog" "github.com/OpenDiablo2/HellSpawner/hswindow/hsdialog/hsprojectpropertiesdialog" @@ -76,13 +77,43 @@ func (a *App) setup() (err error) { } func (a *App) setupMasterWindow() { - color := a.config.BGColor - if bg := uint32(*a.Flags.bgColor); bg != hsconfig.DefaultBGColor { - color = hsutil.Color(bg) + a.masterWindow = g.NewMasterWindow(baseWindowTitle, baseWindowW, baseWindowH, 0, a.setupFonts) + + bgColor := a.determineBackgroundColor() + a.masterWindow.SetBgColor(bgColor) +} + +func (a *App) determineBackgroundColor() color.RGBA { + const bitSize = 64 + + result := a.config.BGColor + + strBytes := []byte(*a.Flags.bgColor) + numChars := len(strBytes) + includesBase := strBytes[1] == 'x' + + base := 16 + if includesBase { + base = 0 } - a.masterWindow = g.NewMasterWindow(baseWindowTitle, baseWindowW, baseWindowH, 0, a.setupFonts) - a.masterWindow.SetBgColor(color) + includesAlpha := false + if includesBase && numChars == len("0xRRGGBBAA") { + includesAlpha = true + } else if !includesBase && numChars == len("RRGGBBAA") { + includesAlpha = true + } + + bg, err := strconv.ParseInt(*a.Flags.bgColor, base, bitSize) + if err == nil { + if !includesAlpha { + bg <<= 8 + } + + result = hsutil.Color(uint32(bg)) + } + + return result } func (a *App) setupAutoSave() { From 6aa785a75b4dab24237fc112b9168eef3c648237 Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 15:43:54 -0700 Subject: [PATCH 13/40] replaced unnecessary var declaration, bgColor bugfix --- hsapp/setup.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hsapp/setup.go b/hsapp/setup.go index 1fdd863a..a2e07790 100644 --- a/hsapp/setup.go +++ b/hsapp/setup.go @@ -98,9 +98,9 @@ func (a *App) determineBackgroundColor() color.RGBA { } includesAlpha := false - if includesBase && numChars == len("0xRRGGBBAA") { + if includesBase && numChars >= len("0xRGGBBAA") { includesAlpha = true - } else if !includesBase && numChars == len("RRGGBBAA") { + } else if !includesBase && numChars >= len("RGGBBAA") { includesAlpha = true } @@ -212,7 +212,7 @@ func (a *App) setupFonts() { builder.AddRanges(fonts.GlyphRangesDefault()) - var font []byte = hsassets.FontNotoSansRegular + font := hsassets.FontNotoSansRegular switch a.config.Locale { // glyphs supported by default From 6376e46b88cdeb35e2dadd08a0fc0c65a505626d Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 17:13:46 -0700 Subject: [PATCH 14/40] added helper functions to clean up menu bar code --- hsapp/menubar.go | 124 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 94 insertions(+), 30 deletions(-) diff --git a/hsapp/menubar.go b/hsapp/menubar.go index 661ed2db..943a7f4a 100644 --- a/hsapp/menubar.go +++ b/hsapp/menubar.go @@ -23,6 +23,69 @@ const ( supportURL = "https://www.patreon.com/bePatron?u=37261055" ) +func (a *App) fileMenu() *g.MenuWidget { + m := menu("MainMenu", "File") + + mNew := menu("MainMenuFile", "New") + mNewProject := menuItem("MainMenuFileNew", "Project...", "Ctrl+Shift+N") + mNewProject.OnClick(a.onNewProjectClicked) + + mOpen := menu("MainMenuFile", "Open") + mOpenProject := menuItem("MainMenuFileOpen", "Project...", "Ctrl+Shift+O") + mOpenProject.OnClick(a.onOpenProjectClicked) + + mSaveProject := menuItem("MainMenuFileSaveProject", "Save Project", "Ctrl+S") + mSaveProject.OnClick(a.Save) + + mPreferences := menuItem("MainMenuFilePreferences", "Preferences...", "Alt+P") + mPreferences.OnClick(a.onFilePreferencesClicked) + + mExit := menuItem("MainMenuFile", "Exit", "Alt+Q") + fnExit := func() { + a.Quit() + os.Exit(0) + } + + m.Layout( + mNew.Layout( + mNewProject, + ), + mOpen.Layout( + mOpenProject, + ), + a.openRecentProjectMenu(), + mSaveProject, + g.Separator(), + mPreferences, + g.Separator(), + mExit.OnClick(fnExit), + ) + + return m +} + +func (a *App) openRecentProjectMenu() *g.MenuWidget { + m := menu("MainMenuFileOpenRecent", "Recent Project...") + + fnRecent := func() { + if len(a.config.RecentProjects) == 0 { + g.MenuItem("No recent projects...##MainMenuOpenRecentItems").Build() + return + } + + for idx := range a.config.RecentProjects { + projectName := a.config.RecentProjects[idx] + g.MenuItem(fmt.Sprintf("%s##MainMenuOpenRecent_%d", projectName, idx)).OnClick(func() { + a.loadProjectFromFile(projectName) + }).Build() + } + } + + m.Layout(g.Custom(fnRecent)) + + return m +} + func (a *App) renderMainMenuBar() { var runAbyssEngineLabel string @@ -36,36 +99,7 @@ func (a *App) renderMainMenuBar() { } menuLayout := g.Layout{ - g.Menu("File##MainMenuFile").Layout(g.Layout{ - g.Menu("New##MainMenuFileNew").Layout(g.Layout{ - g.MenuItem("Project...\t\tCtrl+Shift+N##MainMenuFileNewProject").OnClick(a.onNewProjectClicked), - }), - g.Menu("Open##MainMenuFileOpen").Layout(g.Layout{ - g.MenuItem("Project...\t\tCtrl+O##MainMenuFileOpenProject").OnClick(a.onOpenProjectClicked), - }), - g.MenuItem("Save\t\t\t\t\t\tCtrl+S##MainMenuFileSaveProject").OnClick(a.Save), - g.Menu("Open Recent##MainMenuOpenRecent").Layout(g.Layout{ - g.Custom(func() { - if len(a.config.RecentProjects) == 0 { - g.MenuItem("No recent projects...##MainMenuOpenRecentItems").Build() - return - } - for idx := range a.config.RecentProjects { - projectName := a.config.RecentProjects[idx] - g.MenuItem(fmt.Sprintf("%s##MainMenuOpenRecent_%d", projectName, idx)).OnClick(func() { - a.loadProjectFromFile(projectName) - }).Build() - } - }), - }), - g.Separator(), - g.MenuItem("Preferences...\t\tAlt+P##MainMenuFilePreferences").OnClick(a.onFilePreferencesClicked), - g.Separator(), - g.MenuItem("Exit\t\t\t\t\t\t Alt+Q##MainMenuFileExit").OnClick(func() { - a.Quit() - os.Exit(0) - }), - }), + a.fileMenu(), g.Menu("View##MainMenuView").Layout(a.buildViewMenu()), g.Menu("Project##MainMenuProject").Layout(g.Layout{ g.MenuItem(runAbyssEngineLabel + "##MainMenuProjectRun"). @@ -254,3 +288,33 @@ func (a *App) onReportBugClicked() { log.Fatal(err) } } + +func makeMenuID(name, group, shortcut string) string { + const ( + sep = "##" + fmt2 = "%v%v%v%v" + fmt3 = "%v\t(%v)%v%v%v" + ) + + if len(shortcut) > 0 { + return fmt.Sprintf(fmt3, name, shortcut, sep, group, name) + } + + return fmt.Sprintf(fmt2, name, sep, group, name) +} + +func menuID(group, name string) string { + return makeMenuID(name, group, "") +} + +func itemID(group, name, shortcut string) string { + return makeMenuID(name, group, shortcut) +} + +func menu(group, name string) *g.MenuWidget { + return g.Menu(menuID(group, name)) +} + +func menuItem(group, name, shortcut string) *g.MenuItemWidget { + return g.MenuItem(itemID(group, name, shortcut)) +} From 0e0f59c183f329bcada4ed7005bb4652c1a01596 Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 17:19:37 -0700 Subject: [PATCH 15/40] fix typo in hsasset doc string --- hsassets/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hsassets/doc.go b/hsassets/doc.go index a364c83a..33953629 100644 --- a/hsassets/doc.go +++ b/hsassets/doc.go @@ -1,2 +1,2 @@ -// Package hsassets contanins project assets embeds +// Package hsassets contains project assets embeds package hsassets From 6b1a12520c78648f971fcc91bbd193ab8ca3ac1f Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 17:24:37 -0700 Subject: [PATCH 16/40] adding dov.go for hsfiletypes, fixing typo in doc string --- hscommon/hsfiletypes/doc.go | 2 ++ hscommon/hsfiletypes/filetype.go | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 hscommon/hsfiletypes/doc.go diff --git a/hscommon/hsfiletypes/doc.go b/hscommon/hsfiletypes/doc.go new file mode 100644 index 00000000..b7149efc --- /dev/null +++ b/hscommon/hsfiletypes/doc.go @@ -0,0 +1,2 @@ +// Package hsfiletypes provides utilities for determining the type of a file. +package hsfiletypes diff --git a/hscommon/hsfiletypes/filetype.go b/hscommon/hsfiletypes/filetype.go index 2d39f0f3..77061531 100644 --- a/hscommon/hsfiletypes/filetype.go +++ b/hscommon/hsfiletypes/filetype.go @@ -1,4 +1,3 @@ -// Package hsfiletypes determinates file types package hsfiletypes import ( From 7f620689261fdcb9a776e4d2728d5921a1425fe8 Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 18:02:23 -0700 Subject: [PATCH 17/40] un-exporting string alias --- hscommon/hsnode/node.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/hscommon/hsnode/node.go b/hscommon/hsnode/node.go index c8ade483..f4ae3eb5 100644 --- a/hscommon/hsnode/node.go +++ b/hscommon/hsnode/node.go @@ -8,11 +8,10 @@ const ( sep = "/" ) -// Name represents node's name -type Name = string +type name = string // Children represents represents subnodes -type Children map[Name]*Node +type Children map[name]*Node // Path represents node path type Path = string @@ -20,14 +19,14 @@ type Path = string // Node represents node type Node struct { Parent *Node - Name + name Children } // NewNode creates a new node func NewNode(name string) *Node { n := &Node{ - Name: name, + name: name, Children: make(Children), } @@ -36,7 +35,7 @@ func NewNode(name string) *Node { // String represents node's name func (n *Node) String() string { - return n.Name + return n.name } // Insert inserts a new path to node @@ -58,7 +57,7 @@ func (n *Node) Insert(p Path) *Node { child.Insert(remainder).SetParent(n) } else { - n.Name = p + n.name = p } return n From 9f214d405791de9bd17052efd44d25b53bc6643b Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 18:02:53 -0700 Subject: [PATCH 18/40] adding a couple of more comments for clarity --- hsapp/flags.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hsapp/flags.go b/hsapp/flags.go index d7662efa..3ca6b769 100644 --- a/hsapp/flags.go +++ b/hsapp/flags.go @@ -15,6 +15,7 @@ type Flags struct { logFile *string } +// parse all of the command line args func (a *App) parseArgs() { a.parseConfigArgs() a.parseLogFileArgs() @@ -49,7 +50,7 @@ func (a *App) parseHelpArgs() { if showHelp { flag.Usage() - os.Exit(0) + os.Exit(0) // this is dangerous, forces us to parse the help flags last } } From 932f25a8b26b2baa24ee226ec5b2b8ff55fcf389 Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 19:17:22 -0700 Subject: [PATCH 19/40] renamed EditorState.EditorState * is now called EditorState.Encoded * other functions which used this field have been renamed to be more coherent --- hsapp/app_state.go | 2 +- hscommon/hsstate/editor_state.go | 6 +++--- hswindow/hseditor/editor.go | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hsapp/app_state.go b/hsapp/app_state.go index 2690a2a8..6ae561b9 100644 --- a/hsapp/app_state.go +++ b/hsapp/app_state.go @@ -62,6 +62,6 @@ func (a *App) RestoreAppState(state hsstate.AppState) { continue } - go a.createEditor(&path, editorState.EditorState, editorState.PosX, editorState.PosY, editorState.Width, editorState.Height) + go a.createEditor(&path, editorState.Encoded, editorState.PosX, editorState.PosY, editorState.Width, editorState.Height) } } diff --git a/hscommon/hsstate/editor_state.go b/hscommon/hsstate/editor_state.go index 059a78ae..3b4329f7 100644 --- a/hscommon/hsstate/editor_state.go +++ b/hscommon/hsstate/editor_state.go @@ -1,8 +1,8 @@ package hsstate -// EditorState holds information about all of the open editors. +// EditorState holds information about the state of an open editor type EditorState struct { WindowState - Path []byte `json:"path"` // this gets exported as raw JSON to prevent an import loop - EditorState []byte `json:"state"` + Path []byte `json:"path"` // this gets exported as raw JSON to prevent an import loop + Encoded []byte `json:"state"` } diff --git a/hswindow/hseditor/editor.go b/hswindow/hseditor/editor.go index 6032d994..4ff918d1 100644 --- a/hswindow/hseditor/editor.go +++ b/hswindow/hseditor/editor.go @@ -41,7 +41,7 @@ func (e *Editor) State() hsstate.EditorState { result := hsstate.EditorState{ WindowState: e.Window.State(), Path: path, - EditorState: e.GetStateData(), + Encoded: e.EncodeState(), } return result @@ -121,8 +121,8 @@ func generateWindowTitle(path *hscommon.PathEntry) string { return path.Name + "##" + path.GetUniqueID() } -// GetStateData returns widget's state (unique for each editor type) in byte slice format -func (e *Editor) GetStateData() []byte { +// EncodeState returns widget's state (unique for each editor type) in byte slice format +func (e *Editor) EncodeState() []byte { id := fmt.Sprintf("widget_%s", e.Path.GetUniqueID()) if s := giu.Context.GetState(id); s != nil { From bf8c46479bf29120a6637555d628ee90d82fc095 Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 19:18:41 -0700 Subject: [PATCH 20/40] reworded hsproject doc string to be more coherent --- hscommon/hsproject/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hscommon/hsproject/doc.go b/hscommon/hsproject/doc.go index ee36023a..039b3b9c 100644 --- a/hscommon/hsproject/doc.go +++ b/hscommon/hsproject/doc.go @@ -1,2 +1,2 @@ -// Package hsproject contains common data for HellSpawner's projects +// Package hsproject contains Hellspawner's Project implementation package hsproject From 86560a7832596128500545925e138f1aa0b6963f Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 19:33:07 -0700 Subject: [PATCH 21/40] changed fileCreator to fileMarshaller --- hscommon/hsproject/{file_creator.go => marshaler.go} | 4 ++-- hscommon/hsproject/project.go | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) rename hscommon/hsproject/{file_creator.go => marshaler.go} (92%) diff --git a/hscommon/hsproject/file_creator.go b/hscommon/hsproject/marshaler.go similarity index 92% rename from hscommon/hsproject/file_creator.go rename to hscommon/hsproject/marshaler.go index 12b5ac19..8aed56f6 100644 --- a/hscommon/hsproject/file_creator.go +++ b/hscommon/hsproject/marshaler.go @@ -13,11 +13,11 @@ import ( "github.com/OpenDiablo2/HellSpawner/hscommon/hsfiletypes" ) -type fileCreator interface { +type marshaler interface { Marshal() []byte } -func getFileManager(fileType hsfiletypes.FileType) fileCreator { +func getMarshallerByType(fileType hsfiletypes.FileType) marshaler { switch fileType { case hsfiletypes.FileTypeTBLFontTable: return &d2font.Font{} diff --git a/hscommon/hsproject/project.go b/hscommon/hsproject/project.go index 6df4d81d..15c1a2b9 100644 --- a/hscommon/hsproject/project.go +++ b/hscommon/hsproject/project.go @@ -315,13 +315,13 @@ func (p *Project) CreateNewFile(fileType hsfiletypes.FileType, path *hscommon.Pa log.Fatalf("failed to save font: %s", err) } default: - manager := getFileManager(fileType) - if manager == nil { - return + m := getMarshallerByType(fileType) + if m == nil { + return fmt.Errorf("no marshaller for file %s", fileName) } - if err := ioutil.WriteFile(fileName, manager.Marshal(), os.FileMode(newFileMode)); err != nil { - log.Fatalf("cannot write to file %s: %v", fileName, err) + if err = ioutil.WriteFile(fileName, m.Marshal(), os.FileMode(newFileMode)); err != nil { + return fmt.Errorf("cannot write to file %s: %w", fileName, err) } } From 9e4f7662a63240a5130dff509657c1b819a0d124 Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 19:34:56 -0700 Subject: [PATCH 22/40] bubbled errors up where i could in hsproject * affects hsapp, hsproject, and hstoolwindow --- hsapp/app.go | 5 +- hscommon/hsproject/mpq.go | 3 +- hscommon/hsproject/project.go | 122 +++++++++--------- .../hsprojectexplorer/projectexplorer.go | 52 ++++++-- 4 files changed, 109 insertions(+), 73 deletions(-) diff --git a/hsapp/app.go b/hsapp/app.go index 16e68613..9bf6d8cc 100644 --- a/hsapp/app.go +++ b/hsapp/app.go @@ -355,7 +355,10 @@ func (a *App) onPreferencesChanged(config *hsconfig.Config) { } func (a *App) reloadAuxiliaryMPQs() { - a.project.ReloadAuxiliaryMPQs(a.config) + if err := a.project.ReloadAuxiliaryMPQs(a.config); err != nil { + dialog.Message(err.Error()).Error() + } + a.mpqExplorer.Reset() } diff --git a/hscommon/hsproject/mpq.go b/hscommon/hsproject/mpq.go index 23f0c803..91ad830d 100644 --- a/hscommon/hsproject/mpq.go +++ b/hscommon/hsproject/mpq.go @@ -9,11 +9,10 @@ import ( "path/filepath" "strings" + "github.com/OpenDiablo2/HellSpawner/hscommon" "github.com/OpenDiablo2/HellSpawner/hsconfig" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" - - "github.com/OpenDiablo2/HellSpawner/hscommon" ) // GetMPQFileNodes returns mpq's node diff --git a/hscommon/hsproject/project.go b/hscommon/hsproject/project.go index 15c1a2b9..b26b7306 100644 --- a/hscommon/hsproject/project.go +++ b/hscommon/hsproject/project.go @@ -2,9 +2,9 @@ package hsproject import ( "encoding/json" + "errors" "fmt" "io/ioutil" - "log" "os" "path/filepath" "strings" @@ -26,9 +26,9 @@ const ( ) const ( - newFileMode = 0o644 - newDirMode = 0o755 - maxProjectsCount = 100 + newFileMode = 0o644 + newDirMode = 0o755 + maxNewFileAttempts = 100 ) // Project represents HellSpawner's project @@ -154,13 +154,13 @@ func (p *Project) ensureProjectPaths() error { } // GetFileStructure returns project's file structure -func (p *Project) GetFileStructure() *hscommon.PathEntry { +func (p *Project) GetFileStructure() (*hscommon.PathEntry, error) { if p.pathEntryCache != nil { - return p.pathEntryCache + return p.pathEntryCache, nil } if err := p.ensureProjectPaths(); err != nil { - log.Fatal(err) + return nil, err } result := &hscommon.PathEntry{ @@ -172,17 +172,17 @@ func (p *Project) GetFileStructure() *hscommon.PathEntry { } result.FullPath = filepath.Join(filepath.Dir(p.filePath), "content") - p.getFileNodes(result.FullPath, result) + err := p.getFileNodes(result.FullPath, result) p.pathEntryCache = result - return result + return result, err } -func (p *Project) getFileNodes(path string, entry *hscommon.PathEntry) { +func (p *Project) getFileNodes(path string, entry *hscommon.PathEntry) error { files, err := ioutil.ReadDir(path) if err != nil { - log.Fatal(err) + return fmt.Errorf("cannot read dir, %w", err) } for idx := range files { @@ -199,11 +199,15 @@ func (p *Project) getFileNodes(path string, entry *hscommon.PathEntry) { if files[idx].IsDir() { fileNode.IsDirectory = true - p.getFileNodes(fileNode.FullPath, fileNode) + if err := p.getFileNodes(fileNode.FullPath, fileNode); err != nil { + return err + } } entry.Children = append(entry.Children, fileNode) } + + return nil } // InvalidateFileStructure cleans project's files structure @@ -250,69 +254,68 @@ func (p *Project) searchPathEntries(pathEntry *hscommon.PathEntry, path string) return nil } -// CreateNewFolder creates a new directory -func (p *Project) CreateNewFolder(path *hscommon.PathEntry) { - basePath := path.FullPath - - filePathFormat := filepath.Join(basePath, "untitled%d") - - var fileName string - - for i := 0; ; i++ { - possibleFileName := fmt.Sprintf(filePathFormat, i) - if _, err := os.Stat(possibleFileName); os.IsNotExist(err) { +func checkIfPathExists(fmtPath string, maxAttempt int) (fileName string, err error) { + for i := 0; i <= maxAttempt; i++ { + possibleFileName := fmt.Sprintf(fmtPath, i) + if _, err = os.Stat(possibleFileName); os.IsNotExist(err) { fileName = possibleFileName break } + } - if i > maxProjectsCount { - dialog.Message("Could not create a new project folder!").Error() + if fileName == "" { + err = errors.New("could not create a new project file") + } - return - } + return fileName, err +} + +// CreateNewFolder creates a new directory +func (p *Project) CreateNewFolder(path *hscommon.PathEntry) (err error) { + basePath := path.FullPath + + fmtPath := filepath.Join(basePath, "untitled%d") + + fileName, err := checkIfPathExists(fmtPath, maxNewFileAttempts) + if err != nil { + dialog.Message(err.Error()).Error() + + return err } - if err := os.Mkdir(fileName, 0o644); err != nil { - dialog.Message("Could not create a new project folder!").Error() + err = os.Mkdir(fileName, newFileMode) + if err != nil { + dialog.Message(err.Error()).Error() - return + return fmt.Errorf("could not make directory, %w", err) } p.InvalidateFileStructure() - p.GetFileStructure() + _, err = p.GetFileStructure() p.RenameFile(fileName) + + return err } // CreateNewFile creates a new file -func (p *Project) CreateNewFile(fileType hsfiletypes.FileType, path *hscommon.PathEntry) { +func (p *Project) CreateNewFile(fileType hsfiletypes.FileType, path *hscommon.PathEntry) (err error) { basePath := path.FullPath - fmt.Println(fileType, ";", fileType.FileExtension()) - filePathFormat := filepath.Join(basePath, "untitled%d"+fileType.FileExtension()) - - var fileName string + fmtFile := fmt.Sprintf("untitled%s", fileType.FileExtension()) + fmtPath := filepath.Join(basePath, fmtFile) + fileName, err := checkIfPathExists(fmtPath, maxNewFileAttempts) - for i := 0; ; i++ { - possibleFileName := fmt.Sprintf(filePathFormat, i) - if _, err := os.Stat(possibleFileName); os.IsNotExist(err) { - fileName = possibleFileName - - break - } - - if i > maxProjectsCount { - dialog.Message("Could not create a new project file!").Error() - - return - } + if err != nil { + dialog.Message(err.Error()).Error() + return err } switch fileType { case hsfiletypes.FileTypeFont: - _, err := hsfont.NewFile(fileName) + _, err = hsfont.NewFile(fileName) if err != nil { - log.Fatalf("failed to save font: %s", err) + return fmt.Errorf("failed to save font: %w", err) } default: m := getMarshallerByType(fileType) @@ -328,12 +331,14 @@ func (p *Project) CreateNewFile(fileType hsfiletypes.FileType, path *hscommon.Pa p.InvalidateFileStructure() // Force regeneration of file structure so that rename can find the file - p.GetFileStructure() + _, err = p.GetFileStructure() p.RenameFile(fileName) + + return err } // ReloadAuxiliaryMPQs reloads auxiliary MPQs -func (p *Project) ReloadAuxiliaryMPQs(config *hsconfig.Config) { +func (p *Project) ReloadAuxiliaryMPQs(config *hsconfig.Config) (err error) { p.mpqs = make([]d2interface.Archive, len(p.AuxiliaryMPQs)) wg := sync.WaitGroup{} @@ -343,16 +348,17 @@ func (p *Project) ReloadAuxiliaryMPQs(config *hsconfig.Config) { go func(idx int) { fileName := filepath.Join(config.AuxiliaryMpqPath, p.AuxiliaryMPQs[idx]) - data, err := d2mpq.FromFile(fileName) - if err != nil { - log.Fatal(err) + if data, mpqErr := d2mpq.FromFile(fileName); mpqErr != nil { + err = mpqErr + } else { + p.mpqs[idx] = data } - p.mpqs[idx] = data - wg.Done() }(mpqIdx) } wg.Wait() + + return err } diff --git a/hswindow/hstoolwindow/hsprojectexplorer/projectexplorer.go b/hswindow/hstoolwindow/hsprojectexplorer/projectexplorer.go index 76b664db..0bd046bb 100644 --- a/hswindow/hstoolwindow/hsprojectexplorer/projectexplorer.go +++ b/hswindow/hstoolwindow/hsprojectexplorer/projectexplorer.go @@ -138,13 +138,21 @@ func (m *ProjectExplorer) GetProjectTreeNodes() g.Layout { return []g.Widget{g.Label("No project loaded...")} } - fileStructure := m.project.GetFileStructure() + fileStructure, err := m.project.GetFileStructure() + if err != nil { + dialog.Message(err.Error()).Error() + } if fileStructure == nil { return []g.Widget{g.Label("No file structure detected...")} } - return []g.Widget{m.renderNodes(m.project.GetFileStructure())} + nodes, err := m.project.GetFileStructure() + if err != nil { + return []g.Widget{g.Label(err.Error())} + } + + return []g.Widget{m.renderNodes(nodes)} } func (m *ProjectExplorer) onRefreshProjectExplorerClicked() { @@ -156,7 +164,9 @@ func (m *ProjectExplorer) onRefreshProjectExplorerClicked() { } func (m *ProjectExplorer) onNewFontClicked(pathEntry *hscommon.PathEntry) { - m.project.CreateNewFile(hsfiletypes.FileTypeFont, pathEntry) + if err := m.project.CreateNewFile(hsfiletypes.FileTypeFont, pathEntry); err != nil { + dialog.Message(err.Error()).Error() + } } func (m *ProjectExplorer) renderNodes(pathEntry *hscommon.PathEntry) g.Widget { @@ -234,28 +244,44 @@ func (m *ProjectExplorer) createDirectoryTreeItem(pathEntry *hscommon.PathEntry, g.Separator(), g.MenuItem("Font").OnClick(func() { m.onNewFontClicked(pathEntry) }), g.MenuItem("Font table (.tbl)").OnClick(func() { - m.project.CreateNewFile(hsfiletypes.FileTypeTBLFontTable, pathEntry) + if err := m.project.CreateNewFile(hsfiletypes.FileTypeTBLFontTable, pathEntry); err != nil { + dialog.Message(err.Error()).Error() + } }), g.MenuItem("String table (.tbl)").OnClick(func() { - m.project.CreateNewFile(hsfiletypes.FileTypeTBLStringTable, pathEntry) + if err := m.project.CreateNewFile(hsfiletypes.FileTypeTBLStringTable, pathEntry); err != nil { + dialog.Message(err.Error()).Error() + } }), g.MenuItem("Animation data (.d2)").OnClick(func() { - m.project.CreateNewFile(hsfiletypes.FileTypeAnimationData, pathEntry) + if err := m.project.CreateNewFile(hsfiletypes.FileTypeAnimationData, pathEntry); err != nil { + dialog.Message(err.Error()).Error() + } }), g.MenuItem("Animation (.cof)").OnClick(func() { - m.project.CreateNewFile(hsfiletypes.FileTypeCOF, pathEntry) + if err := m.project.CreateNewFile(hsfiletypes.FileTypeCOF, pathEntry); err != nil { + dialog.Message(err.Error()).Error() + } }), g.MenuItem("Palette (.dat)").OnClick(func() { - m.project.CreateNewFile(hsfiletypes.FileTypePalette, pathEntry) + if err := m.project.CreateNewFile(hsfiletypes.FileTypePalette, pathEntry); err != nil { + dialog.Message(err.Error()).Error() + } }), g.MenuItem("Palette transform (.pl2)").OnClick(func() { - m.project.CreateNewFile(hsfiletypes.FileTypePL2, pathEntry) + if err := m.project.CreateNewFile(hsfiletypes.FileTypePL2, pathEntry); err != nil { + dialog.Message(err.Error()).Error() + } }), g.MenuItem("Map tile data (.ds1)").OnClick(func() { - m.project.CreateNewFile(hsfiletypes.FileTypeDS1, pathEntry) + if err := m.project.CreateNewFile(hsfiletypes.FileTypeDS1, pathEntry); err != nil { + dialog.Message(err.Error()).Error() + } }), g.MenuItem("Map tile animation (.dt1)").OnClick(func() { - m.project.CreateNewFile(hsfiletypes.FileTypeDT1, pathEntry) + if err := m.project.CreateNewFile(hsfiletypes.FileTypeDT1, pathEntry); err != nil { + dialog.Message(err.Error()).Error() + } }), }), } @@ -373,7 +399,9 @@ func (m *ProjectExplorer) onFileRenamed(entry *hscommon.PathEntry) { } func (m *ProjectExplorer) onNewFolderClicked(pathEntry *hscommon.PathEntry) { - m.project.CreateNewFolder(pathEntry) + if err := m.project.CreateNewFolder(pathEntry); err != nil { + dialog.Message(err.Error()).Error() + } } func sortPaths(rootPath *hscommon.PathEntry) { From d2c54e71e1d17ac0862bd714775de31a6b5d665d Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 21:04:40 -0700 Subject: [PATCH 23/40] refactoring project menu layout declaration --- hsapp/menubar.go | 116 +++++++++++++++++++++++++---------------------- 1 file changed, 62 insertions(+), 54 deletions(-) diff --git a/hsapp/menubar.go b/hsapp/menubar.go index 943a7f4a..3ff0ad60 100644 --- a/hsapp/menubar.go +++ b/hsapp/menubar.go @@ -87,56 +87,25 @@ func (a *App) openRecentProjectMenu() *g.MenuWidget { } func (a *App) renderMainMenuBar() { - var runAbyssEngineLabel string - - projectOpened := a.project != nil - enginePathSet := len(a.config.AbyssEnginePath) > 0 - - if a.abyssWrapper.IsRunning() { - runAbyssEngineLabel = "Stop Abyss Engine" - } else { - runAbyssEngineLabel = "Run in Abyss Engine" + openURL := func(url string) func() { + return func() { + if err := browser.OpenURL(url); err != nil { + log.Print(err) + } + } } menuLayout := g.Layout{ a.fileMenu(), - g.Menu("View##MainMenuView").Layout(a.buildViewMenu()), - g.Menu("Project##MainMenuProject").Layout(g.Layout{ - g.MenuItem(runAbyssEngineLabel + "##MainMenuProjectRun"). - Enabled(projectOpened && enginePathSet). - OnClick(a.onProjectRunClicked), - g.Separator(), - g.MenuItem("Properties...##MainMenuProjectProperties"). - Enabled(projectOpened). - OnClick(a.onProjectPropertiesClicked), - g.Separator(), - g.MenuItem("Export MPQ...##MainMenuProjectExport"). - Enabled(projectOpened). - OnClick(a.onProjectExportMPQClicked), - }), + a.viewMenu(), + a.projectMenu(), g.Menu("Help").Layout(g.Layout{ g.MenuItem("About HellSpawner...\tF1##MainMenuHelpAbout").OnClick(a.onHelpAboutClicked), g.Separator(), - g.MenuItem("GitHub repository").OnClick(func() { - if err := browser.OpenURL(githubURL); err != nil { - log.Print(err) - } - }), - g.MenuItem("Join Discord server").OnClick(func() { - if err := browser.OpenURL(discordInvitationURL); err != nil { - log.Print(err) - } - }), - g.MenuItem("Development live stream").OnClick(func() { - if err := browser.OpenURL(twitchURL); err != nil { - log.Print(err) - } - }), - g.MenuItem("Support us").OnClick(func() { - if err := browser.OpenURL(supportURL); err != nil { - log.Print(err) - } - }), + g.MenuItem("GitHub repository").OnClick(openURL(githubURL)), + g.MenuItem("Join Discord server").OnClick(openURL(discordInvitationURL)), + g.MenuItem("Development live stream").OnClick(openURL(twitchURL)), + g.MenuItem("Support us").OnClick(openURL(supportURL)), g.Separator(), g.MenuItem("Report Bug on GitHub##MainMenuHelpBug").OnClick(a.onReportBugClicked), }), @@ -151,10 +120,10 @@ func (a *App) renderMainMenuBar() { menuBar.Build() } -func (a *App) buildViewMenu() g.Layout { - result := make([]g.Widget, 0) +func (a *App) viewMenu() *g.MenuWidget { + viewMenu := menu("MainMenu", "View") - result = append(result, g.Menu("Tool Windows").Layout(g.Layout{ + toolWindows := g.Menu("Tool Windows").Layout(g.Layout{ g.MenuItem("Project Explorer\tCtrl+Shift+P"). Selected(a.projectExplorer.Visible). Enabled(true). @@ -168,20 +137,59 @@ func (a *App) buildViewMenu() g.Layout { g.MenuItem("Console\t\t\t\t\tCtrl+Shift+C"). Selected(a.console.Visible). OnClick(a.toggleConsole), - })) + }) - if len(a.editors) == 0 { - return result + items := []g.Widget{ + toolWindows, } - result = append(result, g.Separator()) + if len(a.editors) > 0 { + items = append(items, g.Separator()) + + for i := range a.editors { + editorItem := g.MenuItem(a.editors[i].GetWindowTitle()).OnClick(a.editors[i].BringToFront) + items = append(items, editorItem) + } + } + + return viewMenu.Layout(items...) +} + +func (a *App) projectMenu() *g.MenuWidget { + const ( + runAbyssEngine = "Run in Abyss Engine" + stopAbyssEngine = "Stop Abyss Engine" + ) - for idx := range a.editors { - i := idx - result = append(result, g.MenuItem(a.editors[idx].GetWindowTitle()).OnClick(a.editors[i].BringToFront)) + projectOpened := a.project != nil + enginePathSet := len(a.config.AbyssEnginePath) > 0 + + label := runAbyssEngine + if a.abyssWrapper.IsRunning() { + label = stopAbyssEngine } - return result + projectMenu := menu("MainMenu", "Project") + + projectMenuRun := menuItem("MainMenuProject", label, ""). + Enabled(projectOpened && enginePathSet). + OnClick(a.onProjectRunClicked) + + projectMenuProperties := menuItem("MainMenuProject", "Properties...", ""). + Enabled(projectOpened). + OnClick(a.onProjectPropertiesClicked) + + projectMenuExportMPQ := menuItem("MainMenuProject", "Export MPQ...", ""). + Enabled(projectOpened). + OnClick(a.onProjectExportMPQClicked) + + return projectMenu.Layout( + projectMenuRun, + g.Separator(), + projectMenuProperties, + g.Separator(), + projectMenuExportMPQ, + ) } func (a *App) onNewProjectClicked() { From 0c1ef6551ffb792d325d28d92716b8f910bd5b70 Mon Sep 17 00:00:00 2001 From: gravestench Date: Fri, 4 Jun 2021 22:01:00 -0700 Subject: [PATCH 24/40] Resolves #259 --- hscommon/hsfiletypes/filetype.go | 47 +++++++++++++++----------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/hscommon/hsfiletypes/filetype.go b/hscommon/hsfiletypes/filetype.go index 77061531..04b93f71 100644 --- a/hscommon/hsfiletypes/filetype.go +++ b/hscommon/hsfiletypes/filetype.go @@ -11,6 +11,7 @@ import ( type FileType int type fileTypeInfoStruct struct { + FileType Name string Extension string subTypeCheck func(*[]byte) (FileType, error) @@ -42,31 +43,28 @@ func determineTBLtype(data *[]byte) (FileType, error) { return FileTypeTBLStringTable, nil } - d := *data - if string(d[:4]) == "Woo!" { + if string((*data)[:4]) == "Woo!" { return FileTypeTBLFontTable, nil } return FileTypeText, nil } -func fileExtensionInfo() map[FileType]fileTypeInfoStruct { - return map[FileType]fileTypeInfoStruct{ - FileTypeUnknown: {}, - FileTypeFont: {Name: "Font", Extension: ".hsf"}, - FileTypePalette: {Name: "Palette", Extension: ".dat"}, - FileTypePL2: {Name: "Palette Map", Extension: ".pl2"}, - FileTypeAudio: {Name: "Audio", Extension: ".wav"}, - FileTypeDCC: {Name: "DCC", Extension: ".dcc"}, - FileTypeDC6: {Name: "DC6", Extension: ".dc6"}, - FileTypeCOF: {Name: "COF", Extension: ".cof"}, - FileTypeDT1: {Name: "DT1", Extension: ".dt1"}, - FileTypeTBL: {Name: "TBL", Extension: ".tbl", subTypeCheck: determineTBLtype}, - FileTypeTBLFontTable: {Name: "Font table", Extension: ".tbl"}, - FileTypeTBLStringTable: {Name: "String table", Extension: ".tbl"}, - FileTypeText: {Name: "Text", Extension: ".txt"}, - FileTypeDS1: {Name: "DS1", Extension: ".ds1"}, - FileTypeAnimationData: {Name: "AnimationData", Extension: ".d2"}, +func fileExtensionInfo() []fileTypeInfoStruct { + return []fileTypeInfoStruct{ + {FileType: FileTypeUnknown}, + {FileType: FileTypeFont, Name: "Font", Extension: ".hsf"}, + {FileType: FileTypePalette, Name: "Palette", Extension: ".dat"}, + {FileType: FileTypePL2, Name: "Palette Map", Extension: ".pl2"}, + {FileType: FileTypeAudio, Name: "Audio", Extension: ".wav"}, + {FileType: FileTypeDCC, Name: "DCC", Extension: ".dcc"}, + {FileType: FileTypeDC6, Name: "DC6", Extension: ".dc6"}, + {FileType: FileTypeCOF, Name: "COF", Extension: ".cof"}, + {FileType: FileTypeDT1, Name: "DT1", Extension: ".dt1"}, + {FileType: FileTypeTBL, Name: "TBL", Extension: ".tbl", subTypeCheck: determineTBLtype}, + {FileType: FileTypeText, Name: "Text", Extension: ".txt"}, + {FileType: FileTypeDS1, Name: "DS1", Extension: ".ds1"}, + {FileType: FileTypeAnimationData, Name: "AnimationData", Extension: ".d2"}, } } @@ -82,14 +80,13 @@ func (f FileType) FileExtension() string { // GetFileTypeFromExtension returns file type func GetFileTypeFromExtension(extension string, data *[]byte) (FileType, error) { - info := fileExtensionInfo() - for idx := range info { - if strings.EqualFold(info[idx].Extension, extension) { - if info[idx].subTypeCheck == nil { - return idx, nil + for _, info := range fileExtensionInfo() { + if strings.EqualFold(info.Extension, extension) { + if info.subTypeCheck == nil { + return info.FileType, nil } - return info[idx].subTypeCheck(data) + return info.subTypeCheck(data) } } From 65e6e29ff9b459164a3e893458cfbad512d5df8c Mon Sep 17 00:00:00 2001 From: gravestench Date: Sat, 5 Jun 2021 00:13:56 -0700 Subject: [PATCH 25/40] isolated help menu declarations into one function --- hsapp/menubar.go | 58 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/hsapp/menubar.go b/hsapp/menubar.go index 3ff0ad60..260b6d6c 100644 --- a/hsapp/menubar.go +++ b/hsapp/menubar.go @@ -87,28 +87,11 @@ func (a *App) openRecentProjectMenu() *g.MenuWidget { } func (a *App) renderMainMenuBar() { - openURL := func(url string) func() { - return func() { - if err := browser.OpenURL(url); err != nil { - log.Print(err) - } - } - } - menuLayout := g.Layout{ a.fileMenu(), a.viewMenu(), a.projectMenu(), - g.Menu("Help").Layout(g.Layout{ - g.MenuItem("About HellSpawner...\tF1##MainMenuHelpAbout").OnClick(a.onHelpAboutClicked), - g.Separator(), - g.MenuItem("GitHub repository").OnClick(openURL(githubURL)), - g.MenuItem("Join Discord server").OnClick(openURL(discordInvitationURL)), - g.MenuItem("Development live stream").OnClick(openURL(twitchURL)), - g.MenuItem("Support us").OnClick(openURL(supportURL)), - g.Separator(), - g.MenuItem("Report Bug on GitHub##MainMenuHelpBug").OnClick(a.onReportBugClicked), - }), + a.helpMenu(), } if a.focusedEditor != nil { @@ -192,6 +175,45 @@ func (a *App) projectMenu() *g.MenuWidget { ) } +func openURL(url string) { + if err := browser.OpenURL(url); err != nil { + log.Print(err) + } +} + +func (a *App) onOpenURL(url string) func() { + return func() { + openURL(url) + } +} + +func (a *App) helpMenu() *g.MenuWidget { + menuHelp := menu("MainMenu", "Help") + menuHelpAbout := menuItem("MainMenuHelp", "About HellSpawner...", "F1"). + OnClick(a.onHelpAboutClicked) + menuHelpGithub := menuItem("MainMenuHelp", "GitHub repository", ""). + OnClick(a.onOpenURL(githubURL)) + menuHelpDiscord := menuItem("MainMenuHelp", "Join Discord server", ""). + OnClick(a.onOpenURL(discordInvitationURL)) + menuHelpTwitch := menuItem("MainMenuHelp", "Development live stream", ""). + OnClick(a.onOpenURL(twitchURL)) + menuHelpSupport := menuItem("MainMenuHelp", "Support us", ""). + OnClick(a.onOpenURL(supportURL)) + menuHelpBug := menuItem("MainMenuHelp", "Report Bug on GitHub", ""). + OnClick(a.onReportBugClicked) + + return menuHelp.Layout( + menuHelpAbout, + g.Separator(), + menuHelpGithub, + menuHelpDiscord, + menuHelpTwitch, + menuHelpSupport, + g.Separator(), + menuHelpBug, + ) +} + func (a *App) onNewProjectClicked() { file, err := dialog.File().Filter("HellSpawner Project", "hsp").Save() if err != nil || file == "" { From de244bc7432b7718798b479126398fc41a51b797 Mon Sep 17 00:00:00 2001 From: gravestench Date: Sat, 5 Jun 2021 00:14:46 -0700 Subject: [PATCH 26/40] make Bug Report menu item not crash the app --- hsapp/menubar.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hsapp/menubar.go b/hsapp/menubar.go index 260b6d6c..12931e79 100644 --- a/hsapp/menubar.go +++ b/hsapp/menubar.go @@ -315,7 +315,7 @@ func (a *App) onReportBugClicked() { err = browser.OpenURL("https://github.com/OpenDiablo2/HellSpawner/issues/new?body=" + strings.Join(body, "%0D")) if err != nil { - log.Fatal(err) + log.Println(err) } } From eb3b49590144522c756c6dc9952ab1c1d2a81cc6 Mon Sep 17 00:00:00 2001 From: gravestench Date: Sat, 5 Jun 2021 00:51:05 -0700 Subject: [PATCH 27/40] Replaced all log.Fatal with log.Print * also some minor edits to comments --- hsapp/app.go | 55 ++----------------- hsapp/menubar.go | 52 ++++++++++++++++++ hscommon/hsutil/datautils.go | 2 +- hsconfig/config.go | 6 +- hswidget/popupconfirm.go | 2 +- .../projectpropertiesdialog.go | 3 +- hswindow/hseditor/editor.go | 2 +- .../hseditor/hssoundeditor/soundeditor.go | 6 +- .../hstoolwindow/hsmpqexplorer/mpqexplorer.go | 2 +- 9 files changed, 71 insertions(+), 59 deletions(-) diff --git a/hsapp/app.go b/hsapp/app.go index 9bf6d8cc..f046880a 100644 --- a/hsapp/app.go +++ b/hsapp/app.go @@ -158,56 +158,13 @@ func (a *App) Run() (err error) { func (a *App) render() { a.TextureLoader.StopLoadingTextures() - a.renderMainMenuBar() - - idx := 0 - for idx < len(a.editors) { - editor := a.editors[idx] - if !editor.IsVisible() { - editor.Cleanup() - - if editor.HasFocus() { - a.focusedEditor = nil - } - - a.editors = append(a.editors[:idx], a.editors[idx+1:]...) - - continue - } - - hadFocus := editor.HasFocus() - - editor.Build() - - // if this window didn't have focus before, but it does now, - // unregister any other window's shortcuts, and register this window's keyboard shortcuts instead - if !hadFocus && editor.HasFocus() { - a.InputManager.UnregisterWindowShortcuts() - - editor.RegisterKeyboardShortcuts(a.InputManager) - a.focusedEditor = editor - } - - idx++ - } - - windows := []hscommon.Renderable{ - a.projectExplorer, - a.mpqExplorer, - a.console, - a.preferencesDialog, - a.aboutDialog, - a.projectPropertiesDialog, - } - - for _, tw := range windows { - if tw.IsVisible() { - tw.Build() - } - } + a.renderMainMenuBar() + a.renderEditors() + a.renderWindows() g.Update() + a.TextureLoader.ResumeLoadingTextures() } @@ -335,7 +292,7 @@ func (a *App) toggleMPQExplorer() { func (a *App) onProjectPropertiesChanged(project *hsproject.Project) { a.project = project if err := a.project.Save(); err != nil { - log.Fatal(err) + log.Print(err) } a.mpqExplorer.SetProject(a.project) @@ -346,7 +303,7 @@ func (a *App) onProjectPropertiesChanged(project *hsproject.Project) { func (a *App) onPreferencesChanged(config *hsconfig.Config) { a.config = config if err := a.config.Save(); err != nil { - log.Fatal(err) + log.Print(err) } if a.project != nil { diff --git a/hsapp/menubar.go b/hsapp/menubar.go index 12931e79..849c396b 100644 --- a/hsapp/menubar.go +++ b/hsapp/menubar.go @@ -13,6 +13,7 @@ import ( g "github.com/ianling/giu" "github.com/pkg/browser" + "github.com/OpenDiablo2/HellSpawner/hscommon" "github.com/OpenDiablo2/HellSpawner/hscommon/hsproject" ) @@ -319,6 +320,57 @@ func (a *App) onReportBugClicked() { } } +func (a *App) renderEditors() { + idx := 0 + for idx < len(a.editors) { + editor := a.editors[idx] + if !editor.IsVisible() { + editor.Cleanup() + + if editor.HasFocus() { + a.focusedEditor = nil + } + + a.editors = append(a.editors[:idx], a.editors[idx+1:]...) + + continue + } + + hadFocus := editor.HasFocus() + + editor.Build() + + // if this window didn't have focus before, but it does now, + // unregister any other window's shortcuts, and register this window's keyboard shortcuts instead + if !hadFocus && editor.HasFocus() { + a.InputManager.UnregisterWindowShortcuts() + + editor.RegisterKeyboardShortcuts(a.InputManager) + + a.focusedEditor = editor + } + + idx++ + } +} + +func (a *App) renderWindows() { + windows := []hscommon.Renderable{ + a.projectExplorer, + a.mpqExplorer, + a.console, + a.preferencesDialog, + a.aboutDialog, + a.projectPropertiesDialog, + } + + for _, tw := range windows { + if tw.IsVisible() { + tw.Build() + } + } +} + func makeMenuID(name, group, shortcut string) string { const ( sep = "##" diff --git a/hscommon/hsutil/datautils.go b/hscommon/hsutil/datautils.go index 403ea4d9..2063ce31 100644 --- a/hscommon/hsutil/datautils.go +++ b/hscommon/hsutil/datautils.go @@ -75,7 +75,7 @@ func ExportToGif(images []*image.RGBA, delay int32) error { defer func() { err := file.Close() // nolint:govet // I want to re-use err if err != nil { - log.Fatalf("Error closing file %s: %v", filePath, err) + log.Printf("Error closing file %s: %v", filePath, err) } }() diff --git a/hsconfig/config.go b/hsconfig/config.go index 3bf81b1c..16227109 100644 --- a/hsconfig/config.go +++ b/hsconfig/config.go @@ -17,8 +17,8 @@ import ( "github.com/kirsle/configdir" ) -// default background color value const ( + // DefaultBGColor is the default background color of the main window DefaultBGColor = 0x0a0a0aff ) @@ -68,7 +68,7 @@ func generateDefaultConfig(path string) *Config { } if err := result.Save(); err != nil { - log.Fatalf("filed to save config: %s", err) + log.Printf("filed to save config: %s", err) } return result @@ -151,7 +151,7 @@ func (c *Config) AddToRecentProjects(filePath string) { } if err := c.Save(); err != nil { - log.Fatalf("failed to save config: %s", err) + log.Printf("failed to save config: %s", err) } } diff --git a/hswidget/popupconfirm.go b/hswidget/popupconfirm.go index 11d23f2f..a1aabb1f 100644 --- a/hswidget/popupconfirm.go +++ b/hswidget/popupconfirm.go @@ -35,7 +35,7 @@ func NewPopUpConfirmDialog(id, header, message string, yCB, nCB func()) *PopUpCo // Build builds a pop up dialog func (p *PopUpConfirmDialog) Build() { if p.header == "" { - log.Fatal("Header is empty; please ensure, if you're building appropriate dialog") + log.Print("Header is empty; please ensure, if you're building appropriate dialog") } open := true diff --git a/hswindow/hsdialog/hsprojectpropertiesdialog/projectpropertiesdialog.go b/hswindow/hsdialog/hsprojectpropertiesdialog/projectpropertiesdialog.go index c9967903..e8632096 100644 --- a/hswindow/hsdialog/hsprojectpropertiesdialog/projectpropertiesdialog.go +++ b/hswindow/hsdialog/hsprojectpropertiesdialog/projectpropertiesdialog.go @@ -272,7 +272,8 @@ func (p *ProjectPropertiesDialog) onAddAuxMpqClicked() { func (p *ProjectPropertiesDialog) addAuxMpq(mpqPath string) { relPath, err := filepath.Rel(p.config.AuxiliaryMpqPath, mpqPath) if err != nil { - log.Fatal(err) + log.Print(err) + return } for idx := range p.project.AuxiliaryMPQs { diff --git a/hswindow/hseditor/editor.go b/hswindow/hseditor/editor.go index 4ff918d1..1c26d0fd 100644 --- a/hswindow/hseditor/editor.go +++ b/hswindow/hseditor/editor.go @@ -131,7 +131,7 @@ func (e *Editor) EncodeState() []byte { Encode() []byte }) if !ok { - log.Fatalf("editor on path %s doesn't support saving state", e.Path.GetUniqueID()) + log.Printf("editor on path %s doesn't support saving state", e.Path.GetUniqueID()) return nil } diff --git a/hswindow/hseditor/hssoundeditor/soundeditor.go b/hswindow/hseditor/hssoundeditor/soundeditor.go index d4ae68ab..61809a16 100644 --- a/hswindow/hseditor/hssoundeditor/soundeditor.go +++ b/hswindow/hseditor/hssoundeditor/soundeditor.go @@ -53,7 +53,8 @@ func Create(_ *hsconfig.Config, data *[]byte, x, y float32, project *hsproject.Project) (hscommon.EditorWindow, error) { streamer, format, err := wav.Decode(bytes.NewReader(*data)) if err != nil { - log.Fatal(err) + log.Print(err) + return nil, err } control := &beep.Ctrl{ @@ -133,7 +134,8 @@ func (s *SoundEditor) stop() { if s.control.Paused { if err := s.streamer.Seek(0); err != nil { - log.Fatal(err) + log.Print(err) + return } } diff --git a/hswindow/hstoolwindow/hsmpqexplorer/mpqexplorer.go b/hswindow/hstoolwindow/hsmpqexplorer/mpqexplorer.go index 895da4dd..ee3720a0 100644 --- a/hswindow/hstoolwindow/hsmpqexplorer/mpqexplorer.go +++ b/hswindow/hstoolwindow/hsmpqexplorer/mpqexplorer.go @@ -117,7 +117,7 @@ func (m *MPQExplorer) GetMpqTreeNodes() []g.Widget { go func(idx int) { mpq, err := d2mpq.FromFile(filepath.Join(m.config.AuxiliaryMpqPath, m.project.AuxiliaryMPQs[idx])) if err != nil { - log.Fatal("failed to load mpq: ", err) + log.Print("failed to load mpq: ", err) } nodes := m.project.GetMPQFileNodes(mpq, m.config) From 5545dca8927791ab9d69eb130c9902c20878f6c0 Mon Sep 17 00:00:00 2001 From: gravestench Date: Sat, 5 Jun 2021 10:52:31 -0700 Subject: [PATCH 28/40] setting pauseTexture to nil during playPauseButton.Dispose --- hswidget/custom_widgets.go | 1 + 1 file changed, 1 insertion(+) diff --git a/hswidget/custom_widgets.go b/hswidget/custom_widgets.go index 7565bda6..18830def 100644 --- a/hswidget/custom_widgets.go +++ b/hswidget/custom_widgets.go @@ -38,6 +38,7 @@ type playPauseButtonState struct { func (s *playPauseButtonState) Dispose() { s.playTexture = nil + s.pauseTexture = nil } // PlayPauseButtonWidget represents a play/pause button From 7be197a37c7f91dcd7eba305c9350f5fb1b12093 Mon Sep 17 00:00:00 2001 From: gravestench Date: Sat, 5 Jun 2021 11:12:16 -0700 Subject: [PATCH 29/40] altered mpq error detection logic * project.ValidateAuxiliaryMPQs now returns an error, as ooposed to bool * MPQExplorer will not add nodes for nil mpq's in case of error during load --- hsapp/app.go | 2 +- hscommon/hsproject/project.go | 6 +++--- hswindow/hstoolwindow/hsmpqexplorer/mpqexplorer.go | 11 +++++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/hsapp/app.go b/hsapp/app.go index f046880a..36f28224 100644 --- a/hsapp/app.go +++ b/hsapp/app.go @@ -252,7 +252,7 @@ func (a *App) loadProjectFromFile(file string) { return } - if !project.ValidateAuxiliaryMPQs(a.config) { + if err = project.ValidateAuxiliaryMPQs(a.config); err != nil { log.Printf("Error loading mpqs: %v", err) dialog.Message("Could not load project.\nCould not locate one or more auxiliary MPQs!").Title("Load HellSpawner Project Error").Error() diff --git a/hscommon/hsproject/project.go b/hscommon/hsproject/project.go index b26b7306..03371d65 100644 --- a/hscommon/hsproject/project.go +++ b/hscommon/hsproject/project.go @@ -102,15 +102,15 @@ func (p *Project) Save() error { } // ValidateAuxiliaryMPQs creates auxiliary mpq's list -func (p *Project) ValidateAuxiliaryMPQs(config *hsconfig.Config) bool { +func (p *Project) ValidateAuxiliaryMPQs(config *hsconfig.Config) error { for idx := range p.AuxiliaryMPQs { realPath := filepath.Join(config.AuxiliaryMpqPath, p.AuxiliaryMPQs[idx]) if _, err := os.Stat(realPath); os.IsNotExist(err) { - return false + return fmt.Errorf("file not found at %s", realPath) } } - return true + return nil } // LoadFromFile loads projects file diff --git a/hswindow/hstoolwindow/hsmpqexplorer/mpqexplorer.go b/hswindow/hstoolwindow/hsmpqexplorer/mpqexplorer.go index ee3720a0..c8484de5 100644 --- a/hswindow/hstoolwindow/hsmpqexplorer/mpqexplorer.go +++ b/hswindow/hstoolwindow/hsmpqexplorer/mpqexplorer.go @@ -115,13 +115,16 @@ func (m *MPQExplorer) GetMpqTreeNodes() []g.Widget { for mpqIndex := range m.project.AuxiliaryMPQs { go func(idx int) { - mpq, err := d2mpq.FromFile(filepath.Join(m.config.AuxiliaryMpqPath, m.project.AuxiliaryMPQs[idx])) + fullPath := filepath.Join(m.config.AuxiliaryMpqPath, m.project.AuxiliaryMPQs[idx]) + mpq, err := d2mpq.FromFile(fullPath) if err != nil { - log.Print("failed to load mpq: ", err) + log.Printf("failed to load mpq: %s", fullPath) } - nodes := m.project.GetMPQFileNodes(mpq, m.config) - result[idx] = m.renderNodes(nodes) + if mpq != nil { + nodes := m.project.GetMPQFileNodes(mpq, m.config) + result[idx] = m.renderNodes(nodes) + } wg.Done() }(mpqIndex) From d1ba5ca51bb7bc469885664170edeb659b5b2eec Mon Sep 17 00:00:00 2001 From: gravestench Date: Sat, 5 Jun 2021 11:21:16 -0700 Subject: [PATCH 30/40] change '[]g.Widget{}' to 'g.Layout{}' --- .../hstoolwindow/hsprojectexplorer/projectexplorer.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hswindow/hstoolwindow/hsprojectexplorer/projectexplorer.go b/hswindow/hstoolwindow/hsprojectexplorer/projectexplorer.go index 0bd046bb..49bb11a6 100644 --- a/hswindow/hstoolwindow/hsprojectexplorer/projectexplorer.go +++ b/hswindow/hstoolwindow/hsprojectexplorer/projectexplorer.go @@ -135,7 +135,7 @@ func (m *ProjectExplorer) makeRefreshButtonLayout() g.Layout { // GetProjectTreeNodes returns project tree func (m *ProjectExplorer) GetProjectTreeNodes() g.Layout { if m.project == nil { - return []g.Widget{g.Label("No project loaded...")} + return g.Layout{g.Label("No project loaded...")} } fileStructure, err := m.project.GetFileStructure() @@ -144,15 +144,15 @@ func (m *ProjectExplorer) GetProjectTreeNodes() g.Layout { } if fileStructure == nil { - return []g.Widget{g.Label("No file structure detected...")} + return g.Layout{g.Label("No file structure detected...")} } nodes, err := m.project.GetFileStructure() if err != nil { - return []g.Widget{g.Label(err.Error())} + return g.Layout{g.Label(err.Error())} } - return []g.Widget{m.renderNodes(nodes)} + return g.Layout{m.renderNodes(nodes)} } func (m *ProjectExplorer) onRefreshProjectExplorerClicked() { From bbc5cb2f367a2aa61fef82e5a2827b4be0611ccb Mon Sep 17 00:00:00 2001 From: gravestench Date: Sat, 5 Jun 2021 11:26:40 -0700 Subject: [PATCH 31/40] lint fix --- hswindow/hstoolwindow/hsmpqexplorer/mpqexplorer.go | 1 + 1 file changed, 1 insertion(+) diff --git a/hswindow/hstoolwindow/hsmpqexplorer/mpqexplorer.go b/hswindow/hstoolwindow/hsmpqexplorer/mpqexplorer.go index c8484de5..d664e608 100644 --- a/hswindow/hstoolwindow/hsmpqexplorer/mpqexplorer.go +++ b/hswindow/hstoolwindow/hsmpqexplorer/mpqexplorer.go @@ -117,6 +117,7 @@ func (m *MPQExplorer) GetMpqTreeNodes() []g.Widget { go func(idx int) { fullPath := filepath.Join(m.config.AuxiliaryMpqPath, m.project.AuxiliaryMPQs[idx]) mpq, err := d2mpq.FromFile(fullPath) + if err != nil { log.Printf("failed to load mpq: %s", fullPath) } From b988e7017005ce3b236d5a9ea19fd9d06ebdcf09 Mon Sep 17 00:00:00 2001 From: gravestench Date: Sat, 5 Jun 2021 11:27:12 -0700 Subject: [PATCH 32/40] log errors to console instead of showing dialog box --- .../hsprojectexplorer/projectexplorer.go | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/hswindow/hstoolwindow/hsprojectexplorer/projectexplorer.go b/hswindow/hstoolwindow/hsprojectexplorer/projectexplorer.go index 49bb11a6..abb5c693 100644 --- a/hswindow/hstoolwindow/hsprojectexplorer/projectexplorer.go +++ b/hswindow/hstoolwindow/hsprojectexplorer/projectexplorer.go @@ -2,6 +2,7 @@ package hsprojectexplorer import ( + "log" "os" "path/filepath" "sort" @@ -140,7 +141,7 @@ func (m *ProjectExplorer) GetProjectTreeNodes() g.Layout { fileStructure, err := m.project.GetFileStructure() if err != nil { - dialog.Message(err.Error()).Error() + log.Print(err) } if fileStructure == nil { @@ -165,7 +166,7 @@ func (m *ProjectExplorer) onRefreshProjectExplorerClicked() { func (m *ProjectExplorer) onNewFontClicked(pathEntry *hscommon.PathEntry) { if err := m.project.CreateNewFile(hsfiletypes.FileTypeFont, pathEntry); err != nil { - dialog.Message(err.Error()).Error() + log.Print(err) } } @@ -245,42 +246,42 @@ func (m *ProjectExplorer) createDirectoryTreeItem(pathEntry *hscommon.PathEntry, g.MenuItem("Font").OnClick(func() { m.onNewFontClicked(pathEntry) }), g.MenuItem("Font table (.tbl)").OnClick(func() { if err := m.project.CreateNewFile(hsfiletypes.FileTypeTBLFontTable, pathEntry); err != nil { - dialog.Message(err.Error()).Error() + log.Print(err) } }), g.MenuItem("String table (.tbl)").OnClick(func() { if err := m.project.CreateNewFile(hsfiletypes.FileTypeTBLStringTable, pathEntry); err != nil { - dialog.Message(err.Error()).Error() + log.Print(err) } }), g.MenuItem("Animation data (.d2)").OnClick(func() { if err := m.project.CreateNewFile(hsfiletypes.FileTypeAnimationData, pathEntry); err != nil { - dialog.Message(err.Error()).Error() + log.Print(err) } }), g.MenuItem("Animation (.cof)").OnClick(func() { if err := m.project.CreateNewFile(hsfiletypes.FileTypeCOF, pathEntry); err != nil { - dialog.Message(err.Error()).Error() + log.Print(err) } }), g.MenuItem("Palette (.dat)").OnClick(func() { if err := m.project.CreateNewFile(hsfiletypes.FileTypePalette, pathEntry); err != nil { - dialog.Message(err.Error()).Error() + log.Print(err) } }), g.MenuItem("Palette transform (.pl2)").OnClick(func() { if err := m.project.CreateNewFile(hsfiletypes.FileTypePL2, pathEntry); err != nil { - dialog.Message(err.Error()).Error() + log.Print(err) } }), g.MenuItem("Map tile data (.ds1)").OnClick(func() { if err := m.project.CreateNewFile(hsfiletypes.FileTypeDS1, pathEntry); err != nil { - dialog.Message(err.Error()).Error() + log.Print(err) } }), g.MenuItem("Map tile animation (.dt1)").OnClick(func() { if err := m.project.CreateNewFile(hsfiletypes.FileTypeDT1, pathEntry); err != nil { - dialog.Message(err.Error()).Error() + log.Print(err) } }), }), @@ -400,7 +401,7 @@ func (m *ProjectExplorer) onFileRenamed(entry *hscommon.PathEntry) { func (m *ProjectExplorer) onNewFolderClicked(pathEntry *hscommon.PathEntry) { if err := m.project.CreateNewFolder(pathEntry); err != nil { - dialog.Message(err.Error()).Error() + log.Print(err) } } From ad403b0dac4ead6807cf79b73a5ba6e3a35af3be Mon Sep 17 00:00:00 2001 From: gravestench Date: Sat, 5 Jun 2021 11:28:03 -0700 Subject: [PATCH 33/40] wrapped external error in fmt.Errorf --- hswindow/hseditor/hssoundeditor/soundeditor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hswindow/hseditor/hssoundeditor/soundeditor.go b/hswindow/hseditor/hssoundeditor/soundeditor.go index 61809a16..9c22e5d2 100644 --- a/hswindow/hseditor/hssoundeditor/soundeditor.go +++ b/hswindow/hseditor/hssoundeditor/soundeditor.go @@ -54,7 +54,7 @@ func Create(_ *hsconfig.Config, streamer, format, err := wav.Decode(bytes.NewReader(*data)) if err != nil { log.Print(err) - return nil, err + return nil, fmt.Errorf("wav decode error: %w", err) } control := &beep.Ctrl{ From 269f2e6515e398c0fa84d5358bc08a26b19a5bfb Mon Sep 17 00:00:00 2001 From: gravestench Date: Sun, 6 Jun 2021 11:09:35 -0700 Subject: [PATCH 34/40] log all errors and show dialog in hsapp.App --- hsapp/app.go | 58 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/hsapp/app.go b/hsapp/app.go index 36f28224..37794e0a 100644 --- a/hsapp/app.go +++ b/hsapp/app.go @@ -131,7 +131,7 @@ func (a *App) Run() (err error) { a.logFile, err = os.OpenFile(filepath.Clean(path), os.O_CREATE|os.O_APPEND|os.O_WRONLY, logFilePerms) if err != nil { - log.Printf("Error opening log file at %s: %v", a.config.LogFilePath, err) + logErr("Error opening log file at %s: %v", a.config.LogFilePath, err) } defer func() { @@ -147,7 +147,10 @@ func (a *App) Run() (err error) { } if a.config.OpenMostRecentOnStartup && len(a.config.RecentProjects) > 0 { - a.loadProjectFromFile(a.config.RecentProjects[0]) + err = a.loadProjectFromFile(a.config.RecentProjects[0]) + if err != nil { + return err + } } a.masterWindow.SetInputCallback(a.InputManager.HandleInput) @@ -168,8 +171,8 @@ func (a *App) render() { a.TextureLoader.ResumeLoadingTextures() } -func logErr(fmtErr string, err error) { - msg := fmt.Sprintf(fmtErr, err) +func logErr(fmtErr string, args ...interface{}) { + msg := fmt.Sprintf(fmtErr, args...) log.Print(msg) dialog.Message(msg).Error() } @@ -240,29 +243,27 @@ func (a *App) openEditor(path *hscommon.PathEntry) { a.createEditor(path, nil, editorWindowDefaultX, editorWindowDefaultY, 0, 0) } -func (a *App) loadProjectFromFile(file string) { +func (a *App) loadProjectFromFile(file string) error { var project *hsproject.Project var err error if project, err = hsproject.LoadFromFile(file); err != nil { - log.Printf("Error loading project: %v", err) - dialog.Message("Could not load project.").Title("Load HellSpawner Project Error").Error() - - return + return err } if err = project.ValidateAuxiliaryMPQs(a.config); err != nil { - log.Printf("Error loading mpqs: %v", err) - dialog.Message("Could not load project.\nCould not locate one or more auxiliary MPQs!").Title("Load HellSpawner Project Error").Error() - - return + return err } a.project = project a.config.AddToRecentProjects(file) a.updateWindowTitle() - a.reloadAuxiliaryMPQs() + + if err := a.reloadAuxiliaryMPQs(); err != nil { + return err + } + a.projectExplorer.SetProject(a.project) a.mpqExplorer.SetProject(a.project) @@ -274,6 +275,8 @@ func (a *App) loadProjectFromFile(file string) { // if we don't have a state saved for this project, just open the project explorer a.projectExplorer.Show() } + + return nil } func (a *App) updateWindowTitle() { @@ -292,31 +295,40 @@ func (a *App) toggleMPQExplorer() { func (a *App) onProjectPropertiesChanged(project *hsproject.Project) { a.project = project if err := a.project.Save(); err != nil { - log.Print(err) + logErr("could not save project properties after changing", err) } a.mpqExplorer.SetProject(a.project) a.updateWindowTitle() - a.reloadAuxiliaryMPQs() + + if err := a.reloadAuxiliaryMPQs(); err != nil { + logErr("could not reload aux mpq's after changing project properties ", err) + } } func (a *App) onPreferencesChanged(config *hsconfig.Config) { a.config = config if err := a.config.Save(); err != nil { - log.Print(err) + logErr("could not save config, %w", err) } - if a.project != nil { - a.reloadAuxiliaryMPQs() + if a.project == nil { + return + } + + if err := a.reloadAuxiliaryMPQs(); err != nil { + logErr("could not reload auxiliary mpq's", err) } } -func (a *App) reloadAuxiliaryMPQs() { +func (a *App) reloadAuxiliaryMPQs() error { if err := a.project.ReloadAuxiliaryMPQs(a.config); err != nil { - dialog.Message(err.Error()).Error() + return err } - a.mpqExplorer.Reset() + a.mpqExplorer.Reset() + + return nil } func (a *App) toggleProjectExplorer() { @@ -361,7 +373,7 @@ func (a *App) Save() { } if err := a.config.Save(); err != nil { - log.Print("failed to save config: ", err) + logErr("failed to save config: ", err) return } From f35a744bc9beba16eea845c5fcb0acdc700f0608 Mon Sep 17 00:00:00 2001 From: gravestench Date: Sun, 6 Jun 2021 11:19:33 -0700 Subject: [PATCH 35/40] cleaned up error printing --- hsapp/app.go | 41 +++++++++---------- hsapp/menubar.go | 22 ++++++---- .../hseditor/hssoundeditor/soundeditor.go | 1 - 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/hsapp/app.go b/hsapp/app.go index 37794e0a..3df2b76c 100644 --- a/hsapp/app.go +++ b/hsapp/app.go @@ -135,14 +135,14 @@ func (a *App) Run() (err error) { } defer func() { - err := a.logFile.Close() - if err != nil { - log.Fatal(err) + if logErr := a.logFile.Close(); logErr != nil { + log.Fatal(logErr) } }() } - if err := a.setup(); err != nil { + err = a.setup() + if err != nil { return err } @@ -244,23 +244,22 @@ func (a *App) openEditor(path *hscommon.PathEntry) { } func (a *App) loadProjectFromFile(file string) error { - var project *hsproject.Project - - var err error - - if project, err = hsproject.LoadFromFile(file); err != nil { - return err + project, err := hsproject.LoadFromFile(file) + if err != nil { + return fmt.Errorf("could not load project from file %s, %w", file, err) } - if err = project.ValidateAuxiliaryMPQs(a.config); err != nil { - return err + err = project.ValidateAuxiliaryMPQs(a.config) + if err != nil { + return fmt.Errorf("could not validate aux mpq's, %w", err) } a.project = project a.config.AddToRecentProjects(file) a.updateWindowTitle() - if err := a.reloadAuxiliaryMPQs(); err != nil { + err = a.reloadAuxiliaryMPQs() + if err != nil { return err } @@ -295,21 +294,21 @@ func (a *App) toggleMPQExplorer() { func (a *App) onProjectPropertiesChanged(project *hsproject.Project) { a.project = project if err := a.project.Save(); err != nil { - logErr("could not save project properties after changing", err) + logErr("could not save project properties after changing, %s", err) } a.mpqExplorer.SetProject(a.project) a.updateWindowTitle() - + if err := a.reloadAuxiliaryMPQs(); err != nil { - logErr("could not reload aux mpq's after changing project properties ", err) + logErr("could not reload aux mpq's after changing project properties, %s", err) } } func (a *App) onPreferencesChanged(config *hsconfig.Config) { a.config = config if err := a.config.Save(); err != nil { - logErr("could not save config, %w", err) + logErr("after changing preferences, %s", err) } if a.project == nil { @@ -317,16 +316,16 @@ func (a *App) onPreferencesChanged(config *hsconfig.Config) { } if err := a.reloadAuxiliaryMPQs(); err != nil { - logErr("could not reload auxiliary mpq's", err) + logErr("after changing preferences, %s", err) } } func (a *App) reloadAuxiliaryMPQs() error { if err := a.project.ReloadAuxiliaryMPQs(a.config); err != nil { - return err + return fmt.Errorf("could not reload aux mpq's in project, %w", err) } - a.mpqExplorer.Reset() + a.mpqExplorer.Reset() return nil } @@ -373,7 +372,7 @@ func (a *App) Save() { } if err := a.config.Save(); err != nil { - logErr("failed to save config: ", err) + logErr("failed to save config: %s", err) return } diff --git a/hsapp/menubar.go b/hsapp/menubar.go index 849c396b..eb3da4f2 100644 --- a/hsapp/menubar.go +++ b/hsapp/menubar.go @@ -77,7 +77,9 @@ func (a *App) openRecentProjectMenu() *g.MenuWidget { for idx := range a.config.RecentProjects { projectName := a.config.RecentProjects[idx] g.MenuItem(fmt.Sprintf("%s##MainMenuOpenRecent_%d", projectName, idx)).OnClick(func() { - a.loadProjectFromFile(projectName) + if err := a.loadProjectFromFile(projectName); err != nil { + logErr("could not open recent file %s", err) + } }).Build() } } @@ -218,16 +220,18 @@ func (a *App) helpMenu() *g.MenuWidget { func (a *App) onNewProjectClicked() { file, err := dialog.File().Filter("HellSpawner Project", "hsp").Save() if err != nil || file == "" { - return + logErr("could not create new project, %s", err) } - var project *hsproject.Project - - if project, err = hsproject.CreateNew(file); err != nil { - return + project, err := hsproject.CreateNew(file) + if err != nil { + logErr("could not create new project file, %s", err) } - a.loadProjectFromFile(project.GetProjectFilePath()) + ppath := project.GetProjectFilePath() + if err := a.loadProjectFromFile(ppath); err != nil { + logErr("could not load new project from file %s, %s", ppath, err) + } } func (a *App) onOpenProjectClicked() { @@ -236,7 +240,9 @@ func (a *App) onOpenProjectClicked() { return } - a.loadProjectFromFile(file) + if err := a.loadProjectFromFile(file); err != nil { + logErr("could not open project file %s, %s", file, err) + } } func (a *App) onProjectPropertiesClicked() { diff --git a/hswindow/hseditor/hssoundeditor/soundeditor.go b/hswindow/hseditor/hssoundeditor/soundeditor.go index 9c22e5d2..afa8d15a 100644 --- a/hswindow/hseditor/hssoundeditor/soundeditor.go +++ b/hswindow/hseditor/hssoundeditor/soundeditor.go @@ -53,7 +53,6 @@ func Create(_ *hsconfig.Config, data *[]byte, x, y float32, project *hsproject.Project) (hscommon.EditorWindow, error) { streamer, format, err := wav.Decode(bytes.NewReader(*data)) if err != nil { - log.Print(err) return nil, fmt.Errorf("wav decode error: %w", err) } From 724261328eb4dd3de6a7a2609a2669c172ee8bce Mon Sep 17 00:00:00 2001 From: gravestench Date: Sun, 6 Jun 2021 15:41:52 -0700 Subject: [PATCH 36/40] handle early app termination from showing usage string --- hsapp/app.go | 6 +++++- hsapp/flags.go | 28 ++++++++++++++-------------- main.go | 2 ++ 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/hsapp/app.go b/hsapp/app.go index 3df2b76c..5f9f9f96 100644 --- a/hsapp/app.go +++ b/hsapp/app.go @@ -97,6 +97,8 @@ type App struct { InputManager *hsinput.InputManager TextureLoader hscommon.TextureLoader + + showUsage bool } // Create creates new app instance @@ -110,7 +112,9 @@ func Create() (*App, error) { abyssWrapper: abysswrapper.Create(), } - result.parseArgs() + if shouldTerminate := result.parseArgs(); shouldTerminate { + return nil, nil + } result.config = hsconfig.Load(*result.Flags.optionalConfigPath) diff --git a/hsapp/flags.go b/hsapp/flags.go index 3ca6b769..8e0ba781 100644 --- a/hsapp/flags.go +++ b/hsapp/flags.go @@ -3,6 +3,7 @@ package hsapp import ( "flag" "fmt" + "log" "os" "github.com/OpenDiablo2/HellSpawner/hsconfig" @@ -16,7 +17,7 @@ type Flags struct { } // parse all of the command line args -func (a *App) parseArgs() { +func (a *App) parseArgs() (shouldTerminate bool){ a.parseConfigArgs() a.parseLogFileArgs() a.parseBackgroundColorArgs() @@ -26,6 +27,15 @@ func (a *App) parseArgs() { // // otherwise, other flags will not be printed in usage string! a.parseHelpArgs() + + flag.Parse() + + if a.showUsage { + flag.Usage() + return true + } + + return false } func (a *App) parseHelpArgs() { @@ -35,23 +45,13 @@ func (a *App) parseHelpArgs() { fmtUsage = "usage: %s []\n\nFlags:\n" ) - // we will use a single variable for both short and long flags - var showHelp bool - - flag.BoolVar(&showHelp, long, false, "Show help") - flag.BoolVar(&showHelp, short, false, "Show help (shorthand)") + flag.BoolVar(&a.showUsage, long, false, "Show help") + flag.BoolVar(&a.showUsage, short, false, "Show help (shorthand)") flag.Usage = func() { - fmt.Printf(fmtUsage, os.Args[0]) + log.Printf(fmtUsage, os.Args[0]) flag.PrintDefaults() } - - flag.Parse() - - if showHelp { - flag.Usage() - os.Exit(0) // this is dangerous, forces us to parse the help flags last - } } func (a *App) parseConfigArgs() { diff --git a/main.go b/main.go index 95731244..535aa15f 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,8 @@ func main() { app, err := hsapp.Create() if err != nil { log.Fatal(err) + } else if app == nil { + return // we've terminated early } if err := app.Run(); err != nil { From f8a4fca009addd217b75ffadf9d6654441c7e2ba Mon Sep 17 00:00:00 2001 From: gravestench Date: Sun, 6 Jun 2021 15:47:40 -0700 Subject: [PATCH 37/40] remove unnecessary dereference. --- hscommon/hsfiletypes/filetype.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hscommon/hsfiletypes/filetype.go b/hscommon/hsfiletypes/filetype.go index 04b93f71..98a1d5f8 100644 --- a/hscommon/hsfiletypes/filetype.go +++ b/hscommon/hsfiletypes/filetype.go @@ -43,7 +43,8 @@ func determineTBLtype(data *[]byte) (FileType, error) { return FileTypeTBLStringTable, nil } - if string((*data)[:4]) == "Woo!" { + d := *data + if string(d[:4]) == "Woo!" { return FileTypeTBLFontTable, nil } From 8e556dd1776783254621d34f043f45486936df10 Mon Sep 17 00:00:00 2001 From: gravestench Date: Sun, 6 Jun 2021 16:32:38 -0700 Subject: [PATCH 38/40] File type/subtype logic refactor --- hsapp/flags.go | 2 +- hscommon/hsfiletypes/filetype.go | 109 ++++++++++++++++++++----------- 2 files changed, 71 insertions(+), 40 deletions(-) diff --git a/hsapp/flags.go b/hsapp/flags.go index 8e0ba781..64fa58cf 100644 --- a/hsapp/flags.go +++ b/hsapp/flags.go @@ -17,7 +17,7 @@ type Flags struct { } // parse all of the command line args -func (a *App) parseArgs() (shouldTerminate bool){ +func (a *App) parseArgs() (shouldTerminate bool) { a.parseConfigArgs() a.parseLogFileArgs() a.parseBackgroundColorArgs() diff --git a/hscommon/hsfiletypes/filetype.go b/hscommon/hsfiletypes/filetype.go index 98a1d5f8..0d329b66 100644 --- a/hscommon/hsfiletypes/filetype.go +++ b/hscommon/hsfiletypes/filetype.go @@ -10,13 +10,6 @@ import ( // FileType represents file type type FileType int -type fileTypeInfoStruct struct { - FileType - Name string - Extension string - subTypeCheck func(*[]byte) (FileType, error) -} - // enumerate known file types const ( FileTypeUnknown FileType = iota @@ -34,61 +27,99 @@ const ( FileTypeTBLFontTable FileTypeDS1 FileTypeAnimationData + numFileTypes ) // determinateTBLtype returns table type -func determineTBLtype(data *[]byte) (FileType, error) { - _, err := d2tbl.LoadTextDictionary(*data) - if err == nil { - return FileTypeTBLStringTable, nil +func determineSubtypeTBL(data *[]byte) FileType { + if _, err := d2tbl.LoadTextDictionary(*data); err == nil { + return FileTypeTBLStringTable } d := *data if string(d[:4]) == "Woo!" { - return FileTypeTBLFontTable, nil + return FileTypeTBLFontTable } - return FileTypeText, nil -} - -func fileExtensionInfo() []fileTypeInfoStruct { - return []fileTypeInfoStruct{ - {FileType: FileTypeUnknown}, - {FileType: FileTypeFont, Name: "Font", Extension: ".hsf"}, - {FileType: FileTypePalette, Name: "Palette", Extension: ".dat"}, - {FileType: FileTypePL2, Name: "Palette Map", Extension: ".pl2"}, - {FileType: FileTypeAudio, Name: "Audio", Extension: ".wav"}, - {FileType: FileTypeDCC, Name: "DCC", Extension: ".dcc"}, - {FileType: FileTypeDC6, Name: "DC6", Extension: ".dc6"}, - {FileType: FileTypeCOF, Name: "COF", Extension: ".cof"}, - {FileType: FileTypeDT1, Name: "DT1", Extension: ".dt1"}, - {FileType: FileTypeTBL, Name: "TBL", Extension: ".tbl", subTypeCheck: determineTBLtype}, - {FileType: FileTypeText, Name: "Text", Extension: ".txt"}, - {FileType: FileTypeDS1, Name: "DS1", Extension: ".ds1"}, - {FileType: FileTypeAnimationData, Name: "AnimationData", Extension: ".d2"}, - } + return FileTypeText } // String returns file type string func (f FileType) String() string { - return fileExtensionInfo()[f].Name + table := map[FileType]string{ + FileTypeUnknown: "unknown", + FileTypeFont: "Hellspawner font", + FileTypePalette: "palette", + FileTypePL2: "palette transform", + FileTypeAudio: "wav", + FileTypeDCC: "DCC image", + FileTypeDC6: "DC6 image", + FileTypeCOF: "COF animation data", + FileTypeDT1: "DT1 tileset", + FileTypeTBLFontTable: "Font Character Table", + FileTypeTBLStringTable: "String Table", + FileTypeText: "text file", + FileTypeDS1: "DS1 Map Stamp", + FileTypeAnimationData: "Animation Dataset", + } + + val, found := table[f] + if !found { + return table[FileTypeUnknown] + } + + return val } // FileExtension returns file's extension func (f FileType) FileExtension() string { - return fileExtensionInfo()[f].Extension + table := map[FileType]string{ + FileTypeFont: ".hsf", + FileTypePalette: ".dat", + FileTypePL2: ".pl2", + FileTypeAudio: ".wav", + FileTypeDCC: ".dcc", + FileTypeDC6: ".dc6", + FileTypeCOF: ".cof", + FileTypeDT1: ".dt1", + FileTypeTBLFontTable: ".tbl", + FileTypeTBLStringTable: ".tbl", + FileTypeText: ".txt", + FileTypeDS1: ".ds1", + FileTypeAnimationData: ".d2", + } + + return table[f] +} + +type fileTypeCheckFn = func(data *[]byte) FileType + +// SubtypeCheckFn returns a function to check a file. This is important for +// distinguishing between files that share a common file extension. +func (f FileType) SubtypeCheckFn() fileTypeCheckFn { + table := map[FileType]fileTypeCheckFn{ + FileTypeTBL: determineSubtypeTBL, + FileTypeTBLFontTable: determineSubtypeTBL, + FileTypeTBLStringTable: determineSubtypeTBL, + } + + return table[f] } // GetFileTypeFromExtension returns file type func GetFileTypeFromExtension(extension string, data *[]byte) (FileType, error) { - for _, info := range fileExtensionInfo() { - if strings.EqualFold(info.Extension, extension) { - if info.subTypeCheck == nil { - return info.FileType, nil - } + for fileType := FileType(0); fileType < numFileTypes; fileType++ { + extensionsMatch := strings.EqualFold(fileType.FileExtension(), extension) + if !extensionsMatch { + continue + } - return info.subTypeCheck(data) + fnDetermine := fileType.SubtypeCheckFn() + if fnDetermine == nil { + return fileType, nil } + + return fnDetermine(data), nil } return FileTypeUnknown, errors.New("filetype: no file type matches the extension provided") From 46eb1dfadea198e149c78dca289efe62b1aceace Mon Sep 17 00:00:00 2001 From: gravestench Date: Sun, 6 Jun 2021 16:36:42 -0700 Subject: [PATCH 39/40] changed private fn name for coherence --- hscommon/hsproject/project.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hscommon/hsproject/project.go b/hscommon/hsproject/project.go index 03371d65..3ed473bf 100644 --- a/hscommon/hsproject/project.go +++ b/hscommon/hsproject/project.go @@ -254,7 +254,7 @@ func (p *Project) searchPathEntries(pathEntry *hscommon.PathEntry, path string) return nil } -func checkIfPathExists(fmtPath string, maxAttempt int) (fileName string, err error) { +func getNextUniqueNewPath(fmtPath string, maxAttempt int) (fileName string, err error) { for i := 0; i <= maxAttempt; i++ { possibleFileName := fmt.Sprintf(fmtPath, i) if _, err = os.Stat(possibleFileName); os.IsNotExist(err) { @@ -277,7 +277,7 @@ func (p *Project) CreateNewFolder(path *hscommon.PathEntry) (err error) { fmtPath := filepath.Join(basePath, "untitled%d") - fileName, err := checkIfPathExists(fmtPath, maxNewFileAttempts) + fileName, err := getNextUniqueNewPath(fmtPath, maxNewFileAttempts) if err != nil { dialog.Message(err.Error()).Error() @@ -304,7 +304,7 @@ func (p *Project) CreateNewFile(fileType hsfiletypes.FileType, path *hscommon.Pa fmtFile := fmt.Sprintf("untitled%s", fileType.FileExtension()) fmtPath := filepath.Join(basePath, fmtFile) - fileName, err := checkIfPathExists(fmtPath, maxNewFileAttempts) + fileName, err := getNextUniqueNewPath(fmtPath, maxNewFileAttempts) if err != nil { dialog.Message(err.Error()).Error() From bfd57dc7fad16e04433a29137315834171de91a4 Mon Sep 17 00:00:00 2001 From: gravestench Date: Sun, 6 Jun 2021 16:52:11 -0700 Subject: [PATCH 40/40] consolidate dialog and logging like in hsapp --- hscommon/hsproject/project.go | 13 ++++++++----- .../hsprojectexplorer/projectexplorer.go | 9 ++++++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/hscommon/hsproject/project.go b/hscommon/hsproject/project.go index 3ed473bf..d03d47d9 100644 --- a/hscommon/hsproject/project.go +++ b/hscommon/hsproject/project.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io/ioutil" + "log" "os" "path/filepath" "strings" @@ -271,6 +272,12 @@ func getNextUniqueNewPath(fmtPath string, maxAttempt int) (fileName string, err return fileName, err } +func logErr(fmtErr string, args ...interface{}) { + msg := fmt.Sprintf(fmtErr, args...) + log.Print(msg) + dialog.Message(msg).Error() +} + // CreateNewFolder creates a new directory func (p *Project) CreateNewFolder(path *hscommon.PathEntry) (err error) { basePath := path.FullPath @@ -279,15 +286,11 @@ func (p *Project) CreateNewFolder(path *hscommon.PathEntry) (err error) { fileName, err := getNextUniqueNewPath(fmtPath, maxNewFileAttempts) if err != nil { - dialog.Message(err.Error()).Error() - return err } err = os.Mkdir(fileName, newFileMode) if err != nil { - dialog.Message(err.Error()).Error() - return fmt.Errorf("could not make directory, %w", err) } @@ -307,7 +310,7 @@ func (p *Project) CreateNewFile(fileType hsfiletypes.FileType, path *hscommon.Pa fileName, err := getNextUniqueNewPath(fmtPath, maxNewFileAttempts) if err != nil { - dialog.Message(err.Error()).Error() + logErr("%s", err) return err } diff --git a/hswindow/hstoolwindow/hsprojectexplorer/projectexplorer.go b/hswindow/hstoolwindow/hsprojectexplorer/projectexplorer.go index abb5c693..e30cf673 100644 --- a/hswindow/hstoolwindow/hsprojectexplorer/projectexplorer.go +++ b/hswindow/hstoolwindow/hsprojectexplorer/projectexplorer.go @@ -2,6 +2,7 @@ package hsprojectexplorer import ( + "fmt" "log" "os" "path/filepath" @@ -399,9 +400,15 @@ func (m *ProjectExplorer) onFileRenamed(entry *hscommon.PathEntry) { m.project.InvalidateFileStructure() } +func logErr(fmtErr string, args ...interface{}) { + msg := fmt.Sprintf(fmtErr, args...) + log.Print(msg) + dialog.Message(msg).Error() +} + func (m *ProjectExplorer) onNewFolderClicked(pathEntry *hscommon.PathEntry) { if err := m.project.CreateNewFolder(pathEntry); err != nil { - log.Print(err) + logErr("%s", err) } }