Skip to content
NathanNeurotic (Ripto) edited this page Sep 16, 2026 · 4 revisions

Theme Engine

A RiptOPL/OPL theme is a folder of images, fonts and one text file — conf_theme.cfg — that lays out the whole UI on a virtual 640×480 canvas. This page is the complete authoring reference: every block family, every element type, every property and value token the parser in src/themes.c actually understands, including this fork's Coverflow carousel and the two-layer overlay system. Nothing here is invented — if a key isn't listed, the engine ignores it.

i Mental model

A theme is a flat, ordered list of elements. Each element has a type= and a handful of position/size properties. OPL draws them back-to-front in file order, scales the 640×480 canvas to the real video mode, and (with scaled=1) corrects for widescreen and the PS2's non-square pixels automatically. Anything a theme doesn't supply falls back to OPL's embedded default assets.

1 · File & folder structure

Themes live in the THM support folder. Each theme is its own sub-folder whose name must start with thm_; the loader strips that prefix to form the name you see in the theme picker. Inside the folder, all the artwork/fonts sit alongside a single conf_theme.cfg.

THM/
  thm_MyTheme/
    conf_theme.cfg      ← the layout (this page)
    incebtion.png       ← background, fonts, case art, screenshots… (any names you reference)
    case.png
    myfont.ttf

RiptOPL also ships two built-in themes you can study as references without copying any files: the classic <OPL> theme and this fork's default <Coverflow> theme. Their sources are misc/conf_theme_OPL.cfg and misc/theme_coverflow.cfg in the repository — both are reproduced and annotated lower down.

conf_theme.cfg is a plain key/value file with exactly two kinds of line:

  • Global (top-level) properties — bare key=value lines, written before any block.
  • Element blocks — a blockname: header line followed by indented key=value lines.
# lines beginning with '#' are comments — comment a whole block to disable it
bg_color=#182580          # a global property

main0:                    # an element block
    type=Background
    pattern=BG

i Block keys are flattened

Internally a block property foo under block main0 is read as the key main0_foo. You almost always write the indented form above, but knowing this explains the flat tables in §3–§4 — every "property" is just a <block>_<property> key.

2 · Block families & the contiguous-numbering rule

Element block names carry a family prefix plus an index — e.g. main0, main1, appsMain2, vcdInfo3. Each family targets one screen/view. The base families are main<N> (the games list/menu page) and info<N> (the game info page). On top of those, this fork adds three per-slot override families. For every slot N the engine looks for the family's block; if it's absent it falls back down a fixed chain — so you override only what differs and inherit the rest.

Family (main / info) Drawn on Per-slot fallback chain
main0… / info0… The games list / info — the base layout (none — this is the base)
appsMain0… / appsInfo0… The apps device list / info mainN / infoN
favsMain0… / favsInfo0… The Favorites tab list / info mainN / infoN
vcdMain0… / vcdInfo0… A device's PS1 / VCD view (toggle with L3), plus every PS1 row's cover and info wherever it appears main: → appsMainNmainN · info: → infoN
favsVcdMain0… / favsVcdInfo0… (opt-in) The Favorites PS1 view, plus PS1 covers and info on the Favorites All in One shelf main: → vcdMainNappsMainNmainN · info: → vcdInfoNinfoN
favsAppsMain0… / favsAppsInfo0… (opt-in) The Favorites ELF view, plus app covers and info on the Favorites All in One shelf main: → favsMainNmainN · info: → favsInfoNinfoN

The parser reads as many base main<N> blocks as you define, then for each slot 0…N resolves every family against that chain. Practically:

  • A theme with only main*/info* blocks looks identical on the games, apps, Favorites and PS1 views — every family inherits the base.
  • Define appsMain5 to give the apps cover its own (square) box while games keep their portrait box; everything else still inherits main*.
  • The VCD/PS1 main family falls back to appsMain before main, so a PS1 disc with no vcdMain blocks automatically uses the square apps box (jewel-case art is square), while vcdInfo falls back to the rich game info page.
  • The Favorites per-kind families (favsVcd*, favsApps*) are opt-in: a family exists only if the theme declares at least one of its blocks. They give PS1 and app favorites their own layout, list and cover proportions without touching the device pages. Undeclared slots fall back to the blocks those Favorites views used before. A cover is taken from them only where you declared it, so an undeclared cover keeps today's square PS1 or app cover rather than a copy of favsMain's portrait case, and it is only used where the page draws the same kind (a declared Coverflow styles a carousel, never an ItemCover panel). Their lists take none of the four global list slots, and PS1 / app favorites keep sharing their existing cover caches.

