From 9cfaebee2bc7754c2cf3514c35e7049db6b49b72 Mon Sep 17 00:00:00 2001 From: pawndev Date: Sun, 4 Jan 2026 15:20:53 +0100 Subject: [PATCH 1/3] feat(cfw): Init support for crossmix --- .github/workflows/release.yml | 9 +- cfw/cfw.go | 34 +++- cfw/crossmix/platforms.json | 252 +++++++++++++++++++++++++++++ cfw/crossmix/save_directories.json | 252 +++++++++++++++++++++++++++++ scripts/CrossMix/config.json | 8 + scripts/CrossMix/grout.png | Bin 0 -> 4993 bytes scripts/CrossMix/launch.sh | 8 + scripts/Spruce/config.json | 2 +- taskfile.yml | 12 ++ 9 files changed, 568 insertions(+), 9 deletions(-) create mode 100644 cfw/crossmix/platforms.json create mode 100644 cfw/crossmix/save_directories.json create mode 100644 scripts/CrossMix/config.json create mode 100644 scripts/CrossMix/grout.png create mode 100644 scripts/CrossMix/launch.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bab5377..77ec648 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -73,7 +73,7 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Build and Package - run: task build extract package-next package-muos package-knulli package-spruce + run: task build extract package-next package-muos package-knulli package-spruce package-crossmix - name: Create NextUI distribution run: | @@ -97,6 +97,12 @@ jobs: zip -r Grout.spruce.zip Grout mv Grout.spruce.zip ../Grout.spruce.zip + - name: Create crossmix distribution + run: | + cd build/CrossMix + zip -r Grout.crossmix.zip Grout + mv Grout.crossmix.zip ../Grout.crossmix.zip + - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: @@ -112,6 +118,7 @@ jobs: build/Grout.pak.zip build/Grout.muxapp build/Grout.spruce.zip + build/Grout.crossmix.zip build/Grout-Knulli.zip build/grout draft: false diff --git a/cfw/cfw.go b/cfw/cfw.go index b08d665..dcb22e1 100644 --- a/cfw/cfw.go +++ b/cfw/cfw.go @@ -12,10 +12,11 @@ import ( type CFW string const ( - NextUI CFW = "NEXTUI" - MuOS CFW = "MUOS" - Knulli CFW = "KNULLI" - Spruce CFW = "SPRUCE" + NextUI CFW = "NEXTUI" + MuOS CFW = "MUOS" + Knulli CFW = "KNULLI" + Spruce CFW = "SPRUCE" + CrossMix CFW = "CROSSMIX" ) var ( @@ -29,6 +30,9 @@ var ( SprucePlatforms = mustLoadJSONMap[string, []string]("spruce/platforms.json") SpruceSaveDirectories = mustLoadJSONMap[string, []string]("spruce/save_directories.json") + CrossMixPlatforms = mustLoadJSONMap[string, []string]("crossmix/platforms.json") + CrossMixSaveDirectories = mustLoadJSONMap[string, []string]("crossmix/save_directories.json") + KnulliPlatforms = mustLoadJSONMap[string, []string]("knulli/platforms.json") ) @@ -41,11 +45,11 @@ func GetCFW() CFW { cfw := CFW(cfwEnv) switch cfw { - case MuOS, NextUI, Knulli, Spruce: + case MuOS, NextUI, Knulli, Spruce, CrossMix: return cfw default: log.SetOutput(os.Stderr) - log.Fatalf("Unsupported CFW: '%s'. Valid options: NextUI, muOS, Knulli, Spruce", cfwEnv) + log.Fatalf("Unsupported CFW: '%s'. Valid options: NextUI, muOS, Knulli, Spruce, CrossMix", cfwEnv) return "" } } @@ -66,6 +70,8 @@ func GetRomDirectory() string { return filepath.Join(getBasePath(Knulli), "roms") case Spruce: return filepath.Join(getBasePath(Spruce), "Roms") + case CrossMix: + return filepath.Join(getBasePath(CrossMix), "Roms") } return "" @@ -83,6 +89,8 @@ func GetBIOSDirectory() string { return filepath.Join(getBasePath(Knulli), "bios") case Spruce: return filepath.Join(getBasePath(Spruce), "BIOS") + case CrossMix: + return filepath.Join(getBasePath(CrossMix), "BIOS") } return "" @@ -117,6 +125,8 @@ func GetPlatformMap(c CFW) map[string][]string { return KnulliPlatforms case Spruce: return SprucePlatforms + case CrossMix: + return CrossMixPlatforms default: return nil } @@ -132,6 +142,8 @@ func EmulatorFolderMap(c CFW) map[string][]string { return KnulliPlatforms case Spruce: return SpruceSaveDirectories + case CrossMix: + return CrossMixSaveDirectories default: return nil } @@ -192,6 +204,9 @@ func getBasePath(cfw CFW) string { case Spruce: return "/mnt/SDCARD" + + case CrossMix: + return "/mnt/SDCARD" default: return "" } @@ -212,6 +227,8 @@ func BaseSavePath() string { return filepath.Join(getBasePath(cfw), "saves") case Spruce: return filepath.Join(getBasePath(cfw), "Saves", "saves") + case CrossMix: + return filepath.Join(getBasePath(cfw), "RetroArch", ".retroarch", "saves") } return "" @@ -230,13 +247,16 @@ func GetPlatformRomDirectory(relativePath, platformSlug string) string { // GetArtDirectory returns the artwork directory for a platform. func GetArtDirectory(romDir string, platformSlug, platformName string) string { - switch GetCFW() { + cfw := GetCFW() + switch cfw { case NextUI: return filepath.Join(romDir, ".media") case Knulli: return filepath.Join(romDir, "images") case Spruce: return filepath.Join(romDir, "Imgs") + case CrossMix: + return filepath.Join(getBasePath(cfw), "Imgs", platformSlug) case MuOS: systemName, exists := MuOSArtDirectory[platformSlug] if !exists { diff --git a/cfw/crossmix/platforms.json b/cfw/crossmix/platforms.json new file mode 100644 index 0000000..6674bee --- /dev/null +++ b/cfw/crossmix/platforms.json @@ -0,0 +1,252 @@ +{ + "3do": [ + "PANASONIC" + ], + "3ds": [], + "acpc": [ + "CPC" + ], + "amiga": [ + "AMIGA" + ], + "arcade": [ + "MAME", + "MAME2003PLUS", + "MAME2010", + "DAPHNE" + ], + "arduboy": [ + "ARDUBOY" + ], + "atari-st": [ + "ATARIST" + ], + "atari2600": [ + "ATARI2600" + ], + "atari5200": [ + "ATARI5200" + ], + "atari7800": [ + "ATARI7800" + ], + "c128": [ + "AMIGA", + "AMIGACD" + ], + "c64": [ + "C64" + ], + "cave-story": [ + "CAVESTORY" + ], + "cbm-ii": [ + "VIC20" + ], + "chailove": [ + "CHAILOVE" + ], + "chip-8": [], + "colecovision": [ + "COLECO", + "COLSGM" + ], + "cpet": [ + "CPET" + ], + "dc": [ + "DC" + ], + "doom": [ + "DOOM" + ], + "dos": [ + "DOS" + ], + "fairchild-channel-f": [ + "FAIRCHILD" + ], + "famicom": [ + "FC" + ], + "fds": [ + "FDS" + ], + "g-and-w": [ + "GW" + ], + "galaksija": [], + "gamegear": [ + "GG" + ], + "gb": [ + "GB" + ], + "gba": [ + "GBA" + ], + "gbc": [ + "GBC" + ], + "genesis": [ + "MD" + ], + "intellivision": [ + "INTELLIVISION" + ], + "j2me": [], + "jaguar": [], + "karaoke": [], + "lowres": [ + "LOWRESNX" + ], + "lua": [ + "LUTRO" + ], + "lynx": [ + "LYNX" + ], + "media-player": [], + "mega-duck-slash-cougar-boy": [ + "MEGADUCK" + ], + "msx": [ + "MSX" + ], + "n64": [ + "N64" + ], + "naomi": [ + "NAOMI" + ], + "nds": [ + "NDS" + ], + "neo-geo-cd": [ + "NEOCD" + ], + "neo-geo-pocket": [ + "NEOGEO" + ], + "neo-geo-pocket-color": [ + "NEOGEO" + ], + "neogeoaes": [ + "NEOGEO" + ], + "neogeomvs": [ + "NEOGEO" + ], + "nes": [ + "FC" + ], + "odyssey": [ + "ODYSSEY" + ], + "onscripter": [], + "openbor": [ + "OPENBOR" + ], + "pc-8000": [ + "PC88" + ], + "pc-9800-series": [], + "pc-fx": [], + "philips-cd-i": [], + "pico": [], + "pico-8": [ + "PICO" + ], + "pokemon-mini": [ + "POKEMINI" + ], + "ports": [], + "ps2": [], + "psp": [ + "PSP" + ], + "psx": [ + "PS" + ], + "quake": [ + "TYRQUAKE" + ], + "rpg-maker": [], + "saturn": [ + "SATURN" + ], + "scummvm": [ + "SCUMMVM" + ], + "sega32": [ + "SEGA32X" + ], + "segacd": [ + "SEGACD" + ], + "sfam": [ + "SFC" + ], + "sg1000": [ + "SG1000" + ], + "sharp-x68000": [ + "X68000" + ], + "sms": [ + "MS" + ], + "snes": [ + "SFC" + ], + "supergrafx": [ + "SFX" + ], + "supervision": [ + "SUPERVISION" + ], + "tg16": [ + "PCE" + ], + "ti-83": [ + "TI83" + ], + "tic-80": [ + "TIC" + ], + "turbografx-cd": [ + "PCECD" + ], + "uzebox": [ + "UZEBOX" + ], + "vectrex": [ + "VECTREX" + ], + "vemulator": [ + "VMU" + ], + "vic-20": [ + "VIC20" + ], + "vircon-32": [], + "virtualboy": [ + "VB" + ], + "wasm-4": [], + "wolfenstein-3d": [], + "wonderswan": [ + "WS" + ], + "wonderswan-color": [ + "WS", + "WSC" + ], + "x1": [ + "X1" + ], + "zx81": [], + "zxs": [ + "ZXS" + ] +} diff --git a/cfw/crossmix/save_directories.json b/cfw/crossmix/save_directories.json new file mode 100644 index 0000000..6674bee --- /dev/null +++ b/cfw/crossmix/save_directories.json @@ -0,0 +1,252 @@ +{ + "3do": [ + "PANASONIC" + ], + "3ds": [], + "acpc": [ + "CPC" + ], + "amiga": [ + "AMIGA" + ], + "arcade": [ + "MAME", + "MAME2003PLUS", + "MAME2010", + "DAPHNE" + ], + "arduboy": [ + "ARDUBOY" + ], + "atari-st": [ + "ATARIST" + ], + "atari2600": [ + "ATARI2600" + ], + "atari5200": [ + "ATARI5200" + ], + "atari7800": [ + "ATARI7800" + ], + "c128": [ + "AMIGA", + "AMIGACD" + ], + "c64": [ + "C64" + ], + "cave-story": [ + "CAVESTORY" + ], + "cbm-ii": [ + "VIC20" + ], + "chailove": [ + "CHAILOVE" + ], + "chip-8": [], + "colecovision": [ + "COLECO", + "COLSGM" + ], + "cpet": [ + "CPET" + ], + "dc": [ + "DC" + ], + "doom": [ + "DOOM" + ], + "dos": [ + "DOS" + ], + "fairchild-channel-f": [ + "FAIRCHILD" + ], + "famicom": [ + "FC" + ], + "fds": [ + "FDS" + ], + "g-and-w": [ + "GW" + ], + "galaksija": [], + "gamegear": [ + "GG" + ], + "gb": [ + "GB" + ], + "gba": [ + "GBA" + ], + "gbc": [ + "GBC" + ], + "genesis": [ + "MD" + ], + "intellivision": [ + "INTELLIVISION" + ], + "j2me": [], + "jaguar": [], + "karaoke": [], + "lowres": [ + "LOWRESNX" + ], + "lua": [ + "LUTRO" + ], + "lynx": [ + "LYNX" + ], + "media-player": [], + "mega-duck-slash-cougar-boy": [ + "MEGADUCK" + ], + "msx": [ + "MSX" + ], + "n64": [ + "N64" + ], + "naomi": [ + "NAOMI" + ], + "nds": [ + "NDS" + ], + "neo-geo-cd": [ + "NEOCD" + ], + "neo-geo-pocket": [ + "NEOGEO" + ], + "neo-geo-pocket-color": [ + "NEOGEO" + ], + "neogeoaes": [ + "NEOGEO" + ], + "neogeomvs": [ + "NEOGEO" + ], + "nes": [ + "FC" + ], + "odyssey": [ + "ODYSSEY" + ], + "onscripter": [], + "openbor": [ + "OPENBOR" + ], + "pc-8000": [ + "PC88" + ], + "pc-9800-series": [], + "pc-fx": [], + "philips-cd-i": [], + "pico": [], + "pico-8": [ + "PICO" + ], + "pokemon-mini": [ + "POKEMINI" + ], + "ports": [], + "ps2": [], + "psp": [ + "PSP" + ], + "psx": [ + "PS" + ], + "quake": [ + "TYRQUAKE" + ], + "rpg-maker": [], + "saturn": [ + "SATURN" + ], + "scummvm": [ + "SCUMMVM" + ], + "sega32": [ + "SEGA32X" + ], + "segacd": [ + "SEGACD" + ], + "sfam": [ + "SFC" + ], + "sg1000": [ + "SG1000" + ], + "sharp-x68000": [ + "X68000" + ], + "sms": [ + "MS" + ], + "snes": [ + "SFC" + ], + "supergrafx": [ + "SFX" + ], + "supervision": [ + "SUPERVISION" + ], + "tg16": [ + "PCE" + ], + "ti-83": [ + "TI83" + ], + "tic-80": [ + "TIC" + ], + "turbografx-cd": [ + "PCECD" + ], + "uzebox": [ + "UZEBOX" + ], + "vectrex": [ + "VECTREX" + ], + "vemulator": [ + "VMU" + ], + "vic-20": [ + "VIC20" + ], + "vircon-32": [], + "virtualboy": [ + "VB" + ], + "wasm-4": [], + "wolfenstein-3d": [], + "wonderswan": [ + "WS" + ], + "wonderswan-color": [ + "WS", + "WSC" + ], + "x1": [ + "X1" + ], + "zx81": [], + "zxs": [ + "ZXS" + ] +} diff --git a/scripts/CrossMix/config.json b/scripts/CrossMix/config.json new file mode 100644 index 0000000..db852c8 --- /dev/null +++ b/scripts/CrossMix/config.json @@ -0,0 +1,8 @@ +{ + "label": "Grout", + "icon": "/mnt/SDCARD/Apps/Grout/grout.png", + "iconsel": "/mnt/SDCARD/Apps/Grout/grout.png", + "launch": "launch.sh", + "description": "A RomM client for NextUI, muOS, spruce, crossmix and knulli", + "devices": ["MIYOO_A30", "TRIMUI_BRICK", "TRIMUI_SMART_PRO_S", "TRIMUI_SMART_PRO","MIYOO_FLIP"] +} \ No newline at end of file diff --git a/scripts/CrossMix/grout.png b/scripts/CrossMix/grout.png new file mode 100644 index 0000000000000000000000000000000000000000..435b454333b8a2a6bd663f654d76ba3346d9dcc5 GIT binary patch literal 4993 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 Date: Sun, 4 Jan 2026 18:35:39 +0100 Subject: [PATCH 2/3] fix(cfw/crossmix): Don't forget to embed json --- cfw/json_loader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfw/json_loader.go b/cfw/json_loader.go index d4322fe..714ab9c 100644 --- a/cfw/json_loader.go +++ b/cfw/json_loader.go @@ -6,7 +6,7 @@ import ( "grout/internal/jsonutil" ) -//go:embed nextui muos knulli spruce +//go:embed nextui muos knulli spruce crossmix var embeddedFiles embed.FS func mustLoadJSONMap[K comparable, V any](path string) map[K]V { From c503bcc0e40bdf0146622149640db7054279c0f6 Mon Sep 17 00:00:00 2001 From: pawndev Date: Mon, 5 Jan 2026 10:52:20 +0100 Subject: [PATCH 3/3] fix(cfw/crossmix): Correct save mapping for gba --- cfw/crossmix/save_directories.json | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cfw/crossmix/save_directories.json b/cfw/crossmix/save_directories.json index 6674bee..94caa05 100644 --- a/cfw/crossmix/save_directories.json +++ b/cfw/crossmix/save_directories.json @@ -1,6 +1,6 @@ { "3do": [ - "PANASONIC" + "PANASONIC/Opera" ], "3ds": [], "acpc": [ @@ -83,7 +83,12 @@ "GB" ], "gba": [ - "GBA" + "GBA/gpSP", + "GBA/mGBA", + "GBA/VAB-M", + "GBA/VBA-Next", + "GBA/Meteor", + "GBA/mGBA - Standalone" ], "gbc": [ "GBC"