diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..62ea6ad --- /dev/null +++ b/.editorconfig @@ -0,0 +1,23 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 + +# shell scripts, command files and translations +[{mod/**,*.sh}] +indent_size = 2 + +[*.{yml,yaml,json}] +indent_size = 2 + +[{Makefile,*.mk}] +indent_style = tab + +# two trailing spaces are a line break +[*.md] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..29b79f9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,8 @@ +# 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 +*.png binary +*.lua binary +*.wav binary diff --git a/.github/check_style.sh b/.github/check_style.sh new file mode 100755 index 0000000..b60f3a4 --- /dev/null +++ b/.github/check_style.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# check_style.sh : the lines added since follow .editorconfig +base="${1:-origin/master}" +failed=0 + +error() { + echo "::error file=$1::$2" + echo "$1: $2" + failed=1 +} + +git diff --check "$base...HEAD" -- . ':!*.md' || failed=1 + +files=$(git diff --name-only --diff-filter=ACMR "$base...HEAD") +for file in $files; do + [ -f "$file" ] || continue + case "$file" in + *.png | *.ttf | *.hmod | *.xz | *.pem) continue ;; + esac + added=$(git diff -U0 "$base...HEAD" -- "$file" | grep '^+' | grep -v '^+++' || true) + [ -n "$added" ] || continue + printf '%s\n' "$added" | grep -q "$(printf '\r')" && error "$file" "CRLF line endings; use LF" + case "$file" in + Makefile | *.mk | *.md | .gitmodules) ;; + *) printf '%s\n' "$added" | grep -q "^+$(printf '\t')" && error "$file" "indented with tabs; use spaces (see .editorconfig)" ;; + esac + [ -n "$(tail -c1 "$file")" ] && error "$file" "missing newline at the end of the file" +done + +exit "$failed" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..c5e65d1 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,17 @@ +name: Build + +on: + push: + branches: ["**"] + tags: ["v*"] + pull_request: + +jobs: + hmod: + uses: DefKorns/hmod-build/.github/workflows/hmod.yml@v1.5.0 + with: + release-name: Theme Selector + docker: true + secrets: inherit + permissions: + contents: write 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/.github/workflows/style.yml b/.github/workflows/style.yml new file mode 100644 index 0000000..c565494 --- /dev/null +++ b/.github/workflows/style.yml @@ -0,0 +1,17 @@ +name: Style + +# checks only the lines a pull request adds, so older files never fail a PR they didn't touch +on: + pull_request: + +jobs: + changed-lines: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Trailing whitespace, line endings and indentation + env: + BASE: origin/${{ github.base_ref }} + run: sh .github/check_style.sh "$BASE" diff --git a/.gitignore b/.gitignore index f092d53..e17aa43 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ *.o [Dd]ebug*/ [Rr]elease*/ +!release-notes/ *.mode* *.app/ *.pyc @@ -498,27 +499,9 @@ out/ default-strings.lng default/ temp/ -mod/etc/options_menu/themes/scripts/b8024_themeselector -mod-/ -out--/ -mod/etc/options_menu/themes-old/ -modules/ -mod/etc/options_menu/themes/scripts/scripts/* -mod/etc/options_menu/themes/scripts/80s_neon* -mod/etc/options_menu/themes/scripts/ghost* - -mod/etc/options_menu/themes/scripts/images/ -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 -mod/etc/options_menu/p8025_fontfix -old.themescript.sh -out---backup/ -BACKUP.gpg -out---/ +mod/etc/options_menu/lib/ +.deps/ + +# local-only files (console dumps, test hmods) +local/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..106fc89 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,10 @@ +[submodule "vendor/OptionsMenu"] + path = vendor/OptionsMenu + url = https://github.com/DefKorns/OptionsMenu + branch = master +[submodule "hmod-build"] + path = vendor/hmod-build + url = https://github.com/DefKorns/hmod-build.git +[submodule "vendor/pngpack"] + path = vendor/pngpack + url = https://github.com/DefKorns/pngpack.git diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..ddf86be --- /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", + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 392d6fa..29a768e 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, @@ -29,4 +21,6 @@ "editor.tabSize": 2, }, "diffEditor.ignoreTrimWhitespace": false, -} \ No newline at end of file + "shellcheck.useWorkspaceRootAsCwd": true, + "cmake.sourceDirectory": "E:/Documents/_projects/hmods/om_theme-selector/vendor/pngpack/jsoncpp", +} diff --git a/Makefile b/Makefile index 6b8fde2..bd8a005 100644 --- a/Makefile +++ b/Makefile @@ -1,105 +1,64 @@ -SHELL = /bin/sh -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]+') - -# 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=`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 ) -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 - -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/ - -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 - @echo $(NEXT_PATCH_VERSION) > VERSION - -rc: hmod - @echo $(NEXT_RC_VERSION) > VERSION - -update: all - @echo $(NEXT_MINOR_VERSION) > VERSION - -upgrade: all - @echo $(NEXT_MAJOR_VERSION) > VERSION - -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)" - @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)" +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/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. +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++14 -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)/download_screen.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 $(FRAMEWORK_DIR)/list_screen.cpp $(FRAMEWORK_DIR)/text_layout.cpp $(FRAMEWORK_DIR)/close_on_exec.cpp +OBJECTS = $(SOURCES:.cpp=.o) + +all: hmod + +compile: $(MOD_DEPS) + +mod/etc/options_menu/lib/theme_manager: $(OBJECTS) + mkdir -p $(@D) + $(CROSS_PREFIX)$(CXX) $(OBJECTS) $(LDLIBS) $(LDFLAGS) -Wl,-rpath,/etc/options_menu/lib -o $@ + $(CROSS_PREFIX)$(STRIP) $@ + +DEPDIR = .deps + +%.o: %.cpp + @mkdir -p $(DEPDIR) + $(CROSS_PREFIX)$(CXX) $(CXXFLAGS) -MMD -MP -MF $(DEPDIR)/$(subst /,_,$*).d -c $< -o $@ + +-include $(wildcard $(DEPDIR)/*.d) + +# 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. +mod/bin/sprite_crop: vendor/pngpack/pngpacker/sprite_crop.cpp vendor/pngpack/pngpacker/pixeltools.cpp + mkdir -p $(@D) + $(MAKE) -C vendor/pngpack build/sprite_crop CROSS=$(CROSS_PREFIX) LDFLAGS='-Wl,--gc-sections -Wl,-rpath,/etc/options_menu/lib' + cp vendor/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 "./vendor/*" -delete + rm -rf $(DEPDIR) + rm -f $(MOD_DEPS) + rm -rf out/ +include vendor/hmod-build/hmod.mk -.PHONY: clean +.PHONY: all compile update-headers clean diff --git a/README.md b/README.md index 11be108..1d71e0c 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,119 @@ # 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 +[![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/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) (**Off by default**) -* 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://raw.githubusercontent.com/wiki/DefKorns/om_theme-selector/images/demo_video.png)](https://youtu.be/3UilWr1NvFA "Theme Selector - Demo") + +## What is it? + +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. + +Full guides are on the **[wiki](https://github.com/DefKorns/om_theme-selector/wiki)**. + +## Features + +- **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 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) +- 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`) +- Every console and region: NES, Famicom, Famicom Shonen Jump, SNES (USA/EUR) and Super Famicom + +## Requirements + +- [Hakchi2 CE](https://github.com/TeamShinkansen/hakchi2/releases/latest) +- [My Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases) v2.1.0 or newer — the base UI this mod plugs into +- Hakchi Wi-Fi mod (WPA Supplicant), from KMFDManic's store in hakchi — only to download themes from the console + - Recommended adapter: TP-Link TL-WN725N (Realtek 8188 chipset). Other adapters may work, but aren't guaranteed to. +- Font Remount mod (`font_remount`), one of hakchi's built-in mods — applies the theme fonts +- A USB/SD drive is recommended: themes are often 5–30 MB each + +## Install -### 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. +**From my Mod Hub (recommended):** in hakchi choose **Modules → Manage mod repositories** and add `https://defkorns.github.io/hakchi-repo/`. Then open **Modules → DefKorns Store** and install **Options Menu** (from the *Options Menu* tab) and **Options Menu - Theme Selector** (from the *Options Menu - Addons* tab). Updates show up there automatically. -### 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. +**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**). -### 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`. +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). -### 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. +## Getting themes -### I own a Famicom/Shonen/Super Famicom, can i install this? -The mod is compatible with all Nintendo Classic consoles including all regions. +**From the console** (needs the Wi-Fi mod): -### 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. +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**. A progress bar shows the download, and you're back in the list once the theme is installed. +3. Back in **Installed Themes**, select the new theme and press **A** to apply it. The console reboots with it. -### 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) +**All SNES Themes** / **All NES Themes** downloads the whole catalog at once, with a log of each theme. It's several hundred MB, so it only shows up when a USB/SD drive is connected. -### How do i use it +**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 open **Themes**: packages are unpacked and new themes added on their own. 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. -**NAND or USB/SD** +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). -- Install Hmod -- Download the themes and enjoy +## Where things live -**NAND** themes will be on `/var/lib/hakchi/usr/share/themes/your-system` +| | 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` | -**USB/SD** themes will be on `/media/hakchi/themes/your-system` +`` is `nes`, `snes` or `shonen`. With a USB/SD drive connected, themes always go there. -> **Note:** Where `your-system` changes depending on own mini `nes`, `snes` or `shonen` +## Customizing the look -### Notes -Enjoy it as much as i did making it. +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. -### Credits -- [DefKorns](https://gitlab.com/DefKorns) -- [DanTheMan827](https://github.com/DanTheMan827) (packed.png extractor) +## Building from source + +```sh +git clone --recursive https://github.com/DefKorns/om_theme-selector.git +cd om_theme-selector +./build.sh # cross-compiles theme_manager and sprite_crop, then packages out/om_theme-selector.hmod +./build.sh compile # binaries only +``` + +`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). + +## Contributing + +Bug reports and ideas are welcome in the [issues](https://github.com/DefKorns/om_theme-selector/issues), and so are pull requests: + +- **Formatting:** [`.editorconfig`](.editorconfig) sets it: UTF-8, LF line endings, a final newline, no trailing whitespace, 4 spaces for C/C++, 2 for shell scripts, tabs only in the Makefile. +- **Only change what you need:** don't reformat, re-indent or strip whitespace in lines you aren't changing. The **Style** check on pull requests looks only at the lines you add. +- Everything else follows the Options Menu's [code style](https://github.com/DefKorns/OptionsMenu#code-style). + +## Credits + +- [DefKorns](https://github.com/DefKorns) +- [DanTheMan827](https://github.com/DanTheMan827) — packed.png extractor +- Theme authors — credited on each theme in the download catalog + +## Thanks -### Thanks - AluCarD +- Aranthys +- AxelStone - [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/VERSION b/VERSION deleted file mode 100644 index 227cea2..0000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -2.0.0 diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..d51ef33 --- /dev/null +++ b/build.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +[ -f vendor/hmod-build/docker-make.sh ] || git submodule update --init vendor/hmod-build +[ -f vendor/OptionsMenu/src/framework/sdl_context.cpp ] || git submodule update --init --recursive vendor/OptionsMenu + +sh vendor/hmod-build/docker-make.sh "$@" + +ls -la mod/etc/options_menu/lib/theme_manager out/*.hmod 2>/dev/null diff --git a/mod/etc/options_menu/commands/c0075_ThemeOptions b/mod/etc/options_menu/commands/c0075_ThemeOptions new file mode 100644 index 0000000..af412d7 --- /dev/null +++ b/mod/etc/options_menu/commands/c0075_ThemeOptions @@ -0,0 +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 diff --git a/mod/etc/options_menu/commands/c0090_ThemeOptions b/mod/etc/options_menu/commands/c0090_ThemeOptions deleted file mode 100644 index 85888a1..0000000 --- a/mod/etc/options_menu/commands/c0090_ThemeOptions +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=Themes -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 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/images/advanced.png b/mod/etc/options_menu/images/advanced.png deleted file mode 100644 index 291cd8f..0000000 Binary files a/mod/etc/options_menu/images/advanced.png and /dev/null differ diff --git a/mod/etc/options_menu/images/audio.png b/mod/etc/options_menu/images/audio.png deleted file mode 100644 index dfbd0c9..0000000 Binary files a/mod/etc/options_menu/images/audio.png and /dev/null differ diff --git a/mod/etc/options_menu/images/audioall.png b/mod/etc/options_menu/images/audioall.png deleted file mode 100644 index cb5648a..0000000 Binary files a/mod/etc/options_menu/images/audioall.png and /dev/null differ diff --git a/mod/etc/options_menu/images/audiohome.png b/mod/etc/options_menu/images/audiohome.png deleted file mode 100644 index c2df812..0000000 Binary files a/mod/etc/options_menu/images/audiohome.png and /dev/null differ diff --git a/mod/etc/options_menu/images/back.png b/mod/etc/options_menu/images/back.png deleted file mode 100644 index 9ae7d1b..0000000 Binary files a/mod/etc/options_menu/images/back.png and /dev/null differ diff --git a/mod/etc/options_menu/images/clear.png b/mod/etc/options_menu/images/clear.png deleted file mode 100644 index 9169a66..0000000 Binary files a/mod/etc/options_menu/images/clear.png and /dev/null differ diff --git a/mod/etc/options_menu/images/diy.png b/mod/etc/options_menu/images/diy.png deleted file mode 100644 index 053f281..0000000 Binary files a/mod/etc/options_menu/images/diy.png and /dev/null differ diff --git a/mod/etc/options_menu/images/download.png b/mod/etc/options_menu/images/download.png deleted file mode 100644 index 999c901..0000000 Binary files a/mod/etc/options_menu/images/download.png and /dev/null differ diff --git a/mod/etc/options_menu/images/exit.png b/mod/etc/options_menu/images/exit.png deleted file mode 100644 index d93f950..0000000 Binary files a/mod/etc/options_menu/images/exit.png and /dev/null differ 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 67cfd60..0000000 Binary files a/mod/etc/options_menu/images/loading/downloadtheme.png and /dev/null differ 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 a96a8a0..0000000 Binary files a/mod/etc/options_menu/images/loading/downloadthemelist.png and /dev/null differ 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 417fd06..0000000 Binary files a/mod/etc/options_menu/images/loading/loading.png and /dev/null differ 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 9f253eb..0000000 Binary files a/mod/etc/options_menu/images/loading/loadingassets.png and /dev/null differ diff --git a/mod/etc/options_menu/images/loading/saving.png b/mod/etc/options_menu/images/loading/saving.png deleted file mode 100644 index 3f82a64..0000000 Binary files a/mod/etc/options_menu/images/loading/saving.png and /dev/null differ 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 d7f08cd..0000000 Binary files a/mod/etc/options_menu/images/loading/themepreview.png and /dev/null differ diff --git a/mod/etc/options_menu/images/luigi.png b/mod/etc/options_menu/images/luigi.png deleted file mode 100644 index 900dae3..0000000 Binary files a/mod/etc/options_menu/images/luigi.png and /dev/null differ diff --git a/mod/etc/options_menu/images/mario.png b/mod/etc/options_menu/images/mario.png deleted file mode 100644 index b99b3bd..0000000 Binary files a/mod/etc/options_menu/images/mario.png and /dev/null differ diff --git a/mod/etc/options_menu/images/packed.png b/mod/etc/options_menu/images/packed.png deleted file mode 100644 index 34220da..0000000 Binary files a/mod/etc/options_menu/images/packed.png and /dev/null differ 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 e25e806..0000000 Binary files a/mod/etc/options_menu/images/packed_nes.png and /dev/null differ diff --git a/mod/etc/options_menu/images/pole.png b/mod/etc/options_menu/images/pole.png deleted file mode 100644 index e0e7322..0000000 Binary files a/mod/etc/options_menu/images/pole.png and /dev/null differ 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 0000000..d24504b Binary files /dev/null and b/mod/etc/options_menu/images/preview_assign_music.png differ 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 0000000..02bf7d9 Binary files /dev/null and b/mod/etc/options_menu/images/preview_audio_randomizer_all.png differ diff --git a/mod/etc/options_menu/images/preview_audio_randomizer_home.png b/mod/etc/options_menu/images/preview_audio_randomizer_home.png new file mode 100644 index 0000000..e31e023 Binary files /dev/null and b/mod/etc/options_menu/images/preview_audio_randomizer_home.png differ diff --git a/mod/etc/options_menu/images/preview_clean_up.png b/mod/etc/options_menu/images/preview_clean_up.png new file mode 100644 index 0000000..0564b67 Binary files /dev/null and b/mod/etc/options_menu/images/preview_clean_up.png differ 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_music_per_folder.png b/mod/etc/options_menu/images/preview_music_per_folder.png new file mode 100644 index 0000000..3e452e4 Binary files /dev/null and b/mod/etc/options_menu/images/preview_music_per_folder.png differ diff --git a/mod/etc/options_menu/images/preview_randtheme.png b/mod/etc/options_menu/images/preview_randtheme.png new file mode 100644 index 0000000..ff72c56 Binary files /dev/null and b/mod/etc/options_menu/images/preview_randtheme.png differ 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 0000000..aacd961 Binary files /dev/null and b/mod/etc/options_menu/images/preview_theme_options.png differ 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 0000000..20d2920 Binary files /dev/null and b/mod/etc/options_menu/images/preview_theme_randomizer.png differ diff --git a/mod/etc/options_menu/images/preview_uninstall.png b/mod/etc/options_menu/images/preview_uninstall.png new file mode 100644 index 0000000..f0101ae Binary files /dev/null and b/mod/etc/options_menu/images/preview_uninstall.png differ diff --git a/mod/etc/options_menu/images/randtheme.png b/mod/etc/options_menu/images/randtheme.png deleted file mode 100644 index b1da58e..0000000 Binary files a/mod/etc/options_menu/images/randtheme.png and /dev/null differ diff --git a/mod/etc/options_menu/images/reboot.png b/mod/etc/options_menu/images/reboot.png deleted file mode 100644 index f40fa29..0000000 Binary files a/mod/etc/options_menu/images/reboot.png and /dev/null differ diff --git a/mod/etc/options_menu/images/retroarch.png b/mod/etc/options_menu/images/retroarch.png deleted file mode 100644 index 93e9d57..0000000 Binary files a/mod/etc/options_menu/images/retroarch.png and /dev/null differ diff --git a/mod/etc/options_menu/images/sbm.png b/mod/etc/options_menu/images/sbm.png deleted file mode 100644 index 3249e6a..0000000 Binary files a/mod/etc/options_menu/images/sbm.png and /dev/null differ diff --git a/mod/etc/options_menu/images/settings.png b/mod/etc/options_menu/images/settings.png deleted file mode 100644 index 4bf7aa9..0000000 Binary files a/mod/etc/options_menu/images/settings.png and /dev/null differ diff --git a/mod/etc/options_menu/images/shutdown.png b/mod/etc/options_menu/images/shutdown.png deleted file mode 100644 index c3df9ff..0000000 Binary files a/mod/etc/options_menu/images/shutdown.png and /dev/null differ diff --git a/mod/etc/options_menu/images/themes.png b/mod/etc/options_menu/images/themes.png deleted file mode 100644 index 9e20786..0000000 Binary files a/mod/etc/options_menu/images/themes.png and /dev/null differ diff --git a/mod/etc/options_menu/images/ui/chip_apply.png b/mod/etc/options_menu/images/ui/chip_apply.png new file mode 100644 index 0000000..ea6d581 Binary files /dev/null and b/mod/etc/options_menu/images/ui/chip_apply.png differ diff --git a/mod/etc/options_menu/images/ui/chip_clear.png b/mod/etc/options_menu/images/ui/chip_clear.png new file mode 100644 index 0000000..1771d10 Binary files /dev/null and b/mod/etc/options_menu/images/ui/chip_clear.png differ diff --git a/mod/etc/options_menu/images/ui/chip_diy.png b/mod/etc/options_menu/images/ui/chip_diy.png new file mode 100644 index 0000000..8f495bc Binary files /dev/null and b/mod/etc/options_menu/images/ui/chip_diy.png differ diff --git a/mod/etc/options_menu/images/ui/chip_download.png b/mod/etc/options_menu/images/ui/chip_download.png new file mode 100644 index 0000000..380f5d1 Binary files /dev/null and b/mod/etc/options_menu/images/ui/chip_download.png differ diff --git a/mod/etc/options_menu/images/ui/chip_preview.png b/mod/etc/options_menu/images/ui/chip_preview.png new file mode 100644 index 0000000..49ebfd5 Binary files /dev/null and b/mod/etc/options_menu/images/ui/chip_preview.png differ diff --git a/mod/etc/options_menu/images/ui/chip_refresh.png b/mod/etc/options_menu/images/ui/chip_refresh.png new file mode 100644 index 0000000..14bdaa2 Binary files /dev/null and b/mod/etc/options_menu/images/ui/chip_refresh.png differ diff --git a/mod/etc/options_menu/images/ui/chip_settings.png b/mod/etc/options_menu/images/ui/chip_settings.png new file mode 100644 index 0000000..01901ed Binary files /dev/null and b/mod/etc/options_menu/images/ui/chip_settings.png differ diff --git a/mod/etc/options_menu/images/uninstall.png b/mod/etc/options_menu/images/uninstall.png deleted file mode 100644 index 9bbe789..0000000 Binary files a/mod/etc/options_menu/images/uninstall.png and /dev/null differ diff --git a/mod/etc/options_menu/images/update.png b/mod/etc/options_menu/images/update.png deleted file mode 100644 index ee8b517..0000000 Binary files a/mod/etc/options_menu/images/update.png and /dev/null differ diff --git a/mod/etc/options_menu/images/wall.png b/mod/etc/options_menu/images/wall.png deleted file mode 100644 index 9da2f3d..0000000 Binary files a/mod/etc/options_menu/images/wall.png and /dev/null differ diff --git a/mod/etc/options_menu/images/wall_far.png b/mod/etc/options_menu/images/wall_far.png deleted file mode 100644 index 36662d1..0000000 Binary files a/mod/etc/options_menu/images/wall_far.png and /dev/null differ 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 0821181..0000000 Binary files a/mod/etc/options_menu/images/wall_near.png and /dev/null differ diff --git a/mod/etc/options_menu/scripts/om_functions b/mod/etc/options_menu/scripts/om_functions deleted file mode 100644 index cba5fc1..0000000 --- a/mod/etc/options_menu/scripts/om_functions +++ /dev/null @@ -1,145 +0,0 @@ -#!/bin/sh -# shellcheck disable=SC2154,SC1091,SC2034,SC2068,SC2039 -# 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 -# 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 . -# -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" -# 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)" - -rename() { - [ -f "$1" ] && mv "$1" "$2" -} - -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 - $noPkgErr - else - $noNetErr - 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/scripts/om_themeselector b/mod/etc/options_menu/scripts/om_theme_selector similarity index 66% rename from mod/etc/options_menu/scripts/om_themeselector rename to mod/etc/options_menu/scripts/om_theme_selector index 2a3b57e..ef41af5 100644 --- a/mod/etc/options_menu/scripts/om_themeselector +++ b/mod/etc/options_menu/scripts/om_theme_selector @@ -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,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 @@ -15,21 +15,26 @@ # 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" +hookChmenu +migrateThemes && set -- reload hasThemesToInstall enableMenuOptions -remove "$omDummy" +remove "$om_dummy" + +[ -f "$om_firstrun" ] && remove "$om_themerandomizer" "$om_themeperfolder" "$om_musicperfolder" "$om_audiorandomizer" "$om_advmusichack" if [ "$1" = "reload" ] || [ -f "$om_firstrun" ] ; then loadThemeList -r remove "$om_firstrun" modCleanUp +elif themeCountChanged; then + loadThemeList -s else loadThemeList fi -$optionsMenu/options --commandPath $omModCommands/ --scriptPath $omModScripts --title "$omTitle" & +$options_menu/lib/theme_manager --title INSTALLED_THEMES & diff --git a/mod/etc/options_menu/themes/commands/_2_download b/mod/etc/options_menu/themes/commands/_2_download new file mode 100644 index 0000000..d64d4ac --- /dev/null +++ b/mod/etc/options_menu/themes/commands/_2_download @@ -0,0 +1,5 @@ +COMMAND_NAME=DOWNLOAD_THEMES +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %script_dir%/om_getThemes +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_download.png diff --git a/mod/etc/options_menu/themes/commands/_5_remove b/mod/etc/options_menu/themes/commands/_5_remove new file mode 100644 index 0000000..5f212a1 --- /dev/null +++ b/mod/etc/options_menu/themes/commands/_5_remove @@ -0,0 +1,5 @@ +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/ui/chip_clear.png diff --git a/mod/etc/options_menu/themes/commands/_download b/mod/etc/options_menu/themes/commands/_download deleted file mode 100644 index eb919c0..0000000 --- a/mod/etc/options_menu/themes/commands/_download +++ /dev/null @@ -1,7 +0,0 @@ -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 diff --git a/mod/etc/options_menu/themes/commands/_remove b/mod/etc/options_menu/themes/commands/_remove deleted file mode 100644 index 0acced5..0000000 --- a/mod/etc/options_menu/themes/commands/_remove +++ /dev/null @@ -1,7 +0,0 @@ -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_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/commands/c0000_1_refresh b/mod/etc/options_menu/themes/commands/c0000_1_refresh new file mode 100644 index 0000000..b083501 --- /dev/null +++ b/mod/etc/options_menu/themes/commands/c0000_1_refresh @@ -0,0 +1,5 @@ +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/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 new file mode 100644 index 0000000..914a746 --- /dev/null +++ b/mod/etc/options_menu/themes/commands/c0000_3_generatetheme @@ -0,0 +1,7 @@ +COMMAND_NAME=DIY_THEME +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %script_dir%/om_themeCreator +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_diy.png +ATTENTION_PATH=/tmp/diy_theme +ATTENTION_NOTICE=DIY_DRAFT_NOTICE diff --git a/mod/etc/options_menu/themes/commands/c0000_4_options b/mod/etc/options_menu/themes/commands/c0000_4_options new file mode 100644 index 0000000..885cc47 --- /dev/null +++ b/mod/etc/options_menu/themes/commands/c0000_4_options @@ -0,0 +1,5 @@ +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/ui/chip_settings.png diff --git a/mod/etc/options_menu/themes/commands/c0000_generatetheme b/mod/etc/options_menu/themes/commands/c0000_generatetheme deleted file mode 100644 index 873786a..0000000 --- a/mod/etc/options_menu/themes/commands/c0000_generatetheme +++ /dev/null @@ -1,7 +0,0 @@ -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 diff --git a/mod/etc/options_menu/themes/commands/c0000_options b/mod/etc/options_menu/themes/commands/c0000_options deleted file mode 100644 index c704835..0000000 --- a/mod/etc/options_menu/themes/commands/c0000_options +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=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_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 deleted file mode 100644 index e9e960a..0000000 --- a/mod/etc/options_menu/themes/commands/c0000_refresh +++ /dev/null @@ -1,7 +0,0 @@ -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_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file 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 c216cad..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 0926616..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/etc/options_menu/themes/creator/commands/_3_apply b/mod/etc/options_menu/themes/creator/commands/_3_apply new file mode 100644 index 0000000..aba0252 --- /dev/null +++ b/mod/etc/options_menu/themes/creator/commands/_3_apply @@ -0,0 +1,5 @@ +COMMAND_NAME=SAVE_CUSTOM_THEME +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %script_dir%/om_applytheme & +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 new file mode 100644 index 0000000..a1a2c13 --- /dev/null +++ b/mod/etc/options_menu/themes/creator/commands/_4_preview @@ -0,0 +1,6 @@ +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/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 new file mode 100644 index 0000000..6ef8555 --- /dev/null +++ b/mod/etc/options_menu/themes/creator/commands/_5_remove @@ -0,0 +1,5 @@ +COMMAND_NAME=CLEAN_CUSTOM_THEME +COMMAND_TYPE=EXTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %script_dir%/om_removediy & +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_clear.png diff --git a/mod/etc/options_menu/themes/creator/commands/_apply b/mod/etc/options_menu/themes/creator/commands/_apply deleted file mode 100644 index 7ac7fbb..0000000 --- a/mod/etc/options_menu/themes/creator/commands/_apply +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=Save custom theme -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_applytheme & -PREVIEW_IMAGE=/etc/options_menu/images/reboot.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 diff --git a/mod/etc/options_menu/themes/creator/commands/_preview b/mod/etc/options_menu/themes/creator/commands/_preview deleted file mode 100644 index 492fd37..0000000 --- a/mod/etc/options_menu/themes/creator/commands/_preview +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME= Preview custom theme -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_preview & -PREVIEW_IMAGE=/etc/options_menu/images/reboot.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 diff --git a/mod/etc/options_menu/themes/creator/commands/_remove b/mod/etc/options_menu/themes/creator/commands/_remove deleted file mode 100644 index 3cb1f1d..0000000 --- a/mod/etc/options_menu/themes/creator/commands/_remove +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=Clean custom theme -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_removediy & -PREVIEW_IMAGE=/etc/options_menu/images/reboot.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 diff --git a/mod/etc/options_menu/themes/creator/commands/c0000_refresh b/mod/etc/options_menu/themes/creator/commands/c0000_refresh deleted file mode 100644 index bc70421..0000000 --- a/mod/etc/options_menu/themes/creator/commands/c0000_refresh +++ /dev/null @@ -1,7 +0,0 @@ -COMMAND_NAME=Refresh Assets -COMMAND_TYPE=EXTERNAL -RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_refresh -PREVIEW_IMAGE=/etc/options_menu/images/update.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file diff --git a/mod/etc/options_menu/themes/creator/commands/c0001_packed b/mod/etc/options_menu/themes/creator/commands/c0001_packed index 936de80..3c74714 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0001_packed +++ b/mod/etc/options_menu/themes/creator/commands/c0001_packed @@ -1,9 +1,7 @@ -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 & -PREVIEW_IMAGE=/etc/options_menu/images/packed.png -PREVIEW_IMAGE_X=780 -PREVIEW_IMAGE_Y=275 -PREVIEW_IMAGE_WIDTH=0 -PREVIEW_IMAGE_HEIGHT=200 \ No newline at end of file +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 f694c52..5818ace 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0001_packed_nes +++ b/mod/etc/options_menu/themes/creator/commands/c0001_packed_nes @@ -1,9 +1,7 @@ -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 & -PREVIEW_IMAGE=/etc/options_menu/images/packed_nes.png -PREVIEW_IMAGE_X=780 -PREVIEW_IMAGE_Y=275 -PREVIEW_IMAGE_WIDTH=0 -PREVIEW_IMAGE_HEIGHT=200 \ No newline at end of file +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 7cfe278..34a8691 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0001_wall +++ b/mod/etc/options_menu/themes/creator/commands/c0001_wall @@ -1,9 +1,6 @@ -COMMAND_NAME=Background +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_X=920 -PREVIEW_IMAGE_Y=275 -PREVIEW_IMAGE_WIDTH=250 -PREVIEW_IMAGE_HEIGHT=250 \ No newline at end of file +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 610005c..283e056 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0002_mario +++ b/mod/etc/options_menu/themes/creator/commands/c0002_mario @@ -1,9 +1,7 @@ -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 & -PREVIEW_IMAGE=/etc/options_menu/images/mario.png -PREVIEW_IMAGE_X=900 -PREVIEW_IMAGE_Y=275 -PREVIEW_IMAGE_WIDTH=0 -PREVIEW_IMAGE_HEIGHT=250 \ No newline at end of file +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 b49c1e2..be7441a 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0002_sbm +++ b/mod/etc/options_menu/themes/creator/commands/c0002_sbm @@ -1,9 +1,7 @@ -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 & -PREVIEW_IMAGE=/etc/options_menu/images/sbm.png -PREVIEW_IMAGE_X=900 -PREVIEW_IMAGE_Y=275 -PREVIEW_IMAGE_WIDTH=0 -PREVIEW_IMAGE_HEIGHT=250 \ No newline at end of file +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 2b8f0ec..3c93f04 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0003_luigi +++ b/mod/etc/options_menu/themes/creator/commands/c0003_luigi @@ -1,9 +1,7 @@ -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 & -PREVIEW_IMAGE=/etc/options_menu/images/luigi.png -PREVIEW_IMAGE_X=900 -PREVIEW_IMAGE_Y=275 -PREVIEW_IMAGE_WIDTH=0 -PREVIEW_IMAGE_HEIGHT=250 \ No newline at end of file +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 2a5e057..c3f7888 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0004_wall_near +++ b/mod/etc/options_menu/themes/creator/commands/c0004_wall_near @@ -1,9 +1,6 @@ -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 & -PREVIEW_IMAGE=/etc/options_menu/images/wall_near.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=275 -PREVIEW_IMAGE_WIDTH=250 -PREVIEW_IMAGE_HEIGHT=250 \ No newline at end of file +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 38185f5..3ba2eb1 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0005_wall_far +++ b/mod/etc/options_menu/themes/creator/commands/c0005_wall_far @@ -1,9 +1,6 @@ -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 & -PREVIEW_IMAGE=/etc/options_menu/images/wall_far.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=275 -PREVIEW_IMAGE_WIDTH=250 -PREVIEW_IMAGE_HEIGHT=250 \ No newline at end of file +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 bf7adbd..d65fe87 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0006_colors +++ b/mod/etc/options_menu/themes/creator/commands/c0006_colors @@ -1,9 +1,6 @@ -COMMAND_NAME=Colors +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_X=762 -PREVIEW_IMAGE_Y=276 -PREVIEW_IMAGE_WIDTH=0 -PREVIEW_IMAGE_HEIGHT=272 \ No newline at end of file +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 f713ffd..dfa1957 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0006_colors_nes +++ b/mod/etc/options_menu/themes/creator/commands/c0006_colors_nes @@ -1,9 +1,7 @@ -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 & -PREVIEW_IMAGE=/etc/options_menu/images/colors_nes.png -PREVIEW_IMAGE_X=762 -PREVIEW_IMAGE_Y=276 -PREVIEW_IMAGE_WIDTH=0 -PREVIEW_IMAGE_HEIGHT=272 \ No newline at end of file +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 f129cac..5e60748 100644 --- a/mod/etc/options_menu/themes/creator/commands/c0006_pole +++ b/mod/etc/options_menu/themes/creator/commands/c0006_pole @@ -1,9 +1,7 @@ -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 & -PREVIEW_IMAGE=/etc/options_menu/images/pole.png -PREVIEW_IMAGE_X=1024 -PREVIEW_IMAGE_Y=275 -PREVIEW_IMAGE_WIDTH=0 -PREVIEW_IMAGE_HEIGHT=250 \ No newline at end of file +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/c9999_Back b/mod/etc/options_menu/themes/creator/commands/c9999_Back deleted file mode 100644 index 15ce5c5..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 c811bde..74b6580 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,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 @@ -15,10 +15,11 @@ # 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 -decodepng "$omLoaders/saving.png" >/dev/fb0 +loadingSplash THEME_SAVING name="diy_theme" i=1 @@ -30,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}" "$diy_selection_path" clearList -disable_cmds "Clear" "Preview" "DiY" -enable_cmds "Refresh" -remove "$omDummy" +disableCmds "clear" "preview" "diy" +enableCmds "refresh" +remove "$om_dummy" loadThemeList -r -$optionsMenu/options --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 6340a2a..9c0b534 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,SC3046,SC3051 # -# Copyright (c) 2022 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 @@ -15,45 +16,60 @@ # 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 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 -find_diy_asset "$1" +# 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 + findDiyAsset "$1" +fi -$optionsMenu/options --commandPath $diy_om_sprite_path/commands/ --scriptPath $diy_om_sprite_path/scripts --title "$omTitle" & +$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/creator/scripts/om_preview b/mod/etc/options_menu/themes/creator/scripts/om_preview index 314d17e..b850e67 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,SC3046,SC3051 # -# Copyright (c) 2022 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 @@ -15,18 +16,19 @@ # 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 -decodepng "$omLoaders/themepreview.png" >/dev/fb0 +loadingSplash THEME_PREVIEW_LOADING if [ ! -f "$b_file" ]; then - sed '1,2d' $omThemeScript >"$b_file" - echo "themeSelector" >"$p_file" + sed '1,2d' $om_theme_script >"$b_file" + echo "themeSelector" >"$p_file" fi -echo "usleep 20000; \ -uistart; \ -chmenu 001; \ -usleep 200000; \ -chmenu 000; \ -/etc/init.d/S8103OptionsMenu restart " >/var/exec.flag +# chmenu doesn't re-run preinit.d, so apply the DIY theme here +# shellcheck source=/dev/null +source "$b_file" +themeSelector + +omReloadMenuLater /etc/init.d/S8103OptionsMenu restart 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 62aebab..0000000 --- a/mod/etc/options_menu/themes/creator/scripts/om_refresh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh -# -# 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 -# 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 . -# -source $mountpoint/etc/options_menu/themes/scripts/om_vars -script_init - -diyThemeRemover -diyThemeChecker "loader" -touch "$omModGenSpacer" - -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & \ No newline at end of file diff --git a/mod/etc/options_menu/themes/creator/scripts/om_removediy b/mod/etc/options_menu/themes/creator/scripts/om_removediy index 73ae9f3..49a9828 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,SC3046,SC3051 # -# Copyright (c) 2022 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 @@ -15,9 +16,19 @@ # 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 +# a previewed DIY stays bound over the UI until the theme is mounted again +diy_bound="" +awk '$4 ~ /^\/diy_theme\// { found = 1 } END { exit !found }' /proc/self/mountinfo && diy_bound=1 + diyThemeRemover -$optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & +if [ -n "$diy_bound" ]; then + loadingSplash THEME_RESTORING + omReloadMenuLater /etc/init.d/S8103OptionsMenu restart +else + $options_menu/lib/theme_manager --commandPath $om_mod_gen_cmds/ --scriptPath $om_mod_gen_scripts --title "$om_title" & +fi 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 3a7483a..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_jump01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_jump02.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_jump02.png deleted file mode 100644 index ab8afe3..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_jump02.png and /dev/null differ 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 c899e60..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_run01.png and /dev/null differ 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 a77b1e0..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_run02.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_run03.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_run03.png deleted file mode 100644 index d1eb0c0..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Fire_Bros/luigi_run03.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_jump01.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_jump01.png deleted file mode 100644 index 16c11c9..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_jump01.png and /dev/null differ 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 a3d2315..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_jump02.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_run01.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_run01.png deleted file mode 100644 index 92d68ec..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_run01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_run02.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_run02.png deleted file mode 100644 index 16c11c9..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_run02.png and /dev/null differ 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 ca7d05c..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Frogman/luigi_run03.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_jump01.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_jump01.png deleted file mode 100644 index 6d0f1ee..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_jump01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_jump02.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_jump02.png deleted file mode 100644 index 6398b59..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_jump02.png and /dev/null differ 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 1ffee80..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_run01.png and /dev/null differ 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 95542cd..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_run02.png and /dev/null differ 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 f26bcfa..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Hammer_Bro/luigi_run03.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_jump01.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_jump01.png deleted file mode 100644 index 9187850..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_jump01.png and /dev/null differ 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 9187850..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_jump02.png and /dev/null differ 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 9d0d945..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_run01.png and /dev/null differ 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 1581317..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_run02.png and /dev/null differ 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 7e45259..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Little_Bro/luigi_run03.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_jump01.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_jump01.png deleted file mode 100644 index b95df26..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_jump01.png and /dev/null differ 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 513a34e..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_jump02.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_run01.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_run01.png deleted file mode 100644 index aa318f7..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_run01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_run02.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_run02.png deleted file mode 100644 index 19d81de..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_run02.png and /dev/null differ 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 31ce979..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Racoon_tail/luigi_run03.png and /dev/null differ 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 9eafe11..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_jump01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_jump02.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_jump02.png deleted file mode 100644 index 383c65c..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_jump02.png and /dev/null differ 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 4e61129..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_run01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_run02.png b/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_run02.png deleted file mode 100644 index 0a7003f..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_run02.png and /dev/null differ 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 e2fc30d..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Super_Bro/luigi_run03.png and /dev/null differ 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 3656774..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_jump01.png and /dev/null differ 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 cd2a5ce..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_jump02.png and /dev/null differ 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 fe7688d..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_run01.png and /dev/null differ 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 6e3a97a..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_run02.png and /dev/null differ 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 3837c36..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/luigi/Tanooki/luigi_run03.png and /dev/null differ 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 8020cfd..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_dush01.png and /dev/null differ 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 5f9333d..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_dush02.png and /dev/null differ 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 8a7510e..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_dush03.png and /dev/null differ 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 df03848..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_jump01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_jump02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_jump02.png deleted file mode 100644 index 7d69f79..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_jump02.png and /dev/null differ 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 747bbb8..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_look.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_run01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_run01.png deleted file mode 100644 index eb7d548..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_run01.png and /dev/null differ 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 0aa87c2..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_run02.png and /dev/null differ 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 ffbe42a..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_run03.png and /dev/null differ 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 16bf83d..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_spin01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_spin02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_spin02.png deleted file mode 100644 index ab0d441..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Fire_Bros/mario_spin02.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_jump01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_jump01.png deleted file mode 100644 index 0805c77..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_jump01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_jump02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_jump02.png deleted file mode 100644 index 984a582..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_jump02.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_look.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_look.png deleted file mode 100644 index cdd0cfe..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_look.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_run01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_run01.png deleted file mode 100644 index 2f7148a..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_run01.png and /dev/null differ 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 0805c77..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_run02.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_run03.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_run03.png deleted file mode 100644 index d487f8b..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Frogman/mario_run03.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_jump01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_jump01.png deleted file mode 100644 index a6e1289..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_jump01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_jump02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_jump02.png deleted file mode 100644 index fd60b5b..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_jump02.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_look.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_look.png deleted file mode 100644 index fd3769e..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_look.png and /dev/null differ 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 d8056c3..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_run01.png and /dev/null differ 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 9b244b2..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_run02.png and /dev/null differ 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 a211901..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_run03.png and /dev/null differ 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 a70981a..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_spin01.png and /dev/null differ 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 3f94dea..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Hammer_Bro/mario_spin02.png and /dev/null differ 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 3f1f036..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_dush01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_dush02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_dush02.png deleted file mode 100644 index b1651e3..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_dush02.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_dush03.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_dush03.png deleted file mode 100644 index a0f5590..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_dush03.png and /dev/null differ 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 ece51b7..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_jump01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_jump02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_jump02.png deleted file mode 100644 index ece51b7..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_jump02.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_look.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_look.png deleted file mode 100644 index cf05091..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_look.png and /dev/null differ 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 03da3c8..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_run01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_run02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_run02.png deleted file mode 100644 index 0daa860..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_run02.png and /dev/null differ 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 a70873a..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_run03.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_spin01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_spin01.png deleted file mode 100644 index efcb591..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_spin01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_spin02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_spin02.png deleted file mode 100644 index 07adce2..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Little_Bro/mario_spin02.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_dush01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_dush01.png deleted file mode 100644 index a9e752f..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_dush01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_dush02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_dush02.png deleted file mode 100644 index 3bc83e3..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_dush02.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_dush03.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_dush03.png deleted file mode 100644 index 83f740f..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_dush03.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_jump01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_jump01.png deleted file mode 100644 index 7cb92a0..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_jump01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_jump02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_jump02.png deleted file mode 100644 index 5720054..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_jump02.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_look.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_look.png deleted file mode 100644 index e547bef..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_look.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_run01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_run01.png deleted file mode 100644 index 31dfe1e..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_run01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_run02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_run02.png deleted file mode 100644 index a52f195..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_run02.png and /dev/null differ 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 23e9a2f..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_run03.png and /dev/null differ 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 a50a57f..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_spin01.png and /dev/null differ 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 f105db3..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Racoon_tail/mario_spin02.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_dush01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_dush01.png deleted file mode 100644 index e8b81d1..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_dush01.png and /dev/null differ 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 87bd131..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_dush02.png and /dev/null differ 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 a9052c2..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_dush03.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_jump01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_jump01.png deleted file mode 100644 index 2fe62a5..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_jump01.png and /dev/null differ 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 c5d67e9..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_jump02.png and /dev/null differ 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 948776e..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_look.png and /dev/null differ 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 1d87f23..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_run01.png and /dev/null differ 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 5c5e183..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_run02.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_run03.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_run03.png deleted file mode 100644 index 17dfce9..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_run03.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_spin01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_spin01.png deleted file mode 100644 index 968ec9e..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_spin01.png and /dev/null differ 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 a56537d..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Super_Bro/mario_spin02.png and /dev/null differ 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 0e01db0..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_dush01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_dush02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_dush02.png deleted file mode 100644 index 68932ae..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_dush02.png and /dev/null differ 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 7c7f807..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_dush03.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_jump01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_jump01.png deleted file mode 100644 index 10bcf41..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_jump01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_jump02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_jump02.png deleted file mode 100644 index cccec0c..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_jump02.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_look.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_look.png deleted file mode 100644 index 27f015b..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_look.png and /dev/null differ 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 57bc4b5..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_run01.png and /dev/null differ 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 936b779..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_run02.png and /dev/null differ 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 0942bf1..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_run03.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_spin01.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_spin01.png deleted file mode 100644 index 3eb72a5..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_spin01.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_spin02.png b/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_spin02.png deleted file mode 100644 index 0b57381..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/mario/Tanooki/mario_spin02.png and /dev/null differ 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 61cbc15..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_cannabis.png and /dev/null differ 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 c26f2c8..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_famicom.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_gameboy_advanced.png b/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_gameboy_advanced.png deleted file mode 100644 index 841e822..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_gameboy_advanced.png and /dev/null differ 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 abdd940..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_gameboy_color.png and /dev/null differ 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 56a99c7..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_gold.png and /dev/null differ 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 f9fb70b..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_mega.png and /dev/null differ 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 f4dc9df..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_metroid.png and /dev/null differ 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 e25e806..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_nes.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_zelda.png b/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_zelda.png deleted file mode 100644 index 55137aa..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/packed_nes/packed_nes_zelda.png and /dev/null differ 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 a4752ec..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/wall/Fire_Bros/wall.png and /dev/null differ 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 0c2c9c1..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/wall/Frogman/wall.png and /dev/null differ diff --git a/mod/etc/options_menu/themes/creator/sprites/assets/wall/Hammer_Bro/wall.png b/mod/etc/options_menu/themes/creator/sprites/assets/wall/Hammer_Bro/wall.png deleted file mode 100644 index e1d1159..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/wall/Hammer_Bro/wall.png and /dev/null differ 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 3aad5ca..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/wall/Little_Bro/wall.png and /dev/null differ 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 069c577..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/wall/Racoon_tail/wall.png and /dev/null differ 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 314ded8..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/wall/Super_Bro/wall.png and /dev/null differ 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 be981d7..0000000 Binary files a/mod/etc/options_menu/themes/creator/sprites/assets/wall/Tanooki/wall.png and /dev/null differ 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..37556a1 --- /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/ui/chip_refresh.png 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 b891523..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/commands/c9998_separator b/mod/etc/options_menu/themes/creator/sprites/colors/commands/commandshere similarity index 100% rename from mod/etc/options_menu/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/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors/scripts/om_return index 66ff2b9..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 @@ -1,23 +1,26 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 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 # 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 +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +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/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/colors_nes/commands/c0000_1_refresh new file mode 100644 index 0000000..c2fea0a --- /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/ui/chip_refresh.png 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 b891523..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/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/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/colors_nes/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/colors_nes/scripts/om_return index 66ff2b9..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 @@ -1,23 +1,26 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 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 # 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 +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +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/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/luigi/commands/c0000_1_refresh new file mode 100644 index 0000000..56bc789 --- /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/ui/chip_refresh.png 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 b891523..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/commands/c0000_separator b/mod/etc/options_menu/themes/creator/sprites/luigi/commands/commandshere similarity index 100% rename from mod/etc/options_menu/themes/creator/commands/c0000_separator rename to mod/etc/options_menu/themes/creator/sprites/luigi/commands/commandshere 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..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 @@ -1,23 +1,26 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 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 # 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 +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +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/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/mario/commands/c0000_1_refresh new file mode 100644 index 0000000..3d40552 --- /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/ui/chip_refresh.png 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 b891523..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/commands/c9997_separator b/mod/etc/options_menu/themes/creator/sprites/mario/commands/commandshere similarity index 100% rename from mod/etc/options_menu/themes/creator/commands/c9997_separator rename to mod/etc/options_menu/themes/creator/sprites/mario/commands/commandshere 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..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 @@ -1,23 +1,26 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 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 # 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 +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +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/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/packed/commands/c0000_1_refresh new file mode 100644 index 0000000..25be7b2 --- /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/ui/chip_refresh.png 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 b891523..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/colors/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/colors/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/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/packed/scripts/om_return index 66ff2b9..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 @@ -1,23 +1,26 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 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 # 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 +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +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/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/packed_nes/commands/c0000_1_refresh new file mode 100644 index 0000000..455c5b8 --- /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/ui/chip_refresh.png 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 b891523..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/colors_nes/commands/c9998_separator b/mod/etc/options_menu/themes/creator/sprites/packed_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/packed_nes/commands/commandshere 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..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 @@ -1,23 +1,26 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 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 # 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 +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +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/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/pole/commands/c0000_1_refresh new file mode 100644 index 0000000..abd5278 --- /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/ui/chip_refresh.png 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 b891523..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/luigi/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/luigi/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/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/pole/scripts/om_return index 66ff2b9..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 @@ -1,23 +1,26 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 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 # 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 +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +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/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c0000_1_refresh new file mode 100644 index 0000000..cd01e74 --- /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/ui/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c9998_separator b/mod/etc/options_menu/themes/creator/sprites/sbm/commands/c9998_separator deleted file mode 100644 index e69de29..0000000 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 b891523..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/mario/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/mario/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/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/sbm/scripts/om_return index 66ff2b9..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 @@ -1,23 +1,26 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 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 # 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 +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +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/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/wall/commands/c0000_1_refresh new file mode 100644 index 0000000..01e95a6 --- /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/ui/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/wall/commands/c9998_separator b/mod/etc/options_menu/themes/creator/sprites/wall/commands/c9998_separator deleted file mode 100644 index e69de29..0000000 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 b891523..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/packed/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/packed/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/scripts/om_return b/mod/etc/options_menu/themes/creator/sprites/wall/scripts/om_return index 66ff2b9..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 @@ -1,23 +1,26 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 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 # 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 +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +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/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c0000_1_refresh new file mode 100644 index 0000000..018262c --- /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/ui/chip_refresh.png diff --git a/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c9998_separator b/mod/etc/options_menu/themes/creator/sprites/wall_far/commands/c9998_separator deleted file mode 100644 index e69de29..0000000 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 b891523..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/packed/commands/scriptshere b/mod/etc/options_menu/themes/creator/sprites/wall_far/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/wall_far/commands/commandshere 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..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 @@ -1,23 +1,26 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 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 # 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 +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +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/commands/c0000_1_refresh b/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c0000_1_refresh new file mode 100644 index 0000000..5550594 --- /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/ui/chip_refresh.png 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_near/commands/c9999_Back b/mod/etc/options_menu/themes/creator/sprites/wall_near/commands/c9999_Back deleted file mode 100644 index b891523..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/packed_nes/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/packed_nes/commands/c9998_separator rename to mod/etc/options_menu/themes/creator/sprites/wall_near/commands/commandshere 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..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 @@ -1,23 +1,26 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2119,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 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 # 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 +# shellcheck disable=SC2119 # deliberately called with no args here diyThemeChecker -usleep 50000 && $optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts/ --title "$omTitle" & \ No newline at end of file +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/commands/c0000_downloadThemes b/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes index b42f831..4f06db7 100644 --- a/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes +++ b/mod/etc/options_menu/themes/downloads/commands/c0000_downloadThemes @@ -1,7 +1,5 @@ -COMMAND_NAME=Update Theme List +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/ui/chip_refresh.png diff --git a/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes b/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes index c2d23a4..f5ae782 100644 --- a/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes +++ b/mod/etc/options_menu/themes/downloads/commands/c0000_removeThemes @@ -1,7 +1,5 @@ -COMMAND_NAME=Clear List +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_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file +PREVIEW_IMAGE=/etc/options_menu/images/ui/chip_clear.png 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 b891523..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 e726ed5..95acac3 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads +++ b/mod/etc/options_menu/themes/downloads/scripts/om_clearDownloads @@ -1,25 +1,26 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 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 # 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 clearDownloads -remove "$subDummy" -remove "$omModSubSpacer" +remove "$sub_dummy" -$optionsMenu/options --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title "$omTitle" & +showDownloads diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes index 874e8b0..c022cba 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes +++ b/mod/etc/options_menu/themes/downloads/scripts/om_refreshThemes @@ -1,23 +1,25 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 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 # 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 -get_theme_list +getThemeList -$optionsMenu/options --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title "$omTitle" & +showDownloads diff --git a/mod/etc/options_menu/themes/downloads/scripts/om_return b/mod/etc/options_menu/themes/downloads/scripts/om_return index c02eeac..b0af5b0 100644 --- a/mod/etc/options_menu/themes/downloads/scripts/om_return +++ b/mod/etc/options_menu/themes/downloads/scripts/om_return @@ -1,30 +1,33 @@ #!/bin/sh +# shellcheck disable=SC2034,SC2086,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 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 # 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 -remove "$omDummy" +remove "$om_dummy" if [ -n "$(ls -A $theme_path)" ]; then - touch "$omDummy" - themeLoader + touch "$om_dummy" + themeCountChanged && themeSync else - remove "$omModSpacer" - clearList + remove "$om_mod_spacer" + clearList fi +toggleThemeCmds -usleep 50000 && $optionsMenu/options --commandPath $omModCommands/ --scriptPath $omModScripts/ --title "$omTitle" & +usleep 50000 && $options_menu/lib/theme_manager --title INSTALLED_THEMES & 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..543e306 --- /dev/null +++ b/mod/etc/options_menu/themes/lang/de-DE.lang @@ -0,0 +1,88 @@ +THEME_OPTIONS=Themes +THEME_SELECTOR=Themenauswahl +INSTALLED_THEMES=Installierte Themes +AVAILABLE_THEMES=Verfügbare Themes +DIY_THEME=Eigenes Theme +THEME_SETTINGS=Einstellungen +REFRESH=Aktualisieren +REFRESH_ASSETS=Assets aktualisieren +BASE_UI=Basis-UI +BACKGROUND=Hintergrund +DEMO_CHARACTER_1=Demo: Charakter Nr. 1 +DEMO_CHARACTERS=Demo: Charaktere +DEMO_CHARACTER_2=Demo: Charakter Nr. 2 +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 +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 +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 +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) +CLEAN_UP=Aufräumen +RESET_SETTINGS=Einstellungen zurücksetzen +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... +INSTALLING_THEME=Installiere +NEW_BADGE=NEU +INSTALLATION_COMPLETE=Installation abgeschlossen. +ALL_THEMES_INSTALLED=Alle Themen sind bereits installiert, nichts herunterzuladen. +DOWNLOAD_OVERWRITE_CONFIRM=Theme bereits installiert. Herunterladen und überschreiben? +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 . . . +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... +THEME_SAVING=Eigenes Theme wird gespeichert... +THEME_PREVIEW_LOADING=Themenvorschau wird geladen... +THEME_RESTORING=Design wird wiederhergestellt... +THEME_APPLYING_LINE1=Design wird übernommen +THEME_APPLYING_LINE2=Neustart läuft . . . +THEME_DELETE_IN_USE=Dieses Design ist aktiv und kann nicht entfernt werden. 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..9dbbb5c --- /dev/null +++ b/mod/etc/options_menu/themes/lang/en-US.lang @@ -0,0 +1,88 @@ +THEME_OPTIONS=Themes +THEME_SELECTOR=Theme Selector +INSTALLED_THEMES=Installed Themes +AVAILABLE_THEMES=Available Themes +DIY_THEME=DIY Theme +THEME_SETTINGS=Settings +REFRESH=Refresh +REFRESH_ASSETS=Refresh Assets +BASE_UI=Base UI +BACKGROUND=Background +DEMO_CHARACTER_1=Demo: Character #1 +DEMO_CHARACTERS=Demo: Characters +DEMO_CHARACTER_2=Demo: Character #2 +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 +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 +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 +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) +CLEAN_UP=Clean Up +RESET_SETTINGS=Reset Settings +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... +INSTALLING_THEME=Installing +NEW_BADGE=NEW +INSTALLATION_COMPLETE=Installation complete. +ALL_THEMES_INSTALLED=All themes are already installed, nothing to download. +DOWNLOAD_OVERWRITE_CONFIRM=Theme already installed. Download and overwrite it? +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... +THEME_SAVING=Saving custom theme... +THEME_PREVIEW_LOADING=Loading theme preview... +THEME_RESTORING=Restoring the theme... +THEME_APPLYING_LINE1=Applying Theme +THEME_APPLYING_LINE2=Rebooting Now . . . +THEME_DELETE_IN_USE=This theme is in use and can't be removed. 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..ab6e562 --- /dev/null +++ b/mod/etc/options_menu/themes/lang/es-ES.lang @@ -0,0 +1,88 @@ +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 +REFRESH_ASSETS=Actualizar recursos +BASE_UI=Interfaz base +BACKGROUND=Fondo +DEMO_CHARACTER_1=Demo: Personaje 1 +DEMO_CHARACTERS=Demo: Personajes +DEMO_CHARACTER_2=Demo: Personaje 2 +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 +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 +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 +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) +CLEAN_UP=Limpieza +RESET_SETTINGS=Restablecer ajustes +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... +INSTALLING_THEME=Instalando +NEW_BADGE=NUEVO +INSTALLATION_COMPLETE=Instalación completada. +ALL_THEMES_INSTALLED=Todos los temas ya están instalados, no hay nada que descargar. +DOWNLOAD_OVERWRITE_CONFIRM=Tema ya instalado. ¿Descargar y sobrescribir? +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 . . . +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... +THEME_SAVING=Guardando tema personalizado... +THEME_PREVIEW_LOADING=Cargando vista previa del tema... +THEME_RESTORING=Restaurando el tema... +THEME_APPLYING_LINE1=Aplicando Tema +THEME_APPLYING_LINE2=Reiniciando Ahora . . . +THEME_DELETE_IN_USE=Este tema está en uso y no se puede eliminar. 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..fe6055f --- /dev/null +++ b/mod/etc/options_menu/themes/lang/fr-FR.lang @@ -0,0 +1,88 @@ +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 +REFRESH_ASSETS=Actualiser les ressources +BASE_UI=Interface de base +BACKGROUND=Arrière-plan +DEMO_CHARACTER_1=Démo : Personnage #1 +DEMO_CHARACTERS=Démo : Personnages +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 +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 +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 +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 +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) +CLEAN_UP=Nettoyage +RESET_SETTINGS=Réinitialiser les réglages +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... +INSTALLING_THEME=Installation de +NEW_BADGE=NOUVEAU +INSTALLATION_COMPLETE=Installation terminée. +ALL_THEMES_INSTALLED=Tous les thèmes sont déjà installés, rien à télécharger. +DOWNLOAD_OVERWRITE_CONFIRM=Thème déjà installé. Le télécharger et le remplacer ? +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... +THEME_SAVING=Enregistrement du thème personnalisé... +THEME_PREVIEW_LOADING=Chargement de l'aperçu du thème... +THEME_RESTORING=Restauration du thème... +THEME_APPLYING_LINE1=Application du Thème +THEME_APPLYING_LINE2=Redémarrage en Cours . . . +THEME_DELETE_IN_USE=Ce thème est utilisé et ne peut pas être supprimé. 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..420a31d --- /dev/null +++ b/mod/etc/options_menu/themes/lang/it-IT.lang @@ -0,0 +1,88 @@ +THEME_OPTIONS=Temi +THEME_SELECTOR=Selettore temi +INSTALLED_THEMES=Temi installati +AVAILABLE_THEMES=Temi disponibili +DIY_THEME=Tema personalizzato +THEME_SETTINGS=Impostazioni +REFRESH=Aggiorna +REFRESH_ASSETS=Aggiorna risorse +BASE_UI=Interfaccia base +BACKGROUND=Sfondo +DEMO_CHARACTER_1=Demo: Personaggio #1 +DEMO_CHARACTERS=Demo: Personaggi +DEMO_CHARACTER_2=Demo: Personaggio #2 +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 +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 +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 +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) +CLEAN_UP=Pulizia +RESET_SETTINGS=Ripristina impostazioni +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... +INSTALLING_THEME=Installazione di +NEW_BADGE=NUOVO +INSTALLATION_COMPLETE=Installazione completata. +ALL_THEMES_INSTALLED=Tutti i temi sono già installati, niente da scaricare. +DOWNLOAD_OVERWRITE_CONFIRM=Tema già installato. Scaricarlo e sovrascriverlo? +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... +THEME_SAVING=Salvataggio tema personalizzato... +THEME_PREVIEW_LOADING=Caricamento anteprima tema... +THEME_RESTORING=Ripristino del tema... +THEME_APPLYING_LINE1=Applicazione del Tema in Corso +THEME_APPLYING_LINE2=Riavvio in Corso . . . +THEME_DELETE_IN_USE=Questo tema è in uso e non può essere eliminato. 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..0d5ae87 --- /dev/null +++ b/mod/etc/options_menu/themes/lang/ja-JP.lang @@ -0,0 +1,88 @@ +THEME_OPTIONS=Theme +THEME_SELECTOR=Themeせんたく +INSTALLED_THEMES=インストール済みテーマ +AVAILABLE_THEMES=利用可能なテーマ +DIY_THEME=じぶんのTheme +THEME_SETTINGS=せってい +REFRESH=さいよみこみ +REFRESH_ASSETS=Assetをさいよみこみ +BASE_UI=きほんUI +BACKGROUND=はいけい +DEMO_CHARACTER_1=Demo: Character1 +DEMO_CHARACTERS=Demo: Characters +DEMO_CHARACTER_2=Demo: Character2 +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 +ASSIGN_FOLDER_THEMES=FolderにThemeをわりあてる +MUSIC_PER_FOLDER=Folderごとのおんがく +ASSIGN_FOLDER_MUSIC=Folderにおんがくをわりあてる +FOLDER_MUSIC=Folderのおんがく +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 +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のみ) +CLEAN_UP=せいり +RESET_SETTINGS=せっていをreset +DIY_DRAFT_NOTICE=じぶんのThemeがほぞんされていません. さいきどうするときえます +CATEGORY_UI_MOD=カテゴリ: UI Mod +CHECKING_THEME_FILES=えらんだThemeのFileをかくにんしています +DOWNLOADING=downloadしています... +INSTALLING_THEME=インストール中: +NEW_BADGE=新着 +INSTALLATION_COMPLETE=インストールかんりょう +ALL_THEMES_INSTALLED=すべてのThemeはインストールずみです. downloadするものはありません +DOWNLOAD_OVERWRITE_CONFIRM=このThemeはインストールずみです. うわがきしますか? +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をよみこみちゅう... +THEME_SAVING=じぶんのThemeをほぞんちゅう... +THEME_PREVIEW_LOADING=Themeのpreviewをよみこみちゅう... +THEME_RESTORING=Themeをもとにもどしています... +THEME_APPLYING_LINE1=Themeを てきようしています +THEME_APPLYING_LINE2=さいきどうしています . . . +THEME_DELETE_IN_USE=このテーマは使用中のため削除できません。 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..03737ba --- /dev/null +++ b/mod/etc/options_menu/themes/lang/nl-NL.lang @@ -0,0 +1,88 @@ +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 +REFRESH_ASSETS=Assets vernieuwen +BASE_UI=Basisinterface +BACKGROUND=Achtergrond +DEMO_CHARACTER_1=Demo: Personage #1 +DEMO_CHARACTERS=Demo: Personages +DEMO_CHARACTER_2=Demo: Personage #2 +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 +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 +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 +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) +CLEAN_UP=Opschonen +RESET_SETTINGS=Instellingen herstellen +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... +INSTALLING_THEME=Installeren: +NEW_BADGE=NIEUW +INSTALLATION_COMPLETE=Installatie voltooid. +ALL_THEMES_INSTALLED=Alle thema's zijn al geïnstalleerd, er is niets te downloaden. +DOWNLOAD_OVERWRITE_CONFIRM=Thema al geïnstalleerd. Downloaden en overschrijven? +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 . . . +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... +THEME_SAVING=Eigen thema wordt opgeslagen... +THEME_PREVIEW_LOADING=Themavoorbeeld wordt geladen... +THEME_RESTORING=Thema wordt hersteld... +THEME_APPLYING_LINE1=Thema Wordt Toegepast +THEME_APPLYING_LINE2=Nu Herstarten . . . +THEME_DELETE_IN_USE=Dit thema wordt gebruikt en kan niet worden verwijderd. 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..99ccf98 --- /dev/null +++ b/mod/etc/options_menu/themes/lang/pt-PT.lang @@ -0,0 +1,88 @@ +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 +REFRESH_ASSETS=Atualizar Recursos +BASE_UI=Interface Base +BACKGROUND=Fundo +DEMO_CHARACTER_1=Demonstração: Personagem #1 +DEMO_CHARACTERS=Demonstração: Personagens +DEMO_CHARACTER_2=Demonstração: Personagem #2 +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 +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 +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 +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) +CLEAN_UP=Limpeza +RESET_SETTINGS=Repor Definições +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... +INSTALLING_THEME=A instalar +NEW_BADGE=NOVO +INSTALLATION_COMPLETE=Instalação concluída. +ALL_THEMES_INSTALLED=Todos os temas já estão instalados, não há nada para descarregar. +DOWNLOAD_OVERWRITE_CONFIRM=Tema já instalado. Descarregar e substituir? +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 . . . +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... +THEME_SAVING=A guardar tema personalizado... +THEME_PREVIEW_LOADING=A carregar pré-visualização do tema... +THEME_RESTORING=A repor o tema... +THEME_APPLYING_LINE1=A Aplicar o Tema +THEME_APPLYING_LINE2=A Reiniciar Agora . . . +THEME_DELETE_IN_USE=Este tema está em uso e não pode ser removido. 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..027b400 --- /dev/null +++ b/mod/etc/options_menu/themes/lang/ru-RU.lang @@ -0,0 +1,88 @@ +THEME_OPTIONS=Темы +THEME_SELECTOR=Выбор тем +INSTALLED_THEMES=Установленные темы +AVAILABLE_THEMES=Доступные темы +DIY_THEME=Своя тема +THEME_SETTINGS=Настройки +REFRESH=Обновить +REFRESH_ASSETS=Обновить ресурсы +BASE_UI=Базовый интерфейс +BACKGROUND=Фон +DEMO_CHARACTER_1=Демо: Персонаж №1 +DEMO_CHARACTERS=Демо: Персонажи +DEMO_CHARACTER_2=Демо: Персонаж №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=Тема для каждой папки +ASSIGN_FOLDER_THEMES=Назначить тему папке +MUSIC_PER_FOLDER=Музыка для каждой папки +ASSIGN_FOLDER_MUSIC=Назначить музыку папке +FOLDER_MUSIC=Музыка папок +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 +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=Все папки (только темы БЕЗ аудиофайлов) +CLEAN_UP=Очистка +RESET_SETTINGS=Сбросить настройки +DIY_DRAFT_NOTICE=Своя тема не сохранена: откройте «Своя тема», чтобы сохранить её, иначе она пропадёт после перезагрузки. +CATEGORY_UI_MOD=Категория: Мод интерфейса +CHECKING_THEME_FILES=Проверка файлов выбранной темы. +DOWNLOADING=Загрузка... +INSTALLING_THEME=Установка +NEW_BADGE=НОВЫЙ +INSTALLATION_COMPLETE=Установка завершена. +ALL_THEMES_INSTALLED=Все темы уже установлены, загружать нечего. +DOWNLOAD_OVERWRITE_CONFIRM=Тема уже установлена. Скачать и перезаписать? +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=Загрузка ресурсов... +THEME_SAVING=Сохранение своей темы... +THEME_PREVIEW_LOADING=Загрузка предпросмотра темы... +THEME_RESTORING=Восстановление темы... +THEME_APPLYING_LINE1=Применение Темы +THEME_APPLYING_LINE2=Перезагрузка . . . +THEME_DELETE_IN_USE=Эта тема используется и не может быть удалена. 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_clearList b/mod/etc/options_menu/themes/scripts/om_clearList index 2852460..102f9ac 100644 --- a/mod/etc/options_menu/themes/scripts/om_clearList +++ b/mod/etc/options_menu/themes/scripts/om_clearList @@ -1,26 +1,27 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 +# Copyright (c) 2026 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 # 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 clearList -disable_cmds "Clear" -enable_cmds "Refresh" +disableCmds "clear" +enableCmds "refresh" -$optionsMenu/options --commandPath $omModCommands/ --scriptPath $omModScripts --title "$omTitle" & +$options_menu/lib/theme_manager --title INSTALLED_THEMES & 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_getThemes b/mod/etc/options_menu/themes/scripts/om_getThemes index 75abb58..0620f2a 100644 --- a/mod/etc/options_menu/themes/scripts/om_getThemes +++ b/mod/etc/options_menu/themes/scripts/om_getThemes @@ -1,23 +1,25 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 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 # 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 -[ ! -f "$subDummy" ] && get_theme_list +[ ! -f "$sub_dummy" ] && getThemeList -$optionsMenu/options --commandPath $omModSubCmds/ --scriptPath $omModSubScripts --title "$omTitle" & +showDownloads diff --git a/mod/etc/options_menu/themes/scripts/om_license b/mod/etc/options_menu/themes/scripts/om_license index 688afc8..4dc040c 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,SC3046,SC3051 # -# Copyright (c) 2022 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 @@ -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=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init 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 5491626..0000000 --- a/mod/etc/options_menu/themes/scripts/om_overlay +++ /dev/null @@ -1 +0,0 @@ -chrono_trigger diff --git a/mod/etc/options_menu/themes/scripts/om_overlaythmcript b/mod/etc/options_menu/themes/scripts/om_overlaythmcript index 48ed31b..fd7da43 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) 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 @@ -15,82 +15,169 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -font_bind_fix() { - [ "$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" +# corrected copies of theme files, at their path under the UI; they sit on top of the theme. +# /var is already a tmpfs at boot, where /tmp is still the NAND +theme_fixes="$mountpoint/var/theme_fixes" + +themeFixesUnbind() { + awk -v dir="$original_theme/" 'index($5, dir) == 1 && $4 ~ /\/theme_fixes\// { print $5 }' /proc/self/mountinfo | + sed -n '1!G;h;$p' | while IFS= read -r fix; do + umount "$fix" + done + rm -rf "$theme_fixes" +} + +# the USA/EUR firmwares name the s/m/l fonts *.font and the Super Famicom *.fnt; +# the UI exits when dependencies.json lists one that isn't there +themeDepsFix() { + local deps="$original_theme/dependencies.json" + local fixed="$theme_fixes/dependencies.json" + local font other + [ -f "$deps" ] || return 0 + mkdir -p "$theme_fixes" + cp -f "$deps" "$fixed" + for font in $(grep -o '"/fonts/[^"]*"' "$deps" | tr -d '"'); do + [ -e "$original_theme/resources$font" ] && continue + case "$font" in + *.font) other="${font%.font}.fnt" ;; + *.fnt) other="${font%.fnt}.font" ;; + *) continue ;; + esac + [ -e "$original_theme/resources$other" ] && sed -i "s#\"$font\"#\"$other\"#" "$fixed" + done + if cmp -s "$deps" "$fixed"; then + rm -f "$fixed" + else + mount -o bind "$fixed" "$deps" + fi +} + +# byte offset of the first occurrence of $2 in file $1 +byteOffset() { + local want + want="$(printf '%s' "$2" | od -An -tx1 | tr -d ' \n')" + od -An -v -tx1 "$1" | tr -s ' ' '\n' | grep . | awk -v want="$want" -v n="${#2}" ' + { s = s $1; if (length(s) > 2 * n) s = substr(s, 3); if (s == want) { print NR - n; exit } }' +} + +# each firmware reads only its own palette (env_.lua loads colors_); a theme made +# for another region gets its colors_*.lua renamed in place (same length) and bound over this one +themeColorsFix() { + local env_dir="$original_theme/resources/scripts/env" + local theme_env="$full_theme_path/resources/scripts/env" + local region src fixed offset + for region in jpn usa eur; do + [ -f "$env_dir/env_$region.lua" ] && break + done + [ -f "$env_dir/env_$region.lua" ] && [ ! -f "$theme_env/colors_$region.lua" ] || return 0 + for src in usa eur jpn; do + [ -f "$theme_env/colors_$src.lua" ] && break + done + [ -f "$theme_env/colors_$src.lua" ] || return 0 + fixed="$theme_fixes/resources/scripts/env/colors_$region.lua" + mkdir -p "${fixed%/*}" + cp -f "$theme_env/colors_$src.lua" "$fixed" + offset="$(byteOffset "$fixed" "colors_$src")" + if [ -z "$offset" ]; then + rm -f "$fixed" + return 0 + fi + printf 'colors_%s' "$region" | dd of="$fixed" bs=1 seek="$offset" conv=notrunc 2>/dev/null + mount -o bind "$fixed" "$env_dir/colors_$region.lua" +} + +fontBindFix() { + [ ! -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 + cmp -s "$full_theme_path/fonts/${font_name}.fnt" "${font_path}/${font_name}.fnt" || + 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/${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/activeTheme" - 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_FONT_BACKUP="${TITLE_FONT}.backup" - 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" - if [ "$SELECTED_THEME" != "$PREVIOUS_THEME" ] || [ ! -f "$LAST_THEME" ] || [ ! -f "$ACTIVE_THEME" ]; then - echo "$SELECTED_THEME" >"$LAST_THEME" - echo "$FULL_THEME_PATH" >"$ACTIVE_THEME" + # an install leaves the placeholder until a theme is applied from the menu + [ "$selected_theme" = "CHANGEME" ] && return 0 + + 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 - font_bind_fix "backup" - fi + if [ -d "$full_theme_path/fonts" ]; then + fontBindFix "backup" else - font_bind_fix "restore" + 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" - + themeFixesUnbind + 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" + themeDepsFix + themeColorsFix + local cl_functions="$rootfs/etc/options_menu/console_language/scripts/cl_functions" + if [ -f "$cl_functions" ]; then + # shellcheck source=/dev/null + source "$cl_functions" + clInit + # older Console Language releases have no clRebind + if type clRebind >/dev/null 2>&1; then + clRebind + else + clUmountLang + clMountLang + fi + fi } 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_settings b/mod/etc/options_menu/themes/scripts/om_settings index cf8209c..c4aaf80 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,SC3046,SC3051 # -# Copyright (c) 2022 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 @@ -15,20 +16,14 @@ # 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 -hasSelectedOptions "$om_themeperfolder" "$enableThemePerFolder" -hasSelectedOptions "$om_themerandomizer" "$enableThemeSetting" +# cached for om_state: one lookup instead of one per toggle row +echo "$mod_state_path" > /tmp/om_state_path -hasItemsToCleanUp "SystemCleanUp" +settingsRows +folderThemesRow -if [ -f "$b_file" ] && [ -f "$p_file" ]; then - enable_cmds "Uninstaller" - [ ! -f "$omModSettingsSpacer" ] && touch "$omModSettingsSpacer" -else - disable_cmds "Uninstaller" - [ ! -f "$enableSystemCleanUp" ] && remove "$omModSettingsSpacer" -fi - -$optionsMenu/options --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & +$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 ac09f9a..0e519db 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,SC3046,SC3051 # -# Copyright (c) 2022 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 @@ -15,9 +16,10 @@ # 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 diyThemeChecker -$optionsMenu/options --commandPath $omModGenCmds/ --scriptPath $omModGenScripts --title "$omTitle" & +$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 1ff9faf..914f597 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) +# 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 # it under the terms of the GNU General Public License as published by @@ -16,171 +16,331 @@ # 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 [ -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 return 0 } -music_hack_unbind() { - 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" +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 +} + +# corrected copies of theme files, at their path under the UI; they sit on top of the theme. +# /var is already a tmpfs at boot, where /tmp is still the NAND +theme_fixes="$mountpoint/var/theme_fixes" + +themeFixesUnbind() { + awk -v dir="$original_theme/" 'index($5, dir) == 1 && $4 ~ /\/theme_fixes\// { print $5 }' /proc/self/mountinfo | + sed -n '1!G;h;$p' | while IFS= read -r fix; do + umount "$fix" + done + rm -rf "$theme_fixes" +} + +# the USA/EUR firmwares name the s/m/l fonts *.font and the Super Famicom *.fnt; +# the UI exits when dependencies.json lists one that isn't there +themeDepsFix() { + local deps="$original_theme/dependencies.json" + local fixed="$theme_fixes/dependencies.json" + local font other + [ -f "$deps" ] || return 0 + mkdir -p "$theme_fixes" + cp -f "$deps" "$fixed" + for font in $(grep -o '"/fonts/[^"]*"' "$deps" | tr -d '"'); do + [ -e "$original_theme/resources$font" ] && continue + case "$font" in + *.font) other="${font%.font}.fnt" ;; + *.fnt) other="${font%.fnt}.font" ;; + *) continue ;; + esac + [ -e "$original_theme/resources$other" ] && sed -i "s#\"$font\"#\"$other\"#" "$fixed" + done + if cmp -s "$deps" "$fixed"; then + rm -f "$fixed" + else + mount -o bind "$fixed" "$deps" + fi +} + +# byte offset of the first occurrence of $2 in file $1 +byteOffset() { + local want + want="$(printf '%s' "$2" | od -An -tx1 | tr -d ' \n')" + od -An -v -tx1 "$1" | tr -s ' ' '\n' | grep . | awk -v want="$want" -v n="${#2}" ' + { s = s $1; if (length(s) > 2 * n) s = substr(s, 3); if (s == want) { print NR - n; exit } }' +} + +# each firmware reads only its own palette (env_.lua loads colors_); a theme made +# for another region gets its colors_*.lua renamed in place (same length) and bound over this one +themeColorsFix() { + local env_dir="$original_theme/resources/scripts/env" + local theme_env="$full_theme_path/resources/scripts/env" + local region src fixed offset + for region in jpn usa eur; do + [ -f "$env_dir/env_$region.lua" ] && break + done + [ -f "$env_dir/env_$region.lua" ] && [ ! -f "$theme_env/colors_$region.lua" ] || return 0 + for src in usa eur jpn; do + [ -f "$theme_env/colors_$src.lua" ] && break + done + [ -f "$theme_env/colors_$src.lua" ] || return 0 + fixed="$theme_fixes/resources/scripts/env/colors_$region.lua" + mkdir -p "${fixed%/*}" + cp -f "$theme_env/colors_$src.lua" "$fixed" + offset="$(byteOffset "$fixed" "colors_$src")" + if [ -z "$offset" ]; then + rm -f "$fixed" + return 0 + fi + printf 'colors_%s' "$region" | dd of="$fixed" bs=1 seek="$offset" conv=notrunc 2>/dev/null + mount -o bind "$fixed" "$env_dir/colors_$region.lua" +} - for WAVE in $AUDIO_FILES_ARR; do - umount "${ORIGINAL_THEME_AUDIO}/${WAVE}.wav" +fontBindFix() { + [ ! -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 + cmp -s "$full_theme_path/fonts/${font_name}.fnt" "${font_path}/${font_name}.fnt" || + 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/${ui_subdir}/${font_name}.fnt" 2>/dev/null + rm -f "${font_path}/${font_name}.fnt" + fi done } -font_bind_fix() { - [ "$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" +assignedFolderTheme() { + 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 <"$theme_state_path/folder_themes" + return 1 +} + +assignedFolderMusic() { + 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 <"$theme_state_path/folder_music" + return 1 +} + +folderEntry() { + 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 +} + +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 level=0 + if [ "$folder_code" == "000" ]; then + echo "home" + return + fi + while [ "$folder_code" != "000" ] && [ "$level" -lt 16 ]; do + level=$((level + 1)) + 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 + [ "$folder_key" != "home" ] && [ -d "$themes_path/$folder_key" ] && echo "$folder_key" && return 0 + done + return 1 +} + +folderMusic() { + local folder_key + for folder_key in $1; do + assignedFolderMusic "$folder_key" && return 0 + 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 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" - 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" - [ -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 TITLE_FONT_BACKUP="${TITLE_FONT}.backup" - - 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" - 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 - - 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 + 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_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" + 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 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)" + + activeFolderExists || return 0 + + 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 - 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 /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" - fi + # an install leaves the placeholder until a theme is applied from the menu + [ "$selected_theme" = "CHANGEME" ] && [ "$full_theme_path" != "$diy_theme" ] && return 0 - local TITLE_THEME_FONT="$FULL_THEME_PATH/fonts/title.fnt" + 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 - font_bind_fix "backup" - fi - else - font_bind_fix "restore" - fi + 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 - music_hack_unbind + if [ -d "$full_theme_path/fonts" ]; then + fontBindFix "backup" + else + fontBindFix "restore" + fi - 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 + themeFixesUnbind + unbindThemeMounts + while grep -q " $original_theme overlayfs " /proc/mounts; do + umount -l "$original_theme" || break + done - if [ -f "$STATE_PATH/om_audiorandomizer" ] && [ -n "$(ls -A "$MUSIC_PATH")" ]; then - if [ "$FOLDER_ID" = "000" ]; then - music_hack_bind "$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 [ -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")" ] && music_hack_bind "$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 - 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" + local skip_bgm="none" + [ -n "$assigned_music" ] && skip_bgm="bgm_*.wav" + 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 + [ -n "$assigned_music" ] && musicHackBind "$music_path/$assigned_music" "$music_path/$assigned_music" "$original_theme_audio" + themeDepsFix + themeColorsFix - cd - >/dev/ || exit - done - + local cl_functions="$rootfs/etc/options_menu/console_language/scripts/cl_functions" + if [ -f "$cl_functions" ]; then + # shellcheck source=/dev/null + source "$cl_functions" + clInit + # older Console Language releases have no clRebind + if type clRebind >/dev/null 2>&1; then + clRebind + else + clUmountLang + clMountLang + fi + fi + [ -z "$mountpoint" ] && echo "$applied" >"$applied_file" } diff --git a/mod/etc/options_menu/themes/scripts/om_vars b/mod/etc/options_menu/themes/scripts/om_vars index 6a6d2ce..a5b7e9b 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=SC2034,SC2086,SC2154,SC3010,SC3043,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 @@ -15,713 +15,1060 @@ # 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" +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" - -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/" - -# Options Menu -omMod="$optionsMenu/$modtitle" -omModImgs="$optionsMenu/images" -omLoaders="$omModImgs/loading/" -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" -thmOverlay="$omModScripts/om_overlay" - -om_themerandomizer="$state_path/om_themerandomizer" -om_themeperfolder="$state_path/om_themeperfolder" -om_audiorandomizer="$state_path/om_audiorandomizer" -om_advmusichack="$state_path/om_advmusichack" -om_firstrun="$state_path/om_firstrun" +has_folders="${game_storage}/001" + +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" +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_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" +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" +om_active_theme="$mod_state_path/om_active_theme" #### SEPARATORS -omDummy="$omModCommands/c0000_0000" -subDummy="$omModSubCmds/c0000_0000" -omModSpacer="$omModCommands/c0000_separator" -omModSubSpacer="$omModSubCmds/c9998_separator" -omModGenSpacer="$omModGenCmds/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_cmds/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" -enableAudioSetting="$omModSettingsCmds/c0001_audiorandomizer" -disableAudioSetting="$omModSettingsCmds/_audiorandomizer" -enableAdvancedMusicHack="$omModSettingsCmds/c0001_audiorandomizerall" -disableAdvancedMusicHack="$omModSettingsCmds/_audiorandomizerall" -enableThemeSetting="$omModSettingsCmds/c0000_themerandomizer" -disableThemeSetting="$omModSettingsCmds/_themerandomizer" -enableThemePerFolder="$omModSettingsCmds/c0000_themeperfolder" -disableThemePerFolder="$omModSettingsCmds/_themeperfolder" -enableSystemCleanUp="$omModSettingsCmds/c0001_systemcleanup" -disableSystemCleanUp="$omModSettingsCmds/_systemcleanup" -enableThemeGenerator="$omModCommands/c0000_generatetheme" -disableThemeGenerator="$omModCommands/_generatetheme" -enableUninstaller="c0001_uninstaller" -disableUninstaller="_uninstaller" -enableDiY="$omModGenCmds/c0000_apply" -disableDiY="$omModGenCmds/_apply" -enableDelDiY="$omModGenCmds/c0000_remove" -disableDelDiY="$omModGenCmds/_remove" -enablePreview="$omModGenCmds/c0000_preview" -disablePreview="$omModGenCmds/_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" - -# SHARED -thm_chk="$mountpoint$profilepath/$modname/activeTheme" -system="$(echo "$s_type" | awk '{print toupper($0)}')" +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_clear_downloads="$om_mod_sub_cmds/c0000_removeThemes" +disable_clear_downloads="$om_mod_sub_cmds/_removeThemes" +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_assign" +disable_folder_themes="$om_mod_settings_cmds/_themeperfolder_assign" +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_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" +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" + +thm_chk="$mountpoint$profilepath/$modname/active_theme" +catalog_type="$s_type" +[ "$s_type" = "hvc" ] && catalog_type="nes" +system_name="$(echo "$catalog_type" | awk '{print toupper($0)}')" +seen_themes="$mod_state_path/seen_themes_$catalog_type" 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" -nes_theme_path="${om_mod_path}/nes " +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 _ +} +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" #### 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" +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" 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)" - -################ -# shellcheck disable=SC2154 +original_theme_ui0="ui/$sftype-$sfregion" case "$sftype" in -hvcj) - items_to_delete="$del_on_shonen" - ;; -nes) - items_to_delete="$del_on_nes" - ;; + 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" + +#### MAIN MENU +# one bar per script, or two would draw over each other +loadingSplash() { + [ -n "$loading_splash" ] && return + loading_splash=1 + [ $# -gt 0 ] || set -- LOADING + omLoading "$@" +} -hasThemesToInstall() { - if [ "$has_theme" != 0 ]; then - for f in *.tar.gz; do - tar -xzvf "$f" && rm -f "$f" - done - fi +hookChmenu() { + grep -q "# themeselector" "$chmenu_file" && return + # sed -i gives the rootfs file a new inode, which /bin keeps hiding until a reboot + # shellcheck disable=SC1004 + sed '/^overmount_games.*/a # themeselector fix \ +themeSelector \ +fontFix' /bin/chmenu >/tmp/chmenu.new && cat /tmp/chmenu.new >/bin/chmenu + rm -f /tmp/chmenu.new } -clearList() { - find "$omModCommands" "$omModScripts" -type f \ - -not -name 'om_*' -not -name 'c0000_*' \ - -not -name 'c999[0-9]_*' -not -name '_*' -print0 -exec rm {} \; - remove "$omModSpacer" +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 + loadingSplash THEME_MOVING_LINE1 THEME_MOVING_LINE2 + 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 } -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 {} \; +hasThemesToInstall() { + local f + for f in "$theme_path/$system_name".*.tar.gz; do + [ -f "$f" ] || continue + loadingSplash + tar -xzf "$f" -C "$theme_path" && rm -f "$f" + done } -enableMenuOptions() { - enable_cmds "Pole Mario" "Luigi" "Wall" "WallNear" "WallFar" "Packed" "ThemeGenerator" "Clear" - disable_cmds "ColorsNes" "PackedNes" "Smb" "Settings" "Color" +toggleThemeCmds() { + if [ -z "$(ls -A "$theme_path")" ]; then + disableCmds "theme_generator" "clear" + else + enableCmds "theme_generator" "clear" + fi + [ ! -d "$has_folders" ] && disableCmds "theme_generator" +} - if [ ! -f "$omDummy" ]; then - disable_cmds "Clear" +nesSpriteNames() { + sprite_src="hvc" + sprite_dest="nes" + if [ "$sfregion" = "jpn" ]; then + sprite_src="nes" + sprite_dest="hvc" fi +} - toggleDownloads +enableMenuOptions() { + disableCmds "settings" "color" - # shellcheck disable=SC2039 - [[ -n $(ls -A "$theme_path") ]] && disable_cmds "ThemeGenerator" + toggleDownloads + toggleThemeCmds if [ -f "$b_file" ] || [ -f "$p_file" ]; then - enable_cmds "Settings" + enableCmds "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" + 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 - disable_cmds "Pole" "Mario" "Luigi" "Wall" "WallNear" "WallFar" "Color" "Packed" - enable_cmds "ColorsNes" "PackedNes" "Smb" - - if [ "$sftype" = "nes" ]; then - src="hvc" - dest="nes" - # shellcheck disable=SC2154 - if [ "$sfregion" = "jpn" ]; then - src="nes" - dest="hvc" - fi - - for subdir in "${omModPackedNes}/assets"/*; do - rename "${subdir}/${src}.png" "${subdir}/${dest}.png" - done - fi + nesSpriteNames + 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" } -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 - 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" - 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)" - tmp_thm_script="$omThemeScript" +#### INSTALLED THEMES +themeIsBindable() { + 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 +} - if [ ! -f "$full_path/info" ]; then - echo "NAME=$thm_title [${size}b]" >"$full_path/info" - fi +clearList() { + 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 "$om_mod_spacer" +} - if [ ! -f "$full_path.png" ]; then - if [ -d "$usb_path" ]; then - copier "$thmPreview/$thm.png" "$full_path.png" - else - link "$thmPreview/$thm.png" "$full_path.png" - fi - fi +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" ] || [ "$(cat "$thm_chk" 2> /dev/null)" != "$(cat "$om_active_theme" 2> /dev/null)" ] +} - create "$omModSpacer" +themeVars() { + thm="$1" + full_path="$theme_path/$thm" + cmd_list="$om_mod_commands/c0001_$thm" + thm_script="$om_mod_scripts/$thm" +} - remove "$cmd_list" - touch "$cmd_list" +themeDeleteLine() { + if [ "${active_theme##*/}" = "$thm" ]; then + echo "DELETE_BLOCKED_KEY=THEME_DELETE_IN_USE" >> "$cmd_list" + else + echo "DELETE_STR=rm -rf \"$full_path\" \"$full_path.png\" \"$cmd_list\" \"$thm_script.sh\" && usleep 50000 && sh %options_path%scripts/om_theme_selector &" >> "$cmd_list" + fi +} - if [ "$sftype" = "nes" ]; then - src="hvc" - dest="nes" +themeCommand() { + themeVars "$1" + resource_path="$full_path/resources" + sprite_path="$resource_path/sprites" + script_path="$resource_path/scripts" + tmp_thm_script="$om_theme_script" - if [ "$sfregion" = "jpn" ]; then - src="nes" - dest="hvc" - fi + 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')" + size="$(du -sh "$full_path" | cut -f1)" + echo "NAME=$thm_title [${size}b]" > "$full_path/info" + fi - for ext in "png" "json"; do - rename "${sprite_path}/${src}.${ext}" "${sprite_path}/${dest}.${ext}" - done - rename "${script_path}/colors_${src}.lua" "${script_path}/colors_${dest}.lua" + 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 - 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\" -touch \"$hadThumb\" -done" >>"$thm_script.sh" + if [ ! -s "$full_path.png" ]; then + if [ -d "$usb_path" ]; then + copier "$thm_preview/$thm.png" "$full_path.png" else - echo "if [ -f \"$hadThumb\" ]; then - sh $omModScripts/om_restorethumb - rm \"$hadThumb\" -fi" >>"$thm_script.sh" + link "$thm_preview/$thm.png" "$full_path.png" fi + fi - if grep -q -w "$thm" "$thmOverlay" || [ ! -d "$has_folders" ]; then - tmp_thm_script="$omOverlayThemeScript" - fi + create "$om_mod_spacer" + + if [ "$sftype" = "nes" ]; then + nesSpriteNames + for ext in "png" "json"; do + rename "${sprite_path}/${sprite_src}.${ext}" "${sprite_path}/${sprite_dest}.${ext}" + done + rename "${script_path}/colors_${sprite_src}.lua" "${script_path}/colors_${sprite_dest}.lua" + 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\" -# 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" + copy "$om_mod_license" "$thm_script.sh" - echo "sleep 1 - hakchi reboot" >>"$thm_script.sh" + if [ ! -d "$has_folders" ]; then + tmp_thm_script="$om_overlay_theme_script" + fi - 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" + 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\" +" >> "$thm_script.sh" - cmdName="$(sed -r 's/NAME=//g' <"$full_path/info")" - echo "COMMAND_NAME=$cmdName -COMMAND_TYPE=INTERNAL + echo "loadingSplash THEME_APPLYING_LINE1 THEME_APPLYING_LINE2 +sleep 1 + hakchi reboot" >> "$thm_script.sh" + + 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/preview_diy.png" + [ ! -f "$preview_image" ] && preview_image="$options_menu/images/preview_default.png" + + IFS= read -r cmd_name < "$full_path/info" + cmd_name="${cmd_name#NAME=}" + echo "COMMAND_NAME=$cmd_name +COMMAND_TYPE=EXTERNAL 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=$preview_image" > "$cmd_list" + themeDeleteLine +} + +themeLoader() { + local dir + loadingSplash + removeRegionalTheme + clearList + active_theme="$(cat "$thm_chk" 2> /dev/null)" + echo "$active_theme" > "$om_active_theme" + for dir in "$theme_path"/*/; do + dir="${dir%/}" + [ -d "$dir" ] && themeCommand "${dir##*/}" done } -remove_regional_theme() { - region_files="$omModSubCmds/c0010_snesce-$sfregion $omModSubScripts/getSnes$sfregion $omModSub/$sfregion.png $omModSub/snesce-$sfregion.png" - for item in $region_files; do - remove "$item" +themeSync() { + local previous + local missing + removeRegionalTheme + missing="$(cd "$theme_path" && for thm in */; do thm="${thm%/}"; [ -d "$thm" ] && [ ! -f "$om_mod_commands/c0001_$thm" ] && echo "$thm"; done)" + [ "$(echo "$missing" | grep -c .)" -ge 5 ] && loadingSplash + previous="$(cat "$om_active_theme" 2> /dev/null)" + active_theme="$(cat "$thm_chk" 2> /dev/null)" + echo "$active_theme" > "$om_active_theme" + for cmd_list in "$om_mod_commands"/c0001_*; do + [ -f "$cmd_list" ] || continue + thm="${cmd_list##*/c0001_}" + [ -d "$theme_path/$thm" ] || remove "$cmd_list" "$om_mod_scripts/$thm.sh" + done + echo "$missing" | while IFS= read -r thm; do + [ -n "$thm" ] && themeCommand "$thm" + done + [ "$previous" = "$active_theme" ] && return + for thm in "${previous##*/}" "${active_theme##*/}"; do + if [ -n "$thm" ] && [ -f "$om_mod_commands/c0001_$thm" ]; then + themeVars "$thm" + sed -i '/^DELETE_/d' "$cmd_list" + themeDeleteLine + fi done - [ -d "$theme_path/snesce-$sfregion" ] && rm -rf "$theme_path/snesce-$sfregion" } -get_theme_list() { - decodepng "$omLoaders/downloadthemelist.png" >/dev/fb0 - cd "$omModSub" || exit +removeRegionalTheme() { + [ "$(globalTheme)" = "$sfregion" ] && return + remove "$theme_path/$sfregion" "$theme_path/$sfregion.png" \ + "$om_mod_commands/c0001_$sfregion" "$om_mod_scripts/$sfregion.sh" +} - 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}" - else - netChecker - fi +loadThemeList() { + if [ -n "$(ls -A "$theme_path")" ]; then + create "$om_dummy" + case "$1" in + -r) themeLoader ;; + -s) themeSync ;; + esac + else + remove "$om_mod_spacer" + clearList + fi +} +#### DOWNLOADS +getThemeList() { + local catalog="${catalog_type}${list_name}" + local hash="${catalog_type}-list-v2.md5" + if [ ! -x "$om_scripts/downloader" ]; then + omSplash DOWNLOADER_MISSING_LINE1 DOWNLOADER_MISSING_LINE2 + sleep 3 + return 1 + fi + loadingSplash THEME_LIST_DOWNLOADING_LINE1 THEME_LIST_DOWNLOADING_LINE2 + cd "$om_mod_sub" || exit + rm -f "/tmp/$hash" + if "$om_scripts/downloader" "/tmp/$hash" "$themes_github_url/$hash" > /dev/null 2>&1 && cmp -s "/tmp/$hash" "$hash"; then + rm -f "/tmp/$hash" + touch "$sub_dummy" + return 0 fi - tar -xzf "${s_type}${listName}" && rm -f *.tar.gz - remove_regional_theme - touch "$subDummy" - create "$omModSubSpacer" - usleep 1000 + omDownload "$catalog" "$themes_github_url/$catalog" "$catalogs_classicmods_url/$catalog" || return + clearDownloads + omUnpack "$catalog" . || return + [ -f "/tmp/$hash" ] && mv "/tmp/$hash" "$hash" + markNewThemes + removeRegionalTheme + touch "$sub_dummy" } -injectFile() { - # shellcheck disable=SC2068 - for scrpt in $@; do - case "$scrpt" in - chmenu) - injected_file="$chmenu_file" - ;; - fontfix) - injected_file="$fontfix_file" - ;; - esac +clearDownloads() { + find "$om_mod_sub" -type f \ + -not -name 'c000[0-8]_*' -not -name 'c999[0-9]_*' -not -name "${catalog_type}${list_name}" \ + -not -name 'om_*' -not -name '_*' -not -path "$thm_preview/*" -exec rm {} + +} - if ! grep -q "# themeselector" "$injected_file"; then - if [ "$scrpt" = "chmenu" ]; then - # shellcheck disable=SC1004 - sed -i '/^overmount_games.*/a # themeselector fix \ -themeSelector \ -fontFix' "/var/lib/hakchi/rootfs/bin/chmenu" - fi - - 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") \ - fontFix_getPath0 "$active_theme/fonts/$1" && return 0 \ - fi' "$mountpoint/etc/preinit.d/b8025_fontfix" - fi - fi - done +# no seen list yet: a first run marks nothing as new +markNewThemes() { + local catalog_themes + local theme + catalog_themes="$(find "$om_mod_sub_cmds" -name 'c0010_*' | sed 's#.*/c0010_##' | grep -vx all)" + if [ -f "$seen_themes" ]; then + for theme in $catalog_themes; do + grep -qx "$theme" "$seen_themes" || printf '\nNEW=TRUE\n' >> "$om_mod_sub_cmds/c0010_$theme" + done + fi + { cat "$seen_themes" 2> /dev/null; echo "$catalog_themes"; } | sort -u > "$seen_themes.tmp" && mv "$seen_themes.tmp" "$seen_themes" } -loadThemeList() { - if [ -n "$(ls -A "$theme_path")" ]; then - create "$omDummy" - if [ "$1" = "-r" ]; then - themeLoader - fi +showDownloads() { + if ls "$om_mod_sub_cmds"/c0010_* > /dev/null 2>&1; then + enableCmds "clear_downloads" else - remove "$omModSpacer" - clearList + disableCmds "clear_downloads" fi + $options_menu/lib/theme_manager --commandPath $om_mod_sub_cmds/ --scriptPath $om_mod_sub_scripts --title AVAILABLE_THEMES --downloads --themePath "$theme_path" & } -# verify themeGenerator function +# the catalog's get_ scripts call this by name themeGenerator() { - echo "Packaging: DefKorns" - echo "Category: User Interface Mod " - echo "" - echo "Checking selected theme files." - echo "" + translate CHECKING_THEME_FILES theme_name="${1}" - full_title="$(grep -F 'NAME=' "${omModSubCmds}/c0010_${theme_name}" | sed -e 's/.*=\(.*\)\[.*/\1/')" - full_path="${theme_path}/${theme_name}" + failed="" + [ -d "${theme_path}" ] || mkdir -p "${theme_path}" cd "${theme_path}" || exit - - 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 - echo "Downloading..." - wget "${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}" - tar -xzvf "${system}.${t}.tar.gz" && remove "${system}.${t}.tar.gz" - done <"${sftype}-all" + 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 + omDownload "${catalog_type}-all" "$themes_github_url/${catalog_type}-all" "$catalogs_classicmods_url/${catalog_type}-all" + rc=$? + if [ "$rc" = 0 ]; then + downloadThemes "${catalog_type}-all" + rc=$? 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 "Installation complete." + remove "${catalog_type}-all" else - netChecker + downloadTheme "$theme_name" + rc=$? + [ "$rc" = 0 ] && writeThemeInfo "$theme_name" + fi + + removeRegionalTheme + echo "" + case "$rc" in + 0) translate INSTALLATION_COMPLETE ;; + 4) translate ALL_THEMES_INSTALLED ;; + esac + if [ -n "$failed" ]; then + echo "" + translate THEMES_FAILED + echo "$failed" + return 1 + fi + return "$rc" +} + +urlEncodeName() { + printf '%s' "$1" | sed 's/%/%25/g; s/#/%23/g; s/?/%3F/g' +} + +writeThemeInfo() { + info_title="$1" + info_cmd="${om_mod_sub_cmds}/c0010_$1" + [ -f "$info_cmd" ] || return 0 + sed -i '/^NEW=TRUE$/d' "$info_cmd" + 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" +} + +downloadTheme() { + 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" + while IFS= read -r t; do + { [ -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 + omDownloadAll "$pending" installDownloaded 2 + rc=$? + remove "$pending" + return "$rc" +} + +installDownloaded() { + t="${2#"$system_name".}" + t="${t%.tar.gz}" + t_rc="$1" + if [ "$t_rc" = 0 ]; then + unpackTheme "$t" && writeThemeInfo "$t" + t_rc=$? fi + [ "$t_rc" = 0 ] && echo "$info_title" + case "$t_rc" in + 0) ;; + 3) downloadError 3 && return 3 ;; + *) failed="$failed $t" ;; + esac +} +abortDownload() { + trap - TERM INT HUP + [ -n "$unpacking" ] && rm -rf "$unpacking" "$unpacking.png" + rm -f "$system_name".*.tar.gz "/tmp/${sftype}-pending" "${catalog_type}-all" + exit 1 } -find_diy_asset() { +unpackTheme() { + unpacking="$1" + omUnpack "${system_name}.$1.tar.gz" . + dl_rc=$? + [ "$dl_rc" = 0 ] || rm -rf "$1" "$1.png" + unpacking="" + return "$dl_rc" +} + +#### DIY +diyAssetSpec() { 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" colors="0" + preview_square="0" + preview_cols="" + preview_top="0" + theme_preview="" json="" - preview_x="920" - preview_y="275" - preview_w="250" - 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_square="1" + preview_cols="5" + ;; + wall | wall_near | wall_far) + preview_square="1" + preview_cols="5" + ;; + sbm) + diy_file="characters_sbm.png" + asset_path="sprites" + asset="characters_sbm.png" + chars="1" + ;; + packed) + asset_path="sprites" + preview_square="1" + preview_cols="4" + preview_top="1" + theme_preview="extras/preview_baseui.png" + ;; + packed_nes) + diy_file="${sftype}.png" + [ "$sfregion" = "jpn" ] && diy_file="hvc.png" + asset_path="sprites" + asset="$diy_file" + preview_square="1" + preview_cols="4" + preview_top="1" + theme_preview="extras/preview_baseui.png" + ;; + 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_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 + 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 +} + +findDiyAsset() { + loadingSplash - om_default_json="$omModScripts/om_${1}.json" - paths_to_search="$omModSprites/assets/$1 $theme_path" + diyAssetSpec "$1" + diyCropFrame "$1" + 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_*' -not -name '_*' -print0 -exec rm {} \; + -not -name 'om_*' -not -name 'c999[0-9]_*' -not -name 'c0000_*' \ + -not -name 'c0001_*' -not -name '_*' -exec rm {} + + + 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="" 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 + # 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 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" [ -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" - 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" + + 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="${om_mod_sprites}/assets/${1}/${1}_${asset_name}.png" if [ "$colors" = "1" ]; then preview_image="$temp_preview" fi [ "$chars" = "1" ] && temp_preview="$preview_image" - if [ -n "$json" ] && [ ! -f "$temp_preview" ]; then - decodepng "$omLoaders/loading.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" + 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" + elif [ "$colors" = "0" ] && [ "$chars" != "1" ] && [ -n "$diy_crop_x" ]; then + if [ ! -f "$temp_preview" ]; then + 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 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" || + { [ -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')" + + preview_nearest="" + [ "$chars" = "1" ] && preview_nearest=" +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" + 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 +PREVIEW_HIDE_LABEL=TRUE" 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" >"$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" 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 } diySpriteSelector() { - diySelectedAsset="$1" - diyLayout="$diyResources/$2" - diyAsset="$diyLayout" - - [ ! -d "$diyAsset" ] && mkdir -p "$diyAsset" - find "$diySelectedAsset" -name "$3" -exec cp {} "$diyLayout" \; + diy_selected_asset="$1" + diy_layout="$diy_resources/$2" + diy_asset="$diy_layout" + + [ ! -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' "$omThemeScript" >"$b_file" - echo themeSelector >"$p_file" + 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 "" - echo "Asset applied successfully! " - 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." - echo "" - echo "" } diyThemeChecker() { - [ "$1" = "loader" ] && decodepng "$omLoaders/loadingassets.png" >/dev/fb0 + [ "$1" = "loader" ] && loadingSplash THEME_LOADING_ASSETS + + [ ! -d "$diy_theme_path" ] && mkdir -p "$diy_theme_path" - [ ! -d "$diyThemePath" ] && mkdir -p "$diyThemePath" - create "$omModGenSpacer" - 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 - + + diyHomePreviews + [ "$1" = "loader" ] && usleep 1000 } -diyThemeRemover() { - disable_cmds "DiY" "DelDiY" "Preview" +diyStockPreview() { + stock_preview="" + [ "$colors" = "1" ] && return + [ "$sftype" = "hvcj" ] && return + 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" +} - for item in $del_arr; do - find "$item" -type f -not -name 'c999[0-9]_*' -not -name 'om_*' -maxdepth 1 -print0 -exec rm {} \; - done +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 - [ -d "$diyThemePath" ] && find "$diyThemePath" -type f -not -name 'assets_here' -print0 -exec rm {} \; + 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' -modCleanUp() { - for item in $items_to_delete; do - remove "$item" + 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 - hasItemsToCleanUp "SystemCleanUp" } -themeSettings() { - if [ "$1" = "tpf" ]; then - themeSettingsChecker "$om_themeperfolder" "$enableThemePerFolder" +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 {} + + +} + +#### SETTINGS +toggleFlag() { + if [ -f "$1" ]; then + remove "$1" else - themeSettingsChecker "$om_themerandomizer" "$enableThemeSetting" + touch "$1" fi } +themeSettings() { + case "$1" in + tpf) toggleFlag "$om_themeperfolder" ;; + mpf) toggleFlag "$om_musicperfolder" ;; + *) toggleFlag "$om_themerandomizer" ;; + esac +} + +# music for every folder needs the random music on 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 + toggleFlag "$om_advmusichack" + [ -f "$om_advmusichack" ] && touch "$om_audiorandomizer" else - if [ ! -f "$om_audiorandomizer" ]; then - audioSettingsChecker "on" "$om_audiorandomizer" "$enableAudioSetting" - else - audioSettingsChecker "off" "$om_audiorandomizer" "$enableAudioSetting" - audioSettingsChecker "off" "$om_advmusichack" "$enableAdvancedMusicHack" - fi + toggleFlag "$om_audiorandomizer" + [ -f "$om_audiorandomizer" ] || remove "$om_advmusichack" fi } -themeSettingsChecker() { - if [ ! -f "$1" ]; then - touch "$1" - switchOn "$2" - else - remove "$1" - switchOff "$2" - fi +hasCleanUpItems() { + local item + for item in $items_to_delete; do + [ -e "$item" ] && return 0 + done + return 1 } -audioSettingsChecker() { - if [ "$1" = "off" ]; then - remove "$2" - switchOff "$3" +modCleanUp() { + for item in $items_to_delete; do + remove "$item" + done + settingsRows +} + +settingsRows() { + if hasCleanUpItems; then + enableCmds "system_clean_up" + else + disableCmds "system_clean_up" + fi + if [ -f "$b_file" ] && [ -f "$p_file" ]; then + enableCmds "uninstaller" + else + disableCmds "uninstaller" + fi + if [ -f "$enable_system_clean_up" ] || [ -f "$enable_uninstaller" ]; then + create "$om_mod_settings_spacer" else - touch "$2" - switchOn "$3" + remove "$om_mod_settings_spacer" fi } -hasItemsToCleanUp() { - for item in $items_to_delete; do - if [ -e "$item" ]; then - has_non_system_files="true" - break - fi - done - if [ "$has_non_system_files" = "true" ]; then - enable_cmds "$1" - [ ! -f "$omModSettingsSpacer" ] && touch "$omModSettingsSpacer" +musicPerFolder() { + [ -f "$om_musicperfolder" ] && hasMusicFiles "$music_path" +} + +folderThemesRow() { + if [ -f "$om_themeperfolder" ] && [ -d "$has_folders" ]; then + enableCmds "folder_themes" + else + disableCmds "folder_themes" + fi + if hasMusicFiles "$music_path"; then + enableCmds "music_per_folder" + else + disableCmds "music_per_folder" + fi + if musicPerFolder && [ -d "$has_folders" ]; then + enableCmds "folder_music" else - disable_cmds "$1" - [ ! -f "$enableUninstaller" ] && remove "$omModSettingsSpacer" + disableCmds "folder_music" fi } -hasSelectedOptions() { - if [ -f "$1" ]; then - if ! grep -q "\[X\]" "$2"; then - switchOn "$2" - fi - else - switchOff "$2" +# a folder entry living in games/

