From 8f3a596efa8a4cad9c1707d13f507055bcc04604 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sat, 5 Oct 2024 21:08:31 +0000 Subject: [PATCH 001/210] update workspace --- theme-selector-workspace.code-workspace | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/theme-selector-workspace.code-workspace b/theme-selector-workspace.code-workspace index 5c989ec..3456332 100644 --- a/theme-selector-workspace.code-workspace +++ b/theme-selector-workspace.code-workspace @@ -1,7 +1,7 @@ { "folders": [ { - "name": "om_theme-selectorv2", + "name": "om_theme-selector", "path": "." }, { @@ -27,9 +27,6 @@ "c[0-9]*": "properties" }, "explorer.confirmDragAndDrop": false, - "prettier.disableLanguages": [ - "html" - ], "git.confirmSync": false, "vsicons.projectDetection.autoReload": true, "vsicons.dontShowNewVersionMessage": true, @@ -38,7 +35,6 @@ "workbench.iconTheme": "vscode-icons", "beautify.config": "", "http.proxyAuthorization": null, - "workbench.colorTheme": "Flatland Monokai", } } \ No newline at end of file From 9f840d8c60daeb0a554dad9f5a6e86f0faebb3ac Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sat, 5 Oct 2024 21:29:47 +0000 Subject: [PATCH 002/210] update copyright --- mod/etc/options_menu/scripts/om_functions | 2 +- mod/etc/options_menu/scripts/om_themeselector | 2 +- mod/etc/options_menu/themes/creator/scripts/om_applytheme | 2 +- mod/etc/options_menu/themes/creator/scripts/om_diy_asset | 2 +- mod/etc/options_menu/themes/creator/scripts/om_preview | 2 +- mod/etc/options_menu/themes/creator/scripts/om_refresh | 2 +- mod/etc/options_menu/themes/creator/scripts/om_removediy | 2 +- .../themes/creator/sprites/colors/scripts/om_return | 2 +- .../themes/creator/sprites/colors_nes/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/luigi/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/mario/scripts/om_return | 2 +- .../themes/creator/sprites/packed/scripts/om_return | 2 +- .../themes/creator/sprites/packed_nes/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/pole/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/sbm/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/wall/scripts/om_return | 2 +- .../themes/creator/sprites/wall_far/scripts/om_return | 2 +- .../themes/creator/sprites/wall_near/scripts/om_return | 2 +- mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads | 2 +- mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes | 2 +- mod/etc/options_menu/themes/downloads/scripts/om_return | 2 +- mod/etc/options_menu/themes/scripts/om_clearList | 2 +- mod/etc/options_menu/themes/scripts/om_getThemes | 2 +- mod/etc/options_menu/themes/scripts/om_license | 2 +- mod/etc/options_menu/themes/scripts/om_overlaythmcript | 2 +- mod/etc/options_menu/themes/scripts/om_restorethumb | 2 +- mod/etc/options_menu/themes/scripts/om_settings | 2 +- mod/etc/options_menu/themes/scripts/om_themeCreator | 2 +- mod/etc/options_menu/themes/scripts/om_themescript | 2 +- mod/etc/options_menu/themes/scripts/om_vars | 2 +- mod/etc/options_menu/themes/settings/scripts/om_audio | 2 +- mod/etc/options_menu/themes/settings/scripts/om_reset | 2 +- mod/etc/options_menu/themes/settings/scripts/om_systemcleanup | 2 +- mod/etc/options_menu/themes/settings/scripts/om_theme | 2 +- mod/install | 2 +- mod/uninstall | 2 +- 36 files changed, 36 insertions(+), 36 deletions(-) diff --git a/mod/etc/options_menu/scripts/om_functions b/mod/etc/options_menu/scripts/om_functions index bc98059..cba5fc1 100644 --- a/mod/etc/options_menu/scripts/om_functions +++ b/mod/etc/options_menu/scripts/om_functions @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC2154,SC1091,SC2034,SC2068,SC2039 -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/scripts/om_themeselector b/mod/etc/options_menu/scripts/om_themeselector index 0883ec8..2a3b57e 100644 --- a/mod/etc/options_menu/scripts/om_themeselector +++ b/mod/etc/options_menu/scripts/om_themeselector @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC1090,SC2039,SC2086,SC2154 -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/scripts/om_applytheme b/mod/etc/options_menu/themes/creator/scripts/om_applytheme index 43df6ed..c811bde 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_applytheme +++ b/mod/etc/options_menu/themes/creator/scripts/om_applytheme @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC1090,SC2039,SC2086,SC2154 -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset index f3335ab..6340a2a 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset +++ b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/scripts/om_preview b/mod/etc/options_menu/themes/creator/scripts/om_preview index 5d2397c..314d17e 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_preview +++ b/mod/etc/options_menu/themes/creator/scripts/om_preview @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/scripts/om_refresh b/mod/etc/options_menu/themes/creator/scripts/om_refresh index 30a6c32..62aebab 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_refresh +++ b/mod/etc/options_menu/themes/creator/scripts/om_refresh @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/scripts/om_removediy b/mod/etc/options_menu/themes/creator/scripts/om_removediy index 36ba932..73ae9f3 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_removediy +++ b/mod/etc/options_menu/themes/creator/scripts/om_removediy @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return index cb779cf..66ff2b9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return index cb779cf..66ff2b9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return index cb779cf..66ff2b9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return index cb779cf..66ff2b9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return index cb779cf..66ff2b9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return index cb779cf..66ff2b9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return index cb779cf..66ff2b9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return index cb779cf..66ff2b9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return index cb779cf..66ff2b9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return index cb779cf..66ff2b9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return index cb779cf..66ff2b9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads index 5f4d29b..e726ed5 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads +++ b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes index 6dd83e5..874e8b0 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes +++ b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_return b/mod/etc/options_menu/themes/downloads/scripts/om_return index 64b4c00..c02eeac 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_return +++ b/mod/etc/options_menu/themes/downloads/scripts/om_return @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_clearList b/mod/etc/options_menu/themes/scripts/om_clearList index 35fb34f..2852460 100644 --- a/mod/etc/options_menu/themes/scripts/om_clearList +++ b/mod/etc/options_menu/themes/scripts/om_clearList @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_getThemes b/mod/etc/options_menu/themes/scripts/om_getThemes index f6108fe..75abb58 100644 --- a/mod/etc/options_menu/themes/scripts/om_getThemes +++ b/mod/etc/options_menu/themes/scripts/om_getThemes @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_license b/mod/etc/options_menu/themes/scripts/om_license index 65d3ae0..688afc8 100644 --- a/mod/etc/options_menu/themes/scripts/om_license +++ b/mod/etc/options_menu/themes/scripts/om_license @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_overlaythmcript b/mod/etc/options_menu/themes/scripts/om_overlaythmcript index bf5defa..48ed31b 100644 --- a/mod/etc/options_menu/themes/scripts/om_overlaythmcript +++ b/mod/etc/options_menu/themes/scripts/om_overlaythmcript @@ -1,6 +1,6 @@ #!/bin/bash # shellcheck disable=SC2154,SC2038,SC2143 -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_restorethumb b/mod/etc/options_menu/themes/scripts/om_restorethumb index ebdb6d7..d115b36 100644 --- a/mod/etc/options_menu/themes/scripts/om_restorethumb +++ b/mod/etc/options_menu/themes/scripts/om_restorethumb @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_settings b/mod/etc/options_menu/themes/scripts/om_settings index d023f2d..cf8209c 100644 --- a/mod/etc/options_menu/themes/scripts/om_settings +++ b/mod/etc/options_menu/themes/scripts/om_settings @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_themeCreator b/mod/etc/options_menu/themes/scripts/om_themeCreator index c643772..ac09f9a 100644 --- a/mod/etc/options_menu/themes/scripts/om_themeCreator +++ b/mod/etc/options_menu/themes/scripts/om_themeCreator @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_themescript b/mod/etc/options_menu/themes/scripts/om_themescript index a165015..1ff9faf 100644 --- a/mod/etc/options_menu/themes/scripts/om_themescript +++ b/mod/etc/options_menu/themes/scripts/om_themescript @@ -1,6 +1,6 @@ #!/bin/bash # shellcheck disable=SC2154,SC2038,SC2143 -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 2c19872..6a6d2ce 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC2039 -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/settings/scripts/om_audio b/mod/etc/options_menu/themes/settings/scripts/om_audio index 4550b6a..cce5402 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_audio +++ b/mod/etc/options_menu/themes/settings/scripts/om_audio @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC1090,SC2039,SC2086,SC2154 -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/settings/scripts/om_reset b/mod/etc/options_menu/themes/settings/scripts/om_reset index 7e1cf79..0439865 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_reset +++ b/mod/etc/options_menu/themes/settings/scripts/om_reset @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup index 5e19786..498fbb6 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup +++ b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC1090,SC1091,SC2039,SC2086,SC2154,SC3046 -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/settings/scripts/om_theme b/mod/etc/options_menu/themes/settings/scripts/om_theme index 1f36cca..bd05f99 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_theme +++ b/mod/etc/options_menu/themes/settings/scripts/om_theme @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/install b/mod/install index 72dd308..1907cbe 100644 --- a/mod/install +++ b/mod/install @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC2154 -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/uninstall b/mod/uninstall index 6645428..8fec0f0 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2154,SC2068 # -# Copyright (c) 2021 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From d9b8fe816227d1147815460e2cf0b3002542bd46 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sat, 5 Oct 2024 23:16:29 +0000 Subject: [PATCH 003/210] update copyright, update shellcheck source --- mod/etc/options_menu/scripts/om_functions | 3 ++- mod/etc/options_menu/scripts/om_themeselector | 5 +++-- mod/etc/options_menu/themes/creator/scripts/om_applytheme | 5 +++-- mod/etc/options_menu/themes/creator/scripts/om_diy_asset | 4 +++- mod/etc/options_menu/themes/creator/scripts/om_preview | 4 +++- mod/etc/options_menu/themes/creator/scripts/om_refresh | 4 +++- mod/etc/options_menu/themes/creator/scripts/om_removediy | 4 +++- .../themes/creator/sprites/colors/scripts/om_return | 4 +++- .../themes/creator/sprites/colors_nes/scripts/om_return | 4 +++- .../themes/creator/sprites/luigi/scripts/om_return | 4 +++- .../themes/creator/sprites/mario/scripts/om_return | 4 +++- .../themes/creator/sprites/packed/scripts/om_return | 4 +++- .../themes/creator/sprites/packed_nes/scripts/om_return | 4 +++- .../themes/creator/sprites/pole/scripts/om_return | 4 +++- .../themes/creator/sprites/sbm/scripts/om_return | 4 +++- .../themes/creator/sprites/wall/scripts/om_return | 4 +++- .../themes/creator/sprites/wall_far/scripts/om_return | 4 +++- .../themes/creator/sprites/wall_near/scripts/om_return | 4 +++- .../options_menu/themes/downloads/scripts/om_clearDownloads | 4 +++- .../options_menu/themes/downloads/scripts/om_refreshThemes | 4 +++- mod/etc/options_menu/themes/downloads/scripts/om_return | 4 +++- mod/etc/options_menu/themes/scripts/om_clearList | 6 +++--- mod/etc/options_menu/themes/scripts/om_getThemes | 4 +++- mod/etc/options_menu/themes/scripts/om_license | 4 +++- mod/etc/options_menu/themes/scripts/om_overlaythmcript | 2 +- mod/etc/options_menu/themes/scripts/om_restorethumb | 4 +++- mod/etc/options_menu/themes/scripts/om_settings | 4 +++- mod/etc/options_menu/themes/scripts/om_themeCreator | 4 +++- mod/etc/options_menu/themes/scripts/om_themescript | 2 +- mod/etc/options_menu/themes/scripts/om_vars | 5 +++-- mod/etc/options_menu/themes/settings/scripts/om_audio | 5 +++-- mod/etc/options_menu/themes/settings/scripts/om_reset | 5 +++-- .../options_menu/themes/settings/scripts/om_systemcleanup | 5 +++-- mod/etc/options_menu/themes/settings/scripts/om_theme | 5 +++-- mod/install | 2 +- mod/uninstall | 2 +- 36 files changed, 99 insertions(+), 45 deletions(-) diff --git a/mod/etc/options_menu/scripts/om_functions b/mod/etc/options_menu/scripts/om_functions index cba5fc1..a6023fc 100644 --- a/mod/etc/options_menu/scripts/om_functions +++ b/mod/etc/options_menu/scripts/om_functions @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC2154,SC1091,SC2034,SC2068,SC2039 -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=preinit source /etc/preinit script_init diff --git a/mod/etc/options_menu/scripts/om_themeselector b/mod/etc/options_menu/scripts/om_themeselector index 2a3b57e..cc2145d 100644 --- a/mod/etc/options_menu/scripts/om_themeselector +++ b/mod/etc/options_menu/scripts/om_themeselector @@ -1,6 +1,6 @@ #!/bin/sh -# shellcheck disable=SC1090,SC2039,SC2086,SC2154 -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# shellcheck disable=SC1091,SC2039,SC2086,SC2154,SC3046 +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/scripts/om_applytheme b/mod/etc/options_menu/themes/creator/scripts/om_applytheme index c811bde..6c99ea1 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_applytheme +++ b/mod/etc/options_menu/themes/creator/scripts/om_applytheme @@ -1,6 +1,6 @@ #!/bin/sh -# shellcheck disable=SC1090,SC2039,SC2086,SC2154 -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# shellcheck disable=SC2086,SC2154 +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset index 6340a2a..e9b8193 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset +++ b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset @@ -1,6 +1,7 @@ #!/bin/bash +# shellcheck disable=SC2086,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/scripts/om_preview b/mod/etc/options_menu/themes/creator/scripts/om_preview index 314d17e..2bd5a60 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_preview +++ b/mod/etc/options_menu/themes/creator/scripts/om_preview @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/scripts/om_refresh b/mod/etc/options_menu/themes/creator/scripts/om_refresh index 62aebab..4cb065c 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_refresh +++ b/mod/etc/options_menu/themes/creator/scripts/om_refresh @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/scripts/om_removediy b/mod/etc/options_menu/themes/creator/scripts/om_removediy index 73ae9f3..4645257 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_removediy +++ b/mod/etc/options_menu/themes/creator/scripts/om_removediy @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return index 66ff2b9..2f69102 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return index 66ff2b9..2f69102 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return index 66ff2b9..2f69102 100644 --- a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return index 66ff2b9..2f69102 100644 --- a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return index 66ff2b9..2f69102 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return index 66ff2b9..2f69102 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return index 66ff2b9..2f69102 100644 --- a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return index 66ff2b9..2f69102 100644 --- a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return index 66ff2b9..2f69102 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return index 66ff2b9..2f69102 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return index 66ff2b9..2f69102 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads index e726ed5..564d5af 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads +++ b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes index 874e8b0..ac9d8ab 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes +++ b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_return b/mod/etc/options_menu/themes/downloads/scripts/om_return index c02eeac..4aae0b4 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_return +++ b/mod/etc/options_menu/themes/downloads/scripts/om_return @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init remove "$omDummy" diff --git a/mod/etc/options_menu/themes/scripts/om_clearList b/mod/etc/options_menu/themes/scripts/om_clearList index 2852460..93937a6 100644 --- a/mod/etc/options_menu/themes/scripts/om_clearList +++ b/mod/etc/options_menu/themes/scripts/om_clearList @@ -1,6 +1,6 @@ #!/bin/sh -# -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# shellcheck disable=SC2086,SC2154 +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,7 +14,7 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/scripts/om_getThemes b/mod/etc/options_menu/themes/scripts/om_getThemes index 75abb58..ba617ee 100644 --- a/mod/etc/options_menu/themes/scripts/om_getThemes +++ b/mod/etc/options_menu/themes/scripts/om_getThemes @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/scripts/om_license b/mod/etc/options_menu/themes/scripts/om_license index 688afc8..1df6a97 100644 --- a/mod/etc/options_menu/themes/scripts/om_license +++ b/mod/etc/options_menu/themes/scripts/om_license @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,5 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/scripts/om_overlaythmcript b/mod/etc/options_menu/themes/scripts/om_overlaythmcript index 48ed31b..cacb297 100644 --- a/mod/etc/options_menu/themes/scripts/om_overlaythmcript +++ b/mod/etc/options_menu/themes/scripts/om_overlaythmcript @@ -1,6 +1,6 @@ #!/bin/bash # shellcheck disable=SC2154,SC2038,SC2143 -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_restorethumb b/mod/etc/options_menu/themes/scripts/om_restorethumb index d115b36..fe16c94 100644 --- a/mod/etc/options_menu/themes/scripts/om_restorethumb +++ b/mod/etc/options_menu/themes/scripts/om_restorethumb @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/scripts/om_settings b/mod/etc/options_menu/themes/scripts/om_settings index cf8209c..e2675e2 100644 --- a/mod/etc/options_menu/themes/scripts/om_settings +++ b/mod/etc/options_menu/themes/scripts/om_settings @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/scripts/om_themeCreator b/mod/etc/options_menu/themes/scripts/om_themeCreator index ac09f9a..9ff2ca5 100644 --- a/mod/etc/options_menu/themes/scripts/om_themeCreator +++ b/mod/etc/options_menu/themes/scripts/om_themeCreator @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/scripts/om_themescript b/mod/etc/options_menu/themes/scripts/om_themescript index 1ff9faf..3d64abe 100644 --- a/mod/etc/options_menu/themes/scripts/om_themescript +++ b/mod/etc/options_menu/themes/scripts/om_themescript @@ -1,6 +1,6 @@ #!/bin/bash # shellcheck disable=SC2154,SC2038,SC2143 -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 6a6d2ce..cf0e54b 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -1,6 +1,6 @@ #!/bin/sh -# shellcheck disable=SC2039 -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# # shellcheck disable=SC2039 +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/scripts/om_functions source $mountpoint/etc/options_menu/scripts/om_functions script_init diff --git a/mod/etc/options_menu/themes/settings/scripts/om_audio b/mod/etc/options_menu/themes/settings/scripts/om_audio index cce5402..c5e7539 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_audio +++ b/mod/etc/options_menu/themes/settings/scripts/om_audio @@ -1,6 +1,6 @@ #!/bin/sh -# shellcheck disable=SC1090,SC2039,SC2086,SC2154 -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# shellcheck disable=SC2086,SC2154 +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/settings/scripts/om_reset b/mod/etc/options_menu/themes/settings/scripts/om_reset index 0439865..36855c6 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_reset +++ b/mod/etc/options_menu/themes/settings/scripts/om_reset @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck disable=SC2154,SC1090,SC2039,SC2086 +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup index 498fbb6..3dcef79 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup +++ b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup @@ -1,6 +1,6 @@ #!/bin/sh -# shellcheck disable=SC1090,SC1091,SC2039,SC2086,SC2154,SC3046 -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# shellcheck disable=SC2086,SC2154 +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/settings/scripts/om_theme b/mod/etc/options_menu/themes/settings/scripts/om_theme index bd05f99..66602b0 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_theme +++ b/mod/etc/options_menu/themes/settings/scripts/om_theme @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck disable=SC2154,SC1090,SC2039,SC2086 +# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/install b/mod/install index 1907cbe..fc14bfb 100644 --- a/mod/install +++ b/mod/install @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC2154 -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/uninstall b/mod/uninstall index 8fec0f0..8853bb5 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2154,SC2068 # -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From af8618f4c99fb602393a3685cd99ba5f15fd011a Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sat, 5 Oct 2024 23:16:52 +0000 Subject: [PATCH 004/210] update gitinit --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index f092d53..14162fb 100644 --- a/.gitignore +++ b/.gitignore @@ -522,3 +522,6 @@ out---backup/ BACKUP.gpg out---/ +preinit +b0010_functions +b0050_boot From 1823cd2031a85838a0c98b94c528e75fecafb047 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sat, 5 Oct 2024 23:27:54 +0000 Subject: [PATCH 005/210] workspace and recomended extensions --- .vscode/extensions.json | 8 +++++ theme-selector-workspace.code-workspace | 39 ++++++++++++++++++------- 2 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..4f5a76c --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "timonwong.shellcheck", + "foxundermoon.shell-format", + "mads-hartmann.bash-ide-vscode", + "designbyajay.bash-cli-snippets", + ] +} \ No newline at end of file diff --git a/theme-selector-workspace.code-workspace b/theme-selector-workspace.code-workspace index 3456332..7113cbf 100644 --- a/theme-selector-workspace.code-workspace +++ b/theme-selector-workspace.code-workspace @@ -1,19 +1,21 @@ { - "folders": [ + "folders": [ { "name": "om_theme-selector", "path": "." }, - { - "name": "out", - "path": "out" - } + // { + // "name": "out", + // "path": "out" + // } ], "settings": { "files.autoSave": "off", + "[bash]": { + "editor.formatOnSave": true + }, // "terminal.integrated.shell.windows": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", - "terminal.integrated.shell.windows": "C:\\Program Files\\Git\\bin\\bash.exe", - + // "terminal.integrated.shell.windows": "C:\\Program Files\\Git\\bin\\bash.exe", "shellformat.path": "C:\\bin\\shfmt_v3.2.4_windows_amd64.exe", "window.zoomLevel": 0, "files.associations": { @@ -31,10 +33,27 @@ "vsicons.projectDetection.autoReload": true, "vsicons.dontShowNewVersionMessage": true, "explorer.confirmDelete": false, - "terminal.integrated.rendererType": "dom", + // "terminal.integrated.rendererType": "dom", "workbench.iconTheme": "vscode-icons", - "beautify.config": "", - "http.proxyAuthorization": null, + // "beautify.config": "", + // "http.proxyAuthorization": null, + + // + "editor.tabSize": 4, + // "editor.wordWrap": "wordWrapColumn", + // "editor.wordWrapColumn": 80, + // "editor.fontSize": 16, + "editor.insertSpaces": true, + "editor.detectIndentation": false, + "editor.renderWhitespace": "all", + "shellcheck.enable": true, + "shellcheck.useWorkspaceRootAsCwd": true, + "shellcheck.run": "onSave", + "shellcheck.customArgs": [ + "-x" + ], + "shellcheck.exclude": ["3046", "3014", "3051"], + "shellformat.flag": "-i 2 -ci -sr -bn", } } \ No newline at end of file From cc44dd0c2578740373c78ddee9d5e82d3c59cff6 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sat, 5 Oct 2024 23:36:54 +0000 Subject: [PATCH 006/210] shellcheck --- mod/etc/options_menu/themes/scripts/om_vars | 5 ++--- theme-selector-workspace.code-workspace | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index cf0e54b..f9e60b5 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -1,4 +1,5 @@ #!/bin/sh +# shellcheck disable=SC2034,SC3046,SC3051 # # shellcheck disable=SC2039 # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # @@ -218,7 +219,6 @@ enableMenuOptions() { toggleDownloads - # shellcheck disable=SC2039 [[ -n $(ls -A "$theme_path") ]] && disable_cmds "ThemeGenerator" if [ -f "$b_file" ] || [ -f "$p_file" ]; then @@ -239,7 +239,7 @@ enableMenuOptions() { if [ "$sftype" = "nes" ]; then src="hvc" dest="nes" - # shellcheck disable=SC2154 + if [ "$sfregion" = "jpn" ]; then src="nes" dest="hvc" @@ -303,7 +303,6 @@ themeLoader() { copy "$omModLicense" "$thm_script.sh" if [ -f "$xtras/thumb.png" ]; then - # shellcheck disable=SC2154 echo "find \"$gStorage\" -path \"*_small.png*\" | while read -r file; do copier \"\$file\" \"\$file\"_backup copier \"$xtras/thumb.png\" \"\$file\" diff --git a/theme-selector-workspace.code-workspace b/theme-selector-workspace.code-workspace index 7113cbf..f5b742d 100644 --- a/theme-selector-workspace.code-workspace +++ b/theme-selector-workspace.code-workspace @@ -53,7 +53,7 @@ "shellcheck.customArgs": [ "-x" ], - "shellcheck.exclude": ["3046", "3014", "3051"], + "shellcheck.exclude": ["3046", "3014","3046", "3051"], "shellformat.flag": "-i 2 -ci -sr -bn", } } \ No newline at end of file From 40e0c1c9f3f3855942c03795aae461a652cd72d3 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sat, 5 Oct 2024 23:44:55 +0000 Subject: [PATCH 007/210] shellcheck --- mod/etc/options_menu/themes/creator/scripts/om_applytheme | 2 +- mod/etc/options_menu/themes/creator/scripts/om_diy_asset | 2 +- mod/etc/options_menu/themes/creator/scripts/om_preview | 2 +- mod/etc/options_menu/themes/creator/scripts/om_refresh | 2 +- mod/etc/options_menu/themes/creator/scripts/om_removediy | 2 +- .../themes/creator/sprites/colors/scripts/om_return | 2 +- .../themes/creator/sprites/colors_nes/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/luigi/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/mario/scripts/om_return | 2 +- .../themes/creator/sprites/packed/scripts/om_return | 2 +- .../themes/creator/sprites/packed_nes/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/pole/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/sbm/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/wall/scripts/om_return | 2 +- .../themes/creator/sprites/wall_far/scripts/om_return | 2 +- .../themes/creator/sprites/wall_near/scripts/om_return | 2 +- mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads | 2 +- mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes | 2 +- mod/etc/options_menu/themes/downloads/scripts/om_return | 2 +- mod/etc/options_menu/themes/scripts/om_clearList | 2 +- mod/etc/options_menu/themes/scripts/om_getThemes | 2 +- mod/etc/options_menu/themes/scripts/om_license | 2 +- mod/etc/options_menu/themes/scripts/om_restorethumb | 2 +- mod/etc/options_menu/themes/scripts/om_settings | 2 +- mod/etc/options_menu/themes/settings/scripts/om_audio | 2 +- mod/etc/options_menu/themes/settings/scripts/om_reset | 2 +- mod/etc/options_menu/themes/settings/scripts/om_systemcleanup | 2 +- mod/etc/options_menu/themes/settings/scripts/om_theme | 2 +- 28 files changed, 28 insertions(+), 28 deletions(-) diff --git a/mod/etc/options_menu/themes/creator/scripts/om_applytheme b/mod/etc/options_menu/themes/creator/scripts/om_applytheme index 6c99ea1..1dd42c4 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_applytheme +++ b/mod/etc/options_menu/themes/creator/scripts/om_applytheme @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify diff --git a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset index e9b8193..ece394d 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset +++ b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset @@ -1,5 +1,5 @@ #!/bin/bash -# shellcheck disable=SC2086,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/creator/scripts/om_preview b/mod/etc/options_menu/themes/creator/scripts/om_preview index 2bd5a60..5518964 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_preview +++ b/mod/etc/options_menu/themes/creator/scripts/om_preview @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/creator/scripts/om_refresh b/mod/etc/options_menu/themes/creator/scripts/om_refresh index 4cb065c..96f5590 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_refresh +++ b/mod/etc/options_menu/themes/creator/scripts/om_refresh @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/creator/scripts/om_removediy b/mod/etc/options_menu/themes/creator/scripts/om_removediy index 4645257..f7ff511 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_removediy +++ b/mod/etc/options_menu/themes/creator/scripts/om_removediy @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return index 2f69102..6f064b9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2119,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return index 2f69102..6f064b9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2119,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return index 2f69102..6f064b9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2119,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return index 2f69102..a0b26c9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2119,SC2154 +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return index 2f69102..a0b26c9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2119,SC2154 +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return index 2f69102..a0b26c9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2119,SC2154 +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return index 2f69102..a0b26c9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2119,SC2154 +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return index 2f69102..a0b26c9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2119,SC2154 +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return index 2f69102..a0b26c9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2119,SC2154 +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return index 2f69102..a0b26c9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2119,SC2154 +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return index 2f69102..a0b26c9 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2119,SC2154 +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads index 564d5af..33c389e 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads +++ b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes index ac9d8ab..ccdb037 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes +++ b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_return b/mod/etc/options_menu/themes/downloads/scripts/om_return index 4aae0b4..009eb50 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_return +++ b/mod/etc/options_menu/themes/downloads/scripts/om_return @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/scripts/om_clearList b/mod/etc/options_menu/themes/scripts/om_clearList index 93937a6..37c6f01 100644 --- a/mod/etc/options_menu/themes/scripts/om_clearList +++ b/mod/etc/options_menu/themes/scripts/om_clearList @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify diff --git a/mod/etc/options_menu/themes/scripts/om_getThemes b/mod/etc/options_menu/themes/scripts/om_getThemes index ba617ee..072e491 100644 --- a/mod/etc/options_menu/themes/scripts/om_getThemes +++ b/mod/etc/options_menu/themes/scripts/om_getThemes @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/scripts/om_license b/mod/etc/options_menu/themes/scripts/om_license index 1df6a97..d6d71d3 100644 --- a/mod/etc/options_menu/themes/scripts/om_license +++ b/mod/etc/options_menu/themes/scripts/om_license @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/scripts/om_restorethumb b/mod/etc/options_menu/themes/scripts/om_restorethumb index fe16c94..aea7f14 100644 --- a/mod/etc/options_menu/themes/scripts/om_restorethumb +++ b/mod/etc/options_menu/themes/scripts/om_restorethumb @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086 +# shellcheck disable=SC2086,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/scripts/om_settings b/mod/etc/options_menu/themes/scripts/om_settings index e2675e2..fc7da89 100644 --- a/mod/etc/options_menu/themes/scripts/om_settings +++ b/mod/etc/options_menu/themes/scripts/om_settings @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/settings/scripts/om_audio b/mod/etc/options_menu/themes/settings/scripts/om_audio index c5e7539..578b397 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_audio +++ b/mod/etc/options_menu/themes/settings/scripts/om_audio @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify diff --git a/mod/etc/options_menu/themes/settings/scripts/om_reset b/mod/etc/options_menu/themes/settings/scripts/om_reset index 36855c6..fc2ea38 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_reset +++ b/mod/etc/options_menu/themes/settings/scripts/om_reset @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup index 3dcef79..868440b 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup +++ b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify diff --git a/mod/etc/options_menu/themes/settings/scripts/om_theme b/mod/etc/options_menu/themes/settings/scripts/om_theme index 66602b0..d6c325d 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_theme +++ b/mod/etc/options_menu/themes/settings/scripts/om_theme @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2154 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # From 6b25f12b6a5a9d142b655ed064cd365f11f302d6 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sat, 5 Oct 2024 23:45:09 +0000 Subject: [PATCH 008/210] shellcheck --- mod/etc/options_menu/scripts/om_themeselector | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/etc/options_menu/scripts/om_themeselector b/mod/etc/options_menu/scripts/om_themeselector index cc2145d..3de9ad2 100644 --- a/mod/etc/options_menu/scripts/om_themeselector +++ b/mod/etc/options_menu/scripts/om_themeselector @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC1091,SC2039,SC2086,SC2154,SC3046 +# shellcheck disable=SC1091,SC2086,SC2039,SC2154,SC3046,SC3051 # Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify From 5cbb8c0818ee60d08c86f8446d0ee879b96c1aa3 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 8 Oct 2024 22:09:42 +0000 Subject: [PATCH 009/210] child --- VERSION | 2 +- .../themes/scripts/om_themescript | 11 ++--- mod/etc/options_menu/themes/scripts/om_vars | 43 +++++++++++-------- .../commands/c0000_themeperfolder_child | 7 +++ mod/install | 13 +++++- 5 files changed, 50 insertions(+), 26 deletions(-) create mode 100644 mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child diff --git a/VERSION b/VERSION index 227cea2..38f77a6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.0.0 +2.0.1 diff --git a/mod/etc/options_menu/themes/scripts/om_themescript b/mod/etc/options_menu/themes/scripts/om_themescript index 3d64abe..1e164b0 100644 --- a/mod/etc/options_menu/themes/scripts/om_themescript +++ b/mod/etc/options_menu/themes/scripts/om_themescript @@ -98,6 +98,7 @@ themeSelector() { [ ! -d "$FONT_PATH" ] && FONT_PATH="$rootfs/usr/share/fonts" local TITLE_FONT="$FONT_PATH/title.fnt" local TITLE_FONT_BACKUP="${TITLE_FONT}.backup" + local THEME_FONT_PATH="$FULL_THEME_PATH/fonts" SILENT_WAV="$rootfs/usr/share/themes/bgm_boot.wav" MUSIC_PATH="$mountpoint/media/$modname/music_menu" @@ -138,7 +139,9 @@ themeSelector() { [ "$FOLDER_ID" == "000" ] && [ -d "$THEMES_PATH/default" ] && SELECTED_THEME="default" if [ -f "$STATE_PATH/om_themeperfolder" ]; then [ -n "$THEME_MATCH" ] && SELECTED_THEME="$GET_THEME_NAME" - [ -n "$IS_CHILD" ] && SELECTED_THEME="$PARENT_THEME_TITLE" + if [ ! -f "$STATE_PATH/om_themeperfolderchild" ]; then + [ -n "$IS_CHILD" ] && SELECTED_THEME="$PARENT_THEME_TITLE" + fi fi FULL_THEME_PATH="$THEMES_PATH/$SELECTED_THEME" @@ -152,10 +155,8 @@ themeSelector() { echo "$FULL_THEME_PATH" >"$ACTIVE_THEME" fi - local TITLE_THEME_FONT="$FULL_THEME_PATH/fonts/title.fnt" - - if [ -d "$FULL_THEME_PATH/fonts" ]; then - if [ "$(cksum "$TITLE_FONT" | awk '{ print $1; }')" != "$(cksum "$TITLE_THEME_FONT" | awk '{ print $1; }')" ]; then + if [ -d "$THEME_FONT_PATH" ]; then + if [ "$(cksum "$TITLE_FONT" | awk '{ print $1; }')" != "$(cksum "$THEME_FONT_PATH/title.fnt" | awk '{ print $1; }')" ]; then font_bind_fix "backup" fi else diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index f9e60b5..6214079 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -85,6 +85,7 @@ thmOverlay="$omModScripts/om_overlay" om_themerandomizer="$state_path/om_themerandomizer" om_themeperfolder="$state_path/om_themeperfolder" +om_themeperfolderchild="$state_path/om_themeperfolderchild" om_audiorandomizer="$state_path/om_audiorandomizer" om_advmusichack="$state_path/om_advmusichack" om_firstrun="$state_path/om_firstrun" @@ -111,6 +112,8 @@ enableThemeSetting="$omModSettingsCmds/c0000_themerandomizer" disableThemeSetting="$omModSettingsCmds/_themerandomizer" enableThemePerFolder="$omModSettingsCmds/c0000_themeperfolder" disableThemePerFolder="$omModSettingsCmds/_themeperfolder" +enableThemePerFolderChild="$omModSettingsCmds/c0000_themeperfolder_child" +disableThemePerFolderChild="$omModSettingsCmds/_themeperfolder_child" enableSystemCleanUp="$omModSettingsCmds/c0001_systemcleanup" disableSystemCleanUp="$omModSettingsCmds/_systemcleanup" enableThemeGenerator="$omModCommands/c0000_generatetheme" @@ -631,7 +634,7 @@ diyThemeChecker() { else disable_cmds "DiY" "DelDiY" "Preview" fi - + [ "$1" = "loader" ] && usleep 1000 } @@ -654,43 +657,47 @@ modCleanUp() { } themeSettings() { - if [ "$1" = "tpf" ]; then - themeSettingsChecker "$om_themeperfolder" "$enableThemePerFolder" + if [ "$1" = "child" ]; then + toggleOptions "$om_themeperfolderchild" "$enableThemePerFolderChild" "$om_themeperfolder" "$enableThemePerFolder" + elif [ "$1" = "tpf" ]; then + toggleOptions "$om_themeperfolder" "$enableThemePerFolder" "$om_themeperfolderchild" "$enableThemePerFolderChild" "switchOn" else - themeSettingsChecker "$om_themerandomizer" "$enableThemeSetting" + if [ ! -f "$om_themerandomizer" ]; then + settingsOptionChecker "on" "$om_themerandomizer" "$enableThemeSetting" + else + settingsOptionChecker "off" "$om_themerandomizer" "$enableThemeSetting" + fi fi } audioSettings() { if [ "$1" = "-amh" ]; then - if [ ! -f "$om_advmusichack" ]; then - audioSettingsChecker "on" "$om_advmusichack" "$enableAdvancedMusicHack" - [ ! -f "$om_audiorandomizer" ] && audioSettingsChecker "on" "$om_audiorandomizer" "$enableAudioSetting" - else - remove "$om_advmusichack" - switchOff "$enableAdvancedMusicHack" - fi + toggleOptions "$om_advmusichack" "$enableAdvancedMusicHack" "$om_audiorandomizer" "$enableAudioSetting" else if [ ! -f "$om_audiorandomizer" ]; then - audioSettingsChecker "on" "$om_audiorandomizer" "$enableAudioSetting" + settingsOptionChecker "on" "$om_audiorandomizer" "$enableAudioSetting" else - audioSettingsChecker "off" "$om_audiorandomizer" "$enableAudioSetting" - audioSettingsChecker "off" "$om_advmusichack" "$enableAdvancedMusicHack" + settingsOptionChecker "off" "$om_audiorandomizer" "$enableAudioSetting" + settingsOptionChecker "off" "$om_advmusichack" "$enableAdvancedMusicHack" fi fi } -themeSettingsChecker() { +toggleOptions() { if [ ! -f "$1" ]; then - touch "$1" - switchOn "$2" + settingsOptionChecker "on" "$1" "$2" + if [ -n "$5" ]; then + [ -f "$3" ] && switchOn "$4" + else + [ ! -f "$3" ] && settingsOptionChecker "on" "$3" "$4" + fi else remove "$1" switchOff "$2" fi } -audioSettingsChecker() { +settingsOptionChecker() { if [ "$1" = "off" ]; then remove "$2" switchOff "$3" diff --git a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child new file mode 100644 index 0000000..6423873 --- /dev/null +++ b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child @@ -0,0 +1,7 @@ +COMMAND_NAME= Allow Different Theme to SubFolder +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %script_dir%/om_theme child +PREVIEW_IMAGE=/etc/options_menu/images/randtheme.png +PREVIEW_IMAGE_X=920 +PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/install b/mod/install index fc14bfb..51d1f78 100644 --- a/mod/install +++ b/mod/install @@ -53,9 +53,18 @@ find "$hmod_dir" -type f -name "*advanced_music_hack" -print0 -exec rm -f {} \; unset cfg_theme_randomizR_enabled transfer_default - -om_firstrun="$mountpoint/$profilepath/$modname/om_firstrun" +om_state_path="$mountpoint/$profilepath/$modname" +om_firstrun="$om_state_path/om_firstrun" +om_themerandomizer="$om_state_path/om_themerandomizer" +om_themeperfolder="$om_state_path/om_themeperfolder" +om_themeperfolderchild="$om_state_path/om_themeperfolderchild" +om_audiorandomizer="$om_state_path/om_audiorandomizer" +om_advmusichack="$om_state_path/om_advmusichack" +remove_settings="$om_themerandomizer $om_themeperfolder $om_themeperfolderchild $om_audiorandomizer $om_advmusichack" [ ! -f "$om_firstrun" ] && touch "$om_firstrun" +for option in $remove_settings; do + del "$option" +done copy "$om_themes_scripts/om_fontfix" "$pre_initd/b8025_fontfix" copy "$om_themes_scripts/om_chmenu" "$rootfs/bin/chmenu" chmod +x "$rootfs/bin/chmenu" From a75daf700caddbbdd75ff31bb7b60e1af75b56d5 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 22 Jan 2025 23:43:13 -0100 Subject: [PATCH 010/210] Update shellcheck settings and modify script header comments --- .vscode/settings.json | 1 + mod/etc/options_menu/scripts/om_functions | 6 +++--- mod/etc/options_menu/scripts/om_themeselector | 2 +- mod/etc/options_menu/themes/creator/scripts/om_applytheme | 2 +- mod/etc/options_menu/themes/creator/scripts/om_diy_asset | 2 +- mod/etc/options_menu/themes/creator/scripts/om_preview | 2 +- mod/etc/options_menu/themes/creator/scripts/om_refresh | 2 +- mod/etc/options_menu/themes/creator/scripts/om_removediy | 2 +- .../themes/creator/sprites/colors/scripts/om_return | 2 +- .../themes/creator/sprites/colors_nes/scripts/om_return | 2 +- .../themes/creator/sprites/luigi/scripts/om_return | 2 +- .../themes/creator/sprites/mario/scripts/om_return | 2 +- .../themes/creator/sprites/packed/scripts/om_return | 2 +- .../themes/creator/sprites/packed_nes/scripts/om_return | 2 +- .../themes/creator/sprites/pole/scripts/om_return | 2 +- .../themes/creator/sprites/sbm/scripts/om_return | 2 +- .../themes/creator/sprites/wall/scripts/om_return | 2 +- .../themes/creator/sprites/wall_far/scripts/om_return | 2 +- .../themes/creator/sprites/wall_near/scripts/om_return | 2 +- .../options_menu/themes/downloads/scripts/om_clearDownloads | 2 +- .../options_menu/themes/downloads/scripts/om_refreshThemes | 2 +- mod/etc/options_menu/themes/downloads/scripts/om_return | 2 +- mod/etc/options_menu/themes/scripts/om_clearList | 2 +- mod/etc/options_menu/themes/scripts/om_getThemes | 2 +- mod/etc/options_menu/themes/scripts/om_license | 2 +- mod/etc/options_menu/themes/scripts/om_overlaythmcript | 2 +- mod/etc/options_menu/themes/scripts/om_restorethumb | 2 +- mod/etc/options_menu/themes/scripts/om_settings | 2 +- mod/etc/options_menu/themes/scripts/om_themeCreator | 2 +- mod/etc/options_menu/themes/scripts/om_themescript | 2 +- mod/etc/options_menu/themes/scripts/om_vars | 2 +- mod/etc/options_menu/themes/settings/scripts/om_audio | 2 +- mod/etc/options_menu/themes/settings/scripts/om_reset | 2 +- .../options_menu/themes/settings/scripts/om_systemcleanup | 2 +- mod/etc/options_menu/themes/settings/scripts/om_theme | 2 +- mod/install | 2 +- mod/uninstall | 2 +- 37 files changed, 39 insertions(+), 38 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 392d6fa..af83009 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -29,4 +29,5 @@ "editor.tabSize": 2, }, "diffEditor.ignoreTrimWhitespace": false, + "shellcheck.useWorkspaceRootAsCwd": true, } \ No newline at end of file diff --git a/mod/etc/options_menu/scripts/om_functions b/mod/etc/options_menu/scripts/om_functions index a6023fc..6d7e052 100644 --- a/mod/etc/options_menu/scripts/om_functions +++ b/mod/etc/options_menu/scripts/om_functions @@ -1,6 +1,6 @@ #!/bin/sh -# shellcheck disable=SC2154,SC1091,SC2034,SC2068,SC2039 -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# shellcheck disable=SC2154,SC1091,SC2034,SC2068,SC2039,SC3051,SC3046 +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=preinit +# shellcheck source=/dev/null source /etc/preinit script_init diff --git a/mod/etc/options_menu/scripts/om_themeselector b/mod/etc/options_menu/scripts/om_themeselector index 3de9ad2..fbabc39 100644 --- a/mod/etc/options_menu/scripts/om_themeselector +++ b/mod/etc/options_menu/scripts/om_themeselector @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC1091,SC2086,SC2039,SC2154,SC3046,SC3051 -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/scripts/om_applytheme b/mod/etc/options_menu/themes/creator/scripts/om_applytheme index 1dd42c4..31a5b66 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_applytheme +++ b/mod/etc/options_menu/themes/creator/scripts/om_applytheme @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset index ece394d..26900c5 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset +++ b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset @@ -1,7 +1,7 @@ #!/bin/bash # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/scripts/om_preview b/mod/etc/options_menu/themes/creator/scripts/om_preview index 5518964..894b626 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_preview +++ b/mod/etc/options_menu/themes/creator/scripts/om_preview @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/scripts/om_refresh b/mod/etc/options_menu/themes/creator/scripts/om_refresh index 96f5590..a7ac1c4 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_refresh +++ b/mod/etc/options_menu/themes/creator/scripts/om_refresh @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/scripts/om_removediy b/mod/etc/options_menu/themes/creator/scripts/om_removediy index f7ff511..7d12e2a 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_removediy +++ b/mod/etc/options_menu/themes/creator/scripts/om_removediy @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return index 6f064b9..946b9db 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return index 6f064b9..946b9db 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return index 6f064b9..946b9db 100644 --- a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return index a0b26c9..1a79fde 100644 --- a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return index a0b26c9..1a79fde 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return index a0b26c9..1a79fde 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return index a0b26c9..1a79fde 100644 --- a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return index a0b26c9..1a79fde 100644 --- a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return index a0b26c9..1a79fde 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return index a0b26c9..1a79fde 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return index a0b26c9..1a79fde 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads index 33c389e..b527f4a 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads +++ b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes index ccdb037..9c304ca 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes +++ b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_return b/mod/etc/options_menu/themes/downloads/scripts/om_return index 009eb50..a3f7aa7 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_return +++ b/mod/etc/options_menu/themes/downloads/scripts/om_return @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_clearList b/mod/etc/options_menu/themes/scripts/om_clearList index 37c6f01..f270627 100644 --- a/mod/etc/options_menu/themes/scripts/om_clearList +++ b/mod/etc/options_menu/themes/scripts/om_clearList @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_getThemes b/mod/etc/options_menu/themes/scripts/om_getThemes index 072e491..57e2df4 100644 --- a/mod/etc/options_menu/themes/scripts/om_getThemes +++ b/mod/etc/options_menu/themes/scripts/om_getThemes @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_license b/mod/etc/options_menu/themes/scripts/om_license index d6d71d3..0a128b4 100644 --- a/mod/etc/options_menu/themes/scripts/om_license +++ b/mod/etc/options_menu/themes/scripts/om_license @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_overlaythmcript b/mod/etc/options_menu/themes/scripts/om_overlaythmcript index cacb297..31d4744 100644 --- a/mod/etc/options_menu/themes/scripts/om_overlaythmcript +++ b/mod/etc/options_menu/themes/scripts/om_overlaythmcript @@ -1,6 +1,6 @@ #!/bin/bash # shellcheck disable=SC2154,SC2038,SC2143 -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_restorethumb b/mod/etc/options_menu/themes/scripts/om_restorethumb index aea7f14..969f609 100644 --- a/mod/etc/options_menu/themes/scripts/om_restorethumb +++ b/mod/etc/options_menu/themes/scripts/om_restorethumb @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_settings b/mod/etc/options_menu/themes/scripts/om_settings index fc7da89..e797220 100644 --- a/mod/etc/options_menu/themes/scripts/om_settings +++ b/mod/etc/options_menu/themes/scripts/om_settings @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_themeCreator b/mod/etc/options_menu/themes/scripts/om_themeCreator index 9ff2ca5..088116b 100644 --- a/mod/etc/options_menu/themes/scripts/om_themeCreator +++ b/mod/etc/options_menu/themes/scripts/om_themeCreator @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_themescript b/mod/etc/options_menu/themes/scripts/om_themescript index 1e164b0..3bb4754 100644 --- a/mod/etc/options_menu/themes/scripts/om_themescript +++ b/mod/etc/options_menu/themes/scripts/om_themescript @@ -1,6 +1,6 @@ #!/bin/bash # shellcheck disable=SC2154,SC2038,SC2143 -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 6214079..aecca01 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2034,SC3046,SC3051 # # shellcheck disable=SC2039 -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/settings/scripts/om_audio b/mod/etc/options_menu/themes/settings/scripts/om_audio index 578b397..c4ea2a3 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_audio +++ b/mod/etc/options_menu/themes/settings/scripts/om_audio @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/settings/scripts/om_reset b/mod/etc/options_menu/themes/settings/scripts/om_reset index fc2ea38..7cbdfc4 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_reset +++ b/mod/etc/options_menu/themes/settings/scripts/om_reset @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup index 868440b..8da244b 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup +++ b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/settings/scripts/om_theme b/mod/etc/options_menu/themes/settings/scripts/om_theme index d6c325d..e983b0c 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_theme +++ b/mod/etc/options_menu/themes/settings/scripts/om_theme @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/install b/mod/install index 51d1f78..bd4ab55 100644 --- a/mod/install +++ b/mod/install @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC2154 -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/uninstall b/mod/uninstall index 8853bb5..1ac6598 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2154,SC2068 # -# Copyright (c) 2024 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From df479688336eb81958fc171c75b4d16be8b50418 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 22 Jan 2025 23:56:46 -0100 Subject: [PATCH 011/210] Refactor shellcheck directives and improve script formatting --- mod/etc/options_menu/scripts/om_themeselector | 2 +- mod/etc/options_menu/themes/scripts/om_restorethumb | 2 +- mod/etc/options_menu/themes/scripts/om_themeCreator | 2 +- mod/etc/options_menu/themes/scripts/om_vars | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mod/etc/options_menu/scripts/om_themeselector b/mod/etc/options_menu/scripts/om_themeselector index fbabc39..d5f488f 100644 --- a/mod/etc/options_menu/scripts/om_themeselector +++ b/mod/etc/options_menu/scripts/om_themeselector @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC1091,SC2086,SC2039,SC2154,SC3046,SC3051 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify diff --git a/mod/etc/options_menu/themes/scripts/om_restorethumb b/mod/etc/options_menu/themes/scripts/om_restorethumb index 969f609..c04b270 100644 --- a/mod/etc/options_menu/themes/scripts/om_restorethumb +++ b/mod/etc/options_menu/themes/scripts/om_restorethumb @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC3046,SC3051 +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # # Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/scripts/om_themeCreator b/mod/etc/options_menu/themes/scripts/om_themeCreator index 088116b..14cda9c 100644 --- a/mod/etc/options_menu/themes/scripts/om_themeCreator +++ b/mod/etc/options_menu/themes/scripts/om_themeCreator @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2119,SC2154 +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # # Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index aecca01..07c7a2c 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2034,SC3046,SC3051 +# shellcheck disable=SC2034,SC2086,SC2154,SC3010,SC3046,SC3051 # # shellcheck disable=SC2039 # Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) # @@ -660,7 +660,7 @@ themeSettings() { if [ "$1" = "child" ]; then toggleOptions "$om_themeperfolderchild" "$enableThemePerFolderChild" "$om_themeperfolder" "$enableThemePerFolder" elif [ "$1" = "tpf" ]; then - toggleOptions "$om_themeperfolder" "$enableThemePerFolder" "$om_themeperfolderchild" "$enableThemePerFolderChild" "switchOn" + toggleOptions "$om_themeperfolder" "$enableThemePerFolder" "$om_themeperfolderchild" "$enableThemePerFolderChild" "switchOn" else if [ ! -f "$om_themerandomizer" ]; then settingsOptionChecker "on" "$om_themerandomizer" "$enableThemeSetting" @@ -672,7 +672,7 @@ themeSettings() { audioSettings() { if [ "$1" = "-amh" ]; then - toggleOptions "$om_advmusichack" "$enableAdvancedMusicHack" "$om_audiorandomizer" "$enableAudioSetting" + toggleOptions "$om_advmusichack" "$enableAdvancedMusicHack" "$om_audiorandomizer" "$enableAudioSetting" else if [ ! -f "$om_audiorandomizer" ]; then settingsOptionChecker "on" "$om_audiorandomizer" "$enableAudioSetting" From eea6a9b6567c3d6a78d08658b9a070504355b963 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Mon, 31 Aug 2026 10:49:37 +0000 Subject: [PATCH 012/210] Format script indentation and improve readability in theme scripts --- mod/etc/options_menu/themes/creator/scripts/om_preview | 4 ++-- mod/etc/options_menu/themes/downloads/scripts/om_return | 8 ++++---- mod/etc/options_menu/themes/scripts/om_restorethumb | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mod/etc/options_menu/themes/creator/scripts/om_preview b/mod/etc/options_menu/themes/creator/scripts/om_preview index 894b626..95d90ba 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_preview +++ b/mod/etc/options_menu/themes/creator/scripts/om_preview @@ -22,8 +22,8 @@ script_init decodepng "$omLoaders/themepreview.png" >/dev/fb0 if [ ! -f "$b_file" ]; then - sed '1,2d' $omThemeScript >"$b_file" - echo "themeSelector" >"$p_file" + sed '1,2d' $omThemeScript >"$b_file" + echo "themeSelector" >"$p_file" fi echo "usleep 20000; \ diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_return b/mod/etc/options_menu/themes/downloads/scripts/om_return index a3f7aa7..af1b6fe 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_return +++ b/mod/etc/options_menu/themes/downloads/scripts/om_return @@ -22,11 +22,11 @@ script_init remove "$omDummy" if [ -n "$(ls -A $theme_path)" ]; then - touch "$omDummy" - themeLoader + touch "$omDummy" + themeLoader else - remove "$omModSpacer" - clearList + remove "$omModSpacer" + clearList fi usleep 50000 && $optionsMenu/options --commandPath $omModCommands/ --scriptPath $omModScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/scripts/om_restorethumb b/mod/etc/options_menu/themes/scripts/om_restorethumb index c04b270..7e6b9e4 100644 --- a/mod/etc/options_menu/themes/scripts/om_restorethumb +++ b/mod/etc/options_menu/themes/scripts/om_restorethumb @@ -21,5 +21,5 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init find "$gStorage" -path "*_small.png*" | while read -r file; do - copier "$file"_backup "$file" && rm "$file"_backup + copier "$file"_backup "$file" && rm "$file"_backup done From 33b1147948de379e76b524f203cd107d4bd0ac54 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Mon, 31 Aug 2026 10:50:22 +0000 Subject: [PATCH 013/210] Adjust editor tab size to 2 and format shellcheck exclusions for consistency --- theme-selector-workspace.code-workspace | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/theme-selector-workspace.code-workspace b/theme-selector-workspace.code-workspace index f5b742d..16e3175 100644 --- a/theme-selector-workspace.code-workspace +++ b/theme-selector-workspace.code-workspace @@ -37,23 +37,26 @@ "workbench.iconTheme": "vscode-icons", // "beautify.config": "", // "http.proxyAuthorization": null, - // - "editor.tabSize": 4, + "editor.tabSize": 2, // "editor.wordWrap": "wordWrapColumn", // "editor.wordWrapColumn": 80, // "editor.fontSize": 16, "editor.insertSpaces": true, "editor.detectIndentation": false, "editor.renderWhitespace": "all", - "shellcheck.enable": true, "shellcheck.useWorkspaceRootAsCwd": true, "shellcheck.run": "onSave", "shellcheck.customArgs": [ "-x" ], - "shellcheck.exclude": ["3046", "3014","3046", "3051"], + "shellcheck.exclude": [ + "3046", + "3014", + "3046", + "3051" + ], "shellformat.flag": "-i 2 -ci -sr -bn", } } \ No newline at end of file From 173bd88fadbae8ece6da119ec952c24fdaa0cd91 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Mon, 31 Aug 2026 11:16:57 +0000 Subject: [PATCH 014/210] update copyright --- Makefile | 5 +- mod/etc/options_menu/scripts/om_functions | 2 +- mod/etc/options_menu/scripts/om_themeselector | 2 +- .../themes/creator/scripts/om_applytheme | 2 +- .../themes/creator/scripts/om_diy_asset | 2 +- .../themes/creator/scripts/om_preview | 2 +- .../themes/creator/scripts/om_refresh | 4 +- .../themes/creator/scripts/om_removediy | 2 +- .../creator/sprites/colors/scripts/om_return | 10 +-- .../sprites/colors_nes/scripts/om_return | 10 +-- .../creator/sprites/luigi/scripts/om_return | 10 +-- .../creator/sprites/mario/scripts/om_return | 10 +-- .../creator/sprites/packed/scripts/om_return | 10 +-- .../sprites/packed_nes/scripts/om_return | 10 +-- .../creator/sprites/pole/scripts/om_return | 10 +-- .../creator/sprites/sbm/scripts/om_return | 10 +-- .../creator/sprites/wall/scripts/om_return | 10 +-- .../sprites/wall_far/scripts/om_return | 10 +-- .../sprites/wall_near/scripts/om_return | 10 +-- .../downloads/scripts/om_clearDownloads | 8 +-- .../themes/downloads/scripts/om_refreshThemes | 8 +-- .../themes/downloads/scripts/om_return | 8 +-- .../options_menu/themes/scripts/om_clearList | 9 +-- .../options_menu/themes/scripts/om_getThemes | 8 +-- .../options_menu/themes/scripts/om_license | 2 +- .../themes/scripts/om_overlaythmcript | 2 +- .../themes/scripts/om_restorethumb | 2 +- .../options_menu/themes/scripts/om_settings | 2 +- .../themes/scripts/om_themeCreator | 2 +- .../themes/scripts/om_themescript | 2 +- mod/etc/options_menu/themes/scripts/om_vars | 2 +- .../themes/settings/scripts/om_audio | 2 +- .../themes/settings/scripts/om_reset | 8 +-- .../themes/settings/scripts/om_systemcleanup | 2 +- .../themes/settings/scripts/om_theme | 2 +- mod/install | 2 +- mod/uninstall | 2 +- tools/license-header.txt | 15 ++++ tools/update-license-headers.sh | 69 +++++++++++++++++++ 39 files changed, 188 insertions(+), 100 deletions(-) create mode 100644 tools/license-header.txt create mode 100644 tools/update-license-headers.sh diff --git a/Makefile b/Makefile index 6b8fde2..ed80f29 100644 --- a/Makefile +++ b/Makefile @@ -98,8 +98,11 @@ info: @echo "next minor version: $(NEXT_MINOR_VERSION)" @echo "next patch version: $(NEXT_PATCH_VERSION)" +update-headers: + sh tools/update-license-headers.sh $(YEAR) + clean: rm -rf out/ temp/ -.PHONY: clean +.PHONY: clean update-headers diff --git a/mod/etc/options_menu/scripts/om_functions b/mod/etc/options_menu/scripts/om_functions index 6d7e052..df1d332 100644 --- a/mod/etc/options_menu/scripts/om_functions +++ b/mod/etc/options_menu/scripts/om_functions @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC2154,SC1091,SC2034,SC2068,SC2039,SC3051,SC3046 -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/scripts/om_themeselector b/mod/etc/options_menu/scripts/om_themeselector index d5f488f..0b61cdf 100644 --- a/mod/etc/options_menu/scripts/om_themeselector +++ b/mod/etc/options_menu/scripts/om_themeselector @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/scripts/om_applytheme b/mod/etc/options_menu/themes/creator/scripts/om_applytheme index 31a5b66..18e3fb6 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_applytheme +++ b/mod/etc/options_menu/themes/creator/scripts/om_applytheme @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset index 26900c5..77767ba 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset +++ b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset @@ -1,7 +1,7 @@ #!/bin/bash # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/scripts/om_preview b/mod/etc/options_menu/themes/creator/scripts/om_preview index 95d90ba..c70e973 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_preview +++ b/mod/etc/options_menu/themes/creator/scripts/om_preview @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/scripts/om_refresh b/mod/etc/options_menu/themes/creator/scripts/om_refresh index a7ac1c4..0c27916 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_refresh +++ b/mod/etc/options_menu/themes/creator/scripts/om_refresh @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -24,4 +24,4 @@ diyThemeRemover diyThemeChecker "loader" touch "$omModGenSpacer" -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & \ No newline at end of file +usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/scripts/om_removediy b/mod/etc/options_menu/themes/creator/scripts/om_removediy index 7d12e2a..8286967 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_removediy +++ b/mod/etc/options_menu/themes/creator/scripts/om_removediy @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return index 946b9db..fd8fe2c 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return @@ -1,18 +1,18 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) -# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # @@ -22,4 +22,4 @@ script_init diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return index 946b9db..fd8fe2c 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return @@ -1,18 +1,18 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) -# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # @@ -22,4 +22,4 @@ script_init diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return index 946b9db..fd8fe2c 100644 --- a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return @@ -1,18 +1,18 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) -# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # @@ -22,4 +22,4 @@ script_init diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return index 1a79fde..a8ecc86 100644 --- a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return @@ -1,18 +1,18 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) -# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # @@ -22,4 +22,4 @@ script_init diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return index 1a79fde..a8ecc86 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return @@ -1,18 +1,18 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) -# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # @@ -22,4 +22,4 @@ script_init diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return index 1a79fde..a8ecc86 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return @@ -1,18 +1,18 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) -# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # @@ -22,4 +22,4 @@ script_init diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return index 1a79fde..a8ecc86 100644 --- a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return @@ -1,18 +1,18 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) -# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # @@ -22,4 +22,4 @@ script_init diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return index 1a79fde..a8ecc86 100644 --- a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return @@ -1,18 +1,18 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) -# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # @@ -22,4 +22,4 @@ script_init diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return index 1a79fde..a8ecc86 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return @@ -1,18 +1,18 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) -# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # @@ -22,4 +22,4 @@ script_init diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return index 1a79fde..a8ecc86 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return @@ -1,18 +1,18 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) -# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # @@ -22,4 +22,4 @@ script_init diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return index 1a79fde..a8ecc86 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return @@ -1,18 +1,18 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) -# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # @@ -22,4 +22,4 @@ script_init diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads index b527f4a..f50e81d 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads +++ b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads @@ -1,18 +1,18 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) -# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes index 9c304ca..aadf3df 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes +++ b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes @@ -1,18 +1,18 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) -# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_return b/mod/etc/options_menu/themes/downloads/scripts/om_return index af1b6fe..356052c 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_return +++ b/mod/etc/options_menu/themes/downloads/scripts/om_return @@ -1,18 +1,18 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) -# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # diff --git a/mod/etc/options_menu/themes/scripts/om_clearList b/mod/etc/options_menu/themes/scripts/om_clearList index f270627..05e66dd 100644 --- a/mod/etc/options_menu/themes/scripts/om_clearList +++ b/mod/etc/options_menu/themes/scripts/om_clearList @@ -1,19 +1,20 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) -# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . +# # shellcheck source=mod/etc/options_menu/themes/scripts/om_vars source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/scripts/om_getThemes b/mod/etc/options_menu/themes/scripts/om_getThemes index 57e2df4..fd7c557 100644 --- a/mod/etc/options_menu/themes/scripts/om_getThemes +++ b/mod/etc/options_menu/themes/scripts/om_getThemes @@ -1,18 +1,18 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) -# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # diff --git a/mod/etc/options_menu/themes/scripts/om_license b/mod/etc/options_menu/themes/scripts/om_license index 0a128b4..fb7451d 100644 --- a/mod/etc/options_menu/themes/scripts/om_license +++ b/mod/etc/options_menu/themes/scripts/om_license @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_overlaythmcript b/mod/etc/options_menu/themes/scripts/om_overlaythmcript index 31d4744..ef23b98 100644 --- a/mod/etc/options_menu/themes/scripts/om_overlaythmcript +++ b/mod/etc/options_menu/themes/scripts/om_overlaythmcript @@ -1,6 +1,6 @@ #!/bin/bash # shellcheck disable=SC2154,SC2038,SC2143 -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_restorethumb b/mod/etc/options_menu/themes/scripts/om_restorethumb index 7e6b9e4..4492113 100644 --- a/mod/etc/options_menu/themes/scripts/om_restorethumb +++ b/mod/etc/options_menu/themes/scripts/om_restorethumb @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_settings b/mod/etc/options_menu/themes/scripts/om_settings index e797220..cfdf1af 100644 --- a/mod/etc/options_menu/themes/scripts/om_settings +++ b/mod/etc/options_menu/themes/scripts/om_settings @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_themeCreator b/mod/etc/options_menu/themes/scripts/om_themeCreator index 14cda9c..d3049c3 100644 --- a/mod/etc/options_menu/themes/scripts/om_themeCreator +++ b/mod/etc/options_menu/themes/scripts/om_themeCreator @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_themescript b/mod/etc/options_menu/themes/scripts/om_themescript index 3bb4754..90054b6 100644 --- a/mod/etc/options_menu/themes/scripts/om_themescript +++ b/mod/etc/options_menu/themes/scripts/om_themescript @@ -1,6 +1,6 @@ #!/bin/bash # shellcheck disable=SC2154,SC2038,SC2143 -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 07c7a2c..98d6825 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2034,SC2086,SC2154,SC3010,SC3046,SC3051 # # shellcheck disable=SC2039 -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/settings/scripts/om_audio b/mod/etc/options_menu/themes/settings/scripts/om_audio index c4ea2a3..8bc3829 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_audio +++ b/mod/etc/options_menu/themes/settings/scripts/om_audio @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/settings/scripts/om_reset b/mod/etc/options_menu/themes/settings/scripts/om_reset index 7cbdfc4..1c5fe85 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_reset +++ b/mod/etc/options_menu/themes/settings/scripts/om_reset @@ -1,18 +1,18 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) -# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . # diff --git a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup index 8da244b..e4f570e 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup +++ b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/etc/options_menu/themes/settings/scripts/om_theme b/mod/etc/options_menu/themes/settings/scripts/om_theme index e983b0c..99bb8f6 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_theme +++ b/mod/etc/options_menu/themes/settings/scripts/om_theme @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/install b/mod/install index bd4ab55..242cc19 100644 --- a/mod/install +++ b/mod/install @@ -1,6 +1,6 @@ #!/bin/sh # shellcheck disable=SC2154 -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mod/uninstall b/mod/uninstall index 1ac6598..d4d5638 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -1,7 +1,7 @@ #!/bin/sh # shellcheck disable=SC2154,SC2068 # -# Copyright (c) 2025 DefKorns (https://defkorns.github.io/LICENSE) +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tools/license-header.txt b/tools/license-header.txt new file mode 100644 index 0000000..e32bdde --- /dev/null +++ b/tools/license-header.txt @@ -0,0 +1,15 @@ +# Copyright (c) {YEAR} DefKorns (https://defkorns.github.io/LICENSE) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# diff --git a/tools/update-license-headers.sh b/tools/update-license-headers.sh new file mode 100644 index 0000000..6b16a49 --- /dev/null +++ b/tools/update-license-headers.sh @@ -0,0 +1,69 @@ +#!/bin/sh +# Stamps the canonical GPL header (tools/license-header.txt) into the top of +# every project script, replacing only the "Copyright (c) ... gnu.org" block. +# Everything before it (shebang, shellcheck disable comments) and after it +# (source/script_init lines, code) is left untouched. +# +# Usage: +# sh tools/update-license-headers.sh [YEAR] +# YEAR defaults to the current year. +set -eu + +script_dir="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)" +repo_root="$(CDPATH='' cd -- "$script_dir/.." && pwd)" +header_file="$script_dir/license-header.txt" +year="${1:-$(date +%Y)}" + +if [ ! -f "$header_file" ]; then + echo "missing $header_file" >&2 + exit 1 +fi + +files=$( + { + [ -f "$repo_root/mod/install" ] && echo "$repo_root/mod/install" + [ -f "$repo_root/mod/uninstall" ] && echo "$repo_root/mod/uninstall" + find "$repo_root/mod" -type f -name 'om_*' + } | sort -u | xargs grep -lE '^#!/bin/(sh|bash)' 2>/dev/null +) + +updated=0 +for f in $files; do + if grep -q '^#[[:space:]]*Copyright (c)' "$f"; then + tmp="$f.tmp.$$" + awk -v year="$year" -v hdrfile="$header_file" ' + BEGIN { + n = 0 + while ((getline line < hdrfile) > 0) hdr[n++] = line + close(hdrfile) + } + skip_blank { + skip_blank = 0 + if ($0 ~ /^#[ \t]*$/) next + } + !started && /^#[ \t]*Copyright \(c\)/ { + started = 1 + inblock = 1 + for (i = 0; i < n; i++) { + l = hdr[i] + gsub(/\{YEAR\}/, year, l) + print l + } + next + } + inblock { + if ($0 ~ /gnu\.org\/licenses/) { + inblock = 0 + skip_blank = 1 + } + next + } + { print } + ' "$f" >"$tmp" + mv "$tmp" "$f" + updated=$((updated + 1)) + echo "updated: ${f#"$repo_root"/}" + fi +done + +echo "$updated file(s) updated to copyright year $year" From 64a9e108ed47785d693c496a47d26fef7d0bb125 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Mon, 31 Aug 2026 11:31:21 +0000 Subject: [PATCH 015/210] Fix version bump tooling and wire tags to releases - RC extraction now falls back to 0, so `make rc` can bootstrap the first -rc1 instead of failing (expr on an empty operand) and getting stuck on a bare "-rc" suffix forever. - GIT_COMMIT now reads git diff-index's exit code instead of its (always empty, --quiet-suppressed) stdout, so packaged .hmod readmes correctly show "-dirty" when built from an uncommitted tree. - Removed the duplicate GIT_DIRTY definition. - fix/rc/update/upgrade now tag the version they just built (v$(MOD_VER)) and commit the VERSION bump, so git tags stay in sync with releases instead of drifting from them. --- Makefile | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index ed80f29..00f4cf1 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ MOD_VER = $(shell [ -f VERSION ] && head VERSION || echo $(GET_VER)) MAJOR = $(shell echo $(MOD_VER) | sed "s/^\([0-9]*\).*/\1/") MINOR = $(shell echo $(MOD_VER) | sed "s/[0-9]*\.\([0-9]*\).*/\1/") PATCH = $(shell echo $(MOD_VER) | sed "s/[0-9]*\.[0-9]*\.\([0-9]*\).*/\1/") -RC = $(shell echo $(MOD_VER) | grep -oP '(?<=rc)[0-9]+') +RC = $(shell echo $(MOD_VER) | grep -oP '(?<=rc)[0-9]+' || echo 0) # total number of commits BUILD = $(shell git log --oneline | wc -l | sed -e "s/[ \t]*//g") @@ -26,8 +26,7 @@ NEXT_PATCH_VERSION = $(MAJOR).$(MINOR).$(shell expr $(PATCH) + 1)-b$(BUILD) NEXT_RC_VERSION = $(MAJOR).$(MINOR).$(PATCH)-rc$(shell expr $(RC) + 1) MOD_URL=`git config --get remote.origin.url` -GIT_COMMIT := $(shell echo "`git rev-parse --short HEAD``git diff-index --quiet HEAD --`") -GIT_DIRTY = $(shell git diff --shortstat 2> /dev/null | tail -n1 ) +GIT_COMMIT := $(shell git rev-parse --short HEAD)$(shell git diff-index --quiet HEAD -- || echo -dirty) RSYNC = $(shell rsync -a --exclude-from=exclude-file.txt mod/etc/options_menu/ temp/ --links --delete) MOD_FILENAME = $(shell basename `pwd`) DEV_DIR = $(shell realpath .) @@ -73,16 +72,32 @@ zip: cd temp/; zip -r $(OUT)/$(MOD_FILENAME)-$(MOD_VER).zip * fix: hmod - @echo $(NEXT_PATCH_VERSION) > VERSION + @ver="$(NEXT_PATCH_VERSION)" && \ + git tag "v$(MOD_VER)" && \ + echo "$$ver" > VERSION && \ + git add VERSION && \ + git commit -m "Bump version to v$$ver" rc: hmod - @echo $(NEXT_RC_VERSION) > VERSION + @ver="$(NEXT_RC_VERSION)" && \ + git tag "v$(MOD_VER)" && \ + echo "$$ver" > VERSION && \ + git add VERSION && \ + git commit -m "Bump version to v$$ver" update: all - @echo $(NEXT_MINOR_VERSION) > VERSION + @ver="$(NEXT_MINOR_VERSION)" && \ + git tag "v$(MOD_VER)" && \ + echo "$$ver" > VERSION && \ + git add VERSION && \ + git commit -m "Bump version to v$$ver" upgrade: all - @echo $(NEXT_MAJOR_VERSION) > VERSION + @ver="$(NEXT_MAJOR_VERSION)" && \ + git tag "v$(MOD_VER)" && \ + echo "$$ver" > VERSION && \ + git add VERSION && \ + git commit -m "Bump version to v$$ver" upload: rm -f $(OUT)/$(MOD_FILENAME).* From d67c5f57e08b7c4a4e1ebc415528f8221f43895b Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 1 Sep 2026 08:19:56 +0000 Subject: [PATCH 016/210] Add CI configuration for linting and building with shellcheck --- .gitlab-ci.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..8a076c8 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,34 @@ +stages: + - lint + - build + +# om_chmenu_og / om_fontfix_og are frozen copies of base-OS files (used only +# by mod/uninstall to restore the original chmenu/fontfix) - they're not +# authored in our style and aren't meant to pass our lint conventions. +.script_files: &list_script_files | + files=$( + { echo mod/install; echo mod/uninstall; find mod -type f -name 'om_*'; } | + grep -v -e om_chmenu_og -e om_fontfix_og | + sort -u | xargs grep -lE '^#!/bin/(sh|bash)' + ) + +shellcheck: + stage: lint + image: alpine:3.20 + before_script: + - apk add --no-cache bash findutils grep shellcheck + script: + - *list_script_files + - shellcheck -x -e SC3046,SC3051,SC3014 $files + +build: + stage: build + image: alpine:3.20 + before_script: + - apk add --no-cache make bash rsync tar zip git grep sed coreutils + script: + - make all + artifacts: + paths: + - out/* + expire_in: 30 days From f298fa127fd3414fa6202016fc8e5d663c3ac58c Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 1 Sep 2026 10:16:00 +0000 Subject: [PATCH 017/210] Add shellcheck directive to disable SC2119 for diyThemeChecker calls --- .../options_menu/themes/creator/sprites/colors/scripts/om_return | 1 + .../themes/creator/sprites/colors_nes/scripts/om_return | 1 + .../options_menu/themes/creator/sprites/luigi/scripts/om_return | 1 + .../options_menu/themes/creator/sprites/mario/scripts/om_return | 1 + .../options_menu/themes/creator/sprites/packed/scripts/om_return | 1 + .../themes/creator/sprites/packed_nes/scripts/om_return | 1 + .../options_menu/themes/creator/sprites/pole/scripts/om_return | 1 + .../options_menu/themes/creator/sprites/sbm/scripts/om_return | 1 + .../options_menu/themes/creator/sprites/wall/scripts/om_return | 1 + .../themes/creator/sprites/wall_far/scripts/om_return | 1 + .../themes/creator/sprites/wall_near/scripts/om_return | 1 + 11 files changed, 11 insertions(+) diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return index fd8fe2c..d1eaf26 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return @@ -20,6 +20,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return index fd8fe2c..d1eaf26 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return @@ -20,6 +20,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return index fd8fe2c..d1eaf26 100644 --- a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return @@ -20,6 +20,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return index a8ecc86..e9c8591 100644 --- a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return @@ -20,6 +20,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return index a8ecc86..e9c8591 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return @@ -20,6 +20,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return index a8ecc86..e9c8591 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return @@ -20,6 +20,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return index a8ecc86..e9c8591 100644 --- a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return @@ -20,6 +20,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return index a8ecc86..e9c8591 100644 --- a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return @@ -20,6 +20,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return index a8ecc86..e9c8591 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return @@ -20,6 +20,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return index a8ecc86..e9c8591 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return @@ -20,6 +20,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return index a8ecc86..e9c8591 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return @@ -20,6 +20,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & From 0770dc298e8b61b005e632a190d937d4c6503bda Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 1 Sep 2026 10:20:05 +0000 Subject: [PATCH 018/210] Refactor shell command syntax for consistency and readability --- mod/etc/options_menu/themes/scripts/om_vars | 166 ++++++++++---------- 1 file changed, 84 insertions(+), 82 deletions(-) diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 98d6825..9f7fc98 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -178,17 +178,17 @@ del_on_shonen="$snes_theme_path $del_shared" items_to_delete="$del_on_snes" # shellcheck disable=SC2012 -has_theme="$(ls -1 "$theme_path/$system".*.tar.gz 2>/dev/null | wc -l)" +has_theme="$(ls -1 "$theme_path/$system".*.tar.gz 2> /dev/null | wc -l)" ################ # shellcheck disable=SC2154 case "$sftype" in -hvcj) - items_to_delete="$del_on_shonen" - ;; -nes) - items_to_delete="$del_on_nes" - ;; + hvcj) + items_to_delete="$del_on_shonen" + ;; + nes) + items_to_delete="$del_on_nes" + ;; esac hasThemesToInstall() { @@ -233,7 +233,7 @@ enableMenuOptions() { if grep -q "$advThm" "$thm_chk"; then disable_cmds "AudioSetting" "ThemeSetting" "AdvancedMusicHack" fi - done <"$thmOverlay" + done < "$thmOverlay" if [ "$sftype" = "nes" ]; then disable_cmds "Pole" "Mario" "Luigi" "Wall" "WallNear" "WallFar" "Color" "Packed" @@ -273,7 +273,7 @@ themeLoader() { tmp_thm_script="$omThemeScript" if [ ! -f "$full_path/info" ]; then - echo "NAME=$thm_title [${size}b]" >"$full_path/info" + echo "NAME=$thm_title [${size}b]" > "$full_path/info" fi if [ ! -f "$full_path.png" ]; then @@ -310,12 +310,12 @@ themeLoader() { copier \"\$file\" \"\$file\"_backup copier \"$xtras/thumb.png\" \"\$file\" touch \"$hadThumb\" -done" >>"$thm_script.sh" +done" >> "$thm_script.sh" else echo "if [ -f \"$hadThumb\" ]; then sh $omModScripts/om_restorethumb rm \"$hadThumb\" -fi" >>"$thm_script.sh" +fi" >> "$thm_script.sh" fi if grep -q -w "$thm" "$thmOverlay" || [ ! -d "$has_folders" ]; then @@ -328,17 +328,17 @@ sed -i \"s/SELECTED_THEME=\\\"CHANGEME\\\"/SELECTED_THEME=\\\"${thm}\\\"/g;\" \" # if [ \"$tmp_thm_script\" = \"$omOverlayThemeScript\"] && [ ! -d \"${gStorage}/001\" ];then # sed -i \"s/HAS_FOLDERS=\\\"true\\\"/HAS_FOLDERS=\\\"false\\\"/g;\" \"$b_file\" # fi -" >>"$thm_script.sh" +" >> "$thm_script.sh" echo "sleep 1 - hakchi reboot" >>"$thm_script.sh" + hakchi reboot" >> "$thm_script.sh" preview_image="$thmPreview/$thm.png" [ ! -f "$preview_image" ] && preview_image="$theme_path/$thm.png" [ ! -f "$preview_image" ] && [ -z "${thm##diy_theme*}" ] && preview_image="$optionsMenu/images/diy_preview.png" [ ! -f "$preview_image" ] && preview_image="$optionsMenu/images/default_preview.png" - cmdName="$(sed -r 's/NAME=//g' <"$full_path/info")" + cmdName="$(sed -r 's/NAME=//g' < "$full_path/info")" echo "COMMAND_NAME=$cmdName COMMAND_TYPE=INTERNAL COMMAND_STR=sh $thm_script.sh @@ -346,7 +346,7 @@ PREVIEW_IMAGE=$preview_image PREVIEW_IMAGE_X=762 PREVIEW_IMAGE_Y=276 PREVIEW_IMAGE_WIDTH=0 -PREVIEW_IMAGE_HEIGHT=272" >"$cmd_list" +PREVIEW_IMAGE_HEIGHT=272" > "$cmd_list" done } @@ -359,18 +359,19 @@ remove_regional_theme() { } get_theme_list() { - decodepng "$omLoaders/downloadthemelist.png" >/dev/fb0 + decodepng "$omLoaders/downloadthemelist.png" > /dev/fb0 cd "$omModSub" || exit if [ ! -e "${s_type}${listName}" ]; then clearDownloads - if wget -q --spider "$downloadURL/${s_type}${listName}" >/dev/null 2>&1; then + if wget -q --spider "$downloadURL/${s_type}${listName}" > /dev/null 2>&1; then wget "$downloadURL/${s_type}${listName}" else netChecker fi fi + # shellcheck disable=SC2035 tar -xzf "${s_type}${listName}" && rm -f *.tar.gz remove_regional_theme touch "$subDummy" @@ -382,12 +383,12 @@ injectFile() { # shellcheck disable=SC2068 for scrpt in $@; do case "$scrpt" in - chmenu) - injected_file="$chmenu_file" - ;; - fontfix) - injected_file="$fontfix_file" - ;; + chmenu) + injected_file="$chmenu_file" + ;; + fontfix) + injected_file="$fontfix_file" + ;; esac if ! grep -q "# themeselector" "$injected_file"; then @@ -437,7 +438,7 @@ themeGenerator() { theme_pkg="${themeURL}/${system}.${theme_name}.tar.gz" [ "${theme_name}" = "all" ] && theme_pkg="${downloadURL}/${sftype}-all" - if wget -q --spider "${theme_pkg}" >/dev/null 2>&1; then + if wget -q --spider "${theme_pkg}" > /dev/null 2>&1; then echo "Downloading..." wget "${theme_pkg}" if [ "${theme_name}" = "all" ]; then @@ -447,14 +448,14 @@ themeGenerator() { theme_pkg="${themeURL}/${system}.${t}.tar.gz" wget "${theme_pkg}" tar -xzvf "${system}.${t}.tar.gz" && remove "${system}.${t}.tar.gz" - done <"${sftype}-all" + done < "${sftype}-all" fi [ -f "${system}.${theme_name}.tar.gz" ] && tar -xzvf "${system}.${theme_name}.tar.gz" && rm -f ./*.tar.gz remove "${sftype}-all" remove_regional_theme echo "" size="$(du -sh "${full_path}" | cut -f1)" - echo "NAME=${full_title}[${size}b]" >"${full_path}/info" + echo "NAME=${full_title}[${size}b]" > "${full_path}/info" echo "Installation complete." else netChecker @@ -476,54 +477,54 @@ find_diy_asset() { preview_h="250" case "$1" in - luigi | mario) - diy_file="${1}_run01.png" - asset_path="layout/asset/sprite/${1}" - asset="*.*" - chars="1" - preview_w="0" - ;; - sbm) - diy_file="characters_sbm.png" - asset_path="sprites" - asset="characters_sbm.png" - chars="1" - preview_w="0" - ;; - packed) - asset_path="sprites" - preview_x="780" - preview_w="0" - preview_h="200" - ;; - packed_nes) - diy_file="${sftype}.png" - [ "$sfregion" = "jpn" ] && diy_file="hvc.png" - asset_path="sprites" - asset="$diy_file" - preview_x="780" - preview_w="0" - preview_h="200" - ;; - colors) - asset_path="scripts/env" - diy_file="$1_$sfregion.lua" - asset="$diy_file" - colors="1" - ;; - colors_nes) - asset_path="scripts" - diy_file="${1}.lua" - [ "$sfregion" = "jpn" ] && diy_file="colors_hvc.lua" - asset="$diy_file" - colors="1" - ;; - pole) - diy_file="${1}01.png" - asset="*.*" - chars="1" - preview_w="0" - ;; + luigi | mario) + diy_file="${1}_run01.png" + asset_path="layout/asset/sprite/${1}" + asset="*.*" + chars="1" + preview_w="0" + ;; + sbm) + diy_file="characters_sbm.png" + asset_path="sprites" + asset="characters_sbm.png" + chars="1" + preview_w="0" + ;; + packed) + asset_path="sprites" + preview_x="780" + preview_w="0" + preview_h="200" + ;; + packed_nes) + diy_file="${sftype}.png" + [ "$sfregion" = "jpn" ] && diy_file="hvc.png" + asset_path="sprites" + asset="$diy_file" + preview_x="780" + preview_w="0" + preview_h="200" + ;; + colors) + asset_path="scripts/env" + diy_file="$1_$sfregion.lua" + asset="$diy_file" + colors="1" + ;; + colors_nes) + asset_path="scripts" + diy_file="${1}.lua" + [ "$sfregion" = "jpn" ] && diy_file="colors_hvc.lua" + asset="$diy_file" + colors="1" + ;; + pole) + diy_file="${1}01.png" + asset="*.*" + chars="1" + preview_w="0" + ;; esac om_default_json="$omModScripts/om_${1}.json" @@ -552,7 +553,7 @@ find_diy_asset() { cmd_name="$(echo "$asset_name" | sed -e 's/_/ /g' | awk '{for(i=1;i<=NF;i++){$i=toupper(substr($i,1,1))substr($i,2)}}1')" if [ "$p" = "$theme_path" ]; then - cmd_name="$(sed -r 's/NAME=//g' <"$full_asset_path/info" | awk '{$NF="";sub(/[ \t]+$/,"")}1')" + cmd_name="$(sed -r 's/NAME=//g' < "$full_asset_path/info" | awk '{$NF="";sub(/[ \t]+$/,"")}1')" full_asset_path="$resource_path/$asset_path" fi @@ -564,9 +565,9 @@ find_diy_asset() { fi [ "$chars" = "1" ] && temp_preview="$preview_image" if [ -n "$json" ] && [ ! -f "$temp_preview" ]; then - decodepng "$omLoaders/loading.png" >/dev/fb0 + decodepng "$omLoaders/loading.png" > /dev/fb0 if [ "$colors" = "0" ]; then - sed -r "s/changeme/${1}_${asset_name}/g;" "$om_default_json" >"$json" + sed -r "s/changeme/${1}_${asset_name}/g;" "$om_default_json" > "$json" pngpack-upx u "$json" "$preview_image" "$omModSprites/assets/$1" remove "$json" fi @@ -583,10 +584,10 @@ PREVIEW_IMAGE=$preview_image PREVIEW_IMAGE_X=$preview_x PREVIEW_IMAGE_Y=$preview_y PREVIEW_IMAGE_WIDTH=$preview_w -PREVIEW_IMAGE_HEIGHT=$preview_h" >"$cmd_file" +PREVIEW_IMAGE_HEIGHT=$preview_h" > "$cmd_file" fi copy "$omModLicense" "$sprite_script.sh" - echo "diySpriteSelector \"$full_asset_path\" \"$asset_path\" \"$asset\"" >>"$sprite_script.sh" + echo "diySpriteSelector \"$full_asset_path\" \"$asset_path\" \"$asset\"" >> "$sprite_script.sh" fi done fi @@ -601,8 +602,8 @@ diySpriteSelector() { [ ! -d "$diyAsset" ] && mkdir -p "$diyAsset" find "$diySelectedAsset" -name "$3" -exec cp {} "$diyLayout" \; if [ ! -f "$b_file" ]; then - sed '1,2d' "$omThemeScript" >"$b_file" - echo themeSelector >"$p_file" + sed '1,2d' "$omThemeScript" > "$b_file" + echo themeSelector > "$p_file" fi echo "" echo " ____ _____ __ _____ _ _ _____ __ __ _____ ____ ____ _____ _ _____ ___ ____ " @@ -625,10 +626,11 @@ diySpriteSelector() { } diyThemeChecker() { - [ "$1" = "loader" ] && decodepng "$omLoaders/loadingassets.png" >/dev/fb0 + [ "$1" = "loader" ] && decodepng "$omLoaders/loadingassets.png" > /dev/fb0 [ ! -d "$diyThemePath" ] && mkdir -p "$diyThemePath" create "$omModGenSpacer" + # shellcheck disable=SC3061 if find "$diyThemePath" -type f | read -r; then enable_cmds "DiY" "DelDiY" "Preview" else From 22ec63dd548bc1483867232a91e336b996cae94d Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 1 Sep 2026 10:24:56 +0000 Subject: [PATCH 019/210] Fix whitespace issues in script for improved readability --- mod/etc/options_menu/themes/scripts/om_vars | 44 ++++++++++----------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 9f7fc98..2638d1d 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -178,7 +178,7 @@ del_on_shonen="$snes_theme_path $del_shared" items_to_delete="$del_on_snes" # shellcheck disable=SC2012 -has_theme="$(ls -1 "$theme_path/$system".*.tar.gz 2> /dev/null | wc -l)" +has_theme="$(ls -1 "$theme_path/$system".*.tar.gz 2>/dev/null | wc -l)" ################ # shellcheck disable=SC2154 @@ -233,7 +233,7 @@ enableMenuOptions() { if grep -q "$advThm" "$thm_chk"; then disable_cmds "AudioSetting" "ThemeSetting" "AdvancedMusicHack" fi - done < "$thmOverlay" + done <"$thmOverlay" if [ "$sftype" = "nes" ]; then disable_cmds "Pole" "Mario" "Luigi" "Wall" "WallNear" "WallFar" "Color" "Packed" @@ -273,7 +273,7 @@ themeLoader() { tmp_thm_script="$omThemeScript" if [ ! -f "$full_path/info" ]; then - echo "NAME=$thm_title [${size}b]" > "$full_path/info" + echo "NAME=$thm_title [${size}b]" >"$full_path/info" fi if [ ! -f "$full_path.png" ]; then @@ -310,12 +310,12 @@ themeLoader() { copier \"\$file\" \"\$file\"_backup copier \"$xtras/thumb.png\" \"\$file\" touch \"$hadThumb\" -done" >> "$thm_script.sh" +done" >>"$thm_script.sh" else echo "if [ -f \"$hadThumb\" ]; then sh $omModScripts/om_restorethumb rm \"$hadThumb\" -fi" >> "$thm_script.sh" +fi" >>"$thm_script.sh" fi if grep -q -w "$thm" "$thmOverlay" || [ ! -d "$has_folders" ]; then @@ -328,17 +328,17 @@ sed -i \"s/SELECTED_THEME=\\\"CHANGEME\\\"/SELECTED_THEME=\\\"${thm}\\\"/g;\" \" # if [ \"$tmp_thm_script\" = \"$omOverlayThemeScript\"] && [ ! -d \"${gStorage}/001\" ];then # sed -i \"s/HAS_FOLDERS=\\\"true\\\"/HAS_FOLDERS=\\\"false\\\"/g;\" \"$b_file\" # fi -" >> "$thm_script.sh" +|" >>"$thm_script.sh" echo "sleep 1 - hakchi reboot" >> "$thm_script.sh" + hakchi reboot" >>"$thm_script.sh" preview_image="$thmPreview/$thm.png" [ ! -f "$preview_image" ] && preview_image="$theme_path/$thm.png" [ ! -f "$preview_image" ] && [ -z "${thm##diy_theme*}" ] && preview_image="$optionsMenu/images/diy_preview.png" [ ! -f "$preview_image" ] && preview_image="$optionsMenu/images/default_preview.png" - cmdName="$(sed -r 's/NAME=//g' < "$full_path/info")" + cmdName="$(sed -r 's/NAME=//g' <"$full_path/info")" echo "COMMAND_NAME=$cmdName COMMAND_TYPE=INTERNAL COMMAND_STR=sh $thm_script.sh @@ -346,7 +346,7 @@ PREVIEW_IMAGE=$preview_image PREVIEW_IMAGE_X=762 PREVIEW_IMAGE_Y=276 PREVIEW_IMAGE_WIDTH=0 -PREVIEW_IMAGE_HEIGHT=272" > "$cmd_list" +PREVIEW_IMAGE_HEIGHT=272" >"$cmd_list" done } @@ -359,12 +359,12 @@ remove_regional_theme() { } get_theme_list() { - decodepng "$omLoaders/downloadthemelist.png" > /dev/fb0 + decodepng "$omLoaders/downloadthemelist.png" >/dev/fb0 cd "$omModSub" || exit if [ ! -e "${s_type}${listName}" ]; then clearDownloads - if wget -q --spider "$downloadURL/${s_type}${listName}" > /dev/null 2>&1; then + if wget -q --spider "$downloadURL/${s_type}${listName}" >/dev/null 2>&1; then wget "$downloadURL/${s_type}${listName}" else netChecker @@ -438,7 +438,7 @@ themeGenerator() { theme_pkg="${themeURL}/${system}.${theme_name}.tar.gz" [ "${theme_name}" = "all" ] && theme_pkg="${downloadURL}/${sftype}-all" - if wget -q --spider "${theme_pkg}" > /dev/null 2>&1; then + if wget -q --spider "${theme_pkg}" >/dev/null 2>&1; then echo "Downloading..." wget "${theme_pkg}" if [ "${theme_name}" = "all" ]; then @@ -448,14 +448,14 @@ themeGenerator() { theme_pkg="${themeURL}/${system}.${t}.tar.gz" wget "${theme_pkg}" tar -xzvf "${system}.${t}.tar.gz" && remove "${system}.${t}.tar.gz" - done < "${sftype}-all" + done <"${sftype}-all" fi [ -f "${system}.${theme_name}.tar.gz" ] && tar -xzvf "${system}.${theme_name}.tar.gz" && rm -f ./*.tar.gz remove "${sftype}-all" remove_regional_theme echo "" size="$(du -sh "${full_path}" | cut -f1)" - echo "NAME=${full_title}[${size}b]" > "${full_path}/info" + echo "NAME=${full_title}[${size}b]" >"${full_path}/info" echo "Installation complete." else netChecker @@ -553,7 +553,7 @@ find_diy_asset() { cmd_name="$(echo "$asset_name" | sed -e 's/_/ /g' | awk '{for(i=1;i<=NF;i++){$i=toupper(substr($i,1,1))substr($i,2)}}1')" if [ "$p" = "$theme_path" ]; then - cmd_name="$(sed -r 's/NAME=//g' < "$full_asset_path/info" | awk '{$NF="";sub(/[ \t]+$/,"")}1')" + cmd_name="$(sed -r 's/NAME=//g' <"$full_asset_path/info" | awk '{$NF="";sub(/[ \t]+$/,"")}1')" full_asset_path="$resource_path/$asset_path" fi @@ -565,9 +565,9 @@ find_diy_asset() { fi [ "$chars" = "1" ] && temp_preview="$preview_image" if [ -n "$json" ] && [ ! -f "$temp_preview" ]; then - decodepng "$omLoaders/loading.png" > /dev/fb0 + decodepng "$omLoaders/loading.png" >/dev/fb0 if [ "$colors" = "0" ]; then - sed -r "s/changeme/${1}_${asset_name}/g;" "$om_default_json" > "$json" + sed -r "s/changeme/${1}_${asset_name}/g;" "$om_default_json" >"$json" pngpack-upx u "$json" "$preview_image" "$omModSprites/assets/$1" remove "$json" fi @@ -584,10 +584,10 @@ PREVIEW_IMAGE=$preview_image PREVIEW_IMAGE_X=$preview_x PREVIEW_IMAGE_Y=$preview_y PREVIEW_IMAGE_WIDTH=$preview_w -PREVIEW_IMAGE_HEIGHT=$preview_h" > "$cmd_file" +PREVIEW_IMAGE_HEIGHT=$preview_h" >"$cmd_file" fi copy "$omModLicense" "$sprite_script.sh" - echo "diySpriteSelector \"$full_asset_path\" \"$asset_path\" \"$asset\"" >> "$sprite_script.sh" + echo "diySpriteSelector \"$full_asset_path\" \"$asset_path\" \"$asset\"" >>"$sprite_script.sh" fi done fi @@ -602,8 +602,8 @@ diySpriteSelector() { [ ! -d "$diyAsset" ] && mkdir -p "$diyAsset" find "$diySelectedAsset" -name "$3" -exec cp {} "$diyLayout" \; if [ ! -f "$b_file" ]; then - sed '1,2d' "$omThemeScript" > "$b_file" - echo themeSelector > "$p_file" + sed '1,2d' "$omThemeScript" >"$b_file" + echo themeSelector >"$p_file" fi echo "" echo " ____ _____ __ _____ _ _ _____ __ __ _____ ____ ____ _____ _ _____ ___ ____ " @@ -626,7 +626,7 @@ diySpriteSelector() { } diyThemeChecker() { - [ "$1" = "loader" ] && decodepng "$omLoaders/loadingassets.png" > /dev/fb0 + [ "$1" = "loader" ] && decodepng "$omLoaders/loadingassets.png" >/dev/fb0 [ ! -d "$diyThemePath" ] && mkdir -p "$diyThemePath" create "$omModGenSpacer" From c34dff16425c3950622ac212b0a4d7a316325f34 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 9 Sep 2026 22:49:45 +0000 Subject: [PATCH 020/210] Refactor function names for consistency and readability; update workspace settings for improved formatting --- .../themes/creator/scripts/om_diy_asset | 2 +- .../themes/downloads/scripts/om_refreshThemes | 2 +- .../options_menu/themes/scripts/om_getThemes | 2 +- .../themes/scripts/om_overlaythmcript | 8 +- .../themes/scripts/om_themescript | 43 +++---- mod/etc/options_menu/themes/scripts/om_vars | 108 ++++++++---------- mod/uninstall | 2 +- theme-selector-workspace.code-workspace | 15 ++- 8 files changed, 93 insertions(+), 89 deletions(-) diff --git a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset index 77767ba..d82dd08 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset +++ b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset @@ -56,6 +56,6 @@ sbm) ;; esac -find_diy_asset "$1" +findDiyAsset "$1" $optionsMenu/options --commandPath $diy_om_sprite_path/commands/ --scriptPath $diy_om_sprite_path/scripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes index aadf3df..0bdaaa2 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes +++ b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes @@ -20,6 +20,6 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init -get_theme_list +getThemeList $optionsMenu/options --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/scripts/om_getThemes b/mod/etc/options_menu/themes/scripts/om_getThemes index fd7c557..7388749 100644 --- a/mod/etc/options_menu/themes/scripts/om_getThemes +++ b/mod/etc/options_menu/themes/scripts/om_getThemes @@ -20,6 +20,6 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init -[ ! -f "$subDummy" ] && get_theme_list +[ ! -f "$subDummy" ] && getThemeList $optionsMenu/options --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/scripts/om_overlaythmcript b/mod/etc/options_menu/themes/scripts/om_overlaythmcript index ef23b98..f5453a2 100644 --- a/mod/etc/options_menu/themes/scripts/om_overlaythmcript +++ b/mod/etc/options_menu/themes/scripts/om_overlaythmcript @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -font_bind_fix() { +fontBindFix() { [ "$1" == "backup" ] && FONT_TYPE="title s m l small medium large" [ "$1" == "restore" ] && FONT_TYPE="s m l small medium large" [ "$1" == "restore" ] && [ -f "$TITLE_FONT_BACKUP" ] && mv "$TITLE_FONT_BACKUP" "$TITLE_FONT" @@ -57,7 +57,7 @@ themeSelector() { local STATE_PATH="$mountpoint$profilepath/$modname" local LAST_THEME="$STATE_PATH/lastTheme" - local ACTIVE_THEME="$STATE_PATH/activeTheme" + local ACTIVE_THEME="$STATE_PATH/active_theme" local SELECTED_THEME="CHANGEME" if [ -f "$STATE_PATH/om_themerandomizer" ]; then @@ -84,10 +84,10 @@ themeSelector() { if [ -d "$FULL_THEME_PATH/fonts" ]; then if [ "$(cksum "$TITLE_FONT" | awk '{ print $1; }')" != "$(cksum "$TITLE_THEME_FONT" | awk '{ print $1; }')" ]; then - font_bind_fix "backup" + fontBindFix "backup" fi else - font_bind_fix "restore" + fontBindFix "restore" fi mountpoint -q "$ORIGINAL_THEME" && umount "$ORIGINAL_THEME" diff --git a/mod/etc/options_menu/themes/scripts/om_themescript b/mod/etc/options_menu/themes/scripts/om_themescript index 90054b6..cb8cbf4 100644 --- a/mod/etc/options_menu/themes/scripts/om_themescript +++ b/mod/etc/options_menu/themes/scripts/om_themescript @@ -16,7 +16,7 @@ # along with this program. If not, see . # -music_hack_bind() { +musicHackBind() { { [ -f "$1" ] && [ -f "$2" ] && [ -f "$3/bgm_boot.wav" ] && [ -f "$3/bgm_home.wav" ]; } || return 1 if [ "$sftype" = "nes" ]; then @@ -29,15 +29,19 @@ music_hack_bind() { return 0 } -music_hack_unbind() { +musicHackUnbind() { AUDIO_FILES_ARR="bgm_boot bgm_home se_sys_bounce se_sys_cancel se_sys_click se_sys_click_game se_sys_cursor se_sys_lock se_sys_ng se_sys_push se_sys_smoke se_sys_unlock" + local CURRENT_MOUNTS + CURRENT_MOUNTS="$(cat /proc/mounts)" for WAVE in $AUDIO_FILES_ARR; do - umount "${ORIGINAL_THEME_AUDIO}/${WAVE}.wav" + case "$CURRENT_MOUNTS" in + *" ${ORIGINAL_THEME_AUDIO}/${WAVE}.wav "*) umount "${ORIGINAL_THEME_AUDIO}/${WAVE}.wav" ;; + esac done } -font_bind_fix() { +fontBindFix() { [ "$1" == "backup" ] && FONT_TYPE="title s m l small medium large" [ "$1" == "restore" ] && FONT_TYPE="s m l small medium large" [ "$1" == "restore" ] && [ -f "$TITLE_FONT_BACKUP" ] && mv "$TITLE_FONT_BACKUP" "$TITLE_FONT" @@ -84,11 +88,13 @@ themeSelector() { local ORIGINAL_THEME_AUDIO="$ORIGINAL_THEME/resources/sounds/hvc" local THEMES_PATH="$mountpoint/media/$modname/themes/$SYSTEM_TYPE" [ ! -d "$THEMES_PATH" ] && THEMES_PATH="$rootfs/usr/share/themes/$SYSTEM_TYPE" + local THEME_DIR_LIST + THEME_DIR_LIST="$(find "$THEMES_PATH" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sed 's#^.*/##')" STATE_PATH="$mountpoint$profilepath/$modname" FOLDER_ID="$(cat "$STATE_PATH/menu")" local ACTIVE_FOLDER="CLV-S-00${FOLDER_ID}" local LAST_THEME="$STATE_PATH/lastTheme" - local ACTIVE_THEME="$STATE_PATH/activeTheme" + local ACTIVE_THEME="$STATE_PATH/active_theme" [ -f "$LAST_THEME" ] && PREVIOUS_THEME=$(cat "$LAST_THEME") local SELECTED_THEME="CHANGEME" @@ -103,13 +109,12 @@ themeSelector() { SILENT_WAV="$rootfs/usr/share/themes/bgm_boot.wav" MUSIC_PATH="$mountpoint/media/$modname/music_menu" [ ! -d "$MUSIC_PATH" ] && MUSIC_PATH="$rootfs/usr/share/music_menu" - WAV_FILE="$(find "$MUSIC_PATH" -maxdepth 1 -type f -name "*.[Ww][Aa][Vv]" -exec basename {} \; | sed "s|^\./||" | shuf | head -n1)" - BGM_HOME="$MUSIC_PATH/$WAV_FILE" + BGM_HOME="" GAME_STORAGE="$(findGameStorage)" find "$GAME_STORAGE" -maxdepth 3 -type f -name "$ACTIVE_FOLDER.desktop" | sort -u | while IFS= read -r CONFIG_FILE; do cd "$GAME_STORAGE" || exit - for i in $(find . -type d -name "$ACTIVE_FOLDER" | sed "s|^\./||"); do + for i in $(find . -maxdepth 3 -type d -name "$ACTIVE_FOLDER" | sed "s|^\./||"); do if [ "$(grep -L -e "Name=Back" -e "Name=Home" "$i/$(basename "$i").desktop")" ]; then PARENT_ID="$(dirname "$i")" @@ -122,17 +127,16 @@ themeSelector() { PARENT_THEME_TITLE="$(grep -F 'Name=' "$w" | sed -r 's/Name=//g;s/ /_/g' | awk '{print tolower($0)}')" done /dev/null | read -r; then FULL_THEME_PATH="$DIY_THEME" fi @@ -156,24 +160,23 @@ themeSelector() { fi if [ -d "$THEME_FONT_PATH" ]; then - if [ "$(cksum "$TITLE_FONT" | awk '{ print $1; }')" != "$(cksum "$THEME_FONT_PATH/title.fnt" | awk '{ print $1; }')" ]; then - font_bind_fix "backup" - fi + cmp -s "$TITLE_FONT" "$THEME_FONT_PATH/title.fnt" || fontBindFix "backup" else - font_bind_fix "restore" + fontBindFix "restore" fi - music_hack_unbind + musicHackUnbind cd "$THEMES_PATH/$PREVIOUS_THEME" && find ./* -type f ! -name "*.wav" ! -path "./xtras/*" ! -path "./fonts/*" | cut -c3- | while IFS= read -r UMOUNT_FILE; do umount "$ORIGINAL_THEME/$UMOUNT_FILE" done if [ -f "$STATE_PATH/om_audiorandomizer" ] && [ -n "$(ls -A "$MUSIC_PATH")" ]; then + [ -z "$BGM_HOME" ] && BGM_HOME="$(find "$MUSIC_PATH" -maxdepth 1 -type f -iname "*.wav" | shuf | head -n1)" if [ "$FOLDER_ID" = "000" ]; then - music_hack_bind "$BGM_HOME" "$BGM_HOME" "$ORIGINAL_THEME_AUDIO" + musicHackBind "$BGM_HOME" "$BGM_HOME" "$ORIGINAL_THEME_AUDIO" else - [ -f "$STATE_PATH/om_advmusichack" ] && [ -z "$(find "$FULL_THEME_PATH" -type f -name "*.wav")" ] && music_hack_bind "$BGM_HOME" "$BGM_HOME" "$ORIGINAL_THEME_AUDIO" + [ -f "$STATE_PATH/om_advmusichack" ] && [ -z "$(find "$FULL_THEME_PATH" -type f -name "*.wav")" ] && musicHackBind "$BGM_HOME" "$BGM_HOME" "$ORIGINAL_THEME_AUDIO" fi fi diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 2638d1d..f56d4b3 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -30,8 +30,8 @@ has_folders="${gStorage}/001" downloadURL="http://hakchicloud.com/Hakchi_Themes/scripts" themeURL="http://hakchicloud.com/Hakchi_Themes/themes" -! wget -q --spider "$downloadURL" >/dev/null 2>&1 && downloadURL="http://classicmods.net/files/themes/themeselector-scripts/" -! wget -q --spider "$themeURL" >/dev/null 2>&1 && themeURL="http://classicmods.net/files/themes/themeselector/" +! wget -q --spider "$downloadURL" > /dev/null 2>&1 && downloadURL="http://classicmods.net/files/themes/themeselector-scripts/" +! wget -q --spider "$themeURL" > /dev/null 2>&1 && themeURL="http://classicmods.net/files/themes/themeselector/" # Options Menu omMod="$optionsMenu/$modtitle" @@ -118,8 +118,8 @@ enableSystemCleanUp="$omModSettingsCmds/c0001_systemcleanup" disableSystemCleanUp="$omModSettingsCmds/_systemcleanup" enableThemeGenerator="$omModCommands/c0000_generatetheme" disableThemeGenerator="$omModCommands/_generatetheme" -enableUninstaller="c0001_uninstaller" -disableUninstaller="_uninstaller" +enableUninstaller="$omModSettingsCmds/c0001_uninstaller" +disableUninstaller="$omModSettingsCmds/_uninstaller" enableDiY="$omModGenCmds/c0000_apply" disableDiY="$omModGenCmds/_apply" enableDelDiY="$omModGenCmds/c0000_remove" @@ -151,12 +151,12 @@ disableWallNear="$omModGenCmds/_wall_near" listName="-list-v2.tar.gz" # SHARED -thm_chk="$mountpoint$profilepath/$modname/activeTheme" +thm_chk="$mountpoint$profilepath/$modname/active_theme" system="$(echo "$s_type" | awk '{print toupper($0)}')" om_mod_path="$nand_path/$modtitle" theme_path="$usb_path/$modtitle/$s_type" [ ! -d "$theme_path" ] && theme_path="$nand_path/$modtitle/$s_type" -nes_theme_path="${om_mod_path}/nes " +nes_theme_path="${om_mod_path}/nes" shonen_theme_path="${om_mod_path}/shonen" snes_theme_path="${om_mod_path}/snes" @@ -178,10 +178,9 @@ del_on_shonen="$snes_theme_path $del_shared" items_to_delete="$del_on_snes" # shellcheck disable=SC2012 -has_theme="$(ls -1 "$theme_path/$system".*.tar.gz 2>/dev/null | wc -l)" +has_theme="$(ls -1 "$theme_path/$system".*.tar.gz 2> /dev/null | wc -l)" ################ -# shellcheck disable=SC2154 case "$sftype" in hvcj) items_to_delete="$del_on_shonen" @@ -202,14 +201,14 @@ hasThemesToInstall() { clearList() { find "$omModCommands" "$omModScripts" -type f \ -not -name 'om_*' -not -name 'c0000_*' \ - -not -name 'c999[0-9]_*' -not -name '_*' -print0 -exec rm {} \; + -not -name 'c999[0-9]_*' -not -name '_*' -exec rm {} + remove "$omModSpacer" } clearDownloads() { find "$omModSubCmds" "$omModSubScripts" "$omModSub" -type f \ -not -name 'c000[0-8]_*' -not -name 'c999[0-9]_*' \ - -not -name 'om_*' -not -name '_*' -print0 -exec rm {} \; + -not -name 'om_*' -not -name '_*' -exec rm {} + } enableMenuOptions() { @@ -222,7 +221,7 @@ enableMenuOptions() { toggleDownloads - [[ -n $(ls -A "$theme_path") ]] && disable_cmds "ThemeGenerator" + [ -z "$(ls -A "$theme_path")" ] && disable_cmds "ThemeGenerator" if [ -f "$b_file" ] || [ -f "$p_file" ]; then enable_cmds "Settings" @@ -233,7 +232,7 @@ enableMenuOptions() { if grep -q "$advThm" "$thm_chk"; then disable_cmds "AudioSetting" "ThemeSetting" "AdvancedMusicHack" fi - done <"$thmOverlay" + done < "$thmOverlay" if [ "$sftype" = "nes" ]; then disable_cmds "Pole" "Mario" "Luigi" "Wall" "WallNear" "WallFar" "Color" "Packed" @@ -257,9 +256,8 @@ enableMenuOptions() { } themeLoader() { - remove_regional_theme - # shellcheck disable=SC2038 - cd "$theme_path" && find . -mindepth 1 -maxdepth 1 -type d | xargs -n 1 basename | sort -u | while IFS= read -r thm; do + removeRegionalTheme + cd "$theme_path" && find . -mindepth 1 -maxdepth 1 -type d | sed 's#^\./##' | sort -u | while IFS= read -r thm; do full_path="$theme_path/$thm" xtras="$full_path/xtras" resource_path="$full_path/resources" @@ -267,13 +265,13 @@ themeLoader() { script_path="$resource_path/scripts" cmd_list="$omModCommands/c0001_$thm" thm_script="$omModScripts/$thm" - hadThumb="$theme_path/thumb" - thm_title="$(echo "$thm" | sed -e 's/_/ /g' | awk '{for(i=1;i<=NF;i++){$i=toupper(substr($i,1,1))substr($i,2)}}1')" - size="$(du -sh "$full_path" | cut -f1)" + had_thumb="$theme_path/thumb" tmp_thm_script="$omThemeScript" if [ ! -f "$full_path/info" ]; then - echo "NAME=$thm_title [${size}b]" >"$full_path/info" + thm_title="$(echo "$thm" | sed -e 's/_/ /g' | awk '{for(i=1;i<=NF;i++){$i=toupper(substr($i,1,1))substr($i,2)}}1')" + size="$(du -sh "$full_path" | cut -f1)" + echo "NAME=$thm_title [${size}b]" > "$full_path/info" fi if [ ! -f "$full_path.png" ]; then @@ -286,9 +284,6 @@ themeLoader() { create "$omModSpacer" - remove "$cmd_list" - touch "$cmd_list" - if [ "$sftype" = "nes" ]; then src="hvc" dest="nes" @@ -309,13 +304,13 @@ themeLoader() { echo "find \"$gStorage\" -path \"*_small.png*\" | while read -r file; do copier \"\$file\" \"\$file\"_backup copier \"$xtras/thumb.png\" \"\$file\" -touch \"$hadThumb\" -done" >>"$thm_script.sh" +touch \"$had_thumb\" +done" >> "$thm_script.sh" else - echo "if [ -f \"$hadThumb\" ]; then + echo "if [ -f \"$had_thumb\" ]; then sh $omModScripts/om_restorethumb - rm \"$hadThumb\" -fi" >>"$thm_script.sh" + rm \"$had_thumb\" +fi" >> "$thm_script.sh" fi if grep -q -w "$thm" "$thmOverlay" || [ ! -d "$has_folders" ]; then @@ -325,32 +320,29 @@ fi" >>"$thm_script.sh" echo "sed '1,2d' \"$tmp_thm_script\" >\"$b_file\" [ ! -f \"$p_file\" ] && echo themeSelector >\"$p_file\" sed -i \"s/SELECTED_THEME=\\\"CHANGEME\\\"/SELECTED_THEME=\\\"${thm}\\\"/g;\" \"$b_file\" -# if [ \"$tmp_thm_script\" = \"$omOverlayThemeScript\"] && [ ! -d \"${gStorage}/001\" ];then -# sed -i \"s/HAS_FOLDERS=\\\"true\\\"/HAS_FOLDERS=\\\"false\\\"/g;\" \"$b_file\" -# fi -|" >>"$thm_script.sh" +" >> "$thm_script.sh" echo "sleep 1 - hakchi reboot" >>"$thm_script.sh" + hakchi reboot" >> "$thm_script.sh" preview_image="$thmPreview/$thm.png" [ ! -f "$preview_image" ] && preview_image="$theme_path/$thm.png" [ ! -f "$preview_image" ] && [ -z "${thm##diy_theme*}" ] && preview_image="$optionsMenu/images/diy_preview.png" [ ! -f "$preview_image" ] && preview_image="$optionsMenu/images/default_preview.png" - cmdName="$(sed -r 's/NAME=//g' <"$full_path/info")" - echo "COMMAND_NAME=$cmdName + cmd_name="$(sed -r 's/NAME=//g' < "$full_path/info")" + echo "COMMAND_NAME=$cmd_name COMMAND_TYPE=INTERNAL COMMAND_STR=sh $thm_script.sh PREVIEW_IMAGE=$preview_image PREVIEW_IMAGE_X=762 PREVIEW_IMAGE_Y=276 PREVIEW_IMAGE_WIDTH=0 -PREVIEW_IMAGE_HEIGHT=272" >"$cmd_list" +PREVIEW_IMAGE_HEIGHT=272" > "$cmd_list" done } -remove_regional_theme() { +removeRegionalTheme() { region_files="$omModSubCmds/c0010_snesce-$sfregion $omModSubScripts/getSnes$sfregion $omModSub/$sfregion.png $omModSub/snesce-$sfregion.png" for item in $region_files; do remove "$item" @@ -358,13 +350,13 @@ remove_regional_theme() { [ -d "$theme_path/snesce-$sfregion" ] && rm -rf "$theme_path/snesce-$sfregion" } -get_theme_list() { - decodepng "$omLoaders/downloadthemelist.png" >/dev/fb0 +getThemeList() { + decodepng "$omLoaders/downloadthemelist.png" > /dev/fb0 cd "$omModSub" || exit if [ ! -e "${s_type}${listName}" ]; then clearDownloads - if wget -q --spider "$downloadURL/${s_type}${listName}" >/dev/null 2>&1; then + if wget -q --spider "$downloadURL/${s_type}${listName}" > /dev/null 2>&1; then wget "$downloadURL/${s_type}${listName}" else netChecker @@ -373,7 +365,7 @@ get_theme_list() { fi # shellcheck disable=SC2035 tar -xzf "${s_type}${listName}" && rm -f *.tar.gz - remove_regional_theme + removeRegionalTheme touch "$subDummy" create "$omModSubSpacer" usleep 1000 @@ -402,8 +394,8 @@ fontFix' "/var/lib/hakchi/rootfs/bin/chmenu" if [ "$scrpt" = "fontfix" ]; then # shellcheck disable=SC1004,SC2016 sed -i '/^fontFix_getPath().*/a # themeselector \ - if [ -f "$mountpoint$profilepath/$modname/activeTheme" ]; then \ - active_theme=$(cat "$mountpoint$profilepath/$modname/activeTheme") \ + if [ -f "$mountpoint$profilepath/$modname/active_theme" ]; then \ + active_theme=$(cat "$mountpoint$profilepath/$modname/active_theme") \ fontFix_getPath0 "$active_theme/fonts/$1" && return 0 \ fi' "$mountpoint/etc/preinit.d/b8025_fontfix" fi @@ -438,7 +430,7 @@ themeGenerator() { theme_pkg="${themeURL}/${system}.${theme_name}.tar.gz" [ "${theme_name}" = "all" ] && theme_pkg="${downloadURL}/${sftype}-all" - if wget -q --spider "${theme_pkg}" >/dev/null 2>&1; then + if wget -q --spider "${theme_pkg}" > /dev/null 2>&1; then echo "Downloading..." wget "${theme_pkg}" if [ "${theme_name}" = "all" ]; then @@ -448,14 +440,14 @@ themeGenerator() { theme_pkg="${themeURL}/${system}.${t}.tar.gz" wget "${theme_pkg}" tar -xzvf "${system}.${t}.tar.gz" && remove "${system}.${t}.tar.gz" - done <"${sftype}-all" + done < "${sftype}-all" fi [ -f "${system}.${theme_name}.tar.gz" ] && tar -xzvf "${system}.${theme_name}.tar.gz" && rm -f ./*.tar.gz remove "${sftype}-all" - remove_regional_theme + removeRegionalTheme echo "" size="$(du -sh "${full_path}" | cut -f1)" - echo "NAME=${full_title}[${size}b]" >"${full_path}/info" + echo "NAME=${full_title}[${size}b]" > "${full_path}/info" echo "Installation complete." else netChecker @@ -463,7 +455,7 @@ themeGenerator() { } -find_diy_asset() { +findDiyAsset() { diy_file="${1}.png" diy_sprite="$omModSprites/${1}" asset_path="layout/asset/sprite/wallpaper" @@ -532,7 +524,7 @@ find_diy_asset() { find "$diy_sprite/commands" "$diy_sprite/scripts" -type f \ -not -name 'om_*' -not -name 'c999[0-9]_*' \ - -not -name 'c0000_*' -not -name '_*' -print0 -exec rm {} \; + -not -name 'c0000_*' -not -name '_*' -exec rm {} + for p in $paths_to_search; do if [ "$(ls -A "$p")" ]; then @@ -553,7 +545,7 @@ find_diy_asset() { cmd_name="$(echo "$asset_name" | sed -e 's/_/ /g' | awk '{for(i=1;i<=NF;i++){$i=toupper(substr($i,1,1))substr($i,2)}}1')" if [ "$p" = "$theme_path" ]; then - cmd_name="$(sed -r 's/NAME=//g' <"$full_asset_path/info" | awk '{$NF="";sub(/[ \t]+$/,"")}1')" + cmd_name="$(sed -r 's/NAME=//g' < "$full_asset_path/info" | awk '{$NF="";sub(/[ \t]+$/,"")}1')" full_asset_path="$resource_path/$asset_path" fi @@ -565,9 +557,9 @@ find_diy_asset() { fi [ "$chars" = "1" ] && temp_preview="$preview_image" if [ -n "$json" ] && [ ! -f "$temp_preview" ]; then - decodepng "$omLoaders/loading.png" >/dev/fb0 + decodepng "$omLoaders/loading.png" > /dev/fb0 if [ "$colors" = "0" ]; then - sed -r "s/changeme/${1}_${asset_name}/g;" "$om_default_json" >"$json" + sed -r "s/changeme/${1}_${asset_name}/g;" "$om_default_json" > "$json" pngpack-upx u "$json" "$preview_image" "$omModSprites/assets/$1" remove "$json" fi @@ -584,10 +576,10 @@ PREVIEW_IMAGE=$preview_image PREVIEW_IMAGE_X=$preview_x PREVIEW_IMAGE_Y=$preview_y PREVIEW_IMAGE_WIDTH=$preview_w -PREVIEW_IMAGE_HEIGHT=$preview_h" >"$cmd_file" +PREVIEW_IMAGE_HEIGHT=$preview_h" > "$cmd_file" fi copy "$omModLicense" "$sprite_script.sh" - echo "diySpriteSelector \"$full_asset_path\" \"$asset_path\" \"$asset\"" >>"$sprite_script.sh" + echo "diySpriteSelector \"$full_asset_path\" \"$asset_path\" \"$asset\"" >> "$sprite_script.sh" fi done fi @@ -602,8 +594,8 @@ diySpriteSelector() { [ ! -d "$diyAsset" ] && mkdir -p "$diyAsset" find "$diySelectedAsset" -name "$3" -exec cp {} "$diyLayout" \; if [ ! -f "$b_file" ]; then - sed '1,2d' "$omThemeScript" >"$b_file" - echo themeSelector >"$p_file" + sed '1,2d' "$omThemeScript" > "$b_file" + echo themeSelector > "$p_file" fi echo "" echo " ____ _____ __ _____ _ _ _____ __ __ _____ ____ ____ _____ _ _____ ___ ____ " @@ -626,7 +618,7 @@ diySpriteSelector() { } diyThemeChecker() { - [ "$1" = "loader" ] && decodepng "$omLoaders/loadingassets.png" >/dev/fb0 + [ "$1" = "loader" ] && decodepng "$omLoaders/loadingassets.png" > /dev/fb0 [ ! -d "$diyThemePath" ] && mkdir -p "$diyThemePath" create "$omModGenSpacer" @@ -644,10 +636,10 @@ diyThemeRemover() { disable_cmds "DiY" "DelDiY" "Preview" for item in $del_arr; do - find "$item" -type f -not -name 'c999[0-9]_*' -not -name 'om_*' -maxdepth 1 -print0 -exec rm {} \; + find "$item" -type f -not -name 'c999[0-9]_*' -not -name 'om_*' -maxdepth 1 -exec rm {} + done - [ -d "$diyThemePath" ] && find "$diyThemePath" -type f -not -name 'assets_here' -print0 -exec rm {} \; + [ -d "$diyThemePath" ] && find "$diyThemePath" -type f -not -name 'assets_here' -exec rm {} + } diff --git a/mod/uninstall b/mod/uninstall index d4d5638..bcc9ebf 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -51,7 +51,7 @@ copy "$rootfs/etc/options_menu/themes/scripts/om_chmenu_og" "$rootfs/bin/chmenu" copy "$rootfs/etc/options_menu/themes/scripts/om_fontfix_og" "$rootfs/etc/preinit.d/b8025_fontfix" remove "$mountpoint/$profilepath/$modname/lastTheme" remove "$mountpoint/$profilepath/$modname/prevTheme" -remove "$mountpoint/$profilepath/$modname/activeTheme" +remove "$mountpoint/$profilepath/$modname/active_theme" remove "$mountpoint/$profilepath/$modname/om_advmusichack" remove "$mountpoint/$profilepath/$modname/om_audiorandomizer" remove "$mountpoint/$profilepath/$modname/om_themerandomizer" diff --git a/theme-selector-workspace.code-workspace b/theme-selector-workspace.code-workspace index 16e3175..f090f92 100644 --- a/theme-selector-workspace.code-workspace +++ b/theme-selector-workspace.code-workspace @@ -12,11 +12,14 @@ "settings": { "files.autoSave": "off", "[bash]": { - "editor.formatOnSave": true + "editor.formatOnSave": true, + "editor.tabSize": 2, + "editor.insertSpaces": true, + "editor.detectIndentation": false }, // "terminal.integrated.shell.windows": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", // "terminal.integrated.shell.windows": "C:\\Program Files\\Git\\bin\\bash.exe", - "shellformat.path": "C:\\bin\\shfmt_v3.2.4_windows_amd64.exe", + "shellformat.path": "C:\\bin\\shfmt_v3.12.0_windows_amd64.exe", "window.zoomLevel": 0, "files.associations": { "b[0-9]*": "shellscript", @@ -48,6 +51,7 @@ "shellcheck.enable": true, "shellcheck.useWorkspaceRootAsCwd": true, "shellcheck.run": "onSave", + "shellcheck.enableQuickFix": true, "shellcheck.customArgs": [ "-x" ], @@ -57,6 +61,11 @@ "3046", "3051" ], - "shellformat.flag": "-i 2 -ci -sr -bn", + // "shellformat.flag": "-i 2 -ci -sr -bn", + "shfmtIde.flag": "-p -bn -ci -i=2 -ln=posix/sh", + "shfmt.executableArgs": [ + "-i=2", + "-ci" + ] } } \ No newline at end of file From 915e9d3964e392be484c098561d1abf944dd1e74 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 9 Sep 2026 22:51:09 +0000 Subject: [PATCH 021/210] Refactor command names and add localization support for theme options - Updated command names to use uppercase and underscores for consistency across the theme options and commands. - Introduced translation functionality in the script to support multiple languages. - Added English and Portuguese language files for theme-related strings. - Modified various command scripts to utilize the new translation function for error messages and prompts. - Ensured all theme-related commands are now properly localized and follow the new naming convention. --- .../options_menu/commands/c0090_ThemeOptions | 2 +- mod/etc/options_menu/scripts/om_functions | 22 +++++++++--- .../options_menu/themes/commands/_download | 2 +- mod/etc/options_menu/themes/commands/_remove | 2 +- .../themes/commands/c0000_generatetheme | 2 +- .../themes/commands/c0000_options | 2 +- .../themes/commands/c0000_refresh | 2 +- .../options_menu/themes/commands/c9999_Back | 2 +- .../options_menu/themes/commands/c9999_Exit | 2 +- .../themes/creator/commands/_apply | 2 +- .../themes/creator/commands/_preview | 3 +- .../themes/creator/commands/_remove | 2 +- .../themes/creator/commands/c0000_refresh | 2 +- .../themes/creator/commands/c0001_packed | 2 +- .../themes/creator/commands/c0001_packed_nes | 2 +- .../themes/creator/commands/c0001_wall | 2 +- .../themes/creator/commands/c0002_mario | 2 +- .../themes/creator/commands/c0002_sbm | 2 +- .../themes/creator/commands/c0003_luigi | 2 +- .../themes/creator/commands/c0004_wall_near | 2 +- .../themes/creator/commands/c0005_wall_far | 2 +- .../themes/creator/commands/c0006_colors | 2 +- .../themes/creator/commands/c0006_colors_nes | 2 +- .../themes/creator/commands/c0006_pole | 2 +- .../themes/creator/commands/c9999_Back | 4 +-- .../sprites/colors/commands/c9999_Back | 2 +- .../sprites/colors_nes/commands/c9999_Back | 2 +- .../creator/sprites/luigi/commands/c9999_Back | 2 +- .../creator/sprites/mario/commands/c9999_Back | 2 +- .../sprites/packed/commands/c9999_Back | 2 +- .../sprites/packed_nes/commands/c9999_Back | 2 +- .../creator/sprites/pole/commands/c9999_Back | 2 +- .../creator/sprites/sbm/commands/c9999_Back | 2 +- .../creator/sprites/wall/commands/c9999_Back | 2 +- .../sprites/wall_far/commands/c9999_Back | 2 +- .../sprites/wall_near/commands/c9999_Back | 2 +- .../downloads/commands/c0000_downloadThemes | 2 +- .../downloads/commands/c0000_removeThemes | 2 +- .../themes/downloads/commands/c9999_Back | 2 +- mod/etc/options_menu/themes/lang/en-US.lang | 36 +++++++++++++++++++ mod/etc/options_menu/themes/lang/pt-PT.lang | 36 +++++++++++++++++++ mod/etc/options_menu/themes/scripts/om_vars | 16 ++++----- .../settings/commands/c0000_themeperfolder | 2 +- .../commands/c0000_themeperfolder_child | 3 +- .../settings/commands/c0000_themerandomizer | 2 +- .../settings/commands/c0001_audiorandomizer | 2 +- .../commands/c0001_audiorandomizerall | 3 +- .../settings/commands/c0001_systemcleanup | 2 +- .../settings/commands/c0001_uninstaller | 2 +- .../themes/settings/commands/c9999_Back | 4 +-- 50 files changed, 148 insertions(+), 61 deletions(-) create mode 100644 mod/etc/options_menu/themes/lang/en-US.lang create mode 100644 mod/etc/options_menu/themes/lang/pt-PT.lang diff --git a/mod/etc/options_menu/commands/c0090_ThemeOptions b/mod/etc/options_menu/commands/c0090_ThemeOptions index 85888a1..419595c 100644 --- a/mod/etc/options_menu/commands/c0090_ThemeOptions +++ b/mod/etc/options_menu/commands/c0090_ThemeOptions @@ -1,4 +1,4 @@ -COMMAND_NAME=Themes +COMMAND_NAME=THEME_OPTIONS COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_themeselector diff --git a/mod/etc/options_menu/scripts/om_functions b/mod/etc/options_menu/scripts/om_functions index df1d332..ea2ecfa 100644 --- a/mod/etc/options_menu/scripts/om_functions +++ b/mod/etc/options_menu/scripts/om_functions @@ -63,9 +63,6 @@ optionsMenu="$mountpoint/etc/options_menu" omCommands="$optionsMenu/commands" omScripts="$optionsMenu/scripts" omImages="$optionsMenu/images" -# MESSAGES -noNetErr="echo Error while connecting to server, please check your internet connection and try again. " -noPkgErr="echo The package you resquested isn't available at the moment, please try again later." # FUNCTIONS gStorage="$(findGameStorage)" @@ -74,6 +71,21 @@ rename() { [ -f "$1" ] && mv "$1" "$2" } +translate() { + key="$1" + langCode="en-US" + [ -f "$optionsMenu/language.cfg" ] && langCode="$(cat "$optionsMenu/language.cfg")" + [ -z "$langCode" ] && langCode="en-US" + + value="" + for langDir in "$optionsMenu/lang" "$optionsMenu"/*/lang; do + [ -z "$value" ] && [ -f "$langDir/$langCode.lang" ] && value="$(grep "^${key}=" "$langDir/$langCode.lang" 2>/dev/null | tail -1 | cut -d= -f2-)" + [ -z "$value" ] && [ -f "$langDir/en-US.lang" ] && value="$(grep "^${key}=" "$langDir/en-US.lang" 2>/dev/null | tail -1 | cut -d= -f2-)" + done + [ -z "$value" ] && value="$key" + echo "$value" +} + remove() { for f in $@; do [ -f "$f" ] || [ -d "$f" ] && rm -rf "$f" @@ -112,9 +124,9 @@ disable_cmds() { netChecker() { if ! nc -z 8.8.8.8 53 >/dev/null 2>&1; then - $noPkgErr + translate NO_PKG_ERR else - $noNetErr + translate NO_NET_ERR fi } diff --git a/mod/etc/options_menu/themes/commands/_download b/mod/etc/options_menu/themes/commands/_download index eb919c0..e7a791d 100644 --- a/mod/etc/options_menu/themes/commands/_download +++ b/mod/etc/options_menu/themes/commands/_download @@ -1,4 +1,4 @@ -COMMAND_NAME=Download Themes +COMMAND_NAME=DOWNLOAD_THEMES COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_getThemes diff --git a/mod/etc/options_menu/themes/commands/_remove b/mod/etc/options_menu/themes/commands/_remove index 0acced5..f24f931 100644 --- a/mod/etc/options_menu/themes/commands/_remove +++ b/mod/etc/options_menu/themes/commands/_remove @@ -1,4 +1,4 @@ -COMMAND_NAME=Clear List +COMMAND_NAME=CLEAR_LIST COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_clearList & diff --git a/mod/etc/options_menu/themes/commands/c0000_generatetheme b/mod/etc/options_menu/themes/commands/c0000_generatetheme index 873786a..8f891a9 100644 --- a/mod/etc/options_menu/themes/commands/c0000_generatetheme +++ b/mod/etc/options_menu/themes/commands/c0000_generatetheme @@ -1,4 +1,4 @@ -COMMAND_NAME=DIY Theme +COMMAND_NAME=DIY_THEME COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_themeCreator diff --git a/mod/etc/options_menu/themes/commands/c0000_options b/mod/etc/options_menu/themes/commands/c0000_options index c704835..bb80720 100644 --- a/mod/etc/options_menu/themes/commands/c0000_options +++ b/mod/etc/options_menu/themes/commands/c0000_options @@ -1,4 +1,4 @@ -COMMAND_NAME=Settings +COMMAND_NAME=THEME_SETTINGS COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_settings & diff --git a/mod/etc/options_menu/themes/commands/c0000_refresh b/mod/etc/options_menu/themes/commands/c0000_refresh index e9e960a..b5d1ea1 100644 --- a/mod/etc/options_menu/themes/commands/c0000_refresh +++ b/mod/etc/options_menu/themes/commands/c0000_refresh @@ -1,4 +1,4 @@ -COMMAND_NAME=Refresh +COMMAND_NAME=REFRESH COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh /etc/options_menu/scripts/om_themeselector reload diff --git a/mod/etc/options_menu/themes/commands/c9999_Back b/mod/etc/options_menu/themes/commands/c9999_Back index c216cad..1e68533 100644 --- a/mod/etc/options_menu/themes/commands/c9999_Back +++ b/mod/etc/options_menu/themes/commands/c9999_Back @@ -1,4 +1,4 @@ -COMMAND_NAME=Back +COMMAND_NAME=BACK COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && %options_path%/options & diff --git a/mod/etc/options_menu/themes/commands/c9999_Exit b/mod/etc/options_menu/themes/commands/c9999_Exit index 0926616..a4a6ead 100644 --- a/mod/etc/options_menu/themes/commands/c9999_Exit +++ b/mod/etc/options_menu/themes/commands/c9999_Exit @@ -1,4 +1,4 @@ -COMMAND_NAME=Exit +COMMAND_NAME=EXIT COMMAND_TYPE=EXTERNAL RESTART_UI=TRUE COMMAND_STR=echo "Closing Options Menu" diff --git a/mod/etc/options_menu/themes/creator/commands/_apply b/mod/etc/options_menu/themes/creator/commands/_apply index 7ac7fbb..3af87a7 100644 --- a/mod/etc/options_menu/themes/creator/commands/_apply +++ b/mod/etc/options_menu/themes/creator/commands/_apply @@ -1,4 +1,4 @@ -COMMAND_NAME=Save custom theme +COMMAND_NAME=SAVE_CUSTOM_THEME COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_applytheme & diff --git a/mod/etc/options_menu/themes/creator/commands/_preview b/mod/etc/options_menu/themes/creator/commands/_preview index 492fd37..50cb17c 100644 --- a/mod/etc/options_menu/themes/creator/commands/_preview +++ b/mod/etc/options_menu/themes/creator/commands/_preview @@ -1,6 +1,7 @@ -COMMAND_NAME= Preview custom theme +COMMAND_NAME=PREVIEW_CUSTOM_THEME COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE +CHILD=TRUE COMMAND_STR=sh %script_dir%/om_preview & PREVIEW_IMAGE=/etc/options_menu/images/reboot.png PREVIEW_IMAGE_X=920 diff --git a/mod/etc/options_menu/themes/creator/commands/_remove b/mod/etc/options_menu/themes/creator/commands/_remove index 3cb1f1d..c2278e1 100644 --- a/mod/etc/options_menu/themes/creator/commands/_remove +++ b/mod/etc/options_menu/themes/creator/commands/_remove @@ -1,4 +1,4 @@ -COMMAND_NAME=Clean custom theme +COMMAND_NAME=CLEAN_CUSTOM_THEME COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_removediy & diff --git a/mod/etc/options_menu/themes/creator/commands/c0000_refresh b/mod/etc/options_menu/themes/creator/commands/c0000_refresh index bc70421..55da4e6 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0000_refresh +++ b/mod/etc/options_menu/themes/creator/commands/c0000_refresh @@ -1,4 +1,4 @@ -COMMAND_NAME=Refresh Assets +COMMAND_NAME=REFRESH_ASSETS COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_refresh diff --git a/mod/etc/options_menu/themes/creator/commands/c0001_packed b/mod/etc/options_menu/themes/creator/commands/c0001_packed index 936de80..422ebf9 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0001_packed +++ b/mod/etc/options_menu/themes/creator/commands/c0001_packed @@ -1,4 +1,4 @@ -COMMAND_NAME=Base UI +COMMAND_NAME=BASE_UI COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset packed & diff --git a/mod/etc/options_menu/themes/creator/commands/c0001_packed_nes b/mod/etc/options_menu/themes/creator/commands/c0001_packed_nes index f694c52..4973ce5 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0001_packed_nes +++ b/mod/etc/options_menu/themes/creator/commands/c0001_packed_nes @@ -1,4 +1,4 @@ -COMMAND_NAME=Base UI +COMMAND_NAME=BASE_UI COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset packed_nes & diff --git a/mod/etc/options_menu/themes/creator/commands/c0001_wall b/mod/etc/options_menu/themes/creator/commands/c0001_wall index 7cfe278..424984b 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0001_wall +++ b/mod/etc/options_menu/themes/creator/commands/c0001_wall @@ -1,4 +1,4 @@ -COMMAND_NAME=Background +COMMAND_NAME=BACKGROUND COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset wall & diff --git a/mod/etc/options_menu/themes/creator/commands/c0002_mario b/mod/etc/options_menu/themes/creator/commands/c0002_mario index 610005c..4f74b67 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0002_mario +++ b/mod/etc/options_menu/themes/creator/commands/c0002_mario @@ -1,4 +1,4 @@ -COMMAND_NAME=Demo: Character #1 - Mario +COMMAND_NAME=DEMO_CHARACTER_1_MARIO COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset mario & diff --git a/mod/etc/options_menu/themes/creator/commands/c0002_sbm b/mod/etc/options_menu/themes/creator/commands/c0002_sbm index b49c1e2..ca60e7e 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0002_sbm +++ b/mod/etc/options_menu/themes/creator/commands/c0002_sbm @@ -1,4 +1,4 @@ -COMMAND_NAME=Demo: Characters +COMMAND_NAME=DEMO_CHARACTERS COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset sbm & diff --git a/mod/etc/options_menu/themes/creator/commands/c0003_luigi b/mod/etc/options_menu/themes/creator/commands/c0003_luigi index 2b8f0ec..4b0ccf1 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0003_luigi +++ b/mod/etc/options_menu/themes/creator/commands/c0003_luigi @@ -1,4 +1,4 @@ -COMMAND_NAME=Demo: Character #2 - Luigi +COMMAND_NAME=DEMO_CHARACTER_2_LUIGI COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset luigi & diff --git a/mod/etc/options_menu/themes/creator/commands/c0004_wall_near b/mod/etc/options_menu/themes/creator/commands/c0004_wall_near index 2a5e057..cb4ede4 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0004_wall_near +++ b/mod/etc/options_menu/themes/creator/commands/c0004_wall_near @@ -1,4 +1,4 @@ -COMMAND_NAME=Demo: Background Near +COMMAND_NAME=DEMO_BACKGROUND_NEAR COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset wall_near & diff --git a/mod/etc/options_menu/themes/creator/commands/c0005_wall_far b/mod/etc/options_menu/themes/creator/commands/c0005_wall_far index 38185f5..1f478d9 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0005_wall_far +++ b/mod/etc/options_menu/themes/creator/commands/c0005_wall_far @@ -1,4 +1,4 @@ -COMMAND_NAME=Demo: Background Far +COMMAND_NAME=DEMO_BACKGROUND_FAR COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset wall_far & diff --git a/mod/etc/options_menu/themes/creator/commands/c0006_colors b/mod/etc/options_menu/themes/creator/commands/c0006_colors index bf7adbd..3c0e8bf 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0006_colors +++ b/mod/etc/options_menu/themes/creator/commands/c0006_colors @@ -1,4 +1,4 @@ -COMMAND_NAME=Colors +COMMAND_NAME=COLORS COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset colors & diff --git a/mod/etc/options_menu/themes/creator/commands/c0006_colors_nes b/mod/etc/options_menu/themes/creator/commands/c0006_colors_nes index f713ffd..42bbdb2 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0006_colors_nes +++ b/mod/etc/options_menu/themes/creator/commands/c0006_colors_nes @@ -1,4 +1,4 @@ -COMMAND_NAME=Background Color +COMMAND_NAME=BACKGROUND_COLOR COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset colors_nes & diff --git a/mod/etc/options_menu/themes/creator/commands/c0006_pole b/mod/etc/options_menu/themes/creator/commands/c0006_pole index f129cac..a20ddc6 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0006_pole +++ b/mod/etc/options_menu/themes/creator/commands/c0006_pole @@ -1,4 +1,4 @@ -COMMAND_NAME=Demo: Pole +COMMAND_NAME=DEMO_POLE COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset pole & diff --git a/mod/etc/options_menu/themes/creator/commands/c9999_Back b/mod/etc/options_menu/themes/creator/commands/c9999_Back index 15ce5c5..a224a60 100644 --- a/mod/etc/options_menu/themes/creator/commands/c9999_Back +++ b/mod/etc/options_menu/themes/creator/commands/c9999_Back @@ -1,7 +1,7 @@ -COMMAND_NAME=Back +COMMAND_NAME=BACK COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE -COMMAND_STR=usleep 50000 && %options_path%/options --commandPath %options_path%/themes/commands/ --scriptPath %options_path%/themes/scripts --title "Theme Selector" & +COMMAND_STR=usleep 50000 && %options_path%/options --commandPath %options_path%/themes/commands/ --scriptPath %options_path%/themes/scripts --title "THEME_SELECTOR" & PREVIEW_IMAGE=/etc/options_menu/images/back.png PREVIEW_IMAGE_X=920 PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/colors/commands/c9999_Back index b891523..d96b9c2 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors/commands/c9999_Back +++ b/mod/etc/options_menu/themes/creator/sprites/colors/commands/c9999_Back @@ -1,4 +1,4 @@ -COMMAND_NAME=Back +COMMAND_NAME=BACK COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_return & diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c9999_Back index b891523..d96b9c2 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c9999_Back +++ b/mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c9999_Back @@ -1,4 +1,4 @@ -COMMAND_NAME=Back +COMMAND_NAME=BACK COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_return & diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/luigi/commands/c9999_Back index b891523..d96b9c2 100644 --- a/mod/etc/options_menu/themes/creator/sprites/luigi/commands/c9999_Back +++ b/mod/etc/options_menu/themes/creator/sprites/luigi/commands/c9999_Back @@ -1,4 +1,4 @@ -COMMAND_NAME=Back +COMMAND_NAME=BACK COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_return & diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/mario/commands/c9999_Back index b891523..d96b9c2 100644 --- a/mod/etc/options_menu/themes/creator/sprites/mario/commands/c9999_Back +++ b/mod/etc/options_menu/themes/creator/sprites/mario/commands/c9999_Back @@ -1,4 +1,4 @@ -COMMAND_NAME=Back +COMMAND_NAME=BACK COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_return & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/packed/commands/c9999_Back index b891523..d96b9c2 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed/commands/c9999_Back +++ b/mod/etc/options_menu/themes/creator/sprites/packed/commands/c9999_Back @@ -1,4 +1,4 @@ -COMMAND_NAME=Back +COMMAND_NAME=BACK COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_return & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c9999_Back index b891523..d96b9c2 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c9999_Back +++ b/mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c9999_Back @@ -1,4 +1,4 @@ -COMMAND_NAME=Back +COMMAND_NAME=BACK COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_return & diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/pole/commands/c9999_Back index b891523..d96b9c2 100644 --- a/mod/etc/options_menu/themes/creator/sprites/pole/commands/c9999_Back +++ b/mod/etc/options_menu/themes/creator/sprites/pole/commands/c9999_Back @@ -1,4 +1,4 @@ -COMMAND_NAME=Back +COMMAND_NAME=BACK COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_return & diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c9999_Back index b891523..d96b9c2 100644 --- a/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c9999_Back +++ b/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c9999_Back @@ -1,4 +1,4 @@ -COMMAND_NAME=Back +COMMAND_NAME=BACK COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_return & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/wall/commands/c9999_Back index b891523..d96b9c2 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall/commands/c9999_Back +++ b/mod/etc/options_menu/themes/creator/sprites/wall/commands/c9999_Back @@ -1,4 +1,4 @@ -COMMAND_NAME=Back +COMMAND_NAME=BACK COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_return & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c9999_Back index b891523..d96b9c2 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c9999_Back +++ b/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c9999_Back @@ -1,4 +1,4 @@ -COMMAND_NAME=Back +COMMAND_NAME=BACK COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_return & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c9999_Back index b891523..d96b9c2 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c9999_Back +++ b/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c9999_Back @@ -1,4 +1,4 @@ -COMMAND_NAME=Back +COMMAND_NAME=BACK COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_return & diff --git a/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes b/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes index b42f831..5e0da78 100644 --- a/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes +++ b/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes @@ -1,4 +1,4 @@ -COMMAND_NAME=Update Theme List +COMMAND_NAME=UPDATE_THEME_LIST COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_refreshThemes & diff --git a/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes b/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes index c2d23a4..c475da7 100644 --- a/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes +++ b/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes @@ -1,4 +1,4 @@ -COMMAND_NAME=Clear List +COMMAND_NAME=CLEAR_LIST COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_clearDownloads & diff --git a/mod/etc/options_menu/themes/downloads/commands/c9999_Back b/mod/etc/options_menu/themes/downloads/commands/c9999_Back index b891523..d96b9c2 100644 --- a/mod/etc/options_menu/themes/downloads/commands/c9999_Back +++ b/mod/etc/options_menu/themes/downloads/commands/c9999_Back @@ -1,4 +1,4 @@ -COMMAND_NAME=Back +COMMAND_NAME=BACK COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_return & diff --git a/mod/etc/options_menu/themes/lang/en-US.lang b/mod/etc/options_menu/themes/lang/en-US.lang new file mode 100644 index 0000000..58c2a62 --- /dev/null +++ b/mod/etc/options_menu/themes/lang/en-US.lang @@ -0,0 +1,36 @@ +THEME_OPTIONS=Themes +THEME_SELECTOR=Theme Selector +DIY_THEME=DIY Theme +THEME_SETTINGS=Settings +REFRESH=Refresh +REFRESH_ASSETS=Refresh Assets +BASE_UI=Base UI +BACKGROUND=Background +DEMO_CHARACTER_1_MARIO=Demo: Character #1 - Mario +DEMO_CHARACTERS=Demo: Characters +DEMO_CHARACTER_2_LUIGI=Demo: Character #2 - Luigi +DEMO_BACKGROUND_NEAR=Demo: Background Near +DEMO_BACKGROUND_FAR=Demo: Background Far +COLORS=Colors +BACKGROUND_COLOR=Background Color +DEMO_POLE=Demo: Pole +SAVE_CUSTOM_THEME=Save custom theme +CLEAN_CUSTOM_THEME=Clean custom theme +PREVIEW_CUSTOM_THEME=Preview custom theme +UPDATE_THEME_LIST=Update Theme List +CLEAR_LIST=Clear List +DOWNLOAD_THEMES=Download Themes +THEME_PER_FOLDER=Theme Per Folder +ALLOW_DIFFERENT_THEME_SUBFOLDER=Allow Different Theme to SubFolder +THEME_RANDOMIZER=Theme Randomizer +AUDIO_RANDOMIZER_HOME=Audio Randomizer: Home +AUDIO_RANDOMIZER_ALL_FOLDERS=All Folders (Only Themes Without ANY Audio Files) +CLEAN_UP=Clean Up +RESET_SETTINGS=Reset Settings +ASSET_APPLIED_SUCCESS=Asset applied successfully! +SAVE_PREVIEW_HINT_LINE1=Don't forget to select 'Save custom theme' when you finish selecting your assets, or simply select +SAVE_PREVIEW_HINT_LINE2='Preview custom theme', to have a preview before saving your theme. +CATEGORY_UI_MOD=Category: User Interface Mod +CHECKING_THEME_FILES=Checking selected theme files. +DOWNLOADING=Downloading... +INSTALLATION_COMPLETE=Installation complete. diff --git a/mod/etc/options_menu/themes/lang/pt-PT.lang b/mod/etc/options_menu/themes/lang/pt-PT.lang new file mode 100644 index 0000000..b1702c0 --- /dev/null +++ b/mod/etc/options_menu/themes/lang/pt-PT.lang @@ -0,0 +1,36 @@ +THEME_OPTIONS=Temas +THEME_SELECTOR=Seletor de Temas +DIY_THEME=Tema Personalizado +THEME_SETTINGS=Definições +REFRESH=Atualizar +REFRESH_ASSETS=Atualizar Recursos +BASE_UI=Interface Base +BACKGROUND=Fundo +DEMO_CHARACTER_1_MARIO=Demonstração: Personagem #1 - Mario +DEMO_CHARACTERS=Demonstração: Personagens +DEMO_CHARACTER_2_LUIGI=Demonstração: Personagem #2 - Luigi +DEMO_BACKGROUND_NEAR=Demonstração: Fundo Próximo +DEMO_BACKGROUND_FAR=Demonstração: Fundo Distante +COLORS=Cores +BACKGROUND_COLOR=Cor de Fundo +DEMO_POLE=Demonstração: Poste +SAVE_CUSTOM_THEME=Guardar tema personalizado +CLEAN_CUSTOM_THEME=Limpar tema personalizado +PREVIEW_CUSTOM_THEME=Pré-visualizar tema personalizado +UPDATE_THEME_LIST=Atualizar Lista de Temas +CLEAR_LIST=Limpar Lista +DOWNLOAD_THEMES=Descarregar Temas +THEME_PER_FOLDER=Tema Por Pasta +ALLOW_DIFFERENT_THEME_SUBFOLDER=Permitir Tema Diferente na Subpasta +THEME_RANDOMIZER=Tema Aleatório +AUDIO_RANDOMIZER_HOME=Áudio Aleatório: Home +AUDIO_RANDOMIZER_ALL_FOLDERS=Todas as Pastas (Só Temas Sem Ficheiros de Áudio) +CLEAN_UP=Limpeza +RESET_SETTINGS=Repor Definições +ASSET_APPLIED_SUCCESS=Recurso aplicado com sucesso! +SAVE_PREVIEW_HINT_LINE1=Não te esqueças de selecionar 'Guardar tema personalizado' quando terminares, ou seleciona +SAVE_PREVIEW_HINT_LINE2='Pré-visualizar tema personalizado', para ver uma pré-visualização antes de guardar. +CATEGORY_UI_MOD=Categoria: Mod de Interface +CHECKING_THEME_FILES=A verificar os ficheiros do tema selecionado. +DOWNLOADING=A descarregar... +INSTALLATION_COMPLETE=Instalação concluída. diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index f56d4b3..654d1ac 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -21,7 +21,7 @@ source $mountpoint/etc/options_menu/scripts/om_functions script_init modtitle="themes" -omTitle="Theme Selector" +omTitle="THEME_SELECTOR" b_file="$mountpoint/etc/preinit.d/b8024_themeselector" p_file="$mountpoint/etc/preinit.d/p8024_themeselector" fontfix_file="$mountpoint/etc/preinit.d/b8025_fontfix" @@ -418,9 +418,9 @@ loadThemeList() { # verify themeGenerator function themeGenerator() { echo "Packaging: DefKorns" - echo "Category: User Interface Mod " + translate CATEGORY_UI_MOD echo "" - echo "Checking selected theme files." + translate CHECKING_THEME_FILES echo "" theme_name="${1}" full_title="$(grep -F 'NAME=' "${omModSubCmds}/c0010_${theme_name}" | sed -e 's/.*=\(.*\)\[.*/\1/')" @@ -431,7 +431,7 @@ themeGenerator() { [ "${theme_name}" = "all" ] && theme_pkg="${downloadURL}/${sftype}-all" if wget -q --spider "${theme_pkg}" > /dev/null 2>&1; then - echo "Downloading..." + translate DOWNLOADING wget "${theme_pkg}" if [ "${theme_name}" = "all" ]; then while IFS= read -r t; do @@ -448,7 +448,7 @@ themeGenerator() { echo "" size="$(du -sh "${full_path}" | cut -f1)" echo "NAME=${full_title}[${size}b]" > "${full_path}/info" - echo "Installation complete." + translate INSTALLATION_COMPLETE else netChecker fi @@ -608,11 +608,11 @@ diySpriteSelector() { echo "" echo "" echo "" - echo "Asset applied successfully! " + translate ASSET_APPLIED_SUCCESS echo "" echo "" - echo "Don't forget to select 'Save custom theme' when you finish selecting your assets, or simply select" - echo "'Preview custom theme', to have a preview before saving your theme." + translate SAVE_PREVIEW_HINT_LINE1 + translate SAVE_PREVIEW_HINT_LINE2 echo "" echo "" } diff --git a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder index 8783149..65155c4 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder +++ b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder @@ -1,4 +1,4 @@ -COMMAND_NAME=Theme Per Folder +COMMAND_NAME=THEME_PER_FOLDER COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_theme tpf diff --git a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child index 6423873..3baac93 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child +++ b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child @@ -1,6 +1,7 @@ -COMMAND_NAME= Allow Different Theme to SubFolder +COMMAND_NAME=ALLOW_DIFFERENT_THEME_SUBFOLDER COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE +CHILD=TRUE COMMAND_STR=sh %script_dir%/om_theme child PREVIEW_IMAGE=/etc/options_menu/images/randtheme.png PREVIEW_IMAGE_X=920 diff --git a/mod/etc/options_menu/themes/settings/commands/c0000_themerandomizer b/mod/etc/options_menu/themes/settings/commands/c0000_themerandomizer index 52b5916..1cf04ba 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0000_themerandomizer +++ b/mod/etc/options_menu/themes/settings/commands/c0000_themerandomizer @@ -1,4 +1,4 @@ -COMMAND_NAME=Theme Randomizer +COMMAND_NAME=THEME_RANDOMIZER COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_theme & diff --git a/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizer b/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizer index 81c1119..f1dcfee 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizer +++ b/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizer @@ -1,4 +1,4 @@ -COMMAND_NAME=Audio Randomizer: Home +COMMAND_NAME=AUDIO_RANDOMIZER_HOME COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_audio & diff --git a/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizerall b/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizerall index 2ec6eb0..dcef1db 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizerall +++ b/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizerall @@ -1,6 +1,7 @@ -COMMAND_NAME= All Folders (Only Themes Without ANY Audio Files) +COMMAND_NAME=AUDIO_RANDOMIZER_ALL_FOLDERS COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE +CHILD=TRUE COMMAND_STR=usleep 50000 && sh %script_dir%/om_audio -amh & PREVIEW_IMAGE=/etc/options_menu/images/audioall.png PREVIEW_IMAGE_X=920 diff --git a/mod/etc/options_menu/themes/settings/commands/c0001_systemcleanup b/mod/etc/options_menu/themes/settings/commands/c0001_systemcleanup index aeff326..fc98774 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0001_systemcleanup +++ b/mod/etc/options_menu/themes/settings/commands/c0001_systemcleanup @@ -1,4 +1,4 @@ -COMMAND_NAME=Clean Up +COMMAND_NAME=CLEAN_UP COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_systemcleanup & diff --git a/mod/etc/options_menu/themes/settings/commands/c0001_uninstaller b/mod/etc/options_menu/themes/settings/commands/c0001_uninstaller index 97c18a5..4dc3de0 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0001_uninstaller +++ b/mod/etc/options_menu/themes/settings/commands/c0001_uninstaller @@ -1,4 +1,4 @@ -COMMAND_NAME=Reset Settings +COMMAND_NAME=RESET_SETTINGS COMMAND_TYPE=INTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_reset diff --git a/mod/etc/options_menu/themes/settings/commands/c9999_Back b/mod/etc/options_menu/themes/settings/commands/c9999_Back index 15ce5c5..a224a60 100644 --- a/mod/etc/options_menu/themes/settings/commands/c9999_Back +++ b/mod/etc/options_menu/themes/settings/commands/c9999_Back @@ -1,7 +1,7 @@ -COMMAND_NAME=Back +COMMAND_NAME=BACK COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE -COMMAND_STR=usleep 50000 && %options_path%/options --commandPath %options_path%/themes/commands/ --scriptPath %options_path%/themes/scripts --title "Theme Selector" & +COMMAND_STR=usleep 50000 && %options_path%/options --commandPath %options_path%/themes/commands/ --scriptPath %options_path%/themes/scripts --title "THEME_SELECTOR" & PREVIEW_IMAGE=/etc/options_menu/images/back.png PREVIEW_IMAGE_X=920 PREVIEW_IMAGE_Y=262 \ No newline at end of file From ce03637644a0064ec472481078e481c9cb2e1280 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 11 Sep 2026 22:27:16 +0000 Subject: [PATCH 022/210] Add submodule configuration for toolchain and pngpack --- .gitmodules | 6 ++++++ pngpack | 1 + toolchain | 1 + 3 files changed, 8 insertions(+) create mode 100644 .gitmodules create mode 160000 pngpack create mode 160000 toolchain diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..3e7615b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "toolchain"] + path = toolchain + url = https://github.com/DefKorns/hakchi-toolchain.git +[submodule "pngpack"] + path = pngpack + url = https://github.com/DefKorns/pngpack.git diff --git a/pngpack b/pngpack new file mode 160000 index 0000000..20e8e9e --- /dev/null +++ b/pngpack @@ -0,0 +1 @@ +Subproject commit 20e8e9e5213c2c2f4334f944b81fa90b2d30523f diff --git a/toolchain b/toolchain new file mode 160000 index 0000000..4fa4f1f --- /dev/null +++ b/toolchain @@ -0,0 +1 @@ +Subproject commit 4fa4f1f10d8dc4fcebf3365884af58a4e29a9cfd From 5c0cc22cac39b1c9d7d1e48ba499e3980806a73b Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 11 Sep 2026 22:28:05 +0000 Subject: [PATCH 023/210] Update submodule commit reference for toolchain --- toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolchain b/toolchain index 4fa4f1f..216f5ab 160000 --- a/toolchain +++ b/toolchain @@ -1 +1 @@ -Subproject commit 4fa4f1f10d8dc4fcebf3365884af58a4e29a9cfd +Subproject commit 216f5abcf008e285c3e833c223538518fc9da1ff From 9f35599cbbc4aa22a65b1d370bd7265a7d3a086d Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sat, 12 Sep 2026 00:38:16 +0000 Subject: [PATCH 024/210] Stop shipping a duplicate om_functions, fix stale c9999_Exit/separator collision --- .gitignore | 1 + ...{c0090_ThemeOptions => c0075_ThemeOptions} | 0 mod/etc/options_menu/commands/c9998_separator | 0 mod/etc/options_menu/commands/c9999_Exit | 7 - mod/etc/options_menu/scripts/om_functions | 158 ------------------ .../themes/scripts/om_theme_functions | 31 ++++ mod/etc/options_menu/themes/scripts/om_vars | 3 + mod/uninstall | 7 +- 8 files changed, 36 insertions(+), 171 deletions(-) rename mod/etc/options_menu/commands/{c0090_ThemeOptions => c0075_ThemeOptions} (100%) delete mode 100644 mod/etc/options_menu/commands/c9998_separator delete mode 100644 mod/etc/options_menu/commands/c9999_Exit delete mode 100644 mod/etc/options_menu/scripts/om_functions create mode 100644 mod/etc/options_menu/themes/scripts/om_theme_functions diff --git a/.gitignore b/.gitignore index 14162fb..e684935 100644 --- a/.gitignore +++ b/.gitignore @@ -500,6 +500,7 @@ default/ temp/ mod/etc/options_menu/themes/scripts/b8024_themeselector mod-/ +mod-backup/ out--/ mod/etc/options_menu/themes-old/ modules/ diff --git a/mod/etc/options_menu/commands/c0090_ThemeOptions b/mod/etc/options_menu/commands/c0075_ThemeOptions similarity index 100% rename from mod/etc/options_menu/commands/c0090_ThemeOptions rename to mod/etc/options_menu/commands/c0075_ThemeOptions diff --git a/mod/etc/options_menu/commands/c9998_separator b/mod/etc/options_menu/commands/c9998_separator deleted file mode 100644 index e69de29..0000000 diff --git a/mod/etc/options_menu/commands/c9999_Exit b/mod/etc/options_menu/commands/c9999_Exit deleted file mode 100644 index 0926616..0000000 --- a/mod/etc/options_menu/commands/c9999_Exit +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=Exit -COMMAND_TYPE=EXTERNAL -RESTART_UI=TRUE -COMMAND_STR=echo "Closing Options Menu" -PREVIEW_IMAGE=/etc/options_menu/images/exit.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/scripts/om_functions b/mod/etc/options_menu/scripts/om_functions deleted file mode 100644 index ea2ecfa..0000000 --- a/mod/etc/options_menu/scripts/om_functions +++ /dev/null @@ -1,158 +0,0 @@ -#!/bin/sh -# shellcheck disable=SC2154,SC1091,SC2034,SC2068,SC2039,SC3051,SC3046 -# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# shellcheck source=/dev/null -source /etc/preinit -script_init - -# PATHS -nand_path="$rootfs/usr/share" -share_path="$mountpoint/usr/share" -ui_path="$share_path/ui" -cloverui_path="$share_path/clover-ui/" -string_path="resources/strings" -root_bin="$rootfs/bin" -usb_path="$mountpoint/media/$modname" -font_dir="$usb_path/fonts" -state_path="$mountpoint$profilepath/$modname" -[ ! -d "$font_dir" ] && font_dir="$nand_path/fonts" - - -system="$sftype" -s_type="$system" -core="$sftype" -case "$sftype" in -hvcj) - system="shonen" - s_type="$system" - system_string="$ui_path/hvc/$string_path" - core="nes" - ;; -nes) - if [ "$sfregion" = "jpn" ]; then - system="hvc" - s_type="$system" - system_string="$cloverui_path/$string_path" - fi - ;; -snes) - system_string="$ui_path/$sftype-$sfregion/$string_path" - if [ "$sfregion" = "jpn" ]; then - system="shvc" - system_string="$ui_path/$system/$string_path" - fi - ;; -esac - -# OM PATHS -optionsMenu="$mountpoint/etc/options_menu" -omCommands="$optionsMenu/commands" -omScripts="$optionsMenu/scripts" -omImages="$optionsMenu/images" - -# FUNCTIONS -gStorage="$(findGameStorage)" - -rename() { - [ -f "$1" ] && mv "$1" "$2" -} - -translate() { - key="$1" - langCode="en-US" - [ -f "$optionsMenu/language.cfg" ] && langCode="$(cat "$optionsMenu/language.cfg")" - [ -z "$langCode" ] && langCode="en-US" - - value="" - for langDir in "$optionsMenu/lang" "$optionsMenu"/*/lang; do - [ -z "$value" ] && [ -f "$langDir/$langCode.lang" ] && value="$(grep "^${key}=" "$langDir/$langCode.lang" 2>/dev/null | tail -1 | cut -d= -f2-)" - [ -z "$value" ] && [ -f "$langDir/en-US.lang" ] && value="$(grep "^${key}=" "$langDir/en-US.lang" 2>/dev/null | tail -1 | cut -d= -f2-)" - done - [ -z "$value" ] && value="$key" - echo "$value" -} - -remove() { - for f in $@; do - [ -f "$f" ] || [ -d "$f" ] && rm -rf "$f" - done -} - -link() { - [ -f "$1" ] && ln -sf "$1" "$2" -} - -copier() { - [ -f "$1" ] && cp -r "$1" "$2" -} - -create() { - [ ! -f "$1" ] && touch "$1" -} - -enable_cmds() { - for arg in $@; do - in="disable$arg" - out="enable$arg" - # shellcheck disable=SC2086 - eval "rename "\$$in" "\$$out"" - done -} - -disable_cmds() { - for arg in $@; do - in="enable$arg" - out="disable$arg" - # shellcheck disable=SC2086 - eval "rename "\$$in" "\$$out"" - done -} - -netChecker() { - if ! nc -z 8.8.8.8 53 >/dev/null 2>&1; then - translate NO_PKG_ERR - else - translate NO_NET_ERR - fi -} - -toggleDownloads() { - nc -z 8.8.8.8 53 >/dev/null 2>&1 - # shellcheck disable=SC2181 - if [ $? -eq 0 ]; then - enable_cmds "Downloads" - else - disable_cmds "Downloads" - fi -} - -switchOff() { - for item in $@; do - [ -f "$item" ] && sed -r -i 's/ \[X\]//g' "$item" - done - -} - -switchOn() { - for item in $@; do - [ -f "$item" ] && sed -r -i "1 s|$| [X]|" "$item" - done -} - -overlayFS() { - [ -d "$1" ] && mount -t overlayfs -o lowerdir="$2",upperdir="$1" overlayfs "$2" -} diff --git a/mod/etc/options_menu/themes/scripts/om_theme_functions b/mod/etc/options_menu/themes/scripts/om_theme_functions new file mode 100644 index 0000000..af9b1f3 --- /dev/null +++ b/mod/etc/options_menu/themes/scripts/om_theme_functions @@ -0,0 +1,31 @@ +#!/bin/sh +# shellcheck disable=SC2034,SC2086,SC2154,SC3010,SC3046,SC3051 +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# theme-selector's own toggle-marker helpers - kept separate from the shared +# om_functions (owned by OptionsMenu) so this hmod never overwrites it. + +switchOff() { + for item in "$@"; do + [ -f "$item" ] && sed -r -i 's/ \[X\]//g' "$item" + done +} + +switchOn() { + for item in "$@"; do + [ -f "$item" ] && sed -r -i "1 s|$| [X]|" "$item" + done +} diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 654d1ac..cca8226 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -20,6 +20,9 @@ source $mountpoint/etc/options_menu/scripts/om_functions script_init +# shellcheck source=mod/etc/options_menu/themes/scripts/om_theme_functions +source $mountpoint/etc/options_menu/themes/scripts/om_theme_functions + modtitle="themes" omTitle="THEME_SELECTOR" b_file="$mountpoint/etc/preinit.d/b8024_themeselector" diff --git a/mod/uninstall b/mod/uninstall index bcc9ebf..de8730f 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -61,7 +61,7 @@ remove "$rootfs/usr/share/music_menu" chmod +x "$rootfs/bin/chmenu" remove "$rootfs/etc/preinit.d/b8024_themeselector" remove "$rootfs/etc/preinit.d/p8024_themeselector" -remove "$rootfs/etc/options_menu/commands/c0090_ThemeOptions" +remove "$rootfs/etc/options_menu/commands/c0075_ThemeOptions" remove "$rootfs/etc/options_menu/scripts/om_themeselector" remove "$rootfs/etc/options_menu/themes" remove "$rootfs/etc/options_menu/images/audioall.png" @@ -105,9 +105,4 @@ for FONT in s m l small medium large; do rm "${FONT_PATH}/${FONT}.fnt" done -echo "COMMAND_NAME=Exit -COMMAND_TYPE=EXTERNAL -RESTART_UI=TRUE -COMMAND_STR=echo \"Closing Options Menu\"" >"$rootfs/etc/options_menu/commands/c9999_Exit" - unset cfg_OM_ThemeSelector_enabled From 5228fec3ff09229e9beb06fa552588a9cae4ae0c Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sat, 12 Sep 2026 01:17:06 +0000 Subject: [PATCH 025/210] Add .gitattributes for consistent line endings and create om_themes_vars script --- .gitattributes | 5 ++++ mod/etc/options_menu/scripts/om_themeselector | 2 +- .../themes/scripts/om_themes_vars | 23 +++++++++++++++++++ mod/etc/options_menu/themes/scripts/om_vars | 5 +--- 4 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 .gitattributes create mode 100644 mod/etc/options_menu/themes/scripts/om_themes_vars diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..533e396 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# Force LF regardless of platform checkout settings (core.autocrlf, etc.) - +# GitHub Actions workflow YAML and shell here don't want a stray \r. +* text=auto eol=lf + +*.ttf binary diff --git a/mod/etc/options_menu/scripts/om_themeselector b/mod/etc/options_menu/scripts/om_themeselector index 0b61cdf..426aaa7 100644 --- a/mod/etc/options_menu/scripts/om_themeselector +++ b/mod/etc/options_menu/scripts/om_themeselector @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/scripts/om_themes_vars b/mod/etc/options_menu/themes/scripts/om_themes_vars new file mode 100644 index 0000000..74725ad --- /dev/null +++ b/mod/etc/options_menu/themes/scripts/om_themes_vars @@ -0,0 +1,23 @@ +#!/bin/sh +# shellcheck disable=SC2086,SC2154,SC3051 +# # shellcheck disable=SC2034,SC2086,SC2154,SC3010,SC3046,SC3051 +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# shellcheck source=/dev/null +source $mountpoint/etc/options_menu/scripts/om_functions +script_init + + diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index cca8226..3bffbac 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -16,13 +16,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/scripts/om_functions +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/scripts/om_functions script_init -# shellcheck source=mod/etc/options_menu/themes/scripts/om_theme_functions -source $mountpoint/etc/options_menu/themes/scripts/om_theme_functions - modtitle="themes" omTitle="THEME_SELECTOR" b_file="$mountpoint/etc/preinit.d/b8024_themeselector" From fd6fbc8474739722c028156667bf6c6a4fa3f7c4 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Mon, 14 Sep 2026 16:14:42 +0000 Subject: [PATCH 026/210] Replace vendored chmenu/fontfix backups with surgical sed removal on uninstall --- .gitignore | 1 + .../options_menu/commands/c0075_ThemeOptions | 6 +- .../images/preview_theme_options.png | Bin 0 -> 5425 bytes mod/etc/options_menu/images/themes.png | Bin 8883 -> 0 bytes .../options_menu/scripts/om_theme_selector | 20 +++++ .../options_menu/themes/scripts/om_chmenu_og | 28 ------- .../options_menu/themes/scripts/om_fontfix_og | 74 ------------------ .../themes/scripts/om_themes_vars | 50 +++++++++++- mod/uninstall | 4 +- 9 files changed, 74 insertions(+), 109 deletions(-) create mode 100644 mod/etc/options_menu/images/preview_theme_options.png delete mode 100644 mod/etc/options_menu/images/themes.png create mode 100644 mod/etc/options_menu/scripts/om_theme_selector delete mode 100644 mod/etc/options_menu/themes/scripts/om_chmenu_og delete mode 100644 mod/etc/options_menu/themes/scripts/om_fontfix_og diff --git a/.gitignore b/.gitignore index e684935..1a9bc8e 100644 --- a/.gitignore +++ b/.gitignore @@ -526,3 +526,4 @@ out---/ preinit b0010_functions b0050_boot +tools/gen_theme_options_preview.py diff --git a/mod/etc/options_menu/commands/c0075_ThemeOptions b/mod/etc/options_menu/commands/c0075_ThemeOptions index 419595c..d09eb9c 100644 --- a/mod/etc/options_menu/commands/c0075_ThemeOptions +++ b/mod/etc/options_menu/commands/c0075_ThemeOptions @@ -1,7 +1,5 @@ COMMAND_NAME=THEME_OPTIONS COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_themeselector -PREVIEW_IMAGE=/etc/options_menu/images/themes.png -PREVIEW_IMAGE_X=900 -PREVIEW_IMAGE_Y=262 \ No newline at end of file +COMMAND_STR=sh %script_dir%/om_theme_selector +PREVIEW_IMAGE=/etc/options_menu/images/preview_theme_options.png \ No newline at end of file diff --git a/mod/etc/options_menu/images/preview_theme_options.png b/mod/etc/options_menu/images/preview_theme_options.png new file mode 100644 index 0000000000000000000000000000000000000000..750c38c37564593d2bab4a8b2c4d4e87e2ae637f GIT binary patch literal 5425 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 literal 0 HcmV?d00001 diff --git a/mod/etc/options_menu/images/themes.png b/mod/etc/options_menu/images/themes.png deleted file mode 100644 index 9e2078696ef8b22614eb629b45a2e94149d6540f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8883 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. +# +# shellcheck source=/dev/null +source $mountpoint/etc/options_menu/themes/scripts/om_vars +script_init diff --git a/mod/etc/options_menu/themes/scripts/om_chmenu_og b/mod/etc/options_menu/themes/scripts/om_chmenu_og deleted file mode 100644 index bcdfc22..0000000 --- a/mod/etc/options_menu/themes/scripts/om_chmenu_og +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh -source /etc/preinit -script_init - -state_path="/var/saves/$modname" -state_file="$state_path/menu" - -[ -z "$1" ] && exit 1 -[ -f "$state_file" ] && prev_code="$(cat "$state_file")" || prev_code="000" -[ "$1" == "$prev_code" ] && exit 1 - -echo "Switching to menu $1" -gameStorage="$(findGameStorage)" -targetpath="$gameStorage/$1" -if ! containsGames "$targetpath"; then - [ "$1" == "000" ] || exit 1 - targetpath="$gameStorage" - containsGames "$targetpath" || exit 1 -fi - -mkdir -p "$state_path" -echo "$1" > "$state_file" - -echo "New directory: $targetpath" -uistop -timeout="" waittimeout 1500 '! lsof -n | grep -qF "/dev/fb0"' "framebuffer" -overmount_games "$1" -touch "/var/startmcp.flag" diff --git a/mod/etc/options_menu/themes/scripts/om_fontfix_og b/mod/etc/options_menu/themes/scripts/om_fontfix_og deleted file mode 100644 index 08aa41e..0000000 --- a/mod/etc/options_menu/themes/scripts/om_fontfix_og +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/sh - -fontFix_bind(){ - if [ -f "$1" ] && [ -f "$2" ]; then - mount_bind "$1" "$2" && return 0 - fi - return 1 -} - -fontFix_getPath0(){ - [ -f "$1.fnt" ] && echo "$1.fnt" && return 0 - [ -f "$1.font" ] && echo "$1.font" && return 0 - return 1 -} - -fontFix_getPath(){ - fontFix_getPath0 "$mountpoint/media/$modname/fonts/$1" && return 0 - fontFix_getPath0 "$installpath/fonts/$1" ] && return 0 - fontFix_getPath0 "$rootfs/usr/share/fonts/$1" ] && return 0 - fontFix_getPath0 "$squashfs$gamepath/$1" ] && return 0 - return 1 -} - -fontFix0(){ - local fontPath="$(fontFix_getPath "$4")" - fontFix_bind "$fontPath" "$mountpoint/usr/share/$1/resources/fonts/$2$3.fnt" || \ - fontFix_bind "$fontPath" "$mountpoint/usr/share/$1/resources/fonts/$2$3.font" -} - -fontFix(){ - local ui0="ui/$sftype-$sfregion" - local ui1="" - case "$sftype-$sfregion" in - nes-usa) - ui0="clover-ui" - ui1="nes/" - ;; - nes-jpn) - ui0="clover-ui" - ui1="hvc/" - ;; - snes-jpn) - ui0="ui/shvc" - ;; - hvcj-jpn) - ui0="ui/hvc" - ;; - esac - - repair_fonts "$rootfs$gamepath" - fontFix0 "$ui0" "$ui1" "s" "small" - fontFix0 "$ui0" "$ui1" "m" "medium" - fontFix0 "$ui0" "$ui1" "l" "large" - local font="" - for font in title copyright s m l; do - fontFix0 "$ui0" "$ui1" "$font" "$font" - done - fontFix0 "$ui0" "" "debug" "debug" -} - -repair_fonts(){ - [ "$sftype" = "nes" ] || return 0 - - local titleFontPath="$(fontFix_getPath "title")" - local copyrightFontPath="$(fontFix_getPath "copyright")" - - titleFontPath="${titleFontPath#$mountpoint}" - copyrightFontPath="${copyrightFontPath#$mountpoint}" - - mkdir -p "$1" || return 1 - - [ -f "$mountpoint$titleFontPath" ] && (ln -nsf "$titleFontPath" "$1/title.fnt" || copy "$mountpoint$titleFontPath" "$1/") - [ -f "$mountpoint$copyrightFontPath" ] && (ln -nsf "$copyrightFontPath" "$1/copyright.fnt" || copy "$mountpoint$copyrightFontPath" "$1/") -} diff --git a/mod/etc/options_menu/themes/scripts/om_themes_vars b/mod/etc/options_menu/themes/scripts/om_themes_vars index 74725ad..78f5713 100644 --- a/mod/etc/options_menu/themes/scripts/om_themes_vars +++ b/mod/etc/options_menu/themes/scripts/om_themes_vars @@ -1,5 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2154,SC3051 +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # # shellcheck disable=SC2034,SC2086,SC2154,SC3010,SC3046,SC3051 # Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # @@ -20,4 +20,52 @@ source $mountpoint/etc/options_menu/scripts/om_functions script_init +modtitle="themes" + +theme_path="$usb_path/$modtitle/$s_type" +[ ! -d "$theme_path" ] && theme_path="$nand_path/$modtitle/$s_type" +system="$(echo "$s_type" | awk '{print toupper($0)}')" +has_theme="$(ls -1 "$theme_path/$system".*.tar.gz 2> /dev/null | wc -l)" + +chmenu_file="$root_bin/chmenu" +fontfix_file="$mountpoint/etc/preinit.d/b8025_fontfix" + +# extracts any theme archives already sitting in $theme_path (e.g. copied +# there over USB), instead of the old version's "*.tar.gz in whatever the +# CWD happens to be" - which didn't match what $has_theme itself checked +hasThemesToInstall() { + [ "$has_theme" = 0 ] && return + for f in "$theme_path/$system".*.tar.gz; do + [ -f "$f" ] || continue + tar -xzvf "$f" -C "$theme_path" && rm -f "$f" + done +} + +# patches chmenu/fontfix (via $injected_file, not a hardcoded path - so the +# grep guard and the sed always target the same file even if $rootfs +# resolves differently) to call into the theme selector's own hooks +injectFile() { + for scrpt in "$@"; do + case "$scrpt" in + chmenu) injected_file="$chmenu_file" ;; + fontfix) injected_file="$fontfix_file" ;; + esac + + grep -q "# themeselector" "$injected_file" && continue + + if [ "$scrpt" = "chmenu" ]; then + # shellcheck disable=SC1004 + sed -i '/^overmount_games.*/a # themeselector fix \ +themeSelector \ +fontFix' "$injected_file" + elif [ "$scrpt" = "fontfix" ]; then + # shellcheck disable=SC1004,SC2016 + sed -i '/^fontFix_getPath().*/a # themeselector \ + if [ -f "$mountpoint$profilepath/$modname/active_theme" ]; then \ + active_theme=$(cat "$mountpoint$profilepath/$modname/active_theme") \ + fontFix_getPath0 "$active_theme/fonts/$1" && return 0 \ + fi' "$injected_file" + fi + done +} diff --git a/mod/uninstall b/mod/uninstall index de8730f..662de06 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -47,8 +47,8 @@ esac ORIGINAL_THEME="$mountpoint/usr/share/$UI0" -copy "$rootfs/etc/options_menu/themes/scripts/om_chmenu_og" "$rootfs/bin/chmenu" -copy "$rootfs/etc/options_menu/themes/scripts/om_fontfix_og" "$rootfs/etc/preinit.d/b8025_fontfix" +sed -i '/# themeselector fix$/,+2d' "$rootfs/bin/chmenu" +sed -i '/# themeselector$/,+4d' "$rootfs/etc/preinit.d/b8025_fontfix" remove "$mountpoint/$profilepath/$modname/lastTheme" remove "$mountpoint/$profilepath/$modname/prevTheme" remove "$mountpoint/$profilepath/$modname/active_theme" From 1c2203f08f4f93e36ca6f6ff980e9321f25e3bd0 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Mon, 14 Sep 2026 17:08:44 +0000 Subject: [PATCH 027/210] Generate wall/packed DIY templates from the console's own data instead of vendoring them --- .../themes/scripts/om_packed.json | 1 - .../themes/scripts/om_packed.sprites.json | 1 + .../themes/scripts/om_packed_nes.json | 1 - .../themes/scripts/om_packed_nes.sprites.json | 1 + .../themes/scripts/om_themes_vars | 10 +++++-- .../options_menu/themes/scripts/om_wall.json | 1 - .../themes/scripts/om_wall.sprites.json | 1 + .../themes/scripts/om_wall_far.json | 1 - .../themes/scripts/om_wall_far.sprites.json | 1 + .../themes/scripts/om_wall_near.json | 1 - .../themes/scripts/om_wall_near.sprites.json | 1 + mod/install | 29 +++++++++++++++++++ 12 files changed, 41 insertions(+), 8 deletions(-) delete mode 100644 mod/etc/options_menu/themes/scripts/om_packed.json create mode 100644 mod/etc/options_menu/themes/scripts/om_packed.sprites.json delete mode 100644 mod/etc/options_menu/themes/scripts/om_packed_nes.json create mode 100644 mod/etc/options_menu/themes/scripts/om_packed_nes.sprites.json delete mode 100644 mod/etc/options_menu/themes/scripts/om_wall.json create mode 100644 mod/etc/options_menu/themes/scripts/om_wall.sprites.json delete mode 100644 mod/etc/options_menu/themes/scripts/om_wall_far.json create mode 100644 mod/etc/options_menu/themes/scripts/om_wall_far.sprites.json delete mode 100644 mod/etc/options_menu/themes/scripts/om_wall_near.json create mode 100644 mod/etc/options_menu/themes/scripts/om_wall_near.sprites.json diff --git a/mod/etc/options_menu/themes/scripts/om_packed.json b/mod/etc/options_menu/themes/scripts/om_packed.json deleted file mode 100644 index 9a1a9e8..0000000 --- a/mod/etc/options_menu/themes/scripts/om_packed.json +++ /dev/null @@ -1 +0,0 @@ -{"texture":{"id":"ff9fb46c-ad68-46f6-9e87-7108e2080773","width":512,"height":1024},"sprites":{"changeme.png":{"textureFrame":[4,4,424,320],"pivot":[216,40],"sourceSize":[424,324],"sourceOffset":[0,0]}}} \ No newline at end of file diff --git a/mod/etc/options_menu/themes/scripts/om_packed.sprites.json b/mod/etc/options_menu/themes/scripts/om_packed.sprites.json new file mode 100644 index 0000000..2359ead --- /dev/null +++ b/mod/etc/options_menu/themes/scripts/om_packed.sprites.json @@ -0,0 +1 @@ +{"changeme.png":{"textureFrame":[4,4,424,320],"pivot":[216,40],"sourceSize":[424,324],"sourceOffset":[0,0]}} diff --git a/mod/etc/options_menu/themes/scripts/om_packed_nes.json b/mod/etc/options_menu/themes/scripts/om_packed_nes.json deleted file mode 100644 index 2eae929..0000000 --- a/mod/etc/options_menu/themes/scripts/om_packed_nes.json +++ /dev/null @@ -1 +0,0 @@ -{"texture":{"id":"ff9fb46c-ad68-46f6-9e87-7108e2080773","width":512,"height":1024},"sprites":{"changeme.png":{"textureFrame":[1,2,424,202],"pivot":[216,40],"sourceSize":[424,324],"sourceOffset":[0,0]}}} \ No newline at end of file diff --git a/mod/etc/options_menu/themes/scripts/om_packed_nes.sprites.json b/mod/etc/options_menu/themes/scripts/om_packed_nes.sprites.json new file mode 100644 index 0000000..37188df --- /dev/null +++ b/mod/etc/options_menu/themes/scripts/om_packed_nes.sprites.json @@ -0,0 +1 @@ +{"changeme.png":{"textureFrame":[1,2,424,202],"pivot":[216,40],"sourceSize":[424,324],"sourceOffset":[0,0]}} diff --git a/mod/etc/options_menu/themes/scripts/om_themes_vars b/mod/etc/options_menu/themes/scripts/om_themes_vars index 78f5713..22f6000 100644 --- a/mod/etc/options_menu/themes/scripts/om_themes_vars +++ b/mod/etc/options_menu/themes/scripts/om_themes_vars @@ -21,14 +21,19 @@ source $mountpoint/etc/options_menu/scripts/om_functions script_init modtitle="themes" +omTitle="THEME_SELECTOR" +b_file="$mountpoint/etc/preinit.d/b8024_themeselector" +p_file="$mountpoint/etc/preinit.d/p8024_themeselector" +fontfix_file="$mountpoint/etc/preinit.d/b8025_fontfix" +chmenu_file="$root_bin/chmenu" +has_folders="${gStorage}/001" + theme_path="$usb_path/$modtitle/$s_type" [ ! -d "$theme_path" ] && theme_path="$nand_path/$modtitle/$s_type" system="$(echo "$s_type" | awk '{print toupper($0)}')" has_theme="$(ls -1 "$theme_path/$system".*.tar.gz 2> /dev/null | wc -l)" -chmenu_file="$root_bin/chmenu" -fontfix_file="$mountpoint/etc/preinit.d/b8025_fontfix" # extracts any theme archives already sitting in $theme_path (e.g. copied # there over USB), instead of the old version's "*.tar.gz in whatever the @@ -68,4 +73,3 @@ fontFix' "$injected_file" fi done } - diff --git a/mod/etc/options_menu/themes/scripts/om_wall.json b/mod/etc/options_menu/themes/scripts/om_wall.json deleted file mode 100644 index 46f79e0..0000000 --- a/mod/etc/options_menu/themes/scripts/om_wall.json +++ /dev/null @@ -1 +0,0 @@ -{"texture": {"id": "1c75bca3-310d-48f1-95ad-20e7ebc294c6","width": 512,"height": 184},"sprites": {"changeme.png": {"textureFrame": [50,1,180,180],"pivot": [256,40],"sourceSize": [180,180],"sourceOffset": [0,0]}}} \ No newline at end of file diff --git a/mod/etc/options_menu/themes/scripts/om_wall.sprites.json b/mod/etc/options_menu/themes/scripts/om_wall.sprites.json new file mode 100644 index 0000000..6456acf --- /dev/null +++ b/mod/etc/options_menu/themes/scripts/om_wall.sprites.json @@ -0,0 +1 @@ +{"changeme.png":{"textureFrame":[50,1,180,180],"pivot":[256,40],"sourceSize":[180,180],"sourceOffset":[0,0]}} diff --git a/mod/etc/options_menu/themes/scripts/om_wall_far.json b/mod/etc/options_menu/themes/scripts/om_wall_far.json deleted file mode 100644 index c86bb9a..0000000 --- a/mod/etc/options_menu/themes/scripts/om_wall_far.json +++ /dev/null @@ -1 +0,0 @@ -{"texture":{"id":"b7e86fa6-e226-4058-a686-e71f7d01bcc6","width":512,"height":178},"sprites": {"changeme.png": {"textureFrame": [50,1,178,178],"pivot": [256,89],"sourceSize": [178,178],"sourceOffset": [0,0]}}} \ No newline at end of file diff --git a/mod/etc/options_menu/themes/scripts/om_wall_far.sprites.json b/mod/etc/options_menu/themes/scripts/om_wall_far.sprites.json new file mode 100644 index 0000000..dc86902 --- /dev/null +++ b/mod/etc/options_menu/themes/scripts/om_wall_far.sprites.json @@ -0,0 +1 @@ +{"changeme.png":{"textureFrame":[50,1,178,178],"pivot":[256,89],"sourceSize":[178,178],"sourceOffset":[0,0]}} diff --git a/mod/etc/options_menu/themes/scripts/om_wall_near.json b/mod/etc/options_menu/themes/scripts/om_wall_near.json deleted file mode 100644 index 801c50f..0000000 --- a/mod/etc/options_menu/themes/scripts/om_wall_near.json +++ /dev/null @@ -1 +0,0 @@ -{"texture":{"id":"3384d6e1-fad7-4b8d-9a5e-38ea786aaf28","width":1024,"height":100},"sprites": {"changeme.png": {"textureFrame": [50,1,100,100],"pivot": [512,50],"sourceSize": [100,100],"sourceOffset": [0,0]}}} \ No newline at end of file diff --git a/mod/etc/options_menu/themes/scripts/om_wall_near.sprites.json b/mod/etc/options_menu/themes/scripts/om_wall_near.sprites.json new file mode 100644 index 0000000..994b400 --- /dev/null +++ b/mod/etc/options_menu/themes/scripts/om_wall_near.sprites.json @@ -0,0 +1 @@ +{"changeme.png":{"textureFrame":[50,1,100,100],"pivot":[512,50],"sourceSize":[100,100],"sourceOffset":[0,0]}} diff --git a/mod/install b/mod/install index 242cc19..85b5aad 100644 --- a/mod/install +++ b/mod/install @@ -23,6 +23,18 @@ del() { [ -f "$1" ] && rm -rf "$1" } +# writes $om_themes_scripts/om_.json by combining the console's own +# "texture" block (read live from $2, never shipped by us) with our own +# "sprites" fragment (om_.sprites.json) - so the only thing this hmod +# vendors is content it actually authored, not a copy of the console's own +# sprite atlas metadata +genThemeJson() { + [ -f "$2" ] || return + texture="$(grep -o '"texture":[[:space:]]*{[^}]*}' "$2")" + sprites="$(cat "$om_themes_scripts/om_${1}.sprites.json")" + echo "{${texture},\"sprites\":${sprites}}" >"$om_themes_scripts/om_${1}.json" +} + om_themes_scripts="$rootfs/etc/options_menu/themes/scripts" usr="$rootfs/usr" usr_share="$usr/share" @@ -73,6 +85,23 @@ chmod +x "$usr/bin/pngpack-upx" sed '1,2d' "$om_themes_scripts/om_themescript" >"$b_file" [ ! -f "$p_file" ] && echo themeSelector >"$p_file" +# regenerate the DIY sprite templates against this console's own atlas data +# (see genThemeJson above) - wall/wall_far/wall_near/packed only exist on +# SNES's UI, packed_nes is clover-ui's (NES) own equivalent +if [ "$sftype" = "snes" ]; then + ui0="ui/$sftype-$sfregion" + [ "$sfregion" = "jpn" ] && ui0="ui/shvc" + wallpaper_dir="$usr_share/$ui0/resources/layout/asset/sprite/wallpaper" + genThemeJson wall "$wallpaper_dir/wall.json" + genThemeJson wall_far "$wallpaper_dir/wall_far.json" + genThemeJson wall_near "$wallpaper_dir/wall_near.json" + genThemeJson packed "$usr_share/$ui0/resources/sprites/packed.json" +elif [ "$sftype" = "nes" ]; then + packed_nes_src="$usr_share/clover-ui/resources/sprites/nes.json" + [ "$sfregion" = "jpn" ] && packed_nes_src="$usr_share/clover-ui/resources/sprites/hvc.json" + genThemeJson packed_nes "$packed_nes_src" +fi + # shellcheck disable=SC2034 cfg_OM_ThemeSelector_enabled='y' return 1 From d24a37776ccafe17da652d66e948824b898bbab5 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Mon, 14 Sep 2026 17:11:39 +0000 Subject: [PATCH 028/210] Remove redundant om_firstrun marker hardcoded to save_path --- mod/var/lib/clover/profiles/0/hakchi/om_firstrun | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 mod/var/lib/clover/profiles/0/hakchi/om_firstrun diff --git a/mod/var/lib/clover/profiles/0/hakchi/om_firstrun b/mod/var/lib/clover/profiles/0/hakchi/om_firstrun deleted file mode 100644 index e69de29..0000000 From aaef02dca4b7dcd2ac80c2150e6e94216a533c7d Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 15 Sep 2026 20:41:02 +0000 Subject: [PATCH 029/210] Remove sprite assets excluded from every build (mario/luigi/wall character sets, packed_nes PNGs) --- .gitignore | 26 ++++++++++++++++++ .../assets/luigi/Fire_Bros/luigi_jump01.png | Bin 609 -> 0 bytes .../assets/luigi/Fire_Bros/luigi_jump02.png | Bin 586 -> 0 bytes .../assets/luigi/Fire_Bros/luigi_run01.png | Bin 516 -> 0 bytes .../assets/luigi/Fire_Bros/luigi_run02.png | Bin 584 -> 0 bytes .../assets/luigi/Fire_Bros/luigi_run03.png | Bin 592 -> 0 bytes .../assets/luigi/Frogman/luigi_jump01.png | Bin 517 -> 0 bytes .../assets/luigi/Frogman/luigi_jump02.png | Bin 506 -> 0 bytes .../assets/luigi/Frogman/luigi_run01.png | Bin 457 -> 0 bytes .../assets/luigi/Frogman/luigi_run02.png | Bin 517 -> 0 bytes .../assets/luigi/Frogman/luigi_run03.png | Bin 504 -> 0 bytes .../assets/luigi/Hammer_Bro/luigi_jump01.png | Bin 640 -> 0 bytes .../assets/luigi/Hammer_Bro/luigi_jump02.png | Bin 622 -> 0 bytes .../assets/luigi/Hammer_Bro/luigi_run01.png | Bin 572 -> 0 bytes .../assets/luigi/Hammer_Bro/luigi_run02.png | Bin 621 -> 0 bytes .../assets/luigi/Hammer_Bro/luigi_run03.png | Bin 648 -> 0 bytes .../assets/luigi/Little_Bro/luigi_jump01.png | Bin 431 -> 0 bytes .../assets/luigi/Little_Bro/luigi_jump02.png | Bin 431 -> 0 bytes .../assets/luigi/Little_Bro/luigi_run01.png | Bin 353 -> 0 bytes .../assets/luigi/Little_Bro/luigi_run02.png | Bin 402 -> 0 bytes .../assets/luigi/Little_Bro/luigi_run03.png | Bin 400 -> 0 bytes .../assets/luigi/Racoon_tail/luigi_jump01.png | Bin 671 -> 0 bytes .../assets/luigi/Racoon_tail/luigi_jump02.png | Bin 698 -> 0 bytes .../assets/luigi/Racoon_tail/luigi_run01.png | Bin 583 -> 0 bytes .../assets/luigi/Racoon_tail/luigi_run02.png | Bin 675 -> 0 bytes .../assets/luigi/Racoon_tail/luigi_run03.png | Bin 673 -> 0 bytes .../assets/luigi/Super_Bro/luigi_jump01.png | Bin 609 -> 0 bytes .../assets/luigi/Super_Bro/luigi_jump02.png | Bin 597 -> 0 bytes .../assets/luigi/Super_Bro/luigi_run01.png | Bin 516 -> 0 bytes .../assets/luigi/Super_Bro/luigi_run02.png | Bin 594 -> 0 bytes .../assets/luigi/Super_Bro/luigi_run03.png | Bin 598 -> 0 bytes .../assets/luigi/Tanooki/luigi_jump01.png | Bin 646 -> 0 bytes .../assets/luigi/Tanooki/luigi_jump02.png | Bin 666 -> 0 bytes .../assets/luigi/Tanooki/luigi_run01.png | Bin 586 -> 0 bytes .../assets/luigi/Tanooki/luigi_run02.png | Bin 634 -> 0 bytes .../assets/luigi/Tanooki/luigi_run03.png | Bin 635 -> 0 bytes .../assets/mario/Fire_Bros/mario_dush01.png | Bin 557 -> 0 bytes .../assets/mario/Fire_Bros/mario_dush02.png | Bin 535 -> 0 bytes .../assets/mario/Fire_Bros/mario_dush03.png | Bin 570 -> 0 bytes .../assets/mario/Fire_Bros/mario_jump01.png | Bin 561 -> 0 bytes .../assets/mario/Fire_Bros/mario_jump02.png | Bin 537 -> 0 bytes .../assets/mario/Fire_Bros/mario_look.png | Bin 384 -> 0 bytes .../assets/mario/Fire_Bros/mario_run01.png | Bin 482 -> 0 bytes .../assets/mario/Fire_Bros/mario_run02.png | Bin 539 -> 0 bytes .../assets/mario/Fire_Bros/mario_run03.png | Bin 549 -> 0 bytes .../assets/mario/Fire_Bros/mario_spin01.png | Bin 520 -> 0 bytes .../assets/mario/Fire_Bros/mario_spin02.png | Bin 505 -> 0 bytes .../assets/mario/Frogman/mario_jump01.png | Bin 510 -> 0 bytes .../assets/mario/Frogman/mario_jump02.png | Bin 511 -> 0 bytes .../assets/mario/Frogman/mario_look.png | Bin 429 -> 0 bytes .../assets/mario/Frogman/mario_run01.png | Bin 447 -> 0 bytes .../assets/mario/Frogman/mario_run02.png | Bin 510 -> 0 bytes .../assets/mario/Frogman/mario_run03.png | Bin 509 -> 0 bytes .../assets/mario/Hammer_Bro/mario_jump01.png | Bin 626 -> 0 bytes .../assets/mario/Hammer_Bro/mario_jump02.png | Bin 592 -> 0 bytes .../assets/mario/Hammer_Bro/mario_look.png | Bin 364 -> 0 bytes .../assets/mario/Hammer_Bro/mario_run01.png | Bin 557 -> 0 bytes .../assets/mario/Hammer_Bro/mario_run02.png | Bin 591 -> 0 bytes .../assets/mario/Hammer_Bro/mario_run03.png | Bin 610 -> 0 bytes .../assets/mario/Hammer_Bro/mario_spin01.png | Bin 535 -> 0 bytes .../assets/mario/Hammer_Bro/mario_spin02.png | Bin 473 -> 0 bytes .../assets/mario/Little_Bro/mario_dush01.png | Bin 409 -> 0 bytes .../assets/mario/Little_Bro/mario_dush02.png | Bin 406 -> 0 bytes .../assets/mario/Little_Bro/mario_dush03.png | Bin 415 -> 0 bytes .../assets/mario/Little_Bro/mario_jump01.png | Bin 452 -> 0 bytes .../assets/mario/Little_Bro/mario_jump02.png | Bin 452 -> 0 bytes .../assets/mario/Little_Bro/mario_look.png | Bin 386 -> 0 bytes .../assets/mario/Little_Bro/mario_run01.png | Bin 350 -> 0 bytes .../assets/mario/Little_Bro/mario_run02.png | Bin 415 -> 0 bytes .../assets/mario/Little_Bro/mario_run03.png | Bin 412 -> 0 bytes .../assets/mario/Little_Bro/mario_spin01.png | Bin 384 -> 0 bytes .../assets/mario/Little_Bro/mario_spin02.png | Bin 379 -> 0 bytes .../assets/mario/Racoon_tail/mario_dush01.png | Bin 659 -> 0 bytes .../assets/mario/Racoon_tail/mario_dush02.png | Bin 632 -> 0 bytes .../assets/mario/Racoon_tail/mario_dush03.png | Bin 640 -> 0 bytes .../assets/mario/Racoon_tail/mario_jump01.png | Bin 656 -> 0 bytes .../assets/mario/Racoon_tail/mario_jump02.png | Bin 652 -> 0 bytes .../assets/mario/Racoon_tail/mario_look.png | Bin 447 -> 0 bytes .../assets/mario/Racoon_tail/mario_run01.png | Bin 555 -> 0 bytes .../assets/mario/Racoon_tail/mario_run02.png | Bin 637 -> 0 bytes .../assets/mario/Racoon_tail/mario_run03.png | Bin 624 -> 0 bytes .../assets/mario/Racoon_tail/mario_spin01.png | Bin 571 -> 0 bytes .../assets/mario/Racoon_tail/mario_spin02.png | Bin 579 -> 0 bytes .../assets/mario/Super_Bro/mario_dush01.png | Bin 566 -> 0 bytes .../assets/mario/Super_Bro/mario_dush02.png | Bin 547 -> 0 bytes .../assets/mario/Super_Bro/mario_dush03.png | Bin 565 -> 0 bytes .../assets/mario/Super_Bro/mario_jump01.png | Bin 578 -> 0 bytes .../assets/mario/Super_Bro/mario_jump02.png | Bin 559 -> 0 bytes .../assets/mario/Super_Bro/mario_look.png | Bin 385 -> 0 bytes .../assets/mario/Super_Bro/mario_run01.png | Bin 477 -> 0 bytes .../assets/mario/Super_Bro/mario_run02.png | Bin 561 -> 0 bytes .../assets/mario/Super_Bro/mario_run03.png | Bin 537 -> 0 bytes .../assets/mario/Super_Bro/mario_spin01.png | Bin 534 -> 0 bytes .../assets/mario/Super_Bro/mario_spin02.png | Bin 500 -> 0 bytes .../assets/mario/Tanooki/mario_dush01.png | Bin 619 -> 0 bytes .../assets/mario/Tanooki/mario_dush02.png | Bin 597 -> 0 bytes .../assets/mario/Tanooki/mario_dush03.png | Bin 625 -> 0 bytes .../assets/mario/Tanooki/mario_jump01.png | Bin 596 -> 0 bytes .../assets/mario/Tanooki/mario_jump02.png | Bin 625 -> 0 bytes .../assets/mario/Tanooki/mario_look.png | Bin 450 -> 0 bytes .../assets/mario/Tanooki/mario_run01.png | Bin 573 -> 0 bytes .../assets/mario/Tanooki/mario_run02.png | Bin 605 -> 0 bytes .../assets/mario/Tanooki/mario_run03.png | Bin 651 -> 0 bytes .../assets/mario/Tanooki/mario_spin01.png | Bin 532 -> 0 bytes .../assets/mario/Tanooki/mario_spin02.png | Bin 474 -> 0 bytes .../assets/packed_nes/packed_nes_cannabis.png | Bin 6473 -> 0 bytes .../assets/packed_nes/packed_nes_famicom.png | Bin 752 -> 0 bytes .../packed_nes_gameboy_advanced.png | Bin 8099 -> 0 bytes .../packed_nes/packed_nes_gameboy_color.png | Bin 6910 -> 0 bytes .../assets/packed_nes/packed_nes_gold.png | Bin 608 -> 0 bytes .../assets/packed_nes/packed_nes_mega.png | Bin 2229 -> 0 bytes .../assets/packed_nes/packed_nes_metroid.png | Bin 1458 -> 0 bytes .../assets/packed_nes/packed_nes_nes.png | Bin 681 -> 0 bytes .../assets/packed_nes/packed_nes_zelda.png | Bin 1073 -> 0 bytes .../sprites/assets/wall/Fire_Bros/wall.png | Bin 123059 -> 0 bytes .../sprites/assets/wall/Frogman/wall.png | Bin 41258 -> 0 bytes .../sprites/assets/wall/Hammer_Bro/wall.png | Bin 288145 -> 0 bytes .../sprites/assets/wall/Little_Bro/wall.png | Bin 211003 -> 0 bytes .../sprites/assets/wall/Racoon_tail/wall.png | Bin 87386 -> 0 bytes .../sprites/assets/wall/Super_Bro/wall.png | Bin 137337 -> 0 bytes .../sprites/assets/wall/Tanooki/wall.png | Bin 232129 -> 0 bytes 121 files changed, 26 insertions(+) delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_jump01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_jump02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_run01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_run02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_run03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_jump01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_jump02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_run01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_run02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_run03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_jump01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_jump02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_run01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_run02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_run03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_jump01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_jump02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_run01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_run02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_run03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_jump01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_jump02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_run01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_run02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_run03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_jump01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_jump02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_run01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_run02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_run03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_jump01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_jump02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_run01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_run02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_run03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_dush01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_dush02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_dush03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_jump01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_jump02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_look.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_run01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_run02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_run03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_spin01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_spin02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_jump01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_jump02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_look.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_run01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_run02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_run03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_jump01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_jump02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_look.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_run01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_run02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_run03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_spin01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_spin02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_dush01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_dush02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_dush03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_jump01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_jump02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_look.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_run01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_run02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_run03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_spin01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_spin02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_dush01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_dush02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_dush03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_jump01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_jump02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_look.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_run01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_run02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_run03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_spin01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_spin02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_dush01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_dush02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_dush03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_jump01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_jump02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_look.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_run01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_run02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_run03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_spin01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_spin02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_dush01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_dush02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_dush03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_jump01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_jump02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_look.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_run01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_run02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_run03.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_spin01.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_spin02.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_cannabis.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_famicom.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_gameboy_advanced.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_gameboy_color.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_gold.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_mega.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_metroid.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_nes.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_zelda.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/wall/Fire_Bros/wall.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/wall/Frogman/wall.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/wall/Hammer_Bro/wall.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/wall/Little_Bro/wall.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/wall/Racoon_tail/wall.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/wall/Super_Bro/wall.png delete mode 100644 mod/etc/options_menu/themes/creator/sprites/assets/wall/Tanooki/wall.png diff --git a/.gitignore b/.gitignore index 1a9bc8e..389aa9e 100644 --- a/.gitignore +++ b/.gitignore @@ -502,6 +502,31 @@ mod/etc/options_menu/themes/scripts/b8024_themeselector mod-/ mod-backup/ out--/ + +# excluded from every build (exclude-file.txt) - kept on disk for local +# testing, never tracked +mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/ +mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/ +mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/ +mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/ +mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/ +mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/ +mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/ +mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/ +mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/ +mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/ +mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/ +mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/ +mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/ +mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/ +mod/etc/options_menu/themes/creator/sprites/assets/wall/Fire_Bros/ +mod/etc/options_menu/themes/creator/sprites/assets/wall/Frogman/ +mod/etc/options_menu/themes/creator/sprites/assets/wall/Hammer_Bro/ +mod/etc/options_menu/themes/creator/sprites/assets/wall/Little_Bro/ +mod/etc/options_menu/themes/creator/sprites/assets/wall/Racoon_tail/ +mod/etc/options_menu/themes/creator/sprites/assets/wall/Super_Bro/ +mod/etc/options_menu/themes/creator/sprites/assets/wall/Tanooki/ +mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/*.png mod/etc/options_menu/themes-old/ modules/ mod/etc/options_menu/themes/scripts/scripts/* @@ -527,3 +552,4 @@ preinit b0010_functions b0050_boot tools/gen_theme_options_preview.py +_console_originals/ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_jump01.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_jump01.png deleted file mode 100644 index 3a7483aa6b4aaa74ea557d44423d39bbe03f4100..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 609 zcmV-n0-pVeP)&J2iP}Yzh;2MWaqK=%E0f6*#8o8EaO|ptSD%$xS zJL&llAX?8WX&;7J&m$-9A-G6#>A# z7ZhChfWVx4-Eg3=wf`vwz*69Cz)=w@Z32I+&`AbP9oxX`e2<8OX8|l5NiZt%sdBbW z$&dDd=qqsRcOdKFsnk4X5MzR6!yTn1ivjS>V$}}T_S#r{b_GOV!RkG`eMdovXtLH$ zR_cj}re=6s=I$SU{>K2Z-~sRf$YBk9q0MQQY+GVH-vX&J^I=;#Ja7Pj)jPNrG(+Ws z7epv%F8HWuSD$PK08q6h+Uf%U&|zMw>q~37rdoFLOkT!i0H)3ooDWW}DHZJih-ez> z0)O7-W73!#I-CQL>b&d$C)bok8E6qGF!bL7>U0Cz&|kHsTXiz9vt*IA2<`)XAoojI zY9Bmq6+oDR#_W6-n&&Q{-3=oOa53aR0yYKgmX*FXvx*cmw8W*l18~>Giy^?+ojOur vDT^|Arg05*T0y{rZkl7;OY*J`LNfW+0%@t zjg`D7Jy}YC|7;(nHxkhpUyDfbvqj*}xy4!&rtDWKiDM1N8qcc+To1mhO;&rBO)~|+ z_B#D}+@TuI%9V7KM8|gp09lfv37Sy zRnnKO0e4Nfb|MSEV;5xd1p2m>>US=(*PT(RDgXkL_O?|4JSHs8AeP2!y#^dK!B|=N z_X_=XAH;gcSpoawBP)@Jd=Sw%XbMuT!Q*@PFp37&nj{8KN#iY>K^&a{#sPD~t!t99 zZ~GvwgRv6Up`Ef|doAvl4fv~T#^>qp7{)a>BN2^lf1LMTUt#}8!TtJ1qt;$Kt3erNUI*v*K~x_6E1(0NW~5G!@&4%z-4PYO=`wM$^?`X0(TH&Q2tN zhfES9dsm`3dZIe)o34gF^Wy!G>*8GkA{zUq!k;6cPYxmv+;CoynDg8Nht}bFtJX02 z3@kG=6zpdOeR=@h(r>!jN1X)rmaKtg#;ZpF@+)PjJ}4R$3kh1;_%1ZpNaiNuhW!?>LL*PgBMFa2U;^O}32XU$W Y1G_DHR=e>buK)l507*qoM6N<$f?RzLlmGw# diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_run01.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_run01.png deleted file mode 100644 index c899e60edaf58052629df8d07f5140618d6aa78f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 516 zcmV+f0{i`mP)PdRG9N?d^kuD^ne%xnf@m`T}=iKC*6E5LRO5$vR+pz##2e=Nl)lX5ykPI_spsN(N z`-AUXTj0oL-@i-7K5iF)CYzmPmt&;1O=0pW+OfvUASM*j@(v)TpQ3=~wjbK%=>yE1 zSp9(L!8xBhFkdHQM6%PAjewaqXsAam^LP&NZ_AzRsh!Hdn1Go#w?++yrTGU|hzxjd z*J2fc`BwP3UtVMVQ9W>4Y*C7csE-+!Kpa8wHGbIT1@9N?VEjDSPEP^=0000)Qwb diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_run02.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_run02.png deleted file mode 100644 index a77b1e07dbdbf934aa2251bfa3cb350a8f61f07c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 584 zcmV-O0=NB%P) zEwke=4BXxv2s~IAC}?PCSa>k7Ft9(NurRRjVBx{QKtn;n!ootc9LU&lKJ4etJaC%v zw6T(1X=N!npwNhDjITxHQ2J~UxN~l?)`ThhQA*-i!?DKerUBQ3A8M1;o@LWa0kFMI zf1VGhhO=@d9VOB6LjgdRq$qwmc7QdcUHM|JsBZAVCVq2B; zWoy7)6Rw@e!tdAxnLL5MZ>9Q~i|qYiRH_Pqz@)uxRRE6(i!+F&@mj9|2Td?m7XH0L zf7}PL-f>pI{`kmBBq9eQ8V5~5sx^3g?;b|cz*>{U;3;XmWiyDQGr%}tZn$+#QubvZ z#C0%M!aBB77Hn_D{jvdncg^@Z{~p7*=4K?KvF%Uu-urLZ|5k9nz9LE1z;`O!8z5(> zhGDuI!{xa+s!FLaRrjoTT(`Z!{WQR~3KdPob|P~iiK&__vcJ)EHJBOgv756K3E&}< z#K_*2D2|?}4*RC7p)b66KjgZ2mw<@IzNzr*1n9Ga$P;&579{2}_rS4rc-^ZtOg;n4 z3=IYQr-D8|fo|zHUG1Yz0((o=z%t|ABLMl8vQ!@wjf#Z?t!#W(n)a@svFL?!6$%dl zs{-EVNd36bO2IPk2Dc&bBl;=?h})cw6j{o4Gx+4Yvv1NT2q=6XvV3nYjRC zr|^Cn9yhlubSKqv6&JR$ldGP8*GP6Lm>MR}TVoTFZxh5Mrq^gL0@G_aaWhGb9Ld{O z;4PT`p`y7+c0Ef#(#yG<@13}&wh3R#+JSD7Z7MCV8cNuVM zh}PI-{pBX-dJvZ=Rt1!;qgbNw0ZBhKoXb^vH-}w@wIqqN-!i?I+%z7zWnaEMXd(FW zS$jTWI^7`Vch!@sZc1{tWf6GAd(-hT_EZ2=g!Y(!Tr;RXW@$1oOIbUBCCL; z58e~9)XguLUPF;keDq>!2(9ze9Z=k+VW%tu1lvgL;dU(&Yz~@;;57Zjx&4IJ8q1xu zW!cq13nb(bfh<<^-aF_{=J#Jx5LEPI8r3T~eNqEeGg2W@fS5X8p=yRYUsGP+ z2QbXh=Pk=Z6GYUjdUjS6?)>u!=$63DK_5YYIM*#rV*)D*fC#wXhw&}QYiPoD?mUm6 z(`kn6-T^S|;!)0!V>gFO)jWl4D^$@W=PJjsW8^A_4HL&H<^fXFD&b0jI5}7f%P^y> z6@Xzk50De86!Vg7*SXOKzz}}{Ep4lcoDeZ{R<;^O8^<{Sl&#*iaxrfQ%$ywoF9%(% zCa7p4MQE!!VCGE9G*@}>&#Gpir9K9U(rqm)lWVt;*iM42VmbEq!JttJcE6KS4MDsR zNvRrUl&!}5ZT4n6CQElQJ*?`$Fi#0xTkRSoln+nIwsUbRKfrB*0$;ZB;0}O6T#`^` z&d|E~(3c90X`BVb%u5PUxa_2=4wVW*mdTEmX$%4YhiYgajcJ0A;0GsUj?%|asyh7s zZh%1K-qwsH+wNN50(h{o5KcG7^3aiVE=q#@h5gFeb<4g0iRBcvtV!}w00000NkvXX Hu0mjfNaW_0 diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_jump02.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_jump02.png deleted file mode 100644 index a3d2315a8557206ffce24c8e9537abe1d7740b56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 506 zcmV8j1CCveP* z-x?IDAQ26wnUYmi{QL6>*p9%=$@Pl_lH7LWV?tI{01!okAALc_tr!NI~nL&1KASq2g%vTG;qdxJfaN=@&q zv!nuVyz#~+h-i?feEwJR@OvKQdA#Cfk&iWUe?3OIZ#u(R#w#L40f9N)U@XI&Znd2C z0)crouE8#sB%;CCg{*Dk?;j7qI|4Ijf3NXbBKMA#Ir*902ytub$FaF0PO##oSB1 zH3&YS>qjDedficy*OffY0W43r3T~eNqEeGg2W@fS5X8p=yRYUsGP+ z2QbXh=Pk=Z6GYUjdUjS6?)>u!=$63DK_5YYIM*#rV*)D*fC#wXhw&}QYiPoD?mUm6 z(`kn6-T^S|;!)0!V>gFO)jWl4D^$@W=PJjsW8^A_4HL&H<^fXFD&b0jI5}7f%P^y> z6@Xzk50De86!Vg7*SXOKzz}}{Ep4lcoDeZ{R<;^O8^<{Sl&#*iaxrfQ%$ywoF9%(% zCa7p4MQE!!VCGE9G*@}>&#Gpir9K9U(rqm)lWVt;*iM42VmbEq!JttJcE6KS4MDsR zNvRrUl&!}5ZT4n6CQElQJ*?`$Fi#0xTkRSoln+nIwsUbRKfrB*0$;ZB;0}O6T#`^` z&d|E~(3c90X`BVb%u5PUxa_2=4wVW*mdTEmX$%4YhiYgajcJ0A;0GsUj?%|asyh7s zZh%1K-qwsH+wNN50(h{o5KcG7^3aiVE=q#@h5gFeb<4g0iRBcvtV!}w00000NkvXX Hu0mjfNaW_0 diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_run03.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_run03.png deleted file mode 100644 index ca7d05ccbc522d2744c2d70056c2504e515f6c75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 504 zcmVY$%_Lt-$xVNZVn1|&>KI!{vIGKkx{R__Br)40W^KVs;VcXxPnq)Ael3Qj!QS%K5VK}djY-J9vq zWMLn>Z+8jskFiS`h-mQtjWN6B-qM!B@04?WmH+_94izu}fGi3~IqoyFzkSG&L|WJhh- za7#U8QGl&Q*F4d5`^~YPB_X20^>XMN3`8`vN=KdPY zKSrS(NFK&CN)sIwSq;39+s+9j8;C9J~kt0x;+#Vu5CLx7eo*=YH}|rJF_+k zPIV1tMtM9=d(N2g7nG}F`gx;;xh%_aMpjO>8%rxf97$ zcD2&3WE=R;cG$C-h{mN)Rg*D(?+MO%4rT@bh=^MnQy?EJ2xaYz^Lzs9`)DP}3W#Ww zHUskd&fl-jEO%&Y=TKSJwXCQ6{0Aay@eF6Z0j58dq zzI`1iH^Wp-_a~@V5+Eh8GpA9AMj)X95smU)&cAK#zWfPu8vp=2J3+>ikb_J-AFb{J z35x7f27#`D>i#kiJJ(K2dgG(JG>YMnt2n8TW$bL$Bp2>&h5N z-_PCCvPOq8+NQQ<3^L4Zc5GrHvbG^=crOlx7K(7xpLaR!m8o1zH`NXYzyao4L zl^a3Jk64(v2FsE!ZHu~TU~5K3lh}jmoJh+WtRSj^+gKR`5kYY}y{c24gF6ca2O<>t z*%w7I`zvEmT(0o%htw1Y0f=b4l^MQHKv4~_*1oM*#^C6Q?AkSE!3wMh3NY+XhN8Nz z)+=KGyuB>;0r()xKV&Q6PPfGfg_2u?7hOoR#K30V@IL25|49f*CeK z*9yR$J7*<8*B3|(EM~z9KD{@6yOmjTeb?hRWd6+TN$j85U)bhAel$YRBme*a07*qo IM6N<$f=A>Nk^lez diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_run01.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_run01.png deleted file mode 100644 index 1ffee8087393cae068831f2272fbe7a2a94fe1dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 572 zcmV-C0>k}@P)Wd9yAmb3_MsEmIH}hCv~jM&g`4% zjN;mUlAbIZ_+}jTP9&mnJ*KMW`k#9@=c)oT0{}!M{54u6#Xoai0{~Z5w59Khk!(gl zM58ngsBX`}zMTl_zU0?Ff&jj}FTSO5kj5?N)U=r@Gev)ov=}>ijYM1z3p+>h!-~MM z!^tZgJ2-X-gj^4h?hfhhktm03inhc}A>BQuFO9||Afi#;FY$X&g}<@Iw~1^7gX+_k z$+MGJEcBQw%0gD;wYWi80FmV6HCEG>z?%etWIg6@93x7JzjV-4^$;R)?6#(v{is-` zs(Ltijb7)xw_u)wHA%X8+cY4eu~gP(s|brr9?#7$+b)U(L^P&n;Mh)Wy1P7Z>3&(h z0Vt#>Z0?BQXe!NhUpl_MO%X7It14T0rUrxR^h-1#{=JwZBJna>qgbZL7B2^`yF8!* z0Q*v8io6!L*MGCMBuK8&1Dy*1bYCuF|Zu7m^6~XbEK1pn^Q~U>y$O~h+Chmj)0000< KMNUMnLSTX)qzlmi diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_run02.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_run02.png deleted file mode 100644 index 95542cdc7d18eb5240c6b2adbd26a94fff7ca338..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 621 zcmV-z0+RiSP)rLO3LWjDkOqGCepNuci=q^X$icR$rD_}8pBsylJ*>7Me(>5z_Q1>T zWDz;kz;eTL!)-N5Sylr}l|QD+59n+KZlD_KQwqY=?!I)z(B?$8_5nsuu+c$!@I z3`&xlGT5f3QPNASx>{-MmGAM~w5Y)gKoB3iCZfv+P_wKI${FOyrXQF|iWc1_u?0>g|73g)*!)n0ZB zTCD-RyevKg@IkhL?6YNSMDaU0dsL(-;Az5%Rvk_VgX{oKLT(YT6VNq)eHT?q!6fK< z0kG%JNeGbr1rh^G*{*_j-ka90O_81RcCe87d*+kG{xACnN_9Yf0zJ&200000NkvXX Hu0mjfHJliq diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_run03.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_run03.png deleted file mode 100644 index f26bcfade706424bc82a0582164160ded958cbb0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 648 zcmV;30(bq1P)%19(~<;a#Tkt-u{tl)Zf2s6NK)x9bT zm`}g{&j9}!yR8d}Xm}kHk=6abw+gq11FW?G0A|*yVU1*QGNm;D*ux=wbJs0elC^+{ z2CfZoxPA|I_mQCNmh{?2ps>Yl^3Sya*Jiq=q^zq}UG(8-GIsP5h-}$UzEdQ>)+{)B zfw3mMmC%;`XwBnKAW9)gKtw|>zzU6zEp9WH#hAr_hz7nD;lG#BCH`uc__h-O!1KW{ zcGRlp(fp&AACXLg61zoxtwBwVt7~!ro)M&o6tVWPRlst~NV-TjXqu|Kme%I5n5H7K zJ9-Hk19R=yuv!N($*b^tU1$51Vzu3-1YFK%yf;lCxt8Uo);$xfPv6*qtG6LH_R%!i z!pv5(W)0T{!k;J}d-$sjk1czUeFFfv(~OpadgD4r-D zdoVMMF*A9|G}U0fHZg4*aaMKm31FYKVCEvt8elvcFXuCu8Sc)rOsegAs%8sKgXWwI zWO^n8T#N_TJ-iJ_j--;jWvkeML5lR2Tqw?-5%QTLOI7!X^vid{)8H zH3iPOFuny70wNmP!eD;_+@%Qt1`u44!cryAT;!SYYytmnLOamA4|mHh-w}-!M5GR}sz#5wetrt?W8^M1RK5aW4K={-TU%ndT!38a zS0aM~z>VA~+x?BW50mOkky#@GI3>c`DW^m_;eaWDQ3nD3V5vgu4q_9N|+zpCI@(%D)teU`gpn?55p`6 z;iu2lim&q-XpUrmTn%{>@3&1bq;nUHMSEGU{h3-HpclOrWf~M2vg7aoag)h Z;Rk`D&SJ&PIi3Ij002ovPDHLkV1k7qyQ2UA diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_jump02.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_jump02.png deleted file mode 100644 index 9187850d67d1779a86a7294e82d5d8eebd9f3e9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 431 zcmV;g0Z{&lP)Hh-w}-!M5GR}sz#5wetrt?W8^M1RK5aW4K={-TU%ndT!38a zS0aM~z>VA~+x?BW50mOkky#@GI3>c`DW^m_;eaWDQ3nD3V5vgu4q_9N|+zpCI@(%D)teU`gpn?55p`6 z;iu2lim&q-XpUrmTn%{>@3&1bq;nUHMSEGU{h3-HpclOrWf~M2vg7aoag)h Z;Rk`D&SJ&PIi3Ij002ovPDHLkV1k7qyQ2UA diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_run01.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_run01.png deleted file mode 100644 index 9d0d945cb3c73057d7be2792fd0b9accd6483da0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 353 zcmV-n0iOPeP)NvRRm3Pq}_DRbAcH zaN@*?6DLl<<&~dBWZKrMntcEM3US@(LJapi(8`n&Np>J2ldE+1du-*k!hX%7AbOoH;aI(ULKcfU9$Pm9!FI)vp`oL$JFFNM5dxqC`;6p6pbRw z#9XES#J5PMXg#zpUeV1|`rgu-_0WbZv957$j%n>nV2){O(Ix>^aL&<}834YGJWJIx zm}45gGKL;PoDc7+vWOqY)Z_rbf2_x4>wLuzNv?#5(zo$!00000NkvXXu0mjfaS)&5 diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_run02.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_run02.png deleted file mode 100644 index 1581317dbb693d04128501b640491b07340873ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 402 zcmV;D0d4+?P)RPZPxk&%&+$bE!70uot?L{_duA|sI_NAd`jI4Y8*G+}|9o>Rr|_V!<9 zXJE&U9XodX9fz$KMP!(+&1}=#_pQWvZFgemI-rtai^$>y5gF2pMbEX8)g-G4v=XjI z;nW(}qX2Mfi=|pj@a1)M90&t??Y9J2|AXl| zhy?OeYpZ|owsbgs9P0lH`KrL9$dnwO6VBnK5{;s%$n7d1005Xk2`#ZpI|3tb1LX7px1tvrL-SW`EuO4fMlvm wP@uspqJDHL_1G!nz+HhnC^(%HY_8$;^Da#X@8P5=M^07*qoM6N<$f>>g*MgRZ+ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_run03.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_run03.png deleted file mode 100644 index 7e452596a5b95d211ec52471163a35100025adaf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 400 zcmV;B0dM|^P)BX2yNc9?;v z|1)^85=8|XmjH0~{ZyJqqoRJ?k9Sk_4_dtsS>qD$esUd1MLmTWh1hC0El#qTy%oq% upusDmesU`KF=09xiD7q}K9*taY>po>!^3jL^(wpo0000ndv5 zBy?wbCIi5K#${`eh@Jo-BD?c@YaFhx9;``FWB%{Ri0E09Ap6LX^>P@27a*c%Un&5A zZW;Vd0D#KOBuze(Cn^E@Lw@{<2Sy%o*97yJ$wlm?O7X&H5m+?x7=+#AGg;mU(i9kS z0hloIcuK%0XzBkRRB1QBEgdE}5Qs(YdToV^PL*<6z~>;2AW;djUV>jI$}Md^vn$|s zb{4MUjhybxe5Igshif z{;gKwsv{;|`hCZ?_$h%j1u%)+HF>KXVoQE>4)R7Y3|NUQ$rfei2)vC)R;}8#!xlMUJ1mj4 zQI*G$ta9KI9g{ChRp;m1%4P(nB5+Gco63_XL`cXc{I1D2ydMdmD8S4xpJ87bMS^lY zlY`)$?Cw*Q24cGHg7?F+T=#MyBA1I>bSn~kd;TZbWSqlL&u1fEf*w%cE>zPF{ZbfpuXu@gICEoeE{s)X}Ymar{zX$*T002ovPDHLk FV1lk>Fl_(; diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_jump02.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_jump02.png deleted file mode 100644 index 513a34ead4aecf1c338c8a9db76e903c8fabce90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 698 zcmV;r0!96aP)y#ilc0RT!XFLRJgZh^<4 zYDdI9y#!wo#3YkmOW~8MYB@}>%7Nchr~;*xprf0^(+ zOIqhM&p{?npnKkxYSjm~fnAM100F1Ht<|cr{QRL4#L{@JbHGUxN-N3ykIFPB@)7GD zp9=T~FYvOc-jYX9!e|W z66H#`>f~_0Bm_isx3$8g2}E>nMhV^nV?aCvhgJ14lB7&+twJ+3qu_FR*n%;@FoaFb zC=hYZntM|IwM%l~!K-c!9+#@F7FW#`XvvMY;#3F{iO9((acl_O6F#~ofEPq4$R&Kz z6bvta^QvwE0M&0dp5!1L)fg}{#&haRvq(~|xvP2~i@$nLwfpFr0Is?Y7>4Bgke92M z1pv;a+viR2&4XV|f%h|@KWAL|iV90oVGPPB$)a3EdFcRP>|5}Sz$2rnmVAXITV`k| z*gpmI=YT5~c|3`e5v!O*a4rpaxJdfXx6GmmbX%)ngB-v?* zhoHOem+@!*6bM?OBw0!GU|5>xVHa8{SmsUOgdqG93IQ^&Hf)g{iEx6W_6qe%__nS zuUWo0qR_f36Hg8R_)U!Mb&(`S9)m9E`=%C-%rWS9=837-Ivm3fX0|3(6oLN2zh=?^GxgQ)!PxPb(r06qr5xnM^RSQi!Z50RU}1wc&S3fr!}Xh4W~7^rl~o$?9a@Zuz1D zZw&k~wHpDFiI@f?HX-G&~awu@Lac^#o!?$U7R~Q~&eKVc;)wgLh1|ni(7GBR=`a^;gaPK1OcENu3wuB*28 zBa`k-&t#SiPe4Qq05EfQwtg04aXDIOiP2&svSdWGw8SVsDwK-PxX%Uj4*>w0aUYjqbyd{vb2>?_rQo{B%(BIO1pE>OF zSsVzQl@8&r}x0GL#f!kSeO>B zI!?YXfdCOLsus8x1R`3Rk=^WplpsC02`lO}k)TagE!<^lM#k;7js+=!VQ`z8k-=;x z>#La*)WDKg-JaZU)uP&_YH|iP3;+AIYcMq(R4tH`DfZ0l4w5oegBrL%DWSE-_!-V6 zOKxGO>KU9x{^_scx_A$Hym7e9eHAPGr)PiS~-j!gQ3i zZtPnCKvkRfv2W2bg&8QVQ9wZb$x(a_n6TMnGcl(+@FGI>8yFu^7g>>*hz_hSdJR=? zk2s&}CjZIfa8!`?EQzuqJ)k9CMa*eqjE_j5WpbNL23zY9hV4;t9T$ZVP+A9MW3K}d zX3j450}(AP6TtJXuaCr@t;e{e1jV??{B_WZWKtjRJYD~H`~~1|b#OqRhHn4>002ov JPDHLkV1oVOHKhOm diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_run03.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_run03.png deleted file mode 100644 index 31ce9791491f1b4c4f0f2b4f1411ee78a6bf199d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 673 zcmV;S0$%-zP)5WVbK!B2Rya%3d3awRf90l5;1L{=hKMj|UKkt0VUS61S(f+i$0{ z!{`cyfd@|#$}5s62YaidD--}AYxt$ECmWG%mSwz*|N*cM^R+oaD6}gdqyO0$wK=n1#q4AR>Fq$cJl9_J9K$Wx7TZ zTE7?kDM3i$WS*B_H<|-qY-$Y{gS-~>{l0EqOw(}wm#s{L4gicn*&Zv`MJK!QP__aQ zpe-q?q%6loQV7tHeddMtU=9vU;3*MO8gp^W$dB>E9=J-G1Gupfj)%VAN8mjWk;GLA zGXq+KnUOV&frqF}kBsa^qqzj1)X9@6HRV8*1{wpvjdR#JMz~(Ra87mU*FE>a;Xsd# z&rjP_0Dwv;&>e}QG9@AlJ&kM!?!2K{Zm%~Jt@)zio;5BK(S0xno-FQhQm4opCsl0# zjLaX80~Lw)=FNd$b&BpQ1&oQni48=s4lacDd;DMKW1jd<(euWKsAIJ;6|eZSTdoA07f**$u*M?ZATsN@gtqAk)_)~?A9y-|pz3WQ(G00000NkvXX Hu0mjf?B*ne diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_jump01.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_jump01.png deleted file mode 100644 index 9eafe11a20552bc1232c627ae0ab6c8785d79f3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 609 zcmV-n0-pVeP)@=9dnm3#tnBywfsN+c4AM7C_nCyd0+3MPR-0^GZ`r-~vW zov*=PdZ_ z0IL|P6u_Q~Bn2j(kPpkM_2)`V0rxwA3nI~ht-n)H<-7p4l#I!xAorSfy z1>=aF$eKLmno|q5mgIJEi6g#(n2yNpzGc{ z*BGE_P_+UPcsIsem9$`u7&!pc1%YUGqblQ;9LB%}dP^Q0M#XViIkljz2y6-Wy7J^1 z5i*(s-fQv&?^^;e2F#51Bdkl4NRY0n7$nbR*S@QJAf{~-oDVKtQ+r`2Kt!+3Wcc+l zACn`GD9{8Vd9(CI84x#HnqvzFW@so3e=_LL1|v-PgE8+_CwV=RF&LQf?BI#qlR|2< zot6p!feU0~4RM19&B_n#Zx~U5hmfBHgaITp%Z=|{F0@h@n4^o^3Ak(GLkN&*mmMjw v6rvVfy>|jYXR^cb%S0(`FUkMP_htMAP60*MPIqP)pdm5ZrbiFzhD;9uyQbEHo4>^d}ew8a^0UXc%Z{C@6T)pAcx~0!NA*>r`dty_@l5 z97(%(x4KP${~T|n8;NL*-R0p|-qRPYM0<9K`*Os{+#Yk!=w(zY)CW*nN*7(R)5NB5d z>x9R^J!>MxuX7OVP+JZUtL+Lmectp$0wP*%t1xK_cI~|f#(>-aZ{ay-6|bpnRT!r3 z${5GHEf@m~L)hD08O*Y?iW+$EraywmS5-F;S1qo_^>gn_9^8jy*~u2g*`L(uv0x1JW!U{b(3^f^{yL+=0Mb^qC3Z<#0PBGAWW3l>YAofYDUh j^@c( diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_run01.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_run01.png deleted file mode 100644 index 4e61129674425782ddb8659ce0287be06b31bc5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 516 zcmV+f0{i`mP)?)1Bv%t$)v z_V(5~1^CZ)(rzRf)^ib2omFij8D2kRvv#ei0(sH^06_g^QS}xgfCyk)d0>^YYzCa-V&vA06tZdz$v9HH2~6Ls~Ft3hH+fRG5D{IWO(^_58nIWy$`%x0?GM@W}5GvS1|y9`qnR#rcu=& zP(C=fe#Gd;%^H=JDKZd|6VV4j17Iv|e=zmmmI>@}OxI^yg$h7KPIUboSX+PKfXKj< z?NK`fFuWUl-G0Vy>PZ*LtF0;#$&d=JF*SqpzxBhezrEkwv-kWx{%HyT0000pdm5Zt^eFnpg7cu-K#u+UJj(4Sx!Xjm9nXc%Z{C@6T)pAcx~0#}M0>r@5yW;_{3 z((c`@Zj%!VgNWAnotaN1=ZZEF(c0a3_g&vm5y`(20sv_43e8=i9u$}X)$*aLRAe)d z0GfWQW}X3e%~v8Wlw_ycjVMP_RKcX_uvS|RBsqet2L4c`D%6&DpPtlRX_w4P1SM?( z0NW~YEvte1G~s>W{WV?O658Ym^wk|&^_I)*J~&jW3V^`Yd)ul29upQ<5Vywn`WbN2 zgxd0MJtg$Za}f7Ct_n!oN47=Gd?KQC(iB?t3?A=IVHA5&OcH}jt?`kqAkMA^)(MY+ zd)7pXU*{m!p|%_zR@)VB`n>6h1VpsjR$stCH>1HrR2YLg z5}DO@qohbyV4Z`8g8izXKL*S&`whnYWG&)`5H+yQ;XWb&`9}(A4vMTIZUH}4LvFx@ zHs%XxEt-Q1Q#b_d1tbh1Q*ReqDOl%{$sGv1MPHZzUJkcIB9kInLFxZa02ob`T`vZb gA{O_R#Qsiv1FaKFk)07$#{d8T07*qoM6N<$g2K@NYybcN diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_run03.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_run03.png deleted file mode 100644 index e2fc30d3c4138cebdfab9c49aa2bf6403f9b1f99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 598 zcmV-c0;&CpP)3Y}Iw_C$QB)^Uj06_CHXkG@YfGS{GeN&Z+Vg@OI zhpo^X0RRjGAbnX$c499((=H_|ksMO+thxGr@wy`UGC@q@TZ6h6_|{;QaVCi)Me=(O zcrScw004C_$@wG!$w~g$c|5AFz_u~Um`xz7;N=9Bsa60Nukfu&mM0f)HH%9Vn!5#j z*2Kl@YW~wrPW2!zQLGBcTSvA;;Rlk_v*ui`(%MuU_M(_1&VJ92KINwIf!p@w%Lgrh zzkRe8a1PB)i=I?;Q<75vzupziEy~(CY%M$$#giQD;d)o70{1IdJE?B4khBW)oVF^G z3bx?D0$!1*_`es=nz%JgLF_9CJ7o(HY$LXZVaO9C6{zYd>=F?Htc8f6?ggWat8Kbx z*~Nnv8hkeAnar4_2Wl+PyLo*IXZON6->&m`kReJSS|=_`klm&vC)mZZ19(uwa&PCz z-UnOYCF22Sb6%+_#hU_>)5BKqe=ER+`hLubgDMV#CkX(+i{SOZiX_xIkjx2*L&y9Z z>5V{DPn~imdDtr1L_K(^C3~Q%rw*Jtgo~W3Il|4^K5PY_zV87Xd>6&plbkv#>W}l_ kMbPOt-bFdmI7*k!Ka?X-u006hH~;_u07*qoM6N<$g7J?GFaQ7m diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_jump01.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_jump01.png deleted file mode 100644 index 3656774211419c07b05ff99d7cb5bf763a9d5040..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 646 zcmV;10(t$3P)5`|Mht)XB4cjy@ssJKcF%RwMms3v20qxmpB}EQ6=P$*4y!4PkQ~C_bhG_O+ zMlvB2+3YzrvIip$v}dq&8UGtaPqAO)ngIatjg4MYrCf}g8QB{}Bq!}+%KNlajS}c> zAFF7K;8_GzJxhvzi^(hkl=HEQC{=``-UADLP|Gk5rbd3Fd=|kpl8bS8U?}*d<~tMt zMUggK3fiZ$!zoIQ0KmQU=sGNRFQswI$j45$b&34eAIucxN3vobQd<}`$jSGQ^xmwB?(GvmAdprH z?OA;JlaVFRy~qK1MTcp1q(qVoeZs7jYb1H9WUchlS#>SWD4Kp%K60>k`~t!2w#>E# g@nP6);mFtZfAKU?>@p5D%>V!Z07*qoM6N<$g7I1>^#A|> diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_jump02.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_jump02.png deleted file mode 100644 index cd2a5ce2599a567ec30a4d21919497f1577bb5c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 666 zcmV;L0%iS)P)NaV_u$dkyGmppUkB`1-{mB^JVk(J0v&LB@#;yA&Z((+T- zOWq_6fwtc_o%v=8@Sp9l^pS{q0AS|ed}xi>^V&p_7+f#{2^bOeMPi_kAk`eg4ij8- zj660Va=u{%k5mNcO?*79P2{n`=>!~)!;o*h?X+jJ6A)1^p119E4wj&6y2lCp+Qc}$`lD(&}$#E4Vai3PI&K0_*vntw3uy+DkNJRY- z@NaFknbir|P>DV2{VKr{$#1}cK(k}!LkjdYM!_!?-! zR5NawLEB=ndYSi;crtatn<4|!ceI~JwC!*|h!u%hC7IrKUJjO8@bSIHSt(DdN!fVe za>1n&s7nPR>TNG#=0mF4^Bw@yKk$eM5&4a83Jw5uRRS1m$7^<>cy`Rga86m87=K=0 zEx3eENB~EsQ^kliO#`KrZ6AQ&LjHbz!EKP`#{hoO4Vd}xB;fZfg1!zzT`Hjdw3~{= zK*E=5w(-}wLQ`bc}|6WIOK}`&uD_pDEz5{+PX9VtMv1tWQ>p*NtB+u?f0h$(1=?Fmnjr^)!SKn#{ zu5Flx^Hg=g^|K3SkCi6IJ&U#%B5)ss?2*6gAER@9uX~pt<^TWy07*qoM6N<$f}2h^ AG5`Po diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_run01.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_run01.png deleted file mode 100644 index fe7688d48f8e571269f54d596995cf8a3bdaef3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 586 zcmV-Q0=4~#P)qh8>?y(uGxGGzHKoQkzPdl57Di>>kdSuFVmKb8-4&Eq8y9U*Bu!7 z@d#Nqtd(g?k-qOH2bk=ok&Da?RpBI#!s~s$O^4CQ1T52bW@HZaEdpK-=9E@PHus zCNS`F^blzks+tsD$Ed#&Jp@}ca{_(Mph+10UPMz3Ya+zOwWxlfRTz1AG+ot5i3Ub~5aA$?c5(LU*t+*3s(}?whr}WI zRzx#sE+;5{j%#`T5IuHZEJ0P1Xa>)S-EtS#Qk+Tv>T3lsnV)@%i1ZsjTjpynFD4y$ zDX3_x$j?`(e+|);+003qwz#Spqg5Sr>Qw1eCz`EhVC2s*4P4bM9$T|nJZ7;^m{K45p-&V=DAew=Q^#1pO_76z*hGd|(zr&yYKR5tzKb|j|{dWPt YKN&9~4w5@GHvj+t07*qoM6N<$f;M9ULjV8( diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_run02.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_run02.png deleted file mode 100644 index 6e3a97a1f1abf006716a4e1ab9f19aad7d864bc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 634 zcmV-=0)_pFP)#D18dcJw2F!e@O^Q}qOa9Xudq%Sd3j#?~q7uxXbCNw+)S#~l_rELU zsrFlp5deT`dgrw$CF^)WvNsA>9A80__xVZfN`or<;!97G@siD+6g{9C+iX00Zd zD#5$nza&^A`HgWP((IV|kO5PRr{LEP-xgL+vJm)%2kc4q$=*&85ly0$tYcgYgJHnx z>1EzW63Emy-d6>XePQ^2!Z6tTK~QP4nKg zZYT8T7^JB!PeGC8LXyKz_+kV@ocI#aP*V`Ru9035`cLMd-DWjwihCDAH?v^*Y%&{2e12G U5qbh03;+NC07*qoM6N<$f@3QhqW}N^ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_run03.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_run03.png deleted file mode 100644 index 3837c36fb8e355dd05be34174341ff5e10d19c09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 635 zcmV->0)+jEP)q08T(eD-o@w^JjQDBkvn`AS9x7(5{pl0PuQdFs?!0I1p{)cWmTZwZLqP1+pFh zK;JmLy#bfYK334h#DXfFknqUPLofp!X|yxn$9(wzTLFo+#6Py}BES~HhXq0pjBBF# z(5pv84mn`vO2{Wo{=1RI-jpB`NNc=Swg09frSN4+M5`S-Z&>b3bFUs_JQDx_H(I~z z#V!DVcIJb~BVJU%UOm7bcPT}|+$$`0!N@a!@+x}WcEnyNNBlp??tqBaNs|v3fTwm3 zoU{sNuG*P>9zr}q4xD%(5KjbY_?|k+A$dBtw{YNnZi++>Aqo#$-?*qR8Q1tzoa7^fH&@Kvzh)8~>(E!*=R|G}q za32iX!CVb6{RIF_{pD^u3g4zN8rz}s6(&M)6@Ap6%v_Bp4{x$m$XpGWw>44>;oIp& z$os~@-2mPH@4={N$lEKe2kO-PvA2i zJ{LpsoGrIT*6kbF=A(09PKd+?0JpC8-saY&bJ=%Mx|oPoAr|tdA6_}+T6gKC^96&6 Vbg9<~ye9wv002ovPDHLkV1kYQAs7Gv diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_dush01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_dush01.png deleted file mode 100644 index 8020cfdb4c8e260634f33fcb2e056d4b080b320c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 557 zcmV+|0@D47P)NkllnSXfwS7+6?nXjo_%D7bK8U|^w`4#aEAv7|aTbEMOq z$;4}0``*6&Fz{Q?2env zQkG3McbMIAan1v5%SFkO5B4zQ!OqtukUy!LB63iWQ%y+#+wuk|W!V}iBV!F3C#!#B z%?zwSxvcLia>m?CK8s?J`MsTeFS1;-N8=V>0qT!!Es{0J1qFb{3EYdt353SUER{ar zl|ihh28bCe>o{oRB-u&Dkxvy)<%lx^^6TT?2Suot=pH&53?ew;zn6fD_AxB6~^>-0{`&(?2qZ46BV_&Nj;Icxw#Y(@Q;l5e`Yzy=k zLF1OQcTIB2Y_xWI{=QYqyQmU?9wU0^>ER*I_rNjZYXn?xHY^onuVp3bNKj8&JPm#&LCkh+4IDE%Nz0V| vCU>CFL5F5xeG6U+p3UHvX^wqNC>P@|p>{mXo5;b`00000NkvXXu0mjf`WybR diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_dush02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_dush02.png deleted file mode 100644 index 5f9333dccd2c45c6244e8d314aa9e21a9b8d6017..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 535 zcmV+y0_gpTP)#q0TEfKY+q$ymS#pYfB z0Ae=~yBT>FL(|*!QHS4#GWYS^?6B7z^>C?WRomURgj%(_bd*j?PO1|dLHK@nuC;504S}(t{Dvo zrPUBB|9zGR{djr>eT%)-eWj(F`d;2j;5o14QX_Yz1vJXy4gPUAaL?z{Im{CM>ca%1S&fcJH) zs>O&vRf`e`AriX@iUxSA4~i-oB;JYSL*x<5gOm>3m`<)4TuHQ)vxm~)kXipYTu0en zAzVZh?jJUbxHopTyJ?G~1gcsvGjP*|1NRhU&QIt8va^VH1cBz3{5yd^bWV!mY|I)J zQ)_Y+b2+8_krd5-3A;78H4EJRTHxoOSCq>=n*DO}A8E3R9+hCX2JgpSvuDQaze6BG-!=NWB06002ovPDHLkV1kR6=&Aq! diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_dush03.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_dush03.png deleted file mode 100644 index 8a7510e3ddc02aa3008055db8db3025768e25db8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 570 zcmV-A0>%A_P)m`^5Pf@fRl(g!KA~kxBr@(S5*ZnJ5{ZnAL?SP4<;syQTdrK0xQXHf3^Obis=Lqm`&lDmWVp}y-^iLWM@LkQt?Y)T?i^R0^DZ0S99|KjgD7+Qfw#Xq?2PrN1l=*Sb;7X!fdD9{nTttBTX$L)w zi@V$ta0PT1MQN5nHx&@kaI7-?yakHdLP`th88}W&bvqI`C+eJ%!IKJ%2nsMvmw{_Q zavwNEof8@wl{KItMv^>DjR>Bn+KTwgfqAs2QQw5Ix@{hIVWtk9=xV-TMgv)pZ4xx< zr2|WU>mbYSvL<_z!2Mk|N`Tr`@ls?de8>k>=>3O&lzj>O1=+1D`^m^Fvj6}907*qo IM6N<$f-h6_oB#j- diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_jump01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_jump01.png deleted file mode 100644 index df038483276ca90f2661d0e49b1c3a2646084243..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 561 zcmV-10?z%3P)aGFe1Hqdaq?HI*v%~`;^3rqM2Ezu8)#Tr>iQoWQ@*=xMTkJjSz762|2syVf zM38fv2sv@H7}-NP7UeY6Q6!{nV-@zmxPj1EjawDgbGBH?rIfV?PDC54#dOUAq~{pG zsqoYzV7aP`TO{@bu8&EoPKDRRvmOA;UIchBu8*4=V1y8ncg}hD{PJ=9{2Jz7^G;g7 zR?Vy8LButPpxk;+YcU3J*VxpgQt*S4!eZvtcCnbI5!N{H&A=WQhF-a^8jCK(D;JBh z2hA(b&l+Ag3Il0zEw$}gv%9ti_F}*{lL9M6{V8zbey@c_=y^i*R!m1>0An$VKKSUCoRRlE4-Lol9vk&S`8_F?T*<6Im%8`qwoecO&jHU?io0id%AyJt)wbXLPv`SZIJr1$9oQjfjY zDQIUkn@JDVfxnJa5m2t`YOB~v)l|{Lh2}k1)u(DT1K{>hBw3^>5nb(`F}1X4bSa?N zBVbMF?R-oTy=crr`E zM$x_roVbVTV5Pu{past)J3w9{ku(an@!f%Df&sJ{;FW@uX$jF=#Bp zT&foTZ~$P}M39r{>bzyf09lX~2?nJg9a!G)F7fhoS(8->e0vy_0ClV2<;cF(i0JN5 bm~XL95n?jz6U=oy00000NkvXXu0mjfe=GY; diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_look.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_look.png deleted file mode 100644 index 747bbb8e92dbe39cee63bc77fc486f3d8ecbe5d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 384 zcmV-`0e}99P)~?EB zCwFZR*gj+o062HyFLx?h!hC{RFxVbAcTf%A?_ub~>=?8nPq2C8A0 zqgvQ8k}X6_G|$Ey6)GZBL;!$6)8a<|A|RrXznWlM5x3X|UoOdJiL3|)P0-(rK~wO* z840#0N$Fv8BWK1v9v56hU~}=Ml1b~pV>R<^K7r~zJRd2+CC8MA#`Ypm4Zxn`b!xtU zn|1`ZuLA`z9Er;|awaiRSVeVH$ZJI&*C9+(NV>WY#w25zhQ=LIB(o|C50=1uSDV^*a|%fU*9Q(xj%;c`RlpA5 z2GD!7u_&U$&KXxkP&8D|8YCkCFp#Q;ssl;IO3^Wm>83PCMX4fIK?Ft$_26d(5gxL^k@X&%Hdn zaR;p@N1{lkXpN^aWJg8Lz^`UeNMx=G&tCeb5A8$J1qK%Uw#1AtMOLN&P#nUK%DQp! Y4QnCn18!K8EdT%j07*qoM6N<$f};P`K>z>% diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_run02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_run02.png deleted file mode 100644 index 0aa87c2be74b8049d0422e98e4c6f1dddc92c13e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 539 zcmV+$0_6RPP)qg_%_jVC@a1ja5nE7d ziv-V@J(g8eb0BYhI6Kw27l+QqvnOcZ*SV3UB9H}Cp+||2AMXpp+MNc=H_gqz9HoF-Bx2Gb>s!WOKZjX$qrNy92 z0mYsH^Exlg%~o=xSVcYrA_BN8@G;KkM#1d~z$xDU0%QcZ$9-3s`UHwS2XO9X2EWmw zv6#6!E;h^5n{*C58Q3Gk(4+QML(|E~*`Zf$H0d67{-)mD0GuiX$`du4RxhV#X zO_CV?LhgA$-_mAn$!A2lM{ d^%v&fv@epcGVIm*zAOL$002ovPDHLkV1jY8@>>7^ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_run03.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_run03.png deleted file mode 100644 index ffbe42ad5e0fbe7a86c8aa52f890320a48b3ce6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 549 zcmV+=0^0qFP)Y+@l|F^ zVWw(*EbcHZ`VQR4YRMKiwkVU1Jqo-NW2t}7P!1M006HVfdE^*6=nurHG+Hb@ZwRu3kojcxr}!&-XyrHSz4MA zV6NEhIRmYo-tVG0aH@DDzPS(PM3z;hRPc#bN$whU03x`!!)ix=Yt!*AB6;_o2qH2p zo*Pq~Y15&(5W%54a3h)LnR(drM@Yxu24S9sZPUv^iaTWB1rZ9GY|3mmnVEsAKGYc0 zbM=}NC%qA)?`|U)Lqvd=i-X=2%$yCs+G#fMZYEeFOiS`{QgCv$K{+XiCXDd0rV>T;RPK4?Lq#VB*K n=A3*D+{|FE)KZ@cvM{~?4qr;9nhbHx00000NkvXXu0mjf>hk{2 diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_spin01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_spin01.png deleted file mode 100644 index 16bf83dd7650fe66136ecb6b226ed4a40b1d7228..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 520 zcmV+j0{8uiP)iS4P*5;1u#d2?aN)ti!h?l{g?WU+Ktn;nh4%<%F7VHK6UXuGv3?bD zqVbF*%SwPB`}TI|1rm`jpH;PM?=C12i^$D;X6_PdI*G`O2;e^J0P_T9Zi;L=h{%im z16(HXNvar`7&xB5!^@_~uT};vasa`1nVJ+;RU+~-aVDP!s%{>w0tlJ#Nvc__G9V&v z&kQ#slN&Y|t-`RmStnsP1NO|TPOAz4fT3%|hD|m4rLWvm+A1X?@?}YCBu+KIbK>%P zw7M$O!%{$14pgn4BU1K{0RpSE))zb>?ar&-X|O^>?agHc<{l(gMos9g@uJ?F7P>Z9NCw?nMXR^ z8P6oLto6#465tm<9yeQ@NJh&(P1Du&R~<-39iPoP8IGGsy1~Q8-!wv4j*GpeE~Cj# zGOFsAA-paf+{64RB26WCT?uv2t#buTR+3Rq@@3e2d-dCCeiW~!ij@MAaXbjbr}Qj{ z3}nCYAQ)Z{Ym!@Qks~o4ionHAE!%hyIMUpJc?Myc008HK5U)vvK_Xsp9=KRjqj?5- zXF&t*M|<&kAe=Z1FDXr%&LBIIj6nmOIGkb2cj6E zfOS1|D;YpDcuA6B-K6dZCkK~H3FOq&?~gk+i_JcR727_I)_VtC1;F+ceOtkIDzoRi v>9^%yg996y9RNJhPSew5xA7AH{mg}b0ZW((ZNq{)@?TflykfI3m`3l`K^!Zw{ zeILLvC*N~+h%FFNuiM#~Qo!S}99u(R=BN(f?yXKb$Vqh*fn%N?0C3jfWNa`^CAJ_-YC^@8`TEY2WJ?-t zvlz<1q{&$a066R0w3dS;uZmbCz}$E_NN_R~#rr2uSA>7xiIQTFO{0mD95YVF;^Q`Z z^Bt2{@047FGIh*TzC))CL3HHe3(ItLE>7hE<|gyu{*9ZA02q`a1!d*~%(5Q#(x7wQ z;s7ZN$~2}3QP#21QOLU3$tFob0N~gR{n5ED3dw%a*2c_-8W_?xrc$@@{ks7Ik+I)1 zQf%w(eG0IFjY4?4G1fmF#h=V9GZ#3gNV2TRnWFAYW@IV4 zz1w@(z`q3%O++-M>#g`G9lHJqBAQ%(gf@(KNU{WCG{iT3y+u-tK%XwqhM`ZFEjH^2 zM&{ykEt+(Lh$d~G#*_l?_jv6rfteR~bJ(pND79P@JvIyjmZln#h(Nq|mf#+H5{*&- z^GqTU)u5PUV3xyKii|3S>WW%O42=Zsqs*%HN32XaQI1? zh6o9AQM?=#z^u2k1lJ!i810Ke8;*uz4lfCqxw)Qa<0G?AcMHd^HGz=Gx&tQt7*~e_aJ%L2RJ~W%ASEnG&b_az za!^A0wo9=|q7XzhNoNC3?^C=j(b}l4{hawT`~u?$1qe&#SR4QV002ovPDHLkV1ihx Bq=8zCgq6t3$Q}d7iqe?^DfF#b!&iYO|0hiX zz=aDJF8n{LqeCK^qs`1!=~;@eBXVt6Bus5>c=cQ?utnPkA|jgGp3ego+)9W6fC%UY z-o5w!4+7hgy_boUTE|Ge$X1{YL#aUgbgk&~jUk8_hO;F4?0OKJo08YM#(iYex?b5& z*TVZyNv_%1Ca2^PH4$q1WSWZPP&7r2br_At#Egj$FVBF++&l9Ld}G-jV}XJg%a57MTm zRTK#@bLA|(MN-5)ND;|%QHUU%~?+{ zGOxxpIB17NG}-Q*%oyPL%%8m@Fthr9itn|T#>}6IUI!fmj;1Y=8G+p1JA!|mNj1g* z%z;EI+QMd&fgp!>lqEF=RgO|6@{aKMp%}LV$8&W18a=B(G;M5yc3?EN$;g0ud1eGP z>%cqOMG_?{CwFAV(Am|Wy`x|N7cQkaD(k^)u>6R22t*H1>_v$RV3 diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_run02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_run02.png deleted file mode 100644 index 0805c77bad51947eb696dc2c9b23df7cf765196b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 510 zcmVg}b0ZW((ZNq{)@?TflykfI3m`3l`K^!Zw{ zeILLvC*N~+h%FFNuiM#~Qo!S}99u(R=BN(f?yXKb$Vqh*fn%N?0C3jfWNa`^CAJ_-YC^@8`TEY2WJ?-t zvlz<1q{&$a066R0w3dS;uZmbCz}$E_NN_R~#rr2uSA>7xiIQTFO{0mD95YVF;^Q`Z z^Bt2{@047FGIh*TzC))CL3HHe3(ItLE>7hE<|gyu{*9ZA02q`a1!d*~%(5Q#(x7wQ z;s7ZN$~2}3QP#21QOLU3$tFob0N~gR{n5ED3dw%a*2c_-8W_?xrc$@@{ks7Ik+I)1 zQf%w(eG0IFjY4?4G1fmF#h=V9^7-9}_UxjYMk zl5yZHRgrj!(#{>3F&MphxujI12u5cA0Kn)*9IVUA6gsv=(4+@*$(8+XU|+J%QnX%+ zB-+e*BQlL{1OS}%&3_9!e2rvvROBR)h%Pq<^D@ynl58I^bJGD?ZByf+R5|PpD=_#RYfq9BqP_9)bIPdA_Jjw;&}fNlyRQ z#W~nQ`nL;FEl~&}nxwOVr}rUVl4xyIhk5!1=raWf>pzY^00000NkvXXu0mjfL?_~pvnan~)A!_W1S3!3f0J;-`8FlV{;>&y%1-sNi(9nB6 z^)5@Y*eW_>*G1Rqhhdt_q)9-Poye-y=;JO-*EfRYzBCCaT|DS@I#sW4bgxxRZgvo_ z)A6pdQ@_rt)%R|24S`s>rpG>HRdzz$-{`#_`>dOTOlHy0cwGPFH|kssf8|aMO!Kr2WVef~GeU4B`|^_e#+4^Ewcbwh$wA zj@2uq+i*P|0eC+g`gI`@P}n!Ks8%HiL68-N3L91iClTIa%5yAa^=c#RIpGeGqYUZ>pOFw z8Q%QlOY$WF_>X^$r#+X5X!yFPs=MC)-ea8e9Ccm8IR_D$j)pxX(F-B3CsaiN5rOpm z!$^V=5YZsrYxEJ9r#&4D)8* zn{QypjvYI8?0{-3Kq)n*?nPwtUx4qe#QJc^g^VRc$w#Hs=n`wKF~(r61z;EkA2&Ro zY>|AevAg7eKeg7M_k4GGg0OnPSMJUfay}&qLO40X(4cIGPRTCe<_0EZQ7q}e`kZ~>O`&XqN0_%whCZ<+5H1Meu->ssuDQ>0000< KMNUMnLSTYyVxO=8 diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_run01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_run01.png deleted file mode 100644 index d8056c34247bf72aacaa28fc6cebc6eb1b588c3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 557 zcmV+|0@D47P)NklTz1p^lrn&p6FPulfIZVnu| zcc#-ivGqunY!&#gf33G8lZa@2eWt31)_y-?+|)JNwgodoMEtAu2qZZn<~0CZ&u0LD zh(MO57)dk&B3fnYq6^IB?!N0fXEzTBTE;JrWa@xrgz1;yOfHHvU>RY0`)quu9JFpB zgM+`$c~vn7zfKgMHLL;T4g24zD88itenDj_G_0sh6=0^fkg0=AT`_`Wl5>k>>O!*j z(HsI0(JJ>#7+Y0vYqDPE6pg^DK99wsVSUQSQAHC&L~AU$fJ=JAx+~Xof|CFb*^m7; zS+P&h(n-#~e2v7*8y|-OZQo}>*^{iQZw-*GC?H%L(G#6K2Y`!!TLb^`nl29j!0G+n z0Zh8ek^pbKB0@!On^ILIBDkK<(esmy0B|L1RYP(CfXvU*vOK_havB9z>{I1Tmj_fn zqfx|^OtOf8=NZjbC0>hq<3GYYV@$F?6t`#`2G_l|eeQ+O%XaDQlAG#oNq{j1%nShF zdB**A1K?Vwsq5e|&vVwnC}8giGY8Nfo?Ek5_NqA2cT0`GcvX}De{~n_LHtqSv28bR vC3#aS0*Y0XGJaIWe|7+Hy6#)zpEaG|u^$U2eOqT#00000NkvXXu0mjfukHG4 diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_run02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_run02.png deleted file mode 100644 index 9b244b225749f2a2fe40aeaf0ed0ea5b1c1abbc9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 591 zcmV-V0Nn1(%)q;V_ifT}1UB9OjM zW)jVShz9B2qU2njQ?X5mGEW5kWo^vsaqTbQ#si4!k=9`5U=FdDiAe1BnSfmlJ`An) zk4_&%*=`0uQUm~yafYMQ#TrPt@erFqy0>UU>=L9PHUsh+T%I8|+ayVM(YYKQoe5qQ zuI6tCkrdghem=`s>!VX_=@P@HF!NM{vmoqz|LAm4rY)hrJ;|X=^NfIa7wukFd###s z9wi8otLjUv{krl#J(u0#`8gn>!PSfpLlcqMY~ipg?;w5!@hbqpvPS11+C}qeRk@+p z8-N3)xDi;s#p2v=(}0V$ zsGA0^W@I#q?p5K%vdtPiUQ^$@Z$e`4v1)INnx0AI02^esET57W=`DFNYLtb2GJ5Z5g3eO&OY*B=7~^5 z(CQ}%(UV>D71H-fyJ1EPL96SD-y~?2rl^2beWwoNJl_p&MG&rsRtZqQs%wnwuY!n% d)IZd}jo&hOEy8iXJo5km002ovPDHLkV1l*v6}SKZ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_run03.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_run03.png deleted file mode 100644 index a211901586d74498a2d040b8e45f5a380450eea6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 610 zcmV-o0-gPdP)2oKAk*?3`5qF zqz6PaO7|I^H03hwv6S@9+O^8hmzBDby z4#qx+lsD}9F)w~(J$M*YtbFSTxxPbM1?fJs9wZ4;8RQ_X0*VS;UZ#EmQ4NS_luPNq z@7BUpoa1|sz!~*@nL=rrA1JkGU{~9yz>C5E8}0 z5*oj@EvmYXlDu@ArZTE4Zz#KcmSi(cT`R3Z%S$bYKhTvo0DwLoqjr*MYAS}l!W-{9 zK`pdMSFQ!pexSoQm>D7hrIt03^uP((mA8d)MFd5YCSRJeRh;y|TDv=7I{g&m{Y?C* z4|f0n80`0A%_f0^h`{tcfGNhzyePDTu}`|ZMGzxo4m2nV)H2Vz!-U8d+WB;X)_OKz z47AoT#&|+t`faAtG?_)A(a-|`p2THN4$-@)FBSGjK%ehVKX@(*A0`oj41?brtK_H$ zMg$rR^GiXWe}ju6d_6o>bwd`7jo-3Q1lHPJ0~#AlBz>uH?B=J*nxrokOeRXnI&dTt w=F<_;*pP*Ogvp4YvGjMhsS`Wg{O%L-52+qKe;~*@S^xk507*qoM6N<$f;JHfzyJUM diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_spin01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_spin01.png deleted file mode 100644 index a70981aa4975fa8ffabadf21cd4e303b4a977a62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 535 zcmV+y0_gpTP)7zP#w3JNYX>?imW3KY&M$_dD%|M| zlE@GMo>S3t=YsQ!E7Vp|akb(vhJv09r)WFh0c@$_>LFPk;3wr}X>7}=h)@y1wzoSG zbP{DMW&i-@EO6rBROd$=%vtuBrn|^TS6jV{c6;XEV_j6d$^Ij6BepHWi9>Dmv75Zj zBu2T&ivUD44b2yt;gE{~UguZ83L=_%R*VV&hu~;Q9(dF%dcL9qMgOeHn_Qb_X5?0T z;#UBG$~CK(1t6ly_dEdDc5c&AZ%$|afP20B`4G*(i36Yu9_WOC5ac1LSIhvIyW@ge z&;Lv>*w0S@fO&fZ05J1PZx?{7J`vI64bFWZHT1H&>sGc~oot&fXR$G6CAZcBm}hft zY!-*Uh=7@c!>z1DyM}%?fWpu(XOP$^_EK?iBBJS?6lSv*SrNP|6_>AK21|#J&_7=t ZzX1xr5#Xv-LEZoW002ovPDHLkV1hil?#BQC diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_spin02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_spin02.png deleted file mode 100644 index 3f94dea7e2e3e0b3b681b1525ad53f69f275c3c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 473 zcmV;~0Ve*5P)0p?>zt@gwSoI znIa`(=AAetZ=q!LRRY1jZT2Jg@(4Z7B*ng82%$omT^!p@V@REJ#+ zW{Q{@B4U#4lXM&977h`_7*A;xwLH}|x;NKXZ!`e7UM>J|ILuuBts5<^5+ziqoSmyi z*nR2eB&obMDwh6Qk`HIN#>rcl|SnuljXl6AK P00000NkvXXu0mjfJ)zFO diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_dush01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_dush01.png deleted file mode 100644 index 3f1f03666365588ec83f66a28026755c2b857df8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 409 zcmV;K0cQS*P)(eX;FYpMj#=f2c0KzNH7-SRxP@9_Cn6_eP*qxu# z;)tB`Q_vc_nEus+Ayb!oSKS^s4S@zDa#3jfKq-lUR4bH{Yn)N3l4?%i`vBCjk0(M9 zXK%e21h@#4l8eZj`T3pzJ4{gk;G13BtJxE%>L!Aw8%rGjwBxM^7m_E$%Ev}F6w%~G z12D$$o!Y!_Mbm3By#V05t zAug375&+^-pJo#VpHt%H!4Qa0>s_pbzyw68ZC?)tfGDLIGZ{4isBKMc?PJHCU_U;` z;u3X~2lEi_%Jiom_(Z$9_v!bb!)N3zk-hAwRneqCxe)`IRwy^pB&Sj((-ISg0MtoH zAc9HqK`*=pRc;AP0JOj1yZG^X0=}`%0T9}6V0a?0fvRp|XbPTXNs`M;&RK+`J_SP{ zO0BmCE0GBGM4{a|FKEdJ>MEh=Phchw7FV9D{eevYKBmm`Lx3kjt#`8=yuc$o8~b_)00_@CV~|k*Ky7MjW7>+H zVRwE`FGu8*AA;7{#q_Hl44JyzyXyA9X$UkJk&8m(8%jw8q*|eroa2m2l~i*A-v^+M zeLN9@ID6{FAizbSl$=DK%#YUu*kFnR0N-rdUd^6BRaX%--B{xIs~xXJIFZ~TRz5be zp@=3g8h|l|&(!99*$dI}0zjn-MfOnGkk2F_Pdxy_ea(8CSku1lkhcJdC}Ch5zRhaz zcMnwqBx0e74*xP{?}0Pun}|qr-Psf7#=Hk`MsD0Un40@DeFG(X$FNfq{|Ep8002ov JPDHLkV1lMuw(0-? diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_jump01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_jump01.png deleted file mode 100644 index ece51b76a851f69511e6675948933e3652c8b915..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 452 zcmV;#0XzPQP)7MIe!p$d)6KE0K{aS4KvjMD9;OB9SMNkt0X8T)7fCu1{ecI?=(W5>VaX&w*}>z`HiX&#&B?%{b(?%x~$F+>hv03oITgv$lx3cy*! z9ub)w5E1*x1%QKBYV8(38j0L>ve=?pJ5G`gv5#Chcx5a;h}{u*UQ75lMEi;1IxK-T~_ig%di)!u2PWdGEUGhzch>-dt*|R=6SK0T7!ZbZbISFH2SM8sl@ zR#d@?Dp-usM8s;a8cam&dV00kmKNJ`)yc1=V0#YuI>9+dr%I>F;rAcXqMTF>5LLfM zc>#+pDn-eiqi7;z$4NdFp=-jJ;(Fk`BE|e7ybgV0qe*EfwzbN00007MIe!p$d)6KE0K{aS4KvjMD9;OB9SMNkt0X8T)7fCu1{ecI?=(W5>VaX&w*}>z`HiX&#&B?%{b(?%x~$F+>hv03oITgv$lx3cy*! z9ub)w5E1*x1%QKBYV8(38j0L>ve=?pJ5G`gv5#Chcx5a;h}{u*UQ75lMEi;1IxK-T~_ig%di)!u2PWdGEUGhzch>-dt*|R=6SK0T7!ZbZbISFH2SM8sl@ zR#d@?Dp-usM8s;a8cam&dV00kmKNJ`)yc1=V0#YuI>9+dr%I>F;rAcXqMTF>5LLfM zc>#+pDn-eiqi7;z$4NdFp=-jJ;(Fk`BE|e7ybgV0qe*EfwzbN0000Hhf&~lUV-86~9}PmauHexW8A|M7Xeiu@A*XkF{~mX`eF zx;T4!UY88=fgi)DMb})&Y}OTfMJOI@idi&yi-OlFY-)Y~7L8VZK}sY#gD~jex&5v< z>wUo4$qM&V0MM#N2Mq9;>Zo-st?D3Wjp-g+$5S00a`Xg!5l}D%J1XFL3R!_ECX2(I z8JL2V2-oA5?b)B`vJ*D@3wKp$Dj+?B>A%PnqaD&z@7A-{G9Ll7niN1%;&@At^fc407*qoM6N<$f&&w%>Hq)$ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_run01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_run01.png deleted file mode 100644 index 03da3c8c3c7688f7f993039f0b5e32d2deea1961..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 350 zcmV-k0iphhP)SXf8Zr&lZb@h=ACn!=aU@ZBid;xG4*)&;Q6p_%narI!X zN!ASjdM#AO(Q5&qGVWoPFnCRgn_5wb;N4x!K%ols)lS1&0idshHIr2X0B=3s+BxDV zxLn^NJ4F#wt64av>FGC!CNb|za>TO{kzgo83qdzskoQwCl)+GjwI2DixPR00e99#G z$dRVG05G~oX?BCj3NXt6pg$i`YN{f+R0vZQRmMHn2TMlhP)c$8C;+(i9o6BO6dNnH`hyvn+v9H?*fcQZB2r>!)XiP(6Om|{u z*q@)GNf9{}+o&^kF}>7-F;ka&U;Q39`9OCOxhOQfpj0G4t~E-68^W8ZER+zE`AT+D4S5GTY)kOkrKbAE8?#4?I_GDLxRfvtu z7m@d(0T^TW%x&JMFCp2T0H{@=Jl-74SAiE$i=atya&!;Xd)u*d1EUqjCl{>jNEu=@M!K&^#eob$FSQmOPc@y002ov JPDHLkV1kR$tNj1~ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_run03.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_run03.png deleted file mode 100644 index a70873a2662f255da56fa4f309d801120b4b2110..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 412 zcmV;N0b~A&P)^F1)vlO5}6GU5m%dj@z66OK^a_9 zVXq9AQ~=m3a~JzZ;A4qgof9GvthtdXh?0o3(!=LD0U)h#Ya+7(09IS9*5i#m;nW|3 zpb~kgb21Y9GQHdbFXHPy#WOt!%AhtqhAUOx&>Mp`EijDjN#Y=(Kjey?^J@(*%^N3K z;c9S--DtOM;cQ#Dxst+6ll=t!)tQdX$^u}EStk-{0pb$fv46~m#7Cw(7 zq2CuWh!tk}m$5oUp5T#0L|l6uGE`c>Cg6$u{*hqKH^nci;moV{Lm(ai00001vohPT-87H#dLt$n{w1-@l60}+Yus*?gAB9l&% z54!BmUH~W>O|Btbu9uW}vvUAsE+cap0D0-i%irhC&L#Ua8dXH%*$bJ=uwWF8(gp1B zpMm>3V=i9D0;uY$fOtj*f@4gHCmq~6fomV`nX5k}9CYI0P-3dd?2p>Am3%74@g{e) zb-;r4V!?8PWK{mFwMcT{pIop+8+ZT}B&Dl?j&T30&!WalR8 e)8qQt5&aEO;jb-q2|`c+00000fhdEP)P)nD<&#fFR<5i>B9WDqk&z>jDnEk|p?- zeDf6dJJGQu`y>LuE2mY(Vufgr{PG)bk&ps~=GiKw2*h`C>mILX1V=>QjH_FEwYmb2Cxb4y7wH0UqxxijTjqGel^1y$%&bh0tg|h*}}kvK^H8<9{{eC zV~=}dK8MI}*7A^jHRz(|`)0BO!CBGTuUj3^r9Tl=!qRiM@kg-=o*yg3CMq90pK#~ z#Ahd99)BCnBfB>Hvn$#$MD4@O$YN<-O}IXIUIo$%kXOAc&B)p~EdERpLov-hRBOV_ z$q4|oF~}jIYk|B{A(70S5SrnQWC5~yiD*1vh?1;8 tgMx3GDM?H6BjCYYp8WbET2n;-UP%yCYpkZKOV4$Et!9v3W0fGj7&jqp+ z*|B38m2`+?eP*;@1OM@_k;NZG)B*rAXJd!`{H>OzK6IcR$ zkjZC*ABm_nzh1*e6S<->O)vMoz{`i3jYTg_hok^dXofyWTtO%_2Un$L(?b?{f?>7l zgY1zOnv*hVv=w*1Ttq}|><0f*)8vrC zBHt<4i$cW~a=UeVbsa=Jro>jB7{DaCtKLm$etXJ8Y(!Uh^6k`)iL978D}dmliY*Lm zXmsI)_ypi8Ikvbl=DCafWGoKbw-f59_}-auB$`a+A(%M}K{nk>%F10j?>p1WFGJ_?p;_HxH z5#xGWLstSn#0o#Zukh{9ZxrhruDA6fa$qEj6ccnMU@z`l&lJ)e7Q1Mv1%CmfZZg~r S-WkvU0000AQO?^ic%JP%39ZOFAKO< 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 XrL#p;NIV{h00000NkvXXu0mjfT#G0O diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_run03.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_run03.png deleted file mode 100644 index 23e9a2f3f19c3733a0c3ff2547a1e1d1915a5f70..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 624 zcmV-$0+0QPP)ciD=X$KNdSnpobFd{1w1=b@TTKLhvx4 z6AQNVV{ic2q(A1?w-XBh*tQjFgu*`d zctM1M9&M@$yGlKf44Q~+YG4F_mP!ESgE*Oun2%e0PU|&w^VFuyjD6<;FD*eSm2G~t zk!-^QfN;`jdE^D62DS|_^d12HXfgjq5ej<2b2nv!rb$pKZ*|wZ ztmV=ryfy_4b96AvjL95m(5S%yGA2#Q*Mesbrb_43r-GKi0pJ^`fN;g0Z$JwG0000< KMNUMnLSTZ(zZnex diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_spin01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_spin01.png deleted file mode 100644 index a50a57f942932eb702308d184e0bb30cc9c95dbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 571 zcmV-B0>u4^P) z6j{1Ax<{7bzx}f&B8`YNf1CF+S9IM9*XqI%X;gJJ28pqwD6S}R(5UKa5dna)1{X@o zwQ@n4P#1%9E7Fu}a7Br+2K);hjP0xyfod27F6$zhzldP_6*y~!aAz-)%=x$~H!Aj$ zC;E{JMlF>)pBZp~PZ5;+pk{2zpoladsvY=snQHyS^E#1fGDWJoa?rpZ z?YCdy;cEx#yy6eG1CF(0yxA#=;1Q$_bqC%*%Yj2?ilRlij5Px=0DrI@Sq^*=0X!em zw+KX}IqUo_g?5eXrO1+Gx9M3&yJz&>CvHjPwQVXV*E&7z?Pdc&xZP&+W(nd=jx+BM z?X)O&c;L4Cr(l{leG`%$w3kPFKBJ{qWb>YO0PS>Kk0Zd+xKf@ItD64I=~dkoGp5jmjLB# z(W-c6Z||Z=auj82?I7+$;Hc?mvTutdih%Ff(+(gn)w90`_zlUAB?G&oAN&9S002ov JPDHLkV1gGZ1v>x$ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_spin02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_spin02.png deleted file mode 100644 index f105db35e41ec82d984349ffeac3efefa910a700..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 579 zcmV-J0=)f+P)OcAZbb3zp?*!%t_NggVq@H5p#AnO#^Kx@hZG)33Z5@;vfwa<^wN@@E?Dfs1VD1{AIA{KyHFDdg8N@?AY} z8%f%s8=x|U2jomtrZ99vlsuY2n29UFJd1EWMjp)|gd@JpYFunNz9BXl7^6>)+LIH9 z$;mlE`WgTq$a3Bu9-3 z+Lz_^seS3zAojpb&TvG$f+mO0ZqIz$t_oC4ykkuQ=?UnY@s^_j`UXf(evBAAQ85t( zR!=ejPyRh`#<7rJJ8$#Z2;JmlG_6BtThF)30L) zA9-1lGQ-pYMFTF0MN5V&-S1!WmyKf z%RSrh%=pBesHh})P{Ztu_7Sf)m5V+RjRR~ztU*H+SR4mba+8TOB@Ez)XWl% zu#i^;J1X>S(VWljEE<5`JXo@d_VL|2cisCV48I%6kQ?!6yyk6KnE7-7VCGW`gb?*? zVPJthLwo{oog6EkjoEzn$5|9hmqf7RIv)HS+AH=xfI^bZgUL(pF8#heq`%zP52pb* zcW&J|E-EOQq_t!%)=~0w9}v+ZuWIY?5fVuR%k1{kQ0#-Gg18TCG>>d*_MbZtT*ah( z9mX{gEn_F3djUJD;MlX>>;YgJpopLQA82D>M{GbsD58YcShk83F)od(@9Qvc;vBOb zI{|GB6sP~o**;L9MZq`Cn&>@v$$3`6o@tJKOep8$3$;8(FS7eg2mk;807*qoM6N<$ Eg8i2K(*OVf diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_dush02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_dush02.png deleted file mode 100644 index 87bd13148cc5b6b5ee62c2eb0c88469f5ed402e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 547 zcmV+;0^I$HP)rNsYuf2Y`w- zD%Kawd=`u#bhu(|pBV?Ni3XW?7g8d7Wwy^lAY2MhV;jT`S3g1}Ha>uXBCAs*^Qt}c zu{=?que*)=4KQ7_Bm70aT8lDruI#jvlB@fGh?;Cxo;lQxbhyvhZ?5|uSSQ;OKsmG} zYC!-~XdU^W#4JfFVta<9;x^{IB*yMIfvg0c#2C*HW88mzqL>`9J5Hj%F40?5GC@`X ldfU!ruaRQ*;}pdhd;>en5g5;$d~W~%002ovPDHLkV1hAM@D~68 diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_dush03.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_dush03.png deleted file mode 100644 index a9052c2c678ccbd6d94eca474ce5ebc17819b41d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 565 zcmV-50?Pe~P)!q3g=gbj&epr_JEoO1x^*`j9~F-uuC zg52Srt$${mu_Ib!l3XZ>?48*^FM&L#UNZB_fgBr30<;OBlx1_^BC2GCR`bP^uLA{s*(T%=|Y zkd=j4E7*fV&la+-r?sE~I&)yjTXd`Mt@EjRUwrf1knp*wE`=xG_9HW|A^>JywSbqX zXA1)h+(w8qfT@y0#)C0e`|kFlkh&m(J z19dlzNGCb!MMN|nx(d3lAmv2e1{E!cATA8sB(id4W#r1r%F4`APMNLTG_TyKva0N)uBHc40GY>wt0zg*j-3uojJm^Ipdh+Yu<`OwTDF)>efW0cy98EC_k*aPo zh!=u4GXsYAUY89C7gS&pimUG!qPOmE^FN;a#MYG?UC$SRGY3g`E*+Q~~LTkY(KG^=#6T<;3(Rbv9D z#8Z!8y{i{Pbkc@W%&1hS#E;1DdI0NQ1o41U%&N%ZLCA$~4Zf}^#YA$Ora(WR9*vB< zWjZAfqrv2kQVcd}`X@~h{;ZCS)f{-5Xraj+&Bcux%N{-ZL3{}_fnZ4p2^iA1GxedM z+F6nzFRk`{kg-0K7ClJc&H(%^dqR6462rK;p3Z@YbeAHbIwjP@hzKybV|(oAV5aK2iZ&cz-Ig#STK1+<;Tby4gmAtK$Q5C4zi3yk9~=LW$N QrvLx|07*qoM6N<$g4{g^xc~qF diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_jump02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_jump02.png deleted file mode 100644 index c5d67e90d84654ddebfdd2d38c7fe63bb3c717c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 559 zcmV+~0?_@5P)Q-=Te=W*EDAR^6htg4&e>?R1oeI0jmCaPW{`nk0s1oveLKmjP9e26rA zNESDIl_sFjX9X?EW+k(WOTh!)RlDC?B&!lVQkk94W{8&XV{S5J6q$(3>F6Dqs3ZQ6 z_Pti|EWs;qXS2G#cn1D*?8fy#irUv?t{(@2^y+8NV(|>V1_j8R;?9N#LguuSDy;9S zV5nyZFw8h)Jq(&TUA##z`5?$KtO(X!z1b?}uIjL&myH%OSJlIon*mM|uabY7@dTdKPq8OvNf?@#h2j0jZqkU;~jwk=|kw;ScQzH!rixMg7Vg9&vCMx8iU1;$W{rgN>@6J^%ZV? xmdTWClOVJQs{&e2bX_F-PKZde^(Wz5@dw{G8K0S diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_look.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_look.png deleted file mode 100644 index 948776e278aaf071ca5a50f48896a268c6a11820..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 385 zcmV-{0e=38P)w8faHjxp86d9X7KjN zT}0;~CjmBS(O9)ELLHU#T<0HC@P`Os?%P8}4Ve4x&rbULDIMkOq@)bgEyLq`vX$1W f{YL!n5CHrD@eieCJ*?h?00000NkvXXu0mjfew?Z5 diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_run01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_run01.png deleted file mode 100644 index 1d87f234483f43f541dfff4b3320bf32a15f420e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 477 zcmV<30V4j1P)IRga(L6>$X)jI44>_L|W5q0Wehsz+SeigdSoBS#p3vYYMF=EAwGC z3g6_TYS+&b$|q-$;7TT)F_BpzASg1~qVeF5Oa_l7jYaT~%R@?bF_ecCfW|cBrB25B zR`KW~j)~5SW*6ffGZ7Gx*1W&=?M5{i{Sszv+Nk!ACC)B}Bu)Bru}X8+{Cd)pTsvLhHWmp24b80bq>brBHGwt1YQT zOwkK&(eCZaZQl{)#~Wu(PUkCB*Y$}eBJK9MBiepPSvz-t`yCRPb&;xW>ielD8xT68 zF<9K9G4W3jOtcWeuWFGiR2B*Ky=#}I%jlDaFa6+ei;p}OL7M_(d-qaVS1!H)TovuG Tf-|F800000NkvXXu0mjfNDJ5J diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_run02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_run02.png deleted file mode 100644 index 5c5e1833e7b402a78b2a9e8f6b67bca67e7ec7c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 561 zcmV-10?z%3P)PNk}< z)F)ZadYpMPZ}*K1|M9o6+wzi#wBxm^Zu;LQf{@%dY5&Y5^=6{)TdD}jeN_Qa0?H>J zBJDGh)!!(2=Zm5)H(X2fVBHzt>2fN_5X<@p-X8w1jW5K&U7&35Cv)^U{J6}Xd$##Z;hpAY@I8A;LjhQbZ&K#*VjY%f;#;A>KV!YS@#xFZx!d#OTv zQwL)`LxOR|q3Utc!s+5kdf5j-j&Vm2clD-Gv2;|&9ldO{P`Rq^p1B#|C~1^rc1%U2 zbtl6;m&u?TCoHwbWEm#QfX-|$`yfc*n-pJFD4zhNd&^N$Oj37OmLdFzk}Mw6uz{u4 zcL9;@B2VRS}RI!4Cx;7V?(;PlXvRd zu(r|>Sl!q`fb*XE_DHDOU!jw9Zy`Hibui)PQ#3<$9jfOr3+KjQ@e{ID0;|%u4pV)F z>riDjCwoc|#)DM>ZDzVg$^Ilnq927JREG!%>JlH?spkd*`!oa|TfrEyEf`JDO%`&jqXQx)07Vb?X z&P($BKG~82{EvS=UiQ31M00*GBD=oZ1t6lCL!0h-n(#{PZqT>APNFyf^y2X*a+6st zR0rC}z4){ieBwg1nMw7*9%pYNX}txxo5AmnZk`>S6p*I?ImDd}x+}w-4FDi;NRT{g z5cQDvY*U0sa4o)T;{1LB0EA^5?zz`O%^-**@xTFr1G+1tF-z|EfJTN4G= zj@{W5q6a6>SJ7HHcO1bF_rV&Zm1=vm@T1*?JT>e9B=KJSW=G#G(|G037TxPdi#P-c zKU8z-sEExK#fQn^LR8dzcV%#B!PBH>p_l1$kiGaqydgqEUu9|Gr5Fb!3JI_z@M^*TZlOc4hvRYXMdcxvGKCUvP7NkVBIfztp>`v19F zrj8kYL;*=ISCD(5Hku_tw;bqk2*noUZpTvt#|$g=QK9yM1vK|#TVfq{l*Iv`7xZOK+s*4ITF z?Pw;u-d!8`Z~wdXTxo^Q2$WVcKM0?0XP92EIxQ;z04gbq(klD!moZf+K zqaCB)8CjL-`%*we4jtft-?cx!*~ynk+8KfAbtDdVmwee0rNHBxjU=NTBY7GtkpmTj z79Mg+1^}klQS7Nf3j)A?oBq`#U)Yo&0DyogiL4%SO1>mYEfw}6bUBf%008jTc_*p8 zZd(ADEx#OYQy@%o=y}uoZV^0yiP!I+25THvS!6Tt!y}20#nNkvxZMl@-yNS|^sV*j zpWZ#$uZxdqt!KC@0TDTnbm2n#^CNpP#JXuIhPzA5W}BitqnS$6ESA|Y?V~{ixPSav zn=XkOG7{!Qwrp3RmZ)S^t76Zd-bF#=m4c(zvvXrDUM2sUOs%R!f$$lt`Z_C>s^;tN Y3)nUcg}Upv)c^nh07*qoM6N<$g8J&~T>t<8 diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_spin02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_spin02.png deleted file mode 100644 index a56537d12089ef7e3354defcba78d3a238811f12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 500 zcmV?aHs77h*?<`W7F`w4@Afq{mBgMnrqNSrx|?Rb+$rX8tL z?fW?Q;p-Uq$DhX`M~S2j?Q5-f?ROPO+PfZtvI~n{lBCdgUX|rS*7bcBNmM}6cBa@k zbnpo)F+@p%o2jzI5XFs##XqE=||Ongq(1aCrS=sfX=6 zBqtd_+_^{M5SoYN2gf-trs!O0)@$^S=&(iSz(+yOPDASy(is5RCHi&-IZu)t-K=NJ qPn`o9mmL5ohQ9|&@Au)?Uv{6qz5v|-KZ8900000; diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_dush01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_dush01.png deleted file mode 100644 index 0e01db00c2d6aadba9718e9d323d34b1b399b6bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 619 zcmV-x0+juUP)?q)Xt|M9Ppr5i*vF>^*lQ@GE}nY<65A0bc4`--4}sZlQa&@9A_a;T|-2BtGc z^1aPQIS?g#JR7X3LQxigJ)HHldK14(`4YwKqFx&_u zGi+0SO?H4NL?iP$h7sd1qA~-3HDuz&nFMjjwAVwq{Awn*6m=8#NsB7G`_ya$## zXq3bA^$F?Afz3o%7`0gH-8}dcogx(?6W?Nsbl`-M#<-002ovPDHLk FV1lIqP)?0VOxj>OE%i6@71tq=b zsE^yIVLZMAlsp zgq*2yrY4M*6!C?49Kr2pHk!r`I8$SP*u~!5i{kcC zQZvYGH2=Q;Q*OvTYS3^dJB!lQ`Oyg)V^d^REmXCzV}+c*ZpdFncG$UJCFAe?;cxx) j;Rv#~sCk=m=9lmn^yer;Pp^xD00000NkvXXu0mjfxS1Ko diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_dush03.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_dush03.png deleted file mode 100644 index 7c7f807ebab3a5e1de084cd7ad0bc115727b1864..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 625 zcmV-%0*?KOP)G$e6Et?|1rp{{9I2CHc7)8W>F(W*hB=*pz`*Y8n`~ zi0rQ|nld1=So2Y^m71a|z-@^io|X$I?+}5*AznX45)hG5BhQKTd3mC(NIA>}?!o)% z*@p6Au~#*Cb>(VPE-W$^d4yRFY$!R`ktx5&NVk(-70{y=Rl)N96Ooi65gGfp7J&Xu z7`(@s>U&{ln5Ag&e!BXBp&zJi4@@c3Q*0@4643FlZJ&r6=V@GI)BATYi(u(L0M1kl z{lLjjqq{Zwm4J$9I&$Qv*h-!Ru0@{1lmQz`-s5XsM2iHt-dS0XDTBd>h&$;djMC@D!P6n1ClnLRU| zgd{yDCpY&N_>Z5)B6YOZmwZA*7IR}FvS0=uiQLU5zS22bMA8rJLn`5P?rAB~q`=Q(lej~%^& zyC6>;%@`uLzKiND!eIwMIA}T%ysh8gc82A6pcA=0p`!~O#s%PZWXXk#_pusdvQ#9Z#+T>O z8qG;=*)?~A?gUbh$4F)5U2>Qx72}%(4CJ}^E98ZPITuO%T?cmylE@VD3>SGb28ldf z%gP>U?RuEYv-@D;%OaEIy0S+A8uv+S*X+G2^1-;kBbN^y-nVo zNR{_lE{MpYaNv*CK$?prVko~GF80oRXnV&y3qX5v?7c#3eOad6TNR0Ci05AD>zARm zYYrx2To|$%ZVC9TjHEaf4t}4%0NAoZKEH->t74JWRzqq<@S60000RuCVMpptuu=~EliY(Ji;OeHW*xrkx5@iOJ5`16cAUlJj49@t0GB=h-mPq8UWx= zgq|6$$-i#4!XiXH^R#@A-uEc12QY<9ys%-Jt2tu!^;i(1(xvgZ6oiha?s`^1iNKnW zz%@B~-+!5WRX{|<3qi*&3CSzuiGZGvYhD0jF9^0g(C;$yE{ zCXcIiX~PM*crGHqMwxo=;{$m0GJB6oo~2~5kl*x0SHVDD4tz93uZ*}!upu} zWETB6-v9td>?1iaCR2B{7*kje*GyF=BSlwM;=!x1!QfnHsJcGcU+arE{nDI7)_h-# zIoDYv-s4GRkkA1pL96zmZU!}5S5=RdFMquH5EGK#Y+ zZ6pB)4jede;Qz?BUJy|y&&<4e?k}9Th;KTiSc5@quju3u5p_ld5CKIIWA8V>9G`2_ z$jcl6psoK%WTgV2aSG2OB=VXx03a`OXzO=XkOYXR8`~n}HYk8y3^HI;ANvrC3dD}a zMG~=W{NAVlOVsVQNHTV&OfhXe3{OS&;53kPCgeF0@}BXf2{x!y*YkfP3 zeDlh*xel0{_R znne-kB4!`&TeC11o>(Nq);I;jp^q)e1=4Y^hXnvswL#fDy0Op+GcZSsY*48^i_@% diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_run01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_run01.png deleted file mode 100644 index 57bc4b564e941edc23946deebbaf090038b65435..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 573 zcmV-D0>b@?P)2~X|!%0W8?X1=@+03TPvd*ya}+9~wPp`!-ca85P=;8*Sd z(Y8c55dPnBQ<6ZmCkoG1wULc%)^m4AvKEN&Gm;##1Ycj<5{S!yycMTG27tk+-E-`= zyGUBV%!PIeQxl|34Pk0%NzL~N4i?1bmURi38(d=_C)cKTty#Z&}a*9{kqzvVjFGmsr-Tib1LOTdvE<31MNbF<@(X-OCp*j ziv%N^Rj?pOa9RhsRh*`^|C4p#}8Zbf-*Fao1#!djjz`86n(JUrO2|rV8FzP%8%v{K1 zqw4og_#Mc&&3@ULKPBAXZO_;c)CW^C@8b+&=HgqQj4(-}00000 LNkvXXu0mjf?Bx+j diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_run02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_run02.png deleted file mode 100644 index 936b779f7c172fb6158ff3eca027849f6e5d79af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 605 zcmV-j0;2tiP)j0vTC}yb>9StgP%$heR?rIxO=-J3Yj*a` zFl25r=j46@@E=dGTN{XI1OR5<$y&h7yY-o80zY!bP7`WiG%6%^dm}a~;6xfVFg_E> z3J3|&%Epu6i8P9;0Nk_t10OkK%ML_9Sx(QNAQ2#>m zsouYKgu4(8%**2k41R#N9>5YZ@xq4tT&*!@KOPH0RJ$}DohG!rbl0;AN(6F30%s}= zet4TaDbwvXMRm&F4Mses2xMf z6!jVXrA)|VdNz9MYQq7RXme!e$8~)M0F>t*{iTErD(MGpJ=WifN640`_kKQ5RTZ46 zP~74p{ZTuHQDL(en0c3Y;J%8PG!_wJU<#$c1_j@o(6x4Q{A2(CU2E~_6o_d2klPy- z5UxPDPD1^qL}yAgIzU@LcQvpy;MN$CLP*35& zW%{;=2VyqKA{1lY1IOEXO87m87Y_j7&@MSygPC{PDw56IUMzMz>~Uy^ r*FKqBhvGe(@%FEPh{i`V>)*mJ6z@2Ye#(b400000NkvXXu0mjfggFNm diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_run03.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_run03.png deleted file mode 100644 index 0942bf1583eaf9035580f8cb12af391a72978bee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 651 zcmV;60(AX}P)_sD4Jppw zw^T!EUV?o&0TGQ-SP-2b<`b@#6>5b=D!#n8S(g5}Co%>p`zi2#@7JJ0S%JG0?DzTe zM#8V2$j6Sl*yN4QyF1sDC6Os$=9RJ@yss&)z8wD{K`ObZin!jBB$kMXMzd|>hGT}7 z)%A#T2?A*RZ5#lg(LPZRLdW_x7lqQ)%D@>1XB-;MXihh_R_0~}XZK)wAe6}&hmJLZ z36{>Hi-*FnOCzY+))vgmOEi06s%Cl@mL|!XOs6qSMB}!$@%=^%UmEQdToW-n5siLI z8klVhG|vEls;V-(*2+XROH^bREm$)|056D8&}}z6mWY{G4KSwcW8xFqlt75IUsE-+ zUo-$P#-OSyq>gGdLki!AVA;@@rj@8R-IykNH$j>tgcLRcYgys7?*Odh{#jM|^7^@ppGbd2~=#VCM>yM~7hmo}PeV@PC%VV(0uw-?;+6b_YEC z;Vk(u3_r6H{eC^*JJy*6nR%s71TI-Kb&a~hK7PYLZDd|c$7F9dF-}e$^Q59 z;<4=iEk%+C_>X^$Q_es{3ji?lNybN{HfC12)JY;*CQBIEVVGO=?W`U`Y-h6Q9IGPF zIS(4s0F0HK=y>|g)|duxI?q#1VDC|Uzr4gp6U^bxB_dkVkfh`(zP{p{d7B&N_AX4> z5FJk_gt>L8P?dmyIi5^I-_FHQJ^ThjKH11JS=x7y&%Lr{5^sC%q1Hi~^Xusk z@YnNJfJwR@ap!^Dv$8=xwuY;ncf{3|EDLvc@${~aWlb(ktFFvx95&(``8roX)rCW( z6P9WOsAv_rzbt}W7p)Q~iIm8T0z|a9j|TH+{QWw{T>m`BTdjQs>|6M~2aoI}5B>ru Wz#tP6Sckj-0000!v}o?!7vvnFP0VknoNtjGairi z*?s>6_{Xy#qM3;1SI@oYE)AN{vqe`CG0NdU8hc=7=1r48iX?^3sp{RLz=1Tt763rB z-eX>@FX63fLO}lU;h&UX4i7OSqB#r+(Lx$yn!G21&MCxsD0(1PPLqE^V)?Fl(v;+o z_@`*f!7C^VgzBjLMBAK5fLH3^aF^7yGPdSS0cGGZA$W`ADY%usCKf%JQt$ekP|fjZPDEb zU|g-e>;X_7-8Jj00z@=_9ku`fJB~Q957CJjdba}reD>GphFvI9+OK)=4ZPRrSp*YT Q5&!@I07*qoM6N<$f|6{+wEzGB diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_cannabis.png b/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_cannabis.png deleted file mode 100644 index 61cbc1560c9c758e39d3191209140c9d339fc3b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6473 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 diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_famicom.png b/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_famicom.png deleted file mode 100644 index c26f2c81d394937fbfccebd7713312a94160f091..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 752 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 O!-1uhq$(r~1OEqdZ$x+i diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_gameboy_color.png b/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_gameboy_color.png deleted file mode 100644 index abdd940b7045b28d50dd6ba108a55fe42a2c0320..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6910 zcmcgxRZtwjmK`iOLx7-Ra0nLMU4mPH!O7qRcL^FCLU2j&;2GTA-64U&1`7mt8C;h4 zUcIWV{n=Ohu|Hkix2yY}d+xneeX650)Z}ro$guzb0Is5ftR?_}^z(T>f`R-z24={( z0s!Rwin3DL-be>2nBK{-=IcqJRsJ*;#g6jMbBXl%ljp6FY_JrWDC)Fu4OH_D;6o!O(e`@t zH4Emo1X7s!Z@#`G6_P3o-3vRH+ikH__Gi^a+Zr?YjEda739x+I^|L$_M=q2)O5+j# ztHtnTfqrwET!0R}#OTg;^yQWOJy{X0jP*{}ZC89?J(`TG8!2)#Gh3Jka~|O(DSZi{jt6p)0(J&JzoD^>5RzWK47=-o;jIh- z1}X^_q5hKD8h&x;e7K%g8E?O*O+(SVO-Uhi1`P~D9sjhfBbUDGk)dJL#6Glv*XL|H zWE>zkpH!mJX?D%?wSiE}0*>Ks#rQL(pBV35;a1hm>jZ;L^ia|-iO$LTngRYT3FiAnl(#H^c8V)Nm7cN^qrW0&1DROF^oH# zmz}sTyVhYpxeB$~K35^=b8uCcAh-8mH4tc4NH`?w>|UfmBw8q#Rsb+CX2F;+E3~!4 zVl^&78wnw8>4)&^or>CSZ~>{4wA-&yHY&Jxtf>ROU(%}E+uk{M`Cx#{IM@udb31uv z^X41UsQy%E5wN9_k~ZXv4u%vFHnMQ&Sdvl5fCepi>&@s#g`(}`k+6F`?Q-Yl^r3a;TkB3OPe_tw6WM*K9?q@<)b4B zH_J>$eYcoQLAan&RL#Q5tQ@?%$i1dTD360_PYO$WgPSBPnZWoa@#bX-z6`)8Mv$wC zKv>@0N=1)^7!9Ho%FJq>bWX6xHd)e!S&tXR;4m)=e(@!uy$ii$jeqG z^-Z9Mztly!qyB^=bc8KVJh;?tj!)s` zEcQ7gb66k1;6gNj5>Mjl*~3qIXVC9SQOeNh`DLc$@V-FB?{ImJOqiqNZBTap#N%{m zVl?xw+E`PYeSAkQM=&-B+mJ{K@WKLIq-}3tkI^?{lc$yxgYRyIVpHa((jA+hYnyJ* zGEK`#N{_9GPwSm(NWmRztsn03*@HbbYTTw4XEnDLX~{?2ez7{+@5)|UepVCt{2;e~7I~p!$z;GpZ}R;J754144%^i) z161|lhoEaphLtc$>YX6^_Tng50X3AI1R0oELION0iE8;9E9tPgS?gWj)Rc@a+FRN} z0)3mxroY#^8D1){#(F4pM`A=o6lVL^oFZkhQ=V5q>9agiIrq&nxO@^;y%n#i%Qxci^P&c%5U231s52n-9b0) zvUEH1wP9h=EG)Cc#1oZkT0y}Bbu%wo0o4FbI^ST-YSq&;Vs&Ng1k1nY za=Z*!!;R_Vh++y(#+PF5@Tpu~TS`i{EiiyFf)B1%U;FE>x28u-)e&nu%9vyS&BAP7 z*_1Jznjb@@TK9y86w{7Dy|E_y2(*LMGyA#DZlGP3{n1J^fGZj+l+d#&_w81_qnR0M zmQ~^X1zZs&4Qd!g=NoszQf|t#7@?QO>*+}h5T55}$_S@^8?+6^i z&ZnpxL#L6ZTd4WI1BJtK()phN$V~i^zwJf6IfeE@cB=)8J%~=&NAD!oNe5>F`)RwV zyRv<{^XRMRWKzCU`lo~Xa+F~9k!UBpbbr-xc&jUwzk}cb=cxI;<>novsU$XM5a*Ba zl`KU=P@Hy|{zC1^8f~Go{NsZh zUQ0;PYYc-os>-XH5U+Pnui3k`oP~R9kl9tcx01eBQ4x^hX7@*4^F-V(?b_i17b(xE z$!gp)Tl<-I-&0Tt7!~6jhA!vCK`rpj1YDE1U!9DE-z-EMBzRH}Z@YAqu{P zMX%cK^`q%M90l%u=~FD(H`aKOKJ&yLz2N*OvjhzyG-94Mb-~Np;WI;R z8NuBgd;G!K$ zC|L{|ZqOI*VHg3A%oO$KHiJodg;`u&0X6BP1f+6^QNVMDy?vkxN{=)M2CJ#;kb|X6 zfDwQ4({)q)X-ojZZh&e$As_l!bK#R1>H*5CuVkE<29NABue+%kD%t6eEe{5-H{jWp zA|Xwgxp18?57`tN`@URO;Vh6OOh1imIUZ{3(Mc2cBGW<k2HcG+}Qbv4B7WuD}7oRq(<0xgQw zi})MCAWb+~zZ>EQx9a9IC+A)&awHh^`FOSyZhf@!vJoWWi-JdmZcXrIJP*y0!B{;L zJ$32AQA%NwK2$H&V;XU{G3&;!BjBmM6B0N5vgp@<3fsbrw2EJHWjNa?=OMLLRSb_< zv&(*UtBgVt{lUe#;(Ii>X$P%K0D--)=ZBN1Q}jDK)66{%MW+Ip@l)!fdT)d#yc|7> zBjAjFWYpRvKj<#;OmwZhtqo`BQf@ya-6o+f?Ywl(sm@-r_D?6xaXJ@OqKZ65OiNmp zaq59Q@i$|A_ZLRz5W^SukA*K&V;T=eK0S_0f$MFiOr1Y`yt%s4g%uPQr?LN;M^Ddy zay&RT3oKtWsb()|CET2LtAJJo&#y~zjuJK|%tj=QSnFdJ<E2u;YL{7!=b(?A&YDNr&Db@ca8x3UAtW)#*?NA zPBa?#GRA$Wpm{2&yjFOZFLTbn6XzQ;T+ZZ6=BIP=3~&Xde$fvC_&^cbF`1}#3e>UY zdTRmvbh@9F*hi0+*Sx!8N85rbWAcKN`m4`1iKRp~WPvD=>Cxo&PZCJ{kGZm!f8f!t z0V~U+L}sd^D;d}ENzbQK81hPx3F(ThSSazb#!ZBX=ja51i1>I*z;1F2kAa!M{CCwf zfOfynN$fKAHmH5Sn(OIt1jCv)EC`jZ7n!6+JjAt^;?V)#6l~HTb-^rg<0=!M<$_T6 zdOB}pO{D*Q5_S1V8KXJVi^`<$N=*A9#Ow>MS;VX11~u8cathJ8MEb!H&eqK(0;$+W z3#jF$M7u6;Lq);8mcD1>o=C%si%Hpm@roCiJRxV(W{>g-p~g4*D06cpZ1egiz4r|d zLpvMi=wOZov5QTuh19+P2@~j_0-NQTjU=AEum|Dcuo4z%pc!AqFXcbt%^An-AD2qDmC?JSpT>8Yh$n=wf>_}B5oj5`-0ouTOB+|@6`oXp z`SVdiwb63r8Gc)n3Q-7eHPD`8jwH)p^-#Ujb3VgSdMgNi^%ioyYrc=o@BdvFoMdHC z5a?;q6FHFM_i(afs%j(-!>yqClpFAffz#>u+h$1(^vLxqt+E*?WUJ7&aW>*@74U;L5#Iy^2x%?x zMaLL`>dK=!n?Yapdabl4#R;)Ut+2-wI8zIG8(Dkv3zaA2IeVrx-|$i8lI;$Wrv6Ii zkk8KYUVCrY?6kU89J~F3Z8I6_fbGp>i7FWbeVo0(dpo4zQZKbex>jZ@I=wlIa#HyH zLUg;Jew%3ZlQ4arZ^Bj!y%&uFJMY$q7vlR6rhyNVt9Szq5UfOb#aTeB;yaxZjv}ul z`bzAQ4?V_(w+5)eq@<8Oy_tS<8lf>qmD89DX<;HVAa$Cy!*P?N1}LK7ucz~egJ11s zDs9w3l2gix7v#WoTIJtHjhIJ9YYfV%?%O`4x|QLH99S;A!7N^5iI-Gw4YZLIhh7^P zUSD*-_-O26{iBzxD9j$cns^&_Mfqi{t;BV~&$n3q)pq)v@Uu9j z6ggs+_{_HdRvMFX>vS5OLT70hSw5@~x>ECuqyJgIFuZ@(U;eHOnmPHR|9HU}B_jt( zo{;TCtw;MhAogHwBPjG`O!ycr$ccNn?pX25BjBL*_21e~vefuSco%r5E=sf4n(b&o zlFmX?c72T&!->ycq5n{6O|N(O)(J6CgDa#X)t^xbg;s$(K&=~e2*?w^u-{nwK`zFW zT$5!fyn24Dzu0k?I0ssYMW$BfuDCQ1(vv@1_ub#fE%{hEL{D6p$VOanxZgxDDY}sx zNiK}h*L;51N8G3-qlN_U*Ws_uhm_KYF22yJe?nakN!SM!bh#ej}#5E%<0qNhYgS z3KBx_QI0|Ojs0icSzU&?CRGF$?gW-i&$F;7;fEvr(>mCklTknFCo!iT- zN`=)Nto88?I@zP4hGdqVb4$6#5F}e-J@4cNkkx4T z-A+z_sMgb4p0;gIt;_gb4Q1C-OyViZF8YeF&ulI_?|UD3n#YjbrXUTAbX{UA-DLZ1m2a8-r?3HR^b4^3u|Lxq*;>#`#1%aUoV-rICI>Te+S2Cn4h)O>V^8l>S<^J70ZtQDCP$VQaYXxki{x8n?c<1wyo zQ#pPJnfqR(oC7^?tC$|N8g(%bY|S)!jf=;JqC0Z4O8VJe|4ql)w8R&|isP01dg86- zbj|V)r5=bGVO?JIXWG1H`D)4aVc*2(;E5eme*8>K0lzh8TXYMH-Q%*G8mLlYBkX}qet38)?d>^BE`+|*MWov@D}jN8wk;$2hI($2|#8OX2t z83tNI&X&1e%573w8UUMkhMJyb<-y;|25b?z`U8A}$Wf160(c|Wp_K*xj*>pVS+j!s z!Ty=BZ37+BQ26+|9cC#I3JD}J?e&bexd>LOYAJ5oiaewSaC;g)_u5}YaYP^X*0d)M zU?@(0AIUk!S_A<28?%nG_WBF(v)jM>F5iRt0c3!sM`-y|w8|SX} z)YZJ&jZ2th;i>ID(k+$8ahY;p7h_LQkj7CEzA7vODVEVs^7S8-bqp$e`RmVu zJ?MQe#i?UYkX%B6k#zrUYw@4Ayd88>x}NAdXZ>^FKMq47?WGI7Q-z@bV)=D}CSUgt zHN|Kq8V+ED-c#CQpOLgHLu;=X{0=__guU)${tuiy`=49+$9Y$w0IeRc7ugU@>tnkL zP!}nPX341p1aPiv_5qk1Iepv1bZM}x%rM~axt3>q+v+$Ou>wNxV$RTPKCMV$zLTgH5oZ$ zH;V_wier=anI5QgCsyX3WAyh9ty<9&1FU`2LI5sLuU#R}4BWUwv%BQX5dhEs8?uRF zWkje^>}vAK1Ny%xC;@eei%RP`l|56y`=1f{&qPkB{<*sks9XUS!1421bK+tGFYgh& zlWqtm6r9nTHu1Ldr-#cE+__6|AV=3p@gA-~IPgb)wGW`{*Z;`>|0s=fY)c$xe@b74 Sz~_H0fZ{tf*$Qcs;Qs=)qf6=l diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_gold.png b/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_gold.png deleted file mode 100644 index 56a99c7f0e8ad79a44a43456d8b84c05c30e17ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 608 zcmeAS@N?(olHy`uVBq!ia0y~yU|a!YpJHJKl1$f6iUBFb0G|-oa|?kihAkUcu(7cT z3k$7jWB7lP`_EB^ZG8;?f#B_01EIpTK;6GQT^vIy7~ft!n0v@Uz%B9jv5T+y_NXpt z7MUn^$!VR;TRtAy30j-WdWs@MA~&^NwBMsIsGxV|v(7R`h6k-nm%Y98zI*q!&vLI8 zn3eyXu>0%LzkBOG%zIUEB{p@S9+#;uav)~vUiQC*7W}&|4!9&o_e}AXV03H zRcG_PuS~kCUwJ$D)ZC}b&2C=Vv_N->zV@m2-&dskT={JFuWqgQRbLtG5`)k0_?@d* z89niD`KcSftgn_mT{x%nX=0_&{@;uo2fvk{azCT7;)9)*+VS(%43k$(*t34s$z^-k z4LZ3_{$4dJ=TtSrqHk`kPijMb&7Rsa1iG*~PwAQws8|^vI@!09-{HzVY+5EG8L0sZ zO9qC7{rwF|ZnbP8##*dbYFMAter5%VHgLEV^3Pt@z2&Fl)y!RW-n-8}xANMvGURID z;<}L9e`@b%_Ra1Ta-71oC`4dslZL3vN(C?0K&_4-a)gdHuTXg(x@ZqS|MC3y%l5b% zb$+t50r^{HQO6`Vh#iaiq}JavmMxWDy<&3Rm7RUFj;zirmc80@+4WY;{C!KkVt{F- bq5dz!RmOzp&RQYGAg_74`njxgN@xNA5@r#Y diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_mega.png b/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_mega.png deleted file mode 100644 index f9fb70bdd087e59f2c53ed206dd05f64b268d36f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2229 zcmchYX;jkP7RLW(it~(PX0K@>YMH2|sVO-U5eZH=%d{M@a4K`a(kx#SDH}l3tWd#F zQ*Q%JkPTO}u+j=OGo&0)Oz{l^Q$b!y&tA_yYn{E;DMh1#b+s(D z007Vp4G9PX02PLkIU1_UwQIednNljyNNixW5*UE&@)o5^p2HOih2)(pkhnr|Fjdm) z`Lohfkpd__0`ip_OPwV@M_(!cV8{`W$z%d2NM1AOGI^pi55)^Hz*|#@cv`H{J7%NsB*VzBH@VnV7&FaV(G+fGSZpq0h!--TY z2WvG-LUeAdx3;`|f%zaR-K^{>PYyG4Gmu4`9pIv<$GUu6p{{;4kJeGz zQED0HjE;`%b4V-6Y(-p+zY{{;u<^36#ZNLTOv60&&ALy}7l$4x`rNN}6en=_ zc%~1)jOmZb1LY-RH=SzB>u;weT)Ym9FAJJk{m)Vv6+EjsW?pjsOAHlZS`{WOA$&$L zx^NHej5NNvc=raQTOO=^o3|d@-}jUR9iRzHb!&D@gdcqIpjt2eZCl~T*W9vAW6jtU z`x}hebzidEPo14>2H*eWsDU^i^oBvp@*rK={i>HFZnUxy>%gW2|NPu{e`p<7{MR=# z4K4G@C0SBDer&T;#QH%DZPW7 zlyZpnaJPB;ciyeB`~+t3(4#5R^;I`wyo4m3+0GDLb@i55ni}!=2}jH>ij9{sIjZwQ z3Ou@jkFS7o;%b`TB;MgI-|`tCL2uUOUowSs-`(fbUx9GvCU&EAm^@$2rtuLBytCPu zGrSKVhu~j?xYtf%tcmQpd`bi)Nr=(24EN zUqts&ljn<*=O;vtZw#BDfOLn>Psr_Gk}v)hgCD}5 zm>;m_iwz?iElqZ}9o!SYx)YY2Zq#U8yShf1e8Qg#h-VN^+#b-O*d4|WU)2j#KFPGqdK>AiRE^vI z04>Dd8Qh{Dw~$m^8gX~zN=&_8eHNqQlDU2KJh6Z3s)uOQ;oVeq>%zTE!5i|9*j(W> zbg}lepv;Ag--qUJ4FgF?v8S~l^V@`Xwg+PTBha^TK@@)5B2oN< z<;#E-Txe|5cINk-cvcC3C&~=Mc8kwoS3>OV3pNvErR&)UKpsiwyB zJzS8>sUqrf;3W{mUOA|RVMbK4vi?7!Ke{|wTtw~F(JETh-x*`h6XnWZC6{%&7?40whZD+!G6QvP6gAfg0dHu&2?6{{?QDOqFNs zsBI+~L*WdKTHDh^gWo{B137GTgLdsT`eo^RA)YaMJ);W&k*@u>2HtR*DNc4c_1_p70?RbU-UP|pBkV5 diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_metroid.png b/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_metroid.png deleted file mode 100644 index f4dc9dfa6b8fd0de211685cf76a98eddafb50579..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1458 zcmbu9c~H^`6vuyxQh8)MX`ZAttO=HwDeY>73WRqER%Yc@xJs6J8|#6#TZ!q0x2BRD zUTt}1TA2r#Eg~6-9WGv_HYiFenJ3H6?(FpczIpTB=Y8kRyzgHx9q)zH)-cil06^Q- z1w#M;l`19k)IiFr`sR-v$_B@K_+aS(4FCZO0FZY%E0*1rGz9b|Qh^~$0AMMX0EI%K zR1{OJkH-T|l(TjW!HcLA|BC|RRcH_ZsKZ?`PCf}L(!Bi<)5vX9teS=D`B#P>H$ocw zXLMwUKqqP==t-LjZANu-Xx}Fh`dXIXdj7#BxoJ~Mxq^dpi~ES77lLUCf+YI)Ma-+K zF+JyyH!XJbhL?!+56K3W0x6i!nomd@!L57eH&=_CB;oQgLI(4zT}toT&^Yo0&vKN3 z^20q@j%qIU^Z4_QXntS%Iwjh8|Hy5%a*;$!(Xf8;vV%aZMhMV%zfp^#jtdpRwkJK) z(x{5l(g@U1@>v(wZ`Wo6rUVAQ_Y1yetOg}7(Bs)@Avj6pDy-GA!ysB@Xh=~P8fd#K zGU|N}7p(Mqkm|tJ^`*|#&i9t=o%`N4bLKpqS)rGXFul)MW#+#k_yq^Rpr686ls)8K zofvml5H^xH%H&OLO$5BjfFT1$#YRSJeprucd{+{IluoGVr2N>!Y8#0OX?o3VjC<7n z^>au_q5hnja8wn>nmTACxC;uUk^ zaOIj8$!8JyK&)|HT&pXh%(iTzPZ$plP%<6#eD4YKyF_E-900GO!{N1pOmk>k@rQz! z!-DG8vi*s|Ks$(z)Sm;c%6xuq)8kwfM69h1RNiv0PwpjYW$koILuIkS)PDZln&{^G^Hm$* zk<^AaV>!rV`oN=^^W@8q7BBFi2+n@zTgQU3po8(K9Lh4xqVf0aQmexyFfsIC82Nh8 z;L%tcyB|#J@Y6PtFkoitj)OD;U-RX_^z5Qqbt$MLJl22R1*dZXE-kYH-V+CkYck~% zCySbh@@yv<@Rs39?}GsD^#kdh1fRm6r%SH2j!2+I#STC|EzjO{GWl-KdGah^PXCsJ ztE}UzJqXFso2=7yx%@2P1y>tb)agf8)xg`-e2%LpfneVisrWcd9{uv-wMn-{>cR8KcJ5>!KnV<+%kCt6MGth5rU*%Aa2 zG-jOK-Ls)@F(D2&2vwl%pM{LN@|e+<6NEe(pBg1-Uf>4}Cj*DK=NSncs|0V@vLvXv zUV+N5!iz=4=%StAaKFsmNRa21xD{L_chtz^E~h7iIxuNmn)<@iehc$}8J@XV4uU(5 zI67l~as+GxfIBH>5Eqi!Fbxkjn6zy{Ie8Ju@oD_C>snmve{&)7%$ix>l7D6U#;(4R zq!Z6d`z1LpXZeuVtIGlcCJ+X%)7A5(Y3#+S=QCB^y;C_=9R#2MDVO8OxU!eK<%las z+$$!xrgMC0w%;6(5#X!QvxURG4`-BCwmn=KBqoK*uiCbG{bQP+zF=AhN;SEp?>qnZ Oe|5!rVeUHzru_xE=(hd< diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_nes.png b/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_nes.png deleted file mode 100644 index e25e8069e2dfeb90b6c2cb74623d9de0f3ab2df4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 681 zcmeAS@N?(olHy`uVBq!ia0y~yU|a!YpJHJKl1$f6iUBF10G|-oJ$v>5nYXnWN=r-s z|Nrmk<$5Fi<5HlSAWs*^kP61PR|5NP8Hl(&^x4IzbHw@+*NRQ8c@6FF>b18DJEuu) z7l=HwcfVzNfKaQ)+2f_Q8DiUH*W7;@9r$sM-OGUAheCG;eC(4-4qxHpQU5cz=j9_C zH~$T_D?T%I75da4ep|GycJ5q(S1}PYr=%6{7IE*LANbTSeTAQz>6#UZH#MelKkRi{ zw%z^g?{b~x#gQd1_uq_I_jtMbsmGtopMI=7{H0y0?a#EAbNF6b%)hL;_eaf%=eLDl zKmJ*@)71VE-_}20Bq!JZl6v*;NbQE7_x{Ah$Q8E#vRb+2{@wU(W-AxkR2=?2=gN+A z59KFI{ht*Q`26yVk|3kTAkHaX`~j0wM6VS74HGH-^IkWg?$*vfhrWD{u-%dMe2K%% zqo0^lB~J5CNz}0qXxsS5p^@pUff*N=DR~Smw?24^oAvbhZn1u=ystDrT^`9=x}i=z zV8ImTr9v809age&fj| zbrJtpyp7#fZs(M|Upo8mcmCTuW16hA8kktQL^J{x0Ik1zY313H(oU{p%~iaotOCwo zd;IBj(c%na%e$8x8)3>qt+&oxrx$pzB4E8^B>StIRWWt3c_O)L%cHFzrT~pwk+t0H z@>NwW3I1268m}E}qx(;+s_5AKj*vtTHp#)8*ZO!z`($K)YHW=q=ND7R7bzZHWF=j z_uD$G$+^j({(-l_x}Y`W>HX^kO<70k7NpuQon^b?&5hy}osUnNz&KK=f1*o zj>RvzKRe#bQ@~zTzwb7MVf9JXMUPA(Rl|R)nXh|$=iaO7SJWrR zW(#{3YPQ<)yg9v%@6iF_P5)eq9&q+g=D)g4b?UUkhdY+MDd^fCRJW63ZuP`#Sre=q zCt11qt=kLccjm4A2cy{f;mEPn5;*GDIQ*0Q|z#CdD3 z=hC9z-J-2iWc@ydpVAj!Sge03inmBTAn5ps%&Fn4Qf16|k^*xtZ*f(&e#dk*v)yb? z%v33}J9Cx)N8jE&offskQ*Je@($m34J!G!R910{Q)7@u02iy*LI+I|jaH_m0SiR9 z3cXSVKlx{^`C4H$FU!_cJh*u7*)2P-&6E>g967E3Ci}0mX-j*8^uB(-^C$JzKi}TG zpB;R$8&^+>oOa5q_x-e^UN=p3mL3Sts&HF=Dl~8zK{AeS;<{t_Cfip zOL>B}{o1(lf7-PFesO1ZZoG6kNu=rgQZ;_JSLq@_%g%OvlJC9pcdzAn;k_T6HVReG z>8f4we%jBQpOy4h-j2yKns4u-5b82Fa_;~6MRQNq#CnOgtlPHw$}hX?k!$MR6+-JK zZ^}t+O8UDa?$#IQy#1$+oAYyh{+(sNZ=%OtDcM@{!_&K%3uP4xy#z(U8~zEK2xd4Ropreu!P-2efkP_gx{D oOlKCTsj}K^(i7!JG5;UK)E#`j0y%yUfccui)78&qol`;+0AeKSBLDyZ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/wall/Fire_Bros/wall.png b/mod/etc/options_menu/themes/creator/sprites/assets/wall/Fire_Bros/wall.png deleted file mode 100644 index a4752ecd253458a51d6208cf6321f2b28e2f75af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 123059 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

AKuk)iH;=xrxi@Vd-M#xhDkw+gYe(NJaJX`l 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 U^#c*x+yDRo07*qoM6N<$f<+=h-T(jq diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/wall/Frogman/wall.png b/mod/etc/options_menu/themes/creator/sprites/assets/wall/Frogman/wall.png deleted file mode 100644 index 0c2c9c12c70250e203bc33879c07ea9f78b22326..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41258 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 WLvII4&BpQo0000~-AT_3lr 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 K|J85*_y0ejl0qi{ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/wall/Little_Bro/wall.png b/mod/etc/options_menu/themes/creator/sprites/assets/wall/Little_Bro/wall.png deleted file mode 100644 index 3aad5cae026f0e4bdd3dfd0cce25f99b69ad0d85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 211003 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%0DK++dMC;Wrx%`5cshS>$tFsa~R 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�uMexa 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

LgGxBdAi*#ers;(+iTqfEJ|EG1yD1Ym3`E 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%0Dm4}S2)^89o@6{(a``2upW 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 FV1l~9h|K^1 diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/wall/Racoon_tail/wall.png b/mod/etc/options_menu/themes/creator/sprites/assets/wall/Racoon_tail/wall.png deleted file mode 100644 index 069c57765dea89c7c680f86d2e618f9d66523e52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 87386 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

!hV* zzvC= zIh}Pc!ix)pc#2$~G zImv>Lb{w`BW5iTQe_0|6fz^#yORu?mjC9BvoOq7QatP6bqUP*bsD+bDw!OoD$-n0@ zE*u{*Vx?4lLVf}f#_+E>4a7;SZ zuc`(oVJj_uHIVhIL&sA4n0?f^hAPdu&TFSa#7wdsbF1ir2G)E*$PfT{sw zuv~#9FtZZBj^LB0=sQgJelBL&D9S!)5;`1 z@1r~Lc0unn2Ly`)Mez!rHH!yW=c7jiL0@BR{mM0>SmVg{u7cu)e*NZInY9q4w=;dJ z;%)Lg#@VhP)SeCN^B5_o(906G>s_4RxOP|vqAoy4G?l!m_6gok@gwC5Q7%N7>Cq`; z5Tm1`<=zgwpON2wB6+BXg;QtzP}t86+?rqOW+qpu(o+>Z1bqvG&liAO&as*%u>YGs z;JS<2*v^o+*NAKpBT3;XXXae;?CjCP9@(0Dbb-jKI*fa4^poCpM_Btyk&I=OuF2xB z08J#}vqwn_(YDFmDt@1poCl_c56&5f<>&pK>visn=oo!oXj z&wM(oj3wKwv-aY%hPR##Rb`HU^v`e!Hf)@_YA?ulw9-NRD#;;KdAlmzi;bQ0rH06} zZ|l#tS0We^>8OiR!pbB_T4VbSGe?w}s2&)!s>M>rC`agUr+;1PZ0aH$Hz4P zwR1I^#=$z$C*0eCh-w5%()aD#3%&Js@(A7l!6^#dM$r?u4mm zITL_09Ia{X_Sz+hLR~TqMv9dUuHpfHH1_H$7BNM`fC`)rI4#~jssx%PJ7J~)_5mW}cHEm&rg(w+ZFyxnKd>KE>o=5~RSzbwu;@A@<< z>6;kUSxWp6k-P_FB>#$}^uBuRgZ|pK_|fQ*nxPYKYzStXpNbP@cnF%50|EO#)}Hs^ zRa?orA>%6a0&R3`y!!ymvc#R2ZHFDg{NDg^5>MRWt6E9H3h?TXoNd=9!}Lw%X7~{g z`5If)Wq?Zo!}EWjp-0jF+%ReB7k_k5&!aDBC5|E@WS>MzQo{Cfs_^rf9%LMj~2?VB}E!WL)n>&gbx^2n`a3zH*Z-p zkf>km{3o|TnIr!z5Zb9TKd7t?FeBOXytzDPhsGU|u4JBQ0K&aTSCc*7=k40}HqS-` zXKXsARMR^|7~dc37&E#(%tn%2nUCeSc`%Cr0#9HTh~yhZ^Sw5t7rBP}37p(^HXmcXD1h``K3BMX5waKELnL>L|F0tXbkv zRxZe|gH7|0(qpS~J}2+l|3+bfsu1bg;g#fXc}8s+j{%SIT_DEW-3Io4)$o=v&PQ+2 z%@KzHiv>XoDt1FW0=wy2J@`yKOnG?j>#B(5OA?TVzXPyKcTFUE5GkS!91_6yRf%3v zZ0Z^Hfzh40acVkBGVto5@PKllL4qo1H38nk?QRM9U*r4&>No8x6`hCM>SEI& zh-s_7V*U!Eym5rlL#XxhBu&D?*e_+uY_!jjMcCu*aG44pFMoio@s|gul3x*b)EINF zvEd;uM$TIy`&x+L8Z{M+)sZl>A{pM{zpILy-2;Wlyv1-`MS3BGa|@1PJW2FKKMD#m z^JS0l<$wf~8NJ%4)&F;um5d}NA{t^9wg{3S6B;5r;3sq?Ce4fQp(G@qz?CmE$b-&W zD`CtI=hw$jNS~w~eFV|1iGPn#zs!zgKq_f9g^L_DC@c9@f=@hgQ|zbSq$VrJ7ofV2 zp-2Rx$YC?##3m(Wccmn#V-m!CKrsuAojI0j(1%%sogR~2G8xr|Z0@VRH{|}V-)?L< zw59-MQw)bGMxglj7P6_o+t+!Nq?>az;nsOeo~ZAG0ij@^Qa49Kc4MAmH|^T{7Q-=8L;NTv$%7ATK7Baaef%GZt?Ib;Dh9 z<9c|l#Nk6k?nmda>8vd*1nw>vR`|s7zts_GP)FFZU4SCuQCtx6&rzi24Ys~x5e

hQjmFSr-h>mtK)h0$rj>9<1XP5bA1vc2 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 SaP<)IOF>5EZM~Ff`2PSbG1Pqk diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/wall/Super_Bro/wall.png b/mod/etc/options_menu/themes/creator/sprites/assets/wall/Super_Bro/wall.png deleted file mode 100644 index 314ded8263a1573619ee1bf7924b332113d53e77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 137337 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 la@qv@HCmlGY)84IHGm+?@qGj*yC$ED!3 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

GuSO_-azXV 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ⓈvoY7}`)4AM6DG`-t2+a$SVTqE$vWn1W&q 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%0AcIXT=i5$V?1F{7xQmBZr$ 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 diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/wall/Tanooki/wall.png b/mod/etc/options_menu/themes/creator/sprites/assets/wall/Tanooki/wall.png deleted file mode 100644 index be981d763a55aac37f806cb2cc6b1a667121cef1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 232129 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 |&LlpLvxz(Lb@EPJOnL&BQug3N;*+?P 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 Date: Tue, 15 Sep 2026 22:44:23 +0000 Subject: [PATCH 030/210] Generate loading-screen splashes via gen_splash instead of static English PNGs --- .../images/loading/downloadtheme.png | Bin 1910 -> 0 bytes .../images/loading/downloadthemelist.png | Bin 1810 -> 0 bytes mod/etc/options_menu/images/loading/loading.png | Bin 1180 -> 0 bytes .../images/loading/loadingassets.png | Bin 4668 -> 0 bytes mod/etc/options_menu/images/loading/saving.png | Bin 4831 -> 0 bytes .../options_menu/images/loading/themepreview.png | Bin 1276 -> 0 bytes .../themes/creator/scripts/om_applytheme | 2 +- .../themes/creator/scripts/om_preview | 2 +- mod/etc/options_menu/themes/lang/en-US.lang | 5 +++++ mod/etc/options_menu/themes/lang/pt-PT.lang | 5 +++++ mod/etc/options_menu/themes/scripts/om_vars | 7 +++---- .../themes/settings/scripts/om_systemcleanup | 2 +- mod/uninstall | 9 --------- 13 files changed, 16 insertions(+), 16 deletions(-) delete mode 100644 mod/etc/options_menu/images/loading/downloadtheme.png delete mode 100644 mod/etc/options_menu/images/loading/downloadthemelist.png delete mode 100644 mod/etc/options_menu/images/loading/loading.png delete mode 100644 mod/etc/options_menu/images/loading/loadingassets.png delete mode 100644 mod/etc/options_menu/images/loading/saving.png delete mode 100644 mod/etc/options_menu/images/loading/themepreview.png diff --git a/mod/etc/options_menu/images/loading/downloadtheme.png b/mod/etc/options_menu/images/loading/downloadtheme.png deleted file mode 100644 index 67cfd60c5ab710a7b52dfb86f6d2877f802639d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1910 zcmbVNdpHwn9R9ZEdU9Qf#1=|L5ei{O#As`|)+x(+R45`bdtA0uE)^=5gXC5mm6Ob+ zxzFPu&FRcWHJ6zXVl21g5<9EsJpFP0=;`tP@xAXK@Ap36_x--_`z7FU4sx6}X?o9atm-CDLbx}no ze;e2A*cjG-yLo1+OdsLitsOXWLbm-TMCY1~ehFT&>az$RCwcLcd#dHU z7ai0YZ%V@dsTSScnzfMouy;0kux|)YUzscgfiI^i{!$$G<@mUr)LPu+a9Zuju^f+4 zloPov?0sT$kEi?j*o%OfHjEwcZwz%--DAyK&f4FuW(=vYKd$0))!(;A%9VUTKgYYe zmL=cB?)GcnH=5@Bj&{l7F|jR;)*Pbim9Y2oYX>014_87%={pxXCl&I}%t^fPJyA6+ ziGQS}k%~=EJU0De4fubElfcXM)vDM-YllL+(#DEN&WnwZuGBe6e6~7zcU?M>P3$#q53{KuR4Hr^ z=5Q6lTc9T-U_nFK2!_4abW3k8;FbJ-kQ(M;=(U}Cd7@KN7=Q&L3UHVA-WINeu)RX8 zfDAt~6K|q4hT~8kC1|v?)TWN}YqY5E`dPp~LY=`m)|8vONHRD?HuLRWVgcS21f^o- zGroR-D+}qnpZtEy7KVUMSlZp2Oflbjq;W3?$S>~+97>A4#||ZIl@S*^fur#}dT>FH ztk+>d8Cu~ctZa&XJg^*JqZ_R?dw;q`U!BoM;JFXPjn)O686lRDzeF(wtFl8T)R8Jj z#OI@=wOABQ|6}l5ZBb=6DKhbzKrybmO-~Z(70E21`?{Dj z2}_O>*fGV+^HvZ0{VWYUW3VQ(3v|Db5}M{VuQ#+UIi1Toh=O_+_hBoxTKSR|G`=#6 z9I|=m@zc$Y`?7T28Qx+J*kYbX`s{P^+3-FyidpTzTQQ8Z-dC$jD~Q{Cu#*k{>zsLt zPJh`2Ge4(GY@xz+}%4MGa_(`7u!B4fW-qf?m>oXg#gPVX5t}@5aBkaj0B_QsDt#9P{a-D< z7v)DHyi7{!+b@V-D^1LMx^C?Gui=2ZqKn$a>yCNq6cL2}x*p`1Xd?Q?*T(rH1S#6` VkwbUf+&sU!F!ngR=eFK4{{lYXLWTeU diff --git a/mod/etc/options_menu/images/loading/downloadthemelist.png b/mod/etc/options_menu/images/loading/downloadthemelist.png deleted file mode 100644 index a96a8a0429f242f302132b89d3944f6d442a585a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1810 zcmbu9X*ASr7{>pz*w+*hNemH^SBkM`DaE82%WI$EHByF2hRHVnN|r21)|fb_$TCfF zreq6A3RzA~p)jV%PL#nI-qE+dyr<*6zFhZnp6mIY`#jHmo@6J~2?^0Xq5uF$*q^jP z1AqX(g!aN=d^5u|2k`BnlY_G@-+6x~{LXw5KP?fCMqz-+dKy>@v&p*U8tLPj)!FY+KjOjHG{Y7AXYywka+y zxcNAbdr2)u={FxHqIV%_%3?C2Q#h=;GhCN3kByPI+ep?Ejujdf_^wXg9V0R;r=!{v ztzTf_c>DbfEeBQ4i^C4Q(YTBxQD{e~L4R~7pNSmLKy89~MEmVh<2;MOh>;^){G89H zy4+Gn=|K1CDwfss3eKv?#lXj_OuAGM6MYCr?qen@T+WTmP&zPz3sZ8~<5ggIG5y_m zxruquosbRb%YJv}ykj)kKGi!q@$MYI7L<~OP9C0_Ix8mo;yt$W?rC|SaL3lvhR*3s z|9QrG$hF&ovhWOmo?&>)pd_nsYF{bohljbQH7eX!kXRjU4=lNtuAGYx+)A6*LaVdTDa#2i`ra=&p;5Xvf<-plQ+?c6toT9>q${wtFQ_H)O`^X)g6VZhPMcg6HkRU6*8wE-41QrZ zcx##SuoX*BGe`p_1Orz+O5fA` zHvtEHA^PiyQy!$HWOvZpAfJ14)?K-9VsRgI;!d=mj&QM?6xN+!t@Y#w5=&!zMPi$g ztZLVzV44*$Ul;`h5~7VLg+dDiPZ4ZkK(HH3zJeDD_^U(7CQO)oJTOSFT-|@4woGZd zp$69MgdjjQO=ubC>!QT2=L~F=A^YGIPx~?ycp(g>iWZ%*3{I&jPo1#6r_+!j(20z=xt9mwO zAHRLfGT~@YUB+9&XOpAGKg9apNK#l;tHeM5FdDiPr4HJgoNoq0c*)oEo*XTvLvzONg=-xY-S4I>myYlIQoY9S_J@{yyiRB4Q-* zoD2uOn3;B~+C3ICFgfu&mT{9kLnHR!PfXlzIErkPM-Jp_6c`c?D!!!+BzE!l7P1=g zmVkZ*s+yXUDUn~ew@E*9$l{GMrN*1;5RpPn=a0m|*xc#hi1Uwz zyts%uG}AqzAOv|iA*^EOBE@3kV9>m74NwJqrZ24Wib3AH@JyUFHVwk1;nFn{ke6lb zqN_Tv6^tUKzutdWJnk@XuN#FlF6gtD)*F{KO^p05E-4^1?^8YHyk98Onv9WSoujDT~oQB%aPlzPQF!NQc^h>vrJpFYrgJ z1-z6bd@BjA`zl)fk3X~j8Vs_c_Ho^CM4Kc8mC5l7wE_hsH*U Rr%|_3_O>V+`f=>le*wEK-wpr( diff --git a/mod/etc/options_menu/images/loading/loading.png b/mod/etc/options_menu/images/loading/loading.png deleted file mode 100644 index 417fd06947ebb08c78fcf59eb4590df31e62b52e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1180 zcmeAS@N?(olHy`uVBq!ia0y~yUqE9fSkjM3MT#s z&Flhun$kZoHHp92#xJT^Ug)u`bGP$uo{pp+f90mlJTtxK_lM;rv)G6Vkp#19(L-^2 zZ*xZO+9^KC{j1USeY{6czA+J9a`F1#^va@XY}sG`UC_O~hBx#d-`3kUUp{e}yihpS zT~)50_A*Uwa_HVOTaRyyI5TafLa*Gn`A<&ra&Z=4>!z>(>M z3X?~09sh&75|1l)b*-M#>?*g{gVE*;=iW1{*Dp^qoz|YZG3&ck_vebZLsowO()RV8 z-g0F|8K=Tz4h3I92Q@*5SxOB)N)0AYuQAkd?md2+>Br(~v2XdD4~}1`zO??&-rrx0 z-#_^3f3Ni8syC;O)=f3pCwbua-NKsrG8ON*(?(@Ste}zGm+t4%iecq z1rB_9ky@cmK5h1 z`ddfxC)|tM{jvPOWrH8LYFD#AnO8Rdb689mbA)(BJV*#-m{=`c= zQjqL!G&3`T%u)D%-7SE8b400>o0i&ID1h7}@a2E$1wK)b%kTlQ8(y!c_uab>EFTyc NJYD@<);T3K0RRIOzQ+In diff --git a/mod/etc/options_menu/images/loading/loadingassets.png b/mod/etc/options_menu/images/loading/loadingassets.png deleted file mode 100644 index 9f253eb603024aa3496c32ac4e95c94f46257904..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4668 zcmeH}`%jZs7{^Zm9Wr3lTd-D`HGuD^+?G;07r>RFP`OwcbV@BE%oGH<3~!@?a~a-1 zH=#INN@TvEBBg*9Y_UZLbV9iqZc-{@Svw(ZEwn&^$X=ZPgh`)Y&Ph)4J)eA@=OpK1 zbmVRaJB%Fwz#(i;2o-=;$kwXYb~Uo}u#~X?thEjc*%fn&t5quYsrIY~y`4t)V7tgC zP_Qk>3bNno1MRi@1wa9-s)B9kVpsp(;otFV9^5Z|Gss7)1|xe2FJTJ=>>Sb7LCf zq;;EB3!f%mNFI$a3>?JrB&5`3n%bORNC*2>Rn%tZkH)D2}6xGDc8K1+x4|yv6 zq#zVNWcJBgK2tMQJl7sSH-1rAHyP8FOy@Ow%9OD8(YlR(1<0+v^lr18W{J^Otvji7 zPm`)gE)Nk~SI5)_>U3~OjYq0QAZ;n<@%vgxWU^zLlmXd>Jt&Gr#nc} z^4!InRMB$!dDh^anC@|BHjkW>Df3Uh{}5pl;CUbN8c^@QC7wT6JAqfD^s)WF-epro zQIHZ#Au}%5@-UHCv^lTe7S~}OTpDQ5_U%Y=MCG5GcL)agoBrV*-PP&$xvcr>mA6oZgBCApSFiPY_t! zr-L|Eoq5wow5sUYd0kak3DyeG(ElGo@a0cJCb(%Y`vZ7_Mc;y;4E}@@(txF#;+0N- zmQ!CN%C1z5uYW}U8R&wpwhnS| ztQxvUjB@>Rd!ytjE%jl)X>!E-5dgProL5Lg zqv}e)F@C=(kAOVEjaja<7fo*Ia$1Gx8p#H%uK0i|M;d7c)pX-$WBGc#zACZ*;r1$AkP#c@eq@J1=*WwoWbK+n`7 zr|D`yVM2KYZRS#;g$>5 z0RW)y?*|V9fNknlcVNd?>hd~gA_4&RQ2pWF$gIkR330N`aUA#dv*mkS;4eGZ*+f4`&-ORE#7qY@2B>cg~4E1(t73TOqi z0$-58PapZi`am2WJWDHNwLK_mDI09=#97%~#LW{xl)UKScSgO0GZ;kZW=N;6utz?C zrC@Wika=QQ%HH045ISehkER&tHe4{zFbI!riQuys9yAuCg5Zk_XO^1aWRLrym__-E zqkOZ>+q5Vb*eRKS_YDx^`Sw=z9SWe~^-{T!eY7 zAVrHAXXb?gtFtcm;(0Mx`rAZlfJJGXsRPw-z(p|JPaQQQjXCKQ;LdXd6ne1QV8L&%MJYZ+>0X4 z010=jA{u2EzB0c(w)TkBG{krxAbp8dXA4i~UETfc^0}lx!p02VSOP!|Nu9K2)ZS*) zsrLupE+@-j^#BbQwixCY76(``y&)z5x!JeuLk*xmm!AD~BiV;}c2UcMo zi9Ku#w*T0e3rRdA`0Qr^O&E$ybs+J-?nU3$dH;=hJ`jj04QtKvg7esTgg61cWL>1+ z@%ap5qKi8->5WigGeQ(&)lb*o6(q0erF2Um7qV}=0hMxsNb?~DfKJKQ10BR3Fmp!CndY7}smx^QPZX}k)Y!@4$w<^NX&b`S_>;z;$(9(Ec^I>PAnHiok4IhQzuK}GIC zvEi{l``54pW-3aXuNdzZ-#l!$i~SkYBWnSZ zryns#B_5vlWz53j{%-#@T7j(U+a+>tyGyQxE^+LMs#&flHvu+nwwY>Tn)L0>2U==v zDfK}y1)1;I)c(aO2dK;$b`dt0Il#SVRJ*=6?|2zTB#@Vd33DPjBg3Pm$)HI-HMn?w zOcqnYL^FsKMkj^S9UsGC9pM{|P&nHk{xjt&K}P6%>C1Y=Ml)eX8Qoo wlX|Y2xSDsasoJF#&$rMF~#%hB5p9+REPR3pTAD!={%m4rY diff --git a/mod/etc/options_menu/images/loading/themepreview.png b/mod/etc/options_menu/images/loading/themepreview.png deleted file mode 100644 index d7f08cd62225af8c673bedd9f0e0f03e4e0588a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1276 zcmeAS@N?(olHy`uVBq!ia0y~yUh^2)ez#} z^zTYSb7uG}N_R#6xSaK^^Ntm-yV-(p_n#kwwoOyao^kc4-S5B@p)09!0?*smyx^Mq z)wzrNaF)Z#9Wi@MKmGrqp?|D)_R=X$-p_JRu$`Rq8)$~c-Kch9=?kmvKRvd|&E)jm zyz55#a?RYPSaXl<=6_mR{a*8TC0%1)qyH_SX5(tpV7W^o?K(pXE2ywy=uv5X9Sg8Vt8QwqJ7(0e^{QXxXz%jQoV7m=)_KaxnrO0#r1D9f4SMl zQ2TKP1HZp>!@93$Ro9mBUbwO5)}P{!D{LL+=kf-u<@oSHK*358=(MTp;+<|W%Tx%( z?rWLS9r`=};NAT#Hx$iTDg-zL?g}U{emhymG-dOTXKJe!cKCdkU$Elh+4E(uV*doq zmOb@(OJ8?^Y^qNK|Is?8JA7yUys~4yQ~7O2)$6&>BbjP6I0WW`Oz7GtlQiw2^Oyc` z*9PstkENxDZ%+MtGt>6K>mR0_Z~jPL5T9IqFPTTkNaC?BnD*l zs^=dmY3yF;&zmaL)5HAE>Fu?iTega>4Zh!7UN{u`dq^gHuLio`WSZ?o8who-~sZM!i)de;WuEu#Yz8Q_1|mQxbw7P97u_$tDnm{r-UW|O1K?; diff --git a/mod/etc/options_menu/themes/creator/scripts/om_applytheme b/mod/etc/options_menu/themes/creator/scripts/om_applytheme index 18e3fb6..288b5a7 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_applytheme +++ b/mod/etc/options_menu/themes/creator/scripts/om_applytheme @@ -19,7 +19,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init -decodepng "$omLoaders/saving.png" >/dev/fb0 +"$omScripts/gen_splash" /tmp/om_splash.png THEME_SAVING && decodepng /tmp/om_splash.png >/dev/fb0 name="diy_theme" i=1 diff --git a/mod/etc/options_menu/themes/creator/scripts/om_preview b/mod/etc/options_menu/themes/creator/scripts/om_preview index c70e973..3ff53a1 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_preview +++ b/mod/etc/options_menu/themes/creator/scripts/om_preview @@ -20,7 +20,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init -decodepng "$omLoaders/themepreview.png" >/dev/fb0 +"$omScripts/gen_splash" /tmp/om_splash.png THEME_PREVIEW_LOADING && decodepng /tmp/om_splash.png >/dev/fb0 if [ ! -f "$b_file" ]; then sed '1,2d' $omThemeScript >"$b_file" echo "themeSelector" >"$p_file" diff --git a/mod/etc/options_menu/themes/lang/en-US.lang b/mod/etc/options_menu/themes/lang/en-US.lang index 58c2a62..614f371 100644 --- a/mod/etc/options_menu/themes/lang/en-US.lang +++ b/mod/etc/options_menu/themes/lang/en-US.lang @@ -34,3 +34,8 @@ CATEGORY_UI_MOD=Category: User Interface Mod CHECKING_THEME_FILES=Checking selected theme files. DOWNLOADING=Downloading... INSTALLATION_COMPLETE=Installation complete. +THEME_LIST_DOWNLOADING=Downloading theme list... please wait +THEME_LOADING=Loading... +THEME_LOADING_ASSETS=Loading assets... +THEME_SAVING=Saving custom theme... +THEME_PREVIEW_LOADING=Loading theme preview... diff --git a/mod/etc/options_menu/themes/lang/pt-PT.lang b/mod/etc/options_menu/themes/lang/pt-PT.lang index b1702c0..dfb92ae 100644 --- a/mod/etc/options_menu/themes/lang/pt-PT.lang +++ b/mod/etc/options_menu/themes/lang/pt-PT.lang @@ -34,3 +34,8 @@ CATEGORY_UI_MOD=Categoria: Mod de Interface CHECKING_THEME_FILES=A verificar os ficheiros do tema selecionado. DOWNLOADING=A descarregar... INSTALLATION_COMPLETE=Instalação concluída. +THEME_LIST_DOWNLOADING=A descarregar lista de temas... aguarda por favor +THEME_LOADING=A carregar... +THEME_LOADING_ASSETS=A carregar recursos... +THEME_SAVING=A guardar tema personalizado... +THEME_PREVIEW_LOADING=A carregar pré-visualização do tema... diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 3bffbac..f4c6cbf 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -36,7 +36,6 @@ themeURL="http://hakchicloud.com/Hakchi_Themes/themes" # Options Menu omMod="$optionsMenu/$modtitle" omModImgs="$optionsMenu/images" -omLoaders="$omModImgs/loading/" omModCommands="$omMod/commands" omModScripts="$omMod/scripts" omModFnts="$omMod/fonts" # verify @@ -351,7 +350,7 @@ removeRegionalTheme() { } getThemeList() { - decodepng "$omLoaders/downloadthemelist.png" > /dev/fb0 + "$omScripts/gen_splash" /tmp/om_splash.png THEME_LIST_DOWNLOADING && decodepng /tmp/om_splash.png > /dev/fb0 cd "$omModSub" || exit if [ ! -e "${s_type}${listName}" ]; then @@ -557,7 +556,7 @@ findDiyAsset() { fi [ "$chars" = "1" ] && temp_preview="$preview_image" if [ -n "$json" ] && [ ! -f "$temp_preview" ]; then - decodepng "$omLoaders/loading.png" > /dev/fb0 + "$omScripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png > /dev/fb0 if [ "$colors" = "0" ]; then sed -r "s/changeme/${1}_${asset_name}/g;" "$om_default_json" > "$json" pngpack-upx u "$json" "$preview_image" "$omModSprites/assets/$1" @@ -618,7 +617,7 @@ diySpriteSelector() { } diyThemeChecker() { - [ "$1" = "loader" ] && decodepng "$omLoaders/loadingassets.png" > /dev/fb0 + [ "$1" = "loader" ] && { "$omScripts/gen_splash" /tmp/om_splash.png THEME_LOADING_ASSETS && decodepng /tmp/om_splash.png > /dev/fb0; } [ ! -d "$diyThemePath" ] && mkdir -p "$diyThemePath" create "$omModGenSpacer" diff --git a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup index e4f570e..1026186 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup +++ b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup @@ -19,7 +19,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init -decodepng "$omLoaders/loading.png" >/dev/fb0 +"$omScripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png >/dev/fb0 modCleanUp diff --git a/mod/uninstall b/mod/uninstall index 662de06..4bbb038 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -73,14 +73,6 @@ remove "$rootfs/etc/options_menu/images/default_preview.png" remove "$rootfs/etc/options_menu/images/diy.png" remove "$rootfs/etc/options_menu/images/download.png" remove "$rootfs/etc/options_menu/images/loading" -remove "$rootfs/etc/options_menu/images/downloadtheme.png" -remove "$rootfs/etc/options_menu/images/downloadthemelist.png" -remove "$rootfs/etc/options_menu/images/themepreview.png" -remove "$rootfs/etc/options_menu/images/downloadtheme.png" -remove "$rootfs/etc/options_menu/images/downloadthemelist.png" -remove "$rootfs/etc/options_menu/images/loading.png" -remove "$rootfs/etc/options_menu/images/themepreview.png" -remove "$rootfs/etc/options_menu/images/loadingassets.png" remove "$rootfs/etc/options_menu/images/luigi.png" remove "$rootfs/etc/options_menu/images/mario.png" remove "$rootfs/etc/options_menu/images/packed.png" @@ -96,7 +88,6 @@ remove "$rootfs/etc/options_menu/images/uninstall.png" remove "$rootfs/etc/options_menu/images/update.png" remove "$rootfs/etc/options_menu/images/wall.png" remove "$rootfs/etc/options_menu/images/wall_far.png" -remove "$rootfs/etc/options_menu/images/loading" remove "$rootfs/etc/usr/bin/pngpack-upx" [ -f "$TITLE_FONT_BACKUP" ] && mv "$TITLE_FONT_BACKUP" "$TITLE_FONT" From 9275198548cd716902e3b2ae7f4568e77bf458d9 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 15 Sep 2026 23:24:09 +0000 Subject: [PATCH 031/210] Add preview icons for audio/theme randomizers, clean up, DIY theme, downloads, and reload --- .gitignore | 2 +- mod/etc/options_menu/images/audioall.png | Bin 6996 -> 0 bytes mod/etc/options_menu/images/audiohome.png | Bin 8390 -> 0 bytes mod/etc/options_menu/images/diy.png | Bin 3316 -> 0 bytes mod/etc/options_menu/images/download.png | Bin 7975 -> 0 bytes .../images/preview_audio_randomizer_all.png | Bin 0 -> 2007 bytes .../images/preview_audio_randomizer_home.png | Bin 0 -> 1848 bytes .../options_menu/images/preview_clean_up.png | Bin 0 -> 847 bytes .../options_menu/images/preview_diy_theme.png | Bin 0 -> 1684 bytes .../images/preview_download_themes.png | Bin 0 -> 1391 bytes .../images/preview_theme_options.png | Bin 5425 -> 3169 bytes .../images/preview_theme_randomizer.png | Bin 0 -> 3034 bytes .../images/preview_update_theme_list.png | Bin 0 -> 2141 bytes .../options_menu/themes/commands/_download | 4 +--- .../themes/commands/c0000_generatetheme | 4 +--- .../downloads/commands/c0000_downloadThemes | 4 +--- .../settings/commands/c0000_themerandomizer | 4 +--- .../settings/commands/c0001_audiorandomizer | 4 +--- .../commands/c0001_audiorandomizerall | 4 +--- .../settings/commands/c0001_systemcleanup | 4 +--- 20 files changed, 8 insertions(+), 22 deletions(-) delete mode 100644 mod/etc/options_menu/images/audioall.png delete mode 100644 mod/etc/options_menu/images/audiohome.png delete mode 100644 mod/etc/options_menu/images/diy.png delete mode 100644 mod/etc/options_menu/images/download.png create mode 100644 mod/etc/options_menu/images/preview_audio_randomizer_all.png create mode 100644 mod/etc/options_menu/images/preview_audio_randomizer_home.png create mode 100644 mod/etc/options_menu/images/preview_clean_up.png create mode 100644 mod/etc/options_menu/images/preview_diy_theme.png create mode 100644 mod/etc/options_menu/images/preview_download_themes.png create mode 100644 mod/etc/options_menu/images/preview_theme_randomizer.png create mode 100644 mod/etc/options_menu/images/preview_update_theme_list.png diff --git a/.gitignore b/.gitignore index 389aa9e..c89dadd 100644 --- a/.gitignore +++ b/.gitignore @@ -551,5 +551,5 @@ out---/ preinit b0010_functions b0050_boot -tools/gen_theme_options_preview.py _console_originals/ +tools/*.py diff --git a/mod/etc/options_menu/images/audioall.png b/mod/etc/options_menu/images/audioall.png deleted file mode 100644 index cb5648a5bf1307f6bfb9a4ad6bfa4431d1167553..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6996 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 QhW~rHID4>9JFeaIZ`5TQ1poj5 diff --git a/mod/etc/options_menu/images/audiohome.png b/mod/etc/options_menu/images/audiohome.png deleted file mode 100644 index c2df81248f61483bb18ee017b3141107b66dac53..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8390 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 diff --git a/mod/etc/options_menu/images/diy.png b/mod/etc/options_menu/images/diy.png deleted file mode 100644 index 053f2817e52f141d655ef54573018464f874eb63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3316 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 V0wSNab5?3DYfC$e`U|(?{|7x~M%(}Z diff --git a/mod/etc/options_menu/images/download.png b/mod/etc/options_menu/images/download.png deleted file mode 100644 index 999c90133daf392bbde84b1fd034840acf9117ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7975 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 GUH=2{w+x>E diff --git a/mod/etc/options_menu/images/preview_audio_randomizer_all.png b/mod/etc/options_menu/images/preview_audio_randomizer_all.png new file mode 100644 index 0000000000000000000000000000000000000000..86678fc45047f7fb4bef5804dd717b4973e76531 GIT binary patch literal 2007 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 literal 0 HcmV?d00001 diff --git a/mod/etc/options_menu/images/preview_diy_theme.png b/mod/etc/options_menu/images/preview_diy_theme.png new file mode 100644 index 0000000000000000000000000000000000000000..309532a80edfc61bdd3e50d3798f002a34bb58b2 GIT binary patch literal 1684 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 literal 5425 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 diff --git a/mod/etc/options_menu/images/preview_theme_randomizer.png b/mod/etc/options_menu/images/preview_theme_randomizer.png new file mode 100644 index 0000000000000000000000000000000000000000..bbba495ee6725a0bd28eb7ee33938907da74b2c9 GIT binary patch literal 3034 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 L=>V@XzxC)p#2COu literal 0 HcmV?d00001 diff --git a/mod/etc/options_menu/images/preview_update_theme_list.png b/mod/etc/options_menu/images/preview_update_theme_list.png new file mode 100644 index 0000000000000000000000000000000000000000..8231ffa3e48ab45f4e93ee53015c6feac850dbe1 GIT binary patch literal 2141 zcmchZ`BTz~8^^&=u}q~bZ$ZR7SIg8^^A-@KG&Ao|OVm7I*E7WwJle=82{p>Jc9k;4 z@+ip4yG+uoB=h>DR@;S!tw)gJvF_jSo%#Ooyq|f^JTuSCGtYJQkgxaF&AOXGAkbDk z4t;pT3pb1jRoYO;i=RJ&Ku{DO?QtZtLNbvpj0rS&I`?H?Ii{S*r1++3Sy@zn8y ziSH`KE*<z7k*zpq_(ocWgXYqs0>h{Oi|2ph!IoF>WHlg*v68n7Ds{(GA> zRO?d?RqfMuiQnW;#pt|Nd(^RMhnqG zJNJ+ML3A}?_rqWfP~z-yiQkq$%XhSve2~Gd<6sp91r!v@LLl7p>7f4seznWq{`hW= zI=+`cE$2p~Ts%dCY_*1l4?%buXVDZtLRjW=c_xw~2FE_~j)9QWlBOa> z1e3O-q5u;E(NAH{%*-*i;P%pbI62ZpyqDds4?Fq%C$GU-!PZLvHx)nEWd@m1_07fH z<%eU^8dRkojHVw2gea3)I*n~_q+d`(OYu-%cXDGkfm+R-5ZKfQo2xSEs=|y>*J^=| zPg#QD5BVb>s@GTzc{p818?3{Sh+Ka*5k#xIMu*R`bCcv;hFP%ffjs(wXVTX*Wz`i!F0-YJMEn$M z(FH$(c`^wDtO4gBQ2?qfL{#D=o5SHpMkftnpQO8QcFiYlU#Ii}lQXXS#&U}#%D%}J zh7|cx;C}k-!cL$!Mse4wHIJ<_j(8o&v*x79u!FR+@Fuq5HdvfDy4Lp~3Q1uw?h~lD zxa#57&alVbSrCsz{_1{RBw(ZN)~nC!3cWq8!Xp zp?vbOFh~p6!`PH8eq{W75Q{?QhAEhdMWK@DH!#**Tc}8iy;6)H-}5mrx|H!VPQqO2 zte5eNphE2=4$u;}uPDbB8KDLHM0s-*U1zlz1?t{Q$I+$N<~-hXoheasZ>?4%J<){Xu5(Phh6()7Rs&eE#;MOt!FjbK zP0OXq*RbY(($$-(`$tJoT!F-RCaTNd)%BZE7t#np2G~>LUHB>8pO>TZG*|-L$@o&9 z!@R&cwwQjetTqd~)J5-A*uvO=EFN~LEY=J;skhgm|9gij6bBTrWf|(3R*iHhF5E#{ zXD4cU0u`o#LuH{UR_gyv5)PPDX|N7afRG;2_3hs^bt)hc zWeXKn7OFkv@}aH6r#w@4D9~dE&j{H^&3nW~?&s5!rZA$!5+vtkYC_dO5q)tfX@1gN z0Kd_*Q1x_2RXzQ*l7a}%T1R!fcML-a)JqoIxx(}8c9-6Ud+{)q8f;Drf8DR>TO)BC zdCm^wFV*k&z7_Gq2(cQLIu((W{WyD=%!w+E$9X>p6=70We6H$(#Yf`e*F6t&pG()` zfx97t0s`^sKRovAB~5u$+Z{97@b|a@$G$G;(acKsSG4<64OoZY z@(w8wFGm8Yaxxk2|M=oT+u%##`(2QI%ya*RoXjN>EmEnWLr*gjvjnwclVPUCnYq

1gv}A~HmtPl>aWMrQ)cwY9lRHs zZZM#CyuIbZZr2BY8{hEyXY5>~)#F(HZxP&P!^L(Q`he4$HJ^sE2u{H)@t|N)NUIS< zx7&yxGFn`&XtgA*7dn|WTaAwK-ZoKv(JL9umw%35SHUj$nj>>b@DXj>8FbR(T0?( z(lET+#k6#Escg3?g!E$i(1N8_W|eZi$Br#H4Gn~cI~@WBZ-YSo&tNF9F>koj-C0D+ zvxL4n$Abn{s@LIfXDj|ycRKK6dbzeT1cmhN-@K_mL9*2DpuB!k(Fhe{ZwR}Pvp&1@ ziVBA~JleRsPbZ*_NzHC+1N3j+hyFBq9bma^GZ4`kS8Ip6x$!oD@EBk89nYxze*sbc B)ZzdD literal 0 HcmV?d00001 diff --git a/mod/etc/options_menu/themes/commands/_download b/mod/etc/options_menu/themes/commands/_download index e7a791d..bfd9d73 100644 --- a/mod/etc/options_menu/themes/commands/_download +++ b/mod/etc/options_menu/themes/commands/_download @@ -2,6 +2,4 @@ COMMAND_NAME=DOWNLOAD_THEMES COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_getThemes -PREVIEW_IMAGE=/etc/options_menu/images/download.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file +PREVIEW_IMAGE=/etc/options_menu/images/preview_download_themes.png \ No newline at end of file diff --git a/mod/etc/options_menu/themes/commands/c0000_generatetheme b/mod/etc/options_menu/themes/commands/c0000_generatetheme index 8f891a9..c3a168b 100644 --- a/mod/etc/options_menu/themes/commands/c0000_generatetheme +++ b/mod/etc/options_menu/themes/commands/c0000_generatetheme @@ -2,6 +2,4 @@ COMMAND_NAME=DIY_THEME COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_themeCreator -PREVIEW_IMAGE=/etc/options_menu/images/diy.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file +PREVIEW_IMAGE=/etc/options_menu/images/preview_diy_theme.png \ No newline at end of file diff --git a/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes b/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes index 5e0da78..f320200 100644 --- a/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes +++ b/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes @@ -2,6 +2,4 @@ COMMAND_NAME=UPDATE_THEME_LIST COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_refreshThemes & -PREVIEW_IMAGE=/etc/options_menu/images/update.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file +PREVIEW_IMAGE=/etc/options_menu/images/preview_update_theme_list.png \ No newline at end of file diff --git a/mod/etc/options_menu/themes/settings/commands/c0000_themerandomizer b/mod/etc/options_menu/themes/settings/commands/c0000_themerandomizer index 1cf04ba..1dcd1de 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0000_themerandomizer +++ b/mod/etc/options_menu/themes/settings/commands/c0000_themerandomizer @@ -2,6 +2,4 @@ COMMAND_NAME=THEME_RANDOMIZER COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_theme & -PREVIEW_IMAGE=/etc/options_menu/images/randtheme.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file +PREVIEW_IMAGE=/etc/options_menu/images/preview_theme_randomizer.png \ No newline at end of file diff --git a/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizer b/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizer index f1dcfee..8eb2ce3 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizer +++ b/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizer @@ -2,6 +2,4 @@ COMMAND_NAME=AUDIO_RANDOMIZER_HOME COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_audio & -PREVIEW_IMAGE=/etc/options_menu/images/audiohome.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file +PREVIEW_IMAGE=/etc/options_menu/images/preview_audio_randomizer_home.png \ No newline at end of file diff --git a/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizerall b/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizerall index dcef1db..7caac16 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizerall +++ b/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizerall @@ -3,6 +3,4 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE CHILD=TRUE COMMAND_STR=usleep 50000 && sh %script_dir%/om_audio -amh & -PREVIEW_IMAGE=/etc/options_menu/images/audioall.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file +PREVIEW_IMAGE=/etc/options_menu/images/preview_audio_randomizer_all.png \ No newline at end of file diff --git a/mod/etc/options_menu/themes/settings/commands/c0001_systemcleanup b/mod/etc/options_menu/themes/settings/commands/c0001_systemcleanup index fc98774..564e066 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0001_systemcleanup +++ b/mod/etc/options_menu/themes/settings/commands/c0001_systemcleanup @@ -2,6 +2,4 @@ COMMAND_NAME=CLEAN_UP COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_systemcleanup & -PREVIEW_IMAGE=/etc/options_menu/images/uninstall.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file +PREVIEW_IMAGE=/etc/options_menu/images/preview_clean_up.png \ No newline at end of file From 9b356e0b5f1d2935db05fb7a8062ad14606e8a04 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 15 Sep 2026 23:30:34 +0000 Subject: [PATCH 032/210] Add fr-FR, de-DE, es-ES, it-IT, nl-NL, ru-RU, ja-JP translations, matching OptionsMenu's 9 supported languages --- mod/etc/options_menu/themes/lang/de-DE.lang | 42 +++++++++++++++++++++ mod/etc/options_menu/themes/lang/en-US.lang | 3 +- mod/etc/options_menu/themes/lang/es-ES.lang | 42 +++++++++++++++++++++ mod/etc/options_menu/themes/lang/fr-FR.lang | 42 +++++++++++++++++++++ mod/etc/options_menu/themes/lang/it-IT.lang | 42 +++++++++++++++++++++ mod/etc/options_menu/themes/lang/ja-JP.lang | 42 +++++++++++++++++++++ mod/etc/options_menu/themes/lang/nl-NL.lang | 42 +++++++++++++++++++++ mod/etc/options_menu/themes/lang/pt-PT.lang | 3 +- mod/etc/options_menu/themes/lang/ru-RU.lang | 42 +++++++++++++++++++++ mod/etc/options_menu/themes/scripts/om_vars | 2 +- 10 files changed, 299 insertions(+), 3 deletions(-) create mode 100644 mod/etc/options_menu/themes/lang/de-DE.lang create mode 100644 mod/etc/options_menu/themes/lang/es-ES.lang create mode 100644 mod/etc/options_menu/themes/lang/fr-FR.lang create mode 100644 mod/etc/options_menu/themes/lang/it-IT.lang create mode 100644 mod/etc/options_menu/themes/lang/ja-JP.lang create mode 100644 mod/etc/options_menu/themes/lang/nl-NL.lang create mode 100644 mod/etc/options_menu/themes/lang/ru-RU.lang diff --git a/mod/etc/options_menu/themes/lang/de-DE.lang b/mod/etc/options_menu/themes/lang/de-DE.lang new file mode 100644 index 0000000..e7f6b06 --- /dev/null +++ b/mod/etc/options_menu/themes/lang/de-DE.lang @@ -0,0 +1,42 @@ +THEME_OPTIONS=Themes +THEME_SELECTOR=Themenauswahl +DIY_THEME=Eigenes Theme +THEME_SETTINGS=Einstellungen +REFRESH=Aktualisieren +REFRESH_ASSETS=Assets aktualisieren +BASE_UI=Basis-UI +BACKGROUND=Hintergrund +DEMO_CHARACTER_1_MARIO=Demo: Charakter Nr. 1 - Mario +DEMO_CHARACTERS=Demo: Charaktere +DEMO_CHARACTER_2_LUIGI=Demo: Charakter Nr. 2 - Luigi +DEMO_BACKGROUND_NEAR=Demo: Hintergrund nah +DEMO_BACKGROUND_FAR=Demo: Hintergrund fern +COLORS=Farben +BACKGROUND_COLOR=Hintergrundfarbe +DEMO_POLE=Demo: Mast +SAVE_CUSTOM_THEME=Eigenes Theme speichern +CLEAN_CUSTOM_THEME=Eigenes Theme löschen +PREVIEW_CUSTOM_THEME=Eigenes Theme in der Vorschau ansehen +UPDATE_THEME_LIST=Themenliste aktualisieren +CLEAR_LIST=Liste leeren +DOWNLOAD_THEMES=Themes herunterladen +THEME_PER_FOLDER=Theme pro Ordner +ALLOW_DIFFERENT_THEME_SUBFOLDER=Anderes Theme für Unterordner erlauben +THEME_RANDOMIZER=Zufälliges Theme +AUDIO_RANDOMIZER_HOME=Zufalls-Audio: Home +AUDIO_RANDOMIZER_ALL_FOLDERS=Alle Ordner (nur Themes ohne JEGLICHE Audiodateien) +CLEAN_UP=Aufräumen +RESET_SETTINGS=Einstellungen zurücksetzen +ASSET_APPLIED_SUCCESS=Asset erfolgreich angewendet! +SAVE_PREVIEW_HINT_LINE1=Vergiss nicht, nach der Auswahl deiner Assets 'Eigenes Theme speichern' auszuwählen, oder wähle einfach +SAVE_PREVIEW_HINT_LINE2='Eigenes Theme in der Vorschau ansehen', um eine Vorschau zu sehen, bevor du dein Theme speicherst. +CATEGORY_UI_MOD=Kategorie: Benutzeroberflächen-Mod +CHECKING_THEME_FILES=Dateien des ausgewählten Themes werden geprüft. +DOWNLOADING=Wird heruntergeladen... +INSTALLATION_COMPLETE=Installation abgeschlossen. +THEME_LIST_DOWNLOADING_LINE1=Themenliste wird heruntergeladen +THEME_LIST_DOWNLOADING_LINE2=Bitte warten . . . +THEME_LOADING=Wird geladen... +THEME_LOADING_ASSETS=Assets werden geladen... +THEME_SAVING=Eigenes Theme wird gespeichert... +THEME_PREVIEW_LOADING=Themenvorschau wird geladen... diff --git a/mod/etc/options_menu/themes/lang/en-US.lang b/mod/etc/options_menu/themes/lang/en-US.lang index 614f371..14058b1 100644 --- a/mod/etc/options_menu/themes/lang/en-US.lang +++ b/mod/etc/options_menu/themes/lang/en-US.lang @@ -34,7 +34,8 @@ CATEGORY_UI_MOD=Category: User Interface Mod CHECKING_THEME_FILES=Checking selected theme files. DOWNLOADING=Downloading... INSTALLATION_COMPLETE=Installation complete. -THEME_LIST_DOWNLOADING=Downloading theme list... please wait +THEME_LIST_DOWNLOADING_LINE1=Downloading Theme List +THEME_LIST_DOWNLOADING_LINE2=Please Wait . . . THEME_LOADING=Loading... THEME_LOADING_ASSETS=Loading assets... THEME_SAVING=Saving custom theme... diff --git a/mod/etc/options_menu/themes/lang/es-ES.lang b/mod/etc/options_menu/themes/lang/es-ES.lang new file mode 100644 index 0000000..247e053 --- /dev/null +++ b/mod/etc/options_menu/themes/lang/es-ES.lang @@ -0,0 +1,42 @@ +THEME_OPTIONS=Temas +THEME_SELECTOR=Selector de temas +DIY_THEME=Tema personalizado +THEME_SETTINGS=Ajustes +REFRESH=Actualizar +REFRESH_ASSETS=Actualizar recursos +BASE_UI=Interfaz base +BACKGROUND=Fondo +DEMO_CHARACTER_1_MARIO=Demo: Personaje 1 - Mario +DEMO_CHARACTERS=Demo: Personajes +DEMO_CHARACTER_2_LUIGI=Demo: Personaje 2 - Luigi +DEMO_BACKGROUND_NEAR=Demo: Fondo cercano +DEMO_BACKGROUND_FAR=Demo: Fondo lejano +COLORS=Colores +BACKGROUND_COLOR=Color de fondo +DEMO_POLE=Demo: Poste +SAVE_CUSTOM_THEME=Guardar tema personalizado +CLEAN_CUSTOM_THEME=Limpiar tema personalizado +PREVIEW_CUSTOM_THEME=Vista previa del tema personalizado +UPDATE_THEME_LIST=Actualizar lista de temas +CLEAR_LIST=Vaciar lista +DOWNLOAD_THEMES=Descargar temas +THEME_PER_FOLDER=Tema por carpeta +ALLOW_DIFFERENT_THEME_SUBFOLDER=Permitir tema distinto en la subcarpeta +THEME_RANDOMIZER=Tema aleatorio +AUDIO_RANDOMIZER_HOME=Audio aleatorio: Inicio +AUDIO_RANDOMIZER_ALL_FOLDERS=Todas las carpetas (solo temas sin NINGÚN archivo de audio) +CLEAN_UP=Limpieza +RESET_SETTINGS=Restablecer ajustes +ASSET_APPLIED_SUCCESS=¡Recurso aplicado correctamente! +SAVE_PREVIEW_HINT_LINE1=No olvides seleccionar 'Guardar tema personalizado' cuando termines de elegir tus recursos, o simplemente selecciona +SAVE_PREVIEW_HINT_LINE2='Vista previa del tema personalizado', para ver una vista previa antes de guardar tu tema. +CATEGORY_UI_MOD=Categoría: Mod de interfaz +CHECKING_THEME_FILES=Comprobando los archivos del tema seleccionado. +DOWNLOADING=Descargando... +INSTALLATION_COMPLETE=Instalación completada. +THEME_LIST_DOWNLOADING_LINE1=Descargando Lista de Temas +THEME_LIST_DOWNLOADING_LINE2=Espere Por Favor . . . +THEME_LOADING=Cargando... +THEME_LOADING_ASSETS=Cargando recursos... +THEME_SAVING=Guardando tema personalizado... +THEME_PREVIEW_LOADING=Cargando vista previa del tema... diff --git a/mod/etc/options_menu/themes/lang/fr-FR.lang b/mod/etc/options_menu/themes/lang/fr-FR.lang new file mode 100644 index 0000000..dd099a1 --- /dev/null +++ b/mod/etc/options_menu/themes/lang/fr-FR.lang @@ -0,0 +1,42 @@ +THEME_OPTIONS=Thèmes +THEME_SELECTOR=Sélecteur de thèmes +DIY_THEME=Thème personnalisé +THEME_SETTINGS=Réglages +REFRESH=Actualiser +REFRESH_ASSETS=Actualiser les ressources +BASE_UI=Interface de base +BACKGROUND=Arrière-plan +DEMO_CHARACTER_1_MARIO=Démo : Personnage #1 - Mario +DEMO_CHARACTERS=Démo : Personnages +DEMO_CHARACTER_2_LUIGI=Démo : Personnage #2 - Luigi +DEMO_BACKGROUND_NEAR=Démo : Arrière-plan proche +DEMO_BACKGROUND_FAR=Démo : Arrière-plan éloigné +COLORS=Couleurs +BACKGROUND_COLOR=Couleur d'arrière-plan +DEMO_POLE=Démo : Poteau +SAVE_CUSTOM_THEME=Enregistrer le thème personnalisé +CLEAN_CUSTOM_THEME=Nettoyer le thème personnalisé +PREVIEW_CUSTOM_THEME=Aperçu du thème personnalisé +UPDATE_THEME_LIST=Mettre à jour la liste des thèmes +CLEAR_LIST=Vider la liste +DOWNLOAD_THEMES=Télécharger des thèmes +THEME_PER_FOLDER=Thème par dossier +ALLOW_DIFFERENT_THEME_SUBFOLDER=Autoriser un thème différent pour le sous-dossier +THEME_RANDOMIZER=Thème aléatoire +AUDIO_RANDOMIZER_HOME=Audio aléatoire : Accueil +AUDIO_RANDOMIZER_ALL_FOLDERS=Tous les dossiers (thèmes sans AUCUN fichier audio) +CLEAN_UP=Nettoyage +RESET_SETTINGS=Réinitialiser les réglages +ASSET_APPLIED_SUCCESS=Ressource appliquée avec succès ! +SAVE_PREVIEW_HINT_LINE1=N'oublie pas de sélectionner « Enregistrer le thème personnalisé » quand tu as fini de choisir tes ressources, ou sélectionne simplement +SAVE_PREVIEW_HINT_LINE2=« Aperçu du thème personnalisé », pour voir un aperçu avant d'enregistrer ton thème. +CATEGORY_UI_MOD=Catégorie : Mod d'interface +CHECKING_THEME_FILES=Vérification des fichiers du thème sélectionné. +DOWNLOADING=Téléchargement... +INSTALLATION_COMPLETE=Installation terminée. +THEME_LIST_DOWNLOADING_LINE1=Téléchargement de la Liste des Thèmes +THEME_LIST_DOWNLOADING_LINE2=Veuillez Patienter . . . +THEME_LOADING=Chargement... +THEME_LOADING_ASSETS=Chargement des ressources... +THEME_SAVING=Enregistrement du thème personnalisé... +THEME_PREVIEW_LOADING=Chargement de l'aperçu du thème... diff --git a/mod/etc/options_menu/themes/lang/it-IT.lang b/mod/etc/options_menu/themes/lang/it-IT.lang new file mode 100644 index 0000000..d4db285 --- /dev/null +++ b/mod/etc/options_menu/themes/lang/it-IT.lang @@ -0,0 +1,42 @@ +THEME_OPTIONS=Temi +THEME_SELECTOR=Selettore temi +DIY_THEME=Tema personalizzato +THEME_SETTINGS=Impostazioni +REFRESH=Aggiorna +REFRESH_ASSETS=Aggiorna risorse +BASE_UI=Interfaccia base +BACKGROUND=Sfondo +DEMO_CHARACTER_1_MARIO=Demo: Personaggio #1 - Mario +DEMO_CHARACTERS=Demo: Personaggi +DEMO_CHARACTER_2_LUIGI=Demo: Personaggio #2 - Luigi +DEMO_BACKGROUND_NEAR=Demo: Sfondo vicino +DEMO_BACKGROUND_FAR=Demo: Sfondo lontano +COLORS=Colori +BACKGROUND_COLOR=Colore di sfondo +DEMO_POLE=Demo: Palo +SAVE_CUSTOM_THEME=Salva tema personalizzato +CLEAN_CUSTOM_THEME=Pulisci tema personalizzato +PREVIEW_CUSTOM_THEME=Anteprima tema personalizzato +UPDATE_THEME_LIST=Aggiorna elenco temi +CLEAR_LIST=Svuota elenco +DOWNLOAD_THEMES=Scarica temi +THEME_PER_FOLDER=Tema per cartella +ALLOW_DIFFERENT_THEME_SUBFOLDER=Consenti tema diverso nella sottocartella +THEME_RANDOMIZER=Tema casuale +AUDIO_RANDOMIZER_HOME=Audio casuale: Home +AUDIO_RANDOMIZER_ALL_FOLDERS=Tutte le cartelle (solo temi senza ALCUN file audio) +CLEAN_UP=Pulizia +RESET_SETTINGS=Ripristina impostazioni +ASSET_APPLIED_SUCCESS=Risorsa applicata con successo! +SAVE_PREVIEW_HINT_LINE1=Non dimenticare di selezionare 'Salva tema personalizzato' quando hai finito di scegliere le risorse, oppure seleziona semplicemente +SAVE_PREVIEW_HINT_LINE2='Anteprima tema personalizzato', per vedere un'anteprima prima di salvare il tema. +CATEGORY_UI_MOD=Categoria: Mod interfaccia utente +CHECKING_THEME_FILES=Controllo dei file del tema selezionato. +DOWNLOADING=Download in corso... +INSTALLATION_COMPLETE=Installazione completata. +THEME_LIST_DOWNLOADING_LINE1=Download Elenco Temi in Corso +THEME_LIST_DOWNLOADING_LINE2=Attendere Prego . . . +THEME_LOADING=Caricamento... +THEME_LOADING_ASSETS=Caricamento risorse... +THEME_SAVING=Salvataggio tema personalizzato... +THEME_PREVIEW_LOADING=Caricamento anteprima tema... diff --git a/mod/etc/options_menu/themes/lang/ja-JP.lang b/mod/etc/options_menu/themes/lang/ja-JP.lang new file mode 100644 index 0000000..e8ace8d --- /dev/null +++ b/mod/etc/options_menu/themes/lang/ja-JP.lang @@ -0,0 +1,42 @@ +THEME_OPTIONS=Theme +THEME_SELECTOR=Themeせんたく +DIY_THEME=じぶんのTheme +THEME_SETTINGS=せってい +REFRESH=さいよみこみ +REFRESH_ASSETS=Assetをさいよみこみ +BASE_UI=きほんUI +BACKGROUND=はいけい +DEMO_CHARACTER_1_MARIO=Demo: Character1 - Mario +DEMO_CHARACTERS=Demo: Characters +DEMO_CHARACTER_2_LUIGI=Demo: Character2 - Luigi +DEMO_BACKGROUND_NEAR=Demo: はいけい(ちかく) +DEMO_BACKGROUND_FAR=Demo: はいけい(とおく) +COLORS=いろ +BACKGROUND_COLOR=はいけいのいろ +DEMO_POLE=Demo: Pole +SAVE_CUSTOM_THEME=じぶんのThemeをほぞん +CLEAN_CUSTOM_THEME=じぶんのThemeをさくじょ +PREVIEW_CUSTOM_THEME=じぶんのThemeをPreview +UPDATE_THEME_LIST=Themeいちらんをこうしん +CLEAR_LIST=いちらんをさくじょ +DOWNLOAD_THEMES=Themeをdownload +THEME_PER_FOLDER=Folderごとのtheme +ALLOW_DIFFERENT_THEME_SUBFOLDER=サブFolderにちがうThemeをゆるす +THEME_RANDOMIZER=Themeをrandomに +AUDIO_RANDOMIZER_HOME=Audioをrandomに: Home +AUDIO_RANDOMIZER_ALL_FOLDERS=すべてのFolder (Audioファイルが1つもないThemeのみ) +CLEAN_UP=せいり +RESET_SETTINGS=せっていをreset +ASSET_APPLIED_SUCCESS=Assetをてきようしました! +SAVE_PREVIEW_HINT_LINE1=Assetをえらびおわったら「じぶんのThemeをほぞん」をわすれずに、または +SAVE_PREVIEW_HINT_LINE2=「じぶんのThemeをPreview」をえらぶと、ほぞんまえにかくにんできます。 +CATEGORY_UI_MOD=カテゴリ: UI Mod +CHECKING_THEME_FILES=えらんだThemeのFileをかくにんしています +DOWNLOADING=downloadしています... +INSTALLATION_COMPLETE=インストールかんりょう +THEME_LIST_DOWNLOADING_LINE1=Themeいちらんを download しています +THEME_LIST_DOWNLOADING_LINE2=おまちください . . . +THEME_LOADING=よみこみちゅう... +THEME_LOADING_ASSETS=Assetをよみこみちゅう... +THEME_SAVING=じぶんのThemeをほぞんちゅう... +THEME_PREVIEW_LOADING=Themeのpreviewをよみこみちゅう... diff --git a/mod/etc/options_menu/themes/lang/nl-NL.lang b/mod/etc/options_menu/themes/lang/nl-NL.lang new file mode 100644 index 0000000..29f8204 --- /dev/null +++ b/mod/etc/options_menu/themes/lang/nl-NL.lang @@ -0,0 +1,42 @@ +THEME_OPTIONS=Thema's +THEME_SELECTOR=Themakiezer +DIY_THEME=Eigen thema +THEME_SETTINGS=Instellingen +REFRESH=Vernieuwen +REFRESH_ASSETS=Assets vernieuwen +BASE_UI=Basisinterface +BACKGROUND=Achtergrond +DEMO_CHARACTER_1_MARIO=Demo: Personage #1 - Mario +DEMO_CHARACTERS=Demo: Personages +DEMO_CHARACTER_2_LUIGI=Demo: Personage #2 - Luigi +DEMO_BACKGROUND_NEAR=Demo: Achtergrond dichtbij +DEMO_BACKGROUND_FAR=Demo: Achtergrond veraf +COLORS=Kleuren +BACKGROUND_COLOR=Achtergrondkleur +DEMO_POLE=Demo: Paal +SAVE_CUSTOM_THEME=Eigen thema opslaan +CLEAN_CUSTOM_THEME=Eigen thema wissen +PREVIEW_CUSTOM_THEME=Voorbeeld van eigen thema +UPDATE_THEME_LIST=Themalijst bijwerken +CLEAR_LIST=Lijst wissen +DOWNLOAD_THEMES=Thema's downloaden +THEME_PER_FOLDER=Thema per map +ALLOW_DIFFERENT_THEME_SUBFOLDER=Ander thema voor submap toestaan +THEME_RANDOMIZER=Willekeurig thema +AUDIO_RANDOMIZER_HOME=Willekeurige audio: Start +AUDIO_RANDOMIZER_ALL_FOLDERS=Alle mappen (alleen thema's zonder ENIG audiobestand) +CLEAN_UP=Opschonen +RESET_SETTINGS=Instellingen herstellen +ASSET_APPLIED_SUCCESS=Asset succesvol toegepast! +SAVE_PREVIEW_HINT_LINE1=Vergeet niet 'Eigen thema opslaan' te kiezen als je klaar bent met het kiezen van je assets, of kies gewoon +SAVE_PREVIEW_HINT_LINE2='Voorbeeld van eigen thema', om een voorbeeld te zien voordat je je thema opslaat. +CATEGORY_UI_MOD=Categorie: Interface-mod +CHECKING_THEME_FILES=Bestanden van het gekozen thema worden gecontroleerd. +DOWNLOADING=Downloaden... +INSTALLATION_COMPLETE=Installatie voltooid. +THEME_LIST_DOWNLOADING_LINE1=Themalijst Wordt Gedownload +THEME_LIST_DOWNLOADING_LINE2=Even Geduld . . . +THEME_LOADING=Laden... +THEME_LOADING_ASSETS=Assets worden geladen... +THEME_SAVING=Eigen thema wordt opgeslagen... +THEME_PREVIEW_LOADING=Themavoorbeeld wordt geladen... diff --git a/mod/etc/options_menu/themes/lang/pt-PT.lang b/mod/etc/options_menu/themes/lang/pt-PT.lang index dfb92ae..6133f2a 100644 --- a/mod/etc/options_menu/themes/lang/pt-PT.lang +++ b/mod/etc/options_menu/themes/lang/pt-PT.lang @@ -34,7 +34,8 @@ CATEGORY_UI_MOD=Categoria: Mod de Interface CHECKING_THEME_FILES=A verificar os ficheiros do tema selecionado. DOWNLOADING=A descarregar... INSTALLATION_COMPLETE=Instalação concluída. -THEME_LIST_DOWNLOADING=A descarregar lista de temas... aguarda por favor +THEME_LIST_DOWNLOADING_LINE1=A Descarregar Lista de Temas +THEME_LIST_DOWNLOADING_LINE2=Aguarde Por Favor . . . THEME_LOADING=A carregar... THEME_LOADING_ASSETS=A carregar recursos... THEME_SAVING=A guardar tema personalizado... diff --git a/mod/etc/options_menu/themes/lang/ru-RU.lang b/mod/etc/options_menu/themes/lang/ru-RU.lang new file mode 100644 index 0000000..bf4eb30 --- /dev/null +++ b/mod/etc/options_menu/themes/lang/ru-RU.lang @@ -0,0 +1,42 @@ +THEME_OPTIONS=Темы +THEME_SELECTOR=Выбор тем +DIY_THEME=Своя тема +THEME_SETTINGS=Настройки +REFRESH=Обновить +REFRESH_ASSETS=Обновить ресурсы +BASE_UI=Базовый интерфейс +BACKGROUND=Фон +DEMO_CHARACTER_1_MARIO=Демо: Персонаж №1 - Марио +DEMO_CHARACTERS=Демо: Персонажи +DEMO_CHARACTER_2_LUIGI=Демо: Персонаж №2 - Луиджи +DEMO_BACKGROUND_NEAR=Демо: Ближний фон +DEMO_BACKGROUND_FAR=Демо: Дальний фон +COLORS=Цвета +BACKGROUND_COLOR=Цвет фона +DEMO_POLE=Демо: Столб +SAVE_CUSTOM_THEME=Сохранить свою тему +CLEAN_CUSTOM_THEME=Очистить свою тему +PREVIEW_CUSTOM_THEME=Просмотр своей темы +UPDATE_THEME_LIST=Обновить список тем +CLEAR_LIST=Очистить список +DOWNLOAD_THEMES=Скачать темы +THEME_PER_FOLDER=Тема для каждой папки +ALLOW_DIFFERENT_THEME_SUBFOLDER=Разрешить другую тему для подпапки +THEME_RANDOMIZER=Случайная тема +AUDIO_RANDOMIZER_HOME=Случайное аудио: Главное меню +AUDIO_RANDOMIZER_ALL_FOLDERS=Все папки (только темы БЕЗ аудиофайлов) +CLEAN_UP=Очистка +RESET_SETTINGS=Сбросить настройки +ASSET_APPLIED_SUCCESS=Ресурс успешно применён! +SAVE_PREVIEW_HINT_LINE1=Не забудьте выбрать «Сохранить свою тему», когда закончите выбирать ресурсы, или просто выберите +SAVE_PREVIEW_HINT_LINE2=«Просмотр своей темы», чтобы увидеть предпросмотр перед сохранением темы. +CATEGORY_UI_MOD=Категория: Мод интерфейса +CHECKING_THEME_FILES=Проверка файлов выбранной темы. +DOWNLOADING=Загрузка... +INSTALLATION_COMPLETE=Установка завершена. +THEME_LIST_DOWNLOADING_LINE1=Загрузка Списка Тем +THEME_LIST_DOWNLOADING_LINE2=Пожалуйста, подождите . . . +THEME_LOADING=Загрузка... +THEME_LOADING_ASSETS=Загрузка ресурсов... +THEME_SAVING=Сохранение своей темы... +THEME_PREVIEW_LOADING=Загрузка предпросмотра темы... diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index f4c6cbf..66b6049 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -350,7 +350,7 @@ removeRegionalTheme() { } getThemeList() { - "$omScripts/gen_splash" /tmp/om_splash.png THEME_LIST_DOWNLOADING && decodepng /tmp/om_splash.png > /dev/fb0 + "$omScripts/gen_splash" /tmp/om_splash.png THEME_LIST_DOWNLOADING_LINE1 THEME_LIST_DOWNLOADING_LINE2 && decodepng /tmp/om_splash.png > /dev/fb0 cd "$omModSub" || exit if [ ! -e "${s_type}${listName}" ]; then From 295352105128f9a36b81e93d9083f72699f28a8e Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 15 Sep 2026 23:38:14 +0000 Subject: [PATCH 033/210] fix SC1091 --- mod/etc/options_menu/themes/creator/scripts/om_applytheme | 2 +- mod/etc/options_menu/themes/creator/scripts/om_diy_asset | 2 +- mod/etc/options_menu/themes/creator/scripts/om_preview | 2 +- mod/etc/options_menu/themes/creator/scripts/om_refresh | 2 +- mod/etc/options_menu/themes/creator/scripts/om_removediy | 2 +- .../themes/creator/sprites/colors/scripts/om_return | 2 +- .../themes/creator/sprites/colors_nes/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/luigi/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/mario/scripts/om_return | 2 +- .../themes/creator/sprites/packed/scripts/om_return | 2 +- .../themes/creator/sprites/packed_nes/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/pole/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/sbm/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/wall/scripts/om_return | 2 +- .../themes/creator/sprites/wall_far/scripts/om_return | 2 +- .../themes/creator/sprites/wall_near/scripts/om_return | 2 +- mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads | 2 +- mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes | 2 +- mod/etc/options_menu/themes/downloads/scripts/om_return | 2 +- mod/etc/options_menu/themes/scripts/om_clearList | 2 +- mod/etc/options_menu/themes/scripts/om_getThemes | 2 +- mod/etc/options_menu/themes/scripts/om_license | 2 +- mod/etc/options_menu/themes/scripts/om_restorethumb | 2 +- mod/etc/options_menu/themes/scripts/om_settings | 2 +- mod/etc/options_menu/themes/scripts/om_themeCreator | 2 +- mod/etc/options_menu/themes/settings/scripts/om_audio | 2 +- mod/etc/options_menu/themes/settings/scripts/om_reset | 2 +- mod/etc/options_menu/themes/settings/scripts/om_systemcleanup | 2 +- mod/etc/options_menu/themes/settings/scripts/om_theme | 2 +- 29 files changed, 29 insertions(+), 29 deletions(-) diff --git a/mod/etc/options_menu/themes/creator/scripts/om_applytheme b/mod/etc/options_menu/themes/creator/scripts/om_applytheme index 288b5a7..8df2adb 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_applytheme +++ b/mod/etc/options_menu/themes/creator/scripts/om_applytheme @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset index d82dd08..dc140f8 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset +++ b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/scripts/om_preview b/mod/etc/options_menu/themes/creator/scripts/om_preview index 3ff53a1..53ff181 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_preview +++ b/mod/etc/options_menu/themes/creator/scripts/om_preview @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/scripts/om_refresh b/mod/etc/options_menu/themes/creator/scripts/om_refresh index 0c27916..2c369bf 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_refresh +++ b/mod/etc/options_menu/themes/creator/scripts/om_refresh @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/scripts/om_removediy b/mod/etc/options_menu/themes/creator/scripts/om_removediy index 8286967..74f5f9c 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_removediy +++ b/mod/etc/options_menu/themes/creator/scripts/om_removediy @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return index d1eaf26..037dc6c 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return index d1eaf26..037dc6c 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return index d1eaf26..037dc6c 100644 --- a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return index e9c8591..a92669e 100644 --- a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return index e9c8591..a92669e 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return index e9c8591..a92669e 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return index e9c8591..a92669e 100644 --- a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return index e9c8591..a92669e 100644 --- a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return index e9c8591..a92669e 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return index e9c8591..a92669e 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return index e9c8591..a92669e 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads index f50e81d..cef7160 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads +++ b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes index 0bdaaa2..1d4beb1 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes +++ b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_return b/mod/etc/options_menu/themes/downloads/scripts/om_return index 356052c..6c43562 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_return +++ b/mod/etc/options_menu/themes/downloads/scripts/om_return @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init remove "$omDummy" diff --git a/mod/etc/options_menu/themes/scripts/om_clearList b/mod/etc/options_menu/themes/scripts/om_clearList index 05e66dd..8ec6f83 100644 --- a/mod/etc/options_menu/themes/scripts/om_clearList +++ b/mod/etc/options_menu/themes/scripts/om_clearList @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/scripts/om_getThemes b/mod/etc/options_menu/themes/scripts/om_getThemes index 7388749..bf79aed 100644 --- a/mod/etc/options_menu/themes/scripts/om_getThemes +++ b/mod/etc/options_menu/themes/scripts/om_getThemes @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/scripts/om_license b/mod/etc/options_menu/themes/scripts/om_license index fb7451d..4dc040c 100644 --- a/mod/etc/options_menu/themes/scripts/om_license +++ b/mod/etc/options_menu/themes/scripts/om_license @@ -16,6 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/scripts/om_restorethumb b/mod/etc/options_menu/themes/scripts/om_restorethumb index 4492113..fcb213d 100644 --- a/mod/etc/options_menu/themes/scripts/om_restorethumb +++ b/mod/etc/options_menu/themes/scripts/om_restorethumb @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/scripts/om_settings b/mod/etc/options_menu/themes/scripts/om_settings index cfdf1af..7730c03 100644 --- a/mod/etc/options_menu/themes/scripts/om_settings +++ b/mod/etc/options_menu/themes/scripts/om_settings @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/scripts/om_themeCreator b/mod/etc/options_menu/themes/scripts/om_themeCreator index d3049c3..7355ac9 100644 --- a/mod/etc/options_menu/themes/scripts/om_themeCreator +++ b/mod/etc/options_menu/themes/scripts/om_themeCreator @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/settings/scripts/om_audio b/mod/etc/options_menu/themes/settings/scripts/om_audio index 8bc3829..73f7f69 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_audio +++ b/mod/etc/options_menu/themes/settings/scripts/om_audio @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/settings/scripts/om_reset b/mod/etc/options_menu/themes/settings/scripts/om_reset index 1c5fe85..f0657ae 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_reset +++ b/mod/etc/options_menu/themes/settings/scripts/om_reset @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup index 1026186..8f62e22 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup +++ b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init diff --git a/mod/etc/options_menu/themes/settings/scripts/om_theme b/mod/etc/options_menu/themes/settings/scripts/om_theme index 99bb8f6..f01231c 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_theme +++ b/mod/etc/options_menu/themes/settings/scripts/om_theme @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# shellcheck source=mod/etc/options_menu/themes/scripts/om_vars +# shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init From f8f6e91d8b521af624e96b213ee508823e026f80 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 15 Sep 2026 23:55:32 +0000 Subject: [PATCH 034/210] Remove orphaned PNGs and obsolete c9998/c9999 files, backfill uninstall cleanup --- mod/etc/options_menu/images/advanced.png | Bin 2192 -> 0 bytes mod/etc/options_menu/images/audio.png | Bin 1634 -> 0 bytes mod/etc/options_menu/images/exit.png | Bin 1369 -> 0 bytes mod/etc/options_menu/images/retroarch.png | Bin 1471 -> 0 bytes mod/etc/options_menu/images/shutdown.png | Bin 2071 -> 0 bytes .../options_menu/themes/commands/c9998_separator | 0 mod/etc/options_menu/themes/commands/c9999_Back | 7 ------- mod/etc/options_menu/themes/commands/c9999_Exit | 7 ------- mod/uninstall | 13 +++++++++++++ 9 files changed, 13 insertions(+), 14 deletions(-) delete mode 100644 mod/etc/options_menu/images/advanced.png delete mode 100644 mod/etc/options_menu/images/audio.png delete mode 100644 mod/etc/options_menu/images/exit.png delete mode 100644 mod/etc/options_menu/images/retroarch.png delete mode 100644 mod/etc/options_menu/images/shutdown.png delete mode 100644 mod/etc/options_menu/themes/commands/c9998_separator delete mode 100644 mod/etc/options_menu/themes/commands/c9999_Back delete mode 100644 mod/etc/options_menu/themes/commands/c9999_Exit diff --git a/mod/etc/options_menu/images/advanced.png b/mod/etc/options_menu/images/advanced.png deleted file mode 100644 index 291cd8fc6d1474cd5d1c625d183d01ba889f42b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2192 zcma);X*3&%0)^9})Ea9nL+muAnTkOvVi#(yhMw9g(^8|9wjw4;rHYoQt$kF*E|zC0 zHIhf{1f@d7(paLkR54OwPx1VkU+>4e=iKw1bI<*M?+bTV2QgtqVE_OihH$j?1OU$X zomz&U?=*?s+*dt?j{7w)yHlS0AF4HXx^h|-d*tbQ9RSnQmJ}0EP?J5ce8Ez*oO`vH)ee~o~djAJRI+pK1K`(7= zBUm#a=Sjj`m<5wARIvAl-c~%yadWr{?%cF@8#Ej`)mLUdrOXiuq2s{PZ>qCB_3Twp zas;DSDkursIhO~JDe3oCB6&u;{emL^?Mnq|=~v2(gntMakVlfN^PYy?D^zV!b4C{G z`inK7l#*fUErIGBl(2SO(25TXH8e|q6r>Ph9BM3K= zx$l}J_&a%_ccb1t&>GTHE&DsmHivv`7-obg;XIDCjl z(8g8zOC^Vc4Nep<3DjPeHon&lSK;e*c*~_bM7X9BO$IOqsgFZPL~6k;EI)5)?p3*U zxu!0U@C=X^Oo+T{hAbs8bN3-KQ?fUb&iF}ZGz(|X1i+Pj_VDI0dNZ9~YH&H1L#YG& z7XYAIZJ`Pt>wmUwK4P0kA}|r9BkLA2nnPCqdX+z z6F%gxx;mJRj-l3jV7LwDz8OA+?U^9#KuTo1Bk^N4`VrA$q!1_CABdZYQ!%VHvnU23 z$zV4&9bdp-Ra}`MEu3=Bvob2v+u-mS>|;8)uDoW*xi$%|3F(($*uUcs3CV?Cb3@R~ zCQgpo(?dF$AUSdU#hmxK9S*cXz&luUY#F4la5TihajA7|GM_%kbPMenv+NzRxfJQI zsq;897?(?#%zjlSTc$W4nmg)o4Z_t!{MM1`H4*&HEfQs8Uzrtx``*?5w^h_tt+o@0 zfpQ{aP4pXmQy@Do3cUmwzL=F6wKc@)c8w=93(>1m%Dx0x4khD-fyJ7Zsfo^Kzr``1 zmSd^whe1(~W*uQ{6USnk=oj45Cp-K)g@TTGT&0}-+3`X)v}QWeL)RRw<=PiC| zTZAS!Hs5+B|6?(;A%zfTUZM_^Skk&dAF014mPL7a6yQ9yOw7>b=&Bmtyy$dx6pjdj ze}>|XwA(ccm47$W8_s#BNwBN85Q10Hwl^_%A(;x*NwmM%&DC#d@hC9GvtLBv!>uu- zf630IAuqk_*y&%{hazoH#ySFkPx53eL*~;Ev@Br3cOWM{#z5_OTrFR;cP;N{5dR9W zE@?~6|CnrNA@ji)b5JjLK<=(L4?DI>!{!?YbSQq(^e~uvo8DDwd&NA zyDTI7gUw%3>K?nA*L;dyIu0Aj4iVg1dwpXv9(41qQF>f_fJANE+?>NNW2@Ny^4;Ws zh`K;azVYGFnt5Fpx#5aw2K))i6FTONas`K?@A(oM{mvfo?=D=`wwqArMKU?)D8$K& zb+jj?fT%(;0n4lN2D~_vEpTxcSyo*+-g1$Je!j5BXY(EJ*v$6g|8hh=S;G5|?K&$_ zz_X1<$7E7@;0#8VOcD%YVJ^~1!6+8SicIR;?!$O@DbuWWFy0tZm}JibY(2N+fRbHc90syO>F^sNAO0#tfDF)x|80P34JS zYjNwM@=S>>61|PQyTleoUn4J**pl{v%4;LGxP7GZR!wid^Z>~#Mzhu7bJaY()D|d{ z%JXS|D2*XrkubeIh3lfvPKM3krj}=o!{=}t%d^=L{|uX9@0FZ9*J=wu?EFs?hwi0y zg%D8z^OYp(v?@j!i-e+`L_>GJ#ElgZ_K$9l2vOcseyvauQVG@IY~b%uO|Tp{ObS#S zDQ;p?ewoATj)zX%*9YqrK#}6e#th>X_J_c|a@xkg>$IeEsX{7UBqVOzDKFn9b^A=c zMua0cherBnoI9ZOoW3Y#o6>Lg3!w2e1#1D+FP7-;oUI|z{!FaxWub471Z%-797fW` zy`~MK|9|eHf4E=7Jeh*o0?6&LEZ9FAt^OinVO8p`eOOM%>0baK>|AYYtplI_7q9$D AqyPW_ diff --git a/mod/etc/options_menu/images/audio.png b/mod/etc/options_menu/images/audio.png deleted file mode 100644 index dfbd0c99e7239bc6e3933a3418073c14bdd45f03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1634 zcma)7Z7>@K0F9if z&U@mgT3|xq;YUCRec&UAgIg0-Sr=li zAA2W6G#USrG^c0q7{)}PoL**$87y!F^)!aMM>zkg239rZIA zp}VNJ-a~1k=#7G0{KAP@%0k1PU4>5tMS?cx3|hjU2Ad9v!fRtQFxZJarz54@ z0Da-{7`>5ss$o--!gYyXQYK2oedv&N|NIe3ec^ZtOY zdt3T{Kad2{hb+z#)flW!+563SL{5%jV>ErpH zn}*KRSX{dhz)W~HA6)~#=V<}kf*?;%EuV+3XgA4M3li-O_VwNrD)FjIz`Y5Eu=eIkA3XNh> zsf<-tQvF@0q|D0EPa{4O0}Ts%1&0j2trqEW4Ho;_b5tRzAmAvTv0kck#?{|6IH&F` z+ar48QPL#x_5(BPHZRX|{Kx9}eT&0Zj-O~nNI~5Ot>WCr2XKova6Ay>>IKRP?~$T$BIcX=`mj?G4l-7Bi(7izw&Gk<&aZ7D2uV4+lw zR*Jzp=I&9lHamHr?5BY9Lvb&Dx0NLJRGn&#sG$$xwS=W6!tY@XXi`IGIZrB{P{)~%(Hp6gmdbMjZY=59KXH2!Bbbs7dvmW zLgRd)Hr-V)v!<2)a3wgULTe71Mi`txU-(m9UDZ_++E17VYIc{-y zsq;=n{4z8NsZOP6zGii>mF>;UWoY>{C-tjCoE%R$E7^Wqvp=iVk`+2w$5aUg{6%ub zA2WIG#U_!J>H8iQv-2!IPfMRzJ;}i6G&I$oYtAAc6iV4YUaGN7xraxGm6hQ03sH7-Ovf<4BtX~D#(U!u^Cj`N? zReNUlsi$N^JGPSOT^duuK%t`fcK@;~Md!Vi)SOt!dP4)=CX~RIEroxPbk|x`b?sTHI=79|dL0^QnomHwLLM?0cvU)Yb9fnxXr^(egUP zr3WKAA0#hp?p%M)Y5an2LHf*F5ZVy0mzZ=*h4sITZ4sz$>N$55Pb6Z#cH@KOPl&@C z-v&EwPtlVr^Fa9v*mEhxMAh>5(c$zZmwaV}cFE37!p1nc5@ES2r>cQV3e{L}DP>L( z-Z(jR=`SB?&_#Rk)Z}l7HhM+3#+Ycmwl6Q_a{o#~qx`G!msYD0Fhs&yM24!HI4S(t z(nZ`d8xmGCX0@9M-fazgH=U(BTMFDiv#ZEbg0Oo;!M2}Yxx4!S05mGdzu7m6`8Nl< BAvpj5 diff --git a/mod/etc/options_menu/images/exit.png b/mod/etc/options_menu/images/exit.png deleted file mode 100644 index d93f9500e215aacd6e2fff062370674c1abc5302..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1369 zcma)6c~H^`6#jXznMdZCB}xZqCPrpz;*l14B$}z^Vkzd9RH#_yQNg02wyvp3Yi33$ z6YiEFwYX6pKP|~}n1Ia#6%DpFZ!{y7Fm(B&e|G=cH}k%4-kUe?y?OKID~^f`F*Dv^ z3;=*xSSThM0Q3@cZf$6w%UqmOZ`6@<6gGChj#vJNHToa$(UqE=kB*E1Jlss-MviN& z?Y0_PD5|hm0Kl*}3=w^1u=Y>3$@BEd{G!>?0CoM{^zevQf`FfWOGRd+1*8f_|A ztAmwYt_MX*+%GhOV^}g+6<6uVOdcMg7~O0dT$sY?I>CDa z9K`proreOcnPUTNh|ls^L}Lk44dbg0H3nu2&$(P}Xt3J^#Z8m;PjpDOEK63~QIM9XS}i)?n(Rh{->(3S!x;lP0njMTr;>$6IOMNDIKvo^}5Y zgXWdVONmWFuB!wTXYt&=nhxYJ2yURs^isrO?={Lo_ha%$BsOd7A&M{hIGjfgnom1| zJbc@5KJ73vpJT9?^|;+nu+Z1GV4?ue5QZQba%E)w@g=luRX=SBeUT*47fJ^`25n4s z;9Yy55^@&TFkjWjwbU2$oi}|XcOGQ{6k3JJxczYb)p7fwRb@I5KqeZ@?;2y}oM=cr zX?`X!O6~v?m|rHvB`lOM3&6PaBN^fTP+Qal*&e-4hR`b_DYr8!)S;s-wmimzH#xP( z>4f+JKA_CL|0!(>3V*fhB|jKlkX~9#@vr_)IrY@@`9c<{^dV)$7MFn^>o*#fUVxp) z`}Qa(oO9-_+{)8xkXKza+dz7;(Li2}6?Wv*4_~KoaWmb|rLUThOjQ9HXi@q<1HA623>53q6;THpCWndd<;Aaq*~h z%~N-3g?T!)`_dduA-nb7$*6a+IW}+NDC6W!un*!8D(^sT(TqOc;~6W_`#iEu51@ot z0%GrDl4mN114g^$T1&&%IUy<9ea_R;2h3+=C30%o!UTz5avHbFrq(e|x+=6a3x%Bt z)5`Z3j2!>)iLCFU#T9A3u3E1)M!P-~ORtrKHMOKCVwB4Per=Eb zj{#SkL~WU)M!%z&uorV|6lEeDuX$4iw*Tkz>PU%Tk7|BrLr7ZH#ikX}V!JPFXaAR) z5KARys=219R_yKeZaOa&?z5*KYUhtA;n5Z3L;_PY_(RCL_XBg+C%nQFqU@(t>`dSG zmV54lo5KkYczMh{v)H81Dy?i%e<*ds&9$}4!ny)LnM-H&hC=CZaO{Z!w76M=$j7u4jHW$p6cEuu-O zaG&1TOp~Oyx8nBjM#b<*Ga59jB*>1u%qiWb0p!Z!R|0;nPij>gn8ZVai+2;=!fuOB z1YnKJ#j%;y?LA)t@w6vjcFa@$P|}h-y5;vsRsrH~ljhW`qiybsh{#_%bixOO?T^GX I?oGP<519hELI3~& diff --git a/mod/etc/options_menu/images/retroarch.png b/mod/etc/options_menu/images/retroarch.png deleted file mode 100644 index 93e9d576f3e6de69c3911046dba6c7b38afa69ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1471 zcma)*X;2af6vr`Bvt7wGuS7F-Ma+=Q6O~NYi%?TE@d(UtJvNUDEpwCDv|K9G^(yk( zytc9=&kRMBG{+V*4-z!QKs3)n@<>tF_S??xhyBmI-+%sZ-prf#;gNlPJXCh;>;?b; zDo9UPGyoun-7fW=^4pyh$?Ab^Gx7BbaNG8+|DhVharpMAN+R0FA7H7q7ieeGPgYF=EZ+ z1ke;ckXe`P>JnRL_xtzM-pe`Z+Fp&xchrV&e|Sxy?m~_!W_;YFSMej~?BdhGpOFIx z_UgnSPPW8BMu9wEUSc*tbIzIC`F(sexUj;JTxr#9>@AHk z?vE{4ad~-zOT3qMg);YwN^b1=lWRS=V8+Y0z;EewgZuhtSPRAQG;6r|zVxgfR!m*B zc_{t_Zt%>g4WZ@OMHRNe#SDC|Y^JY>G`5Oh)7IvcA5g-anC$R26pJVWO1%kyLWu+0 zwpT9Jl-PwL5FOs=GS%8&Dsu;{M9)~L>^uv3E>WgwEGY1^@xKyEkY$#FElC>PG`QS4 z{hny$Fe%HYlr%DpQS>w-6?Y~~zxFhVAW*WRrFs5p?aLB0AlM-X%>X;w}G zqK$n}=5Brlm5SMziH$lN#<;i#KC zlO{0~x>-mB56|UBSxqv#pSvwkt<}q%4nXJ=W1EJBkSJUfeF~cRV?BPsd)P(`^lg?s zL!yfK!&-B8IqpI%z8~H+{mBKA@?z`M=bh4tfD}n>OM*={iqYj!v$4|%IvOl%@@R_N z1YJ6EzDB2zOzDV(l~647pX;U(B%4#ez+o+D@(MfBj%s12AqC@`irLzyvev62`rcK= z%jlN#@s!E@l;o(9T*QulKmDp8;A{n}Hn7?a&wIOHL5M{gykJ{3bx@KXJmUz;jMz~< zqSmm2u%PQyFv_HL?@&a%YJKKSORxMVv0|cF4m+b^t#d}tJhv$B9wyFA>k#0V&H7=*G{RK7t-ckSn diff --git a/mod/etc/options_menu/images/shutdown.png b/mod/etc/options_menu/images/shutdown.png deleted file mode 100644 index c3df9ffacbf4521fa805f8080b6d9029f8cffe31..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2071 zcma)-c{mde1IOpS+FV01WytZ%nK|aR2sxU`k!bj-R3viGnpi*gQ7UualVh8dNX&hN zMv=2j&N0zK&iMK3ectE&>-|2@_xU`}_xb++NkH0L0{JER0RRBd+R7XS0I+!+7tG0V zyhO5gWR9VTw7p_+%%lG!WTQ*)<0~*2W$y^k;sr{m$;*f+aZ0J^NB?OA0C=8So10w; zpZt;cVbI0_+?q!HRYwsAZLO3DXj`i6r4w|p@z7UD@5B@Z*%#O4yHjM%{b0nC;{F4E zS31*OU#jB3+I3@H7|PQ0#=VUFsR|0VW&PoHK4#6e##Kc;smuG-|KjGx=4L)e+)_uw zPtN`DIoaHhqB!=6ZfW{^m{n%ISS9VtEDG7ZTo3Fn{ndOTdA zX6$Hq5VKs)-t=w8u6@mRAaGe-a>|8lXJDqA`b=MbLsi3>cVY?Yjb{9!xjaS@PcNSv z^Q4ODTSmH}(7as?Qsy$EDIjguAf?9Bv|oBLRi|K56!U;IQU-MmqVuCiucya41koY2 zs+wW*o-e*iu)kGP1J_nLU^m=_KR&M=v!a)tDWU99ArlhWwqoIlNa>5)dEFytdGtKR z+azSONvRA>^6b=ne5?12pJP<{>l4fkL@|OD;);q-6w*Ea6&V1Yd4ye@|Te2UzNi);)#t`=xw?a?CHFG~<6*txX z26rCDujLFI^k~^Z+=sAQkp>M-Z(h!T4)R)wv zQO@?v+K|csUGJSrm2Yrjw{BsYUPXagK@apUV8_;CL`F9o?J5L9h zP7Er4@nsKJe{Fib6h^hIW8qo~E`KbFUR@*Zi(iNLH3XY>;deB0l`^O1W(<1Yud%ne zxEv{DR0&>ua^jDakwFB@qXA}O&215Vxcp9cdp-A#=%r}h=K7^U9Cl&YqsW<__)Aqc zr9n8(*jS-e+n9Ea?&VNUGZ|V6^Fig%m4%2)BD8|6T^3iffE#G}E9cPx#b$D&*Q5oc z+V%aLi-^qb=!3>f#%TdY<>e87NX0sUTfvk7dnc)(+D6?@+ZjOJ_v#b+N-u%F(%<&a zKkkq%x1X5UzHK=6*NDR>!prst z?Hb50l^D42v<#S9A zVbP6%pOjVZP~vwJz5Ah4eO!YD>aLhAb3rbAX;z+E>HyU0k%~eqK7j4oqd+T@7YPwJ zg4Pgzwl9UxTBY<5a)ZP^%e8hwV!HF6f)}d{-TaRA1#>#a_@8bY8f)>-Ha28bxu5KpyExWSS-(Ya3u~VQ)@Gc*gt-rg8Q} diff --git a/mod/etc/options_menu/themes/commands/c9998_separator b/mod/etc/options_menu/themes/commands/c9998_separator deleted file mode 100644 index e69de29..0000000 diff --git a/mod/etc/options_menu/themes/commands/c9999_Back b/mod/etc/options_menu/themes/commands/c9999_Back deleted file mode 100644 index 1e68533..0000000 --- a/mod/etc/options_menu/themes/commands/c9999_Back +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=BACK -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=usleep 50000 && %options_path%/options & -PREVIEW_IMAGE=/etc/options_menu/images/back.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/commands/c9999_Exit b/mod/etc/options_menu/themes/commands/c9999_Exit deleted file mode 100644 index a4a6ead..0000000 --- a/mod/etc/options_menu/themes/commands/c9999_Exit +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=EXIT -COMMAND_TYPE=EXTERNAL -RESTART_UI=TRUE -COMMAND_STR=echo "Closing Options Menu" -PREVIEW_IMAGE=/etc/options_menu/images/exit.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/uninstall b/mod/uninstall index 4bbb038..7f9f1a6 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -64,6 +64,8 @@ remove "$rootfs/etc/preinit.d/p8024_themeselector" remove "$rootfs/etc/options_menu/commands/c0075_ThemeOptions" remove "$rootfs/etc/options_menu/scripts/om_themeselector" remove "$rootfs/etc/options_menu/themes" +remove "$rootfs/etc/options_menu/images/advanced.png" +remove "$rootfs/etc/options_menu/images/audio.png" remove "$rootfs/etc/options_menu/images/audioall.png" remove "$rootfs/etc/options_menu/images/audiohome.png" remove "$rootfs/etc/options_menu/images/back.png" @@ -72,17 +74,28 @@ remove "$rootfs/etc/options_menu/images/colors_nes.png" remove "$rootfs/etc/options_menu/images/default_preview.png" remove "$rootfs/etc/options_menu/images/diy.png" remove "$rootfs/etc/options_menu/images/download.png" +remove "$rootfs/etc/options_menu/images/exit.png" remove "$rootfs/etc/options_menu/images/loading" remove "$rootfs/etc/options_menu/images/luigi.png" remove "$rootfs/etc/options_menu/images/mario.png" remove "$rootfs/etc/options_menu/images/packed.png" remove "$rootfs/etc/options_menu/images/packed_nes.png" remove "$rootfs/etc/options_menu/images/pole.png" +remove "$rootfs/etc/options_menu/images/preview_audio_randomizer_all.png" +remove "$rootfs/etc/options_menu/images/preview_audio_randomizer_home.png" +remove "$rootfs/etc/options_menu/images/preview_clean_up.png" +remove "$rootfs/etc/options_menu/images/preview_diy_theme.png" +remove "$rootfs/etc/options_menu/images/preview_download_themes.png" +remove "$rootfs/etc/options_menu/images/preview_theme_options.png" +remove "$rootfs/etc/options_menu/images/preview_theme_randomizer.png" +remove "$rootfs/etc/options_menu/images/preview_update_theme_list.png" remove "$rootfs/etc/options_menu/images/randtheme.png" remove "$rootfs/etc/options_menu/images/retro_gaming.zip" +remove "$rootfs/etc/options_menu/images/retroarch.png" remove "$rootfs/etc/options_menu/images/saving.png" remove "$rootfs/etc/options_menu/images/sbm.png" remove "$rootfs/etc/options_menu/images/settings.png" +remove "$rootfs/etc/options_menu/images/shutdown.png" remove "$rootfs/etc/options_menu/images/themes.png" remove "$rootfs/etc/options_menu/images/uninstall.png" remove "$rootfs/etc/options_menu/images/update.png" From d51f6c6626ee4cb6a31b1eaaee0290c83cd79c4d Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 16 Sep 2026 14:28:30 +0000 Subject: [PATCH 035/210] Replace the generic options UI with a dedicated theme_manager engine Builds a standalone Modern-UI screen (src/main.cpp) for the theme selector, reusing OptionsMenu's framework/command/localization classes via the vendor/OptionsMenu submodule instead of the generic `options` binary. Every screen (root, Downloads, DIY at every depth, Settings) is the same theme_manager binary relaunched with a different --commandPath, exactly like `options` itself already worked. - Synthesize Back rows in C++ instead of static c9999_Back files: root goes back to OptionsMenu via the OM_BACK_STACK it already exports; every other screen relaunches theme_manager directly, or runs its scriptLocation's own om_return first if one exists (DIY sprite pickers and Downloads use theirs for real cleanup - refreshing the theme list, re-enabling rows - not just navigation). - Wire Settings' toggles to real STATE_STR checks (om_state) instead of the dead switchOn/switchOff "[X]" text-marker code that predated real switch graphics; remove that dead code and its om_theme_functions source. - Fix om_theme_selector (the OptionsMenu-row entry point) - it sourced om_vars and did nothing else, never actually launching anything. - Replace pngpack-upx with a new sprite_crop tool (see the pngpack repo) for the DIY asset preview scan: no JSON generation/parsing, uncompressed fast PNG encode, batched 4-way parallel - cuts a category's first-load time from ~84s (silently erroring on missing install-time JSON) to ~35s correct. Drop the now-unused genThemeJson install-time generation. - Add PREVIEW_NEAREST support (nearest-neighbor scaling) for the DIY character sprite previews, which were blurry under the default linear filter at that much upscaling. - Performance: stop the two wget --spider mirror checks from running on every om_vars source (only where a download can actually happen now), bound every wget with -T, cache $state_path for Settings' toggle checks instead of re-deriving it once per row, and skip Downloads' full themeLoader rescan on Back unless the installed theme count changed. --- .gitmodules | 4 + .../options_menu/scripts/om_theme_selector | 2 + mod/etc/options_menu/scripts/om_themeselector | 2 +- .../themes/creator/commands/c9999_Back | 7 - .../themes/creator/scripts/om_applytheme | 2 +- .../themes/creator/scripts/om_diy_asset | 2 +- .../themes/creator/scripts/om_refresh | 2 +- .../themes/creator/scripts/om_removediy | 2 +- .../sprites/colors/commands/c9999_Back | 7 - .../creator/sprites/colors/scripts/om_return | 2 +- .../sprites/colors_nes/commands/c9999_Back | 7 - .../sprites/colors_nes/scripts/om_return | 2 +- .../creator/sprites/luigi/commands/c9999_Back | 7 - .../creator/sprites/luigi/scripts/om_return | 2 +- .../creator/sprites/mario/commands/c9999_Back | 7 - .../creator/sprites/mario/scripts/om_return | 2 +- .../sprites/packed/commands/c9999_Back | 7 - .../creator/sprites/packed/scripts/om_return | 2 +- .../sprites/packed_nes/commands/c9999_Back | 7 - .../sprites/packed_nes/scripts/om_return | 2 +- .../creator/sprites/pole/commands/c9999_Back | 7 - .../creator/sprites/pole/scripts/om_return | 2 +- .../creator/sprites/sbm/commands/c9999_Back | 7 - .../creator/sprites/sbm/scripts/om_return | 2 +- .../creator/sprites/wall/commands/c9999_Back | 7 - .../creator/sprites/wall/scripts/om_return | 2 +- .../sprites/wall_far/commands/c9999_Back | 7 - .../sprites/wall_far/scripts/om_return | 2 +- .../sprites/wall_near/commands/c9999_Back | 7 - .../sprites/wall_near/scripts/om_return | 2 +- .../themes/downloads/commands/c9999_Back | 7 - .../downloads/scripts/om_clearDownloads | 2 +- .../themes/downloads/scripts/om_refreshThemes | 2 +- .../themes/downloads/scripts/om_return | 4 +- .../options_menu/themes/scripts/om_clearList | 2 +- .../options_menu/themes/scripts/om_getThemes | 2 +- .../options_menu/themes/scripts/om_settings | 10 +- .../themes/scripts/om_themeCreator | 2 +- .../themes/scripts/om_theme_functions | 31 -- mod/etc/options_menu/themes/scripts/om_vars | 141 ++++-- .../settings/commands/c0000_themeperfolder | 1 + .../commands/c0000_themeperfolder_child | 1 + .../settings/commands/c0000_themerandomizer | 1 + .../settings/commands/c0001_audiorandomizer | 1 + .../commands/c0001_audiorandomizerall | 1 + .../themes/settings/commands/c9999_Back | 7 - .../themes/settings/scripts/om_audio | 2 +- .../themes/settings/scripts/om_reset | 2 +- .../themes/settings/scripts/om_state | 40 ++ .../themes/settings/scripts/om_systemcleanup | 2 +- .../themes/settings/scripts/om_theme | 2 +- mod/install | 31 +- mod/uninstall | 1 + src/main.cpp | 477 ++++++++++++++++++ vendor/OptionsMenu | 1 + 55 files changed, 659 insertions(+), 236 deletions(-) delete mode 100644 mod/etc/options_menu/themes/creator/commands/c9999_Back delete mode 100644 mod/etc/options_menu/themes/creator/sprites/colors/commands/c9999_Back delete mode 100644 mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c9999_Back delete mode 100644 mod/etc/options_menu/themes/creator/sprites/luigi/commands/c9999_Back delete mode 100644 mod/etc/options_menu/themes/creator/sprites/mario/commands/c9999_Back delete mode 100644 mod/etc/options_menu/themes/creator/sprites/packed/commands/c9999_Back delete mode 100644 mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c9999_Back delete mode 100644 mod/etc/options_menu/themes/creator/sprites/pole/commands/c9999_Back delete mode 100644 mod/etc/options_menu/themes/creator/sprites/sbm/commands/c9999_Back delete mode 100644 mod/etc/options_menu/themes/creator/sprites/wall/commands/c9999_Back delete mode 100644 mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c9999_Back delete mode 100644 mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c9999_Back delete mode 100644 mod/etc/options_menu/themes/downloads/commands/c9999_Back delete mode 100644 mod/etc/options_menu/themes/scripts/om_theme_functions delete mode 100644 mod/etc/options_menu/themes/settings/commands/c9999_Back create mode 100644 mod/etc/options_menu/themes/settings/scripts/om_state create mode 100644 src/main.cpp create mode 160000 vendor/OptionsMenu diff --git a/.gitmodules b/.gitmodules index 3e7615b..8707366 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,7 @@ [submodule "pngpack"] path = pngpack url = https://github.com/DefKorns/pngpack.git +[submodule "vendor/OptionsMenu"] + path = vendor/OptionsMenu + url = https://github.com/DefKorns/OptionsMenu + branch = modern-ui-ttf diff --git a/mod/etc/options_menu/scripts/om_theme_selector b/mod/etc/options_menu/scripts/om_theme_selector index 8d5c9f5..65c942b 100644 --- a/mod/etc/options_menu/scripts/om_theme_selector +++ b/mod/etc/options_menu/scripts/om_theme_selector @@ -18,3 +18,5 @@ # shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init + +sh "$mountpoint/etc/options_menu/scripts/om_themeselector" diff --git a/mod/etc/options_menu/scripts/om_themeselector b/mod/etc/options_menu/scripts/om_themeselector index 426aaa7..df5f4b9 100644 --- a/mod/etc/options_menu/scripts/om_themeselector +++ b/mod/etc/options_menu/scripts/om_themeselector @@ -33,4 +33,4 @@ else loadThemeList fi -$optionsMenu/options --commandPath $omModCommands/ --scriptPath $omModScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager & diff --git a/mod/etc/options_menu/themes/creator/commands/c9999_Back b/mod/etc/options_menu/themes/creator/commands/c9999_Back deleted file mode 100644 index a224a60..0000000 --- a/mod/etc/options_menu/themes/creator/commands/c9999_Back +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=BACK -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=usleep 50000 && %options_path%/options --commandPath %options_path%/themes/commands/ --scriptPath %options_path%/themes/scripts --title "THEME_SELECTOR" & -PREVIEW_IMAGE=/etc/options_menu/images/back.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/creator/scripts/om_applytheme b/mod/etc/options_menu/themes/creator/scripts/om_applytheme index 8df2adb..8a511b2 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_applytheme +++ b/mod/etc/options_menu/themes/creator/scripts/om_applytheme @@ -41,4 +41,4 @@ remove "$omDummy" loadThemeList -r -$optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset index dc140f8..aea2a85 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset +++ b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset @@ -58,4 +58,4 @@ esac findDiyAsset "$1" -$optionsMenu/options --commandPath $diy_om_sprite_path/commands/ --scriptPath $diy_om_sprite_path/scripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $diy_om_sprite_path/commands/ --scriptPath $diy_om_sprite_path/scripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/scripts/om_refresh b/mod/etc/options_menu/themes/creator/scripts/om_refresh index 2c369bf..35c2e66 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_refresh +++ b/mod/etc/options_menu/themes/creator/scripts/om_refresh @@ -24,4 +24,4 @@ diyThemeRemover diyThemeChecker "loader" touch "$omModGenSpacer" -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/scripts/om_removediy b/mod/etc/options_menu/themes/creator/scripts/om_removediy index 74f5f9c..3630c2d 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_removediy +++ b/mod/etc/options_menu/themes/creator/scripts/om_removediy @@ -22,4 +22,4 @@ script_init diyThemeRemover -$optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/colors/commands/c9999_Back deleted file mode 100644 index d96b9c2..0000000 --- a/mod/etc/options_menu/themes/creator/sprites/colors/commands/c9999_Back +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=BACK -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_return & -PREVIEW_IMAGE=/etc/options_menu/images/back.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return index 037dc6c..69a2afe 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c9999_Back deleted file mode 100644 index d96b9c2..0000000 --- a/mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c9999_Back +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=BACK -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_return & -PREVIEW_IMAGE=/etc/options_menu/images/back.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return index 037dc6c..69a2afe 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/luigi/commands/c9999_Back deleted file mode 100644 index d96b9c2..0000000 --- a/mod/etc/options_menu/themes/creator/sprites/luigi/commands/c9999_Back +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=BACK -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_return & -PREVIEW_IMAGE=/etc/options_menu/images/back.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return index 037dc6c..69a2afe 100644 --- a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/mario/commands/c9999_Back deleted file mode 100644 index d96b9c2..0000000 --- a/mod/etc/options_menu/themes/creator/sprites/mario/commands/c9999_Back +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=BACK -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_return & -PREVIEW_IMAGE=/etc/options_menu/images/back.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return index a92669e..669243b 100644 --- a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/packed/commands/c9999_Back deleted file mode 100644 index d96b9c2..0000000 --- a/mod/etc/options_menu/themes/creator/sprites/packed/commands/c9999_Back +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=BACK -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_return & -PREVIEW_IMAGE=/etc/options_menu/images/back.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return index a92669e..669243b 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c9999_Back deleted file mode 100644 index d96b9c2..0000000 --- a/mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c9999_Back +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=BACK -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_return & -PREVIEW_IMAGE=/etc/options_menu/images/back.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return index a92669e..669243b 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/pole/commands/c9999_Back deleted file mode 100644 index d96b9c2..0000000 --- a/mod/etc/options_menu/themes/creator/sprites/pole/commands/c9999_Back +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=BACK -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_return & -PREVIEW_IMAGE=/etc/options_menu/images/back.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return index a92669e..669243b 100644 --- a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c9999_Back deleted file mode 100644 index d96b9c2..0000000 --- a/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c9999_Back +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=BACK -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_return & -PREVIEW_IMAGE=/etc/options_menu/images/back.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return index a92669e..669243b 100644 --- a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/wall/commands/c9999_Back deleted file mode 100644 index d96b9c2..0000000 --- a/mod/etc/options_menu/themes/creator/sprites/wall/commands/c9999_Back +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=BACK -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_return & -PREVIEW_IMAGE=/etc/options_menu/images/back.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return index a92669e..669243b 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c9999_Back deleted file mode 100644 index d96b9c2..0000000 --- a/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c9999_Back +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=BACK -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_return & -PREVIEW_IMAGE=/etc/options_menu/images/back.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return index a92669e..669243b 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c9999_Back deleted file mode 100644 index d96b9c2..0000000 --- a/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c9999_Back +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=BACK -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_return & -PREVIEW_IMAGE=/etc/options_menu/images/back.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return index a92669e..669243b 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/downloads/commands/c9999_Back b/mod/etc/options_menu/themes/downloads/commands/c9999_Back deleted file mode 100644 index d96b9c2..0000000 --- a/mod/etc/options_menu/themes/downloads/commands/c9999_Back +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=BACK -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_return & -PREVIEW_IMAGE=/etc/options_menu/images/back.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads index cef7160..b8e0598 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads +++ b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads @@ -24,4 +24,4 @@ clearDownloads remove "$subDummy" remove "$omModSubSpacer" -$optionsMenu/options --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes index 1d4beb1..e37ecc8 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes +++ b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes @@ -22,4 +22,4 @@ script_init getThemeList -$optionsMenu/options --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_return b/mod/etc/options_menu/themes/downloads/scripts/om_return index 6c43562..67f5a49 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_return +++ b/mod/etc/options_menu/themes/downloads/scripts/om_return @@ -23,10 +23,10 @@ remove "$omDummy" if [ -n "$(ls -A $theme_path)" ]; then touch "$omDummy" - themeLoader + themeCountChanged && themeLoader else remove "$omModSpacer" clearList fi -usleep 50000 && $optionsMenu/options --commandPath $omModCommands/ --scriptPath $omModScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager & diff --git a/mod/etc/options_menu/themes/scripts/om_clearList b/mod/etc/options_menu/themes/scripts/om_clearList index 8ec6f83..dfd6aee 100644 --- a/mod/etc/options_menu/themes/scripts/om_clearList +++ b/mod/etc/options_menu/themes/scripts/om_clearList @@ -24,4 +24,4 @@ clearList disable_cmds "Clear" enable_cmds "Refresh" -$optionsMenu/options --commandPath $omModCommands/ --scriptPath $omModScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModCommands/ --scriptPath $omModScripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/scripts/om_getThemes b/mod/etc/options_menu/themes/scripts/om_getThemes index bf79aed..0f5ed97 100644 --- a/mod/etc/options_menu/themes/scripts/om_getThemes +++ b/mod/etc/options_menu/themes/scripts/om_getThemes @@ -22,4 +22,4 @@ script_init [ ! -f "$subDummy" ] && getThemeList -$optionsMenu/options --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/scripts/om_settings b/mod/etc/options_menu/themes/scripts/om_settings index 7730c03..c1fb0ab 100644 --- a/mod/etc/options_menu/themes/scripts/om_settings +++ b/mod/etc/options_menu/themes/scripts/om_settings @@ -20,8 +20,12 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init -hasSelectedOptions "$om_themeperfolder" "$enableThemePerFolder" -hasSelectedOptions "$om_themerandomizer" "$enableThemeSetting" +# theme_manager's STATE_STR checks (om_state) would otherwise each re-derive +# $state_path from scratch via om_functions/preinit - cache it once here so +# 5 toggle rows cost one lookup instead of five. Plain /tmp, no $mountpoint: +# this is throwaway same-boot-session IPC, not data that needs to live inside +# a particular mounted rootfs the way e.g. diyThemePath does +echo "$state_path" > /tmp/om_state_path hasItemsToCleanUp "SystemCleanUp" @@ -33,4 +37,4 @@ else [ ! -f "$enableSystemCleanUp" ] && remove "$omModSettingsSpacer" fi -$optionsMenu/options --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/scripts/om_themeCreator b/mod/etc/options_menu/themes/scripts/om_themeCreator index 7355ac9..60d10d4 100644 --- a/mod/etc/options_menu/themes/scripts/om_themeCreator +++ b/mod/etc/options_menu/themes/scripts/om_themeCreator @@ -22,4 +22,4 @@ script_init diyThemeChecker -$optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/scripts/om_theme_functions b/mod/etc/options_menu/themes/scripts/om_theme_functions deleted file mode 100644 index af9b1f3..0000000 --- a/mod/etc/options_menu/themes/scripts/om_theme_functions +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh -# shellcheck disable=SC2034,SC2086,SC2154,SC3010,SC3046,SC3051 -# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# theme-selector's own toggle-marker helpers - kept separate from the shared -# om_functions (owned by OptionsMenu) so this hmod never overwrites it. - -switchOff() { - for item in "$@"; do - [ -f "$item" ] && sed -r -i 's/ \[X\]//g' "$item" - done -} - -switchOn() { - for item in "$@"; do - [ -f "$item" ] && sed -r -i "1 s|$| [X]|" "$item" - done -} diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 66b6049..d3625ee 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -30,8 +30,9 @@ has_folders="${gStorage}/001" downloadURL="http://hakchicloud.com/Hakchi_Themes/scripts" themeURL="http://hakchicloud.com/Hakchi_Themes/themes" -! wget -q --spider "$downloadURL" > /dev/null 2>&1 && downloadURL="http://classicmods.net/files/themes/themeselector-scripts/" -! wget -q --spider "$themeURL" > /dev/null 2>&1 && themeURL="http://classicmods.net/files/themes/themeselector/" +# resolveDownloadUrls (below) does the actual availability check - two blocking +# wget calls (~0.9s combined) here would run on every om_vars source, i.e. on +# every screen of the theme selector, not just the ones that download anything # Options Menu omMod="$optionsMenu/$modtitle" @@ -88,6 +89,7 @@ om_themeperfolderchild="$state_path/om_themeperfolderchild" om_audiorandomizer="$state_path/om_audiorandomizer" om_advmusichack="$state_path/om_advmusichack" om_firstrun="$state_path/om_firstrun" +om_theme_count="$state_path/om_theme_count" #### SEPARATORS omDummy="$omModCommands/c0000_0000" @@ -109,10 +111,6 @@ enableAdvancedMusicHack="$omModSettingsCmds/c0001_audiorandomizerall" disableAdvancedMusicHack="$omModSettingsCmds/_audiorandomizerall" enableThemeSetting="$omModSettingsCmds/c0000_themerandomizer" disableThemeSetting="$omModSettingsCmds/_themerandomizer" -enableThemePerFolder="$omModSettingsCmds/c0000_themeperfolder" -disableThemePerFolder="$omModSettingsCmds/_themeperfolder" -enableThemePerFolderChild="$omModSettingsCmds/c0000_themeperfolder_child" -disableThemePerFolderChild="$omModSettingsCmds/_themeperfolder_child" enableSystemCleanUp="$omModSettingsCmds/c0001_systemcleanup" disableSystemCleanUp="$omModSettingsCmds/_systemcleanup" enableThemeGenerator="$omModCommands/c0000_generatetheme" @@ -254,6 +252,16 @@ enableMenuOptions() { [ ! -d "$has_folders" ] && disable_cmds "ThemeGenerator" } +# cheap stand-in for "did the installed theme list actually change" - avoids +# re-running the full themeLoader scan (~1.7s with a few dozen themes) when +# nothing was added or removed, e.g. just browsing Downloads then going Back +themeCountChanged() { + new_count="$(find "$theme_path" -mindepth 1 -maxdepth 1 -type d | wc -l)" + old_count="$(cat "$om_theme_count" 2> /dev/null)" + echo "$new_count" > "$om_theme_count" + [ "$new_count" != "$old_count" ] +} + themeLoader() { removeRegionalTheme cd "$theme_path" && find . -mindepth 1 -maxdepth 1 -type d | sed 's#^\./##' | sort -u | while IFS= read -r thm; do @@ -349,14 +357,28 @@ removeRegionalTheme() { [ -d "$theme_path/snesce-$sfregion" ] && rm -rf "$theme_path/snesce-$sfregion" } +# picks the mirror that's actually reachable for downloadURL/themeURL - two +# blocking wget calls, so only run this where a download is about to happen, +# never unconditionally at om_vars source time +resolveDownloadUrls() { + # -T bounds a bad/unreachable network to a few seconds instead of hanging + # the whole UI (theme_manager blocks on this the entire time it runs) + ! wget -q -T 5 --spider "$downloadURL" > /dev/null 2>&1 && downloadURL="http://classicmods.net/files/themes/themeselector-scripts/" + ! wget -q -T 5 --spider "$themeURL" > /dev/null 2>&1 && themeURL="http://classicmods.net/files/themes/themeselector/" +} + getThemeList() { + # splash first - resolveDownloadUrls can take a few seconds (bounded by its + # own -T 5 now, but still real) when hakchicloud.com is slow/unreachable, + # and that shouldn't be a blank screen before the loading indicator shows "$omScripts/gen_splash" /tmp/om_splash.png THEME_LIST_DOWNLOADING_LINE1 THEME_LIST_DOWNLOADING_LINE2 && decodepng /tmp/om_splash.png > /dev/fb0 + resolveDownloadUrls cd "$omModSub" || exit if [ ! -e "${s_type}${listName}" ]; then clearDownloads - if wget -q --spider "$downloadURL/${s_type}${listName}" > /dev/null 2>&1; then - wget "$downloadURL/${s_type}${listName}" + if wget -q -T 5 --spider "$downloadURL/${s_type}${listName}" > /dev/null 2>&1; then + wget -T 10 "$downloadURL/${s_type}${listName}" else netChecker fi @@ -416,6 +438,7 @@ loadThemeList() { # verify themeGenerator function themeGenerator() { + resolveDownloadUrls echo "Packaging: DefKorns" translate CATEGORY_UI_MOD echo "" @@ -429,15 +452,15 @@ themeGenerator() { theme_pkg="${themeURL}/${system}.${theme_name}.tar.gz" [ "${theme_name}" = "all" ] && theme_pkg="${downloadURL}/${sftype}-all" - if wget -q --spider "${theme_pkg}" > /dev/null 2>&1; then + if wget -q -T 5 --spider "${theme_pkg}" > /dev/null 2>&1; then translate DOWNLOADING - wget "${theme_pkg}" + wget -T 10 "${theme_pkg}" if [ "${theme_name}" = "all" ]; then while IFS= read -r t; do echo "$t" echo "" theme_pkg="${themeURL}/${system}.${t}.tar.gz" - wget "${theme_pkg}" + wget -T 10 "${theme_pkg}" tar -xzvf "${system}.${t}.tar.gz" && remove "${system}.${t}.tar.gz" done < "${sftype}-all" fi @@ -455,6 +478,11 @@ themeGenerator() { } findDiyAsset() { + # drawn once, up front - previously this was redrawn per theme inside the + # loop below, which (now that the crops run backgrounded/parallel instead + # of one at a time) no longer reliably stays on screen for the whole scan + "$omScripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png > /dev/fb0 + diy_file="${1}.png" diy_sprite="$omModSprites/${1}" asset_path="layout/asset/sprite/wallpaper" @@ -518,17 +546,38 @@ findDiyAsset() { ;; esac - om_default_json="$omModScripts/om_${1}.json" + # the crop rectangle is the same for every theme in this category (it's + # the same region of each theme's own packed.png/wall.png every time, only + # the source theme differs) - read it once here, not per theme + diy_crop_x="" + diy_sprites_json="$omModScripts/om_${1}.sprites.json" + if [ "$colors" = "0" ] && [ -f "$diy_sprites_json" ]; then + diy_frame="$(grep -o '"textureFrame":\[[0-9]*,[0-9]*,[0-9]*,[0-9]*\]' "$diy_sprites_json" | grep -o '[0-9]*,[0-9]*,[0-9]*,[0-9]*')" + diy_crop_x="${diy_frame%%,*}" + diy_rest="${diy_frame#*,}" + diy_crop_y="${diy_rest%%,*}" + diy_rest="${diy_rest#*,}" + diy_crop_w="${diy_rest%%,*}" + diy_crop_h="${diy_rest#*,}" + fi paths_to_search="$omModSprites/assets/$1 $theme_path" find "$diy_sprite/commands" "$diy_sprite/scripts" -type f \ -not -name 'om_*' -not -name 'c999[0-9]_*' \ -not -name 'c0000_*' -not -name '_*' -exec rm {} + + diy_crop_batch=0 for p in $paths_to_search; do if [ "$(ls -A "$p")" ]; then # shellcheck disable=SC2038 - cd "$p" && find . -mindepth 1 -maxdepth 1 -type d | xargs -n 1 basename | sort -u | while IFS= read -r asset_name; do + # listed to a temp file and read via redirection, not piped straight + # into the while loop - a pipe would run the loop in a subshell, and + # the backgrounded sprite_crop jobs below would then belong to that + # subshell, not this function, so the "wait" after the loop (needed to + # not return before the last, sub-4 batch of crops finishes) wouldn't + # actually catch them + cd "$p" && find . -mindepth 1 -maxdepth 1 -type d | xargs -n 1 basename | sort -u > /tmp/diy_asset_list + while IFS= read -r asset_name; do has_sprite="0" full_asset_path="$p/$asset_name" @@ -556,11 +605,16 @@ findDiyAsset() { fi [ "$chars" = "1" ] && temp_preview="$preview_image" if [ -n "$json" ] && [ ! -f "$temp_preview" ]; then - "$omScripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png > /dev/fb0 - if [ "$colors" = "0" ]; then - sed -r "s/changeme/${1}_${asset_name}/g;" "$om_default_json" > "$json" - pngpack-upx u "$json" "$preview_image" "$omModSprites/assets/$1" - remove "$json" + if [ "$colors" = "0" ] && [ -n "$diy_crop_x" ]; then + # backgrounded and batched (4 at a time, matching this console's + # core count) - a bare sequential loop over every installed + # theme was the whole reason this screen took so long to open + sprite_crop "$preview_image" "$diy_crop_x" "$diy_crop_y" "$diy_crop_w" "$diy_crop_h" "$temp_preview" & + diy_crop_batch=$((diy_crop_batch + 1)) + if [ "$diy_crop_batch" -ge 4 ]; then + wait + diy_crop_batch=0 + fi fi preview_image="$temp_preview" usleep 1000 @@ -568,6 +622,11 @@ findDiyAsset() { if [ ! -f "$cmd_file" ]; then touch "$cmd_file" + # chars=1 previews are tiny sprites (e.g. mario_run01.png) blown + # up a lot - nearest-neighbor keeps them crisp instead of blurry + preview_nearest="" + [ "$chars" = "1" ] && preview_nearest=" +PREVIEW_NEAREST=TRUE" echo "COMMAND_NAME=$cmd_name COMMAND_TYPE=INTERNAL COMMAND_STR=sh $sprite_script.sh @@ -575,14 +634,16 @@ PREVIEW_IMAGE=$preview_image PREVIEW_IMAGE_X=$preview_x PREVIEW_IMAGE_Y=$preview_y PREVIEW_IMAGE_WIDTH=$preview_w -PREVIEW_IMAGE_HEIGHT=$preview_h" > "$cmd_file" +PREVIEW_IMAGE_HEIGHT=$preview_h${preview_nearest}" > "$cmd_file" fi copy "$omModLicense" "$sprite_script.sh" echo "diySpriteSelector \"$full_asset_path\" \"$asset_path\" \"$asset\"" >> "$sprite_script.sh" fi - done + done < /tmp/diy_asset_list + remove /tmp/diy_asset_list fi done + wait # catch whatever's left of the last batch before returning } diySpriteSelector() { @@ -651,52 +712,48 @@ modCleanUp() { themeSettings() { if [ "$1" = "child" ]; then - toggleOptions "$om_themeperfolderchild" "$enableThemePerFolderChild" "$om_themeperfolder" "$enableThemePerFolder" + toggleOptions "$om_themeperfolderchild" "$om_themeperfolder" elif [ "$1" = "tpf" ]; then - toggleOptions "$om_themeperfolder" "$enableThemePerFolder" "$om_themeperfolderchild" "$enableThemePerFolderChild" "switchOn" + toggleOptions "$om_themeperfolder" "$om_themeperfolderchild" "no-cascade" else if [ ! -f "$om_themerandomizer" ]; then - settingsOptionChecker "on" "$om_themerandomizer" "$enableThemeSetting" + settingsOptionChecker "on" "$om_themerandomizer" else - settingsOptionChecker "off" "$om_themerandomizer" "$enableThemeSetting" + settingsOptionChecker "off" "$om_themerandomizer" fi fi } audioSettings() { if [ "$1" = "-amh" ]; then - toggleOptions "$om_advmusichack" "$enableAdvancedMusicHack" "$om_audiorandomizer" "$enableAudioSetting" + toggleOptions "$om_advmusichack" "$om_audiorandomizer" else if [ ! -f "$om_audiorandomizer" ]; then - settingsOptionChecker "on" "$om_audiorandomizer" "$enableAudioSetting" + settingsOptionChecker "on" "$om_audiorandomizer" else - settingsOptionChecker "off" "$om_audiorandomizer" "$enableAudioSetting" - settingsOptionChecker "off" "$om_advmusichack" "$enableAdvancedMusicHack" + settingsOptionChecker "off" "$om_audiorandomizer" + settingsOptionChecker "off" "$om_advmusichack" fi fi } toggleOptions() { + # $1=this flag file, $2=paired parent/child flag file, $3=set to skip the + # cascade (used for a parent toggling itself back on - its own child's + # state stays whatever it already was) if [ ! -f "$1" ]; then - settingsOptionChecker "on" "$1" "$2" - if [ -n "$5" ]; then - [ -f "$3" ] && switchOn "$4" - else - [ ! -f "$3" ] && settingsOptionChecker "on" "$3" "$4" - fi + settingsOptionChecker "on" "$1" + [ -z "$3" ] && [ ! -f "$2" ] && settingsOptionChecker "on" "$2" else remove "$1" - switchOff "$2" fi } settingsOptionChecker() { if [ "$1" = "off" ]; then remove "$2" - switchOff "$3" else touch "$2" - switchOn "$3" fi } @@ -715,13 +772,3 @@ hasItemsToCleanUp() { [ ! -f "$enableUninstaller" ] && remove "$omModSettingsSpacer" fi } - -hasSelectedOptions() { - if [ -f "$1" ]; then - if ! grep -q "\[X\]" "$2"; then - switchOn "$2" - fi - else - switchOff "$2" - fi -} diff --git a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder index 65155c4..209c097 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder +++ b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder @@ -2,6 +2,7 @@ COMMAND_NAME=THEME_PER_FOLDER COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_theme tpf +STATE_STR=sh %script_dir%/om_state theme_per_folder PREVIEW_IMAGE=/etc/options_menu/images/randtheme.png PREVIEW_IMAGE_X=920 PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child index 3baac93..b5468df 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child +++ b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child @@ -3,6 +3,7 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE CHILD=TRUE COMMAND_STR=sh %script_dir%/om_theme child +STATE_STR=sh %script_dir%/om_state theme_per_folder_child PREVIEW_IMAGE=/etc/options_menu/images/randtheme.png PREVIEW_IMAGE_X=920 PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/settings/commands/c0000_themerandomizer b/mod/etc/options_menu/themes/settings/commands/c0000_themerandomizer index 1dcd1de..1238987 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0000_themerandomizer +++ b/mod/etc/options_menu/themes/settings/commands/c0000_themerandomizer @@ -2,4 +2,5 @@ COMMAND_NAME=THEME_RANDOMIZER COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_theme & +STATE_STR=sh %script_dir%/om_state theme_randomizer PREVIEW_IMAGE=/etc/options_menu/images/preview_theme_randomizer.png \ No newline at end of file diff --git a/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizer b/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizer index 8eb2ce3..9ccc576 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizer +++ b/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizer @@ -2,4 +2,5 @@ COMMAND_NAME=AUDIO_RANDOMIZER_HOME COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_audio & +STATE_STR=sh %script_dir%/om_state audio_randomizer PREVIEW_IMAGE=/etc/options_menu/images/preview_audio_randomizer_home.png \ No newline at end of file diff --git a/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizerall b/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizerall index 7caac16..038a97c 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizerall +++ b/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizerall @@ -3,4 +3,5 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE CHILD=TRUE COMMAND_STR=usleep 50000 && sh %script_dir%/om_audio -amh & +STATE_STR=sh %script_dir%/om_state audio_randomizer_all PREVIEW_IMAGE=/etc/options_menu/images/preview_audio_randomizer_all.png \ No newline at end of file diff --git a/mod/etc/options_menu/themes/settings/commands/c9999_Back b/mod/etc/options_menu/themes/settings/commands/c9999_Back deleted file mode 100644 index a224a60..0000000 --- a/mod/etc/options_menu/themes/settings/commands/c9999_Back +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=BACK -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=usleep 50000 && %options_path%/options --commandPath %options_path%/themes/commands/ --scriptPath %options_path%/themes/scripts --title "THEME_SELECTOR" & -PREVIEW_IMAGE=/etc/options_menu/images/back.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/settings/scripts/om_audio b/mod/etc/options_menu/themes/settings/scripts/om_audio index 73f7f69..1b1c364 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_audio +++ b/mod/etc/options_menu/themes/settings/scripts/om_audio @@ -21,4 +21,4 @@ script_init audioSettings "$1" -$optionsMenu/options --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/settings/scripts/om_reset b/mod/etc/options_menu/themes/settings/scripts/om_reset index f0657ae..7264180 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_reset +++ b/mod/etc/options_menu/themes/settings/scripts/om_reset @@ -23,4 +23,4 @@ script_init remove "$b_file" "$p_file" hakchi reboot -$optionsMenu/options --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/settings/scripts/om_state b/mod/etc/options_menu/themes/settings/scripts/om_state new file mode 100644 index 0000000..d5a75fa --- /dev/null +++ b/mod/etc/options_menu/themes/settings/scripts/om_state @@ -0,0 +1,40 @@ +#!/bin/sh +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Reports current on/off state for a Settings toggle row - read-only +# counterpart to om_theme/om_audio, which flip the same flag files. The +# engine calls this once per toggle row every time the Settings screen +# loads, so it reads $state_path from the cache om_settings writes just +# before launching theme_manager, instead of re-sourcing om_functions/ +# preinit (~0.1-0.3s) five times over for the same value. +state_path="$(cat /tmp/om_state_path 2> /dev/null)" +if [ -z "$state_path" ]; then + # shellcheck source=/dev/null + source $mountpoint/etc/options_menu/scripts/om_functions + script_init +fi + +case "$1" in + theme_per_folder) flag="$state_path/om_themeperfolder" ;; + theme_per_folder_child) flag="$state_path/om_themeperfolderchild" ;; + theme_randomizer) flag="$state_path/om_themerandomizer" ;; + audio_randomizer) flag="$state_path/om_audiorandomizer" ;; + audio_randomizer_all) flag="$state_path/om_advmusichack" ;; +esac + +[ -f "$flag" ] && echo on || echo off diff --git a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup index 8f62e22..783aaf4 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup +++ b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup @@ -25,4 +25,4 @@ modCleanUp usleep 1000 -$optionsMenu/options --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/settings/scripts/om_theme b/mod/etc/options_menu/themes/settings/scripts/om_theme index f01231c..5be57b0 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_theme +++ b/mod/etc/options_menu/themes/settings/scripts/om_theme @@ -22,4 +22,4 @@ script_init themeSettings "$1" -$optionsMenu/options --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & diff --git a/mod/install b/mod/install index 85b5aad..f1dee6f 100644 --- a/mod/install +++ b/mod/install @@ -23,18 +23,6 @@ del() { [ -f "$1" ] && rm -rf "$1" } -# writes $om_themes_scripts/om_.json by combining the console's own -# "texture" block (read live from $2, never shipped by us) with our own -# "sprites" fragment (om_.sprites.json) - so the only thing this hmod -# vendors is content it actually authored, not a copy of the console's own -# sprite atlas metadata -genThemeJson() { - [ -f "$2" ] || return - texture="$(grep -o '"texture":[[:space:]]*{[^}]*}' "$2")" - sprites="$(cat "$om_themes_scripts/om_${1}.sprites.json")" - echo "{${texture},\"sprites\":${sprites}}" >"$om_themes_scripts/om_${1}.json" -} - om_themes_scripts="$rootfs/etc/options_menu/themes/scripts" usr="$rootfs/usr" usr_share="$usr/share" @@ -80,28 +68,11 @@ done copy "$om_themes_scripts/om_fontfix" "$pre_initd/b8025_fontfix" copy "$om_themes_scripts/om_chmenu" "$rootfs/bin/chmenu" chmod +x "$rootfs/bin/chmenu" -chmod +x "$usr/bin/pngpack-upx" +chmod +x "$usr/bin/sprite_crop" sed '1,2d' "$om_themes_scripts/om_themescript" >"$b_file" [ ! -f "$p_file" ] && echo themeSelector >"$p_file" -# regenerate the DIY sprite templates against this console's own atlas data -# (see genThemeJson above) - wall/wall_far/wall_near/packed only exist on -# SNES's UI, packed_nes is clover-ui's (NES) own equivalent -if [ "$sftype" = "snes" ]; then - ui0="ui/$sftype-$sfregion" - [ "$sfregion" = "jpn" ] && ui0="ui/shvc" - wallpaper_dir="$usr_share/$ui0/resources/layout/asset/sprite/wallpaper" - genThemeJson wall "$wallpaper_dir/wall.json" - genThemeJson wall_far "$wallpaper_dir/wall_far.json" - genThemeJson wall_near "$wallpaper_dir/wall_near.json" - genThemeJson packed "$usr_share/$ui0/resources/sprites/packed.json" -elif [ "$sftype" = "nes" ]; then - packed_nes_src="$usr_share/clover-ui/resources/sprites/nes.json" - [ "$sfregion" = "jpn" ] && packed_nes_src="$usr_share/clover-ui/resources/sprites/hvc.json" - genThemeJson packed_nes "$packed_nes_src" -fi - # shellcheck disable=SC2034 cfg_OM_ThemeSelector_enabled='y' return 1 diff --git a/mod/uninstall b/mod/uninstall index 7f9f1a6..f39034c 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -102,6 +102,7 @@ remove "$rootfs/etc/options_menu/images/update.png" remove "$rootfs/etc/options_menu/images/wall.png" remove "$rootfs/etc/options_menu/images/wall_far.png" remove "$rootfs/etc/usr/bin/pngpack-upx" +remove "$rootfs/etc/usr/bin/sprite_crop" [ -f "$TITLE_FONT_BACKUP" ] && mv "$TITLE_FONT_BACKUP" "$TITLE_FONT" for FONT in s m l small medium large; do diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..7ddd232 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,477 @@ +/** + * Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +// Standalone theme manager - a dedicated Modern-UI screen for om_theme-selector, +// reusing OptionsMenu's engine (framework/ + command.cpp + localization.cpp, +// vendored as a submodule) instead of the generic `options` binary. Every +// om_theme-selector screen (root, downloads, DIY creator at every depth, +// settings) is just this same binary relaunched with a different +// --commandPath, exactly like `options` itself - the shell side already +// generates real command files per screen (om_vars' themeLoader/getThemeList/ +// diyThemeChecker etc.), this only needs to read and render them. + +#include "framework/sdl_helper.h" +#include "framework/controller.h" +#include "framework/powerwatch.h" +#include "framework/draw_helpers.h" +#include "framework/utf8.h" +#include "command.h" +#include "localization.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifndef MOD_VERSION +#define MOD_VERSION "dev" +#endif + +static void sReplace(std::string & command, std::string oldString, std::string newString) +{ + size_t pos; + while((pos = command.find(oldString)) != std::string::npos) + command.replace(pos, oldString.size(), newString); +} + +int main(int argc, char * argv[]) +{ + const std::string optionsLocation = "/etc/options_menu/"; + std::string commandLocation = optionsLocation + "themes/commands/"; + std::string scriptLocation = optionsLocation + "themes/scripts/"; + std::string titleKey = "THEME_SELECTOR"; + bool isRootScreen = true; // no --commandPath override - this is the top of the theme-selector's own tree + + for(int i = 1; i < argc; ++i) + { + if(strcmp(argv[i], "--commandPath") == 0 && i+1 < argc) + { + commandLocation = argv[++i]; + isRootScreen = false; + } + else if(strcmp(argv[i], "--scriptPath") == 0 && i+1 < argc) + scriptLocation = argv[++i]; + else if(strcmp(argv[i], "--title") == 0 && i+1 < argc) + titleKey = argv[++i]; + } + if(!commandLocation.empty() && commandLocation.back() != '/') + commandLocation += '/'; + if(!scriptLocation.empty() && scriptLocation.back() != '/') + scriptLocation += '/'; + + // set only by OptionsMenu itself (main.cpp), right before running the "Theme + // Options" row - a leftover pointer to OptionsMenu's own screen, so the root + // of the theme selector can offer a real way back to it (see the synthesized + // BACK row below, after commands load) + const char * backStackEnv = getenv("OM_BACK_STACK"); + std::string backStack = (isRootScreen && backStackEnv) ? backStackEnv : ""; + + LoadLanguageFromConfig(optionsLocation); + SetTTFFontPath(optionsLocation); + + // Read commands from the real, shell-generated command folder for this screen + std::vector commands; + std::ifstream in; + if(auto dir = opendir(commandLocation.c_str())) + { + std::list fileList; + while(auto entry = readdir(dir)) + { + if(entry->d_type == DT_REG && entry->d_name[0] == 'c') + fileList.push_back(entry->d_name); + } + closedir(dir); + + fileList.sort(); + for(auto & file : fileList) + { + in.open(commandLocation+file); + if(in.is_open()) + { + Command c(in); + if(!(commands.size() == 0 && c.command.size() == 0)) + { + sReplace(c.command, "%options_path%", optionsLocation); + sReplace(c.command, "%script_dir%", scriptLocation); + sReplace(c.deleteCommand, "%options_path%", optionsLocation); + sReplace(c.deleteCommand, "%script_dir%", scriptLocation); + if(c.isToggle) + { + sReplace(c.stateCommand, "%options_path%", optionsLocation); + sReplace(c.stateCommand, "%script_dir%", scriptLocation); + c.UpdateState(); + } + commands.push_back(c); + } + } + } + } + else + { + std::cerr << "Cannot open input folder.\n"; + exit(1); + } + + // real way back to OptionsMenu's own screen, only offered on the theme + // selector's own root (sub-screens already have their own static Back row + // pointing back to this root) - mirrors OptionsMenu main.cpp's own BACK + // synthesis so returning there lands on the exact screen we came from + if(!backStack.empty()) + { + std::vector entries; + for(size_t start = 0; start < backStack.size();) + { + size_t sep = backStack.find(';', start); + if(sep == std::string::npos) sep = backStack.size(); + entries.push_back(backStack.substr(start, sep-start)); + start = sep+1; + } + std::string lastEntry = entries.back(); + entries.pop_back(); + std::string remainingStack; + for(size_t i = 0; i < entries.size(); ++i) + { + if(i) remainingStack += ";"; + remainingStack += entries[i]; + } + + size_t c1 = lastEntry.find(','), c2 = lastEntry.find(',', c1+1); + std::string backPath = lastEntry.substr(0, c1); + std::string backScriptPath = lastEntry.substr(c1+1, c2-c1-1); + std::string backTitleKey = lastEntry.substr(c2+1); + + Command back; + back.name = "BACK"; + back.runInternal = false; + back.restartUI = false; + back.previewImage = optionsLocation + "images/preview_placeholder.png"; + back.command = "usleep 50000 && OM_BACK_STACK=\"" + remainingStack + "\" " + optionsLocation + "options --commandPath " + backPath + + (backScriptPath.empty() ? "" : " --scriptPath " + backScriptPath) + + " --title \"" + backTitleKey + "\" &"; + commands.push_back(back); + } + else if(!isRootScreen) + { + // every sub-screen used to need its own static c9999_Back file just + // to relaunch theme_manager - synthesized here instead. Screens with + // real cleanup to do first (e.g. the DIY sprite pickers re-enabling + // "Preview"/"DiY" rows, or Downloads refreshing the theme list) keep + // that as an om_return script in their own scriptLocation; this only + // needs to know whether one exists, not what it does. Screens with + // nothing to clean up (Settings, the DIY category list) just relaunch + // straight back to the theme selector's own root. + Command back; + back.name = "BACK"; + back.runInternal = false; + back.restartUI = false; + back.previewImage = optionsLocation + "images/preview_placeholder.png"; + std::ifstream returnScript(scriptLocation + "om_return"); + if(returnScript.good()) + back.command = "sh " + scriptLocation + "om_return &"; + else + back.command = "usleep 50000 && " + optionsLocation + "themes/theme_manager &"; + commands.push_back(back); + } + + if(commands.empty()) + { + std::cerr << "No usable commands in " << commandLocation << "\n"; + exit(1); + } + + // BACK/EXIT rows (the synthesized one above, or a static c9999_Back file - + // both sort last) are pinned near the footer instead of scrolling with + // the rest of the list, matching OptionsMenu's own main.cpp + int pinnedStartIndex = static_cast(commands.size()); + while(pinnedStartIndex > 0 && (commands[pinnedStartIndex-1].name == "BACK" || commands[pinnedStartIndex-1].name == "EXIT")) + --pinnedStartIndex; + + int currentCommandId = 0; + + SDL_Context sdl_context(std::chrono::milliseconds(33), false); + auto renderer = sdl_context.renderer; + Controller controller(1); + + const Uint8 bgR = UiTheme::BgR; + const Uint8 bgG = UiTheme::BgG; + const Uint8 bgB = UiTheme::BgB; + SDL_SetRenderDrawColor(renderer, bgR, bgG, bgB, 0xFF); + + Texture gearIcon(optionsLocation + UiTheme::AssetGear, renderer, UiTheme::GearX, UiTheme::GearY); + Texture switchOn(optionsLocation + UiTheme::AssetSwitchOn, renderer); + Texture switchOff(optionsLocation + UiTheme::AssetSwitchOff, renderer); + + Texture appTitleText("Theme Manager", UiTheme::TitleFontSize, renderer, UiTheme::TitleX, UiTheme::TitleY, false, 0xFFFFFFFF, true); + appTitleText.rect.y -= appTitleText.rect.h / 2; + Texture appVersionText(MOD_VERSION, UiTheme::VersionFontSize, renderer, appTitleText.rect.x + appTitleText.rect.w + UiTheme::VersionGap, UiTheme::TitleY, false, 0xFFFFFFFF, true); + appVersionText.rect.y -= appVersionText.rect.h / 2; + Texture titleText(Translate(titleKey), UiTheme::SectionTitleFontSize, renderer, UiTheme::SectionTitleX, UiTheme::SectionTitleY, false, 0xFFFFFFFF, true); + SDL_Rect selectedRowRect{ UiTheme::ListX, UiTheme::RowFirstY - 2, UiTheme::ListContentRightX - UiTheme::ListX, 0 }; + Texture creditText("Theme Manager - by DefKorns", 16, renderer, UiTheme::CreditX, UiTheme::CreditY, false, 0xFFFFFFFF, true); + Texture scrollUp("^", 16, renderer, UiTheme::ScrollX, UiTheme::ScrollUpY, false, 0xFFFFFFFF, true); + scrollUp.rect.x -= scrollUp.rect.w / 2; + Texture scrollDown = scrollUp; + scrollDown.rect.y = UiTheme::ScrollDownY; + + struct Badge { Texture letter; Texture label; UiTheme::BadgeColor rim; UiTheme::BadgeColor fill; }; + Badge badgeA{ Texture("A", 16, renderer, 0, 0, false, UiTheme::BadgeLetterColor, true), Texture(Translate("HINT_SELECT"), 16, renderer, 0, 0, false, 0xFFFFFFFF, true), UiTheme::BadgeADark, UiTheme::BadgeA }; + Texture badgeOuter(optionsLocation + UiTheme::AssetBadgeOuter, renderer); + Texture badgeInner(optionsLocation + UiTheme::AssetBadgeInner, renderer); + auto DrawBadge = [&](Badge & badge, int rightEdgeX) -> int + { + int groupW = UiTheme::BadgeOuterSize + UiTheme::BadgeLabelGap + badge.label.rect.w; + int x = rightEdgeX - groupW; + int y = UiTheme::BadgeBandY; + badgeOuter.rect = { x, y, UiTheme::BadgeOuterSize, UiTheme::BadgeOuterSize }; + SDL_SetTextureColorMod(badgeOuter.texture.get(), badge.rim.r, badge.rim.g, badge.rim.b); + badgeOuter.Draw(renderer); + int innerOffset = (UiTheme::BadgeOuterSize - UiTheme::BadgeInnerSize) / 2; + badgeInner.rect = { x+innerOffset, y+innerOffset, UiTheme::BadgeInnerSize, UiTheme::BadgeInnerSize }; + SDL_SetTextureColorMod(badgeInner.texture.get(), badge.fill.r, badge.fill.g, badge.fill.b); + badgeInner.Draw(renderer); + badge.letter.rect.x = x + (UiTheme::BadgeOuterSize - badge.letter.rect.w)/2; + badge.letter.rect.y = y + (UiTheme::BadgeOuterSize - badge.letter.rect.h)/2; + badge.letter.Draw(renderer); + badge.label.rect.x = x + UiTheme::BadgeOuterSize + UiTheme::BadgeLabelGap; + badge.label.rect.y = y + (UiTheme::BadgeOuterSize - badge.label.rect.h)/2; + badge.label.Draw(renderer); + return x - UiTheme::BadgeGroupGap; + }; + + const int RowGlyphSize = 16; + const int RowTextGapPx = 16; + const int ChildIndent = 4*16; + for(Command & c : commands) + { + int textX = UiTheme::RowTextX + (c.child ? ChildIndent : 0); + std::string label = Translate(c.name); + + int maxRight = UiTheme::RowControlRightX - RowTextGapPx; + bool rowUsesTTF = CanRenderWithTTF(label, RowGlyphSize); + int available = std::max(0, maxRight - textX); + if(rowUsesTTF) + { + if(MeasureTTFWidth(label, RowGlyphSize) > available) + { + int n = Utf8Length(label); + while(n > 0 && MeasureTTFWidth(TruncateUtf8(label, n) + "...", RowGlyphSize) > available) + --n; + label = TruncateUtf8(label, n) + "..."; + } + } + else + { + int maxChars = available / RowGlyphSize; + if(TruncateUtf8(label, maxChars).size() != label.size()) + label = TruncateUtf8(label, std::max(0, maxChars - 3)) + "..."; + } + + c.texture = Texture(label, RowGlyphSize, renderer, textX, 0, false, 0xFFFFFFFF, true); + } + + const int modernRowPitch = std::max(UiTheme::RowPitch, GetTTFLineHeight(RowGlyphSize)); + int pinnedAreaTop; + { + int slotBottom = UiTheme::FooterDividerY - UiTheme::PinnedBottomMargin; + for(int i = static_cast(commands.size())-1; i >= pinnedStartIndex; --i) + { + int slotTop = slotBottom - modernRowPitch; + commands[i].texture.rect.y = slotTop + (modernRowPitch - commands[i].texture.rect.h) / 2 + UiTheme::RowTextYNudge; + slotBottom = slotTop; + } + pinnedAreaTop = slotBottom; + } + const int DisplayItemCount = std::max(1, (pinnedAreaTop - UiTheme::RowFirstY) / modernRowPitch); + + int topListItemNumber = 1; // forces SetCurrentCommand(0) below to lay out row Y positions the first time + std::shared_ptr PreviewImage; + auto SetCurrentCommand = [&] (int newCommandId) + { + currentCommandId = newCommandId; + const Command & currentCommand = commands[currentCommandId]; + + // pinned rows don't affect scroll position + int scrollTarget = std::min(currentCommandId, std::max(0, pinnedStartIndex-1)); + + bool updateCommandYPos = false; + if(scrollTarget < topListItemNumber) + { + topListItemNumber = scrollTarget; + updateCommandYPos = true; + } + else if(scrollTarget >= topListItemNumber+DisplayItemCount) + { + topListItemNumber = scrollTarget-DisplayItemCount+1; + updateCommandYPos = true; + } + if(updateCommandYPos) + { + int y = UiTheme::RowFirstY; + for(int i = 0, count = std::min(DisplayItemCount, pinnedStartIndex-topListItemNumber); i < count; ++i) + { + Texture & rowTexture = commands[i+topListItemNumber].texture; + rowTexture.rect.y = y + (modernRowPitch - rowTexture.rect.h) / 2 + UiTheme::RowTextYNudge; + y += modernRowPitch; + } + } + + selectedRowRect.y = currentCommand.texture.rect.y - 2; + selectedRowRect.h = currentCommand.texture.rect.h + 4; + + if(currentCommand.previewImage.size()) + { + // linear by default (photo-like screenshots); nearest for small + // pixel-art sprites blown up a lot, where linear just blurs them + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, currentCommand.previewNearest ? "0" : "1"); + PreviewImage = std::make_shared(currentCommand.previewImage, renderer, 0, 0); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + if(PreviewImage->rect.w > 0 && PreviewImage->rect.h > 0) + { + const int maxW = UiTheme::DetailW - 2*UiTheme::ContentPadding; + const int maxH = UiTheme::PreviewBoxH - 2*UiTheme::ContentPadding; + double scale = std::min((double)maxW / PreviewImage->rect.w, (double)maxH / PreviewImage->rect.h); + PreviewImage->rect.w = static_cast(PreviewImage->rect.w * scale); + PreviewImage->rect.h = static_cast(PreviewImage->rect.h * scale); + } + } + else + PreviewImage.reset(); + }; + SetCurrentCommand(0); + + auto DrawRow = [&](Command & rowCommand, bool isLastOverall) + { + bool selected = &rowCommand == &commands[currentCommandId]; + rowCommand.texture.Draw(renderer); + if(rowCommand.isToggle) + { + Texture & rowSwitch = rowCommand.stateOn ? switchOn : switchOff; + rowSwitch.rect.x = UiTheme::SwitchRightX - UiTheme::SwitchW; + rowSwitch.rect.y = rowCommand.texture.rect.y + (rowCommand.texture.rect.h - UiTheme::SwitchH) / 2; + rowSwitch.Draw(renderer); + } + // skip for separators and the last row - nothing to separate there + if(!selected && !rowCommand.command.empty() && !isLastOverall) + DrawHLine(renderer, UiTheme::ListX, UiTheme::ListContentRightX, rowCommand.texture.rect.y + rowCommand.texture.rect.h + 3, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB); + }; + + auto DrawChrome = [&]() + { + DrawStrokeRect(renderer, UiTheme::OuterRect, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BorderRadius); + gearIcon.Draw(renderer); + appTitleText.Draw(renderer); + appVersionText.Draw(renderer); + DrawHLine(renderer, UiTheme::HeaderDividerX, UiTheme::HeaderDividerX + UiTheme::HeaderDividerW, UiTheme::HeaderDividerY, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth); + DrawHLine(renderer, UiTheme::OuterRect.x, UiTheme::OuterRect.x + UiTheme::OuterRect.w, UiTheme::FooterDividerY, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth); + int accentBarY = titleText.rect.y + (titleText.rect.h - UiTheme::SectionAccentBarH) / 2; + DrawFillRect(renderer, { UiTheme::SectionTitleX - UiTheme::SectionAccentBarW - 14, accentBarY, UiTheme::SectionAccentBarW, UiTheme::SectionAccentBarH }, UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB); + + DrawRoundedFillRect(renderer, selectedRowRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); + DrawStrokeRect(renderer, selectedRowRect, UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB, 2, UiTheme::BoxRadius); + + if(PreviewImage.get()) + { + SDL_Rect previewBox{ UiTheme::DetailX, UiTheme::PreviewBoxY, UiTheme::DetailW, UiTheme::PreviewBoxH }; + DrawStrokeRect(renderer, previewBox, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BoxRadius); + PreviewImage->rect.x = previewBox.x + (previewBox.w - PreviewImage->rect.w) / 2; + PreviewImage->rect.y = previewBox.y + (previewBox.h - PreviewImage->rect.h) / 2; + PreviewImage->Draw(renderer); + } + + // B has nothing to do on any screen here - navigation is always via a + // real "Back" row in the list (like every other row), not a shortcut + DrawBadge(badgeA, UiTheme::BadgeClusterRightX); + }; + + for(;;) + { + sdl_context.StartFrame(); + controller.Update(); + + SDL_Event e; + while(SDL_PollEvent(&e)) + if(e.type == SDL_QUIT) + return 0; + + if(sdl_context.powerwatch->buttonPress()) + break; + + if(controller.GetButtonStatus(A) || controller.GetButtonStatus(START)) + { + if(commands[currentCommandId].runInternal) + { + commands[currentCommandId].RunCommand(sdl_context, &controller, { gearIcon, appTitleText, appVersionText, creditText }, bgR, bgG, bgB); + if(commands[currentCommandId].isToggle) + commands[currentCommandId].UpdateState(); + } + else + { + system(commands[currentCommandId].command.c_str()); + break; + } + } + else if(controller.GetButtonStatus(UP)) + { + // bounded so an all-separator list can't spin forever + int newCommandId = currentCommandId; + for(size_t tries = 0; tries < commands.size(); ++tries) + { + newCommandId = (newCommandId-1+commands.size())%commands.size(); + if(commands[newCommandId].command.size() != 0) + break; + } + SetCurrentCommand(newCommandId); + } + else if(controller.GetButtonStatus(DOWN)) + { + int newCommandId = currentCommandId; + for(size_t tries = 0; tries < commands.size(); ++tries) + { + newCommandId = (newCommandId+1)%commands.size(); + if(commands[newCommandId].command.size() != 0) + break; + } + SetCurrentCommand(newCommandId); + } + // tap B = jump straight to the last row (BACK here, EXIT on the real + // OptionsMenu) - matches main.cpp's own B behavior + else if(controller.GetButtonStatus(B)) + { + SetCurrentCommand(commands.size()-1); + } + + DrawChrome(); + titleText.Draw(renderer); + + int lastCommandIndex = static_cast(commands.size()) - 1; + for(int i = 0, count = std::min(DisplayItemCount, pinnedStartIndex-topListItemNumber); i < count; ++i) + DrawRow(commands[i+topListItemNumber], (i+topListItemNumber) == lastCommandIndex); + + // pinned trailing rows (Back/Exit) - always visible near the footer + for(int i = pinnedStartIndex; i <= lastCommandIndex; ++i) + DrawRow(commands[i], i == lastCommandIndex); + + creditText.Draw(renderer); + + // bounded by pinnedStartIndex, not commands.size() - pinned rows need no scroll + if(topListItemNumber != 0) + scrollUp.Draw(renderer); + if((topListItemNumber + DisplayItemCount) < pinnedStartIndex) + scrollDown.Draw(renderer, SDL_FLIP_VERTICAL); + + SDL_SetRenderDrawColor(renderer, bgR, bgG, bgB, 0xFF); + + sdl_context.EndFrame(); + } + + return 0; +} diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu new file mode 160000 index 0000000..dcf34a8 --- /dev/null +++ b/vendor/OptionsMenu @@ -0,0 +1 @@ +Subproject commit dcf34a85b1200c5483df9f841e820ad50861ef83 From faf5c604c77ceed2585d71f24bbac45cac2a0984 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 16 Sep 2026 14:29:13 +0000 Subject: [PATCH 036/210] Update subproject commit reference for OptionsMenu --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index dcf34a8..d30df3b 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit dcf34a85b1200c5483df9f841e820ad50861ef83 +Subproject commit d30df3befde78b7801b7e47adf4dd96c0eaf46e7 From d79a47a6581d23290d85cbaacdf5727da0e962d4 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 16 Sep 2026 14:43:42 +0000 Subject: [PATCH 037/210] Update subproject commit reference for pngpack --- pngpack | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pngpack b/pngpack index 20e8e9e..a5d5bec 160000 --- a/pngpack +++ b/pngpack @@ -1 +1 @@ -Subproject commit 20e8e9e5213c2c2f4334f944b81fa90b2d30523f +Subproject commit a5d5bec183cd93a9bd29df5c6cd05a8644474bc2 From f0ce6a88c8c2ed987d018077f4bdad8716a8aa44 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 16 Sep 2026 15:54:36 +0000 Subject: [PATCH 038/210] Update subproject commit reference for OptionsMenu --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index d30df3b..798db78 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit d30df3befde78b7801b7e47adf4dd96c0eaf46e7 +Subproject commit 798db78e0095b1ace3a62cb26ec7ea4bd52e34cd From 96ff8618e6a39532e41cb71bc103e8fb21645015 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 16 Sep 2026 18:54:46 +0000 Subject: [PATCH 039/210] update .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c89dadd..ba985ed 100644 --- a/.gitignore +++ b/.gitignore @@ -553,3 +553,5 @@ b0010_functions b0050_boot _console_originals/ tools/*.py +tools/MaterialSymbolsOutlined.codepoints +tools/MaterialSymbolsOutlined.ttf From a3222e3e32e57cb77dd1fbdc4e88cd1fbcbdcaf7 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 16 Sep 2026 18:55:01 +0000 Subject: [PATCH 040/210] Update subproject commit reference for OptionsMenu --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 798db78..e07cc5d 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 798db78e0095b1ace3a62cb26ec7ea4bd52e34cd +Subproject commit e07cc5da4c4d95cbb91a94a2efd6393ecbf343cd From d3f4a11047e9b34a0a8040332e39f2eaf652ae8e Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 16 Sep 2026 20:05:18 +0000 Subject: [PATCH 041/210] Add standalone Docker build wrapper for theme_manager build.sh fetches the toolchain/vendor submodules on demand and runs Makefile.docker inside the hakchi-toolchain:jessie-armhf container, so building theme_manager doesn't require a manually set up cross compiler on the host. --- Makefile.docker | 40 ++++++++++++++++++++++++++++++++++++++++ build.sh | 30 ++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 Makefile.docker create mode 100644 build.sh diff --git a/Makefile.docker b/Makefile.docker new file mode 100644 index 0000000..97c3833 --- /dev/null +++ b/Makefile.docker @@ -0,0 +1,40 @@ +# Cross-compiles the standalone theme manager skeleton (src/main.cpp) against +# the shared rendering framework vendored from OptionsMenu (vendor/OptionsMenu, +# a git submodule - see that repo's src/framework/ for what's reused here). +# This is a separate build from the existing Makefile (which only packages +# the shell-script based hmod) - run via build.sh, mirroring OptionsMenu's +# own Docker-toolchain setup. +FRAMEWORK_DIR = vendor/OptionsMenu/src/framework +CXX = g++ +STRIP = strip +ifdef CROSS_PREFIX +PKG_CONFIG_LIBDIR = /usr/lib/arm-linux-gnueabihf/pkgconfig +SDL_CFLAGS = -I/usr/include/arm-linux-gnueabihf $(shell PKG_CONFIG_LIBDIR=$(PKG_CONFIG_LIBDIR) pkg-config --cflags sdl2 SDL2_ttf libpng) +SDL_LIBS = $(shell PKG_CONFIG_LIBDIR=$(PKG_CONFIG_LIBDIR) pkg-config --libs sdl2 SDL2_ttf libpng) +LDFLAGS = -Wl,--allow-shlib-undefined +else +SDL_CFLAGS = $(shell sdl2-config --cflags) $(shell pkg-config --cflags SDL2_ttf) +SDL_LIBS = $(shell sdl2-config --libs) $(shell pkg-config --libs SDL2_ttf) -lpng +LDFLAGS = +endif +CXXFLAGS = -std=c++11 -Os -Ivendor/OptionsMenu/src $(SDL_CFLAGS) +LDLIBS = $(SDL_LIBS) +VENDOR_SRC_DIR = vendor/OptionsMenu/src +SOURCES = src/main.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp +OBJECTS = $(SOURCES:.cpp=.o) + +all: out/theme_manager + +out/theme_manager: $(OBJECTS) + mkdir -p out + $(CROSS_PREFIX)$(CXX) $(OBJECTS) $(LDLIBS) $(LDFLAGS) -Wl,-rpath,/etc/options_menu -o out/theme_manager + $(CROSS_PREFIX)$(STRIP) out/theme_manager + +%.o: %.cpp + $(CROSS_PREFIX)$(CXX) $(CXXFLAGS) -c $< -o $@ + +clean: + find . -name "*.o" -type f -delete + rm -rf out/ + +.PHONY: clean diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..175d99e --- /dev/null +++ b/build.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -euo pipefail + +# stops Git Bash from mangling the /src path below into a Windows path +export MSYS_NO_PATHCONV=1 + +IMAGE="hakchi-toolchain:jessie-armhf" + +if [ ! -f toolchain/Dockerfile.jessie-armhf ]; then + echo "==> Fetching toolchain submodule..." + git submodule update --init --recursive toolchain +fi + +if [ ! -f vendor/OptionsMenu/src/framework/sdl_context.cpp ]; then + echo "==> Fetching vendor/OptionsMenu submodule..." + git submodule update --init --recursive vendor/OptionsMenu +fi + +if ! docker image inspect "$IMAGE" >/dev/null 2>&1; then + echo "==> Building Docker image..." + docker build -f toolchain/Dockerfile.jessie-armhf -t "$IMAGE" toolchain +fi + +echo "==> Running make inside the container..." +docker run --rm -v "$PWD:/src" "$IMAGE" make -f Makefile.docker CROSS_PREFIX=arm-linux-gnueabihf- "$@" + +if [ -d out/ ]; then + echo "==> Build complete. Files in out/:" + ls -la out/ +fi From cc998132428aee7ebe6e75d47121cf883a9da0dc Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 16 Sep 2026 20:06:15 +0000 Subject: [PATCH 042/210] Add grid layout mode to theme_manager Extends the existing list-only renderer with a --layout grid mode: an action strip of fixed commands, a theme/asset grid below it with lazy-loaded tile images, 2D navigation between strip and grid, and a footer B badge that runs the pinned Back/Exit row directly instead of rendering it as a chip. Screens with no fixed actions (a DIY sprite category) skip the strip and use the freed row for a 3rd grid row; screens whose preview crops are square-ish use a denser, more square tile column count instead of the default wide tile. --- src/main.cpp | 470 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 388 insertions(+), 82 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 7ddd232..6be8d88 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,14 +7,15 @@ * (at your option) any later version. */ -// Standalone theme manager - a dedicated Modern-UI screen for om_theme-selector, -// reusing OptionsMenu's engine (framework/ + command.cpp + localization.cpp, -// vendored as a submodule) instead of the generic `options` binary. Every -// om_theme-selector screen (root, downloads, DIY creator at every depth, -// settings) is just this same binary relaunched with a different -// --commandPath, exactly like `options` itself - the shell side already -// generates real command files per screen (om_vars' themeLoader/getThemeList/ -// diyThemeChecker etc.), this only needs to read and render them. +// Standalone theme manager for om_theme-selector, reusing OptionsMenu's +// engine (framework/ + command.cpp + localization.cpp, vendored submodule) +// instead of the generic `options` binary. Every screen is this same binary +// relaunched with a different --commandPath, reading command files the +// shell side already generates. +// +// --layout list (default): single-column option picker. +// --layout grid: fixed actions as a chip strip, everything else (themes, +// DIY assets) as a scrollable tile grid. #include "framework/sdl_helper.h" #include "framework/controller.h" @@ -43,6 +44,42 @@ static void sReplace(std::string & command, std::string oldString, std::string n command.replace(pos, oldString.size(), newString); } +// scale-to-fit-centered +static void FitCentered(Texture & tex, int boxX, int boxY, int boxW, int boxH, int padding) +{ + const int maxW = boxW - 2*padding; + const int maxH = boxH - 2*padding; + if(tex.rect.w <= 0 || tex.rect.h <= 0) + return; + double scale = std::min((double)maxW / tex.rect.w, (double)maxH / tex.rect.h); + tex.rect.w = static_cast(tex.rect.w * scale); + tex.rect.h = static_cast(tex.rect.h * scale); + tex.rect.x = boxX + (boxW - tex.rect.w) / 2; + tex.rect.y = boxY + (boxH - tex.rect.h) / 2; +} + +static std::string TruncateToWidth(const std::string & text, int glyphSize, int available) +{ + std::string label = text; + if(CanRenderWithTTF(label, glyphSize)) + { + if(MeasureTTFWidth(label, glyphSize) > available) + { + int n = Utf8Length(label); + while(n > 0 && MeasureTTFWidth(TruncateUtf8(label, n) + "...", glyphSize) > available) + --n; + label = TruncateUtf8(label, n) + "..."; + } + } + else + { + int maxChars = available / glyphSize; + if(TruncateUtf8(label, maxChars).size() != label.size()) + label = TruncateUtf8(label, std::max(0, maxChars - 3)) + "..."; + } + return label; +} + int main(int argc, char * argv[]) { const std::string optionsLocation = "/etc/options_menu/"; @@ -50,6 +87,7 @@ int main(int argc, char * argv[]) std::string scriptLocation = optionsLocation + "themes/scripts/"; std::string titleKey = "THEME_SELECTOR"; bool isRootScreen = true; // no --commandPath override - this is the top of the theme-selector's own tree + bool gridLayout = false; for(int i = 1; i < argc; ++i) { @@ -62,24 +100,26 @@ int main(int argc, char * argv[]) scriptLocation = argv[++i]; else if(strcmp(argv[i], "--title") == 0 && i+1 < argc) titleKey = argv[++i]; + else if(strcmp(argv[i], "--layout") == 0 && i+1 < argc) + gridLayout = (std::string(argv[++i]) == "grid"); } if(!commandLocation.empty() && commandLocation.back() != '/') commandLocation += '/'; if(!scriptLocation.empty() && scriptLocation.back() != '/') scriptLocation += '/'; - // set only by OptionsMenu itself (main.cpp), right before running the "Theme - // Options" row - a leftover pointer to OptionsMenu's own screen, so the root - // of the theme selector can offer a real way back to it (see the synthesized - // BACK row below, after commands load) + // set by OptionsMenu before launching the "Theme Options" row - lets the + // root screen offer a real way back to it const char * backStackEnv = getenv("OM_BACK_STACK"); std::string backStack = (isRootScreen && backStackEnv) ? backStackEnv : ""; LoadLanguageFromConfig(optionsLocation); SetTTFFontPath(optionsLocation); - // Read commands from the real, shell-generated command folder for this screen + // isThemeItem: false for a "c0000_*" fixed action, true for a theme/asset + // row - only used by grid layout, to split strip vs. tile std::vector commands; + std::vector isThemeItem; std::ifstream in; if(auto dir = opendir(commandLocation.c_str())) { @@ -111,6 +151,7 @@ int main(int argc, char * argv[]) c.UpdateState(); } commands.push_back(c); + isThemeItem.push_back(file.compare(0, 6, "c0000_") != 0); } } } @@ -121,10 +162,6 @@ int main(int argc, char * argv[]) exit(1); } - // real way back to OptionsMenu's own screen, only offered on the theme - // selector's own root (sub-screens already have their own static Back row - // pointing back to this root) - mirrors OptionsMenu main.cpp's own BACK - // synthesis so returning there lands on the exact screen we came from if(!backStack.empty()) { std::vector entries; @@ -158,17 +195,12 @@ int main(int argc, char * argv[]) + (backScriptPath.empty() ? "" : " --scriptPath " + backScriptPath) + " --title \"" + backTitleKey + "\" &"; commands.push_back(back); + isThemeItem.push_back(false); } else if(!isRootScreen) { - // every sub-screen used to need its own static c9999_Back file just - // to relaunch theme_manager - synthesized here instead. Screens with - // real cleanup to do first (e.g. the DIY sprite pickers re-enabling - // "Preview"/"DiY" rows, or Downloads refreshing the theme list) keep - // that as an om_return script in their own scriptLocation; this only - // needs to know whether one exists, not what it does. Screens with - // nothing to clean up (Settings, the DIY category list) just relaunch - // straight back to the theme selector's own root. + // use the screen's own om_return for cleanup if it has one, else + // just relaunch straight back to root Command back; back.name = "BACK"; back.runInternal = false; @@ -178,8 +210,9 @@ int main(int argc, char * argv[]) if(returnScript.good()) back.command = "sh " + scriptLocation + "om_return &"; else - back.command = "usleep 50000 && " + optionsLocation + "themes/theme_manager &"; + back.command = "usleep 50000 && " + optionsLocation + "themes/theme_manager --title INSTALLED_THEMES --layout grid &"; commands.push_back(back); + isThemeItem.push_back(false); } if(commands.empty()) @@ -188,13 +221,17 @@ int main(int argc, char * argv[]) exit(1); } - // BACK/EXIT rows (the synthesized one above, or a static c9999_Back file - - // both sort last) are pinned near the footer instead of scrolling with - // the rest of the list, matching OptionsMenu's own main.cpp + // BACK/EXIT sort last - pin them near the footer instead of scrolling int pinnedStartIndex = static_cast(commands.size()); while(pinnedStartIndex > 0 && (commands[pinnedStartIndex-1].name == "BACK" || commands[pinnedStartIndex-1].name == "EXIT")) --pinnedStartIndex; + // grid layout only: first non-fixed-action row (themeStart==pinnedStartIndex + // for an empty list - still renders as grid, just with an empty grid area) + int themeStart = 0; + while(themeStart < pinnedStartIndex && !isThemeItem[themeStart]) + ++themeStart; + int currentCommandId = 0; SDL_Context sdl_context(std::chrono::milliseconds(33), false); @@ -215,12 +252,7 @@ int main(int argc, char * argv[]) Texture appVersionText(MOD_VERSION, UiTheme::VersionFontSize, renderer, appTitleText.rect.x + appTitleText.rect.w + UiTheme::VersionGap, UiTheme::TitleY, false, 0xFFFFFFFF, true); appVersionText.rect.y -= appVersionText.rect.h / 2; Texture titleText(Translate(titleKey), UiTheme::SectionTitleFontSize, renderer, UiTheme::SectionTitleX, UiTheme::SectionTitleY, false, 0xFFFFFFFF, true); - SDL_Rect selectedRowRect{ UiTheme::ListX, UiTheme::RowFirstY - 2, UiTheme::ListContentRightX - UiTheme::ListX, 0 }; Texture creditText("Theme Manager - by DefKorns", 16, renderer, UiTheme::CreditX, UiTheme::CreditY, false, 0xFFFFFFFF, true); - Texture scrollUp("^", 16, renderer, UiTheme::ScrollX, UiTheme::ScrollUpY, false, 0xFFFFFFFF, true); - scrollUp.rect.x -= scrollUp.rect.w / 2; - Texture scrollDown = scrollUp; - scrollDown.rect.y = UiTheme::ScrollDownY; struct Badge { Texture letter; Texture label; UiTheme::BadgeColor rim; UiTheme::BadgeColor fill; }; Badge badgeA{ Texture("A", 16, renderer, 0, 0, false, UiTheme::BadgeLetterColor, true), Texture(Translate("HINT_SELECT"), 16, renderer, 0, 0, false, 0xFFFFFFFF, true), UiTheme::BadgeADark, UiTheme::BadgeA }; @@ -247,34 +279,333 @@ int main(int argc, char * argv[]) return x - UiTheme::BadgeGroupGap; }; - const int RowGlyphSize = 16; - const int RowTextGapPx = 16; - const int ChildIndent = 4*16; - for(Command & c : commands) + // shared chrome; section title is separate since grid positions it lower + auto DrawChromeCommon = [&]() { - int textX = UiTheme::RowTextX + (c.child ? ChildIndent : 0); - std::string label = Translate(c.name); + DrawStrokeRect(renderer, UiTheme::OuterRect, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BorderRadius); + gearIcon.Draw(renderer); + appTitleText.Draw(renderer); + appVersionText.Draw(renderer); + DrawHLine(renderer, UiTheme::HeaderDividerX, UiTheme::HeaderDividerX + UiTheme::HeaderDividerW, UiTheme::HeaderDividerY, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth); + DrawHLine(renderer, UiTheme::OuterRect.x, UiTheme::OuterRect.x + UiTheme::OuterRect.w, UiTheme::FooterDividerY, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth); + DrawBadge(badgeA, UiTheme::BadgeClusterRightX); + creditText.Draw(renderer); + }; + auto DrawSectionTitle = [&]() + { + int accentBarY = titleText.rect.y + (titleText.rect.h - UiTheme::SectionAccentBarH) / 2; + DrawFillRect(renderer, { UiTheme::SectionTitleX - UiTheme::SectionAccentBarW - 14, accentBarY, UiTheme::SectionAccentBarW, UiTheme::SectionAccentBarH }, UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB); + titleText.Draw(renderer); + }; - int maxRight = UiTheme::RowControlRightX - RowTextGapPx; - bool rowUsesTTF = CanRenderWithTTF(label, RowGlyphSize); - int available = std::max(0, maxRight - textX); - if(rowUsesTTF) + if(gridLayout) + { + // ============================= GRID LAYOUT ============================= + // squareTiles: pack more/narrower columns for square-ish previews + const bool squareTiles = themeStart < pinnedStartIndex && commands[themeStart].previewSquare; + const int GridCols = squareTiles ? 7 : 4; + const int GridGap = 20; + + // strip = fixed actions [0,themeStart), skipping empty/separator rows. + // Back/Exit aren't chips here - hinted in the footer instead (badgeB) + std::vector stripIndices; + for(int i = 0; i < themeStart; ++i) + if(!commands[i].command.empty()) + stripIndices.push_back(i); + const int stripCount = static_cast(stripIndices.size()); + auto StripToCommand = [&](int pos) -> int { return stripIndices[pos]; }; + auto CommandToStrip = [&](int idx) -> int + { + for(int p = 0; p < stripCount; ++p) + if(stripIndices[p] == idx) + return p; + return 0; + }; + const int chipGap = 14; + const int chipIconSize = 22; // small, matches the 14px label's own scale + + // no strip on a pure-tile screen (e.g. a DIY category) - use the row for a 3rd grid row + const bool hasStrip = stripCount > 0; + const int StripTop = UiTheme::HeaderDividerY + 22; + const int StripH = hasStrip ? 58 : 0; + const int GridLeft = UiTheme::FrameX + 32; + const int GridRight = UiTheme::FrameX + UiTheme::FrameW - 32; + const int chipW = hasStrip ? (GridRight - GridLeft - (stripCount-1)*chipGap) / stripCount : 0; + titleText = Texture(Translate(titleKey), UiTheme::SectionTitleFontSize - 6, renderer, UiTheme::SectionTitleX, 0, false, 0xFFFFFFFF, true); + titleText.rect.y = hasStrip ? (StripTop + StripH + 22) : (UiTheme::HeaderDividerY + 22); + const int GridTop = titleText.rect.y + titleText.rect.h + 20; + const int GridBottom = UiTheme::FooterDividerY - 14; + const int TileW = (GridRight - GridLeft - (GridCols-1)*GridGap) / GridCols; + int TileH, GridRowsVisible; + if(hasStrip) + { + TileH = TileW * 9 / 16; + GridRowsVisible = std::max(1, (GridBottom - GridTop + GridGap) / (TileH + GridGap)); + } + else + { + GridRowsVisible = 3; + TileH = (GridBottom - GridTop - (GridRowsVisible-1)*GridGap) / GridRowsVisible; + } + const int GridRowPitch = TileH + GridGap; + + Texture gridScrollUp("^", 16, renderer, GridRight + 22, GridTop + 24, false, 0xFFFFFFFF, true); + gridScrollUp.rect.x -= gridScrollUp.rect.w / 2; + Texture gridScrollDown = gridScrollUp; + gridScrollDown.rect.y = GridBottom - 24 - gridScrollDown.rect.h; + + // Back/Exit hint, footer badge next to A's - B runs it directly + Badge badgeB{ Texture("B", 16, renderer, 0, 0, false, UiTheme::BadgeLetterColor, true), + Texture(pinnedStartIndex < (int)commands.size() ? Translate(commands[pinnedStartIndex].name) : "", 16, renderer, 0, 0, false, 0xFFFFFFFF, true), + UiTheme::BadgeBDark, UiTheme::BadgeB }; + + std::vector chipIcons(commands.size()); + std::vector chipLabels(commands.size()); + std::vector tileImages(commands.size()); + std::vector tileLabels(commands.size()); + for(int i = 0; i < (int)commands.size(); ++i) { - if(MeasureTTFWidth(label, RowGlyphSize) > available) + Command & c = commands[i]; + std::string label = Translate(c.name); + if(i < themeStart || i >= pinnedStartIndex) { - int n = Utf8Length(label); - while(n > 0 && MeasureTTFWidth(TruncateUtf8(label, n) + "...", RowGlyphSize) > available) - --n; - label = TruncateUtf8(label, n) + "..."; + if(commands[i].command.empty()) + continue; // separator, not a chip + int labelAvail = chipW - chipIconSize - 24; + if(c.previewImage.size()) + { + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); // smooth - these are small glyphs, nearest looks jagged + Texture icon(c.previewImage, renderer, 0, 0); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + FitCentered(icon, 0, 0, chipIconSize, chipIconSize, 0); + SDL_SetTextureColorMod(icon.texture.get(), UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB); + chipIcons[i] = icon; + labelAvail = chipW - chipIconSize - 32; + } + label = TruncateToWidth(label, 14, labelAvail); + chipLabels[i] = Texture(label, 14, renderer, 0, 0, false, 0xFFFFFFFF, true); + } + else + { + // tile image loaded lazily, see EnsureTileImage below + label = TruncateToWidth(label, 15, TileW - 12); + tileLabels[i] = Texture(label, 15, renderer, 0, 0, false, 0xFFFFFFFF, true); } } - else + std::vector tileImageLoaded(commands.size(), false); + auto EnsureTileImage = [&](int idx) { - int maxChars = available / RowGlyphSize; - if(TruncateUtf8(label, maxChars).size() != label.size()) - label = TruncateUtf8(label, std::max(0, maxChars - 3)) + "..."; + if(tileImageLoaded[idx]) + return; + tileImageLoaded[idx] = true; + const Command & c = commands[idx]; + if(c.previewImage.empty()) + return; + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, c.previewNearest ? "0" : "1"); + Texture art(c.previewImage, renderer, 0, 0); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + FitCentered(art, 0, 0, TileW - 16, TileH - 30, 0); + tileImages[idx] = art; + }; + + int gridTopRow = 0; + auto SetCurrentCommand = [&](int newId) + { + currentCommandId = newId; + if(newId >= themeStart && newId < pinnedStartIndex) + { + int row = (newId - themeStart) / GridCols; + if(row < gridTopRow) gridTopRow = row; + else if(row >= gridTopRow + GridRowsVisible) gridTopRow = row - GridRowsVisible + 1; + } + }; + SetCurrentCommand(themeStart < pinnedStartIndex ? themeStart : (stripCount > 0 ? StripToCommand(0) : 0)); + + for(;;) + { + sdl_context.StartFrame(); + controller.Update(); + + SDL_Event e; + while(SDL_PollEvent(&e)) + if(e.type == SDL_QUIT) + return 0; + + if(sdl_context.powerwatch->buttonPress()) + break; + + bool inGrid = currentCommandId >= themeStart && currentCommandId < pinnedStartIndex; + + if(controller.GetButtonStatus(A) || controller.GetButtonStatus(START)) + { + if(commands[currentCommandId].runInternal) + { + commands[currentCommandId].RunCommand(sdl_context, &controller, { gearIcon, appTitleText, appVersionText, creditText }, bgR, bgG, bgB); + if(commands[currentCommandId].isToggle) + commands[currentCommandId].UpdateState(); + } + else + { + system(commands[currentCommandId].command.c_str()); + break; + } + } + else if(controller.GetButtonStatus(LEFT)) + { + if(inGrid && currentCommandId > themeStart) + SetCurrentCommand(currentCommandId - 1); + else if(!inGrid && stripCount > 0) + SetCurrentCommand(StripToCommand(std::max(0, CommandToStrip(currentCommandId) - 1))); + } + else if(controller.GetButtonStatus(RIGHT)) + { + if(inGrid && currentCommandId < pinnedStartIndex - 1) + SetCurrentCommand(currentCommandId + 1); + else if(!inGrid && stripCount > 0) + SetCurrentCommand(StripToCommand(std::min(stripCount - 1, CommandToStrip(currentCommandId) + 1))); + } + else if(controller.GetButtonStatus(UP)) + { + if(inGrid) + { + int col = (currentCommandId - themeStart) % GridCols; + if(currentCommandId - themeStart < GridCols) // top grid row - jump up to the strip + { + if(stripCount > 0) + SetCurrentCommand(StripToCommand(std::min(stripCount - 1, col))); + } + else + SetCurrentCommand(currentCommandId - GridCols); + } + } + else if(controller.GetButtonStatus(DOWN)) + { + if(!inGrid && themeStart < pinnedStartIndex) + { + int col = std::min(GridCols - 1, CommandToStrip(currentCommandId)); + SetCurrentCommand(std::min(pinnedStartIndex - 1, themeStart + col)); + } + else if(inGrid && currentCommandId + GridCols < pinnedStartIndex) + SetCurrentCommand(currentCommandId + GridCols); + } + else if(controller.GetButtonStatus(B) && pinnedStartIndex < (int)commands.size()) + { + // Back/Exit isn't a chip - B runs it directly + Command & backCmd = commands[pinnedStartIndex]; + if(backCmd.runInternal) + { + backCmd.RunCommand(sdl_context, &controller, { gearIcon, appTitleText, appVersionText, creditText }, bgR, bgG, bgB); + if(backCmd.isToggle) + backCmd.UpdateState(); + } + else + { + system(backCmd.command.c_str()); + break; + } + } + + DrawChromeCommon(); + DrawSectionTitle(); + if(pinnedStartIndex < (int)commands.size()) + DrawBadge(badgeB, UiTheme::BadgeClusterRightX - UiTheme::BadgeOuterSize - UiTheme::BadgeLabelGap - badgeA.label.rect.w - UiTheme::BadgeGroupGap); + + // action strip + { + int x = GridLeft; + for(int pos = 0; pos < stripCount; ++pos) + { + int idx = StripToCommand(pos); + bool selected = idx == currentCommandId; + SDL_Rect chipRect{ x, StripTop, chipW, StripH }; + if(selected) + { + DrawRoundedFillRect(renderer, chipRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); + DrawStrokeRect(renderer, chipRect, UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB, 2, UiTheme::BoxRadius); + } + else + DrawStrokeRect(renderer, chipRect, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BoxRadius); + + // icon+label centered as one group, not each alone + Texture & icon = chipIcons[idx]; + Texture & label = chipLabels[idx]; + int iconW = icon.rect.h > 0 ? chipIconSize : 0; + int groupW = iconW + (iconW > 0 ? 12 : 0) + label.rect.w; + int gx = x + (chipW - groupW) / 2; + if(icon.rect.h > 0) + { + icon.rect.x = gx + (chipIconSize - icon.rect.w) / 2; + icon.rect.y = StripTop + (StripH - icon.rect.h) / 2; + icon.Draw(renderer); + gx += iconW + 12; + } + label.rect.x = gx; + label.rect.y = StripTop + (StripH - label.rect.h) / 2; + label.Draw(renderer); + + x += chipW + chipGap; + } + } + + // theme/asset grid + { + int lastThemeIndex = pinnedStartIndex - 1; + for(int row = 0; row < GridRowsVisible; ++row) + { + int rowStart = themeStart + (gridTopRow+row)*GridCols; + if(rowStart > lastThemeIndex) + break; + int y = GridTop + row*GridRowPitch; + for(int col = 0; col < GridCols; ++col) + { + int idx = rowStart + col; + if(idx > lastThemeIndex) + break; + int x = GridLeft + col*(TileW+GridGap); + bool selected = idx == currentCommandId; + SDL_Rect tileRect{ x, y, TileW, TileH }; + DrawRoundedFillRect(renderer, tileRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); + EnsureTileImage(idx); + if(tileImages[idx].rect.w > 0) + { + FitCentered(tileImages[idx], x, y+6, TileW, TileH-24, 8); + tileImages[idx].Draw(renderer); + } + DrawStrokeRect(renderer, tileRect, selected ? UiTheme::AccentR : UiTheme::BorderR, selected ? UiTheme::AccentG : UiTheme::BorderG, selected ? UiTheme::AccentB : UiTheme::BorderB, selected ? 3 : UiTheme::BorderWidth, UiTheme::BoxRadius); + tileLabels[idx].rect.x = x + (TileW - tileLabels[idx].rect.w) / 2; + tileLabels[idx].rect.y = y + TileH - tileLabels[idx].rect.h - 6; + tileLabels[idx].Draw(renderer); + } + } + + int totalRows = (lastThemeIndex - themeStart + GridCols) / GridCols; + if(gridTopRow > 0) + gridScrollUp.Draw(renderer); + if(gridTopRow + GridRowsVisible < totalRows) + gridScrollDown.Draw(renderer, SDL_FLIP_VERTICAL); + } + + SDL_SetRenderDrawColor(renderer, bgR, bgG, bgB, 0xFF); + sdl_context.EndFrame(); } + return 0; + } + + // ============================= LIST LAYOUT ============================= + Texture scrollUp("^", 16, renderer, UiTheme::ScrollX, UiTheme::ScrollUpY, false, 0xFFFFFFFF, true); + scrollUp.rect.x -= scrollUp.rect.w / 2; + Texture scrollDown = scrollUp; + scrollDown.rect.y = UiTheme::ScrollDownY; + SDL_Rect selectedRowRect{ UiTheme::ListX, UiTheme::RowFirstY - 2, UiTheme::ListContentRightX - UiTheme::ListX, 0 }; + + const int RowGlyphSize = 16; + const int RowTextGapPx = 16; + const int ChildIndent = 4*16; + for(Command & c : commands) + { + int textX = UiTheme::RowTextX + (c.child ? ChildIndent : 0); + std::string label = TruncateToWidth(Translate(c.name), RowGlyphSize, UiTheme::RowControlRightX - RowTextGapPx - textX); c.texture = Texture(label, RowGlyphSize, renderer, textX, 0, false, 0xFFFFFFFF, true); } @@ -329,19 +660,10 @@ int main(int argc, char * argv[]) if(currentCommand.previewImage.size()) { - // linear by default (photo-like screenshots); nearest for small - // pixel-art sprites blown up a lot, where linear just blurs them SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, currentCommand.previewNearest ? "0" : "1"); PreviewImage = std::make_shared(currentCommand.previewImage, renderer, 0, 0); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); - if(PreviewImage->rect.w > 0 && PreviewImage->rect.h > 0) - { - const int maxW = UiTheme::DetailW - 2*UiTheme::ContentPadding; - const int maxH = UiTheme::PreviewBoxH - 2*UiTheme::ContentPadding; - double scale = std::min((double)maxW / PreviewImage->rect.w, (double)maxH / PreviewImage->rect.h); - PreviewImage->rect.w = static_cast(PreviewImage->rect.w * scale); - PreviewImage->rect.h = static_cast(PreviewImage->rect.h * scale); - } + FitCentered(*PreviewImage, UiTheme::DetailX, UiTheme::PreviewBoxY, UiTheme::DetailW, UiTheme::PreviewBoxH, UiTheme::ContentPadding); } else PreviewImage.reset(); @@ -366,14 +688,8 @@ int main(int argc, char * argv[]) auto DrawChrome = [&]() { - DrawStrokeRect(renderer, UiTheme::OuterRect, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BorderRadius); - gearIcon.Draw(renderer); - appTitleText.Draw(renderer); - appVersionText.Draw(renderer); - DrawHLine(renderer, UiTheme::HeaderDividerX, UiTheme::HeaderDividerX + UiTheme::HeaderDividerW, UiTheme::HeaderDividerY, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth); - DrawHLine(renderer, UiTheme::OuterRect.x, UiTheme::OuterRect.x + UiTheme::OuterRect.w, UiTheme::FooterDividerY, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth); - int accentBarY = titleText.rect.y + (titleText.rect.h - UiTheme::SectionAccentBarH) / 2; - DrawFillRect(renderer, { UiTheme::SectionTitleX - UiTheme::SectionAccentBarW - 14, accentBarY, UiTheme::SectionAccentBarW, UiTheme::SectionAccentBarH }, UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB); + DrawChromeCommon(); + DrawSectionTitle(); DrawRoundedFillRect(renderer, selectedRowRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); DrawStrokeRect(renderer, selectedRowRect, UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB, 2, UiTheme::BoxRadius); @@ -382,14 +698,8 @@ int main(int argc, char * argv[]) { SDL_Rect previewBox{ UiTheme::DetailX, UiTheme::PreviewBoxY, UiTheme::DetailW, UiTheme::PreviewBoxH }; DrawStrokeRect(renderer, previewBox, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BoxRadius); - PreviewImage->rect.x = previewBox.x + (previewBox.w - PreviewImage->rect.w) / 2; - PreviewImage->rect.y = previewBox.y + (previewBox.h - PreviewImage->rect.h) / 2; PreviewImage->Draw(renderer); } - - // B has nothing to do on any screen here - navigation is always via a - // real "Back" row in the list (like every other row), not a shortcut - DrawBadge(badgeA, UiTheme::BadgeClusterRightX); }; for(;;) @@ -442,15 +752,13 @@ int main(int argc, char * argv[]) } SetCurrentCommand(newCommandId); } - // tap B = jump straight to the last row (BACK here, EXIT on the real - // OptionsMenu) - matches main.cpp's own B behavior + // B = jump straight to the last row (BACK/EXIT) else if(controller.GetButtonStatus(B)) { SetCurrentCommand(commands.size()-1); } DrawChrome(); - titleText.Draw(renderer); int lastCommandIndex = static_cast(commands.size()) - 1; for(int i = 0, count = std::min(DisplayItemCount, pinnedStartIndex-topListItemNumber); i < count; ++i) @@ -460,8 +768,6 @@ int main(int argc, char * argv[]) for(int i = pinnedStartIndex; i <= lastCommandIndex; ++i) DrawRow(commands[i], i == lastCommandIndex); - creditText.Draw(renderer); - // bounded by pinnedStartIndex, not commands.size() - pinned rows need no scroll if(topListItemNumber != 0) scrollUp.Draw(renderer); From 48e8e48494bc0093723ddb1d8750752c32cbea0b Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 16 Sep 2026 20:07:08 +0000 Subject: [PATCH 043/210] Wire --layout grid and per-screen titles into root/downloads/DIY entry points Root, Downloads, DIY Theme and Settings now pass --layout grid and a translated --title key instead of the generic "$omTitle", so each screen shows its own name (Installed Themes, Available Themes, DIY Theme, Settings) instead of "Theme Selector" everywhere, including after backing out of a DIY sprite category into the category list. Adds the two new title keys across all 9 supported languages. --- mod/etc/options_menu/scripts/om_themeselector | 2 +- mod/etc/options_menu/themes/creator/scripts/om_refresh | 2 +- .../themes/creator/sprites/colors/scripts/om_return | 2 +- .../themes/creator/sprites/colors_nes/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/luigi/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/mario/scripts/om_return | 2 +- .../themes/creator/sprites/packed/scripts/om_return | 2 +- .../themes/creator/sprites/packed_nes/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/pole/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/sbm/scripts/om_return | 2 +- .../options_menu/themes/creator/sprites/wall/scripts/om_return | 2 +- .../themes/creator/sprites/wall_far/scripts/om_return | 2 +- .../themes/creator/sprites/wall_near/scripts/om_return | 2 +- mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads | 2 +- mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes | 2 +- mod/etc/options_menu/themes/downloads/scripts/om_return | 2 +- mod/etc/options_menu/themes/lang/de-DE.lang | 2 ++ mod/etc/options_menu/themes/lang/en-US.lang | 2 ++ mod/etc/options_menu/themes/lang/es-ES.lang | 2 ++ mod/etc/options_menu/themes/lang/fr-FR.lang | 2 ++ mod/etc/options_menu/themes/lang/it-IT.lang | 2 ++ mod/etc/options_menu/themes/lang/ja-JP.lang | 2 ++ mod/etc/options_menu/themes/lang/nl-NL.lang | 2 ++ mod/etc/options_menu/themes/lang/pt-PT.lang | 2 ++ mod/etc/options_menu/themes/lang/ru-RU.lang | 2 ++ mod/etc/options_menu/themes/scripts/om_clearList | 2 +- mod/etc/options_menu/themes/scripts/om_getThemes | 2 +- mod/etc/options_menu/themes/scripts/om_settings | 2 +- mod/etc/options_menu/themes/scripts/om_themeCreator | 2 +- mod/etc/options_menu/themes/settings/scripts/om_audio | 2 +- mod/etc/options_menu/themes/settings/scripts/om_reset | 2 +- mod/etc/options_menu/themes/settings/scripts/om_systemcleanup | 2 +- mod/etc/options_menu/themes/settings/scripts/om_theme | 2 +- 33 files changed, 42 insertions(+), 24 deletions(-) diff --git a/mod/etc/options_menu/scripts/om_themeselector b/mod/etc/options_menu/scripts/om_themeselector index df5f4b9..c1c3a6e 100644 --- a/mod/etc/options_menu/scripts/om_themeselector +++ b/mod/etc/options_menu/scripts/om_themeselector @@ -33,4 +33,4 @@ else loadThemeList fi -$optionsMenu/themes/theme_manager & +$optionsMenu/themes/theme_manager --title INSTALLED_THEMES --layout grid & diff --git a/mod/etc/options_menu/themes/creator/scripts/om_refresh b/mod/etc/options_menu/themes/creator/scripts/om_refresh index 35c2e66..ba989e6 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_refresh +++ b/mod/etc/options_menu/themes/creator/scripts/om_refresh @@ -24,4 +24,4 @@ diyThemeRemover diyThemeChecker "loader" touch "$omModGenSpacer" -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return index 69a2afe..a7c3736 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return index 69a2afe..a7c3736 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return index 69a2afe..a7c3736 100644 --- a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return index 669243b..3aa4540 100644 --- a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return index 669243b..3aa4540 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return index 669243b..3aa4540 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return index 669243b..3aa4540 100644 --- a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return index 669243b..3aa4540 100644 --- a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return index 669243b..3aa4540 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return index 669243b..3aa4540 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return index 669243b..3aa4540 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads index b8e0598..6b33cb2 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads +++ b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads @@ -24,4 +24,4 @@ clearDownloads remove "$subDummy" remove "$omModSubSpacer" -$optionsMenu/themes/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES --layout grid & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes index e37ecc8..7d64fd4 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes +++ b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes @@ -22,4 +22,4 @@ script_init getThemeList -$optionsMenu/themes/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES --layout grid & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_return b/mod/etc/options_menu/themes/downloads/scripts/om_return index 67f5a49..e3b4d71 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_return +++ b/mod/etc/options_menu/themes/downloads/scripts/om_return @@ -29,4 +29,4 @@ else clearList fi -usleep 50000 && $optionsMenu/themes/theme_manager & +usleep 50000 && $optionsMenu/themes/theme_manager --title INSTALLED_THEMES --layout grid & diff --git a/mod/etc/options_menu/themes/lang/de-DE.lang b/mod/etc/options_menu/themes/lang/de-DE.lang index e7f6b06..266d758 100644 --- a/mod/etc/options_menu/themes/lang/de-DE.lang +++ b/mod/etc/options_menu/themes/lang/de-DE.lang @@ -1,5 +1,7 @@ THEME_OPTIONS=Themes THEME_SELECTOR=Themenauswahl +INSTALLED_THEMES=Installierte Themes +AVAILABLE_THEMES=Verfügbare Themes DIY_THEME=Eigenes Theme THEME_SETTINGS=Einstellungen REFRESH=Aktualisieren diff --git a/mod/etc/options_menu/themes/lang/en-US.lang b/mod/etc/options_menu/themes/lang/en-US.lang index 14058b1..11cce49 100644 --- a/mod/etc/options_menu/themes/lang/en-US.lang +++ b/mod/etc/options_menu/themes/lang/en-US.lang @@ -1,5 +1,7 @@ THEME_OPTIONS=Themes THEME_SELECTOR=Theme Selector +INSTALLED_THEMES=Installed Themes +AVAILABLE_THEMES=Available Themes DIY_THEME=DIY Theme THEME_SETTINGS=Settings REFRESH=Refresh diff --git a/mod/etc/options_menu/themes/lang/es-ES.lang b/mod/etc/options_menu/themes/lang/es-ES.lang index 247e053..de88f1b 100644 --- a/mod/etc/options_menu/themes/lang/es-ES.lang +++ b/mod/etc/options_menu/themes/lang/es-ES.lang @@ -1,5 +1,7 @@ THEME_OPTIONS=Temas THEME_SELECTOR=Selector de temas +INSTALLED_THEMES=Temas instalados +AVAILABLE_THEMES=Temas disponibles DIY_THEME=Tema personalizado THEME_SETTINGS=Ajustes REFRESH=Actualizar diff --git a/mod/etc/options_menu/themes/lang/fr-FR.lang b/mod/etc/options_menu/themes/lang/fr-FR.lang index dd099a1..0cb1811 100644 --- a/mod/etc/options_menu/themes/lang/fr-FR.lang +++ b/mod/etc/options_menu/themes/lang/fr-FR.lang @@ -1,5 +1,7 @@ THEME_OPTIONS=Thèmes THEME_SELECTOR=Sélecteur de thèmes +INSTALLED_THEMES=Thèmes installés +AVAILABLE_THEMES=Thèmes disponibles DIY_THEME=Thème personnalisé THEME_SETTINGS=Réglages REFRESH=Actualiser diff --git a/mod/etc/options_menu/themes/lang/it-IT.lang b/mod/etc/options_menu/themes/lang/it-IT.lang index d4db285..b6e0a9b 100644 --- a/mod/etc/options_menu/themes/lang/it-IT.lang +++ b/mod/etc/options_menu/themes/lang/it-IT.lang @@ -1,5 +1,7 @@ THEME_OPTIONS=Temi THEME_SELECTOR=Selettore temi +INSTALLED_THEMES=Temi installati +AVAILABLE_THEMES=Temi disponibili DIY_THEME=Tema personalizzato THEME_SETTINGS=Impostazioni REFRESH=Aggiorna diff --git a/mod/etc/options_menu/themes/lang/ja-JP.lang b/mod/etc/options_menu/themes/lang/ja-JP.lang index e8ace8d..d5b7ea9 100644 --- a/mod/etc/options_menu/themes/lang/ja-JP.lang +++ b/mod/etc/options_menu/themes/lang/ja-JP.lang @@ -1,5 +1,7 @@ THEME_OPTIONS=Theme THEME_SELECTOR=Themeせんたく +INSTALLED_THEMES=インストール済みテーマ +AVAILABLE_THEMES=利用可能なテーマ DIY_THEME=じぶんのTheme THEME_SETTINGS=せってい REFRESH=さいよみこみ diff --git a/mod/etc/options_menu/themes/lang/nl-NL.lang b/mod/etc/options_menu/themes/lang/nl-NL.lang index 29f8204..b4c761e 100644 --- a/mod/etc/options_menu/themes/lang/nl-NL.lang +++ b/mod/etc/options_menu/themes/lang/nl-NL.lang @@ -1,5 +1,7 @@ THEME_OPTIONS=Thema's THEME_SELECTOR=Themakiezer +INSTALLED_THEMES=Geïnstalleerde thema's +AVAILABLE_THEMES=Beschikbare thema's DIY_THEME=Eigen thema THEME_SETTINGS=Instellingen REFRESH=Vernieuwen diff --git a/mod/etc/options_menu/themes/lang/pt-PT.lang b/mod/etc/options_menu/themes/lang/pt-PT.lang index 6133f2a..e9ec3f8 100644 --- a/mod/etc/options_menu/themes/lang/pt-PT.lang +++ b/mod/etc/options_menu/themes/lang/pt-PT.lang @@ -1,5 +1,7 @@ THEME_OPTIONS=Temas THEME_SELECTOR=Seletor de Temas +INSTALLED_THEMES=Temas Instalados +AVAILABLE_THEMES=Temas Disponíveis DIY_THEME=Tema Personalizado THEME_SETTINGS=Definições REFRESH=Atualizar diff --git a/mod/etc/options_menu/themes/lang/ru-RU.lang b/mod/etc/options_menu/themes/lang/ru-RU.lang index bf4eb30..e9320bf 100644 --- a/mod/etc/options_menu/themes/lang/ru-RU.lang +++ b/mod/etc/options_menu/themes/lang/ru-RU.lang @@ -1,5 +1,7 @@ THEME_OPTIONS=Темы THEME_SELECTOR=Выбор тем +INSTALLED_THEMES=Установленные темы +AVAILABLE_THEMES=Доступные темы DIY_THEME=Своя тема THEME_SETTINGS=Настройки REFRESH=Обновить diff --git a/mod/etc/options_menu/themes/scripts/om_clearList b/mod/etc/options_menu/themes/scripts/om_clearList index dfd6aee..3288585 100644 --- a/mod/etc/options_menu/themes/scripts/om_clearList +++ b/mod/etc/options_menu/themes/scripts/om_clearList @@ -24,4 +24,4 @@ clearList disable_cmds "Clear" enable_cmds "Refresh" -$optionsMenu/themes/theme_manager --commandPath $omModCommands/ --scriptPath $omModScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --title INSTALLED_THEMES --layout grid & diff --git a/mod/etc/options_menu/themes/scripts/om_getThemes b/mod/etc/options_menu/themes/scripts/om_getThemes index 0f5ed97..4f7bbf8 100644 --- a/mod/etc/options_menu/themes/scripts/om_getThemes +++ b/mod/etc/options_menu/themes/scripts/om_getThemes @@ -22,4 +22,4 @@ script_init [ ! -f "$subDummy" ] && getThemeList -$optionsMenu/themes/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES --layout grid & diff --git a/mod/etc/options_menu/themes/scripts/om_settings b/mod/etc/options_menu/themes/scripts/om_settings index c1fb0ab..2d53b76 100644 --- a/mod/etc/options_menu/themes/scripts/om_settings +++ b/mod/etc/options_menu/themes/scripts/om_settings @@ -37,4 +37,4 @@ else [ ! -f "$enableSystemCleanUp" ] && remove "$omModSettingsSpacer" fi -$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & diff --git a/mod/etc/options_menu/themes/scripts/om_themeCreator b/mod/etc/options_menu/themes/scripts/om_themeCreator index 60d10d4..377212e 100644 --- a/mod/etc/options_menu/themes/scripts/om_themeCreator +++ b/mod/etc/options_menu/themes/scripts/om_themeCreator @@ -22,4 +22,4 @@ script_init diyThemeChecker -$optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/settings/scripts/om_audio b/mod/etc/options_menu/themes/settings/scripts/om_audio index 1b1c364..4781072 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_audio +++ b/mod/etc/options_menu/themes/settings/scripts/om_audio @@ -21,4 +21,4 @@ script_init audioSettings "$1" -$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & diff --git a/mod/etc/options_menu/themes/settings/scripts/om_reset b/mod/etc/options_menu/themes/settings/scripts/om_reset index 7264180..beb5a72 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_reset +++ b/mod/etc/options_menu/themes/settings/scripts/om_reset @@ -23,4 +23,4 @@ script_init remove "$b_file" "$p_file" hakchi reboot -$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & diff --git a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup index 783aaf4..bf11808 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup +++ b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup @@ -25,4 +25,4 @@ modCleanUp usleep 1000 -$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & diff --git a/mod/etc/options_menu/themes/settings/scripts/om_theme b/mod/etc/options_menu/themes/settings/scripts/om_theme index 5be57b0..aa87b2b 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_theme +++ b/mod/etc/options_menu/themes/settings/scripts/om_theme @@ -22,4 +22,4 @@ script_init themeSettings "$1" -$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & From 639fb90f1f702a88aa1290878684255a0455811d Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 16 Sep 2026 20:07:53 +0000 Subject: [PATCH 044/210] Add gold chip icons for the grid action strip The existing preview-card assets (settings.png, update.png, etc.) have a bordered card baked in and aren't pure white, so they showed visible artifacts and tinted the wrong color when shrunk to chip size. Adds 5 dedicated 40x40 borderless Material Symbols glyphs instead and points the root/downloads action commands at them. --- mod/etc/options_menu/images/chip_clear.png | Bin 0 -> 291 bytes mod/etc/options_menu/images/chip_diy.png | Bin 0 -> 486 bytes mod/etc/options_menu/images/chip_download.png | Bin 0 -> 447 bytes mod/etc/options_menu/images/chip_refresh.png | Bin 0 -> 603 bytes mod/etc/options_menu/images/chip_settings.png | Bin 0 -> 918 bytes mod/etc/options_menu/themes/commands/_download | 2 +- mod/etc/options_menu/themes/commands/_remove | 2 +- .../themes/commands/c0000_generatetheme | 2 +- .../options_menu/themes/commands/c0000_options | 2 +- .../options_menu/themes/commands/c0000_refresh | 2 +- .../downloads/commands/c0000_downloadThemes | 2 +- .../themes/downloads/commands/c0000_removeThemes | 2 +- 12 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 mod/etc/options_menu/images/chip_clear.png create mode 100644 mod/etc/options_menu/images/chip_diy.png create mode 100644 mod/etc/options_menu/images/chip_download.png create mode 100644 mod/etc/options_menu/images/chip_refresh.png create mode 100644 mod/etc/options_menu/images/chip_settings.png diff --git a/mod/etc/options_menu/images/chip_clear.png b/mod/etc/options_menu/images/chip_clear.png new file mode 100644 index 0000000000000000000000000000000000000000..1771d106b3b6fc0e2d8f62c8b5aa069b7a408496 GIT binary patch literal 291 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEuRL8GLn`LHy>6JtWGK-3&|F!o zZ(<)`r^D3lfP(ffS9WsnntM*x&CJLs>fqq~wEU!37|U+YYiru3?$3I2@ka7m?)4!& zb4*nmk5Bv6x#X<=x0&0`lEPx=gTe~DWM4fWJ!W; literal 0 HcmV?d00001 diff --git a/mod/etc/options_menu/images/chip_diy.png b/mod/etc/options_menu/images/chip_diy.png new file mode 100644 index 0000000000000000000000000000000000000000..8f495bc7293e0b0b60d400531dd3b4dc0e760735 GIT binary patch literal 486 zcmV@P)ya45W$il+PW^x&qald3^qa;y6Nume|DUw@>B=ROKD8*N& zPCEVlhat~%l=G|E%=y;&G;?O2P$*ci-x)B%$2|eHe*rg4;ynU2e&U8c!FiF@e&7a4 zetWiY1AK1}HgWxYf48=9ef(@u8@OI^(-)u~*!TiB38a|D^@y9k0(I`SEMO-AZW>56 zjq6r$4Wg+8o<%on0~c35l}6E&09T@!2f}{fnnY6sT!>~7Nb>_16-@zfCYog%xGn`3 z6HOj)ESgmy-88OK!Nouu1&#>izWiBi^lnyb4=q}zdAR8bYMj^2hI=0G_FO#&3KO{APra& z&5`pLV;a}2;N}D_3@nJ|1jsjvb8n7vG`*jj4%9v9y*mdAjpG#Ho%d*RoF~lHBjZg#+7TOR&fe2(Qbe;>$oI=&U0Dr3oZ%ZJy78rE?BT& c!T#Ca0J|erAnDACzyJUM07*qoM6N<$f=>R?pa1{> literal 0 HcmV?d00001 diff --git a/mod/etc/options_menu/images/chip_download.png b/mod/etc/options_menu/images/chip_download.png new file mode 100644 index 0000000000000000000000000000000000000000..380f5d16325635cd9d43e0a8b7f1325437170f6c GIT binary patch literal 447 zcmV;w0YLtVP)5Ql$rJ`lkmf{j*oDwa0>2O@SB_KH?^+SplWp>3LwQUp7}La??@Cl;2Xq!AGW zABf2(2d8X8a(P`YN4T%r9fsk%+1p{Ils?CzDgDuBU|KDdBiZ zIGz%Yr-b7v;dts-L14Cp+!AtRD5B`rANC&dA<0(?82@w;^Ib`ZFz+V96A=SZ#tC7RTLU}VQwkq!zaw#;(HQ+RuhnWVsxvJW(Yz2dg2s7W04M}5W_7+v$$OL`N>_^h8 zBBb{>tiukLBn<@X(XfA|MJB8Ee&^e|#dnhC)m_SxGG_MP&Jmxe`(*3MH3hI4JOuup pSW)p94ERVdYD!8p}a0yqmy(l|3fU+qgR zs6k*IFhPFa1G^W*Ag~2^l@Rm^aG^G*fDV`8c3=;11^5uWUj+_EH3+N)UIe0Ffs;Tb zrEe>637GTx{)}yKED(GRbY|4(0lw53`uqlg-h|9;%c<29WHQy@@)W^klKO(jXUyz* z17^As9#2WSC8<|2a1-6Qw$WewQW^YMC!0-U@Qqh)12(J442}i$BQb&kqri9f`37vwE7Tb*s4sz* zg64JvnvYdb@NOV@zJQjwQ?}2kq6T*c8~iM=Kd08!z}!=}-xmYuPdIcQ%b2-q33rxV zh0VDD6<|0B@Cz8IT>~1JbN3St2q&UmJKcrnhNPoGQ&rN4q&t${x+`a!r0tRpN!pvR z_@6ejLB+-m)kT-RJk(Z!ebJ58S$>8x7@Gnv``6Az`DX#x=+2A>ByDor;Tkv0lad}t px+iJK%w~$jDk&)`DJfAdegpf+-YZPb^oalf002ovPDHLkV1i^T3X1>$ literal 0 HcmV?d00001 diff --git a/mod/etc/options_menu/images/chip_settings.png b/mod/etc/options_menu/images/chip_settings.png new file mode 100644 index 0000000000000000000000000000000000000000..01901ed41e92fc5442f0f8ce9b52b79097c8cb55 GIT binary patch literal 918 zcmV;H18Mw;P)(fG(o( zE!`YhX^1OQN{xzfD+c0x21lS$s?5``bf^T{iQ5wvlv2+^{QjRnAP=|!YzHR9tYE9K zwT1WvvBv}Tz%8I4CFMY!wb_3Gmw<)9Fkl_fVcAa#?h(uO3D{tG)xbp)FTl1`#Ek`h zgj8m?<648@?{xU|gv8U=liLQsQT%Zes1W?=!1W|zoK8ku1@PMCmm zhViq}25bTDiR-g`#3g_`7HI&OW^7qN6>#2s3O#SQ5V#Cn1r`C>=K55i-^%qcutf!1 z5w*7>c%y+Oz$Ty;80X2`D)Oz3axMbe1))hNk4aMJC;+|k`+?jDUa_cljnBZCFoHe? z4)8BN01HAmdx*way=@%4Ilvo-|6w3MYC$x)MmKo}`It@C1%e+|iNpU;$kA0BDNKl* z1hj}CeR`EGpA((}TP#ii@Ke}zl~3^&X9deQdnv$EVSj}`hFiSnVYZ}taUzlEQ%c!o z-z!{3CUL*T*7t6ar#mHqKzWRy4o`6dN~tdEw>=;BhYTXhr%=9&N1?mRUy;B;&&PYs z;*^Lk6&x*deTC%$`y5GhRj+guQlE#wN=GyJ>bUs^ASX(kzPn~ef-8D{3*jsmqmzVl zl+vBXv_Aamdh|3^@3{*V+091(`Q-$4(od5s;07*qoM6N<$f+9Dp%K!iX literal 0 HcmV?d00001 diff --git a/mod/etc/options_menu/themes/commands/_download b/mod/etc/options_menu/themes/commands/_download index bfd9d73..bb2da8c 100644 --- a/mod/etc/options_menu/themes/commands/_download +++ b/mod/etc/options_menu/themes/commands/_download @@ -2,4 +2,4 @@ COMMAND_NAME=DOWNLOAD_THEMES COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_getThemes -PREVIEW_IMAGE=/etc/options_menu/images/preview_download_themes.png \ No newline at end of file +PREVIEW_IMAGE=/etc/options_menu/images/chip_download.png \ No newline at end of file diff --git a/mod/etc/options_menu/themes/commands/_remove b/mod/etc/options_menu/themes/commands/_remove index f24f931..7694d6d 100644 --- a/mod/etc/options_menu/themes/commands/_remove +++ b/mod/etc/options_menu/themes/commands/_remove @@ -2,6 +2,6 @@ COMMAND_NAME=CLEAR_LIST COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_clearList & -PREVIEW_IMAGE=/etc/options_menu/images/clear.png +PREVIEW_IMAGE=/etc/options_menu/images/chip_clear.png PREVIEW_IMAGE_X=920 PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/commands/c0000_generatetheme b/mod/etc/options_menu/themes/commands/c0000_generatetheme index c3a168b..b3233eb 100644 --- a/mod/etc/options_menu/themes/commands/c0000_generatetheme +++ b/mod/etc/options_menu/themes/commands/c0000_generatetheme @@ -2,4 +2,4 @@ COMMAND_NAME=DIY_THEME COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_themeCreator -PREVIEW_IMAGE=/etc/options_menu/images/preview_diy_theme.png \ No newline at end of file +PREVIEW_IMAGE=/etc/options_menu/images/chip_diy.png \ No newline at end of file diff --git a/mod/etc/options_menu/themes/commands/c0000_options b/mod/etc/options_menu/themes/commands/c0000_options index bb80720..e9d95d5 100644 --- a/mod/etc/options_menu/themes/commands/c0000_options +++ b/mod/etc/options_menu/themes/commands/c0000_options @@ -2,6 +2,6 @@ COMMAND_NAME=THEME_SETTINGS COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_settings & -PREVIEW_IMAGE=/etc/options_menu/images/settings.png +PREVIEW_IMAGE=/etc/options_menu/images/chip_settings.png PREVIEW_IMAGE_X=920 PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/commands/c0000_refresh b/mod/etc/options_menu/themes/commands/c0000_refresh index b5d1ea1..618fd49 100644 --- a/mod/etc/options_menu/themes/commands/c0000_refresh +++ b/mod/etc/options_menu/themes/commands/c0000_refresh @@ -2,6 +2,6 @@ COMMAND_NAME=REFRESH COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh /etc/options_menu/scripts/om_themeselector reload -PREVIEW_IMAGE=/etc/options_menu/images/update.png +PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png PREVIEW_IMAGE_X=920 PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes b/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes index f320200..002862b 100644 --- a/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes +++ b/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes @@ -2,4 +2,4 @@ COMMAND_NAME=UPDATE_THEME_LIST COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_refreshThemes & -PREVIEW_IMAGE=/etc/options_menu/images/preview_update_theme_list.png \ No newline at end of file +PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png \ No newline at end of file diff --git a/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes b/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes index c475da7..1bd6650 100644 --- a/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes +++ b/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes @@ -2,6 +2,6 @@ COMMAND_NAME=CLEAR_LIST COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_clearDownloads & -PREVIEW_IMAGE=/etc/options_menu/images/clear.png +PREVIEW_IMAGE=/etc/options_menu/images/chip_clear.png PREVIEW_IMAGE_X=920 PREVIEW_IMAGE_Y=262 \ No newline at end of file From 99e18f7e2dc72350c0d4e7cf6825b957ca271a60 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 16 Sep 2026 20:08:34 +0000 Subject: [PATCH 045/210] Fix DIY sprite category previews: title, prefix collision, and stale cache om_diy_asset now maps each category to its own title key (Base UI, Background, Mario, ...) instead of the generic "DIY Theme", and disambiguates the c0000_ "fixed action" prefix from per-theme asset entries (renamed to c0001_, matching root/downloads) so grid mode can tell them apart. Categories whose crop is square-ish get a new PREVIEW_SQUARE flag for a denser tile layout. Also fixes findDiyAsset(): the cropped-preview reassignment was nested inside the same cache-miss check as the crop operation itself, so any visit after the first silently fell back to showing the uncropped source image instead of the cached crop. COMMAND_NAME derivation and per-theme script generation are now deferred to the cache-miss branch too, cutting a warm-cache category visit from ~2.2s to ~0.8s. --- .../themes/creator/scripts/om_diy_asset | 13 ++++- mod/etc/options_menu/themes/scripts/om_vars | 47 +++++++++++-------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset index aea2a85..17ba6a6 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset +++ b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset @@ -23,39 +23,50 @@ script_init case "$1" in luigi) diy_om_sprite_path="$omModLuigi" + diy_title="DEMO_CHARACTER_2_LUIGI" ;; pole) diy_om_sprite_path="$omModPole" + diy_title="DEMO_POLE" ;; wall) diy_om_sprite_path="$omModWall" + diy_title="BACKGROUND" ;; wall_near) diy_om_sprite_path="$omModWallN" + diy_title="DEMO_BACKGROUND_NEAR" ;; wall_far) diy_om_sprite_path="$omModWallF" + diy_title="DEMO_BACKGROUND_FAR" ;; packed) diy_om_sprite_path="$omModPacked" + diy_title="BASE_UI" ;; mario) diy_om_sprite_path="$omModMario" + diy_title="DEMO_CHARACTER_1_MARIO" ;; colors) diy_om_sprite_path="$omModColors" + diy_title="COLORS" ;; colors_nes) diy_om_sprite_path="$omModColorsNes" + diy_title="BACKGROUND_COLOR" ;; packed_nes) diy_om_sprite_path="$omModPackedNes" + diy_title="BASE_UI" ;; sbm) diy_om_sprite_path="$omModSbm" + diy_title="DEMO_CHARACTERS" ;; esac findDiyAsset "$1" -$optionsMenu/themes/theme_manager --commandPath $diy_om_sprite_path/commands/ --scriptPath $diy_om_sprite_path/scripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $diy_om_sprite_path/commands/ --scriptPath $diy_om_sprite_path/scripts --title $diy_title --layout grid & diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index d3625ee..b4e5e7d 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -489,6 +489,7 @@ findDiyAsset() { asset="$diy_file" chars="0" colors="0" + preview_square="0" json="" preview_x="920" preview_y="275" @@ -502,6 +503,10 @@ findDiyAsset() { asset="*.*" chars="1" preview_w="0" + preview_square="1" + ;; + wall | wall_near | wall_far) + preview_square="1" ;; sbm) diy_file="characters_sbm.png" @@ -515,6 +520,7 @@ findDiyAsset() { preview_x="780" preview_w="0" preview_h="200" + preview_square="1" ;; packed_nes) diy_file="${sftype}.png" @@ -523,6 +529,7 @@ findDiyAsset() { asset="$diy_file" preview_x="780" preview_w="0" + preview_square="1" preview_h="200" ;; colors) @@ -543,6 +550,7 @@ findDiyAsset() { asset="*.*" chars="1" preview_w="0" + preview_square="1" ;; esac @@ -564,7 +572,7 @@ findDiyAsset() { find "$diy_sprite/commands" "$diy_sprite/scripts" -type f \ -not -name 'om_*' -not -name 'c999[0-9]_*' \ - -not -name 'c0000_*' -not -name '_*' -exec rm {} + + -not -name 'c0001_*' -not -name '_*' -exec rm {} + diy_crop_batch=0 for p in $paths_to_search; do @@ -588,14 +596,11 @@ findDiyAsset() { [ -f "$resource_path/$asset_path/$diy_file" ] && has_sprite="1" if [ $has_sprite = "1" ] && [ "$asset_name" = "${asset_name#diy_theme}" ]; then - cmd_file="$diy_sprite/commands/c0000_$asset_name" + cmd_file="$diy_sprite/commands/c0001_$asset_name" sprite_script="$diy_sprite/scripts/$asset_name" - cmd_name="$(echo "$asset_name" | sed -e 's/_/ /g' | awk '{for(i=1;i<=NF;i++){$i=toupper(substr($i,1,1))substr($i,2)}}1')" + info_path="$full_asset_path" # theme root, before the next line repoints full_asset_path - if [ "$p" = "$theme_path" ]; then - cmd_name="$(sed -r 's/NAME=//g' < "$full_asset_path/info" | awk '{$NF="";sub(/[ \t]+$/,"")}1')" - full_asset_path="$resource_path/$asset_path" - fi + [ "$p" = "$theme_path" ] && full_asset_path="$resource_path/$asset_path" preview_image="$full_asset_path/$diy_file" temp_preview="${omModSprites}/assets/${1}/${1}_${asset_name}.png" @@ -604,29 +609,32 @@ findDiyAsset() { preview_image="$temp_preview" fi [ "$chars" = "1" ] && temp_preview="$preview_image" - if [ -n "$json" ] && [ ! -f "$temp_preview" ]; then - if [ "$colors" = "0" ] && [ -n "$diy_crop_x" ]; then - # backgrounded and batched (4 at a time, matching this console's - # core count) - a bare sequential loop over every installed - # theme was the whole reason this screen took so long to open + if [ "$colors" = "0" ] && [ "$chars" != "1" ] && [ -n "$diy_crop_x" ]; then + if [ ! -f "$temp_preview" ]; then + # backgrounded, batched 4 at a time (this console's core count) sprite_crop "$preview_image" "$diy_crop_x" "$diy_crop_y" "$diy_crop_w" "$diy_crop_h" "$temp_preview" & diy_crop_batch=$((diy_crop_batch + 1)) if [ "$diy_crop_batch" -ge 4 ]; then wait diy_crop_batch=0 fi + usleep 1000 fi - preview_image="$temp_preview" - usleep 1000 + preview_image="$temp_preview" # cropped copy, queued above or already cached fi if [ ! -f "$cmd_file" ]; then touch "$cmd_file" - # chars=1 previews are tiny sprites (e.g. mario_run01.png) blown - # up a lot - nearest-neighbor keeps them crisp instead of blurry + cmd_name="$(echo "$asset_name" | sed -e 's/_/ /g' | awk '{for(i=1;i<=NF;i++){$i=toupper(substr($i,1,1))substr($i,2)}}1')" + [ "$p" = "$theme_path" ] && cmd_name="$(sed -r 's/NAME=//g' < "$info_path/info" | awk '{$NF="";sub(/[ \t]+$/,"")}1')" + + # chars=1: tiny sprites blown up a lot, nearest keeps them crisp preview_nearest="" [ "$chars" = "1" ] && preview_nearest=" PREVIEW_NEAREST=TRUE" + preview_square_str="" + [ "$preview_square" = "1" ] && preview_square_str=" +PREVIEW_SQUARE=TRUE" echo "COMMAND_NAME=$cmd_name COMMAND_TYPE=INTERNAL COMMAND_STR=sh $sprite_script.sh @@ -634,10 +642,11 @@ PREVIEW_IMAGE=$preview_image PREVIEW_IMAGE_X=$preview_x PREVIEW_IMAGE_Y=$preview_y PREVIEW_IMAGE_WIDTH=$preview_w -PREVIEW_IMAGE_HEIGHT=$preview_h${preview_nearest}" > "$cmd_file" +PREVIEW_IMAGE_HEIGHT=$preview_h${preview_nearest}${preview_square_str}" > "$cmd_file" + + copy "$omModLicense" "$sprite_script.sh" + echo "diySpriteSelector \"$full_asset_path\" \"$asset_path\" \"$asset\"" >> "$sprite_script.sh" fi - copy "$omModLicense" "$sprite_script.sh" - echo "diySpriteSelector \"$full_asset_path\" \"$asset_path\" \"$asset\"" >> "$sprite_script.sh" fi done < /tmp/diy_asset_list remove /tmp/diy_asset_list From 633dd5a36752ad8e75688b2a6479d91ac347f3fb Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 16 Sep 2026 20:09:28 +0000 Subject: [PATCH 046/210] Remove obsolete c9998_separator from DIY sprite category commands --- .../sprites/colors/commands/{c9998_separator => commandshere} | 0 .../sprites/colors_nes/commands/{c9998_separator => commandshere} | 0 .../sprites/luigi/commands/{c9998_separator => commandshere} | 0 .../sprites/mario/commands/{c9998_separator => commandshere} | 0 .../sprites/packed/commands/{c9998_separator => commandshere} | 0 .../commands/scriptshere => packed_nes/commands/commandshere} | 0 .../commands/c9998_separator => pole/commands/commandshere} | 0 .../{pole/commands/c9998_separator => sbm/commands/commandshere} | 0 .../{sbm/commands/c9998_separator => wall/commands/commandshere} | 0 .../commands/c9998_separator => wall_far/commands/commandshere} | 0 .../themes/creator/sprites/wall_near/commands/c9998_separator | 0 .../commands/c9998_separator => wall_near/commands/commandshere} | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename mod/etc/options_menu/themes/creator/sprites/colors/commands/{c9998_separator => commandshere} (100%) rename mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/{c9998_separator => commandshere} (100%) rename mod/etc/options_menu/themes/creator/sprites/luigi/commands/{c9998_separator => commandshere} (100%) rename mod/etc/options_menu/themes/creator/sprites/mario/commands/{c9998_separator => commandshere} (100%) rename mod/etc/options_menu/themes/creator/sprites/packed/commands/{c9998_separator => commandshere} (100%) rename mod/etc/options_menu/themes/creator/sprites/{packed/commands/scriptshere => packed_nes/commands/commandshere} (100%) rename mod/etc/options_menu/themes/creator/sprites/{packed_nes/commands/c9998_separator => pole/commands/commandshere} (100%) rename mod/etc/options_menu/themes/creator/sprites/{pole/commands/c9998_separator => sbm/commands/commandshere} (100%) rename mod/etc/options_menu/themes/creator/sprites/{sbm/commands/c9998_separator => wall/commands/commandshere} (100%) rename mod/etc/options_menu/themes/creator/sprites/{wall/commands/c9998_separator => wall_far/commands/commandshere} (100%) delete mode 100644 mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c9998_separator rename mod/etc/options_menu/themes/creator/sprites/{wall_far/commands/c9998_separator => wall_near/commands/commandshere} (100%) diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/commands/c9998_separator b/mod/etc/options_menu/themes/creator/sprites/colors/commands/commandshere similarity index 100% rename from mod/etc/options_menu/themes/creator/sprites/colors/commands/c9998_separator rename to mod/etc/options_menu/themes/creator/sprites/colors/commands/commandshere diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c9998_separator b/mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/commandshere similarity index 100% rename from mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c9998_separator rename to mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/commandshere diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/commands/c9998_separator b/mod/etc/options_menu/themes/creator/sprites/luigi/commands/commandshere similarity index 100% rename from mod/etc/options_menu/themes/creator/sprites/luigi/commands/c9998_separator rename to mod/etc/options_menu/themes/creator/sprites/luigi/commands/commandshere diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/commands/c9998_separator b/mod/etc/options_menu/themes/creator/sprites/mario/commands/commandshere similarity index 100% rename from mod/etc/options_menu/themes/creator/sprites/mario/commands/c9998_separator rename to mod/etc/options_menu/themes/creator/sprites/mario/commands/commandshere diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/commands/c9998_separator b/mod/etc/options_menu/themes/creator/sprites/packed/commands/commandshere similarity index 100% rename from mod/etc/options_menu/themes/creator/sprites/packed/commands/c9998_separator rename to mod/etc/options_menu/themes/creator/sprites/packed/commands/commandshere diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/commands/scriptshere b/mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/commandshere similarity index 100% rename from mod/etc/options_menu/themes/creator/sprites/packed/commands/scriptshere rename to mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/commandshere diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c9998_separator b/mod/etc/options_menu/themes/creator/sprites/pole/commands/commandshere similarity index 100% rename from mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c9998_separator rename to mod/etc/options_menu/themes/creator/sprites/pole/commands/commandshere diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/commands/c9998_separator b/mod/etc/options_menu/themes/creator/sprites/sbm/commands/commandshere similarity index 100% rename from mod/etc/options_menu/themes/creator/sprites/pole/commands/c9998_separator rename to mod/etc/options_menu/themes/creator/sprites/sbm/commands/commandshere diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c9998_separator b/mod/etc/options_menu/themes/creator/sprites/wall/commands/commandshere similarity index 100% rename from mod/etc/options_menu/themes/creator/sprites/sbm/commands/c9998_separator rename to mod/etc/options_menu/themes/creator/sprites/wall/commands/commandshere diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/commands/c9998_separator b/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/commandshere similarity index 100% rename from mod/etc/options_menu/themes/creator/sprites/wall/commands/c9998_separator rename to mod/etc/options_menu/themes/creator/sprites/wall_far/commands/commandshere diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c9998_separator b/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c9998_separator deleted file mode 100644 index e69de29..0000000 diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c9998_separator b/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/commandshere similarity index 100% rename from mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c9998_separator rename to mod/etc/options_menu/themes/creator/sprites/wall_near/commands/commandshere From d9d253a3f0ba6a904329701dfa300745a647660a Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 16 Sep 2026 20:16:22 +0000 Subject: [PATCH 047/210] Fix shellcheck failures in om_themes_vars --- mod/etc/options_menu/themes/scripts/om_themes_vars | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mod/etc/options_menu/themes/scripts/om_themes_vars b/mod/etc/options_menu/themes/scripts/om_themes_vars index 22f6000..b9c3afb 100644 --- a/mod/etc/options_menu/themes/scripts/om_themes_vars +++ b/mod/etc/options_menu/themes/scripts/om_themes_vars @@ -1,6 +1,5 @@ #!/bin/sh -# shellcheck disable=SC2086,SC2154,SC3046,SC3051 -# # shellcheck disable=SC2034,SC2086,SC2154,SC3010,SC3046,SC3051 +# shellcheck disable=SC2034,SC2086,SC2154,SC3046,SC3051 # Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify @@ -32,7 +31,7 @@ has_folders="${gStorage}/001" theme_path="$usb_path/$modtitle/$s_type" [ ! -d "$theme_path" ] && theme_path="$nand_path/$modtitle/$s_type" system="$(echo "$s_type" | awk '{print toupper($0)}')" -has_theme="$(ls -1 "$theme_path/$system".*.tar.gz 2> /dev/null | wc -l)" +has_theme="$(find "$theme_path" -maxdepth 1 -name "$system.*.tar.gz" 2> /dev/null | wc -l)" # extracts any theme archives already sitting in $theme_path (e.g. copied From f6a2508df2f407477bb2c2032f8fc5abb4ab8d71 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 16 Sep 2026 23:56:28 +0000 Subject: [PATCH 048/210] Update subproject commit reference in OptionsMenu --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index e07cc5d..432526f 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit e07cc5da4c4d95cbb91a94a2efd6393ecbf343cd +Subproject commit 432526fd74a54daa4e915054c8dbf946ed51aff3 From 20a440c31f86a19cb77ddb792a7063e7b65057e5 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 17 Sep 2026 00:08:08 +0000 Subject: [PATCH 049/210] Update subproject commit reference in OptionsMenu --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 432526f..bca82b5 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 432526fd74a54daa4e915054c8dbf946ed51aff3 +Subproject commit bca82b5e5b328a83e32a89ae0cb6000699b6b5f4 From 74d6c9156c2e841af615d3d75bf2624f550a4e7f Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 17 Sep 2026 18:31:36 +0000 Subject: [PATCH 050/210] Update subproject commit reference in toolchain --- toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolchain b/toolchain index 216f5ab..45e8452 160000 --- a/toolchain +++ b/toolchain @@ -1 +1 @@ -Subproject commit 216f5abcf008e285c3e833c223538518fc9da1ff +Subproject commit 45e8452a11d83cd09192a6c2e523e69706a836e5 From 319653373f527ce92ec39186a9ee5eaa38963224 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 17 Sep 2026 18:42:07 +0000 Subject: [PATCH 051/210] Update subproject commit reference in OptionsMenu --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index bca82b5..9fc8926 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit bca82b5e5b328a83e32a89ae0cb6000699b6b5f4 +Subproject commit 9fc89262639ccc5e8eddabf6a3b2677a467a08ca From 0bfb8f6312ee9af65020435363085026e938402f Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 17 Sep 2026 19:31:08 +0000 Subject: [PATCH 052/210] Update subproject commit reference in OptionsMenu --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 9fc8926..2c0247f 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 9fc89262639ccc5e8eddabf6a3b2677a467a08ca +Subproject commit 2c0247f3f5d1bef7bab5e30cb2c33c70d46a3d01 From e0b0d9cbe45f80dd62a8c43dc755143bee541cc7 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 17 Sep 2026 19:31:58 +0000 Subject: [PATCH 053/210] Add theme downloader and theme manager to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index ba985ed..c876d1d 100644 --- a/.gitignore +++ b/.gitignore @@ -555,3 +555,5 @@ _console_originals/ tools/*.py tools/MaterialSymbolsOutlined.codepoints tools/MaterialSymbolsOutlined.ttf +theme_downloader/theme_downloader +mod/etc/options_menu/themes/theme_manager From 7fe930f437d747cfe41c38e8b86c408c7edcc19e Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 17 Sep 2026 19:49:07 +0000 Subject: [PATCH 054/210] Add new theme command configurations for download, clear list, refresh, DIY theme, and theme settings --- mod/etc/options_menu/themes/commands/{_download => _2_download} | 0 mod/etc/options_menu/themes/commands/{_remove => _5_remove} | 0 .../themes/commands/{c0000_refresh => c0000_1_refresh} | 0 .../commands/{c0000_generatetheme => c0000_3_generatetheme} | 0 .../themes/commands/{c0000_options => c0000_4_options} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename mod/etc/options_menu/themes/commands/{_download => _2_download} (100%) rename mod/etc/options_menu/themes/commands/{_remove => _5_remove} (100%) rename mod/etc/options_menu/themes/commands/{c0000_refresh => c0000_1_refresh} (100%) rename mod/etc/options_menu/themes/commands/{c0000_generatetheme => c0000_3_generatetheme} (100%) rename mod/etc/options_menu/themes/commands/{c0000_options => c0000_4_options} (100%) diff --git a/mod/etc/options_menu/themes/commands/_download b/mod/etc/options_menu/themes/commands/_2_download similarity index 100% rename from mod/etc/options_menu/themes/commands/_download rename to mod/etc/options_menu/themes/commands/_2_download diff --git a/mod/etc/options_menu/themes/commands/_remove b/mod/etc/options_menu/themes/commands/_5_remove similarity index 100% rename from mod/etc/options_menu/themes/commands/_remove rename to mod/etc/options_menu/themes/commands/_5_remove diff --git a/mod/etc/options_menu/themes/commands/c0000_refresh b/mod/etc/options_menu/themes/commands/c0000_1_refresh similarity index 100% rename from mod/etc/options_menu/themes/commands/c0000_refresh rename to mod/etc/options_menu/themes/commands/c0000_1_refresh diff --git a/mod/etc/options_menu/themes/commands/c0000_generatetheme b/mod/etc/options_menu/themes/commands/c0000_3_generatetheme similarity index 100% rename from mod/etc/options_menu/themes/commands/c0000_generatetheme rename to mod/etc/options_menu/themes/commands/c0000_3_generatetheme diff --git a/mod/etc/options_menu/themes/commands/c0000_options b/mod/etc/options_menu/themes/commands/c0000_4_options similarity index 100% rename from mod/etc/options_menu/themes/commands/c0000_options rename to mod/etc/options_menu/themes/commands/c0000_4_options From b71259611ab9757cf3d36864d169c91f0c43bfc3 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 17 Sep 2026 22:34:39 +0000 Subject: [PATCH 055/210] Add new theme command files for saving, previewing, cleaning, and refreshing themes --- mod/etc/options_menu/themes/creator/commands/{_apply => _3_apply} | 0 .../options_menu/themes/creator/commands/{_preview => _4_preview} | 0 .../options_menu/themes/creator/commands/{_remove => _5_remove} | 0 .../themes/creator/commands/{c0000_refresh => c0000_1_refresh} | 0 .../creator/commands/{c0000_separator => c0000_2_separator} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename mod/etc/options_menu/themes/creator/commands/{_apply => _3_apply} (100%) rename mod/etc/options_menu/themes/creator/commands/{_preview => _4_preview} (100%) rename mod/etc/options_menu/themes/creator/commands/{_remove => _5_remove} (100%) rename mod/etc/options_menu/themes/creator/commands/{c0000_refresh => c0000_1_refresh} (100%) rename mod/etc/options_menu/themes/creator/commands/{c0000_separator => c0000_2_separator} (100%) diff --git a/mod/etc/options_menu/themes/creator/commands/_apply b/mod/etc/options_menu/themes/creator/commands/_3_apply similarity index 100% rename from mod/etc/options_menu/themes/creator/commands/_apply rename to mod/etc/options_menu/themes/creator/commands/_3_apply diff --git a/mod/etc/options_menu/themes/creator/commands/_preview b/mod/etc/options_menu/themes/creator/commands/_4_preview similarity index 100% rename from mod/etc/options_menu/themes/creator/commands/_preview rename to mod/etc/options_menu/themes/creator/commands/_4_preview diff --git a/mod/etc/options_menu/themes/creator/commands/_remove b/mod/etc/options_menu/themes/creator/commands/_5_remove similarity index 100% rename from mod/etc/options_menu/themes/creator/commands/_remove rename to mod/etc/options_menu/themes/creator/commands/_5_remove diff --git a/mod/etc/options_menu/themes/creator/commands/c0000_refresh b/mod/etc/options_menu/themes/creator/commands/c0000_1_refresh similarity index 100% rename from mod/etc/options_menu/themes/creator/commands/c0000_refresh rename to mod/etc/options_menu/themes/creator/commands/c0000_1_refresh diff --git a/mod/etc/options_menu/themes/creator/commands/c0000_separator b/mod/etc/options_menu/themes/creator/commands/c0000_2_separator similarity index 100% rename from mod/etc/options_menu/themes/creator/commands/c0000_separator rename to mod/etc/options_menu/themes/creator/commands/c0000_2_separator From 321652678522ff10b631f324a6d714d84679a5af Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 18 Sep 2026 00:18:06 +0000 Subject: [PATCH 056/210] Update subproject commit reference in OptionsMenu --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 2c0247f..80e05bd 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 2c0247f3f5d1bef7bab5e30cb2c33c70d46a3d01 +Subproject commit 80e05bd25ebec492d8176abbb57bb0b64026a16b From 511be922ddfe6430811d0825c45b61caed256d05 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 18 Sep 2026 00:18:17 +0000 Subject: [PATCH 057/210] Add cacert.pem to .gitignore for theme downloader --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c876d1d..04d1410 100644 --- a/.gitignore +++ b/.gitignore @@ -557,3 +557,4 @@ tools/MaterialSymbolsOutlined.codepoints tools/MaterialSymbolsOutlined.ttf theme_downloader/theme_downloader mod/etc/options_menu/themes/theme_manager +theme_downloader/cacert.pem From bec197b460ee07590e81aae0516abb9c0fd099f0 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 18 Sep 2026 00:21:23 +0000 Subject: [PATCH 058/210] Add theme_downloader, statically-linked curl+OpenSSL binary --- .gitignore | 1 - Makefile.docker | 19 +++- build.sh | 6 +- mod/install | 2 + mod/uninstall | 2 + theme_downloader/Makefile | 39 +++++++ theme_downloader/theme_downloader.c | 168 ++++++++++++++++++++++++++++ 7 files changed, 226 insertions(+), 11 deletions(-) create mode 100644 theme_downloader/Makefile create mode 100644 theme_downloader/theme_downloader.c diff --git a/.gitignore b/.gitignore index 04d1410..934b00d 100644 --- a/.gitignore +++ b/.gitignore @@ -538,7 +538,6 @@ mod/etc/options_menu/themes/scripts/commands/c0009_* mod/etc/options_menu/themes/scripts/commands/c0010_* mod/etc/options_menu/themes/settings/commandss/ mod/etc/options_menu/images/retro_gaming.zip -Makefile mod/etc/options_menu/themes/downloads/scripts/getAll retro_gaming.zip mod/etc/options_menu/b8025_fontfix diff --git a/Makefile.docker b/Makefile.docker index 97c3833..d4ccfbf 100644 --- a/Makefile.docker +++ b/Makefile.docker @@ -23,18 +23,25 @@ VENDOR_SRC_DIR = vendor/OptionsMenu/src SOURCES = src/main.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp OBJECTS = $(SOURCES:.cpp=.o) -all: out/theme_manager +all: mod/etc/options_menu/themes/theme_manager mod/bin/theme_downloader -out/theme_manager: $(OBJECTS) - mkdir -p out - $(CROSS_PREFIX)$(CXX) $(OBJECTS) $(LDLIBS) $(LDFLAGS) -Wl,-rpath,/etc/options_menu -o out/theme_manager - $(CROSS_PREFIX)$(STRIP) out/theme_manager +mod/etc/options_menu/themes/theme_manager: $(OBJECTS) + $(CROSS_PREFIX)$(CXX) $(OBJECTS) $(LDLIBS) $(LDFLAGS) -Wl,-rpath,/etc/options_menu -o mod/etc/options_menu/themes/theme_manager + $(CROSS_PREFIX)$(STRIP) mod/etc/options_menu/themes/theme_manager %.o: %.cpp $(CROSS_PREFIX)$(CXX) $(CXXFLAGS) -c $< -o $@ +# theme_downloader has its own Makefile (links the toolchain's static +# libcurl+OpenSSL, not SDL2 - unrelated build inputs from theme_manager). +.PHONY: mod/bin/theme_downloader +mod/bin/theme_downloader: + $(MAKE) -C theme_downloader CROSS_PREFIX=$(CROSS_PREFIX) + cp theme_downloader/theme_downloader mod/bin/theme_downloader + clean: find . -name "*.o" -type f -delete - rm -rf out/ + rm -f mod/etc/options_menu/themes/theme_manager mod/bin/theme_downloader + $(MAKE) -C theme_downloader clean .PHONY: clean diff --git a/build.sh b/build.sh index 175d99e..45be59c 100644 --- a/build.sh +++ b/build.sh @@ -24,7 +24,5 @@ fi echo "==> Running make inside the container..." docker run --rm -v "$PWD:/src" "$IMAGE" make -f Makefile.docker CROSS_PREFIX=arm-linux-gnueabihf- "$@" -if [ -d out/ ]; then - echo "==> Build complete. Files in out/:" - ls -la out/ -fi +echo "==> Build complete:" +ls -la mod/etc/options_menu/themes/theme_manager mod/bin/theme_downloader 2>/dev/null diff --git a/mod/install b/mod/install index f1dee6f..05384cb 100644 --- a/mod/install +++ b/mod/install @@ -69,6 +69,8 @@ copy "$om_themes_scripts/om_fontfix" "$pre_initd/b8025_fontfix" copy "$om_themes_scripts/om_chmenu" "$rootfs/bin/chmenu" chmod +x "$rootfs/bin/chmenu" chmod +x "$usr/bin/sprite_crop" +chmod +x "$usr/bin/theme_downloader" +chmod +x "$rootfs/etc/options_menu/themes/theme_manager" sed '1,2d' "$om_themes_scripts/om_themescript" >"$b_file" [ ! -f "$p_file" ] && echo themeSelector >"$p_file" diff --git a/mod/uninstall b/mod/uninstall index f39034c..d4964ca 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -103,6 +103,8 @@ remove "$rootfs/etc/options_menu/images/wall.png" remove "$rootfs/etc/options_menu/images/wall_far.png" remove "$rootfs/etc/usr/bin/pngpack-upx" remove "$rootfs/etc/usr/bin/sprite_crop" +remove "$rootfs/etc/usr/bin/theme_downloader" +remove "$rootfs/etc/options_menu/themes/theme_manager" [ -f "$TITLE_FONT_BACKUP" ] && mv "$TITLE_FONT_BACKUP" "$TITLE_FONT" for FONT in s m l small medium large; do diff --git a/theme_downloader/Makefile b/theme_downloader/Makefile new file mode 100644 index 0000000..6aeef5d --- /dev/null +++ b/theme_downloader/Makefile @@ -0,0 +1,39 @@ +# Cross-compiles theme_downloader against the static libcurl+OpenSSL built +# into the toolchain image (toolchain/Dockerfile.jessie-armhf). Run via +# ../build.sh theme_downloader (from the repo root, same Docker toolchain +# as the theme_manager build), not directly - CROSS_PREFIX must point at +# the arm-linux-gnueabihf- cross tools. +CURL_PREFIX = /opt/curl-static +CC = gcc +STRIP = strip +CFLAGS = -O2 -Wall -I$(CURL_PREFIX)/include +# NOT -static: glibc's getaddrinfo needs libnss_dns.so.2, loaded at runtime +# via dlopen - a fully static binary can't do that and fails DNS resolution +# instantly. curl/ssl/crypto/zlib link statically anyway since only .a +# versions of them exist in this toolchain; libc/libdl/libpthread/libresolv +# stay dynamic, resolved against the console's own libc at runtime (same as +# theme_manager/sprite_crop already do for SDL2/libpng). +LDLIBS = -L$(CURL_PREFIX)/lib -L/usr/lib/arm-linux-gnueabihf -lcurl -lssl -lcrypto -lz -ldl -lpthread + +all: theme_downloader + +# gitignored (not source we own) - fetched here so a fresh clone can still +# build instead of failing on a missing file +cacert.pem: + curl -fL -o cacert.pem https://curl.se/ca/cacert.pem + +# ld -r -b binary embeds cacert.pem verbatim as a linkable object, exposing +# _binary_cacert_pem_start/_end - avoids generating a multi-thousand-line +# .c array just to get the CA bundle into the binary. +ca_bundle.o: cacert.pem + $(CROSS_PREFIX)ld -r -b binary -o ca_bundle.o cacert.pem + +theme_downloader: theme_downloader.c ca_bundle.o + $(CROSS_PREFIX)$(CC) $(CFLAGS) theme_downloader.c ca_bundle.o $(LDLIBS) -o theme_downloader + $(CROSS_PREFIX)$(STRIP) theme_downloader + upx --best theme_downloader + +clean: + rm -f theme_downloader ca_bundle.o + +.PHONY: all clean diff --git a/theme_downloader/theme_downloader.c b/theme_downloader/theme_downloader.c new file mode 100644 index 0000000..f108358 --- /dev/null +++ b/theme_downloader/theme_downloader.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2026 DefKorns + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Fetches om_theme-selector's theme catalog/packages over HTTPS. The + * console's own BusyBox wget can't negotiate TLS 1.2 at all, so this + * replaces it - statically linked against curl+OpenSSL so it carries no + * runtime library dependency on the console. Knows the GitHub release host + * and the classicmods.net fallback itself (falls back automatically if + * GitHub isn't reachable), so the shell side only needs to say what it + * wants, not where to get it. */ + +#include +#include +#include +#include +#include + +extern const unsigned char _binary_cacert_pem_start[]; +extern const unsigned char _binary_cacert_pem_end[]; + +static const char *GITHUB_BASE = "https://github.com/DefKorns/om-theme-downloads/releases/download/themes-v1"; +static const char *CLASSICMODS_SCRIPTS = "http://classicmods.net/files/themes/themeselector-scripts/"; +static const char *CLASSICMODS_THEMES = "http://classicmods.net/files/themes/themeselector/"; +static const char *LIST_SUFFIX = "-list-v2.tar.gz"; +static const char *CA_PATH = "/tmp/.theme_downloader_ca.pem"; + +static void to_upper(char *dst, const char *src, size_t dst_size) { + size_t i = 0; + for (; src[i] && i + 1 < dst_size; i++) { + dst[i] = (char)toupper((unsigned char)src[i]); + } + dst[i] = '\0'; +} + +/* extracted once per boot into /tmp - cheap to redo, avoids re-embedding + * the CA bundle as a shell-visible file inside the hmod itself */ +static void ensure_ca_bundle(void) { + FILE *f = fopen(CA_PATH, "rb"); + if (f) { + fclose(f); + return; + } + f = fopen(CA_PATH, "wb"); + if (!f) return; + fwrite(_binary_cacert_pem_start, 1, (size_t)(_binary_cacert_pem_end - _binary_cacert_pem_start), f); + fclose(f); +} + +static CURL *make_handle(const char *url, long connect_timeout, long total_timeout) { + CURL *c = curl_easy_init(); + if (!c) return NULL; + curl_easy_setopt(c, CURLOPT_URL, url); + curl_easy_setopt(c, CURLOPT_CAINFO, CA_PATH); + curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(c, CURLOPT_MAXREDIRS, 10L); + curl_easy_setopt(c, CURLOPT_FAILONERROR, 1L); + curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1L); + curl_easy_setopt(c, CURLOPT_USERAGENT, "theme_downloader/1.0"); + if (connect_timeout > 0) curl_easy_setopt(c, CURLOPT_CONNECTTIMEOUT, connect_timeout); + if (total_timeout > 0) curl_easy_setopt(c, CURLOPT_TIMEOUT, total_timeout); + return c; +} + +static int url_reachable(const char *url) { + CURL *c = make_handle(url, 5L, 5L); + if (!c) return 0; + curl_easy_setopt(c, CURLOPT_NOBODY, 1L); + CURLcode res = curl_easy_perform(c); + curl_easy_cleanup(c); + return res == CURLE_OK; +} + +static size_t write_to_file(void *ptr, size_t size, size_t nmemb, void *userdata) { + return fwrite(ptr, size, nmemb, (FILE *)userdata); +} + +static int download_to(const char *url, const char *out_path) { + FILE *f = fopen(out_path, "wb"); + if (!f) return 0; + CURL *c = make_handle(url, 10L, 0L); + if (!c) { + fclose(f); + return 0; + } + curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, write_to_file); + curl_easy_setopt(c, CURLOPT_WRITEDATA, f); + CURLcode res = curl_easy_perform(c); + curl_easy_cleanup(c); + fclose(f); + if (res != CURLE_OK) { + remove(out_path); + return 0; + } + return 1; +} + +static int fetch_with_fallback(const char *gh_url, const char *fallback_url, const char *out_path) { + if (url_reachable(gh_url) && download_to(gh_url, out_path)) return 1; + if (url_reachable(fallback_url) && download_to(fallback_url, out_path)) return 1; + return 0; +} + +static void usage(const char *prog) { + fprintf(stderr, + "usage:\n" + " %s catalog \n" + " %s theme \n" + " %s all-manifest \n", + prog, prog, prog); +} + +int main(int argc, char **argv) { + if (argc < 2) { + usage(argv[0]); + return 2; + } + + curl_global_init(CURL_GLOBAL_DEFAULT); + ensure_ca_bundle(); + + char gh_url[512]; + char fallback_url[512]; + int ok = 0; + + if (strcmp(argv[1], "catalog") == 0 && argc == 4) { + const char *sftype = argv[2]; + const char *out_path = argv[3]; + snprintf(gh_url, sizeof(gh_url), "%s/%s%s", GITHUB_BASE, sftype, LIST_SUFFIX); + snprintf(fallback_url, sizeof(fallback_url), "%s%s%s", CLASSICMODS_SCRIPTS, sftype, LIST_SUFFIX); + ok = fetch_with_fallback(gh_url, fallback_url, out_path); + } else if (strcmp(argv[1], "theme") == 0 && argc == 5) { + const char *sftype = argv[2]; + const char *theme_name = argv[3]; + const char *out_path = argv[4]; + char system_name[32]; + to_upper(system_name, sftype, sizeof(system_name)); + snprintf(gh_url, sizeof(gh_url), "%s/%s.%s.tar.gz", GITHUB_BASE, system_name, theme_name); + snprintf(fallback_url, sizeof(fallback_url), "%s%s.%s.tar.gz", CLASSICMODS_THEMES, system_name, theme_name); + ok = fetch_with_fallback(gh_url, fallback_url, out_path); + } else if (strcmp(argv[1], "all-manifest") == 0 && argc == 4) { + const char *sftype = argv[2]; + const char *out_path = argv[3]; + snprintf(gh_url, sizeof(gh_url), "%s/%s-all", GITHUB_BASE, sftype); + snprintf(fallback_url, sizeof(fallback_url), "%s%s-all", CLASSICMODS_SCRIPTS, sftype); + ok = fetch_with_fallback(gh_url, fallback_url, out_path); + } else { + usage(argv[0]); + curl_global_cleanup(); + return 2; + } + + curl_global_cleanup(); + return ok ? 0 : 1; +} From 7c11d6a6ce251946a5acb4c00a678a503b899263 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 18 Sep 2026 00:21:59 +0000 Subject: [PATCH 059/210] Redesign DIY Theme strip, lazy-load per-category assets, drop dead preview fields --- mod/etc/options_menu/images/chip_apply.png | Bin 0 -> 501 bytes mod/etc/options_menu/images/chip_preview.png | Bin 0 -> 609 bytes .../options_menu/themes/commands/_5_remove | 2 - .../themes/commands/c0000_1_refresh | 2 - .../themes/commands/c0000_4_options | 2 - .../themes/creator/commands/_3_apply | 4 +- .../themes/creator/commands/_4_preview | 4 +- .../themes/creator/commands/_5_remove | 4 +- .../themes/creator/commands/c0000_1_refresh | 7 - .../themes/creator/commands/c0000_2_separator | 0 .../themes/creator/commands/c0001_packed | 4 - .../themes/creator/commands/c0001_packed_nes | 4 - .../themes/creator/commands/c0001_wall | 4 - .../themes/creator/commands/c0002_mario | 4 - .../themes/creator/commands/c0002_sbm | 4 - .../themes/creator/commands/c0003_luigi | 4 - .../themes/creator/commands/c0004_wall_near | 4 - .../themes/creator/commands/c0005_wall_far | 4 - .../themes/creator/commands/c0006_colors | 4 - .../themes/creator/commands/c0006_colors_nes | 4 - .../themes/creator/commands/c0006_pole | 4 - .../themes/creator/commands/c9997_separator | 0 .../themes/creator/scripts/om_applytheme | 2 +- .../themes/creator/scripts/om_diy_asset | 5 +- .../themes/creator/scripts/om_preview | 7 + .../themes/creator/scripts/om_refresh | 27 -- .../themes/creator/scripts/om_removediy | 2 +- .../sprites/colors/commands/c0000_1_refresh | 5 + .../creator/sprites/colors/scripts/om_return | 2 +- .../colors_nes/commands/c0000_1_refresh | 5 + .../sprites/colors_nes/scripts/om_return | 2 +- .../sprites/luigi/commands/c0000_1_refresh | 5 + .../creator/sprites/luigi/scripts/om_return | 2 +- .../sprites/mario/commands/c0000_1_refresh | 5 + .../creator/sprites/mario/scripts/om_return | 2 +- .../sprites/packed/commands/c0000_1_refresh | 5 + .../creator/sprites/packed/scripts/om_return | 2 +- .../packed_nes/commands/c0000_1_refresh | 5 + .../sprites/packed_nes/scripts/om_return | 2 +- .../sprites/pole/commands/c0000_1_refresh | 5 + .../creator/sprites/pole/scripts/om_return | 2 +- .../sprites/sbm/commands/c0000_1_refresh | 5 + .../creator/sprites/sbm/scripts/om_return | 2 +- .../sprites/wall/commands/c0000_1_refresh | 5 + .../creator/sprites/wall/scripts/om_return | 2 +- .../sprites/wall_far/commands/c0000_1_refresh | 5 + .../sprites/wall_far/scripts/om_return | 2 +- .../wall_near/commands/c0000_1_refresh | 5 + .../sprites/wall_near/scripts/om_return | 2 +- .../downloads/commands/c0000_removeThemes | 2 - .../downloads/scripts/om_clearDownloads | 1 - .../options_menu/themes/scripts/om_overlay | 2 + .../themes/scripts/om_themeCreator | 2 +- .../themes/scripts/om_themescript | 2 +- mod/etc/options_menu/themes/scripts/om_vars | 158 ++++------ .../settings/commands/c0000_themeperfolder | 2 - .../commands/c0000_themeperfolder_child | 2 - .../settings/commands/c0001_uninstaller | 2 - src/main.cpp | 269 +++++++++++++++--- 59 files changed, 375 insertions(+), 256 deletions(-) create mode 100644 mod/etc/options_menu/images/chip_apply.png create mode 100644 mod/etc/options_menu/images/chip_preview.png delete mode 100644 mod/etc/options_menu/themes/creator/commands/c0000_1_refresh delete mode 100644 mod/etc/options_menu/themes/creator/commands/c0000_2_separator delete mode 100644 mod/etc/options_menu/themes/creator/commands/c9997_separator delete mode 100644 mod/etc/options_menu/themes/creator/scripts/om_refresh create mode 100644 mod/etc/options_menu/themes/creator/sprites/colors/commands/c0000_1_refresh create mode 100644 mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c0000_1_refresh create mode 100644 mod/etc/options_menu/themes/creator/sprites/luigi/commands/c0000_1_refresh create mode 100644 mod/etc/options_menu/themes/creator/sprites/mario/commands/c0000_1_refresh create mode 100644 mod/etc/options_menu/themes/creator/sprites/packed/commands/c0000_1_refresh create mode 100644 mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c0000_1_refresh create mode 100644 mod/etc/options_menu/themes/creator/sprites/pole/commands/c0000_1_refresh create mode 100644 mod/etc/options_menu/themes/creator/sprites/sbm/commands/c0000_1_refresh create mode 100644 mod/etc/options_menu/themes/creator/sprites/wall/commands/c0000_1_refresh create mode 100644 mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c0000_1_refresh create mode 100644 mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c0000_1_refresh diff --git a/mod/etc/options_menu/images/chip_apply.png b/mod/etc/options_menu/images/chip_apply.png new file mode 100644 index 0000000000000000000000000000000000000000..ea6d58172ac697a91632fb621dddb2daf30df59a GIT binary patch literal 501 zcmVENrx~vWeOVFO{W; z1RE7G_;O@qqsyA-&imYl?&)^#-I>Fk+1Z(emj4lT+3B(}uHd@U=aw3EE;}ZueV|iZ z_JB)_&mrI*u)lQgK5#iAz7H#C>GEYnEUE`b6x)o5Per?@fhWM;Qv5M+E+W2Vw^$j9 zwH@va!)m4Nv+8Cg=b2ir>@T*5ab`qBs^A46O`~nj08i&aFcA@tfTdJWeshv}5llqH z9pE)JG$vS3uHak>*0P&?e!k5k@RnH!M#Kpkpu?Qg;a?Y2L%pKDQopJj>O=MDWE3+$ zb$^k2FyIsSSv^(L&szC`X;knkaDG5+AF!$(7^j1A1+Ue_7l4b62o{w&oRNB*hAOyg z!g-SwjEIlGyNuK~G&l!u*Tla7cbgPk0iF+tr48v)M7*b|bugjsQLn2Xiu^70Mco>h zyAF;mSXs0e)GnFi2oBoKY?X$;t1jktni!%O-AbR?$If-&2(zUZ5yx_0lAFw)8>9T6 rNxwk)PLICYQ_~5rrKP2%g+KWLib$Ng`pTe&00000NkvXXu0mjfTd?Lv literal 0 HcmV?d00001 diff --git a/mod/etc/options_menu/images/chip_preview.png b/mod/etc/options_menu/images/chip_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..49ebfd5415098c5ef63204ea4b6b5ab39a21c0b9 GIT binary patch literal 609 zcmV-n0-pVeP)jqQ!@h`GWkwm)#p9}ZFvB?k*Voc;J?9UT@O5Nw%0V&C}5Ul=Cj#97Ub4a z{9(-2tjJCj8NmdY^f5O}&l`UQcxh_Jo!{gy0l$3ib!|51F-f|CZ)RB;3LyJ{UZDP7 z2Mn8K=PS^a_cGJ#J4Q{ffV+MowEFD?a1VH6e6GV>369l#K%egc7HC}az?qce#rM=< zR5$U|+tYTXe*mWZw*TG-E0K8Bz`GU~y~R6JyV$dG?Uve vv~$7xT(h/dev/null | read -r; then + findDiyAsset "$1" +fi $optionsMenu/themes/theme_manager --commandPath $diy_om_sprite_path/commands/ --scriptPath $diy_om_sprite_path/scripts --title $diy_title --layout grid & diff --git a/mod/etc/options_menu/themes/creator/scripts/om_preview b/mod/etc/options_menu/themes/creator/scripts/om_preview index 53ff181..0ebb48e 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_preview +++ b/mod/etc/options_menu/themes/creator/scripts/om_preview @@ -26,6 +26,13 @@ if [ ! -f "$b_file" ]; then echo "themeSelector" >"$p_file" fi +# chmenu/restart alone don't re-run preinit.d, so themeSelector's own +# DIY_THEME pickup (om_themescript) never re-fired - call it directly here, +# same as it'll run again at the next real boot via b_file/p_file +# shellcheck source=/dev/null +source "$b_file" +themeSelector + echo "usleep 20000; \ uistart; \ chmenu 001; \ diff --git a/mod/etc/options_menu/themes/creator/scripts/om_refresh b/mod/etc/options_menu/themes/creator/scripts/om_refresh deleted file mode 100644 index ba989e6..0000000 --- a/mod/etc/options_menu/themes/creator/scripts/om_refresh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -# shellcheck disable=SC2086,SC2154,SC3046,SC3051 -# -# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# shellcheck source=/dev/null -source $mountpoint/etc/options_menu/themes/scripts/om_vars -script_init - -diyThemeRemover -diyThemeChecker "loader" -touch "$omModGenSpacer" - -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/scripts/om_removediy b/mod/etc/options_menu/themes/creator/scripts/om_removediy index 3630c2d..3397cd6 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_removediy +++ b/mod/etc/options_menu/themes/creator/scripts/om_removediy @@ -22,4 +22,4 @@ script_init diyThemeRemover -$optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & +$optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/colors/commands/c0000_1_refresh new file mode 100644 index 0000000..4a7cc5b --- /dev/null +++ b/mod/etc/options_menu/themes/creator/sprites/colors/commands/c0000_1_refresh @@ -0,0 +1,5 @@ +COMMAND_NAME=REFRESH +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset colors force & +PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return index a7c3736..e5f4656 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c0000_1_refresh new file mode 100644 index 0000000..3a2a8ac --- /dev/null +++ b/mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c0000_1_refresh @@ -0,0 +1,5 @@ +COMMAND_NAME=REFRESH +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset colors_nes force & +PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return index a7c3736..e5f4656 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/luigi/commands/c0000_1_refresh new file mode 100644 index 0000000..ec50f39 --- /dev/null +++ b/mod/etc/options_menu/themes/creator/sprites/luigi/commands/c0000_1_refresh @@ -0,0 +1,5 @@ +COMMAND_NAME=REFRESH +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset luigi force & +PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return index a7c3736..e5f4656 100644 --- a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/mario/commands/c0000_1_refresh new file mode 100644 index 0000000..93a0d8d --- /dev/null +++ b/mod/etc/options_menu/themes/creator/sprites/mario/commands/c0000_1_refresh @@ -0,0 +1,5 @@ +COMMAND_NAME=REFRESH +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset mario force & +PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return index 3aa4540..1d1a989 100644 --- a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/packed/commands/c0000_1_refresh new file mode 100644 index 0000000..5d5d51f --- /dev/null +++ b/mod/etc/options_menu/themes/creator/sprites/packed/commands/c0000_1_refresh @@ -0,0 +1,5 @@ +COMMAND_NAME=REFRESH +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset packed force & +PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return index 3aa4540..1d1a989 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c0000_1_refresh new file mode 100644 index 0000000..051d48e --- /dev/null +++ b/mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c0000_1_refresh @@ -0,0 +1,5 @@ +COMMAND_NAME=REFRESH +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset packed_nes force & +PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return index 3aa4540..1d1a989 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/pole/commands/c0000_1_refresh new file mode 100644 index 0000000..477e58a --- /dev/null +++ b/mod/etc/options_menu/themes/creator/sprites/pole/commands/c0000_1_refresh @@ -0,0 +1,5 @@ +COMMAND_NAME=REFRESH +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset pole force & +PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return index 3aa4540..1d1a989 100644 --- a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c0000_1_refresh new file mode 100644 index 0000000..41c492c --- /dev/null +++ b/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c0000_1_refresh @@ -0,0 +1,5 @@ +COMMAND_NAME=REFRESH +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset sbm force & +PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return index 3aa4540..1d1a989 100644 --- a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/wall/commands/c0000_1_refresh new file mode 100644 index 0000000..36078b9 --- /dev/null +++ b/mod/etc/options_menu/themes/creator/sprites/wall/commands/c0000_1_refresh @@ -0,0 +1,5 @@ +COMMAND_NAME=REFRESH +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset wall force & +PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return index 3aa4540..1d1a989 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c0000_1_refresh new file mode 100644 index 0000000..bdaf9b7 --- /dev/null +++ b/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c0000_1_refresh @@ -0,0 +1,5 @@ +COMMAND_NAME=REFRESH +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset wall_far force & +PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return index 3aa4540..1d1a989 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c0000_1_refresh new file mode 100644 index 0000000..6887463 --- /dev/null +++ b/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c0000_1_refresh @@ -0,0 +1,5 @@ +COMMAND_NAME=REFRESH +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset wall_near force & +PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return index 3aa4540..1d1a989 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes b/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes index 1bd6650..f57f41c 100644 --- a/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes +++ b/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes @@ -3,5 +3,3 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_clearDownloads & PREVIEW_IMAGE=/etc/options_menu/images/chip_clear.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads index 6b33cb2..0928629 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads +++ b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads @@ -22,6 +22,5 @@ script_init clearDownloads remove "$subDummy" -remove "$omModSubSpacer" $optionsMenu/themes/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES --layout grid & diff --git a/mod/etc/options_menu/themes/scripts/om_overlay b/mod/etc/options_menu/themes/scripts/om_overlay index 5491626..244cbe0 100644 --- a/mod/etc/options_menu/themes/scripts/om_overlay +++ b/mod/etc/options_menu/themes/scripts/om_overlay @@ -1 +1,3 @@ chrono_trigger +turbografx16_mini +pce_mini_a diff --git a/mod/etc/options_menu/themes/scripts/om_themeCreator b/mod/etc/options_menu/themes/scripts/om_themeCreator index 377212e..2e01ce2 100644 --- a/mod/etc/options_menu/themes/scripts/om_themeCreator +++ b/mod/etc/options_menu/themes/scripts/om_themeCreator @@ -22,4 +22,4 @@ script_init diyThemeChecker -$optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title DIY_THEME & +$optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/scripts/om_themescript b/mod/etc/options_menu/themes/scripts/om_themescript index cb8cbf4..a8327f7 100644 --- a/mod/etc/options_menu/themes/scripts/om_themescript +++ b/mod/etc/options_menu/themes/scripts/om_themescript @@ -150,7 +150,7 @@ themeSelector() { FULL_THEME_PATH="$THEMES_PATH/$SELECTED_THEME" - if find "$DIY_THEME" -type f -print -quit 2>/dev/null | read -r; then + if find "$DIY_THEME" -type f 2>/dev/null | read -r; then FULL_THEME_PATH="$DIY_THEME" fi diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index b4e5e7d..dd83cb2 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -28,12 +28,6 @@ fontfix_file="$mountpoint/etc/preinit.d/b8025_fontfix" chmenu_file="$root_bin/chmenu" has_folders="${gStorage}/001" -downloadURL="http://hakchicloud.com/Hakchi_Themes/scripts" -themeURL="http://hakchicloud.com/Hakchi_Themes/themes" -# resolveDownloadUrls (below) does the actual availability check - two blocking -# wget calls (~0.9s combined) here would run on every om_vars source, i.e. on -# every screen of the theme selector, not just the ones that download anything - # Options Menu omMod="$optionsMenu/$modtitle" omModImgs="$optionsMenu/images" @@ -95,16 +89,14 @@ om_theme_count="$state_path/om_theme_count" omDummy="$omModCommands/c0000_0000" subDummy="$omModSubCmds/c0000_0000" omModSpacer="$omModCommands/c0000_separator" -omModSubSpacer="$omModSubCmds/c9998_separator" -omModGenSpacer="$omModGenCmds/c0000_separator" omModSettingsSpacer="$omModSettings/c0001_separator" #### ENABLE/DISABLE -enableDownloads="$omModCommands/c0000_download" -disableDownloads="$omModCommands/_download" -enableClear="$omModCommands/c0000_remove" -disableClear="$omModCommands/_remove" -enableSettings="$omModCommands/c0000_options" -disableSettings="$omModCommands/_options" +enableDownloads="$omModCommands/c0000_2_download" +disableDownloads="$omModCommands/_2_download" +enableClear="$omModCommands/c0000_5_remove" +disableClear="$omModCommands/_5_remove" +enableSettings="$omModCommands/c0000_4_options" +disableSettings="$omModCommands/_4_options" enableAudioSetting="$omModSettingsCmds/c0001_audiorandomizer" disableAudioSetting="$omModSettingsCmds/_audiorandomizer" enableAdvancedMusicHack="$omModSettingsCmds/c0001_audiorandomizerall" @@ -113,16 +105,16 @@ enableThemeSetting="$omModSettingsCmds/c0000_themerandomizer" disableThemeSetting="$omModSettingsCmds/_themerandomizer" enableSystemCleanUp="$omModSettingsCmds/c0001_systemcleanup" disableSystemCleanUp="$omModSettingsCmds/_systemcleanup" -enableThemeGenerator="$omModCommands/c0000_generatetheme" -disableThemeGenerator="$omModCommands/_generatetheme" +enableThemeGenerator="$omModCommands/c0000_3_generatetheme" +disableThemeGenerator="$omModCommands/_3_generatetheme" enableUninstaller="$omModSettingsCmds/c0001_uninstaller" disableUninstaller="$omModSettingsCmds/_uninstaller" -enableDiY="$omModGenCmds/c0000_apply" -disableDiY="$omModGenCmds/_apply" -enableDelDiY="$omModGenCmds/c0000_remove" -disableDelDiY="$omModGenCmds/_remove" -enablePreview="$omModGenCmds/c0000_preview" -disablePreview="$omModGenCmds/_preview" +enableDiY="$omModGenCmds/c0000_3_apply" +disableDiY="$omModGenCmds/_3_apply" +enableDelDiY="$omModGenCmds/c0000_5_remove" +disableDelDiY="$omModGenCmds/_5_remove" +enablePreview="$omModGenCmds/c0000_4_preview" +disablePreview="$omModGenCmds/_4_preview" enablePole="$omModGenCmds/c0006_pole" disablePole="$omModGenCmds/_pole" enableColor="$omModGenCmds/c0006_colors" @@ -159,14 +151,6 @@ snes_theme_path="${om_mod_path}/snes" #### ARRAYS diy_asset_array="diy_packed diy_wall diy_wall_far diy_wall_near diy_mario diy_luigi diy_colors diy_pole" # verify -asset_nes_arr="$omModPackedNesAssets $omModSbmAssets" -asset_snes_arr="$omModPoleAssets $omModMarioAssets $omModLuigiAssets $omModWallAssets $omModWallNAssets $omModWallFAssets $omModPackedAssets $omModColorsAssets" -asset_arr="$asset_snes_arr $asset_nes_arr" -scripts_arr="$omModPole/scripts $omModMario/scripts $omModLuigi/scripts $omModWall/scripts $omModWallN/scripts $omModWallF/scripts $omModPacked/scripts $omModColors/scripts" -nes_cmds_arr="$omModPackedNes/commands $omModColorsNes/commands $omModSbm/commands" -snes_cmds_arr="$omModPole/commands $omModMario/commands $omModLuigi/commands $omModWall/commands $omModWallN/commands $omModWallF/commands $omModPacked/commands $omModColors/commands" -cmds_arr="$snes_cmds_arr $nes_cmds_arr" -del_arr="$asset_arr $scripts_arr $cmds_arr" del_shared="$nes_theme_path $disableSmb $disablePackedNes $disableColorsNes $omModPackedNes $omModSbm $omModColorsNes ${omModImgs}/smb.png ${omModImgs}/packed_nes.png $disableColor $omModColors" del_on_nes="$snes_theme_path $shonen_theme_path $disableColor $disablePole $disableMario $disableLuigi $disablePacked $disableWall $disableWallFar $disableWallNear $omModPole $omModMario $omModLuigi $omModWall $omModWallN $omModWallF $omModPacked $omModColors ${omModImgs}/luigi.png ${omModImgs}/mario.png ${omModImgs}/packed.png ${omModImgs}/pole.png ${omModImgs}/wall*.png" @@ -188,11 +172,11 @@ case "$sftype" in esac hasThemesToInstall() { - if [ "$has_theme" != 0 ]; then - for f in *.tar.gz; do - tar -xzvf "$f" && rm -f "$f" - done - fi + [ "$has_theme" = 0 ] && return + for f in "$theme_path/$system".*.tar.gz; do + [ -f "$f" ] || continue + tar -xzvf "$f" -C "$theme_path" && rm -f "$f" + done } clearList() { @@ -342,10 +326,7 @@ sed -i \"s/SELECTED_THEME=\\\"CHANGEME\\\"/SELECTED_THEME=\\\"${thm}\\\"/g;\" \" COMMAND_TYPE=INTERNAL COMMAND_STR=sh $thm_script.sh PREVIEW_IMAGE=$preview_image -PREVIEW_IMAGE_X=762 -PREVIEW_IMAGE_Y=276 -PREVIEW_IMAGE_WIDTH=0 -PREVIEW_IMAGE_HEIGHT=272" > "$cmd_list" +DELETE_STR=rm -rf \"$full_path\" \"$full_path.png\" \"$cmd_list\" \"$thm_script.sh\" && usleep 50000 && %options_path%themes/theme_manager --title INSTALLED_THEMES --layout grid &" > "$cmd_list" done } @@ -357,38 +338,21 @@ removeRegionalTheme() { [ -d "$theme_path/snesce-$sfregion" ] && rm -rf "$theme_path/snesce-$sfregion" } -# picks the mirror that's actually reachable for downloadURL/themeURL - two -# blocking wget calls, so only run this where a download is about to happen, -# never unconditionally at om_vars source time -resolveDownloadUrls() { - # -T bounds a bad/unreachable network to a few seconds instead of hanging - # the whole UI (theme_manager blocks on this the entire time it runs) - ! wget -q -T 5 --spider "$downloadURL" > /dev/null 2>&1 && downloadURL="http://classicmods.net/files/themes/themeselector-scripts/" - ! wget -q -T 5 --spider "$themeURL" > /dev/null 2>&1 && themeURL="http://classicmods.net/files/themes/themeselector/" -} - getThemeList() { - # splash first - resolveDownloadUrls can take a few seconds (bounded by its - # own -T 5 now, but still real) when hakchicloud.com is slow/unreachable, - # and that shouldn't be a blank screen before the loading indicator shows + # splash first - theme_downloader's own GitHub/classicmods fallback check + # can take a few seconds when a host is slow/unreachable, and that + # shouldn't be a blank screen before the loading indicator shows "$omScripts/gen_splash" /tmp/om_splash.png THEME_LIST_DOWNLOADING_LINE1 THEME_LIST_DOWNLOADING_LINE2 && decodepng /tmp/om_splash.png > /dev/fb0 - resolveDownloadUrls cd "$omModSub" || exit if [ ! -e "${s_type}${listName}" ]; then clearDownloads - if wget -q -T 5 --spider "$downloadURL/${s_type}${listName}" > /dev/null 2>&1; then - wget -T 10 "$downloadURL/${s_type}${listName}" - else - netChecker - fi - + theme_downloader catalog "$s_type" "${s_type}${listName}" || netChecker fi # shellcheck disable=SC2035 tar -xzf "${s_type}${listName}" && rm -f *.tar.gz removeRegionalTheme touch "$subDummy" - create "$omModSubSpacer" usleep 1000 } @@ -438,7 +402,6 @@ loadThemeList() { # verify themeGenerator function themeGenerator() { - resolveDownloadUrls echo "Packaging: DefKorns" translate CATEGORY_UI_MOD echo "" @@ -449,21 +412,27 @@ themeGenerator() { full_path="${theme_path}/${theme_name}" cd "${theme_path}" || exit - theme_pkg="${themeURL}/${system}.${theme_name}.tar.gz" - [ "${theme_name}" = "all" ] && theme_pkg="${downloadURL}/${sftype}-all" - - if wget -q -T 5 --spider "${theme_pkg}" > /dev/null 2>&1; then - translate DOWNLOADING - wget -T 10 "${theme_pkg}" - if [ "${theme_name}" = "all" ]; then + if [ "${theme_name}" = "all" ]; then + ok=0 + if theme_downloader all-manifest "$sftype" "${sftype}-all"; then + ok=1 + translate DOWNLOADING while IFS= read -r t; do echo "$t" echo "" - theme_pkg="${themeURL}/${system}.${t}.tar.gz" - wget -T 10 "${theme_pkg}" - tar -xzvf "${system}.${t}.tar.gz" && remove "${system}.${t}.tar.gz" + theme_downloader theme "$sftype" "$t" "${system}.${t}.tar.gz" \ + && tar -xzvf "${system}.${t}.tar.gz" && remove "${system}.${t}.tar.gz" done < "${sftype}-all" fi + else + ok=0 + if theme_downloader theme "$sftype" "$theme_name" "${system}.${theme_name}.tar.gz"; then + ok=1 + translate DOWNLOADING + fi + fi + + if [ "$ok" = 1 ]; then [ -f "${system}.${theme_name}.tar.gz" ] && tar -xzvf "${system}.${theme_name}.tar.gz" && rm -f ./*.tar.gz remove "${sftype}-all" removeRegionalTheme @@ -474,7 +443,6 @@ themeGenerator() { else netChecker fi - } findDiyAsset() { @@ -490,11 +458,8 @@ findDiyAsset() { chars="0" colors="0" preview_square="0" + preview_cols="" json="" - preview_x="920" - preview_y="275" - preview_w="250" - preview_h="250" case "$1" in luigi | mario) @@ -502,10 +467,13 @@ findDiyAsset() { asset_path="layout/asset/sprite/${1}" asset="*.*" chars="1" - preview_w="0" preview_square="1" ;; - wall | wall_near | wall_far) + wall) + preview_square="1" + preview_cols="5" + ;; + wall_near | wall_far) preview_square="1" ;; sbm) @@ -513,24 +481,19 @@ findDiyAsset() { asset_path="sprites" asset="characters_sbm.png" chars="1" - preview_w="0" ;; packed) asset_path="sprites" - preview_x="780" - preview_w="0" - preview_h="200" preview_square="1" + preview_cols="4" # full UI screenshots - too detailed to shrink to 7 cols ;; packed_nes) diy_file="${sftype}.png" [ "$sfregion" = "jpn" ] && diy_file="hvc.png" asset_path="sprites" asset="$diy_file" - preview_x="780" - preview_w="0" preview_square="1" - preview_h="200" + preview_cols="4" # full UI screenshots - too detailed to shrink to 7 cols ;; colors) asset_path="scripts/env" @@ -549,7 +512,6 @@ findDiyAsset() { diy_file="${1}01.png" asset="*.*" chars="1" - preview_w="0" preview_square="1" ;; esac @@ -571,9 +533,13 @@ findDiyAsset() { paths_to_search="$omModSprites/assets/$1 $theme_path" find "$diy_sprite/commands" "$diy_sprite/scripts" -type f \ - -not -name 'om_*' -not -name 'c999[0-9]_*' \ + -not -name 'om_*' -not -name 'c999[0-9]_*' -not -name 'c0000_*' \ -not -name 'c0001_*' -not -name '_*' -exec rm {} + + # read once - avoids a [ -f ] stat() per theme below, slow on USB/SD + existing_cmds="$(ls -1 "$diy_sprite/commands" 2>/dev/null)" + existing_scripts="$(ls -1 "$diy_sprite/scripts" 2>/dev/null)" + diy_crop_batch=0 for p in $paths_to_search; do if [ "$(ls -A "$p")" ]; then @@ -623,7 +589,7 @@ findDiyAsset() { preview_image="$temp_preview" # cropped copy, queued above or already cached fi - if [ ! -f "$cmd_file" ]; then + if ! printf '%s\n' "$existing_cmds" | grep -qxF "c0001_$asset_name" || ! printf '%s\n' "$existing_scripts" | grep -qxF "$asset_name.sh"; then touch "$cmd_file" cmd_name="$(echo "$asset_name" | sed -e 's/_/ /g' | awk '{for(i=1;i<=NF;i++){$i=toupper(substr($i,1,1))substr($i,2)}}1')" [ "$p" = "$theme_path" ] && cmd_name="$(sed -r 's/NAME=//g' < "$info_path/info" | awk '{$NF="";sub(/[ \t]+$/,"")}1')" @@ -635,14 +601,13 @@ PREVIEW_NEAREST=TRUE" preview_square_str="" [ "$preview_square" = "1" ] && preview_square_str=" PREVIEW_SQUARE=TRUE" + preview_cols_str="" + [ -n "$preview_cols" ] && preview_cols_str=" +PREVIEW_GRID_COLS=$preview_cols" echo "COMMAND_NAME=$cmd_name COMMAND_TYPE=INTERNAL COMMAND_STR=sh $sprite_script.sh -PREVIEW_IMAGE=$preview_image -PREVIEW_IMAGE_X=$preview_x -PREVIEW_IMAGE_Y=$preview_y -PREVIEW_IMAGE_WIDTH=$preview_w -PREVIEW_IMAGE_HEIGHT=$preview_h${preview_nearest}${preview_square_str}" > "$cmd_file" +PREVIEW_IMAGE=$preview_image${preview_nearest}${preview_square_str}${preview_cols_str}" > "$cmd_file" copy "$omModLicense" "$sprite_script.sh" echo "diySpriteSelector \"$full_asset_path\" \"$asset_path\" \"$asset\"" >> "$sprite_script.sh" @@ -690,7 +655,6 @@ diyThemeChecker() { [ "$1" = "loader" ] && { "$omScripts/gen_splash" /tmp/om_splash.png THEME_LOADING_ASSETS && decodepng /tmp/om_splash.png > /dev/fb0; } [ ! -d "$diyThemePath" ] && mkdir -p "$diyThemePath" - create "$omModGenSpacer" # shellcheck disable=SC3061 if find "$diyThemePath" -type f | read -r; then enable_cmds "DiY" "DelDiY" "Preview" @@ -704,10 +668,8 @@ diyThemeChecker() { diyThemeRemover() { disable_cmds "DiY" "DelDiY" "Preview" - for item in $del_arr; do - find "$item" -type f -not -name 'c999[0-9]_*' -not -name 'om_*' -maxdepth 1 -exec rm {} + - done - + # only the applied selection - each category's own browse cache is + # refreshed by that category's own Refresh chip, not here [ -d "$diyThemePath" ] && find "$diyThemePath" -type f -not -name 'assets_here' -exec rm {} + } diff --git a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder index 209c097..2c12f8e 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder +++ b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder @@ -4,5 +4,3 @@ RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_theme tpf STATE_STR=sh %script_dir%/om_state theme_per_folder PREVIEW_IMAGE=/etc/options_menu/images/randtheme.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child index b5468df..acc2571 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child +++ b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child @@ -5,5 +5,3 @@ CHILD=TRUE COMMAND_STR=sh %script_dir%/om_theme child STATE_STR=sh %script_dir%/om_state theme_per_folder_child PREVIEW_IMAGE=/etc/options_menu/images/randtheme.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/settings/commands/c0001_uninstaller b/mod/etc/options_menu/themes/settings/commands/c0001_uninstaller index 4dc3de0..6067bf8 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0001_uninstaller +++ b/mod/etc/options_menu/themes/settings/commands/c0001_uninstaller @@ -3,5 +3,3 @@ COMMAND_TYPE=INTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_reset PREVIEW_IMAGE=/etc/options_menu/images/uninstall.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 6be8d88..aa721f4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,12 +26,14 @@ #include "localization.h" #include +#include #include #include #include #include #include #include +#include #ifndef MOD_VERSION #define MOD_VERSION "dev" @@ -58,6 +60,46 @@ static void FitCentered(Texture & tex, int boxX, int boxY, int boxW, int boxH, i tex.rect.y = boxY + (boxH - tex.rect.h) / 2; } +// dark gradient over the tile's bottom edge, solid for the lower ~60% (where +// the overlaid label sits) and fading to transparent above that, so the +// label stays legible over any image - SDL has no gradient fill primitive, +// so this is a stack of alpha-stepped bands +static void DrawBottomScrim(SDL_Renderer * renderer, int x, int y, int w, int h) +{ + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + const int bands = 24; + const Uint8 maxAlpha = 235; + const double rampFrac = 0.4; // reaches maxAlpha by 40% down the scrim, solid for the rest - the label sits in that solid tail, not the fade + for(int i = 0; i < bands; ++i) + { + double t = (double)i / (bands - 1); // 0 at top of scrim, 1 at bottom + double ramp = std::min(1.0, t / rampFrac); + Uint8 alpha = static_cast(maxAlpha * ramp); + SDL_Rect band{ x, y + h * i / bands, w, h / bands + 1 }; + SDL_SetRenderDrawColor(renderer, 0, 0, 0, alpha); + SDL_RenderFillRect(renderer, &band); + } + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); +} + +// scale-to-cover-centered (crops the overflow) - pair with a clip rect at +// (boxX+padding, boxY+padding, boxW-2*padding, boxH-2*padding) so the +// overflow doesn't bleed past the box, and DrawRoundedCornerMask afterwards +// to fake-round that clip rect's square corners back to match a rounded +// container +static void FitCover(Texture & tex, int boxX, int boxY, int boxW, int boxH, int padding) +{ + const int maxW = boxW - 2*padding; + const int maxH = boxH - 2*padding; + if(tex.rect.w <= 0 || tex.rect.h <= 0) + return; + double scale = std::max((double)maxW / tex.rect.w, (double)maxH / tex.rect.h); + tex.rect.w = static_cast(tex.rect.w * scale); + tex.rect.h = static_cast(tex.rect.h * scale); + tex.rect.x = boxX + (boxW - tex.rect.w) / 2; + tex.rect.y = boxY + (boxH - tex.rect.h) / 2; +} + static std::string TruncateToWidth(const std::string & text, int glyphSize, int available) { std::string label = text; @@ -80,8 +122,41 @@ static std::string TruncateToWidth(const std::string & text, int glyphSize, int return label; } +// only one theme_manager should ever hold /dev/fb0 at a time - two screens +// (e.g. a stuck/crashed instance plus a freshly-launched one) fight over it +// and both flicker. Takes over rather than refusing to start, so a lock left +// behind by a crash never blocks every future launch. +static const char * LockPath = "/tmp/theme_manager.lock"; + +static void ReleaseSingleInstanceLock() +{ + remove(LockPath); +} + +static void AcquireSingleInstanceLock() +{ + std::ifstream in(LockPath); + pid_t oldPid = 0; + if(in.good()) + in >> oldPid; + in.close(); + if(oldPid > 0 && oldPid != getpid() && kill(oldPid, 0) == 0) + { + kill(oldPid, SIGTERM); + for(int i = 0; i < 40 && kill(oldPid, 0) == 0; ++i) + usleep(50000); + if(kill(oldPid, 0) == 0) + kill(oldPid, SIGKILL); + } + std::ofstream(LockPath, std::ios::trunc) << getpid(); + atexit(ReleaseSingleInstanceLock); + signal(SIGTERM, [](int) { exit(0); }); // so a newer instance's takeover runs our atexit cleanup too +} + int main(int argc, char * argv[]) { + AcquireSingleInstanceLock(); + const std::string optionsLocation = "/etc/options_menu/"; std::string commandLocation = optionsLocation + "themes/commands/"; std::string scriptLocation = optionsLocation + "themes/scripts/"; @@ -138,7 +213,12 @@ int main(int argc, char * argv[]) if(in.is_open()) { Command c(in); - if(!(commands.size() == 0 && c.command.size() == 0)) + // c0000_0000: leading empty sentinel, always skipped. A + // later empty COMMAND_STR is a reserved blank slot instead - + // kept and skipped by the strip/grid draw loops below + if(commands.empty() && c.command.empty()) + continue; + if(!c.command.empty()) { sReplace(c.command, "%options_path%", optionsLocation); sReplace(c.command, "%script_dir%", scriptLocation); @@ -150,9 +230,9 @@ int main(int argc, char * argv[]) sReplace(c.stateCommand, "%script_dir%", scriptLocation); c.UpdateState(); } - commands.push_back(c); - isThemeItem.push_back(file.compare(0, 6, "c0000_") != 0); } + commands.push_back(c); + isThemeItem.push_back(file.compare(0, 6, "c0000_") != 0); } } } @@ -279,6 +359,44 @@ int main(int argc, char * argv[]) return x - UiTheme::BadgeGroupGap; }; + // hold-to-delete hint, different color than the real B badge + Badge badgeHold{ Texture("B", 16, renderer, 0, 0, false, UiTheme::BadgeLetterColor, true), Texture(Translate("HINT_DELETE"), 16, renderer, 0, 0, false, 0xFFFFFFFF, true), UiTheme::BadgeXDark, UiTheme::BadgeX }; + + // true if deleted - caller should break out of the main loop + auto ConfirmDelete = [&]() -> bool + { + const std::string & confirmKey = commands[currentCommandId].deleteConfirmKey; + Texture confirmTitle(Translate(confirmKey.empty() ? "DELETE_CONFIRM_GENERIC" : confirmKey), 24, renderer, 640, 320, true, 0xFFFFFFFF, true); + Texture confirmHint(Translate("DELETE_CONFIRM_HINT"), 16, renderer, 640, 360, true, 0xFFFFFFFF, true); + controller.GetButtonStatus(B); // consume the still-held B from the triggering long-press + bool confirmed = false; + for(;;) + { + controller.Update(); + if(controller.GetButtonStatus(B)) + break; + if(controller.GetButtonStatus(A) || controller.GetButtonStatus(START)) + { + confirmed = true; + break; + } + sdl_context.StartFrame(); + DrawFillRect(renderer, UiTheme::FrameRect, UiTheme::BgR, UiTheme::BgG, UiTheme::BgB); + DrawStrokeRect(renderer, UiTheme::FrameRect, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BorderRadius); + confirmTitle.Draw(renderer); + confirmHint.Draw(renderer); + SDL_SetRenderDrawColor(renderer, bgR, bgG, bgB, 0xFF); // flat helpers leave the draw color dirty + sdl_context.EndFrame(); + } + if(!confirmed) + return false; + system(commands[currentCommandId].deleteCommand.c_str()); + return true; + }; + + const unsigned int bHoldThresholdMs = 1000; + bool bWasHeld = false, bHoldFired = false; + // shared chrome; section title is separate since grid positions it lower auto DrawChromeCommon = [&]() { @@ -301,15 +419,25 @@ int main(int argc, char * argv[]) if(gridLayout) { // ============================= GRID LAYOUT ============================= - // squareTiles: pack more/narrower columns for square-ish previews + // squareTiles: pack more/narrower columns for square-ish previews, + // unless the category overrides the column count itself const bool squareTiles = themeStart < pinnedStartIndex && commands[themeStart].previewSquare; - const int GridCols = squareTiles ? 7 : 4; + const int explicitCols = themeStart < pinnedStartIndex ? commands[themeStart].previewGridCols : -1; + const int GridCols = explicitCols > 0 ? explicitCols : (squareTiles ? 7 : 4); const int GridGap = 20; - // strip = fixed actions [0,themeStart), skipping empty/separator rows. - // Back/Exit aren't chips here - hinted in the footer instead (badgeB) - std::vector stripIndices; + // strip = fixed actions [0,themeStart). An empty entry stays a + // reserved-width slot rather than being compacted out, so a + // conditional button appearing/disappearing doesn't shift the rest. + // stripSlots drives layout; stripIndices is the navigable subset - + // the D-pad skips blank slots. Back/Exit aren't chips here - hinted + // in the footer instead (badgeB) + std::vector stripSlots; for(int i = 0; i < themeStart; ++i) + stripSlots.push_back(i); + const int stripSlotCount = static_cast(stripSlots.size()); + std::vector stripIndices; + for(int i : stripSlots) if(!commands[i].command.empty()) stripIndices.push_back(i); const int stripCount = static_cast(stripIndices.size()); @@ -325,12 +453,14 @@ int main(int argc, char * argv[]) const int chipIconSize = 22; // small, matches the 14px label's own scale // no strip on a pure-tile screen (e.g. a DIY category) - use the row for a 3rd grid row - const bool hasStrip = stripCount > 0; + const bool hasStrip = stripSlotCount > 0; const int StripTop = UiTheme::HeaderDividerY + 22; const int StripH = hasStrip ? 58 : 0; const int GridLeft = UiTheme::FrameX + 32; const int GridRight = UiTheme::FrameX + UiTheme::FrameW - 32; - const int chipW = hasStrip ? (GridRight - GridLeft - (stripCount-1)*chipGap) / stripCount : 0; + // capped so a strip with few slots doesn't stretch its chips wide + const int MaxChipW = 200; + const int chipW = hasStrip ? std::min(MaxChipW, (GridRight - GridLeft - (stripSlotCount-1)*chipGap) / stripSlotCount) : 0; titleText = Texture(Translate(titleKey), UiTheme::SectionTitleFontSize - 6, renderer, UiTheme::SectionTitleX, 0, false, 0xFFFFFFFF, true); titleText.rect.y = hasStrip ? (StripTop + StripH + 22) : (UiTheme::HeaderDividerY + 22); const int GridTop = titleText.rect.y + titleText.rect.h + 20; @@ -366,11 +496,11 @@ int main(int argc, char * argv[]) for(int i = 0; i < (int)commands.size(); ++i) { Command & c = commands[i]; + if(c.command.empty()) + continue; // reserved blank slot, not a chip or tile std::string label = Translate(c.name); if(i < themeStart || i >= pinnedStartIndex) { - if(commands[i].command.empty()) - continue; // separator, not a chip int labelAvail = chipW - chipIconSize - 24; if(c.previewImage.size()) { @@ -387,8 +517,10 @@ int main(int argc, char * argv[]) } else { - // tile image loaded lazily, see EnsureTileImage below - label = TruncateToWidth(label, 15, TileW - 12); + // tile image loaded lazily, see EnsureTileImage below - + // overlaid on the image's scrim, left-aligned with margins + // on both sides (see the draw loop) + label = TruncateToWidth(label, 15, TileW - 28); tileLabels[i] = Texture(label, 15, renderer, 0, 0, false, 0xFFFFFFFF, true); } } @@ -404,7 +536,8 @@ int main(int argc, char * argv[]) SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, c.previewNearest ? "0" : "1"); Texture art(c.previewImage, renderer, 0, 0); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); - FitCentered(art, 0, 0, TileW - 16, TileH - 30, 0); + // fills the whole tile - matches the per-frame FitCover below + FitCover(art, 0, 0, TileW, TileH, 0); tileImages[idx] = art; }; @@ -450,21 +583,21 @@ int main(int argc, char * argv[]) break; } } - else if(controller.GetButtonStatus(LEFT)) + else if(controller.HeldRepeat(LEFT)) { if(inGrid && currentCommandId > themeStart) SetCurrentCommand(currentCommandId - 1); else if(!inGrid && stripCount > 0) SetCurrentCommand(StripToCommand(std::max(0, CommandToStrip(currentCommandId) - 1))); } - else if(controller.GetButtonStatus(RIGHT)) + else if(controller.HeldRepeat(RIGHT)) { if(inGrid && currentCommandId < pinnedStartIndex - 1) SetCurrentCommand(currentCommandId + 1); else if(!inGrid && stripCount > 0) SetCurrentCommand(StripToCommand(std::min(stripCount - 1, CommandToStrip(currentCommandId) + 1))); } - else if(controller.GetButtonStatus(UP)) + else if(controller.HeldRepeat(UP)) { if(inGrid) { @@ -478,7 +611,7 @@ int main(int argc, char * argv[]) SetCurrentCommand(currentCommandId - GridCols); } } - else if(controller.GetButtonStatus(DOWN)) + else if(controller.HeldRepeat(DOWN)) { if(!inGrid && themeStart < pinnedStartIndex) { @@ -488,34 +621,62 @@ int main(int argc, char * argv[]) else if(inGrid && currentCommandId + GridCols < pinnedStartIndex) SetCurrentCommand(currentCommandId + GridCols); } - else if(controller.GetButtonStatus(B) && pinnedStartIndex < (int)commands.size()) + // B tracked outside the else-if chain, independent of pinned Back/Exit { - // Back/Exit isn't a chip - B runs it directly - Command & backCmd = commands[pinnedStartIndex]; - if(backCmd.runInternal) + bool bHeldNow = controller.PeekButtonStatus(B); + if(bHeldNow) { - backCmd.RunCommand(sdl_context, &controller, { gearIcon, appTitleText, appVersionText, creditText }, bgR, bgG, bgB); - if(backCmd.isToggle) - backCmd.UpdateState(); + if(!bHoldFired && controller.HeldMillis(B) >= bHoldThresholdMs) + { + bHoldFired = true; + if(!commands[currentCommandId].deleteCommand.empty() && ConfirmDelete()) + break; + } } else { - system(backCmd.command.c_str()); - break; + if(bWasHeld && !bHoldFired && pinnedStartIndex < (int)commands.size()) + { + // quick tap - Back/Exit isn't a chip, B runs it directly + Command & backCmd = commands[pinnedStartIndex]; + if(backCmd.runInternal) + { + backCmd.RunCommand(sdl_context, &controller, { gearIcon, appTitleText, appVersionText, creditText }, bgR, bgG, bgB); + if(backCmd.isToggle) + backCmd.UpdateState(); + } + else + { + system(backCmd.command.c_str()); + break; + } + } + bHoldFired = false; } + bWasHeld = bHeldNow; } DrawChromeCommon(); DrawSectionTitle(); - if(pinnedStartIndex < (int)commands.size()) - DrawBadge(badgeB, UiTheme::BadgeClusterRightX - UiTheme::BadgeOuterSize - UiTheme::BadgeLabelGap - badgeA.label.rect.w - UiTheme::BadgeGroupGap); + { + int rightEdge = UiTheme::BadgeClusterRightX - UiTheme::BadgeOuterSize - UiTheme::BadgeLabelGap - badgeA.label.rect.w - UiTheme::BadgeGroupGap; + if(pinnedStartIndex < (int)commands.size()) + rightEdge = DrawBadge(badgeB, rightEdge); + if(!commands[currentCommandId].deleteCommand.empty()) + DrawBadge(badgeHold, rightEdge); + } // action strip { int x = GridLeft; - for(int pos = 0; pos < stripCount; ++pos) + for(int pos = 0; pos < stripSlotCount; ++pos) { - int idx = StripToCommand(pos); + int idx = stripSlots[pos]; + if(commands[idx].command.empty()) + { + x += chipW + chipGap; // reserved gap - just leave it blank + continue; + } bool selected = idx == currentCommandId; SDL_Rect chipRect{ x, StripTop, chipW, StripH }; if(selected) @@ -561,6 +722,8 @@ int main(int argc, char * argv[]) int idx = rowStart + col; if(idx > lastThemeIndex) break; + if(commands[idx].command.empty()) + continue; // reserved blank cell, not a tile int x = GridLeft + col*(TileW+GridGap); bool selected = idx == currentCommandId; SDL_Rect tileRect{ x, y, TileW, TileH }; @@ -568,12 +731,23 @@ int main(int argc, char * argv[]) EnsureTileImage(idx); if(tileImages[idx].rect.w > 0) { - FitCentered(tileImages[idx], x, y+6, TileW, TileH-24, 8); + // cover-fills the whole tile (crops overflow) + // instead of letterboxing, touching all 4 edges - + // clip to the tile so the crop doesn't bleed into + // neighboring tiles, then mask the clip's square + // corners back to rounded + SDL_RenderSetClipRect(renderer, &tileRect); + FitCover(tileImages[idx], x, y, TileW, TileH, 0); tileImages[idx].Draw(renderer); + const int ScrimH = 56; + DrawBottomScrim(renderer, x, y + TileH - ScrimH, TileW, ScrimH); + SDL_RenderSetClipRect(renderer, nullptr); + DrawRoundedCornerMask(renderer, tileRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); } DrawStrokeRect(renderer, tileRect, selected ? UiTheme::AccentR : UiTheme::BorderR, selected ? UiTheme::AccentG : UiTheme::BorderG, selected ? UiTheme::AccentB : UiTheme::BorderB, selected ? 3 : UiTheme::BorderWidth, UiTheme::BoxRadius); - tileLabels[idx].rect.x = x + (TileW - tileLabels[idx].rect.w) / 2; - tileLabels[idx].rect.y = y + TileH - tileLabels[idx].rect.h - 6; + // overlaid on the scrim, left-aligned with a small margin + tileLabels[idx].rect.x = x + 14; + tileLabels[idx].rect.y = y + TileH - tileLabels[idx].rect.h - 12; tileLabels[idx].Draw(renderer); } } @@ -690,6 +864,8 @@ int main(int argc, char * argv[]) { DrawChromeCommon(); DrawSectionTitle(); + if(!commands[currentCommandId].deleteCommand.empty()) + DrawBadge(badgeHold, UiTheme::BadgeClusterRightX - UiTheme::BadgeOuterSize - UiTheme::BadgeLabelGap - badgeA.label.rect.w - UiTheme::BadgeGroupGap); DrawRoundedFillRect(renderer, selectedRowRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); DrawStrokeRect(renderer, selectedRowRect, UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB, 2, UiTheme::BoxRadius); @@ -729,7 +905,7 @@ int main(int argc, char * argv[]) break; } } - else if(controller.GetButtonStatus(UP)) + else if(controller.HeldRepeat(UP)) { // bounded so an all-separator list can't spin forever int newCommandId = currentCommandId; @@ -741,7 +917,7 @@ int main(int argc, char * argv[]) } SetCurrentCommand(newCommandId); } - else if(controller.GetButtonStatus(DOWN)) + else if(controller.HeldRepeat(DOWN)) { int newCommandId = currentCommandId; for(size_t tries = 0; tries < commands.size(); ++tries) @@ -752,11 +928,24 @@ int main(int argc, char * argv[]) } SetCurrentCommand(newCommandId); } - // B = jump straight to the last row (BACK/EXIT) - else if(controller.GetButtonStatus(B)) + // B tracked outside the else-if chain: tap = last row, hold ~1s = delete + bool bHeldNow = controller.PeekButtonStatus(B); + if(bHeldNow) + { + if(!bHoldFired && controller.HeldMillis(B) >= bHoldThresholdMs) + { + bHoldFired = true; + if(!commands[currentCommandId].deleteCommand.empty() && ConfirmDelete()) + break; + } + } + else { - SetCurrentCommand(commands.size()-1); + if(bWasHeld && !bHoldFired) + SetCurrentCommand(commands.size()-1); + bHoldFired = false; } + bWasHeld = bHeldNow; DrawChrome(); From 23bf9fac4c9b0e34b197b6c20091ce913c8b86b1 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 18 Sep 2026 01:16:43 +0000 Subject: [PATCH 060/210] Update subproject commit reference in OptionsMenu --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 80e05bd..6c3d40e 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 80e05bd25ebec492d8176abbb57bb0b64026a16b +Subproject commit 6c3d40e0801189ad967178751d60279340033844 From 1005e9e0512e0848ce6ef1ebc97b8ef9b4d0cf20 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 18 Sep 2026 01:17:26 +0000 Subject: [PATCH 061/210] Add uitheme.cpp to SOURCES and update texture color references to use UiTheme settings --- Makefile.docker | 2 +- src/main.cpp | 36 +++++++++++++++++++----------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Makefile.docker b/Makefile.docker index d4ccfbf..2230736 100644 --- a/Makefile.docker +++ b/Makefile.docker @@ -20,7 +20,7 @@ endif CXXFLAGS = -std=c++11 -Os -Ivendor/OptionsMenu/src $(SDL_CFLAGS) LDLIBS = $(SDL_LIBS) VENDOR_SRC_DIR = vendor/OptionsMenu/src -SOURCES = src/main.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp +SOURCES = src/main.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp OBJECTS = $(SOURCES:.cpp=.o) all: mod/etc/options_menu/themes/theme_manager mod/bin/theme_downloader diff --git a/src/main.cpp b/src/main.cpp index aa721f4..a37cdf4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -190,6 +190,7 @@ int main(int argc, char * argv[]) LoadLanguageFromConfig(optionsLocation); SetTTFFontPath(optionsLocation); + UiTheme::LoadThemeConfig(optionsLocation); // isThemeItem: false for a "c0000_*" fixed action, true for a theme/asset // row - only used by grid layout, to split strip vs. tile @@ -327,15 +328,15 @@ int main(int argc, char * argv[]) Texture switchOn(optionsLocation + UiTheme::AssetSwitchOn, renderer); Texture switchOff(optionsLocation + UiTheme::AssetSwitchOff, renderer); - Texture appTitleText("Theme Manager", UiTheme::TitleFontSize, renderer, UiTheme::TitleX, UiTheme::TitleY, false, 0xFFFFFFFF, true); + Texture appTitleText("Theme Manager", UiTheme::TitleFontSize, renderer, UiTheme::TitleX, UiTheme::TitleY, false, UiTheme::TextColor, true); appTitleText.rect.y -= appTitleText.rect.h / 2; - Texture appVersionText(MOD_VERSION, UiTheme::VersionFontSize, renderer, appTitleText.rect.x + appTitleText.rect.w + UiTheme::VersionGap, UiTheme::TitleY, false, 0xFFFFFFFF, true); + Texture appVersionText(MOD_VERSION, UiTheme::VersionFontSize, renderer, appTitleText.rect.x + appTitleText.rect.w + UiTheme::VersionGap, UiTheme::TitleY, false, UiTheme::TextColor, true); appVersionText.rect.y -= appVersionText.rect.h / 2; - Texture titleText(Translate(titleKey), UiTheme::SectionTitleFontSize, renderer, UiTheme::SectionTitleX, UiTheme::SectionTitleY, false, 0xFFFFFFFF, true); - Texture creditText("Theme Manager - by DefKorns", 16, renderer, UiTheme::CreditX, UiTheme::CreditY, false, 0xFFFFFFFF, true); + Texture titleText(Translate(titleKey), UiTheme::SectionTitleFontSize, renderer, UiTheme::SectionTitleX, UiTheme::SectionTitleY, false, UiTheme::TextColor, true); + Texture creditText("Theme Manager - by DefKorns", 16, renderer, UiTheme::CreditX, UiTheme::CreditY, false, UiTheme::TextColor, true); struct Badge { Texture letter; Texture label; UiTheme::BadgeColor rim; UiTheme::BadgeColor fill; }; - Badge badgeA{ Texture("A", 16, renderer, 0, 0, false, UiTheme::BadgeLetterColor, true), Texture(Translate("HINT_SELECT"), 16, renderer, 0, 0, false, 0xFFFFFFFF, true), UiTheme::BadgeADark, UiTheme::BadgeA }; + Badge badgeA{ Texture("A", 16, renderer, 0, 0, false, UiTheme::BadgeLetterColor, true), Texture(Translate("HINT_SELECT"), 16, renderer, 0, 0, false, UiTheme::TextColor, true), UiTheme::BadgeADark, UiTheme::BadgeA }; Texture badgeOuter(optionsLocation + UiTheme::AssetBadgeOuter, renderer); Texture badgeInner(optionsLocation + UiTheme::AssetBadgeInner, renderer); auto DrawBadge = [&](Badge & badge, int rightEdgeX) -> int @@ -350,8 +351,9 @@ int main(int argc, char * argv[]) badgeInner.rect = { x+innerOffset, y+innerOffset, UiTheme::BadgeInnerSize, UiTheme::BadgeInnerSize }; SDL_SetTextureColorMod(badgeInner.texture.get(), badge.fill.r, badge.fill.g, badge.fill.b); badgeInner.Draw(renderer); - badge.letter.rect.x = x + (UiTheme::BadgeOuterSize - badge.letter.rect.w)/2; - badge.letter.rect.y = y + (UiTheme::BadgeOuterSize - badge.letter.rect.h)/2; + // glyph bearing makes the pure-math center look 1px down/left - nudged + badge.letter.rect.x = x + (UiTheme::BadgeOuterSize - badge.letter.rect.w)/2 + 1; + badge.letter.rect.y = y + (UiTheme::BadgeOuterSize - badge.letter.rect.h)/2 - 1; badge.letter.Draw(renderer); badge.label.rect.x = x + UiTheme::BadgeOuterSize + UiTheme::BadgeLabelGap; badge.label.rect.y = y + (UiTheme::BadgeOuterSize - badge.label.rect.h)/2; @@ -360,14 +362,14 @@ int main(int argc, char * argv[]) }; // hold-to-delete hint, different color than the real B badge - Badge badgeHold{ Texture("B", 16, renderer, 0, 0, false, UiTheme::BadgeLetterColor, true), Texture(Translate("HINT_DELETE"), 16, renderer, 0, 0, false, 0xFFFFFFFF, true), UiTheme::BadgeXDark, UiTheme::BadgeX }; + Badge badgeHold{ Texture("B", 16, renderer, 0, 0, false, UiTheme::BadgeLetterColor, true), Texture(Translate("HINT_DELETE"), 16, renderer, 0, 0, false, UiTheme::TextColor, true), UiTheme::BadgeXDark, UiTheme::BadgeX }; // true if deleted - caller should break out of the main loop auto ConfirmDelete = [&]() -> bool { const std::string & confirmKey = commands[currentCommandId].deleteConfirmKey; - Texture confirmTitle(Translate(confirmKey.empty() ? "DELETE_CONFIRM_GENERIC" : confirmKey), 24, renderer, 640, 320, true, 0xFFFFFFFF, true); - Texture confirmHint(Translate("DELETE_CONFIRM_HINT"), 16, renderer, 640, 360, true, 0xFFFFFFFF, true); + Texture confirmTitle(Translate(confirmKey.empty() ? "DELETE_CONFIRM_GENERIC" : confirmKey), 24, renderer, 640, 320, true, UiTheme::TextColor, true); + Texture confirmHint(Translate("DELETE_CONFIRM_HINT"), 16, renderer, 640, 360, true, UiTheme::TextColor, true); controller.GetButtonStatus(B); // consume the still-held B from the triggering long-press bool confirmed = false; for(;;) @@ -461,7 +463,7 @@ int main(int argc, char * argv[]) // capped so a strip with few slots doesn't stretch its chips wide const int MaxChipW = 200; const int chipW = hasStrip ? std::min(MaxChipW, (GridRight - GridLeft - (stripSlotCount-1)*chipGap) / stripSlotCount) : 0; - titleText = Texture(Translate(titleKey), UiTheme::SectionTitleFontSize - 6, renderer, UiTheme::SectionTitleX, 0, false, 0xFFFFFFFF, true); + titleText = Texture(Translate(titleKey), UiTheme::SectionTitleFontSize - 6, renderer, UiTheme::SectionTitleX, 0, false, UiTheme::TextColor, true); titleText.rect.y = hasStrip ? (StripTop + StripH + 22) : (UiTheme::HeaderDividerY + 22); const int GridTop = titleText.rect.y + titleText.rect.h + 20; const int GridBottom = UiTheme::FooterDividerY - 14; @@ -479,14 +481,14 @@ int main(int argc, char * argv[]) } const int GridRowPitch = TileH + GridGap; - Texture gridScrollUp("^", 16, renderer, GridRight + 22, GridTop + 24, false, 0xFFFFFFFF, true); + Texture gridScrollUp("^", 16, renderer, GridRight + 22, GridTop + 24, false, UiTheme::TextColor, true); gridScrollUp.rect.x -= gridScrollUp.rect.w / 2; Texture gridScrollDown = gridScrollUp; gridScrollDown.rect.y = GridBottom - 24 - gridScrollDown.rect.h; // Back/Exit hint, footer badge next to A's - B runs it directly Badge badgeB{ Texture("B", 16, renderer, 0, 0, false, UiTheme::BadgeLetterColor, true), - Texture(pinnedStartIndex < (int)commands.size() ? Translate(commands[pinnedStartIndex].name) : "", 16, renderer, 0, 0, false, 0xFFFFFFFF, true), + Texture(pinnedStartIndex < (int)commands.size() ? Translate(commands[pinnedStartIndex].name) : "", 16, renderer, 0, 0, false, UiTheme::TextColor, true), UiTheme::BadgeBDark, UiTheme::BadgeB }; std::vector chipIcons(commands.size()); @@ -513,7 +515,7 @@ int main(int argc, char * argv[]) labelAvail = chipW - chipIconSize - 32; } label = TruncateToWidth(label, 14, labelAvail); - chipLabels[i] = Texture(label, 14, renderer, 0, 0, false, 0xFFFFFFFF, true); + chipLabels[i] = Texture(label, 14, renderer, 0, 0, false, UiTheme::TextColor, true); } else { @@ -521,7 +523,7 @@ int main(int argc, char * argv[]) // overlaid on the image's scrim, left-aligned with margins // on both sides (see the draw loop) label = TruncateToWidth(label, 15, TileW - 28); - tileLabels[i] = Texture(label, 15, renderer, 0, 0, false, 0xFFFFFFFF, true); + tileLabels[i] = Texture(label, 15, renderer, 0, 0, false, UiTheme::TextColor, true); } } std::vector tileImageLoaded(commands.size(), false); @@ -767,7 +769,7 @@ int main(int argc, char * argv[]) } // ============================= LIST LAYOUT ============================= - Texture scrollUp("^", 16, renderer, UiTheme::ScrollX, UiTheme::ScrollUpY, false, 0xFFFFFFFF, true); + Texture scrollUp("^", 16, renderer, UiTheme::ScrollX, UiTheme::ScrollUpY, false, UiTheme::TextColor, true); scrollUp.rect.x -= scrollUp.rect.w / 2; Texture scrollDown = scrollUp; scrollDown.rect.y = UiTheme::ScrollDownY; @@ -780,7 +782,7 @@ int main(int argc, char * argv[]) { int textX = UiTheme::RowTextX + (c.child ? ChildIndent : 0); std::string label = TruncateToWidth(Translate(c.name), RowGlyphSize, UiTheme::RowControlRightX - RowTextGapPx - textX); - c.texture = Texture(label, RowGlyphSize, renderer, textX, 0, false, 0xFFFFFFFF, true); + c.texture = Texture(label, RowGlyphSize, renderer, textX, 0, false, UiTheme::TextColor, true); } const int modernRowPitch = std::max(UiTheme::RowPitch, GetTTFLineHeight(RowGlyphSize)); From a0a295d6409517624bc9cbf7117d45547f76acf7 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 18 Sep 2026 19:49:37 +0000 Subject: [PATCH 062/210] Refactor character demo names and animate mario/luigi DIY tiles, dedup identical sprites, drop character branding --- .../themes/creator/commands/c0002_mario | 2 +- .../themes/creator/commands/c0003_luigi | 2 +- .../themes/creator/scripts/om_diy_asset | 4 +- mod/etc/options_menu/themes/lang/de-DE.lang | 4 +- mod/etc/options_menu/themes/lang/en-US.lang | 4 +- mod/etc/options_menu/themes/lang/es-ES.lang | 4 +- mod/etc/options_menu/themes/lang/fr-FR.lang | 4 +- mod/etc/options_menu/themes/lang/it-IT.lang | 4 +- mod/etc/options_menu/themes/lang/ja-JP.lang | 4 +- mod/etc/options_menu/themes/lang/nl-NL.lang | 4 +- mod/etc/options_menu/themes/lang/pt-PT.lang | 4 +- mod/etc/options_menu/themes/lang/ru-RU.lang | 4 +- mod/etc/options_menu/themes/scripts/om_vars | 16 +++++ src/main.cpp | 71 +++++++++++++++---- 14 files changed, 95 insertions(+), 36 deletions(-) diff --git a/mod/etc/options_menu/themes/creator/commands/c0002_mario b/mod/etc/options_menu/themes/creator/commands/c0002_mario index 30f1f23..8b8cbf7 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0002_mario +++ b/mod/etc/options_menu/themes/creator/commands/c0002_mario @@ -1,4 +1,4 @@ -COMMAND_NAME=DEMO_CHARACTER_1_MARIO +COMMAND_NAME=DEMO_CHARACTER_1 COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset mario & diff --git a/mod/etc/options_menu/themes/creator/commands/c0003_luigi b/mod/etc/options_menu/themes/creator/commands/c0003_luigi index 521ba58..6b9c9e5 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0003_luigi +++ b/mod/etc/options_menu/themes/creator/commands/c0003_luigi @@ -1,4 +1,4 @@ -COMMAND_NAME=DEMO_CHARACTER_2_LUIGI +COMMAND_NAME=DEMO_CHARACTER_2 COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset luigi & diff --git a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset index 16ff55f..b7e5837 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset +++ b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset @@ -23,7 +23,7 @@ script_init case "$1" in luigi) diy_om_sprite_path="$omModLuigi" - diy_title="DEMO_CHARACTER_2_LUIGI" + diy_title="DEMO_CHARACTER_2" ;; pole) diy_om_sprite_path="$omModPole" @@ -47,7 +47,7 @@ packed) ;; mario) diy_om_sprite_path="$omModMario" - diy_title="DEMO_CHARACTER_1_MARIO" + diy_title="DEMO_CHARACTER_1" ;; colors) diy_om_sprite_path="$omModColors" diff --git a/mod/etc/options_menu/themes/lang/de-DE.lang b/mod/etc/options_menu/themes/lang/de-DE.lang index 266d758..97efad0 100644 --- a/mod/etc/options_menu/themes/lang/de-DE.lang +++ b/mod/etc/options_menu/themes/lang/de-DE.lang @@ -8,9 +8,9 @@ REFRESH=Aktualisieren REFRESH_ASSETS=Assets aktualisieren BASE_UI=Basis-UI BACKGROUND=Hintergrund -DEMO_CHARACTER_1_MARIO=Demo: Charakter Nr. 1 - Mario +DEMO_CHARACTER_1=Demo: Charakter Nr. 1 DEMO_CHARACTERS=Demo: Charaktere -DEMO_CHARACTER_2_LUIGI=Demo: Charakter Nr. 2 - Luigi +DEMO_CHARACTER_2=Demo: Charakter Nr. 2 DEMO_BACKGROUND_NEAR=Demo: Hintergrund nah DEMO_BACKGROUND_FAR=Demo: Hintergrund fern COLORS=Farben diff --git a/mod/etc/options_menu/themes/lang/en-US.lang b/mod/etc/options_menu/themes/lang/en-US.lang index 11cce49..f3ddf78 100644 --- a/mod/etc/options_menu/themes/lang/en-US.lang +++ b/mod/etc/options_menu/themes/lang/en-US.lang @@ -8,9 +8,9 @@ REFRESH=Refresh REFRESH_ASSETS=Refresh Assets BASE_UI=Base UI BACKGROUND=Background -DEMO_CHARACTER_1_MARIO=Demo: Character #1 - Mario +DEMO_CHARACTER_1=Demo: Character #1 DEMO_CHARACTERS=Demo: Characters -DEMO_CHARACTER_2_LUIGI=Demo: Character #2 - Luigi +DEMO_CHARACTER_2=Demo: Character #2 DEMO_BACKGROUND_NEAR=Demo: Background Near DEMO_BACKGROUND_FAR=Demo: Background Far COLORS=Colors diff --git a/mod/etc/options_menu/themes/lang/es-ES.lang b/mod/etc/options_menu/themes/lang/es-ES.lang index de88f1b..b126c46 100644 --- a/mod/etc/options_menu/themes/lang/es-ES.lang +++ b/mod/etc/options_menu/themes/lang/es-ES.lang @@ -8,9 +8,9 @@ REFRESH=Actualizar REFRESH_ASSETS=Actualizar recursos BASE_UI=Interfaz base BACKGROUND=Fondo -DEMO_CHARACTER_1_MARIO=Demo: Personaje 1 - Mario +DEMO_CHARACTER_1=Demo: Personaje 1 DEMO_CHARACTERS=Demo: Personajes -DEMO_CHARACTER_2_LUIGI=Demo: Personaje 2 - Luigi +DEMO_CHARACTER_2=Demo: Personaje 2 DEMO_BACKGROUND_NEAR=Demo: Fondo cercano DEMO_BACKGROUND_FAR=Demo: Fondo lejano COLORS=Colores diff --git a/mod/etc/options_menu/themes/lang/fr-FR.lang b/mod/etc/options_menu/themes/lang/fr-FR.lang index 0cb1811..9c5c525 100644 --- a/mod/etc/options_menu/themes/lang/fr-FR.lang +++ b/mod/etc/options_menu/themes/lang/fr-FR.lang @@ -8,9 +8,9 @@ REFRESH=Actualiser REFRESH_ASSETS=Actualiser les ressources BASE_UI=Interface de base BACKGROUND=Arrière-plan -DEMO_CHARACTER_1_MARIO=Démo : Personnage #1 - Mario +DEMO_CHARACTER_1=Démo : Personnage #1 DEMO_CHARACTERS=Démo : Personnages -DEMO_CHARACTER_2_LUIGI=Démo : Personnage #2 - Luigi +DEMO_CHARACTER_2=Démo : Personnage #2 DEMO_BACKGROUND_NEAR=Démo : Arrière-plan proche DEMO_BACKGROUND_FAR=Démo : Arrière-plan éloigné COLORS=Couleurs diff --git a/mod/etc/options_menu/themes/lang/it-IT.lang b/mod/etc/options_menu/themes/lang/it-IT.lang index b6e0a9b..b72b1d9 100644 --- a/mod/etc/options_menu/themes/lang/it-IT.lang +++ b/mod/etc/options_menu/themes/lang/it-IT.lang @@ -8,9 +8,9 @@ REFRESH=Aggiorna REFRESH_ASSETS=Aggiorna risorse BASE_UI=Interfaccia base BACKGROUND=Sfondo -DEMO_CHARACTER_1_MARIO=Demo: Personaggio #1 - Mario +DEMO_CHARACTER_1=Demo: Personaggio #1 DEMO_CHARACTERS=Demo: Personaggi -DEMO_CHARACTER_2_LUIGI=Demo: Personaggio #2 - Luigi +DEMO_CHARACTER_2=Demo: Personaggio #2 DEMO_BACKGROUND_NEAR=Demo: Sfondo vicino DEMO_BACKGROUND_FAR=Demo: Sfondo lontano COLORS=Colori diff --git a/mod/etc/options_menu/themes/lang/ja-JP.lang b/mod/etc/options_menu/themes/lang/ja-JP.lang index d5b7ea9..0c0d86a 100644 --- a/mod/etc/options_menu/themes/lang/ja-JP.lang +++ b/mod/etc/options_menu/themes/lang/ja-JP.lang @@ -8,9 +8,9 @@ REFRESH=さいよみこみ REFRESH_ASSETS=Assetをさいよみこみ BASE_UI=きほんUI BACKGROUND=はいけい -DEMO_CHARACTER_1_MARIO=Demo: Character1 - Mario +DEMO_CHARACTER_1=Demo: Character1 DEMO_CHARACTERS=Demo: Characters -DEMO_CHARACTER_2_LUIGI=Demo: Character2 - Luigi +DEMO_CHARACTER_2=Demo: Character2 DEMO_BACKGROUND_NEAR=Demo: はいけい(ちかく) DEMO_BACKGROUND_FAR=Demo: はいけい(とおく) COLORS=いろ diff --git a/mod/etc/options_menu/themes/lang/nl-NL.lang b/mod/etc/options_menu/themes/lang/nl-NL.lang index b4c761e..55a2edc 100644 --- a/mod/etc/options_menu/themes/lang/nl-NL.lang +++ b/mod/etc/options_menu/themes/lang/nl-NL.lang @@ -8,9 +8,9 @@ REFRESH=Vernieuwen REFRESH_ASSETS=Assets vernieuwen BASE_UI=Basisinterface BACKGROUND=Achtergrond -DEMO_CHARACTER_1_MARIO=Demo: Personage #1 - Mario +DEMO_CHARACTER_1=Demo: Personage #1 DEMO_CHARACTERS=Demo: Personages -DEMO_CHARACTER_2_LUIGI=Demo: Personage #2 - Luigi +DEMO_CHARACTER_2=Demo: Personage #2 DEMO_BACKGROUND_NEAR=Demo: Achtergrond dichtbij DEMO_BACKGROUND_FAR=Demo: Achtergrond veraf COLORS=Kleuren diff --git a/mod/etc/options_menu/themes/lang/pt-PT.lang b/mod/etc/options_menu/themes/lang/pt-PT.lang index e9ec3f8..8128822 100644 --- a/mod/etc/options_menu/themes/lang/pt-PT.lang +++ b/mod/etc/options_menu/themes/lang/pt-PT.lang @@ -8,9 +8,9 @@ REFRESH=Atualizar REFRESH_ASSETS=Atualizar Recursos BASE_UI=Interface Base BACKGROUND=Fundo -DEMO_CHARACTER_1_MARIO=Demonstração: Personagem #1 - Mario +DEMO_CHARACTER_1=Demonstração: Personagem #1 DEMO_CHARACTERS=Demonstração: Personagens -DEMO_CHARACTER_2_LUIGI=Demonstração: Personagem #2 - Luigi +DEMO_CHARACTER_2=Demonstração: Personagem #2 DEMO_BACKGROUND_NEAR=Demonstração: Fundo Próximo DEMO_BACKGROUND_FAR=Demonstração: Fundo Distante COLORS=Cores diff --git a/mod/etc/options_menu/themes/lang/ru-RU.lang b/mod/etc/options_menu/themes/lang/ru-RU.lang index e9320bf..bdcc31e 100644 --- a/mod/etc/options_menu/themes/lang/ru-RU.lang +++ b/mod/etc/options_menu/themes/lang/ru-RU.lang @@ -8,9 +8,9 @@ REFRESH=Обновить REFRESH_ASSETS=Обновить ресурсы BASE_UI=Базовый интерфейс BACKGROUND=Фон -DEMO_CHARACTER_1_MARIO=Демо: Персонаж №1 - Марио +DEMO_CHARACTER_1=Демо: Персонаж №1 DEMO_CHARACTERS=Демо: Персонажи -DEMO_CHARACTER_2_LUIGI=Демо: Персонаж №2 - Луиджи +DEMO_CHARACTER_2=Демо: Персонаж №2 DEMO_BACKGROUND_NEAR=Демо: Ближний фон DEMO_BACKGROUND_FAR=Демо: Дальний фон COLORS=Цвета diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index dd83cb2..b2eda85 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -468,6 +468,7 @@ findDiyAsset() { asset="*.*" chars="1" preview_square="1" + preview_cols="5" ;; wall) preview_square="1" @@ -541,6 +542,9 @@ findDiyAsset() { existing_scripts="$(ls -1 "$diy_sprite/scripts" 2>/dev/null)" diy_crop_batch=0 + # chars=1 only: many themes reuse byte-identical sprites under different + # names - collapse those into a single tile instead of one per theme + diy_seen_hashes="" for p in $paths_to_search; do if [ "$(ls -A "$p")" ]; then # shellcheck disable=SC2038 @@ -589,6 +593,18 @@ findDiyAsset() { preview_image="$temp_preview" # cropped copy, queued above or already cached fi + if [ "$chars" = "1" ] && [ -f "$preview_image" ]; then + sprite_hash="$(md5sum "$preview_image" | cut -d' ' -f1)" + if printf '%s\n' "$diy_seen_hashes" | grep -qxF "$sprite_hash"; then + # identical sprite already listed under an earlier theme name - + # drop this one, and remove any tile already generated for it + rm -f "$cmd_file" "$sprite_script.sh" + continue + fi + diy_seen_hashes="$diy_seen_hashes +$sprite_hash" + fi + if ! printf '%s\n' "$existing_cmds" | grep -qxF "c0001_$asset_name" || ! printf '%s\n' "$existing_scripts" | grep -qxF "$asset_name.sh"; then touch "$cmd_file" cmd_name="$(echo "$asset_name" | sed -e 's/_/ /g' | awk '{for(i=1;i<=NF;i++){$i=toupper(substr($i,1,1))substr($i,2)}}1')" diff --git a/src/main.cpp b/src/main.cpp index a37cdf4..21bfb9e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -480,6 +480,7 @@ int main(int argc, char * argv[]) TileH = (GridBottom - GridTop - (GridRowsVisible-1)*GridGap) / GridRowsVisible; } const int GridRowPitch = TileH + GridGap; + const int ScrimH = 56; Texture gridScrollUp("^", 16, renderer, GridRight + 22, GridTop + 24, false, UiTheme::TextColor, true); gridScrollUp.rect.x -= gridScrollUp.rect.w / 2; @@ -495,6 +496,12 @@ int main(int argc, char * argv[]) std::vector chipLabels(commands.size()); std::vector tileImages(commands.size()); std::vector tileLabels(commands.size()); + // sized >1 only for a mario/luigi tile with sibling _run02/_run03/... + // frames next to _run01 - cycled in the draw loop instead of static + std::vector> tileRunFrames(commands.size()); + // true for any _run01.png tile (character sprite) - fit-centered + // instead of cover-filled, even when only one frame was found + std::vector tileIsCharacter(commands.size(), false); for(int i = 0; i < (int)commands.size(); ++i) { Command & c = commands[i]; @@ -538,9 +545,36 @@ int main(int argc, char * argv[]) SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, c.previewNearest ? "0" : "1"); Texture art(c.previewImage, renderer, 0, 0); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); - // fills the whole tile - matches the per-frame FitCover below - FitCover(art, 0, 0, TileW, TileH, 0); + + const std::string runSuffix = "_run01.png"; + bool isRunCycle = c.previewImage.size() > runSuffix.size() && + c.previewImage.compare(c.previewImage.size() - runSuffix.size(), runSuffix.size(), runSuffix) == 0; + tileIsCharacter[idx] = isRunCycle; + + if(isRunCycle) + FitCentered(art, 0, 0, TileW, TileH, 0); + else + FitCover(art, 0, 0, TileW, TileH, 0); tileImages[idx] = art; + + if(isRunCycle) + { + // probe for however many _run02/_run03/... siblings a theme + // shipped, cycled at 100ms/frame in the draw loop below + std::string base = c.previewImage.substr(0, c.previewImage.size() - runSuffix.size()) + "_run"; + std::vector frames{ art }; + for(int n = 2; n <= 8; ++n) + { + std::string framePath = base + (n < 10 ? "0" : "") + std::to_string(n) + ".png"; + if(!std::ifstream(framePath).good()) + break; + Texture frame(framePath, renderer, 0, 0); + FitCentered(frame, 0, 0, TileW, TileH, 0); + frames.push_back(frame); + } + if(frames.size() > 1) + tileRunFrames[idx] = std::move(frames); + } }; int gridTopRow = 0; @@ -731,26 +765,35 @@ int main(int argc, char * argv[]) SDL_Rect tileRect{ x, y, TileW, TileH }; DrawRoundedFillRect(renderer, tileRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); EnsureTileImage(idx); - if(tileImages[idx].rect.w > 0) + std::vector & runFrames = tileRunFrames[idx]; + Texture & tileArt = runFrames.size() > 1 + ? runFrames[(SDL_GetTicks() / 100) % runFrames.size()] + : tileImages[idx]; + if(tileArt.rect.w > 0) { - // cover-fills the whole tile (crops overflow) - // instead of letterboxing, touching all 4 edges - - // clip to the tile so the crop doesn't bleed into + // clip to the tile so art doesn't bleed into // neighboring tiles, then mask the clip's square // corners back to rounded SDL_RenderSetClipRect(renderer, &tileRect); - FitCover(tileImages[idx], x, y, TileW, TileH, 0); - tileImages[idx].Draw(renderer); - const int ScrimH = 56; - DrawBottomScrim(renderer, x, y + TileH - ScrimH, TileW, ScrimH); + if(tileIsCharacter[idx]) + // 10px margin keeps clear of the rounded-corner mask below + FitCentered(tileArt, x, y, TileW, TileH, 10); + else + FitCover(tileArt, x, y, TileW, TileH, 0); + tileArt.Draw(renderer); + if(!tileIsCharacter[idx]) + DrawBottomScrim(renderer, x, y + TileH - ScrimH, TileW, ScrimH); SDL_RenderSetClipRect(renderer, nullptr); DrawRoundedCornerMask(renderer, tileRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); } DrawStrokeRect(renderer, tileRect, selected ? UiTheme::AccentR : UiTheme::BorderR, selected ? UiTheme::AccentG : UiTheme::BorderG, selected ? UiTheme::AccentB : UiTheme::BorderB, selected ? 3 : UiTheme::BorderWidth, UiTheme::BoxRadius); - // overlaid on the scrim, left-aligned with a small margin - tileLabels[idx].rect.x = x + 14; - tileLabels[idx].rect.y = y + TileH - tileLabels[idx].rect.h - 12; - tileLabels[idx].Draw(renderer); + if(!tileIsCharacter[idx]) + { + // overlaid on the scrim, left-aligned with a small margin + tileLabels[idx].rect.x = x + 14; + tileLabels[idx].rect.y = y + TileH - tileLabels[idx].rect.h - 12; + tileLabels[idx].Draw(renderer); + } } } From 4f5b91fcc642d67ec1eb0022c6bf6c85798b1b8d Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sun, 20 Sep 2026 18:53:50 +0000 Subject: [PATCH 063/210] Fix read command in find checks to avoid unused variable warnings --- mod/etc/options_menu/themes/creator/scripts/om_diy_asset | 2 +- mod/etc/options_menu/themes/scripts/om_themescript | 2 +- mod/etc/options_menu/themes/scripts/om_vars | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset index b7e5837..70e5221 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset +++ b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset @@ -68,7 +68,7 @@ sbm) esac # skip the rescan unless $2=force or nothing's been scanned yet -if [ "$2" = "force" ] || ! find "$diy_om_sprite_path/commands" -maxdepth 1 -name 'c0001_*' 2>/dev/null | read -r; then +if [ "$2" = "force" ] || ! find "$diy_om_sprite_path/commands" -maxdepth 1 -name 'c0001_*' 2>/dev/null | read -r _; then findDiyAsset "$1" fi diff --git a/mod/etc/options_menu/themes/scripts/om_themescript b/mod/etc/options_menu/themes/scripts/om_themescript index a8327f7..fa5e50a 100644 --- a/mod/etc/options_menu/themes/scripts/om_themescript +++ b/mod/etc/options_menu/themes/scripts/om_themescript @@ -150,7 +150,7 @@ themeSelector() { FULL_THEME_PATH="$THEMES_PATH/$SELECTED_THEME" - if find "$DIY_THEME" -type f 2>/dev/null | read -r; then + if find "$DIY_THEME" -type f 2>/dev/null | read -r _; then FULL_THEME_PATH="$DIY_THEME" fi diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index b2eda85..28ac817 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -671,8 +671,8 @@ diyThemeChecker() { [ "$1" = "loader" ] && { "$omScripts/gen_splash" /tmp/om_splash.png THEME_LOADING_ASSETS && decodepng /tmp/om_splash.png > /dev/fb0; } [ ! -d "$diyThemePath" ] && mkdir -p "$diyThemePath" - # shellcheck disable=SC3061 - if find "$diyThemePath" -type f | read -r; then + + if find "$diyThemePath" -type f | read -r _; then enable_cmds "DiY" "DelDiY" "Preview" else disable_cmds "DiY" "DelDiY" "Preview" From 43179226cee49bf1658ce08a779fa185b79b8114 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sun, 20 Sep 2026 18:55:05 +0000 Subject: [PATCH 064/210] Add PREVIEW_FIT_CONTAIN and PREVIEW_HIDE_LABEL options for theme tiles --- .../options_menu/commands/c0075_ThemeOptions | 1 + .../themes/creator/commands/c0002_mario | 1 + .../themes/creator/commands/c0003_luigi | 1 + mod/etc/options_menu/themes/scripts/om_vars | 8 +- src/main.cpp | 75 +++++++++---------- 5 files changed, 44 insertions(+), 42 deletions(-) diff --git a/mod/etc/options_menu/commands/c0075_ThemeOptions b/mod/etc/options_menu/commands/c0075_ThemeOptions index d09eb9c..1e45721 100644 --- a/mod/etc/options_menu/commands/c0075_ThemeOptions +++ b/mod/etc/options_menu/commands/c0075_ThemeOptions @@ -1,5 +1,6 @@ COMMAND_NAME=THEME_OPTIONS COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE +SUBMENU=TRUE COMMAND_STR=sh %script_dir%/om_theme_selector PREVIEW_IMAGE=/etc/options_menu/images/preview_theme_options.png \ No newline at end of file diff --git a/mod/etc/options_menu/themes/creator/commands/c0002_mario b/mod/etc/options_menu/themes/creator/commands/c0002_mario index 8b8cbf7..2163c61 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0002_mario +++ b/mod/etc/options_menu/themes/creator/commands/c0002_mario @@ -3,3 +3,4 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset mario & PREVIEW_IMAGE=/etc/options_menu/images/mario.png +PREVIEW_FIT_CONTAIN=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0003_luigi b/mod/etc/options_menu/themes/creator/commands/c0003_luigi index 6b9c9e5..297fbcc 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0003_luigi +++ b/mod/etc/options_menu/themes/creator/commands/c0003_luigi @@ -3,3 +3,4 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset luigi & PREVIEW_IMAGE=/etc/options_menu/images/luigi.png +PREVIEW_FIT_CONTAIN=TRUE diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 28ac817..7833dc7 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -620,10 +620,16 @@ PREVIEW_SQUARE=TRUE" preview_cols_str="" [ -n "$preview_cols" ] && preview_cols_str=" PREVIEW_GRID_COLS=$preview_cols" + # chars=1: portrait sprite art, not a wide/square screenshot - + # fit without cropping, and skip the title label entirely + preview_fit_str="" + [ "$chars" = "1" ] && preview_fit_str=" +PREVIEW_FIT_CONTAIN=TRUE +PREVIEW_HIDE_LABEL=TRUE" echo "COMMAND_NAME=$cmd_name COMMAND_TYPE=INTERNAL COMMAND_STR=sh $sprite_script.sh -PREVIEW_IMAGE=$preview_image${preview_nearest}${preview_square_str}${preview_cols_str}" > "$cmd_file" +PREVIEW_IMAGE=$preview_image${preview_nearest}${preview_square_str}${preview_cols_str}${preview_fit_str}" > "$cmd_file" copy "$omModLicense" "$sprite_script.sh" echo "diySpriteSelector \"$full_asset_path\" \"$asset_path\" \"$asset\"" >> "$sprite_script.sh" diff --git a/src/main.cpp b/src/main.cpp index 21bfb9e..29905d2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,28 +60,6 @@ static void FitCentered(Texture & tex, int boxX, int boxY, int boxW, int boxH, i tex.rect.y = boxY + (boxH - tex.rect.h) / 2; } -// dark gradient over the tile's bottom edge, solid for the lower ~60% (where -// the overlaid label sits) and fading to transparent above that, so the -// label stays legible over any image - SDL has no gradient fill primitive, -// so this is a stack of alpha-stepped bands -static void DrawBottomScrim(SDL_Renderer * renderer, int x, int y, int w, int h) -{ - SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); - const int bands = 24; - const Uint8 maxAlpha = 235; - const double rampFrac = 0.4; // reaches maxAlpha by 40% down the scrim, solid for the rest - the label sits in that solid tail, not the fade - for(int i = 0; i < bands; ++i) - { - double t = (double)i / (bands - 1); // 0 at top of scrim, 1 at bottom - double ramp = std::min(1.0, t / rampFrac); - Uint8 alpha = static_cast(maxAlpha * ramp); - SDL_Rect band{ x, y + h * i / bands, w, h / bands + 1 }; - SDL_SetRenderDrawColor(renderer, 0, 0, 0, alpha); - SDL_RenderFillRect(renderer, &band); - } - SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); -} - // scale-to-cover-centered (crops the overflow) - pair with a clip rect at // (boxX+padding, boxY+padding, boxW-2*padding, boxH-2*padding) so the // overflow doesn't bleed past the box, and DrawRoundedCornerMask afterwards @@ -426,6 +404,9 @@ int main(int argc, char * argv[]) const bool squareTiles = themeStart < pinnedStartIndex && commands[themeStart].previewSquare; const int explicitCols = themeStart < pinnedStartIndex ? commands[themeStart].previewGridCols : -1; const int GridCols = explicitCols > 0 ? explicitCols : (squareTiles ? 7 : 4); + // a screen where every tile hides its label (PREVIEW_HIDE_LABEL) needs + // no caption row at all - checked once here since it's uniform per screen + const bool hasCaptions = themeStart < pinnedStartIndex && !commands[themeStart].previewHideLabel; const int GridGap = 20; // strip = fixed actions [0,themeStart). An empty entry stays a @@ -467,20 +448,29 @@ int main(int argc, char * argv[]) titleText.rect.y = hasStrip ? (StripTop + StripH + 22) : (UiTheme::HeaderDividerY + 22); const int GridTop = titleText.rect.y + titleText.rect.h + 20; const int GridBottom = UiTheme::FooterDividerY - 14; + // caption sits below the tile's own border, on the plain screen + // background - not overlaid on the art, so no scrim/shadow is + // needed for legibility against busy preview images. Row-to-row + // spacing can be tighter than the column gap since the caption + // itself already reads as a separator between rows. + const int LabelH = hasCaptions ? 18 : 0; + const int RowGap = hasCaptions ? 8 : GridGap; const int TileW = (GridRight - GridLeft - (GridCols-1)*GridGap) / GridCols; int TileH, GridRowsVisible; if(hasStrip) { - TileH = TileW * 9 / 16; - GridRowsVisible = std::max(1, (GridBottom - GridTop + GridGap) / (TileH + GridGap)); + // a bit shorter than a pure 16:9 crop when captions are shown, + // so 2 rows (the usual case at this width) plus their captions + // still fit the same vertical budget captions now share + TileH = hasCaptions ? TileW / 2 : TileW * 9 / 16; + GridRowsVisible = std::max(1, (GridBottom - GridTop + RowGap) / (TileH + LabelH + RowGap)); } else { GridRowsVisible = 3; - TileH = (GridBottom - GridTop - (GridRowsVisible-1)*GridGap) / GridRowsVisible; + TileH = (GridBottom - GridTop - (GridRowsVisible-1)*RowGap - GridRowsVisible*LabelH) / GridRowsVisible; } - const int GridRowPitch = TileH + GridGap; - const int ScrimH = 56; + const int GridRowPitch = TileH + LabelH + RowGap; Texture gridScrollUp("^", 16, renderer, GridRight + 22, GridTop + 24, false, UiTheme::TextColor, true); gridScrollUp.rect.x -= gridScrollUp.rect.w / 2; @@ -496,12 +486,11 @@ int main(int argc, char * argv[]) std::vector chipLabels(commands.size()); std::vector tileImages(commands.size()); std::vector tileLabels(commands.size()); - // sized >1 only for a mario/luigi tile with sibling _run02/_run03/... - // frames next to _run01 - cycled in the draw loop instead of static + // sized >1 only for a _run01.png tile with sibling _run02/_run03/... + // frames found alongside it - cycled in the draw loop instead of static std::vector> tileRunFrames(commands.size()); - // true for any _run01.png tile (character sprite) - fit-centered - // instead of cover-filled, even when only one frame was found - std::vector tileIsCharacter(commands.size(), false); + std::vector tileHideLabel(commands.size(), false); // PREVIEW_HIDE_LABEL + std::vector tileFitContain(commands.size(), false); // PREVIEW_FIT_CONTAIN for(int i = 0; i < (int)commands.size(); ++i) { Command & c = commands[i]; @@ -546,12 +535,17 @@ int main(int argc, char * argv[]) Texture art(c.previewImage, renderer, 0, 0); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + tileHideLabel[idx] = c.previewHideLabel; + tileFitContain[idx] = c.previewFitContain; + + // run-cycle animation only applies to a _run01.png sprite - this + // is a naming convention to discover however many sibling frames + // a theme shipped, not what drives previewFitContain/HideLabel const std::string runSuffix = "_run01.png"; bool isRunCycle = c.previewImage.size() > runSuffix.size() && c.previewImage.compare(c.previewImage.size() - runSuffix.size(), runSuffix.size(), runSuffix) == 0; - tileIsCharacter[idx] = isRunCycle; - if(isRunCycle) + if(tileFitContain[idx]) FitCentered(art, 0, 0, TileW, TileH, 0); else FitCover(art, 0, 0, TileW, TileH, 0); @@ -775,23 +769,22 @@ int main(int argc, char * argv[]) // neighboring tiles, then mask the clip's square // corners back to rounded SDL_RenderSetClipRect(renderer, &tileRect); - if(tileIsCharacter[idx]) + if(tileFitContain[idx]) // 10px margin keeps clear of the rounded-corner mask below FitCentered(tileArt, x, y, TileW, TileH, 10); else FitCover(tileArt, x, y, TileW, TileH, 0); tileArt.Draw(renderer); - if(!tileIsCharacter[idx]) - DrawBottomScrim(renderer, x, y + TileH - ScrimH, TileW, ScrimH); SDL_RenderSetClipRect(renderer, nullptr); DrawRoundedCornerMask(renderer, tileRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); } DrawStrokeRect(renderer, tileRect, selected ? UiTheme::AccentR : UiTheme::BorderR, selected ? UiTheme::AccentG : UiTheme::BorderG, selected ? UiTheme::AccentB : UiTheme::BorderB, selected ? 3 : UiTheme::BorderWidth, UiTheme::BoxRadius); - if(!tileIsCharacter[idx]) + if(!tileHideLabel[idx]) { - // overlaid on the scrim, left-aligned with a small margin - tileLabels[idx].rect.x = x + 14; - tileLabels[idx].rect.y = y + TileH - tileLabels[idx].rect.h - 12; + // caption below the tile's own border, centered - + // on the plain screen background, not the art + tileLabels[idx].rect.x = x + (TileW - tileLabels[idx].rect.w) / 2; + tileLabels[idx].rect.y = y + TileH + (LabelH - tileLabels[idx].rect.h) / 2; tileLabels[idx].Draw(renderer); } } From bca03f689267c6644168e65259e1326f4b85b980 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sun, 20 Sep 2026 18:55:14 +0000 Subject: [PATCH 065/210] Update subproject commit reference in OptionsMenu --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 6c3d40e..565e091 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 6c3d40e0801189ad967178751d60279340033844 +Subproject commit 565e0919e7525e729da776af659ccebc9b2cd23b From b3d35b5bea8ef6cd08523df46ccffb1acba52201 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sun, 20 Sep 2026 23:13:49 +0000 Subject: [PATCH 066/210] Update theme_manager references to use the lib directory --- .gitignore | 2 +- mod/etc/options_menu/scripts/om_themeselector | 2 +- .../themes/creator/scripts/om_applytheme | 2 +- .../themes/creator/scripts/om_diy_asset | 2 +- .../themes/creator/scripts/om_removediy | 2 +- .../creator/sprites/colors/scripts/om_return | 2 +- .../sprites/colors_nes/scripts/om_return | 2 +- .../creator/sprites/luigi/scripts/om_return | 2 +- .../creator/sprites/mario/scripts/om_return | 2 +- .../creator/sprites/packed/scripts/om_return | 2 +- .../sprites/packed_nes/scripts/om_return | 2 +- .../creator/sprites/pole/scripts/om_return | 2 +- .../creator/sprites/sbm/scripts/om_return | 2 +- .../creator/sprites/wall/scripts/om_return | 2 +- .../sprites/wall_far/scripts/om_return | 2 +- .../sprites/wall_near/scripts/om_return | 2 +- .../downloads/scripts/om_clearDownloads | 2 +- .../themes/downloads/scripts/om_refreshThemes | 2 +- .../themes/downloads/scripts/om_return | 2 +- .../options_menu/themes/scripts/om_clearList | 2 +- .../options_menu/themes/scripts/om_getThemes | 2 +- .../options_menu/themes/scripts/om_settings | 2 +- .../themes/scripts/om_themeCreator | 2 +- .../themes/scripts/om_themescript | 29 ++++++++++--------- mod/etc/options_menu/themes/scripts/om_vars | 2 +- .../themes/settings/scripts/om_audio | 2 +- .../themes/settings/scripts/om_reset | 2 +- .../themes/settings/scripts/om_systemcleanup | 2 +- .../themes/settings/scripts/om_theme | 2 +- mod/install | 2 +- mod/uninstall | 2 +- 31 files changed, 45 insertions(+), 44 deletions(-) diff --git a/.gitignore b/.gitignore index 934b00d..704549b 100644 --- a/.gitignore +++ b/.gitignore @@ -555,5 +555,5 @@ tools/*.py tools/MaterialSymbolsOutlined.codepoints tools/MaterialSymbolsOutlined.ttf theme_downloader/theme_downloader -mod/etc/options_menu/themes/theme_manager theme_downloader/cacert.pem +mod/etc/options_menu/lib/ diff --git a/mod/etc/options_menu/scripts/om_themeselector b/mod/etc/options_menu/scripts/om_themeselector index c1c3a6e..a86ca51 100644 --- a/mod/etc/options_menu/scripts/om_themeselector +++ b/mod/etc/options_menu/scripts/om_themeselector @@ -33,4 +33,4 @@ else loadThemeList fi -$optionsMenu/themes/theme_manager --title INSTALLED_THEMES --layout grid & +$optionsMenu/lib/theme_manager --title INSTALLED_THEMES --layout grid & diff --git a/mod/etc/options_menu/themes/creator/scripts/om_applytheme b/mod/etc/options_menu/themes/creator/scripts/om_applytheme index 11ec34f..3777d99 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_applytheme +++ b/mod/etc/options_menu/themes/creator/scripts/om_applytheme @@ -41,4 +41,4 @@ remove "$omDummy" loadThemeList -r -$optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" --layout grid & +$optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" --layout grid & diff --git a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset index 70e5221..26315f2 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset +++ b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset @@ -72,4 +72,4 @@ if [ "$2" = "force" ] || ! find "$diy_om_sprite_path/commands" -maxdepth 1 -name findDiyAsset "$1" fi -$optionsMenu/themes/theme_manager --commandPath $diy_om_sprite_path/commands/ --scriptPath $diy_om_sprite_path/scripts --title $diy_title --layout grid & +$optionsMenu/lib/theme_manager --commandPath $diy_om_sprite_path/commands/ --scriptPath $diy_om_sprite_path/scripts --title $diy_title --layout grid & diff --git a/mod/etc/options_menu/themes/creator/scripts/om_removediy b/mod/etc/options_menu/themes/creator/scripts/om_removediy index 3397cd6..670620f 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_removediy +++ b/mod/etc/options_menu/themes/creator/scripts/om_removediy @@ -22,4 +22,4 @@ script_init diyThemeRemover -$optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" --layout grid & +$optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return index e5f4656..836c862 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return index e5f4656..836c862 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return index e5f4656..836c862 100644 --- a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return index 1d1a989..613a278 100644 --- a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return index 1d1a989..613a278 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return index 1d1a989..613a278 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return index 1d1a989..613a278 100644 --- a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return index 1d1a989..613a278 100644 --- a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return index 1d1a989..613a278 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return index 1d1a989..613a278 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return index 1d1a989..613a278 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads index 0928629..116f446 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads +++ b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads @@ -23,4 +23,4 @@ script_init clearDownloads remove "$subDummy" -$optionsMenu/themes/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES --layout grid & +$optionsMenu/lib/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES --layout grid & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes index 7d64fd4..805c1da 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes +++ b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes @@ -22,4 +22,4 @@ script_init getThemeList -$optionsMenu/themes/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES --layout grid & +$optionsMenu/lib/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES --layout grid & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_return b/mod/etc/options_menu/themes/downloads/scripts/om_return index e3b4d71..f92929b 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_return +++ b/mod/etc/options_menu/themes/downloads/scripts/om_return @@ -29,4 +29,4 @@ else clearList fi -usleep 50000 && $optionsMenu/themes/theme_manager --title INSTALLED_THEMES --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --title INSTALLED_THEMES --layout grid & diff --git a/mod/etc/options_menu/themes/scripts/om_clearList b/mod/etc/options_menu/themes/scripts/om_clearList index 3288585..f85fdfd 100644 --- a/mod/etc/options_menu/themes/scripts/om_clearList +++ b/mod/etc/options_menu/themes/scripts/om_clearList @@ -24,4 +24,4 @@ clearList disable_cmds "Clear" enable_cmds "Refresh" -$optionsMenu/themes/theme_manager --title INSTALLED_THEMES --layout grid & +$optionsMenu/lib/theme_manager --title INSTALLED_THEMES --layout grid & diff --git a/mod/etc/options_menu/themes/scripts/om_getThemes b/mod/etc/options_menu/themes/scripts/om_getThemes index 4f7bbf8..68211e3 100644 --- a/mod/etc/options_menu/themes/scripts/om_getThemes +++ b/mod/etc/options_menu/themes/scripts/om_getThemes @@ -22,4 +22,4 @@ script_init [ ! -f "$subDummy" ] && getThemeList -$optionsMenu/themes/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES --layout grid & +$optionsMenu/lib/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES --layout grid & diff --git a/mod/etc/options_menu/themes/scripts/om_settings b/mod/etc/options_menu/themes/scripts/om_settings index 2d53b76..52bb365 100644 --- a/mod/etc/options_menu/themes/scripts/om_settings +++ b/mod/etc/options_menu/themes/scripts/om_settings @@ -37,4 +37,4 @@ else [ ! -f "$enableSystemCleanUp" ] && remove "$omModSettingsSpacer" fi -$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & +$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & diff --git a/mod/etc/options_menu/themes/scripts/om_themeCreator b/mod/etc/options_menu/themes/scripts/om_themeCreator index 2e01ce2..4797b6f 100644 --- a/mod/etc/options_menu/themes/scripts/om_themeCreator +++ b/mod/etc/options_menu/themes/scripts/om_themeCreator @@ -22,4 +22,4 @@ script_init diyThemeChecker -$optionsMenu/themes/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title DIY_THEME --layout grid & +$optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title DIY_THEME --layout grid & diff --git a/mod/etc/options_menu/themes/scripts/om_themescript b/mod/etc/options_menu/themes/scripts/om_themescript index fa5e50a..e6b1173 100644 --- a/mod/etc/options_menu/themes/scripts/om_themescript +++ b/mod/etc/options_menu/themes/scripts/om_themescript @@ -42,15 +42,18 @@ musicHackUnbind() { } fontBindFix() { - [ "$1" == "backup" ] && FONT_TYPE="title s m l small medium large" - [ "$1" == "restore" ] && FONT_TYPE="s m l small medium large" - [ "$1" == "restore" ] && [ -f "$TITLE_FONT_BACKUP" ] && mv "$TITLE_FONT_BACKUP" "$TITLE_FONT" - for FONT in $FONT_TYPE; do - [ "$1" == "backup" ] && [ ! -f "$TITLE_FONT_BACKUP" ] && mv "${FONT_PATH}/${FONT}.fnt" "${FONT_PATH}/${FONT}.fnt.backup" - [ "$1" == "backup" ] && cp "$FULL_THEME_PATH/fonts/${FONT}.fnt" "${FONT_PATH}/${FONT}.fnt" - [ "$1" == "restore" ] && umount "${ORIGINAL_THEME}/resources/fonts/${UI1}/${FONT}.fnt" - [ "$1" == "restore" ] && rm "${FONT_PATH}/${FONT}.fnt" - done + if [ "$1" == "backup" ]; then + for FONT in title s m l small medium large; do + [ ! -f "$TITLE_FONT_BACKUP" ] && mv "${FONT_PATH}/${FONT}.fnt" "${FONT_PATH}/${FONT}.fnt.backup" + cp "$FULL_THEME_PATH/fonts/${FONT}.fnt" "${FONT_PATH}/${FONT}.fnt" + done + elif [ "$1" == "restore" ]; then + [ -f "$TITLE_FONT_BACKUP" ] && mv "$TITLE_FONT_BACKUP" "$TITLE_FONT" + for FONT in s m l small medium large; do + umount "${ORIGINAL_THEME}/resources/fonts/${UI1}/${FONT}.fnt" + rm "${FONT_PATH}/${FONT}.fnt" + done + fi } themeSelector() { @@ -62,6 +65,7 @@ themeSelector() { local PARENT_THEME_TITLE local PREVIOUS_THEME local THEME_MATCH + local PARENT_DESKTOP_FILE local UI0="ui/$sftype-$sfregion" local UI1="" local SYSTEM_TYPE="$sftype" @@ -122,11 +126,8 @@ themeSelector() { break fi done - find "." -maxdepth 3 -type f -name "$FULL_PARENT_ID.desktop" -exec grep -L -e "Name=Back" -e "Name=Home" {} \; >tmp - while IFS= read -r w; do - PARENT_THEME_TITLE="$(grep -F 'Name=' "$w" | sed -r 's/Name=//g;s/ /_/g' | awk '{print tolower($0)}')" - done "$cmd_list" +DELETE_STR=rm -rf \"$full_path\" \"$full_path.png\" \"$cmd_list\" \"$thm_script.sh\" && usleep 50000 && %options_path%lib/theme_manager --title INSTALLED_THEMES --layout grid &" > "$cmd_list" done } diff --git a/mod/etc/options_menu/themes/settings/scripts/om_audio b/mod/etc/options_menu/themes/settings/scripts/om_audio index 4781072..0e6c243 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_audio +++ b/mod/etc/options_menu/themes/settings/scripts/om_audio @@ -21,4 +21,4 @@ script_init audioSettings "$1" -$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & +$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & diff --git a/mod/etc/options_menu/themes/settings/scripts/om_reset b/mod/etc/options_menu/themes/settings/scripts/om_reset index beb5a72..7b1bc8f 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_reset +++ b/mod/etc/options_menu/themes/settings/scripts/om_reset @@ -23,4 +23,4 @@ script_init remove "$b_file" "$p_file" hakchi reboot -$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & +$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & diff --git a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup index bf11808..cb12131 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup +++ b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup @@ -25,4 +25,4 @@ modCleanUp usleep 1000 -$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & +$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & diff --git a/mod/etc/options_menu/themes/settings/scripts/om_theme b/mod/etc/options_menu/themes/settings/scripts/om_theme index aa87b2b..d4f5503 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_theme +++ b/mod/etc/options_menu/themes/settings/scripts/om_theme @@ -22,4 +22,4 @@ script_init themeSettings "$1" -$optionsMenu/themes/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & +$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & diff --git a/mod/install b/mod/install index 05384cb..81b73b9 100644 --- a/mod/install +++ b/mod/install @@ -70,7 +70,7 @@ copy "$om_themes_scripts/om_chmenu" "$rootfs/bin/chmenu" chmod +x "$rootfs/bin/chmenu" chmod +x "$usr/bin/sprite_crop" chmod +x "$usr/bin/theme_downloader" -chmod +x "$rootfs/etc/options_menu/themes/theme_manager" +chmod +x "$rootfs/etc/options_menu/lib/theme_manager" sed '1,2d' "$om_themes_scripts/om_themescript" >"$b_file" [ ! -f "$p_file" ] && echo themeSelector >"$p_file" diff --git a/mod/uninstall b/mod/uninstall index d4964ca..904badc 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -104,7 +104,7 @@ remove "$rootfs/etc/options_menu/images/wall_far.png" remove "$rootfs/etc/usr/bin/pngpack-upx" remove "$rootfs/etc/usr/bin/sprite_crop" remove "$rootfs/etc/usr/bin/theme_downloader" -remove "$rootfs/etc/options_menu/themes/theme_manager" +remove "$rootfs/etc/options_menu/lib/theme_manager" [ -f "$TITLE_FONT_BACKUP" ] && mv "$TITLE_FONT_BACKUP" "$TITLE_FONT" for FONT in s m l small medium large; do From 90ae9df7d575927b5bbd599d2e057309f2e64e6a Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sun, 20 Sep 2026 23:28:58 +0000 Subject: [PATCH 067/210] Fix comments in .gitlab-ci.yml for clarity on frozen base-OS files --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8a076c8..df47b70 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,8 +3,8 @@ stages: - build # om_chmenu_og / om_fontfix_og are frozen copies of base-OS files (used only -# by mod/uninstall to restore the original chmenu/fontfix) - they're not -# authored in our style and aren't meant to pass our lint conventions. +# by mod/uninstall to restore the original chmenu/fontfix) - I didn't author +# them in my style and don't expect them to pass my lint conventions. .script_files: &list_script_files | files=$( { echo mod/install; echo mod/uninstall; find mod -type f -name 'om_*'; } | From 313f7b63f6c25cc9e482294e9bec8bec5276044b Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sun, 20 Sep 2026 23:32:32 +0000 Subject: [PATCH 068/210] Update subproject commit reference in toolchain --- toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolchain b/toolchain index 45e8452..db3c8d9 160000 --- a/toolchain +++ b/toolchain @@ -1 +1 @@ -Subproject commit 45e8452a11d83cd09192a6c2e523e69706a836e5 +Subproject commit db3c8d9043ebb48f37a04d9c4a07387a03d8f8af From 29555453b6985b37c077def66702a6b28531b2c3 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sun, 20 Sep 2026 23:40:52 +0000 Subject: [PATCH 069/210] Update subproject commit reference in toolchain --- toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolchain b/toolchain index db3c8d9..8d9428e 160000 --- a/toolchain +++ b/toolchain @@ -1 +1 @@ -Subproject commit db3c8d9043ebb48f37a04d9c4a07387a03d8f8af +Subproject commit 8d9428e2a2e69cb33987390587f7d1d134e0316a From 7c63778be139867a2831abfd978c229053ca3753 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sun, 20 Sep 2026 23:42:52 +0000 Subject: [PATCH 070/210] Update subproject commit reference in OptionsMenu --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 565e091..89ab978 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 565e0919e7525e729da776af659ccebc9b2cd23b +Subproject commit 89ab97828a5add13c97aaabb41813325d2aeafb8 From 9277955a24b9657e7427a261e8bcb965a60f93fe Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sun, 20 Sep 2026 23:57:19 +0000 Subject: [PATCH 071/210] Add theme_downloader implementation to src/ and remove Makefile --- {theme_downloader => src}/theme_downloader.c | 0 theme_downloader/Makefile | 39 -------------------- 2 files changed, 39 deletions(-) rename {theme_downloader => src}/theme_downloader.c (100%) delete mode 100644 theme_downloader/Makefile diff --git a/theme_downloader/theme_downloader.c b/src/theme_downloader.c similarity index 100% rename from theme_downloader/theme_downloader.c rename to src/theme_downloader.c diff --git a/theme_downloader/Makefile b/theme_downloader/Makefile deleted file mode 100644 index 6aeef5d..0000000 --- a/theme_downloader/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -# Cross-compiles theme_downloader against the static libcurl+OpenSSL built -# into the toolchain image (toolchain/Dockerfile.jessie-armhf). Run via -# ../build.sh theme_downloader (from the repo root, same Docker toolchain -# as the theme_manager build), not directly - CROSS_PREFIX must point at -# the arm-linux-gnueabihf- cross tools. -CURL_PREFIX = /opt/curl-static -CC = gcc -STRIP = strip -CFLAGS = -O2 -Wall -I$(CURL_PREFIX)/include -# NOT -static: glibc's getaddrinfo needs libnss_dns.so.2, loaded at runtime -# via dlopen - a fully static binary can't do that and fails DNS resolution -# instantly. curl/ssl/crypto/zlib link statically anyway since only .a -# versions of them exist in this toolchain; libc/libdl/libpthread/libresolv -# stay dynamic, resolved against the console's own libc at runtime (same as -# theme_manager/sprite_crop already do for SDL2/libpng). -LDLIBS = -L$(CURL_PREFIX)/lib -L/usr/lib/arm-linux-gnueabihf -lcurl -lssl -lcrypto -lz -ldl -lpthread - -all: theme_downloader - -# gitignored (not source we own) - fetched here so a fresh clone can still -# build instead of failing on a missing file -cacert.pem: - curl -fL -o cacert.pem https://curl.se/ca/cacert.pem - -# ld -r -b binary embeds cacert.pem verbatim as a linkable object, exposing -# _binary_cacert_pem_start/_end - avoids generating a multi-thousand-line -# .c array just to get the CA bundle into the binary. -ca_bundle.o: cacert.pem - $(CROSS_PREFIX)ld -r -b binary -o ca_bundle.o cacert.pem - -theme_downloader: theme_downloader.c ca_bundle.o - $(CROSS_PREFIX)$(CC) $(CFLAGS) theme_downloader.c ca_bundle.o $(LDLIBS) -o theme_downloader - $(CROSS_PREFIX)$(STRIP) theme_downloader - upx --best theme_downloader - -clean: - rm -f theme_downloader ca_bundle.o - -.PHONY: all clean From 335082f7ba31f601179245ced30cc6c4e692b267 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sun, 20 Sep 2026 23:58:01 +0000 Subject: [PATCH 072/210] Update .gitignore to include src/cacert.pem and modify .gitmodules for toolchain URL --- .gitignore | 3 +-- .gitmodules | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 704549b..2100167 100644 --- a/.gitignore +++ b/.gitignore @@ -554,6 +554,5 @@ _console_originals/ tools/*.py tools/MaterialSymbolsOutlined.codepoints tools/MaterialSymbolsOutlined.ttf -theme_downloader/theme_downloader -theme_downloader/cacert.pem +src/cacert.pem mod/etc/options_menu/lib/ diff --git a/.gitmodules b/.gitmodules index 8707366..cf7307c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "toolchain"] path = toolchain - url = https://github.com/DefKorns/hakchi-toolchain.git + url = https://github.com/DefKorns/classicmini-cross-toolchain.git [submodule "pngpack"] path = pngpack url = https://github.com/DefKorns/pngpack.git From a6bb9f111e208e09db2260610bd419b5a32652a6 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sun, 20 Sep 2026 23:58:10 +0000 Subject: [PATCH 073/210] Update subproject commit reference in OptionsMenu --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 89ab978..d291937 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 89ab97828a5add13c97aaabb41813325d2aeafb8 +Subproject commit d291937799bcbcf89b899a4e52f77bf2415e8cd4 From 5d8993ecac9520e23729d80e3b4ac9a9f70f1732 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Sun, 20 Sep 2026 23:59:08 +0000 Subject: [PATCH 074/210] Fix theme manager command path and add debug checkpoints in main function --- src/main.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 29905d2..4dbf9db 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -269,7 +270,7 @@ int main(int argc, char * argv[]) if(returnScript.good()) back.command = "sh " + scriptLocation + "om_return &"; else - back.command = "usleep 50000 && " + optionsLocation + "themes/theme_manager --title INSTALLED_THEMES --layout grid &"; + back.command = "usleep 50000 && " + optionsLocation + "lib/theme_manager --title INSTALLED_THEMES --layout grid &"; commands.push_back(back); isThemeItem.push_back(false); } @@ -292,10 +293,13 @@ int main(int argc, char * argv[]) ++themeStart; int currentCommandId = 0; + fprintf(stderr, "CHECKPOINT 1: commands loaded, count=%zu\n", commands.size()); fflush(stderr); SDL_Context sdl_context(std::chrono::milliseconds(33), false); + fprintf(stderr, "CHECKPOINT 2: sdl_context created\n"); fflush(stderr); auto renderer = sdl_context.renderer; Controller controller(1); + fprintf(stderr, "CHECKPOINT 3: controller created\n"); fflush(stderr); const Uint8 bgR = UiTheme::BgR; const Uint8 bgG = UiTheme::BgG; @@ -312,6 +316,7 @@ int main(int argc, char * argv[]) appVersionText.rect.y -= appVersionText.rect.h / 2; Texture titleText(Translate(titleKey), UiTheme::SectionTitleFontSize, renderer, UiTheme::SectionTitleX, UiTheme::SectionTitleY, false, UiTheme::TextColor, true); Texture creditText("Theme Manager - by DefKorns", 16, renderer, UiTheme::CreditX, UiTheme::CreditY, false, UiTheme::TextColor, true); + fprintf(stderr, "CHECKPOINT 4: basic textures created\n"); fflush(stderr); struct Badge { Texture letter; Texture label; UiTheme::BadgeColor rim; UiTheme::BadgeColor fill; }; Badge badgeA{ Texture("A", 16, renderer, 0, 0, false, UiTheme::BadgeLetterColor, true), Texture(Translate("HINT_SELECT"), 16, renderer, 0, 0, false, UiTheme::TextColor, true), UiTheme::BadgeADark, UiTheme::BadgeA }; From ffbaffbe80f98a01b9bb87d8720232f2eb3a21f6 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Mon, 21 Sep 2026 00:19:46 +0000 Subject: [PATCH 075/210] Refactor Makefile and build scripts for improved theme manager compilation and packaging --- Makefile | 134 +++++++++++++++++++++++++-------------- Makefile.docker | 139 +++++++++++++++++++++++++++++++++++------ build.sh | 4 +- src/main.cpp | 21 ++----- src/theme_downloader.c | 12 +--- 5 files changed, 217 insertions(+), 93 deletions(-) diff --git a/Makefile b/Makefile index 00f4cf1..2dfcf5d 100644 --- a/Makefile +++ b/Makefile @@ -1,44 +1,97 @@ SHELL = /bin/sh -UNAME = $(shell uname) +UNAME := $(shell uname) MOD_NAME := Options Menu - Theme Selector MOD_CREATOR := DefKorns MOD_CATEGORY := User Interface -LAST_TAG_COMMIT = $(shell git rev-list --tags --max-count=1) -LAST_TAG = $(shell git describe --tags $(LAST_TAG_COMMIT) ) -TAG_PREFIX = "v" -CURRENT_BRANCH = $(shell git rev-parse --abbrev-ref HEAD) -GIT_REMOTES = $(shell git remote | xargs echo ) -GIT_DIRTY = $(shell git diff --shortstat 2> /dev/null | tail -n1 ) -GET_VER = $(shell git describe --tags $(LAST_TAG_COMMIT) | sed "s/^$(TAG_PREFIX)//") -#MOD_VER = $(shell [ -f VERSION ] && head VERSION || echo "0.0.1") -MOD_VER = $(shell [ -f VERSION ] && head VERSION || echo $(GET_VER)) -MAJOR = $(shell echo $(MOD_VER) | sed "s/^\([0-9]*\).*/\1/") -MINOR = $(shell echo $(MOD_VER) | sed "s/[0-9]*\.\([0-9]*\).*/\1/") -PATCH = $(shell echo $(MOD_VER) | sed "s/[0-9]*\.[0-9]*\.\([0-9]*\).*/\1/") -RC = $(shell echo $(MOD_VER) | grep -oP '(?<=rc)[0-9]+' || echo 0) +LAST_TAG_COMMIT := $(shell git rev-list --tags --max-count=1) +LAST_TAG := $(shell git describe --tags $(LAST_TAG_COMMIT) 2> /dev/null || echo "v0.0.0") +TAG_PREFIX := "v" +CURRENT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD) +GIT_REMOTES := $(shell git remote | xargs echo ) +GIT_DIRTY := $(shell git diff --shortstat 2> /dev/null | tail -n1 ) +GET_VER := $(shell git describe --tags $(LAST_TAG_COMMIT) 2> /dev/null | sed "s/^$(TAG_PREFIX)//" || echo "0.0.0") +MOD_VER := $(shell [ -f VERSION ] && head VERSION || echo $(GET_VER)) +MAJOR := $(shell echo $(MOD_VER) | sed "s/^\([0-9]*\).*/\1/") +MINOR := $(shell echo $(MOD_VER) | sed "s/[0-9]*\.\([0-9]*\).*/\1/") +PATCH := $(shell echo $(MOD_VER) | sed "s/[0-9]*\.[0-9]*\.\([0-9]*\).*/\1/") +RC := $(shell echo $(MOD_VER) | sed -n "s/.*rc\([0-9]*\)$$/\1/p") +RC := $(if $(RC),$(RC),0) # total number of commits -BUILD = $(shell git log --oneline | wc -l | sed -e "s/[ \t]*//g") -NEXT_MAJOR_VERSION = $(shell expr $(MAJOR) + 1).0.0 -NEXT_MINOR_VERSION = $(MAJOR).$(shell expr $(MINOR) + 1).0-b$(BUILD) -NEXT_PATCH_VERSION = $(MAJOR).$(MINOR).$(shell expr $(PATCH) + 1)-b$(BUILD) -NEXT_RC_VERSION = $(MAJOR).$(MINOR).$(PATCH)-rc$(shell expr $(RC) + 1) +BUILD := $(shell git log --oneline | wc -l | sed -e "s/[ \t]*//g") +NEXT_MAJOR_VERSION := $(shell expr $(MAJOR) + 1).0.0 +NEXT_MINOR_VERSION := $(MAJOR).$(shell expr $(MINOR) + 1).0-b$(BUILD) +NEXT_PATCH_VERSION := $(MAJOR).$(MINOR).$(shell expr $(PATCH) + 1)-b$(BUILD) +NEXT_RC_VERSION := $(MAJOR).$(MINOR).$(PATCH)-rc$(shell expr $(RC) + 1) -MOD_URL=`git config --get remote.origin.url` +MOD_URL := $(shell git config --get remote.origin.url) GIT_COMMIT := $(shell git rev-parse --short HEAD)$(shell git diff-index --quiet HEAD -- || echo -dirty) -RSYNC = $(shell rsync -a --exclude-from=exclude-file.txt mod/etc/options_menu/ temp/ --links --delete) -MOD_FILENAME = $(shell basename `pwd`) -DEV_DIR = $(shell realpath .) -#DEV_DIR=/d/om_theme-selectorv2 -# DEV_DIR=/i/om_theme-selectorv2 -OUT=$(DEV_DIR)/out - - -all: hmod tar zip - rm -r temp/ - -deploy: customlang localization patches upload +# Derive the package name from the remote; Docker mounts every repo at /src. +MOD_FILENAME := $(shell basename $(MOD_URL) .git) +DEV_DIR := $(CURDIR) +OUT := $(DEV_DIR)/out + +# Build without Docker when the ARM toolchain and dependencies are installed. +# Set CROSS_PREFIX=arm-linux-gnueabihf- to cross-compile for the console; +# leave unset to build natively. +FRAMEWORK_DIR = vendor/OptionsMenu/src/framework +CXX = g++ +STRIP = strip +ifdef CROSS_PREFIX +PKG_CONFIG_LIBDIR = /usr/lib/arm-linux-gnueabihf/pkgconfig +SDL_CFLAGS = -I/usr/include/arm-linux-gnueabihf $(shell PKG_CONFIG_LIBDIR=$(PKG_CONFIG_LIBDIR) pkg-config --cflags sdl2 SDL2_ttf libpng) +SDL_LIBS = $(shell PKG_CONFIG_LIBDIR=$(PKG_CONFIG_LIBDIR) pkg-config --libs sdl2 SDL2_ttf libpng) +LDFLAGS = -Wl,--allow-shlib-undefined +else +SDL_CFLAGS = $(shell sdl2-config --cflags) $(shell pkg-config --cflags SDL2_ttf) +SDL_LIBS = $(shell sdl2-config --libs) $(shell pkg-config --libs SDL2_ttf) -lpng +LDFLAGS = +endif +CXXFLAGS = -std=c++11 -Os -Ivendor/OptionsMenu/src $(SDL_CFLAGS) +LDLIBS = $(SDL_LIBS) +VENDOR_SRC_DIR = vendor/OptionsMenu/src +SOURCES = src/main.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp +OBJECTS = $(SOURCES:.cpp=.o) + +# Build theme_downloader with the Docker toolchain's static curl+OpenSSL. +# No native fallback is available because the libraries live at CURL_PREFIX. +CC = gcc +CURL_PREFIX = /opt/curl-static +CFLAGS = -O2 -Wall -I$(CURL_PREFIX)/include +CURL_LDLIBS = -L$(CURL_PREFIX)/lib -L/usr/lib/arm-linux-gnueabihf -lcurl -lssl -lcrypto -lz -ldl -lpthread + +# `all`/`hmod` package mod/ without compiling for toolchain-free CI. +# Use `full` to build the binaries from source first, then package. +# Avoid "build": GNU Make's .sh rule conflicts with build.sh. +all: hmod + +full: compile hmod + +compile: mod/etc/options_menu/lib/theme_manager mod/bin/theme_downloader + +mod/etc/options_menu/lib/theme_manager: $(OBJECTS) + $(CROSS_PREFIX)$(CXX) $(OBJECTS) $(LDLIBS) $(LDFLAGS) -Wl,-rpath,/etc/options_menu/lib -o mod/etc/options_menu/lib/theme_manager + $(CROSS_PREFIX)$(STRIP) mod/etc/options_menu/lib/theme_manager + +%.o: %.cpp + $(CROSS_PREFIX)$(CXX) $(CXXFLAGS) -c $< -o $@ + +%.o: %.c + $(CROSS_PREFIX)$(CC) $(CFLAGS) -c $< -o $@ + +# Fetch the gitignored CA bundle for fresh builds. +src/cacert.pem: + curl -fL -o src/cacert.pem https://curl.se/ca/cacert.pem + +# Embed cacert.pem from src/ to keep "src/" out of the symbol names. +src/ca_bundle.o: src/cacert.pem + cd src && $(CROSS_PREFIX)ld -r -b binary -o ca_bundle.o cacert.pem + +mod/bin/theme_downloader: src/theme_downloader.o src/ca_bundle.o + $(CROSS_PREFIX)$(CC) src/theme_downloader.o src/ca_bundle.o $(CURL_LDLIBS) -o mod/bin/theme_downloader + $(CROSS_PREFIX)$(STRIP) mod/bin/theme_downloader + upx --best mod/bin/theme_downloader hmod: clean @@ -59,17 +112,6 @@ hmod: clean cd temp/; tar -czf $(OUT)/$(MOD_FILENAME)-$(MOD_VER).hmod * rm -r temp/ - -tar: - mkdir -p out/ temp/ - $(RSYNC) - cd temp/; tar -czf $(OUT)/$(MOD_FILENAME)-$(MOD_VER).tar.gz * - rm -r temp/ - -zip: - mkdir -p out/ temp/ - #$(RSYNC) - cd temp/; zip -r $(OUT)/$(MOD_FILENAME)-$(MOD_VER).zip * fix: hmod @ver="$(NEXT_PATCH_VERSION)" && \ @@ -99,10 +141,6 @@ upgrade: all git add VERSION && \ git commit -m "Bump version to v$$ver" -upload: - rm -f $(OUT)/$(MOD_FILENAME).* - rsync -e ssh --progress --exclude 'rsync*' --exclude 'src' -avzzp out/* defkorns@hakchicloud.com:/var/www/html/Hakchi_Themes/options_menu - info: @echo "Mod Dir: $(MOD_FILENAME)" @echo "Current version: $(MOD_VER)" @@ -120,4 +158,4 @@ clean: rm -rf out/ temp/ -.PHONY: clean update-headers +.PHONY: all full compile hmod fix rc update upgrade info update-headers clean diff --git a/Makefile.docker b/Makefile.docker index 2230736..33484e9 100644 --- a/Makefile.docker +++ b/Makefile.docker @@ -1,9 +1,5 @@ -# Cross-compiles the standalone theme manager skeleton (src/main.cpp) against -# the shared rendering framework vendored from OptionsMenu (vendor/OptionsMenu, -# a git submodule - see that repo's src/framework/ for what's reused here). -# This is a separate build from the existing Makefile (which only packages -# the shell-script based hmod) - run via build.sh, mirroring OptionsMenu's -# own Docker-toolchain setup. +# Cross-compile against the vendored OptionsMenu framework, then package the +# result as a .hmod. Run inside the Docker toolchain image from build.sh. FRAMEWORK_DIR = vendor/OptionsMenu/src/framework CXX = g++ STRIP = strip @@ -23,25 +19,130 @@ VENDOR_SRC_DIR = vendor/OptionsMenu/src SOURCES = src/main.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp OBJECTS = $(SOURCES:.cpp=.o) -all: mod/etc/options_menu/themes/theme_manager mod/bin/theme_downloader +# Build theme_downloader with the toolchain's static curl+OpenSSL. +CC = gcc +CURL_PREFIX = /opt/curl-static +CFLAGS = -O2 -Wall -I$(CURL_PREFIX)/include +CURL_LDLIBS = -L$(CURL_PREFIX)/lib -L/usr/lib/arm-linux-gnueabihf -lcurl -lssl -lcrypto -lz -ldl -lpthread -mod/etc/options_menu/themes/theme_manager: $(OBJECTS) - $(CROSS_PREFIX)$(CXX) $(OBJECTS) $(LDLIBS) $(LDFLAGS) -Wl,-rpath,/etc/options_menu -o mod/etc/options_menu/themes/theme_manager - $(CROSS_PREFIX)$(STRIP) mod/etc/options_menu/themes/theme_manager +MOD_NAME := Options Menu - Theme Selector +MOD_CREATOR := DefKorns +MOD_CATEGORY := User Interface + +LAST_TAG_COMMIT := $(shell git rev-list --tags --max-count=1) +LAST_TAG := $(shell git describe --tags $(LAST_TAG_COMMIT) 2> /dev/null || echo "v0.0.0") +TAG_PREFIX := "v" +GET_VER := $(shell git describe --tags $(LAST_TAG_COMMIT) 2> /dev/null | sed "s/^$(TAG_PREFIX)//" || echo "0.0.0") +MOD_VER := $(shell [ -f VERSION ] && head VERSION || echo $(GET_VER)) +MAJOR := $(shell echo $(MOD_VER) | sed "s/^\([0-9]*\).*/\1/") +MINOR := $(shell echo $(MOD_VER) | sed "s/[0-9]*\.\([0-9]*\).*/\1/") +PATCH := $(shell echo $(MOD_VER) | sed "s/[0-9]*\.[0-9]*\.\([0-9]*\).*/\1/") +RC := $(shell echo $(MOD_VER) | sed -n "s/.*rc\([0-9]*\)$$/\1/p") +RC := $(if $(RC),$(RC),0) + +# total number of commits +BUILD := $(shell git log --oneline | wc -l | sed -e "s/[ \t]*//g") +NEXT_MAJOR_VERSION := $(shell expr $(MAJOR) + 1).0.0 +NEXT_MINOR_VERSION := $(MAJOR).$(shell expr $(MINOR) + 1).0-b$(BUILD) +NEXT_PATCH_VERSION := $(MAJOR).$(MINOR).$(shell expr $(PATCH) + 1)-b$(BUILD) +NEXT_RC_VERSION := $(MAJOR).$(MINOR).$(PATCH)-rc$(shell expr $(RC) + 1) + +GIT_COMMIT := $(shell git rev-parse --short HEAD)$(shell git diff-index --quiet HEAD -- || echo -dirty) +MOD_URL := $(shell git config --get remote.origin.url) +# Derive the package name from the remote; Docker mounts every repo at /src. +MOD_FILENAME := $(shell basename $(MOD_URL) .git) +DEV_DIR := $(CURDIR) +OUT := $(DEV_DIR)/out + +# Avoid "build": GNU Make's .sh rule conflicts with build.sh. +all: compile hmod + +compile: mod/etc/options_menu/lib/theme_manager mod/bin/theme_downloader + +mod/etc/options_menu/lib/theme_manager: $(OBJECTS) + $(CROSS_PREFIX)$(CXX) $(OBJECTS) $(LDLIBS) $(LDFLAGS) -Wl,-rpath,/etc/options_menu/lib -o mod/etc/options_menu/lib/theme_manager + $(CROSS_PREFIX)$(STRIP) mod/etc/options_menu/lib/theme_manager %.o: %.cpp $(CROSS_PREFIX)$(CXX) $(CXXFLAGS) -c $< -o $@ -# theme_downloader has its own Makefile (links the toolchain's static -# libcurl+OpenSSL, not SDL2 - unrelated build inputs from theme_manager). -.PHONY: mod/bin/theme_downloader -mod/bin/theme_downloader: - $(MAKE) -C theme_downloader CROSS_PREFIX=$(CROSS_PREFIX) - cp theme_downloader/theme_downloader mod/bin/theme_downloader +%.o: %.c + $(CROSS_PREFIX)$(CC) $(CFLAGS) -c $< -o $@ + +# Fetch the gitignored CA bundle for fresh builds. +src/cacert.pem: + curl -fL -o src/cacert.pem https://curl.se/ca/cacert.pem + +# Embed cacert.pem from src/ to keep "src/" out of the symbol names. +src/ca_bundle.o: src/cacert.pem + cd src && $(CROSS_PREFIX)ld -r -b binary -o ca_bundle.o cacert.pem + +mod/bin/theme_downloader: src/theme_downloader.o src/ca_bundle.o + $(CROSS_PREFIX)$(CC) src/theme_downloader.o src/ca_bundle.o $(CURL_LDLIBS) -o mod/bin/theme_downloader + $(CROSS_PREFIX)$(STRIP) mod/bin/theme_downloader + upx --best mod/bin/theme_downloader + +# Package mod/ without recompiling; run `all` first for fresh binaries. +hmod: + mkdir -p out/ temp/ + rsync -a --exclude-from=exclude-file.txt mod/ temp/ --links --delete + + printf "%s\n" \ + "---" \ + "Name: $(MOD_NAME)" \ + "Creator: $(MOD_CREATOR)" \ + "Category: $(MOD_CATEGORY)" \ + "Version: $(MOD_VER)" \ + "Built on: $(shell date +"%A, %d %b %Y - %T")" \ + "Git commit: $(GIT_COMMIT)" \ + "---" > temp/readme.md + + sed 1d mod/readme.md >> temp/readme.md + + cd temp/; tar -czf $(OUT)/$(MOD_FILENAME)-$(MOD_VER).hmod * + rm -r temp/ + +fix: hmod + @ver="$(NEXT_PATCH_VERSION)" && \ + git tag "v$(MOD_VER)" && \ + echo "$$ver" > VERSION && \ + git add VERSION && \ + git commit -m "Bump version to v$$ver" + +rc: hmod + @ver="$(NEXT_RC_VERSION)" && \ + git tag "v$(MOD_VER)" && \ + echo "$$ver" > VERSION && \ + git add VERSION && \ + git commit -m "Bump version to v$$ver" + +update: all + @ver="$(NEXT_MINOR_VERSION)" && \ + git tag "v$(MOD_VER)" && \ + echo "$$ver" > VERSION && \ + git add VERSION && \ + git commit -m "Bump version to v$$ver" + +upgrade: all + @ver="$(NEXT_MAJOR_VERSION)" && \ + git tag "v$(MOD_VER)" && \ + echo "$$ver" > VERSION && \ + git add VERSION && \ + git commit -m "Bump version to v$$ver" + +info: + @echo "Mod Dir: $(MOD_FILENAME)" + @echo "Current version: $(MOD_VER)" + @echo "Last tag: $(LAST_TAG)" + @echo "$(shell git rev-list $(LAST_TAG).. --count) commit(s) since last tag" + @echo "Build: $(BUILD) (total number of commits)" + @echo "next major version: $(NEXT_MAJOR_VERSION)" + @echo "next minor version: $(NEXT_MINOR_VERSION)" + @echo "next patch version: $(NEXT_PATCH_VERSION)" clean: find . -name "*.o" -type f -delete - rm -f mod/etc/options_menu/themes/theme_manager mod/bin/theme_downloader - $(MAKE) -C theme_downloader clean + rm -f mod/etc/options_menu/lib/theme_manager mod/bin/theme_downloader + rm -rf out/ temp/ -.PHONY: clean +.PHONY: all compile hmod fix rc update upgrade info clean diff --git a/build.sh b/build.sh index 45be59c..5d4c711 100644 --- a/build.sh +++ b/build.sh @@ -4,7 +4,7 @@ set -euo pipefail # stops Git Bash from mangling the /src path below into a Windows path export MSYS_NO_PATHCONV=1 -IMAGE="hakchi-toolchain:jessie-armhf" +IMAGE="classicmini-cross-toolchain:jessie-armhf" if [ ! -f toolchain/Dockerfile.jessie-armhf ]; then echo "==> Fetching toolchain submodule..." @@ -25,4 +25,4 @@ echo "==> Running make inside the container..." docker run --rm -v "$PWD:/src" "$IMAGE" make -f Makefile.docker CROSS_PREFIX=arm-linux-gnueabihf- "$@" echo "==> Build complete:" -ls -la mod/etc/options_menu/themes/theme_manager mod/bin/theme_downloader 2>/dev/null +ls -la mod/etc/options_menu/lib/theme_manager mod/bin/theme_downloader 2>/dev/null diff --git a/src/main.cpp b/src/main.cpp index 4dbf9db..64027a6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,11 +7,8 @@ * (at your option) any later version. */ -// Standalone theme manager for om_theme-selector, reusing OptionsMenu's -// engine (framework/ + command.cpp + localization.cpp, vendored submodule) -// instead of the generic `options` binary. Every screen is this same binary -// relaunched with a different --commandPath, reading command files the -// shell side already generates. +// Standalone theme manager using the vendored OptionsMenu engine. +// Each screen relaunches this binary with a different --commandPath. // // --layout list (default): single-column option picker. // --layout grid: fixed actions as a chip strip, everything else (themes, @@ -61,11 +58,7 @@ static void FitCentered(Texture & tex, int boxX, int boxY, int boxW, int boxH, i tex.rect.y = boxY + (boxH - tex.rect.h) / 2; } -// scale-to-cover-centered (crops the overflow) - pair with a clip rect at -// (boxX+padding, boxY+padding, boxW-2*padding, boxH-2*padding) so the -// overflow doesn't bleed past the box, and DrawRoundedCornerMask afterwards -// to fake-round that clip rect's square corners back to match a rounded -// container +// Scale to cover, clip the overflow, then restore rounded corners. static void FitCover(Texture & tex, int boxX, int boxY, int boxW, int boxH, int padding) { const int maxW = boxW - 2*padding; @@ -101,10 +94,8 @@ static std::string TruncateToWidth(const std::string & text, int glyphSize, int return label; } -// only one theme_manager should ever hold /dev/fb0 at a time - two screens -// (e.g. a stuck/crashed instance plus a freshly-launched one) fight over it -// and both flicker. Takes over rather than refusing to start, so a lock left -// behind by a crash never blocks every future launch. +// Keep one theme_manager on /dev/fb0 to prevent flicker. +// Take over stale instances instead of rejecting future launches. static const char * LockPath = "/tmp/theme_manager.lock"; static void ReleaseSingleInstanceLock() @@ -129,7 +120,7 @@ static void AcquireSingleInstanceLock() } std::ofstream(LockPath, std::ios::trunc) << getpid(); atexit(ReleaseSingleInstanceLock); - signal(SIGTERM, [](int) { exit(0); }); // so a newer instance's takeover runs our atexit cleanup too + signal(SIGTERM, [](int) { exit(0); }); // Run atexit cleanup during takeover. } int main(int argc, char * argv[]) diff --git a/src/theme_downloader.c b/src/theme_downloader.c index f108358..500883f 100644 --- a/src/theme_downloader.c +++ b/src/theme_downloader.c @@ -15,13 +15,8 @@ * along with this program. If not, see . */ -/* Fetches om_theme-selector's theme catalog/packages over HTTPS. The - * console's own BusyBox wget can't negotiate TLS 1.2 at all, so this - * replaces it - statically linked against curl+OpenSSL so it carries no - * runtime library dependency on the console. Knows the GitHub release host - * and the classicmods.net fallback itself (falls back automatically if - * GitHub isn't reachable), so the shell side only needs to say what it - * wants, not where to get it. */ +/* Fetch theme catalogs and packages over HTTPS using static curl+OpenSSL. + * Fall back from GitHub to classicmods.net automatically. */ #include #include @@ -46,8 +41,7 @@ static void to_upper(char *dst, const char *src, size_t dst_size) { dst[i] = '\0'; } -/* extracted once per boot into /tmp - cheap to redo, avoids re-embedding - * the CA bundle as a shell-visible file inside the hmod itself */ +/* Extract the embedded CA bundle into /tmp once per boot. */ static void ensure_ca_bundle(void) { FILE *f = fopen(CA_PATH, "rb"); if (f) { From 9a30c5acb9a8f9a68a6fb4220744e0fdacb3cd0b Mon Sep 17 00:00:00 2001 From: DefKorns Date: Mon, 21 Sep 2026 00:30:57 +0000 Subject: [PATCH 076/210] Update subproject commit reference in OptionsMenu --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index d291937..2bce3c1 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit d291937799bcbcf89b899a4e52f77bf2415e8cd4 +Subproject commit 2bce3c180def93bf218acaaf61c5e35c19b88789 From cb72f9f887981333d69912c7f01ecb11d0e35970 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 22 Sep 2026 23:04:43 +0000 Subject: [PATCH 077/210] Remove commented-out scale functions and clean up variable declaration in main function --- src/main.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 64027a6..54ad971 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -44,7 +44,6 @@ static void sReplace(std::string & command, std::string oldString, std::string n command.replace(pos, oldString.size(), newString); } -// scale-to-fit-centered static void FitCentered(Texture & tex, int boxX, int boxY, int boxW, int boxH, int padding) { const int maxW = boxW - 2*padding; @@ -58,7 +57,6 @@ static void FitCentered(Texture & tex, int boxX, int boxY, int boxW, int boxH, i tex.rect.y = boxY + (boxH - tex.rect.h) / 2; } -// Scale to cover, clip the overflow, then restore rounded corners. static void FitCover(Texture & tex, int boxX, int boxY, int boxW, int boxH, int padding) { const int maxW = boxW - 2*padding; @@ -429,7 +427,7 @@ int main(int argc, char * argv[]) return 0; }; const int chipGap = 14; - const int chipIconSize = 22; // small, matches the 14px label's own scale + const int chipIconSize = 22; // no strip on a pure-tile screen (e.g. a DIY category) - use the row for a 3rd grid row const bool hasStrip = stripSlotCount > 0; From 24639efc451fe7bbb0e92c184662163cbee298e9 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 23 Sep 2026 12:09:40 +0000 Subject: [PATCH 078/210] Split theme_manager into command_loader/theme_manager_app/single_instance_lock/texture_utils, drop the now-default --layout grid flag across launch sites, and remove the superseded sprites.json files --- Makefile | 2 +- Makefile.docker | 2 +- mod/etc/options_menu/scripts/om_themeselector | 2 +- .../themes/creator/scripts/om_applytheme | 2 +- .../themes/creator/scripts/om_diy_asset | 2 +- .../themes/creator/scripts/om_removediy | 2 +- .../creator/sprites/colors/scripts/om_return | 2 +- .../sprites/colors_nes/scripts/om_return | 2 +- .../creator/sprites/luigi/scripts/om_return | 2 +- .../creator/sprites/mario/scripts/om_return | 2 +- .../creator/sprites/packed/scripts/om_return | 2 +- .../sprites/packed_nes/scripts/om_return | 2 +- .../creator/sprites/pole/scripts/om_return | 2 +- .../creator/sprites/sbm/scripts/om_return | 2 +- .../creator/sprites/wall/scripts/om_return | 2 +- .../sprites/wall_far/scripts/om_return | 2 +- .../sprites/wall_near/scripts/om_return | 2 +- .../downloads/scripts/om_clearDownloads | 2 +- .../themes/downloads/scripts/om_refreshThemes | 2 +- .../themes/downloads/scripts/om_return | 2 +- .../options_menu/themes/scripts/om_clearList | 2 +- .../options_menu/themes/scripts/om_getThemes | 2 +- .../options_menu/themes/scripts/om_settings | 2 +- .../themes/scripts/om_themeCreator | 2 +- mod/etc/options_menu/themes/scripts/om_vars | 2 +- .../themes/scripts/om_wall.sprites.json | 1 - .../themes/scripts/om_wall_far.sprites.json | 1 - .../themes/scripts/om_wall_near.sprites.json | 1 - .../themes/settings/scripts/om_audio | 2 +- .../themes/settings/scripts/om_reset | 2 +- .../themes/settings/scripts/om_systemcleanup | 2 +- .../themes/settings/scripts/om_theme | 2 +- src/command_loader.cpp | 164 +++ src/command_loader.h | 39 + src/main.cpp | 988 +----------------- src/single_instance_lock.cpp | 49 + src/single_instance_lock.h | 35 + src/texture_utils.cpp | 64 ++ src/texture_utils.h | 21 + src/theme_manager_app.cpp | 725 +++++++++++++ src/theme_manager_app.h | 75 ++ 41 files changed, 1218 insertions(+), 1003 deletions(-) delete mode 100644 mod/etc/options_menu/themes/scripts/om_wall.sprites.json delete mode 100644 mod/etc/options_menu/themes/scripts/om_wall_far.sprites.json delete mode 100644 mod/etc/options_menu/themes/scripts/om_wall_near.sprites.json create mode 100644 src/command_loader.cpp create mode 100644 src/command_loader.h create mode 100644 src/single_instance_lock.cpp create mode 100644 src/single_instance_lock.h create mode 100644 src/texture_utils.cpp create mode 100644 src/texture_utils.h create mode 100644 src/theme_manager_app.cpp create mode 100644 src/theme_manager_app.h diff --git a/Makefile b/Makefile index 2dfcf5d..2fda511 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ endif CXXFLAGS = -std=c++11 -Os -Ivendor/OptionsMenu/src $(SDL_CFLAGS) LDLIBS = $(SDL_LIBS) VENDOR_SRC_DIR = vendor/OptionsMenu/src -SOURCES = src/main.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp +SOURCES = src/main.cpp src/single_instance_lock.cpp src/command_loader.cpp src/texture_utils.cpp src/theme_manager_app.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp OBJECTS = $(SOURCES:.cpp=.o) # Build theme_downloader with the Docker toolchain's static curl+OpenSSL. diff --git a/Makefile.docker b/Makefile.docker index 33484e9..daf35ce 100644 --- a/Makefile.docker +++ b/Makefile.docker @@ -16,7 +16,7 @@ endif CXXFLAGS = -std=c++11 -Os -Ivendor/OptionsMenu/src $(SDL_CFLAGS) LDLIBS = $(SDL_LIBS) VENDOR_SRC_DIR = vendor/OptionsMenu/src -SOURCES = src/main.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp +SOURCES = src/main.cpp src/single_instance_lock.cpp src/command_loader.cpp src/texture_utils.cpp src/theme_manager_app.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp OBJECTS = $(SOURCES:.cpp=.o) # Build theme_downloader with the toolchain's static curl+OpenSSL. diff --git a/mod/etc/options_menu/scripts/om_themeselector b/mod/etc/options_menu/scripts/om_themeselector index a86ca51..67f4030 100644 --- a/mod/etc/options_menu/scripts/om_themeselector +++ b/mod/etc/options_menu/scripts/om_themeselector @@ -33,4 +33,4 @@ else loadThemeList fi -$optionsMenu/lib/theme_manager --title INSTALLED_THEMES --layout grid & +$optionsMenu/lib/theme_manager --title INSTALLED_THEMES & diff --git a/mod/etc/options_menu/themes/creator/scripts/om_applytheme b/mod/etc/options_menu/themes/creator/scripts/om_applytheme index 3777d99..d082d2e 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_applytheme +++ b/mod/etc/options_menu/themes/creator/scripts/om_applytheme @@ -41,4 +41,4 @@ remove "$omDummy" loadThemeList -r -$optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" --layout grid & +$optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset index 26315f2..95a91dd 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset +++ b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset @@ -72,4 +72,4 @@ if [ "$2" = "force" ] || ! find "$diy_om_sprite_path/commands" -maxdepth 1 -name findDiyAsset "$1" fi -$optionsMenu/lib/theme_manager --commandPath $diy_om_sprite_path/commands/ --scriptPath $diy_om_sprite_path/scripts --title $diy_title --layout grid & +$optionsMenu/lib/theme_manager --commandPath $diy_om_sprite_path/commands/ --scriptPath $diy_om_sprite_path/scripts --title $diy_title & diff --git a/mod/etc/options_menu/themes/creator/scripts/om_removediy b/mod/etc/options_menu/themes/creator/scripts/om_removediy index 670620f..b1d3595 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_removediy +++ b/mod/etc/options_menu/themes/creator/scripts/om_removediy @@ -22,4 +22,4 @@ script_init diyThemeRemover -$optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" --layout grid & +$optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return index 836c862..0f1d86b 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return index 836c862..0f1d86b 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return index 836c862..0f1d86b 100644 --- a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return index 613a278..7c3e78e 100644 --- a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return index 613a278..7c3e78e 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return index 613a278..7c3e78e 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return index 613a278..7c3e78e 100644 --- a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return index 613a278..7c3e78e 100644 --- a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return index 613a278..7c3e78e 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return index 613a278..7c3e78e 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return index 613a278..7c3e78e 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads index 116f446..a779d16 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads +++ b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads @@ -23,4 +23,4 @@ script_init clearDownloads remove "$subDummy" -$optionsMenu/lib/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES --layout grid & +$optionsMenu/lib/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes index 805c1da..1c34d44 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes +++ b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes @@ -22,4 +22,4 @@ script_init getThemeList -$optionsMenu/lib/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES --layout grid & +$optionsMenu/lib/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_return b/mod/etc/options_menu/themes/downloads/scripts/om_return index f92929b..150ee05 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_return +++ b/mod/etc/options_menu/themes/downloads/scripts/om_return @@ -29,4 +29,4 @@ else clearList fi -usleep 50000 && $optionsMenu/lib/theme_manager --title INSTALLED_THEMES --layout grid & +usleep 50000 && $optionsMenu/lib/theme_manager --title INSTALLED_THEMES & diff --git a/mod/etc/options_menu/themes/scripts/om_clearList b/mod/etc/options_menu/themes/scripts/om_clearList index f85fdfd..42dd0b3 100644 --- a/mod/etc/options_menu/themes/scripts/om_clearList +++ b/mod/etc/options_menu/themes/scripts/om_clearList @@ -24,4 +24,4 @@ clearList disable_cmds "Clear" enable_cmds "Refresh" -$optionsMenu/lib/theme_manager --title INSTALLED_THEMES --layout grid & +$optionsMenu/lib/theme_manager --title INSTALLED_THEMES & diff --git a/mod/etc/options_menu/themes/scripts/om_getThemes b/mod/etc/options_menu/themes/scripts/om_getThemes index 68211e3..301322a 100644 --- a/mod/etc/options_menu/themes/scripts/om_getThemes +++ b/mod/etc/options_menu/themes/scripts/om_getThemes @@ -22,4 +22,4 @@ script_init [ ! -f "$subDummy" ] && getThemeList -$optionsMenu/lib/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES --layout grid & +$optionsMenu/lib/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES & diff --git a/mod/etc/options_menu/themes/scripts/om_settings b/mod/etc/options_menu/themes/scripts/om_settings index 52bb365..14b6c01 100644 --- a/mod/etc/options_menu/themes/scripts/om_settings +++ b/mod/etc/options_menu/themes/scripts/om_settings @@ -37,4 +37,4 @@ else [ ! -f "$enableSystemCleanUp" ] && remove "$omModSettingsSpacer" fi -$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & +$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS --layout list & diff --git a/mod/etc/options_menu/themes/scripts/om_themeCreator b/mod/etc/options_menu/themes/scripts/om_themeCreator index 4797b6f..d15e505 100644 --- a/mod/etc/options_menu/themes/scripts/om_themeCreator +++ b/mod/etc/options_menu/themes/scripts/om_themeCreator @@ -22,4 +22,4 @@ script_init diyThemeChecker -$optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title DIY_THEME --layout grid & +$optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 04fd7a9..ef2cd32 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -326,7 +326,7 @@ sed -i \"s/SELECTED_THEME=\\\"CHANGEME\\\"/SELECTED_THEME=\\\"${thm}\\\"/g;\" \" COMMAND_TYPE=INTERNAL COMMAND_STR=sh $thm_script.sh PREVIEW_IMAGE=$preview_image -DELETE_STR=rm -rf \"$full_path\" \"$full_path.png\" \"$cmd_list\" \"$thm_script.sh\" && usleep 50000 && %options_path%lib/theme_manager --title INSTALLED_THEMES --layout grid &" > "$cmd_list" +DELETE_STR=rm -rf \"$full_path\" \"$full_path.png\" \"$cmd_list\" \"$thm_script.sh\" && usleep 50000 && %options_path%lib/theme_manager --title INSTALLED_THEMES &" > "$cmd_list" done } diff --git a/mod/etc/options_menu/themes/scripts/om_wall.sprites.json b/mod/etc/options_menu/themes/scripts/om_wall.sprites.json deleted file mode 100644 index 6456acf..0000000 --- a/mod/etc/options_menu/themes/scripts/om_wall.sprites.json +++ /dev/null @@ -1 +0,0 @@ -{"changeme.png":{"textureFrame":[50,1,180,180],"pivot":[256,40],"sourceSize":[180,180],"sourceOffset":[0,0]}} diff --git a/mod/etc/options_menu/themes/scripts/om_wall_far.sprites.json b/mod/etc/options_menu/themes/scripts/om_wall_far.sprites.json deleted file mode 100644 index dc86902..0000000 --- a/mod/etc/options_menu/themes/scripts/om_wall_far.sprites.json +++ /dev/null @@ -1 +0,0 @@ -{"changeme.png":{"textureFrame":[50,1,178,178],"pivot":[256,89],"sourceSize":[178,178],"sourceOffset":[0,0]}} diff --git a/mod/etc/options_menu/themes/scripts/om_wall_near.sprites.json b/mod/etc/options_menu/themes/scripts/om_wall_near.sprites.json deleted file mode 100644 index 994b400..0000000 --- a/mod/etc/options_menu/themes/scripts/om_wall_near.sprites.json +++ /dev/null @@ -1 +0,0 @@ -{"changeme.png":{"textureFrame":[50,1,100,100],"pivot":[512,50],"sourceSize":[100,100],"sourceOffset":[0,0]}} diff --git a/mod/etc/options_menu/themes/settings/scripts/om_audio b/mod/etc/options_menu/themes/settings/scripts/om_audio index 0e6c243..3e8704c 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_audio +++ b/mod/etc/options_menu/themes/settings/scripts/om_audio @@ -21,4 +21,4 @@ script_init audioSettings "$1" -$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & +$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS --layout list & diff --git a/mod/etc/options_menu/themes/settings/scripts/om_reset b/mod/etc/options_menu/themes/settings/scripts/om_reset index 7b1bc8f..6f832ef 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_reset +++ b/mod/etc/options_menu/themes/settings/scripts/om_reset @@ -23,4 +23,4 @@ script_init remove "$b_file" "$p_file" hakchi reboot -$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & +$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS --layout list & diff --git a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup index cb12131..8f5562b 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup +++ b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup @@ -25,4 +25,4 @@ modCleanUp usleep 1000 -$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & +$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS --layout list & diff --git a/mod/etc/options_menu/themes/settings/scripts/om_theme b/mod/etc/options_menu/themes/settings/scripts/om_theme index d4f5503..1804692 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_theme +++ b/mod/etc/options_menu/themes/settings/scripts/om_theme @@ -22,4 +22,4 @@ script_init themeSettings "$1" -$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS & +$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS --layout list & diff --git a/src/command_loader.cpp b/src/command_loader.cpp new file mode 100644 index 0000000..7ee9ecd --- /dev/null +++ b/src/command_loader.cpp @@ -0,0 +1,164 @@ +/** + * Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#include "command_loader.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace { + +void ReplaceAll(std::string & command, const std::string & oldString, const std::string & newString) +{ + size_t pos; + while((pos = command.find(oldString)) != std::string::npos) + command.replace(pos, oldString.size(), newString); +} + +struct DirCloser { void operator()(DIR * dir) const { if(dir) closedir(dir); } }; +using DirHandle = std::unique_ptr; + +} // namespace + +AppOptions ParseArgs(int argc, char * argv[], const std::string & optionsLocation) +{ + AppOptions options; + options.commandLocation = optionsLocation + "themes/commands/"; + options.scriptLocation = optionsLocation + "themes/scripts/"; + + for(int i = 1; i < argc; ++i) + { + if(std::strcmp(argv[i], "--commandPath") == 0 && i+1 < argc) + { + options.commandLocation = argv[++i]; + options.isRootScreen = false; + } + else if(std::strcmp(argv[i], "--scriptPath") == 0 && i+1 < argc) + options.scriptLocation = argv[++i]; + else if(std::strcmp(argv[i], "--title") == 0 && i+1 < argc) + options.titleKey = argv[++i]; + else if(std::strcmp(argv[i], "--layout") == 0 && i+1 < argc) + options.gridLayout = (std::string(argv[++i]) == "grid"); + } + if(!options.commandLocation.empty() && options.commandLocation.back() != '/') + options.commandLocation += '/'; + if(!options.scriptLocation.empty() && options.scriptLocation.back() != '/') + options.scriptLocation += '/'; + return options; +} + +std::string ReadBackStack(bool isRootScreen) +{ + const char * backStackEnv = std::getenv("OM_BACK_STACK"); + return (isRootScreen && backStackEnv) ? backStackEnv : ""; +} + +bool LoadCommands(const std::string & commandLocation, const std::string & scriptLocation, const std::string & optionsLocation, + std::vector & commands, std::vector & isThemeItem) +{ + DirHandle dir(opendir(commandLocation.c_str())); + if(!dir) + { + std::cerr << "Cannot open input folder.\n"; + return false; + } + + std::list fileList; + while(auto entry = readdir(dir.get())) + if(entry->d_type == DT_REG && entry->d_name[0] == 'c') + fileList.push_back(entry->d_name); + fileList.sort(); + + std::ifstream in; + for(auto & file : fileList) + { + in.open(commandLocation + file); + if(!in.is_open()) + continue; + Command c(in); // closes `in` itself once read + + // c0000_0000 leading sentinel only - a later empty COMMAND_STR is a reserved blank slot instead + if(commands.empty() && c.command.empty()) + continue; + if(!c.command.empty()) + { + ReplaceAll(c.command, "%options_path%", optionsLocation); + ReplaceAll(c.command, "%script_dir%", scriptLocation); + ReplaceAll(c.deleteCommand, "%options_path%", optionsLocation); + ReplaceAll(c.deleteCommand, "%script_dir%", scriptLocation); + if(c.isToggle) + { + ReplaceAll(c.stateCommand, "%options_path%", optionsLocation); + ReplaceAll(c.stateCommand, "%script_dir%", scriptLocation); + c.UpdateState(); + } + } + commands.push_back(c); + isThemeItem.push_back(file.compare(0, 6, "c0000_") != 0); + } + return true; +} + +void AppendBackNavigation(const std::string & optionsLocation, const AppOptions & options, const std::string & backStack, + std::vector & commands, std::vector & isThemeItem) +{ + std::string backCommand; + if(!backStack.empty()) + { + std::vector entries; + for(size_t start = 0; start < backStack.size();) + { + size_t sep = backStack.find(';', start); + if(sep == std::string::npos) sep = backStack.size(); + entries.push_back(backStack.substr(start, sep-start)); + start = sep+1; + } + std::string lastEntry = entries.back(); + entries.pop_back(); + std::string remainingStack; + for(size_t i = 0; i < entries.size(); ++i) + { + if(i) remainingStack += ";"; + remainingStack += entries[i]; + } + + size_t c1 = lastEntry.find(','), c2 = lastEntry.find(',', c1+1); + std::string backPath = lastEntry.substr(0, c1); + std::string backScriptPath = lastEntry.substr(c1+1, c2-c1-1); + std::string backTitleKey = lastEntry.substr(c2+1); + + backCommand = "usleep 50000 && OM_BACK_STACK=\"" + remainingStack + "\" " + optionsLocation + "options --commandPath " + backPath + + (backScriptPath.empty() ? "" : " --scriptPath " + backScriptPath) + + " --title \"" + backTitleKey + "\" &"; + } + else if(!options.isRootScreen) + { + std::ifstream returnScript(options.scriptLocation + "om_return"); + if(returnScript.good()) + backCommand = "sh " + options.scriptLocation + "om_return &"; + else + backCommand = "usleep 50000 && " + optionsLocation + "lib/theme_manager --title INSTALLED_THEMES --layout grid &"; + } + else + return; + + Command back; + back.name = "BACK"; + back.runInternal = false; + back.restartUI = false; + back.previewImage = optionsLocation + "images/preview_placeholder.png"; + back.command = backCommand; + commands.push_back(back); + isThemeItem.push_back(false); +} diff --git a/src/command_loader.h b/src/command_loader.h new file mode 100644 index 0000000..15743a1 --- /dev/null +++ b/src/command_loader.h @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef COMMAND_LOADER_H_ +#define COMMAND_LOADER_H_ + +#include "command.h" + +#include +#include + +struct AppOptions +{ + std::string commandLocation; + std::string scriptLocation; + std::string titleKey = "THEME_SELECTOR"; + bool isRootScreen = true; // no --commandPath override - top of the theme-selector's own tree + bool gridLayout = true; +}; + +AppOptions ParseArgs(int argc, char * argv[], const std::string & optionsLocation); + +// OM_BACK_STACK is set by OptionsMenu before launching "Theme Options" +std::string ReadBackStack(bool isRootScreen); + +// isThemeItem[i] is false for a fixed "c0000_*" action, true for a theme/asset row +bool LoadCommands(const std::string & commandLocation, const std::string & scriptLocation, const std::string & optionsLocation, + std::vector & commands, std::vector & isThemeItem); + +void AppendBackNavigation(const std::string & optionsLocation, const AppOptions & options, const std::string & backStack, + std::vector & commands, std::vector & isThemeItem); + +#endif diff --git a/src/main.cpp b/src/main.cpp index 54ad971..7ad84ef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,998 +7,44 @@ * (at your option) any later version. */ -// Standalone theme manager using the vendored OptionsMenu engine. -// Each screen relaunches this binary with a different --commandPath. -// -// --layout list (default): single-column option picker. -// --layout grid: fixed actions as a chip strip, everything else (themes, -// DIY assets) as a scrollable tile grid. +// Standalone theme manager using the vendored OptionsMenu engine. Each +// screen relaunches this binary with a different --commandPath. +#include "single_instance_lock.h" +#include "command_loader.h" +#include "theme_manager_app.h" #include "framework/sdl_helper.h" -#include "framework/controller.h" -#include "framework/powerwatch.h" -#include "framework/draw_helpers.h" -#include "framework/utf8.h" -#include "command.h" #include "localization.h" -#include -#include -#include -#include -#include #include -#include -#include -#include -#include - -#ifndef MOD_VERSION -#define MOD_VERSION "dev" -#endif - -static void sReplace(std::string & command, std::string oldString, std::string newString) -{ - size_t pos; - while((pos = command.find(oldString)) != std::string::npos) - command.replace(pos, oldString.size(), newString); -} - -static void FitCentered(Texture & tex, int boxX, int boxY, int boxW, int boxH, int padding) -{ - const int maxW = boxW - 2*padding; - const int maxH = boxH - 2*padding; - if(tex.rect.w <= 0 || tex.rect.h <= 0) - return; - double scale = std::min((double)maxW / tex.rect.w, (double)maxH / tex.rect.h); - tex.rect.w = static_cast(tex.rect.w * scale); - tex.rect.h = static_cast(tex.rect.h * scale); - tex.rect.x = boxX + (boxW - tex.rect.w) / 2; - tex.rect.y = boxY + (boxH - tex.rect.h) / 2; -} - -static void FitCover(Texture & tex, int boxX, int boxY, int boxW, int boxH, int padding) -{ - const int maxW = boxW - 2*padding; - const int maxH = boxH - 2*padding; - if(tex.rect.w <= 0 || tex.rect.h <= 0) - return; - double scale = std::max((double)maxW / tex.rect.w, (double)maxH / tex.rect.h); - tex.rect.w = static_cast(tex.rect.w * scale); - tex.rect.h = static_cast(tex.rect.h * scale); - tex.rect.x = boxX + (boxW - tex.rect.w) / 2; - tex.rect.y = boxY + (boxH - tex.rect.h) / 2; -} - -static std::string TruncateToWidth(const std::string & text, int glyphSize, int available) -{ - std::string label = text; - if(CanRenderWithTTF(label, glyphSize)) - { - if(MeasureTTFWidth(label, glyphSize) > available) - { - int n = Utf8Length(label); - while(n > 0 && MeasureTTFWidth(TruncateUtf8(label, n) + "...", glyphSize) > available) - --n; - label = TruncateUtf8(label, n) + "..."; - } - } - else - { - int maxChars = available / glyphSize; - if(TruncateUtf8(label, maxChars).size() != label.size()) - label = TruncateUtf8(label, std::max(0, maxChars - 3)) + "..."; - } - return label; -} - -// Keep one theme_manager on /dev/fb0 to prevent flicker. -// Take over stale instances instead of rejecting future launches. -static const char * LockPath = "/tmp/theme_manager.lock"; - -static void ReleaseSingleInstanceLock() -{ - remove(LockPath); -} - -static void AcquireSingleInstanceLock() -{ - std::ifstream in(LockPath); - pid_t oldPid = 0; - if(in.good()) - in >> oldPid; - in.close(); - if(oldPid > 0 && oldPid != getpid() && kill(oldPid, 0) == 0) - { - kill(oldPid, SIGTERM); - for(int i = 0; i < 40 && kill(oldPid, 0) == 0; ++i) - usleep(50000); - if(kill(oldPid, 0) == 0) - kill(oldPid, SIGKILL); - } - std::ofstream(LockPath, std::ios::trunc) << getpid(); - atexit(ReleaseSingleInstanceLock); - signal(SIGTERM, [](int) { exit(0); }); // Run atexit cleanup during takeover. -} +#include +#include int main(int argc, char * argv[]) { - AcquireSingleInstanceLock(); + SingleInstanceLock::Acquire(); const std::string optionsLocation = "/etc/options_menu/"; - std::string commandLocation = optionsLocation + "themes/commands/"; - std::string scriptLocation = optionsLocation + "themes/scripts/"; - std::string titleKey = "THEME_SELECTOR"; - bool isRootScreen = true; // no --commandPath override - this is the top of the theme-selector's own tree - bool gridLayout = false; - - for(int i = 1; i < argc; ++i) - { - if(strcmp(argv[i], "--commandPath") == 0 && i+1 < argc) - { - commandLocation = argv[++i]; - isRootScreen = false; - } - else if(strcmp(argv[i], "--scriptPath") == 0 && i+1 < argc) - scriptLocation = argv[++i]; - else if(strcmp(argv[i], "--title") == 0 && i+1 < argc) - titleKey = argv[++i]; - else if(strcmp(argv[i], "--layout") == 0 && i+1 < argc) - gridLayout = (std::string(argv[++i]) == "grid"); - } - if(!commandLocation.empty() && commandLocation.back() != '/') - commandLocation += '/'; - if(!scriptLocation.empty() && scriptLocation.back() != '/') - scriptLocation += '/'; - - // set by OptionsMenu before launching the "Theme Options" row - lets the - // root screen offer a real way back to it - const char * backStackEnv = getenv("OM_BACK_STACK"); - std::string backStack = (isRootScreen && backStackEnv) ? backStackEnv : ""; + AppOptions options = ParseArgs(argc, argv, optionsLocation); + const std::string backStack = ReadBackStack(options.isRootScreen); LoadLanguageFromConfig(optionsLocation); SetTTFFontPath(optionsLocation); UiTheme::LoadThemeConfig(optionsLocation); - // isThemeItem: false for a "c0000_*" fixed action, true for a theme/asset - // row - only used by grid layout, to split strip vs. tile std::vector commands; std::vector isThemeItem; - std::ifstream in; - if(auto dir = opendir(commandLocation.c_str())) - { - std::list fileList; - while(auto entry = readdir(dir)) - { - if(entry->d_type == DT_REG && entry->d_name[0] == 'c') - fileList.push_back(entry->d_name); - } - closedir(dir); - - fileList.sort(); - for(auto & file : fileList) - { - in.open(commandLocation+file); - if(in.is_open()) - { - Command c(in); - // c0000_0000: leading empty sentinel, always skipped. A - // later empty COMMAND_STR is a reserved blank slot instead - - // kept and skipped by the strip/grid draw loops below - if(commands.empty() && c.command.empty()) - continue; - if(!c.command.empty()) - { - sReplace(c.command, "%options_path%", optionsLocation); - sReplace(c.command, "%script_dir%", scriptLocation); - sReplace(c.deleteCommand, "%options_path%", optionsLocation); - sReplace(c.deleteCommand, "%script_dir%", scriptLocation); - if(c.isToggle) - { - sReplace(c.stateCommand, "%options_path%", optionsLocation); - sReplace(c.stateCommand, "%script_dir%", scriptLocation); - c.UpdateState(); - } - } - commands.push_back(c); - isThemeItem.push_back(file.compare(0, 6, "c0000_") != 0); - } - } - } - else - { - std::cerr << "Cannot open input folder.\n"; - exit(1); - } - - if(!backStack.empty()) - { - std::vector entries; - for(size_t start = 0; start < backStack.size();) - { - size_t sep = backStack.find(';', start); - if(sep == std::string::npos) sep = backStack.size(); - entries.push_back(backStack.substr(start, sep-start)); - start = sep+1; - } - std::string lastEntry = entries.back(); - entries.pop_back(); - std::string remainingStack; - for(size_t i = 0; i < entries.size(); ++i) - { - if(i) remainingStack += ";"; - remainingStack += entries[i]; - } + if(!LoadCommands(options.commandLocation, options.scriptLocation, optionsLocation, commands, isThemeItem)) + return 1; - size_t c1 = lastEntry.find(','), c2 = lastEntry.find(',', c1+1); - std::string backPath = lastEntry.substr(0, c1); - std::string backScriptPath = lastEntry.substr(c1+1, c2-c1-1); - std::string backTitleKey = lastEntry.substr(c2+1); - - Command back; - back.name = "BACK"; - back.runInternal = false; - back.restartUI = false; - back.previewImage = optionsLocation + "images/preview_placeholder.png"; - back.command = "usleep 50000 && OM_BACK_STACK=\"" + remainingStack + "\" " + optionsLocation + "options --commandPath " + backPath - + (backScriptPath.empty() ? "" : " --scriptPath " + backScriptPath) - + " --title \"" + backTitleKey + "\" &"; - commands.push_back(back); - isThemeItem.push_back(false); - } - else if(!isRootScreen) - { - // use the screen's own om_return for cleanup if it has one, else - // just relaunch straight back to root - Command back; - back.name = "BACK"; - back.runInternal = false; - back.restartUI = false; - back.previewImage = optionsLocation + "images/preview_placeholder.png"; - std::ifstream returnScript(scriptLocation + "om_return"); - if(returnScript.good()) - back.command = "sh " + scriptLocation + "om_return &"; - else - back.command = "usleep 50000 && " + optionsLocation + "lib/theme_manager --title INSTALLED_THEMES --layout grid &"; - commands.push_back(back); - isThemeItem.push_back(false); - } + AppendBackNavigation(optionsLocation, options, backStack, commands, isThemeItem); if(commands.empty()) { - std::cerr << "No usable commands in " << commandLocation << "\n"; - exit(1); - } - - // BACK/EXIT sort last - pin them near the footer instead of scrolling - int pinnedStartIndex = static_cast(commands.size()); - while(pinnedStartIndex > 0 && (commands[pinnedStartIndex-1].name == "BACK" || commands[pinnedStartIndex-1].name == "EXIT")) - --pinnedStartIndex; - - // grid layout only: first non-fixed-action row (themeStart==pinnedStartIndex - // for an empty list - still renders as grid, just with an empty grid area) - int themeStart = 0; - while(themeStart < pinnedStartIndex && !isThemeItem[themeStart]) - ++themeStart; - - int currentCommandId = 0; - fprintf(stderr, "CHECKPOINT 1: commands loaded, count=%zu\n", commands.size()); fflush(stderr); - - SDL_Context sdl_context(std::chrono::milliseconds(33), false); - fprintf(stderr, "CHECKPOINT 2: sdl_context created\n"); fflush(stderr); - auto renderer = sdl_context.renderer; - Controller controller(1); - fprintf(stderr, "CHECKPOINT 3: controller created\n"); fflush(stderr); - - const Uint8 bgR = UiTheme::BgR; - const Uint8 bgG = UiTheme::BgG; - const Uint8 bgB = UiTheme::BgB; - SDL_SetRenderDrawColor(renderer, bgR, bgG, bgB, 0xFF); - - Texture gearIcon(optionsLocation + UiTheme::AssetGear, renderer, UiTheme::GearX, UiTheme::GearY); - Texture switchOn(optionsLocation + UiTheme::AssetSwitchOn, renderer); - Texture switchOff(optionsLocation + UiTheme::AssetSwitchOff, renderer); - - Texture appTitleText("Theme Manager", UiTheme::TitleFontSize, renderer, UiTheme::TitleX, UiTheme::TitleY, false, UiTheme::TextColor, true); - appTitleText.rect.y -= appTitleText.rect.h / 2; - Texture appVersionText(MOD_VERSION, UiTheme::VersionFontSize, renderer, appTitleText.rect.x + appTitleText.rect.w + UiTheme::VersionGap, UiTheme::TitleY, false, UiTheme::TextColor, true); - appVersionText.rect.y -= appVersionText.rect.h / 2; - Texture titleText(Translate(titleKey), UiTheme::SectionTitleFontSize, renderer, UiTheme::SectionTitleX, UiTheme::SectionTitleY, false, UiTheme::TextColor, true); - Texture creditText("Theme Manager - by DefKorns", 16, renderer, UiTheme::CreditX, UiTheme::CreditY, false, UiTheme::TextColor, true); - fprintf(stderr, "CHECKPOINT 4: basic textures created\n"); fflush(stderr); - - struct Badge { Texture letter; Texture label; UiTheme::BadgeColor rim; UiTheme::BadgeColor fill; }; - Badge badgeA{ Texture("A", 16, renderer, 0, 0, false, UiTheme::BadgeLetterColor, true), Texture(Translate("HINT_SELECT"), 16, renderer, 0, 0, false, UiTheme::TextColor, true), UiTheme::BadgeADark, UiTheme::BadgeA }; - Texture badgeOuter(optionsLocation + UiTheme::AssetBadgeOuter, renderer); - Texture badgeInner(optionsLocation + UiTheme::AssetBadgeInner, renderer); - auto DrawBadge = [&](Badge & badge, int rightEdgeX) -> int - { - int groupW = UiTheme::BadgeOuterSize + UiTheme::BadgeLabelGap + badge.label.rect.w; - int x = rightEdgeX - groupW; - int y = UiTheme::BadgeBandY; - badgeOuter.rect = { x, y, UiTheme::BadgeOuterSize, UiTheme::BadgeOuterSize }; - SDL_SetTextureColorMod(badgeOuter.texture.get(), badge.rim.r, badge.rim.g, badge.rim.b); - badgeOuter.Draw(renderer); - int innerOffset = (UiTheme::BadgeOuterSize - UiTheme::BadgeInnerSize) / 2; - badgeInner.rect = { x+innerOffset, y+innerOffset, UiTheme::BadgeInnerSize, UiTheme::BadgeInnerSize }; - SDL_SetTextureColorMod(badgeInner.texture.get(), badge.fill.r, badge.fill.g, badge.fill.b); - badgeInner.Draw(renderer); - // glyph bearing makes the pure-math center look 1px down/left - nudged - badge.letter.rect.x = x + (UiTheme::BadgeOuterSize - badge.letter.rect.w)/2 + 1; - badge.letter.rect.y = y + (UiTheme::BadgeOuterSize - badge.letter.rect.h)/2 - 1; - badge.letter.Draw(renderer); - badge.label.rect.x = x + UiTheme::BadgeOuterSize + UiTheme::BadgeLabelGap; - badge.label.rect.y = y + (UiTheme::BadgeOuterSize - badge.label.rect.h)/2; - badge.label.Draw(renderer); - return x - UiTheme::BadgeGroupGap; - }; - - // hold-to-delete hint, different color than the real B badge - Badge badgeHold{ Texture("B", 16, renderer, 0, 0, false, UiTheme::BadgeLetterColor, true), Texture(Translate("HINT_DELETE"), 16, renderer, 0, 0, false, UiTheme::TextColor, true), UiTheme::BadgeXDark, UiTheme::BadgeX }; - - // true if deleted - caller should break out of the main loop - auto ConfirmDelete = [&]() -> bool - { - const std::string & confirmKey = commands[currentCommandId].deleteConfirmKey; - Texture confirmTitle(Translate(confirmKey.empty() ? "DELETE_CONFIRM_GENERIC" : confirmKey), 24, renderer, 640, 320, true, UiTheme::TextColor, true); - Texture confirmHint(Translate("DELETE_CONFIRM_HINT"), 16, renderer, 640, 360, true, UiTheme::TextColor, true); - controller.GetButtonStatus(B); // consume the still-held B from the triggering long-press - bool confirmed = false; - for(;;) - { - controller.Update(); - if(controller.GetButtonStatus(B)) - break; - if(controller.GetButtonStatus(A) || controller.GetButtonStatus(START)) - { - confirmed = true; - break; - } - sdl_context.StartFrame(); - DrawFillRect(renderer, UiTheme::FrameRect, UiTheme::BgR, UiTheme::BgG, UiTheme::BgB); - DrawStrokeRect(renderer, UiTheme::FrameRect, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BorderRadius); - confirmTitle.Draw(renderer); - confirmHint.Draw(renderer); - SDL_SetRenderDrawColor(renderer, bgR, bgG, bgB, 0xFF); // flat helpers leave the draw color dirty - sdl_context.EndFrame(); - } - if(!confirmed) - return false; - system(commands[currentCommandId].deleteCommand.c_str()); - return true; - }; - - const unsigned int bHoldThresholdMs = 1000; - bool bWasHeld = false, bHoldFired = false; - - // shared chrome; section title is separate since grid positions it lower - auto DrawChromeCommon = [&]() - { - DrawStrokeRect(renderer, UiTheme::OuterRect, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BorderRadius); - gearIcon.Draw(renderer); - appTitleText.Draw(renderer); - appVersionText.Draw(renderer); - DrawHLine(renderer, UiTheme::HeaderDividerX, UiTheme::HeaderDividerX + UiTheme::HeaderDividerW, UiTheme::HeaderDividerY, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth); - DrawHLine(renderer, UiTheme::OuterRect.x, UiTheme::OuterRect.x + UiTheme::OuterRect.w, UiTheme::FooterDividerY, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth); - DrawBadge(badgeA, UiTheme::BadgeClusterRightX); - creditText.Draw(renderer); - }; - auto DrawSectionTitle = [&]() - { - int accentBarY = titleText.rect.y + (titleText.rect.h - UiTheme::SectionAccentBarH) / 2; - DrawFillRect(renderer, { UiTheme::SectionTitleX - UiTheme::SectionAccentBarW - 14, accentBarY, UiTheme::SectionAccentBarW, UiTheme::SectionAccentBarH }, UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB); - titleText.Draw(renderer); - }; - - if(gridLayout) - { - // ============================= GRID LAYOUT ============================= - // squareTiles: pack more/narrower columns for square-ish previews, - // unless the category overrides the column count itself - const bool squareTiles = themeStart < pinnedStartIndex && commands[themeStart].previewSquare; - const int explicitCols = themeStart < pinnedStartIndex ? commands[themeStart].previewGridCols : -1; - const int GridCols = explicitCols > 0 ? explicitCols : (squareTiles ? 7 : 4); - // a screen where every tile hides its label (PREVIEW_HIDE_LABEL) needs - // no caption row at all - checked once here since it's uniform per screen - const bool hasCaptions = themeStart < pinnedStartIndex && !commands[themeStart].previewHideLabel; - const int GridGap = 20; - - // strip = fixed actions [0,themeStart). An empty entry stays a - // reserved-width slot rather than being compacted out, so a - // conditional button appearing/disappearing doesn't shift the rest. - // stripSlots drives layout; stripIndices is the navigable subset - - // the D-pad skips blank slots. Back/Exit aren't chips here - hinted - // in the footer instead (badgeB) - std::vector stripSlots; - for(int i = 0; i < themeStart; ++i) - stripSlots.push_back(i); - const int stripSlotCount = static_cast(stripSlots.size()); - std::vector stripIndices; - for(int i : stripSlots) - if(!commands[i].command.empty()) - stripIndices.push_back(i); - const int stripCount = static_cast(stripIndices.size()); - auto StripToCommand = [&](int pos) -> int { return stripIndices[pos]; }; - auto CommandToStrip = [&](int idx) -> int - { - for(int p = 0; p < stripCount; ++p) - if(stripIndices[p] == idx) - return p; - return 0; - }; - const int chipGap = 14; - const int chipIconSize = 22; - - // no strip on a pure-tile screen (e.g. a DIY category) - use the row for a 3rd grid row - const bool hasStrip = stripSlotCount > 0; - const int StripTop = UiTheme::HeaderDividerY + 22; - const int StripH = hasStrip ? 58 : 0; - const int GridLeft = UiTheme::FrameX + 32; - const int GridRight = UiTheme::FrameX + UiTheme::FrameW - 32; - // capped so a strip with few slots doesn't stretch its chips wide - const int MaxChipW = 200; - const int chipW = hasStrip ? std::min(MaxChipW, (GridRight - GridLeft - (stripSlotCount-1)*chipGap) / stripSlotCount) : 0; - titleText = Texture(Translate(titleKey), UiTheme::SectionTitleFontSize - 6, renderer, UiTheme::SectionTitleX, 0, false, UiTheme::TextColor, true); - titleText.rect.y = hasStrip ? (StripTop + StripH + 22) : (UiTheme::HeaderDividerY + 22); - const int GridTop = titleText.rect.y + titleText.rect.h + 20; - const int GridBottom = UiTheme::FooterDividerY - 14; - // caption sits below the tile's own border, on the plain screen - // background - not overlaid on the art, so no scrim/shadow is - // needed for legibility against busy preview images. Row-to-row - // spacing can be tighter than the column gap since the caption - // itself already reads as a separator between rows. - const int LabelH = hasCaptions ? 18 : 0; - const int RowGap = hasCaptions ? 8 : GridGap; - const int TileW = (GridRight - GridLeft - (GridCols-1)*GridGap) / GridCols; - int TileH, GridRowsVisible; - if(hasStrip) - { - // a bit shorter than a pure 16:9 crop when captions are shown, - // so 2 rows (the usual case at this width) plus their captions - // still fit the same vertical budget captions now share - TileH = hasCaptions ? TileW / 2 : TileW * 9 / 16; - GridRowsVisible = std::max(1, (GridBottom - GridTop + RowGap) / (TileH + LabelH + RowGap)); - } - else - { - GridRowsVisible = 3; - TileH = (GridBottom - GridTop - (GridRowsVisible-1)*RowGap - GridRowsVisible*LabelH) / GridRowsVisible; - } - const int GridRowPitch = TileH + LabelH + RowGap; - - Texture gridScrollUp("^", 16, renderer, GridRight + 22, GridTop + 24, false, UiTheme::TextColor, true); - gridScrollUp.rect.x -= gridScrollUp.rect.w / 2; - Texture gridScrollDown = gridScrollUp; - gridScrollDown.rect.y = GridBottom - 24 - gridScrollDown.rect.h; - - // Back/Exit hint, footer badge next to A's - B runs it directly - Badge badgeB{ Texture("B", 16, renderer, 0, 0, false, UiTheme::BadgeLetterColor, true), - Texture(pinnedStartIndex < (int)commands.size() ? Translate(commands[pinnedStartIndex].name) : "", 16, renderer, 0, 0, false, UiTheme::TextColor, true), - UiTheme::BadgeBDark, UiTheme::BadgeB }; - - std::vector chipIcons(commands.size()); - std::vector chipLabels(commands.size()); - std::vector tileImages(commands.size()); - std::vector tileLabels(commands.size()); - // sized >1 only for a _run01.png tile with sibling _run02/_run03/... - // frames found alongside it - cycled in the draw loop instead of static - std::vector> tileRunFrames(commands.size()); - std::vector tileHideLabel(commands.size(), false); // PREVIEW_HIDE_LABEL - std::vector tileFitContain(commands.size(), false); // PREVIEW_FIT_CONTAIN - for(int i = 0; i < (int)commands.size(); ++i) - { - Command & c = commands[i]; - if(c.command.empty()) - continue; // reserved blank slot, not a chip or tile - std::string label = Translate(c.name); - if(i < themeStart || i >= pinnedStartIndex) - { - int labelAvail = chipW - chipIconSize - 24; - if(c.previewImage.size()) - { - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); // smooth - these are small glyphs, nearest looks jagged - Texture icon(c.previewImage, renderer, 0, 0); - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); - FitCentered(icon, 0, 0, chipIconSize, chipIconSize, 0); - SDL_SetTextureColorMod(icon.texture.get(), UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB); - chipIcons[i] = icon; - labelAvail = chipW - chipIconSize - 32; - } - label = TruncateToWidth(label, 14, labelAvail); - chipLabels[i] = Texture(label, 14, renderer, 0, 0, false, UiTheme::TextColor, true); - } - else - { - // tile image loaded lazily, see EnsureTileImage below - - // overlaid on the image's scrim, left-aligned with margins - // on both sides (see the draw loop) - label = TruncateToWidth(label, 15, TileW - 28); - tileLabels[i] = Texture(label, 15, renderer, 0, 0, false, UiTheme::TextColor, true); - } - } - std::vector tileImageLoaded(commands.size(), false); - auto EnsureTileImage = [&](int idx) - { - if(tileImageLoaded[idx]) - return; - tileImageLoaded[idx] = true; - const Command & c = commands[idx]; - if(c.previewImage.empty()) - return; - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, c.previewNearest ? "0" : "1"); - Texture art(c.previewImage, renderer, 0, 0); - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); - - tileHideLabel[idx] = c.previewHideLabel; - tileFitContain[idx] = c.previewFitContain; - - // run-cycle animation only applies to a _run01.png sprite - this - // is a naming convention to discover however many sibling frames - // a theme shipped, not what drives previewFitContain/HideLabel - const std::string runSuffix = "_run01.png"; - bool isRunCycle = c.previewImage.size() > runSuffix.size() && - c.previewImage.compare(c.previewImage.size() - runSuffix.size(), runSuffix.size(), runSuffix) == 0; - - if(tileFitContain[idx]) - FitCentered(art, 0, 0, TileW, TileH, 0); - else - FitCover(art, 0, 0, TileW, TileH, 0); - tileImages[idx] = art; - - if(isRunCycle) - { - // probe for however many _run02/_run03/... siblings a theme - // shipped, cycled at 100ms/frame in the draw loop below - std::string base = c.previewImage.substr(0, c.previewImage.size() - runSuffix.size()) + "_run"; - std::vector frames{ art }; - for(int n = 2; n <= 8; ++n) - { - std::string framePath = base + (n < 10 ? "0" : "") + std::to_string(n) + ".png"; - if(!std::ifstream(framePath).good()) - break; - Texture frame(framePath, renderer, 0, 0); - FitCentered(frame, 0, 0, TileW, TileH, 0); - frames.push_back(frame); - } - if(frames.size() > 1) - tileRunFrames[idx] = std::move(frames); - } - }; - - int gridTopRow = 0; - auto SetCurrentCommand = [&](int newId) - { - currentCommandId = newId; - if(newId >= themeStart && newId < pinnedStartIndex) - { - int row = (newId - themeStart) / GridCols; - if(row < gridTopRow) gridTopRow = row; - else if(row >= gridTopRow + GridRowsVisible) gridTopRow = row - GridRowsVisible + 1; - } - }; - SetCurrentCommand(themeStart < pinnedStartIndex ? themeStart : (stripCount > 0 ? StripToCommand(0) : 0)); - - for(;;) - { - sdl_context.StartFrame(); - controller.Update(); - - SDL_Event e; - while(SDL_PollEvent(&e)) - if(e.type == SDL_QUIT) - return 0; - - if(sdl_context.powerwatch->buttonPress()) - break; - - bool inGrid = currentCommandId >= themeStart && currentCommandId < pinnedStartIndex; - - if(controller.GetButtonStatus(A) || controller.GetButtonStatus(START)) - { - if(commands[currentCommandId].runInternal) - { - commands[currentCommandId].RunCommand(sdl_context, &controller, { gearIcon, appTitleText, appVersionText, creditText }, bgR, bgG, bgB); - if(commands[currentCommandId].isToggle) - commands[currentCommandId].UpdateState(); - } - else - { - system(commands[currentCommandId].command.c_str()); - break; - } - } - else if(controller.HeldRepeat(LEFT)) - { - if(inGrid && currentCommandId > themeStart) - SetCurrentCommand(currentCommandId - 1); - else if(!inGrid && stripCount > 0) - SetCurrentCommand(StripToCommand(std::max(0, CommandToStrip(currentCommandId) - 1))); - } - else if(controller.HeldRepeat(RIGHT)) - { - if(inGrid && currentCommandId < pinnedStartIndex - 1) - SetCurrentCommand(currentCommandId + 1); - else if(!inGrid && stripCount > 0) - SetCurrentCommand(StripToCommand(std::min(stripCount - 1, CommandToStrip(currentCommandId) + 1))); - } - else if(controller.HeldRepeat(UP)) - { - if(inGrid) - { - int col = (currentCommandId - themeStart) % GridCols; - if(currentCommandId - themeStart < GridCols) // top grid row - jump up to the strip - { - if(stripCount > 0) - SetCurrentCommand(StripToCommand(std::min(stripCount - 1, col))); - } - else - SetCurrentCommand(currentCommandId - GridCols); - } - } - else if(controller.HeldRepeat(DOWN)) - { - if(!inGrid && themeStart < pinnedStartIndex) - { - int col = std::min(GridCols - 1, CommandToStrip(currentCommandId)); - SetCurrentCommand(std::min(pinnedStartIndex - 1, themeStart + col)); - } - else if(inGrid && currentCommandId + GridCols < pinnedStartIndex) - SetCurrentCommand(currentCommandId + GridCols); - } - // B tracked outside the else-if chain, independent of pinned Back/Exit - { - bool bHeldNow = controller.PeekButtonStatus(B); - if(bHeldNow) - { - if(!bHoldFired && controller.HeldMillis(B) >= bHoldThresholdMs) - { - bHoldFired = true; - if(!commands[currentCommandId].deleteCommand.empty() && ConfirmDelete()) - break; - } - } - else - { - if(bWasHeld && !bHoldFired && pinnedStartIndex < (int)commands.size()) - { - // quick tap - Back/Exit isn't a chip, B runs it directly - Command & backCmd = commands[pinnedStartIndex]; - if(backCmd.runInternal) - { - backCmd.RunCommand(sdl_context, &controller, { gearIcon, appTitleText, appVersionText, creditText }, bgR, bgG, bgB); - if(backCmd.isToggle) - backCmd.UpdateState(); - } - else - { - system(backCmd.command.c_str()); - break; - } - } - bHoldFired = false; - } - bWasHeld = bHeldNow; - } - - DrawChromeCommon(); - DrawSectionTitle(); - { - int rightEdge = UiTheme::BadgeClusterRightX - UiTheme::BadgeOuterSize - UiTheme::BadgeLabelGap - badgeA.label.rect.w - UiTheme::BadgeGroupGap; - if(pinnedStartIndex < (int)commands.size()) - rightEdge = DrawBadge(badgeB, rightEdge); - if(!commands[currentCommandId].deleteCommand.empty()) - DrawBadge(badgeHold, rightEdge); - } - - // action strip - { - int x = GridLeft; - for(int pos = 0; pos < stripSlotCount; ++pos) - { - int idx = stripSlots[pos]; - if(commands[idx].command.empty()) - { - x += chipW + chipGap; // reserved gap - just leave it blank - continue; - } - bool selected = idx == currentCommandId; - SDL_Rect chipRect{ x, StripTop, chipW, StripH }; - if(selected) - { - DrawRoundedFillRect(renderer, chipRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); - DrawStrokeRect(renderer, chipRect, UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB, 2, UiTheme::BoxRadius); - } - else - DrawStrokeRect(renderer, chipRect, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BoxRadius); - - // icon+label centered as one group, not each alone - Texture & icon = chipIcons[idx]; - Texture & label = chipLabels[idx]; - int iconW = icon.rect.h > 0 ? chipIconSize : 0; - int groupW = iconW + (iconW > 0 ? 12 : 0) + label.rect.w; - int gx = x + (chipW - groupW) / 2; - if(icon.rect.h > 0) - { - icon.rect.x = gx + (chipIconSize - icon.rect.w) / 2; - icon.rect.y = StripTop + (StripH - icon.rect.h) / 2; - icon.Draw(renderer); - gx += iconW + 12; - } - label.rect.x = gx; - label.rect.y = StripTop + (StripH - label.rect.h) / 2; - label.Draw(renderer); - - x += chipW + chipGap; - } - } - - // theme/asset grid - { - int lastThemeIndex = pinnedStartIndex - 1; - for(int row = 0; row < GridRowsVisible; ++row) - { - int rowStart = themeStart + (gridTopRow+row)*GridCols; - if(rowStart > lastThemeIndex) - break; - int y = GridTop + row*GridRowPitch; - for(int col = 0; col < GridCols; ++col) - { - int idx = rowStart + col; - if(idx > lastThemeIndex) - break; - if(commands[idx].command.empty()) - continue; // reserved blank cell, not a tile - int x = GridLeft + col*(TileW+GridGap); - bool selected = idx == currentCommandId; - SDL_Rect tileRect{ x, y, TileW, TileH }; - DrawRoundedFillRect(renderer, tileRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); - EnsureTileImage(idx); - std::vector & runFrames = tileRunFrames[idx]; - Texture & tileArt = runFrames.size() > 1 - ? runFrames[(SDL_GetTicks() / 100) % runFrames.size()] - : tileImages[idx]; - if(tileArt.rect.w > 0) - { - // clip to the tile so art doesn't bleed into - // neighboring tiles, then mask the clip's square - // corners back to rounded - SDL_RenderSetClipRect(renderer, &tileRect); - if(tileFitContain[idx]) - // 10px margin keeps clear of the rounded-corner mask below - FitCentered(tileArt, x, y, TileW, TileH, 10); - else - FitCover(tileArt, x, y, TileW, TileH, 0); - tileArt.Draw(renderer); - SDL_RenderSetClipRect(renderer, nullptr); - DrawRoundedCornerMask(renderer, tileRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); - } - DrawStrokeRect(renderer, tileRect, selected ? UiTheme::AccentR : UiTheme::BorderR, selected ? UiTheme::AccentG : UiTheme::BorderG, selected ? UiTheme::AccentB : UiTheme::BorderB, selected ? 3 : UiTheme::BorderWidth, UiTheme::BoxRadius); - if(!tileHideLabel[idx]) - { - // caption below the tile's own border, centered - - // on the plain screen background, not the art - tileLabels[idx].rect.x = x + (TileW - tileLabels[idx].rect.w) / 2; - tileLabels[idx].rect.y = y + TileH + (LabelH - tileLabels[idx].rect.h) / 2; - tileLabels[idx].Draw(renderer); - } - } - } - - int totalRows = (lastThemeIndex - themeStart + GridCols) / GridCols; - if(gridTopRow > 0) - gridScrollUp.Draw(renderer); - if(gridTopRow + GridRowsVisible < totalRows) - gridScrollDown.Draw(renderer, SDL_FLIP_VERTICAL); - } - - SDL_SetRenderDrawColor(renderer, bgR, bgG, bgB, 0xFF); - sdl_context.EndFrame(); - } - - return 0; - } - - // ============================= LIST LAYOUT ============================= - Texture scrollUp("^", 16, renderer, UiTheme::ScrollX, UiTheme::ScrollUpY, false, UiTheme::TextColor, true); - scrollUp.rect.x -= scrollUp.rect.w / 2; - Texture scrollDown = scrollUp; - scrollDown.rect.y = UiTheme::ScrollDownY; - SDL_Rect selectedRowRect{ UiTheme::ListX, UiTheme::RowFirstY - 2, UiTheme::ListContentRightX - UiTheme::ListX, 0 }; - - const int RowGlyphSize = 16; - const int RowTextGapPx = 16; - const int ChildIndent = 4*16; - for(Command & c : commands) - { - int textX = UiTheme::RowTextX + (c.child ? ChildIndent : 0); - std::string label = TruncateToWidth(Translate(c.name), RowGlyphSize, UiTheme::RowControlRightX - RowTextGapPx - textX); - c.texture = Texture(label, RowGlyphSize, renderer, textX, 0, false, UiTheme::TextColor, true); - } - - const int modernRowPitch = std::max(UiTheme::RowPitch, GetTTFLineHeight(RowGlyphSize)); - int pinnedAreaTop; - { - int slotBottom = UiTheme::FooterDividerY - UiTheme::PinnedBottomMargin; - for(int i = static_cast(commands.size())-1; i >= pinnedStartIndex; --i) - { - int slotTop = slotBottom - modernRowPitch; - commands[i].texture.rect.y = slotTop + (modernRowPitch - commands[i].texture.rect.h) / 2 + UiTheme::RowTextYNudge; - slotBottom = slotTop; - } - pinnedAreaTop = slotBottom; - } - const int DisplayItemCount = std::max(1, (pinnedAreaTop - UiTheme::RowFirstY) / modernRowPitch); - - int topListItemNumber = 1; // forces SetCurrentCommand(0) below to lay out row Y positions the first time - std::shared_ptr PreviewImage; - auto SetCurrentCommand = [&] (int newCommandId) - { - currentCommandId = newCommandId; - const Command & currentCommand = commands[currentCommandId]; - - // pinned rows don't affect scroll position - int scrollTarget = std::min(currentCommandId, std::max(0, pinnedStartIndex-1)); - - bool updateCommandYPos = false; - if(scrollTarget < topListItemNumber) - { - topListItemNumber = scrollTarget; - updateCommandYPos = true; - } - else if(scrollTarget >= topListItemNumber+DisplayItemCount) - { - topListItemNumber = scrollTarget-DisplayItemCount+1; - updateCommandYPos = true; - } - if(updateCommandYPos) - { - int y = UiTheme::RowFirstY; - for(int i = 0, count = std::min(DisplayItemCount, pinnedStartIndex-topListItemNumber); i < count; ++i) - { - Texture & rowTexture = commands[i+topListItemNumber].texture; - rowTexture.rect.y = y + (modernRowPitch - rowTexture.rect.h) / 2 + UiTheme::RowTextYNudge; - y += modernRowPitch; - } - } - - selectedRowRect.y = currentCommand.texture.rect.y - 2; - selectedRowRect.h = currentCommand.texture.rect.h + 4; - - if(currentCommand.previewImage.size()) - { - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, currentCommand.previewNearest ? "0" : "1"); - PreviewImage = std::make_shared(currentCommand.previewImage, renderer, 0, 0); - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); - FitCentered(*PreviewImage, UiTheme::DetailX, UiTheme::PreviewBoxY, UiTheme::DetailW, UiTheme::PreviewBoxH, UiTheme::ContentPadding); - } - else - PreviewImage.reset(); - }; - SetCurrentCommand(0); - - auto DrawRow = [&](Command & rowCommand, bool isLastOverall) - { - bool selected = &rowCommand == &commands[currentCommandId]; - rowCommand.texture.Draw(renderer); - if(rowCommand.isToggle) - { - Texture & rowSwitch = rowCommand.stateOn ? switchOn : switchOff; - rowSwitch.rect.x = UiTheme::SwitchRightX - UiTheme::SwitchW; - rowSwitch.rect.y = rowCommand.texture.rect.y + (rowCommand.texture.rect.h - UiTheme::SwitchH) / 2; - rowSwitch.Draw(renderer); - } - // skip for separators and the last row - nothing to separate there - if(!selected && !rowCommand.command.empty() && !isLastOverall) - DrawHLine(renderer, UiTheme::ListX, UiTheme::ListContentRightX, rowCommand.texture.rect.y + rowCommand.texture.rect.h + 3, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB); - }; - - auto DrawChrome = [&]() - { - DrawChromeCommon(); - DrawSectionTitle(); - if(!commands[currentCommandId].deleteCommand.empty()) - DrawBadge(badgeHold, UiTheme::BadgeClusterRightX - UiTheme::BadgeOuterSize - UiTheme::BadgeLabelGap - badgeA.label.rect.w - UiTheme::BadgeGroupGap); - - DrawRoundedFillRect(renderer, selectedRowRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); - DrawStrokeRect(renderer, selectedRowRect, UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB, 2, UiTheme::BoxRadius); - - if(PreviewImage.get()) - { - SDL_Rect previewBox{ UiTheme::DetailX, UiTheme::PreviewBoxY, UiTheme::DetailW, UiTheme::PreviewBoxH }; - DrawStrokeRect(renderer, previewBox, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BoxRadius); - PreviewImage->Draw(renderer); - } - }; - - for(;;) - { - sdl_context.StartFrame(); - controller.Update(); - - SDL_Event e; - while(SDL_PollEvent(&e)) - if(e.type == SDL_QUIT) - return 0; - - if(sdl_context.powerwatch->buttonPress()) - break; - - if(controller.GetButtonStatus(A) || controller.GetButtonStatus(START)) - { - if(commands[currentCommandId].runInternal) - { - commands[currentCommandId].RunCommand(sdl_context, &controller, { gearIcon, appTitleText, appVersionText, creditText }, bgR, bgG, bgB); - if(commands[currentCommandId].isToggle) - commands[currentCommandId].UpdateState(); - } - else - { - system(commands[currentCommandId].command.c_str()); - break; - } - } - else if(controller.HeldRepeat(UP)) - { - // bounded so an all-separator list can't spin forever - int newCommandId = currentCommandId; - for(size_t tries = 0; tries < commands.size(); ++tries) - { - newCommandId = (newCommandId-1+commands.size())%commands.size(); - if(commands[newCommandId].command.size() != 0) - break; - } - SetCurrentCommand(newCommandId); - } - else if(controller.HeldRepeat(DOWN)) - { - int newCommandId = currentCommandId; - for(size_t tries = 0; tries < commands.size(); ++tries) - { - newCommandId = (newCommandId+1)%commands.size(); - if(commands[newCommandId].command.size() != 0) - break; - } - SetCurrentCommand(newCommandId); - } - // B tracked outside the else-if chain: tap = last row, hold ~1s = delete - bool bHeldNow = controller.PeekButtonStatus(B); - if(bHeldNow) - { - if(!bHoldFired && controller.HeldMillis(B) >= bHoldThresholdMs) - { - bHoldFired = true; - if(!commands[currentCommandId].deleteCommand.empty() && ConfirmDelete()) - break; - } - } - else - { - if(bWasHeld && !bHoldFired) - SetCurrentCommand(commands.size()-1); - bHoldFired = false; - } - bWasHeld = bHeldNow; - - DrawChrome(); - - int lastCommandIndex = static_cast(commands.size()) - 1; - for(int i = 0, count = std::min(DisplayItemCount, pinnedStartIndex-topListItemNumber); i < count; ++i) - DrawRow(commands[i+topListItemNumber], (i+topListItemNumber) == lastCommandIndex); - - // pinned trailing rows (Back/Exit) - always visible near the footer - for(int i = pinnedStartIndex; i <= lastCommandIndex; ++i) - DrawRow(commands[i], i == lastCommandIndex); - - // bounded by pinnedStartIndex, not commands.size() - pinned rows need no scroll - if(topListItemNumber != 0) - scrollUp.Draw(renderer); - if((topListItemNumber + DisplayItemCount) < pinnedStartIndex) - scrollDown.Draw(renderer, SDL_FLIP_VERTICAL); - - SDL_SetRenderDrawColor(renderer, bgR, bgG, bgB, 0xFF); - - sdl_context.EndFrame(); + std::cerr << "No usable commands in " << options.commandLocation << "\n"; + return 1; } - return 0; + ThemeManagerApp app(optionsLocation, std::move(options), std::move(commands), std::move(isThemeItem)); + return app.Run(); } diff --git a/src/single_instance_lock.cpp b/src/single_instance_lock.cpp new file mode 100644 index 0000000..1b767b7 --- /dev/null +++ b/src/single_instance_lock.cpp @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#include "single_instance_lock.h" + +#include +#include +#include +#include +#include + +void SingleInstanceLock::Acquire() +{ + static SingleInstanceLock instance; + (void)instance; +} + +SingleInstanceLock::SingleInstanceLock() +{ + TakeOverStaleInstance(); + std::ofstream(LockPath, std::ios::trunc) << getpid(); + std::signal(SIGTERM, &SingleInstanceLock::HandleSigTerm); +} + +SingleInstanceLock::~SingleInstanceLock() { std::remove(LockPath); } + +void SingleInstanceLock::TakeOverStaleInstance() +{ + std::ifstream in(LockPath); + pid_t oldPid = 0; + if(in.good()) + in >> oldPid; + in.close(); + if(oldPid <= 0 || oldPid == getpid() || kill(oldPid, 0) != 0) + return; + kill(oldPid, SIGTERM); + for(int i = 0; i < 40 && kill(oldPid, 0) == 0; ++i) + usleep(50000); + if(kill(oldPid, 0) == 0) + kill(oldPid, SIGKILL); +} + +void SingleInstanceLock::HandleSigTerm(int) { std::exit(0); } diff --git a/src/single_instance_lock.h b/src/single_instance_lock.h new file mode 100644 index 0000000..a3a173e --- /dev/null +++ b/src/single_instance_lock.h @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef SINGLE_INSTANCE_LOCK_H_ +#define SINGLE_INSTANCE_LOCK_H_ + +// Keeps one theme_manager on /dev/fb0, taking over a stale instance instead of rejecting the new launch. +class SingleInstanceLock +{ +public: + SingleInstanceLock(const SingleInstanceLock &) = delete; + SingleInstanceLock & operator=(const SingleInstanceLock &) = delete; + + // The lock lives in a function-local static (not a stack local) so its + // destructor still runs from HandleSigTerm's std::exit() - std::exit() + // only unwinds static-storage objects, not automatic ones. + static void Acquire(); + +private: + static constexpr const char * LockPath = "/tmp/theme_manager.lock"; + + SingleInstanceLock(); + ~SingleInstanceLock(); + + static void TakeOverStaleInstance(); + static void HandleSigTerm(int); // runs ~SingleInstanceLock() during cleanup +}; + +#endif diff --git a/src/texture_utils.cpp b/src/texture_utils.cpp new file mode 100644 index 0000000..b99ff71 --- /dev/null +++ b/src/texture_utils.cpp @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#include "texture_utils.h" +#include "framework/utf8.h" + +#include + +namespace { + +void FitScaled(Texture & tex, int boxX, int boxY, int boxW, int boxH, int padding, bool cover) +{ + const int maxW = boxW - 2*padding; + const int maxH = boxH - 2*padding; + if(tex.rect.w <= 0 || tex.rect.h <= 0) + return; + const double scaleW = static_cast(maxW) / tex.rect.w; + const double scaleH = static_cast(maxH) / tex.rect.h; + const double scale = cover ? std::max(scaleW, scaleH) : std::min(scaleW, scaleH); + tex.rect.w = static_cast(tex.rect.w * scale); + tex.rect.h = static_cast(tex.rect.h * scale); + tex.rect.x = boxX + (boxW - tex.rect.w) / 2; + tex.rect.y = boxY + (boxH - tex.rect.h) / 2; +} + +} // namespace + +void FitCentered(Texture & tex, int boxX, int boxY, int boxW, int boxH, int padding) +{ + FitScaled(tex, boxX, boxY, boxW, boxH, padding, false); +} + +void FitCover(Texture & tex, int boxX, int boxY, int boxW, int boxH, int padding) +{ + FitScaled(tex, boxX, boxY, boxW, boxH, padding, true); +} + +std::string TruncateToWidth(const std::string & text, int glyphSize, int available) +{ + std::string label = text; + if(CanRenderWithTTF(label, glyphSize)) + { + if(MeasureTTFWidth(label, glyphSize) > available) + { + int n = Utf8Length(label); + while(n > 0 && MeasureTTFWidth(TruncateUtf8(label, n) + "...", glyphSize) > available) + --n; + label = TruncateUtf8(label, n) + "..."; + } + } + else + { + int maxChars = available / glyphSize; + if(TruncateUtf8(label, maxChars).size() != label.size()) + label = TruncateUtf8(label, std::max(0, maxChars - 3)) + "..."; + } + return label; +} diff --git a/src/texture_utils.h b/src/texture_utils.h new file mode 100644 index 0000000..bec932a --- /dev/null +++ b/src/texture_utils.h @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef TEXTURE_UTILS_H_ +#define TEXTURE_UTILS_H_ + +#include "framework/sdl_helper.h" + +#include + +void FitCentered(Texture & tex, int boxX, int boxY, int boxW, int boxH, int padding); // letterboxed +void FitCover(Texture & tex, int boxX, int boxY, int boxW, int boxH, int padding); // cropped +std::string TruncateToWidth(const std::string & text, int glyphSize, int available); + +#endif diff --git a/src/theme_manager_app.cpp b/src/theme_manager_app.cpp new file mode 100644 index 0000000..52cb753 --- /dev/null +++ b/src/theme_manager_app.cpp @@ -0,0 +1,725 @@ +/** + * Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#include "theme_manager_app.h" +#include "texture_utils.h" +#include "framework/draw_helpers.h" +#include "framework/powerwatch.h" +#include "localization.h" + +#include +#include +#include +#include +#include + +#ifndef MOD_VERSION +#define MOD_VERSION "dev" +#endif + +ThemeManagerApp::ThemeManagerApp(std::string optionsLocation, AppOptions options, std::vector commands, std::vector isThemeItem) + : optionsLocation_(std::move(optionsLocation)) + , options_(std::move(options)) + , commands_(std::move(commands)) + , isThemeItem_(std::move(isThemeItem)) +{ + fprintf(stderr, "CHECKPOINT 1: commands loaded, count=%zu\n", commands_.size()); fflush(stderr); + + // std::make_unique is C++14; this project builds as C++11 + sdlContext_.reset(new SDL_Context(std::chrono::milliseconds(33), false)); + fprintf(stderr, "CHECKPOINT 2: sdl_context created\n"); fflush(stderr); + renderer_ = sdlContext_->renderer; + controller_.reset(new Controller(1)); + fprintf(stderr, "CHECKPOINT 3: controller created\n"); fflush(stderr); + + bgR_ = UiTheme::BgR; + bgG_ = UiTheme::BgG; + bgB_ = UiTheme::BgB; + SDL_SetRenderDrawColor(renderer_, bgR_, bgG_, bgB_, 0xFF); + + gearIcon_ = Texture(optionsLocation_ + UiTheme::AssetGear, renderer_, UiTheme::GearX, UiTheme::GearY); + switchOn_ = Texture(optionsLocation_ + UiTheme::AssetSwitchOn, renderer_); + switchOff_ = Texture(optionsLocation_ + UiTheme::AssetSwitchOff, renderer_); + + appTitleText_ = Texture("Theme Manager", UiTheme::TitleFontSize, renderer_, UiTheme::TitleX, UiTheme::TitleY, false, UiTheme::TextColor, true); + appTitleText_.rect.y -= appTitleText_.rect.h / 2; + appVersionText_ = Texture(MOD_VERSION, UiTheme::VersionFontSize, renderer_, appTitleText_.rect.x + appTitleText_.rect.w + UiTheme::VersionGap, UiTheme::TitleY, false, UiTheme::TextColor, true); + appVersionText_.rect.y -= appVersionText_.rect.h / 2; + titleText_ = Texture(Translate(options_.titleKey), UiTheme::SectionTitleFontSize, renderer_, UiTheme::SectionTitleX, UiTheme::SectionTitleY, false, UiTheme::TextColor, true); + creditText_ = Texture("Theme Manager - by DefKorns", 16, renderer_, UiTheme::CreditX, UiTheme::CreditY, false, UiTheme::TextColor, true); + fprintf(stderr, "CHECKPOINT 4: basic textures created\n"); fflush(stderr); + + badgeA_ = Badge{ Texture("A", 16, renderer_, 0, 0, false, UiTheme::BadgeLetterColor, true), Texture(Translate("HINT_SELECT"), 16, renderer_, 0, 0, false, UiTheme::TextColor, true), UiTheme::BadgeADark, UiTheme::BadgeA }; + badgeRowRightEdge_ = UiTheme::BadgeClusterRightX - UiTheme::BadgeOuterSize - UiTheme::BadgeLabelGap - badgeA_.label.rect.w - UiTheme::BadgeGroupGap; + badgeOuter_ = Texture(optionsLocation_ + UiTheme::AssetBadgeOuter, renderer_); + badgeInner_ = Texture(optionsLocation_ + UiTheme::AssetBadgeInner, renderer_); + badgeHold_ = Badge{ Texture("B", 16, renderer_, 0, 0, false, UiTheme::BadgeLetterColor, true), Texture(Translate("HINT_DELETE"), 16, renderer_, 0, 0, false, UiTheme::TextColor, true), UiTheme::BadgeXDark, UiTheme::BadgeX }; + + ComputePinnedAndThemeRanges(); +} + +void ThemeManagerApp::ComputePinnedAndThemeRanges() +{ + pinnedStartIndex_ = static_cast(commands_.size()); + while(pinnedStartIndex_ > 0 && (commands_[pinnedStartIndex_-1].name == "BACK" || commands_[pinnedStartIndex_-1].name == "EXIT")) + --pinnedStartIndex_; + + themeStart_ = 0; + while(themeStart_ < pinnedStartIndex_ && !isThemeItem_[themeStart_]) + ++themeStart_; +} + +int ThemeManagerApp::DrawBadge(Badge & badge, int rightEdgeX) +{ + int groupW = UiTheme::BadgeOuterSize + UiTheme::BadgeLabelGap + badge.label.rect.w; + int x = rightEdgeX - groupW; + int y = UiTheme::BadgeBandY; + badgeOuter_.rect = { x, y, UiTheme::BadgeOuterSize, UiTheme::BadgeOuterSize }; + SDL_SetTextureColorMod(badgeOuter_.texture.get(), badge.rim.r, badge.rim.g, badge.rim.b); + badgeOuter_.Draw(renderer_); + int innerOffset = (UiTheme::BadgeOuterSize - UiTheme::BadgeInnerSize) / 2; + badgeInner_.rect = { x+innerOffset, y+innerOffset, UiTheme::BadgeInnerSize, UiTheme::BadgeInnerSize }; + SDL_SetTextureColorMod(badgeInner_.texture.get(), badge.fill.r, badge.fill.g, badge.fill.b); + badgeInner_.Draw(renderer_); + // glyph bearing makes the pure-math center look 1px down/left - nudged + badge.letter.rect.x = x + (UiTheme::BadgeOuterSize - badge.letter.rect.w)/2 + 1; + badge.letter.rect.y = y + (UiTheme::BadgeOuterSize - badge.letter.rect.h)/2 - 1; + badge.letter.Draw(renderer_); + badge.label.rect.x = x + UiTheme::BadgeOuterSize + UiTheme::BadgeLabelGap; + badge.label.rect.y = y + (UiTheme::BadgeOuterSize - badge.label.rect.h)/2; + badge.label.Draw(renderer_); + return x - UiTheme::BadgeGroupGap; +} + +// true if deleted - caller should break out of the main loop +bool ThemeManagerApp::ConfirmDelete() +{ + const int DialogCenterX = 640, DialogTitleY = 320, DialogHintY = 360; // screen center (1280x720) + const std::string & confirmKey = commands_[currentCommandId_].deleteConfirmKey; + Texture confirmTitle(Translate(confirmKey.empty() ? "DELETE_CONFIRM_GENERIC" : confirmKey), 24, renderer_, DialogCenterX, DialogTitleY, true, UiTheme::TextColor, true); + Texture confirmHint(Translate("DELETE_CONFIRM_HINT"), 16, renderer_, DialogCenterX, DialogHintY, true, UiTheme::TextColor, true); + controller_->GetButtonStatus(B); // consume the still-held B from the triggering long-press + bool confirmed = false; + for(;;) + { + controller_->Update(); + if(controller_->GetButtonStatus(B)) + break; + if(controller_->GetButtonStatus(A) || controller_->GetButtonStatus(START)) + { + confirmed = true; + break; + } + sdlContext_->StartFrame(); + DrawFillRect(renderer_, UiTheme::FrameRect, UiTheme::BgR, UiTheme::BgG, UiTheme::BgB); + DrawStrokeRect(renderer_, UiTheme::FrameRect, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BorderRadius); + confirmTitle.Draw(renderer_); + confirmHint.Draw(renderer_); + SDL_SetRenderDrawColor(renderer_, bgR_, bgG_, bgB_, 0xFF); // flat helpers leave the draw color dirty + sdlContext_->EndFrame(); + } + if(!confirmed) + return false; + system(commands_[currentCommandId_].deleteCommand.c_str()); + return true; +} + +void ThemeManagerApp::DrawChromeCommon() +{ + DrawStrokeRect(renderer_, UiTheme::OuterRect, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BorderRadius); + gearIcon_.Draw(renderer_); + appTitleText_.Draw(renderer_); + appVersionText_.Draw(renderer_); + DrawHLine(renderer_, UiTheme::HeaderDividerX, UiTheme::HeaderDividerX + UiTheme::HeaderDividerW, UiTheme::HeaderDividerY, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth); + DrawHLine(renderer_, UiTheme::OuterRect.x, UiTheme::OuterRect.x + UiTheme::OuterRect.w, UiTheme::FooterDividerY, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth); + DrawBadge(badgeA_, UiTheme::BadgeClusterRightX); + creditText_.Draw(renderer_); +} + +void ThemeManagerApp::DrawSectionTitle() +{ + int accentBarY = titleText_.rect.y + (titleText_.rect.h - UiTheme::SectionAccentBarH) / 2; + DrawFillRect(renderer_, { UiTheme::SectionTitleX - UiTheme::SectionAccentBarW - 14, accentBarY, UiTheme::SectionAccentBarW, UiTheme::SectionAccentBarH }, UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB); + titleText_.Draw(renderer_); +} + +Texture ThemeManagerApp::MakeScrollArrow(int x, int y) const +{ + Texture arrow("^", 16, renderer_, x, y, false, UiTheme::TextColor, true); + arrow.rect.x -= arrow.rect.w / 2; + return arrow; +} + +ThemeManagerApp::FrameEvent ThemeManagerApp::PollFrameEvents() +{ + sdlContext_->StartFrame(); + controller_->Update(); + + SDL_Event e; + while(SDL_PollEvent(&e)) + if(e.type == SDL_QUIT) + return FrameEvent::Quit; + + if(sdlContext_->powerwatch->buttonPress()) + return FrameEvent::PowerButtonPressed; + + return FrameEvent::Continue; +} + +bool ThemeManagerApp::ActivateCommand(Command & cmd) +{ + if(cmd.runInternal) + { + cmd.RunCommand(*sdlContext_, controller_.get(), { gearIcon_, appTitleText_, appVersionText_, creditText_ }, bgR_, bgG_, bgB_); + if(cmd.isToggle) + cmd.UpdateState(); + return false; + } + system(cmd.command.c_str()); + return true; +} + +bool ThemeManagerApp::UpdateBButton(bool & tapped) +{ + tapped = false; + bool bHeldNow = controller_->PeekButtonStatus(B); + if(bHeldNow) + { + if(!bHoldFired_ && controller_->HeldMillis(B) >= BHoldThresholdMs) + { + bHoldFired_ = true; + if(!commands_[currentCommandId_].deleteCommand.empty() && ConfirmDelete()) + return true; + } + } + else + { + tapped = bWasHeld_ && !bHoldFired_; + bHoldFired_ = false; + } + bWasHeld_ = bHeldNow; + return false; +} + +int ThemeManagerApp::RunGridLayout() +{ + const bool squareTiles = themeStart_ < pinnedStartIndex_ && commands_[themeStart_].previewSquare; + const int explicitCols = themeStart_ < pinnedStartIndex_ ? commands_[themeStart_].previewGridCols : -1; + const int GridCols = explicitCols > 0 ? explicitCols : (squareTiles ? 7 : 4); + const bool hasCaptions = themeStart_ < pinnedStartIndex_ && !commands_[themeStart_].previewHideLabel; + const int GridGap = 20; + + // an empty slot stays reserved-width rather than compacted out, so a + // conditional button appearing/disappearing doesn't shift the rest + std::vector stripSlots; + for(int i = 0; i < themeStart_; ++i) + stripSlots.push_back(i); + const int stripSlotCount = static_cast(stripSlots.size()); + std::vector stripIndices; + for(int i : stripSlots) + if(!commands_[i].command.empty()) + stripIndices.push_back(i); + const int stripCount = static_cast(stripIndices.size()); + auto StripToCommand = [&](int pos) -> int { return stripIndices[pos]; }; + auto CommandToStrip = [&](int idx) -> int + { + for(int p = 0; p < stripCount; ++p) + if(stripIndices[p] == idx) + return p; + return 0; + }; + const int chipGap = 14; + const int chipIconSize = 22; + + const bool hasStrip = stripSlotCount > 0; + const int StripTop = UiTheme::HeaderDividerY + 22; + const int StripH = hasStrip ? 58 : 0; + const int GridLeft = UiTheme::FrameX + 32; + const int GridRight = UiTheme::FrameX + UiTheme::FrameW - 32; + // capped so a strip with few slots doesn't stretch its chips wide + const int MaxChipW = 200; + const int chipW = hasStrip ? std::min(MaxChipW, (GridRight - GridLeft - (stripSlotCount-1)*chipGap) / stripSlotCount) : 0; + titleText_ = Texture(Translate(options_.titleKey), UiTheme::SectionTitleFontSize - 6, renderer_, UiTheme::SectionTitleX, 0, false, UiTheme::TextColor, true); + titleText_.rect.y = hasStrip ? (StripTop + StripH + 22) : (UiTheme::HeaderDividerY + 22); + const int GridTop = titleText_.rect.y + titleText_.rect.h + 20; + const int GridBottom = UiTheme::FooterDividerY - 14; + const int LabelH = hasCaptions ? 18 : 0; + const int RowGap = hasCaptions ? 8 : GridGap; + const int TileW = (GridRight - GridLeft - (GridCols-1)*GridGap) / GridCols; + int TileH, GridRowsVisible; + if(hasStrip) + { + TileH = hasCaptions ? TileW / 2 : TileW * 9 / 16; + GridRowsVisible = std::max(1, (GridBottom - GridTop + RowGap) / (TileH + LabelH + RowGap)); + } + else + { + GridRowsVisible = 3; + TileH = (GridBottom - GridTop - (GridRowsVisible-1)*RowGap - GridRowsVisible*LabelH) / GridRowsVisible; + } + const int GridRowPitch = TileH + LabelH + RowGap; + + Texture gridScrollUp = MakeScrollArrow(GridRight + 22, GridTop + 24); + Texture gridScrollDown = gridScrollUp; + gridScrollDown.rect.y = GridBottom - 24 - gridScrollDown.rect.h; + + // B runs Back/Exit directly - it's a footer badge, not a chip + Badge badgeB{ Texture("B", 16, renderer_, 0, 0, false, UiTheme::BadgeLetterColor, true), + Texture(pinnedStartIndex_ < (int)commands_.size() ? Translate(commands_[pinnedStartIndex_].name) : "", 16, renderer_, 0, 0, false, UiTheme::TextColor, true), + UiTheme::BadgeBDark, UiTheme::BadgeB }; + + std::vector chipIcons(commands_.size()); + std::vector chipLabels(commands_.size()); + std::vector tileImages(commands_.size()); + std::vector tileLabels(commands_.size()); + std::vector> tileRunFrames(commands_.size()); // sized >1 only for an animated _run01.png tile + std::vector tileHideLabel(commands_.size(), false); // PREVIEW_HIDE_LABEL + std::vector tileFitContain(commands_.size(), false); // PREVIEW_FIT_CONTAIN + for(int i = 0; i < (int)commands_.size(); ++i) + { + Command & c = commands_[i]; + if(c.command.empty()) + continue; // reserved blank slot, not a chip or tile + std::string label = Translate(c.name); + if(i < themeStart_ || i >= pinnedStartIndex_) + { + int labelAvail = chipW - chipIconSize - 24; + if(c.previewImage.size()) + { + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); // smooth - these are small glyphs, nearest looks jagged + Texture icon(c.previewImage, renderer_, 0, 0); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + FitCentered(icon, 0, 0, chipIconSize, chipIconSize, 0); + SDL_SetTextureColorMod(icon.texture.get(), UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB); + chipIcons[i] = icon; + labelAvail = chipW - chipIconSize - 32; + } + label = TruncateToWidth(label, 14, labelAvail); + chipLabels[i] = Texture(label, 14, renderer_, 0, 0, false, UiTheme::TextColor, true); + } + else + { + label = TruncateToWidth(label, 15, TileW - 28); + tileLabels[i] = Texture(label, 15, renderer_, 0, 0, false, UiTheme::TextColor, true); + } + } + std::vector tileImageLoaded(commands_.size(), false); + auto EnsureTileImage = [&](int idx) + { + if(tileImageLoaded[idx]) + return; + tileImageLoaded[idx] = true; + const Command & c = commands_[idx]; + if(c.previewImage.empty()) + return; + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, c.previewNearest ? "0" : "1"); + Texture art(c.previewImage, renderer_, 0, 0); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + + tileHideLabel[idx] = c.previewHideLabel; + tileFitContain[idx] = c.previewFitContain; + + // _run01.png with sibling _run02/_run03/... is this project's naming + // convention for a multi-frame run-cycle animation + const std::string runSuffix = "_run01.png"; + bool isRunCycle = c.previewImage.size() > runSuffix.size() && + c.previewImage.compare(c.previewImage.size() - runSuffix.size(), runSuffix.size(), runSuffix) == 0; + + if(tileFitContain[idx]) + FitCentered(art, 0, 0, TileW, TileH, 0); + else + FitCover(art, 0, 0, TileW, TileH, 0); + tileImages[idx] = art; + + if(isRunCycle) + { + std::string base = c.previewImage.substr(0, c.previewImage.size() - runSuffix.size()) + "_run"; + std::vector frames{ art }; + for(int n = 2; n <= 8; ++n) + { + std::string framePath = base + (n < 10 ? "0" : "") + std::to_string(n) + ".png"; + if(!std::ifstream(framePath).good()) + break; + Texture frame(framePath, renderer_, 0, 0); + FitCentered(frame, 0, 0, TileW, TileH, 0); + frames.push_back(frame); + } + if(frames.size() > 1) + tileRunFrames[idx] = std::move(frames); + } + }; + + int gridTopRow = 0; + auto SetCurrentCommand = [&](int newId) + { + currentCommandId_ = newId; + if(newId >= themeStart_ && newId < pinnedStartIndex_) + { + int row = (newId - themeStart_) / GridCols; + if(row < gridTopRow) gridTopRow = row; + else if(row >= gridTopRow + GridRowsVisible) gridTopRow = row - GridRowsVisible + 1; + } + }; + SetCurrentCommand(themeStart_ < pinnedStartIndex_ ? themeStart_ : (stripCount > 0 ? StripToCommand(0) : 0)); + + // fixed for the whole screen (only gridTopRow scrolls within them) + const int lastThemeIndex = pinnedStartIndex_ - 1; + const int totalRows = (lastThemeIndex - themeStart_ + GridCols) / GridCols; + + for(;;) + { + FrameEvent frameEvent = PollFrameEvents(); + if(frameEvent == FrameEvent::Quit) + return 0; + if(frameEvent == FrameEvent::PowerButtonPressed) + break; + + bool inGrid = currentCommandId_ >= themeStart_ && currentCommandId_ < pinnedStartIndex_; + + if(controller_->GetButtonStatus(A) || controller_->GetButtonStatus(START)) + { + if(ActivateCommand(commands_[currentCommandId_])) + break; + } + else if(controller_->HeldRepeat(LEFT)) + { + if(inGrid && currentCommandId_ > themeStart_) + SetCurrentCommand(currentCommandId_ - 1); + else if(!inGrid && stripCount > 0) + SetCurrentCommand(StripToCommand(std::max(0, CommandToStrip(currentCommandId_) - 1))); + } + else if(controller_->HeldRepeat(RIGHT)) + { + if(inGrid && currentCommandId_ < pinnedStartIndex_ - 1) + SetCurrentCommand(currentCommandId_ + 1); + else if(!inGrid && stripCount > 0) + SetCurrentCommand(StripToCommand(std::min(stripCount - 1, CommandToStrip(currentCommandId_) + 1))); + } + else if(controller_->HeldRepeat(UP)) + { + if(inGrid) + { + int col = (currentCommandId_ - themeStart_) % GridCols; + if(currentCommandId_ - themeStart_ < GridCols) // top grid row - jump up to the strip + { + if(stripCount > 0) + SetCurrentCommand(StripToCommand(std::min(stripCount - 1, col))); + } + else + SetCurrentCommand(currentCommandId_ - GridCols); + } + } + else if(controller_->HeldRepeat(DOWN)) + { + if(!inGrid && themeStart_ < pinnedStartIndex_) + { + int col = std::min(GridCols - 1, CommandToStrip(currentCommandId_)); + SetCurrentCommand(std::min(pinnedStartIndex_ - 1, themeStart_ + col)); + } + else if(inGrid && currentCommandId_ + GridCols < pinnedStartIndex_) + SetCurrentCommand(currentCommandId_ + GridCols); + } + { + bool tapped = false; + if(UpdateBButton(tapped)) + break; + // quick tap - Back/Exit isn't a chip, B runs it directly + if(tapped && pinnedStartIndex_ < (int)commands_.size() && ActivateCommand(commands_[pinnedStartIndex_])) + break; + } + + DrawChromeCommon(); + DrawSectionTitle(); + { + int rightEdge = badgeRowRightEdge_; + if(pinnedStartIndex_ < (int)commands_.size()) + rightEdge = DrawBadge(badgeB, rightEdge); + if(!commands_[currentCommandId_].deleteCommand.empty()) + DrawBadge(badgeHold_, rightEdge); + } + + { + int x = GridLeft; + for(int pos = 0; pos < stripSlotCount; ++pos) + { + int idx = stripSlots[pos]; + if(commands_[idx].command.empty()) + { + x += chipW + chipGap; + continue; + } + bool selected = idx == currentCommandId_; + SDL_Rect chipRect{ x, StripTop, chipW, StripH }; + if(selected) + { + DrawRoundedFillRect(renderer_, chipRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); + DrawStrokeRect(renderer_, chipRect, UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB, 2, UiTheme::BoxRadius); + } + else + DrawStrokeRect(renderer_, chipRect, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BoxRadius); + + // icon+label centered as one group, not each alone + Texture & icon = chipIcons[idx]; + Texture & label = chipLabels[idx]; + int iconW = icon.rect.h > 0 ? chipIconSize : 0; + int groupW = iconW + (iconW > 0 ? 12 : 0) + label.rect.w; + int gx = x + (chipW - groupW) / 2; + if(icon.rect.h > 0) + { + icon.rect.x = gx + (chipIconSize - icon.rect.w) / 2; + icon.rect.y = StripTop + (StripH - icon.rect.h) / 2; + icon.Draw(renderer_); + gx += iconW + 12; + } + label.rect.x = gx; + label.rect.y = StripTop + (StripH - label.rect.h) / 2; + label.Draw(renderer_); + + x += chipW + chipGap; + } + } + + { + for(int row = 0; row < GridRowsVisible; ++row) + { + int rowStart = themeStart_ + (gridTopRow+row)*GridCols; + if(rowStart > lastThemeIndex) + break; + int y = GridTop + row*GridRowPitch; + for(int col = 0; col < GridCols; ++col) + { + int idx = rowStart + col; + if(idx > lastThemeIndex) + break; + if(commands_[idx].command.empty()) + continue; // reserved blank cell, not a tile + int x = GridLeft + col*(TileW+GridGap); + bool selected = idx == currentCommandId_; + SDL_Rect tileRect{ x, y, TileW, TileH }; + DrawRoundedFillRect(renderer_, tileRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); + EnsureTileImage(idx); + std::vector & runFrames = tileRunFrames[idx]; + Texture & tileArt = runFrames.size() > 1 + ? runFrames[(SDL_GetTicks() / 100) % runFrames.size()] + : tileImages[idx]; + if(tileArt.rect.w > 0) + { + // clip to the tile, then mask the clip's square corners back to rounded + SDL_RenderSetClipRect(renderer_, &tileRect); + if(tileFitContain[idx]) + // 10px margin keeps clear of the rounded-corner mask below + FitCentered(tileArt, x, y, TileW, TileH, 10); + else + FitCover(tileArt, x, y, TileW, TileH, 0); + tileArt.Draw(renderer_); + SDL_RenderSetClipRect(renderer_, nullptr); + DrawRoundedCornerMask(renderer_, tileRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); + } + DrawStrokeRect(renderer_, tileRect, selected ? UiTheme::AccentR : UiTheme::BorderR, selected ? UiTheme::AccentG : UiTheme::BorderG, selected ? UiTheme::AccentB : UiTheme::BorderB, selected ? 3 : UiTheme::BorderWidth, UiTheme::BoxRadius); + if(!tileHideLabel[idx]) + { + tileLabels[idx].rect.x = x + (TileW - tileLabels[idx].rect.w) / 2; + tileLabels[idx].rect.y = y + TileH + (LabelH - tileLabels[idx].rect.h) / 2; + tileLabels[idx].Draw(renderer_); + } + } + } + + if(gridTopRow > 0) + gridScrollUp.Draw(renderer_); + if(gridTopRow + GridRowsVisible < totalRows) + gridScrollDown.Draw(renderer_, SDL_FLIP_VERTICAL); + } + + SDL_SetRenderDrawColor(renderer_, bgR_, bgG_, bgB_, 0xFF); + sdlContext_->EndFrame(); + } + + return 0; +} + +int ThemeManagerApp::RunListLayout() +{ + Texture scrollUp = MakeScrollArrow(UiTheme::ScrollX, UiTheme::ScrollUpY); + Texture scrollDown = scrollUp; + scrollDown.rect.y = UiTheme::ScrollDownY; + SDL_Rect selectedRowRect{ UiTheme::ListX, UiTheme::RowFirstY - 2, UiTheme::ListContentRightX - UiTheme::ListX, 0 }; + + const int RowGlyphSize = 16; + const int RowTextGapPx = 16; + const int ChildIndent = 4*16; + for(Command & c : commands_) + { + int textX = UiTheme::RowTextX + (c.child ? ChildIndent : 0); + std::string label = TruncateToWidth(Translate(c.name), RowGlyphSize, UiTheme::RowControlRightX - RowTextGapPx - textX); + c.texture = Texture(label, RowGlyphSize, renderer_, textX, 0, false, UiTheme::TextColor, true); + } + + const int modernRowPitch = std::max(UiTheme::RowPitch, GetTTFLineHeight(RowGlyphSize)); + int pinnedAreaTop; + { + int slotBottom = UiTheme::FooterDividerY - UiTheme::PinnedBottomMargin; + for(int i = static_cast(commands_.size())-1; i >= pinnedStartIndex_; --i) + { + int slotTop = slotBottom - modernRowPitch; + commands_[i].texture.rect.y = slotTop + (modernRowPitch - commands_[i].texture.rect.h) / 2 + UiTheme::RowTextYNudge; + slotBottom = slotTop; + } + pinnedAreaTop = slotBottom; + } + const int DisplayItemCount = std::max(1, (pinnedAreaTop - UiTheme::RowFirstY) / modernRowPitch); + + int topListItemNumber = 1; // forces SetCurrentCommand(0) below to lay out row Y positions the first time + std::shared_ptr PreviewImage; + auto SetCurrentCommand = [&] (int newCommandId) + { + currentCommandId_ = newCommandId; + const Command & currentCommand = commands_[currentCommandId_]; + + // pinned rows don't affect scroll position + int scrollTarget = std::min(currentCommandId_, std::max(0, pinnedStartIndex_-1)); + + bool updateCommandYPos = false; + if(scrollTarget < topListItemNumber) + { + topListItemNumber = scrollTarget; + updateCommandYPos = true; + } + else if(scrollTarget >= topListItemNumber+DisplayItemCount) + { + topListItemNumber = scrollTarget-DisplayItemCount+1; + updateCommandYPos = true; + } + if(updateCommandYPos) + { + int y = UiTheme::RowFirstY; + for(int i = 0, count = std::min(DisplayItemCount, pinnedStartIndex_-topListItemNumber); i < count; ++i) + { + Texture & rowTexture = commands_[i+topListItemNumber].texture; + rowTexture.rect.y = y + (modernRowPitch - rowTexture.rect.h) / 2 + UiTheme::RowTextYNudge; + y += modernRowPitch; + } + } + + selectedRowRect.y = currentCommand.texture.rect.y - 2; + selectedRowRect.h = currentCommand.texture.rect.h + 4; + + if(currentCommand.previewImage.size()) + { + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, currentCommand.previewNearest ? "0" : "1"); + PreviewImage = std::make_shared(currentCommand.previewImage, renderer_, 0, 0); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + FitCentered(*PreviewImage, UiTheme::DetailX, UiTheme::PreviewBoxY, UiTheme::DetailW, UiTheme::PreviewBoxH, UiTheme::ContentPadding); + } + else + PreviewImage.reset(); + }; + SetCurrentCommand(0); + + auto DrawRow = [&](Command & rowCommand, bool isLastOverall) + { + bool selected = &rowCommand == &commands_[currentCommandId_]; + rowCommand.texture.Draw(renderer_); + if(rowCommand.isToggle) + { + Texture & rowSwitch = rowCommand.stateOn ? switchOn_ : switchOff_; + rowSwitch.rect.x = UiTheme::SwitchRightX - UiTheme::SwitchW; + rowSwitch.rect.y = rowCommand.texture.rect.y + (rowCommand.texture.rect.h - UiTheme::SwitchH) / 2; + rowSwitch.Draw(renderer_); + } + // skip for separators and the last row - nothing to separate there + if(!selected && !rowCommand.command.empty() && !isLastOverall) + DrawHLine(renderer_, UiTheme::ListX, UiTheme::ListContentRightX, rowCommand.texture.rect.y + rowCommand.texture.rect.h + 3, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB); + }; + + auto DrawChrome = [&]() + { + DrawChromeCommon(); + DrawSectionTitle(); + if(!commands_[currentCommandId_].deleteCommand.empty()) + DrawBadge(badgeHold_, badgeRowRightEdge_); + + DrawRoundedFillRect(renderer_, selectedRowRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); + DrawStrokeRect(renderer_, selectedRowRect, UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB, 2, UiTheme::BoxRadius); + + if(PreviewImage.get()) + { + SDL_Rect previewBox{ UiTheme::DetailX, UiTheme::PreviewBoxY, UiTheme::DetailW, UiTheme::PreviewBoxH }; + DrawStrokeRect(renderer_, previewBox, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BoxRadius); + PreviewImage->Draw(renderer_); + } + }; + + const int lastCommandIndex = static_cast(commands_.size()) - 1; // commands_ never resized during this loop + + for(;;) + { + FrameEvent frameEvent = PollFrameEvents(); + if(frameEvent == FrameEvent::Quit) + return 0; + if(frameEvent == FrameEvent::PowerButtonPressed) + break; + + if(controller_->GetButtonStatus(A) || controller_->GetButtonStatus(START)) + { + if(ActivateCommand(commands_[currentCommandId_])) + break; + } + else if(controller_->HeldRepeat(UP)) + { + // bounded so an all-separator list can't spin forever + int newCommandId = currentCommandId_; + for(size_t tries = 0; tries < commands_.size(); ++tries) + { + newCommandId = (newCommandId-1+commands_.size())%commands_.size(); + if(commands_[newCommandId].command.size() != 0) + break; + } + SetCurrentCommand(newCommandId); + } + else if(controller_->HeldRepeat(DOWN)) + { + int newCommandId = currentCommandId_; + for(size_t tries = 0; tries < commands_.size(); ++tries) + { + newCommandId = (newCommandId+1)%commands_.size(); + if(commands_[newCommandId].command.size() != 0) + break; + } + SetCurrentCommand(newCommandId); + } + { + bool tapped = false; + if(UpdateBButton(tapped)) + break; + if(tapped) + SetCurrentCommand(commands_.size()-1); + } + + DrawChrome(); + + for(int i = 0, count = std::min(DisplayItemCount, pinnedStartIndex_-topListItemNumber); i < count; ++i) + DrawRow(commands_[i+topListItemNumber], (i+topListItemNumber) == lastCommandIndex); + + for(int i = pinnedStartIndex_; i <= lastCommandIndex; ++i) + DrawRow(commands_[i], i == lastCommandIndex); + + // bounded by pinnedStartIndex_, not commands_.size() - pinned rows need no scroll + if(topListItemNumber != 0) + scrollUp.Draw(renderer_); + if((topListItemNumber + DisplayItemCount) < pinnedStartIndex_) + scrollDown.Draw(renderer_, SDL_FLIP_VERTICAL); + + SDL_SetRenderDrawColor(renderer_, bgR_, bgG_, bgB_, 0xFF); + + sdlContext_->EndFrame(); + } + + return 0; +} diff --git a/src/theme_manager_app.h b/src/theme_manager_app.h new file mode 100644 index 0000000..8226669 --- /dev/null +++ b/src/theme_manager_app.h @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef THEME_MANAGER_APP_H_ +#define THEME_MANAGER_APP_H_ + +#include "command_loader.h" +#include "command.h" +#include "framework/controller.h" +#include "framework/sdl_helper.h" + +#include +#include +#include + +// Owns the SDL/controller session for one screen and runs its grid or list layout loop. +class ThemeManagerApp +{ +public: + ThemeManagerApp(std::string optionsLocation, AppOptions options, std::vector commands, std::vector isThemeItem); + + int Run() { return options_.gridLayout ? RunGridLayout() : RunListLayout(); } + +private: + struct Badge { Texture letter; Texture label; UiTheme::BadgeColor rim; UiTheme::BadgeColor fill; }; + enum class FrameEvent { Continue, Quit, PowerButtonPressed }; + + std::string optionsLocation_; + AppOptions options_; + std::vector commands_; + std::vector isThemeItem_; + int pinnedStartIndex_ = 0; // BACK/EXIT sort last - pinned near the footer instead of scrolling + int themeStart_ = 0; // grid layout only: first non-fixed-action row + + // unique_ptr so construction can be deferred past the CHECKPOINT logs below + std::unique_ptr sdlContext_; + SDL_Renderer * renderer_ = nullptr; + std::unique_ptr controller_; + int currentCommandId_ = 0; + Uint8 bgR_ = 0, bgG_ = 0, bgB_ = 0; + + Texture gearIcon_, switchOn_, switchOff_; + Texture appTitleText_, appVersionText_, titleText_, creditText_; + Texture badgeOuter_, badgeInner_; + Badge badgeA_; + Badge badgeHold_; + int badgeRowRightEdge_ = 0; // x just left of badge A - fixed once badgeA_ is built + + static constexpr unsigned int BHoldThresholdMs = 1000; + bool bWasHeld_ = false; + bool bHoldFired_ = false; + + void ComputePinnedAndThemeRanges(); + + int DrawBadge(Badge & badge, int rightEdgeX); + bool ConfirmDelete(); + void DrawChromeCommon(); + void DrawSectionTitle(); + Texture MakeScrollArrow(int x, int y) const; + + FrameEvent PollFrameEvents(); + bool ActivateCommand(Command & cmd); // true if the caller's loop should break + bool UpdateBButton(bool & tapped); // true if delete confirmed (caller should break); tapped on a quick tap + + int RunGridLayout(); + int RunListLayout(); +}; + +#endif From 1e8d54d1c0c2191aae2908b694e5a8fb0cddee70 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 23 Sep 2026 12:10:12 +0000 Subject: [PATCH 079/210] Resume the paused game UI on power-button exit from theme_manager to avoid an unclean shutdown (C8) --- src/theme_manager_app.cpp | 46 ++++++++++++++++++++++++++++++++++++--- src/theme_manager_app.h | 6 +++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/theme_manager_app.cpp b/src/theme_manager_app.cpp index 52cb753..c94eef6 100644 --- a/src/theme_manager_app.cpp +++ b/src/theme_manager_app.cpp @@ -156,6 +156,31 @@ Texture ThemeManagerApp::MakeScrollArrow(int x, int y) const return arrow; } +std::string ThemeManagerApp::FocusStatePath() const +{ + std::string sanitized = options_.commandLocation; + for(char & c : sanitized) + if(c == '/') c = '_'; + return "/tmp/om_focus" + sanitized; +} + +void ThemeManagerApp::SaveFocusState() const +{ + std::ofstream(FocusStatePath(), std::ios::trunc) << commands_[currentCommandId_].name; +} + +int ThemeManagerApp::LoadFocusIndex() const +{ + std::ifstream in(FocusStatePath()); + std::string name; + if(!std::getline(in, name) || name.empty()) + return -1; + for(int i = 0; i < static_cast(commands_.size()); ++i) + if(commands_[i].name == name) + return i; + return -1; +} + ThemeManagerApp::FrameEvent ThemeManagerApp::PollFrameEvents() { sdlContext_->StartFrame(); @@ -172,6 +197,11 @@ ThemeManagerApp::FrameEvent ThemeManagerApp::PollFrameEvents() return FrameEvent::Continue; } +void ThemeManagerApp::ResumeUnderlyingUi() const +{ + system(("/bin/sh " + optionsLocation_ + "scripts/ResumeUI.sh").c_str()); +} + bool ThemeManagerApp::ActivateCommand(Command & cmd) { if(cmd.runInternal) @@ -365,8 +395,10 @@ int ThemeManagerApp::RunGridLayout() if(row < gridTopRow) gridTopRow = row; else if(row >= gridTopRow + GridRowsVisible) gridTopRow = row - GridRowsVisible + 1; } + SaveFocusState(); }; - SetCurrentCommand(themeStart_ < pinnedStartIndex_ ? themeStart_ : (stripCount > 0 ? StripToCommand(0) : 0)); + int restoredId = LoadFocusIndex(); + SetCurrentCommand(restoredId >= 0 ? restoredId : (themeStart_ < pinnedStartIndex_ ? themeStart_ : (stripCount > 0 ? StripToCommand(0) : 0))); // fixed for the whole screen (only gridTopRow scrolls within them) const int lastThemeIndex = pinnedStartIndex_ - 1; @@ -378,7 +410,10 @@ int ThemeManagerApp::RunGridLayout() if(frameEvent == FrameEvent::Quit) return 0; if(frameEvent == FrameEvent::PowerButtonPressed) + { + ResumeUnderlyingUi(); break; + } bool inGrid = currentCommandId_ >= themeStart_ && currentCommandId_ < pinnedStartIndex_; @@ -575,7 +610,7 @@ int ThemeManagerApp::RunListLayout() } const int DisplayItemCount = std::max(1, (pinnedAreaTop - UiTheme::RowFirstY) / modernRowPitch); - int topListItemNumber = 1; // forces SetCurrentCommand(0) below to lay out row Y positions the first time + int topListItemNumber = pinnedStartIndex_ + 1; // forces the first SetCurrentCommand to lay out row positions std::shared_ptr PreviewImage; auto SetCurrentCommand = [&] (int newCommandId) { @@ -619,8 +654,10 @@ int ThemeManagerApp::RunListLayout() } else PreviewImage.reset(); + SaveFocusState(); }; - SetCurrentCommand(0); + int restoredId = LoadFocusIndex(); + SetCurrentCommand(restoredId >= 0 ? restoredId : 0); auto DrawRow = [&](Command & rowCommand, bool isLastOverall) { @@ -664,7 +701,10 @@ int ThemeManagerApp::RunListLayout() if(frameEvent == FrameEvent::Quit) return 0; if(frameEvent == FrameEvent::PowerButtonPressed) + { + ResumeUnderlyingUi(); break; + } if(controller_->GetButtonStatus(A) || controller_->GetButtonStatus(START)) { diff --git a/src/theme_manager_app.h b/src/theme_manager_app.h index 8226669..d361617 100644 --- a/src/theme_manager_app.h +++ b/src/theme_manager_app.h @@ -68,6 +68,12 @@ class ThemeManagerApp bool ActivateCommand(Command & cmd); // true if the caller's loop should break bool UpdateBButton(bool & tapped); // true if delete confirmed (caller should break); tapped on a quick tap + void ResumeUnderlyingUi() const; // CONTs the game PauseUI.sh stopped - nothing else does this on power-button exit + + std::string FocusStatePath() const; + void SaveFocusState() const; + int LoadFocusIndex() const; // -1 if none saved + int RunGridLayout(); int RunListLayout(); }; From d5298d66420ff7d8ed4210b6501ab328d364a062 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 23 Sep 2026 12:10:48 +0000 Subject: [PATCH 080/210] Update wall/wall_near/wall_far preview artwork --- mod/etc/options_menu/images/wall.png | Bin 15861 -> 32533 bytes mod/etc/options_menu/images/wall_far.png | Bin 2735 -> 17040 bytes mod/etc/options_menu/images/wall_near.png | Bin 1162 -> 18894 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/mod/etc/options_menu/images/wall.png b/mod/etc/options_menu/images/wall.png index 9da2f3db215907f4abaf1cd3d19e98e7c52a3a6d..560cfc18c57561a3596e5fd5ba91877702880197 100644 GIT binary patch literal 32533 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 WLt<5_?*CkE1u4m?$<|9-ME)1#ysU@- diff --git a/mod/etc/options_menu/images/wall_far.png b/mod/etc/options_menu/images/wall_far.png index 36662d12e9e9201136971324377c0f8a0040350f..1be8f98b810d409f5d6e9e52ae9589ba2f42de30 100644 GIT binary patch delta 16315 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 diff --git a/mod/etc/options_menu/images/wall_near.png b/mod/etc/options_menu/images/wall_near.png index 082118155f2d22ed51b673c5bde3fab5f854024f..eef7fd580867c197f76802bf07ab437ffba07f36 100644 GIT binary patch literal 18894 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 KMNUMnLSTYY4Gk>- delta 956 zcmV;t14I1IlL3keC5cc@M-2)Z3IG5A4M|8uQUCw|WB>pFWCQ~M004r_h-Ux*0ZWlO zPXP*%iaatkE;cP=F*GtDH#sgbHZVCdG%O%8FflhTFgGtTGc7PNIxsdmGdUn2ARr(h zARr<>3X%9i0tz6LIsqPkFFpz&JTG!&W;#+tMm`EWFL*k5ZE$U6bYVUUJU@7FVPk7$ zbRcDJWIZBsB0oN!9ANJN000DMK}|sb0I`n?{9y$E0004VQb$4nuFf3k0000UP)t-s z0002ZoLT_Q0OrL~5K?mh{{VYx7yx@p0D}O%tYXq;C#C=Z00DG=QchCl>LYW7s#S-+b&~+ zF=bpHtDzRW5QYxwB>3M4lO}_d^Q)qH~+(lbtVkAOb z%EomUX0=7*GLl+ZUU zW5#t06r5}@_`7>3-f3JW%1IRCG*^7^U zbzyK4uQtcYn#o)bxN(`--R9=&5jTz#LXtX-=LC28-O6Qu(_}Jn+kD0A%HU+X8p+{g zv#yfG$;L#oI0>rhu{c?)F*)%#St|CJocK(z$K%9T+hcMv*T(j^oVZM|$L3@n7xtK( zxJ)QXf|FXgOt8n}#8%(qaf0>Ydpu6&ak*XrC%#v}iSNbFl~Rbcx0Tr7-G7oOo3*!* z{CNhF=;dF3d(kh#Um_)GmR=NPM{{DU@5L{STDI(XX%s0z8?ocW)sC1L+KBXOXh~?Z zTOBQ_)be1vY#K#MtPT0{n_EX6f3`_U(1wtp4aJLc*-#R9nwS?WX%uyq?9j-M#c?)J zZbN_g)cT#@X9H;)8UoQl+9n}M{k%Bh29h>OZU7*EH?)|WoJ1QPRf!+Fee6G+SqNJ-4iD#1a^sm*f}xV%!5C>!XD7s%rEKax*# edw${{xBdV-rgFp00xG%y0000 Date: Wed, 23 Sep 2026 12:11:04 +0000 Subject: [PATCH 081/210] Render wall/wall_near/wall_far/sbm DIY category tiles as fit-contain instead of cover --- mod/etc/options_menu/themes/creator/commands/c0001_wall | 1 + mod/etc/options_menu/themes/creator/commands/c0002_sbm | 1 + mod/etc/options_menu/themes/creator/commands/c0004_wall_near | 1 + mod/etc/options_menu/themes/creator/commands/c0005_wall_far | 1 + 4 files changed, 4 insertions(+) diff --git a/mod/etc/options_menu/themes/creator/commands/c0001_wall b/mod/etc/options_menu/themes/creator/commands/c0001_wall index 8cfed2f..ea96c66 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0001_wall +++ b/mod/etc/options_menu/themes/creator/commands/c0001_wall @@ -3,3 +3,4 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset wall & PREVIEW_IMAGE=/etc/options_menu/images/wall.png +PREVIEW_FIT_CONTAIN=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0002_sbm b/mod/etc/options_menu/themes/creator/commands/c0002_sbm index 6818122..4602a1e 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0002_sbm +++ b/mod/etc/options_menu/themes/creator/commands/c0002_sbm @@ -3,3 +3,4 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset sbm & PREVIEW_IMAGE=/etc/options_menu/images/sbm.png +PREVIEW_FIT_CONTAIN=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0004_wall_near b/mod/etc/options_menu/themes/creator/commands/c0004_wall_near index 717437e..46cd2c4 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0004_wall_near +++ b/mod/etc/options_menu/themes/creator/commands/c0004_wall_near @@ -3,3 +3,4 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset wall_near & PREVIEW_IMAGE=/etc/options_menu/images/wall_near.png +PREVIEW_FIT_CONTAIN=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0005_wall_far b/mod/etc/options_menu/themes/creator/commands/c0005_wall_far index 583e6b2..d1c4218 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0005_wall_far +++ b/mod/etc/options_menu/themes/creator/commands/c0005_wall_far @@ -3,3 +3,4 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset wall_far & PREVIEW_IMAGE=/etc/options_menu/images/wall_far.png +PREVIEW_FIT_CONTAIN=TRUE From 8428fe67c56e1f069e772c0b8fe92fd04ffdb05e Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 23 Sep 2026 12:11:38 +0000 Subject: [PATCH 082/210] Update subproject commit reference for OptionsMenu --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 2bce3c1..406ad3d 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 2bce3c180def93bf218acaaf61c5e35c19b88789 +Subproject commit 406ad3d617de3df49cb5aaa8aa7e81deb9ad4227 From 9c3029012a97834fc152255a1154dfcd5e849704 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 23 Sep 2026 12:12:34 +0000 Subject: [PATCH 083/210] Hash-dedupe DIY sprite previews before queuing the crop job, and trim over-verbose comments in om_vars --- mod/etc/options_menu/themes/scripts/om_vars | 59 ++++++--------------- 1 file changed, 17 insertions(+), 42 deletions(-) diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index ef2cd32..db59a67 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -193,7 +193,7 @@ clearDownloads() { } enableMenuOptions() { - enable_cmds "Pole Mario" "Luigi" "Wall" "WallNear" "WallFar" "Packed" "ThemeGenerator" "Clear" + enable_cmds "Pole" "Mario" "Luigi" "Wall" "WallNear" "WallFar" "Packed" "ThemeGenerator" "Clear" disable_cmds "ColorsNes" "PackedNes" "Smb" "Settings" "Color" if [ ! -f "$omDummy" ]; then @@ -236,9 +236,7 @@ enableMenuOptions() { [ ! -d "$has_folders" ] && disable_cmds "ThemeGenerator" } -# cheap stand-in for "did the installed theme list actually change" - avoids -# re-running the full themeLoader scan (~1.7s with a few dozen themes) when -# nothing was added or removed, e.g. just browsing Downloads then going Back +# avoids re-running the full themeLoader scan (~1.7s) when nothing changed themeCountChanged() { new_count="$(find "$theme_path" -mindepth 1 -maxdepth 1 -type d | wc -l)" old_count="$(cat "$om_theme_count" 2> /dev/null)" @@ -339,9 +337,6 @@ removeRegionalTheme() { } getThemeList() { - # splash first - theme_downloader's own GitHub/classicmods fallback check - # can take a few seconds when a host is slow/unreachable, and that - # shouldn't be a blank screen before the loading indicator shows "$omScripts/gen_splash" /tmp/om_splash.png THEME_LIST_DOWNLOADING_LINE1 THEME_LIST_DOWNLOADING_LINE2 && decodepng /tmp/om_splash.png > /dev/fb0 cd "$omModSub" || exit @@ -400,7 +395,6 @@ loadThemeList() { fi } -# verify themeGenerator function themeGenerator() { echo "Packaging: DefKorns" translate CATEGORY_UI_MOD @@ -446,9 +440,6 @@ themeGenerator() { } findDiyAsset() { - # drawn once, up front - previously this was redrawn per theme inside the - # loop below, which (now that the crops run backgrounded/parallel instead - # of one at a time) no longer reliably stays on screen for the whole scan "$omScripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png > /dev/fb0 diy_file="${1}.png" @@ -517,9 +508,6 @@ findDiyAsset() { ;; esac - # the crop rectangle is the same for every theme in this category (it's - # the same region of each theme's own packed.png/wall.png every time, only - # the source theme differs) - read it once here, not per theme diy_crop_x="" diy_sprites_json="$omModScripts/om_${1}.sprites.json" if [ "$colors" = "0" ] && [ -f "$diy_sprites_json" ]; then @@ -542,18 +530,11 @@ findDiyAsset() { existing_scripts="$(ls -1 "$diy_sprite/scripts" 2>/dev/null)" diy_crop_batch=0 - # chars=1 only: many themes reuse byte-identical sprites under different - # names - collapse those into a single tile instead of one per theme - diy_seen_hashes="" + diy_seen_hashes="" # collapses themes that reuse a byte-identical source asset under a different name for p in $paths_to_search; do if [ "$(ls -A "$p")" ]; then # shellcheck disable=SC2038 - # listed to a temp file and read via redirection, not piped straight - # into the while loop - a pipe would run the loop in a subshell, and - # the backgrounded sprite_crop jobs below would then belong to that - # subshell, not this function, so the "wait" after the loop (needed to - # not return before the last, sub-4 batch of crops finishes) wouldn't - # actually catch them + # a temp file, not a pipe into the while loop: a pipe would put the backgrounded crops in a subshell the wait below can't catch cd "$p" && find . -mindepth 1 -maxdepth 1 -type d | xargs -n 1 basename | sort -u > /tmp/diy_asset_list while IFS= read -r asset_name; do @@ -573,6 +554,18 @@ findDiyAsset() { [ "$p" = "$theme_path" ] && full_asset_path="$resource_path/$asset_path" preview_image="$full_asset_path/$diy_file" + + # hashed pre-crop, so this doesn't have to wait on the backgrounded crop job below + if [ -f "$preview_image" ]; then + sprite_hash="$(md5sum "$preview_image" | cut -d' ' -f1)" + if printf '%s\n' "$diy_seen_hashes" | grep -qxF "$sprite_hash"; then + rm -f "$cmd_file" "$sprite_script.sh" + continue + fi + diy_seen_hashes="$diy_seen_hashes +$sprite_hash" + fi + temp_preview="${omModSprites}/assets/${1}/${1}_${asset_name}.png" if [ "$colors" = "1" ]; then @@ -593,18 +586,6 @@ findDiyAsset() { preview_image="$temp_preview" # cropped copy, queued above or already cached fi - if [ "$chars" = "1" ] && [ -f "$preview_image" ]; then - sprite_hash="$(md5sum "$preview_image" | cut -d' ' -f1)" - if printf '%s\n' "$diy_seen_hashes" | grep -qxF "$sprite_hash"; then - # identical sprite already listed under an earlier theme name - - # drop this one, and remove any tile already generated for it - rm -f "$cmd_file" "$sprite_script.sh" - continue - fi - diy_seen_hashes="$diy_seen_hashes -$sprite_hash" - fi - if ! printf '%s\n' "$existing_cmds" | grep -qxF "c0001_$asset_name" || ! printf '%s\n' "$existing_scripts" | grep -qxF "$asset_name.sh"; then touch "$cmd_file" cmd_name="$(echo "$asset_name" | sed -e 's/_/ /g' | awk '{for(i=1;i<=NF;i++){$i=toupper(substr($i,1,1))substr($i,2)}}1')" @@ -620,8 +601,6 @@ PREVIEW_SQUARE=TRUE" preview_cols_str="" [ -n "$preview_cols" ] && preview_cols_str=" PREVIEW_GRID_COLS=$preview_cols" - # chars=1: portrait sprite art, not a wide/square screenshot - - # fit without cropping, and skip the title label entirely preview_fit_str="" [ "$chars" = "1" ] && preview_fit_str=" PREVIEW_FIT_CONTAIN=TRUE @@ -690,8 +669,6 @@ diyThemeChecker() { diyThemeRemover() { disable_cmds "DiY" "DelDiY" "Preview" - # only the applied selection - each category's own browse cache is - # refreshed by that category's own Refresh chip, not here [ -d "$diyThemePath" ] && find "$diyThemePath" -type f -not -name 'assets_here' -exec rm {} + } @@ -731,9 +708,7 @@ audioSettings() { } toggleOptions() { - # $1=this flag file, $2=paired parent/child flag file, $3=set to skip the - # cascade (used for a parent toggling itself back on - its own child's - # state stays whatever it already was) + # $1=this flag file, $2=paired parent/child flag file, $3=set to skip the cascade if [ ! -f "$1" ]; then settingsOptionChecker "on" "$1" [ -z "$3" ] && [ ! -f "$2" ] && settingsOptionChecker "on" "$2" From e6a8915388cf951e8c01de32d64bb0d7672c031b Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 23 Sep 2026 12:40:39 +0000 Subject: [PATCH 084/210] Add NES_ONLY/SNES_ONLY command filtering to theme_manager, replacing om_vars's rename-based sftype toggling --- .../themes/creator/commands/c0001_packed | 1 + .../themes/creator/commands/c0001_packed_nes | 1 + .../themes/creator/commands/c0001_wall | 1 + .../themes/creator/commands/c0002_mario | 1 + .../themes/creator/commands/c0002_sbm | 1 + .../themes/creator/commands/c0003_luigi | 1 + .../themes/creator/commands/c0004_wall_near | 1 + .../themes/creator/commands/c0005_wall_far | 1 + .../themes/creator/commands/c0006_colors_nes | 1 + .../themes/creator/commands/c0006_pole | 1 + mod/etc/options_menu/themes/scripts/om_vars | 27 ++++---- src/command_loader.cpp | 61 +++++++++++++++++++ 12 files changed, 82 insertions(+), 16 deletions(-) diff --git a/mod/etc/options_menu/themes/creator/commands/c0001_packed b/mod/etc/options_menu/themes/creator/commands/c0001_packed index 0f076da..b0494fd 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0001_packed +++ b/mod/etc/options_menu/themes/creator/commands/c0001_packed @@ -3,3 +3,4 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset packed & PREVIEW_IMAGE=/etc/options_menu/images/packed.png +SNES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0001_packed_nes b/mod/etc/options_menu/themes/creator/commands/c0001_packed_nes index 9e8ec87..cb5315e 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0001_packed_nes +++ b/mod/etc/options_menu/themes/creator/commands/c0001_packed_nes @@ -3,3 +3,4 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset packed_nes & PREVIEW_IMAGE=/etc/options_menu/images/packed_nes.png +NES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0001_wall b/mod/etc/options_menu/themes/creator/commands/c0001_wall index ea96c66..3aaa673 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0001_wall +++ b/mod/etc/options_menu/themes/creator/commands/c0001_wall @@ -4,3 +4,4 @@ RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset wall & PREVIEW_IMAGE=/etc/options_menu/images/wall.png PREVIEW_FIT_CONTAIN=TRUE +SNES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0002_mario b/mod/etc/options_menu/themes/creator/commands/c0002_mario index 2163c61..5fdcb8c 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0002_mario +++ b/mod/etc/options_menu/themes/creator/commands/c0002_mario @@ -4,3 +4,4 @@ RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset mario & PREVIEW_IMAGE=/etc/options_menu/images/mario.png PREVIEW_FIT_CONTAIN=TRUE +SNES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0002_sbm b/mod/etc/options_menu/themes/creator/commands/c0002_sbm index 4602a1e..ecf5b19 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0002_sbm +++ b/mod/etc/options_menu/themes/creator/commands/c0002_sbm @@ -4,3 +4,4 @@ RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset sbm & PREVIEW_IMAGE=/etc/options_menu/images/sbm.png PREVIEW_FIT_CONTAIN=TRUE +NES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0003_luigi b/mod/etc/options_menu/themes/creator/commands/c0003_luigi index 297fbcc..8b2b1ee 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0003_luigi +++ b/mod/etc/options_menu/themes/creator/commands/c0003_luigi @@ -4,3 +4,4 @@ RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset luigi & PREVIEW_IMAGE=/etc/options_menu/images/luigi.png PREVIEW_FIT_CONTAIN=TRUE +SNES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0004_wall_near b/mod/etc/options_menu/themes/creator/commands/c0004_wall_near index 46cd2c4..c25c337 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0004_wall_near +++ b/mod/etc/options_menu/themes/creator/commands/c0004_wall_near @@ -4,3 +4,4 @@ RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset wall_near & PREVIEW_IMAGE=/etc/options_menu/images/wall_near.png PREVIEW_FIT_CONTAIN=TRUE +SNES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0005_wall_far b/mod/etc/options_menu/themes/creator/commands/c0005_wall_far index d1c4218..fa6e790 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0005_wall_far +++ b/mod/etc/options_menu/themes/creator/commands/c0005_wall_far @@ -4,3 +4,4 @@ RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset wall_far & PREVIEW_IMAGE=/etc/options_menu/images/wall_far.png PREVIEW_FIT_CONTAIN=TRUE +SNES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0006_colors_nes b/mod/etc/options_menu/themes/creator/commands/c0006_colors_nes index 6f30988..95d3a96 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0006_colors_nes +++ b/mod/etc/options_menu/themes/creator/commands/c0006_colors_nes @@ -3,3 +3,4 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset colors_nes & PREVIEW_IMAGE=/etc/options_menu/images/colors_nes.png +NES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0006_pole b/mod/etc/options_menu/themes/creator/commands/c0006_pole index b300704..2e02957 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0006_pole +++ b/mod/etc/options_menu/themes/creator/commands/c0006_pole @@ -3,3 +3,4 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset pole & PREVIEW_IMAGE=/etc/options_menu/images/pole.png +SNES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index db59a67..cf112c9 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -193,8 +193,8 @@ clearDownloads() { } enableMenuOptions() { - enable_cmds "Pole" "Mario" "Luigi" "Wall" "WallNear" "WallFar" "Packed" "ThemeGenerator" "Clear" - disable_cmds "ColorsNes" "PackedNes" "Smb" "Settings" "Color" + enable_cmds "ThemeGenerator" "Clear" + disable_cmds "Settings" "Color" if [ ! -f "$omDummy" ]; then disable_cmds "Clear" @@ -216,22 +216,17 @@ enableMenuOptions() { done < "$thmOverlay" if [ "$sftype" = "nes" ]; then - disable_cmds "Pole" "Mario" "Luigi" "Wall" "WallNear" "WallFar" "Color" "Packed" - enable_cmds "ColorsNes" "PackedNes" "Smb" + src="hvc" + dest="nes" - if [ "$sftype" = "nes" ]; then - src="hvc" - dest="nes" - - if [ "$sfregion" = "jpn" ]; then - src="nes" - dest="hvc" - fi - - for subdir in "${omModPackedNes}/assets"/*; do - rename "${subdir}/${src}.png" "${subdir}/${dest}.png" - done + if [ "$sfregion" = "jpn" ]; then + src="nes" + dest="hvc" fi + + for subdir in "${omModPackedNes}/assets"/*; do + rename "${subdir}/${src}.png" "${subdir}/${dest}.png" + done fi [ ! -d "$has_folders" ] && disable_cmds "ThemeGenerator" } diff --git a/src/command_loader.cpp b/src/command_loader.cpp index 7ee9ecd..0471edb 100644 --- a/src/command_loader.cpp +++ b/src/command_loader.cpp @@ -9,6 +9,7 @@ #include "command_loader.h" +#include #include #include #include @@ -29,6 +30,53 @@ void ReplaceAll(std::string & command, const std::string & oldString, const std: struct DirCloser { void operator()(DIR * dir) const { if(dir) closedir(dir); } }; using DirHandle = std::unique_ptr; +struct PipeCloser { void operator()(FILE * pipe) const { if(pipe) pclose(pipe); } }; +using PipeHandle = std::unique_ptr; + +// cached in /tmp (wiped on reboot) - sftype never changes within a boot, so only the first screen +// that needs it pays for the shell fork +std::string ReadSftype() +{ + static constexpr const char * CachePath = "/tmp/om_sftype"; + std::string result; + std::ifstream cached(CachePath); + if(std::getline(cached, result) && !result.empty()) + return result; + + PipeHandle pipe(popen("source /etc/preinit; script_init; echo $sftype", "r")); + if(pipe) + { + char buffer[32] = {0}; + if(fgets(buffer, sizeof(buffer), pipe.get())) + result = buffer; + } + while(!result.empty() && (result.back() == '\n' || result.back() == '\r')) + result.pop_back(); + + std::ofstream(CachePath, std::ios::trunc) << result; + return result; +} + +// my own convention, not vendor's Command format - read directly instead of extending that parser +struct ConsoleOnlyFlags { bool nesOnly = false; bool snesOnly = false; }; + +ConsoleOnlyFlags ReadConsoleOnlyFlags(const std::string & path) +{ + ConsoleOnlyFlags flags; + std::ifstream in(path); + std::string line; + while(std::getline(in, line)) + { + if(!line.empty() && line.back() == '\r') + line.pop_back(); + if(line == "NES_ONLY=TRUE") + flags.nesOnly = true; + else if(line == "SNES_ONLY=TRUE") + flags.snesOnly = true; + } + return flags; +} + } // namespace AppOptions ParseArgs(int argc, char * argv[], const std::string & optionsLocation) @@ -81,6 +129,8 @@ bool LoadCommands(const std::string & commandLocation, const std::string & scrip fileList.sort(); std::ifstream in; + std::string sftype; + bool sftypeLoaded = false; for(auto & file : fileList) { in.open(commandLocation + file); @@ -91,6 +141,17 @@ bool LoadCommands(const std::string & commandLocation, const std::string & scrip // c0000_0000 leading sentinel only - a later empty COMMAND_STR is a reserved blank slot instead if(commands.empty() && c.command.empty()) continue; + ConsoleOnlyFlags consoleOnly = ReadConsoleOnlyFlags(commandLocation + file); + if(consoleOnly.nesOnly || consoleOnly.snesOnly) + { + if(!sftypeLoaded) + { + sftype = ReadSftype(); + sftypeLoaded = true; + } + if((consoleOnly.nesOnly && sftype != "nes") || (consoleOnly.snesOnly && sftype == "nes")) + continue; + } if(!c.command.empty()) { ReplaceAll(c.command, "%options_path%", optionsLocation); From c59668800db5ee8dd4d30a014666d3aed1a666f8 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 23 Sep 2026 21:59:21 +0000 Subject: [PATCH 085/210] Remove PREVIEW_FIT_CONTAIN from wall/wall_near/wall_far commands and add it to pole command --- mod/etc/options_menu/themes/creator/commands/c0001_wall | 1 - mod/etc/options_menu/themes/creator/commands/c0004_wall_near | 1 - mod/etc/options_menu/themes/creator/commands/c0005_wall_far | 1 - mod/etc/options_menu/themes/creator/commands/c0006_pole | 1 + 4 files changed, 1 insertion(+), 3 deletions(-) diff --git a/mod/etc/options_menu/themes/creator/commands/c0001_wall b/mod/etc/options_menu/themes/creator/commands/c0001_wall index 3aaa673..9b6532e 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0001_wall +++ b/mod/etc/options_menu/themes/creator/commands/c0001_wall @@ -3,5 +3,4 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset wall & PREVIEW_IMAGE=/etc/options_menu/images/wall.png -PREVIEW_FIT_CONTAIN=TRUE SNES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0004_wall_near b/mod/etc/options_menu/themes/creator/commands/c0004_wall_near index c25c337..a17760a 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0004_wall_near +++ b/mod/etc/options_menu/themes/creator/commands/c0004_wall_near @@ -3,5 +3,4 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset wall_near & PREVIEW_IMAGE=/etc/options_menu/images/wall_near.png -PREVIEW_FIT_CONTAIN=TRUE SNES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0005_wall_far b/mod/etc/options_menu/themes/creator/commands/c0005_wall_far index fa6e790..674b446 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0005_wall_far +++ b/mod/etc/options_menu/themes/creator/commands/c0005_wall_far @@ -3,5 +3,4 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset wall_far & PREVIEW_IMAGE=/etc/options_menu/images/wall_far.png -PREVIEW_FIT_CONTAIN=TRUE SNES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0006_pole b/mod/etc/options_menu/themes/creator/commands/c0006_pole index 2e02957..2bdaed8 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0006_pole +++ b/mod/etc/options_menu/themes/creator/commands/c0006_pole @@ -3,4 +3,5 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset pole & PREVIEW_IMAGE=/etc/options_menu/images/pole.png +PREVIEW_FIT_CONTAIN=TRUE SNES_ONLY=TRUE From 413e18e21ccdf1fa92fbd3a6efe1b272dac702f6 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 23 Sep 2026 21:59:54 +0000 Subject: [PATCH 086/210] Refactor font management in theme scripts and remove unused om_themes_vars file --- .../themes/scripts/om_overlaythmcript | 19 ++--- .../themes/scripts/om_themes_vars | 74 ------------------- .../themes/scripts/om_themescript | 22 +++--- .../themes/settings/scripts/om_reset | 7 ++ mod/uninstall | 8 +- 5 files changed, 30 insertions(+), 100 deletions(-) delete mode 100644 mod/etc/options_menu/themes/scripts/om_themes_vars diff --git a/mod/etc/options_menu/themes/scripts/om_overlaythmcript b/mod/etc/options_menu/themes/scripts/om_overlaythmcript index f5453a2..d844197 100644 --- a/mod/etc/options_menu/themes/scripts/om_overlaythmcript +++ b/mod/etc/options_menu/themes/scripts/om_overlaythmcript @@ -16,14 +16,16 @@ # along with this program. If not, see . # fontBindFix() { - [ "$1" == "backup" ] && FONT_TYPE="title s m l small medium large" - [ "$1" == "restore" ] && FONT_TYPE="s m l small medium large" - [ "$1" == "restore" ] && [ -f "$TITLE_FONT_BACKUP" ] && mv "$TITLE_FONT_BACKUP" "$TITLE_FONT" - for FONT in $FONT_TYPE; do - [ "$1" == "backup" ] && [ ! -f "$TITLE_FONT_BACKUP" ] && mv "${FONT_PATH}/${FONT}.fnt" "${FONT_PATH}/${FONT}.fnt.backup" - [ "$1" == "backup" ] && cp "$FULL_THEME_PATH/fonts/${FONT}.fnt" "${FONT_PATH}/${FONT}.fnt" - [ "$1" == "restore" ] && umount "${ORIGINAL_THEME}/resources/fonts/${UI1}/${FONT}.fnt" - [ "$1" == "restore" ] && rm "${FONT_PATH}/${FONT}.fnt" + [ ! -f "${FONT_PATH}/title.fnt.backup" ] && cp "${FONT_PATH}/title.fnt" "${FONT_PATH}/title.fnt.backup" + for FONT in title s m l small medium large; do + if [ "$1" == "backup" ] && [ -f "$FULL_THEME_PATH/fonts/${FONT}.fnt" ]; then + cp "$FULL_THEME_PATH/fonts/${FONT}.fnt" "${FONT_PATH}/${FONT}.fnt" + elif [ "$FONT" == "title" ]; then + cp "${FONT_PATH}/title.fnt.backup" "${FONT_PATH}/title.fnt" + else + umount "${ORIGINAL_THEME}/resources/fonts/${UI1}/${FONT}.fnt" 2>/dev/null + rm -f "${FONT_PATH}/${FONT}.fnt" + fi done } @@ -74,7 +76,6 @@ themeSelector() { local FONT_PATH="$mountpoint/media/$modname/fonts" [ ! -d "$FONT_PATH" ] && FONT_PATH="$rootfs/usr/share/fonts" local TITLE_FONT="$FONT_PATH/title.fnt" - local TITLE_FONT_BACKUP="${TITLE_FONT}.backup" local TITLE_THEME_FONT="$FULL_THEME_PATH/fonts/title.fnt" if [ "$SELECTED_THEME" != "$PREVIOUS_THEME" ] || [ ! -f "$LAST_THEME" ] || [ ! -f "$ACTIVE_THEME" ]; then diff --git a/mod/etc/options_menu/themes/scripts/om_themes_vars b/mod/etc/options_menu/themes/scripts/om_themes_vars deleted file mode 100644 index b9c3afb..0000000 --- a/mod/etc/options_menu/themes/scripts/om_themes_vars +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/sh -# shellcheck disable=SC2034,SC2086,SC2154,SC3046,SC3051 -# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# shellcheck source=/dev/null -source $mountpoint/etc/options_menu/scripts/om_functions -script_init - -modtitle="themes" -omTitle="THEME_SELECTOR" -b_file="$mountpoint/etc/preinit.d/b8024_themeselector" -p_file="$mountpoint/etc/preinit.d/p8024_themeselector" -fontfix_file="$mountpoint/etc/preinit.d/b8025_fontfix" -chmenu_file="$root_bin/chmenu" -has_folders="${gStorage}/001" - - -theme_path="$usb_path/$modtitle/$s_type" -[ ! -d "$theme_path" ] && theme_path="$nand_path/$modtitle/$s_type" -system="$(echo "$s_type" | awk '{print toupper($0)}')" -has_theme="$(find "$theme_path" -maxdepth 1 -name "$system.*.tar.gz" 2> /dev/null | wc -l)" - - -# extracts any theme archives already sitting in $theme_path (e.g. copied -# there over USB), instead of the old version's "*.tar.gz in whatever the -# CWD happens to be" - which didn't match what $has_theme itself checked -hasThemesToInstall() { - [ "$has_theme" = 0 ] && return - for f in "$theme_path/$system".*.tar.gz; do - [ -f "$f" ] || continue - tar -xzvf "$f" -C "$theme_path" && rm -f "$f" - done -} - -# patches chmenu/fontfix (via $injected_file, not a hardcoded path - so the -# grep guard and the sed always target the same file even if $rootfs -# resolves differently) to call into the theme selector's own hooks -injectFile() { - for scrpt in "$@"; do - case "$scrpt" in - chmenu) injected_file="$chmenu_file" ;; - fontfix) injected_file="$fontfix_file" ;; - esac - - grep -q "# themeselector" "$injected_file" && continue - - if [ "$scrpt" = "chmenu" ]; then - # shellcheck disable=SC1004 - sed -i '/^overmount_games.*/a # themeselector fix \ -themeSelector \ -fontFix' "$injected_file" - elif [ "$scrpt" = "fontfix" ]; then - # shellcheck disable=SC1004,SC2016 - sed -i '/^fontFix_getPath().*/a # themeselector \ - if [ -f "$mountpoint$profilepath/$modname/active_theme" ]; then \ - active_theme=$(cat "$mountpoint$profilepath/$modname/active_theme") \ - fontFix_getPath0 "$active_theme/fonts/$1" && return 0 \ - fi' "$injected_file" - fi - done -} diff --git a/mod/etc/options_menu/themes/scripts/om_themescript b/mod/etc/options_menu/themes/scripts/om_themescript index e6b1173..b224308 100644 --- a/mod/etc/options_menu/themes/scripts/om_themescript +++ b/mod/etc/options_menu/themes/scripts/om_themescript @@ -42,18 +42,17 @@ musicHackUnbind() { } fontBindFix() { - if [ "$1" == "backup" ]; then - for FONT in title s m l small medium large; do - [ ! -f "$TITLE_FONT_BACKUP" ] && mv "${FONT_PATH}/${FONT}.fnt" "${FONT_PATH}/${FONT}.fnt.backup" + [ ! -f "${FONT_PATH}/title.fnt.backup" ] && cp "${FONT_PATH}/title.fnt" "${FONT_PATH}/title.fnt.backup" + for FONT in title s m l small medium large; do + if [ "$1" == "backup" ] && [ -f "$FULL_THEME_PATH/fonts/${FONT}.fnt" ]; then cp "$FULL_THEME_PATH/fonts/${FONT}.fnt" "${FONT_PATH}/${FONT}.fnt" - done - elif [ "$1" == "restore" ]; then - [ -f "$TITLE_FONT_BACKUP" ] && mv "$TITLE_FONT_BACKUP" "$TITLE_FONT" - for FONT in s m l small medium large; do - umount "${ORIGINAL_THEME}/resources/fonts/${UI1}/${FONT}.fnt" - rm "${FONT_PATH}/${FONT}.fnt" - done - fi + elif [ "$FONT" == "title" ]; then + cp "${FONT_PATH}/title.fnt.backup" "${FONT_PATH}/title.fnt" + else + umount "${ORIGINAL_THEME}/resources/fonts/${UI1}/${FONT}.fnt" 2>/dev/null + rm -f "${FONT_PATH}/${FONT}.fnt" + fi + done } themeSelector() { @@ -107,7 +106,6 @@ themeSelector() { local FONT_PATH="$mountpoint/media/$modname/fonts" [ ! -d "$FONT_PATH" ] && FONT_PATH="$rootfs/usr/share/fonts" local TITLE_FONT="$FONT_PATH/title.fnt" - local TITLE_FONT_BACKUP="${TITLE_FONT}.backup" local THEME_FONT_PATH="$FULL_THEME_PATH/fonts" SILENT_WAV="$rootfs/usr/share/themes/bgm_boot.wav" diff --git a/mod/etc/options_menu/themes/settings/scripts/om_reset b/mod/etc/options_menu/themes/settings/scripts/om_reset index 6f832ef..6d0a458 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_reset +++ b/mod/etc/options_menu/themes/settings/scripts/om_reset @@ -20,6 +20,13 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init +FONT_PATH="$mountpoint/media/$modname/fonts" +[ ! -d "$FONT_PATH" ] && FONT_PATH="$rootfs/usr/share/fonts" +[ -f "${FONT_PATH}/title.fnt.backup" ] && cp "${FONT_PATH}/title.fnt.backup" "${FONT_PATH}/title.fnt" +for FONT in s m l small medium large; do + rm -f "${FONT_PATH}/${FONT}.fnt" +done + remove "$b_file" "$p_file" hakchi reboot diff --git a/mod/uninstall b/mod/uninstall index 904badc..9e7dfcb 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -23,8 +23,6 @@ remove() { } FONT_PATH="$rootfs/usr/share/fonts" -TITLE_FONT="$FONT_PATH/title.fnt" -TITLE_FONT_BACKUP="${TITLE_FONT}.backup" UI0="ui/$sftype-$sfregion" UI1="" @@ -106,10 +104,10 @@ remove "$rootfs/etc/usr/bin/sprite_crop" remove "$rootfs/etc/usr/bin/theme_downloader" remove "$rootfs/etc/options_menu/lib/theme_manager" -[ -f "$TITLE_FONT_BACKUP" ] && mv "$TITLE_FONT_BACKUP" "$TITLE_FONT" +[ -f "${FONT_PATH}/title.fnt.backup" ] && cp "${FONT_PATH}/title.fnt.backup" "${FONT_PATH}/title.fnt" for FONT in s m l small medium large; do - umount "${ORIGINAL_THEME}/resources/fonts/${UI1}/${FONT}.fnt" - rm "${FONT_PATH}/${FONT}.fnt" + umount "${ORIGINAL_THEME}/resources/fonts/${UI1}/${FONT}.fnt" 2>/dev/null + rm -f "${FONT_PATH}/${FONT}.fnt" done unset cfg_OM_ThemeSelector_enabled From 2289a4e43ad058f1b6624e9b1f9e8a58ba9ab4bc Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 23 Sep 2026 22:00:27 +0000 Subject: [PATCH 087/210] Refactor ReadSftype to use environment variable and remove caching mechanism --- src/command_loader.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/command_loader.cpp b/src/command_loader.cpp index 0471edb..879ab9c 100644 --- a/src/command_loader.cpp +++ b/src/command_loader.cpp @@ -33,16 +33,14 @@ using DirHandle = std::unique_ptr; struct PipeCloser { void operator()(FILE * pipe) const { if(pipe) pclose(pipe); } }; using PipeHandle = std::unique_ptr; -// cached in /tmp (wiped on reboot) - sftype never changes within a boot, so only the first screen -// that needs it pays for the shell fork +// child screens inherit this via fork+exec, same as OM_BACK_STACK - so only the first screen +// that needs sftype pays for the shell fork std::string ReadSftype() { - static constexpr const char * CachePath = "/tmp/om_sftype"; - std::string result; - std::ifstream cached(CachePath); - if(std::getline(cached, result) && !result.empty()) - return result; + if(const char * env = std::getenv("OM_SFTYPE")) + return env; + std::string result; PipeHandle pipe(popen("source /etc/preinit; script_init; echo $sftype", "r")); if(pipe) { @@ -53,11 +51,11 @@ std::string ReadSftype() while(!result.empty() && (result.back() == '\n' || result.back() == '\r')) result.pop_back(); - std::ofstream(CachePath, std::ios::trunc) << result; + setenv("OM_SFTYPE", result.c_str(), 1); return result; } -// my own convention, not vendor's Command format - read directly instead of extending that parser +// my own convention, not OptionsMenu's Command format struct ConsoleOnlyFlags { bool nesOnly = false; bool snesOnly = false; }; ConsoleOnlyFlags ReadConsoleOnlyFlags(const std::string & path) @@ -77,7 +75,7 @@ ConsoleOnlyFlags ReadConsoleOnlyFlags(const std::string & path) return flags; } -} // namespace +} AppOptions ParseArgs(int argc, char * argv[], const std::string & optionsLocation) { From 958547924ff491d55aa068dbd1974399b7edfec0 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 24 Sep 2026 19:34:01 +0000 Subject: [PATCH 088/210] Add vendor/chmenu to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2100167..08567c6 100644 --- a/.gitignore +++ b/.gitignore @@ -556,3 +556,4 @@ tools/MaterialSymbolsOutlined.codepoints tools/MaterialSymbolsOutlined.ttf src/cacert.pem mod/etc/options_menu/lib/ +vendor/chmenu From bee39190c0983a6649075be6f3d5164fb30b6a51 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 24 Sep 2026 20:13:39 +0000 Subject: [PATCH 089/210] Refactor theme management scripts: remove unused files and enhance theme loading logic --- .../options_menu/scripts/om_theme_selector | 15 +++++- mod/etc/options_menu/scripts/om_themeselector | 36 -------------- .../options_menu/themes/scripts/om_overlay | 3 -- .../themes/scripts/om_restorethumb | 25 ---------- .../themes/scripts/om_themescript | 32 ++++++++----- mod/etc/options_menu/themes/scripts/om_vars | 48 ++++++++++--------- mod/uninstall | 2 +- 7 files changed, 60 insertions(+), 101 deletions(-) delete mode 100644 mod/etc/options_menu/scripts/om_themeselector delete mode 100644 mod/etc/options_menu/themes/scripts/om_overlay delete mode 100644 mod/etc/options_menu/themes/scripts/om_restorethumb diff --git a/mod/etc/options_menu/scripts/om_theme_selector b/mod/etc/options_menu/scripts/om_theme_selector index 65c942b..56a91e2 100644 --- a/mod/etc/options_menu/scripts/om_theme_selector +++ b/mod/etc/options_menu/scripts/om_theme_selector @@ -19,4 +19,17 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init -sh "$mountpoint/etc/options_menu/scripts/om_themeselector" +injectFile "chmenu" +hasThemesToInstall +enableMenuOptions +remove "$omDummy" + +if [ "$1" = "reload" ] || [ -f "$om_firstrun" ] ; then + loadThemeList -r + remove "$om_firstrun" + modCleanUp +else + loadThemeList +fi + +$optionsMenu/lib/theme_manager --title INSTALLED_THEMES & diff --git a/mod/etc/options_menu/scripts/om_themeselector b/mod/etc/options_menu/scripts/om_themeselector deleted file mode 100644 index 67f4030..0000000 --- a/mod/etc/options_menu/scripts/om_themeselector +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/sh -# shellcheck disable=SC2086,SC2154,SC3046,SC3051 -# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# shellcheck source=/dev/null -source $mountpoint/etc/options_menu/themes/scripts/om_vars -script_init - -# injectFile "chmenu" "fontfix" -injectFile "chmenu" -hasThemesToInstall -enableMenuOptions -remove "$omDummy" - -if [ "$1" = "reload" ] || [ -f "$om_firstrun" ] ; then - loadThemeList -r - remove "$om_firstrun" - modCleanUp -else - loadThemeList -fi - -$optionsMenu/lib/theme_manager --title INSTALLED_THEMES & diff --git a/mod/etc/options_menu/themes/scripts/om_overlay b/mod/etc/options_menu/themes/scripts/om_overlay deleted file mode 100644 index 244cbe0..0000000 --- a/mod/etc/options_menu/themes/scripts/om_overlay +++ /dev/null @@ -1,3 +0,0 @@ -chrono_trigger -turbografx16_mini -pce_mini_a diff --git a/mod/etc/options_menu/themes/scripts/om_restorethumb b/mod/etc/options_menu/themes/scripts/om_restorethumb deleted file mode 100644 index fcb213d..0000000 --- a/mod/etc/options_menu/themes/scripts/om_restorethumb +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh -# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 -# -# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# shellcheck source=/dev/null -source $mountpoint/etc/options_menu/themes/scripts/om_vars -script_init - -find "$gStorage" -path "*_small.png*" | while read -r file; do - copier "$file"_backup "$file" && rm "$file"_backup -done diff --git a/mod/etc/options_menu/themes/scripts/om_themescript b/mod/etc/options_menu/themes/scripts/om_themescript index b224308..49af448 100644 --- a/mod/etc/options_menu/themes/scripts/om_themescript +++ b/mod/etc/options_menu/themes/scripts/om_themescript @@ -1,5 +1,5 @@ #!/bin/bash -# shellcheck disable=SC2154,SC2038,SC2143 +# shellcheck disable=SC2154,SC2038,SC2143,SC2030,SC2031,SC2001 # Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) # # This program is free software: you can redistribute it and/or modify @@ -164,26 +164,32 @@ themeSelector() { fontBindFix "restore" fi + mountpoint -q "$ORIGINAL_THEME" && umount "$ORIGINAL_THEME" musicHackUnbind - cd "$THEMES_PATH/$PREVIOUS_THEME" && find ./* -type f ! -name "*.wav" ! -path "./xtras/*" ! -path "./fonts/*" | cut -c3- | while IFS= read -r UMOUNT_FILE; do + cd "$THEMES_PATH/$PREVIOUS_THEME" && find ./* -type f ! -name "*.wav" ! -path "./fonts/*" | cut -c3- | while IFS= read -r UMOUNT_FILE; do umount "$ORIGINAL_THEME/$UMOUNT_FILE" done - if [ -f "$STATE_PATH/om_audiorandomizer" ] && [ -n "$(ls -A "$MUSIC_PATH")" ]; then - [ -z "$BGM_HOME" ] && BGM_HOME="$(find "$MUSIC_PATH" -maxdepth 1 -type f -iname "*.wav" | shuf | head -n1)" - if [ "$FOLDER_ID" = "000" ]; then - musicHackBind "$BGM_HOME" "$BGM_HOME" "$ORIGINAL_THEME_AUDIO" - else - [ -f "$STATE_PATH/om_advmusichack" ] && [ -z "$(find "$FULL_THEME_PATH" -type f -name "*.wav")" ] && musicHackBind "$BGM_HOME" "$BGM_HOME" "$ORIGINAL_THEME_AUDIO" + if [ "$FULL_THEME_PATH" != "$DIY_THEME" ] && [ -f "$FULL_THEME_PATH/needs_overlay" ]; then + [ -d "$FULL_THEME_PATH" ] && mount -t overlayfs -o lowerdir="$ORIGINAL_THEME",upperdir="$FULL_THEME_PATH" overlayfs "$ORIGINAL_THEME" + else + if [ -f "$STATE_PATH/om_audiorandomizer" ] && [ -n "$(ls -A "$MUSIC_PATH")" ]; then + [ -z "$BGM_HOME" ] && BGM_HOME="$(find "$MUSIC_PATH" -maxdepth 1 -type f -iname "*.wav" | shuf | head -n1)" + if [ "$FOLDER_ID" = "000" ]; then + musicHackBind "$BGM_HOME" "$BGM_HOME" "$ORIGINAL_THEME_AUDIO" + else + [ -f "$STATE_PATH/om_advmusichack" ] && [ -z "$(find "$FULL_THEME_PATH" -type f -name "*.wav")" ] && musicHackBind "$BGM_HOME" "$BGM_HOME" "$ORIGINAL_THEME_AUDIO" + fi fi - fi - cd "$FULL_THEME_PATH" && find . -type f ! -path "./xtras/*" ! -path "./fonts/*" | while IFS= read -r THEME_FILE; do - mount_bind "$FULL_THEME_PATH/$THEME_FILE" "$ORIGINAL_THEME/$THEME_FILE" - done + cd "$FULL_THEME_PATH" && find . -type f ! -path "./fonts/*" | while IFS= read -r THEME_FILE; do + mount_bind "$FULL_THEME_PATH/$THEME_FILE" "$ORIGINAL_THEME/$THEME_FILE" + done + fi - cd - >/dev/ || exit + cd - >/dev/null || exit + break done } diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index cf112c9..bca429f 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -75,7 +75,6 @@ omModLicense="$omModScripts/om_license" thmPreview="$omModSub/images" omThemeScript="$omModScripts/om_themescript" omOverlayThemeScript="$omModScripts/om_overlaythmcript" -thmOverlay="$omModScripts/om_overlay" om_themerandomizer="$state_path/om_themerandomizer" om_themeperfolder="$state_path/om_themeperfolder" @@ -162,14 +161,29 @@ items_to_delete="$del_on_snes" has_theme="$(ls -1 "$theme_path/$system".*.tar.gz 2> /dev/null | wc -l)" ################ +original_theme_ui0="ui/$sftype-$sfregion" case "$sftype" in hvcj) items_to_delete="$del_on_shonen" + original_theme_ui0="ui/hvc" ;; nes) items_to_delete="$del_on_nes" + original_theme_ui0="clover-ui" + ;; + snes) + [ "$sfregion" = "jpn" ] && original_theme_ui0="ui/shvc" ;; esac +original_theme_path="$mountpoint/usr/share/$original_theme_ui0" + +themeIsBindable() { + find "$1" -type f ! -path "$1/fonts/*" ! -path "$1/info" ! -path "$1/dependencies.json" \ + ! -path "$1/resources/scripts/env/env_*.lua" | while IFS= read -r f; do + [ -f "$original_theme_path/${f#"$1"/}" ] || exit 1 + done +} + hasThemesToInstall() { [ "$has_theme" = 0 ] && return @@ -208,12 +222,8 @@ enableMenuOptions() { enable_cmds "Settings" fi - while IFS='' read -r advThm || [ -n "$advThm" ]; do - enable_cmds "AudioSetting" "ThemeSetting" "AdvancedMusicHack" - if grep -q "$advThm" "$thm_chk"; then - disable_cmds "AudioSetting" "ThemeSetting" "AdvancedMusicHack" - fi - done < "$thmOverlay" + enable_cmds "AudioSetting" "AdvancedMusicHack" + [ -f "$(cat "$thm_chk" 2> /dev/null)/needs_overlay" ] && disable_cmds "AudioSetting" "AdvancedMusicHack" if [ "$sftype" = "nes" ]; then src="hvc" @@ -243,13 +253,11 @@ themeLoader() { removeRegionalTheme cd "$theme_path" && find . -mindepth 1 -maxdepth 1 -type d | sed 's#^\./##' | sort -u | while IFS= read -r thm; do full_path="$theme_path/$thm" - xtras="$full_path/xtras" resource_path="$full_path/resources" sprite_path="$resource_path/sprites" script_path="$resource_path/scripts" cmd_list="$omModCommands/c0001_$thm" thm_script="$omModScripts/$thm" - had_thumb="$theme_path/thumb" tmp_thm_script="$omThemeScript" if [ ! -f "$full_path/info" ]; then @@ -258,6 +266,14 @@ themeLoader() { echo "NAME=$thm_title [${size}b]" > "$full_path/info" fi + if [ ! -f "$full_path/needs_overlay" ] && [ ! -f "$full_path/bind_ok" ]; then + if themeIsBindable "$full_path"; then + touch "$full_path/bind_ok" + else + touch "$full_path/needs_overlay" + fi + fi + if [ ! -f "$full_path.png" ]; then if [ -d "$usb_path" ]; then copier "$thmPreview/$thm.png" "$full_path.png" @@ -284,20 +300,8 @@ themeLoader() { fi copy "$omModLicense" "$thm_script.sh" - if [ -f "$xtras/thumb.png" ]; then - echo "find \"$gStorage\" -path \"*_small.png*\" | while read -r file; do -copier \"\$file\" \"\$file\"_backup -copier \"$xtras/thumb.png\" \"\$file\" -touch \"$had_thumb\" -done" >> "$thm_script.sh" - else - echo "if [ -f \"$had_thumb\" ]; then - sh $omModScripts/om_restorethumb - rm \"$had_thumb\" -fi" >> "$thm_script.sh" - fi - if grep -q -w "$thm" "$thmOverlay" || [ ! -d "$has_folders" ]; then + if [ ! -d "$has_folders" ]; then tmp_thm_script="$omOverlayThemeScript" fi diff --git a/mod/uninstall b/mod/uninstall index 9e7dfcb..55b3017 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -45,7 +45,7 @@ esac ORIGINAL_THEME="$mountpoint/usr/share/$UI0" -sed -i '/# themeselector fix$/,+2d' "$rootfs/bin/chmenu" +sed -i '/# themeselector fix *$/,+2d' "$rootfs/bin/chmenu" sed -i '/# themeselector$/,+4d' "$rootfs/etc/preinit.d/b8025_fontfix" remove "$mountpoint/$profilepath/$modname/lastTheme" remove "$mountpoint/$profilepath/$modname/prevTheme" From 34e0fbbc460075dd2630327d5582133889354232 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 24 Sep 2026 20:13:59 +0000 Subject: [PATCH 090/210] Add default ActiveThemeBorder color handling and update theme rendering logic --- mod/install | 5 ++ src/theme_manager_app.cpp | 135 +++++++++++++++++++++++++++++--------- 2 files changed, 109 insertions(+), 31 deletions(-) diff --git a/mod/install b/mod/install index 81b73b9..c37a342 100644 --- a/mod/install +++ b/mod/install @@ -72,6 +72,11 @@ chmod +x "$usr/bin/sprite_crop" chmod +x "$usr/bin/theme_downloader" chmod +x "$rootfs/etc/options_menu/lib/theme_manager" +theme_cfg="$rootfs/etc/options_menu/theme.cfg" +if [ -f "$theme_cfg" ] && ! grep -q "^ActiveThemeBorder=" "$theme_cfg"; then + echo "ActiveThemeBorder=76,175,110" >> "$theme_cfg" +fi + sed '1,2d' "$om_themes_scripts/om_themescript" >"$b_file" [ ! -f "$p_file" ] && echo themeSelector >"$p_file" diff --git a/src/theme_manager_app.cpp b/src/theme_manager_app.cpp index c94eef6..20a710c 100644 --- a/src/theme_manager_app.cpp +++ b/src/theme_manager_app.cpp @@ -23,12 +23,39 @@ #define MOD_VERSION "dev" #endif +namespace +{ + Uint8 ActiveThemeBorderR = 0x4C, ActiveThemeBorderG = 0xAF, ActiveThemeBorderB = 0x6E; // green + + void LoadActiveThemeBorderColor(const std::string & optionsLocation) + { + std::ifstream in(optionsLocation + "theme.cfg"); + std::string line; + while(std::getline(in, line)) + { + if(!line.empty() && line.back() == '\r') + line.pop_back(); + size_t eq = line.find('='); + if(eq == std::string::npos || line[0] == '#' || line.compare(0, eq, "ActiveThemeBorder") != 0) + continue; + int r, g, b; + if(std::sscanf(line.c_str() + eq + 1, "%d,%d,%d", &r, &g, &b) == 3) + { + ActiveThemeBorderR = static_cast(r); + ActiveThemeBorderG = static_cast(g); + ActiveThemeBorderB = static_cast(b); + } + } + } +} + ThemeManagerApp::ThemeManagerApp(std::string optionsLocation, AppOptions options, std::vector commands, std::vector isThemeItem) : optionsLocation_(std::move(optionsLocation)) , options_(std::move(options)) , commands_(std::move(commands)) , isThemeItem_(std::move(isThemeItem)) { + LoadActiveThemeBorderColor(optionsLocation_); fprintf(stderr, "CHECKPOINT 1: commands loaded, count=%zu\n", commands_.size()); fflush(stderr); // std::make_unique is C++14; this project builds as C++11 @@ -385,6 +412,33 @@ int ThemeManagerApp::RunGridLayout() } }; + // marks the tile of the theme currently applied on the console, so it stays visually + // distinguishable from the rest of the grid regardless of where the cursor is + std::vector isActiveTheme(commands_.size(), false); + if(options_.titleKey == "INSTALLED_THEMES") + { + std::string activeTheme; + std::ifstream in("/var/lib/clover/profiles/0/hakchi/lastTheme"); + std::getline(in, activeTheme); + if(!activeTheme.empty() && activeTheme.back() == '\r') + activeTheme.pop_back(); + + if(!activeTheme.empty()) + { + const std::string suffix = ".sh"; + for(int i = themeStart_; i < pinnedStartIndex_; ++i) + { + const std::string & cmd = commands_[i].command; + if(cmd.size() <= suffix.size() || cmd.compare(cmd.size()-suffix.size(), suffix.size(), suffix) != 0) + continue; + size_t slashPos = cmd.find_last_of('/'); + size_t start = (slashPos == std::string::npos) ? 0 : slashPos+1; + if(cmd.compare(start, cmd.size()-suffix.size()-start, activeTheme) == 0) + isActiveTheme[i] = true; + } + } + } + int gridTopRow = 0; auto SetCurrentCommand = [&](int newId) { @@ -556,7 +610,19 @@ int ThemeManagerApp::RunGridLayout() SDL_RenderSetClipRect(renderer_, nullptr); DrawRoundedCornerMask(renderer_, tileRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); } - DrawStrokeRect(renderer_, tileRect, selected ? UiTheme::AccentR : UiTheme::BorderR, selected ? UiTheme::AccentG : UiTheme::BorderG, selected ? UiTheme::AccentB : UiTheme::BorderB, selected ? 3 : UiTheme::BorderWidth, UiTheme::BoxRadius); + Uint8 strokeR = UiTheme::BorderR, strokeG = UiTheme::BorderG, strokeB = UiTheme::BorderB; + int strokeW = UiTheme::BorderWidth; + if(selected) + { + strokeR = UiTheme::AccentR; strokeG = UiTheme::AccentG; strokeB = UiTheme::AccentB; + strokeW = 3; + } + else if(isActiveTheme[idx]) + { + strokeR = ActiveThemeBorderR; strokeG = ActiveThemeBorderG; strokeB = ActiveThemeBorderB; + strokeW = 3; + } + DrawStrokeRect(renderer_, tileRect, strokeR, strokeG, strokeB, strokeW, UiTheme::BoxRadius); if(!tileHideLabel[idx]) { tileLabels[idx].rect.x = x + (TileW - tileLabels[idx].rect.w) / 2; @@ -586,6 +652,11 @@ int ThemeManagerApp::RunListLayout() scrollDown.rect.y = UiTheme::ScrollDownY; SDL_Rect selectedRowRect{ UiTheme::ListX, UiTheme::RowFirstY - 2, UiTheme::ListContentRightX - UiTheme::ListX, 0 }; + // B runs Back/Exit directly - same footer badge and behavior as the grid layout + Badge badgeB{ Texture("B", 16, renderer_, 0, 0, false, UiTheme::BadgeLetterColor, true), + Texture(pinnedStartIndex_ < (int)commands_.size() ? Translate(commands_[pinnedStartIndex_].name) : "", 16, renderer_, 0, 0, false, UiTheme::TextColor, true), + UiTheme::BadgeBDark, UiTheme::BadgeB }; + const int RowGlyphSize = 16; const int RowTextGapPx = 16; const int ChildIndent = 4*16; @@ -597,18 +668,8 @@ int ThemeManagerApp::RunListLayout() } const int modernRowPitch = std::max(UiTheme::RowPitch, GetTTFLineHeight(RowGlyphSize)); - int pinnedAreaTop; - { - int slotBottom = UiTheme::FooterDividerY - UiTheme::PinnedBottomMargin; - for(int i = static_cast(commands_.size())-1; i >= pinnedStartIndex_; --i) - { - int slotTop = slotBottom - modernRowPitch; - commands_[i].texture.rect.y = slotTop + (modernRowPitch - commands_[i].texture.rect.h) / 2 + UiTheme::RowTextYNudge; - slotBottom = slotTop; - } - pinnedAreaTop = slotBottom; - } - const int DisplayItemCount = std::max(1, (pinnedAreaTop - UiTheme::RowFirstY) / modernRowPitch); + // Back/Exit no longer takes a row of its own here - B runs it directly via the footer badge above + const int DisplayItemCount = std::max(1, (UiTheme::FooterDividerY - UiTheme::PinnedBottomMargin - UiTheme::RowFirstY) / modernRowPitch); int topListItemNumber = pinnedStartIndex_ + 1; // forces the first SetCurrentCommand to lay out row positions std::shared_ptr PreviewImage; @@ -631,6 +692,16 @@ int ThemeManagerApp::RunListLayout() topListItemNumber = scrollTarget-DisplayItemCount+1; updateCommandYPos = true; } + + // never scroll past the point where the list stops filling the display area - + // otherwise restoring focus on a short list (e.g. after a toggle relaunches this + // screen) scrolls the focused row to the top and hides earlier rows for no reason + const int maxTopListItemNumber = std::max(0, pinnedStartIndex_-DisplayItemCount); + if(topListItemNumber > maxTopListItemNumber) + { + topListItemNumber = maxTopListItemNumber; + updateCommandYPos = true; + } if(updateCommandYPos) { int y = UiTheme::RowFirstY; @@ -657,7 +728,7 @@ int ThemeManagerApp::RunListLayout() SaveFocusState(); }; int restoredId = LoadFocusIndex(); - SetCurrentCommand(restoredId >= 0 ? restoredId : 0); + SetCurrentCommand(restoredId >= 0 && restoredId < pinnedStartIndex_ ? restoredId : 0); auto DrawRow = [&](Command & rowCommand, bool isLastOverall) { @@ -679,8 +750,13 @@ int ThemeManagerApp::RunListLayout() { DrawChromeCommon(); DrawSectionTitle(); - if(!commands_[currentCommandId_].deleteCommand.empty()) - DrawBadge(badgeHold_, badgeRowRightEdge_); + { + int rightEdge = badgeRowRightEdge_; + if(pinnedStartIndex_ < (int)commands_.size()) + rightEdge = DrawBadge(badgeB, rightEdge); + if(!commands_[currentCommandId_].deleteCommand.empty()) + DrawBadge(badgeHold_, rightEdge); + } DrawRoundedFillRect(renderer_, selectedRowRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); DrawStrokeRect(renderer_, selectedRowRect, UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB, 2, UiTheme::BoxRadius); @@ -693,8 +769,6 @@ int ThemeManagerApp::RunListLayout() } }; - const int lastCommandIndex = static_cast(commands_.size()) - 1; // commands_ never resized during this loop - for(;;) { FrameEvent frameEvent = PollFrameEvents(); @@ -711,24 +785,25 @@ int ThemeManagerApp::RunListLayout() if(ActivateCommand(commands_[currentCommandId_])) break; } - else if(controller_->HeldRepeat(UP)) + else if(controller_->HeldRepeat(UP) && pinnedStartIndex_ > 0) { - // bounded so an all-separator list can't spin forever + // bounded so an all-separator list can't spin forever; stays below + // pinnedStartIndex_ - Back/Exit isn't a row here, B runs it directly int newCommandId = currentCommandId_; - for(size_t tries = 0; tries < commands_.size(); ++tries) + for(int tries = 0; tries < pinnedStartIndex_; ++tries) { - newCommandId = (newCommandId-1+commands_.size())%commands_.size(); + newCommandId = (newCommandId-1+pinnedStartIndex_)%pinnedStartIndex_; if(commands_[newCommandId].command.size() != 0) break; } SetCurrentCommand(newCommandId); } - else if(controller_->HeldRepeat(DOWN)) + else if(controller_->HeldRepeat(DOWN) && pinnedStartIndex_ > 0) { int newCommandId = currentCommandId_; - for(size_t tries = 0; tries < commands_.size(); ++tries) + for(int tries = 0; tries < pinnedStartIndex_; ++tries) { - newCommandId = (newCommandId+1)%commands_.size(); + newCommandId = (newCommandId+1)%pinnedStartIndex_; if(commands_[newCommandId].command.size() != 0) break; } @@ -738,17 +813,15 @@ int ThemeManagerApp::RunListLayout() bool tapped = false; if(UpdateBButton(tapped)) break; - if(tapped) - SetCurrentCommand(commands_.size()-1); + // quick tap - Back/Exit isn't a row you navigate to, B runs it directly (matches the grid layout) + if(tapped && pinnedStartIndex_ < (int)commands_.size() && ActivateCommand(commands_[pinnedStartIndex_])) + break; } DrawChrome(); for(int i = 0, count = std::min(DisplayItemCount, pinnedStartIndex_-topListItemNumber); i < count; ++i) - DrawRow(commands_[i+topListItemNumber], (i+topListItemNumber) == lastCommandIndex); - - for(int i = pinnedStartIndex_; i <= lastCommandIndex; ++i) - DrawRow(commands_[i], i == lastCommandIndex); + DrawRow(commands_[i+topListItemNumber], (i+topListItemNumber) == pinnedStartIndex_-1); // bounded by pinnedStartIndex_, not commands_.size() - pinned rows need no scroll if(topListItemNumber != 0) From 84728a4592d50e5b969b8b79a3db00be31ae35ad Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 24 Sep 2026 20:35:36 +0000 Subject: [PATCH 091/210] Update README.md to clarify compatibility and enhance feature descriptions --- README.md | 113 +++++++++++++++++++++++++++++--------------------- mod/readme.md | 103 +++++++++++++++++++++++---------------------- 2 files changed, 119 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index ceac30c..0d295ea 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,93 @@ # Options Menu - Theme Selector -**This modification is only for use with [CompCom's](https://github.com/CompCom) Options Menu.** -### HMOD NOT COMPATIBLE WITH EXTERNAL UI.
PREVIOUS VERSIONS OF THEME RANDOMIZER AND ADVANCED MUSIC HACK WILL BE UNINSTALLED. -### What is it? -This [Options Menu](https://github.com/CompCom/OptionsMenu/releases/latest) module gives you the possibility to choose your favorite theme and apply it on your NESCE or SNESCE.
-It includes all the features of it's predecessor and much more +**Requires [my Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases/latest) as a base — not compatible with any other UI.** `Click the image below to view it on YouTube.` -[![Theme Selector - Demo](https://i.imgur.com/Ixi8LYu.png)](https://youtu.be/yRUbGssu8ko "Theme Selector - Demo") -### Features -* Graphical interface to allow you to select themes -* Download themes directy from the internet (**Wi-Fi mod required**) -* Theme randomizer (**Off by default**) -* Audio randomizer on home folder (**Off by default**) -* Set theme per folder (keeping the same the per sub-folder) -* Create a theme based your existent theme resources -* Custom fonts per theme -* Select a theme for the entire system -* Select a theme just for the home folder -* Custom icons for Option Menu +[![Theme Selector - Demo](https://i.imgur.com/7JgP6JI.png)](https://youtu.be/yRUbGssu8ko "Theme Selector - Demo") -### Can i use it with NAND -Well, yes. But this mod was designed for USB-HOST, as the themes may include audio files and they may get over 10Mb. +## What is it? -### Set theme per folder, what is that? -If you are like me and have tons of games organized with tons of system folders... Wouldn't be nice if you could have a theme associated with each system? Now you can, just create your own theme and add it to the HMOD or directly to your USB/SD theme folder. -On this version all subfolders, will keep the parent theme. +A graphical theme manager for NES/SNES/Famicom/Super Famicom Classic consoles, built on top of [my fork](https://github.com/DefKorns/OptionsMenu) of [CompCom's OptionsMenu](https://github.com/CompCom/OptionsMenu). Pick a theme from a preview grid, download more from a catalog, build your own from pieces of the themes you already have, and tune per-folder/randomizer/audio behavior — all from the console itself, no PC required after install. -### Ok and how will the system load the theme? -Just give it the same name as your folder (lower snake_case). Eg: game folder: `Nintendo - Nintendo Entertainment System` theme folder: `nintendo_-_nintendo_entertainment_system`. +The UI is my own standalone C++/SDL app (`theme_manager`), not a set of Lua scripts layered on the stock menu — it's built from source against the vendored engine (see [Building from source](#building-from-source)). -### What if i want a specific theme on my main menu? -Add a theme as before but name it `default` in order to make it you home menu theme. -That simple. +## Features -### I own a Famicom/Shonen/Super Famicom, can i install this? -The mod is compatible with all Nintendo Classic consoles including all regions. +- Preview grid to browse and apply installed themes; the theme currently applied is outlined so you can spot it at a glance +- Download themes directly from the internet (**Wi-Fi mod required**) +- Build your own DIY theme from pieces of the themes you already have (background, sprites, colors, packed art, fonts...) +- Theme randomizer — a different theme each time you go Home (off by default) +- Audio randomizer on the Home folder, optionally extended to every folder (off by default) +- Theme per folder: give a folder its own theme by naming a theme after it; a folder with its own theme keeps its own icon instead of inheriting the parent theme's +- Custom fonts per theme +- Custom color palette for the Theme Manager UI itself (`theme.cfg`) +- System clean-up to remove files your console type doesn't need +- Reset just this mod's settings without a full uninstall -### What do you mean with "Create a theme based your existent theme resources"? -Exactly that! If you have several themes on your console/usb, why not take advantage of their resources and create your own DIY theme? -Simply select from the listed previews and create your own DIY theme. +## Set a theme per folder, what is that? -### Requirements -* [Hakchi CE v3.5.0 >=](https://github.com/TeamShinkansen/hakchi2/releases/latest) -* [Options Menu v.1.3.4 >=](https://github.com/CompCom/OptionsMenu/releases/latest) -* [Hakchi Wi-Fi mod (WPA Supplicant)](https://hakchi.net/hakchi/hmods/wpa-supplicant.hmod) +If you have your games organized into system folders, you can give each one its own theme instead of one theme for the whole console. Turn on **Theme Per Folder** in Settings, then name a theme folder after the game folder it should apply to (lower snake_case). Eg: game folder `Nintendo - Nintendo Entertainment System` → theme folder `nintendo_-_nintendo_entertainment_system`. -### How do i use it +By default, a subfolder inherits its parent's theme. Turn on **Allow Different Theme To Subfolder** if you want a subfolder to use its own matching theme instead — and if it does, that subfolder keeps its own real icon rather than being overwritten by the parent theme's. -**NAND or USB/SD** +## What if I want a specific theme on my main menu? -- Install Hmod -- Download the themes and enjoy +Name a theme `default` and it becomes your Home menu theme. -on NAND themes will be on `/var/lib/hakchi/usr/share/themes/consoletype` -on USB/SD themes will be on `/media/hakchi/themes/consoletype` +## What do you mean by "build your own DIY theme"? -*consoletype is just `nes`, `snes` or `shonen`* +Exactly that — pick and mix backgrounds, sprites, colors and packed art from the themes already on your console/USB into a theme of your own, previewed live as you build it. -### Notes -Enjoy it as much as i did making it. +## I own a Famicom/Shonen/Super Famicom, can I install this? + +Yes — it supports all Nintendo Classic consoles, all regions. + +## Requirements + +- [Hakchi CE](https://github.com/TeamShinkansen/hakchi2/releases/latest) +- [My Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases/latest) — the base UI this mod plugs into +- [Hakchi Wi-Fi mod (WPA Supplicant)](https://hakchi.net/hakchi/hmods/wpa-supplicant.hmod) — only needed to download themes from the internet + +## How do I use it + +Works the same whether your games are on NAND or USB/SD: + +- Install the hmod +- Open Theme Options from the Options Menu, download or build a theme, and apply it + +On NAND, themes live at `/var/lib/hakchi/usr/share/themes/`. +On USB/SD, themes live at `/media/hakchi/themes/`. + +*`consoletype` is `nes`, `snes` or `shonen`.* + +## Customizing the look + +The Theme Manager's own UI (not your game themes) reads its color palette from `/etc/options_menu/theme.cfg` at startup — edit `Key=R,G,B` lines there (background, borders, accent, badges, the active-theme border...) to restyle it, or delete a line to fall back to the default. + +## Building from source + +```sh +./build.sh compile # cross-compiles theme_manager + theme_downloader via Docker +./build.sh hmod # packages mod/ into out/*.hmod +``` + +`build.sh` pulls the toolchain and `vendor/OptionsMenu` submodules on first run and builds everything inside a Docker container — no local ARM toolchain needed. + +## Credits -### Credits - [DefKorns](https://gitlab.com/DefKorns) - [DanTheMan827](https://github.com/DanTheMan827) (packed.png extractor) -### Thanks +## Thanks + - AluCarD - [BsLeNuL](https://github.com/bslenul) -- [CompCom](https://github.com/CompCom) +- [CompCom](https://github.com/CompCom) — original Options Menu - [KMFDManic](https://github.com/KMFDManic) - [ModMyClassic](https://modmyclassic.com/) - NESminiling0618 - [Swingflip](https://github.com/swingflip) +## License + +GPLv3 or later — see [LICENSE](LICENSE). diff --git a/mod/readme.md b/mod/readme.md index e3bb5c8..1e7ee95 100644 --- a/mod/readme.md +++ b/mod/readme.md @@ -1,74 +1,79 @@ # Options Menu - Theme Selector -**This modification is only for use with [CompCom's](https://github.com/CompCom) Options Menu.** -### HMOD NOT COMPATIBLE WITH EXTERNAL UI.
PREVIOUS VERSIONS OF THEME RANDOMIZER AND ADVANCED MUSIC HACK WILL BE UNINSTALLED. -### What is it? -This [Options Menu](https://github.com/CompCom/OptionsMenu/releases/latest) module gives you the possibility to choose your favorite theme and apply it on your NESCE or SNESCE.
-It includes all the features of it's predecessor and much more +**Requires [my Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases/latest) as a base — not compatible with any other UI.** -[![Theme Selector](https://i.imgur.com/WXN7Nwl.png)](https://i.imgur.com/qyTv9Fs.png) -### Features -* Graphical interface to allow you to select themes -* Download themes directy from the internet (Wi-Fi mod required) -* Theme randomizer (Off by default) -* Audio randomizer on home folder (Off by default) -* Set theme per folder (keeping the same the per sub-folder) -* Create a theme based your existent theme resources -* Custom fonts per theme -* Select a theme for the entire system -* Select a theme just for the home folder -* Custom icons for Option Menu +[![Theme Selector](https://i.imgur.com/7JgP6JI.png)](https://i.imgur.com/7JgP6JI.png) -### Can i use it with NAND -Well, yes. But this mod was designed for USB-HOST, as the themes may include audio files and they may get over 10mb. +## What is it? -### Set theme per folder, what is that? -If you are like me and have tons of games organized with tons of system folders... Wouldn't be nice if you could have a theme associated with each system? Now you can, just create your own theme and add it to the HMOD or directly to your USB/SD theme folder. -On this version all subfolders, will keep the parent theme. +A graphical theme manager for NES/SNES/Famicom/Super Famicom Classic consoles, built on top of [my fork](https://github.com/DefKorns/OptionsMenu) of [CompCom's OptionsMenu](https://github.com/CompCom/OptionsMenu). Pick a theme from a preview grid, download more from a catalog, build your own from pieces of the themes you already have, and tune per-folder/randomizer/audio behavior — all from the console itself, no PC required after install. -### Ok and how will the system load the theme? -Just give it the same name as your folder (lower snake_case). Eg: game folder: `Nintendo - Nintendo Entertainment System` theme folder: `nintendo_-_nintendo_entertainment_system`. +The UI is my own standalone C++/SDL app (`theme_manager`), not a set of Lua scripts layered on the stock menu. -### What if i want a specific theme on my main menu? -Add a theme as before but name it `default` in order to make it you home menu theme. -That simple. +## Features -### I own a Famicom/Shonen/Super Famicom, can i install this? -The mod is compatible with all Nintendo Classic consoles including all regions. +- Preview grid to browse and apply installed themes; the theme currently applied is outlined so you can spot it at a glance +- Download themes directly from the internet (**Wi-Fi mod required**) +- Build your own DIY theme from pieces of the themes you already have (background, sprites, colors, packed art, fonts...) +- Theme randomizer — a different theme each time you go Home (off by default) +- Audio randomizer on the Home folder, optionally extended to every folder (off by default) +- Theme per folder: give a folder its own theme by naming a theme after it; a folder with its own theme keeps its own icon instead of inheriting the parent theme's +- Custom fonts per theme +- Custom color palette for the Theme Manager UI itself (`theme.cfg`) +- System clean-up to remove files your console type doesn't need +- Reset just this mod's settings without a full uninstall -### What do you mean with "Create a theme based your existent theme resources"? -Exactly that! If you have several themes on your console/usb, why not take advantage of their resources and create your own DIY theme? -Simply select from the listed previews and create your own DIY theme. +## Set a theme per folder, what is that? -### Requirements -* [Hakchi CE v3.5.0 >=](https://github.com/TeamShinkansen/hakchi2/releases/latest) -* [Options Menu v.1.3.4 >=](https://github.com/CompCom/OptionsMenu/releases/latest) -* [Hakchi Wi-Fi mod (WPA Supplicant)](https://hakchi.net/hakchi/hmods/wpa-supplicant.hmod) +If you have your games organized into system folders, you can give each one its own theme instead of one theme for the whole console. Turn on **Theme Per Folder** in Settings, then name a theme folder after the game folder it should apply to (lower snake_case). Eg: game folder `Nintendo - Nintendo Entertainment System` → theme folder `nintendo_-_nintendo_entertainment_system`. -### How do i use it +By default, a subfolder inherits its parent's theme. Turn on **Allow Different Theme To Subfolder** if you want a subfolder to use its own matching theme instead — and if it does, that subfolder keeps its own real icon rather than being overwritten by the parent theme's. -**NAND or USB/SD** +## What if I want a specific theme on my main menu? -- Install Hmod -- Download the themes and enjoy +Name a theme `default` and it becomes your Home menu theme. -on NAND themes will be on `/var/lib/hakchi/usr/share/themes/consoletype` -on USB/SD themes will be on `/media/hakchi/themes/consoletype` +## What do you mean by "build your own DIY theme"? -*consoletype is just `nes`, `snes` or `shonen`* +Exactly that — pick and mix backgrounds, sprites, colors and packed art from the themes already on your console/USB into a theme of your own, previewed live as you build it. -### Notes -Enjoy it as much as i did making it. +## I own a Famicom/Shonen/Super Famicom, can I install this? + +Yes — it supports all Nintendo Classic consoles, all regions. + +## Requirements + +- [Hakchi CE](https://github.com/TeamShinkansen/hakchi2/releases/latest) +- [My Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases/latest) — the base UI this mod plugs into +- [Hakchi Wi-Fi mod (WPA Supplicant)](https://hakchi.net/hakchi/hmods/wpa-supplicant.hmod) — only needed to download themes from the internet + +## How do I use it + +Works the same whether your games are on NAND or USB/SD: + +- Install the hmod +- Open Theme Options from the Options Menu, download or build a theme, and apply it + +On NAND, themes live at `/var/lib/hakchi/usr/share/themes/`. +On USB/SD, themes live at `/media/hakchi/themes/`. + +*`consoletype` is `nes`, `snes` or `shonen`.* + +## Customizing the look + +The Theme Manager's own UI (not your game themes) reads its color palette from `/etc/options_menu/theme.cfg` at startup — edit `Key=R,G,B` lines there (background, borders, accent, badges, the active-theme border...) to restyle it, or delete a line to fall back to the default. + +## Credits -### Credits - [DefKorns](https://gitlab.com/DefKorns) - [DanTheMan827](https://github.com/DanTheMan827) (packed.png extractor) -### Thanks +## Thanks + - AluCarD - [BsLeNuL](https://github.com/bslenul) -- [CompCom](https://github.com/CompCom) +- [CompCom](https://github.com/CompCom) — original Options Menu - [KMFDManic](https://github.com/KMFDManic) - [ModMyClassic](https://modmyclassic.com/) - NESminiling0618 -- [Swingflip](https://github.com/swingflip) \ No newline at end of file +- [Swingflip](https://github.com/swingflip) From d60350a9ced2d64cc4d0cd50291faf26e9853209 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 24 Sep 2026 23:32:41 +0000 Subject: [PATCH 092/210] Update preview images in options menu for improved visuals --- .../images/preview_audio_randomizer_all.png | Bin 2007 -> 4296 bytes .../images/preview_audio_randomizer_home.png | Bin 1848 -> 4242 bytes .../options_menu/images/preview_clean_up.png | Bin 847 -> 2972 bytes .../options_menu/images/preview_diy_theme.png | Bin 1684 -> 4968 bytes .../images/preview_download_themes.png | Bin 1391 -> 5258 bytes .../images/preview_theme_options.png | Bin 3169 -> 4475 bytes .../images/preview_theme_randomizer.png | Bin 3034 -> 5254 bytes .../images/preview_update_theme_list.png | Bin 2141 -> 4418 bytes mod/etc/options_menu/images/randtheme.png | Bin 6188 -> 4117 bytes mod/etc/options_menu/images/uninstall.png | Bin 933 -> 5322 bytes 10 files changed, 0 insertions(+), 0 deletions(-) diff --git a/mod/etc/options_menu/images/preview_audio_randomizer_all.png b/mod/etc/options_menu/images/preview_audio_randomizer_all.png index 86678fc45047f7fb4bef5804dd717b4973e76531..02bf7d9091127e0f693f330c6c0cdb8bd25994aa 100644 GIT binary patch literal 4296 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 literal 2007 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 Y8II2psF+y#{c8ZME$q!(OuZld4@{iwIsgCw literal 1848 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 diff --git a/mod/etc/options_menu/images/preview_diy_theme.png b/mod/etc/options_menu/images/preview_diy_theme.png index 309532a80edfc61bdd3e50d3798f002a34bb58b2..536511e0bf472a74d7d01ff92a08eff8e2bd40a1 100644 GIT binary patch literal 4968 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 YQ+bqDyQlj1@TmgeEN#&*Eqr4C2gBt^L;wH) literal 3169 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 diff --git a/mod/etc/options_menu/images/preview_theme_randomizer.png b/mod/etc/options_menu/images/preview_theme_randomizer.png index bbba495ee6725a0bd28eb7ee33938907da74b2c9..20d29208a63ed9e7508751b3679e13aab632c477 100644 GIT binary patch literal 5254 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 L=>V@XzxC)p#2COu diff --git a/mod/etc/options_menu/images/preview_update_theme_list.png b/mod/etc/options_menu/images/preview_update_theme_list.png index 8231ffa3e48ab45f4e93ee53015c6feac850dbe1..b2ffb3352484904fc9a475334a0b9dc18a72bf45 100644 GIT binary patch literal 4418 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 BG&cYM literal 2141 zcmchZ`BTz~8^^&=u}q~bZ$ZR7SIg8^^A-@KG&Ao|OVm7I*E7WwJle=82{p>Jc9k;4 z@+ip4yG+uoB=h>DR@;S!tw)gJvF_jSo%#Ooyq|f^JTuSCGtYJQkgxaF&AOXGAkbDk z4t;pT3pb1jRoYO;i=RJ&Ku{DO?QtZtLNbvpj0rS&I`?H?Ii{S*r1++3Sy@zn8y ziSH`KE*<z7k*zpq_(ocWgXYqs0>h{Oi|2ph!IoF>WHlg*v68n7Ds{(GA> zRO?d?RqfMuiQnW;#pt|Nd(^RMhnqG zJNJ+ML3A}?_rqWfP~z-yiQkq$%XhSve2~Gd<6sp91r!v@LLl7p>7f4seznWq{`hW= zI=+`cE$2p~Ts%dCY_*1l4?%buXVDZtLRjW=c_xw~2FE_~j)9QWlBOa> z1e3O-q5u;E(NAH{%*-*i;P%pbI62ZpyqDds4?Fq%C$GU-!PZLvHx)nEWd@m1_07fH z<%eU^8dRkojHVw2gea3)I*n~_q+d`(OYu-%cXDGkfm+R-5ZKfQo2xSEs=|y>*J^=| zPg#QD5BVb>s@GTzc{p818?3{Sh+Ka*5k#xIMu*R`bCcv;hFP%ffjs(wXVTX*Wz`i!F0-YJMEn$M z(FH$(c`^wDtO4gBQ2?qfL{#D=o5SHpMkftnpQO8QcFiYlU#Ii}lQXXS#&U}#%D%}J zh7|cx;C}k-!cL$!Mse4wHIJ<_j(8o&v*x79u!FR+@Fuq5HdvfDy4Lp~3Q1uw?h~lD zxa#57&alVbSrCsz{_1{RBw(ZN)~nC!3cWq8!Xp zp?vbOFh~p6!`PH8eq{W75Q{?QhAEhdMWK@DH!#**Tc}8iy;6)H-}5mrx|H!VPQqO2 zte5eNphE2=4$u;}uPDbB8KDLHM0s-*U1zlz1?t{Q$I+$N<~-hXoheasZ>?4%J<){Xu5(Phh6()7Rs&eE#;MOt!FjbK zP0OXq*RbY(($$-(`$tJoT!F-RCaTNd)%BZE7t#np2G~>LUHB>8pO>TZG*|-L$@o&9 z!@R&cwwQjetTqd~)J5-A*uvO=EFN~LEY=J;skhgm|9gij6bBTrWf|(3R*iHhF5E#{ zXD4cU0u`o#LuH{UR_gyv5)PPDX|N7afRG;2_3hs^bt)hc zWeXKn7OFkv@}aH6r#w@4D9~dE&j{H^&3nW~?&s5!rZA$!5+vtkYC_dO5q)tfX@1gN z0Kd_*Q1x_2RXzQ*l7a}%T1R!fcML-a)JqoIxx(}8c9-6Ud+{)q8f;Drf8DR>TO)BC zdCm^wFV*k&z7_Gq2(cQLIu((W{WyD=%!w+E$9X>p6=70We6H$(#Yf`e*F6t&pG()` zfx97t0s`^sKRovAB~5u$+Z{97@b|a@$G$G;(acKsSG4<64OoZY z@(w8wFGm8Yaxxk2|M=oT+u%##`(2QI%ya*RoXjN>EmEnWLr*gjvjnwclVPUCnYq

1gv}A~HmtPl>aWMrQ)cwY9lRHs zZZM#CyuIbZZr2BY8{hEyXY5>~)#F(HZxP&P!^L(Q`he4$HJ^sE2u{H)@t|N)NUIS< zx7&yxGFn`&XtgA*7dn|WTaAwK-ZoKv(JL9umw%35SHUj$nj>>b@DXj>8FbR(T0?( z(lET+#k6#Escg3?g!E$i(1N8_W|eZi$Br#H4Gn~cI~@WBZ-YSo&tNF9F>koj-C0D+ zvxL4n$Abn{s@LIfXDj|ycRKK6dbzeT1cmhN-@K_mL9*2DpuB!k(Fhe{ZwR}Pvp&1@ ziVBA~JleRsPbZ*_NzHC+1N3j+hyFBq9bma^GZ4`kS8Ip6x$!oD@EBk89nYxze*sbc B)ZzdD diff --git a/mod/etc/options_menu/images/randtheme.png b/mod/etc/options_menu/images/randtheme.png index b1da58e1d6cd05a80c7493cef69253a4bffc1103..ff72c560ad83a897326cc3c0593f2905317c23d6 100644 GIT binary patch literal 4117 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 diff --git a/mod/etc/options_menu/images/uninstall.png b/mod/etc/options_menu/images/uninstall.png index 9bbe7890cef0d6541997056f61e5bad751be8cf2..f0101ae1664ebc3cd8a7b26d1fe32d54dc10fec8 100644 GIT binary patch literal 5322 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 literal 933 zcmeAS@N?(olHy`uVBq!ia0y~yV2lM~5f)~kNY%$53P4IFz$e5NNdG4Uws7{o1ghgH z3GxeOFjm!)7v-1aV%9a7o+!42fq_}Y)5S5Q;?~={8~ud?We$9l7xj95guhWKwo31m zT61$^ql>~tR@S6N-m@ogNvqCU6kMv%7b?s%+t)-bkLAaIoA;NVg!3%D@_knL=f64c z@2dXgTW|g)_7@A#$^&0(a$ipED$Z9GxGDVBwJ`Qjz{Pt$-U6R=-}-LUR6LQj{!oO# z;#;dHwJ0p!yGvhj!FJ}oU!uNbJvx5vX&lRq=vQUjd%s-r*59l2<o?;v*Y%h(q9q1{m;4%{a3e~KWYYDS3A0R z&8~2<`n*-u_m9l)+GVaB_Tu#Q_e^$y-@6|9`qgH1=tuhgEjs+0IqA&XeJ6wace89O z54%(0uMe@A@$0#olEshqf0tsaINcCe^>XQS5b=H2r>ibY>d%D!zV7v%>5^lxO|+lA z@2~9_-cI+ddb#z*((8++$92b5=~n4~d)6;}_S+AO(vL+3d(HLsG>Vnx@$_duGJ{wE z_MaxI9uxzp%6Y$cak-H9+mCftdHlyOJ*+XJ7oQ|B#Q0vrvX(mDi*6Zw-uW$X zfxER*`);AGiJqNmDhpMO?boGxhHxoumE?52WW7%6d8o8xXz%in?<$v^?yM1R{I*Va zy;rU6;;%0h!e_5uf5JF+>e*j)3kB2iEzeyFa(lcbret-D|Fy$)rO~Gkem~XW=K(au zif3=o<-JSH_ZhJ4-C2G6m+_5zXEIASUb$KQJ@k&XnCyw&Gb{eq%~*BHEz#Xc@a2+{ sFU!swOKL889Uh`8eIJzH9_;@nz5Qg~wJ2`+Cm=s~y85}Sb4q9e0Bow;ZvX%Q From 29b31641c1992d25a8d6bfd95aa0fc7174f5abbb Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 24 Sep 2026 23:58:52 +0000 Subject: [PATCH 093/210] Add theme applying messages in multiple languages and update theme script for reboot handling --- mod/etc/options_menu/themes/lang/de-DE.lang | 2 ++ mod/etc/options_menu/themes/lang/en-US.lang | 2 ++ mod/etc/options_menu/themes/lang/es-ES.lang | 2 ++ mod/etc/options_menu/themes/lang/fr-FR.lang | 2 ++ mod/etc/options_menu/themes/lang/it-IT.lang | 2 ++ mod/etc/options_menu/themes/lang/ja-JP.lang | 2 ++ mod/etc/options_menu/themes/lang/nl-NL.lang | 2 ++ mod/etc/options_menu/themes/lang/pt-PT.lang | 2 ++ mod/etc/options_menu/themes/lang/ru-RU.lang | 2 ++ mod/etc/options_menu/themes/scripts/om_vars | 5 +++-- 10 files changed, 21 insertions(+), 2 deletions(-) diff --git a/mod/etc/options_menu/themes/lang/de-DE.lang b/mod/etc/options_menu/themes/lang/de-DE.lang index 97efad0..a3d8e23 100644 --- a/mod/etc/options_menu/themes/lang/de-DE.lang +++ b/mod/etc/options_menu/themes/lang/de-DE.lang @@ -42,3 +42,5 @@ THEME_LOADING=Wird geladen... THEME_LOADING_ASSETS=Assets werden geladen... THEME_SAVING=Eigenes Theme wird gespeichert... THEME_PREVIEW_LOADING=Themenvorschau wird geladen... +THEME_APPLYING_LINE1=Design wird übernommen +THEME_APPLYING_LINE2=Neustart läuft . . . diff --git a/mod/etc/options_menu/themes/lang/en-US.lang b/mod/etc/options_menu/themes/lang/en-US.lang index f3ddf78..7bc6fbb 100644 --- a/mod/etc/options_menu/themes/lang/en-US.lang +++ b/mod/etc/options_menu/themes/lang/en-US.lang @@ -42,3 +42,5 @@ THEME_LOADING=Loading... THEME_LOADING_ASSETS=Loading assets... THEME_SAVING=Saving custom theme... THEME_PREVIEW_LOADING=Loading theme preview... +THEME_APPLYING_LINE1=Applying Theme +THEME_APPLYING_LINE2=Rebooting Now . . . diff --git a/mod/etc/options_menu/themes/lang/es-ES.lang b/mod/etc/options_menu/themes/lang/es-ES.lang index b126c46..32ba1d0 100644 --- a/mod/etc/options_menu/themes/lang/es-ES.lang +++ b/mod/etc/options_menu/themes/lang/es-ES.lang @@ -42,3 +42,5 @@ THEME_LOADING=Cargando... THEME_LOADING_ASSETS=Cargando recursos... THEME_SAVING=Guardando tema personalizado... THEME_PREVIEW_LOADING=Cargando vista previa del tema... +THEME_APPLYING_LINE1=Aplicando Tema +THEME_APPLYING_LINE2=Reiniciando Ahora . . . diff --git a/mod/etc/options_menu/themes/lang/fr-FR.lang b/mod/etc/options_menu/themes/lang/fr-FR.lang index 9c5c525..0c02144 100644 --- a/mod/etc/options_menu/themes/lang/fr-FR.lang +++ b/mod/etc/options_menu/themes/lang/fr-FR.lang @@ -42,3 +42,5 @@ THEME_LOADING=Chargement... THEME_LOADING_ASSETS=Chargement des ressources... THEME_SAVING=Enregistrement du thème personnalisé... THEME_PREVIEW_LOADING=Chargement de l'aperçu du thème... +THEME_APPLYING_LINE1=Application du Thème +THEME_APPLYING_LINE2=Redémarrage en Cours . . . diff --git a/mod/etc/options_menu/themes/lang/it-IT.lang b/mod/etc/options_menu/themes/lang/it-IT.lang index b72b1d9..0bfa487 100644 --- a/mod/etc/options_menu/themes/lang/it-IT.lang +++ b/mod/etc/options_menu/themes/lang/it-IT.lang @@ -42,3 +42,5 @@ THEME_LOADING=Caricamento... THEME_LOADING_ASSETS=Caricamento risorse... THEME_SAVING=Salvataggio tema personalizzato... THEME_PREVIEW_LOADING=Caricamento anteprima tema... +THEME_APPLYING_LINE1=Applicazione del Tema in Corso +THEME_APPLYING_LINE2=Riavvio in Corso . . . diff --git a/mod/etc/options_menu/themes/lang/ja-JP.lang b/mod/etc/options_menu/themes/lang/ja-JP.lang index 0c0d86a..86dc940 100644 --- a/mod/etc/options_menu/themes/lang/ja-JP.lang +++ b/mod/etc/options_menu/themes/lang/ja-JP.lang @@ -42,3 +42,5 @@ THEME_LOADING=よみこみちゅう... THEME_LOADING_ASSETS=Assetをよみこみちゅう... THEME_SAVING=じぶんのThemeをほぞんちゅう... THEME_PREVIEW_LOADING=Themeのpreviewをよみこみちゅう... +THEME_APPLYING_LINE1=Themeを てきようしています +THEME_APPLYING_LINE2=さいきどうしています . . . diff --git a/mod/etc/options_menu/themes/lang/nl-NL.lang b/mod/etc/options_menu/themes/lang/nl-NL.lang index 55a2edc..eb54454 100644 --- a/mod/etc/options_menu/themes/lang/nl-NL.lang +++ b/mod/etc/options_menu/themes/lang/nl-NL.lang @@ -42,3 +42,5 @@ THEME_LOADING=Laden... THEME_LOADING_ASSETS=Assets worden geladen... THEME_SAVING=Eigen thema wordt opgeslagen... THEME_PREVIEW_LOADING=Themavoorbeeld wordt geladen... +THEME_APPLYING_LINE1=Thema Wordt Toegepast +THEME_APPLYING_LINE2=Nu Herstarten . . . diff --git a/mod/etc/options_menu/themes/lang/pt-PT.lang b/mod/etc/options_menu/themes/lang/pt-PT.lang index 8128822..79caf35 100644 --- a/mod/etc/options_menu/themes/lang/pt-PT.lang +++ b/mod/etc/options_menu/themes/lang/pt-PT.lang @@ -42,3 +42,5 @@ THEME_LOADING=A carregar... THEME_LOADING_ASSETS=A carregar recursos... THEME_SAVING=A guardar tema personalizado... THEME_PREVIEW_LOADING=A carregar pré-visualização do tema... +THEME_APPLYING_LINE1=A Aplicar o Tema +THEME_APPLYING_LINE2=A Reiniciar Agora . . . diff --git a/mod/etc/options_menu/themes/lang/ru-RU.lang b/mod/etc/options_menu/themes/lang/ru-RU.lang index bdcc31e..c8e6073 100644 --- a/mod/etc/options_menu/themes/lang/ru-RU.lang +++ b/mod/etc/options_menu/themes/lang/ru-RU.lang @@ -42,3 +42,5 @@ THEME_LOADING=Загрузка... THEME_LOADING_ASSETS=Загрузка ресурсов... THEME_SAVING=Сохранение своей темы... THEME_PREVIEW_LOADING=Загрузка предпросмотра темы... +THEME_APPLYING_LINE1=Применение Темы +THEME_APPLYING_LINE2=Перезагрузка . . . diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index bca429f..8912787 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -310,7 +310,8 @@ themeLoader() { sed -i \"s/SELECTED_THEME=\\\"CHANGEME\\\"/SELECTED_THEME=\\\"${thm}\\\"/g;\" \"$b_file\" " >> "$thm_script.sh" - echo "sleep 1 + echo "\"\$omScripts/gen_splash\" /tmp/om_splash.png THEME_APPLYING_LINE1 THEME_APPLYING_LINE2 && decodepng /tmp/om_splash.png > /dev/fb0 +sleep 1 hakchi reboot" >> "$thm_script.sh" preview_image="$thmPreview/$thm.png" @@ -320,7 +321,7 @@ sed -i \"s/SELECTED_THEME=\\\"CHANGEME\\\"/SELECTED_THEME=\\\"${thm}\\\"/g;\" \" cmd_name="$(sed -r 's/NAME=//g' < "$full_path/info")" echo "COMMAND_NAME=$cmd_name -COMMAND_TYPE=INTERNAL +COMMAND_TYPE=EXTERNAL COMMAND_STR=sh $thm_script.sh PREVIEW_IMAGE=$preview_image DELETE_STR=rm -rf \"$full_path\" \"$full_path.png\" \"$cmd_list\" \"$thm_script.sh\" && usleep 50000 && %options_path%lib/theme_manager --title INSTALLED_THEMES &" > "$cmd_list" From dd3c4e94464a3f0a58d98eaff5d693abe7110bbf Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 24 Sep 2026 23:59:00 +0000 Subject: [PATCH 094/210] Update OptionsMenu submodule to latest commit --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 406ad3d..324fcd1 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 406ad3d617de3df49cb5aaa8aa7e81deb9ad4227 +Subproject commit 324fcd1b2d815160700580226a0af86c812b6a17 From a930c9b3faba0463773bf8d2c9c80ba267015e12 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 25 Sep 2026 11:18:03 +0000 Subject: [PATCH 095/210] Update OptionsMenu submodule to latest commit e32b11b --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 324fcd1..e32b11b 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 324fcd1b2d815160700580226a0af86c812b6a17 +Subproject commit e32b11be2b33dde3ac7d0387f1708fb3a65592a7 From 4b24c975e13df3e4b2407d4adbdd951f9c300b1e Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 25 Sep 2026 11:21:00 +0000 Subject: [PATCH 096/210] Migrate theme_manager to OptionsMenu's Color type --- src/theme_manager_app.cpp | 102 ++++++++++++++++++-------------------- src/theme_manager_app.h | 4 +- 2 files changed, 50 insertions(+), 56 deletions(-) diff --git a/src/theme_manager_app.cpp b/src/theme_manager_app.cpp index 20a710c..9bc78ae 100644 --- a/src/theme_manager_app.cpp +++ b/src/theme_manager_app.cpp @@ -25,7 +25,7 @@ namespace { - Uint8 ActiveThemeBorderR = 0x4C, ActiveThemeBorderG = 0xAF, ActiveThemeBorderB = 0x6E; // green + Color ActiveThemeBorder{ 0x4C, 0xAF, 0x6E }; // green void LoadActiveThemeBorderColor(const std::string & optionsLocation) { @@ -40,11 +40,7 @@ namespace continue; int r, g, b; if(std::sscanf(line.c_str() + eq + 1, "%d,%d,%d", &r, &g, &b) == 3) - { - ActiveThemeBorderR = static_cast(r); - ActiveThemeBorderG = static_cast(g); - ActiveThemeBorderB = static_cast(b); - } + ActiveThemeBorder = { static_cast(r), static_cast(g), static_cast(b) }; } } } @@ -65,28 +61,26 @@ ThemeManagerApp::ThemeManagerApp(std::string optionsLocation, AppOptions options controller_.reset(new Controller(1)); fprintf(stderr, "CHECKPOINT 3: controller created\n"); fflush(stderr); - bgR_ = UiTheme::BgR; - bgG_ = UiTheme::BgG; - bgB_ = UiTheme::BgB; - SDL_SetRenderDrawColor(renderer_, bgR_, bgG_, bgB_, 0xFF); + bg_ = UiTheme::Bg; + SetDrawColor(renderer_, bg_); gearIcon_ = Texture(optionsLocation_ + UiTheme::AssetGear, renderer_, UiTheme::GearX, UiTheme::GearY); switchOn_ = Texture(optionsLocation_ + UiTheme::AssetSwitchOn, renderer_); switchOff_ = Texture(optionsLocation_ + UiTheme::AssetSwitchOff, renderer_); - appTitleText_ = Texture("Theme Manager", UiTheme::TitleFontSize, renderer_, UiTheme::TitleX, UiTheme::TitleY, false, UiTheme::TextColor, true); + appTitleText_ = Texture("Theme Manager", UiTheme::TitleFontSize, renderer_, UiTheme::TitleX, UiTheme::TitleY, false, ToAbgr(UiTheme::Text), true); appTitleText_.rect.y -= appTitleText_.rect.h / 2; - appVersionText_ = Texture(MOD_VERSION, UiTheme::VersionFontSize, renderer_, appTitleText_.rect.x + appTitleText_.rect.w + UiTheme::VersionGap, UiTheme::TitleY, false, UiTheme::TextColor, true); + appVersionText_ = Texture(MOD_VERSION, UiTheme::VersionFontSize, renderer_, appTitleText_.rect.x + appTitleText_.rect.w + UiTheme::VersionGap, UiTheme::TitleY, false, ToAbgr(UiTheme::Text), true); appVersionText_.rect.y -= appVersionText_.rect.h / 2; - titleText_ = Texture(Translate(options_.titleKey), UiTheme::SectionTitleFontSize, renderer_, UiTheme::SectionTitleX, UiTheme::SectionTitleY, false, UiTheme::TextColor, true); - creditText_ = Texture("Theme Manager - by DefKorns", 16, renderer_, UiTheme::CreditX, UiTheme::CreditY, false, UiTheme::TextColor, true); + titleText_ = Texture(Translate(options_.titleKey), UiTheme::SectionTitleFontSize, renderer_, UiTheme::SectionTitleX, UiTheme::SectionTitleY, false, ToAbgr(UiTheme::Text), true); + creditText_ = Texture("Theme Manager - by DefKorns", 16, renderer_, UiTheme::CreditX, UiTheme::CreditY, false, ToAbgr(UiTheme::Text), true); fprintf(stderr, "CHECKPOINT 4: basic textures created\n"); fflush(stderr); - badgeA_ = Badge{ Texture("A", 16, renderer_, 0, 0, false, UiTheme::BadgeLetterColor, true), Texture(Translate("HINT_SELECT"), 16, renderer_, 0, 0, false, UiTheme::TextColor, true), UiTheme::BadgeADark, UiTheme::BadgeA }; + badgeA_ = Badge{ Texture("A", 16, renderer_, 0, 0, false, ToAbgr(UiTheme::BadgeLetter), true), Texture(Translate("HINT_SELECT"), 16, renderer_, 0, 0, false, ToAbgr(UiTheme::Text), true), UiTheme::BadgeADark, UiTheme::BadgeA }; badgeRowRightEdge_ = UiTheme::BadgeClusterRightX - UiTheme::BadgeOuterSize - UiTheme::BadgeLabelGap - badgeA_.label.rect.w - UiTheme::BadgeGroupGap; badgeOuter_ = Texture(optionsLocation_ + UiTheme::AssetBadgeOuter, renderer_); badgeInner_ = Texture(optionsLocation_ + UiTheme::AssetBadgeInner, renderer_); - badgeHold_ = Badge{ Texture("B", 16, renderer_, 0, 0, false, UiTheme::BadgeLetterColor, true), Texture(Translate("HINT_DELETE"), 16, renderer_, 0, 0, false, UiTheme::TextColor, true), UiTheme::BadgeXDark, UiTheme::BadgeX }; + badgeHold_ = Badge{ Texture("B", 16, renderer_, 0, 0, false, ToAbgr(UiTheme::BadgeLetter), true), Texture(Translate("HINT_DELETE"), 16, renderer_, 0, 0, false, ToAbgr(UiTheme::Text), true), UiTheme::BadgeXDark, UiTheme::BadgeX }; ComputePinnedAndThemeRanges(); } @@ -108,11 +102,11 @@ int ThemeManagerApp::DrawBadge(Badge & badge, int rightEdgeX) int x = rightEdgeX - groupW; int y = UiTheme::BadgeBandY; badgeOuter_.rect = { x, y, UiTheme::BadgeOuterSize, UiTheme::BadgeOuterSize }; - SDL_SetTextureColorMod(badgeOuter_.texture.get(), badge.rim.r, badge.rim.g, badge.rim.b); + SetColorMod(badgeOuter_.texture.get(), badge.rim); badgeOuter_.Draw(renderer_); int innerOffset = (UiTheme::BadgeOuterSize - UiTheme::BadgeInnerSize) / 2; badgeInner_.rect = { x+innerOffset, y+innerOffset, UiTheme::BadgeInnerSize, UiTheme::BadgeInnerSize }; - SDL_SetTextureColorMod(badgeInner_.texture.get(), badge.fill.r, badge.fill.g, badge.fill.b); + SetColorMod(badgeInner_.texture.get(), badge.fill); badgeInner_.Draw(renderer_); // glyph bearing makes the pure-math center look 1px down/left - nudged badge.letter.rect.x = x + (UiTheme::BadgeOuterSize - badge.letter.rect.w)/2 + 1; @@ -129,8 +123,8 @@ bool ThemeManagerApp::ConfirmDelete() { const int DialogCenterX = 640, DialogTitleY = 320, DialogHintY = 360; // screen center (1280x720) const std::string & confirmKey = commands_[currentCommandId_].deleteConfirmKey; - Texture confirmTitle(Translate(confirmKey.empty() ? "DELETE_CONFIRM_GENERIC" : confirmKey), 24, renderer_, DialogCenterX, DialogTitleY, true, UiTheme::TextColor, true); - Texture confirmHint(Translate("DELETE_CONFIRM_HINT"), 16, renderer_, DialogCenterX, DialogHintY, true, UiTheme::TextColor, true); + Texture confirmTitle(Translate(confirmKey.empty() ? "DELETE_CONFIRM_GENERIC" : confirmKey), 24, renderer_, DialogCenterX, DialogTitleY, true, ToAbgr(UiTheme::Text), true); + Texture confirmHint(Translate("DELETE_CONFIRM_HINT"), 16, renderer_, DialogCenterX, DialogHintY, true, ToAbgr(UiTheme::Text), true); controller_->GetButtonStatus(B); // consume the still-held B from the triggering long-press bool confirmed = false; for(;;) @@ -144,11 +138,11 @@ bool ThemeManagerApp::ConfirmDelete() break; } sdlContext_->StartFrame(); - DrawFillRect(renderer_, UiTheme::FrameRect, UiTheme::BgR, UiTheme::BgG, UiTheme::BgB); - DrawStrokeRect(renderer_, UiTheme::FrameRect, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BorderRadius); + DrawFillRect(renderer_, UiTheme::FrameRect, UiTheme::Bg); + DrawStrokeRect(renderer_, UiTheme::FrameRect, UiTheme::Border, UiTheme::BorderWidth, UiTheme::BorderRadius); confirmTitle.Draw(renderer_); confirmHint.Draw(renderer_); - SDL_SetRenderDrawColor(renderer_, bgR_, bgG_, bgB_, 0xFF); // flat helpers leave the draw color dirty + SetDrawColor(renderer_, bg_); // flat helpers leave the draw color dirty sdlContext_->EndFrame(); } if(!confirmed) @@ -159,12 +153,12 @@ bool ThemeManagerApp::ConfirmDelete() void ThemeManagerApp::DrawChromeCommon() { - DrawStrokeRect(renderer_, UiTheme::OuterRect, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BorderRadius); + DrawStrokeRect(renderer_, UiTheme::OuterRect, UiTheme::Border, UiTheme::BorderWidth, UiTheme::BorderRadius); gearIcon_.Draw(renderer_); appTitleText_.Draw(renderer_); appVersionText_.Draw(renderer_); - DrawHLine(renderer_, UiTheme::HeaderDividerX, UiTheme::HeaderDividerX + UiTheme::HeaderDividerW, UiTheme::HeaderDividerY, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth); - DrawHLine(renderer_, UiTheme::OuterRect.x, UiTheme::OuterRect.x + UiTheme::OuterRect.w, UiTheme::FooterDividerY, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth); + DrawHLine(renderer_, UiTheme::HeaderDividerX, UiTheme::HeaderDividerX + UiTheme::HeaderDividerW, UiTheme::HeaderDividerY, UiTheme::Border, UiTheme::BorderWidth); + DrawHLine(renderer_, UiTheme::OuterRect.x, UiTheme::OuterRect.x + UiTheme::OuterRect.w, UiTheme::FooterDividerY, UiTheme::Border, UiTheme::BorderWidth); DrawBadge(badgeA_, UiTheme::BadgeClusterRightX); creditText_.Draw(renderer_); } @@ -172,13 +166,13 @@ void ThemeManagerApp::DrawChromeCommon() void ThemeManagerApp::DrawSectionTitle() { int accentBarY = titleText_.rect.y + (titleText_.rect.h - UiTheme::SectionAccentBarH) / 2; - DrawFillRect(renderer_, { UiTheme::SectionTitleX - UiTheme::SectionAccentBarW - 14, accentBarY, UiTheme::SectionAccentBarW, UiTheme::SectionAccentBarH }, UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB); + DrawFillRect(renderer_, { UiTheme::SectionTitleX - UiTheme::SectionAccentBarW - 14, accentBarY, UiTheme::SectionAccentBarW, UiTheme::SectionAccentBarH }, UiTheme::Accent); titleText_.Draw(renderer_); } Texture ThemeManagerApp::MakeScrollArrow(int x, int y) const { - Texture arrow("^", 16, renderer_, x, y, false, UiTheme::TextColor, true); + Texture arrow("^", 16, renderer_, x, y, false, ToAbgr(UiTheme::Text), true); arrow.rect.x -= arrow.rect.w / 2; return arrow; } @@ -233,7 +227,7 @@ bool ThemeManagerApp::ActivateCommand(Command & cmd) { if(cmd.runInternal) { - cmd.RunCommand(*sdlContext_, controller_.get(), { gearIcon_, appTitleText_, appVersionText_, creditText_ }, bgR_, bgG_, bgB_); + cmd.RunCommand(*sdlContext_, controller_.get(), { gearIcon_, appTitleText_, appVersionText_, creditText_ }, bg_); if(cmd.isToggle) cmd.UpdateState(); return false; @@ -302,7 +296,7 @@ int ThemeManagerApp::RunGridLayout() // capped so a strip with few slots doesn't stretch its chips wide const int MaxChipW = 200; const int chipW = hasStrip ? std::min(MaxChipW, (GridRight - GridLeft - (stripSlotCount-1)*chipGap) / stripSlotCount) : 0; - titleText_ = Texture(Translate(options_.titleKey), UiTheme::SectionTitleFontSize - 6, renderer_, UiTheme::SectionTitleX, 0, false, UiTheme::TextColor, true); + titleText_ = Texture(Translate(options_.titleKey), UiTheme::SectionTitleFontSize - 6, renderer_, UiTheme::SectionTitleX, 0, false, ToAbgr(UiTheme::Text), true); titleText_.rect.y = hasStrip ? (StripTop + StripH + 22) : (UiTheme::HeaderDividerY + 22); const int GridTop = titleText_.rect.y + titleText_.rect.h + 20; const int GridBottom = UiTheme::FooterDividerY - 14; @@ -327,8 +321,8 @@ int ThemeManagerApp::RunGridLayout() gridScrollDown.rect.y = GridBottom - 24 - gridScrollDown.rect.h; // B runs Back/Exit directly - it's a footer badge, not a chip - Badge badgeB{ Texture("B", 16, renderer_, 0, 0, false, UiTheme::BadgeLetterColor, true), - Texture(pinnedStartIndex_ < (int)commands_.size() ? Translate(commands_[pinnedStartIndex_].name) : "", 16, renderer_, 0, 0, false, UiTheme::TextColor, true), + Badge badgeB{ Texture("B", 16, renderer_, 0, 0, false, ToAbgr(UiTheme::BadgeLetter), true), + Texture(pinnedStartIndex_ < (int)commands_.size() ? Translate(commands_[pinnedStartIndex_].name) : "", 16, renderer_, 0, 0, false, ToAbgr(UiTheme::Text), true), UiTheme::BadgeBDark, UiTheme::BadgeB }; std::vector chipIcons(commands_.size()); @@ -353,17 +347,17 @@ int ThemeManagerApp::RunGridLayout() Texture icon(c.previewImage, renderer_, 0, 0); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); FitCentered(icon, 0, 0, chipIconSize, chipIconSize, 0); - SDL_SetTextureColorMod(icon.texture.get(), UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB); + SetColorMod(icon.texture.get(), UiTheme::Accent); chipIcons[i] = icon; labelAvail = chipW - chipIconSize - 32; } label = TruncateToWidth(label, 14, labelAvail); - chipLabels[i] = Texture(label, 14, renderer_, 0, 0, false, UiTheme::TextColor, true); + chipLabels[i] = Texture(label, 14, renderer_, 0, 0, false, ToAbgr(UiTheme::Text), true); } else { label = TruncateToWidth(label, 15, TileW - 28); - tileLabels[i] = Texture(label, 15, renderer_, 0, 0, false, UiTheme::TextColor, true); + tileLabels[i] = Texture(label, 15, renderer_, 0, 0, false, ToAbgr(UiTheme::Text), true); } } std::vector tileImageLoaded(commands_.size(), false); @@ -547,11 +541,11 @@ int ThemeManagerApp::RunGridLayout() SDL_Rect chipRect{ x, StripTop, chipW, StripH }; if(selected) { - DrawRoundedFillRect(renderer_, chipRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); - DrawStrokeRect(renderer_, chipRect, UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB, 2, UiTheme::BoxRadius); + DrawRoundedFillRect(renderer_, chipRect, UiTheme::SelectedRowBg, UiTheme::BoxRadius); + DrawStrokeRect(renderer_, chipRect, UiTheme::Accent, 2, UiTheme::BoxRadius); } else - DrawStrokeRect(renderer_, chipRect, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BoxRadius); + DrawStrokeRect(renderer_, chipRect, UiTheme::Border, UiTheme::BorderWidth, UiTheme::BoxRadius); // icon+label centered as one group, not each alone Texture & icon = chipIcons[idx]; @@ -591,7 +585,7 @@ int ThemeManagerApp::RunGridLayout() int x = GridLeft + col*(TileW+GridGap); bool selected = idx == currentCommandId_; SDL_Rect tileRect{ x, y, TileW, TileH }; - DrawRoundedFillRect(renderer_, tileRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); + DrawRoundedFillRect(renderer_, tileRect, UiTheme::SelectedRowBg, UiTheme::BoxRadius); EnsureTileImage(idx); std::vector & runFrames = tileRunFrames[idx]; Texture & tileArt = runFrames.size() > 1 @@ -608,21 +602,21 @@ int ThemeManagerApp::RunGridLayout() FitCover(tileArt, x, y, TileW, TileH, 0); tileArt.Draw(renderer_); SDL_RenderSetClipRect(renderer_, nullptr); - DrawRoundedCornerMask(renderer_, tileRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); + DrawRoundedCornerMask(renderer_, tileRect, UiTheme::SelectedRowBg, UiTheme::BoxRadius); } - Uint8 strokeR = UiTheme::BorderR, strokeG = UiTheme::BorderG, strokeB = UiTheme::BorderB; + Color stroke = UiTheme::Border; int strokeW = UiTheme::BorderWidth; if(selected) { - strokeR = UiTheme::AccentR; strokeG = UiTheme::AccentG; strokeB = UiTheme::AccentB; + stroke = UiTheme::Accent; strokeW = 3; } else if(isActiveTheme[idx]) { - strokeR = ActiveThemeBorderR; strokeG = ActiveThemeBorderG; strokeB = ActiveThemeBorderB; + stroke = ActiveThemeBorder; strokeW = 3; } - DrawStrokeRect(renderer_, tileRect, strokeR, strokeG, strokeB, strokeW, UiTheme::BoxRadius); + DrawStrokeRect(renderer_, tileRect, stroke, strokeW, UiTheme::BoxRadius); if(!tileHideLabel[idx]) { tileLabels[idx].rect.x = x + (TileW - tileLabels[idx].rect.w) / 2; @@ -638,7 +632,7 @@ int ThemeManagerApp::RunGridLayout() gridScrollDown.Draw(renderer_, SDL_FLIP_VERTICAL); } - SDL_SetRenderDrawColor(renderer_, bgR_, bgG_, bgB_, 0xFF); + SetDrawColor(renderer_, bg_); sdlContext_->EndFrame(); } @@ -653,8 +647,8 @@ int ThemeManagerApp::RunListLayout() SDL_Rect selectedRowRect{ UiTheme::ListX, UiTheme::RowFirstY - 2, UiTheme::ListContentRightX - UiTheme::ListX, 0 }; // B runs Back/Exit directly - same footer badge and behavior as the grid layout - Badge badgeB{ Texture("B", 16, renderer_, 0, 0, false, UiTheme::BadgeLetterColor, true), - Texture(pinnedStartIndex_ < (int)commands_.size() ? Translate(commands_[pinnedStartIndex_].name) : "", 16, renderer_, 0, 0, false, UiTheme::TextColor, true), + Badge badgeB{ Texture("B", 16, renderer_, 0, 0, false, ToAbgr(UiTheme::BadgeLetter), true), + Texture(pinnedStartIndex_ < (int)commands_.size() ? Translate(commands_[pinnedStartIndex_].name) : "", 16, renderer_, 0, 0, false, ToAbgr(UiTheme::Text), true), UiTheme::BadgeBDark, UiTheme::BadgeB }; const int RowGlyphSize = 16; @@ -664,12 +658,12 @@ int ThemeManagerApp::RunListLayout() { int textX = UiTheme::RowTextX + (c.child ? ChildIndent : 0); std::string label = TruncateToWidth(Translate(c.name), RowGlyphSize, UiTheme::RowControlRightX - RowTextGapPx - textX); - c.texture = Texture(label, RowGlyphSize, renderer_, textX, 0, false, UiTheme::TextColor, true); + c.texture = Texture(label, RowGlyphSize, renderer_, textX, 0, false, ToAbgr(UiTheme::Text), true); } const int modernRowPitch = std::max(UiTheme::RowPitch, GetTTFLineHeight(RowGlyphSize)); // Back/Exit no longer takes a row of its own here - B runs it directly via the footer badge above - const int DisplayItemCount = std::max(1, (UiTheme::FooterDividerY - UiTheme::PinnedBottomMargin - UiTheme::RowFirstY) / modernRowPitch); + const int DisplayItemCount = std::max(1, (UiTheme::FooterDividerY - UiTheme::ListBottomMargin - UiTheme::RowFirstY) / modernRowPitch); int topListItemNumber = pinnedStartIndex_ + 1; // forces the first SetCurrentCommand to lay out row positions std::shared_ptr PreviewImage; @@ -743,7 +737,7 @@ int ThemeManagerApp::RunListLayout() } // skip for separators and the last row - nothing to separate there if(!selected && !rowCommand.command.empty() && !isLastOverall) - DrawHLine(renderer_, UiTheme::ListX, UiTheme::ListContentRightX, rowCommand.texture.rect.y + rowCommand.texture.rect.h + 3, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB); + DrawHLine(renderer_, UiTheme::ListX, UiTheme::ListContentRightX, rowCommand.texture.rect.y + rowCommand.texture.rect.h + 3, UiTheme::Border); }; auto DrawChrome = [&]() @@ -758,13 +752,13 @@ int ThemeManagerApp::RunListLayout() DrawBadge(badgeHold_, rightEdge); } - DrawRoundedFillRect(renderer_, selectedRowRect, UiTheme::SelectedRowBgR, UiTheme::SelectedRowBgG, UiTheme::SelectedRowBgB, UiTheme::BoxRadius); - DrawStrokeRect(renderer_, selectedRowRect, UiTheme::AccentR, UiTheme::AccentG, UiTheme::AccentB, 2, UiTheme::BoxRadius); + DrawRoundedFillRect(renderer_, selectedRowRect, UiTheme::SelectedRowBg, UiTheme::BoxRadius); + DrawStrokeRect(renderer_, selectedRowRect, UiTheme::Accent, 2, UiTheme::BoxRadius); if(PreviewImage.get()) { SDL_Rect previewBox{ UiTheme::DetailX, UiTheme::PreviewBoxY, UiTheme::DetailW, UiTheme::PreviewBoxH }; - DrawStrokeRect(renderer_, previewBox, UiTheme::BorderR, UiTheme::BorderG, UiTheme::BorderB, UiTheme::BorderWidth, UiTheme::BoxRadius); + DrawStrokeRect(renderer_, previewBox, UiTheme::Border, UiTheme::BorderWidth, UiTheme::BoxRadius); PreviewImage->Draw(renderer_); } }; @@ -829,7 +823,7 @@ int ThemeManagerApp::RunListLayout() if((topListItemNumber + DisplayItemCount) < pinnedStartIndex_) scrollDown.Draw(renderer_, SDL_FLIP_VERTICAL); - SDL_SetRenderDrawColor(renderer_, bgR_, bgG_, bgB_, 0xFF); + SetDrawColor(renderer_, bg_); sdlContext_->EndFrame(); } diff --git a/src/theme_manager_app.h b/src/theme_manager_app.h index d361617..75d67dc 100644 --- a/src/theme_manager_app.h +++ b/src/theme_manager_app.h @@ -28,7 +28,7 @@ class ThemeManagerApp int Run() { return options_.gridLayout ? RunGridLayout() : RunListLayout(); } private: - struct Badge { Texture letter; Texture label; UiTheme::BadgeColor rim; UiTheme::BadgeColor fill; }; + struct Badge { Texture letter; Texture label; Color rim; Color fill; }; enum class FrameEvent { Continue, Quit, PowerButtonPressed }; std::string optionsLocation_; @@ -43,7 +43,7 @@ class ThemeManagerApp SDL_Renderer * renderer_ = nullptr; std::unique_ptr controller_; int currentCommandId_ = 0; - Uint8 bgR_ = 0, bgG_ = 0, bgB_ = 0; + Color bg_{}; Texture gearIcon_, switchOn_, switchOff_; Texture appTitleText_, appVersionText_, titleText_, creditText_; From 85adfed0aa247142bd80aca98606d24d91d5c1e6 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 25 Sep 2026 11:21:18 +0000 Subject: [PATCH 097/210] Point Refresh at the renamed om_theme_selector script --- mod/etc/options_menu/themes/commands/c0000_1_refresh | 2 +- mod/uninstall | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mod/etc/options_menu/themes/commands/c0000_1_refresh b/mod/etc/options_menu/themes/commands/c0000_1_refresh index 0cc5233..2f7b6f8 100644 --- a/mod/etc/options_menu/themes/commands/c0000_1_refresh +++ b/mod/etc/options_menu/themes/commands/c0000_1_refresh @@ -1,5 +1,5 @@ COMMAND_NAME=REFRESH COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE -COMMAND_STR=sh /etc/options_menu/scripts/om_themeselector reload +COMMAND_STR=sh /etc/options_menu/scripts/om_theme_selector reload PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png diff --git a/mod/uninstall b/mod/uninstall index 55b3017..8692a1a 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -61,6 +61,7 @@ remove "$rootfs/etc/preinit.d/b8024_themeselector" remove "$rootfs/etc/preinit.d/p8024_themeselector" remove "$rootfs/etc/options_menu/commands/c0075_ThemeOptions" remove "$rootfs/etc/options_menu/scripts/om_themeselector" +remove "$rootfs/etc/options_menu/scripts/om_theme_selector" remove "$rootfs/etc/options_menu/themes" remove "$rootfs/etc/options_menu/images/advanced.png" remove "$rootfs/etc/options_menu/images/audio.png" From 68a3f249153fa15d5b18fa4d310414c5bc71e9b3 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 25 Sep 2026 11:21:54 +0000 Subject: [PATCH 098/210] Fix first-run reload and settings reset when saves live on SD/USB --- mod/etc/options_menu/scripts/om_theme_selector | 2 ++ mod/etc/options_menu/themes/scripts/om_vars | 2 +- mod/install | 13 ++----------- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/mod/etc/options_menu/scripts/om_theme_selector b/mod/etc/options_menu/scripts/om_theme_selector index 56a91e2..f91b2a7 100644 --- a/mod/etc/options_menu/scripts/om_theme_selector +++ b/mod/etc/options_menu/scripts/om_theme_selector @@ -24,6 +24,8 @@ hasThemesToInstall enableMenuOptions remove "$omDummy" +[ -f "$om_firstrun" ] && remove "$om_themerandomizer" "$om_themeperfolder" "$om_themeperfolderchild" "$om_audiorandomizer" "$om_advmusichack" + if [ "$1" = "reload" ] || [ -f "$om_firstrun" ] ; then loadThemeList -r remove "$om_firstrun" diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 8912787..a9520fb 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -81,7 +81,7 @@ om_themeperfolder="$state_path/om_themeperfolder" om_themeperfolderchild="$state_path/om_themeperfolderchild" om_audiorandomizer="$state_path/om_audiorandomizer" om_advmusichack="$state_path/om_advmusichack" -om_firstrun="$state_path/om_firstrun" +om_firstrun="$omMod/om_firstrun" om_theme_count="$state_path/om_theme_count" #### SEPARATORS diff --git a/mod/install b/mod/install index c37a342..3b3d4a9 100644 --- a/mod/install +++ b/mod/install @@ -53,18 +53,9 @@ find "$hmod_dir" -type f -name "*advanced_music_hack" -print0 -exec rm -f {} \; unset cfg_theme_randomizR_enabled transfer_default -om_state_path="$mountpoint/$profilepath/$modname" -om_firstrun="$om_state_path/om_firstrun" -om_themerandomizer="$om_state_path/om_themerandomizer" -om_themeperfolder="$om_state_path/om_themeperfolder" -om_themeperfolderchild="$om_state_path/om_themeperfolderchild" -om_audiorandomizer="$om_state_path/om_audiorandomizer" -om_advmusichack="$om_state_path/om_advmusichack" -remove_settings="$om_themerandomizer $om_themeperfolder $om_themeperfolderchild $om_audiorandomizer $om_advmusichack" +# saves aren't mounted yet during install - om_theme_selector resets settings on first run +om_firstrun="$rootfs/etc/options_menu/themes/om_firstrun" [ ! -f "$om_firstrun" ] && touch "$om_firstrun" -for option in $remove_settings; do - del "$option" -done copy "$om_themes_scripts/om_fontfix" "$pre_initd/b8025_fontfix" copy "$om_themes_scripts/om_chmenu" "$rootfs/bin/chmenu" chmod +x "$rootfs/bin/chmenu" From 6bef1f68331464b78191db78b8d2aae144a911ee Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 25 Sep 2026 15:26:48 +0000 Subject: [PATCH 099/210] Point author links at GitHub --- README.md | 2 +- mod/readme.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0d295ea..2a612a8 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ The Theme Manager's own UI (not your game themes) reads its color palette from ` ## Credits -- [DefKorns](https://gitlab.com/DefKorns) +- [DefKorns](https://github.com/DefKorns) - [DanTheMan827](https://github.com/DanTheMan827) (packed.png extractor) ## Thanks diff --git a/mod/readme.md b/mod/readme.md index 1e7ee95..4d6e90c 100644 --- a/mod/readme.md +++ b/mod/readme.md @@ -65,7 +65,7 @@ The Theme Manager's own UI (not your game themes) reads its color palette from ` ## Credits -- [DefKorns](https://gitlab.com/DefKorns) +- [DefKorns](https://github.com/DefKorns) - [DanTheMan827](https://github.com/DanTheMan827) (packed.png extractor) ## Thanks From f4ef301ac4a3ab31b3263045517c4c58e1b89b41 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 25 Sep 2026 15:27:14 +0000 Subject: [PATCH 100/210] Update OptionsMenu submodule to latest commit fbd9dcc --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index e32b11b..fbd9dcc 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit e32b11be2b33dde3ac7d0387f1708fb3a65592a7 +Subproject commit fbd9dcc47a44229b086f28f293ce2ff68c72e5d5 From d1251d2544afbd1c8b79f9fe9786a01c44005def Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 25 Sep 2026 15:43:36 +0000 Subject: [PATCH 101/210] Add GitHub Actions build and tag-driven releases, set version 3.0.0-alpha --- .github/workflows/build.yml | 65 +++++++++++++++++++++++++++++++++++++ Makefile.docker | 2 +- VERSION | 2 +- 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..4a2a358 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,65 @@ +name: Build + +on: + push: + branches: ['**'] + tags: ['v*'] + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + submodules: true + + # The toolchain image needs the console's own SDL2 headers + libs in + # toolchain/vendor. They're proprietary, so they live in a private repo + # cloned with a read-only fine-grained PAT (secrets VENDOR_REPO and + # VENDOR_REPO_PAT, same as OptionsMenu). + - name: Restore vendor/ from private repo + run: | + git clone --depth 1 "https://x-access-token:${{ secrets.VENDOR_REPO_PAT }}@github.com/${{ secrets.VENDOR_REPO }}.git" toolchain/vendor + + - name: Build cross-compile toolchain image + run: docker build -f toolchain/Dockerfile.jessie-armhf -t classicmini-cross-toolchain:jessie-armhf toolchain + + # Tag builds take their version from the tag (v3.0.0-alpha -> 3.0.0-alpha) + # instead of the VERSION file. safe.directory: the container runs as root + # over a checkout owned by the runner user, and git refuses it otherwise - + # which leaves the Makefile's git-derived hmod name empty. + - name: Compile and package + run: | + MOD_VER_ARG="" + if [ "${{ github.ref_type }}" = "tag" ]; then + MOD_VER_ARG="MOD_VER=${GITHUB_REF_NAME#v}" + fi + docker run --rm -v "${{ github.workspace }}:/src" classicmini-cross-toolchain:jessie-armhf \ + sh -c "git config --global --add safe.directory '*' && make -f Makefile.docker CROSS_PREFIX=arm-linux-gnueabihf- $MOD_VER_ARG" + + - name: Upload .hmod build artifact + uses: actions/upload-artifact@v7 + with: + name: theme-selector-hmod + path: out/*.hmod + + release: + needs: build + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + steps: + - name: Download build artifact + uses: actions/download-artifact@v8 + with: + name: theme-selector-hmod + path: out + + - name: Create GitHub Release + uses: softprops/action-gh-release@v3 + with: + name: Theme Selector ${{ github.ref_name }} + files: out/*.hmod + generate_release_notes: true + # -alpha/-beta/-rc tags must not take over "Latest" + prerelease: ${{ contains(github.ref_name, '-') }} diff --git a/Makefile.docker b/Makefile.docker index daf35ce..b26e26a 100644 --- a/Makefile.docker +++ b/Makefile.docker @@ -13,7 +13,7 @@ SDL_CFLAGS = $(shell sdl2-config --cflags) $(shell pkg-config --cflags SDL2_ttf) SDL_LIBS = $(shell sdl2-config --libs) $(shell pkg-config --libs SDL2_ttf) -lpng LDFLAGS = endif -CXXFLAGS = -std=c++11 -Os -Ivendor/OptionsMenu/src $(SDL_CFLAGS) +CXXFLAGS = -std=c++11 -Os -Ivendor/OptionsMenu/src $(SDL_CFLAGS) -DMOD_VERSION=\"v$(MOD_VER)\" LDLIBS = $(SDL_LIBS) VENDOR_SRC_DIR = vendor/OptionsMenu/src SOURCES = src/main.cpp src/single_instance_lock.cpp src/command_loader.cpp src/texture_utils.cpp src/theme_manager_app.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp diff --git a/VERSION b/VERSION index 38f77a6..ff3a372 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.0.1 +3.0.0-alpha From cbc1b8657c7336169fd09d1f4c0fa4dc6004f1ce Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 25 Sep 2026 15:51:57 +0000 Subject: [PATCH 102/210] Fix clean-checkout builds: create output dirs, build sprite_crop from pngpack --- .github/workflows/build.yml | 2 +- Makefile.docker | 11 ++++++++++- README.md | 4 ++-- mod/readme.md | 4 ++-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4a2a358..2a72193 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ jobs: steps: - uses: actions/checkout@v7 with: - submodules: true + submodules: recursive # pngpack pulls in lodepng # The toolchain image needs the console's own SDL2 headers + libs in # toolchain/vendor. They're proprietary, so they live in a private repo diff --git a/Makefile.docker b/Makefile.docker index b26e26a..e46b522 100644 --- a/Makefile.docker +++ b/Makefile.docker @@ -57,9 +57,10 @@ OUT := $(DEV_DIR)/out # Avoid "build": GNU Make's .sh rule conflicts with build.sh. all: compile hmod -compile: mod/etc/options_menu/lib/theme_manager mod/bin/theme_downloader +compile: mod/etc/options_menu/lib/theme_manager mod/bin/theme_downloader mod/bin/sprite_crop mod/etc/options_menu/lib/theme_manager: $(OBJECTS) + mkdir -p $(@D) $(CROSS_PREFIX)$(CXX) $(OBJECTS) $(LDLIBS) $(LDFLAGS) -Wl,-rpath,/etc/options_menu/lib -o mod/etc/options_menu/lib/theme_manager $(CROSS_PREFIX)$(STRIP) mod/etc/options_menu/lib/theme_manager @@ -78,10 +79,18 @@ src/ca_bundle.o: src/cacert.pem cd src && $(CROSS_PREFIX)ld -r -b binary -o ca_bundle.o cacert.pem mod/bin/theme_downloader: src/theme_downloader.o src/ca_bundle.o + mkdir -p $(@D) $(CROSS_PREFIX)$(CC) src/theme_downloader.o src/ca_bundle.o $(CURL_LDLIBS) -o mod/bin/theme_downloader $(CROSS_PREFIX)$(STRIP) mod/bin/theme_downloader upx --best mod/bin/theme_downloader +# DIY sprite previews (om_vars). Built by pngpack's own Makefile; its default +# LDFLAGS (-dead_strip) is macOS-only. +mod/bin/sprite_crop: pngpack/pngpacker/sprite_crop.cpp pngpack/pngpacker/pixeltools.cpp + mkdir -p $(@D) + $(MAKE) -C pngpack build/sprite_crop CROSS=$(CROSS_PREFIX) LDFLAGS=-Wl,--gc-sections + cp pngpack/build/sprite_crop $@ + # Package mod/ without recompiling; run `all` first for fresh binaries. hmod: mkdir -p out/ temp/ diff --git a/README.md b/README.md index 2a612a8..7a22e85 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Options Menu - Theme Selector -**Requires [my Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases/latest) as a base — not compatible with any other UI.** +**Requires [my Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases) as a base — not compatible with any other UI.** `Click the image below to view it on YouTube.` [![Theme Selector - Demo](https://i.imgur.com/7JgP6JI.png)](https://youtu.be/yRUbGssu8ko "Theme Selector - Demo") @@ -45,7 +45,7 @@ Yes — it supports all Nintendo Classic consoles, all regions. ## Requirements - [Hakchi CE](https://github.com/TeamShinkansen/hakchi2/releases/latest) -- [My Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases/latest) — the base UI this mod plugs into +- [My Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases) — the base UI this mod plugs into - [Hakchi Wi-Fi mod (WPA Supplicant)](https://hakchi.net/hakchi/hmods/wpa-supplicant.hmod) — only needed to download themes from the internet ## How do I use it diff --git a/mod/readme.md b/mod/readme.md index 4d6e90c..06d443b 100644 --- a/mod/readme.md +++ b/mod/readme.md @@ -1,6 +1,6 @@ # Options Menu - Theme Selector -**Requires [my Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases/latest) as a base — not compatible with any other UI.** +**Requires [my Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases) as a base — not compatible with any other UI.** [![Theme Selector](https://i.imgur.com/7JgP6JI.png)](https://i.imgur.com/7JgP6JI.png) @@ -44,7 +44,7 @@ Yes — it supports all Nintendo Classic consoles, all regions. ## Requirements - [Hakchi CE](https://github.com/TeamShinkansen/hakchi2/releases/latest) -- [My Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases/latest) — the base UI this mod plugs into +- [My Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases) — the base UI this mod plugs into - [Hakchi Wi-Fi mod (WPA Supplicant)](https://hakchi.net/hakchi/hmods/wpa-supplicant.hmod) — only needed to download themes from the internet ## How do I use it From e41bd379a277fcfed8ce40341d8e96ab78ad1521 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 25 Sep 2026 16:31:38 +0000 Subject: [PATCH 103/210] Point install/uninstall at the real /bin location of the helper binaries --- mod/install | 4 ++-- mod/uninstall | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mod/install b/mod/install index 3b3d4a9..43485ca 100644 --- a/mod/install +++ b/mod/install @@ -59,8 +59,8 @@ om_firstrun="$rootfs/etc/options_menu/themes/om_firstrun" copy "$om_themes_scripts/om_fontfix" "$pre_initd/b8025_fontfix" copy "$om_themes_scripts/om_chmenu" "$rootfs/bin/chmenu" chmod +x "$rootfs/bin/chmenu" -chmod +x "$usr/bin/sprite_crop" -chmod +x "$usr/bin/theme_downloader" +chmod +x "$rootfs/bin/sprite_crop" +chmod +x "$rootfs/bin/theme_downloader" chmod +x "$rootfs/etc/options_menu/lib/theme_manager" theme_cfg="$rootfs/etc/options_menu/theme.cfg" diff --git a/mod/uninstall b/mod/uninstall index 8692a1a..4baf309 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -101,8 +101,8 @@ remove "$rootfs/etc/options_menu/images/update.png" remove "$rootfs/etc/options_menu/images/wall.png" remove "$rootfs/etc/options_menu/images/wall_far.png" remove "$rootfs/etc/usr/bin/pngpack-upx" -remove "$rootfs/etc/usr/bin/sprite_crop" -remove "$rootfs/etc/usr/bin/theme_downloader" +remove "$rootfs/bin/sprite_crop" +remove "$rootfs/bin/theme_downloader" remove "$rootfs/etc/options_menu/lib/theme_manager" [ -f "${FONT_PATH}/title.fnt.backup" ] && cp "${FONT_PATH}/title.fnt.backup" "${FONT_PATH}/title.fnt" From 76482003d06c6f439a8183ab5815f5f449c15e00 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 25 Sep 2026 16:35:08 +0000 Subject: [PATCH 104/210] Update OptionsMenu submodule to latest commit cfb7e3f --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index fbd9dcc..cfb7e3f 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit fbd9dcc47a44229b086f28f293ce2ff68c72e5d5 +Subproject commit cfb7e3f624f33e3b73cac9cbf9bf76e9572c82ca From 60d35691b6167a949c5707747de5d6f76b2a73cf Mon Sep 17 00:00:00 2001 From: DefKorns Date: Fri, 25 Sep 2026 16:52:14 +0000 Subject: [PATCH 105/210] Update OptionsMenu submodule to commit 7609718 --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index cfb7e3f..7609718 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit cfb7e3f624f33e3b73cac9cbf9bf76e9572c82ca +Subproject commit 7609718a6a52812ecf1d2774892f20cf55296f2c From 3d2934c19d387a02d62941bd6de631f379a249a4 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 6 Oct 2026 11:09:20 +0000 Subject: [PATCH 106/210] Enhance uninstall process for legacy installs, including Theme Randomizer and Advanced Music Hack --- mod/install | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/mod/install b/mod/install index 43485ca..5b3f462 100644 --- a/mod/install +++ b/mod/install @@ -32,6 +32,33 @@ hmod_dir="$mountpoint/var/lib/hakchi/hmod" b_file="$pre_initd/b8024_themeselector" p_file="$pre_initd/p8024_themeselector" +# Uninstall any previous or legacy install (Theme Randomizer, Advanced Music Hack) +ts_state="$mountpoint/$profilepath/$modname" +ts_backup="$installpath/om_ts_backup" +rm -rf "$ts_backup" +mkdir -p "$ts_backup/state" +for ts_dir in themes music_menu; do + [ -d "$usr_share/$ts_dir" ] && mv "$usr_share/$ts_dir" "$ts_backup/" +done +for ts_file in lastTheme prevTheme active_theme; do + [ -f "$ts_state/$ts_file" ] && cp -p "$ts_state/$ts_file" "$ts_backup/state/" +done +for ts_unfile in "$hmod_dir/uninstall-"*andomiz* \ + "$hmod_dir/uninstall-advanced_music_hack" \ + "$hmod_dir/uninstall-om_theme-selector"* \ + "$hmod_dir/uninstall-om_theme_selector"*; do + [ -f "$ts_unfile" ] && pack_uninstall "${ts_unfile##*/uninstall-}" +done +cd "$transferpath" || return 1 +for ts_dir in themes music_menu; do + [ -d "$ts_backup/$ts_dir" ] && rm -rf "${usr_share:?}/$ts_dir" && mv "$ts_backup/$ts_dir" "$usr_share/" +done +mkdir -p "$ts_state" +for ts_file in "$ts_backup/state/"*; do + [ -f "$ts_file" ] && cp -p "$ts_file" "$ts_state/" +done +rm -rf "$ts_backup" + mkdir -p "$usr_share/music_menu" move "$usr_share/menu_music"/* "$usr_share/music_menu" move "$usr_share/advanced_music_hack/bgm_boot.wav" "$usr_share/themes/bgm_boot.wav" @@ -47,8 +74,6 @@ del "$rootfs/chmenu" del "$rootfs/chmenu_backup" del "$rootfs/b8025_fontfix_backup" -find "$hmod_dir" -type f -name "*andomiz*" -print0 -exec rm -f {} \; -find "$hmod_dir" -type f -name "*advanced_music_hack" -print0 -exec rm -f {} \; # Marks this mod as uninstalled unset cfg_theme_randomizR_enabled From 8985c891f4ade3146c7107ab3d980d825be1cecf Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 6 Oct 2026 12:22:00 +0000 Subject: [PATCH 107/210] Fix overlay theme staying mounted after leaving its folder fontFix binds title.font on top of the overlay after every chmenu, so the plain umount of the overlay failed with EBUSY and the next folder (or Home) kept the previous overlay theme. Unmount it lazily, and only when the mount at the UI path is overlayfs. --- mod/etc/options_menu/themes/scripts/om_themescript | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mod/etc/options_menu/themes/scripts/om_themescript b/mod/etc/options_menu/themes/scripts/om_themescript index 49af448..e71472d 100644 --- a/mod/etc/options_menu/themes/scripts/om_themescript +++ b/mod/etc/options_menu/themes/scripts/om_themescript @@ -164,7 +164,10 @@ themeSelector() { fontBindFix "restore" fi - mountpoint -q "$ORIGINAL_THEME" && umount "$ORIGINAL_THEME" + # lazy, since fontFix binds title.font on top of the overlay after every chmenu + while grep -q " $ORIGINAL_THEME overlayfs " /proc/mounts; do + umount -l "$ORIGINAL_THEME" || break + done musicHackUnbind cd "$THEMES_PATH/$PREVIOUS_THEME" && find ./* -type f ! -name "*.wav" ! -path "./fonts/*" | cut -c3- | while IFS= read -r UMOUNT_FILE; do From 7bed8e275f2336c888b55f2a3b67d3b36452c495 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 6 Oct 2026 12:56:48 +0000 Subject: [PATCH 108/210] Add a Folder Themes screen to assign themes to folders without renaming Theme Per Folder gains "Assign Theme to Folder", a native theme_manager screen (--folderThemes) listing Home and every game folder as a tree. Left/Right cycles a folder's theme in place, A opens a theme list, and Y resets to Automatic unless hakchi's "X/Y as autofire A/B" turns Y into B (or on NES). The preview shows where each folder's theme comes from and how many folders use it. Assignments are stored as "=" in folder_themes, so one theme can serve any number of folders without copies. om_themescript now resolves a folder's own assignment, then a theme named after it, then whatever its parent folder gets, up to the global theme. A folder's own theme always wins, so "Allow Different Theme to SubFolder" is gone. Naming a theme "default" for Home still works. install now keeps folder_themes across upgrades, since the previous version's uninstall removes it. "=" in folder_themes, so one theme can serve any number of folders without copies. "Automatic" clears an assignment. Naming a theme after a folder, and the "default" theme, still work. --- Makefile | 2 +- Makefile.docker | 2 +- .../options_menu/scripts/om_theme_selector | 2 +- mod/etc/options_menu/themes/lang/de-DE.lang | 22 +- mod/etc/options_menu/themes/lang/en-US.lang | 22 +- mod/etc/options_menu/themes/lang/es-ES.lang | 22 +- mod/etc/options_menu/themes/lang/fr-FR.lang | 22 +- mod/etc/options_menu/themes/lang/it-IT.lang | 22 +- mod/etc/options_menu/themes/lang/ja-JP.lang | 22 +- mod/etc/options_menu/themes/lang/nl-NL.lang | 22 +- mod/etc/options_menu/themes/lang/pt-PT.lang | 22 +- mod/etc/options_menu/themes/lang/ru-RU.lang | 22 +- .../options_menu/themes/scripts/om_settings | 1 + .../themes/scripts/om_themescript | 76 ++-- mod/etc/options_menu/themes/scripts/om_vars | 86 +++- .../settings/commands/_themeperfolder_assign | 6 + .../commands/c0000_themeperfolder_child | 7 - .../themes/settings/scripts/om_folderthemes | 23 + .../themes/settings/scripts/om_state | 1 - .../themes/settings/scripts/om_theme | 1 + mod/install | 3 +- mod/readme.md | 10 +- mod/uninstall | 1 + src/command_loader.cpp | 48 +- src/command_loader.h | 4 + src/folder_theme_model.cpp | 181 ++++++++ src/folder_theme_model.h | 70 +++ src/folder_themes_layout.cpp | 421 ++++++++++++++++++ src/theme_manager_app.h | 8 +- 29 files changed, 1072 insertions(+), 79 deletions(-) create mode 100644 mod/etc/options_menu/themes/settings/commands/_themeperfolder_assign delete mode 100644 mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child create mode 100644 mod/etc/options_menu/themes/settings/scripts/om_folderthemes create mode 100644 src/folder_theme_model.cpp create mode 100644 src/folder_theme_model.h create mode 100644 src/folder_themes_layout.cpp diff --git a/Makefile b/Makefile index 2fda511..ebfa203 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ endif CXXFLAGS = -std=c++11 -Os -Ivendor/OptionsMenu/src $(SDL_CFLAGS) LDLIBS = $(SDL_LIBS) VENDOR_SRC_DIR = vendor/OptionsMenu/src -SOURCES = src/main.cpp src/single_instance_lock.cpp src/command_loader.cpp src/texture_utils.cpp src/theme_manager_app.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp +SOURCES = src/main.cpp src/single_instance_lock.cpp src/command_loader.cpp src/texture_utils.cpp src/theme_manager_app.cpp src/folder_theme_model.cpp src/folder_themes_layout.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp OBJECTS = $(SOURCES:.cpp=.o) # Build theme_downloader with the Docker toolchain's static curl+OpenSSL. diff --git a/Makefile.docker b/Makefile.docker index e46b522..ac8346c 100644 --- a/Makefile.docker +++ b/Makefile.docker @@ -16,7 +16,7 @@ endif CXXFLAGS = -std=c++11 -Os -Ivendor/OptionsMenu/src $(SDL_CFLAGS) -DMOD_VERSION=\"v$(MOD_VER)\" LDLIBS = $(SDL_LIBS) VENDOR_SRC_DIR = vendor/OptionsMenu/src -SOURCES = src/main.cpp src/single_instance_lock.cpp src/command_loader.cpp src/texture_utils.cpp src/theme_manager_app.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp +SOURCES = src/main.cpp src/single_instance_lock.cpp src/command_loader.cpp src/texture_utils.cpp src/theme_manager_app.cpp src/folder_theme_model.cpp src/folder_themes_layout.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp OBJECTS = $(SOURCES:.cpp=.o) # Build theme_downloader with the toolchain's static curl+OpenSSL. diff --git a/mod/etc/options_menu/scripts/om_theme_selector b/mod/etc/options_menu/scripts/om_theme_selector index f91b2a7..0512aed 100644 --- a/mod/etc/options_menu/scripts/om_theme_selector +++ b/mod/etc/options_menu/scripts/om_theme_selector @@ -24,7 +24,7 @@ hasThemesToInstall enableMenuOptions remove "$omDummy" -[ -f "$om_firstrun" ] && remove "$om_themerandomizer" "$om_themeperfolder" "$om_themeperfolderchild" "$om_audiorandomizer" "$om_advmusichack" +[ -f "$om_firstrun" ] && remove "$om_themerandomizer" "$om_themeperfolder" "$om_audiorandomizer" "$om_advmusichack" if [ "$1" = "reload" ] || [ -f "$om_firstrun" ] ; then loadThemeList -r diff --git a/mod/etc/options_menu/themes/lang/de-DE.lang b/mod/etc/options_menu/themes/lang/de-DE.lang index a3d8e23..61a7860 100644 --- a/mod/etc/options_menu/themes/lang/de-DE.lang +++ b/mod/etc/options_menu/themes/lang/de-DE.lang @@ -23,7 +23,27 @@ UPDATE_THEME_LIST=Themenliste aktualisieren CLEAR_LIST=Liste leeren DOWNLOAD_THEMES=Themes herunterladen THEME_PER_FOLDER=Theme pro Ordner -ALLOW_DIFFERENT_THEME_SUBFOLDER=Anderes Theme für Unterordner erlauben +ASSIGN_FOLDER_THEMES=Theme einem Ordner zuweisen +FOLDER_THEMES=Ordner-Themes +HOME_FOLDER=Home-Menü +AUTOMATIC=Automatisch +FOLDER_THEME_LIST=Liste +FOLDER_THEME_CANCEL=Abbrechen +FOLDER_THEME_CHANGE=Ändern +FOLDER_THEME_RANDOM=Zufälliges Theme +FOLDER_THEME_STOCK=Original-Theme +FOLDER_THEME_TAG_ASSIGNED=ZUGEWIESEN +FOLDER_THEME_TAG_INHERITED=GEERBT +FOLDER_THEME_TAG_GLOBAL=GLOBAL +FOLDER_THEME_TAG_NAME=NACH NAME +FOLDER_THEME_SOURCE_ASSIGNED=Für diesen Ordner gewählt +FOLDER_THEME_SOURCE_NAME=Theme mit dem Namen dieses Ordners +FOLDER_THEME_SOURCE_INHERITED=Von %s +FOLDER_THEME_SOURCE_GLOBAL=Globales Theme der Konsole +FOLDER_THEME_USED_IN=In %s Ordnern verwendet +FOLDER_THEME_USED_ONCE=In 1 Ordner verwendet +FOLDER_THEME_UNUSED=Keinem Ordner zugewiesen +FOLDER_THEME_FOR=Theme für %s THEME_RANDOMIZER=Zufälliges Theme AUDIO_RANDOMIZER_HOME=Zufalls-Audio: Home AUDIO_RANDOMIZER_ALL_FOLDERS=Alle Ordner (nur Themes ohne JEGLICHE Audiodateien) diff --git a/mod/etc/options_menu/themes/lang/en-US.lang b/mod/etc/options_menu/themes/lang/en-US.lang index 7bc6fbb..d8715ae 100644 --- a/mod/etc/options_menu/themes/lang/en-US.lang +++ b/mod/etc/options_menu/themes/lang/en-US.lang @@ -23,7 +23,27 @@ UPDATE_THEME_LIST=Update Theme List CLEAR_LIST=Clear List DOWNLOAD_THEMES=Download Themes THEME_PER_FOLDER=Theme Per Folder -ALLOW_DIFFERENT_THEME_SUBFOLDER=Allow Different Theme to SubFolder +ASSIGN_FOLDER_THEMES=Assign Theme to Folder +FOLDER_THEMES=Folder Themes +HOME_FOLDER=Home +AUTOMATIC=Automatic +FOLDER_THEME_LIST=List +FOLDER_THEME_CANCEL=Cancel +FOLDER_THEME_CHANGE=Change +FOLDER_THEME_RANDOM=Random theme +FOLDER_THEME_STOCK=Original theme +FOLDER_THEME_TAG_ASSIGNED=ASSIGNED +FOLDER_THEME_TAG_INHERITED=INHERITED +FOLDER_THEME_TAG_GLOBAL=GLOBAL +FOLDER_THEME_TAG_NAME=BY NAME +FOLDER_THEME_SOURCE_ASSIGNED=Chosen for this folder +FOLDER_THEME_SOURCE_NAME=Theme named after this folder +FOLDER_THEME_SOURCE_INHERITED=From %s +FOLDER_THEME_SOURCE_GLOBAL=The console's global theme +FOLDER_THEME_USED_IN=Used in %s folders +FOLDER_THEME_USED_ONCE=Used in 1 folder +FOLDER_THEME_UNUSED=Not assigned to any folder +FOLDER_THEME_FOR=Theme for %s THEME_RANDOMIZER=Theme Randomizer AUDIO_RANDOMIZER_HOME=Audio Randomizer: Home AUDIO_RANDOMIZER_ALL_FOLDERS=All Folders (Only Themes Without ANY Audio Files) diff --git a/mod/etc/options_menu/themes/lang/es-ES.lang b/mod/etc/options_menu/themes/lang/es-ES.lang index 32ba1d0..be6b10f 100644 --- a/mod/etc/options_menu/themes/lang/es-ES.lang +++ b/mod/etc/options_menu/themes/lang/es-ES.lang @@ -23,7 +23,27 @@ UPDATE_THEME_LIST=Actualizar lista de temas CLEAR_LIST=Vaciar lista DOWNLOAD_THEMES=Descargar temas THEME_PER_FOLDER=Tema por carpeta -ALLOW_DIFFERENT_THEME_SUBFOLDER=Permitir tema distinto en la subcarpeta +ASSIGN_FOLDER_THEMES=Asignar tema a carpeta +FOLDER_THEMES=Temas por carpeta +HOME_FOLDER=Inicio +AUTOMATIC=Automático +FOLDER_THEME_LIST=Lista +FOLDER_THEME_CANCEL=Cancelar +FOLDER_THEME_CHANGE=Cambiar +FOLDER_THEME_RANDOM=Tema aleatorio +FOLDER_THEME_STOCK=Tema original +FOLDER_THEME_TAG_ASSIGNED=ASIGNADO +FOLDER_THEME_TAG_INHERITED=HEREDADO +FOLDER_THEME_TAG_GLOBAL=GLOBAL +FOLDER_THEME_TAG_NAME=POR NOMBRE +FOLDER_THEME_SOURCE_ASSIGNED=Elegido para esta carpeta +FOLDER_THEME_SOURCE_NAME=Tema con el nombre de esta carpeta +FOLDER_THEME_SOURCE_INHERITED=De %s +FOLDER_THEME_SOURCE_GLOBAL=Tema global de la consola +FOLDER_THEME_USED_IN=Usado en %s carpetas +FOLDER_THEME_USED_ONCE=Usado en 1 carpeta +FOLDER_THEME_UNUSED=Sin carpetas asignadas +FOLDER_THEME_FOR=Tema para %s THEME_RANDOMIZER=Tema aleatorio AUDIO_RANDOMIZER_HOME=Audio aleatorio: Inicio AUDIO_RANDOMIZER_ALL_FOLDERS=Todas las carpetas (solo temas sin NINGÚN archivo de audio) diff --git a/mod/etc/options_menu/themes/lang/fr-FR.lang b/mod/etc/options_menu/themes/lang/fr-FR.lang index 0c02144..3339435 100644 --- a/mod/etc/options_menu/themes/lang/fr-FR.lang +++ b/mod/etc/options_menu/themes/lang/fr-FR.lang @@ -23,7 +23,27 @@ UPDATE_THEME_LIST=Mettre à jour la liste des thèmes CLEAR_LIST=Vider la liste DOWNLOAD_THEMES=Télécharger des thèmes THEME_PER_FOLDER=Thème par dossier -ALLOW_DIFFERENT_THEME_SUBFOLDER=Autoriser un thème différent pour le sous-dossier +ASSIGN_FOLDER_THEMES=Attribuer un thème au dossier +FOLDER_THEMES=Thèmes des dossiers +HOME_FOLDER=Accueil +AUTOMATIC=Automatique +FOLDER_THEME_LIST=Liste +FOLDER_THEME_CANCEL=Annuler +FOLDER_THEME_CHANGE=Changer +FOLDER_THEME_RANDOM=Thème aléatoire +FOLDER_THEME_STOCK=Thème d'origine +FOLDER_THEME_TAG_ASSIGNED=ATTRIBUÉ +FOLDER_THEME_TAG_INHERITED=HÉRITÉ +FOLDER_THEME_TAG_GLOBAL=GLOBAL +FOLDER_THEME_TAG_NAME=PAR NOM +FOLDER_THEME_SOURCE_ASSIGNED=Choisi pour ce dossier +FOLDER_THEME_SOURCE_NAME=Thème au nom de ce dossier +FOLDER_THEME_SOURCE_INHERITED=De %s +FOLDER_THEME_SOURCE_GLOBAL=Thème global de la console +FOLDER_THEME_USED_IN=Utilisé dans %s dossiers +FOLDER_THEME_USED_ONCE=Utilisé dans 1 dossier +FOLDER_THEME_UNUSED=Attribué à aucun dossier +FOLDER_THEME_FOR=Thème pour %s THEME_RANDOMIZER=Thème aléatoire AUDIO_RANDOMIZER_HOME=Audio aléatoire : Accueil AUDIO_RANDOMIZER_ALL_FOLDERS=Tous les dossiers (thèmes sans AUCUN fichier audio) diff --git a/mod/etc/options_menu/themes/lang/it-IT.lang b/mod/etc/options_menu/themes/lang/it-IT.lang index 0bfa487..0362c7f 100644 --- a/mod/etc/options_menu/themes/lang/it-IT.lang +++ b/mod/etc/options_menu/themes/lang/it-IT.lang @@ -23,7 +23,27 @@ UPDATE_THEME_LIST=Aggiorna elenco temi CLEAR_LIST=Svuota elenco DOWNLOAD_THEMES=Scarica temi THEME_PER_FOLDER=Tema per cartella -ALLOW_DIFFERENT_THEME_SUBFOLDER=Consenti tema diverso nella sottocartella +ASSIGN_FOLDER_THEMES=Assegna tema alla cartella +FOLDER_THEMES=Temi delle cartelle +HOME_FOLDER=Home +AUTOMATIC=Automatico +FOLDER_THEME_LIST=Elenco +FOLDER_THEME_CANCEL=Annulla +FOLDER_THEME_CHANGE=Cambia +FOLDER_THEME_RANDOM=Tema casuale +FOLDER_THEME_STOCK=Tema originale +FOLDER_THEME_TAG_ASSIGNED=ASSEGNATO +FOLDER_THEME_TAG_INHERITED=EREDITATO +FOLDER_THEME_TAG_GLOBAL=GLOBALE +FOLDER_THEME_TAG_NAME=PER NOME +FOLDER_THEME_SOURCE_ASSIGNED=Scelto per questa cartella +FOLDER_THEME_SOURCE_NAME=Tema con il nome di questa cartella +FOLDER_THEME_SOURCE_INHERITED=Da %s +FOLDER_THEME_SOURCE_GLOBAL=Tema globale della console +FOLDER_THEME_USED_IN=Usato in %s cartelle +FOLDER_THEME_USED_ONCE=Usato in 1 cartella +FOLDER_THEME_UNUSED=Non assegnato a nessuna cartella +FOLDER_THEME_FOR=Tema per %s THEME_RANDOMIZER=Tema casuale AUDIO_RANDOMIZER_HOME=Audio casuale: Home AUDIO_RANDOMIZER_ALL_FOLDERS=Tutte le cartelle (solo temi senza ALCUN file audio) diff --git a/mod/etc/options_menu/themes/lang/ja-JP.lang b/mod/etc/options_menu/themes/lang/ja-JP.lang index 86dc940..9a6aee9 100644 --- a/mod/etc/options_menu/themes/lang/ja-JP.lang +++ b/mod/etc/options_menu/themes/lang/ja-JP.lang @@ -23,7 +23,27 @@ UPDATE_THEME_LIST=Themeいちらんをこうしん CLEAR_LIST=いちらんをさくじょ DOWNLOAD_THEMES=Themeをdownload THEME_PER_FOLDER=Folderごとのtheme -ALLOW_DIFFERENT_THEME_SUBFOLDER=サブFolderにちがうThemeをゆるす +ASSIGN_FOLDER_THEMES=FolderにThemeをわりあてる +FOLDER_THEMES=FolderのTheme +HOME_FOLDER=ホーム +AUTOMATIC=じどう +FOLDER_THEME_LIST=リスト +FOLDER_THEME_CANCEL=キャンセル +FOLDER_THEME_CHANGE=かえる +FOLDER_THEME_RANDOM=ランダムTheme +FOLDER_THEME_STOCK=オリジナルTheme +FOLDER_THEME_TAG_ASSIGNED=してい +FOLDER_THEME_TAG_INHERITED=ひきつぎ +FOLDER_THEME_TAG_GLOBAL=グローバル +FOLDER_THEME_TAG_NAME=なまえ +FOLDER_THEME_SOURCE_ASSIGNED=このFolderにえらんだTheme +FOLDER_THEME_SOURCE_NAME=このFolderとおなじなまえのTheme +FOLDER_THEME_SOURCE_INHERITED=%sから +FOLDER_THEME_SOURCE_GLOBAL=コンソールのグローバルTheme +FOLDER_THEME_USED_IN=%sこのFolderでしよう +FOLDER_THEME_USED_ONCE=1このFolderでしよう +FOLDER_THEME_UNUSED=どのFolderにもしていされていない +FOLDER_THEME_FOR=%sのTheme THEME_RANDOMIZER=Themeをrandomに AUDIO_RANDOMIZER_HOME=Audioをrandomに: Home AUDIO_RANDOMIZER_ALL_FOLDERS=すべてのFolder (Audioファイルが1つもないThemeのみ) diff --git a/mod/etc/options_menu/themes/lang/nl-NL.lang b/mod/etc/options_menu/themes/lang/nl-NL.lang index eb54454..6291fef 100644 --- a/mod/etc/options_menu/themes/lang/nl-NL.lang +++ b/mod/etc/options_menu/themes/lang/nl-NL.lang @@ -23,7 +23,27 @@ UPDATE_THEME_LIST=Themalijst bijwerken CLEAR_LIST=Lijst wissen DOWNLOAD_THEMES=Thema's downloaden THEME_PER_FOLDER=Thema per map -ALLOW_DIFFERENT_THEME_SUBFOLDER=Ander thema voor submap toestaan +ASSIGN_FOLDER_THEMES=Thema aan map toewijzen +FOLDER_THEMES=Mapthema's +HOME_FOLDER=Home +AUTOMATIC=Automatisch +FOLDER_THEME_LIST=Lijst +FOLDER_THEME_CANCEL=Annuleren +FOLDER_THEME_CHANGE=Wijzigen +FOLDER_THEME_RANDOM=Willekeurig thema +FOLDER_THEME_STOCK=Origineel thema +FOLDER_THEME_TAG_ASSIGNED=TOEGEWEZEN +FOLDER_THEME_TAG_INHERITED=GEËRFD +FOLDER_THEME_TAG_GLOBAL=GLOBAAL +FOLDER_THEME_TAG_NAME=OP NAAM +FOLDER_THEME_SOURCE_ASSIGNED=Gekozen voor deze map +FOLDER_THEME_SOURCE_NAME=Thema met de naam van deze map +FOLDER_THEME_SOURCE_INHERITED=Van %s +FOLDER_THEME_SOURCE_GLOBAL=Globaal thema van de console +FOLDER_THEME_USED_IN=Gebruikt in %s mappen +FOLDER_THEME_USED_ONCE=Gebruikt in 1 map +FOLDER_THEME_UNUSED=Aan geen map toegewezen +FOLDER_THEME_FOR=Thema voor %s THEME_RANDOMIZER=Willekeurig thema AUDIO_RANDOMIZER_HOME=Willekeurige audio: Start AUDIO_RANDOMIZER_ALL_FOLDERS=Alle mappen (alleen thema's zonder ENIG audiobestand) diff --git a/mod/etc/options_menu/themes/lang/pt-PT.lang b/mod/etc/options_menu/themes/lang/pt-PT.lang index 79caf35..b1af290 100644 --- a/mod/etc/options_menu/themes/lang/pt-PT.lang +++ b/mod/etc/options_menu/themes/lang/pt-PT.lang @@ -23,7 +23,27 @@ UPDATE_THEME_LIST=Atualizar Lista de Temas CLEAR_LIST=Limpar Lista DOWNLOAD_THEMES=Descarregar Temas THEME_PER_FOLDER=Tema Por Pasta -ALLOW_DIFFERENT_THEME_SUBFOLDER=Permitir Tema Diferente na Subpasta +ASSIGN_FOLDER_THEMES=Atribuir Tema a Pasta +FOLDER_THEMES=Temas por Pasta +HOME_FOLDER=Início +AUTOMATIC=Automático +FOLDER_THEME_LIST=Lista +FOLDER_THEME_CANCEL=Cancelar +FOLDER_THEME_CHANGE=Mudar +FOLDER_THEME_RANDOM=Tema Aleatório +FOLDER_THEME_STOCK=Tema Original +FOLDER_THEME_TAG_ASSIGNED=ATRIBUÍDO +FOLDER_THEME_TAG_INHERITED=HERDADO +FOLDER_THEME_TAG_GLOBAL=GLOBAL +FOLDER_THEME_TAG_NAME=POR NOME +FOLDER_THEME_SOURCE_ASSIGNED=Escolhido para esta pasta +FOLDER_THEME_SOURCE_NAME=Tema com o nome desta pasta +FOLDER_THEME_SOURCE_INHERITED=Vem de %s +FOLDER_THEME_SOURCE_GLOBAL=Tema global da consola +FOLDER_THEME_USED_IN=Usado em %s pastas +FOLDER_THEME_USED_ONCE=Usado em 1 pasta +FOLDER_THEME_UNUSED=Sem pastas atribuídas +FOLDER_THEME_FOR=Tema para %s THEME_RANDOMIZER=Tema Aleatório AUDIO_RANDOMIZER_HOME=Áudio Aleatório: Home AUDIO_RANDOMIZER_ALL_FOLDERS=Todas as Pastas (Só Temas Sem Ficheiros de Áudio) diff --git a/mod/etc/options_menu/themes/lang/ru-RU.lang b/mod/etc/options_menu/themes/lang/ru-RU.lang index c8e6073..b75aa3f 100644 --- a/mod/etc/options_menu/themes/lang/ru-RU.lang +++ b/mod/etc/options_menu/themes/lang/ru-RU.lang @@ -23,7 +23,27 @@ UPDATE_THEME_LIST=Обновить список тем CLEAR_LIST=Очистить список DOWNLOAD_THEMES=Скачать темы THEME_PER_FOLDER=Тема для каждой папки -ALLOW_DIFFERENT_THEME_SUBFOLDER=Разрешить другую тему для подпапки +ASSIGN_FOLDER_THEMES=Назначить тему папке +FOLDER_THEMES=Темы папок +HOME_FOLDER=Главное меню +AUTOMATIC=Автоматически +FOLDER_THEME_LIST=Список +FOLDER_THEME_CANCEL=Отмена +FOLDER_THEME_CHANGE=Изменить +FOLDER_THEME_RANDOM=Случайная тема +FOLDER_THEME_STOCK=Оригинальная тема +FOLDER_THEME_TAG_ASSIGNED=НАЗНАЧЕНА +FOLDER_THEME_TAG_INHERITED=УНАСЛЕДОВАНА +FOLDER_THEME_TAG_GLOBAL=ГЛОБАЛЬНАЯ +FOLDER_THEME_TAG_NAME=ПО ИМЕНИ +FOLDER_THEME_SOURCE_ASSIGNED=Выбрана для этой папки +FOLDER_THEME_SOURCE_NAME=Тема с именем этой папки +FOLDER_THEME_SOURCE_INHERITED=Из %s +FOLDER_THEME_SOURCE_GLOBAL=Глобальная тема консоли +FOLDER_THEME_USED_IN=Используется в папках: %s +FOLDER_THEME_USED_ONCE=Используется в 1 папке +FOLDER_THEME_UNUSED=Не назначена ни одной папке +FOLDER_THEME_FOR=Тема для %s THEME_RANDOMIZER=Случайная тема AUDIO_RANDOMIZER_HOME=Случайное аудио: Главное меню AUDIO_RANDOMIZER_ALL_FOLDERS=Все папки (только темы БЕЗ аудиофайлов) diff --git a/mod/etc/options_menu/themes/scripts/om_settings b/mod/etc/options_menu/themes/scripts/om_settings index 14b6c01..257c55b 100644 --- a/mod/etc/options_menu/themes/scripts/om_settings +++ b/mod/etc/options_menu/themes/scripts/om_settings @@ -28,6 +28,7 @@ script_init echo "$state_path" > /tmp/om_state_path hasItemsToCleanUp "SystemCleanUp" +folderThemesRow if [ -f "$b_file" ] && [ -f "$p_file" ]; then enable_cmds "Uninstaller" diff --git a/mod/etc/options_menu/themes/scripts/om_themescript b/mod/etc/options_menu/themes/scripts/om_themescript index e71472d..2e90edd 100644 --- a/mod/etc/options_menu/themes/scripts/om_themescript +++ b/mod/etc/options_menu/themes/scripts/om_themescript @@ -55,16 +55,56 @@ fontBindFix() { done } +# folder_themes lines: = +assignedFolderTheme() { + local LINE + [ -n "$1" ] && [ -f "$STATE_PATH/folder_themes" ] || return 1 + while IFS= read -r LINE; do + if [ "${LINE%=*}" = "$1" ] && [ -d "$THEMES_PATH/${LINE##*=}" ]; then + echo "${LINE##*=}" + return 0 + fi + done <"$STATE_PATH/folder_themes" + return 1 +} + +# the entry for folder $1 inside its parent folder (not a Back/Home shortcut to it) +folderEntry() { + local DESKTOP + for DESKTOP in "$GAME_STORAGE"/*/CLV-S-00"$1"/CLV-S-00"$1".desktop; do + [ -f "$DESKTOP" ] || continue + grep -q -e '^Name=Back$' -e '^Name=Home$' "$DESKTOP" && continue + echo "$DESKTOP" + return 0 + done + return 1 +} + +# own assignment, else a theme named after the folder, else whatever its parent folder gets +folderTheme() { + local CODE="$1" + local DESKTOP + local KEY + local DEPTH=0 + if [ "$CODE" == "000" ]; then + assignedFolderTheme "home" + return + fi + while [ "$CODE" != "000" ] && [ "$DEPTH" -lt 16 ]; do + DEPTH=$((DEPTH + 1)) + DESKTOP="$(folderEntry "$CODE")" || return 1 + KEY="$(sed -n 's/^Name=//p' "$DESKTOP" | head -n1 | sed 's/ /_/g' | awk '{print tolower($0)}')" + assignedFolderTheme "$KEY" && return 0 + [ -n "$KEY" ] && [ -d "$THEMES_PATH/$KEY" ] && echo "$KEY" && return 0 + CODE="$(basename "$(dirname "$(dirname "$DESKTOP")")")" + done + return 1 +} + themeSelector() { local FOLDER_ID local GAME_STORAGE - local GET_THEME_NAME - local IS_CHILD - local PARENT_ID - local PARENT_THEME_TITLE local PREVIOUS_THEME - local THEME_MATCH - local PARENT_DESKTOP_FILE local UI0="ui/$sftype-$sfregion" local UI1="" local SYSTEM_TYPE="$sftype" @@ -114,22 +154,7 @@ themeSelector() { BGM_HOME="" GAME_STORAGE="$(findGameStorage)" - find "$GAME_STORAGE" -maxdepth 3 -type f -name "$ACTIVE_FOLDER.desktop" | sort -u | while IFS= read -r CONFIG_FILE; do - cd "$GAME_STORAGE" || exit - for i in $(find . -maxdepth 3 -type d -name "$ACTIVE_FOLDER" | sed "s|^\./||"); do - - if [ "$(grep -L -e "Name=Back" -e "Name=Home" "$i/$(basename "$i").desktop")" ]; then - PARENT_ID="$(dirname "$i")" - local FULL_PARENT_ID="CLV-S-00${PARENT_ID}" - break - fi - done - PARENT_DESKTOP_FILE="$(find "." -maxdepth 3 -type f -name "$FULL_PARENT_ID.desktop" -exec grep -L -e "Name=Back" -e "Name=Home" {} \; | tail -n1)" - [ -n "$PARENT_DESKTOP_FILE" ] && PARENT_THEME_TITLE="$(grep -F 'Name=' "$PARENT_DESKTOP_FILE" | sed -r 's/Name=//g;s/ /_/g' | awk '{print tolower($0)}')" - IS_CHILD="$(printf '%s\n' "$THEME_DIR_LIST" | grep -w "$PARENT_THEME_TITLE")" - GET_THEME_NAME="$(grep -F 'Name=' "$CONFIG_FILE" | sed -r '/[Bb]ack/d;s/Name=//g;s/ /_/g' | awk '{print tolower($0)}')" - - THEME_MATCH="$(printf '%s\n' "$THEME_DIR_LIST" | grep -w "$GET_THEME_NAME")" + find "$GAME_STORAGE" -maxdepth 3 -type f -name "$ACTIVE_FOLDER.desktop" | sort -u | while IFS= read -r _; do if [ -f "$STATE_PATH/om_themerandomizer" ]; then SELECTED_THEME="$PREVIOUS_THEME" local n="0" @@ -141,10 +166,9 @@ themeSelector() { [ "$FOLDER_ID" == "000" ] && [ -d "$THEMES_PATH/default" ] && SELECTED_THEME="default" if [ -f "$STATE_PATH/om_themeperfolder" ]; then - [ -n "$THEME_MATCH" ] && SELECTED_THEME="$GET_THEME_NAME" - if [ ! -f "$STATE_PATH/om_themeperfolderchild" ]; then - [ -n "$IS_CHILD" ] && SELECTED_THEME="$PARENT_THEME_TITLE" - fi + local FOLDER_THEME + FOLDER_THEME="$(folderTheme "$FOLDER_ID")" + [ -n "$FOLDER_THEME" ] && SELECTED_THEME="$FOLDER_THEME" fi FULL_THEME_PATH="$THEMES_PATH/$SELECTED_THEME" diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index a9520fb..e360999 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -78,7 +78,8 @@ omOverlayThemeScript="$omModScripts/om_overlaythmcript" om_themerandomizer="$state_path/om_themerandomizer" om_themeperfolder="$state_path/om_themeperfolder" -om_themeperfolderchild="$state_path/om_themeperfolderchild" +om_folderthemes="$state_path/folder_themes" +folderThemesTmp="/tmp/om_folderthemes" om_audiorandomizer="$state_path/om_audiorandomizer" om_advmusichack="$state_path/om_advmusichack" om_firstrun="$omMod/om_firstrun" @@ -100,6 +101,8 @@ enableAudioSetting="$omModSettingsCmds/c0001_audiorandomizer" disableAudioSetting="$omModSettingsCmds/_audiorandomizer" enableAdvancedMusicHack="$omModSettingsCmds/c0001_audiorandomizerall" disableAdvancedMusicHack="$omModSettingsCmds/_audiorandomizerall" +enableFolderThemes="$omModSettingsCmds/c0000_themeperfolder_assign" +disableFolderThemes="$omModSettingsCmds/_themeperfolder_assign" enableThemeSetting="$omModSettingsCmds/c0000_themerandomizer" disableThemeSetting="$omModSettingsCmds/_themerandomizer" enableSystemCleanUp="$omModSettingsCmds/c0001_systemcleanup" @@ -681,10 +684,12 @@ modCleanUp() { } themeSettings() { - if [ "$1" = "child" ]; then - toggleOptions "$om_themeperfolderchild" "$om_themeperfolder" - elif [ "$1" = "tpf" ]; then - toggleOptions "$om_themeperfolder" "$om_themeperfolderchild" "no-cascade" + if [ "$1" = "tpf" ]; then + if [ ! -f "$om_themeperfolder" ]; then + settingsOptionChecker "on" "$om_themeperfolder" + else + settingsOptionChecker "off" "$om_themeperfolder" + fi else if [ ! -f "$om_themerandomizer" ]; then settingsOptionChecker "on" "$om_themerandomizer" @@ -740,3 +745,74 @@ hasItemsToCleanUp() { [ ! -f "$enableUninstaller" ] && remove "$omModSettingsSpacer" fi } + +folderThemesRow() { + if [ -f "$om_themeperfolder" ] && [ -d "$has_folders" ]; then + enable_cmds "FolderThemes" + else + disable_cmds "FolderThemes" + fi +} + +# folder key (same snake_case om_themescript derives from the folder Name=) folder name depth, +# in tree order: a folder entry living in games/

/CLV-S-00/ is folder inside folder

+listFolders() { + tab="$(printf '\t')" + find "$gStorage" -maxdepth 3 -type f -name 'CLV-S-00*.desktop' -exec awk ' + /^Name=/ { + name = substr($0, 6) + if (name == "Back" || name == "Home") next + n = split(FILENAME, path, "/") + print path[n - 2] "\t" substr(path[n - 1], 9) "\t" name + }' {} + | sort -f -t "$tab" -k3,3 | awk -F "$tab" ' + { kids[$1, ++count[$1]] = $2; names[$2] = $3 } + function walk(parent, depth, i, child, key) { + for (i = 1; i <= count[parent]; i++) { + child = kids[parent, i] + if (child in seen) continue + seen[child] = 1 + key = names[child] + gsub(/ /, "_", key) + print tolower(key) "\t" names[child] "\t" depth + walk(child, depth + 1) + } + } + END { walk("000", 0) }' +} + +themeTitle() { + title="$(sed -n 's/^NAME=//p' "$theme_path/$1/info" 2>/dev/null | sed 's/ \[[^]]*\]$//')" + echo "${title:-$1}" +} + +themePreview() { + preview="$thmPreview/$1.png" + [ ! -f "$preview" ] && preview="$theme_path/$1.png" + [ ! -f "$preview" ] && preview="$optionsMenu/images/default_preview.png" + echo "$preview" +} + +# the theme every folder without one of its own falls back to +globalTheme() { + if [ -f "$om_themerandomizer" ]; then + echo "@random" + elif [ -f "$b_file" ]; then + sed -n 's/^ *local SELECTED_THEME="\(.*\)"$/\1/p' "$b_file" | head -n1 | grep -v '^CHANGEME$' + fi +} + +folderThemesMenu() { + data_dir="$folderThemesTmp" + rm -rf "$data_dir" && mkdir -p "$data_dir" + { + printf 'home\t%s\t0\n' "$(translate HOME_FOLDER)" + listFolders + } >"$data_dir/folders.list" + find "$theme_path" -mindepth 1 -maxdepth 1 -type d | sed 's#^.*/##' | sort | while IFS= read -r thm; do + printf '%s\t%s\t%s\n' "$thm" "$(themeTitle "$thm")" "$(themePreview "$thm")" + done >"$data_dir/themes.list" + globalTheme >"$data_dir/global" + echo "sh $omModScripts/om_settings" >"$data_dir/om_return" + + $optionsMenu/lib/theme_manager --commandPath "$data_dir/" --scriptPath "$data_dir" --title FOLDER_THEMES --folderThemes "$data_dir" --mapping "$om_folderthemes" & +} diff --git a/mod/etc/options_menu/themes/settings/commands/_themeperfolder_assign b/mod/etc/options_menu/themes/settings/commands/_themeperfolder_assign new file mode 100644 index 0000000..b6cfa4b --- /dev/null +++ b/mod/etc/options_menu/themes/settings/commands/_themeperfolder_assign @@ -0,0 +1,6 @@ +COMMAND_NAME=ASSIGN_FOLDER_THEMES +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +CHILD=TRUE +COMMAND_STR=usleep 50000 && sh %script_dir%/om_folderthemes & +PREVIEW_IMAGE=/etc/options_menu/images/randtheme.png diff --git a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child deleted file mode 100644 index acc2571..0000000 --- a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder_child +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=ALLOW_DIFFERENT_THEME_SUBFOLDER -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -CHILD=TRUE -COMMAND_STR=sh %script_dir%/om_theme child -STATE_STR=sh %script_dir%/om_state theme_per_folder_child -PREVIEW_IMAGE=/etc/options_menu/images/randtheme.png diff --git a/mod/etc/options_menu/themes/settings/scripts/om_folderthemes b/mod/etc/options_menu/themes/settings/scripts/om_folderthemes new file mode 100644 index 0000000..ea046cf --- /dev/null +++ b/mod/etc/options_menu/themes/settings/scripts/om_folderthemes @@ -0,0 +1,23 @@ +#!/bin/sh +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# shellcheck source=/dev/null +source $mountpoint/etc/options_menu/themes/scripts/om_vars +script_init + +folderThemesMenu diff --git a/mod/etc/options_menu/themes/settings/scripts/om_state b/mod/etc/options_menu/themes/settings/scripts/om_state index d5a75fa..68adfd6 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_state +++ b/mod/etc/options_menu/themes/settings/scripts/om_state @@ -31,7 +31,6 @@ fi case "$1" in theme_per_folder) flag="$state_path/om_themeperfolder" ;; - theme_per_folder_child) flag="$state_path/om_themeperfolderchild" ;; theme_randomizer) flag="$state_path/om_themerandomizer" ;; audio_randomizer) flag="$state_path/om_audiorandomizer" ;; audio_randomizer_all) flag="$state_path/om_advmusichack" ;; diff --git a/mod/etc/options_menu/themes/settings/scripts/om_theme b/mod/etc/options_menu/themes/settings/scripts/om_theme index 1804692..e9a4bfd 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_theme +++ b/mod/etc/options_menu/themes/settings/scripts/om_theme @@ -21,5 +21,6 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init themeSettings "$1" +folderThemesRow $optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS --layout list & diff --git a/mod/install b/mod/install index 5b3f462..c2d5d1d 100644 --- a/mod/install +++ b/mod/install @@ -40,7 +40,7 @@ mkdir -p "$ts_backup/state" for ts_dir in themes music_menu; do [ -d "$usr_share/$ts_dir" ] && mv "$usr_share/$ts_dir" "$ts_backup/" done -for ts_file in lastTheme prevTheme active_theme; do +for ts_file in lastTheme prevTheme active_theme folder_themes; do [ -f "$ts_state/$ts_file" ] && cp -p "$ts_state/$ts_file" "$ts_backup/state/" done for ts_unfile in "$hmod_dir/uninstall-"*andomiz* \ @@ -73,6 +73,7 @@ del "$rootfs/fontfix" del "$rootfs/chmenu" del "$rootfs/chmenu_backup" del "$rootfs/b8025_fontfix_backup" +del "$ts_state/om_themeperfolderchild" # Marks this mod as uninstalled unset cfg_theme_randomizR_enabled diff --git a/mod/readme.md b/mod/readme.md index 06d443b..8f6f5cf 100644 --- a/mod/readme.md +++ b/mod/readme.md @@ -17,7 +17,7 @@ The UI is my own standalone C++/SDL app (`theme_manager`), not a set of Lua scri - Build your own DIY theme from pieces of the themes you already have (background, sprites, colors, packed art, fonts...) - Theme randomizer — a different theme each time you go Home (off by default) - Audio randomizer on the Home folder, optionally extended to every folder (off by default) -- Theme per folder: give a folder its own theme by naming a theme after it; a folder with its own theme keeps its own icon instead of inheriting the parent theme's +- Theme per folder: give any folder (and Home) its own theme from one screen; subfolders without one inherit their parent folder's - Custom fonts per theme - Custom color palette for the Theme Manager UI itself (`theme.cfg`) - System clean-up to remove files your console type doesn't need @@ -25,13 +25,15 @@ The UI is my own standalone C++/SDL app (`theme_manager`), not a set of Lua scri ## Set a theme per folder, what is that? -If you have your games organized into system folders, you can give each one its own theme instead of one theme for the whole console. Turn on **Theme Per Folder** in Settings, then name a theme folder after the game folder it should apply to (lower snake_case). Eg: game folder `Nintendo - Nintendo Entertainment System` → theme folder `nintendo_-_nintendo_entertainment_system`. +If you have your games organized into system folders, you can give each one its own theme instead of one theme for the whole console. Turn on **Theme Per Folder** in Settings, then open **Assign Theme To Folder**. Your folders are listed as a tree: press Left/Right on a folder to cycle through the themes, or A to pick one from a list. The preview shows where each folder's theme comes from. The same theme can be assigned to as many folders as you like, no copies or renaming needed. **Automatic**, the first entry when cycling and in the list, clears a folder's assignment. -By default, a subfolder inherits its parent's theme. Turn on **Allow Different Theme To Subfolder** if you want a subfolder to use its own matching theme instead — and if it does, that subfolder keeps its own real icon rather than being overwritten by the parent theme's. +Naming a theme folder after the game folder (lower snake_case) still works for folders without an assignment. Eg: game folder `Nintendo - Nintendo Entertainment System` → theme folder `nintendo_-_nintendo_entertainment_system`. + +A folder on Automatic uses its parent folder's theme, all the way up; a top-level folder on Automatic uses the theme you applied for the whole console. A folder's own theme always wins over its parent's. ## What if I want a specific theme on my main menu? -Name a theme `default` and it becomes your Home menu theme. +With **Theme Per Folder** on, open **Assign Theme To Folder** and set a theme on **Home**, the first entry in the list. Naming a theme `default` also still works. ## What do you mean by "build your own DIY theme"? diff --git a/mod/uninstall b/mod/uninstall index 4baf309..c032af1 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -50,6 +50,7 @@ sed -i '/# themeselector$/,+4d' "$rootfs/etc/preinit.d/b8025_fontfix" remove "$mountpoint/$profilepath/$modname/lastTheme" remove "$mountpoint/$profilepath/$modname/prevTheme" remove "$mountpoint/$profilepath/$modname/active_theme" +remove "$mountpoint/$profilepath/$modname/folder_themes" remove "$mountpoint/$profilepath/$modname/om_advmusichack" remove "$mountpoint/$profilepath/$modname/om_audiorandomizer" remove "$mountpoint/$profilepath/$modname/om_themerandomizer" diff --git a/src/command_loader.cpp b/src/command_loader.cpp index 879ab9c..4a0c6cb 100644 --- a/src/command_loader.cpp +++ b/src/command_loader.cpp @@ -33,28 +33,6 @@ using DirHandle = std::unique_ptr; struct PipeCloser { void operator()(FILE * pipe) const { if(pipe) pclose(pipe); } }; using PipeHandle = std::unique_ptr; -// child screens inherit this via fork+exec, same as OM_BACK_STACK - so only the first screen -// that needs sftype pays for the shell fork -std::string ReadSftype() -{ - if(const char * env = std::getenv("OM_SFTYPE")) - return env; - - std::string result; - PipeHandle pipe(popen("source /etc/preinit; script_init; echo $sftype", "r")); - if(pipe) - { - char buffer[32] = {0}; - if(fgets(buffer, sizeof(buffer), pipe.get())) - result = buffer; - } - while(!result.empty() && (result.back() == '\n' || result.back() == '\r')) - result.pop_back(); - - setenv("OM_SFTYPE", result.c_str(), 1); - return result; -} - // my own convention, not OptionsMenu's Command format struct ConsoleOnlyFlags { bool nesOnly = false; bool snesOnly = false; }; @@ -77,6 +55,28 @@ ConsoleOnlyFlags ReadConsoleOnlyFlags(const std::string & path) } +// child screens inherit this via fork+exec, same as OM_BACK_STACK - so only the first screen +// that needs sftype pays for the shell fork +std::string ReadSftype() +{ + if(const char * env = std::getenv("OM_SFTYPE")) + return env; + + std::string result; + PipeHandle pipe(popen("source /etc/preinit; script_init; echo $sftype", "r")); + if(pipe) + { + char buffer[32] = {0}; + if(fgets(buffer, sizeof(buffer), pipe.get())) + result = buffer; + } + while(!result.empty() && (result.back() == '\n' || result.back() == '\r')) + result.pop_back(); + + setenv("OM_SFTYPE", result.c_str(), 1); + return result; +} + AppOptions ParseArgs(int argc, char * argv[], const std::string & optionsLocation) { AppOptions options; @@ -96,6 +96,10 @@ AppOptions ParseArgs(int argc, char * argv[], const std::string & optionsLocatio options.titleKey = argv[++i]; else if(std::strcmp(argv[i], "--layout") == 0 && i+1 < argc) options.gridLayout = (std::string(argv[++i]) == "grid"); + else if(std::strcmp(argv[i], "--folderThemes") == 0 && i+1 < argc) + options.folderThemesDir = argv[++i]; + else if(std::strcmp(argv[i], "--mapping") == 0 && i+1 < argc) + options.folderThemesMapping = argv[++i]; } if(!options.commandLocation.empty() && options.commandLocation.back() != '/') options.commandLocation += '/'; diff --git a/src/command_loader.h b/src/command_loader.h index 15743a1..37444fb 100644 --- a/src/command_loader.h +++ b/src/command_loader.h @@ -22,8 +22,12 @@ struct AppOptions std::string titleKey = "THEME_SELECTOR"; bool isRootScreen = true; // no --commandPath override - top of the theme-selector's own tree bool gridLayout = true; + std::string folderThemesDir; // --folderThemes: the folder theme screen, fed by om_folderthemes + std::string folderThemesMapping; }; +std::string ReadSftype(); // cached in OM_SFTYPE for child screens + AppOptions ParseArgs(int argc, char * argv[], const std::string & optionsLocation); // OM_BACK_STACK is set by OptionsMenu before launching "Theme Options" diff --git a/src/folder_theme_model.cpp b/src/folder_theme_model.cpp new file mode 100644 index 0000000..7e4e86c --- /dev/null +++ b/src/folder_theme_model.cpp @@ -0,0 +1,181 @@ +/** + * Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#include "folder_theme_model.h" + +#include +#include +#include + +namespace +{ + const char * const HomeKey = "home"; + const char * const HomeNameMatch = "default"; + const char * const RandomGlobal = "@random"; + + std::vector SplitTabs(const std::string & line) + { + std::vector fields; + size_t start = 0; + for(size_t tab; (tab = line.find('\t', start)) != std::string::npos; start = tab + 1) + fields.push_back(line.substr(start, tab - start)); + fields.push_back(line.substr(start)); + return fields; + } + + bool ReadLine(std::ifstream & in, std::string & line) + { + if(!std::getline(in, line)) + return false; + if(!line.empty() && line.back() == '\r') + line.pop_back(); + return true; + } +} + +bool FolderThemeModel::Load(const std::string & dataDir, const std::string & mappingPath) +{ + mappingPath_ = mappingPath; + + std::ifstream folders(dataDir + "/folders.list"); + std::vector lastAtDepth; + for(std::string line; ReadLine(folders, line);) + { + std::vector f = SplitTabs(line); + if(f.size() < 3 || f[0].empty()) + continue; + FolderEntry entry{ f[0], f[1], std::atoi(f[2].c_str()), -1 }; + if(entry.depth < 0 || entry.depth > static_cast(lastAtDepth.size())) + entry.depth = static_cast(lastAtDepth.size()); + if(entry.depth > 0) + entry.parent = lastAtDepth[entry.depth - 1]; + lastAtDepth.resize(entry.depth); + lastAtDepth.push_back(static_cast(folders_.size())); + folders_.push_back(entry); + } + + std::ifstream themes(dataDir + "/themes.list"); + for(std::string line; ReadLine(themes, line);) + { + std::vector f = SplitTabs(line); + if(f.size() < 3 || f[0].empty()) + continue; + themes_.push_back({ f[0], f[1].empty() ? f[0] : f[1], f[2] }); + } + + std::ifstream global(dataDir + "/global"); + std::string globalId; + ReadLine(global, globalId); + globalRandom_ = globalId == RandomGlobal; + globalTheme_ = ThemeIndex(globalId); + + assigned_.assign(folders_.size(), -1); + std::ifstream mapping(mappingPath_); + for(std::string line; ReadLine(mapping, line);) + { + size_t eq = line.rfind('='); + int theme = eq == std::string::npos ? -1 : ThemeIndex(line.substr(eq + 1)); + int folder = eq == std::string::npos ? -1 : FolderIndex(line.substr(0, eq)); + if(theme < 0 || folder < 0) + { + if(!line.empty()) + unknownLines_.push_back(line); + continue; + } + for(size_t i = 0; i < folders_.size(); ++i) + if(folders_[i].key == folders_[folder].key) + assigned_[i] = theme; + } + + return !folders_.empty(); +} + +bool FolderThemeModel::Save() const +{ + const std::string tmpPath = mappingPath_ + ".tmp"; + { + std::ofstream out(tmpPath, std::ios::trunc); + if(!out) + return false; + for(const std::string & line : unknownLines_) + out << line << '\n'; + std::vector written; + for(size_t i = 0; i < folders_.size(); ++i) + { + if(assigned_[i] < 0) + continue; + bool seen = false; + for(const std::string & key : written) + seen = seen || key == folders_[i].key; + if(seen) + continue; + written.push_back(folders_[i].key); + out << folders_[i].key << '=' << themes_[assigned_[i]].id << '\n'; + } + if(!out) + return false; + } + return std::rename(tmpPath.c_str(), mappingPath_.c_str()) == 0; +} + +void FolderThemeModel::Assign(int folder, int theme) +{ + // the mapping is keyed by folder name, so same-named folders share it + for(size_t i = 0; i < folders_.size(); ++i) + if(folders_[i].key == folders_[folder].key) + assigned_[i] = theme; +} + +ResolvedTheme FolderThemeModel::Resolve(int folder) const +{ + if(assigned_[folder] >= 0) + return { assigned_[folder], ThemeSource::Assigned, folder }; + return ResolveAutomatic(folder); +} + +ResolvedTheme FolderThemeModel::ResolveAutomatic(int folder) const +{ + const FolderEntry & entry = folders_[folder]; + int nameMatch = ThemeIndex(entry.key == HomeKey && entry.depth == 0 ? HomeNameMatch : entry.key); + if(nameMatch >= 0) + return { nameMatch, ThemeSource::NameMatch, folder }; + + if(entry.parent >= 0) + { + ResolvedTheme up = Resolve(entry.parent); + if(up.source != ThemeSource::Global) + up.source = ThemeSource::Inherited; + return up; + } + return { globalTheme_, ThemeSource::Global, -1 }; +} + +int FolderThemeModel::UsageCount(int theme) const +{ + int count = 0; + for(int assigned : assigned_) + count += assigned == theme ? 1 : 0; + return count; +} + +int FolderThemeModel::ThemeIndex(const std::string & id) const +{ + for(size_t i = 0; i < themes_.size(); ++i) + if(themes_[i].id == id) + return static_cast(i); + return -1; +} + +int FolderThemeModel::FolderIndex(const std::string & key) const +{ + for(size_t i = 0; i < folders_.size(); ++i) + if(folders_[i].key == key) + return static_cast(i); + return -1; +} diff --git a/src/folder_theme_model.h b/src/folder_theme_model.h new file mode 100644 index 0000000..e0e4ea9 --- /dev/null +++ b/src/folder_theme_model.h @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef FOLDER_THEME_MODEL_H_ +#define FOLDER_THEME_MODEL_H_ + +#include +#include + +struct FolderEntry +{ + std::string key; // snake_case of the folder's Name=, as om_themescript derives it + std::string name; + int depth; + int parent; +}; + +struct ThemeEntry +{ + std::string id; + std::string title; + std::string preview; +}; + +enum class ThemeSource { Assigned, NameMatch, Inherited, Global }; + +struct ResolvedTheme +{ + int theme; // -1: stock UI, or a random theme when the randomizer is on + ThemeSource source; + int fromFolder; +}; + +// Folder -> theme assignments, resolved the same way om_themescript applies them on chmenu +class FolderThemeModel +{ +public: + bool Load(const std::string & dataDir, const std::string & mappingPath); + bool Save() const; + + const std::vector & Folders() const { return folders_; } + const std::vector & Themes() const { return themes_; } + bool GlobalIsRandom() const { return globalRandom_; } + + int Assigned(int folder) const { return assigned_[folder]; } + void Assign(int folder, int theme); // -1 clears + ResolvedTheme Resolve(int folder) const; + ResolvedTheme ResolveAutomatic(int folder) const; // what the folder gets without its own assignment + int UsageCount(int theme) const; + +private: + int ThemeIndex(const std::string & id) const; + int FolderIndex(const std::string & key) const; + + std::vector folders_; + std::vector themes_; + std::vector assigned_; + std::vector unknownLines_; // folders or themes not on this console right now, kept on save + std::string mappingPath_; + int globalTheme_ = -1; + bool globalRandom_ = false; +}; + +#endif diff --git a/src/folder_themes_layout.cpp b/src/folder_themes_layout.cpp new file mode 100644 index 0000000..82bd88c --- /dev/null +++ b/src/folder_themes_layout.cpp @@ -0,0 +1,421 @@ +/** + * Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#include "theme_manager_app.h" +#include "folder_theme_model.h" +#include "texture_utils.h" +#include "framework/draw_helpers.h" +#include "framework/powerwatch.h" +#include "localization.h" + +#include +#include +#include +#include + +namespace +{ + const int ListRight = 760; + const int DetailX = 800; + const int DetailW = 384; + const int PreviewH = 216; + const int TreeIndent = 26; + const int RowGlyphSize = 16; + const int ValueMaxW = 250; + const int ChevronGap = 10; + + const SDL_Rect PickerRect{ DetailX - 16, UiTheme::HeaderDividerY + 14, DetailW + 32, UiTheme::FooterDividerY - UiTheme::HeaderDividerY - 28 }; + const int PickerHeaderH = 44; + const int PickerRowH = 40; + const int ThumbW = 60, ThumbH = 34; + + bool ReadParameter(const std::string & name) + { + std::ifstream in("/sys/module/clovercon/parameters/" + name); + int value = 0; + return (in >> value) && value != 0; + } + + // hakchi's "Use X/Y on Classic Controller as autofire A/B" - Y then arrives as B presses + bool PadHasY() + { + return ReadSftype() != "nes" && !ReadParameter("autofire_xy"); + } + + std::string Format(const std::string & key, const std::string & value) + { + std::string text = Translate(key); + size_t at = text.find("%s"); + return at == std::string::npos ? text : text.replace(at, 2, value); + } +} + +int ThemeManagerApp::RunFolderThemesLayout() +{ + FolderThemeModel model; + if(!model.Load(options_.folderThemesDir, options_.folderThemesMapping)) + return 1; + const std::vector & folders = model.Folders(); + const std::vector & themes = model.Themes(); + const int folderCount = static_cast(folders.size()); + const int optionCount = static_cast(themes.size()) + 1; // 0 = Automatic, n = themes[n-1] + const bool hasY = PadHasY(); + + const int rowPitch = std::max(UiTheme::RowPitch, GetTTFLineHeight(RowGlyphSize)); + const int displayRows = std::max(1, (UiTheme::FooterDividerY - UiTheme::ListBottomMargin - UiTheme::RowFirstY) / rowPitch); + const int pickerRows = (PickerRect.h - PickerHeaderH - 8) / PickerRowH; + + Texture chevron(optionsLocation_ + UiTheme::AssetChevronRight, renderer_); + SetColorMod(chevron.texture.get(), UiTheme::Accent); + Texture scrollUp = MakeScrollArrow(UiTheme::ScrollX, UiTheme::ScrollUpY); + Texture scrollDown = scrollUp; + scrollDown.rect.y = UiTheme::ScrollDownY; + + auto MakeText = [&](const std::string & text, int size, Color color) { return Texture(text, size, renderer_, 0, 0, false, ToAbgr(color), true); }; + auto MakeBadge = [&](const char * letter, const std::string & labelKey, Color rim, Color fill) + { + return Badge{ MakeText(letter, 16, UiTheme::BadgeLetter), MakeText(Translate(labelKey), 16, UiTheme::Text), rim, fill }; + }; + const Badge listBadgeA = MakeBadge("A", "FOLDER_THEME_LIST", UiTheme::BadgeADark, UiTheme::BadgeA); + const Badge pickBadgeA = MakeBadge("A", "HINT_SELECT", UiTheme::BadgeADark, UiTheme::BadgeA); + Badge backBadge = MakeBadge("B", "BACK", UiTheme::BadgeBDark, UiTheme::BadgeB); + Badge cancelBadge = MakeBadge("B", "FOLDER_THEME_CANCEL", UiTheme::BadgeBDark, UiTheme::BadgeB); + Badge autoBadge = MakeBadge("Y", "AUTOMATIC", UiTheme::BadgeYDark, UiTheme::BadgeY); + Texture changeLabel = MakeText(Translate("FOLDER_THEME_CHANGE"), 16, UiTheme::Text); + + auto ThemeName = [&](int theme) + { + if(theme >= 0) + return themes[theme].title; + return Translate(model.GlobalIsRandom() ? "FOLDER_THEME_RANDOM" : "FOLDER_THEME_STOCK"); + }; + auto AutomaticLabel = [&](int folder) { return Translate("AUTOMATIC") + " · " + ThemeName(model.ResolveAutomatic(folder).theme); }; + + std::vector previews(themes.size()); + std::vector previewLoaded(themes.size(), 0); // not vector: Preview() needs a real reference + Texture fallbackPreview; + char fallbackLoaded = 0; + auto Preview = [&](int theme) -> Texture & + { + char & loaded = theme >= 0 ? previewLoaded[theme] : fallbackLoaded; + Texture & slot = theme >= 0 ? previews[theme] : fallbackPreview; + if(!loaded) + { + loaded = 1; + std::string path = theme >= 0 ? themes[theme].preview : ""; + if(path.empty() || !std::ifstream(path).good()) + path = optionsLocation_ + (theme < 0 && model.GlobalIsRandom() ? "images/randtheme.png" : "images/default_preview.png"); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); + slot = Texture(path, renderer_, 0, 0); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + } + return slot; + }; + auto DrawCover = [&](Texture art, const SDL_Rect & box, int radius, Color around) + { + DrawRoundedFillRect(renderer_, box, UiTheme::SelectedRowBg, radius); + if(art.rect.w <= 0) + return; + SDL_RenderSetClipRect(renderer_, &box); + FitCover(art, box.x, box.y, box.w, box.h, 0); + art.Draw(renderer_); + SDL_RenderSetClipRect(renderer_, nullptr); + DrawRoundedCornerMask(renderer_, box, around, radius); + }; + + std::vector nameTex(folders.size()), valueTex(folders.size()); + auto RebuildRows = [&]() + { + for(int i = 0; i < folderCount; ++i) + { + bool own = model.Assigned(i) >= 0; + std::string value = own ? themes[model.Assigned(i)].title : AutomaticLabel(i); + valueTex[i] = MakeText(TruncateToWidth(value, RowGlyphSize, ValueMaxW), RowGlyphSize, own ? UiTheme::Text : UiTheme::TextDim); + int textX = UiTheme::RowTextX + folders[i].depth * TreeIndent + (folders[i].depth > 0 ? 14 : 0); + int nameAvail = ListRight - 16 - valueTex[i].rect.w - 2 * (chevron.rect.w + ChevronGap) - 16 - textX; + nameTex[i] = MakeText(TruncateToWidth(folders[i].name, RowGlyphSize, nameAvail), RowGlyphSize, UiTheme::Text); + nameTex[i].rect.x = textX; + } + }; + + int selected = 0, topRow = 0; + Texture detailName, detailTag, detailSource, detailUsage; + Color detailTagFill{}; + int detailTheme = -1; + auto RebuildDetail = [&]() + { + ResolvedTheme resolved = model.Resolve(selected); + detailTheme = resolved.theme; + detailName = MakeText(TruncateToWidth(ThemeName(resolved.theme), 22, DetailW), 22, UiTheme::Text); + + std::string tagKey = "FOLDER_THEME_TAG_GLOBAL", sourceText = Translate("FOLDER_THEME_SOURCE_GLOBAL"); + Color tagText = UiTheme::Text; + detailTagFill = UiTheme::Border; + switch(resolved.source) + { + case ThemeSource::Assigned: + tagKey = "FOLDER_THEME_TAG_ASSIGNED"; + sourceText = Translate("FOLDER_THEME_SOURCE_ASSIGNED"); + detailTagFill = UiTheme::Accent; + tagText = UiTheme::Bg; + break; + case ThemeSource::NameMatch: + tagKey = "FOLDER_THEME_TAG_NAME"; + sourceText = Translate("FOLDER_THEME_SOURCE_NAME"); + break; + case ThemeSource::Inherited: + tagKey = "FOLDER_THEME_TAG_INHERITED"; + sourceText = Format("FOLDER_THEME_SOURCE_INHERITED", folders[resolved.fromFolder].name); + detailTagFill = UiTheme::BadgeXDark; + break; + case ThemeSource::Global: + break; + } + detailTag = MakeText(Translate(tagKey), 13, tagText); + int tagW = detailTag.rect.w + 16; + detailSource = MakeText(TruncateToWidth(sourceText, 16, DetailW - tagW - 10), 16, UiTheme::Text); + + int usage = resolved.theme >= 0 ? model.UsageCount(resolved.theme) : 0; + std::string usageText = usage > 1 ? Format("FOLDER_THEME_USED_IN", std::to_string(usage)) + : Translate(usage == 1 ? "FOLDER_THEME_USED_ONCE" : "FOLDER_THEME_UNUSED"); + detailUsage = MakeText(usageText, 14, UiTheme::TextDim); + }; + + auto Select = [&](int folder) + { + selected = folder; + if(selected < topRow) + topRow = selected; + else if(selected >= topRow + displayRows) + topRow = selected - displayRows + 1; + RebuildDetail(); + }; + auto Assign = [&](int theme) + { + if(model.Assigned(selected) == theme) + return; + model.Assign(selected, theme); + model.Save(); + RebuildRows(); + RebuildDetail(); + }; + auto Cycle = [&](int step) + { + int option = model.Assigned(selected) + 1; + Assign((option + step + optionCount) % optionCount - 1); + }; + + bool picking = false; + int pick = 0, pickTop = 0; + Texture pickerTitle; + std::vector optionTex; + auto OpenPicker = [&]() + { + picking = true; + pick = model.Assigned(selected) + 1; + pickTop = std::max(0, std::min(pick - pickerRows / 2, optionCount - pickerRows)); + pickerTitle = MakeText(TruncateToWidth(Format("FOLDER_THEME_FOR", folders[selected].name), 16, PickerRect.w - 32), 16, UiTheme::Text); + optionTex.clear(); + int labelW = PickerRect.w - 24 - ThumbW - 12 - 24; + optionTex.push_back(MakeText(TruncateToWidth(AutomaticLabel(selected), 16, labelW), 16, UiTheme::Text)); + for(const ThemeEntry & theme : themes) + optionTex.push_back(MakeText(TruncateToWidth(theme.title, 16, labelW), 16, UiTheme::Text)); + }; + auto MovePick = [&](int to) + { + pick = std::max(0, std::min(optionCount - 1, to)); + if(pick < pickTop) + pickTop = pick; + else if(pick >= pickTop + pickerRows) + pickTop = pick - pickerRows + 1; + }; + + RebuildRows(); + Select(0); + + const bool hasBack = pinnedStartIndex_ < static_cast(commands_.size()); + for(;;) + { + FrameEvent frameEvent = PollFrameEvents(); + if(frameEvent == FrameEvent::Quit) + return 0; + if(frameEvent == FrameEvent::PowerButtonPressed) + { + ResumeUnderlyingUi(); + break; + } + + if(picking) + { + if(controller_->GetButtonStatus(A) || controller_->GetButtonStatus(START)) + { + Assign(pick - 1); + picking = false; + } + else if(controller_->GetButtonStatus(B)) + picking = false; + else if(controller_->HeldRepeat(UP)) + MovePick(pick - 1); + else if(controller_->HeldRepeat(DOWN)) + MovePick(pick + 1); + } + else + { + if(controller_->GetButtonStatus(A) || controller_->GetButtonStatus(START)) + OpenPicker(); + else if(controller_->GetButtonStatus(B)) + { + if(hasBack && ActivateCommand(commands_[pinnedStartIndex_])) + break; + } + else if(hasY && controller_->GetButtonStatus(Y)) + Assign(-1); + else if(controller_->HeldRepeat(UP)) + Select((selected - 1 + folderCount) % folderCount); + else if(controller_->HeldRepeat(DOWN)) + Select((selected + 1) % folderCount); + else if(controller_->HeldRepeat(LEFT)) + Cycle(-1); + else if(controller_->HeldRepeat(RIGHT)) + Cycle(1); + } + + badgeA_ = picking ? pickBadgeA : listBadgeA; + DrawChromeCommon(); + DrawSectionTitle(); + { + int rightEdge = UiTheme::BadgeClusterRightX - UiTheme::BadgeOuterSize - UiTheme::BadgeLabelGap - badgeA_.label.rect.w - UiTheme::BadgeGroupGap; + if(picking) + DrawBadge(cancelBadge, rightEdge); + else + { + if(hasBack) + rightEdge = DrawBadge(backBadge, rightEdge); + if(hasY) + rightEdge = DrawBadge(autoBadge, rightEdge); + int groupW = 2 * chevron.rect.w + 4 + UiTheme::BadgeLabelGap + changeLabel.rect.w; + int x = rightEdge - groupW; + int midY = UiTheme::BadgeBandY + UiTheme::BadgeOuterSize / 2; + chevron.rect.x = x; + chevron.rect.y = midY - chevron.rect.h / 2; + chevron.Draw(renderer_, SDL_FLIP_HORIZONTAL); + chevron.rect.x = x + chevron.rect.w + 4; + chevron.Draw(renderer_); + changeLabel.rect.x = chevron.rect.x + chevron.rect.w + UiTheme::BadgeLabelGap; + changeLabel.rect.y = midY - changeLabel.rect.h / 2; + changeLabel.Draw(renderer_); + } + } + + for(int row = 0; row < displayRows && topRow + row < folderCount; ++row) + { + int i = topRow + row; + int y = UiTheme::RowFirstY + row * rowPitch; + bool isSelected = i == selected; + SDL_Rect rowRect{ UiTheme::ListX, y - 2, ListRight - UiTheme::ListX, rowPitch - 2 }; + if(isSelected) + { + DrawRoundedFillRect(renderer_, rowRect, UiTheme::SelectedRowBg, UiTheme::BoxRadius); + DrawStrokeRect(renderer_, rowRect, UiTheme::Accent, 2, UiTheme::BoxRadius); + } + else if(i + 1 < folderCount && row + 1 < displayRows) + DrawHLine(renderer_, UiTheme::ListX, ListRight, y + rowPitch - 3, UiTheme::Border); + + int midY = y - 2 + (rowPitch - 2) / 2; + if(folders[i].depth > 0) + { + int twigX = nameTex[i].rect.x - 14; + DrawVLine(renderer_, twigX, y + 6, midY, UiTheme::Border, 2); + DrawHLine(renderer_, twigX, twigX + 9, midY, UiTheme::Border, 2); + } + nameTex[i].rect.y = midY - nameTex[i].rect.h / 2; + nameTex[i].Draw(renderer_); + + int valueRight = ListRight - 16; + if(isSelected && !picking) + { + chevron.rect.x = valueRight - chevron.rect.w; + chevron.rect.y = midY - chevron.rect.h / 2; + chevron.Draw(renderer_); + valueRight = chevron.rect.x - ChevronGap; + } + valueTex[i].rect.x = valueRight - valueTex[i].rect.w; + valueTex[i].rect.y = midY - valueTex[i].rect.h / 2; + valueTex[i].Draw(renderer_); + if(isSelected && !picking) + { + chevron.rect.x = valueTex[i].rect.x - ChevronGap - chevron.rect.w; + chevron.Draw(renderer_, SDL_FLIP_HORIZONTAL); + } + } + if(topRow > 0) + scrollUp.Draw(renderer_); + if(topRow + displayRows < folderCount) + scrollDown.Draw(renderer_, SDL_FLIP_VERTICAL); + + { + SDL_Rect previewBox{ DetailX, UiTheme::RowFirstY, DetailW, PreviewH }; + DrawCover(Preview(detailTheme), previewBox, UiTheme::BoxRadius, UiTheme::Bg); + DrawStrokeRect(renderer_, previewBox, UiTheme::Border, UiTheme::BorderWidth, UiTheme::BoxRadius); + int y = previewBox.y + PreviewH + 16; + detailName.rect.x = DetailX; + detailName.rect.y = y; + detailName.Draw(renderer_); + y += detailName.rect.h + 12; + SDL_Rect tagRect{ DetailX, y, detailTag.rect.w + 16, detailTag.rect.h + 8 }; + DrawRoundedFillRect(renderer_, tagRect, detailTagFill, 6); + detailTag.rect.x = tagRect.x + 8; + detailTag.rect.y = tagRect.y + 4; + detailTag.Draw(renderer_); + detailSource.rect.x = tagRect.x + tagRect.w + 10; + detailSource.rect.y = tagRect.y + (tagRect.h - detailSource.rect.h) / 2; + detailSource.Draw(renderer_); + detailUsage.rect.x = DetailX; + detailUsage.rect.y = tagRect.y + tagRect.h + 12; + detailUsage.Draw(renderer_); + } + + if(picking) + { + DrawRoundedFillRect(renderer_, PickerRect, UiTheme::Bg, UiTheme::BoxRadius); + DrawStrokeRect(renderer_, PickerRect, UiTheme::Accent, 2, UiTheme::BoxRadius); + pickerTitle.rect.x = PickerRect.x + 16; + pickerTitle.rect.y = PickerRect.y + (PickerHeaderH - pickerTitle.rect.h) / 2; + pickerTitle.Draw(renderer_); + + int current = model.Assigned(selected) + 1; + for(int row = 0; row < pickerRows && pickTop + row < optionCount; ++row) + { + int option = pickTop + row; + SDL_Rect rowRect{ PickerRect.x + 8, PickerRect.y + PickerHeaderH + row * PickerRowH, PickerRect.w - 16, PickerRowH - 4 }; + if(option == pick) + { + DrawRoundedFillRect(renderer_, rowRect, UiTheme::SelectedRowBg, UiTheme::BoxRadius); + DrawStrokeRect(renderer_, rowRect, UiTheme::Accent, 2, UiTheme::BoxRadius); + } + SDL_Rect thumb{ rowRect.x + 8, rowRect.y + (rowRect.h - ThumbH) / 2, ThumbW, ThumbH }; + if(option == 0) + DrawStrokeRect(renderer_, thumb, UiTheme::Border, 2, 4); + else + DrawCover(Preview(option - 1), thumb, 4, option == pick ? UiTheme::SelectedRowBg : UiTheme::Bg); + Texture & label = optionTex[option]; + label.rect.x = thumb.x + ThumbW + 12; + label.rect.y = rowRect.y + (rowRect.h - label.rect.h) / 2; + label.Draw(renderer_); + if(option == current) + DrawRoundedFillRect(renderer_, { rowRect.x + rowRect.w - 22, rowRect.y + rowRect.h / 2 - 5, 10, 10 }, UiTheme::BadgeA, 5); + } + } + + SetDrawColor(renderer_, bg_); + sdlContext_->EndFrame(); + } + + return 0; +} diff --git a/src/theme_manager_app.h b/src/theme_manager_app.h index 75d67dc..530c2e2 100644 --- a/src/theme_manager_app.h +++ b/src/theme_manager_app.h @@ -25,7 +25,12 @@ class ThemeManagerApp public: ThemeManagerApp(std::string optionsLocation, AppOptions options, std::vector commands, std::vector isThemeItem); - int Run() { return options_.gridLayout ? RunGridLayout() : RunListLayout(); } + int Run() + { + if(!options_.folderThemesDir.empty()) + return RunFolderThemesLayout(); + return options_.gridLayout ? RunGridLayout() : RunListLayout(); + } private: struct Badge { Texture letter; Texture label; Color rim; Color fill; }; @@ -76,6 +81,7 @@ class ThemeManagerApp int RunGridLayout(); int RunListLayout(); + int RunFolderThemesLayout(); // folder_themes_layout.cpp }; #endif From 262214e1f531b676a593275ca9352c4991dcbd1f Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 6 Oct 2026 13:19:51 +0000 Subject: [PATCH 109/210] Update OptionsMenu submodule to commit b673707 --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 7609718..b673707 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 7609718a6a52812ecf1d2774892f20cf55296f2c +Subproject commit b673707de0e20a1d93bc21a6f882982bc260e8f7 From 98af94a11f1a92784e0f256361e37cf013e5c95d Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 6 Oct 2026 15:10:57 +0000 Subject: [PATCH 110/210] Add Music Per Folder and regroup the theme and audio settings Music Per Folder works like Theme Per Folder: "Assign Music to Folder" opens the folder tree in a music view (--folderChannel music), and assignments go to folder_music as "=". A folder's own music, or its parent's, plays instead of the theme's music and wins over the Audio Randomizer; folders on Automatic keep the old behaviour. The toggle only shows up when music_menu has .wav files. install keeps folder_music across upgrades and uninstall removes it. Settings are now grouped: theme options first, then a separator, then the audio ones, each "Assign ... to Folder" row under its own toggle. Adds Bootstrap icons for both music rows (generated by OptionsMenu's gen_ui_assets.py). Also fixes: - music_menu is taken from USB only when it has .wav files, so an empty USB folder no longer hides the console's own music - musicHackUnbind now removes every stacked bgm bind, not just the top one, so a leftover theme or random track no longer leaks into the next folder - leaving the theme manager (Back to Options Menu, or power button) clears each screen's remembered focus --- .../images/preview_assign_music.png | Bin 0 -> 4107 bytes .../images/preview_music_per_folder.png | Bin 0 -> 3773 bytes .../options_menu/scripts/om_theme_selector | 2 +- mod/etc/options_menu/themes/lang/de-DE.lang | 13 ++ mod/etc/options_menu/themes/lang/en-US.lang | 13 ++ mod/etc/options_menu/themes/lang/es-ES.lang | 13 ++ mod/etc/options_menu/themes/lang/fr-FR.lang | 13 ++ mod/etc/options_menu/themes/lang/it-IT.lang | 13 ++ mod/etc/options_menu/themes/lang/ja-JP.lang | 13 ++ mod/etc/options_menu/themes/lang/nl-NL.lang | 13 ++ mod/etc/options_menu/themes/lang/pt-PT.lang | 13 ++ mod/etc/options_menu/themes/lang/ru-RU.lang | 13 ++ .../themes/scripts/om_themescript | 50 +++++- mod/etc/options_menu/themes/scripts/om_vars | 63 ++++++-- .../themes/settings/commands/_musicperfolder | 6 + .../settings/commands/_musicperfolder_assign | 6 + ...rfolder_assign => _themeperfolder_1assign} | 2 +- .../themes/settings/commands/c0000_zseparator | 0 .../themes/settings/scripts/om_folderthemes | 2 +- .../themes/settings/scripts/om_state | 1 + mod/install | 2 +- mod/readme.md | 10 +- mod/uninstall | 1 + src/command_loader.cpp | 6 +- src/command_loader.h | 9 +- src/folder_theme_model.cpp | 152 ++++++++++++------ src/folder_theme_model.h | 61 ++++--- src/folder_themes_layout.cpp | 115 ++++++++----- src/theme_manager_app.cpp | 4 +- src/theme_manager_app.h | 2 +- 30 files changed, 467 insertions(+), 144 deletions(-) create mode 100644 mod/etc/options_menu/images/preview_assign_music.png create mode 100644 mod/etc/options_menu/images/preview_music_per_folder.png create mode 100644 mod/etc/options_menu/themes/settings/commands/_musicperfolder create mode 100644 mod/etc/options_menu/themes/settings/commands/_musicperfolder_assign rename mod/etc/options_menu/themes/settings/commands/{_themeperfolder_assign => _themeperfolder_1assign} (66%) create mode 100644 mod/etc/options_menu/themes/settings/commands/c0000_zseparator diff --git a/mod/etc/options_menu/images/preview_assign_music.png b/mod/etc/options_menu/images/preview_assign_music.png new file mode 100644 index 0000000000000000000000000000000000000000..d24504b405371e559ddb1394c2106f4a114eb4c0 GIT binary patch literal 4107 zcmb`K_cz-Q)W<)uS1U0?wN`Z4vqBLg_8u*1Zy! zLgLyg05Fg0BGoLOXKZI;%k1v6cYis!QNVUblfj)QK_C~*b|s44jZKbQ?v+5;?HJ0i zD071YUN_df&d{h3tGL+QA{-o9j8j#0AEsCacjcPtr7{%Y8iIa{*H$c4%yhJ_&j&dC zZtge_?(O~6Og8xaEX1w6^>E&U>&M~_4Utrq7<3op=myNFusTOX-EAEwHCX(>%0BfD z_$o-p4|8ff$pi6FEEWSfPt_iz)!~z_>RI$vXuhI`90Z_F={Grc5tcYBfJa;h&|Iia1plmZN`%swS}CyA^E^}*_nXn2Dd;{j6?>RX0%N< z%z)vu=1wr7*?L4PF?J4SO+Qmm?sXlUui9TmX0tYP?2d#p7*(MfHPbnH(>s z)K!bUed#osEK{fLmaOYz=ozKn4D0J%-_NF`+uRHLCr%Gjw{PbiUk68yE}mBcJMp{T zjVRW?^;t}gl(;XZhBY^GZ|Ba9!8=pFApj)~qRUNqE_6)yF6g=AFtczcKrinR)4gn( zg9qza#YWK@qs5cKLNcjO6RC2lukxR3bN`S;G4Z=6s74g2Q9yEO=#f#}F!299kro}; z^*o_(&2^@s((I<6lng9k52C@M){kDvbl;1f(BTuLxb_d334Ly`YBkSHlu3;^asm)i zwf$pKEV|^&GLRJgny;Zr*MRE;1{fDYv#1w=4{Z1{oC5DtvOdrYK2I@%FMrPP8yC9=Xt?>lN*#D#A+o!~)A8#_-v6I3a<%>F1I?3}9r{UD{B zuAJWSz7xUn(QjTkjVhj)Y`W5SHn0)yNbKum*qcfUc;<2xjdE8|_}7$GyqEN5UR^Fq ziiudglr5_7vPKfjpL@}C^MOX<%lXt2O z_r?$3w?AtG_urhK z0bU>W#5e^N)(h^PNb7y-jh4xWhsJ`_MXo5ub@oscNvt|K z8R;iNk8xDeEmi~TfsH4xR>dE+VlSX|65=mhO1#dRrh;|&)W4f#;N_5}qOE7zxLhh} z^-ZGD#l7UDr0ovUc z0SH#2rIiL+J?LF~Gk0#_r=yF5IV<;mnpqx1&gMU(3WS4^Z+_UlI~}_A98NRKC0mW~ z^*rCWMvlqU=f5aZH81`MBEu)nUjT)ji_Q;jIE6N6Q#w+gaG9e}5rnsNx{|-(>rMRc z*nJHH91;#@V)CaPUUo)x^N7$bJj5{jQmNRMXl@~RlIim%JAJzSl1$#`&5y4TEpT-|HoLxmpNwdm1D+L5X-P1UgWNV5C0X=W+SBN2Zz8 zI5?Mt_4uZzI==fiJ}}-dtZD1GVE^gqXWJL@EHc#&{2|S38%6=vy>O?yzu{-i+4 z3yrz;-m0M-8j4Qh;FNJ^J^5%e$sf%>x|QLLVJ0DA*a5ZkmV2dRT5OMuRkSUFtSR}_ zE7NPn5bxuBJ$=cO6!1j#>|(eXWu?gEhm@YcmYz95rqiTRb)qI6EK+pt?9tz8a@J2F z<7Z?`z-n?J>bbvxQimVVlI+5^Jy8&0QbAqCiYp!1(rat|ObCo{`;yUQFC!En(TtM3 z+rHAawUexAU-1^hn>?y(9Jh#P>%p{f1y0|sy@4%T-wau49&o{d+k_kKO^K4y z91tq_w?man*G@NEzAMd3JkU@P5*e5fd&IN4Uls^m50F7U3G3OpMqZO)Gyja<5~Ex2 zU#gWr7y319s+lb;?iy_>)Nrq#)M{McvFP)$lK&|bXXlyQ#rU;8<`wn{vkWdNK~Q96 zeZVuXS^LH4zI5W#<3PBjdT?!i@V6jVqmL*v*jgF04PyAmPW zwPxNSL(@6}P3rupCFSKIOyspc60G)w%Dlw)v~oY4ZtK{KgZe1lWwpuNxY-o1GS+^Y z&f%olZO;dr-yLa0bVdyAKT-~yUh~Uc_~W(1xac_n&(?jdC^z9DB0d0_CI6u@t}r8w zC`x?o#2mJAMWG3Qm^5SYv8{Oa;Q{(Y+wGk_j$qcaQ~R2<2#+UkQs?Hql(iqMzeI_a z7r^~Dmb#t^l7HP8GW=qezW>LXf16Hz^GYI0s1qWv)YyM?eO7Gce0a;=#TY_STt*xH zgDX38`#=#{X||$Jtqu8f{Hl;{iIpAxqvkD*Se;41WZxXxmtsI{iAZ zDYbd1u(|%7`C8$Ry7){zcloZrk*>rNuhBp4Gp`L|*}y+HL-0#MZeVry=^* z`Y|T`*AwN!4!+f=ch2HfDSkOPxFt1fKdMWvt{#*XO_T7U==jcDr<|s>k$?1K{XYD2y2z01C1IQi4P;s;msf8bwk)F9@lRdT zc`WDbWd<4b>qw*rW;~qF_^+bLac;M1=<~mf)c9yj`20PRh(#7OS!K>9!Pg3GDc?pM z=pSl~vP^sFdS8IsM=*!ATrnNkp+>Un^nRQ&iw^J7Kh4;Zi^S_2-utDpoFWJGCAHXq zMJXxw)_Zmh8?R?I1B#Mq(r%*chaQf1*~JKcne5tQI?e2EyMXBM`F-b&&M?RLt4BC1 zAU2K8&r1DTmuuqyajM-ut7~SxOD6@M1x}Ok?fU`F*$_hNHZz+H7yyuo`u5LOFwPvtQ75oH(Y~zdSO5CCteyu zV=X{dS7KbmaPb9q zPH)pSS@|)s*2vvD?wWxgbG&~nJ+NLukJ`TQV?ncPOPb{kJ`11UoCVT$v zcts`6mjaO*r8T_Y@Z+62>h+l=FrOv&Z=36WpQTcpSb4O>9h8PT1(#YQNgny+DyLFG z8vYK}wld#j7gmXlP0dVa8P8FLC5&+P9+Bdo`F*%wJ%;WQzB8MF0bkr8;$2W))&cSA ziK}Z zC0(&^38N?6+X<@OFIR&*H>IxfXjcC}qxS#edJZZ)?1b=NvHClt;4gs)$Wg)#8p-T* zaKSR^R)b6_2*FbC#hD@f`uCFH+JF~cw;|lMU6&{PK_K|Mrx)yrk%{>)d=H)ayOI>4doy0 zM?$eyv_i38L1ncp6Dt8uus@Q&sp|x^n@-M)!$yE?o2a%VcL~CYI&h2PQtpulw%V&; ztQJOF7lRb!W&qwbM>gd8vJ%*2>ul>7xwNVr3FiPkN8o}$w(gkZ{pMHoCZl`6hiaZl zEFj+Rp@mjA<<*R38IWpor*;a67tpvRsmk6JVYE`!6Cwg}qqROBo^xPceApd+0WP8N zVZ`0D!0N;*?zBOP^P<5oZueX~pn%KhO}p|hXj_M(t!I`0n`z}Do|g&MB(4aN_mtuY zcvCc}t2$*Ht~X)#5F;Y8!oNM2ZvcbzGO#{{3fjhlhM6e)yI2Hu>c~*&m)8;}_kjoL z)1PL;OR{i^iD-oO?_gsf9&_Y)phl$?CHoX|hCjeeY`MvPDXKfjxB#U6lHR1+NqoN) P`GBsb5wb$vG3T>|I)k+ondT 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 iB&AOw`5))kUjkA~^3{{KI%UOIQrfI literal 0 HcmV?d00001 diff --git a/mod/etc/options_menu/scripts/om_theme_selector b/mod/etc/options_menu/scripts/om_theme_selector index 0512aed..693d4be 100644 --- a/mod/etc/options_menu/scripts/om_theme_selector +++ b/mod/etc/options_menu/scripts/om_theme_selector @@ -24,7 +24,7 @@ hasThemesToInstall enableMenuOptions remove "$omDummy" -[ -f "$om_firstrun" ] && remove "$om_themerandomizer" "$om_themeperfolder" "$om_audiorandomizer" "$om_advmusichack" +[ -f "$om_firstrun" ] && remove "$om_themerandomizer" "$om_themeperfolder" "$om_musicperfolder" "$om_audiorandomizer" "$om_advmusichack" if [ "$1" = "reload" ] || [ -f "$om_firstrun" ] ; then loadThemeList -r diff --git a/mod/etc/options_menu/themes/lang/de-DE.lang b/mod/etc/options_menu/themes/lang/de-DE.lang index 61a7860..001ace8 100644 --- a/mod/etc/options_menu/themes/lang/de-DE.lang +++ b/mod/etc/options_menu/themes/lang/de-DE.lang @@ -24,6 +24,9 @@ CLEAR_LIST=Liste leeren DOWNLOAD_THEMES=Themes herunterladen THEME_PER_FOLDER=Theme pro Ordner ASSIGN_FOLDER_THEMES=Theme einem Ordner zuweisen +MUSIC_PER_FOLDER=Musik pro Ordner +ASSIGN_FOLDER_MUSIC=Musik einem Ordner zuweisen +FOLDER_MUSIC=Ordner-Musik FOLDER_THEMES=Ordner-Themes HOME_FOLDER=Home-Menü AUTOMATIC=Automatisch @@ -44,6 +47,16 @@ FOLDER_THEME_USED_IN=In %s Ordnern verwendet FOLDER_THEME_USED_ONCE=In 1 Ordner verwendet FOLDER_THEME_UNUSED=Keinem Ordner zugewiesen FOLDER_THEME_FOR=Theme für %s +FOLDER_MUSIC_THEME=Theme-Musik +FOLDER_MUSIC_RANDOM=Zufällige Musik +FOLDER_MUSIC_ORIGINAL=Originalmusik +FOLDER_MUSIC_TAG_THEME=THEME +FOLDER_MUSIC_TAG_RANDOM=ZUFALL +FOLDER_MUSIC_TAG_ORIGINAL=ORIGINAL +FOLDER_MUSIC_SOURCE_THEME=Die Musik des Themes +FOLDER_MUSIC_SOURCE_RANDOM=Vom Audio-Zufallsgenerator +FOLDER_MUSIC_SOURCE_ORIGINAL=Die Originalmusik der Konsole +FOLDER_MUSIC_FOR=Musik für %s THEME_RANDOMIZER=Zufälliges Theme AUDIO_RANDOMIZER_HOME=Zufalls-Audio: Home AUDIO_RANDOMIZER_ALL_FOLDERS=Alle Ordner (nur Themes ohne JEGLICHE Audiodateien) diff --git a/mod/etc/options_menu/themes/lang/en-US.lang b/mod/etc/options_menu/themes/lang/en-US.lang index d8715ae..46ee6a8 100644 --- a/mod/etc/options_menu/themes/lang/en-US.lang +++ b/mod/etc/options_menu/themes/lang/en-US.lang @@ -24,6 +24,9 @@ CLEAR_LIST=Clear List DOWNLOAD_THEMES=Download Themes THEME_PER_FOLDER=Theme Per Folder ASSIGN_FOLDER_THEMES=Assign Theme to Folder +MUSIC_PER_FOLDER=Music Per Folder +ASSIGN_FOLDER_MUSIC=Assign Music to Folder +FOLDER_MUSIC=Folder Music FOLDER_THEMES=Folder Themes HOME_FOLDER=Home AUTOMATIC=Automatic @@ -44,6 +47,16 @@ FOLDER_THEME_USED_IN=Used in %s folders FOLDER_THEME_USED_ONCE=Used in 1 folder FOLDER_THEME_UNUSED=Not assigned to any folder FOLDER_THEME_FOR=Theme for %s +FOLDER_MUSIC_THEME=Theme music +FOLDER_MUSIC_RANDOM=Random music +FOLDER_MUSIC_ORIGINAL=Original music +FOLDER_MUSIC_TAG_THEME=THEME +FOLDER_MUSIC_TAG_RANDOM=RANDOM +FOLDER_MUSIC_TAG_ORIGINAL=ORIGINAL +FOLDER_MUSIC_SOURCE_THEME=The theme's own music +FOLDER_MUSIC_SOURCE_RANDOM=From the Audio Randomizer +FOLDER_MUSIC_SOURCE_ORIGINAL=The console's original music +FOLDER_MUSIC_FOR=Music for %s THEME_RANDOMIZER=Theme Randomizer AUDIO_RANDOMIZER_HOME=Audio Randomizer: Home AUDIO_RANDOMIZER_ALL_FOLDERS=All Folders (Only Themes Without ANY Audio Files) diff --git a/mod/etc/options_menu/themes/lang/es-ES.lang b/mod/etc/options_menu/themes/lang/es-ES.lang index be6b10f..8383698 100644 --- a/mod/etc/options_menu/themes/lang/es-ES.lang +++ b/mod/etc/options_menu/themes/lang/es-ES.lang @@ -24,6 +24,9 @@ CLEAR_LIST=Vaciar lista DOWNLOAD_THEMES=Descargar temas THEME_PER_FOLDER=Tema por carpeta ASSIGN_FOLDER_THEMES=Asignar tema a carpeta +MUSIC_PER_FOLDER=Música por carpeta +ASSIGN_FOLDER_MUSIC=Asignar música a carpeta +FOLDER_MUSIC=Música por carpeta FOLDER_THEMES=Temas por carpeta HOME_FOLDER=Inicio AUTOMATIC=Automático @@ -44,6 +47,16 @@ FOLDER_THEME_USED_IN=Usado en %s carpetas FOLDER_THEME_USED_ONCE=Usado en 1 carpeta FOLDER_THEME_UNUSED=Sin carpetas asignadas FOLDER_THEME_FOR=Tema para %s +FOLDER_MUSIC_THEME=Música del tema +FOLDER_MUSIC_RANDOM=Música aleatoria +FOLDER_MUSIC_ORIGINAL=Música original +FOLDER_MUSIC_TAG_THEME=TEMA +FOLDER_MUSIC_TAG_RANDOM=ALEATORIA +FOLDER_MUSIC_TAG_ORIGINAL=ORIGINAL +FOLDER_MUSIC_SOURCE_THEME=La música propia del tema +FOLDER_MUSIC_SOURCE_RANDOM=Del aleatorizador de audio +FOLDER_MUSIC_SOURCE_ORIGINAL=La música original de la consola +FOLDER_MUSIC_FOR=Música para %s THEME_RANDOMIZER=Tema aleatorio AUDIO_RANDOMIZER_HOME=Audio aleatorio: Inicio AUDIO_RANDOMIZER_ALL_FOLDERS=Todas las carpetas (solo temas sin NINGÚN archivo de audio) diff --git a/mod/etc/options_menu/themes/lang/fr-FR.lang b/mod/etc/options_menu/themes/lang/fr-FR.lang index 3339435..a05faf7 100644 --- a/mod/etc/options_menu/themes/lang/fr-FR.lang +++ b/mod/etc/options_menu/themes/lang/fr-FR.lang @@ -24,6 +24,9 @@ CLEAR_LIST=Vider la liste DOWNLOAD_THEMES=Télécharger des thèmes THEME_PER_FOLDER=Thème par dossier ASSIGN_FOLDER_THEMES=Attribuer un thème au dossier +MUSIC_PER_FOLDER=Musique par dossier +ASSIGN_FOLDER_MUSIC=Attribuer une musique au dossier +FOLDER_MUSIC=Musique des dossiers FOLDER_THEMES=Thèmes des dossiers HOME_FOLDER=Accueil AUTOMATIC=Automatique @@ -44,6 +47,16 @@ FOLDER_THEME_USED_IN=Utilisé dans %s dossiers FOLDER_THEME_USED_ONCE=Utilisé dans 1 dossier FOLDER_THEME_UNUSED=Attribué à aucun dossier FOLDER_THEME_FOR=Thème pour %s +FOLDER_MUSIC_THEME=Musique du thème +FOLDER_MUSIC_RANDOM=Musique aléatoire +FOLDER_MUSIC_ORIGINAL=Musique d'origine +FOLDER_MUSIC_TAG_THEME=THÈME +FOLDER_MUSIC_TAG_RANDOM=ALÉATOIRE +FOLDER_MUSIC_TAG_ORIGINAL=ORIGINE +FOLDER_MUSIC_SOURCE_THEME=La musique du thème +FOLDER_MUSIC_SOURCE_RANDOM=Du randomiseur audio +FOLDER_MUSIC_SOURCE_ORIGINAL=La musique d'origine de la console +FOLDER_MUSIC_FOR=Musique pour %s THEME_RANDOMIZER=Thème aléatoire AUDIO_RANDOMIZER_HOME=Audio aléatoire : Accueil AUDIO_RANDOMIZER_ALL_FOLDERS=Tous les dossiers (thèmes sans AUCUN fichier audio) diff --git a/mod/etc/options_menu/themes/lang/it-IT.lang b/mod/etc/options_menu/themes/lang/it-IT.lang index 0362c7f..b67b27a 100644 --- a/mod/etc/options_menu/themes/lang/it-IT.lang +++ b/mod/etc/options_menu/themes/lang/it-IT.lang @@ -24,6 +24,9 @@ CLEAR_LIST=Svuota elenco DOWNLOAD_THEMES=Scarica temi THEME_PER_FOLDER=Tema per cartella ASSIGN_FOLDER_THEMES=Assegna tema alla cartella +MUSIC_PER_FOLDER=Musica per cartella +ASSIGN_FOLDER_MUSIC=Assegna musica alla cartella +FOLDER_MUSIC=Musica delle cartelle FOLDER_THEMES=Temi delle cartelle HOME_FOLDER=Home AUTOMATIC=Automatico @@ -44,6 +47,16 @@ FOLDER_THEME_USED_IN=Usato in %s cartelle FOLDER_THEME_USED_ONCE=Usato in 1 cartella FOLDER_THEME_UNUSED=Non assegnato a nessuna cartella FOLDER_THEME_FOR=Tema per %s +FOLDER_MUSIC_THEME=Musica del tema +FOLDER_MUSIC_RANDOM=Musica casuale +FOLDER_MUSIC_ORIGINAL=Musica originale +FOLDER_MUSIC_TAG_THEME=TEMA +FOLDER_MUSIC_TAG_RANDOM=CASUALE +FOLDER_MUSIC_TAG_ORIGINAL=ORIGINALE +FOLDER_MUSIC_SOURCE_THEME=La musica del tema +FOLDER_MUSIC_SOURCE_RANDOM=Dal randomizzatore audio +FOLDER_MUSIC_SOURCE_ORIGINAL=La musica originale della console +FOLDER_MUSIC_FOR=Musica per %s THEME_RANDOMIZER=Tema casuale AUDIO_RANDOMIZER_HOME=Audio casuale: Home AUDIO_RANDOMIZER_ALL_FOLDERS=Tutte le cartelle (solo temi senza ALCUN file audio) diff --git a/mod/etc/options_menu/themes/lang/ja-JP.lang b/mod/etc/options_menu/themes/lang/ja-JP.lang index 9a6aee9..7beaeb8 100644 --- a/mod/etc/options_menu/themes/lang/ja-JP.lang +++ b/mod/etc/options_menu/themes/lang/ja-JP.lang @@ -24,6 +24,9 @@ CLEAR_LIST=いちらんをさくじょ DOWNLOAD_THEMES=Themeをdownload THEME_PER_FOLDER=Folderごとのtheme ASSIGN_FOLDER_THEMES=FolderにThemeをわりあてる +MUSIC_PER_FOLDER=Folderごとのおんがく +ASSIGN_FOLDER_MUSIC=Folderにおんがくをわりあてる +FOLDER_MUSIC=Folderのおんがく FOLDER_THEMES=FolderのTheme HOME_FOLDER=ホーム AUTOMATIC=じどう @@ -44,6 +47,16 @@ FOLDER_THEME_USED_IN=%sこのFolderでしよう FOLDER_THEME_USED_ONCE=1このFolderでしよう FOLDER_THEME_UNUSED=どのFolderにもしていされていない FOLDER_THEME_FOR=%sのTheme +FOLDER_MUSIC_THEME=Themeのおんがく +FOLDER_MUSIC_RANDOM=ランダムなおんがく +FOLDER_MUSIC_ORIGINAL=オリジナルのおんがく +FOLDER_MUSIC_TAG_THEME=THEME +FOLDER_MUSIC_TAG_RANDOM=ランダム +FOLDER_MUSIC_TAG_ORIGINAL=オリジナル +FOLDER_MUSIC_SOURCE_THEME=Themeのおんがく +FOLDER_MUSIC_SOURCE_RANDOM=オーディオランダマイザーから +FOLDER_MUSIC_SOURCE_ORIGINAL=コンソールのオリジナルのおんがく +FOLDER_MUSIC_FOR=%sのおんがく THEME_RANDOMIZER=Themeをrandomに AUDIO_RANDOMIZER_HOME=Audioをrandomに: Home AUDIO_RANDOMIZER_ALL_FOLDERS=すべてのFolder (Audioファイルが1つもないThemeのみ) diff --git a/mod/etc/options_menu/themes/lang/nl-NL.lang b/mod/etc/options_menu/themes/lang/nl-NL.lang index 6291fef..391f55a 100644 --- a/mod/etc/options_menu/themes/lang/nl-NL.lang +++ b/mod/etc/options_menu/themes/lang/nl-NL.lang @@ -24,6 +24,9 @@ CLEAR_LIST=Lijst wissen DOWNLOAD_THEMES=Thema's downloaden THEME_PER_FOLDER=Thema per map ASSIGN_FOLDER_THEMES=Thema aan map toewijzen +MUSIC_PER_FOLDER=Muziek per map +ASSIGN_FOLDER_MUSIC=Muziek aan map toewijzen +FOLDER_MUSIC=Mapmuziek FOLDER_THEMES=Mapthema's HOME_FOLDER=Home AUTOMATIC=Automatisch @@ -44,6 +47,16 @@ FOLDER_THEME_USED_IN=Gebruikt in %s mappen FOLDER_THEME_USED_ONCE=Gebruikt in 1 map FOLDER_THEME_UNUSED=Aan geen map toegewezen FOLDER_THEME_FOR=Thema voor %s +FOLDER_MUSIC_THEME=Themamuziek +FOLDER_MUSIC_RANDOM=Willekeurige muziek +FOLDER_MUSIC_ORIGINAL=Originele muziek +FOLDER_MUSIC_TAG_THEME=THEMA +FOLDER_MUSIC_TAG_RANDOM=WILLEKEURIG +FOLDER_MUSIC_TAG_ORIGINAL=ORIGINEEL +FOLDER_MUSIC_SOURCE_THEME=De eigen muziek van het thema +FOLDER_MUSIC_SOURCE_RANDOM=Van de audio-randomizer +FOLDER_MUSIC_SOURCE_ORIGINAL=De originele muziek van de console +FOLDER_MUSIC_FOR=Muziek voor %s THEME_RANDOMIZER=Willekeurig thema AUDIO_RANDOMIZER_HOME=Willekeurige audio: Start AUDIO_RANDOMIZER_ALL_FOLDERS=Alle mappen (alleen thema's zonder ENIG audiobestand) diff --git a/mod/etc/options_menu/themes/lang/pt-PT.lang b/mod/etc/options_menu/themes/lang/pt-PT.lang index b1af290..b579e92 100644 --- a/mod/etc/options_menu/themes/lang/pt-PT.lang +++ b/mod/etc/options_menu/themes/lang/pt-PT.lang @@ -24,6 +24,9 @@ CLEAR_LIST=Limpar Lista DOWNLOAD_THEMES=Descarregar Temas THEME_PER_FOLDER=Tema Por Pasta ASSIGN_FOLDER_THEMES=Atribuir Tema a Pasta +MUSIC_PER_FOLDER=Música Por Pasta +ASSIGN_FOLDER_MUSIC=Atribuir Música a Pasta +FOLDER_MUSIC=Música por Pasta FOLDER_THEMES=Temas por Pasta HOME_FOLDER=Início AUTOMATIC=Automático @@ -44,6 +47,16 @@ FOLDER_THEME_USED_IN=Usado em %s pastas FOLDER_THEME_USED_ONCE=Usado em 1 pasta FOLDER_THEME_UNUSED=Sem pastas atribuídas FOLDER_THEME_FOR=Tema para %s +FOLDER_MUSIC_THEME=Música do tema +FOLDER_MUSIC_RANDOM=Música aleatória +FOLDER_MUSIC_ORIGINAL=Música original +FOLDER_MUSIC_TAG_THEME=TEMA +FOLDER_MUSIC_TAG_RANDOM=ALEATÓRIA +FOLDER_MUSIC_TAG_ORIGINAL=ORIGINAL +FOLDER_MUSIC_SOURCE_THEME=A música do próprio tema +FOLDER_MUSIC_SOURCE_RANDOM=Do Audio Randomizer +FOLDER_MUSIC_SOURCE_ORIGINAL=A música original da consola +FOLDER_MUSIC_FOR=Música para %s THEME_RANDOMIZER=Tema Aleatório AUDIO_RANDOMIZER_HOME=Áudio Aleatório: Home AUDIO_RANDOMIZER_ALL_FOLDERS=Todas as Pastas (Só Temas Sem Ficheiros de Áudio) diff --git a/mod/etc/options_menu/themes/lang/ru-RU.lang b/mod/etc/options_menu/themes/lang/ru-RU.lang index b75aa3f..5212462 100644 --- a/mod/etc/options_menu/themes/lang/ru-RU.lang +++ b/mod/etc/options_menu/themes/lang/ru-RU.lang @@ -24,6 +24,9 @@ CLEAR_LIST=Очистить список DOWNLOAD_THEMES=Скачать темы THEME_PER_FOLDER=Тема для каждой папки ASSIGN_FOLDER_THEMES=Назначить тему папке +MUSIC_PER_FOLDER=Музыка для каждой папки +ASSIGN_FOLDER_MUSIC=Назначить музыку папке +FOLDER_MUSIC=Музыка папок FOLDER_THEMES=Темы папок HOME_FOLDER=Главное меню AUTOMATIC=Автоматически @@ -44,6 +47,16 @@ FOLDER_THEME_USED_IN=Используется в папках: %s FOLDER_THEME_USED_ONCE=Используется в 1 папке FOLDER_THEME_UNUSED=Не назначена ни одной папке FOLDER_THEME_FOR=Тема для %s +FOLDER_MUSIC_THEME=Музыка темы +FOLDER_MUSIC_RANDOM=Случайная музыка +FOLDER_MUSIC_ORIGINAL=Оригинальная музыка +FOLDER_MUSIC_TAG_THEME=ТЕМА +FOLDER_MUSIC_TAG_RANDOM=СЛУЧАЙНО +FOLDER_MUSIC_TAG_ORIGINAL=ОРИГИНАЛ +FOLDER_MUSIC_SOURCE_THEME=Собственная музыка темы +FOLDER_MUSIC_SOURCE_RANDOM=Из случайного выбора аудио +FOLDER_MUSIC_SOURCE_ORIGINAL=Оригинальная музыка консоли +FOLDER_MUSIC_FOR=Музыка для %s THEME_RANDOMIZER=Случайная тема AUDIO_RANDOMIZER_HOME=Случайное аудио: Главное меню AUDIO_RANDOMIZER_ALL_FOLDERS=Все папки (только темы БЕЗ аудиофайлов) diff --git a/mod/etc/options_menu/themes/scripts/om_themescript b/mod/etc/options_menu/themes/scripts/om_themescript index 2e90edd..4bc4277 100644 --- a/mod/etc/options_menu/themes/scripts/om_themescript +++ b/mod/etc/options_menu/themes/scripts/om_themescript @@ -36,7 +36,12 @@ musicHackUnbind() { for WAVE in $AUDIO_FILES_ARR; do case "$CURRENT_MOUNTS" in - *" ${ORIGINAL_THEME_AUDIO}/${WAVE}.wav "*) umount "${ORIGINAL_THEME_AUDIO}/${WAVE}.wav" ;; + *" ${ORIGINAL_THEME_AUDIO}/${WAVE}.wav "*) + # a folder's music can sit on top of the theme's own bind + while grep -q " ${ORIGINAL_THEME_AUDIO}/${WAVE}.wav " /proc/mounts; do + umount "${ORIGINAL_THEME_AUDIO}/${WAVE}.wav" || break + done + ;; esac done } @@ -55,7 +60,6 @@ fontBindFix() { done } -# folder_themes lines: = assignedFolderTheme() { local LINE [ -n "$1" ] && [ -f "$STATE_PATH/folder_themes" ] || return 1 @@ -68,6 +72,18 @@ assignedFolderTheme() { return 1 } +assignedFolderMusic() { + local LINE + [ -n "$1" ] && [ -f "$STATE_PATH/folder_music" ] || return 1 + while IFS= read -r LINE; do + if [ "${LINE%=*}" = "$1" ] && [ -f "$MUSIC_PATH/${LINE##*=}" ]; then + echo "${LINE##*=}" + return 0 + fi + done <"$STATE_PATH/folder_music" + return 1 +} + # the entry for folder $1 inside its parent folder (not a Back/Home shortcut to it) folderEntry() { local DESKTOP @@ -101,6 +117,25 @@ folderTheme() { return 1 } +folderMusic() { + local CODE="$1" + local DESKTOP + local KEY + local DEPTH=0 + if [ "$CODE" == "000" ]; then + assignedFolderMusic "home" + return + fi + while [ "$CODE" != "000" ] && [ "$DEPTH" -lt 16 ]; do + DEPTH=$((DEPTH + 1)) + DESKTOP="$(folderEntry "$CODE")" || return 1 + KEY="$(sed -n 's/^Name=//p' "$DESKTOP" | head -n1 | sed 's/ /_/g' | awk '{print tolower($0)}')" + assignedFolderMusic "$KEY" && return 0 + CODE="$(basename "$(dirname "$(dirname "$DESKTOP")")")" + done + return 1 +} + themeSelector() { local FOLDER_ID local GAME_STORAGE @@ -150,7 +185,7 @@ themeSelector() { SILENT_WAV="$rootfs/usr/share/themes/bgm_boot.wav" MUSIC_PATH="$mountpoint/media/$modname/music_menu" - [ ! -d "$MUSIC_PATH" ] && MUSIC_PATH="$rootfs/usr/share/music_menu" + find "$MUSIC_PATH" -maxdepth 1 -type f -iname "*.wav" 2>/dev/null | read -r _ || MUSIC_PATH="$rootfs/usr/share/music_menu" BGM_HOME="" GAME_STORAGE="$(findGameStorage)" @@ -170,6 +205,8 @@ themeSelector() { FOLDER_THEME="$(folderTheme "$FOLDER_ID")" [ -n "$FOLDER_THEME" ] && SELECTED_THEME="$FOLDER_THEME" fi + local FOLDER_MUSIC="" + [ -f "$STATE_PATH/om_musicperfolder" ] && FOLDER_MUSIC="$(folderMusic "$FOLDER_ID")" FULL_THEME_PATH="$THEMES_PATH/$SELECTED_THEME" @@ -201,7 +238,7 @@ themeSelector() { if [ "$FULL_THEME_PATH" != "$DIY_THEME" ] && [ -f "$FULL_THEME_PATH/needs_overlay" ]; then [ -d "$FULL_THEME_PATH" ] && mount -t overlayfs -o lowerdir="$ORIGINAL_THEME",upperdir="$FULL_THEME_PATH" overlayfs "$ORIGINAL_THEME" else - if [ -f "$STATE_PATH/om_audiorandomizer" ] && [ -n "$(ls -A "$MUSIC_PATH")" ]; then + if [ -z "$FOLDER_MUSIC" ] && [ -f "$STATE_PATH/om_audiorandomizer" ] && [ -n "$(ls -A "$MUSIC_PATH")" ]; then [ -z "$BGM_HOME" ] && BGM_HOME="$(find "$MUSIC_PATH" -maxdepth 1 -type f -iname "*.wav" | shuf | head -n1)" if [ "$FOLDER_ID" = "000" ]; then musicHackBind "$BGM_HOME" "$BGM_HOME" "$ORIGINAL_THEME_AUDIO" @@ -210,10 +247,13 @@ themeSelector() { fi fi - cd "$FULL_THEME_PATH" && find . -type f ! -path "./fonts/*" | while IFS= read -r THEME_FILE; do + local SKIP_BGM="none" + [ -n "$FOLDER_MUSIC" ] && SKIP_BGM="bgm_*.wav" + cd "$FULL_THEME_PATH" && find . -type f ! -path "./fonts/*" ! -name "$SKIP_BGM" | while IFS= read -r THEME_FILE; do mount_bind "$FULL_THEME_PATH/$THEME_FILE" "$ORIGINAL_THEME/$THEME_FILE" done fi + [ -n "$FOLDER_MUSIC" ] && musicHackBind "$MUSIC_PATH/$FOLDER_MUSIC" "$MUSIC_PATH/$FOLDER_MUSIC" "$ORIGINAL_THEME_AUDIO" cd - >/dev/null || exit break diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index e360999..7ccc0dd 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -78,7 +78,9 @@ omOverlayThemeScript="$omModScripts/om_overlaythmcript" om_themerandomizer="$state_path/om_themerandomizer" om_themeperfolder="$state_path/om_themeperfolder" +om_musicperfolder="$state_path/om_musicperfolder" om_folderthemes="$state_path/folder_themes" +om_foldermusic="$state_path/folder_music" folderThemesTmp="/tmp/om_folderthemes" om_audiorandomizer="$state_path/om_audiorandomizer" om_advmusichack="$state_path/om_advmusichack" @@ -101,8 +103,12 @@ enableAudioSetting="$omModSettingsCmds/c0001_audiorandomizer" disableAudioSetting="$omModSettingsCmds/_audiorandomizer" enableAdvancedMusicHack="$omModSettingsCmds/c0001_audiorandomizerall" disableAdvancedMusicHack="$omModSettingsCmds/_audiorandomizerall" -enableFolderThemes="$omModSettingsCmds/c0000_themeperfolder_assign" -disableFolderThemes="$omModSettingsCmds/_themeperfolder_assign" +enableFolderThemes="$omModSettingsCmds/c0000_themeperfolder_1assign" +disableFolderThemes="$omModSettingsCmds/_themeperfolder_1assign" +enableMusicPerFolder="$omModSettingsCmds/c0001_musicperfolder" +disableMusicPerFolder="$omModSettingsCmds/_musicperfolder" +enableFolderMusic="$omModSettingsCmds/c0001_musicperfolder_assign" +disableFolderMusic="$omModSettingsCmds/_musicperfolder_assign" enableThemeSetting="$omModSettingsCmds/c0000_themerandomizer" disableThemeSetting="$omModSettingsCmds/_themerandomizer" enableSystemCleanUp="$omModSettingsCmds/c0001_systemcleanup" @@ -147,6 +153,11 @@ system="$(echo "$s_type" | awk '{print toupper($0)}')" om_mod_path="$nand_path/$modtitle" theme_path="$usb_path/$modtitle/$s_type" [ ! -d "$theme_path" ] && theme_path="$nand_path/$modtitle/$s_type" +hasMusicFiles() { + find "$1" -maxdepth 1 -type f -iname "*.wav" 2>/dev/null | read -r _ +} +music_path="$usb_path/music_menu" +hasMusicFiles "$music_path" || music_path="$nand_path/music_menu" nes_theme_path="${om_mod_path}/nes" shonen_theme_path="${om_mod_path}/shonen" snes_theme_path="${om_mod_path}/snes" @@ -684,11 +695,13 @@ modCleanUp() { } themeSettings() { - if [ "$1" = "tpf" ]; then - if [ ! -f "$om_themeperfolder" ]; then - settingsOptionChecker "on" "$om_themeperfolder" + if [ "$1" = "tpf" ] || [ "$1" = "mpf" ]; then + flag="$om_themeperfolder" + [ "$1" = "mpf" ] && flag="$om_musicperfolder" + if [ ! -f "$flag" ]; then + settingsOptionChecker "on" "$flag" else - settingsOptionChecker "off" "$om_themeperfolder" + settingsOptionChecker "off" "$flag" fi else if [ ! -f "$om_themerandomizer" ]; then @@ -746,16 +759,29 @@ hasItemsToCleanUp() { fi } +musicPerFolder() { + [ -f "$om_musicperfolder" ] && hasMusicFiles "$music_path" +} + folderThemesRow() { if [ -f "$om_themeperfolder" ] && [ -d "$has_folders" ]; then enable_cmds "FolderThemes" else disable_cmds "FolderThemes" fi + if hasMusicFiles "$music_path"; then + enable_cmds "MusicPerFolder" + else + disable_cmds "MusicPerFolder" + fi + if musicPerFolder && [ -d "$has_folders" ]; then + enable_cmds "FolderMusic" + else + disable_cmds "FolderMusic" + fi } -# folder key (same snake_case om_themescript derives from the folder Name=) folder name depth, -# in tree order: a folder entry living in games/

/CLV-S-00/ is folder inside folder

+# a folder entry living in games/

/CLV-S-00/ is folder inside folder

listFolders() { tab="$(printf '\t')" find "$gStorage" -maxdepth 3 -type f -name 'CLV-S-00*.desktop' -exec awk ' @@ -792,7 +818,6 @@ themePreview() { echo "$preview" } -# the theme every folder without one of its own falls back to globalTheme() { if [ -f "$om_themerandomizer" ]; then echo "@random" @@ -809,10 +834,26 @@ folderThemesMenu() { listFolders } >"$data_dir/folders.list" find "$theme_path" -mindepth 1 -maxdepth 1 -type d | sed 's#^.*/##' | sort | while IFS= read -r thm; do - printf '%s\t%s\t%s\n' "$thm" "$(themeTitle "$thm")" "$(themePreview "$thm")" + has_music=0 + find "$theme_path/$thm" -type f -name "*.wav" | read -r _ && has_music=1 + needs_overlay=0 + [ -f "$theme_path/$thm/needs_overlay" ] && needs_overlay=1 + printf '%s\t%s\t%s\t%s\t%s\n' "$thm" "$(themeTitle "$thm")" "$(themePreview "$thm")" "$has_music" "$needs_overlay" done >"$data_dir/themes.list" + find "$music_path" -maxdepth 1 -type f -iname "*.wav" 2>/dev/null | sed 's#^.*/##' | sort | while IFS= read -r wav; do + printf '%s\t%s\n' "$wav" "$(echo "${wav%.*}" | sed 's/_/ /g')" + done >"$data_dir/music.list" globalTheme >"$data_dir/global" + { + [ -f "$om_audiorandomizer" ] && echo random_home + [ -f "$om_advmusichack" ] && echo random_folders + } >"$data_dir/audio" echo "sh $omModScripts/om_settings" >"$data_dir/om_return" - $optionsMenu/lib/theme_manager --commandPath "$data_dir/" --scriptPath "$data_dir" --title FOLDER_THEMES --folderThemes "$data_dir" --mapping "$om_folderthemes" & + title="FOLDER_THEMES" + [ "$1" = "music" ] && title="FOLDER_MUSIC" + set -- --commandPath "$data_dir/" --scriptPath "$data_dir" --title "$title" --folderThemes "$data_dir" --folderChannel "$1" + [ -f "$om_themeperfolder" ] && set -- "$@" --mapping "$om_folderthemes" + musicPerFolder && set -- "$@" --musicMapping "$om_foldermusic" + $optionsMenu/lib/theme_manager "$@" & } diff --git a/mod/etc/options_menu/themes/settings/commands/_musicperfolder b/mod/etc/options_menu/themes/settings/commands/_musicperfolder new file mode 100644 index 0000000..9c8dc1e --- /dev/null +++ b/mod/etc/options_menu/themes/settings/commands/_musicperfolder @@ -0,0 +1,6 @@ +COMMAND_NAME=MUSIC_PER_FOLDER +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %script_dir%/om_theme mpf +STATE_STR=sh %script_dir%/om_state music_per_folder +PREVIEW_IMAGE=/etc/options_menu/images/preview_music_per_folder.png diff --git a/mod/etc/options_menu/themes/settings/commands/_musicperfolder_assign b/mod/etc/options_menu/themes/settings/commands/_musicperfolder_assign new file mode 100644 index 0000000..283d5d2 --- /dev/null +++ b/mod/etc/options_menu/themes/settings/commands/_musicperfolder_assign @@ -0,0 +1,6 @@ +COMMAND_NAME=ASSIGN_FOLDER_MUSIC +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +CHILD=TRUE +COMMAND_STR=usleep 50000 && sh %script_dir%/om_folderthemes music & +PREVIEW_IMAGE=/etc/options_menu/images/preview_assign_music.png diff --git a/mod/etc/options_menu/themes/settings/commands/_themeperfolder_assign b/mod/etc/options_menu/themes/settings/commands/_themeperfolder_1assign similarity index 66% rename from mod/etc/options_menu/themes/settings/commands/_themeperfolder_assign rename to mod/etc/options_menu/themes/settings/commands/_themeperfolder_1assign index b6cfa4b..1df1eb4 100644 --- a/mod/etc/options_menu/themes/settings/commands/_themeperfolder_assign +++ b/mod/etc/options_menu/themes/settings/commands/_themeperfolder_1assign @@ -2,5 +2,5 @@ COMMAND_NAME=ASSIGN_FOLDER_THEMES COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE CHILD=TRUE -COMMAND_STR=usleep 50000 && sh %script_dir%/om_folderthemes & +COMMAND_STR=usleep 50000 && sh %script_dir%/om_folderthemes theme & PREVIEW_IMAGE=/etc/options_menu/images/randtheme.png diff --git a/mod/etc/options_menu/themes/settings/commands/c0000_zseparator b/mod/etc/options_menu/themes/settings/commands/c0000_zseparator new file mode 100644 index 0000000..e69de29 diff --git a/mod/etc/options_menu/themes/settings/scripts/om_folderthemes b/mod/etc/options_menu/themes/settings/scripts/om_folderthemes index ea046cf..94c7826 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_folderthemes +++ b/mod/etc/options_menu/themes/settings/scripts/om_folderthemes @@ -20,4 +20,4 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init -folderThemesMenu +folderThemesMenu "${1:-theme}" diff --git a/mod/etc/options_menu/themes/settings/scripts/om_state b/mod/etc/options_menu/themes/settings/scripts/om_state index 68adfd6..999aa95 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_state +++ b/mod/etc/options_menu/themes/settings/scripts/om_state @@ -31,6 +31,7 @@ fi case "$1" in theme_per_folder) flag="$state_path/om_themeperfolder" ;; + music_per_folder) flag="$state_path/om_musicperfolder" ;; theme_randomizer) flag="$state_path/om_themerandomizer" ;; audio_randomizer) flag="$state_path/om_audiorandomizer" ;; audio_randomizer_all) flag="$state_path/om_advmusichack" ;; diff --git a/mod/install b/mod/install index c2d5d1d..5488a2a 100644 --- a/mod/install +++ b/mod/install @@ -40,7 +40,7 @@ mkdir -p "$ts_backup/state" for ts_dir in themes music_menu; do [ -d "$usr_share/$ts_dir" ] && mv "$usr_share/$ts_dir" "$ts_backup/" done -for ts_file in lastTheme prevTheme active_theme folder_themes; do +for ts_file in lastTheme prevTheme active_theme folder_themes folder_music; do [ -f "$ts_state/$ts_file" ] && cp -p "$ts_state/$ts_file" "$ts_backup/state/" done for ts_unfile in "$hmod_dir/uninstall-"*andomiz* \ diff --git a/mod/readme.md b/mod/readme.md index 8f6f5cf..7b4e367 100644 --- a/mod/readme.md +++ b/mod/readme.md @@ -17,23 +17,25 @@ The UI is my own standalone C++/SDL app (`theme_manager`), not a set of Lua scri - Build your own DIY theme from pieces of the themes you already have (background, sprites, colors, packed art, fonts...) - Theme randomizer — a different theme each time you go Home (off by default) - Audio randomizer on the Home folder, optionally extended to every folder (off by default) -- Theme per folder: give any folder (and Home) its own theme from one screen; subfolders without one inherit their parent folder's +- Theme and music per folder: give any folder (and Home) its own theme and its own menu music from one screen; subfolders without one inherit their parent folder's - Custom fonts per theme - Custom color palette for the Theme Manager UI itself (`theme.cfg`) - System clean-up to remove files your console type doesn't need - Reset just this mod's settings without a full uninstall -## Set a theme per folder, what is that? +## Set a theme or music per folder, what is that? -If you have your games organized into system folders, you can give each one its own theme instead of one theme for the whole console. Turn on **Theme Per Folder** in Settings, then open **Assign Theme To Folder**. Your folders are listed as a tree: press Left/Right on a folder to cycle through the themes, or A to pick one from a list. The preview shows where each folder's theme comes from. The same theme can be assigned to as many folders as you like, no copies or renaming needed. **Automatic**, the first entry when cycling and in the list, clears a folder's assignment. +If you have your games organized into system folders, you can give each one its own theme instead of one theme for the whole console. Turn on **Theme Per Folder** in Settings, then open **Assign Theme to Folder** right below it. Your folders are listed as a tree: press Left/Right on a folder to cycle through the themes, or A to pick one from a list. The preview shows where each folder's theme comes from. The same theme can be assigned to as many folders as you like, no copies or renaming needed. **Automatic**, the first entry when cycling and in the list, clears a folder's assignment. Naming a theme folder after the game folder (lower snake_case) still works for folders without an assignment. Eg: game folder `Nintendo - Nintendo Entertainment System` → theme folder `nintendo_-_nintendo_entertainment_system`. A folder on Automatic uses its parent folder's theme, all the way up; a top-level folder on Automatic uses the theme you applied for the whole console. A folder's own theme always wins over its parent's. +**Music Per Folder** works the same way: turn it on and open **Assign Music to Folder** to give folders their own menu music from `music_menu` (on USB, else the console's own). It only shows up when `music_menu` has `.wav` files, and works with or without Theme Per Folder. Music assigned to a folder plays instead of the theme's own music; a folder on Automatic keeps today's behaviour (the theme's music, the Audio Randomizer, or the original music). + ## What if I want a specific theme on my main menu? -With **Theme Per Folder** on, open **Assign Theme To Folder** and set a theme on **Home**, the first entry in the list. Naming a theme `default` also still works. +With **Theme Per Folder** on, open **Assign Theme to Folder** and set a theme on **Home**, the first entry in the list. Naming a theme `default` also still works. ## What do you mean by "build your own DIY theme"? diff --git a/mod/uninstall b/mod/uninstall index c032af1..c0ed4e2 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -51,6 +51,7 @@ remove "$mountpoint/$profilepath/$modname/lastTheme" remove "$mountpoint/$profilepath/$modname/prevTheme" remove "$mountpoint/$profilepath/$modname/active_theme" remove "$mountpoint/$profilepath/$modname/folder_themes" +remove "$mountpoint/$profilepath/$modname/folder_music" remove "$mountpoint/$profilepath/$modname/om_advmusichack" remove "$mountpoint/$profilepath/$modname/om_audiorandomizer" remove "$mountpoint/$profilepath/$modname/om_themerandomizer" diff --git a/src/command_loader.cpp b/src/command_loader.cpp index 4a0c6cb..c280513 100644 --- a/src/command_loader.cpp +++ b/src/command_loader.cpp @@ -100,6 +100,10 @@ AppOptions ParseArgs(int argc, char * argv[], const std::string & optionsLocatio options.folderThemesDir = argv[++i]; else if(std::strcmp(argv[i], "--mapping") == 0 && i+1 < argc) options.folderThemesMapping = argv[++i]; + else if(std::strcmp(argv[i], "--musicMapping") == 0 && i+1 < argc) + options.folderMusicMapping = argv[++i]; + else if(std::strcmp(argv[i], "--folderChannel") == 0 && i+1 < argc) + options.folderMusicScreen = std::strcmp(argv[++i], "music") == 0; } if(!options.commandLocation.empty() && options.commandLocation.back() != '/') options.commandLocation += '/'; @@ -201,7 +205,7 @@ void AppendBackNavigation(const std::string & optionsLocation, const AppOptions std::string backScriptPath = lastEntry.substr(c1+1, c2-c1-1); std::string backTitleKey = lastEntry.substr(c2+1); - backCommand = "usleep 50000 && OM_BACK_STACK=\"" + remainingStack + "\" " + optionsLocation + "options --commandPath " + backPath + backCommand = "rm -f " + std::string(FocusStatePrefix) + "*; usleep 50000 && OM_BACK_STACK=\"" + remainingStack + "\" " + optionsLocation + "options --commandPath " + backPath + (backScriptPath.empty() ? "" : " --scriptPath " + backScriptPath) + " --title \"" + backTitleKey + "\" &"; } diff --git a/src/command_loader.h b/src/command_loader.h index 37444fb..149667f 100644 --- a/src/command_loader.h +++ b/src/command_loader.h @@ -22,11 +22,16 @@ struct AppOptions std::string titleKey = "THEME_SELECTOR"; bool isRootScreen = true; // no --commandPath override - top of the theme-selector's own tree bool gridLayout = true; - std::string folderThemesDir; // --folderThemes: the folder theme screen, fed by om_folderthemes + std::string folderThemesDir; std::string folderThemesMapping; + std::string folderMusicMapping; + bool folderMusicScreen = false; }; -std::string ReadSftype(); // cached in OM_SFTYPE for child screens +// each screen remembers its last focused row here; cleared on leaving the theme manager +constexpr const char * FocusStatePrefix = "/tmp/om_focus"; + +std::string ReadSftype(); AppOptions ParseArgs(int argc, char * argv[], const std::string & optionsLocation); diff --git a/src/folder_theme_model.cpp b/src/folder_theme_model.cpp index 7e4e86c..c17f3f9 100644 --- a/src/folder_theme_model.cpp +++ b/src/folder_theme_model.cpp @@ -37,12 +37,25 @@ namespace line.pop_back(); return true; } + + std::vector ReadOptions(const std::string & path) + { + std::vector options; + std::ifstream in(path); + for(std::string line; ReadLine(in, line);) + { + std::vector f = SplitTabs(line); + if(f[0].empty()) + continue; + f.resize(5); + options.push_back({ f[0], f[1].empty() ? f[0] : f[1], f[2], f[3] == "1", f[4] == "1" }); + } + return options; + } } -bool FolderThemeModel::Load(const std::string & dataDir, const std::string & mappingPath) +bool FolderThemeModel::Load(const std::string & dataDir, const std::string & themeMapping, const std::string & musicMapping) { - mappingPath_ = mappingPath; - std::ifstream folders(dataDir + "/folders.list"); std::vector lastAtDepth; for(std::string line; ReadLine(folders, line);) @@ -60,55 +73,68 @@ bool FolderThemeModel::Load(const std::string & dataDir, const std::string & map folders_.push_back(entry); } - std::ifstream themes(dataDir + "/themes.list"); - for(std::string line; ReadLine(themes, line);) - { - std::vector f = SplitTabs(line); - if(f.size() < 3 || f[0].empty()) - continue; - themes_.push_back({ f[0], f[1].empty() ? f[0] : f[1], f[2] }); - } + slot(Channel::Theme).options = ReadOptions(dataDir + "/themes.list"); + slot(Channel::Music).options = ReadOptions(dataDir + "/music.list"); std::ifstream global(dataDir + "/global"); std::string globalId; ReadLine(global, globalId); - globalRandom_ = globalId == RandomGlobal; - globalTheme_ = ThemeIndex(globalId); + globalThemeRandom_ = globalId == RandomGlobal; + globalTheme_ = OptionIndex(slot(Channel::Theme), globalId); + + std::ifstream audio(dataDir + "/audio"); + for(std::string line; ReadLine(audio, line);) + { + musicRandomHome_ = musicRandomHome_ || line == "random_home"; + musicRandomFolders_ = musicRandomFolders_ || line == "random_folders"; + } + + themesPerFolder_ = !themeMapping.empty(); + LoadMapping(slot(Channel::Theme), themeMapping); + LoadMapping(slot(Channel::Music), musicMapping); + return !folders_.empty(); +} - assigned_.assign(folders_.size(), -1); - std::ifstream mapping(mappingPath_); +void FolderThemeModel::LoadMapping(Slot & s, const std::string & mappingPath) +{ + s.mappingPath = mappingPath; + s.assigned.assign(folders_.size(), -1); + if(mappingPath.empty()) + return; + std::ifstream mapping(mappingPath); for(std::string line; ReadLine(mapping, line);) { size_t eq = line.rfind('='); - int theme = eq == std::string::npos ? -1 : ThemeIndex(line.substr(eq + 1)); + int option = eq == std::string::npos ? -1 : OptionIndex(s, line.substr(eq + 1)); int folder = eq == std::string::npos ? -1 : FolderIndex(line.substr(0, eq)); - if(theme < 0 || folder < 0) + if(option < 0 || folder < 0) { if(!line.empty()) - unknownLines_.push_back(line); + s.unknownLines.push_back(line); continue; } for(size_t i = 0; i < folders_.size(); ++i) if(folders_[i].key == folders_[folder].key) - assigned_[i] = theme; + s.assigned[i] = option; } - - return !folders_.empty(); } -bool FolderThemeModel::Save() const +bool FolderThemeModel::Save(Channel channel) const { - const std::string tmpPath = mappingPath_ + ".tmp"; + const Slot & s = slot(channel); + if(s.mappingPath.empty()) + return false; + const std::string tmpPath = s.mappingPath + ".tmp"; { std::ofstream out(tmpPath, std::ios::trunc); if(!out) return false; - for(const std::string & line : unknownLines_) + for(const std::string & line : s.unknownLines) out << line << '\n'; std::vector written; for(size_t i = 0; i < folders_.size(); ++i) { - if(assigned_[i] < 0) + if(s.assigned[i] < 0) continue; bool seen = false; for(const std::string & key : written) @@ -116,58 +142,83 @@ bool FolderThemeModel::Save() const if(seen) continue; written.push_back(folders_[i].key); - out << folders_[i].key << '=' << themes_[assigned_[i]].id << '\n'; + out << folders_[i].key << '=' << s.options[s.assigned[i]].id << '\n'; } if(!out) return false; } - return std::rename(tmpPath.c_str(), mappingPath_.c_str()) == 0; + return std::rename(tmpPath.c_str(), s.mappingPath.c_str()) == 0; } -void FolderThemeModel::Assign(int folder, int theme) +void FolderThemeModel::Assign(Channel channel, int folder, int option) { // the mapping is keyed by folder name, so same-named folders share it + Slot & s = slot(channel); for(size_t i = 0; i < folders_.size(); ++i) if(folders_[i].key == folders_[folder].key) - assigned_[i] = theme; + s.assigned[i] = option; } -ResolvedTheme FolderThemeModel::Resolve(int folder) const +ResolvedOption FolderThemeModel::Resolve(Channel channel, int folder) const { - if(assigned_[folder] >= 0) - return { assigned_[folder], ThemeSource::Assigned, folder }; - return ResolveAutomatic(folder); + int own = slot(channel).assigned[folder]; + if(own >= 0) + return { own, OptionSource::Assigned, folder }; + return ResolveAutomatic(channel, folder); } -ResolvedTheme FolderThemeModel::ResolveAutomatic(int folder) const +ResolvedOption FolderThemeModel::ResolveAutomatic(Channel channel, int folder) const { const FolderEntry & entry = folders_[folder]; - int nameMatch = ThemeIndex(entry.key == HomeKey && entry.depth == 0 ? HomeNameMatch : entry.key); - if(nameMatch >= 0) - return { nameMatch, ThemeSource::NameMatch, folder }; + if(channel == Channel::Theme && (themesPerFolder_ || IsHome(folder))) + { + int nameMatch = OptionIndex(slot(channel), IsHome(folder) ? HomeNameMatch : entry.key); + if(nameMatch >= 0) + return { nameMatch, OptionSource::NameMatch, folder }; + } + if(channel == Channel::Theme && !themesPerFolder_) + return { globalTheme_, OptionSource::Global, -1 }; if(entry.parent >= 0) { - ResolvedTheme up = Resolve(entry.parent); - if(up.source != ThemeSource::Global) - up.source = ThemeSource::Inherited; - return up; + ResolvedOption up = Resolve(channel, entry.parent); + if(up.source == OptionSource::Assigned || up.source == OptionSource::NameMatch || up.source == OptionSource::Inherited) + { + up.source = OptionSource::Inherited; + return up; + } } - return { globalTheme_, ThemeSource::Global, -1 }; + + if(channel == Channel::Music) + return AutomaticMusic(folder); + return { globalTheme_, OptionSource::Global, -1 }; +} + +// mirrors om_themescript: the theme's own .wav wins, else the randomizer where it applies, else stock +ResolvedOption FolderThemeModel::AutomaticMusic(int folder) const +{ + int theme = Resolve(Channel::Theme, folder).option; + const std::vector & themes = slot(Channel::Theme).options; + if(theme >= 0 && themes[theme].hasMusic) + return { -1, OptionSource::ThemeMusic, -1 }; + bool randomApplies = IsHome(folder) ? musicRandomHome_ : musicRandomHome_ && musicRandomFolders_; + if(randomApplies && !(theme >= 0 && themes[theme].needsOverlay)) + return { -1, OptionSource::RandomMusic, -1 }; + return { -1, OptionSource::OriginalMusic, -1 }; } -int FolderThemeModel::UsageCount(int theme) const +int FolderThemeModel::UsageCount(Channel channel, int option) const { int count = 0; - for(int assigned : assigned_) - count += assigned == theme ? 1 : 0; + for(int assigned : slot(channel).assigned) + count += assigned == option ? 1 : 0; return count; } -int FolderThemeModel::ThemeIndex(const std::string & id) const +int FolderThemeModel::OptionIndex(const Slot & s, const std::string & id) const { - for(size_t i = 0; i < themes_.size(); ++i) - if(themes_[i].id == id) + for(size_t i = 0; i < s.options.size(); ++i) + if(s.options[i].id == id) return static_cast(i); return -1; } @@ -179,3 +230,8 @@ int FolderThemeModel::FolderIndex(const std::string & key) const return static_cast(i); return -1; } + +bool FolderThemeModel::IsHome(int folder) const +{ + return folders_[folder].key == HomeKey && folders_[folder].depth == 0; +} diff --git a/src/folder_theme_model.h b/src/folder_theme_model.h index e0e4ea9..5edc3ef 100644 --- a/src/folder_theme_model.h +++ b/src/folder_theme_model.h @@ -15,56 +15,73 @@ struct FolderEntry { - std::string key; // snake_case of the folder's Name=, as om_themescript derives it + std::string key; std::string name; int depth; int parent; }; -struct ThemeEntry +struct FolderOption { std::string id; std::string title; std::string preview; + bool hasMusic; + bool needsOverlay; }; -enum class ThemeSource { Assigned, NameMatch, Inherited, Global }; +enum class Channel { Theme = 0, Music = 1 }; -struct ResolvedTheme +enum class OptionSource { Assigned, NameMatch, Inherited, Global, ThemeMusic, RandomMusic, OriginalMusic }; + +struct ResolvedOption { - int theme; // -1: stock UI, or a random theme when the randomizer is on - ThemeSource source; + int option; // -1: nothing specific (stock UI, a random theme, the theme's own or random music) + OptionSource source; int fromFolder; }; -// Folder -> theme assignments, resolved the same way om_themescript applies them on chmenu +// Folder -> theme and folder -> music assignments, resolved the same way om_themescript applies them on chmenu class FolderThemeModel { public: - bool Load(const std::string & dataDir, const std::string & mappingPath); - bool Save() const; + bool Load(const std::string & dataDir, const std::string & themeMapping, const std::string & musicMapping); + bool Save(Channel channel) const; const std::vector & Folders() const { return folders_; } - const std::vector & Themes() const { return themes_; } - bool GlobalIsRandom() const { return globalRandom_; } + const std::vector & Options(Channel channel) const { return slot(channel).options; } + bool GlobalThemeIsRandom() const { return globalThemeRandom_; } - int Assigned(int folder) const { return assigned_[folder]; } - void Assign(int folder, int theme); // -1 clears - ResolvedTheme Resolve(int folder) const; - ResolvedTheme ResolveAutomatic(int folder) const; // what the folder gets without its own assignment - int UsageCount(int theme) const; + int Assigned(Channel channel, int folder) const { return slot(channel).assigned[folder]; } + void Assign(Channel channel, int folder, int option); // -1 clears + ResolvedOption Resolve(Channel channel, int folder) const; + ResolvedOption ResolveAutomatic(Channel channel, int folder) const; + int UsageCount(Channel channel, int option) const; private: - int ThemeIndex(const std::string & id) const; + struct Slot + { + std::vector options; + std::vector assigned; + std::vector unknownLines; // folders or options not on this console right now, kept on save + std::string mappingPath; + }; + + Slot & slot(Channel channel) { return slots_[static_cast(channel)]; } + const Slot & slot(Channel channel) const { return slots_[static_cast(channel)]; } + void LoadMapping(Slot & s, const std::string & mappingPath); + int OptionIndex(const Slot & s, const std::string & id) const; int FolderIndex(const std::string & key) const; + bool IsHome(int folder) const; + ResolvedOption AutomaticMusic(int folder) const; std::vector folders_; - std::vector themes_; - std::vector assigned_; - std::vector unknownLines_; // folders or themes not on this console right now, kept on save - std::string mappingPath_; + Slot slots_[2]; int globalTheme_ = -1; - bool globalRandom_ = false; + bool globalThemeRandom_ = false; + bool musicRandomHome_ = false; + bool musicRandomFolders_ = false; + bool themesPerFolder_ = false; // off: om_themescript ignores assignments and name matches alike }; #endif diff --git a/src/folder_themes_layout.cpp b/src/folder_themes_layout.cpp index 82bd88c..b5172e7 100644 --- a/src/folder_themes_layout.cpp +++ b/src/folder_themes_layout.cpp @@ -59,13 +59,13 @@ namespace int ThemeManagerApp::RunFolderThemesLayout() { FolderThemeModel model; - if(!model.Load(options_.folderThemesDir, options_.folderThemesMapping)) + if(!model.Load(options_.folderThemesDir, options_.folderThemesMapping, options_.folderMusicMapping)) return 1; const std::vector & folders = model.Folders(); - const std::vector & themes = model.Themes(); const int folderCount = static_cast(folders.size()); - const int optionCount = static_cast(themes.size()) + 1; // 0 = Automatic, n = themes[n-1] const bool hasY = PadHasY(); + const Channel channel = options_.folderMusicScreen ? Channel::Music : Channel::Theme; + auto OptionCount = [&]() { return static_cast(model.Options(channel).size()) + 1; }; // 0 = Automatic const int rowPitch = std::max(UiTheme::RowPitch, GetTTFLineHeight(RowGlyphSize)); const int displayRows = std::max(1, (UiTheme::FooterDividerY - UiTheme::ListBottomMargin - UiTheme::RowFirstY) / rowPitch); @@ -89,14 +89,21 @@ int ThemeManagerApp::RunFolderThemesLayout() Badge autoBadge = MakeBadge("Y", "AUTOMATIC", UiTheme::BadgeYDark, UiTheme::BadgeY); Texture changeLabel = MakeText(Translate("FOLDER_THEME_CHANGE"), 16, UiTheme::Text); - auto ThemeName = [&](int theme) + auto OptionName = [&](Channel ch, const ResolvedOption & resolved) { - if(theme >= 0) - return themes[theme].title; - return Translate(model.GlobalIsRandom() ? "FOLDER_THEME_RANDOM" : "FOLDER_THEME_STOCK"); + if(resolved.option >= 0) + return model.Options(ch)[resolved.option].title; + switch(resolved.source) + { + case OptionSource::ThemeMusic: return Translate("FOLDER_MUSIC_THEME"); + case OptionSource::RandomMusic: return Translate("FOLDER_MUSIC_RANDOM"); + case OptionSource::OriginalMusic: return Translate("FOLDER_MUSIC_ORIGINAL"); + default: return Translate(model.GlobalThemeIsRandom() ? "FOLDER_THEME_RANDOM" : "FOLDER_THEME_STOCK"); + } }; - auto AutomaticLabel = [&](int folder) { return Translate("AUTOMATIC") + " · " + ThemeName(model.ResolveAutomatic(folder).theme); }; + auto AutomaticLabel = [&](int folder) { return Translate("AUTOMATIC") + " · " + OptionName(channel, model.ResolveAutomatic(channel, folder)); }; + const std::vector & themes = model.Options(Channel::Theme); std::vector previews(themes.size()); std::vector previewLoaded(themes.size(), 0); // not vector: Preview() needs a real reference Texture fallbackPreview; @@ -110,7 +117,7 @@ int ThemeManagerApp::RunFolderThemesLayout() loaded = 1; std::string path = theme >= 0 ? themes[theme].preview : ""; if(path.empty() || !std::ifstream(path).good()) - path = optionsLocation_ + (theme < 0 && model.GlobalIsRandom() ? "images/randtheme.png" : "images/default_preview.png"); + path = optionsLocation_ + (theme < 0 && model.GlobalThemeIsRandom() ? "images/randtheme.png" : "images/default_preview.png"); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); slot = Texture(path, renderer_, 0, 0); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); @@ -134,9 +141,9 @@ int ThemeManagerApp::RunFolderThemesLayout() { for(int i = 0; i < folderCount; ++i) { - bool own = model.Assigned(i) >= 0; - std::string value = own ? themes[model.Assigned(i)].title : AutomaticLabel(i); - valueTex[i] = MakeText(TruncateToWidth(value, RowGlyphSize, ValueMaxW), RowGlyphSize, own ? UiTheme::Text : UiTheme::TextDim); + int own = model.Assigned(channel, i); + std::string value = own >= 0 ? model.Options(channel)[own].title : AutomaticLabel(i); + valueTex[i] = MakeText(TruncateToWidth(value, RowGlyphSize, ValueMaxW), RowGlyphSize, own >= 0 ? UiTheme::Text : UiTheme::TextDim); int textX = UiTheme::RowTextX + folders[i].depth * TreeIndent + (folders[i].depth > 0 ? 14 : 0); int nameAvail = ListRight - 16 - valueTex[i].rect.w - 2 * (chevron.rect.w + ChevronGap) - 16 - textX; nameTex[i] = MakeText(TruncateToWidth(folders[i].name, RowGlyphSize, nameAvail), RowGlyphSize, UiTheme::Text); @@ -150,40 +157,54 @@ int ThemeManagerApp::RunFolderThemesLayout() int detailTheme = -1; auto RebuildDetail = [&]() { - ResolvedTheme resolved = model.Resolve(selected); - detailTheme = resolved.theme; - detailName = MakeText(TruncateToWidth(ThemeName(resolved.theme), 22, DetailW), 22, UiTheme::Text); + detailTheme = model.Resolve(Channel::Theme, selected).option; + ResolvedOption resolved = model.Resolve(channel, selected); + detailName = MakeText(TruncateToWidth(OptionName(channel, resolved), 22, DetailW), 22, UiTheme::Text); std::string tagKey = "FOLDER_THEME_TAG_GLOBAL", sourceText = Translate("FOLDER_THEME_SOURCE_GLOBAL"); Color tagText = UiTheme::Text; detailTagFill = UiTheme::Border; switch(resolved.source) { - case ThemeSource::Assigned: + case OptionSource::Assigned: tagKey = "FOLDER_THEME_TAG_ASSIGNED"; sourceText = Translate("FOLDER_THEME_SOURCE_ASSIGNED"); detailTagFill = UiTheme::Accent; tagText = UiTheme::Bg; break; - case ThemeSource::NameMatch: + case OptionSource::NameMatch: tagKey = "FOLDER_THEME_TAG_NAME"; sourceText = Translate("FOLDER_THEME_SOURCE_NAME"); break; - case ThemeSource::Inherited: + case OptionSource::Inherited: tagKey = "FOLDER_THEME_TAG_INHERITED"; sourceText = Format("FOLDER_THEME_SOURCE_INHERITED", folders[resolved.fromFolder].name); detailTagFill = UiTheme::BadgeXDark; break; - case ThemeSource::Global: + case OptionSource::ThemeMusic: + tagKey = "FOLDER_MUSIC_TAG_THEME"; + sourceText = Translate("FOLDER_MUSIC_SOURCE_THEME"); + break; + case OptionSource::RandomMusic: + tagKey = "FOLDER_MUSIC_TAG_RANDOM"; + sourceText = Translate("FOLDER_MUSIC_SOURCE_RANDOM"); + break; + case OptionSource::OriginalMusic: + tagKey = "FOLDER_MUSIC_TAG_ORIGINAL"; + sourceText = Translate("FOLDER_MUSIC_SOURCE_ORIGINAL"); + break; + case OptionSource::Global: break; } detailTag = MakeText(Translate(tagKey), 13, tagText); int tagW = detailTag.rect.w + 16; detailSource = MakeText(TruncateToWidth(sourceText, 16, DetailW - tagW - 10), 16, UiTheme::Text); - int usage = resolved.theme >= 0 ? model.UsageCount(resolved.theme) : 0; - std::string usageText = usage > 1 ? Format("FOLDER_THEME_USED_IN", std::to_string(usage)) - : Translate(usage == 1 ? "FOLDER_THEME_USED_ONCE" : "FOLDER_THEME_UNUSED"); + int usage = resolved.option >= 0 ? model.UsageCount(channel, resolved.option) : 0; + std::string usageText; + if(resolved.option >= 0) + usageText = usage > 1 ? Format("FOLDER_THEME_USED_IN", std::to_string(usage)) + : Translate(usage == 1 ? "FOLDER_THEME_USED_ONCE" : "FOLDER_THEME_UNUSED"); detailUsage = MakeText(usageText, 14, UiTheme::TextDim); }; @@ -196,19 +217,19 @@ int ThemeManagerApp::RunFolderThemesLayout() topRow = selected - displayRows + 1; RebuildDetail(); }; - auto Assign = [&](int theme) + auto Assign = [&](int option) { - if(model.Assigned(selected) == theme) + if(model.Assigned(channel, selected) == option) return; - model.Assign(selected, theme); - model.Save(); + model.Assign(channel, selected, option); + model.Save(channel); RebuildRows(); RebuildDetail(); }; auto Cycle = [&](int step) { - int option = model.Assigned(selected) + 1; - Assign((option + step + optionCount) % optionCount - 1); + int option = model.Assigned(channel, selected) + 1; + Assign((option + step + OptionCount()) % OptionCount() - 1); }; bool picking = false; @@ -218,18 +239,18 @@ int ThemeManagerApp::RunFolderThemesLayout() auto OpenPicker = [&]() { picking = true; - pick = model.Assigned(selected) + 1; - pickTop = std::max(0, std::min(pick - pickerRows / 2, optionCount - pickerRows)); - pickerTitle = MakeText(TruncateToWidth(Format("FOLDER_THEME_FOR", folders[selected].name), 16, PickerRect.w - 32), 16, UiTheme::Text); + pick = model.Assigned(channel, selected) + 1; + pickTop = std::max(0, std::min(pick - pickerRows / 2, OptionCount() - pickerRows)); + pickerTitle = MakeText(TruncateToWidth(Format(channel == Channel::Theme ? "FOLDER_THEME_FOR" : "FOLDER_MUSIC_FOR", folders[selected].name), 16, PickerRect.w - 32), 16, UiTheme::Text); optionTex.clear(); - int labelW = PickerRect.w - 24 - ThumbW - 12 - 24; + int labelW = PickerRect.w - 24 - (channel == Channel::Theme ? ThumbW + 12 : 8) - 24; optionTex.push_back(MakeText(TruncateToWidth(AutomaticLabel(selected), 16, labelW), 16, UiTheme::Text)); - for(const ThemeEntry & theme : themes) - optionTex.push_back(MakeText(TruncateToWidth(theme.title, 16, labelW), 16, UiTheme::Text)); + for(const FolderOption & option : model.Options(channel)) + optionTex.push_back(MakeText(TruncateToWidth(option.title, 16, labelW), 16, UiTheme::Text)); }; auto MovePick = [&](int to) { - pick = std::max(0, std::min(optionCount - 1, to)); + pick = std::max(0, std::min(OptionCount() - 1, to)); if(pick < pickTop) pickTop = pick; else if(pick >= pickTop + pickerRows) @@ -299,9 +320,9 @@ int ThemeManagerApp::RunFolderThemesLayout() rightEdge = DrawBadge(backBadge, rightEdge); if(hasY) rightEdge = DrawBadge(autoBadge, rightEdge); + int midY = UiTheme::BadgeBandY + UiTheme::BadgeOuterSize / 2; int groupW = 2 * chevron.rect.w + 4 + UiTheme::BadgeLabelGap + changeLabel.rect.w; int x = rightEdge - groupW; - int midY = UiTheme::BadgeBandY + UiTheme::BadgeOuterSize / 2; chevron.rect.x = x; chevron.rect.y = midY - chevron.rect.h / 2; chevron.Draw(renderer_, SDL_FLIP_HORIZONTAL); @@ -389,8 +410,9 @@ int ThemeManagerApp::RunFolderThemesLayout() pickerTitle.rect.y = PickerRect.y + (PickerHeaderH - pickerTitle.rect.h) / 2; pickerTitle.Draw(renderer_); - int current = model.Assigned(selected) + 1; - for(int row = 0; row < pickerRows && pickTop + row < optionCount; ++row) + const bool thumbs = channel == Channel::Theme; + int current = model.Assigned(channel, selected) + 1; + for(int row = 0; row < pickerRows && pickTop + row < OptionCount(); ++row) { int option = pickTop + row; SDL_Rect rowRect{ PickerRect.x + 8, PickerRect.y + PickerHeaderH + row * PickerRowH, PickerRect.w - 16, PickerRowH - 4 }; @@ -399,13 +421,18 @@ int ThemeManagerApp::RunFolderThemesLayout() DrawRoundedFillRect(renderer_, rowRect, UiTheme::SelectedRowBg, UiTheme::BoxRadius); DrawStrokeRect(renderer_, rowRect, UiTheme::Accent, 2, UiTheme::BoxRadius); } - SDL_Rect thumb{ rowRect.x + 8, rowRect.y + (rowRect.h - ThumbH) / 2, ThumbW, ThumbH }; - if(option == 0) - DrawStrokeRect(renderer_, thumb, UiTheme::Border, 2, 4); - else - DrawCover(Preview(option - 1), thumb, 4, option == pick ? UiTheme::SelectedRowBg : UiTheme::Bg); + int labelX = rowRect.x + 16; + if(thumbs) + { + SDL_Rect thumb{ rowRect.x + 8, rowRect.y + (rowRect.h - ThumbH) / 2, ThumbW, ThumbH }; + if(option == 0) + DrawStrokeRect(renderer_, thumb, UiTheme::Border, 2, 4); + else + DrawCover(Preview(option - 1), thumb, 4, option == pick ? UiTheme::SelectedRowBg : UiTheme::Bg); + labelX = thumb.x + ThumbW + 12; + } Texture & label = optionTex[option]; - label.rect.x = thumb.x + ThumbW + 12; + label.rect.x = labelX; label.rect.y = rowRect.y + (rowRect.h - label.rect.h) / 2; label.Draw(renderer_); if(option == current) diff --git a/src/theme_manager_app.cpp b/src/theme_manager_app.cpp index 9bc78ae..dbbb25b 100644 --- a/src/theme_manager_app.cpp +++ b/src/theme_manager_app.cpp @@ -182,7 +182,7 @@ std::string ThemeManagerApp::FocusStatePath() const std::string sanitized = options_.commandLocation; for(char & c : sanitized) if(c == '/') c = '_'; - return "/tmp/om_focus" + sanitized; + return FocusStatePrefix + sanitized; } void ThemeManagerApp::SaveFocusState() const @@ -220,7 +220,7 @@ ThemeManagerApp::FrameEvent ThemeManagerApp::PollFrameEvents() void ThemeManagerApp::ResumeUnderlyingUi() const { - system(("/bin/sh " + optionsLocation_ + "scripts/ResumeUI.sh").c_str()); + system(("rm -f " + std::string(FocusStatePrefix) + "*; /bin/sh " + optionsLocation_ + "scripts/ResumeUI.sh").c_str()); } bool ThemeManagerApp::ActivateCommand(Command & cmd) diff --git a/src/theme_manager_app.h b/src/theme_manager_app.h index 530c2e2..34f86e7 100644 --- a/src/theme_manager_app.h +++ b/src/theme_manager_app.h @@ -81,7 +81,7 @@ class ThemeManagerApp int RunGridLayout(); int RunListLayout(); - int RunFolderThemesLayout(); // folder_themes_layout.cpp + int RunFolderThemesLayout(); }; #endif From 06f758ee0cb6a1d55dedb1962f1de506b1f0bae9 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 6 Oct 2026 15:14:00 +0000 Subject: [PATCH 111/210] Update OptionsMenu submodule to latest commit 6d8053b --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index b673707..6d8053b 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit b673707de0e20a1d93bc21a6f882982bc260e8f7 +Subproject commit 6d8053bd34de2179a12655ea7ea48cecb6811c8f From af459cb49c70081227e62b44b0a8bba667f1f408 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 6 Oct 2026 17:29:18 +0000 Subject: [PATCH 112/210] Update OptionsMenu submodule to commit fbdc5be --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 6d8053b..fbdc5be 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 6d8053bd34de2179a12655ea7ea48cecb6811c8f +Subproject commit fbdc5be95c07d7885fb60c71560b26d27c86425a From 1b066b78917bfc049e617efb0f07ab64ce8a0a24 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 6 Oct 2026 17:29:57 +0000 Subject: [PATCH 113/210] Apply snake_case/camelCase naming to the shell scripts Variables are snake_case and functions camelCase across install, uninstall and every script under mod/etc, with no name shared with hakchi's preinit or OptionsMenu's om_functions: - om_vars paths and toggles (omModSprites -> om_mod_sprites, enableDiY -> enable_diy, ...), switched to OptionsMenu's enableCmds/disableCmds - om_themescript/om_overlaythmcript locals (SELECTED_THEME -> selected_theme, ...); the ones that leaked into preinit are local now - system/src/dest -> system_name/sprite_src/sprite_dest - uninstall's remove() -> removeItem(), OptionsMenu defines remove - follows om_functions' renames (options_menu, om_scripts, game_storage, mod_state_path) --- .../options_menu/scripts/om_theme_selector | 4 +- .../themes/creator/scripts/om_applytheme | 14 +- .../themes/creator/scripts/om_diy_asset | 24 +- .../themes/creator/scripts/om_preview | 4 +- .../themes/creator/scripts/om_removediy | 2 +- .../creator/sprites/colors/scripts/om_return | 2 +- .../sprites/colors_nes/scripts/om_return | 2 +- .../creator/sprites/luigi/scripts/om_return | 2 +- .../creator/sprites/mario/scripts/om_return | 2 +- .../creator/sprites/packed/scripts/om_return | 2 +- .../sprites/packed_nes/scripts/om_return | 2 +- .../creator/sprites/pole/scripts/om_return | 2 +- .../creator/sprites/sbm/scripts/om_return | 2 +- .../creator/sprites/wall/scripts/om_return | 2 +- .../sprites/wall_far/scripts/om_return | 2 +- .../sprites/wall_near/scripts/om_return | 2 +- .../downloads/scripts/om_clearDownloads | 4 +- .../themes/downloads/scripts/om_refreshThemes | 2 +- .../themes/downloads/scripts/om_return | 8 +- .../options_menu/themes/scripts/om_clearList | 6 +- .../options_menu/themes/scripts/om_getThemes | 4 +- .../themes/scripts/om_overlaythmcript | 84 ++-- .../options_menu/themes/scripts/om_settings | 18 +- .../themes/scripts/om_themeCreator | 2 +- .../themes/scripts/om_themescript | 262 +++++------ mod/etc/options_menu/themes/scripts/om_vars | 420 +++++++++--------- .../themes/settings/scripts/om_audio | 2 +- .../themes/settings/scripts/om_reset | 12 +- .../themes/settings/scripts/om_state | 16 +- .../themes/settings/scripts/om_systemcleanup | 4 +- .../themes/settings/scripts/om_theme | 2 +- mod/uninstall | 146 +++--- 32 files changed, 531 insertions(+), 531 deletions(-) diff --git a/mod/etc/options_menu/scripts/om_theme_selector b/mod/etc/options_menu/scripts/om_theme_selector index 693d4be..b04a367 100644 --- a/mod/etc/options_menu/scripts/om_theme_selector +++ b/mod/etc/options_menu/scripts/om_theme_selector @@ -22,7 +22,7 @@ script_init injectFile "chmenu" hasThemesToInstall enableMenuOptions -remove "$omDummy" +remove "$om_dummy" [ -f "$om_firstrun" ] && remove "$om_themerandomizer" "$om_themeperfolder" "$om_musicperfolder" "$om_audiorandomizer" "$om_advmusichack" @@ -34,4 +34,4 @@ else loadThemeList fi -$optionsMenu/lib/theme_manager --title INSTALLED_THEMES & +$options_menu/lib/theme_manager --title INSTALLED_THEMES & diff --git a/mod/etc/options_menu/themes/creator/scripts/om_applytheme b/mod/etc/options_menu/themes/creator/scripts/om_applytheme index d082d2e..13592e6 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_applytheme +++ b/mod/etc/options_menu/themes/creator/scripts/om_applytheme @@ -19,7 +19,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init -"$omScripts/gen_splash" /tmp/om_splash.png THEME_SAVING && decodepng /tmp/om_splash.png >/dev/fb0 +"$om_scripts/gen_splash" /tmp/om_splash.png THEME_SAVING && decodepng /tmp/om_splash.png >/dev/fb0 name="diy_theme" i=1 @@ -31,14 +31,14 @@ if [ -d "${theme_path}/${name}" ]; then name="${name}-${i}" fi -cp -R "${diyThemePath}" "${theme_path}/${name}" -remove "${diyThemePath}" +cp -R "${diy_theme_path}" "${theme_path}/${name}" +remove "${diy_theme_path}" clearList -disable_cmds "Clear" "Preview" "DiY" -enable_cmds "Refresh" -remove "$omDummy" +disableCmds "clear" "preview" "diy" +enableCmds "refresh" +remove "$om_dummy" loadThemeList -r -$optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & +$options_menu/lib/theme_manager --commandPath $om_mod_gen_cmds/ --scriptPath $om_mod_gen_scripts --title "$om_title" & diff --git a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset index 95a91dd..dd8cb7f 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset +++ b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset @@ -22,47 +22,47 @@ script_init case "$1" in luigi) - diy_om_sprite_path="$omModLuigi" + diy_om_sprite_path="$om_mod_luigi" diy_title="DEMO_CHARACTER_2" ;; pole) - diy_om_sprite_path="$omModPole" + diy_om_sprite_path="$om_mod_pole" diy_title="DEMO_POLE" ;; wall) - diy_om_sprite_path="$omModWall" + diy_om_sprite_path="$om_mod_wall" diy_title="BACKGROUND" ;; wall_near) - diy_om_sprite_path="$omModWallN" + diy_om_sprite_path="$om_mod_wall_n" diy_title="DEMO_BACKGROUND_NEAR" ;; wall_far) - diy_om_sprite_path="$omModWallF" + diy_om_sprite_path="$om_mod_wall_f" diy_title="DEMO_BACKGROUND_FAR" ;; packed) - diy_om_sprite_path="$omModPacked" + diy_om_sprite_path="$om_mod_packed" diy_title="BASE_UI" ;; mario) - diy_om_sprite_path="$omModMario" + diy_om_sprite_path="$om_mod_mario" diy_title="DEMO_CHARACTER_1" ;; colors) - diy_om_sprite_path="$omModColors" + diy_om_sprite_path="$om_mod_colors" diy_title="COLORS" ;; colors_nes) - diy_om_sprite_path="$omModColorsNes" + diy_om_sprite_path="$om_mod_colors_nes" diy_title="BACKGROUND_COLOR" ;; packed_nes) - diy_om_sprite_path="$omModPackedNes" + diy_om_sprite_path="$om_mod_packed_nes" diy_title="BASE_UI" ;; sbm) - diy_om_sprite_path="$omModSbm" + diy_om_sprite_path="$om_mod_sbm" diy_title="DEMO_CHARACTERS" ;; esac @@ -72,4 +72,4 @@ if [ "$2" = "force" ] || ! find "$diy_om_sprite_path/commands" -maxdepth 1 -name findDiyAsset "$1" fi -$optionsMenu/lib/theme_manager --commandPath $diy_om_sprite_path/commands/ --scriptPath $diy_om_sprite_path/scripts --title $diy_title & +$options_menu/lib/theme_manager --commandPath $diy_om_sprite_path/commands/ --scriptPath $diy_om_sprite_path/scripts --title $diy_title & diff --git a/mod/etc/options_menu/themes/creator/scripts/om_preview b/mod/etc/options_menu/themes/creator/scripts/om_preview index 0ebb48e..2044890 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_preview +++ b/mod/etc/options_menu/themes/creator/scripts/om_preview @@ -20,9 +20,9 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init -"$omScripts/gen_splash" /tmp/om_splash.png THEME_PREVIEW_LOADING && decodepng /tmp/om_splash.png >/dev/fb0 +"$om_scripts/gen_splash" /tmp/om_splash.png THEME_PREVIEW_LOADING && decodepng /tmp/om_splash.png >/dev/fb0 if [ ! -f "$b_file" ]; then - sed '1,2d' $omThemeScript >"$b_file" + sed '1,2d' $om_theme_script >"$b_file" echo "themeSelector" >"$p_file" fi diff --git a/mod/etc/options_menu/themes/creator/scripts/om_removediy b/mod/etc/options_menu/themes/creator/scripts/om_removediy index b1d3595..27275da 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_removediy +++ b/mod/etc/options_menu/themes/creator/scripts/om_removediy @@ -22,4 +22,4 @@ script_init diyThemeRemover -$optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & +$options_menu/lib/theme_manager --commandPath $om_mod_gen_cmds/ --scriptPath $om_mod_gen_scripts --title "$om_title" & diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return index 0f1d86b..a1329f6 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $options_menu/lib/theme_manager --commandPath $om_mod_gen_cmds/ --scriptPath $om_mod_gen_scripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return index 0f1d86b..a1329f6 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $options_menu/lib/theme_manager --commandPath $om_mod_gen_cmds/ --scriptPath $om_mod_gen_scripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return index 0f1d86b..a1329f6 100644 --- a/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/luigi/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $options_menu/lib/theme_manager --commandPath $om_mod_gen_cmds/ --scriptPath $om_mod_gen_scripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return index 7c3e78e..02d4a4c 100644 --- a/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/mario/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $options_menu/lib/theme_manager --commandPath $om_mod_gen_cmds/ --scriptPath $om_mod_gen_scripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return index 7c3e78e..02d4a4c 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $options_menu/lib/theme_manager --commandPath $om_mod_gen_cmds/ --scriptPath $om_mod_gen_scripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return index 7c3e78e..02d4a4c 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/packed_nes/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $options_menu/lib/theme_manager --commandPath $om_mod_gen_cmds/ --scriptPath $om_mod_gen_scripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return index 7c3e78e..02d4a4c 100644 --- a/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $options_menu/lib/theme_manager --commandPath $om_mod_gen_cmds/ --scriptPath $om_mod_gen_scripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return index 7c3e78e..02d4a4c 100644 --- a/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $options_menu/lib/theme_manager --commandPath $om_mod_gen_cmds/ --scriptPath $om_mod_gen_scripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return index 7c3e78e..02d4a4c 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $options_menu/lib/theme_manager --commandPath $om_mod_gen_cmds/ --scriptPath $om_mod_gen_scripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return index 7c3e78e..02d4a4c 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_far/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $options_menu/lib/theme_manager --commandPath $om_mod_gen_cmds/ --scriptPath $om_mod_gen_scripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return index 7c3e78e..02d4a4c 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return +++ b/mod/etc/options_menu/themes/creator/sprites/wall_near/scripts/om_return @@ -23,4 +23,4 @@ script_init # shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title DIY_THEME & +usleep 50000 && $options_menu/lib/theme_manager --commandPath $om_mod_gen_cmds/ --scriptPath $om_mod_gen_scripts/ --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads index a779d16..71bd2e6 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads +++ b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads @@ -21,6 +21,6 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init clearDownloads -remove "$subDummy" +remove "$sub_dummy" -$optionsMenu/lib/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES & +$options_menu/lib/theme_manager --commandPath $om_mod_sub_cmds/ --scriptPath $om_mod_sub_scripts --title AVAILABLE_THEMES & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes index 1c34d44..77ead87 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes +++ b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes @@ -22,4 +22,4 @@ script_init getThemeList -$optionsMenu/lib/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES & +$options_menu/lib/theme_manager --commandPath $om_mod_sub_cmds/ --scriptPath $om_mod_sub_scripts --title AVAILABLE_THEMES & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_return b/mod/etc/options_menu/themes/downloads/scripts/om_return index 150ee05..e925840 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_return +++ b/mod/etc/options_menu/themes/downloads/scripts/om_return @@ -19,14 +19,14 @@ # shellcheck source=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init -remove "$omDummy" +remove "$om_dummy" if [ -n "$(ls -A $theme_path)" ]; then - touch "$omDummy" + touch "$om_dummy" themeCountChanged && themeLoader else - remove "$omModSpacer" + remove "$om_mod_spacer" clearList fi -usleep 50000 && $optionsMenu/lib/theme_manager --title INSTALLED_THEMES & +usleep 50000 && $options_menu/lib/theme_manager --title INSTALLED_THEMES & diff --git a/mod/etc/options_menu/themes/scripts/om_clearList b/mod/etc/options_menu/themes/scripts/om_clearList index 42dd0b3..102f9ac 100644 --- a/mod/etc/options_menu/themes/scripts/om_clearList +++ b/mod/etc/options_menu/themes/scripts/om_clearList @@ -21,7 +21,7 @@ script_init clearList -disable_cmds "Clear" -enable_cmds "Refresh" +disableCmds "clear" +enableCmds "refresh" -$optionsMenu/lib/theme_manager --title INSTALLED_THEMES & +$options_menu/lib/theme_manager --title INSTALLED_THEMES & diff --git a/mod/etc/options_menu/themes/scripts/om_getThemes b/mod/etc/options_menu/themes/scripts/om_getThemes index 301322a..e9ccd63 100644 --- a/mod/etc/options_menu/themes/scripts/om_getThemes +++ b/mod/etc/options_menu/themes/scripts/om_getThemes @@ -20,6 +20,6 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init -[ ! -f "$subDummy" ] && getThemeList +[ ! -f "$sub_dummy" ] && getThemeList -$optionsMenu/lib/theme_manager --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title AVAILABLE_THEMES & +$options_menu/lib/theme_manager --commandPath $om_mod_sub_cmds/ --scriptPath $om_mod_sub_scripts --title AVAILABLE_THEMES & diff --git a/mod/etc/options_menu/themes/scripts/om_overlaythmcript b/mod/etc/options_menu/themes/scripts/om_overlaythmcript index d844197..31d2d2c 100644 --- a/mod/etc/options_menu/themes/scripts/om_overlaythmcript +++ b/mod/etc/options_menu/themes/scripts/om_overlaythmcript @@ -16,82 +16,82 @@ # along with this program. If not, see . # fontBindFix() { - [ ! -f "${FONT_PATH}/title.fnt.backup" ] && cp "${FONT_PATH}/title.fnt" "${FONT_PATH}/title.fnt.backup" - for FONT in title s m l small medium large; do - if [ "$1" == "backup" ] && [ -f "$FULL_THEME_PATH/fonts/${FONT}.fnt" ]; then - cp "$FULL_THEME_PATH/fonts/${FONT}.fnt" "${FONT_PATH}/${FONT}.fnt" - elif [ "$FONT" == "title" ]; then - cp "${FONT_PATH}/title.fnt.backup" "${FONT_PATH}/title.fnt" + [ ! -f "${font_path}/title.fnt.backup" ] && cp "${font_path}/title.fnt" "${font_path}/title.fnt.backup" + for font_name in title s m l small medium large; do + if [ "$1" == "backup" ] && [ -f "$full_theme_path/fonts/${font_name}.fnt" ]; then + cp "$full_theme_path/fonts/${font_name}.fnt" "${font_path}/${font_name}.fnt" + elif [ "$font_name" == "title" ]; then + cp "${font_path}/title.fnt.backup" "${font_path}/title.fnt" else - umount "${ORIGINAL_THEME}/resources/fonts/${UI1}/${FONT}.fnt" 2>/dev/null - rm -f "${FONT_PATH}/${FONT}.fnt" + umount "${original_theme}/resources/fonts/${ui_subdir}/${font_name}.fnt" 2>/dev/null + rm -f "${font_path}/${font_name}.fnt" fi done } themeSelector() { - local UI0="ui/$sftype-$sfregion" - local UI1="" - local SYSTEM_TYPE="$sftype" + local ui_dir="ui/$sftype-$sfregion" + local ui_subdir="" + local system_type="$sftype" case "$sftype" in hvcj) - UI0="ui/hvc" - SYSTEM_TYPE="shonen" + ui_dir="ui/hvc" + system_type="shonen" ;; nes) - UI0="clover-ui" - UI1="nes/" + ui_dir="clover-ui" + ui_subdir="nes/" if [ "$sfregion" = "jpn" ]; then - UI0="clover-ui" - UI1="hvc" - SYSTEM_TYPE="$UI1" + ui_dir="clover-ui" + ui_subdir="hvc" + system_type="$ui_subdir" fi ;; snes) - [ "$sfregion" = "jpn" ] && UI0="ui/shvc" + [ "$sfregion" = "jpn" ] && ui_dir="ui/shvc" ;; esac - local ORIGINAL_THEME="$mountpoint/usr/share/$UI0" + local original_theme="$mountpoint/usr/share/$ui_dir" - local THEMES_PATH="$mountpoint/media/$modname/themes/$SYSTEM_TYPE" - [ ! -d "$THEMES_PATH" ] && THEMES_PATH="$rootfs/usr/share/themes/$SYSTEM_TYPE" + local themes_path="$mountpoint/media/$modname/themes/$system_type" + [ ! -d "$themes_path" ] && themes_path="$rootfs/usr/share/themes/$system_type" - local STATE_PATH="$mountpoint$profilepath/$modname" - local LAST_THEME="$STATE_PATH/lastTheme" - local ACTIVE_THEME="$STATE_PATH/active_theme" - local SELECTED_THEME="CHANGEME" + local theme_state_path="$mountpoint$profilepath/$modname" + local last_theme="$theme_state_path/lastTheme" + local active_theme_file="$theme_state_path/active_theme" + local selected_theme="CHANGEME" - if [ -f "$STATE_PATH/om_themerandomizer" ]; then - SELECTED_THEME="$PREVIOUS_THEME" + if [ -f "$theme_state_path/om_themerandomizer" ]; then + selected_theme="$previous_theme" local n="0" - while [ "$SELECTED_THEME" == "$PREVIOUS_THEME" ] && [ "$n" -lt 3 ]; do + while [ "$selected_theme" == "$previous_theme" ] && [ "$n" -lt 3 ]; do n=$((n + 1)) - SELECTED_THEME="$(find "$THEMES_PATH" -maxdepth 1 -type d | shuf | head -n1 | xargs -n 1 basename)" + selected_theme="$(find "$themes_path" -maxdepth 1 -type d | shuf | head -n1 | xargs -n 1 basename)" done fi - local FULL_THEME_PATH="$THEMES_PATH/$SELECTED_THEME" + local full_theme_path="$themes_path/$selected_theme" - local FONT_PATH="$mountpoint/media/$modname/fonts" - [ ! -d "$FONT_PATH" ] && FONT_PATH="$rootfs/usr/share/fonts" - local TITLE_FONT="$FONT_PATH/title.fnt" - local TITLE_THEME_FONT="$FULL_THEME_PATH/fonts/title.fnt" + local font_path="$mountpoint/media/$modname/fonts" + [ ! -d "$font_path" ] && font_path="$rootfs/usr/share/fonts" + local title_font="$font_path/title.fnt" + local title_theme_font="$full_theme_path/fonts/title.fnt" - if [ "$SELECTED_THEME" != "$PREVIOUS_THEME" ] || [ ! -f "$LAST_THEME" ] || [ ! -f "$ACTIVE_THEME" ]; then - echo "$SELECTED_THEME" >"$LAST_THEME" - echo "$FULL_THEME_PATH" >"$ACTIVE_THEME" + if [ "$selected_theme" != "$previous_theme" ] || [ ! -f "$last_theme" ] || [ ! -f "$active_theme_file" ]; then + echo "$selected_theme" >"$last_theme" + echo "$full_theme_path" >"$active_theme_file" fi - if [ -d "$FULL_THEME_PATH/fonts" ]; then - if [ "$(cksum "$TITLE_FONT" | awk '{ print $1; }')" != "$(cksum "$TITLE_THEME_FONT" | awk '{ print $1; }')" ]; then + if [ -d "$full_theme_path/fonts" ]; then + if [ "$(cksum "$title_font" | awk '{ print $1; }')" != "$(cksum "$title_theme_font" | awk '{ print $1; }')" ]; then fontBindFix "backup" fi else fontBindFix "restore" fi - mountpoint -q "$ORIGINAL_THEME" && umount "$ORIGINAL_THEME" - [ -d "$FULL_THEME_PATH" ] && mount -t overlayfs -o lowerdir="$ORIGINAL_THEME",upperdir="$FULL_THEME_PATH" overlayfs "$ORIGINAL_THEME" + mountpoint -q "$original_theme" && umount "$original_theme" + [ -d "$full_theme_path" ] && mount -t overlayfs -o lowerdir="$original_theme",upperdir="$full_theme_path" overlayfs "$original_theme" } diff --git a/mod/etc/options_menu/themes/scripts/om_settings b/mod/etc/options_menu/themes/scripts/om_settings index 257c55b..d7722bf 100644 --- a/mod/etc/options_menu/themes/scripts/om_settings +++ b/mod/etc/options_menu/themes/scripts/om_settings @@ -21,21 +21,21 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init # theme_manager's STATE_STR checks (om_state) would otherwise each re-derive -# $state_path from scratch via om_functions/preinit - cache it once here so +# $mod_state_path from scratch via om_functions/preinit - cache it once here so # 5 toggle rows cost one lookup instead of five. Plain /tmp, no $mountpoint: # this is throwaway same-boot-session IPC, not data that needs to live inside -# a particular mounted rootfs the way e.g. diyThemePath does -echo "$state_path" > /tmp/om_state_path +# a particular mounted rootfs the way e.g. diy_theme_path does +echo "$mod_state_path" > /tmp/om_state_path -hasItemsToCleanUp "SystemCleanUp" +hasItemsToCleanUp "system_clean_up" folderThemesRow if [ -f "$b_file" ] && [ -f "$p_file" ]; then - enable_cmds "Uninstaller" - [ ! -f "$omModSettingsSpacer" ] && touch "$omModSettingsSpacer" + enableCmds "uninstaller" + [ ! -f "$om_mod_settings_spacer" ] && touch "$om_mod_settings_spacer" else - disable_cmds "Uninstaller" - [ ! -f "$enableSystemCleanUp" ] && remove "$omModSettingsSpacer" + disableCmds "uninstaller" + [ ! -f "$enable_system_clean_up" ] && remove "$om_mod_settings_spacer" fi -$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS --layout list & +$options_menu/lib/theme_manager --commandPath $om_mod_settings_cmds/ --scriptPath $om_mod_settings_scripts --title THEME_SETTINGS --layout list & diff --git a/mod/etc/options_menu/themes/scripts/om_themeCreator b/mod/etc/options_menu/themes/scripts/om_themeCreator index d15e505..0e519db 100644 --- a/mod/etc/options_menu/themes/scripts/om_themeCreator +++ b/mod/etc/options_menu/themes/scripts/om_themeCreator @@ -22,4 +22,4 @@ script_init diyThemeChecker -$optionsMenu/lib/theme_manager --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title DIY_THEME & +$options_menu/lib/theme_manager --commandPath $om_mod_gen_cmds/ --scriptPath $om_mod_gen_scripts --title DIY_THEME & diff --git a/mod/etc/options_menu/themes/scripts/om_themescript b/mod/etc/options_menu/themes/scripts/om_themescript index 4bc4277..48538de 100644 --- a/mod/etc/options_menu/themes/scripts/om_themescript +++ b/mod/etc/options_menu/themes/scripts/om_themescript @@ -22,7 +22,7 @@ musicHackBind() { if [ "$sftype" = "nes" ]; then [ -f "$1" ] && mount_bind "$1" "$3/bgm_boot.wav" || return 1 else - [ -f "$SILENT_WAV" ] && mount_bind "$SILENT_WAV" "$3/bgm_boot.wav" || return 1 + [ -f "$silent_wav" ] && mount_bind "$silent_wav" "$3/bgm_boot.wav" || return 1 fi [ -f "$2" ] && mount_bind "$2" "$3/bgm_home.wav" || return 1 @@ -30,16 +30,16 @@ musicHackBind() { } musicHackUnbind() { - AUDIO_FILES_ARR="bgm_boot bgm_home se_sys_bounce se_sys_cancel se_sys_click se_sys_click_game se_sys_cursor se_sys_lock se_sys_ng se_sys_push se_sys_smoke se_sys_unlock" - local CURRENT_MOUNTS - CURRENT_MOUNTS="$(cat /proc/mounts)" + audio_files="bgm_boot bgm_home se_sys_bounce se_sys_cancel se_sys_click se_sys_click_game se_sys_cursor se_sys_lock se_sys_ng se_sys_push se_sys_smoke se_sys_unlock" + local current_mounts + current_mounts="$(cat /proc/mounts)" - for WAVE in $AUDIO_FILES_ARR; do - case "$CURRENT_MOUNTS" in - *" ${ORIGINAL_THEME_AUDIO}/${WAVE}.wav "*) + for wav_name in $audio_files; do + case "$current_mounts" in + *" ${original_theme_audio}/${wav_name}.wav "*) # a folder's music can sit on top of the theme's own bind - while grep -q " ${ORIGINAL_THEME_AUDIO}/${WAVE}.wav " /proc/mounts; do - umount "${ORIGINAL_THEME_AUDIO}/${WAVE}.wav" || break + while grep -q " ${original_theme_audio}/${wav_name}.wav " /proc/mounts; do + umount "${original_theme_audio}/${wav_name}.wav" || break done ;; esac @@ -47,50 +47,50 @@ musicHackUnbind() { } fontBindFix() { - [ ! -f "${FONT_PATH}/title.fnt.backup" ] && cp "${FONT_PATH}/title.fnt" "${FONT_PATH}/title.fnt.backup" - for FONT in title s m l small medium large; do - if [ "$1" == "backup" ] && [ -f "$FULL_THEME_PATH/fonts/${FONT}.fnt" ]; then - cp "$FULL_THEME_PATH/fonts/${FONT}.fnt" "${FONT_PATH}/${FONT}.fnt" - elif [ "$FONT" == "title" ]; then - cp "${FONT_PATH}/title.fnt.backup" "${FONT_PATH}/title.fnt" + [ ! -f "${font_path}/title.fnt.backup" ] && cp "${font_path}/title.fnt" "${font_path}/title.fnt.backup" + for font_name in title s m l small medium large; do + if [ "$1" == "backup" ] && [ -f "$full_theme_path/fonts/${font_name}.fnt" ]; then + cp "$full_theme_path/fonts/${font_name}.fnt" "${font_path}/${font_name}.fnt" + elif [ "$font_name" == "title" ]; then + cp "${font_path}/title.fnt.backup" "${font_path}/title.fnt" else - umount "${ORIGINAL_THEME}/resources/fonts/${UI1}/${FONT}.fnt" 2>/dev/null - rm -f "${FONT_PATH}/${FONT}.fnt" + umount "${original_theme}/resources/fonts/${ui_subdir}/${font_name}.fnt" 2>/dev/null + rm -f "${font_path}/${font_name}.fnt" fi done } assignedFolderTheme() { - local LINE - [ -n "$1" ] && [ -f "$STATE_PATH/folder_themes" ] || return 1 - while IFS= read -r LINE; do - if [ "${LINE%=*}" = "$1" ] && [ -d "$THEMES_PATH/${LINE##*=}" ]; then - echo "${LINE##*=}" + local mapping_line + [ -n "$1" ] && [ -f "$theme_state_path/folder_themes" ] || return 1 + while IFS= read -r mapping_line; do + if [ "${mapping_line%=*}" = "$1" ] && [ -d "$themes_path/${mapping_line##*=}" ]; then + echo "${mapping_line##*=}" return 0 fi - done <"$STATE_PATH/folder_themes" + done <"$theme_state_path/folder_themes" return 1 } assignedFolderMusic() { - local LINE - [ -n "$1" ] && [ -f "$STATE_PATH/folder_music" ] || return 1 - while IFS= read -r LINE; do - if [ "${LINE%=*}" = "$1" ] && [ -f "$MUSIC_PATH/${LINE##*=}" ]; then - echo "${LINE##*=}" + local mapping_line + [ -n "$1" ] && [ -f "$theme_state_path/folder_music" ] || return 1 + while IFS= read -r mapping_line; do + if [ "${mapping_line%=*}" = "$1" ] && [ -f "$music_path/${mapping_line##*=}" ]; then + echo "${mapping_line##*=}" return 0 fi - done <"$STATE_PATH/folder_music" + done <"$theme_state_path/folder_music" return 1 } # the entry for folder $1 inside its parent folder (not a Back/Home shortcut to it) folderEntry() { - local DESKTOP - for DESKTOP in "$GAME_STORAGE"/*/CLV-S-00"$1"/CLV-S-00"$1".desktop; do - [ -f "$DESKTOP" ] || continue - grep -q -e '^Name=Back$' -e '^Name=Home$' "$DESKTOP" && continue - echo "$DESKTOP" + local folder_desktop + for folder_desktop in "$game_storage"/*/CLV-S-00"$1"/CLV-S-00"$1".desktop; do + [ -f "$folder_desktop" ] || continue + grep -q -e '^Name=Back$' -e '^Name=Home$' "$folder_desktop" && continue + echo "$folder_desktop" return 0 done return 1 @@ -98,162 +98,162 @@ folderEntry() { # own assignment, else a theme named after the folder, else whatever its parent folder gets folderTheme() { - local CODE="$1" - local DESKTOP - local KEY - local DEPTH=0 - if [ "$CODE" == "000" ]; then + local folder_code="$1" + local folder_desktop + local folder_key + local level=0 + if [ "$folder_code" == "000" ]; then assignedFolderTheme "home" return fi - while [ "$CODE" != "000" ] && [ "$DEPTH" -lt 16 ]; do - DEPTH=$((DEPTH + 1)) - DESKTOP="$(folderEntry "$CODE")" || return 1 - KEY="$(sed -n 's/^Name=//p' "$DESKTOP" | head -n1 | sed 's/ /_/g' | awk '{print tolower($0)}')" - assignedFolderTheme "$KEY" && return 0 - [ -n "$KEY" ] && [ -d "$THEMES_PATH/$KEY" ] && echo "$KEY" && return 0 - CODE="$(basename "$(dirname "$(dirname "$DESKTOP")")")" + while [ "$folder_code" != "000" ] && [ "$level" -lt 16 ]; do + level=$((level + 1)) + folder_desktop="$(folderEntry "$folder_code")" || return 1 + folder_key="$(sed -n 's/^Name=//p' "$folder_desktop" | head -n1 | sed 's/ /_/g' | awk '{print tolower($0)}')" + assignedFolderTheme "$folder_key" && return 0 + [ -n "$folder_key" ] && [ -d "$themes_path/$folder_key" ] && echo "$folder_key" && return 0 + folder_code="$(basename "$(dirname "$(dirname "$folder_desktop")")")" done return 1 } folderMusic() { - local CODE="$1" - local DESKTOP - local KEY - local DEPTH=0 - if [ "$CODE" == "000" ]; then + local folder_code="$1" + local folder_desktop + local folder_key + local level=0 + if [ "$folder_code" == "000" ]; then assignedFolderMusic "home" return fi - while [ "$CODE" != "000" ] && [ "$DEPTH" -lt 16 ]; do - DEPTH=$((DEPTH + 1)) - DESKTOP="$(folderEntry "$CODE")" || return 1 - KEY="$(sed -n 's/^Name=//p' "$DESKTOP" | head -n1 | sed 's/ /_/g' | awk '{print tolower($0)}')" - assignedFolderMusic "$KEY" && return 0 - CODE="$(basename "$(dirname "$(dirname "$DESKTOP")")")" + while [ "$folder_code" != "000" ] && [ "$level" -lt 16 ]; do + level=$((level + 1)) + folder_desktop="$(folderEntry "$folder_code")" || return 1 + folder_key="$(sed -n 's/^Name=//p' "$folder_desktop" | head -n1 | sed 's/ /_/g' | awk '{print tolower($0)}')" + assignedFolderMusic "$folder_key" && return 0 + folder_code="$(basename "$(dirname "$(dirname "$folder_desktop")")")" done return 1 } themeSelector() { - local FOLDER_ID - local GAME_STORAGE - local PREVIOUS_THEME - local UI0="ui/$sftype-$sfregion" - local UI1="" - local SYSTEM_TYPE="$sftype" + local folder_id + local game_storage + local previous_theme + local ui_dir="ui/$sftype-$sfregion" + local ui_subdir="" + local system_type="$sftype" case "$sftype" in hvcj) - UI0="ui/hvc" - SYSTEM_TYPE="shonen" + ui_dir="ui/hvc" + system_type="shonen" ;; nes) - UI0="clover-ui" - UI1="nes/" + ui_dir="clover-ui" + ui_subdir="nes/" if [ "$sfregion" = "jpn" ]; then - UI0="clover-ui" - UI1="hvc" - SYSTEM_TYPE="$UI1" + ui_dir="clover-ui" + ui_subdir="hvc" + system_type="$ui_subdir" fi ;; snes) - [ "$sfregion" = "jpn" ] && UI0="ui/shvc" + [ "$sfregion" = "jpn" ] && ui_dir="ui/shvc" ;; esac - local ORIGINAL_THEME="$mountpoint/usr/share/$UI0" - local ORIGINAL_THEME_AUDIO="$ORIGINAL_THEME/resources/sounds/hvc" - local THEMES_PATH="$mountpoint/media/$modname/themes/$SYSTEM_TYPE" - [ ! -d "$THEMES_PATH" ] && THEMES_PATH="$rootfs/usr/share/themes/$SYSTEM_TYPE" - local THEME_DIR_LIST - THEME_DIR_LIST="$(find "$THEMES_PATH" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sed 's#^.*/##')" - STATE_PATH="$mountpoint$profilepath/$modname" - FOLDER_ID="$(cat "$STATE_PATH/menu")" - local ACTIVE_FOLDER="CLV-S-00${FOLDER_ID}" - local LAST_THEME="$STATE_PATH/lastTheme" - local ACTIVE_THEME="$STATE_PATH/active_theme" - [ -f "$LAST_THEME" ] && PREVIOUS_THEME=$(cat "$LAST_THEME") + local original_theme="$mountpoint/usr/share/$ui_dir" + local original_theme_audio="$original_theme/resources/sounds/hvc" + local themes_path="$mountpoint/media/$modname/themes/$system_type" + [ ! -d "$themes_path" ] && themes_path="$rootfs/usr/share/themes/$system_type" + local theme_dir_list + theme_dir_list="$(find "$themes_path" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sed 's#^.*/##')" + local theme_state_path="$mountpoint$profilepath/$modname" + folder_id="$(cat "$theme_state_path/menu")" + local active_folder="CLV-S-00${folder_id}" + local last_theme="$theme_state_path/lastTheme" + local active_theme_file="$theme_state_path/active_theme" + [ -f "$last_theme" ] && previous_theme=$(cat "$last_theme") - local SELECTED_THEME="CHANGEME" - local DIY_THEME="$mountpoint/tmp/diy_theme" - local FULL_THEME_PATH="$THEMES_PATH/$SELECTED_THEME" - local FONT_PATH="$mountpoint/media/$modname/fonts" - [ ! -d "$FONT_PATH" ] && FONT_PATH="$rootfs/usr/share/fonts" - local TITLE_FONT="$FONT_PATH/title.fnt" - local THEME_FONT_PATH="$FULL_THEME_PATH/fonts" + local selected_theme="CHANGEME" + local diy_theme="$mountpoint/tmp/diy_theme" + local full_theme_path="$themes_path/$selected_theme" + local font_path="$mountpoint/media/$modname/fonts" + [ ! -d "$font_path" ] && font_path="$rootfs/usr/share/fonts" + local title_font="$font_path/title.fnt" + local theme_font_path="$full_theme_path/fonts" - SILENT_WAV="$rootfs/usr/share/themes/bgm_boot.wav" - MUSIC_PATH="$mountpoint/media/$modname/music_menu" - find "$MUSIC_PATH" -maxdepth 1 -type f -iname "*.wav" 2>/dev/null | read -r _ || MUSIC_PATH="$rootfs/usr/share/music_menu" - BGM_HOME="" - GAME_STORAGE="$(findGameStorage)" + local silent_wav="$rootfs/usr/share/themes/bgm_boot.wav" + local music_path="$mountpoint/media/$modname/music_menu" + find "$music_path" -maxdepth 1 -type f -iname "*.wav" 2>/dev/null | read -r _ || music_path="$rootfs/usr/share/music_menu" + local bgm_home="" + game_storage="$(findGameStorage)" - find "$GAME_STORAGE" -maxdepth 3 -type f -name "$ACTIVE_FOLDER.desktop" | sort -u | while IFS= read -r _; do - if [ -f "$STATE_PATH/om_themerandomizer" ]; then - SELECTED_THEME="$PREVIOUS_THEME" + find "$game_storage" -maxdepth 3 -type f -name "$active_folder.desktop" | sort -u | while IFS= read -r _; do + if [ -f "$theme_state_path/om_themerandomizer" ]; then + selected_theme="$previous_theme" local n="0" - while [ "$SELECTED_THEME" == "$PREVIOUS_THEME" ] && [ "$n" -lt 3 ]; do + while [ "$selected_theme" == "$previous_theme" ] && [ "$n" -lt 3 ]; do n=$((n + 1)) - SELECTED_THEME="$(printf '%s\n' "$THEME_DIR_LIST" | shuf | head -n1)" + selected_theme="$(printf '%s\n' "$theme_dir_list" | shuf | head -n1)" done fi - [ "$FOLDER_ID" == "000" ] && [ -d "$THEMES_PATH/default" ] && SELECTED_THEME="default" - if [ -f "$STATE_PATH/om_themeperfolder" ]; then - local FOLDER_THEME - FOLDER_THEME="$(folderTheme "$FOLDER_ID")" - [ -n "$FOLDER_THEME" ] && SELECTED_THEME="$FOLDER_THEME" + [ "$folder_id" == "000" ] && [ -d "$themes_path/default" ] && selected_theme="default" + if [ -f "$theme_state_path/om_themeperfolder" ]; then + local assigned_theme + assigned_theme="$(folderTheme "$folder_id")" + [ -n "$assigned_theme" ] && selected_theme="$assigned_theme" fi - local FOLDER_MUSIC="" - [ -f "$STATE_PATH/om_musicperfolder" ] && FOLDER_MUSIC="$(folderMusic "$FOLDER_ID")" + local assigned_music="" + [ -f "$theme_state_path/om_musicperfolder" ] && assigned_music="$(folderMusic "$folder_id")" - FULL_THEME_PATH="$THEMES_PATH/$SELECTED_THEME" + full_theme_path="$themes_path/$selected_theme" - if find "$DIY_THEME" -type f 2>/dev/null | read -r _; then - FULL_THEME_PATH="$DIY_THEME" + if find "$diy_theme" -type f 2>/dev/null | read -r _; then + full_theme_path="$diy_theme" fi - if [ "$SELECTED_THEME" != "$PREVIOUS_THEME" ] || [ ! -f "$LAST_THEME" ] || [ ! -f "$ACTIVE_THEME" ]; then - echo "$SELECTED_THEME" >"$LAST_THEME" - echo "$FULL_THEME_PATH" >"$ACTIVE_THEME" + if [ "$selected_theme" != "$previous_theme" ] || [ ! -f "$last_theme" ] || [ ! -f "$active_theme_file" ]; then + echo "$selected_theme" >"$last_theme" + echo "$full_theme_path" >"$active_theme_file" fi - if [ -d "$THEME_FONT_PATH" ]; then - cmp -s "$TITLE_FONT" "$THEME_FONT_PATH/title.fnt" || fontBindFix "backup" + if [ -d "$theme_font_path" ]; then + cmp -s "$title_font" "$theme_font_path/title.fnt" || fontBindFix "backup" else fontBindFix "restore" fi # lazy, since fontFix binds title.font on top of the overlay after every chmenu - while grep -q " $ORIGINAL_THEME overlayfs " /proc/mounts; do - umount -l "$ORIGINAL_THEME" || break + while grep -q " $original_theme overlayfs " /proc/mounts; do + umount -l "$original_theme" || break done musicHackUnbind - cd "$THEMES_PATH/$PREVIOUS_THEME" && find ./* -type f ! -name "*.wav" ! -path "./fonts/*" | cut -c3- | while IFS= read -r UMOUNT_FILE; do - umount "$ORIGINAL_THEME/$UMOUNT_FILE" + cd "$themes_path/$previous_theme" && find ./* -type f ! -name "*.wav" ! -path "./fonts/*" | cut -c3- | while IFS= read -r umount_file; do + umount "$original_theme/$umount_file" done - if [ "$FULL_THEME_PATH" != "$DIY_THEME" ] && [ -f "$FULL_THEME_PATH/needs_overlay" ]; then - [ -d "$FULL_THEME_PATH" ] && mount -t overlayfs -o lowerdir="$ORIGINAL_THEME",upperdir="$FULL_THEME_PATH" overlayfs "$ORIGINAL_THEME" + if [ "$full_theme_path" != "$diy_theme" ] && [ -f "$full_theme_path/needs_overlay" ]; then + [ -d "$full_theme_path" ] && mount -t overlayfs -o lowerdir="$original_theme",upperdir="$full_theme_path" overlayfs "$original_theme" else - if [ -z "$FOLDER_MUSIC" ] && [ -f "$STATE_PATH/om_audiorandomizer" ] && [ -n "$(ls -A "$MUSIC_PATH")" ]; then - [ -z "$BGM_HOME" ] && BGM_HOME="$(find "$MUSIC_PATH" -maxdepth 1 -type f -iname "*.wav" | shuf | head -n1)" - if [ "$FOLDER_ID" = "000" ]; then - musicHackBind "$BGM_HOME" "$BGM_HOME" "$ORIGINAL_THEME_AUDIO" + if [ -z "$assigned_music" ] && [ -f "$theme_state_path/om_audiorandomizer" ] && [ -n "$(ls -A "$music_path")" ]; then + [ -z "$bgm_home" ] && bgm_home="$(find "$music_path" -maxdepth 1 -type f -iname "*.wav" | shuf | head -n1)" + if [ "$folder_id" = "000" ]; then + musicHackBind "$bgm_home" "$bgm_home" "$original_theme_audio" else - [ -f "$STATE_PATH/om_advmusichack" ] && [ -z "$(find "$FULL_THEME_PATH" -type f -name "*.wav")" ] && musicHackBind "$BGM_HOME" "$BGM_HOME" "$ORIGINAL_THEME_AUDIO" + [ -f "$theme_state_path/om_advmusichack" ] && [ -z "$(find "$full_theme_path" -type f -name "*.wav")" ] && musicHackBind "$bgm_home" "$bgm_home" "$original_theme_audio" fi fi - local SKIP_BGM="none" - [ -n "$FOLDER_MUSIC" ] && SKIP_BGM="bgm_*.wav" - cd "$FULL_THEME_PATH" && find . -type f ! -path "./fonts/*" ! -name "$SKIP_BGM" | while IFS= read -r THEME_FILE; do - mount_bind "$FULL_THEME_PATH/$THEME_FILE" "$ORIGINAL_THEME/$THEME_FILE" + local skip_bgm="none" + [ -n "$assigned_music" ] && skip_bgm="bgm_*.wav" + cd "$full_theme_path" && find . -type f ! -path "./fonts/*" ! -name "$skip_bgm" | while IFS= read -r theme_file; do + mount_bind "$full_theme_path/$theme_file" "$original_theme/$theme_file" done fi - [ -n "$FOLDER_MUSIC" ] && musicHackBind "$MUSIC_PATH/$FOLDER_MUSIC" "$MUSIC_PATH/$FOLDER_MUSIC" "$ORIGINAL_THEME_AUDIO" + [ -n "$assigned_music" ] && musicHackBind "$music_path/$assigned_music" "$music_path/$assigned_music" "$original_theme_audio" cd - >/dev/null || exit break diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 7ccc0dd..4e7d23a 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -21,135 +21,135 @@ source $mountpoint/etc/options_menu/scripts/om_functions script_init modtitle="themes" -omTitle="THEME_SELECTOR" +om_title="THEME_SELECTOR" b_file="$mountpoint/etc/preinit.d/b8024_themeselector" p_file="$mountpoint/etc/preinit.d/p8024_themeselector" fontfix_file="$mountpoint/etc/preinit.d/b8025_fontfix" chmenu_file="$root_bin/chmenu" -has_folders="${gStorage}/001" +has_folders="${game_storage}/001" # Options Menu -omMod="$optionsMenu/$modtitle" -omModImgs="$optionsMenu/images" -omModCommands="$omMod/commands" -omModScripts="$omMod/scripts" -omModFnts="$omMod/fonts" # verify -omModSub="$omMod/downloads" -omModSubCmds="$omModSub/commands" -omModSubScripts="$omModSub/scripts" -omModGen="$omMod/creator" -omModGenCmds="$omModGen/commands" -omModGenScripts="$omModGen/scripts" -omModSprites="$omModGen/sprites" -omModSpritesAssets="$omModSprites/assets" -omModMario="$omModSprites/mario" -omModLuigi="$omModSprites/luigi" -omModWall="$omModSprites/wall" -omModWallN="$omModSprites/wall_near" -omModWallF="$omModSprites/wall_far" -omModPacked="$omModSprites/packed" -omModColors="$omModSprites/colors" -omModColorsNes="$omModSprites/colors_nes" -omModPole="$omModSprites/pole" -omModPackedNes="$omModSprites/packed_nes" -omModSbm="$omModSprites/sbm" -omModMarioAssets="$omModSpritesAssets/mario" -omModLuigiAssets="$omModSpritesAssets/luigi" -omModWallAssets="$omModSpritesAssets/wall" -omModWallNAssets="$omModSpritesAssets/wall_near" -omModWallFAssets="$omModSpritesAssets/wall_far" -omModPackedAssets="$omModSpritesAssets/packed" -omModColorsAssets="$omModSpritesAssets/colors" -omModColorsNesAssets="$omModSpritesAssets/colors_nes" -omModPoleAssets="$omModSpritesAssets/pole" -omModPackedNesAssets="$omModSpritesAssets/packed_nes" -omModSbmAssets="$omModSpritesAssets/smb" -omModSettings="$omMod/settings" -omModSettingsCmds="$omModSettings/commands" -omModSettingsScripts="$omModSettings/scripts" -omModLicense="$omModScripts/om_license" -diyThemePath="$mountpoint/tmp/diy_theme" -diyResources="$diyThemePath/resources" -diyLayout="$diyResources/layout/asset/sprite" -omModLicense="$omModScripts/om_license" -thmPreview="$omModSub/images" -omThemeScript="$omModScripts/om_themescript" -omOverlayThemeScript="$omModScripts/om_overlaythmcript" - -om_themerandomizer="$state_path/om_themerandomizer" -om_themeperfolder="$state_path/om_themeperfolder" -om_musicperfolder="$state_path/om_musicperfolder" -om_folderthemes="$state_path/folder_themes" -om_foldermusic="$state_path/folder_music" -folderThemesTmp="/tmp/om_folderthemes" -om_audiorandomizer="$state_path/om_audiorandomizer" -om_advmusichack="$state_path/om_advmusichack" -om_firstrun="$omMod/om_firstrun" -om_theme_count="$state_path/om_theme_count" +om_mod="$options_menu/$modtitle" +om_mod_imgs="$options_menu/images" +om_mod_commands="$om_mod/commands" +om_mod_scripts="$om_mod/scripts" +om_mod_fnts="$om_mod/fonts" # verify +om_mod_sub="$om_mod/downloads" +om_mod_sub_cmds="$om_mod_sub/commands" +om_mod_sub_scripts="$om_mod_sub/scripts" +om_mod_gen="$om_mod/creator" +om_mod_gen_cmds="$om_mod_gen/commands" +om_mod_gen_scripts="$om_mod_gen/scripts" +om_mod_sprites="$om_mod_gen/sprites" +om_mod_sprites_assets="$om_mod_sprites/assets" +om_mod_mario="$om_mod_sprites/mario" +om_mod_luigi="$om_mod_sprites/luigi" +om_mod_wall="$om_mod_sprites/wall" +om_mod_wall_n="$om_mod_sprites/wall_near" +om_mod_wall_f="$om_mod_sprites/wall_far" +om_mod_packed="$om_mod_sprites/packed" +om_mod_colors="$om_mod_sprites/colors" +om_mod_colors_nes="$om_mod_sprites/colors_nes" +om_mod_pole="$om_mod_sprites/pole" +om_mod_packed_nes="$om_mod_sprites/packed_nes" +om_mod_sbm="$om_mod_sprites/sbm" +om_mod_mario_assets="$om_mod_sprites_assets/mario" +om_mod_luigi_assets="$om_mod_sprites_assets/luigi" +om_mod_wall_assets="$om_mod_sprites_assets/wall" +om_mod_wall_n_assets="$om_mod_sprites_assets/wall_near" +om_mod_wall_f_assets="$om_mod_sprites_assets/wall_far" +om_mod_packed_assets="$om_mod_sprites_assets/packed" +om_mod_colors_assets="$om_mod_sprites_assets/colors" +om_mod_colors_nes_assets="$om_mod_sprites_assets/colors_nes" +om_mod_pole_assets="$om_mod_sprites_assets/pole" +om_mod_packed_nes_assets="$om_mod_sprites_assets/packed_nes" +om_mod_sbm_assets="$om_mod_sprites_assets/smb" +om_mod_settings="$om_mod/settings" +om_mod_settings_cmds="$om_mod_settings/commands" +om_mod_settings_scripts="$om_mod_settings/scripts" +om_mod_license="$om_mod_scripts/om_license" +diy_theme_path="$mountpoint/tmp/diy_theme" +diy_resources="$diy_theme_path/resources" +diy_layout="$diy_resources/layout/asset/sprite" +om_mod_license="$om_mod_scripts/om_license" +thm_preview="$om_mod_sub/images" +om_theme_script="$om_mod_scripts/om_themescript" +om_overlay_theme_script="$om_mod_scripts/om_overlaythmcript" + +om_themerandomizer="$mod_state_path/om_themerandomizer" +om_themeperfolder="$mod_state_path/om_themeperfolder" +om_musicperfolder="$mod_state_path/om_musicperfolder" +om_folderthemes="$mod_state_path/folder_themes" +om_foldermusic="$mod_state_path/folder_music" +folder_themes_tmp="/tmp/om_folderthemes" +om_audiorandomizer="$mod_state_path/om_audiorandomizer" +om_advmusichack="$mod_state_path/om_advmusichack" +om_firstrun="$om_mod/om_firstrun" +om_theme_count="$mod_state_path/om_theme_count" #### SEPARATORS -omDummy="$omModCommands/c0000_0000" -subDummy="$omModSubCmds/c0000_0000" -omModSpacer="$omModCommands/c0000_separator" -omModSettingsSpacer="$omModSettings/c0001_separator" +om_dummy="$om_mod_commands/c0000_0000" +sub_dummy="$om_mod_sub_cmds/c0000_0000" +om_mod_spacer="$om_mod_commands/c0000_separator" +om_mod_settings_spacer="$om_mod_settings/c0001_separator" #### ENABLE/DISABLE -enableDownloads="$omModCommands/c0000_2_download" -disableDownloads="$omModCommands/_2_download" -enableClear="$omModCommands/c0000_5_remove" -disableClear="$omModCommands/_5_remove" -enableSettings="$omModCommands/c0000_4_options" -disableSettings="$omModCommands/_4_options" -enableAudioSetting="$omModSettingsCmds/c0001_audiorandomizer" -disableAudioSetting="$omModSettingsCmds/_audiorandomizer" -enableAdvancedMusicHack="$omModSettingsCmds/c0001_audiorandomizerall" -disableAdvancedMusicHack="$omModSettingsCmds/_audiorandomizerall" -enableFolderThemes="$omModSettingsCmds/c0000_themeperfolder_1assign" -disableFolderThemes="$omModSettingsCmds/_themeperfolder_1assign" -enableMusicPerFolder="$omModSettingsCmds/c0001_musicperfolder" -disableMusicPerFolder="$omModSettingsCmds/_musicperfolder" -enableFolderMusic="$omModSettingsCmds/c0001_musicperfolder_assign" -disableFolderMusic="$omModSettingsCmds/_musicperfolder_assign" -enableThemeSetting="$omModSettingsCmds/c0000_themerandomizer" -disableThemeSetting="$omModSettingsCmds/_themerandomizer" -enableSystemCleanUp="$omModSettingsCmds/c0001_systemcleanup" -disableSystemCleanUp="$omModSettingsCmds/_systemcleanup" -enableThemeGenerator="$omModCommands/c0000_3_generatetheme" -disableThemeGenerator="$omModCommands/_3_generatetheme" -enableUninstaller="$omModSettingsCmds/c0001_uninstaller" -disableUninstaller="$omModSettingsCmds/_uninstaller" -enableDiY="$omModGenCmds/c0000_3_apply" -disableDiY="$omModGenCmds/_3_apply" -enableDelDiY="$omModGenCmds/c0000_5_remove" -disableDelDiY="$omModGenCmds/_5_remove" -enablePreview="$omModGenCmds/c0000_4_preview" -disablePreview="$omModGenCmds/_4_preview" -enablePole="$omModGenCmds/c0006_pole" -disablePole="$omModGenCmds/_pole" -enableColor="$omModGenCmds/c0006_colors" -disableColor="$omModGenCmds/_colors" -enableMario="$omModGenCmds/c0002_mario" -disableMario="$omModGenCmds/_mario" -enableLuigi="$omModGenCmds/c0003_luigi" -disableLuigi="$omModGenCmds/_luigi" -enableSmb="$omModGenCmds/c0002_sbm" -disableSmb="$omModGenCmds/_sbm" -enablePackedNes="$omModGenCmds/c0001_packed_nes" -disablePackedNes="$omModGenCmds/_packed_nes" -enableColorsNes="$omModGenCmds/c0006_colors_nes" -disableColorsNes="$omModGenCmds/_colors_nes" -enablePacked="$omModGenCmds/c0001_packed" -disablePacked="$omModGenCmds/_packed" -enableWall="$omModGenCmds/c0001_wall" -disableWall="$omModGenCmds/_wall" -enableWallFar="$omModGenCmds/c0005_wall_far" -disableWallFar="$omModGenCmds/_wall_far" -enableWallNear="$omModGenCmds/c0004_wall_near" -disableWallNear="$omModGenCmds/_wall_near" -listName="-list-v2.tar.gz" +enable_downloads="$om_mod_commands/c0000_2_download" +disable_downloads="$om_mod_commands/_2_download" +enable_clear="$om_mod_commands/c0000_5_remove" +disable_clear="$om_mod_commands/_5_remove" +enable_settings="$om_mod_commands/c0000_4_options" +disable_settings="$om_mod_commands/_4_options" +enable_audio_setting="$om_mod_settings_cmds/c0001_audiorandomizer" +disable_audio_setting="$om_mod_settings_cmds/_audiorandomizer" +enable_advanced_music_hack="$om_mod_settings_cmds/c0001_audiorandomizerall" +disable_advanced_music_hack="$om_mod_settings_cmds/_audiorandomizerall" +enable_folder_themes="$om_mod_settings_cmds/c0000_themeperfolder_1assign" +disable_folder_themes="$om_mod_settings_cmds/_themeperfolder_1assign" +enable_music_per_folder="$om_mod_settings_cmds/c0001_musicperfolder" +disable_music_per_folder="$om_mod_settings_cmds/_musicperfolder" +enable_folder_music="$om_mod_settings_cmds/c0001_musicperfolder_assign" +disable_folder_music="$om_mod_settings_cmds/_musicperfolder_assign" +enable_theme_setting="$om_mod_settings_cmds/c0000_themerandomizer" +disable_theme_setting="$om_mod_settings_cmds/_themerandomizer" +enable_system_clean_up="$om_mod_settings_cmds/c0001_systemcleanup" +disable_system_clean_up="$om_mod_settings_cmds/_systemcleanup" +enable_theme_generator="$om_mod_commands/c0000_3_generatetheme" +disable_theme_generator="$om_mod_commands/_3_generatetheme" +enable_uninstaller="$om_mod_settings_cmds/c0001_uninstaller" +disable_uninstaller="$om_mod_settings_cmds/_uninstaller" +enable_diy="$om_mod_gen_cmds/c0000_3_apply" +disable_diy="$om_mod_gen_cmds/_3_apply" +enable_del_diy="$om_mod_gen_cmds/c0000_5_remove" +disable_del_diy="$om_mod_gen_cmds/_5_remove" +enable_preview="$om_mod_gen_cmds/c0000_4_preview" +disable_preview="$om_mod_gen_cmds/_4_preview" +enable_pole="$om_mod_gen_cmds/c0006_pole" +disable_pole="$om_mod_gen_cmds/_pole" +enable_color="$om_mod_gen_cmds/c0006_colors" +disable_color="$om_mod_gen_cmds/_colors" +enable_mario="$om_mod_gen_cmds/c0002_mario" +disable_mario="$om_mod_gen_cmds/_mario" +enable_luigi="$om_mod_gen_cmds/c0003_luigi" +disable_luigi="$om_mod_gen_cmds/_luigi" +enable_smb="$om_mod_gen_cmds/c0002_sbm" +disable_smb="$om_mod_gen_cmds/_sbm" +enable_packed_nes="$om_mod_gen_cmds/c0001_packed_nes" +disable_packed_nes="$om_mod_gen_cmds/_packed_nes" +enable_colors_nes="$om_mod_gen_cmds/c0006_colors_nes" +disable_colors_nes="$om_mod_gen_cmds/_colors_nes" +enable_packed="$om_mod_gen_cmds/c0001_packed" +disable_packed="$om_mod_gen_cmds/_packed" +enable_wall="$om_mod_gen_cmds/c0001_wall" +disable_wall="$om_mod_gen_cmds/_wall" +enable_wall_far="$om_mod_gen_cmds/c0005_wall_far" +disable_wall_far="$om_mod_gen_cmds/_wall_far" +enable_wall_near="$om_mod_gen_cmds/c0004_wall_near" +disable_wall_near="$om_mod_gen_cmds/_wall_near" +list_name="-list-v2.tar.gz" # SHARED thm_chk="$mountpoint$profilepath/$modname/active_theme" -system="$(echo "$s_type" | awk '{print toupper($0)}')" +system_name="$(echo "$s_type" | awk '{print toupper($0)}')" om_mod_path="$nand_path/$modtitle" theme_path="$usb_path/$modtitle/$s_type" [ ! -d "$theme_path" ] && theme_path="$nand_path/$modtitle/$s_type" @@ -164,15 +164,15 @@ snes_theme_path="${om_mod_path}/snes" #### ARRAYS diy_asset_array="diy_packed diy_wall diy_wall_far diy_wall_near diy_mario diy_luigi diy_colors diy_pole" # verify -del_shared="$nes_theme_path $disableSmb $disablePackedNes $disableColorsNes $omModPackedNes $omModSbm $omModColorsNes ${omModImgs}/smb.png ${omModImgs}/packed_nes.png $disableColor $omModColors" +del_shared="$nes_theme_path $disable_smb $disable_packed_nes $disable_colors_nes $om_mod_packed_nes $om_mod_sbm $om_mod_colors_nes ${om_mod_imgs}/smb.png ${om_mod_imgs}/packed_nes.png $disable_color $om_mod_colors" -del_on_nes="$snes_theme_path $shonen_theme_path $disableColor $disablePole $disableMario $disableLuigi $disablePacked $disableWall $disableWallFar $disableWallNear $omModPole $omModMario $omModLuigi $omModWall $omModWallN $omModWallF $omModPacked $omModColors ${omModImgs}/luigi.png ${omModImgs}/mario.png ${omModImgs}/packed.png ${omModImgs}/pole.png ${omModImgs}/wall*.png" +del_on_nes="$snes_theme_path $shonen_theme_path $disable_color $disable_pole $disable_mario $disable_luigi $disable_packed $disable_wall $disable_wall_far $disable_wall_near $om_mod_pole $om_mod_mario $om_mod_luigi $om_mod_wall $om_mod_wall_n $om_mod_wall_f $om_mod_packed $om_mod_colors ${om_mod_imgs}/luigi.png ${om_mod_imgs}/mario.png ${om_mod_imgs}/packed.png ${om_mod_imgs}/pole.png ${om_mod_imgs}/wall*.png" del_on_snes="$shonen_theme_path $del_shared" del_on_shonen="$snes_theme_path $del_shared" items_to_delete="$del_on_snes" # shellcheck disable=SC2012 -has_theme="$(ls -1 "$theme_path/$system".*.tar.gz 2> /dev/null | wc -l)" +has_theme="$(ls -1 "$theme_path/$system_name".*.tar.gz 2> /dev/null | wc -l)" ################ original_theme_ui0="ui/$sftype-$sfregion" @@ -201,58 +201,58 @@ themeIsBindable() { hasThemesToInstall() { [ "$has_theme" = 0 ] && return - for f in "$theme_path/$system".*.tar.gz; do + for f in "$theme_path/$system_name".*.tar.gz; do [ -f "$f" ] || continue tar -xzvf "$f" -C "$theme_path" && rm -f "$f" done } clearList() { - find "$omModCommands" "$omModScripts" -type f \ + find "$om_mod_commands" "$om_mod_scripts" -type f \ -not -name 'om_*' -not -name 'c0000_*' \ -not -name 'c999[0-9]_*' -not -name '_*' -exec rm {} + - remove "$omModSpacer" + remove "$om_mod_spacer" } clearDownloads() { - find "$omModSubCmds" "$omModSubScripts" "$omModSub" -type f \ + find "$om_mod_sub_cmds" "$om_mod_sub_scripts" "$om_mod_sub" -type f \ -not -name 'c000[0-8]_*' -not -name 'c999[0-9]_*' \ -not -name 'om_*' -not -name '_*' -exec rm {} + } enableMenuOptions() { - enable_cmds "ThemeGenerator" "Clear" - disable_cmds "Settings" "Color" + enableCmds "theme_generator" "clear" + disableCmds "settings" "color" - if [ ! -f "$omDummy" ]; then - disable_cmds "Clear" + if [ ! -f "$om_dummy" ]; then + disableCmds "clear" fi toggleDownloads - [ -z "$(ls -A "$theme_path")" ] && disable_cmds "ThemeGenerator" + [ -z "$(ls -A "$theme_path")" ] && disableCmds "theme_generator" if [ -f "$b_file" ] || [ -f "$p_file" ]; then - enable_cmds "Settings" + enableCmds "settings" fi - enable_cmds "AudioSetting" "AdvancedMusicHack" - [ -f "$(cat "$thm_chk" 2> /dev/null)/needs_overlay" ] && disable_cmds "AudioSetting" "AdvancedMusicHack" + enableCmds "audio_setting" "advanced_music_hack" + [ -f "$(cat "$thm_chk" 2> /dev/null)/needs_overlay" ] && disableCmds "audio_setting" "advanced_music_hack" if [ "$sftype" = "nes" ]; then - src="hvc" - dest="nes" + sprite_src="hvc" + sprite_dest="nes" if [ "$sfregion" = "jpn" ]; then - src="nes" - dest="hvc" + sprite_src="nes" + sprite_dest="hvc" fi - for subdir in "${omModPackedNes}/assets"/*; do - rename "${subdir}/${src}.png" "${subdir}/${dest}.png" + for subdir in "${om_mod_packed_nes}/assets"/*; do + rename "${subdir}/${sprite_src}.png" "${subdir}/${sprite_dest}.png" done fi - [ ! -d "$has_folders" ] && disable_cmds "ThemeGenerator" + [ ! -d "$has_folders" ] && disableCmds "theme_generator" } # avoids re-running the full themeLoader scan (~1.7s) when nothing changed @@ -270,9 +270,9 @@ themeLoader() { resource_path="$full_path/resources" sprite_path="$resource_path/sprites" script_path="$resource_path/scripts" - cmd_list="$omModCommands/c0001_$thm" - thm_script="$omModScripts/$thm" - tmp_thm_script="$omThemeScript" + cmd_list="$om_mod_commands/c0001_$thm" + thm_script="$om_mod_scripts/$thm" + tmp_thm_script="$om_theme_script" if [ ! -f "$full_path/info" ]; then thm_title="$(echo "$thm" | sed -e 's/_/ /g' | awk '{for(i=1;i<=NF;i++){$i=toupper(substr($i,1,1))substr($i,2)}}1')" @@ -290,48 +290,48 @@ themeLoader() { if [ ! -f "$full_path.png" ]; then if [ -d "$usb_path" ]; then - copier "$thmPreview/$thm.png" "$full_path.png" + copier "$thm_preview/$thm.png" "$full_path.png" else - link "$thmPreview/$thm.png" "$full_path.png" + link "$thm_preview/$thm.png" "$full_path.png" fi fi - create "$omModSpacer" + create "$om_mod_spacer" if [ "$sftype" = "nes" ]; then - src="hvc" - dest="nes" + sprite_src="hvc" + sprite_dest="nes" if [ "$sfregion" = "jpn" ]; then - src="nes" - dest="hvc" + sprite_src="nes" + sprite_dest="hvc" fi for ext in "png" "json"; do - rename "${sprite_path}/${src}.${ext}" "${sprite_path}/${dest}.${ext}" + rename "${sprite_path}/${sprite_src}.${ext}" "${sprite_path}/${sprite_dest}.${ext}" done - rename "${script_path}/colors_${src}.lua" "${script_path}/colors_${dest}.lua" + rename "${script_path}/colors_${sprite_src}.lua" "${script_path}/colors_${sprite_dest}.lua" fi - copy "$omModLicense" "$thm_script.sh" + copy "$om_mod_license" "$thm_script.sh" if [ ! -d "$has_folders" ]; then - tmp_thm_script="$omOverlayThemeScript" + tmp_thm_script="$om_overlay_theme_script" fi echo "sed '1,2d' \"$tmp_thm_script\" >\"$b_file\" [ ! -f \"$p_file\" ] && echo themeSelector >\"$p_file\" -sed -i \"s/SELECTED_THEME=\\\"CHANGEME\\\"/SELECTED_THEME=\\\"${thm}\\\"/g;\" \"$b_file\" +sed -i \"s/selected_theme=\\\"CHANGEME\\\"/selected_theme=\\\"${thm}\\\"/g;\" \"$b_file\" " >> "$thm_script.sh" - echo "\"\$omScripts/gen_splash\" /tmp/om_splash.png THEME_APPLYING_LINE1 THEME_APPLYING_LINE2 && decodepng /tmp/om_splash.png > /dev/fb0 + echo "\"\$om_scripts/gen_splash\" /tmp/om_splash.png THEME_APPLYING_LINE1 THEME_APPLYING_LINE2 && decodepng /tmp/om_splash.png > /dev/fb0 sleep 1 hakchi reboot" >> "$thm_script.sh" - preview_image="$thmPreview/$thm.png" + preview_image="$thm_preview/$thm.png" [ ! -f "$preview_image" ] && preview_image="$theme_path/$thm.png" - [ ! -f "$preview_image" ] && [ -z "${thm##diy_theme*}" ] && preview_image="$optionsMenu/images/diy_preview.png" - [ ! -f "$preview_image" ] && preview_image="$optionsMenu/images/default_preview.png" + [ ! -f "$preview_image" ] && [ -z "${thm##diy_theme*}" ] && preview_image="$options_menu/images/diy_preview.png" + [ ! -f "$preview_image" ] && preview_image="$options_menu/images/default_preview.png" cmd_name="$(sed -r 's/NAME=//g' < "$full_path/info")" echo "COMMAND_NAME=$cmd_name @@ -343,7 +343,7 @@ DELETE_STR=rm -rf \"$full_path\" \"$full_path.png\" \"$cmd_list\" \"$thm_script. } removeRegionalTheme() { - region_files="$omModSubCmds/c0010_snesce-$sfregion $omModSubScripts/getSnes$sfregion $omModSub/$sfregion.png $omModSub/snesce-$sfregion.png" + region_files="$om_mod_sub_cmds/c0010_snesce-$sfregion $om_mod_sub_scripts/getSnes$sfregion $om_mod_sub/$sfregion.png $om_mod_sub/snesce-$sfregion.png" for item in $region_files; do remove "$item" done @@ -351,17 +351,17 @@ removeRegionalTheme() { } getThemeList() { - "$omScripts/gen_splash" /tmp/om_splash.png THEME_LIST_DOWNLOADING_LINE1 THEME_LIST_DOWNLOADING_LINE2 && decodepng /tmp/om_splash.png > /dev/fb0 - cd "$omModSub" || exit + "$om_scripts/gen_splash" /tmp/om_splash.png THEME_LIST_DOWNLOADING_LINE1 THEME_LIST_DOWNLOADING_LINE2 && decodepng /tmp/om_splash.png > /dev/fb0 + cd "$om_mod_sub" || exit - if [ ! -e "${s_type}${listName}" ]; then + if [ ! -e "${s_type}${list_name}" ]; then clearDownloads - theme_downloader catalog "$s_type" "${s_type}${listName}" || netChecker + theme_downloader catalog "$s_type" "${s_type}${list_name}" || netChecker fi # shellcheck disable=SC2035 - tar -xzf "${s_type}${listName}" && rm -f *.tar.gz + tar -xzf "${s_type}${list_name}" && rm -f *.tar.gz removeRegionalTheme - touch "$subDummy" + touch "$sub_dummy" usleep 1000 } @@ -399,12 +399,12 @@ fontFix' "/var/lib/hakchi/rootfs/bin/chmenu" loadThemeList() { if [ -n "$(ls -A "$theme_path")" ]; then - create "$omDummy" + create "$om_dummy" if [ "$1" = "-r" ]; then themeLoader fi else - remove "$omModSpacer" + remove "$om_mod_spacer" clearList fi } @@ -416,7 +416,7 @@ themeGenerator() { translate CHECKING_THEME_FILES echo "" theme_name="${1}" - full_title="$(grep -F 'NAME=' "${omModSubCmds}/c0010_${theme_name}" | sed -e 's/.*=\(.*\)\[.*/\1/')" + full_title="$(grep -F 'NAME=' "${om_mod_sub_cmds}/c0010_${theme_name}" | sed -e 's/.*=\(.*\)\[.*/\1/')" full_path="${theme_path}/${theme_name}" cd "${theme_path}" || exit @@ -428,20 +428,20 @@ themeGenerator() { while IFS= read -r t; do echo "$t" echo "" - theme_downloader theme "$sftype" "$t" "${system}.${t}.tar.gz" \ - && tar -xzvf "${system}.${t}.tar.gz" && remove "${system}.${t}.tar.gz" + theme_downloader theme "$sftype" "$t" "${system_name}.${t}.tar.gz" \ + && tar -xzvf "${system_name}.${t}.tar.gz" && remove "${system_name}.${t}.tar.gz" done < "${sftype}-all" fi else ok=0 - if theme_downloader theme "$sftype" "$theme_name" "${system}.${theme_name}.tar.gz"; then + if theme_downloader theme "$sftype" "$theme_name" "${system_name}.${theme_name}.tar.gz"; then ok=1 translate DOWNLOADING fi fi if [ "$ok" = 1 ]; then - [ -f "${system}.${theme_name}.tar.gz" ] && tar -xzvf "${system}.${theme_name}.tar.gz" && rm -f ./*.tar.gz + [ -f "${system_name}.${theme_name}.tar.gz" ] && tar -xzvf "${system_name}.${theme_name}.tar.gz" && rm -f ./*.tar.gz remove "${sftype}-all" removeRegionalTheme echo "" @@ -454,10 +454,10 @@ themeGenerator() { } findDiyAsset() { - "$omScripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png > /dev/fb0 + "$om_scripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png > /dev/fb0 diy_file="${1}.png" - diy_sprite="$omModSprites/${1}" + diy_sprite="$om_mod_sprites/${1}" asset_path="layout/asset/sprite/wallpaper" asset="$diy_file" chars="0" @@ -523,7 +523,7 @@ findDiyAsset() { esac diy_crop_x="" - diy_sprites_json="$omModScripts/om_${1}.sprites.json" + diy_sprites_json="$om_mod_scripts/om_${1}.sprites.json" if [ "$colors" = "0" ] && [ -f "$diy_sprites_json" ]; then diy_frame="$(grep -o '"textureFrame":\[[0-9]*,[0-9]*,[0-9]*,[0-9]*\]' "$diy_sprites_json" | grep -o '[0-9]*,[0-9]*,[0-9]*,[0-9]*')" diy_crop_x="${diy_frame%%,*}" @@ -533,7 +533,7 @@ findDiyAsset() { diy_crop_w="${diy_rest%%,*}" diy_crop_h="${diy_rest#*,}" fi - paths_to_search="$omModSprites/assets/$1 $theme_path" + paths_to_search="$om_mod_sprites/assets/$1 $theme_path" find "$diy_sprite/commands" "$diy_sprite/scripts" -type f \ -not -name 'om_*' -not -name 'c999[0-9]_*' -not -name 'c0000_*' \ @@ -554,7 +554,7 @@ findDiyAsset() { has_sprite="0" full_asset_path="$p/$asset_name" - json="${omModSprites}/${1}_${asset_name}.json" + json="${om_mod_sprites}/${1}_${asset_name}.json" resource_path="$full_asset_path/resources" [ -f "$full_asset_path/$diy_file" ] && has_sprite="1" @@ -580,7 +580,7 @@ findDiyAsset() { $sprite_hash" fi - temp_preview="${omModSprites}/assets/${1}/${1}_${asset_name}.png" + temp_preview="${om_mod_sprites}/assets/${1}/${1}_${asset_name}.png" if [ "$colors" = "1" ]; then preview_image="$temp_preview" @@ -624,7 +624,7 @@ COMMAND_TYPE=INTERNAL COMMAND_STR=sh $sprite_script.sh PREVIEW_IMAGE=$preview_image${preview_nearest}${preview_square_str}${preview_cols_str}${preview_fit_str}" > "$cmd_file" - copy "$omModLicense" "$sprite_script.sh" + copy "$om_mod_license" "$sprite_script.sh" echo "diySpriteSelector \"$full_asset_path\" \"$asset_path\" \"$asset\"" >> "$sprite_script.sh" fi fi @@ -636,14 +636,14 @@ PREVIEW_IMAGE=$preview_image${preview_nearest}${preview_square_str}${preview_col } diySpriteSelector() { - diySelectedAsset="$1" - diyLayout="$diyResources/$2" - diyAsset="$diyLayout" + diy_selected_asset="$1" + diy_layout="$diy_resources/$2" + diy_asset="$diy_layout" - [ ! -d "$diyAsset" ] && mkdir -p "$diyAsset" - find "$diySelectedAsset" -name "$3" -exec cp {} "$diyLayout" \; + [ ! -d "$diy_asset" ] && mkdir -p "$diy_asset" + find "$diy_selected_asset" -name "$3" -exec cp {} "$diy_layout" \; if [ ! -f "$b_file" ]; then - sed '1,2d' "$omThemeScript" > "$b_file" + sed '1,2d' "$om_theme_script" > "$b_file" echo themeSelector > "$p_file" fi echo "" @@ -667,23 +667,23 @@ diySpriteSelector() { } diyThemeChecker() { - [ "$1" = "loader" ] && { "$omScripts/gen_splash" /tmp/om_splash.png THEME_LOADING_ASSETS && decodepng /tmp/om_splash.png > /dev/fb0; } + [ "$1" = "loader" ] && { "$om_scripts/gen_splash" /tmp/om_splash.png THEME_LOADING_ASSETS && decodepng /tmp/om_splash.png > /dev/fb0; } - [ ! -d "$diyThemePath" ] && mkdir -p "$diyThemePath" + [ ! -d "$diy_theme_path" ] && mkdir -p "$diy_theme_path" - if find "$diyThemePath" -type f | read -r _; then - enable_cmds "DiY" "DelDiY" "Preview" + if find "$diy_theme_path" -type f | read -r _; then + enableCmds "diy" "del_diy" "preview" else - disable_cmds "DiY" "DelDiY" "Preview" + disableCmds "diy" "del_diy" "preview" fi [ "$1" = "loader" ] && usleep 1000 } diyThemeRemover() { - disable_cmds "DiY" "DelDiY" "Preview" + disableCmds "diy" "del_diy" "preview" - [ -d "$diyThemePath" ] && find "$diyThemePath" -type f -not -name 'assets_here' -exec rm {} + + [ -d "$diy_theme_path" ] && find "$diy_theme_path" -type f -not -name 'assets_here' -exec rm {} + } @@ -691,7 +691,7 @@ modCleanUp() { for item in $items_to_delete; do remove "$item" done - hasItemsToCleanUp "SystemCleanUp" + hasItemsToCleanUp "system_clean_up" } themeSettings() { @@ -751,11 +751,11 @@ hasItemsToCleanUp() { fi done if [ "$has_non_system_files" = "true" ]; then - enable_cmds "$1" - [ ! -f "$omModSettingsSpacer" ] && touch "$omModSettingsSpacer" + enableCmds "$1" + [ ! -f "$om_mod_settings_spacer" ] && touch "$om_mod_settings_spacer" else - disable_cmds "$1" - [ ! -f "$enableUninstaller" ] && remove "$omModSettingsSpacer" + disableCmds "$1" + [ ! -f "$enable_uninstaller" ] && remove "$om_mod_settings_spacer" fi } @@ -765,26 +765,26 @@ musicPerFolder() { folderThemesRow() { if [ -f "$om_themeperfolder" ] && [ -d "$has_folders" ]; then - enable_cmds "FolderThemes" + enableCmds "folder_themes" else - disable_cmds "FolderThemes" + disableCmds "folder_themes" fi if hasMusicFiles "$music_path"; then - enable_cmds "MusicPerFolder" + enableCmds "music_per_folder" else - disable_cmds "MusicPerFolder" + disableCmds "music_per_folder" fi if musicPerFolder && [ -d "$has_folders" ]; then - enable_cmds "FolderMusic" + enableCmds "folder_music" else - disable_cmds "FolderMusic" + disableCmds "folder_music" fi } # a folder entry living in games/

/CLV-S-00/ is folder inside folder

listFolders() { tab="$(printf '\t')" - find "$gStorage" -maxdepth 3 -type f -name 'CLV-S-00*.desktop' -exec awk ' + find "$game_storage" -maxdepth 3 -type f -name 'CLV-S-00*.desktop' -exec awk ' /^Name=/ { name = substr($0, 6) if (name == "Back" || name == "Home") next @@ -812,9 +812,9 @@ themeTitle() { } themePreview() { - preview="$thmPreview/$1.png" + preview="$thm_preview/$1.png" [ ! -f "$preview" ] && preview="$theme_path/$1.png" - [ ! -f "$preview" ] && preview="$optionsMenu/images/default_preview.png" + [ ! -f "$preview" ] && preview="$options_menu/images/default_preview.png" echo "$preview" } @@ -822,12 +822,12 @@ globalTheme() { if [ -f "$om_themerandomizer" ]; then echo "@random" elif [ -f "$b_file" ]; then - sed -n 's/^ *local SELECTED_THEME="\(.*\)"$/\1/p' "$b_file" | head -n1 | grep -v '^CHANGEME$' + sed -n 's/^ *local selected_theme="\(.*\)"$/\1/p' "$b_file" | head -n1 | grep -v '^CHANGEME$' fi } folderThemesMenu() { - data_dir="$folderThemesTmp" + data_dir="$folder_themes_tmp" rm -rf "$data_dir" && mkdir -p "$data_dir" { printf 'home\t%s\t0\n' "$(translate HOME_FOLDER)" @@ -848,12 +848,12 @@ folderThemesMenu() { [ -f "$om_audiorandomizer" ] && echo random_home [ -f "$om_advmusichack" ] && echo random_folders } >"$data_dir/audio" - echo "sh $omModScripts/om_settings" >"$data_dir/om_return" + echo "sh $om_mod_scripts/om_settings" >"$data_dir/om_return" title="FOLDER_THEMES" [ "$1" = "music" ] && title="FOLDER_MUSIC" set -- --commandPath "$data_dir/" --scriptPath "$data_dir" --title "$title" --folderThemes "$data_dir" --folderChannel "$1" [ -f "$om_themeperfolder" ] && set -- "$@" --mapping "$om_folderthemes" musicPerFolder && set -- "$@" --musicMapping "$om_foldermusic" - $optionsMenu/lib/theme_manager "$@" & + $options_menu/lib/theme_manager "$@" & } diff --git a/mod/etc/options_menu/themes/settings/scripts/om_audio b/mod/etc/options_menu/themes/settings/scripts/om_audio index 3e8704c..10e3774 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_audio +++ b/mod/etc/options_menu/themes/settings/scripts/om_audio @@ -21,4 +21,4 @@ script_init audioSettings "$1" -$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS --layout list & +$options_menu/lib/theme_manager --commandPath $om_mod_settings_cmds/ --scriptPath $om_mod_settings_scripts --title THEME_SETTINGS --layout list & diff --git a/mod/etc/options_menu/themes/settings/scripts/om_reset b/mod/etc/options_menu/themes/settings/scripts/om_reset index 6d0a458..ab31691 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_reset +++ b/mod/etc/options_menu/themes/settings/scripts/om_reset @@ -20,14 +20,14 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init -FONT_PATH="$mountpoint/media/$modname/fonts" -[ ! -d "$FONT_PATH" ] && FONT_PATH="$rootfs/usr/share/fonts" -[ -f "${FONT_PATH}/title.fnt.backup" ] && cp "${FONT_PATH}/title.fnt.backup" "${FONT_PATH}/title.fnt" -for FONT in s m l small medium large; do - rm -f "${FONT_PATH}/${FONT}.fnt" +font_path="$mountpoint/media/$modname/fonts" +[ ! -d "$font_path" ] && font_path="$rootfs/usr/share/fonts" +[ -f "${font_path}/title.fnt.backup" ] && cp "${font_path}/title.fnt.backup" "${font_path}/title.fnt" +for font_name in s m l small medium large; do + rm -f "${font_path}/${font_name}.fnt" done remove "$b_file" "$p_file" hakchi reboot -$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS --layout list & +$options_menu/lib/theme_manager --commandPath $om_mod_settings_cmds/ --scriptPath $om_mod_settings_scripts --title THEME_SETTINGS --layout list & diff --git a/mod/etc/options_menu/themes/settings/scripts/om_state b/mod/etc/options_menu/themes/settings/scripts/om_state index 999aa95..524104c 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_state +++ b/mod/etc/options_menu/themes/settings/scripts/om_state @@ -19,22 +19,22 @@ # Reports current on/off state for a Settings toggle row - read-only # counterpart to om_theme/om_audio, which flip the same flag files. The # engine calls this once per toggle row every time the Settings screen -# loads, so it reads $state_path from the cache om_settings writes just +# loads, so it reads $mod_state_path from the cache om_settings writes just # before launching theme_manager, instead of re-sourcing om_functions/ # preinit (~0.1-0.3s) five times over for the same value. -state_path="$(cat /tmp/om_state_path 2> /dev/null)" -if [ -z "$state_path" ]; then +mod_state_path="$(cat /tmp/om_state_path 2> /dev/null)" +if [ -z "$mod_state_path" ]; then # shellcheck source=/dev/null source $mountpoint/etc/options_menu/scripts/om_functions script_init fi case "$1" in - theme_per_folder) flag="$state_path/om_themeperfolder" ;; - music_per_folder) flag="$state_path/om_musicperfolder" ;; - theme_randomizer) flag="$state_path/om_themerandomizer" ;; - audio_randomizer) flag="$state_path/om_audiorandomizer" ;; - audio_randomizer_all) flag="$state_path/om_advmusichack" ;; + theme_per_folder) flag="$mod_state_path/om_themeperfolder" ;; + music_per_folder) flag="$mod_state_path/om_musicperfolder" ;; + theme_randomizer) flag="$mod_state_path/om_themerandomizer" ;; + audio_randomizer) flag="$mod_state_path/om_audiorandomizer" ;; + audio_randomizer_all) flag="$mod_state_path/om_advmusichack" ;; esac [ -f "$flag" ] && echo on || echo off diff --git a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup index 8f5562b..e6c2d67 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup +++ b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup @@ -19,10 +19,10 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init -"$omScripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png >/dev/fb0 +"$om_scripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png >/dev/fb0 modCleanUp usleep 1000 -$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS --layout list & +$options_menu/lib/theme_manager --commandPath $om_mod_settings_cmds/ --scriptPath $om_mod_settings_scripts --title THEME_SETTINGS --layout list & diff --git a/mod/etc/options_menu/themes/settings/scripts/om_theme b/mod/etc/options_menu/themes/settings/scripts/om_theme index e9a4bfd..67f6bff 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_theme +++ b/mod/etc/options_menu/themes/settings/scripts/om_theme @@ -23,4 +23,4 @@ script_init themeSettings "$1" folderThemesRow -$optionsMenu/lib/theme_manager --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title THEME_SETTINGS --layout list & +$options_menu/lib/theme_manager --commandPath $om_mod_settings_cmds/ --scriptPath $om_mod_settings_scripts --title THEME_SETTINGS --layout list & diff --git a/mod/uninstall b/mod/uninstall index c0ed4e2..726138f 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -16,101 +16,101 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -remove() { +removeItem() { for f in $@; do [ -f "$f" ] || [ -d "$f" ] && rm -rf "$f" done } -FONT_PATH="$rootfs/usr/share/fonts" -UI0="ui/$sftype-$sfregion" -UI1="" +font_path="$rootfs/usr/share/fonts" +ui_dir="ui/$sftype-$sfregion" +ui_subdir="" case "$sftype" in hvcj) - UI0="ui/hvc" + ui_dir="ui/hvc" ;; nes) - UI0="clover-ui" - UI1="nes/" + ui_dir="clover-ui" + ui_subdir="nes/" if [ "$sfregion" = "jpn" ]; then - UI0="clover-ui" - UI1="hvc" + ui_dir="clover-ui" + ui_subdir="hvc" fi ;; snes) - [ "$sfregion" = "jpn" ] && UI0="ui/shvc" + [ "$sfregion" = "jpn" ] && ui_dir="ui/shvc" ;; esac -ORIGINAL_THEME="$mountpoint/usr/share/$UI0" +original_theme="$mountpoint/usr/share/$ui_dir" sed -i '/# themeselector fix *$/,+2d' "$rootfs/bin/chmenu" sed -i '/# themeselector$/,+4d' "$rootfs/etc/preinit.d/b8025_fontfix" -remove "$mountpoint/$profilepath/$modname/lastTheme" -remove "$mountpoint/$profilepath/$modname/prevTheme" -remove "$mountpoint/$profilepath/$modname/active_theme" -remove "$mountpoint/$profilepath/$modname/folder_themes" -remove "$mountpoint/$profilepath/$modname/folder_music" -remove "$mountpoint/$profilepath/$modname/om_advmusichack" -remove "$mountpoint/$profilepath/$modname/om_audiorandomizer" -remove "$mountpoint/$profilepath/$modname/om_themerandomizer" -remove "$mountpoint/$profilepath/$modname/om_firstrun" -remove "$rootfs/usr/share/themes" -remove "$rootfs/usr/share/music_menu" +removeItem "$mountpoint/$profilepath/$modname/lastTheme" +removeItem "$mountpoint/$profilepath/$modname/prevTheme" +removeItem "$mountpoint/$profilepath/$modname/active_theme" +removeItem "$mountpoint/$profilepath/$modname/folder_themes" +removeItem "$mountpoint/$profilepath/$modname/folder_music" +removeItem "$mountpoint/$profilepath/$modname/om_advmusichack" +removeItem "$mountpoint/$profilepath/$modname/om_audiorandomizer" +removeItem "$mountpoint/$profilepath/$modname/om_themerandomizer" +removeItem "$mountpoint/$profilepath/$modname/om_firstrun" +removeItem "$rootfs/usr/share/themes" +removeItem "$rootfs/usr/share/music_menu" chmod +x "$rootfs/bin/chmenu" -remove "$rootfs/etc/preinit.d/b8024_themeselector" -remove "$rootfs/etc/preinit.d/p8024_themeselector" -remove "$rootfs/etc/options_menu/commands/c0075_ThemeOptions" -remove "$rootfs/etc/options_menu/scripts/om_themeselector" -remove "$rootfs/etc/options_menu/scripts/om_theme_selector" -remove "$rootfs/etc/options_menu/themes" -remove "$rootfs/etc/options_menu/images/advanced.png" -remove "$rootfs/etc/options_menu/images/audio.png" -remove "$rootfs/etc/options_menu/images/audioall.png" -remove "$rootfs/etc/options_menu/images/audiohome.png" -remove "$rootfs/etc/options_menu/images/back.png" -remove "$rootfs/etc/options_menu/images/clear.png" -remove "$rootfs/etc/options_menu/images/colors_nes.png" -remove "$rootfs/etc/options_menu/images/default_preview.png" -remove "$rootfs/etc/options_menu/images/diy.png" -remove "$rootfs/etc/options_menu/images/download.png" -remove "$rootfs/etc/options_menu/images/exit.png" -remove "$rootfs/etc/options_menu/images/loading" -remove "$rootfs/etc/options_menu/images/luigi.png" -remove "$rootfs/etc/options_menu/images/mario.png" -remove "$rootfs/etc/options_menu/images/packed.png" -remove "$rootfs/etc/options_menu/images/packed_nes.png" -remove "$rootfs/etc/options_menu/images/pole.png" -remove "$rootfs/etc/options_menu/images/preview_audio_randomizer_all.png" -remove "$rootfs/etc/options_menu/images/preview_audio_randomizer_home.png" -remove "$rootfs/etc/options_menu/images/preview_clean_up.png" -remove "$rootfs/etc/options_menu/images/preview_diy_theme.png" -remove "$rootfs/etc/options_menu/images/preview_download_themes.png" -remove "$rootfs/etc/options_menu/images/preview_theme_options.png" -remove "$rootfs/etc/options_menu/images/preview_theme_randomizer.png" -remove "$rootfs/etc/options_menu/images/preview_update_theme_list.png" -remove "$rootfs/etc/options_menu/images/randtheme.png" -remove "$rootfs/etc/options_menu/images/retro_gaming.zip" -remove "$rootfs/etc/options_menu/images/retroarch.png" -remove "$rootfs/etc/options_menu/images/saving.png" -remove "$rootfs/etc/options_menu/images/sbm.png" -remove "$rootfs/etc/options_menu/images/settings.png" -remove "$rootfs/etc/options_menu/images/shutdown.png" -remove "$rootfs/etc/options_menu/images/themes.png" -remove "$rootfs/etc/options_menu/images/uninstall.png" -remove "$rootfs/etc/options_menu/images/update.png" -remove "$rootfs/etc/options_menu/images/wall.png" -remove "$rootfs/etc/options_menu/images/wall_far.png" -remove "$rootfs/etc/usr/bin/pngpack-upx" -remove "$rootfs/bin/sprite_crop" -remove "$rootfs/bin/theme_downloader" -remove "$rootfs/etc/options_menu/lib/theme_manager" +removeItem "$rootfs/etc/preinit.d/b8024_themeselector" +removeItem "$rootfs/etc/preinit.d/p8024_themeselector" +removeItem "$rootfs/etc/options_menu/commands/c0075_ThemeOptions" +removeItem "$rootfs/etc/options_menu/scripts/om_themeselector" +removeItem "$rootfs/etc/options_menu/scripts/om_theme_selector" +removeItem "$rootfs/etc/options_menu/themes" +removeItem "$rootfs/etc/options_menu/images/advanced.png" +removeItem "$rootfs/etc/options_menu/images/audio.png" +removeItem "$rootfs/etc/options_menu/images/audioall.png" +removeItem "$rootfs/etc/options_menu/images/audiohome.png" +removeItem "$rootfs/etc/options_menu/images/back.png" +removeItem "$rootfs/etc/options_menu/images/clear.png" +removeItem "$rootfs/etc/options_menu/images/colors_nes.png" +removeItem "$rootfs/etc/options_menu/images/default_preview.png" +removeItem "$rootfs/etc/options_menu/images/diy.png" +removeItem "$rootfs/etc/options_menu/images/download.png" +removeItem "$rootfs/etc/options_menu/images/exit.png" +removeItem "$rootfs/etc/options_menu/images/loading" +removeItem "$rootfs/etc/options_menu/images/luigi.png" +removeItem "$rootfs/etc/options_menu/images/mario.png" +removeItem "$rootfs/etc/options_menu/images/packed.png" +removeItem "$rootfs/etc/options_menu/images/packed_nes.png" +removeItem "$rootfs/etc/options_menu/images/pole.png" +removeItem "$rootfs/etc/options_menu/images/preview_audio_randomizer_all.png" +removeItem "$rootfs/etc/options_menu/images/preview_audio_randomizer_home.png" +removeItem "$rootfs/etc/options_menu/images/preview_clean_up.png" +removeItem "$rootfs/etc/options_menu/images/preview_diy_theme.png" +removeItem "$rootfs/etc/options_menu/images/preview_download_themes.png" +removeItem "$rootfs/etc/options_menu/images/preview_theme_options.png" +removeItem "$rootfs/etc/options_menu/images/preview_theme_randomizer.png" +removeItem "$rootfs/etc/options_menu/images/preview_update_theme_list.png" +removeItem "$rootfs/etc/options_menu/images/randtheme.png" +removeItem "$rootfs/etc/options_menu/images/retro_gaming.zip" +removeItem "$rootfs/etc/options_menu/images/retroarch.png" +removeItem "$rootfs/etc/options_menu/images/saving.png" +removeItem "$rootfs/etc/options_menu/images/sbm.png" +removeItem "$rootfs/etc/options_menu/images/settings.png" +removeItem "$rootfs/etc/options_menu/images/shutdown.png" +removeItem "$rootfs/etc/options_menu/images/themes.png" +removeItem "$rootfs/etc/options_menu/images/uninstall.png" +removeItem "$rootfs/etc/options_menu/images/update.png" +removeItem "$rootfs/etc/options_menu/images/wall.png" +removeItem "$rootfs/etc/options_menu/images/wall_far.png" +removeItem "$rootfs/etc/usr/bin/pngpack-upx" +removeItem "$rootfs/bin/sprite_crop" +removeItem "$rootfs/bin/theme_downloader" +removeItem "$rootfs/etc/options_menu/lib/theme_manager" -[ -f "${FONT_PATH}/title.fnt.backup" ] && cp "${FONT_PATH}/title.fnt.backup" "${FONT_PATH}/title.fnt" -for FONT in s m l small medium large; do - umount "${ORIGINAL_THEME}/resources/fonts/${UI1}/${FONT}.fnt" 2>/dev/null - rm -f "${FONT_PATH}/${FONT}.fnt" +[ -f "${font_path}/title.fnt.backup" ] && cp "${font_path}/title.fnt.backup" "${font_path}/title.fnt" +for font_name in s m l small medium large; do + umount "${original_theme}/resources/fonts/${ui_subdir}/${font_name}.fnt" 2>/dev/null + rm -f "${font_path}/${font_name}.fnt" done unset cfg_OM_ThemeSelector_enabled From b9b58c2587d7837cff7d6c29bbbe54db7cf3a602 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 6 Oct 2026 22:48:41 +0000 Subject: [PATCH 114/210] Keep theme downloads off a full NAND and report failed installs - Store themes on USB/SD whenever it is mounted, creating themes/ and moving NAND themes over (splash only on a real migration) - Check free space against the unpacked size before extracting and stop the All download when space runs out - Never leave a half-extracted theme or tarball behind, and tell corrupt packages apart from a full disk - theme_downloader: catch write failures at fclose and exit 3 instead of reporting success - List the themes that failed after an All download - Drop the bundled theme of the console's own region - Keep downloads/images on Clear List and regenerate empty previews - Remove the dead om_fontfix/om_chmenu copies from install --- .../options_menu/scripts/om_theme_selector | 1 + mod/etc/options_menu/themes/lang/de-DE.lang | 4 + mod/etc/options_menu/themes/lang/en-US.lang | 4 + mod/etc/options_menu/themes/lang/es-ES.lang | 4 + mod/etc/options_menu/themes/lang/fr-FR.lang | 4 + mod/etc/options_menu/themes/lang/it-IT.lang | 4 + mod/etc/options_menu/themes/lang/ja-JP.lang | 4 + mod/etc/options_menu/themes/lang/nl-NL.lang | 4 + mod/etc/options_menu/themes/lang/pt-PT.lang | 4 + mod/etc/options_menu/themes/lang/ru-RU.lang | 4 + mod/etc/options_menu/themes/scripts/om_vars | 119 ++++++++++++++---- mod/install | 3 - src/theme_downloader.c | 39 +++--- 13 files changed, 150 insertions(+), 48 deletions(-) diff --git a/mod/etc/options_menu/scripts/om_theme_selector b/mod/etc/options_menu/scripts/om_theme_selector index b04a367..3441ccc 100644 --- a/mod/etc/options_menu/scripts/om_theme_selector +++ b/mod/etc/options_menu/scripts/om_theme_selector @@ -20,6 +20,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init injectFile "chmenu" +migrateThemes && set -- reload hasThemesToInstall enableMenuOptions remove "$om_dummy" diff --git a/mod/etc/options_menu/themes/lang/de-DE.lang b/mod/etc/options_menu/themes/lang/de-DE.lang index 001ace8..dc7ad33 100644 --- a/mod/etc/options_menu/themes/lang/de-DE.lang +++ b/mod/etc/options_menu/themes/lang/de-DE.lang @@ -69,8 +69,12 @@ CATEGORY_UI_MOD=Kategorie: Benutzeroberflächen-Mod CHECKING_THEME_FILES=Dateien des ausgewählten Themes werden geprüft. DOWNLOADING=Wird heruntergeladen... INSTALLATION_COMPLETE=Installation abgeschlossen. +NOT_ENOUGH_SPACE=Nicht genügend freier Speicher, gib Speicher frei und versuche es erneut. +THEMES_FAILED=Diese Themen konnten nicht installiert werden: THEME_LIST_DOWNLOADING_LINE1=Themenliste wird heruntergeladen THEME_LIST_DOWNLOADING_LINE2=Bitte warten . . . +THEME_MOVING_LINE1=Themen werden auf USB/SD verschoben +THEME_MOVING_LINE2=Bitte warten . . . THEME_LOADING=Wird geladen... THEME_LOADING_ASSETS=Assets werden geladen... THEME_SAVING=Eigenes Theme wird gespeichert... diff --git a/mod/etc/options_menu/themes/lang/en-US.lang b/mod/etc/options_menu/themes/lang/en-US.lang index 46ee6a8..cf7b28f 100644 --- a/mod/etc/options_menu/themes/lang/en-US.lang +++ b/mod/etc/options_menu/themes/lang/en-US.lang @@ -69,8 +69,12 @@ CATEGORY_UI_MOD=Category: User Interface Mod CHECKING_THEME_FILES=Checking selected theme files. DOWNLOADING=Downloading... INSTALLATION_COMPLETE=Installation complete. +NOT_ENOUGH_SPACE=Not enough free space, free some space and try again. +THEMES_FAILED=These themes failed to install: THEME_LIST_DOWNLOADING_LINE1=Downloading Theme List THEME_LIST_DOWNLOADING_LINE2=Please Wait . . . +THEME_MOVING_LINE1=Moving Themes to USB/SD +THEME_MOVING_LINE2=Please Wait . . . THEME_LOADING=Loading... THEME_LOADING_ASSETS=Loading assets... THEME_SAVING=Saving custom theme... diff --git a/mod/etc/options_menu/themes/lang/es-ES.lang b/mod/etc/options_menu/themes/lang/es-ES.lang index 8383698..de1d668 100644 --- a/mod/etc/options_menu/themes/lang/es-ES.lang +++ b/mod/etc/options_menu/themes/lang/es-ES.lang @@ -69,8 +69,12 @@ CATEGORY_UI_MOD=Categoría: Mod de interfaz CHECKING_THEME_FILES=Comprobando los archivos del tema seleccionado. DOWNLOADING=Descargando... INSTALLATION_COMPLETE=Instalación completada. +NOT_ENOUGH_SPACE=No hay suficiente espacio libre, libera espacio e inténtalo de nuevo. +THEMES_FAILED=Estos temas no se pudieron instalar: THEME_LIST_DOWNLOADING_LINE1=Descargando Lista de Temas THEME_LIST_DOWNLOADING_LINE2=Espere Por Favor . . . +THEME_MOVING_LINE1=Moviendo Temas a USB/SD +THEME_MOVING_LINE2=Espere Por Favor . . . THEME_LOADING=Cargando... THEME_LOADING_ASSETS=Cargando recursos... THEME_SAVING=Guardando tema personalizado... diff --git a/mod/etc/options_menu/themes/lang/fr-FR.lang b/mod/etc/options_menu/themes/lang/fr-FR.lang index a05faf7..ce80d31 100644 --- a/mod/etc/options_menu/themes/lang/fr-FR.lang +++ b/mod/etc/options_menu/themes/lang/fr-FR.lang @@ -69,8 +69,12 @@ CATEGORY_UI_MOD=Catégorie : Mod d'interface CHECKING_THEME_FILES=Vérification des fichiers du thème sélectionné. DOWNLOADING=Téléchargement... INSTALLATION_COMPLETE=Installation terminée. +NOT_ENOUGH_SPACE=Espace libre insuffisant, libérez de l'espace et réessayez. +THEMES_FAILED=Ces thèmes n'ont pas pu être installés : THEME_LIST_DOWNLOADING_LINE1=Téléchargement de la Liste des Thèmes THEME_LIST_DOWNLOADING_LINE2=Veuillez Patienter . . . +THEME_MOVING_LINE1=Déplacement des Thèmes vers USB/SD +THEME_MOVING_LINE2=Veuillez Patienter . . . THEME_LOADING=Chargement... THEME_LOADING_ASSETS=Chargement des ressources... THEME_SAVING=Enregistrement du thème personnalisé... diff --git a/mod/etc/options_menu/themes/lang/it-IT.lang b/mod/etc/options_menu/themes/lang/it-IT.lang index b67b27a..469c1f3 100644 --- a/mod/etc/options_menu/themes/lang/it-IT.lang +++ b/mod/etc/options_menu/themes/lang/it-IT.lang @@ -69,8 +69,12 @@ CATEGORY_UI_MOD=Categoria: Mod interfaccia utente CHECKING_THEME_FILES=Controllo dei file del tema selezionato. DOWNLOADING=Download in corso... INSTALLATION_COMPLETE=Installazione completata. +NOT_ENOUGH_SPACE=Spazio libero insufficiente, libera spazio e riprova. +THEMES_FAILED=Questi temi non sono stati installati: THEME_LIST_DOWNLOADING_LINE1=Download Elenco Temi in Corso THEME_LIST_DOWNLOADING_LINE2=Attendere Prego . . . +THEME_MOVING_LINE1=Spostamento Temi su USB/SD +THEME_MOVING_LINE2=Attendere Prego . . . THEME_LOADING=Caricamento... THEME_LOADING_ASSETS=Caricamento risorse... THEME_SAVING=Salvataggio tema personalizzato... diff --git a/mod/etc/options_menu/themes/lang/ja-JP.lang b/mod/etc/options_menu/themes/lang/ja-JP.lang index 7beaeb8..bef3c26 100644 --- a/mod/etc/options_menu/themes/lang/ja-JP.lang +++ b/mod/etc/options_menu/themes/lang/ja-JP.lang @@ -69,8 +69,12 @@ CATEGORY_UI_MOD=カテゴリ: UI Mod CHECKING_THEME_FILES=えらんだThemeのFileをかくにんしています DOWNLOADING=downloadしています... INSTALLATION_COMPLETE=インストールかんりょう +NOT_ENOUGH_SPACE=あきようりょうがたりません. ようりょうをあけて, もういちどおためしください. +THEMES_FAILED=これらのThemeはインストールできませんでした: THEME_LIST_DOWNLOADING_LINE1=Themeいちらんを download しています THEME_LIST_DOWNLOADING_LINE2=おまちください . . . +THEME_MOVING_LINE1=ThemeをUSB/SDにいどうしています +THEME_MOVING_LINE2=おまちください . . . THEME_LOADING=よみこみちゅう... THEME_LOADING_ASSETS=Assetをよみこみちゅう... THEME_SAVING=じぶんのThemeをほぞんちゅう... diff --git a/mod/etc/options_menu/themes/lang/nl-NL.lang b/mod/etc/options_menu/themes/lang/nl-NL.lang index 391f55a..7310aea 100644 --- a/mod/etc/options_menu/themes/lang/nl-NL.lang +++ b/mod/etc/options_menu/themes/lang/nl-NL.lang @@ -69,8 +69,12 @@ CATEGORY_UI_MOD=Categorie: Interface-mod CHECKING_THEME_FILES=Bestanden van het gekozen thema worden gecontroleerd. DOWNLOADING=Downloaden... INSTALLATION_COMPLETE=Installatie voltooid. +NOT_ENOUGH_SPACE=Onvoldoende vrije ruimte, maak ruimte vrij en probeer het opnieuw. +THEMES_FAILED=Deze thema's konden niet worden geïnstalleerd: THEME_LIST_DOWNLOADING_LINE1=Themalijst Wordt Gedownload THEME_LIST_DOWNLOADING_LINE2=Even Geduld . . . +THEME_MOVING_LINE1=Thema's naar USB/SD verplaatsen +THEME_MOVING_LINE2=Even Geduld . . . THEME_LOADING=Laden... THEME_LOADING_ASSETS=Assets worden geladen... THEME_SAVING=Eigen thema wordt opgeslagen... diff --git a/mod/etc/options_menu/themes/lang/pt-PT.lang b/mod/etc/options_menu/themes/lang/pt-PT.lang index b579e92..2262685 100644 --- a/mod/etc/options_menu/themes/lang/pt-PT.lang +++ b/mod/etc/options_menu/themes/lang/pt-PT.lang @@ -69,8 +69,12 @@ CATEGORY_UI_MOD=Categoria: Mod de Interface CHECKING_THEME_FILES=A verificar os ficheiros do tema selecionado. DOWNLOADING=A descarregar... INSTALLATION_COMPLETE=Instalação concluída. +NOT_ENOUGH_SPACE=Não há espaço livre suficiente, liberta algum espaço e tenta novamente. +THEMES_FAILED=Estes temas não foram instalados: THEME_LIST_DOWNLOADING_LINE1=A Descarregar Lista de Temas THEME_LIST_DOWNLOADING_LINE2=Aguarde Por Favor . . . +THEME_MOVING_LINE1=A Mover Temas para USB/SD +THEME_MOVING_LINE2=Aguarde Por Favor . . . THEME_LOADING=A carregar... THEME_LOADING_ASSETS=A carregar recursos... THEME_SAVING=A guardar tema personalizado... diff --git a/mod/etc/options_menu/themes/lang/ru-RU.lang b/mod/etc/options_menu/themes/lang/ru-RU.lang index 5212462..52ab48b 100644 --- a/mod/etc/options_menu/themes/lang/ru-RU.lang +++ b/mod/etc/options_menu/themes/lang/ru-RU.lang @@ -69,8 +69,12 @@ CATEGORY_UI_MOD=Категория: Мод интерфейса CHECKING_THEME_FILES=Проверка файлов выбранной темы. DOWNLOADING=Загрузка... INSTALLATION_COMPLETE=Установка завершена. +NOT_ENOUGH_SPACE=Недостаточно свободного места, освободите место и повторите попытку. +THEMES_FAILED=Эти темы не удалось установить: THEME_LIST_DOWNLOADING_LINE1=Загрузка Списка Тем THEME_LIST_DOWNLOADING_LINE2=Пожалуйста, подождите . . . +THEME_MOVING_LINE1=Перемещение тем на USB/SD +THEME_MOVING_LINE2=Пожалуйста, подождите . . . THEME_LOADING=Загрузка... THEME_LOADING_ASSETS=Загрузка ресурсов... THEME_SAVING=Сохранение своей темы... diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 4e7d23a..dd75463 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -151,8 +151,13 @@ list_name="-list-v2.tar.gz" thm_chk="$mountpoint$profilepath/$modname/active_theme" system_name="$(echo "$s_type" | awk '{print toupper($0)}')" om_mod_path="$nand_path/$modtitle" +nand_theme_path="$nand_path/$modtitle/$s_type" theme_path="$usb_path/$modtitle/$s_type" -[ ! -d "$theme_path" ] && theme_path="$nand_path/$modtitle/$s_type" +if [ -d "$usb_path" ]; then + mkdir -p "$theme_path" +else + theme_path="$nand_theme_path" +fi hasMusicFiles() { find "$1" -maxdepth 1 -type f -iname "*.wav" 2>/dev/null | read -r _ } @@ -199,6 +204,29 @@ themeIsBindable() { } +migrateThemes() { + [ "$theme_path" = "$nand_theme_path" ] && return 1 + [ -n "$(ls -A "$nand_theme_path" 2> /dev/null)" ] || return 1 + for item in "$nand_theme_path"/*; do + name="${item##*/}" + if [ "$name" != "$sfregion" ] && [ ! -e "$theme_path/$name" ]; then + "$om_scripts/gen_splash" /tmp/om_splash.png THEME_MOVING_LINE1 THEME_MOVING_LINE2 && decodepng /tmp/om_splash.png > /dev/fb0 + break + fi + done + for item in "$nand_theme_path"/*; do + dest="$theme_path/${item##*/}" + rm -rf "$dest" + if cp -a "$item" "$dest"; then + rm -rf "$item" + else + rm -rf "$dest" + fi + done + [ -f "$thm_chk" ] && sed -i "s#^$nand_theme_path/#$theme_path/#" "$thm_chk" + return 0 +} + hasThemesToInstall() { [ "$has_theme" = 0 ] && return for f in "$theme_path/$system_name".*.tar.gz; do @@ -217,7 +245,7 @@ clearList() { clearDownloads() { find "$om_mod_sub_cmds" "$om_mod_sub_scripts" "$om_mod_sub" -type f \ -not -name 'c000[0-8]_*' -not -name 'c999[0-9]_*' \ - -not -name 'om_*' -not -name '_*' -exec rm {} + + -not -name 'om_*' -not -name '_*' -not -path "$thm_preview/*" -exec rm {} + } enableMenuOptions() { @@ -288,7 +316,7 @@ themeLoader() { fi fi - if [ ! -f "$full_path.png" ]; then + if [ ! -s "$full_path.png" ]; then if [ -d "$usb_path" ]; then copier "$thm_preview/$thm.png" "$full_path.png" else @@ -343,11 +371,9 @@ DELETE_STR=rm -rf \"$full_path\" \"$full_path.png\" \"$cmd_list\" \"$thm_script. } removeRegionalTheme() { - region_files="$om_mod_sub_cmds/c0010_snesce-$sfregion $om_mod_sub_scripts/getSnes$sfregion $om_mod_sub/$sfregion.png $om_mod_sub/snesce-$sfregion.png" - for item in $region_files; do - remove "$item" - done - [ -d "$theme_path/snesce-$sfregion" ] && rm -rf "$theme_path/snesce-$sfregion" + [ "$(globalTheme)" = "$sfregion" ] && return + remove "$theme_path/$sfregion" "$theme_path/$sfregion.png" \ + "$om_mod_commands/c0001_$sfregion" "$om_mod_scripts/$sfregion.sh" } getThemeList() { @@ -356,7 +382,12 @@ getThemeList() { if [ ! -e "${s_type}${list_name}" ]; then clearDownloads - theme_downloader catalog "$s_type" "${s_type}${list_name}" || netChecker + theme_downloader catalog "$s_type" "${s_type}${list_name}" + case "$?" in + 0) ;; + 3) translate NOT_ENOUGH_SPACE ;; + *) netChecker ;; + esac fi # shellcheck disable=SC2035 tar -xzf "${s_type}${list_name}" && rm -f *.tar.gz @@ -419,38 +450,72 @@ themeGenerator() { full_title="$(grep -F 'NAME=' "${om_mod_sub_cmds}/c0010_${theme_name}" | sed -e 's/.*=\(.*\)\[.*/\1/')" full_path="${theme_path}/${theme_name}" cd "${theme_path}" || exit + translate DOWNLOADING if [ "${theme_name}" = "all" ]; then - ok=0 - if theme_downloader all-manifest "$sftype" "${sftype}-all"; then - ok=1 - translate DOWNLOADING + theme_downloader all-manifest "$sftype" "${sftype}-all" + rc=$? + failed="" + if [ "$rc" = 0 ]; then while IFS= read -r t; do echo "$t" echo "" - theme_downloader theme "$sftype" "$t" "${system_name}.${t}.tar.gz" \ - && tar -xzvf "${system_name}.${t}.tar.gz" && remove "${system_name}.${t}.tar.gz" + downloadTheme "$t" + case "$?" in + 0) ;; + 3) rc=3 && break ;; + *) failed="$failed $t" ;; + esac done < "${sftype}-all" fi + remove "${sftype}-all" else - ok=0 - if theme_downloader theme "$sftype" "$theme_name" "${system_name}.${theme_name}.tar.gz"; then - ok=1 - translate DOWNLOADING + downloadTheme "$theme_name" + rc=$? + if [ "$rc" = 0 ]; then + size="$(du -sh "${full_path}" | cut -f1)" + echo "NAME=${full_title}[${size}b]" > "${full_path}/info" fi fi - if [ "$ok" = 1 ]; then - [ -f "${system_name}.${theme_name}.tar.gz" ] && tar -xzvf "${system_name}.${theme_name}.tar.gz" && rm -f ./*.tar.gz - remove "${sftype}-all" - removeRegionalTheme + removeRegionalTheme + echo "" + case "$rc" in + 0) translate INSTALLATION_COMPLETE ;; + 3) translate NOT_ENOUGH_SPACE ;; + *) netChecker ;; + esac + if [ -n "$failed" ]; then echo "" - size="$(du -sh "${full_path}" | cut -f1)" - echo "NAME=${full_title}[${size}b]" > "${full_path}/info" - translate INSTALLATION_COMPLETE + translate THEMES_FAILED + echo "$failed" + fi +} + +freeSpaceKb() { + df -Pk "$1" | awk 'NR==2 {print $4}' +} + +unpackedSizeKb() { + size_kb=$(( $(tail -c4 "$1" | od -An -tu4) / 1024 + 1 )) + echo $(( size_kb + size_kb / 10 )) +} + +downloadTheme() { + tarball="${system_name}.$1.tar.gz" + theme_downloader theme "$sftype" "$1" "$tarball" || return + if [ "$(freeSpaceKb .)" -lt "$(unpackedSizeKb "$tarball")" ]; then + dl_rc=3 + gzip -t "$tarball" 2> /dev/null || dl_rc=1 + elif ! tar -xzvf "$tarball"; then + dl_rc=1 + [ "$(freeSpaceKb .)" -lt 1024 ] && dl_rc=3 else - netChecker + remove "$tarball" + return 0 fi + rm -rf "$tarball" "$1" "$1.png" + return "$dl_rc" } findDiyAsset() { diff --git a/mod/install b/mod/install index 5488a2a..2582b1b 100644 --- a/mod/install +++ b/mod/install @@ -82,9 +82,6 @@ transfer_default # saves aren't mounted yet during install - om_theme_selector resets settings on first run om_firstrun="$rootfs/etc/options_menu/themes/om_firstrun" [ ! -f "$om_firstrun" ] && touch "$om_firstrun" -copy "$om_themes_scripts/om_fontfix" "$pre_initd/b8025_fontfix" -copy "$om_themes_scripts/om_chmenu" "$rootfs/bin/chmenu" -chmod +x "$rootfs/bin/chmenu" chmod +x "$rootfs/bin/sprite_crop" chmod +x "$rootfs/bin/theme_downloader" chmod +x "$rootfs/etc/options_menu/lib/theme_manager" diff --git a/src/theme_downloader.c b/src/theme_downloader.c index 500883f..accb8e6 100644 --- a/src/theme_downloader.c +++ b/src/theme_downloader.c @@ -82,30 +82,32 @@ static size_t write_to_file(void *ptr, size_t size, size_t nmemb, void *userdata return fwrite(ptr, size, nmemb, (FILE *)userdata); } -static int download_to(const char *url, const char *out_path) { +enum fetch_result { FETCH_FAILED, FETCH_OK, FETCH_WRITE_ERROR }; + +static enum fetch_result download_to(const char *url, const char *out_path) { FILE *f = fopen(out_path, "wb"); - if (!f) return 0; + if (!f) return FETCH_WRITE_ERROR; CURL *c = make_handle(url, 10L, 0L); if (!c) { fclose(f); - return 0; + remove(out_path); + return FETCH_FAILED; } curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, write_to_file); curl_easy_setopt(c, CURLOPT_WRITEDATA, f); CURLcode res = curl_easy_perform(c); curl_easy_cleanup(c); - fclose(f); - if (res != CURLE_OK) { - remove(out_path); - return 0; - } - return 1; + int closed = fclose(f) == 0; + if (res == CURLE_OK && closed) return FETCH_OK; + remove(out_path); + return (res == CURLE_WRITE_ERROR || !closed) ? FETCH_WRITE_ERROR : FETCH_FAILED; } -static int fetch_with_fallback(const char *gh_url, const char *fallback_url, const char *out_path) { - if (url_reachable(gh_url) && download_to(gh_url, out_path)) return 1; - if (url_reachable(fallback_url) && download_to(fallback_url, out_path)) return 1; - return 0; +static enum fetch_result fetch_with_fallback(const char *gh_url, const char *fallback_url, const char *out_path) { + enum fetch_result r = FETCH_FAILED; + if (url_reachable(gh_url)) r = download_to(gh_url, out_path); + if (r == FETCH_FAILED && url_reachable(fallback_url)) r = download_to(fallback_url, out_path); + return r; } static void usage(const char *prog) { @@ -128,14 +130,14 @@ int main(int argc, char **argv) { char gh_url[512]; char fallback_url[512]; - int ok = 0; + enum fetch_result result = FETCH_FAILED; if (strcmp(argv[1], "catalog") == 0 && argc == 4) { const char *sftype = argv[2]; const char *out_path = argv[3]; snprintf(gh_url, sizeof(gh_url), "%s/%s%s", GITHUB_BASE, sftype, LIST_SUFFIX); snprintf(fallback_url, sizeof(fallback_url), "%s%s%s", CLASSICMODS_SCRIPTS, sftype, LIST_SUFFIX); - ok = fetch_with_fallback(gh_url, fallback_url, out_path); + result = fetch_with_fallback(gh_url, fallback_url, out_path); } else if (strcmp(argv[1], "theme") == 0 && argc == 5) { const char *sftype = argv[2]; const char *theme_name = argv[3]; @@ -144,13 +146,13 @@ int main(int argc, char **argv) { to_upper(system_name, sftype, sizeof(system_name)); snprintf(gh_url, sizeof(gh_url), "%s/%s.%s.tar.gz", GITHUB_BASE, system_name, theme_name); snprintf(fallback_url, sizeof(fallback_url), "%s%s.%s.tar.gz", CLASSICMODS_THEMES, system_name, theme_name); - ok = fetch_with_fallback(gh_url, fallback_url, out_path); + result = fetch_with_fallback(gh_url, fallback_url, out_path); } else if (strcmp(argv[1], "all-manifest") == 0 && argc == 4) { const char *sftype = argv[2]; const char *out_path = argv[3]; snprintf(gh_url, sizeof(gh_url), "%s/%s-all", GITHUB_BASE, sftype); snprintf(fallback_url, sizeof(fallback_url), "%s%s-all", CLASSICMODS_SCRIPTS, sftype); - ok = fetch_with_fallback(gh_url, fallback_url, out_path); + result = fetch_with_fallback(gh_url, fallback_url, out_path); } else { usage(argv[0]); curl_global_cleanup(); @@ -158,5 +160,6 @@ int main(int argc, char **argv) { } curl_global_cleanup(); - return ok ? 0 : 1; + if (result == FETCH_WRITE_ERROR) return 3; + return result == FETCH_OK ? 0 : 1; } From 8ba3442fbc644a0dbd73f6390787c7a75b415d9f Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 6 Oct 2026 22:49:04 +0000 Subject: [PATCH 115/210] Set version 3.0.0-alpha4 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ff3a372..42a4229 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.0.0-alpha +3.0.0-alpha4 From c9cca314de2b16df6997e1e678540a56ce3351f6 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 10:09:32 +0000 Subject: [PATCH 116/210] Show each download's author as a strip over its grid preview --- src/command_loader.cpp | 29 +++++++++++++++------------ src/command_loader.h | 11 +++++++--- src/main.cpp | 8 ++++---- src/theme_manager_app.cpp | 42 +++++++++++++++++++++++++++++---------- src/theme_manager_app.h | 4 ++-- 5 files changed, 62 insertions(+), 32 deletions(-) diff --git a/src/command_loader.cpp b/src/command_loader.cpp index c280513..0afedd5 100644 --- a/src/command_loader.cpp +++ b/src/command_loader.cpp @@ -34,11 +34,12 @@ struct PipeCloser { void operator()(FILE * pipe) const { if(pipe) pclose(pipe); using PipeHandle = std::unique_ptr; // my own convention, not OptionsMenu's Command format -struct ConsoleOnlyFlags { bool nesOnly = false; bool snesOnly = false; }; +struct SelectorKeys { bool nesOnly = false; bool snesOnly = false; std::string author; }; -ConsoleOnlyFlags ReadConsoleOnlyFlags(const std::string & path) +SelectorKeys ReadSelectorKeys(const std::string & path) { - ConsoleOnlyFlags flags; + const std::string authorKey = "AUTHOR="; + SelectorKeys keys; std::ifstream in(path); std::string line; while(std::getline(in, line)) @@ -46,11 +47,13 @@ ConsoleOnlyFlags ReadConsoleOnlyFlags(const std::string & path) if(!line.empty() && line.back() == '\r') line.pop_back(); if(line == "NES_ONLY=TRUE") - flags.nesOnly = true; + keys.nesOnly = true; else if(line == "SNES_ONLY=TRUE") - flags.snesOnly = true; + keys.snesOnly = true; + else if(line.compare(0, authorKey.size(), authorKey) == 0) + keys.author = line.substr(authorKey.size()); } - return flags; + return keys; } } @@ -119,7 +122,7 @@ std::string ReadBackStack(bool isRootScreen) } bool LoadCommands(const std::string & commandLocation, const std::string & scriptLocation, const std::string & optionsLocation, - std::vector & commands, std::vector & isThemeItem) + std::vector & commands, std::vector & items) { DirHandle dir(opendir(commandLocation.c_str())); if(!dir) @@ -147,15 +150,15 @@ bool LoadCommands(const std::string & commandLocation, const std::string & scrip // c0000_0000 leading sentinel only - a later empty COMMAND_STR is a reserved blank slot instead if(commands.empty() && c.command.empty()) continue; - ConsoleOnlyFlags consoleOnly = ReadConsoleOnlyFlags(commandLocation + file); - if(consoleOnly.nesOnly || consoleOnly.snesOnly) + SelectorKeys keys = ReadSelectorKeys(commandLocation + file); + if(keys.nesOnly || keys.snesOnly) { if(!sftypeLoaded) { sftype = ReadSftype(); sftypeLoaded = true; } - if((consoleOnly.nesOnly && sftype != "nes") || (consoleOnly.snesOnly && sftype == "nes")) + if((keys.nesOnly && sftype != "nes") || (keys.snesOnly && sftype == "nes")) continue; } if(!c.command.empty()) @@ -172,13 +175,13 @@ bool LoadCommands(const std::string & commandLocation, const std::string & scrip } } commands.push_back(c); - isThemeItem.push_back(file.compare(0, 6, "c0000_") != 0); + items.push_back({ file.compare(0, 6, "c0000_") != 0, std::move(keys.author) }); } return true; } void AppendBackNavigation(const std::string & optionsLocation, const AppOptions & options, const std::string & backStack, - std::vector & commands, std::vector & isThemeItem) + std::vector & commands, std::vector & items) { std::string backCommand; if(!backStack.empty()) @@ -227,5 +230,5 @@ void AppendBackNavigation(const std::string & optionsLocation, const AppOptions back.previewImage = optionsLocation + "images/preview_placeholder.png"; back.command = backCommand; commands.push_back(back); - isThemeItem.push_back(false); + items.push_back({ false, "" }); } diff --git a/src/command_loader.h b/src/command_loader.h index 149667f..b8153b6 100644 --- a/src/command_loader.h +++ b/src/command_loader.h @@ -38,11 +38,16 @@ AppOptions ParseArgs(int argc, char * argv[], const std::string & optionsLocatio // OM_BACK_STACK is set by OptionsMenu before launching "Theme Options" std::string ReadBackStack(bool isRootScreen); -// isThemeItem[i] is false for a fixed "c0000_*" action, true for a theme/asset row +struct ItemInfo +{ + bool isThemeItem; // false for a fixed "c0000_*" action, true for a theme/asset row + std::string author; +}; + bool LoadCommands(const std::string & commandLocation, const std::string & scriptLocation, const std::string & optionsLocation, - std::vector & commands, std::vector & isThemeItem); + std::vector & commands, std::vector & items); void AppendBackNavigation(const std::string & optionsLocation, const AppOptions & options, const std::string & backStack, - std::vector & commands, std::vector & isThemeItem); + std::vector & commands, std::vector & items); #endif diff --git a/src/main.cpp b/src/main.cpp index 7ad84ef..6793440 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -33,11 +33,11 @@ int main(int argc, char * argv[]) UiTheme::LoadThemeConfig(optionsLocation); std::vector commands; - std::vector isThemeItem; - if(!LoadCommands(options.commandLocation, options.scriptLocation, optionsLocation, commands, isThemeItem)) + std::vector items; + if(!LoadCommands(options.commandLocation, options.scriptLocation, optionsLocation, commands, items)) return 1; - AppendBackNavigation(optionsLocation, options, backStack, commands, isThemeItem); + AppendBackNavigation(optionsLocation, options, backStack, commands, items); if(commands.empty()) { @@ -45,6 +45,6 @@ int main(int argc, char * argv[]) return 1; } - ThemeManagerApp app(optionsLocation, std::move(options), std::move(commands), std::move(isThemeItem)); + ThemeManagerApp app(optionsLocation, std::move(options), std::move(commands), std::move(items)); return app.Run(); } diff --git a/src/theme_manager_app.cpp b/src/theme_manager_app.cpp index dbbb25b..f5c5216 100644 --- a/src/theme_manager_app.cpp +++ b/src/theme_manager_app.cpp @@ -45,11 +45,11 @@ namespace } } -ThemeManagerApp::ThemeManagerApp(std::string optionsLocation, AppOptions options, std::vector commands, std::vector isThemeItem) +ThemeManagerApp::ThemeManagerApp(std::string optionsLocation, AppOptions options, std::vector commands, std::vector items) : optionsLocation_(std::move(optionsLocation)) , options_(std::move(options)) , commands_(std::move(commands)) - , isThemeItem_(std::move(isThemeItem)) + , items_(std::move(items)) { LoadActiveThemeBorderColor(optionsLocation_); fprintf(stderr, "CHECKPOINT 1: commands loaded, count=%zu\n", commands_.size()); fflush(stderr); @@ -92,7 +92,7 @@ void ThemeManagerApp::ComputePinnedAndThemeRanges() --pinnedStartIndex_; themeStart_ = 0; - while(themeStart_ < pinnedStartIndex_ && !isThemeItem_[themeStart_]) + while(themeStart_ < pinnedStartIndex_ && !items_[themeStart_].isThemeItem) ++themeStart_; } @@ -329,6 +329,7 @@ int ThemeManagerApp::RunGridLayout() std::vector chipLabels(commands_.size()); std::vector tileImages(commands_.size()); std::vector tileLabels(commands_.size()); + std::vector tileAuthors(commands_.size()); std::vector> tileRunFrames(commands_.size()); // sized >1 only for an animated _run01.png tile std::vector tileHideLabel(commands_.size(), false); // PREVIEW_HIDE_LABEL std::vector tileFitContain(commands_.size(), false); // PREVIEW_FIT_CONTAIN @@ -358,6 +359,11 @@ int ThemeManagerApp::RunGridLayout() { label = TruncateToWidth(label, 15, TileW - 28); tileLabels[i] = Texture(label, 15, renderer_, 0, 0, false, ToAbgr(UiTheme::Text), true); + if(!items_[i].author.empty()) + { + std::string author = TruncateToWidth("by: " + items_[i].author, 13, TileW - 20); + tileAuthors[i] = Texture(author, 13, renderer_, 0, 0, false, ToAbgr(UiTheme::Text), true); + } } } std::vector tileImageLoaded(commands_.size(), false); @@ -591,16 +597,32 @@ int ThemeManagerApp::RunGridLayout() Texture & tileArt = runFrames.size() > 1 ? runFrames[(SDL_GetTicks() / 100) % runFrames.size()] : tileImages[idx]; - if(tileArt.rect.w > 0) + Texture & author = tileAuthors[idx]; + if(tileArt.rect.w > 0 || author.rect.w > 0) { // clip to the tile, then mask the clip's square corners back to rounded SDL_RenderSetClipRect(renderer_, &tileRect); - if(tileFitContain[idx]) - // 10px margin keeps clear of the rounded-corner mask below - FitCentered(tileArt, x, y, TileW, TileH, 10); - else - FitCover(tileArt, x, y, TileW, TileH, 0); - tileArt.Draw(renderer_); + if(tileArt.rect.w > 0) + { + if(tileFitContain[idx]) + // 10px margin keeps clear of the rounded-corner mask below + FitCentered(tileArt, x, y, TileW, TileH, 10); + else + FitCover(tileArt, x, y, TileW, TileH, 0); + tileArt.Draw(renderer_); + } + if(author.rect.w > 0) + { + const int stripH = author.rect.h + 6; + const SDL_Rect strip{ x, y + TileH - stripH, TileW, stripH }; + SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0xB4); + SDL_RenderFillRect(renderer_, &strip); + SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_NONE); + author.rect.x = x + (TileW - author.rect.w) / 2; + author.rect.y = strip.y + (stripH - author.rect.h) / 2; + author.Draw(renderer_); + } SDL_RenderSetClipRect(renderer_, nullptr); DrawRoundedCornerMask(renderer_, tileRect, UiTheme::SelectedRowBg, UiTheme::BoxRadius); } diff --git a/src/theme_manager_app.h b/src/theme_manager_app.h index 34f86e7..df3bc81 100644 --- a/src/theme_manager_app.h +++ b/src/theme_manager_app.h @@ -23,7 +23,7 @@ class ThemeManagerApp { public: - ThemeManagerApp(std::string optionsLocation, AppOptions options, std::vector commands, std::vector isThemeItem); + ThemeManagerApp(std::string optionsLocation, AppOptions options, std::vector commands, std::vector items); int Run() { @@ -39,7 +39,7 @@ class ThemeManagerApp std::string optionsLocation_; AppOptions options_; std::vector commands_; - std::vector isThemeItem_; + std::vector items_; int pinnedStartIndex_ = 0; // BACK/EXIT sort last - pinned near the footer instead of scrolling int themeStart_ = 0; // grid layout only: first non-fixed-action row From f9694b35f655f1846d3939ddf33ff0804b377875 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 10:26:13 +0000 Subject: [PATCH 117/210] Build and release through hmod-build" --- .github/workflows/build.yml | 64 ++------------- .gitmodules | 3 + Makefile | 137 +++++++------------------------ Makefile.docker | 157 ------------------------------------ VERSION | 1 - build.sh | 4 +- hmod-build | 1 + 7 files changed, 42 insertions(+), 325 deletions(-) delete mode 100644 Makefile.docker delete mode 100644 VERSION create mode 160000 hmod-build diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2a72193..fc142dc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,59 +7,11 @@ on: pull_request: jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - with: - submodules: recursive # pngpack pulls in lodepng - - # The toolchain image needs the console's own SDL2 headers + libs in - # toolchain/vendor. They're proprietary, so they live in a private repo - # cloned with a read-only fine-grained PAT (secrets VENDOR_REPO and - # VENDOR_REPO_PAT, same as OptionsMenu). - - name: Restore vendor/ from private repo - run: | - git clone --depth 1 "https://x-access-token:${{ secrets.VENDOR_REPO_PAT }}@github.com/${{ secrets.VENDOR_REPO }}.git" toolchain/vendor - - - name: Build cross-compile toolchain image - run: docker build -f toolchain/Dockerfile.jessie-armhf -t classicmini-cross-toolchain:jessie-armhf toolchain - - # Tag builds take their version from the tag (v3.0.0-alpha -> 3.0.0-alpha) - # instead of the VERSION file. safe.directory: the container runs as root - # over a checkout owned by the runner user, and git refuses it otherwise - - # which leaves the Makefile's git-derived hmod name empty. - - name: Compile and package - run: | - MOD_VER_ARG="" - if [ "${{ github.ref_type }}" = "tag" ]; then - MOD_VER_ARG="MOD_VER=${GITHUB_REF_NAME#v}" - fi - docker run --rm -v "${{ github.workspace }}:/src" classicmini-cross-toolchain:jessie-armhf \ - sh -c "git config --global --add safe.directory '*' && make -f Makefile.docker CROSS_PREFIX=arm-linux-gnueabihf- $MOD_VER_ARG" - - - name: Upload .hmod build artifact - uses: actions/upload-artifact@v7 - with: - name: theme-selector-hmod - path: out/*.hmod - - release: - needs: build - if: startsWith(github.ref, 'refs/tags/v') - runs-on: ubuntu-latest - steps: - - name: Download build artifact - uses: actions/download-artifact@v8 - with: - name: theme-selector-hmod - path: out - - - name: Create GitHub Release - uses: softprops/action-gh-release@v3 - with: - name: Theme Selector ${{ github.ref_name }} - files: out/*.hmod - generate_release_notes: true - # -alpha/-beta/-rc tags must not take over "Latest" - prerelease: ${{ contains(github.ref_name, '-') }} + hmod: + uses: DefKorns/hmod-build/.github/workflows/hmod.yml@master + with: + release-name: Theme Selector + docker: true + secrets: inherit + permissions: + contents: write diff --git a/.gitmodules b/.gitmodules index cf7307c..7ecc750 100644 --- a/.gitmodules +++ b/.gitmodules @@ -8,3 +8,6 @@ path = vendor/OptionsMenu url = https://github.com/DefKorns/OptionsMenu branch = modern-ui-ttf +[submodule "hmod-build"] + path = hmod-build + url = https://github.com/DefKorns/hmod-build.git diff --git a/Makefile b/Makefile index ebfa203..d0e0ffe 100644 --- a/Makefile +++ b/Makefile @@ -1,40 +1,11 @@ -SHELL = /bin/sh -UNAME := $(shell uname) -MOD_NAME := Options Menu - Theme Selector -MOD_CREATOR := DefKorns +MOD_ID := om_theme-selector +MOD_NAME := Options Menu - Theme Selector MOD_CATEGORY := User Interface +MOD_EXCLUDE := exclude-file.txt +MOD_DEPS := mod/etc/options_menu/lib/theme_manager mod/bin/theme_downloader mod/bin/sprite_crop -LAST_TAG_COMMIT := $(shell git rev-list --tags --max-count=1) -LAST_TAG := $(shell git describe --tags $(LAST_TAG_COMMIT) 2> /dev/null || echo "v0.0.0") -TAG_PREFIX := "v" -CURRENT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD) -GIT_REMOTES := $(shell git remote | xargs echo ) -GIT_DIRTY := $(shell git diff --shortstat 2> /dev/null | tail -n1 ) -GET_VER := $(shell git describe --tags $(LAST_TAG_COMMIT) 2> /dev/null | sed "s/^$(TAG_PREFIX)//" || echo "0.0.0") -MOD_VER := $(shell [ -f VERSION ] && head VERSION || echo $(GET_VER)) -MAJOR := $(shell echo $(MOD_VER) | sed "s/^\([0-9]*\).*/\1/") -MINOR := $(shell echo $(MOD_VER) | sed "s/[0-9]*\.\([0-9]*\).*/\1/") -PATCH := $(shell echo $(MOD_VER) | sed "s/[0-9]*\.[0-9]*\.\([0-9]*\).*/\1/") -RC := $(shell echo $(MOD_VER) | sed -n "s/.*rc\([0-9]*\)$$/\1/p") -RC := $(if $(RC),$(RC),0) - -# total number of commits -BUILD := $(shell git log --oneline | wc -l | sed -e "s/[ \t]*//g") -NEXT_MAJOR_VERSION := $(shell expr $(MAJOR) + 1).0.0 -NEXT_MINOR_VERSION := $(MAJOR).$(shell expr $(MINOR) + 1).0-b$(BUILD) -NEXT_PATCH_VERSION := $(MAJOR).$(MINOR).$(shell expr $(PATCH) + 1)-b$(BUILD) -NEXT_RC_VERSION := $(MAJOR).$(MINOR).$(PATCH)-rc$(shell expr $(RC) + 1) - -MOD_URL := $(shell git config --get remote.origin.url) -GIT_COMMIT := $(shell git rev-parse --short HEAD)$(shell git diff-index --quiet HEAD -- || echo -dirty) -# Derive the package name from the remote; Docker mounts every repo at /src. -MOD_FILENAME := $(shell basename $(MOD_URL) .git) -DEV_DIR := $(CURDIR) -OUT := $(DEV_DIR)/out - -# Build without Docker when the ARM toolchain and dependencies are installed. -# Set CROSS_PREFIX=arm-linux-gnueabihf- to cross-compile for the console; -# leave unset to build natively. +# Set CROSS_PREFIX=arm-linux-gnueabihf- to cross-compile for the console +# (build.sh does this inside the Docker toolchain); leave unset to build natively. FRAMEWORK_DIR = vendor/OptionsMenu/src/framework CXX = g++ STRIP = strip @@ -48,31 +19,26 @@ SDL_CFLAGS = $(shell sdl2-config --cflags) $(shell pkg-config --cflags SDL2_ttf) SDL_LIBS = $(shell sdl2-config --libs) $(shell pkg-config --libs SDL2_ttf) -lpng LDFLAGS = endif -CXXFLAGS = -std=c++11 -Os -Ivendor/OptionsMenu/src $(SDL_CFLAGS) +CXXFLAGS = -std=c++11 -Os -Ivendor/OptionsMenu/src $(SDL_CFLAGS) -DMOD_VERSION=\"v$(MOD_VER)\" LDLIBS = $(SDL_LIBS) VENDOR_SRC_DIR = vendor/OptionsMenu/src SOURCES = src/main.cpp src/single_instance_lock.cpp src/command_loader.cpp src/texture_utils.cpp src/theme_manager_app.cpp src/folder_theme_model.cpp src/folder_themes_layout.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp OBJECTS = $(SOURCES:.cpp=.o) -# Build theme_downloader with the Docker toolchain's static curl+OpenSSL. -# No native fallback is available because the libraries live at CURL_PREFIX. +# Build theme_downloader with the toolchain's static curl+OpenSSL. CC = gcc CURL_PREFIX = /opt/curl-static CFLAGS = -O2 -Wall -I$(CURL_PREFIX)/include CURL_LDLIBS = -L$(CURL_PREFIX)/lib -L/usr/lib/arm-linux-gnueabihf -lcurl -lssl -lcrypto -lz -ldl -lpthread -# `all`/`hmod` package mod/ without compiling for toolchain-free CI. -# Use `full` to build the binaries from source first, then package. -# Avoid "build": GNU Make's .sh rule conflicts with build.sh. all: hmod -full: compile hmod - -compile: mod/etc/options_menu/lib/theme_manager mod/bin/theme_downloader +compile: $(MOD_DEPS) mod/etc/options_menu/lib/theme_manager: $(OBJECTS) - $(CROSS_PREFIX)$(CXX) $(OBJECTS) $(LDLIBS) $(LDFLAGS) -Wl,-rpath,/etc/options_menu/lib -o mod/etc/options_menu/lib/theme_manager - $(CROSS_PREFIX)$(STRIP) mod/etc/options_menu/lib/theme_manager + mkdir -p $(@D) + $(CROSS_PREFIX)$(CXX) $(OBJECTS) $(LDLIBS) $(LDFLAGS) -Wl,-rpath,/etc/options_menu/lib -o $@ + $(CROSS_PREFIX)$(STRIP) $@ %.o: %.cpp $(CROSS_PREFIX)$(CXX) $(CXXFLAGS) -c $< -o $@ @@ -89,73 +55,26 @@ src/ca_bundle.o: src/cacert.pem cd src && $(CROSS_PREFIX)ld -r -b binary -o ca_bundle.o cacert.pem mod/bin/theme_downloader: src/theme_downloader.o src/ca_bundle.o - $(CROSS_PREFIX)$(CC) src/theme_downloader.o src/ca_bundle.o $(CURL_LDLIBS) -o mod/bin/theme_downloader - $(CROSS_PREFIX)$(STRIP) mod/bin/theme_downloader - upx --best mod/bin/theme_downloader - -hmod: clean - - mkdir -p out/ temp/ - rsync -a --exclude-from=exclude-file.txt mod/ temp/ --links --delete - - printf "%s\n" \ - "---" \ - "Name: $(MOD_NAME)" \ - "Creator: $(MOD_CREATOR)" \ - "Category: $(MOD_CATEGORY)" \ - "Version: $(MOD_VER)" \ - "Built on: $(shell date +"%A, %d %b %Y - %T")" \ - "Git commit: $(GIT_COMMIT)" \ - "---" > temp/readme.md - - sed 1d mod/readme.md >> temp/readme.md - - cd temp/; tar -czf $(OUT)/$(MOD_FILENAME)-$(MOD_VER).hmod * - rm -r temp/ - -fix: hmod - @ver="$(NEXT_PATCH_VERSION)" && \ - git tag "v$(MOD_VER)" && \ - echo "$$ver" > VERSION && \ - git add VERSION && \ - git commit -m "Bump version to v$$ver" - -rc: hmod - @ver="$(NEXT_RC_VERSION)" && \ - git tag "v$(MOD_VER)" && \ - echo "$$ver" > VERSION && \ - git add VERSION && \ - git commit -m "Bump version to v$$ver" - -update: all - @ver="$(NEXT_MINOR_VERSION)" && \ - git tag "v$(MOD_VER)" && \ - echo "$$ver" > VERSION && \ - git add VERSION && \ - git commit -m "Bump version to v$$ver" - -upgrade: all - @ver="$(NEXT_MAJOR_VERSION)" && \ - git tag "v$(MOD_VER)" && \ - echo "$$ver" > VERSION && \ - git add VERSION && \ - git commit -m "Bump version to v$$ver" - -info: - @echo "Mod Dir: $(MOD_FILENAME)" - @echo "Current version: $(MOD_VER)" - @echo "Last tag: $(LAST_TAG)" - @echo "$(shell git rev-list $(LAST_TAG).. --count) commit(s) since last tag" - @echo "Build: $(BUILD) (total number of commits)" - @echo "next major version: $(NEXT_MAJOR_VERSION)" - @echo "next minor version: $(NEXT_MINOR_VERSION)" - @echo "next patch version: $(NEXT_PATCH_VERSION)" + mkdir -p $(@D) + $(CROSS_PREFIX)$(CC) src/theme_downloader.o src/ca_bundle.o $(CURL_LDLIBS) -o $@ + $(CROSS_PREFIX)$(STRIP) $@ + upx --best $@ + +# DIY sprite previews (om_vars). Built by pngpack's own Makefile; its default +# LDFLAGS (-dead_strip) is macOS-only. +mod/bin/sprite_crop: pngpack/pngpacker/sprite_crop.cpp pngpack/pngpacker/pixeltools.cpp + mkdir -p $(@D) + $(MAKE) -C pngpack build/sprite_crop CROSS=$(CROSS_PREFIX) LDFLAGS=-Wl,--gc-sections + cp pngpack/build/sprite_crop $@ update-headers: sh tools/update-license-headers.sh $(YEAR) clean: - rm -rf out/ temp/ + find . -name "*.o" -type f -not -path "./toolchain/*" -delete + rm -f $(MOD_DEPS) + rm -rf out/ +include hmod-build/hmod.mk -.PHONY: all full compile hmod fix rc update upgrade info update-headers clean +.PHONY: all compile update-headers clean diff --git a/Makefile.docker b/Makefile.docker deleted file mode 100644 index ac8346c..0000000 --- a/Makefile.docker +++ /dev/null @@ -1,157 +0,0 @@ -# Cross-compile against the vendored OptionsMenu framework, then package the -# result as a .hmod. Run inside the Docker toolchain image from build.sh. -FRAMEWORK_DIR = vendor/OptionsMenu/src/framework -CXX = g++ -STRIP = strip -ifdef CROSS_PREFIX -PKG_CONFIG_LIBDIR = /usr/lib/arm-linux-gnueabihf/pkgconfig -SDL_CFLAGS = -I/usr/include/arm-linux-gnueabihf $(shell PKG_CONFIG_LIBDIR=$(PKG_CONFIG_LIBDIR) pkg-config --cflags sdl2 SDL2_ttf libpng) -SDL_LIBS = $(shell PKG_CONFIG_LIBDIR=$(PKG_CONFIG_LIBDIR) pkg-config --libs sdl2 SDL2_ttf libpng) -LDFLAGS = -Wl,--allow-shlib-undefined -else -SDL_CFLAGS = $(shell sdl2-config --cflags) $(shell pkg-config --cflags SDL2_ttf) -SDL_LIBS = $(shell sdl2-config --libs) $(shell pkg-config --libs SDL2_ttf) -lpng -LDFLAGS = -endif -CXXFLAGS = -std=c++11 -Os -Ivendor/OptionsMenu/src $(SDL_CFLAGS) -DMOD_VERSION=\"v$(MOD_VER)\" -LDLIBS = $(SDL_LIBS) -VENDOR_SRC_DIR = vendor/OptionsMenu/src -SOURCES = src/main.cpp src/single_instance_lock.cpp src/command_loader.cpp src/texture_utils.cpp src/theme_manager_app.cpp src/folder_theme_model.cpp src/folder_themes_layout.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp -OBJECTS = $(SOURCES:.cpp=.o) - -# Build theme_downloader with the toolchain's static curl+OpenSSL. -CC = gcc -CURL_PREFIX = /opt/curl-static -CFLAGS = -O2 -Wall -I$(CURL_PREFIX)/include -CURL_LDLIBS = -L$(CURL_PREFIX)/lib -L/usr/lib/arm-linux-gnueabihf -lcurl -lssl -lcrypto -lz -ldl -lpthread - -MOD_NAME := Options Menu - Theme Selector -MOD_CREATOR := DefKorns -MOD_CATEGORY := User Interface - -LAST_TAG_COMMIT := $(shell git rev-list --tags --max-count=1) -LAST_TAG := $(shell git describe --tags $(LAST_TAG_COMMIT) 2> /dev/null || echo "v0.0.0") -TAG_PREFIX := "v" -GET_VER := $(shell git describe --tags $(LAST_TAG_COMMIT) 2> /dev/null | sed "s/^$(TAG_PREFIX)//" || echo "0.0.0") -MOD_VER := $(shell [ -f VERSION ] && head VERSION || echo $(GET_VER)) -MAJOR := $(shell echo $(MOD_VER) | sed "s/^\([0-9]*\).*/\1/") -MINOR := $(shell echo $(MOD_VER) | sed "s/[0-9]*\.\([0-9]*\).*/\1/") -PATCH := $(shell echo $(MOD_VER) | sed "s/[0-9]*\.[0-9]*\.\([0-9]*\).*/\1/") -RC := $(shell echo $(MOD_VER) | sed -n "s/.*rc\([0-9]*\)$$/\1/p") -RC := $(if $(RC),$(RC),0) - -# total number of commits -BUILD := $(shell git log --oneline | wc -l | sed -e "s/[ \t]*//g") -NEXT_MAJOR_VERSION := $(shell expr $(MAJOR) + 1).0.0 -NEXT_MINOR_VERSION := $(MAJOR).$(shell expr $(MINOR) + 1).0-b$(BUILD) -NEXT_PATCH_VERSION := $(MAJOR).$(MINOR).$(shell expr $(PATCH) + 1)-b$(BUILD) -NEXT_RC_VERSION := $(MAJOR).$(MINOR).$(PATCH)-rc$(shell expr $(RC) + 1) - -GIT_COMMIT := $(shell git rev-parse --short HEAD)$(shell git diff-index --quiet HEAD -- || echo -dirty) -MOD_URL := $(shell git config --get remote.origin.url) -# Derive the package name from the remote; Docker mounts every repo at /src. -MOD_FILENAME := $(shell basename $(MOD_URL) .git) -DEV_DIR := $(CURDIR) -OUT := $(DEV_DIR)/out - -# Avoid "build": GNU Make's .sh rule conflicts with build.sh. -all: compile hmod - -compile: mod/etc/options_menu/lib/theme_manager mod/bin/theme_downloader mod/bin/sprite_crop - -mod/etc/options_menu/lib/theme_manager: $(OBJECTS) - mkdir -p $(@D) - $(CROSS_PREFIX)$(CXX) $(OBJECTS) $(LDLIBS) $(LDFLAGS) -Wl,-rpath,/etc/options_menu/lib -o mod/etc/options_menu/lib/theme_manager - $(CROSS_PREFIX)$(STRIP) mod/etc/options_menu/lib/theme_manager - -%.o: %.cpp - $(CROSS_PREFIX)$(CXX) $(CXXFLAGS) -c $< -o $@ - -%.o: %.c - $(CROSS_PREFIX)$(CC) $(CFLAGS) -c $< -o $@ - -# Fetch the gitignored CA bundle for fresh builds. -src/cacert.pem: - curl -fL -o src/cacert.pem https://curl.se/ca/cacert.pem - -# Embed cacert.pem from src/ to keep "src/" out of the symbol names. -src/ca_bundle.o: src/cacert.pem - cd src && $(CROSS_PREFIX)ld -r -b binary -o ca_bundle.o cacert.pem - -mod/bin/theme_downloader: src/theme_downloader.o src/ca_bundle.o - mkdir -p $(@D) - $(CROSS_PREFIX)$(CC) src/theme_downloader.o src/ca_bundle.o $(CURL_LDLIBS) -o mod/bin/theme_downloader - $(CROSS_PREFIX)$(STRIP) mod/bin/theme_downloader - upx --best mod/bin/theme_downloader - -# DIY sprite previews (om_vars). Built by pngpack's own Makefile; its default -# LDFLAGS (-dead_strip) is macOS-only. -mod/bin/sprite_crop: pngpack/pngpacker/sprite_crop.cpp pngpack/pngpacker/pixeltools.cpp - mkdir -p $(@D) - $(MAKE) -C pngpack build/sprite_crop CROSS=$(CROSS_PREFIX) LDFLAGS=-Wl,--gc-sections - cp pngpack/build/sprite_crop $@ - -# Package mod/ without recompiling; run `all` first for fresh binaries. -hmod: - mkdir -p out/ temp/ - rsync -a --exclude-from=exclude-file.txt mod/ temp/ --links --delete - - printf "%s\n" \ - "---" \ - "Name: $(MOD_NAME)" \ - "Creator: $(MOD_CREATOR)" \ - "Category: $(MOD_CATEGORY)" \ - "Version: $(MOD_VER)" \ - "Built on: $(shell date +"%A, %d %b %Y - %T")" \ - "Git commit: $(GIT_COMMIT)" \ - "---" > temp/readme.md - - sed 1d mod/readme.md >> temp/readme.md - - cd temp/; tar -czf $(OUT)/$(MOD_FILENAME)-$(MOD_VER).hmod * - rm -r temp/ - -fix: hmod - @ver="$(NEXT_PATCH_VERSION)" && \ - git tag "v$(MOD_VER)" && \ - echo "$$ver" > VERSION && \ - git add VERSION && \ - git commit -m "Bump version to v$$ver" - -rc: hmod - @ver="$(NEXT_RC_VERSION)" && \ - git tag "v$(MOD_VER)" && \ - echo "$$ver" > VERSION && \ - git add VERSION && \ - git commit -m "Bump version to v$$ver" - -update: all - @ver="$(NEXT_MINOR_VERSION)" && \ - git tag "v$(MOD_VER)" && \ - echo "$$ver" > VERSION && \ - git add VERSION && \ - git commit -m "Bump version to v$$ver" - -upgrade: all - @ver="$(NEXT_MAJOR_VERSION)" && \ - git tag "v$(MOD_VER)" && \ - echo "$$ver" > VERSION && \ - git add VERSION && \ - git commit -m "Bump version to v$$ver" - -info: - @echo "Mod Dir: $(MOD_FILENAME)" - @echo "Current version: $(MOD_VER)" - @echo "Last tag: $(LAST_TAG)" - @echo "$(shell git rev-list $(LAST_TAG).. --count) commit(s) since last tag" - @echo "Build: $(BUILD) (total number of commits)" - @echo "next major version: $(NEXT_MAJOR_VERSION)" - @echo "next minor version: $(NEXT_MINOR_VERSION)" - @echo "next patch version: $(NEXT_PATCH_VERSION)" - -clean: - find . -name "*.o" -type f -delete - rm -f mod/etc/options_menu/lib/theme_manager mod/bin/theme_downloader - rm -rf out/ temp/ - -.PHONY: all compile hmod fix rc update upgrade info clean diff --git a/VERSION b/VERSION deleted file mode 100644 index 42a4229..0000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -3.0.0-alpha4 diff --git a/build.sh b/build.sh index 5d4c711..2e9a323 100644 --- a/build.sh +++ b/build.sh @@ -22,7 +22,7 @@ if ! docker image inspect "$IMAGE" >/dev/null 2>&1; then fi echo "==> Running make inside the container..." -docker run --rm -v "$PWD:/src" "$IMAGE" make -f Makefile.docker CROSS_PREFIX=arm-linux-gnueabihf- "$@" +docker run --rm -v "$PWD:/src" "$IMAGE" make CROSS_PREFIX=arm-linux-gnueabihf- "$@" echo "==> Build complete:" -ls -la mod/etc/options_menu/lib/theme_manager mod/bin/theme_downloader 2>/dev/null +ls -la mod/etc/options_menu/lib/theme_manager mod/bin/theme_downloader out/*.hmod 2>/dev/null diff --git a/hmod-build b/hmod-build new file mode 160000 index 0000000..38654e9 --- /dev/null +++ b/hmod-build @@ -0,0 +1 @@ +Subproject commit 38654e9535a04120c81a9276447d785514001c59 From 9b41ceb7bd815389db4917d2ed12953c160ff2fc Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 10:41:29 +0000 Subject: [PATCH 118/210] Rewrite the readmes for Theme Selector 3 --- README.md | 86 +++++++++++++++++++++++++-------------------------- mod/readme.md | 77 ++++++++++++++++++--------------------------- 2 files changed, 73 insertions(+), 90 deletions(-) diff --git a/README.md b/README.md index 7a22e85..700cf4b 100644 --- a/README.md +++ b/README.md @@ -1,82 +1,82 @@ # Options Menu - Theme Selector +[![Build](https://github.com/DefKorns/om_theme-selector/actions/workflows/build.yml/badge.svg)](https://github.com/DefKorns/om_theme-selector/actions/workflows/build.yml) +[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](LICENSE) + **Requires [my Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases) as a base — not compatible with any other UI.** `Click the image below to view it on YouTube.` -[![Theme Selector - Demo](https://i.imgur.com/7JgP6JI.png)](https://youtu.be/yRUbGssu8ko "Theme Selector - Demo") +[![Theme Selector - Demo](https://i.imgur.com/7JgP6JI.png)](https://youtu.be/3UilWr1NvFA "Theme Selector - Demo") ## What is it? -A graphical theme manager for NES/SNES/Famicom/Super Famicom Classic consoles, built on top of [my fork](https://github.com/DefKorns/OptionsMenu) of [CompCom's OptionsMenu](https://github.com/CompCom/OptionsMenu). Pick a theme from a preview grid, download more from a catalog, build your own from pieces of the themes you already have, and tune per-folder/randomizer/audio behavior — all from the console itself, no PC required after install. +A graphical theme manager for the NES, SNES, Famicom and Super Famicom Classic consoles, built on top of [my fork](https://github.com/DefKorns/OptionsMenu) of [CompCom's OptionsMenu](https://github.com/CompCom/OptionsMenu). Pick a theme from a preview grid, download more from an online catalog, build your own from pieces of the themes you already have, and give each folder its own theme and music — all from the console itself, no PC needed after install. + +The UI is a standalone C++/SDL app (`theme_manager`) built against the vendored Options Menu engine, not a set of scripts layered on the stock menu. -The UI is my own standalone C++/SDL app (`theme_manager`), not a set of Lua scripts layered on the stock menu — it's built from source against the vendored engine (see [Building from source](#building-from-source)). +Full guides are on the **[wiki](https://github.com/DefKorns/om_theme-selector/wiki)**. ## Features -- Preview grid to browse and apply installed themes; the theme currently applied is outlined so you can spot it at a glance -- Download themes directly from the internet (**Wi-Fi mod required**) -- Build your own DIY theme from pieces of the themes you already have (background, sprites, colors, packed art, fonts...) -- Theme randomizer — a different theme each time you go Home (off by default) -- Audio randomizer on the Home folder, optionally extended to every folder (off by default) -- Theme per folder: give a folder its own theme by naming a theme after it; a folder with its own theme keeps its own icon instead of inheriting the parent theme's +- **Installed themes grid** — browse your themes by their preview and apply one; the active theme is outlined +- **Download themes** from an online catalog of 90+ themes, each credited to its author (**Wi-Fi mod required**) +- **DIY theme** — mix the UI, background, demo characters, pole and colors of the themes you already have, preview it live, then save it as a theme of your own +- **Theme and music per folder** — give any folder (and Home) its own theme and its own menu music from one screen; folders without one inherit their parent's +- **Theme randomizer** — a different theme every time you change folder (off by default) +- **Audio randomizer** on Home, optionally on every folder (off by default) - Custom fonts per theme +- Delete a theme from the grid by holding **B** +- **Clean Up** removes the theme files your console type doesn't use +- **Reset Settings** puts the mod back to its defaults without uninstalling it or deleting your themes - Custom color palette for the Theme Manager UI itself (`theme.cfg`) -- System clean-up to remove files your console type doesn't need -- Reset just this mod's settings without a full uninstall - -## Set a theme per folder, what is that? - -If you have your games organized into system folders, you can give each one its own theme instead of one theme for the whole console. Turn on **Theme Per Folder** in Settings, then name a theme folder after the game folder it should apply to (lower snake_case). Eg: game folder `Nintendo - Nintendo Entertainment System` → theme folder `nintendo_-_nintendo_entertainment_system`. - -By default, a subfolder inherits its parent's theme. Turn on **Allow Different Theme To Subfolder** if you want a subfolder to use its own matching theme instead — and if it does, that subfolder keeps its own real icon rather than being overwritten by the parent theme's. - -## What if I want a specific theme on my main menu? - -Name a theme `default` and it becomes your Home menu theme. - -## What do you mean by "build your own DIY theme"? - -Exactly that — pick and mix backgrounds, sprites, colors and packed art from the themes already on your console/USB into a theme of your own, previewed live as you build it. - -## I own a Famicom/Shonen/Super Famicom, can I install this? - -Yes — it supports all Nintendo Classic consoles, all regions. +- Every console and region: NES, Famicom, Famicom Shonen Jump, SNES (USA/EUR) and Super Famicom ## Requirements -- [Hakchi CE](https://github.com/TeamShinkansen/hakchi2/releases/latest) +- [Hakchi2 CE](https://github.com/TeamShinkansen/hakchi2/releases/latest) - [My Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases) — the base UI this mod plugs into -- [Hakchi Wi-Fi mod (WPA Supplicant)](https://hakchi.net/hakchi/hmods/wpa-supplicant.hmod) — only needed to download themes from the internet +- [Hakchi Wi-Fi mod (WPA Supplicant)](https://hakchi.net/hakchi/hmods/wpa-supplicant.hmod) — only to download themes from the console +- A USB/SD drive is recommended: themes are often 5–30 MB each -## How do I use it +## Install -Works the same whether your games are on NAND or USB/SD: +**From my Mod Hub (recommended):** in hakchi open **Manage repositories**, add `https://defkorns.github.io/hakchi-repo/`, then install **Options Menu** and **Options Menu - Theme Selector** from it. Updates show up there automatically. -- Install the hmod -- Open Theme Options from the Options Menu, download or build a theme, and apply it +**By hand:** download `om_theme-selector.hmod` from [Releases](https://github.com/DefKorns/om_theme-selector/releases) and install it with hakchi (**Modules → Install extra modules**). -On NAND, themes live at `/var/lib/hakchi/usr/share/themes/`. -On USB/SD, themes live at `/media/hakchi/themes/`. +Then open the Options Menu (hold **L+R** on a SNES/Super Famicom, **B+Down** on a NES/Famicom) and go to **Themes**. Step by step, with screenshots: [Installation](https://github.com/DefKorns/om_theme-selector/wiki/Installation) and [Usage](https://github.com/DefKorns/om_theme-selector/wiki/Usage). -*`consoletype` is `nes`, `snes` or `shonen`.* +## Where things live + +| | USB/SD | NAND | +| --- | --- | --- | +| Themes | `/media/hakchi/themes/` | `/var/lib/hakchi/rootfs/usr/share/themes/` | +| Menu music | `/media/hakchi/music_menu` | `/var/lib/hakchi/rootfs/usr/share/music_menu` | + +`` is `nes`, `snes` or `shonen`. With a USB/SD drive connected, themes always go there. ## Customizing the look -The Theme Manager's own UI (not your game themes) reads its color palette from `/etc/options_menu/theme.cfg` at startup — edit `Key=R,G,B` lines there (background, borders, accent, badges, the active-theme border...) to restyle it, or delete a line to fall back to the default. +The Theme Manager's own UI (not your game themes) reads its color palette from `/etc/options_menu/theme.cfg` at startup — edit the `Key=R,G,B` lines (background, borders, accent, badges, the active-theme border...) or delete a line to fall back to the default. ## Building from source ```sh -./build.sh compile # cross-compiles theme_manager + theme_downloader via Docker -./build.sh hmod # packages mod/ into out/*.hmod +git clone --recursive https://github.com/DefKorns/om_theme-selector.git +cd om_theme-selector +./build.sh # cross-compiles theme_manager, theme_downloader and sprite_crop, then packages out/om_theme-selector.hmod +./build.sh compile # binaries only ``` -`build.sh` pulls the toolchain and `vendor/OptionsMenu` submodules on first run and builds everything inside a Docker container — no local ARM toolchain needed. +`build.sh` runs `make` inside the [classicmini-cross-toolchain](https://github.com/DefKorns/classicmini-cross-toolchain) Docker image, so no local ARM toolchain is needed. Versioning and packaging come from [hmod-build](https://github.com/DefKorns/hmod-build): the version is the latest `v*` git tag, and pushing a tag builds and publishes the release. + +The downloadable themes catalog lives in [om-theme-downloads](https://github.com/DefKorns/om-theme-downloads). ## Credits - [DefKorns](https://github.com/DefKorns) -- [DanTheMan827](https://github.com/DanTheMan827) (packed.png extractor) +- [DanTheMan827](https://github.com/DanTheMan827) — packed.png extractor +- Theme authors — credited on each theme in the download catalog ## Thanks diff --git a/mod/readme.md b/mod/readme.md index 7b4e367..7f6544f 100644 --- a/mod/readme.md +++ b/mod/readme.md @@ -2,75 +2,58 @@ **Requires [my Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases) as a base — not compatible with any other UI.** -[![Theme Selector](https://i.imgur.com/7JgP6JI.png)](https://i.imgur.com/7JgP6JI.png) +[![Theme Selector](https://i.imgur.com/7JgP6JI.png)](https://youtu.be/3UilWr1NvFA) ## What is it? -A graphical theme manager for NES/SNES/Famicom/Super Famicom Classic consoles, built on top of [my fork](https://github.com/DefKorns/OptionsMenu) of [CompCom's OptionsMenu](https://github.com/CompCom/OptionsMenu). Pick a theme from a preview grid, download more from a catalog, build your own from pieces of the themes you already have, and tune per-folder/randomizer/audio behavior — all from the console itself, no PC required after install. +A graphical theme manager for the NES, SNES, Famicom and Super Famicom Classic consoles. Pick a theme from a preview grid, download more from an online catalog, build your own from pieces of the themes you already have, and give each folder its own theme and music — all from the console itself. -The UI is my own standalone C++/SDL app (`theme_manager`), not a set of Lua scripts layered on the stock menu. +Open the Options Menu (hold **L+R** on a SNES/Super Famicom, **B+Down** on a NES/Famicom) and go to **Themes**. + +Full guides: [github.com/DefKorns/om_theme-selector/wiki](https://github.com/DefKorns/om_theme-selector/wiki) ## Features -- Preview grid to browse and apply installed themes; the theme currently applied is outlined so you can spot it at a glance -- Download themes directly from the internet (**Wi-Fi mod required**) -- Build your own DIY theme from pieces of the themes you already have (background, sprites, colors, packed art, fonts...) -- Theme randomizer — a different theme each time you go Home (off by default) -- Audio randomizer on the Home folder, optionally extended to every folder (off by default) -- Theme and music per folder: give any folder (and Home) its own theme and its own menu music from one screen; subfolders without one inherit their parent folder's +- **Installed themes grid** — browse your themes by their preview and apply one; the active theme is outlined +- **Download themes** from an online catalog of 90+ themes, each credited to its author (**Wi-Fi mod required**) +- **DIY theme** — mix the UI, background, demo characters, pole and colors of the themes you already have, preview it live, then save it as a theme of your own +- **Theme and music per folder** — give any folder (and Home) its own theme and its own menu music from one screen; folders without one inherit their parent's +- **Theme randomizer** — a different theme every time you change folder (off by default) +- **Audio randomizer** on Home, optionally on every folder (off by default) - Custom fonts per theme -- Custom color palette for the Theme Manager UI itself (`theme.cfg`) -- System clean-up to remove files your console type doesn't need -- Reset just this mod's settings without a full uninstall - -## Set a theme or music per folder, what is that? - -If you have your games organized into system folders, you can give each one its own theme instead of one theme for the whole console. Turn on **Theme Per Folder** in Settings, then open **Assign Theme to Folder** right below it. Your folders are listed as a tree: press Left/Right on a folder to cycle through the themes, or A to pick one from a list. The preview shows where each folder's theme comes from. The same theme can be assigned to as many folders as you like, no copies or renaming needed. **Automatic**, the first entry when cycling and in the list, clears a folder's assignment. - -Naming a theme folder after the game folder (lower snake_case) still works for folders without an assignment. Eg: game folder `Nintendo - Nintendo Entertainment System` → theme folder `nintendo_-_nintendo_entertainment_system`. - -A folder on Automatic uses its parent folder's theme, all the way up; a top-level folder on Automatic uses the theme you applied for the whole console. A folder's own theme always wins over its parent's. - -**Music Per Folder** works the same way: turn it on and open **Assign Music to Folder** to give folders their own menu music from `music_menu` (on USB, else the console's own). It only shows up when `music_menu` has `.wav` files, and works with or without Theme Per Folder. Music assigned to a folder plays instead of the theme's own music; a folder on Automatic keeps today's behaviour (the theme's music, the Audio Randomizer, or the original music). +- Delete a theme from the grid by holding **B** +- **Clean Up** removes the theme files your console type doesn't use +- **Reset Settings** puts the mod back to its defaults without uninstalling it or deleting your themes -## What if I want a specific theme on my main menu? +## Theme and music per folder -With **Theme Per Folder** on, open **Assign Theme to Folder** and set a theme on **Home**, the first entry in the list. Naming a theme `default` also still works. +Turn on **Theme Per Folder** in **Settings**, then open **Assign Theme to Folder** right below it. Your folders are listed as a tree: press Left/Right on a folder to cycle through the themes, or A to pick one from a list. **Automatic** clears a folder's assignment, so it uses its parent folder's theme (a top-level folder uses the console's theme). The same theme can go on as many folders as you like. -## What do you mean by "build your own DIY theme"? +**Music Per Folder** works the same way with the `.wav` files in `music_menu`, with or without Theme Per Folder. -Exactly that — pick and mix backgrounds, sprites, colors and packed art from the themes already on your console/USB into a theme of your own, previewed live as you build it. - -## I own a Famicom/Shonen/Super Famicom, can I install this? - -Yes — it supports all Nintendo Classic consoles, all regions. +A theme named after a folder (lower snake_case, e.g. `nintendo_-_nintendo_entertainment_system`) or named `default` (for Home) still works too. ## Requirements -- [Hakchi CE](https://github.com/TeamShinkansen/hakchi2/releases/latest) -- [My Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases) — the base UI this mod plugs into -- [Hakchi Wi-Fi mod (WPA Supplicant)](https://hakchi.net/hakchi/hmods/wpa-supplicant.hmod) — only needed to download themes from the internet - -## How do I use it - -Works the same whether your games are on NAND or USB/SD: - -- Install the hmod -- Open Theme Options from the Options Menu, download or build a theme, and apply it - -On NAND, themes live at `/var/lib/hakchi/usr/share/themes/`. -On USB/SD, themes live at `/media/hakchi/themes/`. +- [Hakchi2 CE](https://github.com/TeamShinkansen/hakchi2/releases/latest) +- [My Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases) +- [Hakchi Wi-Fi mod (WPA Supplicant)](https://hakchi.net/hakchi/hmods/wpa-supplicant.hmod) — only to download themes from the console +- A USB/SD drive is recommended: themes are often 5–30 MB each -*`consoletype` is `nes`, `snes` or `shonen`.* +## Where things live -## Customizing the look +| | USB/SD | NAND | +| --- | --- | --- | +| Themes | `/media/hakchi/themes/` | `/var/lib/hakchi/rootfs/usr/share/themes/` | +| Menu music | `/media/hakchi/music_menu` | `/var/lib/hakchi/rootfs/usr/share/music_menu` | -The Theme Manager's own UI (not your game themes) reads its color palette from `/etc/options_menu/theme.cfg` at startup — edit `Key=R,G,B` lines there (background, borders, accent, badges, the active-theme border...) to restyle it, or delete a line to fall back to the default. +`` is `nes`, `snes` or `shonen`. With a USB/SD drive connected, themes always go there. ## Credits - [DefKorns](https://github.com/DefKorns) -- [DanTheMan827](https://github.com/DanTheMan827) (packed.png extractor) +- [DanTheMan827](https://github.com/DanTheMan827) — packed.png extractor +- Theme authors — credited on each theme in the download catalog ## Thanks From 054a424ed51a29ef4d3088303ea55cb46b5e5469 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 10:55:12 +0000 Subject: [PATCH 119/210] Hide USB_ONLY downloads without a USB/SD drive and update readme --- README.md | 16 +++++++++++++++- mod/readme.md | 6 ++++++ src/command_loader.cpp | 4 ++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 700cf4b..cbe34ef 100644 --- a/README.md +++ b/README.md @@ -42,10 +42,24 @@ Full guides are on the **[wiki](https://github.com/DefKorns/om_theme-selector/wi **From my Mod Hub (recommended):** in hakchi open **Manage repositories**, add `https://defkorns.github.io/hakchi-repo/`, then install **Options Menu** and **Options Menu - Theme Selector** from it. Updates show up there automatically. -**By hand:** download `om_theme-selector.hmod` from [Releases](https://github.com/DefKorns/om_theme-selector/releases) and install it with hakchi (**Modules → Install extra modules**). +**By hand:** download `om_theme-selector.hmod` from [Releases](https://github.com/DefKorns/om_theme-selector/releases), put it in hakchi's `user_mods` folder (or drag and drop it onto the hakchi window), then install it from hakchi (**Modules → Install extra modules**). Then open the Options Menu (hold **L+R** on a SNES/Super Famicom, **B+Down** on a NES/Famicom) and go to **Themes**. Step by step, with screenshots: [Installation](https://github.com/DefKorns/om_theme-selector/wiki/Installation) and [Usage](https://github.com/DefKorns/om_theme-selector/wiki/Usage). +## Getting themes + +**From the console** (needs the Wi-Fi mod): + +1. Go to **Themes → Download Themes** and select **Update Theme List**. +2. Pick a theme from the grid — each one shows its preview, size and author — and press **A**. Press **B** when the download log finishes. +3. Back in **Installed Themes**, select the new theme and press **A** to apply it. The console reboots with it. + +**All SNES Themes** / **All NES Themes** downloads the whole catalog at once. It's several hundred MB, so it only shows up when a USB/SD drive is connected. + +**By hand:** copy a theme folder, or a catalog package such as `SNES..tar.gz`, into the themes folder for your console (see [Where things live](#where-things-live)), then select **Refresh** in **Themes**. Packages are in the [om-theme-downloads release](https://github.com/DefKorns/om-theme-downloads/releases/tag/themes-v1); the prefix (`SNES.`, `NES.`, `SHONEN.`) must match your console. + +More: [Usage → Downloading themes](https://github.com/DefKorns/om_theme-selector/wiki/Usage#downloading-themes) and [Adding themes by hand](https://github.com/DefKorns/om_theme-selector/wiki/Usage#adding-themes-by-hand). + ## Where things live | | USB/SD | NAND | diff --git a/mod/readme.md b/mod/readme.md index 7f6544f..6eb25fd 100644 --- a/mod/readme.md +++ b/mod/readme.md @@ -25,6 +25,12 @@ Full guides: [github.com/DefKorns/om_theme-selector/wiki](https://github.com/Def - **Clean Up** removes the theme files your console type doesn't use - **Reset Settings** puts the mod back to its defaults without uninstalling it or deleting your themes +## Getting themes + +**From the console** (needs the Wi-Fi mod): go to **Themes → Download Themes**, select **Update Theme List**, then pick a theme and press **A**. Back in **Installed Themes**, select it and press **A** to apply it. + +**By hand:** copy a theme folder, or a catalog package such as `SNES..tar.gz` from [om-theme-downloads](https://github.com/DefKorns/om-theme-downloads/releases/tag/themes-v1), into your themes folder (see below), then select **Refresh** in **Themes**. + ## Theme and music per folder Turn on **Theme Per Folder** in **Settings**, then open **Assign Theme to Folder** right below it. Your folders are listed as a tree: press Left/Right on a folder to cycle through the themes, or A to pick one from a list. **Automatic** clears a folder's assignment, so it uses its parent folder's theme (a top-level folder uses the console's theme). The same theme can go on as many folders as you like. diff --git a/src/command_loader.cpp b/src/command_loader.cpp index 0afedd5..d320c3a 100644 --- a/src/command_loader.cpp +++ b/src/command_loader.cpp @@ -137,6 +137,8 @@ bool LoadCommands(const std::string & commandLocation, const std::string & scrip fileList.push_back(entry->d_name); fileList.sort(); + const bool usbReady = DirHandle(opendir("/media/hakchi/")) != nullptr; + std::ifstream in; std::string sftype; bool sftypeLoaded = false; @@ -150,6 +152,8 @@ bool LoadCommands(const std::string & commandLocation, const std::string & scrip // c0000_0000 leading sentinel only - a later empty COMMAND_STR is a reserved blank slot instead if(commands.empty() && c.command.empty()) continue; + if(c.usbOnly && !usbReady) + continue; SelectorKeys keys = ReadSelectorKeys(commandLocation + file); if(keys.nesOnly || keys.snesOnly) { From 090ce2ec6a6721074565f322ccf67ee606840acc Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 11:35:02 +0000 Subject: [PATCH 120/210] Mark binaries in .gitattributes so CI builds aren't tagged dirty --- .gitattributes | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitattributes b/.gitattributes index 533e396..29b79f9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,3 +3,6 @@ * text=auto eol=lf *.ttf binary +*.png binary +*.lua binary +*.wav binary From fcda646f7d29a556cc1471da7727c13eda5d17d5 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 11:50:57 +0000 Subject: [PATCH 121/210] Update DIY theme description in README files for clarity --- README.md | 2 +- mod/readme.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cbe34ef..252e032 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Full guides are on the **[wiki](https://github.com/DefKorns/om_theme-selector/wi - **Installed themes grid** — browse your themes by their preview and apply one; the active theme is outlined - **Download themes** from an online catalog of 90+ themes, each credited to its author (**Wi-Fi mod required**) -- **DIY theme** — mix the UI, background, demo characters, pole and colors of the themes you already have, preview it live, then save it as a theme of your own +- **DIY theme** — mix the UI, background and demo sprites (or background color, on a NES) of the themes you already have, preview it live, then save it as a theme of your own - **Theme and music per folder** — give any folder (and Home) its own theme and its own menu music from one screen; folders without one inherit their parent's - **Theme randomizer** — a different theme every time you change folder (off by default) - **Audio randomizer** on Home, optionally on every folder (off by default) diff --git a/mod/readme.md b/mod/readme.md index 6eb25fd..c32f6fc 100644 --- a/mod/readme.md +++ b/mod/readme.md @@ -16,7 +16,7 @@ Full guides: [github.com/DefKorns/om_theme-selector/wiki](https://github.com/Def - **Installed themes grid** — browse your themes by their preview and apply one; the active theme is outlined - **Download themes** from an online catalog of 90+ themes, each credited to its author (**Wi-Fi mod required**) -- **DIY theme** — mix the UI, background, demo characters, pole and colors of the themes you already have, preview it live, then save it as a theme of your own +- **DIY theme** — mix the UI, background and demo sprites (or background color, on a NES) of the themes you already have, preview it live, then save it as a theme of your own - **Theme and music per folder** — give any folder (and Home) its own theme and its own menu music from one screen; folders without one inherit their parent's - **Theme randomizer** — a different theme every time you change folder (off by default) - **Audio randomizer** on Home, optionally on every folder (off by default) From 760143d14c39cdd38dc9ba46868f1f7ffe6c8aa3 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 14:07:39 +0000 Subject: [PATCH 122/210] Download all themes over one connection and unpack while the next downloads Skip themes that are already installed, drop the HEAD probe before each download, write to .part files so an interrupted transfer is never unpacked at boot, and stop listing every extracted file on screen. --- mod/etc/options_menu/themes/scripts/om_vars | 53 ++++++-- src/theme_downloader.c | 139 ++++++++++++-------- 2 files changed, 126 insertions(+), 66 deletions(-) diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index dd75463..000b4ed 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -231,7 +231,7 @@ hasThemesToInstall() { [ "$has_theme" = 0 ] && return for f in "$theme_path/$system_name".*.tar.gz; do [ -f "$f" ] || continue - tar -xzvf "$f" -C "$theme_path" && rm -f "$f" + tar -xzf "$f" -C "$theme_path" && rm -f "$f" done } @@ -457,16 +457,8 @@ themeGenerator() { rc=$? failed="" if [ "$rc" = 0 ]; then - while IFS= read -r t; do - echo "$t" - echo "" - downloadTheme "$t" - case "$?" in - 0) ;; - 3) rc=3 && break ;; - *) failed="$failed $t" ;; - esac - done < "${sftype}-all" + downloadThemes "${sftype}-all" + rc=$? fi remove "${sftype}-all" else @@ -502,12 +494,47 @@ unpackedSizeKb() { } downloadTheme() { + theme_downloader theme "$sftype" "$1" "${system_name}.$1.tar.gz" || return + unpackTheme "$1" +} + +downloadThemes() { + pending="/tmp/${sftype}-pending" + progress="/tmp/${sftype}-progress" + while IFS= read -r t; do + [ -d "$t" ] || [ "$t" = "$sfregion" ] || echo "$t" + done < "$1" > "$pending" + remove "$progress" + mkfifo "$progress" + theme_downloader themes "$sftype" "$pending" > "$progress" & + dl_pid=$! + dl_all_rc=0 + while read -r t_rc t; do + echo "$t" + echo "" + if [ "$t_rc" = 0 ]; then + unpackTheme "$t" + t_rc=$? + fi + case "$t_rc" in + 0) ;; + 3) dl_all_rc=3 && break ;; + *) failed="$failed $t" ;; + esac + done < "$progress" + kill "$dl_pid" 2> /dev/null + wait "$dl_pid" + [ "$?" = 3 ] && dl_all_rc=3 + rm -f "$pending" "$progress" "$system_name".*.tar.gz "$system_name".*.tar.gz.part + return "$dl_all_rc" +} + +unpackTheme() { tarball="${system_name}.$1.tar.gz" - theme_downloader theme "$sftype" "$1" "$tarball" || return if [ "$(freeSpaceKb .)" -lt "$(unpackedSizeKb "$tarball")" ]; then dl_rc=3 gzip -t "$tarball" 2> /dev/null || dl_rc=1 - elif ! tar -xzvf "$tarball"; then + elif ! tar -xzf "$tarball"; then dl_rc=1 [ "$(freeSpaceKb .)" -lt 1024 ] && dl_rc=3 else diff --git a/src/theme_downloader.c b/src/theme_downloader.c index accb8e6..2da6e23 100644 --- a/src/theme_downloader.c +++ b/src/theme_downloader.c @@ -23,15 +23,20 @@ #include #include #include +#include extern const unsigned char _binary_cacert_pem_start[]; extern const unsigned char _binary_cacert_pem_end[]; -static const char *GITHUB_BASE = "https://github.com/DefKorns/om-theme-downloads/releases/download/themes-v1"; -static const char *CLASSICMODS_SCRIPTS = "http://classicmods.net/files/themes/themeselector-scripts/"; -static const char *CLASSICMODS_THEMES = "http://classicmods.net/files/themes/themeselector/"; -static const char *LIST_SUFFIX = "-list-v2.tar.gz"; -static const char *CA_PATH = "/tmp/.theme_downloader_ca.pem"; +static const char *const GITHUB_BASE = "https://github.com/DefKorns/om-theme-downloads/releases/download/themes-v1"; +static const char *const CLASSICMODS_SCRIPTS = "http://classicmods.net/files/themes/themeselector-scripts/"; +static const char *const CLASSICMODS_THEMES = "http://classicmods.net/files/themes/themeselector/"; +static const char *const LIST_SUFFIX = "-list-v2.tar.gz"; +static const char *const CA_PATH = "/tmp/.theme_downloader_ca.pem"; + +enum { CONNECT_TIMEOUT_S = 10, STALL_TIMEOUT_S = 30, MAX_REDIRECTS = 10 }; +enum { REMOVAL_POLL_US = 200000, TARBALLS_ON_DISK = 2 }; +enum exit_code { EXIT_OK = 0, EXIT_FAILED = 1, EXIT_USAGE = 2, EXIT_NO_SPACE = 3 }; static void to_upper(char *dst, const char *src, size_t dst_size) { size_t i = 0; @@ -54,112 +59,140 @@ static void ensure_ca_bundle(void) { fclose(f); } -static CURL *make_handle(const char *url, long connect_timeout, long total_timeout) { +static CURL *make_handle(void) { CURL *c = curl_easy_init(); if (!c) return NULL; - curl_easy_setopt(c, CURLOPT_URL, url); curl_easy_setopt(c, CURLOPT_CAINFO, CA_PATH); curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(c, CURLOPT_MAXREDIRS, 10L); + curl_easy_setopt(c, CURLOPT_MAXREDIRS, (long)MAX_REDIRECTS); curl_easy_setopt(c, CURLOPT_FAILONERROR, 1L); curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1L); curl_easy_setopt(c, CURLOPT_USERAGENT, "theme_downloader/1.0"); - if (connect_timeout > 0) curl_easy_setopt(c, CURLOPT_CONNECTTIMEOUT, connect_timeout); - if (total_timeout > 0) curl_easy_setopt(c, CURLOPT_TIMEOUT, total_timeout); + curl_easy_setopt(c, CURLOPT_CONNECTTIMEOUT, (long)CONNECT_TIMEOUT_S); + curl_easy_setopt(c, CURLOPT_LOW_SPEED_LIMIT, 1L); + curl_easy_setopt(c, CURLOPT_LOW_SPEED_TIME, (long)STALL_TIMEOUT_S); return c; } -static int url_reachable(const char *url) { - CURL *c = make_handle(url, 5L, 5L); - if (!c) return 0; - curl_easy_setopt(c, CURLOPT_NOBODY, 1L); - CURLcode res = curl_easy_perform(c); - curl_easy_cleanup(c); - return res == CURLE_OK; -} - static size_t write_to_file(void *ptr, size_t size, size_t nmemb, void *userdata) { return fwrite(ptr, size, nmemb, (FILE *)userdata); } enum fetch_result { FETCH_FAILED, FETCH_OK, FETCH_WRITE_ERROR }; -static enum fetch_result download_to(const char *url, const char *out_path) { - FILE *f = fopen(out_path, "wb"); +/* A .part file keeps an interrupted transfer away from the boot-time installer. */ +static enum fetch_result download_to(CURL *c, const char *url, const char *out_path) { + char part_path[512]; + snprintf(part_path, sizeof(part_path), "%s.part", out_path); + FILE *f = fopen(part_path, "wb"); if (!f) return FETCH_WRITE_ERROR; - CURL *c = make_handle(url, 10L, 0L); - if (!c) { - fclose(f); - remove(out_path); - return FETCH_FAILED; - } + curl_easy_setopt(c, CURLOPT_URL, url); curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, write_to_file); curl_easy_setopt(c, CURLOPT_WRITEDATA, f); CURLcode res = curl_easy_perform(c); - curl_easy_cleanup(c); int closed = fclose(f) == 0; - if (res == CURLE_OK && closed) return FETCH_OK; - remove(out_path); + if (res == CURLE_OK && closed && rename(part_path, out_path) == 0) return FETCH_OK; + remove(part_path); return (res == CURLE_WRITE_ERROR || !closed) ? FETCH_WRITE_ERROR : FETCH_FAILED; } -static enum fetch_result fetch_with_fallback(const char *gh_url, const char *fallback_url, const char *out_path) { - enum fetch_result r = FETCH_FAILED; - if (url_reachable(gh_url)) r = download_to(gh_url, out_path); - if (r == FETCH_FAILED && url_reachable(fallback_url)) r = download_to(fallback_url, out_path); +static enum fetch_result fetch_with_fallback(CURL *c, const char *gh_url, const char *fallback_url, const char *out_path) { + enum fetch_result r = download_to(c, gh_url, out_path); + if (r == FETCH_FAILED) r = download_to(c, fallback_url, out_path); return r; } +static enum fetch_result fetch_theme(CURL *c, const char *sftype, const char *theme_name, const char *out_path) { + char system_name[32]; + char gh_url[512]; + char fallback_url[512]; + to_upper(system_name, sftype, sizeof(system_name)); + snprintf(gh_url, sizeof(gh_url), "%s/%s.%s.tar.gz", GITHUB_BASE, system_name, theme_name); + snprintf(fallback_url, sizeof(fallback_url), "%s%s.%s.tar.gz", CLASSICMODS_THEMES, system_name, theme_name); + return fetch_with_fallback(c, gh_url, fallback_url, out_path); +} + +static enum exit_code result_code(enum fetch_result r) { + if (r == FETCH_WRITE_ERROR) return EXIT_NO_SPACE; + return r == FETCH_OK ? EXIT_OK : EXIT_FAILED; +} + +static void wait_until_removed(const char *path) { + while (path[0] && access(path, F_OK) == 0) usleep(REMOVAL_POLL_US); +} + +/* Reports " " per theme so the caller can unpack while the next one downloads. */ +static enum exit_code fetch_theme_list(CURL *c, const char *sftype, const char *list_path) { + FILE *list = fopen(list_path, "r"); + if (!list) return EXIT_FAILED; + char system_name[32]; + to_upper(system_name, sftype, sizeof(system_name)); + char name[256]; + char out_path[TARBALLS_ON_DISK][512] = {{0}}; + int slot = 0; + enum exit_code code = EXIT_OK; + while (fgets(name, sizeof(name), list)) { + name[strcspn(name, "\r\n")] = '\0'; + if (!name[0]) continue; + wait_until_removed(out_path[slot]); + snprintf(out_path[slot], sizeof(out_path[slot]), "%s.%s.tar.gz", system_name, name); + code = result_code(fetch_theme(c, sftype, name, out_path[slot])); + if (code != EXIT_OK) out_path[slot][0] = '\0'; + if (printf("%d %s\n", code, name) < 0 || fflush(stdout) != 0) break; + if (code == EXIT_NO_SPACE) break; + slot = (slot + 1) % TARBALLS_ON_DISK; + } + fclose(list); + return code == EXIT_NO_SPACE ? EXIT_NO_SPACE : EXIT_OK; +} + static void usage(const char *prog) { fprintf(stderr, "usage:\n" " %s catalog \n" " %s theme \n" + " %s themes \n" " %s all-manifest \n", - prog, prog, prog); + prog, prog, prog, prog); } int main(int argc, char **argv) { if (argc < 2) { usage(argv[0]); - return 2; + return EXIT_USAGE; } curl_global_init(CURL_GLOBAL_DEFAULT); ensure_ca_bundle(); + CURL *c = make_handle(); + if (!c) { + curl_global_cleanup(); + return EXIT_FAILED; + } char gh_url[512]; char fallback_url[512]; - enum fetch_result result = FETCH_FAILED; + enum exit_code code = EXIT_USAGE; if (strcmp(argv[1], "catalog") == 0 && argc == 4) { const char *sftype = argv[2]; - const char *out_path = argv[3]; snprintf(gh_url, sizeof(gh_url), "%s/%s%s", GITHUB_BASE, sftype, LIST_SUFFIX); snprintf(fallback_url, sizeof(fallback_url), "%s%s%s", CLASSICMODS_SCRIPTS, sftype, LIST_SUFFIX); - result = fetch_with_fallback(gh_url, fallback_url, out_path); + code = result_code(fetch_with_fallback(c, gh_url, fallback_url, argv[3])); } else if (strcmp(argv[1], "theme") == 0 && argc == 5) { - const char *sftype = argv[2]; - const char *theme_name = argv[3]; - const char *out_path = argv[4]; - char system_name[32]; - to_upper(system_name, sftype, sizeof(system_name)); - snprintf(gh_url, sizeof(gh_url), "%s/%s.%s.tar.gz", GITHUB_BASE, system_name, theme_name); - snprintf(fallback_url, sizeof(fallback_url), "%s%s.%s.tar.gz", CLASSICMODS_THEMES, system_name, theme_name); - result = fetch_with_fallback(gh_url, fallback_url, out_path); + code = result_code(fetch_theme(c, argv[2], argv[3], argv[4])); + } else if (strcmp(argv[1], "themes") == 0 && argc == 4) { + code = fetch_theme_list(c, argv[2], argv[3]); } else if (strcmp(argv[1], "all-manifest") == 0 && argc == 4) { const char *sftype = argv[2]; - const char *out_path = argv[3]; snprintf(gh_url, sizeof(gh_url), "%s/%s-all", GITHUB_BASE, sftype); snprintf(fallback_url, sizeof(fallback_url), "%s%s-all", CLASSICMODS_SCRIPTS, sftype); - result = fetch_with_fallback(gh_url, fallback_url, out_path); + code = result_code(fetch_with_fallback(c, gh_url, fallback_url, argv[3])); } else { usage(argv[0]); - curl_global_cleanup(); - return 2; } + curl_easy_cleanup(c); curl_global_cleanup(); - if (result == FETCH_WRITE_ERROR) return 3; - return result == FETCH_OK ? 0 : 1; + return code; } From a31ec74e59014b9155f7bbf374da5c4544efa349 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 14:31:18 +0000 Subject: [PATCH 123/210] Set the hmod category to its tab in the hub --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d0e0ffe..d3babb8 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ MOD_ID := om_theme-selector MOD_NAME := Options Menu - Theme Selector -MOD_CATEGORY := User Interface +MOD_CATEGORY := Options Menu - Addons MOD_EXCLUDE := exclude-file.txt MOD_DEPS := mod/etc/options_menu/lib/theme_manager mod/bin/theme_downloader mod/bin/sprite_crop From bd21fe2fdece258bd90163daf5b37f31f6ae5221 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 14:39:30 +0000 Subject: [PATCH 124/210] Say so when Download all finds every theme already installed --- mod/etc/options_menu/themes/lang/de-DE.lang | 1 + mod/etc/options_menu/themes/lang/en-US.lang | 1 + mod/etc/options_menu/themes/lang/es-ES.lang | 1 + mod/etc/options_menu/themes/lang/fr-FR.lang | 1 + mod/etc/options_menu/themes/lang/it-IT.lang | 1 + mod/etc/options_menu/themes/lang/ja-JP.lang | 1 + mod/etc/options_menu/themes/lang/nl-NL.lang | 1 + mod/etc/options_menu/themes/lang/pt-PT.lang | 1 + mod/etc/options_menu/themes/lang/ru-RU.lang | 1 + mod/etc/options_menu/themes/scripts/om_vars | 5 +++++ 10 files changed, 14 insertions(+) diff --git a/mod/etc/options_menu/themes/lang/de-DE.lang b/mod/etc/options_menu/themes/lang/de-DE.lang index dc7ad33..4a4d86e 100644 --- a/mod/etc/options_menu/themes/lang/de-DE.lang +++ b/mod/etc/options_menu/themes/lang/de-DE.lang @@ -69,6 +69,7 @@ CATEGORY_UI_MOD=Kategorie: Benutzeroberflächen-Mod CHECKING_THEME_FILES=Dateien des ausgewählten Themes werden geprüft. DOWNLOADING=Wird heruntergeladen... INSTALLATION_COMPLETE=Installation abgeschlossen. +ALL_THEMES_INSTALLED=Alle Themen sind bereits installiert, nichts herunterzuladen. NOT_ENOUGH_SPACE=Nicht genügend freier Speicher, gib Speicher frei und versuche es erneut. THEMES_FAILED=Diese Themen konnten nicht installiert werden: THEME_LIST_DOWNLOADING_LINE1=Themenliste wird heruntergeladen diff --git a/mod/etc/options_menu/themes/lang/en-US.lang b/mod/etc/options_menu/themes/lang/en-US.lang index cf7b28f..89c041f 100644 --- a/mod/etc/options_menu/themes/lang/en-US.lang +++ b/mod/etc/options_menu/themes/lang/en-US.lang @@ -69,6 +69,7 @@ CATEGORY_UI_MOD=Category: User Interface Mod CHECKING_THEME_FILES=Checking selected theme files. DOWNLOADING=Downloading... INSTALLATION_COMPLETE=Installation complete. +ALL_THEMES_INSTALLED=All themes are already installed, nothing to download. NOT_ENOUGH_SPACE=Not enough free space, free some space and try again. THEMES_FAILED=These themes failed to install: THEME_LIST_DOWNLOADING_LINE1=Downloading Theme List diff --git a/mod/etc/options_menu/themes/lang/es-ES.lang b/mod/etc/options_menu/themes/lang/es-ES.lang index de1d668..e1676bd 100644 --- a/mod/etc/options_menu/themes/lang/es-ES.lang +++ b/mod/etc/options_menu/themes/lang/es-ES.lang @@ -69,6 +69,7 @@ CATEGORY_UI_MOD=Categoría: Mod de interfaz CHECKING_THEME_FILES=Comprobando los archivos del tema seleccionado. DOWNLOADING=Descargando... INSTALLATION_COMPLETE=Instalación completada. +ALL_THEMES_INSTALLED=Todos los temas ya están instalados, no hay nada que descargar. NOT_ENOUGH_SPACE=No hay suficiente espacio libre, libera espacio e inténtalo de nuevo. THEMES_FAILED=Estos temas no se pudieron instalar: THEME_LIST_DOWNLOADING_LINE1=Descargando Lista de Temas diff --git a/mod/etc/options_menu/themes/lang/fr-FR.lang b/mod/etc/options_menu/themes/lang/fr-FR.lang index ce80d31..84be0bd 100644 --- a/mod/etc/options_menu/themes/lang/fr-FR.lang +++ b/mod/etc/options_menu/themes/lang/fr-FR.lang @@ -69,6 +69,7 @@ CATEGORY_UI_MOD=Catégorie : Mod d'interface CHECKING_THEME_FILES=Vérification des fichiers du thème sélectionné. DOWNLOADING=Téléchargement... INSTALLATION_COMPLETE=Installation terminée. +ALL_THEMES_INSTALLED=Tous les thèmes sont déjà installés, rien à télécharger. NOT_ENOUGH_SPACE=Espace libre insuffisant, libérez de l'espace et réessayez. THEMES_FAILED=Ces thèmes n'ont pas pu être installés : THEME_LIST_DOWNLOADING_LINE1=Téléchargement de la Liste des Thèmes diff --git a/mod/etc/options_menu/themes/lang/it-IT.lang b/mod/etc/options_menu/themes/lang/it-IT.lang index 469c1f3..b177843 100644 --- a/mod/etc/options_menu/themes/lang/it-IT.lang +++ b/mod/etc/options_menu/themes/lang/it-IT.lang @@ -69,6 +69,7 @@ CATEGORY_UI_MOD=Categoria: Mod interfaccia utente CHECKING_THEME_FILES=Controllo dei file del tema selezionato. DOWNLOADING=Download in corso... INSTALLATION_COMPLETE=Installazione completata. +ALL_THEMES_INSTALLED=Tutti i temi sono già installati, niente da scaricare. NOT_ENOUGH_SPACE=Spazio libero insufficiente, libera spazio e riprova. THEMES_FAILED=Questi temi non sono stati installati: THEME_LIST_DOWNLOADING_LINE1=Download Elenco Temi in Corso diff --git a/mod/etc/options_menu/themes/lang/ja-JP.lang b/mod/etc/options_menu/themes/lang/ja-JP.lang index bef3c26..945e4a7 100644 --- a/mod/etc/options_menu/themes/lang/ja-JP.lang +++ b/mod/etc/options_menu/themes/lang/ja-JP.lang @@ -69,6 +69,7 @@ CATEGORY_UI_MOD=カテゴリ: UI Mod CHECKING_THEME_FILES=えらんだThemeのFileをかくにんしています DOWNLOADING=downloadしています... INSTALLATION_COMPLETE=インストールかんりょう +ALL_THEMES_INSTALLED=すべてのThemeはインストールずみです. downloadするものはありません NOT_ENOUGH_SPACE=あきようりょうがたりません. ようりょうをあけて, もういちどおためしください. THEMES_FAILED=これらのThemeはインストールできませんでした: THEME_LIST_DOWNLOADING_LINE1=Themeいちらんを download しています diff --git a/mod/etc/options_menu/themes/lang/nl-NL.lang b/mod/etc/options_menu/themes/lang/nl-NL.lang index 7310aea..08cd39f 100644 --- a/mod/etc/options_menu/themes/lang/nl-NL.lang +++ b/mod/etc/options_menu/themes/lang/nl-NL.lang @@ -69,6 +69,7 @@ CATEGORY_UI_MOD=Categorie: Interface-mod CHECKING_THEME_FILES=Bestanden van het gekozen thema worden gecontroleerd. DOWNLOADING=Downloaden... INSTALLATION_COMPLETE=Installatie voltooid. +ALL_THEMES_INSTALLED=Alle thema's zijn al geïnstalleerd, er is niets te downloaden. NOT_ENOUGH_SPACE=Onvoldoende vrije ruimte, maak ruimte vrij en probeer het opnieuw. THEMES_FAILED=Deze thema's konden niet worden geïnstalleerd: THEME_LIST_DOWNLOADING_LINE1=Themalijst Wordt Gedownload diff --git a/mod/etc/options_menu/themes/lang/pt-PT.lang b/mod/etc/options_menu/themes/lang/pt-PT.lang index 2262685..87f33b4 100644 --- a/mod/etc/options_menu/themes/lang/pt-PT.lang +++ b/mod/etc/options_menu/themes/lang/pt-PT.lang @@ -69,6 +69,7 @@ CATEGORY_UI_MOD=Categoria: Mod de Interface CHECKING_THEME_FILES=A verificar os ficheiros do tema selecionado. DOWNLOADING=A descarregar... INSTALLATION_COMPLETE=Instalação concluída. +ALL_THEMES_INSTALLED=Todos os temas já estão instalados, não há nada para descarregar. NOT_ENOUGH_SPACE=Não há espaço livre suficiente, liberta algum espaço e tenta novamente. THEMES_FAILED=Estes temas não foram instalados: THEME_LIST_DOWNLOADING_LINE1=A Descarregar Lista de Temas diff --git a/mod/etc/options_menu/themes/lang/ru-RU.lang b/mod/etc/options_menu/themes/lang/ru-RU.lang index 52ab48b..85ca260 100644 --- a/mod/etc/options_menu/themes/lang/ru-RU.lang +++ b/mod/etc/options_menu/themes/lang/ru-RU.lang @@ -69,6 +69,7 @@ CATEGORY_UI_MOD=Категория: Мод интерфейса CHECKING_THEME_FILES=Проверка файлов выбранной темы. DOWNLOADING=Загрузка... INSTALLATION_COMPLETE=Установка завершена. +ALL_THEMES_INSTALLED=Все темы уже установлены, загружать нечего. NOT_ENOUGH_SPACE=Недостаточно свободного места, освободите место и повторите попытку. THEMES_FAILED=Эти темы не удалось установить: THEME_LIST_DOWNLOADING_LINE1=Загрузка Списка Тем diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 000b4ed..baa8d99 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -475,6 +475,7 @@ themeGenerator() { case "$rc" in 0) translate INSTALLATION_COMPLETE ;; 3) translate NOT_ENOUGH_SPACE ;; + 4) translate ALL_THEMES_INSTALLED ;; *) netChecker ;; esac if [ -n "$failed" ]; then @@ -504,6 +505,10 @@ downloadThemes() { while IFS= read -r t; do [ -d "$t" ] || [ "$t" = "$sfregion" ] || echo "$t" done < "$1" > "$pending" + if [ ! -s "$pending" ]; then + remove "$pending" + return 4 + fi remove "$progress" mkfifo "$progress" theme_downloader themes "$sftype" "$pending" > "$progress" & From b4d7bb59b2bc6406d422a689245eff9107ce2e5e Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 14:45:52 +0000 Subject: [PATCH 125/210] Size each strip chip to its label so long translations fit Trailing blank slots no longer take a chip's width, and when the strip overflows only the widest chips shrink. --- src/texture_utils.cpp | 5 ++++ src/texture_utils.h | 1 + src/theme_manager_app.cpp | 51 ++++++++++++++++++++++++++++++--------- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/texture_utils.cpp b/src/texture_utils.cpp index b99ff71..e1d5ba5 100644 --- a/src/texture_utils.cpp +++ b/src/texture_utils.cpp @@ -41,6 +41,11 @@ void FitCover(Texture & tex, int boxX, int boxY, int boxW, int boxH, int padding FitScaled(tex, boxX, boxY, boxW, boxH, padding, true); } +int TextWidth(const std::string & text, int glyphSize) +{ + return CanRenderWithTTF(text, glyphSize) ? MeasureTTFWidth(text, glyphSize) : Utf8Length(text) * glyphSize; +} + std::string TruncateToWidth(const std::string & text, int glyphSize, int available) { std::string label = text; diff --git a/src/texture_utils.h b/src/texture_utils.h index bec932a..c7f9b1f 100644 --- a/src/texture_utils.h +++ b/src/texture_utils.h @@ -16,6 +16,7 @@ void FitCentered(Texture & tex, int boxX, int boxY, int boxW, int boxH, int padding); // letterboxed void FitCover(Texture & tex, int boxX, int boxY, int boxW, int boxH, int padding); // cropped +int TextWidth(const std::string & text, int glyphSize); std::string TruncateToWidth(const std::string & text, int glyphSize, int available); #endif diff --git a/src/theme_manager_app.cpp b/src/theme_manager_app.cpp index f5c5216..3479ed6 100644 --- a/src/theme_manager_app.cpp +++ b/src/theme_manager_app.cpp @@ -268,8 +268,11 @@ int ThemeManagerApp::RunGridLayout() // an empty slot stays reserved-width rather than compacted out, so a // conditional button appearing/disappearing doesn't shift the rest + int stripEnd = themeStart_; + while(stripEnd > 0 && commands_[stripEnd-1].command.empty()) + --stripEnd; std::vector stripSlots; - for(int i = 0; i < themeStart_; ++i) + for(int i = 0; i < stripEnd; ++i) stripSlots.push_back(i); const int stripSlotCount = static_cast(stripSlots.size()); std::vector stripIndices; @@ -287,15 +290,41 @@ int ThemeManagerApp::RunGridLayout() }; const int chipGap = 14; const int chipIconSize = 22; + const int chipIconGap = 12; + const int chipPadX = 20; + const int chipLabelSize = 14; + const int MinChipW = 140; const bool hasStrip = stripSlotCount > 0; const int StripTop = UiTheme::HeaderDividerY + 22; const int StripH = hasStrip ? 58 : 0; const int GridLeft = UiTheme::FrameX + 32; const int GridRight = UiTheme::FrameX + UiTheme::FrameW - 32; - // capped so a strip with few slots doesn't stretch its chips wide - const int MaxChipW = 200; - const int chipW = hasStrip ? std::min(MaxChipW, (GridRight - GridLeft - (stripSlotCount-1)*chipGap) / stripSlotCount) : 0; + auto ChipIconW = [&](int idx) { return commands_[idx].previewImage.empty() ? 0 : chipIconSize + chipIconGap; }; + std::vector chipWidths; + for(int idx : stripSlots) + { + const Command & c = commands_[idx]; + const int contentW = c.command.empty() ? 0 : ChipIconW(idx) + TextWidth(Translate(c.name), chipLabelSize); + chipWidths.push_back(std::max(MinChipW, contentW + 2*chipPadX)); + } + // the widest chips shrink first, down to a shared cap, when the strip overflows + const int stripAvail = GridRight - GridLeft - std::max(0, stripSlotCount-1)*chipGap; + auto StripWidthAt = [&](int cap) + { + int total = 0; + for(int w : chipWidths) + total += std::min(w, cap); + return total; + }; + int chipCap = chipWidths.empty() ? 0 : *std::max_element(chipWidths.begin(), chipWidths.end()); + while(chipCap > MinChipW && StripWidthAt(chipCap) > stripAvail) + --chipCap; + for(int & w : chipWidths) + w = std::min(w, chipCap); + std::vector chipWidthOf(commands_.size(), 0); + for(int pos = 0; pos < stripSlotCount; ++pos) + chipWidthOf[stripSlots[pos]] = chipWidths[pos]; titleText_ = Texture(Translate(options_.titleKey), UiTheme::SectionTitleFontSize - 6, renderer_, UiTheme::SectionTitleX, 0, false, ToAbgr(UiTheme::Text), true); titleText_.rect.y = hasStrip ? (StripTop + StripH + 22) : (UiTheme::HeaderDividerY + 22); const int GridTop = titleText_.rect.y + titleText_.rect.h + 20; @@ -341,7 +370,7 @@ int ThemeManagerApp::RunGridLayout() std::string label = Translate(c.name); if(i < themeStart_ || i >= pinnedStartIndex_) { - int labelAvail = chipW - chipIconSize - 24; + const int labelAvail = chipWidthOf[i] - 2*chipPadX - ChipIconW(i); if(c.previewImage.size()) { SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); // smooth - these are small glyphs, nearest looks jagged @@ -350,10 +379,9 @@ int ThemeManagerApp::RunGridLayout() FitCentered(icon, 0, 0, chipIconSize, chipIconSize, 0); SetColorMod(icon.texture.get(), UiTheme::Accent); chipIcons[i] = icon; - labelAvail = chipW - chipIconSize - 32; } - label = TruncateToWidth(label, 14, labelAvail); - chipLabels[i] = Texture(label, 14, renderer_, 0, 0, false, ToAbgr(UiTheme::Text), true); + label = TruncateToWidth(label, chipLabelSize, labelAvail); + chipLabels[i] = Texture(label, chipLabelSize, renderer_, 0, 0, false, ToAbgr(UiTheme::Text), true); } else { @@ -537,7 +565,8 @@ int ThemeManagerApp::RunGridLayout() int x = GridLeft; for(int pos = 0; pos < stripSlotCount; ++pos) { - int idx = stripSlots[pos]; + const int idx = stripSlots[pos]; + const int chipW = chipWidths[pos]; if(commands_[idx].command.empty()) { x += chipW + chipGap; @@ -557,14 +586,14 @@ int ThemeManagerApp::RunGridLayout() Texture & icon = chipIcons[idx]; Texture & label = chipLabels[idx]; int iconW = icon.rect.h > 0 ? chipIconSize : 0; - int groupW = iconW + (iconW > 0 ? 12 : 0) + label.rect.w; + int groupW = iconW + (iconW > 0 ? chipIconGap : 0) + label.rect.w; int gx = x + (chipW - groupW) / 2; if(icon.rect.h > 0) { icon.rect.x = gx + (chipIconSize - icon.rect.w) / 2; icon.rect.y = StripTop + (StripH - icon.rect.h) / 2; icon.Draw(renderer_); - gx += iconW + 12; + gx += iconW + chipIconGap; } label.rect.x = gx; label.rect.y = StripTop + (StripH - label.rect.h) / 2; From c34aca127000fa4f9b32b4f258fba96dac98cec7 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 14:57:33 +0000 Subject: [PATCH 126/210] Clean up a theme download aborted with B, show Loading on return --- .../options_menu/themes/downloads/scripts/om_return | 5 ++++- mod/etc/options_menu/themes/scripts/om_vars | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_return b/mod/etc/options_menu/themes/downloads/scripts/om_return index e925840..7fc21e5 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_return +++ b/mod/etc/options_menu/themes/downloads/scripts/om_return @@ -23,7 +23,10 @@ remove "$om_dummy" if [ -n "$(ls -A $theme_path)" ]; then touch "$om_dummy" - themeCountChanged && themeLoader + if themeCountChanged; then + "$om_scripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png > /dev/fb0 + themeLoader + fi else remove "$om_mod_spacer" clearList diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index baa8d99..1ac4f40 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -450,6 +450,7 @@ themeGenerator() { full_title="$(grep -F 'NAME=' "${om_mod_sub_cmds}/c0010_${theme_name}" | sed -e 's/.*=\(.*\)\[.*/\1/')" full_path="${theme_path}/${theme_name}" cd "${theme_path}" || exit + trap abortDownload TERM INT HUP translate DOWNLOADING if [ "${theme_name}" = "all" ]; then @@ -534,8 +535,17 @@ downloadThemes() { return "$dl_all_rc" } +abortDownload() { + trap - TERM INT HUP + [ -n "$dl_pid" ] && kill "$dl_pid" 2> /dev/null + [ -n "$unpacking" ] && rm -rf "$unpacking" "$unpacking.png" + rm -f "$system_name".*.tar.gz "$system_name".*.tar.gz.part "/tmp/${sftype}-pending" "/tmp/${sftype}-progress" "${sftype}-all" + exit 1 +} + unpackTheme() { tarball="${system_name}.$1.tar.gz" + unpacking="$1" if [ "$(freeSpaceKb .)" -lt "$(unpackedSizeKb "$tarball")" ]; then dl_rc=3 gzip -t "$tarball" 2> /dev/null || dl_rc=1 @@ -544,9 +554,11 @@ unpackTheme() { [ "$(freeSpaceKb .)" -lt 1024 ] && dl_rc=3 else remove "$tarball" + unpacking="" return 0 fi rm -rf "$tarball" "$1" "$1.png" + unpacking="" return "$dl_rc" } From f54e9d04072714c375a71a0e3131126a2794dd6b Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 15:01:24 +0000 Subject: [PATCH 127/210] Update OptionsMenu submodule to 3b9b9f5 (abort INTERNAL commands with B) --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index fbdc5be..3b9b9f5 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit fbdc5be95c07d7885fb60c71560b26d27c86425a +Subproject commit 3b9b9f56717b595a69e421fe44b0c4ad88ebd5e1 From 21baeeb4c1e875bd22796e558be89f86d17b8eba Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 15:34:10 +0000 Subject: [PATCH 128/210] Flag an unsaved DIY draft and the chosen DIY asset A command can name an ATTENTION_PATH: while it holds files, its chip gets an accent dot and its ATTENTION_NOTICE shows beside the title. Picking a DIY asset records it under /tmp/diy_selection and returns straight to its grid, which outlines that tile in green. The Makefile now tracks header dependencies, so a header change rebuilds every object that includes it. --- .gitignore | 1 + Makefile | 8 ++- .../themes/commands/c0000_3_generatetheme | 4 +- .../themes/creator/scripts/om_applytheme | 2 +- .../themes/creator/scripts/om_diy_asset | 2 +- mod/etc/options_menu/themes/lang/de-DE.lang | 4 +- mod/etc/options_menu/themes/lang/en-US.lang | 4 +- mod/etc/options_menu/themes/lang/es-ES.lang | 4 +- mod/etc/options_menu/themes/lang/fr-FR.lang | 4 +- mod/etc/options_menu/themes/lang/it-IT.lang | 4 +- mod/etc/options_menu/themes/lang/ja-JP.lang | 4 +- mod/etc/options_menu/themes/lang/nl-NL.lang | 4 +- mod/etc/options_menu/themes/lang/pt-PT.lang | 4 +- mod/etc/options_menu/themes/lang/ru-RU.lang | 4 +- mod/etc/options_menu/themes/scripts/om_vars | 23 ++----- src/command_loader.cpp | 31 ++++++++- src/command_loader.h | 3 + src/theme_downloader.c | 2 - src/theme_manager_app.cpp | 65 +++++++++++++------ 19 files changed, 103 insertions(+), 74 deletions(-) diff --git a/.gitignore b/.gitignore index 08567c6..00ce1b4 100644 --- a/.gitignore +++ b/.gitignore @@ -557,3 +557,4 @@ tools/MaterialSymbolsOutlined.ttf src/cacert.pem mod/etc/options_menu/lib/ vendor/chmenu +.deps/ diff --git a/Makefile b/Makefile index d3babb8..aed0706 100644 --- a/Makefile +++ b/Makefile @@ -40,8 +40,13 @@ mod/etc/options_menu/lib/theme_manager: $(OBJECTS) $(CROSS_PREFIX)$(CXX) $(OBJECTS) $(LDLIBS) $(LDFLAGS) -Wl,-rpath,/etc/options_menu/lib -o $@ $(CROSS_PREFIX)$(STRIP) $@ +DEPDIR = .deps + %.o: %.cpp - $(CROSS_PREFIX)$(CXX) $(CXXFLAGS) -c $< -o $@ + @mkdir -p $(DEPDIR) + $(CROSS_PREFIX)$(CXX) $(CXXFLAGS) -MMD -MP -MF $(DEPDIR)/$(subst /,_,$*).d -c $< -o $@ + +-include $(wildcard $(DEPDIR)/*.d) %.o: %.c $(CROSS_PREFIX)$(CC) $(CFLAGS) -c $< -o $@ @@ -72,6 +77,7 @@ update-headers: clean: find . -name "*.o" -type f -not -path "./toolchain/*" -delete + rm -rf $(DEPDIR) rm -f $(MOD_DEPS) rm -rf out/ diff --git a/mod/etc/options_menu/themes/commands/c0000_3_generatetheme b/mod/etc/options_menu/themes/commands/c0000_3_generatetheme index b3233eb..c2a10e2 100644 --- a/mod/etc/options_menu/themes/commands/c0000_3_generatetheme +++ b/mod/etc/options_menu/themes/commands/c0000_3_generatetheme @@ -2,4 +2,6 @@ COMMAND_NAME=DIY_THEME COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_themeCreator -PREVIEW_IMAGE=/etc/options_menu/images/chip_diy.png \ No newline at end of file +PREVIEW_IMAGE=/etc/options_menu/images/chip_diy.png +ATTENTION_PATH=/tmp/diy_theme +ATTENTION_NOTICE=DIY_DRAFT_NOTICE \ No newline at end of file diff --git a/mod/etc/options_menu/themes/creator/scripts/om_applytheme b/mod/etc/options_menu/themes/creator/scripts/om_applytheme index 13592e6..f241e76 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_applytheme +++ b/mod/etc/options_menu/themes/creator/scripts/om_applytheme @@ -32,7 +32,7 @@ if [ -d "${theme_path}/${name}" ]; then fi cp -R "${diy_theme_path}" "${theme_path}/${name}" -remove "${diy_theme_path}" +remove "${diy_theme_path}" "$diy_selection_path" clearList disableCmds "clear" "preview" "diy" diff --git a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset index dd8cb7f..9c0b534 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_diy_asset +++ b/mod/etc/options_menu/themes/creator/scripts/om_diy_asset @@ -72,4 +72,4 @@ if [ "$2" = "force" ] || ! find "$diy_om_sprite_path/commands" -maxdepth 1 -name findDiyAsset "$1" fi -$options_menu/lib/theme_manager --commandPath $diy_om_sprite_path/commands/ --scriptPath $diy_om_sprite_path/scripts --title $diy_title & +$options_menu/lib/theme_manager --commandPath $diy_om_sprite_path/commands/ --scriptPath $diy_om_sprite_path/scripts --title $diy_title --selectedMarker "$diy_selection_path/$1" & diff --git a/mod/etc/options_menu/themes/lang/de-DE.lang b/mod/etc/options_menu/themes/lang/de-DE.lang index 4a4d86e..f0cb0c7 100644 --- a/mod/etc/options_menu/themes/lang/de-DE.lang +++ b/mod/etc/options_menu/themes/lang/de-DE.lang @@ -62,9 +62,7 @@ AUDIO_RANDOMIZER_HOME=Zufalls-Audio: Home AUDIO_RANDOMIZER_ALL_FOLDERS=Alle Ordner (nur Themes ohne JEGLICHE Audiodateien) CLEAN_UP=Aufräumen RESET_SETTINGS=Einstellungen zurücksetzen -ASSET_APPLIED_SUCCESS=Asset erfolgreich angewendet! -SAVE_PREVIEW_HINT_LINE1=Vergiss nicht, nach der Auswahl deiner Assets 'Eigenes Theme speichern' auszuwählen, oder wähle einfach -SAVE_PREVIEW_HINT_LINE2='Eigenes Theme in der Vorschau ansehen', um eine Vorschau zu sehen, bevor du dein Theme speicherst. +DIY_DRAFT_NOTICE=Eigenes Theme nicht gespeichert: öffne Eigenes Theme zum Speichern, sonst geht es beim Neustart verloren. CATEGORY_UI_MOD=Kategorie: Benutzeroberflächen-Mod CHECKING_THEME_FILES=Dateien des ausgewählten Themes werden geprüft. DOWNLOADING=Wird heruntergeladen... diff --git a/mod/etc/options_menu/themes/lang/en-US.lang b/mod/etc/options_menu/themes/lang/en-US.lang index 89c041f..4cea61e 100644 --- a/mod/etc/options_menu/themes/lang/en-US.lang +++ b/mod/etc/options_menu/themes/lang/en-US.lang @@ -62,9 +62,7 @@ AUDIO_RANDOMIZER_HOME=Audio Randomizer: Home AUDIO_RANDOMIZER_ALL_FOLDERS=All Folders (Only Themes Without ANY Audio Files) CLEAN_UP=Clean Up RESET_SETTINGS=Reset Settings -ASSET_APPLIED_SUCCESS=Asset applied successfully! -SAVE_PREVIEW_HINT_LINE1=Don't forget to select 'Save custom theme' when you finish selecting your assets, or simply select -SAVE_PREVIEW_HINT_LINE2='Preview custom theme', to have a preview before saving your theme. +DIY_DRAFT_NOTICE=Unsaved custom theme: open DIY Theme to save it, or it's discarded on reboot. CATEGORY_UI_MOD=Category: User Interface Mod CHECKING_THEME_FILES=Checking selected theme files. DOWNLOADING=Downloading... diff --git a/mod/etc/options_menu/themes/lang/es-ES.lang b/mod/etc/options_menu/themes/lang/es-ES.lang index e1676bd..75c2130 100644 --- a/mod/etc/options_menu/themes/lang/es-ES.lang +++ b/mod/etc/options_menu/themes/lang/es-ES.lang @@ -62,9 +62,7 @@ AUDIO_RANDOMIZER_HOME=Audio aleatorio: Inicio AUDIO_RANDOMIZER_ALL_FOLDERS=Todas las carpetas (solo temas sin NINGÚN archivo de audio) CLEAN_UP=Limpieza RESET_SETTINGS=Restablecer ajustes -ASSET_APPLIED_SUCCESS=¡Recurso aplicado correctamente! -SAVE_PREVIEW_HINT_LINE1=No olvides seleccionar 'Guardar tema personalizado' cuando termines de elegir tus recursos, o simplemente selecciona -SAVE_PREVIEW_HINT_LINE2='Vista previa del tema personalizado', para ver una vista previa antes de guardar tu tema. +DIY_DRAFT_NOTICE=Tema personalizado sin guardar: abre Tema personalizado para guardarlo o se perderá al reiniciar. CATEGORY_UI_MOD=Categoría: Mod de interfaz CHECKING_THEME_FILES=Comprobando los archivos del tema seleccionado. DOWNLOADING=Descargando... diff --git a/mod/etc/options_menu/themes/lang/fr-FR.lang b/mod/etc/options_menu/themes/lang/fr-FR.lang index 84be0bd..2fac17d 100644 --- a/mod/etc/options_menu/themes/lang/fr-FR.lang +++ b/mod/etc/options_menu/themes/lang/fr-FR.lang @@ -62,9 +62,7 @@ AUDIO_RANDOMIZER_HOME=Audio aléatoire : Accueil AUDIO_RANDOMIZER_ALL_FOLDERS=Tous les dossiers (thèmes sans AUCUN fichier audio) CLEAN_UP=Nettoyage RESET_SETTINGS=Réinitialiser les réglages -ASSET_APPLIED_SUCCESS=Ressource appliquée avec succès ! -SAVE_PREVIEW_HINT_LINE1=N'oublie pas de sélectionner « Enregistrer le thème personnalisé » quand tu as fini de choisir tes ressources, ou sélectionne simplement -SAVE_PREVIEW_HINT_LINE2=« Aperçu du thème personnalisé », pour voir un aperçu avant d'enregistrer ton thème. +DIY_DRAFT_NOTICE=Thème personnalisé non enregistré : ouvrez Thème personnalisé pour le garder, sinon il sera perdu au redémarrage. CATEGORY_UI_MOD=Catégorie : Mod d'interface CHECKING_THEME_FILES=Vérification des fichiers du thème sélectionné. DOWNLOADING=Téléchargement... diff --git a/mod/etc/options_menu/themes/lang/it-IT.lang b/mod/etc/options_menu/themes/lang/it-IT.lang index b177843..f82222e 100644 --- a/mod/etc/options_menu/themes/lang/it-IT.lang +++ b/mod/etc/options_menu/themes/lang/it-IT.lang @@ -62,9 +62,7 @@ AUDIO_RANDOMIZER_HOME=Audio casuale: Home AUDIO_RANDOMIZER_ALL_FOLDERS=Tutte le cartelle (solo temi senza ALCUN file audio) CLEAN_UP=Pulizia RESET_SETTINGS=Ripristina impostazioni -ASSET_APPLIED_SUCCESS=Risorsa applicata con successo! -SAVE_PREVIEW_HINT_LINE1=Non dimenticare di selezionare 'Salva tema personalizzato' quando hai finito di scegliere le risorse, oppure seleziona semplicemente -SAVE_PREVIEW_HINT_LINE2='Anteprima tema personalizzato', per vedere un'anteprima prima di salvare il tema. +DIY_DRAFT_NOTICE=Tema personalizzato non salvato: apri Tema personalizzato per salvarlo, o andrà perso al riavvio. CATEGORY_UI_MOD=Categoria: Mod interfaccia utente CHECKING_THEME_FILES=Controllo dei file del tema selezionato. DOWNLOADING=Download in corso... diff --git a/mod/etc/options_menu/themes/lang/ja-JP.lang b/mod/etc/options_menu/themes/lang/ja-JP.lang index 945e4a7..cc837a0 100644 --- a/mod/etc/options_menu/themes/lang/ja-JP.lang +++ b/mod/etc/options_menu/themes/lang/ja-JP.lang @@ -62,9 +62,7 @@ AUDIO_RANDOMIZER_HOME=Audioをrandomに: Home AUDIO_RANDOMIZER_ALL_FOLDERS=すべてのFolder (Audioファイルが1つもないThemeのみ) CLEAN_UP=せいり RESET_SETTINGS=せっていをreset -ASSET_APPLIED_SUCCESS=Assetをてきようしました! -SAVE_PREVIEW_HINT_LINE1=Assetをえらびおわったら「じぶんのThemeをほぞん」をわすれずに、または -SAVE_PREVIEW_HINT_LINE2=「じぶんのThemeをPreview」をえらぶと、ほぞんまえにかくにんできます。 +DIY_DRAFT_NOTICE=じぶんのThemeがほぞんされていません. さいきどうするときえます CATEGORY_UI_MOD=カテゴリ: UI Mod CHECKING_THEME_FILES=えらんだThemeのFileをかくにんしています DOWNLOADING=downloadしています... diff --git a/mod/etc/options_menu/themes/lang/nl-NL.lang b/mod/etc/options_menu/themes/lang/nl-NL.lang index 08cd39f..07e314c 100644 --- a/mod/etc/options_menu/themes/lang/nl-NL.lang +++ b/mod/etc/options_menu/themes/lang/nl-NL.lang @@ -62,9 +62,7 @@ AUDIO_RANDOMIZER_HOME=Willekeurige audio: Start AUDIO_RANDOMIZER_ALL_FOLDERS=Alle mappen (alleen thema's zonder ENIG audiobestand) CLEAN_UP=Opschonen RESET_SETTINGS=Instellingen herstellen -ASSET_APPLIED_SUCCESS=Asset succesvol toegepast! -SAVE_PREVIEW_HINT_LINE1=Vergeet niet 'Eigen thema opslaan' te kiezen als je klaar bent met het kiezen van je assets, of kies gewoon -SAVE_PREVIEW_HINT_LINE2='Voorbeeld van eigen thema', om een voorbeeld te zien voordat je je thema opslaat. +DIY_DRAFT_NOTICE=Eigen thema niet opgeslagen: open Eigen thema om het op te slaan, anders gaat het verloren bij herstarten. CATEGORY_UI_MOD=Categorie: Interface-mod CHECKING_THEME_FILES=Bestanden van het gekozen thema worden gecontroleerd. DOWNLOADING=Downloaden... diff --git a/mod/etc/options_menu/themes/lang/pt-PT.lang b/mod/etc/options_menu/themes/lang/pt-PT.lang index 87f33b4..b400557 100644 --- a/mod/etc/options_menu/themes/lang/pt-PT.lang +++ b/mod/etc/options_menu/themes/lang/pt-PT.lang @@ -62,9 +62,7 @@ AUDIO_RANDOMIZER_HOME=Áudio Aleatório: Home AUDIO_RANDOMIZER_ALL_FOLDERS=Todas as Pastas (Só Temas Sem Ficheiros de Áudio) CLEAN_UP=Limpeza RESET_SETTINGS=Repor Definições -ASSET_APPLIED_SUCCESS=Recurso aplicado com sucesso! -SAVE_PREVIEW_HINT_LINE1=Não te esqueças de selecionar 'Guardar tema personalizado' quando terminares, ou seleciona -SAVE_PREVIEW_HINT_LINE2='Pré-visualizar tema personalizado', para ver uma pré-visualização antes de guardar. +DIY_DRAFT_NOTICE=Tema personalizado por gravar: abre Tema Personalizado para o guardar, ou perde-se ao reiniciar. CATEGORY_UI_MOD=Categoria: Mod de Interface CHECKING_THEME_FILES=A verificar os ficheiros do tema selecionado. DOWNLOADING=A descarregar... diff --git a/mod/etc/options_menu/themes/lang/ru-RU.lang b/mod/etc/options_menu/themes/lang/ru-RU.lang index 85ca260..a8bcc9f 100644 --- a/mod/etc/options_menu/themes/lang/ru-RU.lang +++ b/mod/etc/options_menu/themes/lang/ru-RU.lang @@ -62,9 +62,7 @@ AUDIO_RANDOMIZER_HOME=Случайное аудио: Главное меню AUDIO_RANDOMIZER_ALL_FOLDERS=Все папки (только темы БЕЗ аудиофайлов) CLEAN_UP=Очистка RESET_SETTINGS=Сбросить настройки -ASSET_APPLIED_SUCCESS=Ресурс успешно применён! -SAVE_PREVIEW_HINT_LINE1=Не забудьте выбрать «Сохранить свою тему», когда закончите выбирать ресурсы, или просто выберите -SAVE_PREVIEW_HINT_LINE2=«Просмотр своей темы», чтобы увидеть предпросмотр перед сохранением темы. +DIY_DRAFT_NOTICE=Своя тема не сохранена: откройте «Своя тема», чтобы сохранить её, иначе она пропадёт после перезагрузки. CATEGORY_UI_MOD=Категория: Мод интерфейса CHECKING_THEME_FILES=Проверка файлов выбранной темы. DOWNLOADING=Загрузка... diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 1ac4f40..40e78ef 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -70,6 +70,7 @@ om_mod_settings_scripts="$om_mod_settings/scripts" om_mod_license="$om_mod_scripts/om_license" diy_theme_path="$mountpoint/tmp/diy_theme" diy_resources="$diy_theme_path/resources" +diy_selection_path="/tmp/diy_selection" diy_layout="$diy_resources/layout/asset/sprite" om_mod_license="$om_mod_scripts/om_license" thm_preview="$om_mod_sub/images" @@ -751,28 +752,13 @@ diySpriteSelector() { [ ! -d "$diy_asset" ] && mkdir -p "$diy_asset" find "$diy_selected_asset" -name "$3" -exec cp {} "$diy_layout" \; + sel_category="$(basename "$(dirname "$(dirname "$0")")")" + mkdir -p "$diy_selection_path" + basename "$0" .sh > "$diy_selection_path/$sel_category" if [ ! -f "$b_file" ]; then sed '1,2d' "$om_theme_script" > "$b_file" echo themeSelector > "$p_file" fi - echo "" - echo " ____ _____ __ _____ _ _ _____ __ __ _____ ____ ____ _____ _ _____ ___ ____ " - echo " | _ \\_ _\\ \\ / / |_ _| | | | ____| \\/ | ____| / ___| _ \\| ____| / \\|_ _/ _ \\| _ \\ " - echo " | | | | | \\ V / | | | |_| | _| | |\\/| | _| | | | |_) | _| / _ \\ | || | | | |_) |" - echo " | |_| | | | | | | | _ | |___| | | | |___ | |___| _ <| |___ / ___ \\| || |_| | _ < " - echo " |____/___| |_| |_| |_| |_|_____|_| |_|_____| \\____|_| \\_\\_____/_/ \\_\\_| \\___/|_| \\_\\" - echo " " - echo " by: DefKorns " - echo "" - echo "" - echo "" - translate ASSET_APPLIED_SUCCESS - echo "" - echo "" - translate SAVE_PREVIEW_HINT_LINE1 - translate SAVE_PREVIEW_HINT_LINE2 - echo "" - echo "" } diyThemeChecker() { @@ -791,6 +777,7 @@ diyThemeChecker() { diyThemeRemover() { disableCmds "diy" "del_diy" "preview" + remove "$diy_selection_path" [ -d "$diy_theme_path" ] && find "$diy_theme_path" -type f -not -name 'assets_here' -exec rm {} + diff --git a/src/command_loader.cpp b/src/command_loader.cpp index d320c3a..52477ba 100644 --- a/src/command_loader.cpp +++ b/src/command_loader.cpp @@ -34,11 +34,13 @@ struct PipeCloser { void operator()(FILE * pipe) const { if(pipe) pclose(pipe); using PipeHandle = std::unique_ptr; // my own convention, not OptionsMenu's Command format -struct SelectorKeys { bool nesOnly = false; bool snesOnly = false; std::string author; }; +struct SelectorKeys { bool nesOnly = false; bool snesOnly = false; std::string author; std::string attentionPath; std::string attentionNotice; }; SelectorKeys ReadSelectorKeys(const std::string & path) { const std::string authorKey = "AUTHOR="; + const std::string attentionPathKey = "ATTENTION_PATH="; + const std::string attentionNoticeKey = "ATTENTION_NOTICE="; SelectorKeys keys; std::ifstream in(path); std::string line; @@ -52,10 +54,30 @@ SelectorKeys ReadSelectorKeys(const std::string & path) keys.snesOnly = true; else if(line.compare(0, authorKey.size(), authorKey) == 0) keys.author = line.substr(authorKey.size()); + else if(line.compare(0, attentionPathKey.size(), attentionPathKey) == 0) + keys.attentionPath = line.substr(attentionPathKey.size()); + else if(line.compare(0, attentionNoticeKey.size(), attentionNoticeKey) == 0) + keys.attentionNotice = line.substr(attentionNoticeKey.size()); } return keys; } +bool HasAnyFile(const std::string & dirPath) +{ + DirHandle dir(opendir(dirPath.c_str())); + if(!dir) + return false; + while(auto entry = readdir(dir.get())) + { + const std::string name = entry->d_name; + if(name == "." || name == "..") + continue; + if(entry->d_type == DT_REG || (entry->d_type == DT_DIR && HasAnyFile(dirPath + "/" + name))) + return true; + } + return false; +} + } // child screens inherit this via fork+exec, same as OM_BACK_STACK - so only the first screen @@ -107,6 +129,8 @@ AppOptions ParseArgs(int argc, char * argv[], const std::string & optionsLocatio options.folderMusicMapping = argv[++i]; else if(std::strcmp(argv[i], "--folderChannel") == 0 && i+1 < argc) options.folderMusicScreen = std::strcmp(argv[++i], "music") == 0; + else if(std::strcmp(argv[i], "--selectedMarker") == 0 && i+1 < argc) + options.selectedMarker = argv[++i]; } if(!options.commandLocation.empty() && options.commandLocation.back() != '/') options.commandLocation += '/'; @@ -179,7 +203,8 @@ bool LoadCommands(const std::string & commandLocation, const std::string & scrip } } commands.push_back(c); - items.push_back({ file.compare(0, 6, "c0000_") != 0, std::move(keys.author) }); + const bool needsAttention = !keys.attentionPath.empty() && HasAnyFile(keys.attentionPath); + items.push_back({ file.compare(0, 6, "c0000_") != 0, std::move(keys.author), needsAttention, std::move(keys.attentionNotice) }); } return true; } @@ -234,5 +259,5 @@ void AppendBackNavigation(const std::string & optionsLocation, const AppOptions back.previewImage = optionsLocation + "images/preview_placeholder.png"; back.command = backCommand; commands.push_back(back); - items.push_back({ false, "" }); + items.push_back({ false, "", false, "" }); } diff --git a/src/command_loader.h b/src/command_loader.h index b8153b6..7cd3b0d 100644 --- a/src/command_loader.h +++ b/src/command_loader.h @@ -26,6 +26,7 @@ struct AppOptions std::string folderThemesMapping; std::string folderMusicMapping; bool folderMusicScreen = false; + std::string selectedMarker; }; // each screen remembers its last focused row here; cleared on leaving the theme manager @@ -42,6 +43,8 @@ struct ItemInfo { bool isThemeItem; // false for a fixed "c0000_*" action, true for a theme/asset row std::string author; + bool needsAttention; + std::string attentionNotice; }; bool LoadCommands(const std::string & commandLocation, const std::string & scriptLocation, const std::string & optionsLocation, diff --git a/src/theme_downloader.c b/src/theme_downloader.c index 2da6e23..d7a45b4 100644 --- a/src/theme_downloader.c +++ b/src/theme_downloader.c @@ -80,7 +80,6 @@ static size_t write_to_file(void *ptr, size_t size, size_t nmemb, void *userdata enum fetch_result { FETCH_FAILED, FETCH_OK, FETCH_WRITE_ERROR }; -/* A .part file keeps an interrupted transfer away from the boot-time installer. */ static enum fetch_result download_to(CURL *c, const char *url, const char *out_path) { char part_path[512]; snprintf(part_path, sizeof(part_path), "%s.part", out_path); @@ -121,7 +120,6 @@ static void wait_until_removed(const char *path) { while (path[0] && access(path, F_OK) == 0) usleep(REMOVAL_POLL_US); } -/* Reports " " per theme so the caller can unpack while the next one downloads. */ static enum exit_code fetch_theme_list(CURL *c, const char *sftype, const char *list_path) { FILE *list = fopen(list_path, "r"); if (!list) return EXIT_FAILED; diff --git a/src/theme_manager_app.cpp b/src/theme_manager_app.cpp index 3479ed6..ef73ea9 100644 --- a/src/theme_manager_app.cpp +++ b/src/theme_manager_app.cpp @@ -308,7 +308,6 @@ int ThemeManagerApp::RunGridLayout() const int contentW = c.command.empty() ? 0 : ChipIconW(idx) + TextWidth(Translate(c.name), chipLabelSize); chipWidths.push_back(std::max(MinChipW, contentW + 2*chipPadX)); } - // the widest chips shrink first, down to a shared cap, when the strip overflows const int stripAvail = GridRight - GridLeft - std::max(0, stripSlotCount-1)*chipGap; auto StripWidthAt = [&](int cap) { @@ -328,6 +327,22 @@ int ThemeManagerApp::RunGridLayout() titleText_ = Texture(Translate(options_.titleKey), UiTheme::SectionTitleFontSize - 6, renderer_, UiTheme::SectionTitleX, 0, false, ToAbgr(UiTheme::Text), true); titleText_.rect.y = hasStrip ? (StripTop + StripH + 22) : (UiTheme::HeaderDividerY + 22); const int GridTop = titleText_.rect.y + titleText_.rect.h + 20; + + const int noticeSize = 14; + const int noticeGap = 24; + const int attentionDotSize = 10; + const int attentionDotInset = 7; + Texture noticeText; + for(int i = 0; i < (int)commands_.size(); ++i) + { + if(!items_[i].needsAttention || items_[i].attentionNotice.empty()) + continue; + const int noticeAvail = GridRight - (titleText_.rect.x + titleText_.rect.w + noticeGap); + noticeText = Texture(TruncateToWidth(Translate(items_[i].attentionNotice), noticeSize, noticeAvail), noticeSize, renderer_, 0, 0, false, ToAbgr(UiTheme::Accent), true); + noticeText.rect.x = GridRight - noticeText.rect.w; + noticeText.rect.y = titleText_.rect.y + (titleText_.rect.h - noticeText.rect.h) / 2; + break; + } const int GridBottom = UiTheme::FooterDividerY - 14; const int LabelH = hasCaptions ? 18 : 0; const int RowGap = hasCaptions ? 8 : GridGap; @@ -443,29 +458,34 @@ int ThemeManagerApp::RunGridLayout() // marks the tile of the theme currently applied on the console, so it stays visually // distinguishable from the rest of the grid regardless of where the cursor is std::vector isActiveTheme(commands_.size(), false); - if(options_.titleKey == "INSTALLED_THEMES") + const std::string activeMarker = options_.titleKey == "INSTALLED_THEMES" ? "/var/lib/clover/profiles/0/hakchi/lastTheme" : options_.selectedMarker; + auto RefreshActiveTiles = [&]() { - std::string activeTheme; - std::ifstream in("/var/lib/clover/profiles/0/hakchi/lastTheme"); - std::getline(in, activeTheme); - if(!activeTheme.empty() && activeTheme.back() == '\r') - activeTheme.pop_back(); + std::fill(isActiveTheme.begin(), isActiveTheme.end(), false); + std::string activeName; + if(!activeMarker.empty()) + { + std::ifstream in(activeMarker); + std::getline(in, activeName); + } + if(!activeName.empty() && activeName.back() == '\r') + activeName.pop_back(); + if(activeName.empty()) + return; - if(!activeTheme.empty()) + const std::string suffix = ".sh"; + for(int i = themeStart_; i < pinnedStartIndex_; ++i) { - const std::string suffix = ".sh"; - for(int i = themeStart_; i < pinnedStartIndex_; ++i) - { - const std::string & cmd = commands_[i].command; - if(cmd.size() <= suffix.size() || cmd.compare(cmd.size()-suffix.size(), suffix.size(), suffix) != 0) - continue; - size_t slashPos = cmd.find_last_of('/'); - size_t start = (slashPos == std::string::npos) ? 0 : slashPos+1; - if(cmd.compare(start, cmd.size()-suffix.size()-start, activeTheme) == 0) - isActiveTheme[i] = true; - } + const std::string & cmd = commands_[i].command; + if(cmd.size() <= suffix.size() || cmd.compare(cmd.size()-suffix.size(), suffix.size(), suffix) != 0) + continue; + size_t slashPos = cmd.find_last_of('/'); + size_t start = (slashPos == std::string::npos) ? 0 : slashPos+1; + if(cmd.compare(start, cmd.size()-suffix.size()-start, activeName) == 0) + isActiveTheme[i] = true; } - } + }; + RefreshActiveTiles(); int gridTopRow = 0; auto SetCurrentCommand = [&](int newId) @@ -503,6 +523,7 @@ int ThemeManagerApp::RunGridLayout() { if(ActivateCommand(commands_[currentCommandId_])) break; + RefreshActiveTiles(); } else if(controller_->HeldRepeat(LEFT)) { @@ -599,9 +620,13 @@ int ThemeManagerApp::RunGridLayout() label.rect.y = StripTop + (StripH - label.rect.h) / 2; label.Draw(renderer_); + if(items_[idx].needsAttention) + DrawRoundedFillRect(renderer_, { x + chipW - attentionDotSize - attentionDotInset, StripTop + attentionDotInset, attentionDotSize, attentionDotSize }, UiTheme::Accent, attentionDotSize / 2); + x += chipW + chipGap; } } + noticeText.Draw(renderer_); { for(int row = 0; row < GridRowsVisible; ++row) From 5b6c9e4801ddd5ada2da748d069c3921b0db2b2b Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 16:03:19 +0000 Subject: [PATCH 129/210] Unmount every theme bind on folder change and skip unchanged themes The old loop only unmounted paths present in the previous theme, so binds from a DIY draft or a deleted theme stayed over the UI. Find them through mountinfo instead, and skip the remount when the folder resolves to the theme and music already applied. --- .../themes/scripts/om_themescript | 187 +++++++++--------- 1 file changed, 91 insertions(+), 96 deletions(-) diff --git a/mod/etc/options_menu/themes/scripts/om_themescript b/mod/etc/options_menu/themes/scripts/om_themescript index 48538de..34ce457 100644 --- a/mod/etc/options_menu/themes/scripts/om_themescript +++ b/mod/etc/options_menu/themes/scripts/om_themescript @@ -29,21 +29,10 @@ musicHackBind() { return 0 } -musicHackUnbind() { - audio_files="bgm_boot bgm_home se_sys_bounce se_sys_cancel se_sys_click se_sys_click_game se_sys_cursor se_sys_lock se_sys_ng se_sys_push se_sys_smoke se_sys_unlock" - local current_mounts - current_mounts="$(cat /proc/mounts)" - - for wav_name in $audio_files; do - case "$current_mounts" in - *" ${original_theme_audio}/${wav_name}.wav "*) - # a folder's music can sit on top of the theme's own bind - while grep -q " ${original_theme_audio}/${wav_name}.wav " /proc/mounts; do - umount "${original_theme_audio}/${wav_name}.wav" || break - done - ;; - esac - done +unbindThemeMounts() { + local mount_points + mount_points="$(awk -v dir="$original_theme/" 'index($5, dir) == 1 && ($4 ~ /\/themes\// || $4 ~ /^\/diy_theme\// || $4 ~ /\/music_menu\//) { print $5 }' /proc/self/mountinfo | sed -n '1!G;h;$p')" + [ -n "$mount_points" ] && echo "$mount_points" | xargs umount } fontBindFix() { @@ -84,7 +73,6 @@ assignedFolderMusic() { return 1 } -# the entry for folder $1 inside its parent folder (not a Back/Home shortcut to it) folderEntry() { local folder_desktop for folder_desktop in "$game_storage"/*/CLV-S-00"$1"/CLV-S-00"$1".desktop; do @@ -96,42 +84,44 @@ folderEntry() { return 1 } -# own assignment, else a theme named after the folder, else whatever its parent folder gets -folderTheme() { +activeFolderExists() { + local folder_desktop + for folder_desktop in "$game_storage/$active_folder.desktop" "$game_storage/$active_folder/$active_folder.desktop" "$game_storage"/*/"$active_folder"/"$active_folder".desktop; do + [ -f "$folder_desktop" ] && return 0 + done + return 1 +} + +folderKeys() { local folder_code="$1" local folder_desktop - local folder_key local level=0 if [ "$folder_code" == "000" ]; then - assignedFolderTheme "home" + echo "home" return fi while [ "$folder_code" != "000" ] && [ "$level" -lt 16 ]; do level=$((level + 1)) - folder_desktop="$(folderEntry "$folder_code")" || return 1 - folder_key="$(sed -n 's/^Name=//p' "$folder_desktop" | head -n1 | sed 's/ /_/g' | awk '{print tolower($0)}')" + folder_desktop="$(folderEntry "$folder_code")" || return + awk '/^Name=/ { name = substr($0, 6); gsub(/ /, "_", name); print tolower(name); exit }' "$folder_desktop" + folder_code="${folder_desktop%/*/*}" + folder_code="${folder_code##*/}" + done +} + +folderTheme() { + local folder_key + for folder_key in $1; do assignedFolderTheme "$folder_key" && return 0 - [ -n "$folder_key" ] && [ -d "$themes_path/$folder_key" ] && echo "$folder_key" && return 0 - folder_code="$(basename "$(dirname "$(dirname "$folder_desktop")")")" + [ "$folder_key" != "home" ] && [ -d "$themes_path/$folder_key" ] && echo "$folder_key" && return 0 done return 1 } folderMusic() { - local folder_code="$1" - local folder_desktop local folder_key - local level=0 - if [ "$folder_code" == "000" ]; then - assignedFolderMusic "home" - return - fi - while [ "$folder_code" != "000" ] && [ "$level" -lt 16 ]; do - level=$((level + 1)) - folder_desktop="$(folderEntry "$folder_code")" || return 1 - folder_key="$(sed -n 's/^Name=//p' "$folder_desktop" | head -n1 | sed 's/ /_/g' | awk '{print tolower($0)}')" + for folder_key in $1; do assignedFolderMusic "$folder_key" && return 0 - folder_code="$(basename "$(dirname "$(dirname "$folder_desktop")")")" done return 1 } @@ -166,13 +156,12 @@ themeSelector() { local original_theme_audio="$original_theme/resources/sounds/hvc" local themes_path="$mountpoint/media/$modname/themes/$system_type" [ ! -d "$themes_path" ] && themes_path="$rootfs/usr/share/themes/$system_type" - local theme_dir_list - theme_dir_list="$(find "$themes_path" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sed 's#^.*/##')" local theme_state_path="$mountpoint$profilepath/$modname" folder_id="$(cat "$theme_state_path/menu")" local active_folder="CLV-S-00${folder_id}" local last_theme="$theme_state_path/lastTheme" local active_theme_file="$theme_state_path/active_theme" + local applied_file="/tmp/om_theme_applied" [ -f "$last_theme" ] && previous_theme=$(cat "$last_theme") local selected_theme="CHANGEME" @@ -189,74 +178,80 @@ themeSelector() { local bgm_home="" game_storage="$(findGameStorage)" - find "$game_storage" -maxdepth 3 -type f -name "$active_folder.desktop" | sort -u | while IFS= read -r _; do - if [ -f "$theme_state_path/om_themerandomizer" ]; then - selected_theme="$previous_theme" - local n="0" - while [ "$selected_theme" == "$previous_theme" ] && [ "$n" -lt 3 ]; do - n=$((n + 1)) - selected_theme="$(printf '%s\n' "$theme_dir_list" | shuf | head -n1)" - done - fi + activeFolderExists || return 0 - [ "$folder_id" == "000" ] && [ -d "$themes_path/default" ] && selected_theme="default" - if [ -f "$theme_state_path/om_themeperfolder" ]; then - local assigned_theme - assigned_theme="$(folderTheme "$folder_id")" - [ -n "$assigned_theme" ] && selected_theme="$assigned_theme" - fi - local assigned_music="" - [ -f "$theme_state_path/om_musicperfolder" ] && assigned_music="$(folderMusic "$folder_id")" + if [ -f "$theme_state_path/om_themerandomizer" ]; then + local theme_dir_list + theme_dir_list="$(find "$themes_path" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sed 's#^.*/##')" + selected_theme="$previous_theme" + local n="0" + while [ "$selected_theme" == "$previous_theme" ] && [ "$n" -lt 3 ]; do + n=$((n + 1)) + selected_theme="$(printf '%s\n' "$theme_dir_list" | shuf | head -n1)" + done + fi - full_theme_path="$themes_path/$selected_theme" + [ "$folder_id" == "000" ] && [ -d "$themes_path/default" ] && selected_theme="default" + local folder_keys="" + if [ -f "$theme_state_path/om_themeperfolder" ] || [ -f "$theme_state_path/om_musicperfolder" ]; then + folder_keys="$(folderKeys "$folder_id")" + fi + if [ -f "$theme_state_path/om_themeperfolder" ]; then + local assigned_theme + assigned_theme="$(folderTheme "$folder_keys")" + [ -n "$assigned_theme" ] && selected_theme="$assigned_theme" + fi + local assigned_music="" + [ -f "$theme_state_path/om_musicperfolder" ] && assigned_music="$(folderMusic "$folder_keys")" - if find "$diy_theme" -type f 2>/dev/null | read -r _; then - full_theme_path="$diy_theme" - fi + full_theme_path="$themes_path/$selected_theme" - if [ "$selected_theme" != "$previous_theme" ] || [ ! -f "$last_theme" ] || [ ! -f "$active_theme_file" ]; then - echo "$selected_theme" >"$last_theme" - echo "$full_theme_path" >"$active_theme_file" - fi + if find "$diy_theme" -type f 2>/dev/null | read -r _; then + full_theme_path="$diy_theme" + fi - if [ -d "$theme_font_path" ]; then - cmp -s "$title_font" "$theme_font_path/title.fnt" || fontBindFix "backup" - else - fontBindFix "restore" - fi + if [ "$selected_theme" != "$previous_theme" ] || [ ! -f "$last_theme" ] || [ ! -f "$active_theme_file" ]; then + echo "$selected_theme" >"$last_theme" + echo "$full_theme_path" >"$active_theme_file" + fi - # lazy, since fontFix binds title.font on top of the overlay after every chmenu - while grep -q " $original_theme overlayfs " /proc/mounts; do - umount -l "$original_theme" || break - done - musicHackUnbind + local applied="$full_theme_path|$assigned_music" + if [ -z "$mountpoint" ] && [ "$full_theme_path" != "$diy_theme" ] && [ -d "$full_theme_path" ] && + [ ! -f "$theme_state_path/om_themerandomizer" ] && [ ! -f "$theme_state_path/om_audiorandomizer" ] && + [ "$(cat "$applied_file" 2>/dev/null)" = "$applied" ]; then + return 0 + fi - cd "$themes_path/$previous_theme" && find ./* -type f ! -name "*.wav" ! -path "./fonts/*" | cut -c3- | while IFS= read -r umount_file; do - umount "$original_theme/$umount_file" - done + if [ -d "$theme_font_path" ]; then + cmp -s "$title_font" "$theme_font_path/title.fnt" || fontBindFix "backup" + else + fontBindFix "restore" + fi - if [ "$full_theme_path" != "$diy_theme" ] && [ -f "$full_theme_path/needs_overlay" ]; then - [ -d "$full_theme_path" ] && mount -t overlayfs -o lowerdir="$original_theme",upperdir="$full_theme_path" overlayfs "$original_theme" - else - if [ -z "$assigned_music" ] && [ -f "$theme_state_path/om_audiorandomizer" ] && [ -n "$(ls -A "$music_path")" ]; then - [ -z "$bgm_home" ] && bgm_home="$(find "$music_path" -maxdepth 1 -type f -iname "*.wav" | shuf | head -n1)" - if [ "$folder_id" = "000" ]; then - musicHackBind "$bgm_home" "$bgm_home" "$original_theme_audio" - else - [ -f "$theme_state_path/om_advmusichack" ] && [ -z "$(find "$full_theme_path" -type f -name "*.wav")" ] && musicHackBind "$bgm_home" "$bgm_home" "$original_theme_audio" - fi - fi + unbindThemeMounts + while grep -q " $original_theme overlayfs " /proc/mounts; do + umount -l "$original_theme" || break + done - local skip_bgm="none" - [ -n "$assigned_music" ] && skip_bgm="bgm_*.wav" - cd "$full_theme_path" && find . -type f ! -path "./fonts/*" ! -name "$skip_bgm" | while IFS= read -r theme_file; do - mount_bind "$full_theme_path/$theme_file" "$original_theme/$theme_file" - done + if [ "$full_theme_path" != "$diy_theme" ] && [ -f "$full_theme_path/needs_overlay" ]; then + [ -d "$full_theme_path" ] && mount -t overlayfs -o lowerdir="$original_theme",upperdir="$full_theme_path" overlayfs "$original_theme" + else + if [ -z "$assigned_music" ] && [ -f "$theme_state_path/om_audiorandomizer" ] && [ -n "$(ls -A "$music_path")" ]; then + [ -z "$bgm_home" ] && bgm_home="$(find "$music_path" -maxdepth 1 -type f -iname "*.wav" | shuf | head -n1)" + if [ "$folder_id" = "000" ]; then + musicHackBind "$bgm_home" "$bgm_home" "$original_theme_audio" + else + [ -f "$theme_state_path/om_advmusichack" ] && [ -z "$(find "$full_theme_path" -type f -name "*.wav")" ] && musicHackBind "$bgm_home" "$bgm_home" "$original_theme_audio" + fi fi - [ -n "$assigned_music" ] && musicHackBind "$music_path/$assigned_music" "$music_path/$assigned_music" "$original_theme_audio" - cd - >/dev/null || exit - break - done + local skip_bgm="none" + [ -n "$assigned_music" ] && skip_bgm="bgm_*.wav" + find "$full_theme_path" -type f ! -path "$full_theme_path/fonts/*" ! -name "$skip_bgm" ! -name info ! -name bind_ok ! -name needs_overlay 2>/dev/null | while IFS= read -r theme_file; do + mount -o bind "$theme_file" "$original_theme/${theme_file#"$full_theme_path"/}" + done + fi + [ -n "$assigned_music" ] && musicHackBind "$music_path/$assigned_music" "$music_path/$assigned_music" "$original_theme_audio" + [ -z "$mountpoint" ] && echo "$applied" >"$applied_file" } From 7320d2e35ab872dc488c083cf08836b16e263f93 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 17:19:49 +0000 Subject: [PATCH 130/210] Update OptionsMenu submodule to 72f2f4b --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 3b9b9f5..72f2f4b 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 3b9b9f56717b595a69e421fe44b0c4ad88ebd5e1 +Subproject commit 72f2f4b3b42098a2ce99f50ac5b331510ce4d645 From 7159bc940f448366d10c3c182cf1b67bff3f054d Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 17:20:20 +0000 Subject: [PATCH 131/210] Confirm theme deletion with the shared dimmed dialog Deleting waits for the end of the frame, snapshots the screen and shows it dimmed behind the Options Menu confirm dialog, with A/B badges instead of the plain 'A = Yes B = No' text. --- Makefile | 2 +- src/theme_manager_app.cpp | 75 +++++++++++++++++---------------------- src/theme_manager_app.h | 11 +++--- 3 files changed, 40 insertions(+), 48 deletions(-) diff --git a/Makefile b/Makefile index aed0706..9c74cb8 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ endif CXXFLAGS = -std=c++11 -Os -Ivendor/OptionsMenu/src $(SDL_CFLAGS) -DMOD_VERSION=\"v$(MOD_VER)\" LDLIBS = $(SDL_LIBS) VENDOR_SRC_DIR = vendor/OptionsMenu/src -SOURCES = src/main.cpp src/single_instance_lock.cpp src/command_loader.cpp src/texture_utils.cpp src/theme_manager_app.cpp src/folder_theme_model.cpp src/folder_themes_layout.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp +SOURCES = src/main.cpp src/single_instance_lock.cpp src/command_loader.cpp src/texture_utils.cpp src/theme_manager_app.cpp src/folder_theme_model.cpp src/folder_themes_layout.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp $(FRAMEWORK_DIR)/badge.cpp $(FRAMEWORK_DIR)/dialog.cpp OBJECTS = $(SOURCES:.cpp=.o) # Build theme_downloader with the toolchain's static curl+OpenSSL. diff --git a/src/theme_manager_app.cpp b/src/theme_manager_app.cpp index ef73ea9..f6f3a75 100644 --- a/src/theme_manager_app.cpp +++ b/src/theme_manager_app.cpp @@ -78,8 +78,7 @@ ThemeManagerApp::ThemeManagerApp(std::string optionsLocation, AppOptions options badgeA_ = Badge{ Texture("A", 16, renderer_, 0, 0, false, ToAbgr(UiTheme::BadgeLetter), true), Texture(Translate("HINT_SELECT"), 16, renderer_, 0, 0, false, ToAbgr(UiTheme::Text), true), UiTheme::BadgeADark, UiTheme::BadgeA }; badgeRowRightEdge_ = UiTheme::BadgeClusterRightX - UiTheme::BadgeOuterSize - UiTheme::BadgeLabelGap - badgeA_.label.rect.w - UiTheme::BadgeGroupGap; - badgeOuter_ = Texture(optionsLocation_ + UiTheme::AssetBadgeOuter, renderer_); - badgeInner_ = Texture(optionsLocation_ + UiTheme::AssetBadgeInner, renderer_); + badges_.reset(new BadgePainter(optionsLocation_, renderer_)); badgeHold_ = Badge{ Texture("B", 16, renderer_, 0, 0, false, ToAbgr(UiTheme::BadgeLetter), true), Texture(Translate("HINT_DELETE"), 16, renderer_, 0, 0, false, ToAbgr(UiTheme::Text), true), UiTheme::BadgeXDark, UiTheme::BadgeX }; ComputePinnedAndThemeRanges(); @@ -98,33 +97,15 @@ void ThemeManagerApp::ComputePinnedAndThemeRanges() int ThemeManagerApp::DrawBadge(Badge & badge, int rightEdgeX) { - int groupW = UiTheme::BadgeOuterSize + UiTheme::BadgeLabelGap + badge.label.rect.w; - int x = rightEdgeX - groupW; - int y = UiTheme::BadgeBandY; - badgeOuter_.rect = { x, y, UiTheme::BadgeOuterSize, UiTheme::BadgeOuterSize }; - SetColorMod(badgeOuter_.texture.get(), badge.rim); - badgeOuter_.Draw(renderer_); - int innerOffset = (UiTheme::BadgeOuterSize - UiTheme::BadgeInnerSize) / 2; - badgeInner_.rect = { x+innerOffset, y+innerOffset, UiTheme::BadgeInnerSize, UiTheme::BadgeInnerSize }; - SetColorMod(badgeInner_.texture.get(), badge.fill); - badgeInner_.Draw(renderer_); - // glyph bearing makes the pure-math center look 1px down/left - nudged - badge.letter.rect.x = x + (UiTheme::BadgeOuterSize - badge.letter.rect.w)/2 + 1; - badge.letter.rect.y = y + (UiTheme::BadgeOuterSize - badge.letter.rect.h)/2 - 1; - badge.letter.Draw(renderer_); - badge.label.rect.x = x + UiTheme::BadgeOuterSize + UiTheme::BadgeLabelGap; - badge.label.rect.y = y + (UiTheme::BadgeOuterSize - badge.label.rect.h)/2; - badge.label.Draw(renderer_); - return x - UiTheme::BadgeGroupGap; + return badges_->Draw(badge, rightEdgeX, UiTheme::BadgeBandY); } -// true if deleted - caller should break out of the main loop -bool ThemeManagerApp::ConfirmDelete() +bool ThemeManagerApp::ConfirmDelete(const ScreenSnapshot & background) { - const int DialogCenterX = 640, DialogTitleY = 320, DialogHintY = 360; // screen center (1280x720) const std::string & confirmKey = commands_[currentCommandId_].deleteConfirmKey; - Texture confirmTitle(Translate(confirmKey.empty() ? "DELETE_CONFIRM_GENERIC" : confirmKey), 24, renderer_, DialogCenterX, DialogTitleY, true, ToAbgr(UiTheme::Text), true); - Texture confirmHint(Translate("DELETE_CONFIRM_HINT"), 16, renderer_, DialogCenterX, DialogHintY, true, ToAbgr(UiTheme::Text), true); + ConfirmDialog dialog(renderer_, *badges_, Translate(confirmKey.empty() ? "DELETE_CONFIRM_GENERIC" : confirmKey), + badges_->Make("A", Translate("HINT_CONFIRM"), UiTheme::BadgeADark, UiTheme::BadgeA), + badges_->Make("B", Translate("HINT_CANCEL"), UiTheme::BadgeBDark, UiTheme::BadgeB)); controller_->GetButtonStatus(B); // consume the still-held B from the triggering long-press bool confirmed = false; for(;;) @@ -138,11 +119,9 @@ bool ThemeManagerApp::ConfirmDelete() break; } sdlContext_->StartFrame(); - DrawFillRect(renderer_, UiTheme::FrameRect, UiTheme::Bg); - DrawStrokeRect(renderer_, UiTheme::FrameRect, UiTheme::Border, UiTheme::BorderWidth, UiTheme::BorderRadius); - confirmTitle.Draw(renderer_); - confirmHint.Draw(renderer_); - SetDrawColor(renderer_, bg_); // flat helpers leave the draw color dirty + background.Draw(); + dialog.Draw(); + SetDrawColor(renderer_, bg_); sdlContext_->EndFrame(); } if(!confirmed) @@ -151,6 +130,20 @@ bool ThemeManagerApp::ConfirmDelete() return true; } +bool ThemeManagerApp::FinishFrame() +{ + SetDrawColor(renderer_, bg_); + if(!deleteRequested_) + { + sdlContext_->EndFrame(); + return false; + } + deleteRequested_ = false; + const ScreenSnapshot background(renderer_); + sdlContext_->EndFrame(); + return ConfirmDelete(background); +} + void ThemeManagerApp::DrawChromeCommon() { DrawStrokeRect(renderer_, UiTheme::OuterRect, UiTheme::Border, UiTheme::BorderWidth, UiTheme::BorderRadius); @@ -236,7 +229,7 @@ bool ThemeManagerApp::ActivateCommand(Command & cmd) return true; } -bool ThemeManagerApp::UpdateBButton(bool & tapped) +void ThemeManagerApp::UpdateBButton(bool & tapped) { tapped = false; bool bHeldNow = controller_->PeekButtonStatus(B); @@ -245,8 +238,8 @@ bool ThemeManagerApp::UpdateBButton(bool & tapped) if(!bHoldFired_ && controller_->HeldMillis(B) >= BHoldThresholdMs) { bHoldFired_ = true; - if(!commands_[currentCommandId_].deleteCommand.empty() && ConfirmDelete()) - return true; + if(!commands_[currentCommandId_].deleteCommand.empty()) + deleteRequested_ = true; } } else @@ -255,7 +248,6 @@ bool ThemeManagerApp::UpdateBButton(bool & tapped) bHoldFired_ = false; } bWasHeld_ = bHeldNow; - return false; } int ThemeManagerApp::RunGridLayout() @@ -565,8 +557,7 @@ int ThemeManagerApp::RunGridLayout() } { bool tapped = false; - if(UpdateBButton(tapped)) - break; + UpdateBButton(tapped); // quick tap - Back/Exit isn't a chip, B runs it directly if(tapped && pinnedStartIndex_ < (int)commands_.size() && ActivateCommand(commands_[pinnedStartIndex_])) break; @@ -708,8 +699,8 @@ int ThemeManagerApp::RunGridLayout() gridScrollDown.Draw(renderer_, SDL_FLIP_VERTICAL); } - SetDrawColor(renderer_, bg_); - sdlContext_->EndFrame(); + if(FinishFrame()) + break; } return 0; @@ -881,8 +872,7 @@ int ThemeManagerApp::RunListLayout() } { bool tapped = false; - if(UpdateBButton(tapped)) - break; + UpdateBButton(tapped); // quick tap - Back/Exit isn't a row you navigate to, B runs it directly (matches the grid layout) if(tapped && pinnedStartIndex_ < (int)commands_.size() && ActivateCommand(commands_[pinnedStartIndex_])) break; @@ -899,9 +889,8 @@ int ThemeManagerApp::RunListLayout() if((topListItemNumber + DisplayItemCount) < pinnedStartIndex_) scrollDown.Draw(renderer_, SDL_FLIP_VERTICAL); - SetDrawColor(renderer_, bg_); - - sdlContext_->EndFrame(); + if(FinishFrame()) + break; } return 0; diff --git a/src/theme_manager_app.h b/src/theme_manager_app.h index df3bc81..b1119c4 100644 --- a/src/theme_manager_app.h +++ b/src/theme_manager_app.h @@ -12,7 +12,9 @@ #include "command_loader.h" #include "command.h" +#include "framework/badge.h" #include "framework/controller.h" +#include "framework/dialog.h" #include "framework/sdl_helper.h" #include @@ -33,7 +35,6 @@ class ThemeManagerApp } private: - struct Badge { Texture letter; Texture label; Color rim; Color fill; }; enum class FrameEvent { Continue, Quit, PowerButtonPressed }; std::string optionsLocation_; @@ -52,7 +53,8 @@ class ThemeManagerApp Texture gearIcon_, switchOn_, switchOff_; Texture appTitleText_, appVersionText_, titleText_, creditText_; - Texture badgeOuter_, badgeInner_; + std::unique_ptr badges_; + bool deleteRequested_ = false; Badge badgeA_; Badge badgeHold_; int badgeRowRightEdge_ = 0; // x just left of badge A - fixed once badgeA_ is built @@ -64,14 +66,15 @@ class ThemeManagerApp void ComputePinnedAndThemeRanges(); int DrawBadge(Badge & badge, int rightEdgeX); - bool ConfirmDelete(); + bool ConfirmDelete(const ScreenSnapshot & background); + bool FinishFrame(); void DrawChromeCommon(); void DrawSectionTitle(); Texture MakeScrollArrow(int x, int y) const; FrameEvent PollFrameEvents(); bool ActivateCommand(Command & cmd); // true if the caller's loop should break - bool UpdateBButton(bool & tapped); // true if delete confirmed (caller should break); tapped on a quick tap + void UpdateBButton(bool & tapped); void ResumeUnderlyingUi() const; // CONTs the game PauseUI.sh stopped - nothing else does this on power-button exit From 35182474ef8cea952244a194994137e84424a2bb Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 17:37:24 +0000 Subject: [PATCH 132/210] Update OptionsMenu submodule to 3f393cf (shared badges and confirm dialog) --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 72f2f4b..3f393cf 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 72f2f4b3b42098a2ce99f50ac5b331510ce4d645 +Subproject commit 3f393cfdeda3e49016982649cff07d350a9b1243 From 06e27d56826d7d9d2702cbc3791e8f3c582cdfd1 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 22:16:44 +0000 Subject: [PATCH 133/210] Update OptionsMenu submodule to f7fc180 --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 3f393cf..f7fc180 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 3f393cfdeda3e49016982649cff07d350a9b1243 +Subproject commit f7fc180af603463dab5d83c3257cb1ee14d8ce64 From 061de4741c5536a2921da347dcc94e7bd02c15b9 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 7 Oct 2026 23:36:30 +0000 Subject: [PATCH 134/210] Update hmod-build submodule to 9f553b5 --- hmod-build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmod-build b/hmod-build index 38654e9..9f553b5 160000 --- a/hmod-build +++ b/hmod-build @@ -1 +1 @@ -Subproject commit 38654e9535a04120c81a9276447d785514001c59 +Subproject commit 9f553b5718ca671cf7bf59f9c714c3e07042ee4d From 7194bf546c9dd33259a72a6ff6c6e552a8ea9fa4 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 8 Oct 2026 11:31:39 +0000 Subject: [PATCH 135/210] Remove unused images from options menu and add new preview images --- mod/etc/options_menu/images/back.png | Bin 6062 -> 0 bytes mod/etc/options_menu/images/clear.png | Bin 5705 -> 0 bytes mod/etc/options_menu/images/luigi.png | Bin 18838 -> 0 bytes mod/etc/options_menu/images/mario.png | Bin 20502 -> 0 bytes mod/etc/options_menu/images/packed.png | Bin 9410 -> 0 bytes mod/etc/options_menu/images/packed_nes.png | Bin 681 -> 0 bytes mod/etc/options_menu/images/pole.png | Bin 238 -> 0 bytes .../{colors_nes.png => preview_colors_nes.png} | Bin ...{default_preview.png => preview_default.png} | Bin .../images/{diy_preview.png => preview_diy.png} | Bin .../options_menu/images/preview_diy_theme.png | Bin 4968 -> 0 bytes .../images/preview_download_themes.png | Bin 5258 -> 0 bytes .../{randtheme.png => preview_randtheme.png} | Bin .../{uninstall.png => preview_uninstall.png} | Bin .../images/preview_update_theme_list.png | Bin 4418 -> 0 bytes mod/etc/options_menu/images/reboot.png | Bin 2094 -> 0 bytes mod/etc/options_menu/images/sbm.png | Bin 2638 -> 0 bytes mod/etc/options_menu/images/settings.png | Bin 7701 -> 0 bytes .../options_menu/images/{ => ui}/chip_apply.png | Bin .../options_menu/images/{ => ui}/chip_clear.png | Bin .../options_menu/images/{ => ui}/chip_diy.png | Bin .../images/{ => ui}/chip_download.png | Bin .../images/{ => ui}/chip_preview.png | Bin .../images/{ => ui}/chip_refresh.png | Bin .../images/{ => ui}/chip_settings.png | Bin mod/etc/options_menu/images/update.png | Bin 2206 -> 0 bytes mod/etc/options_menu/images/wall.png | Bin 32533 -> 0 bytes mod/etc/options_menu/images/wall_far.png | Bin 17040 -> 0 bytes mod/etc/options_menu/images/wall_near.png | Bin 18894 -> 0 bytes 29 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 mod/etc/options_menu/images/back.png delete mode 100644 mod/etc/options_menu/images/clear.png delete mode 100644 mod/etc/options_menu/images/luigi.png delete mode 100644 mod/etc/options_menu/images/mario.png delete mode 100644 mod/etc/options_menu/images/packed.png delete mode 100644 mod/etc/options_menu/images/packed_nes.png delete mode 100644 mod/etc/options_menu/images/pole.png rename mod/etc/options_menu/images/{colors_nes.png => preview_colors_nes.png} (100%) rename mod/etc/options_menu/images/{default_preview.png => preview_default.png} (100%) rename mod/etc/options_menu/images/{diy_preview.png => preview_diy.png} (100%) delete mode 100644 mod/etc/options_menu/images/preview_diy_theme.png delete mode 100644 mod/etc/options_menu/images/preview_download_themes.png rename mod/etc/options_menu/images/{randtheme.png => preview_randtheme.png} (100%) rename mod/etc/options_menu/images/{uninstall.png => preview_uninstall.png} (100%) delete mode 100644 mod/etc/options_menu/images/preview_update_theme_list.png delete mode 100644 mod/etc/options_menu/images/reboot.png delete mode 100644 mod/etc/options_menu/images/sbm.png delete mode 100644 mod/etc/options_menu/images/settings.png rename mod/etc/options_menu/images/{ => ui}/chip_apply.png (100%) rename mod/etc/options_menu/images/{ => ui}/chip_clear.png (100%) rename mod/etc/options_menu/images/{ => ui}/chip_diy.png (100%) rename mod/etc/options_menu/images/{ => ui}/chip_download.png (100%) rename mod/etc/options_menu/images/{ => ui}/chip_preview.png (100%) rename mod/etc/options_menu/images/{ => ui}/chip_refresh.png (100%) rename mod/etc/options_menu/images/{ => ui}/chip_settings.png (100%) delete mode 100644 mod/etc/options_menu/images/update.png delete mode 100644 mod/etc/options_menu/images/wall.png delete mode 100644 mod/etc/options_menu/images/wall_far.png delete mode 100644 mod/etc/options_menu/images/wall_near.png diff --git a/mod/etc/options_menu/images/back.png b/mod/etc/options_menu/images/back.png deleted file mode 100644 index 9ae7d1b29296eb2b2c9012b6f103c6554a625b83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6062 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 diff --git a/mod/etc/options_menu/images/clear.png b/mod/etc/options_menu/images/clear.png deleted file mode 100644 index 9169a66820ef0de1907a7b091410517083b36cb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5705 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 Y*dD||tk2o0|CVUXj4h064G>ZP1JM&3+W-In diff --git a/mod/etc/options_menu/images/mario.png b/mod/etc/options_menu/images/mario.png deleted file mode 100644 index b99b3bd13c34698c23555c6d9b3f0d0ffaa4d1ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20502 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 E8~QP@?*IS* diff --git a/mod/etc/options_menu/images/packed_nes.png b/mod/etc/options_menu/images/packed_nes.png deleted file mode 100644 index e25e8069e2dfeb90b6c2cb74623d9de0f3ab2df4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 681 zcmeAS@N?(olHy`uVBq!ia0y~yU|a!YpJHJKl1$f6iUBF10G|-oJ$v>5nYXnWN=r-s z|Nrmk<$5Fi<5HlSAWs*^kP61PR|5NP8Hl(&^x4IzbHw@+*NRQ8c@6FF>b18DJEuu) z7l=HwcfVzNfKaQ)+2f_Q8DiUH*W7;@9r$sM-OGUAheCG;eC(4-4qxHpQU5cz=j9_C zH~$T_D?T%I75da4ep|GycJ5q(S1}PYr=%6{7IE*LANbTSeTAQz>6#UZH#MelKkRi{ zw%z^g?{b~x#gQd1_uq_I_jtMbsmGtopMI=7{H0y0?a#EAbNF6b%)hL;_eaf%=eLDl zKmJ*@)71VE-_}20Bq!JZl6v*;NbQE7_x{Ah$Q8E#vRb+2{@wU(W-AxkR2=?2=gN+A z59KFI{ht*Q`26yVk|3kTAkHaX`~j0wM6VS74HGH-^IkWg?$*vfhrWD{u-%dMe2K%% zqo0^lB~J5CNz}0qXxsS5p^@pUff*N=DR~Smw?24^oAvbhZn1u=ystDrT^`9=x}i=z zV8ImTr9v809age&fj| zbrJtpyp7#fZs(M|Upo8mcmCTuW16hA8kktQL^J{x0Ik1zY313H(oU{p%~iaotOCwo zd;IBj(c%na%e$8x8)3>qt+&oxrx$pzB4E8^B>StIRWWt3c_O)L%cHFzrT~pwk+t0H z@>NwW3I1268m}E}qx(;X{a`u09 z_1-nl{=fV7f6km|NB{rdvgQBWIU*)Nm5e1pe!&b5&u*jvIdz^cjv*GOlM@6)QW}_5 zRoU3wl9L%F=AS(3+URIuq9drbASiCy5e1ovZ2~KNn7LXMIj1zTT)M>bkSR0Mk0&W} zWdY;XEhQ5iv?m=XS>V7n$wAFSVUcLwim0b06Sn4hT=SE5C^zBKa%8+B+nH&=Q0lJa kbd-amKd`(fqMU)@LFF>RA8ngH16{)4>FVdQ&MBb@08>m>VgLXD diff --git a/mod/etc/options_menu/images/colors_nes.png b/mod/etc/options_menu/images/preview_colors_nes.png similarity index 100% rename from mod/etc/options_menu/images/colors_nes.png rename to mod/etc/options_menu/images/preview_colors_nes.png diff --git a/mod/etc/options_menu/images/default_preview.png b/mod/etc/options_menu/images/preview_default.png similarity index 100% rename from mod/etc/options_menu/images/default_preview.png rename to mod/etc/options_menu/images/preview_default.png diff --git a/mod/etc/options_menu/images/diy_preview.png b/mod/etc/options_menu/images/preview_diy.png similarity index 100% rename from mod/etc/options_menu/images/diy_preview.png rename to mod/etc/options_menu/images/preview_diy.png diff --git a/mod/etc/options_menu/images/preview_diy_theme.png b/mod/etc/options_menu/images/preview_diy_theme.png deleted file mode 100644 index 536511e0bf472a74d7d01ff92a08eff8e2bd40a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4968 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 BG&cYM diff --git a/mod/etc/options_menu/images/reboot.png b/mod/etc/options_menu/images/reboot.png deleted file mode 100644 index f40fa29e7b8371cac02578e7281480bbc40005b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2094 zcma)-XEYm(8po5`V%CUQZ;jHZh*`6h5Gpk)M%4;6TS{KFQoDjssjBhXF=}-PrwApn zNAapvd(>#jtFA`&zJx6<(Fb@b(5D{oErcD@S@F7HUI$7 z^*q9?Ea#huy~9iAP_nkLGdbtk|2IjkgYfffz7QKLTYv^kUXh!RMNC4NUGNX&=Zzcy zfTIG9LfYZywu;bigpdOLLp=kzsLopp;JU>o!deoex48_jnhaW%2?7f*T<0={mvKHV zljRI7k!BSUL9(tr#jDB2Gl*(|Op?NZ#k~DL_s^_$=*HC!wkNfeO)|HRBpTGH-0+>nQ=x+cQ$6b)-;J2N5bJ~xWS}>&0o5h6d%zBb% z4n%V`W#J(c&Y!T7fb-f>HUYJIBI>5QLXC8ixfM*XY4YIpAMC{hPzlw+Rew*nIrjrL z3dCJ4WnlIIUPYL^sHU7o|IPOiL}#*Np-cPjkLp@CWsQuOERY|T5P+KetW1o>B>8yA7x^S`pYXvAZPbP0n&T>xH$3 zhA%lvvx&AdDnoYNloPnEUmVp?=~rVXMf2YyOh!I4r@-p@p$V^4>4Pv;ja!VcaAHZZgkN` z=X<$-Q0?fUzIzm`=PT0~=J@@h{b-q-W~>N@X4V{P!4<~;vs^Ses#RnQBsDOCDW-jy z3&ezddn;BP!SN!n;KdJ5Bq8qZXhZ{iDH`@(CNMvXH}+IzbzVTL`?5p+VaXL$+KqQT z7nFH&mLX2Sj|G(Gs5!8PdcJQ@XMpoJCwTf*vMpDdc zUz(JP+3{oy!(iNo0|kZfHDPvpkS-UwNj8!Ab2XNKwrXvzzF9vZTvFZ!ETdLxNrq^v`+Xcz$@0SgJ9VgQ* zRGP`mo%>%cv72xWnvtD)vp2uk_SNa(Bl!`vt3EmJ0an2aO*@|38Buv_iIV)!pmpwf z7tfz-M8{kR$+9$Y9klc6E#~qu8?J+oj?IrohCwL023-~i=5js|qb(!=%#dz0voV$S zN((R}`Fc?idALE$Ku;YPvb4=_n#6K_oWyeJ9sBx;rmSt%{k^joyfJ(Vs4&@1euQ^C z;bMh4>i2)eZ`2&d>dR5Yr3NglPIe>^M)#7gA%pTp-5G`B$a% zkO8BuGIV;vb#aYCQiC3Iu$RQ>`ImQjgW{}& zhHq$=qCmOi54tG z_xB~$^3sTUq{Or!NT!CE9?_wzT$qgD7~rz_pv$=OTnz#$?S9)YZ;_C;FD!xrRecQB zY13R_2kowj8+3997r1omkeXz~+V*)SmDy77%Go_wRlaMoE_Ir+dbN({EU$z5H`Ti$ zaW=gCS+6-YuIRfVEQWvKGOh!AuowlGZN$_U)))SMEdj2A-K}$<>-U5|fropDlkfjvlo7V1NkRy)V44CgQQ=n1#jy3`UJ z{H=kqGz>e))_q%YWSX1!Losd2$5@rYC|xgBatB+UiRL&EaeYunHO48ac5iuj&IM*< z?Gos15XBp(By06VT>6)E`*nd-%l>&tyW3i+9%ozp;qVfV^yWP}eKvD8lNe+gKGW)P zxX!Csuc3Wy%knt|-$~r$kF4c;S7Sfhp#9bt(AZXb$o7r-_kXOJy8&8>u$RsLj3A$@ O9Dp{lLQ#y|#jMjQU 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 Y)=Tu7lP-b2_dNg*&K~eK$KY#!0|xnDH~;_u diff --git a/mod/etc/options_menu/images/wall.png b/mod/etc/options_menu/images/wall.png deleted file mode 100644 index 560cfc18c57561a3596e5fd5ba91877702880197..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32533 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 P8KEq%CRZb48vK6&EvOWb diff --git a/mod/etc/options_menu/images/wall_near.png b/mod/etc/options_menu/images/wall_near.png deleted file mode 100644 index eef7fd580867c197f76802bf07ab437ffba07f36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18894 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 KMNUMnLSTYY4Gk>- From d2214f0b7c21d21c11e67bf8da8df5fa1809908a Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 8 Oct 2026 11:37:32 +0000 Subject: [PATCH 136/210] Preview the chosen or stock DIY asset on the DIY home, follow the image renames - DIY home tiles show the picked asset, else the console's stock one read from the squashfs (skipped on hvcj, whose packed.png atlas differs); Base UI tiles anchor to the top via PREVIEW_ALIGN_TOP - animate pole previews like the character run cycles - 5 grid columns for wall near/far and pole now that duplicates are collapsed - show the loading splash while the installed theme list is rebuilt - point every command at the preview_* names and images/ui/chip_* - install drops generated DIY entries and legacy image names; uninstall lists every current and legacy image - fix del_shared removing smb.png instead of sbm --- .../options_menu/themes/commands/_2_download | 2 +- .../options_menu/themes/commands/_5_remove | 2 +- .../themes/commands/c0000_1_refresh | 2 +- .../themes/commands/c0000_3_generatetheme | 2 +- .../themes/commands/c0000_4_options | 2 +- .../themes/creator/commands/_3_apply | 2 +- .../themes/creator/commands/_4_preview | 2 +- .../themes/creator/commands/_5_remove | 2 +- .../themes/creator/commands/c0001_packed | 3 +- .../themes/creator/commands/c0001_packed_nes | 3 +- .../themes/creator/commands/c0001_wall | 2 +- .../themes/creator/commands/c0002_mario | 2 +- .../themes/creator/commands/c0002_sbm | 2 +- .../themes/creator/commands/c0003_luigi | 2 +- .../themes/creator/commands/c0004_wall_near | 2 +- .../themes/creator/commands/c0005_wall_far | 2 +- .../themes/creator/commands/c0006_colors | 3 +- .../themes/creator/commands/c0006_colors_nes | 3 +- .../themes/creator/commands/c0006_pole | 2 +- .../sprites/colors/commands/c0000_1_refresh | 2 +- .../colors_nes/commands/c0000_1_refresh | 2 +- .../sprites/luigi/commands/c0000_1_refresh | 2 +- .../sprites/mario/commands/c0000_1_refresh | 2 +- .../sprites/packed/commands/c0000_1_refresh | 2 +- .../packed_nes/commands/c0000_1_refresh | 2 +- .../sprites/pole/commands/c0000_1_refresh | 2 +- .../sprites/sbm/commands/c0000_1_refresh | 2 +- .../sprites/wall/commands/c0000_1_refresh | 2 +- .../sprites/wall_far/commands/c0000_1_refresh | 2 +- .../wall_near/commands/c0000_1_refresh | 2 +- .../downloads/commands/c0000_downloadThemes | 2 +- .../downloads/commands/c0000_removeThemes | 2 +- .../themes/downloads/scripts/om_return | 5 +- mod/etc/options_menu/themes/scripts/om_vars | 78 ++++++++++++++++--- .../settings/commands/_themeperfolder_1assign | 2 +- .../settings/commands/c0000_themeperfolder | 2 +- .../settings/commands/c0001_uninstaller | 2 +- mod/install | 5 ++ mod/uninstall | 25 ++++++ src/command_loader.cpp | 8 +- src/command_loader.h | 1 + src/folder_themes_layout.cpp | 2 +- src/theme_manager_app.cpp | 14 ++-- 43 files changed, 152 insertions(+), 60 deletions(-) diff --git a/mod/etc/options_menu/themes/commands/_2_download b/mod/etc/options_menu/themes/commands/_2_download index bb2da8c..ce101c2 100644 --- a/mod/etc/options_menu/themes/commands/_2_download +++ b/mod/etc/options_menu/themes/commands/_2_download @@ -2,4 +2,4 @@ COMMAND_NAME=DOWNLOAD_THEMES COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_getThemes -PREVIEW_IMAGE=/etc/options_menu/images/chip_download.png \ No newline at end of file +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_download.png \ No newline at end of file diff --git a/mod/etc/options_menu/themes/commands/_5_remove b/mod/etc/options_menu/themes/commands/_5_remove index 769470e..5f212a1 100644 --- a/mod/etc/options_menu/themes/commands/_5_remove +++ b/mod/etc/options_menu/themes/commands/_5_remove @@ -2,4 +2,4 @@ COMMAND_NAME=CLEAR_LIST COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_clearList & -PREVIEW_IMAGE=/etc/options_menu/images/chip_clear.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_clear.png diff --git a/mod/etc/options_menu/themes/commands/c0000_1_refresh b/mod/etc/options_menu/themes/commands/c0000_1_refresh index 2f7b6f8..b083501 100644 --- a/mod/etc/options_menu/themes/commands/c0000_1_refresh +++ b/mod/etc/options_menu/themes/commands/c0000_1_refresh @@ -2,4 +2,4 @@ COMMAND_NAME=REFRESH COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh /etc/options_menu/scripts/om_theme_selector reload -PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_refresh.png diff --git a/mod/etc/options_menu/themes/commands/c0000_3_generatetheme b/mod/etc/options_menu/themes/commands/c0000_3_generatetheme index c2a10e2..5c63769 100644 --- a/mod/etc/options_menu/themes/commands/c0000_3_generatetheme +++ b/mod/etc/options_menu/themes/commands/c0000_3_generatetheme @@ -2,6 +2,6 @@ COMMAND_NAME=DIY_THEME COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_themeCreator -PREVIEW_IMAGE=/etc/options_menu/images/chip_diy.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_diy.png ATTENTION_PATH=/tmp/diy_theme ATTENTION_NOTICE=DIY_DRAFT_NOTICE \ No newline at end of file diff --git a/mod/etc/options_menu/themes/commands/c0000_4_options b/mod/etc/options_menu/themes/commands/c0000_4_options index eecded3..885cc47 100644 --- a/mod/etc/options_menu/themes/commands/c0000_4_options +++ b/mod/etc/options_menu/themes/commands/c0000_4_options @@ -2,4 +2,4 @@ COMMAND_NAME=THEME_SETTINGS COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_settings & -PREVIEW_IMAGE=/etc/options_menu/images/chip_settings.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_settings.png diff --git a/mod/etc/options_menu/themes/creator/commands/_3_apply b/mod/etc/options_menu/themes/creator/commands/_3_apply index 23f5dfe..aba0252 100644 --- a/mod/etc/options_menu/themes/creator/commands/_3_apply +++ b/mod/etc/options_menu/themes/creator/commands/_3_apply @@ -2,4 +2,4 @@ COMMAND_NAME=SAVE_CUSTOM_THEME COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_applytheme & -PREVIEW_IMAGE=/etc/options_menu/images/chip_apply.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_apply.png diff --git a/mod/etc/options_menu/themes/creator/commands/_4_preview b/mod/etc/options_menu/themes/creator/commands/_4_preview index c89efb8..a1a2c13 100644 --- a/mod/etc/options_menu/themes/creator/commands/_4_preview +++ b/mod/etc/options_menu/themes/creator/commands/_4_preview @@ -3,4 +3,4 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE CHILD=TRUE COMMAND_STR=sh %script_dir%/om_preview & -PREVIEW_IMAGE=/etc/options_menu/images/chip_preview.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_preview.png diff --git a/mod/etc/options_menu/themes/creator/commands/_5_remove b/mod/etc/options_menu/themes/creator/commands/_5_remove index e805b3d..6ef8555 100644 --- a/mod/etc/options_menu/themes/creator/commands/_5_remove +++ b/mod/etc/options_menu/themes/creator/commands/_5_remove @@ -2,4 +2,4 @@ COMMAND_NAME=CLEAN_CUSTOM_THEME COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_removediy & -PREVIEW_IMAGE=/etc/options_menu/images/chip_clear.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_clear.png diff --git a/mod/etc/options_menu/themes/creator/commands/c0001_packed b/mod/etc/options_menu/themes/creator/commands/c0001_packed index b0494fd..3c74714 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0001_packed +++ b/mod/etc/options_menu/themes/creator/commands/c0001_packed @@ -2,5 +2,6 @@ COMMAND_NAME=BASE_UI COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset packed & -PREVIEW_IMAGE=/etc/options_menu/images/packed.png +PREVIEW_IMAGE=/etc/options_menu/images/preview_default.png +PREVIEW_ALIGN_TOP=TRUE SNES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0001_packed_nes b/mod/etc/options_menu/themes/creator/commands/c0001_packed_nes index cb5315e..5818ace 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0001_packed_nes +++ b/mod/etc/options_menu/themes/creator/commands/c0001_packed_nes @@ -2,5 +2,6 @@ COMMAND_NAME=BASE_UI COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset packed_nes & -PREVIEW_IMAGE=/etc/options_menu/images/packed_nes.png +PREVIEW_IMAGE=/etc/options_menu/images/preview_default.png +PREVIEW_ALIGN_TOP=TRUE NES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0001_wall b/mod/etc/options_menu/themes/creator/commands/c0001_wall index 9b6532e..34a8691 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0001_wall +++ b/mod/etc/options_menu/themes/creator/commands/c0001_wall @@ -2,5 +2,5 @@ COMMAND_NAME=BACKGROUND COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset wall & -PREVIEW_IMAGE=/etc/options_menu/images/wall.png +PREVIEW_IMAGE=/etc/options_menu/images/preview_default.png SNES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0002_mario b/mod/etc/options_menu/themes/creator/commands/c0002_mario index 5fdcb8c..283e056 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0002_mario +++ b/mod/etc/options_menu/themes/creator/commands/c0002_mario @@ -2,6 +2,6 @@ COMMAND_NAME=DEMO_CHARACTER_1 COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset mario & -PREVIEW_IMAGE=/etc/options_menu/images/mario.png +PREVIEW_IMAGE=/etc/options_menu/images/preview_default.png PREVIEW_FIT_CONTAIN=TRUE SNES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0002_sbm b/mod/etc/options_menu/themes/creator/commands/c0002_sbm index ecf5b19..be7441a 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0002_sbm +++ b/mod/etc/options_menu/themes/creator/commands/c0002_sbm @@ -2,6 +2,6 @@ COMMAND_NAME=DEMO_CHARACTERS COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset sbm & -PREVIEW_IMAGE=/etc/options_menu/images/sbm.png +PREVIEW_IMAGE=/etc/options_menu/images/preview_default.png PREVIEW_FIT_CONTAIN=TRUE NES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0003_luigi b/mod/etc/options_menu/themes/creator/commands/c0003_luigi index 8b2b1ee..3c93f04 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0003_luigi +++ b/mod/etc/options_menu/themes/creator/commands/c0003_luigi @@ -2,6 +2,6 @@ COMMAND_NAME=DEMO_CHARACTER_2 COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset luigi & -PREVIEW_IMAGE=/etc/options_menu/images/luigi.png +PREVIEW_IMAGE=/etc/options_menu/images/preview_default.png PREVIEW_FIT_CONTAIN=TRUE SNES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0004_wall_near b/mod/etc/options_menu/themes/creator/commands/c0004_wall_near index a17760a..c3f7888 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0004_wall_near +++ b/mod/etc/options_menu/themes/creator/commands/c0004_wall_near @@ -2,5 +2,5 @@ COMMAND_NAME=DEMO_BACKGROUND_NEAR COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset wall_near & -PREVIEW_IMAGE=/etc/options_menu/images/wall_near.png +PREVIEW_IMAGE=/etc/options_menu/images/preview_default.png SNES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0005_wall_far b/mod/etc/options_menu/themes/creator/commands/c0005_wall_far index 674b446..3ba2eb1 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0005_wall_far +++ b/mod/etc/options_menu/themes/creator/commands/c0005_wall_far @@ -2,5 +2,5 @@ COMMAND_NAME=DEMO_BACKGROUND_FAR COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset wall_far & -PREVIEW_IMAGE=/etc/options_menu/images/wall_far.png +PREVIEW_IMAGE=/etc/options_menu/images/preview_default.png SNES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0006_colors b/mod/etc/options_menu/themes/creator/commands/c0006_colors index 306bcb7..d65fe87 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0006_colors +++ b/mod/etc/options_menu/themes/creator/commands/c0006_colors @@ -2,4 +2,5 @@ COMMAND_NAME=COLORS COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset colors & -PREVIEW_IMAGE=/etc/options_menu/images/colors_nes.png +PREVIEW_IMAGE=/etc/options_menu/images/preview_colors_nes.png +DEFAULT_PREVIEW_IMAGE=/etc/options_menu/images/preview_colors_nes.png diff --git a/mod/etc/options_menu/themes/creator/commands/c0006_colors_nes b/mod/etc/options_menu/themes/creator/commands/c0006_colors_nes index 95d3a96..dfa1957 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0006_colors_nes +++ b/mod/etc/options_menu/themes/creator/commands/c0006_colors_nes @@ -2,5 +2,6 @@ COMMAND_NAME=BACKGROUND_COLOR COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset colors_nes & -PREVIEW_IMAGE=/etc/options_menu/images/colors_nes.png +PREVIEW_IMAGE=/etc/options_menu/images/preview_colors_nes.png +DEFAULT_PREVIEW_IMAGE=/etc/options_menu/images/preview_colors_nes.png NES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/commands/c0006_pole b/mod/etc/options_menu/themes/creator/commands/c0006_pole index 2bdaed8..5e60748 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0006_pole +++ b/mod/etc/options_menu/themes/creator/commands/c0006_pole @@ -2,6 +2,6 @@ COMMAND_NAME=DEMO_POLE COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_diy_asset pole & -PREVIEW_IMAGE=/etc/options_menu/images/pole.png +PREVIEW_IMAGE=/etc/options_menu/images/preview_default.png PREVIEW_FIT_CONTAIN=TRUE SNES_ONLY=TRUE diff --git a/mod/etc/options_menu/themes/creator/sprites/colors/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/colors/commands/c0000_1_refresh index 4a7cc5b..37556a1 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors/commands/c0000_1_refresh +++ b/mod/etc/options_menu/themes/creator/sprites/colors/commands/c0000_1_refresh @@ -2,4 +2,4 @@ COMMAND_NAME=REFRESH COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset colors force & -PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c0000_1_refresh index 3a2a8ac..c2fea0a 100644 --- a/mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c0000_1_refresh +++ b/mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c0000_1_refresh @@ -2,4 +2,4 @@ COMMAND_NAME=REFRESH COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset colors_nes force & -PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/luigi/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/luigi/commands/c0000_1_refresh index ec50f39..56bc789 100644 --- a/mod/etc/options_menu/themes/creator/sprites/luigi/commands/c0000_1_refresh +++ b/mod/etc/options_menu/themes/creator/sprites/luigi/commands/c0000_1_refresh @@ -2,4 +2,4 @@ COMMAND_NAME=REFRESH COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset luigi force & -PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/mario/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/mario/commands/c0000_1_refresh index 93a0d8d..3d40552 100644 --- a/mod/etc/options_menu/themes/creator/sprites/mario/commands/c0000_1_refresh +++ b/mod/etc/options_menu/themes/creator/sprites/mario/commands/c0000_1_refresh @@ -2,4 +2,4 @@ COMMAND_NAME=REFRESH COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset mario force & -PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/packed/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/packed/commands/c0000_1_refresh index 5d5d51f..25be7b2 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed/commands/c0000_1_refresh +++ b/mod/etc/options_menu/themes/creator/sprites/packed/commands/c0000_1_refresh @@ -2,4 +2,4 @@ COMMAND_NAME=REFRESH COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset packed force & -PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c0000_1_refresh index 051d48e..455c5b8 100644 --- a/mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c0000_1_refresh +++ b/mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c0000_1_refresh @@ -2,4 +2,4 @@ COMMAND_NAME=REFRESH COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset packed_nes force & -PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/pole/commands/c0000_1_refresh index 477e58a..abd5278 100644 --- a/mod/etc/options_menu/themes/creator/sprites/pole/commands/c0000_1_refresh +++ b/mod/etc/options_menu/themes/creator/sprites/pole/commands/c0000_1_refresh @@ -2,4 +2,4 @@ COMMAND_NAME=REFRESH COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset pole force & -PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c0000_1_refresh index 41c492c..cd01e74 100644 --- a/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c0000_1_refresh +++ b/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c0000_1_refresh @@ -2,4 +2,4 @@ COMMAND_NAME=REFRESH COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset sbm force & -PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/wall/commands/c0000_1_refresh index 36078b9..01e95a6 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall/commands/c0000_1_refresh +++ b/mod/etc/options_menu/themes/creator/sprites/wall/commands/c0000_1_refresh @@ -2,4 +2,4 @@ COMMAND_NAME=REFRESH COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset wall force & -PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c0000_1_refresh index bdaf9b7..018262c 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c0000_1_refresh +++ b/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c0000_1_refresh @@ -2,4 +2,4 @@ COMMAND_NAME=REFRESH COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset wall_far force & -PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c0000_1_refresh index 6887463..5550594 100644 --- a/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c0000_1_refresh +++ b/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c0000_1_refresh @@ -2,4 +2,4 @@ COMMAND_NAME=REFRESH COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %options_path%/themes/creator/scripts/om_diy_asset wall_near force & -PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_refresh.png diff --git a/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes b/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes index 002862b..bad1fc3 100644 --- a/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes +++ b/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes @@ -2,4 +2,4 @@ COMMAND_NAME=UPDATE_THEME_LIST COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_refreshThemes & -PREVIEW_IMAGE=/etc/options_menu/images/chip_refresh.png \ No newline at end of file +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_refresh.png \ No newline at end of file diff --git a/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes b/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes index f57f41c..f5ae782 100644 --- a/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes +++ b/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes @@ -2,4 +2,4 @@ COMMAND_NAME=CLEAR_LIST COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_clearDownloads & -PREVIEW_IMAGE=/etc/options_menu/images/chip_clear.png +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_clear.png diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_return b/mod/etc/options_menu/themes/downloads/scripts/om_return index 7fc21e5..e925840 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_return +++ b/mod/etc/options_menu/themes/downloads/scripts/om_return @@ -23,10 +23,7 @@ remove "$om_dummy" if [ -n "$(ls -A $theme_path)" ]; then touch "$om_dummy" - if themeCountChanged; then - "$om_scripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png > /dev/fb0 - themeLoader - fi + themeCountChanged && themeLoader else remove "$om_mod_spacer" clearList diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 40e78ef..ad2888c 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -170,9 +170,9 @@ snes_theme_path="${om_mod_path}/snes" #### ARRAYS diy_asset_array="diy_packed diy_wall diy_wall_far diy_wall_near diy_mario diy_luigi diy_colors diy_pole" # verify -del_shared="$nes_theme_path $disable_smb $disable_packed_nes $disable_colors_nes $om_mod_packed_nes $om_mod_sbm $om_mod_colors_nes ${om_mod_imgs}/smb.png ${om_mod_imgs}/packed_nes.png $disable_color $om_mod_colors" +del_shared="$nes_theme_path $disable_smb $disable_packed_nes $disable_colors_nes $om_mod_packed_nes $om_mod_sbm $om_mod_colors_nes $disable_color $om_mod_colors" -del_on_nes="$snes_theme_path $shonen_theme_path $disable_color $disable_pole $disable_mario $disable_luigi $disable_packed $disable_wall $disable_wall_far $disable_wall_near $om_mod_pole $om_mod_mario $om_mod_luigi $om_mod_wall $om_mod_wall_n $om_mod_wall_f $om_mod_packed $om_mod_colors ${om_mod_imgs}/luigi.png ${om_mod_imgs}/mario.png ${om_mod_imgs}/packed.png ${om_mod_imgs}/pole.png ${om_mod_imgs}/wall*.png" +del_on_nes="$snes_theme_path $shonen_theme_path $disable_color $disable_pole $disable_mario $disable_luigi $disable_packed $disable_wall $disable_wall_far $disable_wall_near $om_mod_pole $om_mod_mario $om_mod_luigi $om_mod_wall $om_mod_wall_n $om_mod_wall_f $om_mod_packed $om_mod_colors" del_on_snes="$shonen_theme_path $del_shared" del_on_shonen="$snes_theme_path $del_shared" items_to_delete="$del_on_snes" @@ -230,6 +230,7 @@ migrateThemes() { hasThemesToInstall() { [ "$has_theme" = 0 ] && return + "$om_scripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png > /dev/fb0 for f in "$theme_path/$system_name".*.tar.gz; do [ -f "$f" ] || continue tar -xzf "$f" -C "$theme_path" && rm -f "$f" @@ -293,6 +294,7 @@ themeCountChanged() { } themeLoader() { + "$om_scripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png > /dev/fb0 removeRegionalTheme cd "$theme_path" && find . -mindepth 1 -maxdepth 1 -type d | sed 's#^\./##' | sort -u | while IFS= read -r thm; do full_path="$theme_path/$thm" @@ -359,8 +361,8 @@ sleep 1 preview_image="$thm_preview/$thm.png" [ ! -f "$preview_image" ] && preview_image="$theme_path/$thm.png" - [ ! -f "$preview_image" ] && [ -z "${thm##diy_theme*}" ] && preview_image="$options_menu/images/diy_preview.png" - [ ! -f "$preview_image" ] && preview_image="$options_menu/images/default_preview.png" + [ ! -f "$preview_image" ] && [ -z "${thm##diy_theme*}" ] && preview_image="$options_menu/images/preview_diy.png" + [ ! -f "$preview_image" ] && preview_image="$options_menu/images/preview_default.png" cmd_name="$(sed -r 's/NAME=//g' < "$full_path/info")" echo "COMMAND_NAME=$cmd_name @@ -563,9 +565,7 @@ unpackTheme() { return "$dl_rc" } -findDiyAsset() { - "$om_scripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png > /dev/fb0 - +diyAssetSpec() { diy_file="${1}.png" diy_sprite="$om_mod_sprites/${1}" asset_path="layout/asset/sprite/wallpaper" @@ -585,13 +585,10 @@ findDiyAsset() { preview_square="1" preview_cols="5" ;; - wall) + wall | wall_near | wall_far) preview_square="1" preview_cols="5" ;; - wall_near | wall_far) - preview_square="1" - ;; sbm) diy_file="characters_sbm.png" asset_path="sprites" @@ -629,9 +626,12 @@ findDiyAsset() { asset="*.*" chars="1" preview_square="1" + preview_cols="5" ;; esac +} +diyCropFrame() { diy_crop_x="" diy_sprites_json="$om_mod_scripts/om_${1}.sprites.json" if [ "$colors" = "0" ] && [ -f "$diy_sprites_json" ]; then @@ -643,6 +643,13 @@ findDiyAsset() { diy_crop_w="${diy_rest%%,*}" diy_crop_h="${diy_rest#*,}" fi +} + +findDiyAsset() { + "$om_scripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png > /dev/fb0 + + diyAssetSpec "$1" + diyCropFrame "$1" paths_to_search="$om_mod_sprites/assets/$1 $theme_path" find "$diy_sprite/commands" "$diy_sprite/scripts" -type f \ @@ -772,12 +779,59 @@ diyThemeChecker() { disableCmds "diy" "del_diy" "preview" fi + diyHomePreviews + [ "$1" = "loader" ] && usleep 1000 } +# the console's own stock asset, read from the squashfs so an applied theme can't shadow it +diyStockPreview() { + stock_preview="" + [ "$colors" = "1" ] && return + [ "$sftype" = "hvcj" ] && return # its packed.png atlas doesn't match om_packed.sprites.json + stock_file="$squashfs/usr/share/$original_theme_ui0/resources/$asset_path/$diy_file" + [ -f "$stock_file" ] || return + + diyCropFrame "$1" + if [ -z "$diy_crop_x" ]; then + stock_preview="$stock_file" + return + fi + stock_crop="$om_mod_sprites/assets/$1/.stock.png" + [ -f "$stock_crop" ] || sprite_crop "$stock_file" "$diy_crop_x" "$diy_crop_y" "$diy_crop_w" "$diy_crop_h" "$stock_crop" || return + stock_preview="$stock_crop" +} + +# each DIY home entry previews the asset picked for it, else the console's stock one +diyHomePreviews() { + for home_cmd in "$om_mod_gen_cmds"/*; do + diy_category="$(sed -n 's/^COMMAND_STR=.*om_diy_asset \([a-z_]*\) .*/\1/p' "$home_cmd")" + [ -z "$diy_category" ] && continue + + diyAssetSpec "$diy_category" + picked_cmd="" + [ -f "$diy_selection_path/$diy_category" ] && picked_cmd="$om_mod_sprites/$diy_category/commands/c0001_$(cat "$diy_selection_path/$diy_category")" + if [ -n "$picked_cmd" ] && [ -f "$picked_cmd" ]; then + home_preview="$(sed -n 's/^PREVIEW_IMAGE=//p' "$picked_cmd")" + else + diyStockPreview "$diy_category" + home_preview="$stock_preview" + [ -z "$home_preview" ] && home_preview="$(sed -n 's/^DEFAULT_PREVIEW_IMAGE=//p' "$home_cmd")" + fi + [ -z "$home_preview" ] && home_preview="$om_mod_imgs/preview_default.png" + + home_nearest="" + [ "$chars" = "1" ] && home_nearest='\nPREVIEW_NEAREST=TRUE' + + home_preview="$(printf '%s' "$home_preview" | sed 's/[&#\\]/\\&/g')" + sed -i -e '/^PREVIEW_NEAREST=/d' -e "s#^PREVIEW_IMAGE=.*#PREVIEW_IMAGE=$home_preview$home_nearest#" "$home_cmd" + done +} + diyThemeRemover() { disableCmds "diy" "del_diy" "preview" remove "$diy_selection_path" + diyHomePreviews [ -d "$diy_theme_path" ] && find "$diy_theme_path" -type f -not -name 'assets_here' -exec rm {} + @@ -910,7 +964,7 @@ themeTitle() { themePreview() { preview="$thm_preview/$1.png" [ ! -f "$preview" ] && preview="$theme_path/$1.png" - [ ! -f "$preview" ] && preview="$options_menu/images/default_preview.png" + [ ! -f "$preview" ] && preview="$options_menu/images/preview_default.png" echo "$preview" } diff --git a/mod/etc/options_menu/themes/settings/commands/_themeperfolder_1assign b/mod/etc/options_menu/themes/settings/commands/_themeperfolder_1assign index 1df1eb4..e57bd0d 100644 --- a/mod/etc/options_menu/themes/settings/commands/_themeperfolder_1assign +++ b/mod/etc/options_menu/themes/settings/commands/_themeperfolder_1assign @@ -3,4 +3,4 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE CHILD=TRUE COMMAND_STR=usleep 50000 && sh %script_dir%/om_folderthemes theme & -PREVIEW_IMAGE=/etc/options_menu/images/randtheme.png +PREVIEW_IMAGE=/etc/options_menu/images/preview_randtheme.png diff --git a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder index 2c12f8e..d4ec1b4 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder +++ b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder @@ -3,4 +3,4 @@ COMMAND_TYPE=EXTERNAL RESTART_UI=FALSE COMMAND_STR=sh %script_dir%/om_theme tpf STATE_STR=sh %script_dir%/om_state theme_per_folder -PREVIEW_IMAGE=/etc/options_menu/images/randtheme.png +PREVIEW_IMAGE=/etc/options_menu/images/preview_randtheme.png diff --git a/mod/etc/options_menu/themes/settings/commands/c0001_uninstaller b/mod/etc/options_menu/themes/settings/commands/c0001_uninstaller index 6067bf8..8d6f4d3 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0001_uninstaller +++ b/mod/etc/options_menu/themes/settings/commands/c0001_uninstaller @@ -2,4 +2,4 @@ COMMAND_NAME=RESET_SETTINGS COMMAND_TYPE=INTERNAL RESTART_UI=FALSE COMMAND_STR=usleep 50000 && sh %script_dir%/om_reset -PREVIEW_IMAGE=/etc/options_menu/images/uninstall.png +PREVIEW_IMAGE=/etc/options_menu/images/preview_uninstall.png diff --git a/mod/install b/mod/install index 2582b1b..5045dda 100644 --- a/mod/install +++ b/mod/install @@ -82,6 +82,11 @@ transfer_default # saves aren't mounted yet during install - om_theme_selector resets settings on first run om_firstrun="$rootfs/etc/options_menu/themes/om_firstrun" [ ! -f "$om_firstrun" ] && touch "$om_firstrun" +for legacy_img in back chip_apply chip_clear chip_diy chip_download chip_preview chip_refresh chip_settings clear colors_nes default_preview diy_preview luigi mario packed packed_nes pole randtheme reboot sbm settings uninstall update wall wall_far wall_near; do + del "$rootfs/etc/options_menu/images/$legacy_img.png" +done +# DIY asset entries regenerate on their next open, so an update's preview layout reaches them +find "$rootfs/etc/options_menu/themes/creator/sprites" -path '*/commands/c0001_*' -type f -exec rm {} + 2>/dev/null chmod +x "$rootfs/bin/sprite_crop" chmod +x "$rootfs/bin/theme_downloader" chmod +x "$rootfs/etc/options_menu/lib/theme_manager" diff --git a/mod/uninstall b/mod/uninstall index 726138f..08fb8a3 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -70,6 +70,13 @@ removeItem "$rootfs/etc/options_menu/images/audio.png" removeItem "$rootfs/etc/options_menu/images/audioall.png" removeItem "$rootfs/etc/options_menu/images/audiohome.png" removeItem "$rootfs/etc/options_menu/images/back.png" +removeItem "$rootfs/etc/options_menu/images/ui/chip_apply.png" +removeItem "$rootfs/etc/options_menu/images/ui/chip_clear.png" +removeItem "$rootfs/etc/options_menu/images/ui/chip_diy.png" +removeItem "$rootfs/etc/options_menu/images/ui/chip_download.png" +removeItem "$rootfs/etc/options_menu/images/ui/chip_preview.png" +removeItem "$rootfs/etc/options_menu/images/ui/chip_refresh.png" +removeItem "$rootfs/etc/options_menu/images/ui/chip_settings.png" removeItem "$rootfs/etc/options_menu/images/clear.png" removeItem "$rootfs/etc/options_menu/images/colors_nes.png" removeItem "$rootfs/etc/options_menu/images/default_preview.png" @@ -82,15 +89,32 @@ removeItem "$rootfs/etc/options_menu/images/mario.png" removeItem "$rootfs/etc/options_menu/images/packed.png" removeItem "$rootfs/etc/options_menu/images/packed_nes.png" removeItem "$rootfs/etc/options_menu/images/pole.png" +removeItem "$rootfs/etc/options_menu/images/preview_assign_music.png" removeItem "$rootfs/etc/options_menu/images/preview_audio_randomizer_all.png" removeItem "$rootfs/etc/options_menu/images/preview_audio_randomizer_home.png" removeItem "$rootfs/etc/options_menu/images/preview_clean_up.png" +removeItem "$rootfs/etc/options_menu/images/preview_colors_nes.png" +removeItem "$rootfs/etc/options_menu/images/preview_default.png" +removeItem "$rootfs/etc/options_menu/images/preview_diy.png" removeItem "$rootfs/etc/options_menu/images/preview_diy_theme.png" removeItem "$rootfs/etc/options_menu/images/preview_download_themes.png" +removeItem "$rootfs/etc/options_menu/images/preview_luigi.png" +removeItem "$rootfs/etc/options_menu/images/preview_mario.png" +removeItem "$rootfs/etc/options_menu/images/preview_music_per_folder.png" +removeItem "$rootfs/etc/options_menu/images/preview_packed.png" +removeItem "$rootfs/etc/options_menu/images/preview_packed_nes.png" +removeItem "$rootfs/etc/options_menu/images/preview_pole.png" +removeItem "$rootfs/etc/options_menu/images/preview_randtheme.png" +removeItem "$rootfs/etc/options_menu/images/preview_sbm.png" removeItem "$rootfs/etc/options_menu/images/preview_theme_options.png" removeItem "$rootfs/etc/options_menu/images/preview_theme_randomizer.png" +removeItem "$rootfs/etc/options_menu/images/preview_uninstall.png" removeItem "$rootfs/etc/options_menu/images/preview_update_theme_list.png" +removeItem "$rootfs/etc/options_menu/images/preview_wall.png" +removeItem "$rootfs/etc/options_menu/images/preview_wall_far.png" +removeItem "$rootfs/etc/options_menu/images/preview_wall_near.png" removeItem "$rootfs/etc/options_menu/images/randtheme.png" +removeItem "$rootfs/etc/options_menu/images/reboot.png" removeItem "$rootfs/etc/options_menu/images/retro_gaming.zip" removeItem "$rootfs/etc/options_menu/images/retroarch.png" removeItem "$rootfs/etc/options_menu/images/saving.png" @@ -102,6 +126,7 @@ removeItem "$rootfs/etc/options_menu/images/uninstall.png" removeItem "$rootfs/etc/options_menu/images/update.png" removeItem "$rootfs/etc/options_menu/images/wall.png" removeItem "$rootfs/etc/options_menu/images/wall_far.png" +removeItem "$rootfs/etc/options_menu/images/wall_near.png" removeItem "$rootfs/etc/usr/bin/pngpack-upx" removeItem "$rootfs/bin/sprite_crop" removeItem "$rootfs/bin/theme_downloader" diff --git a/src/command_loader.cpp b/src/command_loader.cpp index 52477ba..36992da 100644 --- a/src/command_loader.cpp +++ b/src/command_loader.cpp @@ -34,7 +34,7 @@ struct PipeCloser { void operator()(FILE * pipe) const { if(pipe) pclose(pipe); using PipeHandle = std::unique_ptr; // my own convention, not OptionsMenu's Command format -struct SelectorKeys { bool nesOnly = false; bool snesOnly = false; std::string author; std::string attentionPath; std::string attentionNotice; }; +struct SelectorKeys { bool nesOnly = false; bool snesOnly = false; std::string author; std::string attentionPath; std::string attentionNotice; bool previewAlignTop = false; }; SelectorKeys ReadSelectorKeys(const std::string & path) { @@ -52,6 +52,8 @@ SelectorKeys ReadSelectorKeys(const std::string & path) keys.nesOnly = true; else if(line == "SNES_ONLY=TRUE") keys.snesOnly = true; + else if(line == "PREVIEW_ALIGN_TOP=TRUE") + keys.previewAlignTop = true; else if(line.compare(0, authorKey.size(), authorKey) == 0) keys.author = line.substr(authorKey.size()); else if(line.compare(0, attentionPathKey.size(), attentionPathKey) == 0) @@ -204,7 +206,7 @@ bool LoadCommands(const std::string & commandLocation, const std::string & scrip } commands.push_back(c); const bool needsAttention = !keys.attentionPath.empty() && HasAnyFile(keys.attentionPath); - items.push_back({ file.compare(0, 6, "c0000_") != 0, std::move(keys.author), needsAttention, std::move(keys.attentionNotice) }); + items.push_back({ file.compare(0, 6, "c0000_") != 0, std::move(keys.author), needsAttention, std::move(keys.attentionNotice), keys.previewAlignTop }); } return true; } @@ -259,5 +261,5 @@ void AppendBackNavigation(const std::string & optionsLocation, const AppOptions back.previewImage = optionsLocation + "images/preview_placeholder.png"; back.command = backCommand; commands.push_back(back); - items.push_back({ false, "", false, "" }); + items.push_back({ false, "", false, "", false }); } diff --git a/src/command_loader.h b/src/command_loader.h index 7cd3b0d..4330834 100644 --- a/src/command_loader.h +++ b/src/command_loader.h @@ -45,6 +45,7 @@ struct ItemInfo std::string author; bool needsAttention; std::string attentionNotice; + bool previewAlignTop; // PREVIEW_ALIGN_TOP }; bool LoadCommands(const std::string & commandLocation, const std::string & scriptLocation, const std::string & optionsLocation, diff --git a/src/folder_themes_layout.cpp b/src/folder_themes_layout.cpp index b5172e7..24124ae 100644 --- a/src/folder_themes_layout.cpp +++ b/src/folder_themes_layout.cpp @@ -117,7 +117,7 @@ int ThemeManagerApp::RunFolderThemesLayout() loaded = 1; std::string path = theme >= 0 ? themes[theme].preview : ""; if(path.empty() || !std::ifstream(path).good()) - path = optionsLocation_ + (theme < 0 && model.GlobalThemeIsRandom() ? "images/randtheme.png" : "images/default_preview.png"); + path = optionsLocation_ + (theme < 0 && model.GlobalThemeIsRandom() ? "images/preview_randtheme.png" : "images/preview_default.png"); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); slot = Texture(path, renderer_, 0, 0); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); diff --git a/src/theme_manager_app.cpp b/src/theme_manager_app.cpp index f6f3a75..7e0ad89 100644 --- a/src/theme_manager_app.cpp +++ b/src/theme_manager_app.cpp @@ -417,10 +417,10 @@ int ThemeManagerApp::RunGridLayout() tileHideLabel[idx] = c.previewHideLabel; tileFitContain[idx] = c.previewFitContain; - // _run01.png with sibling _run02/_run03/... is this project's naming - // convention for a multi-frame run-cycle animation - const std::string runSuffix = "_run01.png"; - bool isRunCycle = c.previewImage.size() > runSuffix.size() && + // a sprite preview named *01.png with sibling *02/*03/... is a multi-frame + // animation (mario_run01.png, pole01.png) + const std::string runSuffix = "01.png"; + bool isRunCycle = c.previewNearest && c.previewImage.size() > runSuffix.size() && c.previewImage.compare(c.previewImage.size() - runSuffix.size(), runSuffix.size(), runSuffix) == 0; if(tileFitContain[idx]) @@ -431,7 +431,7 @@ int ThemeManagerApp::RunGridLayout() if(isRunCycle) { - std::string base = c.previewImage.substr(0, c.previewImage.size() - runSuffix.size()) + "_run"; + std::string base = c.previewImage.substr(0, c.previewImage.size() - runSuffix.size()); std::vector frames{ art }; for(int n = 2; n <= 8; ++n) { @@ -653,7 +653,11 @@ int ThemeManagerApp::RunGridLayout() // 10px margin keeps clear of the rounded-corner mask below FitCentered(tileArt, x, y, TileW, TileH, 10); else + { FitCover(tileArt, x, y, TileW, TileH, 0); + if(items_[idx].previewAlignTop) + tileArt.rect.y = y; + } tileArt.Draw(renderer_); } if(author.rect.w > 0) From 452601e55d2b0383139ad7072cff48e15d2b2f71 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 8 Oct 2026 12:11:15 +0000 Subject: [PATCH 137/210] Show the Options Menu loading bar while themes and DIY assets load loadingSplash calls gen_splash --loading, which uses the shared LOADING string, so THEME_LOADING leaves the language files. Covers the installed theme list rebuild, theme unpacking, DIY asset scans and System Clean Up. Requires Options Menu v2.0.0-alpha8. --- mod/etc/options_menu/themes/lang/de-DE.lang | 1 - mod/etc/options_menu/themes/lang/en-US.lang | 1 - mod/etc/options_menu/themes/lang/es-ES.lang | 1 - mod/etc/options_menu/themes/lang/fr-FR.lang | 1 - mod/etc/options_menu/themes/lang/it-IT.lang | 1 - mod/etc/options_menu/themes/lang/ja-JP.lang | 1 - mod/etc/options_menu/themes/lang/nl-NL.lang | 1 - mod/etc/options_menu/themes/lang/pt-PT.lang | 1 - mod/etc/options_menu/themes/lang/ru-RU.lang | 1 - mod/etc/options_menu/themes/scripts/om_vars | 16 ++++++++++------ .../themes/settings/scripts/om_systemcleanup | 2 +- mod/install | 1 - src/command_loader.h | 2 +- src/theme_manager_app.cpp | 2 -- 14 files changed, 12 insertions(+), 20 deletions(-) diff --git a/mod/etc/options_menu/themes/lang/de-DE.lang b/mod/etc/options_menu/themes/lang/de-DE.lang index f0cb0c7..e2edb2b 100644 --- a/mod/etc/options_menu/themes/lang/de-DE.lang +++ b/mod/etc/options_menu/themes/lang/de-DE.lang @@ -74,7 +74,6 @@ THEME_LIST_DOWNLOADING_LINE1=Themenliste wird heruntergeladen THEME_LIST_DOWNLOADING_LINE2=Bitte warten . . . THEME_MOVING_LINE1=Themen werden auf USB/SD verschoben THEME_MOVING_LINE2=Bitte warten . . . -THEME_LOADING=Wird geladen... THEME_LOADING_ASSETS=Assets werden geladen... THEME_SAVING=Eigenes Theme wird gespeichert... THEME_PREVIEW_LOADING=Themenvorschau wird geladen... diff --git a/mod/etc/options_menu/themes/lang/en-US.lang b/mod/etc/options_menu/themes/lang/en-US.lang index 4cea61e..82df588 100644 --- a/mod/etc/options_menu/themes/lang/en-US.lang +++ b/mod/etc/options_menu/themes/lang/en-US.lang @@ -74,7 +74,6 @@ THEME_LIST_DOWNLOADING_LINE1=Downloading Theme List THEME_LIST_DOWNLOADING_LINE2=Please Wait . . . THEME_MOVING_LINE1=Moving Themes to USB/SD THEME_MOVING_LINE2=Please Wait . . . -THEME_LOADING=Loading... THEME_LOADING_ASSETS=Loading assets... THEME_SAVING=Saving custom theme... THEME_PREVIEW_LOADING=Loading theme preview... diff --git a/mod/etc/options_menu/themes/lang/es-ES.lang b/mod/etc/options_menu/themes/lang/es-ES.lang index 75c2130..0c6f0e2 100644 --- a/mod/etc/options_menu/themes/lang/es-ES.lang +++ b/mod/etc/options_menu/themes/lang/es-ES.lang @@ -74,7 +74,6 @@ THEME_LIST_DOWNLOADING_LINE1=Descargando Lista de Temas THEME_LIST_DOWNLOADING_LINE2=Espere Por Favor . . . THEME_MOVING_LINE1=Moviendo Temas a USB/SD THEME_MOVING_LINE2=Espere Por Favor . . . -THEME_LOADING=Cargando... THEME_LOADING_ASSETS=Cargando recursos... THEME_SAVING=Guardando tema personalizado... THEME_PREVIEW_LOADING=Cargando vista previa del tema... diff --git a/mod/etc/options_menu/themes/lang/fr-FR.lang b/mod/etc/options_menu/themes/lang/fr-FR.lang index 2fac17d..00bad08 100644 --- a/mod/etc/options_menu/themes/lang/fr-FR.lang +++ b/mod/etc/options_menu/themes/lang/fr-FR.lang @@ -74,7 +74,6 @@ THEME_LIST_DOWNLOADING_LINE1=Téléchargement de la Liste des Thèmes THEME_LIST_DOWNLOADING_LINE2=Veuillez Patienter . . . THEME_MOVING_LINE1=Déplacement des Thèmes vers USB/SD THEME_MOVING_LINE2=Veuillez Patienter . . . -THEME_LOADING=Chargement... THEME_LOADING_ASSETS=Chargement des ressources... THEME_SAVING=Enregistrement du thème personnalisé... THEME_PREVIEW_LOADING=Chargement de l'aperçu du thème... diff --git a/mod/etc/options_menu/themes/lang/it-IT.lang b/mod/etc/options_menu/themes/lang/it-IT.lang index f82222e..5991405 100644 --- a/mod/etc/options_menu/themes/lang/it-IT.lang +++ b/mod/etc/options_menu/themes/lang/it-IT.lang @@ -74,7 +74,6 @@ THEME_LIST_DOWNLOADING_LINE1=Download Elenco Temi in Corso THEME_LIST_DOWNLOADING_LINE2=Attendere Prego . . . THEME_MOVING_LINE1=Spostamento Temi su USB/SD THEME_MOVING_LINE2=Attendere Prego . . . -THEME_LOADING=Caricamento... THEME_LOADING_ASSETS=Caricamento risorse... THEME_SAVING=Salvataggio tema personalizzato... THEME_PREVIEW_LOADING=Caricamento anteprima tema... diff --git a/mod/etc/options_menu/themes/lang/ja-JP.lang b/mod/etc/options_menu/themes/lang/ja-JP.lang index cc837a0..3c483f1 100644 --- a/mod/etc/options_menu/themes/lang/ja-JP.lang +++ b/mod/etc/options_menu/themes/lang/ja-JP.lang @@ -74,7 +74,6 @@ THEME_LIST_DOWNLOADING_LINE1=Themeいちらんを download しています THEME_LIST_DOWNLOADING_LINE2=おまちください . . . THEME_MOVING_LINE1=ThemeをUSB/SDにいどうしています THEME_MOVING_LINE2=おまちください . . . -THEME_LOADING=よみこみちゅう... THEME_LOADING_ASSETS=Assetをよみこみちゅう... THEME_SAVING=じぶんのThemeをほぞんちゅう... THEME_PREVIEW_LOADING=Themeのpreviewをよみこみちゅう... diff --git a/mod/etc/options_menu/themes/lang/nl-NL.lang b/mod/etc/options_menu/themes/lang/nl-NL.lang index 07e314c..98561c7 100644 --- a/mod/etc/options_menu/themes/lang/nl-NL.lang +++ b/mod/etc/options_menu/themes/lang/nl-NL.lang @@ -74,7 +74,6 @@ THEME_LIST_DOWNLOADING_LINE1=Themalijst Wordt Gedownload THEME_LIST_DOWNLOADING_LINE2=Even Geduld . . . THEME_MOVING_LINE1=Thema's naar USB/SD verplaatsen THEME_MOVING_LINE2=Even Geduld . . . -THEME_LOADING=Laden... THEME_LOADING_ASSETS=Assets worden geladen... THEME_SAVING=Eigen thema wordt opgeslagen... THEME_PREVIEW_LOADING=Themavoorbeeld wordt geladen... diff --git a/mod/etc/options_menu/themes/lang/pt-PT.lang b/mod/etc/options_menu/themes/lang/pt-PT.lang index b400557..bf8ea28 100644 --- a/mod/etc/options_menu/themes/lang/pt-PT.lang +++ b/mod/etc/options_menu/themes/lang/pt-PT.lang @@ -74,7 +74,6 @@ THEME_LIST_DOWNLOADING_LINE1=A Descarregar Lista de Temas THEME_LIST_DOWNLOADING_LINE2=Aguarde Por Favor . . . THEME_MOVING_LINE1=A Mover Temas para USB/SD THEME_MOVING_LINE2=Aguarde Por Favor . . . -THEME_LOADING=A carregar... THEME_LOADING_ASSETS=A carregar recursos... THEME_SAVING=A guardar tema personalizado... THEME_PREVIEW_LOADING=A carregar pré-visualização do tema... diff --git a/mod/etc/options_menu/themes/lang/ru-RU.lang b/mod/etc/options_menu/themes/lang/ru-RU.lang index a8bcc9f..6f89447 100644 --- a/mod/etc/options_menu/themes/lang/ru-RU.lang +++ b/mod/etc/options_menu/themes/lang/ru-RU.lang @@ -74,7 +74,6 @@ THEME_LIST_DOWNLOADING_LINE1=Загрузка Списка Тем THEME_LIST_DOWNLOADING_LINE2=Пожалуйста, подождите . . . THEME_MOVING_LINE1=Перемещение тем на USB/SD THEME_MOVING_LINE2=Пожалуйста, подождите . . . -THEME_LOADING=Загрузка... THEME_LOADING_ASSETS=Загрузка ресурсов... THEME_SAVING=Сохранение своей темы... THEME_PREVIEW_LOADING=Загрузка предпросмотра темы... diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index ad2888c..a2ebe17 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -205,6 +205,12 @@ themeIsBindable() { } +loadingSplash() { + [ -n "$loading_splash" ] && return + loading_splash=1 + "$om_scripts/gen_splash" --loading 2> /dev/null +} + migrateThemes() { [ "$theme_path" = "$nand_theme_path" ] && return 1 [ -n "$(ls -A "$nand_theme_path" 2> /dev/null)" ] || return 1 @@ -230,7 +236,7 @@ migrateThemes() { hasThemesToInstall() { [ "$has_theme" = 0 ] && return - "$om_scripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png > /dev/fb0 + loadingSplash for f in "$theme_path/$system_name".*.tar.gz; do [ -f "$f" ] || continue tar -xzf "$f" -C "$theme_path" && rm -f "$f" @@ -294,7 +300,7 @@ themeCountChanged() { } themeLoader() { - "$om_scripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png > /dev/fb0 + loadingSplash removeRegionalTheme cd "$theme_path" && find . -mindepth 1 -maxdepth 1 -type d | sed 's#^\./##' | sort -u | while IFS= read -r thm; do full_path="$theme_path/$thm" @@ -646,7 +652,7 @@ diyCropFrame() { } findDiyAsset() { - "$om_scripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png > /dev/fb0 + loadingSplash diyAssetSpec "$1" diyCropFrame "$1" @@ -784,11 +790,10 @@ diyThemeChecker() { [ "$1" = "loader" ] && usleep 1000 } -# the console's own stock asset, read from the squashfs so an applied theme can't shadow it diyStockPreview() { stock_preview="" [ "$colors" = "1" ] && return - [ "$sftype" = "hvcj" ] && return # its packed.png atlas doesn't match om_packed.sprites.json + [ "$sftype" = "hvcj" ] && return stock_file="$squashfs/usr/share/$original_theme_ui0/resources/$asset_path/$diy_file" [ -f "$stock_file" ] || return @@ -802,7 +807,6 @@ diyStockPreview() { stock_preview="$stock_crop" } -# each DIY home entry previews the asset picked for it, else the console's stock one diyHomePreviews() { for home_cmd in "$om_mod_gen_cmds"/*; do diy_category="$(sed -n 's/^COMMAND_STR=.*om_diy_asset \([a-z_]*\) .*/\1/p' "$home_cmd")" diff --git a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup index e6c2d67..85656c0 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup +++ b/mod/etc/options_menu/themes/settings/scripts/om_systemcleanup @@ -19,7 +19,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init -"$om_scripts/gen_splash" /tmp/om_splash.png THEME_LOADING && decodepng /tmp/om_splash.png >/dev/fb0 +loadingSplash modCleanUp diff --git a/mod/install b/mod/install index 5045dda..f0163fa 100644 --- a/mod/install +++ b/mod/install @@ -85,7 +85,6 @@ om_firstrun="$rootfs/etc/options_menu/themes/om_firstrun" for legacy_img in back chip_apply chip_clear chip_diy chip_download chip_preview chip_refresh chip_settings clear colors_nes default_preview diy_preview luigi mario packed packed_nes pole randtheme reboot sbm settings uninstall update wall wall_far wall_near; do del "$rootfs/etc/options_menu/images/$legacy_img.png" done -# DIY asset entries regenerate on their next open, so an update's preview layout reaches them find "$rootfs/etc/options_menu/themes/creator/sprites" -path '*/commands/c0001_*' -type f -exec rm {} + 2>/dev/null chmod +x "$rootfs/bin/sprite_crop" chmod +x "$rootfs/bin/theme_downloader" diff --git a/src/command_loader.h b/src/command_loader.h index 4330834..402f642 100644 --- a/src/command_loader.h +++ b/src/command_loader.h @@ -45,7 +45,7 @@ struct ItemInfo std::string author; bool needsAttention; std::string attentionNotice; - bool previewAlignTop; // PREVIEW_ALIGN_TOP + bool previewAlignTop; }; bool LoadCommands(const std::string & commandLocation, const std::string & scriptLocation, const std::string & optionsLocation, diff --git a/src/theme_manager_app.cpp b/src/theme_manager_app.cpp index 7e0ad89..a8b2b50 100644 --- a/src/theme_manager_app.cpp +++ b/src/theme_manager_app.cpp @@ -417,8 +417,6 @@ int ThemeManagerApp::RunGridLayout() tileHideLabel[idx] = c.previewHideLabel; tileFitContain[idx] = c.previewFitContain; - // a sprite preview named *01.png with sibling *02/*03/... is a multi-frame - // animation (mario_run01.png, pole01.png) const std::string runSuffix = "01.png"; bool isRunCycle = c.previewNearest && c.previewImage.size() > runSuffix.size() && c.previewImage.compare(c.previewImage.size() - runSuffix.size(), runSuffix.size(), runSuffix) == 0; From 5d47e91905c34b04077ea8cb258dfe1e70176604 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 8 Oct 2026 12:14:42 +0000 Subject: [PATCH 138/210] Update OptionsMenu submodule to v2.0.0-alpha8 --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index f7fc180..a07a5fd 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit f7fc180af603463dab5d83c3257cb1ee14d8ce64 +Subproject commit a07a5fd588a49b5cf02b3cb4587f69155711f889 From 0feae0748f0e337d51544f3e2bc8d39c603b223c Mon Sep 17 00:00:00 2001 From: DefKorns Date: Thu, 8 Oct 2026 22:27:51 +0000 Subject: [PATCH 139/210] Update OptionsMenu submodule to v2.0.0-alpha9 --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index a07a5fd..dec109c 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit a07a5fd588a49b5cf02b3cb4587f69155711f889 +Subproject commit dec109ce7f5caec792b282404a43faec251eeba0 From d24d9a9a4ffff6ac23c255fdf5440c6934de630b Mon Sep 17 00:00:00 2001 From: DefKorns Date: Mon, 12 Oct 2026 08:37:16 +0000 Subject: [PATCH 140/210] Link sprite_crop with the Options Menu lib rpath RetroArch 1.8.4 SC replaces the system libstdc++, so sprite_crop crashed with "Illegal instruction" and the DIY Base UI previews stayed empty. The rpath lets it use the private libstdc++ that options_menu_compat installs, as theme_manager already does. --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 9c74cb8..9f6419b 100644 --- a/Makefile +++ b/Makefile @@ -66,10 +66,11 @@ mod/bin/theme_downloader: src/theme_downloader.o src/ca_bundle.o upx --best $@ # DIY sprite previews (om_vars). Built by pngpack's own Makefile; its default -# LDFLAGS (-dead_strip) is macOS-only. +# LDFLAGS (-dead_strip) is macOS-only. The rpath picks up options_menu_compat's +# private libstdc++ when another hmod has replaced the system one. mod/bin/sprite_crop: pngpack/pngpacker/sprite_crop.cpp pngpack/pngpacker/pixeltools.cpp mkdir -p $(@D) - $(MAKE) -C pngpack build/sprite_crop CROSS=$(CROSS_PREFIX) LDFLAGS=-Wl,--gc-sections + $(MAKE) -C pngpack build/sprite_crop CROSS=$(CROSS_PREFIX) LDFLAGS='-Wl,--gc-sections -Wl,-rpath,/etc/options_menu/lib' cp pngpack/build/sprite_crop $@ update-headers: From ee0e11ab26b67286339ed351f96a7f6418bf805c Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 13 Oct 2026 08:53:52 +0000 Subject: [PATCH 141/210] Address PR #14 review: lock, downloader and preview cache fixes - Only take over a stale lock when the old pid is still a theme_manager, so a reused pid never gets killed - Exit the SIGTERM handler with unlink() + _exit() instead of std::exit(), which is not async-signal-safe - Fall back to stat() when readdir() reports DT_UNKNOWN - URL-escape theme names in theme_downloader - Stop waiting for a tarball to be removed once the unpacking shell is gone, instead of hanging as an orphan - Cap the folder-themes preview cache with LRU eviction - Drop leftover CHECKPOINT debug prints --- src/command_loader.cpp | 17 +++++++++++++++-- src/folder_themes_layout.cpp | 18 ++++++++++++++++++ src/single_instance_lock.cpp | 22 ++++++++++++++++++++-- src/single_instance_lock.h | 5 +---- src/theme_downloader.c | 15 ++++++++++----- src/theme_manager_app.cpp | 4 ---- 6 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/command_loader.cpp b/src/command_loader.cpp index 36992da..5fc8f39 100644 --- a/src/command_loader.cpp +++ b/src/command_loader.cpp @@ -17,6 +17,7 @@ #include #include #include +#include namespace { @@ -64,6 +65,17 @@ SelectorKeys ReadSelectorKeys(const std::string & path) return keys; } +// some filesystems leave d_type as DT_UNKNOWN +unsigned char EntryType(const std::string & dirPath, const dirent * entry) +{ + if(entry->d_type != DT_UNKNOWN) + return entry->d_type; + struct stat st; + if(stat((dirPath + "/" + entry->d_name).c_str(), &st) != 0) + return DT_UNKNOWN; + return S_ISREG(st.st_mode) ? DT_REG : S_ISDIR(st.st_mode) ? DT_DIR : DT_UNKNOWN; +} + bool HasAnyFile(const std::string & dirPath) { DirHandle dir(opendir(dirPath.c_str())); @@ -74,7 +86,8 @@ bool HasAnyFile(const std::string & dirPath) const std::string name = entry->d_name; if(name == "." || name == "..") continue; - if(entry->d_type == DT_REG || (entry->d_type == DT_DIR && HasAnyFile(dirPath + "/" + name))) + const unsigned char type = EntryType(dirPath, entry); + if(type == DT_REG || (type == DT_DIR && HasAnyFile(dirPath + "/" + name))) return true; } return false; @@ -159,7 +172,7 @@ bool LoadCommands(const std::string & commandLocation, const std::string & scrip std::list fileList; while(auto entry = readdir(dir.get())) - if(entry->d_type == DT_REG && entry->d_name[0] == 'c') + if(entry->d_name[0] == 'c' && EntryType(commandLocation, entry) == DT_REG) fileList.push_back(entry->d_name); fileList.sort(); diff --git a/src/folder_themes_layout.cpp b/src/folder_themes_layout.cpp index 24124ae..3bfd7af 100644 --- a/src/folder_themes_layout.cpp +++ b/src/folder_themes_layout.cpp @@ -106,15 +106,33 @@ int ThemeManagerApp::RunFolderThemesLayout() const std::vector & themes = model.Options(Channel::Theme); std::vector previews(themes.size()); std::vector previewLoaded(themes.size(), 0); // not vector: Preview() needs a real reference + std::vector previewUsed(themes.size(), 0); + unsigned previewClock = 0; + int previewCount = 0; + const int maxPreviews = pickerRows + 4; // whole picker page + detail, so a redraw never evicts what it draws Texture fallbackPreview; char fallbackLoaded = 0; auto Preview = [&](int theme) -> Texture & { char & loaded = theme >= 0 ? previewLoaded[theme] : fallbackLoaded; Texture & slot = theme >= 0 ? previews[theme] : fallbackPreview; + if(theme >= 0) + previewUsed[theme] = ++previewClock; + if(!loaded && theme >= 0 && previewCount >= maxPreviews) + { + int oldest = -1; + for(int i = 0; i < static_cast(previews.size()); ++i) + if(previewLoaded[i] && i != theme && (oldest < 0 || previewUsed[i] < previewUsed[oldest])) + oldest = i; + previews[oldest] = Texture(); + previewLoaded[oldest] = 0; + --previewCount; + } if(!loaded) { loaded = 1; + if(theme >= 0) + ++previewCount; std::string path = theme >= 0 ? themes[theme].preview : ""; if(path.empty() || !std::ifstream(path).good()) path = optionsLocation_ + (theme < 0 && model.GlobalThemeIsRandom() ? "images/preview_randtheme.png" : "images/preview_default.png"); diff --git a/src/single_instance_lock.cpp b/src/single_instance_lock.cpp index 1b767b7..687e98c 100644 --- a/src/single_instance_lock.cpp +++ b/src/single_instance_lock.cpp @@ -11,10 +11,21 @@ #include #include -#include #include +#include #include +namespace { + +std::string ProcessName(const std::string & pid) +{ + std::string name; + std::getline(std::ifstream("/proc/" + pid + "/comm"), name); + return name; +} + +} + void SingleInstanceLock::Acquire() { static SingleInstanceLock instance; @@ -39,6 +50,9 @@ void SingleInstanceLock::TakeOverStaleInstance() in.close(); if(oldPid <= 0 || oldPid == getpid() || kill(oldPid, 0) != 0) return; + // the pid may have been reused after a crash left the lock behind + if(ProcessName(std::to_string(oldPid)) != ProcessName("self")) + return; kill(oldPid, SIGTERM); for(int i = 0; i < 40 && kill(oldPid, 0) == 0; ++i) usleep(50000); @@ -46,4 +60,8 @@ void SingleInstanceLock::TakeOverStaleInstance() kill(oldPid, SIGKILL); } -void SingleInstanceLock::HandleSigTerm(int) { std::exit(0); } +void SingleInstanceLock::HandleSigTerm(int) +{ + unlink(LockPath); + _exit(0); +} diff --git a/src/single_instance_lock.h b/src/single_instance_lock.h index a3a173e..b6d8e98 100644 --- a/src/single_instance_lock.h +++ b/src/single_instance_lock.h @@ -17,9 +17,6 @@ class SingleInstanceLock SingleInstanceLock(const SingleInstanceLock &) = delete; SingleInstanceLock & operator=(const SingleInstanceLock &) = delete; - // The lock lives in a function-local static (not a stack local) so its - // destructor still runs from HandleSigTerm's std::exit() - std::exit() - // only unwinds static-storage objects, not automatic ones. static void Acquire(); private: @@ -29,7 +26,7 @@ class SingleInstanceLock ~SingleInstanceLock(); static void TakeOverStaleInstance(); - static void HandleSigTerm(int); // runs ~SingleInstanceLock() during cleanup + static void HandleSigTerm(int); }; #endif diff --git a/src/theme_downloader.c b/src/theme_downloader.c index d7a45b4..184826d 100644 --- a/src/theme_downloader.c +++ b/src/theme_downloader.c @@ -105,9 +105,12 @@ static enum fetch_result fetch_theme(CURL *c, const char *sftype, const char *th char system_name[32]; char gh_url[512]; char fallback_url[512]; + char *escaped = curl_easy_escape(c, theme_name, 0); + if (!escaped) return FETCH_FAILED; to_upper(system_name, sftype, sizeof(system_name)); - snprintf(gh_url, sizeof(gh_url), "%s/%s.%s.tar.gz", GITHUB_BASE, system_name, theme_name); - snprintf(fallback_url, sizeof(fallback_url), "%s%s.%s.tar.gz", CLASSICMODS_THEMES, system_name, theme_name); + snprintf(gh_url, sizeof(gh_url), "%s/%s.%s.tar.gz", GITHUB_BASE, system_name, escaped); + snprintf(fallback_url, sizeof(fallback_url), "%s%s.%s.tar.gz", CLASSICMODS_THEMES, system_name, escaped); + curl_free(escaped); return fetch_with_fallback(c, gh_url, fallback_url, out_path); } @@ -116,8 +119,9 @@ static enum exit_code result_code(enum fetch_result r) { return r == FETCH_OK ? EXIT_OK : EXIT_FAILED; } -static void wait_until_removed(const char *path) { - while (path[0] && access(path, F_OK) == 0) usleep(REMOVAL_POLL_US); +/* Stop waiting if the unpacking shell is gone. */ +static void wait_until_removed(const char *path, pid_t unpacker) { + while (path[0] && access(path, F_OK) == 0 && getppid() == unpacker) usleep(REMOVAL_POLL_US); } static enum exit_code fetch_theme_list(CURL *c, const char *sftype, const char *list_path) { @@ -129,10 +133,11 @@ static enum exit_code fetch_theme_list(CURL *c, const char *sftype, const char * char out_path[TARBALLS_ON_DISK][512] = {{0}}; int slot = 0; enum exit_code code = EXIT_OK; + const pid_t unpacker = getppid(); while (fgets(name, sizeof(name), list)) { name[strcspn(name, "\r\n")] = '\0'; if (!name[0]) continue; - wait_until_removed(out_path[slot]); + wait_until_removed(out_path[slot], unpacker); snprintf(out_path[slot], sizeof(out_path[slot]), "%s.%s.tar.gz", system_name, name); code = result_code(fetch_theme(c, sftype, name, out_path[slot])); if (code != EXIT_OK) out_path[slot][0] = '\0'; diff --git a/src/theme_manager_app.cpp b/src/theme_manager_app.cpp index a8b2b50..b9dd065 100644 --- a/src/theme_manager_app.cpp +++ b/src/theme_manager_app.cpp @@ -52,14 +52,11 @@ ThemeManagerApp::ThemeManagerApp(std::string optionsLocation, AppOptions options , items_(std::move(items)) { LoadActiveThemeBorderColor(optionsLocation_); - fprintf(stderr, "CHECKPOINT 1: commands loaded, count=%zu\n", commands_.size()); fflush(stderr); // std::make_unique is C++14; this project builds as C++11 sdlContext_.reset(new SDL_Context(std::chrono::milliseconds(33), false)); - fprintf(stderr, "CHECKPOINT 2: sdl_context created\n"); fflush(stderr); renderer_ = sdlContext_->renderer; controller_.reset(new Controller(1)); - fprintf(stderr, "CHECKPOINT 3: controller created\n"); fflush(stderr); bg_ = UiTheme::Bg; SetDrawColor(renderer_, bg_); @@ -74,7 +71,6 @@ ThemeManagerApp::ThemeManagerApp(std::string optionsLocation, AppOptions options appVersionText_.rect.y -= appVersionText_.rect.h / 2; titleText_ = Texture(Translate(options_.titleKey), UiTheme::SectionTitleFontSize, renderer_, UiTheme::SectionTitleX, UiTheme::SectionTitleY, false, ToAbgr(UiTheme::Text), true); creditText_ = Texture("Theme Manager - by DefKorns", 16, renderer_, UiTheme::CreditX, UiTheme::CreditY, false, ToAbgr(UiTheme::Text), true); - fprintf(stderr, "CHECKPOINT 4: basic textures created\n"); fflush(stderr); badgeA_ = Badge{ Texture("A", 16, renderer_, 0, 0, false, ToAbgr(UiTheme::BadgeLetter), true), Texture(Translate("HINT_SELECT"), 16, renderer_, 0, 0, false, ToAbgr(UiTheme::Text), true), UiTheme::BadgeADark, UiTheme::BadgeA }; badgeRowRightEdge_ = UiTheme::BadgeClusterRightX - UiTheme::BadgeOuterSize - UiTheme::BadgeLabelGap - badgeA_.label.rect.w - UiTheme::BadgeGroupGap; From 0e2bc366b728ec58a8aca4b8dc35b981051b9ce3 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 13 Oct 2026 09:53:18 +0000 Subject: [PATCH 142/210] Align Base UI DIY previews to the top Generated packed/packed_nes asset commands now carry PREVIEW_ALIGN_TOP, like the Base UI tile on the DIY home. Commands generated by earlier versions get the key appended instead of being regenerated. --- mod/etc/options_menu/themes/scripts/om_vars | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index a2ebe17..8175049 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -580,6 +580,7 @@ diyAssetSpec() { colors="0" preview_square="0" preview_cols="" + preview_top="0" json="" case "$1" in @@ -605,6 +606,7 @@ diyAssetSpec() { asset_path="sprites" preview_square="1" preview_cols="4" # full UI screenshots - too detailed to shrink to 7 cols + preview_top="1" ;; packed_nes) diy_file="${sftype}.png" @@ -613,6 +615,7 @@ diyAssetSpec() { asset="$diy_file" preview_square="1" preview_cols="4" # full UI screenshots - too detailed to shrink to 7 cols + preview_top="1" ;; colors) asset_path="scripts/env" @@ -665,6 +668,11 @@ findDiyAsset() { # read once - avoids a [ -f ] stat() per theme below, slow on USB/SD existing_cmds="$(ls -1 "$diy_sprite/commands" 2>/dev/null)" existing_scripts="$(ls -1 "$diy_sprite/scripts" 2>/dev/null)" + if [ "$preview_top" = "1" ] && [ -n "$existing_cmds" ]; then + grep -Lx "PREVIEW_ALIGN_TOP=TRUE" "$diy_sprite/commands"/c0001_* 2>/dev/null | while IFS= read -r old_cmd; do + echo "PREVIEW_ALIGN_TOP=TRUE" >> "$old_cmd" + done + fi diy_crop_batch=0 diy_seen_hashes="" # collapses themes that reuse a byte-identical source asset under a different name @@ -738,6 +746,9 @@ PREVIEW_SQUARE=TRUE" preview_cols_str="" [ -n "$preview_cols" ] && preview_cols_str=" PREVIEW_GRID_COLS=$preview_cols" + preview_top_str="" + [ "$preview_top" = "1" ] && preview_top_str=" +PREVIEW_ALIGN_TOP=TRUE" preview_fit_str="" [ "$chars" = "1" ] && preview_fit_str=" PREVIEW_FIT_CONTAIN=TRUE @@ -745,7 +756,7 @@ PREVIEW_HIDE_LABEL=TRUE" echo "COMMAND_NAME=$cmd_name COMMAND_TYPE=INTERNAL COMMAND_STR=sh $sprite_script.sh -PREVIEW_IMAGE=$preview_image${preview_nearest}${preview_square_str}${preview_cols_str}${preview_fit_str}" > "$cmd_file" +PREVIEW_IMAGE=$preview_image${preview_nearest}${preview_square_str}${preview_cols_str}${preview_top_str}${preview_fit_str}" > "$cmd_file" copy "$om_mod_license" "$sprite_script.sh" echo "diySpriteSelector \"$full_asset_path\" \"$asset_path\" \"$asset\"" >> "$sprite_script.sh" From 6e102bb5141ba945ecb0d3d2340012294f5585eb Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 13 Oct 2026 13:18:13 +0000 Subject: [PATCH 143/210] Update hmod-build: changelog in pre-release readmes --- hmod-build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmod-build b/hmod-build index 9f553b5..14408f7 160000 --- a/hmod-build +++ b/hmod-build @@ -1 +1 @@ -Subproject commit 9f553b5718ca671cf7bf59f9c714c3e07042ee4d +Subproject commit 14408f7b6e4e3aa1488e0d50152ffc9fe1e4e966 From dc0eb4ea28c8355ef0103fd42343ea03194c418e Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 13 Oct 2026 14:24:58 +0000 Subject: [PATCH 144/210] Pin hmod-build workflow and clean vendor objects after build --- .github/workflows/build.yml | 24 ++++++++++++------------ build.sh | 2 ++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fc142dc..9dc8a45 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,17 +1,17 @@ name: Build on: - push: - branches: ['**'] - tags: ['v*'] - pull_request: + push: + branches: ["**"] + tags: ["v*"] + pull_request: jobs: - hmod: - uses: DefKorns/hmod-build/.github/workflows/hmod.yml@master - with: - release-name: Theme Selector - docker: true - secrets: inherit - permissions: - contents: write + hmod: + uses: DefKorns/hmod-build/.github/workflows/hmod.yml@14408f7b6e4e3aa1488e0d50152ffc9fe1e4e966 + with: + release-name: Theme Selector + docker: true + secrets: inherit + permissions: + contents: write diff --git a/build.sh b/build.sh index 2e9a323..807a41d 100644 --- a/build.sh +++ b/build.sh @@ -24,5 +24,7 @@ fi echo "==> Running make inside the container..." docker run --rm -v "$PWD:/src" "$IMAGE" make CROSS_PREFIX=arm-linux-gnueabihf- "$@" +find vendor/OptionsMenu -name "*.o" -type f -delete + echo "==> Build complete:" ls -la mod/etc/options_menu/lib/theme_manager mod/bin/theme_downloader out/*.hmod 2>/dev/null From 26a24d63079da75b2f8c76354ae5f747291954d3 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 13 Oct 2026 14:25:56 +0000 Subject: [PATCH 145/210] Use theme-provided Base UI previews from extras/ --- mod/etc/options_menu/themes/scripts/om_themescript | 2 +- mod/etc/options_menu/themes/scripts/om_vars | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/mod/etc/options_menu/themes/scripts/om_themescript b/mod/etc/options_menu/themes/scripts/om_themescript index 34ce457..413ff93 100644 --- a/mod/etc/options_menu/themes/scripts/om_themescript +++ b/mod/etc/options_menu/themes/scripts/om_themescript @@ -247,7 +247,7 @@ themeSelector() { local skip_bgm="none" [ -n "$assigned_music" ] && skip_bgm="bgm_*.wav" - find "$full_theme_path" -type f ! -path "$full_theme_path/fonts/*" ! -name "$skip_bgm" ! -name info ! -name bind_ok ! -name needs_overlay 2>/dev/null | while IFS= read -r theme_file; do + find "$full_theme_path" -type f ! -path "$full_theme_path/fonts/*" ! -path "$full_theme_path/extras/*" ! -name "$skip_bgm" ! -name info ! -name bind_ok ! -name needs_overlay 2>/dev/null | while IFS= read -r theme_file; do mount -o bind "$theme_file" "$original_theme/${theme_file#"$full_theme_path"/}" done fi diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 8175049..56a54d7 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -198,7 +198,7 @@ esac original_theme_path="$mountpoint/usr/share/$original_theme_ui0" themeIsBindable() { - find "$1" -type f ! -path "$1/fonts/*" ! -path "$1/info" ! -path "$1/dependencies.json" \ + find "$1" -type f ! -path "$1/fonts/*" ! -path "$1/extras/*" ! -path "$1/info" ! -path "$1/dependencies.json" \ ! -path "$1/resources/scripts/env/env_*.lua" | while IFS= read -r f; do [ -f "$original_theme_path/${f#"$1"/}" ] || exit 1 done @@ -581,6 +581,7 @@ diyAssetSpec() { preview_square="0" preview_cols="" preview_top="0" + theme_preview="" json="" case "$1" in @@ -607,6 +608,7 @@ diyAssetSpec() { preview_square="1" preview_cols="4" # full UI screenshots - too detailed to shrink to 7 cols preview_top="1" + theme_preview="extras/preview_baseui.png" ;; packed_nes) diy_file="${sftype}.png" @@ -616,6 +618,7 @@ diyAssetSpec() { preview_square="1" preview_cols="4" # full UI screenshots - too detailed to shrink to 7 cols preview_top="1" + theme_preview="extras/preview_baseui.png" ;; colors) asset_path="scripts/env" @@ -717,7 +720,11 @@ $sprite_hash" preview_image="$temp_preview" fi [ "$chars" = "1" ] && temp_preview="$preview_image" - if [ "$colors" = "0" ] && [ "$chars" != "1" ] && [ -n "$diy_crop_x" ]; then + own_preview="" + [ -n "$theme_preview" ] && [ "$p" = "$theme_path" ] && [ -f "$info_path/$theme_preview" ] && own_preview="$info_path/$theme_preview" + if [ -n "$own_preview" ]; then + preview_image="$own_preview" # drawn by the theme author, for atlases a fixed crop can't show (Wii) + elif [ "$colors" = "0" ] && [ "$chars" != "1" ] && [ -n "$diy_crop_x" ]; then if [ ! -f "$temp_preview" ]; then # backgrounded, batched 4 at a time (this console's core count) sprite_crop "$preview_image" "$diy_crop_x" "$diy_crop_y" "$diy_crop_w" "$diy_crop_h" "$temp_preview" & @@ -731,7 +738,8 @@ $sprite_hash" preview_image="$temp_preview" # cropped copy, queued above or already cached fi - if ! printf '%s\n' "$existing_cmds" | grep -qxF "c0001_$asset_name" || ! printf '%s\n' "$existing_scripts" | grep -qxF "$asset_name.sh"; then + if ! printf '%s\n' "$existing_cmds" | grep -qxF "c0001_$asset_name" || ! printf '%s\n' "$existing_scripts" | grep -qxF "$asset_name.sh" || + { [ -n "$own_preview" ] && ! grep -qxF "PREVIEW_IMAGE=$own_preview" "$cmd_file"; }; then touch "$cmd_file" cmd_name="$(echo "$asset_name" | sed -e 's/_/ /g' | awk '{for(i=1;i<=NF;i++){$i=toupper(substr($i,1,1))substr($i,2)}}1')" [ "$p" = "$theme_path" ] && cmd_name="$(sed -r 's/NAME=//g' < "$info_path/info" | awk '{$NF="";sub(/[ \t]+$/,"")}1')" From 591b59d5fee22e85ccb3f727044760e7087d8a37 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Tue, 13 Oct 2026 14:29:39 +0000 Subject: [PATCH 146/210] Update OptionsMenu submodule to v2.0.0-alpha10 --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index dec109c..12e4d54 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit dec109ce7f5caec792b282404a43faec251eeba0 +Subproject commit 12e4d54a0cf6f1c1cf0fe2bacf236a5d51ee982f From 2969e3cfc5d1f78d894dbf479085d77089380230 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 14 Oct 2026 11:07:15 +0000 Subject: [PATCH 147/210] Move the shellcheck lint to GitHub Actions and drop local paths from VS Code settings --- .github/workflows/lint.yml | 22 ++++++++++++++++ .gitlab-ci.yml | 34 ------------------------- .vscode/settings.json | 8 ------ theme-selector-workspace.code-workspace | 3 --- 4 files changed, 22 insertions(+), 45 deletions(-) create mode 100644 .github/workflows/lint.yml delete mode 100644 .gitlab-ci.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..a565e57 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,22 @@ +name: Lint + +on: + push: + branches: ['**'] + pull_request: + +jobs: + shellcheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + # om_chmenu_og / om_fontfix_og are frozen copies of base-OS files, only used by uninstall + - name: shellcheck + run: | + files=$( + { echo mod/install; echo mod/uninstall; find mod -type f -name 'om_*'; } | + grep -v -e om_chmenu_og -e om_fontfix_og | + sort -u | xargs grep -lE '^#!/bin/(sh|bash)' + ) + shellcheck -x -e SC3046,SC3051,SC3014 $files diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index df47b70..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,34 +0,0 @@ -stages: - - lint - - build - -# om_chmenu_og / om_fontfix_og are frozen copies of base-OS files (used only -# by mod/uninstall to restore the original chmenu/fontfix) - I didn't author -# them in my style and don't expect them to pass my lint conventions. -.script_files: &list_script_files | - files=$( - { echo mod/install; echo mod/uninstall; find mod -type f -name 'om_*'; } | - grep -v -e om_chmenu_og -e om_fontfix_og | - sort -u | xargs grep -lE '^#!/bin/(sh|bash)' - ) - -shellcheck: - stage: lint - image: alpine:3.20 - before_script: - - apk add --no-cache bash findutils grep shellcheck - script: - - *list_script_files - - shellcheck -x -e SC3046,SC3051,SC3014 $files - -build: - stage: build - image: alpine:3.20 - before_script: - - apk add --no-cache make bash rsync tar zip git grep sed coreutils - script: - - make all - artifacts: - paths: - - out/* - expire_in: 30 days diff --git a/.vscode/settings.json b/.vscode/settings.json index af83009..1e2ac66 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,13 +1,5 @@ { "files.autoSave": "off", - // PowerShell - //"terminal.integrated.shell.windows": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", - // Git Bash - // "terminal.integrated.shell.windows": "C:\\Program Files\\Git\\bin\\bash.exe", - // Command Prompt - //"terminal.integrated.shell.windows": "C:\\Windows\\System32\\cmd.exe", - // Git Bash - "terminal.integrated.shell.windows": "C:\\Program Files\\Git\\bin\\bash.exe", "git.autofetch": true, "editor.tabSize": 4, "editor.insertSpaces": true, diff --git a/theme-selector-workspace.code-workspace b/theme-selector-workspace.code-workspace index f090f92..fa58c8b 100644 --- a/theme-selector-workspace.code-workspace +++ b/theme-selector-workspace.code-workspace @@ -17,9 +17,6 @@ "editor.insertSpaces": true, "editor.detectIndentation": false }, - // "terminal.integrated.shell.windows": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", - // "terminal.integrated.shell.windows": "C:\\Program Files\\Git\\bin\\bash.exe", - "shellformat.path": "C:\\bin\\shfmt_v3.12.0_windows_amd64.exe", "window.zoomLevel": 0, "files.associations": { "b[0-9]*": "shellscript", From cbfa76981c2a03a100fc846fe0f0a941e15274b1 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 14 Oct 2026 21:46:34 +0000 Subject: [PATCH 148/210] Use the NES theme catalog on the Famicom The Famicom reported its catalog as hvc, which the themes release does not have. A failed catalog fetch also left the downloads marker behind, so the list was never requested again. --- mod/etc/options_menu/themes/scripts/om_vars | 23 ++++++++++++--------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 56a54d7..2d6cbdc 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -150,7 +150,9 @@ list_name="-list-v2.tar.gz" # SHARED thm_chk="$mountpoint$profilepath/$modname/active_theme" -system_name="$(echo "$s_type" | awk '{print toupper($0)}')" +catalog_type="$s_type" +[ "$s_type" = "hvc" ] && catalog_type="nes" +system_name="$(echo "$catalog_type" | awk '{print toupper($0)}')" om_mod_path="$nand_path/$modtitle" nand_theme_path="$nand_path/$modtitle/$s_type" theme_path="$usb_path/$modtitle/$s_type" @@ -389,17 +391,18 @@ getThemeList() { "$om_scripts/gen_splash" /tmp/om_splash.png THEME_LIST_DOWNLOADING_LINE1 THEME_LIST_DOWNLOADING_LINE2 && decodepng /tmp/om_splash.png > /dev/fb0 cd "$om_mod_sub" || exit - if [ ! -e "${s_type}${list_name}" ]; then + if [ ! -e "${catalog_type}${list_name}" ]; then clearDownloads - theme_downloader catalog "$s_type" "${s_type}${list_name}" + theme_downloader catalog "$catalog_type" "${catalog_type}${list_name}" case "$?" in 0) ;; 3) translate NOT_ENOUGH_SPACE ;; *) netChecker ;; esac fi + tar -xzf "${catalog_type}${list_name}" || return # shellcheck disable=SC2035 - tar -xzf "${s_type}${list_name}" && rm -f *.tar.gz + rm -f *.tar.gz removeRegionalTheme touch "$sub_dummy" usleep 1000 @@ -463,14 +466,14 @@ themeGenerator() { translate DOWNLOADING if [ "${theme_name}" = "all" ]; then - theme_downloader all-manifest "$sftype" "${sftype}-all" + theme_downloader all-manifest "$catalog_type" "${catalog_type}-all" rc=$? failed="" if [ "$rc" = 0 ]; then - downloadThemes "${sftype}-all" + downloadThemes "${catalog_type}-all" rc=$? fi - remove "${sftype}-all" + remove "${catalog_type}-all" else downloadTheme "$theme_name" rc=$? @@ -505,7 +508,7 @@ unpackedSizeKb() { } downloadTheme() { - theme_downloader theme "$sftype" "$1" "${system_name}.$1.tar.gz" || return + theme_downloader theme "$catalog_type" "$1" "${system_name}.$1.tar.gz" || return unpackTheme "$1" } @@ -521,7 +524,7 @@ downloadThemes() { fi remove "$progress" mkfifo "$progress" - theme_downloader themes "$sftype" "$pending" > "$progress" & + theme_downloader themes "$catalog_type" "$pending" > "$progress" & dl_pid=$! dl_all_rc=0 while read -r t_rc t; do @@ -548,7 +551,7 @@ abortDownload() { trap - TERM INT HUP [ -n "$dl_pid" ] && kill "$dl_pid" 2> /dev/null [ -n "$unpacking" ] && rm -rf "$unpacking" "$unpacking.png" - rm -f "$system_name".*.tar.gz "$system_name".*.tar.gz.part "/tmp/${sftype}-pending" "/tmp/${sftype}-progress" "${sftype}-all" + rm -f "$system_name".*.tar.gz "$system_name".*.tar.gz.part "/tmp/${sftype}-pending" "/tmp/${sftype}-progress" "${catalog_type}-all" exit 1 } From 2ce51b6ef52402a23df56e9884092ebbc1e49cc3 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 14 Oct 2026 22:00:56 +0000 Subject: [PATCH 149/210] Update the Options Menu submodule to the shared downloader --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 12e4d54..8a6863c 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 12e4d54a0cf6f1c1cf0fe2bacf236a5d51ee982f +Subproject commit 8a6863c0a68e5bcd0369afe457236061b9188711 From f4fa9ca0bf90e430d9d805c86424db32f7fd857f Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 14 Oct 2026 22:08:20 +0000 Subject: [PATCH 150/210] Use the Options Menu downloader for theme downloads Theme downloads now go through omDownload/omDownloadAll from Options Menu v2.0.0-alpha12, so the bundled theme_downloader is gone and the hmod is about 1.2 MB smaller. Downloads show a live progress line. --- .gitignore | 1 - .vscode/settings.json | 1 + Makefile | 25 +-- README.md | 4 +- build.sh | 2 +- mod/etc/options_menu/themes/lang/de-DE.lang | 2 + mod/etc/options_menu/themes/lang/en-US.lang | 2 + mod/etc/options_menu/themes/lang/es-ES.lang | 2 + mod/etc/options_menu/themes/lang/fr-FR.lang | 2 + mod/etc/options_menu/themes/lang/it-IT.lang | 2 + mod/etc/options_menu/themes/lang/ja-JP.lang | 2 + mod/etc/options_menu/themes/lang/nl-NL.lang | 2 + mod/etc/options_menu/themes/lang/pt-PT.lang | 2 + mod/etc/options_menu/themes/lang/ru-RU.lang | 2 + mod/etc/options_menu/themes/scripts/om_vars | 89 +++++---- mod/install | 2 +- mod/readme.md | 2 +- mod/uninstall | 1 - src/theme_downloader.c | 201 -------------------- 19 files changed, 77 insertions(+), 269 deletions(-) delete mode 100644 src/theme_downloader.c diff --git a/.gitignore b/.gitignore index 00ce1b4..3ff2049 100644 --- a/.gitignore +++ b/.gitignore @@ -554,7 +554,6 @@ _console_originals/ tools/*.py tools/MaterialSymbolsOutlined.codepoints tools/MaterialSymbolsOutlined.ttf -src/cacert.pem mod/etc/options_menu/lib/ vendor/chmenu .deps/ diff --git a/.vscode/settings.json b/.vscode/settings.json index 1e2ac66..b2e5966 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -22,4 +22,5 @@ }, "diffEditor.ignoreTrimWhitespace": false, "shellcheck.useWorkspaceRootAsCwd": true, + "cmake.sourceDirectory": "E:/Documents/_projects/hmods/om_theme-selector/pngpack/jsoncpp", } \ No newline at end of file diff --git a/Makefile b/Makefile index 9f6419b..97bfd79 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ MOD_ID := om_theme-selector MOD_NAME := Options Menu - Theme Selector MOD_CATEGORY := Options Menu - Addons MOD_EXCLUDE := exclude-file.txt -MOD_DEPS := mod/etc/options_menu/lib/theme_manager mod/bin/theme_downloader mod/bin/sprite_crop +MOD_DEPS := mod/etc/options_menu/lib/theme_manager mod/bin/sprite_crop # Set CROSS_PREFIX=arm-linux-gnueabihf- to cross-compile for the console # (build.sh does this inside the Docker toolchain); leave unset to build natively. @@ -25,12 +25,6 @@ VENDOR_SRC_DIR = vendor/OptionsMenu/src SOURCES = src/main.cpp src/single_instance_lock.cpp src/command_loader.cpp src/texture_utils.cpp src/theme_manager_app.cpp src/folder_theme_model.cpp src/folder_themes_layout.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp $(FRAMEWORK_DIR)/badge.cpp $(FRAMEWORK_DIR)/dialog.cpp OBJECTS = $(SOURCES:.cpp=.o) -# Build theme_downloader with the toolchain's static curl+OpenSSL. -CC = gcc -CURL_PREFIX = /opt/curl-static -CFLAGS = -O2 -Wall -I$(CURL_PREFIX)/include -CURL_LDLIBS = -L$(CURL_PREFIX)/lib -L/usr/lib/arm-linux-gnueabihf -lcurl -lssl -lcrypto -lz -ldl -lpthread - all: hmod compile: $(MOD_DEPS) @@ -48,23 +42,6 @@ DEPDIR = .deps -include $(wildcard $(DEPDIR)/*.d) -%.o: %.c - $(CROSS_PREFIX)$(CC) $(CFLAGS) -c $< -o $@ - -# Fetch the gitignored CA bundle for fresh builds. -src/cacert.pem: - curl -fL -o src/cacert.pem https://curl.se/ca/cacert.pem - -# Embed cacert.pem from src/ to keep "src/" out of the symbol names. -src/ca_bundle.o: src/cacert.pem - cd src && $(CROSS_PREFIX)ld -r -b binary -o ca_bundle.o cacert.pem - -mod/bin/theme_downloader: src/theme_downloader.o src/ca_bundle.o - mkdir -p $(@D) - $(CROSS_PREFIX)$(CC) src/theme_downloader.o src/ca_bundle.o $(CURL_LDLIBS) -o $@ - $(CROSS_PREFIX)$(STRIP) $@ - upx --best $@ - # DIY sprite previews (om_vars). Built by pngpack's own Makefile; its default # LDFLAGS (-dead_strip) is macOS-only. The rpath picks up options_menu_compat's # private libstdc++ when another hmod has replaced the system one. diff --git a/README.md b/README.md index 252e032..02af57b 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Full guides are on the **[wiki](https://github.com/DefKorns/om_theme-selector/wi ## Requirements - [Hakchi2 CE](https://github.com/TeamShinkansen/hakchi2/releases/latest) -- [My Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases) — the base UI this mod plugs into +- [My Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases) v2.0.0-alpha12 or newer — the base UI this mod plugs into - [Hakchi Wi-Fi mod (WPA Supplicant)](https://hakchi.net/hakchi/hmods/wpa-supplicant.hmod) — only to download themes from the console - A USB/SD drive is recommended: themes are often 5–30 MB each @@ -78,7 +78,7 @@ The Theme Manager's own UI (not your game themes) reads its color palette from ` ```sh git clone --recursive https://github.com/DefKorns/om_theme-selector.git cd om_theme-selector -./build.sh # cross-compiles theme_manager, theme_downloader and sprite_crop, then packages out/om_theme-selector.hmod +./build.sh # cross-compiles theme_manager and sprite_crop, then packages out/om_theme-selector.hmod ./build.sh compile # binaries only ``` diff --git a/build.sh b/build.sh index 807a41d..d2ab2eb 100644 --- a/build.sh +++ b/build.sh @@ -27,4 +27,4 @@ docker run --rm -v "$PWD:/src" "$IMAGE" make CROSS_PREFIX=arm-linux-gnueabihf- " find vendor/OptionsMenu -name "*.o" -type f -delete echo "==> Build complete:" -ls -la mod/etc/options_menu/lib/theme_manager mod/bin/theme_downloader out/*.hmod 2>/dev/null +ls -la mod/etc/options_menu/lib/theme_manager out/*.hmod 2>/dev/null diff --git a/mod/etc/options_menu/themes/lang/de-DE.lang b/mod/etc/options_menu/themes/lang/de-DE.lang index e2edb2b..63e1843 100644 --- a/mod/etc/options_menu/themes/lang/de-DE.lang +++ b/mod/etc/options_menu/themes/lang/de-DE.lang @@ -72,6 +72,8 @@ NOT_ENOUGH_SPACE=Nicht genügend freier Speicher, gib Speicher frei und versuche THEMES_FAILED=Diese Themen konnten nicht installiert werden: THEME_LIST_DOWNLOADING_LINE1=Themenliste wird heruntergeladen THEME_LIST_DOWNLOADING_LINE2=Bitte warten . . . +DOWNLOADER_MISSING_LINE1=Theme-Downloads benötigen +DOWNLOADER_MISSING_LINE2=Options Menu v2.0.0-alpha12 oder neuer THEME_MOVING_LINE1=Themen werden auf USB/SD verschoben THEME_MOVING_LINE2=Bitte warten . . . THEME_LOADING_ASSETS=Assets werden geladen... diff --git a/mod/etc/options_menu/themes/lang/en-US.lang b/mod/etc/options_menu/themes/lang/en-US.lang index 82df588..dac6b7a 100644 --- a/mod/etc/options_menu/themes/lang/en-US.lang +++ b/mod/etc/options_menu/themes/lang/en-US.lang @@ -72,6 +72,8 @@ NOT_ENOUGH_SPACE=Not enough free space, free some space and try again. THEMES_FAILED=These themes failed to install: THEME_LIST_DOWNLOADING_LINE1=Downloading Theme List THEME_LIST_DOWNLOADING_LINE2=Please Wait . . . +DOWNLOADER_MISSING_LINE1=Theme downloads need +DOWNLOADER_MISSING_LINE2=Options Menu v2.0.0-alpha12 or newer THEME_MOVING_LINE1=Moving Themes to USB/SD THEME_MOVING_LINE2=Please Wait . . . THEME_LOADING_ASSETS=Loading assets... diff --git a/mod/etc/options_menu/themes/lang/es-ES.lang b/mod/etc/options_menu/themes/lang/es-ES.lang index 0c6f0e2..2cdf298 100644 --- a/mod/etc/options_menu/themes/lang/es-ES.lang +++ b/mod/etc/options_menu/themes/lang/es-ES.lang @@ -72,6 +72,8 @@ NOT_ENOUGH_SPACE=No hay suficiente espacio libre, libera espacio e inténtalo de THEMES_FAILED=Estos temas no se pudieron instalar: THEME_LIST_DOWNLOADING_LINE1=Descargando Lista de Temas THEME_LIST_DOWNLOADING_LINE2=Espere Por Favor . . . +DOWNLOADER_MISSING_LINE1=Las descargas de temas requieren +DOWNLOADER_MISSING_LINE2=Options Menu v2.0.0-alpha12 o posterior THEME_MOVING_LINE1=Moviendo Temas a USB/SD THEME_MOVING_LINE2=Espere Por Favor . . . THEME_LOADING_ASSETS=Cargando recursos... diff --git a/mod/etc/options_menu/themes/lang/fr-FR.lang b/mod/etc/options_menu/themes/lang/fr-FR.lang index 00bad08..b910854 100644 --- a/mod/etc/options_menu/themes/lang/fr-FR.lang +++ b/mod/etc/options_menu/themes/lang/fr-FR.lang @@ -72,6 +72,8 @@ NOT_ENOUGH_SPACE=Espace libre insuffisant, libérez de l'espace et réessayez. THEMES_FAILED=Ces thèmes n'ont pas pu être installés : THEME_LIST_DOWNLOADING_LINE1=Téléchargement de la Liste des Thèmes THEME_LIST_DOWNLOADING_LINE2=Veuillez Patienter . . . +DOWNLOADER_MISSING_LINE1=Le téléchargement de thèmes nécessite +DOWNLOADER_MISSING_LINE2=Options Menu v2.0.0-alpha12 ou plus récent THEME_MOVING_LINE1=Déplacement des Thèmes vers USB/SD THEME_MOVING_LINE2=Veuillez Patienter . . . THEME_LOADING_ASSETS=Chargement des ressources... diff --git a/mod/etc/options_menu/themes/lang/it-IT.lang b/mod/etc/options_menu/themes/lang/it-IT.lang index 5991405..22259f5 100644 --- a/mod/etc/options_menu/themes/lang/it-IT.lang +++ b/mod/etc/options_menu/themes/lang/it-IT.lang @@ -72,6 +72,8 @@ NOT_ENOUGH_SPACE=Spazio libero insufficiente, libera spazio e riprova. THEMES_FAILED=Questi temi non sono stati installati: THEME_LIST_DOWNLOADING_LINE1=Download Elenco Temi in Corso THEME_LIST_DOWNLOADING_LINE2=Attendere Prego . . . +DOWNLOADER_MISSING_LINE1=Il download dei temi richiede +DOWNLOADER_MISSING_LINE2=Options Menu v2.0.0-alpha12 o successivo THEME_MOVING_LINE1=Spostamento Temi su USB/SD THEME_MOVING_LINE2=Attendere Prego . . . THEME_LOADING_ASSETS=Caricamento risorse... diff --git a/mod/etc/options_menu/themes/lang/ja-JP.lang b/mod/etc/options_menu/themes/lang/ja-JP.lang index 3c483f1..de51e3a 100644 --- a/mod/etc/options_menu/themes/lang/ja-JP.lang +++ b/mod/etc/options_menu/themes/lang/ja-JP.lang @@ -72,6 +72,8 @@ NOT_ENOUGH_SPACE=あきようりょうがたりません. ようりょうをあ THEMES_FAILED=これらのThemeはインストールできませんでした: THEME_LIST_DOWNLOADING_LINE1=Themeいちらんを download しています THEME_LIST_DOWNLOADING_LINE2=おまちください . . . +DOWNLOADER_MISSING_LINE1=テーマのダウンロードには +DOWNLOADER_MISSING_LINE2=Options Menu v2.0.0-alpha12 以降が必要です THEME_MOVING_LINE1=ThemeをUSB/SDにいどうしています THEME_MOVING_LINE2=おまちください . . . THEME_LOADING_ASSETS=Assetをよみこみちゅう... diff --git a/mod/etc/options_menu/themes/lang/nl-NL.lang b/mod/etc/options_menu/themes/lang/nl-NL.lang index 98561c7..6a961d3 100644 --- a/mod/etc/options_menu/themes/lang/nl-NL.lang +++ b/mod/etc/options_menu/themes/lang/nl-NL.lang @@ -72,6 +72,8 @@ NOT_ENOUGH_SPACE=Onvoldoende vrije ruimte, maak ruimte vrij en probeer het opnie THEMES_FAILED=Deze thema's konden niet worden geïnstalleerd: THEME_LIST_DOWNLOADING_LINE1=Themalijst Wordt Gedownload THEME_LIST_DOWNLOADING_LINE2=Even Geduld . . . +DOWNLOADER_MISSING_LINE1=Thema-downloads vereisen +DOWNLOADER_MISSING_LINE2=Options Menu v2.0.0-alpha12 of nieuwer THEME_MOVING_LINE1=Thema's naar USB/SD verplaatsen THEME_MOVING_LINE2=Even Geduld . . . THEME_LOADING_ASSETS=Assets worden geladen... diff --git a/mod/etc/options_menu/themes/lang/pt-PT.lang b/mod/etc/options_menu/themes/lang/pt-PT.lang index bf8ea28..42ce7e1 100644 --- a/mod/etc/options_menu/themes/lang/pt-PT.lang +++ b/mod/etc/options_menu/themes/lang/pt-PT.lang @@ -72,6 +72,8 @@ NOT_ENOUGH_SPACE=Não há espaço livre suficiente, liberta algum espaço e tent THEMES_FAILED=Estes temas não foram instalados: THEME_LIST_DOWNLOADING_LINE1=A Descarregar Lista de Temas THEME_LIST_DOWNLOADING_LINE2=Aguarde Por Favor . . . +DOWNLOADER_MISSING_LINE1=Os downloads de temas precisam do +DOWNLOADER_MISSING_LINE2=Options Menu v2.0.0-alpha12 ou mais recente THEME_MOVING_LINE1=A Mover Temas para USB/SD THEME_MOVING_LINE2=Aguarde Por Favor . . . THEME_LOADING_ASSETS=A carregar recursos... diff --git a/mod/etc/options_menu/themes/lang/ru-RU.lang b/mod/etc/options_menu/themes/lang/ru-RU.lang index 6f89447..181964a 100644 --- a/mod/etc/options_menu/themes/lang/ru-RU.lang +++ b/mod/etc/options_menu/themes/lang/ru-RU.lang @@ -72,6 +72,8 @@ NOT_ENOUGH_SPACE=Недостаточно свободного места, ос THEMES_FAILED=Эти темы не удалось установить: THEME_LIST_DOWNLOADING_LINE1=Загрузка Списка Тем THEME_LIST_DOWNLOADING_LINE2=Пожалуйста, подождите . . . +DOWNLOADER_MISSING_LINE1=Для загрузки тем нужен +DOWNLOADER_MISSING_LINE2=Options Menu v2.0.0-alpha12 или новее THEME_MOVING_LINE1=Перемещение тем на USB/SD THEME_MOVING_LINE2=Пожалуйста, подождите . . . THEME_LOADING_ASSETS=Загрузка ресурсов... diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 2d6cbdc..34b0eae 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -147,6 +147,9 @@ disable_wall_far="$om_mod_gen_cmds/_wall_far" enable_wall_near="$om_mod_gen_cmds/c0004_wall_near" disable_wall_near="$om_mod_gen_cmds/_wall_near" list_name="-list-v2.tar.gz" +themes_github_url="https://github.com/DefKorns/om-theme-downloads/releases/download/themes-v1" +themes_classicmods_url="http://classicmods.net/files/themes/themeselector" +catalogs_classicmods_url="http://classicmods.net/files/themes/themeselector-scripts" # SHARED thm_chk="$mountpoint$profilepath/$modname/active_theme" @@ -391,14 +394,14 @@ getThemeList() { "$om_scripts/gen_splash" /tmp/om_splash.png THEME_LIST_DOWNLOADING_LINE1 THEME_LIST_DOWNLOADING_LINE2 && decodepng /tmp/om_splash.png > /dev/fb0 cd "$om_mod_sub" || exit + if [ ! -x "$om_scripts/downloader" ]; then + "$om_scripts/gen_splash" /tmp/om_splash.png DOWNLOADER_MISSING_LINE1 DOWNLOADER_MISSING_LINE2 && decodepng /tmp/om_splash.png > /dev/fb0 + sleep 3 + return 1 + fi if [ ! -e "${catalog_type}${list_name}" ]; then clearDownloads - theme_downloader catalog "$catalog_type" "${catalog_type}${list_name}" - case "$?" in - 0) ;; - 3) translate NOT_ENOUGH_SPACE ;; - *) netChecker ;; - esac + omDownload "${catalog_type}${list_name}" "$themes_github_url/${catalog_type}${list_name}" "$catalogs_classicmods_url/${catalog_type}${list_name}" fi tar -xzf "${catalog_type}${list_name}" || return # shellcheck disable=SC2035 @@ -462,11 +465,16 @@ themeGenerator() { full_title="$(grep -F 'NAME=' "${om_mod_sub_cmds}/c0010_${theme_name}" | sed -e 's/.*=\(.*\)\[.*/\1/')" full_path="${theme_path}/${theme_name}" cd "${theme_path}" || exit + if [ ! -x "$om_scripts/downloader" ]; then + translate DOWNLOADER_MISSING_LINE1 + translate DOWNLOADER_MISSING_LINE2 + return 1 + fi trap abortDownload TERM INT HUP translate DOWNLOADING if [ "${theme_name}" = "all" ]; then - theme_downloader all-manifest "$catalog_type" "${catalog_type}-all" + omDownload "${catalog_type}-all" "$themes_github_url/${catalog_type}-all" "$catalogs_classicmods_url/${catalog_type}-all" rc=$? failed="" if [ "$rc" = 0 ]; then @@ -487,9 +495,7 @@ themeGenerator() { echo "" case "$rc" in 0) translate INSTALLATION_COMPLETE ;; - 3) translate NOT_ENOUGH_SPACE ;; 4) translate ALL_THEMES_INSTALLED ;; - *) netChecker ;; esac if [ -n "$failed" ]; then echo "" @@ -507,51 +513,60 @@ unpackedSizeKb() { echo $(( size_kb + size_kb / 10 )) } +urlEncodeName() { + printf '%s' "$1" | sed 's/%/%25/g; s/#/%23/g; s/?/%3F/g' +} + +remoteTarball() { + echo "${system_name}.$(urlEncodeName "$1").tar.gz" +} + downloadTheme() { - theme_downloader theme "$catalog_type" "$1" "${system_name}.$1.tar.gz" || return + remote="$(remoteTarball "$1")" + omDownload "${system_name}.$1.tar.gz" "$themes_github_url/$remote" "$themes_classicmods_url/$remote" || return unpackTheme "$1" + rc=$? + downloadError "$rc" + return "$rc" } downloadThemes() { pending="/tmp/${sftype}-pending" - progress="/tmp/${sftype}-progress" while IFS= read -r t; do - [ -d "$t" ] || [ "$t" = "$sfregion" ] || echo "$t" + { [ -d "$t" ] || [ "$t" = "$sfregion" ]; } && continue + remote="$(remoteTarball "$t")" + printf '%s\t%s\t%s\n' "${system_name}.$t.tar.gz" "$themes_github_url/$remote" "$themes_classicmods_url/$remote" done < "$1" > "$pending" if [ ! -s "$pending" ]; then remove "$pending" return 4 fi - remove "$progress" - mkfifo "$progress" - theme_downloader themes "$catalog_type" "$pending" > "$progress" & - dl_pid=$! - dl_all_rc=0 - while read -r t_rc t; do - echo "$t" - echo "" - if [ "$t_rc" = 0 ]; then - unpackTheme "$t" - t_rc=$? - fi - case "$t_rc" in - 0) ;; - 3) dl_all_rc=3 && break ;; - *) failed="$failed $t" ;; - esac - done < "$progress" - kill "$dl_pid" 2> /dev/null - wait "$dl_pid" - [ "$?" = 3 ] && dl_all_rc=3 - rm -f "$pending" "$progress" "$system_name".*.tar.gz "$system_name".*.tar.gz.part - return "$dl_all_rc" + omDownloadAll "$pending" installDownloaded 2 + rc=$? + remove "$pending" + return "$rc" +} + +# omDownloadAll handler: +installDownloaded() { + t="${2#"$system_name".}" + t="${t%.tar.gz}" + t_rc="$1" + if [ "$t_rc" = 0 ]; then + unpackTheme "$t" + t_rc=$? + fi + case "$t_rc" in + 0) ;; + 3) downloadError 3 && return 3 ;; + *) failed="$failed $t" ;; + esac } abortDownload() { trap - TERM INT HUP - [ -n "$dl_pid" ] && kill "$dl_pid" 2> /dev/null [ -n "$unpacking" ] && rm -rf "$unpacking" "$unpacking.png" - rm -f "$system_name".*.tar.gz "$system_name".*.tar.gz.part "/tmp/${sftype}-pending" "/tmp/${sftype}-progress" "${catalog_type}-all" + rm -f "$system_name".*.tar.gz "/tmp/${sftype}-pending" "${catalog_type}-all" exit 1 } diff --git a/mod/install b/mod/install index f0163fa..2405022 100644 --- a/mod/install +++ b/mod/install @@ -87,7 +87,7 @@ for legacy_img in back chip_apply chip_clear chip_diy chip_download chip_preview done find "$rootfs/etc/options_menu/themes/creator/sprites" -path '*/commands/c0001_*' -type f -exec rm {} + 2>/dev/null chmod +x "$rootfs/bin/sprite_crop" -chmod +x "$rootfs/bin/theme_downloader" +del "$rootfs/bin/theme_downloader" chmod +x "$rootfs/etc/options_menu/lib/theme_manager" theme_cfg="$rootfs/etc/options_menu/theme.cfg" diff --git a/mod/readme.md b/mod/readme.md index c32f6fc..cf55b15 100644 --- a/mod/readme.md +++ b/mod/readme.md @@ -42,7 +42,7 @@ A theme named after a folder (lower snake_case, e.g. `nintendo_-_nintendo_entert ## Requirements - [Hakchi2 CE](https://github.com/TeamShinkansen/hakchi2/releases/latest) -- [My Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases) +- [My Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases) v2.0.0-alpha12 or newer - [Hakchi Wi-Fi mod (WPA Supplicant)](https://hakchi.net/hakchi/hmods/wpa-supplicant.hmod) — only to download themes from the console - A USB/SD drive is recommended: themes are often 5–30 MB each diff --git a/mod/uninstall b/mod/uninstall index 08fb8a3..4322dad 100644 --- a/mod/uninstall +++ b/mod/uninstall @@ -129,7 +129,6 @@ removeItem "$rootfs/etc/options_menu/images/wall_far.png" removeItem "$rootfs/etc/options_menu/images/wall_near.png" removeItem "$rootfs/etc/usr/bin/pngpack-upx" removeItem "$rootfs/bin/sprite_crop" -removeItem "$rootfs/bin/theme_downloader" removeItem "$rootfs/etc/options_menu/lib/theme_manager" [ -f "${font_path}/title.fnt.backup" ] && cp "${font_path}/title.fnt.backup" "${font_path}/title.fnt" diff --git a/src/theme_downloader.c b/src/theme_downloader.c deleted file mode 100644 index 184826d..0000000 --- a/src/theme_downloader.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2026 DefKorns - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/* Fetch theme catalogs and packages over HTTPS using static curl+OpenSSL. - * Fall back from GitHub to classicmods.net automatically. */ - -#include -#include -#include -#include -#include -#include - -extern const unsigned char _binary_cacert_pem_start[]; -extern const unsigned char _binary_cacert_pem_end[]; - -static const char *const GITHUB_BASE = "https://github.com/DefKorns/om-theme-downloads/releases/download/themes-v1"; -static const char *const CLASSICMODS_SCRIPTS = "http://classicmods.net/files/themes/themeselector-scripts/"; -static const char *const CLASSICMODS_THEMES = "http://classicmods.net/files/themes/themeselector/"; -static const char *const LIST_SUFFIX = "-list-v2.tar.gz"; -static const char *const CA_PATH = "/tmp/.theme_downloader_ca.pem"; - -enum { CONNECT_TIMEOUT_S = 10, STALL_TIMEOUT_S = 30, MAX_REDIRECTS = 10 }; -enum { REMOVAL_POLL_US = 200000, TARBALLS_ON_DISK = 2 }; -enum exit_code { EXIT_OK = 0, EXIT_FAILED = 1, EXIT_USAGE = 2, EXIT_NO_SPACE = 3 }; - -static void to_upper(char *dst, const char *src, size_t dst_size) { - size_t i = 0; - for (; src[i] && i + 1 < dst_size; i++) { - dst[i] = (char)toupper((unsigned char)src[i]); - } - dst[i] = '\0'; -} - -/* Extract the embedded CA bundle into /tmp once per boot. */ -static void ensure_ca_bundle(void) { - FILE *f = fopen(CA_PATH, "rb"); - if (f) { - fclose(f); - return; - } - f = fopen(CA_PATH, "wb"); - if (!f) return; - fwrite(_binary_cacert_pem_start, 1, (size_t)(_binary_cacert_pem_end - _binary_cacert_pem_start), f); - fclose(f); -} - -static CURL *make_handle(void) { - CURL *c = curl_easy_init(); - if (!c) return NULL; - curl_easy_setopt(c, CURLOPT_CAINFO, CA_PATH); - curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(c, CURLOPT_MAXREDIRS, (long)MAX_REDIRECTS); - curl_easy_setopt(c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1L); - curl_easy_setopt(c, CURLOPT_USERAGENT, "theme_downloader/1.0"); - curl_easy_setopt(c, CURLOPT_CONNECTTIMEOUT, (long)CONNECT_TIMEOUT_S); - curl_easy_setopt(c, CURLOPT_LOW_SPEED_LIMIT, 1L); - curl_easy_setopt(c, CURLOPT_LOW_SPEED_TIME, (long)STALL_TIMEOUT_S); - return c; -} - -static size_t write_to_file(void *ptr, size_t size, size_t nmemb, void *userdata) { - return fwrite(ptr, size, nmemb, (FILE *)userdata); -} - -enum fetch_result { FETCH_FAILED, FETCH_OK, FETCH_WRITE_ERROR }; - -static enum fetch_result download_to(CURL *c, const char *url, const char *out_path) { - char part_path[512]; - snprintf(part_path, sizeof(part_path), "%s.part", out_path); - FILE *f = fopen(part_path, "wb"); - if (!f) return FETCH_WRITE_ERROR; - curl_easy_setopt(c, CURLOPT_URL, url); - curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, write_to_file); - curl_easy_setopt(c, CURLOPT_WRITEDATA, f); - CURLcode res = curl_easy_perform(c); - int closed = fclose(f) == 0; - if (res == CURLE_OK && closed && rename(part_path, out_path) == 0) return FETCH_OK; - remove(part_path); - return (res == CURLE_WRITE_ERROR || !closed) ? FETCH_WRITE_ERROR : FETCH_FAILED; -} - -static enum fetch_result fetch_with_fallback(CURL *c, const char *gh_url, const char *fallback_url, const char *out_path) { - enum fetch_result r = download_to(c, gh_url, out_path); - if (r == FETCH_FAILED) r = download_to(c, fallback_url, out_path); - return r; -} - -static enum fetch_result fetch_theme(CURL *c, const char *sftype, const char *theme_name, const char *out_path) { - char system_name[32]; - char gh_url[512]; - char fallback_url[512]; - char *escaped = curl_easy_escape(c, theme_name, 0); - if (!escaped) return FETCH_FAILED; - to_upper(system_name, sftype, sizeof(system_name)); - snprintf(gh_url, sizeof(gh_url), "%s/%s.%s.tar.gz", GITHUB_BASE, system_name, escaped); - snprintf(fallback_url, sizeof(fallback_url), "%s%s.%s.tar.gz", CLASSICMODS_THEMES, system_name, escaped); - curl_free(escaped); - return fetch_with_fallback(c, gh_url, fallback_url, out_path); -} - -static enum exit_code result_code(enum fetch_result r) { - if (r == FETCH_WRITE_ERROR) return EXIT_NO_SPACE; - return r == FETCH_OK ? EXIT_OK : EXIT_FAILED; -} - -/* Stop waiting if the unpacking shell is gone. */ -static void wait_until_removed(const char *path, pid_t unpacker) { - while (path[0] && access(path, F_OK) == 0 && getppid() == unpacker) usleep(REMOVAL_POLL_US); -} - -static enum exit_code fetch_theme_list(CURL *c, const char *sftype, const char *list_path) { - FILE *list = fopen(list_path, "r"); - if (!list) return EXIT_FAILED; - char system_name[32]; - to_upper(system_name, sftype, sizeof(system_name)); - char name[256]; - char out_path[TARBALLS_ON_DISK][512] = {{0}}; - int slot = 0; - enum exit_code code = EXIT_OK; - const pid_t unpacker = getppid(); - while (fgets(name, sizeof(name), list)) { - name[strcspn(name, "\r\n")] = '\0'; - if (!name[0]) continue; - wait_until_removed(out_path[slot], unpacker); - snprintf(out_path[slot], sizeof(out_path[slot]), "%s.%s.tar.gz", system_name, name); - code = result_code(fetch_theme(c, sftype, name, out_path[slot])); - if (code != EXIT_OK) out_path[slot][0] = '\0'; - if (printf("%d %s\n", code, name) < 0 || fflush(stdout) != 0) break; - if (code == EXIT_NO_SPACE) break; - slot = (slot + 1) % TARBALLS_ON_DISK; - } - fclose(list); - return code == EXIT_NO_SPACE ? EXIT_NO_SPACE : EXIT_OK; -} - -static void usage(const char *prog) { - fprintf(stderr, - "usage:\n" - " %s catalog \n" - " %s theme \n" - " %s themes \n" - " %s all-manifest \n", - prog, prog, prog, prog); -} - -int main(int argc, char **argv) { - if (argc < 2) { - usage(argv[0]); - return EXIT_USAGE; - } - - curl_global_init(CURL_GLOBAL_DEFAULT); - ensure_ca_bundle(); - CURL *c = make_handle(); - if (!c) { - curl_global_cleanup(); - return EXIT_FAILED; - } - - char gh_url[512]; - char fallback_url[512]; - enum exit_code code = EXIT_USAGE; - - if (strcmp(argv[1], "catalog") == 0 && argc == 4) { - const char *sftype = argv[2]; - snprintf(gh_url, sizeof(gh_url), "%s/%s%s", GITHUB_BASE, sftype, LIST_SUFFIX); - snprintf(fallback_url, sizeof(fallback_url), "%s%s%s", CLASSICMODS_SCRIPTS, sftype, LIST_SUFFIX); - code = result_code(fetch_with_fallback(c, gh_url, fallback_url, argv[3])); - } else if (strcmp(argv[1], "theme") == 0 && argc == 5) { - code = result_code(fetch_theme(c, argv[2], argv[3], argv[4])); - } else if (strcmp(argv[1], "themes") == 0 && argc == 4) { - code = fetch_theme_list(c, argv[2], argv[3]); - } else if (strcmp(argv[1], "all-manifest") == 0 && argc == 4) { - const char *sftype = argv[2]; - snprintf(gh_url, sizeof(gh_url), "%s/%s-all", GITHUB_BASE, sftype); - snprintf(fallback_url, sizeof(fallback_url), "%s%s-all", CLASSICMODS_SCRIPTS, sftype); - code = result_code(fetch_with_fallback(c, gh_url, fallback_url, argv[3])); - } else { - usage(argv[0]); - } - - curl_easy_cleanup(c); - curl_global_cleanup(); - return code; -} From 51b10b9e832af4fad29a6b5f3a212d5f8bfb9c93 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 14 Oct 2026 22:18:29 +0000 Subject: [PATCH 151/210] Update the Options Menu submodule to show free space while downloading --- vendor/OptionsMenu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu index 8a6863c..fa5e4cc 160000 --- a/vendor/OptionsMenu +++ b/vendor/OptionsMenu @@ -1 +1 @@ -Subproject commit 8a6863c0a68e5bcd0369afe457236061b9188711 +Subproject commit fa5e4ccae55ca4a370ff607b8973b56ed11bed71 From 1c6ef4a36f1f8da36cf0937d4eb16b6c02ac3610 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 14 Oct 2026 22:23:22 +0000 Subject: [PATCH 152/210] Show NAND and USB free space on the downloads screen The downloads list draws a free-space meter for NAND and, when one is mounted, the USB/SD drive, with the download destination highlighted and the bar colored by how full the drive is. --- Makefile | 2 +- .../themes/creator/scripts/om_preview | 4 +- .../downloads/scripts/om_clearDownloads | 2 +- .../themes/downloads/scripts/om_refreshThemes | 2 +- .../options_menu/themes/scripts/om_getThemes | 2 +- .../options_menu/themes/scripts/om_settings | 6 +- mod/etc/options_menu/themes/scripts/om_vars | 13 +- .../themes/settings/scripts/om_state | 7 +- src/command_loader.cpp | 7 +- src/command_loader.h | 1 + src/folder_theme_model.h | 2 +- src/main.cpp | 3 +- src/single_instance_lock.h | 2 +- src/storage_indicator.cpp | 139 ++++++++++++++++++ src/storage_indicator.h | 49 ++++++ src/theme_manager_app.cpp | 31 ++-- src/theme_manager_app.h | 5 +- 17 files changed, 229 insertions(+), 48 deletions(-) create mode 100644 src/storage_indicator.cpp create mode 100644 src/storage_indicator.h diff --git a/Makefile b/Makefile index 97bfd79..c41559e 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ endif CXXFLAGS = -std=c++11 -Os -Ivendor/OptionsMenu/src $(SDL_CFLAGS) -DMOD_VERSION=\"v$(MOD_VER)\" LDLIBS = $(SDL_LIBS) VENDOR_SRC_DIR = vendor/OptionsMenu/src -SOURCES = src/main.cpp src/single_instance_lock.cpp src/command_loader.cpp src/texture_utils.cpp src/theme_manager_app.cpp src/folder_theme_model.cpp src/folder_themes_layout.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp $(FRAMEWORK_DIR)/badge.cpp $(FRAMEWORK_DIR)/dialog.cpp +SOURCES = src/main.cpp src/single_instance_lock.cpp src/command_loader.cpp src/texture_utils.cpp src/theme_manager_app.cpp src/folder_theme_model.cpp src/folder_themes_layout.cpp src/storage_indicator.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp $(FRAMEWORK_DIR)/badge.cpp $(FRAMEWORK_DIR)/dialog.cpp OBJECTS = $(SOURCES:.cpp=.o) all: hmod diff --git a/mod/etc/options_menu/themes/creator/scripts/om_preview b/mod/etc/options_menu/themes/creator/scripts/om_preview index 2044890..d6d646e 100644 --- a/mod/etc/options_menu/themes/creator/scripts/om_preview +++ b/mod/etc/options_menu/themes/creator/scripts/om_preview @@ -26,9 +26,7 @@ if [ ! -f "$b_file" ]; then echo "themeSelector" >"$p_file" fi -# chmenu/restart alone don't re-run preinit.d, so themeSelector's own -# DIY_THEME pickup (om_themescript) never re-fired - call it directly here, -# same as it'll run again at the next real boot via b_file/p_file +# chmenu doesn't re-run preinit.d, so apply the DIY theme here # shellcheck source=/dev/null source "$b_file" themeSelector diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads index 71bd2e6..2ecda24 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads +++ b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads @@ -23,4 +23,4 @@ script_init clearDownloads remove "$sub_dummy" -$options_menu/lib/theme_manager --commandPath $om_mod_sub_cmds/ --scriptPath $om_mod_sub_scripts --title AVAILABLE_THEMES & +$options_menu/lib/theme_manager --commandPath $om_mod_sub_cmds/ --scriptPath $om_mod_sub_scripts --title AVAILABLE_THEMES --storage & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes index 77ead87..154e992 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes +++ b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes @@ -22,4 +22,4 @@ script_init getThemeList -$options_menu/lib/theme_manager --commandPath $om_mod_sub_cmds/ --scriptPath $om_mod_sub_scripts --title AVAILABLE_THEMES & +$options_menu/lib/theme_manager --commandPath $om_mod_sub_cmds/ --scriptPath $om_mod_sub_scripts --title AVAILABLE_THEMES --storage & diff --git a/mod/etc/options_menu/themes/scripts/om_getThemes b/mod/etc/options_menu/themes/scripts/om_getThemes index e9ccd63..bfb5ed7 100644 --- a/mod/etc/options_menu/themes/scripts/om_getThemes +++ b/mod/etc/options_menu/themes/scripts/om_getThemes @@ -22,4 +22,4 @@ script_init [ ! -f "$sub_dummy" ] && getThemeList -$options_menu/lib/theme_manager --commandPath $om_mod_sub_cmds/ --scriptPath $om_mod_sub_scripts --title AVAILABLE_THEMES & +$options_menu/lib/theme_manager --commandPath $om_mod_sub_cmds/ --scriptPath $om_mod_sub_scripts --title AVAILABLE_THEMES --storage & diff --git a/mod/etc/options_menu/themes/scripts/om_settings b/mod/etc/options_menu/themes/scripts/om_settings index d7722bf..1efc67c 100644 --- a/mod/etc/options_menu/themes/scripts/om_settings +++ b/mod/etc/options_menu/themes/scripts/om_settings @@ -20,11 +20,7 @@ source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init -# theme_manager's STATE_STR checks (om_state) would otherwise each re-derive -# $mod_state_path from scratch via om_functions/preinit - cache it once here so -# 5 toggle rows cost one lookup instead of five. Plain /tmp, no $mountpoint: -# this is throwaway same-boot-session IPC, not data that needs to live inside -# a particular mounted rootfs the way e.g. diy_theme_path does +# cached for om_state: one lookup instead of one per toggle row echo "$mod_state_path" > /tmp/om_state_path hasItemsToCleanUp "system_clean_up" diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 34b0eae..d1de4e8 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -547,7 +547,6 @@ downloadThemes() { return "$rc" } -# omDownloadAll handler: installDownloaded() { t="${2#"$system_name".}" t="${t%.tar.gz}" @@ -686,7 +685,7 @@ findDiyAsset() { -not -name 'om_*' -not -name 'c999[0-9]_*' -not -name 'c0000_*' \ -not -name 'c0001_*' -not -name '_*' -exec rm {} + - # read once - avoids a [ -f ] stat() per theme below, slow on USB/SD + # one read instead of a stat per theme (slow on USB/SD) existing_cmds="$(ls -1 "$diy_sprite/commands" 2>/dev/null)" existing_scripts="$(ls -1 "$diy_sprite/scripts" 2>/dev/null)" if [ "$preview_top" = "1" ] && [ -n "$existing_cmds" ]; then @@ -696,11 +695,11 @@ findDiyAsset() { fi diy_crop_batch=0 - diy_seen_hashes="" # collapses themes that reuse a byte-identical source asset under a different name + diy_seen_hashes="" # skips assets identical to one already listed for p in $paths_to_search; do if [ "$(ls -A "$p")" ]; then # shellcheck disable=SC2038 - # a temp file, not a pipe into the while loop: a pipe would put the backgrounded crops in a subshell the wait below can't catch + # a temp file, not a pipe: wait can't catch jobs in a pipe's subshell cd "$p" && find . -mindepth 1 -maxdepth 1 -type d | xargs -n 1 basename | sort -u > /tmp/diy_asset_list while IFS= read -r asset_name; do @@ -721,7 +720,7 @@ findDiyAsset() { preview_image="$full_asset_path/$diy_file" - # hashed pre-crop, so this doesn't have to wait on the backgrounded crop job below + # hashed pre-crop, so it doesn't wait on the crop job if [ -f "$preview_image" ]; then sprite_hash="$(md5sum "$preview_image" | cut -d' ' -f1)" if printf '%s\n' "$diy_seen_hashes" | grep -qxF "$sprite_hash"; then @@ -741,10 +740,10 @@ $sprite_hash" own_preview="" [ -n "$theme_preview" ] && [ "$p" = "$theme_path" ] && [ -f "$info_path/$theme_preview" ] && own_preview="$info_path/$theme_preview" if [ -n "$own_preview" ]; then - preview_image="$own_preview" # drawn by the theme author, for atlases a fixed crop can't show (Wii) + preview_image="$own_preview" # for atlases a fixed crop can't show elif [ "$colors" = "0" ] && [ "$chars" != "1" ] && [ -n "$diy_crop_x" ]; then if [ ! -f "$temp_preview" ]; then - # backgrounded, batched 4 at a time (this console's core count) + # 4 at a time (core count) sprite_crop "$preview_image" "$diy_crop_x" "$diy_crop_y" "$diy_crop_w" "$diy_crop_h" "$temp_preview" & diy_crop_batch=$((diy_crop_batch + 1)) if [ "$diy_crop_batch" -ge 4 ]; then diff --git a/mod/etc/options_menu/themes/settings/scripts/om_state b/mod/etc/options_menu/themes/settings/scripts/om_state index 524104c..3154124 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_state +++ b/mod/etc/options_menu/themes/settings/scripts/om_state @@ -16,12 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# Reports current on/off state for a Settings toggle row - read-only -# counterpart to om_theme/om_audio, which flip the same flag files. The -# engine calls this once per toggle row every time the Settings screen -# loads, so it reads $mod_state_path from the cache om_settings writes just -# before launching theme_manager, instead of re-sourcing om_functions/ -# preinit (~0.1-0.3s) five times over for the same value. +# on/off state of a Settings toggle; reads the path om_settings cached mod_state_path="$(cat /tmp/om_state_path 2> /dev/null)" if [ -z "$mod_state_path" ]; then # shellcheck source=/dev/null diff --git a/src/command_loader.cpp b/src/command_loader.cpp index 5fc8f39..3eaf310 100644 --- a/src/command_loader.cpp +++ b/src/command_loader.cpp @@ -95,8 +95,7 @@ bool HasAnyFile(const std::string & dirPath) } -// child screens inherit this via fork+exec, same as OM_BACK_STACK - so only the first screen -// that needs sftype pays for the shell fork +// inherited by child screens, so only the first one forks a shell std::string ReadSftype() { if(const char * env = std::getenv("OM_SFTYPE")) @@ -146,6 +145,8 @@ AppOptions ParseArgs(int argc, char * argv[], const std::string & optionsLocatio options.folderMusicScreen = std::strcmp(argv[++i], "music") == 0; else if(std::strcmp(argv[i], "--selectedMarker") == 0 && i+1 < argc) options.selectedMarker = argv[++i]; + else if(std::strcmp(argv[i], "--storage") == 0) + options.showStorage = true; } if(!options.commandLocation.empty() && options.commandLocation.back() != '/') options.commandLocation += '/'; @@ -188,7 +189,7 @@ bool LoadCommands(const std::string & commandLocation, const std::string & scrip continue; Command c(in); // closes `in` itself once read - // c0000_0000 leading sentinel only - a later empty COMMAND_STR is a reserved blank slot instead + // only a leading c0000_0000 is the sentinel; later empty ones are blank slots if(commands.empty() && c.command.empty()) continue; if(c.usbOnly && !usbReady) diff --git a/src/command_loader.h b/src/command_loader.h index 402f642..20d2112 100644 --- a/src/command_loader.h +++ b/src/command_loader.h @@ -27,6 +27,7 @@ struct AppOptions std::string folderMusicMapping; bool folderMusicScreen = false; std::string selectedMarker; + bool showStorage = false; }; // each screen remembers its last focused row here; cleared on leaving the theme manager diff --git a/src/folder_theme_model.h b/src/folder_theme_model.h index 5edc3ef..a35749a 100644 --- a/src/folder_theme_model.h +++ b/src/folder_theme_model.h @@ -41,7 +41,7 @@ struct ResolvedOption int fromFolder; }; -// Folder -> theme and folder -> music assignments, resolved the same way om_themescript applies them on chmenu +// folder -> theme/music, resolved like om_themescript class FolderThemeModel { public: diff --git a/src/main.cpp b/src/main.cpp index 6793440..2131e87 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,8 +7,7 @@ * (at your option) any later version. */ -// Standalone theme manager using the vendored OptionsMenu engine. Each -// screen relaunches this binary with a different --commandPath. +// Each screen relaunches this binary with its own --commandPath. #include "single_instance_lock.h" #include "command_loader.h" diff --git a/src/single_instance_lock.h b/src/single_instance_lock.h index b6d8e98..6a51138 100644 --- a/src/single_instance_lock.h +++ b/src/single_instance_lock.h @@ -10,7 +10,7 @@ #ifndef SINGLE_INSTANCE_LOCK_H_ #define SINGLE_INSTANCE_LOCK_H_ -// Keeps one theme_manager on /dev/fb0, taking over a stale instance instead of rejecting the new launch. +// takes over a stale instance instead of refusing to start class SingleInstanceLock { public: diff --git a/src/storage_indicator.cpp b/src/storage_indicator.cpp new file mode 100644 index 0000000..e15342f --- /dev/null +++ b/src/storage_indicator.cpp @@ -0,0 +1,139 @@ +/** + * Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#include "storage_indicator.h" +#include "framework/draw_helpers.h" +#include "localization.h" + +#include +#include +#include +#include + +namespace +{ + constexpr const char * UsbPath = "/media"; + constexpr const char * NandPath = "/var/lib/hakchi"; + constexpr int FontSize = 14; + constexpr int BarW = 110; + constexpr int BarH = 10; + constexpr int InnerGap = 10; + constexpr int MeterGap = 32; + constexpr double LowFree = 0.25; + constexpr double CriticalFree = 0.10; + constexpr Color Healthy{ 0x4C, 0xAF, 0x6E }; + + bool UsbMounted() + { + struct stat media, root; + return stat(UsbPath, &media) == 0 && stat("/", &root) == 0 && media.st_dev != root.st_dev; + } + + std::string FormatFree(double bytes) + { + const double mb = bytes / (1024.0 * 1024.0); + char text[32]; + if(mb >= 1024.0) + std::snprintf(text, sizeof(text), "%.1f GB", mb / 1024.0); + else + std::snprintf(text, sizeof(text), "%.1f MB", mb); + return text; + } + + Color LevelColor(double freeFraction) + { + if(freeFraction < CriticalFree) + return UiTheme::BadgeB; + if(freeFraction < LowFree) + return UiTheme::Accent; + return Healthy; + } +} + +StorageIndicator::StorageIndicator(SDL_Renderer * renderer) + : renderer_(renderer) +{ + Refresh(); +} + +void StorageIndicator::Update() +{ + if(SDL_GetTicks() - lastRefreshMs_ >= RefreshMs) + Refresh(); +} + +void StorageIndicator::Refresh() +{ + lastRefreshMs_ = SDL_GetTicks(); + const bool usb = UsbMounted(); + if(meters_.size() != (usb ? 2u : 1u)) + { + meters_.assign(usb ? 2 : 1, Meter{}); + meters_[0].name = "NAND"; + meters_[0].path = NandPath; + if(usb) + { + meters_[1].name = "USB"; + meters_[1].path = UsbPath; + } + } + destination_ = static_cast(meters_.size()) - 1; + for(int i = 0; i < static_cast(meters_.size()); ++i) + ReadMeter(meters_[i], i == destination_); +} + +void StorageIndicator::ReadMeter(Meter & meter, bool isDestination) +{ + struct statvfs vfs; + if(statvfs(meter.path.c_str(), &vfs) != 0 || vfs.f_blocks == 0) + return; + const double blockSize = static_cast(vfs.f_frsize); + const double total = vfs.f_blocks * blockSize; + const double available = vfs.f_bavail * blockSize; + meter.usedFraction = (vfs.f_blocks - vfs.f_bfree) * blockSize / total; + meter.freeFraction = available / total; + + const std::string valueText = FormatFree(available) + " " + Translate("FREE_SPACE"); + if(valueText == meter.valueText && meter.label.texture) + return; + meter.valueText = valueText; + const Uint32 textColor = ToAbgr(isDestination ? UiTheme::Text : UiTheme::TextDim); + meter.label = Texture(meter.name, FontSize, renderer_, 0, 0, false, textColor, true); + meter.value = Texture(valueText, FontSize, renderer_, 0, 0, false, textColor, true); +} + +void StorageIndicator::Draw(int rightX, int centerY) +{ + int x = rightX; + for(int i = static_cast(meters_.size()) - 1; i >= 0; --i) + { + Meter & meter = meters_[i]; + x -= meter.value.rect.w; + meter.value.rect.x = x; + meter.value.rect.y = centerY - meter.value.rect.h / 2; + meter.value.Draw(renderer_); + + x -= InnerGap + BarW; + const SDL_Rect track{ x, centerY - BarH / 2, BarW, BarH }; + DrawRoundedFillRect(renderer_, track, UiTheme::Border, BarH / 2); + const int usedW = static_cast(BarW * meter.usedFraction + 0.5); + if(usedW > 0) + { + const Color level = i == destination_ ? LevelColor(meter.freeFraction) : UiTheme::TextDim; + DrawRoundedFillRect(renderer_, { x, track.y, std::max(usedW, BarH), BarH }, level, BarH / 2); + } + + x -= InnerGap + meter.label.rect.w; + meter.label.rect.x = x; + meter.label.rect.y = centerY - meter.label.rect.h / 2; + meter.label.Draw(renderer_); + + x -= MeterGap; + } +} diff --git a/src/storage_indicator.h b/src/storage_indicator.h new file mode 100644 index 0000000..99f484f --- /dev/null +++ b/src/storage_indicator.h @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef STORAGE_INDICATOR_H_ +#define STORAGE_INDICATOR_H_ + +#include "framework/sdl_helper.h" + +#include +#include + +class StorageIndicator +{ +public: + explicit StorageIndicator(SDL_Renderer * renderer); + + void Update(); + void Refresh(); + void Draw(int rightX, int centerY); + +private: + struct Meter + { + std::string name; + std::string path; + double usedFraction = 0; + double freeFraction = 1; + std::string valueText; + Texture label; + Texture value; + }; + + static constexpr Uint32 RefreshMs = 2000; + + SDL_Renderer * renderer_; + std::vector meters_; + int destination_ = 0; + Uint32 lastRefreshMs_ = 0; + + void ReadMeter(Meter & meter, bool isDestination); +}; + +#endif diff --git a/src/theme_manager_app.cpp b/src/theme_manager_app.cpp index b9dd065..6bdf956 100644 --- a/src/theme_manager_app.cpp +++ b/src/theme_manager_app.cpp @@ -75,6 +75,8 @@ ThemeManagerApp::ThemeManagerApp(std::string optionsLocation, AppOptions options badgeA_ = Badge{ Texture("A", 16, renderer_, 0, 0, false, ToAbgr(UiTheme::BadgeLetter), true), Texture(Translate("HINT_SELECT"), 16, renderer_, 0, 0, false, ToAbgr(UiTheme::Text), true), UiTheme::BadgeADark, UiTheme::BadgeA }; badgeRowRightEdge_ = UiTheme::BadgeClusterRightX - UiTheme::BadgeOuterSize - UiTheme::BadgeLabelGap - badgeA_.label.rect.w - UiTheme::BadgeGroupGap; badges_.reset(new BadgePainter(optionsLocation_, renderer_)); + if(options_.showStorage) + storage_.reset(new StorageIndicator(renderer_)); badgeHold_ = Badge{ Texture("B", 16, renderer_, 0, 0, false, ToAbgr(UiTheme::BadgeLetter), true), Texture(Translate("HINT_DELETE"), 16, renderer_, 0, 0, false, ToAbgr(UiTheme::Text), true), UiTheme::BadgeXDark, UiTheme::BadgeX }; ComputePinnedAndThemeRanges(); @@ -254,8 +256,7 @@ int ThemeManagerApp::RunGridLayout() const bool hasCaptions = themeStart_ < pinnedStartIndex_ && !commands_[themeStart_].previewHideLabel; const int GridGap = 20; - // an empty slot stays reserved-width rather than compacted out, so a - // conditional button appearing/disappearing doesn't shift the rest + // empty slots keep their width so the other chips don't shift int stripEnd = themeStart_; while(stripEnd > 0 && commands_[stripEnd-1].command.empty()) --stripEnd; @@ -376,7 +377,7 @@ int ThemeManagerApp::RunGridLayout() const int labelAvail = chipWidthOf[i] - 2*chipPadX - ChipIconW(i); if(c.previewImage.size()) { - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); // smooth - these are small glyphs, nearest looks jagged + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); // nearest looks jagged on small glyphs Texture icon(c.previewImage, renderer_, 0, 0); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); FitCentered(icon, 0, 0, chipIconSize, chipIconSize, 0); @@ -441,8 +442,7 @@ int ThemeManagerApp::RunGridLayout() } }; - // marks the tile of the theme currently applied on the console, so it stays visually - // distinguishable from the rest of the grid regardless of where the cursor is + // the theme applied on the console std::vector isActiveTheme(commands_.size(), false); const std::string activeMarker = options_.titleKey == "INSTALLED_THEMES" ? "/var/lib/clover/profiles/0/hakchi/lastTheme" : options_.selectedMarker; auto RefreshActiveTiles = [&]() @@ -510,6 +510,8 @@ int ThemeManagerApp::RunGridLayout() if(ActivateCommand(commands_[currentCommandId_])) break; RefreshActiveTiles(); + if(storage_) + storage_->Refresh(); } else if(controller_->HeldRepeat(LEFT)) { @@ -552,7 +554,7 @@ int ThemeManagerApp::RunGridLayout() { bool tapped = false; UpdateBButton(tapped); - // quick tap - Back/Exit isn't a chip, B runs it directly + // quick tap runs Back/Exit if(tapped && pinnedStartIndex_ < (int)commands_.size() && ActivateCommand(commands_[pinnedStartIndex_])) break; } @@ -612,6 +614,11 @@ int ThemeManagerApp::RunGridLayout() } } noticeText.Draw(renderer_); + if(storage_) + { + storage_->Update(); + storage_->Draw(GridRight, titleText_.rect.y + titleText_.rect.h / 2); + } { for(int row = 0; row < GridRowsVisible; ++row) @@ -711,7 +718,7 @@ int ThemeManagerApp::RunListLayout() scrollDown.rect.y = UiTheme::ScrollDownY; SDL_Rect selectedRowRect{ UiTheme::ListX, UiTheme::RowFirstY - 2, UiTheme::ListContentRightX - UiTheme::ListX, 0 }; - // B runs Back/Exit directly - same footer badge and behavior as the grid layout + // B runs Back/Exit, as in the grid Badge badgeB{ Texture("B", 16, renderer_, 0, 0, false, ToAbgr(UiTheme::BadgeLetter), true), Texture(pinnedStartIndex_ < (int)commands_.size() ? Translate(commands_[pinnedStartIndex_].name) : "", 16, renderer_, 0, 0, false, ToAbgr(UiTheme::Text), true), UiTheme::BadgeBDark, UiTheme::BadgeB }; @@ -727,7 +734,6 @@ int ThemeManagerApp::RunListLayout() } const int modernRowPitch = std::max(UiTheme::RowPitch, GetTTFLineHeight(RowGlyphSize)); - // Back/Exit no longer takes a row of its own here - B runs it directly via the footer badge above const int DisplayItemCount = std::max(1, (UiTheme::FooterDividerY - UiTheme::ListBottomMargin - UiTheme::RowFirstY) / modernRowPitch); int topListItemNumber = pinnedStartIndex_ + 1; // forces the first SetCurrentCommand to lay out row positions @@ -752,9 +758,7 @@ int ThemeManagerApp::RunListLayout() updateCommandYPos = true; } - // never scroll past the point where the list stops filling the display area - - // otherwise restoring focus on a short list (e.g. after a toggle relaunches this - // screen) scrolls the focused row to the top and hides earlier rows for no reason + // a short list never scrolls, or a restored focus would hide earlier rows const int maxTopListItemNumber = std::max(0, pinnedStartIndex_-DisplayItemCount); if(topListItemNumber > maxTopListItemNumber) { @@ -846,8 +850,7 @@ int ThemeManagerApp::RunListLayout() } else if(controller_->HeldRepeat(UP) && pinnedStartIndex_ > 0) { - // bounded so an all-separator list can't spin forever; stays below - // pinnedStartIndex_ - Back/Exit isn't a row here, B runs it directly + // bounded for an all-separator list; Back/Exit isn't a row int newCommandId = currentCommandId_; for(int tries = 0; tries < pinnedStartIndex_; ++tries) { @@ -871,7 +874,7 @@ int ThemeManagerApp::RunListLayout() { bool tapped = false; UpdateBButton(tapped); - // quick tap - Back/Exit isn't a row you navigate to, B runs it directly (matches the grid layout) + // quick tap runs Back/Exit if(tapped && pinnedStartIndex_ < (int)commands_.size() && ActivateCommand(commands_[pinnedStartIndex_])) break; } diff --git a/src/theme_manager_app.h b/src/theme_manager_app.h index b1119c4..9bfa015 100644 --- a/src/theme_manager_app.h +++ b/src/theme_manager_app.h @@ -12,6 +12,7 @@ #include "command_loader.h" #include "command.h" +#include "storage_indicator.h" #include "framework/badge.h" #include "framework/controller.h" #include "framework/dialog.h" @@ -21,7 +22,6 @@ #include #include -// Owns the SDL/controller session for one screen and runs its grid or list layout loop. class ThemeManagerApp { public: @@ -54,6 +54,7 @@ class ThemeManagerApp Texture gearIcon_, switchOn_, switchOff_; Texture appTitleText_, appVersionText_, titleText_, creditText_; std::unique_ptr badges_; + std::unique_ptr storage_; bool deleteRequested_ = false; Badge badgeA_; Badge badgeHold_; @@ -76,7 +77,7 @@ class ThemeManagerApp bool ActivateCommand(Command & cmd); // true if the caller's loop should break void UpdateBButton(bool & tapped); - void ResumeUnderlyingUi() const; // CONTs the game PauseUI.sh stopped - nothing else does this on power-button exit + void ResumeUnderlyingUi() const; // nothing else resumes it on a power-button exit std::string FocusStatePath() const; void SaveFocusState() const; From b5a79435d0dfffa2b3c4d53c5e2d9c71e041e4c6 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 14 Oct 2026 22:43:05 +0000 Subject: [PATCH 153/210] Write the catalog title for themes installed by All Themes --- mod/etc/options_menu/themes/scripts/om_vars | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index d1de4e8..a1a8957 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -462,8 +462,6 @@ themeGenerator() { translate CHECKING_THEME_FILES echo "" theme_name="${1}" - full_title="$(grep -F 'NAME=' "${om_mod_sub_cmds}/c0010_${theme_name}" | sed -e 's/.*=\(.*\)\[.*/\1/')" - full_path="${theme_path}/${theme_name}" cd "${theme_path}" || exit if [ ! -x "$om_scripts/downloader" ]; then translate DOWNLOADER_MISSING_LINE1 @@ -485,10 +483,7 @@ themeGenerator() { else downloadTheme "$theme_name" rc=$? - if [ "$rc" = 0 ]; then - size="$(du -sh "${full_path}" | cut -f1)" - echo "NAME=${full_title}[${size}b]" > "${full_path}/info" - fi + [ "$rc" = 0 ] && writeThemeInfo "$theme_name" fi removeRegionalTheme @@ -517,6 +512,13 @@ urlEncodeName() { printf '%s' "$1" | sed 's/%/%25/g; s/#/%23/g; s/?/%3F/g' } +writeThemeInfo() { + info_cmd="${om_mod_sub_cmds}/c0010_$1" + [ -f "$info_cmd" ] || return 0 + info_title="$(grep -F 'NAME=' "$info_cmd" | sed -e 's/.*=\(.*\)\[.*/\1/')" + echo "NAME=${info_title}[$(du -sh "$theme_path/$1" | cut -f1)b]" > "$theme_path/$1/info" +} + remoteTarball() { echo "${system_name}.$(urlEncodeName "$1").tar.gz" } @@ -552,7 +554,7 @@ installDownloaded() { t="${t%.tar.gz}" t_rc="$1" if [ "$t_rc" = 0 ]; then - unpackTheme "$t" + unpackTheme "$t" && writeThemeInfo "$t" t_rc=$? fi case "$t_rc" in From 19d341838e83589e24cc8b6d220a8d4ec1a60288 Mon Sep 17 00:00:00 2001 From: DefKorns Date: Wed, 14 Oct 2026 23:08:39 +0000 Subject: [PATCH 154/210] Show a download splash with a progress bar and return on success Single themes now install behind a splash with the theme name, its author and a progress bar, and the screen goes back to the list by itself when the install succeeds. All Themes keeps the text screen and lists each theme as it is installed. --- Makefile | 2 +- .../downloads/scripts/om_clearDownloads | 2 +- .../themes/downloads/scripts/om_refreshThemes | 2 +- mod/etc/options_menu/themes/lang/de-DE.lang | 1 + mod/etc/options_menu/themes/lang/en-US.lang | 1 + mod/etc/options_menu/themes/lang/es-ES.lang | 1 + mod/etc/options_menu/themes/lang/fr-FR.lang | 1 + mod/etc/options_menu/themes/lang/it-IT.lang | 1 + mod/etc/options_menu/themes/lang/ja-JP.lang | 1 + mod/etc/options_menu/themes/lang/nl-NL.lang | 1 + mod/etc/options_menu/themes/lang/pt-PT.lang | 1 + mod/etc/options_menu/themes/lang/ru-RU.lang | 1 + .../options_menu/themes/scripts/om_getThemes | 2 +- mod/etc/options_menu/themes/scripts/om_vars | 4 + src/command_loader.cpp | 4 +- src/command_loader.h | 2 +- src/download_screen.cpp | 253 ++++++++++++++++++ src/download_screen.h | 23 ++ src/theme_manager_app.cpp | 10 +- 19 files changed, 305 insertions(+), 8 deletions(-) create mode 100644 src/download_screen.cpp create mode 100644 src/download_screen.h diff --git a/Makefile b/Makefile index c41559e..9f678e6 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ endif CXXFLAGS = -std=c++11 -Os -Ivendor/OptionsMenu/src $(SDL_CFLAGS) -DMOD_VERSION=\"v$(MOD_VER)\" LDLIBS = $(SDL_LIBS) VENDOR_SRC_DIR = vendor/OptionsMenu/src -SOURCES = src/main.cpp src/single_instance_lock.cpp src/command_loader.cpp src/texture_utils.cpp src/theme_manager_app.cpp src/folder_theme_model.cpp src/folder_themes_layout.cpp src/storage_indicator.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp $(FRAMEWORK_DIR)/badge.cpp $(FRAMEWORK_DIR)/dialog.cpp +SOURCES = src/main.cpp src/single_instance_lock.cpp src/command_loader.cpp src/texture_utils.cpp src/theme_manager_app.cpp src/folder_theme_model.cpp src/folder_themes_layout.cpp src/storage_indicator.cpp src/download_screen.cpp $(VENDOR_SRC_DIR)/command.cpp $(VENDOR_SRC_DIR)/localization.cpp $(FRAMEWORK_DIR)/sdl_context.cpp $(FRAMEWORK_DIR)/texture.cpp $(FRAMEWORK_DIR)/controller.cpp $(FRAMEWORK_DIR)/powerwatch.cpp $(FRAMEWORK_DIR)/draw_helpers.cpp $(FRAMEWORK_DIR)/utf8.cpp $(FRAMEWORK_DIR)/font8x8_lookup.cpp $(FRAMEWORK_DIR)/uitheme.cpp $(FRAMEWORK_DIR)/badge.cpp $(FRAMEWORK_DIR)/dialog.cpp OBJECTS = $(SOURCES:.cpp=.o) all: hmod diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads index 2ecda24..cfb511c 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads +++ b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads @@ -23,4 +23,4 @@ script_init clearDownloads remove "$sub_dummy" -$options_menu/lib/theme_manager --commandPath $om_mod_sub_cmds/ --scriptPath $om_mod_sub_scripts --title AVAILABLE_THEMES --storage & +$options_menu/lib/theme_manager --commandPath $om_mod_sub_cmds/ --scriptPath $om_mod_sub_scripts --title AVAILABLE_THEMES --downloads & diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes index 154e992..2678b68 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes +++ b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes @@ -22,4 +22,4 @@ script_init getThemeList -$options_menu/lib/theme_manager --commandPath $om_mod_sub_cmds/ --scriptPath $om_mod_sub_scripts --title AVAILABLE_THEMES --storage & +$options_menu/lib/theme_manager --commandPath $om_mod_sub_cmds/ --scriptPath $om_mod_sub_scripts --title AVAILABLE_THEMES --downloads & diff --git a/mod/etc/options_menu/themes/lang/de-DE.lang b/mod/etc/options_menu/themes/lang/de-DE.lang index 63e1843..4a154cf 100644 --- a/mod/etc/options_menu/themes/lang/de-DE.lang +++ b/mod/etc/options_menu/themes/lang/de-DE.lang @@ -66,6 +66,7 @@ DIY_DRAFT_NOTICE=Eigenes Theme nicht gespeichert: öffne Eigenes Theme zum Speic CATEGORY_UI_MOD=Kategorie: Benutzeroberflächen-Mod CHECKING_THEME_FILES=Dateien des ausgewählten Themes werden geprüft. DOWNLOADING=Wird heruntergeladen... +INSTALLING_THEME=Installiere INSTALLATION_COMPLETE=Installation abgeschlossen. ALL_THEMES_INSTALLED=Alle Themen sind bereits installiert, nichts herunterzuladen. NOT_ENOUGH_SPACE=Nicht genügend freier Speicher, gib Speicher frei und versuche es erneut. diff --git a/mod/etc/options_menu/themes/lang/en-US.lang b/mod/etc/options_menu/themes/lang/en-US.lang index dac6b7a..b29b3c3 100644 --- a/mod/etc/options_menu/themes/lang/en-US.lang +++ b/mod/etc/options_menu/themes/lang/en-US.lang @@ -66,6 +66,7 @@ DIY_DRAFT_NOTICE=Unsaved custom theme: open DIY Theme to save it, or it's discar CATEGORY_UI_MOD=Category: User Interface Mod CHECKING_THEME_FILES=Checking selected theme files. DOWNLOADING=Downloading... +INSTALLING_THEME=Installing INSTALLATION_COMPLETE=Installation complete. ALL_THEMES_INSTALLED=All themes are already installed, nothing to download. NOT_ENOUGH_SPACE=Not enough free space, free some space and try again. diff --git a/mod/etc/options_menu/themes/lang/es-ES.lang b/mod/etc/options_menu/themes/lang/es-ES.lang index 2cdf298..91d54f9 100644 --- a/mod/etc/options_menu/themes/lang/es-ES.lang +++ b/mod/etc/options_menu/themes/lang/es-ES.lang @@ -66,6 +66,7 @@ DIY_DRAFT_NOTICE=Tema personalizado sin guardar: abre Tema personalizado para gu CATEGORY_UI_MOD=Categoría: Mod de interfaz CHECKING_THEME_FILES=Comprobando los archivos del tema seleccionado. DOWNLOADING=Descargando... +INSTALLING_THEME=Instalando INSTALLATION_COMPLETE=Instalación completada. ALL_THEMES_INSTALLED=Todos los temas ya están instalados, no hay nada que descargar. NOT_ENOUGH_SPACE=No hay suficiente espacio libre, libera espacio e inténtalo de nuevo. diff --git a/mod/etc/options_menu/themes/lang/fr-FR.lang b/mod/etc/options_menu/themes/lang/fr-FR.lang index b910854..030b926 100644 --- a/mod/etc/options_menu/themes/lang/fr-FR.lang +++ b/mod/etc/options_menu/themes/lang/fr-FR.lang @@ -66,6 +66,7 @@ DIY_DRAFT_NOTICE=Thème personnalisé non enregistré : ouvrez Thème personnali CATEGORY_UI_MOD=Catégorie : Mod d'interface CHECKING_THEME_FILES=Vérification des fichiers du thème sélectionné. DOWNLOADING=Téléchargement... +INSTALLING_THEME=Installation de INSTALLATION_COMPLETE=Installation terminée. ALL_THEMES_INSTALLED=Tous les thèmes sont déjà installés, rien à télécharger. NOT_ENOUGH_SPACE=Espace libre insuffisant, libérez de l'espace et réessayez. diff --git a/mod/etc/options_menu/themes/lang/it-IT.lang b/mod/etc/options_menu/themes/lang/it-IT.lang index 22259f5..9f3d4ea 100644 --- a/mod/etc/options_menu/themes/lang/it-IT.lang +++ b/mod/etc/options_menu/themes/lang/it-IT.lang @@ -66,6 +66,7 @@ DIY_DRAFT_NOTICE=Tema personalizzato non salvato: apri Tema personalizzato per s CATEGORY_UI_MOD=Categoria: Mod interfaccia utente CHECKING_THEME_FILES=Controllo dei file del tema selezionato. DOWNLOADING=Download in corso... +INSTALLING_THEME=Installazione di INSTALLATION_COMPLETE=Installazione completata. ALL_THEMES_INSTALLED=Tutti i temi sono già installati, niente da scaricare. NOT_ENOUGH_SPACE=Spazio libero insufficiente, libera spazio e riprova. diff --git a/mod/etc/options_menu/themes/lang/ja-JP.lang b/mod/etc/options_menu/themes/lang/ja-JP.lang index de51e3a..6aa545e 100644 --- a/mod/etc/options_menu/themes/lang/ja-JP.lang +++ b/mod/etc/options_menu/themes/lang/ja-JP.lang @@ -66,6 +66,7 @@ DIY_DRAFT_NOTICE=じぶんのThemeがほぞんされていません. さいき CATEGORY_UI_MOD=カテゴリ: UI Mod CHECKING_THEME_FILES=えらんだThemeのFileをかくにんしています DOWNLOADING=downloadしています... +INSTALLING_THEME=インストール中: INSTALLATION_COMPLETE=インストールかんりょう ALL_THEMES_INSTALLED=すべてのThemeはインストールずみです. downloadするものはありません NOT_ENOUGH_SPACE=あきようりょうがたりません. ようりょうをあけて, もういちどおためしください. diff --git a/mod/etc/options_menu/themes/lang/nl-NL.lang b/mod/etc/options_menu/themes/lang/nl-NL.lang index 6a961d3..eeb92e4 100644 --- a/mod/etc/options_menu/themes/lang/nl-NL.lang +++ b/mod/etc/options_menu/themes/lang/nl-NL.lang @@ -66,6 +66,7 @@ DIY_DRAFT_NOTICE=Eigen thema niet opgeslagen: open Eigen thema om het op te slaa CATEGORY_UI_MOD=Categorie: Interface-mod CHECKING_THEME_FILES=Bestanden van het gekozen thema worden gecontroleerd. DOWNLOADING=Downloaden... +INSTALLING_THEME=Installeren: INSTALLATION_COMPLETE=Installatie voltooid. ALL_THEMES_INSTALLED=Alle thema's zijn al geïnstalleerd, er is niets te downloaden. NOT_ENOUGH_SPACE=Onvoldoende vrije ruimte, maak ruimte vrij en probeer het opnieuw. diff --git a/mod/etc/options_menu/themes/lang/pt-PT.lang b/mod/etc/options_menu/themes/lang/pt-PT.lang index 42ce7e1..cd2196b 100644 --- a/mod/etc/options_menu/themes/lang/pt-PT.lang +++ b/mod/etc/options_menu/themes/lang/pt-PT.lang @@ -66,6 +66,7 @@ DIY_DRAFT_NOTICE=Tema personalizado por gravar: abre Tema Personalizado para o g CATEGORY_UI_MOD=Categoria: Mod de Interface CHECKING_THEME_FILES=A verificar os ficheiros do tema selecionado. DOWNLOADING=A descarregar... +INSTALLING_THEME=A instalar INSTALLATION_COMPLETE=Instalação concluída. ALL_THEMES_INSTALLED=Todos os temas já estão instalados, não há nada para descarregar. NOT_ENOUGH_SPACE=Não há espaço livre suficiente, liberta algum espaço e tenta novamente. diff --git a/mod/etc/options_menu/themes/lang/ru-RU.lang b/mod/etc/options_menu/themes/lang/ru-RU.lang index 181964a..8e7c964 100644 --- a/mod/etc/options_menu/themes/lang/ru-RU.lang +++ b/mod/etc/options_menu/themes/lang/ru-RU.lang @@ -66,6 +66,7 @@ DIY_DRAFT_NOTICE=Своя тема не сохранена: откройте « CATEGORY_UI_MOD=Категория: Мод интерфейса CHECKING_THEME_FILES=Проверка файлов выбранной темы. DOWNLOADING=Загрузка... +INSTALLING_THEME=Установка INSTALLATION_COMPLETE=Установка завершена. ALL_THEMES_INSTALLED=Все темы уже установлены, загружать нечего. NOT_ENOUGH_SPACE=Недостаточно свободного места, освободите место и повторите попытку. diff --git a/mod/etc/options_menu/themes/scripts/om_getThemes b/mod/etc/options_menu/themes/scripts/om_getThemes index bfb5ed7..3b929ab 100644 --- a/mod/etc/options_menu/themes/scripts/om_getThemes +++ b/mod/etc/options_menu/themes/scripts/om_getThemes @@ -22,4 +22,4 @@ script_init [ ! -f "$sub_dummy" ] && getThemeList -$options_menu/lib/theme_manager --commandPath $om_mod_sub_cmds/ --scriptPath $om_mod_sub_scripts --title AVAILABLE_THEMES --storage & +$options_menu/lib/theme_manager --commandPath $om_mod_sub_cmds/ --scriptPath $om_mod_sub_scripts --title AVAILABLE_THEMES --downloads & diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index a1a8957..9e1237d 100644 --- a/mod/etc/options_menu/themes/scripts/om_vars +++ b/mod/etc/options_menu/themes/scripts/om_vars @@ -496,7 +496,9 @@ themeGenerator() { echo "" translate THEMES_FAILED echo "$failed" + return 1 fi + return "$rc" } freeSpaceKb() { @@ -513,6 +515,7 @@ urlEncodeName() { } writeThemeInfo() { + info_title="$1" info_cmd="${om_mod_sub_cmds}/c0010_$1" [ -f "$info_cmd" ] || return 0 info_title="$(grep -F 'NAME=' "$info_cmd" | sed -e 's/.*=\(.*\)\[.*/\1/')" @@ -557,6 +560,7 @@ installDownloaded() { unpackTheme "$t" && writeThemeInfo "$t" t_rc=$? fi + [ "$t_rc" = 0 ] && echo "$info_title" case "$t_rc" in 0) ;; 3) downloadError 3 && return 3 ;; diff --git a/src/command_loader.cpp b/src/command_loader.cpp index 3eaf310..0195966 100644 --- a/src/command_loader.cpp +++ b/src/command_loader.cpp @@ -145,8 +145,8 @@ AppOptions ParseArgs(int argc, char * argv[], const std::string & optionsLocatio options.folderMusicScreen = std::strcmp(argv[++i], "music") == 0; else if(std::strcmp(argv[i], "--selectedMarker") == 0 && i+1 < argc) options.selectedMarker = argv[++i]; - else if(std::strcmp(argv[i], "--storage") == 0) - options.showStorage = true; + else if(std::strcmp(argv[i], "--downloads") == 0) + options.downloadsScreen = true; } if(!options.commandLocation.empty() && options.commandLocation.back() != '/') options.commandLocation += '/'; diff --git a/src/command_loader.h b/src/command_loader.h index 20d2112..e8b898b 100644 --- a/src/command_loader.h +++ b/src/command_loader.h @@ -27,7 +27,7 @@ struct AppOptions std::string folderMusicMapping; bool folderMusicScreen = false; std::string selectedMarker; - bool showStorage = false; + bool downloadsScreen = false; }; // each screen remembers its last focused row here; cleared on leaving the theme manager diff --git a/src/download_screen.cpp b/src/download_screen.cpp new file mode 100644 index 0000000..bac145e --- /dev/null +++ b/src/download_screen.cpp @@ -0,0 +1,253 @@ +/** + * Copyright (c) 2026 DefKorns (https://defkorns.github.io/LICENSE) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#include "download_screen.h" +#include "texture_utils.h" +#include "framework/draw_helpers.h" +#include "localization.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + constexpr int PollTimeoutMs = 20; + constexpr Uint32 AutoCloseMs = 1500; + constexpr int TitleSize = 28; + constexpr int AuthorSize = 16; + constexpr int DetailSize = 14; + constexpr int MessageSize = 16; + constexpr int BarW = 640; + constexpr int BarH = 16; + constexpr size_t MaxMessages = 4; + + class ChildProcess + { + public: + explicit ChildProcess(const std::string & command) + { + int fds[2]; + if(pipe(fds) != 0) + return; + pid_ = fork(); + if(pid_ < 0) + { + close(fds[0]); + close(fds[1]); + return; + } + if(pid_ == 0) + { + setpgid(0, 0); + dup2(fds[1], STDOUT_FILENO); + close(fds[0]); + close(fds[1]); + execl("/bin/sh", "sh", "-c", command.c_str(), static_cast(nullptr)); + _exit(127); + } + setpgid(pid_, pid_); + close(fds[1]); + out_ = fdopen(fds[0], "r"); + if(!out_) + close(fds[0]); + } + + ~ChildProcess() { Finish(true); } + + ChildProcess(const ChildProcess &) = delete; + ChildProcess & operator=(const ChildProcess &) = delete; + + FILE * Output() const { return out_; } + + // exit status, or -1 when cancelled or not started + int Finish(bool cancel) + { + if(pid_ <= 0) + return -1; + if(cancel) + kill(-pid_, SIGTERM); + if(out_) + fclose(out_); + out_ = nullptr; + int status = 0; + waitpid(pid_, &status, 0); + pid_ = -1; + return !cancel && WIFEXITED(status) ? WEXITSTATUS(status) : -1; + } + + private: + pid_t pid_ = -1; + FILE * out_ = nullptr; + }; + + // a trailing & would hand back the shell's status instead of the script's + std::string Foreground(std::string command) + { + while(!command.empty() && (command.back() == ' ' || command.back() == '&')) + command.pop_back(); + return command; + } + + // "