mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-01-18 07:05:39 +00:00
Merge branch 'master' into 1208_new_emulators
This commit is contained in:
commit
db7aed1075
29
CHANGELOG.md
29
CHANGELOG.md
|
@ -13,6 +13,7 @@
|
||||||
* Made fundamental changes to the application logic by removing most view styles and replacing them with a new theme variants concept
|
* Made fundamental changes to the application logic by removing most view styles and replacing them with a new theme variants concept
|
||||||
* Added theme support for defining and applying different layouts for various display aspect ratios such as 16:9 and 4:3
|
* Added theme support for defining and applying different layouts for various display aspect ratios such as 16:9 and 4:3
|
||||||
* Added theme support for defining and applying different color schemes
|
* Added theme support for defining and applying different color schemes
|
||||||
|
* Added a new grid component that is usable in both the system and gamelist views
|
||||||
* Made gamelist theming much more flexible by allowing any number of elements of any types to be defined
|
* Made gamelist theming much more flexible by allowing any number of elements of any types to be defined
|
||||||
* Deprecated multiple older theming concepts like features, extras and hardcoded metadata attributes
|
* Deprecated multiple older theming concepts like features, extras and hardcoded metadata attributes
|
||||||
* Renamed the default theme set from rbsimple-DE to slate-DE
|
* Renamed the default theme set from rbsimple-DE to slate-DE
|
||||||
|
@ -21,10 +22,15 @@
|
||||||
* Added support for caching of SVG images
|
* Added support for caching of SVG images
|
||||||
* Added support for sizing SVG images arbitrarily (overriding the image aspect ratio by stretching and squashing)
|
* Added support for sizing SVG images arbitrarily (overriding the image aspect ratio by stretching and squashing)
|
||||||
* (Windows) Made game launching more seamless by making the application window one pixel wider instead of one pixel less in height
|
* (Windows) Made game launching more seamless by making the application window one pixel wider instead of one pixel less in height
|
||||||
|
* Expanded the quick system select menu option from an on/off entry to a selection of different button combinations
|
||||||
|
* Changed the order of the help system entries Y, X, B and A to instead be listed as A, B, X and Y
|
||||||
|
* Changed the start button for the screensaver from "Back" to "X"
|
||||||
|
* Changed the help system description for the "A" button in the gamelist view from "Launch" to "Select"
|
||||||
|
* Changed the menu header for the gamelist options menu from "Options" to "Gamelist options"
|
||||||
|
* Added support for the Nintendo Wii U (wiiu) game system on Linux and macOS by adding the Cemu standalone emulator
|
||||||
* Added ares standalone as an alternative emulator for many systems
|
* Added ares standalone as an alternative emulator for many systems
|
||||||
* Added MAME standalone as an alternative emulator for the gameandwatch system
|
* Added MAME standalone as an alternative emulator for the gameandwatch system
|
||||||
* Added openMSX standalone as an alternative emulator for the colecovision, msx, msx1, msx2 and msxturbor systems
|
* Added openMSX standalone as an alternative emulator for the colecovision, msx, msx1, msx2 and msxturbor systems
|
||||||
* (Linux) Added support for the Nintendo Wii U (wiiu) game system by adding the Cemu standalone emulator
|
|
||||||
* (Linux) Added support for the Sega Model 3 (model3) game system by adding the Supermodel standalone emulator
|
* (Linux) Added support for the Sega Model 3 (model3) game system by adding the Supermodel standalone emulator
|
||||||
* (Linux) Added Supermodel standalone as an alternative emulator for the arcade and mame systems
|
* (Linux) Added Supermodel standalone as an alternative emulator for the arcade and mame systems
|
||||||
* Added support for the Sega Model 2 (model2) game system on Linux on macOS by adding the MAME - Current RetroArch core
|
* Added support for the Sega Model 2 (model2) game system on Linux on macOS by adding the MAME - Current RetroArch core
|
||||||
|
@ -34,7 +40,7 @@
|
||||||
* Added a %GAMEDIR% variable to the -rompath option for all MAME standalone entries to allow launching games from subdirectories
|
* Added a %GAMEDIR% variable to the -rompath option for all MAME standalone entries to allow launching games from subdirectories
|
||||||
* Added Triforce (Dolphin fork) standalone as an alternative emulator for the gc system on Linux and Windows
|
* Added Triforce (Dolphin fork) standalone as an alternative emulator for the gc system on Linux and Windows
|
||||||
* Added simple64 standalone as an alternative emulator for the n64 system on Linux and Windows
|
* Added simple64 standalone as an alternative emulator for the n64 system on Linux and Windows
|
||||||
* (Linux) Added Rosalie's Mupen GUI standalone as an alternative emulator for the n64 system
|
* Added Rosalie's Mupen GUI standalone as an alternative emulator for the n64 system on Linux and Windows
|
||||||
* Added VICE standalone as an alternative emulator for the c64 (x64sc only) and vic20 systems
|
* Added VICE standalone as an alternative emulator for the c64 (x64sc only) and vic20 systems
|
||||||
* (Windows) Added PrimeHack as an alternative emulator for the gc and wii systems
|
* (Windows) Added PrimeHack as an alternative emulator for the gc and wii systems
|
||||||
* (Windows) Added Project64 as an alternative emulator for the n64 system
|
* (Windows) Added Project64 as an alternative emulator for the n64 system
|
||||||
|
@ -43,6 +49,7 @@
|
||||||
* Added support for the mugen system on Linux and macOS using the Ikemen GO game engine
|
* Added support for the mugen system on Linux and macOS using the Ikemen GO game engine
|
||||||
* Added CPCemu standalone as an alternative emulator for the amstradcpc system
|
* Added CPCemu standalone as an alternative emulator for the amstradcpc system
|
||||||
* Added MAME standalone as an alternative emulator for the gx4000 system
|
* Added MAME standalone as an alternative emulator for the gx4000 system
|
||||||
|
* Added the . (dot) file extension to the xbox360 system on Windows to support extensionless XBLA games
|
||||||
* Added the .car and .rom file extensions to the a5200 system
|
* Added the .car and .rom file extensions to the a5200 system
|
||||||
* Added the .car file extension to the atari800 system
|
* Added the .car file extension to the atari800 system
|
||||||
* Added the .bin file extension to the gx4000 system
|
* Added the .bin file extension to the gx4000 system
|
||||||
|
@ -72,6 +79,7 @@
|
||||||
* Greatly improved application startup speed by avoiding a lot of unnecessary SVG rasterizations
|
* Greatly improved application startup speed by avoiding a lot of unnecessary SVG rasterizations
|
||||||
* Implemented dynamic texture allocation to the font code to reduce memory usage and avoid missing glyphs
|
* Implemented dynamic texture allocation to the font code to reduce memory usage and avoid missing glyphs
|
||||||
* Large optimizations to the text wrapping code (generallly 300-400% faster)
|
* Large optimizations to the text wrapping code (generallly 300-400% faster)
|
||||||
|
* Added support for linear interpolation for font texture magnifications
|
||||||
* Added support for texture mipmapping with trilinear filtering
|
* Added support for texture mipmapping with trilinear filtering
|
||||||
* Added on-demand texture loading to the carousel
|
* Added on-demand texture loading to the carousel
|
||||||
* Improved the renderer scaling accuracy
|
* Improved the renderer scaling accuracy
|
||||||
|
@ -102,11 +110,13 @@
|
||||||
* Added support for defining the scrollable container speed, start delay and reset delay from the theme configuration
|
* Added support for defining the scrollable container speed, start delay and reset delay from the theme configuration
|
||||||
* Added support for arbitrary aspect ratios for RatingComponent images and also added an overlay property
|
* Added support for arbitrary aspect ratios for RatingComponent images and also added an overlay property
|
||||||
* Added theme support for defining the opacity for most element types
|
* Added theme support for defining the opacity for most element types
|
||||||
|
* Added theme support for defining relative brightness for images, videos and animations
|
||||||
* Added theme support for defining color saturation for images, videos and animations
|
* Added theme support for defining color saturation for images, videos and animations
|
||||||
* Added theme support for defining the video fade-in time
|
* Added theme support for defining the video fade-in time
|
||||||
* Added theme support for enabling and disabling video pillarboxes and scanline rendering
|
* Added theme support for enabling and disabling video pillarboxes and scanline rendering
|
||||||
* Added theme support for defining the threshold for when pillarboxes should be applied to a video
|
* Added theme support for defining the threshold for when pillarboxes should be applied to a video
|
||||||
* Added theme support for enabling or disabling audio playback for videos
|
* Added theme support for enabling or disabling audio playback for videos
|
||||||
|
* Added theme support for color shifting videos (and the static image)
|
||||||
* Added theme support for setting separate textColorDimmed and iconColorDimmed properties for the system and gamelist views
|
* Added theme support for setting separate textColorDimmed and iconColorDimmed properties for the system and gamelist views
|
||||||
* Added support for nesting of theme variables
|
* Added support for nesting of theme variables
|
||||||
* Added support for defining multiple theme "variables" tags in the same XML file
|
* Added support for defining multiple theme "variables" tags in the same XML file
|
||||||
|
@ -120,6 +130,7 @@
|
||||||
* Improved theme element placement by replacing the "alignment" and "logoAlignment" properties with specific horizontal and vertical properties
|
* Improved theme element placement by replacing the "alignment" and "logoAlignment" properties with specific horizontal and vertical properties
|
||||||
* Made it possible to use almost all game metadata field when theming text elements
|
* Made it possible to use almost all game metadata field when theming text elements
|
||||||
* Made it possible to set the image interpolation method (nearest neighbor or linear filtering) per image from the theme configuration
|
* Made it possible to set the image interpolation method (nearest neighbor or linear filtering) per image from the theme configuration
|
||||||
|
* Added support for resizing and cropping images to fill the entire defined area (cover fitting)
|
||||||
* Changed the helpsystem properties entrySpacing and iconTextSpacing from fixed pixel values to relative values
|
* Changed the helpsystem properties entrySpacing and iconTextSpacing from fixed pixel values to relative values
|
||||||
* Added support for using unsigned integers for theme properties
|
* Added support for using unsigned integers for theme properties
|
||||||
* Added a metadataElement theme property to the image, video, animation and text element types to control fading and hiding of arbitrary elements
|
* Added a metadataElement theme property to the image, video, animation and text element types to control fading and hiding of arbitrary elements
|
||||||
|
@ -129,6 +140,7 @@
|
||||||
* Added scraping of fan art and updated the media viewer to display these images
|
* Added scraping of fan art and updated the media viewer to display these images
|
||||||
* Added scraping of box back covers when using TheGamesDB
|
* Added scraping of box back covers when using TheGamesDB
|
||||||
* If a wheel (marquee) image on ScreenScraper falls back to another region, then the wheel-hd image is now used instead if it matches the set region
|
* If a wheel (marquee) image on ScreenScraper falls back to another region, then the wheel-hd image is now used instead if it matches the set region
|
||||||
|
* Removed scraping of arcade controller information using ScreenScraper as they have ruined this functionality
|
||||||
* Added a ScreenScraper-specific option to remove dots from game name searches when using the multi-scraper in automatic mode
|
* Added a ScreenScraper-specific option to remove dots from game name searches when using the multi-scraper in automatic mode
|
||||||
* Moved the option "Scrape actual folders" higher up in the scraper options menu
|
* Moved the option "Scrape actual folders" higher up in the scraper options menu
|
||||||
* Added the ability to set a manual sortname specifically for custom collections using the metadata editor
|
* Added the ability to set a manual sortname specifically for custom collections using the metadata editor
|
||||||
|
@ -148,7 +160,7 @@
|
||||||
* Added opacity support to the scanline shader
|
* Added opacity support to the scanline shader
|
||||||
* Added the rlottie library as a Git subtree
|
* Added the rlottie library as a Git subtree
|
||||||
* Updated to build correctly with FFmpeg 5.1
|
* Updated to build correctly with FFmpeg 5.1
|
||||||
* Updated FFmpeg to 5.1.2, SDL to 2.24.1, FreeType to 2.12.1 and pugixml to 1.12.1 on Windows and macOS
|
* Updated SDL to 2.26.1, FFmpeg to 5.1.2, FreeType to 2.12.1 and pugixml to 1.12.1 on Windows and macOS
|
||||||
* Updated curl to 7.86.0 on Windows
|
* Updated curl to 7.86.0 on Windows
|
||||||
* Added a workaround for playing broken video files with invalid PTS values
|
* Added a workaround for playing broken video files with invalid PTS values
|
||||||
* Refactored the rendering code from a shared namespace into proper classes
|
* Refactored the rendering code from a shared namespace into proper classes
|
||||||
|
@ -166,9 +178,12 @@
|
||||||
* Made the logging thread safe
|
* Made the logging thread safe
|
||||||
* (Windows) Changed many logging entries to use backslashes instead of forward slashes as directory separators
|
* (Windows) Changed many logging entries to use backslashes instead of forward slashes as directory separators
|
||||||
* Added the build date to to main menu for alpha and dev builds
|
* Added the build date to to main menu for alpha and dev builds
|
||||||
|
* Added a left trigger + right trigger help system icon and removed the deprecated hotkey icon
|
||||||
|
* Added an arcade twin stick controller badge icon
|
||||||
* Moved all Platform functions to the utility namespace instead of using the global namespace
|
* Moved all Platform functions to the utility namespace instead of using the global namespace
|
||||||
* Implemented proper XML attribute support in ThemeData that eliminates the risk of name collisions
|
* Implemented proper XML attribute support in ThemeData that eliminates the risk of name collisions
|
||||||
* Added size restrictions to images and fonts so incorrect theme configuration would not lead to crashes or excessive memory utilization
|
* Added size restrictions to images and fonts so incorrect theme configuration would not lead to crashes or excessive memory utilization
|
||||||
|
* Made animations on the carousel better looking by using a new non-linear interpolation method
|
||||||
* Migrated the carousel code from SystemView to a separate new CarouselComponent
|
* Migrated the carousel code from SystemView to a separate new CarouselComponent
|
||||||
* Changed the carousel properties to be more generic by renaming "logo" to "item", e.g. itemSize, maxItemCount etc.
|
* Changed the carousel properties to be more generic by renaming "logo" to "item", e.g. itemSize, maxItemCount etc.
|
||||||
* Added the properties "itemsBeforeCenter" and "itemsAfterCenter" to define entries for carousels of the wheel type
|
* Added the properties "itemsBeforeCenter" and "itemsAfterCenter" to define entries for carousels of the wheel type
|
||||||
|
@ -176,7 +191,11 @@
|
||||||
* Added reflections support to the carousel
|
* Added reflections support to the carousel
|
||||||
* Added a new itemAxisHorizontal property to the carousel to keep wheel items horizontal at all times
|
* Added a new itemAxisHorizontal property to the carousel to keep wheel items horizontal at all times
|
||||||
* Added carousel theme support for setting the opacity for unfocused entries
|
* Added carousel theme support for setting the opacity for unfocused entries
|
||||||
|
* Added carousel theme support for applying image color shifts
|
||||||
|
* Added carousel theme support for defining image brightness
|
||||||
|
* Added carousel theme support for defining image saturation
|
||||||
* Added carousel theme support for setting item transitions to "slide" or "instant"
|
* Added carousel theme support for setting item transitions to "slide" or "instant"
|
||||||
|
* Added carousel theme support for controlling item stacking for overlapping items
|
||||||
* Added a fadeAbovePrimary property to control whether elements above the system view carousel and textlist should be rendered during fade transitions
|
* Added a fadeAbovePrimary property to control whether elements above the system view carousel and textlist should be rendered during fade transitions
|
||||||
* Removed support for the thumbnail game media type
|
* Removed support for the thumbnail game media type
|
||||||
* Changed all occurances of "GameList" to "Gamelist" throughout the codebase
|
* Changed all occurances of "GameList" to "Gamelist" throughout the codebase
|
||||||
|
@ -206,11 +225,14 @@
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
|
||||||
* Multiple levels of symlinking in the ROMs directory tree could crash the application on startup
|
* Multiple levels of symlinking in the ROMs directory tree could crash the application on startup
|
||||||
|
* Adding a dot (.) to the es_systems.xml extension tag (to setup extensionless entries) lead to a crash if the system contained folders
|
||||||
* For the cps system, MAME standalone was configured with the wrong system directory for the -rompath option, pointing to "arcade" instead of "cps"
|
* For the cps system, MAME standalone was configured with the wrong system directory for the -rompath option, pointing to "arcade" instead of "cps"
|
||||||
* During some menu operations that reloaded the gamelist view, the cached background could miss some components as they were not rendered in time
|
* During some menu operations that reloaded the gamelist view, the cached background could miss some components as they were not rendered in time
|
||||||
* Text wrapping did not work correctly for text that typically does not contain spaces, like Japanese
|
* Text wrapping did not work correctly for text that typically does not contain spaces, like Japanese
|
||||||
* Changing some values using the metadata editor could lead to an incorrect sort order if the changes were done from within a grouped custom collection
|
* Changing some values using the metadata editor could lead to an incorrect sort order if the changes were done from within a grouped custom collection
|
||||||
* Changing the setting "Group unthemed custom collections" could lead to incorrect custom collections sorting under some circumstances
|
* Changing the setting "Group unthemed custom collections" could lead to incorrect custom collections sorting under some circumstances
|
||||||
|
* For gamelists which mixed files and folders, the folder sorting was sometimes incorrect
|
||||||
|
* Incorrect folder paths were displayed in the metadata editor if the setting "Only show ROMs from gamelist.xml files" was enabled
|
||||||
* Games located in subdirectories were not added back to custom collections when disabling the "Exclude from game counter" metadata option
|
* Games located in subdirectories were not added back to custom collections when disabling the "Exclude from game counter" metadata option
|
||||||
* Enabling and then disabling the "Exclude from game counter" metadata option would remove a game from all currently disabled custom collections
|
* Enabling and then disabling the "Exclude from game counter" metadata option would remove a game from all currently disabled custom collections
|
||||||
* Navigation sounds for the trigger buttons would play when repeatedly pressed at the start or end of text lists
|
* Navigation sounds for the trigger buttons would play when repeatedly pressed at the start or end of text lists
|
||||||
|
@ -226,6 +248,7 @@
|
||||||
* The video player output frame width was not set correctly which made some videos render as garbled when using FFmpeg 5.1 and later
|
* The video player output frame width was not set correctly which made some videos render as garbled when using FFmpeg 5.1 and later
|
||||||
* If a gamelist scroll fade-in animation was playing when opening a menu, it would continue to play after closing the menu
|
* If a gamelist scroll fade-in animation was playing when opening a menu, it would continue to play after closing the menu
|
||||||
* The gamelist quick list scrolling overlay would not disappear as intended under some circumstances
|
* The gamelist quick list scrolling overlay would not disappear as intended under some circumstances
|
||||||
|
* Using the trigger buttons to jump to the start or end of a gamelist did not reset any currently held navigation buttons
|
||||||
* When a legacy theme set had a video view style but did not have a valid md_video entry then the video player would still start (and play the audio)
|
* When a legacy theme set had a video view style but did not have a valid md_video entry then the video player would still start (and play the audio)
|
||||||
* Clearing a game in the metadata editor would sometimes not remove all media files (if there were both a .jpg and a .png for a certain file type)
|
* Clearing a game in the metadata editor would sometimes not remove all media files (if there were both a .jpg and a .png for a certain file type)
|
||||||
* The tile property for the image element did not work correctly with SVG images
|
* The tile property for the image element did not work correctly with SVG images
|
||||||
|
|
|
@ -401,7 +401,7 @@ elseif(WIN32)
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/external/FreeImage/Dist/x64
|
${CMAKE_CURRENT_SOURCE_DIR}/external/FreeImage/Dist/x64
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/external/freetype/include
|
${CMAKE_CURRENT_SOURCE_DIR}/external/freetype/include
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/external/pugixml/src
|
${CMAKE_CURRENT_SOURCE_DIR}/external/pugixml/src
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/external/SDL2-2.24.1)
|
${CMAKE_CURRENT_SOURCE_DIR}/external/SDL2-2.26.1)
|
||||||
elseif(EMSCRIPTEN)
|
elseif(EMSCRIPTEN)
|
||||||
set(COMMON_INCLUDE_DIRS ${COMMON_INCLUDE_DIRS}
|
set(COMMON_INCLUDE_DIRS ${COMMON_INCLUDE_DIRS}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/external/curl/include
|
${CMAKE_CURRENT_SOURCE_DIR}/external/curl/include
|
||||||
|
@ -450,7 +450,7 @@ if(APPLE)
|
||||||
${PROJECT_SOURCE_DIR}/libfreeimage.a
|
${PROJECT_SOURCE_DIR}/libfreeimage.a
|
||||||
${PROJECT_SOURCE_DIR}/libfreetype.6.dylib
|
${PROJECT_SOURCE_DIR}/libfreetype.6.dylib
|
||||||
${PROJECT_SOURCE_DIR}/libpugixml.a
|
${PROJECT_SOURCE_DIR}/libpugixml.a
|
||||||
${PROJECT_SOURCE_DIR}/libSDL2-2.0.dylib)
|
${PROJECT_SOURCE_DIR}/libSDL2-2.0.0.dylib)
|
||||||
elseif(WIN32)
|
elseif(WIN32)
|
||||||
if(CMAKE_CXX_COMPILER_ID MATCHES MSVC)
|
if(CMAKE_CXX_COMPILER_ID MATCHES MSVC)
|
||||||
set(COMMON_LIBRARIES ${PROJECT_SOURCE_DIR}/avcodec.lib
|
set(COMMON_LIBRARIES ${PROJECT_SOURCE_DIR}/avcodec.lib
|
||||||
|
|
|
@ -8,7 +8,7 @@ Table of contents:
|
||||||
|
|
||||||
[[_TOC_]]
|
[[_TOC_]]
|
||||||
|
|
||||||
## Development Environment
|
## Development environment
|
||||||
|
|
||||||
ES-DE is developed and compiled using Clang/LLVM and GCC on Unix, Clang/LLVM on macOS and MSVC and GCC (MinGW) on Windows.
|
ES-DE is developed and compiled using Clang/LLVM and GCC on Unix, Clang/LLVM on macOS and MSVC and GCC (MinGW) on Windows.
|
||||||
|
|
||||||
|
@ -1165,6 +1165,8 @@ Wildcards are supported for emulator binaries, but not for directories:
|
||||||
<command>~/App*/yuzu*.AppImage %ROM%</command>
|
<command>~/App*/yuzu*.AppImage %ROM%</command>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
There is a special case when it comes to file extensions where it's possible to use extensionless files if required. To accomplish this simply add a dot (.) to the list of extensions in the `<extension>` tag. Obviously this makes it impossible to use the _directories interpreted as files_ functionality as there is no file extension, but apart from that everything should work the same as for regular files.
|
||||||
|
|
||||||
Keep in mind that you have to set up your emulators separately from ES-DE as the es_systems.xml file assumes that your emulator environment is properly configured.
|
Keep in mind that you have to set up your emulators separately from ES-DE as the es_systems.xml file assumes that your emulator environment is properly configured.
|
||||||
|
|
||||||
Below is an overview of the file layout with various examples. For the command tag, the newer es_find_rules.xml logic described later in this document removes the need for most of the legacy options, but they are still supported for special configurations and for backward compatibility with old configuration files.
|
Below is an overview of the file layout with various examples. For the command tag, the newer es_find_rules.xml logic described later in this document removes the need for most of the legacy options, but they are still supported for special configurations and for backward compatibility with old configuration files.
|
||||||
|
@ -1195,8 +1197,8 @@ Below is an overview of the file layout with various examples. For the command t
|
||||||
All subdirectories (and non-recursive links) will be included. -->
|
All subdirectories (and non-recursive links) will be included. -->
|
||||||
<path>%ROMPATH%/snes</path>
|
<path>%ROMPATH%/snes</path>
|
||||||
|
|
||||||
<!-- A list of extensions to search for, delimited by any of the whitespace characters (", \r\n\t").
|
<!-- A list of extensions to search for, delimited by any of the whitespace characters (", \r\n\t"). Extensions are
|
||||||
The extensions are case sensitive and they must begin with a dot. -->
|
case sensitive and they must begin with a dot. It's also possible to add just a dot to include extensionless files. -->
|
||||||
<extension>.smc .SMC .sfc .SFC .swc .SWC .fig .FIG .bs .BS .bin .BIN .mgd .MGD .7z .7Z .zip .ZIP</extension>
|
<extension>.smc .SMC .sfc .SFC .swc .SWC .fig .FIG .bs .BS .bin .BIN .mgd .MGD .7z .7Z .zip .ZIP</extension>
|
||||||
|
|
||||||
<!-- The command executed when a game is launched. Various variables are replaced if found for a command tag as explained below.
|
<!-- The command executed when a game is launched. Various variables are replaced if found for a command tag as explained below.
|
||||||
|
@ -1804,7 +1806,7 @@ In addition to this extra logging, a few key combinations are enabled when in de
|
||||||
|
|
||||||
**Ctrl + i**
|
**Ctrl + i**
|
||||||
|
|
||||||
This will draw a semi-transparent red frame behind all image and animation components. It will also draw a green frame around the carousel.
|
This will draw a semi-transparent red frame behind all image and animation components. It will also draw a green frame around the carousel and grid.
|
||||||
|
|
||||||
**Ctrl + t**
|
**Ctrl + t**
|
||||||
|
|
||||||
|
|
1656
THEMES-DEV.md
1656
THEMES-DEV.md
File diff suppressed because it is too large
Load diff
|
@ -481,8 +481,8 @@ ES-DE supports three separate modes, **Full**, **Kiosk** and **Kid**.
|
||||||
These modes mandate the functionalty provided by the application in the following way:
|
These modes mandate the functionalty provided by the application in the following way:
|
||||||
|
|
||||||
* Full - This is the default mode which enables all functionality.
|
* Full - This is the default mode which enables all functionality.
|
||||||
* Kiosk - The main menu will be severely restricted, only displaying the entry to change the audio volume. The game options menu will be restricted as well, removing the metadata editor and the ability to modify custom game collections. And finally the ability to flag or unflag games as favorites will be removed. Apart from this all games will be playable.
|
* Kiosk - The main menu will be severely restricted, only displaying the entry to change the audio volume. The gamelist options menu will be restricted as well, removing the metadata editor and the ability to modify custom game collections. And finally the ability to flag or unflag games as favorites will be removed. Apart from this all games will be playable.
|
||||||
* Kid - Only games marked as being suitable for children will be displayed (this flag is set manually per game using the metadata editor). Additionally, the game options menu is disabled as is the ability to flag and unflag games as favorites. There is also a separate option available to enable or disable the main menu when in Kid mode, see _Enable menu in kid mode_ for additional information.
|
* Kid - Only games marked as being suitable for children will be displayed (this flag is set manually per game using the metadata editor). Additionally, the gamelist options menu is disabled as is the ability to flag and unflag games as favorites. There is also a separate option available to enable or disable the main menu when in Kid mode, see _Enable menu in kid mode_ for additional information.
|
||||||
|
|
||||||
There is an unlock code available to revert to the Full mode from the Kiosk or Kid mode, as is described when changing this setting from the main menu. By default the button sequence is **Up, Up, Down, Down, Left, Right, Left, Right, B, A** (or equivalent buttons if an Xbox controller is not used). Either the keyboard or a controller can be used to input the passkey sequence, but it can't be entered when a menu is open.
|
There is an unlock code available to revert to the Full mode from the Kiosk or Kid mode, as is described when changing this setting from the main menu. By default the button sequence is **Up, Up, Down, Down, Left, Right, Left, Right, B, A** (or equivalent buttons if an Xbox controller is not used). Either the keyboard or a controller can be used to input the passkey sequence, but it can't be entered when a menu is open.
|
||||||
|
|
||||||
|
@ -506,12 +506,12 @@ Default keyboard mappings are shown in brackets.
|
||||||
**Up and down**\
|
**Up and down**\
|
||||||
_(Arrow up / Arrow down)_
|
_(Arrow up / Arrow down)_
|
||||||
|
|
||||||
Navigate up and down in gamelists, between systems in the system view (if the theme has a vertical carousel) and in menus.
|
Navigates between system and game entries where these buttons are applicable, such as for textlists and vertical carousels. Also used in menus for general navigation.
|
||||||
|
|
||||||
**Left and right**\
|
**Left and right**\
|
||||||
_(Arrow left / Arrow right)_
|
_(Arrow left / Arrow right)_
|
||||||
|
|
||||||
Navigate between gamelists (if the _Quick system select_ option has been enabled), between systems in the system view (if the theme has a horizontal carousel) and between media files in the media viewer. If the _Enable screensaver controls_ option has been enabled, either button also randomly selects a new game when using the Video or Slideshow screensavers.
|
Navigates between system and game entries where these buttons are applicable, such as for grids and horizontal carousels. Navigates between gamelists if the _Quick system select_ option has been set to use these buttons and the primary element supports it. Navigates between media files in the media viewer and selects a random game when using the _Video_ or _Slideshow_ screensavers if the _Enable screensaver controls_ option has been enabled. Also used in menus for general navigation.
|
||||||
|
|
||||||
**Start button**\
|
**Start button**\
|
||||||
_(Escape)_
|
_(Escape)_
|
||||||
|
@ -521,17 +521,17 @@ Opens and closes the main menu.
|
||||||
**Back button**\
|
**Back button**\
|
||||||
_(F1)_
|
_(F1)_
|
||||||
|
|
||||||
Opens and closes the game options menu in the gamelist view, or toggles the screensaver in the system view (if the _Enable screensaver controls_ setting is enabled).
|
Opens and closes the gamelist options menu in the gamelist view.
|
||||||
|
|
||||||
**Left and right shoulder buttons**\
|
**Left and right shoulder buttons**\
|
||||||
_(Page up / Page down)_
|
_(Page up / Page down)_
|
||||||
|
|
||||||
Provides quick jumping in gamelists and menus, jumps 10 games in the gamelists and 6 entries in the menus. Also jumps forward in text edit dialogs.
|
Provides quick jumping in textlists and menus, jumps 10 games in the gamelists and 6 entries in the menus. Navigates between gamelists if the _Quick system select_ option has been set to use these buttons and the primary element supports it. Also used as back and blankspace keys in text edit dialogs.
|
||||||
|
|
||||||
**Left and right trigger buttons**\
|
**Left and right trigger buttons**\
|
||||||
_(Home / End)_
|
_(Home / End)_
|
||||||
|
|
||||||
Jumps to the first and last entry of the gamelists, menus and text edit dialogs.
|
Jumps to the first or last entries in carousels, grids and textlists as well as in menus and text edit dialogs. Navigates between gamelists if the _Quick system select_ option has been set to use these buttons and the primary element supports it.
|
||||||
|
|
||||||
**Left and right thumbstick click**\
|
**Left and right thumbstick click**\
|
||||||
_(F2 / F3)_
|
_(F2 / F3)_
|
||||||
|
@ -541,7 +541,7 @@ Jumps to a random game or system depending on whether pressed when in the system
|
||||||
**A button**\
|
**A button**\
|
||||||
_(Enter)_
|
_(Enter)_
|
||||||
|
|
||||||
Opens gamelists from the system view, launches games, enters folders, selects menu entries etc.
|
Select button which opens gamelists from the system view, launches games, enters folders, selects menu entries etc.
|
||||||
|
|
||||||
**B button**\
|
**B button**\
|
||||||
_(Back key)_
|
_(Back key)_
|
||||||
|
@ -551,7 +551,7 @@ Back button, self explanatory.
|
||||||
**X button**\
|
**X button**\
|
||||||
_(Delete)_
|
_(Delete)_
|
||||||
|
|
||||||
Starts the game media viewer (which is accessible from the gamelist views). Used by some other minor functions as explained by the help system and/or this guide.
|
Starts the media viewer in the gamelist view or the screensaver in the system view (if the _Enable screensaver controls_ setting is enabled). Used by some other minor functions as explained by the help system and/or this guide.
|
||||||
|
|
||||||
**Y button**\
|
**Y button**\
|
||||||
_(Insert on Unix and Windows, F13 on macOS)_
|
_(Insert on Unix and Windows, F13 on macOS)_
|
||||||
|
@ -1000,11 +1000,9 @@ Apart from the potential difficulty in locating the emulator binary, there are s
|
||||||
|
|
||||||
#### Nintendo Wii U
|
#### Nintendo Wii U
|
||||||
|
|
||||||
Cemu is available for Windows and Linux.
|
The .wua archive format is the preferred method to use for Wii U games, but the method of using unpacked games is also documented here for reference.
|
||||||
|
|
||||||
Some time ago Cemu added support for the .wua archive format which is much easier to use than the unpacked game format. Therefore this is now the recommended approach, but both this and the traditional method of adding unpacked games are covered here.
|
.wud and .wux files are supported as well, but these two formats are not discussed here as the .wua format is clearly the way to go in the future.
|
||||||
|
|
||||||
.wud and .wux files are also supported, but these two formats are not discussed here as the .wua format is clearly the way to go in the future.
|
|
||||||
|
|
||||||
**Method 1, using .wua files**
|
**Method 1, using .wua files**
|
||||||
|
|
||||||
|
@ -1014,6 +1012,8 @@ Following this just start ES-DE and the game should be shown as a single entry t
|
||||||
|
|
||||||
**Method 2, unpacked games**
|
**Method 2, unpacked games**
|
||||||
|
|
||||||
|
Only the setup on Windows is covered here, but it's the same principle in Linux and macOS.
|
||||||
|
|
||||||
Using this unpacked approach, the content of each game is divided into the three directories _code, content_ and _meta_.
|
Using this unpacked approach, the content of each game is divided into the three directories _code, content_ and _meta_.
|
||||||
|
|
||||||
The first step is to prepare the target directory in the `wiiu` ROMs directory, for this example we'll go for the game _Super Mario 3D World_. So simply create a directory with this name inside the wiiu folder. It should look something like the following:
|
The first step is to prepare the target directory in the `wiiu` ROMs directory, for this example we'll go for the game _Super Mario 3D World_. So simply create a directory with this name inside the wiiu folder. It should look something like the following:
|
||||||
|
@ -1915,7 +1915,6 @@ Here's an overview of what's supported when using these scrapers:
|
||||||
| Multi-language | Yes | No |
|
| Multi-language | Yes | No |
|
||||||
| Game names | Yes | Yes |
|
| Game names | Yes | Yes |
|
||||||
| Ratings | Yes | No |
|
| Ratings | Yes | No |
|
||||||
| Controllers (arcade systems only) | Yes | No |
|
|
||||||
| Other game metadata | Yes | Yes |
|
| Other game metadata | Yes | Yes |
|
||||||
| Videos | Yes | No |
|
| Videos | Yes | No |
|
||||||
| Screenshots | Yes | Yes |
|
| Screenshots | Yes | Yes |
|
||||||
|
@ -1931,8 +1930,6 @@ The category **Other game metadata** includes Description, Release date, Develop
|
||||||
|
|
||||||
The **Multi-language** support includes translated game genres and game descriptions for a number of languages.
|
The **Multi-language** support includes translated game genres and game descriptions for a number of languages.
|
||||||
|
|
||||||
**Controllers** is metadata indicating the controller type (it's not images of controllers).
|
|
||||||
|
|
||||||
**Physical media** means images of cartridges, diskettes, tapes, CD-ROMs etc. that were used to distribute the games.
|
**Physical media** means images of cartridges, diskettes, tapes, CD-ROMs etc. that were used to distribute the games.
|
||||||
|
|
||||||
There are two approaches to scraping, either for a single game from the metadata editor, or for multiple games and systems using the multi-scraper.
|
There are two approaches to scraping, either for a single game from the metadata editor, or for multiple games and systems using the multi-scraper.
|
||||||
|
@ -1942,11 +1939,11 @@ _Here's an example of the multi-scraper running in interactive mode, asking the
|
||||||
|
|
||||||
### Single-game scraper
|
### Single-game scraper
|
||||||
|
|
||||||
The single-game scraper is launched from the metadata editor. You navigate to a game, open the game options menu, choose **Edit this game's metadata** and then select the **Scrape** button (alternatively the "Y" button shortcut can be used to start the scraper).
|
The single-game scraper is launched from the metadata editor. You navigate to a game, open the gamelist options menu, choose **Edit this game's metadata** and then select the **Scrape** button (alternatively the _Y_ button shortcut can be used to start the scraper).
|
||||||
|
|
||||||
### Multi-scraper
|
### Multi-scraper
|
||||||
|
|
||||||
The multi-scraper is accessed from the main menu by entering the **Scraper** menu and then selecting the **Start** button (or the "Y" button shortcut can be used).
|
The multi-scraper is accessed from the main menu by entering the **Scraper** menu and then selecting the **Start** button (or the _Y_ button shortcut can be used).
|
||||||
|
|
||||||
### Scraping process
|
### Scraping process
|
||||||
|
|
||||||
|
@ -2103,10 +2100,6 @@ Whether to scrape the names of the games. This does not affect the actual files
|
||||||
|
|
||||||
Downloads game ratings.
|
Downloads game ratings.
|
||||||
|
|
||||||
**Controllers (arcade systems only)** _(ScreenScraper only)_
|
|
||||||
|
|
||||||
Scrapes controller metadata which is used to set the correct controller badge. This is only available for MAME arcade games, for systems such as _arcade_, _mame_, _neogeo_, _fba_ etc. This is so because ScreenScraper does not seem to provide controller information for other platforms. The type of controllers that are scraped are _joystick_ (separated into entries from no buttons up to 6 buttons), _steering wheel_, _flight stick_, _spinner_, _trackball_ and _lightgun_.
|
|
||||||
|
|
||||||
**Other metadata**
|
**Other metadata**
|
||||||
|
|
||||||
This includes the game description, release date, developer, publisher, genre and the number of players.
|
This includes the game description, release date, developer, publisher, genre and the number of players.
|
||||||
|
@ -2295,17 +2288,21 @@ Sets the view style to _Automatic, Basic, Detailed or Video_ for legacy themes.
|
||||||
|
|
||||||
Transition animation when navigating between gamelists, or between systems on the System view carousel. Can be set to _Slide, Fade_ or _Instant_. Only applicable for legacy themes as the newer type of theme sets lets the theme author define the transition animations in a more fine-grained manner. Therefore this option will be grayed out if a modern theme set has been selected.
|
Transition animation when navigating between gamelists, or between systems on the System view carousel. Can be set to _Slide, Fade_ or _Instant_. Only applicable for legacy themes as the newer type of theme sets lets the theme author define the transition animations in a more fine-grained manner. Therefore this option will be grayed out if a modern theme set has been selected.
|
||||||
|
|
||||||
|
**Quick system select**
|
||||||
|
|
||||||
|
The buttons to use to jump between systems in the gamelist view. The options are _Left/right or shoulders_, _Left/right or triggers_, _Shoulders_, _Triggers_, _Left/right_ or _Disabled_. The first two options will apply either left/right or shoulder/trigger buttons depending on the type of primary element used for the gamelist. For example a textlist or a vertical carousel will allow the use of the left and right buttons, but for horizontal carousels and grids these buttons are reserved for navigating the entries so instead the secondary buttons will be used, i.e. the shoulder or trigger buttons. Using these two options therefore leads to a slight inconsistency as different buttons will be used depending on the theme configuration. If instead using any of the single button pair options, i.e. _Shoulders_, _Triggers_ or _Left/right_, the navigation will be consistent regardless of theme configuration but you'll sacrifice the ability to use the selected buttons if the gamelist supports it, such as the ability to jump rows in a textlist using the shoulder and trigger buttons.
|
||||||
|
|
||||||
**Gamelist on startup**
|
**Gamelist on startup**
|
||||||
|
|
||||||
If set to _None_, the system view will be displayed. Any other value will jump to that game system automatically on startup.
|
If set to _None_, the system view will be displayed. Any other value will jump to that game system automatically on startup.
|
||||||
|
|
||||||
**Default sort order**
|
**Default sort order**
|
||||||
|
|
||||||
The order in which to sort your gamelists. This can be overriden per game system using the game options menu, but that override will only be persistent during the application session. The _System_ sorting can not be selected here as it's only applicable to collection systems.
|
The order in which to sort your gamelists. This can be overriden per game system using the gamelist options menu, but that override will only be persistent during the application session. The _System_ sorting can not be selected here as it's only applicable to collection systems.
|
||||||
|
|
||||||
**Menu opening effect**
|
**Menu opening effect**
|
||||||
|
|
||||||
Animation to play when opening the main menu or the game options menu. Also sets the animation for the game launch screen. Can be set to _Scale-up_ or _None_.
|
Animation to play when opening the main menu or the gamelist options menu. Also sets the animation for the game launch screen. Can be set to _Scale-up_ or _None_.
|
||||||
|
|
||||||
**Launch screen duration**
|
**Launch screen duration**
|
||||||
|
|
||||||
|
@ -2367,10 +2364,6 @@ This enables or disables the ability to jump to a random system or game. It's ma
|
||||||
|
|
||||||
Activating or deactivating the ability to filter your gamelists. This can normally be left enabled.
|
Activating or deactivating the ability to filter your gamelists. This can normally be left enabled.
|
||||||
|
|
||||||
**Enable quick system select**
|
|
||||||
|
|
||||||
If enabled, it's possible to navigate between gamelists using the _Left_ and _Right_ buttons without having to first go back to the System view.
|
|
||||||
|
|
||||||
**Display on-screen help**
|
**Display on-screen help**
|
||||||
|
|
||||||
Activates or deactivates the built-in help system that provides contextual information regarding button usage.
|
Activates or deactivates the built-in help system that provides contextual information regarding button usage.
|
||||||
|
@ -2663,14 +2656,14 @@ Self explanatory.
|
||||||
|
|
||||||
Self explanatory.
|
Self explanatory.
|
||||||
|
|
||||||
## Game options menu
|
## Gamelist options menu
|
||||||
|
|
||||||
This menu is opened from the gamelist view, and can't be accessed from the system view. The menu changes slightly depending on the context, for example if a game file or a folder is selected, or whether the current system is a collection or a normal game system.
|
This menu is opened from the gamelist view, and can't be accessed from the system view. The menu changes slightly depending on the context, for example if a game file or a folder is selected, or whether the current system is a collection or a normal game system.
|
||||||
|
|
||||||
You open the menu using the **Back** button, and by pressing **B** or selecting the **Apply** button any settings such as letter jumping using the quick selector or sorting changes are applied. If you instead press the Back button again or select the **Cancel** button, the menu is closed without applying any changes.
|
You open the menu using the **Back** button, and by pressing **B** or selecting the **Apply** button any settings such as letter jumping using the quick selector or sorting changes are applied. If you instead press the Back button again or select the **Cancel** button, the menu is closed without applying any changes.
|
||||||
|
|
||||||
![alt text](images/es-de_game_options_menu.png "ES-DE Game Options Menu")
|
![alt text](images/es-de_game_options_menu.png "ES-DE Gamelist Options Menu")
|
||||||
_The game options menu as laid out when opening it from within a custom collection, which adds the menu entry to add or remove games from the collection._
|
_The gamelist options menu as laid out when opening it from within a custom collection, which adds the menu entry to add or remove games from the collection._
|
||||||
|
|
||||||
Here's a summary of the menu entries:
|
Here's a summary of the menu entries:
|
||||||
|
|
||||||
|
@ -2680,7 +2673,7 @@ This provides a quick selector for jumping to a certain letter. If the setting t
|
||||||
|
|
||||||
### Sort games by
|
### Sort games by
|
||||||
|
|
||||||
This is the sort order for the gamelist. The default sorting shown here is taken from the setting _Default sort order_ under _UI settings_ in the main menu. Any sorting that is applied via the game options menu will be persistent throughout the program session, and it can be set individually per game system and per collection.
|
This is the sort order for the gamelist. The default sorting shown here is taken from the setting _Default sort order_ under _UI settings_ in the main menu. Any sorting that is applied via the gamelist options menu will be persistent throughout the program session, and it can be set individually per game system and per collection.
|
||||||
|
|
||||||
Sorting can be applied using the following metadata, in either ascending or descending order:
|
Sorting can be applied using the following metadata, in either ascending or descending order:
|
||||||
|
|
||||||
|
@ -2702,7 +2695,7 @@ The secondary sorting is always in ascending filename order.
|
||||||
Choosing this entry opens a separate screen where it's possible to apply a filter to the gamelist. The filter is persistent throughout the program session, or until it's manually reset. The option to reset all filters is shown on the same screen.
|
Choosing this entry opens a separate screen where it's possible to apply a filter to the gamelist. The filter is persistent throughout the program session, or until it's manually reset. The option to reset all filters is shown on the same screen.
|
||||||
|
|
||||||
![alt text](images/es-de_gamelist_filters.png "ES-DE Gamelist Filters")
|
![alt text](images/es-de_gamelist_filters.png "ES-DE Gamelist Filters")
|
||||||
_The gamelist filter screen, accessed from the game options menu._
|
_The gamelist filter screen, accessed from the gamelist options menu._
|
||||||
|
|
||||||
The following filters can be applied:
|
The following filters can be applied:
|
||||||
|
|
||||||
|
@ -2775,7 +2768,7 @@ This is the name that will be shown when browsing the gamelist. If no sortname h
|
||||||
|
|
||||||
**Sortname** _(files only)_
|
**Sortname** _(files only)_
|
||||||
|
|
||||||
This entry makes it possible to change the sorting of a game without having to change its name. For instance it can be used to sort _Mille Miglia_ as _1000 Miglia_ or _The Punisher_ as _Punisher, The_. Note that the _Jump to..._ quick selector on the game options menu will base its index on the first character of the sortname if it exists for a game, which could be slightly confusing in some instances when quick jumping in the gamelist. The _sortname_ entry also affects custom collections, although for these it's possible to override the value as described below. This entry only applies if the sort order has been set to _Filename, Ascending_ or _Filename, Descending_.
|
This entry makes it possible to change the sorting of a game without having to change its name. For instance it can be used to sort _Mille Miglia_ as _1000 Miglia_ or _The Punisher_ as _Punisher, The_. Note that the _Jump to..._ quick selector on the gamelist options menu will base its index on the first character of the sortname if it exists for a game, which could be slightly confusing in some instances when quick jumping in the gamelist. The _sortname_ entry also affects custom collections, although for these it's possible to override the value as described below. This entry only applies if the sort order has been set to _Filename, Ascending_ or _Filename, Descending_.
|
||||||
|
|
||||||
**Custom collections sortname** _(only visible when editing a game from within a custom collection)_
|
**Custom collections sortname** _(only visible when editing a game from within a custom collection)_
|
||||||
|
|
||||||
|
@ -2823,7 +2816,7 @@ A flag to mark whether the game is suitable for children. This will be applied a
|
||||||
|
|
||||||
**Hidden**
|
**Hidden**
|
||||||
|
|
||||||
A flag to indicate that the game is hidden. If the corresponding option has been set in the main menu, the game will not be shown. Useful for example for DOS games to hide batch scripts and unnecessary binaries or to hide the actual game files for multi-disc games. If a file or folder is flagged as hidden but the corresponding option to hide hidden games has not been enabled, then the opacity of the text will be lowered significantly to make it clear that it's a hidden entry.
|
A flag to indicate that the game is hidden. If the corresponding option has been set in the main menu, the game will not be shown. Useful for example for DOS games to hide batch files and unnecessary binaries or to hide the actual game files for multi-disc games. If a file or folder is flagged as hidden but the corresponding option to hide hidden games has not been enabled, then the opacity for the entry will be lowered significantly to make it clear that it's a hidden entry.
|
||||||
|
|
||||||
**Broken/not working**
|
**Broken/not working**
|
||||||
|
|
||||||
|
@ -2855,7 +2848,7 @@ If the option _Enable alternative emulators per game_ has been enabled, there wi
|
||||||
|
|
||||||
**Folder link** _(folders only)_
|
**Folder link** _(folders only)_
|
||||||
|
|
||||||
Using this option it's possible to link a specific file inside the game's folder structure that will be launched directly instead of entering the folder when pressing the _A_ button. This is very useful for systems where there are multiple files per game, such as multi-disc games where an .m3u file is used to launch the game. As the name implies this is only a link, and as folders can't be part of collections (the automatic collections _All games, Favorites_ and _Last played_ as well as any custom collections) it's the linked file inside the folder that is included in such collections. So for instance, launching a game via a linked folder will have the linked file show up in the _Last played_ collection rather than the folder itself. This also means that you should scrape the linked file in addition to the folder to be able to see game media and metadata throughout the application. To override the folder link and enter the directory, there is an entry available in the game options menu.
|
Using this option it's possible to link a specific file inside the game's folder structure that will be launched directly instead of entering the folder when pressing the _A_ button. This is very useful for systems where there are multiple files per game, such as multi-disc games where an .m3u file is used to launch the game. As the name implies this is only a link, and as folders can't be part of collections (the automatic collections _All games, Favorites_ and _Last played_ as well as any custom collections) it's the linked file inside the folder that is included in such collections. So for instance, launching a game via a linked folder will have the linked file show up in the _Last played_ collection rather than the folder itself. This also means that you should scrape the linked file in addition to the folder to be able to see game media and metadata throughout the application. To override the folder link and enter the directory, there is an entry available in the gamelist options menu.
|
||||||
|
|
||||||
### Buttons
|
### Buttons
|
||||||
|
|
||||||
|
@ -2901,7 +2894,7 @@ Numerous options can be set for these screensavers, as detailed [here](USERGUIDE
|
||||||
|
|
||||||
The Dim screensaver simply dims and desaturates the current view and Black will show a black screen. The Slideshow and Video screensavers are more interesting as they can display images and videos from your game collection. In addition to this, the Slideshow screensaver can be configured to only show images from a specified directory.
|
The Dim screensaver simply dims and desaturates the current view and Black will show a black screen. The Slideshow and Video screensavers are more interesting as they can display images and videos from your game collection. In addition to this, the Slideshow screensaver can be configured to only show images from a specified directory.
|
||||||
|
|
||||||
If the option _Enable screensaver controls_ has been activated, you can manually toggle the screensaver from the system view by pressing the _Back_ button. In addition to this, for the Slideshow and Video screensavers, the controls will allow you to jump to a new random image or video by using the _Left_ and _Right_ buttons on your keyboard or controller. It's also possible to launch the game currently displayed using the _A_ button, and the _Y_ button will jump to the game in its gamelist without starting it.
|
If the option _Enable screensaver controls_ has been activated, you can manually toggle the screensaver from the system view by pressing the _X_ button. In addition to this, for the Slideshow and Video screensavers, the controls will allow you to jump to a new random image or video by using the _Left_ and _Right_ buttons on your keyboard or controller. It's also possible to launch the game currently displayed using the _A_ button, and the _Y_ button will jump to the game in its gamelist without starting it.
|
||||||
|
|
||||||
For the video and slideshow screensavers, an overlay can be enabled via the screensaver options that displays the game name and the game system as well as a star symbol if the game is marked as a favorite.
|
For the video and slideshow screensavers, an overlay can be enabled via the screensaver options that displays the game name and the game system as well as a star symbol if the game is marked as a favorite.
|
||||||
|
|
||||||
|
@ -2944,9 +2937,9 @@ During the time that the collection is being edited, any game that is part of th
|
||||||
|
|
||||||
As well, when editing custom collections the _folder link_ configuration is disabled, making it possible to enter folders with such configuration just as if there were no folder links configured.
|
As well, when editing custom collections the _folder link_ configuration is disabled, making it possible to enter folders with such configuration just as if there were no folder links configured.
|
||||||
|
|
||||||
When you are done adding games, you can either open the main menu and go to **Game collection settings** and select the **Finish editing 'Platform' collection** or you can open the game options menu and select the same option there. The latter works from within any gamelist, so you don't need to first navigate back to the collection that you're editing.
|
When you are done adding games, you can either open the main menu and go to **Game collection settings** and select the **Finish editing 'Platform' collection** or you can open the gamelist options menu and select the same option there. The latter works from within any gamelist, so you don't need to first navigate back to the collection that you're editing.
|
||||||
|
|
||||||
You can later add additional games to the collection by navigating to it, bringing up the game options menu and choosing **Add/remove games to this game collection**.
|
You can later add additional games to the collection by navigating to it, bringing up the gamelist options menu and choosing **Add/remove games to this game collection**.
|
||||||
|
|
||||||
![alt text](images/es-de_custom_collections.png "ES-DE Custom Collections")
|
![alt text](images/es-de_custom_collections.png "ES-DE Custom Collections")
|
||||||
_Example of custom collections, here configured as genres._
|
_Example of custom collections, here configured as genres._
|
||||||
|
@ -3071,7 +3064,7 @@ The **@** symbol indicates that the emulator is _deprecated_ and will be removed
|
||||||
| android | Google Android | BlueStacks **(Standalone)** [W] | | No | Shortcut (.lnk) file in root folder |
|
| android | Google Android | BlueStacks **(Standalone)** [W] | | No | Shortcut (.lnk) file in root folder |
|
||||||
| apple2 | Apple II | LinApple **(Standalone)** [U],<br>Mednafen **(Standalone)** [M],<br>AppleWin **(Standalone)** [W*] | Mednafen **(Standalone)** [UW*],<br>MAME **(Standalone)** [UMW*] | Yes for Mednafen and MAME | See the specific _Apple II_ section elsewhere in this guide |
|
| apple2 | Apple II | LinApple **(Standalone)** [U],<br>Mednafen **(Standalone)** [M],<br>AppleWin **(Standalone)** [W*] | Mednafen **(Standalone)** [UW*],<br>MAME **(Standalone)** [UMW*] | Yes for Mednafen and MAME | See the specific _Apple II_ section elsewhere in this guide |
|
||||||
| apple2gs | Apple IIGS | MAME **(Standalone)** [UMW*] | | Yes | See the specific _Apple IIGS_ section elsewhere in this guide |
|
| apple2gs | Apple IIGS | MAME **(Standalone)** [UMW*] | | Yes | See the specific _Apple IIGS_ section elsewhere in this guide |
|
||||||
| arcade | Arcade | MAME - Current | MAME 2010,<br>MAME 2003-Plus,<br>MAME 2000,<br>MAME **(Standalone)** [UMW*],<br>FinalBurn Neo,<br>FB Alpha 2012,<br>Flycast,<br>Flycast **(Standalone)** [UMW*],<br>Kronos [UW],<br>Model 2 Emulator **(Standalone)** [W*],<br>Model 2 Emulator [Suspend ES-DE] **(Standalone)** [W*],<br>Supermodel **(Standalone)** [UW*] | Depends | See the specific _Arcade and Neo Geo_ section elsewhere in this guide |
|
| arcade | Arcade | MAME - Current | MAME 2010,<br>MAME 2003-Plus,<br>MAME 2000,<br>MAME **(Standalone)** [UMW*],<br>FinalBurn Neo,<br>FB Alpha 2012,<br>Flycast,<br>Flycast **(Standalone)** [UMW*],<br>Kronos [UW],<br>Model 2 Emulator **(Standalone)** [W*],<br>Model 2 Emulator [Suspend ES-DE] **(Standalone)** [W*],<br>Supermodel **(Standalone)** [UW*],<br>Supermodel [Fullscreen] **(Standalone)** [UW*] | Depends | See the specific _Arcade and Neo Geo_ section elsewhere in this guide |
|
||||||
| astrocde | Bally Astrocade | MAME - Current | MAME **(Standalone)** [UMW*] | | See the specific _Bally Astrocade_ section elsewhere in this guide |
|
| astrocde | Bally Astrocade | MAME - Current | MAME **(Standalone)** [UMW*] | | See the specific _Bally Astrocade_ section elsewhere in this guide |
|
||||||
| atari2600 | Atari 2600 | Stella | Stella 2014,<br>ares **(Standalone)** [UMW*] | No | Single archive or ROM file in root folder |
|
| atari2600 | Atari 2600 | Stella | Stella 2014,<br>ares **(Standalone)** [UMW*] | No | Single archive or ROM file in root folder |
|
||||||
| atari5200 | Atari 5200 | a5200 | Atari800,<br>Atari800 **(Standalone)** [UMW*] | Yes | |
|
| atari5200 | Atari 5200 | a5200 | Atari800,<br>Atari800 **(Standalone)** [UMW*] | Yes | |
|
||||||
|
@ -3121,7 +3114,7 @@ The **@** symbol indicates that the emulator is _deprecated_ and will be removed
|
||||||
| lutris | Lutris Open Gaming Platform | Lutris application **(Standalone)** [U] | | No | See the specific _Lutris_ section elsewhere in this guide |
|
| lutris | Lutris Open Gaming Platform | Lutris application **(Standalone)** [U] | | No | See the specific _Lutris_ section elsewhere in this guide |
|
||||||
| lutro | Lutro Game Engine | Lutro | | | |
|
| lutro | Lutro Game Engine | Lutro | | | |
|
||||||
| macintosh | Apple Macintosh | Basilisk II **(Standalone)** [UMW*] | SheepShaver **(Standalone)** [UMW*] | Yes | See the specific _Apple Macintosh_ section elsewhere in this guide |
|
| macintosh | Apple Macintosh | Basilisk II **(Standalone)** [UMW*] | SheepShaver **(Standalone)** [UMW*] | Yes | See the specific _Apple Macintosh_ section elsewhere in this guide |
|
||||||
| mame | Multiple Arcade Machine Emulator | MAME - Current | MAME 2010,<br>MAME 2003-Plus,<br>MAME 2000,<br>MAME **(Standalone)** [UMW*],<br>FinalBurn Neo,<br>FB Alpha 2012,<br>Flycast,<br>Flycast **(Standalone)** [UMW*],<br>Kronos [UW],<br>Model 2 Emulator **(Standalone)** [W*],<br>Model 2 Emulator [Suspend ES-DE] **(Standalone)** [W*],<br>Supermodel **(Standalone)** [UW*] | Depends | See the specific _Arcade and Neo Geo_ section elsewhere in this guide |
|
| mame | Multiple Arcade Machine Emulator | MAME - Current | MAME 2010,<br>MAME 2003-Plus,<br>MAME 2000,<br>MAME **(Standalone)** [UMW*],<br>FinalBurn Neo,<br>FB Alpha 2012,<br>Flycast,<br>Flycast **(Standalone)** [UMW*],<br>Kronos [UW],<br>Model 2 Emulator **(Standalone)** [W*],<br>Model 2 Emulator [Suspend ES-DE] **(Standalone)** [W*],<br>Supermodel **(Standalone)** [UW*],<br>Supermodel [Fullscreen] **(Standalone)** [UW*] | Depends | See the specific _Arcade and Neo Geo_ section elsewhere in this guide |
|
||||||
| mame-advmame | AdvanceMAME | _Placeholder_ | | Depends | |
|
| mame-advmame | AdvanceMAME | _Placeholder_ | | Depends | |
|
||||||
| mame-mame4all | MAME4ALL | _Placeholder_ | | Depends | |
|
| mame-mame4all | MAME4ALL | _Placeholder_ | | Depends | |
|
||||||
| mastersystem | Sega Master System | Genesis Plus GX | Genesis Plus GX Wide,<br>SMS Plus GX,<br>Gearsystem,<br>PicoDrive,<br>Mednafen **(Standalone)** [UMW*],<br>ares **(Standalone)** [UMW*] | No | Single archive or ROM file in root folder |
|
| mastersystem | Sega Master System | Genesis Plus GX | Genesis Plus GX Wide,<br>SMS Plus GX,<br>Gearsystem,<br>PicoDrive,<br>Mednafen **(Standalone)** [UMW*],<br>ares **(Standalone)** [UMW*] | No | Single archive or ROM file in root folder |
|
||||||
|
@ -3131,7 +3124,7 @@ The **@** symbol indicates that the emulator is _deprecated_ and will be removed
|
||||||
| megaduck | Creatronic Mega Duck | SameDuck | | No | Single archive or ROM file in root folder |
|
| megaduck | Creatronic Mega Duck | SameDuck | | No | Single archive or ROM file in root folder |
|
||||||
| mess | Multi Emulator Super System | MESS 2015 | | | |
|
| mess | Multi Emulator Super System | MESS 2015 | | | |
|
||||||
| model2 | Sega Model 2 | Model 2 Emulator **(Standalone)** [W*],<br>MAME - Current [UM] | Model 2 Emulator [Suspend ES-DE] **(Standalone)** [W*],<br>MAME - Current [W],<br>MAME **(Standalone)** [UMW*] | Yes for MAME | See the specific _Arcade and Neo Geo_ section elsewhere in this guide |
|
| model2 | Sega Model 2 | Model 2 Emulator **(Standalone)** [W*],<br>MAME - Current [UM] | Model 2 Emulator [Suspend ES-DE] **(Standalone)** [W*],<br>MAME - Current [W],<br>MAME **(Standalone)** [UMW*] | Yes for MAME | See the specific _Arcade and Neo Geo_ section elsewhere in this guide |
|
||||||
| model3 | Sega Model 3 | Supermodel **(Standalone)** [UW*] | | No | See the specific _Arcade and Neo Geo_ section elsewhere in this guide |
|
| model3 | Sega Model 3 | Supermodel **(Standalone)** [UW*] | Supermodel [Fullscreen] **(Standalone)** [UW*] | No | See the specific _Arcade and Neo Geo_ section elsewhere in this guide |
|
||||||
| moonlight | Moonlight Game Streaming | _Placeholder_ | | | |
|
| moonlight | Moonlight Game Streaming | _Placeholder_ | | | |
|
||||||
| moto | Thomson MO/TO Series | Theodore | | | |
|
| moto | Thomson MO/TO Series | Theodore | | | |
|
||||||
| msx | MSX | blueMSX | fMSX,<br>openMSX **(Standalone)** [UMW*],<br>openMSX No Machine **(Standalone)** [UMW*],<br>ares **(Standalone)** [UMW*] | Yes | |
|
| msx | MSX | blueMSX | fMSX,<br>openMSX **(Standalone)** [UMW*],<br>openMSX No Machine **(Standalone)** [UMW*],<br>ares **(Standalone)** [UMW*] | Yes | |
|
||||||
|
@ -3143,7 +3136,7 @@ The **@** symbol indicates that the emulator is _deprecated_ and will be removed
|
||||||
| naomi | Sega NAOMI | Flycast | Flycast **(Standalone)** [UMW*] | | |
|
| naomi | Sega NAOMI | Flycast | Flycast **(Standalone)** [UMW*] | | |
|
||||||
| naomigd | Sega NAOMI GD-ROM | Flycast | Flycast **(Standalone)** [UMW*] | | |
|
| naomigd | Sega NAOMI GD-ROM | Flycast | Flycast **(Standalone)** [UMW*] | | |
|
||||||
| n3ds | Nintendo 3DS | Citra [UW],<br>Citra **(Standalone)** [M] | Citra 2018 [UW],<br>Citra **(Standalone)** [UW*] | No | Single ROM file in root folder |
|
| n3ds | Nintendo 3DS | Citra [UW],<br>Citra **(Standalone)** [M] | Citra 2018 [UW],<br>Citra **(Standalone)** [UW*] | No | Single ROM file in root folder |
|
||||||
| n64 | Nintendo 64 | Mupen64Plus-Next [UW],<br>ParaLLEl N64 [M] | Mupen64Plus **(Standalone)** [UMW*],<br>ParaLLEl N64 [UW],<br>simple64 **(Standalone)** [UW*],<br>Rosalie's Mupen GUI **(Standalone)** [U],<br>Project64 **(Standalone)** [W*],<br>ares **(Standalone)** [UMW*],<br>sixtyforce **(Standalone)** [M] | No | Single archive or ROM file in root folder |
|
| n64 | Nintendo 64 | Mupen64Plus-Next [UW],<br>ParaLLEl N64 [M] | Mupen64Plus **(Standalone)** [UMW*],<br>ParaLLEl N64 [UW],<br>simple64 **(Standalone)** [UW*],<br>Rosalie's Mupen GUI **(Standalone)** [UW],<br>Project64 **(Standalone)** [W*],<br>ares **(Standalone)** [UMW*],<br>sixtyforce **(Standalone)** [M] | No | Single archive or ROM file in root folder |
|
||||||
| n64dd | Nintendo 64DD | ParaLLEl N64 | Mupen64Plus-Next [UW] | Yes | See the specific _Nintendo 64DD_ section elsewhere in this guide |
|
| n64dd | Nintendo 64DD | ParaLLEl N64 | Mupen64Plus-Next [UW] | Yes | See the specific _Nintendo 64DD_ section elsewhere in this guide |
|
||||||
| nds | Nintendo DS | DeSmuME | DeSmuME 2015,<br>DeSmuME **(Standalone)** [U],<br>melonDS,<br>melonDS **(Standalone)** [UMW*] | No | |
|
| nds | Nintendo DS | DeSmuME | DeSmuME 2015,<br>DeSmuME **(Standalone)** [U],<br>melonDS,<br>melonDS **(Standalone)** [UMW*] | No | |
|
||||||
| neogeo | SNK Neo Geo | FinalBurn Neo | FinalBurn Neo **(Standalone)** [UW*],<br>MAME **(Standalone)** [UMW*] | Yes | See the specific _Arcade and Neo Geo_ section elsewhere in this guide |
|
| neogeo | SNK Neo Geo | FinalBurn Neo | FinalBurn Neo **(Standalone)** [UW*],<br>MAME **(Standalone)** [UMW*] | Yes | See the specific _Arcade and Neo Geo_ section elsewhere in this guide |
|
||||||
|
@ -3207,7 +3200,7 @@ The **@** symbol indicates that the emulator is _deprecated_ and will be removed
|
||||||
| videopac | Philips Videopac G7000 | O2EM | | | |
|
| videopac | Philips Videopac G7000 | O2EM | | | |
|
||||||
| virtualboy | Nintendo Virtual Boy | Beetle VB | Mednafen **(Standalone)** [UMW*] | No | |
|
| virtualboy | Nintendo Virtual Boy | Beetle VB | Mednafen **(Standalone)** [UMW*] | No | |
|
||||||
| wii | Nintendo Wii | Dolphin | Dolphin **(Standalone)** [UMW*],<br>PrimeHack **(Standalone)** [UW*] | No | |
|
| wii | Nintendo Wii | Dolphin | Dolphin **(Standalone)** [UMW*],<br>PrimeHack **(Standalone)** [UW*] | No | |
|
||||||
| wiiu | Nintendo Wii U | Cemu **(Standalone)** [UW*] | | No | See the specific _Nintendo Wii U_ section elsewhere in this guide |
|
| wiiu | Nintendo Wii U | Cemu **(Standalone)** [UMW*] | | No | See the specific _Nintendo Wii U_ section elsewhere in this guide |
|
||||||
| wonderswan | Bandai WonderSwan | Beetle Cygne | Mednafen **(Standalone)** [UMW*],<br>ares **(Standalone)** [UMW*] | No | |
|
| wonderswan | Bandai WonderSwan | Beetle Cygne | Mednafen **(Standalone)** [UMW*],<br>ares **(Standalone)** [UMW*] | No | |
|
||||||
| wonderswancolor | Bandai WonderSwan Color | Beetle Cygne | Mednafen **(Standalone)** [UMW*],<br>ares **(Standalone)** [UMW*] | No | |
|
| wonderswancolor | Bandai WonderSwan Color | Beetle Cygne | Mednafen **(Standalone)** [UMW*],<br>ares **(Standalone)** [UMW*] | No | |
|
||||||
| x1 | Sharp X1 | x1 | | | Single archive or ROM file in root folder |
|
| x1 | Sharp X1 | x1 | | | Single archive or ROM file in root folder |
|
||||||
|
|
|
@ -209,7 +209,7 @@ elseif(APPLE)
|
||||||
PERMISSIONS ${APPLE_DYLIB_PERMISSIONS} DESTINATION ../MacOS)
|
PERMISSIONS ${APPLE_DYLIB_PERMISSIONS} DESTINATION ../MacOS)
|
||||||
install(FILES ${CMAKE_SOURCE_DIR}/libfreetype.6.dylib
|
install(FILES ${CMAKE_SOURCE_DIR}/libfreetype.6.dylib
|
||||||
PERMISSIONS ${APPLE_DYLIB_PERMISSIONS} DESTINATION ../MacOS)
|
PERMISSIONS ${APPLE_DYLIB_PERMISSIONS} DESTINATION ../MacOS)
|
||||||
install(FILES ${CMAKE_SOURCE_DIR}/libSDL2-2.0.dylib
|
install(FILES ${CMAKE_SOURCE_DIR}/libSDL2-2.0.0.dylib
|
||||||
PERMISSIONS ${APPLE_DYLIB_PERMISSIONS} DESTINATION ../MacOS)
|
PERMISSIONS ${APPLE_DYLIB_PERMISSIONS} DESTINATION ../MacOS)
|
||||||
|
|
||||||
install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION ../Resources)
|
install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION ../Resources)
|
||||||
|
|
|
@ -64,6 +64,7 @@ Emulators\PPSSPP\PPSSPPWindows64.exe
|
||||||
Emulators\PrimeHack\Dolphin.exe
|
Emulators\PrimeHack\Dolphin.exe
|
||||||
Emulators\Project64\Project64.exe
|
Emulators\Project64\Project64.exe
|
||||||
Emulators\redream\redream.exe
|
Emulators\redream\redream.exe
|
||||||
|
Emulators\RMG\RMG.exe
|
||||||
Emulators\RPCS3\rpcs3.exe
|
Emulators\RPCS3\rpcs3.exe
|
||||||
Emulators\ruffle\ruffle.exe
|
Emulators\ruffle\ruffle.exe
|
||||||
Emulators\ryujinx\Ryujinx.exe
|
Emulators\ryujinx\Ryujinx.exe
|
||||||
|
|
|
@ -40,6 +40,7 @@ FileData::FileData(FileType type,
|
||||||
, mEnvData {envData}
|
, mEnvData {envData}
|
||||||
, mSystem {system}
|
, mSystem {system}
|
||||||
, mOnlyFolders {false}
|
, mOnlyFolders {false}
|
||||||
|
, mHasFolders {false}
|
||||||
, mUpdateChildrenLastPlayed {false}
|
, mUpdateChildrenLastPlayed {false}
|
||||||
, mUpdateChildrenMostPlayed {false}
|
, mUpdateChildrenMostPlayed {false}
|
||||||
, mDeletionFlag {false}
|
, mDeletionFlag {false}
|
||||||
|
@ -76,7 +77,7 @@ FileData::~FileData()
|
||||||
|
|
||||||
std::string FileData::getDisplayName() const
|
std::string FileData::getDisplayName() const
|
||||||
{
|
{
|
||||||
std::string stem {Utils::FileSystem::getStem(mPath)};
|
const std::string& stem {Utils::FileSystem::getStem(mPath)};
|
||||||
return stem;
|
return stem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +168,7 @@ const std::vector<FileData*> FileData::getChildrenRecursive() const
|
||||||
|
|
||||||
const std::string FileData::getROMDirectory()
|
const std::string FileData::getROMDirectory()
|
||||||
{
|
{
|
||||||
std::string romDirSetting {Settings::getInstance()->getString("ROMDirectory")};
|
const std::string& romDirSetting {Settings::getInstance()->getString("ROMDirectory")};
|
||||||
std::string romDirPath;
|
std::string romDirPath;
|
||||||
|
|
||||||
if (romDirSetting == "") {
|
if (romDirSetting == "") {
|
||||||
|
@ -196,7 +197,7 @@ const std::string FileData::getROMDirectory()
|
||||||
|
|
||||||
const std::string FileData::getMediaDirectory()
|
const std::string FileData::getMediaDirectory()
|
||||||
{
|
{
|
||||||
std::string mediaDirSetting {Settings::getInstance()->getString("MediaDirectory")};
|
const std::string& mediaDirSetting {Settings::getInstance()->getString("MediaDirectory")};
|
||||||
std::string mediaDirPath;
|
std::string mediaDirPath;
|
||||||
|
|
||||||
if (mediaDirSetting == "") {
|
if (mediaDirSetting == "") {
|
||||||
|
@ -233,7 +234,7 @@ const std::string FileData::getMediafilePath(const std::string& subdirectory) co
|
||||||
subFolders + "/" + getDisplayName()};
|
subFolders + "/" + getDisplayName()};
|
||||||
|
|
||||||
// Look for an image file in the media directory.
|
// Look for an image file in the media directory.
|
||||||
for (size_t i = 0; i < extList.size(); ++i) {
|
for (size_t i {0}; i < extList.size(); ++i) {
|
||||||
std::string mediaPath {tempPath + extList[i]};
|
std::string mediaPath {tempPath + extList[i]};
|
||||||
if (Utils::FileSystem::exists(mediaPath))
|
if (Utils::FileSystem::exists(mediaPath))
|
||||||
return mediaPath;
|
return mediaPath;
|
||||||
|
@ -332,7 +333,7 @@ const std::string FileData::getVideoPath() const
|
||||||
getDisplayName()};
|
getDisplayName()};
|
||||||
|
|
||||||
// Look for media in the media directory.
|
// Look for media in the media directory.
|
||||||
for (size_t i = 0; i < extList.size(); ++i) {
|
for (size_t i {0}; i < extList.size(); ++i) {
|
||||||
std::string mediaPath {tempPath + extList[i]};
|
std::string mediaPath {tempPath + extList[i]};
|
||||||
if (Utils::FileSystem::exists(mediaPath))
|
if (Utils::FileSystem::exists(mediaPath))
|
||||||
return mediaPath;
|
return mediaPath;
|
||||||
|
@ -424,7 +425,7 @@ std::vector<FileData*> FileData::getScrapeFilesRecursive(bool includeFolders,
|
||||||
|
|
||||||
const bool FileData::isArcadeAsset() const
|
const bool FileData::isArcadeAsset() const
|
||||||
{
|
{
|
||||||
const std::string stem {Utils::FileSystem::getStem(mPath)};
|
const std::string& stem {Utils::FileSystem::getStem(mPath)};
|
||||||
return ((mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) ||
|
return ((mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) ||
|
||||||
mSystem->hasPlatformId(PlatformIds::SNK_NEO_GEO))) &&
|
mSystem->hasPlatformId(PlatformIds::SNK_NEO_GEO))) &&
|
||||||
(MameNames::getInstance().isBios(stem) || MameNames::getInstance().isDevice(stem)));
|
(MameNames::getInstance().isBios(stem) || MameNames::getInstance().isDevice(stem)));
|
||||||
|
@ -432,7 +433,7 @@ const bool FileData::isArcadeAsset() const
|
||||||
|
|
||||||
const bool FileData::isArcadeGame() const
|
const bool FileData::isArcadeGame() const
|
||||||
{
|
{
|
||||||
const std::string stem {Utils::FileSystem::getStem(mPath)};
|
const std::string& stem {Utils::FileSystem::getStem(mPath)};
|
||||||
return ((mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) ||
|
return ((mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) ||
|
||||||
mSystem->hasPlatformId(PlatformIds::SNK_NEO_GEO))) &&
|
mSystem->hasPlatformId(PlatformIds::SNK_NEO_GEO))) &&
|
||||||
(!MameNames::getInstance().isBios(stem) && !MameNames::getInstance().isDevice(stem)));
|
(!MameNames::getInstance().isBios(stem) && !MameNames::getInstance().isDevice(stem)));
|
||||||
|
@ -444,7 +445,7 @@ void FileData::addChild(FileData* file)
|
||||||
if (!mSystem->getFlattenFolders())
|
if (!mSystem->getFlattenFolders())
|
||||||
assert(file->getParent() == nullptr);
|
assert(file->getParent() == nullptr);
|
||||||
|
|
||||||
const std::string key = file->getKey();
|
const std::string& key {file->getKey()};
|
||||||
if (mChildrenByFilename.find(key) == mChildrenByFilename.cend()) {
|
if (mChildrenByFilename.find(key) == mChildrenByFilename.cend()) {
|
||||||
mChildrenByFilename[key] = file;
|
mChildrenByFilename[key] = file;
|
||||||
mChildren.emplace_back(file);
|
mChildren.emplace_back(file);
|
||||||
|
@ -481,7 +482,7 @@ void FileData::sort(ComparisonFunction& comparator,
|
||||||
std::vector<FileData*> mChildrenOthers;
|
std::vector<FileData*> mChildrenOthers;
|
||||||
|
|
||||||
if (mSystem->isGroupedCustomCollection())
|
if (mSystem->isGroupedCustomCollection())
|
||||||
gameCount = {};
|
gameCount = {0, 0};
|
||||||
|
|
||||||
if (!showHiddenGames) {
|
if (!showHiddenGames) {
|
||||||
for (auto it = mChildren.begin(); it != mChildren.end();) {
|
for (auto it = mChildren.begin(); it != mChildren.end();) {
|
||||||
|
@ -505,20 +506,20 @@ void FileData::sort(ComparisonFunction& comparator,
|
||||||
// The main custom collections view is sorted during startup in CollectionSystemsManager.
|
// The main custom collections view is sorted during startup in CollectionSystemsManager.
|
||||||
// The individual collections are however sorted as any normal systems/folders.
|
// The individual collections are however sorted as any normal systems/folders.
|
||||||
if (mSystem->isCollection() && mSystem->getFullName() == "collections") {
|
if (mSystem->isCollection() && mSystem->getFullName() == "collections") {
|
||||||
std::pair<unsigned int, unsigned int> tempGameCount {};
|
std::pair<unsigned int, unsigned int> tempGameCount {0, 0};
|
||||||
for (auto it = mChildren.cbegin(); it != mChildren.cend(); ++it) {
|
for (auto it = mChildren.cbegin(); it != mChildren.cend(); ++it) {
|
||||||
if ((*it)->getChildren().size() > 0)
|
if ((*it)->getChildren().size() > 0)
|
||||||
(*it)->sort(comparator, gameCount);
|
(*it)->sort(comparator, gameCount);
|
||||||
tempGameCount.first += gameCount.first;
|
tempGameCount.first += gameCount.first;
|
||||||
tempGameCount.second += gameCount.second;
|
tempGameCount.second += gameCount.second;
|
||||||
gameCount = {};
|
gameCount = {0, 0};
|
||||||
}
|
}
|
||||||
gameCount = tempGameCount;
|
gameCount = tempGameCount;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foldersOnTop) {
|
if (foldersOnTop) {
|
||||||
for (unsigned int i = 0; i < mChildren.size(); ++i) {
|
for (unsigned int i {0}; i < mChildren.size(); ++i) {
|
||||||
if (mChildren[i]->getType() == FOLDER) {
|
if (mChildren[i]->getType() == FOLDER) {
|
||||||
mChildrenFolders.emplace_back(mChildren[i]);
|
mChildrenFolders.emplace_back(mChildren[i]);
|
||||||
}
|
}
|
||||||
|
@ -538,7 +539,7 @@ void FileData::sort(ComparisonFunction& comparator,
|
||||||
getSortTypeFromString("filename, ascending").comparisonFunction);
|
getSortTypeFromString("filename, ascending").comparisonFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foldersOnTop && mOnlyFolders)
|
if (foldersOnTop)
|
||||||
std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), comparator);
|
std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), comparator);
|
||||||
|
|
||||||
std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), comparator);
|
std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), comparator);
|
||||||
|
@ -597,24 +598,24 @@ void FileData::sortFavoritesOnTop(ComparisonFunction& comparator,
|
||||||
std::vector<FileData*> mChildrenOthers;
|
std::vector<FileData*> mChildrenOthers;
|
||||||
|
|
||||||
if (mSystem->isGroupedCustomCollection())
|
if (mSystem->isGroupedCustomCollection())
|
||||||
gameCount = {};
|
gameCount = {0, 0};
|
||||||
|
|
||||||
// The main custom collections view is sorted during startup in CollectionSystemsManager.
|
// The main custom collections view is sorted during startup in CollectionSystemsManager.
|
||||||
// The individual collections are however sorted as any normal systems/folders.
|
// The individual collections are however sorted as any normal systems/folders.
|
||||||
if (mSystem->isCollection() && mSystem->getFullName() == "collections") {
|
if (mSystem->isCollection() && mSystem->getFullName() == "collections") {
|
||||||
std::pair<unsigned int, unsigned int> tempGameCount = {};
|
std::pair<unsigned int, unsigned int> tempGameCount = {0, 0};
|
||||||
for (auto it = mChildren.cbegin(); it != mChildren.cend(); ++it) {
|
for (auto it = mChildren.cbegin(); it != mChildren.cend(); ++it) {
|
||||||
if ((*it)->getChildren().size() > 0)
|
if ((*it)->getChildren().size() > 0)
|
||||||
(*it)->sortFavoritesOnTop(comparator, gameCount);
|
(*it)->sortFavoritesOnTop(comparator, gameCount);
|
||||||
tempGameCount.first += gameCount.first;
|
tempGameCount.first += gameCount.first;
|
||||||
tempGameCount.second += gameCount.second;
|
tempGameCount.second += gameCount.second;
|
||||||
gameCount = {};
|
gameCount = {0, 0};
|
||||||
}
|
}
|
||||||
gameCount = tempGameCount;
|
gameCount = tempGameCount;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i < mChildren.size(); ++i) {
|
for (unsigned int i {0}; i < mChildren.size(); ++i) {
|
||||||
// If the option to hide hidden games has been set and the game is hidden,
|
// If the option to hide hidden games has been set and the game is hidden,
|
||||||
// then skip it. Normally games are hidden during loading of the gamelists in
|
// then skip it. Normally games are hidden during loading of the gamelists in
|
||||||
// Gamelist::parseGamelist() and this code should only run when a user has marked
|
// Gamelist::parseGamelist() and this code should only run when a user has marked
|
||||||
|
@ -685,7 +686,7 @@ void FileData::sortFavoritesOnTop(ComparisonFunction& comparator,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort favorite games and the other games separately.
|
// Sort favorite games and the other games separately.
|
||||||
if (foldersOnTop && mOnlyFolders) {
|
if (foldersOnTop) {
|
||||||
std::stable_sort(mChildrenFavoritesFolders.begin(), mChildrenFavoritesFolders.end(),
|
std::stable_sort(mChildrenFavoritesFolders.begin(), mChildrenFavoritesFolders.end(),
|
||||||
comparator);
|
comparator);
|
||||||
std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), comparator);
|
std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), comparator);
|
||||||
|
@ -745,7 +746,7 @@ void FileData::countGames(std::pair<unsigned int, unsigned int>& gameCount)
|
||||||
bool isKidMode {(Settings::getInstance()->getString("UIMode") == "kid" ||
|
bool isKidMode {(Settings::getInstance()->getString("UIMode") == "kid" ||
|
||||||
Settings::getInstance()->getBool("ForceKid"))};
|
Settings::getInstance()->getBool("ForceKid"))};
|
||||||
|
|
||||||
for (unsigned int i = 0; i < mChildren.size(); ++i) {
|
for (unsigned int i {0}; i < mChildren.size(); ++i) {
|
||||||
if (mChildren[i]->getType() == GAME && mChildren[i]->getCountAsGame()) {
|
if (mChildren[i]->getType() == GAME && mChildren[i]->getCountAsGame()) {
|
||||||
if (!isKidMode || (isKidMode && mChildren[i]->getKidgame())) {
|
if (!isKidMode || (isKidMode && mChildren[i]->getKidgame())) {
|
||||||
++gameCount.first;
|
++gameCount.first;
|
||||||
|
@ -798,9 +799,9 @@ void FileData::updateMostPlayedList()
|
||||||
|
|
||||||
const FileData::SortType& FileData::getSortTypeFromString(const std::string& desc) const
|
const FileData::SortType& FileData::getSortTypeFromString(const std::string& desc) const
|
||||||
{
|
{
|
||||||
std::vector<FileData::SortType> SortTypes = FileSorts::SortTypes;
|
std::vector<FileData::SortType> SortTypes {FileSorts::SortTypes};
|
||||||
|
|
||||||
for (unsigned int i = 0; i < FileSorts::SortTypes.size(); ++i) {
|
for (unsigned int i {0}; i < FileSorts::SortTypes.size(); ++i) {
|
||||||
const FileData::SortType& sort {FileSorts::SortTypes.at(i)};
|
const FileData::SortType& sort {FileSorts::SortTypes.at(i)};
|
||||||
if (sort.description == desc)
|
if (sort.description == desc)
|
||||||
return sort;
|
return sort;
|
||||||
|
@ -983,11 +984,11 @@ void FileData::launchGame()
|
||||||
command = Utils::FileSystem::expandHomePath(command);
|
command = Utils::FileSystem::expandHomePath(command);
|
||||||
|
|
||||||
// Check that the emulator binary actually exists, and if so, get its path.
|
// Check that the emulator binary actually exists, and if so, get its path.
|
||||||
std::string binaryPath {findEmulatorPath(command)};
|
const std::string& binaryPath {findEmulatorPath(command)};
|
||||||
|
|
||||||
// Hack to show an error message if there was no emulator entry in es_find_rules.xml.
|
// Hack to show an error message if there was no emulator entry in es_find_rules.xml.
|
||||||
if (binaryPath.substr(0, 18) == "NO EMULATOR RULE: ") {
|
if (binaryPath.substr(0, 18) == "NO EMULATOR RULE: ") {
|
||||||
std::string emulatorEntry {binaryPath.substr(18, binaryPath.size() - 18)};
|
const std::string& emulatorEntry {binaryPath.substr(18, binaryPath.size() - 18)};
|
||||||
LOG(LogError) << "Couldn't launch game, either there is no emulator entry for \""
|
LOG(LogError) << "Couldn't launch game, either there is no emulator entry for \""
|
||||||
<< emulatorEntry << "\" in es_find_rules.xml or there are no rules defined";
|
<< emulatorEntry << "\" in es_find_rules.xml or there are no rules defined";
|
||||||
LOG(LogError) << "Raw emulator launch command:";
|
LOG(LogError) << "Raw emulator launch command:";
|
||||||
|
@ -1051,7 +1052,7 @@ void FileData::launchGame()
|
||||||
quotationMarkPos =
|
quotationMarkPos =
|
||||||
static_cast<unsigned int>(command.find("\"", emuPathPos + 9) - emuPathPos);
|
static_cast<unsigned int>(command.find("\"", emuPathPos + 9) - emuPathPos);
|
||||||
}
|
}
|
||||||
size_t spacePos {command.find(" ", emuPathPos + quotationMarkPos)};
|
const size_t spacePos {command.find(" ", emuPathPos + quotationMarkPos)};
|
||||||
std::string coreRaw;
|
std::string coreRaw;
|
||||||
std::string coreFile;
|
std::string coreFile;
|
||||||
if (spacePos != std::string::npos) {
|
if (spacePos != std::string::npos) {
|
||||||
|
@ -1120,7 +1121,7 @@ void FileData::launchGame()
|
||||||
|
|
||||||
// If a %CORE_ find rule entry is used in es_systems.xml for this system, then try to find
|
// If a %CORE_ find rule entry is used in es_systems.xml for this system, then try to find
|
||||||
// the emulator core using the rules defined in es_find_rules.xml.
|
// the emulator core using the rules defined in es_find_rules.xml.
|
||||||
for (std::string path : emulatorCorePaths) {
|
for (std::string& path : emulatorCorePaths) {
|
||||||
// The position of the %CORE_ variable could have changed as there may have been an
|
// The position of the %CORE_ variable could have changed as there may have been an
|
||||||
// %EMULATOR_ variable that was substituted for the actual emulator binary.
|
// %EMULATOR_ variable that was substituted for the actual emulator binary.
|
||||||
coreEntryPos = command.find("%CORE_");
|
coreEntryPos = command.find("%CORE_");
|
||||||
|
@ -1210,7 +1211,7 @@ void FileData::launchGame()
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string startDirectory;
|
std::string startDirectory;
|
||||||
size_t startDirPos {command.find("%STARTDIR%")};
|
const size_t startDirPos {command.find("%STARTDIR%")};
|
||||||
|
|
||||||
if (startDirPos != std::string::npos) {
|
if (startDirPos != std::string::npos) {
|
||||||
bool invalidEntry {false};
|
bool invalidEntry {false};
|
||||||
|
@ -1233,7 +1234,7 @@ void FileData::launchGame()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!invalidEntry) {
|
else if (!invalidEntry) {
|
||||||
size_t spacePos {command.find(" ", startDirPos)};
|
const size_t spacePos {command.find(" ", startDirPos)};
|
||||||
if (spacePos != std::string::npos) {
|
if (spacePos != std::string::npos) {
|
||||||
startDirectory = command.substr(startDirPos + 11, spacePos - startDirPos - 11);
|
startDirectory = command.substr(startDirPos + 11, spacePos - startDirPos - 11);
|
||||||
command = command.replace(startDirPos, spacePos - startDirPos + 1, "");
|
command = command.replace(startDirPos, spacePos - startDirPos + 1, "");
|
||||||
|
@ -1331,7 +1332,7 @@ void FileData::launchGame()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!invalidEntry) {
|
else if (!invalidEntry) {
|
||||||
size_t spacePos {command.find(" ", injectPos)};
|
const size_t spacePos {command.find(" ", injectPos)};
|
||||||
if (spacePos != std::string::npos) {
|
if (spacePos != std::string::npos) {
|
||||||
injectFile = command.substr(injectPos + 9, spacePos - injectPos - 9);
|
injectFile = command.substr(injectPos + 9, spacePos - injectPos - 9);
|
||||||
command = command.replace(injectPos, spacePos - injectPos + 1, "");
|
command = command.replace(injectPos, spacePos - injectPos + 1, "");
|
||||||
|
@ -1407,8 +1408,8 @@ void FileData::launchGame()
|
||||||
// The special characters need to be procesed in this order.
|
// The special characters need to be procesed in this order.
|
||||||
std::string specialCharacters {"^&()=;,"};
|
std::string specialCharacters {"^&()=;,"};
|
||||||
|
|
||||||
for (size_t i = 0; i < specialCharacters.size(); ++i) {
|
for (size_t i {0}; i < specialCharacters.size(); ++i) {
|
||||||
std::string special(1, specialCharacters[i]);
|
const std::string& special {1, specialCharacters[i]};
|
||||||
if (romPath.find(special) != std::string::npos) {
|
if (romPath.find(special) != std::string::npos) {
|
||||||
romPath = Utils::String::replace(romPath, special, "^" + special);
|
romPath = Utils::String::replace(romPath, special, "^" + special);
|
||||||
foundSpecial = true;
|
foundSpecial = true;
|
||||||
|
@ -1701,15 +1702,15 @@ const std::string FileData::findEmulatorPath(std::string& command)
|
||||||
return "NO EMULATOR RULE: " + emulatorEntry;
|
return "NO EMULATOR RULE: " + emulatorEntry;
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
for (std::string path : emulatorWinRegistryPaths) {
|
for (std::string& path : emulatorWinRegistryPaths) {
|
||||||
// Search for the emulator using the App Paths keys in the Windows Registry.
|
// Search for the emulator using the App Paths keys in the Windows Registry.
|
||||||
std::string registryKeyPath {"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" +
|
const std::string& registryKeyPath {
|
||||||
path};
|
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" + path};
|
||||||
|
|
||||||
HKEY registryKey;
|
HKEY registryKey;
|
||||||
LSTATUS keyStatus {-1};
|
LSTATUS keyStatus {-1};
|
||||||
LSTATUS pathStatus {-1};
|
LSTATUS pathStatus {-1};
|
||||||
char registryPath[1024] {};
|
char registryPath[1024] {0};
|
||||||
DWORD pathSize {1024};
|
DWORD pathSize {1024};
|
||||||
|
|
||||||
// First look in HKEY_CURRENT_USER.
|
// First look in HKEY_CURRENT_USER.
|
||||||
|
@ -1746,11 +1747,11 @@ const std::string FileData::findEmulatorPath(std::string& command)
|
||||||
RegCloseKey(registryKey);
|
RegCloseKey(registryKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::string value : emulatorWinRegistryValues) {
|
for (std::string& value : emulatorWinRegistryValues) {
|
||||||
// If the pipe character is found, then the string following this should be appended
|
// If the pipe character is found, then the string following this should be appended
|
||||||
// to the key value, assuming the key is found.
|
// to the key value, assuming the key is found.
|
||||||
std::string appendString;
|
std::string appendString;
|
||||||
size_t pipePos {value.find('|')};
|
const size_t pipePos {value.find('|')};
|
||||||
|
|
||||||
if (pipePos != std::string::npos) {
|
if (pipePos != std::string::npos) {
|
||||||
appendString = value.substr(pipePos + 1, std::string::npos);
|
appendString = value.substr(pipePos + 1, std::string::npos);
|
||||||
|
@ -1758,14 +1759,14 @@ const std::string FileData::findEmulatorPath(std::string& command)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search for the defined value in the Windows Registry.
|
// Search for the defined value in the Windows Registry.
|
||||||
std::string registryValueKey {
|
const std::string& registryValueKey {
|
||||||
Utils::String::replace(Utils::FileSystem::getParent(value), "/", "\\")};
|
Utils::String::replace(Utils::FileSystem::getParent(value), "/", "\\")};
|
||||||
std::string registryValue {Utils::FileSystem::getFileName(value)};
|
const std::string& registryValue {Utils::FileSystem::getFileName(value)};
|
||||||
|
|
||||||
HKEY registryKey;
|
HKEY registryKey;
|
||||||
LSTATUS keyStatus {-1};
|
LSTATUS keyStatus {-1};
|
||||||
LSTATUS pathStatus {-1};
|
LSTATUS pathStatus {-1};
|
||||||
char path[1024] {};
|
char path[1024] {0};
|
||||||
DWORD pathSize {1024};
|
DWORD pathSize {1024};
|
||||||
|
|
||||||
// First look in HKEY_CURRENT_USER.
|
// First look in HKEY_CURRENT_USER.
|
||||||
|
@ -1811,7 +1812,7 @@ const std::string FileData::findEmulatorPath(std::string& command)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (std::string path : emulatorSystemPaths) {
|
for (std::string& path : emulatorSystemPaths) {
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
std::wstring pathWide {Utils::String::stringToWideString(path)};
|
std::wstring pathWide {Utils::String::stringToWideString(path)};
|
||||||
// Search for the emulator using the PATH environment variable.
|
// Search for the emulator using the PATH environment variable.
|
||||||
|
@ -1846,11 +1847,11 @@ const std::string FileData::findEmulatorPath(std::string& command)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::string path : emulatorStaticPaths) {
|
for (std::string& path : emulatorStaticPaths) {
|
||||||
// If a pipe character is present in the staticpath entry it means we should substitute
|
// If a pipe character is present in the staticpath entry it means we should substitute
|
||||||
// the emulator binary with whatever is defined after the pipe character.
|
// the emulator binary with whatever is defined after the pipe character.
|
||||||
std::string replaceCommand;
|
std::string replaceCommand;
|
||||||
size_t pipePos {path.find('|')};
|
const size_t pipePos {path.find('|')};
|
||||||
|
|
||||||
if (pipePos != std::string::npos) {
|
if (pipePos != std::string::npos) {
|
||||||
replaceCommand = path.substr(pipePos + 1);
|
replaceCommand = path.substr(pipePos + 1);
|
||||||
|
@ -1899,7 +1900,7 @@ const std::string FileData::findEmulatorPath(std::string& command)
|
||||||
// If the first character is a quotation mark, then we need to extract up to the
|
// If the first character is a quotation mark, then we need to extract up to the
|
||||||
// next quotation mark, otherwise we'll only extract up to the first space character.
|
// next quotation mark, otherwise we'll only extract up to the first space character.
|
||||||
if (command.front() == '\"') {
|
if (command.front() == '\"') {
|
||||||
std::string emuTemp {command.substr(1, std::string::npos)};
|
const std::string& emuTemp {command.substr(1, std::string::npos)};
|
||||||
emuExecutable = emuTemp.substr(0, emuTemp.find('"'));
|
emuExecutable = emuTemp.substr(0, emuTemp.find('"'));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1917,7 +1918,8 @@ const std::string FileData::findEmulatorPath(std::string& command)
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
std::wstring emuExecutableWide {Utils::String::stringToWideString(emuExecutable)};
|
std::wstring emuExecutableWide {Utils::String::stringToWideString(emuExecutable)};
|
||||||
// Search for the emulator using the PATH environment variable.
|
// Search for the emulator using the PATH environment variable.
|
||||||
DWORD size {SearchPathW(nullptr, emuExecutableWide.c_str(), L".exe", 0, nullptr, nullptr)};
|
const DWORD size {
|
||||||
|
SearchPathW(nullptr, emuExecutableWide.c_str(), L".exe", 0, nullptr, nullptr)};
|
||||||
|
|
||||||
if (size) {
|
if (size) {
|
||||||
std::vector<wchar_t> pathBuffer(static_cast<size_t>(size) + 1);
|
std::vector<wchar_t> pathBuffer(static_cast<size_t>(size) + 1);
|
||||||
|
|
|
@ -32,20 +32,33 @@ namespace GamelistFileParser
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::FileSystem::StringList pathList = Utils::FileSystem::getPathList(relative);
|
const Utils::FileSystem::StringList& pathList {Utils::FileSystem::getPathList(relative)};
|
||||||
auto path_it = pathList.begin();
|
auto path_it = pathList.begin();
|
||||||
FileData* treeNode = root;
|
FileData* treeNode {root};
|
||||||
bool found = false;
|
bool found {false};
|
||||||
while (path_it != pathList.end()) {
|
|
||||||
const std::unordered_map<std::string, FileData*>& children =
|
|
||||||
treeNode->getChildrenByFilename();
|
|
||||||
|
|
||||||
std::string key = *path_it;
|
while (path_it != pathList.end()) {
|
||||||
found = children.find(key) != children.cend();
|
// Workaround for an extremely rare issue that can basically only happen if a dot (.)
|
||||||
if (found) {
|
// has been defined as a valid extension for the system (meaning extensionless files
|
||||||
treeNode = children.at(key);
|
// are loaded), in combination with the "Only show ROMs from gamelist.xml files" option
|
||||||
|
// being enabled and a stale entry being present in the gamelist.xml file that perfectly
|
||||||
|
// matches a folder which is actually in use. The workaround is not a perfect solution
|
||||||
|
// but it at least prevents the application from crashing.
|
||||||
|
if (treeNode->getType() != FOLDER) {
|
||||||
|
LOG(LogWarning)
|
||||||
|
<< "Invalid gamelist entry caused by folder having the same name as a stale "
|
||||||
|
<< "extensionless game file (this may cause undefined behavior):";
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::unordered_map<std::string, FileData*>& children {
|
||||||
|
treeNode->getChildrenByFilename()};
|
||||||
|
|
||||||
|
const std::string key {*path_it};
|
||||||
|
found = children.find(key) != children.cend();
|
||||||
|
if (found)
|
||||||
|
treeNode = children.at(key);
|
||||||
|
|
||||||
// This is the end.
|
// This is the end.
|
||||||
if (path_it == --pathList.end()) {
|
if (path_it == --pathList.end()) {
|
||||||
if (found)
|
if (found)
|
||||||
|
@ -58,8 +71,8 @@ namespace GamelistFileParser
|
||||||
|
|
||||||
// Handle the special situation where a file exists and has an entry in the
|
// Handle the special situation where a file exists and has an entry in the
|
||||||
// gamelist.xml file but the file extension is not configured in es_systems.xml.
|
// gamelist.xml file but the file extension is not configured in es_systems.xml.
|
||||||
const std::vector<std::string> extensions =
|
const std::vector<std::string>& extensions {
|
||||||
system->getSystemEnvData()->mSearchExtensions;
|
system->getSystemEnvData()->mSearchExtensions};
|
||||||
|
|
||||||
if (std::find(extensions.cbegin(), extensions.cend(),
|
if (std::find(extensions.cbegin(), extensions.cend(),
|
||||||
Utils::FileSystem::getExtension(path)) == extensions.cend()) {
|
Utils::FileSystem::getExtension(path)) == extensions.cend()) {
|
||||||
|
@ -69,7 +82,7 @@ namespace GamelistFileParser
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileData* file = new FileData(type, path, system->getSystemEnvData(), system);
|
FileData* file {new FileData(type, path, system->getSystemEnvData(), system)};
|
||||||
|
|
||||||
// Skipping arcade assets from gamelist.
|
// Skipping arcade assets from gamelist.
|
||||||
if (!file->isArcadeAsset())
|
if (!file->isArcadeAsset())
|
||||||
|
@ -87,9 +100,8 @@ namespace GamelistFileParser
|
||||||
|
|
||||||
if (!system->getFlattenFolders()) {
|
if (!system->getFlattenFolders()) {
|
||||||
// Create missing folder.
|
// Create missing folder.
|
||||||
FileData* folder {new FileData(
|
FileData* folder {new FileData(FOLDER, treeNode->getPath() + "/" + *path_it,
|
||||||
FOLDER, Utils::FileSystem::getStem(treeNode->getPath()) + "/" + *path_it,
|
system->getSystemEnvData(), system)};
|
||||||
system->getSystemEnvData(), system)};
|
|
||||||
treeNode->addChild(folder);
|
treeNode->addChild(folder);
|
||||||
treeNode = folder;
|
treeNode = folder;
|
||||||
}
|
}
|
||||||
|
@ -103,8 +115,8 @@ namespace GamelistFileParser
|
||||||
|
|
||||||
void parseGamelist(SystemData* system)
|
void parseGamelist(SystemData* system)
|
||||||
{
|
{
|
||||||
bool trustGamelist = Settings::getInstance()->getBool("ParseGamelistOnly");
|
const bool trustGamelist {Settings::getInstance()->getBool("ParseGamelistOnly")};
|
||||||
std::string xmlpath = system->getGamelistPath(false);
|
const std::string& xmlpath {system->getGamelistPath(false)};
|
||||||
|
|
||||||
if (!Utils::FileSystem::exists(xmlpath)) {
|
if (!Utils::FileSystem::exists(xmlpath)) {
|
||||||
LOG(LogDebug) << "GamelistFileParser::parseGamelist(): System \"" << system->getName()
|
LOG(LogDebug) << "GamelistFileParser::parseGamelist(): System \"" << system->getName()
|
||||||
|
@ -121,10 +133,10 @@ namespace GamelistFileParser
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
pugi::xml_parse_result result =
|
const pugi::xml_parse_result& result {
|
||||||
doc.load_file(Utils::String::stringToWideString(xmlpath).c_str());
|
doc.load_file(Utils::String::stringToWideString(xmlpath).c_str())};
|
||||||
#else
|
#else
|
||||||
pugi::xml_parse_result result = doc.load_file(xmlpath.c_str());
|
const pugi::xml_parse_result& result {doc.load_file(xmlpath.c_str())};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
|
@ -133,18 +145,18 @@ namespace GamelistFileParser
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pugi::xml_node root = doc.child("gameList");
|
const pugi::xml_node& root {doc.child("gameList")};
|
||||||
if (!root) {
|
if (!root) {
|
||||||
LOG(LogError) << "Couldn't find <gameList> node in gamelist \"" << xmlpath << "\"";
|
LOG(LogError) << "Couldn't find <gameList> node in gamelist \"" << xmlpath << "\"";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pugi::xml_node alternativeEmulator = doc.child("alternativeEmulator");
|
const pugi::xml_node& alternativeEmulator {doc.child("alternativeEmulator")};
|
||||||
if (alternativeEmulator) {
|
if (alternativeEmulator) {
|
||||||
std::string label = alternativeEmulator.child("label").text().get();
|
const std::string& label {alternativeEmulator.child("label").text().get()};
|
||||||
if (label != "") {
|
if (label != "") {
|
||||||
bool validLabel = false;
|
bool validLabel {false};
|
||||||
for (auto command : system->getSystemEnvData()->mLaunchCommands) {
|
for (auto& command : system->getSystemEnvData()->mLaunchCommands) {
|
||||||
if (command.second == label)
|
if (command.second == label)
|
||||||
validLabel = true;
|
validLabel = true;
|
||||||
}
|
}
|
||||||
|
@ -165,18 +177,19 @@ namespace GamelistFileParser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string relativeTo = system->getStartPath();
|
const std::string& relativeTo {system->getStartPath()};
|
||||||
bool showHiddenFiles = Settings::getInstance()->getBool("ShowHiddenFiles");
|
const bool showHiddenFiles {Settings::getInstance()->getBool("ShowHiddenFiles")};
|
||||||
|
|
||||||
std::vector<std::string> tagList = {"game", "folder"};
|
const std::vector<std::string> tagList {"game", "folder"};
|
||||||
FileType typeList[2] = {GAME, FOLDER};
|
const FileType typeList[2] = {GAME, FOLDER};
|
||||||
for (int i = 0; i < 2; ++i) {
|
|
||||||
std::string tag = tagList[i];
|
for (int i {0}; i < 2; ++i) {
|
||||||
FileType type = typeList[i];
|
std::string tag {tagList[i]};
|
||||||
for (pugi::xml_node fileNode = root.child(tag.c_str()); fileNode;
|
FileType type {typeList[i]};
|
||||||
|
for (pugi::xml_node fileNode {root.child(tag.c_str())}; fileNode;
|
||||||
fileNode = fileNode.next_sibling(tag.c_str())) {
|
fileNode = fileNode.next_sibling(tag.c_str())) {
|
||||||
const std::string path = Utils::FileSystem::resolveRelativePath(
|
const std::string& path {Utils::FileSystem::resolveRelativePath(
|
||||||
fileNode.child("path").text().get(), relativeTo, false);
|
fileNode.child("path").text().get(), relativeTo, false)};
|
||||||
|
|
||||||
if (!trustGamelist && !Utils::FileSystem::exists(path)) {
|
if (!trustGamelist && !Utils::FileSystem::exists(path)) {
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
|
@ -199,7 +212,7 @@ namespace GamelistFileParser
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileData* file = findOrCreateFile(system, path, type);
|
FileData* file {findOrCreateFile(system, path, type)};
|
||||||
|
|
||||||
// Don't load entries with the wrong type. This should very rarely (if ever) happen.
|
// Don't load entries with the wrong type. This should very rarely (if ever) happen.
|
||||||
if (file != nullptr && ((tag == "game" && file->getType() == FOLDER) ||
|
if (file != nullptr && ((tag == "game" && file->getType() == FOLDER) ||
|
||||||
|
@ -214,7 +227,7 @@ namespace GamelistFileParser
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (!file->isArcadeAsset()) {
|
else if (!file->isArcadeAsset()) {
|
||||||
std::string defaultName = file->metadata.get("name");
|
const std::string& defaultName {file->metadata.get("name")};
|
||||||
if (file->getType() == FOLDER) {
|
if (file->getType() == FOLDER) {
|
||||||
file->metadata =
|
file->metadata =
|
||||||
MetaDataList::createFromXML(FOLDER_METADATA, fileNode, relativeTo);
|
MetaDataList::createFromXML(FOLDER_METADATA, fileNode, relativeTo);
|
||||||
|
@ -265,7 +278,7 @@ namespace GamelistFileParser
|
||||||
SystemData* system)
|
SystemData* system)
|
||||||
{
|
{
|
||||||
// Create game and add to parent node.
|
// Create game and add to parent node.
|
||||||
pugi::xml_node newNode = parent.append_child(tag.c_str());
|
pugi::xml_node newNode {parent.append_child(tag.c_str())};
|
||||||
|
|
||||||
// Write metadata.
|
// Write metadata.
|
||||||
file->metadata.appendToXML(newNode, true, system->getStartPath());
|
file->metadata.appendToXML(newNode, true, system->getStartPath());
|
||||||
|
@ -303,17 +316,17 @@ namespace GamelistFileParser
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
pugi::xml_node root;
|
pugi::xml_node root;
|
||||||
std::string xmlReadPath = system->getGamelistPath(false);
|
const std::string& xmlReadPath {system->getGamelistPath(false)};
|
||||||
bool hasAlternativeEmulatorTag = false;
|
bool hasAlternativeEmulatorTag {false};
|
||||||
|
|
||||||
if (Utils::FileSystem::exists(xmlReadPath)) {
|
if (Utils::FileSystem::exists(xmlReadPath)) {
|
||||||
// Parse an existing file first.
|
// Parse an existing file first.
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
pugi::xml_parse_result result =
|
const pugi::xml_parse_result& result {
|
||||||
doc.load_file(Utils::String::stringToWideString(xmlReadPath).c_str());
|
doc.load_file(Utils::String::stringToWideString(xmlReadPath).c_str())};
|
||||||
#else
|
#else
|
||||||
pugi::xml_parse_result result = doc.load_file(xmlReadPath.c_str());
|
const pugi::xml_parse_result& result {doc.load_file(xmlReadPath.c_str())};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
|
@ -329,7 +342,7 @@ namespace GamelistFileParser
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (updateAlternativeEmulator) {
|
if (updateAlternativeEmulator) {
|
||||||
pugi::xml_node alternativeEmulator = doc.child("alternativeEmulator");
|
pugi::xml_node alternativeEmulator {doc.child("alternativeEmulator")};
|
||||||
|
|
||||||
if (alternativeEmulator)
|
if (alternativeEmulator)
|
||||||
hasAlternativeEmulatorTag = true;
|
hasAlternativeEmulatorTag = true;
|
||||||
|
@ -340,7 +353,7 @@ namespace GamelistFileParser
|
||||||
alternativeEmulator = doc.child("alternativeEmulator");
|
alternativeEmulator = doc.child("alternativeEmulator");
|
||||||
}
|
}
|
||||||
|
|
||||||
pugi::xml_node label = alternativeEmulator.child("label");
|
const pugi::xml_node& label {alternativeEmulator.child("label")};
|
||||||
|
|
||||||
if (label && system->getAlternativeEmulator() !=
|
if (label && system->getAlternativeEmulator() !=
|
||||||
alternativeEmulator.child("label").text().get()) {
|
alternativeEmulator.child("label").text().get()) {
|
||||||
|
@ -360,7 +373,7 @@ namespace GamelistFileParser
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (updateAlternativeEmulator && system->getAlternativeEmulator() != "") {
|
if (updateAlternativeEmulator && system->getAlternativeEmulator() != "") {
|
||||||
pugi::xml_node alternativeEmulator = doc.prepend_child("alternativeEmulator");
|
pugi::xml_node alternativeEmulator {doc.prepend_child("alternativeEmulator")};
|
||||||
alternativeEmulator.prepend_child("label").text().set(
|
alternativeEmulator.prepend_child("label").text().set(
|
||||||
system->getAlternativeEmulator().c_str());
|
system->getAlternativeEmulator().c_str());
|
||||||
}
|
}
|
||||||
|
@ -372,14 +385,14 @@ namespace GamelistFileParser
|
||||||
// through all our games and add the information from there.
|
// through all our games and add the information from there.
|
||||||
FileData* rootFolder {system->getRootFolder()};
|
FileData* rootFolder {system->getRootFolder()};
|
||||||
if (rootFolder != nullptr) {
|
if (rootFolder != nullptr) {
|
||||||
int numUpdated = 0;
|
int numUpdated {0};
|
||||||
|
|
||||||
// Get only files, no folders.
|
// Get only files, no folders.
|
||||||
std::vector<FileData*> files = rootFolder->getFilesRecursive(GAME | FOLDER);
|
std::vector<FileData*> files {rootFolder->getFilesRecursive(GAME | FOLDER)};
|
||||||
// Iterate through all files, checking if they're already in the XML file.
|
// Iterate through all files, checking if they're already in the XML file.
|
||||||
for (std::vector<FileData*>::const_iterator fit = files.cbegin(); // Line break.
|
for (std::vector<FileData*>::const_iterator fit {files.cbegin()}; // Line break.
|
||||||
fit != files.cend(); ++fit) {
|
fit != files.cend(); ++fit) {
|
||||||
const std::string tag = ((*fit)->getType() == GAME) ? "game" : "folder";
|
const std::string& tag {((*fit)->getType() == GAME) ? "game" : "folder"};
|
||||||
|
|
||||||
// Do not touch if it wasn't changed and is not flagged for deletion.
|
// Do not touch if it wasn't changed and is not flagged for deletion.
|
||||||
if (!(*fit)->metadata.wasChanged() && !(*fit)->getDeletionFlag())
|
if (!(*fit)->metadata.wasChanged() && !(*fit)->getDeletionFlag())
|
||||||
|
@ -387,18 +400,19 @@ namespace GamelistFileParser
|
||||||
|
|
||||||
// Check if the file already exists in the XML file.
|
// Check if the file already exists in the XML file.
|
||||||
// If it does, remove the entry before adding it back.
|
// If it does, remove the entry before adding it back.
|
||||||
for (pugi::xml_node fileNode = root.child(tag.c_str()); fileNode;
|
for (pugi::xml_node fileNode {root.child(tag.c_str())}; fileNode;
|
||||||
fileNode = fileNode.next_sibling(tag.c_str())) {
|
fileNode = fileNode.next_sibling(tag.c_str())) {
|
||||||
pugi::xml_node pathNode = fileNode.child("path");
|
const pugi::xml_node& pathNode {fileNode.child("path")};
|
||||||
if (!pathNode) {
|
if (!pathNode) {
|
||||||
LOG(LogError) << "<" << tag << "> node contains no <path> child";
|
LOG(LogError) << "<" << tag << "> node contains no <path> child";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string nodePath =
|
const std::string& nodePath {
|
||||||
Utils::FileSystem::getCanonicalPath(Utils::FileSystem::resolveRelativePath(
|
Utils::FileSystem::getCanonicalPath(Utils::FileSystem::resolveRelativePath(
|
||||||
pathNode.text().get(), system->getStartPath(), true));
|
pathNode.text().get(), system->getStartPath(), true))};
|
||||||
std::string gamePath = Utils::FileSystem::getCanonicalPath((*fit)->getPath());
|
const std::string& gamePath {
|
||||||
|
Utils::FileSystem::getCanonicalPath((*fit)->getPath())};
|
||||||
|
|
||||||
if (nodePath == gamePath) {
|
if (nodePath == gamePath) {
|
||||||
// Found it
|
// Found it
|
||||||
|
@ -420,7 +434,7 @@ namespace GamelistFileParser
|
||||||
// Now write the file.
|
// Now write the file.
|
||||||
if (numUpdated > 0 || updateAlternativeEmulator) {
|
if (numUpdated > 0 || updateAlternativeEmulator) {
|
||||||
// Make sure the folders leading up to this path exist (or the write will fail).
|
// Make sure the folders leading up to this path exist (or the write will fail).
|
||||||
std::string xmlWritePath(system->getGamelistPath(true));
|
const std::string& xmlWritePath {system->getGamelistPath(true)};
|
||||||
Utils::FileSystem::createDirectory(Utils::FileSystem::getParent(xmlWritePath));
|
Utils::FileSystem::createDirectory(Utils::FileSystem::getParent(xmlWritePath));
|
||||||
|
|
||||||
if (updateAlternativeEmulator) {
|
if (updateAlternativeEmulator) {
|
||||||
|
|
|
@ -40,8 +40,8 @@ FindRules::FindRules()
|
||||||
|
|
||||||
void FindRules::loadFindRules()
|
void FindRules::loadFindRules()
|
||||||
{
|
{
|
||||||
std::string customSystemsDirectory {Utils::FileSystem::getHomePath() +
|
const std::string& customSystemsDirectory {Utils::FileSystem::getHomePath() +
|
||||||
"/.emulationstation/custom_systems"};
|
"/.emulationstation/custom_systems"};
|
||||||
|
|
||||||
std::string path {customSystemsDirectory + "/es_find_rules.xml"};
|
std::string path {customSystemsDirectory + "/es_find_rules.xml"};
|
||||||
|
|
||||||
|
@ -75,9 +75,10 @@ void FindRules::loadFindRules()
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
pugi::xml_parse_result res = doc.load_file(Utils::String::stringToWideString(path).c_str());
|
const pugi::xml_parse_result& res {
|
||||||
|
doc.load_file(Utils::String::stringToWideString(path).c_str())};
|
||||||
#else
|
#else
|
||||||
pugi::xml_parse_result res = doc.load_file(path.c_str());
|
const pugi::xml_parse_result& res {doc.load_file(path.c_str())};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
|
@ -86,7 +87,7 @@ void FindRules::loadFindRules()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually read the file.
|
// Actually read the file.
|
||||||
pugi::xml_node ruleList = doc.child("ruleList");
|
const pugi::xml_node& ruleList {doc.child("ruleList")};
|
||||||
|
|
||||||
if (!ruleList) {
|
if (!ruleList) {
|
||||||
LOG(LogError) << "es_find_rules.xml is missing the <ruleList> tag";
|
LOG(LogError) << "es_find_rules.xml is missing the <ruleList> tag";
|
||||||
|
@ -96,9 +97,9 @@ void FindRules::loadFindRules()
|
||||||
EmulatorRules emulatorRules;
|
EmulatorRules emulatorRules;
|
||||||
CoreRules coreRules;
|
CoreRules coreRules;
|
||||||
|
|
||||||
for (pugi::xml_node emulator = ruleList.child("emulator"); emulator;
|
for (pugi::xml_node emulator {ruleList.child("emulator")}; emulator;
|
||||||
emulator = emulator.next_sibling("emulator")) {
|
emulator = emulator.next_sibling("emulator")) {
|
||||||
std::string emulatorName = emulator.attribute("name").as_string();
|
const std::string& emulatorName {emulator.attribute("name").as_string()};
|
||||||
if (emulatorName.empty()) {
|
if (emulatorName.empty()) {
|
||||||
LOG(LogWarning) << "Found emulator tag without name attribute, skipping entry";
|
LOG(LogWarning) << "Found emulator tag without name attribute, skipping entry";
|
||||||
continue;
|
continue;
|
||||||
|
@ -109,7 +110,7 @@ void FindRules::loadFindRules()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (pugi::xml_node rule = emulator.child("rule"); rule; rule = rule.next_sibling("rule")) {
|
for (pugi::xml_node rule = emulator.child("rule"); rule; rule = rule.next_sibling("rule")) {
|
||||||
std::string ruleType = rule.attribute("type").as_string();
|
const std::string& ruleType {rule.attribute("type").as_string()};
|
||||||
if (ruleType.empty()) {
|
if (ruleType.empty()) {
|
||||||
LOG(LogWarning) << "Found rule tag without type attribute for emulator \""
|
LOG(LogWarning) << "Found rule tag without type attribute for emulator \""
|
||||||
<< emulatorName << "\", skipping entry";
|
<< emulatorName << "\", skipping entry";
|
||||||
|
@ -125,18 +126,18 @@ void FindRules::loadFindRules()
|
||||||
<< emulatorName << "\", skipping entry";
|
<< emulatorName << "\", skipping entry";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (pugi::xml_node entry = rule.child("entry"); entry;
|
for (pugi::xml_node entry {rule.child("entry")}; entry;
|
||||||
entry = entry.next_sibling("entry")) {
|
entry = entry.next_sibling("entry")) {
|
||||||
std::string entryValue = entry.text().get();
|
const std::string& entryValue {entry.text().get()};
|
||||||
if (ruleType == "systempath")
|
if (ruleType == "systempath")
|
||||||
emulatorRules.systemPaths.push_back(entryValue);
|
emulatorRules.systemPaths.emplace_back(entryValue);
|
||||||
else if (ruleType == "staticpath")
|
else if (ruleType == "staticpath")
|
||||||
emulatorRules.staticPaths.push_back(entryValue);
|
emulatorRules.staticPaths.emplace_back(entryValue);
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
else if (ruleType == "winregistrypath")
|
else if (ruleType == "winregistrypath")
|
||||||
emulatorRules.winRegistryPaths.push_back(entryValue);
|
emulatorRules.winRegistryPaths.emplace_back(entryValue);
|
||||||
else if (ruleType == "winregistryvalue")
|
else if (ruleType == "winregistryvalue")
|
||||||
emulatorRules.winRegistryValues.push_back(entryValue);
|
emulatorRules.winRegistryValues.emplace_back(entryValue);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,8 +150,8 @@ void FindRules::loadFindRules()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
for (pugi::xml_node core = ruleList.child("core"); core; core = core.next_sibling("core")) {
|
for (pugi::xml_node core {ruleList.child("core")}; core; core = core.next_sibling("core")) {
|
||||||
std::string coreName = core.attribute("name").as_string();
|
const std::string& coreName {core.attribute("name").as_string()};
|
||||||
if (coreName.empty()) {
|
if (coreName.empty()) {
|
||||||
LOG(LogWarning) << "Found core tag without name attribute, skipping entry";
|
LOG(LogWarning) << "Found core tag without name attribute, skipping entry";
|
||||||
continue;
|
continue;
|
||||||
|
@ -159,8 +160,8 @@ void FindRules::loadFindRules()
|
||||||
LOG(LogWarning) << "Found repeating core tag \"" << coreName << "\", skipping entry";
|
LOG(LogWarning) << "Found repeating core tag \"" << coreName << "\", skipping entry";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (pugi::xml_node rule = core.child("rule"); rule; rule = rule.next_sibling("rule")) {
|
for (pugi::xml_node rule {core.child("rule")}; rule; rule = rule.next_sibling("rule")) {
|
||||||
std::string ruleType = rule.attribute("type").as_string();
|
const std::string& ruleType {rule.attribute("type").as_string()};
|
||||||
if (ruleType.empty()) {
|
if (ruleType.empty()) {
|
||||||
LOG(LogWarning) << "Found rule tag without type attribute for core \"" << coreName
|
LOG(LogWarning) << "Found rule tag without type attribute for core \"" << coreName
|
||||||
<< "\", skipping entry";
|
<< "\", skipping entry";
|
||||||
|
@ -171,11 +172,11 @@ void FindRules::loadFindRules()
|
||||||
<< coreName << "\", skipping entry";
|
<< coreName << "\", skipping entry";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (pugi::xml_node entry = rule.child("entry"); entry;
|
for (pugi::xml_node entry {rule.child("entry")}; entry;
|
||||||
entry = entry.next_sibling("entry")) {
|
entry = entry.next_sibling("entry")) {
|
||||||
std::string entryValue = entry.text().get();
|
const std::string& entryValue {entry.text().get()};
|
||||||
if (ruleType == "corepath")
|
if (ruleType == "corepath")
|
||||||
coreRules.corePaths.push_back(entryValue);
|
coreRules.corePaths.emplace_back(entryValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mCores[coreName] = coreRules;
|
mCores[coreName] = coreRules;
|
||||||
|
@ -265,13 +266,12 @@ void SystemData::setIsGameSystemStatus()
|
||||||
|
|
||||||
bool SystemData::populateFolder(FileData* folder)
|
bool SystemData::populateFolder(FileData* folder)
|
||||||
{
|
{
|
||||||
const std::string& folderPath = folder->getPath();
|
|
||||||
|
|
||||||
std::string filePath;
|
std::string filePath;
|
||||||
std::string extension;
|
std::string extension;
|
||||||
bool isGame;
|
const std::string& folderPath {folder->getPath()};
|
||||||
bool showHiddenFiles = Settings::getInstance()->getBool("ShowHiddenFiles");
|
const bool showHiddenFiles {Settings::getInstance()->getBool("ShowHiddenFiles")};
|
||||||
Utils::FileSystem::StringList dirContent = Utils::FileSystem::getDirContent(folderPath);
|
const Utils::FileSystem::StringList& dirContent {Utils::FileSystem::getDirContent(folderPath)};
|
||||||
|
bool isGame {false};
|
||||||
|
|
||||||
// If system directory exists but contains no games, return as error.
|
// If system directory exists but contains no games, return as error.
|
||||||
if (dirContent.size() == 0)
|
if (dirContent.size() == 0)
|
||||||
|
@ -290,9 +290,10 @@ bool SystemData::populateFolder(FileData* folder)
|
||||||
mFlattenFolders = true;
|
mFlattenFolders = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Utils::FileSystem::StringList::const_iterator it = dirContent.cbegin();
|
for (Utils::FileSystem::StringList::const_iterator it {dirContent.cbegin()};
|
||||||
it != dirContent.cend(); ++it) {
|
it != dirContent.cend(); ++it) {
|
||||||
filePath = *it;
|
filePath = *it;
|
||||||
|
const bool isDirectory {Utils::FileSystem::isDirectory(filePath)};
|
||||||
|
|
||||||
// Skip any recursive symlinks as those would hang the application at various places.
|
// Skip any recursive symlinks as those would hang the application at various places.
|
||||||
if (Utils::FileSystem::isSymlink(filePath)) {
|
if (Utils::FileSystem::isSymlink(filePath)) {
|
||||||
|
@ -306,8 +307,7 @@ bool SystemData::populateFolder(FileData* folder)
|
||||||
// Skip hidden files and folders.
|
// Skip hidden files and folders.
|
||||||
if (!showHiddenFiles && Utils::FileSystem::isHidden(filePath)) {
|
if (!showHiddenFiles && Utils::FileSystem::isHidden(filePath)) {
|
||||||
LOG(LogDebug) << "SystemData::populateFolder(): Skipping hidden "
|
LOG(LogDebug) << "SystemData::populateFolder(): Skipping hidden "
|
||||||
<< (Utils::FileSystem::isDirectory(filePath) ? "directory \"" : "file \"")
|
<< (isDirectory ? "directory \"" : "file \"") << filePath << "\"";
|
||||||
<< filePath << "\"";
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,17 +317,19 @@ bool SystemData::populateFolder(FileData* folder)
|
||||||
extension = Utils::FileSystem::getExtension(filePath);
|
extension = Utils::FileSystem::getExtension(filePath);
|
||||||
|
|
||||||
isGame = false;
|
isGame = false;
|
||||||
|
|
||||||
if (std::find(mEnvData->mSearchExtensions.cbegin(), mEnvData->mSearchExtensions.cend(),
|
if (std::find(mEnvData->mSearchExtensions.cbegin(), mEnvData->mSearchExtensions.cend(),
|
||||||
extension) != mEnvData->mSearchExtensions.cend()) {
|
extension) != mEnvData->mSearchExtensions.cend() &&
|
||||||
FileData* newGame = new FileData(GAME, filePath, mEnvData, this);
|
!(isDirectory && extension == ".")) {
|
||||||
|
FileData* newGame {new FileData(GAME, filePath, mEnvData, this)};
|
||||||
|
|
||||||
// If adding a configured file extension to a directory it will get interpreted as
|
// If adding a configured file extension to a directory it will get interpreted as
|
||||||
// a regular file. This is useful for displaying multi-file/multi-disc games as single
|
// a regular file. This is useful for displaying multi-file/multi-disc games as single
|
||||||
// entries or for emulators that can get directories passed to them as command line
|
// entries or for emulators that can get directories passed to them as command line
|
||||||
// parameters instead of regular files. In these instances we remove the extension
|
// parameters instead of regular files. In these instances we remove the extension
|
||||||
// from the metadata name so it does not show up in the gamelists and similar.
|
// from the metadata name so it does not show up in the gamelists and similar.
|
||||||
if (Utils::FileSystem::isDirectory(filePath)) {
|
if (isDirectory && extension != ".") {
|
||||||
const std::string folderName = newGame->metadata.get("name");
|
const std::string& folderName {newGame->metadata.get("name")};
|
||||||
newGame->metadata.set(
|
newGame->metadata.set(
|
||||||
"name", folderName.substr(0, folderName.length() - extension.length()));
|
"name", folderName.substr(0, folderName.length() - extension.length()));
|
||||||
}
|
}
|
||||||
|
@ -343,15 +345,15 @@ bool SystemData::populateFolder(FileData* folder)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add directories that also do not match an extension as folders.
|
// Add directories that also do not match an extension as folders.
|
||||||
if (!isGame && Utils::FileSystem::isDirectory(filePath)) {
|
if (!isGame && isDirectory) {
|
||||||
// Make sure that it's not a recursive symlink pointing to a location higher in the
|
// Make sure that it's not a recursive symlink pointing to a location higher in the
|
||||||
// hierarchy as the application would run forever trying to resolve the link.
|
// hierarchy as the application would run forever trying to resolve the link.
|
||||||
if (Utils::FileSystem::isSymlink(filePath)) {
|
if (Utils::FileSystem::isSymlink(filePath)) {
|
||||||
const std::string canonicalPath {Utils::FileSystem::getCanonicalPath(filePath)};
|
const std::string& canonicalPath {Utils::FileSystem::getCanonicalPath(filePath)};
|
||||||
const std::string canonicalStartPath {
|
const std::string& canonicalStartPath {
|
||||||
Utils::FileSystem::getCanonicalPath(mEnvData->mStartPath)};
|
Utils::FileSystem::getCanonicalPath(mEnvData->mStartPath)};
|
||||||
if (canonicalPath.size() >= canonicalStartPath.size()) {
|
if (canonicalPath.size() >= canonicalStartPath.size()) {
|
||||||
const std::string combinedPath {
|
const std::string& combinedPath {
|
||||||
mEnvData->mStartPath +
|
mEnvData->mStartPath +
|
||||||
canonicalPath.substr(canonicalStartPath.size(),
|
canonicalPath.substr(canonicalStartPath.size(),
|
||||||
canonicalStartPath.size() - canonicalPath.size())};
|
canonicalStartPath.size() - canonicalPath.size())};
|
||||||
|
@ -384,9 +386,9 @@ bool SystemData::populateFolder(FileData* folder)
|
||||||
|
|
||||||
void SystemData::indexAllGameFilters(const FileData* folder)
|
void SystemData::indexAllGameFilters(const FileData* folder)
|
||||||
{
|
{
|
||||||
const std::vector<FileData*>& children = folder->getChildren();
|
const std::vector<FileData*>& children {folder->getChildren()};
|
||||||
|
|
||||||
for (std::vector<FileData*>::const_iterator it = children.cbegin(); // Line break.
|
for (std::vector<FileData*>::const_iterator it {children.cbegin()}; // Line break.
|
||||||
it != children.cend(); ++it) {
|
it != children.cend(); ++it) {
|
||||||
switch ((*it)->getType()) {
|
switch ((*it)->getType()) {
|
||||||
case GAME:
|
case GAME:
|
||||||
|
@ -405,10 +407,10 @@ std::vector<std::string> readList(const std::string& str, const std::string& del
|
||||||
{
|
{
|
||||||
std::vector<std::string> ret;
|
std::vector<std::string> ret;
|
||||||
|
|
||||||
size_t prevOff = str.find_first_not_of(delims, 0);
|
size_t prevOff {str.find_first_not_of(delims, 0)};
|
||||||
size_t off = str.find_first_of(delims, prevOff);
|
size_t off {str.find_first_of(delims, prevOff)};
|
||||||
while (off != std::string::npos || prevOff != std::string::npos) {
|
while (off != std::string::npos || prevOff != std::string::npos) {
|
||||||
ret.push_back(str.substr(prevOff, off - prevOff));
|
ret.emplace_back(str.substr(prevOff, off - prevOff));
|
||||||
|
|
||||||
prevOff = str.find_first_not_of(delims, off);
|
prevOff = str.find_first_not_of(delims, off);
|
||||||
off = str.find_first_of(delims, prevOff);
|
off = str.find_first_of(delims, prevOff);
|
||||||
|
@ -430,12 +432,12 @@ bool SystemData::loadConfig()
|
||||||
LOG(LogInfo) << "Only parsing the gamelist.xml files, not scanning system directories";
|
LOG(LogInfo) << "Only parsing the gamelist.xml files, not scanning system directories";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> configPaths = getConfigPath(true);
|
const std::vector<std::string>& configPaths {getConfigPath(true)};
|
||||||
const std::string rompath = FileData::getROMDirectory();
|
const std::string& rompath {FileData::getROMDirectory()};
|
||||||
|
|
||||||
bool onlyProcessCustomFile = false;
|
bool onlyProcessCustomFile {false};
|
||||||
|
|
||||||
for (auto configPath : configPaths) {
|
for (auto& configPath : configPaths) {
|
||||||
// If the loadExclusive tag is present in the custom es_systems.xml file, then skip
|
// If the loadExclusive tag is present in the custom es_systems.xml file, then skip
|
||||||
// processing of the bundled configuration file.
|
// processing of the bundled configuration file.
|
||||||
if (onlyProcessCustomFile)
|
if (onlyProcessCustomFile)
|
||||||
|
@ -450,10 +452,10 @@ bool SystemData::loadConfig()
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
pugi::xml_parse_result res =
|
const pugi::xml_parse_result& res {
|
||||||
doc.load_file(Utils::String::stringToWideString(configPath).c_str());
|
doc.load_file(Utils::String::stringToWideString(configPath).c_str())};
|
||||||
#else
|
#else
|
||||||
pugi::xml_parse_result res = doc.load_file(configPath.c_str());
|
const pugi::xml_parse_result& res {doc.load_file(configPath.c_str())};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
|
@ -461,7 +463,7 @@ bool SystemData::loadConfig()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pugi::xml_node loadExclusive = doc.child("loadExclusive");
|
const pugi::xml_node& loadExclusive {doc.child("loadExclusive")};
|
||||||
if (loadExclusive) {
|
if (loadExclusive) {
|
||||||
if (configPath == configPaths.front() && configPaths.size() > 1) {
|
if (configPath == configPaths.front() && configPaths.size() > 1) {
|
||||||
LOG(LogInfo) << "Only loading custom file as the <loadExclusive> tag is present";
|
LOG(LogInfo) << "Only loading custom file as the <loadExclusive> tag is present";
|
||||||
|
@ -475,14 +477,14 @@ bool SystemData::loadConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually read the file.
|
// Actually read the file.
|
||||||
pugi::xml_node systemList = doc.child("systemList");
|
const pugi::xml_node& systemList {doc.child("systemList")};
|
||||||
|
|
||||||
if (!systemList) {
|
if (!systemList) {
|
||||||
LOG(LogError) << "es_systems.xml is missing the <systemList> tag";
|
LOG(LogError) << "es_systems.xml is missing the <systemList> tag";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (pugi::xml_node system = systemList.child("system"); system;
|
for (pugi::xml_node system {systemList.child("system")}; system;
|
||||||
system = system.next_sibling("system")) {
|
system = system.next_sibling("system")) {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string fullname;
|
std::string fullname;
|
||||||
|
@ -545,7 +547,7 @@ bool SystemData::loadConfig()
|
||||||
if (Utils::FileSystem::isSymlink(path)) {
|
if (Utils::FileSystem::isSymlink(path)) {
|
||||||
// Make sure that the symlink is not pointing to somewhere higher in the hierarchy
|
// Make sure that the symlink is not pointing to somewhere higher in the hierarchy
|
||||||
// as that would lead to an infite loop, meaning the application would never start.
|
// as that would lead to an infite loop, meaning the application would never start.
|
||||||
std::string resolvedRompath = Utils::FileSystem::getCanonicalPath(rompath);
|
const std::string& resolvedRompath {Utils::FileSystem::getCanonicalPath(rompath)};
|
||||||
if (resolvedRompath.find(Utils::FileSystem::getCanonicalPath(path)) == 0) {
|
if (resolvedRompath.find(Utils::FileSystem::getCanonicalPath(path)) == 0) {
|
||||||
LOG(LogWarning)
|
LOG(LogWarning)
|
||||||
<< "Skipping system \"" << name << "\" as the defined ROM directory \""
|
<< "Skipping system \"" << name << "\" as the defined ROM directory \""
|
||||||
|
@ -561,7 +563,7 @@ bool SystemData::loadConfig()
|
||||||
// the label attribute needs to be set on all entries as it's a requirement for the
|
// the label attribute needs to be set on all entries as it's a requirement for the
|
||||||
// alternative emulator logic.
|
// alternative emulator logic.
|
||||||
std::vector<std::pair<std::string, std::string>> commands;
|
std::vector<std::pair<std::string, std::string>> commands;
|
||||||
for (pugi::xml_node entry = system.child("command"); entry;
|
for (pugi::xml_node entry {system.child("command")}; entry;
|
||||||
entry = entry.next_sibling("command")) {
|
entry = entry.next_sibling("command")) {
|
||||||
if (!entry.attribute("label")) {
|
if (!entry.attribute("label")) {
|
||||||
if (commands.size() == 1) {
|
if (commands.size() == 1) {
|
||||||
|
@ -589,29 +591,29 @@ bool SystemData::loadConfig()
|
||||||
<< name << "\"";
|
<< name << "\"";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
commands.push_back(
|
commands.emplace_back(
|
||||||
std::make_pair(entry.text().get(), entry.attribute("label").as_string()));
|
std::make_pair(entry.text().get(), entry.attribute("label").as_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Platform ID list
|
// Platform ID list
|
||||||
const std::string platformList =
|
const std::string& platformList {
|
||||||
Utils::String::toLower(system.child("platform").text().get());
|
Utils::String::toLower(system.child("platform").text().get())};
|
||||||
|
|
||||||
if (platformList == "") {
|
if (platformList == "") {
|
||||||
LOG(LogWarning) << "No platform defined for system \"" << name
|
LOG(LogWarning) << "No platform defined for system \"" << name
|
||||||
<< "\", scraper searches will be inaccurate";
|
<< "\", scraper searches will be inaccurate";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> platformStrs = readList(platformList);
|
const std::vector<std::string>& platformStrs {readList(platformList)};
|
||||||
std::vector<PlatformIds::PlatformId> platformIds;
|
std::vector<PlatformIds::PlatformId> platformIds;
|
||||||
for (auto it = platformStrs.cbegin(); it != platformStrs.cend(); ++it) {
|
for (auto it = platformStrs.cbegin(); it != platformStrs.cend(); ++it) {
|
||||||
std::string str = *it;
|
std::string str {*it};
|
||||||
PlatformIds::PlatformId platformId = PlatformIds::getPlatformId(str);
|
const PlatformIds::PlatformId platformId {PlatformIds::getPlatformId(str)};
|
||||||
|
|
||||||
if (platformId == PlatformIds::PLATFORM_IGNORE) {
|
if (platformId == PlatformIds::PLATFORM_IGNORE) {
|
||||||
// When platform is PLATFORM_IGNORE, do not allow other platforms.
|
// When platform is PLATFORM_IGNORE, do not allow other platforms.
|
||||||
platformIds.clear();
|
platformIds.clear();
|
||||||
platformIds.push_back(platformId);
|
platformIds.emplace_back(platformId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,7 +623,7 @@ bool SystemData::loadConfig()
|
||||||
LOG(LogWarning) << "Unknown platform \"" << str << "\" defined for system \""
|
LOG(LogWarning) << "Unknown platform \"" << str << "\" defined for system \""
|
||||||
<< name << "\", scraper searches will be inaccurate";
|
<< name << "\", scraper searches will be inaccurate";
|
||||||
else if (platformId != PlatformIds::PLATFORM_UNKNOWN)
|
else if (platformId != PlatformIds::PLATFORM_UNKNOWN)
|
||||||
platformIds.push_back(platformId);
|
platformIds.emplace_back(platformId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Theme folder.
|
// Theme folder.
|
||||||
|
@ -662,20 +664,20 @@ bool SystemData::loadConfig()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Create the system runtime environment data.
|
// Create the system runtime environment data.
|
||||||
SystemEnvironmentData* envData = new SystemEnvironmentData;
|
SystemEnvironmentData* envData {new SystemEnvironmentData};
|
||||||
envData->mStartPath = path;
|
envData->mStartPath = path;
|
||||||
envData->mSearchExtensions = extensions;
|
envData->mSearchExtensions = extensions;
|
||||||
envData->mLaunchCommands = commands;
|
envData->mLaunchCommands = commands;
|
||||||
envData->mPlatformIds = platformIds;
|
envData->mPlatformIds = platformIds;
|
||||||
|
|
||||||
SystemData* newSys = new SystemData(name, fullname, sortName, envData, themeFolder);
|
SystemData* newSys {new SystemData(name, fullname, sortName, envData, themeFolder)};
|
||||||
bool onlyHidden = false;
|
bool onlyHidden {false};
|
||||||
|
|
||||||
// If the option to show hidden games has been disabled, then check whether all
|
// If the option to show hidden games has been disabled, then check whether all
|
||||||
// games for the system are hidden. That will flag the system as empty.
|
// games for the system are hidden. That will flag the system as empty.
|
||||||
if (!Settings::getInstance()->getBool("ShowHiddenGames")) {
|
if (!Settings::getInstance()->getBool("ShowHiddenGames")) {
|
||||||
std::vector<FileData*> recursiveGames =
|
std::vector<FileData*> recursiveGames {
|
||||||
newSys->getRootFolder()->getChildrenRecursive();
|
newSys->getRootFolder()->getChildrenRecursive()};
|
||||||
onlyHidden = true;
|
onlyHidden = true;
|
||||||
for (auto it = recursiveGames.cbegin(); it != recursiveGames.cend(); ++it) {
|
for (auto it = recursiveGames.cbegin(); it != recursiveGames.cend(); ++it) {
|
||||||
if ((*it)->getType() != FOLDER) {
|
if ((*it)->getType() != FOLDER) {
|
||||||
|
@ -692,7 +694,7 @@ bool SystemData::loadConfig()
|
||||||
delete newSys;
|
delete newSys;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sSystemVector.push_back(newSys);
|
sSystemVector.emplace_back(newSys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -723,7 +725,7 @@ std::string SystemData::getLaunchCommandFromLabel(const std::string& label)
|
||||||
|
|
||||||
void SystemData::deleteSystems()
|
void SystemData::deleteSystems()
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < sSystemVector.size(); ++i)
|
for (unsigned int i {0}; i < sSystemVector.size(); ++i)
|
||||||
delete sSystemVector.at(i);
|
delete sSystemVector.at(i);
|
||||||
|
|
||||||
sSystemVector.clear();
|
sSystemVector.clear();
|
||||||
|
@ -734,8 +736,8 @@ std::vector<std::string> SystemData::getConfigPath(bool legacyWarning)
|
||||||
std::vector<std::string> paths;
|
std::vector<std::string> paths;
|
||||||
|
|
||||||
if (legacyWarning) {
|
if (legacyWarning) {
|
||||||
std::string legacyConfigFile =
|
const std::string& legacyConfigFile {Utils::FileSystem::getHomePath() +
|
||||||
Utils::FileSystem::getHomePath() + "/.emulationstation/es_systems.cfg";
|
"/.emulationstation/es_systems.cfg"};
|
||||||
|
|
||||||
if (Utils::FileSystem::exists(legacyConfigFile)) {
|
if (Utils::FileSystem::exists(legacyConfigFile)) {
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
|
@ -751,8 +753,8 @@ std::vector<std::string> SystemData::getConfigPath(bool legacyWarning)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string customSystemsDirectory =
|
const std::string& customSystemsDirectory {Utils::FileSystem::getHomePath() +
|
||||||
Utils::FileSystem::getHomePath() + "/.emulationstation/custom_systems";
|
"/.emulationstation/custom_systems"};
|
||||||
|
|
||||||
if (!Utils::FileSystem::exists(customSystemsDirectory)) {
|
if (!Utils::FileSystem::exists(customSystemsDirectory)) {
|
||||||
LOG(LogInfo) << "Creating custom systems directory \"" << customSystemsDirectory << "\"...";
|
LOG(LogInfo) << "Creating custom systems directory \"" << customSystemsDirectory << "\"...";
|
||||||
|
@ -766,7 +768,7 @@ std::vector<std::string> SystemData::getConfigPath(bool legacyWarning)
|
||||||
|
|
||||||
if (Utils::FileSystem::exists(path)) {
|
if (Utils::FileSystem::exists(path)) {
|
||||||
LOG(LogInfo) << "Found custom systems configuration file";
|
LOG(LogInfo) << "Found custom systems configuration file";
|
||||||
paths.push_back(path);
|
paths.emplace_back(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
|
@ -777,14 +779,14 @@ std::vector<std::string> SystemData::getConfigPath(bool legacyWarning)
|
||||||
path = ResourceManager::getInstance().getResourcePath(":/systems/unix/es_systems.xml", true);
|
path = ResourceManager::getInstance().getResourcePath(":/systems/unix/es_systems.xml", true);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
paths.push_back(path);
|
paths.emplace_back(path);
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemData::createSystemDirectories()
|
bool SystemData::createSystemDirectories()
|
||||||
{
|
{
|
||||||
std::vector<std::string> configPaths = getConfigPath(true);
|
std::vector<std::string> configPaths {getConfigPath(true)};
|
||||||
const std::string rompath = FileData::getROMDirectory();
|
const std::string& rompath {FileData::getROMDirectory()};
|
||||||
|
|
||||||
bool onlyProcessCustomFile = false;
|
bool onlyProcessCustomFile = false;
|
||||||
|
|
||||||
|
@ -831,13 +833,13 @@ bool SystemData::createSystemDirectories()
|
||||||
// processing of the bundled configuration file.
|
// processing of the bundled configuration file.
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
pugi::xml_parse_result res =
|
const pugi::xml_parse_result& res {
|
||||||
doc.load_file(Utils::String::stringToWideString(configPaths.front()).c_str());
|
doc.load_file(Utils::String::stringToWideString(configPaths.front()).c_str())};
|
||||||
#else
|
#else
|
||||||
pugi::xml_parse_result res = doc.load_file(configPaths.front().c_str());
|
const pugi::xml_parse_result& res {doc.load_file(configPaths.front().c_str())};
|
||||||
#endif
|
#endif
|
||||||
if (res) {
|
if (res) {
|
||||||
pugi::xml_node loadExclusive = doc.child("loadExclusive");
|
const pugi::xml_node& loadExclusive {doc.child("loadExclusive")};
|
||||||
if (loadExclusive)
|
if (loadExclusive)
|
||||||
onlyProcessCustomFile = true;
|
onlyProcessCustomFile = true;
|
||||||
}
|
}
|
||||||
|
@ -849,7 +851,7 @@ bool SystemData::createSystemDirectories()
|
||||||
|
|
||||||
std::vector<std::pair<std::string, std::string>> systemsVector;
|
std::vector<std::pair<std::string, std::string>> systemsVector;
|
||||||
|
|
||||||
for (auto configPath : configPaths) {
|
for (auto& configPath : configPaths) {
|
||||||
// If the loadExclusive tag is present.
|
// If the loadExclusive tag is present.
|
||||||
if (onlyProcessCustomFile && configPath == configPaths.front())
|
if (onlyProcessCustomFile && configPath == configPaths.front())
|
||||||
continue;
|
continue;
|
||||||
|
@ -863,10 +865,10 @@ bool SystemData::createSystemDirectories()
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
pugi::xml_parse_result res =
|
const pugi::xml_parse_result& res {
|
||||||
doc.load_file(Utils::String::stringToWideString(configPath).c_str());
|
doc.load_file(Utils::String::stringToWideString(configPath).c_str())};
|
||||||
#else
|
#else
|
||||||
pugi::xml_parse_result res = doc.load_file(configPath.c_str());
|
const pugi::xml_parse_result& res {doc.load_file(configPath.c_str())};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
|
@ -876,14 +878,14 @@ bool SystemData::createSystemDirectories()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually read the file.
|
// Actually read the file.
|
||||||
pugi::xml_node systemList = doc.child("systemList");
|
const pugi::xml_node& systemList {doc.child("systemList")};
|
||||||
|
|
||||||
if (!systemList) {
|
if (!systemList) {
|
||||||
LOG(LogError) << "es_systems.xml is missing the <systemList> tag";
|
LOG(LogError) << "es_systems.xml is missing the <systemList> tag";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (pugi::xml_node system = systemList.child("system"); system;
|
for (pugi::xml_node system {systemList.child("system")}; system;
|
||||||
system = system.next_sibling("system")) {
|
system = system.next_sibling("system")) {
|
||||||
std::string systemDir;
|
std::string systemDir;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
@ -893,7 +895,7 @@ bool SystemData::createSystemDirectories()
|
||||||
std::vector<std::string> commands;
|
std::vector<std::string> commands;
|
||||||
std::string platform;
|
std::string platform;
|
||||||
std::string themeFolder;
|
std::string themeFolder;
|
||||||
const std::string systemInfoFileName = "/systeminfo.txt";
|
const std::string systemInfoFileName {"/systeminfo.txt"};
|
||||||
bool replaceInfoFile = false;
|
bool replaceInfoFile = false;
|
||||||
std::ofstream systemInfoFile;
|
std::ofstream systemInfoFile;
|
||||||
|
|
||||||
|
@ -901,9 +903,9 @@ bool SystemData::createSystemDirectories()
|
||||||
fullname = system.child("fullname").text().get();
|
fullname = system.child("fullname").text().get();
|
||||||
path = system.child("path").text().get();
|
path = system.child("path").text().get();
|
||||||
extensions = system.child("extension").text().get();
|
extensions = system.child("extension").text().get();
|
||||||
for (pugi::xml_node entry = system.child("command"); entry;
|
for (pugi::xml_node entry {system.child("command")}; entry;
|
||||||
entry = entry.next_sibling("command")) {
|
entry = entry.next_sibling("command")) {
|
||||||
commands.push_back(entry.text().get());
|
commands.emplace_back(entry.text().get());
|
||||||
}
|
}
|
||||||
platform = Utils::String::toLower(system.child("platform").text().get());
|
platform = Utils::String::toLower(system.child("platform").text().get());
|
||||||
themeFolder = system.child("theme").text().as_string(name.c_str());
|
themeFolder = system.child("theme").text().as_string(name.c_str());
|
||||||
|
@ -1000,9 +1002,10 @@ bool SystemData::createSystemDirectories()
|
||||||
systemsVector.erase(systemIter);
|
systemsVector.erase(systemIter);
|
||||||
|
|
||||||
if (configPaths.size() != 1 && configPath == configPaths.back())
|
if (configPaths.size() != 1 && configPath == configPaths.back())
|
||||||
systemsVector.push_back(std::make_pair(systemDir + " (custom system)", fullname));
|
systemsVector.emplace_back(
|
||||||
|
std::make_pair(systemDir + " (custom system)", fullname));
|
||||||
else
|
else
|
||||||
systemsVector.push_back(std::make_pair(systemDir, fullname));
|
systemsVector.emplace_back(std::make_pair(systemDir, fullname));
|
||||||
|
|
||||||
if (replaceInfoFile) {
|
if (replaceInfoFile) {
|
||||||
LOG(LogInfo) << "Replaced existing system information file \""
|
LOG(LogInfo) << "Replaced existing system information file \""
|
||||||
|
@ -1019,8 +1022,8 @@ bool SystemData::createSystemDirectories()
|
||||||
// mappings between the system directory names and the full system names. This makes it
|
// mappings between the system directory names and the full system names. This makes it
|
||||||
// easier for the users to identify the correct directories for their games.
|
// easier for the users to identify the correct directories for their games.
|
||||||
if (!systemsVector.empty()) {
|
if (!systemsVector.empty()) {
|
||||||
const std::string systemsFileName = "/systems.txt";
|
const std::string& systemsFileName {"/systems.txt"};
|
||||||
bool systemsFileSuccess = true;
|
bool systemsFileSuccess {true};
|
||||||
|
|
||||||
if (Utils::FileSystem::exists(rompath + systemsFileName)) {
|
if (Utils::FileSystem::exists(rompath + systemsFileName)) {
|
||||||
if (Utils::FileSystem::removeFile(rompath + systemsFileName))
|
if (Utils::FileSystem::removeFile(rompath + systemsFileName))
|
||||||
|
@ -1133,7 +1136,7 @@ std::string SystemData::getThemePath() const
|
||||||
|
|
||||||
SystemData* SystemData::getRandomSystem(const SystemData* currentSystem)
|
SystemData* SystemData::getRandomSystem(const SystemData* currentSystem)
|
||||||
{
|
{
|
||||||
unsigned int total = 0;
|
int total {0};
|
||||||
for (auto it = sSystemVector.cbegin(); it != sSystemVector.cend(); ++it) {
|
for (auto it = sSystemVector.cbegin(); it != sSystemVector.cend(); ++it) {
|
||||||
if ((*it)->isGameSystem())
|
if ((*it)->isGameSystem())
|
||||||
++total;
|
++total;
|
||||||
|
@ -1142,15 +1145,15 @@ SystemData* SystemData::getRandomSystem(const SystemData* currentSystem)
|
||||||
if (total < 2)
|
if (total < 2)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
SystemData* randomSystem = nullptr;
|
SystemData* randomSystem {nullptr};
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Get a random number in range.
|
// Get a random number in range.
|
||||||
std::random_device randDev;
|
std::random_device randDev;
|
||||||
// Mersenne Twister pseudorandom number generator.
|
// Mersenne Twister pseudorandom number generator.
|
||||||
std::mt19937 engine {randDev()};
|
std::mt19937 engine {randDev()};
|
||||||
std::uniform_int_distribution<int> uniform_dist(0, total - 1);
|
std::uniform_int_distribution<int> uniform_dist {0, total - 1};
|
||||||
int target = uniform_dist(engine);
|
int target {uniform_dist(engine)};
|
||||||
|
|
||||||
for (auto it = sSystemVector.cbegin(); it != sSystemVector.cend(); ++it) {
|
for (auto it = sSystemVector.cbegin(); it != sSystemVector.cend(); ++it) {
|
||||||
if ((*it)->isGameSystem()) {
|
if ((*it)->isGameSystem()) {
|
||||||
|
@ -1171,8 +1174,8 @@ SystemData* SystemData::getRandomSystem(const SystemData* currentSystem)
|
||||||
FileData* SystemData::getRandomGame(const FileData* currentGame, bool gameSelectorMode)
|
FileData* SystemData::getRandomGame(const FileData* currentGame, bool gameSelectorMode)
|
||||||
{
|
{
|
||||||
std::vector<FileData*> gameList;
|
std::vector<FileData*> gameList;
|
||||||
bool onlyFolders = false;
|
bool onlyFolders {false};
|
||||||
bool hasFolders = false;
|
bool hasFolders {false};
|
||||||
|
|
||||||
// If we're in the custom collection group list, then get the list of collections,
|
// If we're in the custom collection group list, then get the list of collections,
|
||||||
// otherwise get a list of all the folder and file entries in the view.
|
// otherwise get a list of all the folder and file entries in the view.
|
||||||
|
@ -1203,7 +1206,7 @@ FileData* SystemData::getRandomGame(const FileData* currentGame, bool gameSelect
|
||||||
// If this is a mixed view of folders and files, then remove all the folder entries
|
// If this is a mixed view of folders and files, then remove all the folder entries
|
||||||
// as we want to exclude them from the random selection.
|
// as we want to exclude them from the random selection.
|
||||||
if (!onlyFolders && hasFolders) {
|
if (!onlyFolders && hasFolders) {
|
||||||
unsigned int i = 0;
|
unsigned int i {0};
|
||||||
do {
|
do {
|
||||||
if (gameList[i]->getType() == FOLDER)
|
if (gameList[i]->getType() == FOLDER)
|
||||||
gameList.erase(gameList.begin() + i);
|
gameList.erase(gameList.begin() + i);
|
||||||
|
@ -1222,8 +1225,8 @@ FileData* SystemData::getRandomGame(const FileData* currentGame, bool gameSelect
|
||||||
if (currentGame && currentGame->getType() == PLACEHOLDER)
|
if (currentGame && currentGame->getType() == PLACEHOLDER)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
unsigned int total = static_cast<int>(gameList.size());
|
int total {static_cast<int>(gameList.size())};
|
||||||
int target = 0;
|
int target {0};
|
||||||
|
|
||||||
if (total < 2)
|
if (total < 2)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -1233,7 +1236,7 @@ FileData* SystemData::getRandomGame(const FileData* currentGame, bool gameSelect
|
||||||
std::random_device randDev;
|
std::random_device randDev;
|
||||||
// Mersenne Twister pseudorandom number generator.
|
// Mersenne Twister pseudorandom number generator.
|
||||||
std::mt19937 engine {randDev()};
|
std::mt19937 engine {randDev()};
|
||||||
std::uniform_int_distribution<int> uniform_dist(0, total - 1);
|
std::uniform_int_distribution<int> uniform_dist {0, total - 1};
|
||||||
target = uniform_dist(engine);
|
target = uniform_dist(engine);
|
||||||
} while (currentGame && gameList.at(target) == currentGame);
|
} while (currentGame && gameList.at(target) == currentGame);
|
||||||
|
|
||||||
|
@ -1245,7 +1248,7 @@ void SystemData::sortSystem(bool reloadGamelist, bool jumpToFirstRow)
|
||||||
if (getName() == "recent")
|
if (getName() == "recent")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool favoritesSorting;
|
bool favoritesSorting {false};
|
||||||
|
|
||||||
if (this->isCustomCollection() ||
|
if (this->isCustomCollection() ||
|
||||||
(this->isCollection() && this->getFullName() == "collections")) {
|
(this->isCollection() && this->getFullName() == "collections")) {
|
||||||
|
@ -1255,7 +1258,7 @@ void SystemData::sortSystem(bool reloadGamelist, bool jumpToFirstRow)
|
||||||
favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst");
|
favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst");
|
||||||
}
|
}
|
||||||
|
|
||||||
FileData* rootFolder = getRootFolder();
|
FileData* rootFolder {getRootFolder()};
|
||||||
// Assign the sort type to all grouped custom collections.
|
// Assign the sort type to all grouped custom collections.
|
||||||
if (mIsCollectionSystem && mFullName == "collections") {
|
if (mIsCollectionSystem && mFullName == "collections") {
|
||||||
for (auto it = rootFolder->getChildren().begin(); // Line break.
|
for (auto it = rootFolder->getChildren().begin(); // Line break.
|
||||||
|
@ -1289,7 +1292,7 @@ void SystemData::loadTheme()
|
||||||
{
|
{
|
||||||
mTheme = std::make_shared<ThemeData>();
|
mTheme = std::make_shared<ThemeData>();
|
||||||
|
|
||||||
std::string path {getThemePath()};
|
const std::string& path {getThemePath()};
|
||||||
|
|
||||||
if (!Utils::FileSystem::exists(path)) {
|
if (!Utils::FileSystem::exists(path)) {
|
||||||
// No theme available for this platform.
|
// No theme available for this platform.
|
||||||
|
@ -1364,7 +1367,7 @@ void SystemData::setupSystemSortType(FileData* rootFolder)
|
||||||
{
|
{
|
||||||
// If DefaultSortOrder is set to something, check that it is actually a valid value.
|
// If DefaultSortOrder is set to something, check that it is actually a valid value.
|
||||||
if (Settings::getInstance()->getString("DefaultSortOrder") != "") {
|
if (Settings::getInstance()->getString("DefaultSortOrder") != "") {
|
||||||
for (unsigned int i = 0; i < FileSorts::SortTypes.size(); ++i) {
|
for (unsigned int i {0}; i < FileSorts::SortTypes.size(); ++i) {
|
||||||
if (FileSorts::SortTypes.at(i).description ==
|
if (FileSorts::SortTypes.at(i).description ==
|
||||||
Settings::getInstance()->getString("DefaultSortOrder")) {
|
Settings::getInstance()->getString("DefaultSortOrder")) {
|
||||||
rootFolder->setSortTypeString(
|
rootFolder->setSortTypeString(
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
GuiGamelistOptions::GuiGamelistOptions(SystemData* system)
|
GuiGamelistOptions::GuiGamelistOptions(SystemData* system)
|
||||||
: mMenu {"OPTIONS"}
|
: mMenu {"GAMELIST OPTIONS"}
|
||||||
, mSystem {system}
|
, mSystem {system}
|
||||||
, mFiltersChanged {false}
|
, mFiltersChanged {false}
|
||||||
, mCancelled {false}
|
, mCancelled {false}
|
||||||
|
@ -403,29 +403,29 @@ void GuiGamelistOptions::openMetaDataEd()
|
||||||
clearGameBtnFunc = [this, file] {
|
clearGameBtnFunc = [this, file] {
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
if (file->getType() == FOLDER) {
|
if (file->getType() == FOLDER) {
|
||||||
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the folder \""
|
LOG(LogInfo) << "Deleting media files and gamelist.xml entry for the folder \""
|
||||||
<< Utils::String::replace(file->getFullPath(), "/", "\\") << "\"";
|
<< Utils::String::replace(file->getFullPath(), "/", "\\") << "\"";
|
||||||
}
|
}
|
||||||
else if (file->getType() == GAME && Utils::FileSystem::isDirectory(file->getFullPath())) {
|
else if (file->getType() == GAME && Utils::FileSystem::isDirectory(file->getFullPath())) {
|
||||||
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the "
|
LOG(LogInfo) << "Deleting media files and gamelist.xml entry for the "
|
||||||
"file-interpreted folder \""
|
"file-interpreted folder \""
|
||||||
<< Utils::String::replace(file->getFullPath(), "/", "\\") << "\"";
|
<< Utils::String::replace(file->getFullPath(), "/", "\\") << "\"";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the file \""
|
LOG(LogInfo) << "Deleting media files and gamelist.xml entry for the file \""
|
||||||
<< Utils::String::replace(file->getFullPath(), "/", "\\") << "\"";
|
<< Utils::String::replace(file->getFullPath(), "/", "\\") << "\"";
|
||||||
#else
|
#else
|
||||||
if (file->getType() == FOLDER) {
|
if (file->getType() == FOLDER) {
|
||||||
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the folder \""
|
LOG(LogInfo) << "Deleting media files and gamelist.xml entry for the folder \""
|
||||||
<< file->getFullPath() << "\"";
|
<< file->getFullPath() << "\"";
|
||||||
}
|
}
|
||||||
else if (file->getType() == GAME && Utils::FileSystem::isDirectory(file->getFullPath())) {
|
else if (file->getType() == GAME && Utils::FileSystem::isDirectory(file->getFullPath())) {
|
||||||
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the "
|
LOG(LogInfo) << "Deleting media files and gamelist.xml entry for the "
|
||||||
"file-interpreted folder \""
|
"file-interpreted folder \""
|
||||||
<< file->getFullPath() << "\"";
|
<< file->getFullPath() << "\"";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the file \""
|
LOG(LogInfo) << "Deleting media files and gamelist.xml entry for the file \""
|
||||||
<< file->getFullPath() << "\"";
|
<< file->getFullPath() << "\"";
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -473,8 +473,8 @@ void GuiGamelistOptions::openMetaDataEd()
|
||||||
};
|
};
|
||||||
|
|
||||||
deleteGameBtnFunc = [this, file] {
|
deleteGameBtnFunc = [this, file] {
|
||||||
LOG(LogInfo) << "Deleting the game file \"" << file->getFullPath()
|
LOG(LogInfo) << "Deleting game file \"" << file->getFullPath()
|
||||||
<< "\", all its media files and its gamelist.xml entry.";
|
<< "\", all its media files and its gamelist.xml entry";
|
||||||
CollectionSystemsManager::getInstance()->deleteCollectionFiles(file);
|
CollectionSystemsManager::getInstance()->deleteCollectionFiles(file);
|
||||||
ViewController::getInstance()->getGamelistView(file->getSystem()).get()->removeMedia(file);
|
ViewController::getInstance()->getGamelistView(file->getSystem()).get()->removeMedia(file);
|
||||||
ViewController::getInstance()->getGamelistView(file->getSystem()).get()->remove(file, true);
|
ViewController::getInstance()->getGamelistView(file->getSystem()).get()->remove(file, true);
|
||||||
|
|
|
@ -119,7 +119,7 @@ void GuiMenu::openUIOptions()
|
||||||
|
|
||||||
for (auto it = themeSets.cbegin(); it != themeSets.cend(); ++it) {
|
for (auto it = themeSets.cbegin(); it != themeSets.cend(); ++it) {
|
||||||
// If required, abbreviate the theme set name so it doesn't overlap the setting name.
|
// If required, abbreviate the theme set name so it doesn't overlap the setting name.
|
||||||
float maxNameLength = mSize.x * 0.62f;
|
const float maxNameLength = mSize.x * 0.62f;
|
||||||
themeSet->add(it->first, it->first, it == selectedSet, maxNameLength);
|
themeSet->add(it->first, it->first, it == selectedSet, maxNameLength);
|
||||||
}
|
}
|
||||||
s->addWithLabel("THEME SET", themeSet);
|
s->addWithLabel("THEME SET", themeSet);
|
||||||
|
@ -176,7 +176,7 @@ void GuiMenu::openUIOptions()
|
||||||
if (variant.selectable) {
|
if (variant.selectable) {
|
||||||
// If required, abbreviate the variant name so it doesn't overlap the
|
// If required, abbreviate the variant name so it doesn't overlap the
|
||||||
// setting name.
|
// setting name.
|
||||||
float maxNameLength {mSize.x * 0.62f};
|
const float maxNameLength {mSize.x * 0.62f};
|
||||||
themeVariant->add(variant.label, variant.name, variant.name == selectedVariant,
|
themeVariant->add(variant.label, variant.name, variant.name == selectedVariant,
|
||||||
maxNameLength);
|
maxNameLength);
|
||||||
}
|
}
|
||||||
|
@ -226,7 +226,7 @@ void GuiMenu::openUIOptions()
|
||||||
for (auto& colorScheme : currentSet->second.capabilities.colorSchemes) {
|
for (auto& colorScheme : currentSet->second.capabilities.colorSchemes) {
|
||||||
// If required, abbreviate the color scheme name so it doesn't overlap the
|
// If required, abbreviate the color scheme name so it doesn't overlap the
|
||||||
// setting name.
|
// setting name.
|
||||||
float maxNameLength {mSize.x * 0.52f};
|
const float maxNameLength {mSize.x * 0.52f};
|
||||||
themeColorScheme->add(colorScheme.label, colorScheme.name,
|
themeColorScheme->add(colorScheme.label, colorScheme.name,
|
||||||
colorScheme.name == selectedColorScheme, maxNameLength);
|
colorScheme.name == selectedColorScheme, maxNameLength);
|
||||||
}
|
}
|
||||||
|
@ -298,12 +298,12 @@ void GuiMenu::openUIOptions()
|
||||||
auto gamelistViewStyle = std::make_shared<OptionListComponent<std::string>>(
|
auto gamelistViewStyle = std::make_shared<OptionListComponent<std::string>>(
|
||||||
getHelpStyle(), "LEGACY GAMELIST VIEW STYLE", false);
|
getHelpStyle(), "LEGACY GAMELIST VIEW STYLE", false);
|
||||||
std::string selectedViewStyle {Settings::getInstance()->getString("GamelistViewStyle")};
|
std::string selectedViewStyle {Settings::getInstance()->getString("GamelistViewStyle")};
|
||||||
gamelistViewStyle->add("automatic", "automatic", selectedViewStyle == "automatic");
|
gamelistViewStyle->add("AUTOMATIC", "automatic", selectedViewStyle == "automatic");
|
||||||
gamelistViewStyle->add("basic", "basic", selectedViewStyle == "basic");
|
gamelistViewStyle->add("BASIC", "basic", selectedViewStyle == "basic");
|
||||||
gamelistViewStyle->add("detailed", "detailed", selectedViewStyle == "detailed");
|
gamelistViewStyle->add("DETAILED", "detailed", selectedViewStyle == "detailed");
|
||||||
gamelistViewStyle->add("video", "video", selectedViewStyle == "video");
|
gamelistViewStyle->add("VIDEO", "video", selectedViewStyle == "video");
|
||||||
// If there are no objects returned, then there must be a manually modified entry in the
|
// If there are no objects returned, then there must be a manually modified entry in the
|
||||||
// configuration file. Simply set the view style to Automatic in this case.
|
// configuration file. Simply set the view style to "automatic" in this case.
|
||||||
if (gamelistViewStyle->getSelectedObjects().size() == 0)
|
if (gamelistViewStyle->getSelectedObjects().size() == 0)
|
||||||
gamelistViewStyle->selectEntry(0);
|
gamelistViewStyle->selectEntry(0);
|
||||||
s->addWithLabel("LEGACY GAMELIST VIEW STYLE", gamelistViewStyle);
|
s->addWithLabel("LEGACY GAMELIST VIEW STYLE", gamelistViewStyle);
|
||||||
|
@ -337,20 +337,43 @@ void GuiMenu::openUIOptions()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Quick system select (navigate between systems in the gamelist view).
|
||||||
|
auto quickSystemSelect = std::make_shared<OptionListComponent<std::string>>(
|
||||||
|
getHelpStyle(), "QUICK SYSTEM SELECT", false);
|
||||||
|
std::string selectedQuickSelect {Settings::getInstance()->getString("QuickSystemSelect")};
|
||||||
|
quickSystemSelect->add("LEFT/RIGHT OR SHOULDERS", "leftrightshoulders",
|
||||||
|
selectedQuickSelect == "leftrightshoulders");
|
||||||
|
quickSystemSelect->add("LEFT/RIGHT OR TRIGGERS", "leftrighttriggers",
|
||||||
|
selectedQuickSelect == "leftrighttriggers");
|
||||||
|
quickSystemSelect->add("SHOULDERS", "shoulders", selectedQuickSelect == "shoulders");
|
||||||
|
quickSystemSelect->add("TRIGGERS", "triggers", selectedQuickSelect == "triggers");
|
||||||
|
quickSystemSelect->add("LEFT/RIGHT", "leftright", selectedQuickSelect == "leftright");
|
||||||
|
quickSystemSelect->add("DISABLED", "disabled", selectedQuickSelect == "disabled");
|
||||||
|
// If there are no objects returned, then there must be a manually modified entry in the
|
||||||
|
// configuration file. Simply set the quick system select to "leftrightshoulders" in this case.
|
||||||
|
if (quickSystemSelect->getSelectedObjects().size() == 0)
|
||||||
|
quickSystemSelect->selectEntry(0);
|
||||||
|
s->addWithLabel("QUICK SYSTEM SELECT", quickSystemSelect);
|
||||||
|
s->addSaveFunc([quickSystemSelect, s] {
|
||||||
|
if (quickSystemSelect->getSelected() !=
|
||||||
|
Settings::getInstance()->getString("QuickSystemSelect")) {
|
||||||
|
Settings::getInstance()->setString("QuickSystemSelect",
|
||||||
|
quickSystemSelect->getSelected());
|
||||||
|
s->setNeedsSaving();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Optionally start in selected system/gamelist.
|
// Optionally start in selected system/gamelist.
|
||||||
auto startupSystem = std::make_shared<OptionListComponent<std::string>>(
|
auto startupSystem = std::make_shared<OptionListComponent<std::string>>(
|
||||||
getHelpStyle(), "GAMELIST ON STARTUP", false);
|
getHelpStyle(), "GAMELIST ON STARTUP", false);
|
||||||
startupSystem->add("NONE", "", Settings::getInstance()->getString("StartupSystem") == "");
|
startupSystem->add("NONE", "", Settings::getInstance()->getString("StartupSystem") == "");
|
||||||
for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
|
for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
|
||||||
it != SystemData::sSystemVector.cend(); ++it) {
|
it != SystemData::sSystemVector.cend(); ++it) {
|
||||||
if ((*it)->getName() != "retropie") {
|
// If required, abbreviate the system name so it doesn't overlap the setting name.
|
||||||
// If required, abbreviate the system name so it doesn't overlap the setting name.
|
float maxNameLength {mSize.x * 0.51f};
|
||||||
float maxNameLength {mSize.x * 0.51f};
|
startupSystem->add((*it)->getFullName(), (*it)->getName(),
|
||||||
startupSystem->add((*it)->getFullName(), (*it)->getName(),
|
Settings::getInstance()->getString("StartupSystem") == (*it)->getName(),
|
||||||
Settings::getInstance()->getString("StartupSystem") ==
|
maxNameLength);
|
||||||
(*it)->getName(),
|
|
||||||
maxNameLength);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// This can probably not happen but as an extra precaution select the "NONE" entry if no
|
// This can probably not happen but as an extra precaution select the "NONE" entry if no
|
||||||
// entry is selected.
|
// entry is selected.
|
||||||
|
@ -686,18 +709,6 @@ void GuiMenu::openUIOptions()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Quick system select (navigate left/right in gamelist view).
|
|
||||||
auto quickSystemSelect = std::make_shared<SwitchComponent>();
|
|
||||||
quickSystemSelect->setState(Settings::getInstance()->getBool("QuickSystemSelect"));
|
|
||||||
s->addWithLabel("ENABLE QUICK SYSTEM SELECT", quickSystemSelect);
|
|
||||||
s->addSaveFunc([quickSystemSelect, s] {
|
|
||||||
if (Settings::getInstance()->getBool("QuickSystemSelect") !=
|
|
||||||
quickSystemSelect->getState()) {
|
|
||||||
Settings::getInstance()->setBool("QuickSystemSelect", quickSystemSelect->getState());
|
|
||||||
s->setNeedsSaving();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// On-screen help prompts.
|
// On-screen help prompts.
|
||||||
auto showHelpPrompts = std::make_shared<SwitchComponent>();
|
auto showHelpPrompts = std::make_shared<SwitchComponent>();
|
||||||
showHelpPrompts->setState(Settings::getInstance()->getBool("ShowHelpPrompts"));
|
showHelpPrompts->setState(Settings::getInstance()->getBool("ShowHelpPrompts"));
|
||||||
|
|
|
@ -149,8 +149,7 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md,
|
||||||
case MD_BOOL: {
|
case MD_BOOL: {
|
||||||
ed = std::make_shared<SwitchComponent>();
|
ed = std::make_shared<SwitchComponent>();
|
||||||
// Make the switches slightly smaller.
|
// Make the switches slightly smaller.
|
||||||
glm::vec2 switchSize {ed->getSize() * 0.9f};
|
ed->setSize(glm::ceil(ed->getSize() * 0.9f));
|
||||||
ed->setResize(ceilf(switchSize.x), switchSize.y);
|
|
||||||
|
|
||||||
ed->setChangedColor(ICONCOLOR_USERMARKED);
|
ed->setChangedColor(ICONCOLOR_USERMARKED);
|
||||||
row.addElement(ed, false, true);
|
row.addElement(ed, false, true);
|
||||||
|
|
|
@ -139,7 +139,7 @@ GuiScraperMenu::GuiScraperMenu(std::string title)
|
||||||
|
|
||||||
addChild(&mMenu);
|
addChild(&mMenu);
|
||||||
|
|
||||||
mMenu.addButton("START", "start", std::bind(&GuiScraperMenu::pressedStart, this));
|
mMenu.addButton("START", "start scraper", std::bind(&GuiScraperMenu::pressedStart, this));
|
||||||
mMenu.addButton("BACK", "back", [&] { delete this; });
|
mMenu.addButton("BACK", "back", [&] { delete this; });
|
||||||
|
|
||||||
setSize(mMenu.getSize());
|
setSize(mMenu.getSize());
|
||||||
|
@ -252,27 +252,28 @@ void GuiScraperMenu::openContentOptions()
|
||||||
->setOpacity(DISABLED_OPACITY);
|
->setOpacity(DISABLED_OPACITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scrape controllers (arcade systems only).
|
// ScreenScraper controller scraping is currently broken, it's unclear if they will fix it.
|
||||||
auto scrapeControllers = std::make_shared<SwitchComponent>();
|
// // Scrape controllers (arcade systems only).
|
||||||
scrapeControllers->setState(Settings::getInstance()->getBool("ScrapeControllers"));
|
// auto scrapeControllers = std::make_shared<SwitchComponent>();
|
||||||
s->addWithLabel("CONTROLLERS (ARCADE SYSTEMS ONLY)", scrapeControllers);
|
// scrapeControllers->setState(Settings::getInstance()->getBool("ScrapeControllers"));
|
||||||
s->addSaveFunc([scrapeControllers, s] {
|
// s->addWithLabel("CONTROLLERS (ARCADE SYSTEMS ONLY)", scrapeControllers);
|
||||||
if (scrapeControllers->getState() !=
|
// s->addSaveFunc([scrapeControllers, s] {
|
||||||
Settings::getInstance()->getBool("ScrapeControllers")) {
|
// if (scrapeControllers->getState() !=
|
||||||
Settings::getInstance()->setBool("ScrapeControllers", scrapeControllers->getState());
|
// Settings::getInstance()->getBool("ScrapeControllers")) {
|
||||||
s->setNeedsSaving();
|
// Settings::getInstance()->setBool("ScrapeControllers", scrapeControllers->getState());
|
||||||
}
|
// s->setNeedsSaving();
|
||||||
});
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
// Controllers are not supported by TheGamesDB, so gray out the option if this scraper is
|
// // Controllers are not supported by TheGamesDB, so gray out the option if this scraper is
|
||||||
// selected.
|
// // selected.
|
||||||
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
|
// if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
|
||||||
scrapeControllers->setEnabled(false);
|
// scrapeControllers->setEnabled(false);
|
||||||
scrapeControllers->setOpacity(DISABLED_OPACITY);
|
// scrapeControllers->setOpacity(DISABLED_OPACITY);
|
||||||
scrapeControllers->getParent()
|
// scrapeControllers->getParent()
|
||||||
->getChild(scrapeControllers->getChildIndex() - 1)
|
// ->getChild(scrapeControllers->getChildIndex() - 1)
|
||||||
->setOpacity(DISABLED_OPACITY);
|
// ->setOpacity(DISABLED_OPACITY);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Scrape other metadata.
|
// Scrape other metadata.
|
||||||
auto scrapeMetadata = std::make_shared<SwitchComponent>();
|
auto scrapeMetadata = std::make_shared<SwitchComponent>();
|
||||||
|
@ -1059,11 +1060,12 @@ void GuiScraperMenu::start()
|
||||||
contentToScrape = true;
|
contentToScrape = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (scraperService == "screenscraper" &&
|
// ScreenScraper controller scraping is currently broken, it's unclear if they will fix it.
|
||||||
Settings::getInstance()->getBool("ScrapeControllers")) {
|
// if (scraperService == "screenscraper" &&
|
||||||
contentToScrape = true;
|
// Settings::getInstance()->getBool("ScrapeControllers")) {
|
||||||
break;
|
// contentToScrape = true;
|
||||||
}
|
// break;
|
||||||
|
// }
|
||||||
if (Settings::getInstance()->getBool("ScrapeMetadata")) {
|
if (Settings::getInstance()->getBool("ScrapeMetadata")) {
|
||||||
contentToScrape = true;
|
contentToScrape = true;
|
||||||
break;
|
break;
|
||||||
|
@ -1192,6 +1194,6 @@ std::vector<HelpPrompt> GuiScraperMenu::getHelpPrompts()
|
||||||
{
|
{
|
||||||
std::vector<HelpPrompt> prompts {mMenu.getHelpPrompts()};
|
std::vector<HelpPrompt> prompts {mMenu.getHelpPrompts()};
|
||||||
prompts.push_back(HelpPrompt("b", "back"));
|
prompts.push_back(HelpPrompt("b", "back"));
|
||||||
prompts.push_back(HelpPrompt("y", "start"));
|
prompts.push_back(HelpPrompt("y", "start scraper"));
|
||||||
return prompts;
|
return prompts;
|
||||||
}
|
}
|
||||||
|
|
|
@ -966,9 +966,10 @@ bool GuiScraperSearch::saveMetadata(const ScraperSearchResult& result,
|
||||||
if (key == "rating" && !Settings::getInstance()->getBool("ScrapeRatings"))
|
if (key == "rating" && !Settings::getInstance()->getBool("ScrapeRatings"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Skip saving of controller metadata if the corresponding option has been set to false.
|
// ScreenScraper controller scraping is currently broken, it's unclear if they will fix it.
|
||||||
if (key == "controller" && !Settings::getInstance()->getBool("ScrapeControllers"))
|
// // Skip saving of controller metadata if the corresponding option has been set to false.
|
||||||
continue;
|
// if (key == "controller" && !Settings::getInstance()->getBool("ScrapeControllers"))
|
||||||
|
// continue;
|
||||||
|
|
||||||
// Skip saving of game name if the corresponding option has been set to false.
|
// Skip saving of game name if the corresponding option has been set to false.
|
||||||
if (key == "name" && !Settings::getInstance()->getBool("ScrapeGameNames"))
|
if (key == "name" && !Settings::getInstance()->getBool("ScrapeGameNames"))
|
||||||
|
|
|
@ -471,93 +471,97 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc,
|
||||||
if (result.platformIDs.empty())
|
if (result.platformIDs.empty())
|
||||||
result.platformIDs.push_back(PlatformId::PLATFORM_UNKNOWN);
|
result.platformIDs.push_back(PlatformId::PLATFORM_UNKNOWN);
|
||||||
|
|
||||||
// Controller (only for the Arcade and SNK Neo Geo systems).
|
// ScreenScraper controller scraping is currently broken, it's unclear if they will fix it.
|
||||||
if (parentPlatformID == 75 || parentPlatformID == 142) {
|
// // Controller (only for the Arcade and SNK Neo Geo systems).
|
||||||
std::string controller = Utils::String::toLower(game.child("controles").text().get());
|
// if (parentPlatformID == 75 || parentPlatformID == 142) {
|
||||||
if (!controller.empty()) {
|
// std::string controller {Utils::String::toLower(game.child("controles").text().get())};
|
||||||
std::string controllerDescription = "Other";
|
//
|
||||||
// Place the steering wheel entry first as some games support both joysticks and
|
// LOG(LogError) << controller;
|
||||||
// and steering wheels and it's likely more interesting to capture the steering
|
//
|
||||||
// wheel option in this case.
|
// if (!controller.empty()) {
|
||||||
if (controller.find("steering wheel") != std::string::npos ||
|
// std::string controllerDescription = "Other";
|
||||||
controller.find("paddle") != std::string::npos ||
|
// // Place the steering wheel entry first as some games support both joysticks and
|
||||||
controller.find("pedal") != std::string::npos) {
|
// // and steering wheels and it's likely more interesting to capture the steering
|
||||||
result.mdl.set("controller", "steering_wheel_generic");
|
// // wheel option in this case.
|
||||||
controllerDescription = "Steering wheel";
|
// if (controller.find("steering wheel") != std::string::npos ||
|
||||||
}
|
// controller.find("paddle") != std::string::npos ||
|
||||||
else if (controller.find("control type=\"joy") != std::string::npos ||
|
// controller.find("pedal") != std::string::npos) {
|
||||||
controller.find("joystick") != std::string::npos) {
|
// result.mdl.set("controller", "steering_wheel_generic");
|
||||||
std::string buttonEntry;
|
// controllerDescription = "Steering wheel";
|
||||||
std::string buttonCount;
|
// }
|
||||||
if (controller.find("p1numbuttons=") != std::string::npos)
|
// else if (controller.find("control type=\"joy") != std::string::npos ||
|
||||||
buttonEntry = controller.substr(controller.find("p1numbuttons=") + 13, 4);
|
// controller.find("joystick") != std::string::npos) {
|
||||||
else if (controller.find("buttons=") != std::string::npos)
|
// std::string buttonEntry;
|
||||||
buttonEntry = controller.substr(controller.find("buttons=") + 8, 5);
|
// std::string buttonCount;
|
||||||
|
// if (controller.find("p1numbuttons=") != std::string::npos)
|
||||||
bool foundDigit = false;
|
// buttonEntry = controller.substr(controller.find("p1numbuttons=") + 13, 4);
|
||||||
for (unsigned char character : buttonEntry) {
|
// else if (controller.find("buttons=") != std::string::npos)
|
||||||
if (std::isdigit(character)) {
|
// buttonEntry = controller.substr(controller.find("buttons=") + 8, 5);
|
||||||
buttonCount.push_back(character);
|
//
|
||||||
foundDigit = true;
|
// bool foundDigit = false;
|
||||||
}
|
// for (unsigned char character : buttonEntry) {
|
||||||
else if (foundDigit == true) {
|
// if (std::isdigit(character)) {
|
||||||
break;
|
// buttonCount.push_back(character);
|
||||||
}
|
// foundDigit = true;
|
||||||
}
|
// }
|
||||||
|
// else if (foundDigit == true) {
|
||||||
if (buttonCount == "0") {
|
// break;
|
||||||
result.mdl.set("controller", "joystick_arcade_no_buttons");
|
// }
|
||||||
controllerDescription = "Joystick (no buttons)";
|
// }
|
||||||
}
|
//
|
||||||
else if (buttonCount == "1") {
|
// if (buttonCount == "0") {
|
||||||
result.mdl.set("controller", "joystick_arcade_1_button");
|
// result.mdl.set("controller", "joystick_arcade_no_buttons");
|
||||||
controllerDescription = "Joystick (1 button)";
|
// controllerDescription = "Joystick (no buttons)";
|
||||||
}
|
// }
|
||||||
else if (buttonCount == "2") {
|
// else if (buttonCount == "1") {
|
||||||
result.mdl.set("controller", "joystick_arcade_2_buttons");
|
// result.mdl.set("controller", "joystick_arcade_1_button");
|
||||||
controllerDescription = "Joystick (2 buttons)";
|
// controllerDescription = "Joystick (1 button)";
|
||||||
}
|
// }
|
||||||
else if (buttonCount == "3") {
|
// else if (buttonCount == "2") {
|
||||||
result.mdl.set("controller", "joystick_arcade_3_buttons");
|
// result.mdl.set("controller", "joystick_arcade_2_buttons");
|
||||||
controllerDescription = "Joystick (3 buttons)";
|
// controllerDescription = "Joystick (2 buttons)";
|
||||||
}
|
// }
|
||||||
else if (buttonCount == "4") {
|
// else if (buttonCount == "3") {
|
||||||
result.mdl.set("controller", "joystick_arcade_4_buttons");
|
// result.mdl.set("controller", "joystick_arcade_3_buttons");
|
||||||
controllerDescription = "Joystick (4 buttons)";
|
// controllerDescription = "Joystick (3 buttons)";
|
||||||
}
|
// }
|
||||||
else if (buttonCount == "5") {
|
// else if (buttonCount == "4") {
|
||||||
result.mdl.set("controller", "joystick_arcade_5_buttons");
|
// result.mdl.set("controller", "joystick_arcade_4_buttons");
|
||||||
controllerDescription = "Joystick (5 buttons)";
|
// controllerDescription = "Joystick (4 buttons)";
|
||||||
}
|
// }
|
||||||
else if (buttonCount == "6") {
|
// else if (buttonCount == "5") {
|
||||||
result.mdl.set("controller", "joystick_arcade_6_buttons");
|
// result.mdl.set("controller", "joystick_arcade_5_buttons");
|
||||||
controllerDescription = "Joystick (6 buttons)";
|
// controllerDescription = "Joystick (5 buttons)";
|
||||||
}
|
// }
|
||||||
else {
|
// else if (buttonCount == "6") {
|
||||||
controllerDescription = "Joystick (other)";
|
// result.mdl.set("controller", "joystick_arcade_6_buttons");
|
||||||
}
|
// controllerDescription = "Joystick (6 buttons)";
|
||||||
}
|
// }
|
||||||
else if (controller.find("spinner") != std::string::npos) {
|
// else {
|
||||||
result.mdl.set("controller", "spinner_generic");
|
// controllerDescription = "Joystick (other)";
|
||||||
controllerDescription = "Spinner";
|
// }
|
||||||
}
|
// }
|
||||||
else if (controller.find("trackball") != std::string::npos) {
|
// else if (controller.find("spinner") != std::string::npos) {
|
||||||
result.mdl.set("controller", "trackball_generic");
|
// result.mdl.set("controller", "spinner_generic");
|
||||||
controllerDescription = "Trackball";
|
// controllerDescription = "Spinner";
|
||||||
}
|
// }
|
||||||
else if (controller.find("gun") != std::string::npos) {
|
// else if (controller.find("trackball") != std::string::npos) {
|
||||||
result.mdl.set("controller", "lightgun_generic");
|
// result.mdl.set("controller", "trackball_generic");
|
||||||
controllerDescription = "Lightgun";
|
// controllerDescription = "Trackball";
|
||||||
}
|
// }
|
||||||
else if (controller.find("stick") != std::string::npos) {
|
// else if (controller.find("gun") != std::string::npos) {
|
||||||
result.mdl.set("controller", "flight_stick_generic");
|
// result.mdl.set("controller", "lightgun_generic");
|
||||||
controllerDescription = "Flight stick";
|
// controllerDescription = "Lightgun";
|
||||||
}
|
// }
|
||||||
|
// else if (controller.find("stick") != std::string::npos) {
|
||||||
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Controller: "
|
// result.mdl.set("controller", "flight_stick_generic");
|
||||||
<< controllerDescription;
|
// controllerDescription = "Flight stick";
|
||||||
}
|
// }
|
||||||
}
|
//
|
||||||
|
// LOG(LogDebug) << "ScreenScraperRequest::processGame(): Controller: "
|
||||||
|
// << controllerDescription;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// Media super-node.
|
// Media super-node.
|
||||||
pugi::xml_node media_list = game.child("medias");
|
pugi::xml_node media_list = game.child("medias");
|
||||||
|
|
|
@ -196,8 +196,7 @@ bool GamelistBase::input(InputConfig* config, Input input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (config->isMappedLike(getQuickSystemSelectRightButton(), input)) {
|
else if (config->isMappedLike(getQuickSystemSelectRightButton(), input)) {
|
||||||
if (mLeftRightAvailable && Settings::getInstance()->getBool("QuickSystemSelect") &&
|
if (SystemData::sSystemVector.size() > 1) {
|
||||||
SystemData::sSystemVector.size() > 1) {
|
|
||||||
muteViewVideos();
|
muteViewVideos();
|
||||||
onFocusLost();
|
onFocusLost();
|
||||||
stopListScrolling();
|
stopListScrolling();
|
||||||
|
@ -207,8 +206,7 @@ bool GamelistBase::input(InputConfig* config, Input input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (config->isMappedLike(getQuickSystemSelectLeftButton(), input)) {
|
else if (config->isMappedLike(getQuickSystemSelectLeftButton(), input)) {
|
||||||
if (mLeftRightAvailable && Settings::getInstance()->getBool("QuickSystemSelect") &&
|
if (SystemData::sSystemVector.size() > 1) {
|
||||||
SystemData::sSystemVector.size() > 1) {
|
|
||||||
muteViewVideos();
|
muteViewVideos();
|
||||||
onFocusLost();
|
onFocusLost();
|
||||||
stopListScrolling();
|
stopListScrolling();
|
||||||
|
@ -435,11 +433,18 @@ bool GamelistBase::input(InputConfig* config, Input input)
|
||||||
}
|
}
|
||||||
else if (CollectionSystemsManager::getInstance()->toggleGameInCollection(
|
else if (CollectionSystemsManager::getInstance()->toggleGameInCollection(
|
||||||
entryToUpdate)) {
|
entryToUpdate)) {
|
||||||
|
// Needed to avoid some minor transition animation glitches.
|
||||||
|
auto grid =
|
||||||
|
ViewController::getInstance()->getGamelistView(system).get()->mGrid.get();
|
||||||
|
if (grid != nullptr)
|
||||||
|
grid->setSuppressTransitions(true);
|
||||||
|
|
||||||
// As the toggling of the game destroyed this object, we need to get the view
|
// As the toggling of the game destroyed this object, we need to get the view
|
||||||
// from ViewController instead of using the reference that existed before the
|
// from ViewController instead of using the reference that existed before the
|
||||||
// destruction. Otherwise we get random crashes.
|
// destruction. Otherwise we get random crashes.
|
||||||
GamelistView* view {
|
GamelistView* view {
|
||||||
ViewController::getInstance()->getGamelistView(system).get()};
|
ViewController::getInstance()->getGamelistView(system).get()};
|
||||||
|
|
||||||
// Jump to the first entry in the gamelist if the last favorite was unmarked.
|
// Jump to the first entry in the gamelist if the last favorite was unmarked.
|
||||||
if (foldersOnTop && removedLastFavorite &&
|
if (foldersOnTop && removedLastFavorite &&
|
||||||
!entryToUpdate->getSystem()->isCustomCollection()) {
|
!entryToUpdate->getSystem()->isCustomCollection()) {
|
||||||
|
@ -457,6 +462,10 @@ bool GamelistBase::input(InputConfig* config, Input input)
|
||||||
else if (selectLastEntry && view->getPrimary()->size() > 0) {
|
else if (selectLastEntry && view->getPrimary()->size() > 0) {
|
||||||
view->setCursor(view->getLastEntry());
|
view->setCursor(view->getLastEntry());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (grid != nullptr)
|
||||||
|
grid->setSuppressTransitions(false);
|
||||||
|
|
||||||
// Display the indication icons which show what games are part of the
|
// Display the indication icons which show what games are part of the
|
||||||
// custom collection currently being edited. This is done cheaply using
|
// custom collection currently being edited. This is done cheaply using
|
||||||
// onFileChanged() which will trigger populateList().
|
// onFileChanged() which will trigger populateList().
|
||||||
|
@ -568,15 +577,19 @@ void GamelistBase::populateList(const std::vector<FileData*>& files, FileData* f
|
||||||
|
|
||||||
auto theme = mRoot->getSystem()->getTheme();
|
auto theme = mRoot->getSystem()->getTheme();
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string carouselItemType;
|
std::string defaultImage;
|
||||||
std::string carouselDefaultItem;
|
|
||||||
|
|
||||||
if (mCarousel != nullptr) {
|
if (mCarousel != nullptr) {
|
||||||
carouselItemType = mCarousel->getItemType();
|
defaultImage = mCarousel->getDefaultImage();
|
||||||
carouselDefaultItem = mCarousel->getDefaultItem();
|
if (!ResourceManager::getInstance().fileExists(defaultImage))
|
||||||
if (!ResourceManager::getInstance().fileExists(carouselDefaultItem))
|
defaultImage = "";
|
||||||
carouselDefaultItem = "";
|
|
||||||
}
|
}
|
||||||
|
else if (mGrid != nullptr) {
|
||||||
|
defaultImage = mGrid->getDefaultImage();
|
||||||
|
if (!ResourceManager::getInstance().fileExists(defaultImage))
|
||||||
|
defaultImage = "";
|
||||||
|
}
|
||||||
|
|
||||||
if (files.size() > 0) {
|
if (files.size() > 0) {
|
||||||
for (auto it = files.cbegin(); it != files.cend(); ++it) {
|
for (auto it = files.cbegin(); it != files.cend(); ++it) {
|
||||||
|
|
||||||
|
@ -593,8 +606,6 @@ void GamelistBase::populateList(const std::vector<FileData*>& files, FileData* f
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCarousel != nullptr) {
|
if (mCarousel != nullptr) {
|
||||||
assert(carouselItemType != "");
|
|
||||||
|
|
||||||
CarouselComponent<FileData*>::Entry carouselEntry;
|
CarouselComponent<FileData*>::Entry carouselEntry;
|
||||||
carouselEntry.name = (*it)->getName();
|
carouselEntry.name = (*it)->getName();
|
||||||
carouselEntry.object = *it;
|
carouselEntry.object = *it;
|
||||||
|
@ -606,13 +617,29 @@ void GamelistBase::populateList(const std::vector<FileData*>& files, FileData* f
|
||||||
else if (letterCase == LetterCase::CAPITALIZED)
|
else if (letterCase == LetterCase::CAPITALIZED)
|
||||||
carouselEntry.name = Utils::String::toCapitalized(carouselEntry.name);
|
carouselEntry.name = Utils::String::toCapitalized(carouselEntry.name);
|
||||||
|
|
||||||
if (carouselDefaultItem != "")
|
if (defaultImage != "")
|
||||||
carouselEntry.data.defaultItemPath = carouselDefaultItem;
|
carouselEntry.data.defaultImagePath = defaultImage;
|
||||||
|
|
||||||
mCarousel->addEntry(carouselEntry, theme);
|
mCarousel->addEntry(carouselEntry, theme);
|
||||||
}
|
}
|
||||||
|
else if (mGrid != nullptr) {
|
||||||
|
GridComponent<FileData*>::Entry gridEntry;
|
||||||
|
gridEntry.name = (*it)->getName();
|
||||||
|
gridEntry.object = *it;
|
||||||
|
|
||||||
if (mTextList != nullptr) {
|
if (letterCase == LetterCase::UPPERCASE)
|
||||||
|
gridEntry.name = Utils::String::toUpper(gridEntry.name);
|
||||||
|
else if (letterCase == LetterCase::LOWERCASE)
|
||||||
|
gridEntry.name = Utils::String::toLower(gridEntry.name);
|
||||||
|
else if (letterCase == LetterCase::CAPITALIZED)
|
||||||
|
gridEntry.name = Utils::String::toCapitalized(gridEntry.name);
|
||||||
|
|
||||||
|
if (defaultImage != "")
|
||||||
|
gridEntry.data.defaultImagePath = defaultImage;
|
||||||
|
|
||||||
|
mGrid->addEntry(gridEntry, theme);
|
||||||
|
}
|
||||||
|
else if (mTextList != nullptr) {
|
||||||
TextListComponent<FileData*>::Entry textListEntry;
|
TextListComponent<FileData*>::Entry textListEntry;
|
||||||
std::string indicators {mTextList->getIndicators()};
|
std::string indicators {mTextList->getIndicators()};
|
||||||
std::string collectionIndicators {mTextList->getCollectionIndicators()};
|
std::string collectionIndicators {mTextList->getCollectionIndicators()};
|
||||||
|
@ -685,6 +712,9 @@ void GamelistBase::populateList(const std::vector<FileData*>& files, FileData* f
|
||||||
addPlaceholder(firstEntry);
|
addPlaceholder(firstEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mGrid != nullptr)
|
||||||
|
mGrid->calculateLayout();
|
||||||
|
|
||||||
generateGamelistInfo(getCursor(), firstEntry);
|
generateGamelistInfo(getCursor(), firstEntry);
|
||||||
generateFirstLetterIndex(files);
|
generateFirstLetterIndex(files);
|
||||||
}
|
}
|
||||||
|
@ -717,13 +747,20 @@ void GamelistBase::addPlaceholder(FileData* firstEntry)
|
||||||
textListEntry.data.entryType = TextListEntryType::SECONDARY;
|
textListEntry.data.entryType = TextListEntryType::SECONDARY;
|
||||||
mTextList->addEntry(textListEntry);
|
mTextList->addEntry(textListEntry);
|
||||||
}
|
}
|
||||||
if (mCarousel != nullptr) {
|
else if (mCarousel != nullptr) {
|
||||||
CarouselComponent<FileData*>::Entry carouselEntry;
|
CarouselComponent<FileData*>::Entry carouselEntry;
|
||||||
carouselEntry.name = placeholder->getName();
|
carouselEntry.name = placeholder->getName();
|
||||||
letterCaseFunc(carouselEntry.name);
|
letterCaseFunc(carouselEntry.name);
|
||||||
carouselEntry.object = placeholder;
|
carouselEntry.object = placeholder;
|
||||||
mCarousel->addEntry(carouselEntry, mRoot->getSystem()->getTheme());
|
mCarousel->addEntry(carouselEntry, mRoot->getSystem()->getTheme());
|
||||||
}
|
}
|
||||||
|
else if (mGrid != nullptr) {
|
||||||
|
GridComponent<FileData*>::Entry gridEntry;
|
||||||
|
gridEntry.name = placeholder->getName();
|
||||||
|
letterCaseFunc(gridEntry.name);
|
||||||
|
gridEntry.object = placeholder;
|
||||||
|
mGrid->addEntry(gridEntry, mRoot->getSystem()->getTheme());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GamelistBase::generateFirstLetterIndex(const std::vector<FileData*>& files)
|
void GamelistBase::generateFirstLetterIndex(const std::vector<FileData*>& files)
|
||||||
|
@ -961,3 +998,79 @@ void GamelistBase::removeMedia(FileData* game)
|
||||||
removeEmptyDirFunc(systemMediaDir, mediaType, path);
|
removeEmptyDirFunc(systemMediaDir, mediaType, path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string GamelistBase::getQuickSystemSelectLeftButton()
|
||||||
|
{
|
||||||
|
if (Settings::getInstance()->getString("QuickSystemSelect") == "leftrightshoulders") {
|
||||||
|
if (mLeftRightAvailable)
|
||||||
|
return "left";
|
||||||
|
else
|
||||||
|
return "leftshoulder";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings::getInstance()->getString("QuickSystemSelect") == "leftrighttriggers") {
|
||||||
|
if (mLeftRightAvailable)
|
||||||
|
return "left";
|
||||||
|
else
|
||||||
|
return "lefttrigger";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings::getInstance()->getString("QuickSystemSelect") == "shoulders")
|
||||||
|
return "leftshoulder";
|
||||||
|
|
||||||
|
if (Settings::getInstance()->getString("QuickSystemSelect") == "triggers")
|
||||||
|
return "lefttrigger";
|
||||||
|
|
||||||
|
if (Settings::getInstance()->getString("QuickSystemSelect") == "leftright") {
|
||||||
|
if (mLeftRightAvailable)
|
||||||
|
return "left";
|
||||||
|
else
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings::getInstance()->getString("QuickSystemSelect") == "disabled")
|
||||||
|
return "";
|
||||||
|
|
||||||
|
// This should only happen if there is an invalid value in es_settings.xml.
|
||||||
|
if (mLeftRightAvailable)
|
||||||
|
return "left";
|
||||||
|
else
|
||||||
|
return "leftshoulder";
|
||||||
|
}
|
||||||
|
std::string GamelistBase::getQuickSystemSelectRightButton()
|
||||||
|
{
|
||||||
|
if (Settings::getInstance()->getString("QuickSystemSelect") == "leftrightshoulders") {
|
||||||
|
if (mLeftRightAvailable)
|
||||||
|
return "right";
|
||||||
|
else
|
||||||
|
return "rightshoulder";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings::getInstance()->getString("QuickSystemSelect") == "leftrighttriggers") {
|
||||||
|
if (mLeftRightAvailable)
|
||||||
|
return "right";
|
||||||
|
else
|
||||||
|
return "righttrigger";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings::getInstance()->getString("QuickSystemSelect") == "shoulders")
|
||||||
|
return "rightshoulder";
|
||||||
|
|
||||||
|
if (Settings::getInstance()->getString("QuickSystemSelect") == "triggers")
|
||||||
|
return "righttrigger";
|
||||||
|
|
||||||
|
if (Settings::getInstance()->getString("QuickSystemSelect") == "leftright") {
|
||||||
|
if (mLeftRightAvailable)
|
||||||
|
return "right";
|
||||||
|
else
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings::getInstance()->getString("QuickSystemSelect") == "disabled")
|
||||||
|
return "";
|
||||||
|
|
||||||
|
if (mLeftRightAvailable)
|
||||||
|
return "right";
|
||||||
|
else
|
||||||
|
return "rightshoulder";
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "components/TextComponent.h"
|
#include "components/TextComponent.h"
|
||||||
#include "components/VideoFFmpegComponent.h"
|
#include "components/VideoFFmpegComponent.h"
|
||||||
#include "components/primary/CarouselComponent.h"
|
#include "components/primary/CarouselComponent.h"
|
||||||
|
#include "components/primary/GridComponent.h"
|
||||||
#include "components/primary/TextListComponent.h"
|
#include "components/primary/TextListComponent.h"
|
||||||
|
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
@ -85,11 +86,12 @@ protected:
|
||||||
|
|
||||||
bool isListScrolling() override { return mPrimary->isScrolling(); }
|
bool isListScrolling() override { return mPrimary->isScrolling(); }
|
||||||
|
|
||||||
std::string getQuickSystemSelectRightButton() { return "right"; }
|
std::string getQuickSystemSelectLeftButton();
|
||||||
std::string getQuickSystemSelectLeftButton() { return "left"; }
|
std::string getQuickSystemSelectRightButton();
|
||||||
|
|
||||||
FileData* mRoot;
|
FileData* mRoot;
|
||||||
std::unique_ptr<CarouselComponent<FileData*>> mCarousel;
|
std::unique_ptr<CarouselComponent<FileData*>> mCarousel;
|
||||||
|
std::unique_ptr<GridComponent<FileData*>> mGrid;
|
||||||
std::unique_ptr<TextListComponent<FileData*>> mTextList;
|
std::unique_ptr<TextListComponent<FileData*>> mTextList;
|
||||||
PrimaryComponent<FileData*>* mPrimary;
|
PrimaryComponent<FileData*>* mPrimary;
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,12 @@ void GamelistView::onFileChanged(FileData* file, bool reloadGamelist)
|
||||||
FileData* cursor {getCursor()};
|
FileData* cursor {getCursor()};
|
||||||
if (!cursor->isPlaceHolder()) {
|
if (!cursor->isPlaceHolder()) {
|
||||||
populateList(cursor->getParent()->getChildrenListToDisplay(), cursor->getParent());
|
populateList(cursor->getParent()->getChildrenListToDisplay(), cursor->getParent());
|
||||||
|
// Needed to avoid some minor transition animation glitches.
|
||||||
|
if (mGrid != nullptr)
|
||||||
|
mGrid->setSuppressTransitions(true);
|
||||||
setCursor(cursor);
|
setCursor(cursor);
|
||||||
|
if (mGrid != nullptr)
|
||||||
|
mGrid->setSuppressTransitions(false);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
populateList(mRoot->getChildrenListToDisplay(), mRoot);
|
populateList(mRoot->getChildrenListToDisplay(), mRoot);
|
||||||
|
@ -114,15 +119,24 @@ void GamelistView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
||||||
|
|
||||||
if (mTheme->hasView("gamelist")) {
|
if (mTheme->hasView("gamelist")) {
|
||||||
for (auto& element : mTheme->getViewElements("gamelist").elements) {
|
for (auto& element : mTheme->getViewElements("gamelist").elements) {
|
||||||
if (element.second.type == "textlist" || element.second.type == "carousel") {
|
if (element.second.type == "carousel" || element.second.type == "grid" ||
|
||||||
if (element.second.type == "carousel" && mTextList != nullptr) {
|
element.second.type == "textlist") {
|
||||||
|
if (element.second.type == "carousel" &&
|
||||||
|
(mGrid != nullptr || mTextList != nullptr)) {
|
||||||
LOG(LogWarning) << "SystemView::populate(): Multiple primary components "
|
LOG(LogWarning) << "SystemView::populate(): Multiple primary components "
|
||||||
<< "defined, skipping <carousel> configuration entry";
|
<< "defined, skipping carousel configuration entry";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (element.second.type == "textlist" && mCarousel != nullptr) {
|
if (element.second.type == "grid" &&
|
||||||
|
(mCarousel != nullptr || mTextList != nullptr)) {
|
||||||
LOG(LogWarning) << "SystemView::populate(): Multiple primary components "
|
LOG(LogWarning) << "SystemView::populate(): Multiple primary components "
|
||||||
<< "defined, skipping <textlist> configuration entry";
|
<< "defined, skipping grid configuration entry";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (element.second.type == "textlist" &&
|
||||||
|
(mCarousel != nullptr || mGrid != nullptr)) {
|
||||||
|
LOG(LogWarning) << "SystemView::populate(): Multiple primary components "
|
||||||
|
<< "defined, skipping textlist configuration entry";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,9 +145,6 @@ void GamelistView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
||||||
mTextList = std::make_unique<TextListComponent<FileData*>>();
|
mTextList = std::make_unique<TextListComponent<FileData*>>();
|
||||||
mPrimary = mTextList.get();
|
mPrimary = mTextList.get();
|
||||||
}
|
}
|
||||||
mPrimary->setPosition(0.0f, mSize.y * 0.1f);
|
|
||||||
mPrimary->setSize(mSize.x, mSize.y * 0.8f);
|
|
||||||
mPrimary->setAlignment(TextListComponent<FileData*>::PrimaryAlignment::ALIGN_LEFT);
|
|
||||||
mPrimary->setCursorChangedCallback(
|
mPrimary->setCursorChangedCallback(
|
||||||
[&](const CursorState& state) { updateView(state); });
|
[&](const CursorState& state) { updateView(state); });
|
||||||
mPrimary->setDefaultZIndex(50.0f);
|
mPrimary->setDefaultZIndex(50.0f);
|
||||||
|
@ -144,28 +155,51 @@ void GamelistView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
||||||
if (element.second.type == "carousel") {
|
if (element.second.type == "carousel") {
|
||||||
if (mCarousel == nullptr) {
|
if (mCarousel == nullptr) {
|
||||||
mCarousel = std::make_unique<CarouselComponent<FileData*>>();
|
mCarousel = std::make_unique<CarouselComponent<FileData*>>();
|
||||||
if (element.second.has("itemType")) {
|
if (element.second.has("imageType")) {
|
||||||
|
const std::string imageType {element.second.get<std::string>("imageType")};
|
||||||
|
if (imageType == "marquee" || imageType == "cover" ||
|
||||||
|
imageType == "backcover" || imageType == "3dbox" ||
|
||||||
|
imageType == "physicalmedia" || imageType == "screenshot" ||
|
||||||
|
imageType == "titlescreen" || imageType == "miximage" ||
|
||||||
|
imageType == "fanart" || imageType == "none") {
|
||||||
|
mCarousel->setImageType(imageType);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LOG(LogWarning) << "GamelistView::onThemeChanged(): Invalid theme "
|
||||||
|
"configuration, carousel property \"imageType\" "
|
||||||
|
"for element \""
|
||||||
|
<< element.first.substr(9) << "\" defined as \""
|
||||||
|
<< imageType << "\"";
|
||||||
|
mCarousel->setImageType("marquee");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (element.second.has("itemType")) {
|
||||||
|
// TEMPORARY: Backward compatiblity due to property name changes.
|
||||||
const std::string itemType {element.second.get<std::string>("itemType")};
|
const std::string itemType {element.second.get<std::string>("itemType")};
|
||||||
if (itemType == "marquee" || itemType == "cover" ||
|
if (itemType == "marquee" || itemType == "cover" ||
|
||||||
itemType == "backcover" || itemType == "3dbox" ||
|
itemType == "backcover" || itemType == "3dbox" ||
|
||||||
itemType == "physicalmedia" || itemType == "screenshot" ||
|
itemType == "physicalmedia" || itemType == "screenshot" ||
|
||||||
itemType == "titlescreen" || itemType == "miximage" ||
|
itemType == "titlescreen" || itemType == "miximage" ||
|
||||||
itemType == "fanart" || itemType == "none") {
|
itemType == "fanart" || itemType == "none") {
|
||||||
mCarousel->setItemType(itemType);
|
mCarousel->setImageType(itemType);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG(LogWarning)
|
LOG(LogWarning) << "GamelistView::onThemeChanged(): Invalid theme "
|
||||||
<< "GamelistView::onThemeChanged(): Invalid theme configuration, "
|
"configuration, carousel property \"itemType\" "
|
||||||
"<itemType> property defined as \""
|
"for element \""
|
||||||
<< itemType << "\"";
|
<< element.first.substr(9) << "\" defined as \""
|
||||||
mCarousel->setItemType("marquee");
|
<< itemType << "\"";
|
||||||
|
mCarousel->setImageType("marquee");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mCarousel->setItemType("marquee");
|
mCarousel->setImageType("marquee");
|
||||||
}
|
}
|
||||||
|
// TEMPORARY: Backward compatiblity due to property name changes.
|
||||||
if (element.second.has("defaultItem"))
|
if (element.second.has("defaultItem"))
|
||||||
mCarousel->setDefaultItem(element.second.get<std::string>("defaultItem"));
|
mCarousel->setDefaultImage(element.second.get<std::string>("defaultItem"));
|
||||||
|
if (element.second.has("defaultImage"))
|
||||||
|
mCarousel->setDefaultImage(element.second.get<std::string>("defaultImage"));
|
||||||
mPrimary = mCarousel.get();
|
mPrimary = mCarousel.get();
|
||||||
}
|
}
|
||||||
mPrimary->setCursorChangedCallback(
|
mPrimary->setCursorChangedCallback(
|
||||||
|
@ -174,6 +208,40 @@ void GamelistView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
||||||
mPrimary->applyTheme(theme, "gamelist", element.first, ALL);
|
mPrimary->applyTheme(theme, "gamelist", element.first, ALL);
|
||||||
addChild(mPrimary);
|
addChild(mPrimary);
|
||||||
}
|
}
|
||||||
|
if (element.second.type == "grid") {
|
||||||
|
if (mGrid == nullptr) {
|
||||||
|
mGrid = std::make_unique<GridComponent<FileData*>>();
|
||||||
|
if (element.second.has("imageType")) {
|
||||||
|
const std::string imageType {element.second.get<std::string>("imageType")};
|
||||||
|
if (imageType == "marquee" || imageType == "cover" ||
|
||||||
|
imageType == "backcover" || imageType == "3dbox" ||
|
||||||
|
imageType == "physicalmedia" || imageType == "screenshot" ||
|
||||||
|
imageType == "titlescreen" || imageType == "miximage" ||
|
||||||
|
imageType == "fanart" || imageType == "none") {
|
||||||
|
mGrid->setImageType(imageType);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LOG(LogWarning) << "GamelistView::onThemeChanged(): Invalid theme "
|
||||||
|
"configuration, grid property \"imageType\" "
|
||||||
|
"for element \""
|
||||||
|
<< element.first.substr(5) << "\" defined as \""
|
||||||
|
<< imageType << "\"";
|
||||||
|
mGrid->setImageType("marquee");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mGrid->setImageType("marquee");
|
||||||
|
}
|
||||||
|
if (element.second.has("defaultImage"))
|
||||||
|
mGrid->setDefaultImage(element.second.get<std::string>("defaultImage"));
|
||||||
|
mPrimary = mGrid.get();
|
||||||
|
}
|
||||||
|
mPrimary->setCursorChangedCallback(
|
||||||
|
[&](const CursorState& state) { updateView(state); });
|
||||||
|
mPrimary->setDefaultZIndex(50.0f);
|
||||||
|
mPrimary->applyTheme(theme, "gamelist", element.first, ALL);
|
||||||
|
addChild(mPrimary);
|
||||||
|
}
|
||||||
if (element.second.type == "image") {
|
if (element.second.type == "image") {
|
||||||
// If this is the startup system, then forceload the images to avoid texture pop-in.
|
// If this is the startup system, then forceload the images to avoid texture pop-in.
|
||||||
if (isStartupSystem)
|
if (isStartupSystem)
|
||||||
|
@ -313,9 +381,6 @@ void GamelistView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
||||||
if (mPrimary == nullptr) {
|
if (mPrimary == nullptr) {
|
||||||
mTextList = std::make_unique<TextListComponent<FileData*>>();
|
mTextList = std::make_unique<TextListComponent<FileData*>>();
|
||||||
mPrimary = mTextList.get();
|
mPrimary = mTextList.get();
|
||||||
mPrimary->setPosition(0.0f, mSize.y * 0.1f);
|
|
||||||
mPrimary->setSize(mSize.x, mSize.y * 0.8f);
|
|
||||||
mPrimary->setAlignment(TextListComponent<FileData*>::PrimaryAlignment::ALIGN_LEFT);
|
|
||||||
mPrimary->setCursorChangedCallback([&](const CursorState& state) { updateView(state); });
|
mPrimary->setCursorChangedCallback([&](const CursorState& state) { updateView(state); });
|
||||||
mPrimary->setDefaultZIndex(50.0f);
|
mPrimary->setDefaultZIndex(50.0f);
|
||||||
mPrimary->setZIndex(50.0f);
|
mPrimary->setZIndex(50.0f);
|
||||||
|
@ -325,12 +390,15 @@ void GamelistView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
||||||
|
|
||||||
populateList(mRoot->getChildrenListToDisplay(), mRoot);
|
populateList(mRoot->getChildrenListToDisplay(), mRoot);
|
||||||
|
|
||||||
// Disable quick system select if the primary component uses the left and right buttons.
|
// Check whether the primary component uses the left and right buttons for its navigation.
|
||||||
if (mCarousel != nullptr) {
|
if (mCarousel != nullptr) {
|
||||||
if (mCarousel->getType() == CarouselComponent<FileData*>::CarouselType::HORIZONTAL ||
|
if (mCarousel->getType() == CarouselComponent<FileData*>::CarouselType::HORIZONTAL ||
|
||||||
mCarousel->getType() == CarouselComponent<FileData*>::CarouselType::HORIZONTAL_WHEEL)
|
mCarousel->getType() == CarouselComponent<FileData*>::CarouselType::HORIZONTAL_WHEEL)
|
||||||
mLeftRightAvailable = false;
|
mLeftRightAvailable = false;
|
||||||
}
|
}
|
||||||
|
else if (mGrid != nullptr) {
|
||||||
|
mLeftRightAvailable = false;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto& video : mStaticVideoComponents) {
|
for (auto& video : mStaticVideoComponents) {
|
||||||
if (video->hasStaticVideo())
|
if (video->hasStaticVideo())
|
||||||
|
@ -386,15 +454,20 @@ std::vector<HelpPrompt> GamelistView::getHelpPrompts()
|
||||||
{
|
{
|
||||||
std::vector<HelpPrompt> prompts;
|
std::vector<HelpPrompt> prompts;
|
||||||
|
|
||||||
if (Settings::getInstance()->getBool("QuickSystemSelect") &&
|
if (Settings::getInstance()->getString("QuickSystemSelect") != "disabled") {
|
||||||
SystemData::sSystemVector.size() > 1 && mLeftRightAvailable)
|
if (getQuickSystemSelectLeftButton() == "leftshoulder")
|
||||||
prompts.push_back(HelpPrompt("left/right", "system"));
|
prompts.push_back(HelpPrompt("lr", "system"));
|
||||||
|
else if (getQuickSystemSelectLeftButton() == "lefttrigger")
|
||||||
|
prompts.push_back(HelpPrompt("ltrt", "system"));
|
||||||
|
else if (getQuickSystemSelectLeftButton() == "left")
|
||||||
|
prompts.push_back(HelpPrompt("left/right", "system"));
|
||||||
|
}
|
||||||
|
|
||||||
if (mRoot->getSystem()->getThemeFolder() == "custom-collections" && mCursorStack.empty() &&
|
if (mRoot->getSystem()->getThemeFolder() == "custom-collections" && mCursorStack.empty() &&
|
||||||
ViewController::getInstance()->getState().viewing == ViewController::GAMELIST)
|
ViewController::getInstance()->getState().viewing == ViewController::GAMELIST)
|
||||||
prompts.push_back(HelpPrompt("a", "enter"));
|
prompts.push_back(HelpPrompt("a", "select"));
|
||||||
else
|
else
|
||||||
prompts.push_back(HelpPrompt("a", "launch"));
|
prompts.push_back(HelpPrompt("a", "select"));
|
||||||
|
|
||||||
prompts.push_back(HelpPrompt("b", "back"));
|
prompts.push_back(HelpPrompt("b", "back"));
|
||||||
prompts.push_back(HelpPrompt("x", "view media"));
|
prompts.push_back(HelpPrompt("x", "view media"));
|
||||||
|
|
|
@ -128,7 +128,7 @@ bool SystemView::input(InputConfig* config, Input input)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->isMappedTo("back", input) &&
|
if (config->isMappedTo("x", input) &&
|
||||||
Settings::getInstance()->getBool("ScreensaverControls")) {
|
Settings::getInstance()->getBool("ScreensaverControls")) {
|
||||||
if (!mWindow->isScreensaverActive()) {
|
if (!mWindow->isScreensaverActive()) {
|
||||||
ViewController::getInstance()->stopScrolling();
|
ViewController::getInstance()->stopScrolling();
|
||||||
|
@ -145,9 +145,6 @@ bool SystemView::input(InputConfig* config, Input input)
|
||||||
|
|
||||||
void SystemView::update(int deltaTime)
|
void SystemView::update(int deltaTime)
|
||||||
{
|
{
|
||||||
if (!mPrimary->isAnimationPlaying(0))
|
|
||||||
mMaxFade = false;
|
|
||||||
|
|
||||||
mPrimary->update(deltaTime);
|
mPrimary->update(deltaTime);
|
||||||
|
|
||||||
for (auto& video : mSystemElements[mPrimary->getCursor()].videoComponents) {
|
for (auto& video : mSystemElements[mPrimary->getCursor()].videoComponents) {
|
||||||
|
@ -208,6 +205,9 @@ std::vector<HelpPrompt> SystemView::getHelpPrompts()
|
||||||
else
|
else
|
||||||
prompts.push_back(HelpPrompt("left/right", "choose"));
|
prompts.push_back(HelpPrompt("left/right", "choose"));
|
||||||
}
|
}
|
||||||
|
else if (mGrid != nullptr) {
|
||||||
|
prompts.push_back(HelpPrompt("up/down/left/right", "choose"));
|
||||||
|
}
|
||||||
else if (mTextList != nullptr) {
|
else if (mTextList != nullptr) {
|
||||||
prompts.push_back(HelpPrompt("up/down", "choose"));
|
prompts.push_back(HelpPrompt("up/down", "choose"));
|
||||||
}
|
}
|
||||||
|
@ -218,14 +218,50 @@ std::vector<HelpPrompt> SystemView::getHelpPrompts()
|
||||||
prompts.push_back(HelpPrompt("thumbstickclick", "random"));
|
prompts.push_back(HelpPrompt("thumbstickclick", "random"));
|
||||||
|
|
||||||
if (Settings::getInstance()->getBool("ScreensaverControls"))
|
if (Settings::getInstance()->getBool("ScreensaverControls"))
|
||||||
prompts.push_back(HelpPrompt("back", "screensaver"));
|
prompts.push_back(HelpPrompt("x", "screensaver"));
|
||||||
|
|
||||||
return prompts;
|
return prompts;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemView::onCursorChanged(const CursorState& state)
|
void SystemView::onCursorChanged(const CursorState& state)
|
||||||
{
|
{
|
||||||
int cursor {mPrimary->getCursor()};
|
const int cursor {mPrimary->getCursor()};
|
||||||
|
const int scrollVelocity {mPrimary->getScrollingVelocity()};
|
||||||
|
const std::string& transitionStyle {Settings::getInstance()->getString("TransitionStyle")};
|
||||||
|
mFadeTransitions = transitionStyle == "fade";
|
||||||
|
|
||||||
|
// Some logic needed to avoid various navigation glitches with GridComponent and
|
||||||
|
// TextListComponent.
|
||||||
|
if (state == CursorState::CURSOR_STOPPED && mCarousel == nullptr) {
|
||||||
|
const int numEntries {static_cast<int>(mPrimary->getNumEntries())};
|
||||||
|
bool doStop {false};
|
||||||
|
|
||||||
|
if (cursor == 0 && mLastCursor == numEntries - 1 && std::abs(scrollVelocity) == 1)
|
||||||
|
doStop = false;
|
||||||
|
else if (cursor == 0)
|
||||||
|
doStop = true;
|
||||||
|
else if (cursor == numEntries - 1 && mLastCursor == 0 && std::abs(scrollVelocity) == 1)
|
||||||
|
doStop = false;
|
||||||
|
else if (cursor == numEntries - 1)
|
||||||
|
doStop = true;
|
||||||
|
|
||||||
|
if (!doStop && mGrid != nullptr && std::abs(scrollVelocity) == mGrid->getColumnCount()) {
|
||||||
|
const int columns {mGrid->getColumnCount()};
|
||||||
|
const int columnModulus {numEntries % columns};
|
||||||
|
|
||||||
|
if (cursor < columns)
|
||||||
|
doStop = true;
|
||||||
|
else if (cursor >= numEntries - (columnModulus == 0 ? columns : columnModulus))
|
||||||
|
doStop = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doStop) {
|
||||||
|
if (mGrid != nullptr)
|
||||||
|
mGrid->setScrollVelocity(0);
|
||||||
|
mPrimary->stopScrolling();
|
||||||
|
mNavigated = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Avoid double updates.
|
// Avoid double updates.
|
||||||
if (cursor != mLastCursor) {
|
if (cursor != mLastCursor) {
|
||||||
|
@ -240,6 +276,31 @@ void SystemView::onCursorChanged(const CursorState& state)
|
||||||
video->stopVideoPlayer();
|
video->stopVideoPlayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is needed to avoid erratic camera movements during extreme navigation input when using
|
||||||
|
// slide transitions. This should very rarely occur during normal application usage.
|
||||||
|
if (transitionStyle == "slide") {
|
||||||
|
bool resetCamOffset {false};
|
||||||
|
|
||||||
|
if (scrollVelocity == -1 && mPreviousScrollVelocity == 1) {
|
||||||
|
if (mLastCursor > cursor && mCamOffset > static_cast<float>(mLastCursor))
|
||||||
|
resetCamOffset = true;
|
||||||
|
else if (mLastCursor > cursor && mCamOffset < static_cast<float>(cursor))
|
||||||
|
resetCamOffset = true;
|
||||||
|
else if (mLastCursor < cursor && mCamOffset <= static_cast<float>(cursor) &&
|
||||||
|
mCamOffset != static_cast<float>(mLastCursor))
|
||||||
|
resetCamOffset = true;
|
||||||
|
}
|
||||||
|
else if (scrollVelocity == 1 && mPreviousScrollVelocity == -1) {
|
||||||
|
if (mLastCursor > cursor && mCamOffset < static_cast<float>(mLastCursor))
|
||||||
|
resetCamOffset = true;
|
||||||
|
else if (mLastCursor < cursor && mCamOffset > static_cast<float>(cursor))
|
||||||
|
resetCamOffset = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resetCamOffset)
|
||||||
|
mCamOffset = static_cast<float>(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
mLastCursor = cursor;
|
mLastCursor = cursor;
|
||||||
|
|
||||||
for (auto& video : mSystemElements[cursor].videoComponents)
|
for (auto& video : mSystemElements[cursor].videoComponents)
|
||||||
|
@ -255,10 +316,9 @@ void SystemView::onCursorChanged(const CursorState& state)
|
||||||
startViewVideos();
|
startViewVideos();
|
||||||
updateHelpPrompts();
|
updateHelpPrompts();
|
||||||
|
|
||||||
int scrollVelocity {mPrimary->getScrollingVelocity()};
|
const float posMax {static_cast<float>(mPrimary->getNumEntries())};
|
||||||
|
const float target {static_cast<float>(cursor)};
|
||||||
float startPos {mCamOffset};
|
float startPos {mCamOffset};
|
||||||
float posMax {static_cast<float>(mPrimary->getNumEntries())};
|
|
||||||
float target {static_cast<float>(cursor)};
|
|
||||||
float endPos {target};
|
float endPos {target};
|
||||||
|
|
||||||
if (mPreviousScrollVelocity > 0 && scrollVelocity == 0 && mCamOffset > posMax - 1.0f)
|
if (mPreviousScrollVelocity > 0 && scrollVelocity == 0 && mCamOffset > posMax - 1.0f)
|
||||||
|
@ -290,24 +350,32 @@ void SystemView::onCursorChanged(const CursorState& state)
|
||||||
if (scrollVelocity != 0)
|
if (scrollVelocity != 0)
|
||||||
mPreviousScrollVelocity = scrollVelocity;
|
mPreviousScrollVelocity = scrollVelocity;
|
||||||
|
|
||||||
std::string transitionStyle {Settings::getInstance()->getString("TransitionStyle")};
|
|
||||||
mFadeTransitions = transitionStyle == "fade";
|
|
||||||
|
|
||||||
Animation* anim;
|
Animation* anim;
|
||||||
|
|
||||||
|
float animTime {400.0f};
|
||||||
|
float timeMin {200.0f};
|
||||||
|
float timeDiff {1.0f};
|
||||||
|
|
||||||
|
if (mGrid != nullptr) {
|
||||||
|
animTime = 300.0f;
|
||||||
|
timeMin = 100.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If startPos is inbetween two positions then reduce the time slightly as the distance will
|
||||||
|
// be shorter meaning the animation would play for too long if not compensated for.
|
||||||
|
if (scrollVelocity == 1)
|
||||||
|
timeDiff = endPos - startPos;
|
||||||
|
else if (scrollVelocity == -1)
|
||||||
|
timeDiff = startPos - endPos;
|
||||||
|
|
||||||
|
if (timeDiff != 1.0f)
|
||||||
|
animTime =
|
||||||
|
glm::clamp(std::fabs(glm::mix(0.0f, animTime, timeDiff * 1.5f)), timeMin, animTime);
|
||||||
|
|
||||||
if (transitionStyle == "fade") {
|
if (transitionStyle == "fade") {
|
||||||
float startFade {mFadeOpacity};
|
float startFade {mFadeOpacity};
|
||||||
anim = new LambdaAnimation(
|
anim = new LambdaAnimation(
|
||||||
[this, startFade, startPos, endPos, posMax](float t) {
|
[this, startFade, endPos](float t) {
|
||||||
t -= 1;
|
|
||||||
float f {glm::mix(startPos, endPos, t * t * t + 1.0f)};
|
|
||||||
if (f < 0.0f)
|
|
||||||
f += posMax;
|
|
||||||
if (f >= posMax)
|
|
||||||
f -= posMax;
|
|
||||||
|
|
||||||
t += 1;
|
|
||||||
|
|
||||||
if (t < 0.3f)
|
if (t < 0.3f)
|
||||||
mFadeOpacity =
|
mFadeOpacity =
|
||||||
glm::mix(0.0f, 1.0f, glm::clamp(t / 0.2f + startFade, 0.0f, 1.0f));
|
glm::mix(0.0f, 1.0f, glm::clamp(t / 0.2f + startFade, 0.0f, 1.0f));
|
||||||
|
@ -319,7 +387,7 @@ void SystemView::onCursorChanged(const CursorState& state)
|
||||||
if (t > 0.5f)
|
if (t > 0.5f)
|
||||||
mCamOffset = endPos;
|
mCamOffset = endPos;
|
||||||
|
|
||||||
if (t >= 0.7f && t != 1.0f)
|
if (mNavigated && t >= 0.7f && t != 1.0f)
|
||||||
mMaxFade = true;
|
mMaxFade = true;
|
||||||
|
|
||||||
// Update the game count when the entire animation has been completed.
|
// Update the game count when the entire animation has been completed.
|
||||||
|
@ -328,15 +396,17 @@ void SystemView::onCursorChanged(const CursorState& state)
|
||||||
updateGameCount();
|
updateGameCount();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
500);
|
static_cast<int>(animTime * 1.3f));
|
||||||
}
|
}
|
||||||
else if (transitionStyle == "slide") {
|
else if (transitionStyle == "slide") {
|
||||||
mUpdatedGameCount = false;
|
mUpdatedGameCount = false;
|
||||||
anim = new LambdaAnimation(
|
anim = new LambdaAnimation(
|
||||||
[this, startPos, endPos, posMax](float t) {
|
[this, startPos, endPos, posMax](float t) {
|
||||||
t -= 1;
|
// Non-linear interpolation.
|
||||||
float f {glm::mix(startPos, endPos, t * t * t + 1.0f)};
|
t = 1.0f - (1.0f - t) * (1.0f - t);
|
||||||
if (f < 0.0f)
|
float f {(endPos * t) + (startPos * (1.0f - t))};
|
||||||
|
|
||||||
|
if (f < 0)
|
||||||
f += posMax;
|
f += posMax;
|
||||||
if (f >= posMax)
|
if (f >= posMax)
|
||||||
f -= posMax;
|
f -= posMax;
|
||||||
|
@ -362,23 +432,13 @@ void SystemView::onCursorChanged(const CursorState& state)
|
||||||
updateGameCount();
|
updateGameCount();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
500);
|
static_cast<int>(animTime));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Instant.
|
// Instant.
|
||||||
updateGameCount();
|
updateGameCount();
|
||||||
anim = new LambdaAnimation(
|
anim = new LambdaAnimation([this, endPos](float t) { mCamOffset = endPos; },
|
||||||
[this, startPos, endPos, posMax](float t) {
|
static_cast<int>(animTime));
|
||||||
t -= 1;
|
|
||||||
float f {glm::mix(startPos, endPos, t * t * t + 1.0f)};
|
|
||||||
if (f < 0.0f)
|
|
||||||
f += posMax;
|
|
||||||
if (f >= posMax)
|
|
||||||
f -= posMax;
|
|
||||||
|
|
||||||
mCamOffset = endPos;
|
|
||||||
},
|
|
||||||
500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setAnimation(anim, 0, nullptr, false, 0);
|
setAnimation(anim, 0, nullptr, false, 0);
|
||||||
|
@ -389,7 +449,7 @@ void SystemView::populate()
|
||||||
if (SystemData::sSystemVector.size() == 0)
|
if (SystemData::sSystemVector.size() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
LOG(LogDebug) << "SystemView::populate(): Populating carousel";
|
LOG(LogDebug) << "SystemView::populate(): Populating primary element...";
|
||||||
|
|
||||||
auto themeSets = ThemeData::getThemeSets();
|
auto themeSets = ThemeData::getThemeSets();
|
||||||
std::map<std::string, ThemeData::ThemeSet, ThemeData::StringComparator>::const_iterator
|
std::map<std::string, ThemeData::ThemeSet, ThemeData::StringComparator>::const_iterator
|
||||||
|
@ -405,8 +465,9 @@ void SystemView::populate()
|
||||||
|
|
||||||
for (auto it : SystemData::sSystemVector) {
|
for (auto it : SystemData::sSystemVector) {
|
||||||
const std::shared_ptr<ThemeData>& theme {it->getTheme()};
|
const std::shared_ptr<ThemeData>& theme {it->getTheme()};
|
||||||
std::string itemPath;
|
std::string imagePath;
|
||||||
std::string defaultItemPath;
|
std::string defaultImagePath;
|
||||||
|
std::string itemText;
|
||||||
|
|
||||||
if (mLegacyMode && mViewNeedsReload) {
|
if (mLegacyMode && mViewNeedsReload) {
|
||||||
if (mCarousel == nullptr) {
|
if (mCarousel == nullptr) {
|
||||||
|
@ -458,17 +519,27 @@ void SystemView::populate()
|
||||||
ThemeFlags::ALL);
|
ThemeFlags::ALL);
|
||||||
elements.gameSelectors.back()->setNeedsRefresh();
|
elements.gameSelectors.back()->setNeedsRefresh();
|
||||||
}
|
}
|
||||||
if (element.second.type == "textlist" || element.second.type == "carousel") {
|
if (element.second.type == "carousel" || element.second.type == "grid" ||
|
||||||
if (element.second.type == "carousel" && mTextList != nullptr) {
|
element.second.type == "textlist") {
|
||||||
|
if (element.second.type == "carousel" &&
|
||||||
|
(mGrid != nullptr || mTextList != nullptr)) {
|
||||||
LOG(LogWarning)
|
LOG(LogWarning)
|
||||||
<< "SystemView::populate(): Multiple primary components "
|
<< "SystemView::populate(): Multiple primary components "
|
||||||
<< "defined, skipping <carousel> configuration entry";
|
<< "defined, skipping carousel configuration entry";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (element.second.type == "textlist" && mCarousel != nullptr) {
|
if (element.second.type == "grid" &&
|
||||||
|
(mCarousel != nullptr || mTextList != nullptr)) {
|
||||||
LOG(LogWarning)
|
LOG(LogWarning)
|
||||||
<< "SystemView::populate(): Multiple primary components "
|
<< "SystemView::populate(): Multiple primary components "
|
||||||
<< "defined, skipping <textlist> configuration entry";
|
<< "defined, skipping grid configuration entry";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (element.second.type == "textlist" &&
|
||||||
|
(mCarousel != nullptr || mGrid != nullptr)) {
|
||||||
|
LOG(LogWarning)
|
||||||
|
<< "SystemView::populate(): Multiple primary components "
|
||||||
|
<< "defined, skipping textlist configuration entry";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (element.second.type == "carousel" && mCarousel == nullptr) {
|
if (element.second.type == "carousel" && mCarousel == nullptr) {
|
||||||
|
@ -476,6 +547,11 @@ void SystemView::populate()
|
||||||
mPrimary = mCarousel.get();
|
mPrimary = mCarousel.get();
|
||||||
mPrimaryType = PrimaryType::CAROUSEL;
|
mPrimaryType = PrimaryType::CAROUSEL;
|
||||||
}
|
}
|
||||||
|
else if (element.second.type == "grid" && mGrid == nullptr) {
|
||||||
|
mGrid = std::make_unique<GridComponent<SystemData*>>();
|
||||||
|
mPrimary = mGrid.get();
|
||||||
|
mPrimaryType = PrimaryType::GRID;
|
||||||
|
}
|
||||||
else if (element.second.type == "textlist" && mTextList == nullptr) {
|
else if (element.second.type == "textlist" && mTextList == nullptr) {
|
||||||
mTextList = std::make_unique<TextListComponent<SystemData*>>();
|
mTextList = std::make_unique<TextListComponent<SystemData*>>();
|
||||||
mPrimary = mTextList.get();
|
mPrimary = mTextList.get();
|
||||||
|
@ -497,11 +573,21 @@ void SystemView::populate()
|
||||||
anim->setPauseAnimation(true);
|
anim->setPauseAnimation(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (mCarousel != nullptr) {
|
if (mCarousel != nullptr || mGrid != nullptr) {
|
||||||
if (element.second.has("staticItem"))
|
if (mCarousel != nullptr) {
|
||||||
itemPath = element.second.get<std::string>("staticItem");
|
// TEMPORARY: Backward compatiblity due to property name changes.
|
||||||
if (element.second.has("defaultItem"))
|
if (element.second.has("staticItem"))
|
||||||
defaultItemPath = element.second.get<std::string>("defaultItem");
|
imagePath = element.second.get<std::string>("staticItem");
|
||||||
|
if (element.second.has("defaultItem"))
|
||||||
|
defaultImagePath =
|
||||||
|
element.second.get<std::string>("defaultItem");
|
||||||
|
}
|
||||||
|
if (element.second.has("staticImage"))
|
||||||
|
imagePath = element.second.get<std::string>("staticImage");
|
||||||
|
if (element.second.has("defaultImage"))
|
||||||
|
defaultImagePath = element.second.get<std::string>("defaultImage");
|
||||||
|
if (element.second.has("text"))
|
||||||
|
itemText = element.second.get<std::string>("text");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (element.second.type == "image") {
|
else if (element.second.type == "image") {
|
||||||
|
@ -663,18 +749,35 @@ void SystemView::populate()
|
||||||
|
|
||||||
if (mCarousel != nullptr) {
|
if (mCarousel != nullptr) {
|
||||||
CarouselComponent<SystemData*>::Entry entry;
|
CarouselComponent<SystemData*>::Entry entry;
|
||||||
// Keep showing only the short name for legacy themes to maintain maximum
|
if (mLegacyMode) {
|
||||||
// backward compatibility. This also applies to unreadable theme sets.
|
// Keep showing only the short name for legacy themes to maintain maximum
|
||||||
if (mLegacyMode)
|
// backward compatibility. This also applies to unreadable theme sets.
|
||||||
entry.name = it->getName();
|
entry.name = it->getName();
|
||||||
else
|
}
|
||||||
entry.name = it->getFullName();
|
else {
|
||||||
|
if (itemText == "")
|
||||||
|
entry.name = it->getFullName();
|
||||||
|
else
|
||||||
|
entry.name = itemText;
|
||||||
|
}
|
||||||
letterCaseFunc(entry.name);
|
letterCaseFunc(entry.name);
|
||||||
entry.object = it;
|
entry.object = it;
|
||||||
entry.data.itemPath = itemPath;
|
entry.data.imagePath = imagePath;
|
||||||
entry.data.defaultItemPath = defaultItemPath;
|
entry.data.defaultImagePath = defaultImagePath;
|
||||||
mCarousel->addEntry(entry, theme);
|
mCarousel->addEntry(entry, theme);
|
||||||
}
|
}
|
||||||
|
else if (mGrid != nullptr) {
|
||||||
|
GridComponent<SystemData*>::Entry entry;
|
||||||
|
if (itemText == "")
|
||||||
|
entry.name = it->getFullName();
|
||||||
|
else
|
||||||
|
entry.name = itemText;
|
||||||
|
letterCaseFunc(entry.name);
|
||||||
|
entry.object = it;
|
||||||
|
entry.data.imagePath = imagePath;
|
||||||
|
entry.data.defaultImagePath = defaultImagePath;
|
||||||
|
mGrid->addEntry(entry, theme);
|
||||||
|
}
|
||||||
else if (mTextList != nullptr) {
|
else if (mTextList != nullptr) {
|
||||||
TextListComponent<SystemData*>::Entry entry;
|
TextListComponent<SystemData*>::Entry entry;
|
||||||
entry.name = it->getFullName();
|
entry.name = it->getFullName();
|
||||||
|
@ -685,6 +788,9 @@ void SystemView::populate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mGrid != nullptr)
|
||||||
|
mGrid->calculateLayout();
|
||||||
|
|
||||||
for (auto& elements : mSystemElements) {
|
for (auto& elements : mSystemElements) {
|
||||||
for (auto& text : elements.textComponents) {
|
for (auto& text : elements.textComponents) {
|
||||||
if (text->getThemeSystemdata() != "") {
|
if (text->getThemeSystemdata() != "") {
|
||||||
|
@ -772,12 +878,12 @@ void SystemView::updateGameSelectors()
|
||||||
if (mLegacyMode)
|
if (mLegacyMode)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int cursor {mPrimary->getCursor()};
|
const int cursor {mPrimary->getCursor()};
|
||||||
|
|
||||||
if (mSystemElements[cursor].gameSelectors.size() == 0)
|
if (mSystemElements[cursor].gameSelectors.size() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool multipleSelectors {mSystemElements[cursor].gameSelectors.size() > 1};
|
const bool multipleSelectors {mSystemElements[cursor].gameSelectors.size() > 1};
|
||||||
|
|
||||||
for (auto& image : mSystemElements[cursor].imageComponents) {
|
for (auto& image : mSystemElements[cursor].imageComponents) {
|
||||||
if (image->getThemeImageTypes().size() == 0)
|
if (image->getThemeImageTypes().size() == 0)
|
||||||
|
@ -809,76 +915,79 @@ void SystemView::updateGameSelectors()
|
||||||
else {
|
else {
|
||||||
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
||||||
}
|
}
|
||||||
|
const size_t gameSelectorEntry {static_cast<size_t>(
|
||||||
|
glm::clamp(image->getThemeGameSelectorEntry(), 0u,
|
||||||
|
static_cast<unsigned int>(gameSelector->getGameCount() - 1)))};
|
||||||
gameSelector->refreshGames();
|
gameSelector->refreshGames();
|
||||||
std::vector<FileData*> games {gameSelector->getGames()};
|
std::vector<FileData*> games {gameSelector->getGames()};
|
||||||
if (!games.empty()) {
|
if (games.size() > gameSelectorEntry) {
|
||||||
std::string path;
|
std::string path;
|
||||||
for (auto& imageType : image->getThemeImageTypes()) {
|
for (auto& imageType : image->getThemeImageTypes()) {
|
||||||
if (imageType == "image") {
|
if (imageType == "image") {
|
||||||
path = games.front()->getImagePath();
|
path = games.at(gameSelectorEntry)->getImagePath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
image->setImage(path);
|
image->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "miximage") {
|
else if (imageType == "miximage") {
|
||||||
path = games.front()->getMiximagePath();
|
path = games.at(gameSelectorEntry)->getMiximagePath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
image->setImage(path);
|
image->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "marquee") {
|
else if (imageType == "marquee") {
|
||||||
path = games.front()->getMarqueePath();
|
path = games.at(gameSelectorEntry)->getMarqueePath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
image->setImage(path);
|
image->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "screenshot") {
|
else if (imageType == "screenshot") {
|
||||||
path = games.front()->getScreenshotPath();
|
path = games.at(gameSelectorEntry)->getScreenshotPath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
image->setImage(path);
|
image->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "titlescreen") {
|
else if (imageType == "titlescreen") {
|
||||||
path = games.front()->getTitleScreenPath();
|
path = games.at(gameSelectorEntry)->getTitleScreenPath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
image->setImage(path);
|
image->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "cover") {
|
else if (imageType == "cover") {
|
||||||
path = games.front()->getCoverPath();
|
path = games.at(gameSelectorEntry)->getCoverPath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
image->setImage(path);
|
image->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "backcover") {
|
else if (imageType == "backcover") {
|
||||||
path = games.front()->getBackCoverPath();
|
path = games.at(gameSelectorEntry)->getBackCoverPath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
image->setImage(path);
|
image->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "3dbox") {
|
else if (imageType == "3dbox") {
|
||||||
path = games.front()->get3DBoxPath();
|
path = games.at(gameSelectorEntry)->get3DBoxPath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
image->setImage(path);
|
image->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "physicalmedia") {
|
else if (imageType == "physicalmedia") {
|
||||||
path = games.front()->getPhysicalMediaPath();
|
path = games.at(gameSelectorEntry)->getPhysicalMediaPath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
image->setImage(path);
|
image->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "fanart") {
|
else if (imageType == "fanart") {
|
||||||
path = games.front()->getFanArtPath();
|
path = games.at(gameSelectorEntry)->getFanArtPath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
image->setImage(path);
|
image->setImage(path);
|
||||||
break;
|
break;
|
||||||
|
@ -925,10 +1034,13 @@ void SystemView::updateGameSelectors()
|
||||||
else {
|
else {
|
||||||
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
||||||
}
|
}
|
||||||
|
const size_t gameSelectorEntry {static_cast<size_t>(
|
||||||
|
glm::clamp(video->getThemeGameSelectorEntry(), 0u,
|
||||||
|
static_cast<unsigned int>(gameSelector->getGameCount() - 1)))};
|
||||||
gameSelector->refreshGames();
|
gameSelector->refreshGames();
|
||||||
std::vector<FileData*> games {gameSelector->getGames()};
|
std::vector<FileData*> games {gameSelector->getGames()};
|
||||||
if (!games.empty()) {
|
if (games.size() > gameSelectorEntry) {
|
||||||
if (!video->setVideo(games.front()->getVideoPath()))
|
if (!video->setVideo(games.at(gameSelectorEntry)->getVideoPath()))
|
||||||
video->setDefaultVideo();
|
video->setDefaultVideo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -963,76 +1075,79 @@ void SystemView::updateGameSelectors()
|
||||||
else {
|
else {
|
||||||
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
||||||
}
|
}
|
||||||
|
const size_t gameSelectorEntry {static_cast<size_t>(
|
||||||
|
glm::clamp(video->getThemeGameSelectorEntry(), 0u,
|
||||||
|
static_cast<unsigned int>(gameSelector->getGameCount() - 1)))};
|
||||||
gameSelector->refreshGames();
|
gameSelector->refreshGames();
|
||||||
std::vector<FileData*> games {gameSelector->getGames()};
|
std::vector<FileData*> games {gameSelector->getGames()};
|
||||||
if (!games.empty()) {
|
if (games.size() > gameSelectorEntry) {
|
||||||
std::string path;
|
std::string path;
|
||||||
for (auto& imageType : video->getThemeImageTypes()) {
|
for (auto& imageType : video->getThemeImageTypes()) {
|
||||||
if (imageType == "image") {
|
if (imageType == "image") {
|
||||||
path = games.front()->getImagePath();
|
path = games.at(gameSelectorEntry)->getImagePath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
video->setImage(path);
|
video->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "miximage") {
|
else if (imageType == "miximage") {
|
||||||
path = games.front()->getMiximagePath();
|
path = games.at(gameSelectorEntry)->getMiximagePath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
video->setImage(path);
|
video->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "marquee") {
|
else if (imageType == "marquee") {
|
||||||
path = games.front()->getMarqueePath();
|
path = games.at(gameSelectorEntry)->getMarqueePath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
video->setImage(path);
|
video->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "screenshot") {
|
else if (imageType == "screenshot") {
|
||||||
path = games.front()->getScreenshotPath();
|
path = games.at(gameSelectorEntry)->getScreenshotPath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
video->setImage(path);
|
video->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "titlescreen") {
|
else if (imageType == "titlescreen") {
|
||||||
path = games.front()->getTitleScreenPath();
|
path = games.at(gameSelectorEntry)->getTitleScreenPath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
video->setImage(path);
|
video->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "cover") {
|
else if (imageType == "cover") {
|
||||||
path = games.front()->getCoverPath();
|
path = games.at(gameSelectorEntry)->getCoverPath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
video->setImage(path);
|
video->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "backcover") {
|
else if (imageType == "backcover") {
|
||||||
path = games.front()->getBackCoverPath();
|
path = games.at(gameSelectorEntry)->getBackCoverPath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
video->setImage(path);
|
video->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "3dbox") {
|
else if (imageType == "3dbox") {
|
||||||
path = games.front()->get3DBoxPath();
|
path = games.at(gameSelectorEntry)->get3DBoxPath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
video->setImage(path);
|
video->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "physicalmedia") {
|
else if (imageType == "physicalmedia") {
|
||||||
path = games.front()->getPhysicalMediaPath();
|
path = games.at(gameSelectorEntry)->getPhysicalMediaPath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
video->setImage(path);
|
video->setImage(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageType == "fanart") {
|
else if (imageType == "fanart") {
|
||||||
path = games.front()->getFanArtPath();
|
path = games.at(gameSelectorEntry)->getFanArtPath();
|
||||||
if (path != "") {
|
if (path != "") {
|
||||||
video->setImage(path);
|
video->setImage(path);
|
||||||
break;
|
break;
|
||||||
|
@ -1078,37 +1193,45 @@ void SystemView::updateGameSelectors()
|
||||||
else {
|
else {
|
||||||
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
||||||
}
|
}
|
||||||
|
const size_t gameSelectorEntry {static_cast<size_t>(
|
||||||
|
glm::clamp(text->getThemeGameSelectorEntry(), 0u,
|
||||||
|
static_cast<unsigned int>(gameSelector->getGameCount() - 1)))};
|
||||||
gameSelector->refreshGames();
|
gameSelector->refreshGames();
|
||||||
std::vector<FileData*> games {gameSelector->getGames()};
|
std::vector<FileData*> games {gameSelector->getGames()};
|
||||||
if (!games.empty()) {
|
if (games.size() > gameSelectorEntry) {
|
||||||
const std::string metadata {text->getThemeMetadata()};
|
const std::string metadata {text->getThemeMetadata()};
|
||||||
if (metadata == "name")
|
if (metadata == "name")
|
||||||
text->setValue(games.front()->metadata.get("name"));
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("name"));
|
||||||
if (metadata == "description")
|
if (metadata == "description")
|
||||||
text->setValue(games.front()->metadata.get("desc"));
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("desc"));
|
||||||
if (metadata == "rating")
|
if (metadata == "rating")
|
||||||
text->setValue(
|
text->setValue(RatingComponent::getRatingValue(
|
||||||
RatingComponent::getRatingValue(games.front()->metadata.get("rating")));
|
games.at(gameSelectorEntry)->metadata.get("rating")));
|
||||||
if (metadata == "developer")
|
if (metadata == "developer")
|
||||||
text->setValue(games.front()->metadata.get("developer"));
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("developer"));
|
||||||
if (metadata == "publisher")
|
if (metadata == "publisher")
|
||||||
text->setValue(games.front()->metadata.get("publisher"));
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("publisher"));
|
||||||
if (metadata == "genre")
|
if (metadata == "genre")
|
||||||
text->setValue(games.front()->metadata.get("genre"));
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("genre"));
|
||||||
if (metadata == "players")
|
if (metadata == "players")
|
||||||
text->setValue(games.front()->metadata.get("players"));
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("players"));
|
||||||
if (metadata == "favorite")
|
if (metadata == "favorite")
|
||||||
text->setValue(games.front()->metadata.get("favorite") == "true" ? "yes" : "no");
|
text->setValue(
|
||||||
|
games.at(gameSelectorEntry)->metadata.get("favorite") == "true" ? "yes" : "no");
|
||||||
if (metadata == "completed")
|
if (metadata == "completed")
|
||||||
text->setValue(games.front()->metadata.get("completed") == "true" ? "yes" : "no");
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("completed") == "true" ?
|
||||||
|
"yes" :
|
||||||
|
"no");
|
||||||
if (metadata == "kidgame")
|
if (metadata == "kidgame")
|
||||||
text->setValue(games.front()->metadata.get("kidgame") == "true" ? "yes" : "no");
|
text->setValue(
|
||||||
|
games.at(gameSelectorEntry)->metadata.get("kidgame") == "true" ? "yes" : "no");
|
||||||
if (metadata == "broken")
|
if (metadata == "broken")
|
||||||
text->setValue(games.front()->metadata.get("broken") == "true" ? "yes" : "no");
|
text->setValue(
|
||||||
|
games.at(gameSelectorEntry)->metadata.get("broken") == "true" ? "yes" : "no");
|
||||||
if (metadata == "playcount")
|
if (metadata == "playcount")
|
||||||
text->setValue(games.front()->metadata.get("playcount"));
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("playcount"));
|
||||||
if (metadata == "altemulator")
|
if (metadata == "altemulator")
|
||||||
text->setValue(games.front()->metadata.get("altemulator"));
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("altemulator"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
text->setValue("");
|
text->setValue("");
|
||||||
|
@ -1145,15 +1268,18 @@ void SystemView::updateGameSelectors()
|
||||||
else {
|
else {
|
||||||
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
||||||
}
|
}
|
||||||
|
const size_t gameSelectorEntry {static_cast<size_t>(
|
||||||
|
glm::clamp(dateTime->getThemeGameSelectorEntry(), 0u,
|
||||||
|
static_cast<unsigned int>(gameSelector->getGameCount() - 1)))};
|
||||||
gameSelector->refreshGames();
|
gameSelector->refreshGames();
|
||||||
std::vector<FileData*> games {gameSelector->getGames()};
|
std::vector<FileData*> games {gameSelector->getGames()};
|
||||||
if (!games.empty()) {
|
if (games.size() > gameSelectorEntry) {
|
||||||
dateTime->setVisible(true);
|
dateTime->setVisible(true);
|
||||||
const std::string metadata {dateTime->getThemeMetadata()};
|
const std::string metadata {dateTime->getThemeMetadata()};
|
||||||
if (metadata == "releasedate")
|
if (metadata == "releasedate")
|
||||||
dateTime->setValue(games.front()->metadata.get("releasedate"));
|
dateTime->setValue(games.at(gameSelectorEntry)->metadata.get("releasedate"));
|
||||||
if (metadata == "lastplayed")
|
if (metadata == "lastplayed")
|
||||||
dateTime->setValue(games.front()->metadata.get("lastplayed"));
|
dateTime->setValue(games.at(gameSelectorEntry)->metadata.get("lastplayed"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
dateTime->setVisible(false);
|
dateTime->setVisible(false);
|
||||||
|
@ -1189,11 +1315,14 @@ void SystemView::updateGameSelectors()
|
||||||
else {
|
else {
|
||||||
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
||||||
}
|
}
|
||||||
|
const size_t gameSelectorEntry {static_cast<size_t>(
|
||||||
|
glm::clamp(rating->getThemeGameSelectorEntry(), 0u,
|
||||||
|
static_cast<unsigned int>(gameSelector->getGameCount() - 1)))};
|
||||||
gameSelector->refreshGames();
|
gameSelector->refreshGames();
|
||||||
std::vector<FileData*> games {gameSelector->getGames()};
|
std::vector<FileData*> games {gameSelector->getGames()};
|
||||||
if (!games.empty()) {
|
if (games.size() > gameSelectorEntry) {
|
||||||
rating->setVisible(true);
|
rating->setVisible(true);
|
||||||
rating->setValue(games.front()->metadata.get("rating"));
|
rating->setValue(games.at(gameSelectorEntry)->metadata.get("rating"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rating->setVisible(false);
|
rating->setVisible(false);
|
||||||
|
@ -1241,7 +1370,7 @@ void SystemView::renderElements(const glm::mat4& parentTrans, bool abovePrimary)
|
||||||
int renderAfter {static_cast<int>(mCamOffset)};
|
int renderAfter {static_cast<int>(mCamOffset)};
|
||||||
|
|
||||||
// If we're transitioning then also render the previous and next systems.
|
// If we're transitioning then also render the previous and next systems.
|
||||||
if (mPrimary->isAnimationPlaying(0)) {
|
if (isAnimationPlaying(0)) {
|
||||||
renderBefore -= 1;
|
renderBefore -= 1;
|
||||||
renderAfter += 1;
|
renderAfter += 1;
|
||||||
}
|
}
|
||||||
|
@ -1253,7 +1382,7 @@ void SystemView::renderElements(const glm::mat4& parentTrans, bool abovePrimary)
|
||||||
while (index >= static_cast<int>(mPrimary->getNumEntries()))
|
while (index >= static_cast<int>(mPrimary->getNumEntries()))
|
||||||
index -= static_cast<int>(mPrimary->getNumEntries());
|
index -= static_cast<int>(mPrimary->getNumEntries());
|
||||||
|
|
||||||
if (mPrimary->isAnimationPlaying(0) || index == mPrimary->getCursor()) {
|
if (isAnimationPlaying(0) || index == mPrimary->getCursor()) {
|
||||||
glm::mat4 elementTrans {trans};
|
glm::mat4 elementTrans {trans};
|
||||||
if (mCarousel != nullptr) {
|
if (mCarousel != nullptr) {
|
||||||
if (mCarousel->getType() ==
|
if (mCarousel->getType() ==
|
||||||
|
@ -1268,6 +1397,10 @@ void SystemView::renderElements(const glm::mat4& parentTrans, bool abovePrimary)
|
||||||
elementTrans,
|
elementTrans,
|
||||||
glm::round(glm::vec3 {0.0f, (i - mCamOffset) * mSize.y, 0.0f}));
|
glm::round(glm::vec3 {0.0f, (i - mCamOffset) * mSize.y, 0.0f}));
|
||||||
}
|
}
|
||||||
|
else if (mGrid != nullptr) {
|
||||||
|
elementTrans = glm::translate(
|
||||||
|
elementTrans, glm::round(glm::vec3 {0.0f, (i - mCamOffset) * mSize.y, 0.0f}));
|
||||||
|
}
|
||||||
else if (mTextList != nullptr) {
|
else if (mTextList != nullptr) {
|
||||||
elementTrans = glm::translate(
|
elementTrans = glm::translate(
|
||||||
elementTrans, glm::round(glm::vec3 {0.0f, (i - mCamOffset) * mSize.y, 0.0f}));
|
elementTrans, glm::round(glm::vec3 {0.0f, (i - mCamOffset) * mSize.y, 0.0f}));
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "components/TextComponent.h"
|
#include "components/TextComponent.h"
|
||||||
#include "components/VideoFFmpegComponent.h"
|
#include "components/VideoFFmpegComponent.h"
|
||||||
#include "components/primary/CarouselComponent.h"
|
#include "components/primary/CarouselComponent.h"
|
||||||
|
#include "components/primary/GridComponent.h"
|
||||||
#include "components/primary/TextListComponent.h"
|
#include "components/primary/TextListComponent.h"
|
||||||
#include "resources/Font.h"
|
#include "resources/Font.h"
|
||||||
|
|
||||||
|
@ -125,6 +126,7 @@ private:
|
||||||
|
|
||||||
Renderer* mRenderer;
|
Renderer* mRenderer;
|
||||||
std::unique_ptr<CarouselComponent<SystemData*>> mCarousel;
|
std::unique_ptr<CarouselComponent<SystemData*>> mCarousel;
|
||||||
|
std::unique_ptr<GridComponent<SystemData*>> mGrid;
|
||||||
std::unique_ptr<TextListComponent<SystemData*>> mTextList;
|
std::unique_ptr<TextListComponent<SystemData*>> mTextList;
|
||||||
std::unique_ptr<TextComponent> mLegacySystemInfo;
|
std::unique_ptr<TextComponent> mLegacySystemInfo;
|
||||||
std::vector<SystemViewElements> mSystemElements;
|
std::vector<SystemViewElements> mSystemElements;
|
||||||
|
|
|
@ -33,6 +33,7 @@ set(CORE_HEADERS
|
||||||
|
|
||||||
# Primary GUI components
|
# Primary GUI components
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/primary/CarouselComponent.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/components/primary/CarouselComponent.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/components/primary/GridComponent.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/primary/PrimaryComponent.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/components/primary/PrimaryComponent.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/primary/TextListComponent.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/components/primary/TextListComponent.h
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
GuiComponent::GuiComponent()
|
GuiComponent::GuiComponent()
|
||||||
: mWindow {Window::getInstance()}
|
: mWindow {Window::getInstance()}
|
||||||
, mParent {nullptr}
|
, mParent {nullptr}
|
||||||
|
, mThemeGameSelectorEntry {0}
|
||||||
, mColor {0}
|
, mColor {0}
|
||||||
, mColorShift {0}
|
, mColorShift {0}
|
||||||
, mColorShiftEnd {0}
|
, mColorShiftEnd {0}
|
||||||
|
@ -29,6 +30,7 @@ GuiComponent::GuiComponent()
|
||||||
, mOrigin {0.0f, 0.0f}
|
, mOrigin {0.0f, 0.0f}
|
||||||
, mRotationOrigin {0.5f, 0.5f}
|
, mRotationOrigin {0.5f, 0.5f}
|
||||||
, mSize {0.0f, 0.0f}
|
, mSize {0.0f, 0.0f}
|
||||||
|
, mBrightness {0.0f}
|
||||||
, mOpacity {1.0f}
|
, mOpacity {1.0f}
|
||||||
, mSaturation {1.0f}
|
, mSaturation {1.0f}
|
||||||
, mDimming {1.0f}
|
, mDimming {1.0f}
|
||||||
|
@ -183,6 +185,16 @@ const int GuiComponent::getChildIndex() const
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GuiComponent::setBrightness(float brightness)
|
||||||
|
{
|
||||||
|
if (mBrightness == brightness)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mBrightness = brightness;
|
||||||
|
for (auto it = mChildren.cbegin(); it != mChildren.cend(); ++it)
|
||||||
|
(*it)->setBrightness(brightness);
|
||||||
|
}
|
||||||
|
|
||||||
void GuiComponent::setOpacity(float opacity)
|
void GuiComponent::setOpacity(float opacity)
|
||||||
{
|
{
|
||||||
if (mOpacity == opacity)
|
if (mOpacity == opacity)
|
||||||
|
@ -371,6 +383,9 @@ void GuiComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
else
|
else
|
||||||
setZIndex(getDefaultZIndex());
|
setZIndex(getDefaultZIndex());
|
||||||
|
|
||||||
|
if (properties & ThemeFlags::BRIGHTNESS && elem->has("brightness"))
|
||||||
|
mBrightness = glm::clamp(elem->get<float>("brightness"), -2.0f, 2.0f);
|
||||||
|
|
||||||
if (properties & ThemeFlags::OPACITY && elem->has("opacity"))
|
if (properties & ThemeFlags::OPACITY && elem->has("opacity"))
|
||||||
mThemeOpacity = glm::clamp(elem->get<float>("opacity"), 0.0f, 1.0f);
|
mThemeOpacity = glm::clamp(elem->get<float>("opacity"), 0.0f, 1.0f);
|
||||||
|
|
||||||
|
@ -384,6 +399,9 @@ void GuiComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
|
|
||||||
if (properties && elem->has("gameselector"))
|
if (properties && elem->has("gameselector"))
|
||||||
mThemeGameSelector = elem->get<std::string>("gameselector");
|
mThemeGameSelector = elem->get<std::string>("gameselector");
|
||||||
|
|
||||||
|
if (properties && elem->has("gameselectorEntry"))
|
||||||
|
mThemeGameSelectorEntry = elem->get<unsigned int>("gameselectorEntry");
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiComponent::updateHelpPrompts()
|
void GuiComponent::updateHelpPrompts()
|
||||||
|
|
|
@ -104,8 +104,8 @@ public:
|
||||||
virtual glm::vec2 getSize() const { return mSize; }
|
virtual glm::vec2 getSize() const { return mSize; }
|
||||||
void setSize(const glm::vec2& size) { setSize(size.x, size.y); }
|
void setSize(const glm::vec2& size) { setSize(size.x, size.y); }
|
||||||
void setSize(const float w, const float h);
|
void setSize(const float w, const float h);
|
||||||
virtual void setResize(float width, float height) {}
|
virtual void setResize(const float width, const float height) {}
|
||||||
virtual void setResize(float width, float height, bool rasterize) {}
|
virtual void setResize(const glm::vec2& size, bool rasterize = true) {}
|
||||||
virtual void onSizeChanged() {}
|
virtual void onSizeChanged() {}
|
||||||
|
|
||||||
virtual glm::vec2 getRotationSize() const { return getSize(); }
|
virtual glm::vec2 getRotationSize() const { return getSize(); }
|
||||||
|
@ -222,8 +222,10 @@ public:
|
||||||
virtual void stopGamelistFadeAnimations() {}
|
virtual void stopGamelistFadeAnimations() {}
|
||||||
virtual bool isListScrolling() { return false; }
|
virtual bool isListScrolling() { return false; }
|
||||||
virtual void stopListScrolling() {}
|
virtual void stopListScrolling() {}
|
||||||
|
virtual const float getBrightness() const { return mBrightness; }
|
||||||
virtual const float getOpacity() const { return mOpacity; }
|
virtual const float getOpacity() const { return mOpacity; }
|
||||||
virtual const float getColorOpacity() const { return 1.0f; }
|
virtual const float getColorOpacity() const { return 1.0f; }
|
||||||
|
virtual void setBrightness(float brightness);
|
||||||
virtual void setOpacity(float opacity);
|
virtual void setOpacity(float opacity);
|
||||||
virtual float getSaturation() const { return static_cast<float>(mColor); }
|
virtual float getSaturation() const { return static_cast<float>(mColor); }
|
||||||
virtual void setSaturation(float saturation) { mSaturation = saturation; }
|
virtual void setSaturation(float saturation) { mSaturation = saturation; }
|
||||||
|
@ -257,7 +259,8 @@ public:
|
||||||
const std::string& getThemeMetadata() { return mThemeMetadata; }
|
const std::string& getThemeMetadata() { return mThemeMetadata; }
|
||||||
void setThemeMetadata(const std::string& text) { mThemeMetadata = text; }
|
void setThemeMetadata(const std::string& text) { mThemeMetadata = text; }
|
||||||
const std::vector<std::string>& getThemeImageTypes() { return mThemeImageTypes; }
|
const std::vector<std::string>& getThemeImageTypes() { return mThemeImageTypes; }
|
||||||
const std::string& getThemeGameSelector() { return mThemeGameSelector; }
|
const std::string& getThemeGameSelector() const { return mThemeGameSelector; }
|
||||||
|
const unsigned int getThemeGameSelectorEntry() const { return mThemeGameSelectorEntry; }
|
||||||
|
|
||||||
virtual std::shared_ptr<Font> getFont() const { return nullptr; }
|
virtual std::shared_ptr<Font> getFont() const { return nullptr; }
|
||||||
|
|
||||||
|
@ -323,6 +326,7 @@ protected:
|
||||||
std::string mThemeSystemdata;
|
std::string mThemeSystemdata;
|
||||||
std::string mThemeMetadata;
|
std::string mThemeMetadata;
|
||||||
std::string mThemeGameSelector;
|
std::string mThemeGameSelector;
|
||||||
|
unsigned int mThemeGameSelectorEntry;
|
||||||
|
|
||||||
unsigned int mColor;
|
unsigned int mColor;
|
||||||
unsigned int mColorShift;
|
unsigned int mColorShift;
|
||||||
|
@ -336,6 +340,7 @@ protected:
|
||||||
glm::vec2 mRotationOrigin;
|
glm::vec2 mRotationOrigin;
|
||||||
glm::vec2 mSize;
|
glm::vec2 mSize;
|
||||||
|
|
||||||
|
float mBrightness;
|
||||||
float mOpacity;
|
float mOpacity;
|
||||||
float mSaturation;
|
float mSaturation;
|
||||||
float mDimming;
|
float mDimming;
|
||||||
|
|
|
@ -63,7 +63,7 @@ void HelpStyle::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::s
|
||||||
iconColorDimmed = iconColor;
|
iconColorDimmed = iconColor;
|
||||||
|
|
||||||
if (elem->has("fontPath") || elem->has("fontSize"))
|
if (elem->has("fontPath") || elem->has("fontSize"))
|
||||||
font = Font::getFromTheme(elem, ThemeFlags::ALL, font, 0.0f, theme->isLegacyTheme());
|
font = Font::getFromTheme(elem, ThemeFlags::ALL, font, 0.0f, false, theme->isLegacyTheme());
|
||||||
|
|
||||||
if (elem->has("entrySpacing"))
|
if (elem->has("entrySpacing"))
|
||||||
entrySpacing = glm::clamp(elem->get<float>("entrySpacing"), 0.0f, 0.04f);
|
entrySpacing = glm::clamp(elem->get<float>("entrySpacing"), 0.0f, 0.04f);
|
||||||
|
@ -100,6 +100,8 @@ void HelpStyle::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::s
|
||||||
mCustomButtons.button_lt = elem->get<std::string>(PREFIX "button_lt");
|
mCustomButtons.button_lt = elem->get<std::string>(PREFIX "button_lt");
|
||||||
if (elem->has(PREFIX "button_rt"))
|
if (elem->has(PREFIX "button_rt"))
|
||||||
mCustomButtons.button_rt = elem->get<std::string>(PREFIX "button_rt");
|
mCustomButtons.button_rt = elem->get<std::string>(PREFIX "button_rt");
|
||||||
|
if (elem->has(PREFIX "button_ltrt"))
|
||||||
|
mCustomButtons.button_ltrt = elem->get<std::string>(PREFIX "button_ltrt");
|
||||||
|
|
||||||
// SNES.
|
// SNES.
|
||||||
if (elem->has(PREFIX "button_a_SNES"))
|
if (elem->has(PREFIX "button_a_SNES"))
|
||||||
|
|
|
@ -42,6 +42,7 @@ struct HelpStyle {
|
||||||
std::string button_lr;
|
std::string button_lr;
|
||||||
std::string button_lt;
|
std::string button_lt;
|
||||||
std::string button_rt;
|
std::string button_rt;
|
||||||
|
std::string button_ltrt;
|
||||||
|
|
||||||
// SNES
|
// SNES
|
||||||
std::string button_a_SNES;
|
std::string button_a_SNES;
|
||||||
|
|
|
@ -88,7 +88,8 @@ void Settings::setDefaults()
|
||||||
|
|
||||||
mBoolMap["ScrapeGameNames"] = {true, true};
|
mBoolMap["ScrapeGameNames"] = {true, true};
|
||||||
mBoolMap["ScrapeRatings"] = {true, true};
|
mBoolMap["ScrapeRatings"] = {true, true};
|
||||||
mBoolMap["ScrapeControllers"] = {true, true};
|
// ScreenScraper controller scraping is currently broken, it's unclear if they will fix it.
|
||||||
|
// mBoolMap["ScrapeControllers"] = {true, true};
|
||||||
mBoolMap["ScrapeMetadata"] = {true, true};
|
mBoolMap["ScrapeMetadata"] = {true, true};
|
||||||
mBoolMap["ScrapeVideos"] = {true, true};
|
mBoolMap["ScrapeVideos"] = {true, true};
|
||||||
mBoolMap["ScrapeScreenshots"] = {true, true};
|
mBoolMap["ScrapeScreenshots"] = {true, true};
|
||||||
|
@ -136,6 +137,7 @@ void Settings::setDefaults()
|
||||||
mStringMap["ThemeAspectRatio"] = {"", ""};
|
mStringMap["ThemeAspectRatio"] = {"", ""};
|
||||||
mStringMap["GamelistViewStyle"] = {"automatic", "automatic"};
|
mStringMap["GamelistViewStyle"] = {"automatic", "automatic"};
|
||||||
mStringMap["TransitionStyle"] = {"slide", "slide"};
|
mStringMap["TransitionStyle"] = {"slide", "slide"};
|
||||||
|
mStringMap["QuickSystemSelect"] = {"leftrightshoulders", "leftrightshoulders"};
|
||||||
mStringMap["StartupSystem"] = {"", ""};
|
mStringMap["StartupSystem"] = {"", ""};
|
||||||
mStringMap["DefaultSortOrder"] = {"filename, ascending", "filename, ascending"};
|
mStringMap["DefaultSortOrder"] = {"filename, ascending", "filename, ascending"};
|
||||||
mStringMap["MenuOpeningEffect"] = {"scale-up", "scale-up"};
|
mStringMap["MenuOpeningEffect"] = {"scale-up", "scale-up"};
|
||||||
|
@ -190,7 +192,6 @@ void Settings::setDefaults()
|
||||||
mBoolMap["FavoritesAddButton"] = {true, true};
|
mBoolMap["FavoritesAddButton"] = {true, true};
|
||||||
mBoolMap["RandomAddButton"] = {false, false};
|
mBoolMap["RandomAddButton"] = {false, false};
|
||||||
mBoolMap["GamelistFilters"] = {true, true};
|
mBoolMap["GamelistFilters"] = {true, true};
|
||||||
mBoolMap["QuickSystemSelect"] = {true, true};
|
|
||||||
mBoolMap["ShowHelpPrompts"] = {true, true};
|
mBoolMap["ShowHelpPrompts"] = {true, true};
|
||||||
|
|
||||||
// Sound settings.
|
// Sound settings.
|
||||||
|
|
|
@ -43,7 +43,7 @@ std::vector<std::string> ThemeData::sLegacySupportedFeatures {
|
||||||
{"z-index"},
|
{"z-index"},
|
||||||
{"visible"}};
|
{"visible"}};
|
||||||
|
|
||||||
std::vector<std::string> ThemeData::sLegacyElements {
|
std::vector<std::string> ThemeData::sLegacyProperties {
|
||||||
{"showSnapshotNoVideo"},
|
{"showSnapshotNoVideo"},
|
||||||
{"showSnapshotDelay"},
|
{"showSnapshotDelay"},
|
||||||
{"forceUppercase"},
|
{"forceUppercase"},
|
||||||
|
@ -56,6 +56,12 @@ std::vector<std::string> ThemeData::sLegacyElements {
|
||||||
{"logoAlignment"},
|
{"logoAlignment"},
|
||||||
{"maxLogoCount"}};
|
{"maxLogoCount"}};
|
||||||
|
|
||||||
|
std::vector<std::string> ThemeData::sDeprecatedProperties {
|
||||||
|
{"staticItem"},
|
||||||
|
{"itemType"},
|
||||||
|
{"defaultItem"},
|
||||||
|
{"itemInterpolation"}};
|
||||||
|
|
||||||
std::vector<std::pair<std::string, std::string>> ThemeData::sSupportedAspectRatios {
|
std::vector<std::pair<std::string, std::string>> ThemeData::sSupportedAspectRatios {
|
||||||
{"automatic", "automatic"},
|
{"automatic", "automatic"},
|
||||||
{"16:9", "16:9"},
|
{"16:9", "16:9"},
|
||||||
|
@ -101,6 +107,140 @@ std::map<std::string, std::map<std::string, std::string>> ThemeData::sPropertyAt
|
||||||
|
|
||||||
std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
|
std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
|
||||||
ThemeData::sElementMap {
|
ThemeData::sElementMap {
|
||||||
|
{"carousel",
|
||||||
|
{{"pos", NORMALIZED_PAIR},
|
||||||
|
{"size", NORMALIZED_PAIR},
|
||||||
|
{"origin", NORMALIZED_PAIR},
|
||||||
|
{"type", STRING},
|
||||||
|
{"staticImage", PATH},
|
||||||
|
{"imageType", STRING},
|
||||||
|
{"defaultImage", PATH},
|
||||||
|
{"staticItem", PATH}, // TEMPORARY: For backward compatibility.
|
||||||
|
{"itemType", STRING}, // TEMPORARY: For backward compatibility.
|
||||||
|
{"defaultItem", PATH}, // TEMPORARY: For backward compatibility.
|
||||||
|
{"maxItemCount", FLOAT},
|
||||||
|
{"maxLogoCount", FLOAT}, // For backward compatibility with legacy themes.
|
||||||
|
{"itemsBeforeCenter", UNSIGNED_INTEGER},
|
||||||
|
{"itemsAfterCenter", UNSIGNED_INTEGER},
|
||||||
|
{"itemStacking", STRING},
|
||||||
|
{"itemSize", NORMALIZED_PAIR},
|
||||||
|
{"itemScale", FLOAT},
|
||||||
|
{"itemRotation", FLOAT},
|
||||||
|
{"itemRotationOrigin", NORMALIZED_PAIR},
|
||||||
|
{"itemAxisHorizontal", BOOLEAN},
|
||||||
|
{"itemInterpolation", STRING}, // TEMPORARY: For backward compatibility.
|
||||||
|
{"imageInterpolation", STRING},
|
||||||
|
{"imageColor", COLOR},
|
||||||
|
{"imageColorEnd", COLOR},
|
||||||
|
{"imageGradientType", STRING},
|
||||||
|
{"imageBrightness", FLOAT},
|
||||||
|
{"imageSaturation", FLOAT},
|
||||||
|
{"itemTransitions", STRING},
|
||||||
|
{"itemHorizontalAlignment", STRING},
|
||||||
|
{"itemVerticalAlignment", STRING},
|
||||||
|
{"wheelHorizontalAlignment", STRING},
|
||||||
|
{"horizontalOffset", FLOAT},
|
||||||
|
{"verticalOffset", FLOAT},
|
||||||
|
{"reflections", BOOLEAN},
|
||||||
|
{"reflectionsOpacity", FLOAT},
|
||||||
|
{"reflectionsFalloff", FLOAT},
|
||||||
|
{"unfocusedItemOpacity", FLOAT},
|
||||||
|
{"defaultLogo", PATH}, // For backward compatibility with legacy themes.
|
||||||
|
{"logoSize", NORMALIZED_PAIR}, // For backward compatibility with legacy themes.
|
||||||
|
{"logoScale", FLOAT}, // For backward compatibility with legacy themes.
|
||||||
|
{"logoRotation", FLOAT}, // For backward compatibility with legacy themes.
|
||||||
|
{"logoRotationOrigin", NORMALIZED_PAIR}, // For backward compatibility with legacy themes.
|
||||||
|
{"logoAlignment", STRING}, // For backward compatibility with legacy themes.
|
||||||
|
{"color", COLOR},
|
||||||
|
{"colorEnd", COLOR},
|
||||||
|
{"gradientType", STRING},
|
||||||
|
{"text", STRING},
|
||||||
|
{"textColor", COLOR},
|
||||||
|
{"textBackgroundColor", COLOR},
|
||||||
|
{"fontPath", PATH},
|
||||||
|
{"fontSize", FLOAT},
|
||||||
|
{"letterCase", STRING},
|
||||||
|
{"letterCaseCollections", STRING},
|
||||||
|
{"letterCaseGroupedCollections", STRING},
|
||||||
|
{"lineSpacing", FLOAT},
|
||||||
|
{"fadeAbovePrimary", BOOLEAN},
|
||||||
|
{"zIndex", FLOAT},
|
||||||
|
{"legacyZIndexMode", STRING}}}, // For backward compatibility with legacy themes.
|
||||||
|
{"grid",
|
||||||
|
{{"pos", NORMALIZED_PAIR},
|
||||||
|
{"size", NORMALIZED_PAIR},
|
||||||
|
{"origin", NORMALIZED_PAIR},
|
||||||
|
{"staticImage", PATH},
|
||||||
|
{"imageType", STRING},
|
||||||
|
{"defaultImage", PATH},
|
||||||
|
{"itemSize", NORMALIZED_PAIR},
|
||||||
|
{"itemScale", FLOAT},
|
||||||
|
{"itemSpacing", NORMALIZED_PAIR},
|
||||||
|
{"fractionalRows", BOOLEAN},
|
||||||
|
{"itemTransitions", STRING},
|
||||||
|
{"rowTransitions", STRING},
|
||||||
|
{"unfocusedItemOpacity", FLOAT},
|
||||||
|
{"edgeScaleInwards", BOOLEAN}, // TODO
|
||||||
|
{"imageFit", STRING},
|
||||||
|
{"imageRelativeScale", FLOAT},
|
||||||
|
{"imageColor", COLOR},
|
||||||
|
{"imageColorEnd", COLOR},
|
||||||
|
{"imageGradientType", STRING},
|
||||||
|
{"imageBrightness", FLOAT},
|
||||||
|
{"imageSaturation", FLOAT},
|
||||||
|
{"backgroundImage", PATH},
|
||||||
|
{"backgroundRelativeScale", FLOAT},
|
||||||
|
{"backgroundColor", COLOR},
|
||||||
|
{"backgroundColorEnd", COLOR},
|
||||||
|
{"backgroundGradientType", STRING},
|
||||||
|
{"selectorImage", PATH},
|
||||||
|
{"selectorRelativeScale", FLOAT},
|
||||||
|
{"selectorLayer", STRING},
|
||||||
|
{"selectorColor", COLOR},
|
||||||
|
{"selectorColorEnd", COLOR},
|
||||||
|
{"selectorGradientType", STRING},
|
||||||
|
{"text", STRING},
|
||||||
|
{"textRelativeScale", FLOAT},
|
||||||
|
{"textColor", COLOR},
|
||||||
|
{"textBackgroundColor", COLOR},
|
||||||
|
{"fontPath", PATH},
|
||||||
|
{"fontSize", FLOAT},
|
||||||
|
{"letterCase", STRING},
|
||||||
|
{"letterCaseCollections", STRING},
|
||||||
|
{"letterCaseGroupedCollections", STRING},
|
||||||
|
{"lineSpacing", FLOAT},
|
||||||
|
{"fadeAbovePrimary", BOOLEAN},
|
||||||
|
{"zIndex", FLOAT}}},
|
||||||
|
{"textlist",
|
||||||
|
{{"pos", NORMALIZED_PAIR},
|
||||||
|
{"size", NORMALIZED_PAIR},
|
||||||
|
{"origin", NORMALIZED_PAIR},
|
||||||
|
{"selectorHeight", FLOAT},
|
||||||
|
{"selectorOffsetY", FLOAT},
|
||||||
|
{"selectorColor", COLOR},
|
||||||
|
{"selectorColorEnd", COLOR},
|
||||||
|
{"selectorGradientType", STRING},
|
||||||
|
{"selectorImagePath", PATH},
|
||||||
|
{"selectorImageTile", BOOLEAN},
|
||||||
|
{"primaryColor", COLOR},
|
||||||
|
{"secondaryColor", COLOR},
|
||||||
|
{"selectedColor", COLOR},
|
||||||
|
{"selectedSecondaryColor", COLOR},
|
||||||
|
{"fontPath", PATH},
|
||||||
|
{"fontSize", FLOAT},
|
||||||
|
{"scrollSound", PATH}, // For backward compatibility with legacy themes.
|
||||||
|
{"horizontalAlignment", STRING},
|
||||||
|
{"alignment", STRING}, // For backward compatibility with legacy themes.
|
||||||
|
{"horizontalMargin", FLOAT},
|
||||||
|
{"letterCase", STRING},
|
||||||
|
{"letterCaseCollections", STRING},
|
||||||
|
{"letterCaseGroupedCollections", STRING},
|
||||||
|
{"forceUppercase", BOOLEAN}, // For backward compatibility with legacy themes.
|
||||||
|
{"lineSpacing", FLOAT},
|
||||||
|
{"indicators", STRING},
|
||||||
|
{"collectionIndicators", STRING},
|
||||||
|
{"fadeAbovePrimary", BOOLEAN},
|
||||||
|
{"zIndex", FLOAT}}},
|
||||||
{"image",
|
{"image",
|
||||||
{{"pos", NORMALIZED_PAIR},
|
{{"pos", NORMALIZED_PAIR},
|
||||||
{"size", NORMALIZED_PAIR},
|
{"size", NORMALIZED_PAIR},
|
||||||
|
@ -113,6 +253,7 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
|
||||||
{"imageType", STRING},
|
{"imageType", STRING},
|
||||||
{"metadataElement", BOOLEAN},
|
{"metadataElement", BOOLEAN},
|
||||||
{"gameselector", STRING},
|
{"gameselector", STRING},
|
||||||
|
{"gameselectorEntry", UNSIGNED_INTEGER},
|
||||||
{"tile", BOOLEAN},
|
{"tile", BOOLEAN},
|
||||||
{"tileSize", NORMALIZED_PAIR},
|
{"tileSize", NORMALIZED_PAIR},
|
||||||
{"tileHorizontalAlignment", STRING},
|
{"tileHorizontalAlignment", STRING},
|
||||||
|
@ -122,6 +263,7 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
|
||||||
{"colorEnd", COLOR},
|
{"colorEnd", COLOR},
|
||||||
{"gradientType", STRING},
|
{"gradientType", STRING},
|
||||||
{"scrollFadeIn", BOOLEAN},
|
{"scrollFadeIn", BOOLEAN},
|
||||||
|
{"brightness", FLOAT},
|
||||||
{"opacity", FLOAT},
|
{"opacity", FLOAT},
|
||||||
{"saturation", FLOAT},
|
{"saturation", FLOAT},
|
||||||
{"visible", BOOLEAN},
|
{"visible", BOOLEAN},
|
||||||
|
@ -137,14 +279,19 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
|
||||||
{"imageType", STRING},
|
{"imageType", STRING},
|
||||||
{"metadataElement", BOOLEAN},
|
{"metadataElement", BOOLEAN},
|
||||||
{"gameselector", STRING},
|
{"gameselector", STRING},
|
||||||
|
{"gameselectorEntry", UNSIGNED_INTEGER},
|
||||||
{"audio", BOOLEAN},
|
{"audio", BOOLEAN},
|
||||||
{"interpolation", STRING},
|
{"interpolation", STRING},
|
||||||
|
{"color", COLOR},
|
||||||
|
{"colorEnd", COLOR},
|
||||||
|
{"gradientType", STRING},
|
||||||
{"pillarboxes", BOOLEAN},
|
{"pillarboxes", BOOLEAN},
|
||||||
{"pillarboxThreshold", NORMALIZED_PAIR},
|
{"pillarboxThreshold", NORMALIZED_PAIR},
|
||||||
{"scanlines", BOOLEAN},
|
{"scanlines", BOOLEAN},
|
||||||
{"delay", FLOAT},
|
{"delay", FLOAT},
|
||||||
{"fadeInTime", FLOAT},
|
{"fadeInTime", FLOAT},
|
||||||
{"scrollFadeIn", BOOLEAN},
|
{"scrollFadeIn", BOOLEAN},
|
||||||
|
{"brightness", FLOAT},
|
||||||
{"opacity", FLOAT},
|
{"opacity", FLOAT},
|
||||||
{"saturation", FLOAT},
|
{"saturation", FLOAT},
|
||||||
{"visible", BOOLEAN},
|
{"visible", BOOLEAN},
|
||||||
|
@ -163,6 +310,7 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
|
||||||
{"direction", STRING},
|
{"direction", STRING},
|
||||||
{"keepAspectRatio", BOOLEAN},
|
{"keepAspectRatio", BOOLEAN},
|
||||||
{"interpolation", STRING},
|
{"interpolation", STRING},
|
||||||
|
{"brightness", FLOAT},
|
||||||
{"opacity", FLOAT},
|
{"opacity", FLOAT},
|
||||||
{"saturation", FLOAT},
|
{"saturation", FLOAT},
|
||||||
{"visible", BOOLEAN},
|
{"visible", BOOLEAN},
|
||||||
|
@ -201,6 +349,7 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
|
||||||
{"metadata", STRING},
|
{"metadata", STRING},
|
||||||
{"metadataElement", BOOLEAN},
|
{"metadataElement", BOOLEAN},
|
||||||
{"gameselector", STRING},
|
{"gameselector", STRING},
|
||||||
|
{"gameselectorEntry", UNSIGNED_INTEGER},
|
||||||
{"container", BOOLEAN},
|
{"container", BOOLEAN},
|
||||||
{"containerVerticalSnap", BOOLEAN},
|
{"containerVerticalSnap", BOOLEAN},
|
||||||
{"containerScrollSpeed", FLOAT},
|
{"containerScrollSpeed", FLOAT},
|
||||||
|
@ -227,6 +376,7 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
|
||||||
{"rotationOrigin", NORMALIZED_PAIR},
|
{"rotationOrigin", NORMALIZED_PAIR},
|
||||||
{"metadata", STRING},
|
{"metadata", STRING},
|
||||||
{"gameselector", STRING},
|
{"gameselector", STRING},
|
||||||
|
{"gameselectorEntry", UNSIGNED_INTEGER},
|
||||||
{"fontPath", PATH},
|
{"fontPath", PATH},
|
||||||
{"fontSize", FLOAT},
|
{"fontSize", FLOAT},
|
||||||
{"horizontalAlignment", STRING},
|
{"horizontalAlignment", STRING},
|
||||||
|
@ -265,6 +415,7 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
|
||||||
{"rotation", FLOAT},
|
{"rotation", FLOAT},
|
||||||
{"rotationOrigin", NORMALIZED_PAIR},
|
{"rotationOrigin", NORMALIZED_PAIR},
|
||||||
{"gameselector", STRING},
|
{"gameselector", STRING},
|
||||||
|
{"gameselectorEntry", UNSIGNED_INTEGER},
|
||||||
{"interpolation", STRING},
|
{"interpolation", STRING},
|
||||||
{"color", COLOR},
|
{"color", COLOR},
|
||||||
{"filledPath", PATH},
|
{"filledPath", PATH},
|
||||||
|
@ -273,85 +424,6 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
|
||||||
{"opacity", FLOAT},
|
{"opacity", FLOAT},
|
||||||
{"visible", BOOLEAN},
|
{"visible", BOOLEAN},
|
||||||
{"zIndex", FLOAT}}},
|
{"zIndex", FLOAT}}},
|
||||||
{"carousel",
|
|
||||||
{{"pos", NORMALIZED_PAIR},
|
|
||||||
{"size", NORMALIZED_PAIR},
|
|
||||||
{"origin", NORMALIZED_PAIR},
|
|
||||||
{"type", STRING},
|
|
||||||
{"staticItem", PATH},
|
|
||||||
{"itemType", STRING},
|
|
||||||
{"defaultItem", PATH},
|
|
||||||
{"maxItemCount", FLOAT},
|
|
||||||
{"maxLogoCount", FLOAT}, // For backward compatibility with legacy themes.
|
|
||||||
{"itemsBeforeCenter", UNSIGNED_INTEGER},
|
|
||||||
{"itemsAfterCenter", UNSIGNED_INTEGER},
|
|
||||||
{"itemSize", NORMALIZED_PAIR},
|
|
||||||
{"itemScale", FLOAT},
|
|
||||||
{"itemTransitions", STRING},
|
|
||||||
{"itemInterpolation", STRING},
|
|
||||||
{"itemRotation", FLOAT},
|
|
||||||
{"itemRotationOrigin", NORMALIZED_PAIR},
|
|
||||||
{"itemAxisHorizontal", BOOLEAN},
|
|
||||||
{"itemHorizontalAlignment", STRING},
|
|
||||||
{"itemVerticalAlignment", STRING},
|
|
||||||
{"wheelHorizontalAlignment", STRING},
|
|
||||||
{"horizontalOffset", FLOAT},
|
|
||||||
{"verticalOffset", FLOAT},
|
|
||||||
{"reflections", BOOLEAN},
|
|
||||||
{"reflectionsOpacity", FLOAT},
|
|
||||||
{"reflectionsFalloff", FLOAT},
|
|
||||||
{"unfocusedItemOpacity", FLOAT},
|
|
||||||
{"defaultLogo", PATH}, // For backward compatibility with legacy themes.
|
|
||||||
{"logoSize", NORMALIZED_PAIR}, // For backward compatibility with legacy themes.
|
|
||||||
{"logoScale", FLOAT}, // For backward compatibility with legacy themes.
|
|
||||||
{"logoRotation", FLOAT}, // For backward compatibility with legacy themes.
|
|
||||||
{"logoRotationOrigin", NORMALIZED_PAIR}, // For backward compatibility with legacy themes.
|
|
||||||
{"logoAlignment", STRING}, // For backward compatibility with legacy themes.
|
|
||||||
{"color", COLOR},
|
|
||||||
{"colorEnd", COLOR},
|
|
||||||
{"gradientType", STRING},
|
|
||||||
{"text", STRING},
|
|
||||||
{"textColor", COLOR},
|
|
||||||
{"textBackgroundColor", COLOR},
|
|
||||||
{"fontPath", PATH},
|
|
||||||
{"fontSize", FLOAT},
|
|
||||||
{"letterCase", STRING},
|
|
||||||
{"letterCaseCollections", STRING},
|
|
||||||
{"letterCaseGroupedCollections", STRING},
|
|
||||||
{"lineSpacing", FLOAT},
|
|
||||||
{"fadeAbovePrimary", BOOLEAN},
|
|
||||||
{"zIndex", FLOAT},
|
|
||||||
{"legacyZIndexMode", STRING}}}, // For backward compatibility with legacy themes.
|
|
||||||
{"textlist",
|
|
||||||
{{"pos", NORMALIZED_PAIR},
|
|
||||||
{"size", NORMALIZED_PAIR},
|
|
||||||
{"origin", NORMALIZED_PAIR},
|
|
||||||
{"selectorHeight", FLOAT},
|
|
||||||
{"selectorOffsetY", FLOAT},
|
|
||||||
{"selectorColor", COLOR},
|
|
||||||
{"selectorColorEnd", COLOR},
|
|
||||||
{"selectorGradientType", STRING},
|
|
||||||
{"selectorImagePath", PATH},
|
|
||||||
{"selectorImageTile", BOOLEAN},
|
|
||||||
{"primaryColor", COLOR},
|
|
||||||
{"secondaryColor", COLOR},
|
|
||||||
{"selectedColor", COLOR},
|
|
||||||
{"selectedSecondaryColor", COLOR},
|
|
||||||
{"fontPath", PATH},
|
|
||||||
{"fontSize", FLOAT},
|
|
||||||
{"scrollSound", PATH}, // For backward compatibility with legacy themes.
|
|
||||||
{"horizontalAlignment", STRING},
|
|
||||||
{"alignment", STRING}, // For backward compatibility with legacy themes.
|
|
||||||
{"horizontalMargin", FLOAT},
|
|
||||||
{"letterCase", STRING},
|
|
||||||
{"letterCaseCollections", STRING},
|
|
||||||
{"letterCaseGroupedCollections", STRING},
|
|
||||||
{"forceUppercase", BOOLEAN}, // For backward compatibility with legacy themes.
|
|
||||||
{"lineSpacing", FLOAT},
|
|
||||||
{"indicators", STRING},
|
|
||||||
{"collectionIndicators", STRING},
|
|
||||||
{"fadeAbovePrimary", BOOLEAN},
|
|
||||||
{"zIndex", FLOAT}}},
|
|
||||||
{"gameselector",
|
{"gameselector",
|
||||||
{{"selection", STRING},
|
{{"selection", STRING},
|
||||||
{"gameCount", UNSIGNED_INTEGER}}},
|
{"gameCount", UNSIGNED_INTEGER}}},
|
||||||
|
@ -370,8 +442,6 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
|
||||||
{"textStyle", STRING}, // For backward compatibility with legacy themes.
|
{"textStyle", STRING}, // For backward compatibility with legacy themes.
|
||||||
{"opacity", FLOAT},
|
{"opacity", FLOAT},
|
||||||
{"customButtonIcon", PATH}}},
|
{"customButtonIcon", PATH}}},
|
||||||
{"sound",
|
|
||||||
{{"path", PATH}}},
|
|
||||||
{"navigationsounds",
|
{"navigationsounds",
|
||||||
{{"systembrowseSound", PATH},
|
{{"systembrowseSound", PATH},
|
||||||
{"quicksysselectSound", PATH},
|
{"quicksysselectSound", PATH},
|
||||||
|
@ -381,6 +451,8 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
|
||||||
{"favoriteSound", PATH},
|
{"favoriteSound", PATH},
|
||||||
{"launchSound", PATH}}},
|
{"launchSound", PATH}}},
|
||||||
// Legacy components below, not in use any longer but needed for backward compatibility.
|
// Legacy components below, not in use any longer but needed for backward compatibility.
|
||||||
|
{"sound",
|
||||||
|
{{"path", PATH}}},
|
||||||
{"imagegrid",
|
{"imagegrid",
|
||||||
{{"pos", NORMALIZED_PAIR},
|
{{"pos", NORMALIZED_PAIR},
|
||||||
{"size", NORMALIZED_PAIR},
|
{"size", NORMALIZED_PAIR},
|
||||||
|
@ -1415,17 +1487,29 @@ void ThemeData::parseElement(const pugi::xml_node& root,
|
||||||
|
|
||||||
std::string nodeName = node.name();
|
std::string nodeName = node.name();
|
||||||
|
|
||||||
// Strictly enforce removal of legacy elements for non-legacy theme sets by creating
|
// Strictly enforce removal of legacy properties for non-legacy theme sets by creating
|
||||||
// an unthemed system if they're present in the configuration.
|
// an unthemed system if they're present in the configuration.
|
||||||
if (!mLegacyTheme) {
|
if (!mLegacyTheme) {
|
||||||
for (auto& legacyElement : sLegacyElements) {
|
for (auto& legacyProperty : sLegacyProperties) {
|
||||||
if (nodeName == legacyElement) {
|
if (nodeName == legacyProperty) {
|
||||||
throw error << ": Legacy <" << nodeName
|
throw error << ": Legacy <" << nodeName
|
||||||
<< "> property found for non-legacy theme set";
|
<< "> property found for non-legacy theme set";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Print a warning if a deprecated property is used for a non-legacy theme set.
|
||||||
|
if (!mLegacyTheme) {
|
||||||
|
for (auto& deprecatedProperty : sDeprecatedProperties) {
|
||||||
|
if (nodeName == deprecatedProperty) {
|
||||||
|
LOG(LogWarning)
|
||||||
|
<< "ThemeData::parseElement(): Property \"" << deprecatedProperty
|
||||||
|
<< "\" is deprecated and support for it will be removed in a future "
|
||||||
|
"version";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If an attribute exists, then replace nodeName with its name.
|
// If an attribute exists, then replace nodeName with its name.
|
||||||
auto attributeEntry = sPropertyAttributeMap.find(element.type);
|
auto attributeEntry = sPropertyAttributeMap.find(element.type);
|
||||||
if (attributeEntry != sPropertyAttributeMap.end()) {
|
if (attributeEntry != sPropertyAttributeMap.end()) {
|
||||||
|
|
|
@ -57,9 +57,10 @@ namespace ThemeFlags
|
||||||
DELAY = 0x00002000,
|
DELAY = 0x00002000,
|
||||||
Z_INDEX = 0x00004000,
|
Z_INDEX = 0x00004000,
|
||||||
ROTATION = 0x00008000,
|
ROTATION = 0x00008000,
|
||||||
OPACITY = 0x00010000,
|
BRIGHTNESS = 0x00010000,
|
||||||
SATURATION = 0x00020000,
|
OPACITY = 0x00020000,
|
||||||
VISIBLE = 0x00040000,
|
SATURATION = 0x00040000,
|
||||||
|
VISIBLE = 0x00080000,
|
||||||
ALL = 0xFFFFFFFF
|
ALL = 0xFFFFFFFF
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
@ -281,7 +282,8 @@ private:
|
||||||
static std::vector<std::string> sSupportedViews;
|
static std::vector<std::string> sSupportedViews;
|
||||||
static std::vector<std::string> sLegacySupportedViews;
|
static std::vector<std::string> sLegacySupportedViews;
|
||||||
static std::vector<std::string> sLegacySupportedFeatures;
|
static std::vector<std::string> sLegacySupportedFeatures;
|
||||||
static std::vector<std::string> sLegacyElements;
|
static std::vector<std::string> sLegacyProperties;
|
||||||
|
static std::vector<std::string> sDeprecatedProperties;
|
||||||
static std::vector<std::pair<std::string, std::string>> sSupportedAspectRatios;
|
static std::vector<std::pair<std::string, std::string>> sSupportedAspectRatios;
|
||||||
static std::map<std::string, float> sAspectRatioMap;
|
static std::map<std::string, float> sAspectRatioMap;
|
||||||
|
|
||||||
|
|
|
@ -680,14 +680,14 @@ void Window::setHelpPrompts(const std::vector<HelpPrompt>& prompts, const HelpSt
|
||||||
static const std::vector<std::string> map = {"up/down/left/right",
|
static const std::vector<std::string> map = {"up/down/left/right",
|
||||||
"up/down",
|
"up/down",
|
||||||
"left/right",
|
"left/right",
|
||||||
"a",
|
|
||||||
"b",
|
|
||||||
"x",
|
|
||||||
"y",
|
|
||||||
"r",
|
|
||||||
"l",
|
|
||||||
"rt",
|
"rt",
|
||||||
"lt",
|
"lt",
|
||||||
|
"r",
|
||||||
|
"l",
|
||||||
|
"y",
|
||||||
|
"x",
|
||||||
|
"b",
|
||||||
|
"a",
|
||||||
"start",
|
"start",
|
||||||
"back"};
|
"back"};
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "animations/AnimationController.h"
|
#include "animations/AnimationController.h"
|
||||||
|
|
||||||
#include "animations/Animation.h"
|
#include "animations/Animation.h"
|
||||||
|
#include "utils/MathUtil.h"
|
||||||
|
|
||||||
AnimationController::AnimationController(Animation* anim,
|
AnimationController::AnimationController(Animation* anim,
|
||||||
int delay,
|
int delay,
|
||||||
|
@ -37,16 +38,10 @@ bool AnimationController::update(int deltaTime)
|
||||||
if (mTime < 0) // Are we still in delay?
|
if (mTime < 0) // Are we still in delay?
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
float t = static_cast<float>(mTime) / mAnimation->getDuration();
|
float animTime {glm::clamp(static_cast<float>(mTime) / mAnimation->getDuration(), 0.0f, 1.0f)};
|
||||||
|
mAnimation->apply(mReverse ? 1.0f - animTime : animTime);
|
||||||
|
|
||||||
if (t > 1.0f)
|
if (animTime == 1.0f)
|
||||||
t = 1.0f;
|
|
||||||
else if (t < 0.0f)
|
|
||||||
t = 0.0f;
|
|
||||||
|
|
||||||
mAnimation->apply(mReverse ? 1.0f - t : t);
|
|
||||||
|
|
||||||
if (t == 1.0f)
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -32,7 +32,7 @@ void AnimatedImageComponent::load(const AnimationDef* def)
|
||||||
}
|
}
|
||||||
|
|
||||||
auto img = std::unique_ptr<ImageComponent>(new ImageComponent);
|
auto img = std::unique_ptr<ImageComponent>(new ImageComponent);
|
||||||
img->setResize(mSize.x, mSize.y);
|
img->setResize(mSize);
|
||||||
img->setImage(std::string(def->frames[i].path), false);
|
img->setImage(std::string(def->frames[i].path), false);
|
||||||
|
|
||||||
mFrames.push_back(ImageFrame(std::move(img), def->frames[i].time));
|
mFrames.push_back(ImageFrame(std::move(img), def->frames[i].time));
|
||||||
|
@ -53,9 +53,8 @@ void AnimatedImageComponent::reset()
|
||||||
|
|
||||||
void AnimatedImageComponent::onSizeChanged()
|
void AnimatedImageComponent::onSizeChanged()
|
||||||
{
|
{
|
||||||
for (auto it = mFrames.cbegin(); it != mFrames.cend(); ++it) {
|
for (auto it = mFrames.cbegin(); it != mFrames.cend(); ++it)
|
||||||
it->first->setResize(mSize.x, mSize.y);
|
it->first->setResize(mSize);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimatedImageComponent::update(int deltaTime)
|
void AnimatedImageComponent::update(int deltaTime)
|
||||||
|
|
|
@ -40,6 +40,7 @@ namespace
|
||||||
{"gamepad_xbox", "Gamepad (Xbox)", ":/graphics/controllers/gamepad_xbox.svg"},
|
{"gamepad_xbox", "Gamepad (Xbox)", ":/graphics/controllers/gamepad_xbox.svg"},
|
||||||
{"joystick_generic", "Joystick (Generic)", ":/graphics/controllers/joystick_generic.svg"},
|
{"joystick_generic", "Joystick (Generic)", ":/graphics/controllers/joystick_generic.svg"},
|
||||||
{"joystick_arcade_no_buttons", "Joystick (Arcade No Buttons)", ":/graphics/controllers/joystick_arcade_no_buttons.svg"},
|
{"joystick_arcade_no_buttons", "Joystick (Arcade No Buttons)", ":/graphics/controllers/joystick_arcade_no_buttons.svg"},
|
||||||
|
{"joystick_arcade_no_buttons_twin", "Joystick (Arcade No Buttons Twin Stick)", ":/graphics/controllers/joystick_arcade_no_buttons_twin.svg"},
|
||||||
{"joystick_arcade_1_button", "Joystick (Arcade 1 Button)", ":/graphics/controllers/joystick_arcade_1_button.svg"},
|
{"joystick_arcade_1_button", "Joystick (Arcade 1 Button)", ":/graphics/controllers/joystick_arcade_1_button.svg"},
|
||||||
{"joystick_arcade_2_buttons", "Joystick (Arcade 2 Buttons)", ":/graphics/controllers/joystick_arcade_2_buttons.svg"},
|
{"joystick_arcade_2_buttons", "Joystick (Arcade 2 Buttons)", ":/graphics/controllers/joystick_arcade_2_buttons.svg"},
|
||||||
{"joystick_arcade_3_buttons", "Joystick (Arcade 3 Buttons)", ":/graphics/controllers/joystick_arcade_3_buttons.svg"},
|
{"joystick_arcade_3_buttons", "Joystick (Arcade 3 Buttons)", ":/graphics/controllers/joystick_arcade_3_buttons.svg"},
|
||||||
|
|
|
@ -224,5 +224,5 @@ void DateTimeComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
if (properties & LINE_SPACING && elem->has("lineSpacing"))
|
if (properties & LINE_SPACING && elem->has("lineSpacing"))
|
||||||
setLineSpacing(glm::clamp(elem->get<float>("lineSpacing"), 0.5f, 3.0f));
|
setLineSpacing(glm::clamp(elem->get<float>("lineSpacing"), 0.5f, 3.0f));
|
||||||
|
|
||||||
setFont(Font::getFromTheme(elem, properties, mFont, maxHeight, theme->isLegacyTheme()));
|
setFont(Font::getFromTheme(elem, properties, mFont, maxHeight, false, theme->isLegacyTheme()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ void FlexboxComponent::render(const glm::mat4& parentTrans)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!mLayoutValid)
|
if (!mLayoutValid)
|
||||||
computeLayout();
|
calculateLayout();
|
||||||
|
|
||||||
glm::mat4 trans {parentTrans * getTransform()};
|
glm::mat4 trans {parentTrans * getTransform()};
|
||||||
mRenderer->setMatrix(trans);
|
mRenderer->setMatrix(trans);
|
||||||
|
@ -82,7 +82,7 @@ void FlexboxComponent::setItemMargin(glm::vec2 value)
|
||||||
mLayoutValid = false;
|
mLayoutValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlexboxComponent::computeLayout()
|
void FlexboxComponent::calculateLayout()
|
||||||
{
|
{
|
||||||
// If we're not clamping itemMargin to a reasonable value, all kinds of weird rendering
|
// If we're not clamping itemMargin to a reasonable value, all kinds of weird rendering
|
||||||
// issues could occur.
|
// issues could occur.
|
||||||
|
|
|
@ -78,7 +78,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Calculate flexbox layout.
|
// Calculate flexbox layout.
|
||||||
void computeLayout();
|
void calculateLayout();
|
||||||
|
|
||||||
Renderer* mRenderer;
|
Renderer* mRenderer;
|
||||||
std::vector<FlexboxItem>& mItems;
|
std::vector<FlexboxItem>& mItems;
|
||||||
|
|
|
@ -495,6 +495,7 @@ void GIFAnimComponent::render(const glm::mat4& parentTrans)
|
||||||
for (int i = 0; i < 4; ++i)
|
for (int i = 0; i < 4; ++i)
|
||||||
vertices[i].position = glm::round(vertices[i].position);
|
vertices[i].position = glm::round(vertices[i].position);
|
||||||
|
|
||||||
|
vertices->brightness = mBrightness;
|
||||||
vertices->saturation = mSaturation * mThemeSaturation;
|
vertices->saturation = mSaturation * mThemeSaturation;
|
||||||
vertices->opacity = mOpacity * mThemeOpacity;
|
vertices->opacity = mOpacity * mThemeOpacity;
|
||||||
vertices->dimming = mDimming;
|
vertices->dimming = mDimming;
|
||||||
|
|
|
@ -45,6 +45,7 @@ public:
|
||||||
const bool getNeedsRefresh() { return mNeedsRefresh; }
|
const bool getNeedsRefresh() { return mNeedsRefresh; }
|
||||||
const GameSelection getGameSelection() const { return mGameSelection; }
|
const GameSelection getGameSelection() const { return mGameSelection; }
|
||||||
const std::string& getSelectorName() const { return mSelectorName; }
|
const std::string& getSelectorName() const { return mSelectorName; }
|
||||||
|
const int getGameCount() const { return mGameCount; }
|
||||||
|
|
||||||
void refreshGames()
|
void refreshGames()
|
||||||
{
|
{
|
||||||
|
@ -63,9 +64,12 @@ public:
|
||||||
Settings::getInstance()->getBool("ForceKid"))};
|
Settings::getInstance()->getBool("ForceKid"))};
|
||||||
|
|
||||||
if (mGameSelection == GameSelection::RANDOM) {
|
if (mGameSelection == GameSelection::RANDOM) {
|
||||||
for (int i = 0; i < mGameCount; ++i) {
|
int tries {8};
|
||||||
|
for (int i {0}; i < mGameCount; ++i) {
|
||||||
if (mSystem->getRootFolder()->getGameCount().first == 0)
|
if (mSystem->getRootFolder()->getGameCount().first == 0)
|
||||||
break;
|
break;
|
||||||
|
if (mSystem->getRootFolder()->getGameCount().first == mGames.size())
|
||||||
|
break;
|
||||||
FileData* randomGame {nullptr};
|
FileData* randomGame {nullptr};
|
||||||
|
|
||||||
if (mGameCount > 1 || lastGame == nullptr ||
|
if (mGameCount > 1 || lastGame == nullptr ||
|
||||||
|
@ -74,6 +78,14 @@ public:
|
||||||
else
|
else
|
||||||
randomGame = mSystem->getRandomGame(lastGame, true);
|
randomGame = mSystem->getRandomGame(lastGame, true);
|
||||||
|
|
||||||
|
if (std::find(mGames.begin(), mGames.end(), randomGame) != mGames.end()) {
|
||||||
|
if (tries > 0) {
|
||||||
|
--i;
|
||||||
|
--tries;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (randomGame != nullptr)
|
if (randomGame != nullptr)
|
||||||
mGames.emplace_back(randomGame);
|
mGames.emplace_back(randomGame);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,9 @@ void HelpComponent::assignIcons()
|
||||||
mStyle.mCustomButtons.button_lt;
|
mStyle.mCustomButtons.button_lt;
|
||||||
sIconPathMap["rt"] = mStyle.mCustomButtons.button_rt.empty() ? ":/graphics/help/button_rt.svg" :
|
sIconPathMap["rt"] = mStyle.mCustomButtons.button_rt.empty() ? ":/graphics/help/button_rt.svg" :
|
||||||
mStyle.mCustomButtons.button_rt;
|
mStyle.mCustomButtons.button_rt;
|
||||||
|
sIconPathMap["ltrt"] = mStyle.mCustomButtons.button_ltrt.empty() ?
|
||||||
|
":/graphics/help/button_ltrt.svg" :
|
||||||
|
mStyle.mCustomButtons.button_ltrt;
|
||||||
|
|
||||||
// These graphics files are custom per controller type.
|
// These graphics files are custom per controller type.
|
||||||
if (controllerType == "snes") {
|
if (controllerType == "snes") {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// EmulationStation Desktop Edition
|
// EmulationStation Desktop Edition
|
||||||
// IList.h
|
// IList.h
|
||||||
//
|
//
|
||||||
// List base class, used by both the gamelist views and the menu.
|
// List base class, used by the system view, gamelist view and menu system.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ES_CORE_COMPONENTS_ILIST_H
|
#ifndef ES_CORE_COMPONENTS_ILIST_H
|
||||||
|
@ -73,6 +73,9 @@ protected:
|
||||||
const ScrollTierList& mTierList;
|
const ScrollTierList& mTierList;
|
||||||
const ListLoopType mLoopType;
|
const ListLoopType mLoopType;
|
||||||
int mCursor;
|
int mCursor;
|
||||||
|
int mLastCursor;
|
||||||
|
int mColumns;
|
||||||
|
int mRows;
|
||||||
int mScrollTier;
|
int mScrollTier;
|
||||||
int mScrollVelocity;
|
int mScrollVelocity;
|
||||||
int mScrollTierAccumulator;
|
int mScrollTierAccumulator;
|
||||||
|
@ -88,6 +91,9 @@ public:
|
||||||
, mTierList {tierList}
|
, mTierList {tierList}
|
||||||
, mLoopType {loopType}
|
, mLoopType {loopType}
|
||||||
, mCursor {0}
|
, mCursor {0}
|
||||||
|
, mLastCursor {0}
|
||||||
|
, mColumns {0}
|
||||||
|
, mRows {0}
|
||||||
, mScrollTier {0}
|
, mScrollTier {0}
|
||||||
, mScrollVelocity {0}
|
, mScrollVelocity {0}
|
||||||
, mScrollTierAccumulator {0}
|
, mScrollTierAccumulator {0}
|
||||||
|
@ -117,6 +123,7 @@ public:
|
||||||
{
|
{
|
||||||
mEntries.clear();
|
mEntries.clear();
|
||||||
mCursor = 0;
|
mCursor = 0;
|
||||||
|
mLastCursor = 0;
|
||||||
listInput(0);
|
listInput(0);
|
||||||
onCursorChanged(CursorState::CURSOR_STOPPED);
|
onCursorChanged(CursorState::CURSOR_STOPPED);
|
||||||
}
|
}
|
||||||
|
@ -213,7 +220,10 @@ protected:
|
||||||
|
|
||||||
bool listFirstRow()
|
bool listFirstRow()
|
||||||
{
|
{
|
||||||
|
mLastCursor = mCursor;
|
||||||
mCursor = 0;
|
mCursor = 0;
|
||||||
|
mScrollVelocity = 0;
|
||||||
|
mScrollTier = 0;
|
||||||
onCursorChanged(CursorState::CURSOR_STOPPED);
|
onCursorChanged(CursorState::CURSOR_STOPPED);
|
||||||
onScroll();
|
onScroll();
|
||||||
return true;
|
return true;
|
||||||
|
@ -221,7 +231,10 @@ protected:
|
||||||
|
|
||||||
bool listLastRow()
|
bool listLastRow()
|
||||||
{
|
{
|
||||||
|
mLastCursor = mCursor;
|
||||||
mCursor = static_cast<int>(mEntries.size()) - 1;
|
mCursor = static_cast<int>(mEntries.size()) - 1;
|
||||||
|
mScrollVelocity = 0;
|
||||||
|
mScrollTier = 0;
|
||||||
onCursorChanged(CursorState::CURSOR_STOPPED);
|
onCursorChanged(CursorState::CURSOR_STOPPED);
|
||||||
onScroll();
|
onScroll();
|
||||||
return true;
|
return true;
|
||||||
|
@ -271,7 +284,7 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually perform the scrolling.
|
// Actually perform the scrolling.
|
||||||
for (int i = 0; i < scrollCount; ++i)
|
for (int i {0}; i < scrollCount; ++i)
|
||||||
scroll(mScrollVelocity);
|
scroll(mScrollVelocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,7 +300,7 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string titleIndex;
|
std::string titleIndex;
|
||||||
bool favoritesSorting;
|
bool favoritesSorting {true};
|
||||||
|
|
||||||
if (getSelected()->getSystem()->isCustomCollection())
|
if (getSelected()->getSystem()->isCustomCollection())
|
||||||
favoritesSorting = Settings::getInstance()->getBool("FavFirstCustom");
|
favoritesSorting = Settings::getInstance()->getBool("FavFirstCustom");
|
||||||
|
@ -323,6 +336,29 @@ protected:
|
||||||
if (mScrollVelocity == 0 || size() < 2)
|
if (mScrollVelocity == 0 || size() < 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
bool doScroll {true};
|
||||||
|
|
||||||
|
// This is only needed for GridComponent.
|
||||||
|
if (mColumns > 1 && mScrollVelocity == -mColumns && mCursor < mColumns) {
|
||||||
|
doScroll = false;
|
||||||
|
}
|
||||||
|
else if (mColumns != 0 && mScrollVelocity == mColumns) {
|
||||||
|
if (size() - mCursor <= size() % mColumns)
|
||||||
|
doScroll = false;
|
||||||
|
else if (mColumns != 1 && mCursor >= (mColumns * mRows) - mColumns &&
|
||||||
|
size() - mCursor <= mColumns && size() % mColumns == 0)
|
||||||
|
doScroll = false;
|
||||||
|
else if (size() < mColumns)
|
||||||
|
doScroll = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mLastCursor = mCursor;
|
||||||
|
|
||||||
|
if (!doScroll) {
|
||||||
|
onCursorChanged(CursorState::CURSOR_STOPPED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int cursor {mCursor + amt};
|
int cursor {mCursor + amt};
|
||||||
int absAmt {amt < 0 ? -amt : amt};
|
int absAmt {amt < 0 ? -amt : amt};
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ ImageComponent::ImageComponent(bool forceLoad, bool dynamic)
|
||||||
, mFlipX {false}
|
, mFlipX {false}
|
||||||
, mFlipY {false}
|
, mFlipY {false}
|
||||||
, mTargetIsMax {false}
|
, mTargetIsMax {false}
|
||||||
|
, mTargetIsCrop {false}
|
||||||
, mTileWidth {0.0f}
|
, mTileWidth {0.0f}
|
||||||
, mTileHeight {0.0f}
|
, mTileHeight {0.0f}
|
||||||
, mColorShift {0xFFFFFFFF}
|
, mColorShift {0xFFFFFFFF}
|
||||||
|
@ -93,6 +94,8 @@ void ImageComponent::setImage(const std::string& path, bool tile)
|
||||||
mMipmapping, static_cast<size_t>(mSize.x),
|
mMipmapping, static_cast<size_t>(mSize.x),
|
||||||
static_cast<size_t>(mSize.y), mTileWidth, mTileHeight);
|
static_cast<size_t>(mSize.y), mTileWidth, mTileHeight);
|
||||||
mTexture->rasterizeAt(mSize.x, mSize.y);
|
mTexture->rasterizeAt(mSize.x, mSize.y);
|
||||||
|
if (mTargetIsCrop)
|
||||||
|
coverFitCrop();
|
||||||
onSizeChanged();
|
onSizeChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,17 +126,19 @@ void ImageComponent::setImage(const std::shared_ptr<TextureResource>& texture, b
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::setResize(float width, float height)
|
void ImageComponent::setResize(const float width, const float height)
|
||||||
{
|
{
|
||||||
mTargetSize = glm::vec2 {width, height};
|
mTargetSize = glm::vec2 {width, height};
|
||||||
mTargetIsMax = false;
|
mTargetIsMax = false;
|
||||||
|
mTargetIsCrop = false;
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::setResize(float width, float height, bool rasterize)
|
void ImageComponent::setResize(const glm::vec2& size, bool rasterize)
|
||||||
{
|
{
|
||||||
mTargetSize = glm::vec2 {width, height};
|
mTargetSize = size;
|
||||||
mTargetIsMax = false;
|
mTargetIsMax = false;
|
||||||
|
mTargetIsCrop = false;
|
||||||
resize(rasterize);
|
resize(rasterize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,39 +146,48 @@ void ImageComponent::setMaxSize(const float width, const float height)
|
||||||
{
|
{
|
||||||
mTargetSize = glm::vec2 {width, height};
|
mTargetSize = glm::vec2 {width, height};
|
||||||
mTargetIsMax = true;
|
mTargetIsMax = true;
|
||||||
|
mTargetIsCrop = false;
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::cropLeft(const float percent)
|
void ImageComponent::setCroppedSize(const glm::vec2& size)
|
||||||
{
|
{
|
||||||
assert(percent >= 0.0f && percent <= 1.0f);
|
mTargetSize = size;
|
||||||
mTopLeftCrop.x = percent;
|
mTargetIsMax = false;
|
||||||
|
mTargetIsCrop = true;
|
||||||
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::cropTop(const float percent)
|
void ImageComponent::cropLeft(const float value)
|
||||||
{
|
{
|
||||||
assert(percent >= 0.0f && percent <= 1.0f);
|
assert(value >= 0.0f && value <= 1.0f);
|
||||||
mTopLeftCrop.y = percent;
|
mTopLeftCrop.x = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::cropRight(const float percent)
|
void ImageComponent::cropTop(const float value)
|
||||||
{
|
{
|
||||||
assert(percent >= 0.0f && percent <= 1.0f);
|
assert(value >= 0.0f && value <= 1.0f);
|
||||||
mBottomRightCrop.x = 1.0f - percent;
|
mTopLeftCrop.y = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::cropBot(const float percent)
|
void ImageComponent::cropRight(const float value)
|
||||||
{
|
{
|
||||||
assert(percent >= 0.0f && percent <= 1.0f);
|
assert(value >= 0.0f && value <= 1.0f);
|
||||||
mBottomRightCrop.y = 1.0f - percent;
|
mBottomRightCrop.x = 1.0f - value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::crop(const float left, const float top, const float right, const float bot)
|
void ImageComponent::cropBottom(const float value)
|
||||||
|
{
|
||||||
|
assert(value >= 0.0f && value <= 1.0f);
|
||||||
|
mBottomRightCrop.y = 1.0f - value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageComponent::crop(const float left, const float top, const float right, const float bottom)
|
||||||
{
|
{
|
||||||
cropLeft(left);
|
cropLeft(left);
|
||||||
cropTop(top);
|
cropTop(top);
|
||||||
cropRight(right);
|
cropRight(right);
|
||||||
cropBot(bot);
|
cropBottom(bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::uncrop()
|
void ImageComponent::uncrop()
|
||||||
|
@ -182,6 +196,24 @@ void ImageComponent::uncrop()
|
||||||
crop(0.0f, 0.0f, 0.0f, 0.0f);
|
crop(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageComponent::coverFitCrop()
|
||||||
|
{
|
||||||
|
assert(mTargetIsCrop);
|
||||||
|
|
||||||
|
if (std::round(mSize.y) > std::round(mTargetSize.y)) {
|
||||||
|
const float cropSize {1.0f - (mTargetSize.y / mSize.y)};
|
||||||
|
cropTop(cropSize / 2.0f);
|
||||||
|
cropBottom(cropSize / 2.0f);
|
||||||
|
mSize.y = mSize.y - (mSize.y * cropSize);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const float cropSize {1.0f - (mTargetSize.x / mSize.x)};
|
||||||
|
cropLeft(cropSize / 2.0f);
|
||||||
|
cropRight(cropSize / 2.0f);
|
||||||
|
mSize.x = mSize.x - (mSize.x * cropSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ImageComponent::cropTransparentPadding(const float maxSizeX, const float maxSizeY)
|
void ImageComponent::cropTransparentPadding(const float maxSizeX, const float maxSizeY)
|
||||||
{
|
{
|
||||||
if (mSize == glm::vec2 {0.0f, 0.0f})
|
if (mSize == glm::vec2 {0.0f, 0.0f})
|
||||||
|
@ -362,8 +394,9 @@ void ImageComponent::render(const glm::mat4& parentTrans)
|
||||||
else
|
else
|
||||||
fadeIn(mTexture->bind());
|
fadeIn(mTexture->bind());
|
||||||
|
|
||||||
mVertices->saturation = mSaturation * mThemeSaturation;
|
mVertices->brightness = mBrightness;
|
||||||
mVertices->opacity = mThemeOpacity;
|
mVertices->opacity = mThemeOpacity;
|
||||||
|
mVertices->saturation = mSaturation * mThemeSaturation;
|
||||||
mVertices->dimming = mDimming;
|
mVertices->dimming = mDimming;
|
||||||
mVertices->reflectionsFalloff = mReflectionsFalloff;
|
mVertices->reflectionsFalloff = mReflectionsFalloff;
|
||||||
|
|
||||||
|
@ -562,7 +595,7 @@ void ImageComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
if (elem->has("colorEnd"))
|
if (elem->has("colorEnd"))
|
||||||
setColorShiftEnd(elem->get<unsigned int>("colorEnd"));
|
setColorShiftEnd(elem->get<unsigned int>("colorEnd"));
|
||||||
if (elem->has("gradientType")) {
|
if (elem->has("gradientType")) {
|
||||||
const std::string gradientType {elem->get<std::string>("gradientType")};
|
const std::string& gradientType {elem->get<std::string>("gradientType")};
|
||||||
if (gradientType == "horizontal") {
|
if (gradientType == "horizontal") {
|
||||||
setColorGradientHorizontal(true);
|
setColorGradientHorizontal(true);
|
||||||
}
|
}
|
||||||
|
@ -617,6 +650,12 @@ void ImageComponent::resize(bool rasterize)
|
||||||
mSize.x = std::min((mSize.y / textureSize.y) * textureSize.x, mTargetSize.x);
|
mSize.x = std::min((mSize.y / textureSize.y) * textureSize.x, mTargetSize.x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (mTargetIsCrop) {
|
||||||
|
// Size texture to allow for cropped image to fill the entire area.
|
||||||
|
const float cropFactor {
|
||||||
|
std::max(mTargetSize.x / textureSize.x, mTargetSize.y / textureSize.y)};
|
||||||
|
mSize = textureSize * cropFactor;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
// If both axes are set we just stretch or squash, if no axes are set we do nothing.
|
// If both axes are set we just stretch or squash, if no axes are set we do nothing.
|
||||||
mSize = mTargetSize == glm::vec2 {0.0f, 0.0f} ? textureSize : mTargetSize;
|
mSize = mTargetSize == glm::vec2 {0.0f, 0.0f} ? textureSize : mTargetSize;
|
||||||
|
@ -640,6 +679,8 @@ void ImageComponent::resize(bool rasterize)
|
||||||
|
|
||||||
if (rasterize) {
|
if (rasterize) {
|
||||||
mTexture->rasterizeAt(mSize.x, mSize.y);
|
mTexture->rasterizeAt(mSize.x, mSize.y);
|
||||||
|
if (mTargetIsCrop)
|
||||||
|
coverFitCrop();
|
||||||
onSizeChanged();
|
onSizeChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,11 +39,7 @@ public:
|
||||||
// Can be set before or after an image is loaded.
|
// Can be set before or after an image is loaded.
|
||||||
// setMaxSize() and setResize() are mutually exclusive.
|
// setMaxSize() and setResize() are mutually exclusive.
|
||||||
void setResize(const float width, const float height) override;
|
void setResize(const float width, const float height) override;
|
||||||
void setResize(const glm::vec2& size, bool rasterize = true)
|
void setResize(const glm::vec2& size, bool rasterize = true) override;
|
||||||
{
|
|
||||||
setResize(size.x, size.y, rasterize);
|
|
||||||
}
|
|
||||||
void setResize(const float width, const float height, bool rasterize) override;
|
|
||||||
|
|
||||||
// Resize the image to be as large as possible but fit within a box of this size.
|
// Resize the image to be as large as possible but fit within a box of this size.
|
||||||
// Can be set before or after an image is loaded.
|
// Can be set before or after an image is loaded.
|
||||||
|
@ -51,6 +47,9 @@ public:
|
||||||
void setMaxSize(const float width, const float height);
|
void setMaxSize(const float width, const float height);
|
||||||
void setMaxSize(const glm::vec2& size) { setMaxSize(size.x, size.y); }
|
void setMaxSize(const glm::vec2& size) { setMaxSize(size.x, size.y); }
|
||||||
|
|
||||||
|
// Resize and crop image so it fills the entire area defined by the size parameter.
|
||||||
|
void setCroppedSize(const glm::vec2& size);
|
||||||
|
|
||||||
void setTileSize(const float width, const float height)
|
void setTileSize(const float width, const float height)
|
||||||
{
|
{
|
||||||
mTileWidth = width;
|
mTileWidth = width;
|
||||||
|
@ -59,14 +58,16 @@ public:
|
||||||
|
|
||||||
glm::vec2 getRotationSize() const override { return mRotateByTargetSize ? mTargetSize : mSize; }
|
glm::vec2 getRotationSize() const override { return mRotateByTargetSize ? mTargetSize : mSize; }
|
||||||
|
|
||||||
// Applied AFTER image positioning and sizing.
|
// Applied after image positioning and sizing.
|
||||||
// cropTop(0.2) will crop 20% of the top of the image.
|
void cropLeft(const float value);
|
||||||
void cropLeft(const float percent);
|
void cropTop(const float value);
|
||||||
void cropTop(const float percent);
|
void cropRight(const float value);
|
||||||
void cropRight(const float percent);
|
void cropBottom(const float value);
|
||||||
void cropBot(const float percent);
|
void crop(const float left, const float top, const float right, const float bottom);
|
||||||
void crop(const float left, const float top, const float right, const float bot);
|
|
||||||
void uncrop();
|
void uncrop();
|
||||||
|
// This essentially implements CSS "object-fit: cover" and has nothing to do with the
|
||||||
|
// cover image type (as the name may seem to imply).
|
||||||
|
void coverFitCrop();
|
||||||
|
|
||||||
// This crops any entirely transparent areas around the actual image.
|
// This crops any entirely transparent areas around the actual image.
|
||||||
// The arguments restrict how much the end result is allowed to be scaled.
|
// The arguments restrict how much the end result is allowed to be scaled.
|
||||||
|
@ -119,6 +120,7 @@ private:
|
||||||
bool mFlipX;
|
bool mFlipX;
|
||||||
bool mFlipY;
|
bool mFlipY;
|
||||||
bool mTargetIsMax;
|
bool mTargetIsMax;
|
||||||
|
bool mTargetIsCrop;
|
||||||
|
|
||||||
float mTileWidth;
|
float mTileWidth;
|
||||||
float mTileHeight;
|
float mTileHeight;
|
||||||
|
|
|
@ -484,6 +484,7 @@ void LottieAnimComponent::render(const glm::mat4& parentTrans)
|
||||||
for (int i = 0; i < 4; ++i)
|
for (int i = 0; i < 4; ++i)
|
||||||
vertices[i].position = glm::round(vertices[i].position);
|
vertices[i].position = glm::round(vertices[i].position);
|
||||||
|
|
||||||
|
vertices->brightness = mBrightness;
|
||||||
vertices->saturation = mSaturation * mThemeSaturation;
|
vertices->saturation = mSaturation * mThemeSaturation;
|
||||||
vertices->opacity = mOpacity * mThemeOpacity;
|
vertices->opacity = mOpacity * mThemeOpacity;
|
||||||
vertices->dimming = mDimming;
|
vertices->dimming = mDimming;
|
||||||
|
|
|
@ -22,7 +22,6 @@ public:
|
||||||
void render(const glm::mat4& parentTrans) override;
|
void render(const glm::mat4& parentTrans) override;
|
||||||
void onSizeChanged() override { mImage.setSize(mSize); }
|
void onSizeChanged() override { mImage.setSize(mSize); }
|
||||||
|
|
||||||
void setResize(float width, float height) override { setSize(width, height); }
|
|
||||||
bool getState() const { return mState; }
|
bool getState() const { return mState; }
|
||||||
void setState(bool state);
|
void setState(bool state);
|
||||||
std::string getValue() const override;
|
std::string getValue() const override;
|
||||||
|
|
|
@ -486,5 +486,5 @@ void TextComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
if (properties & LINE_SPACING && elem->has("lineSpacing"))
|
if (properties & LINE_SPACING && elem->has("lineSpacing"))
|
||||||
setLineSpacing(glm::clamp(elem->get<float>("lineSpacing"), 0.5f, 3.0f));
|
setLineSpacing(glm::clamp(elem->get<float>("lineSpacing"), 0.5f, 3.0f));
|
||||||
|
|
||||||
setFont(Font::getFromTheme(elem, properties, mFont, maxHeight, theme->isLegacyTheme()));
|
setFont(Font::getFromTheme(elem, properties, mFont, maxHeight, false, theme->isLegacyTheme()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,9 @@
|
||||||
VideoComponent::VideoComponent()
|
VideoComponent::VideoComponent()
|
||||||
: mVideoWidth {0}
|
: mVideoWidth {0}
|
||||||
, mVideoHeight {0}
|
, mVideoHeight {0}
|
||||||
|
, mColorShift {0xFFFFFFFF}
|
||||||
|
, mColorShiftEnd {0xFFFFFFFF}
|
||||||
|
, mColorGradientHorizontal {true}
|
||||||
, mTargetSize {0.0f, 0.0f}
|
, mTargetSize {0.0f, 0.0f}
|
||||||
, mVideoAreaPos {0.0f, 0.0f}
|
, mVideoAreaPos {0.0f, 0.0f}
|
||||||
, mVideoAreaSize {0.0f, 0.0f}
|
, mVideoAreaSize {0.0f, 0.0f}
|
||||||
|
@ -254,6 +257,29 @@ void VideoComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (elem->has("color")) {
|
||||||
|
mColorShift = elem->get<unsigned int>("color");
|
||||||
|
mColorShiftEnd = mColorShift;
|
||||||
|
}
|
||||||
|
if (elem->has("colorEnd"))
|
||||||
|
mColorShiftEnd = elem->get<unsigned int>("colorEnd");
|
||||||
|
|
||||||
|
if (elem->has("gradientType")) {
|
||||||
|
const std::string& gradientType {elem->get<std::string>("gradientType")};
|
||||||
|
if (gradientType == "horizontal") {
|
||||||
|
mColorGradientHorizontal = true;
|
||||||
|
}
|
||||||
|
else if (gradientType == "vertical") {
|
||||||
|
mColorGradientHorizontal = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mColorGradientHorizontal = true;
|
||||||
|
LOG(LogWarning) << "VideoComponent: Invalid theme configuration, property "
|
||||||
|
"\"gradientType\" for element \""
|
||||||
|
<< element.substr(6) << "\" defined as \"" << gradientType << "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (elem->has("pillarboxes"))
|
if (elem->has("pillarboxes"))
|
||||||
mDrawPillarboxes = elem->get<bool>("pillarboxes");
|
mDrawPillarboxes = elem->get<bool>("pillarboxes");
|
||||||
|
|
||||||
|
@ -355,6 +381,14 @@ void VideoComponent::renderSnapshot(const glm::mat4& parentTrans)
|
||||||
if (mStaticImagePath != "") {
|
if (mStaticImagePath != "") {
|
||||||
mStaticImage.setOpacity(mOpacity * mThemeOpacity);
|
mStaticImage.setOpacity(mOpacity * mThemeOpacity);
|
||||||
mStaticImage.setSaturation(mSaturation * mThemeSaturation);
|
mStaticImage.setSaturation(mSaturation * mThemeSaturation);
|
||||||
|
if (mBrightness != 0.0f)
|
||||||
|
mStaticImage.setBrightness(mBrightness);
|
||||||
|
if (mColorShift != 0xFFFFFFFF)
|
||||||
|
mStaticImage.setColorShift(mColorShift);
|
||||||
|
if (mColorShift != mColorShiftEnd)
|
||||||
|
mStaticImage.setColorShiftEnd(mColorShiftEnd);
|
||||||
|
if (!mColorGradientHorizontal)
|
||||||
|
mStaticImage.setColorGradientHorizontal(mColorGradientHorizontal);
|
||||||
mStaticImage.setDimming(mDimming);
|
mStaticImage.setDimming(mDimming);
|
||||||
mStaticImage.render(parentTrans);
|
mStaticImage.render(parentTrans);
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,12 +76,6 @@ public:
|
||||||
|
|
||||||
void update(int deltaTime) override;
|
void update(int deltaTime) override;
|
||||||
|
|
||||||
// Resize the video to fit this size. If one axis is zero, scale that axis to maintain
|
|
||||||
// aspect ratio. If both are non-zero, potentially break the aspect ratio. If both are
|
|
||||||
// zero, no resizing. This can be set before or after a video is loaded.
|
|
||||||
// setMaxSize() and setResize() are mutually exclusive.
|
|
||||||
virtual void setResize(float width, float height) override = 0;
|
|
||||||
|
|
||||||
// Resize the video to be as large as possible but fit within a box of this size.
|
// Resize the video to be as large as possible but fit within a box of this size.
|
||||||
// This can be set before or after a video is loaded.
|
// This can be set before or after a video is loaded.
|
||||||
// Never breaks the aspect ratio. setMaxSize() and setResize() are mutually exclusive.
|
// Never breaks the aspect ratio. setMaxSize() and setResize() are mutually exclusive.
|
||||||
|
@ -107,6 +101,9 @@ protected:
|
||||||
|
|
||||||
unsigned mVideoWidth;
|
unsigned mVideoWidth;
|
||||||
unsigned mVideoHeight;
|
unsigned mVideoHeight;
|
||||||
|
unsigned int mColorShift;
|
||||||
|
unsigned int mColorShiftEnd;
|
||||||
|
bool mColorGradientHorizontal;
|
||||||
glm::vec2 mTargetSize;
|
glm::vec2 mTargetSize;
|
||||||
glm::vec2 mVideoAreaPos;
|
glm::vec2 mVideoAreaPos;
|
||||||
glm::vec2 mVideoAreaSize;
|
glm::vec2 mVideoAreaSize;
|
||||||
|
|
|
@ -60,12 +60,12 @@ VideoFFmpegComponent::VideoFFmpegComponent()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoFFmpegComponent::setResize(float width, float height)
|
void VideoFFmpegComponent::setResize(const float width, const float height)
|
||||||
{
|
{
|
||||||
// This resize function is used when stretching videos to full screen in the video screensaver.
|
// This resize function is used when stretching videos to full screen in the video screensaver.
|
||||||
mTargetSize = glm::vec2 {width, height};
|
mTargetSize = glm::vec2 {width, height};
|
||||||
mTargetIsMax = false;
|
mTargetIsMax = false;
|
||||||
mStaticImage.setResize(width, height);
|
mStaticImage.setResize(mTargetSize);
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,6 +164,11 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
|
||||||
vertices[3] = {{mSize.x + mRectangleOffset.x, mSize.y + + mRectangleOffset.y}, {1.0f, 1.0f}, 0xFFFFFFFF};
|
vertices[3] = {{mSize.x + mRectangleOffset.x, mSize.y + + mRectangleOffset.y}, {1.0f, 1.0f}, 0xFFFFFFFF};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
vertices[0].color = mColorShift;
|
||||||
|
vertices[1].color = mColorGradientHorizontal ? mColorShift : mColorShiftEnd;
|
||||||
|
vertices[2].color = mColorGradientHorizontal ? mColorShiftEnd : mColorShift;
|
||||||
|
vertices[3].color = mColorShiftEnd;
|
||||||
|
|
||||||
// Round vertices.
|
// Round vertices.
|
||||||
for (int i = 0; i < 4; ++i)
|
for (int i = 0; i < 4; ++i)
|
||||||
vertices[i].position = glm::round(vertices[i].position);
|
vertices[i].position = glm::round(vertices[i].position);
|
||||||
|
@ -171,6 +176,7 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
|
||||||
if (mFadeIn < 1.0f || mThemeOpacity < 1.0f)
|
if (mFadeIn < 1.0f || mThemeOpacity < 1.0f)
|
||||||
vertices->opacity = mFadeIn * mThemeOpacity;
|
vertices->opacity = mFadeIn * mThemeOpacity;
|
||||||
|
|
||||||
|
vertices->brightness = mBrightness;
|
||||||
vertices->saturation = mSaturation * mThemeSaturation;
|
vertices->saturation = mSaturation * mThemeSaturation;
|
||||||
vertices->dimming = mDimming;
|
vertices->dimming = mDimming;
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ public:
|
||||||
// aspect ratio. If both are non-zero, potentially break the aspect ratio. If both are
|
// aspect ratio. If both are non-zero, potentially break the aspect ratio. If both are
|
||||||
// zero, no resizing. This can be set before or after a video is loaded.
|
// zero, no resizing. This can be set before or after a video is loaded.
|
||||||
// setMaxSize() and setResize() are mutually exclusive.
|
// setMaxSize() and setResize() are mutually exclusive.
|
||||||
void setResize(float width, float height) override;
|
void setResize(const float width, const float height) override;
|
||||||
|
|
||||||
// Resize the video to be as large as possible but fit within a box of this size.
|
// Resize the video to be as large as possible but fit within a box of this size.
|
||||||
// This can be set before or after a video is loaded.
|
// This can be set before or after a video is loaded.
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
// EmulationStation Desktop Edition
|
// EmulationStation Desktop Edition
|
||||||
// CarouselComponent.h
|
// CarouselComponent.h
|
||||||
//
|
//
|
||||||
// Carousel.
|
// Carousel, usable in both the system and gamelist views.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ES_CORE_COMPONENTS_CAROUSEL_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_PRIMARY_CAROUSEL_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_CAROUSEL_COMPONENT_H
|
#define ES_CORE_COMPONENTS_PRIMARY_CAROUSEL_COMPONENT_H
|
||||||
|
|
||||||
#include "Sound.h"
|
#include "Sound.h"
|
||||||
#include "animations/LambdaAnimation.h"
|
#include "animations/LambdaAnimation.h"
|
||||||
|
@ -17,16 +17,15 @@
|
||||||
|
|
||||||
struct CarouselEntry {
|
struct CarouselEntry {
|
||||||
std::shared_ptr<GuiComponent> item;
|
std::shared_ptr<GuiComponent> item;
|
||||||
std::string itemPath;
|
std::string imagePath;
|
||||||
std::string defaultItemPath;
|
std::string defaultImagePath;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class CarouselComponent : public PrimaryComponent<T>, protected IList<CarouselEntry, T>
|
class CarouselComponent : public PrimaryComponent<T>, protected IList<CarouselEntry, T>
|
||||||
{
|
{
|
||||||
using List = IList<CarouselEntry, T>;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
using List = IList<CarouselEntry, T>;
|
||||||
using List::mCursor;
|
using List::mCursor;
|
||||||
using List::mEntries;
|
using List::mEntries;
|
||||||
using List::mScrollVelocity;
|
using List::mScrollVelocity;
|
||||||
|
@ -47,6 +46,14 @@ public:
|
||||||
NO_CAROUSEL
|
NO_CAROUSEL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class ItemStacking {
|
||||||
|
CENTERED,
|
||||||
|
ASCENDING,
|
||||||
|
ASCENDING_RAISED,
|
||||||
|
DESCENDING,
|
||||||
|
DESCENDING_RAISED
|
||||||
|
};
|
||||||
|
|
||||||
CarouselComponent();
|
CarouselComponent();
|
||||||
|
|
||||||
void addEntry(Entry& entry, const std::shared_ptr<ThemeData>& theme);
|
void addEntry(Entry& entry, const std::shared_ptr<ThemeData>& theme);
|
||||||
|
@ -54,10 +61,10 @@ public:
|
||||||
Entry& getEntry(int index) { return mEntries.at(index); }
|
Entry& getEntry(int index) { return mEntries.at(index); }
|
||||||
void onDemandTextureLoad() override;
|
void onDemandTextureLoad() override;
|
||||||
const CarouselType getType() { return mType; }
|
const CarouselType getType() { return mType; }
|
||||||
const std::string& getItemType() { return mItemType; }
|
const std::string& getImageType() { return mImageType; }
|
||||||
void setItemType(std::string itemType) { mItemType = itemType; }
|
void setImageType(std::string imageType) { mImageType = imageType; }
|
||||||
const std::string& getDefaultItem() { return mDefaultItem; }
|
const std::string& getDefaultImage() { return mDefaultImage; }
|
||||||
void setDefaultItem(std::string defaultItem) { mDefaultItem = defaultItem; }
|
void setDefaultImage(std::string defaultImage) { mDefaultImage = defaultImage; }
|
||||||
bool isScrolling() const override { return List::isScrolling(); }
|
bool isScrolling() const override { return List::isScrolling(); }
|
||||||
const LetterCase getLetterCase() const override { return mLetterCase; }
|
const LetterCase getLetterCase() const override { return mLetterCase; }
|
||||||
const LetterCase getLetterCaseCollections() const override { return mLetterCaseCollections; }
|
const LetterCase getLetterCaseCollections() const override { return mLetterCaseCollections; }
|
||||||
|
@ -66,14 +73,14 @@ public:
|
||||||
return mLetterCaseGroupedCollections;
|
return mLetterCaseGroupedCollections;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCursorChangedCallback(const std::function<void(CursorState state)>& func) override
|
|
||||||
{
|
|
||||||
mCursorChangedCallback = func;
|
|
||||||
}
|
|
||||||
void setCancelTransitionsCallback(const std::function<void()>& func) override
|
void setCancelTransitionsCallback(const std::function<void()>& func) override
|
||||||
{
|
{
|
||||||
mCancelTransitionsCallback = func;
|
mCancelTransitionsCallback = func;
|
||||||
}
|
}
|
||||||
|
void setCursorChangedCallback(const std::function<void(CursorState state)>& func) override
|
||||||
|
{
|
||||||
|
mCursorChangedCallback = func;
|
||||||
|
}
|
||||||
|
|
||||||
bool input(InputConfig* config, Input input) override;
|
bool input(InputConfig* config, Input input) override;
|
||||||
void update(int deltaTime) override;
|
void update(int deltaTime) override;
|
||||||
|
@ -122,41 +129,47 @@ private:
|
||||||
bool mPositiveDirection;
|
bool mPositiveDirection;
|
||||||
bool mTriggerJump;
|
bool mTriggerJump;
|
||||||
bool mGamelistView;
|
bool mGamelistView;
|
||||||
|
bool mLegacyMode;
|
||||||
|
|
||||||
CarouselType mType;
|
CarouselType mType;
|
||||||
std::string mItemType;
|
std::string mImageType;
|
||||||
std::string mDefaultItem;
|
std::string mDefaultImage;
|
||||||
bool mLegacyMode;
|
|
||||||
std::shared_ptr<Font> mFont;
|
|
||||||
unsigned int mTextColor;
|
|
||||||
unsigned int mTextBackgroundColor;
|
|
||||||
float mLineSpacing;
|
|
||||||
Alignment mItemHorizontalAlignment;
|
|
||||||
Alignment mItemVerticalAlignment;
|
|
||||||
Alignment mWheelHorizontalAlignment;
|
|
||||||
float mUnfocusedItemOpacity;
|
|
||||||
float mMaxItemCount;
|
float mMaxItemCount;
|
||||||
int mItemsBeforeCenter;
|
int mItemsBeforeCenter;
|
||||||
int mItemsAfterCenter;
|
int mItemsAfterCenter;
|
||||||
|
ItemStacking mItemStacking;
|
||||||
glm::vec2 mItemSize;
|
glm::vec2 mItemSize;
|
||||||
bool mLinearInterpolation;
|
|
||||||
bool mInstantItemTransitions;
|
|
||||||
bool mItemAxisHorizontal;
|
|
||||||
bool mFadeAbovePrimary;
|
|
||||||
LetterCase mLetterCase;
|
|
||||||
LetterCase mLetterCaseCollections;
|
|
||||||
LetterCase mLetterCaseGroupedCollections;
|
|
||||||
float mItemScale;
|
float mItemScale;
|
||||||
float mItemRotation;
|
float mItemRotation;
|
||||||
glm::vec2 mItemRotationOrigin;
|
glm::vec2 mItemRotationOrigin;
|
||||||
unsigned int mCarouselColor;
|
bool mItemAxisHorizontal;
|
||||||
unsigned int mCarouselColorEnd;
|
bool mLinearInterpolation;
|
||||||
bool mColorGradientHorizontal;
|
unsigned int mImageColorShift;
|
||||||
|
unsigned int mImageColorShiftEnd;
|
||||||
|
bool mImageColorGradientHorizontal;
|
||||||
|
float mImageBrightness;
|
||||||
|
float mImageSaturation;
|
||||||
|
bool mInstantItemTransitions;
|
||||||
|
Alignment mItemHorizontalAlignment;
|
||||||
|
Alignment mItemVerticalAlignment;
|
||||||
|
Alignment mWheelHorizontalAlignment;
|
||||||
|
float mHorizontalOffset;
|
||||||
|
float mVerticalOffset;
|
||||||
bool mReflections;
|
bool mReflections;
|
||||||
float mReflectionsOpacity;
|
float mReflectionsOpacity;
|
||||||
float mReflectionsFalloff;
|
float mReflectionsFalloff;
|
||||||
float mHorizontalOffset;
|
float mUnfocusedItemOpacity;
|
||||||
float mVerticalOffset;
|
unsigned int mCarouselColor;
|
||||||
|
unsigned int mCarouselColorEnd;
|
||||||
|
bool mColorGradientHorizontal;
|
||||||
|
unsigned int mTextColor;
|
||||||
|
unsigned int mTextBackgroundColor;
|
||||||
|
std::shared_ptr<Font> mFont;
|
||||||
|
LetterCase mLetterCase;
|
||||||
|
LetterCase mLetterCaseCollections;
|
||||||
|
LetterCase mLetterCaseGroupedCollections;
|
||||||
|
float mLineSpacing;
|
||||||
|
bool mFadeAbovePrimary;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -172,39 +185,45 @@ CarouselComponent<T>::CarouselComponent()
|
||||||
, mPositiveDirection {false}
|
, mPositiveDirection {false}
|
||||||
, mTriggerJump {false}
|
, mTriggerJump {false}
|
||||||
, mGamelistView {std::is_same_v<T, FileData*> ? true : false}
|
, mGamelistView {std::is_same_v<T, FileData*> ? true : false}
|
||||||
, mType {CarouselType::HORIZONTAL}
|
|
||||||
, mLegacyMode {false}
|
, mLegacyMode {false}
|
||||||
, mFont {Font::get(FONT_SIZE_LARGE)}
|
, mType {CarouselType::HORIZONTAL}
|
||||||
, mTextColor {0x000000FF}
|
|
||||||
, mTextBackgroundColor {0xFFFFFF00}
|
|
||||||
, mLineSpacing {1.5f}
|
|
||||||
, mItemHorizontalAlignment {ALIGN_CENTER}
|
|
||||||
, mItemVerticalAlignment {ALIGN_CENTER}
|
|
||||||
, mWheelHorizontalAlignment {ALIGN_CENTER}
|
|
||||||
, mUnfocusedItemOpacity {0.5f}
|
|
||||||
, mMaxItemCount {3.0f}
|
, mMaxItemCount {3.0f}
|
||||||
, mItemsBeforeCenter {8}
|
, mItemsBeforeCenter {8}
|
||||||
, mItemsAfterCenter {8}
|
, mItemsAfterCenter {8}
|
||||||
|
, mItemStacking {ItemStacking::CENTERED}
|
||||||
, mItemSize {glm::vec2 {Renderer::getScreenWidth() * 0.25f,
|
, mItemSize {glm::vec2 {Renderer::getScreenWidth() * 0.25f,
|
||||||
Renderer::getScreenHeight() * 0.155f}}
|
Renderer::getScreenHeight() * 0.155f}}
|
||||||
, mLinearInterpolation {false}
|
|
||||||
, mInstantItemTransitions {false}
|
|
||||||
, mItemAxisHorizontal {false}
|
|
||||||
, mFadeAbovePrimary {false}
|
|
||||||
, mLetterCase {LetterCase::NONE}
|
|
||||||
, mLetterCaseCollections {LetterCase::NONE}
|
|
||||||
, mLetterCaseGroupedCollections {LetterCase::NONE}
|
|
||||||
, mItemScale {1.2f}
|
, mItemScale {1.2f}
|
||||||
, mItemRotation {7.5f}
|
, mItemRotation {7.5f}
|
||||||
, mItemRotationOrigin {-3.0f, 0.5f}
|
, mItemRotationOrigin {-3.0f, 0.5f}
|
||||||
, mCarouselColor {0}
|
, mItemAxisHorizontal {false}
|
||||||
, mCarouselColorEnd {0}
|
, mLinearInterpolation {false}
|
||||||
, mColorGradientHorizontal {true}
|
, mImageColorShift {0xFFFFFFFF}
|
||||||
|
, mImageColorShiftEnd {0xFFFFFFFF}
|
||||||
|
, mImageColorGradientHorizontal {true}
|
||||||
|
, mImageBrightness {0.0f}
|
||||||
|
, mImageSaturation {1.0f}
|
||||||
|
, mInstantItemTransitions {false}
|
||||||
|
, mItemHorizontalAlignment {ALIGN_CENTER}
|
||||||
|
, mItemVerticalAlignment {ALIGN_CENTER}
|
||||||
|
, mWheelHorizontalAlignment {ALIGN_CENTER}
|
||||||
|
, mHorizontalOffset {0.0f}
|
||||||
|
, mVerticalOffset {0.0f}
|
||||||
, mReflections {false}
|
, mReflections {false}
|
||||||
, mReflectionsOpacity {0.5f}
|
, mReflectionsOpacity {0.5f}
|
||||||
, mReflectionsFalloff {1.0f}
|
, mReflectionsFalloff {1.0f}
|
||||||
, mHorizontalOffset {0.0f}
|
, mUnfocusedItemOpacity {0.5f}
|
||||||
, mVerticalOffset {0.0f}
|
, mCarouselColor {0}
|
||||||
|
, mCarouselColorEnd {0}
|
||||||
|
, mColorGradientHorizontal {true}
|
||||||
|
, mTextColor {0x000000FF}
|
||||||
|
, mTextBackgroundColor {0xFFFFFF00}
|
||||||
|
, mFont {Font::get(FONT_SIZE_LARGE)}
|
||||||
|
, mLetterCase {LetterCase::NONE}
|
||||||
|
, mLetterCaseCollections {LetterCase::NONE}
|
||||||
|
, mLetterCaseGroupedCollections {LetterCase::NONE}
|
||||||
|
, mLineSpacing {1.5f}
|
||||||
|
, mFadeAbovePrimary {false}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,34 +259,55 @@ void CarouselComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeDat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (entry.data.itemPath != "" &&
|
if (entry.data.imagePath != "" &&
|
||||||
ResourceManager::getInstance().fileExists(entry.data.itemPath)) {
|
ResourceManager::getInstance().fileExists(entry.data.imagePath)) {
|
||||||
auto item = std::make_shared<ImageComponent>(false, dynamic);
|
auto item = std::make_shared<ImageComponent>(false, dynamic);
|
||||||
item->setLinearInterpolation(mLinearInterpolation);
|
item->setLinearInterpolation(mLinearInterpolation);
|
||||||
item->setMipmapping(true);
|
item->setMipmapping(true);
|
||||||
item->setMaxSize(glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
|
item->setMaxSize(glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
|
||||||
item->setImage(entry.data.itemPath);
|
item->setImage(entry.data.imagePath);
|
||||||
item->applyTheme(theme, "system", "", ThemeFlags::ALL);
|
item->applyTheme(theme, "system", "", ThemeFlags::ALL);
|
||||||
|
if (mImageBrightness != 0.0)
|
||||||
|
item->setBrightness(mImageBrightness);
|
||||||
|
if (mImageSaturation != 1.0)
|
||||||
|
item->setSaturation(mImageSaturation);
|
||||||
|
if (mImageColorShift != 0xFFFFFFFF)
|
||||||
|
item->setColorShift(mImageColorShift);
|
||||||
|
if (mImageColorShiftEnd != mImageColorShift)
|
||||||
|
item->setColorShiftEnd(mImageColorShiftEnd);
|
||||||
|
if (!mImageColorGradientHorizontal)
|
||||||
|
item->setColorGradientHorizontal(false);
|
||||||
item->setRotateByTargetSize(true);
|
item->setRotateByTargetSize(true);
|
||||||
entry.data.item = item;
|
entry.data.item = item;
|
||||||
}
|
}
|
||||||
else if (entry.data.defaultItemPath != "" &&
|
else if (entry.data.defaultImagePath != "" &&
|
||||||
ResourceManager::getInstance().fileExists(entry.data.defaultItemPath)) {
|
ResourceManager::getInstance().fileExists(entry.data.defaultImagePath)) {
|
||||||
auto defaultItem = std::make_shared<ImageComponent>(false, dynamic);
|
auto defaultImage = std::make_shared<ImageComponent>(false, dynamic);
|
||||||
defaultItem->setLinearInterpolation(mLinearInterpolation);
|
defaultImage->setLinearInterpolation(mLinearInterpolation);
|
||||||
defaultItem->setMipmapping(true);
|
defaultImage->setMipmapping(true);
|
||||||
defaultItem->setMaxSize(
|
defaultImage->setMaxSize(
|
||||||
glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
|
glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
|
||||||
defaultItem->setImage(entry.data.defaultItemPath);
|
defaultImage->setImage(entry.data.defaultImagePath);
|
||||||
defaultItem->applyTheme(theme, "system", "", ThemeFlags::ALL);
|
defaultImage->applyTheme(theme, "system", "", ThemeFlags::ALL);
|
||||||
defaultItem->setRotateByTargetSize(true);
|
if (mImageBrightness != 0.0)
|
||||||
entry.data.item = defaultItem;
|
defaultImage->setBrightness(mImageBrightness);
|
||||||
|
if (mImageSaturation != 1.0)
|
||||||
|
defaultImage->setSaturation(mImageSaturation);
|
||||||
|
if (mImageColorShift != 0xFFFFFFFF)
|
||||||
|
defaultImage->setColorShift(mImageColorShift);
|
||||||
|
if (mImageColorShiftEnd != mImageColorShift)
|
||||||
|
defaultImage->setColorShiftEnd(mImageColorShiftEnd);
|
||||||
|
if (!mImageColorGradientHorizontal)
|
||||||
|
defaultImage->setColorGradientHorizontal(false);
|
||||||
|
defaultImage->setRotateByTargetSize(true);
|
||||||
|
// For the gamelist view the default image is applied in onDemandTextureLoad().
|
||||||
|
if (!mGamelistView)
|
||||||
|
entry.data.item = defaultImage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!entry.data.item) {
|
if (!entry.data.item) {
|
||||||
// If no item image is present, add item text as fallback.
|
// If no item image is present, add item text as fallback.
|
||||||
|
|
||||||
auto text = std::make_shared<TextComponent>(
|
auto text = std::make_shared<TextComponent>(
|
||||||
entry.name, mFont, 0x000000FF, mItemHorizontalAlignment, mItemVerticalAlignment,
|
entry.name, mFont, 0x000000FF, mItemHorizontalAlignment, mItemVerticalAlignment,
|
||||||
glm::vec3 {0.0f, 0.0f, 0.0f},
|
glm::vec3 {0.0f, 0.0f, 0.0f},
|
||||||
|
@ -313,13 +353,23 @@ void CarouselComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeDat
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void CarouselComponent<T>::updateEntry(Entry& entry, const std::shared_ptr<ThemeData>& theme)
|
void CarouselComponent<T>::updateEntry(Entry& entry, const std::shared_ptr<ThemeData>& theme)
|
||||||
{
|
{
|
||||||
if (entry.data.itemPath != "") {
|
if (entry.data.imagePath != "") {
|
||||||
auto item = std::make_shared<ImageComponent>(false, true);
|
auto item = std::make_shared<ImageComponent>(false, true);
|
||||||
item->setLinearInterpolation(mLinearInterpolation);
|
item->setLinearInterpolation(mLinearInterpolation);
|
||||||
item->setMipmapping(true);
|
item->setMipmapping(true);
|
||||||
item->setMaxSize(glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
|
item->setMaxSize(glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
|
||||||
item->setImage(entry.data.itemPath);
|
item->setImage(entry.data.imagePath);
|
||||||
item->applyTheme(theme, "system", "", ThemeFlags::ALL);
|
item->applyTheme(theme, "system", "", ThemeFlags::ALL);
|
||||||
|
if (mImageBrightness != 0.0)
|
||||||
|
item->setBrightness(mImageBrightness);
|
||||||
|
if (mImageSaturation != 1.0)
|
||||||
|
item->setSaturation(mImageSaturation);
|
||||||
|
if (mImageColorShift != 0xFFFFFFFF)
|
||||||
|
item->setColorShift(mImageColorShift);
|
||||||
|
if (mImageColorShiftEnd != mImageColorShift)
|
||||||
|
item->setColorShiftEnd(mImageColorShiftEnd);
|
||||||
|
if (!mImageColorGradientHorizontal)
|
||||||
|
item->setColorGradientHorizontal(false);
|
||||||
item->setRotateByTargetSize(true);
|
item->setRotateByTargetSize(true);
|
||||||
entry.data.item = item;
|
entry.data.item = item;
|
||||||
}
|
}
|
||||||
|
@ -349,7 +399,7 @@ void CarouselComponent<T>::updateEntry(Entry& entry, const std::shared_ptr<Theme
|
||||||
template <typename T> void CarouselComponent<T>::onDemandTextureLoad()
|
template <typename T> void CarouselComponent<T>::onDemandTextureLoad()
|
||||||
{
|
{
|
||||||
if constexpr (std::is_same_v<T, FileData*>) {
|
if constexpr (std::is_same_v<T, FileData*>) {
|
||||||
const int numEntries {static_cast<int>(mEntries.size())};
|
const int numEntries {size()};
|
||||||
const int center {getCursor()};
|
const int center {getCursor()};
|
||||||
const bool isWheel {mType == CarouselType::VERTICAL_WHEEL ||
|
const bool isWheel {mType == CarouselType::VERTICAL_WHEEL ||
|
||||||
mType == CarouselType::HORIZONTAL_WHEEL};
|
mType == CarouselType::HORIZONTAL_WHEEL};
|
||||||
|
@ -396,6 +446,7 @@ template <typename T> void CarouselComponent<T>::onDemandTextureLoad()
|
||||||
if (mVerticalOffset > 0.0f)
|
if (mVerticalOffset > 0.0f)
|
||||||
centerOffset = -centerOffset;
|
centerOffset = -centerOffset;
|
||||||
}
|
}
|
||||||
|
itemInclusion += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = center - itemInclusion - itemInclusionBefore;
|
for (int i = center - itemInclusion - itemInclusionBefore;
|
||||||
|
@ -409,30 +460,33 @@ template <typename T> void CarouselComponent<T>::onDemandTextureLoad()
|
||||||
|
|
||||||
auto& entry = mEntries.at(cursor);
|
auto& entry = mEntries.at(cursor);
|
||||||
|
|
||||||
if (entry.data.itemPath == "") {
|
if (entry.data.imagePath == "") {
|
||||||
FileData* game {entry.object};
|
FileData* game {entry.object};
|
||||||
|
|
||||||
if (mItemType == "" || mItemType == "marquee")
|
if (mImageType == "" || mImageType == "marquee")
|
||||||
entry.data.itemPath = game->getMarqueePath();
|
entry.data.imagePath = game->getMarqueePath();
|
||||||
else if (mItemType == "cover")
|
else if (mImageType == "cover")
|
||||||
entry.data.itemPath = game->getCoverPath();
|
entry.data.imagePath = game->getCoverPath();
|
||||||
else if (mItemType == "backcover")
|
else if (mImageType == "backcover")
|
||||||
entry.data.itemPath = game->getBackCoverPath();
|
entry.data.imagePath = game->getBackCoverPath();
|
||||||
else if (mItemType == "3dbox")
|
else if (mImageType == "3dbox")
|
||||||
entry.data.itemPath = game->get3DBoxPath();
|
entry.data.imagePath = game->get3DBoxPath();
|
||||||
else if (mItemType == "physicalmedia")
|
else if (mImageType == "physicalmedia")
|
||||||
entry.data.itemPath = game->getPhysicalMediaPath();
|
entry.data.imagePath = game->getPhysicalMediaPath();
|
||||||
else if (mItemType == "screenshot")
|
else if (mImageType == "screenshot")
|
||||||
entry.data.itemPath = game->getScreenshotPath();
|
entry.data.imagePath = game->getScreenshotPath();
|
||||||
else if (mItemType == "titlescreen")
|
else if (mImageType == "titlescreen")
|
||||||
entry.data.itemPath = game->getTitleScreenPath();
|
entry.data.imagePath = game->getTitleScreenPath();
|
||||||
else if (mItemType == "miximage")
|
else if (mImageType == "miximage")
|
||||||
entry.data.itemPath = game->getMiximagePath();
|
entry.data.imagePath = game->getMiximagePath();
|
||||||
else if (mItemType == "fanart")
|
else if (mImageType == "fanart")
|
||||||
entry.data.itemPath = game->getFanArtPath();
|
entry.data.imagePath = game->getFanArtPath();
|
||||||
else if (mItemType == "none") // Display the game name as text.
|
else if (mImageType == "none") // Display the game name as text.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (entry.data.imagePath == "")
|
||||||
|
entry.data.imagePath = entry.data.defaultImagePath;
|
||||||
|
|
||||||
auto theme = game->getSystem()->getTheme();
|
auto theme = game->getSystem()->getTheme();
|
||||||
updateEntry(entry, theme);
|
updateEntry(entry, theme);
|
||||||
}
|
}
|
||||||
|
@ -527,7 +581,8 @@ template <typename T> bool CarouselComponent<T>::input(InputConfig* config, Inpu
|
||||||
config->isMappedLike("rightshoulder", input) ||
|
config->isMappedLike("rightshoulder", input) ||
|
||||||
config->isMappedLike("lefttrigger", input) ||
|
config->isMappedLike("lefttrigger", input) ||
|
||||||
config->isMappedLike("righttrigger", input)) {
|
config->isMappedLike("righttrigger", input)) {
|
||||||
onCursorChanged(CursorState::CURSOR_STOPPED);
|
if (isScrolling())
|
||||||
|
onCursorChanged(CursorState::CURSOR_STOPPED);
|
||||||
List::listInput(0);
|
List::listInput(0);
|
||||||
mTriggerJump = false;
|
mTriggerJump = false;
|
||||||
}
|
}
|
||||||
|
@ -812,6 +867,33 @@ template <typename T> void CarouselComponent<T>::render(const glm::mat4& parentT
|
||||||
if (renderItems.size() == 1) {
|
if (renderItems.size() == 1) {
|
||||||
renderItemsSorted.emplace_back(renderItems.front());
|
renderItemsSorted.emplace_back(renderItems.front());
|
||||||
}
|
}
|
||||||
|
else if (!isWheel && mItemStacking != ItemStacking::CENTERED) {
|
||||||
|
if (mItemStacking == ItemStacking::ASCENDING) {
|
||||||
|
renderItemsSorted.insert(renderItemsSorted.begin(),
|
||||||
|
std::make_move_iterator(renderItems.begin()),
|
||||||
|
std::make_move_iterator(renderItems.end()));
|
||||||
|
}
|
||||||
|
else if (mItemStacking == ItemStacking::ASCENDING_RAISED) {
|
||||||
|
for (size_t i {0}; i < renderItems.size(); ++i) {
|
||||||
|
if (i == static_cast<size_t>(belowCenter))
|
||||||
|
continue;
|
||||||
|
renderItemsSorted.emplace_back(std::move(renderItems[i]));
|
||||||
|
}
|
||||||
|
renderItemsSorted.emplace_back(std::move(renderItems[belowCenter]));
|
||||||
|
}
|
||||||
|
else if (mItemStacking == ItemStacking::DESCENDING) {
|
||||||
|
for (size_t i {renderItems.size()}; i > 0; --i)
|
||||||
|
renderItemsSorted.emplace_back(std::move(renderItems[i - 1]));
|
||||||
|
}
|
||||||
|
else if (mItemStacking == ItemStacking::DESCENDING_RAISED) {
|
||||||
|
for (size_t i {renderItems.size()}; i > 0; --i) {
|
||||||
|
if (i - 1 == static_cast<size_t>(belowCenter))
|
||||||
|
continue;
|
||||||
|
renderItemsSorted.emplace_back(std::move(renderItems[i - 1]));
|
||||||
|
}
|
||||||
|
renderItemsSorted.emplace_back(std::move(renderItems[belowCenter]));
|
||||||
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
// Make sure that overlapping items are rendered in the correct order.
|
// Make sure that overlapping items are rendered in the correct order.
|
||||||
size_t zeroDistanceEntry {0};
|
size_t zeroDistanceEntry {0};
|
||||||
|
@ -886,8 +968,8 @@ template <typename T> void CarouselComponent<T>::render(const glm::mat4& parentT
|
||||||
|
|
||||||
// TODO: Rewrite to use "real" reflections instead of this hack.
|
// TODO: Rewrite to use "real" reflections instead of this hack.
|
||||||
// Don't attempt to add reflections for text entries.
|
// Don't attempt to add reflections for text entries.
|
||||||
if (mReflections && (mEntries.at(renderItem.index).data.itemPath != "" ||
|
if (mReflections && (mEntries.at(renderItem.index).data.imagePath != "" ||
|
||||||
mEntries.at(renderItem.index).data.defaultItemPath != "")) {
|
mEntries.at(renderItem.index).data.defaultImagePath != "")) {
|
||||||
glm::mat4 reflectionTrans {glm::translate(
|
glm::mat4 reflectionTrans {glm::translate(
|
||||||
renderItem.trans, glm::vec3 {0.0f, comp->getSize().y * renderItem.scale, 0.0f})};
|
renderItem.trans, glm::vec3 {0.0f, comp->getSize().y * renderItem.scale, 0.0f})};
|
||||||
float falloff {glm::clamp(mReflectionsFalloff, 0.0f, 1.0f)};
|
float falloff {glm::clamp(mReflectionsFalloff, 0.0f, 1.0f)};
|
||||||
|
@ -913,9 +995,6 @@ void CarouselComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& element,
|
const std::string& element,
|
||||||
unsigned int properties)
|
unsigned int properties)
|
||||||
{
|
{
|
||||||
using namespace ThemeFlags;
|
|
||||||
const ThemeData::ThemeElement* elem {theme->getElement(view, element, "carousel")};
|
|
||||||
|
|
||||||
mSize.x = Renderer::getScreenWidth();
|
mSize.x = Renderer::getScreenWidth();
|
||||||
mSize.y = Renderer::getScreenHeight() * 0.23240f;
|
mSize.y = Renderer::getScreenHeight() * 0.23240f;
|
||||||
GuiComponent::mPosition.x = 0.0f;
|
GuiComponent::mPosition.x = 0.0f;
|
||||||
|
@ -924,6 +1003,9 @@ void CarouselComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
mCarouselColorEnd = 0xFFFFFFD8;
|
mCarouselColorEnd = 0xFFFFFFD8;
|
||||||
mZIndex = mDefaultZIndex;
|
mZIndex = mDefaultZIndex;
|
||||||
|
|
||||||
|
using namespace ThemeFlags;
|
||||||
|
const ThemeData::ThemeElement* elem {theme->getElement(view, element, "carousel")};
|
||||||
|
|
||||||
if (!elem)
|
if (!elem)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1004,27 +1086,36 @@ void CarouselComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
itemSize * glm::vec2(Renderer::getScreenWidth(), Renderer::getScreenHeight());
|
itemSize * glm::vec2(Renderer::getScreenWidth(), Renderer::getScreenHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elem->has("itemScale"))
|
if (elem->has("itemStacking")) {
|
||||||
mItemScale = glm::clamp(elem->get<float>("itemScale"), 0.2f, 3.0f);
|
const std::string& itemStacking {elem->get<std::string>("itemStacking")};
|
||||||
|
if (itemStacking == "ascending") {
|
||||||
if (elem->has("itemTransitions")) {
|
mItemStacking = ItemStacking::ASCENDING;
|
||||||
const std::string& itemTransitions {elem->get<std::string>("itemTransitions")};
|
|
||||||
if (itemTransitions == "slide") {
|
|
||||||
mInstantItemTransitions = false;
|
|
||||||
}
|
}
|
||||||
else if (itemTransitions == "instant") {
|
else if (itemStacking == "ascendingRaised") {
|
||||||
mInstantItemTransitions = true;
|
mItemStacking = ItemStacking::ASCENDING_RAISED;
|
||||||
|
}
|
||||||
|
else if (itemStacking == "descending") {
|
||||||
|
mItemStacking = ItemStacking::DESCENDING;
|
||||||
|
}
|
||||||
|
else if (itemStacking == "descendingRaised") {
|
||||||
|
mItemStacking = ItemStacking::DESCENDING_RAISED;
|
||||||
|
}
|
||||||
|
else if (itemStacking == "centered") {
|
||||||
|
mItemStacking = ItemStacking::CENTERED;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mInstantItemTransitions = false;
|
mItemStacking = ItemStacking::CENTERED;
|
||||||
LOG(LogWarning) << "CarouselComponent: Invalid theme configuration, property "
|
LOG(LogWarning) << "CarouselComponent: Invalid theme configuration, property "
|
||||||
"\"itemTransitions\" for element \""
|
"\"itemStacking\" for element \""
|
||||||
<< element.substr(9) << "\" defined as \"" << itemTransitions
|
<< element.substr(9) << "\" defined as \"" << itemStacking << "\"";
|
||||||
<< "\"";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (elem->has("itemScale"))
|
||||||
|
mItemScale = glm::clamp(elem->get<float>("itemScale"), 0.2f, 3.0f);
|
||||||
|
|
||||||
if (elem->has("itemInterpolation")) {
|
if (elem->has("itemInterpolation")) {
|
||||||
|
// TEMPORARY: Backward compatiblity due to property name changes.
|
||||||
const std::string& itemInterpolation {elem->get<std::string>("itemInterpolation")};
|
const std::string& itemInterpolation {elem->get<std::string>("itemInterpolation")};
|
||||||
if (itemInterpolation == "linear") {
|
if (itemInterpolation == "linear") {
|
||||||
mLinearInterpolation = true;
|
mLinearInterpolation = true;
|
||||||
|
@ -1041,6 +1132,46 @@ void CarouselComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (elem->has("imageBrightness"))
|
||||||
|
mImageBrightness = glm::clamp(elem->get<float>("imageBrightness"), -2.0f, 2.0f);
|
||||||
|
|
||||||
|
if (elem->has("imageSaturation"))
|
||||||
|
mImageSaturation = glm::clamp(elem->get<float>("imageSaturation"), 0.0f, 1.0f);
|
||||||
|
|
||||||
|
if (elem->has("imageInterpolation")) {
|
||||||
|
const std::string& imageInterpolation {elem->get<std::string>("imageInterpolation")};
|
||||||
|
if (imageInterpolation == "linear") {
|
||||||
|
mLinearInterpolation = true;
|
||||||
|
}
|
||||||
|
else if (imageInterpolation == "nearest") {
|
||||||
|
mLinearInterpolation = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mLinearInterpolation = true;
|
||||||
|
LOG(LogWarning) << "CarouselComponent: Invalid theme configuration, property "
|
||||||
|
"\"imageInterpolation\" for element \""
|
||||||
|
<< element.substr(9) << "\" defined as \"" << imageInterpolation
|
||||||
|
<< "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elem->has("itemTransitions")) {
|
||||||
|
const std::string& itemTransitions {elem->get<std::string>("itemTransitions")};
|
||||||
|
if (itemTransitions == "animate") {
|
||||||
|
mInstantItemTransitions = false;
|
||||||
|
}
|
||||||
|
else if (itemTransitions == "instant") {
|
||||||
|
mInstantItemTransitions = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mInstantItemTransitions = false;
|
||||||
|
LOG(LogWarning) << "CarouselComponent: Invalid theme configuration, property "
|
||||||
|
"\"itemTransitions\" for element \""
|
||||||
|
<< element.substr(9) << "\" defined as \"" << itemTransitions
|
||||||
|
<< "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (elem->has("itemRotation"))
|
if (elem->has("itemRotation"))
|
||||||
mItemRotation = elem->get<float>("itemRotation");
|
mItemRotation = elem->get<float>("itemRotation");
|
||||||
|
|
||||||
|
@ -1050,6 +1181,29 @@ void CarouselComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
mItemAxisHorizontal =
|
mItemAxisHorizontal =
|
||||||
(elem->has("itemAxisHorizontal") && elem->get<bool>("itemAxisHorizontal"));
|
(elem->has("itemAxisHorizontal") && elem->get<bool>("itemAxisHorizontal"));
|
||||||
|
|
||||||
|
if (elem->has("imageColor")) {
|
||||||
|
mImageColorShift = elem->get<unsigned int>("imageColor");
|
||||||
|
mImageColorShiftEnd = mImageColorShift;
|
||||||
|
}
|
||||||
|
if (elem->has("imageColorEnd"))
|
||||||
|
mImageColorShiftEnd = elem->get<unsigned int>("imageColorEnd");
|
||||||
|
|
||||||
|
if (elem->has("imageGradientType")) {
|
||||||
|
const std::string& gradientType {elem->get<std::string>("imageGradientType")};
|
||||||
|
if (gradientType == "horizontal") {
|
||||||
|
mImageColorGradientHorizontal = true;
|
||||||
|
}
|
||||||
|
else if (gradientType == "vertical") {
|
||||||
|
mImageColorGradientHorizontal = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mImageColorGradientHorizontal = true;
|
||||||
|
LOG(LogWarning) << "CarouselComponent: Invalid theme configuration, property "
|
||||||
|
"\"imageGradientType\" for element \""
|
||||||
|
<< element.substr(9) << "\" defined as \"" << gradientType << "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (elem->has("itemHorizontalAlignment")) {
|
if (elem->has("itemHorizontalAlignment")) {
|
||||||
const std::string& alignment {elem->get<std::string>("itemHorizontalAlignment")};
|
const std::string& alignment {elem->get<std::string>("itemHorizontalAlignment")};
|
||||||
if (alignment == "left" && mType != CarouselType::HORIZONTAL) {
|
if (alignment == "left" && mType != CarouselType::HORIZONTAL) {
|
||||||
|
@ -1203,7 +1357,7 @@ void CarouselComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
}
|
}
|
||||||
|
|
||||||
// For non-legacy themes, scale the font size with the itemScale property value.
|
// For non-legacy themes, scale the font size with the itemScale property value.
|
||||||
mFont = Font::getFromTheme(elem, properties, mFont, 0.0f, mLegacyMode,
|
mFont = Font::getFromTheme(elem, properties, mFont, 0.0f, false, mLegacyMode,
|
||||||
(mLegacyMode ? 1.0f : (mItemScale >= 1.0f ? mItemScale : 1.0f)));
|
(mLegacyMode ? 1.0f : (mItemScale >= 1.0f ? mItemScale : 1.0f)));
|
||||||
|
|
||||||
if (elem->has("textColor"))
|
if (elem->has("textColor"))
|
||||||
|
@ -1326,11 +1480,25 @@ template <typename T> void CarouselComponent<T>::onCursorChanged(const CursorSta
|
||||||
mPositiveDirection = false;
|
mPositiveDirection = false;
|
||||||
|
|
||||||
mEntryCamTarget = endPos;
|
mEntryCamTarget = endPos;
|
||||||
|
float animTime {400.0f};
|
||||||
|
float timeDiff {1.0f};
|
||||||
|
|
||||||
|
// If startPos is inbetween two positions then reduce the time slightly as the distance will
|
||||||
|
// be shorter meaning the animation would play for too long if not compensated for.
|
||||||
|
if (mScrollVelocity == 1)
|
||||||
|
timeDiff = endPos - startPos;
|
||||||
|
else if (mScrollVelocity == -1)
|
||||||
|
timeDiff = startPos - endPos;
|
||||||
|
|
||||||
|
if (timeDiff != 1.0f)
|
||||||
|
animTime =
|
||||||
|
glm::clamp(std::fabs(glm::mix(0.0f, animTime, timeDiff * 1.5f)), 200.0f, animTime);
|
||||||
|
|
||||||
Animation* anim {new LambdaAnimation(
|
Animation* anim {new LambdaAnimation(
|
||||||
[this, startPos, endPos, posMax](float t) {
|
[this, startPos, endPos, posMax](float t) {
|
||||||
t -= 1;
|
// Non-linear interpolation.
|
||||||
float f {glm::mix(startPos, endPos, t * t * t + 1)};
|
t = 1.0f - (1.0f - t) * (1.0f - t);
|
||||||
|
float f {(endPos * t) + (startPos * (1.0f - t))};
|
||||||
if (f < 0)
|
if (f < 0)
|
||||||
f += posMax;
|
f += posMax;
|
||||||
if (f >= posMax)
|
if (f >= posMax)
|
||||||
|
@ -1338,7 +1506,7 @@ template <typename T> void CarouselComponent<T>::onCursorChanged(const CursorSta
|
||||||
|
|
||||||
mEntryCamOffset = f;
|
mEntryCamOffset = f;
|
||||||
},
|
},
|
||||||
500)};
|
static_cast<int>(animTime))};
|
||||||
|
|
||||||
GuiComponent::setAnimation(anim, 0, nullptr, false, 0);
|
GuiComponent::setAnimation(anim, 0, nullptr, false, 0);
|
||||||
|
|
||||||
|
@ -1346,4 +1514,4 @@ template <typename T> void CarouselComponent<T>::onCursorChanged(const CursorSta
|
||||||
mCursorChangedCallback(state);
|
mCursorChangedCallback(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_CAROUSEL_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_PRIMARY_CAROUSEL_COMPONENT_H
|
||||||
|
|
1249
es-core/src/components/primary/GridComponent.h
Normal file
1249
es-core/src/components/primary/GridComponent.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -3,17 +3,18 @@
|
||||||
// EmulationStation Desktop Edition
|
// EmulationStation Desktop Edition
|
||||||
// PrimaryComponent.h
|
// PrimaryComponent.h
|
||||||
//
|
//
|
||||||
// Base class for the primary components (carousel and textlist).
|
// Base class for the primary components (carousel, grid and textlist).
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ES_CORE_COMPONENTS_PRIMARY_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_PRIMARY_PRIMARY_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_PRIMARY_COMPONENT_H
|
#define ES_CORE_COMPONENTS_PRIMARY_PRIMARY_COMPONENT_H
|
||||||
|
|
||||||
template <typename T> class PrimaryComponent : public virtual GuiComponent
|
template <typename T> class PrimaryComponent : public virtual GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum class PrimaryType {
|
enum class PrimaryType {
|
||||||
CAROUSEL,
|
CAROUSEL,
|
||||||
|
GRID,
|
||||||
TEXTLIST
|
TEXTLIST
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -52,4 +53,4 @@ public:
|
||||||
virtual void setAlignment(PrimaryAlignment align) {}
|
virtual void setAlignment(PrimaryAlignment align) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_PRIMARY_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_PRIMARY_PRIMARY_COMPONENT_H
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
// EmulationStation Desktop Edition
|
// EmulationStation Desktop Edition
|
||||||
// TextListComponent.h
|
// TextListComponent.h
|
||||||
//
|
//
|
||||||
// Text list used for displaying and navigating the gamelist views.
|
// Text list, usable in both the system and gamelist views.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ES_CORE_COMPONENTS_TEXT_LIST_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_PRIMARY_TEXT_LIST_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_TEXT_LIST_COMPONENT_H
|
#define ES_CORE_COMPONENTS_PRIMARY_TEXT_LIST_COMPONENT_H
|
||||||
|
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Sound.h"
|
#include "Sound.h"
|
||||||
|
@ -57,14 +57,14 @@ public:
|
||||||
|
|
||||||
void setAlignment(PrimaryAlignment align) override { mAlignment = align; }
|
void setAlignment(PrimaryAlignment align) override { mAlignment = align; }
|
||||||
|
|
||||||
void setCursorChangedCallback(const std::function<void(CursorState state)>& func) override
|
|
||||||
{
|
|
||||||
mCursorChangedCallback = func;
|
|
||||||
}
|
|
||||||
void setCancelTransitionsCallback(const std::function<void()>& func) override
|
void setCancelTransitionsCallback(const std::function<void()>& func) override
|
||||||
{
|
{
|
||||||
mCancelTransitionsCallback = func;
|
mCancelTransitionsCallback = func;
|
||||||
}
|
}
|
||||||
|
void setCursorChangedCallback(const std::function<void(CursorState state)>& func) override
|
||||||
|
{
|
||||||
|
mCursorChangedCallback = func;
|
||||||
|
}
|
||||||
|
|
||||||
void setFont(const std::shared_ptr<Font>& font)
|
void setFont(const std::shared_ptr<Font>& font)
|
||||||
{
|
{
|
||||||
|
@ -82,7 +82,7 @@ public:
|
||||||
return mLetterCaseGroupedCollections;
|
return mLetterCaseGroupedCollections;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
void onShow() override { mLoopTime = 0; }
|
void onShow() override { mLoopTime = 0; }
|
||||||
void onScroll() override
|
void onScroll() override
|
||||||
{
|
{
|
||||||
|
@ -90,8 +90,6 @@ protected:
|
||||||
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
||||||
}
|
}
|
||||||
void onCursorChanged(const CursorState& state) override;
|
void onCursorChanged(const CursorState& state) override;
|
||||||
|
|
||||||
private:
|
|
||||||
bool isScrolling() const override { return List::isScrolling(); }
|
bool isScrolling() const override { return List::isScrolling(); }
|
||||||
void stopScrolling() override { List::stopScrolling(); }
|
void stopScrolling() override { List::stopScrolling(); }
|
||||||
const int getScrollingVelocity() override { return List::getScrollingVelocity(); }
|
const int getScrollingVelocity() override { return List::getScrollingVelocity(); }
|
||||||
|
@ -111,6 +109,7 @@ private:
|
||||||
|
|
||||||
Renderer* mRenderer;
|
Renderer* mRenderer;
|
||||||
std::function<void()> mCancelTransitionsCallback;
|
std::function<void()> mCancelTransitionsCallback;
|
||||||
|
std::function<void(CursorState state)> mCursorChangedCallback;
|
||||||
float mCamOffset;
|
float mCamOffset;
|
||||||
int mPreviousScrollVelocity;
|
int mPreviousScrollVelocity;
|
||||||
|
|
||||||
|
@ -122,7 +121,6 @@ private:
|
||||||
PrimaryAlignment mAlignment;
|
PrimaryAlignment mAlignment;
|
||||||
float mHorizontalMargin;
|
float mHorizontalMargin;
|
||||||
|
|
||||||
std::function<void(CursorState state)> mCursorChangedCallback;
|
|
||||||
ImageComponent mSelectorImage;
|
ImageComponent mSelectorImage;
|
||||||
|
|
||||||
std::shared_ptr<Font> mFont;
|
std::shared_ptr<Font> mFont;
|
||||||
|
@ -203,15 +201,19 @@ template <typename T> bool TextListComponent<T>::input(InputConfig* config, Inpu
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (config->isMappedLike("leftshoulder", input)) {
|
if (config->isMappedLike("leftshoulder", input)) {
|
||||||
if (mCancelTransitionsCallback)
|
if (mCursor != 0) {
|
||||||
mCancelTransitionsCallback();
|
if (mCancelTransitionsCallback)
|
||||||
List::listInput(-10);
|
mCancelTransitionsCallback();
|
||||||
|
List::listInput(-10);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (config->isMappedLike("rightshoulder", input)) {
|
if (config->isMappedLike("rightshoulder", input)) {
|
||||||
if (mCancelTransitionsCallback)
|
if (mCursor != size() - 1) {
|
||||||
mCancelTransitionsCallback();
|
if (mCancelTransitionsCallback)
|
||||||
List::listInput(10);
|
mCancelTransitionsCallback();
|
||||||
|
List::listInput(10);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (config->isMappedLike("lefttrigger", input)) {
|
if (config->isMappedLike("lefttrigger", input)) {
|
||||||
|
@ -235,14 +237,9 @@ template <typename T> bool TextListComponent<T>::input(InputConfig* config, Inpu
|
||||||
config->isMappedLike("rightshoulder", input) ||
|
config->isMappedLike("rightshoulder", input) ||
|
||||||
config->isMappedLike("lefttrigger", input) ||
|
config->isMappedLike("lefttrigger", input) ||
|
||||||
config->isMappedLike("righttrigger", input)) {
|
config->isMappedLike("righttrigger", input)) {
|
||||||
if constexpr (std::is_same_v<T, SystemData*>) {
|
if (isScrolling())
|
||||||
if (isScrolling())
|
onCursorChanged(CursorState::CURSOR_STOPPED);
|
||||||
onCursorChanged(CursorState::CURSOR_STOPPED);
|
List::listInput(0);
|
||||||
List::listInput(0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
List::stopScrolling();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -480,7 +477,14 @@ void TextListComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& element,
|
const std::string& element,
|
||||||
unsigned int properties)
|
unsigned int properties)
|
||||||
{
|
{
|
||||||
|
mSize.x = Renderer::getScreenWidth();
|
||||||
|
mSize.y = Renderer::getScreenHeight() * 0.8f;
|
||||||
|
GuiComponent::mPosition.x = 0.0f;
|
||||||
|
GuiComponent::mPosition.y = Renderer::getScreenHeight() * 0.1f;
|
||||||
|
setAlignment(PrimaryAlignment::ALIGN_LEFT);
|
||||||
|
|
||||||
GuiComponent::applyTheme(theme, view, element, properties);
|
GuiComponent::applyTheme(theme, view, element, properties);
|
||||||
|
|
||||||
using namespace ThemeFlags;
|
using namespace ThemeFlags;
|
||||||
const ThemeData::ThemeElement* elem {theme->getElement(view, element, "textlist")};
|
const ThemeData::ThemeElement* elem {theme->getElement(view, element, "textlist")};
|
||||||
|
|
||||||
|
@ -525,7 +529,7 @@ void TextListComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
mSelectedSecondaryColor = mSelectedColor;
|
mSelectedSecondaryColor = mSelectedColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFont(Font::getFromTheme(elem, properties, mFont, 0.0f, mLegacyMode));
|
setFont(Font::getFromTheme(elem, properties, mFont, 0.0f, false, mLegacyMode));
|
||||||
if (mLegacyMode)
|
if (mLegacyMode)
|
||||||
mFont->useLegacyMaxGlyphHeight();
|
mFont->useLegacyMaxGlyphHeight();
|
||||||
const float selectorHeight {mFont->getHeight(mLineSpacing)};
|
const float selectorHeight {mFont->getHeight(mLineSpacing)};
|
||||||
|
@ -550,8 +554,8 @@ void TextListComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
<< "\"";
|
<< "\"";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Legacy themes only.
|
|
||||||
else if (elem->has("alignment")) {
|
else if (elem->has("alignment")) {
|
||||||
|
// Legacy themes only.
|
||||||
const std::string& alignment {elem->get<std::string>("alignment")};
|
const std::string& alignment {elem->get<std::string>("alignment")};
|
||||||
if (alignment == "left") {
|
if (alignment == "left") {
|
||||||
setAlignment(PrimaryAlignment::ALIGN_LEFT);
|
setAlignment(PrimaryAlignment::ALIGN_LEFT);
|
||||||
|
@ -703,10 +707,26 @@ template <typename T> void TextListComponent<T>::onCursorChanged(const CursorSta
|
||||||
float posMax {static_cast<float>(mEntries.size())};
|
float posMax {static_cast<float>(mEntries.size())};
|
||||||
float endPos {static_cast<float>(mCursor)};
|
float endPos {static_cast<float>(mCursor)};
|
||||||
|
|
||||||
|
float animTime {400.0f};
|
||||||
|
float timeDiff {1.0f};
|
||||||
|
|
||||||
|
// If startPos is inbetween two positions then reduce the time slightly as the distance will
|
||||||
|
// be shorter meaning the animation would play for too long if not compensated for.
|
||||||
|
if (mScrollVelocity == 1)
|
||||||
|
timeDiff = endPos - startPos;
|
||||||
|
else if (mScrollVelocity == -1)
|
||||||
|
timeDiff = startPos - endPos;
|
||||||
|
|
||||||
|
if (timeDiff != 1.0f)
|
||||||
|
animTime =
|
||||||
|
glm::clamp(std::fabs(glm::mix(0.0f, animTime, timeDiff * 1.5f)), 200.0f, animTime);
|
||||||
|
|
||||||
Animation* anim {new LambdaAnimation(
|
Animation* anim {new LambdaAnimation(
|
||||||
[this, startPos, endPos, posMax](float t) {
|
[this, startPos, endPos, posMax](float t) {
|
||||||
t -= 1;
|
// Non-linear interpolation.
|
||||||
float f {glm::mix(startPos, endPos, t * t * t + 1)};
|
t = 1.0f - (1.0f - t) * (1.0f - t);
|
||||||
|
float f {(endPos * t) + (startPos * (1.0f - t))};
|
||||||
|
|
||||||
if (f < 0)
|
if (f < 0)
|
||||||
f += posMax;
|
f += posMax;
|
||||||
if (f >= posMax)
|
if (f >= posMax)
|
||||||
|
@ -714,7 +734,7 @@ template <typename T> void TextListComponent<T>::onCursorChanged(const CursorSta
|
||||||
|
|
||||||
mCamOffset = f;
|
mCamOffset = f;
|
||||||
},
|
},
|
||||||
500)};
|
static_cast<int>(animTime))};
|
||||||
|
|
||||||
GuiComponent::setAnimation(anim, 0, nullptr, false, 0);
|
GuiComponent::setAnimation(anim, 0, nullptr, false, 0);
|
||||||
}
|
}
|
||||||
|
@ -723,4 +743,4 @@ template <typename T> void TextListComponent<T>::onCursorChanged(const CursorSta
|
||||||
mCursorChangedCallback(state);
|
mCursorChangedCallback(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_TEXT_LIST_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_PRIMARY_TEXT_LIST_COMPONENT_H
|
||||||
|
|
|
@ -61,6 +61,7 @@ public:
|
||||||
glm::vec2 texcoord;
|
glm::vec2 texcoord;
|
||||||
unsigned int color;
|
unsigned int color;
|
||||||
glm::vec4 clipregion;
|
glm::vec4 clipregion;
|
||||||
|
float brightness;
|
||||||
float opacity;
|
float opacity;
|
||||||
float saturation;
|
float saturation;
|
||||||
float dimming;
|
float dimming;
|
||||||
|
@ -69,7 +70,8 @@ public:
|
||||||
unsigned int shaderFlags;
|
unsigned int shaderFlags;
|
||||||
|
|
||||||
Vertex()
|
Vertex()
|
||||||
: opacity {1.0f}
|
: brightness {0.0f}
|
||||||
|
, opacity {1.0f}
|
||||||
, saturation {1.0f}
|
, saturation {1.0f}
|
||||||
, dimming {1.0f}
|
, dimming {1.0f}
|
||||||
, reflectionsFalloff {0.0f}
|
, reflectionsFalloff {0.0f}
|
||||||
|
@ -86,6 +88,7 @@ public:
|
||||||
, texcoord(textureCoord)
|
, texcoord(textureCoord)
|
||||||
, color(color)
|
, color(color)
|
||||||
, clipregion(clipRegion)
|
, clipregion(clipRegion)
|
||||||
|
, brightness {0.0f}
|
||||||
, opacity {1.0f}
|
, opacity {1.0f}
|
||||||
, saturation {1.0f}
|
, saturation {1.0f}
|
||||||
, dimming {1.0f}
|
, dimming {1.0f}
|
||||||
|
|
|
@ -443,6 +443,7 @@ void RendererOpenGL::drawTriangleStrips(const Vertex* vertices,
|
||||||
GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices,
|
GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices,
|
||||||
GL_DYNAMIC_DRAW));
|
GL_DYNAMIC_DRAW));
|
||||||
mCoreShader->setClipRegion(vertices->clipregion);
|
mCoreShader->setClipRegion(vertices->clipregion);
|
||||||
|
mCoreShader->setBrightness(vertices->brightness);
|
||||||
mCoreShader->setOpacity(vertices->opacity);
|
mCoreShader->setOpacity(vertices->opacity);
|
||||||
mCoreShader->setSaturation(vertices->saturation);
|
mCoreShader->setSaturation(vertices->saturation);
|
||||||
mCoreShader->setDimming(vertices->dimming);
|
mCoreShader->setDimming(vertices->dimming);
|
||||||
|
@ -517,6 +518,7 @@ void RendererOpenGL::drawTriangleStrips(const Vertex* vertices,
|
||||||
GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices,
|
GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices,
|
||||||
GL_DYNAMIC_DRAW));
|
GL_DYNAMIC_DRAW));
|
||||||
mScanlinelShader->setOpacity(vertices->opacity);
|
mScanlinelShader->setOpacity(vertices->opacity);
|
||||||
|
mScanlinelShader->setBrightness(vertices->brightness);
|
||||||
mScanlinelShader->setSaturation(vertices->saturation);
|
mScanlinelShader->setSaturation(vertices->saturation);
|
||||||
mScanlinelShader->setTextureSize({shaderWidth, shaderHeight});
|
mScanlinelShader->setTextureSize({shaderWidth, shaderHeight});
|
||||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
|
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
|
||||||
|
@ -548,7 +550,7 @@ void RendererOpenGL::shaderPostprocessing(unsigned int shaders,
|
||||||
vertices->opacity = parameters.opacity;
|
vertices->opacity = parameters.opacity;
|
||||||
vertices->saturation = parameters.saturation;
|
vertices->saturation = parameters.saturation;
|
||||||
vertices->dimming = parameters.dimming;
|
vertices->dimming = parameters.dimming;
|
||||||
vertices->shaderFlags = ShaderFlags::POST_PROCESSING;
|
vertices->shaderFlags = ShaderFlags::POST_PROCESSING | ShaderFlags::PREMULTIPLIED;
|
||||||
|
|
||||||
if (shaders & Shader::CORE)
|
if (shaders & Shader::CORE)
|
||||||
shaderList.push_back(Shader::CORE);
|
shaderList.push_back(Shader::CORE);
|
||||||
|
|
|
@ -19,6 +19,7 @@ ShaderOpenGL::ShaderOpenGL()
|
||||||
, mShaderTextureCoord {0}
|
, mShaderTextureCoord {0}
|
||||||
, mShaderColor {0}
|
, mShaderColor {0}
|
||||||
, mShaderTextureSize {0}
|
, mShaderTextureSize {0}
|
||||||
|
, mShaderBrightness {0}
|
||||||
, mShaderOpacity {0}
|
, mShaderOpacity {0}
|
||||||
, mShaderSaturation {0}
|
, mShaderSaturation {0}
|
||||||
, mShaderDimming {0}
|
, mShaderDimming {0}
|
||||||
|
@ -122,6 +123,7 @@ void ShaderOpenGL::getVariableLocations(GLuint programID)
|
||||||
mShaderColor = glGetAttribLocation(mProgramID, "colorVertex");
|
mShaderColor = glGetAttribLocation(mProgramID, "colorVertex");
|
||||||
mShaderTextureSize = glGetUniformLocation(mProgramID, "textureSize");
|
mShaderTextureSize = glGetUniformLocation(mProgramID, "textureSize");
|
||||||
mShaderClipRegion = glGetUniformLocation(mProgramID, "clipRegion");
|
mShaderClipRegion = glGetUniformLocation(mProgramID, "clipRegion");
|
||||||
|
mShaderBrightness = glGetUniformLocation(mProgramID, "brightness");
|
||||||
mShaderOpacity = glGetUniformLocation(mProgramID, "opacity");
|
mShaderOpacity = glGetUniformLocation(mProgramID, "opacity");
|
||||||
mShaderSaturation = glGetUniformLocation(mProgramID, "saturation");
|
mShaderSaturation = glGetUniformLocation(mProgramID, "saturation");
|
||||||
mShaderDimming = glGetUniformLocation(mProgramID, "dimming");
|
mShaderDimming = glGetUniformLocation(mProgramID, "dimming");
|
||||||
|
@ -166,6 +168,12 @@ void ShaderOpenGL::setClipRegion(glm::vec4 clipRegion)
|
||||||
clipRegion[3]));
|
clipRegion[3]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShaderOpenGL::setBrightness(GLfloat brightness)
|
||||||
|
{
|
||||||
|
if (mShaderBrightness != -1)
|
||||||
|
GL_CHECK_ERROR(glUniform1f(mShaderBrightness, brightness));
|
||||||
|
}
|
||||||
|
|
||||||
void ShaderOpenGL::setOpacity(GLfloat opacity)
|
void ShaderOpenGL::setOpacity(GLfloat opacity)
|
||||||
{
|
{
|
||||||
if (mShaderOpacity != -1)
|
if (mShaderOpacity != -1)
|
||||||
|
|
|
@ -69,6 +69,7 @@ public:
|
||||||
void setAttribPointers();
|
void setAttribPointers();
|
||||||
void setTextureSize(std::array<GLfloat, 2> shaderVec2);
|
void setTextureSize(std::array<GLfloat, 2> shaderVec2);
|
||||||
void setClipRegion(glm::vec4 clipRegion);
|
void setClipRegion(glm::vec4 clipRegion);
|
||||||
|
void setBrightness(GLfloat brightness);
|
||||||
void setOpacity(GLfloat opacity);
|
void setOpacity(GLfloat opacity);
|
||||||
void setSaturation(GLfloat saturation);
|
void setSaturation(GLfloat saturation);
|
||||||
void setDimming(GLfloat dimming);
|
void setDimming(GLfloat dimming);
|
||||||
|
@ -95,6 +96,7 @@ private:
|
||||||
GLint mShaderColor;
|
GLint mShaderColor;
|
||||||
GLint mShaderClipRegion;
|
GLint mShaderClipRegion;
|
||||||
GLint mShaderTextureSize;
|
GLint mShaderTextureSize;
|
||||||
|
GLint mShaderBrightness;
|
||||||
GLint mShaderOpacity;
|
GLint mShaderOpacity;
|
||||||
GLint mShaderSaturation;
|
GLint mShaderSaturation;
|
||||||
GLint mShaderDimming;
|
GLint mShaderDimming;
|
||||||
|
|
|
@ -15,10 +15,11 @@
|
||||||
#include "utils/PlatformUtil.h"
|
#include "utils/PlatformUtil.h"
|
||||||
#include "utils/StringUtil.h"
|
#include "utils/StringUtil.h"
|
||||||
|
|
||||||
Font::Font(float size, const std::string& path)
|
Font::Font(float size, const std::string& path, const bool linearMagnify)
|
||||||
: mRenderer {Renderer::getInstance()}
|
: mRenderer {Renderer::getInstance()}
|
||||||
, mPath(path)
|
, mPath(path)
|
||||||
, mFontSize {size}
|
, mFontSize {size}
|
||||||
|
, mLinearMagnify {linearMagnify}
|
||||||
, mLetterHeight {0.0f}
|
, mLetterHeight {0.0f}
|
||||||
, mMaxGlyphHeight {static_cast<int>(std::round(size))}
|
, mMaxGlyphHeight {static_cast<int>(std::round(size))}
|
||||||
, mLegacyMaxGlyphHeight {0}
|
, mLegacyMaxGlyphHeight {0}
|
||||||
|
@ -46,7 +47,8 @@ Font::~Font()
|
||||||
{
|
{
|
||||||
unload(ResourceManager::getInstance());
|
unload(ResourceManager::getInstance());
|
||||||
|
|
||||||
auto fontEntry = sFontMap.find(std::pair<std::string, float>(mPath, mFontSize));
|
auto fontEntry =
|
||||||
|
sFontMap.find(std::tuple<float, std::string, bool>(mFontSize, mPath, mLinearMagnify));
|
||||||
|
|
||||||
if (fontEntry != sFontMap.cend())
|
if (fontEntry != sFontMap.cend())
|
||||||
sFontMap.erase(fontEntry);
|
sFontMap.erase(fontEntry);
|
||||||
|
@ -57,11 +59,11 @@ Font::~Font()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Font> Font::get(float size, const std::string& path)
|
std::shared_ptr<Font> Font::get(float size, const std::string& path, const bool linearMagnify)
|
||||||
{
|
{
|
||||||
const std::string canonicalPath {Utils::FileSystem::getCanonicalPath(path)};
|
const std::string canonicalPath {Utils::FileSystem::getCanonicalPath(path)};
|
||||||
const std::pair<std::string, float> def {
|
const std::tuple<float, std::string, bool> def {
|
||||||
canonicalPath.empty() ? getDefaultPath() : canonicalPath, size};
|
size, canonicalPath.empty() ? getDefaultPath() : canonicalPath, linearMagnify};
|
||||||
|
|
||||||
auto foundFont = sFontMap.find(def);
|
auto foundFont = sFontMap.find(def);
|
||||||
if (foundFont != sFontMap.cend()) {
|
if (foundFont != sFontMap.cend()) {
|
||||||
|
@ -69,7 +71,7 @@ std::shared_ptr<Font> Font::get(float size, const std::string& path)
|
||||||
return foundFont->second.lock();
|
return foundFont->second.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Font> font {new Font(def.second, def.first)};
|
std::shared_ptr<Font> font {new Font(std::get<0>(def), std::get<1>(def), std::get<2>(def))};
|
||||||
sFontMap[def] = std::weak_ptr<Font>(font);
|
sFontMap[def] = std::weak_ptr<Font>(font);
|
||||||
ResourceManager::getInstance().addReloadable(font);
|
ResourceManager::getInstance().addReloadable(font);
|
||||||
return font;
|
return font;
|
||||||
|
@ -423,6 +425,7 @@ std::shared_ptr<Font> Font::getFromTheme(const ThemeData::ThemeElement* elem,
|
||||||
unsigned int properties,
|
unsigned int properties,
|
||||||
const std::shared_ptr<Font>& orig,
|
const std::shared_ptr<Font>& orig,
|
||||||
const float maxHeight,
|
const float maxHeight,
|
||||||
|
const bool linearMagnify,
|
||||||
const bool legacyTheme,
|
const bool legacyTheme,
|
||||||
const float sizeMultiplier)
|
const float sizeMultiplier)
|
||||||
{
|
{
|
||||||
|
@ -459,9 +462,9 @@ std::shared_ptr<Font> Font::getFromTheme(const ThemeData::ThemeElement* elem,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mLegacyTheme)
|
if (mLegacyTheme)
|
||||||
return get(std::floor(size), path);
|
return get(std::floor(size), path, false);
|
||||||
else
|
else
|
||||||
return get(size, path);
|
return get(size, path, linearMagnify);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Font::getMemUsage() const
|
size_t Font::getMemUsage() const
|
||||||
|
@ -522,11 +525,12 @@ std::vector<std::string> Font::getFallbackFontPaths()
|
||||||
return fontPaths;
|
return fontPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
Font::FontTexture::FontTexture(const int mFontSize)
|
Font::FontTexture::FontTexture(const int mFontSize, const bool linearMagnifyArg)
|
||||||
{
|
{
|
||||||
textureId = 0;
|
textureId = 0;
|
||||||
rowHeight = 0;
|
rowHeight = 0;
|
||||||
writePos = glm::ivec2 {0, 0};
|
writePos = glm::ivec2 {0, 0};
|
||||||
|
linearMagnify = linearMagnifyArg;
|
||||||
|
|
||||||
// Set the texture to a reasonable size, if we run out of space for adding glyphs then
|
// Set the texture to a reasonable size, if we run out of space for adding glyphs then
|
||||||
// more textures will be created dynamically.
|
// more textures will be created dynamically.
|
||||||
|
@ -573,9 +577,9 @@ void Font::FontTexture::initTexture()
|
||||||
// glyphs will not be visible. That would otherwise lead to edge artifacts as these pixels
|
// glyphs will not be visible. That would otherwise lead to edge artifacts as these pixels
|
||||||
// would get sampled during scaling.
|
// would get sampled during scaling.
|
||||||
std::vector<uint8_t> texture(textureSize.x * textureSize.y * 4, 0);
|
std::vector<uint8_t> texture(textureSize.x * textureSize.y * 4, 0);
|
||||||
textureId =
|
textureId = Renderer::getInstance()->createTexture(Renderer::TextureType::RED, true,
|
||||||
Renderer::getInstance()->createTexture(Renderer::TextureType::RED, true, false, false,
|
linearMagnify, false, false, textureSize.x,
|
||||||
false, textureSize.x, textureSize.y, &texture[0]);
|
textureSize.y, &texture[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Font::FontTexture::deinitTexture()
|
void Font::FontTexture::deinitTexture()
|
||||||
|
@ -664,7 +668,8 @@ void Font::getTextureForNewGlyph(const glm::ivec2& glyphSize,
|
||||||
return; // Yes.
|
return; // Yes.
|
||||||
}
|
}
|
||||||
|
|
||||||
mTextures.emplace_back(std::make_unique<FontTexture>(static_cast<int>(std::round(mFontSize))));
|
mTextures.emplace_back(
|
||||||
|
std::make_unique<FontTexture>(static_cast<int>(std::round(mFontSize)), mLinearMagnify));
|
||||||
tex_out = mTextures.back().get();
|
tex_out = mTextures.back().get();
|
||||||
tex_out->initTexture();
|
tex_out->initTexture();
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,9 @@ class Font : public IReloadable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~Font();
|
virtual ~Font();
|
||||||
static std::shared_ptr<Font> get(float size, const std::string& path = getDefaultPath());
|
static std::shared_ptr<Font> get(float size,
|
||||||
|
const std::string& path = getDefaultPath(),
|
||||||
|
const bool linearMagnify = false);
|
||||||
|
|
||||||
// Returns the expected size of a string when rendered. Extra spacing is applied to the Y axis.
|
// Returns the expected size of a string when rendered. Extra spacing is applied to the Y axis.
|
||||||
glm::vec2 sizeText(std::string text, float lineSpacing = 1.5f);
|
glm::vec2 sizeText(std::string text, float lineSpacing = 1.5f);
|
||||||
|
@ -93,6 +95,7 @@ public:
|
||||||
unsigned int properties,
|
unsigned int properties,
|
||||||
const std::shared_ptr<Font>& orig,
|
const std::shared_ptr<Font>& orig,
|
||||||
const float maxHeight = 0.0f,
|
const float maxHeight = 0.0f,
|
||||||
|
const bool linearMagnify = false,
|
||||||
const bool legacyTheme = false,
|
const bool legacyTheme = false,
|
||||||
const float sizeMultiplier = 1.0f);
|
const float sizeMultiplier = 1.0f);
|
||||||
|
|
||||||
|
@ -102,7 +105,7 @@ public:
|
||||||
static size_t getTotalMemUsage();
|
static size_t getTotalMemUsage();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Font(float size, const std::string& path);
|
Font(float size, const std::string& path, const bool linearMagnify);
|
||||||
static void initLibrary();
|
static void initLibrary();
|
||||||
|
|
||||||
struct FontTexture {
|
struct FontTexture {
|
||||||
|
@ -110,8 +113,9 @@ private:
|
||||||
glm::ivec2 textureSize;
|
glm::ivec2 textureSize;
|
||||||
glm::ivec2 writePos;
|
glm::ivec2 writePos;
|
||||||
int rowHeight;
|
int rowHeight;
|
||||||
|
bool linearMagnify;
|
||||||
|
|
||||||
FontTexture(const int mFontSize);
|
FontTexture(const int mFontSize, const bool linearMagnifyArg);
|
||||||
~FontTexture();
|
~FontTexture();
|
||||||
bool findEmpty(const glm::ivec2& size, glm::ivec2& cursor_out);
|
bool findEmpty(const glm::ivec2& size, glm::ivec2& cursor_out);
|
||||||
|
|
||||||
|
@ -161,7 +165,7 @@ private:
|
||||||
void clearFaceCache() { mFaceCache.clear(); }
|
void clearFaceCache() { mFaceCache.clear(); }
|
||||||
|
|
||||||
static inline FT_Library sLibrary {nullptr};
|
static inline FT_Library sLibrary {nullptr};
|
||||||
static inline std::map<std::pair<std::string, float>, std::weak_ptr<Font>> sFontMap;
|
static inline std::map<std::tuple<float, std::string, bool>, std::weak_ptr<Font>> sFontMap;
|
||||||
static inline bool mLegacyTheme {false};
|
static inline bool mLegacyTheme {false};
|
||||||
|
|
||||||
Renderer* mRenderer;
|
Renderer* mRenderer;
|
||||||
|
@ -171,6 +175,7 @@ private:
|
||||||
|
|
||||||
const std::string mPath;
|
const std::string mPath;
|
||||||
float mFontSize;
|
float mFontSize;
|
||||||
|
const bool mLinearMagnify;
|
||||||
float mLetterHeight;
|
float mLetterHeight;
|
||||||
int mMaxGlyphHeight;
|
int mMaxGlyphHeight;
|
||||||
int mLegacyMaxGlyphHeight;
|
int mLegacyMaxGlyphHeight;
|
||||||
|
|
|
@ -47,14 +47,14 @@
|
||||||
// build environment is broken.
|
// build environment is broken.
|
||||||
#if defined(__unix__)
|
#if defined(__unix__)
|
||||||
#if defined(ES_INSTALL_PREFIX)
|
#if defined(ES_INSTALL_PREFIX)
|
||||||
std::string installPrefix = ES_INSTALL_PREFIX;
|
const std::string installPrefix {ES_INSTALL_PREFIX};
|
||||||
#else
|
#else
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
std::string installPrefix = "/usr";
|
const std::string installPrefix {"/usr"};
|
||||||
#elif defined(__NetBSD__)
|
#elif defined(__NetBSD__)
|
||||||
std::string installPrefix = "/usr/pkg";
|
const std::string installPrefix {"/usr/pkg"};
|
||||||
#else
|
#else
|
||||||
std::string installPrefix = "/usr/local";
|
const std::string installPrefix {"/usr/local"};
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
@ -63,12 +63,12 @@ namespace Utils
|
||||||
{
|
{
|
||||||
namespace FileSystem
|
namespace FileSystem
|
||||||
{
|
{
|
||||||
static std::string homePath = "";
|
static std::string homePath;
|
||||||
static std::string exePath = "";
|
static std::string exePath;
|
||||||
|
|
||||||
StringList getDirContent(const std::string& path, const bool recursive)
|
StringList getDirContent(const std::string& path, const bool recursive)
|
||||||
{
|
{
|
||||||
std::string genericPath = getGenericPath(path);
|
const std::string& genericPath {getGenericPath(path)};
|
||||||
StringList contentList;
|
StringList contentList;
|
||||||
|
|
||||||
// Only parse the directory, if it's a directory.
|
// Only parse the directory, if it's a directory.
|
||||||
|
@ -76,17 +76,19 @@ namespace Utils
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
WIN32_FIND_DATAW findData;
|
WIN32_FIND_DATAW findData;
|
||||||
std::wstring wildcard = Utils::String::stringToWideString(genericPath) + L"/*";
|
const std::wstring& wildcard {Utils::String::stringToWideString(genericPath) +
|
||||||
HANDLE hFind = FindFirstFileW(wildcard.c_str(), &findData);
|
L"/*"};
|
||||||
|
const HANDLE hFind {FindFirstFileW(wildcard.c_str(), &findData)};
|
||||||
|
|
||||||
if (hFind != INVALID_HANDLE_VALUE) {
|
if (hFind != INVALID_HANDLE_VALUE) {
|
||||||
// Loop over all files in the directory.
|
// Loop over all files in the directory.
|
||||||
do {
|
do {
|
||||||
std::string name = Utils::String::wideStringToString(findData.cFileName);
|
const std::string& name {
|
||||||
|
Utils::String::wideStringToString(findData.cFileName)};
|
||||||
// Ignore "." and ".."
|
// Ignore "." and ".."
|
||||||
if ((name != ".") && (name != "..")) {
|
if ((name != ".") && (name != "..")) {
|
||||||
std::string fullName(getGenericPath(genericPath + "/" + name));
|
const std::string& fullName {getGenericPath(genericPath + "/" + name)};
|
||||||
contentList.push_back(fullName);
|
contentList.emplace_back(fullName);
|
||||||
|
|
||||||
if (recursive && isDirectory(fullName)) {
|
if (recursive && isDirectory(fullName)) {
|
||||||
contentList.sort();
|
contentList.sort();
|
||||||
|
@ -97,18 +99,18 @@ namespace Utils
|
||||||
FindClose(hFind);
|
FindClose(hFind);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
DIR* dir = opendir(genericPath.c_str());
|
DIR* dir {opendir(genericPath.c_str())};
|
||||||
|
|
||||||
if (dir != nullptr) {
|
if (dir != nullptr) {
|
||||||
struct dirent* entry;
|
struct dirent* entry;
|
||||||
// Loop over all files in the directory.
|
// Loop over all files in the directory.
|
||||||
while ((entry = readdir(dir)) != nullptr) {
|
while ((entry = readdir(dir)) != nullptr) {
|
||||||
std::string name(entry->d_name);
|
const std::string& name(entry->d_name);
|
||||||
|
|
||||||
// Ignore "." and ".."
|
// Ignore "." and ".."
|
||||||
if ((name != ".") && (name != "..")) {
|
if ((name != ".") && (name != "..")) {
|
||||||
std::string fullName(getGenericPath(genericPath + "/" + name));
|
const std::string& fullName {getGenericPath(genericPath + "/" + name)};
|
||||||
contentList.push_back(fullName);
|
contentList.emplace_back(fullName);
|
||||||
|
|
||||||
if (recursive && isDirectory(fullName)) {
|
if (recursive && isDirectory(fullName)) {
|
||||||
contentList.sort();
|
contentList.sort();
|
||||||
|
@ -132,13 +134,13 @@ namespace Utils
|
||||||
if (entry == std::string::npos)
|
if (entry == std::string::npos)
|
||||||
return files;
|
return files;
|
||||||
|
|
||||||
std::string parent {getParent(pattern)};
|
const std::string& parent {getParent(pattern)};
|
||||||
|
|
||||||
// Don't allow wildcard matching for the parent directory.
|
// Don't allow wildcard matching for the parent directory.
|
||||||
if (entry <= parent.size())
|
if (entry <= parent.size())
|
||||||
return files;
|
return files;
|
||||||
|
|
||||||
StringList dirContent {getDirContent(parent)};
|
const StringList& dirContent {getDirContent(parent)};
|
||||||
|
|
||||||
if (dirContent.size() == 0)
|
if (dirContent.size() == 0)
|
||||||
return files;
|
return files;
|
||||||
|
@ -172,19 +174,19 @@ namespace Utils
|
||||||
StringList getPathList(const std::string& path)
|
StringList getPathList(const std::string& path)
|
||||||
{
|
{
|
||||||
StringList pathList;
|
StringList pathList;
|
||||||
std::string genericPath = getGenericPath(path);
|
const std::string& genericPath {getGenericPath(path)};
|
||||||
size_t start = 0;
|
size_t start {0};
|
||||||
size_t end = 0;
|
size_t end {0};
|
||||||
|
|
||||||
// Split at '/'
|
// Split at '/'
|
||||||
while ((end = genericPath.find("/", start)) != std::string::npos) {
|
while ((end = genericPath.find("/", start)) != std::string::npos) {
|
||||||
if (end != start)
|
if (end != start)
|
||||||
pathList.push_back(std::string(genericPath, start, end - start));
|
pathList.emplace_back(std::string {genericPath, start, end - start});
|
||||||
start = end + 1;
|
start = end + 1;
|
||||||
}
|
}
|
||||||
// Add last folder / file to pathList.
|
// Add last folder / file to pathList.
|
||||||
if (start != genericPath.size())
|
if (start != genericPath.size())
|
||||||
pathList.push_back(std::string(genericPath, start, genericPath.size() - start));
|
pathList.emplace_back(std::string {genericPath, start, genericPath.size() - start});
|
||||||
|
|
||||||
return pathList;
|
return pathList;
|
||||||
}
|
}
|
||||||
|
@ -223,7 +225,7 @@ namespace Utils
|
||||||
#else
|
#else
|
||||||
|
|
||||||
if (!homePath.length()) {
|
if (!homePath.length()) {
|
||||||
std::string envHome = getenv("HOME");
|
const std::string& envHome {getenv("HOME")};
|
||||||
if (envHome.length())
|
if (envHome.length())
|
||||||
homePath = getGenericPath(envHome);
|
homePath = getGenericPath(envHome);
|
||||||
}
|
}
|
||||||
|
@ -260,17 +262,18 @@ namespace Utils
|
||||||
// Ugly hack to compensate for the Flatpak sandbox restrictions. We traverse
|
// Ugly hack to compensate for the Flatpak sandbox restrictions. We traverse
|
||||||
// this hardcoded list of paths and use the "which" command to check outside the
|
// this hardcoded list of paths and use the "which" command to check outside the
|
||||||
// sandbox if the emulator binary exists.
|
// sandbox if the emulator binary exists.
|
||||||
std::string pathVariable {"/var/lib/flatpak/exports/bin:/usr/bin:/usr/local/"
|
const std::string& pathVariable {
|
||||||
"bin:/usr/local/sbin:/usr/sbin:/sbin:/bin:/usr/games:/usr/"
|
"/var/lib/flatpak/exports/bin:/usr/bin:/usr/local/"
|
||||||
"local/games:/snap/bin:/var/lib/snapd/snap/bin"};
|
"bin:/usr/local/sbin:/usr/sbin:/sbin:/bin:/usr/games:/usr/"
|
||||||
|
"local/games:/snap/bin:/var/lib/snapd/snap/bin"};
|
||||||
|
|
||||||
std::vector<std::string> pathList {
|
const std::vector<std::string>& pathList {
|
||||||
Utils::String::delimitedStringToVector(pathVariable, ":")};
|
Utils::String::delimitedStringToVector(pathVariable, ":")};
|
||||||
|
|
||||||
// Using a temporary file is the only viable solution I've found to communicate
|
// Using a temporary file is the only viable solution I've found to communicate
|
||||||
// between the sandbox and the outside world.
|
// between the sandbox and the outside world.
|
||||||
std::string tempFile {Utils::FileSystem::getHomePath() + "/.emulationstation/" +
|
const std::string& tempFile {Utils::FileSystem::getHomePath() + "/.emulationstation/" +
|
||||||
".flatpak_emulator_binary_path.tmp"};
|
".flatpak_emulator_binary_path.tmp"};
|
||||||
|
|
||||||
std::string emulatorPath;
|
std::string emulatorPath;
|
||||||
|
|
||||||
|
@ -293,9 +296,9 @@ namespace Utils
|
||||||
|
|
||||||
return emulatorPath;
|
return emulatorPath;
|
||||||
#else
|
#else
|
||||||
std::string pathVariable {std::string(getenv("PATH"))};
|
const std::string& pathVariable {std::string {getenv("PATH")}};
|
||||||
|
|
||||||
std::vector<std::string> pathList {
|
const std::vector<std::string>& pathList {
|
||||||
Utils::String::delimitedStringToVector(pathVariable, ":")};
|
Utils::String::delimitedStringToVector(pathVariable, ":")};
|
||||||
|
|
||||||
std::string pathTest;
|
std::string pathTest;
|
||||||
|
@ -313,7 +316,7 @@ namespace Utils
|
||||||
|
|
||||||
void setExePath(const std::string& path)
|
void setExePath(const std::string& path)
|
||||||
{
|
{
|
||||||
constexpr int pathMax = 32767;
|
constexpr int pathMax {32767};
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
std::wstring result(pathMax, 0);
|
std::wstring result(pathMax, 0);
|
||||||
if (GetModuleFileNameW(nullptr, &result[0], pathMax) != 0)
|
if (GetModuleFileNameW(nullptr, &result[0], pathMax) != 0)
|
||||||
|
@ -349,20 +352,22 @@ namespace Utils
|
||||||
|
|
||||||
std::string getPreferredPath(const std::string& path)
|
std::string getPreferredPath(const std::string& path)
|
||||||
{
|
{
|
||||||
std::string preferredPath = path;
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
size_t offset = std::string::npos;
|
std::string preferredPath {path};
|
||||||
|
size_t offset {std::string::npos};
|
||||||
// Convert '/' to '\\'
|
// Convert '/' to '\\'
|
||||||
while ((offset = preferredPath.find('/')) != std::string::npos)
|
while ((offset = preferredPath.find('/')) != std::string::npos)
|
||||||
preferredPath.replace(offset, 1, "\\");
|
preferredPath.replace(offset, 1, "\\");
|
||||||
|
#else
|
||||||
|
const std::string& preferredPath {path};
|
||||||
#endif
|
#endif
|
||||||
return preferredPath;
|
return preferredPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getGenericPath(const std::string& path)
|
std::string getGenericPath(const std::string& path)
|
||||||
{
|
{
|
||||||
std::string genericPath = path;
|
std::string genericPath {path};
|
||||||
size_t offset = std::string::npos;
|
size_t offset {std::string::npos};
|
||||||
|
|
||||||
// Remove "\\\\?\\"
|
// Remove "\\\\?\\"
|
||||||
if ((genericPath.find("\\\\?\\")) == 0)
|
if ((genericPath.find("\\\\?\\")) == 0)
|
||||||
|
@ -386,7 +391,7 @@ namespace Utils
|
||||||
|
|
||||||
std::string getEscapedPath(const std::string& path)
|
std::string getEscapedPath(const std::string& path)
|
||||||
{
|
{
|
||||||
std::string escapedPath = getGenericPath(path);
|
std::string escapedPath {getGenericPath(path)};
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
// Windows escapes stuff by just putting everything in quotes.
|
// Windows escapes stuff by just putting everything in quotes.
|
||||||
|
@ -396,12 +401,12 @@ namespace Utils
|
||||||
return getPreferredPath(escapedPath);
|
return getPreferredPath(escapedPath);
|
||||||
#else
|
#else
|
||||||
// Insert a backslash before most characters that would mess up a bash path.
|
// Insert a backslash before most characters that would mess up a bash path.
|
||||||
const char* invalidChars = "\\ '\"!$^&*(){}[]?;<>";
|
const char* invalidChars {"\\ '\"!$^&*(){}[]?;<>"};
|
||||||
const char* invalidChar = invalidChars;
|
const char* invalidChar {invalidChars};
|
||||||
|
|
||||||
while (*invalidChar) {
|
while (*invalidChar) {
|
||||||
size_t start = 0;
|
size_t start {0};
|
||||||
size_t offset = 0;
|
size_t offset {0};
|
||||||
|
|
||||||
while ((offset = escapedPath.find(*invalidChar, start)) != std::string::npos) {
|
while ((offset = escapedPath.find(*invalidChar, start)) != std::string::npos) {
|
||||||
start = offset + 1;
|
start = offset + 1;
|
||||||
|
@ -423,17 +428,17 @@ namespace Utils
|
||||||
if ((path[0] == ':') && (path[1] == '/'))
|
if ((path[0] == ':') && (path[1] == '/'))
|
||||||
return path;
|
return path;
|
||||||
|
|
||||||
std::string canonicalPath = exists(path) ? getAbsolutePath(path) : getGenericPath(path);
|
std::string canonicalPath {exists(path) ? getAbsolutePath(path) : getGenericPath(path)};
|
||||||
|
|
||||||
// Cleanup path.
|
// Cleanup path.
|
||||||
bool scan = true;
|
bool scan {true};
|
||||||
while (scan) {
|
while (scan) {
|
||||||
StringList pathList = getPathList(canonicalPath);
|
const StringList& pathList {getPathList(canonicalPath)};
|
||||||
|
|
||||||
canonicalPath.clear();
|
canonicalPath.clear();
|
||||||
scan = false;
|
scan = false;
|
||||||
|
|
||||||
for (StringList::const_iterator it = pathList.cbegin(); it != pathList.cend();
|
for (StringList::const_iterator it {pathList.cbegin()}; it != pathList.cend();
|
||||||
++it) {
|
++it) {
|
||||||
// Ignore empty.
|
// Ignore empty.
|
||||||
if ((*it).empty())
|
if ((*it).empty())
|
||||||
|
@ -458,7 +463,7 @@ namespace Utils
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (isSymlink(canonicalPath)) {
|
if (isSymlink(canonicalPath)) {
|
||||||
std::string resolved = resolveSymlink(canonicalPath);
|
const std::string& resolved {resolveSymlink(canonicalPath)};
|
||||||
|
|
||||||
if (resolved.empty())
|
if (resolved.empty())
|
||||||
return "";
|
return "";
|
||||||
|
@ -481,8 +486,9 @@ namespace Utils
|
||||||
|
|
||||||
std::string getAbsolutePath(const std::string& path, const std::string& base)
|
std::string getAbsolutePath(const std::string& path, const std::string& base)
|
||||||
{
|
{
|
||||||
std::string absolutePath = getGenericPath(path);
|
const std::string& absolutePath {getGenericPath(path)};
|
||||||
std::string baseVar = isAbsolute(base) ? getGenericPath(base) : getAbsolutePath(base);
|
const std::string& baseVar {isAbsolute(base) ? getGenericPath(base) :
|
||||||
|
getAbsolutePath(base)};
|
||||||
|
|
||||||
return isAbsolute(absolutePath) ? absolutePath :
|
return isAbsolute(absolutePath) ? absolutePath :
|
||||||
getGenericPath(baseVar + "/" + absolutePath);
|
getGenericPath(baseVar + "/" + absolutePath);
|
||||||
|
@ -490,8 +496,8 @@ namespace Utils
|
||||||
|
|
||||||
std::string getParent(const std::string& path)
|
std::string getParent(const std::string& path)
|
||||||
{
|
{
|
||||||
std::string genericPath = getGenericPath(path);
|
std::string genericPath {getGenericPath(path)};
|
||||||
size_t offset = std::string::npos;
|
size_t offset {std::string::npos};
|
||||||
|
|
||||||
// Find last '/' and erase it.
|
// Find last '/' and erase it.
|
||||||
if ((offset = genericPath.find_last_of('/')) != std::string::npos)
|
if ((offset = genericPath.find_last_of('/')) != std::string::npos)
|
||||||
|
@ -503,13 +509,13 @@ namespace Utils
|
||||||
|
|
||||||
std::string getFileName(const std::string& path)
|
std::string getFileName(const std::string& path)
|
||||||
{
|
{
|
||||||
std::string genericPath = getGenericPath(path);
|
const std::string& genericPath {getGenericPath(path)};
|
||||||
size_t offset = std::string::npos;
|
size_t offset {std::string::npos};
|
||||||
|
|
||||||
// Find last '/' and return the filename.
|
// Find last '/' and return the filename.
|
||||||
if ((offset = genericPath.find_last_of('/')) != std::string::npos)
|
if ((offset = genericPath.find_last_of('/')) != std::string::npos)
|
||||||
return ((genericPath[offset + 1] == 0) ? "." :
|
return ((genericPath[offset + 1] == 0) ? "." :
|
||||||
std::string(genericPath, offset + 1));
|
std::string {genericPath, offset + 1});
|
||||||
|
|
||||||
// No '/' found, entire path is a filename.
|
// No '/' found, entire path is a filename.
|
||||||
return genericPath;
|
return genericPath;
|
||||||
|
@ -517,8 +523,8 @@ namespace Utils
|
||||||
|
|
||||||
std::string getStem(const std::string& path)
|
std::string getStem(const std::string& path)
|
||||||
{
|
{
|
||||||
std::string fileName = getFileName(path);
|
std::string fileName {getFileName(path)};
|
||||||
size_t offset = std::string::npos;
|
size_t offset {std::string::npos};
|
||||||
|
|
||||||
// Empty fileName.
|
// Empty fileName.
|
||||||
if (fileName == ".")
|
if (fileName == ".")
|
||||||
|
@ -536,8 +542,8 @@ namespace Utils
|
||||||
|
|
||||||
std::string getExtension(const std::string& path)
|
std::string getExtension(const std::string& path)
|
||||||
{
|
{
|
||||||
std::string fileName = getFileName(path);
|
const std::string& fileName {getFileName(path)};
|
||||||
size_t offset = std::string::npos;
|
size_t offset {std::string::npos};
|
||||||
|
|
||||||
// Empty fileName.
|
// Empty fileName.
|
||||||
if (fileName == ".")
|
if (fileName == ".")
|
||||||
|
@ -545,7 +551,7 @@ namespace Utils
|
||||||
|
|
||||||
// Find last '.' and return the extension.
|
// Find last '.' and return the extension.
|
||||||
if ((offset = fileName.find_last_of('.')) != std::string::npos)
|
if ((offset = fileName.find_last_of('.')) != std::string::npos)
|
||||||
return std::string(fileName, offset);
|
return std::string {fileName, offset};
|
||||||
|
|
||||||
// No '.' found, filename has no extension.
|
// No '.' found, filename has no extension.
|
||||||
return ".";
|
return ".";
|
||||||
|
@ -561,9 +567,9 @@ namespace Utils
|
||||||
const std::string& relativeTo,
|
const std::string& relativeTo,
|
||||||
const bool allowHome)
|
const bool allowHome)
|
||||||
{
|
{
|
||||||
std::string genericPath = getGenericPath(path);
|
const std::string& genericPath {getGenericPath(path)};
|
||||||
std::string relativeToVar =
|
const std::string& relativeToVar {isDirectory(relativeTo) ? getGenericPath(relativeTo) :
|
||||||
isDirectory(relativeTo) ? getGenericPath(relativeTo) : getParent(relativeTo);
|
getParent(relativeTo)};
|
||||||
|
|
||||||
// Nothing to resolve.
|
// Nothing to resolve.
|
||||||
if (!genericPath.length())
|
if (!genericPath.length())
|
||||||
|
@ -585,8 +591,8 @@ namespace Utils
|
||||||
const std::string& relativeTo,
|
const std::string& relativeTo,
|
||||||
const bool allowHome)
|
const bool allowHome)
|
||||||
{
|
{
|
||||||
bool contains = false;
|
bool contains {false};
|
||||||
std::string relativePath = removeCommonPath(path, relativeTo, contains);
|
std::string relativePath {removeCommonPath(path, relativeTo, contains)};
|
||||||
|
|
||||||
if (contains)
|
if (contains)
|
||||||
return ("./" + relativePath);
|
return ("./" + relativePath);
|
||||||
|
@ -604,9 +610,9 @@ namespace Utils
|
||||||
const std::string& commonArg,
|
const std::string& commonArg,
|
||||||
bool& contains)
|
bool& contains)
|
||||||
{
|
{
|
||||||
std::string genericPath = getGenericPath(path);
|
const std::string& genericPath {getGenericPath(path)};
|
||||||
std::string common =
|
const std::string& common {isDirectory(commonArg) ? getGenericPath(commonArg) :
|
||||||
isDirectory(commonArg) ? getGenericPath(commonArg) : getParent(commonArg);
|
getParent(commonArg)};
|
||||||
|
|
||||||
if (genericPath.find(common) == 0) {
|
if (genericPath.find(common) == 0) {
|
||||||
contains = true;
|
contains = true;
|
||||||
|
@ -619,7 +625,7 @@ namespace Utils
|
||||||
|
|
||||||
std::string resolveSymlink(const std::string& path)
|
std::string resolveSymlink(const std::string& path)
|
||||||
{
|
{
|
||||||
std::string genericPath = getGenericPath(path);
|
const std::string& genericPath {getGenericPath(path)};
|
||||||
std::string resolved;
|
std::string resolved;
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
|
@ -675,10 +681,10 @@ namespace Utils
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
std::ifstream sourceFile(Utils::String::stringToWideString(sourcePath).c_str(),
|
std::ifstream sourceFile {Utils::String::stringToWideString(sourcePath).c_str(),
|
||||||
std::ios::binary);
|
std::ios::binary};
|
||||||
#else
|
#else
|
||||||
std::ifstream sourceFile(sourcePath, std::ios::binary);
|
std::ifstream sourceFile {sourcePath, std::ios::binary};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (sourceFile.fail()) {
|
if (sourceFile.fail()) {
|
||||||
|
@ -689,10 +695,10 @@ namespace Utils
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
std::ofstream targetFile(Utils::String::stringToWideString(destinationPath).c_str(),
|
std::ofstream targetFile {Utils::String::stringToWideString(destinationPath).c_str(),
|
||||||
std::ios::binary);
|
std::ios::binary};
|
||||||
#else
|
#else
|
||||||
std::ofstream targetFile(destinationPath, std::ios::binary);
|
std::ofstream targetFile {destinationPath, std::ios::binary};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (targetFile.fail()) {
|
if (targetFile.fail()) {
|
||||||
|
@ -744,7 +750,7 @@ namespace Utils
|
||||||
|
|
||||||
bool removeFile(const std::string& path)
|
bool removeFile(const std::string& path)
|
||||||
{
|
{
|
||||||
std::string genericPath = getGenericPath(path);
|
const std::string& genericPath {getGenericPath(path)};
|
||||||
|
|
||||||
// Don't remove if it doesn't exists.
|
// Don't remove if it doesn't exists.
|
||||||
if (!exists(genericPath))
|
if (!exists(genericPath))
|
||||||
|
@ -798,7 +804,7 @@ namespace Utils
|
||||||
|
|
||||||
bool createDirectory(const std::string& path)
|
bool createDirectory(const std::string& path)
|
||||||
{
|
{
|
||||||
std::string genericPath = getGenericPath(path);
|
const std::string& genericPath {getGenericPath(path)};
|
||||||
|
|
||||||
if (exists(genericPath))
|
if (exists(genericPath))
|
||||||
return true;
|
return true;
|
||||||
|
@ -812,7 +818,7 @@ namespace Utils
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Failed to create directory, try to create the parent.
|
// Failed to create directory, try to create the parent.
|
||||||
std::string parent = getParent(genericPath);
|
const std::string& parent {getParent(genericPath)};
|
||||||
|
|
||||||
// Only try to create parent if it's not identical to genericPath.
|
// Only try to create parent if it's not identical to genericPath.
|
||||||
if (parent != genericPath)
|
if (parent != genericPath)
|
||||||
|
@ -829,7 +835,7 @@ namespace Utils
|
||||||
|
|
||||||
bool exists(const std::string& path)
|
bool exists(const std::string& path)
|
||||||
{
|
{
|
||||||
std::string genericPath = getGenericPath(path);
|
const std::string& genericPath {getGenericPath(path)};
|
||||||
|
|
||||||
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||||
struct stat info;
|
struct stat info;
|
||||||
|
@ -846,7 +852,7 @@ namespace Utils
|
||||||
bool driveExists(const std::string& path)
|
bool driveExists(const std::string& path)
|
||||||
{
|
{
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
std::string genericPath = getGenericPath(path);
|
std::string genericPath {getGenericPath(path)};
|
||||||
// Try to add a dot or a backslash and a dot depending on how the drive
|
// Try to add a dot or a backslash and a dot depending on how the drive
|
||||||
// letter was defined by the user.
|
// letter was defined by the user.
|
||||||
if (genericPath.length() == 2 && genericPath.at(1) == ':')
|
if (genericPath.length() == 2 && genericPath.at(1) == ':')
|
||||||
|
@ -864,7 +870,7 @@ namespace Utils
|
||||||
|
|
||||||
bool isAbsolute(const std::string& path)
|
bool isAbsolute(const std::string& path)
|
||||||
{
|
{
|
||||||
std::string genericPath = getGenericPath(path);
|
const std::string& genericPath {getGenericPath(path)};
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
return ((genericPath.size() > 1) && (genericPath[1] == ':'));
|
return ((genericPath.size() > 1) && (genericPath[1] == ':'));
|
||||||
|
@ -875,7 +881,7 @@ namespace Utils
|
||||||
|
|
||||||
bool isRegularFile(const std::string& path)
|
bool isRegularFile(const std::string& path)
|
||||||
{
|
{
|
||||||
std::string genericPath = getGenericPath(path);
|
const std::string& genericPath {getGenericPath(path)};
|
||||||
|
|
||||||
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||||
struct stat info;
|
struct stat info;
|
||||||
|
@ -897,7 +903,7 @@ namespace Utils
|
||||||
|
|
||||||
bool isDirectory(const std::string& path)
|
bool isDirectory(const std::string& path)
|
||||||
{
|
{
|
||||||
std::string genericPath = getGenericPath(path);
|
const std::string& genericPath {getGenericPath(path)};
|
||||||
|
|
||||||
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||||
struct stat info;
|
struct stat info;
|
||||||
|
@ -919,12 +925,12 @@ namespace Utils
|
||||||
|
|
||||||
bool isSymlink(const std::string& path)
|
bool isSymlink(const std::string& path)
|
||||||
{
|
{
|
||||||
std::string genericPath = getGenericPath(path);
|
const std::string& genericPath {getGenericPath(path)};
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
// Check for symlink attribute.
|
// Check for symlink attribute.
|
||||||
const DWORD Attributes =
|
const DWORD Attributes {
|
||||||
GetFileAttributesW(Utils::String::stringToWideString(genericPath).c_str());
|
GetFileAttributesW(Utils::String::stringToWideString(genericPath).c_str())};
|
||||||
if ((Attributes != INVALID_FILE_ATTRIBUTES) &&
|
if ((Attributes != INVALID_FILE_ATTRIBUTES) &&
|
||||||
(Attributes & FILE_ATTRIBUTE_REPARSE_POINT))
|
(Attributes & FILE_ATTRIBUTE_REPARSE_POINT))
|
||||||
return true;
|
return true;
|
||||||
|
@ -952,12 +958,12 @@ namespace Utils
|
||||||
|
|
||||||
bool isHidden(const std::string& path)
|
bool isHidden(const std::string& path)
|
||||||
{
|
{
|
||||||
std::string genericPath = getGenericPath(path);
|
const std::string& genericPath {getGenericPath(path)};
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
// Check for hidden attribute.
|
// Check for hidden attribute.
|
||||||
const DWORD Attributes =
|
const DWORD Attributes {
|
||||||
GetFileAttributesW(Utils::String::stringToWideString(genericPath).c_str());
|
GetFileAttributesW(Utils::String::stringToWideString(genericPath).c_str())};
|
||||||
if ((Attributes != INVALID_FILE_ATTRIBUTES) && (Attributes & FILE_ATTRIBUTE_HIDDEN))
|
if ((Attributes != INVALID_FILE_ATTRIBUTES) && (Attributes & FILE_ATTRIBUTE_HIDDEN))
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,363 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="256"
|
||||||
|
height="256"
|
||||||
|
viewBox="0 0 67.733333 67.733333"
|
||||||
|
version="1.1"
|
||||||
|
id="svg4925"
|
||||||
|
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||||
|
sodipodi:docname="joystick_arcade_twin.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<defs
|
||||||
|
id="defs4919" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="2.6601232"
|
||||||
|
inkscape:cx="-20.675734"
|
||||||
|
inkscape:cy="97.551873"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="3840"
|
||||||
|
inkscape:window-height="2065"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:pagecheckerboard="0" />
|
||||||
|
<metadata
|
||||||
|
id="metadata4922">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-229.26665)">
|
||||||
|
<rect
|
||||||
|
style="opacity:1;vector-effect:none;fill:#373737;fill-opacity:1;stroke:#000000;stroke-width:0.26458333;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
|
||||||
|
id="rect5985-6-0"
|
||||||
|
width="56.947788"
|
||||||
|
height="33.415451"
|
||||||
|
x="5.3927727"
|
||||||
|
y="246.4256"
|
||||||
|
ry="3.286572"
|
||||||
|
rx="3.1865375" />
|
||||||
|
<g
|
||||||
|
id="g4928"
|
||||||
|
transform="matrix(0.99990441,0,0,1.0164462,0.00195324,-4.327527)">
|
||||||
|
<ellipse
|
||||||
|
ry="10.483982"
|
||||||
|
rx="10.657423"
|
||||||
|
cx="20.499538"
|
||||||
|
cy="263.1333"
|
||||||
|
id="circle34-2-3-3-0-2"
|
||||||
|
style="opacity:1;fill:#7b7b7b;fill-opacity:1;stroke:#080701;stroke-width:0.24870832;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<ellipse
|
||||||
|
ry="6.1186433"
|
||||||
|
rx="6.2198658"
|
||||||
|
cx="20.499538"
|
||||||
|
cy="263.1333"
|
||||||
|
id="circle34-2-3-39-9"
|
||||||
|
style="opacity:1;fill:#424242;fill-opacity:1;stroke-width:0.14515407" />
|
||||||
|
<g
|
||||||
|
transform="matrix(0.83301628,0,0,0.81945968,4.5251315,59.03631)"
|
||||||
|
id="g5882-1-9">
|
||||||
|
<g
|
||||||
|
id="g5815-9-6">
|
||||||
|
<g
|
||||||
|
id="g5786-6-0">
|
||||||
|
<path
|
||||||
|
style="fill:#b7b7b9;fill-opacity:1;stroke-width:0.1078754"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 19.176687,237.11862 -1.677879,1.91267 h 0.81948 v 1.74035 h 1.7166 v -1.74022 h 0.81948 z"
|
||||||
|
id="path4-0-9-2"
|
||||||
|
sodipodi:nodetypes="cccccccc"
|
||||||
|
inkscape:transform-center-x="2.11451"
|
||||||
|
inkscape:transform-center-y="-0.31364665" />
|
||||||
|
<path
|
||||||
|
style="fill:#b7b7b9;fill-opacity:1;stroke-width:0.10787541"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 19.176489,261.00707 1.677879,-1.91267 h -0.81948 v -1.74035 h -1.7166 v 1.74022 h -0.819481 z"
|
||||||
|
id="path4-0-4-3-7"
|
||||||
|
sodipodi:nodetypes="cccccccc"
|
||||||
|
inkscape:transform-center-x="-2.11451"
|
||||||
|
inkscape:transform-center-y="0.31364335" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="rotate(90,19.176585,249.06284)"
|
||||||
|
id="g5786-9-3-6">
|
||||||
|
<path
|
||||||
|
style="fill:#b7b7b9;fill-opacity:1;stroke-width:0.1078754"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 19.176687,237.11862 -1.677879,1.91267 h 0.81948 v 1.74035 h 1.7166 v -1.74022 h 0.81948 z"
|
||||||
|
id="path4-0-1-8-1"
|
||||||
|
sodipodi:nodetypes="cccccccc"
|
||||||
|
inkscape:transform-center-x="2.11451"
|
||||||
|
inkscape:transform-center-y="-0.31364665" />
|
||||||
|
<path
|
||||||
|
style="fill:#b7b7b9;fill-opacity:1;stroke-width:0.10787541"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 19.176489,261.00707 1.677879,-1.91267 h -0.81948 v -1.74035 h -1.7166 v 1.74022 h -0.819481 z"
|
||||||
|
id="path4-0-4-9-0-3"
|
||||||
|
sodipodi:nodetypes="cccccccc"
|
||||||
|
inkscape:transform-center-x="-2.11451"
|
||||||
|
inkscape:transform-center-y="0.31364335" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g5815-6-5-2"
|
||||||
|
transform="rotate(45,19.238276,249.03729)">
|
||||||
|
<g
|
||||||
|
id="g5786-4-6-1">
|
||||||
|
<path
|
||||||
|
style="fill:#b7b7b9;fill-opacity:1;stroke-width:0.1078754"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 19.176687,237.11862 -1.677879,1.91267 h 0.81948 v 1.74035 h 1.7166 v -1.74022 h 0.81948 z"
|
||||||
|
id="path4-0-0-6-59"
|
||||||
|
sodipodi:nodetypes="cccccccc"
|
||||||
|
inkscape:transform-center-x="2.11451"
|
||||||
|
inkscape:transform-center-y="-0.31364665" />
|
||||||
|
<path
|
||||||
|
style="fill:#b7b7b9;fill-opacity:1;stroke-width:0.10787541"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 19.176489,261.00707 1.677879,-1.91267 h -0.81948 v -1.74035 h -1.7166 v 1.74022 h -0.819481 z"
|
||||||
|
id="path4-0-4-0-4-9"
|
||||||
|
sodipodi:nodetypes="cccccccc"
|
||||||
|
inkscape:transform-center-x="-2.11451"
|
||||||
|
inkscape:transform-center-y="0.31364335" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="rotate(90,19.176585,249.06284)"
|
||||||
|
id="g5786-9-4-0-1">
|
||||||
|
<path
|
||||||
|
style="fill:#b7b7b9;fill-opacity:1;stroke-width:0.1078754"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 19.176687,237.11862 -1.677879,1.91267 h 0.81948 v 1.74035 h 1.7166 v -1.74022 h 0.81948 z"
|
||||||
|
id="path4-0-1-6-0-4"
|
||||||
|
sodipodi:nodetypes="cccccccc"
|
||||||
|
inkscape:transform-center-x="2.11451"
|
||||||
|
inkscape:transform-center-y="-0.31364665" />
|
||||||
|
<path
|
||||||
|
style="fill:#b7b7b9;fill-opacity:1;stroke-width:0.10787541"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 19.176489,261.00707 1.677879,-1.91267 h -0.81948 v -1.74035 h -1.7166 v 1.74022 h -0.819481 z"
|
||||||
|
id="path4-0-4-9-2-4-9"
|
||||||
|
sodipodi:nodetypes="cccccccc"
|
||||||
|
inkscape:transform-center-x="-2.11451"
|
||||||
|
inkscape:transform-center-y="0.31364335" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle
|
||||||
|
id="ellipse8-1"
|
||||||
|
transform="rotate(-166.691)"
|
||||||
|
cy="-251.3472"
|
||||||
|
cx="-80.522942"
|
||||||
|
r="3.860163"
|
||||||
|
style="fill:#9e9e9b;stroke-width:0.13708451" />
|
||||||
|
<circle
|
||||||
|
transform="rotate(-90)"
|
||||||
|
id="circle10-0"
|
||||||
|
r="3.0756288"
|
||||||
|
cy="20.498316"
|
||||||
|
cx="-263.13339"
|
||||||
|
style="fill:#565656;stroke-width:0.13708451" />
|
||||||
|
<path
|
||||||
|
id="path12-7"
|
||||||
|
d="m 20.507225,259.29748 c -2.123437,0 -3.844811,1.72138 -3.844811,3.84482 0,2.12343 1.721374,3.84481 3.844811,3.84481 2.123441,0 3.84481,-1.72138 3.84481,-3.84481 0,-2.12344 -1.721369,-3.84482 -3.84481,-3.84482 z m -0.04741,7.48263 c -2.035291,0 -3.685245,-1.64995 -3.685245,-3.68525 0,-2.03529 1.649954,-3.68524 3.685245,-3.68524 2.035292,0 3.685242,1.64995 3.685242,3.68524 0,2.0353 -1.64995,3.68525 -3.685242,3.68525 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#c9c9c7;stroke-width:0.13708451" />
|
||||||
|
<path
|
||||||
|
id="path14-5"
|
||||||
|
d="m 20.507093,266.98697 c 2.123437,0 3.844806,-1.72124 3.844806,-3.84481 0,-2.12344 -1.721369,-3.84481 -3.844806,-3.84481 -2.123442,0 -3.844811,1.72137 -3.844811,3.84481 1.28e-4,2.12357 1.721506,3.84481 3.844811,3.84481 z m 0.04741,-7.48249 c 2.035292,0 3.685241,1.64995 3.685241,3.68525 0,2.03529 -1.649949,3.68524 -3.685241,3.68524 -2.035155,0 -3.685241,-1.64995 -3.685241,-3.68524 1.28e-4,-2.0353 1.650086,-3.68525 3.685241,-3.68525 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#9a9a9b;stroke-width:0.13708451" />
|
||||||
|
<ellipse
|
||||||
|
ry="5.0897107"
|
||||||
|
rx="5.1739116"
|
||||||
|
cx="20.06336"
|
||||||
|
cy="260.785"
|
||||||
|
id="circle34-2-3-1-4-8"
|
||||||
|
style="opacity:1;fill:#f4d400;fill-opacity:1;stroke:#080701;stroke-width:0.25320625;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<ellipse
|
||||||
|
inkscape:transform-center-y="-2.1261916"
|
||||||
|
inkscape:transform-center-x="10.630944"
|
||||||
|
transform="matrix(0.6842207,0.729275,-0.6842207,0.729275,0,0)"
|
||||||
|
ry="0.88702333"
|
||||||
|
rx="0.65102625"
|
||||||
|
cy="164.61426"
|
||||||
|
cx="190.09605"
|
||||||
|
id="path4910-2-7"
|
||||||
|
style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.01910845;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.84313725;paint-order:normal" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g4928-3"
|
||||||
|
transform="matrix(0.99990441,0,0,1.0164462,26.419465,-4.327527)">
|
||||||
|
<ellipse
|
||||||
|
ry="10.483982"
|
||||||
|
rx="10.657423"
|
||||||
|
cx="20.499538"
|
||||||
|
cy="263.1333"
|
||||||
|
id="circle34-2-3-3-0-2-6"
|
||||||
|
style="opacity:1;fill:#7b7b7b;fill-opacity:1;stroke:#080701;stroke-width:0.248708;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<ellipse
|
||||||
|
ry="6.1186433"
|
||||||
|
rx="6.2198658"
|
||||||
|
cx="20.499538"
|
||||||
|
cy="263.1333"
|
||||||
|
id="circle34-2-3-39-9-7"
|
||||||
|
style="opacity:1;fill:#424242;fill-opacity:1;stroke-width:0.145154" />
|
||||||
|
<g
|
||||||
|
transform="matrix(0.83301628,0,0,0.81945968,4.5251315,59.03631)"
|
||||||
|
id="g5882-1-9-5">
|
||||||
|
<g
|
||||||
|
id="g5815-9-6-3">
|
||||||
|
<g
|
||||||
|
id="g5786-6-0-5">
|
||||||
|
<path
|
||||||
|
style="fill:#b7b7b9;fill-opacity:1;stroke-width:0.107875"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 19.176687,237.11862 -1.677879,1.91267 h 0.81948 v 1.74035 h 1.7166 v -1.74022 h 0.81948 z"
|
||||||
|
id="path4-0-9-2-6"
|
||||||
|
sodipodi:nodetypes="cccccccc"
|
||||||
|
inkscape:transform-center-x="2.11451"
|
||||||
|
inkscape:transform-center-y="-0.31364665" />
|
||||||
|
<path
|
||||||
|
style="fill:#b7b7b9;fill-opacity:1;stroke-width:0.107875"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 19.176489,261.00707 1.677879,-1.91267 h -0.81948 v -1.74035 h -1.7166 v 1.74022 h -0.819481 z"
|
||||||
|
id="path4-0-4-3-7-2"
|
||||||
|
sodipodi:nodetypes="cccccccc"
|
||||||
|
inkscape:transform-center-x="-2.11451"
|
||||||
|
inkscape:transform-center-y="0.31364335" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="rotate(90,19.176585,249.06284)"
|
||||||
|
id="g5786-9-3-6-9">
|
||||||
|
<path
|
||||||
|
style="fill:#b7b7b9;fill-opacity:1;stroke-width:0.107875"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 19.176687,237.11862 -1.677879,1.91267 h 0.81948 v 1.74035 h 1.7166 v -1.74022 h 0.81948 z"
|
||||||
|
id="path4-0-1-8-1-1"
|
||||||
|
sodipodi:nodetypes="cccccccc"
|
||||||
|
inkscape:transform-center-x="2.11451"
|
||||||
|
inkscape:transform-center-y="-0.31364665" />
|
||||||
|
<path
|
||||||
|
style="fill:#b7b7b9;fill-opacity:1;stroke-width:0.107875"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 19.176489,261.00707 1.677879,-1.91267 h -0.81948 v -1.74035 h -1.7166 v 1.74022 h -0.819481 z"
|
||||||
|
id="path4-0-4-9-0-3-2"
|
||||||
|
sodipodi:nodetypes="cccccccc"
|
||||||
|
inkscape:transform-center-x="-2.11451"
|
||||||
|
inkscape:transform-center-y="0.31364335" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g5815-6-5-2-7"
|
||||||
|
transform="rotate(45,19.238276,249.03729)">
|
||||||
|
<g
|
||||||
|
id="g5786-4-6-1-0">
|
||||||
|
<path
|
||||||
|
style="fill:#b7b7b9;fill-opacity:1;stroke-width:0.107875"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 19.176687,237.11862 -1.677879,1.91267 h 0.81948 v 1.74035 h 1.7166 v -1.74022 h 0.81948 z"
|
||||||
|
id="path4-0-0-6-59-9"
|
||||||
|
sodipodi:nodetypes="cccccccc"
|
||||||
|
inkscape:transform-center-x="2.11451"
|
||||||
|
inkscape:transform-center-y="-0.31364665" />
|
||||||
|
<path
|
||||||
|
style="fill:#b7b7b9;fill-opacity:1;stroke-width:0.107875"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 19.176489,261.00707 1.677879,-1.91267 h -0.81948 v -1.74035 h -1.7166 v 1.74022 h -0.819481 z"
|
||||||
|
id="path4-0-4-0-4-9-3"
|
||||||
|
sodipodi:nodetypes="cccccccc"
|
||||||
|
inkscape:transform-center-x="-2.11451"
|
||||||
|
inkscape:transform-center-y="0.31364335" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="rotate(90,19.176585,249.06284)"
|
||||||
|
id="g5786-9-4-0-1-6">
|
||||||
|
<path
|
||||||
|
style="fill:#b7b7b9;fill-opacity:1;stroke-width:0.107875"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 19.176687,237.11862 -1.677879,1.91267 h 0.81948 v 1.74035 h 1.7166 v -1.74022 h 0.81948 z"
|
||||||
|
id="path4-0-1-6-0-4-0"
|
||||||
|
sodipodi:nodetypes="cccccccc"
|
||||||
|
inkscape:transform-center-x="2.11451"
|
||||||
|
inkscape:transform-center-y="-0.31364665" />
|
||||||
|
<path
|
||||||
|
style="fill:#b7b7b9;fill-opacity:1;stroke-width:0.107875"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 19.176489,261.00707 1.677879,-1.91267 h -0.81948 v -1.74035 h -1.7166 v 1.74022 h -0.819481 z"
|
||||||
|
id="path4-0-4-9-2-4-9-6"
|
||||||
|
sodipodi:nodetypes="cccccccc"
|
||||||
|
inkscape:transform-center-x="-2.11451"
|
||||||
|
inkscape:transform-center-y="0.31364335" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<circle
|
||||||
|
id="ellipse8-1-2"
|
||||||
|
transform="rotate(-166.691)"
|
||||||
|
cy="-251.3472"
|
||||||
|
cx="-80.522942"
|
||||||
|
r="3.860163"
|
||||||
|
style="fill:#9e9e9b;stroke-width:0.137085" />
|
||||||
|
<circle
|
||||||
|
transform="rotate(-90)"
|
||||||
|
id="circle10-0-6"
|
||||||
|
r="3.0756288"
|
||||||
|
cy="20.498316"
|
||||||
|
cx="-263.13339"
|
||||||
|
style="fill:#565656;stroke-width:0.137085" />
|
||||||
|
<path
|
||||||
|
id="path12-7-1"
|
||||||
|
d="m 20.507225,259.29748 c -2.123437,0 -3.844811,1.72138 -3.844811,3.84482 0,2.12343 1.721374,3.84481 3.844811,3.84481 2.123441,0 3.84481,-1.72138 3.84481,-3.84481 0,-2.12344 -1.721369,-3.84482 -3.84481,-3.84482 z m -0.04741,7.48263 c -2.035291,0 -3.685245,-1.64995 -3.685245,-3.68525 0,-2.03529 1.649954,-3.68524 3.685245,-3.68524 2.035292,0 3.685242,1.64995 3.685242,3.68524 0,2.0353 -1.64995,3.68525 -3.685242,3.68525 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#c9c9c7;stroke-width:0.137085" />
|
||||||
|
<path
|
||||||
|
id="path14-5-8"
|
||||||
|
d="m 20.507093,266.98697 c 2.123437,0 3.844806,-1.72124 3.844806,-3.84481 0,-2.12344 -1.721369,-3.84481 -3.844806,-3.84481 -2.123442,0 -3.844811,1.72137 -3.844811,3.84481 1.28e-4,2.12357 1.721506,3.84481 3.844811,3.84481 z m 0.04741,-7.48249 c 2.035292,0 3.685241,1.64995 3.685241,3.68525 0,2.03529 -1.649949,3.68524 -3.685241,3.68524 -2.035155,0 -3.685241,-1.64995 -3.685241,-3.68524 1.28e-4,-2.0353 1.650086,-3.68525 3.685241,-3.68525 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#9a9a9b;stroke-width:0.137085" />
|
||||||
|
<ellipse
|
||||||
|
ry="5.0897107"
|
||||||
|
rx="5.1739116"
|
||||||
|
cx="20.06336"
|
||||||
|
cy="260.785"
|
||||||
|
id="circle34-2-3-1-4-8-7"
|
||||||
|
style="opacity:1;fill:#f4d400;fill-opacity:1;stroke:#080701;stroke-width:0.253206;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<ellipse
|
||||||
|
inkscape:transform-center-y="-2.1261916"
|
||||||
|
inkscape:transform-center-x="10.630944"
|
||||||
|
transform="matrix(0.6842207,0.729275,-0.6842207,0.729275,0,0)"
|
||||||
|
ry="0.88702333"
|
||||||
|
rx="0.65102625"
|
||||||
|
cy="164.61426"
|
||||||
|
cx="190.09605"
|
||||||
|
id="path4910-2-7-9"
|
||||||
|
style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.0191085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.843137;paint-order:normal" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 16 KiB |
|
@ -1,5 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg width="64" height="64" version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path id="outline" d="M12,4 L52,4 A8,8 90 0,1 60,12 L60,52 A8,8 90 0,1 52,60 L12,60 A8,8 90 0,1 4,52 L4,12 A8,8 90 0,1 12,4Z" fill="none" stroke="#fff" stroke-linejoin="round" stroke-width="2"/>
|
|
||||||
<path id="button_hotkey" d="m12 4a8 8 0 0 0-8 8v40a8 8 0 0 0 8 8h40a8 8 0 0 0 8-8v-40a8 8 0 0 0-8-8h-40zm21.039062 12.302734c2.205868 0 3.894744 0.634451 5.066407 1.904297 1.171662 1.269847 1.757812 3.095918 1.757812 5.478516 0 2.376053-0.58937 4.204077-1.767578 5.480469s-2.867085 1.914062-5.066406 1.914062c-2.199322 0-3.888198-0.63767-5.066406-1.914062s-1.767579-3.110857-1.767579-5.5c0-2.389144 0.589371-4.211995 1.767579-5.46875 1.184753-1.263301 2.87685-1.894532 5.076171-1.894532zm-21.039062 0.226563h3.042969v5.625h5.685547v-5.625h3.033203v14.353515h-3.033203v-6.195312h-5.685547v6.195312h-3.042969v-14.353515zm29.160156 0h10.839844v2.533203h-3.898438v11.820312h-3.042968v-11.820312h-3.898438v-2.533203zm-8.111328 2.306641c-1.210936 0-2.122995 0.410315-2.738281 1.228515-0.615286 0.811654-0.923828 2.017422-0.923828 3.621094 0 3.22698 1.21416 4.841797 3.642578 4.841797 2.428417 0 3.642578-1.614817 3.642578-4.841797 0-3.233526-1.207721-4.84961-3.623047-4.849609zm-21.048828 14.505859h3.425781v6.568359l1.34961-1.6875 4.376953-4.880859h3.800781l-5.636719 6.353515 5.681641 8.001954h-3.890625l-4.232422-6.048828-1.449219 0.923828v5.125h-3.425781v-14.355469zm15.125 0h9.306641v2.494141h-5.878907v3.152343h5.458985v2.494141h-5.458985v3.701172h5.878907v2.513672h-9.306641v-14.355469zm10.75 0h3.712891l3.349609 5.912109 3.371094-5.912109h3.691406l-5.361328 8.767578v5.587891h-3.404297v-5.488282l-5.359375-8.867187z" fill="#fff"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.7 KiB |
17
resources/graphics/help/button_ltrt.svg
Normal file
17
resources/graphics/help/button_ltrt.svg
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="64"
|
||||||
|
height="64"
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 64 64"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
id="button_lt"
|
||||||
|
d="M 2,3 V 16 A 13,13 0 0 0 15,29 H 49 A 13,13 0 0 0 62,16 V 3 Z M 21.939453,7.4335941 H 26.04102 V 21.355469 h 4.28906 v 3.210937 h -8.390627 z m 9.667967,0 H 42.06055 V 10.65625 h -3.1875 V 24.566406 H 34.79492 V 10.65625 h -3.1875 z"
|
||||||
|
fill="#ffffff" />
|
||||||
|
<path
|
||||||
|
id="button_rt"
|
||||||
|
d="m 2.000002,35 v 13 a 13,13 0 0 0 13,13 h 33.999996 a 13,13 0 0 0 13,-13 V 35 Z m 19.119141,4.433594 h 4.546875 c 1.882812,0 3.273437,0.425781 4.171875,1.277344 0.898437,0.851562 1.347655,2.15625 1.347655,3.914062 0,2.039062 -0.699218,3.519531 -2.097655,4.441406 l 3.281245,7.5 h -4.324214 l -2.625,-6.46875 h -0.210938 v 6.46875 h -4.089843 z m 11.308595,0 h 10.45312 v 3.222656 h -3.1875 v 13.910156 h -4.07812 V 42.65625 h -3.1875 z m -7.218752,3.175781 v 4.324219 h 0.304688 c 0.539062,0 0.929687,-0.183594 1.171875,-0.550782 0.25,-0.367187 0.375,-0.921874 0.375,-1.664062 0,-0.75 -0.128906,-1.289062 -0.386719,-1.617188 -0.25,-0.328124 -0.644531,-0.492187 -1.183594,-0.492187 z"
|
||||||
|
fill="#ffffff" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -4,7 +4,7 @@
|
||||||
// core.glsl
|
// core.glsl
|
||||||
//
|
//
|
||||||
// Core shader functionality:
|
// Core shader functionality:
|
||||||
// Clipping, opacity, saturation, dimming and reflections falloff.
|
// Clipping, brightness, saturation, opacity, dimming and reflections falloff.
|
||||||
//
|
//
|
||||||
|
|
||||||
// Vertex section of code:
|
// Vertex section of code:
|
||||||
|
@ -39,8 +39,9 @@ in vec2 texCoord;
|
||||||
in vec4 color;
|
in vec4 color;
|
||||||
|
|
||||||
uniform vec4 clipRegion;
|
uniform vec4 clipRegion;
|
||||||
uniform float opacity;
|
uniform float brightness;
|
||||||
uniform float saturation;
|
uniform float saturation;
|
||||||
|
uniform float opacity;
|
||||||
uniform float dimming;
|
uniform float dimming;
|
||||||
uniform float reflectionsFalloff;
|
uniform float reflectionsFalloff;
|
||||||
uniform uint shaderFlags;
|
uniform uint shaderFlags;
|
||||||
|
@ -70,6 +71,25 @@ void main()
|
||||||
|
|
||||||
vec4 sampledColor = texture(textureSampler, texCoord);
|
vec4 sampledColor = texture(textureSampler, texCoord);
|
||||||
|
|
||||||
|
// Brightness.
|
||||||
|
if (brightness != 0.0) {
|
||||||
|
sampledColor.rgb /= sampledColor.a;
|
||||||
|
sampledColor.rgb += 0.3 * brightness;
|
||||||
|
sampledColor.rgb *= sampledColor.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Saturation.
|
||||||
|
if (saturation != 1.0) {
|
||||||
|
vec3 grayscale;
|
||||||
|
// Premultiplied textures are all in BGRA format.
|
||||||
|
if (0x0u != (shaderFlags & 0x01u))
|
||||||
|
grayscale = vec3(dot(sampledColor.bgr, vec3(0.114, 0.587, 0.299)));
|
||||||
|
else
|
||||||
|
grayscale = vec3(dot(sampledColor.rgb, vec3(0.299, 0.587, 0.114)));
|
||||||
|
vec3 blendedColor = mix(grayscale, sampledColor.rgb, saturation);
|
||||||
|
sampledColor = vec4(blendedColor, sampledColor.a);
|
||||||
|
}
|
||||||
|
|
||||||
// For fonts the alpha information is stored in the red channel.
|
// For fonts the alpha information is stored in the red channel.
|
||||||
if (0x0u != (shaderFlags & 0x2u))
|
if (0x0u != (shaderFlags & 0x2u))
|
||||||
sampledColor = vec4(1.0, 1.0, 1.0, sampledColor.r);
|
sampledColor = vec4(1.0, 1.0, 1.0, sampledColor.r);
|
||||||
|
@ -97,18 +117,6 @@ void main()
|
||||||
sampledColor *= opacity;
|
sampledColor *= opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saturation.
|
|
||||||
if (saturation != 1.0) {
|
|
||||||
vec3 grayscale;
|
|
||||||
// Premultiplied textures are all in BGRA format.
|
|
||||||
if (0x0u != (shaderFlags & 0x01u))
|
|
||||||
grayscale = vec3(dot(sampledColor.bgr, vec3(0.34, 0.55, 0.11)));
|
|
||||||
else
|
|
||||||
grayscale = vec3(dot(sampledColor.rgb, vec3(0.34, 0.55, 0.11)));
|
|
||||||
vec3 blendedColor = mix(grayscale, sampledColor.rgb, saturation);
|
|
||||||
sampledColor = vec4(blendedColor, sampledColor.a);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dimming.
|
// Dimming.
|
||||||
if (dimming != 1.0) {
|
if (dimming != 1.0) {
|
||||||
vec4 dimColor = vec4(dimming, dimming, dimming, 1.0);
|
vec4 dimColor = vec4(dimming, dimming, dimming, 1.0);
|
||||||
|
|
|
@ -30,10 +30,13 @@ precision mediump float;
|
||||||
uniform mat4 MVPMatrix;
|
uniform mat4 MVPMatrix;
|
||||||
in vec2 positionVertex;
|
in vec2 positionVertex;
|
||||||
in vec2 texCoordVertex;
|
in vec2 texCoordVertex;
|
||||||
|
in vec4 colorVertex;
|
||||||
uniform vec2 textureSize;
|
uniform vec2 textureSize;
|
||||||
|
|
||||||
out vec2 texCoord;
|
out vec2 texCoord;
|
||||||
out vec2 onex;
|
out vec2 onex;
|
||||||
out vec2 oney;
|
out vec2 oney;
|
||||||
|
out vec4 colorShift;
|
||||||
|
|
||||||
#define SourceSize vec4(textureSize, 1.0 / textureSize)
|
#define SourceSize vec4(textureSize, 1.0 / textureSize)
|
||||||
|
|
||||||
|
@ -43,6 +46,7 @@ void main()
|
||||||
texCoord = texCoordVertex;
|
texCoord = texCoordVertex;
|
||||||
onex = vec2(SourceSize.z, 0.0);
|
onex = vec2(SourceSize.z, 0.0);
|
||||||
oney = vec2(0.0, SourceSize.w);
|
oney = vec2(0.0, SourceSize.w);
|
||||||
|
colorShift.abgr = colorVertex.rgba;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fragment section of code:
|
// Fragment section of code:
|
||||||
|
@ -54,11 +58,13 @@ precision mediump float;
|
||||||
|
|
||||||
uniform vec2 textureSize;
|
uniform vec2 textureSize;
|
||||||
uniform float opacity;
|
uniform float opacity;
|
||||||
|
uniform float brightness;
|
||||||
uniform float saturation;
|
uniform float saturation;
|
||||||
uniform sampler2D textureSampler;
|
uniform sampler2D textureSampler;
|
||||||
in vec2 texCoord;
|
in vec2 texCoord;
|
||||||
in vec2 onex;
|
in vec2 onex;
|
||||||
in vec2 oney;
|
in vec2 oney;
|
||||||
|
in vec4 colorShift;
|
||||||
out vec4 FragColor;
|
out vec4 FragColor;
|
||||||
|
|
||||||
#define SourceSize vec4(textureSize, 1.0 / textureSize)
|
#define SourceSize vec4(textureSize, 1.0 / textureSize)
|
||||||
|
@ -101,13 +107,6 @@ void main()
|
||||||
float h_weight_00 = dx / SPOT_WIDTH;
|
float h_weight_00 = dx / SPOT_WIDTH;
|
||||||
WEIGHT(h_weight_00);
|
WEIGHT(h_weight_00);
|
||||||
|
|
||||||
// Saturation.
|
|
||||||
if (saturation != 1.0) {
|
|
||||||
vec3 grayscale = vec3(dot(color.rgb, vec3(0.34, 0.55, 0.11)));
|
|
||||||
vec3 blendedColor = mix(grayscale, color.rgb, saturation);
|
|
||||||
color = vec4(blendedColor, color.a);
|
|
||||||
}
|
|
||||||
|
|
||||||
color *= vec4(h_weight_00, h_weight_00, h_weight_00, h_weight_00);
|
color *= vec4(h_weight_00, h_weight_00, h_weight_00, h_weight_00);
|
||||||
|
|
||||||
// Get closest horizontal neighbour to blend.
|
// Get closest horizontal neighbour to blend.
|
||||||
|
@ -122,13 +121,6 @@ void main()
|
||||||
}
|
}
|
||||||
vec4 colorNB = TEX2D(texture_coords + coords01);
|
vec4 colorNB = TEX2D(texture_coords + coords01);
|
||||||
|
|
||||||
// Saturation.
|
|
||||||
if (saturation != 1.0) {
|
|
||||||
vec3 grayscale = vec3(dot(colorNB.rgb, vec3(0.34, 0.55, 0.11)));
|
|
||||||
vec3 blendedColor = mix(grayscale, colorNB.rgb, saturation);
|
|
||||||
colorNB = vec4(blendedColor, colorNB.a);
|
|
||||||
}
|
|
||||||
|
|
||||||
float h_weight_01 = dx / SPOT_WIDTH;
|
float h_weight_01 = dx / SPOT_WIDTH;
|
||||||
WEIGHT(h_weight_01);
|
WEIGHT(h_weight_01);
|
||||||
|
|
||||||
|
@ -152,13 +144,6 @@ void main()
|
||||||
}
|
}
|
||||||
colorNB = TEX2D(texture_coords + coords10);
|
colorNB = TEX2D(texture_coords + coords10);
|
||||||
|
|
||||||
// Saturation.
|
|
||||||
if (saturation != 1.0) {
|
|
||||||
vec3 grayscale = vec3(dot(colorNB.rgb, vec3(0.34, 0.55, 0.11)));
|
|
||||||
vec3 blendedColor = mix(grayscale, colorNB.rgb, saturation);
|
|
||||||
colorNB = vec4(blendedColor, colorNB.a);
|
|
||||||
}
|
|
||||||
|
|
||||||
float v_weight_10 = dy / SPOT_HEIGHT;
|
float v_weight_10 = dy / SPOT_HEIGHT;
|
||||||
WEIGHT(v_weight_10);
|
WEIGHT(v_weight_10);
|
||||||
|
|
||||||
|
@ -170,6 +155,26 @@ void main()
|
||||||
color *= vec4(COLOR_BOOST);
|
color *= vec4(COLOR_BOOST);
|
||||||
|
|
||||||
vec4 colorTemp = clamp(GAMMA_OUT(color), 0.0, 1.0);
|
vec4 colorTemp = clamp(GAMMA_OUT(color), 0.0, 1.0);
|
||||||
|
|
||||||
|
// Brightness.
|
||||||
|
if (brightness != 0.0) {
|
||||||
|
colorTemp.rgb /= colorTemp.a;
|
||||||
|
colorTemp.rgb += 0.3 * brightness;
|
||||||
|
colorTemp.rgb *= colorTemp.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Saturation.
|
||||||
|
if (saturation != 1.0) {
|
||||||
|
vec3 grayscale;
|
||||||
|
grayscale = vec3(dot(colorTemp.bgr, vec3(0.114, 0.587, 0.299)));
|
||||||
|
vec3 blendedColor = mix(grayscale, colorTemp.rgb, saturation);
|
||||||
|
colorTemp = vec4(blendedColor, colorTemp.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color shift.
|
||||||
|
colorTemp.rgb *= colorShift.rgb;
|
||||||
|
colorTemp.a *= colorShift.a;
|
||||||
|
|
||||||
FragColor = vec4(colorTemp.rgb, colorTemp.a * opacity);
|
FragColor = vec4(colorTemp.rgb, colorTemp.a * opacity);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -46,6 +46,12 @@
|
||||||
<entry>/Applications/BasiliskII.app/Contents/MacOS/BasiliskII</entry>
|
<entry>/Applications/BasiliskII.app/Contents/MacOS/BasiliskII</entry>
|
||||||
</rule>
|
</rule>
|
||||||
</emulator>
|
</emulator>
|
||||||
|
<emulator name="CEMU">
|
||||||
|
<!-- Nintendo Wii U emulator Cemu -->
|
||||||
|
<rule type="staticpath">
|
||||||
|
<entry>/Applications/Cemu.app/Contents/MacOS/Cemu</entry>
|
||||||
|
</rule>
|
||||||
|
</emulator>
|
||||||
<emulator name="CITRA">
|
<emulator name="CITRA">
|
||||||
<!-- Nintendo 3DS emulator Citra -->
|
<!-- Nintendo 3DS emulator Citra -->
|
||||||
<rule type="staticpath">
|
<rule type="staticpath">
|
||||||
|
@ -218,6 +224,12 @@
|
||||||
<entry>/Applications/Ruffle.app/Contents/MacOS/ruffle</entry>
|
<entry>/Applications/Ruffle.app/Contents/MacOS/ruffle</entry>
|
||||||
</rule>
|
</rule>
|
||||||
</emulator>
|
</emulator>
|
||||||
|
<emulator name="RYUJINX">
|
||||||
|
<!-- Nintendo Switch emulator Ryujinx -->
|
||||||
|
<rule type="staticpath">
|
||||||
|
<entry>/Applications/Ryujinx.app/Contents/MacOS/Ryujinx</entry>
|
||||||
|
</rule>
|
||||||
|
</emulator>
|
||||||
<emulator name="SAMEBOY">
|
<emulator name="SAMEBOY">
|
||||||
<!-- Nintendo Game Boy / Color emulator SameBoy -->
|
<!-- Nintendo Game Boy / Color emulator SameBoy -->
|
||||||
<rule type="staticpath">
|
<rule type="staticpath">
|
||||||
|
|
|
@ -1440,8 +1440,8 @@
|
||||||
<name>switch</name>
|
<name>switch</name>
|
||||||
<fullname>Nintendo Switch</fullname>
|
<fullname>Nintendo Switch</fullname>
|
||||||
<path>%ROMPATH%/switch</path>
|
<path>%ROMPATH%/switch</path>
|
||||||
<extension>.7z .7Z .zip .ZIP</extension>
|
<extension>.nca .NCA .nro .NRO .nso .NSO .nsp .NSP .xci .XCI .7z .7Z .zip .ZIP</extension>
|
||||||
<command>PLACEHOLDER %ROM%</command>
|
<command label="Ryujinx (Standalone)">%EMULATOR_RYUJINX% %ROM%</command>
|
||||||
<platform>switch</platform>
|
<platform>switch</platform>
|
||||||
<theme>switch</theme>
|
<theme>switch</theme>
|
||||||
</system>
|
</system>
|
||||||
|
@ -1584,8 +1584,8 @@
|
||||||
<name>wiiu</name>
|
<name>wiiu</name>
|
||||||
<fullname>Nintendo Wii U</fullname>
|
<fullname>Nintendo Wii U</fullname>
|
||||||
<path>%ROMPATH%/wiiu</path>
|
<path>%ROMPATH%/wiiu</path>
|
||||||
<extension>.7z .7Z .zip .ZIP</extension>
|
<extension>.rpx .RPX .wua .WUA .wud .WUD .wux .WUX</extension>
|
||||||
<command>PLACEHOLDER %ROM%</command>
|
<command label="Cemu (Standalone)">%EMULATOR_CEMU% -g %ROM%</command>
|
||||||
<platform>wiiu</platform>
|
<platform>wiiu</platform>
|
||||||
<theme>wiiu</theme>
|
<theme>wiiu</theme>
|
||||||
</system>
|
</system>
|
||||||
|
|
|
@ -326,6 +326,7 @@
|
||||||
<!-- Nintendo Game Boy Advance emulator mGBA -->
|
<!-- Nintendo Game Boy Advance emulator mGBA -->
|
||||||
<rule type="systempath">
|
<rule type="systempath">
|
||||||
<entry>mgba</entry>
|
<entry>mgba</entry>
|
||||||
|
<entry>mgba-qt</entry>
|
||||||
<entry>io.mgba.mGBA</entry>
|
<entry>io.mgba.mGBA</entry>
|
||||||
</rule>
|
</rule>
|
||||||
<rule type="staticpath">
|
<rule type="staticpath">
|
||||||
|
@ -591,6 +592,7 @@
|
||||||
<!-- Super Nintendo emulator Snes9x -->
|
<!-- Super Nintendo emulator Snes9x -->
|
||||||
<rule type="systempath">
|
<rule type="systempath">
|
||||||
<entry>snes9x</entry>
|
<entry>snes9x</entry>
|
||||||
|
<entry>snes9x-gtk</entry>
|
||||||
</rule>
|
</rule>
|
||||||
<rule type="staticpath">
|
<rule type="staticpath">
|
||||||
<entry>/var/lib/flatpak/exports/bin/com.snes9x.Snes9x</entry>
|
<entry>/var/lib/flatpak/exports/bin/com.snes9x.Snes9x</entry>
|
||||||
|
|
|
@ -115,6 +115,7 @@
|
||||||
<command label="Flycast (Standalone)">%EMULATOR_FLYCAST% %ROM%</command>
|
<command label="Flycast (Standalone)">%EMULATOR_FLYCAST% %ROM%</command>
|
||||||
<command label="Kronos">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/kronos_libretro.so %ROM%</command>
|
<command label="Kronos">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/kronos_libretro.so %ROM%</command>
|
||||||
<command label="Supermodel (Standalone)">%STARTDIR%=%GAMEDIR% %EMULATOR_SUPERMODEL% -log-output=%GAMEDIR%/Config/Supermodel.log -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
<command label="Supermodel (Standalone)">%STARTDIR%=%GAMEDIR% %EMULATOR_SUPERMODEL% -log-output=%GAMEDIR%/Config/Supermodel.log -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
||||||
|
<command label="Supermodel [Fullscreen] (Standalone)">%STARTDIR%=%GAMEDIR% %EMULATOR_SUPERMODEL% -log-output=%GAMEDIR%/Config/Supermodel.log -fullscreen -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
||||||
<platform>arcade</platform>
|
<platform>arcade</platform>
|
||||||
<theme>arcade</theme>
|
<theme>arcade</theme>
|
||||||
</system>
|
</system>
|
||||||
|
@ -682,6 +683,7 @@
|
||||||
<command label="Flycast (Standalone)">%EMULATOR_FLYCAST% %ROM%</command>
|
<command label="Flycast (Standalone)">%EMULATOR_FLYCAST% %ROM%</command>
|
||||||
<command label="Kronos">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/kronos_libretro.so %ROM%</command>
|
<command label="Kronos">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/kronos_libretro.so %ROM%</command>
|
||||||
<command label="Supermodel (Standalone)">%STARTDIR%=%GAMEDIR% %EMULATOR_SUPERMODEL% -log-output=%GAMEDIR%/Config/Supermodel.log -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
<command label="Supermodel (Standalone)">%STARTDIR%=%GAMEDIR% %EMULATOR_SUPERMODEL% -log-output=%GAMEDIR%/Config/Supermodel.log -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
||||||
|
<command label="Supermodel [Fullscreen] (Standalone)">%STARTDIR%=%GAMEDIR% %EMULATOR_SUPERMODEL% -log-output=%GAMEDIR%/Config/Supermodel.log -fullscreen -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
||||||
<platform>arcade</platform>
|
<platform>arcade</platform>
|
||||||
<theme>mame</theme>
|
<theme>mame</theme>
|
||||||
</system>
|
</system>
|
||||||
|
@ -791,6 +793,7 @@
|
||||||
<path>%ROMPATH%/model3</path>
|
<path>%ROMPATH%/model3</path>
|
||||||
<extension>.7z .7Z .zip .ZIP</extension>
|
<extension>.7z .7Z .zip .ZIP</extension>
|
||||||
<command label="Supermodel (Standalone)">%STARTDIR%=%GAMEDIR% %EMULATOR_SUPERMODEL% -log-output=%GAMEDIR%/Config/Supermodel.log -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
<command label="Supermodel (Standalone)">%STARTDIR%=%GAMEDIR% %EMULATOR_SUPERMODEL% -log-output=%GAMEDIR%/Config/Supermodel.log -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
||||||
|
<command label="Supermodel [Fullscreen] (Standalone)">%STARTDIR%=%GAMEDIR% %EMULATOR_SUPERMODEL% -log-output=%GAMEDIR%/Config/Supermodel.log -fullscreen -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
||||||
<platform>arcade</platform>
|
<platform>arcade</platform>
|
||||||
<theme>model3</theme>
|
<theme>model3</theme>
|
||||||
</system>
|
</system>
|
||||||
|
@ -1684,7 +1687,7 @@
|
||||||
<name>xbox360</name>
|
<name>xbox360</name>
|
||||||
<fullname>Microsoft Xbox 360</fullname>
|
<fullname>Microsoft Xbox 360</fullname>
|
||||||
<path>%ROMPATH%/xbox360</path>
|
<path>%ROMPATH%/xbox360</path>
|
||||||
<extension>.iso .ISO .xex .XEX</extension>
|
<extension>. .iso .ISO .xex .XEX</extension>
|
||||||
<command>PLACEHOLDER %ROM%</command>
|
<command>PLACEHOLDER %ROM%</command>
|
||||||
<platform>xbox360</platform>
|
<platform>xbox360</platform>
|
||||||
<theme>xbox360</theme>
|
<theme>xbox360</theme>
|
||||||
|
|
|
@ -502,6 +502,18 @@
|
||||||
<entry>%ESPATH%\..\redream\redream.exe</entry>
|
<entry>%ESPATH%\..\redream\redream.exe</entry>
|
||||||
</rule>
|
</rule>
|
||||||
</emulator>
|
</emulator>
|
||||||
|
<emulator name="ROSALIES-MUPEN-GUI">
|
||||||
|
<!-- Nintendo 64 emulator Rosalie's Mupen GUI -->
|
||||||
|
<rule type="systempath">
|
||||||
|
<entry>RMG.exe</entry>
|
||||||
|
</rule>
|
||||||
|
<rule type="staticpath">
|
||||||
|
<entry>~\AppData\Local\Programs\Rosalie's Mupen GUI\RMG.exe</entry>
|
||||||
|
<entry>%ESPATH%\Emulators\RMG\RMG.exe</entry>
|
||||||
|
<entry>%ESPATH%\RMG\RMG.exe</entry>
|
||||||
|
<entry>%ESPATH%\..\RMG\RMG.exe</entry>
|
||||||
|
</rule>
|
||||||
|
</emulator>
|
||||||
<emulator name="RPCS3">
|
<emulator name="RPCS3">
|
||||||
<!-- Sony PlayStation 3 emulator RPCS3 -->
|
<!-- Sony PlayStation 3 emulator RPCS3 -->
|
||||||
<rule type="systempath">
|
<rule type="systempath">
|
||||||
|
|
|
@ -339,6 +339,14 @@
|
||||||
<entry>%ESPATH%\..\redream\redream.exe</entry>
|
<entry>%ESPATH%\..\redream\redream.exe</entry>
|
||||||
</rule>
|
</rule>
|
||||||
</emulator>
|
</emulator>
|
||||||
|
<emulator name="ROSALIES-MUPEN-GUI">
|
||||||
|
<!-- Nintendo 64 emulator Rosalie's Mupen GUI -->
|
||||||
|
<rule type="staticpath">
|
||||||
|
<entry>%ESPATH%\Emulators\RMG\RMG.exe</entry>
|
||||||
|
<entry>%ESPATH%\RMG\RMG.exe</entry>
|
||||||
|
<entry>%ESPATH%\..\RMG\RMG.exe</entry>
|
||||||
|
</rule>
|
||||||
|
</emulator>
|
||||||
<emulator name="RPCS3">
|
<emulator name="RPCS3">
|
||||||
<!-- Sony PlayStation 3 emulator RPCS3 -->
|
<!-- Sony PlayStation 3 emulator RPCS3 -->
|
||||||
<rule type="staticpath">
|
<rule type="staticpath">
|
||||||
|
|
|
@ -116,7 +116,8 @@
|
||||||
<command label="Kronos">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\kronos_libretro.dll %ROM%</command>
|
<command label="Kronos">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\kronos_libretro.dll %ROM%</command>
|
||||||
<command label="Model 2 Emulator (Standalone)">%RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_M2EMULATOR% %BASENAME%</command>
|
<command label="Model 2 Emulator (Standalone)">%RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_M2EMULATOR% %BASENAME%</command>
|
||||||
<command label="Model 2 Emulator [Suspend ES-DE] (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_M2EMULATOR% %BASENAME%</command>
|
<command label="Model 2 Emulator [Suspend ES-DE] (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_M2EMULATOR% %BASENAME%</command>
|
||||||
<command label="Supermodel (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_SUPERMODEL% -fullscreen -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
<command label="Supermodel (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_SUPERMODEL% -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
||||||
|
<command label="Supermodel [Fullscreen] (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_SUPERMODEL% -fullscreen -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
||||||
<platform>arcade</platform>
|
<platform>arcade</platform>
|
||||||
<theme>arcade</theme>
|
<theme>arcade</theme>
|
||||||
</system>
|
</system>
|
||||||
|
@ -682,7 +683,8 @@
|
||||||
<command label="Kronos">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\kronos_libretro.dll %ROM%</command>
|
<command label="Kronos">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\kronos_libretro.dll %ROM%</command>
|
||||||
<command label="Model 2 Emulator (Standalone)">%RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_M2EMULATOR% %BASENAME%</command>
|
<command label="Model 2 Emulator (Standalone)">%RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_M2EMULATOR% %BASENAME%</command>
|
||||||
<command label="Model 2 Emulator [Suspend ES-DE] (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_M2EMULATOR% %BASENAME%</command>
|
<command label="Model 2 Emulator [Suspend ES-DE] (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_M2EMULATOR% %BASENAME%</command>
|
||||||
<command label="Supermodel (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_SUPERMODEL% -fullscreen -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
<command label="Supermodel (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_SUPERMODEL% -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
||||||
|
<command label="Supermodel [Fullscreen] (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_SUPERMODEL% -fullscreen -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
||||||
<platform>arcade</platform>
|
<platform>arcade</platform>
|
||||||
<theme>mame</theme>
|
<theme>mame</theme>
|
||||||
</system>
|
</system>
|
||||||
|
@ -792,7 +794,8 @@
|
||||||
<fullname>Sega Model 3</fullname>
|
<fullname>Sega Model 3</fullname>
|
||||||
<path>%ROMPATH%\model3</path>
|
<path>%ROMPATH%\model3</path>
|
||||||
<extension>.7z .7Z .zip .ZIP</extension>
|
<extension>.7z .7Z .zip .ZIP</extension>
|
||||||
<command label="Supermodel (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_SUPERMODEL% -fullscreen -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
<command label="Supermodel (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_SUPERMODEL% -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
||||||
|
<command label="Supermodel [Fullscreen] (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_SUPERMODEL% -fullscreen -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
|
||||||
<platform>arcade</platform>
|
<platform>arcade</platform>
|
||||||
<theme>model3</theme>
|
<theme>model3</theme>
|
||||||
</system>
|
</system>
|
||||||
|
@ -902,6 +905,7 @@
|
||||||
<command label="Mupen64Plus (Standalone)">%EMULATOR_MUPEN64PLUS% --fullscreen %ROM%</command>
|
<command label="Mupen64Plus (Standalone)">%EMULATOR_MUPEN64PLUS% --fullscreen %ROM%</command>
|
||||||
<command label="ParaLLEl N64">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\parallel_n64_libretro.dll %ROM%</command>
|
<command label="ParaLLEl N64">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\parallel_n64_libretro.dll %ROM%</command>
|
||||||
<command label="simple64 (Standalone)">%EMULATOR_SIMPLE64% --nogui %ROM%</command>
|
<command label="simple64 (Standalone)">%EMULATOR_SIMPLE64% --nogui %ROM%</command>
|
||||||
|
<command label="Rosalie's Mupen GUI (Standalone)">%EMULATOR_ROSALIES-MUPEN-GUI% %ROM%</command>
|
||||||
<command label="Project64 (Standalone)">%EMULATOR_PROJECT64% %ROM%</command>
|
<command label="Project64 (Standalone)">%EMULATOR_PROJECT64% %ROM%</command>
|
||||||
<command label="ares (Standalone)">%EMULATOR_ARES% --fullscreen --system "Nintendo 64" %ROM%</command>
|
<command label="ares (Standalone)">%EMULATOR_ARES% --fullscreen --system "Nintendo 64" %ROM%</command>
|
||||||
<platform>n64</platform>
|
<platform>n64</platform>
|
||||||
|
@ -1686,7 +1690,7 @@
|
||||||
<name>xbox360</name>
|
<name>xbox360</name>
|
||||||
<fullname>Microsoft Xbox 360</fullname>
|
<fullname>Microsoft Xbox 360</fullname>
|
||||||
<path>%ROMPATH%\xbox360</path>
|
<path>%ROMPATH%\xbox360</path>
|
||||||
<extension>.iso .ISO .xex .XEX</extension>
|
<extension>. .iso .ISO .xex .XEX</extension>
|
||||||
<command label="xenia (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_XENIA% %ROM%</command>
|
<command label="xenia (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_XENIA% %ROM%</command>
|
||||||
<platform>xbox360</platform>
|
<platform>xbox360</platform>
|
||||||
<theme>xbox360</theme>
|
<theme>xbox360</theme>
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
<pos>0 0.3</pos>
|
<pos>0 0.3</pos>
|
||||||
<size>1 0.4</size>
|
<size>1 0.4</size>
|
||||||
<type>horizontal</type>
|
<type>horizontal</type>
|
||||||
<staticItem>./art/${system.theme}.jpg</staticItem>
|
<staticImage>./art/${system.theme}.jpg</staticImage>
|
||||||
<itemScale>1</itemScale>
|
<itemScale>1</itemScale>
|
||||||
<itemVerticalAlignment>center</itemVerticalAlignment>
|
<itemVerticalAlignment>center</itemVerticalAlignment>
|
||||||
<unfocusedItemOpacity>1</unfocusedItemOpacity>
|
<unfocusedItemOpacity>1</unfocusedItemOpacity>
|
||||||
|
|
|
@ -47,6 +47,11 @@
|
||||||
<selectable>true</selectable>
|
<selectable>true</selectable>
|
||||||
</variant>
|
</variant>
|
||||||
|
|
||||||
|
<variant name="themeEngineTest_4">
|
||||||
|
<label>Theme engine test 4</label>
|
||||||
|
<selectable>true</selectable>
|
||||||
|
</variant>
|
||||||
|
|
||||||
<variant name="noMedia">
|
<variant name="noMedia">
|
||||||
<label>No game media</label>
|
<label>No game media</label>
|
||||||
<selectable>false</selectable>
|
<selectable>false</selectable>
|
||||||
|
|
26
themes/slate-DE/core/images/grid_frame.svg
Normal file
26
themes/slate-DE/core/images/grid_frame.svg
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="400"
|
||||||
|
height="400"
|
||||||
|
viewBox="0 0 105.83333 105.83333"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<g
|
||||||
|
id="layer1">
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke-width:0.0467413;stroke-linejoin:round;stroke-miterlimit:2;-inkscape-stroke:none;stop-color:#000000"
|
||||||
|
id="rect846"
|
||||||
|
width="105.83334"
|
||||||
|
height="105.83334"
|
||||||
|
x="-5.0862632e-06"
|
||||||
|
y="-5.0862632e-06"
|
||||||
|
rx="7.7669764"
|
||||||
|
ry="7.7669764" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 709 B |
|
@ -27,11 +27,11 @@
|
||||||
<pos>0 0.383796</pos>
|
<pos>0 0.383796</pos>
|
||||||
<size>1 0.232407</size>
|
<size>1 0.232407</size>
|
||||||
<type>horizontal</type>
|
<type>horizontal</type>
|
||||||
<staticItem>./${system.theme}/images/logo.svg</staticItem>
|
<staticImage>./${system.theme}/images/logo.svg</staticImage>
|
||||||
<maxItemCount>3</maxItemCount>
|
<maxItemCount>3</maxItemCount>
|
||||||
<itemSize>0.25 0.125</itemSize>
|
<itemSize>0.25 0.125</itemSize>
|
||||||
<itemScale>1.23</itemScale>
|
<itemScale>1.23</itemScale>
|
||||||
<itemInterpolation>linear</itemInterpolation>
|
<imageInterpolation>linear</imageInterpolation>
|
||||||
<unfocusedItemOpacity>0.5</unfocusedItemOpacity>
|
<unfocusedItemOpacity>0.5</unfocusedItemOpacity>
|
||||||
<color>${systemCarouselColor}</color>
|
<color>${systemCarouselColor}</color>
|
||||||
<text>${system.fullName}</text>
|
<text>${system.fullName}</text>
|
||||||
|
@ -431,4 +431,8 @@
|
||||||
<variant name="themeEngineTest_3">
|
<variant name="themeEngineTest_3">
|
||||||
<include>./theme_engine_test_3.xml</include>
|
<include>./theme_engine_test_3.xml</include>
|
||||||
</variant>
|
</variant>
|
||||||
|
|
||||||
|
<variant name="themeEngineTest_4">
|
||||||
|
<include>./theme_engine_test_4.xml</include>
|
||||||
|
</variant>
|
||||||
</theme>
|
</theme>
|
|
@ -6,7 +6,7 @@
|
||||||
<pos>0 0</pos>
|
<pos>0 0</pos>
|
||||||
<size>0.28 1.0</size>
|
<size>0.28 1.0</size>
|
||||||
<type>vertical_wheel</type>
|
<type>vertical_wheel</type>
|
||||||
<staticItem>./${system.theme}/images/logo.svg</staticItem>
|
<staticImage>./${system.theme}/images/logo.svg</staticImage>
|
||||||
<itemsBeforeCenter>4</itemsBeforeCenter>
|
<itemsBeforeCenter>4</itemsBeforeCenter>
|
||||||
<itemsAfterCenter>4</itemsAfterCenter>
|
<itemsAfterCenter>4</itemsAfterCenter>
|
||||||
<itemSize>0.15 0.125</itemSize>
|
<itemSize>0.15 0.125</itemSize>
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
</carousel>
|
</carousel>
|
||||||
<gameselector name="selector_recent">
|
<gameselector name="selector_recent">
|
||||||
<selection>lastplayed</selection>
|
<selection>lastplayed</selection>
|
||||||
<gameCount>3</gameCount>
|
<gameCount>1</gameCount>
|
||||||
</gameselector>
|
</gameselector>
|
||||||
<gameselector name="selector_random">
|
<gameselector name="selector_random">
|
||||||
<selection>random</selection>
|
<selection>random</selection>
|
||||||
|
@ -40,7 +40,6 @@
|
||||||
<imageType>fanart, titlescreen</imageType>
|
<imageType>fanart, titlescreen</imageType>
|
||||||
<gameselector>selector_random</gameselector>
|
<gameselector>selector_random</gameselector>
|
||||||
<interpolation>nearest</interpolation>
|
<interpolation>nearest</interpolation>
|
||||||
<scrollFadeIn>true</scrollFadeIn>
|
|
||||||
<opacity>0.8</opacity>
|
<opacity>0.8</opacity>
|
||||||
<saturation>0.8</saturation>
|
<saturation>0.8</saturation>
|
||||||
<zIndex>3</zIndex>
|
<zIndex>3</zIndex>
|
||||||
|
@ -52,7 +51,6 @@
|
||||||
<imageType>cover</imageType>
|
<imageType>cover</imageType>
|
||||||
<gameselector>selector_recent</gameselector>
|
<gameselector>selector_recent</gameselector>
|
||||||
<interpolation>nearest</interpolation>
|
<interpolation>nearest</interpolation>
|
||||||
<scrollFadeIn>true</scrollFadeIn>
|
|
||||||
<zIndex>3</zIndex>
|
<zIndex>3</zIndex>
|
||||||
</image>
|
</image>
|
||||||
<video name="random_game_video">
|
<video name="random_game_video">
|
||||||
|
@ -118,7 +116,7 @@
|
||||||
<letterCase>none</letterCase>
|
<letterCase>none</letterCase>
|
||||||
<zIndex>50</zIndex>
|
<zIndex>50</zIndex>
|
||||||
</text>
|
</text>
|
||||||
<datetime name="game_release_date">
|
<datetime name="game_lastplayed_date">
|
||||||
<pos>0.775 0.64</pos>
|
<pos>0.775 0.64</pos>
|
||||||
<size>0.2 0.05</size>
|
<size>0.2 0.05</size>
|
||||||
<metadata>lastplayed</metadata>
|
<metadata>lastplayed</metadata>
|
||||||
|
|
|
@ -110,11 +110,11 @@
|
||||||
<pos>0.025 0.201</pos>
|
<pos>0.025 0.201</pos>
|
||||||
<size>0.39 0.711</size>
|
<size>0.39 0.711</size>
|
||||||
<type>vertical</type>
|
<type>vertical</type>
|
||||||
<itemType>marquee</itemType>
|
<imageType>marquee</imageType>
|
||||||
<maxItemCount>5</maxItemCount>
|
<maxItemCount>5</maxItemCount>
|
||||||
<itemSize>0.26 0.105</itemSize>
|
<itemSize>0.26 0.105</itemSize>
|
||||||
<itemScale>1.23</itemScale>
|
<itemScale>1.23</itemScale>
|
||||||
<itemInterpolation>linear</itemInterpolation>
|
<imageInterpolation>linear</imageInterpolation>
|
||||||
<itemHorizontalAlignment>center</itemHorizontalAlignment>
|
<itemHorizontalAlignment>center</itemHorizontalAlignment>
|
||||||
<horizontalOffset>0</horizontalOffset>
|
<horizontalOffset>0</horizontalOffset>
|
||||||
<verticalOffset>0</verticalOffset>
|
<verticalOffset>0</verticalOffset>
|
||||||
|
|
|
@ -110,11 +110,11 @@
|
||||||
<pos>0.025 0.231</pos>
|
<pos>0.025 0.231</pos>
|
||||||
<size>0.95 0.692</size>
|
<size>0.95 0.692</size>
|
||||||
<type>horizontal</type>
|
<type>horizontal</type>
|
||||||
<itemType>cover</itemType>
|
<imageType>cover</imageType>
|
||||||
<maxItemCount>5</maxItemCount>
|
<maxItemCount>5</maxItemCount>
|
||||||
<itemSize>0.105 0.235</itemSize>
|
<itemSize>0.105 0.235</itemSize>
|
||||||
<itemScale>1.53</itemScale>
|
<itemScale>1.53</itemScale>
|
||||||
<itemInterpolation>linear</itemInterpolation>
|
<imageInterpolation>linear</imageInterpolation>
|
||||||
<itemVerticalAlignment>center</itemVerticalAlignment>
|
<itemVerticalAlignment>center</itemVerticalAlignment>
|
||||||
<verticalOffset>0.1</verticalOffset>
|
<verticalOffset>0.1</verticalOffset>
|
||||||
<reflections>true</reflections>
|
<reflections>true</reflections>
|
||||||
|
|
188
themes/slate-DE/theme_engine_test_4.xml
Normal file
188
themes/slate-DE/theme_engine_test_4.xml
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
<theme>
|
||||||
|
<!-- Test of grid element for both the system view and gamelist view -->
|
||||||
|
|
||||||
|
<view name="system">
|
||||||
|
<grid name="system_grid">
|
||||||
|
<pos>0.5 0.2</pos>
|
||||||
|
<size>0.86 0.715</size>
|
||||||
|
<origin>0.5 0</origin>
|
||||||
|
<staticImage>./${system.theme}/images/logo.svg</staticImage>
|
||||||
|
<itemSize>0.151 -1</itemSize>
|
||||||
|
<itemScale>1.1</itemScale>
|
||||||
|
<itemSpacing>0.022 -1</itemSpacing>
|
||||||
|
<fractionalRows>true</fractionalRows>
|
||||||
|
<itemTransitions>animate</itemTransitions>
|
||||||
|
<rowTransitions>animate</rowTransitions>
|
||||||
|
<unfocusedItemOpacity>1.0</unfocusedItemOpacity>
|
||||||
|
<imageRelativeScale>0.9</imageRelativeScale>
|
||||||
|
<imageFit>contain</imageFit>
|
||||||
|
<backgroundImage>./core/images/grid_frame.svg</backgroundImage>
|
||||||
|
<backgroundRelativeScale>1.0</backgroundRelativeScale>
|
||||||
|
<backgroundColor>424242</backgroundColor>
|
||||||
|
<selectorImage>./core/images/grid_frame.svg</selectorImage>
|
||||||
|
<selectorRelativeScale>1.0</selectorRelativeScale>
|
||||||
|
<selectorLayer>middle</selectorLayer>
|
||||||
|
<selectorColor>FF3333</selectorColor>
|
||||||
|
<textRelativeScale>0.8</textRelativeScale>
|
||||||
|
<textColor>F0F0F0</textColor>
|
||||||
|
<fontPath>./core/fonts/Exo2-RegularCondensed.otf</fontPath>
|
||||||
|
<fontSize>0.032</fontSize>
|
||||||
|
<letterCase>uppercase</letterCase>
|
||||||
|
<lineSpacing>1.2</lineSpacing>
|
||||||
|
<fadeAbovePrimary>true</fadeAbovePrimary>
|
||||||
|
</grid>
|
||||||
|
<image name="consolegame">
|
||||||
|
<pos>0.02 0.17</pos>
|
||||||
|
<maxSize>0.4 0.15</maxSize>
|
||||||
|
<origin>0 1</origin>
|
||||||
|
</image>
|
||||||
|
<image name="controller">
|
||||||
|
<pos>0.78 0.17</pos>
|
||||||
|
<maxSize>0.1 0.15</maxSize>
|
||||||
|
<origin>1 1</origin>
|
||||||
|
</image>
|
||||||
|
<image name="backframe2">
|
||||||
|
<pos>0.5 0.19</pos>
|
||||||
|
<size>0.96 0.735</size>
|
||||||
|
<origin>0.5 0</origin>
|
||||||
|
<path>./core/images/frame.png</path>
|
||||||
|
<color>181818</color>
|
||||||
|
<zIndex>10</zIndex>
|
||||||
|
</image>
|
||||||
|
<image name="logo">
|
||||||
|
<pos>0.02 0.17</pos>
|
||||||
|
<maxSize>0.32 0.13</maxSize>
|
||||||
|
<origin>0 1</origin>
|
||||||
|
</image>
|
||||||
|
<text name="game_counter">
|
||||||
|
<pos>0.8 0.145</pos>
|
||||||
|
<size>0.117 0.056</size>
|
||||||
|
<origin>0 0.5</origin>
|
||||||
|
<systemdata>gamecount_games</systemdata>
|
||||||
|
<fontPath>./core/fonts/Exo2-RegularCondensed.otf</fontPath>
|
||||||
|
<fontSize>0.035</fontSize>
|
||||||
|
<horizontalAlignment>center</horizontalAlignment>
|
||||||
|
<color>DDDDDD</color>
|
||||||
|
<backgroundColor>262626DD</backgroundColor>
|
||||||
|
<letterCase>uppercase</letterCase>
|
||||||
|
<zIndex>50</zIndex>
|
||||||
|
</text>
|
||||||
|
<helpsystem name="help">
|
||||||
|
<pos>0.012 0.955</pos>
|
||||||
|
<textColor>${gamelistHelpColor}</textColor>
|
||||||
|
<iconColor>${gamelistHelpColor}</iconColor>
|
||||||
|
<textColorDimmed>${gamelistHelpColorDimmed}</textColorDimmed>
|
||||||
|
<iconColorDimmed>${gamelistHelpColorDimmed}</iconColorDimmed>
|
||||||
|
</helpsystem>
|
||||||
|
<!-- Hide some unwanted elements -->
|
||||||
|
<text name="info1, info2, info3, info4, info5, info6, info7, info8, info9, info10">
|
||||||
|
<visible>false</visible>
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view name="gamelist">
|
||||||
|
<grid name="gamelist_grid">
|
||||||
|
<pos>0.5 0.3</pos>
|
||||||
|
<size>0.86 0.623</size>
|
||||||
|
<origin>0.5 0</origin>
|
||||||
|
<imageType>cover</imageType>
|
||||||
|
<itemSize>0.151 -1</itemSize>
|
||||||
|
<itemScale>1.1</itemScale>
|
||||||
|
<itemSpacing>0.022 -1</itemSpacing>
|
||||||
|
<fractionalRows>false</fractionalRows>
|
||||||
|
<itemTransitions>animate</itemTransitions>
|
||||||
|
<rowTransitions>animate</rowTransitions>
|
||||||
|
<unfocusedItemOpacity>1.0</unfocusedItemOpacity>
|
||||||
|
<imageRelativeScale>0.9</imageRelativeScale>
|
||||||
|
<imageFit>contain</imageFit>
|
||||||
|
<backgroundImage>./core/images/grid_frame.svg</backgroundImage>
|
||||||
|
<backgroundRelativeScale>1.0</backgroundRelativeScale>
|
||||||
|
<backgroundColor>424242</backgroundColor>
|
||||||
|
<selectorImage>./core/images/grid_frame.svg</selectorImage>
|
||||||
|
<selectorRelativeScale>1.0</selectorRelativeScale>
|
||||||
|
<selectorLayer>middle</selectorLayer>
|
||||||
|
<selectorColor>FF3333</selectorColor>
|
||||||
|
<textRelativeScale>0.8</textRelativeScale>
|
||||||
|
<textColor>F0F0F0</textColor>
|
||||||
|
<fontPath>./core/fonts/Exo2-RegularCondensed.otf</fontPath>
|
||||||
|
<fontSize>0.032</fontSize>
|
||||||
|
<letterCase>uppercase</letterCase>
|
||||||
|
<lineSpacing>1.2</lineSpacing>
|
||||||
|
</grid>
|
||||||
|
<image name="backframe2">
|
||||||
|
<pos>0.5 0.19</pos>
|
||||||
|
<size>0.96 0.735</size>
|
||||||
|
<origin>0.5 0</origin>
|
||||||
|
<path>./core/images/frame.png</path>
|
||||||
|
<color>181818</color>
|
||||||
|
<zIndex>10</zIndex>
|
||||||
|
</image>
|
||||||
|
<text name="game_name">
|
||||||
|
<pos>0.92 0.225</pos>
|
||||||
|
<size>0.55 0.08</size>
|
||||||
|
<origin>1 0</origin>
|
||||||
|
<metadata>name</metadata>
|
||||||
|
<gameselector>selector_recent</gameselector>
|
||||||
|
<fontPath>./core/fonts/Exo2-RegularCondensed.otf</fontPath>
|
||||||
|
<fontSize>0.045</fontSize>
|
||||||
|
<horizontalAlignment>right</horizontalAlignment>
|
||||||
|
<verticalAlignment>center</verticalAlignment>
|
||||||
|
<color>D6D6D6</color>
|
||||||
|
<letterCase>none</letterCase>
|
||||||
|
<zIndex>80</zIndex>
|
||||||
|
</text>
|
||||||
|
<text name="md_description">
|
||||||
|
<pos>0.025 0.205</pos>
|
||||||
|
<size>0.446 0.185</size>
|
||||||
|
<fontSize>0.02</fontSize>
|
||||||
|
<lineSpacing>1.2</lineSpacing>
|
||||||
|
</text>
|
||||||
|
<rating name="md_rating">
|
||||||
|
<pos>0.815 0.203</pos>
|
||||||
|
<size>0.04 0.03</size>
|
||||||
|
</rating>
|
||||||
|
<gamelistinfo name="gamelistInfo">
|
||||||
|
<color>888888</color>
|
||||||
|
</gamelistinfo>
|
||||||
|
<badges name="md_badges">
|
||||||
|
<pos>0.03 0.205</pos>
|
||||||
|
<size>0.38 0.08</size>
|
||||||
|
<lines>1</lines>
|
||||||
|
<itemsPerLine>10</itemsPerLine>
|
||||||
|
</badges>
|
||||||
|
<helpsystem name="help">
|
||||||
|
<pos>0.012 0.955</pos>
|
||||||
|
<textColor>${gamelistHelpColor}</textColor>
|
||||||
|
<iconColor>${gamelistHelpColor}</iconColor>
|
||||||
|
<textColorDimmed>${gamelistHelpColorDimmed}</textColorDimmed>
|
||||||
|
<iconColorDimmed>${gamelistHelpColorDimmed}</iconColorDimmed>
|
||||||
|
</helpsystem>
|
||||||
|
<!-- Hide some unwanted elements -->
|
||||||
|
<image name="backframe3">
|
||||||
|
<visible>false</visible>
|
||||||
|
</image>
|
||||||
|
<text name="md_lbl_publisher, md_lbl_releasedate, md_lbl_developer, md_lbl_genre,
|
||||||
|
md_lbl_players, md_lbl_lastplayed, md_developer, md_publisher, md_genre,
|
||||||
|
md_players, md_description">
|
||||||
|
<visible>false</visible>
|
||||||
|
</text>
|
||||||
|
<datetime name="md_releasedate md_lastplayed">
|
||||||
|
<visible>false</visible>
|
||||||
|
</datetime>
|
||||||
|
<rating name="md_rating">
|
||||||
|
<visible>false</visible>
|
||||||
|
</rating>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<aspectRatio name="4:3">
|
||||||
|
<view name="system, gamelist">
|
||||||
|
<grid name="gamelist_grid">
|
||||||
|
<itemSize>0.192 -1</itemSize>
|
||||||
|
</grid>
|
||||||
|
<helpsystem name="help">
|
||||||
|
<fontSize>0.034</fontSize>
|
||||||
|
</helpsystem>
|
||||||
|
</view>
|
||||||
|
<view name="gamelist"></view>
|
||||||
|
</aspectRatio>
|
||||||
|
</theme>
|
|
@ -140,26 +140,26 @@ cd ..
|
||||||
echo:
|
echo:
|
||||||
echo Setting up SDL
|
echo Setting up SDL
|
||||||
|
|
||||||
if exist SDL2-2.24.1\ (
|
if exist SDL2-2.26.1\ (
|
||||||
rmdir /S /Q SDL2-2.24.1
|
rmdir /S /Q SDL2-2.26.1
|
||||||
)
|
)
|
||||||
|
|
||||||
curl -LO https://libsdl.org/release/SDL2-devel-2.24.1-VC.zip
|
curl -LO https://libsdl.org/release/SDL2-devel-2.26.1-VC.zip
|
||||||
|
|
||||||
7z x SDL2-devel-2.24.1-VC.zip
|
7z x SDL2-devel-2.26.1-VC.zip
|
||||||
|
|
||||||
if not exist SDL2-2.24.1\ (
|
if not exist SDL2-2.26.1\ (
|
||||||
echo SDL directory is missing, aborting.
|
echo SDL directory is missing, aborting.
|
||||||
cd ..
|
cd ..
|
||||||
goto end
|
goto end
|
||||||
)
|
)
|
||||||
|
|
||||||
cd SDL2-2.24.1
|
cd SDL2-2.26.1
|
||||||
rename include SDL2
|
rename include SDL2
|
||||||
cd ..
|
cd ..
|
||||||
copy /Y SDL2-2.24.1\lib\x64\SDL2.dll ..
|
copy /Y SDL2-2.26.1\lib\x64\SDL2.dll ..
|
||||||
copy /Y SDL2-2.24.1\lib\x64\SDL2.lib ..
|
copy /Y SDL2-2.26.1\lib\x64\SDL2.lib ..
|
||||||
copy /Y SDL2-2.24.1\lib\x64\SDL2main.lib ..
|
copy /Y SDL2-2.26.1\lib\x64\SDL2main.lib ..
|
||||||
|
|
||||||
echo:
|
echo:
|
||||||
echo Setting up FFmpeg
|
echo Setting up FFmpeg
|
||||||
|
|
|
@ -38,6 +38,11 @@ rm -rf glew-*
|
||||||
curl -LO https://sourceforge.net/projects/glew/files/glew/2.1.0/glew-2.1.0.zip
|
curl -LO https://sourceforge.net/projects/glew/files/glew/2.1.0/glew-2.1.0.zip
|
||||||
unzip glew-2.1.0.zip
|
unzip glew-2.1.0.zip
|
||||||
|
|
||||||
|
if [ ! -d glew-2.1.0 ]; then
|
||||||
|
echo "GLEW directory is missing, aborting."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
echo -e "\nSetting up FreeType"
|
echo -e "\nSetting up FreeType"
|
||||||
rm -rf freetype
|
rm -rf freetype
|
||||||
|
|
||||||
|
@ -83,20 +88,20 @@ cd ..
|
||||||
echo -e "\nSetting up SDL"
|
echo -e "\nSetting up SDL"
|
||||||
rm -rf SDL2-*
|
rm -rf SDL2-*
|
||||||
|
|
||||||
curl -O https://libsdl.org/release/SDL2-devel-2.24.1-mingw.tar.gz
|
curl -O https://libsdl.org/release/SDL2-devel-2.26.1-mingw.tar.gz
|
||||||
|
|
||||||
tar xvzf SDL2-devel-2.24.1-mingw.tar.gz
|
tar xvzf SDL2-devel-2.26.1-mingw.tar.gz
|
||||||
# Needed due to some kind of file system race condition that sometimes occurs on Windows.
|
# Needed due to some kind of file system race condition that sometimes occurs on Windows.
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|
||||||
if [ ! -d SDL2-2.24.1 ]; then
|
if [ ! -d SDL2-2.26.1 ]; then
|
||||||
echo "SDL directory is missing, aborting."
|
echo "SDL directory is missing, aborting."
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mv SDL2-2.24.1/x86_64-w64-mingw32/include/SDL2 SDL2-2.24.1/
|
mv SDL2-2.26.1/x86_64-w64-mingw32/include/SDL2 SDL2-2.26.1/
|
||||||
cp -p SDL2-2.24.1/x86_64-w64-mingw32/lib/libSDL2main.a ..
|
cp -p SDL2-2.26.1/x86_64-w64-mingw32/lib/libSDL2main.a ..
|
||||||
cp -p SDL2-2.24.1/x86_64-w64-mingw32/bin/SDL2.dll ..
|
cp -p SDL2-2.26.1/x86_64-w64-mingw32/bin/SDL2.dll ..
|
||||||
|
|
||||||
echo -e "\nSetting up FFmpeg"
|
echo -e "\nSetting up FFmpeg"
|
||||||
rm -rf ffmpeg-*
|
rm -rf ffmpeg-*
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
# How many CPU threads to use for the compilation.
|
# How many CPU threads to use for the compilation.
|
||||||
JOBS=4
|
JOBS=4
|
||||||
|
|
||||||
SDL_RELEASE_TAG=release-2.24.1
|
SDL_RELEASE_TAG=release-2.26.1
|
||||||
SDL_SHARED_LIBRARY=libSDL2-2.0.so.0.2400.1
|
SDL_SHARED_LIBRARY=libSDL2-2.0.so.0.2600.1
|
||||||
|
|
||||||
echo "Building AppImage..."
|
echo "Building AppImage..."
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
# How many CPU threads to use for the compilation.
|
# How many CPU threads to use for the compilation.
|
||||||
JOBS=4
|
JOBS=4
|
||||||
|
|
||||||
SDL_RELEASE_TAG=release-2.24.1
|
SDL_RELEASE_TAG=release-2.26.1
|
||||||
SDL_SHARED_LIBRARY=libSDL2-2.0.so.0.2400.1
|
SDL_SHARED_LIBRARY=libSDL2-2.0.so.0.2600.1
|
||||||
|
|
||||||
echo "Building Steam Deck AppImage..."
|
echo "Building Steam Deck AppImage..."
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,7 @@ rm -f CMakeCache.txt
|
||||||
cmake -DCMAKE_BUILD_TYPE=Release -S .. -B .
|
cmake -DCMAKE_BUILD_TYPE=Release -S .. -B .
|
||||||
make clean
|
make clean
|
||||||
make -j${JOBS}
|
make -j${JOBS}
|
||||||
cp libSDL2-2.0.dylib ../../..
|
cp libSDL2-2.0.0.dylib ../../..
|
||||||
cd ../..
|
cd ../..
|
||||||
|
|
||||||
echo "\nBuilding libvpx"
|
echo "\nBuilding libvpx"
|
||||||
|
|
|
@ -187,7 +187,7 @@ if [ ! -d SDL ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd SDL
|
cd SDL
|
||||||
git checkout release-2.24.1
|
git checkout release-2.26.1
|
||||||
ln -s include SDL2
|
ln -s include SDL2
|
||||||
mkdir build
|
mkdir build
|
||||||
cd ..
|
cd ..
|
||||||
|
|
Loading…
Reference in a new issue