void RoR::GUI::MultiplayerSelector::MultiplayerSelector::Draw()
{
    const float TABS_BOTTOM_PADDING = 4.f; // They're actually buttons in role of tabs.
    const float CONTENT_TOP_PADDING = 4.f; // Extra space under top horizontal separator bar.
    const float BUTTONS_EXTRA_SPACE = 6.f;
    const float TABLE_PADDING_LEFT = 4.f;

    int window_flags = ImGuiWindowFlags_NoCollapse;
    ImGui::SetNextWindowSize(ImVec2(750.f, 400.f), ImGuiSetCond_FirstUseEver);
    if (!ImGui::Begin(m_window_title, &m_is_visible, window_flags))
    {
        return;
    }

    if (!m_is_visible) // If the window was closed...
    {
        App::GetGuiManager()->SetVisible_GameMainMenu(true);
    }

    // Window mode buttons
    MultiplayerSelector::Mode next_mode = m_mode;

    if (ImGui::Button("Online (click to refresh)"))
    {
        if (m_mode == Mode::ONLINE)
            this->RefreshServerlist();
        else
            next_mode = Mode::ONLINE;
    }
    ImGui::SameLine();
    if (ImGui::Button("Direct IP"))
    {
        next_mode = Mode::DIRECT;
    }
    ImGui::SameLine();
    if (ImGui::Button("Setup"))
    {
        next_mode = Mode::SETUP;
    }

    ImGui::SetCursorPosY(ImGui::GetCursorPosY() + TABS_BOTTOM_PADDING);
    ImGui::Separator();
    ImGui::SetCursorPosY(ImGui::GetCursorPosY() + CONTENT_TOP_PADDING);

    m_mode = next_mode;

    if (m_mode == Mode::SETUP)
    {
        ImGui::PushID("setup");

        DrawGCheckbox(App::mp_join_on_startup,    "Auto connect");
        DrawGCheckbox(App::mp_chat_auto_hide,     "Auto hide chat");
        DrawGCheckbox(App::mp_hide_net_labels,    "Hide net labels");
        DrawGCheckbox(App::mp_hide_own_net_label, "Hide own net label");

        ImGui::SetCursorPosY(ImGui::GetCursorPosY() + BUTTONS_EXTRA_SPACE);
        ImGui::Separator();

        ImGui::SetCursorPosY(ImGui::GetCursorPosY() + CONTENT_TOP_PADDING);
        ImGui::PushItemWidth(250.f);
        DrawGTextEdit(App::mp_player_name,        "Player nickname", m_player_name_buf);
        DrawGTextEdit(App::mp_server_password,    "Default server password", m_password_buf);
        ImGui::PopItemWidth();

        ImGui::SetCursorPosY(ImGui::GetCursorPosY() + BUTTONS_EXTRA_SPACE);
        ImGui::Separator();

        ImGui::SetCursorPosY(ImGui::GetCursorPosY() + CONTENT_TOP_PADDING);
        ImGui::PushItemWidth(250.f);
        ImGui::InputText("Player token", m_user_token_buf.GetBuffer(), m_user_token_buf.GetCapacity());
        ImGui::PopItemWidth();

        ImGui::SetCursorPosY(ImGui::GetCursorPosY() + BUTTONS_EXTRA_SPACE);
        if (ImGui::Button("Update saved hash") && (m_user_token_buf.GetLength() > 0))
        {
            RoR::CSHA1 sha1;
            uint8_t* input_text = (uint8_t *)m_user_token_buf.GetBuffer();
            uint32_t input_size = (uint32_t)m_user_token_buf.GetLength();
            sha1.UpdateHash(input_text, input_size);
            sha1.Final();
            Str<250> hash;
            sha1.ReportHash(hash.GetBuffer(), RoR::CSHA1::REPORT_HEX_SHORT);
            App::mp_player_token_hash.SetActive(hash.ToCStr());
            m_user_token_buf.Clear();
        }
        ImGui::SameLine();
        ImGui::TextDisabled(" Hash: [%s]", App::mp_player_token_hash.GetActive());

        ImGui::PopID();
    }
    else if (m_mode == Mode::DIRECT)
    {
        ImGui::PushID("direct");

        ImGui::PushItemWidth(250.f);
        DrawGTextEdit(App::mp_server_host, "Server host", m_server_host_buf);
        DrawGIntBox(App::mp_server_port, "Server port");
        DrawGTextEdit(App::mp_server_password, "Server password (default)", m_password_buf);
        ImGui::PopItemWidth();

        ImGui::SetCursorPosY(ImGui::GetCursorPosY() + BUTTONS_EXTRA_SPACE);
        if (ImGui::Button("Join"))
        {
            App::mp_state.SetPending (MpState::CONNECTED);
        }

        ImGui::PopID();
    }
    else if (m_mode == Mode::ONLINE)
    {
        const char* draw_label_text = nullptr;
        ImVec4      draw_label_color;
        bool        draw_table = false;

        // DETERMINE WHAT TO DRAW
        if (m_is_refreshing)
        {
            draw_label_text = "... refreshing ...";
            draw_label_color = App::GetGuiManager()->GetTheme().in_progress_text_color;
        }
        else if (m_serverlist_data != nullptr)
        {
            if (m_serverlist_data->success == true)
            {
                draw_table = true;
                if (m_serverlist_data->servers.size() == 0)
                {
                    draw_label_text = "There are no available servers :/"; // Draw empty table _and_ the label.
                    draw_label_color = App::GetGuiManager()->GetTheme().no_entries_text_color;
                }
            }
            else
            {
                draw_label_text = m_serverlist_data->message.c_str();
                draw_label_color = App::GetGuiManager()->GetTheme().error_text_color;
            }
        }

        // DRAW SERVERLIST TABLE
        if (draw_table)
        {
            // Setup serverlist table ... the scroll area
            const float table_height = ImGui::GetWindowHeight()
                - ((2.f * ImGui::GetStyle().WindowPadding.y) + (3.f * ImGui::GetItemsLineHeightWithSpacing())
                    + TABS_BOTTOM_PADDING + CONTENT_TOP_PADDING - ImGui::GetStyle().ItemSpacing.y);
            ImGui::BeginChild("scrolling", ImVec2(0.f, table_height), false);
            // ... and the table itself
            const float table_width = ImGui::GetWindowContentRegionWidth();
            ImGui::Columns(6, "mp-selector-columns");         // Col #0: Passwd
            ImGui::SetColumnOffset(1, 0.08f * table_width);   // Col #1: Server name
            ImGui::SetColumnOffset(2, 0.35f * table_width);   // Col #2: Terrain name
            ImGui::SetColumnOffset(3, 0.70f * table_width);   // Col #3: Users/Max
            ImGui::SetColumnOffset(4, 0.77f * table_width);   // Col #4: Ping
            ImGui::SetColumnOffset(5, 0.82f * table_width);   // Col #5: Host/Port
            // Draw table header
            ImGui::SetCursorPosX(ImGui::GetCursorPosX() + TABLE_PADDING_LEFT);
            DrawTableHeader("Passwd?");
            DrawTableHeader("Name");
            DrawTableHeader("Terrain");
            DrawTableHeader("Users");
            DrawTableHeader("Ping");
            DrawTableHeader("Host/Port");
            ImGui::Separator();
            // Draw table body
            int num_servers = static_cast<int>(m_serverlist_data->servers.size());
            for (int i = 0; i < num_servers; i++)
            {
                ImGui::PushID(i);

                // First column - selection control
                ImGui::SetCursorPosX(ImGui::GetCursorPosX() + TABLE_PADDING_LEFT);
                MpServerlistData::ServerInfo& server = m_serverlist_data->servers[i];
                if (ImGui::Selectable(server.display_passwd, m_selected_item == i, ImGuiSelectableFlags_SpanAllColumns))
                {
                    m_selected_item = i;
                }
                ImGui::NextColumn();

                // Other collumns
                ImGui::Text("%s", server.display_name.ToCStr());   ImGui::NextColumn();
                ImGui::Text("%s", server.display_terrn.ToCStr());  ImGui::NextColumn();
                ImGui::Text("%s", server.display_users.ToCStr());  ImGui::NextColumn();
                ImGui::Text("~");                                  ImGui::NextColumn(); // TODO: ping
                ImGui::Text("%s", server.display_host.ToCStr());   ImGui::NextColumn();

                ImGui::PopID();
            }
            ImGui::Columns(1);
            ImGui::EndChild(); // End of scroll area

            // Simple join button
            if (m_selected_item != -1)
            {
                if (ImGui::Button("Join", ImVec2(200.f, 0.f)))
                {
                    App::mp_server_host.SetActive(m_serverlist_data->servers[m_selected_item].net_host);
                    App::mp_server_port.SetActive(m_serverlist_data->servers[m_selected_item].net_port);
                    App::mp_state.SetPending(MpState::CONNECTED);
                }
            }
        }

        // DRAW CENTERED LABEL
        if (draw_label_text != nullptr)
        {
            const ImVec2 label_size = ImGui::CalcTextSize(draw_label_text);
            ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.f) - (label_size.x / 2.f));
            ImGui::SetCursorPosY((ImGui::GetWindowSize().y / 2.f) - (label_size.y / 2.f));
            ImGui::TextColored(draw_label_color, "%s", draw_label_text);
        }
    }

    ImGui::End();
}
void RoR::GUI::GameSettings::Draw()
{
    bool is_visible = true;
    const int flags = ImGuiWindowFlags_NoCollapse;
    ImGui::SetNextWindowSize(ImVec2(600.f, 400.f), ImGuiSetCond_FirstUseEver);
    ImGui::SetNextWindowPosCenter(ImGuiSetCond_Appearing);
    ImGui::Begin(_LC("GameSettings", "Game settings"), &is_visible, flags);
    if (! is_visible)
    {
        this->SetVisible(false);
        if (App::app_state.GetActive() == RoR::AppState::MAIN_MENU)
        {
            App::GetGuiManager()->SetVisible_GameMainMenu(true);
        }
        ImGui::End();
        return;
    }

    // 'Tabs' buttons
    ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.f, 8.f));

    if (ImGui::Button(_LC("GameSettings", "Render System"))) { m_tab = SettingsTab::RENDER_SYSTEM; }
    ImGui::SameLine();
    if (ImGui::Button(_LC("GameSettings", "General")))       { m_tab = SettingsTab::GENERAL;       }
    ImGui::SameLine();
    if (ImGui::Button(_LC("GameSettings", "Graphics")))      { m_tab = SettingsTab::GRAPHICS;      }
    ImGui::SameLine();
