From c16acc73cc1b78c854ded36231665ba936941f03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Ren=C3=A9=20Caballero=20Marquez?= Date: Sun, 16 Nov 2025 11:15:04 -0300 Subject: [PATCH 01/10] Fix carousel logo overlap by ensuring center logo renders last MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes a long-standing visual issue in the system carousel where the side logos could overlap the center logo when using large logoScale values or wide system logos (common in modern themes). The update introduces a draw-order correction: - Collects all logos before rendering - Sorts by abs(distance from center) - Draws farthest logos first and the center logo last This guarantees that the center logo always appears visually on top of the side logos without modifying their size, spacing, alignment, or theme behavior. The change is fully backward compatible: - Does NOT alter theme XML attributes - Does NOT change default carousel layout - Does NOT modify spacing, scaling, or logo alignment - Only fixes z-order (render order) in all horizontal and vertical modes This aligns EmulationStation’s behavior with Batocera/ES-DE visual standards and corrects a noticeable rendering bug without affecting any existing theme. --- es-app/src/views/SystemView.cpp | 295 ++++++++++++++++++-------------- 1 file changed, 168 insertions(+), 127 deletions(-) diff --git a/es-app/src/views/SystemView.cpp b/es-app/src/views/SystemView.cpp index d01c432b27..4171fbff1b 100644 --- a/es-app/src/views/SystemView.cpp +++ b/es-app/src/views/SystemView.cpp @@ -10,6 +10,9 @@ #include "SystemData.h" #include "Window.h" +#include +#include + // buffer values for scrolling velocity (left, stopped, right) const int logoBuffersLeft[] = { -5, -2, -1 }; const int logoBuffersRight[] = { 1, 2, 5 }; @@ -30,14 +33,14 @@ void SystemView::populate() { mEntries.clear(); - for(auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++) + for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++) { const std::shared_ptr& theme = (*it)->getTheme(); - if(mViewNeedsReload) + if (mViewNeedsReload) getViewElements(theme); - if((*it)->isVisible()) + if ((*it)->isVisible()) { Entry e; e.name = (*it)->getName(); @@ -45,12 +48,12 @@ void SystemView::populate() // make logo const ThemeData::ThemeElement* logoElem = theme->getElement("system", "logo", "image"); - if(logoElem) + if (logoElem) { std::string path = logoElem->get("path"); std::string defaultPath = logoElem->has("default") ? logoElem->get("default") : ""; - if((!path.empty() && ResourceManager::getInstance()->fileExists(path)) - || (!defaultPath.empty() && ResourceManager::getInstance()->fileExists(defaultPath))) + if ((!path.empty() && ResourceManager::getInstance()->fileExists(path)) + || (!defaultPath.empty() && ResourceManager::getInstance()->fileExists(defaultPath))) { ImageComponent* logo = new ImageComponent(mWindow, false, false); logo->setMaxSize(mCarousel.logoSize * mCarousel.logoScale); @@ -75,7 +78,8 @@ void SystemView::populate() { text->setHorizontalAlignment(mCarousel.logoAlignment); text->setVerticalAlignment(ALIGN_CENTER); - } else { + } + else { text->setHorizontalAlignment(ALIGN_CENTER); text->setVerticalAlignment(mCarousel.logoAlignment); } @@ -89,7 +93,8 @@ void SystemView::populate() e.data.logo->setOrigin(1.0, 0.5); else e.data.logo->setOrigin(0.5, 0.5); - } else { + } + else { if (mCarousel.logoAlignment == ALIGN_TOP) e.data.logo->setOrigin(0.5, 0); else if (mCarousel.logoAlignment == ALIGN_BOTTOM) @@ -109,9 +114,9 @@ void SystemView::populate() e.data.backgroundExtras = ThemeData::makeExtras((*it)->getTheme(), "system", mWindow); // sort the extras by z-index - std::stable_sort(e.data.backgroundExtras.begin(), e.data.backgroundExtras.end(), [](GuiComponent* a, GuiComponent* b) { + std::stable_sort(e.data.backgroundExtras.begin(), e.data.backgroundExtras.end(), [](GuiComponent* a, GuiComponent* b) { return b->getZIndex() > a->getZIndex(); - }); + }); this->add(e); } @@ -131,15 +136,15 @@ void SystemView::goToSystem(SystemData* system, bool animate) { setCursor(system); - if(!animate) + if (!animate) finishAnimation(0); } bool SystemView::input(InputConfig* config, Input input) { - if(input.value != 0) + if (input.value != 0) { - if(config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_r && SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) + if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_r && SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) { LOG(LogInfo) << " Reloading all"; ViewController::get()->reloadAll(); @@ -177,7 +182,7 @@ bool SystemView::input(InputConfig* config, Input input) break; } - if(config->isMappedTo("a", input)) + if (config->isMappedTo("a", input)) { stopScrolling(); ViewController::get()->goToGameList(getSelected()); @@ -190,14 +195,15 @@ bool SystemView::input(InputConfig* config, Input input) setCursor(SystemData::getRandomSystem()); return true; } - }else{ - if(config->isMappedLike("left", input) || + } + else { + if (config->isMappedLike("left", input) || config->isMappedLike("right", input) || config->isMappedLike("up", input) || config->isMappedLike("down", input)) listInput(0); Scripting::fireEvent("system-select", this->IList::getSelected()->getName(), "input"); - if(!UIModeController::getInstance()->isUIModeKid() && config->isMappedTo("select", input) && Settings::getInstance()->getBool("ScreenSaverControls")) + if (!UIModeController::getInstance()->isUIModeKid() && config->isMappedTo("select", input) && Settings::getInstance()->getBool("ScreenSaverControls")) { mWindow->startScreenSaver(); mWindow->renderScreenSaver(); @@ -230,9 +236,9 @@ void SystemView::onCursorChanged(const CursorState& /*state*/) float endPos = target; // directly float dist = abs(endPos - startPos); - if(abs(target + posMax - startPos) < dist) + if (abs(target + posMax - startPos) < dist) endPos = target + posMax; // loop around the end (0 -> max) - if(abs(target - posMax - startPos) < dist) + if (abs(target - posMax - startPos) < dist) endPos = target - posMax; // loop around the start (max - 1 -> -1) @@ -246,10 +252,10 @@ void SystemView::onCursorChanged(const CursorState& /*state*/) const float infoStartOpacity = mSystemInfo.getOpacity() / 255.f; Animation* infoFadeOut = new LambdaAnimation( - [infoStartOpacity, this] (float t) - { - mSystemInfo.setOpacity((unsigned char)(Math::lerp(infoStartOpacity, 0.f, t) * 255)); - }, (int)(infoStartOpacity * (goFast ? 10 : 150))); + [infoStartOpacity, this](float t) + { + mSystemInfo.setOpacity((unsigned char)(Math::lerp(infoStartOpacity, 0.f, t) * 255)); + }, (int)(infoStartOpacity * (goFast ? 10 : 150))); unsigned int gameCount = getSelected()->getDisplayedGameCount(); @@ -263,80 +269,82 @@ void SystemView::onCursorChanged(const CursorState& /*state*/) ss << gameCount << " GAME" << (gameCount == 1 ? "" : "S") << " AVAILABLE"; mSystemInfo.setText(ss.str()); - }, false, 1); + }, false, 1); Animation* infoFadeIn = new LambdaAnimation( [this](float t) - { - mSystemInfo.setOpacity((unsigned char)(Math::lerp(0.f, 1.f, t) * 255)); - }, goFast ? 10 : 300); + { + mSystemInfo.setOpacity((unsigned char)(Math::lerp(0.f, 1.f, t) * 255)); + }, goFast ? 10 : 300); // wait 600ms to fade in setAnimation(infoFadeIn, goFast ? 0 : 2000, nullptr, false, 2); // no need to animate transition, we're not going anywhere (probably mEntries.size() == 1) - if(endPos == mCamOffset && endPos == mExtrasCamOffset) + if (endPos == mCamOffset && endPos == mExtrasCamOffset) return; Animation* anim; bool move_carousel = Settings::getInstance()->getBool("MoveCarousel"); - if(transition_style == "fade") + if (transition_style == "fade") { float startExtrasFade = mExtrasFadeOpacity; anim = new LambdaAnimation( [this, startExtrasFade, startPos, endPos, posMax, move_carousel](float t) - { - t -= 1; - float f = Math::lerp(startPos, endPos, t*t*t + 1); - if(f < 0) - f += posMax; - if(f >= posMax) - f -= posMax; - - this->mCamOffset = move_carousel ? f : endPos; - - t += 1; - if(t < 0.3f) - this->mExtrasFadeOpacity = Math::lerp(0.0f, 1.0f, t / 0.3f + startExtrasFade); - else if(t < 0.7f) - this->mExtrasFadeOpacity = 1.0f; - else - this->mExtrasFadeOpacity = Math::lerp(1.0f, 0.0f, (t - 0.7f) / 0.3f); + { + t -= 1; + float f = Math::lerp(startPos, endPos, t * t * t + 1); + if (f < 0) + f += posMax; + if (f >= posMax) + f -= posMax; + + this->mCamOffset = move_carousel ? f : endPos; + + t += 1; + if (t < 0.3f) + this->mExtrasFadeOpacity = Math::lerp(0.0f, 1.0f, t / 0.3f + startExtrasFade); + else if (t < 0.7f) + this->mExtrasFadeOpacity = 1.0f; + else + this->mExtrasFadeOpacity = Math::lerp(1.0f, 0.0f, (t - 0.7f) / 0.3f); - if(t > 0.5f) - this->mExtrasCamOffset = endPos; + if (t > 0.5f) + this->mExtrasCamOffset = endPos; - }, 500); - } else if (transition_style == "slide") { + }, 500); + } + else if (transition_style == "slide") { // slide anim = new LambdaAnimation( [this, startPos, endPos, posMax, move_carousel](float t) - { - t -= 1; - float f = Math::lerp(startPos, endPos, t*t*t + 1); - if(f < 0) - f += posMax; - if(f >= posMax) - f -= posMax; - - this->mCamOffset = move_carousel ? f : endPos; - this->mExtrasCamOffset = f; - }, 500); - } else { + { + t -= 1; + float f = Math::lerp(startPos, endPos, t * t * t + 1); + if (f < 0) + f += posMax; + if (f >= posMax) + f -= posMax; + + this->mCamOffset = move_carousel ? f : endPos; + this->mExtrasCamOffset = f; + }, 500); + } + else { // instant anim = new LambdaAnimation( - [this, startPos, endPos, posMax, move_carousel ](float t) - { - t -= 1; - float f = Math::lerp(startPos, endPos, t*t*t + 1); - if(f < 0) - f += posMax; - if(f >= posMax) - f -= posMax; - - this->mCamOffset = move_carousel ? f : endPos; - this->mExtrasCamOffset = endPos; - }, move_carousel ? 500 : 1); + [this, startPos, endPos, posMax, move_carousel](float t) + { + t -= 1; + float f = Math::lerp(startPos, endPos, t * t * t + 1); + if (f < 0) + f += posMax; + if (f >= posMax) + f -= posMax; + + this->mCamOffset = move_carousel ? f : endPos; + this->mExtrasCamOffset = endPos; + }, move_carousel ? 500 : 1); } @@ -345,7 +353,7 @@ void SystemView::onCursorChanged(const CursorState& /*state*/) void SystemView::render(const Transform4x4f& parentTrans) { - if(size() == 0) + if (size() == 0) return; // nothing to render Transform4x4f trans = getTransform() * parentTrans; @@ -358,7 +366,8 @@ void SystemView::render(const Transform4x4f& parentTrans) if (mCarousel.zIndex > mSystemInfo.getZIndex()) { renderInfoBar(trans); - } else { + } + else { renderCarousel(trans); } @@ -366,7 +375,8 @@ void SystemView::render(const Transform4x4f& parentTrans) if (mCarousel.zIndex > mSystemInfo.getZIndex()) { renderCarousel(trans); - } else { + } + else { renderInfoBar(trans); } @@ -445,47 +455,47 @@ void SystemView::renderCarousel(const Transform4x4f& trans) switch (mCarousel.type) { - case VERTICAL_WHEEL: - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - (mCamOffset * logoSpacing[1]); - if (mCarousel.logoAlignment == ALIGN_LEFT) - xOff = mCarousel.logoSize.x() / 10.f; - else if (mCarousel.logoAlignment == ALIGN_RIGHT) - xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f); - else - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f; - break; - case VERTICAL: - logoSpacing[1] = ((mCarousel.size.y() - (mCarousel.logoSize.y() * mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.y(); - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - (mCamOffset * logoSpacing[1]); - - if (mCarousel.logoAlignment == ALIGN_LEFT) - xOff = mCarousel.logoSize.x() / 10.f; - else if (mCarousel.logoAlignment == ALIGN_RIGHT) - xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f); - else - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2; - break; - case HORIZONTAL_WHEEL: - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2 - (mCamOffset * logoSpacing[1]); - if (mCarousel.logoAlignment == ALIGN_TOP) - yOff = mCarousel.logoSize.y() / 10; - else if (mCarousel.logoAlignment == ALIGN_BOTTOM) - yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f); - else - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2; - break; - case HORIZONTAL: - default: - logoSpacing[0] = ((mCarousel.size.x() - (mCarousel.logoSize.x() * mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.x(); - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f - (mCamOffset * logoSpacing[0]); - - if (mCarousel.logoAlignment == ALIGN_TOP) - yOff = mCarousel.logoSize.y() / 10.f; - else if (mCarousel.logoAlignment == ALIGN_BOTTOM) - yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f); - else - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f; - break; + case VERTICAL_WHEEL: + yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - (mCamOffset * logoSpacing[1]); + if (mCarousel.logoAlignment == ALIGN_LEFT) + xOff = mCarousel.logoSize.x() / 10.f; + else if (mCarousel.logoAlignment == ALIGN_RIGHT) + xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f); + else + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f; + break; + case VERTICAL: + logoSpacing[1] = ((mCarousel.size.y() - (mCarousel.logoSize.y() * mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.y(); + yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - (mCamOffset * logoSpacing[1]); + + if (mCarousel.logoAlignment == ALIGN_LEFT) + xOff = mCarousel.logoSize.x() / 10.f; + else if (mCarousel.logoAlignment == ALIGN_RIGHT) + xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f); + else + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2; + break; + case HORIZONTAL_WHEEL: + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2 - (mCamOffset * logoSpacing[1]); + if (mCarousel.logoAlignment == ALIGN_TOP) + yOff = mCarousel.logoSize.y() / 10; + else if (mCarousel.logoAlignment == ALIGN_BOTTOM) + yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f); + else + yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2; + break; + case HORIZONTAL: + default: + logoSpacing[0] = ((mCarousel.size.x() - (mCarousel.logoSize.x() * mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.x(); + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f - (mCamOffset * logoSpacing[0]); + + if (mCarousel.logoAlignment == ALIGN_TOP) + yOff = mCarousel.logoSize.y() / 10.f; + else if (mCarousel.logoAlignment == ALIGN_BOTTOM) + yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f); + else + yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f; + break; } int center = (int)(mCamOffset); @@ -501,6 +511,17 @@ void SystemView::renderCarousel(const Transform4x4f& trans) bufferRight = 0; } + // Recogemos primero la info de los logos a dibujar + struct LogoRenderData + { + int index; + float distance; + Transform4x4f trans; + }; + + std::vector logosToRender; + logosToRender.reserve(logoCount + bufferRight - bufferLeft + 2); + for (int i = center - logoCount / 2 + bufferLeft; i <= center + logoCount / 2 + bufferRight; i++) { int index = i; @@ -514,22 +535,42 @@ void SystemView::renderCarousel(const Transform4x4f& trans) float distance = i - mCamOffset; - float scale = 1.0f + ((mCarousel.logoScale - 1.0f) * (1.0f - fabs(distance))); - scale = Math::min(mCarousel.logoScale, Math::max(1.0f, scale)); + LogoRenderData data; + data.index = index; + data.distance = distance; + data.trans = logoTrans; + logosToRender.push_back(data); + } + + // Dibujar primero los más lejanos y al final el central (para que quede arriba) + std::sort(logosToRender.begin(), logosToRender.end(), + [](const LogoRenderData& a, const LogoRenderData& b) { + return std::fabs(a.distance) > std::fabs(b.distance); + }); + + for (auto& data : logosToRender) + { + float distance = data.distance; + + float scale = 1.0f + ((mCarousel.logoScale - 1.0f) * (1.0f - std::fabs(distance))); + // Los laterales pueden bajar hasta el 70% del tamaño del central + const float minScaleFactor = 0.7f; + scale = Math::min(mCarousel.logoScale, Math::max(minScaleFactor * mCarousel.logoScale, scale)); scale /= mCarousel.logoScale; - int opacity = (int)Math::round(0x80 + ((0xFF - 0x80) * (1.0f - fabs(distance)))); - opacity = Math::max((int) 0x80, opacity); + int opacity = (int)Math::round(0x80 + ((0xFF - 0x80) * (1.0f - std::fabs(distance)))); + opacity = Math::max((int)0x80, opacity); - const std::shared_ptr &comp = mEntries.at(index).data.logo; + const std::shared_ptr& comp = mEntries.at(data.index).data.logo; if (mCarousel.type == VERTICAL_WHEEL || mCarousel.type == HORIZONTAL_WHEEL) { comp->setRotationDegrees(mCarousel.logoRotation * distance); comp->setRotationOrigin(mCarousel.logoRotationOrigin); } comp->setScale(scale); comp->setOpacity((unsigned char)opacity); - comp->render(logoTrans); + comp->render(data.trans); } + Renderer::popClipRect(); } @@ -567,10 +608,10 @@ void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upp extrasTrans.translate(Vector3f(0, (i - mExtrasCamOffset) * mSize.y(), 0)); Renderer::pushClipRect(Vector2i((int)extrasTrans.translation()[0], (int)extrasTrans.translation()[1]), - Vector2i((int)mSize.x(), (int)mSize.y())); + Vector2i((int)mSize.x(), (int)mSize.y())); SystemViewData data = mEntries.at(index).data; for (unsigned int j = 0; j < data.backgroundExtras.size(); j++) { - GuiComponent *extra = data.backgroundExtras[j]; + GuiComponent* extra = data.backgroundExtras[j]; if (extra->getZIndex() >= lower && extra->getZIndex() < upper) { extra->render(extrasTrans); } @@ -617,7 +658,7 @@ void SystemView::getDefaultElements(void) mCarousel.zIndex = 40; // System Info Bar - mSystemInfo.setSize(mSize.x(), mSystemInfo.getFont()->getLetterHeight()*2.2f); + mSystemInfo.setSize(mSize.x(), mSystemInfo.getFont()->getLetterHeight() * 2.2f); mSystemInfo.setPosition(0, (mCarousel.pos.y() + mCarousel.size.y() - 0.2f)); mSystemInfo.setBackgroundColor(0xDDDDDDD8); mSystemInfo.setRenderBackground(true); From a2a7aac32e0ce894a0f47cb8d0fe86b4d99b96d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Ren=C3=A9=20Caballero=20Marquez?= Date: Mon, 17 Nov 2025 18:16:09 -0300 Subject: [PATCH 02/10] Refactor SystemView.cpp for readability and consistency Refactor SystemView.cpp for improved readability and consistency. Adjust formatting, spacing, and comments for clarity. --- es-app/src/views/SystemView.cpp | 435 ++++++++++++++++++++------------ 1 file changed, 268 insertions(+), 167 deletions(-) diff --git a/es-app/src/views/SystemView.cpp b/es-app/src/views/SystemView.cpp index 4171fbff1b..7ccfea19e5 100644 --- a/es-app/src/views/SystemView.cpp +++ b/es-app/src/views/SystemView.cpp @@ -14,16 +14,18 @@ #include // buffer values for scrolling velocity (left, stopped, right) -const int logoBuffersLeft[] = { -5, -2, -1 }; -const int logoBuffersRight[] = { 1, 2, 5 }; +const int logoBuffersLeft[] = { -5, -2, -1 }; +const int logoBuffersRight[] = { 1, 2, 5 }; -SystemView::SystemView(Window* window) : IList(window, LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP), - mViewNeedsReload(true), - mSystemInfo(window, "SYSTEM INFO", Font::get(FONT_SIZE_SMALL), 0x33333300, ALIGN_CENTER) +SystemView::SystemView(Window* window) + : IList(window, LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP) + , mViewNeedsReload(true) + , mSystemInfo(window, "SYSTEM INFO", Font::get(FONT_SIZE_SMALL), 0x33333300, ALIGN_CENTER) { - mCamOffset = 0; - mExtrasCamOffset = 0; + mCamOffset = 0; + mExtrasCamOffset = 0; mExtrasFadeOpacity = 0.0f; + mShowing = false; setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); populate(); @@ -43,17 +45,17 @@ void SystemView::populate() if ((*it)->isVisible()) { Entry e; - e.name = (*it)->getName(); + e.name = (*it)->getName(); e.object = *it; - // make logo + // ----- LOGO ----- const ThemeData::ThemeElement* logoElem = theme->getElement("system", "logo", "image"); if (logoElem) { - std::string path = logoElem->get("path"); + std::string path = logoElem->get("path"); std::string defaultPath = logoElem->has("default") ? logoElem->get("default") : ""; - if ((!path.empty() && ResourceManager::getInstance()->fileExists(path)) - || (!defaultPath.empty() && ResourceManager::getInstance()->fileExists(defaultPath))) + if ((!path.empty() && ResourceManager::getInstance()->fileExists(path)) || + (!defaultPath.empty() && ResourceManager::getInstance()->fileExists(defaultPath))) { ImageComponent* logo = new ImageComponent(mWindow, false, false); logo->setMaxSize(mCarousel.logoSize * mCarousel.logoScale); @@ -62,16 +64,29 @@ void SystemView::populate() e.data.logo = std::shared_ptr(logo); } } + + // Si no hay imagen, usar texto if (!e.data.logo) { - // no logo in theme; use text - TextComponent* text = new TextComponent(mWindow, + TextComponent* text = new TextComponent( + mWindow, (*it)->getName(), Font::get(FONT_SIZE_LARGE), 0x000000FF, ALIGN_CENTER); + text->setSize(mCarousel.logoSize * mCarousel.logoScale); - text->applyTheme((*it)->getTheme(), "system", "logoText", ThemeFlags::FONT_PATH | ThemeFlags::FONT_SIZE | ThemeFlags::COLOR | ThemeFlags::FORCE_UPPERCASE | ThemeFlags::LINE_SPACING | ThemeFlags::TEXT); + text->applyTheme( + (*it)->getTheme(), + "system", + "logoText", + ThemeFlags::FONT_PATH | + ThemeFlags::FONT_SIZE | + ThemeFlags::COLOR | + ThemeFlags::FORCE_UPPERCASE | + ThemeFlags::LINE_SPACING | + ThemeFlags::TEXT); + e.data.logo = std::shared_ptr(text); if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) @@ -79,55 +94,65 @@ void SystemView::populate() text->setHorizontalAlignment(mCarousel.logoAlignment); text->setVerticalAlignment(ALIGN_CENTER); } - else { + else + { text->setHorizontalAlignment(ALIGN_CENTER); text->setVerticalAlignment(mCarousel.logoAlignment); } } + // Origen del logo if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) { if (mCarousel.logoAlignment == ALIGN_LEFT) - e.data.logo->setOrigin(0, 0.5); + e.data.logo->setOrigin(0, 0.5f); else if (mCarousel.logoAlignment == ALIGN_RIGHT) - e.data.logo->setOrigin(1.0, 0.5); + e.data.logo->setOrigin(1.0f, 0.5f); else - e.data.logo->setOrigin(0.5, 0.5); + e.data.logo->setOrigin(0.5f, 0.5f); } - else { + else + { if (mCarousel.logoAlignment == ALIGN_TOP) - e.data.logo->setOrigin(0.5, 0); + e.data.logo->setOrigin(0.5f, 0.0f); else if (mCarousel.logoAlignment == ALIGN_BOTTOM) - e.data.logo->setOrigin(0.5, 1); + e.data.logo->setOrigin(0.5f, 1.0f); else - e.data.logo->setOrigin(0.5, 0.5); + e.data.logo->setOrigin(0.5f, 0.5f); } Vector2f denormalized = mCarousel.logoSize * e.data.logo->getOrigin(); - e.data.logo->setPosition(denormalized.x(), denormalized.y(), 0.0); - // delete any existing extras + e.data.logo->setPosition(denormalized.x(), denormalized.y(), 0.0f); + + // ----- EXTRAS ----- for (auto extra : e.data.backgroundExtras) delete extra; e.data.backgroundExtras.clear(); - // make background extras e.data.backgroundExtras = ThemeData::makeExtras((*it)->getTheme(), "system", mWindow); - // sort the extras by z-index - std::stable_sort(e.data.backgroundExtras.begin(), e.data.backgroundExtras.end(), [](GuiComponent* a, GuiComponent* b) { - return b->getZIndex() > a->getZIndex(); + std::stable_sort( + e.data.backgroundExtras.begin(), + e.data.backgroundExtras.end(), + [](GuiComponent* a, GuiComponent* b) + { + return b->getZIndex() > a->getZIndex(); }); this->add(e); } } + if (mEntries.size() == 0) { - // Something is wrong, there is not a single system to show, check if UI mode is not full if (!UIModeController::getInstance()->isUIModeFull()) { Settings::getInstance()->setString("UIMode", "Full"); - mWindow->pushGui(new GuiMsgBox(mWindow, "The selected UI mode has nothing to show,\n returning to UI mode: FULL", "OK", nullptr)); + mWindow->pushGui(new GuiMsgBox( + mWindow, + "The selected UI mode has nothing to show,\n returning to UI mode: FULL", + "OK", + nullptr)); } } } @@ -144,7 +169,8 @@ bool SystemView::input(InputConfig* config, Input input) { if (input.value != 0) { - if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_r && SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) + if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_r && + SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) { LOG(LogInfo) << " Reloading all"; ViewController::get()->reloadAll(); @@ -166,6 +192,7 @@ bool SystemView::input(InputConfig* config, Input input) return true; } break; + case HORIZONTAL: case HORIZONTAL_WHEEL: default: @@ -188,22 +215,26 @@ bool SystemView::input(InputConfig* config, Input input) ViewController::get()->goToGameList(getSelected()); return true; } + if (config->isMappedTo("x", input)) { - // get random system - // go to system setCursor(SystemData::getRandomSystem()); return true; } } - else { + else + { if (config->isMappedLike("left", input) || config->isMappedLike("right", input) || config->isMappedLike("up", input) || config->isMappedLike("down", input)) listInput(0); + Scripting::fireEvent("system-select", this->IList::getSelected()->getName(), "input"); - if (!UIModeController::getInstance()->isUIModeKid() && config->isMappedTo("select", input) && Settings::getInstance()->getBool("ScreenSaverControls")) + + if (!UIModeController::getInstance()->isUIModeKid() && + config->isMappedTo("select", input) && + Settings::getInstance()->getBool("ScreenSaverControls")) { mWindow->startScreenSaver(); mWindow->renderScreenSaver(); @@ -222,7 +253,6 @@ void SystemView::update(int deltaTime) void SystemView::onCursorChanged(const CursorState& /*state*/) { - // update help style updateHelpPrompts(); float startPos = mCamOffset; @@ -230,19 +260,13 @@ void SystemView::onCursorChanged(const CursorState& /*state*/) float posMax = (float)mEntries.size(); float target = (float)mCursor; - // what's the shortest way to get to our target? - // it's one of these... - - float endPos = target; // directly - float dist = abs(endPos - startPos); - - if (abs(target + posMax - startPos) < dist) - endPos = target + posMax; // loop around the end (0 -> max) - if (abs(target - posMax - startPos) < dist) - endPos = target - posMax; // loop around the start (max - 1 -> -1) + float endPos = target; + float dist = std::abs(endPos - startPos); - - // animate mSystemInfo's opacity (fade out, wait, fade back in) + if (std::abs(target + posMax - startPos) < dist) + endPos = target + posMax; + if (std::abs(target - posMax - startPos) < dist) + endPos = target - posMax; cancelAnimation(1); cancelAnimation(2); @@ -255,37 +279,41 @@ void SystemView::onCursorChanged(const CursorState& /*state*/) [infoStartOpacity, this](float t) { mSystemInfo.setOpacity((unsigned char)(Math::lerp(infoStartOpacity, 0.f, t) * 255)); - }, (int)(infoStartOpacity * (goFast ? 10 : 150))); + }, + (int)(infoStartOpacity * (goFast ? 10 : 150))); unsigned int gameCount = getSelected()->getDisplayedGameCount(); - // also change the text after we've fully faded out - setAnimation(infoFadeOut, 0, [this, gameCount] { - std::stringstream ss; + setAnimation(infoFadeOut, 0, + [this, gameCount] + { + std::stringstream ss; - if (!getSelected()->isGameSystem()) - ss << "CONFIGURATION"; - else - ss << gameCount << " GAME" << (gameCount == 1 ? "" : "S") << " AVAILABLE"; + if (!getSelected()->isGameSystem()) + ss << "CONFIGURATION"; + else + ss << gameCount << " GAME" << (gameCount == 1 ? "" : "S") << " AVAILABLE"; - mSystemInfo.setText(ss.str()); - }, false, 1); + mSystemInfo.setText(ss.str()); + }, + false, + 1); Animation* infoFadeIn = new LambdaAnimation( [this](float t) { mSystemInfo.setOpacity((unsigned char)(Math::lerp(0.f, 1.f, t) * 255)); - }, goFast ? 10 : 300); + }, + goFast ? 10 : 300); - // wait 600ms to fade in setAnimation(infoFadeIn, goFast ? 0 : 2000, nullptr, false, 2); - // no need to animate transition, we're not going anywhere (probably mEntries.size() == 1) if (endPos == mCamOffset && endPos == mExtrasCamOffset) return; Animation* anim; bool move_carousel = Settings::getInstance()->getBool("MoveCarousel"); + if (transition_style == "fade") { float startExtrasFade = mExtrasFadeOpacity; @@ -311,11 +339,11 @@ void SystemView::onCursorChanged(const CursorState& /*state*/) if (t > 0.5f) this->mExtrasCamOffset = endPos; - - }, 500); + }, + 500); } - else if (transition_style == "slide") { - // slide + else if (transition_style == "slide") + { anim = new LambdaAnimation( [this, startPos, endPos, posMax, move_carousel](float t) { @@ -326,12 +354,13 @@ void SystemView::onCursorChanged(const CursorState& /*state*/) if (f >= posMax) f -= posMax; - this->mCamOffset = move_carousel ? f : endPos; + this->mCamOffset = move_carousel ? f : endPos; this->mExtrasCamOffset = f; - }, 500); + }, + 500); } - else { - // instant + else + { anim = new LambdaAnimation( [this, startPos, endPos, posMax, move_carousel](float t) { @@ -342,43 +371,39 @@ void SystemView::onCursorChanged(const CursorState& /*state*/) if (f >= posMax) f -= posMax; - this->mCamOffset = move_carousel ? f : endPos; + this->mCamOffset = move_carousel ? f : endPos; this->mExtrasCamOffset = endPos; - }, move_carousel ? 500 : 1); + }, + move_carousel ? 500 : 1); } - setAnimation(anim, 0, nullptr, false, 0); } void SystemView::render(const Transform4x4f& parentTrans) { if (size() == 0) - return; // nothing to render + return; Transform4x4f trans = getTransform() * parentTrans; auto systemInfoZIndex = mSystemInfo.getZIndex(); - auto minMax = std::minmax(mCarousel.zIndex, systemInfoZIndex); + auto minMax = std::minmax(mCarousel.zIndex, systemInfoZIndex); renderExtras(trans, INT16_MIN, minMax.first); renderFade(trans); - if (mCarousel.zIndex > mSystemInfo.getZIndex()) { + if (mCarousel.zIndex > mSystemInfo.getZIndex()) renderInfoBar(trans); - } - else { + else renderCarousel(trans); - } renderExtras(trans, minMax.first, minMax.second); - if (mCarousel.zIndex > mSystemInfo.getZIndex()) { + if (mCarousel.zIndex > mSystemInfo.getZIndex()) renderCarousel(trans); - } - else { + else renderInfoBar(trans); - } renderExtras(trans, minMax.second, INT16_MAX); } @@ -390,6 +415,7 @@ std::vector SystemView::getHelpPrompts() prompts.push_back(HelpPrompt("up/down", "choose")); else prompts.push_back(HelpPrompt("left/right", "choose")); + prompts.push_back(HelpPrompt("a", "select")); prompts.push_back(HelpPrompt("x", "random")); @@ -406,15 +432,14 @@ HelpStyle SystemView::getHelpStyle() return style; } -void SystemView::onThemeChanged(const std::shared_ptr& /*theme*/) +void SystemView::onThemeChanged(const std::shared_ptr& /*theme*/) { LOG(LogDebug) << "SystemView::onThemeChanged()"; mViewNeedsReload = true; populate(); } -// Get the ThemeElements that make up the SystemView. -void SystemView::getViewElements(const std::shared_ptr& theme) +void SystemView::getViewElements(const std::shared_ptr& theme) { LOG(LogDebug) << "SystemView::getViewElements()"; @@ -434,24 +459,41 @@ void SystemView::getViewElements(const std::shared_ptr& theme) mViewNeedsReload = false; } -// Render system carousel +// --------------------------- +// NUEVO renderCarousel +// --------------------------- void SystemView::renderCarousel(const Transform4x4f& trans) { - // background box behind logos + if (mEntries.empty()) + return; + + // ----- Transform del carrusel ----- Transform4x4f carouselTrans = trans; - carouselTrans.translate(Vector3f(mCarousel.pos.x(), mCarousel.pos.y(), 0.0)); - carouselTrans.translate(Vector3f(mCarousel.origin.x() * mCarousel.size.x() * -1, mCarousel.origin.y() * mCarousel.size.y() * -1, 0.0f)); + carouselTrans.translate(Vector3f(mCarousel.pos.x(), mCarousel.pos.y(), 0.0f)); + carouselTrans.translate(Vector3f( + mCarousel.origin.x() * mCarousel.size.x() * -1, + mCarousel.origin.y() * mCarousel.size.y() * -1, + 0.0f)); Vector2f clipPos(carouselTrans.translation().x(), carouselTrans.translation().y()); - Renderer::pushClipRect(Vector2i((int)clipPos.x(), (int)clipPos.y()), Vector2i((int)mCarousel.size.x(), (int)mCarousel.size.y())); + Renderer::pushClipRect( + Vector2i((int)clipPos.x(), (int)clipPos.y()), + Vector2i((int)mCarousel.size.x(), (int)mCarousel.size.y())); Renderer::setMatrix(carouselTrans); - Renderer::drawRect(0.0f, 0.0f, mCarousel.size.x(), mCarousel.size.y(), mCarousel.color, mCarousel.colorEnd, mCarousel.colorGradientHorizontal); - - // draw logos - Vector2f logoSpacing(0.0, 0.0); // NB: logoSpacing will include the size of the logo itself as well! - float xOff = 0.0; - float yOff = 0.0; + Renderer::drawRect( + 0.0f, + 0.0f, + mCarousel.size.x(), + mCarousel.size.y(), + mCarousel.color, + mCarousel.colorEnd, + mCarousel.colorGradientHorizontal); + + // ----- Configuración de logos ----- + Vector2f logoSpacing(0.0f, 0.0f); + float xOff = 0.0f; + float yOff = 0.0f; switch (mCarousel.type) { @@ -464,8 +506,10 @@ void SystemView::renderCarousel(const Transform4x4f& trans) else xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f; break; + case VERTICAL: - logoSpacing[1] = ((mCarousel.size.y() - (mCarousel.logoSize.y() * mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.y(); + logoSpacing[1] = ((mCarousel.size.y() - (mCarousel.logoSize.y() * mCarousel.maxLogoCount)) + / mCarousel.maxLogoCount) + mCarousel.logoSize.y(); yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - (mCamOffset * logoSpacing[1]); if (mCarousel.logoAlignment == ALIGN_LEFT) @@ -473,20 +517,23 @@ void SystemView::renderCarousel(const Transform4x4f& trans) else if (mCarousel.logoAlignment == ALIGN_RIGHT) xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f); else - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2; + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f; break; + case HORIZONTAL_WHEEL: - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2 - (mCamOffset * logoSpacing[1]); + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f - (mCamOffset * logoSpacing[0]); if (mCarousel.logoAlignment == ALIGN_TOP) - yOff = mCarousel.logoSize.y() / 10; + yOff = mCarousel.logoSize.y() / 10.f; else if (mCarousel.logoAlignment == ALIGN_BOTTOM) yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f); else - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2; + yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f; break; + case HORIZONTAL: default: - logoSpacing[0] = ((mCarousel.size.x() - (mCarousel.logoSize.x() * mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.x(); + logoSpacing[0] = ((mCarousel.size.x() - (mCarousel.logoSize.x() * mCarousel.maxLogoCount)) + / mCarousel.maxLogoCount) + mCarousel.logoSize.x(); xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f - (mCamOffset * logoSpacing[0]); if (mCarousel.logoAlignment == ALIGN_TOP) @@ -498,99 +545,154 @@ void SystemView::renderCarousel(const Transform4x4f& trans) break; } - int center = (int)(mCamOffset); - int logoCount = Math::min(mCarousel.maxLogoCount, (int)mEntries.size()); + const int total = (int)mEntries.size(); + if (total <= 0) + { + Renderer::popClipRect(); + return; + } + + // Siempre queremos "hasta" mCarousel.maxLogoCount logos visibles (PS4 style) + int logoCount = Math::min(mCarousel.maxLogoCount, total); + + // Centro lógico (redondeado) para evitar "respirar" durante la animación + int centerIndex = (int)std::round(mCamOffset); + while (centerIndex < 0) + centerIndex += total; + while (centerIndex >= total) + centerIndex -= total; + + // Índice base para slots a la izquierda/derecha + int centerSlot = (int)mCamOffset; - // Adding texture loading buffers depending on scrolling speed and status + // Buffers según velocidad int bufferIndex = getScrollingVelocity() + 1; - int bufferLeft = logoBuffersLeft[bufferIndex]; + if (bufferIndex < 0) bufferIndex = 0; + if (bufferIndex > 2) bufferIndex = 2; + + int bufferLeft = logoBuffersLeft[bufferIndex]; int bufferRight = logoBuffersRight[bufferIndex]; + if (logoCount == 1) { - bufferLeft = 0; + bufferLeft = 0; bufferRight = 0; } - // Recogemos primero la info de los logos a dibujar struct LogoRenderData { - int index; - float distance; + int index; // índice real del sistema + float slotDistance; // distancia en "slots" (para rotación wheel) + int ringDistance; // distancia por anillos (0 = centro, 1 = vecino, >=2 = lejano) Transform4x4f trans; }; std::vector logosToRender; logosToRender.reserve(logoCount + bufferRight - bufferLeft + 2); - for (int i = center - logoCount / 2 + bufferLeft; i <= center + logoCount / 2 + bufferRight; i++) + // Función para distancia mínima en carrusel circular + auto wrappedRingDistance = [total](int from, int to) + { + int d = from - to; + int half = total / 2; + + if (d > half) + d -= total; + else if (d < -half) + d += total; + + return std::abs(d); + }; + + for (int i = centerSlot - logoCount / 2 + bufferLeft; + i <= centerSlot + logoCount / 2 + bufferRight; + i++) { int index = i; while (index < 0) - index += (int)mEntries.size(); - while (index >= (int)mEntries.size()) - index -= (int)mEntries.size(); + index += total; + while (index >= total) + index -= total; Transform4x4f logoTrans = carouselTrans; - logoTrans.translate(Vector3f(i * logoSpacing[0] + xOff, i * logoSpacing[1] + yOff, 0)); + logoTrans.translate(Vector3f( + i * logoSpacing[0] + xOff, + i * logoSpacing[1] + yOff, + 0.0f)); - float distance = i - mCamOffset; + float slotDist = (float)i - mCamOffset; + int ringDist = wrappedRingDistance(index, centerIndex); - LogoRenderData data; - data.index = index; - data.distance = distance; - data.trans = logoTrans; - logosToRender.push_back(data); + logosToRender.push_back({ index, slotDist, ringDist, logoTrans }); } - // Dibujar primero los más lejanos y al final el central (para que quede arriba) - std::sort(logosToRender.begin(), logosToRender.end(), - [](const LogoRenderData& a, const LogoRenderData& b) { - return std::fabs(a.distance) > std::fabs(b.distance); + // Dibujamos primero los lejanos, luego vecinos, y al final el central (para que quede por encima) + std::sort( + logosToRender.begin(), + logosToRender.end(), + [](const LogoRenderData& a, const LogoRenderData& b) + { + if (a.ringDistance != b.ringDistance) + return a.ringDistance > b.ringDistance; // mayor ringDistance primero + return std::fabs(a.slotDistance) > std::fabs(b.slotDistance); }); for (auto& data : logosToRender) { - float distance = data.distance; + const int ring = data.ringDistance; - float scale = 1.0f + ((mCarousel.logoScale - 1.0f) * (1.0f - std::fabs(distance))); - // Los laterales pueden bajar hasta el 70% del tamaño del central - const float minScaleFactor = 0.7f; - scale = Math::min(mCarousel.logoScale, Math::max(minScaleFactor * mCarousel.logoScale, scale)); - scale /= mCarousel.logoScale; + // Escala fija por "anillos" (sin animación continua según mCamOffset) + float scale; + unsigned char opacity; - int opacity = (int)Math::round(0x80 + ((0xFF - 0x80) * (1.0f - std::fabs(distance)))); - opacity = Math::max((int)0x80, opacity); + if (ring == 0) +{ + scale = 1.0f; // central + opacity = 255; +} +else +{ + scale = 0.75f; // todos los laterales + opacity = 210; +} const std::shared_ptr& comp = mEntries.at(data.index).data.logo; - if (mCarousel.type == VERTICAL_WHEEL || mCarousel.type == HORIZONTAL_WHEEL) { - comp->setRotationDegrees(mCarousel.logoRotation * distance); + + // Rueda vertical/horizontal sigue usando la distancia en slots para la rotación + if (mCarousel.type == VERTICAL_WHEEL || mCarousel.type == HORIZONTAL_WHEEL) + { + comp->setRotationDegrees(mCarousel.logoRotation * data.slotDistance); comp->setRotationOrigin(mCarousel.logoRotationOrigin); } + comp->setScale(scale); - comp->setOpacity((unsigned char)opacity); + comp->setOpacity(opacity); comp->render(data.trans); } Renderer::popClipRect(); } + void SystemView::renderInfoBar(const Transform4x4f& trans) { Renderer::setMatrix(trans); mSystemInfo.render(trans); } -// Draw background extras void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upper) { int extrasCenter = (int)mExtrasCamOffset; - // Adding texture loading buffers depending on scrolling speed and status int bufferIndex = getScrollingVelocity() + 1; + if (bufferIndex < 0) bufferIndex = 0; + if (bufferIndex > 2) bufferIndex = 2; Renderer::pushClipRect(Vector2i::Zero(), Vector2i((int)mSize.x(), (int)mSize.y())); - for (int i = extrasCenter + logoBuffersLeft[bufferIndex]; i <= extrasCenter + logoBuffersRight[bufferIndex]; i++) + for (int i = extrasCenter + logoBuffersLeft[bufferIndex]; + i <= extrasCenter + logoBuffersRight[bufferIndex]; + i++) { int index = i; while (index < 0) @@ -598,7 +700,6 @@ void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upp while (index >= (int)mEntries.size()) index -= (int)mEntries.size(); - //Only render selected system when not showing if (mShowing || index == mCursor) { Transform4x4f extrasTrans = trans; @@ -607,24 +708,27 @@ void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upp else extrasTrans.translate(Vector3f(0, (i - mExtrasCamOffset) * mSize.y(), 0)); - Renderer::pushClipRect(Vector2i((int)extrasTrans.translation()[0], (int)extrasTrans.translation()[1]), + Renderer::pushClipRect( + Vector2i((int)extrasTrans.translation()[0], (int)extrasTrans.translation()[1]), Vector2i((int)mSize.x(), (int)mSize.y())); + SystemViewData data = mEntries.at(index).data; - for (unsigned int j = 0; j < data.backgroundExtras.size(); j++) { + for (unsigned int j = 0; j < data.backgroundExtras.size(); j++) + { GuiComponent* extra = data.backgroundExtras[j]; - if (extra->getZIndex() >= lower && extra->getZIndex() < upper) { + if (extra->getZIndex() >= lower && extra->getZIndex() < upper) extra->render(extrasTrans); - } } + Renderer::popClipRect(); } } + Renderer::popClipRect(); } void SystemView::renderFade(const Transform4x4f& trans) { - // fade extras if necessary if (mExtrasFadeOpacity) { unsigned int fadeColor = 0x00000000 | (unsigned char)(mExtrasFadeOpacity * 255); @@ -633,31 +737,28 @@ void SystemView::renderFade(const Transform4x4f& trans) } } -// Populate the system carousel with the legacy values -void SystemView::getDefaultElements(void) +void SystemView::getDefaultElements(void) { - // Carousel - mCarousel.type = HORIZONTAL; + mCarousel.type = HORIZONTAL; mCarousel.logoAlignment = ALIGN_CENTER; - mCarousel.size.x() = mSize.x(); - mCarousel.size.y() = 0.2325f * mSize.y(); - mCarousel.pos.x() = 0.0f; - mCarousel.pos.y() = 0.5f * (mSize.y() - mCarousel.size.y()); - mCarousel.origin.x() = 0.0f; - mCarousel.origin.y() = 0.0f; - mCarousel.color = 0xFFFFFFD8; - mCarousel.colorEnd = 0xFFFFFFD8; + mCarousel.size.x() = mSize.x(); + mCarousel.size.y() = 0.2325f * mSize.y(); + mCarousel.pos.x() = 0.0f; + mCarousel.pos.y() = 0.5f * (mSize.y() - mCarousel.size.y()); + mCarousel.origin.x() = 0.0f; + mCarousel.origin.y() = 0.0f; + mCarousel.color = 0xFFFFFFD8; + mCarousel.colorEnd = 0xFFFFFFD8; mCarousel.colorGradientHorizontal = true; - mCarousel.logoScale = 1.2f; - mCarousel.logoRotation = 7.5; + mCarousel.logoScale = 1.2f; + mCarousel.logoRotation = 7.5f; mCarousel.logoRotationOrigin.x() = -5; - mCarousel.logoRotationOrigin.y() = 0.5; - mCarousel.logoSize.x() = 0.25f * mSize.x(); - mCarousel.logoSize.y() = 0.155f * mSize.y(); - mCarousel.maxLogoCount = 3; - mCarousel.zIndex = 40; + mCarousel.logoRotationOrigin.y() = 0.5f; + mCarousel.logoSize.x() = 0.25f * mSize.x(); + mCarousel.logoSize.y() = 0.155f * mSize.y(); + mCarousel.maxLogoCount = 3; + mCarousel.zIndex = 40; - // System Info Bar mSystemInfo.setSize(mSize.x(), mSystemInfo.getFont()->getLetterHeight() * 2.2f); mSystemInfo.setPosition(0, (mCarousel.pos.y() + mCarousel.size.y() - 0.2f)); mSystemInfo.setBackgroundColor(0xDDDDDDD8); @@ -689,7 +790,7 @@ void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem) mCarousel.origin = elem->get("origin"); if (elem->has("color")) { - mCarousel.color = elem->get("color"); + mCarousel.color = elem->get("color"); mCarousel.colorEnd = mCarousel.color; } if (elem->has("colorEnd")) From afe727dd4b1e01f7eb69019ba306495228235c4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Ren=C3=A9=20Caballero=20Marquez?= Date: Tue, 18 Nov 2025 19:14:13 -0300 Subject: [PATCH 03/10] Add carousel properties to ThemeData Added new properties for carousel customization. --- es-core/src/ThemeData.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index c223978352..2fbd0f9266 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -169,7 +169,12 @@ std::map> The { "logoSize", NORMALIZED_PAIR }, { "logoAlignment", STRING }, { "maxLogoCount", FLOAT }, - { "zIndex", FLOAT } } } + { "zIndex", FLOAT }, + // NUEVO: propiedades extra del carrusel + { "minLogoOpacity", FLOAT }, // 0.0 – 1.0 + { "scaledLogoSpacing", FLOAT }, // factor de separación + { "scrollSound", PATH } // sonido opcional (por ahora solo parseado) + } } }; #define MINIMUM_THEME_FORMAT_VERSION 3 From 71007a1f2ea6e320f99353198d7d301281f90c45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Ren=C3=A9=20Caballero=20Marquez?= Date: Tue, 18 Nov 2025 19:15:28 -0300 Subject: [PATCH 04/10] Refactor SystemView for clarity and performance Refactor SystemView for improved readability and performance. Adjusted spacing, alignment, and opacity handling for logos. Enhanced carousel rendering logic and added new properties for logo opacity and spacing. --- es-app/src/views/SystemView.cpp | 683 +++++++++++++++----------------- 1 file changed, 312 insertions(+), 371 deletions(-) diff --git a/es-app/src/views/SystemView.cpp b/es-app/src/views/SystemView.cpp index 7ccfea19e5..dc367f4f8d 100644 --- a/es-app/src/views/SystemView.cpp +++ b/es-app/src/views/SystemView.cpp @@ -10,22 +10,17 @@ #include "SystemData.h" #include "Window.h" -#include -#include - // buffer values for scrolling velocity (left, stopped, right) -const int logoBuffersLeft[] = { -5, -2, -1 }; -const int logoBuffersRight[] = { 1, 2, 5 }; +const int logoBuffersLeft[] = { -5, -2, -1 }; +const int logoBuffersRight[] = { 1, 2, 5 }; -SystemView::SystemView(Window* window) - : IList(window, LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP) - , mViewNeedsReload(true) - , mSystemInfo(window, "SYSTEM INFO", Font::get(FONT_SIZE_SMALL), 0x33333300, ALIGN_CENTER) +SystemView::SystemView(Window* window) : IList(window, LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP), + mViewNeedsReload(true), + mSystemInfo(window, "SYSTEM INFO", Font::get(FONT_SIZE_SMALL), 0x33333300, ALIGN_CENTER) { - mCamOffset = 0; - mExtrasCamOffset = 0; + mCamOffset = 0; + mExtrasCamOffset = 0; mExtrasFadeOpacity = 0.0f; - mShowing = false; setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); populate(); @@ -35,27 +30,27 @@ void SystemView::populate() { mEntries.clear(); - for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++) + for(auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++) { const std::shared_ptr& theme = (*it)->getTheme(); - if (mViewNeedsReload) + if(mViewNeedsReload) getViewElements(theme); - if ((*it)->isVisible()) + if((*it)->isVisible()) { Entry e; - e.name = (*it)->getName(); + e.name = (*it)->getName(); e.object = *it; - // ----- LOGO ----- + // make logo const ThemeData::ThemeElement* logoElem = theme->getElement("system", "logo", "image"); - if (logoElem) + if(logoElem) { - std::string path = logoElem->get("path"); + std::string path = logoElem->get("path"); std::string defaultPath = logoElem->has("default") ? logoElem->get("default") : ""; - if ((!path.empty() && ResourceManager::getInstance()->fileExists(path)) || - (!defaultPath.empty() && ResourceManager::getInstance()->fileExists(defaultPath))) + if((!path.empty() && ResourceManager::getInstance()->fileExists(path)) + || (!defaultPath.empty() && ResourceManager::getInstance()->fileExists(defaultPath))) { ImageComponent* logo = new ImageComponent(mWindow, false, false); logo->setMaxSize(mCarousel.logoSize * mCarousel.logoScale); @@ -64,95 +59,70 @@ void SystemView::populate() e.data.logo = std::shared_ptr(logo); } } - - // Si no hay imagen, usar texto if (!e.data.logo) { - TextComponent* text = new TextComponent( - mWindow, + // no logo in theme; use text + TextComponent* text = new TextComponent(mWindow, (*it)->getName(), Font::get(FONT_SIZE_LARGE), 0x000000FF, ALIGN_CENTER); - text->setSize(mCarousel.logoSize * mCarousel.logoScale); - text->applyTheme( - (*it)->getTheme(), - "system", - "logoText", - ThemeFlags::FONT_PATH | - ThemeFlags::FONT_SIZE | - ThemeFlags::COLOR | - ThemeFlags::FORCE_UPPERCASE | - ThemeFlags::LINE_SPACING | - ThemeFlags::TEXT); - + text->applyTheme((*it)->getTheme(), "system", "logoText", ThemeFlags::FONT_PATH | ThemeFlags::FONT_SIZE | ThemeFlags::COLOR | ThemeFlags::FORCE_UPPERCASE | ThemeFlags::LINE_SPACING | ThemeFlags::TEXT); e.data.logo = std::shared_ptr(text); if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) { text->setHorizontalAlignment(mCarousel.logoAlignment); text->setVerticalAlignment(ALIGN_CENTER); - } - else - { + } else { text->setHorizontalAlignment(ALIGN_CENTER); text->setVerticalAlignment(mCarousel.logoAlignment); } } - // Origen del logo if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) { if (mCarousel.logoAlignment == ALIGN_LEFT) - e.data.logo->setOrigin(0, 0.5f); + e.data.logo->setOrigin(0, 0.5); else if (mCarousel.logoAlignment == ALIGN_RIGHT) - e.data.logo->setOrigin(1.0f, 0.5f); + e.data.logo->setOrigin(1.0, 0.5); else - e.data.logo->setOrigin(0.5f, 0.5f); - } - else - { + e.data.logo->setOrigin(0.5, 0.5); + } else { if (mCarousel.logoAlignment == ALIGN_TOP) - e.data.logo->setOrigin(0.5f, 0.0f); + e.data.logo->setOrigin(0.5, 0); else if (mCarousel.logoAlignment == ALIGN_BOTTOM) - e.data.logo->setOrigin(0.5f, 1.0f); + e.data.logo->setOrigin(0.5, 1); else - e.data.logo->setOrigin(0.5f, 0.5f); + e.data.logo->setOrigin(0.5, 0.5); } Vector2f denormalized = mCarousel.logoSize * e.data.logo->getOrigin(); - e.data.logo->setPosition(denormalized.x(), denormalized.y(), 0.0f); - - // ----- EXTRAS ----- + e.data.logo->setPosition(denormalized.x(), denormalized.y(), 0.0); + // delete any existing extras for (auto extra : e.data.backgroundExtras) delete extra; e.data.backgroundExtras.clear(); + // make background extras e.data.backgroundExtras = ThemeData::makeExtras((*it)->getTheme(), "system", mWindow); - std::stable_sort( - e.data.backgroundExtras.begin(), - e.data.backgroundExtras.end(), - [](GuiComponent* a, GuiComponent* b) - { - return b->getZIndex() > a->getZIndex(); - }); + // sort the extras by z-index + std::stable_sort(e.data.backgroundExtras.begin(), e.data.backgroundExtras.end(), [](GuiComponent* a, GuiComponent* b) { + return b->getZIndex() > a->getZIndex(); + }); this->add(e); } } - if (mEntries.size() == 0) { + // Something is wrong, there is not a single system to show, check if UI mode is not full if (!UIModeController::getInstance()->isUIModeFull()) { Settings::getInstance()->setString("UIMode", "Full"); - mWindow->pushGui(new GuiMsgBox( - mWindow, - "The selected UI mode has nothing to show,\n returning to UI mode: FULL", - "OK", - nullptr)); + mWindow->pushGui(new GuiMsgBox(mWindow, "The selected UI mode has nothing to show,\n returning to UI mode: FULL", "OK", nullptr)); } } } @@ -161,16 +131,15 @@ void SystemView::goToSystem(SystemData* system, bool animate) { setCursor(system); - if (!animate) + if(!animate) finishAnimation(0); } bool SystemView::input(InputConfig* config, Input input) { - if (input.value != 0) + if(input.value != 0) { - if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_r && - SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) + if(config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_r && SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) { LOG(LogInfo) << " Reloading all"; ViewController::get()->reloadAll(); @@ -192,7 +161,6 @@ bool SystemView::input(InputConfig* config, Input input) return true; } break; - case HORIZONTAL: case HORIZONTAL_WHEEL: default: @@ -209,32 +177,27 @@ bool SystemView::input(InputConfig* config, Input input) break; } - if (config->isMappedTo("a", input)) + if(config->isMappedTo("a", input)) { stopScrolling(); ViewController::get()->goToGameList(getSelected()); return true; } - if (config->isMappedTo("x", input)) { + // get random system + // go to system setCursor(SystemData::getRandomSystem()); return true; } - } - else - { - if (config->isMappedLike("left", input) || + }else{ + if(config->isMappedLike("left", input) || config->isMappedLike("right", input) || config->isMappedLike("up", input) || config->isMappedLike("down", input)) listInput(0); - Scripting::fireEvent("system-select", this->IList::getSelected()->getName(), "input"); - - if (!UIModeController::getInstance()->isUIModeKid() && - config->isMappedTo("select", input) && - Settings::getInstance()->getBool("ScreenSaverControls")) + if(!UIModeController::getInstance()->isUIModeKid() && config->isMappedTo("select", input) && Settings::getInstance()->getBool("ScreenSaverControls")) { mWindow->startScreenSaver(); mWindow->renderScreenSaver(); @@ -253,6 +216,7 @@ void SystemView::update(int deltaTime) void SystemView::onCursorChanged(const CursorState& /*state*/) { + // update help style updateHelpPrompts(); float startPos = mCamOffset; @@ -260,13 +224,19 @@ void SystemView::onCursorChanged(const CursorState& /*state*/) float posMax = (float)mEntries.size(); float target = (float)mCursor; - float endPos = target; - float dist = std::abs(endPos - startPos); + // what's the shortest way to get to our target? + // it's one of these... + + float endPos = target; // directly + float dist = abs(endPos - startPos); + + if(abs(target + posMax - startPos) < dist) + endPos = target + posMax; // loop around the end (0 -> max) + if(abs(target - posMax - startPos) < dist) + endPos = target - posMax; // loop around the start (max - 1 -> -1) - if (std::abs(target + posMax - startPos) < dist) - endPos = target + posMax; - if (std::abs(target - posMax - startPos) < dist) - endPos = target - posMax; + + // animate mSystemInfo's opacity (fade out, wait, fade back in) cancelAnimation(1); cancelAnimation(2); @@ -276,134 +246,129 @@ void SystemView::onCursorChanged(const CursorState& /*state*/) const float infoStartOpacity = mSystemInfo.getOpacity() / 255.f; Animation* infoFadeOut = new LambdaAnimation( - [infoStartOpacity, this](float t) - { - mSystemInfo.setOpacity((unsigned char)(Math::lerp(infoStartOpacity, 0.f, t) * 255)); - }, - (int)(infoStartOpacity * (goFast ? 10 : 150))); + [infoStartOpacity, this] (float t) + { + mSystemInfo.setOpacity((unsigned char)(Math::lerp(infoStartOpacity, 0.f, t) * 255)); + }, (int)(infoStartOpacity * (goFast ? 10 : 150))); unsigned int gameCount = getSelected()->getDisplayedGameCount(); - setAnimation(infoFadeOut, 0, - [this, gameCount] - { - std::stringstream ss; + // also change the text after we've fully faded out + setAnimation(infoFadeOut, 0, [this, gameCount] { + std::stringstream ss; - if (!getSelected()->isGameSystem()) - ss << "CONFIGURATION"; - else - ss << gameCount << " GAME" << (gameCount == 1 ? "" : "S") << " AVAILABLE"; + if (!getSelected()->isGameSystem()) + ss << "CONFIGURATION"; + else + ss << gameCount << " GAME" << (gameCount == 1 ? "" : "S") << " AVAILABLE"; - mSystemInfo.setText(ss.str()); - }, - false, - 1); + mSystemInfo.setText(ss.str()); + }, false, 1); Animation* infoFadeIn = new LambdaAnimation( [this](float t) - { - mSystemInfo.setOpacity((unsigned char)(Math::lerp(0.f, 1.f, t) * 255)); - }, - goFast ? 10 : 300); + { + mSystemInfo.setOpacity((unsigned char)(Math::lerp(0.f, 1.f, t) * 255)); + }, goFast ? 10 : 300); + // wait 600ms to fade in setAnimation(infoFadeIn, goFast ? 0 : 2000, nullptr, false, 2); - if (endPos == mCamOffset && endPos == mExtrasCamOffset) + // no need to animate transition, we're not going anywhere (probably mEntries.size() == 1) + if(endPos == mCamOffset && endPos == mExtrasCamOffset) return; Animation* anim; bool move_carousel = Settings::getInstance()->getBool("MoveCarousel"); - - if (transition_style == "fade") + if(transition_style == "fade") { float startExtrasFade = mExtrasFadeOpacity; anim = new LambdaAnimation( [this, startExtrasFade, startPos, endPos, posMax, move_carousel](float t) - { - t -= 1; - float f = Math::lerp(startPos, endPos, t * t * t + 1); - if (f < 0) - f += posMax; - if (f >= posMax) - f -= posMax; - - this->mCamOffset = move_carousel ? f : endPos; - - t += 1; - if (t < 0.3f) - this->mExtrasFadeOpacity = Math::lerp(0.0f, 1.0f, t / 0.3f + startExtrasFade); - else if (t < 0.7f) - this->mExtrasFadeOpacity = 1.0f; - else - this->mExtrasFadeOpacity = Math::lerp(1.0f, 0.0f, (t - 0.7f) / 0.3f); + { + t -= 1; + float f = Math::lerp(startPos, endPos, t*t*t + 1); + if(f < 0) + f += posMax; + if(f >= posMax) + f -= posMax; + + this->mCamOffset = move_carousel ? f : endPos; + + t += 1; + if(t < 0.3f) + this->mExtrasFadeOpacity = Math::lerp(0.0f, 1.0f, t / 0.3f + startExtrasFade); + else if(t < 0.7f) + this->mExtrasFadeOpacity = 1.0f; + else + this->mExtrasFadeOpacity = Math::lerp(1.0f, 0.0f, (t - 0.7f) / 0.3f); - if (t > 0.5f) - this->mExtrasCamOffset = endPos; - }, - 500); - } - else if (transition_style == "slide") - { + if(t > 0.5f) + this->mExtrasCamOffset = endPos; + + }, 500); + } else if (transition_style == "slide") { + // slide anim = new LambdaAnimation( [this, startPos, endPos, posMax, move_carousel](float t) - { - t -= 1; - float f = Math::lerp(startPos, endPos, t * t * t + 1); - if (f < 0) - f += posMax; - if (f >= posMax) - f -= posMax; - - this->mCamOffset = move_carousel ? f : endPos; - this->mExtrasCamOffset = f; - }, - 500); - } - else - { + { + t -= 1; + float f = Math::lerp(startPos, endPos, t*t*t + 1); + if(f < 0) + f += posMax; + if(f >= posMax) + f -= posMax; + + this->mCamOffset = move_carousel ? f : endPos; + this->mExtrasCamOffset = f; + }, 500); + } else { + // instant anim = new LambdaAnimation( - [this, startPos, endPos, posMax, move_carousel](float t) - { - t -= 1; - float f = Math::lerp(startPos, endPos, t * t * t + 1); - if (f < 0) - f += posMax; - if (f >= posMax) - f -= posMax; - - this->mCamOffset = move_carousel ? f : endPos; - this->mExtrasCamOffset = endPos; - }, - move_carousel ? 500 : 1); + [this, startPos, endPos, posMax, move_carousel ](float t) + { + t -= 1; + float f = Math::lerp(startPos, endPos, t*t*t + 1); + if(f < 0) + f += posMax; + if(f >= posMax) + f -= posMax; + + this->mCamOffset = move_carousel ? f : endPos; + this->mExtrasCamOffset = endPos; + }, move_carousel ? 500 : 1); } + setAnimation(anim, 0, nullptr, false, 0); } void SystemView::render(const Transform4x4f& parentTrans) { - if (size() == 0) - return; + if(size() == 0) + return; // nothing to render Transform4x4f trans = getTransform() * parentTrans; auto systemInfoZIndex = mSystemInfo.getZIndex(); - auto minMax = std::minmax(mCarousel.zIndex, systemInfoZIndex); + auto minMax = std::minmax(mCarousel.zIndex, systemInfoZIndex); renderExtras(trans, INT16_MIN, minMax.first); renderFade(trans); - if (mCarousel.zIndex > mSystemInfo.getZIndex()) + if (mCarousel.zIndex > mSystemInfo.getZIndex()) { renderInfoBar(trans); - else + } else { renderCarousel(trans); + } renderExtras(trans, minMax.first, minMax.second); - if (mCarousel.zIndex > mSystemInfo.getZIndex()) + if (mCarousel.zIndex > mSystemInfo.getZIndex()) { renderCarousel(trans); - else + } else { renderInfoBar(trans); + } renderExtras(trans, minMax.second, INT16_MAX); } @@ -415,7 +380,6 @@ std::vector SystemView::getHelpPrompts() prompts.push_back(HelpPrompt("up/down", "choose")); else prompts.push_back(HelpPrompt("left/right", "choose")); - prompts.push_back(HelpPrompt("a", "select")); prompts.push_back(HelpPrompt("x", "random")); @@ -432,14 +396,15 @@ HelpStyle SystemView::getHelpStyle() return style; } -void SystemView::onThemeChanged(const std::shared_ptr& /*theme*/) +void SystemView::onThemeChanged(const std::shared_ptr& /*theme*/) { LOG(LogDebug) << "SystemView::onThemeChanged()"; mViewNeedsReload = true; populate(); } -void SystemView::getViewElements(const std::shared_ptr& theme) +// Get the ThemeElements that make up the SystemView. +void SystemView::getViewElements(const std::shared_ptr& theme) { LOG(LogDebug) << "SystemView::getViewElements()"; @@ -459,240 +424,202 @@ void SystemView::getViewElements(const std::shared_ptr& theme) mViewNeedsReload = false; } -// --------------------------- -// NUEVO renderCarousel -// --------------------------- +// Render system carousel void SystemView::renderCarousel(const Transform4x4f& trans) { if (mEntries.empty()) return; - // ----- Transform del carrusel ----- + // background box behind logos Transform4x4f carouselTrans = trans; - carouselTrans.translate(Vector3f(mCarousel.pos.x(), mCarousel.pos.y(), 0.0f)); - carouselTrans.translate(Vector3f( - mCarousel.origin.x() * mCarousel.size.x() * -1, - mCarousel.origin.y() * mCarousel.size.y() * -1, - 0.0f)); + carouselTrans.translate(Vector3f(mCarousel.pos.x(), mCarousel.pos.y(), 0.0)); + carouselTrans.translate(Vector3f(mCarousel.origin.x() * mCarousel.size.x() * -1, mCarousel.origin.y() * mCarousel.size.y() * -1, 0.0f)); Vector2f clipPos(carouselTrans.translation().x(), carouselTrans.translation().y()); - Renderer::pushClipRect( - Vector2i((int)clipPos.x(), (int)clipPos.y()), - Vector2i((int)mCarousel.size.x(), (int)mCarousel.size.y())); + Renderer::pushClipRect(Vector2i((int)clipPos.x(), (int)clipPos.y()), + Vector2i((int)mCarousel.size.x(), (int)mCarousel.size.y())); Renderer::setMatrix(carouselTrans); - Renderer::drawRect( - 0.0f, - 0.0f, - mCarousel.size.x(), - mCarousel.size.y(), - mCarousel.color, - mCarousel.colorEnd, - mCarousel.colorGradientHorizontal); - - // ----- Configuración de logos ----- - Vector2f logoSpacing(0.0f, 0.0f); + Renderer::drawRect(0.0f, 0.0f, mCarousel.size.x(), mCarousel.size.y(), + mCarousel.color, mCarousel.colorEnd, mCarousel.colorGradientHorizontal); + + // draw logos + Vector2f logoSpacing(0.0, 0.0); // NB: logoSpacing will include the size of the logo itself as well! float xOff = 0.0f; float yOff = 0.0f; switch (mCarousel.type) { - case VERTICAL_WHEEL: - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - (mCamOffset * logoSpacing[1]); - if (mCarousel.logoAlignment == ALIGN_LEFT) - xOff = mCarousel.logoSize.x() / 10.f; - else if (mCarousel.logoAlignment == ALIGN_RIGHT) - xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f); - else - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f; - break; - - case VERTICAL: - logoSpacing[1] = ((mCarousel.size.y() - (mCarousel.logoSize.y() * mCarousel.maxLogoCount)) - / mCarousel.maxLogoCount) + mCarousel.logoSize.y(); - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - (mCamOffset * logoSpacing[1]); - - if (mCarousel.logoAlignment == ALIGN_LEFT) - xOff = mCarousel.logoSize.x() / 10.f; - else if (mCarousel.logoAlignment == ALIGN_RIGHT) - xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f); - else - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f; - break; - - case HORIZONTAL_WHEEL: - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f - (mCamOffset * logoSpacing[0]); - if (mCarousel.logoAlignment == ALIGN_TOP) - yOff = mCarousel.logoSize.y() / 10.f; - else if (mCarousel.logoAlignment == ALIGN_BOTTOM) - yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f); - else - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f; - break; - - case HORIZONTAL: - default: - logoSpacing[0] = ((mCarousel.size.x() - (mCarousel.logoSize.x() * mCarousel.maxLogoCount)) - / mCarousel.maxLogoCount) + mCarousel.logoSize.x(); - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f - (mCamOffset * logoSpacing[0]); - - if (mCarousel.logoAlignment == ALIGN_TOP) - yOff = mCarousel.logoSize.y() / 10.f; - else if (mCarousel.logoAlignment == ALIGN_BOTTOM) - yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f); - else - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f; - break; - } + case VERTICAL_WHEEL: + yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - (mCamOffset * logoSpacing[1]); + if (mCarousel.logoAlignment == ALIGN_LEFT) + xOff = mCarousel.logoSize.x() / 10.f; + else if (mCarousel.logoAlignment == ALIGN_RIGHT) + xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f); + else + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f; + break; - const int total = (int)mEntries.size(); - if (total <= 0) - { - Renderer::popClipRect(); - return; - } + case VERTICAL: + logoSpacing[1] = ((mCarousel.size.y() - (mCarousel.logoSize.y() * mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.y(); + yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - (mCamOffset * logoSpacing[1]); - // Siempre queremos "hasta" mCarousel.maxLogoCount logos visibles (PS4 style) - int logoCount = Math::min(mCarousel.maxLogoCount, total); + if (mCarousel.logoAlignment == ALIGN_LEFT) + xOff = mCarousel.logoSize.x() / 10.f; + else if (mCarousel.logoAlignment == ALIGN_RIGHT) + xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f); + else + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2; + break; - // Centro lógico (redondeado) para evitar "respirar" durante la animación - int centerIndex = (int)std::round(mCamOffset); - while (centerIndex < 0) - centerIndex += total; - while (centerIndex >= total) - centerIndex -= total; + case HORIZONTAL_WHEEL: + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2 - (mCamOffset * logoSpacing[0]); + if (mCarousel.logoAlignment == ALIGN_TOP) + yOff = mCarousel.logoSize.y() / 10; + else if (mCarousel.logoAlignment == ALIGN_BOTTOM) + yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f); + else + yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2; + break; - // Índice base para slots a la izquierda/derecha - int centerSlot = (int)mCamOffset; + case HORIZONTAL: + default: + logoSpacing[0] = ((mCarousel.size.x() - (mCarousel.logoSize.x() * mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.x(); + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f - (mCamOffset * logoSpacing[0]); - // Buffers según velocidad + if (mCarousel.logoAlignment == ALIGN_TOP) + yOff = mCarousel.logoSize.y() / 10.f; + else if (mCarousel.logoAlignment == ALIGN_BOTTOM) + yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f); + else + yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f; + break; + } + + int center = (int)(mCamOffset); + int logoCount = Math::min(mCarousel.maxLogoCount, (int)mEntries.size()); + + // Adding texture loading buffers depending on scrolling speed and status int bufferIndex = getScrollingVelocity() + 1; if (bufferIndex < 0) bufferIndex = 0; if (bufferIndex > 2) bufferIndex = 2; - int bufferLeft = logoBuffersLeft[bufferIndex]; + int bufferLeft = logoBuffersLeft[bufferIndex]; int bufferRight = logoBuffersRight[bufferIndex]; if (logoCount == 1) { - bufferLeft = 0; + bufferLeft = 0; bufferRight = 0; } - struct LogoRenderData - { - int index; // índice real del sistema - float slotDistance; // distancia en "slots" (para rotación wheel) - int ringDistance; // distancia por anillos (0 = centro, 1 = vecino, >=2 = lejano) - Transform4x4f trans; - }; - - std::vector logosToRender; - logosToRender.reserve(logoCount + bufferRight - bufferLeft + 2); - - // Función para distancia mínima en carrusel circular - auto wrappedRingDistance = [total](int from, int to) + // lambda para dibujar un logo + auto renderLogo = [this, &carouselTrans, &logoSpacing, xOff, yOff](int i) { - int d = from - to; - int half = total / 2; + if (mEntries.empty()) + return; - if (d > half) - d -= total; - else if (d < -half) - d += total; - - return std::abs(d); - }; - - for (int i = centerSlot - logoCount / 2 + bufferLeft; - i <= centerSlot + logoCount / 2 + bufferRight; - i++) - { int index = i; while (index < 0) - index += total; - while (index >= total) - index -= total; + index += (int)mEntries.size(); + while (index >= (int)mEntries.size()) + index -= (int)mEntries.size(); Transform4x4f logoTrans = carouselTrans; - logoTrans.translate(Vector3f( - i * logoSpacing[0] + xOff, - i * logoSpacing[1] + yOff, - 0.0f)); - float slotDist = (float)i - mCamOffset; - int ringDist = wrappedRingDistance(index, centerIndex); - - logosToRender.push_back({ index, slotDist, ringDist, logoTrans }); - } + // Ajuste de separación cuando el logo central está escalado (solo HORIZONTAL) + if (mCarousel.type == HORIZONTAL && mCarousel.logoScale != 1.0f && mCarousel.scaledLogoSpacing != 0.0f) + { + float logoDiffX = ((logoSpacing[0] * mCarousel.logoScale) - logoSpacing[0]) / 2.0f * mCarousel.scaledLogoSpacing; - // Dibujamos primero los lejanos, luego vecinos, y al final el central (para que quede por encima) - std::sort( - logosToRender.begin(), - logosToRender.end(), - [](const LogoRenderData& a, const LogoRenderData& b) + if (index == mCursor) + { + logoTrans.translate(Vector3f(i * logoSpacing[0] + xOff, i * logoSpacing[1] + yOff, 0.0f)); + } + else if (i < mCursor || (mCursor == 0 && index > mCarousel.maxLogoCount)) + { + logoTrans.translate(Vector3f(i * logoSpacing[0] + xOff - logoDiffX, i * logoSpacing[1] + yOff, 0.0f)); + } + else + { + logoTrans.translate(Vector3f(i * logoSpacing[0] + xOff + logoDiffX, i * logoSpacing[1] + yOff, 0.0f)); + } + } + else { - if (a.ringDistance != b.ringDistance) - return a.ringDistance > b.ringDistance; // mayor ringDistance primero - return std::fabs(a.slotDistance) > std::fabs(b.slotDistance); - }); + logoTrans.translate(Vector3f(i * logoSpacing[0] + xOff, i * logoSpacing[1] + yOff, 0.0f)); + } - for (auto& data : logosToRender) - { - const int ring = data.ringDistance; + float distance = i - mCamOffset; - // Escala fija por "anillos" (sin animación continua según mCamOffset) - float scale; - unsigned char opacity; + // escala según distancia + float scale = 1.0f + ((mCarousel.logoScale - 1.0f) * (1.0f - fabs(distance))); + scale = Math::min(mCarousel.logoScale, Math::max(1.0f, scale)); + scale /= mCarousel.logoScale; - if (ring == 0) -{ - scale = 1.0f; // central - opacity = 255; -} -else -{ - scale = 0.75f; // todos los laterales - opacity = 210; -} + // opacidad mínima configurable + float minOpacity = mCarousel.minLogoOpacity; + if (minOpacity < 0.0f) minOpacity = 0.0f; + if (minOpacity > 1.0f) minOpacity = 1.0f; - const std::shared_ptr& comp = mEntries.at(data.index).data.logo; + int opref = (int)Math::round(minOpacity * 255.0f); + int opacity = (int)Math::round(opref + ((0xFF - opref) * (1.0f - fabs(distance)))); + if (opacity < opref) + opacity = opref; - // Rueda vertical/horizontal sigue usando la distancia en slots para la rotación - if (mCarousel.type == VERTICAL_WHEEL || mCarousel.type == HORIZONTAL_WHEEL) - { - comp->setRotationDegrees(mCarousel.logoRotation * data.slotDistance); + const std::shared_ptr &comp = mEntries.at(index).data.logo; + if (!comp) + return; + + if (mCarousel.type == VERTICAL_WHEEL || mCarousel.type == HORIZONTAL_WHEEL) { + comp->setRotationDegrees(mCarousel.logoRotation * distance); comp->setRotationOrigin(mCarousel.logoRotationOrigin); } - comp->setScale(scale); - comp->setOpacity(opacity); - comp->render(data.trans); + comp->setOpacity((unsigned char)opacity); + comp->render(logoTrans); + }; + + // Primero todos menos el seleccionado + std::vector activePositions; + for (int i = center - logoCount / 2 + bufferLeft; i <= center + logoCount / 2 + bufferRight; i++) + { + int index = i; + while (index < 0) + index += (int)mEntries.size(); + while (index >= (int)mEntries.size()) + index -= (int)mEntries.size(); + + if (index == mCursor) + activePositions.push_back(i); + else + renderLogo(i); } + // Luego el seleccionado, para vencer la superposición + for (auto activePos : activePositions) + renderLogo(activePos); + Renderer::popClipRect(); } - void SystemView::renderInfoBar(const Transform4x4f& trans) { Renderer::setMatrix(trans); mSystemInfo.render(trans); } +// Draw background extras void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upper) { int extrasCenter = (int)mExtrasCamOffset; + // Adding texture loading buffers depending on scrolling speed and status int bufferIndex = getScrollingVelocity() + 1; - if (bufferIndex < 0) bufferIndex = 0; - if (bufferIndex > 2) bufferIndex = 2; Renderer::pushClipRect(Vector2i::Zero(), Vector2i((int)mSize.x(), (int)mSize.y())); - for (int i = extrasCenter + logoBuffersLeft[bufferIndex]; - i <= extrasCenter + logoBuffersRight[bufferIndex]; - i++) + for (int i = extrasCenter + logoBuffersLeft[bufferIndex]; i <= extrasCenter + logoBuffersRight[bufferIndex]; i++) { int index = i; while (index < 0) @@ -700,6 +627,7 @@ void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upp while (index >= (int)mEntries.size()) index -= (int)mEntries.size(); + //Only render selected system when not showing if (mShowing || index == mCursor) { Transform4x4f extrasTrans = trans; @@ -708,27 +636,24 @@ void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upp else extrasTrans.translate(Vector3f(0, (i - mExtrasCamOffset) * mSize.y(), 0)); - Renderer::pushClipRect( - Vector2i((int)extrasTrans.translation()[0], (int)extrasTrans.translation()[1]), - Vector2i((int)mSize.x(), (int)mSize.y())); - + Renderer::pushClipRect(Vector2i((int)extrasTrans.translation()[0], (int)extrasTrans.translation()[1]), + Vector2i((int)mSize.x(), (int)mSize.y())); SystemViewData data = mEntries.at(index).data; - for (unsigned int j = 0; j < data.backgroundExtras.size(); j++) - { - GuiComponent* extra = data.backgroundExtras[j]; - if (extra->getZIndex() >= lower && extra->getZIndex() < upper) + for (unsigned int j = 0; j < data.backgroundExtras.size(); j++) { + GuiComponent *extra = data.backgroundExtras[j]; + if (extra->getZIndex() >= lower && extra->getZIndex() < upper) { extra->render(extrasTrans); + } } - Renderer::popClipRect(); } } - Renderer::popClipRect(); } void SystemView::renderFade(const Transform4x4f& trans) { + // fade extras if necessary if (mExtrasFadeOpacity) { unsigned int fadeColor = 0x00000000 | (unsigned char)(mExtrasFadeOpacity * 255); @@ -737,29 +662,36 @@ void SystemView::renderFade(const Transform4x4f& trans) } } -void SystemView::getDefaultElements(void) +// Populate the system carousel with the legacy values +void SystemView::getDefaultElements(void) { - mCarousel.type = HORIZONTAL; + // Carousel + mCarousel.type = HORIZONTAL; mCarousel.logoAlignment = ALIGN_CENTER; - mCarousel.size.x() = mSize.x(); - mCarousel.size.y() = 0.2325f * mSize.y(); - mCarousel.pos.x() = 0.0f; - mCarousel.pos.y() = 0.5f * (mSize.y() - mCarousel.size.y()); - mCarousel.origin.x() = 0.0f; - mCarousel.origin.y() = 0.0f; - mCarousel.color = 0xFFFFFFD8; - mCarousel.colorEnd = 0xFFFFFFD8; + mCarousel.size.x() = mSize.x(); + mCarousel.size.y() = 0.2325f * mSize.y(); + mCarousel.pos.x() = 0.0f; + mCarousel.pos.y() = 0.5f * (mSize.y() - mCarousel.size.y()); + mCarousel.origin.x() = 0.0f; + mCarousel.origin.y() = 0.0f; + mCarousel.color = 0xFFFFFFD8; + mCarousel.colorEnd = 0xFFFFFFD8; mCarousel.colorGradientHorizontal = true; - mCarousel.logoScale = 1.2f; - mCarousel.logoRotation = 7.5f; + mCarousel.logoScale = 1.2f; + mCarousel.logoRotation = 7.5; mCarousel.logoRotationOrigin.x() = -5; - mCarousel.logoRotationOrigin.y() = 0.5f; - mCarousel.logoSize.x() = 0.25f * mSize.x(); - mCarousel.logoSize.y() = 0.155f * mSize.y(); - mCarousel.maxLogoCount = 3; - mCarousel.zIndex = 40; - - mSystemInfo.setSize(mSize.x(), mSystemInfo.getFont()->getLetterHeight() * 2.2f); + mCarousel.logoRotationOrigin.y() = 0.5; + mCarousel.logoSize.x() = 0.25f * mSize.x(); + mCarousel.logoSize.y() = 0.155f * mSize.y(); + mCarousel.maxLogoCount = 3; + mCarousel.zIndex = 40; + + // NUEVO: valores por defecto para mejoras visuales + mCarousel.minLogoOpacity = 0.5f; // equivalente a 0x80 de antes + mCarousel.scaledLogoSpacing = 0.0f; // 0 = comportamiento clásico + + // System Info Bar + mSystemInfo.setSize(mSize.x(), mSystemInfo.getFont()->getLetterHeight()*2.2f); mSystemInfo.setPosition(0, (mCarousel.pos.y() + mCarousel.size.y() - 0.2f)); mSystemInfo.setBackgroundColor(0xDDDDDDD8); mSystemInfo.setRenderBackground(true); @@ -790,7 +722,7 @@ void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem) mCarousel.origin = elem->get("origin"); if (elem->has("color")) { - mCarousel.color = elem->get("color"); + mCarousel.color = elem->get("color"); mCarousel.colorEnd = mCarousel.color; } if (elem->has("colorEnd")) @@ -822,6 +754,15 @@ void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem) else mCarousel.logoAlignment = ALIGN_CENTER; } + if (elem->has("scrollSound")) + mScrollSound = elem->get("scrollSound"); + + // NUEVO: lectura de propiedades extra + if (elem->has("minLogoOpacity")) + mCarousel.minLogoOpacity = elem->get("minLogoOpacity"); + + if (elem->has("scaledLogoSpacing")) + mCarousel.scaledLogoSpacing = elem->get("scaledLogoSpacing"); } void SystemView::onShow() From 07a21dea1ef42e85f86b6696ba5d3fd0f89b0d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Ren=C3=A9=20Caballero=20Marquez?= Date: Tue, 18 Nov 2025 19:16:19 -0300 Subject: [PATCH 05/10] Enhance SystemView with new visual and sound properties Added new properties for visual behavior and optional sound. --- es-app/src/views/SystemView.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/es-app/src/views/SystemView.h b/es-app/src/views/SystemView.h index 85b0aca44f..30882acf73 100644 --- a/es-app/src/views/SystemView.h +++ b/es-app/src/views/SystemView.h @@ -7,6 +7,7 @@ #include "resources/Font.h" #include "GuiComponent.h" #include +#include // <-- añadido para std::string class AnimatedImageComponent; class SystemData; @@ -41,8 +42,13 @@ struct SystemViewCarousel int maxLogoCount; // number of logos shown on the carousel Vector2f logoSize; float zIndex; + + // NUEVO: propiedades mejoradas para comportamiento visual + float minLogoOpacity; // Opacidad mínima (0.0f–1.0f) + float scaledLogoSpacing; // Ajuste de separación cuando el logo central está escalado }; + class SystemView : public IList { public: @@ -76,7 +82,6 @@ class SystemView : public IList void renderInfoBar(const Transform4x4f& trans); void renderFade(const Transform4x4f& trans); - SystemViewCarousel mCarousel; TextComponent mSystemInfo; @@ -87,6 +92,9 @@ class SystemView : public IList bool mViewNeedsReload; bool mShowing; + + // NUEVO: sonido opcional al mover el carrusel + std::string mScrollSound; }; #endif // ES_APP_VIEWS_SYSTEM_VIEW_H From 6420736ec184af0412ad920c61c2841b15470902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Ren=C3=A9=20Caballero=20Marquez?= Date: Sun, 7 Dec 2025 09:23:00 -0300 Subject: [PATCH 06/10] Polish SystemView carousel extensions --- es-app/src/views/SystemView.cpp | 39 +++++++++++++++++++-------------- es-app/src/views/SystemView.h | 14 ++++++------ es-core/src/ThemeData.cpp | 16 +++++++------- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/es-app/src/views/SystemView.cpp b/es-app/src/views/SystemView.cpp index dc367f4f8d..495a184270 100644 --- a/es-app/src/views/SystemView.cpp +++ b/es-app/src/views/SystemView.cpp @@ -7,6 +7,7 @@ #include "Log.h" #include "Scripting.h" #include "Settings.h" +#include "Sound.h" #include "SystemData.h" #include "Window.h" @@ -527,10 +528,10 @@ void SystemView::renderCarousel(const Transform4x4f& trans) Transform4x4f logoTrans = carouselTrans; - // Ajuste de separación cuando el logo central está escalado (solo HORIZONTAL) - if (mCarousel.type == HORIZONTAL && mCarousel.logoScale != 1.0f && mCarousel.scaledLogoSpacing != 0.0f) - { - float logoDiffX = ((logoSpacing[0] * mCarousel.logoScale) - logoSpacing[0]) / 2.0f * mCarousel.scaledLogoSpacing; + // Adjust spacing when the focused logo is scaled (horizontal only) + if (mCarousel.type == HORIZONTAL && mCarousel.logoScale != 1.0f && mCarousel.scaledLogoSpacing != 0.0f) + { + float logoDiffX = ((logoSpacing[0] * mCarousel.logoScale) - logoSpacing[0]) / 2.0f * mCarousel.scaledLogoSpacing; if (index == mCursor) { @@ -557,10 +558,10 @@ void SystemView::renderCarousel(const Transform4x4f& trans) scale = Math::min(mCarousel.logoScale, Math::max(1.0f, scale)); scale /= mCarousel.logoScale; - // opacidad mínima configurable - float minOpacity = mCarousel.minLogoOpacity; - if (minOpacity < 0.0f) minOpacity = 0.0f; - if (minOpacity > 1.0f) minOpacity = 1.0f; + // Configurable minimum opacity + float minOpacity = mCarousel.minLogoOpacity; + if (minOpacity < 0.0f) minOpacity = 0.0f; + if (minOpacity > 1.0f) minOpacity = 1.0f; int opref = (int)Math::round(minOpacity * 255.0f); int opacity = (int)Math::round(opref + ((0xFF - opref) * (1.0f - fabs(distance)))); @@ -686,9 +687,9 @@ void SystemView::getDefaultElements(void) mCarousel.maxLogoCount = 3; mCarousel.zIndex = 40; - // NUEVO: valores por defecto para mejoras visuales - mCarousel.minLogoOpacity = 0.5f; // equivalente a 0x80 de antes - mCarousel.scaledLogoSpacing = 0.0f; // 0 = comportamiento clásico + // Default values for additional visual tuning + mCarousel.minLogoOpacity = 0.5f; // equivalent to the previous 0x80 minimum + mCarousel.scaledLogoSpacing = 0.0f; // 0 = classic behavior // System Info Bar mSystemInfo.setSize(mSize.x(), mSystemInfo.getFont()->getLetterHeight()*2.2f); @@ -757,12 +758,18 @@ void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem) if (elem->has("scrollSound")) mScrollSound = elem->get("scrollSound"); - // NUEVO: lectura de propiedades extra - if (elem->has("minLogoOpacity")) - mCarousel.minLogoOpacity = elem->get("minLogoOpacity"); + // Read optional carousel extensions + if (elem->has("minLogoOpacity")) + mCarousel.minLogoOpacity = elem->get("minLogoOpacity"); + + if (elem->has("scaledLogoSpacing")) + mCarousel.scaledLogoSpacing = elem->get("scaledLogoSpacing"); +} - if (elem->has("scaledLogoSpacing")) - mCarousel.scaledLogoSpacing = elem->get("scaledLogoSpacing"); +void SystemView::onScroll(int amt) +{ + if (amt != 0 && !mScrollSound.empty()) + Sound::get(mScrollSound)->play(); } void SystemView::onShow() diff --git a/es-app/src/views/SystemView.h b/es-app/src/views/SystemView.h index 30882acf73..28dcbb8779 100644 --- a/es-app/src/views/SystemView.h +++ b/es-app/src/views/SystemView.h @@ -7,7 +7,7 @@ #include "resources/Font.h" #include "GuiComponent.h" #include -#include // <-- añadido para std::string +#include class AnimatedImageComponent; class SystemData; @@ -43,9 +43,9 @@ struct SystemViewCarousel Vector2f logoSize; float zIndex; - // NUEVO: propiedades mejoradas para comportamiento visual - float minLogoOpacity; // Opacidad mínima (0.0f–1.0f) - float scaledLogoSpacing; // Ajuste de separación cuando el logo central está escalado + // Additional visual tuning properties + float minLogoOpacity; // Minimum opacity (0.0f–1.0f) + float scaledLogoSpacing; // Extra spacing when the focused logo is scaled }; @@ -60,7 +60,8 @@ class SystemView : public IList void goToSystem(SystemData* system, bool animate); bool input(InputConfig* config, Input input) override; - void update(int deltaTime) override; + void update(int deltaTime) override; + void onScroll(int amt) override; void render(const Transform4x4f& parentTrans) override; void onThemeChanged(const std::shared_ptr& theme); @@ -93,8 +94,7 @@ class SystemView : public IList bool mViewNeedsReload; bool mShowing; - // NUEVO: sonido opcional al mover el carrusel - std::string mScrollSound; + std::string mScrollSound; }; #endif // ES_APP_VIEWS_SYSTEM_VIEW_H diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index 2fbd0f9266..e7696cb939 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -167,14 +167,14 @@ std::map> The { "logoRotation", FLOAT }, { "logoRotationOrigin", NORMALIZED_PAIR }, { "logoSize", NORMALIZED_PAIR }, - { "logoAlignment", STRING }, - { "maxLogoCount", FLOAT }, - { "zIndex", FLOAT }, - // NUEVO: propiedades extra del carrusel - { "minLogoOpacity", FLOAT }, // 0.0 – 1.0 - { "scaledLogoSpacing", FLOAT }, // factor de separación - { "scrollSound", PATH } // sonido opcional (por ahora solo parseado) - } } + { "logoAlignment", STRING }, + { "maxLogoCount", FLOAT }, + { "zIndex", FLOAT }, + // Additional carousel options + { "minLogoOpacity", FLOAT }, // 0.0 – 1.0 + { "scaledLogoSpacing", FLOAT }, // spacing multiplier when scaling logos + { "scrollSound", PATH } // optional sound to play while scrolling + } } }; #define MINIMUM_THEME_FORMAT_VERSION 3 From cabfed107788fad4beb1e9395835c86e947be813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Ren=C3=A9=20Caballero=20Marquez?= Date: Sun, 7 Dec 2025 09:40:31 -0300 Subject: [PATCH 07/10] Document carousel options and reset scroll sound --- THEMES.md | 25 ++++++++++++++++++------- es-app/src/views/SystemView.cpp | 9 ++++++--- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/THEMES.md b/THEMES.md index 5399b03143..7ee2e04476 100644 --- a/THEMES.md +++ b/THEMES.md @@ -871,12 +871,17 @@ EmulationStation borrows the concept of "nine patches" from Android (or "9-Slice * `origin` - type: NORMALIZED_PAIR. - Where on the carousel `pos` refers to. For example, an origin of `0.5 0.5` and a `pos` of `0.5 0.5` would place the carousel exactly in the middle of the screen. If the "POSITION" and "SIZE" attributes are themable, "ORIGIN" is implied. * `color` - type: COLOR. - - Controls the color of the carousel background. - - Default is FFFFFFD8 + - Controls the color of the carousel background. + - Default is FFFFFFD8 +* `colorEnd` - type: COLOR. + - Optional end color when drawing a gradient background. + - Defaults to the same value as `color`. +* `gradientType` - type: STRING. + - Sets the gradient direction. Accepted values are "horizontal" (default) or "vertical". * `logoSize` - type: NORMALIZED_PAIR. Default is "0.25 0.155" * `logoScale` - type: FLOAT. - - Selected logo is increased in size by this scale - - Default is 1.2 + - Selected logo is increased in size by this scale + - Default is 1.2 * `logoRotation` - type: FLOAT. - Angle in degrees that the logos should be rotated. Value should be positive. - Default is 7.5 @@ -890,10 +895,16 @@ EmulationStation borrows the concept of "nine patches" from Android (or "9-Slice - Accepted values are "left", "right" or "center" when `type` is "vertical" or "vertical_wheel". - Default is "center" * `maxLogoCount` - type: FLOAT. - - Sets the number of logos to display in the carousel. - - Default is 3 + - Sets the number of logos to display in the carousel. + - Default is 3 +* `minLogoOpacity` - type: FLOAT. + - Sets a minimum opacity for unfocused logos (0.0 - 1.0). Default is 0.5. +* `scaledLogoSpacing` - type: FLOAT. + - Adds extra spacing when scaled logos overlap. Set to 0 for classic behaviour. Default is 0. * `zIndex` - type: FLOAT. - - z-index value for component. Components will be rendered in order of z-index value from low to high. + - z-index value for component. Components will be rendered in order of z-index value from low to high. +* `scrollSound` - type: PATH. + - Optional sound played while scrolling the system carousel. The help system is a special element that displays a context-sensitive list of actions the user can take at any time. You should try and keep the position constant throughout every screen. Keep in mind the "default" settings (including position) are used whenever the user opens a menu. diff --git a/es-app/src/views/SystemView.cpp b/es-app/src/views/SystemView.cpp index 495a184270..70db4a8dec 100644 --- a/es-app/src/views/SystemView.cpp +++ b/es-app/src/views/SystemView.cpp @@ -666,9 +666,9 @@ void SystemView::renderFade(const Transform4x4f& trans) // Populate the system carousel with the legacy values void SystemView::getDefaultElements(void) { - // Carousel - mCarousel.type = HORIZONTAL; - mCarousel.logoAlignment = ALIGN_CENTER; + // Carousel + mCarousel.type = HORIZONTAL; + mCarousel.logoAlignment = ALIGN_CENTER; mCarousel.size.x() = mSize.x(); mCarousel.size.y() = 0.2325f * mSize.y(); mCarousel.pos.x() = 0.0f; @@ -691,6 +691,9 @@ void SystemView::getDefaultElements(void) mCarousel.minLogoOpacity = 0.5f; // equivalent to the previous 0x80 minimum mCarousel.scaledLogoSpacing = 0.0f; // 0 = classic behavior + // Reset optional per-theme values + mScrollSound.clear(); + // System Info Bar mSystemInfo.setSize(mSize.x(), mSystemInfo.getFont()->getLetterHeight()*2.2f); mSystemInfo.setPosition(0, (mCarousel.pos.y() + mCarousel.size.y() - 0.2f)); From 971aa22d17336a260bd365423f63446dcca2c2d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Ren=C3=A9=20Caballero=20Marquez?= Date: Sun, 7 Dec 2025 09:45:05 -0300 Subject: [PATCH 08/10] Reset text list scroll sound when theme removes override --- es-app/src/components/TextListComponent.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/es-app/src/components/TextListComponent.h b/es-app/src/components/TextListComponent.h index 2250769d30..e5a617c54d 100644 --- a/es-app/src/components/TextListComponent.h +++ b/es-app/src/components/TextListComponent.h @@ -427,8 +427,14 @@ void TextListComponent::applyTheme(const std::shared_ptr& theme, c const float selectorHeight = Math::max(mFont->getHeight(1.0), (float)mFont->getSize()) * mLineSpacing; setSelectorHeight(selectorHeight); - if(properties & SOUND && elem->has("scrollSound")) - mScrollSound = elem->get("scrollSound"); + if (properties & SOUND) + { + // Clear any previous theme scroll sound so themes can remove the override. + mScrollSound.clear(); + + if(elem->has("scrollSound")) + mScrollSound = elem->get("scrollSound"); + } if(properties & ALIGNMENT) { From 32b80855314a9d45ce23a4022d8fa596f3a9b0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Ren=C3=A9=20Caballero=20Marquez?= Date: Sat, 3 Jan 2026 21:44:18 -0300 Subject: [PATCH 09/10] ES-X --- es-app/CMakeLists.txt | 35 +- es-app/src/CollectionSystemManager.cpp | 286 +++--- es-app/src/FileSorts.cpp | 57 +- es-app/src/LocaleES.cpp | 136 +++ es-app/src/LocaleES.h | 36 + es-app/src/LocaleESHook.cpp | 9 + es-app/src/MetaData.cpp | 73 +- es-app/src/NavigationSounds.h | 70 ++ es-app/src/Paths.h | 25 + es-app/src/SystemData.cpp | 248 +++++- es-app/src/SystemData.h | 39 +- es-app/src/ThemeOptions.cpp | 5 + es-app/src/ThemeOptions.h | 50 ++ es-app/src/audio/BackgroundMusicManager.cpp | 497 +++++++++++ es-app/src/audio/BackgroundMusicManager.h | 83 ++ es-app/src/components/TextListComponent.h | 29 +- .../src/guis/GuiCollectionSystemsOptions.cpp | 276 ++++-- es-app/src/guis/GuiGamelistOptions.cpp | 202 +++-- es-app/src/guis/GuiGamelistOptions.h | 3 + es-app/src/guis/GuiMenu.cpp | 549 ++++++++---- es-app/src/guis/GuiMenu.h | 36 +- es-app/src/guis/GuiScraperMulti.cpp | 135 ++- es-app/src/guis/GuiScraperStart.cpp | 193 ++-- es-app/src/guis/GuiThemeOptions.cpp | 790 +++++++++++++++++ es-app/src/guis/GuiThemeOptions.h | 18 + es-app/src/main.cpp | 70 +- es-app/src/scrapers/ScreenScraper.cpp | 71 +- es-app/src/views/SystemView.cpp | 824 +++++++++++------- es-app/src/views/SystemView.h | 14 +- es-app/src/views/ViewController.cpp | 47 +- .../src/views/gamelist/BasicGameListView.cpp | 150 +++- es-app/src/views/gamelist/BasicGameListView.h | 7 +- .../views/gamelist/DetailedGameListView.cpp | 80 +- .../src/views/gamelist/GridGameListView.cpp | 97 ++- .../views/gamelist/ISimpleGameListView.cpp | 156 +++- .../src/views/gamelist/VideoGameListView.cpp | 146 ++-- es-core/CMakeLists.txt | 44 +- es-core/src/InputManager.cpp | 19 + es-core/src/LocaleESHook.h | 6 + es-core/src/Settings.cpp | 155 ++-- es-core/src/ThemeData.cpp | 204 ++++- es-core/src/Window.cpp | 179 +++- es-core/src/Window.h | 13 +- es-core/src/components/ButtonComponent.cpp | 129 +-- es-core/src/components/ComponentList.cpp | 508 ++++++----- .../components/ControllerStatusComponent.cpp | 89 ++ .../components/ControllerStatusComponent.h | 24 + es-core/src/components/DateTimeComponent.cpp | 68 +- es-core/src/components/HelpComponent.cpp | 61 +- 49 files changed, 5276 insertions(+), 1765 deletions(-) create mode 100644 es-app/src/LocaleES.cpp create mode 100644 es-app/src/LocaleES.h create mode 100644 es-app/src/LocaleESHook.cpp create mode 100644 es-app/src/NavigationSounds.h create mode 100644 es-app/src/Paths.h create mode 100644 es-app/src/ThemeOptions.cpp create mode 100644 es-app/src/ThemeOptions.h create mode 100644 es-app/src/audio/BackgroundMusicManager.cpp create mode 100644 es-app/src/audio/BackgroundMusicManager.h create mode 100644 es-app/src/guis/GuiThemeOptions.cpp create mode 100644 es-app/src/guis/GuiThemeOptions.h create mode 100644 es-core/src/LocaleESHook.h create mode 100644 es-core/src/components/ControllerStatusComponent.cpp create mode 100644 es-core/src/components/ControllerStatusComponent.h diff --git a/es-app/CMakeLists.txt b/es-app/CMakeLists.txt index a828a462f3..7f9ebc0d4b 100644 --- a/es-app/CMakeLists.txt +++ b/es-app/CMakeLists.txt @@ -13,6 +13,12 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/FileFilterIndex.h ${CMAKE_CURRENT_SOURCE_DIR}/src/SystemScreenSaver.h ${CMAKE_CURRENT_SOURCE_DIR}/src/CollectionSystemManager.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/LocaleES.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/ThemeOptions.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiThemeOptions.h + + # 🔊 Background music + ${CMAKE_CURRENT_SOURCE_DIR}/src/audio/BackgroundMusicManager.h # GuiComponents ${CMAKE_CURRENT_SOURCE_DIR}/src/components/AsyncReqComponent.h @@ -73,6 +79,15 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/FileFilterIndex.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/SystemScreenSaver.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/CollectionSystemManager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/LocaleES.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ThemeOptions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiThemeOptions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiThemeOptions.h + + + # 🔊 Background music + ${CMAKE_CURRENT_SOURCE_DIR}/src/audio/BackgroundMusicManager.cpp + # GuiComponents ${CMAKE_CURRENT_SOURCE_DIR}/src/components/AsyncReqComponent.cpp @@ -115,36 +130,25 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/views/UIModeController.cpp ) -#------------------------------------------------------------------------------- -# define OS specific sources and headers if(MSVC) LIST(APPEND ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/EmulationStation.rc ) endif() -#------------------------------------------------------------------------------- -# define target include_directories(${COMMON_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/src) add_executable(emulationstation ${ES_SOURCES} ${ES_HEADERS}) -target_link_libraries(emulationstation ${COMMON_LIBRARIES} es-core) -# special properties for Windows builds +# 🔗 añadimos SDL2_mixer para la música de fondo +target_link_libraries(emulationstation ${COMMON_LIBRARIES} es-core SDL2_mixer) + if(MSVC) - # Always compile with the "WINDOWS" subsystem to avoid console window flashing at startup - # when --debug is not set (see es-core/src/main.cpp for explanation). - # The console will still be shown if launched with --debug. - # Note that up to CMake 2.8.10 this feature is broken: http://public.kitware.com/Bug/view.php?id=12566 set_target_properties(emulationstation PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS") set_target_properties(emulationstation PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS") set_target_properties(emulationstation PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") set_target_properties(emulationstation PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS") endif() - -#------------------------------------------------------------------------------- -# set up CPack install stuff so `make install` does something useful - install(TARGETS emulationstation RUNTIME DESTINATION bin) @@ -154,7 +158,8 @@ INCLUDE(InstallRequiredSystemLibraries) SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A flexible graphical emulator front-end") SET(CPACK_PACKAGE_DESCRIPTION "EmulationStation is a flexible, graphical front-end designed for keyboardless navigation of your multi-platform retro game collection.") -SET(CPACK_RESOURCE_FILE LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.md") +# 🔹 CORRECCIÓN: se mueve el LICENSE.md a la raíz del proyecto +SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE.md") SET(CPACK_RESOURCE_FILE README "${CMAKE_CURRENT_SOURCE_DIR}/README.md") SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Alec Lofquist ") diff --git a/es-app/src/CollectionSystemManager.cpp b/es-app/src/CollectionSystemManager.cpp index 440abf7fa8..379fce2631 100644 --- a/es-app/src/CollectionSystemManager.cpp +++ b/es-app/src/CollectionSystemManager.cpp @@ -13,29 +13,31 @@ #include "Settings.h" #include "SystemData.h" #include "ThemeData.h" +#include "LocaleES.h" + #include #include #include -/* Handling the getting, initialization, deinitialization, saving and deletion of - * a CollectionSystemManager Instance */ +// Handling the getting, initialization, deinitialization, saving and deletion of +// a CollectionSystemManager Instance CollectionSystemManager* CollectionSystemManager::sInstance = NULL; CollectionSystemManager::CollectionSystemManager(Window* window) : mWindow(window) { CollectionSystemDecl systemDecls[] = { - //type name long name (display) default sort (key, order) theme folder isCustom - { AUTO_ALL_GAMES, "all", "all games", "name, ascending", "auto-allgames", false }, - { AUTO_LAST_PLAYED, "recent", "last played", "last played, descending", "auto-lastplayed", false }, - { AUTO_FAVORITES, "favorites", "favorites", "name, ascending", "auto-favorites", false }, - { AUTO_RANDOM, RANDOM_COLL_ID, "random", "name, ascending", "auto-random", false }, - { CUSTOM_COLLECTION, CUSTOM_COLL_ID, "collections", "name, ascending", "custom-collections", true } + // type name long name (display) default sort (key, order) theme folder isCustom + { AUTO_ALL_GAMES, "all", "ALL GAMES", "name, ascending", "auto-allgames", false }, + { AUTO_LAST_PLAYED, "recent", "LAST PLAYED", "last played, descending", "auto-lastplayed", false }, + { AUTO_FAVORITES, "favorites", "FAVORITES", "name, ascending", "auto-favorites", false }, + { AUTO_RANDOM, RANDOM_COLL_ID, "RANDOM", "name, ascending", "auto-random", false }, + { CUSTOM_COLLECTION, CUSTOM_COLL_ID, "COLLECTIONS", "name, ascending", "custom-collections", true } }; // create a map std::vector tempSystemDecl = std::vector(systemDecls, systemDecls + sizeof(systemDecls) / sizeof(systemDecls[0])); - for (std::vector::const_iterator it = tempSystemDecl.cbegin(); it != tempSystemDecl.cend(); ++it ) + for (std::vector::const_iterator it = tempSystemDecl.cbegin(); it != tempSystemDecl.cend(); ++it) { mCollectionSystemDeclsIndex[(*it).name] = (*it); } @@ -51,7 +53,7 @@ CollectionSystemManager::CollectionSystemManager(Window* window) : mWindow(windo mCollectionEnvData->mPlatformIds = allPlatformIds; std::string path = getCollectionsFolder(); - if(!Utils::FileSystem::exists(path)) + if (!Utils::FileSystem::exists(path)) Utils::FileSystem::createDirectory(path); mIsEditingCustom = false; @@ -67,7 +69,7 @@ CollectionSystemManager::~CollectionSystemManager() removeCollectionsFromDisplayedSystems(); // iterate the map - for(std::map::const_iterator it = mCustomCollectionSystemsData.cbegin() ; it != mCustomCollectionSystemsData.cend() ; it++ ) + for (std::map::const_iterator it = mCustomCollectionSystemsData.cbegin(); it != mCustomCollectionSystemsData.cend(); it++) { if (it->second.isPopulated) { @@ -118,12 +120,12 @@ bool CollectionSystemManager::saveCustomCollection(SystemData* sys) if (!configFile.good()) { auto const errNo = errno; - LOG(LogError) << "Failed to create file, collection not created: " << absCollectionFn << ": " << std::strerror(errNo) << " (" << errNo << ")"; + LOG(LogError) << "Failed to create file, collection not created: " << absCollectionFn << ": " << std::strerror(errNo) << " (" << errNo << ")"; return false; } - for(std::unordered_map::const_iterator iter = games.cbegin(); iter != games.cend(); ++iter) + for (std::unordered_map::const_iterator iter = games.cbegin(); iter != games.cend(); ++iter) { - std::string path = iter->first; + std::string path = iter->first; configFile << path << std::endl; } configFile.close(); @@ -140,7 +142,7 @@ void CollectionSystemManager::loadCollectionSystems(bool async) mCustomCollectionsBundle = createNewCollectionEntry(decl.name, decl, CollectionFlags::NONE); // we will also load custom systems here initCustomCollectionSystems(); - if(Settings::getInstance()->getString("CollectionSystemsAuto") != "" || Settings::getInstance()->getString("CollectionSystemsCustom") != "") + if (Settings::getInstance()->getString("CollectionSystemsAuto") != "" || Settings::getInstance()->getString("CollectionSystemsCustom") != "") { // Now see which ones are enabled loadEnabledListFromSettings(); @@ -158,7 +160,7 @@ void CollectionSystemManager::loadEnabledListFromSettings() std::vector autoSelected = Utils::String::delimitedStringToVector(Settings::getInstance()->getString("CollectionSystemsAuto"), ",", true); // iterate the map - for(std::map::iterator it = mAutoCollectionSystemsData.begin() ; it != mAutoCollectionSystemsData.end() ; it++ ) + for (std::map::iterator it = mAutoCollectionSystemsData.begin(); it != mAutoCollectionSystemsData.end(); it++) { it->second.isEnabled = (std::find(autoSelected.cbegin(), autoSelected.cend(), it->first) != autoSelected.cend()); } @@ -167,7 +169,7 @@ void CollectionSystemManager::loadEnabledListFromSettings() std::vector customSelected = Utils::String::delimitedStringToVector(Settings::getInstance()->getString("CollectionSystemsCustom"), ",", true); // iterate the map - for(std::map::iterator it = mCustomCollectionSystemsData.begin() ; it != mCustomCollectionSystemsData.end() ; it++ ) + for (std::map::iterator it = mCustomCollectionSystemsData.begin(); it != mCustomCollectionSystemsData.end(); it++) { it->second.isEnabled = (std::find(customSelected.cbegin(), customSelected.cend(), it->first) != customSelected.cend()); } @@ -181,13 +183,13 @@ void CollectionSystemManager::updateSystemsList() // add custom enabled ones addEnabledCollectionsToDisplayedSystems(&mCustomCollectionSystemsData, false); - if(Settings::getInstance()->getBool("SortAllSystems")) + if (Settings::getInstance()->getBool("SortAllSystems")) { // sort custom individual systems with other systems std::sort(SystemData::sSystemVector.begin(), SystemData::sSystemVector.end(), systemSort); // move RetroPie system to end, before auto collections - for(auto sysIt = SystemData::sSystemVector.cbegin(); sysIt != SystemData::sSystemVector.cend(); ) + for (auto sysIt = SystemData::sSystemVector.cbegin(); sysIt != SystemData::sSystemVector.cend(); ) { if ((*sysIt)->getName() == "retropie") { @@ -203,7 +205,7 @@ void CollectionSystemManager::updateSystemsList() } } - if(mCustomCollectionsBundle->getRootFolder()->getChildren().size() > 0) + if (mCustomCollectionsBundle->getRootFolder()->getChildren().size() > 0) { mCustomCollectionsBundle->getRootFolder()->sort(getSortTypeFromString(mCollectionSystemDeclsIndex[CUSTOM_COLL_ID].defaultSort)); SystemData::sSystemVector.push_back(mCustomCollectionsBundle); @@ -215,14 +217,14 @@ void CollectionSystemManager::updateSystemsList() addEnabledCollectionsToDisplayedSystems(&mAutoCollectionSystemsData, true); // create views for collections, before reload - for(auto sysIt = SystemData::sSystemVector.cbegin(); sysIt != SystemData::sSystemVector.cend(); sysIt++) + for (auto sysIt = SystemData::sSystemVector.cbegin(); sysIt != SystemData::sSystemVector.cend(); sysIt++) { if ((*sysIt)->isCollection()) ViewController::get()->getGameListView((*sysIt)); } // if we were editing a custom collection, and it's no longer enabled, exit edit mode - if(mIsEditingCustom && !mEditingCollectionSystemData->isEnabled) + if (mIsEditingCustom && !mEditingCollectionSystemData->isEnabled) { exitEditMode(); } @@ -239,7 +241,7 @@ void CollectionSystemManager::refreshCollectionSystems(FileData* file) allCollections.insert(mAutoCollectionSystemsData.cbegin(), mAutoCollectionSystemsData.cend()); allCollections.insert(mCustomCollectionSystemsData.cbegin(), mCustomCollectionSystemsData.cend()); - for(auto sysDataIt = allCollections.cbegin(); sysDataIt != allCollections.cend(); sysDataIt++) + for (auto sysDataIt = allCollections.cbegin(); sysDataIt != allCollections.cend(); sysDataIt++) { updateCollectionSystem(file, sysDataIt->second); } @@ -327,7 +329,7 @@ void CollectionSystemManager::deleteCollectionFiles(FileData* file) allCollections.insert(mAutoCollectionSystemsData.cbegin(), mAutoCollectionSystemsData.cend()); allCollections.insert(mCustomCollectionSystemsData.cbegin(), mCustomCollectionSystemsData.cend()); - for(auto sysDataIt = allCollections.begin(); sysDataIt != allCollections.end(); sysDataIt++) + for (auto sysDataIt = allCollections.begin(); sysDataIt != allCollections.end(); sysDataIt++) { if (sysDataIt->second.isPopulated) { @@ -348,9 +350,9 @@ void CollectionSystemManager::deleteCollectionFiles(FileData* file) bool CollectionSystemManager::isThemeGenericCollectionCompatible(bool genericCustomCollections) { std::vector cfgSys = getCollectionThemeFolders(genericCustomCollections); - for(auto sysIt = cfgSys.cbegin(); sysIt != cfgSys.cend(); sysIt++) + for (auto sysIt = cfgSys.cbegin(); sysIt != cfgSys.cend(); sysIt++) { - if(!themeFolderExists(*sysIt)) + if (!themeFolderExists(*sysIt)) return false; } return true; @@ -364,7 +366,7 @@ bool CollectionSystemManager::isThemeCustomCollectionCompatible(std::vectorgetString("ThemeSet")); - if(set != themeSets.cend()) + if (set != themeSets.cend()) { std::string defaultThemeFilePath = set->second.path + "/theme.xml"; if (Utils::FileSystem::exists(defaultThemeFilePath)) @@ -373,9 +375,9 @@ bool CollectionSystemManager::isThemeCustomCollectionCompatible(std::vector 0) { + if (index > 0) { name = name.substr(0, name.size() - infix.size()); } - return getValidNewCollectionName(name, index+1); + return getValidNewCollectionName(name, index + 1); } } // if it matches one of the custom collections reserved names if (mCollectionSystemDeclsIndex.find(name) != mCollectionSystemDeclsIndex.cend()) - return getValidNewCollectionName(name, index+1); + return getValidNewCollectionName(name, index + 1); return name; } @@ -458,7 +460,19 @@ void CollectionSystemManager::setEditMode(std::string collectionName, bool quiet mEditingCollectionSystemData = sysData; if (!quiet) { - GuiInfoPopup* s = new GuiInfoPopup(mWindow, "Editing the '" + Utils::String::toUpper(collectionName) + "' Collection. Add/remove games with Y.", 8000); + std::string colUpper = Utils::String::toUpper(collectionName); + + std::string editingBase = LocaleES::get("EDITING_COLLECTION"); + if (editingBase == "EDITING_COLLECTION") + editingBase = "Editing the collection"; + + std::string addRemove = LocaleES::get("ADD_REMOVE_GAMES_Y"); + if (addRemove == "ADD_REMOVE_GAMES_Y") + addRemove = "Add/remove games with Y."; + + std::string msg = editingBase + " '" + colUpper + "'. " + addRemove; + + GuiInfoPopup* s = new GuiInfoPopup(mWindow, msg, 8000); mWindow->setInfoPopup(s); } } @@ -466,7 +480,12 @@ void CollectionSystemManager::setEditMode(std::string collectionName, bool quiet void CollectionSystemManager::exitEditMode(bool quiet) { if (!quiet) { - GuiInfoPopup* s = new GuiInfoPopup(mWindow, "Finished editing the '" + Utils::String::toUpper(mEditingCollection) + "' Collection.", 4000); + std::string finishedBase = LocaleES::get("FINISHED_EDITING_COLLECTION"); + if (finishedBase == "FINISHED_EDITING_COLLECTION") + finishedBase = "Finished editing the collection"; + + std::string msg = finishedBase + " '" + Utils::String::toUpper(mEditingCollection) + "'."; + GuiInfoPopup* s = new GuiInfoPopup(mWindow, msg, 4000); mWindow->setInfoPopup(s); } if (mIsEditingCustom) { @@ -481,7 +500,8 @@ int CollectionSystemManager::getPressCountInDuration() { Uint32 now = SDL_GetTicks(); if (now - mFirstPressMs < DOUBLE_PRESS_DETECTION_DURATION) { return 2; - } else { + } + else { mFirstPressMs = now; return 1; } @@ -523,7 +543,7 @@ bool CollectionSystemManager::toggleGameInCollection(FileData* file) // remove from index fileIndex->removeFromIndex(collectionEntry); // remove from bundle index as well, if needed - if(systemViewToUpdate != sysData) + if (systemViewToUpdate != sysData) { systemViewToUpdate->getIndex()->removeFromIndex(collectionEntry); } @@ -543,7 +563,7 @@ bool CollectionSystemManager::toggleGameInCollection(FileData* file) rootFolder->sort(getSortTypeFromString(mEditingCollectionSystemData->decl.defaultSort)); ViewController::get()->onFileChanged(systemViewToUpdate->getRootFolder(), FILE_SORTED); // add to bundle index as well, if needed - if(systemViewToUpdate != sysData) + if (systemViewToUpdate != sysData) { systemViewToUpdate->getIndex()->addToIndex(newGame); } @@ -574,13 +594,40 @@ bool CollectionSystemManager::toggleGameInCollection(FileData* file) refreshCollectionSystems(file->getSourceFileData()); } + + std::string sysNameUpper = Utils::String::toUpper(sysName); + if (adding) { - s = new GuiInfoPopup(mWindow, "Added '" + Utils::String::removeParenthesis(name) + "' to '" + Utils::String::toUpper(sysName) + "'", 4000); + std::string addedBase = LocaleES::get("ADDED_TO_COLLECTION"); + if (addedBase == "ADDED_TO_COLLECTION") + addedBase = "Added"; + + std::string toWord = LocaleES::get("TO_COLLECTION"); + if (toWord == "TO_COLLECTION") + toWord = "to"; + + s = new GuiInfoPopup( + mWindow, + addedBase + " '" + Utils::String::removeParenthesis(name) + "' " + toWord + " '" + sysNameUpper + "'", + 4000 + ); } else { - s = new GuiInfoPopup(mWindow, "Removed '" + Utils::String::removeParenthesis(name) + "' from '" + Utils::String::toUpper(sysName) + "'", 4000); + std::string removedBase = LocaleES::get("REMOVED_FROM_COLLECTION"); + if (removedBase == "REMOVED_FROM_COLLECTION") + removedBase = "Removed"; + + std::string fromWord = LocaleES::get("FROM_COLLECTION"); + if (fromWord == "FROM_COLLECTION") + fromWord = "from"; + + s = new GuiInfoPopup( + mWindow, + removedBase + " '" + Utils::String::removeParenthesis(name) + "' " + fromWord + " '" + sysNameUpper + "'", + 4000 + ); } mWindow->setInfoPopup(s); @@ -638,7 +685,7 @@ void CollectionSystemManager::recreateCollection(SystemData* sysData) SystemData* systemViewToUpdate = getSystemToView(sysData); // while there are games there, remove them from the view and system - while(rootFolder->getChildrenByFilename().size() > 0) + while (rootFolder->getChildrenByFilename().size() > 0) ViewController::get()->getGameListView(systemViewToUpdate).get()->remove(rootFolder->getChildrenByFilename().begin()->second, false, false); colSysData->isPopulated = false; @@ -661,7 +708,7 @@ void CollectionSystemManager::recreateCollection(SystemData* sysData) // loads Automatic Collection systems (All, Favorites, Last Played, Random) void CollectionSystemManager::initAutoCollectionSystems() { - for(std::map::const_iterator it = mCollectionSystemDeclsIndex.cbegin() ; it != mCollectionSystemDeclsIndex.cend() ; it++ ) + for (std::map::const_iterator it = mCollectionSystemDeclsIndex.cbegin(); it != mCollectionSystemDeclsIndex.cend(); it++) { CollectionSystemDecl sysDecl = it->second; if (!sysDecl.isCustom) @@ -679,7 +726,10 @@ void CollectionSystemManager::updateCollectionFolderMetadata(SystemData* sys) { FileData* rootFolder = sys->getRootFolder(); - std::string desc = "This collection is empty."; + std::string desc = LocaleES::get("EMPTY_COLLECTION"); + if (desc == "EMPTY_COLLECTION") + desc = "This collection is empty."; + std::string rating = "0"; std::string players = "1"; std::string releasedate = "N/A"; @@ -691,11 +741,11 @@ void CollectionSystemManager::updateCollectionFolderMetadata(SystemData* sys) std::unordered_map games = rootFolder->getChildrenByFilename(); - if(games.size() > 0) + if (games.size() > 0) { std::string games_list = ""; int games_counter = 0; - for(std::unordered_map::const_iterator iter = games.cbegin(); iter != games.cend(); ++iter) + for (std::unordered_map::const_iterator iter = games.cbegin(); iter != games.cend(); ++iter) { games_counter++; FileData* file = iter->second; @@ -712,21 +762,21 @@ void CollectionSystemManager::updateCollectionFolderMetadata(SystemData* sys) developer = (developer == "None" ? new_developer : (new_developer != developer ? "Various" : new_developer)); genre = (genre == "None" ? new_genre : (new_genre != genre ? "Various" : new_genre)); - switch(games_counter) + switch (games_counter) { - case 2: - case 3: - games_list += ", "; - case 1: - games_list += "'" + file->getName() + "'"; - break; - case 4: - games_list += " among other titles."; + case 2: + case 3: + games_list += ", "; + case 1: + games_list += "'" + file->getName() + "'"; + break; + case 4: + games_list += " among other titles."; } } desc = "This collection contains " + std::to_string(games_counter) + " game" - + (games_counter == 1 ? "" : "s") + ", including " + games_list; + + (games_counter == 1 ? "" : "s") + ", including " + games_list; FileData* randomGame = sys->getRandomGame(); @@ -781,7 +831,27 @@ SystemData* CollectionSystemManager::addNewCustomCollection(std::string name, bo // creates a new, empty Collection system, based on the name and declaration SystemData* CollectionSystemManager::createNewCollectionEntry(std::string name, CollectionSystemDecl sysDecl, const CollectionFlags flags) { - SystemData* newSys = new SystemData(name, sysDecl.longName, mCollectionEnvData, sysDecl.themeFolder, true); + // longName en este punto es: + // - "ALL GAMES" + // - "LAST PLAYED" + // - "FAVORITES" + // - "RANDOM" + // - "COLLECTIONS" + std::string displayName = sysDecl.longName; + + if (!sysDecl.isCustom) + { + // Usamos la misma clave en MAYÚSCULAS para el .ini + std::string key = sysDecl.longName; + std::string translated = LocaleES::get(key); + + if (translated != key) + displayName = translated; + else + displayName = sysDecl.longName; + } + + SystemData* newSys = new SystemData(name, displayName, mCollectionEnvData, sysDecl.themeFolder, true); CollectionSystemData newCollectionData; newCollectionData.system = newSys; @@ -824,7 +894,7 @@ void CollectionSystemManager::addRandomGames(SystemData* newSys, SystemData* sou } // load exclusion collection - std::unordered_map exclusionMap; + std::unordered_map exclusionMap; std::string exclusionCollection = Settings::getInstance()->getString("RandomCollectionExclusionCollection"); auto sysDataIt = mCustomCollectionSystemsData.find(exclusionCollection); @@ -850,7 +920,7 @@ void CollectionSystemManager::addRandomGames(SystemData* newSys, SystemData* sou FileData* randomGame = sourceSystem->getRandomGame()->getSourceFileData(); CollectionFileData* newGame = NULL; - if(exclusionMap.find(randomGame->getFullPath()) == exclusionMap.end()) + if (exclusionMap.find(randomGame->getFullPath()) == exclusionMap.end()) { // Not in the exclusion collection newGame = new CollectionFileData(randomGame, newSys); @@ -889,7 +959,7 @@ void CollectionSystemManager::populateRandomCollectionFromCollections(std::mapgetIndex(); // iterate the auto collections map - for(auto &c : mAutoCollectionSystemsData) + for (auto& c : mAutoCollectionSystemsData) { CollectionSystemData csd = c.second; // we can't add games from the random collection to the random collection @@ -905,7 +975,7 @@ void CollectionSystemManager::populateRandomCollectionFromCollections(std::mapisGameSystem() && !(*sysIt)->isCollection()) @@ -950,24 +1020,24 @@ void CollectionSystemManager::populateAutoCollection(CollectionSystemData* sysDa { std::vector files = (*sysIt)->getRootFolder()->getFilesRecursive(GAME); - for(auto gameIt = files.cbegin(); gameIt != files.cend(); gameIt++) + for (auto gameIt = files.cbegin(); gameIt != files.cend(); gameIt++) { bool include = includeFileInAutoCollections(*gameIt); - switch(sysDecl.type) { - case AUTO_LAST_PLAYED: - include = include && (*gameIt)->metadata.get("playcount") > "0"; - break; - case AUTO_FAVORITES: - // we may still want to add files we don't want in auto collections in "favorites" - include = (*gameIt)->metadata.get("favorite") == "true"; - break; - case AUTO_ALL_GAMES: - break; - default: - // No-op to prevent compiler warnings - // Getting here means that the file is not part of a pre-defined collection. - include = false; - break; + switch (sysDecl.type) { + case AUTO_LAST_PLAYED: + include = include && (*gameIt)->metadata.get("playcount") > "0"; + break; + case AUTO_FAVORITES: + // we may still want to add files we don't want in auto collections in "favorites" + include = (*gameIt)->metadata.get("favorite") == "true"; + break; + case AUTO_ALL_GAMES: + break; + default: + // No-op to prevent compiler warnings + // Getting here means that the file is not part of a pre-defined collection. + include = false; + break; } if (include) @@ -1008,7 +1078,7 @@ void CollectionSystemManager::populateCustomCollection(CollectionSystemData* sys CollectionSystemDecl sysDecl = sysData->decl; std::string path = getCustomCollectionConfigPath(newSys->getName()); - if(!Utils::FileSystem::exists(path)) + if (!Utils::FileSystem::exists(path)) { LOG(LogInfo) << "Couldn't find custom collection config file at " << path; return; @@ -1022,12 +1092,12 @@ void CollectionSystemManager::populateCustomCollection(CollectionSystemData* sys std::ifstream input(path); // get all files map - std::unordered_map allFilesMap = getAllGamesCollection()->getRootFolder()->getChildrenByFilename(); + std::unordered_map allFilesMap = getAllGamesCollection()->getRootFolder()->getChildrenByFilename(); // iterate list of files in config file - for(std::string gameKey; getline(input, gameKey); ) + for (std::string gameKey; getline(input, gameKey); ) { - std::unordered_map::const_iterator it = allFilesMap.find(gameKey); + std::unordered_map::const_iterator it = allFilesMap.find(gameKey); if (it != allFilesMap.cend()) { CollectionFileData* newGame = new CollectionFileData(it->second, newSys); @@ -1048,7 +1118,7 @@ void CollectionSystemManager::populateCustomCollection(CollectionSystemData* sys void CollectionSystemManager::removeCollectionsFromDisplayedSystems() { // remove all Collection Systems - for(auto sysIt = SystemData::sSystemVector.cbegin(); sysIt != SystemData::sSystemVector.cend(); ) + for (auto sysIt = SystemData::sSystemVector.cbegin(); sysIt != SystemData::sSystemVector.cend(); ) { if ((*sysIt)->isCollection()) { @@ -1066,7 +1136,7 @@ void CollectionSystemManager::removeCollectionsFromDisplayedSystems() { FileData* customRoot = mCustomCollectionsBundle->getRootFolder(); std::vector mChildren = customRoot->getChildren(); - for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++) + for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) { customRoot->removeChild(*it); } @@ -1081,24 +1151,24 @@ void CollectionSystemManager::removeCollectionsFromDisplayedSystems() void CollectionSystemManager::addEnabledCollectionsToDisplayedSystems(std::map* colSystemData, bool processRandom) { // add auto enabled ones - for(std::map::iterator it = colSystemData->begin() ; it != colSystemData->end() ; it++ ) + for (std::map::iterator it = colSystemData->begin(); it != colSystemData->end(); it++) { if ((!processRandom && it->second.decl.type != AUTO_RANDOM) || (processRandom && it->second.decl.type == AUTO_RANDOM)) { - if(it->second.isEnabled) + if (it->second.isEnabled) { // check if populated, otherwise populate if (!it->second.isPopulated) { - if(it->second.decl.isCustom) + if (it->second.decl.isCustom) populateCustomCollection(&(it->second)); else populateAutoCollection(&(it->second)); } // check if it has its own view - if(!it->second.decl.isCustom || themeFolderExists(it->first) || !Settings::getInstance()->getBool("UseCustomCollectionsSystem")) + if (!it->second.decl.isCustom || themeFolderExists(it->first) || !Settings::getInstance()->getBool("UseCustomCollectionsSystem")) { // exists theme folder, or we chose not to bundle it under the custom-collections system // so we need to create a view @@ -1121,7 +1191,7 @@ std::vector CollectionSystemManager::getSystemsFromConfig() std::vector systems; std::string path = SystemData::getConfigPath(false); - if(!Utils::FileSystem::exists(path)) + if (!Utils::FileSystem::exists(path)) { return systems; } @@ -1129,7 +1199,7 @@ std::vector CollectionSystemManager::getSystemsFromConfig() pugi::xml_document doc; pugi::xml_parse_result res = doc.load_file(path.c_str()); - if(!res) + if (!res) { return systems; } @@ -1137,12 +1207,12 @@ std::vector CollectionSystemManager::getSystemsFromConfig() //actually read the file pugi::xml_node systemList = doc.child("systemList"); - if(!systemList) + if (!systemList) { return systems; } - for(pugi::xml_node system = systemList.child("system"); system; system = system.next_sibling("system")) + for (pugi::xml_node system = systemList.child("system"); system; system = system.next_sibling("system")) { // theme folder std::string themeFolder = system.child("theme").text().get(); @@ -1158,14 +1228,14 @@ std::vector CollectionSystemManager::getSystemsFromTheme() std::vector systems; auto themeSets = ThemeData::getThemeSets(); - if(themeSets.empty()) + if (themeSets.empty()) { // no theme sets available return systems; } std::map::const_iterator set = themeSets.find(Settings::getInstance()->getString("ThemeSet")); - if(set == themeSets.cend()) + if (set == themeSets.cend()) { // currently selected theme set is missing, so just pick the first available set set = themeSets.cbegin(); @@ -1184,9 +1254,9 @@ std::vector CollectionSystemManager::getSystemsFromTheme() { //... here you have a directory std::string folder = *it; - folder = folder.substr(themePath.size()+1); + folder = folder.substr(themePath.size() + 1); - if(Utils::FileSystem::exists(set->second.getThemePath(folder))) + if (Utils::FileSystem::exists(set->second.getThemePath(folder))) { systems.push_back(folder); } @@ -1215,7 +1285,7 @@ std::vector CollectionSystemManager::getUnusedSystemsFromTheme() systemsInUse.insert(systemsInUse.cend(), customSys.cbegin(), customSys.cend()); systemsInUse.insert(systemsInUse.cend(), userSys.cbegin(), userSys.cend()); - for(auto sysIt = themeSys.cbegin(); sysIt != themeSys.cend(); ) + for (auto sysIt = themeSys.cbegin(); sysIt != themeSys.cend(); ) { if (std::find(systemsInUse.cbegin(), systemsInUse.cend(), *sysIt) != systemsInUse.cend()) { @@ -1248,7 +1318,7 @@ std::vector CollectionSystemManager::getCollectionsFromConfigFolder // need to confirm filename matches config format if (filename != "custom-.cfg" && Utils::String::startsWith(filename, "custom-") && Utils::String::endsWith(filename, ".cfg")) { - filename = filename.substr(7, filename.size()-11); + filename = filename.substr(7, filename.size() - 11); systems.push_back(filename); } else @@ -1265,7 +1335,7 @@ std::vector CollectionSystemManager::getCollectionsFromConfigFolder std::vector CollectionSystemManager::getCollectionThemeFolders(bool custom) { std::vector systems; - for(std::map::const_iterator it = mCollectionSystemDeclsIndex.cbegin() ; it != mCollectionSystemDeclsIndex.cend() ; it++ ) + for (std::map::const_iterator it = mCollectionSystemDeclsIndex.cbegin(); it != mCollectionSystemDeclsIndex.cend(); it++) { CollectionSystemDecl sysDecl = it->second; if (sysDecl.isCustom == custom) @@ -1280,7 +1350,7 @@ std::vector CollectionSystemManager::getCollectionThemeFolders(bool std::vector CollectionSystemManager::getUserCollectionThemeFolders() { std::vector systems; - for(std::map::const_iterator it = mCustomCollectionSystemsData.cbegin() ; it != mCustomCollectionSystemsData.cend() ; it++ ) + for (std::map::const_iterator it = mCustomCollectionSystemsData.cbegin(); it != mCustomCollectionSystemsData.cend(); it++) { systems.push_back(it->second.decl.themeFolder); } @@ -1306,8 +1376,14 @@ bool CollectionSystemManager::includeFileInAutoCollections(FileData* file) bool CollectionSystemManager::needDoublePress(int presscount) { if (Settings::getInstance()->getBool("DoublePressRemovesFromFavs") && presscount < 2) { - GuiInfoPopup* toast = new GuiInfoPopup(mWindow, "Press again to remove from '" + Utils::String::toUpper(mEditingCollection) - + "'", DOUBLE_PRESS_DETECTION_DURATION, 100, 200); + std::string base = LocaleES::get("PRESS_AGAIN_TO_REMOVE"); + if (base == "PRESS_AGAIN_TO_REMOVE") + base = "Press again to remove from"; + + std::string msg = base + " '" + Utils::String::toUpper(mEditingCollection) + "'"; + + GuiInfoPopup* toast = new GuiInfoPopup(mWindow, msg, + DOUBLE_PRESS_DETECTION_DURATION, 100, 200); mWindow->setInfoPopup(toast); return true; } diff --git a/es-app/src/FileSorts.cpp b/es-app/src/FileSorts.cpp index 7f9faa68e8..fd0f9d54cc 100644 --- a/es-app/src/FileSorts.cpp +++ b/es-app/src/FileSorts.cpp @@ -3,40 +3,51 @@ #include "utils/StringUtil.h" #include "Settings.h" #include "Log.h" +#include "LocaleESHook.h" // 🔹 para es_translate() namespace FileSorts { const FileData::SortType typesArr[] = { - FileData::SortType(&compareName, true, "name, ascending"), - FileData::SortType(&compareName, false, "name, descending"), + // Nombre + FileData::SortType(&compareName, true, es_translate("name, ascending")), + FileData::SortType(&compareName, false, es_translate("name, descending")), - FileData::SortType(&compareRating, true, "rating, ascending"), - FileData::SortType(&compareRating, false, "rating, descending"), + // Rating + FileData::SortType(&compareRating, true, es_translate("rating, ascending")), + FileData::SortType(&compareRating, false, es_translate("rating, descending")), - FileData::SortType(&compareTimesPlayed, true, "times played, ascending"), - FileData::SortType(&compareTimesPlayed, false, "times played, descending"), + // Veces jugado + FileData::SortType(&compareTimesPlayed, true, es_translate("times played, ascending")), + FileData::SortType(&compareTimesPlayed, false, es_translate("times played, descending")), - FileData::SortType(&compareLastPlayed, true, "last played, ascending"), - FileData::SortType(&compareLastPlayed, false, "last played, descending"), + // Última vez jugado + FileData::SortType(&compareLastPlayed, true, es_translate("last played, ascending")), + FileData::SortType(&compareLastPlayed, false, es_translate("last played, descending")), - FileData::SortType(&compareNumPlayers, true, "number players, ascending"), - FileData::SortType(&compareNumPlayers, false, "number players, descending"), + // Jugadores + FileData::SortType(&compareNumPlayers, true, es_translate("number players, ascending")), + FileData::SortType(&compareNumPlayers, false, es_translate("number players, descending")), - FileData::SortType(&compareReleaseDate, true, "release date, ascending"), - FileData::SortType(&compareReleaseDate, false, "release date, descending"), + // Fecha de lanzamiento + FileData::SortType(&compareReleaseDate, true, es_translate("release date, ascending")), + FileData::SortType(&compareReleaseDate, false, es_translate("release date, descending")), - FileData::SortType(&compareGenre, true, "genre, ascending"), - FileData::SortType(&compareGenre, false, "genre, descending"), + // Género + FileData::SortType(&compareGenre, true, es_translate("genre, ascending")), + FileData::SortType(&compareGenre, false, es_translate("genre, descending")), - FileData::SortType(&compareDeveloper, true, "developer, ascending"), - FileData::SortType(&compareDeveloper, false, "developer, descending"), + // Desarrollador + FileData::SortType(&compareDeveloper, true, es_translate("developer, ascending")), + FileData::SortType(&compareDeveloper, false, es_translate("developer, descending")), - FileData::SortType(&comparePublisher, true, "publisher, ascending"), - FileData::SortType(&comparePublisher, false, "publisher, descending"), + // Distribuidor + FileData::SortType(&comparePublisher, true, es_translate("publisher, ascending")), + FileData::SortType(&comparePublisher, false, es_translate("publisher, descending")), - FileData::SortType(&compareSystem, true, "system, ascending"), - FileData::SortType(&compareSystem, false, "system, descending") + // Sistema + FileData::SortType(&compareSystem, true, es_translate("system, ascending")), + FileData::SortType(&compareSystem, false, es_translate("system, descending")) }; const std::vector SortTypes(typesArr, typesArr + sizeof(typesArr)/sizeof(typesArr[0])); @@ -123,7 +134,7 @@ namespace FileSorts } //If option is enabled, ignore leading articles by temporarily modifying the name prior to sorting - //(Artciles are defined within the settings config file) + //(Articles are defined within the settings config file) void ignoreLeadingArticles(std::string &name1, std::string &name2) { if (Settings::getInstance()->getBool("IgnoreLeadingArticles")) @@ -133,7 +144,6 @@ namespace FileSorts for(Utils::String::stringVector::iterator it = articles.begin(); it != articles.end(); it++) { - if (Utils::String::startsWith(Utils::String::toUpper(name1), Utils::String::toUpper(it[0]) + " ")) { name1 = Utils::String::replace(Utils::String::toUpper(name1), Utils::String::toUpper(it[0]) + " ", ""); } @@ -141,11 +151,8 @@ namespace FileSorts if (Utils::String::startsWith(Utils::String::toUpper(name2), Utils::String::toUpper(it[0]) + " ")) { name2 = Utils::String::replace(Utils::String::toUpper(name2), Utils::String::toUpper(it[0]) + " ", ""); } - } - } - } }; diff --git a/es-app/src/LocaleES.cpp b/es-app/src/LocaleES.cpp new file mode 100644 index 0000000000..3880ce9011 --- /dev/null +++ b/es-app/src/LocaleES.cpp @@ -0,0 +1,136 @@ +#include "LocaleES.h" +#include "Settings.h" +#include "utils/FileSystemUtil.h" +#include "Log.h" + +#include +#include +#include + +// -------------------- +// Singleton +// -------------------- +LocaleES& LocaleES::getInstance() +{ + static LocaleES instance; + return instance; +} + +LocaleES::LocaleES() + : mCurrentLanguage("en") +{ +} + +// -------------------- +// Lectura de archivos .ini +// -------------------- +bool LocaleES::loadLanguageFile(const std::string& filePath) +{ + std::ifstream in(filePath); + if (!in.is_open()) + { + LOG(LogWarning) << "LocaleES: could not open language file: " << filePath; + return false; + } + + mTranslations.clear(); + + std::string line; + + auto trim = [](std::string& s) + { + // recorta espacios al inicio y al final + size_t start = 0; + while (start < s.size() && (s[start] == ' ' || s[start] == '\t' || s[start] == '\r' || s[start] == '\n')) + ++start; + + size_t end = s.size(); + while (end > start && (s[end - 1] == ' ' || s[end - 1] == '\t' || s[end - 1] == '\r' || s[end - 1] == '\n')) + --end; + + s = s.substr(start, end - start); + }; + + while (std::getline(in, line)) + { + trim(line); + + // Saltar líneas vacías, comentarios o secciones [XXX] + if (line.empty() || line[0] == '#' || line[0] == ';' || line[0] == '[') + continue; + + auto pos = line.find('='); + if (pos == std::string::npos) + continue; + + std::string key = line.substr(0, pos); + std::string value = line.substr(pos + 1); + + trim(key); + trim(value); + + if (!key.empty()) + mTranslations[key] = value; + } + + LOG(LogInfo) << "LocaleES: loaded " << mTranslations.size() + << " entries from " << filePath; + return true; +} + +// -------------------- +// Cargar según Settings +// -------------------- +void LocaleES::loadFromSettings() +{ + std::string lang = Settings::getInstance()->getString("Language"); + if (lang.empty()) + lang = "en"; + + // Si ya está cargado este idioma y hay traducciones, no recargar + if (lang == mCurrentLanguage && !mTranslations.empty()) + return; + + mCurrentLanguage = lang; + mTranslations.clear(); + + // Ruta en el home del usuario: ~/.emulationstation/lang/.ini + std::string home = Utils::FileSystem::getHomePath(); + std::string userPath = home + "/.emulationstation/lang/" + lang + ".ini"; + + // Ruta opcional junto al ejecutable: /lang/.ini + std::string exePath = Utils::FileSystem::getExePath(); + std::string appPath = exePath + "/lang/" + lang + ".ini"; + + if (!loadLanguageFile(userPath)) + { + if (!loadLanguageFile(appPath)) + { + LOG(LogWarning) << "LocaleES: no language file found for '" << lang << "'"; + } + } +} + +// -------------------- +// Traducciones +// -------------------- +std::string LocaleES::translate(const std::string& key) const +{ + auto it = mTranslations.find(key); + if (it != mTranslations.end()) + return it->second; + + // Si no hay traducción, devolvemos la clave tal cual + return key; +} + +std::string LocaleES::get(const std::string& key) +{ + return getInstance().translate(key); +} + +// Función global para usar desde es-core +std::string es_translate(const std::string& key) +{ + return LocaleES::get(key); +} diff --git a/es-app/src/LocaleES.h b/es-app/src/LocaleES.h new file mode 100644 index 0000000000..2e8cadb040 --- /dev/null +++ b/es-app/src/LocaleES.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +class LocaleES +{ +public: + // Singleton + static LocaleES& getInstance(); + + // Carga archivo .ini desde una ruta + bool loadLanguageFile(const std::string& filePath); + + // Carga el idioma según Settings ("Language") + void loadFromSettings(); + + // Traduce una clave usando la instancia global + static std::string get(const std::string& key); + + // Traduce una clave usando esta instancia + std::string translate(const std::string& key) const; + +private: + LocaleES(); // constructor privado (singleton) + + // Evitar copias + LocaleES(const LocaleES&) = delete; + LocaleES& operator=(const LocaleES&) = delete; + + std::string mCurrentLanguage; + std::map mTranslations; +}; + +// Función accesible desde cualquier parte (incluido es-core) +std::string es_translate(const std::string& key); diff --git a/es-app/src/LocaleESHook.cpp b/es-app/src/LocaleESHook.cpp new file mode 100644 index 0000000000..8bea23b202 --- /dev/null +++ b/es-app/src/LocaleESHook.cpp @@ -0,0 +1,9 @@ +#include "LocaleES.h" + +// Ruta relativa: ajusta si tu estructura es distinta +#include "../es-core/src/LocaleESHook.h" + +std::string es_translate(const std::string& key) +{ + return LocaleES::getInstance().translate(key); +} diff --git a/es-app/src/MetaData.cpp b/es-app/src/MetaData.cpp index d163d85f0c..d85ea940f3 100644 --- a/es-app/src/MetaData.cpp +++ b/es-app/src/MetaData.cpp @@ -6,25 +6,25 @@ #include MetaDataDecl gameDecls[] = { - // key, type, default, statistic, name in GuiMetaDataEd, prompt in GuiMetaDataEd - {"name", MD_STRING, "", false, "name", "enter game name"}, - {"sortname", MD_STRING, "", false, "sortname", "enter game sort name"}, - {"desc", MD_MULTILINE_STRING, "", false, "description", "enter description"}, - {"image", MD_PATH, "", false, "image", "enter path to image"}, - {"video", MD_PATH , "", false, "video", "enter path to video"}, - {"marquee", MD_PATH, "", false, "marquee", "enter path to marquee"}, - {"thumbnail", MD_PATH, "", false, "thumbnail", "enter path to thumbnail"}, - {"rating", MD_RATING, "0", false, "rating", "enter rating"}, - {"releasedate", MD_DATE, "not-a-date-time", false, "release date", "enter release date"}, - {"developer", MD_STRING, "unknown", false, "developer", "enter game developer"}, - {"publisher", MD_STRING, "unknown", false, "publisher", "enter game publisher"}, - {"genre", MD_STRING, "unknown", false, "genre", "enter game genre"}, - {"players", MD_INT, "1", false, "players", "enter number of players"}, - {"favorite", MD_BOOL, "false", false, "favorite", "enter favorite off/on"}, - {"hidden", MD_BOOL, "false", false, "hidden", "enter hidden off/on" }, - {"kidgame", MD_BOOL, "false", false, "kidgame", "enter kidgame off/on" }, - {"playcount", MD_INT, "0", true, "play count", "enter number of times played"}, - {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date"} + // key, type, default, statistic, name in GuiMetaDataEd, prompt in GuiMetaDataEd + {"name", MD_STRING, "", false, "NAME", "ENTER GAME NAME"}, + {"sortname", MD_STRING, "", false, "SORTNAME", "ENTER GAME SORT NAME"}, + {"desc", MD_MULTILINE_STRING, "", false, "DESCRIPTION", "ENTER DESCRIPTION"}, + {"image", MD_PATH, "", false, "IMAGE", "ENTER PATH TO IMAGE"}, + {"video", MD_PATH, "", false, "VIDEO", "ENTER PATH TO VIDEO"}, + {"marquee", MD_PATH, "", false, "MARQUEE", "ENTER PATH TO MARQUEE"}, + {"thumbnail", MD_PATH, "", false, "THUMBNAIL", "ENTER PATH TO THUMBNAIL"}, + {"rating", MD_RATING, "0", false, "RATING", "ENTER RATING"}, + {"releasedate", MD_DATE, "not-a-date-time", false, "RELEASE DATE", "ENTER RELEASE DATE"}, + {"developer", MD_STRING, "unknown", false, "DEVELOPER", "ENTER GAME DEVELOPER"}, + {"publisher", MD_STRING, "unknown", false, "PUBLISHER", "ENTER GAME PUBLISHER"}, + {"genre", MD_STRING, "unknown", false, "GENRE", "ENTER GAME GENRE"}, + {"players", MD_INT, "1", false, "PLAYERS", "ENTER NUMBER OF PLAYERS"}, + {"favorite", MD_BOOL, "false", false, "FAVORITE", "ENTER FAVORITE OFF/ON"}, + {"hidden", MD_BOOL, "false", false, "HIDDEN", "ENTER HIDDEN OFF/ON"}, + {"kidgame", MD_BOOL, "false", false, "KIDGAME", "ENTER KIDGAME OFF/ON"}, + {"playcount", MD_INT, "0", true, "PLAY COUNT", "ENTER NUMBER OF TIMES PLAYED"}, + {"lastplayed", MD_TIME, "0", true, "LAST PLAYED", "ENTER LAST PLAYED DATE"} }; const std::vector gameMDD(gameDecls, gameDecls + sizeof(gameDecls) / sizeof(gameDecls[0])); @@ -35,19 +35,19 @@ const inline std::string blankDate() { } MetaDataDecl folderDecls[] = { - {"name", MD_STRING, "", false, "name", "enter game name"}, - {"sortname", MD_STRING, "", false, "sortname", "enter game sort name"}, - {"desc", MD_MULTILINE_STRING, "", false, "description", "enter description"}, - {"image", MD_PATH, "", false, "image", "enter path to image"}, - {"thumbnail", MD_PATH, "", false, "thumbnail", "enter path to thumbnail"}, - {"video", MD_PATH, "", false, "video", "enter path to video"}, - {"marquee", MD_PATH, "", false, "marquee", "enter path to marquee"}, - {"rating", MD_RATING, "0", false, "rating", "enter rating"}, - {"releasedate", MD_DATE, blankDate(), true, "release date", "enter release date"}, - {"developer", MD_STRING, "", false, "developer", "enter game developer"}, - {"publisher", MD_STRING, "", false, "publisher", "enter game publisher"}, - {"genre", MD_STRING, "", false, "genre", "enter game genre"}, - {"players", MD_INT, "", false, "players", "enter number of players"} + {"name", MD_STRING, "", false, "NAME", "ENTER GAME NAME"}, + {"sortname", MD_STRING, "", false, "SORTNAME", "ENTER GAME SORT NAME"}, + {"desc", MD_MULTILINE_STRING, "", false, "DESCRIPTION", "ENTER DESCRIPTION"}, + {"image", MD_PATH, "", false, "IMAGE", "ENTER PATH TO IMAGE"}, + {"thumbnail", MD_PATH, "", false, "THUMBNAIL", "ENTER PATH TO THUMBNAIL"}, + {"video", MD_PATH, "", false, "VIDEO", "ENTER PATH TO VIDEO"}, + {"marquee", MD_PATH, "", false, "MARQUEE", "ENTER PATH TO MARQUEE"}, + {"rating", MD_RATING, "0", false, "RATING", "ENTER RATING"}, + {"releasedate", MD_DATE, blankDate(), true, "RELEASE DATE", "ENTER RELEASE DATE"}, + {"developer", MD_STRING, "", false, "DEVELOPER", "ENTER GAME DEVELOPER"}, + {"publisher", MD_STRING, "", false, "PUBLISHER", "ENTER GAME PUBLISHER"}, + {"genre", MD_STRING, "", false, "GENRE", "ENTER GAME GENRE"}, + {"players", MD_INT, "", false, "PLAYERS", "ENTER NUMBER OF PLAYERS"} }; const std::vector folderMDD(folderDecls, folderDecls + sizeof(folderDecls) / sizeof(folderDecls[0])); @@ -134,7 +134,14 @@ void MetaDataList::set(const std::string& key, const std::string& value) const std::string& MetaDataList::get(const std::string& key) const { - return mMap.at(key); + // FIX: avoid std::out_of_range when key does not exist (e.g. "boxart") + auto it = mMap.find(key); + if (it == mMap.end()) + { + static const std::string empty; + return empty; + } + return it->second; } int MetaDataList::getInt(const std::string& key) const diff --git a/es-app/src/NavigationSounds.h b/es-app/src/NavigationSounds.h new file mode 100644 index 0000000000..4ad920f6d5 --- /dev/null +++ b/es-app/src/NavigationSounds.h @@ -0,0 +1,70 @@ +#pragma once +#ifndef ES_APP_NAVIGATION_SOUNDS_H +#define ES_APP_NAVIGATION_SOUNDS_H + +#include +#include +#include + +#include "ThemeData.h" +#include "Sound.h" + +// Helper simple para obtener sonidos de navegación por nombre lógico. +// Ejemplos de logicalName: "scroll", "back", "select", "favorite", "launch", +// "systembrowse", "quicksysselect". +class NavigationSounds +{ +public: + static std::shared_ptr getFromTheme( + const std::shared_ptr& theme, + const std::string& logicalName) + { + if (!theme) + return nullptr; + + // 1) Nombres candidatos: primero el nombre lógico + std::vector elementNames; + elementNames.push_back(logicalName); + + // 2) Alias de compatibilidad (para temas existentes) + if (logicalName == "scroll") + { + // nombres usados en algunos temas + elementNames.push_back("scrollSound"); + elementNames.push_back("scrollSoundList"); + elementNames.push_back("scrollSoundDetailed"); + } + else if (logicalName == "select") + { + elementNames.push_back("selectSound"); + } + else if (logicalName == "back") + { + elementNames.push_back("backSound"); + } + + // 3) Vistas donde buscamos primero + // "all" → para cuando hagamos + static const char* viewsToCheck[] = { "all", "system", "gamelist", "menu" }; + + for (const char* viewName : viewsToCheck) + { + for (const auto& elemName : elementNames) + { + const ThemeData::ThemeElement* elem = + theme->getElement(viewName, elemName, "sound"); + + if (elem && elem->has("path")) + { + std::string path = elem->get("path"); + if (!path.empty()) + return Sound::get(path); + } + } + } + + return nullptr; + } +}; + +#endif // ES_APP_NAVIGATION_SOUNDS_H diff --git a/es-app/src/Paths.h b/es-app/src/Paths.h new file mode 100644 index 0000000000..afb56a7218 --- /dev/null +++ b/es-app/src/Paths.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include "utils/FileSystemUtil.h" + +namespace Paths +{ + // ~/.emulationstation + inline std::string getUserEmulationStationPath() + { + return Utils::FileSystem::getHomePath() + "/.emulationstation"; + } + + // ~/.emulationstation/music + inline std::string getUserMusicPath() + { + return getUserEmulationStationPath() + "/music"; + } + + // ~/RetroPie/music + inline std::string getMusicPath() + { + return Utils::FileSystem::getHomePath() + "/RetroPie/music"; + } +} diff --git a/es-app/src/SystemData.cpp b/es-app/src/SystemData.cpp index 49f58e1c8f..3bc2e6ab7e 100644 --- a/es-app/src/SystemData.cpp +++ b/es-app/src/SystemData.cpp @@ -22,6 +22,34 @@ std::vector SystemData::sSystemVector; std::vector SystemData::sSystemVectorShuffled; std::ranlux48 SystemData::sURNG = std::ranlux48(std::random_device()()); +namespace +{ + // Trunca el nombre a maxLen caracteres, agregando "..." si hace falta + std::string truncateMostPlayedName(const std::string& name, size_t maxLen) + { + if (name.size() <= maxLen) + return name; + + if (maxLen <= 3) + return name.substr(0, maxLen); + + return name.substr(0, maxLen - 3) + "..."; + } + + static std::string expandHomeTilde(const std::string& p) + { + if (p.empty()) return p; + if (p[0] != '~') return p; + if (p.size() == 1) return Utils::FileSystem::getHomePath(); + if (p[1] == '/') return Utils::FileSystem::getHomePath() + p.substr(1); + return p; + } + + static bool fileExistsAny(const std::string& p) + { + return (!p.empty() && Utils::FileSystem::exists(p)); + } +} SystemData::SystemData(const std::string& name, const std::string& fullName, SystemEnvironmentData* envData, const std::string& themeFolder, bool CollectionSystem) : mName(name), mFullName(fullName), mEnvData(envData), mThemeFolder(themeFolder), mIsCollectionSystem(CollectionSystem), mIsGameSystem(true) @@ -172,7 +200,6 @@ std::vector readList(const std::string& str, const char* delims = " return ret; } - SystemData* SystemData::loadSystem(pugi::xml_node system) { std::string name, fullname, path, cmd, themeFolder, defaultCore; @@ -357,7 +384,7 @@ bool SystemData::loadConfig(Window* window) pThreadPool->wait([window, &processedSystem, systemCount, &systemsNames] { int px = processedSystem - 1; - if (px >= 0 && px < systemsNames.size()) + if (px >= 0 && px < (int)systemsNames.size()) window->renderLoadingScreen(systemsNames.at(px), (float)px / (float)(systemCount + 1)); }, 10); } @@ -375,14 +402,14 @@ bool SystemData::loadConfig(Window* window) delete pThreadPool; if (window != NULL) - window->renderLoadingScreen("Favorites", systemCount == 0 ? 0 : currentSystem / systemCount); + window->renderLoadingScreen("Favorites", systemCount == 0 ? 0 : (float)currentSystem / (float)systemCount); CollectionSystemManager::get()->updateSystemsList(); } else { if (window != NULL) - window->renderLoadingScreen("Favorites", systemCount == 0 ? 0 : currentSystem / systemCount); + window->renderLoadingScreen("Favorites", systemCount == 0 ? 0 : (float)currentSystem / (float)systemCount); CollectionSystemManager::get()->loadCollectionSystems(); } @@ -410,7 +437,7 @@ void SystemData::writeExampleConfig(const std::string& path) " \n" " ~/roms/nes\n" "\n" - " \n" " .nes .NES\n" "\n" @@ -470,7 +497,6 @@ SystemData* SystemData::getNext() const if (it == sSystemVector.cend()) it = sSystemVector.cbegin(); } while (!(*it)->isVisible()); - // as we are starting in a valid gamelistview, this will always succeed, even if we have to come full circle. return *it; } @@ -484,7 +510,6 @@ SystemData* SystemData::getPrev() const if (it == sSystemVector.crend()) it = sSystemVector.crbegin(); } while (!(*it)->isVisible()); - // as we are starting in a valid gamelistview, this will always succeed, even if we have to come full circle. return *it; } @@ -498,7 +523,7 @@ std::string SystemData::getGamelistPath(bool forWrite) const return filePath; filePath = Utils::FileSystem::getHomePath() + "/.emulationstation/gamelists/" + mName + "/gamelist.xml"; - if(forWrite) // make sure the directory exists if we're going to write to it, or crashes will happen + if(forWrite) Utils::FileSystem::createDirectory(Utils::FileSystem::getParent(filePath)); if(forWrite || Utils::FileSystem::exists(filePath)) return filePath; @@ -508,25 +533,16 @@ std::string SystemData::getGamelistPath(bool forWrite) const std::string SystemData::getThemePath() const { - // where we check for themes, in order: - // 1. [SYSTEM_PATH]/theme.xml - // 2. system theme from currently selected theme set [CURRENT_THEME_PATH]/[SYSTEM]/theme.xml - // 3. default system theme from currently selected theme set [CURRENT_THEME_PATH]/theme.xml - - // first, check game folder std::string localThemePath = mRootFolder->getPath() + "/theme.xml"; if(Utils::FileSystem::exists(localThemePath)) return localThemePath; - // not in game folder, try system theme in theme sets localThemePath = ThemeData::getThemeFromCurrentSet(mThemeFolder); if (Utils::FileSystem::exists(localThemePath)) return localThemePath; - // not system theme, try default system theme in theme set localThemePath = Utils::FileSystem::getParent(Utils::FileSystem::getParent(localThemePath)) + "/theme.xml"; - return localThemePath; } @@ -546,9 +562,10 @@ SystemData* SystemData::getRandomSystem() if (sSystemVectorShuffled.empty()) { - std::copy_if(sSystemVector.begin(), sSystemVector.end(), std::back_inserter(sSystemVectorShuffled), [](SystemData *sd){ return sd->isGameSystem(); }); - if (sSystemVectorShuffled.empty()) return NULL; + std::copy_if(sSystemVector.begin(), sSystemVector.end(), std::back_inserter(sSystemVectorShuffled), + [](SystemData *sd){ return sd->isGameSystem(); }); + if (sSystemVectorShuffled.empty()) return NULL; std::shuffle(sSystemVectorShuffled.begin(), sSystemVectorShuffled.end(), sURNG); } @@ -580,6 +597,179 @@ unsigned int SystemData::getDisplayedGameCount() const return (unsigned int)mRootFolder->getFilesRecursive(GAME, true).size(); } +unsigned int SystemData::getFavoriteCount() const +{ + unsigned int fav = 0; + + auto games = mRootFolder->getFilesRecursive(GAME, true); + for (auto game : games) + { + const std::string& favStr = game->metadata.get("favorite"); + if (favStr == "true" || favStr == "1") + ++fav; + } + + return fav; +} + +unsigned int SystemData::getMostPlayedCount() const +{ + unsigned int maxcount = 0; + + auto games = mRootFolder->getFilesRecursive(GAME, true); + for (auto game : games) + { + int pc = game->metadata.getInt("playcount"); + if (pc > (int)maxcount) + maxcount = (unsigned int)pc; + } + + return maxcount; +} + +std::string SystemData::getMostPlayedName() const +{ + std::string bestName; + int maxcount = -1; + + auto games = mRootFolder->getFilesRecursive(GAME, true); + for (auto game : games) + { + int pc = game->metadata.getInt("playcount"); + if (pc > maxcount) + { + maxcount = pc; + + bestName = game->metadata.get("name"); + if (bestName.empty()) + bestName = Utils::FileSystem::getStem(game->getPath()); + } + } + + // Sin juegos o nadie jugado → mostramos "N/A" + if (maxcount <= 0 || bestName.empty()) + return "N/A"; + + // Límite de 22 caracteres como acordamos + return truncateMostPlayedName(bestName, 22); +} + +std::string SystemData::getMostPlayedFull() const +{ + std::string name = getMostPlayedName(); + + if (name == "N/A") + return "N/A"; + + unsigned int count = getMostPlayedCount(); + if (count == 0) + return name; + + return name + " (" + std::to_string(count) + ")"; +} + +// ✅ NUEVO: imagen del más jugado con fallback pulido (boxart → image → thumbnail → titleshot → marquee) +std::string SystemData::getMostPlayedImage() const +{ + FileData* best = nullptr; + int maxcount = -1; + + auto games = mRootFolder->getFilesRecursive(GAME, true); + for (auto game : games) + { + int pc = game->metadata.getInt("playcount"); + if (pc > maxcount) + { + maxcount = pc; + best = game; + } + } + + if (!best || maxcount <= 0) + return ""; + + // Orden pulido (lo que acordamos): boxart → image → thumbnail → (titleshot opcional) → marquee + std::string raw = best->metadata.get("boxart"); + if (raw.empty()) raw = best->metadata.get("image"); + if (raw.empty()) raw = best->metadata.get("thumbnail"); + if (raw.empty()) raw = best->metadata.get("titleshot"); // opcional, lo dejamos porque ya lo usabas + if (raw.empty()) raw = best->metadata.get("marquee"); + if (raw.empty()) + return ""; + + raw = expandHomeTilde(raw); + + // 1) absoluta y existe + if (!raw.empty() && raw[0] == '/' && fileExistsAny(raw)) + return raw; + + // 2) relativa -> resolver contra carpeta del gamelist + { + std::string base = Utils::FileSystem::getParent(getGamelistPath(false)); + std::string candidate = raw; + + if (candidate.size() >= 2 && candidate[0] == '.' && candidate[1] == '/') + candidate = candidate.substr(2); + + candidate = Utils::FileSystem::getGenericPath(base + "/" + candidate); + if (fileExistsAny(candidate)) + return candidate; + } + + // 3) fallback por filename a tus 2 rutas + std::string filename = Utils::FileSystem::getFileName(raw); + if (filename.empty()) + return ""; + + // A) ~/.emulationstation/downloaded_images// + { + std::string p = Utils::FileSystem::getHomePath() + + "/.emulationstation/downloaded_images/" + + mName + "/" + filename; + + p = Utils::FileSystem::getGenericPath(p); + if (fileExistsAny(p)) + return p; + } + + // B) ~/RetroPie/roms//media/screenshots/ + { + std::string p = Utils::FileSystem::getHomePath() + + "/RetroPie/roms/" + + mName + "/media/screenshots/" + filename; + + p = Utils::FileSystem::getGenericPath(p); + if (fileExistsAny(p)) + return p; + } + + // 4) si no hay extensión, probar comunes + std::string ext = Utils::FileSystem::getExtension(filename); + std::string stem = Utils::FileSystem::getStem(filename); + + if (ext.empty() && !stem.empty()) + { + const char* exts[] = { ".png", ".jpg", ".jpeg", ".webp" }; + + for (auto e : exts) + { + std::string pA = Utils::FileSystem::getHomePath() + + "/.emulationstation/downloaded_images/" + mName + "/" + stem + e; + pA = Utils::FileSystem::getGenericPath(pA); + if (fileExistsAny(pA)) + return pA; + + std::string pB = Utils::FileSystem::getHomePath() + + "/RetroPie/roms/" + mName + "/media/screenshots/" + stem + e; + pB = Utils::FileSystem::getGenericPath(pB); + if (fileExistsAny(pB)) + return pB; + } + } + + return ""; +} + void SystemData::loadTheme() { mTheme = std::make_shared(); @@ -593,12 +783,24 @@ void SystemData::loadTheme() { // build map with system variables for theme to use, std::map sysData; - sysData.insert(std::pair("system.name", getName())); - sysData.insert(std::pair("system.theme", getThemeFolder())); - sysData.insert(std::pair("system.fullName", getFullName())); + sysData.insert(std::make_pair("system.name", getName())); + sysData.insert(std::make_pair("system.theme", getThemeFolder())); + sysData.insert(std::make_pair("system.fullName", getFullName())); + + // Extras para temas (contadores y "más jugado") + sysData.insert(std::make_pair("system.gameCount", std::to_string(getGameCount()))); + sysData.insert(std::make_pair("system.displayedGameCount", std::to_string(getDisplayedGameCount()))); + sysData.insert(std::make_pair("system.favoriteCount", std::to_string(getFavoriteCount()))); + sysData.insert(std::make_pair("system.mostPlayedCount", std::to_string(getMostPlayedCount()))); + sysData.insert(std::make_pair("system.mostPlayedName", getMostPlayedName())); + sysData.insert(std::make_pair("system.mostPlayedFull", getMostPlayedFull())); + + // ✅ NUEVO: ruta de imagen resuelta + sysData.insert(std::make_pair("system.mostPlayedImage", getMostPlayedImage())); mTheme->loadFile(sysData, path); - } catch(ThemeException& e) + } + catch(ThemeException& e) { LOG(LogError) << e.what(); mTheme = std::make_shared(); // reset to empty diff --git a/es-app/src/SystemData.h b/es-app/src/SystemData.h index d440239130..4276c40284 100644 --- a/es-app/src/SystemData.h +++ b/es-app/src/SystemData.h @@ -27,7 +27,11 @@ struct SystemEnvironmentData class SystemData { public: - SystemData(const std::string& name, const std::string& fullName, SystemEnvironmentData* envData, const std::string& themeFolder, bool CollectionSystem = false); + SystemData(const std::string& name, + const std::string& fullName, + SystemEnvironmentData* envData, + const std::string& themeFolder, + bool CollectionSystem = false); ~SystemData(); inline FileData* getRootFolder() const { return mRootFolder; }; @@ -38,7 +42,13 @@ class SystemData inline const std::string& getThemeFolder() const { return mThemeFolder; } inline SystemEnvironmentData* getSystemEnvData() const { return mEnvData; } inline const std::vector& getPlatformIds() const { return mEnvData->mPlatformIds; } - inline bool hasPlatformId(PlatformIds::PlatformId id) { if (!mEnvData) return false; return std::find(mEnvData->mPlatformIds.cbegin(), mEnvData->mPlatformIds.cend(), id) != mEnvData->mPlatformIds.cend(); } + inline bool hasPlatformId(PlatformIds::PlatformId id) + { + if (!mEnvData) return false; + return std::find(mEnvData->mPlatformIds.cbegin(), + mEnvData->mPlatformIds.cend(), + id) != mEnvData->mPlatformIds.cend(); + } inline const std::shared_ptr& getTheme() const { return mTheme; } @@ -49,17 +59,32 @@ class SystemData unsigned int getGameCount() const; unsigned int getDisplayedGameCount() const; + // NUEVO: contadores para favoritos y más jugados + unsigned int getFavoriteCount() const; + unsigned int getMostPlayedCount() const; + std::string getMostPlayedName() const; + std::string getMostPlayedFull() const; + + // NUEVO: imagen del juego más jugado (para usar en el tema) + std::string getMostPlayedImage() const; + static void deleteSystems(); - static bool loadConfig(Window* window); //Load the system config file at getConfigPath(). Returns true if no errors were encountered. An example will be written if the file doesn't exist. + static bool loadConfig(Window* window); static void writeExampleConfig(const std::string& path); - static std::string getConfigPath(bool forWrite); // if forWrite, will only return ~/.emulationstation/es_systems.cfg, never /etc/emulationstation/es_systems.cfg + static std::string getConfigPath(bool forWrite); static std::vector sSystemVector; static std::vector sSystemVectorShuffled; static std::ranlux48 sURNG; - inline std::vector::const_iterator getIterator() const { return std::find(sSystemVector.cbegin(), sSystemVector.cend(), this); }; - inline std::vector::const_reverse_iterator getRevIterator() const { return std::find(sSystemVector.crbegin(), sSystemVector.crend(), this); }; + inline std::vector::const_iterator getIterator() const + { + return std::find(sSystemVector.cbegin(), sSystemVector.cend(), this); + }; + inline std::vector::const_reverse_iterator getRevIterator() const + { + return std::find(sSystemVector.crbegin(), sSystemVector.crend(), this); + }; inline bool isCollection() { return mIsCollectionSystem; }; inline bool isGameSystem() { return mIsGameSystem; }; @@ -71,7 +96,6 @@ class SystemData static SystemData* getRandomSystem(); FileData* getRandomGame(); - // Load or re-load theme. void loadTheme(); FileFilterIndex* getIndex() { return mFilterIndex; }; @@ -97,7 +121,6 @@ class SystemData FileFilterIndex* mFilterIndex; FileData* mRootFolder; - // for getRandomGame() std::vector mGamesShuffled; }; diff --git a/es-app/src/ThemeOptions.cpp b/es-app/src/ThemeOptions.cpp new file mode 100644 index 0000000000..a85a877afd --- /dev/null +++ b/es-app/src/ThemeOptions.cpp @@ -0,0 +1,5 @@ +// ThemeOptions.cpp +// Archivo de compatibilidad vacío para ES-X. +// La lógica real de opciones de tema está en guis/GuiThemeOptions.* +// Este archivo se mantiene sólo porque está referenciado en CMakeLists. + diff --git a/es-app/src/ThemeOptions.h b/es-app/src/ThemeOptions.h new file mode 100644 index 0000000000..d6520d6e53 --- /dev/null +++ b/es-app/src/ThemeOptions.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include + +// Gestor simple de opciones por tema, basado en un INI: +// [Pi-Station-X] +// LAYOUT=ps4 +// AVATAR=avatar03 +// START_LABEL=INICIO +class ThemeOptions +{ +public: + // Singleton + static ThemeOptions& getInstance(); + + // Carga desde un INI. Si path == "" usa: + // ~/.emulationstation/theme-options.ini + void load(const std::string& path = ""); + + // Guarda el INI en el mismo path usado en load() + // o en el path por defecto si no se cargó ninguno. + void save(); + + // Lee una opción: sección = nombre del theme. + // Si no existe, devuelve def. + std::string get(const std::string& themeName, + const std::string& key, + const std::string& def = "") const; + + // Escribe/actualiza una opción en memoria. + // NO guarda en disco hasta que se llame a save(). + void set(const std::string& themeName, + const std::string& key, + const std::string& value); + + const std::string& getPath() const { return mPath; } + +private: + ThemeOptions(); + ThemeOptions(const ThemeOptions&) = delete; + ThemeOptions& operator=(const ThemeOptions&) = delete; + + void clear(); + void parseLine(const std::string& line, std::string& currentSection); + + std::string mPath; + // mData[section][key] = value + std::map> mData; +}; diff --git a/es-app/src/audio/BackgroundMusicManager.cpp b/es-app/src/audio/BackgroundMusicManager.cpp new file mode 100644 index 0000000000..0b09e54c87 --- /dev/null +++ b/es-app/src/audio/BackgroundMusicManager.cpp @@ -0,0 +1,497 @@ +#include "audio/BackgroundMusicManager.h" + +#include "Settings.h" +#include "Log.h" +#include "utils/FileSystemUtil.h" +#include "utils/StringUtil.h" + +#include +#include + +#include +#include +#include + +// ============================= +// Singleton +// ============================= + +BackgroundMusicManager* BackgroundMusicManager::sInstance = nullptr; + +BackgroundMusicManager& BackgroundMusicManager::getInstance() +{ + if (!sInstance) + sInstance = new BackgroundMusicManager(); + return *sInstance; +} + +// ============================= +// Ctor / Dtor +// ============================= + +BackgroundMusicManager::BackgroundMusicManager() + : mInitialized(false) + , mEnabled(true) + , mGameRunning(false) + , mMixerOpenedByUs(false) + , mCurrentIndex(-1) + , mCurrentMusic(nullptr) + , mNowPlayingText("") + , mSongNameChanged(false) + , mPendingResume(false) + , mResumeDelayMs(600) + , mResumeTimerMs(0) + , mPendingIndex(-1) + , mPendingReopenOnResume(false) + , mPendingNextFromCallback(false) +{ + mEnabled = Settings::getInstance()->getBool("BackgroundMusic"); +} + +BackgroundMusicManager::~BackgroundMusicManager() +{ + shutdown(); +} + +// ============================= +// Popup helpers +// ============================= + +bool BackgroundMusicManager::songNameChanged() +{ + return mSongNameChanged; +} + +void BackgroundMusicManager::resetSongNameChangedFlag() +{ + mSongNameChanged = false; +} + +std::string BackgroundMusicManager::getCurrentSongDisplayName() const +{ + return mNowPlayingText; +} + +void BackgroundMusicManager::setNowPlaying(const std::string& fullPath) +{ + std::string stem = Utils::FileSystem::getStem(fullPath); + if (stem.empty()) + stem = fullPath; + + mNowPlayingText = std::string("🎵 Now playing: ") + stem; + mSongNameChanged = true; +} + +// ============================= +// Mixer open/close/reopen +// ============================= + +bool BackgroundMusicManager::openMixer() +{ + if (mMixerOpenedByUs) + return true; + + if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) + { + LOG(LogError) << "BGM - Mix_OpenAudio failed: " << Mix_GetError(); + return false; + } + + Mix_AllocateChannels(16); + Mix_HaltMusic(); + Mix_HookMusicFinished(&BackgroundMusicManager::musicFinishedCallbackStatic); + + mMixerOpenedByUs = true; + LOG(LogInfo) << "BGM - Mixer OPEN"; + return true; +} + +void BackgroundMusicManager::closeMixer() +{ + if (!mMixerOpenedByUs) + return; + + mPendingResume = false; + mResumeTimerMs = 0; + mPendingIndex = -1; + mPendingReopenOnResume = false; + mPendingNextFromCallback.store(false); + + stopMusicInternal(false); + Mix_HookMusicFinished(nullptr); + Mix_HaltMusic(); + Mix_CloseAudio(); + + mMixerOpenedByUs = false; + LOG(LogInfo) << "BGM - Mixer CLOSED"; +} + +bool BackgroundMusicManager::reopenMixer() +{ + if (mMixerOpenedByUs) + { + stopMusicInternal(false); + Mix_HookMusicFinished(nullptr); + Mix_HaltMusic(); + Mix_CloseAudio(); + mMixerOpenedByUs = false; + LOG(LogInfo) << "BGM - Mixer REOPEN (close)"; + } + + return openMixer(); +} + +// ============================= +// Init / Shutdown +// ============================= + +void BackgroundMusicManager::init() +{ + if (mInitialized) + return; + + if (!openMixer()) + return; + + buildPlaylist(); + mInitialized = true; + + LOG(LogInfo) << "BGM - Initialized | playlist=" << mPlaylist.size() + << " | enabled=" << (mEnabled ? "1" : "0"); + + if (mEnabled && !mPlaylist.empty()) + playCurrent(); +} + +void BackgroundMusicManager::shutdown() +{ + closeMixer(); + + mInitialized = false; + mPlaylist.clear(); + mLastPlayed.clear(); + mCurrentIndex = -1; + + LOG(LogInfo) << "BGM - Shutdown"; +} + +// ============================= +// Enable / Disable +// ============================= + +void BackgroundMusicManager::setEnabled(bool enabled) +{ + if (enabled == mEnabled) + return; + + mEnabled = enabled; + Settings::getInstance()->setBool("BackgroundMusic", enabled); + + LOG(LogInfo) << "BGM - setEnabled(" << (enabled ? "true" : "false") << ")"; + + if (!mEnabled) + { + mPendingResume = false; + mResumeTimerMs = 0; + mPendingIndex = -1; + mPendingReopenOnResume = false; + mPendingNextFromCallback.store(false); + + stopMusicInternal(true); + return; + } + + if (!mInitialized) + { + init(); + return; + } + + if (!openMixer()) + return; + + if (mPlaylist.empty()) + buildPlaylist(); + + if (!mPlaylist.empty()) + playCurrent(); +} + +// ============================= +// Game hooks +// ============================= + +void BackgroundMusicManager::onGameLaunched() +{ + if (!mInitialized) + return; + + mGameRunning = true; + + mPendingResume = false; + mResumeTimerMs = 0; + mPendingIndex = -1; + mPendingNextFromCallback.store(false); + + LOG(LogInfo) << "BGM - onGameLaunched() -> stop music"; + stopMusicInternal(true); +} + +void BackgroundMusicManager::onGameEnded() +{ + mGameRunning = false; + + if (!mEnabled || !mInitialized) + return; + + if (!mPlaylist.empty()) + mPendingIndex = pickNextIndex(); + else + mPendingIndex = -1; + + mPendingReopenOnResume = true; + mPendingResume = true; + mResumeTimerMs = mResumeDelayMs; + + LOG(LogInfo) << "BGM - onGameEnded(): resume in " << mResumeDelayMs << "ms"; +} + +// ============================= +// Update (main thread) +// ============================= + +void BackgroundMusicManager::update(int deltaTimeMs) +{ + if (!mInitialized) + return; + + if (mPendingNextFromCallback.exchange(false)) + { + if (!mGameRunning && mEnabled) + playNext(); + } + + if (!mPendingResume) + return; + + if (mGameRunning || !mEnabled) + { + mPendingResume = false; + mResumeTimerMs = 0; + mPendingIndex = -1; + mPendingReopenOnResume = false; + return; + } + + mResumeTimerMs -= deltaTimeMs; + if (mResumeTimerMs > 0) + return; + + mPendingResume = false; + mResumeTimerMs = 0; + + if (mPlaylist.empty()) + buildPlaylist(); + + if (mPlaylist.empty()) + return; + + if (mPendingReopenOnResume) + { + mPendingReopenOnResume = false; + if (!reopenMixer()) + return; + } + else + { + if (!openMixer()) + return; + } + + if (mPendingIndex >= 0 && mPendingIndex < (int)mPlaylist.size()) + mCurrentIndex = mPendingIndex; + else + mCurrentIndex = pickNextIndex(); + + mPendingIndex = -1; + + LOG(LogInfo) << "BGM - resume -> playCurrent() index=" << mCurrentIndex; + playCurrent(); +} + +// ============================= +// Playlist +// ============================= + +void BackgroundMusicManager::buildPlaylist() +{ + mPlaylist.clear(); + mLastPlayed.clear(); + mCurrentIndex = -1; + + const std::string home = Utils::FileSystem::getHomePath(); + buildPlaylistFromPath(home + "/RetroPie/music"); + buildPlaylistFromPath(home + "/.emulationstation/music"); + + if (!mPlaylist.empty()) + { + static std::mt19937 rng{ std::random_device{}() }; + std::uniform_int_distribution dist(0, (int)mPlaylist.size() - 1); + mCurrentIndex = dist(rng); + + LOG(LogInfo) << "BGM - Playlist ready (" << mPlaylist.size() << " tracks)"; + } +} + +void BackgroundMusicManager::buildPlaylistFromPath(const std::string& path) +{ + if (!Utils::FileSystem::exists(path) || !Utils::FileSystem::isDirectory(path)) + return; + + for (const auto& e : Utils::FileSystem::getDirContent(path)) + if (!Utils::FileSystem::isDirectory(e) && isValidAudioFile(e)) + mPlaylist.push_back(e); +} + +bool BackgroundMusicManager::isValidAudioFile(const std::string& path) const +{ + const std::string ext = Utils::String::toLower(Utils::FileSystem::getExtension(path)); + return (ext == ".mp3" || ext == ".ogg" || ext == ".flac" || ext == ".wav" || ext == ".mod"); +} + +// ============================= +// Shuffle inteligente +// ============================= + +void BackgroundMusicManager::addLastPlayed(const std::string& song) +{ + int maxHistory = std::max(1, (int)std::floor(mPlaylist.size() * 0.4f)); + + mLastPlayed.push_front(song); + while ((int)mLastPlayed.size() > maxHistory) + mLastPlayed.pop_back(); +} + +bool BackgroundMusicManager::wasPlayedRecently(const std::string& song) const +{ + return std::find(mLastPlayed.begin(), mLastPlayed.end(), song) != mLastPlayed.end(); +} + +int BackgroundMusicManager::pickNextIndex() +{ + if (mPlaylist.size() <= 1) + return mCurrentIndex; + + static std::mt19937 rng{ std::random_device{}() }; + std::uniform_int_distribution dist(0, (int)mPlaylist.size() - 1); + + int tries = 0; + int idx = mCurrentIndex; + + do { + idx = dist(rng); + tries++; + } while (tries < 20 && wasPlayedRecently(mPlaylist[idx])); + + return idx; +} + +// ============================= +// Playback +// ============================= + +void BackgroundMusicManager::playCurrent() +{ + if (!mInitialized || !mEnabled || !mMixerOpenedByUs || mPlaylist.empty()) + return; + + if (Mix_PlayingMusic() && !Mix_PausedMusic()) + return; + + int attempts = 0; + int maxAttempts = std::min((int)mPlaylist.size(), 10); + + while (attempts < maxAttempts && !mPlaylist.empty()) + { + if (mCurrentIndex < 0 || mCurrentIndex >= (int)mPlaylist.size()) + mCurrentIndex = pickNextIndex(); + + const std::string song = mPlaylist[mCurrentIndex]; + + if (mCurrentMusic) + { + Mix_HaltMusic(); + Mix_FreeMusic(mCurrentMusic); + mCurrentMusic = nullptr; + } + + mCurrentMusic = Mix_LoadMUS(song.c_str()); + if (!mCurrentMusic) + { + LOG(LogError) << "BGM - Load failed, skipping: " << song + << " (" << Mix_GetError() << ")"; + + mPlaylist.erase(mPlaylist.begin() + mCurrentIndex); + mCurrentIndex = -1; + attempts++; + continue; + } + + LOG(LogInfo) << "BGM - Playing: " << song; + setNowPlaying(song); + + Mix_PlayMusic(mCurrentMusic, 0); + addLastPlayed(song); + return; + } + + LOG(LogWarning) << "BGM - No valid tracks left, stopping music"; +} + +// ============================= +// Controls +// ============================= + +void BackgroundMusicManager::playNext() +{ + if (!mInitialized || mPlaylist.empty()) + return; + + mCurrentIndex = pickNextIndex(); + playCurrent(); +} + +void BackgroundMusicManager::stopMusicInternal(bool fadeOut) +{ + if (!mMixerOpenedByUs) + return; + + if (fadeOut) + Mix_FadeOutMusic(500); + else + Mix_HaltMusic(); + + if (mCurrentMusic) + { + Mix_FreeMusic(mCurrentMusic); + mCurrentMusic = nullptr; + } +} + +// ============================= +// Callback +// ============================= + +void BackgroundMusicManager::musicFinishedCallbackStatic() +{ + if (sInstance) + sInstance->musicFinishedCallback(); +} + +void BackgroundMusicManager::musicFinishedCallback() +{ + if (mGameRunning || !mEnabled) + return; + + mPendingNextFromCallback.store(true); +} diff --git a/es-app/src/audio/BackgroundMusicManager.h b/es-app/src/audio/BackgroundMusicManager.h new file mode 100644 index 0000000000..cc967e3ac7 --- /dev/null +++ b/es-app/src/audio/BackgroundMusicManager.h @@ -0,0 +1,83 @@ +#pragma once + +#include +#include +#include +#include + +struct _Mix_Music; +typedef struct _Mix_Music Mix_Music; + +class BackgroundMusicManager +{ +public: + static BackgroundMusicManager& getInstance(); + + void init(); + void shutdown(); + + void setEnabled(bool enabled); + bool isEnabled() const { return mEnabled; } + + void onGameLaunched(); + void onGameEnded(); + + void update(int deltaTimeMs); + + void playNext(); + + bool songNameChanged(); + void resetSongNameChangedFlag(); + std::string getCurrentSongDisplayName() const; + +private: + BackgroundMusicManager(); + ~BackgroundMusicManager(); + + bool openMixer(); + void closeMixer(); + bool reopenMixer(); + + void buildPlaylist(); + void buildPlaylistFromPath(const std::string& path); + bool isValidAudioFile(const std::string& path) const; + + void addLastPlayed(const std::string& song); + bool wasPlayedRecently(const std::string& song) const; + int pickNextIndex(); + + void playCurrent(); + void stopMusicInternal(bool fadeOut); + + void setNowPlaying(const std::string& fullPath); + + static void musicFinishedCallbackStatic(); + void musicFinishedCallback(); + +private: + static BackgroundMusicManager* sInstance; + + bool mInitialized; + bool mEnabled; + bool mGameRunning; + bool mMixerOpenedByUs; + + std::vector mPlaylist; + std::deque mLastPlayed; + int mCurrentIndex; + + Mix_Music* mCurrentMusic; + + std::string mNowPlayingText; + bool mSongNameChanged; + + bool mPendingResume; + int mResumeDelayMs; + int mResumeTimerMs; + int mPendingIndex; + + bool mPendingReopenOnResume; + + // ⚠️ cruzado entre audio thread y main thread + std::atomic mPendingNextFromCallback; +}; diff --git a/es-app/src/components/TextListComponent.h b/es-app/src/components/TextListComponent.h index e5a617c54d..d5484a207c 100644 --- a/es-app/src/components/TextListComponent.h +++ b/es-app/src/components/TextListComponent.h @@ -189,7 +189,7 @@ void TextListComponent::render(const Transform4x4f& parentTrans) Renderer::pushClipRect(Vector2i((int)(trans.translation().x() + mHorizontalMargin), (int)trans.translation().y()), Vector2i((int)(dim.x() - mHorizontalMargin*2), (int)dim.y())); - for(int i = mViewportTop; i < listCutoff; i++) + for(int i = mViewportTop; i < (int)listCutoff; i++) { typename IList::Entry& entry = mEntries.at((unsigned int)i); @@ -427,14 +427,27 @@ void TextListComponent::applyTheme(const std::shared_ptr& theme, c const float selectorHeight = Math::max(mFont->getHeight(1.0), (float)mFont->getSize()) * mLineSpacing; setSelectorHeight(selectorHeight); - if (properties & SOUND) - { - // Clear any previous theme scroll sound so themes can remove the override. - mScrollSound.clear(); + // 🔊 SONIDO DE SCROLL + if(properties & SOUND) + { + // 1) Prioridad: scrollSound definido directamente en + if(elem->has("scrollSound")) + { + mScrollSound = elem->get("scrollSound"); + } + else + { + // 2) Si no hay en textlist, usar feature + // con ... + const ThemeData::ThemeElement* sndElem = + theme->getElement("all", "scroll", "sound"); - if(elem->has("scrollSound")) - mScrollSound = elem->get("scrollSound"); - } + if (sndElem && sndElem->has("path")) + { + mScrollSound = sndElem->get("path"); + } + } + } if(properties & ALIGNMENT) { diff --git a/es-app/src/guis/GuiCollectionSystemsOptions.cpp b/es-app/src/guis/GuiCollectionSystemsOptions.cpp index 1019e70780..8bf60bbe15 100644 --- a/es-app/src/guis/GuiCollectionSystemsOptions.cpp +++ b/es-app/src/guis/GuiCollectionSystemsOptions.cpp @@ -4,6 +4,8 @@ #include "components/OptionListComponent.h" #include "components/SwitchComponent.h" +#include "components/TextComponent.h" +#include "components/ImageComponent.h" #include "guis/GuiInfoPopup.h" #include "guis/GuiRandomCollectionOptions.h" #include "guis/GuiSettings.h" @@ -12,9 +14,27 @@ #include "views/ViewController.h" #include "CollectionSystemManager.h" #include "Window.h" +#include "Settings.h" +#include "renderers/Renderer.h" -GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(Window* window) : GuiComponent(window), mMenu(window, "GAME COLLECTION SETTINGS") +#include "LocaleES.h" + +// Helper local para traducciones .ini +namespace +{ + inline std::string _(const std::string& key) + { + return LocaleES::getInstance().translate(key); + } +} + +GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(Window* window) + : GuiComponent(window) + , mMenu(window, _("GAME COLLECTION SETTINGS").c_str()) { + // Asegurar que el idioma actual esté cargado + LocaleES::getInstance().loadFromSettings(); + initializeMenu(); } @@ -22,102 +42,156 @@ void GuiCollectionSystemsOptions::initializeMenu() { addChild(&mMenu); - // get collections + // obtener colecciones addSystemsToMenu(); - // manage random collection - addEntry("RANDOM GAME COLLECTION SETTINGS", 0x777777FF, true, [this] { openRandomCollectionSettings(); }); - + // opción de ajustes de colección aleatoria + addEntry(_("RANDOM GAME COLLECTION SETTINGS").c_str(), 0x777777FF, true, + [this] { openRandomCollectionSettings(); }); ComponentListRow row; - if(CollectionSystemManager::get()->isEditing()) + if (CollectionSystemManager::get()->isEditing()) { - row.addElement(std::make_shared(mWindow, "FINISH EDITING '" + Utils::String::toUpper(CollectionSystemManager::get()->getEditingCollection()) + "' COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + std::string label = + _("FINISH EDITING") + std::string(" '") + + Utils::String::toUpper(CollectionSystemManager::get()->getEditingCollection()) + + std::string("' ") + _("COLLECTION"); + + row.addElement(std::make_shared( + mWindow, + label, + Font::get(FONT_SIZE_MEDIUM), + 0x777777FF), + true); + row.makeAcceptInputHandler(std::bind(&GuiCollectionSystemsOptions::exitEditMode, this)); mMenu.addRow(row); } else { - // add "Create New Custom Collection from Theme" + // Crear nueva colección desde el tema std::vector unusedFolders = CollectionSystemManager::get()->getUnusedSystemsFromTheme(); - if (unusedFolders.size() > 0) + if (!unusedFolders.empty()) { - addEntry("CREATE NEW CUSTOM COLLECTION FROM THEME", 0x777777FF, true, - [this, unusedFolders] { - auto s = new GuiSettings(mWindow, "SELECT THEME FOLDER"); - std::shared_ptr< OptionListComponent > folderThemes = std::make_shared< OptionListComponent >(mWindow, "SELECT THEME FOLDER", true); - - // add Custom Systems - for(auto it = unusedFolders.cbegin() ; it != unusedFolders.cend() ; it++ ) - { - ComponentListRow row; - std::string name = *it; - - std::function createCollectionCall = [name, this, s] { - createCollection(name); - }; - row.makeAcceptInputHandler(createCollectionCall); - - auto themeFolder = std::make_shared(mWindow, Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF); - row.addElement(themeFolder, true); - s->addRow(row); - } - mWindow->pushGui(s); - }); + addEntry(_("CREATE NEW CUSTOM COLLECTION FROM THEME").c_str(), 0x777777FF, true, + [this, unusedFolders] { + auto s = new GuiSettings(mWindow, _("SELECT THEME FOLDER").c_str()); + std::shared_ptr< OptionListComponent > folderThemes = + std::make_shared< OptionListComponent >( + mWindow, + _("SELECT THEME FOLDER"), + true); + + // añadir sistemas del tema + for (auto it = unusedFolders.cbegin(); it != unusedFolders.cend(); ++it) + { + ComponentListRow row; + + std::string name = *it; + std::function createCollectionCall = [name, this, s] { + createCollection(name); + }; + row.makeAcceptInputHandler(createCollectionCall); + + auto themeFolder = std::make_shared( + mWindow, + Utils::String::toUpper(name), + Font::get(FONT_SIZE_SMALL), + 0x777777FF); + + row.addElement(themeFolder, true); + s->addRow(row); + } + + mWindow->pushGui(s); + }); } - row.addElement(std::make_shared(mWindow, "CREATE NEW CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + // Crear nueva colección personalizada + row.elements.clear(); + row.addElement(std::make_shared( + mWindow, + _("CREATE NEW CUSTOM COLLECTION"), + Font::get(FONT_SIZE_MEDIUM), + 0x777777FF), + true); + auto createCustomCollection = [this](const std::string& newVal) { std::string name = newVal; - // we need to store the first Gui and remove it, as it'll be deleted by the actual Gui + // hay que guardar el primer Gui y quitarlo, porque será borrado por el nuevo Gui Window* window = mWindow; GuiComponent* topGui = window->peekGui(); window->removeGui(topGui); createCollection(name); }; + row.makeAcceptInputHandler([this, createCustomCollection] { - mWindow->pushGui(new GuiTextEditPopup(mWindow, "New Collection Name", "", createCustomCollection, false)); - }); + mWindow->pushGui(new GuiTextEditPopup( + mWindow, + _("NEW COLLECTION NAME"), + "", + createCustomCollection, + false)); + }); mMenu.addRow(row); } + // agrupar colecciones sin tema bundleCustomCollections = std::make_shared(mWindow); bundleCustomCollections->setState(Settings::getInstance()->getBool("UseCustomCollectionsSystem")); - mMenu.addWithLabel("GROUP UNTHEMED CUSTOM COLLECTIONS", bundleCustomCollections); + mMenu.addWithLabel(_("GROUP UNTHEMED CUSTOM COLLECTIONS"), bundleCustomCollections); + // ordenar colecciones y sistemas sortAllSystemsSwitch = std::make_shared(mWindow); sortAllSystemsSwitch->setState(Settings::getInstance()->getBool("SortAllSystems")); - mMenu.addWithLabel("SORT CUSTOM COLLECTIONS AND SYSTEMS", sortAllSystemsSwitch); + mMenu.addWithLabel(_("SORT CUSTOM COLLECTIONS AND SYSTEMS"), sortAllSystemsSwitch); + // mostrar nombre del sistema en colecciones toggleSystemNameInCollections = std::make_shared(mWindow); toggleSystemNameInCollections->setState(Settings::getInstance()->getBool("CollectionShowSystemInfo")); - mMenu.addWithLabel("SHOW SYSTEM NAME IN COLLECTIONS", toggleSystemNameInCollections); + mMenu.addWithLabel(_("SHOW SYSTEM NAME IN COLLECTIONS"), toggleSystemNameInCollections); - // double press to remove from favorites + // doble pulsación en Y para quitar de favoritos/colección doublePressToRemoveFavs = std::make_shared(mWindow); doublePressToRemoveFavs->setState(Settings::getInstance()->getBool("DoublePressRemovesFromFavs")); - mMenu.addWithLabel("PRESS (Y) TWICE TO REMOVE FROM FAVS./COLL.", doublePressToRemoveFavs); - - - // Add option to select default collection for screensaver - defaultScreenSaverCollection = std::make_shared< OptionListComponent >(mWindow, "ADD/REMOVE GAMES WHILE SCREENSAVER TO", false); - // Add default option - std::string defaultScreenSaverCollectionName = Settings::getInstance()->getString("DefaultScreenSaverCollection"); - defaultScreenSaverCollection->add("", "", defaultScreenSaverCollectionName == ""); - - std::map customSystems = CollectionSystemManager::get()->getCustomCollectionSystems(); - // add all enabled Custom Systems - for(std::map::const_iterator it = customSystems.cbegin() ; it != customSystems.cend() ; it++ ) + mMenu.addWithLabel(_("PRESS (Y) TWICE TO REMOVE FROM FAVS./COLL."), doublePressToRemoveFavs); + + // opción de colección por defecto para screensaver + defaultScreenSaverCollection = + std::make_shared< OptionListComponent >( + mWindow, + _("ADD/REMOVE GAMES WHILE SCREENSAVER TO"), + false); + + // opción por defecto + std::string defaultScreenSaverCollectionName = + Settings::getInstance()->getString("DefaultScreenSaverCollection"); + defaultScreenSaverCollection->add( + _(""), + "", + defaultScreenSaverCollectionName == ""); + + std::map customSystems = + CollectionSystemManager::get()->getCustomCollectionSystems(); + + // añadir sistemas personalizados habilitados + for (auto it = customSystems.cbegin(); it != customSystems.cend(); ++it) { if (it->second.isEnabled) - defaultScreenSaverCollection->add(it->second.decl.longName, it->second.decl.name, defaultScreenSaverCollectionName == it->second.decl.name); + defaultScreenSaverCollection->add( + it->second.decl.longName, + it->second.decl.name, + defaultScreenSaverCollectionName == it->second.decl.name); } - mMenu.addWithLabel("ADD/REMOVE GAMES WHILE SCREENSAVER TO", defaultScreenSaverCollection); + mMenu.addWithLabel(_("ADD/REMOVE GAMES WHILE SCREENSAVER TO"), defaultScreenSaverCollection); - mMenu.addButton("BACK", "back", std::bind(&GuiCollectionSystemsOptions::applySettings, this)); + // botón volver + mMenu.addButton(_("BACK"), "back", std::bind(&GuiCollectionSystemsOptions::applySettings, this)); - mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2, Renderer::getScreenHeight() * 0.15f); + mMenu.setPosition( + (Renderer::getScreenWidth() - mMenu.getSize().x()) / 2, + Renderer::getScreenHeight() * 0.15f); } void GuiCollectionSystemsOptions::addEntry(const char* name, unsigned int color, bool add_arrow, const std::function& func) @@ -128,7 +202,7 @@ void GuiCollectionSystemsOptions::addEntry(const char* name, unsigned int color, ComponentListRow row; row.addElement(std::make_shared(mWindow, name, font, color), true); - if(add_arrow) + if (add_arrow) { std::shared_ptr bracket = makeArrow(mWindow); row.addElement(bracket, false); @@ -145,11 +219,16 @@ void GuiCollectionSystemsOptions::createCollection(std::string inName) std::string name = collSysMgr->getValidNewCollectionName(inName); SystemData* newSys = collSysMgr->addNewCustomCollection(name, true); - if (!collSysMgr->saveCustomCollection(newSys)) { - GuiInfoPopup* s = new GuiInfoPopup(mWindow, "Failed creating '" + Utils::String::toUpper(name) + "' Collection. See log for details.", 8000); + if (!collSysMgr->saveCustomCollection(newSys)) + { + GuiInfoPopup* s = new GuiInfoPopup( + mWindow, + "Failed creating '" + Utils::String::toUpper(name) + "' Collection. See log for details.", + 8000); mWindow->setInfoPopup(s); return; } + customOptionList->add(name, name, true); std::string outAuto = Utils::String::vectorToDelimitedString(autoOptionList->getSelectedObjects(), ","); std::string outCustom = Utils::String::vectorToDelimitedString(customOptionList->getSelectedObjects(), ","); @@ -158,7 +237,7 @@ void GuiCollectionSystemsOptions::createCollection(std::string inName) Window* window = mWindow; collSysMgr->setEditMode(name); - while(window->peekGui() && window->peekGui() != ViewController::get()) + while (window->peekGui() && window->peekGui() != ViewController::get()) delete window->peekGui(); } @@ -175,33 +254,57 @@ void GuiCollectionSystemsOptions::exitEditMode() GuiCollectionSystemsOptions::~GuiCollectionSystemsOptions() { - } void GuiCollectionSystemsOptions::addSystemsToMenu() { + std::map autoSystems = + CollectionSystemManager::get()->getAutoCollectionSystems(); - std::map autoSystems = CollectionSystemManager::get()->getAutoCollectionSystems(); + autoOptionList = + std::make_shared< OptionListComponent >( + mWindow, + _("SELECT COLLECTIONS"), + true); - autoOptionList = std::make_shared< OptionListComponent >(mWindow, "SELECT COLLECTIONS", true); - - // add Auto Systems - for(std::map::const_iterator it = autoSystems.cbegin() ; it != autoSystems.cend() ; it++ ) + // añadir sistemas automáticos (ALL GAMES, FAVORITES, RANDOM, LAST PLAYED) con traducción .ini + for (auto it = autoSystems.cbegin(); it != autoSystems.cend(); ++it) { - autoOptionList->add(it->second.decl.longName, it->second.decl.name, it->second.isEnabled); + // longName viene como "ALL GAMES", "FAVORITES", etc. + std::string label = it->second.decl.longName; + + // Las claves en el .ini están en minúsculas: "all games", "favorites", "random", "last played" + std::string lowerKey = Utils::String::toLower(label); + std::string translated = LocaleES::getInstance().translate(lowerKey); + + if (translated != lowerKey) + label = translated; // usar la traducción si existe + + autoOptionList->add( + label, + it->second.decl.name, + it->second.isEnabled); } - mMenu.addWithLabel("AUTOMATIC GAME COLLECTIONS", autoOptionList); + mMenu.addWithLabel(_("AUTOMATIC GAME COLLECTIONS"), autoOptionList); - std::map customSystems = CollectionSystemManager::get()->getCustomCollectionSystems(); + std::map customSystems = + CollectionSystemManager::get()->getCustomCollectionSystems(); - customOptionList = std::make_shared< OptionListComponent >(mWindow, "SELECT COLLECTIONS", true); + customOptionList = + std::make_shared< OptionListComponent >( + mWindow, + _("SELECT COLLECTIONS"), + true); - // add Custom Systems - for(std::map::const_iterator it = customSystems.cbegin() ; it != customSystems.cend() ; it++ ) + // añadir sistemas personalizados (se muestran con el nombre que les ponga el usuario) + for (auto it = customSystems.cbegin(); it != customSystems.cend(); ++it) { - customOptionList->add(it->second.decl.longName, it->second.decl.name, it->second.isEnabled); + customOptionList->add( + it->second.decl.longName, + it->second.decl.name, + it->second.isEnabled); } - mMenu.addWithLabel("CUSTOM GAME COLLECTIONS", customOptionList); + mMenu.addWithLabel(_("CUSTOM GAME COLLECTIONS"), customOptionList); } void GuiCollectionSystemsOptions::applySettings() @@ -217,19 +320,19 @@ void GuiCollectionSystemsOptions::applySettings() bool prevShow = Settings::getInstance()->getBool("CollectionShowSystemInfo"); bool outShow = toggleSystemNameInCollections->getState(); - // check if custom collection is still enabled for 'collection during screensaver'. - // if not set 'collection during screensaver' to "" which renders as + // comprobar si la colección personalizada sigue habilitada para 'collection during screensaver' + // si no, ponerla en "" (se muestra como ) std::string enabledCollectionName = ""; std::vector selection = defaultScreenSaverCollection->getSelectedObjects(); - if (selection.size() > 0) + if (!selection.empty()) { std::string selectedCollection = selection.at(0); - if (selectedCollection != "") + if (!selectedCollection.empty()) { std::vector enabledCollections = customOptionList->getSelectedObjects(); - for(auto nameIt = enabledCollections.begin(); nameIt != enabledCollections.end(); nameIt++) + for (auto nameIt = enabledCollections.begin(); nameIt != enabledCollections.end(); ++nameIt) { - if(*nameIt == selectedCollection) + if (*nameIt == selectedCollection) { enabledCollectionName = selectedCollection; break; @@ -241,8 +344,12 @@ void GuiCollectionSystemsOptions::applySettings() Settings::getInstance()->setString("DefaultScreenSaverCollection", enabledCollectionName); Settings::getInstance()->setBool("DoublePressRemovesFromFavs", doublePressToRemoveFavs->getState()); - bool needRefreshCollectionSettings = prevAuto != outAuto || prevCustom != outCustom || outSort != prevSort || outBundle != prevBundle - || prevShow != outShow; + bool needRefreshCollectionSettings = + prevAuto != outAuto || + prevCustom != outCustom || + outSort != prevSort || + outBundle != prevBundle || + prevShow != outShow; if (needRefreshCollectionSettings) { @@ -274,21 +381,20 @@ void GuiCollectionSystemsOptions::updateSettings(std::string newAutoSettings, st bool GuiCollectionSystemsOptions::input(InputConfig* config, Input input) { bool consumed = GuiComponent::input(config, input); - if(consumed) + if (consumed) return true; - if(config->isMappedTo("b", input) && input.value != 0) + if (config->isMappedTo("b", input) && input.value != 0) { applySettings(); } - return false; } std::vector GuiCollectionSystemsOptions::getHelpPrompts() { std::vector prompts = mMenu.getHelpPrompts(); - prompts.push_back(HelpPrompt("b", "back")); + prompts.push_back(HelpPrompt("b", _("BACK"))); return prompts; } diff --git a/es-app/src/guis/GuiGamelistOptions.cpp b/es-app/src/guis/GuiGamelistOptions.cpp index 436b131c46..9e3ab537e9 100644 --- a/es-app/src/guis/GuiGamelistOptions.cpp +++ b/es-app/src/guis/GuiGamelistOptions.cpp @@ -11,10 +11,16 @@ #include "GuiMetaDataEd.h" #include "SystemData.h" #include "components/TextListComponent.h" - -GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : GuiComponent(window), - mSystem(system), mMenu(window, "OPTIONS"), mFromPlaceholder(false), mFiltersChanged(false), - mJumpToSelected(false), mMetadataChanged(false) +#include "../LocaleES.h" // 🔹 Sistema de traducción + +GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) + : GuiComponent(window) + , mMenu(window, es_translate("OPTIONS").c_str(), Font::get(FONT_SIZE_LARGE)) // 🔹 Título traducible + , mSystem(system) + , mFromPlaceholder(false) + , mFiltersChanged(false) + , mJumpToSelected(false) + , mMetadataChanged(false) { addChild(&mMenu); @@ -23,27 +29,28 @@ GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : Gui mFromPlaceholder = file->isPlaceHolder(); ComponentListRow row; - if (!mFromPlaceholder) { - row.elements.clear(); - + if (!mFromPlaceholder) + { std::string currentSort = mSystem->getRootFolder()->getSortDescription(); std::string reqSort = FileSorts::SortTypes.at(0).description; // "jump to letter" menuitem only available (and correct jumping) on sort order "name, asc" - if (currentSort == reqSort) { + if (currentSort == reqSort) + { bool outOfRange = false; char curChar = (char)toupper(getGamelist()->getCursor()->getSortName()[0]); + // define supported character range - // this range includes all numbers, capital letters, and most reasonable symbols char startChar = '!'; char endChar = '_'; - if (curChar < startChar || curChar > endChar) { + if (curChar < startChar || curChar > endChar) + { // most likely 8 bit ASCII or Unicode (Prefix: 0xc2 or 0xe2) value curChar = startChar; outOfRange = true; } - mJumpToLetterList = std::make_shared(mWindow, "JUMP TO ...", false); + mJumpToLetterList = std::make_shared(mWindow, es_translate("JUMP TO ..."), false); for (char c = startChar; c <= endChar; c++) { // check if c is a valid first letter in current list @@ -60,15 +67,21 @@ GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : Gui } } - row.addElement(std::make_shared(mWindow, "JUMP TO ...", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.elements.clear(); + row.addElement(std::make_shared( + mWindow, + es_translate("JUMP TO ..."), + Font::get(FONT_SIZE_MEDIUM), + 0x777777FF), + true); row.addElement(mJumpToLetterList, false); row.input_handler = [&](InputConfig* config, Input input) { - if(config->isMappedTo("a", input) && input.value) + if (config->isMappedTo("a", input) && input.value) { jumpToLetter(); return true; } - else if(mJumpToLetterList->input(config, input)) + else if (mJumpToLetterList->input(config, input)) { return true; } @@ -79,74 +92,115 @@ GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : Gui // add launch system screensaver std::string screensaver_behavior = Settings::getInstance()->getString("ScreenSaverBehavior"); - bool useGamelistMedia = screensaver_behavior == "random video" || (screensaver_behavior == "slideshow" && !Settings::getInstance()->getBool("SlideshowScreenSaverCustomMediaSource")); + bool useGamelistMedia = screensaver_behavior == "random video" || + (screensaver_behavior == "slideshow" && !Settings::getInstance()->getBool("SlideshowScreenSaverCustomMediaSource")); + bool rpConfigSelected = "retropie" == mSystem->getName(); bool collectionsSelected = mSystem->getName() == CollectionSystemManager::get()->getCustomCollectionsBundle()->getName(); - if (!rpConfigSelected && useGamelistMedia && (!collectionsSelected || collectionsSelected && file->getType() == GAME)) { + if (!rpConfigSelected && useGamelistMedia && (!collectionsSelected || (collectionsSelected && file->getType() == GAME))) + { row.elements.clear(); - row.addElement(std::make_shared(mWindow, "LAUNCH SYSTEM SCREENSAVER", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared( + mWindow, + es_translate("LAUNCH SYSTEM SCREENSAVER"), + Font::get(FONT_SIZE_MEDIUM), + 0x777777FF), + true); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::launchSystemScreenSaver, this)); mMenu.addRow(row); } // "sort list by" menuitem - mListSort = std::make_shared(mWindow, "SORT GAMES BY", false); - for(unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) + mListSort = std::make_shared(mWindow, es_translate("SORT GAMES BY"), false); + for (unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) { const FileData::SortType& sort = FileSorts::SortTypes.at(i); mListSort->add(sort.description, &sort, sort.description == currentSort); } - mMenu.addWithLabel("SORT GAMES BY", mListSort); - + mMenu.addWithLabel(es_translate("SORT GAMES BY"), mListSort); } // show filtered menu - if(!Settings::getInstance()->getBool("ForceDisableFilters")) + if (!Settings::getInstance()->getBool("ForceDisableFilters")) { row.elements.clear(); - row.addElement(std::make_shared(mWindow, "FILTER GAMELIST", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared( + mWindow, + es_translate("FILTER GAMELIST"), + Font::get(FONT_SIZE_MEDIUM), + 0x777777FF), + true); row.addElement(makeArrow(mWindow), false); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openGamelistFilter, this)); mMenu.addRow(row); } - std::map customCollections = CollectionSystemManager::get()->getCustomCollectionSystems(); + std::map customCollections = + CollectionSystemManager::get()->getCustomCollectionSystems(); - if(UIModeController::getInstance()->isUIModeFull() && - ((customCollections.find(system->getName()) != customCollections.cend() && CollectionSystemManager::get()->getEditingCollection() != system->getName()) || - CollectionSystemManager::get()->getCustomCollectionsBundle()->getName() == system->getName())) + if (UIModeController::getInstance()->isUIModeFull() && + ((customCollections.find(system->getName()) != customCollections.cend() && + CollectionSystemManager::get()->getEditingCollection() != system->getName()) || + CollectionSystemManager::get()->getCustomCollectionsBundle()->getName() == system->getName())) { row.elements.clear(); - row.addElement(std::make_shared(mWindow, "ADD/REMOVE GAMES TO THIS GAME COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared( + mWindow, + es_translate("ADD/REMOVE GAMES TO THIS GAME COLLECTION"), + Font::get(FONT_SIZE_MEDIUM), + 0x777777FF), + true); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::startEditMode, this)); mMenu.addRow(row); } - if(UIModeController::getInstance()->isUIModeFull() && CollectionSystemManager::get()->isEditing()) + if (UIModeController::getInstance()->isUIModeFull() && CollectionSystemManager::get()->isEditing()) { row.elements.clear(); - row.addElement(std::make_shared(mWindow, "FINISH EDITING '" + Utils::String::toUpper(CollectionSystemManager::get()->getEditingCollection()) + "' COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + std::string finishText = es_translate("FINISH EDITING") + " '" + + Utils::String::toUpper(CollectionSystemManager::get()->getEditingCollection()) + + "' " + es_translate("COLLECTION"); + row.addElement(std::make_shared( + mWindow, + finishText, + Font::get(FONT_SIZE_MEDIUM), + 0x777777FF), + true); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::exitEditMode, this)); mMenu.addRow(row); } - if(UIModeController::getInstance()->isUIModeFull() && system == CollectionSystemManager::get()->getRandomCollection()) + if (UIModeController::getInstance()->isUIModeFull() && system == CollectionSystemManager::get()->getRandomCollection()) { row.elements.clear(); - row.addElement(std::make_shared(mWindow, "GET NEW RANDOM GAMES", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared( + mWindow, + es_translate("GET NEW RANDOM GAMES"), + Font::get(FONT_SIZE_MEDIUM), + 0x777777FF), + true); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::recreateCollection, this)); mMenu.addRow(row); } - if (UIModeController::getInstance()->isUIModeFull() && !mFromPlaceholder && !(mSystem->isCollection() && file->getType() == FOLDER)) + if (UIModeController::getInstance()->isUIModeFull() && !mFromPlaceholder && + !(mSystem->isCollection() && file->getType() == FOLDER)) { row.elements.clear(); - std::string lblTxt = std::string("EDIT THIS "); - lblTxt += std::string((file->getType() == FOLDER ? "FOLDER" : "GAME")); - lblTxt += std::string("'S METADATA"); - row.addElement(std::make_shared(mWindow, lblTxt, Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + std::string lblTxt; + if (file->getType() == FOLDER) + lblTxt = es_translate("EDIT THIS FOLDER'S METADATA"); + else + lblTxt = es_translate("EDIT THIS GAME'S METADATA"); + + row.addElement(std::make_shared( + mWindow, + lblTxt, + Font::get(FONT_SIZE_MEDIUM), + 0x777777FF), + true); row.addElement(makeArrow(mWindow), false); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this)); mMenu.addRow(row); @@ -154,32 +208,36 @@ GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : Gui // center the menu setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); - mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, (mSize.y() - mMenu.getSize().y()) / 2); + mMenu.setPosition( + (mSize.x() - mMenu.getSize().x()) / 2, + (mSize.y() - mMenu.getSize().y()) / 2); } GuiGamelistOptions::~GuiGamelistOptions() { FileData* root = mSystem->getRootFolder(); // apply sort - if (!mFromPlaceholder) { - const FileData::SortType selectedSort = mJumpToSelected ? FileSorts::SortTypes.at(0) /* force "name, asc" */ : *mListSort->getSelected(); - if (root->getSortDescription() != selectedSort.description) { + if (!mFromPlaceholder) + { + const FileData::SortType selectedSort = + mJumpToSelected + ? FileSorts::SortTypes.at(0) // force "name, asc" + : *mListSort->getSelected(); + + if (root->getSortDescription() != selectedSort.description) + { root->sort(selectedSort); // will also recursively sort children - // notify that the root folder was sorted getGamelist()->onFileChanged(root, FILE_SORTED); } } if (mFiltersChanged || mMetadataChanged) { - // force refresh of cursor list position - ViewController::get()->getGameListView(mSystem)->setViewportTop(TextListComponent::REFRESH_LIST_CURSOR_POS); - // re-display the elements for whatever new or renamed game is selected + ViewController::get()->getGameListView(mSystem)->setViewportTop( + TextListComponent::REFRESH_LIST_CURSOR_POS); ViewController::get()->reloadGameListView(mSystem); - if (mFiltersChanged) { - // trigger repaint of cursor and list detail + if (mFiltersChanged) getGamelist()->onFileChanged(root, FILE_SORTED); - } } } @@ -187,13 +245,13 @@ bool GuiGamelistOptions::launchSystemScreenSaver() { SystemData* system = mSystem; std::string systemName = system->getName(); - // need to check if we're in a folder inside the collections bundle, to launch from there - if(systemName == CollectionSystemManager::get()->getCustomCollectionsBundle()->getName()) + + if (systemName == CollectionSystemManager::get()->getCustomCollectionsBundle()->getName()) { FileData* file = getGamelist()->getCursor(); // is GAME otherwise menuentry would have been hidden - // we are inside a specific collection. We want to launch for that one. system = file->getSystem(); } + mWindow->startScreenSaver(system); mWindow->renderScreenSaver(); @@ -217,20 +275,14 @@ void GuiGamelistOptions::recreateCollection() void GuiGamelistOptions::startEditMode() { std::string editingSystem = mSystem->getName(); - // need to check if we're editing the collections bundle, as we will want to edit the selected collection within - if(editingSystem == CollectionSystemManager::get()->getCustomCollectionsBundle()->getName()) + + if (editingSystem == CollectionSystemManager::get()->getCustomCollectionsBundle()->getName()) { FileData* file = getGamelist()->getCursor(); - // do we have the cursor on a specific collection? if (file->getType() == FOLDER) - { editingSystem = file->getName(); - } else - { - // we are inside a specific collection. We want to edit that one. editingSystem = file->getSystem()->getName(); - } } CollectionSystemManager::get()->setEditMode(editingSystem); delete this; @@ -244,8 +296,6 @@ void GuiGamelistOptions::exitEditMode() void GuiGamelistOptions::openMetaDataEd() { - // open metadata editor - // get the FileData that hosts the original metadata FileData* file = getGamelist()->getCursor()->getSourceFileData(); ScraperSearchParams p; p.game = file; @@ -271,7 +321,14 @@ void GuiGamelistOptions::openMetaDataEd() }; } - mWindow->pushGui(new GuiMetaDataEd(mWindow, &file->metadata, file->metadata.getMDD(), p, Utils::FileSystem::getFileName(file->getPath()), saveBtnFunc, deleteBtnFunc)); + mWindow->pushGui(new GuiMetaDataEd( + mWindow, + &file->metadata, + file->metadata.getMDD(), + p, + Utils::FileSystem::getFileName(file->getPath()), + saveBtnFunc, + deleteBtnFunc)); } void GuiGamelistOptions::jumpToLetter() @@ -279,34 +336,33 @@ void GuiGamelistOptions::jumpToLetter() char letter = mJumpToLetterList->getSelected(); IGameListView* gamelist = getGamelist(); - // this is a really shitty way to get a list of files - const std::vector& files = gamelist->getCursor()->getParent()->getChildrenListToDisplay(); + const std::vector& files = + gamelist->getCursor()->getParent()->getChildrenListToDisplay(); long min = 0; long max = (long)files.size() - 1; long mid = 0; - while(max >= min) + while (max >= min) { mid = ((max - min) / 2) + min; - // game somehow has no first character to check - if(files.at(mid)->getName().empty()) + if (files.at(mid)->getName().empty()) continue; char checkLetter = (char)toupper(files.at(mid)->getSortName()[0]); - if(checkLetter < letter) + if (checkLetter < letter) min = mid + 1; - else if(checkLetter > letter || (mid > 0 && (letter == toupper(files.at(mid - 1)->getSortName()[0])))) + else if (checkLetter > letter || + (mid > 0 && (letter == toupper(files.at(mid - 1)->getSortName()[0])))) max = mid - 1; else - break; //exact match found + break; // exact match found } gamelist->setCursor(files.at(mid)); - // flag to force default sort order "name, asc", if user changed the sortorder in the options dialog mJumpToSelected = true; delete this; @@ -314,7 +370,7 @@ void GuiGamelistOptions::jumpToLetter() bool GuiGamelistOptions::input(InputConfig* config, Input input) { - if((config->isMappedTo("b", input) || config->isMappedTo("select", input)) && input.value) + if ((config->isMappedTo("b", input) || config->isMappedTo("select", input)) && input.value) { delete this; return true; @@ -333,7 +389,7 @@ HelpStyle GuiGamelistOptions::getHelpStyle() std::vector GuiGamelistOptions::getHelpPrompts() { auto prompts = mMenu.getHelpPrompts(); - prompts.push_back(HelpPrompt("b", "close")); + prompts.push_back(HelpPrompt("b", es_translate("CLOSE"))); return prompts; } diff --git a/es-app/src/guis/GuiGamelistOptions.h b/es-app/src/guis/GuiGamelistOptions.h index dd14399b54..fcae98d335 100644 --- a/es-app/src/guis/GuiGamelistOptions.h +++ b/es-app/src/guis/GuiGamelistOptions.h @@ -7,6 +7,9 @@ #include "FileData.h" #include "GuiComponent.h" +// 🔹 Ruta correcta +#include "LocaleESHook.h" + class IGameListView; class SystemData; diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index b3150db4a5..41541919ab 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -9,6 +9,7 @@ #include "guis/GuiMsgBox.h" #include "guis/GuiScraperStart.h" #include "guis/GuiSettings.h" +#include "guis/GuiThemeOptions.h" #include "views/UIModeController.h" #include "views/ViewController.h" #include "CollectionSystemManager.h" @@ -18,27 +19,102 @@ #include "VolumeControl.h" #include #include +#include // system() + +#include "PowerSaver.h" #include "platform.h" #include "FileSorts.h" #include "views/gamelist/IGameListView.h" #include "guis/GuiInfoPopup.h" +#include "Settings.h" +#include "utils/FileSystemUtil.h" +#include "utils/StringUtil.h" +#include "resources/Font.h" +#include "ThemeData.h" +#include "LocaleES.h" + +// Navegación por sonido +#include "Sound.h" +#include "NavigationSounds.h" +#include + +// Música de fondo ES-X (gestor central) +#include "audio/BackgroundMusicManager.h" + +// Helper local para traducciones .ini y colores de menú +namespace +{ + inline std::string _(const std::string& key) + { + return LocaleES::getInstance().translate(key); + } -GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MENU"), mVersion(window) + inline unsigned int getMenuTextColor() + { + // Si MenuDark = true → texto claro; si no → gris clásico + return Settings::getInstance()->getBool("MenuDark") ? 0xFFFFFFFF : 0x777777FF; + } + + inline unsigned int getVersionTextColor() + { + // Versión un poco más clara en modo oscuro + return Settings::getInstance()->getBool("MenuDark") ? 0xB0B0B0FF : 0x5E5E5EFF; + } + + // === SONIDOS DE NAVEGACIÓN PARA MENÚ (BACK, ETC.) === + + inline std::shared_ptr getNavSound(const std::string& logicalName) + { + auto vcState = ViewController::get()->getState(); + SystemData* sys = vcState.getSystem(); + if (!sys) + return nullptr; + + const std::shared_ptr& theme = sys->getTheme(); + if (!theme) + return nullptr; + + return NavigationSounds::getFromTheme(theme, logicalName); + } + + inline void playMenuBackSound() + { + // esquema tipo Batocera: "back" + auto snd = getNavSound("back"); + if (snd) + snd->play(); + } +} + +GuiMenu::GuiMenu(Window* window) + : GuiComponent(window) + , mMenu(window, "MAIN MENU") + , mVersion(window) { + // Cargar idioma actual antes de crear las entradas + LocaleES::getInstance().loadFromSettings(); + + // Título del menú + mMenu.setTitle(_("MAIN MENU").c_str(), Font::get(FONT_SIZE_LARGE)); + bool isFullUI = UIModeController::getInstance()->isUIModeFull(); + unsigned int menuColor = getMenuTextColor(); - if (isFullUI) { - addEntry("SCRAPER", 0x777777FF, true, [this] { openScraperSettings(); }); - addEntry("SOUND SETTINGS", 0x777777FF, true, [this] { openSoundSettings(); }); - addEntry("UI SETTINGS", 0x777777FF, true, [this] { openUISettings(); }); - addEntry("GAME COLLECTION SETTINGS", 0x777777FF, true, [this] { openCollectionSystemSettings(); }); - addEntry("OTHER SETTINGS", 0x777777FF, true, [this] { openOtherSettings(); }); - addEntry("CONFIGURE INPUT", 0x777777FF, true, [this] { openConfigInput(); }); - } else { - addEntry("SOUND SETTINGS", 0x777777FF, true, [this] { openSoundSettings(); }); + if (isFullUI) + { + addEntry(_("SCRAPER").c_str(), menuColor, true, [this] { openScraperSettings(); }); + addEntry(_("SOUND SETTINGS").c_str(), menuColor, true, [this] { openSoundSettings(); }); + addEntry(_("UI SETTINGS").c_str(), menuColor, true, [this] { openUISettings(); }); + addEntry(_("GAME COLLECTION SETTINGS").c_str(), menuColor, true, [this] { openCollectionSystemSettings(); }); + addEntry(_("OTHER SETTINGS").c_str(), menuColor, true, [this] { openOtherSettings(); }); + addEntry(_("CONFIGURE INPUT").c_str(), menuColor, true, [this] { openConfigInput(); }); + } + else + { + addEntry(_("SOUND SETTINGS").c_str(), menuColor, true, [this] { openSoundSettings(); }); } - addEntry("QUIT", 0x777777FF, true, [this] {openQuitMenu(); }); + addEntry(_("QUIT").c_str(), menuColor, true, [this] { openQuitMenu(); }); addChild(&mMenu); addVersionInfo(); @@ -48,33 +124,39 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN void GuiMenu::openScraperSettings() { - auto s = new GuiSettings(mWindow, "SCRAPER"); + auto s = new GuiSettings(mWindow, _("SCRAPER").c_str()); // scrape from - auto scraper_list = std::make_shared< OptionListComponent< std::string > >(mWindow, "SCRAPE FROM", false); + auto scraper_list = std::make_shared< OptionListComponent< std::string > >(mWindow, _("SCRAPE FROM").c_str(), false); std::vector scrapers = getScraperList(); - // Select either the first entry of the one read from the settings, just in case the scraper from settings has vanished. - for(auto it = scrapers.cbegin(); it != scrapers.cend(); it++) + for (auto it = scrapers.cbegin(); it != scrapers.cend(); it++) scraper_list->add(*it, *it, *it == Settings::getInstance()->getString("Scraper")); - s->addWithLabel("SCRAPE FROM", scraper_list); - s->addSaveFunc([scraper_list] { Settings::getInstance()->setString("Scraper", scraper_list->getSelected()); }); + s->addWithLabel(_("SCRAPE FROM").c_str(), scraper_list); + s->addSaveFunc([scraper_list] { + Settings::getInstance()->setString("Scraper", scraper_list->getSelected()); + }); // scrape ratings auto scrape_ratings = std::make_shared(mWindow); scrape_ratings->setState(Settings::getInstance()->getBool("ScrapeRatings")); - s->addWithLabel("SCRAPE RATINGS", scrape_ratings); - s->addSaveFunc([scrape_ratings] { Settings::getInstance()->setBool("ScrapeRatings", scrape_ratings->getState()); }); + s->addWithLabel(_("SCRAPE RATINGS").c_str(), scrape_ratings); + s->addSaveFunc([scrape_ratings] { + Settings::getInstance()->setBool("ScrapeRatings", scrape_ratings->getState()); + }); // scrape now ComponentListRow row; auto openScrapeNow = [this] { mWindow->pushGui(new GuiScraperStart(mWindow)); }; std::function openAndSave = openScrapeNow; - openAndSave = [s, openAndSave] { s->save(); openAndSave(); }; + openAndSave = [s, openAndSave] { + s->save(); + openAndSave(); + }; row.makeAcceptInputHandler(openAndSave); - auto scrape_now = std::make_shared(mWindow, "SCRAPE NOW", Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + auto scrape_now = std::make_shared(mWindow, _("SCRAPE NOW"), Font::get(FONT_SIZE_MEDIUM), getMenuTextColor()); auto bracket = makeArrow(mWindow); row.addElement(scrape_now, true); row.addElement(bracket, false); @@ -85,19 +167,37 @@ void GuiMenu::openScraperSettings() void GuiMenu::openSoundSettings() { - auto s = new GuiSettings(mWindow, "SOUND SETTINGS"); + auto s = new GuiSettings(mWindow, _("SOUND SETTINGS").c_str()); // volume auto volume = std::make_shared(mWindow, 0.f, 100.f, 1.f, "%"); volume->setValue((float)VolumeControl::getInstance()->getVolume()); - s->addWithLabel("SYSTEM VOLUME", volume); - s->addSaveFunc([volume] { VolumeControl::getInstance()->setVolume((int)Math::round(volume->getValue())); }); + s->addWithLabel(_("SYSTEM VOLUME").c_str(), volume); + s->addSaveFunc([volume] { + VolumeControl::getInstance()->setVolume((int)Math::round(volume->getValue())); + }); if (UIModeController::getInstance()->isUIModeFull()) { + // 🎵 MÚSICA DE FONDO (ES-X) – usar estado REAL del BackgroundMusicManager + { + auto bgm_switch = std::make_shared(mWindow); + + // Estado inicial: lo que dice el manager (no Settings suelto) + bgm_switch->setState(BackgroundMusicManager::getInstance().isEnabled()); + + s->addWithLabel(_("BACKGROUND MUSIC").c_str(), bgm_switch); + s->addSaveFunc([bgm_switch] { + BackgroundMusicManager::getInstance().setEnabled(bgm_switch->getState()); + + // (Opcional) mantener compatibilidad con una key vieja si la usaste antes: + Settings::getInstance()->setBool("EnableBGM", bgm_switch->getState()); + }); + } + #if defined(__linux__) // audio card - auto audio_card = std::make_shared< OptionListComponent >(mWindow, "AUDIO CARD", false); + auto audio_card = std::make_shared< OptionListComponent >(mWindow, _("AUDIO CARD").c_str(), false); std::vector audio_cards; audio_cards.push_back("default"); audio_cards.push_back("sysdefault"); @@ -105,14 +205,14 @@ void GuiMenu::openSoundSettings() audio_cards.push_back("hw"); audio_cards.push_back("plughw"); audio_cards.push_back("null"); - if (Settings::getInstance()->getString("AudioCard") != "") { - if(std::find(audio_cards.begin(), audio_cards.end(), Settings::getInstance()->getString("AudioCard")) == audio_cards.end()) { + if (Settings::getInstance()->getString("AudioCard") != "") + { + if (std::find(audio_cards.begin(), audio_cards.end(), Settings::getInstance()->getString("AudioCard")) == audio_cards.end()) audio_cards.push_back(Settings::getInstance()->getString("AudioCard")); - } } - for(auto ac = audio_cards.cbegin(); ac != audio_cards.cend(); ac++) + for (auto ac = audio_cards.cbegin(); ac != audio_cards.cend(); ac++) audio_card->add(*ac, *ac, Settings::getInstance()->getString("AudioCard") == *ac); - s->addWithLabel("AUDIO CARD", audio_card); + s->addWithLabel(_("AUDIO CARD").c_str(), audio_card); s->addSaveFunc([audio_card] { Settings::getInstance()->setString("AudioCard", audio_card->getSelected()); VolumeControl::getInstance()->deinit(); @@ -120,7 +220,7 @@ void GuiMenu::openSoundSettings() }); // volume control device - auto vol_dev = std::make_shared< OptionListComponent >(mWindow, "AUDIO DEVICE", false); + auto vol_dev = std::make_shared< OptionListComponent >(mWindow, _("AUDIO DEVICE").c_str(), false); std::vector transitions; transitions.push_back("PCM"); transitions.push_back("HDMI"); @@ -129,14 +229,14 @@ void GuiMenu::openSoundSettings() transitions.push_back("Master"); transitions.push_back("Digital"); transitions.push_back("Analogue"); - if (Settings::getInstance()->getString("AudioDevice") != "") { - if(std::find(transitions.begin(), transitions.end(), Settings::getInstance()->getString("AudioDevice")) == transitions.end()) { + if (Settings::getInstance()->getString("AudioDevice") != "") + { + if (std::find(transitions.begin(), transitions.end(), Settings::getInstance()->getString("AudioDevice")) == transitions.end()) transitions.push_back(Settings::getInstance()->getString("AudioDevice")); - } } - for(auto it = transitions.cbegin(); it != transitions.cend(); it++) + for (auto it = transitions.cbegin(); it != transitions.cend(); it++) vol_dev->add(*it, *it, Settings::getInstance()->getString("AudioDevice") == *it); - s->addWithLabel("AUDIO DEVICE", vol_dev); + s->addWithLabel(_("AUDIO DEVICE").c_str(), vol_dev); s->addSaveFunc([vol_dev] { Settings::getInstance()->setString("AudioDevice", vol_dev->getSelected()); VolumeControl::getInstance()->deinit(); @@ -147,7 +247,7 @@ void GuiMenu::openSoundSettings() // disable sounds auto sounds_enabled = std::make_shared(mWindow); sounds_enabled->setState(Settings::getInstance()->getBool("EnableSounds")); - s->addWithLabel("ENABLE NAVIGATION SOUNDS", sounds_enabled); + s->addWithLabel(_("ENABLE NAVIGATION SOUNDS").c_str(), sounds_enabled); s->addSaveFunc([sounds_enabled] { if (sounds_enabled->getState() && !Settings::getInstance()->getBool("EnableSounds") @@ -161,28 +261,30 @@ void GuiMenu::openSoundSettings() auto video_audio = std::make_shared(mWindow); video_audio->setState(Settings::getInstance()->getBool("VideoAudio")); - s->addWithLabel("ENABLE VIDEO AUDIO", video_audio); - s->addSaveFunc([video_audio] { Settings::getInstance()->setBool("VideoAudio", video_audio->getState()); }); + s->addWithLabel(_("ENABLE VIDEO AUDIO").c_str(), video_audio); + s->addSaveFunc([video_audio] { + Settings::getInstance()->setBool("VideoAudio", video_audio->getState()); + }); #ifdef _OMX_ // OMX player Audio Device - auto omx_audio_dev = std::make_shared< OptionListComponent >(mWindow, "OMX PLAYER AUDIO DEVICE", false); + auto omx_audio_dev = std::make_shared< OptionListComponent >(mWindow, _("OMX PLAYER AUDIO DEVICE").c_str(), false); std::vector omx_cards; - // RPi Specific Audio Cards omx_cards.push_back("local"); omx_cards.push_back("hdmi"); omx_cards.push_back("both"); omx_cards.push_back("alsa"); omx_cards.push_back("alsa:hw:0,0"); omx_cards.push_back("alsa:hw:1,0"); - if (Settings::getInstance()->getString("OMXAudioDev") != "") { - if (std::find(omx_cards.begin(), omx_cards.end(), Settings::getInstance()->getString("OMXAudioDev")) == omx_cards.end()) { + omx_cards.push_back("alsa:hw:1,0"); + if (Settings::getInstance()->getString("OMXAudioDev") != "") + { + if (std::find(omx_cards.begin(), omx_cards.end(), Settings::getInstance()->getString("OMXAudioDev")) == omx_cards.end()) omx_cards.push_back(Settings::getInstance()->getString("OMXAudioDev")); - } } for (auto it = omx_cards.cbegin(); it != omx_cards.cend(); it++) omx_audio_dev->add(*it, *it, Settings::getInstance()->getString("OMXAudioDev") == *it); - s->addWithLabel("OMX PLAYER AUDIO DEVICE", omx_audio_dev); + s->addWithLabel(_("OMX PLAYER AUDIO DEVICE").c_str(), omx_audio_dev); s->addSaveFunc([omx_audio_dev] { if (Settings::getInstance()->getString("OMXAudioDev") != omx_audio_dev->getSelected()) Settings::getInstance()->setString("OMXAudioDev", omx_audio_dev->getSelected()); @@ -191,57 +293,57 @@ void GuiMenu::openSoundSettings() } mWindow->pushGui(s); - } void GuiMenu::openUISettings() { - auto s = new GuiSettings(mWindow, "UI SETTINGS"); + auto s = new GuiSettings(mWindow, _("UI SETTINGS").c_str()); - //UI mode - auto UImodeSelection = std::make_shared< OptionListComponent >(mWindow, "UI MODE", false); + // UI mode + auto UImodeSelection = std::make_shared< OptionListComponent >(mWindow, _("UI MODE").c_str(), false); std::vector UImodes = UIModeController::getInstance()->getUIModes(); for (auto it = UImodes.cbegin(); it != UImodes.cend(); it++) UImodeSelection->add(*it, *it, Settings::getInstance()->getString("UIMode") == *it); - s->addWithLabel("UI MODE", UImodeSelection); Window* window = mWindow; - s->addSaveFunc([ UImodeSelection, window] + s->addSaveFunc([UImodeSelection, window] { std::string selectedMode = UImodeSelection->getSelected(); if (selectedMode != "Full") { - std::string msg = "You are changing the UI to a restricted mode:\n" + selectedMode + "\n"; - msg += "This will hide most menu-options to prevent changes to the system.\n"; - msg += "To unlock and return to the full UI, enter this code: \n"; + std::string msg = _("You are changing the UI to a restricted mode:") + "\n" + selectedMode + "\n"; + msg += _("This will hide most menu-options to prevent changes to the system.") + "\n"; + msg += _("To unlock and return to the full UI, enter this code:") + " \n"; msg += "\"" + UIModeController::getInstance()->getFormattedPassKeyStr() + "\"\n\n"; - msg += "Do you want to proceed?"; + msg += _("Do you want to proceed?"); window->pushGui(new GuiMsgBox(window, msg, - "YES", [selectedMode] { + _("YES").c_str(), [selectedMode] { LOG(LogDebug) << "Setting UI mode to " << selectedMode; Settings::getInstance()->setString("UIMode", selectedMode); Settings::getInstance()->saveFile(); - }, "NO",nullptr)); + }, _("NO").c_str(), nullptr)); } }); // screensaver ComponentListRow screensaver_row; screensaver_row.elements.clear(); - screensaver_row.addElement(std::make_shared(mWindow, "SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + screensaver_row.addElement(std::make_shared(mWindow, _("SCREENSAVER SETTINGS"), Font::get(FONT_SIZE_MEDIUM), getMenuTextColor()), true); screensaver_row.addElement(makeArrow(mWindow), false); screensaver_row.makeAcceptInputHandler(std::bind(&GuiMenu::openScreensaverOptions, this)); s->addRow(screensaver_row); - // quick system select (left/right in game list view) + // quick system select auto quick_sys_select = std::make_shared(mWindow); quick_sys_select->setState(Settings::getInstance()->getBool("QuickSystemSelect")); - s->addWithLabel("QUICK SYSTEM SELECT", quick_sys_select); - s->addSaveFunc([quick_sys_select] { Settings::getInstance()->setBool("QuickSystemSelect", quick_sys_select->getState()); }); + s->addWithLabel(_("QUICK SYSTEM SELECT").c_str(), quick_sys_select); + s->addSaveFunc([quick_sys_select] { + Settings::getInstance()->setBool("QuickSystemSelect", quick_sys_select->getState()); + }); - // carousel transition option + // carousel transitions auto move_carousel = std::make_shared(mWindow); move_carousel->setState(Settings::getInstance()->getBool("MoveCarousel")); - s->addWithLabel("CAROUSEL TRANSITIONS", move_carousel); + s->addWithLabel(_("CAROUSEL TRANSITIONS").c_str(), move_carousel); s->addSaveFunc([move_carousel] { if (move_carousel->getState() && !Settings::getInstance()->getBool("MoveCarousel") @@ -254,14 +356,14 @@ void GuiMenu::openUISettings() }); // transition style - auto transition_style = std::make_shared< OptionListComponent >(mWindow, "TRANSITION STYLE", false); + auto transition_style = std::make_shared< OptionListComponent >(mWindow, _("TRANSITION STYLE").c_str(), false); std::vector transitions; transitions.push_back("fade"); transitions.push_back("slide"); transitions.push_back("instant"); - for(auto it = transitions.cbegin(); it != transitions.cend(); it++) + for (auto it = transitions.cbegin(); it != transitions.cend(); it++) transition_style->add(*it, *it, Settings::getInstance()->getString("TransitionStyle") == *it); - s->addWithLabel("TRANSITION STYLE", transition_style); + s->addWithLabel(_("TRANSITION STYLE").c_str(), transition_style); s->addSaveFunc([transition_style] { if (Settings::getInstance()->getString("TransitionStyle") == "instant" && transition_style->getSelected() != "instant" @@ -273,41 +375,121 @@ void GuiMenu::openUISettings() Settings::getInstance()->setString("TransitionStyle", transition_style->getSelected()); }); + // DARK MENU (modo oscuro para menús) + { + auto menu_dark = std::make_shared(mWindow); + menu_dark->setState(Settings::getInstance()->getBool("MenuDark")); + s->addWithLabel(_("DARK MENU").c_str(), menu_dark); + s->addSaveFunc([menu_dark] { + Settings::getInstance()->setBool("MenuDark", menu_dark->getState()); + }); + } + + // ============================================================ + // CLOCK (reloj global overlay) - ON/OFF + // key: ShowClock + // ============================================================ + { + auto show_clock = std::make_shared(mWindow); + show_clock->setState(Settings::getInstance()->getBool("ShowClock")); + s->addWithLabel(_("CLOCK").c_str(), show_clock); + s->addSaveFunc([show_clock] { + Settings::getInstance()->setBool("ShowClock", show_clock->getState()); + }); + } + // theme set auto themeSets = ThemeData::getThemeSets(); - if(!themeSets.empty()) + if (!themeSets.empty()) { std::map::const_iterator selectedSet = themeSets.find(Settings::getInstance()->getString("ThemeSet")); - if(selectedSet == themeSets.cend()) + if (selectedSet == themeSets.cend()) selectedSet = themeSets.cbegin(); - auto theme_set = std::make_shared< OptionListComponent >(mWindow, "THEME SET", false); - for(auto it = themeSets.cbegin(); it != themeSets.cend(); it++) + auto theme_set = std::make_shared< OptionListComponent >(mWindow, _("THEME SET").c_str(), false); + for (auto it = themeSets.cbegin(); it != themeSets.cend(); it++) theme_set->add(it->first, it->first, it == selectedSet); - s->addWithLabel("THEME SET", theme_set); + s->addWithLabel(_("THEME SET").c_str(), theme_set); - Window* window = mWindow; - s->addSaveFunc([window, theme_set] + Window* window2 = mWindow; + s->addSaveFunc([window2, theme_set] { bool needReload = false; std::string oldTheme = Settings::getInstance()->getString("ThemeSet"); - if(oldTheme != theme_set->getSelected()) + if (oldTheme != theme_set->getSelected()) needReload = true; Settings::getInstance()->setString("ThemeSet", theme_set->getSelected()); - if(needReload) + if (needReload) { Scripting::fireEvent("theme-changed", theme_set->getSelected(), oldTheme); CollectionSystemManager::get()->updateSystemsList(); - ViewController::get()->reloadAll(true); // TODO - replace this with some sort of signal-based implementation + ViewController::get()->reloadAll(true); } }); } + // THEME OPTIONS (GUI interna de ES-X) + { + ComponentListRow theme_row; + theme_row.elements.clear(); + theme_row.addElement(std::make_shared(mWindow, _("THEME OPTIONS"), Font::get(FONT_SIZE_MEDIUM), getMenuTextColor()), true); + theme_row.addElement(makeArrow(mWindow), false); + theme_row.makeAcceptInputHandler(std::bind(&GuiMenu::openThemeOptions, this)); + s->addRow(theme_row); + } + + // LANGUAGE (usando .ini en ~/.emulationstation/lang) + { + auto language_list = std::make_shared< OptionListComponent >(mWindow, _("LANGUAGE").c_str(), false); + + std::string currentLang = Settings::getInstance()->getString("Language"); + if (currentLang.empty()) + currentLang = "en"; + + std::vector languages; + languages.push_back("en"); + languages.push_back("es"); + + std::string langDir = Utils::FileSystem::getHomePath() + "/.emulationstation/lang"; + if (Utils::FileSystem::isDirectory(langDir)) + { + Utils::FileSystem::stringList files = Utils::FileSystem::getDirContent(langDir); + for (auto it = files.cbegin(); it != files.cend(); ++it) + { + if (Utils::FileSystem::isRegularFile(*it) && Utils::FileSystem::getExtension(*it) == ".ini") + { + std::string code = Utils::FileSystem::getStem(*it); + if (std::find(languages.begin(), languages.end(), code) == languages.end()) + languages.push_back(code); + } + } + } + + for (auto &code : languages) + { + std::string label; + if (code == "en") + label = "English"; + else if (code == "es") + label = "Español"; + else + label = Utils::String::toUpper(code); + + language_list->add(label, code, (currentLang == code)); + } + + s->addWithLabel(_("LANGUAGE").c_str(), language_list); + s->addSaveFunc([language_list] { + Settings::getInstance()->setString("Language", language_list->getSelected()); + LocaleES::getInstance().loadFromSettings(); + }); + } + // GameList view style - auto gamelist_style = std::make_shared< OptionListComponent >(mWindow, "GAMELIST VIEW STYLE", false); + auto gamelist_style = std::make_shared< OptionListComponent >(mWindow, _("GAMELIST VIEW STYLE").c_str(), false); std::vector styles; styles.push_back("automatic"); styles.push_back("basic"); @@ -317,7 +499,7 @@ void GuiMenu::openUISettings() for (auto it = styles.cbegin(); it != styles.cend(); it++) gamelist_style->add(*it, *it, Settings::getInstance()->getString("GamelistViewStyle") == *it); - s->addWithLabel("GAMELIST VIEW STYLE", gamelist_style); + s->addWithLabel(_("GAMELIST VIEW STYLE").c_str(), gamelist_style); s->addSaveFunc([gamelist_style] { bool needReload = false; if (Settings::getInstance()->getString("GamelistViewStyle") != gamelist_style->getSelected()) @@ -327,43 +509,38 @@ void GuiMenu::openUISettings() ViewController::get()->reloadAll(); }); - // Optionally ignore leading articles when sorting game titles + // Ignore articles auto ignore_articles = std::make_shared(mWindow); ignore_articles->setState(Settings::getInstance()->getBool("IgnoreLeadingArticles")); - s->addWithLabel("IGNORE ARTICLES (NAME SORT ONLY)", ignore_articles); + s->addWithLabel(_("IGNORE ARTICLES (NAME SORT ONLY)").c_str(), ignore_articles); s->addSaveFunc([ignore_articles, window] { bool articles_are_ignored = Settings::getInstance()->getBool("IgnoreLeadingArticles"); Settings::getInstance()->setBool("IgnoreLeadingArticles", ignore_articles->getState()); if (ignore_articles->getState() != articles_are_ignored) { - //For each system... for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++) { - //Apply sort recursively FileData* root = (*it)->getRootFolder(); root->sort(getSortTypeFromString(root->getSortName())); - - //Notify that the root folder was sorted ViewController::get()->getGameListView((*it))->onFileChanged(root, FILE_SORTED); } - //Display popup to inform user - GuiInfoPopup* popup = new GuiInfoPopup(window, "Files sorted", 4000); + GuiInfoPopup* popup = new GuiInfoPopup(window, _("FILES SORTED"), 4000); window->setInfoPopup(popup); } }); - // lb/rb uses full screen size paging instead of -10/+10 steps + // full screen paging auto use_fullscreen_paging = std::make_shared(mWindow); use_fullscreen_paging->setState(Settings::getInstance()->getBool("UseFullscreenPaging")); - s->addWithLabel("USE FULL SCREEN PAGING FOR LB/RB", use_fullscreen_paging); + s->addWithLabel(_("USE FULL SCREEN PAGING FOR LB/RB").c_str(), use_fullscreen_paging); s->addSaveFunc([use_fullscreen_paging] { Settings::getInstance()->setBool("UseFullscreenPaging", use_fullscreen_paging->getState()); }); - // Optionally start in selected system - auto systemfocus_list = std::make_shared< OptionListComponent >(mWindow, "START ON SYSTEM", false); - systemfocus_list->add("NONE", "", Settings::getInstance()->getString("StartupSystem") == ""); + // startup system + auto systemfocus_list = std::make_shared< OptionListComponent >(mWindow, _("START ON SYSTEM").c_str(), false); + systemfocus_list->add(_("NONE"), "", Settings::getInstance()->getString("StartupSystem") == ""); for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++) { if ("retropie" != (*it)->getName()) @@ -371,7 +548,7 @@ void GuiMenu::openUISettings() systemfocus_list->add((*it)->getName(), (*it)->getName(), Settings::getInstance()->getString("StartupSystem") == (*it)->getName()); } } - s->addWithLabel("START ON SYSTEM", systemfocus_list); + s->addWithLabel(_("START ON SYSTEM").c_str(), systemfocus_list); s->addSaveFunc([systemfocus_list] { Settings::getInstance()->setString("StartupSystem", systemfocus_list->getSelected()); }); @@ -379,41 +556,47 @@ void GuiMenu::openUISettings() // show help auto show_help = std::make_shared(mWindow); show_help->setState(Settings::getInstance()->getBool("ShowHelpPrompts")); - s->addWithLabel("ON-SCREEN HELP", show_help); - s->addSaveFunc([show_help] { Settings::getInstance()->setBool("ShowHelpPrompts", show_help->getState()); }); + s->addWithLabel(_("ON-SCREEN HELP").c_str(), show_help); + s->addSaveFunc([show_help] { + Settings::getInstance()->setBool("ShowHelpPrompts", show_help->getState()); + }); - // enable filters (ForceDisableFilters) + // enable filters auto enable_filter = std::make_shared(mWindow); enable_filter->setState(!Settings::getInstance()->getBool("ForceDisableFilters")); - s->addWithLabel("ENABLE FILTERS", enable_filter); + s->addWithLabel(_("ENABLE FILTERS").c_str(), enable_filter); s->addSaveFunc([enable_filter] { bool filter_is_enabled = !Settings::getInstance()->getBool("ForceDisableFilters"); Settings::getInstance()->setBool("ForceDisableFilters", !enable_filter->getState()); - if (enable_filter->getState() != filter_is_enabled) ViewController::get()->ReloadAndGoToStart(); + if (enable_filter->getState() != filter_is_enabled) + ViewController::get()->ReloadAndGoToStart(); }); - // hide start menu in Kid Mode + // disable start menu in kid mode auto disable_start = std::make_shared(mWindow); disable_start->setState(Settings::getInstance()->getBool("DisableKidStartMenu")); - s->addWithLabel("DISABLE START MENU IN KID MODE", disable_start); - s->addSaveFunc([disable_start] { Settings::getInstance()->setBool("DisableKidStartMenu", disable_start->getState()); }); + s->addWithLabel(_("DISABLE START MENU IN KID MODE").c_str(), disable_start); + s->addSaveFunc([disable_start] { + Settings::getInstance()->setBool("DisableKidStartMenu", disable_start->getState()); + }); mWindow->pushGui(s); - } void GuiMenu::openOtherSettings() { - auto s = new GuiSettings(mWindow, "OTHER SETTINGS"); + auto s = new GuiSettings(mWindow, _("OTHER SETTINGS").c_str()); // maximum vram auto max_vram = std::make_shared(mWindow, 0.f, 1000.f, 10.f, "Mb"); max_vram->setValue((float)(Settings::getInstance()->getInt("MaxVRAM"))); - s->addWithLabel("VRAM LIMIT", max_vram); - s->addSaveFunc([max_vram] { Settings::getInstance()->setInt("MaxVRAM", (int)Math::round(max_vram->getValue())); }); + s->addWithLabel(_("VRAM LIMIT").c_str(), max_vram); + s->addSaveFunc([max_vram] { + Settings::getInstance()->setInt("MaxVRAM", (int)Math::round(max_vram->getValue())); + }); // power saver - auto power_saver = std::make_shared< OptionListComponent >(mWindow, "POWER SAVER MODES", false); + auto power_saver = std::make_shared< OptionListComponent >(mWindow, _("POWER SAVER MODES").c_str(), false); std::vector modes; modes.push_back("disabled"); modes.push_back("default"); @@ -421,9 +604,10 @@ void GuiMenu::openOtherSettings() modes.push_back("instant"); for (auto it = modes.cbegin(); it != modes.cend(); it++) power_saver->add(*it, *it, Settings::getInstance()->getString("PowerSaverMode") == *it); - s->addWithLabel("POWER SAVER MODES", power_saver); + s->addWithLabel(_("POWER SAVER MODES").c_str(), power_saver); s->addSaveFunc([this, power_saver] { - if (Settings::getInstance()->getString("PowerSaverMode") != "instant" && power_saver->getSelected() == "instant") { + if (Settings::getInstance()->getString("PowerSaverMode") != "instant" && power_saver->getSelected() == "instant") + { Settings::getInstance()->setString("TransitionStyle", "instant"); Settings::getInstance()->setBool("MoveCarousel", false); Settings::getInstance()->setBool("EnableSounds", false); @@ -433,90 +617,92 @@ void GuiMenu::openOtherSettings() }); // gamelists - auto gamelistsSaveMode = std::make_shared< OptionListComponent >(mWindow, "SAVE METADATA", false); + auto gamelistsSaveMode = std::make_shared< OptionListComponent >(mWindow, _("SAVE METADATA").c_str(), false); std::vector saveModes; saveModes.push_back("on exit"); saveModes.push_back("always"); saveModes.push_back("never"); - for(auto it = saveModes.cbegin(); it != saveModes.cend(); it++) + for (auto it = saveModes.cbegin(); it != saveModes.cend(); it++) gamelistsSaveMode->add(*it, *it, Settings::getInstance()->getString("SaveGamelistsMode") == *it); - s->addWithLabel("SAVE METADATA", gamelistsSaveMode); + s->addWithLabel(_("SAVE METADATA").c_str(), gamelistsSaveMode); s->addSaveFunc([gamelistsSaveMode] { Settings::getInstance()->setString("SaveGamelistsMode", gamelistsSaveMode->getSelected()); }); auto parse_gamelists = std::make_shared(mWindow); parse_gamelists->setState(Settings::getInstance()->getBool("ParseGamelistOnly")); - s->addWithLabel("PARSE GAMESLISTS ONLY", parse_gamelists); - s->addSaveFunc([parse_gamelists] { Settings::getInstance()->setBool("ParseGamelistOnly", parse_gamelists->getState()); }); + s->addWithLabel(_("PARSE GAMESLISTS ONLY").c_str(), parse_gamelists); + s->addSaveFunc([parse_gamelists] { + Settings::getInstance()->setBool("ParseGamelistOnly", parse_gamelists->getState()); + }); auto local_art = std::make_shared(mWindow); local_art->setState(Settings::getInstance()->getBool("LocalArt")); - s->addWithLabel("SEARCH FOR LOCAL ART", local_art); - s->addSaveFunc([local_art] { Settings::getInstance()->setBool("LocalArt", local_art->getState()); }); + s->addWithLabel(_("SEARCH FOR LOCAL ART").c_str(), local_art); + s->addSaveFunc([local_art] { + Settings::getInstance()->setBool("LocalArt", local_art->getState()); + }); // hidden files auto hidden_files = std::make_shared(mWindow); hidden_files->setState(Settings::getInstance()->getBool("ShowHiddenFiles")); - s->addWithLabel("SHOW HIDDEN FILES", hidden_files); - s->addSaveFunc([hidden_files] { Settings::getInstance()->setBool("ShowHiddenFiles", hidden_files->getState()); }); + s->addWithLabel(_("SHOW HIDDEN FILES").c_str(), hidden_files); + s->addSaveFunc([hidden_files] { + Settings::getInstance()->setBool("ShowHiddenFiles", hidden_files->getState()); + }); #ifdef _OMX_ // Video Player - VideoOmxPlayer auto omx_player = std::make_shared(mWindow); omx_player->setState(Settings::getInstance()->getBool("VideoOmxPlayer")); - s->addWithLabel("USE OMX PLAYER (HW ACCELERATED)", omx_player); + s->addWithLabel(_("USE OMX PLAYER (HW ACCELERATED)").c_str(), omx_player); s->addSaveFunc([omx_player] { - // need to reload all views to re-create the right video components bool needReload = false; - if(Settings::getInstance()->getBool("VideoOmxPlayer") != omx_player->getState()) + if (Settings::getInstance()->getBool("VideoOmxPlayer") != omx_player->getState()) needReload = true; Settings::getInstance()->setBool("VideoOmxPlayer", omx_player->getState()); - if(needReload) + if (needReload) ViewController::get()->reloadAll(); }); - #endif - // hidden files auto background_indexing = std::make_shared(mWindow); background_indexing->setState(Settings::getInstance()->getBool("BackgroundIndexing")); - s->addWithLabel("INDEX FILES DURING SCREENSAVER", background_indexing); - s->addSaveFunc([background_indexing] { Settings::getInstance()->setBool("BackgroundIndexing", background_indexing->getState()); }); + s->addWithLabel(_("INDEX FILES DURING SCREENSAVER").c_str(), background_indexing); + s->addSaveFunc([background_indexing] { + Settings::getInstance()->setBool("BackgroundIndexing", background_indexing->getState()); + }); - // framerate auto framerate = std::make_shared(mWindow); framerate->setState(Settings::getInstance()->getBool("DrawFramerate")); - s->addWithLabel("SHOW FRAMERATE", framerate); - s->addSaveFunc([framerate] { Settings::getInstance()->setBool("DrawFramerate", framerate->getState()); }); - + s->addWithLabel(_("SHOW FRAMERATE").c_str(), framerate); + s->addSaveFunc([framerate] { + Settings::getInstance()->setBool("DrawFramerate", framerate->getState()); + }); mWindow->pushGui(s); - } void GuiMenu::openConfigInput() { Window* window = mWindow; - window->pushGui(new GuiMsgBox(window, "ARE YOU SURE YOU WANT TO CONFIGURE INPUT?", "YES", + window->pushGui(new GuiMsgBox(window, _("ARE YOU SURE YOU WANT TO CONFIGURE INPUT?"), + _("YES").c_str(), [window] { - window->pushGui(new GuiDetectDevice(window, false, nullptr)); - }, "NO", nullptr) + window->pushGui(new GuiDetectDevice(window, false, nullptr)); + }, _("NO").c_str(), nullptr) ); - } void GuiMenu::openQuitMenu() { - auto s = new GuiSettings(mWindow, "QUIT"); + auto s = new GuiSettings(mWindow, _("QUIT").c_str()); Window* window = mWindow; - - // command line switch bool confirm_quit = Settings::getInstance()->getBool("ConfirmQuit"); ComponentListRow row; @@ -524,22 +710,26 @@ void GuiMenu::openQuitMenu() { auto static restart_es_fx = []() { Scripting::fireEvent("quit"); - if (quitES(QuitMode::RESTART)) { + if (quitES(QuitMode::RESTART)) + { LOG(LogWarning) << "Restart terminated with non-zero result!"; } }; - if (confirm_quit) { + if (confirm_quit) + { row.makeAcceptInputHandler([window] { - window->pushGui(new GuiMsgBox(window, "REALLY RESTART?", "YES", restart_es_fx, "NO", nullptr)); + window->pushGui(new GuiMsgBox(window, _("REALLY RESTART?"), _("YES").c_str(), restart_es_fx, _("NO").c_str(), nullptr)); }); - } else { + } + else + { row.makeAcceptInputHandler(restart_es_fx); } - row.addElement(std::make_shared(window, "RESTART EMULATIONSTATION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared(window, _("RESTART EMULATIONSTATION"), Font::get(FONT_SIZE_MEDIUM), getMenuTextColor()), true); s->addRow(row); - if(Settings::getInstance()->getBool("ShowExit")) + if (Settings::getInstance()->getBool("ShowExit")) { auto static quit_es_fx = [] { Scripting::fireEvent("quit"); @@ -547,14 +737,17 @@ void GuiMenu::openQuitMenu() }; row.elements.clear(); - if (confirm_quit) { + if (confirm_quit) + { row.makeAcceptInputHandler([window] { - window->pushGui(new GuiMsgBox(window, "REALLY QUIT?", "YES", quit_es_fx, "NO", nullptr)); + window->pushGui(new GuiMsgBox(window, _("REALLY QUIT?"), _("YES").c_str(), quit_es_fx, _("NO").c_str(), nullptr)); }); - } else { + } + else + { row.makeAcceptInputHandler(quit_es_fx); } - row.addElement(std::make_shared(window, "QUIT EMULATIONSTATION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared(window, _("QUIT EMULATIONSTATION"), Font::get(FONT_SIZE_MEDIUM), getMenuTextColor()), true); s->addRow(row); } } @@ -562,62 +755,79 @@ void GuiMenu::openQuitMenu() auto static reboot_sys_fx = [] { Scripting::fireEvent("quit", "reboot"); Scripting::fireEvent("reboot"); - if (quitES(QuitMode::REBOOT)) { + if (quitES(QuitMode::REBOOT)) + { LOG(LogWarning) << "Restart terminated with non-zero result!"; } }; row.elements.clear(); - if (confirm_quit) { + if (confirm_quit) + { row.makeAcceptInputHandler([window] { - window->pushGui(new GuiMsgBox(window, "REALLY RESTART?", "YES", {reboot_sys_fx}, "NO", nullptr)); + window->pushGui(new GuiMsgBox(window, _("REALLY RESTART?"), _("YES").c_str(), { reboot_sys_fx }, _("NO").c_str(), nullptr)); }); - } else { + } + else + { row.makeAcceptInputHandler(reboot_sys_fx); } - row.addElement(std::make_shared(window, "RESTART SYSTEM", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared(window, _("RESTART SYSTEM"), Font::get(FONT_SIZE_MEDIUM), getMenuTextColor()), true); s->addRow(row); auto static shutdown_sys_fx = [] { Scripting::fireEvent("quit", "shutdown"); Scripting::fireEvent("shutdown"); - if (quitES(QuitMode::SHUTDOWN)) { + if (quitES(QuitMode::SHUTDOWN)) + { LOG(LogWarning) << "Shutdown terminated with non-zero result!"; } }; row.elements.clear(); - if (confirm_quit) { + if (confirm_quit) + { row.makeAcceptInputHandler([window] { - window->pushGui(new GuiMsgBox(window, "REALLY SHUTDOWN?", "YES", shutdown_sys_fx, "NO", nullptr)); + window->pushGui(new GuiMsgBox(window, _("REALLY SHUTDOWN?"), _("YES").c_str(), shutdown_sys_fx, _("NO").c_str(), nullptr)); }); - } else { + } + else + { row.makeAcceptInputHandler(shutdown_sys_fx); } - row.addElement(std::make_shared(window, "SHUTDOWN SYSTEM", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared(window, _("SHUTDOWN SYSTEM"), Font::get(FONT_SIZE_MEDIUM), getMenuTextColor()), true); s->addRow(row); + mWindow->pushGui(s); } void GuiMenu::addVersionInfo() { - std::string buildDate = (Settings::getInstance()->getBool("Debug") ? std::string( " (" + Utils::String::toUpper(PROGRAM_BUILT_STRING) + ")") : ("")); + std::string buildDate = (Settings::getInstance()->getBool("Debug") ? std::string(" (" + Utils::String::toUpper(PROGRAM_BUILT_STRING) + ")") : ("")); mVersion.setFont(Font::get(FONT_SIZE_SMALL)); - mVersion.setColor(0x5E5E5EFF); + mVersion.setColor(getVersionTextColor()); mVersion.setText("EMULATIONSTATION V" + Utils::String::toUpper(PROGRAM_VERSION_STRING) + buildDate); mVersion.setHorizontalAlignment(ALIGN_CENTER); addChild(&mVersion); } -void GuiMenu::openScreensaverOptions() { - mWindow->pushGui(new GuiGeneralScreensaverOptions(mWindow, "SCREENSAVER SETTINGS")); +void GuiMenu::openScreensaverOptions() +{ + mWindow->pushGui(new GuiGeneralScreensaverOptions(mWindow, _("SCREENSAVER SETTINGS").c_str())); } -void GuiMenu::openCollectionSystemSettings() { +void GuiMenu::openCollectionSystemSettings() +{ mWindow->pushGui(new GuiCollectionSystemsOptions(mWindow)); } +// NUEVO: lógica para abrir la GUI de opciones de tema +void GuiMenu::openThemeOptions() +{ + mWindow->pushGui(new GuiThemeOptions(mWindow)); +} + void GuiMenu::onSizeChanged() { mVersion.setSize(mSize.x(), 0); @@ -628,28 +838,29 @@ void GuiMenu::addEntry(const char* name, unsigned int color, bool add_arrow, con { std::shared_ptr font = Font::get(FONT_SIZE_MEDIUM); - // populate the list ComponentListRow row; row.addElement(std::make_shared(mWindow, name, font, color), true); - if(add_arrow) + if (add_arrow) { std::shared_ptr bracket = makeArrow(mWindow); row.addElement(bracket, false); } row.makeAcceptInputHandler(func); - mMenu.addRow(row); } bool GuiMenu::input(InputConfig* config, Input input) { - if(GuiComponent::input(config, input)) + if (GuiComponent::input(config, input)) return true; - if((config->isMappedTo("b", input) || config->isMappedTo("start", input)) && input.value != 0) + if ((config->isMappedTo("b", input) || config->isMappedTo("start", input)) && input.value != 0) { + // Sonido de BACK vía NavigationSounds ("back") + playMenuBackSound(); + delete this; return true; } @@ -667,8 +878,8 @@ HelpStyle GuiMenu::getHelpStyle() std::vector GuiMenu::getHelpPrompts() { std::vector prompts; - prompts.push_back(HelpPrompt("up/down", "choose")); - prompts.push_back(HelpPrompt("a", "select")); - prompts.push_back(HelpPrompt("start", "close")); + prompts.push_back(HelpPrompt("up/down", _("CHOOSE"))); + prompts.push_back(HelpPrompt("a", _("SELECT"))); + prompts.push_back(HelpPrompt("start", _("CLOSE"))); return prompts; } diff --git a/es-app/src/guis/GuiMenu.h b/es-app/src/guis/GuiMenu.h index f251a97123..82a960f77f 100644 --- a/es-app/src/guis/GuiMenu.h +++ b/es-app/src/guis/GuiMenu.h @@ -2,38 +2,44 @@ #ifndef ES_APP_GUIS_GUI_MENU_H #define ES_APP_GUIS_GUI_MENU_H -#include "components/MenuComponent.h" #include "GuiComponent.h" -#include "components/OptionListComponent.h" -#include "FileData.h" +#include "components/MenuComponent.h" +#include "components/TextComponent.h" +#include "HelpStyle.h" +#include "HelpPrompt.h" class GuiMenu : public GuiComponent { public: GuiMenu(Window* window); + virtual ~GuiMenu() {} bool input(InputConfig* config, Input input) override; - void onSizeChanged() override; std::vector getHelpPrompts() override; HelpStyle getHelpStyle() override; private: - void addEntry(const char* name, unsigned int color, bool add_arrow, const std::function& func); - void addVersionInfo(); - void openCollectionSystemSettings(); - void openConfigInput(); - void openOtherSettings(); - void openQuitMenu(); + MenuComponent mMenu; + TextComponent mVersion; + + // Bloques del menú principal void openScraperSettings(); - void openScreensaverOptions(); void openSoundSettings(); void openUISettings(); + void openOtherSettings(); + void openConfigInput(); + void openQuitMenu(); + void openCollectionSystemSettings(); + void openScreensaverOptions(); - MenuComponent mMenu; - TextComponent mVersion; + // NUEVO: opciones de tema internas (GuiThemeOptions) + void openThemeOptions(); + + // Helpers internos + void addEntry(const char* name, unsigned int color, bool add_arrow, const std::function& func); + void addVersionInfo(); - typedef OptionListComponent SortList; - std::shared_ptr mListSort; + void onSizeChanged() override; }; #endif // ES_APP_GUIS_GUI_MENU_H diff --git a/es-app/src/guis/GuiScraperMulti.cpp b/es-app/src/guis/GuiScraperMulti.cpp index 28b87db40f..dd3c6f3e53 100644 --- a/es-app/src/guis/GuiScraperMulti.cpp +++ b/es-app/src/guis/GuiScraperMulti.cpp @@ -10,6 +10,7 @@ #include "PowerSaver.h" #include "SystemData.h" #include "Window.h" +#include "LocaleESHook.h" // ← traducción (es_translate) GuiScraperMulti::GuiScraperMulti(Window* window, const std::queue& searches, bool approveResults) : GuiComponent(window), mBackground(window, ":/frame.png"), mGrid(window, Vector2i(1, 5)), @@ -28,45 +29,86 @@ GuiScraperMulti::GuiScraperMulti(Window* window, const std::queue(mWindow, "SCRAPING IN PROGRESS", Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); + // título + mTitle = std::make_shared( + mWindow, + es_translate("SCRAPING IN PROGRESS"), + Font::get(FONT_SIZE_LARGE), + 0x555555FF, + ALIGN_CENTER); mGrid.setEntry(mTitle, Vector2i(0, 0), false, true); - mSystem = std::make_shared(mWindow, "SYSTEM", Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER); + // sistema + mSystem = std::make_shared( + mWindow, + es_translate("SYSTEM"), + Font::get(FONT_SIZE_MEDIUM), + 0x777777FF, + ALIGN_CENTER); mGrid.setEntry(mSystem, Vector2i(0, 1), false, true); - mSubtitle = std::make_shared(mWindow, "subtitle text", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER); + // subtítulo (se actualiza en doNextSearch) + mSubtitle = std::make_shared( + mWindow, + "", + Font::get(FONT_SIZE_SMALL), + 0x888888FF, + ALIGN_CENTER); mGrid.setEntry(mSubtitle, Vector2i(0, 2), false, true); - mSearchComp = std::make_shared(mWindow, - approveResults ? ScraperSearchComponent::ALWAYS_ACCEPT_MATCHING_CRC : ScraperSearchComponent::ALWAYS_ACCEPT_FIRST_RESULT); + // componente de búsqueda + mSearchComp = std::make_shared( + mWindow, + approveResults ? ScraperSearchComponent::ALWAYS_ACCEPT_MATCHING_CRC + : ScraperSearchComponent::ALWAYS_ACCEPT_FIRST_RESULT); mSearchComp->setAcceptCallback(std::bind(&GuiScraperMulti::acceptResult, this, std::placeholders::_1)); mSearchComp->setSkipCallback(std::bind(&GuiScraperMulti::skip, this)); mSearchComp->setCancelCallback(std::bind(&GuiScraperMulti::finish, this)); - mGrid.setEntry(mSearchComp, Vector2i(0, 3), mSearchComp->getSearchType() != ScraperSearchComponent::ALWAYS_ACCEPT_FIRST_RESULT, true); + mGrid.setEntry( + mSearchComp, + Vector2i(0, 3), + mSearchComp->getSearchType() != ScraperSearchComponent::ALWAYS_ACCEPT_FIRST_RESULT, + true); std::vector< std::shared_ptr > buttons; - if(approveResults) + if (approveResults) { - buttons.push_back(std::make_shared(mWindow, "INPUT", "search", [&] { - mSearchComp->openInputScreen(mSearchQueue.front()); - mGrid.resetCursor(); - })); - - buttons.push_back(std::make_shared(mWindow, "SKIP", "skip", [&] { - skip(); - mGrid.resetCursor(); - })); + // INPUT + buttons.push_back(std::make_shared( + mWindow, + es_translate("INPUT"), + es_translate("SEARCH"), + [&] { + mSearchComp->openInputScreen(mSearchQueue.front()); + mGrid.resetCursor(); + })); + + // SKIP + buttons.push_back(std::make_shared( + mWindow, + es_translate("SKIP"), + es_translate("SKIP"), + [&] { + skip(); + mGrid.resetCursor(); + })); } - buttons.push_back(std::make_shared(mWindow, "STOP", "stop (progress saved)", std::bind(&GuiScraperMulti::finish, this))); + // STOP + buttons.push_back(std::make_shared( + mWindow, + es_translate("STOP"), + es_translate("STOP (PROGRESS SAVED)"), + std::bind(&GuiScraperMulti::finish, this))); mButtonGrid = makeButtonGrid(mWindow, buttons); mGrid.setEntry(mButtonGrid, Vector2i(0, 4), true, false); setSize(Renderer::getScreenWidth() * 0.95f, Renderer::getScreenHeight() * 0.849f); - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2); + setPosition( + (Renderer::getScreenWidth() - mSize.x()) / 2, + (Renderer::getScreenHeight() - mSize.y()) / 2); doNextSearch(); } @@ -74,7 +116,7 @@ GuiScraperMulti::GuiScraperMulti(Window* window, const std::queue detailed) - for(auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++) + for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++) ViewController::get()->reloadGameListView(*it, false); } @@ -91,19 +133,25 @@ void GuiScraperMulti::onSizeChanged() void GuiScraperMulti::doNextSearch() { - if(mSearchQueue.empty()) + if (mSearchQueue.empty()) { finish(); return; } - // update title - std::stringstream ss; + // sistema actual mSystem->setText(Utils::String::toUpper(mSearchQueue.front().system->getFullName())); - // update subtitle - ss.str(""); // clear - ss << "GAME " << (mCurrentGame + 1) << " OF " << mTotalGames << " - " << Utils::String::toUpper(Utils::FileSystem::getFileName(mSearchQueue.front().game->getPath())); + // subtítulo: "GAME X OF Y - NOMBRE.EXT" + std::stringstream ss; + std::string gameLabel = es_translate("GAME"); + std::string ofLabel = es_translate("OF"); + + ss << gameLabel << " " << (mCurrentGame + 1) + << " " << ofLabel << " " << mTotalGames + << " - " + << Utils::String::toUpper(Utils::FileSystem::getFileName(mSearchQueue.front().game->getPath())); + mSubtitle->setText(ss.str()); mSearchComp->search(mSearchQueue.front()); @@ -133,18 +181,35 @@ void GuiScraperMulti::skip() void GuiScraperMulti::finish() { std::stringstream ss; - if(mTotalSuccessful == 0) - { - ss << "NO GAMES WERE SCRAPED."; - }else{ - ss << mTotalSuccessful << " GAME" << ((mTotalSuccessful > 1) ? "S" : "") << " SUCCESSFULLY SCRAPED!"; - if(mTotalSkipped > 0) - ss << "\n" << mTotalSkipped << " GAME" << ((mTotalSkipped > 1) ? "S" : "") << " SKIPPED."; + if (mTotalSuccessful == 0) + { + ss << es_translate("NO GAMES WERE SCRAPED."); + } + else + { + // X GAME(S) SUCCESSFULLY SCRAPED! + if (mTotalSuccessful == 1) + ss << mTotalSuccessful << " " << es_translate("GAME SCRAPED"); + else + ss << mTotalSuccessful << " " << es_translate("GAMES SCRAPED"); + + // Y GAME(S) SKIPPED. + if (mTotalSkipped > 0) + { + ss << "\n"; + if (mTotalSkipped == 1) + ss << mTotalSkipped << " " << es_translate("GAME SKIPPED"); + else + ss << mTotalSkipped << " " << es_translate("GAMES SKIPPED"); + } } - mWindow->pushGui(new GuiMsgBox(mWindow, ss.str(), - "OK", [&] { delete this; })); + mWindow->pushGui(new GuiMsgBox( + mWindow, + ss.str(), + es_translate("OK"), + [&] { delete this; })); mIsProcessing = false; PowerSaver::resume(); diff --git a/es-app/src/guis/GuiScraperStart.cpp b/es-app/src/guis/GuiScraperStart.cpp index 2242046901..84b7dd155a 100644 --- a/es-app/src/guis/GuiScraperStart.cpp +++ b/es-app/src/guis/GuiScraperStart.cpp @@ -7,122 +7,125 @@ #include "views/ViewController.h" #include "FileData.h" #include "SystemData.h" +#include "LocaleESHook.h" // 🔹 Traducción disponible (ruta correcta) GuiScraperStart::GuiScraperStart(Window* window) : GuiComponent(window), - mMenu(window, "SCRAPE NOW") + mMenu(window, es_translate("SCRAPE NOW").c_str()) { - addChild(&mMenu); - - // add filters (with first one selected) - mFilters = std::make_shared< OptionListComponent >(mWindow, "SCRAPE THESE GAMES", false); - mFilters->add("All Games", - [](SystemData*, FileData*) -> bool { return true; }, false); - mFilters->add("Only missing image", - [](SystemData*, FileData* g) -> bool { return g->metadata.get("image").empty(); }, true); - mMenu.addWithLabel("Filter", mFilters); - - //add systems (all with a platformid specified selected) - mSystems = std::make_shared< OptionListComponent >(mWindow, "SCRAPE THESE SYSTEMS", true); - for(auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++) - { - if(!(*it)->hasPlatformId(PlatformIds::PLATFORM_IGNORE)) - mSystems->add((*it)->getFullName(), *it, !(*it)->getPlatformIds().empty()); - } - mMenu.addWithLabel("Systems", mSystems); - - mApproveResults = std::make_shared(mWindow); - mApproveResults->setState(true); - mMenu.addWithLabel("User decides on conflicts", mApproveResults); - - mMenu.addButton("START", "start", std::bind(&GuiScraperStart::pressedStart, this)); - mMenu.addButton("BACK", "back", [&] { delete this; }); - - mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2, Renderer::getScreenHeight() * 0.15f); + addChild(&mMenu); + + // Filters + mFilters = std::make_shared< OptionListComponent >(mWindow, es_translate("SCRAPE THESE GAMES"), false); + mFilters->add(es_translate("ALL GAMES"), + [](SystemData*, FileData*) -> bool { return true; }, false); + mFilters->add(es_translate("ONLY MISSING IMAGE"), + [](SystemData*, FileData* g) -> bool { return g->metadata.get("image").empty(); }, true); + mMenu.addWithLabel(es_translate("FILTER"), mFilters); + + // Systems + mSystems = std::make_shared< OptionListComponent >(mWindow, es_translate("SCRAPE THESE SYSTEMS"), true); + for(auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++) + { + if(!(*it)->hasPlatformId(PlatformIds::PLATFORM_IGNORE)) + mSystems->add((*it)->getFullName(), *it, !(*it)->getPlatformIds().empty()); + } + mMenu.addWithLabel(es_translate("SYSTEMS"), mSystems); + + // Conflicts + mApproveResults = std::make_shared(mWindow); + mApproveResults->setState(true); + mMenu.addWithLabel(es_translate("USER DECIDES ON CONFLICTS"), mApproveResults); + + // Buttons + mMenu.addButton(es_translate("START"), es_translate("START"), std::bind(&GuiScraperStart::pressedStart, this)); + mMenu.addButton(es_translate("BACK"), es_translate("BACK"), [&] { delete this; }); + + mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2, + Renderer::getScreenHeight() * 0.15f); } void GuiScraperStart::pressedStart() { - std::vector sys = mSystems->getSelectedObjects(); - for(auto it = sys.cbegin(); it != sys.cend(); it++) - { - if((*it)->getPlatformIds().empty()) - { - mWindow->pushGui(new GuiMsgBox(mWindow, - Utils::String::toUpper("Warning: some of your selected systems do not have a platform set. Results may be even more inaccurate than usual!\nContinue anyway?"), - "YES", std::bind(&GuiScraperStart::start, this), - "NO", nullptr)); - return; - } - } - - start(); + std::vector sys = mSystems->getSelectedObjects(); + for(auto it = sys.cbegin(); it != sys.cend(); it++) + { + if((*it)->getPlatformIds().empty()) + { + mWindow->pushGui(new GuiMsgBox(mWindow, + es_translate("WARNING: SOME SELECTED SYSTEMS DO NOT HAVE A PLATFORM SET. RESULTS MAY BE INACCURATE.\nCONTINUE ANYWAY?"), + es_translate("YES"), std::bind(&GuiScraperStart::start, this), + es_translate("NO"), nullptr)); + return; + } + } + + start(); } void GuiScraperStart::start() { - std::queue searches = getSearches(mSystems->getSelectedObjects(), mFilters->getSelected()); - - if(searches.empty()) - { - mWindow->pushGui(new GuiMsgBox(mWindow, - "NO GAMES FIT THAT CRITERIA.")); - }else{ - GuiScraperMulti* gsm = new GuiScraperMulti(mWindow, searches, mApproveResults->getState()); - mWindow->pushGui(gsm); - delete this; - } + std::queue searches = getSearches(mSystems->getSelectedObjects(), mFilters->getSelected()); + + if(searches.empty()) + { + mWindow->pushGui(new GuiMsgBox(mWindow, es_translate("NO GAMES FIT THAT CRITERIA."))); + } + else + { + GuiScraperMulti* gsm = new GuiScraperMulti(mWindow, searches, mApproveResults->getState()); + mWindow->pushGui(gsm); + delete this; + } } std::queue GuiScraperStart::getSearches(std::vector systems, GameFilterFunc selector) { - std::queue queue; - for(auto sys = systems.cbegin(); sys != systems.cend(); sys++) - { - std::vector games = (*sys)->getRootFolder()->getFilesRecursive(GAME); - for(auto game = games.cbegin(); game != games.cend(); game++) - { - if(selector((*sys), (*game))) - { - ScraperSearchParams search; - search.game = *game; - search.system = *sys; - - queue.push(search); - } - } - } - - return queue; + std::queue queue; + for(auto sys = systems.cbegin(); sys != systems.cend(); sys++) + { + std::vector games = (*sys)->getRootFolder()->getFilesRecursive(GAME); + for(auto game = games.cbegin(); game != games.cend(); game++) + { + if(selector((*sys), (*game))) + { + ScraperSearchParams search; + search.game = *game; + search.system = *sys; + queue.push(search); + } + } + } + + return queue; } bool GuiScraperStart::input(InputConfig* config, Input input) { - bool consumed = GuiComponent::input(config, input); - if(consumed) - return true; - - if(input.value != 0 && config->isMappedTo("b", input)) - { - delete this; - return true; - } - - if(config->isMappedTo("start", input) && input.value != 0) - { - // close everything - Window* window = mWindow; - while(window->peekGui() && window->peekGui() != ViewController::get()) - delete window->peekGui(); - } - - - return false; + bool consumed = GuiComponent::input(config, input); + if(consumed) + return true; + + if(input.value != 0 && config->isMappedTo("b", input)) + { + delete this; + return true; + } + + if(config->isMappedTo("start", input) && input.value != 0) + { + // close everything + Window* window = mWindow; + while(window->peekGui() && window->peekGui() != ViewController::get()) + delete window->peekGui(); + } + + return false; } std::vector GuiScraperStart::getHelpPrompts() { - std::vector prompts = mMenu.getHelpPrompts(); - prompts.push_back(HelpPrompt("b", "back")); - prompts.push_back(HelpPrompt("start", "close")); - return prompts; + std::vector prompts = mMenu.getHelpPrompts(); + prompts.push_back(HelpPrompt("b", es_translate("BACK"))); + prompts.push_back(HelpPrompt("start", es_translate("CLOSE"))); + return prompts; } diff --git a/es-app/src/guis/GuiThemeOptions.cpp b/es-app/src/guis/GuiThemeOptions.cpp new file mode 100644 index 0000000000..600e3749ea --- /dev/null +++ b/es-app/src/guis/GuiThemeOptions.cpp @@ -0,0 +1,790 @@ +// es-app/src/guis/GuiThemeOptions.cpp + +#include "guis/GuiThemeOptions.h" +#include "guis/GuiMsgBox.h" +#include "guis/GuiTextEditPopup.h" + +#include "LocaleES.h" +#include "Settings.h" +#include "utils/FileSystemUtil.h" +#include "resources/Font.h" +#include "views/ViewController.h" +#include "SystemData.h" +#include "Window.h" +#include "renderers/Renderer.h" + +#include +#include +#include +#include + +namespace +{ + inline std::string _(const std::string& key) + { + return LocaleES::getInstance().translate(key); + } + + struct ThemeOption + { + std::string id; + std::string type; + std::string label; + std::string path; + std::string applyTo; + + // Optional output key for theme.ini (e.g. key=USER_NAME) + std::string key; + + std::vector> values; + std::string defaultValue; + }; + + // ----------------------------- + // Small helpers (ES-X compatible) + // ----------------------------- + static std::string trim(const std::string& s) + { + size_t start = s.find_first_not_of(" \t\r\n"); + if(start == std::string::npos) + return ""; + size_t end = s.find_last_not_of(" \t\r\n"); + return s.substr(start, end - start + 1); + } + + static bool isAbsolutePathSimple(const std::string& p) + { + // Linux/Unix absolute + return !p.empty() && p[0] == '/'; + } + + static std::string joinPathSimple(const std::string& a, const std::string& b) + { + if(a.empty()) return b; + if(b.empty()) return a; + + if(a.back() == '/' && b.front() == '/') + return a + b.substr(1); + if(a.back() != '/' && b.front() != '/') + return a + "/" + b; + return a + b; + } + + static std::string normalizeRelative(const std::string& p) + { + // remove leading "./" + if(p.rfind("./", 0) == 0) + return p.substr(2); + return p; + } + + static std::string resolveThemePath(const std::string& themeDir, const std::string& maybeRelative) + { + if(maybeRelative.empty()) + return ""; + + if(isAbsolutePathSimple(maybeRelative)) + return maybeRelative; + + return joinPathSimple(themeDir, normalizeRelative(maybeRelative)); + } + + static bool copyFileForce(const std::string& src, const std::string& dst) + { + std::ifstream in(src.c_str(), std::ios::binary); + if(!in) + return false; + + std::ofstream out(dst.c_str(), std::ios::binary | std::ios::trunc); + if(!out) + return false; + + out << in.rdbuf(); + return out.good(); + } + + static void parseValues(const std::string& str, std::vector>& out) + { + out.clear(); + std::stringstream ss(str); + std::string item; + + while(std::getline(ss, item, ',')) + { + item = trim(item); + if(item.empty()) + continue; + + auto pos = item.find('|'); + if(pos == std::string::npos) + out.emplace_back(item, item); + else + { + std::string internal = trim(item.substr(0, pos)); + std::string label = trim(item.substr(pos + 1)); + if(!internal.empty() && !label.empty()) + out.emplace_back(internal, label); + } + } + } + + // ------------------------------------------------------------ + // Read INI sections into a map: section -> (key->value) + // ------------------------------------------------------------ + static std::map> + loadIniSections(const std::string& iniPath) + { + std::map> data; + + std::ifstream file(iniPath.c_str()); + if(!file) + return data; + + std::string line; + std::string currentSection; + + while(std::getline(file, line)) + { + line = trim(line); + if(line.empty() || line[0] == ';' || line[0] == '#') + continue; + + if(line.front() == '[' && line.back() == ']') + { + currentSection = trim(line.substr(1, line.size() - 2)); + continue; + } + + auto pos = line.find('='); + if(pos == std::string::npos) + continue; + + std::string key = trim(line.substr(0, pos)); + std::string val = trim(line.substr(pos + 1)); + + if(!currentSection.empty() && !key.empty()) + data[currentSection][key] = val; + } + + return data; + } + + // ------------------------------------------------------------ + // Read top-level key=value (before first [section]) + // ------------------------------------------------------------ + static std::string readTopIniValue(const std::string& iniPath, const std::string& key) + { + std::ifstream in(iniPath.c_str()); + if(!in) + return ""; + + std::string line; + bool inTop = true; + + while(std::getline(in, line)) + { + std::string t = trim(line); + + if(inTop && !t.empty() && t.front() == '[') + break; + + if(t.empty() || t[0] == ';' || t[0] == '#') + continue; + + auto posEq = t.find('='); + if(posEq == std::string::npos) + continue; + + std::string thisKey = trim(t.substr(0, posEq)); + if(thisKey == key) + return trim(t.substr(posEq + 1)); + } + + return ""; + } + + static void updateThemeIniValue(const std::string& iniPath, const std::string& key, const std::string& value) + { + std::ifstream in(iniPath.c_str()); + if(!in) + return; + + std::vector lines; + std::string line; + bool updated = false; + bool inTop = true; + + while(std::getline(in, line)) + { + std::string t = trim(line); + + if(inTop && !t.empty() && t.front() == '[') + inTop = false; + + if(inTop) + { + auto posEq = t.find('='); + if(posEq != std::string::npos) + { + std::string thisKey = trim(t.substr(0, posEq)); + if(thisKey == key) + { + line = key + " = " + value; + updated = true; + } + } + } + lines.push_back(line); + } + in.close(); + + if(!updated) + { + size_t insertPos = lines.size(); + for(size_t i = 0; i < lines.size(); ++i) + { + std::string t = trim(lines[i]); + if(!t.empty() && t.front() == '[') + { + insertPos = i; + break; + } + } + lines.insert(lines.begin() + insertPos, key + " = " + value); + } + + std::ofstream out(iniPath.c_str(), std::ios::trunc); + if(!out) + return; + for(const auto& l : lines) + out << l << "\n"; + } + + // ------------------------------------------------------------ + // Auto-detect /layout subfolders and add [layout] option if missing + // ------------------------------------------------------------ + static void ensureLayoutOptionIfLayoutFolderExists(const std::string& themeDir, std::vector& options) + { + const std::string layoutDir = themeDir + "/layout"; + if(!Utils::FileSystem::isDirectory(layoutDir)) + return; + + for(const auto& o : options) + if(o.id == "layout") + return; + + Utils::FileSystem::stringList lst = Utils::FileSystem::getDirContent(layoutDir); + std::vector dirs(lst.begin(), lst.end()); + + std::vector layouts; + for(const auto& name : dirs) + { + if(name.empty() || name[0] == '.') + continue; + + const std::string full = layoutDir + "/" + name; + if(Utils::FileSystem::isDirectory(full)) + layouts.push_back(name); + } + + if(layouts.empty()) + return; + + std::sort(layouts.begin(), layouts.end()); + + ThemeOption layoutOpt; + layoutOpt.id = "layout"; + layoutOpt.type = "select"; + layoutOpt.applyTo = "layout"; + layoutOpt.label = _("THEME LAYOUT"); + + for(const auto& l : layouts) + layoutOpt.values.emplace_back(l, l); + + const std::string current = Settings::getInstance()->getString("ThemeLayout"); + layoutOpt.defaultValue = !current.empty() ? current : layouts.front(); + + options.insert(options.begin(), layoutOpt); + } + + // ------------------------------------------------------------ + // Auto-detect /_inc/avatars images and add [avatar] option if missing + // ------------------------------------------------------------ + static bool hasImageExt(const std::string& f) + { + auto lower = f; + std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); + + return (lower.size() >= 4 && lower.rfind(".png") == lower.size() - 4) || + (lower.size() >= 4 && lower.rfind(".jpg") == lower.size() - 4) || + (lower.size() >= 5 && lower.rfind(".jpeg") == lower.size() - 5); + } + + static std::string stripExt(const std::string& f) + { + auto pos = f.find_last_of('.'); + if(pos == std::string::npos) return f; + return f.substr(0, pos); + } + + static void ensureAvatarOptionIfAvatarFolderExists(const std::string& themeDir, std::vector& options) + { + // If theme.ini already declared [avatar], do nothing. + for(const auto& o : options) + if(o.id == "avatar") + return; + + const std::string avatarDir = themeDir + "/_inc/avatars"; + if(!Utils::FileSystem::isDirectory(avatarDir)) + return; + + Utils::FileSystem::stringList lst = Utils::FileSystem::getDirContent(avatarDir); + std::vector files(lst.begin(), lst.end()); + + std::vector imgs; + for(const auto& name : files) + { + if(name.empty() || name[0] == '.') + continue; + + const std::string full = avatarDir + "/" + name; + if(Utils::FileSystem::isRegularFile(full) && hasImageExt(name)) + imgs.push_back(name); + } + + if(imgs.empty()) + return; + + std::sort(imgs.begin(), imgs.end()); + + ThemeOption opt; + opt.id = "avatar"; + opt.type = "select"; + opt.applyTo = "theme_ini"; + opt.key = "AVATAR_PATH"; + opt.label = _("AVATAR"); + + for(const auto& f : imgs) + { + const std::string relPath = "./_inc/avatars/" + f; + const std::string nice = stripExt(f); + opt.values.emplace_back(relPath, nice); + } + + const std::string iniPath = themeDir + "/theme.ini"; + std::string current = readTopIniValue(iniPath, opt.key); + if(!current.empty()) + opt.defaultValue = current; + else + opt.defaultValue = opt.values.front().first; + + options.push_back(opt); + } + + // ------------------------------------------------------------ + // Load theme.ini options + // ------------------------------------------------------------ + static std::vector loadThemeOptionsFromIni(const std::string& themeDir) + { + std::vector options; + const std::string iniPath = themeDir + "/theme.ini"; + + if(Utils::FileSystem::isRegularFile(iniPath)) + { + std::ifstream file(iniPath.c_str()); + if(file) + { + ThemeOption current; + std::string line; + + auto pushIfValid = [&options](const ThemeOption& opt) + { + if(opt.id.empty() || opt.type.empty()) + return; + + std::string t = opt.type; + std::transform(t.begin(), t.end(), t.begin(), ::tolower); + + // select requires values, input does not + if(t == "select") + { + if(!opt.values.empty()) + options.push_back(opt); + } + else if(t == "input") + { + options.push_back(opt); + } + }; + + while(std::getline(file, line)) + { + line = trim(line); + if(line.empty() || line[0] == ';' || line[0] == '#') + continue; + + if(line.front() == '[' && line.back() == ']') + { + pushIfValid(current); + current = ThemeOption(); + current.id = trim(line.substr(1, line.size() - 2)); + continue; + } + + auto pos = line.find('='); + if(pos != std::string::npos) + { + std::string key = trim(line.substr(0, pos)); + std::string value = trim(line.substr(pos + 1)); + + if(key == "label") current.label = value; + else if(key == "type") current.type = value; + else if(key == "path") current.path = value; + else if(key == "apply_to" || key == "applyTo") current.applyTo = value; + else if(key == "values") parseValues(value, current.values); + else if(key == "default") current.defaultValue = value; + else if(key == "key") current.key = value; + } + } + + pushIfValid(current); + } + } + + ensureLayoutOptionIfLayoutFolderExists(themeDir, options); + ensureAvatarOptionIfAvatarFolderExists(themeDir, options); + return options; + } + + // ------------------------------------------------------------ + // Apply layout by COPYING layout files to theme root (dialog-style) + // ------------------------------------------------------------ + static bool applyLayoutByCopy(Window* win, const std::string& themeDir, const std::string& layoutId) + { + const std::string iniPath = themeDir + "/theme.ini"; + std::map> ini; + if(Utils::FileSystem::isRegularFile(iniPath)) + ini = loadIniSections(iniPath); + + // Defaults: ./layout//.xml + auto defPath = [&](const std::string& fileName) -> std::string + { + return "./layout/" + layoutId + "/" + fileName; + }; + + const std::string section = "layout_" + layoutId; + + std::string srcTheme = defPath("theme.xml"); + std::string srcSwatch = defPath("Swatch.xml"); + std::string srcAvatar = defPath("avatar.xml"); + std::string srcUser = defPath("user.xml"); + std::string srcStart = defPath("start.xml"); + + // If mapping exists in ini, override + if(!ini.empty() && ini.count(section)) + { + auto& s = ini[section]; + if(s.count("theme")) srcTheme = s["theme"]; + if(s.count("swatch")) srcSwatch = s["swatch"]; + if(s.count("avatar")) srcAvatar = s["avatar"]; + if(s.count("user")) srcUser = s["user"]; + if(s.count("start")) srcStart = s["start"]; + } + + // Resolve paths + const std::string absTheme = resolveThemePath(themeDir, srcTheme); + const std::string absSwatch = resolveThemePath(themeDir, srcSwatch); + const std::string absAvatar = resolveThemePath(themeDir, srcAvatar); + const std::string absUser = resolveThemePath(themeDir, srcUser); + const std::string absStart = resolveThemePath(themeDir, srcStart); + + bool okAny = false; + bool okAll = true; + + auto doCopy = [&](const std::string& srcAbs, const std::string& dstName) + { + if(srcAbs.empty()) + return; + + if(!Utils::FileSystem::isRegularFile(srcAbs)) + { + // Not fatal; just skip + return; + } + + const std::string dstAbs = themeDir + "/" + dstName; + if(!copyFileForce(srcAbs, dstAbs)) + okAll = false; + else + okAny = true; + }; + + doCopy(absTheme, "theme.xml"); + doCopy(absSwatch, "Swatch.xml"); + doCopy(absAvatar, "avatar.xml"); + doCopy(absUser, "user.xml"); + doCopy(absStart, "start.xml"); + + if(!okAny) + { + if(win) + win->pushGui(new GuiMsgBox(win, _("No se encontró ningún XML para copiar en este layout."), _("BACK"))); + return false; + } + + if(!okAll) + { + if(win) + win->pushGui(new GuiMsgBox(win, _("Algunos archivos no se pudieron copiar (revisá permisos/rutas)."), _("BACK"))); + } + + return true; + } + + static void applyThemeOption(Window* win, const std::string& themeDir, const ThemeOption& opt, const std::string& value) + { + if(opt.id.empty() || value.empty()) + return; + + if(opt.applyTo == "layout" || opt.id == "layout") + { + Settings::getInstance()->setString("ThemeLayout", value); + Settings::getInstance()->saveFile(); + + // IMPORTANT: copy layout files to theme root (dialog behavior) + applyLayoutByCopy(win, themeDir, value); + } + else + { + const std::string iniPath = themeDir + "/theme.ini"; + const std::string outKey = !opt.key.empty() ? opt.key : opt.id; + updateThemeIniValue(iniPath, outKey, value); + } + + auto vc = ViewController::get(); + if(vc != nullptr) + vc->reloadAll(); + } + + class GuiThemeOptionSelect : public GuiComponent + { + public: + GuiThemeOptionSelect(Window* window, + const std::string& themeDir, + const ThemeOption& opt, + const std::string& title) + : GuiComponent(window) + , mMenu(window, title.c_str()) + , mThemeDir(themeDir) + , mOption(opt) + { + for(const auto& v : mOption.values) + { + ComponentListRow row; + + auto text = std::make_shared( + mWindow, + v.second, + Font::get(FONT_SIZE_MEDIUM), + 0x777777FF + ); + + text->setColor(0x777777FF); + row.addElement(text, true); + + row.makeAcceptInputHandler([this, v]() + { + applyThemeOption(mWindow, mThemeDir, mOption, v.first); + delete this; + }); + + mMenu.addRow(row); + } + + addChild(&mMenu); + + setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); + mMenu.setPosition( + (mSize.x() - mMenu.getSize().x()) / 2.0f, + Renderer::getScreenHeight() * 0.15f); + } + + bool input(InputConfig* config, Input input) override + { + if(GuiComponent::input(config, input)) + return true; + + if(config->isMappedTo("b", input) && input.value != 0) + { + delete this; + return true; + } + return false; + } + + std::vector getHelpPrompts() override + { + std::vector prompts; + prompts.push_back(HelpPrompt("a", _("SELECT"))); + prompts.push_back(HelpPrompt("b", _("BACK"))); + return prompts; + } + + HelpStyle getHelpStyle() override + { + HelpStyle style; + auto vc = ViewController::get(); + if(vc) + { + auto system = vc->getState().getSystem(); + if(system) + style.applyTheme(system->getTheme(), "system"); + } + return style; + } + + private: + MenuComponent mMenu; + std::string mThemeDir; + ThemeOption mOption; + }; + + static void openThemeInput(Window* win, const std::string& themeDir, const ThemeOption& opt, const std::string& title) + { + if(win == nullptr) + return; + + const std::string iniPath = themeDir + "/theme.ini"; + const std::string outKey = !opt.key.empty() ? opt.key : opt.id; + + std::string current = readTopIniValue(iniPath, outKey); + if(current.empty()) + current = opt.defaultValue; + + // NOTE: acceptBtnText is const char* and may be stored as pointer. + // Use static std::string to guarantee lifetime and allow translation. + static std::string okLabel = _("OK"); + + win->pushGui(new GuiTextEditPopup( + win, + title, + current, + [win, themeDir, opt](const std::string& newValue) + { + if(newValue.empty()) + return; + + applyThemeOption(win, themeDir, opt, newValue); + }, + false, // multiline + okLabel.c_str() // safe lifetime + )); + } +} + +GuiThemeOptions::GuiThemeOptions(Window* window) + : GuiComponent(window) + , mMenu(window, _("THEME OPTIONS").c_str()) +{ + std::string themeSet = Settings::getInstance()->getString("ThemeSet"); + std::string themeDir; + + if(!themeSet.empty()) + themeDir = Utils::FileSystem::getHomePath() + "/.emulationstation/themes/" + themeSet; + + auto options = loadThemeOptionsFromIni(themeDir); + + if(options.empty()) + { + ComponentListRow row; + row.addElement( + std::make_shared( + mWindow, + _("NO THEME OPTIONS AVAILABLE"), + Font::get(FONT_SIZE_MEDIUM), + 0x777777FF), + true); + mMenu.addRow(row); + } + else + { + for(const auto& opt : options) + { + std::string entryLabel = !opt.label.empty() ? opt.label : opt.id; + + ComponentListRow row; + + auto text = std::make_shared( + mWindow, + entryLabel, + Font::get(FONT_SIZE_MEDIUM), + 0x777777FF + ); + text->setColor(0x777777FF); + + row.addElement(text, true); + + row.makeAcceptInputHandler([this, themeDir, opt, entryLabel] + { + std::string t = opt.type; + std::transform(t.begin(), t.end(), t.begin(), ::tolower); + + if(t == "select" && !opt.values.empty()) + { + mWindow->pushGui(new GuiThemeOptionSelect(mWindow, themeDir, opt, entryLabel)); + } + else if(t == "input") + { + openThemeInput(mWindow, themeDir, opt, entryLabel); + } + else + { + std::string msg = entryLabel + "\n\n" + _("(Feature not yet implemented)"); + mWindow->pushGui(new GuiMsgBox(mWindow, msg, _("BACK"))); + } + }); + + mMenu.addRow(row); + } + } + + addChild(&mMenu); + + setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); + mMenu.setPosition( + (mSize.x() - mMenu.getSize().x()) / 2.0f, + Renderer::getScreenHeight() * 0.15f); +} + +bool GuiThemeOptions::input(InputConfig* config, Input input) +{ + if(GuiComponent::input(config, input)) + return true; + + if(config->isMappedTo("b", input) && input.value != 0) + { + delete this; + return true; + } + return false; +} + +std::vector GuiThemeOptions::getHelpPrompts() +{ + std::vector prompts; + prompts.push_back(HelpPrompt("b", _("BACK"))); + return prompts; +} + +HelpStyle GuiThemeOptions::getHelpStyle() +{ + HelpStyle style; + auto vc = ViewController::get(); + if(vc) + { + auto system = vc->getState().getSystem(); + if(system) + style.applyTheme(system->getTheme(), "system"); + } + return style; +} diff --git a/es-app/src/guis/GuiThemeOptions.h b/es-app/src/guis/GuiThemeOptions.h new file mode 100644 index 0000000000..d366c0c0ed --- /dev/null +++ b/es-app/src/guis/GuiThemeOptions.h @@ -0,0 +1,18 @@ +// es-app/src/guis/GuiThemeOptions.h +#pragma once + +#include "GuiComponent.h" +#include "components/MenuComponent.h" + +class GuiThemeOptions : public GuiComponent +{ +public: + explicit GuiThemeOptions(Window* window); + + bool input(InputConfig* config, Input input) override; + std::vector getHelpPrompts() override; + HelpStyle getHelpStyle() override; + +private: + MenuComponent mMenu; +}; diff --git a/es-app/src/main.cpp b/es-app/src/main.cpp index eb0b92cacf..fa3e963e2f 100644 --- a/es-app/src/main.cpp +++ b/es-app/src/main.cpp @@ -28,6 +28,12 @@ #include +// NUEVO: sistema de localización ES-X +#include "LocaleES.h" + +// NUEVO: música de fondo ES-X +#include "audio/BackgroundMusicManager.h" + bool scrape_cmdline = false; bool parseArgs(int argc, char* argv[]) @@ -173,10 +179,6 @@ bool parseArgs(int argc, char* argv[]) else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { #ifdef WIN32 - // This is a bit of a hack, but otherwise output will go to nowhere - // when the application is compiled with the "WINDOWS" subsystem (which we usually are). - // If you're an experienced Windows programmer and know how to do this - // the right way, please submit a pull request! AttachConsole(ATTACH_PARENT_PROCESS); freopen("CONOUT$", "wb", stdout); #endif @@ -231,7 +233,7 @@ bool parseArgs(int argc, char* argv[]) "setting is changed via EmulationStation UI.\n\n" "Please refer to the online documentation for additional information:\n" "https://retropie.org.uk/docs/EmulationStation/\n"; - return false; //exit after printing help + return false; } } @@ -240,7 +242,6 @@ bool parseArgs(int argc, char* argv[]) bool verifyHomeFolderExists() { - //make sure the config directory exists std::string home = Utils::FileSystem::getHomePath(); std::string configDir = home + "/.emulationstation"; if(!Utils::FileSystem::exists(configDir)) @@ -257,7 +258,6 @@ bool verifyHomeFolderExists() return true; } -// Returns true if everything is OK, bool loadSystemConfigFile(Window* window, const char** errorString) { *errorString = NULL; @@ -283,7 +283,6 @@ bool loadSystemConfigFile(Window* window, const char** errorString) return true; } -//called on exit, assuming we get far enough to have the log initialized void onExit() { Log::close(); @@ -296,54 +295,35 @@ int main(int argc, char* argv[]) if(!parseArgs(argc, argv)) return 0; - // only show the console on Windows if HideConsole is false #ifdef WIN32 - // MSVC has a "SubSystem" option, with two primary options: "WINDOWS" and "CONSOLE". - // In "WINDOWS" mode, no console is automatically created for us. This is good, - // because we can choose to only create the console window if the user explicitly - // asks for it, preventing it from flashing open and then closing. - // In "CONSOLE" mode, a console is always automatically created for us before we - // enter main. In this case, we can only hide the console after the fact, which - // will leave a brief flash. - // TL;DR: You should compile ES under the "WINDOWS" subsystem. - // I have no idea how this works with non-MSVC compilers. if(!Settings::getInstance()->getBool("HideConsole")) { - // we want to show the console - // if we're compiled in "CONSOLE" mode, this is already done. - // if we're compiled in "WINDOWS" mode, no console is created for us automatically; - // the user asked for one, so make one and then hook stdin/stdout/sterr up to it - if(AllocConsole()) // should only pass in "WINDOWS" mode + if(AllocConsole()) { freopen("CONIN$", "r", stdin); freopen("CONOUT$", "wb", stdout); freopen("CONOUT$", "wb", stderr); } }else{ - // we want to hide the console - // if we're compiled with the "WINDOWS" subsystem, this is already done. - // if we're compiled with the "CONSOLE" subsystem, a console is already created; - // it'll flash open, but we hide it nearly immediately - if(GetConsoleWindow()) // should only pass in "CONSOLE" mode + if(GetConsoleWindow()) ShowWindow(GetConsoleWindow(), SW_HIDE); } #endif - // call this ONLY when linking with FreeImage as a static library #ifdef FREEIMAGE_LIB FreeImage_Initialise(); #endif - //if ~/.emulationstation doesn't exist and cannot be created, bail if(!verifyHomeFolderExists()) return 1; - //start the logger Log::init(); Log::open(); LOG(LogInfo) << "EmulationStation - v" << PROGRAM_VERSION_STRING << ", built " << PROGRAM_BUILT_STRING; - //always close the log on exit + // 🔤 NUEVO: inicializar localización según Settings::Language + LocaleES::getInstance().loadFromSettings(); + atexit(&onExit); Window window; @@ -374,7 +354,6 @@ int main(int argc, char* argv[]) const char* errorMsg = NULL; if(!loadSystemConfigFile(splashScreen ? &window : nullptr, &errorMsg)) { - // something went terribly wrong if(errorMsg == NULL) { LOG(LogError) << "Unknown error occured while parsing system config file."; @@ -383,7 +362,6 @@ int main(int argc, char* argv[]) return 1; } - // we can't handle es_systems.cfg file problems inside ES itself, so display the error message then quit window.pushGui(new GuiMsgBox(&window, errorMsg, "QUIT", [] { @@ -393,14 +371,11 @@ int main(int argc, char* argv[]) })); } - //run the command line scraper then quit if(scrape_cmdline) { return run_scraper_cmdline(); } - // preload what we can right away instead of waiting for the user to select it - // this makes for no delays when accessing content, but a longer startup time ViewController::get()->preload(); if(splashScreen) @@ -408,7 +383,9 @@ int main(int argc, char* argv[]) InputManager::getInstance()->init(); - //choose which GUI to open depending on if an input configuration already exists + // 🔊 NUEVO: inicializar música de fondo ES-X (si está habilitada) + BackgroundMusicManager::getInstance().init(); + if(errorMsg == NULL) { if(Utils::FileSystem::exists(InputManager::getConfigPath()) && InputManager::getInstance()->getNumConfiguredDevices() > 0) @@ -439,25 +416,20 @@ int main(int argc, char* argv[]) running = false; } while(SDL_PollEvent(&event)); - // triggered if exiting from SDL_WaitEvent due to event if (ps_standby) - // show as if continuing from last event lastTime = SDL_GetTicks(); - // reset counter ps_time = SDL_GetTicks(); } else if (ps_standby) { - // If exitting SDL_WaitEventTimeout due to timeout. Trail considering - // timeout as an event ps_time = SDL_GetTicks(); } if(window.isSleeping()) { lastTime = SDL_GetTicks(); - SDL_Delay(1); // this doesn't need to be accurate, we're just giving up our CPU time until something wakes us up + SDL_Delay(1); continue; } @@ -465,11 +437,14 @@ int main(int argc, char* argv[]) int deltaTime = curTime - lastTime; lastTime = curTime; - // cap deltaTime if it ever goes negative if(deltaTime < 0) deltaTime = 1000; window.update(deltaTime); + + // ✅ BGM: procesar “resume con delay” al volver del juego + BackgroundMusicManager::getInstance().update(deltaTime); + window.render(); Renderer::swapBuffers(); @@ -480,13 +455,16 @@ int main(int argc, char* argv[]) delete window.peekGui(); InputManager::getInstance()->deinit(); + + // 🔊 NUEVO: apagar música antes de cerrar ventana / audio + BackgroundMusicManager::getInstance().shutdown(); + window.deinit(); MameNames::deinit(); CollectionSystemManager::deinit(); SystemData::deleteSystems(); - // call this ONLY when linking with FreeImage as a static library #ifdef FREEIMAGE_LIB FreeImage_DeInitialise(); #endif diff --git a/es-app/src/scrapers/ScreenScraper.cpp b/es-app/src/scrapers/ScreenScraper.cpp index da6fd1e0f4..2a7d7c7788 100644 --- a/es-app/src/scrapers/ScreenScraper.cpp +++ b/es-app/src/scrapers/ScreenScraper.cpp @@ -7,6 +7,7 @@ #include "PlatformId.h" #include "Settings.h" #include "SystemData.h" +#include "LocaleESHook.h" // Puedes dejarlo aunque aquí ya no lo usamos directamente #include #include @@ -214,26 +215,51 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc, std::ve ScraperSearchResult result; ScreenScraperRequest::ScreenScraperConfig ssConfig; - std::string region = Utils::String::toLower(ssConfig.region).c_str(); - std::string language = Utils::String::toLower(ssConfig.language).c_str(); - - // Name fallback: US, WOR(LD). ( Xpath: Data/jeu[0]/noms/nom[*] ). - result.mdl.set("name", find_child_by_attribute_list(game.child("noms"), "nom", "region", { region, "wor", "us" , "ss", "eu", "jp" }).text().get()); - - // Description fallback language: EN, WOR(LD) - std::string description = find_child_by_attribute_list(game.child("synopsis"), "synopsis", "langue", { language, "en", "wor" }).text().get(); + // 🔹 Idioma dinámico según el ajuste "Language" (que ya viene del .ini) + std::string language = Settings::getInstance()->getString("Language"); + if (language.empty()) + language = "en"; + language = Utils::String::toLower(language); + + // usamos el idioma como región base; ScreenScraper también usa "wor", "us", etc. + std::string region = language; + + // Name fallback: región, luego WOR(LD), US, EU, JP, SS. + result.mdl.set("name", find_child_by_attribute_list( + game.child("noms"), + "nom", + "region", + { region, "wor", "us" , "eu", "jp", "ss" } + ).text().get()); + + // Description fallback language: idioma actual, EN, WOR(LD) + std::string description = find_child_by_attribute_list( + game.child("synopsis"), + "synopsis", + "langue", + { language, "en", "wor" } + ).text().get(); if (!description.empty()) { result.mdl.set("desc", Utils::String::replace(description, " ", " ")); } - // Genre fallback language: EN. ( Xpath: Data/jeu[0]/genres/genre[*] ) - result.mdl.set("genre", find_child_by_attribute_list(game.child("genres"), "genre", "langue", { language, "en" }).text().get()); + // Genre fallback language: idioma actual, luego EN. + result.mdl.set("genre", find_child_by_attribute_list( + game.child("genres"), + "genre", + "langue", + { language, "en" } + ).text().get()); LOG(LogDebug) << "Genre: " << result.mdl.get("genre"); - // Get the date proper. The API returns multiple 'date' children nodes to the 'dates' main child of 'jeu'. - // Date fallback: WOR(LD), US, SS, JP, EU - std::string _date = find_child_by_attribute_list(game.child("dates"), "date", "region", { region, "wor", "us", "ss", "jp", "eu" }).text().get(); + // Date fallback: región, WOR(LD), US, SS, JP, EU + std::string _date = find_child_by_attribute_list( + game.child("dates"), + "date", + "region", + { region, "wor", "us", "ss", "jp", "eu" } + ).text().get(); LOG(LogDebug) << "Release Date (unparsed): " << _date; // Date can be YYYY-MM-DD or just YYYY. @@ -277,14 +303,13 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc, std::ve pugi::xml_node art = pugi::xml_node(NULL); // Do an XPath query for media[type='$media_type'], then filter by region - // We need to do this because any child of 'medias' has the form - // - // and we need to find the right media for the region. - pugi::xpath_node_set results = media_list.select_nodes((static_cast("media[@type='") + ssConfig.media_name + "']").c_str()); + pugi::xpath_node_set results = media_list.select_nodes( + (static_cast("media[@type='") + ssConfig.media_name + "']").c_str() + ); if (results.size()) { - // Region fallback: WOR(LD), US, CUS(TOM?), JP, EU + // Region fallback: región actual, WOR(LD), US, CUS(TOM?), JP, EU for (auto _region : std::vector{ region, "wor", "us", "cus", "jp", "eu" }) { if (art) @@ -303,16 +328,15 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc, std::ve if (art) { - // Sending a 'softname' containing space will make the image URLs returned by the API also contain the space. - // Escape any spaces in the URL here + // Fix espacios en la URL result.imageUrl = Utils::String::replace(art.text().get(), " ", "%20"); - // Get the media type returned by ScreenScraper + // Tipo de imagen std::string media_type = art.attribute("format").value(); if (!media_type.empty()) result.imageType = "." + media_type; - // Ask for the same image, but with a smaller size, for the thumbnail displayed during scraping + // Thumbnail pequeño para el scraper result.thumbnailUrl = result.imageUrl + "&maxheight=250"; }else{ LOG(LogDebug) << "Failed to find media XML node with name=" << ssConfig.media_name; @@ -340,9 +364,6 @@ void ScreenScraperRequest::processList(const pugi::xml_document& xmldoc, std::ve ScreenScraperRequest::ScreenScraperConfig ssConfig; // limit the number of results per platform, not in total. - // otherwise if the first platform returns >= 7 games - // but the second platform contains the relevant game, - // the relevant result would not be shown. for (int i = 0; game && i < MAX_SCRAPER_RESULTS; i++) { std::string id = game.child("id").text().get(); diff --git a/es-app/src/views/SystemView.cpp b/es-app/src/views/SystemView.cpp index 70db4a8dec..e8999b6b3c 100644 --- a/es-app/src/views/SystemView.cpp +++ b/es-app/src/views/SystemView.cpp @@ -7,21 +7,30 @@ #include "Log.h" #include "Scripting.h" #include "Settings.h" -#include "Sound.h" #include "SystemData.h" #include "Window.h" +#include "LocaleES.h" +#include "Sound.h" // necesario para Sound::get (scroll directo de ) +#include "NavigationSounds.h" // NUEVO: helper central de sonidos // buffer values for scrolling velocity (left, stopped, right) -const int logoBuffersLeft[] = { -5, -2, -1 }; -const int logoBuffersRight[] = { 1, 2, 5 }; +const int logoBuffersLeft[] = { -5, -2, -1 }; +const int logoBuffersRight[] = { 1, 2, 5 }; -SystemView::SystemView(Window* window) : IList(window, LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP), - mViewNeedsReload(true), - mSystemInfo(window, "SYSTEM INFO", Font::get(FONT_SIZE_SMALL), 0x33333300, ALIGN_CENTER) +SystemView::SystemView(Window* window) : + IList(window, LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP), + mViewNeedsReload(true), + mSystemInfo(window, "SYSTEM INFO", Font::get(FONT_SIZE_SMALL), 0x33333300, ALIGN_CENTER) { + auto& loc = LocaleES::getInstance(); + loc.loadFromSettings(); + + mSystemInfo.setText(loc.translate("SYSTEM INFO")); + mCamOffset = 0; mExtrasCamOffset = 0; mExtrasFadeOpacity = 0.0f; + mScrollSnd.reset(); setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); populate(); @@ -41,17 +50,18 @@ void SystemView::populate() if((*it)->isVisible()) { Entry e; - e.name = (*it)->getName(); + e.name = (*it)->getName(); e.object = *it; // make logo const ThemeData::ThemeElement* logoElem = theme->getElement("system", "logo", "image"); if(logoElem) { - std::string path = logoElem->get("path"); + std::string path = logoElem->get("path"); std::string defaultPath = logoElem->has("default") ? logoElem->get("default") : ""; - if((!path.empty() && ResourceManager::getInstance()->fileExists(path)) - || (!defaultPath.empty() && ResourceManager::getInstance()->fileExists(defaultPath))) + + if((!path.empty() && ResourceManager::getInstance()->fileExists(path)) || + (!defaultPath.empty() && ResourceManager::getInstance()->fileExists(defaultPath))) { ImageComponent* logo = new ImageComponent(mWindow, false, false); logo->setMaxSize(mCarousel.logoSize * mCarousel.logoScale); @@ -60,40 +70,57 @@ void SystemView::populate() e.data.logo = std::shared_ptr(logo); } } - if (!e.data.logo) + + if(!e.data.logo) { // no logo in theme; use text - TextComponent* text = new TextComponent(mWindow, + TextComponent* text = new TextComponent( + mWindow, (*it)->getName(), Font::get(FONT_SIZE_LARGE), 0x000000FF, ALIGN_CENTER); + text->setSize(mCarousel.logoSize * mCarousel.logoScale); - text->applyTheme((*it)->getTheme(), "system", "logoText", ThemeFlags::FONT_PATH | ThemeFlags::FONT_SIZE | ThemeFlags::COLOR | ThemeFlags::FORCE_UPPERCASE | ThemeFlags::LINE_SPACING | ThemeFlags::TEXT); + text->applyTheme( + (*it)->getTheme(), + "system", + "logoText", + ThemeFlags::FONT_PATH + | ThemeFlags::FONT_SIZE + | ThemeFlags::COLOR + | ThemeFlags::FORCE_UPPERCASE + | ThemeFlags::LINE_SPACING + | ThemeFlags::TEXT); + e.data.logo = std::shared_ptr(text); - if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) + if(mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) { text->setHorizontalAlignment(mCarousel.logoAlignment); text->setVerticalAlignment(ALIGN_CENTER); - } else { + } + else + { text->setHorizontalAlignment(ALIGN_CENTER); text->setVerticalAlignment(mCarousel.logoAlignment); } } - if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) + if(mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) { - if (mCarousel.logoAlignment == ALIGN_LEFT) + if(mCarousel.logoAlignment == ALIGN_LEFT) e.data.logo->setOrigin(0, 0.5); - else if (mCarousel.logoAlignment == ALIGN_RIGHT) + else if(mCarousel.logoAlignment == ALIGN_RIGHT) e.data.logo->setOrigin(1.0, 0.5); else e.data.logo->setOrigin(0.5, 0.5); - } else { - if (mCarousel.logoAlignment == ALIGN_TOP) + } + else + { + if(mCarousel.logoAlignment == ALIGN_TOP) e.data.logo->setOrigin(0.5, 0); - else if (mCarousel.logoAlignment == ALIGN_BOTTOM) + else if(mCarousel.logoAlignment == ALIGN_BOTTOM) e.data.logo->setOrigin(0.5, 1); else e.data.logo->setOrigin(0.5, 0.5); @@ -101,8 +128,9 @@ void SystemView::populate() Vector2f denormalized = mCarousel.logoSize * e.data.logo->getOrigin(); e.data.logo->setPosition(denormalized.x(), denormalized.y(), 0.0); + // delete any existing extras - for (auto extra : e.data.backgroundExtras) + for(auto extra : e.data.backgroundExtras) delete extra; e.data.backgroundExtras.clear(); @@ -110,24 +138,43 @@ void SystemView::populate() e.data.backgroundExtras = ThemeData::makeExtras((*it)->getTheme(), "system", mWindow); // sort the extras by z-index - std::stable_sort(e.data.backgroundExtras.begin(), e.data.backgroundExtras.end(), [](GuiComponent* a, GuiComponent* b) { - return b->getZIndex() > a->getZIndex(); - }); + std::stable_sort( + e.data.backgroundExtras.begin(), + e.data.backgroundExtras.end(), + [](GuiComponent* a, GuiComponent* b) + { + return b->getZIndex() > a->getZIndex(); + }); this->add(e); } } - if (mEntries.size() == 0) + + if(mEntries.size() == 0) { // Something is wrong, there is not a single system to show, check if UI mode is not full - if (!UIModeController::getInstance()->isUIModeFull()) + if(!UIModeController::getInstance()->isUIModeFull()) { Settings::getInstance()->setString("UIMode", "Full"); - mWindow->pushGui(new GuiMsgBox(mWindow, "The selected UI mode has nothing to show,\n returning to UI mode: FULL", "OK", nullptr)); + mWindow->pushGui(new GuiMsgBox( + mWindow, + "The selected UI mode has nothing to show,\n returning to UI mode: FULL", + "OK", + nullptr)); } } } +void SystemView::onShow() +{ + mShowing = true; +} + +void SystemView::onHide() +{ + mShowing = false; +} + void SystemView::goToSystem(SystemData* system, bool animate) { setCursor(system); @@ -136,43 +183,86 @@ void SystemView::goToSystem(SystemData* system, bool animate) finishAnimation(0); } +// helper local para reproducir scroll de sistema +inline void playSystemScrollSound(SystemData* sys, + const std::shared_ptr& scrollFromCarousel) +{ + // 1) prioridad: sonido definido en + if (scrollFromCarousel) + { + scrollFromCarousel->play(); + return; + } + + if (!sys) + return; + + const std::shared_ptr& theme = sys->getTheme(); + if (!theme) + return; + + // 2) Intentar esquema tipo Batocera: "systembrowse" → scroll de carrusel + auto snd = NavigationSounds::getFromTheme(theme, "systembrowse"); + if (!snd) + { + // 3) fallback genérico "scroll" + snd = NavigationSounds::getFromTheme(theme, "scroll"); + } + + if (snd) + snd->play(); +} + bool SystemView::input(InputConfig* config, Input input) { if(input.value != 0) { - if(config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_r && SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) + if(config->getDeviceId() == DEVICE_KEYBOARD && input.value && + input.id == SDLK_r && SDL_GetModState() & KMOD_LCTRL && + Settings::getInstance()->getBool("Debug")) { LOG(LogInfo) << " Reloading all"; ViewController::get()->reloadAll(); return true; } - switch (mCarousel.type) + bool moved = false; + + switch(mCarousel.type) { case VERTICAL: case VERTICAL_WHEEL: - if (config->isMappedLike("up", input)) + if(config->isMappedLike("up", input)) { - listInput(-1); + moved = listInput(-1); + if (moved) + playSystemScrollSound(getSelected(), mScrollSnd); return true; } - if (config->isMappedLike("down", input)) + if(config->isMappedLike("down", input)) { - listInput(1); + moved = listInput(1); + if (moved) + playSystemScrollSound(getSelected(), mScrollSnd); return true; } break; + case HORIZONTAL: case HORIZONTAL_WHEEL: default: - if (config->isMappedLike("left", input)) + if(config->isMappedLike("left", input)) { - listInput(-1); + moved = listInput(-1); + if (moved) + playSystemScrollSound(getSelected(), mScrollSnd); return true; } - if (config->isMappedLike("right", input)) + if(config->isMappedLike("right", input)) { - listInput(1); + moved = listInput(1); + if (moved) + playSystemScrollSound(getSelected(), mScrollSnd); return true; } break; @@ -180,25 +270,63 @@ bool SystemView::input(InputConfig* config, Input input) if(config->isMappedTo("a", input)) { + // --- SONIDO DE SELECT COMPATIBLE CON ESQUEMA BATOCERA --- + std::shared_ptr selectSnd; + + SystemData* sys = getSelected(); + if(sys != nullptr) + { + const std::shared_ptr& theme = sys->getTheme(); + if (theme) + { + // 1) esquema nuevo: buscar "select" (navigationsounds) + selectSnd = NavigationSounds::getFromTheme(theme, "select"); + + // 2) fallback: comportamiento viejo, elemento "selectSound" + if (!selectSnd && theme->hasView("system")) + { + const ThemeData::ThemeElement* selectElem = + theme->getElement("system", "selectSound", "sound"); + + if(selectElem && selectElem->has("path")) + { + std::string path = selectElem->get("path"); + if(!path.empty()) + selectSnd = Sound::get(path); + } + } + } + } + + if(selectSnd) + selectSnd->play(); + // ------------------------------------------------------------------------- + stopScrolling(); ViewController::get()->goToGameList(getSelected()); return true; } - if (config->isMappedTo("x", input)) + + if(config->isMappedTo("x", input)) { // get random system - // go to system setCursor(SystemData::getRandomSystem()); return true; } - }else{ + } + else + { if(config->isMappedLike("left", input) || - config->isMappedLike("right", input) || - config->isMappedLike("up", input) || - config->isMappedLike("down", input)) + config->isMappedLike("right", input) || + config->isMappedLike("up", input) || + config->isMappedLike("down", input)) listInput(0); + Scripting::fireEvent("system-select", this->IList::getSelected()->getName(), "input"); - if(!UIModeController::getInstance()->isUIModeKid() && config->isMappedTo("select", input) && Settings::getInstance()->getBool("ScreenSaverControls")) + + if(!UIModeController::getInstance()->isUIModeKid() && + config->isMappedTo("select", input) && + Settings::getInstance()->getBool("ScreenSaverControls")) { mWindow->startScreenSaver(); mWindow->renderScreenSaver(); @@ -225,121 +353,141 @@ void SystemView::onCursorChanged(const CursorState& /*state*/) float posMax = (float)mEntries.size(); float target = (float)mCursor; - // what's the shortest way to get to our target? - // it's one of these... - - float endPos = target; // directly - float dist = abs(endPos - startPos); + // shortest way to target + float endPos = target; // directamente + float dist = abs(endPos - startPos); if(abs(target + posMax - startPos) < dist) endPos = target + posMax; // loop around the end (0 -> max) if(abs(target - posMax - startPos) < dist) endPos = target - posMax; // loop around the start (max - 1 -> -1) - - // animate mSystemInfo's opacity (fade out, wait, fade back in) - + // animar opacidad de mSystemInfo (fade out, espera, fade in) cancelAnimation(1); cancelAnimation(2); std::string transition_style = Settings::getInstance()->getString("TransitionStyle"); - bool goFast = transition_style == "instant"; - const float infoStartOpacity = mSystemInfo.getOpacity() / 255.f; + bool goFast = transition_style == "instant"; + const float infoStartOpacity = mSystemInfo.getOpacity() / 255.f; Animation* infoFadeOut = new LambdaAnimation( - [infoStartOpacity, this] (float t) - { - mSystemInfo.setOpacity((unsigned char)(Math::lerp(infoStartOpacity, 0.f, t) * 255)); - }, (int)(infoStartOpacity * (goFast ? 10 : 150))); + [infoStartOpacity, this](float t) + { + mSystemInfo.setOpacity((unsigned char)(Math::lerp(infoStartOpacity, 0.f, t) * 255)); + }, + (int)(infoStartOpacity * (goFast ? 10 : 150))); unsigned int gameCount = getSelected()->getDisplayedGameCount(); - // also change the text after we've fully faded out - setAnimation(infoFadeOut, 0, [this, gameCount] { - std::stringstream ss; + // Localización + LocaleES& loc = LocaleES::getInstance(); - if (!getSelected()->isGameSystem()) - ss << "CONFIGURATION"; - else - ss << gameCount << " GAME" << (gameCount == 1 ? "" : "S") << " AVAILABLE"; + // cambiar el texto después del fade out + setAnimation( + infoFadeOut, + 0, + [this, gameCount, &loc]() + { + std::stringstream ss; + + if(!getSelected()->isGameSystem()) + { + ss << loc.translate("CONFIGURATION"); + } + else + { + ss << gameCount << " " + << loc.translate(gameCount == 1 ? "GAME" : "GAMES") + << " " + << loc.translate("AVAILABLE"); + } - mSystemInfo.setText(ss.str()); - }, false, 1); + mSystemInfo.setText(ss.str()); + }, + false, + 1); Animation* infoFadeIn = new LambdaAnimation( [this](float t) - { - mSystemInfo.setOpacity((unsigned char)(Math::lerp(0.f, 1.f, t) * 255)); - }, goFast ? 10 : 300); + { + mSystemInfo.setOpacity((unsigned char)(Math::lerp(0.f, 1.f, t) * 255)); + }, + goFast ? 10 : 300); - // wait 600ms to fade in setAnimation(infoFadeIn, goFast ? 0 : 2000, nullptr, false, 2); - // no need to animate transition, we're not going anywhere (probably mEntries.size() == 1) + // si no hay movimiento, no animar if(endPos == mCamOffset && endPos == mExtrasCamOffset) return; Animation* anim; bool move_carousel = Settings::getInstance()->getBool("MoveCarousel"); + if(transition_style == "fade") { float startExtrasFade = mExtrasFadeOpacity; anim = new LambdaAnimation( [this, startExtrasFade, startPos, endPos, posMax, move_carousel](float t) - { - t -= 1; - float f = Math::lerp(startPos, endPos, t*t*t + 1); - if(f < 0) - f += posMax; - if(f >= posMax) - f -= posMax; - - this->mCamOffset = move_carousel ? f : endPos; - - t += 1; - if(t < 0.3f) - this->mExtrasFadeOpacity = Math::lerp(0.0f, 1.0f, t / 0.3f + startExtrasFade); - else if(t < 0.7f) - this->mExtrasFadeOpacity = 1.0f; - else - this->mExtrasFadeOpacity = Math::lerp(1.0f, 0.0f, (t - 0.7f) / 0.3f); + { + t -= 1; + float f = Math::lerp(startPos, endPos, t * t * t + 1); + if(f < 0) + f += posMax; + if(f >= posMax) + f -= posMax; + + this->mCamOffset = move_carousel ? f : endPos; + + t += 1; + if(t < 0.3f) + this->mExtrasFadeOpacity = Math::lerp(0.0f, 1.0f, t / 0.3f + startExtrasFade); + else if(t < 0.7f) + this->mExtrasFadeOpacity = 1.0f; + else + this->mExtrasFadeOpacity = Math::lerp(1.0f, 0.0f, (t - 0.7f) / 0.3f); - if(t > 0.5f) - this->mExtrasCamOffset = endPos; + if(t > 0.5f) + this->mExtrasCamOffset = endPos; - }, 500); - } else if (transition_style == "slide") { - // slide + }, + 500); + } + else if(transition_style == "slide") + { anim = new LambdaAnimation( [this, startPos, endPos, posMax, move_carousel](float t) - { - t -= 1; - float f = Math::lerp(startPos, endPos, t*t*t + 1); - if(f < 0) - f += posMax; - if(f >= posMax) - f -= posMax; - - this->mCamOffset = move_carousel ? f : endPos; - this->mExtrasCamOffset = f; - }, 500); - } else { - // instant - anim = new LambdaAnimation( - [this, startPos, endPos, posMax, move_carousel ](float t) - { - t -= 1; - float f = Math::lerp(startPos, endPos, t*t*t + 1); - if(f < 0) - f += posMax; - if(f >= posMax) - f -= posMax; - - this->mCamOffset = move_carousel ? f : endPos; - this->mExtrasCamOffset = endPos; - }, move_carousel ? 500 : 1); + { + t -= 1; + float f = Math::lerp(startPos, endPos, t * t * t + 1); + if(f < 0) + f += posMax; + if(f >= posMax) + f -= posMax; + + this->mCamOffset = move_carousel ? f : endPos; + this->mExtrasCamOffset = f; + + }, + 500); } + else + { + anim = new LambdaAnimation( + [this, startPos, endPos, posMax, move_carousel](float t) + { + t -= 1; + float f = Math::lerp(startPos, endPos, t * t * t + 1); + if(f < 0) + f += posMax; + if(f >= posMax) + f -= posMax; + + this->mCamOffset = move_carousel ? f : endPos; + this->mExtrasCamOffset = endPos; + }, + move_carousel ? 500 : 1); + } setAnimation(anim, 0, nullptr, false, 0); } @@ -347,45 +495,49 @@ void SystemView::onCursorChanged(const CursorState& /*state*/) void SystemView::render(const Transform4x4f& parentTrans) { if(size() == 0) - return; // nothing to render + return; Transform4x4f trans = getTransform() * parentTrans; auto systemInfoZIndex = mSystemInfo.getZIndex(); - auto minMax = std::minmax(mCarousel.zIndex, systemInfoZIndex); + auto minMax = std::minmax(mCarousel.zIndex, systemInfoZIndex); renderExtras(trans, INT16_MIN, minMax.first); renderFade(trans); - if (mCarousel.zIndex > mSystemInfo.getZIndex()) { + if(mCarousel.zIndex > mSystemInfo.getZIndex()) renderInfoBar(trans); - } else { + else renderCarousel(trans); - } renderExtras(trans, minMax.first, minMax.second); - if (mCarousel.zIndex > mSystemInfo.getZIndex()) { + if(mCarousel.zIndex > mSystemInfo.getZIndex()) renderCarousel(trans); - } else { + else renderInfoBar(trans); - } renderExtras(trans, minMax.second, INT16_MAX); } std::vector SystemView::getHelpPrompts() { + LocaleES& loc = LocaleES::getInstance(); + std::vector prompts; - if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) - prompts.push_back(HelpPrompt("up/down", "choose")); + if(mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) + prompts.push_back(HelpPrompt("up/down", loc.translate("CHOOSE"))); else - prompts.push_back(HelpPrompt("left/right", "choose")); - prompts.push_back(HelpPrompt("a", "select")); - prompts.push_back(HelpPrompt("x", "random")); + prompts.push_back(HelpPrompt("left/right", loc.translate("CHOOSE"))); + + prompts.push_back(HelpPrompt("a", loc.translate("SELECT"))); + prompts.push_back(HelpPrompt("x", loc.translate("RANDOM"))); - if (!UIModeController::getInstance()->isUIModeKid() && Settings::getInstance()->getBool("ScreenSaverControls")) - prompts.push_back(HelpPrompt("select", "launch screensaver")); + if(!UIModeController::getInstance()->isUIModeKid() && + Settings::getInstance()->getBool("ScreenSaverControls")) + { + prompts.push_back(HelpPrompt("select", loc.translate("LAUNCH SCREENSAVER"))); + } return prompts; } @@ -397,7 +549,7 @@ HelpStyle SystemView::getHelpStyle() return style; } -void SystemView::onThemeChanged(const std::shared_ptr& /*theme*/) +void SystemView::onThemeChanged(const std::shared_ptr& /*theme*/) { LOG(LogDebug) << "SystemView::onThemeChanged()"; mViewNeedsReload = true; @@ -405,21 +557,21 @@ void SystemView::onThemeChanged(const std::shared_ptr& /*theme*/) } // Get the ThemeElements that make up the SystemView. -void SystemView::getViewElements(const std::shared_ptr& theme) +void SystemView::getViewElements(const std::shared_ptr& theme) { LOG(LogDebug) << "SystemView::getViewElements()"; getDefaultElements(); - if (!theme->hasView("system")) + if(!theme->hasView("system")) return; const ThemeData::ThemeElement* carouselElem = theme->getElement("system", "systemcarousel", "carousel"); - if (carouselElem) + if(carouselElem) getCarouselFromTheme(carouselElem); const ThemeData::ThemeElement* sysInfoElem = theme->getElement("system", "systemInfo", "text"); - if (sysInfoElem) + if(sysInfoElem) mSystemInfo.applyTheme(theme, "system", "systemInfo", ThemeFlags::ALL); mViewNeedsReload = false; @@ -428,151 +580,185 @@ void SystemView::getViewElements(const std::shared_ptr& theme) // Render system carousel void SystemView::renderCarousel(const Transform4x4f& trans) { - if (mEntries.empty()) + if(mEntries.empty()) return; // background box behind logos Transform4x4f carouselTrans = trans; carouselTrans.translate(Vector3f(mCarousel.pos.x(), mCarousel.pos.y(), 0.0)); - carouselTrans.translate(Vector3f(mCarousel.origin.x() * mCarousel.size.x() * -1, mCarousel.origin.y() * mCarousel.size.y() * -1, 0.0f)); + carouselTrans.translate(Vector3f( + mCarousel.origin.x() * mCarousel.size.x() * -1, + mCarousel.origin.y() * mCarousel.size.y() * -1, + 0.0f)); Vector2f clipPos(carouselTrans.translation().x(), carouselTrans.translation().y()); - Renderer::pushClipRect(Vector2i((int)clipPos.x(), (int)clipPos.y()), - Vector2i((int)mCarousel.size.x(), (int)mCarousel.size.y())); + Renderer::pushClipRect( + Vector2i((int)clipPos.x(), (int)clipPos.y()), + Vector2i((int)mCarousel.size.x(), (int)mCarousel.size.y())); Renderer::setMatrix(carouselTrans); - Renderer::drawRect(0.0f, 0.0f, mCarousel.size.x(), mCarousel.size.y(), - mCarousel.color, mCarousel.colorEnd, mCarousel.colorGradientHorizontal); + Renderer::drawRect( + 0.0f, + 0.0f, + mCarousel.size.x(), + mCarousel.size.y(), + mCarousel.color, + mCarousel.colorEnd, + mCarousel.colorGradientHorizontal); // draw logos - Vector2f logoSpacing(0.0, 0.0); // NB: logoSpacing will include the size of the logo itself as well! + Vector2f logoSpacing(0.0, 0.0); float xOff = 0.0f; float yOff = 0.0f; - switch (mCarousel.type) + switch(mCarousel.type) { - case VERTICAL_WHEEL: - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - (mCamOffset * logoSpacing[1]); - if (mCarousel.logoAlignment == ALIGN_LEFT) - xOff = mCarousel.logoSize.x() / 10.f; - else if (mCarousel.logoAlignment == ALIGN_RIGHT) - xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f); - else - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f; - break; - - case VERTICAL: - logoSpacing[1] = ((mCarousel.size.y() - (mCarousel.logoSize.y() * mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.y(); - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - (mCamOffset * logoSpacing[1]); - - if (mCarousel.logoAlignment == ALIGN_LEFT) - xOff = mCarousel.logoSize.x() / 10.f; - else if (mCarousel.logoAlignment == ALIGN_RIGHT) - xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f); - else - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2; - break; - - case HORIZONTAL_WHEEL: - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2 - (mCamOffset * logoSpacing[0]); - if (mCarousel.logoAlignment == ALIGN_TOP) - yOff = mCarousel.logoSize.y() / 10; - else if (mCarousel.logoAlignment == ALIGN_BOTTOM) - yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f); - else - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2; - break; - - case HORIZONTAL: - default: - logoSpacing[0] = ((mCarousel.size.x() - (mCarousel.logoSize.x() * mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.x(); - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f - (mCamOffset * logoSpacing[0]); - - if (mCarousel.logoAlignment == ALIGN_TOP) - yOff = mCarousel.logoSize.y() / 10.f; - else if (mCarousel.logoAlignment == ALIGN_BOTTOM) - yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f); - else - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f; - break; + case VERTICAL_WHEEL: + yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - (mCamOffset * logoSpacing[1]); + if(mCarousel.logoAlignment == ALIGN_LEFT) + xOff = mCarousel.logoSize.x() / 10.f; + else if(mCarousel.logoAlignment == ALIGN_RIGHT) + xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f); + else + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f; + break; + + case VERTICAL: + logoSpacing[1] = ((mCarousel.size.y() - + (mCarousel.logoSize.y() * mCarousel.maxLogoCount)) / + (mCarousel.maxLogoCount)) + + mCarousel.logoSize.y(); + yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f + - (mCamOffset * logoSpacing[1]); + + if(mCarousel.logoAlignment == ALIGN_LEFT) + xOff = mCarousel.logoSize.x() / 10.f; + else if(mCarousel.logoAlignment == ALIGN_RIGHT) + xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f); + else + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2; + break; + + case HORIZONTAL_WHEEL: + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2 - (mCamOffset * logoSpacing[0]); + if(mCarousel.logoAlignment == ALIGN_TOP) + yOff = mCarousel.logoSize.y() / 10; + else if(mCarousel.logoAlignment == ALIGN_BOTTOM) + yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f); + else + yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2; + break; + + case HORIZONTAL: + default: + logoSpacing[0] = ((mCarousel.size.x() - + (mCarousel.logoSize.x() * mCarousel.maxLogoCount)) / + (mCarousel.maxLogoCount)) + + mCarousel.logoSize.x(); + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f + - (mCamOffset * logoSpacing[0]); + + if(mCarousel.logoAlignment == ALIGN_TOP) + yOff = mCarousel.logoSize.y() / 10.f; + else if(mCarousel.logoAlignment == ALIGN_BOTTOM) + yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f); + else + yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f; + break; } - int center = (int)(mCamOffset); + int center = (int)(mCamOffset); int logoCount = Math::min(mCarousel.maxLogoCount, (int)mEntries.size()); // Adding texture loading buffers depending on scrolling speed and status int bufferIndex = getScrollingVelocity() + 1; - if (bufferIndex < 0) bufferIndex = 0; - if (bufferIndex > 2) bufferIndex = 2; + if(bufferIndex < 0) bufferIndex = 0; + if(bufferIndex > 2) bufferIndex = 2; - int bufferLeft = logoBuffersLeft[bufferIndex]; + int bufferLeft = logoBuffersLeft[bufferIndex]; int bufferRight = logoBuffersRight[bufferIndex]; - if (logoCount == 1) + if(logoCount == 1) { - bufferLeft = 0; + bufferLeft = 0; bufferRight = 0; } // lambda para dibujar un logo auto renderLogo = [this, &carouselTrans, &logoSpacing, xOff, yOff](int i) { - if (mEntries.empty()) + if(mEntries.empty()) return; int index = i; - while (index < 0) + while(index < 0) index += (int)mEntries.size(); - while (index >= (int)mEntries.size()) + while(index >= (int)mEntries.size()) index -= (int)mEntries.size(); Transform4x4f logoTrans = carouselTrans; - // Adjust spacing when the focused logo is scaled (horizontal only) - if (mCarousel.type == HORIZONTAL && mCarousel.logoScale != 1.0f && mCarousel.scaledLogoSpacing != 0.0f) - { - float logoDiffX = ((logoSpacing[0] * mCarousel.logoScale) - logoSpacing[0]) / 2.0f * mCarousel.scaledLogoSpacing; + // Ajuste de separación cuando el logo central está escalado (solo HORIZONTAL) + if(mCarousel.type == HORIZONTAL && + mCarousel.logoScale != 1.0f && + mCarousel.scaledLogoSpacing != 0.0f) + { + float logoDiffX = ((logoSpacing[0] * mCarousel.logoScale) - logoSpacing[0]) + / 2.0f * mCarousel.scaledLogoSpacing; - if (index == mCursor) + if(index == mCursor) { - logoTrans.translate(Vector3f(i * logoSpacing[0] + xOff, i * logoSpacing[1] + yOff, 0.0f)); + logoTrans.translate(Vector3f( + i * logoSpacing[0] + xOff, + i * logoSpacing[1] + yOff, + 0.0f)); } - else if (i < mCursor || (mCursor == 0 && index > mCarousel.maxLogoCount)) + else if(i < mCursor || (mCursor == 0 && index > mCarousel.maxLogoCount)) { - logoTrans.translate(Vector3f(i * logoSpacing[0] + xOff - logoDiffX, i * logoSpacing[1] + yOff, 0.0f)); + logoTrans.translate(Vector3f( + i * logoSpacing[0] + xOff - logoDiffX, + i * logoSpacing[1] + yOff, + 0.0f)); } else { - logoTrans.translate(Vector3f(i * logoSpacing[0] + xOff + logoDiffX, i * logoSpacing[1] + yOff, 0.0f)); + logoTrans.translate(Vector3f( + i * logoSpacing[0] + xOff + logoDiffX, + i * logoSpacing[1] + yOff, + 0.0f)); } } else { - logoTrans.translate(Vector3f(i * logoSpacing[0] + xOff, i * logoSpacing[1] + yOff, 0.0f)); + logoTrans.translate(Vector3f( + i * logoSpacing[0] + xOff, + i * logoSpacing[1] + yOff, + 0.0f)); } float distance = i - mCamOffset; // escala según distancia float scale = 1.0f + ((mCarousel.logoScale - 1.0f) * (1.0f - fabs(distance))); - scale = Math::min(mCarousel.logoScale, Math::max(1.0f, scale)); - scale /= mCarousel.logoScale; + scale = Math::min(mCarousel.logoScale, Math::max(1.0f, scale)); + scale /= mCarousel.logoScale; - // Configurable minimum opacity - float minOpacity = mCarousel.minLogoOpacity; - if (minOpacity < 0.0f) minOpacity = 0.0f; - if (minOpacity > 1.0f) minOpacity = 1.0f; + // opacidad mínima configurable + float minOpacity = mCarousel.minLogoOpacity; + if(minOpacity < 0.0f) minOpacity = 0.0f; + if(minOpacity > 1.0f) minOpacity = 1.0f; - int opref = (int)Math::round(minOpacity * 255.0f); + int opref = (int)Math::round(minOpacity * 255.0f); int opacity = (int)Math::round(opref + ((0xFF - opref) * (1.0f - fabs(distance)))); - if (opacity < opref) + if(opacity < opref) opacity = opref; - const std::shared_ptr &comp = mEntries.at(index).data.logo; - if (!comp) + const std::shared_ptr& comp = mEntries.at(index).data.logo; + if(!comp) return; - if (mCarousel.type == VERTICAL_WHEEL || mCarousel.type == HORIZONTAL_WHEEL) { + if(mCarousel.type == VERTICAL_WHEEL || mCarousel.type == HORIZONTAL_WHEEL) + { comp->setRotationDegrees(mCarousel.logoRotation * distance); comp->setRotationOrigin(mCarousel.logoRotationOrigin); } @@ -583,22 +769,24 @@ void SystemView::renderCarousel(const Transform4x4f& trans) // Primero todos menos el seleccionado std::vector activePositions; - for (int i = center - logoCount / 2 + bufferLeft; i <= center + logoCount / 2 + bufferRight; i++) + for(int i = center - logoCount / 2 + bufferLeft; + i <= center + logoCount / 2 + bufferRight; + i++) { int index = i; - while (index < 0) + while(index < 0) index += (int)mEntries.size(); - while (index >= (int)mEntries.size()) + while(index >= (int)mEntries.size()) index -= (int)mEntries.size(); - if (index == mCursor) + if(index == mCursor) activePositions.push_back(i); else renderLogo(i); } // Luego el seleccionado, para vencer la superposición - for (auto activePos : activePositions) + for(auto activePos : activePositions) renderLogo(activePos); Renderer::popClipRect(); @@ -620,42 +808,47 @@ void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upp Renderer::pushClipRect(Vector2i::Zero(), Vector2i((int)mSize.x(), (int)mSize.y())); - for (int i = extrasCenter + logoBuffersLeft[bufferIndex]; i <= extrasCenter + logoBuffersRight[bufferIndex]; i++) + for(int i = extrasCenter + logoBuffersLeft[bufferIndex]; + i <= extrasCenter + logoBuffersRight[bufferIndex]; + i++) { int index = i; - while (index < 0) + while(index < 0) index += (int)mEntries.size(); - while (index >= (int)mEntries.size()) + while(index >= (int)mEntries.size()) index -= (int)mEntries.size(); - //Only render selected system when not showing - if (mShowing || index == mCursor) + // Only render selected system when not showing + if(mShowing || index == mCursor) { Transform4x4f extrasTrans = trans; - if (mCarousel.type == HORIZONTAL || mCarousel.type == HORIZONTAL_WHEEL) + if(mCarousel.type == HORIZONTAL || mCarousel.type == HORIZONTAL_WHEEL) extrasTrans.translate(Vector3f((i - mExtrasCamOffset) * mSize.x(), 0, 0)); else extrasTrans.translate(Vector3f(0, (i - mExtrasCamOffset) * mSize.y(), 0)); - Renderer::pushClipRect(Vector2i((int)extrasTrans.translation()[0], (int)extrasTrans.translation()[1]), - Vector2i((int)mSize.x(), (int)mSize.y())); + Renderer::pushClipRect( + Vector2i((int)extrasTrans.translation()[0], (int)extrasTrans.translation()[1]), + Vector2i((int)mSize.x(), (int)mSize.y())); + SystemViewData data = mEntries.at(index).data; - for (unsigned int j = 0; j < data.backgroundExtras.size(); j++) { - GuiComponent *extra = data.backgroundExtras[j]; - if (extra->getZIndex() >= lower && extra->getZIndex() < upper) { + for(unsigned int j = 0; j < data.backgroundExtras.size(); j++) + { + GuiComponent* extra = data.backgroundExtras[j]; + if(extra->getZIndex() >= lower && extra->getZIndex() < upper) extra->render(extrasTrans); - } } Renderer::popClipRect(); } } + Renderer::popClipRect(); } void SystemView::renderFade(const Transform4x4f& trans) { // fade extras if necessary - if (mExtrasFadeOpacity) + if(mExtrasFadeOpacity) { unsigned int fadeColor = 0x00000000 | (unsigned char)(mExtrasFadeOpacity * 255); Renderer::setMatrix(trans); @@ -664,38 +857,38 @@ void SystemView::renderFade(const Transform4x4f& trans) } // Populate the system carousel with the legacy values -void SystemView::getDefaultElements(void) +void SystemView::getDefaultElements(void) { - // Carousel - mCarousel.type = HORIZONTAL; - mCarousel.logoAlignment = ALIGN_CENTER; - mCarousel.size.x() = mSize.x(); - mCarousel.size.y() = 0.2325f * mSize.y(); - mCarousel.pos.x() = 0.0f; - mCarousel.pos.y() = 0.5f * (mSize.y() - mCarousel.size.y()); - mCarousel.origin.x() = 0.0f; - mCarousel.origin.y() = 0.0f; - mCarousel.color = 0xFFFFFFD8; - mCarousel.colorEnd = 0xFFFFFFD8; + // Carousel + mCarousel.type = HORIZONTAL; + mCarousel.logoAlignment = ALIGN_CENTER; + mCarousel.size.x() = mSize.x(); + mCarousel.size.y() = 0.2325f * mSize.y(); + mCarousel.pos.x() = 0.0f; + mCarousel.pos.y() = 0.5f * (mSize.y() - mCarousel.size.y()); + mCarousel.origin.x() = 0.0f; + mCarousel.origin.y() = 0.0f; + mCarousel.color = 0xFFFFFFD8; + mCarousel.colorEnd = 0xFFFFFFD8; mCarousel.colorGradientHorizontal = true; - mCarousel.logoScale = 1.2f; - mCarousel.logoRotation = 7.5; - mCarousel.logoRotationOrigin.x() = -5; - mCarousel.logoRotationOrigin.y() = 0.5; - mCarousel.logoSize.x() = 0.25f * mSize.x(); - mCarousel.logoSize.y() = 0.155f * mSize.y(); - mCarousel.maxLogoCount = 3; - mCarousel.zIndex = 40; - - // Default values for additional visual tuning - mCarousel.minLogoOpacity = 0.5f; // equivalent to the previous 0x80 minimum - mCarousel.scaledLogoSpacing = 0.0f; // 0 = classic behavior - - // Reset optional per-theme values - mScrollSound.clear(); + mCarousel.logoScale = 1.2f; + mCarousel.logoRotation = 7.5f; + mCarousel.logoRotationOrigin.x() = -5.0f; + mCarousel.logoRotationOrigin.y() = 0.5f; + mCarousel.logoSize.x() = 0.25f * mSize.x(); + mCarousel.logoSize.y() = 0.155f * mSize.y(); + mCarousel.maxLogoCount = 3; + mCarousel.zIndex = 40; + + // valores por defecto para mejoras visuales + mCarousel.minLogoOpacity = 0.5f; // equivalente a 0x80 de antes + mCarousel.scaledLogoSpacing = 0.0f; // 0 = comportamiento clásico + + // sonido por defecto: ninguno (se rellena desde o NavigationSounds) + mScrollSnd.reset(); // System Info Bar - mSystemInfo.setSize(mSize.x(), mSystemInfo.getFont()->getLetterHeight()*2.2f); + mSystemInfo.setSize(mSize.x(), mSystemInfo.getFont()->getLetterHeight() * 2.2f); mSystemInfo.setPosition(0, (mCarousel.pos.y() + mCarousel.size.y() - 0.2f)); mSystemInfo.setBackgroundColor(0xDDDDDDD8); mSystemInfo.setRenderBackground(true); @@ -707,80 +900,85 @@ void SystemView::getDefaultElements(void) void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem) { - if (elem->has("type")) + if(elem->has("type")) { - if (!(elem->get("type").compare("vertical"))) + if(!(elem->get("type").compare("vertical"))) mCarousel.type = VERTICAL; - else if (!(elem->get("type").compare("vertical_wheel"))) + else if(!(elem->get("type").compare("vertical_wheel"))) mCarousel.type = VERTICAL_WHEEL; - else if (!(elem->get("type").compare("horizontal_wheel"))) + else if(!(elem->get("type").compare("horizontal_wheel"))) mCarousel.type = HORIZONTAL_WHEEL; else mCarousel.type = HORIZONTAL; } - if (elem->has("size")) + + if(elem->has("size")) mCarousel.size = elem->get("size") * mSize; - if (elem->has("pos")) + + if(elem->has("pos")) mCarousel.pos = elem->get("pos") * mSize; - if (elem->has("origin")) + + if(elem->has("origin")) mCarousel.origin = elem->get("origin"); - if (elem->has("color")) + + if(elem->has("color")) { - mCarousel.color = elem->get("color"); + mCarousel.color = elem->get("color"); mCarousel.colorEnd = mCarousel.color; } - if (elem->has("colorEnd")) + + if(elem->has("colorEnd")) mCarousel.colorEnd = elem->get("colorEnd"); - if (elem->has("gradientType")) + + if(elem->has("gradientType")) mCarousel.colorGradientHorizontal = !(elem->get("gradientType").compare("horizontal")); - if (elem->has("logoScale")) + + if(elem->has("logoScale")) mCarousel.logoScale = elem->get("logoScale"); - if (elem->has("logoSize")) + + if(elem->has("logoSize")) mCarousel.logoSize = elem->get("logoSize") * mSize; - if (elem->has("maxLogoCount")) + + if(elem->has("maxLogoCount")) mCarousel.maxLogoCount = (int)Math::round(elem->get("maxLogoCount")); - if (elem->has("zIndex")) + + if(elem->has("zIndex")) mCarousel.zIndex = elem->get("zIndex"); - if (elem->has("logoRotation")) + + if(elem->has("logoRotation")) mCarousel.logoRotation = elem->get("logoRotation"); - if (elem->has("logoRotationOrigin")) + + if(elem->has("logoRotationOrigin")) mCarousel.logoRotationOrigin = elem->get("logoRotationOrigin"); - if (elem->has("logoAlignment")) + + if(elem->has("logoAlignment")) { - if (!(elem->get("logoAlignment").compare("left"))) + if(!(elem->get("logoAlignment").compare("left"))) mCarousel.logoAlignment = ALIGN_LEFT; - else if (!(elem->get("logoAlignment").compare("right"))) + else if(!(elem->get("logoAlignment").compare("right"))) mCarousel.logoAlignment = ALIGN_RIGHT; - else if (!(elem->get("logoAlignment").compare("top"))) + else if(!(elem->get("logoAlignment").compare("top"))) mCarousel.logoAlignment = ALIGN_TOP; - else if (!(elem->get("logoAlignment").compare("bottom"))) + else if(!(elem->get("logoAlignment").compare("bottom"))) mCarousel.logoAlignment = ALIGN_BOTTOM; else mCarousel.logoAlignment = ALIGN_CENTER; } - if (elem->has("scrollSound")) - mScrollSound = elem->get("scrollSound"); - // Read optional carousel extensions - if (elem->has("minLogoOpacity")) - mCarousel.minLogoOpacity = elem->get("minLogoOpacity"); - - if (elem->has("scaledLogoSpacing")) - mCarousel.scaledLogoSpacing = elem->get("scaledLogoSpacing"); -} + // lectura de propiedades extra + if(elem->has("minLogoOpacity")) + mCarousel.minLogoOpacity = elem->get("minLogoOpacity"); -void SystemView::onScroll(int amt) -{ - if (amt != 0 && !mScrollSound.empty()) - Sound::get(mScrollSound)->play(); -} + if(elem->has("scaledLogoSpacing")) + mCarousel.scaledLogoSpacing = elem->get("scaledLogoSpacing"); -void SystemView::onShow() -{ - mShowing = true; -} - -void SystemView::onHide() -{ - mShowing = false; + // sonido opcional para scroll del carrusel, definido dentro del + if(elem->has("scrollSound")) + { + std::string path = elem->get("scrollSound"); + if(!path.empty()) + { + mScrollSnd = Sound::get(path); // registra internamente en AudioManager + } + } } diff --git a/es-app/src/views/SystemView.h b/es-app/src/views/SystemView.h index 28dcbb8779..de6bc8e2ba 100644 --- a/es-app/src/views/SystemView.h +++ b/es-app/src/views/SystemView.h @@ -6,6 +6,7 @@ #include "components/TextComponent.h" #include "resources/Font.h" #include "GuiComponent.h" +#include "Sound.h" // ← para std::shared_ptr #include #include @@ -43,9 +44,9 @@ struct SystemViewCarousel Vector2f logoSize; float zIndex; - // Additional visual tuning properties - float minLogoOpacity; // Minimum opacity (0.0f–1.0f) - float scaledLogoSpacing; // Extra spacing when the focused logo is scaled + // NUEVO: propiedades mejoradas para comportamiento visual + float minLogoOpacity; // Opacidad mínima (0.0f–1.0f) + float scaledLogoSpacing; // Ajuste de separación cuando el logo central está escalado }; @@ -60,8 +61,7 @@ class SystemView : public IList void goToSystem(SystemData* system, bool animate); bool input(InputConfig* config, Input input) override; - void update(int deltaTime) override; - void onScroll(int amt) override; + void update(int deltaTime) override; void render(const Transform4x4f& parentTrans) override; void onThemeChanged(const std::shared_ptr& theme); @@ -94,7 +94,9 @@ class SystemView : public IList bool mViewNeedsReload; bool mShowing; - std::string mScrollSound; + // NUEVO: sonido opcional al mover el carrusel + // Se carga desde scrollSound del . + std::shared_ptr mScrollSnd; }; #endif // ES_APP_VIEWS_SYSTEM_VIEW_H diff --git a/es-app/src/views/ViewController.cpp b/es-app/src/views/ViewController.cpp index 3d5079acec..0c26c87126 100644 --- a/es-app/src/views/ViewController.cpp +++ b/es-app/src/views/ViewController.cpp @@ -17,6 +17,13 @@ #include "Settings.h" #include "SystemData.h" #include "Window.h" +#include "Sound.h" + +// ✅ Popup concreto (implementa Window::InfoPopup) +#include "guis/GuiInfoPopup.h" + +// 🔊 NUEVO: música de fondo ES-X +#include "audio/BackgroundMusicManager.h" ViewController* ViewController::sInstance = NULL; @@ -96,6 +103,15 @@ void ViewController::goToSystemView(SystemData* system) mCurrentView->onHide(); } + // 🔊 SONIDO DE BACK: + // Solo reproducir si venimos de una gamelist (volver al carrusel) + if (mState.viewing == GAME_LIST) + { + auto backSound = Sound::getFromTheme(system->getTheme(), "system", "backSound"); + if (backSound) + backSound->play(); + } + mState.viewing = SYSTEM_SELECT; mState.system = system; @@ -274,6 +290,9 @@ void ViewController::launch(FileData* game, Vector3f center) mWindow->stopInfoPopup(); // make sure we disable any existing info popup mLockInput = true; + // 🔊 NUEVO: avisar que se lanza un juego (detener música de fondo) + BackgroundMusicManager::getInstance().onGameLaunched(); + std::string transition_style = Settings::getInstance()->getString("TransitionStyle"); if(transition_style == "fade") { @@ -284,6 +303,10 @@ void ViewController::launch(FileData* game, Vector3f center) setAnimation(new LambdaAnimation(fadeFunc, 800), 0, [this, game, fadeFunc] { game->launchGame(mWindow); + + // 🔊 NUEVO: al volver del juego → reanudar música si corresponde + BackgroundMusicManager::getInstance().onGameEnded(); + setAnimation(new LambdaAnimation(fadeFunc, 800), 0, [this, game] { mLockInput = false; }, true); this->onFileChanged(game, FILE_METADATA_CHANGED); if (mCurrentView) { @@ -296,6 +319,10 @@ void ViewController::launch(FileData* game, Vector3f center) setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 1500), 0, [this, origCamera, center, game] { game->launchGame(mWindow); + + // 🔊 NUEVO: al volver del juego + BackgroundMusicManager::getInstance().onGameEnded(); + mCamera = origCamera; setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 600), 0, [this, game] { mLockInput = false; }, true); this->onFileChanged(game, FILE_METADATA_CHANGED); @@ -308,6 +335,10 @@ void ViewController::launch(FileData* game, Vector3f center) setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 10), 0, [this, origCamera, center, game] { game->launchGame(mWindow); + + // 🔊 NUEVO: al volver del juego + BackgroundMusicManager::getInstance().onGameEnded(); + mCamera = origCamera; setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 10), 0, [this, game] { mLockInput = false; }, true); this->onFileChanged(game, FILE_METADATA_CHANGED); @@ -424,7 +455,6 @@ std::shared_ptr ViewController::getSystemListView() return mSystemListView; } - bool ViewController::input(InputConfig* config, Input input) { if(mLockInput) @@ -456,6 +486,20 @@ void ViewController::update(int deltaTime) mCurrentView->update(deltaTime); } + // ========================== + // 🎵 Popup “Now playing” + // ========================== + auto& bgm = BackgroundMusicManager::getInstance(); + if (bgm.songNameChanged()) + { + mWindow->stopInfoPopup(); + + // ✅ GuiInfoPopup implementa Window::InfoPopup en tu fork + mWindow->setInfoPopup(new GuiInfoPopup(mWindow, "♪♪" + bgm.getCurrentSongDisplayName(), 4000)); + + bgm.resetSongNameChangedFlag(); + } + updateSelf(deltaTime); } @@ -552,7 +596,6 @@ void ViewController::reloadGameListView(IGameListView* view, bool reloadTheme) // Redisplay the current view if (mCurrentView) mCurrentView->onShow(); - } void ViewController::reloadAll(bool themeChanged) diff --git a/es-app/src/views/gamelist/BasicGameListView.cpp b/es-app/src/views/gamelist/BasicGameListView.cpp index 605c8bad2b..ae15af21cb 100644 --- a/es-app/src/views/gamelist/BasicGameListView.cpp +++ b/es-app/src/views/gamelist/BasicGameListView.cpp @@ -6,6 +6,56 @@ #include "CollectionSystemManager.h" #include "Settings.h" #include "SystemData.h" +#include "LocaleES.h" + +// NUEVO: soporte de sonidos de navegación +#include "Sound.h" +#include "ThemeData.h" + +namespace +{ + // Busca un ThemeElement de tipo "sound" por nombre, considerando + // (view="all") y vistas gamelist/system. + inline std::shared_ptr getNavSound(SystemData* sys, const std::string& name) + { + if (!sys) + return nullptr; + + const std::shared_ptr& theme = sys->getTheme(); + if (!theme) + return nullptr; + + const ThemeData::ThemeElement* elem = nullptr; + + // 1) Esquema Batocera: + // , , , etc. + elem = theme->getElement("all", name, "sound"); + + // 2) Por si el tema lo define en la vista "gamelist" + if (!elem && theme->hasView("gamelist")) + elem = theme->getElement("gamelist", name, "sound"); + + // 3) Fallback: vista "system" + if (!elem && theme->hasView("system")) + elem = theme->getElement("system", name, "sound"); + + if (!elem || !elem->has("path")) + return nullptr; + + std::string path = elem->get("path"); + if (path.empty()) + return nullptr; + + return Sound::get(path); + } + + inline void playNavSound(SystemData* sys, const std::string& name) + { + auto snd = getNavSound(sys, name); + if (snd) + snd->play(); + } +} BasicGameListView::BasicGameListView(Window* window, FileData* root) : ISimpleGameListView(window, root), mList(window) @@ -29,7 +79,7 @@ void BasicGameListView::onThemeChanged(const std::shared_ptr& theme) void BasicGameListView::onFileChanged(FileData* file, FileChangeType change) { - if(change == FILE_METADATA_CHANGED) + if (change == FILE_METADATA_CHANGED) { // might switch to a detailed view ViewController::get()->reloadGameListView(this); @@ -45,7 +95,7 @@ void BasicGameListView::populateList(const std::vector& files) mHeaderText.setText(mRoot->getSystem()->getFullName()); if (files.size() > 0) { - for(auto it = files.cbegin(); it != files.cend(); it++) + for (auto it = files.cbegin(); it != files.cend(); it++) { mList.add((*it)->getName(), *it, ((*it)->getType() == FOLDER)); } @@ -67,7 +117,7 @@ void BasicGameListView::setCursor(FileData* cursor, bool refreshListCursorPos) setViewportTop(mList.REFRESH_LIST_CURSOR_POS); bool notInList = !mList.setCursor(cursor); - if(!refreshListCursorPos && notInList && !cursor->isPlaceHolder()) + if (!refreshListCursorPos && notInList && !cursor->isPlaceHolder()) { populateList(cursor->getParent()->getChildrenListToDisplay()); // this extra call is needed iff a system has games organized in folders @@ -76,11 +126,11 @@ void BasicGameListView::setCursor(FileData* cursor, bool refreshListCursorPos) mList.setCursor(cursor); // update our cursor stack in case our cursor just got set to some folder we weren't in before - if(mCursorStack.empty() || mCursorStack.top() != cursor->getParent()) + if (mCursorStack.empty() || mCursorStack.top() != cursor->getParent()) { std::stack tmp; FileData* ptr = cursor->getParent(); - while(ptr && ptr != mRoot) + while (ptr && ptr != mRoot) { tmp.push(ptr); ptr = ptr->getParent(); @@ -88,7 +138,7 @@ void BasicGameListView::setCursor(FileData* cursor, bool refreshListCursorPos) // flip the stack and put it in mCursorStack mCursorStack = std::stack(); - while(!tmp.empty()) + while (!tmp.empty()) { mCursorStack.push(tmp.top()); tmp.pop(); @@ -102,7 +152,6 @@ void BasicGameListView::setViewportTop(int index) mList.setViewportTop(index); } - int BasicGameListView::getViewportTop() { return mList.getViewportTop(); @@ -110,8 +159,20 @@ int BasicGameListView::getViewportTop() void BasicGameListView::addPlaceholder() { + LocaleES& loc = LocaleES::getInstance(); + + // Intentamos traducir; si no hay traducción, dejamos el texto por defecto. + std::string placeholderName = loc.translate("NO ENTRIES FOUND"); + if (placeholderName == "NO ENTRIES FOUND") + placeholderName = ""; + // empty list - add a placeholder - FileData* placeholder = new FileData(PLACEHOLDER, "", this->mRoot->getSystem()->getSystemEnvData(), this->mRoot->getSystem()); + FileData* placeholder = new FileData( + PLACEHOLDER, + placeholderName, + this->mRoot->getSystem()->getSystemEnvData(), + this->mRoot->getSystem()); + mList.add(placeholder->getName(), placeholder, (placeholder->getType() == PLACEHOLDER)); } @@ -127,6 +188,10 @@ std::string BasicGameListView::getQuickSystemSelectLeftButton() void BasicGameListView::launch(FileData* game) { + // SONIDO DE LAUNCH (Batocera-style) + if (game) + playNavSound(game->getSystem(), "launch"); + ViewController::get()->launch(game); } @@ -191,42 +256,85 @@ void BasicGameListView::remove(FileData *game, bool deleteFile, bool refreshView if ((gamePos + 1) < siblings.size()) { setCursor(siblings.at(gamePos + 1)); - } else if (gamePos > 1) { + } + else if (gamePos > 1) { setCursor(siblings.at(gamePos - 1)); } } } mList.remove(game); - if(mList.size() == 0) + if (mList.size() == 0) { addPlaceholder(); } delete game; // remove before repopulating (removes from parent) - if(refreshView) + if (refreshView) onFileChanged(parent, FILE_REMOVED); // update the view, with game removed } std::vector BasicGameListView::getHelpPrompts() { + LocaleES& loc = LocaleES::getInstance(); + std::vector prompts; - if(Settings::getInstance()->getBool("QuickSystemSelect")) - prompts.push_back(HelpPrompt("left/right", "system")); - prompts.push_back(HelpPrompt("up/down", "choose")); - prompts.push_back(HelpPrompt("a", "launch")); - prompts.push_back(HelpPrompt("b", "back")); - if(!UIModeController::getInstance()->isUIModeKid()) - prompts.push_back(HelpPrompt("select", "options")); - if(mRoot->getSystem()->isGameSystem()) - prompts.push_back(HelpPrompt("x", "random")); - if(mRoot->getSystem()->isGameSystem() && !UIModeController::getInstance()->isUIModeKid()) + if (Settings::getInstance()->getBool("QuickSystemSelect")) + { + // "SYSTEM" no está en tu .ini aún, pero si no existe saldrá "SYSTEM" + prompts.push_back(HelpPrompt("left/right", loc.translate("SYSTEM"))); + } + + prompts.push_back(HelpPrompt("up/down", loc.translate("CHOOSE"))); + prompts.push_back(HelpPrompt("a", loc.translate("START"))); + prompts.push_back(HelpPrompt("b", loc.translate("BACK"))); + + if (!UIModeController::getInstance()->isUIModeKid()) + { + // Puedes añadir OPTIONS=OPCIONES al .ini si quieres + prompts.push_back(HelpPrompt("select", loc.translate("OPTIONS"))); + } + + if (mRoot->getSystem()->isGameSystem()) + prompts.push_back(HelpPrompt("x", loc.translate("RANDOM"))); + + if (mRoot->getSystem()->isGameSystem() && !UIModeController::getInstance()->isUIModeKid()) { std::string prompt = CollectionSystemManager::get()->getEditingCollection(); prompts.push_back(HelpPrompt("y", prompt)); } + return prompts; } void BasicGameListView::onFocusLost() { mList.stopScrolling(true); } + +// NUEVO: interceptar input para reproducir BACK y FAVORITE +bool BasicGameListView::input(InputConfig* config, Input input) +{ + // Llamamos primero a la lógica base para no romper nada importante + if (ISimpleGameListView::input(config, input)) + return true; + + if (input.value != 0) + { + SystemData* sys = mRoot ? mRoot->getSystem() : nullptr; + + // BACK (B) + if (config->isMappedTo("b", input)) + { + playNavSound(sys, "back"); + return true; // evento consumido + } + + // FAVORITE / EDIT COLLECTION (Y) + if (config->isMappedTo("y", input)) + { + playNavSound(sys, "favorite"); + return true; // evento consumido + } + } + + return false; +} diff --git a/es-app/src/views/gamelist/BasicGameListView.h b/es-app/src/views/gamelist/BasicGameListView.h index afa9394c61..8dc1bffff7 100644 --- a/es-app/src/views/gamelist/BasicGameListView.h +++ b/es-app/src/views/gamelist/BasicGameListView.h @@ -25,13 +25,16 @@ class BasicGameListView : public ISimpleGameListView virtual std::vector getHelpPrompts() override; virtual void launch(FileData* game) override; - void onFocusLost() override; + // Necesario porque en el .cpp ya existe BasicGameListView::input(...) + virtual bool input(InputConfig* config, Input input) override; + + void onFocusLost() override; protected: virtual std::string getQuickSystemSelectRightButton() override; virtual std::string getQuickSystemSelectLeftButton() override; virtual void populateList(const std::vector& files) override; - virtual void remove(FileData* game, bool deleteFile, bool refreshView=true) override; + virtual void remove(FileData* game, bool deleteFile, bool refreshView = true) override; virtual void addPlaceholder(); TextListComponent mList; diff --git a/es-app/src/views/gamelist/DetailedGameListView.cpp b/es-app/src/views/gamelist/DetailedGameListView.cpp index ecac95c3bd..4a83c9d19e 100644 --- a/es-app/src/views/gamelist/DetailedGameListView.cpp +++ b/es-app/src/views/gamelist/DetailedGameListView.cpp @@ -2,6 +2,7 @@ #include "animations/LambdaAnimation.h" #include "views/ViewController.h" +#include "LocaleES.h" // ← NUEVO: sistema de idiomas DetailedGameListView::DetailedGameListView(Window* window, FileData* root) : BasicGameListView(window, root), @@ -17,8 +18,6 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) : mGenre(window), mPlayers(window), mLastPlayed(window), mPlayCount(window), mName(window) { - //mHeaderImage.setPosition(mSize.x() * 0.25f, 0); - const float padding = 0.01f; mList.setPosition(mSize.x() * (0.50f + padding), mList.getPosition().y()); @@ -26,15 +25,14 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) : mList.setAlignment(TextListComponent::ALIGN_LEFT); mList.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); }); - // Image + // Imagen principal mImage.setOrigin(0.5f, 0.5f); mImage.setPosition(mSize.x() * 0.25f, mList.getPosition().y() + mSize.y() * 0.2125f); - mImage.setMaxSize(mSize.x() * (0.50f - 2*padding), mSize.y() * 0.4f); + mImage.setMaxSize(mSize.x() * (0.50f - 2 * padding), mSize.y() * 0.4f); mImage.setDefaultZIndex(30); addChild(&mImage); - // Thumbnail - // Default to off the screen + // Thumbnail (por defecto fuera de pantalla) mThumbnail.setOrigin(0.5f, 0.5f); mThumbnail.setPosition(2.0f, 2.0f); mThumbnail.setMaxSize(mSize.x(), mSize.y()); @@ -42,8 +40,7 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) : mThumbnail.setVisible(false); addChild(&mThumbnail); - // Marquee - // Default to off the screen + // Marquee (por defecto fuera de pantalla) mMarquee.setOrigin(0.5f, 0.5f); mMarquee.setPosition(2.0f, 2.0f); mMarquee.setMaxSize(mSize.x(), mSize.y()); @@ -51,32 +48,43 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) : mMarquee.setVisible(false); addChild(&mMarquee); - // metadata labels + values - mLblRating.setText("Rating: "); + // --- Localización de etiquetas --- + LocaleES& loc = LocaleES::getInstance(); + loc.loadFromSettings(); // por si aún no se cargó en este contexto + + mLblRating.setText(loc.translate("RATING") + ": "); addChild(&mLblRating); addChild(&mRating); - mLblReleaseDate.setText("Released: "); + + mLblReleaseDate.setText(loc.translate("RELEASE DATE") + ": "); addChild(&mLblReleaseDate); addChild(&mReleaseDate); - mLblDeveloper.setText("Developer: "); + + mLblDeveloper.setText(loc.translate("DEVELOPER") + ": "); addChild(&mLblDeveloper); addChild(&mDeveloper); - mLblPublisher.setText("Publisher: "); + + mLblPublisher.setText(loc.translate("PUBLISHER") + ": "); addChild(&mLblPublisher); addChild(&mPublisher); - mLblGenre.setText("Genre: "); + + mLblGenre.setText(loc.translate("GENRE") + ": "); addChild(&mLblGenre); addChild(&mGenre); - mLblPlayers.setText("Players: "); + + mLblPlayers.setText(loc.translate("PLAYERS") + ": "); addChild(&mLblPlayers); addChild(&mPlayers); - mLblLastPlayed.setText("Last played: "); + + mLblLastPlayed.setText(loc.translate("LAST PLAYED") + ": "); addChild(&mLblLastPlayed); mLastPlayed.setDisplayRelative(true); addChild(&mLastPlayed); - mLblPlayCount.setText("Times played: "); + + mLblPlayCount.setText(loc.translate("PLAY COUNT") + ": "); addChild(&mLblPlayCount); addChild(&mPlayCount); + // --- fin localización de etiquetas --- mName.setPosition(mSize.x(), mSize.y()); mName.setDefaultZIndex(40); @@ -86,7 +94,7 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) : addChild(&mName); mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f); - mDescContainer.setSize(mSize.x() * (0.50f - 2*padding), mSize.y() - mDescContainer.getPosition().y()); + mDescContainer.setSize(mSize.x() * (0.50f - 2 * padding), mSize.y() - mDescContainer.getPosition().y()); mDescContainer.setAutoScroll(true); mDescContainer.setDefaultZIndex(40); addChild(&mDescContainer); @@ -95,7 +103,6 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) : mDescription.setSize(mDescContainer.getSize().x(), 0); mDescContainer.addChild(&mDescription); - initMDLabels(); initMDValues(); updateInfoPanel(); @@ -119,12 +126,11 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr& them "md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount" }; - for(unsigned int i = 0; i < labels.size(); i++) + for (unsigned int i = 0; i < labels.size(); i++) { labels[i]->applyTheme(theme, getName(), lblElements[i], ALL); } - initMDValues(); std::vector values = getMDValues(); assert(values.size() == 8); @@ -133,7 +139,7 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr& them "md_genre", "md_players", "md_lastplayed", "md_playcount" }; - for(unsigned int i = 0; i < values.size(); i++) + for (unsigned int i = 0; i < values.size(); i++) { values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT); } @@ -157,16 +163,17 @@ void DetailedGameListView::initMDLabels() const float colSize = (mSize.x() * 0.48f) / colCount; const float rowPadding = 0.01f * mSize.y(); - for(unsigned int i = 0; i < components.size(); i++) + for (unsigned int i = 0; i < components.size(); i++) { const unsigned int row = i % rowCount; Vector3f pos(0.0f, 0.0f, 0.0f); - if(row == 0) + if (row == 0) { pos = start + Vector3f(colSize * (i / rowCount), 0, 0); - }else{ + } + else { // work from the last component - GuiComponent* lc = components[i-1]; + GuiComponent* lc = components[i - 1]; pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0); } @@ -194,7 +201,7 @@ void DetailedGameListView::initMDValues() float bottom = 0.0f; const float colSize = (mSize.x() * 0.48f) / 2; - for(unsigned int i = 0; i < labels.size(); i++) + for (unsigned int i = 0; i < labels.size(); i++) { const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2; values[i]->setPosition(labels[i]->getPosition() + Vector3f(labels[i]->getSize().x(), heightDiff, 0)); @@ -202,7 +209,7 @@ void DetailedGameListView::initMDValues() values[i]->setDefaultZIndex(40); float testBot = values[i]->getPosition().y() + values[i]->getSize().y(); - if(testBot > bottom) + if (testBot > bottom) bottom = testBot; } @@ -215,12 +222,11 @@ void DetailedGameListView::updateInfoPanel() FileData* file = (mList.size() == 0 || mList.isScrolling()) ? NULL : mList.getSelected(); bool fadingOut; - if(file == NULL) + if (file == NULL) { - //mImage.setImage(""); - //mDescription.setText(""); fadingOut = true; - }else{ + } + else { mThumbnail.setImage(file->getThumbnailPath()); mMarquee.setImage(file->getMarqueePath()); mImage.setImage(file->getImagePath()); @@ -235,7 +241,7 @@ void DetailedGameListView::updateInfoPanel() mPlayers.setValue(file->metadata.get("players")); mName.setValue(file->metadata.get("name")); - if(file->getType() == GAME) + if (file->getType() == GAME) { mLastPlayed.setValue(file->metadata.get("lastplayed")); mPlayCount.setValue(file->metadata.get("playcount")); @@ -253,19 +259,19 @@ void DetailedGameListView::updateInfoPanel() std::vector labels = getMDLabels(); comps.insert(comps.cend(), labels.cbegin(), labels.cend()); - for(auto it = comps.cbegin(); it != comps.cend(); it++) + for (auto it = comps.cbegin(); it != comps.cend(); it++) { GuiComponent* comp = *it; // an animation is playing // then animate if reverse != fadingOut // an animation is not playing // then animate if opacity != our target opacity - if((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) || + if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) || (!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) { auto func = [comp](float t) { - comp->setOpacity((unsigned char)(Math::lerp(0.0f, 1.0f, t)*255)); + comp->setOpacity((unsigned char)(Math::lerp(0.0f, 1.0f, t) * 255)); }; comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut); } @@ -275,7 +281,7 @@ void DetailedGameListView::updateInfoPanel() void DetailedGameListView::launch(FileData* game) { Vector3f target(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f, 0); - if(mImage.hasImage()) + if (mImage.hasImage()) target = Vector3f(mImage.getCenter().x(), mImage.getCenter().y(), 0); ViewController::get()->launch(game, target); diff --git a/es-app/src/views/gamelist/GridGameListView.cpp b/es-app/src/views/gamelist/GridGameListView.cpp index c013fa3284..a6f8dee3c8 100644 --- a/es-app/src/views/gamelist/GridGameListView.cpp +++ b/es-app/src/views/gamelist/GridGameListView.cpp @@ -10,6 +10,7 @@ #include "components/VideoPlayerComponent.h" #endif #include "components/VideoVlcComponent.h" +#include "LocaleES.h" GridGameListView::GridGameListView(Window* window, FileData* root) : ISimpleGameListView(window, root), @@ -28,7 +29,10 @@ GridGameListView::GridGameListView(Window* window, FileData* root) : { const float padding = 0.01f; -// Create the correct type of video window + // Localización + LocaleES& loc = LocaleES::getInstance(); + + // Create the correct type of video window #ifdef _OMX_ if (Settings::getInstance()->getBool("VideoOmxPlayer")) mVideo = new VideoPlayerComponent(window, ""); @@ -38,49 +42,61 @@ GridGameListView::GridGameListView(Window* window, FileData* root) : mVideo = new VideoVlcComponent(window, getTitlePath()); #endif - mGrid.setPosition(mSize.x() * 0.1f, mSize.y() * 0.1f); + // GRID: columna izquierda (fallback sin tema) + mGrid.setPosition(mSize.x() * 0.05f, mSize.y() * 0.18f); + mGrid.setSize (mSize.x() * 0.55f, mSize.y() * 0.60f); mGrid.setDefaultZIndex(20); mGrid.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); }); addChild(&mGrid); populateList(root->getChildrenListToDisplay()); - // metadata labels + values - mLblRating.setText("Rating: "); + // metadata labels + values (localizados) + mLblRating.setText( loc.translate("RATING") + ": "); addChild(&mLblRating); addChild(&mRating); - mLblReleaseDate.setText("Released: "); + + mLblReleaseDate.setText( loc.translate("RELEASE DATE") + ": "); addChild(&mLblReleaseDate); addChild(&mReleaseDate); - mLblDeveloper.setText("Developer: "); + + mLblDeveloper.setText( loc.translate("DEVELOPER") + ": "); addChild(&mLblDeveloper); addChild(&mDeveloper); - mLblPublisher.setText("Publisher: "); + + mLblPublisher.setText( loc.translate("PUBLISHER") + ": "); addChild(&mLblPublisher); addChild(&mPublisher); - mLblGenre.setText("Genre: "); + + mLblGenre.setText( loc.translate("GENRE") + ": "); addChild(&mLblGenre); addChild(&mGenre); - mLblPlayers.setText("Players: "); + + mLblPlayers.setText( loc.translate("PLAYERS") + ": "); addChild(&mLblPlayers); addChild(&mPlayers); - mLblLastPlayed.setText("Last played: "); + + mLblLastPlayed.setText( loc.translate("LAST PLAYED") + ": "); addChild(&mLblLastPlayed); mLastPlayed.setDisplayRelative(true); addChild(&mLastPlayed); - mLblPlayCount.setText("Times played: "); + + mLblPlayCount.setText( loc.translate("PLAY COUNT") + ": "); addChild(&mLblPlayCount); addChild(&mPlayCount); - mName.setPosition(mSize.x(), mSize.y()); + // Título del juego: columna derecha, arriba + mName.setPosition(mSize.x() * 0.63f, mSize.y() * 0.18f); + mName.setSize(mSize.x() * 0.32f, 0); mName.setDefaultZIndex(40); mName.setColor(0xAAAAAAFF); mName.setFont(Font::get(FONT_SIZE_MEDIUM)); mName.setHorizontalAlignment(ALIGN_CENTER); addChild(&mName); - mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f); - mDescContainer.setSize(mSize.x() * (0.50f - 2*padding), mSize.y() - mDescContainer.getPosition().y()); + // Descripción: columna derecha, debajo de la metadata + mDescContainer.setPosition(mSize.x() * 0.63f, mSize.y() * 0.44f); + mDescContainer.setSize(mSize.x() * 0.32f, mSize.y() - mDescContainer.getPosition().y()); mDescContainer.setAutoScroll(true); mDescContainer.setDefaultZIndex(40); addChild(&mDescContainer); @@ -90,7 +106,6 @@ GridGameListView::GridGameListView(Window* window, FileData* root) : mDescContainer.addChild(&mDescription); // Image - // Default to off the screen mImage.setOrigin(0.5f, 0.5f); mImage.setPosition(2.0f, 2.0f); mImage.setMaxSize(mSize.x(), mSize.y()); @@ -99,7 +114,6 @@ GridGameListView::GridGameListView(Window* window, FileData* root) : addChild(&mImage); // Video - // Default to off the screen mVideo->setOrigin(0.5f, 0.5f); mVideo->setPosition(2.0f, 2.0f); mVideo->setSize(mSize.x(), mSize.y()); @@ -108,7 +122,6 @@ GridGameListView::GridGameListView(Window* window, FileData* root) : addChild(mVideo); // Marquee - // Default to off the screen mMarquee.setOrigin(0.5f, 0.5f); mMarquee.setPosition(2.0f, 2.0f); mMarquee.setMaxSize(mSize.x(), mSize.y()); @@ -212,7 +225,6 @@ void GridGameListView::onThemeChanged(const std::shared_ptr& theme) labels[i]->applyTheme(theme, getName(), lblElements[i], ALL); } - initMDValues(); std::vector values = getMDValues(); assert(values.size() == 8); @@ -245,9 +257,10 @@ void GridGameListView::initMDLabels() const unsigned int colCount = 2; const unsigned int rowCount = (int)(components.size() / 2); - Vector3f start(mSize.x() * 0.01f, mSize.y() * 0.625f, 0.0f); + // Tabla de metadata en la columna derecha + Vector3f start(mSize.x() * 0.63f, mSize.y() * 0.26f, 0.0f); - const float colSize = (mSize.x() * 0.48f) / colCount; + const float colSize = (mSize.x() * 0.35f) / colCount; const float rowPadding = 0.01f * mSize.y(); for(unsigned int i = 0; i < components.size(); i++) @@ -258,7 +271,6 @@ void GridGameListView::initMDLabels() { pos = start + Vector3f(colSize * (i / rowCount), 0, 0); }else{ - // work from the last component GuiComponent* lc = components[i-1]; pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0); } @@ -314,7 +326,6 @@ void GridGameListView::updateInfoPanel() mVideo->setImage(""); mVideoPlaying = false; - //mDescription.setText(""); fadingOut = true; }else{ if (!mVideo->setVideo(file->getVideoPath())) @@ -359,10 +370,6 @@ void GridGameListView::updateInfoPanel() for(auto it = comps.cbegin(); it != comps.cend(); it++) { GuiComponent* comp = *it; - // an animation is playing - // then animate if reverse != fadingOut - // an animation is not playing - // then animate if opacity != our target opacity if((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) || (!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) { @@ -377,8 +384,18 @@ void GridGameListView::updateInfoPanel() void GridGameListView::addPlaceholder() { - // empty grid - add a placeholder - FileData* placeholder = new FileData(PLACEHOLDER, "", this->mRoot->getSystem()->getSystemEnvData(), this->mRoot->getSystem()); + LocaleES& loc = LocaleES::getInstance(); + + std::string placeholderName = loc.translate("NO ENTRIES FOUND"); + if (placeholderName == "NO ENTRIES FOUND") + placeholderName = ""; + + FileData* placeholder = new FileData( + PLACEHOLDER, + placeholderName, + this->mRoot->getSystem()->getSystemEnvData(), + this->mRoot->getSystem()); + mGrid.add(placeholder->getName(), "", placeholder); } @@ -408,15 +425,14 @@ void GridGameListView::launch(FileData* game) } ViewController::get()->launch(game, target); - } void GridGameListView::remove(FileData *game, bool deleteFile, bool refreshView) { if (deleteFile) - Utils::FileSystem::removeFile(game->getPath()); // actually delete the file on the filesystem + Utils::FileSystem::removeFile(game->getPath()); FileData* parent = game->getParent(); - if (getCursor() == game) // Select next element in list, or prev if none + if (getCursor() == game) { std::vector siblings = parent->getChildrenListToDisplay(); auto gameIter = std::find(siblings.cbegin(), siblings.cend(), game); @@ -436,10 +452,10 @@ void GridGameListView::remove(FileData *game, bool deleteFile, bool refreshView) { addPlaceholder(); } - delete game; // remove before repopulating (removes from parent) + delete game; if(refreshView) - onFileChanged(parent, FILE_REMOVED); // update the view, with game removed + onFileChanged(parent, FILE_REMOVED); } std::vector GridGameListView::getMDLabels() @@ -472,17 +488,20 @@ std::vector GridGameListView::getMDValues() std::vector GridGameListView::getHelpPrompts() { + LocaleES& loc = LocaleES::getInstance(); + std::vector prompts; if(Settings::getInstance()->getBool("QuickSystemSelect")) - prompts.push_back(HelpPrompt("lr", "system")); - prompts.push_back(HelpPrompt("up/down/left/right", "choose")); - prompts.push_back(HelpPrompt("a", "launch")); - prompts.push_back(HelpPrompt("b", "back")); + prompts.push_back(HelpPrompt("lr", loc.translate("SYSTEM"))); + + prompts.push_back(HelpPrompt("up/down/left/right", loc.translate("CHOOSE"))); + prompts.push_back(HelpPrompt("a", loc.translate("START"))); + prompts.push_back(HelpPrompt("b", loc.translate("BACK"))); if(!UIModeController::getInstance()->isUIModeKid()) - prompts.push_back(HelpPrompt("select", "options")); + prompts.push_back(HelpPrompt("select", loc.translate("OPTIONS"))); if(mRoot->getSystem()->isGameSystem()) - prompts.push_back(HelpPrompt("x", "random")); + prompts.push_back(HelpPrompt("x", loc.translate("RANDOM"))); if(mRoot->getSystem()->isGameSystem() && !UIModeController::getInstance()->isUIModeKid()) { std::string prompt = CollectionSystemManager::get()->getEditingCollection(); diff --git a/es-app/src/views/gamelist/ISimpleGameListView.cpp b/es-app/src/views/gamelist/ISimpleGameListView.cpp index c81ff0502f..d2d4402100 100644 --- a/es-app/src/views/gamelist/ISimpleGameListView.cpp +++ b/es-app/src/views/gamelist/ISimpleGameListView.cpp @@ -7,11 +7,18 @@ #include "Settings.h" #include "Sound.h" #include "SystemData.h" +#include "LocaleES.h" // soporte de idioma +#include "NavigationSounds.h" // helper central para navigationsounds -ISimpleGameListView::ISimpleGameListView(Window* window, FileData* root) : IGameListView(window, root), +ISimpleGameListView::ISimpleGameListView(Window* window, FileData* root) + : IGameListView(window, root), mHeaderText(window), mHeaderImage(window), mBackground(window) { - mHeaderText.setText("Logo Text"); + // Localización del texto por defecto del encabezado + LocaleES& loc = LocaleES::getInstance(); + loc.loadFromSettings(); + + mHeaderText.setText(loc.translate("LOGO TEXT")); // ← Traducción desde .ini mHeaderText.setSize(mSize.x(), 0); mHeaderText.setPosition(0, 0); mHeaderText.setHorizontalAlignment(ALIGN_CENTER); @@ -51,11 +58,14 @@ void ISimpleGameListView::onThemeChanged(const std::shared_ptr& theme addChild(extra); } - if(mHeaderImage.hasImage()) + // Mostrar imagen si está disponible, sino texto + if (mHeaderImage.hasImage()) { removeChild(&mHeaderText); addChild(&mHeaderImage); - }else{ + } + else + { addChild(&mHeaderText); removeChild(&mHeaderImage); } @@ -63,8 +73,6 @@ void ISimpleGameListView::onThemeChanged(const std::shared_ptr& theme void ISimpleGameListView::onFileChanged(FileData* /*file*/, FileChangeType /*change*/) { - // we could be tricky here to be efficient; - // but this shouldn't happen very often so we'll just always repopulate FileData* cursor = getCursor(); if (!cursor->isPlaceHolder()) { populateList(cursor->getParent()->getChildrenListToDisplay()); @@ -79,36 +87,82 @@ void ISimpleGameListView::onFileChanged(FileData* /*file*/, FileChangeType /*cha bool ISimpleGameListView::input(InputConfig* config, Input input) { - if(input.value != 0) + if (input.value != 0) { + // LANZAR JUEGO / ENTRAR A CARPETA if(config->isMappedTo("a", input)) { FileData* cursor = getCursor(); if(cursor->getType() == GAME) { - Sound::getFromTheme(getTheme(), getName(), "launch")->play(); + // SONIDO DE LAUNCH (compatible con navigationsounds → "launch") + std::shared_ptr launchSnd; + + SystemData* sys = mRoot ? mRoot->getSystem() : nullptr; + if (sys != nullptr) + { + const std::shared_ptr& theme = sys->getTheme(); + if (theme) + { + // 1) esquema nuevo + launchSnd = NavigationSounds::getFromTheme(theme, "launch"); + } + } + + // 2) fallback clásico (por si acaso) + if (!launchSnd) + launchSnd = Sound::getFromTheme(getTheme(), getName(), "launch"); + + if (launchSnd) + launchSnd->play(); + launch(cursor); - }else{ - // it's a folder + } + else + { if(cursor->getChildren().size() > 0) { mCursorStack.push(cursor); populateList(cursor->getChildrenListToDisplay()); - FileData* cursor = getCursor(); - setCursor(cursor); + setCursor(getCursor()); } } return true; - }else if(config->isMappedTo("b", input)) + } + // VOLVER (dentro de carpetas o a SystemView) + else if(config->isMappedTo("b", input)) { + // 🎵 SONIDO DE BACK SIEMPRE (carpeta o salida a SystemView) + std::shared_ptr backSnd; + SystemData* sys = mRoot ? mRoot->getSystem() : nullptr; + if (sys != nullptr) + { + const std::shared_ptr& theme = sys->getTheme(); + if (theme) + { + // 1) esquema nuevo: navigationsounds → "back" + backSnd = NavigationSounds::getFromTheme(theme, "back"); + } + } + // 2) fallback clásico (por vista basic/detailed/video) + if (!backSnd) + backSnd = Sound::getFromTheme(getTheme(), getName(), "back"); + + if (backSnd) + backSnd->play(); + + // Lógica de navegación if(mCursorStack.size()) { + // Volver una carpeta atrás en la misma lista populateList(mCursorStack.top()->getParent()->getChildrenListToDisplay()); setCursor(mCursorStack.top()); mCursorStack.pop(); - Sound::getFromTheme(getTheme(), getName(), "back")->play(); - }else{ + } + else + { + // Salir a SystemView onFocusLost(); SystemData* systemToView = getCursor()->getSystem(); if (systemToView->isCollection()) @@ -119,27 +173,61 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) } return true; - }else if(config->isMappedLike(getQuickSystemSelectRightButton(), input)) + } + // QUICK SYSTEM SELECT → LB/RB (sonido quicksysselect) + else if(config->isMappedLike(getQuickSystemSelectRightButton(), input)) { if(Settings::getInstance()->getBool("QuickSystemSelect")) { + // sonido quicksysselect (Batocera-like) + SystemData* sys = mRoot ? mRoot->getSystem() : nullptr; + if (sys != nullptr) + { + const std::shared_ptr& theme = sys->getTheme(); + if (theme) + { + auto snd = NavigationSounds::getFromTheme(theme, "quicksysselect"); + // fallback opcional al browse + if (!snd) + snd = NavigationSounds::getFromTheme(theme, "systembrowse"); + if (snd) + snd->play(); + } + } + onFocusLost(); ViewController::get()->goToNextGameList(); return true; } - }else if(config->isMappedLike(getQuickSystemSelectLeftButton(), input)) + } + else if(config->isMappedLike(getQuickSystemSelectLeftButton(), input)) { if(Settings::getInstance()->getBool("QuickSystemSelect")) { + SystemData* sys = mRoot ? mRoot->getSystem() : nullptr; + if (sys != nullptr) + { + const std::shared_ptr& theme = sys->getTheme(); + if (theme) + { + auto snd = NavigationSounds::getFromTheme(theme, "quicksysselect"); + if (!snd) + snd = NavigationSounds::getFromTheme(theme, "systembrowse"); + if (snd) + snd->play(); + } + } + onFocusLost(); ViewController::get()->goToPrevGameList(); return true; } - }else if (config->isMappedTo("x", input)) + } + // RANDOM + else if (config->isMappedTo("x", input)) { if (mRoot->getSystem()->isGameSystem()) { - // go to random system game FileData* randomGame = getCursor()->getSystem()->getRandomGame(); if (randomGame) { @@ -147,26 +235,44 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) } return true; } - }else if (config->isMappedTo("y", input) && !UIModeController::getInstance()->isUIModeKid()) + } + // FAVORITO / COLECCIÓN (sonido "favorite") + else if (config->isMappedTo("y", input) && !UIModeController::getInstance()->isUIModeKid()) { if(mRoot->getSystem()->isGameSystem()) { if (CollectionSystemManager::get()->toggleGameInCollection(getCursor())) { + // sonido favorite al togglear colección (normalmente Favoritos) + SystemData* sys = mRoot ? mRoot->getSystem() : nullptr; + if (sys != nullptr) + { + const std::shared_ptr& theme = sys->getTheme(); + if (theme) + { + auto favSnd = NavigationSounds::getFromTheme(theme, "favorite"); + // fallback suave a "select" si el tema no tiene favorite + if (!favSnd) + favSnd = NavigationSounds::getFromTheme(theme, "select"); + if (favSnd) + favSnd->play(); + } + } return true; } } } } + // Evento game-select para scripts FileData* cursor = getCursor(); SystemData* system = this->mRoot->getSystem(); - if (system != NULL) { - Scripting::fireEvent("game-select", system->getName(), cursor->getPath(), cursor->getName(), "input"); - } + if (system != NULL) { + Scripting::fireEvent("game-select", system->getName(), cursor->getPath(), cursor->getName(), "input"); + } else { - Scripting::fireEvent("game-select", "NULL", "NULL", "NULL", "input"); + Scripting::fireEvent("game-select", "NULL", "NULL", "NULL", "input"); } return IGameListView::input(config, input); -} \ No newline at end of file +} diff --git a/es-app/src/views/gamelist/VideoGameListView.cpp b/es-app/src/views/gamelist/VideoGameListView.cpp index e26715c774..62614461b6 100644 --- a/es-app/src/views/gamelist/VideoGameListView.cpp +++ b/es-app/src/views/gamelist/VideoGameListView.cpp @@ -10,6 +10,12 @@ #ifdef _OMX_ #include "Settings.h" #endif +#include "LocaleES.h" // ← NUEVO: soporte de idioma + +// --- NUEVO: soporte de sonido launch --- +#include "Sound.h" +#include "SystemData.h" +// --- FIN NUEVO --- VideoGameListView::VideoGameListView(Window* window, FileData* root) : BasicGameListView(window, root), @@ -29,7 +35,7 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) : { const float padding = 0.01f; - // Create the correct type of video window + // Crear el tipo correcto de componente de video #ifdef _OMX_ Utils::FileSystem::removeFile(getTitlePath()); if (Settings::getInstance()->getBool("VideoOmxPlayer")) @@ -45,8 +51,8 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) : mList.setAlignment(TextListComponent::ALIGN_LEFT); mList.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); }); - // Image - // Default to off the screen + // Imagen + // Por defecto fuera de la pantalla mImage.setOrigin(0.5f, 0.5f); mImage.setPosition(2.0f, 2.0f); mImage.setMaxSize(mSize.x(), mSize.y()); @@ -57,12 +63,12 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) : // Video mVideo->setOrigin(0.5f, 0.5f); mVideo->setPosition(mSize.x() * 0.25f, mSize.y() * 0.4f); - mVideo->setSize(mSize.x() * (0.5f - 2*padding), mSize.y() * 0.4f); + mVideo->setSize(mSize.x() * (0.5f - 2 * padding), mSize.y() * 0.4f); mVideo->setDefaultZIndex(30); addChild(mVideo); // Thumbnail - // Default to off the screen + // Por defecto fuera de la pantalla mThumbnail.setOrigin(0.5f, 0.5f); mThumbnail.setPosition(2.0f, 2.0f); mThumbnail.setMaxSize(mSize.x(), mSize.y()); @@ -71,40 +77,51 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) : addChild(&mThumbnail); // Marquee - // Default to off the screen + // Por defecto fuera de la pantalla mMarquee.setOrigin(0.5f, 0.5f); mMarquee.setPosition(2.0f, 2.0f); mMarquee.setMaxSize(mSize.x(), mSize.y()); mMarquee.setDefaultZIndex(35); - mImage.setVisible(false); + mMarquee.setVisible(false); // ← FIX: antes estaba mImage.setVisible(false); addChild(&mMarquee); - // metadata labels + values - mLblRating.setText("Rating: "); + // --- LOCALIZACIÓN DE ETIQUETAS --- + LocaleES& loc = LocaleES::getInstance(); + loc.loadFromSettings(); + + mLblRating.setText(loc.translate("RATING") + ": "); addChild(&mLblRating); addChild(&mRating); - mLblReleaseDate.setText("Released: "); + + mLblReleaseDate.setText(loc.translate("RELEASE DATE") + ": "); addChild(&mLblReleaseDate); addChild(&mReleaseDate); - mLblDeveloper.setText("Developer: "); + + mLblDeveloper.setText(loc.translate("DEVELOPER") + ": "); addChild(&mLblDeveloper); addChild(&mDeveloper); - mLblPublisher.setText("Publisher: "); + + mLblPublisher.setText(loc.translate("PUBLISHER") + ": "); addChild(&mLblPublisher); addChild(&mPublisher); - mLblGenre.setText("Genre: "); + + mLblGenre.setText(loc.translate("GENRE") + ": "); addChild(&mLblGenre); addChild(&mGenre); - mLblPlayers.setText("Players: "); + + mLblPlayers.setText(loc.translate("PLAYERS") + ": "); addChild(&mLblPlayers); addChild(&mPlayers); - mLblLastPlayed.setText("Last played: "); + + mLblLastPlayed.setText(loc.translate("LAST PLAYED") + ": "); addChild(&mLblLastPlayed); mLastPlayed.setDisplayRelative(true); addChild(&mLastPlayed); - mLblPlayCount.setText("Times played: "); + + mLblPlayCount.setText(loc.translate("PLAY COUNT") + ": "); addChild(&mLblPlayCount); addChild(&mPlayCount); + // --- FIN LOCALIZACIÓN DE ETIQUETAS --- mName.setPosition(mSize.x(), mSize.y()); mName.setDefaultZIndex(40); @@ -114,7 +131,7 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) : addChild(&mName); mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f); - mDescContainer.setSize(mSize.x() * (0.50f - 2*padding), mSize.y() - mDescContainer.getPosition().y()); + mDescContainer.setSize(mSize.x() * (0.50f - 2 * padding), mSize.y() - mDescContainer.getPosition().y()); mDescContainer.setAutoScroll(true); mDescContainer.setDefaultZIndex(40); addChild(&mDescContainer); @@ -151,12 +168,11 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr& theme) "md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount" }; - for(unsigned int i = 0; i < labels.size(); i++) + for (unsigned int i = 0; i < labels.size(); i++) { labels[i]->applyTheme(theme, getName(), lblElements[i], ALL); } - initMDValues(); std::vector values = getMDValues(); assert(values.size() == 8); @@ -165,7 +181,7 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr& theme) "md_genre", "md_players", "md_lastplayed", "md_playcount" }; - for(unsigned int i = 0; i < values.size(); i++) + for (unsigned int i = 0; i < values.size(); i++) { values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT); } @@ -189,16 +205,17 @@ void VideoGameListView::initMDLabels() const float colSize = (mSize.x() * 0.48f) / colCount; const float rowPadding = 0.01f * mSize.y(); - for(unsigned int i = 0; i < components.size(); i++) + for (unsigned int i = 0; i < components.size(); i++) { const unsigned int row = i % rowCount; Vector3f pos(0.0f, 0.0f, 0.0f); - if(row == 0) + if (row == 0) { pos = start + Vector3f(colSize * (i / rowCount), 0, 0); - }else{ - // work from the last component - GuiComponent* lc = components[i-1]; + } + else { + // trabajar desde el último componente + GuiComponent* lc = components[i - 1]; pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0); } @@ -226,7 +243,7 @@ void VideoGameListView::initMDValues() float bottom = 0.0f; const float colSize = (mSize.x() * 0.48f) / 2; - for(unsigned int i = 0; i < labels.size(); i++) + for (unsigned int i = 0; i < labels.size(); i++) { const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2; values[i]->setPosition(labels[i]->getPosition() + Vector3f(labels[i]->getSize().x(), heightDiff, 0)); @@ -234,7 +251,7 @@ void VideoGameListView::initMDValues() values[i]->setDefaultZIndex(40); float testBot = values[i]->getPosition().y() + values[i]->getSize().y(); - if(testBot > bottom) + if (testBot > bottom) bottom = testBot; } @@ -242,14 +259,12 @@ void VideoGameListView::initMDValues() mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() - mDescContainer.getPosition().y()); } - - void VideoGameListView::updateInfoPanel() { FileData* file = (mList.size() == 0 || mList.isScrolling()) ? NULL : mList.getSelected(); bool fadingOut; - if(file == NULL) + if (file == NULL) { mVideo->setVideo(""); mVideo->setImage(""); @@ -258,7 +273,8 @@ void VideoGameListView::updateInfoPanel() //mDescription.setText(""); fadingOut = true; - }else{ + } + else { if (!mVideo->setVideo(file->getVideoPath())) { mVideo->setDefaultVideo(); @@ -281,7 +297,7 @@ void VideoGameListView::updateInfoPanel() mPlayers.setValue(file->metadata.get("players")); mName.setValue(file->metadata.get("name")); - if(file->getType() == GAME) + if (file->getType() == GAME) { mLastPlayed.setValue(file->metadata.get("lastplayed")); mPlayCount.setValue(file->metadata.get("playcount")); @@ -300,19 +316,19 @@ void VideoGameListView::updateInfoPanel() std::vector labels = getMDLabels(); comps.insert(comps.cend(), labels.cbegin(), labels.cend()); - for(auto it = comps.cbegin(); it != comps.cend(); it++) + for (auto it = comps.cbegin(); it != comps.cend(); it++) { GuiComponent* comp = *it; - // an animation is playing - // then animate if reverse != fadingOut - // an animation is not playing - // then animate if opacity != our target opacity - if((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) || + // si hay una animación corriendo: + // animar si reverse != fadingOut + // si no hay animación: + // animar si la opacidad actual no coincide con el objetivo + if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) || (!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) { auto func = [comp](float t) { - comp->setOpacity((unsigned char)(Math::lerp(0.0f, 1.0f, t)*255)); + comp->setOpacity((unsigned char)(Math::lerp(0.0f, 1.0f, t) * 255)); }; comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut); } @@ -321,37 +337,65 @@ void VideoGameListView::updateInfoPanel() void VideoGameListView::launch(FileData* game) { - float screenWidth = (float) Renderer::getScreenWidth(); - float screenHeight = (float) Renderer::getScreenHeight(); + // --- NUEVO: sonido de "launch" compatible con Batocera --- + if (game != nullptr) + { + SystemData* sys = game->getSystem(); + if (sys != nullptr) + { + const std::shared_ptr& theme = sys->getTheme(); + if (theme) + { + // Prioridad: + const ThemeData::ThemeElement* launchElem = + theme->getElement("all", "launch", "sound"); + + // Fallback opcional: por si alguien lo pone en la vista "system" + if (!launchElem) + launchElem = theme->getElement("system", "launch", "sound"); + + if (launchElem && launchElem->has("path")) + { + std::string path = launchElem->get("path"); + if (!path.empty()) + Sound::get(path)->play(); + } + } + } + } + // --- FIN NUEVO --- + + float screenWidth = (float)Renderer::getScreenWidth(); + float screenHeight = (float)Renderer::getScreenHeight(); Vector3f target(screenWidth / 2.0f, screenHeight / 2.0f, 0); - if(mMarquee.hasImage() && + if (mMarquee.hasImage() && (mMarquee.getPosition().x() < screenWidth && mMarquee.getPosition().x() > 0.0f && - mMarquee.getPosition().y() < screenHeight && mMarquee.getPosition().y() > 0.0f)) + mMarquee.getPosition().y() < screenHeight && mMarquee.getPosition().y() > 0.0f)) { target = Vector3f(mMarquee.getCenter().x(), mMarquee.getCenter().y(), 0); } - else if(mThumbnail.hasImage() && + else if (mThumbnail.hasImage() && (mThumbnail.getPosition().x() < screenWidth && mThumbnail.getPosition().x() > 2.0f && - mThumbnail.getPosition().y() < screenHeight && mThumbnail.getPosition().y() > 2.0f)) + mThumbnail.getPosition().y() < screenHeight && mThumbnail.getPosition().y() > 2.0f)) { target = Vector3f(mThumbnail.getCenter().x(), mThumbnail.getCenter().y(), 0); } - else if(mImage.hasImage() && + else if (mImage.hasImage() && (mImage.getPosition().x() < screenWidth && mImage.getPosition().x() > 2.0f && - mImage.getPosition().y() < screenHeight && mImage.getPosition().y() > 2.0f)) + mImage.getPosition().y() < screenHeight && mImage.getPosition().y() > 2.0f)) { target = Vector3f(mImage.getCenter().x(), mImage.getCenter().y(), 0); } - else if(mHeaderImage.hasImage() && + else if (mHeaderImage.hasImage() && (mHeaderImage.getPosition().x() < screenWidth && mHeaderImage.getPosition().x() > 0.0f && - mHeaderImage.getPosition().y() < screenHeight && mHeaderImage.getPosition().y() > 0.0f)) + mHeaderImage.getPosition().y() < screenHeight && mHeaderImage.getPosition().y() > 0.0f)) { target = Vector3f(mHeaderImage.getCenter().x(), mHeaderImage.getCenter().y(), 0); } - else if(mVideo->getPosition().x() < screenWidth && mVideo->getPosition().x() > 0.0f && - mVideo->getPosition().y() < screenHeight && mVideo->getPosition().y() > 0.0f) + else if (mVideo->getPosition().x() < screenWidth && mVideo->getPosition().x() > 0.0f && + mVideo->getPosition().y() < screenHeight && mVideo->getPosition().y() > 0.0f) { target = Vector3f(mVideo->getCenter().x(), mVideo->getCenter().y(), 0); } diff --git a/es-core/CMakeLists.txt b/es-core/CMakeLists.txt index c22459fd7c..73d989fa29 100644 --- a/es-core/CMakeLists.txt +++ b/es-core/CMakeLists.txt @@ -104,26 +104,27 @@ set(CORE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/animations/AnimationController.cpp # GuiComponents - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/AnimatedImageComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/BusyComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ButtonComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentGrid.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentList.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/DateTimeComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/DateTimeEditComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/HelpComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GridTileComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ImageComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/MenuComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/NinePatchComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ScrollableContainer.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SliderComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/SwitchComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextEditComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoPlayerComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoVlcComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/AnimatedImageComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/BusyComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/ButtonComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentGrid.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentList.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/DateTimeComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/DateTimeEditComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/HelpComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/GridTileComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/ImageComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/MenuComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/NinePatchComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/ScrollableContainer.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/SliderComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/SwitchComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/MarqueeTextComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextEditComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoPlayerComponent.cpp +${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoVlcComponent.cpp # Guis ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiDetectDevice.cpp @@ -162,5 +163,8 @@ set(CORE_SOURCES ) include_directories(${COMMON_INCLUDE_DIRS}) +# ⬇⬇⬇ NUEVA LÍNEA - para poder usar LocaleES.h desde el core +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../es-app/src) + add_library(es-core STATIC ${CORE_SOURCES} ${CORE_HEADERS}) target_link_libraries(es-core ${COMMON_LIBRARIES}) diff --git a/es-core/src/InputManager.cpp b/es-core/src/InputManager.cpp index 67a8901c47..5f660fb4d4 100644 --- a/es-core/src/InputManager.cpp +++ b/es-core/src/InputManager.cpp @@ -6,6 +6,8 @@ #include "platform.h" #include "Scripting.h" #include "Window.h" +#include "guis/GuiInfoPopup.h" +#include "utils/StringUtil.h" #include #include #include @@ -275,9 +277,26 @@ bool InputManager::parseEvent(const SDL_Event& ev, Window* window) case SDL_JOYDEVICEADDED: addJoystickByDeviceIndex(ev.jdevice.which); // ev.jdevice.which is a device index + + // ★ Notificación (opcional): mando conectado + if (window != nullptr) + { + const char* joyNameC = SDL_JoystickNameForIndex(ev.jdevice.which); + std::string joyName = joyNameC ? std::string(joyNameC) : std::string("Controller"); + window->setInfoPopup(new GuiInfoPopup(window, std::string("★ Connected: ") + joyName, 4000)); + } return true; case SDL_JOYDEVICEREMOVED: + // ★ Notificación (opcional): mando desconectado + if (window != nullptr) + { + SDL_Joystick* joy = SDL_JoystickFromInstanceID(ev.jdevice.which); + const char* joyNameC = joy ? SDL_JoystickName(joy) : nullptr; + std::string joyName = joyNameC ? std::string(joyNameC) : std::string("Controller"); + window->setInfoPopup(new GuiInfoPopup(window, std::string("★ Disconnected: ") + joyName, 4000)); + } + removeJoystickByJoystickID(ev.jdevice.which); // ev.jdevice.which is an SDL_JoystickID (instance ID) return false; } diff --git a/es-core/src/LocaleESHook.h b/es-core/src/LocaleESHook.h new file mode 100644 index 0000000000..d1abac2fb9 --- /dev/null +++ b/es-core/src/LocaleESHook.h @@ -0,0 +1,6 @@ +#pragma once +#include + +// Función de traducción genérica usada desde es-core. +// La implementación real está en es-app (LocaleESHook.cpp). +std::string es_translate(const std::string& key); diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index aeb1057e33..4c984f60fd 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -1,3 +1,5 @@ +// ========== Settings.cpp (COMPLETO, VERSION ES-X CON EnableBGM) ========== + #include "Settings.h" #include "utils/FileSystemUtil.h" @@ -10,8 +12,7 @@ Settings* Settings::sInstance = NULL; -// these values are NOT saved to es_settings.xml -// since they're set through command-line arguments, and not the in-program settings menu +// Valores que NO se guardan en es_settings.cfg std::vector settings_dont_save { "Debug", "DebugGrid", @@ -47,7 +48,6 @@ Settings* Settings::getInstance() { if(sInstance == NULL) sInstance = new Settings(); - return sInstance; } @@ -79,6 +79,9 @@ void Settings::setDefaults() mBoolMap["QuickSystemSelect"] = true; mBoolMap["MoveCarousel"] = true; + // Modo oscuro ES-X + mBoolMap["MenuDark"] = false; + mBoolMap["ThreadedLoading"] = false; mBoolMap["Debug"] = false; @@ -86,16 +89,20 @@ void Settings::setDefaults() mBoolMap["DebugText"] = false; mBoolMap["DebugImage"] = false; + // 🆕 NUEVO: Música de fondo + mBoolMap["EnableBGM"] = true; + mIntMap["ScreenSaverTime"] = 5 * Settings::ONE_MINUTE_IN_MS; mIntMap["SystemSleepTime"] = 0 * Settings::ONE_MINUTE_IN_MS; mBoolMap["SystemSleepTimeHintDisplayed"] = false; mIntMap["ScraperResizeWidth"] = 400; mIntMap["ScraperResizeHeight"] = 0; - #ifdef _RPI_ - mIntMap["MaxVRAM"] = 80; - #else - mIntMap["MaxVRAM"] = 100; - #endif + +#ifdef _RPI_ + mIntMap["MaxVRAM"] = 80; +#else + mIntMap["MaxVRAM"] = 100; +#endif mStringMap["TransitionStyle"] = "fade"; mStringMap["ThemeSet"] = ""; @@ -118,35 +125,30 @@ void Settings::setDefaults() mStringMap["SlideshowScreenSaverVideoFilter"] = ".mp4,.avi"; mBoolMap["SlideshowScreenSaverRecurse"] = false; - // This setting only applies to raspberry pi but set it for all platforms so - // we don't get a warning if we encounter it on a different platform mBoolMap["VideoOmxPlayer"] = false; - #ifdef _OMX_ - // we're defaulting to OMX Player for full screen video on the Pi - mBoolMap["ScreenSaverOmxPlayer"] = true; - // use OMX Player defaults - mStringMap["SubtitleFont"] = "/usr/share/fonts/truetype/freefont/FreeSans.ttf"; - mStringMap["SubtitleItalicFont"] = "/usr/share/fonts/truetype/freefont/FreeSansOblique.ttf"; - mIntMap["SubtitleSize"] = 55; - mStringMap["SubtitleAlignment"] = "left"; - #else - mBoolMap["ScreenSaverOmxPlayer"] = false; - #endif +#ifdef _OMX_ + mBoolMap["ScreenSaverOmxPlayer"] = true; + mStringMap["SubtitleFont"] = "/usr/share/fonts/truetype/freefont/FreeSans.ttf"; + mStringMap["SubtitleItalicFont"] = "/usr/share/fonts/truetype/freefont/FreeSansOblique.ttf"; + mIntMap["SubtitleSize"] = 55; + mStringMap["SubtitleAlignment"] = "left"; +#else + mBoolMap["ScreenSaverOmxPlayer"] = false; +#endif mIntMap["ScreenSaverSwapVideoTimeout"] = 30000; mBoolMap["VideoAudio"] = true; mBoolMap["ScreenSaverVideoMute"] = false; mStringMap["VlcScreenSaverResolution"] = "original"; - // Audio out device for Video playback using OMX player. mStringMap["OMXAudioDev"] = "both"; - mIntMap["RandomCollectionMaxGames"] = 0; // 0 == no limit - std::map m1; - mMapIntMap["RandomCollectionSystemsAuto"] = m1; - std::map m2; - mMapIntMap["RandomCollectionSystemsCustom"] = m2; - std::map m3; - mMapIntMap["RandomCollectionSystems"] = m3; + + mIntMap["RandomCollectionMaxGames"] = 0; + std::map empty; + mMapIntMap["RandomCollectionSystemsAuto"] = empty; + mMapIntMap["RandomCollectionSystemsCustom"] = empty; + mMapIntMap["RandomCollectionSystems"] = empty; + mStringMap["RandomCollectionExclusionCollection"] = ""; mStringMap["CollectionSystemsAuto"] = ""; mStringMap["CollectionSystemsCustom"] = ""; @@ -158,12 +160,11 @@ void Settings::setDefaults() mBoolMap["LocalArt"] = false; - // Audio out device for volume control - #ifdef _RPI_ - mStringMap["AudioDevice"] = "HDMI"; - #else - mStringMap["AudioDevice"] = "Master"; - #endif +#ifdef _RPI_ + mStringMap["AudioDevice"] = "HDMI"; +#else + mStringMap["AudioDevice"] = "Master"; +#endif mStringMap["AudioCard"] = "default"; mStringMap["UIMode"] = "Full"; @@ -172,21 +173,22 @@ void Settings::setDefaults() mBoolMap["ForceKid"] = false; mBoolMap["ForceDisableFilters"] = false; - mIntMap["WindowWidth"] = 0; - mIntMap["WindowHeight"] = 0; - mIntMap["ScreenWidth"] = 0; - mIntMap["ScreenHeight"] = 0; + mIntMap["WindowWidth"] = 0; + mIntMap["WindowHeight"] = 0; + mIntMap["ScreenWidth"] = 0; + mIntMap["ScreenHeight"] = 0; mIntMap["ScreenOffsetX"] = 0; mIntMap["ScreenOffsetY"] = 0; - mIntMap["ScreenRotate"] = 0; + mIntMap["ScreenRotate"] = 0; mIntMap["MonitorID"] = 0; mBoolMap["UseFullscreenPaging"] = false; - mBoolMap["IgnoreLeadingArticles"] = false; - //No spaces! Order is important! - //"The A Squad" given [a,an,the] will sort as "A Squad", but given [the,a,an] will sort as "Squad" + mStringMap["LeadingArticles"] = "a,an,the"; + + // idioma por defecto ES-X + mStringMap["Language"] = "en"; } template @@ -194,7 +196,6 @@ void saveMap(pugi::xml_document& doc, std::map& map, const char* type) { for(auto iter = map.cbegin(); iter != map.cend(); iter++) { - // key is on the "don't save" list, so don't save it if(std::find(settings_dont_save.cbegin(), settings_dont_save.cend(), iter->first) != settings_dont_save.cend()) continue; @@ -211,11 +212,10 @@ void Settings::saveFile() pugi::xml_document doc; - saveMap(doc, mBoolMap, "bool"); - saveMap(doc, mIntMap, "int"); - saveMap(doc, mFloatMap, "float"); + saveMap(doc, mBoolMap, "bool"); + saveMap(doc, mIntMap, "int"); + saveMap(doc, mFloatMap, "float"); - //saveMap(doc, mStringMap, "string"); for(auto iter = mStringMap.cbegin(); iter != mStringMap.cend(); iter++) { pugi::xml_node node = doc.append_child("string"); @@ -229,7 +229,7 @@ void Settings::saveFile() node.append_attribute("name").set_value(m.first.c_str()); std::string datatype = "int"; node.append_attribute("type").set_value(datatype.c_str()); - for(auto &intMap : m.second) // intMap is a map + for(auto &intMap : m.second) { pugi::xml_node entry = node.append_child(datatype.c_str()); entry.append_attribute("name").set_value(intMap.first.c_str()); @@ -271,46 +271,44 @@ void Settings::loadFile() { std::string mapName = node.attribute("name").as_string(); std::string mapType = node.attribute("type").as_string(); - if (mapType == "int") { - // only supporting int value maps currently - std::map _map; + if(mapType == "int") + { + std::map _map; for(pugi::xml_node entry : node.children(mapType.c_str())) - { _map[entry.attribute("name").as_string()] = entry.attribute("value").as_int(); - } setMap(mapName, _map); - } else { - LOG(LogWarning) << "Map: '" << mapName << "'. Unsupported data type '"<< mapType <<"'. Value ignored!"; + } + else + { + LOG(LogWarning) << "Map '" << mapName << "' unsupported type '" << mapType << "'"; } } processBackwardCompatibility(); } - -void Settings::setMap(const std::string& key, const std::map& map) +void Settings::setMap(const std::string& key, const std::map& map) { mMapIntMap[key] = map; } -const std::map Settings::getMap(const std::string& key) +const std::map Settings::getMap(const std::string& key) { if(mMapIntMap.find(key) == mMapIntMap.cend()) { LOG(LogError) << "Tried to use undefined setting " << key << "!"; - std::map empty; + std::map empty; return empty; - } return mMapIntMap[key]; } - template void Settings::renameSetting(Map& map, std::string&& oldName, std::string&& newName) { - typename Map::const_iterator it = map.find(oldName); - if (it != map.end()) { + auto it = map.find(oldName); + if(it != map.end()) + { map[newName] = it->second; map.erase(it); } @@ -318,24 +316,30 @@ void Settings::renameSetting(Map& map, std::string&& oldName, std::string&& newN void Settings::processBackwardCompatibility() { - { // SaveGamelistsOnExit -> SaveGamelistsMode - std::map::const_iterator it = mBoolMap.find("SaveGamelistsOnExit"); - if (it != mBoolMap.end()) { + { + auto it = mBoolMap.find("SaveGamelistsOnExit"); + if(it != mBoolMap.end()) + { mStringMap["SaveGamelistsMode"] = it->second ? "on exit" : "never"; mBoolMap.erase(it); } } - { // ScreenSaverSlideShow Image -> Media - renameSetting>(mIntMap, std::string("ScreenSaverSwapImageTimeout"), std::string("ScreenSaverSwapMediaTimeout")); - renameSetting>(mBoolMap, std::string("SlideshowScreenSaverCustomImageSource"), std::string("SlideshowScreenSaverCustomMediaSource")); - renameSetting>(mStringMap, std::string("SlideshowScreenSaverImageDir"), std::string("SlideshowScreenSaverMediaDir")); - } -} + renameSetting>(mIntMap, + std::string("ScreenSaverSwapImageTimeout"), + std::string("ScreenSaverSwapMediaTimeout")); + renameSetting>(mBoolMap, + std::string("SlideshowScreenSaverCustomImageSource"), + std::string("SlideshowScreenSaverCustomMediaSource")); -//Print a warning message if the setting we're trying to get doesn't already exist in the map, then return the value in the map. -#define SETTINGS_GETSET(type, mapName, getMethodName, setMethodName) type Settings::getMethodName(const std::string& name) \ + renameSetting>(mStringMap, + std::string("SlideshowScreenSaverImageDir"), + std::string("SlideshowScreenSaverMediaDir")); +} + +#define SETTINGS_GETSET(type, mapName, getMethodName, setMethodName) \ +type Settings::getMethodName(const std::string& name) \ { \ if(mapName.find(name) == mapName.cend()) \ { \ @@ -352,3 +356,4 @@ SETTINGS_GETSET(bool, mBoolMap, getBool, setBool); SETTINGS_GETSET(int, mIntMap, getInt, setInt); SETTINGS_GETSET(float, mFloatMap, getFloat, setFloat); SETTINGS_GETSET(const std::string&, mStringMap, getString, setString); + diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index e7696cb939..9f3bbcecc2 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -2,6 +2,7 @@ #include "components/ImageComponent.h" #include "components/TextComponent.h" +#include "resources/ResourceManager.h" #include "utils/FileSystemUtil.h" #include "utils/StringUtil.h" #include "Log.h" @@ -9,10 +10,16 @@ #include "Settings.h" #include #include +#include +#include -std::vector ThemeData::sSupportedViews { { "system" }, { "basic" }, { "detailed" }, { "grid" }, { "video" } }; -std::vector ThemeData::sSupportedFeatures { { "video" }, { "carousel" }, { "z-index" }, { "visible" } }; +// Vistas soportadas +std::vector ThemeData::sSupportedViews { { "system" }, { "basic" }, { "detailed" }, { "grid" }, { "video" }, { "all" } }; +// Features soportados +std::vector ThemeData::sSupportedFeatures { { "video" }, { "carousel" }, { "z-index" }, { "visible" }, { "navigationsounds" } }; + +// Mapa de elementos y propiedades std::map> ThemeData::sElementMap { { "image", { { "pos", RESOLUTION_PAIR }, @@ -69,7 +76,13 @@ std::map> The { "lineSpacing", FLOAT }, { "value", STRING }, { "visible", BOOLEAN }, - { "zIndex", FLOAT } } }, + { "zIndex", FLOAT }, + + // NUEVO: borde de texto + { "textStrokeColor", COLOR }, + { "textStrokeSize", FLOAT } + } }, + { "textlist", { { "pos", RESOLUTION_PAIR }, { "size", RESOLUTION_PAIR }, @@ -167,21 +180,23 @@ std::map> The { "logoRotation", FLOAT }, { "logoRotationOrigin", NORMALIZED_PAIR }, { "logoSize", NORMALIZED_PAIR }, - { "logoAlignment", STRING }, - { "maxLogoCount", FLOAT }, - { "zIndex", FLOAT }, - // Additional carousel options - { "minLogoOpacity", FLOAT }, // 0.0 – 1.0 - { "scaledLogoSpacing", FLOAT }, // spacing multiplier when scaling logos - { "scrollSound", PATH } // optional sound to play while scrolling - } } + { "logoAlignment", STRING }, + { "maxLogoCount", FLOAT }, + { "zIndex", FLOAT }, + // NUEVO: propiedades extra del carrusel + { "minLogoOpacity", FLOAT }, // 0.0 – 1.0 + { "scaledLogoSpacing", FLOAT }, // factor de separación + { "scrollSound", PATH } // sonido opcional (por ahora solo parseado) + } } }; #define MINIMUM_THEME_FORMAT_VERSION 3 #define CURRENT_THEME_FORMAT_VERSION 6 -// helper -unsigned int getHexColor(const char* str) +// ------------------------------------------------------ +// helper: conversión de color "RRGGBB" o "RRGGBBAA" a int +// ------------------------------------------------------ +static unsigned int getHexColor(const char* str) { ThemeException error; if(!str) @@ -202,6 +217,149 @@ unsigned int getHexColor(const char* str) return val; } +// ───────────────────────────────────────────── +// NUEVO: carga de variables externas desde theme.ini +// con soporte de secciones y layouts +// ───────────────────────────────────────────── +namespace +{ + void loadExternalThemeVariables(const std::string& themeXmlPath, + std::map& outVars) + { + // Layout activo elegido por el usuario (ej: "smd", "layout_smd", etc.) + const std::string activeLayoutSetting = Settings::getInstance()->getString("ThemeLayout"); + const bool hasActiveLayout = !activeLayoutSetting.empty(); + const std::string activeLower = Utils::String::toLower(activeLayoutSetting); + + // Carpeta del theme.xml y sus padres + const std::string xmlDir = Utils::FileSystem::getParent(themeXmlPath); + const std::string parent1 = Utils::FileSystem::getParent(xmlDir); + const std::string parent2 = Utils::FileSystem::getParent(parent1); + + // Rutas candidatas para theme.ini + std::vector candidates; + candidates.push_back(xmlDir + "/theme.ini"); + candidates.push_back(parent1 + "/theme.ini"); + candidates.push_back(parent2 + "/theme.ini"); + + for(const auto& cfgPath : candidates) + { + if(!Utils::FileSystem::exists(cfgPath)) + continue; + + std::ifstream file(cfgPath.c_str()); + if(!file.good()) + { + LOG(LogWarning) << "ThemeData: could not open theme.ini at " << cfgPath; + continue; + } + + LOG(LogInfo) << "ThemeData: loading variables from " << cfgPath; + + std::string line; + std::string currentSection; // "", "global", "layout_smd", etc. + + // Si NO hay ThemeLayout definido, usaremos SOLO la primera sección "layout_..." + bool layoutAlreadyChosen = false; + + while(std::getline(file, line)) + { + line = Utils::String::trim(line); + + // saltar líneas vacías y comentarios + if(line.empty() || line[0] == '#' || line[0] == ';') + continue; + + // Sección INI: [nombre] + if(line.front() == '[' && line.back() == ']' && line.size() >= 2) + { + currentSection = Utils::String::trim(line.substr(1, line.size() - 2)); + continue; + } + + // Clave = valor + auto eqPos = line.find('='); + if(eqPos == std::string::npos) + continue; + + std::string key = Utils::String::trim(line.substr(0, eqPos)); + std::string value = Utils::String::trim(line.substr(eqPos + 1)); + + if(key.empty()) + continue; + + // ¿Aplicamos esta línea? + // - sin sección -> siempre + // - [global] / [default] -> siempre + // - [layout_xyz] con layout -> si coincide con ThemeLayout (flexible) + // - [layout_xyz] sin layout -> solo la PRIMER sección "layout_..." del archivo + bool isGlobalSection = + currentSection.empty() + || Utils::String::toLower(currentSection) == "global" + || Utils::String::toLower(currentSection) == "default"; + + bool matchesLayout = false; + + if(!currentSection.empty()) + { + std::string sectionLower = Utils::String::toLower(currentSection); + + bool isLayoutSection = + sectionLower.size() >= 7 && + sectionLower.substr(0, 7) == "layout_"; + + if(hasActiveLayout) + { + // Modo flexible: + // ThemeLayout = "smd" con sección "[layout_smd]" + // ThemeLayout = "layout_smd" con sección "[layout_smd]" + if(sectionLower == activeLower) + { + matchesLayout = true; + } + else if(isLayoutSection) + { + // sección "layout_xxx", layout "xxx" + std::string withoutPrefix = sectionLower.substr(7); + if(activeLower == withoutPrefix) + matchesLayout = true; + // sección "xxx", layout "layout_xxx" + if(!matchesLayout && activeLower.size() > 7 && + activeLower.substr(0,7) == "layout_" && + sectionLower == activeLower.substr(7)) + { + matchesLayout = true; + } + } + } + else + { + // No hay ThemeLayout: tomamos SOLO la primera sección "layout_..." + bool isLayoutSectionNoSetting = + sectionLower.size() >= 7 && + sectionLower.substr(0, 7) == "layout_"; + + if(isLayoutSectionNoSetting && !layoutAlreadyChosen) + { + matchesLayout = true; + layoutAlreadyChosen = true; + } + } + } + + if(isGlobalSection || matchesLayout) + { + // sobreescribe si ya existe (layout por encima de global) + outVars[key] = value; + } + } + + // Solo usamos el primer theme.ini encontrado + break; + } + } +} + std::string ThemeData::resolvePlaceholders(const char* in) { std::string inStr(in); @@ -219,7 +377,13 @@ std::string ThemeData::resolvePlaceholders(const char* in) std::string replace = inStr.substr(variableBegin + 2, variableEnd - (variableBegin + 2)); std::string suffix = resolvePlaceholders(inStr.substr(variableEnd + 1).c_str()); - return prefix + mVariables[replace] + suffix; + // Si la variable existe, la reemplazamos, si no, la dejamos en blanco + auto varIt = mVariables.find(replace); + std::string value; + if(varIt != mVariables.cend()) + value = varIt->second; + + return prefix + value + suffix; } ThemeData::ThemeData() @@ -243,8 +407,12 @@ void ThemeData::loadFile(std::map sysDataMap, const st mViews.clear(); mVariables.clear(); + // Variables del sistema (nombre, shortName, etc.) mVariables.insert(sysDataMap.cbegin(), sysDataMap.cend()); + // NUEVO: variables externas desde theme.ini (global + layout activo) + loadExternalThemeVariables(path, mVariables); + pugi::xml_document doc; pugi::xml_parse_result res = doc.load_file(path.c_str()); if(!res) @@ -422,7 +590,6 @@ void ThemeData::parseView(const pugi::xml_node& root, ThemeView& view) } } - void ThemeData::parseElement(const pugi::xml_node& root, const std::map& typeMap, ThemeElement& element) { ThemeException error; @@ -522,7 +689,7 @@ void ThemeData::parseElement(const pugi::xml_node& root, const std::mapfileExists(path)) { std::stringstream ss; - ss << " Warning " << error.msg; // "from theme yadda yadda, included file yadda yadda + ss << " Warning " << error.msg; ss << "could not find file \"" << node.text().get() << "\" "; if(node.text().get() != path) ss << "(which resolved to \"" << path << "\") "; @@ -676,7 +843,10 @@ std::string ThemeData::getThemeFromCurrentSet(const std::string& system) return ""; } - std::map::const_iterator set = themeSets.find(Settings::getInstance()->getString("ThemeSet")); + // usar siempre const_iterator para evitar el error de asignación + std::map::const_iterator set = + themeSets.find(Settings::getInstance()->getString("ThemeSet")); + if(set == themeSets.cend()) { // currently selected theme set is missing, so just pick the first available set diff --git a/es-core/src/Window.cpp b/es-core/src/Window.cpp index 42c9bfa746..6698aa0190 100644 --- a/es-core/src/Window.cpp +++ b/es-core/src/Window.cpp @@ -6,15 +6,33 @@ #include "resources/TextureResource.h" #include "Log.h" #include "Scripting.h" + #include #include +#include +#include #ifdef WIN32 #include #endif -Window::Window() : mNormalizeNextUpdate(false), mFrameTimeElapsed(0), mFrameCountElapsed(0), mAverageDeltaTime(10), - mAllowSleep(true), mSleeping(false), mTimeSinceLastInput(0), mScreenSaver(NULL), mRenderScreenSaver(false), mInfoPopup(NULL) +Window::Window() + : mNormalizeNextUpdate(false) + , mFrameTimeElapsed(0) + , mFrameCountElapsed(0) + , mAverageDeltaTime(10) + , mAllowSleep(true) + , mSleeping(false) + , mTimeSinceLastInput(0) + , mScreenSaver(NULL) + , mRenderScreenSaver(false) + , mInfoPopup(NULL) + , mRenderedHelpPrompts(false) + // Clock + , mClockTimeAccum(0) + , mClockLastText("") + , mClockTextCache(nullptr) + , mClockOutlineCache(nullptr) { mHelp = new HelpComponent(this); mBackgroundOverlay = new ImageComponent(this); @@ -25,7 +43,7 @@ Window::~Window() delete mBackgroundOverlay; // delete all our GUIs - while(peekGui()) + while (peekGui()) delete peekGui(); delete mHelp; @@ -44,13 +62,13 @@ void Window::pushGui(GuiComponent* gui) void Window::removeGui(GuiComponent* gui) { - for(auto i = mGuiStack.cbegin(); i != mGuiStack.cend(); i++) + for (auto i = mGuiStack.cbegin(); i != mGuiStack.cend(); i++) { - if(*i == gui) + if (*i == gui) { i = mGuiStack.erase(i); - if(i == mGuiStack.cend() && mGuiStack.size()) // we just popped the stack and the stack is not empty + if (i == mGuiStack.cend() && mGuiStack.size()) // we just popped the stack and the stack is not empty { mGuiStack.back()->updateHelpPrompts(); mGuiStack.back()->topWindow(true); @@ -63,7 +81,7 @@ void Window::removeGui(GuiComponent* gui) GuiComponent* Window::peekGui() { - if(mGuiStack.size() == 0) + if (mGuiStack.size() == 0) return NULL; return mGuiStack.back(); @@ -71,7 +89,7 @@ GuiComponent* Window::peekGui() bool Window::init() { - if(!Renderer::init()) + if (!Renderer::init()) { LOG(LogError) << "Renderer failed to initialize!"; return false; @@ -79,8 +97,8 @@ bool Window::init() ResourceManager::getInstance()->reloadAll(); - //keep a reference to the default fonts, so they don't keep getting destroyed/recreated - if(mDefaultFonts.empty()) + // keep a reference to the default fonts, so they don't keep getting destroyed/recreated + if (mDefaultFonts.empty()) { mDefaultFonts.push_back(Font::get(FONT_SIZE_SMALL)); mDefaultFonts.push_back(Font::get(FONT_SIZE_MEDIUM)); @@ -91,16 +109,22 @@ bool Window::init() mBackgroundOverlay->setResize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); // update our help because font sizes probably changed - if(peekGui()) + if (peekGui()) peekGui()->updateHelpPrompts(); + // Reset clock cache on init (safe) + mClockTimeAccum = 0; + mClockLastText.clear(); + mClockTextCache.reset(); + mClockOutlineCache.reset(); + return true; } void Window::deinit() { // Hide all GUI elements on uninitialisation - this disable - for(auto i = mGuiStack.cbegin(); i != mGuiStack.cend(); i++) + for (auto i = mGuiStack.cbegin(); i != mGuiStack.cend(); i++) { (*i)->onHide(); } @@ -110,7 +134,7 @@ void Window::deinit() void Window::textInput(const char* text) { - if(peekGui()) + if (peekGui()) peekGui()->textInput(text); } @@ -159,20 +183,20 @@ void Window::input(InputConfig* config, Input input) void Window::update(int deltaTime) { - if(mNormalizeNextUpdate) + if (mNormalizeNextUpdate) { mNormalizeNextUpdate = false; - if(deltaTime > mAverageDeltaTime) + if (deltaTime > mAverageDeltaTime) deltaTime = mAverageDeltaTime; } mFrameTimeElapsed += deltaTime; mFrameCountElapsed++; - if(mFrameTimeElapsed > 500) + if (mFrameTimeElapsed > 500) { mAverageDeltaTime = mFrameTimeElapsed / mFrameCountElapsed; - if(Settings::getInstance()->getBool("DrawFramerate")) + if (Settings::getInstance()->getBool("DrawFramerate")) { std::stringstream ss; @@ -186,7 +210,7 @@ void Window::update(int deltaTime) float fontVramUsageMb = Font::getTotalMemUsage() / 1000.0f / 1000.0f; ss << "\nFont VRAM: " << fontVramUsageMb << " Tex VRAM: " << textureVramUsageMb << - " Tex Max: " << textureTotalUsageMb; + " Tex Max: " << textureTotalUsageMb; mFrameDataText = std::unique_ptr(mDefaultFonts.at(1)->buildTextCache(ss.str(), 50.f, 50.f, 0xFF00FFFF)); } @@ -196,12 +220,57 @@ void Window::update(int deltaTime) mTimeSinceLastInput += deltaTime; - if(peekGui()) + if (peekGui()) peekGui()->update(deltaTime); // Update the screensaver if (mScreenSaver) mScreenSaver->update(deltaTime); + + // ============================================================ + // Clock overlay (safe) - update once per second + // Controlled by Settings bool: "ShowClock" + // NOTE: no ThemeData/SystemData access from es-core. + // ============================================================ + const bool showClock = Settings::getInstance()->getBool("ShowClock"); + if (showClock) + { + mClockTimeAccum += deltaTime; + if (mClockTimeAccum >= 1000 || mClockTextCache == nullptr) + { + mClockTimeAccum = 0; + + // Build HH:MM (24h) + std::time_t t = std::time(nullptr); + std::tm tmNow; +#if defined(_WIN32) + localtime_s(&tmNow, &t); +#else + localtime_r(&t, &tmNow); +#endif + std::ostringstream os; + os << std::put_time(&tmNow, "%H:%M"); + const std::string newText = os.str(); + + if (newText != mClockLastText) + { + mClockLastText = newText; + + // Border is ALWAYS black (design decision) + mClockOutlineCache.reset(mDefaultFonts.at(1)->buildTextCache(mClockLastText, 0.f, 0.f, 0x000000FF)); + // Main text (for now white; later can be overridden by style passed from es-app) + mClockTextCache.reset(mDefaultFonts.at(1)->buildTextCache(mClockLastText, 0.f, 0.f, 0xFFFFFFFF)); + } + } + } + else + { + // If disabled, free cache + mClockTimeAccum = 0; + mClockLastText.clear(); + mClockTextCache.reset(); + mClockOutlineCache.reset(); + } } void Window::render() @@ -211,45 +280,76 @@ void Window::render() mRenderedHelpPrompts = false; // draw only bottom and top of GuiStack (if they are different) - if(mGuiStack.size()) + if (mGuiStack.size()) { auto& bottom = mGuiStack.front(); auto& top = mGuiStack.back(); bottom->render(transform); - if(bottom != top) + if (bottom != top) { mBackgroundOverlay->render(transform); top->render(transform); } } - if(!mRenderedHelpPrompts) + if (!mRenderedHelpPrompts) mHelp->render(transform); - if(Settings::getInstance()->getBool("DrawFramerate") && mFrameDataText) + if (Settings::getInstance()->getBool("DrawFramerate") && mFrameDataText) { Renderer::setMatrix(Transform4x4f::Identity()); mDefaultFonts.at(1)->renderTextCache(mFrameDataText.get()); } unsigned int screensaverTime = (unsigned int)Settings::getInstance()->getInt("ScreenSaverTime"); - if(mTimeSinceLastInput >= screensaverTime && screensaverTime != 0) + if (mTimeSinceLastInput >= screensaverTime && screensaverTime != 0) startScreenSaver(); // Always call the screensaver render function regardless of whether the screensaver is active // or not because it may perform a fade on transition renderScreenSaver(); - if(mInfoPopup) + // ============================================================ + // Global overlays (ES-DE style) + // - Clock only when ShowClock = true + // - Hide overlays when screensaver active + // ============================================================ + const bool showClock = Settings::getInstance()->getBool("ShowClock"); + if (showClock && !mRenderScreenSaver && mClockTextCache && mClockOutlineCache) + { + // Top-right placement with padding + const float pad = 20.0f; + const float x = Math::round((float)Renderer::getScreenWidth() - mClockTextCache->metrics.size.x() - pad); + const float y = Math::round(pad); + + // 1px outline (4 directions) using the outline cache (black) + static const int off[4][2] = { {-1,0}, {1,0}, {0,-1}, {0,1} }; + for (int i = 0; i < 4; i++) + { + Transform4x4f t = Transform4x4f::Identity(); + t = t.translate(Vector3f(x + (float)off[i][0], y + (float)off[i][1], 0.0f)); + Renderer::setMatrix(t); + mDefaultFonts.at(1)->renderTextCache(mClockOutlineCache.get()); + } + + // Main text + Transform4x4f t = Transform4x4f::Identity(); + t = t.translate(Vector3f(x, y, 0.0f)); + Renderer::setMatrix(t); + mDefaultFonts.at(1)->renderTextCache(mClockTextCache.get()); + } + + // Info popup always on top + if (mInfoPopup) { mInfoPopup->render(transform); } - if(mTimeSinceLastInput >= screensaverTime && screensaverTime != 0) + if (mTimeSinceLastInput >= screensaverTime && screensaverTime != 0) { unsigned int systemSleepTime = (unsigned int)Settings::getInstance()->getInt("SystemSleepTime"); - if(!isProcessing() && mAllowSleep && systemSleepTime != 0 && mTimeSinceLastInput >= systemSleepTime) { + if (!isProcessing() && mAllowSleep && systemSleepTime != 0 && mTimeSinceLastInput >= systemSleepTime) { mSleeping = true; onSleep(); } @@ -331,29 +431,31 @@ void Window::setHelpPrompts(const std::vector& prompts, const HelpSt std::map inputSeenMap; std::map mappedToSeenMap; - for(auto it = prompts.cbegin(); it != prompts.cend(); it++) + for (auto it = prompts.cbegin(); it != prompts.cend(); it++) { // only add it if the same icon hasn't already been added - if(inputSeenMap.emplace(it->first, true).second) + if (inputSeenMap.emplace(it->first, true).second) { // this symbol hasn't been seen yet, what about the action name? auto mappedTo = mappedToSeenMap.find(it->second); - if(mappedTo != mappedToSeenMap.cend()) + if (mappedTo != mappedToSeenMap.cend()) { // yes, it has! // can we combine? (dpad only) - if((it->first == "up/down" && addPrompts.at(mappedTo->second).first != "left/right") || + if ((it->first == "up/down" && addPrompts.at(mappedTo->second).first != "left/right") || (it->first == "left/right" && addPrompts.at(mappedTo->second).first != "up/down")) { // yes! addPrompts.at(mappedTo->second).first = "up/down/left/right"; // don't need to add this to addPrompts since we just merged - }else{ + } + else { // no, we can't combine! addPrompts.push_back(*it); } - }else{ + } + else { // no, it hasn't! mappedToSeenMap.emplace(it->second, (int)addPrompts.size()); addPrompts.push_back(*it); @@ -376,11 +478,11 @@ void Window::setHelpPrompts(const std::vector& prompts, const HelpSt int i = 0; int aVal = 0; int bVal = 0; - while(map[i] != NULL) + while (map[i] != NULL) { - if(a.first == map[i]) + if (a.first == map[i]) aVal = i; - if(b.first == map[i]) + if (b.first == map[i]) bVal = i; i++; } @@ -391,7 +493,6 @@ void Window::setHelpPrompts(const std::vector& prompts, const HelpSt mHelp->setPrompts(addPrompts); } - void Window::onSleep() { if (Settings::getInstance()->getBool("Windowed")) { @@ -424,7 +525,7 @@ void Window::startScreenSaver(SystemData* system) { Scripting::fireEvent("screensaver-start"); // Tell the GUI components the screensaver is starting - for(auto i = mGuiStack.cbegin(); i != mGuiStack.cend(); i++) + for (auto i = mGuiStack.cbegin(); i != mGuiStack.cend(); i++) (*i)->onScreenSaverActivate(); mScreenSaver->startScreenSaver(system); @@ -441,7 +542,7 @@ bool Window::cancelScreenSaver() Scripting::fireEvent("screensaver-stop"); // Tell the GUI components the screensaver has stopped - for(auto i = mGuiStack.cbegin(); i != mGuiStack.cend(); i++) + for (auto i = mGuiStack.cbegin(); i != mGuiStack.cend(); i++) (*i)->onScreenSaverDeactivate(); return true; diff --git a/es-core/src/Window.h b/es-core/src/Window.h index 8ab3504c4a..0b0a52f864 100644 --- a/es-core/src/Window.h +++ b/es-core/src/Window.h @@ -7,6 +7,7 @@ #include "Settings.h" #include +#include class SystemData; class FileData; @@ -87,8 +88,8 @@ class Window HelpComponent* mHelp; ImageComponent* mBackgroundOverlay; ScreenSaver* mScreenSaver; - InfoPopup* mInfoPopup; - bool mRenderScreenSaver; + InfoPopup* mInfoPopup; + bool mRenderScreenSaver; std::vector mGuiStack; @@ -107,6 +108,14 @@ class Window unsigned int mTimeSinceLastInput; bool mRenderedHelpPrompts; + + // ========================= + // Clock overlay (global) + // ========================= + int mClockTimeAccum; + std::string mClockLastText; + std::unique_ptr mClockTextCache; + std::unique_ptr mClockOutlineCache; }; #endif // ES_CORE_WINDOW_H diff --git a/es-core/src/components/ButtonComponent.cpp b/es-core/src/components/ButtonComponent.cpp index d0f47ce3a7..12c3bf530f 100644 --- a/es-core/src/components/ButtonComponent.cpp +++ b/es-core/src/components/ButtonComponent.cpp @@ -2,118 +2,123 @@ #include "resources/Font.h" #include "utils/StringUtil.h" - -ButtonComponent::ButtonComponent(Window* window, const std::string& text, const std::string& helpText, const std::function& func) : GuiComponent(window), - mBox(window, ":/button.png"), - mFont(Font::get(FONT_SIZE_MEDIUM)), - mFocused(false), - mEnabled(true), - mTextColorFocused(0xFFFFFFFF), mTextColorUnfocused(0x777777FF) +#include "../LocaleESHook.h" // 🔹 Importante para es_translate + +ButtonComponent::ButtonComponent(Window* window, const std::string& text, const std::string& helpText, const std::function& func) + : GuiComponent(window), + mBox(window, ":/button.png"), + mFont(Font::get(FONT_SIZE_MEDIUM)), + mFocused(false), + mEnabled(true), + mTextColorFocused(0xFFFFFFFF), + mTextColorUnfocused(0x777777FF) { - setPressedFunc(func); - setText(text, helpText); - updateImage(); + setPressedFunc(func); + setText(text, helpText); + updateImage(); } void ButtonComponent::onSizeChanged() { - mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); + mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); } void ButtonComponent::setPressedFunc(std::function f) { - mPressedFunc = f; + mPressedFunc = f; } bool ButtonComponent::input(InputConfig* config, Input input) { - if(config->isMappedTo("a", input) && input.value != 0) - { - if(mPressedFunc && mEnabled) - mPressedFunc(); - return true; - } - - return GuiComponent::input(config, input); + if(config->isMappedTo("a", input) && input.value != 0) + { + if(mPressedFunc && mEnabled) + mPressedFunc(); + return true; + } + + return GuiComponent::input(config, input); } void ButtonComponent::setText(const std::string& text, const std::string& helpText) { - mText = Utils::String::toUpper(text); - mHelpText = helpText; + // 🔹 Ahora el texto y helpText pasan por es_translate antes de convertirse a mayúsculas + mText = Utils::String::toUpper(es_translate(text)); + mHelpText = helpText.empty() ? es_translate(text) : es_translate(helpText); - mTextCache = std::unique_ptr(mFont->buildTextCache(mText, 0, 0, getCurTextColor())); + mTextCache = std::unique_ptr(mFont->buildTextCache(mText, 0, 0, getCurTextColor())); - float minWidth = mFont->sizeText("DELETE").x() + 12; - setSize(Math::max(mTextCache->metrics.size.x() + 12, minWidth), mTextCache->metrics.size.y()); + float minWidth = mFont->sizeText("DELETE").x() + 12; + setSize(Math::max(mTextCache->metrics.size.x() + 12, minWidth), mTextCache->metrics.size.y()); - updateHelpPrompts(); + updateHelpPrompts(); } void ButtonComponent::onFocusGained() { - mFocused = true; - updateImage(); + mFocused = true; + updateImage(); } void ButtonComponent::onFocusLost() { - mFocused = false; - updateImage(); + mFocused = false; + updateImage(); } void ButtonComponent::setEnabled(bool enabled) { - mEnabled = enabled; - updateImage(); + mEnabled = enabled; + updateImage(); } void ButtonComponent::updateImage() { - if(!mEnabled || !mPressedFunc) - { - mBox.setImagePath(":/button_filled.png"); - mBox.setCenterColor(0x770000FF); - mBox.setEdgeColor(0x770000FF); - return; - } - - mBox.setCenterColor(0xFFFFFFFF); - mBox.setEdgeColor(0xFFFFFFFF); - mBox.setImagePath(mFocused ? ":/button_filled.png" : ":/button.png"); + if(!mEnabled || !mPressedFunc) + { + mBox.setImagePath(":/button_filled.png"); + mBox.setCenterColor(0x770000FF); + mBox.setEdgeColor(0x770000FF); + return; + } + + mBox.setCenterColor(0xFFFFFFFF); + mBox.setEdgeColor(0xFFFFFFFF); + mBox.setImagePath(mFocused ? ":/button_filled.png" : ":/button.png"); } void ButtonComponent::render(const Transform4x4f& parentTrans) { - Transform4x4f trans = parentTrans * getTransform(); + Transform4x4f trans = parentTrans * getTransform(); - mBox.render(trans); + mBox.render(trans); - if(mTextCache) - { - Vector3f centerOffset((mSize.x() - mTextCache->metrics.size.x()) / 2, (mSize.y() - mTextCache->metrics.size.y()) / 2, 0); - trans = trans.translate(centerOffset); + if(mTextCache) + { + Vector3f centerOffset((mSize.x() - mTextCache->metrics.size.x()) / 2, + (mSize.y() - mTextCache->metrics.size.y()) / 2, 0); + trans = trans.translate(centerOffset); - Renderer::setMatrix(trans); - mTextCache->setColor(getCurTextColor()); - mFont->renderTextCache(mTextCache.get()); - trans = trans.translate(-centerOffset); - } + Renderer::setMatrix(trans); + mTextCache->setColor(getCurTextColor()); + mFont->renderTextCache(mTextCache.get()); + trans = trans.translate(-centerOffset); + } - renderChildren(trans); + renderChildren(trans); } unsigned int ButtonComponent::getCurTextColor() const { - if(!mFocused) - return mTextColorUnfocused; - else - return mTextColorFocused; + return mFocused ? mTextColorFocused : mTextColorUnfocused; } std::vector ButtonComponent::getHelpPrompts() { - std::vector prompts; - prompts.push_back(HelpPrompt("a", mHelpText.empty() ? mText.c_str() : mHelpText.c_str())); - return prompts; + std::vector prompts; + + std::string helpLabel = mHelpText.empty() ? mText : mHelpText; + prompts.push_back(HelpPrompt("a", Utils::String::toUpper(helpLabel))); + + return prompts; } diff --git a/es-core/src/components/ComponentList.cpp b/es-core/src/components/ComponentList.cpp index 1ae1c67562..0b5e253341 100644 --- a/es-core/src/components/ComponentList.cpp +++ b/es-core/src/components/ComponentList.cpp @@ -1,337 +1,335 @@ +// es-core/src/components/ComponentList.cpp + #include "components/ComponentList.h" #define TOTAL_HORIZONTAL_PADDING_PX 20 -ComponentList::ComponentList(Window* window) : IList(window, LIST_SCROLL_STYLE_SLOW, LIST_NEVER_LOOP) +ComponentList::ComponentList(Window* window) + : IList(window, LIST_SCROLL_STYLE_SLOW, LIST_NEVER_LOOP) { - mSelectorBarOffset = 0; - mCameraOffset = 0; - mFocused = false; + mSelectorBarOffset = 0; + mCameraOffset = 0; + mFocused = false; } void ComponentList::addRow(const ComponentListRow& row, bool setCursorHere) { - IList::Entry e; - e.name = ""; - e.object = NULL; - e.data = row; + IList::Entry e; + e.name = ""; + e.object = NULL; + e.data = row; - this->add(e); + this->add(e); - for(auto it = mEntries.back().data.elements.cbegin(); it != mEntries.back().data.elements.cend(); it++) - addChild(it->component.get()); + for (auto it = mEntries.back().data.elements.cbegin(); it != mEntries.back().data.elements.cend(); it++) + addChild(it->component.get()); - updateElementSize(mEntries.back().data); - updateElementPosition(mEntries.back().data); + updateElementSize(mEntries.back().data); + updateElementPosition(mEntries.back().data); - if(setCursorHere) - { - mCursor = (int)mEntries.size() - 1; - onCursorChanged(CURSOR_STOPPED); - } + if (setCursorHere) + { + mCursor = (int)mEntries.size() - 1; + onCursorChanged(CURSOR_STOPPED); + } } void ComponentList::onSizeChanged() { - for(auto it = mEntries.cbegin(); it != mEntries.cend(); it++) - { - updateElementSize(it->data); - updateElementPosition(it->data); - } + for (auto it = mEntries.cbegin(); it != mEntries.cend(); it++) + { + updateElementSize(it->data); + updateElementPosition(it->data); + } - updateCameraOffset(); + updateCameraOffset(); } void ComponentList::onFocusLost() { - mFocused = false; + mFocused = false; } void ComponentList::onFocusGained() { - mFocused = true; + mFocused = true; } bool ComponentList::input(InputConfig* config, Input input) { - if(size() == 0) - return false; - - // give it to the current row's input handler - if(mEntries.at(mCursor).data.input_handler) - { - if(mEntries.at(mCursor).data.input_handler(config, input)) - return true; - }else{ - // no input handler assigned, do the default, which is to give it to the rightmost element in the row - auto& row = mEntries.at(mCursor).data; - if(row.elements.size()) - { - if(row.elements.back().component->input(config, input)) - return true; - } - } - - // input handler didn't consume the input - try to scroll - if(config->isMappedLike("up", input)) - { - return listInput(input.value != 0 ? -1 : 0); - }else if(config->isMappedLike("down", input)) - { - return listInput(input.value != 0 ? 1 : 0); - - }else if(config->isMappedLike("leftshoulder", input)) - { - return listInput(input.value != 0 ? -6 : 0); - }else if(config->isMappedLike("rightshoulder", input)){ - return listInput(input.value != 0 ? 6 : 0); - } - - return false; + if (size() == 0) + return false; + + // give it to the current row's input handler + if (mEntries.at(mCursor).data.input_handler) + { + if (mEntries.at(mCursor).data.input_handler(config, input)) + return true; + } + else + { + // no input handler assigned, do the default + auto& row = mEntries.at(mCursor).data; + if (row.elements.size()) + { + if (row.elements.back().component->input(config, input)) + return true; + } + } + + // input handler didn't consume input - try to scroll + if (config->isMappedLike("up", input)) + { + return listInput(input.value != 0 ? -1 : 0); + } + else if (config->isMappedLike("down", input)) + { + return listInput(input.value != 0 ? 1 : 0); + } + else if (config->isMappedLike("leftshoulder", input)) + { + return listInput(input.value != 0 ? -6 : 0); + } + else if (config->isMappedLike("rightshoulder", input)) + { + return listInput(input.value != 0 ? 6 : 0); + } + + return false; } void ComponentList::update(int deltaTime) { - listUpdate(deltaTime); - - if(size()) - { - // update our currently selected row - for(auto it = mEntries.at(mCursor).data.elements.cbegin(); it != mEntries.at(mCursor).data.elements.cend(); it++) - it->component->update(deltaTime); - } + listUpdate(deltaTime); + + if (size()) + { + for (auto it = mEntries.at(mCursor).data.elements.cbegin(); it != mEntries.at(mCursor).data.elements.cend(); it++) + it->component->update(deltaTime); + } } void ComponentList::onCursorChanged(const CursorState& state) { - // update the selector bar position - // in the future this might be animated - mSelectorBarOffset = 0; - for(int i = 0; i < mCursor; i++) - { - mSelectorBarOffset += getRowHeight(mEntries.at(i).data); - } + mSelectorBarOffset = 0; + for (int i = 0; i < mCursor; i++) + { + mSelectorBarOffset += getRowHeight(mEntries.at(i).data); + } - updateCameraOffset(); + updateCameraOffset(); - // this is terribly inefficient but we don't know what we came from so... - if(size()) - { - for(auto it = mEntries.cbegin(); it != mEntries.cend(); it++) - it->data.elements.back().component->onFocusLost(); + if (size()) + { + for (auto it = mEntries.cbegin(); it != mEntries.cend(); it++) + it->data.elements.back().component->onFocusLost(); - mEntries.at(mCursor).data.elements.back().component->onFocusGained(); - } + mEntries.at(mCursor).data.elements.back().component->onFocusGained(); + } - if(mCursorChangedCallback) - mCursorChangedCallback(state); + if (mCursorChangedCallback) + mCursorChangedCallback(state); - updateHelpPrompts(); + updateHelpPrompts(); } void ComponentList::updateCameraOffset() { - // move the camera to scroll - const float totalHeight = getTotalRowHeight(); - if(totalHeight > mSize.y()) - { - float target = mSelectorBarOffset + getRowHeight(mEntries.at(mCursor).data)/2 - (mSize.y() / 2); - - // clamp it - mCameraOffset = 0; - unsigned int i = 0; - while(mCameraOffset < target && i < mEntries.size()) - { - mCameraOffset += getRowHeight(mEntries.at(i).data); - i++; - } - - if(mCameraOffset < 0) - mCameraOffset = 0; - else if(mCameraOffset + mSize.y() > totalHeight) - mCameraOffset = totalHeight - mSize.y(); - }else{ - mCameraOffset = 0; - } + const float totalHeight = getTotalRowHeight(); + if (totalHeight > mSize.y()) + { + float target = mSelectorBarOffset + getRowHeight(mEntries.at(mCursor).data) / 2 - (mSize.y() / 2); + + mCameraOffset = 0; + unsigned int i = 0; + while (mCameraOffset < target && i < mEntries.size()) + { + mCameraOffset += getRowHeight(mEntries.at(i).data); + i++; + } + + if (mCameraOffset < 0) + mCameraOffset = 0; + else if (mCameraOffset + mSize.y() > totalHeight) + mCameraOffset = totalHeight - mSize.y(); + } + else + { + mCameraOffset = 0; + } } void ComponentList::render(const Transform4x4f& parentTrans) { - if(!size()) - return; - - Transform4x4f trans = parentTrans * getTransform(); - - // clip everything to be inside our bounds - Vector3f dim(mSize.x(), mSize.y(), 0); - dim = trans * dim - trans.translation(); - Renderer::pushClipRect(Vector2i((int)trans.translation().x(), (int)trans.translation().y()), - Vector2i((int)Math::round(dim.x()), (int)Math::round(dim.y() + 1))); - - // scroll the camera - trans.translate(Vector3f(0, -Math::round(mCameraOffset), 0)); - - // draw our entries - std::vector drawAfterCursor; - bool drawAll; - for(unsigned int i = 0; i < mEntries.size(); i++) - { - auto& entry = mEntries.at(i); - drawAll = !mFocused || i != (unsigned int)mCursor; - for(auto it = entry.data.elements.cbegin(); it != entry.data.elements.cend(); it++) - { - if(drawAll || it->invert_when_selected) - { - it->component->render(trans); - }else{ - drawAfterCursor.push_back(it->component.get()); - } - } - } - - // custom rendering - Renderer::setMatrix(trans); - - // draw selector bar - if(mFocused) - { - // inversion: src * (1 - dst) + dst * 0 = where src = 1 - // need a function that goes roughly 0x777777 -> 0xFFFFFF - // and 0xFFFFFF -> 0x777777 - // (1 - dst) + 0x77 - - const float selectedRowHeight = getRowHeight(mEntries.at(mCursor).data); - Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x(), selectedRowHeight, 0xFFFFFFFF, 0xFFFFFFFF, false, Renderer::Blend::ONE_MINUS_DST_COLOR, Renderer::Blend::ZERO); - Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x(), selectedRowHeight, 0x777777FF, 0x777777FF, false, Renderer::Blend::ONE, Renderer::Blend::ONE); - - // hack to draw 2px dark on left/right of the bar - Renderer::drawRect(0.0f, mSelectorBarOffset, 2.0f, selectedRowHeight, 0x878787FF, 0x878787FF); - Renderer::drawRect(mSize.x() - 2.0f, mSelectorBarOffset, 2.0f, selectedRowHeight, 0x878787FF, 0x878787FF); - - for(auto it = drawAfterCursor.cbegin(); it != drawAfterCursor.cend(); it++) - (*it)->render(trans); - - // reset matrix if one of these components changed it - if(drawAfterCursor.size()) - Renderer::setMatrix(trans); - } - - // draw separators - float y = 0; - for(unsigned int i = 0; i < mEntries.size(); i++) - { - Renderer::drawRect(0.0f, y, mSize.x(), 1.0f, 0xC6C7C6FF, 0xC6C7C6FF); - y += getRowHeight(mEntries.at(i).data); - } - Renderer::drawRect(0.0f, y, mSize.x(), 1.0f, 0xC6C7C6FF, 0xC6C7C6FF); - - Renderer::popClipRect(); + if (!size()) + return; + + Transform4x4f trans = parentTrans * getTransform(); + + Vector3f dim(mSize.x(), mSize.y(), 0); + dim = trans * dim - trans.translation(); + Renderer::pushClipRect( + Vector2i((int)trans.translation().x(), (int)trans.translation().y()), + Vector2i((int)Math::round(dim.x()), (int)Math::round(dim.y() + 1))); + + trans.translate(Vector3f(0, -Math::round(mCameraOffset), 0)); + + // draw entries + std::vector drawAfterCursor; + bool drawAll; + + for (unsigned int i = 0; i < mEntries.size(); i++) + { + auto& entry = mEntries.at(i); + drawAll = !mFocused || i != (unsigned int)mCursor; + + for (auto it = entry.data.elements.cbegin(); it != entry.data.elements.cend(); it++) + { + if (drawAll || it->invert_when_selected) + it->component->render(trans); + else + drawAfterCursor.push_back(it->component.get()); + } + } + + Renderer::setMatrix(trans); + + // draw selector bar + if (mFocused) + { + const float selectedRowHeight = getRowHeight(mEntries.at(mCursor).data); + + // --- BARRA AZUL MÁS TRANSPARENTE (30%) --- +const unsigned int barColor = 0x0063BF4C; // azul PS4 con ~30% de opacidad +const unsigned int borderColor = 0x00336666; // bordes sutiles ~40% + + // barra + Renderer::drawRect( + 0.0f, + mSelectorBarOffset, + mSize.x(), + selectedRowHeight, + barColor, + barColor); + + // bordes laterales + Renderer::drawRect(0.0f, mSelectorBarOffset, 2.0f, selectedRowHeight, borderColor, borderColor); + Renderer::drawRect(mSize.x() - 2.0f, mSelectorBarOffset, 2.0f, selectedRowHeight, borderColor, borderColor); + + // texto → SIEMPRE encima + for (auto it = drawAfterCursor.cbegin(); it != drawAfterCursor.cend(); it++) + (*it)->render(trans); + + if (drawAfterCursor.size()) + Renderer::setMatrix(trans); + } + + // draw separators + float y = 0; + for (unsigned int i = 0; i < mEntries.size(); i++) + { + Renderer::drawRect(0.0f, y, mSize.x(), 1.0f, 0xC6C7C6FF, 0xC6C7C6FF); + y += getRowHeight(mEntries.at(i).data); + } + Renderer::drawRect(0.0f, y, mSize.x(), 1.0f, 0xC6C7C6FF, 0xC6C7C6FF); + + Renderer::popClipRect(); } float ComponentList::getRowHeight(const ComponentListRow& row) const { - // returns the highest component height found in the row - float height = 0; - for(unsigned int i = 0; i < row.elements.size(); i++) - { - if(row.elements.at(i).component->getSize().y() > height) - height = row.elements.at(i).component->getSize().y(); - } - - return height; + float height = 0; + for (unsigned int i = 0; i < row.elements.size(); i++) + { + if (row.elements.at(i).component->getSize().y() > height) + height = row.elements.at(i).component->getSize().y(); + } + + return height; } float ComponentList::getTotalRowHeight() const { - float height = 0; - for(auto it = mEntries.cbegin(); it != mEntries.cend(); it++) - { - height += getRowHeight(it->data); - } + float height = 0; + for (auto it = mEntries.cbegin(); it != mEntries.cend(); it++) + height += getRowHeight(it->data); - return height; + return height; } void ComponentList::updateElementPosition(const ComponentListRow& row) { - float yOffset = 0; - for(auto it = mEntries.cbegin(); it != mEntries.cend() && &it->data != &row; it++) - { - yOffset += getRowHeight(it->data); - } - - // assumes updateElementSize has already been called - float rowHeight = getRowHeight(row); - - float x = TOTAL_HORIZONTAL_PADDING_PX / 2; - for(unsigned int i = 0; i < row.elements.size(); i++) - { - const auto comp = row.elements.at(i).component; - - // center vertically - comp->setPosition(x, (rowHeight - comp->getSize().y()) / 2 + yOffset); - x += comp->getSize().x(); - } + float yOffset = 0; + for (auto it = mEntries.cbegin(); it != mEntries.cend() && &it->data != &row; it++) + yOffset += getRowHeight(it->data); + + float rowHeight = getRowHeight(row); + float x = TOTAL_HORIZONTAL_PADDING_PX / 2; + + for (unsigned int i = 0; i < row.elements.size(); i++) + { + const auto comp = row.elements.at(i).component; + + comp->setPosition(x, (rowHeight - comp->getSize().y()) / 2 + yOffset); + x += comp->getSize().x(); + } } void ComponentList::updateElementSize(const ComponentListRow& row) { - float width = mSize.x() - TOTAL_HORIZONTAL_PADDING_PX; - std::vector< std::shared_ptr > resizeVec; - - for(auto it = row.elements.cbegin(); it != row.elements.cend(); it++) - { - if(it->resize_width) - resizeVec.push_back(it->component); - else - width -= it->component->getSize().x(); - } - - // redistribute the "unused" width equally among the components with resize_width set to true - width = width / resizeVec.size(); - for(auto it = resizeVec.cbegin(); it != resizeVec.cend(); it++) - { - (*it)->setSize(width, (*it)->getSize().y()); - } + float width = mSize.x() - TOTAL_HORIZONTAL_PADDING_PX; + std::vector> resizeVec; + + for (auto it = row.elements.cbegin(); it != row.elements.cend(); it++) + { + if (it->resize_width) + resizeVec.push_back(it->component); + else + width -= it->component->getSize().x(); + } + + width = width / resizeVec.size(); + for (auto it = resizeVec.cbegin(); it != resizeVec.cend(); it++) + (*it)->setSize(width, (*it)->getSize().y()); } void ComponentList::textInput(const char* text) { - if(!size()) - return; + if (!size()) + return; - mEntries.at(mCursor).data.elements.back().component->textInput(text); + mEntries.at(mCursor).data.elements.back().component->textInput(text); } std::vector ComponentList::getHelpPrompts() { - if(!size()) - return std::vector(); - - std::vector prompts = mEntries.at(mCursor).data.elements.back().component->getHelpPrompts(); - - if(size() > 1) - { - bool addMovePrompt = true; - for(auto it = prompts.cbegin(); it != prompts.cend(); it++) - { - if(it->first == "up/down" || it->first == "up/down/left/right") - { - addMovePrompt = false; - break; - } - } - - if(addMovePrompt) - prompts.push_back(HelpPrompt("up/down", "choose")); - } - - return prompts; + if (!size()) + return std::vector(); + + std::vector prompts = mEntries.at(mCursor).data.elements.back().component->getHelpPrompts(); + + if (size() > 1) + { + bool addMovePrompt = true; + for (auto it = prompts.cbegin(); it != prompts.cend(); it++) + { + if (it->first == "up/down" || it->first == "up/down/left/right") + { + addMovePrompt = false; + break; + } + } + + if (addMovePrompt) + prompts.push_back(HelpPrompt("up/down", "choose")); + } + + return prompts; } bool ComponentList::moveCursor(int amt) { - bool ret = listInput(amt); - listInput(0); - return ret; + bool ret = listInput(amt); + listInput(0); + return ret; } diff --git a/es-core/src/components/ControllerStatusComponent.cpp b/es-core/src/components/ControllerStatusComponent.cpp new file mode 100644 index 0000000000..eae3b4953c --- /dev/null +++ b/es-core/src/components/ControllerStatusComponent.cpp @@ -0,0 +1,89 @@ +#include "components/ControllerStatusComponent.h" + +#include "InputManager.h" +#include "renderers/Renderer.h" +#include "resources/Font.h" + +ControllerStatusComponent::ControllerStatusComponent(Window* window) + : GuiComponent(window), + mMaxControllers(4), + mIconSize(0.035f) +{ + // Fuente players.ttf dentro de resources + mFont = Font::getFromFile( + ":/fonts/players.ttf", + (int)(Renderer::getScreenHeight() * mIconSize), + FONT_PATH_REGULAR); + + setSize(Renderer::getScreenWidth(), Renderer::getScreenHeight()); +} + +void ControllerStatusComponent::setMaxControllers(int maxControllers) +{ + if (maxControllers < 1) + maxControllers = 1; + mMaxControllers = maxControllers; +} + +void ControllerStatusComponent::setIconSize(float size) +{ + if (size < 0.01f) + size = 0.01f; + + mIconSize = size; + + // Recarga tamaño de fuente + mFont = Font::getFromFile( + ":/fonts/players.ttf", + (int)(Renderer::getScreenHeight() * mIconSize), + FONT_PATH_REGULAR); +} + +void ControllerStatusComponent::update(int /*deltaTime*/) +{ + // UI pasiva: nada por ahora +} + +void ControllerStatusComponent::render(const Transform4x4f& parentTrans) +{ + if (!mFont) + return; + + // Batocera/ES-DE style: tomamos el mapa real de “players” + // En tu log ya se ve que InputManager detecta joysticks. + auto players = InputManager::getInstance()->lastKnownPlayersDeviceIndexes(); + + int count = 0; + for (auto& kv : players) + { + if (kv.second >= 0) + count++; + } + + if (count <= 0) + return; + + if (count > mMaxControllers) + count = mMaxControllers; + + Transform4x4f trans = parentTrans * getTransform(); + + // Arriba a la derecha (ajustable después por tema si querés) + const float y = Renderer::getScreenHeight() * 0.04f; + + // Render de “P1..Pn” como dígitos (players.ttf en Batocera suele mapear 1..4) + // Vamos de derecha a izquierda para que quede pegado al borde. + float x = Renderer::getScreenWidth() * 0.96f; + + for (int i = 0; i < count; i++) + { + const std::string glyph = std::to_string(i + 1); + const auto size = mFont->sizeText(glyph); + + x -= size.x(); + mFont->renderText(glyph, x, y, trans, 0xFFFFFFFF); + + // pequeño espaciado + x -= Renderer::getScreenWidth() * 0.006f; + } +} diff --git a/es-core/src/components/ControllerStatusComponent.h b/es-core/src/components/ControllerStatusComponent.h new file mode 100644 index 0000000000..606a518e83 --- /dev/null +++ b/es-core/src/components/ControllerStatusComponent.h @@ -0,0 +1,24 @@ +#pragma once + +#include "GuiComponent.h" +#include +#include + +class Font; + +class ControllerStatusComponent : public GuiComponent +{ +public: + explicit ControllerStatusComponent(Window* window); + + void setMaxControllers(int maxControllers); + void setIconSize(float size); // relativo a altura (ej: 0.035f) + + void update(int deltaTime) override; + void render(const Transform4x4f& parentTrans) override; + +private: + std::shared_ptr mFont; + int mMaxControllers; + float mIconSize; +}; diff --git a/es-core/src/components/DateTimeComponent.cpp b/es-core/src/components/DateTimeComponent.cpp index c1e719b93c..2eafb29c18 100644 --- a/es-core/src/components/DateTimeComponent.cpp +++ b/es-core/src/components/DateTimeComponent.cpp @@ -3,6 +3,7 @@ #include "utils/StringUtil.h" #include "Log.h" #include "Settings.h" +#include "../LocaleESHook.h" // <-- IMPORTANTE: hook de traducción #include @@ -49,34 +50,51 @@ void DateTimeComponent::onTextChanged() std::string DateTimeComponent::getDisplayString() const { - if(std::difftime(mTime.getTime(), Utils::Time::BLANK_DATE) == 0.0) { + if (std::difftime(mTime.getTime(), Utils::Time::BLANK_DATE) == 0.0) return ""; - } - if (mDisplayRelative) { - //relative time - if(mTime.getTime() == Utils::Time::NOT_A_DATE_TIME) - return "never"; + // Modo relativo (ej.: "123 días atrás") + if (mDisplayRelative) + { + if (mTime.getTime() == Utils::Time::NOT_A_DATE_TIME) + return es_translate("NEVER"); Utils::Time::DateTime now(Utils::Time::now()); Utils::Time::Duration dur(now.getTime() - mTime.getTime()); char buf[64]; - if(dur.getDays() > 0) - sprintf(buf, "%d day%s ago", dur.getDays(), (dur.getDays() > 1) ? "s" : ""); - else if(dur.getHours() > 0) - sprintf(buf, "%d hour%s ago", dur.getHours(), (dur.getHours() > 1) ? "s" : ""); - else if(dur.getMinutes() > 0) - sprintf(buf, "%d minute%s ago", dur.getMinutes(), (dur.getMinutes() > 1) ? "s" : ""); + if (dur.getDays() > 0) + { + snprintf(buf, sizeof(buf), "%d %s", + dur.getDays(), + es_translate("DAYS AGO").c_str()); + } + else if (dur.getHours() > 0) + { + snprintf(buf, sizeof(buf), "%d %s", + dur.getHours(), + es_translate("HOURS AGO").c_str()); + } + else if (dur.getMinutes() > 0) + { + snprintf(buf, sizeof(buf), "%d %s", + dur.getMinutes(), + es_translate("MINUTES AGO").c_str()); + } else - sprintf(buf, "%d second%s ago", dur.getSeconds(), (dur.getSeconds() > 1) ? "s" : ""); + { + snprintf(buf, sizeof(buf), "%d %s", + dur.getSeconds(), + es_translate("SECONDS AGO").c_str()); + } return std::string(buf); } - if(mTime.getTime() == Utils::Time::NOT_A_DATE_TIME) - return "unknown"; + // Modo fecha normal + if (mTime.getTime() == Utils::Time::NOT_A_DATE_TIME) + return es_translate("UNKNOWN"); return Utils::Time::timeToString(mTime.getTime(), mFormat); } @@ -94,13 +112,13 @@ void DateTimeComponent::applyTheme(const std::shared_ptr& theme, cons using namespace ThemeFlags; const ThemeData::ThemeElement* elem = theme->getElement(view, element, "datetime"); - if(!elem) + if (!elem) return; - if(elem->has("displayRelative")) + if (elem->has("displayRelative")) setDisplayRelative(elem->get("displayRelative")); - if(elem->has("format")) + if (elem->has("format")) setFormat(elem->get("format")); if (properties & COLOR && elem->has("color")) @@ -112,23 +130,23 @@ void DateTimeComponent::applyTheme(const std::shared_ptr& theme, cons setRenderBackground(true); } - if(properties & ALIGNMENT && elem->has("alignment")) + if (properties & ALIGNMENT && elem->has("alignment")) { std::string str = elem->get("alignment"); - if(str == "left") + if (str == "left") setHorizontalAlignment(ALIGN_LEFT); - else if(str == "center") + else if (str == "center") setHorizontalAlignment(ALIGN_CENTER); - else if(str == "right") + else if (str == "right") setHorizontalAlignment(ALIGN_RIGHT); else - LOG(LogError) << "Unknown text alignment string: " << str; + LOG(LogError) << "Unknown text alignment string: " << str; } - if(properties & FORCE_UPPERCASE && elem->has("forceUppercase")) + if (properties & FORCE_UPPERCASE && elem->has("forceUppercase")) setUppercase(elem->get("forceUppercase")); - if(properties & LINE_SPACING && elem->has("lineSpacing")) + if (properties & LINE_SPACING && elem->has("lineSpacing")) setLineSpacing(elem->get("lineSpacing")); setFont(Font::getFromTheme(elem, properties, mFont)); diff --git a/es-core/src/components/HelpComponent.cpp b/es-core/src/components/HelpComponent.cpp index 1d7ef55291..0030f7b8c8 100644 --- a/es-core/src/components/HelpComponent.cpp +++ b/es-core/src/components/HelpComponent.cpp @@ -7,13 +7,15 @@ #include "utils/StringUtil.h" #include "Log.h" #include "Settings.h" +#include "../LocaleESHook.h" // Para es_translate() -#define OFFSET_X 12 // move the entire thing right by this amount (px) -#define OFFSET_Y 12 // move the entire thing up by this amount (px) +#define OFFSET_X 12 +#define OFFSET_Y 12 -#define ICON_TEXT_SPACING 8 // space between [icon] and [text] (px) -#define ENTRY_SPACING 16 // space between [text] and next [icon] (px) +#define ICON_TEXT_SPACING 8 +#define ENTRY_SPACING 16 +// 🔹 Mapa de íconos static const std::map ICON_PATH_MAP { { "up/down", ":/help/dpad_updown.svg" }, { "left/right", ":/help/dpad_leftright.svg" }, @@ -51,6 +53,32 @@ void HelpComponent::setStyle(const HelpStyle& style) updateGrid(); } +// 🔹 Normaliza claves como "favorites", "favs", "favorite" → "FAVORITES" +static std::string normalizeKey(const std::string& key) +{ + std::string k = Utils::String::toLower(key); + + if (k == "favorites" || k == "favs" || k == "favorite") + return "FAVORITES"; + + if (k == "back") + return "BACK"; + + if (k == "select") + return "SELECT"; + + if (k == "menu") + return "MENU"; + + if (k == "random") + return "RANDOM"; + + if (k == "choose") + return "CHOOSE"; + + return Utils::String::toUpper(key); +} + void HelpComponent::updateGrid() { if(!Settings::getInstance()->getBool("ShowHelpPrompts") || mPrompts.empty()) @@ -60,15 +88,14 @@ void HelpComponent::updateGrid() } std::shared_ptr& font = mStyle.font; - mGrid = std::make_shared(mWindow, Vector2i((int)mPrompts.size() * 4, 1)); - // [icon] [spacer1] [text] [spacer2] std::vector< std::shared_ptr > icons; std::vector< std::shared_ptr > labels; float width = 0; const float height = Math::round(font->getLetterHeight() * 1.25f); + for(auto it = mPrompts.cbegin(); it != mPrompts.cend(); it++) { auto icon = std::make_shared(mWindow); @@ -77,16 +104,26 @@ void HelpComponent::updateGrid() icon->setResize(0, height); icons.push_back(icon); - auto lbl = std::make_shared(mWindow, Utils::String::toUpper(it->second), font, mStyle.textColor); + // 🔹 Traducción corregida + std::string normalizedKey = normalizeKey(it->second); + std::string translated = es_translate(normalizedKey); + + auto lbl = std::make_shared( + mWindow, + Utils::String::toUpper(translated), + font, + mStyle.textColor + ); labels.push_back(lbl); width += icon->getSize().x() + lbl->getSize().x() + ICON_TEXT_SPACING + ENTRY_SPACING; } mGrid->setSize(width, height); + for(unsigned int i = 0; i < 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 + 1, ICON_TEXT_SPACING / width); mGrid->setColWidthPerc(col + 2, labels.at(i)->getSize().x() / width); @@ -96,8 +133,6 @@ void HelpComponent::updateGrid() } mGrid->setPosition(Vector3f(mStyle.position.x(), mStyle.position.y(), 0.0f)); - //mGrid->setPosition(OFFSET_X, Renderer::getScreenHeight() - mGrid->getSize().y() - OFFSET_Y); - mGrid->setOrigin(mStyle.origin); } std::shared_ptr HelpComponent::getIconTexture(const char* name) @@ -114,11 +149,11 @@ std::shared_ptr HelpComponent::getIconTexture(const char* name) } if(!ResourceManager::getInstance()->fileExists(pathLookup->second)) { - LOG(LogError) << "Help icon \"" << name << "\" - corresponding image file \"" << pathLookup->second << "\" misisng!"; + LOG(LogError) << "Missing icon file \"" << pathLookup->second << "\"!"; return nullptr; } - std::shared_ptr tex = TextureResource::get(pathLookup->second); + auto tex = TextureResource::get(pathLookup->second); mIconCache[std::string(name)] = tex; return tex; } @@ -128,9 +163,7 @@ void HelpComponent::setOpacity(unsigned char opacity) GuiComponent::setOpacity(opacity); for(unsigned int i = 0; i < mGrid->getChildCount(); i++) - { mGrid->getChild(i)->setOpacity(opacity); - } } void HelpComponent::render(const Transform4x4f& parentTrans) From 951d0545979007f5cf77d0ca8ac5a482adbaf6eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Ren=C3=A9=20Caballero=20Marquez?= Date: Sat, 3 Jan 2026 21:44:55 -0300 Subject: [PATCH 10/10] ES-X --- .../src/components/MarqueeTextComponent.cpp | 104 + es-core/src/components/MarqueeTextComponent.h | 56 + es-core/src/components/MenuComponent.cpp | 8 +- es-core/src/components/TextComponent.cpp | 456 +- es-core/src/components/TextComponent.h | 8 + es-core/src/guis/GuiMsgBox.cpp | 199 +- es-core/src/resources/ThemeSounds.cpp | 14 + es-core/src/resources/ThemeSounds.h | 30 + es-core/src/resources/fonts/players.ttf | Bin 0 -> 32184 bytes lang/es.ini | 315 + lang/fr.ini | 314 + lang/gn_PY.ini | 315 + lang/it.ini | 315 + ...\346\227\245\346\234\254\350\252\236).ini" | 248 + lang/pt.ini | 315 + resources/1splash.svg | 5114 +++++++++++++++++ resources/ESX_Guide.pdf | Bin 0 -> 340319 bytes resources/frame.png | Bin 1729 -> 5375 bytes resources/frame_dark.png | Bin 0 -> 5164 bytes resources/highlight_blue.png | Bin 0 -> 4362 bytes resources/highlight_dark.png | Bin 0 -> 4359 bytes resources/splash.png | Bin 0 -> 289005 bytes resources/splash.svg | 260 +- 23 files changed, 7722 insertions(+), 349 deletions(-) create mode 100644 es-core/src/components/MarqueeTextComponent.cpp create mode 100644 es-core/src/components/MarqueeTextComponent.h create mode 100644 es-core/src/resources/ThemeSounds.cpp create mode 100644 es-core/src/resources/ThemeSounds.h create mode 100755 es-core/src/resources/fonts/players.ttf create mode 100644 lang/es.ini create mode 100644 lang/fr.ini create mode 100644 lang/gn_PY.ini create mode 100644 lang/it.ini create mode 100644 "lang/ja (\346\227\245\346\234\254\350\252\236).ini" create mode 100644 lang/pt.ini create mode 100644 resources/1splash.svg create mode 100644 resources/ESX_Guide.pdf create mode 100644 resources/frame_dark.png create mode 100644 resources/highlight_blue.png create mode 100644 resources/highlight_dark.png create mode 100644 resources/splash.png diff --git a/es-core/src/components/MarqueeTextComponent.cpp b/es-core/src/components/MarqueeTextComponent.cpp new file mode 100644 index 0000000000..340cecb55f --- /dev/null +++ b/es-core/src/components/MarqueeTextComponent.cpp @@ -0,0 +1,104 @@ +#include "components/MarqueeTextComponent.h" + +#include "renderers/Renderer.h" +#include "Window.h" + +MarqueeTextComponent::MarqueeTextComponent( + Window* window, + const std::string& text, + const std::shared_ptr& font, + unsigned int color, + Alignment align) + : TextComponent(window, text, font, color, align), + mScrollEnabled(true), + mScrollingActive(false), + mScrollSpeed(40.0f), // velocidad base + mScrollGap(40.0f), // gap al final + mScrollOffset(0.0f), + mScrollDelayMs(1200), // 1.2s de espera inicial + mElapsedMs(0) +{ +} + +void MarqueeTextComponent::setText(const std::string& text) +{ + // llamamos al TextComponent original + TextComponent::setText(text); + + // reiniciamos el estado del scroll + mScrollOffset = 0.0f; + mElapsedMs = 0; + mScrollingActive = false; +} + +void MarqueeTextComponent::update(int deltaTime) +{ + // Primero dejamos que TextComponent haga lo suyo (cacheo, etc.) + TextComponent::update(deltaTime); + + if (!mScrollEnabled) + return; + + // Acumulamos tiempo + mElapsedMs += deltaTime; + + // Activamos scroll después del delay inicial + if (!mScrollingActive) + { + if (mElapsedMs >= mScrollDelayMs) + { + mScrollingActive = true; + mElapsedMs = 0; + } + else + { + return; + } + } + + // deltaTime viene en ms → pasamos a segundos + const float dt = deltaTime / 1000.0f; + mScrollOffset += mScrollSpeed * dt; + + // Usamos un ciclo simple basado en el ancho del componente + gap fijo + const float boxWidth = getSize().x(); + const float resetPoint = boxWidth + mScrollGap; + + if (boxWidth > 0.0f && mScrollOffset > resetPoint) + { + mScrollOffset = 0.0f; + mElapsedMs = 0; + mScrollingActive = false; // volvemos a aplicar delay antes del próximo ciclo + } +} + +void MarqueeTextComponent::render(const Transform4x4f& parentTrans) +{ + if (!isVisible()) + return; + + // Transform base del componente (sin scroll) + Transform4x4f trans = parentTrans * getTransform(); + Vector3f pos = trans.translation(); + Vector2f size = getSize(); + + // Definimos el área de recorte igual que otros componentes + Renderer::pushClipRect( + Vector2i((int)pos.x(), (int)pos.y()), + Vector2i((int)size.x(), (int)size.y())); + + // Creamos un parentTrans con el offset de scroll aplicado + Transform4x4f parentWithScroll = parentTrans; + + if (mScrollingActive) + { + // movemos el texto hacia la izquierda dentro del cuadro + parentWithScroll.translate(Vector3f(-mScrollOffset, 0.0f, 0.0f)); + } + + // Dejamos que TextComponent haga el render normal, + // pero usando el parentTrans desplazado + TextComponent::render(parentWithScroll); + + Renderer::popClipRect(); +} diff --git a/es-core/src/components/MarqueeTextComponent.h b/es-core/src/components/MarqueeTextComponent.h new file mode 100644 index 0000000000..f4fa0465f5 --- /dev/null +++ b/es-core/src/components/MarqueeTextComponent.h @@ -0,0 +1,56 @@ +#pragma once + +#include "components/TextComponent.h" + +// Componente de texto con scroll horizontal sencillo. +// No rompe compatibilidad con temas existentes y usa la misma API básica de TextComponent. +// +// NOTA: Esta versión no intenta calcular el ancho real del texto ni tocar mTextCache. +// Solo desplaza el texto hacia la izquierda dentro del área del componente, +// con un delay inicial y un bucle simple. +// + +class MarqueeTextComponent : public TextComponent +{ +public: + MarqueeTextComponent( + Window* window, + const std::string& text, + const std::shared_ptr& font, + unsigned int color, + Alignment align = ALIGN_LEFT); + + // Redefinimos setText solo para reiniciar el estado interno del scroll. + // No usamos 'override' porque en tu TextComponent setText NO es virtual. + void setText(const std::string& text); + + // Activar / desactivar scroll + void setScrollEnabled(bool enabled) { mScrollEnabled = enabled; } + bool getScrollEnabled() const { return mScrollEnabled; } + + // Velocidad en píxeles por segundo (por defecto 40) + void setScrollSpeed(float speed) { mScrollSpeed = speed; } + float getScrollSpeed() const { return mScrollSpeed; } + + // Retraso antes de empezar a moverse (en ms) + void setScrollDelay(int delayMs) { mScrollDelayMs = delayMs; } + int getScrollDelay() const { return mScrollDelayMs; } + + // Espacio "virtual" después del texto antes de reiniciar (en píxeles) + void setScrollGap(float gap) { mScrollGap = gap; } + float getScrollGap() const { return mScrollGap; } + + void update(int deltaTime) override; + void render(const Transform4x4f& parentTrans) override; + +private: + bool mScrollEnabled; // ¿scroll activado? + bool mScrollingActive; // ¿estamos desplazando ahora? + + float mScrollSpeed; // px/seg + float mScrollGap; // gap fijo antes de reiniciar + float mScrollOffset; // desplazamiento actual en px + + int mScrollDelayMs; // delay inicial + int mElapsedMs; // tiempo acumulado desde que se mostró +}; diff --git a/es-core/src/components/MenuComponent.cpp b/es-core/src/components/MenuComponent.cpp index 2e60b59459..e347bb32ba 100644 --- a/es-core/src/components/MenuComponent.cpp +++ b/es-core/src/components/MenuComponent.cpp @@ -1,6 +1,7 @@ #include "components/MenuComponent.h" #include "components/ButtonComponent.h" +#include "Settings.h" #define BUTTON_GRID_VERT_PADDING 32 #define BUTTON_GRID_HORIZ_PADDING 10 @@ -13,12 +14,15 @@ MenuComponent::MenuComponent(Window* window, const char* title, const std::share addChild(&mBackground); addChild(&mGrid); - mBackground.setImagePath(":/frame.png"); + // Modo claro/oscuro para el fondo del menú + bool darkMenu = Settings::getInstance()->getBool("MenuDark"); + mBackground.setImagePath(darkMenu ? ":/frame_dark.png" : ":/frame.png"); // set up title mTitle = std::make_shared(mWindow); mTitle->setHorizontalAlignment(ALIGN_CENTER); - mTitle->setColor(0x555555FF); + // Título blanco en modo oscuro, gris en modo claro + mTitle->setColor(darkMenu ? 0xFFFFFFFF : 0x555555FF); setTitle(title, titleFont); mGrid.setEntry(mTitle, Vector2i(0, 0), false); diff --git a/es-core/src/components/TextComponent.cpp b/es-core/src/components/TextComponent.cpp index 7a8f52cdf3..4cd27ad7c7 100644 --- a/es-core/src/components/TextComponent.cpp +++ b/es-core/src/components/TextComponent.cpp @@ -5,295 +5,317 @@ #include "Settings.h" TextComponent::TextComponent(Window* window) : GuiComponent(window), - mFont(Font::get(FONT_SIZE_MEDIUM)), mUppercase(false), mColor(0x000000FF), mAutoCalcExtent(true, true), - mHorizontalAlignment(ALIGN_LEFT), mVerticalAlignment(ALIGN_CENTER), mLineSpacing(1.5f), mBgColor(0), - mRenderBackground(false) + mFont(Font::get(FONT_SIZE_MEDIUM)), mUppercase(false), + mColor(0xFFFFFFFF), mBgColor(0x00000000), + mStrokeColor(0x000000FF), mColorOpacity(0xFF), + mBgColorOpacity(0x00), mStrokeColorOpacity(0xFF), + mRenderBackground(false), + mStrokeSize(0.0f), // *** SIN BORDE POR DEFECTO *** + mAutoCalcExtent(true, true), + mHorizontalAlignment(ALIGN_LEFT), + mVerticalAlignment(ALIGN_CENTER), + mLineSpacing(1.5f) { } -TextComponent::TextComponent(Window* window, const std::string& text, const std::shared_ptr& font, unsigned int color, Alignment align, - Vector3f pos, Vector2f size, unsigned int bgcolor) : GuiComponent(window), - mFont(NULL), mUppercase(false), mColor(0x000000FF), mAutoCalcExtent(true, true), - mHorizontalAlignment(align), mVerticalAlignment(ALIGN_CENTER), mLineSpacing(1.5f), mBgColor(0), - mRenderBackground(false) +TextComponent::TextComponent(Window* window, const std::string& text, const std::shared_ptr& font, + unsigned int color, Alignment align, Vector3f pos, Vector2f size, unsigned int bgcolor) + : GuiComponent(window), + mFont(nullptr), mUppercase(false), mColor(color), mBgColor(bgcolor), + mStrokeColor(0x000000FF), mColorOpacity(color & 0xFF), + mBgColorOpacity(bgcolor & 0xFF), mStrokeColorOpacity(0xFF), + mRenderBackground(bgcolor != 0), // activar si hay fondo + mAutoCalcExtent(true, true), + mHorizontalAlignment(align), mVerticalAlignment(ALIGN_CENTER), + mLineSpacing(1.5f), + mStrokeSize(0.0f) // sin borde { - setFont(font); - setColor(color); - setBackgroundColor(bgcolor); - setText(text); - setPosition(pos); - setSize(size); + setFont(font); + setText(text); + setPosition(pos); + setSize(size); } void TextComponent::onSizeChanged() { - mAutoCalcExtent = Vector2i((getSize().x() == 0), (getSize().y() == 0)); - onTextChanged(); + mAutoCalcExtent = Vector2i((getSize().x() == 0), (getSize().y() == 0)); + onTextChanged(); } void TextComponent::setFont(const std::shared_ptr& font) { - mFont = font; - onTextChanged(); + mFont = font; + onTextChanged(); } -// Set the color of the font/text void TextComponent::setColor(unsigned int color) { - mColor = color; - mColorOpacity = mColor & 0x000000FF; - onColorChanged(); + mColor = color; + mColorOpacity = (color & 0xFF); + onColorChanged(); } -// Set the color of the background box void TextComponent::setBackgroundColor(unsigned int color) { - mBgColor = color; - mBgColorOpacity = mBgColor & 0x000000FF; + mBgColor = color; + mBgColorOpacity = (color & 0xFF); + + // activar fondo automáticamente si el color no es transparente + mRenderBackground = (mBgColorOpacity > 0); } void TextComponent::setRenderBackground(bool render) { - mRenderBackground = render; + mRenderBackground = render; +} + +void TextComponent::setTextStrokeColor(unsigned int color) +{ + mStrokeColor = color; + mStrokeColorOpacity = (color & 0xFF); +} + +void TextComponent::setTextStrokeSize(float size) +{ + mStrokeSize = size; } -// Scale the opacity void TextComponent::setOpacity(unsigned char opacity) { - // This method is mostly called to do fading in-out of the Text component element. - // Therefore, we assume here that opacity is a fractional value (expressed as an int 0-255), - // of the opacity originally set with setColor() or setBackgroundColor(). + unsigned char o = (unsigned char)((float)opacity / 255.f * (float)mColorOpacity); + mColor = (mColor & 0xFFFFFF00) | o; - unsigned char o = (unsigned char)((float)opacity / 255.f * (float) mColorOpacity); - mColor = (mColor & 0xFFFFFF00) | (unsigned char) o; + unsigned char bgo = (unsigned char)((float)opacity / 255.f * (float)mBgColorOpacity); + mBgColor = (mBgColor & 0xFFFFFF00) | bgo; - unsigned char bgo = (unsigned char)((float)opacity / 255.f * (float)mBgColorOpacity); - mBgColor = (mBgColor & 0xFFFFFF00) | (unsigned char)bgo; + unsigned char so = (unsigned char)((float)opacity / 255.f * (float)mStrokeColorOpacity); + mStrokeColor = (mStrokeColor & 0xFFFFFF00) | so; - onColorChanged(); + onColorChanged(); - GuiComponent::setOpacity(opacity); + GuiComponent::setOpacity(opacity); } unsigned char TextComponent::getOpacity() const { - return mColor & 0x000000FF; + return (mColor & 0xFF); } void TextComponent::setText(const std::string& text) { - mText = text; - onTextChanged(); + mText = text; + onTextChanged(); } void TextComponent::setUppercase(bool uppercase) { - mUppercase = uppercase; - onTextChanged(); + mUppercase = uppercase; + onTextChanged(); } -void TextComponent::render(const Transform4x4f& parentTrans) +void TextComponent::setHorizontalAlignment(Alignment align) { - if (!isVisible()) - return; - - Transform4x4f trans = parentTrans * getTransform(); - - if (mRenderBackground) - { - Renderer::setMatrix(trans); - Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), mBgColor, mBgColor); - } - - if(mTextCache) - { - const Vector2f& textSize = mTextCache->metrics.size; - float yOff = 0; - switch(mVerticalAlignment) - { - case ALIGN_TOP: - yOff = 0; - break; - case ALIGN_BOTTOM: - yOff = (getSize().y() - textSize.y()); - break; - case ALIGN_CENTER: - yOff = (getSize().y() - textSize.y()) / 2.0f; - break; - default: - LOG(LogError) << "Unknown vertical alignment: " << mVerticalAlignment; - break; - } - Vector3f off(0, yOff, 0); - - if(Settings::getInstance()->getBool("DebugText")) - { - // draw the "textbox" area, what we are aligned within - Renderer::setMatrix(trans); - Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0xFF000033, 0xFF000033); - } - - trans.translate(off); - Renderer::setMatrix(trans); - - // draw the text area, where the text actually is going - if(Settings::getInstance()->getBool("DebugText")) - { - switch(mHorizontalAlignment) - { - case ALIGN_LEFT: - Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x(), mTextCache->metrics.size.y(), 0x00000033, 0x00000033); - break; - case ALIGN_CENTER: - Renderer::drawRect((mSize.x() - mTextCache->metrics.size.x()) / 2.0f, 0.0f, mTextCache->metrics.size.x(), mTextCache->metrics.size.y(), 0x00000033, 0x00000033); - break; - case ALIGN_RIGHT: - Renderer::drawRect(mSize.x() - mTextCache->metrics.size.x(), 0.0f, mTextCache->metrics.size.x(), mTextCache->metrics.size.y(), 0x00000033, 0x00000033); - break; - default: - LOG(LogError) << "Unknown horizontal alignment: " << mHorizontalAlignment; - break; - } - } - mFont->renderTextCache(mTextCache.get()); - } + mHorizontalAlignment = align; + onTextChanged(); } -std::string TextComponent::calculateExtent(bool allow_wrapping) +void TextComponent::setVerticalAlignment(Alignment align) { - std::string text = mUppercase ? Utils::String::toUpper(mText) : mText; - if(mAutoCalcExtent.x()) - { - mSize = mFont->sizeText(text, mLineSpacing); - }else if(mAutoCalcExtent.y() || allow_wrapping) - // usually a textcomponent wraps only when x > 0 and y == 0 in size (see TextComponent.h). - // The extra flag allow_wrapping does wrapping if an textcomponent has x > 0 and y > height of - // one line (calculated by fontsize and line spacing). - // Some themes rely on this wrap functionality while having an fixed y (y>0) in . - { - text = mFont->wrapText(text, getSize().x()); - if (mAutoCalcExtent.y()) { - // only resize when y was 0 before - // otherwise leave y value as defined before (i.e. theme value) - mSize.y() = mFont->sizeText(text, mLineSpacing).y(); - } - } - return text; + mVerticalAlignment = align; } -void TextComponent::onTextChanged() +void TextComponent::setLineSpacing(float spacing) { - if(!mFont || mText.empty()) - { - mTextCache.reset(); - return; - } - - std::shared_ptr f = mFont; - std::string text = calculateExtent(mSize.y() > f->getHeight(mLineSpacing)); - const bool oneLiner = mSize.y() > 0 && mSize.y() <= f->getHeight(mLineSpacing); - - if(oneLiner) - { - bool addAbbrev = false; - size_t newline = text.find('\n'); - text = text.substr(0, newline); // single line of text - stop at the first newline since it'll mess everything up - Vector2f size = f->sizeText(text); - addAbbrev = newline != std::string::npos || size.x() > mSize.x(); - - if(addAbbrev) - { - // abbreviate text - const std::string abbrev = "..."; - Vector2f abbrevSize = f->sizeText(abbrev); - - while(text.size() && size.x() + abbrevSize.x() > mSize.x()) - { - size_t newSize = Utils::String::prevCursor(text, text.size()); - text.erase(newSize, text.size() - newSize); - size = f->sizeText(text); - } - text.append(abbrev); - } - } - mTextCache = std::shared_ptr(f->buildTextCache(text, Vector2f(0, 0), (mColor >> 8 << 8) | mOpacity, mSize.x(), mHorizontalAlignment, mLineSpacing)); + mLineSpacing = spacing; + onTextChanged(); } -void TextComponent::onColorChanged() +void TextComponent::setValue(const std::string& value) { - if(mTextCache) - { - mTextCache->setColor(mColor); - } + setText(value); } -void TextComponent::setHorizontalAlignment(Alignment align) +std::string TextComponent::getValue() const { - mHorizontalAlignment = align; - onTextChanged(); + return mText; } -void TextComponent::setVerticalAlignment(Alignment align) +void TextComponent::render(const Transform4x4f& parentTrans) { - mVerticalAlignment = align; + if (!isVisible() || !mFont) + return; + + Transform4x4f trans = parentTrans * getTransform(); + Renderer::setMatrix(trans); + + // =============================== + // FONDO DEL TEXTO SI CORRESPONDE + // =============================== + if (mRenderBackground && (mBgColorOpacity > 0)) + { + Renderer::drawRect(0, 0, mSize.x(), mSize.y(), mBgColor, mBgColor); + } + + if (!mTextCache) + return; + + const Vector2f& size = mTextCache->metrics.size; + + float yOff = 0.0f; + switch (mVerticalAlignment) + { + case ALIGN_TOP: yOff = 0; break; + case ALIGN_BOTTOM: yOff = (getSize().y() - size.y()); break; + case ALIGN_CENTER: yOff = (getSize().y() - size.y()) / 2.0f; break; + } + + Transform4x4f baseTrans = trans; + baseTrans.translate(Vector3f(0.0f, yOff, 0.0f)); + + // =============================== + // STROKE / BORDE + // =============================== + if (mStrokeSize > 0.0f && (mStrokeColorOpacity > 0)) + { + unsigned int originalColor = mColor; + + mTextCache->setColor(mStrokeColor); + + const float s = mStrokeSize; + + const Vector2f offsets[4] = { + Vector2f(-s, 0), + Vector2f( s, 0), + Vector2f(0, -s), + Vector2f(0, s) + }; + + for (int i = 0; i < 4; i++) + { + Transform4x4f strokeTrans = baseTrans; + strokeTrans.translate(Vector3f(offsets[i].x(), offsets[i].y(), 0)); + Renderer::setMatrix(strokeTrans); + mFont->renderTextCache(mTextCache.get()); + } + + mTextCache->setColor(originalColor); + } + + // =============================== + // TEXTO PRINCIPAL + // =============================== + Renderer::setMatrix(baseTrans); + mFont->renderTextCache(mTextCache.get()); } -void TextComponent::setLineSpacing(float spacing) +std::string TextComponent::calculateExtent(bool allow_wrapping) { - mLineSpacing = spacing; - onTextChanged(); + std::string text = mUppercase ? Utils::String::toUpper(mText) : mText; + + if (mAutoCalcExtent.x()) + { + mSize = mFont->sizeText(text, mLineSpacing); + } + else if (mAutoCalcExtent.y() || allow_wrapping) + { + text = mFont->wrapText(text, getSize().x()); + if (mAutoCalcExtent.y()) + mSize.y() = mFont->sizeText(text, mLineSpacing).y(); + } + + return text; } -void TextComponent::setValue(const std::string& value) +void TextComponent::onTextChanged() { - setText(value); + if (!mFont || mText.empty()) + { + mTextCache.reset(); + return; + } + + std::string text = calculateExtent(mSize.y() > mFont->getHeight(mLineSpacing)); + const bool oneLine = (mSize.y() > 0 && mSize.y() <= mFont->getHeight(mLineSpacing)); + + if (oneLine) + { + bool shorten = false; + size_t newline = text.find('\n'); + text = text.substr(0, newline); + Vector2f ts = mFont->sizeText(text); + shorten = newline != std::string::npos || ts.x() > mSize.x(); + + if (shorten) + { + const std::string dots = "..."; + Vector2f dotsSize = mFont->sizeText(dots); + + while (text.size() && (ts.x() + dotsSize.x() > mSize.x())) + { + size_t newSize = Utils::String::prevCursor(text, text.size()); + text.erase(newSize); + ts = mFont->sizeText(text); + } + text.append(dots); + } + } + + mTextCache = std::shared_ptr( + mFont->buildTextCache( + text, + Vector2f(0, 0), + (mColor >> 8 << 8) | mOpacity, + mSize.x(), + mHorizontalAlignment, + mLineSpacing + )); } -std::string TextComponent::getValue() const +void TextComponent::onColorChanged() { - return mText; + if (mTextCache) + mTextCache->setColor(mColor); } -void TextComponent::applyTheme(const std::shared_ptr& theme, const std::string& view, const std::string& element, unsigned int properties) +void TextComponent::applyTheme(const std::shared_ptr& theme, + const std::string& view, const std::string& element, unsigned int properties) { - GuiComponent::applyTheme(theme, view, element, properties); - - using namespace ThemeFlags; - - const ThemeData::ThemeElement* elem = theme->getElement(view, element, "text"); - if(!elem) - return; - - if (properties & COLOR && elem->has("color")) - setColor(elem->get("color")); - - setRenderBackground(false); - if (properties & COLOR && elem->has("backgroundColor")) { - setBackgroundColor(elem->get("backgroundColor")); - setRenderBackground(true); - } - - if(properties & ALIGNMENT && elem->has("alignment")) - { - std::string str = elem->get("alignment"); - if(str == "left") - setHorizontalAlignment(ALIGN_LEFT); - else if(str == "center") - setHorizontalAlignment(ALIGN_CENTER); - else if(str == "right") - setHorizontalAlignment(ALIGN_RIGHT); - else - LOG(LogError) << "Unknown text alignment string: " << str; - } - - if(properties & TEXT && elem->has("text")) - setText(elem->get("text")); - - if(properties & FORCE_UPPERCASE && elem->has("forceUppercase")) - setUppercase(elem->get("forceUppercase")); - - if(properties & LINE_SPACING && elem->has("lineSpacing")) - setLineSpacing(elem->get("lineSpacing")); - - setFont(Font::getFromTheme(elem, properties, mFont)); + GuiComponent::applyTheme(theme, view, element, properties); + + using namespace ThemeFlags; + const ThemeData::ThemeElement* elem = theme->getElement(view, element, "text"); + if (!elem) + return; + + if (properties & COLOR && elem->has("color")) + setColor(elem->get("color")); + + if (elem->has("backgroundColor")) + setBackgroundColor(elem->get("backgroundColor")); + + if (properties & ALIGNMENT && elem->has("alignment")) + { + std::string a = elem->get("alignment"); + if (a == "left") setHorizontalAlignment(ALIGN_LEFT); + else if (a == "center") setHorizontalAlignment(ALIGN_CENTER); + else if (a == "right") setHorizontalAlignment(ALIGN_RIGHT); + } + + if (properties & TEXT && elem->has("text")) + setText(elem->get("text")); + + if (properties & FORCE_UPPERCASE && elem->has("forceUppercase")) + setUppercase(elem->get("forceUppercase")); + + if (properties & LINE_SPACING && elem->has("lineSpacing")) + setLineSpacing(elem->get("lineSpacing")); + + // STROKE (borde de texto) + if (elem->has("textStrokeColor")) + setTextStrokeColor(elem->get("textStrokeColor")); + + if (elem->has("textStrokeSize")) + setTextStrokeSize(elem->get("textStrokeSize")); + + setFont(Font::getFromTheme(elem, properties, mFont)); } diff --git a/es-core/src/components/TextComponent.h b/es-core/src/components/TextComponent.h index c464ef1140..919f8a222f 100644 --- a/es-core/src/components/TextComponent.h +++ b/es-core/src/components/TextComponent.h @@ -30,6 +30,10 @@ class TextComponent : public GuiComponent void setBackgroundColor(unsigned int color); void setRenderBackground(bool render); + // Stroke / outline del texto + void setTextStrokeColor(unsigned int color); + void setTextStrokeSize(float size); + void render(const Transform4x4f& parentTrans) override; std::string getValue() const override; @@ -55,8 +59,10 @@ class TextComponent : public GuiComponent unsigned int mColor; unsigned int mBgColor; + unsigned int mStrokeColor; unsigned char mColorOpacity; unsigned char mBgColorOpacity; + unsigned char mStrokeColorOpacity; bool mRenderBackground; bool mUppercase; @@ -65,6 +71,8 @@ class TextComponent : public GuiComponent Alignment mHorizontalAlignment; Alignment mVerticalAlignment; float mLineSpacing; + + float mStrokeSize; }; #endif // ES_CORE_COMPONENTS_TEXT_COMPONENT_H diff --git a/es-core/src/guis/GuiMsgBox.cpp b/es-core/src/guis/GuiMsgBox.cpp index 9db9ffff11..8007e76ea9 100644 --- a/es-core/src/guis/GuiMsgBox.cpp +++ b/es-core/src/guis/GuiMsgBox.cpp @@ -1,110 +1,145 @@ +// es-core/src/guis/GuiMsgBox.cpp + #include "guis/GuiMsgBox.h" #include "components/ButtonComponent.h" #include "components/MenuComponent.h" +#include "../LocaleESHook.h" // Para es_translate #define HORIZONTAL_PADDING_PX 20 GuiMsgBox::GuiMsgBox(Window* window, const std::string& text, - const std::string& name1, const std::function& func1, - const std::string& name2, const std::function& func2, - const std::string& name3, const std::function& func3) : GuiComponent(window), - mBackground(window, ":/frame.png"), mGrid(window, Vector2i(1, 2)) + const std::string& name1, const std::function& func1, + const std::string& name2, const std::function& func2, + const std::string& name3, const std::function& func3) + : GuiComponent(window) + , mBackground(window, ":/frame.png") + , mGrid(window, Vector2i(1, 2)) { - float width = Renderer::getScreenWidth() * 0.6f; // max width - float minWidth = Renderer::getScreenWidth() * 0.3f; // minimum width - - mMsg = std::make_shared(mWindow, text, Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER); - mGrid.setEntry(mMsg, Vector2i(0, 0), false, false); - - // create the buttons - mButtons.push_back(std::make_shared(mWindow, name1, name1, std::bind(&GuiMsgBox::deleteMeAndCall, this, func1))); - if(!name2.empty()) - mButtons.push_back(std::make_shared(mWindow, name2, name3, std::bind(&GuiMsgBox::deleteMeAndCall, this, func2))); - if(!name3.empty()) - mButtons.push_back(std::make_shared(mWindow, name3, name3, std::bind(&GuiMsgBox::deleteMeAndCall, this, func3))); - - // set accelerator automatically (button to press when "b" is pressed) - if(mButtons.size() == 1) - { - mAcceleratorFunc = mButtons.front()->getPressedFunc(); - }else{ - for(auto it = mButtons.cbegin(); it != mButtons.cend(); it++) - { - if(Utils::String::toUpper((*it)->getText()) == "OK" || Utils::String::toUpper((*it)->getText()) == "NO") - { - mAcceleratorFunc = (*it)->getPressedFunc(); - break; - } - } - } - - // put the buttons into a ComponentGrid - mButtonGrid = makeButtonGrid(mWindow, mButtons); - mGrid.setEntry(mButtonGrid, Vector2i(0, 1), true, false, Vector2i(1, 1), GridFlags::BORDER_TOP); - - // decide final width - if(mMsg->getSize().x() < width && mButtonGrid->getSize().x() < width) - { - // mMsg and buttons are narrower than width - width = Math::max(mButtonGrid->getSize().x(), mMsg->getSize().x()); - width = Math::max(width, minWidth); - } - - // now that we know width, we can find height - mMsg->setSize(width, 0); // mMsg->getSize.y() now returns the proper length - const float msgHeight = Math::max(Font::get(FONT_SIZE_LARGE)->getHeight(), mMsg->getSize().y()*1.225f); - setSize(width + HORIZONTAL_PADDING_PX*2, msgHeight + mButtonGrid->getSize().y()); - - // center for good measure - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f, (Renderer::getScreenHeight() - mSize.y()) / 2.0f); - - addChild(&mBackground); - addChild(&mGrid); + float width = Renderer::getScreenWidth() * 0.6f; // max width + float minWidth = Renderer::getScreenWidth() * 0.3f; // minimum width + + // Texto del mensaje traducido + mMsg = std::make_shared( + mWindow, + es_translate(text), + Font::get(FONT_SIZE_MEDIUM), + 0x777777FF, + ALIGN_CENTER + ); + mGrid.setEntry(mMsg, Vector2i(0, 0), false, false); + + // Botones traducidos + if (!name1.empty()) + mButtons.push_back(std::make_shared( + mWindow, + es_translate(name1), + name1, + std::bind(&GuiMsgBox::deleteMeAndCall, this, func1))); + + if (!name2.empty()) + mButtons.push_back(std::make_shared( + mWindow, + es_translate(name2), + name2, + std::bind(&GuiMsgBox::deleteMeAndCall, this, func2))); + + if (!name3.empty()) + mButtons.push_back(std::make_shared( + mWindow, + es_translate(name3), + name3, + std::bind(&GuiMsgBox::deleteMeAndCall, this, func3))); + + // Botón “acelerador” (B / Enter / etc.) + if (mButtons.size() == 1) + { + mAcceleratorFunc = mButtons.front()->getPressedFunc(); + } + else + { + for (auto& btn : mButtons) + { + std::string txtUpper = Utils::String::toUpper(btn->getText()); + if (txtUpper == es_translate("OK") || txtUpper == es_translate("NO") || + txtUpper == "OK" || txtUpper == "NO") + { + mAcceleratorFunc = btn->getPressedFunc(); + break; + } + } + } + + // Grid de botones + mButtonGrid = makeButtonGrid(mWindow, mButtons); + mGrid.setEntry(mButtonGrid, Vector2i(0, 1), true, false, + Vector2i(1, 1), GridFlags::BORDER_TOP); + + // Ajuste de ancho + if (mMsg->getSize().x() < width && mButtonGrid->getSize().x() < width) + { + width = Math::max(mButtonGrid->getSize().x(), mMsg->getSize().x()); + width = Math::max(width, minWidth); + } + + mMsg->setSize(width, 0); + const float msgHeight = Math::max( + Font::get(FONT_SIZE_LARGE)->getHeight(), + mMsg->getSize().y() * 1.225f + ); + + setSize(width + HORIZONTAL_PADDING_PX * 2, + msgHeight + mButtonGrid->getSize().y()); + + setPosition( + (Renderer::getScreenWidth() - mSize.x()) / 2.0f, + (Renderer::getScreenHeight() - mSize.y()) / 2.0f + ); + + addChild(&mBackground); + addChild(&mGrid); } bool GuiMsgBox::input(InputConfig* config, Input input) { - // special case for when GuiMsgBox comes up to report errors before anything has been configured - if(config->getDeviceId() == DEVICE_KEYBOARD && !config->isConfigured() && input.value && - (input.id == SDLK_RETURN || input.id == SDLK_ESCAPE || input.id == SDLK_SPACE)) - { - mAcceleratorFunc(); - return true; - } - - if(mAcceleratorFunc && config->isMappedTo("b", input) && input.value != 0) - { - mAcceleratorFunc(); - return true; - } - - return GuiComponent::input(config, input); + if (config->getDeviceId() == DEVICE_KEYBOARD && !config->isConfigured() && + input.value && + (input.id == SDLK_RETURN || input.id == SDLK_ESCAPE || input.id == SDLK_SPACE)) + { + if (mAcceleratorFunc) + mAcceleratorFunc(); + return true; + } + + if (mAcceleratorFunc && config->isMappedTo("b", input) && input.value != 0) + { + mAcceleratorFunc(); + return true; + } + + return GuiComponent::input(config, input); } void GuiMsgBox::onSizeChanged() { - mGrid.setSize(mSize); - mGrid.setRowHeightPerc(1, mButtonGrid->getSize().y() / mSize.y()); + mGrid.setSize(mSize); + mGrid.setRowHeightPerc(1, mButtonGrid->getSize().y() / mSize.y()); - // update messagebox size - mMsg->setSize(mSize.x() - HORIZONTAL_PADDING_PX*2, mGrid.getRowHeight(0)); - mGrid.onSizeChanged(); + mMsg->setSize(mSize.x() - HORIZONTAL_PADDING_PX * 2, mGrid.getRowHeight(0)); + mGrid.onSizeChanged(); - mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); + mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); } void GuiMsgBox::deleteMeAndCall(const std::function& func) { - auto funcCopy = func; - delete this; - - if(funcCopy) - funcCopy(); - + auto funcCopy = func; + delete this; + if (funcCopy) + funcCopy(); } std::vector GuiMsgBox::getHelpPrompts() { - return mGrid.getHelpPrompts(); + return mGrid.getHelpPrompts(); } diff --git a/es-core/src/resources/ThemeSounds.cpp b/es-core/src/resources/ThemeSounds.cpp new file mode 100644 index 0000000000..334f4f6fa6 --- /dev/null +++ b/es-core/src/resources/ThemeSounds.cpp @@ -0,0 +1,14 @@ +#include "ThemeSounds.h" +#include "ThemeData.h" + +// De momento no hacemos nada aquí porque la implementación está en el header +// Solo dejamos las definiciones vacías para que el linker esté contento +void ThemeSounds::loadFrom(const ThemeData& /*theme*/) +{ + // En el futuro: leer rutas de sonidos desde ThemeData y guardarlas en estáticos +} + +void ThemeSounds::play(ThemeSoundId /*id*/) +{ + // En el futuro: usar AudioManager/Sound para reproducir los sonidos cargados +} diff --git a/es-core/src/resources/ThemeSounds.h b/es-core/src/resources/ThemeSounds.h new file mode 100644 index 0000000000..9b43c17de6 --- /dev/null +++ b/es-core/src/resources/ThemeSounds.h @@ -0,0 +1,30 @@ +#pragma once + +// Declaración adelantada para evitar dependencias circulares +class ThemeData; + +// Identificadores de sonidos del tema +enum ThemeSoundId +{ + THEME_SOUND_SCROLL = 0, + THEME_SOUND_SELECT = 1, + THEME_SOUND_BACK = 2 +}; + +// Implementación mínima para que TODO compile sin romper nada. +// Más adelante se puede completar con carga real de sonidos. +class ThemeSounds +{ +public: + // Cargar sonidos desde el theme (por ahora no hace nada) + static void loadFrom(const ThemeData& /*theme*/) + { + // TODO: Implementar lógica real de lectura de sonidos desde ThemeData + } + + // Reproducir un sonido del theme (por ahora no hace nada) + static void play(ThemeSoundId /*id*/) + { + // TODO: Implementar lógica real de reproducción más adelante + } +}; diff --git a/es-core/src/resources/fonts/players.ttf b/es-core/src/resources/fonts/players.ttf new file mode 100755 index 0000000000000000000000000000000000000000..9e83c589d97dd53a5dfb9943aa6b7590261d23a8 GIT binary patch literal 32184 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 OG#}$R{g&sYt^Xf|3(Hsl literal 0 HcmV?d00001 diff --git a/lang/es.ini b/lang/es.ini new file mode 100644 index 0000000000..66a6fcf2b3 --- /dev/null +++ b/lang/es.ini @@ -0,0 +1,315 @@ +[META] +CODE=es +NAME=Español + + +[MAIN] +MAIN MENU=MENÚ PRINCIPAL +QUIT=SALIR +YES=SÍ +NO=NO +OK=ACEPTAR +BACK=VOLVER +START=INICIO +SELECT=SELECCIONAR +CANCEL=CANCELAR + + +[GAME LIST] +OPTIONS=OPCIONES +JUMP TO ...=IR A ... +LAUNCH SYSTEM SCREENSAVER=INICIAR SCREENSAVER DEL SISTEMA +SORT GAMES BY=ORDENAR JUEGOS POR +FILTER GAMELIST=FILTRAR LISTA DE JUEGOS +ADD/REMOVE GAMES TO THIS GAME COLLECTION=AGREGAR/QUITAR JUEGOS A ESTA COLECCIÓN +FINISH EDITING=FINALIZAR EDICIÓN +COLLECTION=COLECCIÓN +COLLECTIONS=COLECCIONES +ALL GAMES=TODOS LOS JUEGOS +GET NEW RANDOM GAMES=OBTENER NUEVOS JUEGOS ALEATORIOS +EDIT THIS FOLDER'S METADATA=EDITAR METADATOS DE CARPETA +EDIT THIS GAME'S METADATA=EDITAR METADATOS DE JUEGO +CLOSE=CERRAR + + +[SCRAPER] +SCRAPER=SCRAPER +SCRAPE FROM=FUENTE DE DATOS +SCRAPE RATINGS=OBTENER PUNTUACIONES +SCRAPE NOW=BUSCAR AHORA + + +[SETTINGS] +SOUND SETTINGS=SONIDO +UI SETTINGS=INTERFAZ +OTHER SETTINGS=OTRAS OPCIONES +GAME COLLECTION SETTINGS=COLECCIONES +SCREEN RESOLUTION=RESOLUCIÓN DE PANTALLA +ENABLE NAVIGATION SOUNDS=SONIDOS DE MENÚ +ENABLE VIDEO AUDIO=AUDIO DE VIDEO +SYSTEM VOLUME=VOLUMEN DEL SISTEMA +THEME SET=TEMA +TRANSITION STYLE=ESTILO DE TRANSICIÓN +CAROUSEL TRANSITIONS=TRANSICIONES DE CARRUSEL +QUICK SYSTEM SELECT=CAMBIO RÁPIDO DE SISTEMA +UI MODE=MODO DE INTERFAZ +SCREENSAVER SETTINGS=AJUSTES DE SCREENSAVER +ON-SCREEN HELP=AYUDA EN PANTALLA +ENABLE FILTERS=HABILITAR FILTROS +USE FULL SCREEN PAGING FOR LB/RB=PAGINADO COMPLETO PARA LB/RB +START ON SYSTEM=INICIAR EN SISTEMA +SHOW FRAMERATE=MOSTRAR FPS +AUDIO CARD=TARJETA DE AUDIO +AUDIO DEVICE=DISPOSITIVO DE AUDIO +POWER SAVER MODES=MODOS DE AHORRO DE ENERGÍA +VRAM LIMIT=LÍMITE DE VRAM +PARSE GAMESLISTS ONLY=SOLO LEER LISTAS DE JUEGOS +SEARCH FOR LOCAL ART=BUSCAR ARTE LOCAL +SHOW HIDDEN FILES=MOSTRAR ARCHIVOS OCULTOS +INDEX FILES DURING SCREENSAVER=INDEXAR ARCHIVOS EN SCREENSAVER +BACKGROUND INDEXING=INDEXADO EN SEGUNDO PLANO +SAVE METADATA=GUARDAR METADATOS +IGNORE ARTICLES (NAME SORT ONLY)=IGNORAR ARTÍCULOS (SOLO ORDEN POR NOMBRE) +DISABLE START MENU IN KID MODE=OCULTAR MENÚ START EN MODO NIÑOS +USE OMX PLAYER (HW ACCELERATED)=USAR OMX PLAYER (ACELERADO HW) +DARK MENU=MENÚ OSCURO +CLOCK=RELOJ +THEME OPTIONS=OPCIONES DEL TEMA + + +[LANGUAGE] +LANGUAGE=IDIOMA + + +[META] +EDIT METADATA=EDITAR METADATOS +GAME=JUEGO +FOLDER=CARPETA +NAME=NOMBRE +ENTER GAME NAME=NOMBRE DEL JUEGO +DESCRIPTION=DESCRIPCIÓN +ENTER DESCRIPTION=INGRESE UNA DESCRIPCIÓN +RELEASE DATE=FECHA DE LANZAMIENTO +ENTER RELEASE DATE=INGRESE FECHA DE LANZAMIENTO +PLAY COUNT=VECES JUGADO +ENTER NUMBER OF TIMES PLAYED=INGRESE CANTIDAD JUGADA +LAST PLAYED=ÚLTIMA VEZ JUGADO +ENTER LAST PLAYED DATE=INGRESE FECHA DE ÚLTIMA VEZ + +SCRAPE=OBTENER DATOS +SAVE=GUARDAR +CANCEL=CANCELAR +DELETE=ELIMINAR +THIS WILL DELETE THE ACTUAL GAME FILE(S)!\nARE YOU SURE?=¡ESTO ELIMINARÁ EL/LOS ARCHIVO(S) DE JUEGO!\n¿ESTÁS SEGURO? +SAVE CHANGES?=¿GUARDAR CAMBIOS? +YES=SÍ +NO=NO +BACK=VOLVER +CLOSE=CERRAR + + +[VIEW] +SYSTEM INFO=INFO DEL SISTEMA +CONFIGURATION=CONFIGURACIÓN +GAME=JUEGO +GAMES=JUEGOS +AVAILABLE=DISPONIBLES +CHOOSE=ELEGIR +SELECT=SELECCIONAR +RANDOM=ALEATORIO +LAUNCH SCREENSAVER=INICIAR SCREENSAVER + + +[GAMELIST] +GAMELIST VIEW STYLE=ESTILO DE VISTA +automatic=automático +basic=básico +detailed=detallado +video=video +grid=cuadrícula + + +[GAME DATA] +RATING=RATING +RELEASE DATE=FECHA +DEVELOPER=DEV. +PUBLISHER=DIST. +GENRE=GÉN. +PLAY COUNT=VECES +LAST PLAYED=ÚLT. VEZ +PLAYERS=JUG. +FAVORITES=FAVS. + + +[MESSAGES] +REALLY RESTART?=¿REINICIAR? +REALLY QUIT?=¿SALIR? +REALLY SHUTDOWN?=¿APAGAR? +FILES SORTED=ARCHIVOS ORDENADOS +You are changing the UI to a restricted mode:=Estás cambiando la interfaz a un modo restringido: +This will hide most menu-options to prevent changes to the system.=Esto ocultará la mayoría de las opciones para evitar cambios en el sistema. +To unlock and return to the full UI, enter this code:=Para desbloquear y volver a la interfaz completa, introduce este código: +Do you want to proceed?=¿Deseas continuar? + + +[GAME COLLECTION] +GAME COLLECTION SETTINGS=AJUSTES DE COLECCIONES +AUTOMATIC GAME COLLECTIONS=COLECCIONES AUTOMÁTICAS +CUSTOM GAME COLLECTIONS=COLECCIONES PERSONALIZADAS +RANDOM GAME COLLECTION SETTINGS=AJUSTES DE COLECCIÓN ALEATORIA +CREATE NEW CUSTOM COLLECTION FROM THEME=CREAR COLECCIÓN DESDE TEMA +CREATE NEW CUSTOM COLLECTION=CREAR NUEVA COLECCIÓN +GROUP UNTHEMED CUSTOM COLLECTIONS=AGRUPAR COLECCIONES SIN TEMA +SORT CUSTOM COLLECTIONS AND SYSTEMS=ORDENAR COLECCIONES Y SISTEMAS +1 SELECTED=1 SELECCIONADA +9 SELECTED=9 SELECCIONADAS +FINISH EDITING=TERMINAR EDICIÓN +COLLECTION=COLECCIÓN +SELECT THEME FOLDER=SELECCIONAR CARPETA DE TEMA +New Collection Name=Nombre de la colección +SHOW SYSTEM NAME IN COLLECTIONS=MOSTRAR SISTEMA +PRESS (Y) TWICE TO REMOVE FROM FAVS./COLL.=PRESIONA (Y) DOS VECES PARA QUITAR DE FAVS./COLL. +ADD/REMOVE GAMES WHILE SCREENSAVER TO=AÑADIR/QUITAR JUEGOS EN SCREENSAVER A += +SELECT COLLECTIONS=SELECCIONAR COLECCIONES + + +[COLLECTIONS] +all games=TODOS LOS JUEGOS +last played=ÚLTIMOS JUGADOS +favorites=FAVORITOS +collections=COLECCIONES +random=ALEATORIOS + + +[GAMELIST_DATA] +RELEASED=LANZAMIENTO +LAST PLAYED=ÚLT. VEZ +TIMES PLAYED=VECES +PLAYERS=JUGADORES +OPTIONS=OPCIONES +MENU=MENÚ +BACK=VOLVER +LAUNCH=INICIAR +SYSTEM=SISTEMA +CHOOSE=ELEGIR +FAVORITES=FAVORITOS +RANDOM=ALEATORIO + + +[HELP] +CHOOSE=ELEGIR +MENU=MENÚ +RANDOM=ALEATORIO +SELECT=SELECCIONAR +BACK=VOLVER +OPTIONS=OPCIONES +FAVORITE=FAVORITO +FAVORITES=FAVORITOS +TOGGLE FAVORITE=MARCAR/DESMARCAR FAVORITO +ADD/REMOVE FAVORITE=AÑADIR/QUITAR FAVORITO +LAUNCH SCREENSAVER=ACTIVAR SCREENSAVER +CHANGE=CAMBIAR + + +[GENERAL] +DEFAULT=PREDETERMINADO +CUSTOM=PERSONALIZADO +SYSTEM=SISTEMA +OPTIONS=OPCIONES +FAVORITES=FAVORITOS +NONE=NINGUNO + + +[RELATIVE TIME] +DAYS_AGO=%d días atrás +HOURS_AGO=%d horas atrás +MINS_AGO=%d min atrás +SECS_AGO=%d seg atrás +NEVER=NUNCA +UNKNOWN=DESCONOCIDO + + +[SCRAPER_MULTI] +SCRAPING IN PROGRESS=OBTENIENDO DATOS... +INPUT=ENTRAR TEXTO +SEARCH=BUSCAR +SKIP=OMITIR +STOP=DETENER +STOP (PROGRESS SAVED)=DETENER (GUARDANDO PROGRESO) +GAME SCRAPED=JUEGO ACTUALIZADO +GAMES SCRAPED=JUEGOS ACTUALIZADOS +GAME SKIPPED=JUEGO OMITIDO +GAMES SKIPPED=JUEGOS OMITIDOS +NO GAMES WERE SCRAPED.=NINGÚN JUEGO FUE ACTUALIZADO. +OF=DE + + +[COLLECTION_POPUPS] +EDITING_COLLECTION=Editando la colección '{collection}'. Añade o quita juegos con (Y). +FINISHED_EDITING_COLLECTION=Edición de la colección '{collection}' finalizada. +PRESS_AGAIN_TO_REMOVE=Pulsa de nuevo para quitar de '{collection}'. +ADDED_TO_COLLECTION=Se agregó '{game}' a '{collection}'. +REMOVED_FROM_COLLECTION=Se eliminó '{game}' de '{collection}'. +EMPTY_COLLECTION=Esta colección está vacía. + + +[COLLECTION_DIALOG] +SELECT ALL=SELECCIONAR TODO +SELECT NONE=NO SELECCIONAR NINGUNA + + +[DATE] +NEVER=NUNCA +UNKNOWN=DESCONOCIDO +DAYS AGO=DÍAS ATRÁS +HOURS AGO=HORAS ATRÁS +MINUTES AGO=MINUTOS ATRÁS +SECONDS AGO=SEGUNDOS ATRÁS + + +[BUTTONS] +A=ACEPTAR +B=VOLVER +X=ALEATORIO +Y=FAVORITOS +START=INICIO +SELECT=SELECCIONAR +CHANGE=CAMBIAR + + +[SYSTEMVIEW] +ARCADE=ARCADE +RETROPIE=RETROPIE +GAMES AVAILABLE=JUEGOS DISPONIBLES + + +[QUIT] +QUIT EMULATIONSTATION=SALIR DE EMULATIONSTATION +RESTART EMULATIONSTATION=REINICIAR EMULATIONSTATION +RESTART SYSTEM=REINICIAR SISTEMA +SHUTDOWN SYSTEM=APAGAR SISTEMA + + +[ERROR] +ERROR=ERROR +NETWORK ERROR=ERROR DE RED +NOT FOUND=NO ENCONTRADO + + +[INPUT] +CONFIGURE INPUT=CONFIGURAR ENTRADAS +ARE YOU SURE YOU WANT TO CONFIGURE INPUT?=¿CONFIGURAR ENTRADAS? + + +[SYSTEM] +SYSTEM SETTINGS=AJUSTES DEL SISTEMA + + +[LOWERCASE] +choose=ELEGIR +select=SELECCIONAR +close=CERRAR +back=volver +dark menu=menú oscuro +change=cambiar diff --git a/lang/fr.ini b/lang/fr.ini new file mode 100644 index 0000000000..0834479759 --- /dev/null +++ b/lang/fr.ini @@ -0,0 +1,314 @@ +[META] +CODE=fr +NAME=Français + + +[MAIN] +MAIN MENU=MENU PRINCIPAL +QUIT=QUITTER +YES=OUI +NO=NON +OK=OK +BACK=RETOUR +START=START +SELECT=SELECT +CANCEL=ANNULER + + +[GAME LIST] +OPTIONS=OPTIONS +JUMP TO ...=ALLER A... +LAUNCH SYSTEM SCREENSAVER=LANCER L'ECRAN DE VEILLE +SORT GAMES BY=TRIER LES JEUX PAR +FILTER GAMELIST=FILTRER LA LISTE DE JEU +ADD/REMOVE GAMES TO THIS GAME COLLECTION=AJOUTER/ENLEVER DES JEUX DANS CETTE COLLECTION +FINISH EDITING=VALIDER L'EDITION +COLLECTION=COLLECTION +COLLECTIONS=COLLECTIONS +ALL GAMES=TOUS LES JEUX +GET NEW RANDOM GAMES=OBTENIR DE NOUVEAUX JEUX ALEATOIREMENT +EDIT THIS FOLDER'S METADATA=EDITER LES METADONNEES DE CE DOSSIER +EDIT THIS GAME'S METADATA=EDITER LES METADONNEES DE CE JEU +CLOSE=FERMER + + +[SCRAPER] +SCRAPER=SCRAPER +SCRAPE FROM=SCRAPER A PARTIR DE +SCRAPE RATINGS=RECUPERER LES NOTES +SCRAPE NOW=SCRAPER MAINTENANT + + +[SETTINGS] +SOUND SETTINGS=PARAMETRES DU SON +UI SETTINGS=INTERFACE +OTHER SETTINGS=AUTRES PARAMETRES +GAME COLLECTION SETTINGS=GERER LES COLLECTIONS +SCREEN RESOLUTION=RESOLUTION DE L'ECRAN +ENABLE NAVIGATION SOUNDS=SONS DU MENU +ENABLE VIDEO AUDIO=SON DES APERCUS VIDEO +SYSTEM VOLUME=VOLUME GENERAL +THEME SET=THEMES +TRANSITION STYLE=STYLE DE TRANSITION +CAROUSEL TRANSITIONS=TRANSITION DU CARROUSEL +QUICK SYSTEM SELECT=CHANGEMENT RAPIDE DE CONSOLE +UI MODE=MODE DE L'INTERFACE +SCREENSAVER SETTINGS=PARAMETRE DE L'ECRAN DE VEILLE +ON-SCREEN HELP=AIDE A L'ECRAN +ENABLE FILTERS=ACTIVER LES FILTRES GRAPHIQUES +USE FULL SCREEN PAGING FOR LB/RB=DEFILEMENT PLUS RAPIDE AVEC LB/RB +START ON SYSTEM=DEMARRER SUR LA CONSOLE +SHOW FRAMERATE=AFFICHER LES IMAGES PAR SECONDE +AUDIO CARD=CARTE AUDIO +AUDIO DEVICE=MATERIEL AUDIO +POWER SAVER MODES=MODE ECONOMIE D'ENERGIE +VRAM LIMIT=LIMITE DE VRAM +PARSE GAMESLISTS ONLY=PARSER UNIQUEMENT LES GAMELISTS +SEARCH FOR LOCAL ART=RECHERCHER DES IMAGES EN LOCAL +SHOW HIDDEN FILES=AFFICHER LES FICHIERS CACHES +INDEX FILES DURING SCREENSAVER=INDEXER PENDANT LA MISE EN VEILLE +BACKGROUND INDEXING=INDEXER EN ARRIERE PLAN +SAVE METADATA=SAUVEGARDER LES METADONNEES +IGNORE ARTICLES (NAME SORT ONLY)=IGNORER LES ARTICLES (TRI ALPHABETIQUE) +DISABLE START MENU IN KID MODE=DESACTIVER CE MENU EN MODE KID +USE OMX PLAYER (HW ACCELERATED)=UTILISER OMX PLAYER(ACCELERATION MATERIELLE) +DARK MENU=MENU SOMBRE + + +[LANGUAGE] +LANGUAGE=LANGUE + + +[META] +EDIT METADATA=EDITER LES METADONNEES +GAME=JEU +FOLDER=DOSSIER +NAME=NOM +ENTER GAME NAME=TAPER LE NOM DU JEU +DESCRIPTION=DESCRIPTION +ENTER DESCRIPTION=VALIDER LA DESCRIPTION +RELEASE DATE=DATE DE SORTIE +ENTER RELEASE DATE=VALIDER LA DATE DE SORTIE +PLAY COUNT=NOMBRE DE PARTIES JOUEES +ENTER NUMBER OF TIMES PLAYED=VALIDER LE NOMBRE DE PARTIES JOUEES +LAST PLAYED=JOUE LA DERNIERE FOIS +ENTER LAST PLAYED DATE=ENTRER LA DATE DU DERNIER LANCEMENT + +SCRAPE=SCRAPER +SAVE=SAUVEGARDER +CANCEL=ANNULER +DELETE=SUPPRIMER +THIS WILL DELETE THE ACTUAL GAME FILE(S)!\nARE YOU SURE?=TOUS LES FICHIERS DU JEU SERONT SUPPRIMES ! ES-TU CERTAIN ? +SAVE CHANGES?=SAUVEGARDER LES CHANGEMENTS ? +YES=OUI +NO=NON +BACK=RETOUR +CLOSE=FERMER + + +[VIEW] +SYSTEM INFO=INFO SYSTEME +CONFIGURATION=CONFIGURATION +GAME=JEU +GAMES=JEUX +AVAILABLE=DISPONIBLES +CHOOSE=CHOISIR +SELECT=SELECTIONNER +RANDOM=ALEATOIRE +LAUNCH SCREENSAVER=DEMARRER L'ECRAN DE VEILLE + + +[GAMELIST] +GAMELIST VIEW STYLE=STYLE DES LISTES DE JEUX +automatic=automatique +basic=basic +detailed=détaillé +video=aperçus vidéo +grid=grille + + +[GAME DATA] +RATING=NOTE +RELEASE DATE=DATE DE SORTIE +DEVELOPER=DEVELOPPEUR +PUBLISHER=EDITEUR +GENRE=GENRE +PLAY COUNT=NOMBRE DE FOIS JOUE +LAST PLAYED=JOUE DERNIEREMENT +PLAYERS=JOUEURS +FAVORITES=FAVORIS + + +[MESSAGES] +REALLY RESTART?=REDEMARRER ? +REALLY QUIT?=FERMER ? +REALLY SHUTDOWN?=ETEINDRE ? +FILES SORTED=FICHIERS TRIES +You are changing the UI to a restricted mode:=L'INTERFACE VA PASSER DANS UN MODE RESTREINT +This will hide most menu-options to prevent changes to the system.=LA PLUPART DES OPTIONS SERONT CACHEES POUR EVITER DE CASSER LE SYSTEME. +To unlock and return to the full UI, enter this code:=POUR DEBLOQUER LE MENU COMPLET, TAPE CE CODE : +Do you want to proceed?=VEUX-TU CONTINUER ? + + +[GAME COLLECTION] +GAME COLLECTION SETTINGS=GERER LES COLLECTIONS +AUTOMATIC GAME COLLECTIONS=COLLECTIONS SYSTEME +CUSTOM GAME COLLECTIONS=COLLECTIONS PERSONNALISEES +RANDOM GAME COLLECTION SETTINGS=AJUSTER LES COLLECTIONS ALEATOIRES +CREATE NEW CUSTOM COLLECTION FROM THEME=CREER UNE NOUVELLE COLLECTION A PARTIR DE... +CREATE NEW CUSTOM COLLECTION=CREER UNE NOUVELLE COLLECTION +GROUP UNTHEMED CUSTOM COLLECTIONS=GROUPER LES COLLECTIONS PERSO SANS THEME +SORT CUSTOM COLLECTIONS AND SYSTEMS=TRIER LES COLLECTIONS PERSO ET LES CONSOLES +1 SELECTED=1 SELECTIONNEE +9 SELECTED=9 SELECTIONNEES +FINISH EDITING=TERMINER L'EDITION DE +COLLECTION=COLLECTION +SELECT THEME FOLDER=SELECTIONNER LE THEME DU DOSSIER +New Collection Name=NOM DE LA NOUVELLE COLLECTION +SHOW SYSTEM NAME IN COLLECTIONS=AFFICHER LE NOM DES CONSOLES DANS LES LISTES +PRESS (Y) TWICE TO REMOVE FROM FAVS./COLL.=PRESSER DEUX FOIS (Y) POUR ENLEVER DES FAV/COLLEC +ADD/REMOVE GAMES WHILE SCREENSAVER TO=AJOUTER/ENLEVER DES JEUX PENDANT LA VEILLE += +SELECT COLLECTIONS=SELECTIONNER LES COLLECTIONS + + +[COLLECTIONS] +all games=TOUS LES JEUX +last played=JOUES RECEMMENT +favorites=FAVORIS +collections=COLLECTIONS +random=ALEATOIRE + + +[GAMELIST_DATA] +RELEASED=DATE DE SORTIE +LAST PLAYED=JOUE DERNIEREMENT +TIMES PLAYED=NOMBRE DE FOIS JOUE +PLAYERS=NOMBRE DE JOUEURS +OPTIONS=OPTIONS +MENU=MENU +BACK=RETOUR +LAUNCH=LANCER +SYSTEM=SYSTEME +CHOOSE=CHOISIR +FAVORITES=FAVORIS +RANDOM=ALEATOIRE + + +[HELP] +CHOOSE=CHOISIR +MENU=MENU +RANDOM=ALEATOIRE +SELECT=SELECTIONNER +BACK=RETOUR +OPTIONS=OPTIONS +FAVORITE=FAVORI +FAVORITES=FAVORIS +TOGGLE FAVORITE=COCHER/DECOCHER FAVORI +ADD/REMOVE FAVORITE=AJOUTER/SUPPRIMER FAVORI +LAUNCH SCREENSAVER=LANCER L'ECRAN DE VEILLE +CHANGE=CHANGER + + +[GENERAL] +DEFAULT=PAR DEFAUT +CUSTOM=PERSONNALISER +SYSTEM=SYSTEME +OPTIONS=OPTIONS +FAVORITES=FAVORIS +NONE=VIDE + + +[RELATIVE TIME] +DAYS_AGO=IL Y A %d JOURS +HOURS_AGO=IL Y A %d HEURES +MINS_AGO=IL A %d MINUTES +SECS_AGO=IL Y A %d SECONDES +NEVER=JAMAIS +UNKNOWN=INCONNU + + +[SCRAPER_MULTI] +SCRAPING IN PROGRESS=TELECHARGEMENT DES DONNEE... +INPUT=TAPER TEXTE +SEARCH=CHERCHER +SKIP=PASSER +STOP=ARRETER +STOP (PROGRESS SAVED)=ARRETER (PROGRESSION SAUVEGARDEE) +GAME SCRAPED=JEU SCRAPE +GAMES SCRAPED=JEUX SCRAPES +GAME SKIPPED=JEU IGNORE +GAMES SKIPPED=JEUX IGNORES +NO GAMES WERE SCRAPED.=AUCUN JEU N'A ETE SCRAPE +OF=DE + + +[COLLECTION_POPUPS] +EDITING_COLLECTION=EDITION DE LA COLLECTION +FINISHED_EDITING_COLLECTION=FIN DE L'EDITION DE +PRESS_AGAIN_TO_REMOVE=APPUYER UNE SECONDE FOIS POUR SUPPRIMER DE +ADDED_TO_COLLECTION=AJOUTE A LA COLLECTION +REMOVED_FROM_COLLECTION=ENLEVE DE LA COLLECTION +EMPTY_COLLECTION=COLLECTION VIDE + + +[COLLECTION_DIALOG] +SELECT ALL=SELECTIONNER TOUTES +SELECT NONE=SELECTIONNER AUCUNE + + +[DATE] +NEVER=JAMAIS +UNKNOWN=INCONNUS +DAYS AGO=JOURS +HOURS AGO=HEURES +MINUTES AGO=MINUTES +SECONDS AGO=SECONDES + + +[BUTTONS] +A=VALIDER +B=RETOUR +X=ALEATOIRE +Y=FAVORIS +START=START +SELECT=SELECT +CHANGE=CHANGER + + +[SYSTEMVIEW] +ARCADE=ARCADE +RETROPIE=RETROPIE +GAMES AVAILABLE=JEUX DISPONIBLES + + +[QUIT] +QUIT EMULATIONSTATION=FERMER EMULATIONSTATION +RESTART EMULATIONSTATION=REDEMARRER EMULATIONSTATION +RESTART SYSTEM=REDEMARRER LE SYSTEME +SHUTDOWN SYSTEM=ETEINDRE LE SYSTEME + + +[ERROR] +ERROR=ERREUR +NETWORK ERROR=ERREUR RESEAU +NOT FOUND=NON TROUVE + + +[INPUT] +CONFIGURE INPUT=CONFIGURER UNE MANETTE +ARE YOU SURE YOU WANT TO CONFIGURE INPUT?= ES-TU CERTAIN DE VOULOIR CONFIGURER UNE MANETTE ? + + +[SYSTEM] +SYSTEM SETTINGS=CONFIGURATION DU SYSTEME + + +[LOWERCASE] +choose=choisir +select=selectionner +close=fermer +back=retour +dark menu=menu sombre +change=changer + diff --git a/lang/gn_PY.ini b/lang/gn_PY.ini new file mode 100644 index 0000000000..5fea5c9a09 --- /dev/null +++ b/lang/gn_PY.ini @@ -0,0 +1,315 @@ +[META] +CODE=gn_PY +NAME=Guaraní + + +[MAIN] +MAIN MENU=MENÚ PRINCIPAL +QUIT=ESÊ +YES=HÉE +NO=NAHÁNIRI +OK=OK +BACK=VOLVER +START=ÑEPYRŨ +SELECT=PORAVO +CANCEL=HEJA + + +[GAME LIST] +OPTIONS=OPCIÓN KUÉRA +JUMP TO ...=HO REHO ... +LAUNCH SYSTEM SCREENSAVER=ÑEPYRŨ PANTALLA JOKY +SORT GAMES BY=EMOHENDA JUEGO KUÉRA +FILTER GAMELIST=MBOPORU LISTA JUEGO +ADD/REMOVE GAMES TO THIS GAME COLLECTION=MOĨ/PE’A JUEGO KO COLECCIÓN-GUI +FINISH EDITING=OPA MOAMBUE +COLLECTION=COLECCIÓN +COLLECTIONS=COLECCIÓN KUÉRA +ALL GAMES=OPA JUEGO +GET NEW RANDOM GAMES=ERU JUEGO ALEATORIO PYAHU +EDIT THIS FOLDER'S METADATA=MOAMBUE METADATO CARPETA +EDIT THIS GAME'S METADATA=MOAMBUE METADATO JUEGO +CLOSE=MBOTY + + +[SCRAPER] +SCRAPER=SCRAPER +SCRAPE FROM=MOOGUE JEPURU +SCRAPE RATINGS=MOOGUE CALIFICACIÓN +SCRAPE NOW=MOOGUE KO’ÁG̃A + + +[SETTINGS] +SOUND SETTINGS=ÑEÑE’Ẽ PUAPU +UI SETTINGS=INTERFAZ +OTHER SETTINGS=OPCIÓN AMBUE +GAME COLLECTION SETTINGS=COLECCIÓN JUEGO +SCREEN RESOLUTION=RESOLUCIÓN PANTALLA +ENABLE NAVIGATION SOUNDS=MYANDU PUAPU MENÚ +ENABLE VIDEO AUDIO=MYANDU PUAPU VIDEO +SYSTEM VOLUME=PUAPU SISTEMA +THEME SET=TEMA +TRANSITION STYLE=ESTILO TRANSICIÓN +CAROUSEL TRANSITIONS=TRANSICIÓN CARRUSEL +QUICK SYSTEM SELECT=PORAVO SISTEMA PYA’E +UI MODE=MODO INTERFAZ +SCREENSAVER SETTINGS=AJUSTE PANTALLA JOKY +ON-SCREEN HELP=PYTYVÕ PANTALLA PE +ENABLE FILTERS=MYANDU FILTRO +USE FULL SCREEN PAGING FOR LB/RB=PÁGINA TUICHÁVA LB/RB +START ON SYSTEM=ÑEPYRŨ SISTEMA RE +SHOW FRAMERATE=HECHA FPS +AUDIO CARD=TARJETA PUAPU +AUDIO DEVICE=PUAPU JEPURU +POWER SAVER MODES=MODO AHORRO ENERGÍA +VRAM LIMIT=LÍMITE VRAM +PARSE GAMESLISTS ONLY=LEE LISTA JUEGO AÑOITE +SEARCH FOR LOCAL ART=HEKA ARTE LOCAL +SHOW HIDDEN FILES=HECHA ARCHIVO OKA’ÝVA +INDEX FILES DURING SCREENSAVER=INDICE ARCHIVO PANTALLA JOKY PE +BACKGROUND INDEXING=INDICE FONDO PE +SAVE METADATA=ÑONGATU METADATO +IGNORE ARTICLES (NAME SORT ONLY)=ANIVE EIPURU ARTÍCULO (TÉRA AÑOITE) +DISABLE START MENU IN KID MODE=MBOTY MENÚ START MITÃ MODE +USE OMX PLAYER (HW ACCELERATED)=IPURU OMX PLAYER (HW) +DARK MENU=MENÚ YVATE’Ỹ +CLOCK=ARAVO +THEME OPTIONS=OPCIÓN TEMA + + +[LANGUAGE] +LANGUAGE=ÑE’Ẽ + + +[META] +EDIT METADATA=MOAMBUE METADATO +GAME=JUEGO +FOLDER=CARPETA +NAME=TÉRA +ENTER GAME NAME=EHAI TÉRA JUEGO +DESCRIPTION=DESCRIPCIÓN +ENTER DESCRIPTION=EHAI DESCRIPCIÓN +RELEASE DATE=ARAVO ÑEMOSÊ +ENTER RELEASE DATE=EHAI ARAVO ÑEMOSÊ +PLAY COUNT=JUEGO JEY +ENTER NUMBER OF TIMES PLAYED=EHAI MBY JUEY +LAST PLAYED=JUEGO PAHÁPE +ENTER LAST PLAYED DATE=EHAI ARAVO JUEGO PAHÁPE + +SCRAPE=MOOGUE +SAVE=ÑONGATU +CANCEL=HEJA +DELETE=MBYAI +THIS WILL DELETE THE ACTUAL GAME FILE(S)!\nARE YOU SURE?=KO’Ã OMBYAI JUEGO ARCHIVO!\nEIKUAÁPA? +SAVE CHANGES?=EÑONGATÚPA MOAMBUE? +YES=HÉE +NO=NAHÁNIRI +BACK=VOLVER +CLOSE=MBOTY + + +[VIEW] +SYSTEM INFO=INFO SISTEMA +CONFIGURATION=CONFIGURACIÓN +GAME=JUEGO +GAMES=JUEGO KUÉRA +AVAILABLE=OJEPURUVA +CHOOSE=PORAVO +SELECT=PORAVO +RANDOM=ALEATORIO +LAUNCH SCREENSAVER=ÑEPYRŨ PANTALLA JOKY + + +[GAMELIST] +GAMELIST VIEW STYLE=ESTILO VISTA +automatic=automático +basic=básico +detailed=detallado +video=video +grid=cuadrícula + + +[GAME DATA] +RATING=CALIF. +RELEASE DATE=ARAVO +DEVELOPER=MOHEÑÓI +PUBLISHER=MOHERAKUÃ +GENRE=TIPO +PLAY COUNT=JUEY +LAST PLAYED=PAHÁPE +PLAYERS=JUG. +FAVORITES=FAV. + + +[MESSAGES] +REALLY RESTART?=REINICIÁPA? +REALLY QUIT?=ESÊPA? +REALLY SHUTDOWN?=MBOTÝPA? +FILES SORTED=ARCHIVO OÑEMOHENDA +You are changing the UI to a restricted mode:=Reimoambue interfaz peteĩ modo jejoko pe: +This will hide most menu-options to prevent changes to the system.=Kóva okañymby heta opción ani hag̃ua oñemoambue sistema. +To unlock and return to the full UI, enter this code:=Rejevy hag̃ua interfaz tuicháva, emoinge ko código: +Do you want to proceed?=Eipotápa eku’e? + + +[GAME COLLECTION] +GAME COLLECTION SETTINGS=AJUSTE COLECCIÓN JUEGO +AUTOMATIC GAME COLLECTIONS=COLECCIÓN AUTOMÁTICA +CUSTOM GAME COLLECTIONS=COLECCIÓN JEIPYRE +RANDOM GAME COLLECTION SETTINGS=AJUSTE COLECCIÓN ALEATORIA +CREATE NEW CUSTOM COLLECTION FROM THEME=JAPO COLECCIÓN PYAHU TEMA GUI +CREATE NEW CUSTOM COLLECTION=JAPO COLECCIÓN PYAHU +GROUP UNTHEMED CUSTOM COLLECTIONS=ATY COLECCIÓN TEMA’Ỹ +SORT CUSTOM COLLECTIONS AND SYSTEMS=EMOHENDA COLECCIÓN HA SISTEMA +1 SELECTED=1 PORAVOPYRE +9 SELECTED=9 PORAVOPYRE +FINISH EDITING=OPA MOAMBUE +COLLECTION=COLECCIÓN +SELECT THEME FOLDER=PORAVO CARPETA TEMA +New Collection Name=Téra coleccióngui +SHOW SYSTEM NAME IN COLLECTIONS=HECHA TÉRA SISTEMA +PRESS (Y) TWICE TO REMOVE FROM FAVS./COLL.=EJOPY (Y) MOKÕI JEY EPE’A HAG̃UA +ADD/REMOVE GAMES WHILE SCREENSAVER TO=MOĨ/PE’A JUEGO PANTALLA JOKY PE += +SELECT COLLECTIONS=PORAVO COLECCIÓN KUÉRA + + +[COLLECTIONS] +all games=OPA JUEGO +last played=JUEGO RAMOITE +favorites=FAVORITO +collections=COLECCIÓN +random=ALEATORIO + + +[GAMELIST_DATA] +RELEASED=ÑEMOSÊ +LAST PLAYED=PAHÁPE +TIMES PLAYED=JUEY +PLAYERS=JUGADOR +OPTIONS=OPCIÓN +MENU=MENÚ +BACK=VOLVER +LAUNCH=ÑEPYRŨ +SYSTEM=SISTEMA +CHOOSE=PORAVO +FAVORITES=FAVORITO +RANDOM=ALEATORIO + + +[HELP] +CHOOSE=PORAVO +MENU=MENÚ +RANDOM=ALEATORIO +SELECT=PORAVO +BACK=VOLVER +OPTIONS=OPCIÓN +FAVORITE=FAVORITO +FAVORITES=FAVORITO +TOGGLE FAVORITE=MOĨ/PE’A FAVORITO +ADD/REMOVE FAVORITE=MOĨ/PE’A FAVORITO +LAUNCH SCREENSAVER=ÑEPYRŨ PANTALLA JOKY +CHANGE=MOAMBUE + + +[GENERAL] +DEFAULT=OĨHAGUÉICHA +CUSTOM=JEIPYRE +SYSTEM=SISTEMA +OPTIONS=OPCIÓN +FAVORITES=FAVORITO +NONE=AVAVE + + +[RELATIVE TIME] +DAYS_AGO=%d ára ohasa +HOURS_AGO=%d aravo ohasa +MINS_AGO=%d aravo’i ohasa +SECS_AGO=%d aravo’i michĩ ohasa +NEVER=ARAMAVE +UNKNOWN=JEIKUAÁ’Ỹ + + +[SCRAPER_MULTI] +SCRAPING IN PROGRESS=MOOGUE OJAPO HÍNA... +INPUT=EHAI +SEARCH=HEKA +SKIP=EPO +STOP=PYTA +STOP (PROGRESS SAVED)=PYTA (OÑONGATU) +GAME SCRAPED=JUEGO OÑEMBOHESA’ỸIJEVY +GAMES SCRAPED=JUEGO KUÉRA OÑEMBOHESA’ỸIJEVY +GAME SKIPPED=JUEGO OÑEPO +GAMES SKIPPED=JUEGO KUÉRA OÑEPO +NO GAMES WERE SCRAPED.=NDAIPÓI JUEGO OÑEMBOHESA’ỸIVA. +OF=GUI + + +[COLLECTION_POPUPS] +EDITING_COLLECTION=Reimoambue colección '{collection}'. Emoĩ térã epe’a juego (Y) rupive. +FINISHED_EDITING_COLLECTION=Opa moambue colección '{collection}'. +PRESS_AGAIN_TO_REMOVE=Ejopy jevy epe’a hag̃ua '{collection}'-gui. +ADDED_TO_COLLECTION=Oñemoĩ '{game}' '{collection}'-pe. +REMOVED_FROM_COLLECTION=Oñembogue '{game}' '{collection}'-gui. +EMPTY_COLLECTION=Ko colección nandi. + + +[COLLECTION_DIALOG] +SELECT ALL=PORAVOPA +SELECT NONE=ANIVE EIPORAVO + + +[DATE] +NEVER=ARAMAVE +UNKNOWN=JEIKUAÁ’Ỹ +DAYS AGO=ÁRA Ohasa +HOURS AGO=ARAVO Ohasa +MINUTES AGO=ARAVO’I Ohasa +SECONDS AGO=ARAVO’I MICHĨ Ohasa + + +[BUTTONS] +A=ACEPTAR +B=VOLVER +X=ALEATORIO +Y=FAVORITO +START=ÑEPYRŨ +SELECT=PORAVO +CHANGE=MOAMBUE + + +[SYSTEMVIEW] +ARCADE=ARCADE +RETROPIE=RETROPIE +GAMES AVAILABLE=JUEGO OJEPURUVA + + +[QUIT] +QUIT EMULATIONSTATION=ESÊ EMULATIONSTATION-GUI +RESTART EMULATIONSTATION=REINICIA EMULATIONSTATION +RESTART SYSTEM=REINICIA SISTEMA +SHUTDOWN SYSTEM=MBOTY SISTEMA + + +[ERROR] +ERROR=JEJAVY +NETWORK ERROR=JEJAVY RED +NOT FOUND=NDOJOTOPÁI + + +[INPUT] +CONFIGURE INPUT=ÑEMOĨ CONTROL +ARE YOU SURE YOU WANT TO CONFIGURE INPUT?=EIKUAÁPA EIPOTA ÑEMOĨ CONTROL? + + +[SYSTEM] +SYSTEM SETTINGS=AJUSTE SISTEMA + + +[LOWERCASE] +choose=poravo +select=poravo +close=mboty +back=guevi +dark menu=menú yvate’ỹ +change=moambue diff --git a/lang/it.ini b/lang/it.ini new file mode 100644 index 0000000000..fa1c465a05 --- /dev/null +++ b/lang/it.ini @@ -0,0 +1,315 @@ +[META] +CODE=it_IT +NAME=Italiano + + +[MAIN] +MAIN MENU=MENU PRINCIPALE +QUIT=ESCI +YES=SÌ +NO=NO +OK=OK +BACK=INDIETRO +START=AVVIO +SELECT=SELEZIONA +CANCEL=ANNULLA + + +[GAME LIST] +OPTIONS=OPZIONI +JUMP TO ...=VAI A ... +LAUNCH SYSTEM SCREENSAVER=AVVIA SALVASCHERMO DI SISTEMA +SORT GAMES BY=ORDINA GIOCHI PER +FILTER GAMELIST=FILTRA LISTA GIOCHI +ADD/REMOVE GAMES TO THIS GAME COLLECTION=AGGIUNGI/RIMUOVI GIOCHI DA QUESTA COLLEZIONE +FINISH EDITING=TERMINA MODIFICA +COLLECTION=COLLEZIONE +COLLECTIONS=COLLEZIONI +ALL GAMES=TUTTI I GIOCHI +GET NEW RANDOM GAMES=OTTIENI NUOVI GIOCHI CASUALI +EDIT THIS FOLDER'S METADATA=MODIFICA METADATI DELLA CARTELLA +EDIT THIS GAME'S METADATA=MODIFICA METADATI DEL GIOCO +CLOSE=CHIUDI + + +[SCRAPER] +SCRAPER=SCRAPER +SCRAPE FROM=FONTE DATI +SCRAPE RATINGS=OTTIENI VALUTAZIONI +SCRAPE NOW=CERCA ORA + + +[SETTINGS] +SOUND SETTINGS=IMPOSTAZIONI AUDIO +UI SETTINGS=INTERFACCIA +OTHER SETTINGS=ALTRE OPZIONI +GAME COLLECTION SETTINGS=COLLEZIONI +SCREEN RESOLUTION=RISOLUZIONE SCHERMO +ENABLE NAVIGATION SOUNDS=SUONI DI NAVIGAZIONE +ENABLE VIDEO AUDIO=AUDIO VIDEO +SYSTEM VOLUME=VOLUME DI SISTEMA +THEME SET=TEMA +TRANSITION STYLE=STILE DI TRANSIZIONE +CAROUSEL TRANSITIONS=TRANSIZIONI CAROSELLO +QUICK SYSTEM SELECT=SELEZIONE RAPIDA SISTEMA +UI MODE=MODALITÀ INTERFACCIA +SCREENSAVER SETTINGS=IMPOSTAZIONI SALVASCHERMO +ON-SCREEN HELP=AIUTO A SCHERMO +ENABLE FILTERS=ABILITA FILTRI +USE FULL SCREEN PAGING FOR LB/RB=PAGINAZIONE A SCHERMO INTERO PER LB/RB +START ON SYSTEM=AVVIA SU SISTEMA +SHOW FRAMERATE=MOSTRA FPS +AUDIO CARD=SCHEDA AUDIO +AUDIO DEVICE=DISPOSITIVO AUDIO +POWER SAVER MODES=MODALITÀ RISPARMIO ENERGIA +VRAM LIMIT=LIMITE VRAM +PARSE GAMESLISTS ONLY=LEGGI SOLO LISTE GIOCHI +SEARCH FOR LOCAL ART=CERCA GRAFICA LOCALE +SHOW HIDDEN FILES=MOSTRA FILE NASCOSTI +INDEX FILES DURING SCREENSAVER=INDICIZZA FILE DURANTE SALVASCHERMO +BACKGROUND INDEXING=INDICIZZAZIONE IN BACKGROUND +SAVE METADATA=SALVA METADATI +IGNORE ARTICLES (NAME SORT ONLY)=IGNORA ARTICOLI (SOLO ORDINAMENTO NOME) +DISABLE START MENU IN KID MODE=DISABILITA MENU START IN MODALITÀ BAMBINI +USE OMX PLAYER (HW ACCELERATED)=USA OMX PLAYER (ACCELERAZIONE HW) +DARK MENU=MENU SCURO +CLOCK=OROLOGIO +THEME OPTIONS=OPZIONI TEMA + + +[LANGUAGE] +LANGUAGE=LINGUA + + +[META] +EDIT METADATA=MODIFICA METADATI +GAME=GIOCO +FOLDER=CARTELLA +NAME=NOME +ENTER GAME NAME=NOME DEL GIOCO +DESCRIPTION=DESCRIZIONE +ENTER DESCRIPTION=INSERISCI DESCRIZIONE +RELEASE DATE=DATA DI USCITA +ENTER RELEASE DATE=INSERISCI DATA DI USCITA +PLAY COUNT=VOLTE GIOCATO +ENTER NUMBER OF TIMES PLAYED=INSERISCI NUMERO DI VOLTE GIOCATO +LAST PLAYED=ULTIMA PARTITA +ENTER LAST PLAYED DATE=INSERISCI DATA ULTIMA PARTITA + +SCRAPE=OTTIENI DATI +SAVE=SALVA +CANCEL=ANNULLA +DELETE=ELIMINA +THIS WILL DELETE THE ACTUAL GAME FILE(S)!\nARE YOU SURE?=QUESTO ELIMINERÀ I FILE DI GIOCO!\nSEI SICURO? +SAVE CHANGES?=SALVARE LE MODIFICHE? +YES=SÌ +NO=NO +BACK=INDIETRO +CLOSE=CHIUDI + + +[VIEW] +SYSTEM INFO=INFO SISTEMA +CONFIGURATION=CONFIGURAZIONE +GAME=GIOCO +GAMES=GIOCHI +AVAILABLE=DISPONIBILI +CHOOSE=SCEGLI +SELECT=SELEZIONA +RANDOM=CASUALE +LAUNCH SCREENSAVER=AVVIA SALVASCHERMO + + +[GAMELIST] +GAMELIST VIEW STYLE=STILE VISUALIZZAZIONE +automatic=automatico +basic=base +detailed=dettagliato +video=video +grid=griglia + + +[GAME DATA] +RATING=VALUTAZIONE +RELEASE DATE=DATA +DEVELOPER=SVIL. +PUBLISHER=DISTR. +GENRE=GEN. +PLAY COUNT=VOLTE +LAST PLAYED=ULT. VOLTA +PLAYERS=GIOC. +FAVORITES=PREF. + + +[MESSAGES] +REALLY RESTART?=RIAVVIARE? +REALLY QUIT?=USCIRE? +REALLY SHUTDOWN?=SPEGNERE? +FILES SORTED=FILE ORDINATI +You are changing the UI to a restricted mode:=Stai cambiando l’interfaccia in modalità limitata: +This will hide most menu-options to prevent changes to the system.=Questo nasconderà la maggior parte delle opzioni per evitare modifiche al sistema. +To unlock and return to the full UI, enter this code:=Per sbloccare e tornare all’interfaccia completa, inserisci questo codice: +Do you want to proceed?=Vuoi continuare? + + +[GAME COLLECTION] +GAME COLLECTION SETTINGS=IMPOSTAZIONI COLLEZIONI +AUTOMATIC GAME COLLECTIONS=COLLEZIONI AUTOMATICHE +CUSTOM GAME COLLECTIONS=COLLEZIONI PERSONALIZZATE +RANDOM GAME COLLECTION SETTINGS=IMPOSTAZIONI COLLEZIONE CASUALE +CREATE NEW CUSTOM COLLECTION FROM THEME=CREA COLLEZIONE DAL TEMA +CREATE NEW CUSTOM COLLECTION=CREA NUOVA COLLEZIONE +GROUP UNTHEMED CUSTOM COLLECTIONS=RAGGRUPPA COLLEZIONI SENZA TEMA +SORT CUSTOM COLLECTIONS AND SYSTEMS=ORDINA COLLEZIONI E SISTEMI +1 SELECTED=1 SELEZIONATA +9 SELECTED=9 SELEZIONATE +FINISH EDITING=TERMINA MODIFICA +COLLECTION=COLLEZIONE +SELECT THEME FOLDER=SELEZIONA CARTELLA TEMA +New Collection Name=Nome della collezione +SHOW SYSTEM NAME IN COLLECTIONS=MOSTRA NOME SISTEMA +PRESS (Y) TWICE TO REMOVE FROM FAVS./COLL.=PREMI (Y) DUE VOLTE PER RIMUOVERE DA PREF./COLL. +ADD/REMOVE GAMES WHILE SCREENSAVER TO=AGGIUNGI/RIMUOVI GIOCHI NEL SALVASCHERMO A += +SELECT COLLECTIONS=SELEZIONA COLLEZIONI + + +[COLLECTIONS] +all games=TUTTI I GIOCHI +last played=ULTIMI GIOCATI +favorites=PREFERITI +collections=COLLEZIONI +random=CASUALI + + +[GAMELIST_DATA] +RELEASED=USCITA +LAST PLAYED=ULT. VOLTA +TIMES PLAYED=VOLTE +PLAYERS=GIOCATORI +OPTIONS=OPZIONI +MENU=MENU +BACK=INDIETRO +LAUNCH=AVVIA +SYSTEM=SISTEMA +CHOOSE=SCEGLI +FAVORITES=PREFERITI +RANDOM=CASUALE + + +[HELP] +CHOOSE=SCEGLI +MENU=MENU +RANDOM=CASUALE +SELECT=SELEZIONA +BACK=INDIETRO +OPTIONS=OPZIONI +FAVORITE=PREFERITO +FAVORITES=PREFERITI +TOGGLE FAVORITE=AGGIUNGI/RIMUOVI PREFERITO +ADD/REMOVE FAVORITE=AGGIUNGI/RIMUOVI PREFERITO +LAUNCH SCREENSAVER=ATTIVA SALVASCHERMO +CHANGE=CAMBIA + + +[GENERAL] +DEFAULT=PREDEFINITO +CUSTOM=PERSONALIZZATO +SYSTEM=SISTEMA +OPTIONS=OPZIONI +FAVORITES=PREFERITI +NONE=NESSUNO + + +[RELATIVE TIME] +DAYS_AGO=%d giorni fa +HOURS_AGO=%d ore fa +MINS_AGO=%d min fa +SECS_AGO=%d sec fa +NEVER=MAI +UNKNOWN=SCONOSCIUTO + + +[SCRAPER_MULTI] +SCRAPING IN PROGRESS=RECUPERO DATI IN CORSO... +INPUT=INSERISCI TESTO +SEARCH=CERCA +SKIP=SALTA +STOP=FERMA +STOP (PROGRESS SAVED)=FERMA (PROGRESSO SALVATO) +GAME SCRAPED=GIOCO AGGIORNATO +GAMES SCRAPED=GIOCHI AGGIORNATI +GAME SKIPPED=GIOCO SALTATO +GAMES SKIPPED=GIOCHI SALTATI +NO GAMES WERE SCRAPED.=NESSUN GIOCO È STATO AGGIORNATO. +OF=DI + + +[COLLECTION_POPUPS] +EDITING_COLLECTION=Modifica della collezione '{collection}'. Aggiungi o rimuovi giochi con (Y). +FINISHED_EDITING_COLLECTION=Modifica della collezione '{collection}' completata. +PRESS_AGAIN_TO_REMOVE=Premi di nuovo per rimuovere da '{collection}'. +ADDED_TO_COLLECTION=Aggiunto '{game}' a '{collection}'. +REMOVED_FROM_COLLECTION=Rimosso '{game}' da '{collection}'. +EMPTY_COLLECTION=Questa collezione è vuota. + + +[COLLECTION_DIALOG] +SELECT ALL=SELEZIONA TUTTO +SELECT NONE=DESELEZIONA TUTTO + + +[DATE] +NEVER=MAI +UNKNOWN=SCONOSCIUTO +DAYS AGO=GIORNI FA +HOURS AGO=ORE FA +MINUTES AGO=MINUTI FA +SECONDS AGO=SECONDI FA + + +[BUTTONS] +A=ACCETTA +B=INDIETRO +X=CASUALE +Y=PREFERITI +START=AVVIO +SELECT=SELEZIONA +CHANGE=CAMBIA + + +[SYSTEMVIEW] +ARCADE=ARCADE +RETROPIE=RETROPIE +GAMES AVAILABLE=GIOCHI DISPONIBILI + + +[QUIT] +QUIT EMULATIONSTATION=ESCI DA EMULATIONSTATION +RESTART EMULATIONSTATION=RIAVVIA EMULATIONSTATION +RESTART SYSTEM=RIAVVIA SISTEMA +SHUTDOWN SYSTEM=SPEGNI SISTEMA + + +[ERROR] +ERROR=ERRORE +NETWORK ERROR=ERRORE DI RETE +NOT FOUND=NON TROVATO + + +[INPUT] +CONFIGURE INPUT=CONFIGURA CONTROLLI +ARE YOU SURE YOU WANT TO CONFIGURE INPUT?=SEI SICURO DI VOLER CONFIGURARE I CONTROLLI? + + +[SYSTEM] +SYSTEM SETTINGS=IMPOSTAZIONI DI SISTEMA + + +[LOWERCASE] +choose=scegli +select=seleziona +close=chiudi +back=indietro +dark menu=menu scuro +change=cambia diff --git "a/lang/ja (\346\227\245\346\234\254\350\252\236).ini" "b/lang/ja (\346\227\245\346\234\254\350\252\236).ini" new file mode 100644 index 0000000000..4dbae37171 --- /dev/null +++ "b/lang/ja (\346\227\245\346\234\254\350\252\236).ini" @@ -0,0 +1,248 @@ +[META] +CODE=ja +NAME=日本語 + +[MAIN] +MAIN MENU=メインメニュー +QUIT=終了 +YES=はい +NO=いいえ +OK=OK +BACK=戻る +START=開始 +SELECT=選択 +CANCEL=キャンセル + +[GAME LIST] +OPTIONS=オプション +JUMP TO ...=移動... +LAUNCH SYSTEM SCREENSAVER=スクリーンセーバー開始 +SORT GAMES BY=並び替え +FILTER GAMELIST=フィルター +ADD/REMOVE GAMES TO THIS GAME COLLECTION=コレクションに追加/削除 +FINISH EDITING=編集終了 +COLLECTION=コレクション +COLLECTIONS=コレクション一覧 +ALL GAMES=すべてのゲーム +GET NEW RANDOM GAMES=ランダムゲーム取得 +EDIT THIS FOLDER'S METADATA=フォルダ情報編集 +EDIT THIS GAME'S METADATA=ゲーム情報編集 +CLOSE=閉じる + +[SCRAPER] +SCRAPER=スクレーパー +SCRAPE FROM=取得元 +SCRAPE RATINGS=評価取得 +SCRAPE NOW=今すぐ取得 + +[SETTINGS] +SOUND SETTINGS=サウンド +UI SETTINGS=UI設定 +OTHER SETTINGS=その他 +GAME COLLECTION SETTINGS=コレクション設定 +SCREEN RESOLUTION=解像度 +ENABLE NAVIGATION SOUNDS=操作音 +ENABLE VIDEO AUDIO=映像音声 +SYSTEM VOLUME=音量 +THEME SET=テーマ +TRANSITION STYLE=切替スタイル +CAROUSEL TRANSITIONS=カルーセル切替 +QUICK SYSTEM SELECT=高速システム選択 +UI MODE=UIモード +SCREENSAVER SETTINGS=スクリーンセーバー +ON-SCREEN HELP=画面ヘルプ +ENABLE FILTERS=フィルター有効 +USE FULL SCREEN PAGING FOR LB/RB=全画面ページ切替 LB/RB +START ON SYSTEM=起動システム +SHOW FRAMERATE=FPS表示 +AUDIO CARD=オーディオカード +AUDIO DEVICE=オーディオデバイス +POWER SAVER MODES=省電力モード +VRAM LIMIT=VRAM上限 +PARSE GAMESLISTS ONLY=ゲームリストのみ解析 +SEARCH FOR LOCAL ART=ローカルアート検索 +SHOW HIDDEN FILES=隠しファイル表示 +INDEX FILES DURING SCREENSAVER=セーバー中にインデックス +SAVE METADATA=メタ保存 +IGNORE ARTICLES (NAME SORT ONLY)=記事無視(並び替え) +DISABLE START MENU IN KID MODE=キッズモードでスタート非表示 +USE OMX PLAYER (HW ACCELERATED)=OMX再生(HW) +BACKGROUND INDEXING=バックグラウンド登録 + +[LANGUAGE] +LANGUAGE=言語 + +[META] +EDIT METADATA=メタ編集 +GAME=ゲーム +FOLDER=フォルダ +NAME=名前 +ENTER GAME NAME=ゲーム名入力 +DESCRIPTION=説明 +ENTER DESCRIPTION=説明入力 +RELEASE DATE=発売日 +ENTER RELEASE DATE=日付入力 +PLAY COUNT=プレイ回数 +ENTER NUMBER OF TIMES PLAYED=回数入力 +LAST PLAYED=最終プレイ +ENTER LAST PLAYED DATE=日付入力 + +SCRAPE=データ取得 +SAVE=保存 +CANCEL=キャンセル +DELETE=削除 +THIS WILL DELETE THE ACTUAL GAME FILE(S)!\nARE YOU SURE?=ゲームファイルを削除します!\nよろしいですか? +SAVE CHANGES?=変更を保存? +YES=はい +NO=いいえ +BACK=戻る +CLOSE=閉じる + +[VIEW] +SYSTEM INFO=システム情報 +CONFIGURATION=設定 +GAME=ゲーム +GAMES=ゲーム +AVAILABLE=利用可能 +CHOOSE=選択 +SELECT=選択 +RANDOM=ランダム +LAUNCH SCREENSAVER=スクリーンセーバー開始 + +[GAMELIST] +GAMELIST VIEW STYLE=表示スタイル +automatic=自動 +basic=基本 +detailed=詳細 +video=ビデオ +grid=グリッド + +[GAME DATA] +RATING=評価 +RELEASE DATE=発売日 +DEVELOPER=開発元 +PUBLISHER=販売元 +GENRE=ジャンル +PLAY COUNT=回数 +LAST PLAYED=最終 +PLAYERS=人数 +FAVORITES=お気に入り + +[MESSAGES] +REALLY RESTART?=再起動しますか? +REALLY QUIT?=終了しますか? +REALLY SHUTDOWN?=電源を切りますか? +FILES SORTED=ファイルを並び替えました +You are changing the UI to a restricted mode:=制限モードに切替中: +This will hide most menu-options to prevent changes to the system.=安全のため、メニュー項目を非表示にします +To unlock and return to the full UI, enter this code:= +フルUIに戻るにはコード入力: +Do you want to proceed?=続行しますか? + +[GAME COLLECTION] +GAME COLLECTION SETTINGS=コレクション設定 +AUTOMATIC GAME COLLECTIONS=自動コレクション +CUSTOM GAME COLLECTIONS=カスタムコレクション +RANDOM GAME COLLECTION SETTINGS=ランダムコレクション設定 +CREATE NEW CUSTOM COLLECTION FROM THEME=テーマで新規コレクション +CREATE NEW CUSTOM COLLECTION=新規カスタムコレクション +GROUP UNTHEMED CUSTOM COLLECTIONS=テーマなしをまとめる +SORT CUSTOM COLLECTIONS AND SYSTEMS=コレクション並び替え +BACK=戻る +1 SELECTED=1 選択 +9 SELECTED=9 選択 +FINISH EDITING=編集完了 +COLLECTION=コレクション +SELECT THEME FOLDER=テーマフォルダ選択 +New Collection Name=名前入力 +SHOW SYSTEM NAME IN COLLECTIONS=システム名表示 +PRESS (Y) TWICE TO REMOVE FROM FAVS./COLL.=Y2回で削除 +ADD/REMOVE GAMES WHILE SCREENSAVER TO=セーバー中に追加/削除 +=デフォルト +SELECT COLLECTIONS=コレクション選択 + +[COLLECTIONS] +all games=すべてのゲーム +last played=最近のプレイ +favorites=お気に入り +collections=コレクション一覧 +random=ランダム + +[GAMELIST_DATA] +RELEASED=発売日 +LAST PLAYED=最終プレイ +TIMES PLAYED=回数 +PLAYERS=人数 +OPTIONS=オプション +MENU=メニュー +BACK=戻る +LAUNCH=開始 +SYSTEM=システム +CHOOSE=選択 +FAVORITES=お気に入り +RANDOM=ランダム + +[HELP] +CHOOSE=選択 +MENU=メニュー +RANDOM=ランダム +SELECT=選択 +BACK=戻る +OPTIONS=オプション +FAVORITE=お気に入り +FAVORITES=お気に入り一覧 +TOGGLE FAVORITE=お気に入り切替 +ADD/REMOVE FAVORITE=お気に入り追加/削除 +LAUNCH SCREENSAVER=セーバー開始 + +[GENERAL] +DEFAULT=デフォルト +CUSTOM=カスタム +SYSTEM=システム +OPTIONS=オプション +FAVORITES=お気に入り +NONE=なし + +[RELATIVE TIME] +DAYS_AGO=%d 日前 +HOURS_AGO=%d 時間前 +MINS_AGO=%d 分前 +SECS_AGO=%d 秒前 +NEVER=なし +UNKNOWN=不明 + +[BUTTONS] +A=OK +B=戻る +X=ランダム +Y=お気に入り +START=開始 +SELECT=選択 + +[SYSTEMVIEW] +ARCADE=アーケード +RETROPIE=レトロパイ +GAMES AVAILABLE=利用可能ゲーム + +[QUIT] +QUIT EMULATIONSTATION=終了 +RESTART EMULATIONSTATION=再起動 +RESTART SYSTEM=システム再起動 +SHUTDOWN SYSTEM=電源オフ + +[ERROR] +ERROR=エラー +NETWORK ERROR=ネットワークエラー +NOT FOUND=見つかりません + +[INPUT] +CONFIGURE INPUT=入力設定 +ARE YOU SURE YOU WANT TO CONFIGURE INPUT?=入力設定しますか? + +[SYSTEM] +SYSTEM SETTINGS=システム設定 + +[LOWERCASE] +choose=選択 +select=選択 +close=閉じる diff --git a/lang/pt.ini b/lang/pt.ini new file mode 100644 index 0000000000..6c35460d5e --- /dev/null +++ b/lang/pt.ini @@ -0,0 +1,315 @@ +[META] +CODE=pt_BR +NAME=Português (Brasil) + + +[MAIN] +MAIN MENU=MENU PRINCIPAL +QUIT=SAIR +YES=SIM +NO=NÃO +OK=OK +BACK=VOLTAR +START=INICIAR +SELECT=SELECIONAR +CANCEL=CANCELAR + + +[GAME LIST] +OPTIONS=OPÇÕES +JUMP TO ...=IR PARA ... +LAUNCH SYSTEM SCREENSAVER=INICIAR PROTETOR DE TELA DO SISTEMA +SORT GAMES BY=ORDENAR JOGOS POR +FILTER GAMELIST=FILTRAR LISTA DE JOGOS +ADD/REMOVE GAMES TO THIS GAME COLLECTION=ADICIONAR/REMOVER JOGOS DESTA COLEÇÃO +FINISH EDITING=FINALIZAR EDIÇÃO +COLLECTION=COLEÇÃO +COLLECTIONS=COLEÇÕES +ALL GAMES=TODOS OS JOGOS +GET NEW RANDOM GAMES=OBTER NOVOS JOGOS ALEATÓRIOS +EDIT THIS FOLDER'S METADATA=EDITAR METADADOS DA PASTA +EDIT THIS GAME'S METADATA=EDITAR METADADOS DO JOGO +CLOSE=FECHAR + + +[SCRAPER] +SCRAPER=SCRAPER +SCRAPE FROM=FONTE DE DADOS +SCRAPE RATINGS=OBTER AVALIAÇÕES +SCRAPE NOW=BUSCAR AGORA + + +[SETTINGS] +SOUND SETTINGS=CONFIGURAÇÕES DE SOM +UI SETTINGS=INTERFACE +OTHER SETTINGS=OUTRAS OPÇÕES +GAME COLLECTION SETTINGS=COLEÇÕES +SCREEN RESOLUTION=RESOLUÇÃO DE TELA +ENABLE NAVIGATION SOUNDS=SONS DE NAVEGAÇÃO +ENABLE VIDEO AUDIO=ÁUDIO DO VÍDEO +SYSTEM VOLUME=VOLUME DO SISTEMA +THEME SET=TEMA +TRANSITION STYLE=ESTILO DE TRANSIÇÃO +CAROUSEL TRANSITIONS=TRANSIÇÕES DO CARROSSEL +QUICK SYSTEM SELECT=SELEÇÃO RÁPIDA DE SISTEMA +UI MODE=MODO DE INTERFACE +SCREENSAVER SETTINGS=CONFIGURAÇÕES DO PROTETOR DE TELA +ON-SCREEN HELP=AJUDA NA TELA +ENABLE FILTERS=HABILITAR FILTROS +USE FULL SCREEN PAGING FOR LB/RB=PAGINAÇÃO EM TELA CHEIA PARA LB/RB +START ON SYSTEM=INICIAR NO SISTEMA +SHOW FRAMERATE=MOSTRAR FPS +AUDIO CARD=PLACA DE ÁUDIO +AUDIO DEVICE=DISPOSITIVO DE ÁUDIO +POWER SAVER MODES=MODOS DE ECONOMIA DE ENERGIA +VRAM LIMIT=LIMITE DE VRAM +PARSE GAMESLISTS ONLY=LER APENAS LISTAS DE JOGOS +SEARCH FOR LOCAL ART=BUSCAR ARTES LOCAIS +SHOW HIDDEN FILES=MOSTRAR ARQUIVOS OCULTOS +INDEX FILES DURING SCREENSAVER=INDEXAR ARQUIVOS NO PROTETOR DE TELA +BACKGROUND INDEXING=INDEXAÇÃO EM SEGUNDO PLANO +SAVE METADATA=SALVAR METADADOS +IGNORE ARTICLES (NAME SORT ONLY)=IGNORAR ARTIGOS (ORDENAR POR NOME) +DISABLE START MENU IN KID MODE=OCULTAR MENU START NO MODO INFANTIL +USE OMX PLAYER (HW ACCELERATED)=USAR OMX PLAYER (ACELERAÇÃO HW) +DARK MENU=MENU ESCURO +CLOCK=RELÓGIO +THEME OPTIONS=OPÇÕES DO TEMA + + +[LANGUAGE] +LANGUAGE=IDIOMA + + +[META] +EDIT METADATA=EDITAR METADADOS +GAME=JOGO +FOLDER=PASTA +NAME=NOME +ENTER GAME NAME=NOME DO JOGO +DESCRIPTION=DESCRIÇÃO +ENTER DESCRIPTION=INSIRA UMA DESCRIÇÃO +RELEASE DATE=DATA DE LANÇAMENTO +ENTER RELEASE DATE=INSIRA A DATA DE LANÇAMENTO +PLAY COUNT=VEZES JOGADO +ENTER NUMBER OF TIMES PLAYED=INSIRA QUANTAS VEZES JOGOU +LAST PLAYED=ÚLTIMA VEZ JOGADO +ENTER LAST PLAYED DATE=INSIRA A DATA DA ÚLTIMA VEZ + +SCRAPE=OBTER DADOS +SAVE=SALVAR +CANCEL=CANCELAR +DELETE=EXCLUIR +THIS WILL DELETE THE ACTUAL GAME FILE(S)!\nARE YOU SURE?=ISTO EXCLUIRÁ O(S) ARQUIVO(S) DO JOGO!\nTEM CERTEZA? +SAVE CHANGES?=SALVAR ALTERAÇÕES? +YES=SIM +NO=NÃO +BACK=VOLTAR +CLOSE=FECHAR + + +[VIEW] +SYSTEM INFO=INFORMAÇÕES DO SISTEMA +CONFIGURATION=CONFIGURAÇÃO +GAME=JOGO +GAMES=JOGOS +AVAILABLE=DISPONÍVEIS +CHOOSE=ESCOLHER +SELECT=SELECIONAR +RANDOM=ALEATÓRIO +LAUNCH SCREENSAVER=INICIAR PROTETOR DE TELA + + +[GAMELIST] +GAMELIST VIEW STYLE=ESTILO DE VISUALIZAÇÃO +automatic=automático +basic=básico +detailed=detalhado +video=vídeo +grid=grade + + +[GAME DATA] +RATING=AVALIAÇÃO +RELEASE DATE=DATA +DEVELOPER=DEV. +PUBLISHER=DIST. +GENRE=GÊN. +PLAY COUNT=VEZES +LAST PLAYED=ÚLT. VEZ +PLAYERS=JOG. +FAVORITES=FAVS. + + +[MESSAGES] +REALLY RESTART?=REINICIAR? +REALLY QUIT?=SAIR? +REALLY SHUTDOWN?=DESLIGAR? +FILES SORTED=ARQUIVOS ORDENADOS +You are changing the UI to a restricted mode:=Você está mudando a interface para o modo restrito: +This will hide most menu-options to prevent changes to the system.=Isso ocultará a maioria das opções para evitar alterações no sistema. +To unlock and return to the full UI, enter this code:=Para desbloquear e voltar à interface completa, digite este código: +Do you want to proceed?=Deseja continuar? + + +[GAME COLLECTION] +GAME COLLECTION SETTINGS=CONFIGURAÇÕES DE COLEÇÕES +AUTOMATIC GAME COLLECTIONS=COLEÇÕES AUTOMÁTICAS +CUSTOM GAME COLLECTIONS=COLEÇÕES PERSONALIZADAS +RANDOM GAME COLLECTION SETTINGS=CONFIGURAÇÕES DE COLEÇÃO ALEATÓRIA +CREATE NEW CUSTOM COLLECTION FROM THEME=CRIAR COLEÇÃO A PARTIR DO TEMA +CREATE NEW CUSTOM COLLECTION=CRIAR NOVA COLEÇÃO +GROUP UNTHEMED CUSTOM COLLECTIONS=AGRUPAR COLEÇÕES SEM TEMA +SORT CUSTOM COLLECTIONS AND SYSTEMS=ORDENAR COLEÇÕES E SISTEMAS +1 SELECTED=1 SELECIONADA +9 SELECTED=9 SELECIONADAS +FINISH EDITING=FINALIZAR EDIÇÃO +COLLECTION=COLEÇÃO +SELECT THEME FOLDER=SELECIONAR PASTA DO TEMA +New Collection Name=Nome da coleção +SHOW SYSTEM NAME IN COLLECTIONS=MOSTRAR NOME DO SISTEMA +PRESS (Y) TWICE TO REMOVE FROM FAVS./COLL.=PRESSIONE (Y) DUAS VEZES PARA REMOVER DOS FAVS./COL. +ADD/REMOVE GAMES WHILE SCREENSAVER TO=ADICIONAR/REMOVER JOGOS NO PROTETOR DE TELA PARA += +SELECT COLLECTIONS=SELECIONAR COLEÇÕES + + +[COLLECTIONS] +all games=TODOS OS JOGOS +last played=ÚLTIMOS JOGADOS +favorites=FAVORITOS +collections=COLEÇÕES +random=ALEATÓRIOS + + +[GAMELIST_DATA] +RELEASED=LANÇAMENTO +LAST PLAYED=ÚLT. VEZ +TIMES PLAYED=VEZES +PLAYERS=JOGADORES +OPTIONS=OPÇÕES +MENU=MENU +BACK=VOLTAR +LAUNCH=INICIAR +SYSTEM=SISTEMA +CHOOSE=ESCOLHER +FAVORITES=FAVORITOS +RANDOM=ALEATÓRIO + + +[HELP] +CHOOSE=ESCOLHER +MENU=MENU +RANDOM=ALEATÓRIO +SELECT=SELECIONAR +BACK=VOLTAR +OPTIONS=OPÇÕES +FAVORITE=FAVORITO +FAVORITES=FAVORITOS +TOGGLE FAVORITE=MARCAR/DESMARCAR FAVORITO +ADD/REMOVE FAVORITE=ADICIONAR/REMOVER FAVORITO +LAUNCH SCREENSAVER=ATIVAR PROTETOR DE TELA +CHANGE=ALTERAR + + +[GENERAL] +DEFAULT=PADRÃO +CUSTOM=PERSONALIZADO +SYSTEM=SISTEMA +OPTIONS=OPÇÕES +FAVORITES=FAVORITOS +NONE=NENHUM + + +[RELATIVE TIME] +DAYS_AGO=%d dias atrás +HOURS_AGO=%d horas atrás +MINS_AGO=%d min atrás +SECS_AGO=%d seg atrás +NEVER=NUNCA +UNKNOWN=DESCONHECIDO + + +[SCRAPER_MULTI] +SCRAPING IN PROGRESS=OBTENDO DADOS... +INPUT=ENTRAR TEXTO +SEARCH=BUSCAR +SKIP=IGNORAR +STOP=PARAR +STOP (PROGRESS SAVED)=PARAR (PROGRESSO SALVO) +GAME SCRAPED=JOGO ATUALIZADO +GAMES SCRAPED=JOGOS ATUALIZADOS +GAME SKIPPED=JOGO IGNORADO +GAMES SKIPPED=JOGOS IGNORADOS +NO GAMES WERE SCRAPED.=NENHUM JOGO FOI ATUALIZADO. +OF=DE + + +[COLLECTION_POPUPS] +EDITING_COLLECTION=Editando a coleção '{collection}'. Adicione ou remova jogos com (Y). +FINISHED_EDITING_COLLECTION=Edição da coleção '{collection}' finalizada. +PRESS_AGAIN_TO_REMOVE=Pressione novamente para remover de '{collection}'. +ADDED_TO_COLLECTION=Adicionado '{game}' à '{collection}'. +REMOVED_FROM_COLLECTION=Removido '{game}' de '{collection}'. +EMPTY_COLLECTION=Esta coleção está vazia. + + +[COLLECTION_DIALOG] +SELECT ALL=SELECIONAR TUDO +SELECT NONE=DESMARCAR TUDO + + +[DATE] +NEVER=NUNCA +UNKNOWN=DESCONHECIDO +DAYS AGO=DIAS ATRÁS +HOURS AGO=HORAS ATRÁS +MINUTES AGO=MINUTOS ATRÁS +SECONDS AGO=SEGUNDOS ATRÁS + + +[BUTTONS] +A=ACEITAR +B=VOLTAR +X=ALEATÓRIO +Y=FAVORITOS +START=INICIAR +SELECT=SELECIONAR +CHANGE=ALTERAR + + +[SYSTEMVIEW] +ARCADE=ARCADE +RETROPIE=RETROPIE +GAMES AVAILABLE=JOGOS DISPONÍVEIS + + +[QUIT] +QUIT EMULATIONSTATION=SAIR DO EMULATIONSTATION +RESTART EMULATIONSTATION=REINICIAR EMULATIONSTATION +RESTART SYSTEM=REINICIAR SISTEMA +SHUTDOWN SYSTEM=DESLIGAR SISTEMA + + +[ERROR] +ERROR=ERRO +NETWORK ERROR=ERRO DE REDE +NOT FOUND=NÃO ENCONTRADO + + +[INPUT] +CONFIGURE INPUT=CONFIGURAR CONTROLES +ARE YOU SURE YOU WANT TO CONFIGURE INPUT?=TEM CERTEZA QUE DESEJA CONFIGURAR OS CONTROLES? + + +[SYSTEM] +SYSTEM SETTINGS=CONFIGURAÇÕES DO SISTEMA + + +[LOWERCASE] +choose=escolher +select=selecionar +close=fechar +back=voltar +dark menu=menu escuro +change=alterar diff --git a/resources/1splash.svg b/resources/1splash.svg new file mode 100644 index 0000000000..3e6d23540b --- /dev/null +++ b/resources/1splash.svg @@ -0,0 +1,5114 @@ + + + + + + + + + + diff --git a/resources/ESX_Guide.pdf b/resources/ESX_Guide.pdf new file mode 100644 index 0000000000000000000000000000000000000000..49c670b81c0d74b0c1dbe8a3f76efbef64255d53 GIT binary patch literal 340319 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 Oc@P9lOj63y1pg1e$nD|) literal 0 HcmV?d00001 diff --git a/resources/frame.png b/resources/frame.png index 0bca559ef1c32435e262aed372e8e5a23f39b391..5dfeb889d999c8d37b5e4f0c6f53afa07c4e6055 100644 GIT binary patch literal 5375 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 ViP(`{Jq3&gLm`jAs)85h{R@KF)D{2$ delta 1095 zcmeybd60L4ikf7JYeY$Kep*R+Vo@rCV@iHfs)A>3VtQ&&YGO)d;mK4R1_tJ|$*oNO zj0%&lGU+-%ltlOjC^+XAr7D=}8S5q+7#b>=nduptn3|ax>L?f)7#ist0Fjxlp^25L znU#^b0u(6OO_pSKnjFh4H@Ti!Z}Jyr+sXS_G$-eVl!EjRhq-Z$k4^r%+z2q zFRP@BLPc(Ym2**QVo82cNPd2ftrEokfQjP!=i&D~nlEukInFS@8`FSv*AQv|fv%(6f zJvX%^5y-KxC`e4sPAySL%}cRWDpIny%Xq)HpMintqo<2wNX4x;v(9@-2Z|g!f5+!C zSK4BQT+u5J!UTdQuG@5i+oh|vE6*!>ZEeH0EwA^rcx~l>pjN+{WmZdy_?E623P1E; z`54Ide*1kaMzU-7B}3-#2d3{l7jyQzb@4fl?|oNu%vu*tnG~U;wk@|n7F0^9GaTDIEwy&%zJ2o={&d;|C9>}O z|IH)c#%cD44Gi}8IUNM5>NorO``11W;F)aa^m)$$P1DR742~R3FJ3(mw3~5G-GeS=iqCd8aPKR>Ui$N}%sJhU)0@6t&0hH4#r$FMhuuk* zeTSd@VeMt{UKZ{=U1SlXYOcgQ=8GOTdVlFJeQLfwl{fj%rDN`4@f}=ljB?^M40&|2-e(y0+$@p0cVS zKXU>DPXnU?1KR-x2?k_NLp4*!t6c(z~KDL_T=Wxn{DfX!MeJ=z5P<&cIC|n9oMt7vlrFW{0W{rXU>|p@7{4K>gnlK z$u-FJ_w_BgnDK#cjpm$)dpB>cyq;w$^tD9uqV(Cz+BW7dVpWAW`H$V7;-$JWM9Y@j z(f>=>+OXC*#!tKU@BhEu*33-p_U+r=(b3VDGhS?e^Dab7)!ErusQ-9=efgR9o1fX9 zpZxc4t)^J_r1Q^NYk!;0^wI0@@9#d#f6ZQP&+)aDA?vTl=jP=65SXFD%*=f0`t|4g zy#>X1d_Q|0PBD7zvF-&|=GLhD53&o3ih^F;O){UnEcOP83ZaNxy@ z6+C==zXa}1*Za4-vg=vo`wx$q88U=Jri(vJSDk$F#F;Za@v8rBYc09@b?WJ-OJekj zzt3AQf5u5OL!wIH8?$>}+2igDT3c*PO`k5koSFD-W=Pmkc}6ydzO8zF%AZWmF#v(5 LtDnm{r-UW|i>cG% diff --git a/resources/frame_dark.png b/resources/frame_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..034de980c8b0431804ef60b1fe3a616895803163 GIT binary patch literal 5164 zcmeHLdr%YS7T{XzGqd@!=R4>3 zo%1`7*-cK^!k|g?nREz(Cdqfg1eV&>kx@nkk;c&JwQ*E3$uOqt zNCs)9;vmR;T^&~IVn5q)C_)+%5KyPDDlU*Yc>5o^aXa(~r{Wy@UAykz*3?w4%g#Dm zQGRg!cyGm_rKN&!2Zvp?_Fefm_h7|#(K-Glg;kFaZMw4Rt>zMT2DuESDre|TfU#OZ)YdgcUjcZ*O$%P}?WEV?#-xce?tMY~xXJ-ySTR}Mk6^^`;sCX+~>1Oq}S&d3u5p9yo_ zuqdJqp0+8~+2vwbNmnVHYoSXIIVWyd>=C-+p@V#8zUz#jT1#8o<=E!hy67biOT^AY zw3Hdet}zGC4(89_>c;ZB)sWure(S_!=fd|ti55)=xT5Cl4B7Ja&}TpNZ$aieGP1)9 zqU($2l>NADWq*^*G~-V6a7O0UtfhzDoSk!~UPRRGliCa9Ep1hc+(|O}?Rj1wA$POS zZePB{?;jn@_0eqw;hdrZuiPnH%X}6e-@9AB&0Hw>BoVtkQMmp6ZW*Xdu5n*h5ug^Q>8wtsc+VAQ#9)4yx*?|S&Q-utJ|vhtKPUc!zT|KDc~ zeN_2=aba)%Y1zL1x|DhKStn!mHS3Cdlnqn~R2$zG((`+gd{y7h-oT)(9<+5WHFc(- zh`y`~9;-Uf?~q^Ho&DANSQoSZ0d1pw-m-m}mx`7Rx_>%3;KR7xCuvSjYwu4XzZk4~ zuSs+)^MmUT25I4Y^abrL+DQ=PB&I-dMae@krB>}jsI;-9k6En)RR=+S{$?GaOd^ep zSTdf{h~Sw{md8uP?JW2VOA$= z447F2+i)@P+bU+k44aBENd!m9!x$2+o@8)+xIPF|YNk@z@H{%hPp^u@6nNk$1h^By z2}Yw1W3kfG(tOf5K3aV|3l$25EQHNsvzb7HX-L-?2{Th;@U%jVVBn-dsi$;CN~>X5 zF^O2M$tZ$hu+MnnpIRrEKZVyAMp*!Su*`&xh58^YwVL(3g~2FI1t6mijcH+s1U<@9 zkOr+uuOy|Zq{is^B7}l`7SmsV0O~IQsFY6~(3$kfHa=8J7MV<{!C402Hu{3kK{9#R zQwuAH@swI;vjW+ALDNXZ{SVKqJ2pO^j|7-M#T_#an=;Un%Q0N5G+Et~;UXAFd{mSQ zdQmL9Gs7$QjQSE`M3b(^Lb*il*^SqXD-tijD$u>J~5|&IYE#@E*gt6`CMNf zlgm~TOadi&OoFFEgoHpKh!qH*gIJ`eKq(W+V|gOMQC^@gTO#3!IZ_Ua3dMXJN7#sv zi}O%KirQdQN-R*TR}&!rl$wYqSvpO;ZNo}1CJvK{V73qPOcIt%7~_C}2o9w*CiAn6 zNJ>pc7zrzTC?CYno z7{w%dk}zuZky>rC2)05ntd>u=<)Hsmgpt4rBMCqeHV;GD7{ZTavoW>+Lw%VD2Sc9I z*Q%(v^gq+Kb{oTQB*np$0i2(16OFWB1er2&H*%Xy*?Nz`uyqkeC`Upt5UHff<|n`! z*-|DDns^e7o6+)oBB#bE1EC;J$VUVyGggTxnOu%a#T4)mg30C~9KMRDM0mdJ7tsyc zIAa>2C&lrAN5B;Fa<{n>jVsR2)C zY2d9dV?{0(yfKcCT@Zvr5l|!)G0SUnAdpPZ1xFhoXyR1sMS}{9-GI=+D3ePa?!N9w zb9Z*QW(WTKu%9f$#gPfOx(m@ooRQxRtBZ`Nixpk;I`$A5f`E=g>%Icl%zc&6@;hGm)_1($1 zqvn4PKCVuG&$F6;+R+emY{#7QoVGU(PiV@r>#&^4I##2t$x;0LsUWwtH7(zzJ*~pB zGUwb>7qRz-%?wx>p{bNN%#j_w8e5s+39oEO8PdEH%S-v$M}k{9B=OEJs6 z!4Y{qZ_Wf+{yKUsqRm+jrKEYe+finm1!r*5mKh0nP2mJYhi)RA`YSL zsqxBH{QR}0@4lq0F*9aTQn#neH50Ci7u-1lA-z=xw$!z+p1yv&o%i&uNsaxFHdNq` zhn;Q@4wtg)L*);*zt{NHRW~I)Bx&1K%mCdbiY&bYo%=jHKD7S{5(YT!7;E`80E(`h zSQ}HeI;`NrQZ-k-_6_Cr`n{pMXY~AB>ZZK)xuU%HoBl_kG(ql}ix*$Ntsf{UEGqgS zrgHIpKNe?_)8z+yKmORuF?(fGS6A2h#zuN`;J_JqG5tXqsi{|6TUt&mkBa)CyMOS;jT_%~r(Ows zFehhncGhpdnSA;D`x%cPoH=vm`rwtAcw%QBq8&XeXxNh8ktg33w;p4{}2L^nzXCneD~~JWBXj+ zC3d0)r34U7q^Pvp5Hw5G6}qZ{X#AMk0j2y*JK7?3FjiQVW?`MSmbxp@mEuI%yK@p8 ztx^?f{Vyl^eDC`{-_Q5^{k-pc^7&a$_4@qWALSwllJ9alY7iu=4nD8cY2h9F>oyvG zoon^g$u(>ojmE@)5CW*&5(Owo2mu61Tnl?XTyPfEjmA5>+uOARhup1y$!E4K8+sy? zvMtr!bX@pqi~YS1p7~|o8}4KOnWwF-ec&Iu*#m!EeCO|r+=%hmxx9&)m z@t`g5)q`hGG%t95eQ~m}%=-1GRV(jmZgzkCiP!1fNiH;HS3aG0>_YSCnbG#e3y)h4 z9SD#0m|q+myirtUdu?IiA{SkH^6{7199p~Z_&?ukD+&Cm;K(y0FJ5wdi`+R~U!1o! zJBlEh4xzl<<0>zos0UWj)3(d*JX`v}`AwHPD}FL>Nx@U^dVYQ5H(2-Yb1PoE@5$}8 zMH}>^TZ{8uon5aCJr?~pcj&XB;RAggeW-3r`yo%)*LvFFRW3g>sHzDf>WPZptc z--aV)Ii;7v#upy${KLrWAEi2VXLQ)MWqaxedRFZJ+v=0a1w)@d(G&dkrId8LFS|>l z*=0FB_fqj7+I#hA{>9X}pVj{Akzr{#=LNlUOS-D z@2bCgdFOARhr@pNdfe%`pd|{;miAW7r3(QME5O9dpntRaL4th zm#(hYIP!jH82{?&UA&-r;0IlFO3=t6(NzEuTXr&9N~ zd4F7ZBKz)@6NA@MmNw+@Rlj&Ec`F%>i(|dQH{Z-EDvfRX^ZW0dfo+!% zV0+cMH!z$S*0a3m2l_-9t`od$YZFnHYXmat2SFiX$G$xNHiink9edQ|Cf(6;&>%Ql zW5C;5?c-V-IV+E?U6X4|FpwY&WEM??LlKEd*fEutfzOJWz)%$;H`=i}w+Ag3V*sV~ zw4TH(5<;^9Ta%00Vtjz9aa4{|z>ytmkmV>t5b=0iA2;g7SdgHsRx3do2!jEK2wZB3 z$ZP_SNXr$9G=~F7Tug|{f*3&+PS!6r$#x8b^XNo=;i%g^Ngt8MRe*XB2{uYldXfl- ziA)bku4slN;|YDIhvb91l&Arc*c9VHMKg%V%QGo>Zqh&66bq^4@Eid`APk`rj7m)j zx!&dWOnN911ch)^^@3tgLCQklo>)_IQ)bk1G82LBle|;V6Sb>gh;q9bhsZT4>A4(s zOo`9%A}8>S`e-F7j<*0Fr>#5*Zvh^+nk;@iK${H~VDOWEvo(Xt6_I2%!U2T}lIsP? zW8f_o)44dY`wd(PPSd=JHF0LzNO2hy8)E|ON;Z_4l|scsDqx~{qu*-4 zOK8%F)4&R|;Y?;6a8?qsldQ=?s#HA3REn`M3zt&}vq3;aBSCdS5u91)aoI70J`GhZ zo)9Yspo1OTAViuH_nBv9?J7N$G~SXqquL@{K?lmt-4b8^}Zw-e9Gtb>&SBqa?dhB7du#Yd71 zNi(z+Cyfl5ffsop(DHp~W%Ho6^pZOT3C3?xP3axw1y7|%>0wAvw-Sn~n}T7vbO{pM z40tt9$d#Vr8rVn>z|&*AT_^0qceFylz><8R1mGl~Se)ia3itB>c80;gvs}Pr25h>j zNp?vL$Z<9X)&-%CP%GG;s#fSqb)i;G%_ZIdlvRL&;gkWN6wE}83r36=Oekl@M8!7Z ze>AbFfQ&;1`lW5~@PcO{F?kq{YXB`4~vmk=ax zk@C?XdwU9@agOYASIqg>T-~hM#udxfZiJ?zF2_1w;@THiC=EjDPLZWIwiO>MJO4Or3W)=8!KoL!Y0|EO^WC$##rC_sFa~VOvan8D3+W2&+LC1K-Ps9_ zR;h~A{ufKO?|t9r`}uyqpZ9%Fe%|J*T2+|;oqPa5p{LSa4M1)!dR}ZapilhIn;G3z^TU3W1}q#<{q^7 zz8<~VWqYB&|4MP0^VPXU^E}M*!;k))pUD{J9{lI&(a?`}F9}Wi()P->YdK%MPg_+= zQd7LfG`eX~p{K3=rGZD{{}y^b92k7PyR{oLZftq0psLMOJ$Un(vi73c9mAgt^f6G z?T5iV?R6K=ZGB~%@Y>7%!%ttY{ov4vy6*FJ!@EYdY~OTv-}?#Kh%Xg+YL=c54kENoKn8Kx8xX#O=XU$CFm=00vU3jh6 zhn33-h%qL{L=hD!snLut%g3AvF~n87S7s>C$c3+0)i_6z$z;-$w3y^Xn4}#J2T7Sp zvzb5$LTQSre2R!Er5Z(=!wnT7A;ncmj$s-nACwzZ7mlNOY%ITM-0K~uk0}`ykRD`; zkCU{CBBN0<+e1++8WBk*p|A8%0%(_#)liWe5(2DfgfX==n?e-E{o@UZh+d8;kT3$H z2&$l{^n{SBJYL_phbBQ-ipF&>B=!WPDur%|H6b@`MlUBj5#&D3I{`gbyADPuua|Sn zLW7o`$L+$k_?##UlE~?gHkM&*wB11nHiv^?LNO zfEpDdH%W-c219~96ch=oXtNTG8M1`KB3cQvm4bFg6lmT?Wl=nskWg3hk?gEADiKk! zteJrpmL^z+McGiaNboES5j-vO7BkJ$_7JO6i2}D$PDFXMoKlnzLoyx<>l2#b+zOw^ zg_}+3sJg`$;nfgwaN%pDSVQU-G$2KxU*$D5X`7X$trprsGgb#%npLGa8wpv7NtUkor^pvokkA)#RJu>Y&W|zLA6)1tB9cGp#XsgXaFc!OwaL_@D;CUW0 zqG-3Wp|q^y?1~&xlY9cM2qPVlR;WF7t+2)VLM@q?OL9HbRsjh{&}L#>Fbka#jLZ~F zYG=k+#ZL17G;!*HtV0I*rETc<4d4ei&vnL-~H2ugqF}n;|f4D#)btosw%xu1P5{ zDezQxP02MW1ttZa>aK50uKZiqDHub)f|BU6v?X@om*}Ep2(Dh`27V9#{*rsPu0)oZ z@ya>{fP#72I|uCSDniB?s>fR~<6pCk)ACAY4+gIyQ?JLpB9OXt^*o&efU)n5C&iJH zpAM96|9$s!VlFt+`8)te*5!br`v7Ro11(2$0fqixFcj#+b#DMLYoR`z6bSizJyRAa d6I~1U-#Y3YaO}$IkssEuQAAKfB2%Gep&s?%MEptMlbN5oM37DxFc2&L9+~}o*a2A7JLPmqO z_qQiTFFCi$FFMhN>Lj}yy9+tOPlu@a*urwQA=(KX{GQ`<>a|3h-FXiZPT`1*9iLhz z(}*!vCo7KPvo>3jd?RzZLlZb*OMx8`SmuzusNb3DRq{pYc?>hC?eQ z%|}pN+HQ%qF}|EUbhOh1EZXZkL!RxOY_x3^*{w&ljmPcu9UbJ&ba~7aS*U6>@%8%z zirTFodCvtXMb+%q5zvM;JM4BF>(S-ie}lusPBhXE7(!=Sw=7%6{SF;w(Ozpi|+wj4mTD)P~jK^UTShe+66tw&Ar=mUUQ1O$XXzvIGZi)Xq|9cLz zFkQ4N_Uz>J4^!mIgVm9tWoYIkP#)uPYwut(J5LuQ2o%0=&{_x#l_Cse^xs~-q68}x z+`}o|VoeeXxWp8t5{`C(d-RDKF3S>}Q7Fn;)ar6eY9=6dY0@MoLw!O$U#wXF!>$2!vh9Pas7;ZB3KHWc(_DL@ z%9sL!?BxJ-gXR0xKq>7$*>)+>Y!j!KCE>QS(~Fay97X&?MDrxXgy=>}9ns;zf9g;A#*X8;L;Z?&(K3ULoT1HFmd>i3xO1N@g&W8F1N;YUB%}#X3Py%pl17Dw*26MNTHrNU z^pkFEUMA65`MsZNmzUPg{sSTL@z829@r_yZ;QDT#a`&>GgnU43`@YqjGV<>_E>B>? zVIytEsM@2NE$0@20O`-j$?>phX~;i02kfQ|Ez=}`2Zz7IPNoBffYF4@s?Dc`re6Q| zL2x6D?r+k@33boz=vO=(K6DCR7i#^p)|oRGv^kb!SG;T&^1a5c)p0qmAJ_7V6elIV z6!bN*=qbOsVOqm>)>DDZUa>}D^z9xKH#elZPlg_7>w0OP6u8-sH+Pz#cM&!TIsUJqhy z9xwSqDsIhN0;0RlD$4K`VzweW0j=mQI@U@4bA(dnQ3PT5~;ujFWlLOI1o#iacuo76J^T~)QX zPUF=KPf4M*+aLT|?l%^WMC>%AhlP&q+=4)>TP>^lkkNYjh%BY7a3wkmNIk#$&j0;TCk^_S~SGa;p zjfFDP%zaFH+Hoz^DPah+4gjQuiv5QPi6^mqzOw99&~)Hv0*s)J%#A=Uym3k*`-%Kc&0#%29Lo&IE%OJIKU zIozRmIpK{w;?yB;`NcIvLvpB#iIiDJPULrE&`z@mJM4SC%Fi%!yYVxpz9UPg<(eIP zdEHDmSh)Fz3G%L)RUe){LdXPU+Ugo9ugGt;mK8iEq@KJ2Murc)Z1Rbig`lH}3Kbu5 z!XMVoGKlikRI8$bhtf^0SUqU<_@T{bZ4MSKlv91rjSk`2#^fyR)rYW$fcH-qjl0Iz zr-N(xbjnSUrtDl`VDL9qi1}6MfTJtRgVd4n)9YMJ;zyjc&_~Gr*H1y2r8wq{>?<`% zZa0EW$J@*wOw%XKHodZF*mWvv1gUd;b%jgCwph) zFm%{_C0k(#1#@~jr+S7ivRF&nls}sMPbTFb5*}R4QrR!u%jXUM$;B&$@KI4dIp#wS zJ>6m4Tr^25pkN|SRs%QXFu^om+c_I|M%`dHfMRY%g%GoO^n&$Lz~u9v$95@X_gLdf z4tJ+G4}L?MjC$u2Y1=$}5dRwc5AAqCe`#HlN5Z!2dcq_6i48X{J7+@*xW_d|g!5Nd z#F{RuO9s?)vZ`wB6 zIdB@{ZKf^vwms~zr1Z~QhGiJfJ?>t3`Do8ZyOfywUu(*{{3;7Qlfl~qp`2nz>n^te zf!n=jR%Sp6PCB|LJ)8cas(6rM!4j@naX}d3MvnXx=ibnTi>l3 zF<6~+voxF0{_2-nFVQ9b9dSb#b4eyA{mBp_#6!;(m}@USnmuUNZuR{2)Dv$2pIoWB z>Z{Cy=8m6EAV^i%_k&}py!1aU7f?xm>N8&z8IFYAH1j>`3lTLYAsK3%?Rj=m%DZN)Zy^%9ubjC-q-t>`Vb(+sB~B<*07;~>yx zHu51~^Vywsg7R*3TSO~OmwFo9x~hPsMxq$f|1YC-F(w56mx`tCq}iVc^@*YmL1k{}m&Ik2>q?;6#OjHC~$Q}=R znueCn9+ zC7xB1)|(OJn)bk5j|bm~o7DP_TU2v?yVs^>VmJ4fu zK~xGd>;8aJlWq|od`b%{gC14R1BcR3N(}x1u{rq3!h-9myMGVw-Gn55bhf1bF&x+2 zuOMMCBvIHFk-1yK8?hpN+Vf7eMqXv>2ODoV*=6n|=WL5nb?iSHj-mT2BEOjFZPpX9 zyV=bb@+V*@5>pttdbEWoinMl(#Fj^H+}^#^)(f@y39iSDYNL`@z0BmsnkF~f$wo3% z2SlO}dZ{j#7thx>`Baf8-Dt?^U*=vWOc)s#R#1y=h@`siK|n#DGSEtrEJv&aqcdh= z*B>~9Oa2oCzwO%R&CjC`DISDTu>{ zw7P8E_?S{Qy|S>V!$7QJS2AmaRlvVJt)UIeKlN@(+pB(=&++@wc!StE%GRi2ge0vD zUd-(^U$9hp=qMsrunED9sKGbLTj!zfsQd7|=z^Lz%|9;_e4CEl4 zdQb?Z;eYzH&^{QRmWS;^e5`E44#b4Q*TW)DVj|Lu4BX01$|Q7>oU@m~{BEh!&KJ*{ zZ8oG-_4k>89y~c6G1T3YyyeMtCa1A9u|_L4l&B_(|Flu3mQq^uy%yWX3`n=-5QL)S7-l zjw((IYBVcXAiiw$%h70;8OeoHbTb{Ixgts5-sun$6htodNxq@5%tdGzmz`X1;=LgM zLcuGOBrHCYo7?SHKgmfH90-~d9$54fnKri4xSXppQVgWX4I$NrjVe+og!Fi{ffi8En=@xsc+@z(mPFUx01F5&o8T!RvaW9s z6PC4lLnqVjrLlq>8y13i(n|DmLlLVis;5Esc3{hitGhxKA0tdlK#ja@^WD9>xFWZP zQc+Qn`5G@tIT39b^)+sMer8GPz2R(F;($gJ)f|xF zO5kB*%h{=+ZQCX^YI~m289#R#d^#9#LW{S%{vTaFqYG*IWtb85r|p<#2j2N=*xIdn zL@ojpMjgT)VwGPoE!B&Lg5B2Zx1Esi73eoaDe3D*Hpugjp}K-(&~DI(eEIQ46HDOm ztJFrM!uyW{QqA7M`3$!~$>VNn^5h_F4q&_Ny*IuZ&nV09&DFmQIN}g4`zu-P*(C)0 zzT((1n|w7~xo0IDoX2IMd3jCh8vyGYzhy;MIrcA)zOcjq_6zW*2vKoy+qQqqx9*8*Wf zoUd=w>NXvY*6L&)xQNQjsii5IOAl8+WCy!sj|G9o-MiSIJy}!59E4fKG|T?=fMzs~ zWvCQh2Y<$mrI1f5u`{IIgCRf?O1TdJfi%i01m_e7l_sjZmr8xH&I*nT8t~kG)wk2K zcG%^~xK!WLV7rn72Jg5XRcW3MA9&0ipGDB-_CFLY5%~Y3-*gWC z@bqtFQRHEs;ZVyyD-%|7a2+4E=MI#RP|k54hMh0Uw|3}mvrbNMCx?Fq1GB4yJb)j{ zAlVg7$L>K3BvF^#bLv6AA0z!c;OJLCJYFeO^INkRGU0}}B1iQbUu%0{s@ga9gAZPR zX0K}@*3K=rv<;)ztUpVce+mT8JyO}K;^Y3LrEDkmf?rT)eZJDPJEi~w2y}IrGA~?Y z?-*xz6Mkp28U*F%)$u9~@(!r|v$6)mtP(v@oU7T0k(%&VEW;Prj*#7Q?|s*!Epkh{ zS;t%pD{#ZGs`N|V`rj%)YVhr>yyKa4Jo6~vCXg={dAwhLH8&+(&&7xW-wXCy^d1B9 z{U!={=PK9G_`g=~wDDiSRX9u2at#r-??)J(xxdjM6MZ$40>Cw?W)DEE0>im;CGjUI z*X8OJ3xerGSbWQtMQ?uUqI^c(vX*W3d!Ivdb2=K|mR$Dn?BH-K^%;5#aJGtDFOm6J~)mC`3 zr2@Qm5D?z_0G`2yr`_)I61oui(E4)P*476aeyL_Eua$IWV|&)48oq3Jx@bZ)Q**xi zUp+@DgdT4$EvxR3!^Q2wHZN0-#_r7B@3f|AFp)ZYX@kydi`nVdP$^G^g8KoQzoe4} zkt-eE8znr9Rp|u_c-t899!$ZTSeaJ*w5`&6%z%*3uzD=U*HP|{MbAiZO>u+wTSlR5 z*!}9_w^n;M9q(Jaa8MBSRP**XR10CGmO(1xpbLHKOx~_qRxPDP2}x{{B!y) za>F|gD(;s8%=t;zmo<*oCP?;8sc(0WBp0(0c<_x5==)xyXG`9Rv?~VPZY$Nb_s3_p z-lw{EUvDzlI{c0EOLhD_<1Ge1sJJ;QVva2-`Ir|xhQz;cFXBl7Bq7cMY#8kuuT9iH zBzBsc%Q^P;4Rzsu&SaDFX$(h2Tju2<%D<<=Q zKtTcraK5JS_-()CHK~XUu=~`Pm4kFd0tw5Or-H55tK2k9JOmXAT@~6fG?ZX!I!~xhO4I-KEj3Ivt4ZT zf>`vIJ?l&ZL@_W#!<&onOJL{B4IWv{|fb2E}K4J?|*e zZ?ayiytJyz-5I{2nYs}SA?+V2@9{S)-efQJoyrCU@+}`Py31sxr8T2}<1}6G5cOIk z1u~tz+VGxQWE8kgfUCekoxB&l%}FTyLAaIRMgPw6PdBpkhsG2VUa&Xm3W6!w*G#lL z&Y8*`2oa=>yG*Qn!A~FFGkICE$D6`^6g?WO+|BWjmL9ch(FOa|BgeOAd4srDth=pY zvJv%@G9Cfk@!+qRP7U!kGFJX>)h6s_k147gEZ@`yqq$g%l z@!ns)rd%SWOL?W$xvR4Tbl4Wn6%zl^RobdEfca9HOH*i?SU(2|*}?kW-84gkuZg&D z-$e!~F*QmF)X*qunQ2dRr_BzAMLH0;C~Dn6M@sI9luwX&5VBQH^Y5HIjEFdv^16{h zPSTd5@qXdp|56Q>!$7i5uCK&McJIu&xW8|4gy5XP&M>3A}Ew7uU+yC*)-V$nS zIc8BlJWQL6J4rhu?Va}8jU3H3waNIiwHXUsq1%5DGW4&l;J>{98MHS{t_P{q{ZE(q z=Qn~W)1eO}#BG;XgP+nKihAQ5dbf4uF;=*2uF%6US_=fbxVm127dRmg3=EXd9h2nS zN-Q{-u15$QblM$vUY3xM;KMeD@4-k1DI5oX-B2I3B{cHwY?p*!ZuUty)=V;qn>R5h zr#3tzQjtdD_X+K{R_PkPc!jPlCaw1L*=gfm>)zmwpvqpi!BE@&+|;rP4f$kUf3+N= zSYXW7A0_5_f`6(OtDr3gpj`-?cwILoUin>8v4`Udl+wC z>qx9Ps}sDpL{dJ`=f7L&Dcbai|5LiOM=lJO-Bg7^H%@eypl7*>aq)89%o;wQ8^vT0 zD=Dagt>l&tq=r2o|4lFJ)?Xq)>!;M9`6tELj+r(p+uy|c_Y##AiYJB}p(MRLE*(&}f3p8* z-RDU?ko_%{!>FT69!Ygx{jaOu1xI~fw6_{{(jEGpjOK#wz-Kq+EhtHP0eemb7DEXe z{s!B)(9INg)4Zc3vEXxsLm8d0)6qs4g2E?gOCLL&R!U9t$T{D~FB14K^t8T)upu38 zuFzR8bQge_E$99P4`HczFicjpjG%hhbW*2zdZ={vWA3QZj&1)xMatHTk@I(oLPjI^ zl=o+tRF1z#V?WRMlqF?}Vc7nx*?m*7=yL;_!Q>%UQUNF1Xwq54zc55z;oE*8cp$Z2gKv2P@_LJc9Mm&;6m-tb%Hp=h;H$D5krLPqeH1ajVY0|3 zPap!Pii7X1{qV=k4l6X&S-qCVp6@E8vsfPPZW{tVNSw{V2$=dyTSMu5tq+ECfblrs zm&W8igqMy3*f8y}Jj>VVfjiR~M6aCKlXi?CdHG;0^u-E^9?PCccx?bFg!PZz_Z zkGl!2B__L3IkPzz-_XTKurMia|H_*-oQtdzrDko7v1}8eBNO>54KCcY)Jhe%ZbM#78=UQf7MABIK+^*X z`tkC7(#+H=-2R zPxRyjMwt|)G%irI%XWPF793-%@ewK2uxHf-~`MYaZ%?5WM4Pu$JH zc{}v?RLggIe0`~2?O3~jB;D5#Sjmr5&=FmJqA0zgXoKnV3l|@Z6<1OzsNvLzj+Cz- z^l#LuGp2wGOQ;4?JK0)iRnEvwSlWiX>aN7u*xt!b z^gjd+Fl~yk(ETlaB<+LSqK6X3Pi~`T%gEX|YgB~(L=StW6Ede*_sqQ9_^RDzc4uKQ zB78x;nqb=vi-g;0Q!W`hdy0wBWRVyy%{%`}8NieGXFYpozw#thPwP|z4mw<-CTi?X zr<$7;JKr&hh#MHgS^eaS?%<|1_-Ezb^=FaXti*z&zY*9GwPbMP&gGqC`}zy4djMHE z;Z&O=9!#HA9|zKI;aAgR;r9Cbh0`#;RDj3l6FXtf&DuIf`w}JK1H$vTB}@6K#xP8T zeRWPFEY+_;Z1*MB)H5yw+#y`jWBcCPZ}hsBqMjC_QLlaji~F# z2C2)4<*SPvG+>}7NUH>Y#@tnm~_55lz<8vq!sOvBB=-@~ULo*2W z@Dm1QwH=A`UoqcBJvxS#LDK79Yr+3)4nleCkRB`>?9wiaE!=C6lLbbkff1^y*eBrxl41NO=%41uj@ zZp0_c+YPxr=|@+E9W5J>2hq559Twlhdc8vV5sih?t8T4a8KtniZX`ru7J&^wq+TVSgXiJy-{*D zLZI<%k@BU63ydJTYSxtFbJV^2&ueI#P|$~A^>z{xGueSJ-*+Z|_doWTfegQMwcZy5 zG_HH+sdoL9?Sz?O&EjO$((5{rR>n$CYQ64H9!t^02=cz>9)f$Z#%P8uvFskc{BsL zMU8;^Y8A-|d{{$hZxkiC$K)S6`_b!yrYw-VF_@?TABt(J5omSI;DB z7An3Y)_!eR*SxSM?07E?uHCD7r^ME{zrs8_D^r`QhOw=eD9tr^QiE0*LaY)$Ri)0b zt16;XqOEUBMa7nm=ej7)4^)|OkMh8ZYV05KfjcHAtK8s9NSmksQXD}$?K|%_7p0^B z9*-^01)X%boSqaNwNekLcE?}&4k4xOuIG`PH%^JqERp?J)5p&+MNe$Q zgZ%=}o&P>Z*8C;svF13p$wRHRJndV`EQ%)hQ;%QIMi&)PF^215LDaqq8H-p8P;Jn`tScE5~a)8D$s&pepeXkL!4 zIMUb+UbJF=@xrjUJ`~;$<9EORX*0z+y-ZdgU<5s*7d*EK?r>NU^sJ||`zvtHDlyK7(e&GETH z3{ZZSNyoPcYf6rD!^rMih}V@r3D}vgc4w%dv8-fpf-BgV0Faj_9YJ7qd=P>crr;y> z{YvL&+b0I?-2V(*u`$T>%^J*OCNkNJEZsQvNXvvAe#oLg+e%07qGvgsAZ0dC%aQeN zBQ~SM2}&JxWpLQ2fM!&h_I*%PfNE-ssXS+P9YV~`k9sa=hbYZr^+wf9%Ql(yITI8L z+=9&E7CC}IV3&U+?f2@WOB<~;dRv;WIf7i*c61g=Y&(jMTyNWtk502g{0rddDT&p) zGn+_a&tU^salYF%sF<&^XIS_f>{0Y@kzw4Pja7?nO+0>m+^cGUUDGRI3>_B##ZAB0 zA6EN|9H=-GPQc8nf4*^(?$kW$;)&MECX*Q7Q8x7$VIg66W|*e338@J^r9`4bSDwSz z*b>2v1plh`idiM=fPT_!60RW`d~_5dp8-4V;%!?~THr8yDKrWwbMDZ=H{=+9cE}o@ zKw646iQVm!5zD>>thockJ2%FD-AyS?D{*7aGV5|>K}<{wrt9uC2ieu`J;`jCB@z4C z{9$4b;V_h}@cw315VZXZ{3h4rTTfUQV6tMGa#)H|2RA+Sa?+HtFqel@*WgkbKgpiM zf!2-scpCYOQ1B5@X>~H?ZDtl!!j|79oSch#D$GZIlW1n@>Yt6cZk`WK%JuNPsF<*6!q7CB1pri}pV_)3SmEd#)%%HqH-P)qK@r@*;u&t&`N zK$3)>W>5IHjh?QV;9`O9q)EgMa)O)iZL-=tpE|blRw<8ms8QrkvChPiJ&##8=jv5RmT_N7^wC{$%q>mP?jRwJtdDJz(M#lk5=$ zUm5h$Od{;eGr6MO6); zj@~St%s5ncz?Y+}-jBeFBXylaNS~W6eF7~-&7Rr2S#51Ghb{hT-k(O)VD=|!T7x4` zisuf8&VB`-c3`4+%g-)H;m^}W6#-u6Q1BkYe5FyyqIX}j%uU)_(>8X(&9AnoC-kX4 z`_uPV{7oI%NE;H?>8=M=l@DlXAM6|{5ipUzg*NEwqPrMVa{?vmg%z!0`J^I$U)bC{ zTZwNa9ZRSlpDrEb%yOkOMNs}|GUr&Zg0hzpUR?g&O!x8tQ~S>>7UQ}rZuY}K-rJX4 zmTnY}>m$Am;s{pakBReHc#DsY2XEYF6z{qW>*Gi*70*3a?Pr&iX5zW=G493r-RK)$ z^59y#pQ71FZw}CQw-<+yOWF`Ls+Apjb8TU#ulzDwkb`{CJ;IB>wjc6m9!;0VD#N@y z9B&jYn086UUbAgF=SIr?Iost?TlZ=LG|kKkeiS6D17NHUD2Nj!tRzTxe;Z4#3#?jR z9q3jbCusDkRb9hQE!T>fG7^z0BH_{-5j)Eek{gne)a*V>LA`=B}&sR28Kf%l_`2@iJWp;bUGICF}lwcm-hQQ2haTV@$zi&0;9Db<+0z6_=wy5o%0dLZcjUXW( z#NAOTPh|Db`qvBOnc9I&B96%#iIPvKzkzH+>aqH)2RKZun_{EEm6yNk&0w zEqgTavlQAZ%GN)zLJ$tpjTSroTl>m0?jyusHMr2Os2LX_nZ@g-dpMu=w93Uc;xv$U zFx2J|x*FHEKQz0`80|28_-^iSI4vsTlzX|8m+tr8qwTo}Ogaq1e(1q1A1KJl-N z{&~3n?zMbLSRKwr?7jnlH9O|FNFij#tRoJ!wCm!X<~u@Jm9^WVa;CbgjAu~T2>+6S z39oqW(nXjR=$x#p?k!WIPgNtRN+;!Ky>_UuKTAGa{ZIkE7S%y-!ITC^(51;2R1unQ zla5Ebr>Nl*=P><4E`2(Xc|jEf^XwUInV5fV{G=+t!c$$k6^B13)eFpqLN8~gkROQ! zO&XRhU#El}l|kCaz0JsD;sNqx38mZB;}hK0DkQBs-d=*q6sTq%e{_26f_^yS%zd9E9JZtH$ zS0tnSC$ly92vxV~L?Pv$8m<=0#zt|Gku=@hWs$ybrrq6M+2eqTD#@~OjAkf|G}pQW zPad)PlUIDrpKMdRR%{Nw=W#TZuG#5HfrmlItoTpXm}~HsWQ9u&0BY!p6&h9f^%C`P zV)AulOMOn^*wXFE&FySEP|2iJyKjHbweGM?%KoL9$DYOG4I@pZFJS5Zq4D7}Eh@Pq zO(k`Ox0ks`$^K*4K<<)6Igi*Z-Xk67bPwhTGn$vh2PYGUe!^Ct z936N?GA=#(ab1^Y>C``MR?W`fppgOMnIb+?BV+Q6u7cXuIlWQtm;@6c>k^v))!r7i zvl41{RX@zam>RVBxymJUn;oM)R4tDVMmgnEr0ocoB~(tv_)t;%U!%7S=e94O(x@Tx zb|(-1@cqN%)DpJ;7Z1j$tz%pcgR;oO*UxcNeLOrz)(YGLgs;{*zvesW(G=&4+W*5K zEgjsnoU@!0zf=2QwbWT*l0jmkKq=UpQa}M7>b6>L()?RP02W0@hw*$-wj%>M3-5yW z5HfjDjx7;$jA!f5SBrm?9a*?T3@qq-ve|5XmA7wN+<8W~zoxRaAHDUyKP~z=9IkR$ zcQ!-qEC|^K2ek$WeyTHl#oV{;t=5Qp;$r^&tEqcqkF&@;0$1&}XDw~#)zhHB6@7g- z=bO&929uYLay{%UHz?EM#cD|DFWu)RAn3-adttQFa)e#SeSKg!K`AtVVf0Woap}q& z+PV4Rgfl301YNbCDRzyTARQ(UN37Q2THp}Xc;Tz66R_SJpBwPIi(6&`iFTRLn^yF& zsfww6&kFjS7FHpN6&3viMWy*aD-D;uora1p?iq;ONNjX3OHr{J$D6Rogf8>1SGbXD zN0eUjgsxla%C`1uf0gwTx1;E7m=}N~%y-w)VS`gL3<%U5%Keha!OB5U<;NCz>27ZO zicV3@RE2qw{#os5J0FM+LF&$rL*pjlE~wnp%Bf@0Y>{Ybycw|xZ$6u)UUqOBKoi$Y zP=0vHy`hfK_rXRf5y^WCMKzZf8fbq&WB*|4Jg58K3-75S*BzG3@eVR6Si2mfl_aDH zf^AEa0ua*WFh&JJq1y`hKpi-Qn#jko+Y2!P!dL>z{3nIa2M`nN19;~xV_E_wiJzri zuVpSL^m;ri36>9u8@iT;>lvKJ3@JEQ%7P?4skH1P3N^_9r5vv0!*ZIMqmOmQbjx&o zMwf@qij7gI| zxCiadRLOn_0y!0;9d;g$7FrD*ws;4&v057l0*hE@th0Bv0*0{n_|R0%=)(b@GbHu6 zG3{6&YUiJ`msnbwzAx`W+h({yJpW@;@$2k!`E`q@G+3pJDnjIjWmW?Bbdo zaaDjQd9G3KvtDPSC8f?qyeSQ?t6>8K0+W3IW1eOCj(+e(^cOIlwMW-QtX1-j1IqJi zPTk!$50wiP#wkH=xQZYC8)54W)nztar#SJ4{dlRTqu+x4ExX?qpi?*wF`Pv(dvEa? z9&9`Z(z^AvBixt#v!zObd~(mxw`ei@UJP@*z;CT!gIo`H#t#Hj6xL7RMfLKszDs%BEWFH#ckpZsy43=L15fe_YNFytBYO za4hC*KXq7x4|fdTQ!hYqZaoQ`Q*u~~L=Ohe0>FSq1dRrCiMtLy1fCZ}??G&(ro*Fe(90kqF z>M|n{_jC{XyyZ(_*b(5Dof)CdKvQs5rMV}*Ri6Zo}ncLy9Rv3 zCQep5z$`bgbvsje`}@IG92;L>IN=O$h~4yx{(Go;M4LMUQp4wTWbgj7)(>Ek(sQt) zJTWQatE_`2wKgyhqF~s6pzhYdK_mSt#HBEF@n5fx67_J#rvX;eD)t0wwbV&jL{jI%q)QPtYI|5S)ZyI4 zm^`M|vy*ap<;_JR-(j~Zt?e*#=rr7K89w)PSFyiYyWgtxqe{@GhV0Mrxh~{b-{pSW zjaO!R*qbkm(FE%VTkJP{+zJVv3oQe@&*8Ag_yzTuqKOmTmC2`Na@H}LZ!N|0fCqAl zFZRq-Ri-$*rzLkzB{zgS9B}MVue%Fzi75XRaScvg1y=3!RfY9>ee=xgjnO-Z#$UTW z@Vee7k)rBwPU#f0t_ARzcV)HHRuB7*run&C6wJuYnT_83jYW{k_#VFvC7W2oWMcL< zMGwlFwOgNBXWT5f1O__s@MQ|gT-q{wc_-03jyH+dJX_sw7;>d1?9;@TMo1DahN$8@rksqxj=#4q=1RC;9AZ~FHts%LJaX5nNp3N;iypw zHe+lySEX1oE^MJMxnCjW16EbcO_J3)jf~diqF%Z6KYDBs}wit-jYz`8y%lh z2-3HkHnTGNMJ>!U16dfvvmJx=m&&~#rt#vQtf^z=Hn(H?ReWYWI$?V4dfTA-S4?An` zJowSrGVbTVSSg&7U{9K2G-eq?<4KSnbwg5PVK2g)8C|bl{?BLk-`}`@e@Gm0L>(H% zhVR)$oYJC&&(io#R#Z>d4tq&EV7%$MZCPcK7WR9y} z0czLba{+F<33kK>VL4PTkvs7CPXX%xFg#j*+HTiOo7Bej$ zem|+3oo4$oqK;`PZ*i*b| zXI%O{ocyw{OSYh;@n@p8!<# zP|QW&F8+VL08%o0GX3n(%%3D|Rl97O`X5JS4f;pL?UERFiKScj;7Ab#8Vi2SOrYuXmH6pV-&I zU83!UMNh0~>mOFF6t%fMUUdbCG@M+ot0PXf%$u4BD_-63aM;K=&&5*r=~|iC&Ykl2 z+T(Pu2sWk0CMr2AG2!yxM}t1tGcs1Zp?5pF8bb+a|Kgqkwp2en(M(C1*XBFwGcnpQ zT=057D@HLkU0S(M;2d$;5(vKsE!&1ag%v_6#ShWKuNIt8(;;i5GZ*U2J1_kq^!GVx zXj|j%?DFiM%IbretEj-Y57pGP5@Hi#pMCUyVOdgw_7`8)9El!-t2W{;s#4Mn|msM9lMf=FEE$k^k8- z&-LZsTv@++N$nQ*rx!PPV|au*T0&Z#g}u1oAF`wuZ_t^T@jsehO>!X5>TC*5-1_qz z-|6_RN>Bob4xA@XA}((#Vtb>*_%6IPs>feSo3g zj`*peLzL|wOs&x4zV4{W8*psJEEvv9*_d(%_k1Y(e3wq3!`-xd<}OFr^Y+BE?z?jk z43(krYLJPHc0g#AHODX9#axxr)K6m<&+>1u#Jn5rsBXvMo|y?4y(75!egexCIY@1l@-1Q54DSPJ|1Az`b z?ji=WnsXqQ!t399nmO{`wA=-3-Bv?fSD%J(lg>-N3G0aitivjJ$?F$h-W{BR&fe?H zlxonidA0+#y++5fkSh$j35^-2bI+UOj!S&|r4~KF`!i}-vZe=j^Qt%O1q`&NFyTtd zdTiCZ5!%{U6#M;=b}|_5mcwYGqm>D~;X{i4b*#wo4RB_fa-D*6_VZB#n`pE6S^C#f zq}5)X|Ef=t6do~rIz}BVGZVapu5sQRGU)n(U^tm|i<$a57XMC`Mdy;XTM;J2c+&w|*72mYQ>d&y*(J}KEv=iW$PWe@{=H^t@~lT@KW$8a&k zu7IWGQmxzzyt@b|{!98ga6BpQ?{2{5!%~SkDEjaZ)9D-e7B(i;7TBrxe*sNQQCtsU z)U#q;)hm;Jn3K90@#eNLdd!cAWTf-m(xJ1XycN5f6!)e7+|u~owc2xc%P*Hw|NY*v z2L8U5*C=4}Z9iQS>-kHHT~cQq@+yo0UHwuTOeh~LvU6mp_8}sbJvPdm>7sT&QKu%A zk5l-5{5jMdRBp6V2G7lmqM6lXFCx`#GS^+AlBO*$4>>55kGv&ire0h!Rn?%B!ZIuWFmE-ssTStZ|zbk?ayuk>WfbxE=DwJ5tZYHT{deWWF!lTsRKbB?#)F-UbBI;tm6T;9EgFifMaE@1;Nta7V$_}-qk61}= zH}bVgv;^EqxQ3hZiVP;N2Or*vPcaoK>3k$36m)a2;b)wh>yzFk(5w8t_aJpwk8X&m zfavb8WYYBR5O(9$SC2+|`5^OLBjqG^Qa8V;x-J zv1b|Q9Ji!=>_|YKa>9N3^W)R+{^WWxTD+*k&0gXBb|3SJrSy@2DK=ZJ@awjg$8KzP zVuS=k>`Yo&1Dh|Tnf*lQInE)%NLjVpPX~cpdY*Qu9sgC2S?XMe%=K5d$KyYLkfr{d z*1R>YlEw=#ng2ZB+c*BAKAqcaGm3pMvr9_>q|_bq(s;T#Awm*e_j>#9UtU>ZV)asS zcw*uYX=E3@^+GPpD{W*Yt?>E6#aS|_Oa@+{t4&)?a#DExV}7x)&(SV|fBpGE@Yxod z-{uSI;U@L;`{f%=tug;EY2%x$8P;qAbQ!34awq z_uhnhHB(@9se~%Eo@5OL6+4@WA-%-3J|S)@A{ahCD?bCVlpWfw(I+wb54e)sOLI&m9=F zZJ${%gBLKvZ#wQI@@z{6+LdruXpEu}!Ses`^o`M( zM9tPuY}-yIwrx9^*tYFtVrydCwr$&**mm;eeeb>BTK%`r>9xALs?M&xcf-^Fl)pP7 z&6hyEm|#C$J~C!yRjK$mXJVCMIS4ptsxV-LXS8PzXSuuzp+u?QpIuFL2lF6wD8lJ7 z8v)ei_lQ$?1TbE7jj2um9b29YGTbdI3znGlS10G@v~K|^W4EDt8-Pf$KVcArCNCw& z9vgvR*oU^!JP1^3DT-S3SEYHPCY5U9V0yC;Z4XYtt-AYR} zKLh)zG`@e2%%5CWJ%L^ne&W0}18a!(`@uLcd1b+wx1!^11?7pWN?uhd)8$4pnUY&E zJJdgqc`n?<%=x8+m++(%$Whg)ae=Hnf!J_oXn>`;uDG5cIK6vVeRHq~v-Us5dH|RB zwmCCG#u9(NqGWliresQ2o@G)q@@Hg|U>1(G(4xP<%G8M8TG;C3iL4Y#@@M^5@Ct&rN%RcDGvZ#J8cavN@&yYHQe#u?fRnzWASK+1v+8x`ZW;DR%AJv~i&&;X&NR*j0+xl1Cl9*((fj5T&BtwhGT7Ql7s4Tq_DTN2sS`$78{YLIx-kDn@C>yWVqP6RHZSVYW)=H#EXWQ7R_L{@vub4_kF-h*8TQ zx@WWOmSh?pRAVSF8{*G++%gKUPhnj)4wf1d!pzdeZ2xxw62EO2Z<|oakDS9Y+fi7=Q|W(kKH(Vh6tO6*Qa3lN+3 zl_b1SsjeI&)*IGDkOGB54*4xKR`W-*XoE;gVUyH{;a+nCLc&X+&jALNX+SORLqIx{ zR%k6%!0HctXeYm2y0FEx&v`2MUZFB+d~BMS&D5NO#EC6m7~hBDM|<3^{oe(V2uhZ;g48i4s6X?L3 z&D*Sq>ky~ia?Akw=o4}DT*58@TFPgC4u|EiRh0_=Mc_I^ zXx34(iu3YdHK14aceR>sKZm2qW{RICw@U%2SMU3VVw(PZ?|Jti=R@}*i?fVEzFsc4 z^gQmEiL}aJg3z_%A{ngdI34a3Y`1V6-OfcXAVrJ9NYwZl^!W<$A zIlh^CHj~D}VitCe{j=eu1-8Z#rvoKeY2}eGTo%xKy$plwR5&W*5<8P-u|f}#Bgynb zav0@b)JDS`;-pC6Er6PTg+q>P-9A{6@8bK{ztbI|n8yrUJ)g#|?3i9s z6Vds&4bhw&sG zF>J+g8=hM8SiE@(HCZwdLd7NaFTz3K+Aib{QYAQ?+>1!-#}KowmV~Dk@np55L6iDR zl;wl%5+!5Q9ih0Ea-)uk3QzH{m!O1XEvA2EBk2?hQWcTGTQhcc3RF%&v1Qd~mC*gp zZoPXxdxQBt7vFiE?s;s_`COykAoKjcoXU2I*h@^B!OTEjgjL|CakBx^DF}1P+_zls zy5SwFftaCIe2U;W0un{pd%vItJX7Tx<7F%R-O*q+!w0te9mY=?WyWtJB$5lsf6L}v z9Ft4mvb*!2qK^oO8h9%q_y{U-qxU)On6P|oamC<1ym_$Dl1G`Oia>?f#{GoBS7Duj zhn!#w!cnjo@MYp0sFdv=tC^oIVUl!Bio<;B)W=$EtJbJG+bhhXEd)pjY0w=OGC}#3 zrLjVmok7Zv5{0LCHt}g`CiEh2T2FR^f>a&rkph#IN{lc=zzyxAA{3Nu48^0@6#;o- z@dWoaD4-xO>9fIs|FTY)>wMFG!M;IF%5EIFXKz>5l)@SC7@GVOeCEa|xkMV@L-5h3h*7wvnhwoY;Z{o>n#l zM+i+iW?(_-aU0!u)G=`p9DdH!fRD-H6okCixup~k`S-6iR4G_=JtLg#{PvV~Q??4t z$VxeMI$#MYyEA%>5686?us*}}8Abr2!?4o(L2uFtS z=`d3%Lv6$YQ#C+|JVs4f(#yir@?IeYkgDVItYYX2#2mQCUrv!F+KY|}1F#QjJ47(p zQ=iNcbM})A$Ww)Ez^`lV7$Zk)Ti_3Pb%0l8?72_I5zEuCRH5=#Ny*hJ_w#>iiw(hp zD$f!l-&}I*Oq>Nk0GkZkCz0f%Z_@IL8Yy@H*9U56@k)Dqlx^MJy>C0!`n7(XUVne} zJk7rE^dtvc|8HojA$;Qna&up9E>JBOM8d4YbFRM9l@j`&J?bzA=UejvTHpp;YneZU z*yrTiaK3q7R5)>5z#;i!ub})No2`D9*~+|Zqc=)h`!5CnkMJ#(=K9qVT#vi$Te4IJ zIP$0{r@9csH5Zz2er?G{W^8pK^_)vgne2Mo0+*e4m_|zvgCzOeJH4pc54LI?yxvQJ zt+;qUxka+dbJ%j87~~7GylcQ1W;Rq>WWWWvL?U68*9(-#nUvqAsnGGL_c0vTojzB_ z)`pxGY?1n1d-IDq3=1^LALygV67Ex>ydpJ&X}yduASL*U<>I7$C@6esy!c*v%0?}k zt5B>mUh1lMxpAx!#FP;lsGM{Jj#S~UrT%>zO&RWBq&F)e0qX^2kzQkVzf3|xKfY`C zh@CPDPHUyt7+ldT*<&Xh;b9{Af%DY5s2}+!}-g$S?9$vvPm*+^N?RxdZ><!Pl-NaQ%>czIUDgAwi(UwiGeXk_sDL>mg9F;o2EnET;TYl^GR9p=A3C z>1bmtv559t!>SiYoek#SvaqQiw?iUemXn%-r%`rkF8!5~sR>`prb7a$kd*nsEJz2I zl*JUT=Y1qSpX1a$KVoseTjrJRn|StahdQ0&mj8jPE-TFISm^FonZ;xuRp=fb*D3SXcVkdifpVtr5r1=ox_a$8Mu$KaC>`>wQkm4GFmUme*#$xaPxxoFb8wJUIk&N zb#c8Dym37%g`~o3o&P-TT*|7{c+M1!lo-9toC6RvTv3Jd(i) zEbmbvPQuY&n8j3qxx!ST=zv2oo>&pL9qM095RjfdBJDv5&1^LyN(AN_7{aurA$7b| zZMOikxE?5!B5L>~EC7-Da+8ySJD!1(XbQe^v49Y`OJ4x-s%aA!{GLx}GH}NDk80>s zAtj7pfq*cvr9w%JYP9hJq&OTqhHPKmNDd+uY?qT*&!T>=q<|$n8QBRLdLgUym%)9k zL#QZQiJqBHnx~ut;fC!)v8>V$=f!0Bg&_PJ>021F(l%cj=i&q08d0n?23A-;fLvWm zzy&002v1rwA_V*;E5Xn>-|E8g!>=RQh&m$!bc991iyR2Wi%FvOS0HTZA`z+nVtfL~bpt1>SRuRXq` z*V0DYS__7fPBDr~$vh_HpPW3yRX>|M&4M;UpD4T7$e%%5-51CKsaBQZ5OBC+4=1Iw zNk+{G2&*QhjiW2MVjEaBvC4+bkL2N%(XI(0F z{ZAy?=)n=zX$wtR^X4Mc!VZdM=A&J8X0M(-h&;aVSSOyf&?YOSt#4SXHi85h#VR@f zB?#@I(2iEi9x?g>k$%Qb^@*kp9*c;nn(m|m!(vKSZt6Z^OKrkr-33CRYuwufXEb2Jy*$P4mG~Os8GMEQG z0$;3YkdSvn)T!cJI4OjJ`DC};nt3IcvO~U?gzji0RlB5we6fOoSs4yYmT#?vDX!&@ zw}pH>$eBpFX8^kY>vfS;G8Mgt0@#F?g+%tDT)6{xtHC+mL608=&P`i0nBhg2Hap)z z2X9}AvHYkA?Brtn@&f%KridIJS*xE|ghV33VjdmuS91d_elFPu&b}O%dpP*I0uUn_ zMMjbO@J=N9JPshgN!?Zi5|evvCSDHKj-(Rtcjgnu%%(mzv0CZ^CAI!pJkOdeDy<4h zcE-|!l(F=!lL@V9r2xssPFzs-#-jNmx)zEuj3%9Z!q2FjWJqQ3@o^S^y2`c<%V?85 zqKMIGfE88Y@)#cGWLce6VISak@W}?9Xo1CO{HdfBBOWKp`|6c?H)&qXcmqs}hE@Ot z4NA?-S3upSu%d!EttRrPUu8ISsWBkxcF+#KZ2|K9e*0}deYRkz*gIzL=k`jMpWT_0R2 zIh#SW>q^I+{hA(&NK#rw95pD;VgrYy>^J|il@~DgTtmDKM1%v7FiABzDF?b>cBb7t zTaKYj(jf7lLnN5OJ>0ayAV3K)=QYT9Fu5jy+7IW93}Ex%1xkWCQYBiD|E-nS=GiRen1=XJDpmunKvYLk3QmJY;iZWu z(0AjUX!I_$OjOAzT@Y)MecHO2iH0@-4)jH8 zQ~RIRwN9)-?Oxp1np->;SeYhNZC6#77YHel1JF>Vs)!+Gs7Z|c!X*JFkP)~^Zp?Zl zb{k8&X4m+t20>Y=UBmQ3{=^VrN}c`+Q>NO%NfayOWcJ*^{mr_6wOsLy=XoTFzr`ws z0DL-*(NDd5=_{*^F#3l3yd^&AMUy#nelgW(2MOf2614cUqoFDh7CW7sY_i9RZ^6t# zVv}MV^Bs!${emY}0c6%sVf%CBv{4jO0vp+Ni8$PnZCE6lH}Md;^$@sR@yPpve3f4V zl!L~@R!w(8ItPKjN8)rAt+2r4Bz^7TxYLX{U~9t@B*lbj+NevH63(SGK{q8-peEJP zh<_mltEQSGg*nuH-^%+P?_uI$B*}SESjiP zG6g{mSFi-ndIP32URF1LX{9u*-s{sP?ll$vFVvzs&*P%nBf_i99ppMa(FB_$o#p`&t4Oi zz&3SQK)5fIomeXcU_F`%14l~!AxY^3njN85M+r)B7i0rONvygBF+EsZSnx<_fe(LN zK5NV{N*MKuB+?UevezXdO{Cvn~D^wI1ZLnwTBPHBqzwXwJ~j&r{2K z!6V}1$)zmjz#VEnnPxi|Dt9K)!%SW$xLOd9^>{YBE^;&Z_Al5B`9xsQQ~naUjHr99`@Eg~D4S6xg^t-K7bV=p>Bl2HFsc~@p_Iz)kb z58y3KpW>gp-5f@kQ?>&tAilX>F{Ibls>F!Op-D!wd;A~RaDWJ;OTQ@9C zaZ$}Y2nVU^iQLnz8S{{)snusMjj`CO(nq>ZPVq^)()c;l102INhkK9OSNv$PcUbwUv*D~f-REz2yl?kqlg{({fEv}|8 zEUxzSonV?oAU=x^?#LhwukQ;|t@OeXtCWaBF8sHuKadRb>E;W|lO$ZnWI)l$fY$1TRiS@0W4f=5(y2 z)(!TZElzm5Vt=_&owxDCYBLiK`qDhZ|Mr&E?t8z@Jd;UXA|Dw!yXb%>UlXU_m0H#@ zOyLl-Y=AFDg#Hxn*uLBK5sR zGIRUXpaUy?whj~m4P@+g1{L-Sy;{QAtoDftgU^hmI`J^B+7vlfENDU`@>jS%z~xuf zw{m3X)PnDB{Y4!)xQ~w4mS=QmB2$8Z?xiM%Fv* zs#7N~0~;zxfkI}Q6(iXsy}P_a1_HSzT#;3CL^cEj(IV0$&Zn81F>7Dp8nu9;zponZ znIFIuB&MUIfXB0DSw*73=dvQItUyaF^T%?3mI1{gN&?L!HllfwvblN6l+*!&248?o zl(ikI!H8pp-pdSB!N~@ME=Tp1j|Ed_Mj8wouTVV+P1A4m+YAm`ZZe^9|HL3wrJVB3 z{n^ST1#;rkq>#8Fg$NvfJT_)QFa8vi=(lIz~=6gPKa9VLq$IY!dUPx*1SjNC& zwqC%-RG7yb$)n$Zc8%$cN1BA>o#+k~>J1AM2Mn5wD(`B;Mi4%tQBYWvk`g}R)ZB9f zU?F#d{A|Blq?;16gB`^!a8IOVO&ojb0-yW*BAe~l>~4&jhy7a2?em~9@3aqFnSwSA znuLw=x>O^l44K}ssJzIZ6|mq!4GIq29+ALuot~Q zAMF@YICO}doUvM#6V)-SLpyWcI*Dvy}dS3 z9>>&(c00-@9a4O(*a1$}_5208RtT3Pw&c()Ph4dX5>}_CDH~7064aIC0z2#K@2*FP z#RG(pn-@zli5b!eB|%hIOhuu{P;g!5Iq>RBtR?D!~z;X50s|| zS1oJ9P)OVQFF1-taBTBLy9&7DgxfyGc1?Ycn5JN9ePhU`8Mh-!tur>*U*KB)n`%SB z?Zcuv$^se4X4W7v0#lv>f%9qe36s1Nt=usM%-1%+#UdRH?4)>+PzBV)!;R#_^vIMG zV2}1qUYEP;6%YF)KPKHE1RPCU9hMqhY+N+~67@BJO)CqXJN>#l<=FO|@ii&FP_V^_ z3^Q94NG?+^%M@vhh43nAERZI&W-d5(V6bA6{y6y4&^xOHJFgYTm7G^YFlP#;Y2`o^ zj{Y6tK#M$=aYAFA@gEH*EMw{HM;r}iW8$%^H2ZX{W+s4GCRzu};lP=ki|paLuXkd2 za|Y3a{?ctTC~z`CumXBnhdiS!D`L_paT zbG-&8ct)M@zcSDkB7}hecD&aZ+=I`Z`dX51vaT15C#sy(7_l4&7AW-VaIMCU-V>Ss8`mWcd&Hk2?_ zDFh5bnMTNgp@Gj@n89hh0YM|ZB|%IQBZN@FQP>)-WhwEGIl8rUsacUc3Ipv@HJ&dX z|MTc~z31Ts-^bVwnylw_`M%@Vx;5gxq(9}rgvNbzBkp~d7`W2?d!!_`z_Cb9#@E*t z^Ag+-A_R&kU^Q?A-)VgA_1f{f+Z zAmqlZ+HbFrS(T+oD*t|bJ{>-jQfEeA+eW_9L%Gs5Lg3d)lQIz;HaV(x{^;4&)6>J{ z_w{kcb^Z!7^?hA&$-IKo0IGoR_i>At(CPhpXK7|8F7k(XUaYkIhsYM&{W4VQj?e6r z!wb0C3q8CI#phD|`W*QVZLeov-OfI1{8dSP$p|gMl#@qhVEh-<6yoq<3MbW4PFV6O z;ej#*56mGc0KNTpcyBb&Beq--ut3!au7FmzU?>={IBx0omNH_H51wbLgKo{?od7I) z@EE+K5l#6Y@|J;2SjNDn^+XDbS~Ad=4%Kw;rJ)iohBFCfOV1CtodzPXB0C=%#av^+ zFj{=p16{zsLf0iFYFyMgk>QA>SWNtKL4jR^Wo@IhhJ05GuY+hjdLhA>M2FQ z9HY*k_}CDYlrqcMN-%^(#QR|7SP8Ap`utr>h( zrfla6h%5Q6pX@^~g3&=%-bNZX|KXooEfQuS4G0T?Ofd6;rp0dPa)1o&`v#A;g@=O_ zBm~@>7KXcQR{4-0KMY~|Sa)vz0XrzbE~Pp8CpyVEHEcN*X$&6mAS9$fUA2YDsKAYl z86}@c!)6>2Zg#uQkr{=JGy)uaTVH+qr@F4V14l}x#*i(aPp}S;2GZM>;aQNNK1QVB zyRkY`hr@x7S!tcsG0b{TebD~zk@cZzB+9;_q9WScczbhm(_f}sLO0~zh1Qz`%JE1I+>Zm*O)3or{Z2$T zWzPYVh-=^!*g7EE(|kqyXvUnfXLwaCUGKM`^gf_wb1~v&^YAZu;$TE} zL8-^l0OYc{-L6~#jvF>DI!a3>4|>xh&u6=}<9rG=V^VZAb$pdgd`^Upd%OEX6f-&e zel9S8FZc}%-&+hjErVA)ifV1XnvN3m_KryOL6d0u6besQrp!YcHvSy0zbm~R0X%HG z7QBD_Is$rk+vn)-;(@-#(^&RSrPf(~?iGxbHBS0?3 z*)<>!wS$|1(uwL?_i8xJOmK%6s3HZhzHTyfg$CWHr}TEIq4)nL+t|zcn&s4HjuRA- z;o2)7?DY&V=N{>s@M)>>HUinmF+wULp%h7LkqWWn25#&SsY$$t0<(U}P+2?y*d3j? zjQh*s&M#6)*06k_cvqteK}B%kNTyms%gJ$rtLfo^1V)^XaDY>ia4MQoVT?f4F}0hN5rw0^CTLI3?rGWJCo!AR9MU zCtD2%RNA}cMdjg~GPkbh68IQ*|h$;phL}jI{>Ip%J@J%yZ zWzsB01r<{vZA?O>oAv{L|LO}ibAOLQS@!0$YxJNL&I}b?2{i+rFpNS?vh^`K+al(O z6H~mkO8#py2p927vsd1ast=7cS%q?N8u)IqDeEoA@4lx2g~IQVCtZi`O#p z^mCfMLZ0V#u&ijUi$d;tYh-BuKgq!(rRw1j{DTj;#*nflv0mKJ@xhCVDA*{pBO}4L zYPUOAyKan5@n!UM=u{6Ser8d#$J$iB;tZokr{igLt-&m3m{#A`_jJC9`)^UFiRYR# z_%F~O_SBy|l}D%R31|hhUOp+s#q~bW2I>WtPjNs>HORI)ZmWD2hr>Rf#&}kx_uTgO zHZ`CVDNqf?5hI`r04Q2o6;TSsgepegMpg)@mUWo&|EB5JYdeVH{_?4Ml&(nD(T?=B z@OeBdIG)b(dIQJD|M%nn&F}DZv6d1s$}81%|L=6+*y0J?&~rBGIczuSnOZK}Yr9i8 zgvyZmzD%Rcq`tzR4``I6XmsRvldq6J{l?zj5Y^q#Px{*dx3_P2su&m;Py=zVO$vmb zt<^QUbpG12pN39#O9?cgMC)zT-B?0<_B&s^TP(3dBDD9gKb@awz0c?G?Z+F~JLB^i ztC#9ZuP^{pj|s#+R~m;dt=HXHIDdR&|M?#yzaL2?c;7O1V;b}D<*#wf@rbs(d=8Xe zcNk)SP=#up4%cfI+2@4Lpnqw~C30PhGI~Kux<>u2ke=v3*SO|El_PX!a62i>lW zlzAB-p6}BufW0CfQJU-JFRre>j;dHv>3&H6BUe{f(vsVTq>F z{AkcR@8@YjK6HA^K2hFxNQup)DT{zbs(wq=X`bh!eFMsXG%OET4lQ99qL#o#Nt@0mH!|&MK8Pfpii~SO z$rvytGdZZQOABt>uN6P96XvI)U;%pCeil4z{nD}$bZ6^a^xNADErH;>fw61+wBmQ! zkHomDSEfig@8VTVO>zld0$=$D6r|4&E_5kjXt_&_yKKcDSPpk5Gf|5O8{d>Ager;m z3XMaO4tb8L9zsiQ{qkqUpo8f%Fa5i6NC*F zYhn~KrLcHML-wlZf(S^*R-Xx4qX>keZ zjE+sb^9IJaY#j8-W6rH@2K!jY8>@P_6-T!jVpJty+KboVbH9px2C_28Lb}R=1ISiY z*XC$7hb8(=mRUO_yTQP0j)xd?JxwiF{#`>IjzPBfX>VV<8HmX(F!@A+;-Bdqw zd3njpr}AM(?t!>+{1}bq zc>(MWX!7k!pjTV4BYX8!xC#C=yVhW1cV-4!tbfZOkc#>E)U32+%mIcON|^H%5qP0 zQ+{o!*N&D^&)SBbp6`5%bX{KA1-uGX9~g@rnLX-Yp0OiqJ`~MpCOzj~E)#YSWCubD zU1e~83W}lRG+ql!f`XfrsS7og$GQr&8sDQIeNsvY$fr{?{7=3(jY*`C|6)w=Bbl6J zEq^2NA4+3x5#2BN{uI%-F!Iz@HnaioGG8Y?IjU5>n>J4;FD3Wc?6f`gBb2oAN-5Rj zZ?WlIcGQ`dD9&3}g(|$CLsTs7@O#|-OX*&Iz8-I~;_qx}xVJ^};PtnxW%a5of_L(y z#sf+1?~rI*j;&s<$XVT3d(UWnT>>_PM?lb1lJaOv`}74|7vA7VWFtezoDR4Y*innU ze`$Og!2X-;luWAp$XK`RTBc9mPm%o4O8dwDQy97%b!!MXosK3&wQ~fYH=83dX7Da9 zyCjX@HbmX&g9TS5DClKwua~GZ$z@P`30-uk!w3|Y{BX}dUf#(8ZdEJiH59M=nvWe4 zuYWBPFq|Ma@sAlBUejQi+ri^#iq}@J@3qmrWL4G5-!cH-ZIJxNrm=i{vfrnjAiFIh z3D40cfOe)y-4E&>*B-j!mWgA!O}h!5C5zJrbC)|;> z!&PtRg@(e3(hlpx8B1q+@N(4Mpuj>)?_zy@TYVMn{4~e90Tza+vq`bXaF2=&Y9>Ex zlJ2IUF6F2Gt-w$9u(|q4rY_LS;^MOoh^pFK+C9#8C?01_IFk3W@REBREq&X6b5904 z*H)IsP_ofBJvn{n3(a@nY@GLMjo^^r1a%8Dcjl}9jV!^i(f(6({4a>Y%OQP~zfb2l z<{3B%vvIHV_)M_+&AGj07F*Z*auJx-J4{AgH8{j?Sj5CipxL;H#Pypfikv?jQD+*C zd)F~tc?-a83n^pmSWQ6O=i1|0t%kw*M~dKF{MhZW@;`gyvYc{B#KjT7_v6?XT7eXs zU@%+UATGCR6(y%;KVRD^O%Xz-83?f4LLGDsEk~=C5IoDBT@TpA(+6vLa&O1lhr4mA z3@H-=?as@Vrbrl*I^%7&Ozu+Kn5VV<9Tkq&w(m23C_eQuT-`aTZc)${ZHwKtT7>ZRq59%son;wZgDV_j=H_qU^n8E zwdF2v(L=!eT2`M3<)J-w>fWaXy&%++CTcT>%>P(qxfwQ*oZx^mGSzuFf?txF%w
  • 54J5dd+r`A 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

    LaB3?GL2ic9meh(SgC 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

    lo~M4C=!p4GRVvH4 zB}tJoX8c?QMkFvt1WJAAuEc#7(gI!z0I{{7P70=i2e+==2BsXVcPYZ_9g?Tp6IIXe z!=dv5U{>dMh11$WKIPQS$qJKJlF@&fi7YYi=@;w9_aM6c)r)O^d1vDN*qA$1`gJQ- z-wgHqeS@%f$^S;Nb9);5k`9C3x~?I&z+w;>5GrxH*l}u(Hiv#mC#d_>QMdek%)MGL z!UZF$UXE$^kc2FiXluXLDP)vNC{v|!sw$=IS(lU6=>^d%QtfbheYZSBSJcpN+@MxT zr(C7!E&7;i?%;KEDtYEs38)?4S{ZEvt-Iq7j=*t@7{O1Qi0Qn%>bME`-rX7EPddoV z{YJ89I(R6K%#s6Br*QdG$rW&9Z4x@1yzLWHqq3SP1tp)ysPs9m!{x$Pl z*SRL6To%p&D#*OiYMLGMRj&ySIgJ_Di)JSN&5A?nndjo zBpG70iv0N+C!Qj@sj+2P=&Ex{Nr4YFgG0}vk#0_#K!`sydrbrf(!OGLlgVQhOOaCO z8X6rOv2CJqxc)z!GMNR`RF)hv5I!TzIlDN>fx7+zB{4O}S){pguyjst^b&$`A*oYJ z0_yQH4)|c>;6vLzP&zh|6mxdT-#-Rc6dqIc)YG>#v}nwaNYvCPe)|d&zEJ4P+#TAN zRrPJ2;6Ig9WBc2gNzK!9{q|P!Ch=jzcypO1fg+eIL?IhF59iun64`9NfySM0`2G+p z`tA#Q-@S>K^jPmCe4e$N<_^7Bv9H3;-_7r*7Q+*LxrL_VCME_BIqz_yBiIEI*nVQ=g+_~JPw%ZU9 zo3StsR)$s;y5L&fJGuc@Q`MW`zu9o9sNb$=&(YF)yo7(3Xi{lrKf}xZi~Sn<{)PX= z%|BJ^d%><>iJQgghen=Ev(9$yIV{$Kswf6fs8lRgUGc6`$|G%h);+F9}R(qde9h(mohED*ow5T3*JgKDdjoj8=DeFxG;$a zJ;_h{$J^dif=&V~&4kG!=8n<`WGD!3tDQ=Ge;zK6B(h`Kjs%LB5>28>knU*-;r!IK z{2st(2O~vYX_M^5;a`4_SGiGR0~<&illnl|ZJTbfnjuzaGqWhuj@b$)*rE|`6D0k# zQ@~pOfEl`kyk$MB1=L(vLAX(I`OBh1IE5f}#pVNpe1tp>wuu!$DFUfmKmY@hbCe*7 zlc4|~GSW;f8&4woXoFhlnH74J=5;&JkW?-j@9;)t;W$*9e3(O~CWoZ)0r;(kLBu+B z1xJ=#peKLWq#J44`gT_NGEUAPaBx1Nd3o7S=E%nhwOF*QKwMhVFMF!%efHbId~TK( ze~J$uH;BaT+0|ht2;t@m5^r~IiQ4_&>&eY+?w8kFanEg!z8Tv?;|?Q<>n0dnWt zcH8rsce6!D_MEjOE)uQ5QgVG{9$djy3Cyn9Dsp@1ygZ+u*1F@&G7K11rqTtd`{XoI zsz8TQDs=UirW>I{CJWtS%>^DP1$Q>)+q|l5kTMQcuj}w403zqZR)=j*mCyquyu>VE3li$11SXGN*!YsTel-@{yK@}I|?B=i`-GCFe5 z@j1qfQ&~~$noZlWR*d#@0i0-9fgXC@o$m0r{jZi6V1Xk(Cef3HeKhGH$@HW!zf}@x z=VVBbSxfc*iBUlQV0`AiM%`}i95a>4yQJf=aiuY(%2%=)N$V&=G`ALDxI~hO>h&%m z($z)k>8#_0^9@wXRIv?)Y@pCQOLlNMS<8Ty$4rufJ zYr$%e0ec76SgUaagOOZDO719IXRgw`zFP&8PKUlOEgd7x8L|Fyn+@=0XfU+)hl&N%1fx)T0^6F=6&ovT3@(XR9kPuvu-nxx&@- zysD$XmA6MNaX`?fgepG!hhnkW$ty_RZ6R$uJ5KeUkHcIZm(*VS(R(ISbG#+ZY|B28ZRc*#uJ}WLi!N z?0cK=6jgzXJ{oH^!ete5^i7!1z5hos@V_z30}hD5e?*f1ABzL>CrWYb3Qd`)#zQ^< zVn`5U1#CnKv5O*IoWaSsN>zHq7V~_qvb3>+l$OxSnPvCeY6bAZ96(JpDJ#BCI=HcW z#c;3XGIi7Ere`$7(GF&-4=F9&kxc=+MSQh@!%zWQ(@LZ{Lk2j8$I66zyY8ddt*fXAB2HZkDxp{#YY$*H)S{4^!i8(|MR!s*2d1{R+mpz3G4DXtLg$4k3JtWp6NYPb+M|V1#@2fW9{cRhHCbGKo@r`B8+$~W^L|L-quy5JuzL4C-v())PmtxlUi=qXDw&2qUMe13Ta1I^7toBUd&7ivnu|% zE~~89j7Ka2j1OU2=^#}Evrk3-6ukElX8ZwWuUV#8gX=9SvoRf}V z2$5hHDikmu7ACu>YtRj%L4Jp>VHhPeDoqOzjtr^!<++ExeMV^nuG1?Gu0VA>pqfvG zf(I#ox0;01h6eRGI2FN|07g+k)>5{G7G-ppr!tM~T{)r*jJU-5$Rs6;-bf0ptkmbg zT_~qDRUsVNX9T&`%s21)E2HV+_t@K7&8#3y?}>JI47>l>rRZwP9Xa@gmVauq%GQYk zB(0v>1b#x`15d*aJ>QJ`?V1?_KE5HK2On}GI?M7eHZoo?JL7W)K_ih6^_##ihGeeu zangv>a>S8!IN;)%CK5$a-(qB7nuw#7ecnO~5qk$Z-90er7?yy*N>j z9eYmUOiZ>Cq=#$4NR3323cP~*UA0qI0f|Aow9X6!uTE7TwM5cc9LQu-x=UwzfR#Ts z!&)kKX0EmU$hmG<%f~kU5HbJ9pVsfo zSpUl8lBJ9vn^5)Zi(b+)0AVUfQbLHDbdEzDEh+$Zz)#m{%c4_fcR>&0l1F*j+qOE9?rd@<$-j zEByY9h|ho}&iZH}&~lIFfzzIkGuwkQTxbYZBzf8gKoFrz$A6ZTD?NnB$Z0Z3x_Akx zH#a-pezte5_D$gBZx8NSzxJoJt%7{dqQ<%Z^QFV)vt#o-!k#f`Kvm;D_eXFL60Q^- zzVUFY>ty3<39TIiB-uBy5xN9~A5oU!KT7s@Wp73&wgKBnA~j`sJfExN3)kf3vIqmUmzuSE|e0}&m^Pio*q1L#SIOb+q>FPEAoG;DF)@HHR z@-w4G>c0~py?oR)UN=;{q%l$GdW=J>!}gJ5+uN$4qmX|c2>jQ)4Cb&sXwxU4oWQfY z^WOr<8xrVdqk`(&;&sX@cM^oO_X=WbD45US|6!CK5a>(o(ncwGbtcNq{>-> zCajN5`7q&KQGM64)q!aHayDF-a$UkA>4s@`h=v1(Q7}QuUCuX*RWwY&`#UeMK9eGFJZd)krRzi85cYk9l6It8qQAPXf7>38`3TK&{J8ncwp^-6+ zxyAU}gQ-T5f*64lM`I?b7e^7$LT&^NaylK6q{YiJ^h7?X8{eHDn#Y35Z9AU*tIBmH z%XPW+V_(Yq%B{OVE%zJk3I1vDG5GM*18ALHBdh=KbO;fm(No-Dj?`6h)TBkju@M^h zlw%5PYb#&1yF(;UV<<|yFh3_m%mIXeiN;y=;AhmA#xndh7cXD_yg zvwS9H{bZf{&Tpvfe9xf8uIqQ-WFMe86JC6ekw|L-+t6;ZSgA3zwR6U5skC!@%?p@N zOiX%DUVr?`P@qSlnUO;QUnubJ+Tz`G*!gTd?}NOprvHkGsns6&dUaZTarlLC02|Yh zx1c<;w_lNxWP5SjO>_3uzPi0A=Wh$VRgwaC91kiUqRD!bBNVK7OL@Y z$EsicmX#jzf5kluIL7mA0b8sjVBg{iCb%XC4QqweVETHpf?5d;7qn2j3;w~6)9SYL z51?SbTcKXGPLZ6pCa~9b6Q*5?FW_`XFZ!&X?gJQa%OPB>Y0Q9;6#%NKhO zfm3I1guM+_AgWOimfZnoKy>;fpSia^S)}Q1KR#>DB82C>Np=JFvI!(5OVE{%&^w;T z=k9yuwg1*9=#8!~DAj?6I(>hYZthyj;3b*{h_cq2aE54C&G|bVYrthvr>zoNq*~_Li)k&ZOw~*!e$V ze|o-mehIg%o+BcB-)pa1AHPFT-;ML@fuCw$yLNeVV`pS~x<;lsLN3wgPjdIYDW~tw zvEc9>(>Q#13DBg}s%>!i#Q6E_=p#eCl#cC>UzEzUrmmT`t{-eZG5Voaq*rXA1WIZ^ zKyU+(n;n!%D z?*-v=!j{IodgS0D$)xxk9@_F*$qPI}{km31^OdiN=B3S_-S2A=;s4G71MPnAc>JU< zTp-nz+Dunfllo&@%EIwHz8o8jSXq^qK@k+E0zGa+tI#s6 zOE4?|BT4Ucj#j2z<*Raz2Y;B%)C_L3K^C@gM3hgjh2=}^8E@`t22yh&e%YbcLBVFD zR)S*WA~c?&BW_q<&iZ0c*+x9tILsn>m{W zKsN|6$M4R<`@X!?!_j>(J4mTGx=$%PE3D(#tzECoP_UOkCIT=b^h45tl^S+44}n1` zOw&m*mGkHw9A`hBQkwY9;^dk{&`;vlU>Rz_>`BGjuS_ZNMJX`nsoZY=aB>{~Pp9%z z64e3&Rf&(twbj)lvW!LYh$k`eM@>DbXJ}Hg2lo>XQ5S$Vsq1(VVbP*Zimay=<45^D zP!v3XADn%(g|M}y&c_xkMoh!IR=$FfP8qe(fYl7<&5J%Nawse5=HBhRIzg48%+b9N z^+b1Z73=1=D-NaT9GLBhDLy#Gr>dIR7OyckZbra|MG!4QGvkIT*NJ#JLD_6pNC8oh z{gcE{VBvv=v9#g0aG`s2iBMebkL4K(y3lS=#QzSNM3Le$N9^>znq5P+hsD$B2(9&b zo%op$`6hh=wJU5BO{Y)`J8Yq^q@pQRRQJRX4#-j{9(L+eWRjQq-^2C;J#JQ_2iw)OfUiYU9 z%#N2wGxz7rt>DR7B$m_^*9;Qk2~-^#&@2b5lPdx|$zI3a;ML`*>}JiA-C16pKW48$ zLK0hL`HM^#$+pI5NVdGYQkU#NrO8=p*!;`?e{-A_$e-1qn=bb~oTKC)eWY9zL<^Uv z1=3~`e4`@2zQSGF`F1^x2->r2)JsVPa{)ym&yCg&1p?J#{mKMtXR<;jrFtPyw(^x} zLG~7K$4l#~-fHBw)yJ37b?aw(e3lD_U(qvDSRIdb~2y6|J$ z(gvmboGU1P;IxKIGt;<|Vu-qf#z?e;#F9sbl&g`gIduc+V&G|;iErOr55x%G561&z z)DDacktx(|2KRmxN)A5G+4&PI=i?*wW>L;BbXoTW&9?PkHh?U*^HxfQ){HmF^6SQ& zG@Q!V1w%Y}hJyCI{Eobi4J5Hx8RM6h_`6J8F>Vv-d7!^VXVA~Fs_7U*&+*0D_Pn#G zl)E%))GS_Srl|=rsW6&-TtO2o?9j(*YpR?yH{@(GjFB`-0;sD?=oF4??gHq%N|j;6 zk7egdcw#RNb%KrAz^t2ffP!`6*^2eUfGyV6*N1z5qyf&)n+v0%;$dMeuCI}J-;Hu7 z&3=zYGrKMuuRHzj`v{-@c;3_P_U|Ci7H*CGo0~dUxH$UH`rSYzB2&cV9-Y!s_YT~i zzd$M|PiK@ZUOD`6o2}q>S>fv0`v1Z3uBJfj)q5_w*z_xyO;LTRpx!-w=s;54sLSUw?IeJT$x= zp~S7v?z&Vz!T-nuxLITdyqOy0BD6VbgNlDK61@{`6ETx!@H9p$-xGK|k_q{cX#YYs zO=X}0D!cA0NS2bxMrzVEGIL9%=Y7V>@jJ8lPt~uP%hzMssL3KnAsNC;pW}@}`dad#IKjo7wK8H2aqu>l<%qY)%X%r@c#u?B0Bhl z1J{r(FSp~(WW|>bBw-r*ctuBtgR_&ueGMSd_B}Mw?)pIH`)csR?|z&2Q)}^#aohL? zY~2W74}NwZzuO;U;~#uo0lEHUSNk`B7X(Ba1+*>`s8x51hzxJimD7hZshi~@Z}QA- zXv>TzN@VldYt|!TwXXMxtW2O3Rnwui?Fd8Q8yDJ&NhsTn=D?Is{Lvnp$j%Sj82qtZP+e3lF`R;(K!Q%Q1&gVw z^<$+Nta^DZ*V`>nQQnknXN`Zl#-!e*b;&P+_c|J;%toZ^xQRKw+5TGP53^cEsPj8Q zbz9>07p7*GAOG5_AF{OpBWZM)CJVC;tP=ZuJuw=&6fTLnnSdWWSHtlS9XabL89ulu zJP2e`bBic{Cp4S1%{ur1-c7EbI8&eg2?%tJ#7&=%FBD$R{i&lEZtuLUeoCOFt*|Fp}bs+fS6o7T5u!Ix<8$&I2p?zvw3Bq@ z`LnKcY5SYv%I8I9gWCoAox7dGXBX%B>Dgc&6}VSJwqkH0xtshLI)o%smX@y2xC8g> zy5_q29*fo}Z`A>tdEHZ2o_1S{b_jQeNb`62+YL}&$@k+*ZPt=5p^o;$yP53%#2}1z zjoa(p{q+0mx-E04ijK213qdY0Ag{L|gi@ZZxB#0@Uv4V_|7Z!Ch_>?d(bsN)$y8v( z_4Hzpk_2~bW9Gv?5*q{8KMwZ4`s%N`>Dw`LUVnT4=LWqd0t|@TbtcCF_m}{@T3yeB zX<6~A&@@HDd47A2<&{^}xw?bqk3Uuxg+$a#R@VPc149c{Vuq4{R!ZY3>Zodfaz3MK zV)cF~E=gWVVhYnPuGnq-N{z1iTV0(5%Kmj3(_N^ntz^>b-F1c=$lhg*DKnSLxSCdm2jExcb7y9se~&D}c3-?v^r z-H-9#gIYen-kt5wSX|wfc-+RpE-o(nt;;7`ivcN{dY{{qD>yAljV}e44=cC%k(VTa zuMyn1*7Mi2n!SmGw*21m=u%_l*w&AnjXoB)7(%<~P0lZDn|TIyZurNx-8%IA_o!1_ zjxZCjx{d%JHhVJ_s(jYz!6RA4v&P|9nR;SwEs@Ib&$M2cYEPdhvlvZdt1J^8?Lf`R zh&K-KC8w3ZnK=B*|6s~{UIZX3eX;L~Gyj19#+ZPf>l=v6!fAebz{ck#ag9K#wqH>B6mzEVEw?PwWr44A|J6FBP_i`OrN#NUfRM{-*M6MP@Ap7as%)j zNG%D=-&0l@k4aQlFn1p1jMUkT24H-QhVPMLO6Q6)7Z5bLv^H!A>{!4ee)I1KAmW*q zAK{5@=$1O)BcG_VEfH)_@bGm&%a+_fZ$)_F`C>pEPmjmR**m&RY)=sMFSO<7 zyo-kT2E#7y>gdfjPa9jv2qcz6R`^U*e@O!w!&Qo$%T%z@k+vmTG&T$u_Mf}oVo&d7 z>iK4wqNujF=l$X}T#{C!EeGmZ@ZQLlW0}VfI#E_Ir|OUgYN^{lI|(Gw+QK1rQg{Ro zJ{e@7r?B48ZS->)#^B0Q!O1G-2mYHeawH`qnqetOYl%bgmgXO#uUZ~XnN(c$Eylyi zIppoFEo%wl3jfsVcSS>obmuX2bOX6eueUg__hIufRmwj)xl1U!?TGZV2A=!IhyM#m zr|lj-hsy=)XPUkOGcZ~Hjqce+D-Gacqs?jQH^rh(U4bW(vXZwrL z!-hE8#d=MXy0&9D2d_M|=X4GOYJvw@^x5C%!!IfJ7k>1#On zPL1s_S~542S-xLy2SA+!Y$L|5Mb!2rR?H55!qWUa6}fyO)w2ZALxy!D^(5vw=v4q$ z+8(2P_WwZ6Z_nx8$Ma?SwAHPvt1GGH8YBGo&FR(tekd=mH4uA9#IUd==lbsM?uhNb zvH)+Zk4lXHl2rVNx!zlM!;Gl_lhue^{TIO$qNp~MOLupck0J&@)+;oI=f;DoY5)}O zI2*^1i%a>VOBfPucs6dDUD2A*6bIEA#WKdf!2CuD877gb|FjF6^*z~oGxN(<|5lHq zu-hT829|AhJ9zDu^C@qGnY(7;WZQ3L>F2;^gjj^lde1enj#&|#(dL_sK3I9zy@6Df zFEgPqjuQD>UnFHb{h=Fyt-2;akmjt+k(djyU@~sQYdGWV(ui3Z-x(I+PQK?Lb!hkj z@4N$Fw8!@6=U0Qp?N0Q(GvNIpW1Ubi-{9qWT z=Bw3^#mkIixY%mF<3}sC+ksMFl-%z}zoG1X;SI;-P0vrStwM3%hcJ(~K2dI$H`J*d z-mr$_X(P=Vf6e&Wa!d5L?X~AWNl|AUxOT1(=3A#BHs}}g;B5%omI-Lr3+W;4B0zvo zA1KG-Fda0(B~r>}+~a z*#8+>{-BZX3}rxdbq$=CF8U!D_g}47#z#I@%1Tsk=Uvary~?(PCoJk|V+sJQSWUiG z2vcpSAXs`n5IpBEO5H_jGUo*-82l|&`(?O;hUw}(xTozCxfAWt&dbO3%J*52Dc?WU zpvil4F+g%-gw;H~I@eLNv5v@!CU2h>#;nm%3^&TL>=Eti^`v0n9#C{i!l00jsh zAX=bOuK1JA3_2AP2^KfM)xWwm&RQU1~`Z$$A zXGOF9YF-MRd&sPW{4vv3p3t6x<|gLU_xd0<(PqWt%9=T@ru{9#2K7M_5r z?u>yGV_Ppi2Bj6x)BEVx1CawpGUm&xEBo&qKxzq8W}=38*s}ZQnTqcdzE4{BD}pax zhi|x7*R{Kll5rdXriI;h{hOG>DRMMmGGk2HoO5RSivpDW8@|1$h)Xa z!bt(dnHGp_dD^Ydhc^Cy^4&EQApF-?k;B_>tY3>5Mz>OPo9Iq;E5A`+&fZZP0D$_(*oA9=rx?MY^yrJ8bZ&LjjT(Nwt_@)!f zQcip<1l3nVEtf0>xnuzA#`L=qtjecMnAortzLU{kFZ(V`re8gT;V*061q{$at=cxl z#&~Vpwmi?>XQlOE_r6lf|8fClA5mJlS=$d{HJEKc9$E1wHg|G1;QU#poj~|h6i^e| zp)s|Ci}kA@skBTapQ2E|_VMsZ(5_`-69v7fs-_AKeu0!c&@c^*nI#>Na@r$lH?3Ne zpv!JL!d!XGgK#)q@wn9Y&OY{&K#fhsd|=s?6S;e<-iPLo!Ml!d?-CU*HU}o230Sv@ z1vvlK;V?)t9Vt&_V~A~dwkgR;sK~~J>`6}58=CL_^x$rF#hv$5krY6Mzoe*FV7b-Y z=KLdC(Rjsm8NY?@t8aigRla~sfKhT7>ARZRT7;ye*>{=|oJ!uI?%e~q154{-k10jo zCjF-*EnbJ0W;*4{K9L!iBuKqsSYT@*Rq>ENiFMy9E&Bc&n#+VUidQO}1V$m_2$K(n zS=P``rm10TaBI*`Aao7)%W&0B zW9vfAzXerG+W0u;`+Kvy4y&(M#gL#$GKskOxVy>6dObXL_Fqlwj50}_jMI4dgDTc% zkD;mxu2JrjZ6~1r@WgJ(9wsP8?{Y&;NnTfSqgKCwPS8!bg@=j%G{Jhe$-G*~%v_ zPc#s>sCnyL6P+(j;P8+;N-QU(0abgD_hBDo0lUbIlJt5-qjl9G9}d6LUq+iPU#GhT zlR=Xgp<+*HH$^@%ToWzM7ekg^2sG8-9aTCF1+Rum^r2^EHtcN_)ui&$A+ofrCYm)$ zR$>~z_7^vpU4qGDJStl#lx|y+FH}_=!t;Dp67pqTSAAp;Ys>+1v9Y28KmN!A+<5N( zrBgXS)s=eY{ys-EcuPTa0gUcG5jjmersMdoCD^bFx7M-MRk=0$rHRdMNfk$_^QG#( z-Ha75Y{If+aQ@>K-I2!-HGRj(F`gf=xLUQLn;$-#MwKKb+NJEmAeCb|-4o=5^(*2abR7fiA~jQU(|s7imtOaKM)>FD!l_cTHuTj5jKnDCY0*HVv0_Umf{j1is4PMhKzstY=bPA80 zwf+y;4AA2@tHar$x0Ln=IJ%sK)(7@_my3LHX~@ze%mV$sYPv&{At-OzCn#&* z3=$mT-%)p5?Te1m6KZ1zM`uv28vEOvcK6e(b_e^TX!si4;-glpCcuyG8nVeV{jF5j zi7a!FWscm+16o?=8G0@39Cywz^;vxv2JO6?wm<8G3`=O;wFwHa*`XYnFC* z^$(A5X7PZE557~$Aw533!2^*?&5d_3(te6NdYH`Ira(#+#fEiW4drv`da;sRG6_JQ zG4^>F;@CI)d$Rzu3D0IuU{YS~i}opYnS}Sul4MVjr0&G&l6qh<+4zT;vCk&t^tL z-pziw+kEWon;s)o_pINmdoJYL-~?ae-qmBU!E_3J4tY_Zvr0vg5ev-H}w~QiK zlA+9Go!-dGvTtlJ{7GRTQ`@f8%kAx4ZQj4GoWid(<8B>oLMk~8%!pN0cjzo7GxL%{ zs~ZT<$sOn0_3#_A*o-|Ji(4%}1J_Xpl{tG$Ecz@P1E5{-tEi$ehaa@H;YgyxMIXtZ+h zQP*pOuurM28k>H6(=(<)z`aXcZo6pNylyEqI9W^lFXMj6^k1)j_}AY(5HCCZcB&Sk zrrX~-LF7*4blhR#vTLhtQB72R=T!#3zuuu8Oauu2se_UBUFKk+0-5r`3PO7sY*@3+ zQcM9qsM~Z9i8PG<2)Ugdy`xjRZe?(ExCdIL(CeIP5#B)E!rGbc<@DpZ;kgfD1S=kuQSTm?ESe@N9HaHe(*s~@%2k10@mzw8Zx88mpypZk=_M(AWytF| zN{Jls%UZW*uFfz(t~U6x(nEOV4YwNs8R3QUwZflM%<1F*3O;uHj~$5D6{EP_I~IrT zu_5zp|C+uez7KvEO4^2)7@CKt+NH*EvJOLZDtr-O*0ru=#(zXnwh_66+CIC+TDlGF z3pPqUCOP2+CoQ+6ru$chtGWjwoe!nAoqUPz74n5%+FI*wMm1bbT+~hM>Nd;2eEThP z7W$eyE-200IaSXd^_VGYn|`4tQWmPY^~RS|IuQaJRz;gIuYk7))^hUYx7YeT{Xn%7 zxSzWQnBeqf%%$AlzZyMg(!ApZw}zS5j81R+8OdhEfO&F-kL;vUkpcmva@F*5C(-6`5mRbLF1#{slpPE8nr#Z zQ2|C|t92u_dvv%NzO0PrWR(as?ykDFR6KQYYO{cQk@bUSq{}3+Q8*KY!>k9^%(hkJ zs`@q}_s#335M{&jbjk6nZ>%5QNP?(Y@wO>acFuHa&;bU}C>KPe^6>hwkt1LzFI_O=CU|D+r0O z-J)zQ_zDV2afHfMMt1*-IG<hT&uQbwm)R80tF(mT{y}d!dwK5!U~9@T1noq)&{nf~pu}a+oow zLCecIsD#hTt_G#Xuj~DlvsD_;EU*OTkyrEUv!BYai_e?|H>JED{Uf^1JDH3|&wDY3t zmYT5$aRdp+*KI*HUIzwQ?amQ=(;S>!f#bygS~W<5GB67uS*IafKBV;<`PqH+$Qp{N zIkuSuO|$e;u%@OyU%qRfk9wO-o;6mUKbQFWakn;V$X&FEQ)m(HtgUYV;2z=W(I(@u zy(A>T%8^u|c>>Vo^8p0}$y8xQ6bX`n5tI^vWf=CPL;9;BVfwYV!AL@Aic+dlpKoxV zCEQs~PhUQ-MloKw>#?+*DQ~~cR5pE^uX&%dcq<#9-h9rnBew~u8mQ;1A{kWdBMkVA zPPlNk+iT*wzBxBOdTNN&_1Vu8d^8SJV@UE;5&ufAGsJFjkpQ$uuW$#-Gm4?J$o%f=0b@+ORFF7)Fb zV;aFDTR(5awqN*%P7W=TF6L@$#3?Zk!xU)AWi2b`ZYYd@BDK0Z{LzvW1_&}=G0drX zwEQ_~rizKXAwA*@FYjm*_%8P6HUj^)lFL5kSSR6AZhK|Mb0 zke--30XN2p_&6N9N^CFwfEaMJ0#5VuVLn$tygPMpDbNKY8N;Lmc!Com46sw6)X?@d zlDfY-Kpl`a`@#v_wMWR~26P7Le94T6QgHNztJ!D#oegTj2`*+_EG0>RUCO20`aXK*vPDEL*s&} zE^Q9omr&0wcfwq8>r`@%BbDdX?kh#dHM&PB%P!x73t3}^v*3Sd$LUgsz`B&jg^Is~ zCfb&;Zr5rdU_KB#yOMr2glLyS(-IQv-ChM!)zx2G)G}Hs87LPcJV4@!_11E$iI@Ic z#fj_sa`C)_v+MaGFlyrG{@t!5^bppu-dAE78kA>PpkbtU2lFpCqsxm=BbY!NvSb#) z!rJFJH1Y0-cGqIU?(;6OWt;!hYPSCbnZA7r%N_Ri8)bk2laz{2lw=d0RUo&g;T8}Z z%Lhq*iM(`QPGVIobeLyUtBqitDT&N#C`fz!_{m#`!Nq)s=JNQzf;mx?_Gek~yid^; zk$cEn5~X|b!9s8ZE6UIl9$j+es*oDcKC7vav)g@5m6+Bc?H7I|&%K~#iQrqn3yTGL zjDL+lgq6T*6oKo(m%qFt+Pm*?eDA`(kW@1llm2*#ePi}}$IRfPH83D4->T$Ma9<-2$1!qkNN0IybGpQ`^?3lp z*=$Du9}|)<4rJ1T!_Y*kSpjG6ZRE9xJyr#ZuM15=AtJW#uxfwu-|a^HT*MlyUUZ;r z`rdYxmr47ALOg)CUTK6_*w|eb*%a5IhJE#l?PXZkv@fwS*cU#{Aqd2ch@E6s+`l!| zvbr4WM+abS)Pfwk{dZVh&KL0_LftI;UM_P1G$9?P70NZhBFqO3heIpxY@$~!J7}m4 zlbm=RX-*`FD`w|f9UAY@$bRE_l((Q0y0iVAZrP#$4M<*{Td6{VL$8pD-xRy6JhZRV zyzInz8CJLu`Mh~sf-@F`ps6hiA`vtb<4M%Y$}2gTR-;3aD3wfp=4e}NbB)gM zBZ6-{dGmFi)&!Di_J&FzS&4uclp)6aiM&0cN|h~0k(`UMq2sGCGEJc_Tl7l0N|P23 za}>RvQ_UDHy5v^*ncga-sH`AfOhYzS09hvY;81gY^!Og!j5Ln!ogXXGS5xf;MrPdd z^00n>hB=Zl{xI2dI%Xg()UahgeWYhHtLh-J2uG29TG#NFVZ@|v90Kn*q3(MR1gqaX zb{!7~2XUNy;(9dm6Aa7a=RU@Vja54pP0oY1qw}C5i(=zlbe@wGF6fGeyz#oqXia^7 z5i(*%Lr@Z@dmTiuMwkJHz4tLZduge9uI|xm6uLq0j>VTr=Ak0|S|XIuN5rAHfJ2Oa z)XWh_=7CRowb}&i?Pj6RG9J%Fpc4bl6jtJjD-7cPKg<6h?UJN6jlkpnY3u^J2}W$PpTy4;(LrEXKpFvylC8L; z8Jr*_H97L;YQzNcT!lHd-?=slvevs~F&3YwG6hqbk#)3GEP>)+5jY~?k>kkVUnBGvtYobB|+vN)tU z+0R))&vyP$yWjn&26|iX6S$X3OYQLw*NJWuS?bNH_4Uy*(BPVb*-U}bgB1Fsc?ro= zHf9#~tf@2=8ryaym#pfg6Sy<>cu$XD+PH0bzZJB9{zrz&CCDhu%5$PjDwJ=>?@Emy z6G)~20&5~W@_b6Tld3u?5=T1Md$)9KF4E#yNFC0^Mm$k2b~_$iTJc#C5J4!cTjP6i zO{ZcjRmains_L+V6WIDrqc*iF_^yXToPHt0Bq-}ZZYt=>j0)|ED=Z4ELDq)giUY*7 z(E&nb@CJ5yqYj0;5huFmDX>hJjLBp5P4|H7_to6}!(qI^yPG_JV69bLs@C1AZM~c| zGN%Z#y0%rIx(U+tOc>HY;q@GeN{wD(Q&vJ4@NqyDjN>pO`QF~tgyz2 z^cvNfaCv^zFAIl6WNeUB>@;#h@!#jE@Kln~b$on47M=%X%R7y^pBg+J?t-75F^{KE z-u7&<@#zd4aFP*rrYZUd1NQ%(LY4c{BVCLqrAOi$n6zVtm}_*ipyL^T*Cs_iLS`` zLCg_Nw)gz1I#S)ou`p6>X|RR+q~DaIfhO?+4SPv+dC_Gf?6RbdK18OlNk`Tpo?5!4 zaFNBGjD+YB;XNzv%vBj?5>PNC>|dM4OcmE^ z$X&z*N2}V(6#e3cg+o&dJfug1BHY)&lNhGB<_p=G?DqF-tT>yU*old4v7DJWwBV}` z4WKnfm1k6ZS!GsYl-&of)~?7qI$osy;IK+@3^I66X3<*k15NiWYqeWgXPHK{&?G&E+hLa9q%ohbNX50pLOE2-6H;Rgg}cA!s2u+(iYuMV zje1b|2H>DWSZ3~E;dupi0$NU$Ax2u z6q~>Z6RGRrBO`5n)TLo4zZ#C95`t`wu3`9R{K#3Muk)go%uosK0~+fM=zJb!>N&;p z0| zpk-{Pfn9e|>KG?x#=xOAZr?>i*8O)rzu+bH^#sf8_ z%`2otLn(Q_N`$pR8AT&8Q4^akM-#S80XKC7H~%-E;Qk@GVhHlGZK)a=E(=j!{UsAk zKD;8!RiT*jay?n8W>ls7av^uNR*==9e}Wj>*pUo_V2n>2ethrgY&a=ar#%lkb}2*( zOutG4G02L zN%n<>K4;~^G&z)y5mSFwBy~_@7MFJM_v%hyT9Pg1_1P#JtvJSfE`+YO`5o?BgJnE( zr#uR*JK=P#Od(H9eoL|_hzwq3sNqegWkq5RYXDV{uvyU9cv@fw58r!dwMKTt}ed z-laW68ph_Cqapp?plC{f{73q}WLP$ovP9%l-(mS#J`>J0+}C|;Rc!Msup*0~%=Q~F z+-%otpwNP7A--74z#Q|pEGumhqsF)J7O$2nf@v3xF}>Kc>QFxz$%_J96H;_7`{I?9 z_=JG%94=HQ?r=9C=dF@`|7MOf{^0bs?m#!t+zOJ`Do+r3EmpD$2V?eSPE^+7AA<2L z8B9#upTd#&u}UH>>AINhwmX7tN>fP2%MF`@&#!R zyeb@SGxDVAqa~~)iir+r2UD&kj-FPH17M<2Mnv6VF-D(>&(sA9=%UL=D9=idY1(tA z^FHG<9q(+M3HXiT#Cl7cRiTE-DPED8`sqWB(6m9pI5_A^mcHzs@ITxtCk?UCWWOtz zMpi&c3uD#<;Eq(ra4G43^K^|sCUg6+_22b*s6!qSDX_?jj3BPR*987sF3g@1OiYM_ z3)E{#lmVZ6+Lk1J*ES+xN_@uo(F&bJ=M|vnb7GYr5e(a4rf0OuDrfspL##=Yv}#m2 zyuZ2FEKzHF9#EM^VM1F7sjK&`04$e@N@~F|)p2?dK|$ z`~^_aQ1Qb$e|6Ry|NGzqUKm-&w`^<{*Ob?zWv^S=Vmy`V8_8qY9Ab{wM>_N>bh$2(-mKh>EUO4%RcO4lQZc?*^fOn%V2J)SK8kpwWpjmj;5pJ{1xE5FA}VV zjIgYoPI$v1JaVR*yt1R5Q@l3#gTkILbP-n)Sop_)5goA6=>dZ8bNyb+#g`D`UwxiN zGb?!^ES!hsFuAbNn(&>~njE5EJOshuMypr|MoU#03+X_9EzDN!&cL*ap8}t0^CtF7 zs6;b{V`2F|?V>#j8DpB3wD^?3G9F63^7v2yB%P|b(L5yPFdFakfK}yxIKm-uB{!qg4 z<-}_3WZF?jdh*`GOI?Yeiy`qKBt!r9^R3BD3^r`tmbdUHcz6b_i8_rca{u2_p~_Qn zhDS#Qu+>CjXjuu?5VaR;)HC29ZEUp8*e}>N7csuqtT9I#h4 zOY3zCq!>=~`vGA3Hm2XmJ()lqjbwBE0-C>IdX>?3=mc&53F^ncSM}Wda9*mhFV}u9 z%bx=e_<(Ebx;9~G1a60={wuGvpjNe>fkYXo^vlVc>3VMWl4WSs3Y%y#*&Hrf6sg~Qst;j~ z*Hz~VEcOMGz=@+Tn0S^b+k~(v{lVBI)hqG@5GB|g1%oB69&*9pnKZ_q3PYqmnoE+hFFbTjjf15 zrI_2w$jqrY1gIj}tG(2^3O_<6mPjTYE2pJfT zA=FNtb7B_=thIczXc!MEWib~+k}N0s38PFzTxkIM`TK7VNfqsG4!dx<0pvfzRhp+% zXIKXCys2Pf3Pmj?5Y8M5j>9e{7(;Qp0VvP42M7ZyL0d8=E|OmT4Qo{{-nKYFcvohB za5$gYeU#)H3R42MkgDU}T#|J#Fo$^6u)Nvu>i(aL^5h-pm`<Tg*k0mi-6MJ@sIZavElo*G|50kNf1k5usM|&{nm(<`}g<5oiIGWOlBl zWdCeYF4Zd0{(?pzzlr#U=Ocz`)H!KZecBvlQP_RL)S15{gBdam53I@gA`ZHfHX6|yRQi-D;8CWRzp0Qh40H=xsN?wu* zMfF-GeXw;woK9zmLaXeN3Fc3YOPCOSzc2&*(@`rhyUAglT8k_;cH}PlljDj>wa*qp zU{>t%QKWw`d&+pTV&c7P_vLtXc|92ST-lz|#2kXC1*WKA-{Y&N@=Utjag%auI^rI3}cZrO^-QmNj{#CyFm3oJqOc6gP3jL zRNt44ZfD-0c|W&UanY9UZ4diNX0#3`*B&0IG0q0m+( zSuF&?s~QAf{~LJ!NqjLsiI4Sqg7lR@!|_1hKv%SxOqlUIk~EWbM=#R_y75KBuwv1E zS##W!oTik*Y{ExIrM5%JxpF3<-E39YRM5e4Clxdnj@AsBZe|ZkbevLk_QXAg4H+5u z$8}Pi-|SN#qHPr?HOC#;&VBrN51rpphuv;_XkU&83MTK_r_!hJ9_NshkhPC8vaL{k z#;e5UcM5N>deJ}JnAQI3qC-bm3o(h20yqO9A^qssvCp1-{2tp_a{l{A9=})QKuk7r z@*4w+>x2M~v0cuDkPtHVJ7lRnT?&oGG?frjo`DR8NlyqxGO^C~Bxcdxx>mp6{239k zGW1kD zS*mKQv7Qsnvs>lRi;s5Qe!+mx-j^ENEBdp{M34QWna(FFzsVp~6A#Q|Rdp04pc4=y zB}oLGkns3ukp36tE-kfYJWwy;36Jg|z&4P%AP+?gq=ad7m+{h^LCm2h>7GUgOjiCP#mxK ztJlMMGdlB;sty)qxCU0c?sxChHTffNnD4&*_U~5L%VYrnuOJPZk7tsB`mnRXZ8J73 zJqqGaPweTo5s{sbv%X#d8_#53zWr#I(BDb`ff}URXXpyK=|3p+a&fK%)+%#C?TtZS zj?_V#ISdI<4fLemAZd3NNccHH#;^2-^NpGf(-0U4aKBt@WXPNpf+Unk@cM%gK=Ll- z^WfTv_wC1)>5@5Fl3LVt?ejQ*^3~?Pxn}meYiZu99Rx6KsaB(V*f8DaJA8do9M2Gi z@u0Euv(N`RWF<)f_g~HxnEptO0GyW1cY9zzu*wcYZh>BpMgr%%f@_SeMo=XEVm+uKXw zS&CB&w>QR)WZKtNFHnfSdc~^Xa@(tG@Jt% zoH8yA_7(1)%q>v8qjsD^6qY$na`h+>fziUFN!uU%XUsK@(0Sa+;d$B=h}kwsLw?=Z zb}iwhxIaQ3$Re>duw7Izv*`JcG}dUJBBGAiNYTjb(fKHBvGlj(narDKYI<@z87m`s zH14({7Yp-O$;9#al1uH3XjfL@j!C%*Ep{v(jYTyn3)F1LOKK0bR2@8PK^!eZ3X|;J zeta-6Kn)&g3{{kf(o4*RqdumfyvnF#N3%XENC;}ItlmrT9|v-RO6urgub3AS3F68h z7WeC)v0~q()w28ZAo{J$_h4?Hn$PF@!;6_WI6>SjFRQ5-mXR+`t8eURj*9p=Wk`#3 z87B1v1@&}V+R$ka=Sxw#r8-@Uz4tX=uHUp*ojP6md?siZp**;`L~n#8;RTx!S%3@G z>;)3NU}j}sA6HeI!Tf0_aaW7xeP8mgDV>(^S3`u%NlK?$2FD9`F126mYRHZ=5W&4t zpa=g+$YOximG6?l=M_Dm!iQTNB{?#yr4Kh=*NH+MuXr0sV+ke4?EU)^7o?Er(3;}XPGuThz?*!_VmUs?EcImqY zsNfQgJ>bl3AS`0<K4lc~NOCJZJU_AqIDMYSVq|4J^3x zT}cG8U}c+6&1ya7V0vW+&ft$&ul)wp!xh2xTs3`Xkt+JX>RO_ zxX{Fxv=Rb5FHTEe;I^OMJs#^SUS_Xf&s%(s$>38Ueij*}O&*^d;YuWejU(#85(*e= ziNY8U7OX0c1*r8MwvFKI@1~g~4nQ|$h|=WC?FR?V80iz(U)_;r`(03>hlQ!<=JY`5 zV`N=Pp2Ro2(3ex5GIK;x00x6e(lNErfQ1;^KuLPk(DJ3IvZcDB|AEGr^-{yu6Gj>{ zJK0J6X&fb17dnf3_g|~C8PJCsrV0Sl89KzNK-VGWy z7|jCN!$2lVy=U12sXXf^u9_!4{de}J)aIs=$Fw+i%ZSF_g9A==d?(f|n3yaG6sI5X&E!=8B(|i{VXRpi_oVe z*A2h@n3}U_=GS|?KbK4``kaYT+{3}zz9nC;S(IS&4X{jsAA;JA$!dNB`{8D%S(}|TWz?*a2dsPflSQ5*TT`yI%m-KT7$}zut=&f|=cCIV z*|cDVyLW%|CQdG%V^V6>=GXdh{WBH#;3*jK(&Dr1}ZN0TY^5nMov6#_=4n8cKuNkf*)>x`y#94sX(eEa(2=zsSC;_Rvj01c0xp9|J( z?{WDzEIqNoKnyT!G4S%T3&dq2wsjX5v6U!Ms5u>o^bvuep~iF*68ip_wXVVRt~)Z| zA1Z*%Fb`jTKEAL1`|9^kon;>=|M7^5*#)67ZHV*`ER8hw#)Lv;Z;A{{vh$IoPW2Yi zwhoU1S_;s90yw%4C1ti9;qMM_L_prw*Hbe$fL1QW6pen9?ASJcDcwdyG&zJ7STLfh znhm{|g@r6I<}!!T%rEIqRrR+*gyEb&^Q|d%%vSpb3bYbm^TqD5|fmjFc} zvxT0BH6hSqto=j31(C>Z>1jl1&d~CQ*LL*uTSxQWtKKVM`=@S?Mc?|gvw!$=yleDn z7t9-TLNj2wv2GkqaxJ4c5e^gr+Edocg3ZEI#y=@E1csD#(gyz{IS^sj0_>(-6u&Dx zZz9aZBMqf3R~kx_>jP#rcKOLg0xhJ7H%BsJuqF_N{KaAU1@$WD7C@zEXn1c&Zir5j|PPUVfzGszS=y@-Y0&@?0Uhp4LwO* z5`;tzGrma;wLu=FeltTu{#Puld>a(CPKo#Y{{WysU%yfa7>S_5F0GF@<`|zkMo3Gk zy((hcngp&YW;R1KcT4|Cdjdu0q@TT;t(|OY&ZIR57z{vsOBdQm^Dq z=0vB66E?bFvAKkKv#s3GLKZbx=mLCj9N)RC&FLvGJ^Ba^&R<~H%`|LQ#okY47pM(W zAqIkU50k~%uI<_b09j0x00=gny?s2lWbEV^kcMV;6W;a7U3;+221R?xCRl6Y0f?4u zj;&|T&Xsc4c5T;o?c>Bl(!YV^62KLj?W4#_GaNzGfWi@a&?uVnay~gxfx~#^pB>;? za|EPhPr|tYZ*}>j$ET~;8TnV1%jK^=guvBewYvKmi^ah^jP{iz7uC?cA($aa`Vxqg z2qlF`lh$dnEITXy_O!?v8W9IrqNtc~q=>IZ(_-T9!rNuu`55RlfJw9oVqBypB zu7Vp7smUN<<57h+StD2OES6|AHj$u7G3q|n-w23-4Wpk_@3RU(ki*bNKRPwkHjX%o z)7?>EyrJLZnvTr-)9R~<#~?;ztu`T;XwClH^1c9>h9VO%*$O%$>k+2xp#nX+9As=yJ9TKh=Gdm8X~o3Bwb1~{fhL4wxa zk9YsrkK=>??5A<{si&|!JfvBGd_ayJiSJ-UbWe2U#1+=xtL7u9$eUIC zE}K-~=1`d)70i0FnyOtOGdURoAmaG`5$@i*i`TvGGx2->gLmNalaI4o%)tjEUdv=AtlN!p~ps$RL_A1Ik7qkm$^?x4*CRmoB24%}OLMIhs6j zz;#TTX|GHZ(A&5XHgm^x>cJ=ln!lb~!zS-pCEvN|qBE>dQZ8WK54>~t4xWDcX~4ZG z|BFo~MP%K2QiWT)sz}4Uh3v6$)WliuT&o9FbQV4w^A=^8J7_@lURb%S#kE?_>N zt6y*E9CG4Kv>{Ala?i&!(v$Dnu04=NJteF$mG36QjI$!l(!*!;@|KacYrFPwwx%Ds z=fW{MB3{gc6U?6b+S4XMS`~5I;3vt6r=I$)5VQK+H@mMq!(k9@vMXxR$!LMc zGZ=u0Qx@xXGwz=|(B8RyjsxII_-z|n$V|T^C}$PEX9y~d1~xd0qW4~oNvO`}bk8Rm zeHYW4t7K9X5FiJ7&?0{=^MEo=z(^z$i@}?!V_dn`%Eo}vqfHYgbe~_zk}$Pm8sOO&u7Tg;owm^|o+Z}>VXioQ$p&HUm?^>8GcX1gIGGyh zv85VzQmnqpR$K$4HZV3Z%J*$3fvib2Xef#!%%EipjzBI>{02sINS$r5We;Bv4oTb-fiCNC(npK_Kcv2?p6UM@UjgydC{IaD5lzC2jJYrFQbc2dq}F=r`lgC~RB>AJC9x8?;)dFa}1uH3a<+qGSL0L>SR#Yaz1 zPyeKb;aLa19AuYhErHnWmF z5~v!X=t};~NN3T@H`aGK0RoE+i2aC? z$6%It^br$Get&|~OEPtQz00LxfHaVD&>eIqqk$#!d&W6_P^7(AF%cp+ zLx1dLmkYKQ;H&iZ@|C-BYaGzTQ-c_q6s9t##c*A{@PX&_{4c*7^Vyt>y*;!5tR?E8 zWg|``Ib_KLM(khmBn6f#;64g>E|7Lkh;BDS1fwScrwD~Ez-TzSdyh9?yn&;mdzjB= z2wfL@k+QRC6yLjx{V>>M4&Aj^1`PXjJMVxhvsa6+Hz@sNm!J&W7L=atK)_vl0Ils; z9SBRCRYil*Xvk{7S?!2*qgzc(&93d*uI<{ceZ1{txm^C#u+e`f*N>*E)gpnR@NJmU zj93$QPJ2wvL5orPe4uqC7pq96!phftJNQV7}my{&E^Zb zuKSiSo4pOBkrs<_%l7W_nkt#|GcyE|Ibh0m_2s{X;1Pdj5^aE}oN!s6sWuF5{V@FB zYagDyc=6%~6L9(|)gA-@sArye#sOe&@5JQ2`7l`We2WPQ@RUsI$Yil}xhc6wsnYG_ zY1y3Ml$;cRN(iGRgfKIcvhiP$r^d{Sl1m__h%<8lMhfomp_ltmLQ8?BG_P!)N@Lp& z0nRVsNSOa}Ra3+HacoOqTcmN6q1qPu88{o^TPzodDV8;XawVacBw$8H#3KI7G~F7PUqz^m^#fp%C6pway;wFch3}3%j|4Z zZYs*E)e;jlDpyL{Nql8VU#(#>Ou4bh@MJbKivUM1-I!owrex%JUSJ5gaOo1h?j7Hz zFa3^h)6wy%u7Bhj!eWjvo5i%|oP{8%1?_StQ}NPEQ%7-H8;} zn_v_;Xd(>#@yf5?xP>qK)^EWVe$7{7|H6foIm?uQVX-b?sGf;5$_%2xNEsHV2q=6QUG+5FllSX4eevcU85icYoPL|l#WPD9v{F~AbW3Qxv2%tsqCn3qC zS^U%ZHzX;8$!IW(pPjm3ylv5$kYr%=5C};CZIBrVV|;%@?yvDAE#6Cpn6Z`KkGVxQ zzH(Q${18rsRXMdddpb1aOJpu8h0Nxb0Cy(g zG_$S1>FEitzi>@=Z{Gzdb@NUHISdGezfA>~=6O?U*B&_F@M+r|gBlG&y9t=}=hTD z2Zo{lg;dj8T@XjIMDnOl<~dDlX{jM4GI{5_lzpG1GS6qM!y~CD5-}jByBkSb7v35| z_nqt0^>^X{t3Myya}2{(#W45jcQG-ek(;9+ZIce^P@)2xP`nHaeDDS6d2Jh8%dK# zK_sQFXA>8jNr;~9(8{M)t=+hiry8%aG!He1YX~2Bxiw4 zEC)y8G=9fwlBlwD_3!N{z5!i3z>)5B1@pqP|VwlL{%|oy{63mkhHVGfaZp^ zu?*>+jTx*GK+~ibjs)_V51G)>t+<@l+-*W0+iHa#v|LX?W# z6~*T56rd*Vwu}ola`j|U**FYlimJ6KjA**N>d-9;=zM-^vc=mV4^FlhTHDg1vrd8n za*_a2z9+F6$QNF^gBu@x0gqh0iuJIz!Ce)wi;IhpHPO>Vr#vKC6v3>)!I>7G94NuG zM|eM_KBt*djEq==$pH2eR+|k!dhJDA87}F<`Ey##I;~dgIQNGLnJipff*>%x!W ze(c4s^&@~??t3iKdkp}3Nj;*+($QkExDDWbb3M;`KYi+h*fvi)F{%l0iD1qq^N=HD zN%IWHiHQ5J@V(u&Pwd$10GvBHIXMU+oHMg4BwsUNumG%qa4hMryT9b_NBy!tKE&ZM zfYV>+oV4o$zy5m>04$GibU0hju9(?nlII5?dczF^LI^8KFZF%@!e+U-dk*JL0IX0` zM|ao$VQi1#XnlA614~<;4uk)dVBt#7U8@KH03ZNKL_t&XxEq@*0hHncG*aAX?01GXu!2r{VQ*@w%RthYR_%z|XD zC`lp!U-$jbJBJ@Q*gN?7ml=Pb_5dKCefC*C^UO1466*VYyj_`EWY0{Y- zDzhB8<}xsstWliWATjPT2(&=YE|1CV&s7Y~kTC;ymuG2bq7e#NnHE!HY2yBDj+q>r z)f(7`EX(n%02eiOpmfe!?xI4P%|xsub{)IYWlV*9`GshcF4Gf>TDJyUKn=jn z7DU>PwP|F9UI75L2uOT^mvrUU0nrIgFw0g6T>wj#*pdv4u&T3*bdg!|ob19{Y9;Gs zZo-*4f%G)GBP|w+dd-@Q0mo$Lnn*_vjME~l%~kj9OdO2!VzOyou@;<; zALL{!HAdwH#oltwtBBzUwk6uImx<_J`8uKyP$8zRgE0V&k!UxK(eZ#&>i3$WqQ!$V z9k)|uV2-vA#xaX(K)ez9*G?HNHpnR^juu}(gG3y6xmb?31Sl8qyczm6nCA4>OSpUc zj@F%NcCgRYYK?vv68$uX|C)8dR6~uD%|;_GEBVikvNtIZq4>9YW9aCz8b$*c8AfqU ziNh+=H9#8F(Xi@QxN+kKj*gFb?qCmlvkvRk8qz|dX}Q?bGtWGuXYnkb`LCYYEs`BYu66emoB}YDv0-uo9^{8^L!>1;Opr3BEN1ezBOq>6F@vg2b{L)V$@=xFMdgEX-cRV28yin{1=K>YiB}p(Rl8{2VrO zEMa&g)dise$8$d{|Gxsd1u(4Au`l91-Za;FNE-p`v}qs!$Ekn55_QZnj-}i$(wtR~ zos+(p|9_K?bM+7xddn47uffK?(SR=@?I}Wg2%MR0wzu31=^D*IEDV;H2t%6Z`t1!vucO%>M<@{W5@~$r#od&y`mrP&GROt{lXB-Qnbs6Bjm{&1Z$cw@Lg01Fpa@6Kt`^gAQ}dDa;6jkSb1(3n4dJZ=J0c zT5JSt03AF0hBkT+3_r2yx_9pZ*HeGpwTGmgKY#uffFD|~hx<_f*W`L)@Zo6i;TDA6 zV0R$(AUEuEnik44T3V?)It-epJx4|1QJB%Sk#Uw6+h&|K^~V~*9T3-~;UX8X|K*n%pP#64TR!K;jT`3{i|_^W`QC4F@6QjtKH`JlW(aH1=M?9H zV{mLp%%}`0fh2oqn8kIGImy8hy%kXu)0#m(QH~*%m|MR`&!8`ZQT~O3$)~Z_wAcpLdljStl z7;H6b!y=H(>qs?gkz`$!6P$~GycF-X%Il<-*p=nIa&_8ldV<8cbC(k94W8bO9J7+x z#Ho2h>%kU2RdK1ak-ibyw^jVKfg!34pwaWTL9SXhV8KXKGCtYD7UPHY#1xt@Klsnc zLY^?E#^J^jUKh_UM4RAN3a&POuB7m=#R5wH%7o7*FL@uR45V@FN_tHLEWAu29w%mv zoE(uN;6X^vq3?C~?j5}A@BKsl)Ia_ayL{^oKl8Dx+FytpNDB}K(J?@TTajKefl>lO zmk&B^T)<5%g<~duuQ!gtON1@xa3|p;?;aoHhyMA`=r8=>-^c6U{AMl>_EU7W8DOAkRZM58wd6JUc2hy|GOc zb!7QUl6_;FW#xi40Pa(~Bk9iIxM9){n)-KPK7aqq0D`Y{d$jNSKN&*!auA2n12-%I z^d!8QRRp>u3u%!7xNksvBgu35`fPdoDO-ke{1}TxWFFD?0KGf@+ylUnOk|Z7IN6iU z(&u<8LVYstwqZaa&7b!L{j|aU-m}PAQ0$eBH_<3ZH%I)B~JQ=jU*%=_|(ptb# zzxwpoGYLl${;3)M+=K3$!!Ud+$=^%zbKLulg)X!2Glga#(4Db1=mEJyzwEvLFJ0ID z+(QDa1OQJs{CC6gRiwRv#CbuNAsm$Z$B|x%;HV#I(zmwz8L(Ca{q7Msf^gsE|5nnE zgb;ohz|Du?TJ86J|HT&UuV%0yEu8_%C&xeNWm*$bD)G z3eEvM0pLFq^u++K06Zjg36aelfpZ~bHBoxXh*W5nXL(w0H^v00hj0S$uAqBP+@$kg zo8f-|@Ejsz^VI~n?%LVUK?AV2Uax<*q(1`ab3m@m?0&rc10dW7ahre*&@96livDFr z;{c)uU<_eD?%zv-21&iqJfb38LD;}G4CZ}s3DhNtmqA>F?2&%x|E8qx+uz^+*-!Fy z(b3V-KyE&F}v;2aB1i@GvgkCLDG+|H|sxhczF0edktO=kurRkgzyD3hV zO&l}%8aT0f2qODaR-q!d3w<{JU0VoE14YV;*b~!bTvkSo3Y08rvaCgHaFNLZynNec z@Rr4c8l)73TBhdOmvj!k-`(!Iu z%}l-;JzFQ(D$m>k$}|nK&UJz}oVO{cnASRM3ToA^Q-L;{{PEfFa_y$u5N|?=*_`nN z2;TzgYamO@78P^Ubpi7vO!U-S$CqfN6R@PksYz7cD<{J?CB(GW0zFW81S5LuV<^ z3GnD#sca-z`g*kPkh0IC23g#bB9*hCM4D+Z(j{8d7#QGWPyOA4U^4g^F<_oPuGQS1bERpyOO}>d~W?@MH9B|mRqX# z8To8+ew2WYz(o+3;+z8%AFm+lDrB_J7w`=( z{Wp%+?kTx0@+Mn z215cX&VaFTM2A`qHIKzx2t( zp_a?}*MoS*tb2NJfTop!bHE;nW1-VzM*{*aBC!*hXrIfbsd8`K*{! z_@!#!CIfL4d(9TeI@9bKb-`Cm#>yZfN2tu1f8W3p8Y67UJ}v7y8RI@0w61z2rc2zw zP2~dRw2q<@tiYhLZf9m3f#u9q1lbd*at4w{i)yN2x#e^QNmj(y8c8_S*2T$@AziG1 zy~BNc#~=6(U47zF{E_eeW8A-X*X_|aLt!47O)d;ES)*FIrb|ZerA9XGw44;eOl+h( z(Phgi2xjo1j~0K!fd2lmmU6Crpu40DphH!XJ1wEcQkV&#mZPQ1-Am#0jiY2WW31?O z0iU!@8{i0CY(TC=X7QNKQGsOExR)9l;ffPFThO#+EGoy+p2ZS8!v%9fi7k@f5%xAU z0!=Q8c`w46)ZR*IjK7{t1{_f^)PSpHOi`FJs**Z+go^Xbt%l^+m}z!ov%;?cjmJs> z1Wt}m`R{(^IlcG#P5-eM1}*BL}- zLq&yPmhnZat71#h&?!D9_9}={)Jz#cY<95fNFhrN*dK=B zo6PLHx~}^Ik{1xAph069uQgy*Q;@6iIxQPXqas~3fF-Z7I&#X;Q3Atwh2Ry0@J0e( z3gMkD|8YrwH-zv*pJbqv!$1=iBFHiV3i9Sz%s>=Kc-nHQ=F7|`6?L8C6SLWtVbm^F z8z8Ur+zdG8XHGl5^K&h)$;!q&$*6URF25p^RRGIF+BjyLJ)k1X;wDDg)-qt2u-ztW zU?uqwhqAWZ_zWTNRR&@az+vC_Uls!Y6Tm*75oo#$V2H_~i8Wju9%F)9UZ^B5Wuh&X zVa<92Q$yzT_ajDkGgg-b_DDX)E`a3U8iwIRgY*yQUH4bh#>1xwa+4zLUW&fa=r6UJgtR@!?N_mL_#mb{dNPCa8Hf#6Uh8Y4Q1&7XMV5 zsFNSROcaeBh%4yqI}Z=f&#qs;{%5aVy?XS?7`yjRPX6e;>;6y|!fOY27;PXKAOwk) zY|}kX?W!hijoXSqk2|#r&8n-hWl~BFErQdm7;pkz-*4XUt{>dnyZyt!o?gKX>4zQw zoL&&U{q1ib0AK|SJzU-Xf|uX;v@Pe0JRw4G*pPWMENK(bR9-n96_SInSOKQ3^9bRZ zIR*DbcBL5STzSIa41$Lcvs-J-HZ1%9$%ii}$*PhsKQY+yxkdn8%VuiK?zI*4v1R^G zHLsiAQ=7#bjX zP)~p6Z=x+0SgqDkwre3flsJ~vC!biQ{#HOZHRkH+l8P0z z%`d7t_vR^7nG&*;RFw`XrSCMsK?QAXmXA&S)+Vl~B5>pu^x9n5NHmQU>l@c!{FTJg=rwwWjo?jUc@=wQOdS!1R4YE0@TlE82UkdHyg-q$ct%xn$iGo-M{sg!};O2 z9xl#(w}Fo-$_lM{P7SO;7~52wvO(3DwPH&Gztsa<2hNh~UZ#V%v6o@pK;jU)~ z2xW6Jr3JJ!YUMo%wwo{0;~Q=5F<^QgpXhdB0NzI6&xa5m18@OFq6{NO?EJi@Y5~`w z0-~dH^y!9GbG@xmDxlp@$j=hheIX-o8N_9yy{PHnh z8iwJUX#5jh*F6E?f`D0ipojFFxAD5EMgyRL<7_NcMkYDfsv#;mN^Z}Gv+4Codyz&FD)twQR2Q2(h;;%tqz~F!@1IKNMKVdCMNhiz4jb|b3uFx zA^bHB`UQ9Y+hG>|4n7TJj1OTm;KJ>7fBJuh5LQI@SE6bcRI~&E33^0@V}G(j9jVU@ z3d&`;3VVhHGcJ8Il|Do%r<5d?!R!%W_~yml-f-i_jX(d$BaeI{pMT4vlcWE1zL@_3 zWB4qPrvaQNj!l=I+CvnZd56T}O9hAup+m4(N65&$n=3iY86i}a5~&g=2r@{r-hDNI ze{r*3{orEp#PP3zb?pGaU+bZD_voq^C>pq0IT8?^R@WjiL=Unie6^>=jgxef9Izzn z&C6_yU3$KKAnP$!2{VYdA4k#1{kf@imAn3)Og@{1zOuxF)D%&NQ*Vh<-T40R)UO zaY&2_N*oAD2#z7*XVfGLekuwov;)f6G~G1aO?3_T-gEYPpZu|gz4ku0+JUN~Y5Mpp zTdVGH&faUU^}f&h4Dv)Pt0VcV1Xp_}aL4;kqU*aF^-_j7k)haDW`{{IMLBt9 zj>zC}XJf@BIUHtPMy|xfi0v$;gQ(>@PV)!-J# zb|TFe$0){ms&rA{rvw*{&1#!{!3tteHxM=uKPN?cf(f=5!c7iQd+p6Z(B6Og4Bmd@ zP5Q;x{HiW};Gk=%gfPmH5peTD$Mv|_C zGjqgO)Pln*86Y$^IWh8La8ODB7X^My&B+cQzArnozyG33b}o5R=)wb;Jl1B6 zD8}sfk?%x2|V%O{bopw&DQ_8w zbqL_7r9x#}&g(8#u$6GZB0flA2J7|y=k@*K^8>>ZkXTBy;H|9?wE2GChK681bUosJ z+6?f}(PzZuqIvVROOiWX=_Mj&qrLa?pbCI$}M^_(@z4NM4Qa{$Umav%$XYSWZ;#TW2=G*-=fwjB7O zHE8lD3vZ_jqm3v<9-hq85mOq2)C z3_!g4YznPnrb@Ya=X>bfex=wH>=C%G@9cp9o&w+p0sI7}{&W}pbrxU~P_vw_1r?QL z21qDO_XkM`6rTv|t{H}SudWM^LGtx0buzTA#ZYd5+VCSG>v+b#M1T}Q3rFmAEYFih zGK7xU7p!@{0)Trn@Cj!2h$#J|!~NmAcX#_g!G&Sh-}CxF(661HcO4!czGm&~hb;QV zGdw-($^gGx4ri(|iyS^S8fh1(xuF3id0m%eltq{T7Zax{VImC0;F$!?!j;E%kNu;a z#p0%egM-tn#*jDU5LF-Z+0ifLh08J7yZ@Y4zi1O=>uind3AfTF}%yiK+nwEC7toy?(hh6`tlRa{|E@q$P(- zkCW0yj#@nqeM<@kk9n=7Kk4Q%=}8n}fx71Y572Y|+4J%2uXrkMf8WhIed;WZU9tx= z!m{)|k-aHT1`f%(GI=<;a^9~K<}yTD)n_NHJdtS=-8=cB5>X*bx?LlUVk%O@Sn`hd zlhclAYA0E$`3m7qLB1KlEGE;Ftzbn;!gEw%5q`F=IFm8~w{(B_4U%KciZGC8#cEu$$Ct!Lx-_@Lz? z^N8H+)-R@UcdSI2Y?Z2PD3TtVH@)$Vc+q#i2o@H$SnU`*S)vC6JW8IKA*NtNNU74y zGd7S$gQ-%jGgB!&RuMoH;jrYpTDD0l^Z1MoFba$+%bIY9qAD6gr)uOZ(nLm<{yJMc z7{L(CEZ-#9V5y+~Fk}DOX0!R|^?LK#)pF-cLl-^@!Yca<*tl1g1RKpCnmk4;UNU86 zj$1XyCD{;KJ&8}ymbbV703ZNKL_t)=X0du&=u;Oil>yfH5X(SWrtJ3wuILusXEJoZ zaIlGAmKhG`t?*m}L8|(WL8DA^P8iY=AXWabVph1@Y*towhGwS|0L1j@YsSnO=Rl)t zuEsxXLK}cKLsv%vA(Xr1Lv+VfQhUK)r%s)^_nG}O-`sV}?;yix0k~`irj|cp3XZDy zBKZ-Ys|||s8L$_|=DS}1!m3u|v(XHoYdK>_91sTJ(h%(FX7-Zxdidsx4fbLsOR# zDg5WW?#t$WNNEGm$Ph3ZYR}%^6gaO@(h`mshFtX5KK8HL#LW*aLiqPI`&^Ff*)RjO zOKoNvS^Y_jz-mKKx@Xn@8OeW|R|*r*NsJS^!kq_&O!grqknI`)-y`6s4iE8AOxc4M z{pHOBN2OI!VVR^r(5BU~_A`^P)v~{Kxzm4^1^XnVdy?5mjo0FIpUQ)BmnZWn0Lo_f zj#jNL(NwiJ^>*a7nZZ9*Bl~tO$Gk6W_d`M;c*4j}U9S57J`BUx9UdNj@ayXo+lT4d|ob=2DR}MZD z-JQq*38^J81MeT$eQRL&oc;a%t1jqS2VgiD9z$RL5*e;Y{6qzUp_o=FKPWc36y!;1 z62s*fSXTO1Ro2sU9}PiKkfVe`6jE98H4=co+zi7j7K_DejvqgM3qB0}GtUQ8qDp{Q zr>1dpD5uyUhf=&Ce9 zg|fhOzda8tLBzy4ER|$(pK|mmAr%Tc@N2K*|9JJQ{p-KzNgVuYb|DlipRV}wWKN43 z4djTvw|;i5wl!PmbJ{+Q#`z5zm`jo-=AwcJt){3o>um~RE1Xu%rtOo)hE-8qJC|bi z+kBZcGdpS?+|~|EYGQ`RpFz8`3IBWwfTnnD45(%@(Gd>u1|%wz=&i!8X`8?qBQSrf zCqZm&L^A85@~AKif36@B%-DR98b@FDOhZ`)=;I#okvMh7lX+%MyzJ$#LjQn=pzoI$ ze1jMVSQ17mc*x*TUL;vMP+Sg~pMt2&aL&{ylENTThUIcetOacb%9B{2J{FvWDR15F zd1?n#=$2c-GR04Bx^`yL8k>6ZjSYrrV#d%et`U5luak-Gooc@~v90k8XkqlFn)gw89SIGMK&LFIVGT@{lr3ph-W};Oho3~uc?gdMg&xi zx%VvNq(LIHw?ckv$s0i~NQfKY%&9Z@tv`4Rk6-<0Y&Jq1yt*(N0K_r`C&XZgK%mP< z>Y@avHI{%RDY}C4Zj;K`>Z~~mkw#M<=9)*D%ir!rOuoLPNu;OLkc^LpB4-9=o!fu` zlAA<+7@Xa0BB53b7Wt%qR7NA^AMKMccTHkU+n9;ySv-LeJ6vEntIHzl```PWptJ$ za5s$md~8Z2j?dq8j!q$>dbXCyr@SFpR$uDG&e904gPsH3Mjwc=?J2SsL-pZ-aYh1^NA0s&OHz{FHb6K}tEjOfiNlHa9|#JY6&_}CI+gukTpB9%&_LV;<u~rf(aa8$HwU#^Sy$XFQ`1Hl1cxQyZTbSv*osG`C9r5rlj(pk4W}z8{%z9lW1~?DxGr+XwUO8&&I16{0aD}m;a>x&A)vq!_Er3d&jhY z`gHM_Dt$%5`PB3i26`MZ$XFhvB37wG_KIgF)`43(!Lwl8oYh6J;7h6zUTip zYqORO2bDTqLRly;7yWL}{3~lL0>H2yzBh#MOf$P&j@XL+Sdu>3z|vC^45tGXH6~s~ zFgZ!4*Q7}z=(I%p&gV60T>~(beP`@0^L~;fEp@jbB?t4Anmi`p^J9#U2q8Qf!28Yz zRFJ_RYY0F+YcvguX;X4vW%g$Rom(m96Z!9U;82mQu!LSvlW`Z(8ibC1x@x(B)ncJi zp-r%qpi?e(d4|}IEEY&is|)&yF@9FC@TDZ5l(B`QfWIkd(T-F&ku$f0ywIc|H*=Gm zT{N1VOx9!Lj*zPz)=3!EuK(&?SN5p#b1WwZ-!F7{ieugH5Ak2Dy4CA0Gyn*|a}p0{ zsZ*0fnsx2U#*i#!-oLfyX^%vvE^Co#`HaK4CJmyVB_vG_&jCJZd6AOLvO~5eU_(0? zu&PmMz32+v$pvk2K~tAyY33FOXB;*&f!Y`hTMdhl{3*o}kqY`H#)6h^`tfyQ^M)X^;=!2mXi@a)O% z%C7UTbhj8c`02jK8!qnE-FIL)%xceBpQiFT9CNSqX zW(a9qXvyrmqC7}aMaM|+%yx~rjm$+`@_9;5Pf0N6F2n#aKskg^f=hH{#gdbN$HbXV zqD&?NLsqCVLWpvAWv*8O8HVWl`vXqg??K`J{GUJKZSTDi>-B~sy{Je_VB3_U^ibq9 znf8Eb*kxr`HgC|Ci6QMs#h-+ijAX~@wQrQS^}S8_bj_hZmugIYw%F#er1>F^ ziCiMr=&kaaQwjl^pMzjf2=K>!{737@f9TtJ_lqYz{7K(W%7Kh7NCxa zWNX5Jv0Qv`)Q@Yv?zVlPGV(uNJH_{2RX)_**>);@^!wco@M*zIekMcWDwtb$U8B~>5s+uhnYjM#M-;i{NNiq>8 z!yyCXBb1Z&h4Xr7AV7i^mB#3@#!Hh@Q-PW0I%j zI4)u^C<3F5eO%WiSu|t2sR>m^nR8MwM|tM>bXd}9v9n+gABN%o48!nAA83&cU<2wf zfBpa^&k&DRd9pPvO6GKU(vc?-Ks6>VrD%&hX@XRt1zU4ilBznDYB5%a5S@rwx{$pz z>t5J+-_**<6CIX`5j$$|VP-saYKw(;Azlv-A$&f`r=&ffbGwY0r(Cgko}3{`%Ep=% zTke2EiuKL{m<9#jUjM+6}iIu1Cof|!Q916>#Tr}Wl8eZ5{k z>Owbw=`*nn$QW4{dEMnGCC^7>0W1cQo=A4ph{Xg%I4pUn($YYT-V``RD&~oWkz9E| z#pLXor$?TTQbo_ZM3Qr>9*aq4C`tnRYaaJx2B=&*K`+GFigMu!r{A9+B+L zFa-`7J_!4Hw~p}{!R*;a{=G!VP?5GH+lDkOvkA{YtkT~6EG4I2X?(WYragH_CtA_; znSvh*8Uzdyhy1vNN=&e{=QK}>8tz6bqaVL-tVbNMU}j$(f(bGx1)+Z&rJn_G3?D?c>pib?YzAHTI>q7VV;tV- z29g*At_7|gii%N)=;T-k3NXez_}_ro5ZHj+kaQ!RWR%qq$dU>|n1$>wSa_#SA_Cgv z47d9b{OrSnga2^)^y$kn22On-YsT)l1NZ7IJcZ!LQ(&MvQ#FEP39%OtDQqCD9a=kZ zkV5O|B!ytK&;e$Y!DKL|X@r6SW)O^?JTV18#mWs%U&}ZDY_mCd<%c`?2b>=X@c0@A zO96kD06HSsPbvd@=9a@G>+mF*1*;l#rPyn-Odyk<$Y=)8J(F2dn`y`*8KhO@wDeb_ zM|TbfPDAoM#ZUZF6D);9Qk%#VmtUrMc#_}oeXqbf-hC3!{kmtN0Oe@X@^E@>>OaLMvM!s%5Ma(YKjf#qq_!$J2KBhb15uZ86ihmjW-9PAA!NKF+c|Z%kH#n zS1H>#XoWNkzM_h9y-u%X2!nc3xcjAZt-Ou9Q*FIR6-1iEczT*ygBCZxa zBoS?7osmf;=fP?nk)vgU2+s+;W&!IMar^sE`ucFq-PJP7c5If4*!oS)*>{sQH)oZy z71+*z)Fak=S*?}ap$5_%*FtlxRs3p>REr}^ylQ?-o6bD(5(dvq)A5FkwmaF<6V#h*61 zyB4?g-rjP{Eys5kyN|l;vdg}J-~-#UAce?q%0`&G$2QWxl$Hg-5OR}}61B(;fUd}$ z(lCv2LM2gY^4wI#?)Av93e=3X2B(9i2{nGqxMBt%fv)SikBZU1dKiYT>$>0gfB@8# zhKvL}Hbi8V+PmBAX*RTecPnoafy{O;}1+1aKTEP?_~x!KKRd=QJf)ZR~v- z&D%g_e<`Xc>AlT7SGJKA6(GS~+S?0`3nytQ!^0mV`1s~t25htc8_zwCfhdzRHv0my zfP*TY_uIqE>3%n4*=vzh=bnbf$$T#V`;=fk4@N=*nAFqQNH=z0g9Um3-;DEbq(41l z#qJ0fI>>)N~czM>W*88-g+o%j#&$Tr%@roR|f5jJaN-{9a zCgH3k2?UybUT1bWfM)`@4HuLRI+j3nkEyl-^+Gz%QE<+BGoK9+d<(>8Ne7(xbH)kK zn9j6NMyvTJPWC6&0PccYR^RxBB+`1Ca-wo+lH$rxrU*46q2AyhyBY8skbimK_iw;G zue(xh7sF!l%bU&SJ+{%0Y_z_g$Klq&@uwm1FXG^r7~^52RxZ0Q#rr}Y;HH?s;Ld

    gK?8u3T}VF`x@ogM__tB^>fPPl_uRAO&JzGwHv7|O_axFubAy6; zt}1f7XlcG(nz=MLR5h|FlMIf1daA^`G^bR|4A|bR!BLv0r5%Ay9j)aX&z++H z6W$3XIHl$B+z9;q_cjML+sUCgu{^5<^8ABLr>3chuz6KA^WU3+eG}Pblai{CRFV?s*RiFTP13VaG2dFi@aTiS=?Uf{rrN7D?^Bb+7PvU;pUdMy|efJ?|2t{81Tjb0+RzWqx+Mpo9Q*j6iPXIT8&p6V8vgVxZ?&&{{!8%2-+Uc+?*9ldEU-Q}Bco_? zS6}kbw?U_H{^GhUF+^kn}Vkl9A{fimdIV zmBEhZ&X=yy-U^E$GO=Ld$DV?1^9Fw!t6R}23 z4P~E;tr~xniD{kOJ@b$aqNOvR?-OQQ-(1iB&x+PivL?NLr)Zk5bPO% zy$!(c{4IeP0tg&)+{Y~)=)FEGg+Lw2SJe|cQOFz7}z(t$;ti2Nt%0FNwjGuXZ`hY=ImO&Dzx=1 zGYv5xE??-t0|WWl7wBIi7sgnc?cz11jzuh zcc^1xX;1x-M*xdQJ$mlQ{Z5*}IF-s!OJDg2LShLe-(#W(y*9>EiLXn~G zJA90Tk3WmEH}7Ki#(Q9pyVUEj@B2SmpFaJpezo%wh3=!w{S)Ew)4El^ko)P*IHde& zALv+<>I8JkY3PDG!9g$w7!5k0u#>zI7)U|J4vtNx&-ave)aeX6?n0nTqWOKIieeAP8+rYUWv%iG?44JD~HU^;42+=5;+7UB&2OPHst86yD`KQoX-u)K0Zn%|o&%tpthaXgrP44P&$!N!(ALq;8AXHS3X)BTDET#e8E?0;hK z+IQXWK@T+RI}V%N51Kdz8)e_WRPQSbo+-~k#7apEZp1J|f6sew=KAn(o=;mPf4P!$ z+!=#aID%7398o0A*`nJZ-?m34XKly$^E5NMPDrceD2YjtIWxr0#z_;1Q|ACC+vhR^ z+h|6wRrxzf-N6L4sF%{_x_WUE>LX4KC|O+uraVd^pLB)dF~MU;J#xt)D*wza;d5bV z+#?LcTi*E|{@U;SfnNLn{SV#yK_73fsNvuMilT*pm~2LBv`C8INK!|Q7TKbE zfubc)CQloxjdKQ5M)*D5Iy0t_QcE2El=UvU=q!R?XALS$dq0x~DtkVtTv}JcmArg` z!}9Y%0-goPcjrU<_S#FaV3wr%xa6`~FfhyHw=g4HRRBFj}ANx3|1>X9JLO)_Oit%gHI1`>E*K6(IworQjR~%vpGTOl&?9K`J^- z8|Z;zVUUQRcZzx7B}IvM?Cs$crU{+r>$O-emsbM#OpM8wN^FCzXKwB{tm#l{CJ{~m z#jOzjzyp6Y5N{!PD}gfr_nR}RFqb+Im#;VLPf*|!4fZL4yq2&qoBOy%VpwaBGEG0# zxNl|z*epqB9mL9j>)bbAZ`Qv8zjMC^Uh9C2@95dx&x||y1c@*>r7nwDKnO7% zE1NvxiSB+!-*uNWv0GW0p@5Y~pR;Ktpk{Cy|@W$r)jyNU57?U*Zt~+#s9#? z6x@$T0F*ppKs~u-P0d>hSDz9NYTuXY^5`1`-sB#C1Hi8<==T>`+yLWQf`$3GIBlmd zIlg~;39DT0x#(jXQxDQQVy-Hz+2DUJe^+5knfNbvwcEq+ zgZIE7ce&TW;`s45A8t0k+=p=guCqrAVMq{3x0{<3W2ixv-gM^4wJh;Q(jOxP46~?d zCBUT+mz*(1CrJw_5DOCeMd*hies7QdN&qkTn+{*k0QXxh`_Cl!JVLS#hAv3xAz}r` z-vJRyE&>xhU?VW(V{6FCe}+XLP&?Y_sZlWlWTLBB+$x6!13L8e`kEo?(4n7P?JmD% zxqIGO{PzHW#kl3q)8sj5o#c0M#9%otK-LX^&_sqTVMMge&#>@9{%LN z{yIG7(T_mR%EnZMHvx=VYQqfdsQ!UROvGC~VM(K%gcs-t6lNx9fwv7_Q#n|J24xP< zbj&?vdW2Q~001BWNklOFXt9MIAdEB;P$$2!S>}ZMfEKH~9q)bL zNp3cqmVr{7!ABOFj>-X^rOBP+-pvcZF$Hc~^i5K-$4AE6dTS;zX|=a1i@*{A2uBn% z^@gwx%qOL?P1A2sW$x5Ousas7329_|v$KY;i5fyXAooUBCKL^l%4|GE6^-I|)gRx0 z{WWitQ)y58i01}o%yVuKeQ%y58{2|}Pa6pY)8Tr9H@)F^{Mldj9Q2o68Au>*Hc91a zl>6WzbfKECxkP%hPsmvyIa`x1%jB<0<4!ydS9?M_Xp*G39Iv5|bzrTO=YPEU~{^k<0n?tbY0(kzJ zGiR1&)~!}6?e7O$t%^TW1DqMcS+M8+L0GL;hcU(%)A*Sr9?&%0N3qFTCPtAuLqc@t z>xW@@=5l#>D{zSeSnLD+3U+WB2*&~L104?zd+hA^4&XrGL7s8qaGzX}Jj_Kp;K0&= zLzm>fvvbg0pa75<Y(W$NOFho!|`%SKizfR>jA9)3NyBt z|7-xfW3gDg1Hdcuwb+foQ!U14f_yB^R!RbrM&deoHVo5}uGR-(9B{?F98)&DCKB0%2$Ty&>~z0tW(R*w^z_Uga0XrXzXAMj0N)0nk1;+~W?x9)(~Vq? z$<8Td*;yiKC)doz!@3OajZTP{>{CB>-6V{lD2iJhWh8x9jYo zLyQMx2q`!Lv5*)e5JQ^aYseuE${s(xr3}O3 z3lh_urwb!vf}ldRrywv0*pRTWT=v5-{GM+%ugl=&Jqg(78vrb?6U$oy`q25|VDOmC zvpw5o8*{9u87i+tN*TT7mKe-()LX&vifvqNGSd=Gg3v7UOwFPa3YabIGr|C8HQa&- z6Eia&={*fSt|!n7}@>}lq58Q zrJ9%3Orx?1edA2a44yPP&Vqx86V^(rgei_pvul$UNW&bfg`XxmNFZ|twgzDTk@#(+M0wCw}kmw zDG^=HgM&5ySUa3)Ex0`$~Xsx#gB8pEz;i35&&I3FIOWlaS~yMZB0{}Zy$aQD_W|#AKy1dX!ZjMJ$u}1X=wBt>3(YeX z0jxs^{{_I$4a4wQhyHa4;UR>TC7_^L65LS`Yx;kTW1ynO!M#6M@|9Wl9T%!8-`RnZ z*;ye1WD;wwR_M+4WZQd;H2Zr_z_s+}rePTV(=c>DwzI>x&6d$o`=HjH z1N<^6rSt|+uzoi}PdIaM_9p`DJ9l<=-tkxLDdAwf{^>r1CkL}mz!0H6YuV^BiCjoZ zF_;uMw&BIfrieH*x+6>E)olKe@t?63^Pu`fC;;p>!{!#bUbb4TUU`uQJRg1lVA<49 z9v;S?RBxmrfGEqN#S+9J#KPGrZLmYq7#EP7DU;5zL?cOr+}Px#J5y93RM89;X__-- z2qzfDm7`+w+=?g!my+yC5k4>{5If?=(f1av`KXW4&;82pXrbg`{dXM~9qDUr7WgZ4<9b)*#2p&RDEwQW&@Of0H|sEyEj@o9a&-r;SN`IAa@~ z%U41Z`If3FeD#hQQIQJLal#rK)3ngSz9}KOJ+9ai?l@yZicQXl&2lL!G}BN^%7tfH zDkQc%Y?@0o_aD*dqzyfh}lhvh2wQe#ni;_uL%09ol!y^iz&*q8ZI(?G@+d;NJnPX z(T+g>ICJcp{=VvPRDr5=LVAI~a%z@T*l*rCCMA4axu;71%h*opm6>_gfF5eFn4*JB zzmXFKkTg1ZYm32}y$q=+K~V_CAv)jn?i=xWpZA6M!#{pEPh5MQmd8$DefD(dtf7V_ z2pNLIN8YsL&dMM5+z%%absEs*Z7gP_+G{E6W!7GwuSh9`)OUK)t`l@aG*4!(fQvL! zj!Z4l5@JY)f6bPKr?5Mfxp&fuBf2b2Saf$}|5ae+#EBEn2qAoGD%9%IHO-R_XSBcu zoT_(fvl)JK7=|BStyZtP6L!eFXwqK+%(SnQX7+mT_0oo(Z28h-QQp>{Z(JrdX}NZu3L*doM7yupUQ4|7YwHa zaR1%q>LCE`r^22jrUHZNwO#Xo3JY}Z)g2{mB)wu7h96uk7H{hNBiF}YmnD6!soA>g zy59oucz6FsK%YCZ z82jDvyeaw;mQtTJ%&0jhIkg!|u)~Hx5O65>R}aJR!o_0o=B_&u5Ye5StL^`PE-+=Y zTrS@V;A;R}Il!M|urDUCm+c9vD^{Mewlor(&d~wE0!xa$*}XcmYXN)&fOp}-*hbv* z`oLcMec!)g0RB|K&jsV9xyB`>C?F*9k`faTHYf>0`UL%@X!I)f9bL(j#HS5pH9C>( zCdRRE?#&5=VD~!4oo8$|@$Ba8<|q2y{teB!TS4vHZ^tD&{rbtv-Tsk)PRJuk_91mb zo@*vEA|(6PhS+2pnMEKHo5XXoCC?!d%Avo?bFIV(ph=1(Xf%_6+tBq-SpaXmXKkPR zP&#j~q3b-lM@Y&+k8uRNXtbdh`+GT1Ee3qSuK@*BVQ%lM?v_(a_PuD75Mrf$)Lp@Y&;HivZPRJa11GDyb! zp^RAU;FcTSD|s9@dAX%6^9XQq3eqd*?gfd1>E=`wOHzG zlYMn_(W@fkKZhhHxqqlWr}E^%go_$Da6Ue|2_He>DgXn`A~ccEaRhIVY;ebVK)T&b zFX;RJ$1cMAzN729UpYKH{B}tHi=#bEp_zW(-ZDHyJGBP;}(dwxJLXz&-jIcYn5-J#VpCyy+rb<&BxWgyh$` z<39q0yop*Pz7c1g&(EF_@J_Ih86Qq#OgUkx@6JWN3hshJ zu}CbzVxcOOm~YA8Fo*#_2eU=!`!BU+`0`;mcw7S2(Ibfz0)U;py@N+~i})jiUuUD1 zO^PWWkQwvyZ2Zniv)KRv$*~QPCBT73FeLt>NGL?WJmguEvVbXzJ23OL3Mr6GI@$+$VuA z>C%~)O|wT>68MxSJWk*84PTB={e;KxAmlIC*(<;MGG}A)yl8Tgg5cd3SDlm8BR*lBbtzrimM|e(`*Xv z>?#l3N;>J}eT@yzc=5ARw32IU#0SxU#3f?h+7D`@&7#mqn5|k#vfRF+Ty1fAO;S~@ zv1cxy%NjT}-F(kqKa>9!HesR0bJRvRJAec3MOh=q!6w>`?|q+s{AEAwpYmx>^ZwqK z>%I?rq+kogA!)sW(Tzm{leQbPIMCAGmpem-k#8y{>kgTF!Uc)6RT^l?D}RgtmBAU( zg9rmXfa<*Du$SoGnCcBLf9E6e>Hr;PVt@w_wNWAvXh5(u7R=~ZNI@}9gPTwp<#(so z%{Sk?+S}WEvcxL@nB>7&T`*cEfwp$!v7NExa-p}m$ESB)_q!MIBb*I=|7=08mUJdR zD{*E_U7Oq{xIo(WXI+Z;EO)#5A_6Udr}xWoqc3M@D=YQ-fL0TXy6>gbnyobEyEeRX zczAfN%2Wc}PrPdt@-|CSE}>*O5xJB}oJ#mvKr+>2>JP;(CM0W#M7+m@ zW=}Sg?YZ@iXTL|ra6z9#0@!8X-z9n9{QCo%0G4sQtY#0H6vAV=I`j??_SqqXS0b^X zFXrnv-G~3+8~<-;ytUQ(o~V{VqiSc|1E^_jx#z|!GN67zCU-G3&vBv#c$0;LnWM!x zh#K9cg?Ylg#piD$8TxW?eg{-j<0Q z0K`nANqx5`xuzLJ(-+XfGf$sBeIkQ^2LLRt*Pla$pXvMl1e3g6LQ1fKWgTSJL|MWB z+j_{G+^NcK%^pIIX4tAK@T>6)O7c<1Fbr=yd-mYh`+oNgNR}`6ye<>~NP^$>UEgH@ za3gN-U?G56j%%JNop~8$Xr|C|lm(;=UiM@;EVIa2fs=y5s4Y2ZDGVBw{7hPG(1=7D z$#j$4vuBC1jpU&0Q47yrm?^r{%1p@M0~cMuW%s`vS3L4@{PCCmtiJTQFT&5f^3~cu zyYJEy0P z>zSsK2vtE4>FCy{J;X6im2^a0gj%?0-sl_!E9ESh30PAAMmG5Fjo6sjztK&n{c zX!4`Rq)wBdHF5TF4RimZ2BzU8jdF68N~dRYS1h&v(WF^K{XHho6Ib>luDRM@_}$Od z!ymkd&Dpc?wSzK50nuc62hZ!;k%a7yA<=BLy6l7_IAZjav%pMg8=tmno4{?C$+^j~ zFwcA*O||R#${{6_plNP_y{%tjU7O;h_@Zj_HU4IsBeUZ@Y2m&R0&hGExA(TXrch8l z&TjMQWHUm!MRpGrCUI(+TCiOcU{Os$uL)SrB!I1~jM4v64fdC^tj48Z%TL!7u8uM} z8O6=vz<1p69)9afei%>t$6tVN{Fd*-@v9!n6IXl`;}GSW!J(MWewPgcvoTIiS;|VV zavG;=XoQTOJRI^M-7JC?5lrO@f-cSGMj7I9~_MvU>LlKyip8dIn#8Y2HRr4E%XoS!ij21y9_ z>Xu>WVghIeEAN%lsP)OVZ!HVc*iiThArv;&WCOe_zq;P+DS#f#GK5i9I*hxnH6&CU z|64p>5G9=*2EFy*0B-Ox`EP z_{jv1HT7j%o0Qq1RWtN4@s*s^ruq|){+(TtwVnQ;YzSTVy3pGT-F3a12D_RElT%0L zaro+}CY;4Dnr_178{a$&z`ZZf@W!|6(DI(Bb2%3_C4N;HNqYY#`Y)^vzqA9qr_hgaYC&-1}w8e=>S z!Vm|gW6N`Yp_tFM4q%rbfG8jq4q!scJSOQ*R!ur`3m})|N+O>U;*kE;63aLFH-!$e zPY(g#R@T}6{^_T>^krteCb~juROO`039^ASCIsH9AgKPWdWff)kx)QDX9i_ zc`}m2VG=7P_JN}IeTeJLdU$7y@kfsxTYbwttL3k;m!FRx^5!=`i~w-#k~2yf=xL%V zaf(biXr^YYFNH{#xn&VI6LzSaGFH4GOAzMh`Ny2fn^<*%OOywMSlD(>1X=WP0+L#8 z6~P#?Z0! zL8~lmX4dA_Xpd}sGMXHNsdN;LB)2_6I1g>jJ-^T*Kq=z6kRHkAea*n6)~7Fd7_r?BZkh&pOn#4Q#;Q#U33{p|Cly1@JDc1TjSeYP`OKQ+xm2O5u(Pwn z2V8Z3?jF0;PrvIVF9|F5%N~krnF3Y6H<_M!iq%0f{R`N=WG|e(l{$NP;0Nmsc6Sz; zXJyW@m;_q6%gjEp;i+kMEwue{GeADdSmrCV@%L*3KiEbVj1?kBTu`HzACB@792Y?~ z_Eu|x+;|p_H&!g4g=oHyN8ZWlDo$V9JY%LjPN;vC5wXHvEjx|cM_Qv7dAn3>T5z!m zI4;n!2$<15j??vMVgKL|XAckXyTA8(JoBr+9q+&8M(kYvk-YTUN0COX*ZVM+ldyyq zr3_!Ad$Nu5B*}#tWGV=(EA7+F6PikaQP#$>OgcypBq7Spu@2CgjyMJ^t~jI6Md)yW z&21gy8VE~@LB*E5cp0eZjI*z6oqfoSmfa!Y1#FgywarWWc@jc6GQHZ}?{-GNdwZcD zhVEe`F3HDVHB*`HlQMv@fPk{bIWj#tZ{*?O_hPX)bFndsT)_ST5MM9(=aF`NzWGjp zh-$SyTEdhsNPlwzDLdoT#X*s5qcKggO&8}R>S)?!GDXx(Mn4}?L*jw_v`)BY+*N6l zlOK~9^LYS6w)eoB0srQm9eeKw>2sWo2)}3WmjU=nfU9PTogEB0<{M$c{+W)FXeVTL z0(Xr>!SP%lbG@^B4BJ6+e($vqEBB@hpFb49=yUBXqsGmE(6Cvw?M2KZfxZ|WAzTRX z55q7#oaBoDT&fb>#0hg)v#n2U<5w$_b=>_|3E9eoGHz>Z+7bT zy*DY~YGyc1JRNmEbNl)=*1xvE;sWdsFh`tDeOGHiw(; zrK^R%&)vUA@Jl@kBnN{HaL9uXL@>fEck4dUW{I;+u#zc!Y*j`&hnKL8hU<|<90@o6AMNT^5n`5LL?ABnvahT|_gx{ne=kzfVA|qT*iPC75 zY9QQ|31>8SXo%o)p-Zm1iaVEGs#pK!TlA_&J(*wfoG-#Np7bev^mPxytX_|6001BW zNklF+&!j#(fJKG+`{)g=>5Frs{1j4I;>jMsu&!5 zX;!EbJlE{6p~y|}v1YQ`2BOsQE*fKa%|I?pNuf1+ewLCw-qW#t8J9XoDRC1X!eVq{ zEy&+^yflnO_h~jrNkh?8$*dMcE#{KEZGkxiZnSo1V~nDg!0bAL-xC z)NKxkP7D0T$MwBk*Zt2AW^=g!{&Il5%7JIvRPx4gZkPryo=vhaRWXEwCMX!R1gysg z2M9OSdTVpN>-%bpp?1dH*4GB$zXAB64?5hMUTduIE;@cu#0e0O*EmKm1r%IxORn7;%T^%<*Z>e_ zXGrav!QIgr>>xjvJ%HYX;PgYq|I6N+#@d#q zj_p{+7#l^2NgOGTll(xDlE{ikd0^(A0QqzSB8p@3p@7$&YVb`<(Isx2n!9?v|=l_tqKqUh7-m_rA~j3_Ox_ z#SCki9h&hG$R3mHU`BjF!e6KBqpQ^e+3#Bl0G8La%dcfgn|n3xPP0YAlDS=qr%heI zdA5pY$1}0y#@B;aNxPdxy%|y_Qr=e-#QnP*FFy|++<{J?#C+#E;_;{u2`fQo^pqx? zyKQe!QLdH5Y6%Df9VI{{ZJNg9TD9GRMy&S^`Q%5w61QG{%KzTq{fGS9|LgbSKm60* zF_YD*K4fTBLbDgw(y+ao#Q|{8bQNl&K|3?eU!vp!Q}#|vt~w9C~t7uc0D z5?wBi4J*jE0Gu>aj+{=UiwbeMAX<`BMo2_hjn1&kAu0PlYCQKj&;!Fs)K zvdB}va^AoQP&JW7Df`x~0b0b_ZlU{(6?^3_sGyF1*e^ozG?!6Ub zpE#TSA{IqmrTz6ClavrLiaToOei4)eKJGxnQFvXkyp;>dpZLV^!4H1$(xt=0p*}qcTA>o)#}21vHP@}-*fss*u1h!n1r64l4$z#O zgkN0m$L|5~4Y?8Po5HiU56zv%7H~TCK?J-HZ(4%n&h}`o;t8MQ!21D6@eF0R+4lQf zYW!KlOzxM!1qO1^a^Mqoz*wPUUF9>& z)DOew^D|GCx9Y>LckIkF)*{=lPF_9}YkIWXnw;YRdG45#6Q%ZV;;%~z6nYSHq zT~;}NOTd>C_;CI#K~1(*zsDL1RcfEQhmc?hgok(?2nPV}DLXy)LFakp*#bshdPHg$ z#T4}ajqCAmT!3|7rM`2Ve=3aQ2MB&su64D1s7NzA%?Gr>(lFC(n2;n4LvUZE8JR@@ z7?OXV!!Q801#pr&>#*`!8=0;u*a!}5Oq=(5^gV~*!HUawQz+~6=?fCX@FXcrrF^0_iOm8o4FXlqRN%VMKKZVsoj=hBZ`iKT+_fwkxa1R`cQp!HaE?5-AIBgxaf>}u3XbHKI zH}a%ph254S8x#efBW!&cKmV~$;v4_a*FpGdm~!wWirq&x=j54suJKpc0=O%Hn-+>K z>LGN=z^ukY#$NI*oKg3&X|~le%gVkWXy?njo14+I1rMD9t!SMp&e}kcqA8a;DL5BF zDD1Yfoi>56MrBn^LC%!r>M@xac6CuL1-&h%L0R6);&sUEqu0UqLUIT=Io)7$da7x= z#hp8M@%Ml9C;5{<@e}yRKmGHt{XOhme?kvG@&3SJ#c7^!eCxKu6@pn-EPM9ErN?ZdA_Vy9tb0atk41yv(U&;*iIr@NZfuyx)@sbCooS~g>ai$N;b1Jag z;PIVwNI&-2V}rS0N*}XQdTM%7Gs0XDmiz4Q!gGBV(r>@rpXbsgybOd-yX(}9tG*%A z8KhmxRIKitNKFmJC+NKXCWk(^-M|5b!msS=g67S;Ai+0-gW8CBvp2Xuz`s1i4e3c# z8&B>W%Q=D&%^Oh3HZ<*S&okiDZ&RbZc&&kx2>u*^D}WxYgDx>2l!ccpYv87Omyf(z zX>S8wTc;3)w;ccs!Bxdl3m`zfKZ>8DRPXFo?o$P@doq%3)4Wy+s}=?e$pIQGH?8rA z;mz1brU_pWg8yMND}YO=fK0NOu{QuB%h%5U21);b#$UMrz|-#QWdN^aFTi~jskx90rgP#GC?z6=L8h_s>cA z*f0$L?}H`rHUNMH1O$=<*!c2f&=jMO%+&7;_(;ilO4&?GUWBPTL9mJ8)Yw;&XCdwf6+vsOn}@Yt10c5-`bw_ke=f8+c9k-qD@e?U(>`Y^uc>%NxX z_MiR-ceAsJ4#zzn9v)yA0^N@7Nu4Hq{YM+;V7AdjMpvbz7Fo70xAMj1TnE65Bh z74d2Y>JreFQ#&g1YQoZPk46QL5>K}qzVzx%9N&7u4z3;vt9@-}!7UVAD_uvaWVB~6 zETvg&Pfl(B+T-}t@BEIQeeSpM{7bL!iH9E|rOeu*%u04`rQuR^B$a6OKWZ3XQpKyZ zyIRyttg$)l0&lahv>@C0svCOa$3$REm3cAyT$bpCTvx1#SSXrkVSi;MC|T8cmmQ_7 zzExj?lC6=;=3VNDV;SX?tA%Q=rd) zU5PyS!1LID--|L#@H-LJ z5)o6yzA=RZr)@hu40Lz+JV(TN`%XHf-Fy_^f|1~T znF8!^nhYdm#$}jW9c{MBXVoGVlU`{I)MdclgcTaU(!oDwnKYb-@t((v)=eRIEmitz z`qa#R7{Dhk;`NU)e%j1FW#k_cJoaUJEte|^K~^`3YVeR8pp5*Z#_^{B{OUd0yKn$_ zlx9cWqjJJ;4IIk_T^m7F8b8ZItj33+ zjjXcBBvS17Lz%>fL_x`!ID{ckl0q4gLO`Gpl5&vM+9m(yJPgoK7&5Fg)#~LM(=Vk_ z3JyB68IZ&kB*;v3+=SWR8O^@)!E$(u06-S7=A75jL$^B*Ww(TYB$8h%4OP^>NxfD7 zSbU9&sw@2(7V;-rJ0O@`gW396cJwl_{{kR~QF56XCk>as;QZ1VlYb5UcrTSV!O`cf4lKe!E#=b%9nC8@V@6z*#|55K z&y++j4r=4WZlav{HlD70?UKseJrx&bO>fIq47FTH!cI3Q`sAlRt-ty={xAOK7jI(! ziEDtFrtQpx%0xPwz%Ph$#YD zLk?U@rA$M`vhLDq{B1@}v*?@U3g$|Qg+9%ZCnu-eZnyN9LOpJsoZ^$8`ZRv>CqKsT{@#B8pI*mmf0V+g z)x%e@clk@PKG+AM5T_aQc0yF9j2a{#AS1?ykGcQ;7ZDEc!a}6crM(jl55G^xpM4nH z7xoY?ZDB)9?^cCfCuW&%y&j%CP^}NUK%52)k4!pv-}5+p_YL}~VcL#FG|(i- z(ycNGPS<3#OIz5GNj1@w(7`hBD-Vs8%o7!e-4ge^-ja-XRo!$ZIVxn=O+k$D9isqd z1^_kB^Q_#tiFB4MSnRX9(favCt0(DRFeC0DJE*q zzqjh2mcd$x`sEPAujAsC7BeDl$?!UX%ge)vIYiRLb6feg@&t_$v;hxZ_u=dH7^fc( zW{)G-pM_zSCsi5OgH>`<4ogqkrk)(0sXhxI_S%3o>c2b_I;s^lgl_E8Pzm?W<$xIs zDi6G>Nm&zR!4Y8yUGOO(rR_a+j};4WkD90Xdt;3Ma5y-)`(O#YMF8L%fq&g;oG50qt0)nx3xl>C=%@Mj{OKv^a?ILk$ zItqASD0wFn(b*z>Q4^J~%?t+a=4KVSj8OAmYKgN5$xU(Kop?$MWAVN$%1cOvO6FX6 z8210QNJI86g|JO90Q23Yy8+>IW>*6cYAGJ7$r@(meu+Cs55vr)zhR{0v0uhH$zI9 zo$}B>>k9FF_j#;>!E!gZjw&QDhq$7 zC@qa}1Qwnjo#V)&Xx2U4=VS(xICAm(x|)WII!umc(wu+B1mKHEYSxQ-e_k-NXH+JM zS`IA|HT%_wQ>Q$6uTMQ}KNg(V7pQ*5SiwL*V{$?Q^o}ds1xd%!o4A(x3;r8R4{SjyyB~2X_OBw8Amx)=O zDbjE_1|b@FaE#%pPtguu5i~*D5gxX%ho2K(`cnGwAUI@ZBy&G4$7Zv|X0s(}I5;}c z(bdZ^3vhQ%rxQ+3Hn@3m7r*d}pR`~2g^wdDO0yJaa66+!P7q-P$5BI|S*Cxu958Zk ze+?sOW{DzWhQZR-?kw)Mqz%SUs=cJU5()DNc+AFN26*(WR1!oo)?>tT@-lLysiht+ zrIaF?u^A60JShS+5tIY+6fv4+Yl%(lfSW78IEKK%2u0DEKA-Z|;#;RH3LwW}z!(Ap z9f@*ODp}E^N^}cp24jF_d8ilES>ilJ#D&0Au$TyDPD(}$K|wJ?7Rfx4(wrO((!9`Q zrol*o@`TPLQ)3C5rj%-J28W4+VRpnAHM?Vq4x1+joU8pk9$kNm>;1Jnst8t;?tj&k z<_%p9W-NQQB?8^%WI~(*R#zUui#NW2Z}~I-rF;_2)>t20634(f4aC7jECor%1Xd-6 zraNXS4Qb5~%%JFI10e)ub3DT55cBo2kmE)iFtZXK%aHu#=-Idd5(gW=7;AZ+NQk0O zo>1WcSTGsZG%0Dbj8s9CV?>lI5-sVi>?+v@tLQ!a@LS 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 K(3&4F{r+E986fZg literal 0 HcmV?d00001 diff --git a/resources/splash.svg b/resources/splash.svg index b15cdec4e2..9bdf04ffde 100644 --- a/resources/splash.svg +++ b/resources/splash.svg @@ -1,55 +1,219 @@ - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - \ No newline at end of file + +-X

    7yKfEyBSy*(l{^xa@bE- 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