Mixed lists: the page picks the family, each row picks its own cover

A mixed list holds more than one kind of media at once — the Favorites All in One shelf, and a device page under PS2/PS1 Game Display = Mixed. The page as a whole still renders from one family (the base main* on a Mixed device page, favsMain* on Favorites): the background, the texts and the ItemsList belong to the page, so they stay put while you scroll.

The cover does not. A PS2 case is portrait, a PS1 case and an app box are square, and forcing one shape on all three stretches whichever kinds are not the page's own. So the cover element — the ItemCover panel, the Coverflow carousel — is resolved per row, by media kind:

Row Cover drawn from
PS2 disc game the page's own family — main2 on a device page, favsMain2 on Favorites
PS1 / VCD vcdMain*appsMain*main*; on Favorites, a cover declared in favsVcdMain* first
App / ELF appsMain*main*; on Favorites, a cover declared in favsAppsMain* first

Only the cover is redirected — its width/height, overlay/overlay2 and default placeholder. Everything else on the page is untouched, and on a single-kind page the row's kind is the page's kind, so nothing is redirected at all. The same rule picks the info screen's cover from the matching *Info* family. In a Coverflow carousel the cover width stays uniform (so spacing stays even) and only the height follows each row's own element aspect.

ℹ Two cover caches on a mixed list

The games, apps and Favorites families share one cover-art cache (deduplicated by pattern), while the PS1/VCD view keeps its own — separate PS2 and PS1 views reuse the device's game list indices, so a shared cache would thrash covers on every toggle. A mixed list therefore draws from two caches at once, PS1 rows from the VCD one and everything else from the shared one, and both are warmed.

⚠ Contiguous-numbering rule

Blocks are read in order from 0 and the engine stops at the first gap. If you define main0, main1, main3 (skipping main2), then main3 is never read. When you delete a block, renumber the ones after it so there are no holes. The same applies inside each info* sequence. (A commented-out block counts as a deletion — re-number around it.)

Why does a Background have to be element 0?

The first element of a list must be the Background (the engine inserts a default plasma background if you forget). Likewise only the first ItemsList becomes the games list, the second the apps list, the third the Favorites list and the fourth the PS1/VCD list — so order matters beyond just draw order. The PS1/VCD slot is optional: define only three lists and the PS1 view falls back to the games list, sharing its cover cache. Give it a fourth and it gets its own.

3 · Global properties

These go at the top of the file, outside any block.

