Add language picker.

This commit is contained in:
Dario 2024-12-02 20:22:54 -03:00
parent 0ef5a57bb1
commit 03381065c7
4 changed files with 140 additions and 43 deletions

View file

@ -1383,7 +1383,7 @@ void Video::CreateHostDevice()
g_commandLists[g_frame]->barriers(RenderBarrierStage::NONE, blankTextureBarriers, std::size(blankTextureBarriers)); g_commandLists[g_frame]->barriers(RenderBarrierStage::NONE, blankTextureBarriers, std::size(blankTextureBarriers));
} }
static void WaitForGPU() void Video::WaitForGPU()
{ {
if (g_vulkan) if (g_vulkan)
{ {
@ -1425,7 +1425,7 @@ static void BeginCommandList()
if (!g_swapChainValid) if (!g_swapChainValid)
{ {
WaitForGPU(); Video::WaitForGPU();
g_backBuffer->framebuffers.clear(); g_backBuffer->framebuffers.clear();
g_swapChainValid = g_swapChain->resize(); g_swapChainValid = g_swapChain->resize();
g_needsResize = g_swapChainValid; g_needsResize = g_swapChainValid;
@ -1449,7 +1449,7 @@ static void BeginCommandList()
if (g_intermediaryBackBufferTextureDescriptorIndex == NULL) if (g_intermediaryBackBufferTextureDescriptorIndex == NULL)
g_intermediaryBackBufferTextureDescriptorIndex = g_textureDescriptorAllocator.allocate(); g_intermediaryBackBufferTextureDescriptorIndex = g_textureDescriptorAllocator.allocate();
WaitForGPU(); // Fine to wait for GPU, this'll only happen during resize. Video::WaitForGPU(); // Fine to wait for GPU, this'll only happen during resize.
g_intermediaryBackBufferTexture = g_device->createTexture(RenderTextureDesc::Texture2D(width, height, 1, BACKBUFFER_FORMAT, RenderTextureFlag::RENDER_TARGET)); g_intermediaryBackBufferTexture = g_device->createTexture(RenderTextureDesc::Texture2D(width, height, 1, BACKBUFFER_FORMAT, RenderTextureFlag::RENDER_TARGET));
g_textureDescriptorSet->setTexture(g_intermediaryBackBufferTextureDescriptorIndex, g_intermediaryBackBufferTexture.get(), RenderTextureLayout::SHADER_READ); g_textureDescriptorSet->setTexture(g_intermediaryBackBufferTextureDescriptorIndex, g_intermediaryBackBufferTexture.get(), RenderTextureLayout::SHADER_READ);

View file

@ -15,6 +15,7 @@ struct Video
static void CreateHostDevice(); static void CreateHostDevice();
static void HostPresent(); static void HostPresent();
static void StartPipelinePrecompilation(); static void StartPipelinePrecompilation();
static void WaitForGPU();
}; };
struct GuestSamplerState struct GuestSamplerState

View file

@ -83,7 +83,35 @@ inline static std::unordered_map<std::string, std::unordered_map<ELanguage, std:
{ {
{ ELanguage::English, "Achievement Unlocked!" } { ELanguage::English, "Achievement Unlocked!" }
} }
},
{
"Installer_Header_Installer",
{
{ ELanguage::English, "INSTALLER" },
{ ELanguage::Spanish, "INSTALADOR" },
},
},
{
"Installer_Header_Installing",
{
{ ELanguage::English, "INSTALLING" },
{ ELanguage::Spanish, "INSTALANDO" },
} }
},
{
"Installer_Button_Next",
{
{ ELanguage::English, "NEXT" },
{ ELanguage::Spanish, "SIGUIENTE" },
}
},
{
"Installer_Button_Skip",
{
{ ELanguage::English, "SKIP" },
{ ELanguage::Spanish, "SALTAR" },
}
},
}; };
static std::string& Localise(const char* key) static std::string& Localise(const char* key)

View file

