diff --git a/README.md b/README.md index 6ec751c64..7ae1af1be 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,25 @@ that contains the preferred partition name (for example `__common`).

+
+ Cheats +

+ +OPL accepts `.cht` files in PS2RD format. Each cheat file corresponds to a specific game and must be stored in the `CHT` directory on your device. +Cheats are structured as hexadecimal codes, with proper headers as descriptions to identify their function. +You can activate cheats via OPL's graphical interface. Navigate to a games settings, enable cheats and select the desired mode. + +### cheat modes + + * Auto Select Cheats: +This mode will enable and apply all cheat codes in your `.cht` file to your game automatically. + + * Select Game Cheats: +When enabled a cheat selection menu will appear when you launch a game. You can navigate the menu and disable undesired cheats for this launch session. `Mastercode`s cannot be disabled as they are required for any other cheats to be applied. + +

+
+
NBD Server

diff --git a/ee_core/include/coreconfig.h b/ee_core/include/coreconfig.h index b3d6a0a77..eac6e6ab2 100644 --- a/ee_core/include/coreconfig.h +++ b/ee_core/include/coreconfig.h @@ -45,7 +45,7 @@ struct EECoreConfig_t char g_ps2_gateway[16]; unsigned char g_ps2_ETHOpMode; - unsigned int *gCheatList; // Store hooks/codes addr+val pairs + u32 *gCheatList; // Store hooks/codes addr+val pairs void *eeloadCopy; void *initUserMemory; diff --git a/include/cheatman.h b/include/cheatman.h index 2068a1736..7d828e4f3 100644 --- a/include/cheatman.h +++ b/include/cheatman.h @@ -34,11 +34,12 @@ #include #include -#define CHEAT_VERSION "0.5.3.65.g774d1" +#define CHEAT_VERSION "0.5.3.7" -#define MAX_HOOKS 5 -#define MAX_CODES 250 -#define MAX_CHEATLIST (MAX_HOOKS * 2 + MAX_CODES * 2) +#define MAX_HOOKS 5 +#define MAX_CODES 250 +#define MAX_CHEATLIST (MAX_HOOKS * 2 + MAX_CODES * 2) +#define CHEAT_NAME_MAX 128 /* Some character defines */ #define NUL 0x00 @@ -59,9 +60,19 @@ typedef struct u32 val; } code_t; +typedef struct +{ + char name[CHEAT_NAME_MAX + 1]; + code_t codes[MAX_CHEATLIST]; + int enabled; +} cheat_entry_t; + +extern cheat_entry_t gCheats[MAX_CODES]; + void InitCheatsConfig(config_set_t *configSet); int GetCheatsEnabled(void); const u32 *GetCheatsList(void); int load_cheats(const char *cheatfile); +void set_cheats_list(void); #endif /* _CHEATMAN_H_ */ diff --git a/include/fntsys.h b/include/fntsys.h index ea58879aa..2706fe30d 100644 --- a/include/fntsys.h +++ b/include/fntsys.h @@ -6,6 +6,8 @@ /// Value returned on errors #define FNT_ERROR (-1) +#define FNTSYS_DEFAULT_SIZE 17 + /** Initializes the font subsystem */ void fntInit(); @@ -15,7 +17,7 @@ void fntEnd(); /** Loads a font from a file path * @param path The path to the font file * @return font slot id (negative value means error happened) */ -int fntLoadFile(char *path); +int fntLoadFile(char *path, int fontSize); /** Reloads the default font */ int fntLoadDefault(char *path); diff --git a/include/gui.h b/include/gui.h index 7b51cb4d6..44c697409 100644 --- a/include/gui.h +++ b/include/gui.h @@ -156,4 +156,6 @@ int guiConfirmVideoMode(void); int guiGameShowRemoveSettings(config_set_t *configSet, config_set_t *configGame); +void guiManageCheats(void); + #endif diff --git a/lng_tmpl/_base.yml b/lng_tmpl/_base.yml index 37ca324bd..89eae9a57 100644 --- a/lng_tmpl/_base.yml +++ b/lng_tmpl/_base.yml @@ -680,3 +680,7 @@ gui_strings: string: Analog X-Axis Sensitivity - label: YSENSITIVITY string: Analog Y-Axis Sensitivity +- label: FILE_COUNT + string: 'Files found: %i' +- label: CHEAT_SELECTION + string: Cheat Selection diff --git a/src/cheatman.c b/src/cheatman.c index 22cea59f5..5fb538b10 100644 --- a/src/cheatman.c +++ b/src/cheatman.c @@ -30,6 +30,7 @@ static int gEnableCheat; // Enables PS2RD Cheat Engine - 0 for Off, 1 for On static int gCheatMode; // Cheat Mode - 0 Enable all cheats, 1 Cheats selected by user static u32 gCheatList[MAX_CHEATLIST]; // Store hooks/codes addr+val pairs +cheat_entry_t gCheats[MAX_CODES]; void InitCheatsConfig(config_set_t *configSet) { @@ -39,7 +40,6 @@ void InitCheatsConfig(config_set_t *configSet) gCheatSource = 0; gEnableCheat = 0; gCheatMode = 0; - memset(gCheatList, 0, sizeof(gCheatList)); if (configGetInt(configSet, CONFIG_ITEM_CHEATSSOURCE, &gCheatSource)) { // Load the rest of the per-game CHEAT configuration if CHEAT is enabled. @@ -80,6 +80,8 @@ static code_t make_code(const char *s) s++; } + digits[i] = '\0'; + sscanf(digits, "%08X %08X", &address, &value); // Return Code Address and Value @@ -244,17 +246,18 @@ static int parse_buf(const char *buf) code_t code; char line[CHEAT_LINE_MAX + 1]; int linenumber = 1; + int cheat_index = -1; // Starts at -1.. indicating no cheat being processed yet + int code_index = 0; + char temp_name[CHEAT_NAME_MAX + 1] = {0}; if (buf == NULL) return -1; - int i = 0; - while (*buf) { /* Scanner */ int len = chr_idx(buf, LF); if (len < 0) - len = strlen(line); + len = strlen(buf); else if (len > CHEAT_LINE_MAX) len = CHEAT_LINE_MAX; @@ -266,23 +269,35 @@ static int parse_buf(const char *buf) term_str(line, is_cmt_str); trim_str(line); - /* Parser */ - code = parse_line(line, linenumber); - if (!((code.addr == 0) && (code.val == 0))) { - gCheatList[i] = code.addr; - i++; - gCheatList[i] = code.val; - i++; + // Check if the line is a cheat name + if (!is_cheat_code(line) && strlen(line) > 0) { + // Store the cheat name temporarily + strncpy(temp_name, line, CHEAT_NAME_MAX); + temp_name[CHEAT_NAME_MAX] = NUL; + // Reset code index in case this is a new cheat + code_index = 0; + } else { + /* Parser */ + code = parse_line(line, linenumber); + if (!(code.addr == 0 && code.val == 0)) { + // Only add the cheat entry if we have a valid cheat name in temp_name + if (cheat_index < MAX_CODES && temp_name[0] != NUL) { + cheat_index++; // Move to the next cheat entry + strncpy(gCheats[cheat_index].name, temp_name, CHEAT_NAME_MAX); + gCheats[cheat_index].name[CHEAT_NAME_MAX] = NUL; + gCheats[cheat_index].enabled = 1; // Set cheat as enabled + temp_name[0] = NUL; // Clear temp_name after use + } + // Add the cheat code to the current cheat entry + if (cheat_index >= 0 && code_index < MAX_CHEATLIST) + gCheats[cheat_index].codes[code_index++] = code; + } } } linenumber++; buf += len + 1; } - gCheatList[i] = 0; - i++; - gCheatList[i] = 0; - return 0; } @@ -304,15 +319,23 @@ static inline char *read_text_file(const char *filename, int maxsize) } filesize = lseek(fd, 0, SEEK_END); - if (maxsize && filesize > maxsize) { + if (filesize < 0) { + LOG("%s: Can't seek in text file %s\n", __FUNCTION__, filename); + close(fd); + return NULL; + } + + if (maxsize > 0 && filesize > maxsize) { LOG("%s: Text file too large: %i bytes, max: %i bytes\n", __FUNCTION__, filesize, maxsize); - goto end; + close(fd); + return NULL; } buf = malloc(filesize + 1); if (buf == NULL) { LOG("%s: Unable to allocate %i bytes\n", __FUNCTION__, filesize + 1); - goto end; + close(fd); + return NULL; } if (filesize > 0) { @@ -320,14 +343,14 @@ static inline char *read_text_file(const char *filename, int maxsize) if (read(fd, buf, filesize) != filesize) { LOG("%s: Can't read from text file %s\n", __FUNCTION__, filename); free(buf); - buf = NULL; - goto end; + close(fd); + return NULL; } } buf[filesize] = '\0'; -end: close(fd); + return buf; } @@ -339,19 +362,52 @@ int load_cheats(const char *cheatfile) char *buf = NULL; int ret; - memset(gCheatList, 0, sizeof(gCheatList)); + memset(gCheats, 0, sizeof(gCheats)); - LOG("%s: Reading cheat file '%s'...", __FUNCTION__, cheatfile); + LOG("%s: Reading cheat file '%s'...\n", __FUNCTION__, cheatfile); buf = read_text_file(cheatfile, 0); if (buf == NULL) { - LOG("\n%s: Could not read cheats file '%s'\n", __FUNCTION__, cheatfile); + LOG("%s: Could not read cheats file '%s'\n", __FUNCTION__, cheatfile); return -1; } - LOG("Ok!\n"); + ret = parse_buf(buf); free(buf); + if (ret < 0) - return -1; - else - return 0; + return ret; + + return (gCheatMode == 0) ? 0 : 1; +} + +void set_cheats_list(void) +{ + int cheatCount = 0; + + memset((void *)gCheatList, 0, sizeof(gCheatList)); + + // Populate the cheat list + for (int i = 0; i < MAX_CODES; ++i) { + if (gCheats[i].enabled) { + for (int j = 0; j < MAX_CHEATLIST && gCheats[i].codes[j].addr != 0; ++j) { + if (cheatCount + 2 <= MAX_CHEATLIST) { + // Store the address and value in gCheatList + gCheatList[cheatCount++] = gCheats[i].codes[j].addr; + gCheatList[cheatCount++] = gCheats[i].codes[j].val; + LOG("%s: Setting cheat for eecore:\n%s\n%08X %08X\n", __FUNCTION__, gCheats[i].name, gCheats[i].codes[j].addr, gCheats[i].codes[j].val); + } else + break; + } + } + } + + // Append a blank cheat entry if theres space + if (cheatCount < MAX_CHEATLIST) { + gCheatList[cheatCount++] = 0; // addr + gCheatList[cheatCount++] = 0; // val + } else { + // Overwrite the last cheat with a blank cheat if we are at max + gCheatList[cheatCount - 2] = 0; // addr + gCheatList[cheatCount - 1] = 0; // val + } } diff --git a/src/fntsys.c b/src/fntsys.c index 0e55a10c3..35f50f10d 100644 --- a/src/fntsys.c +++ b/src/fntsys.c @@ -27,8 +27,6 @@ extern int size_poeveticanew_raw; /// Atlas height in pixels #define ATLAS_HEIGHT 256 -#define FNTSYS_CHAR_SIZE 17 - // freetype vars static FT_Library font_library; @@ -76,6 +74,8 @@ typedef struct /// Nonzero if font is used int isValid; + int fontSize; + /// Texture atlases (default to NULL) atlas_t *atlases[ATLAS_MAX]; @@ -200,6 +200,7 @@ static void fntInitSlot(font_t *font) font->cacheMaxPageID = -1; font->dataPtr = NULL; font->isValid = 0; + font->fontSize = 0; int aid = 0; for (; aid < ATLAS_MAX; ++aid) @@ -220,6 +221,7 @@ static void fntDeleteSlot(font_t *font) } font->isValid = 0; + font->fontSize = 0; } void fntRelease(int id) @@ -228,7 +230,7 @@ void fntRelease(int id) fntDeleteSlot(&fonts[id]); } -static int fntLoadSlot(font_t *font, char *path) +static int fntLoadSlot(font_t *font, char *path, int fontSize) { void *buffer = NULL; int bufferSize = -1; @@ -256,6 +258,7 @@ static int fntLoadSlot(font_t *font, char *path) } font->isValid = 1; + font->fontSize = fontSize; fntUpdateAspectRatio(); return 0; @@ -285,14 +288,14 @@ void fntInit() fntLoadDefault(NULL); } -int fntLoadFile(char *path) +int fntLoadFile(char *path, int fontSize) { font_t *font; int i = 1; for (; i < FNT_MAX_COUNT; i++) { font = &fonts[i]; if (!font->isValid) { - if (fntLoadSlot(font, path) != FNT_ERROR) + if (fntLoadSlot(font, path, fontSize) != FNT_ERROR) return i; break; } @@ -305,7 +308,7 @@ int fntLoadDefault(char *path) { font_t newFont, oldFont; - if (fntLoadSlot(&newFont, path) != FNT_ERROR) { + if (fntLoadSlot(&newFont, path, FNTSYS_DEFAULT_SIZE) != FNT_ERROR) { // copy over the new font definition // we have to lock this phase, as the old font may still be used // Note: No check for concurrency is done here, which is kinda funky! @@ -454,7 +457,7 @@ void fntUpdateAspectRatio() if (fonts[i].isValid) { fntCacheFlush(&fonts[i]); // TODO: this seems correct, but the rest of the OPL UI (i.e. spacers) doesn't seem to be correctly scaled. - FT_Set_Char_Size(fonts[i].face, FNTSYS_CHAR_SIZE * 64, FNTSYS_CHAR_SIZE * 64, fDPI * ws, fDPI * hs); + FT_Set_Char_Size(fonts[i].face, fonts[i].fontSize * 64, fonts[i].fontSize * 64, fDPI * ws, fDPI * hs); } } } @@ -518,9 +521,9 @@ int fntRenderString(int id, int x, int y, short aligned, size_t width, size_t he } if (aligned & ALIGN_VCENTER) { - y += rmScaleY(FNTSYS_CHAR_SIZE - 4) >> 1; + y += rmScaleY(fonts[id].fontSize - 4) >> 1; } else { - y += rmScaleY(FNTSYS_CHAR_SIZE - 2); + y += rmScaleY(fonts[id].fontSize - 2); } quad.color = colour; @@ -644,9 +647,9 @@ int fntRenderString(int id, int x, int y, short aligned, size_t width, size_t he } if (aligned & ALIGN_VCENTER) { - y += rmScaleY(FNTSYS_CHAR_SIZE - 4) >> 1; + y += rmScaleY(fonts[id].fontSize - 4) >> 1; } else { - y += rmScaleY(FNTSYS_CHAR_SIZE - 2); + y += rmScaleY(fonts[id].fontSize - 2); } quad.color = colour; diff --git a/src/gui.c b/src/gui.c index e200d4c6f..a8ed33f73 100644 --- a/src/gui.c +++ b/src/gui.c @@ -1837,3 +1837,80 @@ int guiGameShowRemoveSettings(config_set_t *configSet, config_set_t *configGame) return 1; } + +void guiManageCheats(void) +{ + int offset = 0; + int terminate = 0; + int cheatCount = 0; + int selectedCheat = 0; + int visibleCheats = 10; // Maximum number of cheats visible on screen + + while (cheatCount < MAX_CODES && strlen(gCheats[cheatCount].name) > 0) + cheatCount++; + + sfxPlay(SFX_MESSAGE); + + while (!terminate) { + guiStartFrame(); + readPads(); + + if (getKeyOn(KEY_UP) && selectedCheat > 0) { + selectedCheat -= 1; + if (selectedCheat < offset) + offset = selectedCheat; + } + + if (getKeyOn(KEY_DOWN) && selectedCheat < cheatCount - 1) { + selectedCheat += 1; + if (selectedCheat >= offset + visibleCheats) + offset = selectedCheat - visibleCheats + 1; + } + + if (getKeyOn(gSelectButton)) { + if (!(strncasecmp(gCheats[selectedCheat].name, "mastercode", 10) == 0 || strncasecmp(gCheats[selectedCheat].name, "master code", 11) == 0)) + gCheats[selectedCheat].enabled = !gCheats[selectedCheat].enabled; + } + + if (getKeyOn(KEY_START)) + terminate = 1; + + guiShow(); + + rmDrawRect(0, 0, screenWidth, screenHeight, gColDarker); + rmDrawLine(50, 75, screenWidth - 50, 75, gColWhite); + rmDrawLine(50, 410, screenWidth - 50, 410, gColWhite); + + fntRenderString(gTheme->fonts[0], screenWidth >> 1, 60, ALIGN_CENTER, 0, 0, _l(_STR_CHEAT_SELECTION), gTheme->textColor); + + int renderedCheats = 0; + for (int i = offset; renderedCheats < visibleCheats && i < cheatCount; i++) { + if (strlen(gCheats[i].name) == 0) + continue; + + int enabled = gCheats[i].enabled; + + int boxX = 50; + int boxY = 100 + (renderedCheats * 30); + int boxWidth = rmWideScale(25); + int boxHeight = 17; + + if (enabled) { + rmDrawRect(boxX, boxY + 3, boxWidth, boxHeight, gTheme->textColor); + rmDrawRect(boxX + 2, boxY + 5, boxWidth - 4, boxHeight - 4, gTheme->selTextColor); + } + + u32 textColour = (i == selectedCheat) ? gTheme->selTextColor : gTheme->textColor; + fntRenderString(gTheme->fonts[0], boxX + 35, boxY + 3, ALIGN_LEFT, 0, 0, gCheats[i].name, textColour); + + renderedCheats++; + } + + guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? CIRCLE_ICON : CROSS_ICON, _STR_SELECT, gTheme->fonts[0], 70, 417, gTheme->selTextColor); + guiDrawIconAndText(START_ICON, _STR_RUN, gTheme->fonts[0], 500, 417, gTheme->selTextColor); + + guiEndFrame(); + } + + sfxPlay(SFX_CONFIRM); +} diff --git a/src/opl.c b/src/opl.c index 290b9638f..72a1993f4 100644 --- a/src/opl.c +++ b/src/opl.c @@ -893,7 +893,7 @@ static void _loadConfig() if (!(getKeyPressed(KEY_TRIANGLE) && getKeyPressed(KEY_CROSS))) { configGetInt(configOPL, CONFIG_OPL_VMODE, &gVMode); } else { - LOG("--- Select held at boot - setting Video Mode to Auto ---\n"); + LOG("--- Triangle + Cross held at boot - setting Video Mode to Auto ---\n"); gVMode = 0; configSetInt(configOPL, CONFIG_OPL_VMODE, gVMode); } @@ -1177,6 +1177,13 @@ void applyConfig(int themeID, int langID, int skipDeviceRefresh) moduleUpdateMenuInternal(&list_support[i], changed, langChanged); } + } else { + if (changed) { + for (int i = 0; i < MODE_COUNT; i++) { + if (list_support[i].support && list_support[i].subMenu) + submenuRebuildCache(list_support[i].subMenu); + } + } } bgmUnMute(); diff --git a/src/supportbase.c b/src/supportbase.c index e3c9366a8..b7df52908 100644 --- a/src/supportbase.c +++ b/src/supportbase.c @@ -10,6 +10,7 @@ #include "include/pggsm.h" #include "include/cheatman.h" #include "include/ps2cnf.h" +#include "include/gui.h" #define NEWLIB_PORT_AWARE #include // fileXioMount("iso:", ***), fileXioUmount("iso:") @@ -838,28 +839,20 @@ void sbCreateFolders(const char *path, int createDiscImgFolders) int sbLoadCheats(const char *path, const char *file) { char cheatfile[64]; - const u32 *cheatList; - int result; + int cheatMode = 0; if (GetCheatsEnabled()) { snprintf(cheatfile, sizeof(cheatfile), "%sCHT/%s.cht", path, file); LOG("Loading Cheat File %s\n", cheatfile); - if ((result = load_cheats(cheatfile)) < 0) { - LOG("Error: failed to load cheats\n"); - } else { - cheatList = GetCheatsList(); - if (!((cheatList[0] == 0) && (cheatList[1] == 0))) { - LOG("Cheats found\n"); - result = 0; - } else { - LOG("No cheats found\n"); - result = -ENOENT; - } + if ((cheatMode = load_cheats(cheatfile)) < 0) + LOG("Error: failed to load cheats\n"); + else { + LOG("Cheats found\n"); + if ((gAutoLaunchGame == NULL) && (gAutoLaunchBDMGame == NULL) && (cheatMode == 1)) + guiManageCheats(); } - } else { - result = 0; } - return result; + return cheatMode; } diff --git a/src/system.c b/src/system.c index 011070922..5211dd379 100644 --- a/src/system.c +++ b/src/system.c @@ -895,9 +895,14 @@ void sysLaunchLoaderElf(const char *filename, const char *mode_str, int size_cdv config->EnableDebug = gEnableDebug; config->HDDSpindown = gHDDSpindown; - config->gCheatList = GetCheatsEnabled() ? (unsigned int *)GetCheatsList() : NULL; config->g_ps2_ETHOpMode = gETHOpMode; + if (GetCheatsEnabled()) { + set_cheats_list(); + config->gCheatList = GetCheatsList(); + } else + config->gCheatList = NULL; + sprintf(config->g_ps2_ip, "%u.%u.%u.%u", local_ip_address[0], local_ip_address[1], local_ip_address[2], local_ip_address[3]); sprintf(config->g_ps2_netmask, "%u.%u.%u.%u", local_netmask[0], local_netmask[1], local_netmask[2], local_netmask[3]); sprintf(config->g_ps2_gateway, "%u.%u.%u.%u", local_gateway[0], local_gateway[1], local_gateway[2], local_gateway[3]); diff --git a/src/themes.c b/src/themes.c index 2e5a58649..338dcb1c7 100644 --- a/src/themes.c +++ b/src/themes.c @@ -47,7 +47,7 @@ enum ELEM_ATTRIBUTE_TYPE { ELEM_TYPE_INFO_HINT_TEXT, ELEM_TYPE_LOADING_ICON, ELEM_TYPE_BDM_INDEX, - + ELEM_TYPE_GAME_COUNT_TEXT, ELEM_TYPE_COUNT }; @@ -76,7 +76,7 @@ static const char *elementsType[ELEM_TYPE_COUNT] = { "InfoHintText", "LoadingIcon", "BdmIndex", -}; + "GameCountText"}; // Common functions for Text //////////////////////////////////////////////////////////////////////////////////////////////// @@ -198,6 +198,42 @@ static void initStaticText(const char *themePath, config_set_t *themeConfig, the LOG("THEMES StaticText %s: NO value, elem disabled !!\n", name); } +// GameCountText //////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static int getGameCount(void *support) +{ + item_list_t *list = (item_list_t *)support; + return list->itemGetCount(list); +} + +static void drawGameCountText(struct menu_list *menu, struct submenu_list *item, config_set_t *config, struct theme_element *elem) +{ + mutable_text_t *mutableText = (mutable_text_t *)elem->extended; + + if (config) { + if (mutableText->currentConfigId != config->uid) { + // force refresh + mutableText->currentConfigId = config->uid; + + int count = getGameCount(menu->item->userdata); + snprintf(mutableText->value, sizeof(char) * 60, _l(_STR_FILE_COUNT), count); + } + } + + fntRenderString(elem->font, elem->posX, elem->posY, elem->aligned, 0, 0, mutableText->value, elem->color); +} + +static void initGameCountText(const char *themePath, config_set_t *themeConfig, theme_t *theme, theme_element_t *elem, const char *name) +{ + int length = 60; + char *countStr = (char *)malloc(length * sizeof(char)); + memset(countStr, 0, length * sizeof(char)); + + elem->extended = initMutableText(themePath, themeConfig, theme, name, ELEM_TYPE_ATTRIBUTE_TEXT, elem, countStr, NULL, DISPLAY_ALWAYS, SIZING_NONE); + elem->endElem = &endMutableText; + elem->drawElem = &drawGameCountText; +} + // AttributeText //////////////////////////////////////////////////////////////////////////////////////////////////////////// static void drawAttributeText(struct menu_list *menu, struct submenu_list *item, config_set_t *config, struct theme_element *elem) @@ -990,6 +1026,9 @@ static int addGUIElem(const char *themePath, config_set_t *themeConfig, theme_t } else if (!strcmp(elementsType[ELEM_TYPE_STATIC_TEXT], type)) { elem = initBasic(themePath, themeConfig, theme, name, ELEM_TYPE_STATIC_TEXT, 0, 0, ALIGN_CENTER, DIM_UNDEF, DIM_UNDEF, SCALING_RATIO, theme->textColor, theme->fonts[0]); initStaticText(themePath, themeConfig, theme, elem, name); + } else if (!strcmp(elementsType[ELEM_TYPE_GAME_COUNT_TEXT], type)) { + elem = initBasic(themePath, themeConfig, theme, name, ELEM_TYPE_STATIC_TEXT, 0, 0, ALIGN_CENTER, DIM_UNDEF, DIM_UNDEF, SCALING_RATIO, theme->textColor, theme->fonts[0]); + initGameCountText(themePath, themeConfig, theme, elem, name); } else if (!strcmp(elementsType[ELEM_TYPE_ATTRIBUTE_IMAGE], type)) { elem = initBasic(themePath, themeConfig, theme, name, ELEM_TYPE_ATTRIBUTE_IMAGE, 0, 0, ALIGN_CENTER, DIM_UNDEF, DIM_UNDEF, SCALING_RATIO, gDefaultCol, theme->fonts[0]); initAttributeImage(themePath, themeConfig, theme, elem, name); @@ -1185,8 +1224,18 @@ static void thmLoadFonts(config_set_t *themeConfig, const char *themePath, theme const char *fntFile; if (configGetStr(themeConfig, fntKey, &fntFile)) { snprintf(fullPath, sizeof(fullPath), "%s%s", themePath, fntFile); - int fntHandle = fntLoadFile(fullPath); + int fontSize; + char sizeKey[64]; + if (fntID == 0) + snprintf(sizeKey, sizeof(sizeKey), "default_font_size"); + else + snprintf(sizeKey, sizeof(sizeKey), "font%d_size", fntID); + + if (!configGetInt(themeConfig, sizeKey, &fontSize) || fontSize <= 0) + fontSize = FNTSYS_DEFAULT_SIZE; + + int fntHandle = fntLoadFile(fullPath, fontSize); // Do we have a valid font? Assign the font handle to the theme font slot if (fntHandle != FNT_ERROR) theme->fonts[fntID] = fntHandle;