From 625736d539284fdee337b672c600898922332ced Mon Sep 17 00:00:00 2001 From: Barcia04 <98990328+Barcia04@users.noreply.github.com> Date: Tue, 17 Feb 2026 14:20:29 +0100 Subject: [PATCH 1/8] Fixing PAL region codes and forcing wrong labeled games to their correct region Added country exclusive title ID code in order to fix DSiWare, and DSi games not appearing, and Wii, WiiWare, 3DS, and DS suffering the same in PAL regions. Added Special NTSC-U region, these regions are either Special Editiosn such as Walmart or Toys R' Us which they are blacklisted or Canadian releases. Some games (a small amount of them) are labeled wrong in GameTDB, these games are labeled as PAL instead of NTSC-U, so I added a code to force them to change to NTSC-U --- v6/dllist/titles.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/v6/dllist/titles.go b/v6/dllist/titles.go index f9bd91e3..f4bcb95e 100644 --- a/v6/dllist/titles.go +++ b/v6/dllist/titles.go @@ -101,10 +101,10 @@ var regionToGameTDB = map[constants.Region]string{ constants.Japan: "NTSC-J", } -var regionToCodeTDB = map[constants.Region]byte{ - constants.NTSC: 'E', - constants.PAL: 'P', - constants.Japan: 'J', +var regionToCodeTDB = map[constants.Region][]byte{ + constants.NTSC: {'E', 'L', 'X', 'Z'}, + constants.PAL: {'P', 'F', 'D', 'S', 'I', 'H', 'U', 'X', 'Y', 'V', 'Z'}, + constants.Japan: {'J'}, } var gameTDBRatingToRatingID = map[string]map[string]uint8{ @@ -160,6 +160,13 @@ func (l *List) GenerateTitleStruct(games *[]gametdb.Game, defaultTitleType const // Whatever the reason is, we have no metadata to use. continue } + + forcedRegion := game.Region + isForced := slices.Contains(constants.CanadaUSAIDs, game.ID[:4]) + + if isForced { + forcedRegion = "NTSC-U" + } if game.Region == regionToGameTDB[l.region] || game.Region == "ALL" { titleType := defaultTitleType From 171037a24e55c93ad2f1e41ab11db99e7bd31e2f Mon Sep 17 00:00:00 2001 From: Barcia04 <98990328+Barcia04@users.noreply.github.com> Date: Tue, 17 Feb 2026 14:25:35 +0100 Subject: [PATCH 2/8] Update const.go Updated Touch Generations! IDs to include all region exclusive titles. Added variable to force titleIDs wrongly labeled to their correct region. --- constants/const.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/constants/const.go b/constants/const.go index 2e93e279..7cd992a1 100644 --- a/constants/const.go +++ b/constants/const.go @@ -175,17 +175,21 @@ var TitleTypesData = []TitleTypeData{ } var TouchGenIDs = []string{ - "YBN", "VAA", "AYA", "AND", "ANM", "ATD", "CVN", - "YCU", "ATI", "AOS", "AG3", "AWI", "APL", "AJQ", "CM7", - "AD5", "AD2", "ADG", "AD7", "AD3", "IMW", "C6P", "AXP", - "A8N", "AZI", "ASQ", "ATR", "AGF", - "RFN", "RFP", "R64", "RYW", + "YBN", "VAAE", "VAAV", "AYA", "AND", "ANM", "ATD", "ADJ", "BET", "CNVP", "CNVF", "CNVD", "CNVI", "CNVS", "ANH", "ANG", "AUA", "AUB", "AUC", "AUD", "AUE", "AIX", "AIZ", + "YCU", "ATI", "AOS", "AG3", "AWIE", "APLE", "AJQE", "AJQJ","CM7", "BKCE", "ATG", "AVM", "AD5", "AD2", "ADG", "AD7", "AD3", "IMW", "IA8", "C6P", "AXP", "A4VJ", "AJM", "AOI" + "A8NE", "A2Y", "AZI", "ASQ", "AJY", "ATR", "ARJ", "AGFE", "USK", "VET", "YFCE" "YLZP", "YLZX", "YLZJ", "YNU", "SUPP", "SUPJ", "RFN", "RFP", "R64", "RYW", "RHAP", "RHAJ", + "RJT", "RSPP", "RSPJ", "RZTP", "RFBP", "R4EP", "RNOP", "RTYP", "SP2P", } var DevAppIDs = []string{ "007E", "091E", "410E", "413E", "5NEA", "RAAE", } +var CanadaUSAIDs = []string{ + "VT3Z", "B5BL", "C62L", "B6RX", +} + + // TitleType is the classified type of title according to GameTDB type TitleType uint8 From e897bc24707c1fa06d0c97a4e7682499ae750762 Mon Sep 17 00:00:00 2001 From: Barcia04 <98990328+Barcia04@users.noreply.github.com> Date: Tue, 17 Feb 2026 15:36:22 +0100 Subject: [PATCH 3/8] Attempt to fix region code badly implemented and forcing region IDs Attempted to fix region code that was badly implemented alongside the CanadaUSA IDs getting the ForceRegion implemented but not getting used. --- v6/dllist/titles.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v6/dllist/titles.go b/v6/dllist/titles.go index f4bcb95e..c1a741e0 100644 --- a/v6/dllist/titles.go +++ b/v6/dllist/titles.go @@ -168,7 +168,7 @@ func (l *List) GenerateTitleStruct(games *[]gametdb.Game, defaultTitleType const forcedRegion = "NTSC-U" } - if game.Region == regionToGameTDB[l.region] || game.Region == "ALL" { + if forcedRegion == regionToGameTDB[l.region] || game.Region == "ALL" { titleType := defaultTitleType // (Sketch) The first locale will always be English from what I have observed title := game.Locale[0].Title @@ -202,7 +202,7 @@ func (l *List) GenerateTitleStruct(games *[]gametdb.Game, defaultTitleType const continue } - if game.ID[3] != regionToCodeTDB[l.region] { + if !slices.Contains(regionToCodeTDB[l.region], game.ID[3]) { continue } From f004cdac4f3c5c3c46e382d0ec85d66ea94c2790 Mon Sep 17 00:00:00 2001 From: Barcia04 <98990328+Barcia04@users.noreply.github.com> Date: Tue, 17 Feb 2026 16:55:24 +0100 Subject: [PATCH 4/8] Fix syntax errors in TouchGenIDs declaration Added missing commas in the Touch! Generation IDs causing syntax errors. --- constants/const.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/constants/const.go b/constants/const.go index 7cd992a1..38081655 100644 --- a/constants/const.go +++ b/constants/const.go @@ -176,8 +176,8 @@ var TitleTypesData = []TitleTypeData{ var TouchGenIDs = []string{ "YBN", "VAAE", "VAAV", "AYA", "AND", "ANM", "ATD", "ADJ", "BET", "CNVP", "CNVF", "CNVD", "CNVI", "CNVS", "ANH", "ANG", "AUA", "AUB", "AUC", "AUD", "AUE", "AIX", "AIZ", - "YCU", "ATI", "AOS", "AG3", "AWIE", "APLE", "AJQE", "AJQJ","CM7", "BKCE", "ATG", "AVM", "AD5", "AD2", "ADG", "AD7", "AD3", "IMW", "IA8", "C6P", "AXP", "A4VJ", "AJM", "AOI" - "A8NE", "A2Y", "AZI", "ASQ", "AJY", "ATR", "ARJ", "AGFE", "USK", "VET", "YFCE" "YLZP", "YLZX", "YLZJ", "YNU", "SUPP", "SUPJ", "RFN", "RFP", "R64", "RYW", "RHAP", "RHAJ", + "YCU", "ATI", "AOS", "AG3", "AWIE", "APLE", "AJQE", "AJQJ","CM7", "BKCE", "ATG", "AVM", "AD5", "AD2", "ADG", "AD7", "AD3", "IMW", "IA8", "C6P", "AXP", "A4VJ", "AJM", "AOI", + "A8NE", "A2Y", "AZI", "ASQ", "AJY", "ATR", "ARJ", "AGFE", "USK", "VET", "YFCE", "YLZP", "YLZX", "YLZJ", "YNU", "SUPP", "SUPJ", "RFN", "RFP", "R64", "RYW", "RHAP", "RHAJ", "RJT", "RSPP", "RSPJ", "RZTP", "RFBP", "R4EP", "RNOP", "RTYP", "SP2P", } From cb3097b483947539ea827ae013f760ba4c315d36 Mon Sep 17 00:00:00 2001 From: Barcia04 <98990328+Barcia04@users.noreply.github.com> Date: Tue, 17 Feb 2026 22:49:10 +0100 Subject: [PATCH 5/8] Testing --- test.go | 544 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 544 insertions(+) create mode 100644 test.go diff --git a/test.go b/test.go new file mode 100644 index 00000000..1958bd9b --- /dev/null +++ b/test.go @@ -0,0 +1,544 @@ +package main + +import ( + "NintendoChannel/common" + "NintendoChannel/constants" + "NintendoChannel/gametdb" + "bytes" + "fmt" + "golang.org/x/image/draw" + "image" + "image/color" + "image/jpeg" + "image/png" + "net/http" + "os" + "strings" + "testing" +) + +var langaugeToLocale = map[constants.Language]string{ + constants.Japanese: "JA", + constants.English: "EN", + constants.German: "DE", + constants.French: "FR", + constants.Spanish: "ES", + constants.Italian: "IT", + constants.Dutch: "NL", +} + +var regionToGameTDB = map[constants.Region]string{ + constants.NTSC: "NTSC-U", + constants.PAL: "PAL", + constants.Japan: "NTSC-J", +} + +var regionToCodeTDB = map[constants.Region][]byte{ + constants.NTSC: {'E', 'L', 'X', 'Z'}, + constants.PAL: {'P', 'F', 'D', 'S', 'I', 'H', 'U', 'X', 'Y', 'V', 'Z'}, + constants.Japan: {'J'}, +} + +var gameTDBRatingToRatingID = map[string]map[string]uint8{ + "CERO": { + "A": 8, + "B": 9, + "C": 10, + "D": 11, + "Z": 12, + }, + "ESRB": { + // For some reason GameTDB has EC as 3 for some titles + "3": 8, + "EC": 8, + "E": 9, + "E10+": 10, + "T": 11, + "M": 12, + }, + "PEGI": { + "3": 8, + "4": 8, + "6": 9, + "7": 9, + "12": 10, + "15": 11, + "16": 11, + "18": 12, + }, +} + +func (l *List) MakeTitleTable() { + l.Header.TitleTableOffset = l.GetCurrentSize() + + // Wii + l.GenerateTitleStruct(&gametdb.WiiTDB.Games, constants.Wii) + // DS + l.GenerateTitleStruct(&gametdb.DSTDB.Games, constants.NintendoDS) + // 3DS + l.GenerateTitleStruct(&gametdb.ThreeDSTDB.Games, constants.NintendoThreeDS) + + l.Header.NumberOfTitleTables = uint32(len(l.TitleTable)) +} + +func (l *List) GenerateTitleStruct(games *[]gametdb.Game, defaultTitleType constants.TitleType) { + // Internally only created once, but it doesn't hurt to have it on the stack. + // Required for generating a unique checksum for title ids. + crcTable := crc32.MakeTable(crc32.IEEE) + + for _, game := range *games { + if game.Locale == nil { + // Game doesn't exist for this region? + // Whatever the reason is, we have no metadata to use. + continue + } + + forcedRegion := game.Region + isForced := slices.Contains(constants.CanadaUSAIDs, game.ID[:4]) + + if isForced { + forcedRegion = "NTSC-U" + } + + if forcedRegion == regionToGameTDB[l.region] || game.Region == "ALL" { + titleType := defaultTitleType + // (Sketch) The first locale will always be English from what I have observed + title := game.Locale[0].Title + fullTitle := game.Locale[0].Title + synopsis := game.Locale[0].Synopsis + for _, locale := range game.Locale { + if locale.Language == langaugeToLocale[l.language] { + if game.Type != "" { + title = locale.Title + fullTitle = locale.Title + synopsis = locale.Synopsis + titleType = constants.TitleTypeMap[game.Type] + + if titleType == constants.NES && defaultTitleType == constants.NintendoThreeDS { + titleType = constants.NintendoThreeDS + } + } + } + } + + // We will not include mods, GameCube games, demos, DS Download Stations, or Pokemon distributions + if game.Type == "CUSTOM" || game.Type == "GameCube" || game.Type == "Homebrew" || titleType == constants.ThreeDSDownload || + strings.Contains(title, "(Demo)") || strings.Contains(title, "Download") || + strings.Contains(title, "Distribution") || strings.Contains(title, "DSi XL") || + strings.Contains(title, "Exclusive") || strings.Contains(title, "Toys R Us") || + strings.Contains(title, "GameStop") || strings.Contains(title, "Target") || + strings.Contains(title, "Best Buy") || strings.Contains(title, "Walmart") || + strings.Contains(title, "Limited Edition") || strings.Contains(title, "Collector's Edition") || + strings.Contains(title, "(Beta)") || strings.Contains(title, "Relay") || + slices.Contains(constants.DevAppIDs, game.ID[:4]) { + continue + } + + if !slices.Contains(regionToCodeTDB[l.region], game.ID[3]) { + continue + } + + var titleID [4]byte + copy(titleID[:], game.ID) + + // Wii, DS and 3DS games may share the same IDs are one another. XOR to avoid conflict. + id := crc32.Checksum([]byte(game.ID), crcTable) + if defaultTitleType == constants.NintendoDS { + id ^= 0x22222222 + } else if defaultTitleType == constants.NintendoThreeDS { + id ^= 0x33333333 + } + + var releaseYear uint16 = 0xFFFF + if game.ReleaseDate.Year != "" { + temp, _ := strconv.ParseUint(game.ReleaseDate.Year, 10, 32) + releaseYear = uint16(temp) + } + + var releaseMonth uint8 = 0xFF + if game.ReleaseDate.Month != "" { + temp, _ := strconv.ParseUint(game.ReleaseDate.Month, 10, 32) + releaseMonth = uint8(temp) - 1 + } + + var releaseDay uint8 = 0xFF + if game.ReleaseDate.Day != "" { + temp, _ := strconv.ParseUint(game.ReleaseDate.Day, 10, 32) + releaseDay = uint8(temp) + } + + subtitle := "" + if len(title) > 30 { + wrappedTitle := wordwrap.WrapString(title, 30) + for i, s := range strings.Split(wrappedTitle, "\n") { + switch i { + case 0: + title = s + break + case 1: + subtitle = s + break + default: + break + } + } + } + + var byteTitle [31]uint16 + tempTitle := utf16.Encode([]rune(title)) + copy(byteTitle[:], tempTitle) + + var byteSubtitle [31]uint16 + tempSubtitle := utf16.Encode([]rune(subtitle)) + copy(byteSubtitle[:], tempSubtitle) + + medal := constants.None + if num, ok := recommendations[game.ID[:4]]; ok { + medal = GetMedal(num.NumberOfRecommendations) + } + + companyOffset, companyID := l.GetCompany(&game) + table := TitleTable{ + ID: id, + TitleID: titleID, + TitleType: titleType, + Genre: l.SetGenre(&game), + CompanyOffset: companyOffset, + ReleaseYear: releaseYear, + ReleaseMonth: releaseMonth, + ReleaseDay: releaseDay, + RatingID: GetRatingID(game.Rating), + WithFriendsFemaleSecondRow: 0, + WithFriendsFemaleFirstRow: 0, + WithFriendsMaleSecondRow: 0, + WithFriendsMaleFirstRow: 0, + WithFriendsAllSecondRow: 0, + WithFriendsAllFirstRow: 0, + HardcoreFemaleSecondRow: 0, + HardcoreFemaleFirstRow: 0, + HardcoreMaleSecondRow: 0, + HardcoreMaleFirstRow: 0, + HardcoreAllSecondRow: 0, + HardcoreAllFirstRow: 0, + GamersFemaleSecondRow: 0, + GamersFemaleFirstRow: 0, + GamersMaleSecondRow: 0, + GamersMaleFirstRow: 0, + GamersAllSecondRow: 0, + GamersAllFirstRow: 0, + OtherFlags: 0, + Unknown7: [4]byte{0, 0, 0}, + Unknown8: 0, + MedalType: medal, + Unknown9: 222, + TitleName: byteTitle, + Subtitle: byteSubtitle, + ShortTitle: [31]uint16{}, + } + + table.PopulateCriteria(l, game.ID[:4]) + table.DetermineOtherFlags(game) + + l.TitleTable = append(l.TitleTable, table) + if !generateTitles { + continue + } + + i := info.Info{} + i.MakeHeader(titleID, game.Controllers.Players, companyID, table.TitleType, table.ReleaseYear, table.ReleaseMonth, table.ReleaseDay) + i.RatingID = table.RatingID + i.MakeInfo(id, &game, fullTitle, synopsis, l.region, l.language, defaultTitleType, recommendations) + } + } +} + +func GetRatingID(rating gametdb.Rating) uint8 { + if rating.Value == "" { + // Default to E/7/B + return 9 + } + + return gameTDBRatingToRatingID[rating.Type][rating.Value] +} + +func (l *List) GetCompany(game *gametdb.Game) (uint32, uint32) { + isDiscGame := false + companyID := "" + // This first method of retrieving the company is the most accurate. However, it only works with disc games. + if len(game.ID) != 4 { + isDiscGame = true + companyID = game.ID[4:] + } + + for i, company := range gametdb.WiiTDB.Companies.Companies { + if isDiscGame { + if companyID == company.Code { + intCompanyID, err := strconv.ParseUint(hex.EncodeToString([]byte(company.Code)), 16, 32) + common.CheckError(err) + return l.Header.CompanyTableOffset + (128 * uint32(i)), uint32(intCompanyID) + } + } else { + if strings.Contains(game.Publisher, company.Name) { + intcompanyID, err := strconv.ParseUint(hex.EncodeToString([]byte(company.Code)), 16, 32) + common.CheckError(err) + + return l.Header.CompanyTableOffset + (128 * uint32(i)), uint32(intcompanyID) + } + } + } + + // If all fails, default to Nintendo + return l.Header.CompanyTableOffset, 12337 +} + +func (l *List) SetGenre(game *gametdb.Game) [3]byte { + gameTDBToGenre := map[string]uint8{ + "arcade": 15, + "party": 13, + "puzzle": 5, + "action": 1, + "2D platformer": 1, + "3D platformer": 1, + "shooter": 12, + "first-person shooter": 12, + "third-person shooter": 12, + "rail shooter": 12, + "run and gun": 12, + "shoot 'em up": 12, + "stealth action": 1, + "survival horror": 1, + "sports": 4, + "adventure": 2, + "hidden object": 13, + "interactive fiction": 2, + "interactive movie": 2, + "point-and-click": 13, + "music": 10, + "rhythm": 10, + "dance": 10, + "karaoke": 10, + "racing": 7, + "fighting": 14, + "simulation": 9, + "role-playing": 6, + "strategy": 8, + "traditional": 11, + "health": 3, + "others": 13, + } + + genre := [3]byte{0, 0, 0} + for i, s := range strings.Split(game.Genre, ",") { + if i == 3 { + break + } + + genre[i] = gameTDBToGenre[s] + } + + return genre +} + +func (l *List) MakeNewTitleTable() { + // TODO: Figure out a way to get the newest titles + l.Header.NewTitleTableOffset = l.GetCurrentSize() + l.NewTitleTable = append(l.NewTitleTable, l.Header.TitleTableOffset) + l.Header.NumberOfNewTitleTables = 1 +} + +func GetMedal(numberOfTimesVotes int) constants.Medal { + if numberOfTimesVotes >= 50 { + return constants.Platinum + } else if numberOfTimesVotes >= 35 { + return constants.Gold + } else if numberOfTimesVotes >= 20 { + return constants.Silver + } else if numberOfTimesVotes >= 15 { + return constants.Bronze + } + + return constants.None +} + +// PopulateCriteria fills the bitfield entries in TitleTable +func (t *TitleTable) PopulateCriteria(l *List, gameId string) { + if _, ok := recommendations[gameId]; !ok { + return + } + + // First we will go after the `All` category. + // First 4 entries in the tables are the upper half. + for i := 0; i < 4; i++ { + // If it is none, then the bit will be set to nothing. + if recommendations[gameId].AllRecommendations[i].IsGamers == constants.True { + t.GamersAllFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].AllRecommendations[i].IsGamers == constants.False { + t.GamersAllFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } + + for i := 4; i < 8; i++ { + if recommendations[gameId].AllRecommendations[i].IsGamers == constants.True { + t.GamersAllSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].AllRecommendations[i].IsGamers == constants.False { + t.GamersAllSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } + + for i := 0; i < 4; i++ { + if recommendations[gameId].AllRecommendations[i].IsHardcore == constants.True { + t.HardcoreAllFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].AllRecommendations[i].IsHardcore == constants.False { + t.HardcoreAllFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } + + for i := 4; i < 8; i++ { + if recommendations[gameId].AllRecommendations[i].IsHardcore == constants.True { + t.HardcoreAllSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].AllRecommendations[i].IsHardcore == constants.False { + t.HardcoreAllSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } + + for i := 0; i < 4; i++ { + if recommendations[gameId].AllRecommendations[i].IsWithFriends == constants.True { + t.WithFriendsAllFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].AllRecommendations[i].IsWithFriends == constants.False { + t.WithFriendsAllFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } + + for i := 4; i < 8; i++ { + if recommendations[gameId].AllRecommendations[i].IsWithFriends == constants.True { + t.WithFriendsAllSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].AllRecommendations[i].IsWithFriends == constants.False { + t.WithFriendsAllSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } + + // Next is male. + for i := 0; i < 4; i++ { + // If it is none, then the bit will be set to nothing. + if recommendations[gameId].MaleRecommendations[i].IsGamers == constants.True { + t.GamersMaleFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].MaleRecommendations[i].IsGamers == constants.False { + t.GamersMaleFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } + + for i := 4; i < 8; i++ { + if recommendations[gameId].MaleRecommendations[i].IsGamers == constants.True { + t.GamersMaleSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].MaleRecommendations[i].IsGamers == constants.False { + t.GamersMaleSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } + + for i := 0; i < 4; i++ { + if recommendations[gameId].MaleRecommendations[i].IsHardcore == constants.True { + t.HardcoreMaleFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].MaleRecommendations[i].IsHardcore == constants.False { + t.HardcoreMaleFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } + + for i := 4; i < 8; i++ { + if recommendations[gameId].MaleRecommendations[i].IsHardcore == constants.True { + t.HardcoreMaleSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].MaleRecommendations[i].IsHardcore == constants.False { + t.HardcoreMaleSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } + + for i := 0; i < 4; i++ { + if recommendations[gameId].MaleRecommendations[i].IsWithFriends == constants.True { + t.WithFriendsMaleFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].MaleRecommendations[i].IsWithFriends == constants.False { + t.WithFriendsMaleFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } + + for i := 4; i < 8; i++ { + if recommendations[gameId].MaleRecommendations[i].IsWithFriends == constants.True { + t.WithFriendsMaleSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].MaleRecommendations[i].IsWithFriends == constants.False { + t.WithFriendsMaleSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } + + // Finally is female + for i := 0; i < 4; i++ { + // If it is none, then the bit will be set to nothing. + if recommendations[gameId].FemaleRecommendations[i].IsGamers == constants.True { + t.GamersFemaleFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].FemaleRecommendations[i].IsGamers == constants.False { + t.GamersFemaleFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } + + for i := 4; i < 8; i++ { + if recommendations[gameId].FemaleRecommendations[i].IsGamers == constants.True { + t.GamersFemaleSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].FemaleRecommendations[i].IsGamers == constants.False { + t.GamersFemaleSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } + + for i := 0; i < 4; i++ { + if recommendations[gameId].FemaleRecommendations[i].IsHardcore == constants.True { + t.HardcoreFemaleFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].FemaleRecommendations[i].IsHardcore == constants.False { + t.HardcoreFemaleFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } + + for i := 4; i < 8; i++ { + if recommendations[gameId].FemaleRecommendations[i].IsHardcore == constants.True { + t.HardcoreFemaleSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].FemaleRecommendations[i].IsHardcore == constants.False { + t.HardcoreFemaleSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } + + for i := 0; i < 4; i++ { + if recommendations[gameId].FemaleRecommendations[i].IsWithFriends == constants.True { + t.WithFriendsFemaleFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].FemaleRecommendations[i].IsWithFriends == constants.False { + t.WithFriendsFemaleFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } + + for i := 4; i < 8; i++ { + if recommendations[gameId].FemaleRecommendations[i].IsWithFriends == constants.True { + t.WithFriendsFemaleSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) + } else if recommendations[gameId].FemaleRecommendations[i].IsWithFriends == constants.False { + t.WithFriendsFemaleSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) + } + } +} + +func (t *TitleTable) DetermineOtherFlags(game gametdb.Game) { + value := 0xFF + mask := 0 + + // One player or Multiplayer + if game.Controllers.Players > 1 { + mask |= 1 + } + + // Is the game online + isOnline := false + for _, s := range game.Features.Feature { + if strings.Contains(s, "online") { + isOnline = true + } + } + + if isOnline { + mask |= 12 + } + + // TODO: We don't have titles with any videos at the moment + t.OtherFlags = uint8(value & mask) +} From 3aa7d2b29e299d8c3989d4267f4d3b9e50a57755 Mon Sep 17 00:00:00 2001 From: Barcia04 <98990328+Barcia04@users.noreply.github.com> Date: Tue, 17 Feb 2026 22:50:18 +0100 Subject: [PATCH 6/8] Delete test.go --- test.go | 544 -------------------------------------------------------- 1 file changed, 544 deletions(-) delete mode 100644 test.go diff --git a/test.go b/test.go deleted file mode 100644 index 1958bd9b..00000000 --- a/test.go +++ /dev/null @@ -1,544 +0,0 @@ -package main - -import ( - "NintendoChannel/common" - "NintendoChannel/constants" - "NintendoChannel/gametdb" - "bytes" - "fmt" - "golang.org/x/image/draw" - "image" - "image/color" - "image/jpeg" - "image/png" - "net/http" - "os" - "strings" - "testing" -) - -var langaugeToLocale = map[constants.Language]string{ - constants.Japanese: "JA", - constants.English: "EN", - constants.German: "DE", - constants.French: "FR", - constants.Spanish: "ES", - constants.Italian: "IT", - constants.Dutch: "NL", -} - -var regionToGameTDB = map[constants.Region]string{ - constants.NTSC: "NTSC-U", - constants.PAL: "PAL", - constants.Japan: "NTSC-J", -} - -var regionToCodeTDB = map[constants.Region][]byte{ - constants.NTSC: {'E', 'L', 'X', 'Z'}, - constants.PAL: {'P', 'F', 'D', 'S', 'I', 'H', 'U', 'X', 'Y', 'V', 'Z'}, - constants.Japan: {'J'}, -} - -var gameTDBRatingToRatingID = map[string]map[string]uint8{ - "CERO": { - "A": 8, - "B": 9, - "C": 10, - "D": 11, - "Z": 12, - }, - "ESRB": { - // For some reason GameTDB has EC as 3 for some titles - "3": 8, - "EC": 8, - "E": 9, - "E10+": 10, - "T": 11, - "M": 12, - }, - "PEGI": { - "3": 8, - "4": 8, - "6": 9, - "7": 9, - "12": 10, - "15": 11, - "16": 11, - "18": 12, - }, -} - -func (l *List) MakeTitleTable() { - l.Header.TitleTableOffset = l.GetCurrentSize() - - // Wii - l.GenerateTitleStruct(&gametdb.WiiTDB.Games, constants.Wii) - // DS - l.GenerateTitleStruct(&gametdb.DSTDB.Games, constants.NintendoDS) - // 3DS - l.GenerateTitleStruct(&gametdb.ThreeDSTDB.Games, constants.NintendoThreeDS) - - l.Header.NumberOfTitleTables = uint32(len(l.TitleTable)) -} - -func (l *List) GenerateTitleStruct(games *[]gametdb.Game, defaultTitleType constants.TitleType) { - // Internally only created once, but it doesn't hurt to have it on the stack. - // Required for generating a unique checksum for title ids. - crcTable := crc32.MakeTable(crc32.IEEE) - - for _, game := range *games { - if game.Locale == nil { - // Game doesn't exist for this region? - // Whatever the reason is, we have no metadata to use. - continue - } - - forcedRegion := game.Region - isForced := slices.Contains(constants.CanadaUSAIDs, game.ID[:4]) - - if isForced { - forcedRegion = "NTSC-U" - } - - if forcedRegion == regionToGameTDB[l.region] || game.Region == "ALL" { - titleType := defaultTitleType - // (Sketch) The first locale will always be English from what I have observed - title := game.Locale[0].Title - fullTitle := game.Locale[0].Title - synopsis := game.Locale[0].Synopsis - for _, locale := range game.Locale { - if locale.Language == langaugeToLocale[l.language] { - if game.Type != "" { - title = locale.Title - fullTitle = locale.Title - synopsis = locale.Synopsis - titleType = constants.TitleTypeMap[game.Type] - - if titleType == constants.NES && defaultTitleType == constants.NintendoThreeDS { - titleType = constants.NintendoThreeDS - } - } - } - } - - // We will not include mods, GameCube games, demos, DS Download Stations, or Pokemon distributions - if game.Type == "CUSTOM" || game.Type == "GameCube" || game.Type == "Homebrew" || titleType == constants.ThreeDSDownload || - strings.Contains(title, "(Demo)") || strings.Contains(title, "Download") || - strings.Contains(title, "Distribution") || strings.Contains(title, "DSi XL") || - strings.Contains(title, "Exclusive") || strings.Contains(title, "Toys R Us") || - strings.Contains(title, "GameStop") || strings.Contains(title, "Target") || - strings.Contains(title, "Best Buy") || strings.Contains(title, "Walmart") || - strings.Contains(title, "Limited Edition") || strings.Contains(title, "Collector's Edition") || - strings.Contains(title, "(Beta)") || strings.Contains(title, "Relay") || - slices.Contains(constants.DevAppIDs, game.ID[:4]) { - continue - } - - if !slices.Contains(regionToCodeTDB[l.region], game.ID[3]) { - continue - } - - var titleID [4]byte - copy(titleID[:], game.ID) - - // Wii, DS and 3DS games may share the same IDs are one another. XOR to avoid conflict. - id := crc32.Checksum([]byte(game.ID), crcTable) - if defaultTitleType == constants.NintendoDS { - id ^= 0x22222222 - } else if defaultTitleType == constants.NintendoThreeDS { - id ^= 0x33333333 - } - - var releaseYear uint16 = 0xFFFF - if game.ReleaseDate.Year != "" { - temp, _ := strconv.ParseUint(game.ReleaseDate.Year, 10, 32) - releaseYear = uint16(temp) - } - - var releaseMonth uint8 = 0xFF - if game.ReleaseDate.Month != "" { - temp, _ := strconv.ParseUint(game.ReleaseDate.Month, 10, 32) - releaseMonth = uint8(temp) - 1 - } - - var releaseDay uint8 = 0xFF - if game.ReleaseDate.Day != "" { - temp, _ := strconv.ParseUint(game.ReleaseDate.Day, 10, 32) - releaseDay = uint8(temp) - } - - subtitle := "" - if len(title) > 30 { - wrappedTitle := wordwrap.WrapString(title, 30) - for i, s := range strings.Split(wrappedTitle, "\n") { - switch i { - case 0: - title = s - break - case 1: - subtitle = s - break - default: - break - } - } - } - - var byteTitle [31]uint16 - tempTitle := utf16.Encode([]rune(title)) - copy(byteTitle[:], tempTitle) - - var byteSubtitle [31]uint16 - tempSubtitle := utf16.Encode([]rune(subtitle)) - copy(byteSubtitle[:], tempSubtitle) - - medal := constants.None - if num, ok := recommendations[game.ID[:4]]; ok { - medal = GetMedal(num.NumberOfRecommendations) - } - - companyOffset, companyID := l.GetCompany(&game) - table := TitleTable{ - ID: id, - TitleID: titleID, - TitleType: titleType, - Genre: l.SetGenre(&game), - CompanyOffset: companyOffset, - ReleaseYear: releaseYear, - ReleaseMonth: releaseMonth, - ReleaseDay: releaseDay, - RatingID: GetRatingID(game.Rating), - WithFriendsFemaleSecondRow: 0, - WithFriendsFemaleFirstRow: 0, - WithFriendsMaleSecondRow: 0, - WithFriendsMaleFirstRow: 0, - WithFriendsAllSecondRow: 0, - WithFriendsAllFirstRow: 0, - HardcoreFemaleSecondRow: 0, - HardcoreFemaleFirstRow: 0, - HardcoreMaleSecondRow: 0, - HardcoreMaleFirstRow: 0, - HardcoreAllSecondRow: 0, - HardcoreAllFirstRow: 0, - GamersFemaleSecondRow: 0, - GamersFemaleFirstRow: 0, - GamersMaleSecondRow: 0, - GamersMaleFirstRow: 0, - GamersAllSecondRow: 0, - GamersAllFirstRow: 0, - OtherFlags: 0, - Unknown7: [4]byte{0, 0, 0}, - Unknown8: 0, - MedalType: medal, - Unknown9: 222, - TitleName: byteTitle, - Subtitle: byteSubtitle, - ShortTitle: [31]uint16{}, - } - - table.PopulateCriteria(l, game.ID[:4]) - table.DetermineOtherFlags(game) - - l.TitleTable = append(l.TitleTable, table) - if !generateTitles { - continue - } - - i := info.Info{} - i.MakeHeader(titleID, game.Controllers.Players, companyID, table.TitleType, table.ReleaseYear, table.ReleaseMonth, table.ReleaseDay) - i.RatingID = table.RatingID - i.MakeInfo(id, &game, fullTitle, synopsis, l.region, l.language, defaultTitleType, recommendations) - } - } -} - -func GetRatingID(rating gametdb.Rating) uint8 { - if rating.Value == "" { - // Default to E/7/B - return 9 - } - - return gameTDBRatingToRatingID[rating.Type][rating.Value] -} - -func (l *List) GetCompany(game *gametdb.Game) (uint32, uint32) { - isDiscGame := false - companyID := "" - // This first method of retrieving the company is the most accurate. However, it only works with disc games. - if len(game.ID) != 4 { - isDiscGame = true - companyID = game.ID[4:] - } - - for i, company := range gametdb.WiiTDB.Companies.Companies { - if isDiscGame { - if companyID == company.Code { - intCompanyID, err := strconv.ParseUint(hex.EncodeToString([]byte(company.Code)), 16, 32) - common.CheckError(err) - return l.Header.CompanyTableOffset + (128 * uint32(i)), uint32(intCompanyID) - } - } else { - if strings.Contains(game.Publisher, company.Name) { - intcompanyID, err := strconv.ParseUint(hex.EncodeToString([]byte(company.Code)), 16, 32) - common.CheckError(err) - - return l.Header.CompanyTableOffset + (128 * uint32(i)), uint32(intcompanyID) - } - } - } - - // If all fails, default to Nintendo - return l.Header.CompanyTableOffset, 12337 -} - -func (l *List) SetGenre(game *gametdb.Game) [3]byte { - gameTDBToGenre := map[string]uint8{ - "arcade": 15, - "party": 13, - "puzzle": 5, - "action": 1, - "2D platformer": 1, - "3D platformer": 1, - "shooter": 12, - "first-person shooter": 12, - "third-person shooter": 12, - "rail shooter": 12, - "run and gun": 12, - "shoot 'em up": 12, - "stealth action": 1, - "survival horror": 1, - "sports": 4, - "adventure": 2, - "hidden object": 13, - "interactive fiction": 2, - "interactive movie": 2, - "point-and-click": 13, - "music": 10, - "rhythm": 10, - "dance": 10, - "karaoke": 10, - "racing": 7, - "fighting": 14, - "simulation": 9, - "role-playing": 6, - "strategy": 8, - "traditional": 11, - "health": 3, - "others": 13, - } - - genre := [3]byte{0, 0, 0} - for i, s := range strings.Split(game.Genre, ",") { - if i == 3 { - break - } - - genre[i] = gameTDBToGenre[s] - } - - return genre -} - -func (l *List) MakeNewTitleTable() { - // TODO: Figure out a way to get the newest titles - l.Header.NewTitleTableOffset = l.GetCurrentSize() - l.NewTitleTable = append(l.NewTitleTable, l.Header.TitleTableOffset) - l.Header.NumberOfNewTitleTables = 1 -} - -func GetMedal(numberOfTimesVotes int) constants.Medal { - if numberOfTimesVotes >= 50 { - return constants.Platinum - } else if numberOfTimesVotes >= 35 { - return constants.Gold - } else if numberOfTimesVotes >= 20 { - return constants.Silver - } else if numberOfTimesVotes >= 15 { - return constants.Bronze - } - - return constants.None -} - -// PopulateCriteria fills the bitfield entries in TitleTable -func (t *TitleTable) PopulateCriteria(l *List, gameId string) { - if _, ok := recommendations[gameId]; !ok { - return - } - - // First we will go after the `All` category. - // First 4 entries in the tables are the upper half. - for i := 0; i < 4; i++ { - // If it is none, then the bit will be set to nothing. - if recommendations[gameId].AllRecommendations[i].IsGamers == constants.True { - t.GamersAllFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].AllRecommendations[i].IsGamers == constants.False { - t.GamersAllFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } - - for i := 4; i < 8; i++ { - if recommendations[gameId].AllRecommendations[i].IsGamers == constants.True { - t.GamersAllSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].AllRecommendations[i].IsGamers == constants.False { - t.GamersAllSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } - - for i := 0; i < 4; i++ { - if recommendations[gameId].AllRecommendations[i].IsHardcore == constants.True { - t.HardcoreAllFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].AllRecommendations[i].IsHardcore == constants.False { - t.HardcoreAllFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } - - for i := 4; i < 8; i++ { - if recommendations[gameId].AllRecommendations[i].IsHardcore == constants.True { - t.HardcoreAllSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].AllRecommendations[i].IsHardcore == constants.False { - t.HardcoreAllSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } - - for i := 0; i < 4; i++ { - if recommendations[gameId].AllRecommendations[i].IsWithFriends == constants.True { - t.WithFriendsAllFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].AllRecommendations[i].IsWithFriends == constants.False { - t.WithFriendsAllFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } - - for i := 4; i < 8; i++ { - if recommendations[gameId].AllRecommendations[i].IsWithFriends == constants.True { - t.WithFriendsAllSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].AllRecommendations[i].IsWithFriends == constants.False { - t.WithFriendsAllSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } - - // Next is male. - for i := 0; i < 4; i++ { - // If it is none, then the bit will be set to nothing. - if recommendations[gameId].MaleRecommendations[i].IsGamers == constants.True { - t.GamersMaleFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].MaleRecommendations[i].IsGamers == constants.False { - t.GamersMaleFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } - - for i := 4; i < 8; i++ { - if recommendations[gameId].MaleRecommendations[i].IsGamers == constants.True { - t.GamersMaleSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].MaleRecommendations[i].IsGamers == constants.False { - t.GamersMaleSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } - - for i := 0; i < 4; i++ { - if recommendations[gameId].MaleRecommendations[i].IsHardcore == constants.True { - t.HardcoreMaleFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].MaleRecommendations[i].IsHardcore == constants.False { - t.HardcoreMaleFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } - - for i := 4; i < 8; i++ { - if recommendations[gameId].MaleRecommendations[i].IsHardcore == constants.True { - t.HardcoreMaleSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].MaleRecommendations[i].IsHardcore == constants.False { - t.HardcoreMaleSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } - - for i := 0; i < 4; i++ { - if recommendations[gameId].MaleRecommendations[i].IsWithFriends == constants.True { - t.WithFriendsMaleFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].MaleRecommendations[i].IsWithFriends == constants.False { - t.WithFriendsMaleFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } - - for i := 4; i < 8; i++ { - if recommendations[gameId].MaleRecommendations[i].IsWithFriends == constants.True { - t.WithFriendsMaleSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].MaleRecommendations[i].IsWithFriends == constants.False { - t.WithFriendsMaleSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } - - // Finally is female - for i := 0; i < 4; i++ { - // If it is none, then the bit will be set to nothing. - if recommendations[gameId].FemaleRecommendations[i].IsGamers == constants.True { - t.GamersFemaleFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].FemaleRecommendations[i].IsGamers == constants.False { - t.GamersFemaleFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } - - for i := 4; i < 8; i++ { - if recommendations[gameId].FemaleRecommendations[i].IsGamers == constants.True { - t.GamersFemaleSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].FemaleRecommendations[i].IsGamers == constants.False { - t.GamersFemaleSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } - - for i := 0; i < 4; i++ { - if recommendations[gameId].FemaleRecommendations[i].IsHardcore == constants.True { - t.HardcoreFemaleFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].FemaleRecommendations[i].IsHardcore == constants.False { - t.HardcoreFemaleFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } - - for i := 4; i < 8; i++ { - if recommendations[gameId].FemaleRecommendations[i].IsHardcore == constants.True { - t.HardcoreFemaleSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].FemaleRecommendations[i].IsHardcore == constants.False { - t.HardcoreFemaleSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } - - for i := 0; i < 4; i++ { - if recommendations[gameId].FemaleRecommendations[i].IsWithFriends == constants.True { - t.WithFriendsFemaleFirstRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].FemaleRecommendations[i].IsWithFriends == constants.False { - t.WithFriendsFemaleFirstRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } - - for i := 4; i < 8; i++ { - if recommendations[gameId].FemaleRecommendations[i].IsWithFriends == constants.True { - t.WithFriendsFemaleSecondRow |= uint8(int(math.Pow(2, float64(i))) << i) - } else if recommendations[gameId].FemaleRecommendations[i].IsWithFriends == constants.False { - t.WithFriendsFemaleSecondRow |= uint8(int(math.Pow(2, float64(i+1))) << i) - } - } -} - -func (t *TitleTable) DetermineOtherFlags(game gametdb.Game) { - value := 0xFF - mask := 0 - - // One player or Multiplayer - if game.Controllers.Players > 1 { - mask |= 1 - } - - // Is the game online - isOnline := false - for _, s := range game.Features.Feature { - if strings.Contains(s, "online") { - isOnline = true - } - } - - if isOnline { - mask |= 12 - } - - // TODO: We don't have titles with any videos at the moment - t.OtherFlags = uint8(value & mask) -} From a7286bae0ff587f7909fc4d9c593ba50292ea4b9 Mon Sep 17 00:00:00 2001 From: Barcia04 <98990328+Barcia04@users.noreply.github.com> Date: Sun, 22 Feb 2026 22:41:48 +0100 Subject: [PATCH 7/8] Added missing more Canada IDs that are labeled as PAL Added additional IDs to the CanadaUSAIDs list. --- constants/const.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants/const.go b/constants/const.go index 38081655..e8cbcd28 100644 --- a/constants/const.go +++ b/constants/const.go @@ -186,7 +186,7 @@ var DevAppIDs = []string{ } var CanadaUSAIDs = []string{ - "VT3Z", "B5BL", "C62L", "B6RX", + "VT3Z", "B5BL", "C62L", "B6RX", "BDYX", "BDPL", "BJBX", "BJCZ", "B58L", "BD4X", "B5CX", } From 18bbbcf87d47388f04371701dcb7d8dfbc49dfed Mon Sep 17 00:00:00 2001 From: Barcia04 <98990328+Barcia04@users.noreply.github.com> Date: Wed, 18 Mar 2026 23:20:09 +0100 Subject: [PATCH 8/8] Removed the exclusive region of Cooking Guide/Personal Trainer: Cooking in Touch! Generationsr: Cooking Removed duplicate entries and cleaned up the TouchGenIDs list, due to being in the Gen in all regions not Europe only. --- constants/const.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants/const.go b/constants/const.go index e8cbcd28..1c510738 100644 --- a/constants/const.go +++ b/constants/const.go @@ -175,7 +175,7 @@ var TitleTypesData = []TitleTypeData{ } var TouchGenIDs = []string{ - "YBN", "VAAE", "VAAV", "AYA", "AND", "ANM", "ATD", "ADJ", "BET", "CNVP", "CNVF", "CNVD", "CNVI", "CNVS", "ANH", "ANG", "AUA", "AUB", "AUC", "AUD", "AUE", "AIX", "AIZ", + "YBN", "VAAE", "VAAV", "AYA", "AND", "ANM", "ATD", "ADJ", "BET", "CNV", "ANH", "ANG", "AUA", "AUB", "AUC", "AUD", "AUE", "AIX", "AIZ", "YCU", "ATI", "AOS", "AG3", "AWIE", "APLE", "AJQE", "AJQJ","CM7", "BKCE", "ATG", "AVM", "AD5", "AD2", "ADG", "AD7", "AD3", "IMW", "IA8", "C6P", "AXP", "A4VJ", "AJM", "AOI", "A8NE", "A2Y", "AZI", "ASQ", "AJY", "ATR", "ARJ", "AGFE", "USK", "VET", "YFCE", "YLZP", "YLZX", "YLZJ", "YNU", "SUPP", "SUPJ", "RFN", "RFP", "R64", "RYW", "RHAP", "RHAJ", "RJT", "RSPP", "RSPJ", "RZTP", "RFBP", "R4EP", "RNOP", "RTYP", "SP2P",