/CLV-S-00/ is folder inside folder

+listFolders() { + tab="$(printf '\t')" + 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 + 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="" + [ -f "$theme_path/$1/info" ] && IFS= read -r title < "$theme_path/$1/info" + title="${title#NAME=}" + title="${title% [[]*]}" + [ -z "$title" ] && title="$1" +} + +themePreview() { + preview="$thm_preview/$1.png" + [ ! -f "$preview" ] && preview="$theme_path/$1.png" + [ ! -f "$preview" ] && preview="$options_menu/images/preview_default.png" +} + +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="$folder_themes_tmp" + rm -rf "$data_dir" && mkdir -p "$data_dir" + { + printf 'home\t%s\t0\n' "$(translate HOME_FOLDER)" + listFolders + } >"$data_dir/folders.list" + music_themes="$(find "$theme_path" -mindepth 2 -type f -name "*.wav" | sed "s#^$theme_path/##; s#/.*##" | sort -u)" + find "$theme_path" -mindepth 1 -maxdepth 1 -type d | sed 's#^.*/##' | sort | while IFS= read -r thm; do + has_music=0 + echo "$music_themes" | grep -qxF "$thm" && has_music=1 + needs_overlay=0 + [ -f "$theme_path/$thm/needs_overlay" ] && needs_overlay=1 + themeTitle "$thm" + themePreview "$thm" + printf '%s\t%s\t%s\t%s\t%s\n' "$thm" "$title" "$preview" "$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 $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" + $options_menu/lib/theme_manager "$@" & +} 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_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_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/settings/commands/_musicperfolder b/mod/etc/options_menu/themes/settings/commands/_musicperfolder new file mode 100644 index 0000000..35d56e5 --- /dev/null +++ b/mod/etc/options_menu/themes/settings/commands/_musicperfolder @@ -0,0 +1,6 @@ +COMMAND_NAME=MUSIC_PER_FOLDER +COMMAND_TYPE=INTERNAL +RESTART_UI=FALSE +COMMAND_STR=sh %script_dir%/om_toggle 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_assign new file mode 100644 index 0000000..e57bd0d --- /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 theme & +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 8783149..f5ab1e2 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder +++ b/mod/etc/options_menu/themes/settings/commands/c0000_themeperfolder @@ -1,7 +1,6 @@ -COMMAND_NAME=Theme Per Folder -COMMAND_TYPE=EXTERNAL +COMMAND_NAME=THEME_PER_FOLDER +COMMAND_TYPE=INTERNAL RESTART_UI=FALSE -COMMAND_STR=sh %script_dir%/om_theme tpf -PREVIEW_IMAGE=/etc/options_menu/images/randtheme.png -PREVIEW_IMAGE_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file +COMMAND_STR=sh %script_dir%/om_toggle tpf +STATE_STR=sh %script_dir%/om_state theme_per_folder +PREVIEW_IMAGE=/etc/options_menu/images/preview_randtheme.png diff --git a/mod/etc/options_menu/themes/settings/commands/c0000_themerandomizer b/mod/etc/options_menu/themes/settings/commands/c0000_themerandomizer index 52b5916..96179d7 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0000_themerandomizer +++ b/mod/etc/options_menu/themes/settings/commands/c0000_themerandomizer @@ -1,7 +1,6 @@ -COMMAND_NAME=Theme Randomizer -COMMAND_TYPE=EXTERNAL +COMMAND_NAME=THEME_RANDOMIZER +COMMAND_TYPE=INTERNAL 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 +COMMAND_STR=sh %script_dir%/om_toggle randomizer +STATE_STR=sh %script_dir%/om_state theme_randomizer +PREVIEW_IMAGE=/etc/options_menu/images/preview_theme_randomizer.png diff --git a/mod/etc/options_menu/themes/creator/sprites/pole/commands/c9998_separator b/mod/etc/options_menu/themes/settings/commands/c0000_zseparator similarity index 100% rename from mod/etc/options_menu/themes/creator/sprites/pole/commands/c9998_separator rename to mod/etc/options_menu/themes/settings/commands/c0000_zseparator diff --git a/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizer b/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizer index 81c1119..12e6126 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizer +++ b/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizer @@ -1,7 +1,6 @@ -COMMAND_NAME=Audio Randomizer: Home -COMMAND_TYPE=EXTERNAL +COMMAND_NAME=AUDIO_RANDOMIZER_HOME +COMMAND_TYPE=INTERNAL 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 +COMMAND_STR=sh %script_dir%/om_toggle audio +STATE_STR=sh %script_dir%/om_state audio_randomizer +PREVIEW_IMAGE=/etc/options_menu/images/preview_audio_randomizer_home.png diff --git a/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizerall b/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizerall index 2ec6eb0..ffeccc0 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizerall +++ b/mod/etc/options_menu/themes/settings/commands/c0001_audiorandomizerall @@ -1,7 +1,7 @@ -COMMAND_NAME= All Folders (Only Themes Without ANY Audio Files) -COMMAND_TYPE=EXTERNAL +COMMAND_NAME=AUDIO_RANDOMIZER_ALL_FOLDERS +COMMAND_TYPE=INTERNAL RESTART_UI=FALSE -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 +CHILD=TRUE +COMMAND_STR=sh %script_dir%/om_toggle audio -amh +STATE_STR=sh %script_dir%/om_state audio_randomizer_all +PREVIEW_IMAGE=/etc/options_menu/images/preview_audio_randomizer_all.png diff --git a/mod/etc/options_menu/themes/settings/commands/c0001_systemcleanup b/mod/etc/options_menu/themes/settings/commands/c0001_systemcleanup index aeff326..8bac2bb 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0001_systemcleanup +++ b/mod/etc/options_menu/themes/settings/commands/c0001_systemcleanup @@ -1,7 +1,5 @@ -COMMAND_NAME=Clean Up +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 diff --git a/mod/etc/options_menu/themes/settings/commands/c0001_uninstaller b/mod/etc/options_menu/themes/settings/commands/c0001_uninstaller index 97c18a5..8d6f4d3 100644 --- a/mod/etc/options_menu/themes/settings/commands/c0001_uninstaller +++ b/mod/etc/options_menu/themes/settings/commands/c0001_uninstaller @@ -1,7 +1,5 @@ -COMMAND_NAME=Reset Settings +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_X=920 -PREVIEW_IMAGE_Y=262 \ No newline at end of file +PREVIEW_IMAGE=/etc/options_menu/images/preview_uninstall.png 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 15ce5c5..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/scripts/om_restorethumb b/mod/etc/options_menu/themes/settings/scripts/om_folderthemes similarity index 80% rename from mod/etc/options_menu/themes/scripts/om_restorethumb rename to mod/etc/options_menu/themes/settings/scripts/om_folderthemes index d115b36..94c7826 100644 --- a/mod/etc/options_menu/themes/scripts/om_restorethumb +++ b/mod/etc/options_menu/themes/settings/scripts/om_folderthemes @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2022 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 @@ -15,9 +16,8 @@ # 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 +folderThemesMenu "${1:-theme}" diff --git a/mod/etc/options_menu/themes/settings/scripts/om_reset b/mod/etc/options_menu/themes/settings/scripts/om_reset index 0439865..ab31691 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_reset +++ b/mod/etc/options_menu/themes/settings/scripts/om_reset @@ -1,25 +1,33 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 +# +# Copyright (c) 2026 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 # 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 disable=SC2154,SC1090,SC2039,SC2086 +# shellcheck source=/dev/null 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_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/options --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & +$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 new file mode 100644 index 0000000..3154124 --- /dev/null +++ b/mod/etc/options_menu/themes/settings/scripts/om_state @@ -0,0 +1,35 @@ +#!/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 . +# +# 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 + source $mountpoint/etc/options_menu/scripts/om_functions + script_init +fi + +case "$1" in + 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 498fbb6..85656c0 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,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 @@ -15,13 +15,14 @@ # 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 -decodepng "$omLoaders/loading.png" >/dev/fb0 +loadingSplash modCleanUp usleep 1000 -$optionsMenu/options --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & +$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_toggle similarity index 75% rename from mod/etc/options_menu/themes/settings/scripts/om_theme rename to mod/etc/options_menu/themes/settings/scripts/om_toggle index bd05f99..275a52d 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_theme +++ b/mod/etc/options_menu/themes/settings/scripts/om_toggle @@ -1,6 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2086,SC2154,SC3046,SC3051 # -# Copyright (c) 2022 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 @@ -15,10 +16,12 @@ # 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=/dev/null source $mountpoint/etc/options_menu/themes/scripts/om_vars script_init -themeSettings "$1" - -$optionsMenu/options --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & +case "$1" in + audio) audioSettings "$2" ;; + *) themeSettings "$1" ;; +esac +folderThemesRow diff --git a/mod/install b/mod/install index 1907cbe..d68cf9c 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) 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 @@ -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 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* \ + "$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" @@ -46,20 +73,46 @@ del "$rootfs/fontfix" del "$rootfs/chmenu" del "$rootfs/chmenu_backup" del "$rootfs/b8025_fontfix_backup" +del "$ts_state/om_themeperfolderchild" -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 transfer_default - -om_firstrun="$mountpoint/$profilepath/$modname/om_firstrun" +# 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 "$usr/bin/pngpack-upx" +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 +find "$rootfs/etc/options_menu/themes/creator/sprites" -path '*/commands/c0001_*' -type f -exec rm {} + 2>/dev/null +del "$rootfs/etc/options_menu/themes/settings/commands/c0000_themeperfolder_1assign" +ts_root="etc/options_menu/themes" +for shipped_cmd in "$transferpath/$ts_root/commands/"* "$transferpath/$ts_root/"*/commands/*; do + cmd_name="${shipped_cmd##*/}" + cmd_dir="${shipped_cmd%/*}" + cmd_dir="$rootfs/${cmd_dir#"$transferpath/"}" + case "$cmd_name" in + _*) for renamed_cmd in "$cmd_dir"/c[0-9][0-9][0-9][0-9]"$cmd_name"; do del "$renamed_cmd"; done ;; + c[0-9][0-9][0-9][0-9]_*) del "$cmd_dir/_${cmd_name#*_}" ;; + esac +done +chmod +x "$rootfs/bin/sprite_crop" +del "$rootfs/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 + +# the previous version's uninstall strips the chmenu hook; at boot the rootfs write shows at once +if ! grep -q "# themeselector fix" "$rootfs/bin/chmenu"; then + # shellcheck disable=SC1004 + sed -i '/^overmount_games.*/a # themeselector fix \ +themeSelector \ +fontFix' "$rootfs/bin/chmenu" +fi sed '1,2d' "$om_themes_scripts/om_themescript" >"$b_file" [ ! -f "$p_file" ] && echo themeSelector >"$p_file" diff --git a/mod/readme.md b/mod/readme.md index 176355b..a467b15 100644 --- a/mod/readme.md +++ b/mod/readme.md @@ -1,75 +1,76 @@ # 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) 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) (**Off by default**) -* 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://raw.githubusercontent.com/wiki/DefKorns/om_theme-selector/images/demo_video.png)](https://youtu.be/3UilWr1NvFA) -### 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 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. -### 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`. +Open the Options Menu (hold **L+R** on a SNES/Super Famicom, **B+Down** on a NES/Famicom) and go to **Themes**. -### 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. +Full guides: [github.com/DefKorns/om_theme-selector/wiki](https://github.com/DefKorns/om_theme-selector/wiki) -### I own a Famicom/Shonen/Super Famicom, can i install this? -The mod is compatible with all Nintendo Classic consoles including all regions. +## Features -### 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. +- **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 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) +- 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 -### 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) +## Getting themes -### How do i use it +**From the console** (needs the Wi-Fi mod): go to **Themes → Download Themes**, select **Update Theme List**, then pick a theme and press **A**; a progress bar shows the download. Back in **Installed Themes**, select it and press **A** to apply it. -**NAND or USB/SD** +**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 open **Themes**: packages are unpacked and new themes added on their own. -- Install Hmod -- Download the themes and enjoy +## Theme and music per folder -**NAND** themes will be on `/var/lib/hakchi/usr/share/themes/your-system` +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. -**USB/SD** themes will be on `/media/hakchi/themes/your-system` +**Music Per Folder** works the same way with the `.wav` files in `music_menu`, with or without Theme Per Folder. -> **Note:** Where `your-system` changes depending on own mini `nes`, `snes` or `shonen` +A theme named after a folder (lower snake_case, e.g. `nintendo_-_nintendo_entertainment_system`) or named `default` (for Home) still works too. -### Notes -Enjoy it as much as i did making it. +## Requirements -### Credits -- [DefKorns](https://gitlab.com/DefKorns) -- [DanTheMan827](https://github.com/DanTheMan827) (packed.png extractor) +- [Hakchi2 CE](https://github.com/TeamShinkansen/hakchi2/releases/latest) +- [My Options Menu fork](https://github.com/DefKorns/OptionsMenu/releases) v2.1.0 or newer +- Hakchi Wi-Fi mod (WPA Supplicant), from KMFDManic's store in hakchi — only to download themes from the console + - Recommended adapter: TP-Link TL-WN725N (Realtek 8188 chipset). Other adapters may work, but aren't guaranteed to. +- Font Remount mod (`font_remount`), one of hakchi's built-in mods — applies the theme fonts +- A USB/SD drive is recommended: themes are often 5–30 MB each + +## 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. + +## Credits + +- [DefKorns](https://github.com/DefKorns) +- [DanTheMan827](https://github.com/DanTheMan827) — packed.png extractor +- Theme authors — credited on each theme in the download catalog + +## Thanks -### Thanks - AluCarD +- Aranthys +- AxelStone - [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) diff --git a/mod/uninstall b/mod/uninstall index 8fec0f0..4322dad 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) 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 @@ -16,98 +16,125 @@ # 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" -TITLE_FONT="$FONT_PATH/title.fnt" -TITLE_FONT_BACKUP="${TITLE_FONT}.backup" -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" -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/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" +sed -i '/# themeselector fix *$/,+2d' "$rootfs/bin/chmenu" +sed -i '/# themeselector$/,+4d' "$rootfs/etc/preinit.d/b8025_fontfix" +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/c0090_ThemeOptions" -remove "$rootfs/etc/options_menu/scripts/om_themeselector" -remove "$rootfs/etc/options_menu/themes" -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/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" -remove "$rootfs/etc/options_menu/images/packed_nes.png" -remove "$rootfs/etc/options_menu/images/pole.png" -remove "$rootfs/etc/options_menu/images/randtheme.png" -remove "$rootfs/etc/options_menu/images/retro_gaming.zip" -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/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/options_menu/images/loading" -remove "$rootfs/etc/usr/bin/pngpack-upx" +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/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" +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_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" +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/options_menu/images/wall_near.png" +removeItem "$rootfs/etc/usr/bin/pngpack-upx" +removeItem "$rootfs/bin/sprite_crop" +removeItem "$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 - umount "${ORIGINAL_THEME}/resources/fonts/${UI1}/${FONT}.fnt" - rm "${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 -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 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 diff --git a/release-notes/v3.0.0.md b/release-notes/v3.0.0.md new file mode 100644 index 0000000..1450675 --- /dev/null +++ b/release-notes/v3.0.0.md @@ -0,0 +1,62 @@ +# Theme Selector 3.0.0 + +Theme Selector 3 is a ground-up rewrite. The menu is now its own app, with a preview grid, in-app theme downloads, a DIY theme builder, and a theme and music for every folder. + +## Requirements + +- [Options Menu v2.1.0](https://github.com/DefKorns/OptionsMenu/releases) or newer. Older Options Menu versions don't work. +- Font Remount mod (`font_remount`), one of hakchi's built-in mods. It applies the theme fonts. +- To download themes from the console: the Hakchi Wi-Fi mod (WPA Supplicant) from KMFDManic's store in hakchi, and a USB Wi-Fi adapter (the TP-Link TL-WN725N is recommended). +- A USB/SD drive is recommended: themes are often 5–30 MB each. + +Installing it uninstalls Theme Selector 2, Theme Randomizer and Advanced Music Hack, and keeps your themes, your music and your last applied theme. + +## What's new + +### Theme Manager + +- Browse your installed themes in a preview grid and apply one with **A**. The theme you're using is outlined. +- Hold **B** on a theme to delete it, after a confirmation. The theme you're using can't be deleted. +- Same look as the Options Menu, with its colors taken from `/etc/options_menu/theme.cfg`; the outline of the active theme has its own key, `ActiveThemeBorder`. +- Only themes and options that fit your console are shown. + +### Download themes + +- Browse the online catalog for your console and install themes straight from it, each one credited to its author. Themes added to the catalog since you last looked come first. +- A progress bar shows each download, with the free space left where the theme is going. A full NAND is refused instead of filled. +- **All Themes** downloads the whole catalog at once and skips the themes you already have. It only shows up with a USB/SD drive connected. +- Downloading a theme you already have asks before overwriting it. +- Theme packages copied by hand into the themes folder are unpacked the next time you open **Themes**. + +### Theme and music per folder + +- **Theme Per Folder:** give any folder, and Home, its own theme from a folder tree. Folders on *Automatic* use their parent folder's theme. Themes named after a folder, and a theme named `default` for Home, still work. +- **Music Per Folder:** the same, with the `.wav` files in your `music_menu` folder. + +### DIY themes + +Build your own theme from pieces of the themes you already have: Base UI, background, walls, characters, pole, or the background color on a NES. Every piece is previewed, identical pieces from different themes are listed once, and you can add your own. + +### Settings + +- **Theme Randomizer:** a different theme each time you change folder. +- **Audio Randomizer:** random Home music, optionally on every folder. +- Switches toggle in place, without reloading the screen. +- **Clean Up** runs on its own after installing, and **Reset Settings** puts the console back to its original theme without uninstalling anything. + +### Every console and region + +- NES, Famicom, Famicom Shonen Jump, SNES (USA/EUR) and Super Famicom. +- A theme made for one region works on the others: its font names and color palette are adjusted to your firmware. +- Custom fonts per theme. +- Nine languages, as in the Options Menu. + +## Updating + +Install it over the previous version. Your themes, music, applied theme and folder assignments are kept, but every option in **Settings** is turned off again: turn back on the ones you use. + +## Thanks + +- **[DanTheMan827](https://github.com/DanTheMan827)** for the packed.png extractor, and with **[KMFDManic](https://github.com/KMFDManic)** for always answering my questions about how things work. +- **Aranthys** and **AxelStone** for testing. +- Every theme author in the catalog. diff --git a/src/command_loader.cpp b/src/command_loader.cpp new file mode 100644 index 0000000..5395a19 --- /dev/null +++ b/src/command_loader.cpp @@ -0,0 +1,310 @@ +/** + * 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 +#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; + +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; bool previewAlignTop = false; bool isNew = false; }; + +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; + while(std::getline(in, line)) + { + if(!line.empty() && line.back() == '\r') + line.pop_back(); + if(line == "NES_ONLY=TRUE") + 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 == "NEW=TRUE") + keys.isNew = 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; +} + +// 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())); + if(!dir) + return false; + while(auto entry = readdir(dir.get())) + { + const std::string name = entry->d_name; + if(name == "." || name == "..") + continue; + const unsigned char type = EntryType(dirPath, entry); + if(type == DT_REG || (type == DT_DIR && HasAnyFile(dirPath + "/" + name))) + return true; + } + return false; +} + +} + +// inherited by child screens, so only the first one forks a shell +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; + 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"); + 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]; + 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; + else if(std::strcmp(argv[i], "--selectedMarker") == 0 && i+1 < argc) + options.selectedMarker = argv[++i]; + else if(std::strcmp(argv[i], "--downloads") == 0) + options.downloadsScreen = true; + else if(std::strcmp(argv[i], "--themePath") == 0 && i+1 < argc) + options.themePath = argv[++i]; + } + 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 : ""; +} + +// right after the "All" entry, otherwise in catalog order +void MoveNewThemesUp(std::vector & commands, std::vector & items, const std::vector & allEntry) +{ + std::vector order(commands.size()); + for(size_t i = 0; i < order.size(); ++i) + order[i] = i; + auto Rank = [&](size_t i) { return !items[i].isThemeItem ? 0 : allEntry[i] ? 1 : items[i].isNew ? 2 : 3; }; + std::stable_sort(order.begin(), order.end(), [&](size_t a, size_t b) { return Rank(a) < Rank(b); }); + + std::vector sortedCommands; + std::vector sortedItems; + for(size_t i : order) + { + sortedCommands.push_back(std::move(commands[i])); + sortedItems.push_back(std::move(items[i])); + } + commands = std::move(sortedCommands); + items = std::move(sortedItems); +} + +bool LoadCommands(const std::string & commandLocation, const std::string & scriptLocation, const std::string & optionsLocation, + const std::string & themePath, std::vector & commands, std::vector & items) +{ + std::vector allEntry; + 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_name[0] == 'c' && EntryType(commandLocation, entry) == DT_REG) + 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; + for(auto & file : fileList) + { + in.open(commandLocation + file); + if(!in.is_open()) + continue; + Command c(in); // closes `in` itself once read + + // 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) + continue; + SelectorKeys keys = ReadSelectorKeys(commandLocation + file); + if(keys.nesOnly || keys.snesOnly) + { + if(!sftypeLoaded) + { + sftype = ReadSftype(); + sftypeLoaded = true; + } + if((keys.nesOnly && sftype != "nes") || (keys.snesOnly && sftype == "nes")) + 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); + const bool needsAttention = !keys.attentionPath.empty() && HasAnyFile(keys.attentionPath); + struct stat st; + const bool installed = !themePath.empty() && file.compare(0, 6, "c0010_") == 0 && + stat((themePath + "/" + file.substr(6)).c_str(), &st) == 0 && S_ISDIR(st.st_mode); + items.push_back({ file.compare(0, 6, "c0000_") != 0, std::move(keys.author), needsAttention, std::move(keys.attentionNotice), keys.previewAlignTop, keys.isNew, installed }); + allEntry.push_back(file.size() > 4 && file.compare(file.size() - 4, 4, "_all") == 0); + } + MoveNewThemesUp(commands, items, allEntry); + return true; +} + +void AppendBackNavigation(const std::string & optionsLocation, const AppOptions & options, const std::string & backStack, + std::vector & commands, std::vector & items) +{ + 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 = "rm -f " + std::string(FocusStatePrefix) + "*; 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); + items.push_back({ false, "", false, "", false }); +} diff --git a/src/command_loader.h b/src/command_loader.h new file mode 100644 index 0000000..463c24d --- /dev/null +++ b/src/command_loader.h @@ -0,0 +1,61 @@ +/** + * 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; + std::string folderThemesDir; + std::string folderThemesMapping; + std::string folderMusicMapping; + bool folderMusicScreen = false; + std::string selectedMarker; + bool downloadsScreen = false; + std::string themePath; +}; + +// 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); + +// OM_BACK_STACK is set by OptionsMenu before launching "Theme Options" +std::string ReadBackStack(bool isRootScreen); + +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 previewAlignTop; + bool isNew; + bool installed; +}; + +bool LoadCommands(const std::string & commandLocation, const std::string & scriptLocation, const std::string & optionsLocation, + const std::string & themePath, std::vector & commands, std::vector & items); + +void AppendBackNavigation(const std::string & optionsLocation, const AppOptions & options, const std::string & backStack, + std::vector & commands, std::vector & items); + +#endif diff --git a/src/folder_theme_model.cpp b/src/folder_theme_model.cpp new file mode 100644 index 0000000..c17f3f9 --- /dev/null +++ b/src/folder_theme_model.cpp @@ -0,0 +1,237 @@ +/** + * 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; + } + + 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 & themeMapping, const std::string & musicMapping) +{ + 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); + } + + 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); + 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(); +} + +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 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(option < 0 || folder < 0) + { + if(!line.empty()) + s.unknownLines.push_back(line); + continue; + } + for(size_t i = 0; i < folders_.size(); ++i) + if(folders_[i].key == folders_[folder].key) + s.assigned[i] = option; + } +} + +bool FolderThemeModel::Save(Channel channel) const +{ + 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 : s.unknownLines) + out << line << '\n'; + std::vector written; + for(size_t i = 0; i < folders_.size(); ++i) + { + if(s.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 << '=' << s.options[s.assigned[i]].id << '\n'; + } + if(!out) + return false; + } + return std::rename(tmpPath.c_str(), s.mappingPath.c_str()) == 0; +} + +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) + s.assigned[i] = option; +} + +ResolvedOption FolderThemeModel::Resolve(Channel channel, int folder) const +{ + int own = slot(channel).assigned[folder]; + if(own >= 0) + return { own, OptionSource::Assigned, folder }; + return ResolveAutomatic(channel, folder); +} + +ResolvedOption FolderThemeModel::ResolveAutomatic(Channel channel, int folder) const +{ + const FolderEntry & entry = folders_[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) + { + 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; + } + } + + 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(Channel channel, int option) const +{ + int count = 0; + for(int assigned : slot(channel).assigned) + count += assigned == option ? 1 : 0; + return count; +} + +int FolderThemeModel::OptionIndex(const Slot & s, const std::string & id) const +{ + for(size_t i = 0; i < s.options.size(); ++i) + if(s.options[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; +} + +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 new file mode 100644 index 0000000..a35749a --- /dev/null +++ b/src/folder_theme_model.h @@ -0,0 +1,87 @@ +/** + * 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; + std::string name; + int depth; + int parent; +}; + +struct FolderOption +{ + std::string id; + std::string title; + std::string preview; + bool hasMusic; + bool needsOverlay; +}; + +enum class Channel { Theme = 0, Music = 1 }; + +enum class OptionSource { Assigned, NameMatch, Inherited, Global, ThemeMusic, RandomMusic, OriginalMusic }; + +struct ResolvedOption +{ + int option; // -1: nothing specific (stock UI, a random theme, the theme's own or random music) + OptionSource source; + int fromFolder; +}; + +// folder -> theme/music, resolved like om_themescript +class FolderThemeModel +{ +public: + 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 & Options(Channel channel) const { return slot(channel).options; } + bool GlobalThemeIsRandom() const { return globalThemeRandom_; } + + 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: + 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_; + Slot slots_[2]; + int globalTheme_ = -1; + 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 new file mode 100644 index 0000000..88abcf1 --- /dev/null +++ b/src/folder_themes_layout.cpp @@ -0,0 +1,435 @@ +/** + * 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/list_screen.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 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, options_.folderMusicMapping)) + return 1; + const std::vector & folders = model.Folders(); + const int folderCount = static_cast(folders.size()); + 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); + const int pickerRows = (PickerRect.h - PickerHeaderH - 8) / PickerRowH; + + ValuePicker valuePicker(optionsLocation_, renderer_); + + 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 badges_->Make(letter, Translate(labelKey), 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); + valuePicker.SetHintLabel(Translate("FOLDER_THEME_CHANGE")); + + auto OptionName = [&](Channel ch, const ResolvedOption & resolved) + { + 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") + " · " + 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 + 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"); + 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) + { + 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 - valuePicker.Width(valueTex[i]) - 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 = [&]() + { + 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 OptionSource::Assigned: + tagKey = "FOLDER_THEME_TAG_ASSIGNED"; + sourceText = Translate("FOLDER_THEME_SOURCE_ASSIGNED"); + detailTagFill = UiTheme::Accent; + tagText = UiTheme::Bg; + break; + case OptionSource::NameMatch: + tagKey = "FOLDER_THEME_TAG_NAME"; + sourceText = Translate("FOLDER_THEME_SOURCE_NAME"); + break; + case OptionSource::Inherited: + tagKey = "FOLDER_THEME_TAG_INHERITED"; + sourceText = Format("FOLDER_THEME_SOURCE_INHERITED", folders[resolved.fromFolder].name); + detailTagFill = UiTheme::BadgeXDark; + break; + 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.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); + }; + + auto Select = [&](int folder) + { + selected = folder; + if(selected < topRow) + topRow = selected; + else if(selected >= topRow + displayRows) + topRow = selected - displayRows + 1; + RebuildDetail(); + }; + auto Assign = [&](int option) + { + if(model.Assigned(channel, selected) == option) + return; + model.Assign(channel, selected, option); + model.Save(channel); + RebuildRows(); + RebuildDetail(); + }; + auto Cycle = [&](int step) + { + int option = model.Assigned(channel, 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(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 - (channel == Channel::Theme ? ThumbW + 12 : 8) - 24; + optionTex.push_back(MakeText(TruncateToWidth(AutomaticLabel(selected), 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)); + 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) + rightEdge = DrawBadge(cancelBadge, rightEdge); + else + { + if(hasBack) + rightEdge = DrawBadge(backBadge, rightEdge); + if(hasY) + rightEdge = DrawBadge(autoBadge, rightEdge); + rightEdge = valuePicker.DrawHint(rightEdge, UiTheme::BadgeBandY); + } + DrawFooterDivider(renderer_, rightEdge); + } + + 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_); + + valuePicker.DrawValue(valueTex[i], ListRight - 16, midY, isSelected && !picking); + } + scrollArrows_->Draw(topRow > 0, topRow + displayRows < folderCount); + + { + 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_); + + 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 }; + if(option == pick) + { + DrawRoundedFillRect(renderer_, rowRect, UiTheme::SelectedRowBg, UiTheme::BoxRadius); + DrawStrokeRect(renderer_, rowRect, UiTheme::Accent, 2, UiTheme::BoxRadius); + } + 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 = labelX; + 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/main.cpp b/src/main.cpp new file mode 100644 index 0000000..0df2a84 --- /dev/null +++ b/src/main.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. + */ + +// Each screen relaunches this binary with its own --commandPath. + +#include "single_instance_lock.h" +#include "command_loader.h" +#include "theme_manager_app.h" +#include "framework/sdl_helper.h" +#include "localization.h" + +#include +#include +#include + +int main(int argc, char * argv[]) +{ + SingleInstanceLock::Acquire(); + + const std::string optionsLocation = "/etc/options_menu/"; + AppOptions options = ParseArgs(argc, argv, optionsLocation); + const std::string backStack = ReadBackStack(options.isRootScreen); + + LoadLanguageFromConfig(optionsLocation); + SetTTFFontPath(optionsLocation); + UiTheme::LoadThemeConfig(optionsLocation); + + std::vector commands; + std::vector items; + if(!LoadCommands(options.commandLocation, options.scriptLocation, optionsLocation, options.themePath, commands, items)) + return 1; + + AppendBackNavigation(optionsLocation, options, backStack, commands, items); + + if(commands.empty()) + { + std::cerr << "No usable commands in " << options.commandLocation << "\n"; + return 1; + } + + ThemeManagerApp app(optionsLocation, std::move(options), std::move(commands), std::move(items)); + return app.Run(); +} diff --git a/src/single_instance_lock.cpp b/src/single_instance_lock.cpp new file mode 100644 index 0000000..687e98c --- /dev/null +++ b/src/single_instance_lock.cpp @@ -0,0 +1,67 @@ +/** + * 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 + +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; + (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; + // 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); + if(kill(oldPid, 0) == 0) + kill(oldPid, SIGKILL); +} + +void SingleInstanceLock::HandleSigTerm(int) +{ + unlink(LockPath); + _exit(0); +} diff --git a/src/single_instance_lock.h b/src/single_instance_lock.h new file mode 100644 index 0000000..6a51138 --- /dev/null +++ b/src/single_instance_lock.h @@ -0,0 +1,32 @@ +/** + * 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_ + +// takes over a stale instance instead of refusing to start +class SingleInstanceLock +{ +public: + SingleInstanceLock(const SingleInstanceLock &) = delete; + SingleInstanceLock & operator=(const SingleInstanceLock &) = delete; + + static void Acquire(); + +private: + static constexpr const char * LockPath = "/tmp/theme_manager.lock"; + + SingleInstanceLock(); + ~SingleInstanceLock(); + + static void TakeOverStaleInstance(); + static void HandleSigTerm(int); +}; + +#endif 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/texture_utils.cpp b/src/texture_utils.cpp new file mode 100644 index 0000000..88613a2 --- /dev/null +++ b/src/texture_utils.cpp @@ -0,0 +1,41 @@ +/** + * 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 + +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); +} diff --git a/src/texture_utils.h b/src/texture_utils.h new file mode 100644 index 0000000..9c1d641 --- /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 "framework/text_layout.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 + +#endif diff --git a/src/theme_manager_app.cpp b/src/theme_manager_app.cpp new file mode 100644 index 0000000..9d7b80d --- /dev/null +++ b/src/theme_manager_app.cpp @@ -0,0 +1,979 @@ +/** + * 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 "download_screen.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 + +namespace +{ + Color ActiveThemeBorder{ 0x4C, 0xAF, 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) + ActiveThemeBorder = { static_cast(r), static_cast(g), static_cast(b) }; + } + } +} + +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)) + , items_(std::move(items)) +{ + LoadActiveThemeBorderColor(optionsLocation_); + + sdlContext_ = std::make_unique(std::chrono::milliseconds(33), false); + renderer_ = sdlContext_->renderer; + controller_ = std::make_unique(1); + + bg_ = UiTheme::Bg; + SetDrawColor(renderer_, bg_); + + switchOn_ = Texture(optionsLocation_ + UiTheme::AssetSwitchOn, renderer_); + switchOff_ = Texture(optionsLocation_ + UiTheme::AssetSwitchOff, renderer_); + + chrome_ = std::make_unique(optionsLocation_, renderer_, "Theme Manager", MOD_VERSION, "Theme Manager - by DefKorns"); + scrollArrows_ = std::make_unique(optionsLocation_, renderer_); + titleText_ = Texture(Translate(options_.titleKey), UiTheme::SectionTitleFontSize, renderer_, UiTheme::SectionTitleX, UiTheme::SectionTitleY, false, ToAbgr(UiTheme::Text), true); + + badges_ = std::make_unique(optionsLocation_, renderer_); + badgeA_ = badges_->Make("A", Translate("HINT_SELECT"), UiTheme::BadgeADark, UiTheme::BadgeA); + badgeRowRightEdge_ = UiTheme::BadgeClusterRightX - UiTheme::BadgeOuterSize - UiTheme::BadgeLabelGap - badgeA_.label.rect.w - UiTheme::BadgeGroupGap; + if(options_.downloadsScreen) + storage_ = std::make_unique(renderer_); + badgeHold_ = badges_->Make("B", Translate("HINT_DELETE"), 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_ && !items_[themeStart_].isThemeItem) + ++themeStart_; +} + +int ThemeManagerApp::DrawBadge(Badge & badge, int rightEdgeX) +{ + return badges_->Draw(badge, rightEdgeX, UiTheme::BadgeBandY); +} + +bool ThemeManagerApp::Confirm(const ScreenSnapshot & background, const std::string & title) +{ + ConfirmDialog dialog(renderer_, *badges_, title, + 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(;;) + { + controller_->Update(); + if(controller_->GetButtonStatus(B)) + break; + if(controller_->GetButtonStatus(A) || controller_->GetButtonStatus(START)) + { + confirmed = true; + break; + } + sdlContext_->StartFrame(); + background.Draw(); + dialog.Draw(); + SetDrawColor(renderer_, bg_); + sdlContext_->EndFrame(); + } + return confirmed; +} + +bool ThemeManagerApp::ConfirmDelete(const ScreenSnapshot & background) +{ + const std::string & blockedKey = commands_[currentCommandId_].deleteBlockedKey; + if(!blockedKey.empty()) + { + MessageDialog dialog(renderer_, *badges_, Translate(blockedKey), + badges_->Make("B", Translate("HINT_BACK"), UiTheme::BadgeBDark, UiTheme::BadgeB)); + controller_->GetButtonStatus(B); + for(;;) + { + controller_->Update(); + if(controller_->GetButtonStatus(B) || controller_->GetButtonStatus(A) || controller_->GetButtonStatus(START)) + break; + sdlContext_->StartFrame(); + background.Draw(); + dialog.Draw(); + SetDrawColor(renderer_, bg_); + sdlContext_->EndFrame(); + } + return false; + } + const std::string & confirmKey = commands_[currentCommandId_].deleteConfirmKey; + if(!Confirm(background, Translate(confirmKey.empty() ? "DELETE_CONFIRM_GENERIC" : confirmKey))) + return false; + system(commands_[currentCommandId_].deleteCommand.c_str()); + return true; +} + +bool ThemeManagerApp::FinishFrame() +{ + SetDrawColor(renderer_, bg_); + if(!deleteRequested_ && !overwriteRequested_) + { + sdlContext_->EndFrame(); + return false; + } + const ScreenSnapshot background(renderer_); + sdlContext_->EndFrame(); + if(overwriteRequested_) + { + overwriteRequested_ = false; + if(Confirm(background, Translate("DOWNLOAD_OVERWRITE_CONFIRM"))) + RunDownload(commands_[currentCommandId_]); + return false; + } + deleteRequested_ = false; + return ConfirmDelete(background); +} + +void ThemeManagerApp::DrawFooterBadges(Badge & badgeB) +{ + int rightEdge = badgeRowRightEdge_; + if(pinnedStartIndex_ < (int)commands_.size()) + rightEdge = DrawBadge(badgeB, rightEdge); + if(!commands_[currentCommandId_].deleteCommand.empty() || !commands_[currentCommandId_].deleteBlockedKey.empty()) + rightEdge = DrawBadge(badgeHold_, rightEdge); + DrawFooterDivider(renderer_, rightEdge); +} + +void ThemeManagerApp::DrawChromeCommon() +{ + chrome_->DrawFrame(); + DrawBadge(badgeA_, UiTheme::BadgeClusterRightX); +} + +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::Accent); + titleText_.Draw(renderer_); +} + +Texture ThemeManagerApp::MakeScrollArrow(int x, int y) const +{ + Texture arrow(optionsLocation_ + UiTheme::AssetChevronUp, renderer_, x, y); + SetColorMod(arrow.texture.get(), UiTheme::ScrollArrow); + arrow.rect.x -= arrow.rect.w / 2; + return arrow; +} + +std::string ThemeManagerApp::FocusStatePath() const +{ + std::string sanitized = options_.commandLocation; + for(char & c : sanitized) + if(c == '/') c = '_'; + return FocusStatePrefix + 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(); + 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; +} + +void ThemeManagerApp::ResumeUnderlyingUi() const +{ + system(("rm -f " + std::string(FocusStatePrefix) + "*; /bin/sh " + optionsLocation_ + "scripts/ResumeUI.sh").c_str()); +} + +bool ThemeManagerApp::ActivateCommand(Command & cmd) +{ + if(!cmd.runInternal) + { + system(cmd.command.c_str()); + return true; + } + if(options_.downloadsScreen && cmd.command.find("/get_all") == std::string::npos) + { + if(items_[static_cast(&cmd - commands_.data())].installed) + overwriteRequested_ = true; + else + RunDownload(cmd); + return false; + } + cmd.RunCommand(*sdlContext_, controller_.get(), *chrome_, bg_); + if(cmd.isToggle) + cmd.UpdateState(); + // the caller's frame was cleared before the command drew over it + SetDrawColor(renderer_, bg_); + SDL_RenderClear(renderer_); + return false; +} + +void ThemeManagerApp::ReloadCommands() +{ + std::vector commands; + std::vector items; + if(!LoadCommands(options_.commandLocation, options_.scriptLocation, optionsLocation_, options_.themePath, commands, items)) + return; + AppendBackNavigation(optionsLocation_, options_, ReadBackStack(options_.isRootScreen), commands, items); + if(commands.empty()) + return; + commands_ = std::move(commands); + items_ = std::move(items); + ComputePinnedAndThemeRanges(); +} + +void ThemeManagerApp::RunDownload(Command & cmd) +{ + const size_t index = static_cast(&cmd - commands_.data()); + const std::string title = cmd.name.substr(0, cmd.name.find(" [")); + if(RunDownloadScreen(cmd.command, Translate("INSTALLING_THEME"), Translate(title), items_[index].author, *sdlContext_, *controller_, *chrome_)) + { + items_[index].isNew = false; + items_[index].installed = true; + } + SetDrawColor(renderer_, bg_); + SDL_RenderClear(renderer_); +} + +void 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() || !commands_[currentCommandId_].deleteBlockedKey.empty()) + deleteRequested_ = true; + } + } + else + { + tapped = bWasHeld_ && !bHoldFired_; + bHoldFired_ = false; + controller_->HeldMillis(B); // releases the hold timer, or the next press starts already past the threshold + } + bWasHeld_ = bHeldNow; +} + +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; + + // empty slots keep their width so the other chips don't shift + int stripEnd = themeStart_; + while(stripEnd > 0 && commands_[stripEnd-1].command.empty()) + --stripEnd; + std::vector stripSlots; + for(int i = 0; i < stripEnd; ++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 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; + 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)); + } + 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; + + 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; + 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 = badges_->Make("B", pinnedStartIndex_ < (int)commands_.size() ? Translate(commands_[pinnedStartIndex_].name) : "", 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 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 + Texture newBadge(Translate("NEW_BADGE"), 13, renderer_, 0, 0, false, ToAbgr(UiTheme::Bg), true); + const int newBadgePad = 7; + 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_) + { + const int labelAvail = chipWidthOf[i] - 2*chipPadX - ChipIconW(i); + if(c.previewImage.size()) + { + 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); + SetColorMod(icon.texture.get(), UiTheme::Accent); + chipIcons[i] = icon; + } + label = TruncateToWidth(label, chipLabelSize, labelAvail); + chipLabels[i] = Texture(label, chipLabelSize, renderer_, 0, 0, false, ToAbgr(UiTheme::Text), true); + } + else + { + 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); + 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; + + 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]) + 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()); + 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); + } + }; + + // 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 = [&]() + { + 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; + + 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, activeName) == 0) + isActiveTheme[i] = true; + } + }; + RefreshActiveTiles(); + + 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; + } + SaveFocusState(); + }; + 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; + const int totalRows = (lastThemeIndex - themeStart_ + GridCols) / GridCols; + + for(;;) + { + FrameEvent frameEvent = PollFrameEvents(); + if(frameEvent == FrameEvent::Quit) + return 0; + if(frameEvent == FrameEvent::PowerButtonPressed) + { + ResumeUnderlyingUi(); + break; + } + + bool inGrid = currentCommandId_ >= themeStart_ && currentCommandId_ < pinnedStartIndex_; + + if(controller_->GetButtonStatus(A) || controller_->GetButtonStatus(START)) + { + if(ActivateCommand(commands_[currentCommandId_])) + break; + RefreshActiveTiles(); + if(storage_) + storage_->Refresh(); + } + 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; + UpdateBButton(tapped); + // quick tap runs Back/Exit + if(tapped && pinnedStartIndex_ < (int)commands_.size() && ActivateCommand(commands_[pinnedStartIndex_])) + break; + } + + DrawChromeCommon(); + DrawSectionTitle(); + DrawFooterBadges(badgeB); + + { + int x = GridLeft; + for(int pos = 0; pos < stripSlotCount; ++pos) + { + const int idx = stripSlots[pos]; + const int chipW = chipWidths[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::SelectedRowBg, UiTheme::BoxRadius); + DrawStrokeRect(renderer_, chipRect, UiTheme::Accent, 2, UiTheme::BoxRadius); + } + else + DrawStrokeRect(renderer_, chipRect, UiTheme::Border, 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 ? 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 + chipIconGap; + } + label.rect.x = gx; + 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_); + if(storage_) + { + storage_->Update(); + storage_->Draw(GridRight, titleText_.rect.y + titleText_.rect.h / 2); + } + + { + 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::SelectedRowBg, UiTheme::BoxRadius); + EnsureTileImage(idx); + std::vector & runFrames = tileRunFrames[idx]; + Texture & tileArt = runFrames.size() > 1 + ? runFrames[(SDL_GetTicks() / 100) % runFrames.size()] + : tileImages[idx]; + 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(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); + if(items_[idx].previewAlignTop) + tileArt.rect.y = y; + } + 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); + } + Color stroke = UiTheme::Border; + int strokeW = UiTheme::BorderWidth; + if(selected) + { + stroke = UiTheme::Accent; + strokeW = 3; + } + else if(isActiveTheme[idx]) + { + stroke = ActiveThemeBorder; + strokeW = 3; + } + DrawStrokeRect(renderer_, tileRect, stroke, strokeW, UiTheme::BoxRadius); + if(items_[idx].isNew) + { + const SDL_Rect pill{ x + 10, y + 10, newBadge.rect.w + 2 * newBadgePad, newBadge.rect.h + 4 }; + DrawRoundedFillRect(renderer_, pill, UiTheme::Accent, pill.h / 2); + newBadge.rect.x = pill.x + newBadgePad; + newBadge.rect.y = pill.y + 2; + newBadge.Draw(renderer_); + } + 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); + } + + if(FinishFrame()) + break; + } + + return 0; +} + +int ThemeManagerApp::RunListLayout() +{ + SDL_Rect selectedRowRect{ UiTheme::ListX, UiTheme::RowFirstY - 2, UiTheme::ListContentRightX - UiTheme::ListX, 0 }; + + // B runs Back/Exit, as in the grid + Badge badgeB = badges_->Make("B", pinnedStartIndex_ < (int)commands_.size() ? Translate(commands_[pinnedStartIndex_].name) : "", UiTheme::BadgeBDark, UiTheme::BadgeB); + + const int RowGlyphSize = 16; + const int RowTextGapPx = 16; + const int ChildIndent = 4*16; + auto BuildRowTextures = [&]() + { + 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, ToAbgr(UiTheme::Text), true); + } + }; + BuildRowTextures(); + + const int modernRowPitch = std::max(UiTheme::RowPitch, GetTTFLineHeight(RowGlyphSize)); + 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 + bool rowsReloaded = false; + 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 = rowsReloaded; + rowsReloaded = false; + if(scrollTarget < topListItemNumber) + { + topListItemNumber = scrollTarget; + updateCommandYPos = true; + } + else if(scrollTarget >= topListItemNumber+DisplayItemCount) + { + topListItemNumber = scrollTarget-DisplayItemCount+1; + updateCommandYPos = true; + } + + // a short list never scrolls, or a restored focus would hide earlier rows + const int maxTopListItemNumber = std::max(0, pinnedStartIndex_-DisplayItemCount); + if(topListItemNumber > maxTopListItemNumber) + { + topListItemNumber = maxTopListItemNumber; + 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(); + SaveFocusState(); + }; + int restoredId = LoadFocusIndex(); + SetCurrentCommand(restoredId >= 0 && restoredId < pinnedStartIndex_ ? restoredId : 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::Border); + }; + + auto DrawChrome = [&]() + { + DrawChromeCommon(); + DrawSectionTitle(); + DrawFooterBadges(badgeB); + + 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::Border, UiTheme::BorderWidth, UiTheme::BoxRadius); + PreviewImage->Draw(renderer_); + } + }; + + for(;;) + { + FrameEvent frameEvent = PollFrameEvents(); + if(frameEvent == FrameEvent::Quit) + return 0; + if(frameEvent == FrameEvent::PowerButtonPressed) + { + ResumeUnderlyingUi(); + break; + } + + if(controller_->GetButtonStatus(A) || controller_->GetButtonStatus(START)) + { + const bool isInternalToggle = commands_[currentCommandId_].runInternal && commands_[currentCommandId_].isToggle; + const std::string focusedName = commands_[currentCommandId_].name; + if(ActivateCommand(commands_[currentCommandId_])) + break; + if(isInternalToggle) + { + ReloadCommands(); + BuildRowTextures(); + int focusId = std::min(currentCommandId_, std::max(0, pinnedStartIndex_-1)); + for(int i = 0; i < pinnedStartIndex_; ++i) + if(commands_[i].name == focusedName) + { + focusId = i; + break; + } + rowsReloaded = true; + SetCurrentCommand(focusId); + } + } + else if(controller_->HeldRepeat(UP) && pinnedStartIndex_ > 0) + { + // bounded for an all-separator list; Back/Exit isn't a row + int newCommandId = currentCommandId_; + for(int tries = 0; tries < pinnedStartIndex_; ++tries) + { + newCommandId = (newCommandId-1+pinnedStartIndex_)%pinnedStartIndex_; + if(commands_[newCommandId].command.size() != 0) + break; + } + SetCurrentCommand(newCommandId); + } + else if(controller_->HeldRepeat(DOWN) && pinnedStartIndex_ > 0) + { + int newCommandId = currentCommandId_; + for(int tries = 0; tries < pinnedStartIndex_; ++tries) + { + newCommandId = (newCommandId+1)%pinnedStartIndex_; + if(commands_[newCommandId].command.size() != 0) + break; + } + SetCurrentCommand(newCommandId); + } + { + bool tapped = false; + UpdateBButton(tapped); + // quick tap runs Back/Exit + 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) == pinnedStartIndex_-1); + + // bounded by pinnedStartIndex_, not commands_.size() - pinned rows need no scroll + scrollArrows_->Draw(topListItemNumber != 0, (topListItemNumber + DisplayItemCount) < pinnedStartIndex_); + + if(FinishFrame()) + break; + } + + return 0; +} diff --git a/src/theme_manager_app.h b/src/theme_manager_app.h new file mode 100644 index 0000000..d2d9c66 --- /dev/null +++ b/src/theme_manager_app.h @@ -0,0 +1,99 @@ +/** + * 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 "storage_indicator.h" +#include "framework/badge.h" +#include "framework/controller.h" +#include "framework/dialog.h" +#include "framework/list_screen.h" +#include "framework/sdl_helper.h" + +#include +#include +#include + +class ThemeManagerApp +{ +public: + ThemeManagerApp(std::string optionsLocation, AppOptions options, std::vector commands, std::vector items); + + int Run() + { + if(!options_.folderThemesDir.empty()) + return RunFolderThemesLayout(); + return options_.gridLayout ? RunGridLayout() : RunListLayout(); + } + +private: + enum class FrameEvent { Continue, Quit, PowerButtonPressed }; + + std::string optionsLocation_; + AppOptions options_; + std::vector commands_; + 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 + + // 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; + Color bg_{}; + + Texture switchOn_, switchOff_; + Texture titleText_; + std::unique_ptr chrome_; + std::unique_ptr scrollArrows_; + std::unique_ptr badges_; + std::unique_ptr storage_; + bool deleteRequested_ = false; + bool overwriteRequested_ = false; + 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); + void DrawFooterBadges(Badge & badgeB); + bool Confirm(const ScreenSnapshot & background, const std::string & title); + 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 + void ReloadCommands(); + void RunDownload(Command & cmd); + void UpdateBButton(bool & tapped); + + void ResumeUnderlyingUi() const; // nothing else resumes it on a power-button exit + + std::string FocusStatePath() const; + void SaveFocusState() const; + int LoadFocusIndex() const; // -1 if none saved + + int RunGridLayout(); + int RunListLayout(); + int RunFolderThemesLayout(); +}; + +#endif diff --git a/theme-selector-workspace.code-workspace b/theme-selector-workspace.code-workspace index 5c989ec..1e517df 100644 --- a/theme-selector-workspace.code-workspace +++ b/theme-selector-workspace.code-workspace @@ -1,20 +1,22 @@ { - "folders": [ + "folders": [ { - "name": "om_theme-selectorv2", + "name": "om_theme-selector", "path": "." }, - { - "name": "out", - "path": "out" - } + // { + // "name": "out", + // "path": "out" + // } ], "settings": { "files.autoSave": "off", - // "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", + "[bash]": { + "editor.formatOnSave": true, + "editor.tabSize": 2, + "editor.insertSpaces": true, + "editor.detectIndentation": false + }, "window.zoomLevel": 0, "files.associations": { "b[0-9]*": "shellscript", @@ -27,18 +29,40 @@ "c[0-9]*": "properties" }, "explorer.confirmDragAndDrop": false, - "prettier.disableLanguages": [ - "html" - ], "git.confirmSync": false, "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, - "workbench.colorTheme": "Flatland Monokai", - + // "beautify.config": "", + // "http.proxyAuthorization": null, + // + "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.enableQuickFix": true, + "shellcheck.customArgs": [ + "-x" + ], + "shellcheck.exclude": [ + "3046", + "3014", + "3046", + "3051" + ], + // "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 +} diff --git a/mod/etc/options_menu/themes/settings/scripts/om_audio b/tools/license-header.txt similarity index 66% rename from mod/etc/options_menu/themes/settings/scripts/om_audio rename to tools/license-header.txt index cce5402..e32bdde 100644 --- a/mod/etc/options_menu/themes/settings/scripts/om_audio +++ b/tools/license-header.txt @@ -1,6 +1,4 @@ -#!/bin/sh -# shellcheck disable=SC1090,SC2039,SC2086,SC2154 -# Copyright (c) 2022 DefKorns (https://defkorns.github.io/LICENSE) +# 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 @@ -15,9 +13,3 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -source $mountpoint/etc/options_menu/themes/scripts/om_vars -script_init - -audioSettings "$1" - -$optionsMenu/options --commandPath $omModSettingsCmds/ --scriptPath $omModSettingsScripts --title "$omTitle" & 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" diff --git a/vendor/OptionsMenu b/vendor/OptionsMenu new file mode 160000 index 0000000..f37f1dc --- /dev/null +++ b/vendor/OptionsMenu @@ -0,0 +1 @@ +Subproject commit f37f1dc0c28f09b581c6466ccf9e41b5d20f845b diff --git a/vendor/hmod-build b/vendor/hmod-build new file mode 160000 index 0000000..692c97a --- /dev/null +++ b/vendor/hmod-build @@ -0,0 +1 @@ +Subproject commit 692c97ac0d314ee9d0d64f3cf492564351d0ff2c diff --git a/vendor/pngpack b/vendor/pngpack new file mode 160000 index 0000000..a5d5bec --- /dev/null +++ b/vendor/pngpack @@ -0,0 +1 @@ +Subproject commit a5d5bec183cd93a9bd29df5c6cd05a8644474bc2