#ifdef USE_OPENAL
    if (ImGui::Button(_LC("GameSettings", "Audio")))         { m_tab = SettingsTab::AUDIO;         }
    ImGui::SameLine();
#endif // USE_OPENAL
    if (ImGui::Button(_LC("GameSettings", "Controls")))      { m_tab = SettingsTab::CONTROL;       }
    ImGui::SameLine();
    if (ImGui::Button(_LC("GameSettings", "Diagnostic")))    { m_tab = SettingsTab::DIAG;          }
    ImGui::SameLine();
    if (ImGui::Button(_LC("GameSettings", "Update cache")))
    {
        App::app_force_cache_udpate.SetActive(true);
    }

    ImGui::PopStyleVar(1);

    ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 4.f);
    ImGui::Separator();

    if (m_tab == SettingsTab::RENDER_SYSTEM)
    {
        ImGui::TextDisabled(_LC("GameSettings", "Render system (changes require a restart)"));

        const auto ogre_root = App::GetOgreSubsystem()->GetOgreRoot();
        const auto render_systems = ogre_root->getAvailableRenderers();
        std::string render_system_names;
        for (auto rs : render_systems)
        {
            render_system_names += rs->getName() + '\0';
        }
        const auto ro = ogre_root->getRenderSystemByName(App::app_rendersys_override.GetActive());
        const auto rs = ro ? ro : ogre_root->getRenderSystem();
        const auto it = std::find(render_systems.begin(), render_systems.end(), rs);
        int render_id = it != render_systems.end() ? std::distance(render_systems.begin(), it) : 0;
        /* Combobox for selecting the Render System*/
        if (ImGui::Combo(_LC ("GameSettings", "Render System"), &render_id, render_system_names.c_str()))
        {
            App::app_rendersys_override.SetActive(render_systems[render_id]->getName().c_str());
        }

        const auto config_options = ogre_root->getRenderSystem()->getConfigOptions();
        std::set<std::string> filter = {"Allow NVPerfHUD", "Colour Depth", "Fixed Pipeline Enabled",
            "Floating-point mode", "Resource Creation Policy", "VSync Interval", "sRGB Gamma Conversion"};
        for (auto opt : config_options)
        {
            auto co = opt.second;
            if (co.immutable)
                continue;
            if (co.possibleValues.empty())
                continue;
            if (filter.find(co.name) != filter.end())
                continue;
            std::sort(co.possibleValues.rbegin(), co.possibleValues.rend());
            std::string option_values;
            for (auto value : co.possibleValues)
            {
                option_values += value + '\0';
            }
            const auto it = std::find(co.possibleValues.begin(), co.possibleValues.end(), opt.second.currentValue);
            int option_id = it != co.possibleValues.end() ? std::distance(co.possibleValues.begin(), it) : 0;
            if (ImGui::Combo(co.name.c_str(), &option_id, option_values.c_str()))
            {
                rs->setConfigOption(co.name, co.possibleValues[option_id]);
                if (rs->validateConfigOptions().empty())
                {
                    ogre_root->saveConfig();
                }
            }
        }
    }
    else if (m_tab == SettingsTab::GENERAL)
    {
        ImGui::TextDisabled(_LC("GameSettings", "Application settings"));

#ifndef NOLANG
        std::vector<std::pair<std::string, std::string>> languages = LanguageEngine::getSingleton().getLanguages();
        std::string lang_values;
        for (auto value : languages)
        {
            lang_values += value.first + '\0';
        }
        const auto it = std::find_if(languages.begin(), languages.end(),
                [](const std::pair<std::string, std::string>& l) { return l.second == App::app_language.GetActive(); });
        int lang_selection = it != languages.end() ? std::distance(languages.begin(), it) : 0;
        if (ImGui::Combo(_LC("GameSettings", "Language"), &lang_selection, lang_values.c_str()))
        {
            App::app_language.SetActive(languages[lang_selection].second.c_str());
            LanguageEngine::getSingleton().setup();
        }
#endif

        // Country selection
        static Ogre::FileInfoListPtr fl = Ogre::ResourceGroupManager::getSingleton().findResourceFileInfo("FlagsRG", "*");
        if (!fl->empty())
        {
            static std::vector<std::string> countries;
            if (countries.empty())
            {
                for (auto& file : *fl)
                {
                    std::string country = Ogre::StringUtil::replaceAll(file.filename, ".png", "");
                    if (country.size() == 2) // RoR protocol limitation
                    {
                        countries.push_back(country);
                    }
                }
                std::sort(countries.begin(), countries.end());
            }
            std::string country_values;
            for (auto value : countries)
            {
                country_values += value + '\0';
            }
            const auto it = std::find(countries.begin(), countries.end(), std::string(App::app_country.GetActive()));
            int country_selection = it != countries.end() ? std::distance(countries.begin(), it) : 0;
            if (ImGui::Combo(_LC("GameSettings", "Country"), &country_selection, country_values.c_str()))
            {
                App::app_country.SetActive(countries[country_selection].c_str());
            }
        }

        int sshot_select = (std::strcmp(App::app_screenshot_format.GetActive(),"jpg") == 0) ? 1 : 0; // Hardcoded; TODO: list available formats.

        /* Screenshot format: Can be png or jpg*/
        if (ImGui::Combo(_LC("GameSettings", "Screenshot format"), &sshot_select, "png\0jpg\0\0"))
        {
            App::app_screenshot_format.SetActive((sshot_select == 1) ? "jpg" : "png");
        }

        DrawGTextEdit(App::app_extra_mod_path, _LC("GameSettings", "Extra mod path"),  m_buf_app_extra_mod_dir);

        DrawGCheckbox(App::app_skip_main_menu, _LC("GameSettings", "Skip main menu"));
        DrawGCheckbox(App::app_async_physics, _LC("GameSettings", "Async physics"));
        DrawGCheckbox(App::app_disable_online_api, _LC("GameSettings", "Disable online api"));

        ImGui::Separator();
        ImGui::TextDisabled(_LC("GameSettings", "Simulation settings"));

        DrawGCombo(App::sim_gearbox_mode, _LC("GameSettings", "Gearbox mode"),
            "Automatic shift\0"
            "Manual shift - Auto clutch\0"
            "Fully Manual: sequential shift\0"
            "Fully manual: stick shift\0"
            "Fully Manual: stick shift with ranges\00");

        DrawGCheckbox(App::gfx_speedo_digital, _LC("GameSettings", "Digital speedometer"));
        DrawGCheckbox(App::gfx_speedo_imperial, _LC("GameSettings", "Imperial speedometer"));

        //DrawGCheckbox(App::gfx_flexbody_lods,      "Enable flexbody LODs");
        //DrawGCheckbox(App::gfx_flexbody_cache,     "Enable flexbody cache");

        DrawGCheckbox(App::sim_spawn_running, _LC("GameSettings", "Engines spawn running"));

        DrawGCheckbox(App::sim_replay_enabled, _LC("GameSettings", "Replay mode"));
        if (App::sim_replay_enabled.GetActive())
        {
            DrawGIntBox(App::sim_replay_length, _LC("GameSettings", "Replay length"));
            DrawGIntBox(App::sim_replay_stepping, _LC("GameSettings", "Replay stepping"));
        }

        DrawGCheckbox(App::sim_realistic_commands, _LC("GameSettings", "Realistic forward commands"));

        DrawGCheckbox(App::sim_races_enabled, _LC("GameSettings", "Enable races"));
        DrawGCheckbox(App::sim_direction_arrow, _LC("GameSettings", "Direction arrow"));

        DrawGCheckbox(App::sim_no_self_collisions, _LC("GameSettings", "No intra truck collisions"));
        DrawGCheckbox(App::sim_no_collisions, _LC("GameSettings", "No inter truck collisions"));
    }
