From 65dbeff3489977c3df399e13ee1e9b69e9368f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Boulanouar?= Date: Sun, 19 Dec 2021 19:15:18 +0100 Subject: [PATCH] Custom titledb (#5) * first draft for customTitleDB * Fix lint issues * Add example in config * Add missing part in index for banned theme Co-authored-by: DblK --- README.md | 3 +- config.example.yaml | 14 ++++++++- config/config.go | 35 +++++++++++++-------- gamescollection/collection.go | 57 ++++++++++++++++++++++++++++------ main.go | 4 +-- mock_repository/mock_config.go | 28 +++++++++++++++++ repository/interfaces.go | 21 +++++++++++-- sources/sources.go | 8 ++--- 8 files changed, 136 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 5761660..e2d39cc 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ [![golangci-lint](https://github.com/DblK/tinshop/actions/workflows/golangci-lint.yml/badge.svg?event=release)](https://github.com/DblK/tinshop/actions/workflows/golangci-lint.yml) [![GitHub go.mod Go version of a Go module](https://img.shields.io/github/go-mod/go-version/dblk/tinshop.svg)](https://github.com/dblk/tinshop) -[![GoDoc reference example](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/dblk/tinshop/v0.0.1) +[![GoDoc reference example](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/dblk/tinshop/v0.0.3) [![GoReportCard example](https://goreportcard.com/badge/github.com/dblk/tinshop)](https://goreportcard.com/report/github.com/dblk/tinshop) [![GitHub release](https://img.shields.io/github/release/dblk/tinshop.svg)](https://GitHub.com/dblk/tinshop/releases/) @@ -43,6 +43,7 @@ Here is the list of all main features so far: - [X] Auto-refresh configuration on file change - [X] Add the possibility to whitelist or blacklist a switch - [X] Add the possibility to ban theme +- [X] You can specify custom titledb to be merged with official one # Dev or build from source diff --git a/config.example.yaml b/config.example.yaml index 53f913b..1ca4e65 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -51,4 +51,16 @@ security: # Block access to all switch present in this list # You can find the uid of a switch in the log upon access backlist: - - NOACCESSNOACCESSNOACCESSNOACCESSNOACCESSNOACCESSNOACCESSNOACCESS \ No newline at end of file + - NOACCESSNOACCESSNOACCESSNOACCESSNOACCESSNOACCESSNOACCESSNOACCESS + +# This section describe all custom title db to show up properly in tinfoil +customTitledb: + # Id of the entry + "060000BADDAD0000": + id: "050000BADDAD0000" + name: "Tinfoil" + region: "US" + releaseDate: 20180801 + description: "Nintendo Switch Title Manager" + size: 14000000 + iconUrl: "" \ No newline at end of file diff --git a/config/config.go b/config/config.go index 34c5f2b..5234ca5 100644 --- a/config/config.go +++ b/config/config.go @@ -27,18 +27,19 @@ type security struct { // File holds all config information type File struct { rootShop string - ShopHost string `mapstructure:"host"` - ShopProtocol string `mapstructure:"protocol"` - ShopPort int `mapstructure:"port"` - Debug debug `mapstructure:"debug"` - AllSources repository.Sources `mapstructure:"sources"` - Name string `mapstructure:"name"` - Security security `mapstructure:"security"` + ShopHost string `mapstructure:"host"` + ShopProtocol string `mapstructure:"protocol"` + ShopPort int `mapstructure:"port"` + Debug debug `mapstructure:"debug"` + AllSources repository.Sources `mapstructure:"sources"` + Name string `mapstructure:"name"` + Security security `mapstructure:"security"` + CustomTitleDB map[string]repository.CustomDBEntry `mapstructure:"customTitledb"` shopTemplateData repository.ShopTemplate } var serverConfig File -var allHooks []func(repository.Sources) +var allHooks []func(repository.Config) // LoadConfig handles viper under the hood func LoadConfig() { @@ -71,7 +72,7 @@ func configChange() { // Call all hooks for _, hook := range allHooks { - hook(serverConfig.AllSources) + hook(&serverConfig) } } @@ -134,8 +135,8 @@ func ComputeDefaultValues(config repository.Config) repository.Config { return config } -// HookOnSource Add hook function on change sources -func HookOnSource(f func(repository.Sources)) { +// AddHook Add hook function on change config +func AddHook(f func(repository.Config)) { allHooks = append(allHooks, f) } @@ -179,6 +180,11 @@ func (cfg *File) Directories() []string { return cfg.AllSources.Directories } +// CustomDB returns the list of custom title db +func (cfg *File) CustomDB() map[string]repository.CustomDBEntry { + return cfg.CustomTitleDB +} + // NfsShares returns the list of nfs sources func (cfg *File) NfsShares() []string { return cfg.AllSources.Nfs @@ -235,10 +241,13 @@ func (cfg *File) isInWhiteList(uid string) bool { // IsBannedTheme tells if the theme is banned or not func (cfg *File) IsBannedTheme(theme string) bool { - fmt.Println(theme) - fmt.Println(cfg.Security.BannedTheme) idxBannedTheme := utils.Search(len(cfg.Security.BannedTheme), func(index int) bool { return cfg.Security.BannedTheme[index] == theme }) return idxBannedTheme != -1 } + +// BannedTheme returns all banned theme +func (cfg *File) BannedTheme() []string { + return cfg.Security.BannedTheme +} diff --git a/gamescollection/collection.go b/gamescollection/collection.go index 196f4b8..f83e776 100644 --- a/gamescollection/collection.go +++ b/gamescollection/collection.go @@ -5,6 +5,8 @@ import ( "io" "log" "os" + "reflect" + "strings" "github.com/dblk/tinshop/config" "github.com/dblk/tinshop/repository" @@ -12,6 +14,7 @@ import ( ) var library map[string]map[string]interface{} +var mergedLibrary map[string]interface{} var games repository.GameType // Load ensure that necessary data is loaded @@ -58,28 +61,56 @@ func loadTitlesLibrary() { func initGamesCollection() { // Build games object games.Success = "Welcome to your own shop!" - games.Titledb = make(map[string]map[string]interface{}) + games.Titledb = make(map[string]interface{}) games.Files = make([]interface{}, 0) + games.ThemeBlackList = nil } -// Reset the collection of files -func Reset(src repository.Sources) { +// OnConfigUpdate the collection of files +func OnConfigUpdate(cfg repository.Config) { initGamesCollection() + + // Create merged library + mergedLibrary = make(map[string]interface{}) + + // Copy library + for key, entry := range library { + gameID := strings.ToUpper(key) + + mergedLibrary[gameID] = entry + } + + // Copy CustomDB + for key, entry := range config.GetConfig().CustomDB() { + gameID := strings.ToUpper(key) + if mergedLibrary[gameID] != nil { + log.Println("Duplicate customDB entry from official titledb (consider removing from configuration)", gameID) + } else { + mergedLibrary[gameID] = entry + } + } + + // Check if blacklist entries + if len(config.GetConfig().BannedTheme()) != 0 { + games.ThemeBlackList = config.GetConfig().BannedTheme() + } else { + games.ThemeBlackList = nil + } } // Library returns the titledb library -func Library() map[string]map[string]interface{} { - return library +func Library() map[string]interface{} { + return mergedLibrary } // HasGameIDInLibrary tells if we have gameID information in library func HasGameIDInLibrary(gameID string) bool { - return library[gameID] != nil + return Library()[gameID] != nil } // IsBaseGame tells if the gameID is a base game or not func IsBaseGame(gameID string) bool { - return library[gameID]["iconUrl"] != nil + return Library()[gameID].(map[string]interface{})["iconUrl"] != nil } // Games returns the games inside the library @@ -91,8 +122,14 @@ func Games() repository.GameType { func CountGames() int { var uniqueGames int for _, entry := range games.Titledb { - if entry["iconUrl"] != nil { - uniqueGames++ + if reflect.TypeOf(entry).String() == "repository.CustomDBEntry" { + if entry.(repository.CustomDBEntry).IconURL != "" { + uniqueGames++ + } + } else { + if entry.(map[string]interface{})["iconUrl"] != nil { + uniqueGames++ + } } } return uniqueGames @@ -114,7 +151,7 @@ func AddNewGames(newGames []repository.FileDesc) { if games.Titledb[file.GameID] != nil && IsBaseGame(file.GameID) { log.Println("Already added id!", file.GameID, file.Path) } else { - games.Titledb[file.GameID] = library[file.GameID] + games.Titledb[file.GameID] = Library()[file.GameID] } } else { log.Println("Game not found in database!", file.GameInfo, file.Path) diff --git a/main.go b/main.go index bb92eb2..ff6f0f9 100644 --- a/main.go +++ b/main.go @@ -77,8 +77,8 @@ func initServer() { collection.Load() // Loading config - config.HookOnSource(collection.Reset) - config.HookOnSource(sources.Load) + config.AddHook(collection.OnConfigUpdate) + config.AddHook(sources.OnConfigUpdate) config.LoadConfig() } diff --git a/mock_repository/mock_config.go b/mock_repository/mock_config.go index 789ce7b..37b89e3 100644 --- a/mock_repository/mock_config.go +++ b/mock_repository/mock_config.go @@ -34,6 +34,34 @@ func (m *MockConfig) EXPECT() *MockConfigMockRecorder { return m.recorder } +// BannedTheme mocks base method. +func (m *MockConfig) BannedTheme() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BannedTheme") + ret0, _ := ret[0].([]string) + return ret0 +} + +// BannedTheme indicates an expected call of BannedTheme. +func (mr *MockConfigMockRecorder) BannedTheme() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BannedTheme", reflect.TypeOf((*MockConfig)(nil).BannedTheme)) +} + +// CustomDB mocks base method. +func (m *MockConfig) CustomDB() map[string]repository.CustomDBEntry { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CustomDB") + ret0, _ := ret[0].(map[string]repository.CustomDBEntry) + return ret0 +} + +// CustomDB indicates an expected call of CustomDB. +func (mr *MockConfigMockRecorder) CustomDB() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CustomDB", reflect.TypeOf((*MockConfig)(nil).CustomDB)) +} + // DebugNfs mocks base method. func (m *MockConfig) DebugNfs() bool { m.ctrl.T.Helper() diff --git a/repository/interfaces.go b/repository/interfaces.go index 155281f..1a7dd16 100644 --- a/repository/interfaces.go +++ b/repository/interfaces.go @@ -33,6 +33,9 @@ type Config interface { IsBlacklisted(string) bool IsWhitelisted(string) bool IsBannedTheme(string) bool + BannedTheme() []string + + CustomDB() map[string]CustomDBEntry } // ShopTemplate contains all variables used for shop template @@ -61,7 +64,19 @@ type FileDesc struct { // GameType structure type GameType struct { - Success string `json:"success"` - Titledb map[string]map[string]interface{} `json:"titledb"` - Files []interface{} `json:"files"` + Success string `json:"success"` + Titledb map[string]interface{} `json:"titledb"` + Files []interface{} `json:"files"` + ThemeBlackList []string `json:"themeBlackList,omitempty"` +} + +// CustomDBEntry describe the various fields for entries +type CustomDBEntry struct { + ID string `mapstructure:"id" json:"id"` + Name string `mapstructure:"name" json:"name"` + Region string `mapstructure:"region" json:"region"` + Size int `mapstructure:"size" json:"size"` + ReleaseDate int `mapstructure:"releaseDate" json:"releaseDate"` + Description string `mapstructure:"description" json:"description"` + IconURL string `mapstructure:"iconUrl" json:"iconUrl"` } diff --git a/sources/sources.go b/sources/sources.go index 2086638..03a1aef 100644 --- a/sources/sources.go +++ b/sources/sources.go @@ -10,12 +10,12 @@ import ( var gameFiles []repository.FileDesc -// Load from all sources -func Load(src repository.Sources) { +// OnConfigUpdate from all sources +func OnConfigUpdate(cfg repository.Config) { log.Println("Sources loading...") gameFiles = make([]repository.FileDesc, 0) - loadGamesDirectories(src.Directories, len(src.Nfs) == 0) - loadGamesNfsShares(src.Nfs) + loadGamesDirectories(cfg.Directories(), len(cfg.NfsShares()) == 0) + loadGamesNfsShares(cfg.NfsShares()) } // GetFiles returns all games files in various sources