Merged the master branch into new-svg-renderer.

This commit is contained in:
Leon Styhre 2022-10-26 19:52:01 +02:00
commit e4f4f29198
59 changed files with 986 additions and 1010 deletions

View file

@ -22,7 +22,14 @@
* 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 * (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 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 MAME standalone as an alternative emulator for the model2 system
* (Windows) Added the MAME - Current RetroArch core as an alternative emulator for the model2 system
* (Windows) Added a -force-feedback option and an %INJECT% variable to the Supermodel emulator for the arcade, mame and model3 systems
* 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 * (Linux) Added Rosalie's Mupen GUI standalone as an alternative emulator for the n64 system
@ -33,6 +40,8 @@
* (Windows) Changed the binary for emulator Citra from citra.exe to citra-qt.exe as the command line binary is broken on this OS * (Windows) Changed the binary for emulator Citra from citra.exe to citra-qt.exe as the command line binary is broken on this OS
* 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 .car and .rom file extensions to the a5200 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
* Added the .m3u file extension to the pcfx system * Added the .m3u file extension to the pcfx system
* Removed the .7z and .zip file extensions from the 3do, neogeocd and neogeocdjp systems * Removed the .7z and .zip file extensions from the 3do, neogeocd and neogeocdjp systems
@ -40,7 +49,7 @@
* Removed the .ccd, .cue and .iso file extensions from the neogeo system * Removed the .ccd, .cue and .iso file extensions from the neogeo system
* Added the FinalBurn Neo RetroArch core as an alternative emulator for the neogeocd and neogeocdjp systems * Added the FinalBurn Neo RetroArch core as an alternative emulator for the neogeocd and neogeocdjp systems
* Added MAME standalone as an alternative emulator for the neogeo, neogeocd and neogeocdjp systems * Added MAME standalone as an alternative emulator for the neogeo, neogeocd and neogeocdjp systems
* Added FinalBurn Neo standalone as an alternative emulator for the fbneo, neogeo, neogeocd and neogeocdjp systems on Unix * Added FinalBurn Neo standalone as an alternative emulator for the fbneo, neogeo, neogeocd and neogeocdjp systems on Linux
* Added FinalBurn Neo standalone as an alternative emulator for the fbneo and neogeo system on Windows * Added FinalBurn Neo standalone as an alternative emulator for the fbneo and neogeo system on Windows
* Set DOSBox-X and DOSBox Staging to start in the game directory so per-game dosbox.conf files can be used * Set DOSBox-X and DOSBox Staging to start in the game directory so per-game dosbox.conf files can be used
* (macOS) Added an additional find rule entry for DOSBox-X as the binary name has been changed * (macOS) Added an additional find rule entry for DOSBox-X as the binary name has been changed
@ -56,6 +65,8 @@
* OpenGL ES: Added an OpenGLVersion setting for choosing between OpenGL ES 3.0, 3.1 and 3.2 (has to be manually set in es_settings.xml) * OpenGL ES: Added an OpenGLVersion setting for choosing between OpenGL ES 3.0, 3.1 and 3.2 (has to be manually set in es_settings.xml)
* Greatly improved the performance of shader post-processing such as scanlines and blur rendering * Greatly improved the performance of shader post-processing such as scanlines and blur rendering
* 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
* Large optimizations to the text wrapping code (generallly 300-400% faster)
* 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
@ -87,6 +98,7 @@
* 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 enabling or disabling audio playback for videos * Added theme support for enabling or disabling audio playback for videos
* 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
@ -179,7 +191,9 @@
### 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
* 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
* 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
* 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
@ -200,10 +214,14 @@
* 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
* Defining an itemScale (logoScale) property lower than 1.0 for the carousel did not work correctly
* Carousel text did not get scaled/multiplied correctly with the itemScale property (bug retained for legacy themes for maximum backward compatibility)
* Letters would sometimes get rendered with ugly edge artifacts, visible when scaling text on the carousel
* Text opacity did not work correctly in some places, such as for the help prompts * Text opacity did not work correctly in some places, such as for the help prompts
* ScrollableContainer faded semi-transparent text to fully opaque when resetting * ScrollableContainer faded semi-transparent text to fully opaque when resetting
* ScrollableContainer faded in the background text color in addition to the text color when resetting * ScrollableContainer faded in the background text color in addition to the text color when resetting
* Text elements that had an opacity set to lower than FF via the color tag were faded in during gamelist scrolling * Text elements that had an opacity set to lower than FF via the color tag were faded in during gamelist scrolling
* The help system was rendered on top of menus if placed at such a location on the screen
* Theme sets were not always sorted correctly (as seen when mixing uppercase and lowercase letters in theme names) * Theme sets were not always sorted correctly (as seen when mixing uppercase and lowercase letters in theme names)
* The SliderComponent knob was not consistently positioned * The SliderComponent knob was not consistently positioned
* The device text flickered in GuiDetectDevice when configuring a controller * The device text flickered in GuiDetectDevice when configuring a controller

View file

@ -67,15 +67,17 @@ The roadmap is under constant review so expect it to change from time to time. S
#### v2.0 (in progress) #### v2.0 (in progress)
* New theme engine with generalized views (only System and Gamelist) and theme variants support * New theme engine with generalized views (only System and Gamelist) and theme variants support
* Multiple new gamelist components (more carousel modes, grid component etc.) * Multiple new components (carousel support for the Gamelist view, grid component etc.)
* Lottie animation (vector graphics) and GIF animation support * Lottie animation (vector graphics) and GIF animation support
* OpenGL ES 3.0 renderer for use on the Raspberry Pi * OpenGL ES 3.0 renderer for use on the Raspberry Pi
* Replace the OpenGL fixed function pipeline with a shader-based renderer * Replace the OpenGL fixed function pipeline renderer with a shader-based renderer
* Replace NanoSVG with a more capable SVG rendering library * Replace NanoSVG with a more capable SVG rendering library
* Improve text and font functions, e.g. dynamic texture allocation and faster and cleaner text wrapping
* Improve the performance of the GLSL shader post-processing * Improve the performance of the GLSL shader post-processing
#### v2.1 #### v2.1
* Add element transition animations to the theme engine
* New texture/cache manager with improved memory management and support for GIF and Lottie animations * New texture/cache manager with improved memory management and support for GIF and Lottie animations
* Reduced amount of gamelist reloading to retain cached textures and improve overall performance * Reduced amount of gamelist reloading to retain cached textures and improve overall performance
* Add scraping of game manuals and maps and create a viewer for these (with PDF, GIF, JPG and PNG support) * Add scraping of game manuals and maps and create a viewer for these (with PDF, GIF, JPG and PNG support)
@ -93,7 +95,8 @@ The roadmap is under constant review so expect it to change from time to time. S
* Proper audio mixer * Proper audio mixer
* Checksum support for the scraper for exact searches and for determining when to overwrite files * Checksum support for the scraper for exact searches and for determining when to overwrite files
* Support for portrait orientation, e.g. for Tate Mode arcade cabinets * Support for portrait orientation, e.g. for Tate Mode arcade cabinets
* Improved text and font functions, e.g. faster and cleaner line wrapping and more exact sizing * Replace the built-in Unicode functions and lookup tables with those of the ICU library
* Add text kerning support using the HarfBuzz library
#### v2.3 #### v2.3

4
FAQ.md
View file

@ -86,11 +86,11 @@ This release of RetroArch has multiple technical issues so it's not officially s
## How do I add more themes? ## How do I add more themes?
Most RetroPie EmulationStation theme sets will work with ES-DE, and there are numerous resources online on where to find these. How to install them is described in the _Themes_ section of the [User guide](USERGUIDE.md#themes). Just be aware that some of these themes do not include support for modern systems like PlayStation 3 and Nintendo Switch so those platforms may look a bit ugly depending on how the theme is written. Refer to the official list of [recommended theme sets](https://gitlab.com/es-de/themes/themes-list) for a selection of high-quality themes. There are also some brief instructions there on how to download and install them. More comprehensive documentation is available in the _Themes_ section of the [User guide](USERGUIDE.md#themes). In addition to the recommended theme sets you'll be able to find a lot of additional themes if doing a web search as almost all RetroPie-compatible themes can be used with ES-DE. Just be aware that many of these do not include support for modern systems like PlayStation 3 and Nintendo Switch so those platforms may look a bit ugly depending on how the theme has been written.
## The themes I've added don't seem to work? ## The themes I've added don't seem to work?
Only RetroPie EmulationStation themes are supported, you can't use themes that were specifically developed for Batocera or Recalbox EmulationStation. A very few RetroPie themes like es-theme-carbon-2021 will not work either due to technical reasons. Only RetroPie EmulationStation themes are supported, you can't use themes that were specifically developed for Batocera or Recalbox EmulationStation. A very few RetroPie themes like es-theme-carbon-2021 will not work either due to technical reasons. Refer to the official list of [recommended theme sets](https://gitlab.com/es-de/themes/themes-list) for a selection of high-quality themes that have been thoroughly tested with ES-DE.
## I used to be a Batocera/Recalbox user and ES-DE can't seem to find some of my games? ## I used to be a Batocera/Recalbox user and ES-DE can't seem to find some of my games?

View file

@ -70,7 +70,7 @@ With the new theme engine the view presets were removed and the only views now a
In addition to the variant support which provides an unlimited flexibility for creating custom theme profiles, support for specific aspect ratios was introduced. This makes it possible to define different theme configuration for different display aspect ratios and to provide the user with the option to choose between these from the _UI Settings_ menu. That could for example be choice between a 16:9 and a 4:3 layout, or perhaps also a vertical screen orientation layout in addition to these. In addition to the variant support which provides an unlimited flexibility for creating custom theme profiles, support for specific aspect ratios was introduced. This makes it possible to define different theme configuration for different display aspect ratios and to provide the user with the option to choose between these from the _UI Settings_ menu. That could for example be choice between a 16:9 and a 4:3 layout, or perhaps also a vertical screen orientation layout in addition to these.
As well new theming abilities like Lottie animations were added with the new theme engine. As well new theming abilities like GIF and Lottie animations were added with the new theme engine.
The following are the most important changes compared to the legacy theme structure: The following are the most important changes compared to the legacy theme structure:
@ -95,6 +95,13 @@ Attempting to use any of the legacy logic in the new theme structure will make t
Except the points mentioned above, theme configuration looks pretty similar to the legacy theme structure, so anyone having experience with these older themes should hopefully feel quite at home with the new theme engine. Probably the most important thing to keep in mind is that as there are no longer any view presets available, some more effort is needed from the theme developer to define values for some elements. This is especially true for zIndex values as elements could now be hidden by other elements if care is not taken to explicitly set the zIndex for each of them. This additional work is however a small price to pay for the much more powerful and flexible theming functionality provided by the new theme engine. Except the points mentioned above, theme configuration looks pretty similar to the legacy theme structure, so anyone having experience with these older themes should hopefully feel quite at home with the new theme engine. Probably the most important thing to keep in mind is that as there are no longer any view presets available, some more effort is needed from the theme developer to define values for some elements. This is especially true for zIndex values as elements could now be hidden by other elements if care is not taken to explicitly set the zIndex for each of them. This additional work is however a small price to pay for the much more powerful and flexible theming functionality provided by the new theme engine.
Note that the legacy theme engine had quite inaccurate text sizing and font rendering and while this has been greatly improved in the new engine, for legacy themes most old bugs are retained for maximum backward compatibility. This means that you may need to revise font sizes and text placements when porting a legacy theme to the new engine. Here are some examples:
* Line spacing for the textlist element was not consistently applied across different screen resolutions
* Carousel text entries did not multiply the font size by the itemScale (logoScale) property value
* The defined line spacing was not always applied for automatically sized text elements
* Font sizes were rounded to integers, leading to imprecise text sizing across different resolutions (the rounding was also done incorrectly)
## Simple example ## Simple example
Here is a very simple theme that changes the color of the game name text: Here is a very simple theme that changes the color of the game name text:
@ -699,13 +706,14 @@ The `helpsystem` element does not really have a zIndex value and is always rende
## Theme variables ## Theme variables
Theme variables can be used to simplify theme construction. There are 2 types of variables available. Theme variables can be used to simplify theme construction and there are two types available:
* System variables * System variables
* Theme defined variables * Theme defined variables
### System variables ### System variables
System variables are system specific and are derived from the values in es_systems.xml (except for collections). System variables are system specific and are derived from the values defined in es_systems.xml (except for collections which are derived from hardcoded application-internal values).
* `system.name` * `system.name`
* `system.name.collections` * `system.name.collections`
* `system.name.noCollections` * `system.name.noCollections`
@ -716,9 +724,35 @@ System variables are system specific and are derived from the values in es_syste
* `system.theme.collections` * `system.theme.collections`
* `system.theme.noCollections` * `system.theme.noCollections`
`system.name` expands to the short name of the system as defined by the `name` tag in es_systems.xml\
`system.fullName` expands to the full system name as defined by the `fullname` tag in es_systems.xml\
`system.theme` expands to the theme directory as defined by the `theme` tag in es_systems.xml
The `.collections` and `.noCollections` versions of these variables make it possible to differentiate between regular systems and collections. This can for example be used to apply different formatting to the names of the collections as opposed to regular systems. The below example capitalizes the names of the collections while leaving the regular systems at their default formatting (as they are defined in es_systems.xml). The reason this works is that the .collections and .noCollections variables are mutually exclusive, i.e. they can never both hold a value at the same time as a system is either a real system or a collection and never both.
```xml
<view name="system">
<text name="system_name, collection_name">
<pos>0.05 0.83</pos>
<size>0.9 0.06</size>
<fontSize>0.06</fontSize>
<fontPath>./core/font.ttf</fontPath>
</text>
<text name="collection_name">
<letterCase>capitalize</letterCase>
</text>
<text name="system_name">
<text>${system.fullName.noCollections}</text>
</text>
<text name="collection_name">
<text>${system.fullName.collections}</text>
</text>
</view>
```
### Theme defined variables ### Theme defined variables
Variables can also be defined in the theme. Variables can also be defined in the theme.
``` ```xml
<variables> <variables>
<themeColor>8B0000</themeColor> <themeColor>8B0000</themeColor>
</variables> </variables>
@ -726,19 +760,19 @@ Variables can also be defined in the theme.
### Usage in themes ### Usage in themes
Variables can be used to specify the value of a theme property: Variables can be used to specify the value of a theme property:
``` ```xml
<color>${themeColor}</color> <color>${themeColor}</color>
``` ```
It can also be used to specify only a portion of the value of a theme property: It can also be used to specify only a portion of the value of a theme property:
``` ```xml
<color>${themeColor}C0</color> <color>${themeColor}C0</color>
<path>./core/images/${system.theme}.svg</path> <path>./core/images/${system.theme}.svg</path>
```` ````
Nesting of variables is supported, so the following could be done: Nesting of variables is supported, so the following could be done:
``` ```xml
<variables> <variables>
<colorRed>8b0000</colorRed> <colorRed>8b0000</colorRed>
<themeColor>${colorRed}</themeColor> <themeColor>${colorRed}</themeColor>
@ -915,6 +949,10 @@ Properties:
* `pillarboxes` - type: BOOLEAN * `pillarboxes` - type: BOOLEAN
- Whether to render black pillarboxes (and to a lesses extent letterboxes) for videos with aspect ratios where this is applicable. This is for instance useful for arcade game videos in vertical orientation. - Whether to render black pillarboxes (and to a lesses extent letterboxes) for videos with aspect ratios where this is applicable. This is for instance useful for arcade game videos in vertical orientation.
- Default is `true` - Default is `true`
* `pillarboxThreshold` - type: NORMALIZED_PAIR
- Normally it doesn't look very good to add really narrow pillarboxes or letterboxes, so by default they are skipped if the actual video size is not reaching a threshold value as compared to the overall defined video area size. By modifying this property it's possible to control that threshold, as for some theme designs it will look better with the consistency of always rendering the pillarboxes/letterboxes even if they are narrow. To clarify, the default X axis value of 0.85 means that if the video width is 85% or less as compared to the X axis defined by the `size` property, then pillarboxes will be rendered. So setting the `pillarboxThreshold` value to `1 1` will always apply pillarboxes/letterboxes regardless of the video file dimension.
- Minimum value per axis is `0.2` and maximum value per axis is `1`
- Default is `0.85 0.90`
* `scanlines` - type: BOOLEAN * `scanlines` - type: BOOLEAN
- Whether to use a shader to render scanlines. - Whether to use a shader to render scanlines.
- Default is `false` - Default is `false`
@ -1209,7 +1247,9 @@ Properties:
* `fontPath` - type: PATH * `fontPath` - type: PATH
- Path to a TrueType font (.ttf). - Path to a TrueType font (.ttf).
* `fontSize` - type: FLOAT * `fontSize` - type: FLOAT
- Size of the font as a percentage of screen height (e.g. for a value of `0.1`, the text's height would be 10% of the screen height). - Size of the font as a percentage of screen height (e.g. for a value of `0.1`, the text's height would be 10% of the screen height). This calculation is based on the reference 'S' character so other glyphs may not fill this area, or they may exceed this area.
- Minimum value is `0.001` and maximum value is `1.5`. Note that when running at a really low resolution, the minimum value may get clamped to a larger relative size. The font is allowed to overflow the height of the element by 100%, i.e. `fontSize` can be set to twice that of the y axis of the `size` property. Any value above that will be clamped.
- Default is `0.045`
* `horizontalAlignment` - type: STRING * `horizontalAlignment` - type: STRING
- Controls alignment on the X axis. - Controls alignment on the X axis.
- Valid values are `left`, `center` or `right` - Valid values are `left`, `center` or `right`
@ -1224,7 +1264,7 @@ Properties:
- Valid values are `none`, `uppercase`, `lowercase` or `capitalize` - Valid values are `none`, `uppercase`, `lowercase` or `capitalize`
- Default is `none` (original letter case is retained) - Default is `none` (original letter case is retained)
* `lineSpacing` - type: FLOAT * `lineSpacing` - type: FLOAT
- Controls the space between lines (as a multiple of font height). - Controls the space between lines (as a multiple of the font height). Due to the imprecise nature of typefaces where certain glyphs (characters) may exceed the requested font size, it's recommended to keep this value at around `1.1` or higher for multi-line text fields. This way overlapping glyphs or characters being cut off at the top or bottom will be prevented.
- Minimum value is `0.5` and maximum value is `3` - Minimum value is `0.5` and maximum value is `3`
- Default is `1.5` - Default is `1.5`
* `opacity` - type: FLOAT * `opacity` - type: FLOAT
@ -1277,7 +1317,9 @@ Properties:
* `fontPath` - type: PATH * `fontPath` - type: PATH
- Path to a TrueType font (.ttf). - Path to a TrueType font (.ttf).
* `fontSize` - type: FLOAT * `fontSize` - type: FLOAT
- Size of the font as a percentage of screen height (e.g. for a value of `0.1`, the text's height would be 10% of the screen height). - Size of the font as a percentage of screen height (e.g. for a value of `0.1`, the text's height would be 10% of the screen height). This calculation is based on the reference 'S' character so other glyphs may not fill this area, or they may exceed this area.
- Minimum value is `0.001` and maximum value is `1.5`. Note that when running at a really low resolution, the minimum value may get clamped to a larger relative size. The font is allowed to overflow the height of the element by 100%, i.e. `fontSize` can be set to twice that of the y axis of the `size` property. Any value above that will be clamped.
- Default is `0.045`
* `horizontalAlignment` - type: STRING * `horizontalAlignment` - type: STRING
- Controls alignment on the X axis. - Controls alignment on the X axis.
- Valid values are `left`, `center` or `right` - Valid values are `left`, `center` or `right`
@ -1348,7 +1390,9 @@ Properties:
* `fontPath` - type: PATH * `fontPath` - type: PATH
- Path to a TrueType font (.ttf). - Path to a TrueType font (.ttf).
* `fontSize` - type: FLOAT * `fontSize` - type: FLOAT
- Size of the font as a percentage of screen height (e.g. for a value of `0.1`, the text's height would be 10% of the screen height). - Size of the font as a percentage of screen height (e.g. for a value of `0.1`, the text's height would be 10% of the screen height). This calculation is based on the reference 'S' character so other glyphs may not fill this area, or they may exceed this area.
- Minimum value is `0.001` and maximum value is `1.5`. Note that when running at a really low resolution, the minimum value may get clamped to a larger relative size. The font is allowed to overflow the height of the element by 100%, i.e. `fontSize` can be set to twice that of the y axis of the `size` property. Any value above that will be clamped.
- Default is `0.045`
* `color` - type: COLOR * `color` - type: COLOR
* `backgroundColor` - type: COLOR * `backgroundColor` - type: COLOR
* `horizontalAlignment` - type: STRING * `horizontalAlignment` - type: STRING
@ -1482,12 +1526,12 @@ Properties:
- Minimum value is `0` and maximum value is `20` - Minimum value is `0` and maximum value is `20`
- Default is `8` - Default is `8`
* `itemSize` - type: NORMALIZED_PAIR * `itemSize` - type: NORMALIZED_PAIR
- Both axes need to be defined. - Size of the item prior to multiplication by the `itemScale` value, i.e. the size of all unselected items. Both axes need to be defined.
- Minimum value per axis is `0.05` and maximum value per axis is `1` - Minimum value per axis is `0.05` and maximum value per axis is `1`
- Default is `0.25 0.155` - Default is `0.25 0.155`
* `itemScale` - type: FLOAT. * `itemScale` - type: FLOAT.
- Selected item is increased in size by this scale. - Selected item is increased in size by this scale.
- Minimum value is `0.5` and maximum value is `3` - Minimum value is `0.2` and maximum value is `3`
- Default is `1.2` - Default is `1.2`
* `itemTransitions` - type: STRING * `itemTransitions` - type: STRING
- How to render item transitions when navigating the carousel. By default a slide animation will be played when moving between items but if this property is set to `instant` instead then the transitions will be immediate. - How to render item transitions when navigating the carousel. By default a slide animation will be played when moving between items but if this property is set to `instant` instead then the transitions will be immediate.
@ -1562,13 +1606,14 @@ Properties:
* `fontPath` - type: PATH * `fontPath` - type: PATH
- Path to a TrueType font (.ttf) used as fallback if there is no `staticItem` / `itemType` image defined or found, and if `defaultItem` has not been defined. - Path to a TrueType font (.ttf) used as fallback if there is no `staticItem` / `itemType` image defined or found, and if `defaultItem` has not been defined.
* `fontSize` - type: FLOAT * `fontSize` - type: FLOAT
- Size of the font as a percentage of screen height (e.g. for a value of `0.1`, the text's height would be 10% of the screen height). - Size of the font as a percentage of screen height (e.g. for a value of `0.1`, the text's height would be 10% of the screen height). This calculation is based on the reference 'S' character so other glyphs may not fill this area, or they may exceed this area. This property value is effectively multiplied by the `itemScale` value for the currently selected item (but if this property is omitted then the default value will not get multiplied by `itemScale`).
- Minimum value is `0.001` and maximum value is `1.5`. Note that when running at a really low resolution, the minimum value may get clamped to a larger relative size.
- Default is `0.085` - Default is `0.085`
* `letterCase` - type: STRING * `letterCase` - type: STRING
- Valid values are `none`, `uppercase`, `lowercase` or `capitalize` - Valid values are `none`, `uppercase`, `lowercase` or `capitalize`
- Default is `none` (original letter case is retained) - Default is `none` (original letter case is retained)
* `lineSpacing` - type: FLOAT * `lineSpacing` - type: FLOAT
- Controls the space between lines (as a multiple of font height). - Controls the space between lines (as a multiple of the font height). Due to the imprecise nature of typefaces where certain glyphs (characters) may exceed the requested font size, it's recommended to keep this value at around `1.1` or higher. This way overlapping glyphs or characters being cut off at the top or bottom will be prevented.
- Minimum value is `0.5` and maximum value is `3` - Minimum value is `0.5` and maximum value is `3`
- Default is `1.5` - Default is `1.5`
* `fadeAbovePrimary` - type: BOOLEAN * `fadeAbovePrimary` - type: BOOLEAN
@ -1625,6 +1670,8 @@ Properties:
- Secondary color; what this means depends on the text list. For example, for game lists, it is the color of a folder. - Secondary color; what this means depends on the text list. For example, for game lists, it is the color of a folder.
* `fontPath` - type: PATH * `fontPath` - type: PATH
* `fontSize` - type: FLOAT * `fontSize` - type: FLOAT
- Size of the font as a percentage of screen height (e.g. for a value of `0.1`, the text's height would be 10% of the screen height). This calculation is based on the reference 'S' character so other glyphs may not fill this area, or they may exceed this area.
- Default is `0.045`
* `horizontalAlignment` - type: STRING * `horizontalAlignment` - type: STRING
- Controls alignment on the X axis. - Controls alignment on the X axis.
- Valid values are `left`, `center` or `right` - Valid values are `left`, `center` or `right`
@ -1636,7 +1683,7 @@ Properties:
- Valid values are `none`, `uppercase`, `lowercase` or `capitalize` - Valid values are `none`, `uppercase`, `lowercase` or `capitalize`
- Default is `none` (original letter case is retained) - Default is `none` (original letter case is retained)
* `lineSpacing` - type: FLOAT * `lineSpacing` - type: FLOAT
- Controls the space between lines (as a multiple of font height). - Controls the space between lines (as a multiple of the font height).
- Minimum value is `0.5` and maximum value is `3` - Minimum value is `0.5` and maximum value is `3`
- Default is `1.5` - Default is `1.5`
* `indicators` - type: STRING * `indicators` - type: STRING
@ -1706,7 +1753,8 @@ Properties:
- Default is the same value as iconColor. - Default is the same value as iconColor.
* `fontPath` - type: PATH * `fontPath` - type: PATH
* `fontSize` - type: FLOAT * `fontSize` - type: FLOAT
- This property also implicitly sets the icon size and is therefore the means to change the overall size of the helpsystem element. - This property implicitly sets the icon size and is therefore the means to change the overall size of the helpsystem element. This calculation is based on the reference 'S' character so other glyphs may not fill this area, or they may exceed this area.
- Minimum value is `0.001` and maximum value is `1.5`. Note that when running at a really low resolution, the minimum value may get clamped to a larger relative size.
- Default is `0.035` - Default is `0.035`
* `entrySpacing` - type: FLOAT * `entrySpacing` - type: FLOAT
- Spacing between the help element pairs. - Spacing between the help element pairs.

View file

@ -664,7 +664,7 @@ RetroArch does not embed any version information into the filename so no wildcar
## Using manually downloaded emulators on Linux ## Using manually downloaded emulators on Linux
Normally on Linux you would install emulators using either one of the established package formats, i.e. Flatpak, AppImage or Snap, or you would install them using the operating system repository. Less likely would be to build from source code and install to a standard system directory. In all these instances ES-DE should be able to find the emulator when launching a game. But in some rare cases you may instead manually download an emulator as an archive file to unzip somewhere on the file system. Normally you would want to place these files in your home directory, and if running a distribution that has an immutable filesystem (such as SteamOS), you don't even have the choice to install them to a standard system directory. Normally on Linux you would install emulators using either one of the established cross-distribution package formats, i.e. AppImage, Snap or Flatpak, or you would install them using the operating system repository (including the AUR if available on your OS). Less likely would be to manually build from source code and install to a standard system directory. In all these instances ES-DE should be able to find the emulator when launching a game. But in some rare cases you may instead manually download an emulator as an archive file to unzip somewhere on the file system. Normally you would want to place these files in your home directory, and if running a distribution that has an immutable filesystem (such as SteamOS or Fedora Silverblue), you don't even have the choice to install them to a standard system directory.
For these situations ES-DE looks for emulators in the same directories where it looks for AppImages (as explained in the section above), meaning: For these situations ES-DE looks for emulators in the same directories where it looks for AppImages (as explained in the section above), meaning:
``` ```
@ -691,6 +691,7 @@ The following manually downloaded emulators are supported when using the bundled
| flash | Lightspark | lightspark/lightspark | | flash | Lightspark | lightspark/lightspark |
| flash | Ruffle | ruffle/ruffle | | flash | Ruffle | ruffle/ruffle |
| fmtowns | Tsugaru | tsugaru/Tsugaru_CUI | | fmtowns | Tsugaru | tsugaru/Tsugaru_CUI |
| model3 | Supermodel | Supermodel/supermodel |
| oric | Oricutron | oricutron/Oricutron | | oric | Oricutron | oricutron/Oricutron |
| pico8 | PICO-8 | pico-8/pico8 | | pico8 | PICO-8 | pico-8/pico8 |
| psvita | Vita3K | Vita3K/Vita3K | | psvita | Vita3K | Vita3K/Vita3K |
@ -908,15 +909,21 @@ Not all systems are as simple as described above, or there may be multiple ways
#### Arcade and Neo Geo #### Arcade and Neo Geo
**General**
For all the supported MAME variants as well as Final Burn Alpha/FinalBurn Neo and Neo Geo, single file archives should be used. But these should retain the MAME names as filenames since ES-DE ships with MAME lookup tables, meaning the MAME names are expanded to the full game names. For all the supported MAME variants as well as Final Burn Alpha/FinalBurn Neo and Neo Geo, single file archives should be used. But these should retain the MAME names as filenames since ES-DE ships with MAME lookup tables, meaning the MAME names are expanded to the full game names.
For instance `topgunnr.7z` will be expanded to `Top Gunner`. For instance `topgunnr.7z` will be expanded to `Top Gunner`.
This is required by the TheGamesDB scraper where the expanded filenames are used for game searches. (Screenscraper natively supports searches using the MAME names). It's also quite nice to have the gamelist populated with the expanded game names even before any scraping has taken place. This is required by the TheGamesDB scraper where the expanded filenames are used for game searches. (Screenscraper natively supports searches using the MAME names). It's also quite nice to have the gamelist populated with the expanded game names even before any scraping has taken place.
By default ES-DE will filter out BIOSes and devices that can't be launched directly, meaning these will never show up in the gamelist. But this only applies to files that are listed in the regular MAME driver file and BIOSes and devices for systems like MESS and Model 2 will not be filtered out. You'll instead need to manually hide these files using the _Hidden_ option in the metadata editor. By default ES-DE will filter out BIOSes and devices that can't be launched directly, meaning these will never show up in the gamelist. But this only applies to files that are listed in the regular MAME driver file and BIOSes and devices for systems like MESS will not be filtered out. You'll instead need to manually hide these files using the _Hidden_ option in the metadata editor.
If emulating Sega Model 2 games using _Model 2 Emulator_, then you need to change the ROM directory path in the EMULATOR.INI file to point to your Model 2 ROMs. If you're using a portable ES-DE installation, then you can set the ROM directory path to be relative, for example: If using the standalone release of FinalBurn Neo you also need to define the ROM directory in the fbneo.ini file or via the user interface as this emulator does not support passing the full path to the game ROM on game launch (see the comments about Model 2 Emulator below for more details).
**Sega Model 2**
If emulating Sega Model 2 games using _Model 2 Emulator_ on Windows, then you need to change the ROM directory path in the EMULATOR.INI file to point to your Model 2 ROMs. If you're using a portable ES-DE installation, then you can set the ROM directory path to be relative, for example:
``` ```
[RomDirs] [RomDirs]
Dir1=..\..\ROMs\arcade\Sega Model 2 Dir1=..\..\ROMs\arcade\Sega Model 2
@ -926,7 +933,26 @@ The EMULATOR.INI file is found in the _Model 2 Emulator_ installation directory.
Also note that Model 2 Emulator is a bit broken and on most GPU drivers it will only work correctly if ES-DE keeps running in the background while the game is launched. However, for some GPU drivers the opposite is true and the emulator will only work if ES-DE is suspended. To use the latter setup, switch to the alternative emulator entry _Model 2 Emulator [Suspend ES-DE] (Standalone)_. Also note that Model 2 Emulator is a bit broken and on most GPU drivers it will only work correctly if ES-DE keeps running in the background while the game is launched. However, for some GPU drivers the opposite is true and the emulator will only work if ES-DE is suspended. To use the latter setup, switch to the alternative emulator entry _Model 2 Emulator [Suspend ES-DE] (Standalone)_.
Likewise, if using the standalone release of FinalBurn Neo you also need to define the ROM directory in the fbneo.ini file or via the user interface as this emulator does not support passing the full path to the game ROM on game launch. On Unix/Linux and macOS, the only available emulator for Sega Model 2 is MAME, either the RetroArch - Current core or MAME standalone. Compatibility is still quite poor with only a handful of games working correctly, but this is likely to improve going forward as almost all games for this platform can already start and run to a certain degree. Some games flagged as not working by MAME are still playable with only minor glitches to audio and graphics, just make sure to use a recent ROM set for maximum compatibility.
**Sega Model 3**
For Sega Model 3 emulation on Linux download the custom [Supermodel_2022-10-07.tar.gz](https://gitlab.com/es-de/emulationstation-de/-/package_files/55835402/download) package for ES-DE and follow the instructions in the Readme.txt file. In summary you need to place the `supermodel` binary into `~/Applications/Supermodel/` and you need to place the `Config` and `NVRAM` directories into `~/ROMs/model3/`. Note that this build does not include network support as that would make it incompatible with SteamOS. Apart from that it runs really well. If you're using a Linux OS with access to the AUR, then you can use that release of Supermodel instead. But if you do, you still need to place your Config and NVRAM directories into ~/ROMs/model3/ so it's a good idea to download the custom package linked above and read the Readme.txt file to fully understand the required setup.
Although there is a Homebrew release of Supermodel for macOS this seems to be quite old and is apparently not working correctly so for the time being the model3 system is unsupported on this operating system.
It's possible to add per-game command line parameters that will be passed to Supermodel on launch. To accomplish this, create a file named _\<game\>.commands_ in the same directory as the game file, for example `daytona2.commands` and add the options to this file. Here is an example of what the file contents could look like:
```
-legacy3d -show-fps
```
**MAME standalone on macOS**
If using the Homebrew release of MAME standalone on macOS and emulating MESS systems like astrocde and ti99, then you need to configure the path to the MAME hash files in the mame.ini file. Alternatively you can symlink the installed hash directory to `~/.mame/` like the following (you will of course need to modify the command depending on which MAME version you have installed):
```
ln -s /opt/homebrew/Cellar/mame/0.248/share/mame/hash ~/.mame/ # on ARM/Apple
ln -s /usr/local/Cellar/mame/0.248/share/mame/hash ~/.mame/ # on x86/Intel
```
#### Nintendo Game and Watch #### Nintendo Game and Watch
@ -2881,31 +2907,33 @@ If you're migrating from a previous version of EmulationStation that has absolut
## Themes ## Themes
ES-DE is fully themeable, and although the application ships with the comprehensive slate-DE and modern-DE theme sets, you can use most RetroPie-compatible EmulationStation themes as well. Just be aware that ES-DE has added additional theme functionality compared to the RetroPie fork and more still will be added in future versions. This means that you may not get the full benefits of the application if you're using a theme set which has not been updated specifically for ES-DE. Some themes may also look slightly different as bugs that were present in the RetroPie fork have been fixed. Also note that most Batocera and Recalbox themes are not compatible as these forks are quite different. ES-DE is fully themeable, and although the application ships with the comprehensive slate-DE and modern-DE theme sets, you can use most RetroPie-compatible EmulationStation themes as well as any themes made specifically for the ES-DE 2.0 theme engine. Note that most Batocera and Recalbox themes are not compatible as these forks use a different theme engine than ES-DE.
As a side comment, the terms _theme_ and _theme set_ are both used when talking about theming. The technically correct term for what you apply to the application to achieve a different look is a _theme set_ as it's a collection of a number of themes for a number of game systems. But in practice it doesn't matter as both terms refer to the same thing and the terms are used interchangeably in this guide. As a side comment, the terms _theme_ and _theme set_ are both used when talking about theming. The technically correct term for what you apply to the application to achieve a different look is a _theme set_ as it's a collection of a number of themes for a number of game systems. But in practice it doesn't matter as both terms refer to the same thing and the terms are used interchangeably in the ES-DE documentation.
Themes are most easily installed to your ES-DE home directory, i.e. `~/.emulationstation/themes`. By just adding the theme sets there, one folder each, they will be found during startup and you can then choose between them via the _UI Settings_ menu on the main menu. Themes are most easily installed to your ES-DE home directory, i.e. `~/.emulationstation/themes/`. By just adding the theme sets there, one folder each, they will be found during startup and you can then choose between them via the _UI Settings_ menu on the main menu. If using the portable release of ES-DE on Windows, the .emulationstation folder can be found in the root of the EmulationStation-DE directory.
For this example, we've downloaded the [Carbon](https://github.com/RetroPie/es-theme-carbon) and [Fundamental](https://github.com/G-rila/es-theme-fundamental) themes and uncompressed them to the ES-DE home directory: To download a theme from its GitHub page, press the green _Code_ button in the upper right corner and choose _Download ZIP_. The process is identical on GitLab, but this site uses a button with a download symbol instead of a green button. You then simply unpack the file into `~/.emulationstation/themes/` and restart ES-DE.
For this example, we've downloaded the [alekfull-nx-es-de](https://github.com/anthonycaccese/alekfull-nx-es-de) and [es-theme-carbon](https://github.com/RetroPie/es-theme-carbon) themes and uncompressed them to the themes directory:
``` ```
~/.emulationstation/themes/alekfull-nx-es-de
~/.emulationstation/themes/es-theme-carbon ~/.emulationstation/themes/es-theme-carbon
~/.emulationstation/themes/es-theme-fundamental
``` ```
We now have four entries in the Theme set selector in the UI settings menu, i.e. _slate-DE, modern-DE, es-theme-carbon_ and _es-theme-fundamental_. We now have four entries in the _Theme set_ selector in the UI settings menu, i.e. _alekfull-nx-es-de, es-theme-carbon, modern-DE_ and _slate-DE_.
Although you place additional themes in your ES-DE home directory, the default slate-DE and modern-DE themes are located in the installation folder. For example this could be `/usr/share/emulationstation/themes` or `/usr/local/share/emulationstation/themes` on Unix, `/Applications/EmulationStation Desktop Edition.app/Contents/Resources/themes` on macOS or `C:\Program Files\EmulationStation-DE\themes` on Windows. Although you should place additional themes in your ES-DE home directory, the default slate-DE and modern-DE themes are located in the installation folder as they come bundled with the application. For example this could be `/usr/share/emulationstation/themes/` or `/usr/local/share/emulationstation/themes/` on Unix, `/Applications/EmulationStation Desktop Edition.app/Contents/Resources/themes/` on macOS or `C:\Program Files\EmulationStation-DE\themes\` on Windows. If using the portable ES-DE release on Windows, the themes folder will be located in the root of the EmulationStation-DE directory.
Note that if using the AppImage release on Linux, then there is no installation folder as all files are contained inside the AppImage file. Note that if using the AppImage release on Linux, then there is no installation folder as all files are contained inside the AppImage file.
So if you would like to customize the slate-DE or modern-DE theme sets, simply make a copy of their directories to ~/.emulationstation/themes and then those copies will take precedence over the ones in the application installation directory. If you would like to customize the slate-DE or modern-DE theme sets, simply make a copy of their directories to `~/.emulationstation/themes/` and then those copies will take precedence over the ones in the application installation directory.
Here is a good resource with a list of themes (although you will have to search online for the download location for each theme set): Refer to the official list of recommended theme sets for a selection of high-quality themes:
https://retropie.org.uk/docs/Themes https://gitlab.com/es-de/themes/themes-list
![alt text](images/es-de_ui_theme_support.png "ES-DE Theme Support") ![alt text](images/es-de_ui_theme_support.png "ES-DE Theme Support")
_This is a screenshot of the modern-DE theme that is bundled with ES-DE (in addition to the default slate-DE theme)._ _This is a screenshot of the modern-DE theme that is bundled with ES-DE (in addition to the default slate-DE theme)._
@ -2971,7 +2999,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)** [W*] | 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*] | 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 | |
@ -2993,7 +3021,7 @@ The **@** symbol indicates that the emulator is _deprecated_ and will be removed
| coco | Tandy Color Computer | XRoar CoCo 2 NTSC **(Standalone)** [UMW*] | XRoar CoCo 2 PAL **(Standalone)** [UMW*] | Yes | See the specific _Tandy Color Computer_ section elsewhere in this guide | | coco | Tandy Color Computer | XRoar CoCo 2 NTSC **(Standalone)** [UMW*] | XRoar CoCo 2 PAL **(Standalone)** [UMW*] | Yes | See the specific _Tandy Color Computer_ section elsewhere in this guide |
| colecovision | ColecoVision | blueMSX | Gearcoleco,<br>openMSX **(Standalone)** [UMW*],<br>ares **(Standalone)** [UMW*] | Yes | Single archive or ROM file in root folder | | colecovision | ColecoVision | blueMSX | Gearcoleco,<br>openMSX **(Standalone)** [UMW*],<br>ares **(Standalone)** [UMW*] | Yes | Single archive or ROM file in root folder |
| cps | Capcom Play System | MAME - Current | MAME 2010,<br>MAME 2003-Plus,<br>MAME 2000,<br>MAME **(Standalone)** [UMW*],<br>FinalBurn Neo,<br>FB Alpha 2012,<br>FB Alpha 2012 CPS-1,<br>FB Alpha 2012 CPS-2,<br>FB Alpha 2012 CPS-3 | Depends | See the specific _Arcade and Neo Geo_ section elsewhere in this guide | | cps | Capcom Play System | MAME - Current | MAME 2010,<br>MAME 2003-Plus,<br>MAME 2000,<br>MAME **(Standalone)** [UMW*],<br>FinalBurn Neo,<br>FB Alpha 2012,<br>FB Alpha 2012 CPS-1,<br>FB Alpha 2012 CPS-2,<br>FB Alpha 2012 CPS-3 | Depends | See the specific _Arcade and Neo Geo_ section elsewhere in this guide |
| daphne | Daphne Arcade LaserDisc Emulator | Hypseus [Daphne] **(Standalone)** [UW*] | Hypseus [Singe] **(Standalone)** [UW*] | Yes (Daphne games) | See the specific _Hypseus Singe (Daphne)_ section elsewhere in this guide | | daphne | Daphne Arcade LaserDisc Emulator | Hypseus [Daphne] **(Standalone)** [UW*] | Hypseus [Singe] **(Standalone)** [UW*] | Yes for Daphne games | See the specific _Hypseus Singe (Daphne)_ section elsewhere in this guide |
| desktop | Desktop Applications | _Suspend ES-DE_ | _Keep ES-DE running_ | | See the specific _Ports and desktop applications_ section elsewhere in this guide | | desktop | Desktop Applications | _Suspend ES-DE_ | _Keep ES-DE running_ | | See the specific _Ports and desktop applications_ section elsewhere in this guide |
| doom | Doom | PrBoom | Boom 3 [UW],<br>Boom 3 xp [UW],<br>_Shortcut or script_ | No | | | doom | Doom | PrBoom | Boom 3 [UW],<br>Boom 3 xp [UW],<br>_Shortcut or script_ | No | |
| dos | DOS (PC) | DOSBox-Pure | DOSBox-Core,<br>DOSBox-SVN,<br>DOSBox-X **(Standalone)**,<br>DOSBox Staging **(Standalone)** [UMW*] | No | See the specific _DOS / PC_ section elsewhere in this guide | | dos | DOS (PC) | DOSBox-Pure | DOSBox-Core,<br>DOSBox-SVN,<br>DOSBox-X **(Standalone)**,<br>DOSBox Staging **(Standalone)** [UMW*] | No | See the specific _DOS / PC_ section elsewhere in this guide |
@ -3021,7 +3049,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)** [W*] | 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*] | 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 |
@ -3030,8 +3058,8 @@ The **@** symbol indicates that the emulator is _deprecated_ and will be removed
| megadrive | Sega Mega Drive | Genesis Plus GX | Genesis Plus GX Wide,<br>PicoDrive,<br>BlastEm,<br>BlastEm **(Standalone)** [U],<br>Mednafen **(Standalone)** [UMW*],<br>ares **(Standalone)** [UMW*] | No | Single archive or ROM file in root folder | | megadrive | Sega Mega Drive | Genesis Plus GX | Genesis Plus GX Wide,<br>PicoDrive,<br>BlastEm,<br>BlastEm **(Standalone)** [U],<br>Mednafen **(Standalone)** [UMW*],<br>ares **(Standalone)** [UMW*] | No | Single archive or ROM file in root folder |
| 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*] | Model 2 Emulator [Suspend ES-DE] **(Standalone)** [W*] | | 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)** [W*] | | | 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 |
| 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 | |
@ -3065,7 +3093,7 @@ The **@** symbol indicates that the emulator is _deprecated_ and will be removed
| pico8 | PICO-8 Fantasy Console | PICO-8 **(Standalone)** | PICO-8 Splore **(Standalone)** | No | See the specific _PICO-8_ section elsewhere in this guide | | pico8 | PICO-8 Fantasy Console | PICO-8 **(Standalone)** | PICO-8 Splore **(Standalone)** | No | See the specific _PICO-8_ section elsewhere in this guide |
| pokemini | Nintendo Pokémon Mini | PokeMini | | No | | | pokemini | Nintendo Pokémon Mini | PokeMini | | No | |
| ports | Ports | _Various_ | | No | See the specific _Ports and desktop applications_ section elsewhere in this guide | | ports | Ports | _Various_ | | No | See the specific _Ports and desktop applications_ section elsewhere in this guide |
| ps2 | Sony PlayStation 2 | PCSX2 [UW],<br>PCSX2 **(Standalone)** [M] | PCSX2 **(Standalone)** [UW*],<br>PCSX2 Legacy **(Standalone)**@,<br>Play! **(Standalone)** [UMW*],<br>AetherSX2 **(Standalone)** [M] | Yes (No for Play!) | | | ps2 | Sony PlayStation 2 | PCSX2 [UW],<br>PCSX2 **(Standalone)** [M] | PCSX2 **(Standalone)** [UW*],<br>PCSX2 Legacy **(Standalone)**@,<br>Play! **(Standalone)** [UMW*],<br>AetherSX2 **(Standalone)** [M] | Yes except for Play! | |
| ps3 | Sony PlayStation 3 | RPCS3 Shortcut **(Standalone)** [UMW*] | RPCS3 Directory **(Standalone)** [UMW*] | Yes | See the specific _Sony PlayStation 3_ section elsewhere in this guide | | ps3 | Sony PlayStation 3 | RPCS3 Shortcut **(Standalone)** [UMW*] | RPCS3 Directory **(Standalone)** [UMW*] | Yes | See the specific _Sony PlayStation 3_ section elsewhere in this guide |
| ps4 | Sony PlayStation 4 | _Placeholder_ | | | | | ps4 | Sony PlayStation 4 | _Placeholder_ | | | |
| psp | Sony PlayStation Portable | PPSSPP | PPSSPP **(Standalone)** | No | Single .iso file in root folder | | psp | Sony PlayStation Portable | PPSSPP | PPSSPP **(Standalone)** | No | Single .iso file in root folder |
@ -3097,7 +3125,7 @@ The **@** symbol indicates that the emulator is _deprecated_ and will be removed
| tanodragon | Tano Dragon | XRoar **(Standalone)** | | Yes | See the specific _Dragon 32 and Tano Dragon_ section elsewhere in this guide | | tanodragon | Tano Dragon | XRoar **(Standalone)** | | Yes | See the specific _Dragon 32 and Tano Dragon_ section elsewhere in this guide |
| tg16 | NEC TurboGrafx-16 | Beetle PCE | Beetle PCE FAST,<br>Mednafen **(Standalone)** [UMW*],<br>ares **(Standalone)** [UMW*] | No | Single archive or ROM file in root folder | | tg16 | NEC TurboGrafx-16 | Beetle PCE | Beetle PCE FAST,<br>Mednafen **(Standalone)** [UMW*],<br>ares **(Standalone)** [UMW*] | No | Single archive or ROM file in root folder |
| tg-cd | NEC TurboGrafx-CD | Beetle PCE | Beetle PCE FAST,<br>Mednafen **(Standalone)** [UMW*],<br>ares **(Standalone)** [UMW*] | Yes | | | tg-cd | NEC TurboGrafx-CD | Beetle PCE | Beetle PCE FAST,<br>Mednafen **(Standalone)** [UMW*],<br>ares **(Standalone)** [UMW*] | Yes | |
| ti99 | Texas Instruments TI-99 | MAME **(Standalone)** [UW*] | | Yes | See the specific _Texas Instruments TI-99_ section elsewhere in this guide | | ti99 | Texas Instruments TI-99 | MAME **(Standalone)** [UMW*] | | Yes | See the specific _Texas Instruments TI-99_ section elsewhere in this guide |
| tic80 | TIC-80 Game Engine | TIC-80 | | No | Single .tic file in root folder | | tic80 | TIC-80 Game Engine | TIC-80 | | No | Single .tic file in root folder |
| to8 | Thomson TO8 | Theodore | | | | | to8 | Thomson TO8 | Theodore | | | |
| trs-80 | Tandy TRS-80 | sdl2trs DOS Diskette **(Standalone)** [UW*] | sdl2trs Bootable Diskette **(Standalone)** [UW*],<br>sdl2trs CMD File **(Standalone)** [UW*] | Yes | See the specific _Tandy TRS-80_ section elsewhere in this guide | | trs-80 | Tandy TRS-80 | sdl2trs DOS Diskette **(Standalone)** [UW*] | sdl2trs Bootable Diskette **(Standalone)** [UW*],<br>sdl2trs CMD File **(Standalone)** [UW*] | Yes | See the specific _Tandy TRS-80_ section elsewhere in this guide |

View file

@ -659,7 +659,7 @@ RetroArch does not embed any version information into the filename so no wildcar
## Using manually downloaded emulators on Linux ## Using manually downloaded emulators on Linux
Normally on Linux you would install emulators using either one of the established package formats, i.e. Flatpak, AppImage or Snap, or you would install them using the operating system repository. Less likely would be to build from source code and install to a standard system directory. In all these instances ES-DE should be able to find the emulator when launching a game. But in some rare cases you may instead manually download an emulator as an archive file to unzip somewhere on the file system. Normally you would want to place these files in your home directory, and if running a distribution that has an immutable filesystem (such as SteamOS), you don't even have the choice to install them to a standard system directory. Normally on Linux you would install emulators using either one of the established cross-distribution package formats, i.e. AppImage, Snap or Flatpak, or you would install them using the operating system repository (including the AUR if available on your OS). Less likely would be to manually build from source code and install to a standard system directory. In all these instances ES-DE should be able to find the emulator when launching a game. But in some rare cases you may instead manually download an emulator as an archive file to unzip somewhere on the file system. Normally you would want to place these files in your home directory, and if running a distribution that has an immutable filesystem (such as SteamOS or Fedora Silverblue), you don't even have the choice to install them to a standard system directory.
For these situations ES-DE looks for emulators in the same directories where it looks for AppImages (as explained in the section above), meaning: For these situations ES-DE looks for emulators in the same directories where it looks for AppImages (as explained in the section above), meaning:
``` ```
@ -912,6 +912,12 @@ The EMULATOR.INI file is found in the _Model 2 Emulator_ installation directory.
Also note that Model 2 Emulator is a bit broken and on most GPU drivers it will only work correctly if ES-DE keeps running in the background while the game is launched. However, for some GPU drivers the opposite is true and the emulator will only work if ES-DE is suspended. To use the latter setup, switch to the alternative emulator entry _Model 2 Emulator [Suspend ES-DE] (Standalone)_. Also note that Model 2 Emulator is a bit broken and on most GPU drivers it will only work correctly if ES-DE keeps running in the background while the game is launched. However, for some GPU drivers the opposite is true and the emulator will only work if ES-DE is suspended. To use the latter setup, switch to the alternative emulator entry _Model 2 Emulator [Suspend ES-DE] (Standalone)_.
If using the Homebrew release of MAME standalone on macOS and emulating MESS systems like astrocde and ti99, then you need to configure the path to the MAME hash files in the mame.ini file. Alternatively you can symlink the installed hash directory to `~/.mame/` like the following (you will of course need to modify the command depending on which MAME version you have installed):
```
ln -s /opt/homebrew/Cellar/mame/0.248/share/mame/hash ~/.mame/ # on ARM/Apple
ln -s /usr/local/Cellar/mame/0.248/share/mame/hash ~/.mame/ # on x86/Intel
```
#### Nintendo Switch #### Nintendo Switch
The Nintendo Switch emulator Yuzu is distributed as a Snap package, Flatpak package or AppImage on Linux and as a regular installer on Windows. At the moment there is unfortunately no macOS release of this emulator and it's unclear if it can run on BSD Unix. The Nintendo Switch emulator Yuzu is distributed as a Snap package, Flatpak package or AppImage on Linux and as a regular installer on Windows. At the moment there is unfortunately no macOS release of this emulator and it's unclear if it can run on BSD Unix.
@ -2811,29 +2817,33 @@ If you're migrating from a previous version of EmulationStation that has absolut
## Themes ## Themes
ES-DE is fully themeable, and although the application ships with the comprehensive rbsimple-DE and modern-DE theme sets, you can use most RetroPie-compatible EmulationStation themes as well. Just be aware that ES-DE has added additional theme functionality compared to the RetroPie fork and more still will be added in future versions. This means that you may not get the full benefits of the application if you're using a theme set which has not been updated specifically for ES-DE. Some themes may also look slightly different as bugs that were present in the RetroPie fork have been fixed. Also note that most Batocera and Recalbox themes are not compatible as these forks are quite different. ES-DE is fully themeable, and although the application ships with the comprehensive rbsimple-DE and modern-DE theme sets, you can use most RetroPie-compatible EmulationStation themes as well. Note that most Batocera and Recalbox themes are not compatible as these forks use a different theme engine than ES-DE.
As a side comment, the terms _theme_ and _theme set_ are both used when talking about theming. The technically correct term for what you apply to the application to achieve a different look is a _theme set_ as it's a collection of a number of themes for a number of game systems. But in practice it doesn't matter as both terms refer to the same thing and the terms are used interchangeably in this guide. As a side comment, the terms _theme_ and _theme set_ are both used when talking about theming. The technically correct term for what you apply to the application to achieve a different look is a _theme set_ as it's a collection of a number of themes for a number of game systems. But in practice it doesn't matter as both terms refer to the same thing and the terms are used interchangeably in the ES-DE documentation.
Themes are most easily installed to your ES-DE home directory, i.e. `~/.emulationstation/themes`. By just adding the theme sets there, one folder each, they will be found during startup and you can then choose between them via the _UI Settings_ menu on the main menu. Themes are most easily installed to your ES-DE home directory, i.e. `~/.emulationstation/themes/`. By just adding the theme sets there, one folder each, they will be found during startup and you can then choose between them via the _UI Settings_ menu on the main menu. If using the portable release of ES-DE on Windows, the .emulationstation folder can be found in the root of the EmulationStation-DE directory.
For this example, we've downloaded the [Carbon](https://github.com/RetroPie/es-theme-carbon) and [Fundamental](https://github.com/G-rila/es-theme-fundamental) themes and uncompressed them to the ES-DE home directory: To download a theme from its GitHub page, press the green _Code_ button in the upper right corner and choose _Download ZIP_. The process is identical on GitLab, but this site uses a button with a download symbol instead of a green button. You then simply unpack the file into `~/.emulationstation/themes/` and restart ES-DE.
For this example, we've downloaded the [alekfull-nx-retropie](https://github.com/anthonycaccese/alekfull-nx-retropie) and [es-theme-carbon](https://github.com/RetroPie/es-theme-carbon) themes and uncompressed them to the themes directory:
``` ```
~/.emulationstation/themes/alekfull-nx-retropie
~/.emulationstation/themes/es-theme-carbon ~/.emulationstation/themes/es-theme-carbon
~/.emulationstation/themes/es-theme-fundamental
``` ```
We now have four entries in the Theme set selector in the UI settings menu, i.e. _rbsimple-DE, modern-DE, es-theme-carbon_ and _es-theme-fundamental_. We now have four entries in the _Theme set_ selector in the UI settings menu, i.e. _alekfull-nx-retropie, es-theme-carbon, modern-DE_ and _rbsimple-DE_.
Although you place additional themes in your ES-DE home directory, the default rbsimple-DE and modern-DE themes are located in the installation folder. For example this could be `/usr/share/emulationstation/themes` or `/usr/local/share/emulationstation/themes` on Unix, `/Applications/EmulationStation Desktop Edition.app/Contents/Resources/themes` on macOS or `C:\Program Files\EmulationStation-DE\themes` on Windows. Although you should place additional themes in your ES-DE home directory, the default rbsimple-DE and modern-DE themes are located in the installation folder as they come bundled with the application. For example this could be `/usr/share/emulationstation/themes/` or `/usr/local/share/emulationstation/themes/` on Unix, `/Applications/EmulationStation Desktop Edition.app/Contents/Resources/themes/` on macOS or `C:\Program Files\EmulationStation-DE\themes\` on Windows. If using the portable ES-DE release on Windows, the themes folder will be located in the root of the EmulationStation-DE directory.
So if you would like to customize the rbsimple-DE or modern-DE theme sets, simply make a copy of their directories to ~/.emulationstation/themes and then those copies will take precedence over the ones in the application installation directory. Note that if using the AppImage release on Linux, then there is no installation folder as all files are contained inside the AppImage file.
Here is a good resource with a list of themes (although you will have to search online for the download location for each theme set): If you would like to customize the rbsimple-DE or modern-DE theme sets, simply make a copy of their directories to `~/.emulationstation/themes/` and then those copies will take precedence over the ones in the application installation directory.
https://retropie.org.uk/docs/Themes Refer to the official list of recommended theme sets for a selection of high-quality themes:
https://gitlab.com/es-de/themes/themes-list
![alt text](images/es-de_ui_theme_support.png "ES-DE Theme Support") ![alt text](images/es-de_ui_theme_support.png "ES-DE Theme Support")
_This is a screenshot of the modern-DE theme that is bundled with ES-DE (in addition to the default rbsimple-DE theme)._ _This is a screenshot of the modern-DE theme that is bundled with ES-DE (in addition to the default rbsimple-DE theme)._
@ -3025,7 +3035,7 @@ The **@** symbol indicates that the emulator is _deprecated_ and will be removed
| tanodragon | Tano Dragon | XRoar **(Standalone)** | | Yes | See the specific _Dragon 32 and Tano Dragon_ section elsewhere in this guide | | tanodragon | Tano Dragon | XRoar **(Standalone)** | | Yes | See the specific _Dragon 32 and Tano Dragon_ section elsewhere in this guide |
| tg16 | NEC TurboGrafx-16 | Beetle PCE | Beetle PCE FAST,<br>Mednafen **(Standalone)** [UMW*] | No | Single archive or ROM file in root folder | | tg16 | NEC TurboGrafx-16 | Beetle PCE | Beetle PCE FAST,<br>Mednafen **(Standalone)** [UMW*] | No | Single archive or ROM file in root folder |
| tg-cd | NEC TurboGrafx-CD | Beetle PCE | Beetle PCE FAST,<br>Mednafen **(Standalone)** [UMW*] | Yes | | | tg-cd | NEC TurboGrafx-CD | Beetle PCE | Beetle PCE FAST,<br>Mednafen **(Standalone)** [UMW*] | Yes | |
| ti99 | Texas Instruments TI-99 | MAME **(Standalone)** [UW*] | | Yes | See the specific _Texas Instruments TI-99_ section elsewhere in this guide | | ti99 | Texas Instruments TI-99 | MAME **(Standalone)** [UMW*] | | Yes | See the specific _Texas Instruments TI-99_ section elsewhere in this guide |
| tic80 | TIC-80 Game Engine | TIC-80 | | No | Single .tic file in root folder | | tic80 | TIC-80 Game Engine | TIC-80 | | No | Single .tic file in root folder |
| to8 | Thomson TO8 | Theodore | | | | | to8 | Thomson TO8 | Theodore | | | |
| trs-80 | Tandy TRS-80 | sdl2trs DOS Diskette **(Standalone)** [UW*] | sdl2trs Bootable Diskette **(Standalone)** [UW*],<br>sdl2trs CMD File **(Standalone)** [UW*] | Yes | See the specific _Tandy TRS-80_ section elsewhere in this guide | | trs-80 | Tandy TRS-80 | sdl2trs DOS Diskette **(Standalone)** [UW*] | sdl2trs Bootable Diskette **(Standalone)** [UW*],<br>sdl2trs CMD File **(Standalone)** [UW*] | Yes | See the specific _Tandy TRS-80_ section elsewhere in this guide |

View file

@ -490,8 +490,8 @@ bool SystemData::loadConfig()
std::string path; std::string path;
std::string themeFolder; std::string themeFolder;
name = system.child("name").text().get(); name = Utils::String::replace(system.child("name").text().get(), "\n", "");
fullname = system.child("fullname").text().get(); fullname = Utils::String::replace(system.child("fullname").text().get(), "\n", "");
sortName = system.child("systemsortname").text().get(); sortName = system.child("systemsortname").text().get();
path = system.child("path").text().get(); path = system.child("path").text().get();
@ -1303,7 +1303,9 @@ void SystemData::loadTheme()
} }
try { try {
// Build map with system variables for theme to use. // Build a map with system variables for the theme to use. Assign a backspace character
// to the variables that are not applicable. This will be used in ThemeData to make sure
// unpopulated system variables do not lead to theme loading errors.
std::map<std::string, std::string> sysData; std::map<std::string, std::string> sysData;
sysData.insert(std::pair<std::string, std::string>("system.name", getName())); sysData.insert(std::pair<std::string, std::string>("system.name", getName()));
sysData.insert(std::pair<std::string, std::string>("system.theme", getThemeFolder())); sysData.insert(std::pair<std::string, std::string>("system.theme", getThemeFolder()));
@ -1315,6 +1317,10 @@ void SystemData::loadTheme()
std::pair<std::string, std::string>("system.fullName.collections", getFullName())); std::pair<std::string, std::string>("system.fullName.collections", getFullName()));
sysData.insert( sysData.insert(
std::pair<std::string, std::string>("system.theme.collections", getThemeFolder())); std::pair<std::string, std::string>("system.theme.collections", getThemeFolder()));
sysData.insert(std::pair<std::string, std::string>("system.name.noCollections", "\b"));
sysData.insert(
std::pair<std::string, std::string>("system.fullName.noCollections", "\b"));
sysData.insert(std::pair<std::string, std::string>("system.theme.noCollections", "\b"));
} }
else { else {
sysData.insert( sysData.insert(
@ -1323,6 +1329,10 @@ void SystemData::loadTheme()
getFullName())); getFullName()));
sysData.insert(std::pair<std::string, std::string>("system.theme.noCollections", sysData.insert(std::pair<std::string, std::string>("system.theme.noCollections",
getThemeFolder())); getThemeFolder()));
sysData.insert(std::pair<std::string, std::string>("system.name.collections", "\b"));
sysData.insert(
std::pair<std::string, std::string>("system.fullName.collections", "\b"));
sysData.insert(std::pair<std::string, std::string>("system.theme.collections", "\b"));
} }
mTheme->loadFile(sysData, path); mTheme->loadFile(sysData, path);

View file

@ -55,8 +55,8 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
// Title. // Title.
mTitle = std::make_shared<TextComponent>( mTitle = std::make_shared<TextComponent>(
"LAUNCHING GAME", "LAUNCHING GAME",
Font::get(static_cast<int>( Font::get(titleFontSize *
titleFontSize * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()))), std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())),
0x666666FF, ALIGN_CENTER); 0x666666FF, ALIGN_CENTER);
mGrid->setEntry(mTitle, glm::ivec2 {1, 1}, false, true, glm::ivec2 {1, 1}); mGrid->setEntry(mTitle, glm::ivec2 {1, 1}, false, true, glm::ivec2 {1, 1});
@ -75,8 +75,8 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
// Game name. // Game name.
mGameName = std::make_shared<TextComponent>( mGameName = std::make_shared<TextComponent>(
"GAME NAME", "GAME NAME",
Font::get(static_cast<int>( Font::get(gameNameFontSize *
gameNameFontSize * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()))), std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())),
0x444444FF, ALIGN_CENTER); 0x444444FF, ALIGN_CENTER);
mGrid->setEntry(mGameName, glm::ivec2 {1, 5}, false, true, glm::ivec2 {1, 1}); mGrid->setEntry(mGameName, glm::ivec2 {1, 5}, false, true, glm::ivec2 {1, 1});
@ -108,11 +108,10 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
float maxWidth {Renderer::getScreenWidth() * maxWidthModifier}; float maxWidth {Renderer::getScreenWidth() * maxWidthModifier};
float minWidth {Renderer::getScreenWidth() * minWidthModifier}; float minWidth {Renderer::getScreenWidth() * minWidthModifier};
float fontWidth { float fontWidth {Font::get(gameNameFontSize *
Font::get(static_cast<int>(gameNameFontSize * std::min(Renderer::getScreenHeight(), std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()))
Renderer::getScreenWidth()))) ->sizeText(Utils::String::toUpper(game->getName()))
->sizeText(Utils::String::toUpper(game->getName())) .x};
.x};
// Add a bit of width to compensate for the left and right spacers. // Add a bit of width to compensate for the left and right spacers.
fontWidth += Renderer::getScreenWidth() * 0.05f; fontWidth += Renderer::getScreenWidth() * 0.05f;

View file

@ -296,7 +296,7 @@ void GuiMenu::openUIOptions()
it != SystemData::sSystemVector.cend(); ++it) { it != SystemData::sSystemVector.cend(); ++it) {
if ((*it)->getName() != "retropie") { 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.48f}; float maxNameLength {mSize.x * 0.51f};
startupSystem->add((*it)->getFullName(), (*it)->getName(), startupSystem->add((*it)->getFullName(), (*it)->getName(),
Settings::getInstance()->getString("StartupSystem") == Settings::getInstance()->getString("StartupSystem") ==
(*it)->getName(), (*it)->getName(),

View file

@ -240,7 +240,7 @@ void GuiScraperSearch::resizeMetadata()
{ {
mMD_Grid->setSize(mGrid.getColWidth(2), mGrid.getRowHeight(1)); mMD_Grid->setSize(mGrid.getColWidth(2), mGrid.getRowHeight(1));
if (mMD_Grid->getSize().y > mMD_Pairs.size()) { if (mMD_Grid->getSize().y > mMD_Pairs.size()) {
const int fontHeight {static_cast<int>(mMD_Grid->getSize().y / mMD_Pairs.size() * 0.8f)}; const float fontHeight {mMD_Grid->getSize().y / mMD_Pairs.size() * 0.8f};
auto fontLbl = Font::get(fontHeight, FONT_PATH_REGULAR); auto fontLbl = Font::get(fontHeight, FONT_PATH_REGULAR);
auto fontComp = Font::get(fontHeight, FONT_PATH_LIGHT); auto fontComp = Font::get(fontHeight, FONT_PATH_LIGHT);
@ -554,7 +554,7 @@ void GuiScraperSearch::updateInfoPane()
// Cache the thumbnail image in mScraperResults so that we don't need to download // Cache the thumbnail image in mScraperResults so that we don't need to download
// it every time the list is scrolled back and forth. // it every time the list is scrolled back and forth.
if (mScraperResults[i].thumbnailImageData.size() > 0) { if (mScraperResults[i].thumbnailImageData.size() > 350) {
std::string content {mScraperResults[i].thumbnailImageData}; std::string content {mScraperResults[i].thumbnailImageData};
mResultThumbnail->setImage(content.data(), content.length()); mResultThumbnail->setImage(content.data(), content.length());
mGrid.onSizeChanged(); // A hack to fix the thumbnail position since its size changed. mGrid.onSizeChanged(); // A hack to fix the thumbnail position since its size changed.
@ -837,7 +837,7 @@ void GuiScraperSearch::updateThumbnail()
} }
// Activate the thumbnail in the GUI. // Activate the thumbnail in the GUI.
std::string content {mScraperResults[mResultList->getCursorId()].thumbnailImageData}; std::string content {mScraperResults[mResultList->getCursorId()].thumbnailImageData};
if (content.size() > 0) { if (content.size() > 350) {
mResultThumbnail->setImage(content.data(), content.length()); mResultThumbnail->setImage(content.data(), content.length());
mGrid.onSizeChanged(); // A hack to fix the thumbnail position since its size changed. mGrid.onSizeChanged(); // A hack to fix the thumbnail position since its size changed.
} }

View file

@ -150,7 +150,7 @@ void GuiScraperSingle::onSizeChanged()
mGrid.setColWidthPerc(1, 0.04f); mGrid.setColWidthPerc(1, 0.04f);
mGrid.setSize(glm::round(mSize)); mGrid.setSize(glm::round(mSize));
mBackground.fitTo(mSize, glm::vec3 {}, glm::vec2 {-32.0f, -32.0f}); mBackground.fitTo(mSize, glm::vec3 {0.0f, 0.0f, 0.0f}, glm::vec2 {-32.0f, -32.0f});
// Add some extra margins to the game name. // Add some extra margins to the game name.
const float newSizeX {mSize.x * 0.96f}; const float newSizeX {mSize.x * 0.96f};

View file

@ -1183,12 +1183,11 @@ void SystemView::legacyApplyTheme(const std::shared_ptr<ThemeData>& theme)
mPrimary->applyTheme(theme, "system", "textlist_gamelist", ThemeFlags::ALL); mPrimary->applyTheme(theme, "system", "textlist_gamelist", ThemeFlags::ALL);
mLegacySystemInfo->setSize(mSize.x, mLegacySystemInfo->getFont()->getLetterHeight() * 2.2f); mLegacySystemInfo->setSize(mSize.x, mLegacySystemInfo->getFont()->getLetterHeight() * 2.2f);
mLegacySystemInfo->setPosition(0.0f, std::round(mPrimary->getPosition().y) + mLegacySystemInfo->setPosition(0.0f,
std::round(mPrimary->getSize().y)); std::floor(mPrimary->getPosition().y) + mPrimary->getSize().y);
mLegacySystemInfo->setBackgroundColor(0xDDDDDDD8); mLegacySystemInfo->setBackgroundColor(0xDDDDDDD8);
mLegacySystemInfo->setRenderBackground(true); mLegacySystemInfo->setRenderBackground(true);
mLegacySystemInfo->setFont( mLegacySystemInfo->setFont(Font::get(0.035f * mSize.y, Font::getDefaultPath()));
Font::get(static_cast<int>(0.035f * mSize.y), Font::getDefaultPath()));
mLegacySystemInfo->setColor(0x000000FF); mLegacySystemInfo->setColor(0x000000FF);
mLegacySystemInfo->setUppercase(true); mLegacySystemInfo->setUppercase(true);
mLegacySystemInfo->setZIndex(50.0f); mLegacySystemInfo->setZIndex(50.0f);

View file

@ -160,6 +160,8 @@ public:
mComponentThemeFlags ^= ComponentThemeFlags::METADATA_ELEMENT; mComponentThemeFlags ^= ComponentThemeFlags::METADATA_ELEMENT;
} }
virtual int getTextCacheGlyphHeight() { return 0; }
// Returns the center point of the image (takes origin into account). // Returns the center point of the image (takes origin into account).
const glm::vec2 getCenter() const; const glm::vec2 getCenter() const;

View file

@ -24,7 +24,6 @@ HelpStyle::HelpStyle()
, opacity {1.0f} , opacity {1.0f}
, letterCase {"uppercase"} , letterCase {"uppercase"}
{ {
if (FONT_SIZE_SMALL != 0) if (FONT_SIZE_SMALL != 0)
font = Font::get(FONT_SIZE_SMALL); font = Font::get(FONT_SIZE_SMALL);
else else
@ -61,7 +60,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); font = Font::getFromTheme(elem, ThemeFlags::ALL, font, 0.0f, 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);

View file

@ -123,6 +123,7 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
{"audio", BOOLEAN}, {"audio", BOOLEAN},
{"interpolation", STRING}, {"interpolation", STRING},
{"pillarboxes", BOOLEAN}, {"pillarboxes", BOOLEAN},
{"pillarboxThreshold", NORMALIZED_PAIR},
{"scanlines", BOOLEAN}, {"scanlines", BOOLEAN},
{"delay", FLOAT}, {"delay", FLOAT},
{"fadeInTime", FLOAT}, {"fadeInTime", FLOAT},
@ -1204,6 +1205,14 @@ void ThemeData::parseElement(const pugi::xml_node& root,
std::string str {resolvePlaceholders(node.text().as_string())}; std::string str {resolvePlaceholders(node.text().as_string())};
// Handle the special case with mutually exclusive system variables, for example
// system.fullName.collections and system.fullName.noCollections which can never
// exist at the same time. A backspace is assigned in SystemData to flag the
// variables that do not apply and if it's encountered here we simply skip the
// property.
if (!mLegacyTheme && str == "\b")
continue;
// Skip this check for legacy themes to not break backward compatibility with some // Skip this check for legacy themes to not break backward compatibility with some
// themes sets that include empty property values. // themes sets that include empty property values.
if (!mLegacyTheme && str == "") if (!mLegacyTheme && str == "")

View file

@ -541,6 +541,9 @@ void Window::render()
} }
} }
if (!mRenderedHelpPrompts)
mHelp->render(trans);
if (!mRenderLaunchScreen) if (!mRenderLaunchScreen)
top->render(trans); top->render(trans);
} }
@ -568,9 +571,6 @@ void Window::render()
delete cache; delete cache;
} }
if (!mRenderedHelpPrompts)
mHelp->render(trans);
unsigned int screensaverTimer = unsigned int screensaverTimer =
static_cast<unsigned int>(Settings::getInstance()->getInt("ScreensaverTimer")); static_cast<unsigned int>(Settings::getInstance()->getInt("ScreensaverTimer"));
if (mTimeSinceLastInput >= screensaverTimer && screensaverTimer != 0) { if (mTimeSinceLastInput >= screensaverTimer && screensaverTimer != 0) {

View file

@ -134,7 +134,7 @@ void ComponentList::update(int deltaTime)
mLoopTime = 0; mLoopTime = 0;
} }
const float totalHeight = getTotalRowHeight(); const float totalHeight {getTotalRowHeight()};
// Scroll indicator logic, used by ScrollIndicatorComponent. // Scroll indicator logic, used by ScrollIndicatorComponent.
bool scrollIndicatorChanged = false; bool scrollIndicatorChanged = false;
@ -236,17 +236,17 @@ void ComponentList::onCursorChanged(const CursorState& state)
void ComponentList::updateCameraOffset() void ComponentList::updateCameraOffset()
{ {
float oldCameraOffset = mCameraOffset; float oldCameraOffset {mCameraOffset};
// Move the camera to scroll. // Move the camera to scroll.
const float totalHeight = getTotalRowHeight(); const float totalHeight {getTotalRowHeight()};
if (totalHeight > mSize.y) { if (totalHeight > mSize.y) {
float target = float target {mSelectorBarOffset + getRowHeight(mEntries.at(mCursor).data) / 2.0f -
mSelectorBarOffset + getRowHeight(mEntries.at(mCursor).data) / 2.0f - (mSize.y / 2.0f); (mSize.y / 2.0f)};
// Clamp the camera to prevent a fraction of a row from being displayed. // Clamp the camera to prevent a fraction of a row from being displayed.
mCameraOffset = 0.0f; mCameraOffset = 0.0f;
unsigned int i = 0; unsigned int i {0};
while (mCameraOffset < target && i < mEntries.size()) { while (mCameraOffset < target && i < mEntries.size()) {
mCameraOffset += getRowHeight(mEntries.at(i).data); mCameraOffset += getRowHeight(mEntries.at(i).data);
if (mCameraOffset > totalHeight - mSize.y) { if (mCameraOffset > totalHeight - mSize.y) {
@ -290,22 +290,22 @@ void ComponentList::render(const glm::mat4& parentTrans)
dim.x = (trans[0].x * dim.x + trans[3].x) - trans[3].x; dim.x = (trans[0].x * dim.x + trans[3].x) - trans[3].x;
dim.y = (trans[1].y * dim.y + trans[3].y) - trans[3].y; dim.y = (trans[1].y * dim.y + trans[3].y) - trans[3].y;
const int clipRectPosX {static_cast<int>(std::ceil(trans[3].x))}; const int clipRectPosX {static_cast<int>(std::floor(trans[3].x))};
const int clipRectPosY {static_cast<int>(std::ceil(trans[3].y))}; const int clipRectPosY {static_cast<int>(std::floor(trans[3].y))};
const int clipRectSizeX {static_cast<int>(std::ceil(dim.x))}; const int clipRectSizeX {static_cast<int>(std::round(dim.x))};
const int clipRectSizeY {static_cast<int>(std::ceil(dim.y))}; const int clipRectSizeY {static_cast<int>(std::ceil(dim.y) + 1.0f)};
mRenderer->pushClipRect(glm::ivec2 {clipRectPosX, clipRectPosY}, mRenderer->pushClipRect(glm::ivec2 {clipRectPosX, clipRectPosY},
glm::ivec2 {clipRectSizeX, clipRectSizeY}); glm::ivec2 {clipRectSizeX, clipRectSizeY});
// Scroll the camera. // Scroll the camera.
trans = glm::translate(trans, glm::vec3 {0.0f, std::round(-mCameraOffset), 0.0f}); trans = glm::translate(trans, glm::vec3 {0.0f, -mCameraOffset, 0.0f});
glm::mat4 loopTrans {trans}; glm::mat4 loopTrans {trans};
// Draw our entries. // Draw our entries.
std::vector<GuiComponent*> drawAfterCursor; std::vector<GuiComponent*> drawAfterCursor;
bool drawAll; bool drawAll {false};
for (size_t i = 0; i < mEntries.size(); ++i) { for (size_t i = 0; i < mEntries.size(); ++i) {
if (mLoopRows && mFocused && mLoopOffset > 0) { if (mLoopRows && mFocused && mLoopOffset > 0) {
@ -337,16 +337,16 @@ void ComponentList::render(const glm::mat4& parentTrans)
if (mFocused && i == static_cast<size_t>(mCursor) && if (mFocused && i == static_cast<size_t>(mCursor) &&
it->component->getValue() != "") { it->component->getValue() != "") {
// Check if we're dealing with text or an image component. // Check if we're dealing with text or an image component.
bool isTextComponent = true; bool isTextComponent {true};
unsigned int origColor = it->component->getColor(); unsigned int origColor {it->component->getColor()};
if (origColor == 0) { if (origColor == 0) {
origColor = it->component->getColorShift(); origColor = it->component->getColorShift();
isTextComponent = false; isTextComponent = false;
} }
// Check if the color is neutral. // Check if the color is neutral.
unsigned char byteRed = origColor >> 24 & 0xFF; unsigned char byteRed {static_cast<unsigned char>(origColor >> 24 & 0xFF)};
unsigned char byteGreen = origColor >> 16 & 0xFF; unsigned char byteGreen {static_cast<unsigned char>(origColor >> 16 & 0xFF)};
unsigned char byteBlue = origColor >> 8 & 0xFF; unsigned char byteBlue {static_cast<unsigned char>(origColor >> 8 & 0xFF)};
// If it's neutral, just proceed with normal rendering. // If it's neutral, just proceed with normal rendering.
if (byteRed == byteGreen && byteGreen == byteBlue) { if (byteRed == byteGreen && byteGreen == byteBlue) {
renderLoopFunc(); renderLoopFunc();
@ -379,17 +379,17 @@ void ComponentList::render(const glm::mat4& parentTrans)
// Draw selector bar. // Draw selector bar.
if (mFocused) { if (mFocused) {
const float selectedRowHeight = getRowHeight(mEntries.at(mCursor).data); const float selectedRowHeight {getRowHeight(mEntries.at(mCursor).data)};
if (mOpacity == 1.0f) { if (mOpacity == 1.0f) {
mRenderer->drawRect(0.0f, mSelectorBarOffset, std::round(mSize.x), selectedRowHeight, mRenderer->drawRect(0.0f, mSelectorBarOffset, mSize.x, selectedRowHeight, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, false, mOpacity, mDimming, 0xFFFFFFFF, false, mOpacity, mDimming,
Renderer::BlendFactor::ONE_MINUS_DST_COLOR, Renderer::BlendFactor::ONE_MINUS_DST_COLOR,
Renderer::BlendFactor::ZERO); Renderer::BlendFactor::ZERO);
mRenderer->drawRect(0.0f, mSelectorBarOffset, std::round(mSize.x), selectedRowHeight, mRenderer->drawRect(0.0f, mSelectorBarOffset, mSize.x, selectedRowHeight, 0x777777FF,
0x777777FF, 0x777777FF, false, mOpacity, mDimming, 0x777777FF, false, mOpacity, mDimming, Renderer::BlendFactor::ONE,
Renderer::BlendFactor::ONE, Renderer::BlendFactor::ONE); Renderer::BlendFactor::ONE);
} }
for (auto it = drawAfterCursor.cbegin(); it != drawAfterCursor.cend(); ++it) for (auto it = drawAfterCursor.cbegin(); it != drawAfterCursor.cend(); ++it)
@ -401,34 +401,34 @@ void ComponentList::render(const glm::mat4& parentTrans)
} }
// Draw separators. // Draw separators.
float y = 0; float y {0.0f};
for (unsigned int i = 0; i < mEntries.size(); ++i) { for (unsigned int i = 0; i < mEntries.size(); ++i) {
mRenderer->drawRect(0.0f, y, std::round(mSize.x), mRenderer->drawRect(0.0f, y, mSize.x, 1.0f * Renderer::getScreenHeightModifier(),
1.0f * Renderer::getScreenHeightModifier(), 0xC6C7C6FF, 0xC6C7C6FF, 0xC6C7C6FF, 0xC6C7C6FF, false, mOpacity, mDimming);
false, mOpacity, mDimming);
y += getRowHeight(mEntries.at(i).data); y += getRowHeight(mEntries.at(i).data);
} }
mRenderer->drawRect(0.0f, y, std::round(mSize.x), 1.0f * Renderer::getScreenHeightModifier(), mRenderer->drawRect(0.0f, y, mSize.x, 1.0f * Renderer::getScreenHeightModifier(), 0xC6C7C6FF,
0xC6C7C6FF, 0xC6C7C6FF, false, mOpacity, mDimming); 0xC6C7C6FF, false, mOpacity, mDimming);
mRenderer->popClipRect(); mRenderer->popClipRect();
} }
float ComponentList::getRowHeight(const ComponentListRow& row) const float ComponentList::getRowHeight(const ComponentListRow& row) const
{ {
// Returns the highest component height found in the row. // Returns the highest component height found in the row.
float height = 0; float height {0.0f};
for (unsigned int i = 0; i < row.elements.size(); ++i) { for (unsigned int i = 0; i < row.elements.size(); ++i) {
if (row.elements.at(i).component->getSize().y > height) if (row.elements.at(i).component->getSize().y > height)
height = row.elements.at(i).component->getSize().y; height = row.elements.at(i).component->getSize().y;
} }
// We round down to avoid separator single-pixel alignment issues.
return std::floor(height); return std::floor(height);
} }
float ComponentList::getTotalRowHeight() const float ComponentList::getTotalRowHeight() const
{ {
float height = 0; float height {0.0f};
for (auto it = mEntries.cbegin(); it != mEntries.cend(); ++it) for (auto it = mEntries.cbegin(); it != mEntries.cend(); ++it)
height += getRowHeight(it->data); height += getRowHeight(it->data);
@ -437,13 +437,13 @@ float ComponentList::getTotalRowHeight() const
void ComponentList::updateElementPosition(const ComponentListRow& row) void ComponentList::updateElementPosition(const ComponentListRow& row)
{ {
float yOffset = 0; float yOffset {0.0f};
for (auto it = mEntries.cbegin(); it != mEntries.cend() && &it->data != &row; ++it) for (auto it = mEntries.cbegin(); it != mEntries.cend() && &it->data != &row; ++it)
yOffset += getRowHeight(it->data); yOffset += getRowHeight(it->data);
// Assumes updateElementSize has already been called. // Assumes updateElementSize has already been called.
float rowHeight = getRowHeight(row); float rowHeight {getRowHeight(row)};
float x = mHorizontalPadding / 2.0f; float x {mHorizontalPadding / 2.0f};
for (unsigned int i = 0; i < row.elements.size(); ++i) { for (unsigned int i = 0; i < row.elements.size(); ++i) {
const auto comp = row.elements.at(i).component; const auto comp = row.elements.at(i).component;
@ -456,7 +456,7 @@ void ComponentList::updateElementPosition(const ComponentListRow& row)
void ComponentList::updateElementSize(const ComponentListRow& row) void ComponentList::updateElementSize(const ComponentListRow& row)
{ {
float width = mSize.x - mHorizontalPadding; float width {mSize.x - mHorizontalPadding};
std::vector<std::shared_ptr<GuiComponent>> resizeVec; std::vector<std::shared_ptr<GuiComponent>> resizeVec;
for (auto it = row.elements.cbegin(); it != row.elements.cend(); ++it) { for (auto it = row.elements.cbegin(); it != row.elements.cend(); ++it) {
@ -485,11 +485,11 @@ std::vector<HelpPrompt> ComponentList::getHelpPrompts()
if (!size()) if (!size())
return std::vector<HelpPrompt>(); return std::vector<HelpPrompt>();
std::vector<HelpPrompt> prompts = std::vector<HelpPrompt> prompts {
mEntries.at(mCursor).data.elements.back().component->getHelpPrompts(); mEntries.at(mCursor).data.elements.back().component->getHelpPrompts()};
if (size() > 1) { if (size() > 1) {
bool addMovePrompt = true; bool addMovePrompt {true};
for (auto it = prompts.cbegin(); it != prompts.cend(); ++it) { for (auto it = prompts.cbegin(); it != prompts.cend(); ++it) {
if (it->first == "up/down" || it->first == "up/down/left/right") { if (it->first == "up/down" || it->first == "up/down/left/right") {
addMovePrompt = false; addMovePrompt = false;
@ -505,7 +505,7 @@ std::vector<HelpPrompt> ComponentList::getHelpPrompts()
bool ComponentList::moveCursor(int amt) bool ComponentList::moveCursor(int amt)
{ {
bool ret = listInput(amt); bool ret {listInput(amt)};
listInput(0); listInput(0);
return ret; return ret;
} }

View file

@ -209,6 +209,14 @@ void DateTimeComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
} }
} }
float maxHeight {0.0f};
if (!theme->isLegacyTheme() && elem->has("size")) {
const glm::vec2 size {elem->get<glm::vec2>("size")};
if (size.x != 0.0f && size.y != 0.0f)
maxHeight = mSize.y * 2.0f;
}
// Legacy themes only. // Legacy themes only.
if (properties & FORCE_UPPERCASE && elem->has("forceUppercase")) if (properties & FORCE_UPPERCASE && elem->has("forceUppercase"))
setUppercase(elem->get<bool>("forceUppercase")); setUppercase(elem->get<bool>("forceUppercase"));
@ -216,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)); setFont(Font::getFromTheme(elem, properties, mFont, maxHeight, theme->isLegacyTheme()));
} }

View file

@ -211,7 +211,11 @@ void HelpComponent::updateGrid()
std::vector<std::shared_ptr<TextComponent>> labels; std::vector<std::shared_ptr<TextComponent>> labels;
float width {0.0f}; float width {0.0f};
const float height {std::round(font->getLetterHeight() * 1.25f)}; float height {std::round(font->getLetterHeight() * 1.25f)};
// Make sure both text and icons have either odd or equal sizes to avoid alignment issues.
if (static_cast<int>(font->getHeight()) % 2 != static_cast<int>(height) % 2)
--height;
// State variable indicating whether the GUI is dimmed. // State variable indicating whether the GUI is dimmed.
bool isDimmed {mWindow->isBackgroundDimmed()}; bool isDimmed {mWindow->isBackgroundDimmed()};
@ -244,8 +248,8 @@ void HelpComponent::updateGrid()
mGrid->setSize(width, height); mGrid->setSize(width, height);
for (unsigned int i = 0; i < icons.size(); ++i) { for (int i = 0; i < static_cast<int>(icons.size()); ++i) {
const int col = i * 4; const int col {i * 4};
mGrid->setColWidthPerc(col, icons.at(i)->getSize().x / width); mGrid->setColWidthPerc(col, icons.at(i)->getSize().x / width);
mGrid->setColWidthPerc(col + 1, mGrid->setColWidthPerc(col + 1,
(mStyle.iconTextSpacing * mRenderer->getScreenWidth()) / width); (mStyle.iconTextSpacing * mRenderer->getScreenWidth()) / width);
@ -256,7 +260,6 @@ void HelpComponent::updateGrid()
} }
mGrid->setPosition({mStyle.position.x, mStyle.position.y, 0.0f}); mGrid->setPosition({mStyle.position.x, mStyle.position.y, 0.0f});
mGrid->setOrigin(mStyle.origin); mGrid->setOrigin(mStyle.origin);
} }

View file

@ -11,9 +11,8 @@
#include "Settings.h" #include "Settings.h"
#include "components/ButtonComponent.h" #include "components/ButtonComponent.h"
#define BUTTON_GRID_VERT_PADDING std::round(Font::get(FONT_SIZE_MEDIUM)->getLetterHeight() * 0.915f) #define BUTTON_GRID_VERT_PADDING Font::get(FONT_SIZE_MEDIUM)->getLetterHeight() * 0.915f
#define BUTTON_GRID_HORIZ_PADDING \ #define BUTTON_GRID_HORIZ_PADDING Font::get(FONT_SIZE_MEDIUM)->getLetterHeight() * 0.283f
std::round(Font::get(FONT_SIZE_MEDIUM)->getLetterHeight() * 0.283f)
#define TITLE_HEIGHT (mTitle->getFont()->getLetterHeight() + Renderer::getScreenHeight() * 0.0637f) #define TITLE_HEIGHT (mTitle->getFont()->getLetterHeight() + Renderer::getScreenHeight() * 0.0637f)
@ -86,7 +85,7 @@ void MenuComponent::setTitle(std::string title, const std::shared_ptr<Font>& fon
float MenuComponent::getButtonGridHeight() const float MenuComponent::getButtonGridHeight() const
{ {
return (mButtonGrid ? mButtonGrid->getSize().y : return (mButtonGrid ? mButtonGrid->getSize().y :
Font::get(FONT_SIZE_MEDIUM)->getHeight() + BUTTON_GRID_VERT_PADDING); Font::get(FONT_SIZE_MEDIUM)->getSize() * 1.5f + BUTTON_GRID_VERT_PADDING);
} }
void MenuComponent::updateSize() void MenuComponent::updateSize()
@ -114,7 +113,7 @@ void MenuComponent::updateSize()
void MenuComponent::onSizeChanged() void MenuComponent::onSizeChanged()
{ {
mBackground.fitTo(mSize, glm::vec3 {}, glm::vec2 {-32.0f, -32.0f}); mBackground.fitTo(mSize, glm::vec3 {0.0f, 0.0f, 0.0f}, glm::vec2 {-32.0f, -32.0f});
// Update grid row/column sizes. // Update grid row/column sizes.
mGrid.setRowHeightPerc(0, TITLE_HEIGHT / mSize.y / 2.0f); mGrid.setRowHeightPerc(0, TITLE_HEIGHT / mSize.y / 2.0f);
@ -132,7 +131,7 @@ void MenuComponent::onSizeChanged()
mTitle->setSize(titleSize.x - indicatorsSize, titleSize.y); mTitle->setSize(titleSize.x - indicatorsSize, titleSize.y);
glm::vec3 titlePos {mTitle->getPosition()}; glm::vec3 titlePos {mTitle->getPosition()};
mTitle->setPosition(titlePos.x + std::round(indicatorsSize / 2.0f), titlePos.y, titlePos.z); mTitle->setPosition(titlePos.x + indicatorsSize / 2.0f, titlePos.y, titlePos.z);
} }
void MenuComponent::addButton(const std::string& name, void MenuComponent::addButton(const std::string& name,
@ -186,7 +185,7 @@ std::shared_ptr<ComponentGrid> makeButtonGrid(
std::shared_ptr<ImageComponent> makeArrow() std::shared_ptr<ImageComponent> makeArrow()
{ {
auto bracket = std::make_shared<ImageComponent>(); auto bracket = std::make_shared<ImageComponent>();
bracket->setResize(0, std::round(Font::get(FONT_SIZE_MEDIUM)->getLetterHeight())); bracket->setResize(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight());
bracket->setImage(":/graphics/arrow.svg"); bracket->setImage(":/graphics/arrow.svg");
return bracket; return bracket;
} }

View file

@ -60,9 +60,8 @@ void NinePatchComponent::buildVertices()
else { else {
// Scale the corner size relative to the screen resolution (using the medium sized // Scale the corner size relative to the screen resolution (using the medium sized
// default font as size reference). // default font as size reference).
relCornerSize = relCornerSize = mCornerSize * (Font::get(FONT_SIZE_MEDIUM)->getLetterHeight() *
glm::round(mCornerSize * (Font::get(FONT_SIZE_MEDIUM)->getLetterHeight() * (mSharpCorners == true ? 0.0568f : 0.09f) / 2.0f);
(mSharpCorners == true ? 0.0568f : 0.09f) / 2.0f));
} }
glm::vec2 texSize {relCornerSize * 3.0f}; glm::vec2 texSize {relCornerSize * 3.0f};

View file

@ -86,11 +86,11 @@ public:
mText.getFont()->getHeight()); mText.getFont()->getHeight());
// Position. // Position.
mLeftArrow.setPosition(0.0f, std::round((mSize.y - mLeftArrow.getSize().y) / 2.0f)); mLeftArrow.setPosition(0.0f, (mSize.y - mLeftArrow.getSize().y) / 2.0f);
mText.setPosition(mLeftArrow.getPosition().x + mLeftArrow.getSize().x, mText.setPosition(mLeftArrow.getPosition().x + mLeftArrow.getSize().x,
(mSize.y - mText.getSize().y) / 2.0f); (mSize.y - mText.getSize().y) / 2.0f);
mRightArrow.setPosition(mText.getPosition().x + mText.getSize().x, mRightArrow.setPosition(mText.getPosition().x + mText.getSize().x,
std::round((mSize.y - mRightArrow.getSize().y) / 2.0f)); (mSize.y - mRightArrow.getSize().y) / 2.0f);
} }
bool input(InputConfig* config, Input input) override bool input(InputConfig* config, Input input) override
@ -327,7 +327,7 @@ private:
mText.setText(ss.str()); mText.setText(ss.str());
mText.setSize(0, mText.getSize().y); mText.setSize(0, mText.getSize().y);
setSize(mText.getSize().x + mRightArrow.getSize().x + setSize(mText.getSize().x + mRightArrow.getSize().x +
std::round(Font::get(FONT_SIZE_MEDIUM)->getLetterHeight() * 0.68f), Font::get(FONT_SIZE_MEDIUM)->getLetterHeight() * 0.68f,
mText.getSize().y); mText.getSize().y);
if (mParent) // Hack since there's no "on child size changed" callback. if (mParent) // Hack since there's no "on child size changed" callback.
mParent->onSizeChanged(); mParent->onSizeChanged();
@ -342,26 +342,8 @@ private:
// A maximum length parameter has been passed and the "name" size surpasses // A maximum length parameter has been passed and the "name" size surpasses
// this value, so abbreviate the string inside the arrows. // this value, so abbreviate the string inside the arrows.
auto font = Font::get(FONT_SIZE_MEDIUM); auto font = Font::get(FONT_SIZE_MEDIUM);
// Calculate with an extra dot to give some leeway. mText.setText(Utils::String::toUpper(
float dotsSize {font->sizeText("....").x}; font->wrapText(Utils::String::toUpper(it->name), it->maxNameLength)));
std::string abbreviatedString {font->getTextMaxWidth(
Utils::String::toUpper(it->name), it->maxNameLength)};
float sizeDifference {font->sizeText(Utils::String::toUpper(it->name)).x -
font->sizeText(abbreviatedString).x};
if (sizeDifference > 0.0f) {
// It doesn't make sense to abbreviate if the number of pixels removed
// by the abbreviation is less or equal to the size of the three dots
// that would be appended to the string.
if (sizeDifference <= dotsSize) {
abbreviatedString = it->name;
}
else {
if (abbreviatedString.back() == ' ')
abbreviatedString.pop_back();
abbreviatedString += "...";
}
}
mText.setText(Utils::String::toUpper(abbreviatedString));
} }
else { else {
mText.setText(Utils::String::toUpper(it->name)); mText.setText(Utils::String::toUpper(it->name));
@ -369,7 +351,7 @@ private:
mText.setSize(0.0f, mText.getSize().y); mText.setSize(0.0f, mText.getSize().y);
setSize(mText.getSize().x + mLeftArrow.getSize().x + mRightArrow.getSize().x + setSize(mText.getSize().x + mLeftArrow.getSize().x + mRightArrow.getSize().x +
std::round(Font::get(FONT_SIZE_MEDIUM)->getLetterHeight() * 0.68f), Font::get(FONT_SIZE_MEDIUM)->getLetterHeight() * 0.68f,
mText.getSize().y); mText.getSize().y);
if (mParent) // Hack since there's no "on child size changed" callback. if (mParent) // Hack since there's no "on child size changed" callback.
mParent->onSizeChanged(); mParent->onSizeChanged();

View file

@ -66,10 +66,15 @@ void ScrollableContainer::reset()
mAutoScrollResetAccumulator = 0; mAutoScrollResetAccumulator = 0;
mAutoScrollAccumulator = -mAutoScrollDelay + mAutoScrollSpeed; mAutoScrollAccumulator = -mAutoScrollDelay + mAutoScrollSpeed;
mAtEnd = false; mAtEnd = false;
// This is needed to resize to the designated area when the backgrund image gets invalidated. // This is needed to resize to the designated area when the background image gets invalidated.
if (!mChildren.empty()) { if (!mChildren.empty()) {
float combinedHeight { float combinedHeight {0.0f};
mChildren.front()->getFont()->getHeight(mChildren.front()->getLineSpacing())}; const float cacheGlyphHeight {
static_cast<float>(mChildren.front()->getTextCacheGlyphHeight())};
if (cacheGlyphHeight > 0.0f)
combinedHeight = cacheGlyphHeight * mChildren.front()->getLineSpacing();
else
return;
if (mChildren.front()->getSize().y > mSize.y) { if (mChildren.front()->getSize().y > mSize.y) {
if (mVerticalSnap) { if (mVerticalSnap) {
float numLines {std::floor(mSize.y / combinedHeight)}; float numLines {std::floor(mSize.y / combinedHeight)};
@ -118,11 +123,16 @@ void ScrollableContainer::update(int deltaTime)
if (!isVisible() || mSize == glm::vec2 {0.0f, 0.0f}) if (!isVisible() || mSize == glm::vec2 {0.0f, 0.0f})
return; return;
const glm::vec2 contentSize {mChildren.front()->getSize()}; const glm::vec2 contentSize {glm::round(mChildren.front()->getSize())};
float rowModifier {1.0f}; float rowModifier {1.0f};
float lineSpacing {mChildren.front()->getLineSpacing()}; const float lineSpacing {mChildren.front()->getLineSpacing()};
float combinedHeight {mChildren.front()->getFont()->getHeight(lineSpacing)}; float combinedHeight {0.0f};
const float cacheGlyphHeight {static_cast<float>(mChildren.front()->getTextCacheGlyphHeight())};
if (cacheGlyphHeight > 0.0f)
combinedHeight = cacheGlyphHeight * lineSpacing;
else
return;
// Calculate the spacing which will be used to clip the container. // Calculate the spacing which will be used to clip the container.
if (lineSpacing > 1.2f && mClipSpacing == 0.0f) { if (lineSpacing > 1.2f && mClipSpacing == 0.0f) {
@ -175,7 +185,7 @@ void ScrollableContainer::update(int deltaTime)
mAutoScrollAccumulator += deltaTime; mAutoScrollAccumulator += deltaTime;
while (mAutoScrollAccumulator >= while (mAutoScrollAccumulator >=
static_cast<int>(rowModifier * static_cast<float>(mAdjustedAutoScrollSpeed))) { static_cast<int>(rowModifier * static_cast<float>(mAdjustedAutoScrollSpeed))) {
if (contentSize.y > mAdjustedHeight) if (!mAtEnd && contentSize.y > mAdjustedHeight)
mScrollPos += mScrollDir; mScrollPos += mScrollDir;
mAutoScrollAccumulator -= mAutoScrollAccumulator -=
static_cast<int>(rowModifier * static_cast<float>(mAdjustedAutoScrollSpeed)); static_cast<int>(rowModifier * static_cast<float>(mAdjustedAutoScrollSpeed));
@ -188,18 +198,15 @@ void ScrollableContainer::update(int deltaTime)
if (mScrollPos.y < 0.0f) if (mScrollPos.y < 0.0f)
mScrollPos.y = 0.0f; mScrollPos.y = 0.0f;
if (mScrollPos.x + mSize.x > contentSize.x) { if (mScrollPos.x + std::round(mSize.x) > contentSize.x) {
mScrollPos.x = contentSize.x - mSize.x; mScrollPos.x = contentSize.x - std::round(mSize.x);
mAtEnd = true; mAtEnd = true;
} }
if (contentSize.y < mAdjustedHeight) { if (contentSize.y < mAdjustedHeight)
mScrollPos.y = 0.0f; mScrollPos.y = 0.0f;
} else if (mScrollPos.y + mAdjustedHeight > contentSize.y)
else if (mScrollPos.y + mAdjustedHeight > contentSize.y) {
mScrollPos.y = contentSize.y - mAdjustedHeight;
mAtEnd = true; mAtEnd = true;
}
if (mAtEnd) { if (mAtEnd) {
mAutoScrollResetAccumulator += deltaTime; mAutoScrollResetAccumulator += deltaTime;
@ -237,8 +244,8 @@ void ScrollableContainer::render(const glm::mat4& parentTrans)
dimScaled.x = std::fabs(trans[3].x + mSize.x); dimScaled.x = std::fabs(trans[3].x + mSize.x);
dimScaled.y = std::fabs(trans[3].y + mAdjustedHeight); dimScaled.y = std::fabs(trans[3].y + mAdjustedHeight);
glm::ivec2 clipDim {static_cast<int>(ceilf(dimScaled.x - trans[3].x)), glm::ivec2 clipDim {static_cast<int>(dimScaled.x - trans[3].x),
static_cast<int>(ceilf(dimScaled.y - trans[3].y))}; static_cast<int>(dimScaled.y - trans[3].y)};
// By effectively clipping the upper and lower boundaries of the container we mostly avoid // By effectively clipping the upper and lower boundaries of the container we mostly avoid
// scrolling outside the vertical starting and ending positions. // scrolling outside the vertical starting and ending positions.
@ -247,7 +254,7 @@ void ScrollableContainer::render(const glm::mat4& parentTrans)
mRenderer->pushClipRect(clipPos, clipDim); mRenderer->pushClipRect(clipPos, clipDim);
trans = glm::translate(trans, glm::round(-glm::vec3 {mScrollPos.x, mScrollPos.y, 0.0f})); trans = glm::translate(trans, -glm::vec3 {mScrollPos.x, mScrollPos.y, 0.0f});
mRenderer->setMatrix(trans); mRenderer->setMatrix(trans);
if (Settings::getInstance()->getBool("DebugText")) if (Settings::getInstance()->getBool("DebugText"))

View file

@ -97,11 +97,8 @@ void SliderComponent::render(const glm::mat4& parentTrans)
if (mTextCache) if (mTextCache)
mFont->renderTextCache(mTextCache.get()); mFont->renderTextCache(mTextCache.get());
const float barPosY {mBarHeight == 1.0f ? std::floor(mSize.y / 2.0f - mBarHeight / 2.0f) :
std::round(mSize.y / 2.0f - mBarHeight / 2.0f)};
// Render bar. // Render bar.
mRenderer->drawRect(mKnob.getSize().x / 2.0f, barPosY, width, mBarHeight, 0x777777FF, mRenderer->drawRect(mKnob.getSize().x / 2.0f, mSize.y / 2.0f, width, mBarHeight, 0x777777FF,
0x777777FF); 0x777777FF);
// Render knob. // Render knob.
@ -124,7 +121,7 @@ void SliderComponent::setValue(float value)
void SliderComponent::onSizeChanged() void SliderComponent::onSizeChanged()
{ {
if (!mSuffix.empty()) if (!mSuffix.empty())
mFont = Font::get(static_cast<int>(mSize.y), FONT_PATH_LIGHT); mFont = Font::get(mSize.y, FONT_PATH_LIGHT);
onValueChanged(); onValueChanged();
} }
@ -154,7 +151,7 @@ void SliderComponent::onValueChanged()
mTextCache->metrics.size.x = textSize.x; // Fudge the width. mTextCache->metrics.size.x = textSize.x; // Fudge the width.
} }
mKnob.setResize(0.0f, std::round(mSize.y * 0.7f)); mKnob.setResize(0.0f, mSize.y * 0.7f);
float barLength { float barLength {
mSize.x - mKnob.getSize().x - mSize.x - mKnob.getSize().x -
@ -175,11 +172,11 @@ void SliderComponent::onValueChanged()
barHeight = 1; barHeight = 1;
// Resize the knob one pixel if necessary to keep the bar centered. // Resize the knob one pixel if necessary to keep the bar centered.
if (barHeight % 2 == 0 && static_cast<int>(mKnob.getSize().y) % 2 != 0) { if (barHeight % 2 == 0 && static_cast<int>(std::round(mKnob.getSize().y)) % 2 != 0) {
mKnob.setResize(mKnob.getSize().x - 1.0f, mKnob.getSize().y - 1.0f); mKnob.setResize(mKnob.getSize().x - 1.0f, mKnob.getSize().y - 1.0f);
setSize(getSize().x, getSize().y - 1.0f); setSize(getSize().x, getSize().y - 1.0f);
} }
else if (barHeight == 1 && static_cast<int>(mKnob.getSize().y) % 2 == 0) { else if (barHeight == 1 && static_cast<int>(std::round(mKnob.getSize().y)) % 2 == 0) {
mKnob.setResize(mKnob.getSize().x - 1.0f, mKnob.getSize().y - 1); mKnob.setResize(mKnob.getSize().x - 1.0f, mKnob.getSize().y - 1);
setSize(getSize().x, getSize().y - 1.0f); setSize(getSize().x, getSize().y - 1.0f);
} }

View file

@ -30,6 +30,7 @@ TextComponent::TextComponent()
, mNoTopMargin {false} , mNoTopMargin {false}
, mSelectable {false} , mSelectable {false}
, mVerticalAutoSizing {false} , mVerticalAutoSizing {false}
, mLegacyTheme {false}
{ {
} }
@ -58,6 +59,7 @@ TextComponent::TextComponent(const std::string& text,
, mNoTopMargin {false} , mNoTopMargin {false}
, mSelectable {false} , mSelectable {false}
, mVerticalAutoSizing {false} , mVerticalAutoSizing {false}
, mLegacyTheme {false}
{ {
setFont(font); setFont(font);
setColor(color); setColor(color);
@ -69,7 +71,7 @@ TextComponent::TextComponent(const std::string& text,
void TextComponent::onSizeChanged() void TextComponent::onSizeChanged()
{ {
mAutoCalcExtent = glm::ivec2 {(getSize().x == 0), (getSize().y == 0)}; mAutoCalcExtent = glm::ivec2 {getSize().x == 0, getSize().y == 0};
onTextChanged(); onTextChanged();
} }
@ -180,11 +182,11 @@ void TextComponent::render(const glm::mat4& parentTrans)
break; break;
} }
case ALIGN_BOTTOM: { case ALIGN_BOTTOM: {
yOff = (getSize().y - textSize.y); yOff = mSize.y - textSize.y;
break; break;
} }
case ALIGN_CENTER: { case ALIGN_CENTER: {
yOff = (getSize().y - textSize.y) / 2.0f; yOff = (mSize.y - textSize.y) / 2.0f;
break; break;
} }
default: { default: {
@ -194,7 +196,7 @@ void TextComponent::render(const glm::mat4& parentTrans)
} }
else { else {
// If height is smaller than the font height, then always center vertically. // If height is smaller than the font height, then always center vertically.
yOff = std::round((getSize().y - textSize.y) / 2.0f); yOff = (mSize.y - textSize.y) / 2.0f;
} }
// Draw the overall textbox area. If we're inside a scrollable container then this // Draw the overall textbox area. If we're inside a scrollable container then this
@ -236,110 +238,69 @@ void TextComponent::render(const glm::mat4& parentTrans)
} }
} }
void TextComponent::calculateExtent()
{
if (mAutoCalcExtent.x) {
if (mUppercase)
mSize = mFont->sizeText(Utils::String::toUpper(mText), mLineSpacing);
else if (mLowercase)
mSize = mFont->sizeText(Utils::String::toLower(mText), mLineSpacing);
else if (mCapitalize)
mSize = mFont->sizeText(Utils::String::toCapitalized(mText), mLineSpacing);
else
mSize = mFont->sizeText(mText, mLineSpacing); // Original case.
}
else {
if (mAutoCalcExtent.y) {
if (mUppercase) {
mSize.y =
mFont->sizeWrappedText(Utils::String::toUpper(mText), getSize().x, mLineSpacing)
.y;
}
else if (mLowercase) {
mSize.y =
mFont->sizeWrappedText(Utils::String::toLower(mText), getSize().x, mLineSpacing)
.y;
}
else if (mCapitalize) {
mSize.y = mFont
->sizeWrappedText(Utils::String::toCapitalized(mText), getSize().x,
mLineSpacing)
.y;
}
else {
mSize.y = mFont->sizeWrappedText(mText, getSize().x, mLineSpacing).y;
}
}
}
}
void TextComponent::onTextChanged() void TextComponent::onTextChanged()
{ {
if (!mVerticalAutoSizing) if (!mVerticalAutoSizing)
mVerticalAutoSizing = (mSize.x != 0.0f && mSize.y == 0.0f); mVerticalAutoSizing = (mSize.x != 0.0f && mSize.y == 0.0f);
calculateExtent();
if (!mFont || mText.empty() || mSize.x == 0.0f || mSize.y == 0.0f) {
mTextCache.reset();
return;
}
std::string text; std::string text;
if (mUppercase) if (mText != "") {
text = Utils::String::toUpper(mText); if (mUppercase)
else if (mLowercase) text = Utils::String::toUpper(mText);
text = Utils::String::toLower(mText); else if (mLowercase)
else if (mCapitalize) text = Utils::String::toLower(mText);
text = Utils::String::toCapitalized(mText); else if (mCapitalize)
else text = Utils::String::toCapitalized(mText);
text = mText; // Original case. else
text = mText; // Original case.
}
std::shared_ptr<Font> f {mFont}; if (mFont && mAutoCalcExtent.x) {
const float lineHeight {f->getHeight(mLineSpacing)}; mSize = mFont->sizeText(text, mLineSpacing);
const bool isMultiline {mSize.y > lineHeight}; // This can happen under special circumstances like when a blank/dummy font is used.
if (mSize.x == 0.0f)
return;
}
if (!mFont || text.empty() || mSize.x < 0.0f)
return;
float lineHeight {0.0f};
const bool isScrollable {mParent && mParent->isScrollable()}; const bool isScrollable {mParent && mParent->isScrollable()};
std::shared_ptr<Font> font {mFont};
bool addAbbrev {false}; if (mLegacyTheme && !isScrollable && (mVerticalAutoSizing || mAutoCalcExtent.x)) {
if (!isMultiline) { // This is needed to retain a bug from the legacy theme engine where lineSpacing
size_t newline {text.find('\n')}; // is not sized correctly when using automatic text element sizing. This is only
// Single line of text - stop at the first newline since it'll mess everything up. // applied to legacy themes for backward compatibility reasons.
text = text.substr(0, newline); font->useLegacyMaxGlyphHeight();
addAbbrev = newline != std::string::npos; lineHeight = font->getHeight(mLineSpacing);
}
glm::vec2 size {f->sizeText(text)};
if (!isMultiline && text.size() && (size.x > mSize.x || addAbbrev)) {
// Abbreviate text.
const std::string abbrev {"..."};
float abbrevSize {f->sizeText(abbrev).x};
while (text.size() && size.x + abbrevSize > mSize.x) {
size_t newSize {Utils::String::prevCursor(text, text.size())};
text.erase(newSize, text.size() - newSize);
if (!text.empty() && text.back() == ' ')
text.pop_back();
size = f->sizeText(text);
}
text.append(abbrev);
mTextCache = std::shared_ptr<TextCache>(f->buildTextCache(
text, glm::vec2 {}, mColor, mSize.x, mHorizontalAlignment, mLineSpacing, mNoTopMargin));
}
else if (isMultiline && text.size() && !isScrollable) {
const std::string wrappedText {f->wrapText(
text, mSize.x, (mVerticalAutoSizing ? 0.0f : mSize.y - lineHeight), mLineSpacing)};
mTextCache = std::shared_ptr<TextCache>(f->buildTextCache(wrappedText, glm::vec2 {}, mColor,
mSize.x, mHorizontalAlignment,
mLineSpacing, mNoTopMargin));
} }
else { else {
mTextCache = std::shared_ptr<TextCache>( // Used to initialize all glyphs, which is needed to populate mMaxGlyphHeight.
f->buildTextCache(f->wrapText(text, mSize.x), glm::vec2 {0.0f, 0.0f}, mColor, mSize.x, lineHeight = mFont->loadGlyphs(text + "\n") * mLineSpacing;
mHorizontalAlignment, mLineSpacing, mNoTopMargin));
} }
const bool isMultiline {mAutoCalcExtent.y == 1 || mSize.y > lineHeight};
if (isMultiline && !isScrollable) {
const std::string wrappedText {
font->wrapText(text, mSize.x, (mVerticalAutoSizing ? 0.0f : mSize.y - lineHeight),
mLineSpacing, isMultiline)};
mTextCache = std::shared_ptr<TextCache>(
font->buildTextCache(wrappedText, glm::vec2 {0.0f, 0.0f}, mColor, mSize.x,
mHorizontalAlignment, mLineSpacing, mNoTopMargin));
}
else {
mTextCache = std::shared_ptr<TextCache>(font->buildTextCache(
font->wrapText(text, mSize.x, 0.0f, mLineSpacing, isMultiline), glm::vec2 {0.0f, 0.0f},
mColor, mSize.x, mHorizontalAlignment, mLineSpacing, mNoTopMargin));
}
if (mAutoCalcExtent.y)
mSize.y = mTextCache->metrics.size.y;
if (mOpacity != 1.0f || mThemeOpacity != 1.0f) if (mOpacity != 1.0f || mThemeOpacity != 1.0f)
setOpacity(mOpacity); setOpacity(mOpacity);
@ -387,6 +348,8 @@ void TextComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
using namespace ThemeFlags; using namespace ThemeFlags;
GuiComponent::applyTheme(theme, view, element, properties); GuiComponent::applyTheme(theme, view, element, properties);
mLegacyTheme = theme->isLegacyTheme();
std::string elementType {"text"}; std::string elementType {"text"};
std::string componentName {"TextComponent"}; std::string componentName {"TextComponent"};
@ -395,7 +358,7 @@ void TextComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
componentName = "gamelistInfoComponent"; componentName = "gamelistInfoComponent";
} }
const ThemeData::ThemeElement* elem = theme->getElement(view, element, elementType); const ThemeData::ThemeElement* elem {theme->getElement(view, element, elementType)};
if (!elem) if (!elem)
return; return;
@ -511,6 +474,14 @@ void TextComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
} }
} }
float maxHeight {0.0f};
if (!theme->isLegacyTheme() && elem->has("size")) {
const glm::vec2 size {elem->get<glm::vec2>("size")};
if (size.x != 0.0f && size.y != 0.0f)
maxHeight = mSize.y * 2.0f;
}
// Legacy themes only. // Legacy themes only.
if (properties & FORCE_UPPERCASE && elem->has("forceUppercase")) if (properties & FORCE_UPPERCASE && elem->has("forceUppercase"))
setUppercase(elem->get<bool>("forceUppercase")); setUppercase(elem->get<bool>("forceUppercase"));
@ -518,5 +489,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)); setFont(Font::getFromTheme(elem, properties, mFont, maxHeight, theme->isLegacyTheme()));
} }

View file

@ -14,13 +14,10 @@
class ThemeData; class ThemeData;
// Used to display text. // TextComponent sizing works in the following ways:
// TextComponent::setSize(x, y) works a little differently than most components: // setSize(0.0f, 0.0f) - Automatically sizes single-line text by expanding horizontally.
// * (0, 0) - Will automatically calculate a size that fits // setSize(width, 0.0f) - Limits size horizontally and automatically expands vertically.
// the text on one line (expand horizontally). // setSize(width, height) - Wraps and abbreviates text inside the width and height boundaries.
// * (x != 0, 0) - Wrap text so that it does not reach beyond x. Will
// automatically calculate a vertical size (expand vertically).
// * (x != 0, y <= fontHeight) - Will truncate text so it fits within this box.
class TextComponent : public GuiComponent class TextComponent : public GuiComponent
{ {
public: public:
@ -81,6 +78,11 @@ public:
Alignment getHorizontalAlignment() { return mHorizontalAlignment; } Alignment getHorizontalAlignment() { return mHorizontalAlignment; }
Alignment getVerticalAlignment() { return mVerticalAlignment; } Alignment getVerticalAlignment() { return mVerticalAlignment; }
int getTextCacheGlyphHeight() override
{
return (mTextCache == nullptr ? 0 : mTextCache->metrics.maxGlyphHeight);
}
protected: protected:
virtual void onTextChanged(); virtual void onTextChanged();
@ -89,7 +91,6 @@ protected:
std::shared_ptr<Font> mFont; std::shared_ptr<Font> mFont;
private: private:
void calculateExtent();
void onColorChanged(); void onColorChanged();
static inline std::vector<std::string> supportedSystemdataTypes { static inline std::vector<std::string> supportedSystemdataTypes {
@ -118,6 +119,7 @@ private:
bool mNoTopMargin; bool mNoTopMargin;
bool mSelectable; bool mSelectable;
bool mVerticalAutoSizing; bool mVerticalAutoSizing;
bool mLegacyTheme;
}; };
#endif // ES_CORE_COMPONENTS_TEXT_COMPONENT_H #endif // ES_CORE_COMPONENTS_TEXT_COMPONENT_H

View file

@ -27,6 +27,7 @@ TextEditComponent::TextEditComponent()
, mBlinkTime {0} , mBlinkTime {0}
, mCursorRepeatDir {0} , mCursorRepeatDir {0}
, mScrollOffset {0.0f, 0.0f} , mScrollOffset {0.0f, 0.0f}
, mCursorPos {0.0f, 0.0f}
, mBox {":/graphics/textinput.svg"} , mBox {":/graphics/textinput.svg"}
, mFont {Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT)} , mFont {Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT)}
{ {
@ -51,6 +52,9 @@ void TextEditComponent::onFocusLost()
void TextEditComponent::onSizeChanged() void TextEditComponent::onSizeChanged()
{ {
if (mSize.x == 0.0f || mSize.y == 0.0f)
return;
mBox.fitTo( mBox.fitTo(
mSize, glm::vec3 {}, mSize, glm::vec3 {},
glm::vec2 {-34.0f, -32.0f - (TEXT_PADDING_VERT * Renderer::getScreenHeightModifier())}); glm::vec2 {-34.0f, -32.0f - (TEXT_PADDING_VERT * Renderer::getScreenHeightModifier())});
@ -263,9 +267,10 @@ void TextEditComponent::setCursor(size_t pos)
void TextEditComponent::onTextChanged() void TextEditComponent::onTextChanged()
{ {
std::string wrappedText = (isMultiline() ? mFont->wrapText(mText, getTextAreaSize().x) : mText); mWrappedText =
(isMultiline() ? mFont->wrapText(mText, getTextAreaSize().x, 0.0f, 1.5f, true) : mText);
mTextCache = std::unique_ptr<TextCache>(mFont->buildTextCache( mTextCache = std::unique_ptr<TextCache>(mFont->buildTextCache(
wrappedText, 0.0f, 0.0f, 0x77777700 | static_cast<unsigned char>(mOpacity * 255.0f))); mWrappedText, 0.0f, 0.0f, 0x77777700 | static_cast<unsigned char>(mOpacity * 255.0f)));
if (mCursor > static_cast<int>(mText.length())) if (mCursor > static_cast<int>(mText.length()))
mCursor = static_cast<int>(mText.length()); mCursor = static_cast<int>(mText.length());
@ -274,22 +279,23 @@ void TextEditComponent::onTextChanged()
void TextEditComponent::onCursorChanged() void TextEditComponent::onCursorChanged()
{ {
if (isMultiline()) { if (isMultiline()) {
glm::vec2 textSize {mFont->getWrappedTextCursorOffset(mText, getTextAreaSize().x, mCursor)}; mCursorPos = mFont->getWrappedTextCursorOffset(mWrappedText, mCursor);
// Need to scroll down? // Need to scroll down?
if (mScrollOffset.y + getTextAreaSize().y < textSize.y + mFont->getHeight()) if (mScrollOffset.y + getTextAreaSize().y < mCursorPos.y + mFont->getHeight())
mScrollOffset.y = textSize.y - getTextAreaSize().y + mFont->getHeight(); mScrollOffset.y = mCursorPos.y - getTextAreaSize().y + mFont->getHeight();
// Need to scroll up? // Need to scroll up?
else if (mScrollOffset.y > textSize.y) else if (mScrollOffset.y > mCursorPos.y)
mScrollOffset.y = textSize.y; mScrollOffset.y = mCursorPos.y;
} }
else { else {
glm::vec2 cursorPos {mFont->sizeText(mText.substr(0, mCursor))}; mCursorPos = mFont->sizeText(mText.substr(0, mCursor));
mCursorPos.y = 0.0f;
if (mScrollOffset.x + getTextAreaSize().x < cursorPos.x) if (mScrollOffset.x + getTextAreaSize().x < mCursorPos.x)
mScrollOffset.x = cursorPos.x - getTextAreaSize().x; mScrollOffset.x = mCursorPos.x - getTextAreaSize().x;
else if (mScrollOffset.x > cursorPos.x) else if (mScrollOffset.x > mCursorPos.x)
mScrollOffset.x = cursorPos.x; mScrollOffset.x = mCursorPos.x;
} }
} }
@ -323,25 +329,16 @@ void TextEditComponent::render(const glm::mat4& parentTrans)
mRenderer->popClipRect(); mRenderer->popClipRect();
// Draw cursor. // Draw cursor.
glm::vec2 cursorPos; float cursorHeight {mFont->getHeight() * 0.8f};
if (isMultiline()) {
cursorPos = mFont->getWrappedTextCursorOffset(mText, getTextAreaSize().x, mCursor);
}
else {
cursorPos = mFont->sizeText(mText.substr(0, mCursor));
cursorPos[1] = 0;
}
float cursorHeight = mFont->getHeight() * 0.8f;
if (!mEditing) { if (!mEditing) {
mRenderer->drawRect(cursorPos.x, cursorPos.y + (mFont->getHeight() - cursorHeight) / 2.0f, mRenderer->drawRect(mCursorPos.x, mCursorPos.y + (mFont->getHeight() - cursorHeight) / 2.0f,
2.0f * Renderer::getScreenWidthModifier(), cursorHeight, 0xC7C7C7FF, 2.0f * Renderer::getScreenWidthModifier(), cursorHeight, 0xC7C7C7FF,
0xC7C7C7FF); 0xC7C7C7FF);
} }
if (mEditing && mBlinkTime < BLINKTIME / 2) { if (mEditing && mBlinkTime < BLINKTIME / 2) {
mRenderer->drawRect(cursorPos.x, cursorPos.y + (mFont->getHeight() - cursorHeight) / 2.0f, mRenderer->drawRect(mCursorPos.x, mCursorPos.y + (mFont->getHeight() - cursorHeight) / 2.0f,
2.0f * Renderer::getScreenWidthModifier(), cursorHeight, 0x777777FF, 2.0f * Renderer::getScreenWidthModifier(), cursorHeight, 0x777777FF,
0x777777FF); 0x777777FF);
} }

View file

@ -59,6 +59,7 @@ private:
Renderer* mRenderer; Renderer* mRenderer;
std::string mText; std::string mText;
std::string mWrappedText;
std::string mTextOrig; std::string mTextOrig;
bool mFocused; bool mFocused;
bool mEditing; bool mEditing;
@ -70,6 +71,7 @@ private:
int mCursorRepeatDir; int mCursorRepeatDir;
glm::vec2 mScrollOffset; glm::vec2 mScrollOffset;
glm::vec2 mCursorPos;
NinePatchComponent mBox; NinePatchComponent mBox;

View file

@ -25,6 +25,7 @@ VideoComponent::VideoComponent()
, 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}
, mPillarboxThreshold {0.85f, 0.90f}
, mStartTime {0} , mStartTime {0}
, mIsPlaying {false} , mIsPlaying {false}
, mIsActuallyPlaying {false} , mIsActuallyPlaying {false}
@ -256,6 +257,12 @@ void VideoComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
if (elem->has("pillarboxes")) if (elem->has("pillarboxes"))
mDrawPillarboxes = elem->get<bool>("pillarboxes"); mDrawPillarboxes = elem->get<bool>("pillarboxes");
if (elem->has("pillarboxThreshold")) {
const glm::vec2 pillarboxThreshold {elem->get<glm::vec2>("pillarboxThreshold")};
mPillarboxThreshold.x = glm::clamp(pillarboxThreshold.x, 0.2f, 1.0f);
mPillarboxThreshold.y = glm::clamp(pillarboxThreshold.y, 0.2f, 1.0f);
}
if (elem->has("scanlines")) if (elem->has("scanlines"))
mRenderScanlines = elem->get<bool>("scanlines"); mRenderScanlines = elem->get<bool>("scanlines");

View file

@ -110,6 +110,7 @@ protected:
glm::vec2 mTargetSize; glm::vec2 mTargetSize;
glm::vec2 mVideoAreaPos; glm::vec2 mVideoAreaPos;
glm::vec2 mVideoAreaSize; glm::vec2 mVideoAreaSize;
glm::vec2 mPillarboxThreshold;
std::shared_ptr<TextureResource> mTexture; std::shared_ptr<TextureResource> mTexture;
std::string mStaticImagePath; std::string mStaticImagePath;
std::string mDefaultImagePath; std::string mDefaultImagePath;

View file

@ -103,7 +103,6 @@ void VideoFFmpegComponent::resize()
mSize.y *= resizeScale.y; mSize.y *= resizeScale.y;
} }
mSize.y = std::round(mSize.y);
mSize.x = (mSize.y / textureSize.y) * textureSize.x; mSize.x = (mSize.y / textureSize.y) * textureSize.x;
} }
else { else {
@ -113,11 +112,11 @@ void VideoFFmpegComponent::resize()
// If only one component is set, we resize in a way that maintains aspect ratio. // If only one component is set, we resize in a way that maintains aspect ratio.
if (!mTargetSize.x && mTargetSize.y) { if (!mTargetSize.x && mTargetSize.y) {
mSize.y = std::round(mTargetSize.y); mSize.y = mTargetSize.y;
mSize.x = (mSize.y / textureSize.y) * textureSize.x; mSize.x = (mSize.y / textureSize.y) * textureSize.x;
} }
else if (mTargetSize.x && !mTargetSize.y) { else if (mTargetSize.x && !mTargetSize.y) {
mSize.y = std::round((mTargetSize.x / textureSize.x) * textureSize.y); mSize.y = (mTargetSize.x / textureSize.x) * textureSize.y;
mSize.x = (mSize.y / textureSize.y) * textureSize.x; mSize.x = (mSize.y / textureSize.y) * textureSize.x;
} }
} }
@ -933,6 +932,11 @@ void VideoFFmpegComponent::calculateBlackRectangle()
// otherwise it will exactly match the video size. The reason to add a black rectangle // otherwise it will exactly match the video size. The reason to add a black rectangle
// behind videos in this second instance is that the scanline rendering will make the // behind videos in this second instance is that the scanline rendering will make the
// video partially transparent so this may avoid some unforseen issues with some themes. // video partially transparent so this may avoid some unforseen issues with some themes.
// In general, adding very narrow pillarboxes or letterboxes doesn't look good, so by
// default this is not done unless the size of the video vs the overall video area is
// above the threshold defined by mPillarboxThreshold. By default this is set to 0.85
// for the X axis and 0.90 for the Y axis, but this is theme-controllable via the
// pillarboxThreshold property.
if (mVideoAreaPos != glm::vec2 {0.0f, 0.0f} && mVideoAreaSize != glm::vec2 {0.0f, 0.0f}) { if (mVideoAreaPos != glm::vec2 {0.0f, 0.0f} && mVideoAreaSize != glm::vec2 {0.0f, 0.0f}) {
mVideoRectangleCoords.clear(); mVideoRectangleCoords.clear();
mRectangleOffset = {0.0f, 0.0f}; mRectangleOffset = {0.0f, 0.0f};
@ -949,20 +953,24 @@ void VideoFFmpegComponent::calculateBlackRectangle()
// and then scaled, there could be rounding errors that make the video height // and then scaled, there could be rounding errors that make the video height
// slightly higher than allowed. It's only a single pixel or a few pixels, but // slightly higher than allowed. It's only a single pixel or a few pixels, but
// it's still visible for some videos. // it's still visible for some videos.
if (mSize.y < mVideoAreaSize.y && mSize.y / mVideoAreaSize.y < 0.90f) if (mSize.y < mVideoAreaSize.y &&
mSize.y / mVideoAreaSize.y < mPillarboxThreshold.y)
rectHeight = mVideoAreaSize.y; rectHeight = mVideoAreaSize.y;
else else
rectHeight = mSize.y; rectHeight = mSize.y;
// Don't add a black border that is too narrow, that's what the 0.85 constant if (mSize.x < mVideoAreaSize.x &&
// takes care of. mSize.x / mVideoAreaSize.x < mPillarboxThreshold.x)
if (mSize.x < mVideoAreaSize.x && mSize.x / mVideoAreaSize.x < 0.85f)
rectWidth = mVideoAreaSize.x; rectWidth = mVideoAreaSize.x;
else else
rectWidth = mSize.x; rectWidth = mSize.x;
} }
// Video is in portrait orientation (or completely square). // Video is in portrait orientation (or completely square).
else { else {
rectWidth = mVideoAreaSize.x; if (mSize.x <= mVideoAreaSize.x &&
mSize.x / mVideoAreaSize.x < mPillarboxThreshold.x)
rectWidth = mVideoAreaSize.x;
else
rectWidth = mSize.x;
rectHeight = mSize.y; rectHeight = mSize.y;
} }
// If an origin value other than 0.5 is used, then create an offset for centering // If an origin value other than 0.5 is used, then create an offset for centering

View file

@ -183,7 +183,8 @@ CarouselComponent<T>::CarouselComponent()
, mMaxItemCount {3.0f} , mMaxItemCount {3.0f}
, mItemsBeforeCenter {8} , mItemsBeforeCenter {8}
, mItemsAfterCenter {8} , mItemsAfterCenter {8}
, mItemSize {Renderer::getScreenWidth() * 0.25f, Renderer::getScreenHeight() * 0.155f} , mItemSize {glm::vec2 {Renderer::getScreenWidth() * 0.25f,
Renderer::getScreenHeight() * 0.155f}}
, mLinearInterpolation {false} , mLinearInterpolation {false}
, mInstantItemTransitions {false} , mInstantItemTransitions {false}
, mItemAxisHorizontal {false} , mItemAxisHorizontal {false}
@ -226,7 +227,7 @@ void CarouselComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeDat
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(mItemSize * mItemScale); item->setMaxSize(glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
item->applyTheme(theme, "system", "image_logo", item->applyTheme(theme, "system", "image_logo",
ThemeFlags::PATH | ThemeFlags::COLOR); ThemeFlags::PATH | ThemeFlags::COLOR);
item->setRotateByTargetSize(true); item->setRotateByTargetSize(true);
@ -240,7 +241,7 @@ void CarouselComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeDat
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(mItemSize * mItemScale); item->setMaxSize(glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
item->setImage(entry.data.itemPath); item->setImage(entry.data.itemPath);
item->applyTheme(theme, "system", "", ThemeFlags::ALL); item->applyTheme(theme, "system", "", ThemeFlags::ALL);
item->setRotateByTargetSize(true); item->setRotateByTargetSize(true);
@ -251,7 +252,8 @@ void CarouselComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeDat
auto defaultItem = std::make_shared<ImageComponent>(false, dynamic); auto defaultItem = std::make_shared<ImageComponent>(false, dynamic);
defaultItem->setLinearInterpolation(mLinearInterpolation); defaultItem->setLinearInterpolation(mLinearInterpolation);
defaultItem->setMipmapping(true); defaultItem->setMipmapping(true);
defaultItem->setMaxSize(mItemSize * mItemScale); defaultItem->setMaxSize(
glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
defaultItem->setImage(entry.data.defaultItemPath); defaultItem->setImage(entry.data.defaultItemPath);
defaultItem->applyTheme(theme, "system", "", ThemeFlags::ALL); defaultItem->applyTheme(theme, "system", "", ThemeFlags::ALL);
defaultItem->setRotateByTargetSize(true); defaultItem->setRotateByTargetSize(true);
@ -275,7 +277,8 @@ void CarouselComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeDat
auto text = std::make_shared<TextComponent>( auto text = std::make_shared<TextComponent>(
nameEntry, mFont, 0x000000FF, mItemHorizontalAlignment, mItemVerticalAlignment, nameEntry, mFont, 0x000000FF, mItemHorizontalAlignment, mItemVerticalAlignment,
glm::vec3 {0.0f, 0.0f, 0.0f}, mItemSize * mItemScale, 0x00000000); glm::vec3 {0.0f, 0.0f, 0.0f},
glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)), 0x00000000);
if (legacyMode) { if (legacyMode) {
text->applyTheme(theme, "system", "text_logoText", text->applyTheme(theme, "system", "text_logoText",
ThemeFlags::FONT_PATH | ThemeFlags::FONT_SIZE | ThemeFlags::COLOR | ThemeFlags::FONT_PATH | ThemeFlags::FONT_SIZE | ThemeFlags::COLOR |
@ -310,7 +313,7 @@ void CarouselComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeDat
else else
entry.data.item->setOrigin(entry.data.item->getOrigin().x, 0.5f); entry.data.item->setOrigin(entry.data.item->getOrigin().x, 0.5f);
glm::vec2 denormalized {mItemSize * entry.data.item->getOrigin()}; glm::vec2 denormalized {glm::round(mItemSize * entry.data.item->getOrigin())};
entry.data.item->setPosition(glm::vec3 {denormalized.x, denormalized.y, 0.0f}); entry.data.item->setPosition(glm::vec3 {denormalized.x, denormalized.y, 0.0f});
List::add(entry); List::add(entry);
@ -323,7 +326,7 @@ void CarouselComponent<T>::updateEntry(Entry& entry, const std::shared_ptr<Theme
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(mItemSize * mItemScale); item->setMaxSize(glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
item->setImage(entry.data.itemPath); item->setImage(entry.data.itemPath);
item->applyTheme(theme, "system", "", ThemeFlags::ALL); item->applyTheme(theme, "system", "", ThemeFlags::ALL);
item->setRotateByTargetSize(true); item->setRotateByTargetSize(true);
@ -348,7 +351,7 @@ void CarouselComponent<T>::updateEntry(Entry& entry, const std::shared_ptr<Theme
else else
entry.data.item->setOrigin(entry.data.item->getOrigin().x, 0.5f); entry.data.item->setOrigin(entry.data.item->getOrigin().x, 0.5f);
glm::vec2 denormalized {mItemSize * entry.data.item->getOrigin()}; glm::vec2 denormalized {glm::round(mItemSize * entry.data.item->getOrigin())};
entry.data.item->setPosition(glm::vec3 {denormalized.x, denormalized.y, 0.0f}); entry.data.item->setPosition(glm::vec3 {denormalized.x, denormalized.y, 0.0f});
} }
@ -567,11 +570,10 @@ template <typename T> void CarouselComponent<T>::render(const glm::mat4& parentT
glm::mat4 carouselTrans {parentTrans}; glm::mat4 carouselTrans {parentTrans};
carouselTrans = glm::translate( carouselTrans = glm::translate(
carouselTrans, carouselTrans, glm::vec3 {GuiComponent::mPosition.x, GuiComponent::mPosition.y, 0.0f});
glm::round(glm::vec3 {GuiComponent::mPosition.x, GuiComponent::mPosition.y, 0.0f})); carouselTrans =
carouselTrans = glm::translate( glm::translate(carouselTrans, glm::vec3 {GuiComponent::mOrigin.x * mSize.x * -1.0f,
carouselTrans, glm::round(glm::vec3 {GuiComponent::mOrigin.x * mSize.x * -1.0f, GuiComponent::mOrigin.y * mSize.y * -1.0f, 0.0f});
GuiComponent::mOrigin.y * mSize.y * -1.0f, 0.0f}));
if (carouselTrans[3].x + mSize.x <= 0.0f || carouselTrans[3].y + mSize.y <= 0.0f) if (carouselTrans[3].x + mSize.x <= 0.0f || carouselTrans[3].y + mSize.y <= 0.0f)
return; return;
@ -609,7 +611,7 @@ template <typename T> void CarouselComponent<T>::render(const glm::mat4& parentT
float scaleSize {mItemSize.x * mItemScale - mItemSize.x}; float scaleSize {mItemSize.x * mItemScale - mItemSize.x};
if (isWheel) { if (isWheel) {
xOff = (mSize.x - mItemSize.x) / 2.0f - (camOffset * itemSpacing.y); xOff = (mSize.x - mItemSize.x) / 2.0f;
yOff = (mSize.y - mItemSize.y) / 2.0f; yOff = (mSize.y - mItemSize.y) / 2.0f;
// Alignment of the actual carousel inside to the overall component area. // Alignment of the actual carousel inside to the overall component area.
if (mLegacyMode) { if (mLegacyMode) {
@ -769,16 +771,24 @@ template <typename T> void CarouselComponent<T>::render(const glm::mat4& parentT
if (singleEntry) if (singleEntry)
distance = 0.0f; distance = 0.0f;
float scale {1.0f + ((mItemScale - 1.0f) * (1.0f - fabsf(distance)))}; float scale {0.0f};
scale = std::min(mItemScale, std::max(1.0f, scale));
scale /= mItemScale; if (mItemScale >= 1.0f) {
scale = 1.0f + ((mItemScale - 1.0f) * (1.0f - fabsf(distance)));
scale = std::min(mItemScale, std::max(1.0f, scale));
scale /= mItemScale;
}
else {
scale = 1.0f + ((1.0f - mItemScale) * (fabsf(distance) - 1.0f));
scale = std::max(mItemScale, std::min(1.0f, scale));
}
glm::mat4 itemTrans {carouselTrans}; glm::mat4 itemTrans {carouselTrans};
if (singleEntry) if (singleEntry)
itemTrans = glm::translate(carouselTrans, glm::vec3 {xOff, yOff, 0.0f}); itemTrans = glm::translate(carouselTrans, glm::vec3 {xOff, yOff, 0.0f});
else else
itemTrans = glm::translate( itemTrans = glm::translate(itemTrans, glm::vec3 {(i * itemSpacing.x) + xOff,
itemTrans, glm::vec3 {i * itemSpacing.x + xOff, i * itemSpacing.y + yOff, 0.0f}); (i * itemSpacing.y) + yOff, 0.0f});
float opacity {0.0f}; float opacity {0.0f};
@ -1012,7 +1022,7 @@ void CarouselComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
} }
if (elem->has("itemScale")) if (elem->has("itemScale"))
mItemScale = glm::clamp(elem->get<float>("itemScale"), 0.5f, 3.0f); mItemScale = glm::clamp(elem->get<float>("itemScale"), 0.2f, 3.0f);
if (elem->has("itemTransitions")) { if (elem->has("itemTransitions")) {
const std::string itemTransitions {elem->get<std::string>("itemTransitions")}; const std::string itemTransitions {elem->get<std::string>("itemTransitions")};
@ -1145,8 +1155,9 @@ void CarouselComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
// Legacy themes. // Legacy themes.
if (mLegacyMode) { if (mLegacyMode) {
// Don't allow logoScale below 1.0 for legacy themes as it introduces compatibility issues.
if (elem->has("logoScale")) if (elem->has("logoScale"))
mItemScale = glm::clamp(elem->get<float>("logoScale"), 0.5f, 3.0f); mItemScale = glm::clamp(elem->get<float>("logoScale"), 1.0f, 3.0f);
if (elem->has("logoSize")) { if (elem->has("logoSize")) {
// Keep size within a 0.05 and 1.0 multiple of the screen size. // Keep size within a 0.05 and 1.0 multiple of the screen size.
glm::vec2 itemSize {elem->get<glm::vec2>("logoSize")}; glm::vec2 itemSize {elem->get<glm::vec2>("logoSize")};
@ -1206,7 +1217,9 @@ void CarouselComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
} }
} }
mFont = Font::getFromTheme(elem, properties, mFont); // For non-legacy themes, scale the font size with the itemScale property value.
mFont = Font::getFromTheme(elem, properties, mFont, 0.0f, mLegacyMode,
(mLegacyMode ? 1.0f : (mItemScale >= 1.0f ? mItemScale : 1.0f)));
if (elem->has("textColor")) if (elem->has("textColor"))
mTextColor = elem->get<unsigned int>("textColor"); mTextColor = elem->get<unsigned int>("textColor");

View file

@ -350,10 +350,19 @@ template <typename T> void TextListComponent<T>::render(const glm::mat4& parentT
int screenCount {0}; int screenCount {0};
float y {0.0f}; float y {0.0f};
const float entrySize { float entrySize {0.0f};
std::max(floorf(font->getHeight(1.0f)), floorf(static_cast<float>(font->getSize()))) * float lineSpacingHeight {0.0f};
mLineSpacing};
const float lineSpacingHeight {floorf(font->getHeight(mLineSpacing) - font->getHeight(1.0f))}; // The vertical spacing between rows for legacy themes is very inaccurate and will look
// different depending on the resolution, but it's done for maximum backward compatibility.
if (mLegacyMode) {
entrySize = std::floor(font->getSize()) * mLineSpacing;
lineSpacingHeight = std::floor(font->getSize()) * mLineSpacing - font->getSize() * 1.0f;
}
else {
entrySize = font->getSize() * mLineSpacing;
lineSpacingHeight = font->getSize() * mLineSpacing - font->getSize() * 1.0f;
}
if (mLegacyMode) { if (mLegacyMode) {
// This extra vertical margin is technically incorrect, but it adds a little extra leeway // This extra vertical margin is technically incorrect, but it adds a little extra leeway
@ -366,7 +375,9 @@ template <typename T> void TextListComponent<T>::render(const glm::mat4& parentT
floorf((mSize.y + lineSpacingHeight / 2.0f + extraMargin) / entrySize)); floorf((mSize.y + lineSpacingHeight / 2.0f + extraMargin) / entrySize));
} }
else { else {
screenCount = static_cast<int>(floorf((mSize.y + lineSpacingHeight / 2.0f) / entrySize)); // Number of entries that can fit on the screen simultaneously.
screenCount =
static_cast<int>(std::floor((mSize.y + lineSpacingHeight / 2.0f) / entrySize));
} }
if (size() >= screenCount) { if (size() >= screenCount) {
@ -478,11 +489,10 @@ template <typename T> void TextListComponent<T>::render(const glm::mat4& parentT
// Currently selected item text might be looping. // Currently selected item text might be looping.
if (mCursor == i && mLoopOffset1 > 0) { if (mCursor == i && mLoopOffset1 > 0) {
drawTrans = glm::translate( drawTrans = glm::translate(
drawTrans, drawTrans, offset - glm::vec3 {static_cast<float>(mLoopOffset1), 0.0f, 0.0f});
glm::round(offset - glm::vec3 {static_cast<float>(mLoopOffset1), 0.0f, 0.0f}));
} }
else { else {
drawTrans = glm::translate(drawTrans, glm::round(offset)); drawTrans = glm::translate(drawTrans, offset);
} }
// Needed to avoid flickering when returning to the start position. // Needed to avoid flickering when returning to the start position.
@ -497,8 +507,7 @@ template <typename T> void TextListComponent<T>::render(const glm::mat4& parentT
mLoopScroll = true; mLoopScroll = true;
drawTrans = trans; drawTrans = trans;
drawTrans = glm::translate( drawTrans = glm::translate(
drawTrans, drawTrans, offset - glm::vec3 {static_cast<float>(mLoopOffset2), 0.0f, 0.0f});
glm::round(offset - glm::vec3 {static_cast<float>(mLoopOffset2), 0.0f, 0.0f}));
mRenderer->setMatrix(drawTrans); mRenderer->setMatrix(drawTrans);
font->renderTextCache(entry.data.textCache.get()); font->renderTextCache(entry.data.textCache.get());
} }
@ -555,9 +564,8 @@ void TextListComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
setColor(1, elem->get<unsigned int>("secondaryColor")); setColor(1, elem->get<unsigned int>("secondaryColor"));
} }
setFont(Font::getFromTheme(elem, properties, mFont)); setFont(Font::getFromTheme(elem, properties, mFont, 0.0f, mLegacyMode));
const float selectorHeight { const float selectorHeight {mFont->getHeight(mLineSpacing)};
std::max(mFont->getHeight(1.0), static_cast<float>(mFont->getSize())) * mLineSpacing};
setSelectorHeight(selectorHeight); setSelectorHeight(selectorHeight);
if (properties & ALIGNMENT) { if (properties & ALIGNMENT) {

View file

@ -52,7 +52,7 @@ void Renderer::setIcon()
// Try creating SDL surface from logo data. // Try creating SDL surface from logo data.
SDL_Surface* logoSurface {SDL_CreateRGBSurfaceFrom( SDL_Surface* logoSurface {SDL_CreateRGBSurfaceFrom(
static_cast<void*>(rawData.data()), static_cast<int>(width), static_cast<int>(height), static_cast<void*>(rawData.data()), static_cast<int>(width), static_cast<int>(height),
32, static_cast<int>((width * 4)), rmask, gmask, bmask, amask)}; 32, static_cast<int>(width * 4), rmask, gmask, bmask, amask)};
if (logoSurface != nullptr) { if (logoSurface != nullptr) {
SDL_SetWindowIcon(mSDLWindow, logoSurface); SDL_SetWindowIcon(mSDLWindow, logoSurface);
@ -147,7 +147,7 @@ bool Renderer::createWindow()
if (mWindowWidth != displayMode.w || mWindowHeight != displayMode.h) if (mWindowWidth != displayMode.w || mWindowHeight != displayMode.h)
userResolution = true; userResolution = true;
unsigned int windowFlags; unsigned int windowFlags {0};
setup(); setup();
#if defined(_WIN64) #if defined(_WIN64)
@ -201,7 +201,7 @@ bool Renderer::createWindow()
// instead we simply indicate the physical pixel dimensions in parenthesis in the log // instead we simply indicate the physical pixel dimensions in parenthesis in the log
// file and make sure to double the window and screen sizes in case of a high DPI // file and make sure to double the window and screen sizes in case of a high DPI
// display so that the full application window is used for rendering. // display so that the full application window is used for rendering.
int width = 0; int width {0};
SDL_GL_GetDrawableSize(mSDLWindow, &width, nullptr); SDL_GL_GetDrawableSize(mSDLWindow, &width, nullptr);
int scaleFactor = static_cast<int>(width / mWindowWidth); int scaleFactor = static_cast<int>(width / mWindowWidth);
@ -309,11 +309,11 @@ void Renderer::pushClipRect(const glm::ivec2& pos, const glm::ivec2& size)
box.h = sScreenHeight - box.y; box.h = sScreenHeight - box.y;
if (mScreenRotated) { if (mScreenRotated) {
box = Rect(mWindowWidth - mScreenOffsetX - box.x - box.w, box = Rect {mWindowWidth - mScreenOffsetX - box.x - box.w,
mWindowHeight - mScreenOffsetY - box.y - box.h, box.w, box.h); mWindowHeight - mScreenOffsetY - box.y - box.h, box.w, box.h};
} }
else { else {
box = Rect(mScreenOffsetX + box.x, mScreenOffsetY + box.y, box.w, box.h); box = Rect {mScreenOffsetX + box.x, mScreenOffsetY + box.y, box.w, box.h};
} }
// Make sure the box fits within mClipStack.top(), and clip further accordingly. // Make sure the box fits within mClipStack.top(), and clip further accordingly.
@ -349,7 +349,7 @@ void Renderer::popClipRect()
mClipStack.pop(); mClipStack.pop();
if (mClipStack.empty()) if (mClipStack.empty())
setScissor(Rect(0, 0, 0, 0)); setScissor(Rect {0, 0, 0, 0});
else else
setScissor(mClipStack.top()); setScissor(mClipStack.top());
} }
@ -360,7 +360,7 @@ void Renderer::drawRect(const float x,
const float h, const float h,
const unsigned int color, const unsigned int color,
const unsigned int colorEnd, const unsigned int colorEnd,
bool horizontalGradient, const bool horizontalGradient,
const float opacity, const float opacity,
const float dimming, const float dimming,
const BlendFactor srcBlendFactor, const BlendFactor srcBlendFactor,

View file

@ -146,7 +146,7 @@ public:
const float h, const float h,
const unsigned int color, const unsigned int color,
const unsigned int colorEnd, const unsigned int colorEnd,
bool horizontalGradient = false, const bool horizontalGradient = false,
const float opacity = 1.0, const float opacity = 1.0,
const float dimming = 1.0, const float dimming = 1.0,
const BlendFactor srcBlendFactor = BlendFactor::SRC_ALPHA, const BlendFactor srcBlendFactor = BlendFactor::SRC_ALPHA,

View file

@ -33,19 +33,13 @@ RendererOpenGL::RendererOpenGL() noexcept
{ {
} }
RendererOpenGL::~RendererOpenGL()
{
for (auto it = mShaderProgramVector.cbegin(); it != mShaderProgramVector.cend(); ++it)
delete *it;
}
RendererOpenGL* RendererOpenGL::getInstance() RendererOpenGL* RendererOpenGL::getInstance()
{ {
static RendererOpenGL instance; static RendererOpenGL instance;
return &instance; return &instance;
} }
ShaderOpenGL* RendererOpenGL::getShaderProgram(unsigned int shaderID) std::shared_ptr<ShaderOpenGL> RendererOpenGL::getShaderProgram(unsigned int shaderID)
{ {
unsigned int index {0}; unsigned int index {0};
@ -67,13 +61,13 @@ bool RendererOpenGL::loadShaders()
LOG(LogInfo) << "Loading shaders..."; LOG(LogInfo) << "Loading shaders...";
std::vector<std::string> shaderFiles; std::vector<std::string> shaderFiles;
shaderFiles.push_back(":/shaders/glsl/core.glsl"); shaderFiles.emplace_back(":/shaders/glsl/core.glsl");
shaderFiles.push_back(":/shaders/glsl/blur_horizontal.glsl"); shaderFiles.emplace_back(":/shaders/glsl/blur_horizontal.glsl");
shaderFiles.push_back(":/shaders/glsl/blur_vertical.glsl"); shaderFiles.emplace_back(":/shaders/glsl/blur_vertical.glsl");
shaderFiles.push_back(":/shaders/glsl/scanlines.glsl"); shaderFiles.emplace_back(":/shaders/glsl/scanlines.glsl");
for (auto it = shaderFiles.cbegin(); it != shaderFiles.cend(); ++it) { for (auto it = shaderFiles.cbegin(); it != shaderFiles.cend(); ++it) {
ShaderOpenGL* loadShader = new ShaderOpenGL(); auto loadShader = std::make_shared<ShaderOpenGL>();
loadShader->loadShaderFile(*it, GL_VERTEX_SHADER); loadShader->loadShaderFile(*it, GL_VERTEX_SHADER);
loadShader->loadShaderFile(*it, GL_FRAGMENT_SHADER); loadShader->loadShaderFile(*it, GL_FRAGMENT_SHADER);
@ -83,7 +77,7 @@ bool RendererOpenGL::loadShaders()
return false; return false;
} }
mShaderProgramVector.push_back(loadShader); mShaderProgramVector.emplace_back(std::move(loadShader));
} }
return true; return true;
@ -576,7 +570,7 @@ void RendererOpenGL::shaderPostprocessing(unsigned int shaders,
for (size_t i = 0; i < shaderList.size(); ++i) { for (size_t i = 0; i < shaderList.size(); ++i) {
vertices->shaders = shaderList[i]; vertices->shaders = shaderList[i];
int shaderPasses = 1; int shaderPasses {1};
// For the blur shaders there is an optional variable to set the number of passes // For the blur shaders there is an optional variable to set the number of passes
// to execute, which proportionally affects the blur amount. // to execute, which proportionally affects the blur amount.
if (shaderList[i] == Renderer::Shader::BLUR_HORIZONTAL || if (shaderList[i] == Renderer::Shader::BLUR_HORIZONTAL ||

View file

@ -20,15 +20,14 @@
#include <SDL2/SDL_opengl.h> #include <SDL2/SDL_opengl.h>
#endif #endif
#include <memory>
class RendererOpenGL : public Renderer class RendererOpenGL : public Renderer
{ {
public: public:
RendererOpenGL() noexcept;
~RendererOpenGL();
static RendererOpenGL* getInstance(); static RendererOpenGL* getInstance();
ShaderOpenGL* getShaderProgram(unsigned int shaderID); std::shared_ptr<ShaderOpenGL> getShaderProgram(unsigned int shaderID);
bool loadShaders() override; bool loadShaders() override;
GLenum convertBlendFactor(const BlendFactor BlendFactor); GLenum convertBlendFactor(const BlendFactor BlendFactor);
@ -73,7 +72,9 @@ public:
unsigned char* textureRGBA = nullptr) override; unsigned char* textureRGBA = nullptr) override;
private: private:
std::vector<ShaderOpenGL*> mShaderProgramVector; RendererOpenGL() noexcept;
std::vector<std::shared_ptr<ShaderOpenGL>> mShaderProgramVector;
GLuint mShaderFBO1; GLuint mShaderFBO1;
GLuint mShaderFBO2; GLuint mShaderFBO2;
GLuint mVertexBuffer1; GLuint mVertexBuffer1;
@ -83,14 +84,16 @@ private:
GLuint mWhiteTexture; GLuint mWhiteTexture;
GLuint mPostProcTexture1; GLuint mPostProcTexture1;
GLuint mPostProcTexture2; GLuint mPostProcTexture2;
ShaderOpenGL* mCoreShader; std::shared_ptr<ShaderOpenGL> mCoreShader;
ShaderOpenGL* mBlurHorizontalShader; std::shared_ptr<ShaderOpenGL> mBlurHorizontalShader;
ShaderOpenGL* mBlurVerticalShader; std::shared_ptr<ShaderOpenGL> mBlurVerticalShader;
ShaderOpenGL* mScanlinelShader; std::shared_ptr<ShaderOpenGL> mScanlinelShader;
ShaderOpenGL* mLastShader; std::shared_ptr<ShaderOpenGL> mLastShader;
int mMajorGLVersion; int mMajorGLVersion;
int mMinorGLVersion; int mMinorGLVersion;
friend Renderer;
}; };
#endif // ES_CORE_RENDERER_RENDERER_OPENGL_H #endif // ES_CORE_RENDERER_RENDERER_OPENGL_H

View file

@ -4,7 +4,7 @@
// Font.h // Font.h
// //
// Loading, unloading, caching and rendering of fonts. // Loading, unloading, caching and rendering of fonts.
// Also functions for word wrapping and similar. // Also functions for text wrapping and similar.
// //
#include "resources/Font.h" #include "resources/Font.h"
@ -12,24 +12,23 @@
#include "Log.h" #include "Log.h"
#include "renderers/Renderer.h" #include "renderers/Renderer.h"
#include "utils/FileSystemUtil.h" #include "utils/FileSystemUtil.h"
#include "utils/PlatformUtil.h"
#include "utils/StringUtil.h" #include "utils/StringUtil.h"
FT_Library Font::sLibrary {nullptr}; Font::Font(float size, const std::string& path)
std::map<std::pair<std::string, int>, std::weak_ptr<Font>> Font::sFontMap;
Font::Font(int size, const std::string& path)
: mRenderer {Renderer::getInstance()} : mRenderer {Renderer::getInstance()}
, mSize(size)
, mMaxGlyphHeight {0}
, mPath(path) , mPath(path)
, mFontSize {size}
, mLetterHeight {0.0f}
, mMaxGlyphHeight {static_cast<int>(std::round(size))}
, mLegacyMaxGlyphHeight {0}
{ {
if (mSize < 9) { if (mFontSize < 3.0f) {
mSize = 9; mFontSize = 3.0f;
LOG(LogWarning) << "Requested font size too small, changing to minimum supported size"; LOG(LogWarning) << "Requested font size too small, changing to minimum supported size";
} }
else if (mSize > Renderer::getScreenHeight()) { else if (mFontSize > Renderer::getScreenHeight() * 1.5f) {
mSize = static_cast<int>(Renderer::getScreenHeight()); mFontSize = Renderer::getScreenHeight() * 1.5f;
LOG(LogWarning) << "Requested font size too large, changing to maximum supported size"; LOG(LogWarning) << "Requested font size too large, changing to maximum supported size";
} }
@ -37,7 +36,7 @@ Font::Font(int size, const std::string& path)
initLibrary(); initLibrary();
// Always initialize ASCII characters. // Always initialize ASCII characters.
for (unsigned int i = 32; i < 128; ++i) for (unsigned int i = 32; i < 127; ++i)
getGlyph(i); getGlyph(i);
clearFaceCache(); clearFaceCache();
@ -47,7 +46,7 @@ Font::~Font()
{ {
unload(ResourceManager::getInstance()); unload(ResourceManager::getInstance());
auto fontEntry = sFontMap.find(std::pair<std::string, int>(mPath, mSize)); auto fontEntry = sFontMap.find(std::pair<std::string, float>(mPath, mFontSize));
if (fontEntry != sFontMap.cend()) if (fontEntry != sFontMap.cend())
sFontMap.erase(fontEntry); sFontMap.erase(fontEntry);
@ -58,50 +57,11 @@ Font::~Font()
} }
} }
void Font::initLibrary() std::shared_ptr<Font> Font::get(float size, const std::string& path)
{
assert(sLibrary == nullptr);
if (FT_Init_FreeType(&sLibrary)) {
sLibrary = nullptr;
LOG(LogError) << "Couldn't initialize FreeType";
}
}
std::vector<std::string> Font::getFallbackFontPaths()
{
std::vector<std::string> fontPaths;
// Standard fonts, let's include them here for exception handling purposes even though that's
// not really the correct location. (The application will crash if they are missing.)
ResourceManager::getInstance().getResourcePath(":/fonts/Akrobat-Regular.ttf");
ResourceManager::getInstance().getResourcePath(":/fonts/Akrobat-SemiBold.ttf");
ResourceManager::getInstance().getResourcePath(":/fonts/Akrobat-Bold.ttf");
// Vera sans Unicode.
fontPaths.push_back(ResourceManager::getInstance().getResourcePath(":/fonts/DejaVuSans.ttf"));
// GNU FreeFont monospaced.
fontPaths.push_back(ResourceManager::getInstance().getResourcePath(":/fonts/FreeMono.ttf"));
// Various languages, such as Japanese and Chinese.
fontPaths.push_back(
ResourceManager::getInstance().getResourcePath(":/fonts/DroidSansFallbackFull.ttf"));
// Korean.
fontPaths.push_back(
ResourceManager::getInstance().getResourcePath(":/fonts/NanumMyeongjo.ttf"));
// Font Awesome icon glyphs, used for various special symbols like stars, folders etc.
fontPaths.push_back(
ResourceManager::getInstance().getResourcePath(":/fonts/fontawesome-webfont.ttf"));
// This is only needed for some really rare special characters.
fontPaths.push_back(ResourceManager::getInstance().getResourcePath(":/fonts/Ubuntu-C.ttf"));
return fontPaths;
}
std::shared_ptr<Font> Font::get(int size, const std::string& path)
{ {
const std::string canonicalPath {Utils::FileSystem::getCanonicalPath(path)}; const std::string canonicalPath {Utils::FileSystem::getCanonicalPath(path)};
std::pair<std::string, int> def {canonicalPath.empty() ? getDefaultPath() : canonicalPath, const std::pair<std::string, float> def {
size}; canonicalPath.empty() ? getDefaultPath() : canonicalPath, size};
auto foundFont = sFontMap.find(def); auto foundFont = sFontMap.find(def);
if (foundFont != sFontMap.cend()) { if (foundFont != sFontMap.cend()) {
@ -109,7 +69,7 @@ std::shared_ptr<Font> Font::get(int size, const std::string& path)
return foundFont->second.lock(); return foundFont->second.lock();
} }
std::shared_ptr<Font> font {std::shared_ptr<Font>(new Font(def.second, def.first))}; std::shared_ptr<Font> font {new Font(def.second, def.first)};
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;
@ -117,11 +77,9 @@ std::shared_ptr<Font> Font::get(int size, const std::string& path)
glm::vec2 Font::sizeText(std::string text, float lineSpacing) glm::vec2 Font::sizeText(std::string text, float lineSpacing)
{ {
const float lineHeight {getHeight(lineSpacing)};
float lineWidth {0.0f}; float lineWidth {0.0f};
float highestWidth {0.0f}; float highestWidth {0.0f};
const float lineHeight {getHeight(lineSpacing)};
float y {lineHeight}; float y {lineHeight};
size_t i {0}; size_t i {0};
@ -147,14 +105,18 @@ glm::vec2 Font::sizeText(std::string text, float lineSpacing)
return glm::vec2 {highestWidth, y}; return glm::vec2 {highestWidth, y};
} }
std::string Font::getTextMaxWidth(std::string text, float maxWidth) int Font::loadGlyphs(const std::string& text)
{ {
float width {sizeText(text).x}; mMaxGlyphHeight = static_cast<int>(std::round(mFontSize));
while (width > maxWidth) {
text.pop_back(); for (size_t i = 0; i < text.length();) {
width = sizeText(text).x; unsigned int character {Utils::String::chars2Unicode(text, i)}; // Advances i.
Glyph* glyph {getGlyph(character)};
if (glyph->rows > mMaxGlyphHeight)
mMaxGlyphHeight = glyph->rows;
} }
return text; return mMaxGlyphHeight;
} }
TextCache* Font::buildTextCache(const std::string& text, TextCache* Font::buildTextCache(const std::string& text,
@ -185,11 +147,14 @@ TextCache* Font::buildTextCache(const std::string& text,
yBot = getHeight(1.5); yBot = getHeight(1.5);
} }
else { else {
// TODO: This is lacking some precision which is especially visible at higher resolutions
// like 4K where the text is not always placed entirely correctly vertically. Try to find
// a way to improve on this.
yTop = getGlyph('S')->bearing.y; yTop = getGlyph('S')->bearing.y;
yBot = getHeight(lineSpacing); yBot = getHeight(lineSpacing);
} }
float y {offset[1] + (yBot + yTop) / 2.0f}; float y {std::round(offset[1] + (yBot + yTop) / 2.0f)};
// Vertices by texture. // Vertices by texture.
std::map<FontTexture*, std::vector<Renderer::Vertex>> vertMap; std::map<FontTexture*, std::vector<Renderer::Vertex>> vertMap;
@ -254,14 +219,15 @@ TextCache* Font::buildTextCache(const std::string& text,
TextCache* cache {new TextCache()}; TextCache* cache {new TextCache()};
cache->vertexLists.resize(vertMap.size()); cache->vertexLists.resize(vertMap.size());
cache->metrics = {sizeText(text, lineSpacing)}; cache->metrics.size = {sizeText(text, lineSpacing)};
cache->metrics.maxGlyphHeight = mMaxGlyphHeight;
unsigned int i {0}; size_t i {0};
for (auto it = vertMap.cbegin(); it != vertMap.cend(); ++it) { for (auto it = vertMap.cbegin(); it != vertMap.cend(); ++it) {
TextCache::VertexList& vertList = cache->vertexLists.at(i); TextCache::VertexList& vertList {cache->vertexLists.at(i)};
vertList.textureIdPtr = &it->first->textureId; vertList.textureIdPtr = &it->first->textureId;
vertList.verts = it->second; vertList.verts = it->second;
++i;
} }
clearFaceCache(); clearFaceCache();
@ -287,220 +253,198 @@ void Font::renderTextCache(TextCache* cache)
} }
} }
std::string Font::wrapText(std::string text, float maxLength, float maxHeight, float lineSpacing) std::string Font::wrapText(const std::string& text,
const float maxLength,
const float maxHeight,
const float lineSpacing,
const bool multiLine)
{ {
assert(maxLength != 0.0f); assert(maxLength > 0.0f);
std::string out;
std::string line;
std::string word;
std::string abbreviatedWord;
std::string temp;
size_t space {0};
glm::vec2 textSize {0.0f, 0.0f};
const float dotsSize {sizeText("...").x};
const float lineHeight {getHeight(lineSpacing)}; const float lineHeight {getHeight(lineSpacing)};
float accumHeight {0.0f}; const float dotsWidth {sizeText("...").x};
const bool restrictHeight {maxHeight > 0.0f}; float accumHeight {lineHeight};
bool skipLastLine {false}; float lineWidth {0.0f};
float currLineLength {0.0f}; float charWidth {0.0f};
float lastSpacePos {0.0f};
unsigned int charID {0};
size_t cursor {0};
size_t lastSpace {0};
size_t spaceAccum {0};
size_t byteCount {0};
std::string wrappedText;
std::string charEntry;
std::vector<std::pair<size_t, float>> dotsSection;
bool addDots {false};
// While there's text or we still have text to render. for (size_t i = 0; i < text.length(); ++i) {
while (text.length() > 0) { if (text[i] == '\n') {
if (restrictHeight && accumHeight > maxHeight) if (!multiLine) {
break; addDots = true;
space = text.find_first_of(" \t\n");
if (space == std::string::npos) {
space = text.length() - 1;
}
else if (restrictHeight) {
if (text.at(space) == '\n')
accumHeight += lineHeight;
}
word = text.substr(0, space + 1);
text.erase(0, space + 1);
temp = line + word;
textSize = sizeText(temp);
// If the word will fit on the line, add it to our line and continue.
if (textSize.x <= maxLength) {
line = temp;
currLineLength = textSize.x;
continue;
}
else {
// If the word is too long to fit within maxLength then abbreviate it.
float wordSize {sizeText(word).x};
if (restrictHeight && currLineLength != 0.0f && maxHeight < lineHeight &&
wordSize > maxLength - textSize.x) {
// Multi-word lines.
if (maxLength - currLineLength + dotsSize < wordSize &&
sizeText(line).x + dotsSize > maxLength) {
while (sizeText(line).x + dotsSize > maxLength)
line.pop_back();
}
else {
while (word != "" && wordSize + dotsSize > maxLength - currLineLength) {
word.pop_back();
wordSize = sizeText(word).x;
}
line = line + word;
}
if (line.back() == ' ')
line.pop_back();
line.append("...");
break; break;
} }
if (wordSize > maxLength) { wrappedText.append("\n");
accumHeight += lineHeight;
lineWidth = 0.0f;
lastSpace = 0;
continue;
}
if (line != "" && line.back() != '\n') { charWidth = 0.0f;
if (restrictHeight) { byteCount = 0;
if (accumHeight + lineHeight > maxHeight) cursor = i;
continue;
accumHeight += lineHeight; // Needed to handle multi-byte Unicode characters.
} charID = Utils::String::chars2Unicode(text, cursor);
line.append("\n"); charEntry = text.substr(i, cursor - i);
Glyph* glyph {getGlyph(charID)};
if (glyph != nullptr) {
charWidth = glyph->advance.x;
byteCount = cursor - i;
}
else {
// Missing glyph.
continue;
}
if (multiLine && (charEntry == " " || charEntry == "\t")) {
lastSpace = i;
lastSpacePos = lineWidth;
}
if (lineWidth + charWidth <= maxLength) {
if (lineWidth + charWidth + dotsWidth > maxLength)
dotsSection.emplace_back(std::make_pair(byteCount, charWidth));
lineWidth += charWidth;
wrappedText.append(charEntry);
}
else if (!multiLine) {
addDots = true;
break;
}
else {
if (maxHeight == 0.0f || accumHeight < maxHeight) {
// New row.
float spaceOffset {0.0f};
if (lastSpace == wrappedText.size()) {
wrappedText.append("\n");
} }
else if (lastSpace != 0) {
const float cutTarget {wordSize - maxLength + dotsSize}; if (lastSpace + spaceAccum == wrappedText.size())
float cutSize {0.0f}; wrappedText.append("\n");
else
while (word != "" && cutSize < cutTarget) { wrappedText[lastSpace + spaceAccum] = '\n';
cutSize += sizeText(word.substr(word.size() - 1)).x; spaceOffset = lineWidth - lastSpacePos;
word.pop_back();
} }
else {
word.append("..."); if (lastSpace == 0)
line = line + word; ++spaceAccum;
continue; wrappedText.append("\n");
}
if (charEntry != " " && charEntry != "\t") {
wrappedText.append(charEntry);
lineWidth = charWidth;
}
else {
lineWidth = 0.0f;
}
accumHeight += lineHeight;
lineWidth += spaceOffset;
lastSpacePos = 0.0f;
lastSpace = 0;
} }
else { else {
out += line + '\n'; if (multiLine)
if (restrictHeight) addDots = true;
accumHeight += lineHeight; break;
if (restrictHeight && accumHeight > maxHeight) {
out.pop_back();
skipLastLine = true;
break;
}
}
line = word;
}
}
// Whatever's left should fit.
if (!skipLastLine)
out.append(line);
if (restrictHeight && out.back() == '\n')
out.pop_back();
// If the text has been abbreviated vertically then add "..." at the end of the string.
if (restrictHeight && accumHeight > maxHeight) {
if (out.back() != '\n') {
float cutSize {0.0f};
float cutTarget {sizeText(line).x - maxLength + dotsSize};
while (cutSize < cutTarget) {
cutSize += sizeText(out.substr(out.size() - 1)).x;
out.pop_back();
} }
} }
if (out.back() == ' ')
out.pop_back(); i = cursor - 1;
out.append("...");
} }
return out; if (addDots) {
if (!wrappedText.empty() && wrappedText.back() == ' ') {
lineWidth -= sizeText(" ").x;
wrappedText.pop_back();
}
else if (!wrappedText.empty() && wrappedText.back() == '\t') {
lineWidth -= sizeText("\t").x;
wrappedText.pop_back();
}
while (!wrappedText.empty() && !dotsSection.empty() && lineWidth + dotsWidth > maxLength) {
lineWidth -= dotsSection.back().second;
wrappedText.erase(wrappedText.length() - dotsSection.back().first);
dotsSection.pop_back();
}
if (!wrappedText.empty() && wrappedText.back() == ' ')
wrappedText.pop_back();
wrappedText.append("...");
}
return wrappedText;
} }
glm::vec2 Font::sizeWrappedText(std::string text, float xLen, float lineSpacing) glm::vec2 Font::getWrappedTextCursorOffset(const std::string& wrappedText,
const size_t stop,
const float lineSpacing)
{ {
text = wrapText(text, xLen);
return sizeText(text, lineSpacing);
}
glm::vec2 Font::getWrappedTextCursorOffset(std::string text,
float xLen,
size_t stop,
float lineSpacing)
{
std::string wrappedText {wrapText(text, xLen)};
float lineWidth {0.0f}; float lineWidth {0.0f};
float y {0.0f}; float yPos {0.0f};
size_t wrapCursor {0};
size_t cursor {0}; size_t cursor {0};
while (cursor < stop) { while (cursor < stop) {
unsigned int wrappedCharacter {Utils::String::chars2Unicode(wrappedText, wrapCursor)}; unsigned int character {Utils::String::chars2Unicode(wrappedText, cursor)};
unsigned int character {Utils::String::chars2Unicode(text, cursor)};
if (wrappedCharacter == '\n' && character != '\n') {
// This is where the wordwrap inserted a newline
// Reset lineWidth and increment y, but don't consume .a cursor character.
lineWidth = 0.0f;
y += getHeight(lineSpacing);
cursor = Utils::String::prevCursor(text, cursor); // Unconsume.
continue;
}
if (character == '\n') { if (character == '\n') {
lineWidth = 0.0f; lineWidth = 0.0f;
y += getHeight(lineSpacing); yPos += getHeight(lineSpacing);
continue; continue;
} }
Glyph* glyph = getGlyph(character); Glyph* glyph {getGlyph(character)};
if (glyph) if (glyph)
lineWidth += glyph->advance.x; lineWidth += glyph->advance.x;
} }
return glm::vec2 {lineWidth, y}; return glm::vec2 {lineWidth, yPos};
}
float Font::getHeight(float lineSpacing) const
{
// Return overall height including line spacing.
return mMaxGlyphHeight * lineSpacing;
} }
float Font::getLetterHeight() float Font::getLetterHeight()
{ {
Glyph* glyph {getGlyph('S')}; if (mLetterHeight == 0.0f)
assert(glyph); return mFontSize * 0.737f; // Only needed if face does not contain the letter 'S'.
return glyph->texSize.y * glyph->texture->textureSize.y; else
return mLetterHeight;
} }
std::shared_ptr<Font> Font::getFromTheme(const ThemeData::ThemeElement* elem, 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 bool legacyTheme,
const float sizeMultiplier)
{ {
mLegacyTheme = legacyTheme;
using namespace ThemeFlags; using namespace ThemeFlags;
if (!(properties & FONT_PATH) && !(properties & FONT_SIZE)) if (!(properties & FONT_PATH) && !(properties & FONT_SIZE))
return orig; return orig;
std::shared_ptr<Font> font; float size {static_cast<float>(orig ? orig->mFontSize : FONT_SIZE_MEDIUM)};
int size {static_cast<int>(orig ? orig->mSize : FONT_SIZE_MEDIUM)};
std::string path {orig ? orig->mPath : getDefaultPath()}; std::string path {orig ? orig->mPath : getDefaultPath()};
float sh {static_cast<float>(Renderer::getScreenHeight())}; float screenHeight {static_cast<float>(Renderer::getScreenHeight())};
// Make sure the size is not unreasonably large (which may be caused by a mistake in the if (properties & FONT_SIZE && elem->has("fontSize")) {
// theme configuration). size = glm::clamp(screenHeight * elem->get<float>("fontSize"), screenHeight * 0.001f,
if (properties & FONT_SIZE && elem->has("fontSize")) screenHeight * 1.5f);
size = glm::clamp(static_cast<int>(sh * elem->get<float>("fontSize")), 0, // This is used by the carousel where the itemScale property also scales the font size.
static_cast<int>(Renderer::getInstance()->getScreenHeight())); size *= sizeMultiplier;
}
if (maxHeight != 0.0f && size > maxHeight)
size = maxHeight;
if (properties & FONT_PATH && elem->has("fontPath")) if (properties & FONT_PATH && elem->has("fontPath"))
path = elem->get<std::string>("fontPath"); path = elem->get<std::string>("fontPath");
@ -513,14 +457,17 @@ std::shared_ptr<Font> Font::getFromTheme(const ThemeData::ThemeElement* elem,
path = getDefaultPath(); path = getDefaultPath();
} }
return get(size, path); if (mLegacyTheme)
return get(std::floor(size), path);
else
return get(size, path);
} }
size_t Font::getMemUsage() const size_t Font::getMemUsage() const
{ {
size_t memUsage {0}; size_t memUsage {0};
for (auto it = mTextures.cbegin(); it != mTextures.cend(); ++it) for (auto it = mTextures.cbegin(); it != mTextures.cend(); ++it)
memUsage += it->textureSize.x * it->textureSize.y * 4; memUsage += (*it)->textureSize.x * (*it)->textureSize.y * 4;
for (auto it = mFaceCache.cbegin(); it != mFaceCache.cend(); ++it) for (auto it = mFaceCache.cbegin(); it != mFaceCache.cend(); ++it)
memUsage += it->second->data.length; memUsage += it->second->data.length;
@ -546,38 +493,43 @@ size_t Font::getTotalMemUsage()
return total; return total;
} }
Font::FontTexture::FontTexture(const int mSize) std::vector<std::string> Font::getFallbackFontPaths()
{
std::vector<std::string> fontPaths;
// Default application fonts.
ResourceManager::getInstance().getResourcePath(":/fonts/Akrobat-Regular.ttf");
ResourceManager::getInstance().getResourcePath(":/fonts/Akrobat-SemiBold.ttf");
ResourceManager::getInstance().getResourcePath(":/fonts/Akrobat-Bold.ttf");
// Vera sans Unicode.
fontPaths.push_back(ResourceManager::getInstance().getResourcePath(":/fonts/DejaVuSans.ttf"));
// GNU FreeFont monospaced.
fontPaths.push_back(ResourceManager::getInstance().getResourcePath(":/fonts/FreeMono.ttf"));
// Various languages, such as Japanese and Chinese.
fontPaths.push_back(
ResourceManager::getInstance().getResourcePath(":/fonts/DroidSansFallbackFull.ttf"));
// Korean.
fontPaths.push_back(
ResourceManager::getInstance().getResourcePath(":/fonts/NanumMyeongjo.ttf"));
// Font Awesome icon glyphs, used for various special symbols like stars, folders etc.
fontPaths.push_back(
ResourceManager::getInstance().getResourcePath(":/fonts/fontawesome-webfont.ttf"));
// This is only needed for some really rare special characters.
fontPaths.push_back(ResourceManager::getInstance().getResourcePath(":/fonts/Ubuntu-C.ttf"));
return fontPaths;
}
Font::FontTexture::FontTexture(const int mFontSize)
{ {
textureId = 0; textureId = 0;
// This is a hack to add some extra texture size when running at very low resolutions. If not
// doing this, the use of fallback fonts (such as Japanese characters) could result in the
// texture not fitting the glyphs.
int extraTextureSize {0};
const float screenSizeModifier {
std::min(Renderer::getScreenWidthModifier(), Renderer::getScreenHeightModifier())};
if (screenSizeModifier < 0.2f)
extraTextureSize += 6;
if (screenSizeModifier < 0.45f)
extraTextureSize += 4;
// It's not entirely clear if the 20 and 16 constants are correct, but they seem to provide
// a texture buffer large enough to hold the fonts. This logic is obviously a hack though
// and needs to be properly reviewed and improved.
textureSize = glm::ivec2 {mSize * (20 + extraTextureSize), mSize * (16 + extraTextureSize / 2)};
// Make sure the size is not unreasonably large (which may be caused by a mistake in the
// theme configuration).
if (textureSize.x > static_cast<int>(Renderer::getScreenWidth()) * 10)
textureSize.x =
glm::clamp(textureSize.x, 0, static_cast<int>(Renderer::getScreenWidth()) * 10);
if (textureSize.y > static_cast<int>(Renderer::getScreenHeight()) * 10)
textureSize.y =
glm::clamp(textureSize.y, 0, static_cast<int>(Renderer::getScreenHeight()) * 10);
writePos = glm::ivec2 {0, 0};
rowHeight = 0; rowHeight = 0;
writePos = glm::ivec2 {0, 0};
// Set the texture to a reasonable size, if we run out of space for adding glyphs then
// more textures will be created dynamically.
textureSize = glm::ivec2 {mFontSize * 6, mFontSize * 6};
} }
Font::FontTexture::~FontTexture() Font::FontTexture::~FontTexture()
@ -593,19 +545,18 @@ bool Font::FontTexture::findEmpty(const glm::ivec2& size, glm::ivec2& cursor_out
if (writePos.x + size.x >= textureSize.x && if (writePos.x + size.x >= textureSize.x &&
writePos.y + rowHeight + size.y + 1 < textureSize.y) { writePos.y + rowHeight + size.y + 1 < textureSize.y) {
// Row full, but it should fit on the next row so move the cursor there. // Row is full, but the glyph should fit on the next row so move the cursor there.
// Leave 1px of space between glyphs. // Leave 1 pixel of space between glyphs so that pixels from adjacent glyphs will
// not get sampled during scaling which would lead to edge artifacts.
writePos = glm::ivec2 {0, writePos.y + rowHeight + 1}; writePos = glm::ivec2 {0, writePos.y + rowHeight + 1};
rowHeight = 0; rowHeight = 0;
} }
if (writePos.x + size.x >= textureSize.x || writePos.y + size.y >= textureSize.y) { if (writePos.x + size.x >= textureSize.x || writePos.y + size.y >= textureSize.y)
// Nope, still won't fit. return false; // No it still won't fit.
return false;
}
cursor_out = writePos; cursor_out = writePos;
// Leave 1px of space between glyphs. // Leave 1 pixel of space between glyphs.
writePos.x += size.x + 1; writePos.x += size.x + 1;
if (size.y > rowHeight) if (size.y > rowHeight)
@ -617,9 +568,13 @@ bool Font::FontTexture::findEmpty(const glm::ivec2& size, glm::ivec2& cursor_out
void Font::FontTexture::initTexture() void Font::FontTexture::initTexture()
{ {
assert(textureId == 0); assert(textureId == 0);
// Create a black texture with zero alpha value so that the single-pixel spaces between the
// glyphs will not be visible. That would otherwise lead to edge artifacts as these pixels
// would get sampled during scaling.
std::vector<uint8_t> texture(textureSize.x * textureSize.y * 4, 0);
textureId = textureId =
Renderer::getInstance()->createTexture(Renderer::TextureType::RED, true, false, false, Renderer::getInstance()->createTexture(Renderer::TextureType::RED, true, false, false,
false, textureSize.x, textureSize.y, nullptr); false, textureSize.x, textureSize.y, &texture[0]);
} }
void Font::FontTexture::deinitTexture() void Font::FontTexture::deinitTexture()
@ -630,15 +585,19 @@ void Font::FontTexture::deinitTexture()
} }
} }
Font::FontFace::FontFace(ResourceData&& d, int size) Font::FontFace::FontFace(ResourceData&& d, float size, const std::string& path)
: data {d} : data {d}
{ {
int err { if (FT_New_Memory_Face(sLibrary, d.ptr.get(), static_cast<FT_Long>(d.length), 0, &face) != 0) {
FT_New_Memory_Face(sLibrary, data.ptr.get(), static_cast<FT_Long>(data.length), 0, &face)}; LOG(LogError) << "Couldn't load font file \"" << path << "\"";
assert(!err); Utils::Platform::emergencyShutdown();
}
if (!err) // Even though a fractional font size can be requested, the glyphs will always be rounded
FT_Set_Pixel_Sizes(face, 0, size); // to integers. It's not useless to call FT_Set_Char_Size() instead of FT_Set_Pixel_Sizes()
// though as the glyphs will still be much more evenely sized across different resolutions.
FT_Set_Char_Size(face, static_cast<FT_F26Dot6>(0.0f), static_cast<FT_F26Dot6>(size * 64.0f), 0,
0);
} }
Font::FontFace::~FontFace() Font::FontFace::~FontFace()
@ -647,11 +606,21 @@ Font::FontFace::~FontFace()
FT_Done_Face(face); FT_Done_Face(face);
} }
void Font::initLibrary()
{
assert(sLibrary == nullptr);
if (FT_Init_FreeType(&sLibrary)) {
sLibrary = nullptr;
LOG(LogError) << "Couldn't initialize FreeType";
}
}
void Font::rebuildTextures() void Font::rebuildTextures()
{ {
// Recreate OpenGL textures. // Recreate OpenGL textures.
for (auto it = mTextures.begin(); it != mTextures.end(); ++it) for (auto it = mTextures.begin(); it != mTextures.end(); ++it)
it->initTexture(); (*it)->initTexture();
// Re-upload the texture data. // Re-upload the texture data.
for (auto it = mGlyphMap.cbegin(); it != mGlyphMap.cend(); ++it) { for (auto it = mGlyphMap.cbegin(); it != mGlyphMap.cend(); ++it) {
@ -678,7 +647,7 @@ void Font::rebuildTextures()
void Font::unloadTextures() void Font::unloadTextures()
{ {
for (auto it = mTextures.begin(); it != mTextures.end(); ++it) for (auto it = mTextures.begin(); it != mTextures.end(); ++it)
it->deinitTexture(); (*it)->deinitTexture();
} }
void Font::getTextureForNewGlyph(const glm::ivec2& glyphSize, void Font::getTextureForNewGlyph(const glm::ivec2& glyphSize,
@ -686,29 +655,19 @@ void Font::getTextureForNewGlyph(const glm::ivec2& glyphSize,
glm::ivec2& cursor_out) glm::ivec2& cursor_out)
{ {
if (mTextures.size()) { if (mTextures.size()) {
// Check if the most recent texture has space. // Check if the most recent texture has space available for the glyph.
tex_out = &mTextures.back(); tex_out = mTextures.back().get();
// Will this one work? // Will this one work?
if (tex_out->findEmpty(glyphSize, cursor_out)) if (tex_out->findEmpty(glyphSize, cursor_out))
return; // Yes. return; // Yes.
} }
// This should never happen, assuming the texture size is large enough to fit the font, mTextures.emplace_back(std::make_unique<FontTexture>(static_cast<int>(std::round(mFontSize))));
// as set in the FontTexture constructor. In the unlikely situation that it still happens, tex_out = mTextures.back().get();
// setting the texture to nullptr makes sure the application doesn't crash and that the
// user is clearly notified of the problem by the fact that the glyph/character will be
// completely missing.
if (mGlyphMap.size() > 0) {
tex_out = nullptr;
return;
}
mTextures.push_back(FontTexture(mSize));
tex_out = &mTextures.back();
tex_out->initTexture(); tex_out->initTexture();
bool ok = tex_out->findEmpty(glyphSize, cursor_out); bool ok {tex_out->findEmpty(glyphSize, cursor_out)};
if (!ok) { if (!ok) {
LOG(LogError) << "Glyph too big to fit on a new texture (glyph size > " LOG(LogError) << "Glyph too big to fit on a new texture (glyph size > "
<< tex_out->textureSize.x << ", " << tex_out->textureSize.y << ")"; << tex_out->textureSize.x << ", " << tex_out->textureSize.y << ")";
@ -720,18 +679,15 @@ FT_Face Font::getFaceForChar(unsigned int id)
{ {
static const std::vector<std::string> fallbackFonts {getFallbackFontPaths()}; static const std::vector<std::string> fallbackFonts {getFallbackFontPaths()};
// Look through our current font + fallback fonts to see if any have the // Look for the glyph in our current font and then in the fallback fonts if needed.
// glyph we're looking for.
for (unsigned int i = 0; i < fallbackFonts.size() + 1; ++i) { for (unsigned int i = 0; i < fallbackFonts.size() + 1; ++i) {
auto fit = mFaceCache.find(i); auto fit = mFaceCache.find(i);
// Doesn't exist yet.
if (fit == mFaceCache.cend()) { if (fit == mFaceCache.cend()) {
// i == 0 -> mPath
// Otherwise, take from fallbackFonts.
const std::string& path {i == 0 ? mPath : fallbackFonts.at(i - 1)}; const std::string& path {i == 0 ? mPath : fallbackFonts.at(i - 1)};
ResourceData data {ResourceManager::getInstance().getFileData(path)}; ResourceData data {ResourceManager::getInstance().getFileData(path)};
mFaceCache[i] = std::unique_ptr<FontFace>(new FontFace(std::move(data), mSize)); mFaceCache[i] =
std::unique_ptr<FontFace>(new FontFace(std::move(data), mFontSize, mPath));
fit = mFaceCache.find(i); fit = mFaceCache.find(i);
} }
@ -739,18 +695,18 @@ FT_Face Font::getFaceForChar(unsigned int id)
return fit->second->face; return fit->second->face;
} }
// Nothing has a valid glyph - return the "real" face so we get a "missing" character. // Couldn't find a valid glyph, return the "real" face so we get a "missing" character.
return mFaceCache.cbegin()->second->face; return mFaceCache.cbegin()->second->face;
} }
Font::Glyph* Font::getGlyph(const unsigned int id) Font::Glyph* Font::getGlyph(const unsigned int id)
{ {
// Is it already loaded? // Check if the glyph has already been loaded.
auto it = mGlyphMap.find(id); auto it = mGlyphMap.find(id);
if (it != mGlyphMap.cend()) if (it != mGlyphMap.cend())
return &it->second; return &it->second;
// Nope, need to make a glyph. // We need to create a new entry.
FT_Face face {getFaceForChar(id)}; FT_Face face {getFaceForChar(id)};
if (!face) { if (!face) {
LOG(LogError) << "Couldn't find appropriate font face for character " << id << " for font " LOG(LogError) << "Couldn't find appropriate font face for character " << id << " for font "
@ -758,28 +714,39 @@ Font::Glyph* Font::getGlyph(const unsigned int id)
return nullptr; return nullptr;
} }
FT_GlyphSlot g {face->glyph}; const FT_GlyphSlot glyphSlot {face->glyph};
if (FT_Load_Char(face, id, FT_LOAD_RENDER)) { // TODO: Evaluate/test hinting when HarfBuzz has been added.
// If the font does not contain hinting information then force the use of the automatic
// hinter that is built into FreeType.
// const bool hasHinting {static_cast<bool>(glyphSlot->face->face_flags & FT_FACE_FLAG_HINTER)};
const bool hasHinting {true};
if (FT_Load_Char(face, id,
(hasHinting ?
FT_LOAD_RENDER :
FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT))) {
LOG(LogError) << "Couldn't find glyph for character " << id << " for font " << mPath LOG(LogError) << "Couldn't find glyph for character " << id << " for font " << mPath
<< ", size " << mSize; << ", size " << mFontSize;
return nullptr; return nullptr;
} }
glm::ivec2 glyphSize {g->bitmap.width, g->bitmap.rows};
FontTexture* tex {nullptr}; FontTexture* tex {nullptr};
glm::ivec2 cursor {0, 0}; glm::ivec2 cursor {0, 0};
const glm::ivec2 glyphSize {glyphSlot->bitmap.width, glyphSlot->bitmap.rows};
getTextureForNewGlyph(glyphSize, tex, cursor); getTextureForNewGlyph(glyphSize, tex, cursor);
// getTextureForNewGlyph can fail if the glyph is bigger than the max texture // This should (hopefully) never occur as size constraints are enforced earlier on.
// size (absurdly large font size).
if (tex == nullptr) { if (tex == nullptr) {
LOG(LogError) << "Couldn't create glyph for character " << id << " for font " << mPath LOG(LogError) << "Couldn't create glyph for character " << id << " for font " << mPath
<< ", size " << mSize << " (no suitable texture found)"; << ", size " << mFontSize << " (no suitable texture found)";
return nullptr; return nullptr;
} }
// Use the letter 'S' as a size reference.
if (mLetterHeight == 0 && id == 'S')
mLetterHeight = static_cast<float>(glyphSize.y);
// Create glyph. // Create glyph.
Glyph& glyph {mGlyphMap[id]}; Glyph& glyph {mGlyphMap[id]};
@ -788,21 +755,19 @@ Font::Glyph* Font::getGlyph(const unsigned int id)
cursor.y / static_cast<float>(tex->textureSize.y)}; cursor.y / static_cast<float>(tex->textureSize.y)};
glyph.texSize = glm::vec2 {glyphSize.x / static_cast<float>(tex->textureSize.x), glyph.texSize = glm::vec2 {glyphSize.x / static_cast<float>(tex->textureSize.x),
glyphSize.y / static_cast<float>(tex->textureSize.y)}; glyphSize.y / static_cast<float>(tex->textureSize.y)};
glyph.advance = glm::vec2 {static_cast<float>(glyphSlot->metrics.horiAdvance) / 64.0f,
glyph.advance = glm::vec2 {static_cast<float>(g->metrics.horiAdvance) / 64.0f, static_cast<float>(glyphSlot->metrics.vertAdvance) / 64.0f};
static_cast<float>(g->metrics.vertAdvance) / 64.0f}; glyph.bearing = glm::vec2 {static_cast<float>(glyphSlot->metrics.horiBearingX) / 64.0f,
glyph.bearing = glm::vec2 {static_cast<float>(g->metrics.horiBearingX) / 64.0f, static_cast<float>(glyphSlot->metrics.horiBearingY) / 64.0f};
static_cast<float>(g->metrics.horiBearingY) / 64.0f}; glyph.rows = glyphSlot->bitmap.rows;
// Upload glyph bitmap to texture. // Upload glyph bitmap to texture.
mRenderer->updateTexture(tex->textureId, Renderer::TextureType::RED, cursor.x, cursor.y, mRenderer->updateTexture(tex->textureId, Renderer::TextureType::RED, cursor.x, cursor.y,
glyphSize.x, glyphSize.y, g->bitmap.buffer); glyphSize.x, glyphSize.y, glyphSlot->bitmap.buffer);
// Update max glyph height. if (glyphSize.y > mLegacyMaxGlyphHeight)
if (glyphSize.y > mMaxGlyphHeight) mLegacyMaxGlyphHeight = glyphSize.y;
mMaxGlyphHeight = glyphSize.y;
// Done.
return &glyph; return &glyph;
} }

View file

@ -4,7 +4,7 @@
// Font.h // Font.h
// //
// Loading, unloading, caching and rendering of fonts. // Loading, unloading, caching and rendering of fonts.
// Also functions for word wrapping and similar. // Also functions for text wrapping and similar.
// //
#ifndef ES_CORE_RESOURCES_FONT_H #ifndef ES_CORE_RESOURCES_FONT_H
@ -21,16 +21,10 @@
class TextCache; class TextCache;
// clang-format off #define FONT_SIZE_MINI 0.030f * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())
#define FONT_SIZE_MINI (static_cast<unsigned int>(0.030f * \ #define FONT_SIZE_SMALL 0.035f * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())
std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()))) #define FONT_SIZE_MEDIUM 0.045f * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())
#define FONT_SIZE_SMALL (static_cast<unsigned int>(0.035f * \ #define FONT_SIZE_LARGE 0.085f * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())
std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())))
#define FONT_SIZE_MEDIUM (static_cast<unsigned int>(0.045f * \
std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())))
#define FONT_SIZE_LARGE (static_cast<unsigned int>(0.085f * \
std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())))
// clang-format on
#define FONT_PATH_LIGHT ":/fonts/Akrobat-Regular.ttf" #define FONT_PATH_LIGHT ":/fonts/Akrobat-Regular.ttf"
#define FONT_PATH_REGULAR ":/fonts/Akrobat-SemiBold.ttf" #define FONT_PATH_REGULAR ":/fonts/Akrobat-SemiBold.ttf"
@ -42,15 +36,19 @@ class Font : public IReloadable
{ {
public: public:
virtual ~Font(); virtual ~Font();
static void initLibrary(); static std::shared_ptr<Font> get(float size, const std::string& path = getDefaultPath());
std::vector<std::string> getFallbackFontPaths();
static std::shared_ptr<Font> get(int size, const std::string& path = getDefaultPath());
// 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);
// Returns the portion of a string that fits within the passed argument maxWidth. // Used to determine mMaxGlyphHeight upfront which is needed for accurate text sizing by
std::string getTextMaxWidth(std::string text, float maxWidth); // wrapText and buildTextCache. This is required as the requested font height is not
// guaranteed and can be exceeded by a few pixels for some glyphs.
int loadGlyphs(const std::string& text);
// This is needed to retain a bug from the legacy theme engine where lineSpacing is not
// sized correctly when using automatic text element sizing.
void useLegacyMaxGlyphHeight() { mMaxGlyphHeight = mLegacyMaxGlyphHeight; }
TextCache* buildTextCache(const std::string& text, TextCache* buildTextCache(const std::string& text,
float offsetX, float offsetX,
@ -69,34 +67,35 @@ public:
void renderTextCache(TextCache* cache); void renderTextCache(TextCache* cache);
// Inserts newlines into text to make it wrap properly. // Inserts newlines to make text wrap properly and also abbreviates single-line text.
std::string wrapText(std::string text, std::string wrapText(const std::string& text,
float maxLength, const float maxLength,
float maxHeight = 0.0f, const float maxHeight = 0.0f,
float lineSpacing = 1.5f); const float lineSpacing = 1.5f,
const bool multiLine = false);
// Returns the expected size of a string after wrapping is applied. // Returns the position of the cursor after moving it to the stop position.
glm::vec2 sizeWrappedText(std::string text, float xLen, float lineSpacing = 1.5f); glm::vec2 getWrappedTextCursorOffset(const std::string& wrappedText,
const size_t stop,
const float lineSpacing = 1.5f);
// Returns the position of the cursor after moving a "cursor" amount of characters. // Return overall height including line spacing.
glm::vec2 getWrappedTextCursorOffset(std::string text, float getHeight(float lineSpacing = 1.5f) const { return mMaxGlyphHeight * lineSpacing; }
float xLen,
size_t cursor,
float lineSpacing = 1.5f);
float getHeight(float lineSpacing = 1.5f) const;
float getLetterHeight(); float getLetterHeight();
void reload(ResourceManager& rm) override { rebuildTextures(); } void reload(ResourceManager& rm) override { rebuildTextures(); }
void unload(ResourceManager& rm) override { unloadTextures(); } void unload(ResourceManager& rm) override { unloadTextures(); }
int getSize() const { return mSize; } const float getSize() const { return mFontSize; }
const std::string& getPath() const { return mPath; } const std::string& getPath() const { return mPath; }
static std::string getDefaultPath() { return FONT_PATH_REGULAR; } static std::string getDefaultPath() { return FONT_PATH_REGULAR; }
static std::shared_ptr<Font> getFromTheme(const ThemeData::ThemeElement* elem, static std::shared_ptr<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 = 0.0f,
const bool legacyTheme = false,
const float sizeMultiplier = 1.0f);
// Returns an approximation of VRAM used by this font's texture (in bytes). // Returns an approximation of VRAM used by this font's texture (in bytes).
size_t getMemUsage() const; size_t getMemUsage() const;
@ -104,20 +103,16 @@ public:
static size_t getTotalMemUsage(); static size_t getTotalMemUsage();
private: private:
Renderer* mRenderer; Font(float size, const std::string& path);
static FT_Library sLibrary; static void initLibrary();
static std::map<std::pair<std::string, int>, std::weak_ptr<Font>> sFontMap;
Font(int size, const std::string& path);
struct FontTexture { struct FontTexture {
unsigned int textureId; unsigned int textureId;
glm::ivec2 textureSize; glm::ivec2 textureSize;
glm::ivec2 writePos; glm::ivec2 writePos;
int rowHeight; int rowHeight;
FontTexture(const int mSize); FontTexture(const int mFontSize);
~FontTexture(); ~FontTexture();
bool findEmpty(const glm::ivec2& size, glm::ivec2& cursor_out); bool findEmpty(const glm::ivec2& size, glm::ivec2& cursor_out);
@ -126,8 +121,7 @@ private:
// updating textureId. // updating textureId.
void initTexture(); void initTexture();
// Deinitializes the OpenGL texture if any exists, is automatically called // Deinitializes any existing OpenGL textures, is automatically called in destructor.
// in the destructor.
void deinitTexture(); void deinitTexture();
}; };
@ -135,10 +129,19 @@ private:
const ResourceData data; const ResourceData data;
FT_Face face; FT_Face face;
FontFace(ResourceData&& d, int size); FontFace(ResourceData&& d, float size, const std::string& path);
virtual ~FontFace(); virtual ~FontFace();
}; };
struct Glyph {
FontTexture* texture;
glm::vec2 texPos;
glm::vec2 texSize; // In texels.
glm::vec2 advance;
glm::vec2 bearing;
int rows;
};
// Completely recreate the texture data for all textures based on mGlyphs information. // Completely recreate the texture data for all textures based on mGlyphs information.
void rebuildTextures(); void rebuildTextures();
void unloadTextures(); void unloadTextures();
@ -147,34 +150,31 @@ private:
FontTexture*& tex_out, FontTexture*& tex_out,
glm::ivec2& cursor_out); glm::ivec2& cursor_out);
std::map<unsigned int, std::unique_ptr<FontFace>> mFaceCache; std::vector<std::string> getFallbackFontPaths();
FT_Face getFaceForChar(unsigned int id); FT_Face getFaceForChar(unsigned int id);
void clearFaceCache() { mFaceCache.clear(); }
struct Glyph {
FontTexture* texture;
glm::vec2 texPos;
glm::vec2 texSize; // In texels.
glm::vec2 advance;
glm::vec2 bearing;
};
std::vector<FontTexture> mTextures;
std::map<unsigned int, Glyph> mGlyphMap;
Glyph* getGlyph(const unsigned int id); Glyph* getGlyph(const unsigned int id);
int mSize;
int mMaxGlyphHeight;
const std::string mPath;
float getNewlineStartOffset(const std::string& text, float getNewlineStartOffset(const std::string& text,
const unsigned int& charStart, const unsigned int& charStart,
const float& xLen, const float& xLen,
const Alignment& alignment); const Alignment& alignment);
friend TextCache; void clearFaceCache() { mFaceCache.clear(); }
static inline FT_Library sLibrary {nullptr};
static inline std::map<std::pair<std::string, float>, std::weak_ptr<Font>> sFontMap;
static inline bool mLegacyTheme {false};
Renderer* mRenderer;
std::vector<std::unique_ptr<FontTexture>> mTextures;
std::map<unsigned int, std::unique_ptr<FontFace>> mFaceCache;
std::map<unsigned int, Glyph> mGlyphMap;
const std::string mPath;
float mFontSize;
float mLetterHeight;
int mMaxGlyphHeight;
int mLegacyMaxGlyphHeight;
}; };
// Used to store a sort of "pre-rendered" string. // Used to store a sort of "pre-rendered" string.
@ -188,6 +188,7 @@ class TextCache
public: public:
struct CacheMetrics { struct CacheMetrics {
glm::vec2 size; glm::vec2 size;
int maxGlyphHeight;
} metrics; } metrics;
void setColor(unsigned int color); void setColor(unsigned int color);
@ -199,8 +200,6 @@ public:
protected: protected:
struct VertexList { struct VertexList {
std::vector<Renderer::Vertex> verts; std::vector<Renderer::Vertex> verts;
// This is a pointer because the texture ID can change during
// deinit/reinit (when launching a game).
unsigned int* textureIdPtr; unsigned int* textureIdPtr;
}; };

View file

@ -24,13 +24,7 @@ TextureDataManager::TextureDataManager()
data[i * 4 + 3] = 0; data[i * 4 + 3] = 0;
} }
mBlank->initFromRGBA(data, 5, 5); mBlank->initFromRGBA(data, 5, 5);
mLoader = new TextureLoader; mLoader = std::make_unique<TextureLoader>();
}
TextureDataManager::~TextureDataManager()
{
// Delete TextureLoader object when destroyed.
delete mLoader;
} }
std::shared_ptr<TextureData> TextureDataManager::add(const TextureResource* key, bool tiled) std::shared_ptr<TextureData> TextureDataManager::add(const TextureResource* key, bool tiled)

View file

@ -63,7 +63,6 @@ class TextureDataManager
{ {
public: public:
TextureDataManager(); TextureDataManager();
~TextureDataManager();
std::shared_ptr<TextureData> add(const TextureResource* key, bool tiled); std::shared_ptr<TextureData> add(const TextureResource* key, bool tiled);
@ -90,7 +89,7 @@ private:
std::map<const TextureResource*, std::list<std::shared_ptr<TextureData>>::const_iterator> std::map<const TextureResource*, std::list<std::shared_ptr<TextureData>>::const_iterator>
mTextureLookup; mTextureLookup;
std::shared_ptr<TextureData> mBlank; std::shared_ptr<TextureData> mBlank;
TextureLoader* mLoader; std::unique_ptr<TextureLoader> mLoader;
}; };
#endif // ES_CORE_RESOURCES_TEXTURE_DATA_MANAGER_H #endif // ES_CORE_RESOURCES_TEXTURE_DATA_MANAGER_H

View file

@ -26,17 +26,18 @@ unset(CMAKE_EXE_LINKER_FLAGS)
if(WIN32) if(WIN32)
if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) if(CMAKE_CXX_COMPILER_ID MATCHES MSVC)
# Disable DLL interface warnings for LunaSVG. # Disable DLL interface warnings.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4251") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4251")
else() else()
# Strip the DLL files when building with MinGW. # Strip the DLL files when building with MinGW.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s")
endif() endif()
if (WIN32) endif()
set(BUILD_SHARED_LIBS ON)
else() if (WIN32)
set(BUILD_SHARED_LIBS OFF) set(BUILD_SHARED_LIBS ON)
endif() else()
set(BUILD_SHARED_LIBS OFF)
endif() endif()
add_subdirectory(lunasvg EXCLUDE_FROM_ALL) add_subdirectory(lunasvg EXCLUDE_FROM_ALL)

View file

@ -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">
<circle id="outline" cx="32" cy="32" r="28" fill="none" stroke="#fff" stroke-width="2"/>
<path id="button_1" d="m32 4a28 28 0 0 0-28 28 28 28 0 0 0 28 28 28 28 0 0 0 28-28 28 28 0 0 0-28-28zm-0.986328 13.917969h5.878906v28.554687h-6.738281v-16.679687l0.214844-4.101563c-1.471355 1.523438-2.610677 2.708334-3.417969 3.554688l-3.730469-4.082032 7.792969-7.246093z" fill="#fff"/>
</svg>

Before

Width:  |  Height:  |  Size: 523 B

View file

@ -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">
<circle id="outline" cx="32" cy="32" r="28" fill="none" stroke="#fff" stroke-width="2"/>
<path id="button_2" d="m32 4a28 28 0 0 0-28 28 28 28 0 0 0 28 28 28 28 0 0 0 28-28 28 28 0 0 0-28-28zm-0.126953 13.527344c2.591146 0 4.563802 0.703125 5.917969 2.109375 1.354166 1.393229 2.03125 3.307291 2.03125 5.742187 0 1.601563-0.286459 3.131511-0.859375 4.589844-0.572917 1.458333-1.341146 2.897135-2.304688 4.316406-0.950521 1.419271-2.643229 3.671875-5.078125 6.757813h8.828125v5.429687h-16.523437v-4.296875l5.078125-7.1875c0.690104-0.976562 1.217448-1.751302 1.582031-2.324219 0.377604-0.572916 0.794271-1.282552 1.25-2.128906 0.859375-1.614583 1.289062-3.248698 1.289062-4.902344 0-0.898437-0.15625-1.582031-0.46875-2.050781-0.3125-0.481771-0.735677-0.722656-1.269531-0.722656-0.625 0-1.25 0.266927-1.875 0.800781-0.546875 0.481771-1.028646 0.957032-1.445312 1.425782-0.416667 0.46875-0.638021 0.722656-0.664063 0.761718l-3.769531-3.984375c1.328125-1.458333 2.630208-2.545573 3.90625-3.261719 1.289062-0.716145 2.747396-1.074218 4.375-1.074218z" fill="#fff"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -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">
<circle id="outline" cx="32" cy="32" r="28" fill="none" stroke="#fff" stroke-width="2"/>
<path id="button_3" d="m32 4a28 28 0 0 0-28 28 28 28 0 0 0 28 28 28 28 0 0 0 28-28 28 28 0 0 0-28-28zm-0.439453 13.527344c1.536458 0 2.916667 0.260416 4.140625 0.78125 1.236979 0.507812 2.233073 1.308594 2.988281 2.402344 0.768229 1.080729 1.152344 2.402343 1.152344 3.964843 0 1.549479-0.384115 2.910156-1.152344 4.082031-0.755208 1.158855-1.842448 2.044271-3.261719 2.65625 1.601563 0.442709 2.825521 1.223959 3.671875 2.34375 0.859375 1.119792 1.289063 2.552084 1.289063 4.296876 0 2.747395-0.898438 4.902343-2.695313 6.464843-1.796875 1.549479-4.147135 2.324219-7.050781 2.324219s-5.234375-0.455729-6.992187-1.367188v-5.664062c1.40625 0.807292 2.890625 1.373698 4.453125 1.699219 0.664062 0.130208 1.295573 0.195312 1.894531 0.195312 1.210937 0 2.115885-0.305989 2.714844-0.917969 0.598958-0.611979 0.898437-1.57552 0.898437-2.890624 0-2.48698-1.425781-3.730469-4.277344-3.730469h-1.328125v-4.6875h1.269532c1.25 0 2.20052-0.319011 2.851562-0.957031 0.651042-0.651042 0.976563-1.601563 0.976563-2.851563 0-0.950521-0.182292-1.673177-0.546875-2.167969-0.364584-0.494791-0.885417-0.742187-1.5625-0.742187-1.223959 0-2.48698 0.390625-3.789063 1.171875-0.3125 0.182291-0.625 0.397135-0.9375 0.644531l-2.636719-3.867187c0.221355-0.260417 0.631511-0.598959 1.230469-1.015626 0.598958-0.429687 1.184896-0.78125 1.757813-1.054687 0.572916-0.286458 1.308593-0.540365 2.207031-0.761719 0.898437-0.234375 1.809896-0.351562 2.734375-0.351562z" fill="#fff"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -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">
<circle id="outline" cx="32" cy="32" r="28" fill="none" stroke="#fff" stroke-width="2"/>
<path id="button_4" d="m32 4a28 28 0 0 0-28 28 28 28 0 0 0 28 28 28 28 0 0 0 28-28 28 28 0 0 0-28-28zm-1.158203 13.722656h6.992187v17.636719h2.8125v5.097656h-2.8125v5.820313h-6.523437v-5.820313h-9.042969v-4.628906l8.574219-18.105469zm0.585937 10-3.671875 7.636719h3.554688v-5.429687l0.117187-2.207032z" fill="#fff"/>
</svg>

Before

Width:  |  Height:  |  Size: 552 B

View file

@ -1,19 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg <svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64" width="64"
height="64" height="64"
version="1.1" version="1.1"
viewBox="0 0 64 64" viewBox="0 0 64 64"
id="svg16" id="svg16"
sodipodi:docname="button_start.svg" xmlns="http://www.w3.org/2000/svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"> 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/">
<metadata <metadata
id="metadata22"> id="metadata22">
<rdf:RDF> <rdf:RDF>
@ -22,36 +18,13 @@
<dc:format>image/svg+xml</dc:format> <dc:format>image/svg+xml</dc:format>
<dc:type <dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work> </cc:Work>
</rdf:RDF> </rdf:RDF>
</metadata> </metadata>
<defs <defs
id="defs20" /> id="defs20" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3840"
inkscape:window-height="2065"
id="namedview18"
showgrid="false"
inkscape:pagecheckerboard="true"
inkscape:zoom="8.110593"
inkscape:cx="15.256979"
inkscape:cy="28.10138"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg16" />
<path <path
style="opacity:1;fill:#ffffff;fill-opacity:0.97137403;stroke:none;stroke-width:2.86624074;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.86624074;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 36.705078,7.9726562 c 13.495895,0 24.359375,10.7159498 24.359375,24.0273438 0,13.311394 -10.86348,24.027344 -24.359375,24.027344 H 27.294922 C 13.799027,56.027344 2.9355469,45.311394 2.9355469,32 2.9355469,18.688606 13.799027,7.9726562 27.294922,7.9726562 Z m 4.1875,7.1289058 L 27.240234,23.550781 13.587891,32 27.240234,40.449219 40.892578,48.898438 V 32 Z" d="m 36.705078,7.9726562 c 13.495895,0 24.359375,10.7159498 24.359375,24.0273438 0,13.311394 -10.86348,24.027344 -24.359375,24.027344 H 27.294922 C 13.799027,56.027344 2.9355469,45.311394 2.9355469,32 2.9355469,18.688606 13.799027,7.9726562 27.294922,7.9726562 Z m 4.1875,7.1289058 L 27.240234,23.550781 13.587891,32 27.240234,40.449219 40.892578,48.898438 V 32 Z"
id="rect4801" id="rect4801" />
inkscape:connector-curvature="0" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -1,19 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg <svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64" width="64"
height="64" height="64"
version="1.1" version="1.1"
viewBox="0 0 64 64" viewBox="0 0 64 64"
id="svg4" id="svg4"
sodipodi:docname="button_start_PS5.svg" xmlns="http://www.w3.org/2000/svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"> 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/">
<metadata <metadata
id="metadata10"> id="metadata10">
<rdf:RDF> <rdf:RDF>
@ -22,36 +18,13 @@
<dc:format>image/svg+xml</dc:format> <dc:format>image/svg+xml</dc:format>
<dc:type <dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work> </cc:Work>
</rdf:RDF> </rdf:RDF>
</metadata> </metadata>
<defs <defs
id="defs8" /> id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3840"
inkscape:window-height="2065"
id="namedview6"
showgrid="false"
inkscape:pagecheckerboard="true"
inkscape:zoom="10.114756"
inkscape:cx="-29.467155"
inkscape:cy="35.704758"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4" />
<path <path
style="opacity:1;fill:#ffffff;fill-opacity:0.99427478;stroke:none;stroke-width:3.00568366;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3.00568366;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="M 32,3 A 28.999998,28.999998 0 0 0 3,32 28.999998,28.999998 0 0 0 32,61 28.999998,28.999998 0 0 0 61,32 28.999998,28.999998 0 0 0 32,3 Z M 15.1393,18.994908 h 33.7214 c 0.194964,0 0.37709,0.04431 0.532018,0.139579 0.154927,0.09528 0.301408,0.270435 0.301408,0.497628 v 2.751116 c 0,0.227193 -0.146481,0.40033 -0.301408,0.495605 -0.154928,0.09528 -0.337054,0.139579 -0.532018,0.139579 H 15.1393 c -0.194964,0 -0.37709,-0.04431 -0.532018,-0.139579 -0.154927,-0.09528 -0.301408,-0.268412 -0.301408,-0.495605 v -2.751116 c 0,-0.227193 0.146481,-0.402353 0.301408,-0.497628 0.154928,-0.09528 0.337054,-0.139579 0.532018,-0.139579 z m 0,8.963379 h 33.7214 c 0.194964,0 0.37709,0.04431 0.532018,0.139579 0.154927,0.09528 0.301408,0.268411 0.301408,0.495606 v 2.751116 c 0,0.227191 -0.146481,0.400329 -0.301408,0.495604 -0.154928,0.09528 -0.337054,0.139579 -0.532018,0.139579 H 15.1393 c -0.194964,0 -0.37709,-0.04431 -0.532018,-0.139579 -0.154927,-0.09527 -0.301408,-0.268413 -0.301408,-0.495604 v -2.751116 c 0,-0.227196 0.146481,-0.400331 0.301408,-0.495606 0.154928,-0.09528 0.337054,-0.139579 0.532018,-0.139579 z m 0,8.961356 h 33.7214 c 0.194964,0 0.37709,0.04431 0.532018,0.139579 0.154927,0.09528 0.301408,0.268413 0.301408,0.495605 v 2.751116 c 0,0.227193 -0.146481,0.40033 -0.301408,0.495606 -0.154928,0.09528 -0.337054,0.139578 -0.532018,0.139578 H 15.1393 c -0.194964,0 -0.37709,-0.0443 -0.532018,-0.139578 -0.154927,-0.09528 -0.301408,-0.268413 -0.301408,-0.495606 v -2.751116 c 0,-0.227192 0.146481,-0.40033 0.301408,-0.495605 0.154928,-0.09527 0.337054,-0.139579 0.532018,-0.139579 z" d="M 32,3 A 28.999998,28.999998 0 0 0 3,32 28.999998,28.999998 0 0 0 32,61 28.999998,28.999998 0 0 0 61,32 28.999998,28.999998 0 0 0 32,3 Z M 15.1393,18.994908 h 33.7214 c 0.194964,0 0.37709,0.04431 0.532018,0.139579 0.154927,0.09528 0.301408,0.270435 0.301408,0.497628 v 2.751116 c 0,0.227193 -0.146481,0.40033 -0.301408,0.495605 -0.154928,0.09528 -0.337054,0.139579 -0.532018,0.139579 H 15.1393 c -0.194964,0 -0.37709,-0.04431 -0.532018,-0.139579 -0.154927,-0.09528 -0.301408,-0.268412 -0.301408,-0.495605 v -2.751116 c 0,-0.227193 0.146481,-0.402353 0.301408,-0.497628 0.154928,-0.09528 0.337054,-0.139579 0.532018,-0.139579 z m 0,8.963379 h 33.7214 c 0.194964,0 0.37709,0.04431 0.532018,0.139579 0.154927,0.09528 0.301408,0.268411 0.301408,0.495606 v 2.751116 c 0,0.227191 -0.146481,0.400329 -0.301408,0.495604 -0.154928,0.09528 -0.337054,0.139579 -0.532018,0.139579 H 15.1393 c -0.194964,0 -0.37709,-0.04431 -0.532018,-0.139579 -0.154927,-0.09527 -0.301408,-0.268413 -0.301408,-0.495604 v -2.751116 c 0,-0.227196 0.146481,-0.400331 0.301408,-0.495606 0.154928,-0.09528 0.337054,-0.139579 0.532018,-0.139579 z m 0,8.961356 h 33.7214 c 0.194964,0 0.37709,0.04431 0.532018,0.139579 0.154927,0.09528 0.301408,0.268413 0.301408,0.495605 v 2.751116 c 0,0.227193 -0.146481,0.40033 -0.301408,0.495606 -0.154928,0.09528 -0.337054,0.139578 -0.532018,0.139578 H 15.1393 c -0.194964,0 -0.37709,-0.0443 -0.532018,-0.139578 -0.154927,-0.09528 -0.301408,-0.268413 -0.301408,-0.495606 v -2.751116 c 0,-0.227192 0.146481,-0.40033 0.301408,-0.495605 0.154928,-0.09527 0.337054,-0.139579 0.532018,-0.139579 z"
id="path4749" id="path4749" />
inkscape:connector-curvature="0" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -1,19 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg <svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64" width="64"
height="64" height="64"
version="1.1" version="1.1"
viewBox="0 0 64 64" viewBox="0 0 64 64"
id="svg4" id="svg4"
sodipodi:docname="button_start_XBOX.svg" xmlns="http://www.w3.org/2000/svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"> 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/">
<metadata <metadata
id="metadata10"> id="metadata10">
<rdf:RDF> <rdf:RDF>
@ -22,36 +18,13 @@
<dc:format>image/svg+xml</dc:format> <dc:format>image/svg+xml</dc:format>
<dc:type <dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work> </cc:Work>
</rdf:RDF> </rdf:RDF>
</metadata> </metadata>
<defs <defs
id="defs8" /> id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3840"
inkscape:window-height="2065"
id="namedview6"
showgrid="false"
inkscape:pagecheckerboard="true"
inkscape:zoom="10.114756"
inkscape:cx="54.855698"
inkscape:cy="22.84784"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4" />
<path <path
style="opacity:1;fill:#ffffff;fill-opacity:0.99427478;stroke:none;stroke-width:3.00568366;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3.00568366;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="M 32,3 A 28.999998,28.999998 0 0 0 3,32 28.999998,28.999998 0 0 0 32,61 28.999998,28.999998 0 0 0 61,32 28.999998,28.999998 0 0 0 32,3 Z M 15.1393,18.994908 h 33.7214 c 0.194964,0 0.37709,0.04431 0.532018,0.139579 0.154927,0.09528 0.301408,0.270435 0.301408,0.497628 v 2.751116 c 0,0.227193 -0.146481,0.40033 -0.301408,0.495605 -0.154928,0.09528 -0.337054,0.139579 -0.532018,0.139579 H 15.1393 c -0.194964,0 -0.37709,-0.04431 -0.532018,-0.139579 -0.154927,-0.09528 -0.301408,-0.268412 -0.301408,-0.495605 v -2.751116 c 0,-0.227193 0.146481,-0.402353 0.301408,-0.497628 0.154928,-0.09528 0.337054,-0.139579 0.532018,-0.139579 z m 0,8.963379 h 33.7214 c 0.194964,0 0.37709,0.04431 0.532018,0.139579 0.154927,0.09528 0.301408,0.268411 0.301408,0.495606 v 2.751116 c 0,0.227191 -0.146481,0.400329 -0.301408,0.495604 -0.154928,0.09528 -0.337054,0.139579 -0.532018,0.139579 H 15.1393 c -0.194964,0 -0.37709,-0.04431 -0.532018,-0.139579 -0.154927,-0.09527 -0.301408,-0.268413 -0.301408,-0.495604 v -2.751116 c 0,-0.227196 0.146481,-0.400331 0.301408,-0.495606 0.154928,-0.09528 0.337054,-0.139579 0.532018,-0.139579 z m 0,8.961356 h 33.7214 c 0.194964,0 0.37709,0.04431 0.532018,0.139579 0.154927,0.09528 0.301408,0.268413 0.301408,0.495605 v 2.751116 c 0,0.227193 -0.146481,0.40033 -0.301408,0.495606 -0.154928,0.09528 -0.337054,0.139578 -0.532018,0.139578 H 15.1393 c -0.194964,0 -0.37709,-0.0443 -0.532018,-0.139578 -0.154927,-0.09528 -0.301408,-0.268413 -0.301408,-0.495606 v -2.751116 c 0,-0.227192 0.146481,-0.40033 0.301408,-0.495605 0.154928,-0.09527 0.337054,-0.139579 0.532018,-0.139579 z" d="M 32,3 A 28.999998,28.999998 0 0 0 3,32 28.999998,28.999998 0 0 0 32,61 28.999998,28.999998 0 0 0 61,32 28.999998,28.999998 0 0 0 32,3 Z M 15.1393,18.994908 h 33.7214 c 0.194964,0 0.37709,0.04431 0.532018,0.139579 0.154927,0.09528 0.301408,0.270435 0.301408,0.497628 v 2.751116 c 0,0.227193 -0.146481,0.40033 -0.301408,0.495605 -0.154928,0.09528 -0.337054,0.139579 -0.532018,0.139579 H 15.1393 c -0.194964,0 -0.37709,-0.04431 -0.532018,-0.139579 -0.154927,-0.09528 -0.301408,-0.268412 -0.301408,-0.495605 v -2.751116 c 0,-0.227193 0.146481,-0.402353 0.301408,-0.497628 0.154928,-0.09528 0.337054,-0.139579 0.532018,-0.139579 z m 0,8.963379 h 33.7214 c 0.194964,0 0.37709,0.04431 0.532018,0.139579 0.154927,0.09528 0.301408,0.268411 0.301408,0.495606 v 2.751116 c 0,0.227191 -0.146481,0.400329 -0.301408,0.495604 -0.154928,0.09528 -0.337054,0.139579 -0.532018,0.139579 H 15.1393 c -0.194964,0 -0.37709,-0.04431 -0.532018,-0.139579 -0.154927,-0.09527 -0.301408,-0.268413 -0.301408,-0.495604 v -2.751116 c 0,-0.227196 0.146481,-0.400331 0.301408,-0.495606 0.154928,-0.09528 0.337054,-0.139579 0.532018,-0.139579 z m 0,8.961356 h 33.7214 c 0.194964,0 0.37709,0.04431 0.532018,0.139579 0.154927,0.09528 0.301408,0.268413 0.301408,0.495605 v 2.751116 c 0,0.227193 -0.146481,0.40033 -0.301408,0.495606 -0.154928,0.09528 -0.337054,0.139578 -0.532018,0.139578 H 15.1393 c -0.194964,0 -0.37709,-0.0443 -0.532018,-0.139578 -0.154927,-0.09528 -0.301408,-0.268413 -0.301408,-0.495606 v -2.751116 c 0,-0.227192 0.146481,-0.40033 0.301408,-0.495605 0.154928,-0.09527 0.337054,-0.139579 0.532018,-0.139579 z"
id="path4749" id="path4749" />
inkscape:connector-curvature="0" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -1,19 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg <svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64" width="64"
height="64" height="64"
version="1.1" version="1.1"
viewBox="0 0 64 64" viewBox="0 0 64 64"
id="svg16" id="svg16"
sodipodi:docname="button_start.svg" xmlns="http://www.w3.org/2000/svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"> 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/">
<metadata <metadata
id="metadata22"> id="metadata22">
<rdf:RDF> <rdf:RDF>
@ -22,35 +18,13 @@
<dc:format>image/svg+xml</dc:format> <dc:format>image/svg+xml</dc:format>
<dc:type <dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work> </cc:Work>
</rdf:RDF> </rdf:RDF>
</metadata> </metadata>
<defs <defs
id="defs20" /> id="defs20" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3840"
inkscape:window-height="2065"
id="namedview18"
showgrid="false"
inkscape:pagecheckerboard="true"
inkscape:zoom="8.110593"
inkscape:cx="15.256979"
inkscape:cy="28.10138"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg16" />
<path <path
style="opacity:1;fill:#ffffff;fill-opacity:0.97137403;stroke:none;stroke-width:2.86624074;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.86624074;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="M 27.294922 7.9726562 C 13.799027 7.9726562 2.9355469 18.688606 2.9355469 32 C 2.9355469 45.311394 13.799027 56.027344 27.294922 56.027344 L 36.705078 56.027344 C 50.200973 56.027344 61.064453 45.311394 61.064453 32 C 61.064453 18.688606 50.200973 7.9726562 36.705078 7.9726562 L 27.294922 7.9726562 z M 23.107422 15.101562 L 36.759766 23.550781 L 50.412109 32 L 36.759766 40.449219 L 23.107422 48.898438 L 23.107422 32 L 23.107422 15.101562 z " d="M 27.294922 7.9726562 C 13.799027 7.9726562 2.9355469 18.688606 2.9355469 32 C 2.9355469 45.311394 13.799027 56.027344 27.294922 56.027344 L 36.705078 56.027344 C 50.200973 56.027344 61.064453 45.311394 61.064453 32 C 61.064453 18.688606 50.200973 7.9726562 36.705078 7.9726562 L 27.294922 7.9726562 z M 23.107422 15.101562 L 36.759766 23.550781 L 50.412109 32 L 36.759766 40.449219 L 23.107422 48.898438 L 23.107422 32 L 23.107422 15.101562 z "
id="rect4801" /> id="rect4801" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -85,7 +85,7 @@
<path>%ROMPATH%/apple2</path> <path>%ROMPATH%/apple2</path>
<extension>.do .DO .dsk .DSK .nib .NIB .po .PO</extension> <extension>.do .DO .dsk .DSK .nib .NIB .po .PO</extension>
<command label="Mednafen (Standalone)">%EMULATOR_MEDNAFEN% -force_module apple2 %ROM%</command> <command label="Mednafen (Standalone)">%EMULATOR_MEDNAFEN% -force_module apple2 %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/apple2 apple2e -flop1 %ROM%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/apple2 apple2e -flop1 %ROM%</command>
<platform>apple2</platform> <platform>apple2</platform>
<theme>apple2</theme> <theme>apple2</theme>
</system> </system>
@ -94,7 +94,7 @@
<fullname>Apple IIGS</fullname> <fullname>Apple IIGS</fullname>
<path>%ROMPATH%/apple2gs</path> <path>%ROMPATH%/apple2gs</path>
<extension>.2mg .2MG</extension> <extension>.2mg .2MG</extension>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/apple2gs apple2gs -flop3 %ROM%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/apple2gs apple2gs -flop3 %ROM%</command>
<platform>apple2gs</platform> <platform>apple2gs</platform>
<theme>apple2gs</theme> <theme>apple2gs</theme>
</system> </system>
@ -107,7 +107,7 @@
<command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2010_libretro.dylib %ROM%</command> <command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2010_libretro.dylib %ROM%</command>
<command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2003_plus_libretro.dylib %ROM%</command> <command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2003_plus_libretro.dylib %ROM%</command>
<command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2000_libretro.dylib %ROM%</command> <command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2000_libretro.dylib %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/arcade %BASENAME%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/arcade %BASENAME%</command>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbneo_libretro.dylib %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbneo_libretro.dylib %ROM%</command>
<command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbalpha2012_libretro.dylib %ROM%</command> <command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbalpha2012_libretro.dylib %ROM%</command>
<command label="Flycast">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/flycast_libretro.dylib %ROM%</command> <command label="Flycast">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/flycast_libretro.dylib %ROM%</command>
@ -121,7 +121,7 @@
<path>%ROMPATH%/astrocde</path> <path>%ROMPATH%/astrocde</path>
<extension>.7z .7Z .zip .ZIP</extension> <extension>.7z .7Z .zip .ZIP</extension>
<command label="MAME - Current">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame_libretro.dylib %ROM%</command> <command label="MAME - Current">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame_libretro.dylib %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/astrocde astrocde -cart %BASENAME%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/astrocde astrocde -cart %BASENAME%</command>
<platform>astrocde</platform> <platform>astrocde</platform>
<theme>astrocade</theme> <theme>astrocade</theme>
</system> </system>
@ -140,7 +140,7 @@
<name>atari5200</name> <name>atari5200</name>
<fullname>Atari 5200</fullname> <fullname>Atari 5200</fullname>
<path>%ROMPATH%/atari5200</path> <path>%ROMPATH%/atari5200</path>
<extension>.a52 .A52 .atr .ATR .atx .ATX .bin .BIN .cas .CAS .cdm .CDM .xex .XEX .xfd .XFD .7z .7Z .zip .ZIP</extension> <extension>.a52 .A52 .atr .ATR .atx .ATX .bin .BIN .car .CAR .cas .CAS .cdm .CDM .rom .ROM .xex .XEX .xfd .XFD .7z .7Z .zip .ZIP</extension>
<command label="a5200">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/a5200_libretro.dylib %ROM%</command> <command label="a5200">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/a5200_libretro.dylib %ROM%</command>
<command label="Atari800">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/atari800_libretro.dylib %ROM%</command> <command label="Atari800">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/atari800_libretro.dylib %ROM%</command>
<command label="Atari800 (Standalone)">%EMULATOR_ATARI800% %ROM%</command> <command label="Atari800 (Standalone)">%EMULATOR_ATARI800% %ROM%</command>
@ -160,7 +160,7 @@
<name>atari800</name> <name>atari800</name>
<fullname>Atari 800</fullname> <fullname>Atari 800</fullname>
<path>%ROMPATH%/atari800</path> <path>%ROMPATH%/atari800</path>
<extension>.a52 .A52 .atr .ATR .atx .ATX .bin .BIN .cas .CAS .cdm .CDM .rom .ROM .xex .XEX .xfd .XFD .7z .7Z .zip .ZIP</extension> <extension>.a52 .A52 .atr .ATR .atx .ATX .bin .BIN .car .CAR .cas .CAS .cdm .CDM .rom .ROM .xex .XEX .xfd .XFD .7z .7Z .zip .ZIP</extension>
<command label="Atari800">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/atari800_libretro.dylib %ROM%</command> <command label="Atari800">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/atari800_libretro.dylib %ROM%</command>
<command label="Atari800 (Standalone)">%EMULATOR_ATARI800% %ROM%</command> <command label="Atari800 (Standalone)">%EMULATOR_ATARI800% %ROM%</command>
<platform>atari800</platform> <platform>atari800</platform>
@ -325,7 +325,7 @@
<command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2010_libretro.dylib %ROM%</command> <command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2010_libretro.dylib %ROM%</command>
<command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2003_plus_libretro.dylib %ROM%</command> <command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2003_plus_libretro.dylib %ROM%</command>
<command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2000_libretro.dylib %ROM%</command> <command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2000_libretro.dylib %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/arcade %BASENAME%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/cps %BASENAME%</command>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbneo_libretro.dylib %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbneo_libretro.dylib %ROM%</command>
<command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbalpha2012_libretro.dylib %ROM%</command> <command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbalpha2012_libretro.dylib %ROM%</command>
<command label="FB Alpha 2012 CPS-1">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbalpha2012_cps1_libretro.dylib %ROM%</command> <command label="FB Alpha 2012 CPS-1">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbalpha2012_cps1_libretro.dylib %ROM%</command>
@ -491,8 +491,8 @@
<path>%ROMPATH%/gameandwatch</path> <path>%ROMPATH%/gameandwatch</path>
<extension>.mgw .MGW .7z .7Z .zip .ZIP</extension> <extension>.mgw .MGW .7z .7Z .zip .ZIP</extension>
<command label="Handheld Electronic (GW)">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/gw_libretro.dylib %ROM%</command> <command label="Handheld Electronic (GW)">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/gw_libretro.dylib %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/gameandwatch %BASENAME%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/gameandwatch %BASENAME%</command>
<command label="MAME Local Artwork (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -artpath %ROMPATH%/gameandwatch/artwork -rompath %ROMPATH%/gameandwatch %BASENAME%</command> <command label="MAME Local Artwork (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -artpath %ROMPATH%/gameandwatch/artwork -rompath %GAMEDIR%\;%ROMPATH%/gameandwatch %BASENAME%</command>
<platform>gameandwatch</platform> <platform>gameandwatch</platform>
<theme>gameandwatch</theme> <theme>gameandwatch</theme>
</system> </system>
@ -590,7 +590,7 @@
<extension>.bin .BIN .cdt .CDT .cpr .CPR .dsk .DSK .kcr .KCR .m3u .M3U .sna .SNA .tap .TAR .voc .VOC .7z .7Z .zip .ZIP</extension> <extension>.bin .BIN .cdt .CDT .cpr .CPR .dsk .DSK .kcr .KCR .m3u .M3U .sna .SNA .tap .TAR .voc .VOC .7z .7Z .zip .ZIP</extension>
<command label="Caprice32">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/cap32_libretro.dylib %ROM%</command> <command label="Caprice32">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/cap32_libretro.dylib %ROM%</command>
<command label="CrocoDS">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/crocods_libretro.dylib %ROM%</command> <command label="CrocoDS">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/crocods_libretro.dylib %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/gx4000 gx4000 -cart %ROM%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/gx4000 gx4000 -cart %ROM%</command>
<platform>gx4000</platform> <platform>gx4000</platform>
<theme>gx4000</theme> <theme>gx4000</theme>
</system> </system>
@ -658,7 +658,7 @@
<command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2010_libretro.dylib %ROM%</command> <command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2010_libretro.dylib %ROM%</command>
<command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2003_plus_libretro.dylib %ROM%</command> <command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2003_plus_libretro.dylib %ROM%</command>
<command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2000_libretro.dylib %ROM%</command> <command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2000_libretro.dylib %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/mame %BASENAME%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/mame %BASENAME%</command>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbneo_libretro.dylib %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbneo_libretro.dylib %ROM%</command>
<command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbalpha2012_libretro.dylib %ROM%</command> <command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbalpha2012_libretro.dylib %ROM%</command>
<command label="Flycast">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/flycast_libretro.dylib %ROM%</command> <command label="Flycast">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/flycast_libretro.dylib %ROM%</command>
@ -760,7 +760,8 @@
<fullname>Sega Model 2</fullname> <fullname>Sega Model 2</fullname>
<path>%ROMPATH%/model2</path> <path>%ROMPATH%/model2</path>
<extension>.7z .7Z .zip .ZIP</extension> <extension>.7z .7Z .zip .ZIP</extension>
<command>PLACEHOLDER %ROM%</command> <command label="MAME - Current">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame_libretro.dylib %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/model2 %BASENAME%</command>
<platform>arcade</platform> <platform>arcade</platform>
<theme>model2</theme> <theme>model2</theme>
</system> </system>
@ -927,7 +928,7 @@
<path>%ROMPATH%/neogeo</path> <path>%ROMPATH%/neogeo</path>
<extension>.7z .7Z .zip .ZIP</extension> <extension>.7z .7Z .zip .ZIP</extension>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbneo_libretro.dylib %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbneo_libretro.dylib %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/neogeo %BASENAME%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/neogeo %BASENAME%</command>
<platform>neogeo</platform> <platform>neogeo</platform>
<theme>neogeo</theme> <theme>neogeo</theme>
</system> </system>
@ -938,7 +939,7 @@
<extension>.chd .CHD .cue .CUE</extension> <extension>.chd .CHD .cue .CUE</extension>
<command label="NeoCD">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/neocd_libretro.dylib %ROM%</command> <command label="NeoCD">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/neocd_libretro.dylib %ROM%</command>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% --subsystem neocd -L %CORE_RETROARCH%/fbneo_libretro.dylib %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% --subsystem neocd -L %CORE_RETROARCH%/fbneo_libretro.dylib %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/neogeocd neocdz -cdrm %ROM%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/neogeocd neocdz -cdrm %ROM%</command>
<platform>neogeocd</platform> <platform>neogeocd</platform>
<theme>neogeocd</theme> <theme>neogeocd</theme>
</system> </system>
@ -949,7 +950,7 @@
<extension>.chd .CHD .cue .CUE</extension> <extension>.chd .CHD .cue .CUE</extension>
<command label="NeoCD">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/neocd_libretro.dylib %ROM%</command> <command label="NeoCD">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/neocd_libretro.dylib %ROM%</command>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% --subsystem neocd -L %CORE_RETROARCH%/fbneo_libretro.dylib %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% --subsystem neocd -L %CORE_RETROARCH%/fbneo_libretro.dylib %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/neogeocdjp neocdz -cdrm %ROM%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/neogeocdjp neocdz -cdrm %ROM%</command>
<platform>neogeocd</platform> <platform>neogeocd</platform>
<theme>neogeocdjp</theme> <theme>neogeocdjp</theme>
</system> </system>
@ -1486,7 +1487,7 @@
<fullname>Texas Instruments TI-99</fullname> <fullname>Texas Instruments TI-99</fullname>
<path>%ROMPATH%/ti99</path> <path>%ROMPATH%/ti99</path>
<extension>.rpk .RPK .7z .7Z .zip .ZIP</extension> <extension>.rpk .RPK .7z .7Z .zip .ZIP</extension>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/ti99 ti99_4a -ioport peb -ioport:peb:slot3 speech -cart %BASENAME%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/ti99 ti99_4a -ioport peb -ioport:peb:slot3 speech -cart %BASENAME%</command>
<platform>ti99</platform> <platform>ti99</platform>
<theme>ti99</theme> <theme>ti99</theme>
</system> </system>

View file

@ -576,6 +576,17 @@
<entry>steam</entry> <entry>steam</entry>
</rule> </rule>
</emulator> </emulator>
<emulator name="SUPERMODEL">
<!-- Sega Model 3 emulator Supermodel -->
<rule type="systempath">
<entry>supermodel</entry>
</rule>
<rule type="staticpath">
<entry>~/Applications/Supermodel/supermodel</entry>
<entry>~/.local/bin/Supermodel/supermodel</entry>
<entry>~/bin/Supermodel/supermodel</entry>
</rule>
</emulator>
<emulator name="TRIFORCE"> <emulator name="TRIFORCE">
<!-- Triforce, fork of Nintendo GameCube and Wii emulator Dolphin --> <!-- Triforce, fork of Nintendo GameCube and Wii emulator Dolphin -->
<rule type="systempath"> <rule type="systempath">

View file

@ -86,7 +86,7 @@
<extension>.do .DO .dsk .DSK .nib .NIB .po .PO</extension> <extension>.do .DO .dsk .DSK .nib .NIB .po .PO</extension>
<command label="LinApple (Standalone)">%EMULATOR_LINAPPLE% -f -b --d1 %ROM%</command> <command label="LinApple (Standalone)">%EMULATOR_LINAPPLE% -f -b --d1 %ROM%</command>
<command label="Mednafen (Standalone)">%EMULATOR_MEDNAFEN% -force_module apple2 %ROM%</command> <command label="Mednafen (Standalone)">%EMULATOR_MEDNAFEN% -force_module apple2 %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/apple2 apple2e -flop1 %ROM%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/apple2 apple2e -flop1 %ROM%</command>
<platform>apple2</platform> <platform>apple2</platform>
<theme>apple2</theme> <theme>apple2</theme>
</system> </system>
@ -95,7 +95,7 @@
<fullname>Apple IIGS</fullname> <fullname>Apple IIGS</fullname>
<path>%ROMPATH%/apple2gs</path> <path>%ROMPATH%/apple2gs</path>
<extension>.2mg .2MG</extension> <extension>.2mg .2MG</extension>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/apple2gs apple2gs -flop3 %ROM%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/apple2gs apple2gs -flop3 %ROM%</command>
<platform>apple2gs</platform> <platform>apple2gs</platform>
<theme>apple2gs</theme> <theme>apple2gs</theme>
</system> </system>
@ -108,12 +108,13 @@
<command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2010_libretro.so %ROM%</command> <command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2010_libretro.so %ROM%</command>
<command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2003_plus_libretro.so %ROM%</command> <command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2003_plus_libretro.so %ROM%</command>
<command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2000_libretro.so %ROM%</command> <command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2000_libretro.so %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/arcade %BASENAME%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/arcade %BASENAME%</command>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbneo_libretro.so %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbneo_libretro.so %ROM%</command>
<command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbalpha2012_libretro.so %ROM%</command> <command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbalpha2012_libretro.so %ROM%</command>
<command label="Flycast">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/flycast_libretro.so %ROM%</command> <command label="Flycast">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/flycast_libretro.so %ROM%</command>
<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>
<platform>arcade</platform> <platform>arcade</platform>
<theme>arcade</theme> <theme>arcade</theme>
</system> </system>
@ -123,7 +124,7 @@
<path>%ROMPATH%/astrocde</path> <path>%ROMPATH%/astrocde</path>
<extension>.7z .7Z .zip .ZIP</extension> <extension>.7z .7Z .zip .ZIP</extension>
<command label="MAME - Current">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame_libretro.so %ROM%</command> <command label="MAME - Current">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame_libretro.so %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/astrocde astrocde -cart %BASENAME%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/astrocde astrocde -cart %BASENAME%</command>
<platform>astrocde</platform> <platform>astrocde</platform>
<theme>astrocade</theme> <theme>astrocade</theme>
</system> </system>
@ -142,7 +143,7 @@
<name>atari5200</name> <name>atari5200</name>
<fullname>Atari 5200</fullname> <fullname>Atari 5200</fullname>
<path>%ROMPATH%/atari5200</path> <path>%ROMPATH%/atari5200</path>
<extension>.a52 .A52 .atr .ATR .atx .ATX .bin .BIN .cas .CAS .cdm .CDM .xex .XEX .xfd .XFD .7z .7Z .zip .ZIP</extension> <extension>.a52 .A52 .atr .ATR .atx .ATX .bin .BIN .car .CAR .cas .CAS .cdm .CDM .rom .ROM .xex .XEX .xfd .XFD .7z .7Z .zip .ZIP</extension>
<command label="a5200">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/a5200_libretro.so %ROM%</command> <command label="a5200">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/a5200_libretro.so %ROM%</command>
<command label="Atari800">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/atari800_libretro.so %ROM%</command> <command label="Atari800">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/atari800_libretro.so %ROM%</command>
<command label="Atari800 (Standalone)">%EMULATOR_ATARI800% %ROM%</command> <command label="Atari800 (Standalone)">%EMULATOR_ATARI800% %ROM%</command>
@ -162,7 +163,7 @@
<name>atari800</name> <name>atari800</name>
<fullname>Atari 800</fullname> <fullname>Atari 800</fullname>
<path>%ROMPATH%/atari800</path> <path>%ROMPATH%/atari800</path>
<extension>.a52 .A52 .atr .ATR .atx .ATX .bin .BIN .cas .CAS .cdm .CDM .rom .ROM .xex .XEX .xfd .XFD .7z .7Z .zip .ZIP</extension> <extension>.a52 .A52 .atr .ATR .atx .ATX .bin .BIN .car .CAR .cas .CAS .cdm .CDM .rom .ROM .xex .XEX .xfd .XFD .7z .7Z .zip .ZIP</extension>
<command label="Atari800">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/atari800_libretro.so %ROM%</command> <command label="Atari800">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/atari800_libretro.so %ROM%</command>
<command label="Atari800 (Standalone)">%EMULATOR_ATARI800% %ROM%</command> <command label="Atari800 (Standalone)">%EMULATOR_ATARI800% %ROM%</command>
<platform>atari800</platform> <platform>atari800</platform>
@ -327,7 +328,7 @@
<command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2010_libretro.so %ROM%</command> <command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2010_libretro.so %ROM%</command>
<command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2003_plus_libretro.so %ROM%</command> <command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2003_plus_libretro.so %ROM%</command>
<command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2000_libretro.so %ROM%</command> <command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2000_libretro.so %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/arcade %BASENAME%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/cps %BASENAME%</command>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbneo_libretro.so %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbneo_libretro.so %ROM%</command>
<command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbalpha2012_libretro.so %ROM%</command> <command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbalpha2012_libretro.so %ROM%</command>
<command label="FB Alpha 2012 CPS-1">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbalpha2012_cps1_libretro.so %ROM%</command> <command label="FB Alpha 2012 CPS-1">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbalpha2012_cps1_libretro.so %ROM%</command>
@ -499,8 +500,8 @@
<path>%ROMPATH%/gameandwatch</path> <path>%ROMPATH%/gameandwatch</path>
<extension>.mgw .MGW .7z .7Z .zip .ZIP</extension> <extension>.mgw .MGW .7z .7Z .zip .ZIP</extension>
<command label="Handheld Electronic (GW)">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/gw_libretro.so %ROM%</command> <command label="Handheld Electronic (GW)">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/gw_libretro.so %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/gameandwatch %BASENAME%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/gameandwatch %BASENAME%</command>
<command label="MAME Local Artwork (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -artpath %ROMPATH%/gameandwatch/artwork -rompath %ROMPATH%/gameandwatch %BASENAME%</command> <command label="MAME Local Artwork (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -artpath %ROMPATH%/gameandwatch/artwork -rompath %GAMEDIR%\;%ROMPATH%/gameandwatch %BASENAME%</command>
<platform>gameandwatch</platform> <platform>gameandwatch</platform>
<theme>gameandwatch</theme> <theme>gameandwatch</theme>
</system> </system>
@ -601,7 +602,7 @@
<extension>.bin .BIN .cdt .CDT .cpr .CPR .dsk .DSK .kcr .KCR .m3u .M3U .sna .SNA .tap .TAR .voc .VOC .7z .7Z .zip .ZIP</extension> <extension>.bin .BIN .cdt .CDT .cpr .CPR .dsk .DSK .kcr .KCR .m3u .M3U .sna .SNA .tap .TAR .voc .VOC .7z .7Z .zip .ZIP</extension>
<command label="Caprice32">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/cap32_libretro.so %ROM%</command> <command label="Caprice32">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/cap32_libretro.so %ROM%</command>
<command label="CrocoDS">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/crocods_libretro.so %ROM%</command> <command label="CrocoDS">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/crocods_libretro.so %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/gx4000 gx4000 -cart %ROM%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/gx4000 gx4000 -cart %ROM%</command>
<platform>gx4000</platform> <platform>gx4000</platform>
<theme>gx4000</theme> <theme>gx4000</theme>
</system> </system>
@ -669,12 +670,13 @@
<command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2010_libretro.so %ROM%</command> <command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2010_libretro.so %ROM%</command>
<command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2003_plus_libretro.so %ROM%</command> <command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2003_plus_libretro.so %ROM%</command>
<command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2000_libretro.so %ROM%</command> <command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame2000_libretro.so %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/mame %BASENAME%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/mame %BASENAME%</command>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbneo_libretro.so %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbneo_libretro.so %ROM%</command>
<command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbalpha2012_libretro.so %ROM%</command> <command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbalpha2012_libretro.so %ROM%</command>
<command label="Flycast">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/flycast_libretro.so %ROM%</command> <command label="Flycast">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/flycast_libretro.so %ROM%</command>
<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>
<platform>arcade</platform> <platform>arcade</platform>
<theme>mame</theme> <theme>mame</theme>
</system> </system>
@ -773,7 +775,8 @@
<fullname>Sega Model 2</fullname> <fullname>Sega Model 2</fullname>
<path>%ROMPATH%/model2</path> <path>%ROMPATH%/model2</path>
<extension>.7z .7Z .zip .ZIP</extension> <extension>.7z .7Z .zip .ZIP</extension>
<command>PLACEHOLDER %ROM%</command> <command label="MAME - Current">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/mame_libretro.so %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/model2 %BASENAME%</command>
<platform>arcade</platform> <platform>arcade</platform>
<theme>model2</theme> <theme>model2</theme>
</system> </system>
@ -782,7 +785,7 @@
<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>PLACEHOLDER %ROM%</command> <command label="Supermodel (Standalone)">%STARTDIR%=%GAMEDIR% %EMULATOR_SUPERMODEL% -log-output=%GAMEDIR%/Config/Supermodel.log -force-feedback %INJECT%=%BASENAME%.commands %ROM%</command>
<platform>arcade</platform> <platform>arcade</platform>
<theme>model3</theme> <theme>model3</theme>
</system> </system>
@ -947,7 +950,7 @@
<extension>.7z .7Z .zip .ZIP</extension> <extension>.7z .7Z .zip .ZIP</extension>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbneo_libretro.so %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/fbneo_libretro.so %ROM%</command>
<command label="FinalBurn Neo (Standalone)">%EMULATOR_FINALBURN-NEO% -fullscreen %BASENAME%</command> <command label="FinalBurn Neo (Standalone)">%EMULATOR_FINALBURN-NEO% -fullscreen %BASENAME%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/neogeo %BASENAME%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/neogeo %BASENAME%</command>
<platform>neogeo</platform> <platform>neogeo</platform>
<theme>neogeo</theme> <theme>neogeo</theme>
</system> </system>
@ -959,7 +962,7 @@
<command label="NeoCD">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/neocd_libretro.so %ROM%</command> <command label="NeoCD">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/neocd_libretro.so %ROM%</command>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% --subsystem neocd -L %CORE_RETROARCH%/fbneo_libretro.so %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% --subsystem neocd -L %CORE_RETROARCH%/fbneo_libretro.so %ROM%</command>
<command label="FinalBurn Neo (Standalone)">%EMULATOR_FINALBURN-NEO% neocdz -fullscreen -cd %ROM%</command> <command label="FinalBurn Neo (Standalone)">%EMULATOR_FINALBURN-NEO% neocdz -fullscreen -cd %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/neogeocd neocdz -cdrm %ROM%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/neogeocd neocdz -cdrm %ROM%</command>
<platform>neogeocd</platform> <platform>neogeocd</platform>
<theme>neogeocd</theme> <theme>neogeocd</theme>
</system> </system>
@ -971,7 +974,7 @@
<command label="NeoCD">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/neocd_libretro.so %ROM%</command> <command label="NeoCD">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/neocd_libretro.so %ROM%</command>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% --subsystem neocd -L %CORE_RETROARCH%/fbneo_libretro.so %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% --subsystem neocd -L %CORE_RETROARCH%/fbneo_libretro.so %ROM%</command>
<command label="FinalBurn Neo (Standalone)">%EMULATOR_FINALBURN-NEO% neocdz -fullscreen -cd %ROM%</command> <command label="FinalBurn Neo (Standalone)">%EMULATOR_FINALBURN-NEO% neocdz -fullscreen -cd %ROM%</command>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/neogeocdjp neocdz -cdrm %ROM%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/neogeocdjp neocdz -cdrm %ROM%</command>
<platform>neogeocd</platform> <platform>neogeocd</platform>
<theme>neogeocdjp</theme> <theme>neogeocdjp</theme>
</system> </system>
@ -1522,7 +1525,7 @@
<fullname>Texas Instruments TI-99</fullname> <fullname>Texas Instruments TI-99</fullname>
<path>%ROMPATH%/ti99</path> <path>%ROMPATH%/ti99</path>
<extension>.rpk .RPK .7z .7Z .zip .ZIP</extension> <extension>.rpk .RPK .7z .7Z .zip .ZIP</extension>
<command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %ROMPATH%/ti99 ti99_4a -ioport peb -ioport:peb:slot3 speech -cart %BASENAME%</command> <command label="MAME (Standalone)">%STARTDIR%=~/.mame %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%/ti99 ti99_4a -ioport peb -ioport:peb:slot3 speech -cart %BASENAME%</command>
<platform>ti99</platform> <platform>ti99</platform>
<theme>ti99</theme> <theme>ti99</theme>
</system> </system>

View file

@ -86,7 +86,7 @@
<extension>.do .DO .dsk .DSK .nib .NIB .po .PO</extension> <extension>.do .DO .dsk .DSK .nib .NIB .po .PO</extension>
<command label="AppleWin (Standalone)">%EMULATOR_APPLEWIN% -f -d1 %ROM%</command> <command label="AppleWin (Standalone)">%EMULATOR_APPLEWIN% -f -d1 %ROM%</command>
<command label="Mednafen (Standalone)">%EMULATOR_MEDNAFEN% -force_module apple2 %ROM%</command> <command label="Mednafen (Standalone)">%EMULATOR_MEDNAFEN% -force_module apple2 %ROM%</command>
<command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %ROMPATH%\apple2 apple2e -flop1 %ROM%</command> <command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%\apple2 apple2e -flop1 %ROM%</command>
<platform>apple2</platform> <platform>apple2</platform>
<theme>apple2</theme> <theme>apple2</theme>
</system> </system>
@ -95,7 +95,7 @@
<fullname>Apple IIGS</fullname> <fullname>Apple IIGS</fullname>
<path>%ROMPATH%\apple2gs</path> <path>%ROMPATH%\apple2gs</path>
<extension>.2mg .2MG</extension> <extension>.2mg .2MG</extension>
<command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %ROMPATH%\apple2gs apple2gs -flop3 %ROM%</command> <command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%\apple2gs apple2gs -flop3 %ROM%</command>
<platform>apple2gs</platform> <platform>apple2gs</platform>
<theme>apple2gs</theme> <theme>apple2gs</theme>
</system> </system>
@ -108,7 +108,7 @@
<command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2010_libretro.dll %ROM%</command> <command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2010_libretro.dll %ROM%</command>
<command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2003_plus_libretro.dll %ROM%</command> <command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2003_plus_libretro.dll %ROM%</command>
<command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2000_libretro.dll %ROM%</command> <command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2000_libretro.dll %ROM%</command>
<command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %ROMPATH%\arcade %BASENAME%</command> <command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%\arcade %BASENAME%</command>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\fbneo_libretro.dll %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\fbneo_libretro.dll %ROM%</command>
<command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\fbalpha2012_libretro.dll %ROM%</command> <command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\fbalpha2012_libretro.dll %ROM%</command>
<command label="Flycast">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\flycast_libretro.dll %ROM%</command> <command label="Flycast">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\flycast_libretro.dll %ROM%</command>
@ -116,7 +116,7 @@
<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 %ROM%</command> <command label="Supermodel (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>
@ -126,7 +126,7 @@
<path>%ROMPATH%\astrocde</path> <path>%ROMPATH%\astrocde</path>
<extension>.7z .7Z .zip .ZIP</extension> <extension>.7z .7Z .zip .ZIP</extension>
<command label="MAME - Current">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame_libretro.dll %ROM%</command> <command label="MAME - Current">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame_libretro.dll %ROM%</command>
<command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %ROMPATH%\astrocde astrocde -cart %BASENAME%</command> <command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%\astrocde astrocde -cart %BASENAME%</command>
<platform>astrocde</platform> <platform>astrocde</platform>
<theme>astrocade</theme> <theme>astrocade</theme>
</system> </system>
@ -145,7 +145,7 @@
<name>atari5200</name> <name>atari5200</name>
<fullname>Atari 5200</fullname> <fullname>Atari 5200</fullname>
<path>%ROMPATH%\atari5200</path> <path>%ROMPATH%\atari5200</path>
<extension>.a52 .A52 .atr .ATR .atx .ATX .bin .BIN .cas .CAS .cdm .CDM .xex .XEX .xfd .XFD .7z .7Z .zip .ZIP</extension> <extension>.a52 .A52 .atr .ATR .atx .ATX .bin .BIN .car .CAR .cas .CAS .cdm .CDM .rom .ROM .xex .XEX .xfd .XFD .7z .7Z .zip .ZIP</extension>
<command label="a5200">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\a5200_libretro.dll %ROM%</command> <command label="a5200">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\a5200_libretro.dll %ROM%</command>
<command label="Atari800">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\atari800_libretro.dll %ROM%</command> <command label="Atari800">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\atari800_libretro.dll %ROM%</command>
<command label="Atari800 (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_ATARI800% %ROM%</command> <command label="Atari800 (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_ATARI800% %ROM%</command>
@ -165,7 +165,7 @@
<name>atari800</name> <name>atari800</name>
<fullname>Atari 800</fullname> <fullname>Atari 800</fullname>
<path>%ROMPATH%\atari800</path> <path>%ROMPATH%\atari800</path>
<extension>.a52 .A52 .atr .ATR .atx .ATX .bin .BIN .cas .CAS .cdm .CDM .rom .ROM .xex .XEX .xfd .XFD .7z .7Z .zip .ZIP</extension> <extension>.a52 .A52 .atr .ATR .atx .ATX .bin .BIN .car .CAR .cas .CAS .cdm .CDM .rom .ROM .xex .XEX .xfd .XFD .7z .7Z .zip .ZIP</extension>
<command label="Atari800">%STARTDIR%=%EMUDIR% %EMULATOR_RETROARCH% -L %CORE_RETROARCH%\atari800_libretro.dll %ROM%</command> <command label="Atari800">%STARTDIR%=%EMUDIR% %EMULATOR_RETROARCH% -L %CORE_RETROARCH%\atari800_libretro.dll %ROM%</command>
<command label="Atari800 (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_ATARI800% %ROM%</command> <command label="Atari800 (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_ATARI800% %ROM%</command>
<platform>atari800</platform> <platform>atari800</platform>
@ -330,7 +330,7 @@
<command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2010_libretro.dll %ROM%</command> <command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2010_libretro.dll %ROM%</command>
<command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2003_plus_libretro.dll %ROM%</command> <command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2003_plus_libretro.dll %ROM%</command>
<command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2000_libretro.dll %ROM%</command> <command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2000_libretro.dll %ROM%</command>
<command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %ROMPATH%\arcade %BASENAME%</command> <command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%\cps %BASENAME%</command>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\fbneo_libretro.dll %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\fbneo_libretro.dll %ROM%</command>
<command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\fbalpha2012_libretro.dll %ROM%</command> <command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\fbalpha2012_libretro.dll %ROM%</command>
<command label="FB Alpha 2012 CPS-1">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\fbalpha2012_cps1_libretro.dll %ROM%</command> <command label="FB Alpha 2012 CPS-1">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\fbalpha2012_cps1_libretro.dll %ROM%</command>
@ -500,8 +500,8 @@
<path>%ROMPATH%\gameandwatch</path> <path>%ROMPATH%\gameandwatch</path>
<extension>.mgw .MGW .7z .7Z .zip .ZIP</extension> <extension>.mgw .MGW .7z .7Z .zip .ZIP</extension>
<command label="Handheld Electronic (GW)">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\gw_libretro.dll %ROM%</command> <command label="Handheld Electronic (GW)">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\gw_libretro.dll %ROM%</command>
<command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %ROMPATH%\gameandwatch %BASENAME%</command> <command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%\gameandwatch %BASENAME%</command>
<command label="MAME Local Artwork (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -artpath %ROMPATH%\gameandwatch\artwork -rompath %ROMPATH%\gameandwatch %BASENAME%</command> <command label="MAME Local Artwork (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -artpath %ROMPATH%\gameandwatch\artwork -rompath %GAMEDIR%\;%ROMPATH%\gameandwatch %BASENAME%</command>
<platform>gameandwatch</platform> <platform>gameandwatch</platform>
<theme>gameandwatch</theme> <theme>gameandwatch</theme>
</system> </system>
@ -601,7 +601,7 @@
<extension>.bin .BIN .cdt .CDT .cpr .CPR .dsk .DSK .kcr .KCR .m3u .M3U .sna .SNA .tap .TAR .voc .VOC .7z .7Z .zip .ZIP</extension> <extension>.bin .BIN .cdt .CDT .cpr .CPR .dsk .DSK .kcr .KCR .m3u .M3U .sna .SNA .tap .TAR .voc .VOC .7z .7Z .zip .ZIP</extension>
<command label="Caprice32">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\cap32_libretro.dll %ROM%</command> <command label="Caprice32">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\cap32_libretro.dll %ROM%</command>
<command label="CrocoDS">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\crocods_libretro.dll %ROM%</command> <command label="CrocoDS">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\crocods_libretro.dll %ROM%</command>
<command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %ROMPATH%\gx4000 gx4000 -cart %ROM%</command> <command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%\gx4000 gx4000 -cart %ROM%</command>
<platform>gx4000</platform> <platform>gx4000</platform>
<theme>gx4000</theme> <theme>gx4000</theme>
</system> </system>
@ -670,7 +670,7 @@
<command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2010_libretro.dll %ROM%</command> <command label="MAME 2010">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2010_libretro.dll %ROM%</command>
<command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2003_plus_libretro.dll %ROM%</command> <command label="MAME 2003-Plus">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2003_plus_libretro.dll %ROM%</command>
<command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2000_libretro.dll %ROM%</command> <command label="MAME 2000">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame2000_libretro.dll %ROM%</command>
<command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %ROMPATH%\mame %BASENAME%</command> <command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%\mame %BASENAME%</command>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\fbneo_libretro.dll %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\fbneo_libretro.dll %ROM%</command>
<command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\fbalpha2012_libretro.dll %ROM%</command> <command label="FB Alpha 2012">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\fbalpha2012_libretro.dll %ROM%</command>
<command label="Flycast">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\flycast_libretro.dll %ROM%</command> <command label="Flycast">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\flycast_libretro.dll %ROM%</command>
@ -678,7 +678,7 @@
<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 %ROM%</command> <command label="Supermodel (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>
@ -778,6 +778,8 @@
<extension>.7z .7Z .zip .ZIP</extension> <extension>.7z .7Z .zip .ZIP</extension>
<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="MAME - Current">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\mame_libretro.dll %ROM%</command>
<command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%\model2 %BASENAME%</command>
<platform>arcade</platform> <platform>arcade</platform>
<theme>model2</theme> <theme>model2</theme>
</system> </system>
@ -786,7 +788,7 @@
<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 %ROM%</command> <command label="Supermodel (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>
@ -950,7 +952,7 @@
<extension>.7z .7Z .zip .ZIP</extension> <extension>.7z .7Z .zip .ZIP</extension>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\fbneo_libretro.dll %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\fbneo_libretro.dll %ROM%</command>
<command label="FinalBurn Neo (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_FINALBURN-NEO% %BASENAME%</command> <command label="FinalBurn Neo (Standalone)">%STARTDIR%=%EMUDIR% %EMULATOR_FINALBURN-NEO% %BASENAME%</command>
<command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %ROMPATH%\neogeo %BASENAME%</command> <command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%\neogeo %BASENAME%</command>
<platform>neogeo</platform> <platform>neogeo</platform>
<theme>neogeo</theme> <theme>neogeo</theme>
</system> </system>
@ -961,7 +963,7 @@
<extension>.chd .CHD .cue .CUE</extension> <extension>.chd .CHD .cue .CUE</extension>
<command label="NeoCD">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\neocd_libretro.dll %ROM%</command> <command label="NeoCD">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\neocd_libretro.dll %ROM%</command>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% --subsystem neocd -L %CORE_RETROARCH%\fbneo_libretro.dll %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% --subsystem neocd -L %CORE_RETROARCH%\fbneo_libretro.dll %ROM%</command>
<command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %ROMPATH%\neogeocd neocdz -cdrm %ROM%</command> <command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%\neogeocd neocdz -cdrm %ROM%</command>
<platform>neogeocd</platform> <platform>neogeocd</platform>
<theme>neogeocd</theme> <theme>neogeocd</theme>
</system> </system>
@ -972,7 +974,7 @@
<extension>.chd .CHD .cue .CUE</extension> <extension>.chd .CHD .cue .CUE</extension>
<command label="NeoCD">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\neocd_libretro.dll %ROM%</command> <command label="NeoCD">%EMULATOR_RETROARCH% -L %CORE_RETROARCH%\neocd_libretro.dll %ROM%</command>
<command label="FinalBurn Neo">%EMULATOR_RETROARCH% --subsystem neocd -L %CORE_RETROARCH%\fbneo_libretro.dll %ROM%</command> <command label="FinalBurn Neo">%EMULATOR_RETROARCH% --subsystem neocd -L %CORE_RETROARCH%\fbneo_libretro.dll %ROM%</command>
<command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %ROMPATH%\neogeocdjp neocdz -cdrm %ROM%</command> <command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%\neogeocdjp neocdz -cdrm %ROM%</command>
<platform>neogeocd</platform> <platform>neogeocd</platform>
<theme>neogeocdjp</theme> <theme>neogeocdjp</theme>
</system> </system>
@ -1524,7 +1526,7 @@
<fullname>Texas Instruments TI-99</fullname> <fullname>Texas Instruments TI-99</fullname>
<path>%ROMPATH%\ti99</path> <path>%ROMPATH%\ti99</path>
<extension>.rpk .RPK .7z .7Z .zip .ZIP</extension> <extension>.rpk .RPK .7z .7Z .zip .ZIP</extension>
<command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %ROMPATH%\ti99 ti99_4a -ioport peb -ioport:peb:slot3 speech -cart %BASENAME%</command> <command label="MAME (Standalone)">%HIDEWINDOW% %RUNINBACKGROUND% %STARTDIR%=%EMUDIR% %EMULATOR_MAME% -rompath %GAMEDIR%\;%ROMPATH%\ti99 ti99_4a -ioport peb -ioport:peb:slot3 speech -cart %BASENAME%</command>
<platform>ti99</platform> <platform>ti99</platform>
<theme>ti99</theme> <theme>ti99</theme>
</system> </system>

Binary file not shown.

View file

@ -206,13 +206,13 @@
md_lbl_genre, md_lbl_players, md_lbl_lastplayed "> md_lbl_genre, md_lbl_players, md_lbl_lastplayed ">
<size>0.14 0.02</size> <size>0.14 0.02</size>
<fontPath>./core/fonts/Exo2-BoldCondensed.otf</fontPath> <fontPath>./core/fonts/Exo2-BoldCondensed.otf</fontPath>
<fontSize>0.02</fontSize> <fontSize>0.0195</fontSize>
<letterCase>uppercase</letterCase> <letterCase>uppercase</letterCase>
</text> </text>
<text name="md_developer, md_publisher, md_genre, md_players"> <text name="md_developer, md_publisher, md_genre, md_players">
<size>0.14 0.02</size> <size>0.14 0.02</size>
<fontPath>./core/fonts/Exo2-RegularCondensed.otf</fontPath> <fontPath>./core/fonts/Exo2-RegularCondensed.otf</fontPath>
<fontSize>0.02</fontSize> <fontSize>0.0195</fontSize>
<letterCase>uppercase</letterCase> <letterCase>uppercase</letterCase>
</text> </text>
<text name="md_description"> <text name="md_description">
@ -225,14 +225,14 @@
<containerResetDelay>7</containerResetDelay> <containerResetDelay>7</containerResetDelay>
<metadata>description</metadata> <metadata>description</metadata>
<fontPath>./core/fonts/Exo2-SemiBoldCondensed.otf</fontPath> <fontPath>./core/fonts/Exo2-SemiBoldCondensed.otf</fontPath>
<fontSize>0.02</fontSize> <fontSize>0.0195</fontSize>
<letterCase>uppercase</letterCase> <letterCase>uppercase</letterCase>
<lineSpacing>1.2</lineSpacing> <lineSpacing>1.2</lineSpacing>
</text> </text>
<datetime name="md_releasedate, md_lastplayed"> <datetime name="md_releasedate, md_lastplayed">
<size>0.14 0.02</size> <size>0.14 0.02</size>
<fontPath>./core/fonts/Exo2-RegularCondensed.otf</fontPath> <fontPath>./core/fonts/Exo2-RegularCondensed.otf</fontPath>
<fontSize>0.02</fontSize> <fontSize>0.0195</fontSize>
<letterCase>uppercase</letterCase> <letterCase>uppercase</letterCase>
</datetime> </datetime>
<text name="md_lbl_releasedate"> <text name="md_lbl_releasedate">
@ -328,6 +328,8 @@
<origin>0.5 0.5</origin> <origin>0.5 0.5</origin>
<imageType>image</imageType> <imageType>image</imageType>
<interpolation>nearest</interpolation> <interpolation>nearest</interpolation>
<pillarboxes>true</pillarboxes>
<pillarboxThreshold>0.85 0.90</pillarboxThreshold>
<delay>1.7</delay> <delay>1.7</delay>
<scrollFadeIn>true</scrollFadeIn> <scrollFadeIn>true</scrollFadeIn>
</video> </video>

View file

@ -22,7 +22,7 @@
<horizontalOffset>-0.05</horizontalOffset> <horizontalOffset>-0.05</horizontalOffset>
<text>${system.fullName}</text> <text>${system.fullName}</text>
<fontPath>./core/fonts/Exo2-RegularCondensed.otf</fontPath> <fontPath>./core/fonts/Exo2-RegularCondensed.otf</fontPath>
<fontSize>0.070</fontSize> <fontSize>0.056</fontSize>
<letterCase>uppercase</letterCase> <letterCase>uppercase</letterCase>
<lineSpacing>1.2</lineSpacing> <lineSpacing>1.2</lineSpacing>
</carousel> </carousel>

View file

@ -107,7 +107,7 @@
<horizontalOffset>0</horizontalOffset> <horizontalOffset>0</horizontalOffset>
<verticalOffset>0</verticalOffset> <verticalOffset>0</verticalOffset>
<fontPath>./core/fonts/Exo2-RegularCondensed.otf</fontPath> <fontPath>./core/fonts/Exo2-RegularCondensed.otf</fontPath>
<fontSize>0.070</fontSize> <fontSize>0.056</fontSize>
<letterCase>uppercase</letterCase> <letterCase>uppercase</letterCase>
<lineSpacing>1.2</lineSpacing> <lineSpacing>1.2</lineSpacing>
<zIndex>80</zIndex> <zIndex>80</zIndex>

View file

@ -111,7 +111,7 @@
<unfocusedItemOpacity>1</unfocusedItemOpacity> <unfocusedItemOpacity>1</unfocusedItemOpacity>
<textBackgroundColor>323232CC</textBackgroundColor> <textBackgroundColor>323232CC</textBackgroundColor>
<fontPath>./core/fonts/Exo2-RegularCondensed.otf</fontPath> <fontPath>./core/fonts/Exo2-RegularCondensed.otf</fontPath>
<fontSize>0.050</fontSize> <fontSize>0.032</fontSize>
<letterCase>uppercase</letterCase> <letterCase>uppercase</letterCase>
<lineSpacing>1.2</lineSpacing> <lineSpacing>1.2</lineSpacing>
<zIndex>60</zIndex> <zIndex>60</zIndex>