@ -4,6 +4,7 @@
#include <install/installer.h> #include <install/installer.h>
#include <gpu/video.h> #include <gpu/video.h>
#include <locale/locale.h>
#include <ui/imgui_utils.h> #include <ui/imgui_utils.h>
#include <ui/window.h> #include <ui/window.h>
@ -54,8 +55,8 @@ constexpr float SIDE_CONTAINER_WIDTH = CONTAINER_WIDTH / 2.0f;
constexpr float BOTTOM_X_GAP = 4.0f; constexpr float BOTTOM_X_GAP = 4.0f;
constexpr float BOTTOM_Y_GAP = 4.0f; constexpr float BOTTOM_Y_GAP = 4.0f;
constexpr float SOURCE_BUTTON_WIDTH = 250.0f; constexpr float CONTAINER_BUTTON_WIDTH = 250.0f;
constexpr float SOURCE_BUTTON_GAP = 9.0f; constexpr float CONTAINER_BUTTON_GAP = 9.0f;
constexpr float BUTTON_HEIGHT = 22.0f; constexpr float BUTTON_HEIGHT = 22.0f;
constexpr float BUTTON_TEXT_GAP = 28.0f; constexpr float BUTTON_TEXT_GAP = 28.0f;
@ -94,6 +95,7 @@ static std::string g_installerErrorMessage;
enum class WizardPage enum class WizardPage
{ {
SelectLanguage,
Introduction, Introduction,
SelectGameAndUpdate, SelectGameAndUpdate,
SelectDLC, SelectDLC,
@ -103,17 +105,16 @@ enum class WizardPage
InstallFailed, InstallFailed,
}; };
static WizardPage g_firstPage = WizardPage::Introduction; static WizardPage g_firstPage = WizardPage::SelectLanguage;
static WizardPage g_currentPage = g_firstPage; static WizardPage g_currentPage = g_firstPage;
static std::string g_currentMessagePrompt = ""; static std::string g_currentMessagePrompt = "";
static bool g_currentMessagePromptConfirmation = false; static bool g_currentMessagePromptConfirmation = false;
const char INSTALLER_TEXT[] = "INSTALLER";
const char INSTALLING_TEXT[] = "INSTALLING";
const char CREDITS_TEXT[] = "Sajid (RIP)"; const char CREDITS_TEXT[] = "Sajid (RIP)";
const char *WIZARD_TEXT[] = const char *WIZARD_TEXT[] =
{ {
"Please select a language.\n",
"Welcome to Unleashed Recompiled!\n\nMake sure you have a copy of Sonic Unleashed's files for Xbox 360 before proceeding with the installation.", "Welcome to Unleashed Recompiled!\n\nMake sure you have a copy of Sonic Unleashed's files for Xbox 360 before proceeding with the installation.",
"Select the files for the Game and the Update. You can use digital dumps (PIRS), a folder with the game's contents or a disc image (ISO).", "Select the files for the Game and the Update. You can use digital dumps (PIRS), a folder with the game's contents or a disc image (ISO).",
"Select the files for the DLC Packs. These can be digital dumps (PIRS) or a folder with their contents.", "Select the files for the DLC Packs. These can be digital dumps (PIRS) or a folder with their contents.",
@ -125,6 +126,7 @@ const char *WIZARD_TEXT[] =
static const int WIZARD_INSTALL_TEXTURE_INDEX[] = static const int WIZARD_INSTALL_TEXTURE_INDEX[] =
{ {
0,
0, 0,
1, 1,
2, 2,
@ -134,6 +136,27 @@ static const int WIZARD_INSTALL_TEXTURE_INDEX[] =
5 // Force Eggman on InstallFailed. 5 // Force Eggman on InstallFailed.
}; };
// These are ordered from bottom to top in a 3x2 grid.
const char *LANGUAGE_TEXT[] =
{
"FRANÇAIS", // French
"DEUTSCH", // German
"ENGLISH", // English
"ESPAÑOL", // Spanish
"ITALIANO", // Italian
"日本語", // Japanese
};
const ELanguage LANGUAGE_ENUM[] =
{
ELanguage::French,
ELanguage::German,
ELanguage::English,
ELanguage::Spanish,
ELanguage::Italian,
ELanguage::Japanese,
};
const char GAME_SOURCE_TEXT[] = "FULL GAME"; const char GAME_SOURCE_TEXT[] = "FULL GAME";
const char UPDATE_SOURCE_TEXT[] = "UPDATE"; const char UPDATE_SOURCE_TEXT[] = "UPDATE";
const char *DLC_SOURCE_TEXT[] = const char *DLC_SOURCE_TEXT[] =
@ -148,8 +171,6 @@ const char *DLC_SOURCE_TEXT[] =
const char FILES_BUTTON_TEXT[] = "ADD FILES"; const char FILES_BUTTON_TEXT[] = "ADD FILES";
const char FOLDER_BUTTON_TEXT[] = "ADD FOLDER"; const char FOLDER_BUTTON_TEXT[] = "ADD FOLDER";
const char NEXT_BUTTON_TEXT[] = "NEXT";
const char SKIP_BUTTON_TEXT[] = "SKIP";
const char REQUIRED_SPACE_TEXT[] = "Required space"; const char REQUIRED_SPACE_TEXT[] = "Required space";
const char AVAILABLE_SPACE_TEXT[] = "Available space"; const char AVAILABLE_SPACE_TEXT[] = "Available space";
@ -268,10 +289,9 @@ static void DrawScanlineBars()
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE);
// Installer text // Installer text
// TODO: localise this. const std::string &headerText = Localise(g_currentPage == WizardPage::Installing ? "Installer_Header_Installing" : "Installer_Header_Installer");
const char *headerText = g_currentPage == WizardPage::Installing ? INSTALLING_TEXT : INSTALLER_TEXT;
int textAlpha = std::lround(255.0f * ComputeMotionInstaller(g_appearTime, g_disappearTime, TITLE_ANIMATION_TIME, TITLE_ANIMATION_DURATION)); int textAlpha = std::lround(255.0f * ComputeMotionInstaller(g_appearTime, g_disappearTime, TITLE_ANIMATION_TIME, TITLE_ANIMATION_DURATION));
DrawTextWithOutline<int>(g_dfsogeistdFont, Scale(42.0f), { Scale(285.0f), Scale(57.0f) }, IM_COL32(255, 195, 0, textAlpha), headerText, 4, IM_COL32(0, 0, 0, textAlpha)); DrawTextWithOutline<int>(g_dfsogeistdFont, Scale(42.0f), { Scale(285.0f), Scale(57.0f) }, IM_COL32(255, 195, 0, textAlpha), headerText.c_str(), 4, IM_COL32(0, 0, 0, textAlpha));
// Top bar line // Top bar line
drawList->AddLine drawList->AddLine
@ -452,36 +472,41 @@ static void DrawButton(ImVec2 min, ImVec2 max, const char *buttonText, bool sour
ResetGradient(); ResetGradient();
} }
enum SourceColumn enum ButtonColumn
{ {
SourceColumnLeft, ButtonColumnLeft,
SourceColumnMiddle, ButtonColumnMiddle,
SourceColumnRight ButtonColumnRight
}; };
static void DrawSourceButton(SourceColumn sourceColumn, float yRatio, const char *sourceText, bool sourceSet) static void ComputeButtonColumnCoordinates(ButtonColumn buttonColumn, float &minX, float &maxX)
{
switch (buttonColumn)
{
case ButtonColumnLeft:
minX = Scale(AlignToNextGrid(CONTAINER_X) + CONTAINER_BUTTON_GAP);
maxX = Scale(AlignToNextGrid(CONTAINER_X) + CONTAINER_BUTTON_GAP + CONTAINER_BUTTON_WIDTH);
break;
case ButtonColumnMiddle:
minX = Scale(AlignToNextGrid(CONTAINER_X + CONTAINER_WIDTH / 2.0f) - CONTAINER_BUTTON_WIDTH / 2.0f);
maxX = Scale(AlignToNextGrid(CONTAINER_X + CONTAINER_WIDTH / 2.0f) + CONTAINER_BUTTON_WIDTH / 2.0f);
break;
case ButtonColumnRight:
minX = Scale(AlignToNextGrid(CONTAINER_X + CONTAINER_WIDTH) - CONTAINER_BUTTON_GAP - CONTAINER_BUTTON_WIDTH);
maxX = Scale(AlignToNextGrid(CONTAINER_X + CONTAINER_WIDTH) - CONTAINER_BUTTON_GAP);
break;
}
}
static void DrawSourceButton(ButtonColumn buttonColumn, float yRatio, const char *sourceText, bool sourceSet)
{ {
bool buttonPressed; bool buttonPressed;
float minX, maxX; float minX, maxX;
switch (sourceColumn) ComputeButtonColumnCoordinates(buttonColumn, minX, maxX);
{
case SourceColumnLeft:
minX = Scale(AlignToNextGrid(CONTAINER_X) + SOURCE_BUTTON_GAP);
maxX = Scale(AlignToNextGrid(CONTAINER_X) + SOURCE_BUTTON_GAP + SOURCE_BUTTON_WIDTH);
break;
case SourceColumnMiddle:
minX = Scale(AlignToNextGrid(CONTAINER_X + CONTAINER_WIDTH / 2.0f) - SOURCE_BUTTON_WIDTH / 2.0f);
maxX = Scale(AlignToNextGrid(CONTAINER_X + CONTAINER_WIDTH / 2.0f) + SOURCE_BUTTON_WIDTH / 2.0f);
break;
case SourceColumnRight:
minX = Scale(AlignToNextGrid(CONTAINER_X + CONTAINER_WIDTH) - SOURCE_BUTTON_GAP - SOURCE_BUTTON_WIDTH);
maxX = Scale(AlignToNextGrid(CONTAINER_X + CONTAINER_WIDTH) - SOURCE_BUTTON_GAP);
break;
}
float minusY = (SOURCE_BUTTON_GAP + BUTTON_HEIGHT) * yRatio; float minusY = (CONTAINER_BUTTON_GAP + BUTTON_HEIGHT) * yRatio;
ImVec2 min = { minX, Scale(AlignToNextGrid(CONTAINER_Y + CONTAINER_HEIGHT) - SOURCE_BUTTON_GAP - BUTTON_HEIGHT - minusY) }; ImVec2 min = { minX, Scale(AlignToNextGrid(CONTAINER_Y + CONTAINER_HEIGHT) - CONTAINER_BUTTON_GAP - BUTTON_HEIGHT - minusY) };
ImVec2 max = { maxX, Scale(AlignToNextGrid(CONTAINER_Y + CONTAINER_HEIGHT) - SOURCE_BUTTON_GAP - minusY) }; ImVec2 max = { maxX, Scale(AlignToNextGrid(CONTAINER_Y + CONTAINER_HEIGHT) - CONTAINER_BUTTON_GAP - minusY) };
DrawButton(min, max, sourceText, true, sourceSet, buttonPressed); DrawButton(min, max, sourceText, true, sourceSet, buttonPressed);
} }
@ -634,6 +659,32 @@ static void ParseSourcePaths(std::list<std::filesystem::path> &paths)
} }
} }
static void DrawLanguagePicker()
{
bool buttonPressed = false;
if (g_currentPage == WizardPage::SelectLanguage)
{
bool buttonPressed;
float minX, maxX;
for (int i = 0; i < 6; i++)
{
ComputeButtonColumnCoordinates((i < 3) ? ButtonColumnLeft : ButtonColumnRight, minX, maxX);
float minusY = (CONTAINER_BUTTON_GAP + BUTTON_HEIGHT) * (float(i % 3));
ImVec2 min = { minX, Scale(AlignToNextGrid(CONTAINER_Y + CONTAINER_HEIGHT) - CONTAINER_BUTTON_GAP - BUTTON_HEIGHT - minusY) };
ImVec2 max = { maxX, Scale(AlignToNextGrid(CONTAINER_Y + CONTAINER_HEIGHT) - CONTAINER_BUTTON_GAP - minusY) };
// TODO: The active button should change its style to show an enabled toggle if it matches the current language.
DrawButton(min, max, LANGUAGE_TEXT[i], false, true, buttonPressed);
if (buttonPressed)
{
Config::Language = LANGUAGE_ENUM[i];
}
}
}
}
static void DrawSourcePickers() static void DrawSourcePickers()
{ {
bool buttonPressed = false; bool buttonPressed = false;
@ -668,15 +719,15 @@ static void DrawSources()
{ {
if (g_currentPage == WizardPage::SelectGameAndUpdate) if (g_currentPage == WizardPage::SelectGameAndUpdate)
{ {
DrawSourceButton(SourceColumnMiddle, 1.5f, GAME_SOURCE_TEXT, !g_gameSourcePath.empty()); DrawSourceButton(ButtonColumnMiddle, 1.5f, GAME_SOURCE_TEXT, !g_gameSourcePath.empty());
DrawSourceButton(SourceColumnMiddle, 0.5f, UPDATE_SOURCE_TEXT, !g_updateSourcePath.empty()); DrawSourceButton(ButtonColumnMiddle, 0.5f, UPDATE_SOURCE_TEXT, !g_updateSourcePath.empty());
} }
if (g_currentPage == WizardPage::SelectDLC) if (g_currentPage == WizardPage::SelectDLC)
{ {
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
DrawSourceButton((i < 3) ? SourceColumnLeft : SourceColumnRight, float(i % 3), DLC_SOURCE_TEXT[i], !g_dlcSourcePaths[i].empty() || g_dlcInstalled[i]); DrawSourceButton((i < 3) ? ButtonColumnLeft : ButtonColumnRight, float(i % 3), DLC_SOURCE_TEXT[i], !g_dlcSourcePaths[i].empty() || g_dlcInstalled[i]);
} }
} }
} }
@ -759,15 +810,15 @@ static void DrawNextButton()
skipButton = std::all_of(g_dlcSourcePaths.begin(), g_dlcSourcePaths.end(), [](const std::filesystem::path &path) { return path.empty(); }); skipButton = std::all_of(g_dlcSourcePaths.begin(), g_dlcSourcePaths.end(), [](const std::filesystem::path &path) { return path.empty(); });
} }
const char *buttonText = skipButton ? SKIP_BUTTON_TEXT : NEXT_BUTTON_TEXT; const std::string &buttonText = Localise(skipButton ? "Installer_Button_Skip" : "Installer_Button_Next");
ImVec2 textSize = g_newRodinFont->CalcTextSizeA(20.0f, FLT_MAX, 0.0f, buttonText); ImVec2 textSize = g_newRodinFont->CalcTextSizeA(20.0f, FLT_MAX, 0.0f, buttonText.c_str());
textSize.x += BUTTON_TEXT_GAP; textSize.x += BUTTON_TEXT_GAP;
ImVec2 min = { Scale(AlignToNextGrid(CONTAINER_X + CONTAINER_WIDTH) - textSize.x - BOTTOM_X_GAP), Scale(AlignToNextGrid(CONTAINER_Y + CONTAINER_HEIGHT) + BOTTOM_Y_GAP) }; ImVec2 min = { Scale(AlignToNextGrid(CONTAINER_X + CONTAINER_WIDTH) - textSize.x - BOTTOM_X_GAP), Scale(AlignToNextGrid(CONTAINER_Y + CONTAINER_HEIGHT) + BOTTOM_Y_GAP) };
ImVec2 max = { Scale(AlignToNextGrid(CONTAINER_X + CONTAINER_WIDTH) - BOTTOM_X_GAP), Scale(AlignToNextGrid(CONTAINER_Y + CONTAINER_HEIGHT) + BOTTOM_Y_GAP + BUTTON_HEIGHT) }; ImVec2 max = { Scale(AlignToNextGrid(CONTAINER_X + CONTAINER_WIDTH) - BOTTOM_X_GAP), Scale(AlignToNextGrid(CONTAINER_Y + CONTAINER_HEIGHT) + BOTTOM_Y_GAP + BUTTON_HEIGHT) };
bool buttonPressed = false; bool buttonPressed = false;
DrawButton(min, max, buttonText, false, nextButtonEnabled, buttonPressed); DrawButton(min, max, buttonText.c_str(), false, nextButtonEnabled, buttonPressed);
if (buttonPressed) if (buttonPressed)
{ {
@ -951,6 +1002,7 @@ void InstallerWizard::Draw()
DrawLeftImage(); DrawLeftImage();
DrawScanlineBars(); DrawScanlineBars();
DrawDescriptionContainer(); DrawDescriptionContainer();
DrawLanguagePicker();
DrawSourcePickers(); DrawSourcePickers();
DrawSources(); DrawSources();
DrawInstallingProgress(); DrawInstallingProgress();
@ -970,15 +1022,29 @@ void InstallerWizard::Draw()
void InstallerWizard::Shutdown() void InstallerWizard::Shutdown()
{ {
// Wait for and erase the thread.
if (g_installerThread != nullptr) if (g_installerThread != nullptr)
{ {
g_installerThread->join(); g_installerThread->join();
g_installerThread.reset(); g_installerThread.reset();
} }
// Erase the sources.
g_installerSources.game.reset(); g_installerSources.game.reset();
g_installerSources.update.reset(); g_installerSources.update.reset();
g_installerSources.dlc.clear(); g_installerSources.dlc.clear();
// Make sure the GPU is not currently active before deleting these textures.
Video::WaitForGPU();
// Erase the textures.
g_milesElectricIcon.reset();
g_arrowCircle.reset();
for (auto &texture : g_installTextures)
{
texture.reset();
}
} }
bool InstallerWizard::Run(bool skipGame) bool InstallerWizard::Run(bool skipGame)
@ -1010,5 +1076,7 @@ bool InstallerWizard::Run(bool skipGame)
Window::SetCursorAllowed(false); Window::SetCursorAllowed(false);
NFD_Quit(); NFD_Quit();
InstallerWizard::Shutdown();
return true; return true;
} }