Colours (#RRGGBB)

Key Applies to
bg_color Plasma / background tint (the gradient's HIGH end)
plasma_blend_color Plasma gradient LOW end — defaults to black, which reproduces the historical look
text_color Default element text colour
ui_text_color UI text (menus, buttons, hints)
sel_text_color Selected-item text colour

Colours are 8 bits per channel in hex; alpha is fixed internally (the parser sets it for you).

Fonts

Key Value Notes
default_font font file path The theme's main font; falls back to the built-in font if the file is missing.
font1font15 font file path Extra fonts; each defaults to default_font if not set.
default_font_size int (px) Size for the default font (defaults to the system default if ≤ 0).
font1_sizefont15_size int (px) Per-font sizes.

Reference a font from an element with font=N (0 = default). font1=builtin is not a real token: the parser builds the path <theme>/builtin, that file doesn't exist, so the load fails and the slot falls back to the embedded default font. The <Coverflow> theme relies on this file-not-found fallback (and for the built-in <Coverflow> the font keys aren't parsed at all, since font loading is skipped for buffer-loaded built-in themes).

Other global keys

Key Value Meaning
use_default 0 / 1 Use OPL's embedded default assets for anything the theme omits (default 1).
use_real_height 0 / 1 Lay out against the real screen height instead of the virtual 480.
use_settings_bg 0 / 1 Load the theme's own settings_bg.png behind the settings menus instead of the plasma. Theme-supplied only — there is no embedded default, so the built-in themes leave the plasma in place.
coverflow_cover_offset int (clamped −1024…1024, default 0) Horizontal nudge for the Coverflow carousel (see §9).

i Tinting the plasma

The plasma background is a gradient between two colours: plasma_blend_color is the low end and bg_color the high end. Setting only bg_color keeps the classic look, because plasma_blend_color defaults to black. Set both to tint the whole gradient.

4 · Common element properties

Every element block starts with type=<ElementType> (see §5). Almost all of them also accept these positioning/appearance properties:

Property Type Notes
x int or POS_MID Horizontal position. Negative = measured from the right edge. POS_MID = horizontal centre.
y int or POS_MID Vertical position. Negative = measured from the bottom edge. POS_MID = vertical centre.
width int or DIM_INF DIM_INF = full screen width.
height int or DIM_INF DIM_INF = full screen height.
aligned 0 / 1 0 = anchor top-left; 1 = centre the element on (x, y).
scaled 0 / 1 0 = raw pixels; 1 = ratio-correct (handles widescreen + pixel aspect). Use 1 for images you want undistorted.
color #RRGGBB Text/tint colour. Defaults to text_color.
font 0 … 15 Font index (see §3). Default 0.
reflection 0 / 1 Draw a mirrored, alpha-faded reflection beneath the element (used by Coverflow). Default 0.
reflection_offset int (clamped −1024…1024) Shift the reflection vertically (e.g. to clear a case base).
enabled 0 / 1 Set 0 to skip the element without deleting the block. Default 1.
devices name list This fork. Show the element only on the listed device pages — honoured by MenuIcon, ItemsList and HintText (ignored elsewhere). See Per-device placement.

Value tokens: POS_MID and DIM_INF (above), and #RRGGBB for colours. Negative x/y anchor to the right/bottom edge — let those and the tokens do the work instead of hard-coding resolution-specific numbers.

5 · Element types

Set with type=. Elements marked (item) redraw whenever you move the selection.

type= Renders
Background Full-screen background. With pattern=BG it draws the plasma; with a default= image it draws that. Must be the first element of its list (auto-added if you omit it).
StaticImage A fixed image from default= (supports overlays).
StaticText Fixed text from value=.
AttributeText (item) Game metadata text — see attribute= in §6.
GameCountText (item) The number of items in the current list.
GameImage (item) Dynamic art from pattern= (cover / screenshot / icon …) with a default= fallback.
AttributeImage (item) A badge image chosen by a game attribute — see §6.
MenuIcon The current device/menu icon.
MenuText The current menu name, with left/right arrows when there are sibling menus.
ItemsList (item) The scrolling list of games/apps. First one = games list, second = apps, third = Favorites, fourth = PS1/VCD. Auto-added if omitted. A list with devices= is a per-device override and doesn't count toward those slots.
ItemIcon (item) A per-item icon element (a GameImage bound to the ICO cache).
ItemCover (item) The selected item's cover (a GameImage bound to the COV cache), with optional overlay.
ItemText (item) The selected item's startup filename.
HintText Button hints for the list screen.
InfoHintText Button hints for the info screen.
LoadingIcon The animated loading spinner (only one per theme).
BdmIndex The block-device (BDM) mode indicator — shows which mass device slot is active.
Coverflow (item) This fork: a centred cover-art carousel (see §9).

Text-element extras

Property Used by Notes
value StaticText The literal string to show.
attribute AttributeText, AttributeImage The metadata key (§6).
display text 0 = always show label + value; 1 = only when the value exists; 2 = value only (no label).
wrap text 1 = word-wrap within width/height.
title AttributeText Override the auto-localised label for the attribute.

List extras

Property Used by Notes
decorator ItemsList A GameImage pattern name (e.g. ICO) to draw as a small per-row icon next to each entry.

Per-device placement (devices=) — this fork

Normally one MenuIcon, one ItemsList and one HintText serve every device page, so they share a single position. devices= lets a theme place them differently per device: an element with the key draws only on the listed pages, and an unfiltered element of the same type automatically stands down there (no key needed on it), staying the default everywhere else.

# The shared icon spot, used by every page not claimed below.
main3:
type=MenuIcon
x=POS_MID
y=400

# On the MMCE and APA-HDD pages, pin the icon top-right instead.
main4:
type=MenuIcon
devices=mmce,hdd
x=-40
y=24

Device names (comma-separated, case-insensitive):

Name Page
usb USB mass storage (any slot)
ilink iLink / FireWire
mx4sio MX4SIO
hdd_bd Internal HDD, exFAT (BDM)
hdd Internal HDD, APA/PFS
eth Network (SMB)
smb Alias of eth
mmce MMCE / memory-card emulator
udpbd UDPBD network boot
udpfs UDPFS network boot
app The Apps tab
fav The Favorites tab
bdm The generic BDM page before its driver is identified (auto/manual start)

Rules worth knowing:

  • Only MenuIcon, ItemsList and HintText honour the key; on other element types it parses but has no effect.
  • Matching follows the page's visible device icon, so a BDM page counts as usb/mx4sio/… once its driver is identified, and as bdm before that. In the PS1/VCD (L3) view the page keeps the device's identity — filters keep working there via the vcdMain* family.
  • A filtered ItemsList is an override: it does not consume one of the theme's ItemsList slots (games/apps/favs/VCD, claimed in file order), and navigation/paging automatically follows whichever list is drawn. Give it its own x/y/height (its row count comes from height); decorator= works as usual.
  • Keep one unfiltered ItemsList. If every list in the theme carries devices=, OPL adds a plain default list (373×316 at 42,42) on the pages nothing covers — navigation always needs a list — and it won't match your layout.
  • Unknown device names are logged and skipped; if nothing valid remains, the element behaves as if the key were absent (shared placement) rather than disappearing.
  • Two filtered elements of the same type may list different devices; listing the same device twice draws both — that's an authoring error, not a picker.
  • Themes without the key are untouched — the filter only activates when devices= appears.

Using RiptOPL themes on regular OPL

RiptOPL themes are designed to load on regular/upstream OPL and degrade gracefully — one theme can serve both. Regular OPL's parser never aborts on fork content: keys it doesn't know (devices=, reflection_offset, overlay2, plasma_blend_color, the entire favsMain*, vcdMain*, favsVcdMain* and favsAppsMain* families…) are simply never read, and unknown element types (Coverflow) are skipped without stopping the parse. What to expect there:

  • Invisible: all pure-key extras above — regular OPL renders the theme as if they weren't written.
  • Cosmetic: aligned=2 renders centered; #Size/#DiscType/#System attributes and the Coverflow carousel are silently absent.
  • Ugly but harmless: devices= layouts — regular OPL draws every MenuIcon/HintText block on every page (overlapping), and treats a second ItemsList as the apps list while still drawing it on the games screen. Per-device layouts are effectively RiptOPL-only.
  • ⚠ The one rule: always include one plain (unfiltered) ItemsList. A theme with none — e.g. a Coverflow-only theme, or one where every list carries devices=crashes regular OPL on the first scroll (a latent upstream bug in its default-list repair; RiptOPL fixed it on our side). Ship the plain list and the theme is safe everywhere.

6 · Image & overlay properties (incl. the two-layer system)

Image-type elements (StaticImage, GameImage, Background, AttributeImage, ItemCover, Coverflow) add these:

Property Type Notes
default asset name The image to draw / fall back to (a built-in name from §8, or a file in the theme folder).
pattern cache suffix For dynamic art: COV (cover), SCR / SCR2 (screenshots), ICO (icon), BG (background), …
count int Number of cache slots for pattern (the engine forces a minimum of 2 for COV).
overlay asset name An optional frame drawn around the image (a case / bezel). This is layer 1.
overlay_ulx, overlay_uly int Upper-left corner of the inlaid art inside the overlay.
overlay_urx, overlay_ury int Upper-right corner.
overlay_llx, overlay_lly int Lower-left corner.
overlay_lrx, overlay_lry int Lower-right corner.
overlay2 asset name An optional second overlay (layer 2) painted over overlay. It shares the element's position/size and takes no corners of its own.

The four overlay corners describe where the inner art sits within the overlay frame, in the element's own coordinate space (relative to width/height). They scale with the drawn element, so the cover window tracks the art at any size. Omitted corner keys default to 0.

i The two-layer overlay (RiptOPL)

The draw order is cover → overlayoverlay2. A single asymmetric frame would force the cover off-centre; splitting the frame into two layers over a centred cover avoids that shift. The fork's graphics use it as: the cover, then case (the transparent plastic / case frame, layer 1), then case_overlay (foliage / decoration, layer 2). Both layers are de-duplicated across every element that names the same asset, so re-using a layer costs no extra VRAM. overlay2 is drawn as a plain pixmap over the cover composite — it never takes corner coordinates.

7 · attribute= values

AttributeText (metadata text)

For type=AttributeText, set attribute= to a metadata key. The label is auto-localised (override it with title=):

attribute= Shows
Title Game title
Genre Genre
Developer Developer / publisher
Release Release date
Description Long description (use wrap=1 + a width/height box)
#Size Install size — the value is auto-suffixed with MiB

AttributeImage (badge chosen by the game's value)

For type=AttributeImage, set attribute= to one of the keys below. OPL reads the game's current value for that attribute; where the glyph comes from depends on the theme:

  • Built-in <OPL> theme — draws the embedded glyph whose internal name equals the value: #System=PS2PS2, #Media=CDCD, #Format=ISOISO. Attributes with no embedded glyph (e.g. #DiscType) draw nothing here.
  • Disk theme — loads <value>_<attribute>.png from the theme folder (value first, the same <name>_<suffix> shape as cover art <id>_COV.png): PS2_#System.png, CD_#Media.png, PS2DVD_#DiscType.png. A default= gives the fallback badge.

⚠ Disk-theme filename order

The file is <value>_<attribute>, not <attribute>_<value>PS2DVD_#DiscType.png is correct, #DiscType_PS2DVD.png is never read. The # is a literal filename character, and the extension must be lower-case .png (the PS2 HDD's filesystem is case-sensitive). These badges are populated on every device — USB/BDM, SMB, MMCE and the internal HDD/HDL (and their PS1/VCD lists).

attribute= Axis Value(s) OPL sets Disk-theme file
#System Console (FR #49) PS1, PS2 PS1_#System.png, PS2_#System.png
#Media Disc type CD, DVD (APP for homebrew) CD_#Media.png, DVD_#Media.png
#DiscType Console + media (issue #49) PS1CD, PS2CD, PS2DVD PS1CD_#DiscType.png, PS2CD_#DiscType.png, PS2DVD_#DiscType.png
#Format Image format ISO, ZSO, UL, ELF, HDL, VCD ISO_#Format.png, …
Vmode / Aspect / Scan / Players / Rating Compatibility (value is the full glyph name) Vmode_ntsc, Aspect_w, Scan_480p, Players_2, Rating_3, … <value>_<attribute>.png

i #DiscType — one disc glyph per kind

#System says PS1 vs PS2 and #Media says CD vs DVD, but PS1-CD and PS2-CD both report #Media=CD, so a single #Media glyph can't tell them apart. #DiscType (issue #49) collapses console + media into one token. There are no built-in #DiscType glyphs — supply your own in the theme folder (PS1CD_#DiscType.png / PS2CD_#DiscType.png / PS2DVD_#DiscType.png) and give the element a contiguous mainN/infoN index (the parser stops at the first gap). FR #49 also surfaces #System/#Media on the main page, not just the info page.

8 · Built-in default= asset names

You can point default= or overlay= at any of OPL's embedded textures — no file needed. (For a theme folder, a same-named file overrides the built-in.)

Group Names
Covers / art cover, coverapp, disc, screen, screens (screenshot overlay), missing
Case overlays case (shared frame, layer 1), case_overlay (layer 2, over case), apps_case (legacy apps-only frame)
Device icons usb, mmce, hdd, eth, app, usb_bd, ilk_bd, m4s_bd, hdd_bd
BDM indicators Index_0Index_4
Buttons cross, circle, triangle, square, left, right, select, start
Loading frames load0load7
Boot logo logo, logo0logo6 (animated frames)
Backgrounds incebtion (default theme bg), ip
Format icons ELF, HDL, ISO, ZSO, UL
Media / console APP, CD, DVD, PS1, PS2
Aspect / Scan / Vmode / Rating the badge sets listed in §7

settings_bg is theme-supplied only (no embedded default). Legacy Device_* indicators were removed in this fork — use the device icons and Index_* above.

9 · The Coverflow element

type=Coverflow (this fork) renders the game/app list as a centred cover carousel with a reflection and slide animation. It behaves like a GameImage — it uses the same COV cover cache and overlay/corner system — but draws 3 or 5 covers fanned out around the selection. The first Coverflow element the parser sees flags the theme as "Coverflow active", which unlocks the live Coverflow Settings menu.

Per-theme properties (in conf_theme.cfg)

Property Notes
default Fallback cover when a game's art is missing (e.g. cover, or coverapp for the apps list).
overlay + overlay_* corners The case art drawn around each cover (e.g. case). The corners place the cover inside the case window; the engine auto-centres the visible frame and keeps it aspect-correct in both 4:3 and 16:9.
overlay2 The second case layer (e.g. case_overlay), drawn over each cover composite.
reflection 1 to draw the mirrored reflection below each cover. Tune with reflection_offset.
x, y, width, height Carousel position and per-cover size. width/height should match your case art's pixel size so the overlay corners line up; they also set the box aspect.

⚠ Don't double-correct widescreen

The carousel applies the widescreen / pixel-aspect correction itself at draw time, exactly like the stock cover path. Author your covers' aspect via the element's width:height only — never try to pre-stretch them. Games use a PS2-case portrait box (184×256); apps and PS1/VCD covers use a square box (184×184) to match their square art. Define a vcdMain Coverflow block to give PS1 covers their own square element; omit it and PS1 games reuse appsMain (see §2). More than one of these blocks is live at once on a mixed list, where each row's cover picks the one for its own media kind — so a theme that overrides favsMain2 should size it as its PS2 cover and define vcdMain2 / appsMain2 for the other two kinds, rather than leaving the whole shelf on one shape. To give PS1 or app favorites a carousel of their own, declare a Coverflow block in favsVcdMain* or favsAppsMain*: the Favorites PS1 / ELF view draws it, and the All in One shelf uses its cover shape for those rows. A Coverflow block in any family makes the whole theme a Coverflow theme (Left/Right steps through the list on every browse page), so do this in a theme that already uses Coverflow.

A minimal three-family Coverflow setup (the shape the fork's <Coverflow> theme uses):

main2:
    type=Coverflow
    default=cover
    reflection=1
    y=197
    width=184
    height=256        # portrait PS2 case
    overlay=case
    overlay2=case_overlay
    overlay_ulx=0    overlay_uly=0    overlay_urx=184  overlay_ury=0
    overlay_llx=0    overlay_lly=256  overlay_lrx=184  overlay_lry=256
appsMain2:
    type=Coverflow
    default=coverapp
    reflection=1
    y=239
    width=184
    height=184        # square apps box
    overlay=case
    overlay2=case_overlay
    overlay_ulx=0    overlay_uly=0    overlay_urx=184  overlay_ury=0
    overlay_llx=0    overlay_lly=184  overlay_lrx=184  overlay_lry=184
vcdMain2:
    type=Coverflow
    default=cover
    reflection=1
    y=239
    width=184
    height=184        # square PS1 jewel-case box
    overlay=case
    overlay2=case_overlay
    overlay_ulx=0    overlay_uly=0    overlay_urx=184  overlay_ury=0
    overlay_llx=0    overlay_lly=184  overlay_lrx=184  overlay_lry=184

Global Coverflow tuning (NOT in the theme)

These live in settings_riptopl.cfg and are exposed in the Coverflow Settings menu (shown while a Coverflow theme is active). They apply to any Coverflow theme and are clamped on load:

Config key Values Effect
coverflow_count 3 or 5 Number of covers shown (anything else is forced to 3).
coverflow_scale px (0…1000) How much the centre cover grows over the side covers.
coverflow_anim ms (0…5000) Slide-animation duration; 0 = instant, no animation.
coverflow_dim 0 / 1 Dim the non-centre covers.

The per-theme horizontal nudge coverflow_cover_offset is the one Coverflow knob that does live in the theme (a global key — see §3). For the in-menu walkthrough of these settings, see Coverflow.

10 · Coordinates & scaling

Author on 640×480

All positions/sizes are written for a virtual 640×480 canvas. OPL scales that to the active video mode at draw time — you never hard-code 480p vs. 1080i numbers.

Use scaled=1 for undistorted art

With scaled=1, image widths are corrected for widescreen and the PS2's non-square pixels, so a square stays square in both 4:3 and 16:9. Positions are not aspect-scaled, so layouts simply fill the wider screen naturally.

Anchor to edges, not pixels

Negative x/y anchor to the right/bottom edge; POS_MID centres; DIM_INF fills. Prefer these over magic numbers so a layout survives resolution changes.

11 · Worked examples

An info-page metadata label

info3:
    type=AttributeText
    attribute=Title       # the game's title…
    color=#ffffff
    display=0             # 0 = always show "Title: <value>"
    aligned=0            # anchor top-left at (x,y)
    x=33
    y=70

Switch display=2 to drop the "Title: " label and print the value alone; add wrap=1 with a width/height box for a long Description.

A framed cover with the two-layer case

main5:
    type=ItemCover         # selected game's cover, COV cache
    default=cover         # fallback when art is missing
    x=-128                # 128px in from the right edge
    y=240
    overlay=case          # layer 1: plastic case frame
    overlay2=case_overlay # layer 2: foliage/decoration, over the cover
    overlay_ulx=0    overlay_uly=0
    overlay_urx=184  overlay_ury=0
    overlay_llx=0    overlay_lly=256   # cover window = full 184×256 of the frame
    overlay_lrx=184  overlay_lry=256

A screenshot with a bezel overlay

info14:
    type=GameImage
    pattern=SCR           # first screenshot cache
    default=screen
    x=189
    y=-150               # 150px up from the bottom
    scaled=1             # any non-zero = ratio-correct (same as 1)
    width=200
    height=150
    overlay=screens       # 1px inset bezel around the shot
    overlay_ulx=1    overlay_uly=1    overlay_urx=199  overlay_ury=1
    overlay_llx=1    overlay_lly=149  overlay_lrx=199  overlay_lry=149

12 · A complete, annotated sample theme

This is the shipped default <OPL> theme (misc/conf_theme_OPL.cfg), annotated. It is a full, valid conf_theme.cfg — copy it into THM/thm_MyTheme/, edit, and you have a working theme. Note the contiguous numbering and the single appsMain5 override that swaps the games' portrait cover box for the apps' square one.

# ── Global colours ───────────────────────────────────────────────
bg_color=#182580          # plasma tint
sel_text_color=#ffffff    # selected list row
text_color=#324d9e        # default text
ui_text_color=#00FFFF     # menus / hints

# ── Games LIST page (main*) ──────────────────────────────────────
main0:                    # element 0 MUST be the Background
    type=Background
    pattern=BG            # plasma background
main1:
    type=StaticImage
    default=incebtion     # embedded background art
    aligned=0
    scaled=0
    width=DIM_INF         # fill the screen
    height=DIM_INF
main2:
    type=StaticImage
    default=ip            # small corner image
    x=-132               # 132px in from the right
    y=20
    aligned=0
    width=16
    height=16
main3:
    type=MenuIcon         # current device icon
    x=0                   # (add devices=mmce,hdd,… to place this per device — see §5)
    y=0
    aligned=0
    scaled=0
    width=DIM_INF
    height=DIM_INF
main4:
    type=ItemsList        # the scrolling game list (first ItemsList = games)
    x=30
    y=70
    aligned=0
    width=400
    height=350
main5:
    type=ItemCover        # selected cover, with the two-layer case frame
    default=cover
    x=-128
    y=240
    overlay=case          # layer 1 (plastic case)
    overlay2=case_overlay # layer 2 (over the cover)
    overlay_ulx=0    overlay_uly=0
    overlay_urx=184  overlay_ury=0
    overlay_llx=0    overlay_lly=256   # portrait 184×256 window
    overlay_lrx=184  overlay_lry=256
appsMain5:                # APPS override of slot 5: square cover box
    type=ItemCover
    default=coverapp
    x=-128
    y=-198               # 198px up from the bottom
    overlay=case
    overlay2=case_overlay
    overlay_ulx=0    overlay_uly=0
    overlay_urx=184  overlay_ury=0
    overlay_llx=0    overlay_lly=185   # square 184×185 window
    overlay_lrx=184  overlay_lry=185
main6:
    type=LoadingIcon      # animated spinner (one per theme)
    x=-320
    y=-240
main7:
    type=BdmIndex         # active mass-device slot indicator
    x=7
    y=-35
    width=4
    height=32

# ── Game INFO page (info*) ───────────────────────────────────────
info0:
    type=Background
    pattern=BG
info1:
    type=StaticImage
    default=incebtion
    aligned=0
    scaled=0
    width=DIM_INF
    height=DIM_INF
info2:
    type=StaticImage
    default=ip
    x=-132
    y=20
    aligned=0
    width=16
    height=16
info3:
    type=AttributeText    # Title:
    attribute=Title
    color=#ffffff
    display=0
    aligned=0
    x=33
    y=70
info4:
    type=AttributeText    # Developer:
    attribute=Developer
    color=#ffffff
    display=0
    aligned=0
    x=33
    y=89
info5:
    type=AttributeText    # Size: (auto-suffixed " MiB")
    attribute=#Size
    color=#ffffff
    display=0
    aligned=0
    x=33
    y=108
info6:
    type=AttributeText    # word-wrapped description box
    attribute=Description
    display=0
    color=#ffffff
    aligned=0
    x=33
    y=127
    width=590
    height=60
    wrap=1
info7:
    type=AttributeImage   # disc badge (CD/DVD/APP)
    attribute=#Media
    default=APP
    width=32
    height=16
    x=55
    y=-252
info8:
    type=AttributeImage   # format badge (ISO/ELF/…)
    attribute=#Format
    default=ELF
    width=32
    height=16
    x=86
    y=-252
info9:
    type=AttributeImage   # video-mode badge
    attribute=Vmode
    default=missing
    width=32
    height=16
    x=117
    y=-252
info10:
    type=AttributeImage   # aspect badge
    attribute=Aspect
    default=missing
    width=32
    height=16
    x=148
    y=-252
info11:
    type=AttributeImage   # scan-mode badge
    attribute=Scan
    default=missing
    width=32
    height=16
    x=179
    y=-252
info12:
    type=AttributeImage   # players badge
    attribute=Players
    default=missing
    width=32
    height=16
    x=210
    y=-252
info13:
    type=AttributeImage   # rating badge
    attribute=Rating
    default=no_Rating
    x=-76
    y=-252
    width=128
    height=16
info14:
    type=GameImage        # screenshot 1, with a 1px bezel overlay
    pattern=SCR
    default=screen
    x=189
    y=-150
    scaled=2              # any non-zero = ratio-correct (same as 1)
    width=200
    height=150
    overlay=screens
    overlay_ulx=1    overlay_uly=1    overlay_urx=199  overlay_ury=1
    overlay_llx=1    overlay_lly=149  overlay_lrx=199  overlay_lry=149
info15:
    type=GameImage        # screenshot 2
    pattern=SCR2
    default=screen
    x=-191
    y=-150
    width=200
    height=150
    overlay=screens
    overlay_ulx=1    overlay_uly=1    overlay_urx=199  overlay_ury=1
    overlay_llx=1    overlay_lly=149  overlay_lrx=199  overlay_lry=149

See also

Detailed reference: Theme engine: full authoring reference.


Source: themes.html @ fc2e13154e51. Read on the documentation website.

Clone this wiki locally