#ifdef USE_OPENAL
    else if (m_tab == SettingsTab::AUDIO)
    {
        ImGui::TextDisabled(_LC("GameSettings", "Audio settings"));

        static const ALCchar *devices = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
        const ALCchar *device = devices, *next = devices + 1;
        std::vector<std::string> audio_devices;

        while (device && *device != '\0' && next && *next != '\0')
        {
                audio_devices.push_back(device);
                size_t len = strlen(device);
                device += (len + 1);
                next += (len + 2);
        }

        const auto it = std::find(audio_devices.begin(), audio_devices.end(), App::audio_device_name.GetActive());
        int device_id = it != audio_devices.end() ? std::distance(audio_devices.begin(), it) : 0;
        if (ImGui::Combo(_LC("GameSettings", "Audio device"), &device_id, devices))
        {
            App::audio_device_name.SetActive(audio_devices[device_id].c_str());
        }

        DrawGCheckbox(App::audio_enable_creak,     _LC("GameSettings", "Creak sound"));
        DrawGCheckbox(App::audio_menu_music,       _LC("GameSettings", "Main menu music"));
        DrawGFloatSlider(App::audio_master_volume, _LC("GameSettings", "Master volume"), 0, 1);
    }
#endif // USE_OPENAL
    else if (m_tab == SettingsTab::GRAPHICS)
    {
        ImGui::TextDisabled(_LC("GameSettings", "Video settings"));

        DrawGCombo(App::gfx_flares_mode, _LC("GameSettings", "Lights"),
            "None (fastest)\0"
            "No light sources\0"
            "Only current vehicle, main lights\0"
            "All vehicles, main lights\0"
            "All vehicles, all lights\0\0");

        DrawGCombo(App::gfx_shadow_type, _LC("GameSettings", "Shadow type"),
            "Disabled\0"
            "Texture\0"
            "PSSM\0\0");

        if (App::gfx_shadow_type.GetActive() != GfxShadowType::NONE)
        {
            DrawGCheckbox(App::gfx_reduce_shadows, _LC("GameSettings", "Shadow optimizations"));
            if (App::gfx_shadow_type.GetActive() == GfxShadowType::PSSM)
            {
                DrawGIntSlider(App::gfx_shadow_quality, _LC("GameSettings", "Shadow quality"), 0, 3);
            }
        }

        DrawGCombo(App::gfx_sky_mode, "Sky gfx",
            "Sandstorm (fastest)\0"
            "Caelum (best looking, slower)\0"
            "SkyX (best looking, slower)\0\0");

        DrawGIntSlider(App::gfx_sight_range, _LC("GameSettings", "Sight range (meters)"), 100, 5000);

        DrawGCombo(App::gfx_texture_filter , _LC("GameSettings", "Texture filtering"),
            "None\0"
            "Bilinear\0"
            "Trilinear\0"
            "Anisotropic\0\0");

        if (App::gfx_texture_filter.GetActive() == GfxTexFilter::ANISOTROPIC)
        {
            int anisotropy = Ogre::Math::Clamp(App::gfx_anisotropy.GetActive(), 1, 16);
            int  selection = std::log2(anisotropy);
            if (ImGui::Combo(_LC("GameSettings", "Anisotropy"), &selection, "1\0""2\0""4\0""8\0""16\0\0"))
            {
                App::gfx_anisotropy.SetActive(std::pow(2, selection));
            }
        }

        DrawGCombo(App::gfx_vegetation_mode, _LC("GameSettings", "Vegetation density"),
            "None\0"
            "20%\0"
            "50%\0"
            "Full\0\0");

        DrawGCombo(App::gfx_water_mode, _LC("GameSettings", "Water gfx"),
            "None\0"
            "Basic (fastest)\0"
            "Reflection\0"
            "Reflection + refraction (speed optimized)\0"
            "Reflection + refraction (quality optimized)\0"
            "HydraX\0\0");

        DrawGIntSlider(App::gfx_fps_limit,       _LC("GameSettings", "FPS limit"), 0, 240);

        DrawGIntCheck(App::gfx_particles_mode,   _LC("GameSettings", "Enable particle gfx"));
        DrawGIntCheck(App::gfx_skidmarks_mode,   _LC("GameSettings", "Enable skidmarks"));

        DrawGCheckbox(App::gfx_envmap_enabled,   _LC("GameSettings", "Realtime reflections"));
        if (App::gfx_envmap_enabled.GetActive())
        {
            ImGui::PushItemWidth(125.f); // Width includes [+/-] buttons
            DrawGIntSlider(App::gfx_envmap_rate, _LC("GameSettings", "Realtime refl. update rate"), 0, 6);
            ImGui::PopItemWidth();
        }

        DrawGCheckbox(App::gfx_enable_videocams, _LC("GameSettings", "Render video cameras"));
        DrawGCheckbox(App::gfx_surveymap_icons,  _LC("GameSettings", "Overview map icons"));
        if (App::gfx_surveymap_icons.GetActive())
        {
            DrawGCheckbox(App::gfx_declutter_map,  _LC("GameSettings", "Declutter overview map"));
        }
        DrawGCheckbox(App::gfx_water_waves,      _LC("GameSettings", "Waves on water"));

        DrawGCombo(App::gfx_extcam_mode, "Exterior camera mode",
            "None\0"
            "Static\0"
            "Pitching\0\0");

        DrawGIntSlider(App::gfx_camera_height, _LC("GameSettings", "Static camera height (meters)"), 1, 50);
        DrawGIntSlider(App::gfx_fov_external, _LC("GameSettings", "Exterior field of view"), 10, 120);
        DrawGIntSlider(App::gfx_fov_internal, _LC("GameSettings", "Interior field of view"), 10, 120);
    }
    else if (m_tab == SettingsTab::DIAG)
    {
        ImGui::TextDisabled(_LC("GameSettings", "Diagnostic options"));

        int physics_fps = std::round(1.0f / App::diag_physics_dt.GetActive());
        if (ImGui::SliderInt(_LC("GameSettings", "Physics frames per second"), &physics_fps, 2000, 10000))
        {
            App::diag_physics_dt.SetActive(Ogre::Math::Clamp(1.0f / physics_fps, 0.0001f, 0.0005f));
        }
        DrawGTextEdit(App::diag_preset_terrain,      _LC("GameSettings", "Preselected terrain"),         m_buf_diag_preset_terrain, false);
        DrawGTextEdit(App::diag_preset_vehicle,      _LC("GameSettings", "Preselected vehicle"),         m_buf_diag_preset_vehicle, true);
        DrawGTextEdit(App::diag_preset_veh_config,   _LC("GameSettings", "Presel. veh. config"),         m_buf_diag_preset_veh_config);
        DrawGCheckbox(App::diag_preset_veh_enter,    _LC("GameSettings", "Enter preselected vehicle"));
        DrawGCheckbox(App::diag_auto_spawner_report, _LC("GameSettings", "Auto actor spawner report"));
        DrawGCheckbox(App::diag_rig_log_node_import, _LC("GameSettings", "Log node import (spawn)"));
        DrawGCheckbox(App::diag_rig_log_node_stats,  _LC("GameSettings", "Log node stats (spawn)"));
        DrawGCheckbox(App::diag_rig_log_messages,    _LC("GameSettings", "Log messages (spawn)"));
        DrawGCheckbox(App::diag_camera,              _LC("GameSettings", "Debug camera (rails)"));
        DrawGCheckbox(App::diag_collisions,          _LC("GameSettings", "Debug collisions"));
        DrawGCheckbox(App::diag_truck_mass,          _LC("GameSettings", "Debug actor mass"));
        DrawGCheckbox(App::diag_envmap,              _LC("GameSettings", "Debug realtime reflections"));
        DrawGCheckbox(App::diag_videocameras,        _LC("GameSettings", "Debug videocameras"));
        DrawGCheckbox(App::diag_warning_texture,     _LC("GameSettings", "Debug textures"));
        DrawGCheckbox(App::diag_hide_broken_beams,   _LC("GameSettings", "Hide broken beams"));
        DrawGCheckbox(App::diag_hide_wheel_info,     _LC("GameSettings", "Hide wheel info"));
        DrawGCheckbox(App::diag_hide_wheels,         _LC("GameSettings", "Hide wheels"));
        DrawGCheckbox(App::diag_hide_nodes,          _LC("GameSettings", "Hide nodes"));
        DrawGCheckbox(App::diag_log_console_echo,    _LC("GameSettings", "Echo log to console"));
        DrawGCheckbox(App::diag_log_beam_break,      _LC("GameSettings", "Log beam breaking"));
        DrawGCheckbox(App::diag_log_beam_deform,     _LC("GameSettings", "Log beam deforming"));
        DrawGCheckbox(App::diag_log_beam_trigger,    _LC("GameSettings", "Log beam triggers"));
        if (ImGui::Button(_LC("GameSettings", "Rebuild cache")))
        {
            App::app_force_cache_purge.SetActive(true);
        }
    }
    else if (m_tab == SettingsTab::CONTROL)
    {
        ImGui::TextDisabled(_LC("GameSettings", "Controller options"));

        DrawGCombo(App::io_input_grab_mode, _LC("GameSettings", "Input grab mode"),
            "None\0"
            "All\0"
            "Dynamic\0\0");

        DrawGFloatSlider(App::io_analog_smoothing,   _LC("GameSettings", "Analog Input Smoothing"),   0.5f, 2.0f);
        DrawGFloatSlider(App::io_analog_sensitivity, _LC("GameSettings", "Analog Input Sensitivity"), 0.5f, 2.0f);

        DrawGCheckbox(App::io_arcade_controls, _LC("GameSettings", "Use arcade controls"));

        DrawGCheckbox(App::io_ffb_enabled, _LC("GameSettings", "Enable ForceFeedback"));
        if (App::io_ffb_enabled.GetActive())
        {
            ImGui::PushItemWidth(125.f);
            DrawGFloatBox(App::io_ffb_camera_gain, _LC("GameSettings", "FFB camera gain"));
            DrawGFloatBox(App::io_ffb_center_gain, _LC("GameSettings", "FFB center gain"));
            DrawGFloatBox(App::io_ffb_master_gain, _LC("GameSettings", "FFB master gain"));
            DrawGFloatBox(App::io_ffb_stress_gain, _LC("GameSettings", "FFB stress gain"));
            ImGui::PopItemWidth();
        }

        DrawGIntCheck(App::io_outgauge_mode, _LC("GameSettings", "Enable OutGauge protocol"));
        if (App::io_outgauge_mode.GetActive())
        {
            DrawGTextEdit(App::io_outgauge_ip, _LC("GameSettings", "OutGauge IP"), m_buf_io_outgauge_ip);
            ImGui::PushItemWidth(125.f);
            DrawGIntBox(App::io_outgauge_port,    _LC("GameSettings", "OutGauge port"));
            DrawGIntBox(App::io_outgauge_id,      _LC("GameSettings", "OutGauge ID"));
            DrawGFloatBox(App::io_outgauge_delay, _LC("GameSettings", "OutGauge delay"));
            ImGui::PopItemWidth();
        }
    }

    ImGui::End();
}