diff --git a/library/library.go b/library/library.go index e0cf0d7..46150cf 100644 --- a/library/library.go +++ b/library/library.go @@ -2,6 +2,7 @@ package library import ( "fmt" + "github.com/ralim/switchhost/versionsdb" "io" "os" "sync" @@ -35,9 +36,10 @@ type Library struct { FileIndex *index.Index //Privates - keys *keystore.Keystore - settings *settings.Settings - titledb *titledb.TitlesDB + keys *keystore.Keystore + settings *settings.Settings + titledb *titledb.TitlesDB + versiondb *versionsdb.VersionDB waitgroup *sync.WaitGroup //These channels are used for decoupling the workers for each state of the file import pipeline @@ -59,12 +61,13 @@ type Library struct { organisationLocking organisationLocks } -func NewLibrary(titledb *titledb.TitlesDB, settings *settings.Settings, ui *termui.TermUI) *Library { +func NewLibrary(titledb *titledb.TitlesDB, settings *settings.Settings, ui *termui.TermUI, versions *versionsdb.VersionDB) *Library { library := &Library{ - titledb: titledb, - settings: settings, - ui: ui, - keys: nil, + titledb: titledb, + settings: settings, + versiondb: versions, + ui: ui, + keys: nil, // Channels fileMetaScanRequests: make(chan *fileScanningInfo, settings.QueueLength), fileValidationScanRequests: make(chan *fileScanningInfo, settings.QueueLength), diff --git a/library/library_test.go b/library/library_test.go index c54dcd2..777626e 100644 --- a/library/library_test.go +++ b/library/library_test.go @@ -19,19 +19,19 @@ func TestStopStart(t *testing.T) { NSZCommandLine: "sleep 0.1", QueueLength: 2, } - lib := NewLibrary(nil, &sett, nil) + lib := NewLibrary(nil, &sett, nil, nil) //Inject some pending requests lib.fileCompressionRequests <- &fileScanningInfo{ path: "0.02", } - startime := time.Now() + now := time.Now() lib.Start() //Yield to let our sleep be selected before the close time.Sleep(time.Millisecond * 100) fmt.Println(".........") lib.Stop() - duration := time.Since(startime) + duration := time.Since(now) if duration.Milliseconds() < 100 { t.Error("Didnt wait for the sleep") diff --git a/library/updates.go b/library/updates.go new file mode 100644 index 0000000..7d9c3f5 --- /dev/null +++ b/library/updates.go @@ -0,0 +1,34 @@ +package library + +type GameUpdatePair struct { + Title string + TitleID uint64 + CurrentVersion uint32 + LatestVersion uint32 +} + +func (lib *Library) GetGamesNeedingUpdate() []GameUpdatePair { + //For all known games in the library, find out all the games that need an update + results := make([]GameUpdatePair, 0, 50) + if lib.versiondb == nil || lib.FileIndex == nil { + return results + } + for _, title := range lib.FileIndex.ListTitleFiles() { + titleInfo, _ := lib.FileIndex.GetTitleRecords(title.TitleID) + latest := lib.versiondb.LookupLatestVersion(title.TitleID) + updateLatest := uint32(0) + if titleInfo.Update != nil { + updateLatest = titleInfo.Update.Version + } + if latest > updateLatest { + results = append(results, GameUpdatePair{ + Title: title.Name, + TitleID: title.TitleID, + CurrentVersion: updateLatest, + LatestVersion: latest, + }) + } + } + + return results +} diff --git a/server/http.go b/server/http.go index ff6c74a..2419b8c 100644 --- a/server/http.go +++ b/server/http.go @@ -236,6 +236,8 @@ func (server *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) { server.httpHandleTitlesDB(res, req) case "skeleton.min.css": server.httpHandleCSS(res, req) + case "updates.json": + server.handleServingUpdatesList(res, req) case "index.html": fallthrough case "": diff --git a/server/http_test.go b/server/http_test.go index 5a912db..8610c1b 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -15,24 +15,24 @@ import ( ) func maketestServer(t *testing.T) (*Server, *library.Library, string) { - temp_folder, err := os.MkdirTemp("", "unit_test") + tempFolder, err := os.MkdirTemp("", "unit_test") if err != nil { t.Fatal(err) } - settings := settings.NewSettings(path.Join(temp_folder, "settings.json")) + settings := settings.NewSettings(path.Join(tempFolder, "settings.json")) settings.ServerMOTD = "SwitchRoooooot" // using different one to ensure its honoured titledb := titledb.CreateTitlesDB(settings) - lib := library.NewLibrary(titledb, settings, nil) + lib := library.NewLibrary(titledb, settings, nil, nil) server := NewServer(lib, titledb, settings) - return server, lib, temp_folder + return server, lib, tempFolder } func TestHTTPServerbasics(t *testing.T) { t.Parallel() - server, lib, temp_folder := maketestServer(t) - defer os.RemoveAll(temp_folder) + server, lib, tempFolder := maketestServer(t) + defer os.RemoveAll(tempFolder) //Now we can fake poke server handlers tempBuffer := bytes.NewBuffer([]byte{}) @@ -68,8 +68,8 @@ func TestHTTPServerbasics(t *testing.T) { func TestHTTPFileServingJSON(t *testing.T) { t.Parallel() - server, _, temp_folder := maketestServer(t) - defer os.RemoveAll(temp_folder) + server, _, tempFolder := maketestServer(t) + defer os.RemoveAll(tempFolder) // Create a request to pass to our handler. We don't have any query parameters for now, so we'll // pass 'nil' as the third parameter. @@ -103,8 +103,8 @@ func TestHTTPFileServingJSON(t *testing.T) { func TestHTTPFileServingBinary(t *testing.T) { t.Parallel() - server, lib, temp_folder := maketestServer(t) - defer os.RemoveAll(temp_folder) + server, lib, tempFolder := maketestServer(t) + defer os.RemoveAll(tempFolder) file := &index.FileOnDiskRecord{ Path: "../testing_files/UnitTest_[05123A0000000000].nsp", @@ -144,8 +144,8 @@ func TestHTTPFileServingBinary(t *testing.T) { func TestHTTPFileServingBinaryRangeBytes(t *testing.T) { t.Parallel() - server, lib, temp_folder := maketestServer(t) - defer os.RemoveAll(temp_folder) + server, lib, tempFolder := maketestServer(t) + defer os.RemoveAll(tempFolder) file := &index.FileOnDiskRecord{ Path: "../testing_files/UnitTest_[05123A0000000000].nsp", diff --git a/server/updates.go b/server/updates.go new file mode 100644 index 0000000..366c9ac --- /dev/null +++ b/server/updates.go @@ -0,0 +1,14 @@ +package server + +import ( + "encoding/json" + "net/http" +) + +func (server *Server) handleServingUpdatesList(respWriter http.ResponseWriter, req *http.Request) { + + updates := server.library.GetGamesNeedingUpdate() + data, _ := json.MarshalIndent(updates, "", " ") + _, _ = respWriter.Write(data) + +} diff --git a/switchhost.go b/switchhost.go index d61e319..51129ad 100644 --- a/switchhost.go +++ b/switchhost.go @@ -23,10 +23,11 @@ type SwitchHost struct { KeysFilePath string `flag:"keys" help:"Path to your switch's keyfile"` NoCUI bool `flag:"noCUI" help:"Disable the Console UI"` - lib *library.Library `flag:"-"` - ui *termui.TermUI `flag:"-"` - settings *settings.Settings `flag:"-"` - titleDB *titledb.TitlesDB `flag:"-"` + lib *library.Library `flag:"-"` + ui *termui.TermUI `flag:"-"` + settings *settings.Settings `flag:"-"` + titleDB *titledb.TitlesDB `flag:"-"` + versionDB *versionsdb.VersionDB `flag:"-"` } func NewSwitchHost() *SwitchHost { @@ -67,7 +68,7 @@ func (m *SwitchHost) Run() error { // Download TitlesDB m.loadTitlesDB() - m.lib = library.NewLibrary(m.titleDB, m.settings, m.ui) + m.lib = library.NewLibrary(m.titleDB, m.settings, m.ui, m.versionDB) m.tryAndLoadKeys() @@ -133,7 +134,7 @@ func (m *SwitchHost) loadVersionInfo() { defer versionInfo.UpdateStatus("Done") } versionInfo := versionsdb.NewVersionDBFromURL(m.settings.VersionsDBURL, m.settings.CacheFolder) - versionInfo.LookupLatestVersion(0000) + m.versionDB = versionInfo } func (m *SwitchHost) tryAndLoadKeys() { //First try cli arg path if we can diff --git a/versionsdb/versions.go b/versionsdb/versions.go index 5eb01c7..e3e0825 100644 --- a/versionsdb/versions.go +++ b/versionsdb/versions.go @@ -54,7 +54,7 @@ func NewVersionDB(r io.Reader) *VersionDB { value = existing } //Find newest version - for k, _ := range versions { + for k := range versions { thisVersion := uint64(0) if thisVersion, err = strconv.ParseUint(k, 10, 32); err != nil { log.Warn().Err(err).Str("value", k).Msg("Title version failed parsing in versions update") @@ -69,8 +69,12 @@ func NewVersionDB(r io.Reader) *VersionDB { return db } -func (db *VersionDB) LookupLatestVersion(titleID uint64) { +// LookupLatestVersion returns the newest version for this TitleID or 0 if none found +func (db *VersionDB) LookupLatestVersion(titleID uint64) uint32 { db.RLock() defer db.RUnlock() - + if newest, ok := db.latestVersions[titleID]; ok { + return newest + } + return 0 }