UI: Massive revamp, new features and improvements

This commit is contained in:
Connor McLaughlin
2022-07-11 23:03:29 +10:00
parent 3fb61865e5
commit b42b5501f6
425 changed files with 39701 additions and 29487 deletions
+99
View File
@@ -896,6 +896,105 @@ void GrowableMemoryByteStream::Grow(u32 MinimumGrowth)
ResizeMemory(NewSize);
}
bool ByteStream::ReadU8(u8* dest)
{
return Read2(dest, sizeof(u8));
}
bool ByteStream::ReadU16(u16* dest)
{
return Read2(dest, sizeof(u16));
}
bool ByteStream::ReadU32(u32* dest)
{
return Read2(dest, sizeof(u32));
}
bool ByteStream::ReadU64(u64* dest)
{
return Read2(dest, sizeof(u64));
}
bool ByteStream::ReadS8(s8* dest)
{
return Read2(dest, sizeof(s8));
}
bool ByteStream::ReadS16(s16* dest)
{
return Read2(dest, sizeof(s16));
}
bool ByteStream::ReadS32(s32* dest)
{
return Read2(dest, sizeof(s32));
}
bool ByteStream::ReadS64(s64* dest)
{
return Read2(dest, sizeof(s64));
}
bool ByteStream::ReadSizePrefixedString(std::string* dest)
{
u32 size;
if (!Read2(&size, sizeof(size)))
return false;
dest->resize(size);
if (!Read2(dest->data(), size))
return false;
return true;
}
bool ByteStream::WriteU8(u8 dest)
{
return Write2(&dest, sizeof(u8));
}
bool ByteStream::WriteU16(u16 dest)
{
return Write2(&dest, sizeof(u16));
}
bool ByteStream::WriteU32(u32 dest)
{
return Write2(&dest, sizeof(u32));
}
bool ByteStream::WriteU64(u64 dest)
{
return Write2(&dest, sizeof(u64));
}
bool ByteStream::WriteS8(s8 dest)
{
return Write2(&dest, sizeof(s8));
}
bool ByteStream::WriteS16(s16 dest)
{
return Write2(&dest, sizeof(s16));
}
bool ByteStream::WriteS32(s32 dest)
{
return Write2(&dest, sizeof(s32));
}
bool ByteStream::WriteS64(s64 dest)
{
return Write2(&dest, sizeof(s64));
}
bool ByteStream::WriteSizePrefixedString(const std::string_view& str)
{
const u32 size = static_cast<u32>(str.size());
return (Write2(&size, sizeof(size)) && (size == 0 || Write2(str.data(), size)));
}
std::unique_ptr<ByteStream> ByteStream::OpenFile(const char* fileName, u32 openMode)
{
if ((openMode & (BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE)) == BYTESTREAM_OPEN_WRITE)
+21
View File
@@ -2,6 +2,7 @@
#include "types.h"
#include <memory>
#include <string>
#include <string_view>
#include <vector>
// base byte stream creation functions
@@ -76,6 +77,26 @@ public:
inline void SetErrorState() { m_errorState = true; }
inline void ClearErrorState() { m_errorState = false; }
bool ReadU8(u8* dest);
bool ReadU16(u16* dest);
bool ReadU32(u32* dest);
bool ReadU64(u64* dest);
bool ReadS8(s8* dest);
bool ReadS16(s16* dest);
bool ReadS32(s32* dest);
bool ReadS64(s64* dest);
bool ReadSizePrefixedString(std::string* dest);
bool WriteU8(u8 dest);
bool WriteU16(u16 dest);
bool WriteU32(u32 dest);
bool WriteU64(u64 dest);
bool WriteS8(s8 dest);
bool WriteS16(s16 dest);
bool WriteS32(s32 dest);
bool WriteS64(s64 dest);
bool WriteSizePrefixedString(const std::string_view& str);
// base byte stream creation functions
// opens a local file-based stream. fills in error if passed, and returns false if the file cannot be opened.
static std::unique_ptr<ByteStream> OpenFile(const char* FileName, u32 OpenMode);
+1 -1
View File
@@ -167,7 +167,7 @@ std::string ShaderCache::GetCacheBaseFileName(const std::string_view& base_path,
bool debug)
{
std::string base_filename(base_path);
base_filename += "d3d_shaders_";
base_filename += FS_OSPATH_SEPARATOR_STR "d3d_shaders_";
switch (feature_level)
{
+1 -1
View File
@@ -226,7 +226,7 @@ std::string ShaderCache::GetCacheBaseFileName(const std::string_view& base_path,
D3D_FEATURE_LEVEL feature_level, bool debug)
{
std::string base_filename(base_path);
base_filename += "d3d12_";
base_filename += FS_OSPATH_SEPARATOR_STR "d3d12_";
base_filename += type;
base_filename += "_";
+3 -2
View File
@@ -2,6 +2,7 @@
#include "../file_system.h"
#include "../log.h"
#include "../md5_digest.h"
#include "../path.h"
#include "../string_util.h"
Log_SetChannel(GL::ShaderCache);
@@ -251,12 +252,12 @@ ShaderCache::CacheIndexKey ShaderCache::GetCacheKey(const std::string_view& vert
std::string ShaderCache::GetIndexFileName() const
{
return StringUtil::StdStringFromFormat("%sgl_programs.idx", m_base_path.c_str());
return Path::Combine(m_base_path, "gl_programs.idx");
}
std::string ShaderCache::GetBlobFileName() const
{
return StringUtil::StdStringFromFormat("%sgl_programs.bin", m_base_path.c_str());
return Path::Combine(m_base_path, "gl_programs.bin");
}
std::optional<Program> ShaderCache::GetProgram(const std::string_view vertex_shader,
+1 -1
View File
@@ -7,7 +7,7 @@ Log_SetChannel(HTTPDownloader);
static constexpr float DEFAULT_TIMEOUT_IN_SECONDS = 30;
static constexpr u32 DEFAULT_MAX_ACTIVE_REQUESTS = 4;
namespace FrontendCommon {
namespace Common {
const char HTTPDownloader::DEFAULT_USER_AGENT[] =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0";
+1 -1
View File
@@ -7,7 +7,7 @@
#include <string>
#include <vector>
namespace FrontendCommon {
namespace Common {
class HTTPDownloader
{
+1 -1
View File
@@ -9,7 +9,7 @@
#include <signal.h>
Log_SetChannel(HTTPDownloaderCurl);
namespace FrontendCommon {
namespace Common {
HTTPDownloaderCurl::HTTPDownloaderCurl() : HTTPDownloader() {}
+1 -1
View File
@@ -6,7 +6,7 @@
#include <mutex>
#include <curl/curl.h>
namespace FrontendCommon {
namespace Common {
class HTTPDownloaderCurl final : public HTTPDownloader
{
+1 -1
View File
@@ -13,7 +13,7 @@ Log_SetChannel(HTTPDownloaderWinHttp);
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Web::Http;
namespace FrontendCommon {
namespace Common {
HTTPDownloaderUWP::HTTPDownloaderUWP(std::string user_agent) : HTTPDownloader(), m_user_agent(std::move(user_agent)) {}
+1 -1
View File
@@ -6,7 +6,7 @@
#include <winrt/windows.Web.Http.h>
namespace FrontendCommon {
namespace Common {
class HTTPDownloaderUWP final : public HTTPDownloader
{
+1 -1
View File
@@ -9,7 +9,7 @@ Log_SetChannel(HTTPDownloaderWinHttp);
#pragma comment(lib, "winhttp.lib")
namespace FrontendCommon {
namespace Common {
HTTPDownloaderWinHttp::HTTPDownloaderWinHttp() : HTTPDownloader() {}
+1 -1
View File
@@ -5,7 +5,7 @@
#include <winhttp.h>
namespace FrontendCommon {
namespace Common {
class HTTPDownloaderWinHttp final : public HTTPDownloader
{
+2 -1
View File
@@ -1,4 +1,5 @@
#pragma once
#include "heterogeneous_containers.h"
#include <cstdint>
#include <map>
@@ -13,7 +14,7 @@ class LRUCache
CounterType last_access;
};
using MapType = std::map<K, Item>;
using MapType = std::conditional_t<std::is_same_v<K, std::string>, StringMap<Item>, std::map<K, Item>>;
public:
LRUCache(std::size_t max_capacity = 16, bool manual_evict = false)
+92 -20
View File
@@ -24,15 +24,18 @@
#include <sys/syscall.h>
#define gettid() syscall(SYS_gettid)
#endif
#else
#elif defined(__APPLE__)
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/mach_time.h>
#include <mach/semaphore.h>
#include <mach/task.h>
#include <pthread_np.h>
#endif
#endif
#ifdef _WIN32
// This hacky union would probably fail on some cpu platforms if the contents of FILETIME aren't
// packed (but for any x86 CPU and microsoft compiler, they will be).
union FileTimeSucks
union FileTimeU64Union
{
FILETIME filetime;
u64 u64time;
@@ -208,18 +211,16 @@ Threading::ThreadHandle& Threading::ThreadHandle::operator=(const ThreadHandle&
u64 Threading::ThreadHandle::GetCPUTime() const
{
#if defined(_WIN32)
#if 0
#if defined(_WIN32) && !defined(_UWP) && !defined(_M_ARM64)
u64 ret = 0;
if (m_native_handle)
QueryThreadCycleTime((HANDLE)m_native_handle, &ret);
return ret;
#else
FileTimeSucks user = {}, kernel = {};
#elif defined(_WIN32)
FileTimeU64Union user = {}, kernel = {};
FILETIME dummy;
GetThreadTimes((HANDLE)m_native_handle, &dummy, &dummy, &kernel.filetime, &user.filetime);
return user.u64time + kernel.u64time;
#endif
#elif defined(__APPLE__)
return getthreadtime(pthread_mach_thread_np((pthread_t)m_native_handle));
#elif defined(__linux__)
@@ -457,17 +458,15 @@ Threading::ThreadHandle& Threading::Thread::operator=(Thread&& thread)
u64 Threading::GetThreadCpuTime()
{
#if defined(_WIN32)
#if 0
#if defined(_WIN32) && !defined(_UWP) && !defined(_M_ARM64)
u64 ret = 0;
QueryThreadCycleTime(GetCurrentThread(), &ret);
return ret;
#else
FileTimeSucks user = {}, kernel = {};
#elif defined(_WIN32)
FileTimeU64Union user = {}, kernel = {};
FILETIME dummy;
GetThreadTimes(GetCurrentThread(), &dummy, &dummy, &kernel.filetime, &user.filetime);
return user.u64time + kernel.u64time;
#endif
#elif defined(__APPLE__)
return getthreadtime(pthread_mach_thread_np(pthread_self()));
#else
@@ -477,17 +476,33 @@ u64 Threading::GetThreadCpuTime()
u64 Threading::GetThreadTicksPerSecond()
{
#if defined(_WIN32)
#if 0
#if defined(_WIN32) && !defined(_UWP) && !defined(_M_ARM64)
// On x86, despite what the MS documentation says, this basically appears to be rdtsc.
// So, the frequency is our base clock speed (and stable regardless of power management).
static u64 frequency = 0;
if (unlikely(frequency == 0))
frequency = x86caps.CachedMHz() * u64(1000000);
if (UNLIKELY(frequency == 0))
{
frequency = 1000000;
HKEY hKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_READ, &hKey) ==
ERROR_SUCCESS)
{
DWORD value;
DWORD value_size = sizeof(value);
if (RegQueryValueExW(hKey, L"~MHz", 0, nullptr, reinterpret_cast<LPBYTE>(&value), &value_size) == ERROR_SUCCESS)
{
// value is in mhz, convert to hz
frequency *= value;
}
RegCloseKey(hKey);
}
}
return frequency;
#else
#elif defined(_WIN32)
return 10000000;
#endif
#elif defined(__APPLE__)
return 1000000;
@@ -539,3 +554,60 @@ void Threading::SetNameOfCurrentThread(const char* name)
pthread_set_name_np(pthread_self(), name);
#endif
}
Threading::KernelSemaphore::KernelSemaphore()
{
#ifdef _WIN32
m_sema = CreateSemaphore(nullptr, 0, LONG_MAX, nullptr);
#elif defined(__APPLE__)
semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, 0);
#else
sem_init(&m_sema, false, 0);
#endif
}
Threading::KernelSemaphore::~KernelSemaphore()
{
#ifdef _WIN32
CloseHandle(m_sema);
#elif defined(__APPLE__)
semaphore_destroy(mach_task_self(), m_sema);
#else
sem_destroy(&m_sema);
#endif
}
void Threading::KernelSemaphore::Post()
{
#ifdef _WIN32
ReleaseSemaphore(m_sema, 1, nullptr);
#elif defined(__APPLE__)
semaphore_signal(m_sema);
#else
sem_post(&m_sema);
#endif
}
void Threading::KernelSemaphore::Wait()
{
#ifdef _WIN32
WaitForSingleObject(m_sema, INFINITE);
#elif defined(__APPLE__)
semaphore_wait(m_sema);
#else
sem_wait(&m_sema);
#endif
}
bool Threading::KernelSemaphore::TryWait()
{
#ifdef _WIN32
return WaitForSingleObject(m_sema, 0) == WAIT_OBJECT_0;
#elif defined(__APPLE__)
mach_timespec_t time = {};
kern_return_t res = semaphore_timedwait(m_sema, time);
return (res != KERN_OPERATION_TIMED_OUT);
#else
return sem_trywait(&m_sema) == 0;
#endif
}
+10
View File
@@ -55,6 +55,16 @@ char (&__countof_ArraySizeHelper(T (&array)[N]))[N];
#define printflike(n,m)
#endif
#ifdef _MSC_VER
// TODO: Use C++20 [[likely]] when available.
#define LIKELY(x) (!!(x))
#define UNLIKELY(x) (!!(x))
#else
#define LIKELY(x) __builtin_expect(!!(x), 1)
#define UNLIKELY(x) __builtin_expect(!!(x), 0)
#endif
// disable warnings that show up at warning level 4
// TODO: Move to build system instead
#ifdef _MSC_VER
+2 -2
View File
@@ -386,7 +386,7 @@ void ShaderCache::ClosePipelineCache()
std::string ShaderCache::GetShaderCacheBaseFileName(const std::string_view& base_path, bool debug)
{
std::string base_filename(base_path);
base_filename += "vulkan_shaders";
base_filename += FS_OSPATH_SEPARATOR_STR "vulkan_shaders";
if (debug)
base_filename += "_debug";
@@ -397,7 +397,7 @@ std::string ShaderCache::GetShaderCacheBaseFileName(const std::string_view& base
std::string ShaderCache::GetPipelineCacheBaseFileName(const std::string_view& base_path, bool debug)
{
std::string base_filename(base_path);
base_filename += "vulkan_pipelines";
base_filename += FS_OSPATH_SEPARATOR_STR "vulkan_pipelines";
if (debug)
base_filename += "_debug";
+8 -15
View File
@@ -1,4 +1,5 @@
add_library(core
achievements.h
analog_controller.cpp
analog_controller.h
analog_joystick.cpp
@@ -28,6 +29,8 @@ add_library(core
digital_controller.h
dma.cpp
dma.h
game_database.cpp
game_database.h
gdb_protocol.cpp
gdb_protocol.h
gpu.cpp
@@ -48,21 +51,18 @@ add_library(core
gpu_sw_backend.cpp
gpu_sw_backend.h
gpu_types.h
guncon.cpp
guncon.h
gte.cpp
gte.h
gte_types.h
host.cpp
host.h
host_display.cpp
host_display.h
host_interface.cpp
host_interface.h
host_interface_progress_callback.cpp
host_interface_progress_callback.h
host_settings.h
imgui_styles.cpp
imgui_styles.h
imgui_fullscreen.cpp
imgui_fullscreen.h
interrupt_controller.cpp
interrupt_controller.h
libcrypt_game_codes.cpp
@@ -75,8 +75,6 @@ add_library(core
memory_card_image.h
multitap.cpp
multitap.h
namco_guncon.cpp
namco_guncon.h
negcon.cpp
negcon.h
pad.cpp
@@ -123,7 +121,7 @@ set(RECOMPILER_SRCS
target_include_directories(core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_include_directories(core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_link_libraries(core PUBLIC Threads::Threads common util zlib)
target_link_libraries(core PRIVATE glad stb xxhash imgui)
target_link_libraries(core PRIVATE glad stb xxhash imgui rapidjson tinyxml2)
if(WIN32)
target_sources(core PRIVATE
@@ -161,10 +159,5 @@ else()
endif()
if(ENABLE_CHEEVOS)
target_sources(core PRIVATE
cheevos.cpp
cheevos.h
)
target_compile_definitions(core PUBLIC -DWITH_CHEEVOS=1)
target_link_libraries(core PRIVATE rcheevos rapidjson)
target_compile_definitions(core PRIVATE -DWITH_CHEEVOS=1)
endif()
+55
View File
@@ -0,0 +1,55 @@
#pragma once
#include "common/types.h"
class StateWrapper;
class CDImage;
namespace Achievements {
#ifdef WITH_CHEEVOS
// Implemented in Host.
extern bool Reset();
extern bool DoState(StateWrapper& sw);
extern void GameChanged(const std::string& path, CDImage* image);
/// Re-enables hardcode mode if it is enabled in the settings.
extern void ResetChallengeMode();
/// Forces hardcore mode off until next reset.
extern void DisableChallengeMode();
/// Prompts the user to disable hardcore mode, if they agree, returns true.
extern bool ConfirmChallengeModeDisable(const char* trigger);
/// Returns true if features such as save states should be disabled.
extern bool ChallengeModeActive();
#else
// Make noops when compiling without cheevos.
static inline bool Reset()
{
return true;
}
static inline bool DoState(StateWrapper& sw)
{
return true;
}
static constexpr inline bool ChallengeModeActive()
{
return false;
}
static inline void ResetChallengeMode() {}
static inline void DisableChallengeMode() {}
static inline bool ConfirmChallengeModeDisable(const char* trigger)
{
return true;
}
#endif
} // namespace Achievements
+194 -211
View File
@@ -1,14 +1,14 @@
#include "analog_controller.h"
#include "common/log.h"
#include "common/string_util.h"
#include "host_interface.h"
#include "host.h"
#include "settings.h"
#include "system.h"
#include "util/state_wrapper.h"
#include <cmath>
Log_SetChannel(AnalogController);
AnalogController::AnalogController(u32 index) : m_index(index)
AnalogController::AnalogController(u32 index) : Controller(index)
{
m_axis_state.fill(0x80);
Reset();
@@ -29,7 +29,12 @@ void AnalogController::Reset()
m_tx_buffer.fill(0x00);
m_analog_mode = false;
m_configuration_mode = false;
m_motor_state.fill(0);
for (u32 i = 0; i < NUM_MOTORS; i++)
{
if (m_motor_state[i] != 0)
SetMotorState(i, 0);
}
m_dualshock_enabled = false;
ResetRumbleConfig();
@@ -40,8 +45,8 @@ void AnalogController::Reset()
{
if (g_settings.controller_disable_analog_mode_forcing)
{
g_host_interface->AddOSDMessage(
g_host_interface->TranslateStdString(
Host::AddOSDMessage(
Host::TranslateStdString(
"OSDMessage", "Analog mode forcing is disabled by game settings. Controller will start in digital mode."),
10.0f);
}
@@ -88,72 +93,42 @@ bool AnalogController::DoState(StateWrapper& sw, bool apply_input_state)
if (old_analog_mode != m_analog_mode)
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
5.0f,
m_analog_mode ?
g_host_interface->TranslateString("AnalogController", "Controller %u switched to analog mode.") :
g_host_interface->TranslateString("AnalogController", "Controller %u switched to digital mode."),
m_analog_mode ? Host::TranslateString("AnalogController", "Controller %u switched to analog mode.") :
Host::TranslateString("AnalogController", "Controller %u switched to digital mode."),
m_index + 1u);
}
}
return true;
}
std::optional<s32> AnalogController::GetAxisCodeByName(std::string_view axis_name) const
float AnalogController::GetBindState(u32 index) const
{
return StaticGetAxisCodeByName(axis_name);
}
if (index >= static_cast<u32>(Button::Count))
{
const u32 sub_index = index - static_cast<u32>(Button::Count);
if (sub_index >= static_cast<u32>(m_half_axis_state.size()))
return 0.0f;
std::optional<s32> AnalogController::GetButtonCodeByName(std::string_view button_name) const
{
return StaticGetButtonCodeByName(button_name);
}
float AnalogController::GetAxisState(s32 axis_code) const
{
if (axis_code < 0 || axis_code >= static_cast<s32>(Axis::Count))
return static_cast<float>(m_half_axis_state[sub_index]) * (1.0f / 255.0f);
}
else if (index < static_cast<u32>(Button::Analog))
{
return static_cast<float>(((m_button_state >> index) & 1u) ^ 1u);
}
else
{
return 0.0f;
// 0..255 -> -1..1
const float value = (((static_cast<float>(m_axis_state[static_cast<s32>(axis_code)]) / 255.0f) * 2.0f) - 1.0f);
return std::clamp(value / m_axis_scale, -1.0f, 1.0f);
}
}
void AnalogController::SetAxisState(s32 axis_code, float value)
void AnalogController::SetBindState(u32 index, float value)
{
if (axis_code < 0 || axis_code >= static_cast<s32>(Axis::Count))
return;
// -1..1 -> 0..255
const float scaled_value = std::clamp(value * m_axis_scale, -1.0f, 1.0f);
const u8 u8_value = static_cast<u8>(std::clamp(std::round(((scaled_value + 1.0f) / 2.0f) * 255.0f), 0.0f, 255.0f));
SetAxisState(static_cast<Axis>(axis_code), u8_value);
}
void AnalogController::SetAxisState(Axis axis, u8 value)
{
if (value != m_axis_state[static_cast<u8>(axis)])
System::SetRunaheadReplayFlag();
m_axis_state[static_cast<u8>(axis)] = value;
}
bool AnalogController::GetButtonState(s32 button_code) const
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Analog))
return false;
const u16 bit = u16(1) << static_cast<u8>(button_code);
return ((m_button_state & bit) == 0);
}
void AnalogController::SetButtonState(Button button, bool pressed)
{
if (button == Button::Analog)
if (index == static_cast<s32>(Button::Analog))
{
// analog toggle
if (pressed)
if (value >= 0.5f)
{
if (m_command == Command::Idle)
ProcessAnalogModeToggle();
@@ -163,10 +138,58 @@ void AnalogController::SetButtonState(Button button, bool pressed)
return;
}
else if (index >= static_cast<u32>(Button::Count))
{
const u32 sub_index = index - static_cast<u32>(Button::Count);
if (sub_index >= static_cast<u32>(m_half_axis_state.size()))
return;
const u16 bit = u16(1) << static_cast<u8>(button);
value = ApplyAnalogDeadzoneSensitivity(m_analog_deadzone, m_analog_sensitivity, value);
const u8 u8_value = static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f));
if (u8_value == m_half_axis_state[sub_index])
return;
if (pressed)
m_half_axis_state[sub_index] = u8_value;
System::SetRunaheadReplayFlag();
#define MERGE(pos, neg) \
((m_half_axis_state[static_cast<u32>(pos)] != 0) ? (127u + ((m_half_axis_state[static_cast<u32>(pos)] + 1u) / 2u)) : \
(127u - (m_half_axis_state[static_cast<u32>(neg)] / 2u)))
switch (static_cast<HalfAxis>(sub_index))
{
case HalfAxis::LLeft:
case HalfAxis::LRight:
m_axis_state[static_cast<u8>(Axis::LeftX)] = MERGE(HalfAxis::LRight, HalfAxis::LLeft);
break;
case HalfAxis::LDown:
case HalfAxis::LUp:
m_axis_state[static_cast<u8>(Axis::LeftY)] = MERGE(HalfAxis::LDown, HalfAxis::LUp);
break;
case HalfAxis::RLeft:
case HalfAxis::RRight:
m_axis_state[static_cast<u8>(Axis::RightX)] = MERGE(HalfAxis::RRight, HalfAxis::RLeft);
break;
case HalfAxis::RDown:
case HalfAxis::RUp:
m_axis_state[static_cast<u8>(Axis::RightY)] = MERGE(HalfAxis::RDown, HalfAxis::RUp);
break;
default:
break;
}
#undef MERGE
return;
}
const u16 bit = u16(1) << static_cast<u8>(index);
if (value >= 0.5f)
{
if (m_button_state & bit)
System::SetRunaheadReplayFlag();
@@ -182,14 +205,6 @@ void AnalogController::SetButtonState(Button button, bool pressed)
}
}
void AnalogController::SetButtonState(s32 button_code, bool pressed)
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return;
SetButtonState(static_cast<Button>(button_code), pressed);
}
u32 AnalogController::GetButtonStateBits() const
{
// flip bits, native data is active low
@@ -202,26 +217,6 @@ std::optional<u32> AnalogController::GetAnalogInputBytes() const
m_axis_state[static_cast<size_t>(Axis::RightY)] << 8 | m_axis_state[static_cast<size_t>(Axis::RightX)];
}
u32 AnalogController::GetVibrationMotorCount() const
{
return NUM_MOTORS;
}
float AnalogController::GetVibrationMotorStrength(u32 motor)
{
DebugAssert(motor < NUM_MOTORS);
if (m_motor_state[motor] == 0)
return 0.0f;
// Curve from https://github.com/KrossX/Pokopom/blob/master/Pokopom/Input_XInput.cpp#L210
const double x =
static_cast<double>(std::min<u32>(static_cast<u32>(m_motor_state[motor]) + static_cast<u32>(m_rumble_bias), 255));
const double strength = 0.006474549734772402 * std::pow(x, 3.0) - 1.258165252213538 * std::pow(x, 2.0) +
156.82454281087692 * x + 3.637978807091713e-11;
return static_cast<float>(strength / 65535.0);
}
void AnalogController::ResetTransferState()
{
if (m_analog_toggle_queued)
@@ -240,11 +235,11 @@ void AnalogController::SetAnalogMode(bool enabled)
return;
Log_InfoPrintf("Controller %u switched to %s mode.", m_index + 1u, enabled ? "analog" : "digital");
g_host_interface->AddFormattedOSDMessage(
5.0f,
enabled ? g_host_interface->TranslateString("AnalogController", "Controller %u switched to analog mode.") :
g_host_interface->TranslateString("AnalogController", "Controller %u switched to digital mode."),
m_index + 1u);
Host::AddFormattedOSDMessage(5.0f,
enabled ?
Host::TranslateString("AnalogController", "Controller %u switched to analog mode.") :
Host::TranslateString("AnalogController", "Controller %u switched to digital mode."),
m_index + 1u);
m_analog_mode = enabled;
}
@@ -252,11 +247,10 @@ void AnalogController::ProcessAnalogModeToggle()
{
if (m_analog_locked)
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
5.0f,
m_analog_mode ?
g_host_interface->TranslateString("AnalogController", "Controller %u is locked to analog mode by the game.") :
g_host_interface->TranslateString("AnalogController", "Controller %u is locked to digital mode by the game."),
m_analog_mode ? Host::TranslateString("AnalogController", "Controller %u is locked to analog mode by the game.") :
Host::TranslateString("AnalogController", "Controller %u is locked to digital mode by the game."),
m_index + 1u);
}
else
@@ -269,10 +263,32 @@ void AnalogController::ProcessAnalogModeToggle()
}
}
void AnalogController::SetMotorState(u8 motor, u8 value)
void AnalogController::SetMotorState(u32 motor, u8 value)
{
DebugAssert(motor < NUM_MOTORS);
m_motor_state[motor] = value;
if (m_motor_state[motor] != value)
{
m_motor_state[motor] = value;
UpdateHostVibration();
}
}
void AnalogController::UpdateHostVibration()
{
std::array<float, NUM_MOTORS> hvalues;
for (u32 motor = 0; motor < NUM_MOTORS; motor++)
{
// Curve from https://github.com/KrossX/Pokopom/blob/master/Pokopom/Input_XInput.cpp#L210
const u8 state = m_motor_state[motor];
const double x =
static_cast<double>(std::min<u32>(state + static_cast<u32>(m_rumble_bias), 255));
const double strength = 0.006474549734772402 * std::pow(x, 3.0) - 1.258165252213538 * std::pow(x, 2.0) +
156.82454281087692 * x + 3.637978807091713e-11;
hvalues[motor] = (state != 0) ? static_cast<float>(strength / 65535.0) : 0.0f;
}
Host::SetPadVibrationIntensity(m_index, hvalues[0], hvalues[1]);
}
u8 AnalogController::GetExtraButtonMaskLSB() const
@@ -350,7 +366,7 @@ bool AnalogController::Transfer(const u8 data_in, u8* data_out)
if (data_in == 0x01)
{
Log_DevPrintf("ACK controller access");
Log_DebugPrintf("ACK controller access");
m_command = Command::Ready;
return true;
}
@@ -689,10 +705,10 @@ bool AnalogController::Transfer(const u8 data_in, u8* data_out)
{
m_command = Command::Idle;
Log_DevPrintf("Rx: %02x %02x %02x %02x %02x %02x %02x %02x", m_rx_buffer[0], m_rx_buffer[1], m_rx_buffer[2],
m_rx_buffer[3], m_rx_buffer[4], m_rx_buffer[5], m_rx_buffer[6], m_rx_buffer[7]);
Log_DevPrintf("Tx: %02x %02x %02x %02x %02x %02x %02x %02x", m_tx_buffer[0], m_tx_buffer[1], m_tx_buffer[2],
m_tx_buffer[3], m_tx_buffer[4], m_tx_buffer[5], m_tx_buffer[6], m_tx_buffer[7]);
Log_DebugPrintf("Rx: %02x %02x %02x %02x %02x %02x %02x %02x", m_rx_buffer[0], m_rx_buffer[1], m_rx_buffer[2],
m_rx_buffer[3], m_rx_buffer[4], m_rx_buffer[5], m_rx_buffer[6], m_rx_buffer[7]);
Log_DebugPrintf("Tx: %02x %02x %02x %02x %02x %02x %02x %02x", m_tx_buffer[0], m_tx_buffer[1], m_tx_buffer[2],
m_tx_buffer[3], m_tx_buffer[4], m_tx_buffer[5], m_tx_buffer[6], m_tx_buffer[7]);
m_rx_buffer.fill(0x00);
m_tx_buffer.fill(0x00);
@@ -706,122 +722,89 @@ std::unique_ptr<AnalogController> AnalogController::Create(u32 index)
return std::make_unique<AnalogController>(index);
}
std::optional<s32> AnalogController::StaticGetAxisCodeByName(std::string_view axis_name)
{
#define AXIS(name) \
if (axis_name == #name) \
static const Controller::ControllerBindingInfo s_binding_info[] = {
#define BUTTON(name, display_name, button, genb) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Axis::name))); \
name, display_name, static_cast<u32>(button), Controller::ControllerBindingType::Button, genb \
}
#define AXIS(name, display_name, halfaxis, genb) \
{ \
name, display_name, static_cast<u32>(AnalogController::Button::Count) + static_cast<u32>(halfaxis), \
Controller::ControllerBindingType::HalfAxis, genb \
}
AXIS(LeftX);
AXIS(LeftY);
AXIS(RightX);
AXIS(RightY);
BUTTON("Up", "D-Pad Up", AnalogController::Button::Up, GenericInputBinding::DPadUp),
BUTTON("Right", "D-Pad Right", AnalogController::Button::Right, GenericInputBinding::DPadRight),
BUTTON("Down", "D-Pad Down", AnalogController::Button::Down, GenericInputBinding::DPadDown),
BUTTON("Left", "D-Pad Left", AnalogController::Button::Left, GenericInputBinding::DPadLeft),
BUTTON("Triangle", "Triangle", AnalogController::Button::Triangle, GenericInputBinding::Triangle),
BUTTON("Circle", "Circle", AnalogController::Button::Circle, GenericInputBinding::Circle),
BUTTON("Cross", "Cross", AnalogController::Button::Cross, GenericInputBinding::Cross),
BUTTON("Square", "Square", AnalogController::Button::Square, GenericInputBinding::Square),
BUTTON("Select", "Select", AnalogController::Button::Select, GenericInputBinding::Select),
BUTTON("Start", "Start", AnalogController::Button::Start, GenericInputBinding::Start),
BUTTON("Analog", "Analog Toggle", AnalogController::Button::Analog, GenericInputBinding::System),
BUTTON("L1", "L1", AnalogController::Button::L1, GenericInputBinding::L1),
BUTTON("R1", "R1", AnalogController::Button::R1, GenericInputBinding::R1),
BUTTON("L2", "L2", AnalogController::Button::L2, GenericInputBinding::L2),
BUTTON("R2", "R2", AnalogController::Button::R2, GenericInputBinding::R2),
BUTTON("L3", "L3", AnalogController::Button::L3, GenericInputBinding::L3),
BUTTON("R3", "R3", AnalogController::Button::R3, GenericInputBinding::R3),
return std::nullopt;
AXIS("LLeft", "Left Stick Left", AnalogController::HalfAxis::LLeft, GenericInputBinding::LeftStickLeft),
AXIS("LRight", "Left Stick Right", AnalogController::HalfAxis::LRight, GenericInputBinding::LeftStickRight),
AXIS("LDown", "Left Stick Down", AnalogController::HalfAxis::LDown, GenericInputBinding::LeftStickDown),
AXIS("LUp", "Left Stick Up", AnalogController::HalfAxis::LUp, GenericInputBinding::LeftStickUp),
AXIS("RLeft", "Right Stick Left", AnalogController::HalfAxis::RLeft, GenericInputBinding::RightStickLeft),
AXIS("RRight", "Right Stick Right", AnalogController::HalfAxis::RRight, GenericInputBinding::RightStickRight),
AXIS("RDown", "Right Stick Down", AnalogController::HalfAxis::RDown, GenericInputBinding::RightStickDown),
AXIS("RUp", "Right Stick Up", AnalogController::HalfAxis::RUp, GenericInputBinding::RightStickUp),
#undef AXIS
}
std::optional<s32> AnalogController::StaticGetButtonCodeByName(std::string_view button_name)
{
#define BUTTON(name) \
if (button_name == #name) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Button::name))); \
}
BUTTON(Select);
BUTTON(L3);
BUTTON(R3);
BUTTON(Start);
BUTTON(Up);
BUTTON(Right);
BUTTON(Down);
BUTTON(Left);
BUTTON(L2);
BUTTON(R2);
BUTTON(L1);
BUTTON(R1);
BUTTON(Triangle);
BUTTON(Circle);
BUTTON(Cross);
BUTTON(Square);
BUTTON(Analog);
return std::nullopt;
#undef BUTTON
}
};
Controller::AxisList AnalogController::StaticGetAxisNames()
static const SettingInfo s_settings[] = {
{SettingInfo::Type::Boolean, "ForceAnalogOnReset", TRANSLATABLE("AnalogController", "Force Analog Mode on Reset"),
TRANSLATABLE("AnalogController", "Forces the controller to analog mode when the console is reset/powered on. May "
"cause issues with games, so it is recommended to leave this option off."),
"true"},
{SettingInfo::Type::Boolean, "AnalogDPadInDigitalMode",
TRANSLATABLE("AnalogController", "Use Analog Sticks for D-Pad in Digital Mode"),
TRANSLATABLE("AnalogController",
"Allows you to use the analog sticks to control the d-pad in digital mode, as well as the buttons."),
"true"},
{SettingInfo::Type::Float, "AnalogDeadzone", TRANSLATABLE("AnalogController", "Analog Deadzone"),
TRANSLATABLE("AnalogController",
"Sets the analog stick deadzone, i.e. the fraction of the stick movement which will be ignored."),
"0.00f", "0.00f", "1.00f", "0.01f"},
{SettingInfo::Type::Float, "AnalogSensitivity", TRANSLATABLE("AnalogController", "Analog Sensitivity"),
TRANSLATABLE(
"AnalogController",
"Sets the analog stick axis scaling factor. A value between 1.30 and 1.40 is recommended when using recent "
"controllers, e.g. DualShock 4, Xbox One Controller."),
"1.33f", "0.01f", "2.00f", "0.01f"},
{SettingInfo::Type::Integer, "VibrationBias", TRANSLATABLE("AnalogController", "Vibration Bias"),
TRANSLATABLE("AnalogController", "Sets the rumble bias value. If rumble in some games is too weak or not "
"functioning, try increasing this value."),
"8", "0", "255", "1"}};
const Controller::ControllerInfo AnalogController::INFO = {ControllerType::AnalogController,
"AnalogController",
TRANSLATABLE("ControllerType", "Analog Controller"),
s_binding_info,
countof(s_binding_info),
s_settings,
countof(s_settings),
Controller::VibrationCapabilities::LargeSmallMotors};
void AnalogController::LoadSettings(SettingsInterface& si, const char* section)
{
return {{TRANSLATABLE("AnalogController", "LeftX"), static_cast<s32>(Axis::LeftX), AxisType::Full},
{TRANSLATABLE("AnalogController", "LeftY"), static_cast<s32>(Axis::LeftY), AxisType::Full},
{TRANSLATABLE("AnalogController", "RightX"), static_cast<s32>(Axis::RightX), AxisType::Full},
{TRANSLATABLE("AnalogController", "RightY"), static_cast<s32>(Axis::RightY), AxisType::Full}};
}
Controller::ButtonList AnalogController::StaticGetButtonNames()
{
return {{TRANSLATABLE("AnalogController", "Up"), static_cast<s32>(Button::Up)},
{TRANSLATABLE("AnalogController", "Down"), static_cast<s32>(Button::Down)},
{TRANSLATABLE("AnalogController", "Left"), static_cast<s32>(Button::Left)},
{TRANSLATABLE("AnalogController", "Right"), static_cast<s32>(Button::Right)},
{TRANSLATABLE("AnalogController", "Select"), static_cast<s32>(Button::Select)},
{TRANSLATABLE("AnalogController", "Start"), static_cast<s32>(Button::Start)},
{TRANSLATABLE("AnalogController", "Triangle"), static_cast<s32>(Button::Triangle)},
{TRANSLATABLE("AnalogController", "Cross"), static_cast<s32>(Button::Cross)},
{TRANSLATABLE("AnalogController", "Circle"), static_cast<s32>(Button::Circle)},
{TRANSLATABLE("AnalogController", "Square"), static_cast<s32>(Button::Square)},
{TRANSLATABLE("AnalogController", "L1"), static_cast<s32>(Button::L1)},
{TRANSLATABLE("AnalogController", "L2"), static_cast<s32>(Button::L2)},
{TRANSLATABLE("AnalogController", "R1"), static_cast<s32>(Button::R1)},
{TRANSLATABLE("AnalogController", "R2"), static_cast<s32>(Button::R2)},
{TRANSLATABLE("AnalogController", "L3"), static_cast<s32>(Button::L3)},
{TRANSLATABLE("AnalogController", "R3"), static_cast<s32>(Button::R3)},
{TRANSLATABLE("AnalogController", "Analog"), static_cast<s32>(Button::Analog)}};
}
u32 AnalogController::StaticGetVibrationMotorCount()
{
return NUM_MOTORS;
}
Controller::SettingList AnalogController::StaticGetSettings()
{
static constexpr std::array<SettingInfo, 4> settings = {
{{SettingInfo::Type::Boolean, "ForceAnalogOnReset", TRANSLATABLE("AnalogController", "Force Analog Mode on Reset"),
TRANSLATABLE("AnalogController", "Forces the controller to analog mode when the console is reset/powered on. May "
"cause issues with games, so it is recommended to leave this option off."),
"false"},
{SettingInfo::Type::Boolean, "AnalogDPadInDigitalMode",
TRANSLATABLE("AnalogController", "Use Analog Sticks for D-Pad in Digital Mode"),
TRANSLATABLE("AnalogController",
"Allows you to use the analog sticks to control the d-pad in digital mode, as well as the buttons."),
"false"},
{SettingInfo::Type::Float, "AxisScale", TRANSLATABLE("AnalogController", "Analog Axis Scale"),
TRANSLATABLE(
"AnalogController",
"Sets the analog stick axis scaling factor. A value between 1.30 and 1.40 is recommended when using recent "
"controllers, e.g. DualShock 4, Xbox One Controller."),
"1.00f", "0.01f", "1.50f", "0.01f"},
{SettingInfo::Type::Integer, "VibrationBias", TRANSLATABLE("AnalogController", "Vibration Bias"),
TRANSLATABLE("AnalogController", "Sets the rumble bias value. If rumble in some games is too weak or not "
"functioning, try increasing this value."),
"8", "0", "255", "1"}}};
return SettingList(settings.begin(), settings.end());
}
void AnalogController::LoadSettings(const char* section)
{
Controller::LoadSettings(section);
m_force_analog_on_reset = g_host_interface->GetBoolSettingValue(section, "ForceAnalogOnReset", false);
m_analog_dpad_in_digital_mode = g_host_interface->GetBoolSettingValue(section, "AnalogDPadInDigitalMode", false);
m_axis_scale =
std::clamp(std::abs(g_host_interface->GetFloatSettingValue(section, "AxisScale", 1.00f)), 0.01f, 1.50f);
m_rumble_bias =
static_cast<u8>(std::min<u32>(g_host_interface->GetIntSettingValue(section, "VibrationBias", 8), 255));
Controller::LoadSettings(si, section);
m_force_analog_on_reset = si.GetBoolValue(section, "ForceAnalogOnReset", true);
m_analog_dpad_in_digital_mode = si.GetBoolValue(section, "AnalogDPadInDigitalMode", true);
m_analog_deadzone = std::clamp(si.GetFloatValue(section, "AnalogDeadzone", DEFAULT_STICK_DEADZONE), 0.0f, 1.0f);
m_analog_sensitivity =
std::clamp(si.GetFloatValue(section, "AnalogSensitivity", DEFAULT_STICK_SENSITIVITY), 0.01f, 3.0f);
m_rumble_bias = static_cast<u8>(std::min<u32>(si.GetIntValue(section, "VibrationBias", 8), 255));
}
+25 -23
View File
@@ -39,43 +39,42 @@ public:
Count
};
enum class HalfAxis : u8
{
LLeft,
LRight,
LDown,
LUp,
RLeft,
RRight,
RDown,
RUp,
Count
};
static constexpr u8 NUM_MOTORS = 2;
static const Controller::ControllerInfo INFO;
AnalogController(u32 index);
~AnalogController() override;
static std::unique_ptr<AnalogController> Create(u32 index);
static std::optional<s32> StaticGetAxisCodeByName(std::string_view axis_name);
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
static AxisList StaticGetAxisNames();
static ButtonList StaticGetButtonNames();
static u32 StaticGetVibrationMotorCount();
static SettingList StaticGetSettings();
ControllerType GetType() const override;
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
std::optional<s32> GetButtonCodeByName(std::string_view button_name) const override;
void Reset() override;
bool DoState(StateWrapper& sw, bool ignore_input_state) override;
float GetAxisState(s32 axis_code) const override;
void SetAxisState(s32 axis_code, float value) override;
bool GetButtonState(s32 button_code) const override;
void SetButtonState(s32 button_code, bool pressed) override;
float GetBindState(u32 index) const override;
void SetBindState(u32 index, float value) override;
u32 GetButtonStateBits() const override;
std::optional<u32> GetAnalogInputBytes() const override;
void ResetTransferState() override;
bool Transfer(const u8 data_in, u8* data_out) override;
void SetAxisState(Axis axis, u8 value);
void SetButtonState(Button button, bool pressed);
u32 GetVibrationMotorCount() const override;
float GetVibrationMotorStrength(u32 motor) override;
void LoadSettings(const char* section) override;
void LoadSettings(SettingsInterface& si, const char* section) override;
private:
using MotorState = std::array<u8, NUM_MOTORS>;
@@ -111,16 +110,16 @@ private:
void SetAnalogMode(bool enabled);
void ProcessAnalogModeToggle();
void SetMotorState(u8 motor, u8 value);
void SetMotorState(u32 motor, u8 value);
void UpdateHostVibration();
u8 GetExtraButtonMaskLSB() const;
void ResetRumbleConfig();
void SetMotorStateForConfigIndex(int index, u8 value);
u32 m_index;
bool m_force_analog_on_reset = false;
bool m_analog_dpad_in_digital_mode = false;
float m_axis_scale = 1.00f;
float m_analog_deadzone = 0.0f;
float m_analog_sensitivity = 1.33f;
u8 m_rumble_bias = 8;
bool m_analog_mode = false;
@@ -151,6 +150,9 @@ private:
MotorState m_motor_state{};
// both directions of axis state, merged to m_axis_state
std::array<u8, static_cast<u32>(HalfAxis::Count)> m_half_axis_state{};
// Member variables that are no longer used, but kept and serialized for compatibility with older save states
u8 m_command_param = 0;
bool m_legacy_rumble_unlocked = false;
+146 -167
View File
@@ -1,15 +1,14 @@
#include "analog_joystick.h"
#include "common/log.h"
#include "common/string_util.h"
#include "host_interface.h"
#include "host.h"
#include "system.h"
#include "util/state_wrapper.h"
#include <cmath>
Log_SetChannel(AnalogJoystick);
AnalogJoystick::AnalogJoystick(u32 index)
AnalogJoystick::AnalogJoystick(u32 index) : Controller(index)
{
m_index = index;
m_axis_state.fill(0x80);
Reset();
}
@@ -50,82 +49,101 @@ bool AnalogJoystick::DoState(StateWrapper& sw, bool apply_input_state)
if (sw.IsReading() && (old_analog_mode != m_analog_mode))
{
g_host_interface->AddFormattedOSDMessage(
5.0f,
m_analog_mode ? g_host_interface->TranslateString("AnalogJoystick", "Controller %u switched to analog mode.") :
g_host_interface->TranslateString("AnalogJoystick", "Controller %u switched to digital mode."),
m_index + 1u);
Host::AddFormattedOSDMessage(5.0f,
m_analog_mode ?
Host::TranslateString("AnalogJoystick", "Controller %u switched to analog mode.") :
Host::TranslateString("AnalogJoystick", "Controller %u switched to digital mode."),
m_index + 1u);
}
return true;
}
std::optional<s32> AnalogJoystick::GetAxisCodeByName(std::string_view axis_name) const
float AnalogJoystick::GetBindState(u32 index) const
{
return StaticGetAxisCodeByName(axis_name);
}
std::optional<s32> AnalogJoystick::GetButtonCodeByName(std::string_view button_name) const
{
return StaticGetButtonCodeByName(button_name);
}
float AnalogJoystick::GetAxisState(s32 axis_code) const
{
if (axis_code < 0 || axis_code >= static_cast<s32>(Axis::Count))
return 0.0f;
// 0..255 -> -1..1
const float value = (((static_cast<float>(m_axis_state[static_cast<s32>(axis_code)]) / 255.0f) * 2.0f) - 1.0f);
return std::clamp(value / m_axis_scale, -1.0f, 1.0f);
}
void AnalogJoystick::SetAxisState(s32 axis_code, float value)
{
if (axis_code < 0 || axis_code >= static_cast<s32>(Axis::Count))
return;
// -1..1 -> 0..255
const float scaled_value = std::clamp(value * m_axis_scale, -1.0f, 1.0f);
const u8 u8_value = static_cast<u8>(std::clamp(std::round(((scaled_value + 1.0f) / 2.0f) * 255.0f), 0.0f, 255.0f));
SetAxisState(static_cast<Axis>(axis_code), u8_value);
}
void AnalogJoystick::SetAxisState(Axis axis, u8 value)
{
if (m_axis_state[static_cast<u8>(axis)] != value)
System::SetRunaheadReplayFlag();
m_axis_state[static_cast<u8>(axis)] = value;
}
bool AnalogJoystick::GetButtonState(s32 button_code) const
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return false;
const u16 bit = u16(1) << static_cast<u8>(button_code);
return ((m_button_state & bit) == 0);
}
void AnalogJoystick::SetButtonState(Button button, bool pressed)
{
if (button == Button::Mode)
if (index >= static_cast<u32>(Button::Count))
{
if (pressed)
const u32 sub_index = index - static_cast<u32>(Button::Count);
if (sub_index >= static_cast<u32>(m_half_axis_state.size()))
return 0.0f;
return static_cast<float>(m_half_axis_state[sub_index]) * (1.0f / 255.0f);
}
else if (index < static_cast<u32>(Button::Mode))
{
return static_cast<float>(((m_button_state >> index) & 1u) ^ 1u);
}
else
{
return 0.0f;
}
}
void AnalogJoystick::SetBindState(u32 index, float value)
{
if (index == static_cast<s32>(Button::Mode))
{
// analog toggle
if (value >= 0.5f)
ToggleAnalogMode();
return;
}
else if (index >= static_cast<u32>(Button::Count))
{
const u32 sub_index = index - static_cast<u32>(Button::Count);
if (sub_index >= static_cast<u32>(m_half_axis_state.size()))
return;
const u16 bit = u16(1) << static_cast<u8>(button);
value = ApplyAnalogDeadzoneSensitivity(m_analog_deadzone, m_analog_sensitivity, value);
const u8 u8_value = static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f));
if (u8_value != m_half_axis_state[sub_index])
System::SetRunaheadReplayFlag();
if (pressed)
m_half_axis_state[sub_index] = u8_value;
#define MERGE(pos, neg) \
((m_half_axis_state[static_cast<u32>(pos)] != 0) ? (127u + ((m_half_axis_state[static_cast<u32>(pos)] + 1u) / 2u)) : \
(127u - (m_half_axis_state[static_cast<u32>(neg)] / 2u)))
switch (static_cast<HalfAxis>(sub_index))
{
case HalfAxis::LLeft:
case HalfAxis::LRight:
m_axis_state[static_cast<u8>(Axis::LeftX)] = MERGE(HalfAxis::LRight, HalfAxis::LLeft);
break;
case HalfAxis::LDown:
case HalfAxis::LUp:
m_axis_state[static_cast<u8>(Axis::LeftY)] = MERGE(HalfAxis::LDown, HalfAxis::LUp);
break;
case HalfAxis::RLeft:
case HalfAxis::RRight:
m_axis_state[static_cast<u8>(Axis::RightX)] = MERGE(HalfAxis::RRight, HalfAxis::RLeft);
break;
case HalfAxis::RDown:
case HalfAxis::RUp:
m_axis_state[static_cast<u8>(Axis::RightY)] = MERGE(HalfAxis::RDown, HalfAxis::RUp);
break;
default:
break;
}
#undef MERGE
return;
}
const u16 bit = u16(1) << static_cast<u8>(index);
if (value >= 0.5f)
{
if (m_button_state & bit)
System::SetRunaheadReplayFlag();
m_button_state &= ~bit;
m_button_state &= ~(bit);
}
else
{
@@ -136,14 +154,6 @@ void AnalogJoystick::SetButtonState(Button button, bool pressed)
}
}
void AnalogJoystick::SetButtonState(s32 button_code, bool pressed)
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return;
SetButtonState(static_cast<Button>(button_code), pressed);
}
u32 AnalogJoystick::GetButtonStateBits() const
{
return m_button_state ^ 0xFFFF;
@@ -173,11 +183,11 @@ void AnalogJoystick::ToggleAnalogMode()
m_analog_mode = !m_analog_mode;
Log_InfoPrintf("Joystick %u switched to %s mode.", m_index + 1u, m_analog_mode ? "analog" : "digital");
g_host_interface->AddFormattedOSDMessage(
5.0f,
m_analog_mode ? g_host_interface->TranslateString("AnalogJoystick", "Controller %u switched to analog mode.") :
g_host_interface->TranslateString("AnalogJoystick", "Controller %u switched to digital mode."),
m_index + 1u);
Host::AddFormattedOSDMessage(5.0f,
m_analog_mode ?
Host::TranslateString("AnalogJoystick", "Controller %u switched to analog mode.") :
Host::TranslateString("AnalogJoystick", "Controller %u switched to digital mode."),
m_index + 1u);
}
bool AnalogJoystick::Transfer(const u8 data_in, u8* data_out)
@@ -272,104 +282,73 @@ std::unique_ptr<AnalogJoystick> AnalogJoystick::Create(u32 index)
return std::make_unique<AnalogJoystick>(index);
}
std::optional<s32> AnalogJoystick::StaticGetAxisCodeByName(std::string_view axis_name)
{
#define AXIS(name) \
if (axis_name == #name) \
static const Controller::ControllerBindingInfo s_binding_info[] = {
#define BUTTON(name, display_name, button, genb) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Axis::name))); \
name, display_name, static_cast<u32>(button), Controller::ControllerBindingType::Button, genb \
}
#define AXIS(name, display_name, halfaxis, genb) \
{ \
name, display_name, static_cast<u32>(AnalogJoystick::Button::Count) + static_cast<u32>(halfaxis), \
Controller::ControllerBindingType::HalfAxis, genb \
}
AXIS(LeftX);
AXIS(LeftY);
AXIS(RightX);
AXIS(RightY);
BUTTON("Up", "D-Pad Up", AnalogJoystick::Button::Up, GenericInputBinding::DPadUp),
BUTTON("Right", "D-Pad Right", AnalogJoystick::Button::Right, GenericInputBinding::DPadRight),
BUTTON("Down", "D-Pad Down", AnalogJoystick::Button::Down, GenericInputBinding::DPadDown),
BUTTON("Left", "D-Pad Left", AnalogJoystick::Button::Left, GenericInputBinding::DPadLeft),
BUTTON("Triangle", "Triangle", AnalogJoystick::Button::Triangle, GenericInputBinding::Triangle),
BUTTON("Circle", "Circle", AnalogJoystick::Button::Circle, GenericInputBinding::Circle),
BUTTON("Cross", "Cross", AnalogJoystick::Button::Cross, GenericInputBinding::Cross),
BUTTON("Square", "Square", AnalogJoystick::Button::Square, GenericInputBinding::Square),
BUTTON("Select", "Select", AnalogJoystick::Button::Select, GenericInputBinding::Select),
BUTTON("Start", "Start", AnalogJoystick::Button::Start, GenericInputBinding::Start),
BUTTON("Mode", "Mode Toggle", AnalogJoystick::Button::Mode, GenericInputBinding::System),
BUTTON("L1", "L1", AnalogJoystick::Button::L1, GenericInputBinding::L1),
BUTTON("R1", "R1", AnalogJoystick::Button::R1, GenericInputBinding::R1),
BUTTON("L2", "L2", AnalogJoystick::Button::L2, GenericInputBinding::L2),
BUTTON("R2", "R2", AnalogJoystick::Button::R2, GenericInputBinding::R2),
BUTTON("L3", "L3", AnalogJoystick::Button::L3, GenericInputBinding::L3),
BUTTON("R3", "R3", AnalogJoystick::Button::R3, GenericInputBinding::R3),
return std::nullopt;
AXIS("LLeft", "Left Stick Left", AnalogJoystick::HalfAxis::LLeft, GenericInputBinding::LeftStickLeft),
AXIS("LRight", "Left Stick Right", AnalogJoystick::HalfAxis::LRight, GenericInputBinding::LeftStickRight),
AXIS("LDown", "Left Stick Down", AnalogJoystick::HalfAxis::LDown, GenericInputBinding::LeftStickDown),
AXIS("LUp", "Left Stick Up", AnalogJoystick::HalfAxis::LUp, GenericInputBinding::LeftStickUp),
AXIS("RLeft", "Right Stick Left", AnalogJoystick::HalfAxis::RLeft, GenericInputBinding::RightStickLeft),
AXIS("RRight", "Right Stick Right", AnalogJoystick::HalfAxis::RRight, GenericInputBinding::RightStickRight),
AXIS("RDown", "Right Stick Down", AnalogJoystick::HalfAxis::RDown, GenericInputBinding::RightStickDown),
AXIS("RUp", "Right Stick Up", AnalogJoystick::HalfAxis::RUp, GenericInputBinding::RightStickUp),
#undef AXIS
}
std::optional<s32> AnalogJoystick::StaticGetButtonCodeByName(std::string_view button_name)
{
#define BUTTON(name) \
if (button_name == #name) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Button::name))); \
}
BUTTON(Select);
BUTTON(L3);
BUTTON(R3);
BUTTON(Start);
BUTTON(Up);
BUTTON(Right);
BUTTON(Down);
BUTTON(Left);
BUTTON(L2);
BUTTON(R2);
BUTTON(L1);
BUTTON(R1);
BUTTON(Triangle);
BUTTON(Circle);
BUTTON(Cross);
BUTTON(Square);
BUTTON(Mode);
return std::nullopt;
#undef BUTTON
}
};
Controller::AxisList AnalogJoystick::StaticGetAxisNames()
static const SettingInfo s_settings[] = {
{SettingInfo::Type::Float, "AnalogDeadzone", TRANSLATABLE("AnalogController", "Analog Deadzone"),
TRANSLATABLE("AnalogController",
"Sets the analog stick deadzone, i.e. the fraction of the stick movement which will be ignored.s"),
"1.00f", "0.00f", "1.00f", "0.01f"},
{SettingInfo::Type::Float, "AnalogSensitivity", TRANSLATABLE("AnalogController", "Analog Sensitivity"),
TRANSLATABLE(
"AnalogController",
"Sets the analog stick axis scaling factor. A value between 1.30 and 1.40 is recommended when using recent "
"controllers, e.g. DualShock 4, Xbox One Controller."),
"1.33f", "0.01f", "2.00f", "0.01f"}};
const Controller::ControllerInfo AnalogJoystick::INFO = {ControllerType::AnalogJoystick,
"AnalogJoystick",
TRANSLATABLE("ControllerType", "Analog Joystick"),
s_binding_info,
countof(s_binding_info),
s_settings,
countof(s_settings),
Controller::VibrationCapabilities::NoVibration};
void AnalogJoystick::LoadSettings(SettingsInterface& si, const char* section)
{
return {{TRANSLATABLE("AnalogJoystick", "LeftX"), static_cast<s32>(Axis::LeftX), AxisType::Full},
{TRANSLATABLE("AnalogJoystick", "LeftY"), static_cast<s32>(Axis::LeftY), AxisType::Full},
{TRANSLATABLE("AnalogJoystick", "RightX"), static_cast<s32>(Axis::RightX), AxisType::Full},
{TRANSLATABLE("AnalogJoystick", "RightY"), static_cast<s32>(Axis::RightY), AxisType::Full}};
}
Controller::ButtonList AnalogJoystick::StaticGetButtonNames()
{
return {{TRANSLATABLE("AnalogJoystick", "Up"), static_cast<s32>(Button::Up)},
{TRANSLATABLE("AnalogJoystick", "Down"), static_cast<s32>(Button::Down)},
{TRANSLATABLE("AnalogJoystick", "Left"), static_cast<s32>(Button::Left)},
{TRANSLATABLE("AnalogJoystick", "Right"), static_cast<s32>(Button::Right)},
{TRANSLATABLE("AnalogJoystick", "Select"), static_cast<s32>(Button::Select)},
{TRANSLATABLE("AnalogJoystick", "Start"), static_cast<s32>(Button::Start)},
{TRANSLATABLE("AnalogJoystick", "Triangle"), static_cast<s32>(Button::Triangle)},
{TRANSLATABLE("AnalogJoystick", "Cross"), static_cast<s32>(Button::Cross)},
{TRANSLATABLE("AnalogJoystick", "Circle"), static_cast<s32>(Button::Circle)},
{TRANSLATABLE("AnalogJoystick", "Square"), static_cast<s32>(Button::Square)},
{TRANSLATABLE("AnalogJoystick", "L1"), static_cast<s32>(Button::L1)},
{TRANSLATABLE("AnalogJoystick", "L2"), static_cast<s32>(Button::L2)},
{TRANSLATABLE("AnalogJoystick", "R1"), static_cast<s32>(Button::R1)},
{TRANSLATABLE("AnalogJoystick", "R2"), static_cast<s32>(Button::R2)},
{TRANSLATABLE("AnalogJoystick", "L3"), static_cast<s32>(Button::L3)},
{TRANSLATABLE("AnalogJoystick", "R3"), static_cast<s32>(Button::R3)},
{TRANSLATABLE("AnalogJoystick", "Analog"), static_cast<s32>(Button::Mode)}};
}
u32 AnalogJoystick::StaticGetVibrationMotorCount()
{
return 0;
}
Controller::SettingList AnalogJoystick::StaticGetSettings()
{
static constexpr std::array<SettingInfo, 1> settings = {
{{SettingInfo::Type::Float, "AxisScale", TRANSLATABLE("AnalogJoystick", "Analog Axis Scale"),
TRANSLATABLE(
"AnalogJoystick",
"Sets the analog stick axis scaling factor. A value between 1.30 and 1.40 is recommended when using recent "
"controllers, e.g. DualShock 4, Xbox One Controller."),
"1.00f", "0.01f", "1.50f", "0.01f"}}};
return SettingList(settings.begin(), settings.end());
}
void AnalogJoystick::LoadSettings(const char* section)
{
Controller::LoadSettings(section);
m_axis_scale = std::clamp(g_host_interface->GetFloatSettingValue(section, "AxisScale", 1.00f), 0.01f, 1.50f);
Controller::LoadSettings(si, section);
m_analog_deadzone = std::clamp(si.GetFloatValue(section, "AnalogDeadzone", DEFAULT_STICK_DEADZONE), 0.0f, 1.0f);
m_analog_sensitivity =
std::clamp(si.GetFloatValue(section, "AnalogSensitivity", DEFAULT_STICK_SENSITIVITY), 0.01f, 3.0f);
}
+23 -19
View File
@@ -39,38 +39,40 @@ public:
Count
};
enum class HalfAxis : u8
{
LLeft,
LRight,
LDown,
LUp,
RLeft,
RRight,
RDown,
RUp,
Count
};
static const Controller::ControllerInfo INFO;
AnalogJoystick(u32 index);
~AnalogJoystick() override;
static std::unique_ptr<AnalogJoystick> Create(u32 index);
static std::optional<s32> StaticGetAxisCodeByName(std::string_view axis_name);
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
static AxisList StaticGetAxisNames();
static ButtonList StaticGetButtonNames();
static u32 StaticGetVibrationMotorCount();
static SettingList StaticGetSettings();
ControllerType GetType() const override;
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
std::optional<s32> GetButtonCodeByName(std::string_view button_name) const override;
void Reset() override;
bool DoState(StateWrapper& sw, bool apply_input_state) override;
float GetAxisState(s32 axis_code) const override;
void SetAxisState(s32 axis_code, float value) override;
bool GetButtonState(s32 button_code) const override;
void SetButtonState(s32 button_code, bool pressed) override;
float GetBindState(u32 index) const override;
void SetBindState(u32 index, float value) override;
u32 GetButtonStateBits() const override;
std::optional<u32> GetAnalogInputBytes() const override;
void ResetTransferState() override;
bool Transfer(const u8 data_in, u8* data_out) override;
void LoadSettings(const char* section) override;
void SetAxisState(Axis axis, u8 value);
void SetButtonState(Button button, bool pressed);
void LoadSettings(SettingsInterface& si, const char* section) override;
private:
enum class TransferState : u8
@@ -89,9 +91,8 @@ private:
u16 GetID() const;
void ToggleAnalogMode();
u32 m_index;
float m_axis_scale = 1.00f;
float m_analog_deadzone = 0.0f;
float m_analog_sensitivity = 1.33f;
// On original hardware, the mode toggle is a switch rather than a button, so we'll enable Analog Mode by default
bool m_analog_mode = true;
@@ -101,5 +102,8 @@ private:
std::array<u8, static_cast<u8>(Axis::Count)> m_axis_state{};
// both directions of axis state, merged to m_axis_state
std::array<u8, static_cast<u32>(HalfAxis::Count)> m_half_axis_state{};
TransferState m_transfer_state = TransferState::Idle;
};
+144
View File
@@ -3,7 +3,11 @@
#include "common/file_system.h"
#include "common/log.h"
#include "common/md5_digest.h"
#include "common/path.h"
#include "cpu_disasm.h"
#include "host.h"
#include "host_settings.h"
#include "settings.h"
#include <array>
#include <cerrno>
Log_SetChannel(BIOS);
@@ -272,3 +276,143 @@ DiscRegion GetPSExeDiscRegion(const PSEXEHeader& header)
}
} // namespace BIOS
std::optional<std::vector<u8>> BIOS::GetBIOSImage(ConsoleRegion region)
{
std::string bios_name;
switch (region)
{
case ConsoleRegion::NTSC_J:
bios_name = Host::GetStringSettingValue("BIOS", "PathNTSCJ", "");
break;
case ConsoleRegion::PAL:
bios_name = Host::GetStringSettingValue("BIOS", "PathPAL", "");
break;
case ConsoleRegion::NTSC_U:
default:
bios_name = Host::GetStringSettingValue("BIOS", "PathNTSCU", "");
break;
}
if (bios_name.empty())
{
// auto-detect
return FindBIOSImageInDirectory(region, EmuFolders::Bios.c_str());
}
// try the configured path
std::optional<Image> image = LoadImageFromFile(Path::Combine(EmuFolders::Bios, bios_name).c_str());
if (!image.has_value())
{
Host::ReportFormattedErrorAsync(
"Error", Host::TranslateString("HostInterface", "Failed to load configured BIOS file '%s'"), bios_name.c_str());
return std::nullopt;
}
Hash found_hash = GetHash(*image);
Log_DevPrintf("Hash for BIOS '%s': %s", bios_name.c_str(), found_hash.ToString().c_str());
if (!IsValidHashForRegion(region, found_hash))
Log_WarningPrintf("Hash for BIOS '%s' does not match region. This may cause issues.", bios_name.c_str());
return image;
}
std::optional<std::vector<u8>> BIOS::FindBIOSImageInDirectory(ConsoleRegion region, const char* directory)
{
Log_InfoPrintf("Searching for a %s BIOS in '%s'...", Settings::GetConsoleRegionDisplayName(region), directory);
FileSystem::FindResultsArray results;
FileSystem::FindFiles(
directory, "*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RELATIVE_PATHS, &results);
std::string fallback_path;
std::optional<Image> fallback_image;
const ImageInfo* fallback_info = nullptr;
for (const FILESYSTEM_FIND_DATA& fd : results)
{
if (fd.Size != BIOS_SIZE && fd.Size != BIOS_SIZE_PS2 && fd.Size != BIOS_SIZE_PS3)
{
Log_WarningPrintf("Skipping '%s': incorrect size", fd.FileName.c_str());
continue;
}
std::string full_path(Path::Combine(directory, fd.FileName));
std::optional<Image> found_image = LoadImageFromFile(full_path.c_str());
if (!found_image)
continue;
Hash found_hash = GetHash(*found_image);
Log_DevPrintf("Hash for BIOS '%s': %s", fd.FileName.c_str(), found_hash.ToString().c_str());
const ImageInfo* ii = GetImageInfoForHash(found_hash);
if (IsValidHashForRegion(region, found_hash))
{
Log_InfoPrintf("Using BIOS '%s': %s", fd.FileName.c_str(), ii ? ii->description : "");
return found_image;
}
// don't let an unknown bios take precedence over a known one
if (!fallback_path.empty() && (fallback_info || !ii))
continue;
fallback_path = std::move(full_path);
fallback_image = std::move(found_image);
fallback_info = ii;
}
if (!fallback_image.has_value())
{
Host::ReportFormattedErrorAsync("Error",
Host::TranslateString("HostInterface", "No BIOS image found for %s region"),
Settings::GetConsoleRegionDisplayName(region));
return std::nullopt;
}
if (!fallback_info)
{
Log_WarningPrintf("Using unknown BIOS '%s'. This may crash.", fallback_path.c_str());
}
else
{
Log_WarningPrintf("Falling back to possibly-incompatible image '%s': %s", fallback_path.c_str(),
fallback_info->description);
}
return fallback_image;
}
std::vector<std::pair<std::string, const BIOS::ImageInfo*>> BIOS::FindBIOSImagesInDirectory(const char* directory)
{
std::vector<std::pair<std::string, const ImageInfo*>> results;
FileSystem::FindResultsArray files;
FileSystem::FindFiles(directory, "*",
FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RELATIVE_PATHS, &files);
for (FILESYSTEM_FIND_DATA& fd : files)
{
if (fd.Size != BIOS_SIZE && fd.Size != BIOS_SIZE_PS2 && fd.Size != BIOS_SIZE_PS3)
continue;
std::string full_path(Path::Combine(directory, fd.FileName));
std::optional<Image> found_image = LoadImageFromFile(full_path.c_str());
if (!found_image)
continue;
Hash found_hash = GetHash(*found_image);
const ImageInfo* ii = GetImageInfoForHash(found_hash);
results.emplace_back(std::move(fd.FileName), ii);
}
return results;
}
bool BIOS::HasAnyBIOSImages()
{
return FindBIOSImageInDirectory(ConsoleRegion::Auto, EmuFolders::Bios.c_str()).has_value();
}
+13
View File
@@ -70,4 +70,17 @@ bool PatchBIOSForEXE(u8* image, u32 image_size, u32 r_pc, u32 r_gp, u32 r_sp, u3
bool IsValidPSExeHeader(const PSEXEHeader& header, u32 file_size);
DiscRegion GetPSExeDiscRegion(const PSEXEHeader& header);
/// Loads the BIOS image for the specified region.
std::optional<std::vector<u8>> GetBIOSImage(ConsoleRegion region);
/// Searches for a BIOS image for the specified region in the specified directory. If no match is found, the first
/// BIOS image within 512KB and 4MB will be used.
std::optional<std::vector<u8>> FindBIOSImageInDirectory(ConsoleRegion region, const char* directory);
/// Returns a list of filenames and descriptions for BIOS images in a directory.
std::vector<std::pair<std::string, const BIOS::ImageInfo*>> FindBIOSImagesInDirectory(const char* directory);
/// Returns true if any BIOS images are found in the configured BIOS directory.
bool HasAnyBIOSImages();
} // namespace BIOS
+2 -2
View File
@@ -10,7 +10,7 @@
#include "cpu_disasm.h"
#include "dma.h"
#include "gpu.h"
#include "host_interface.h"
#include "host.h"
#include "interrupt_controller.h"
#include "mdec.h"
#include "pad.h"
@@ -130,7 +130,7 @@ bool Initialize()
{
if (!AllocateMemory(g_settings.enable_8mb_ram))
{
g_host_interface->ReportError("Failed to allocate memory");
Host::ReportErrorAsync("Error", "Failed to allocate memory");
return false;
}
+2 -1
View File
@@ -3,6 +3,7 @@
#include "common/log.h"
#include "common/platform.h"
#include "dma.h"
#include "host.h"
#include "imgui.h"
#include "interrupt_controller.h"
#include "settings.h"
@@ -2722,7 +2723,7 @@ void CDROM::DrawDebugWindow()
{
static const ImVec4 active_color{1.0f, 1.0f, 1.0f, 1.0f};
static const ImVec4 inactive_color{0.4f, 0.4f, 0.4f, 1.0f};
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
const float framebuffer_scale = Host::GetOSDScale();
ImGui::SetNextWindowSize(ImVec2(800.0f * framebuffer_scale, 550.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("CDROM State", nullptr))
+1
View File
@@ -29,6 +29,7 @@ public:
bool HasMedia() const { return m_reader.HasMedia(); }
const std::string& GetMediaFileName() const { return m_reader.GetMediaFileName(); }
const CDImage* GetMedia() const { return m_reader.GetMedia(); }
DiscRegion GetDiscRegion() const { return m_disc_region; }
bool IsMediaPS1Disc() const;
bool DoesMediaRegionMatchConsole() const;
+4 -10
View File
@@ -9,7 +9,7 @@
#include "controller.h"
#include "cpu_code_cache.h"
#include "cpu_core.h"
#include "host_interface.h"
#include "host.h"
#include "system.h"
#include <cctype>
#include <iomanip>
@@ -686,17 +686,11 @@ bool CheatList::SaveToPCSXRFile(const char* filename)
bool CheatList::LoadFromPackage(const std::string& game_code)
{
std::unique_ptr<ByteStream> stream =
g_host_interface->OpenPackageFile("database/chtdb.txt", BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED);
if (!stream)
const std::optional<std::string> db_string(Host::ReadResourceFileToString("chtdb.txt"));
if (!db_string.has_value())
return false;
std::string db_string = ByteStream::ReadStreamToString(stream.get());
stream.reset();
if (db_string.empty())
return false;
std::istringstream iss(db_string);
std::istringstream iss(db_string.value());
std::string line;
while (std::getline(iss, line))
{
-1802
View File
File diff suppressed because it is too large Load Diff
+135 -182
View File
@@ -2,12 +2,27 @@
#include "analog_controller.h"
#include "analog_joystick.h"
#include "digital_controller.h"
#include "namco_guncon.h"
#include "fmt/format.h"
#include "guncon.h"
#include "negcon.h"
#include "playstation_mouse.h"
#include "util/state_wrapper.h"
Controller::Controller() = default;
static const Controller::ControllerInfo s_none_info = {ControllerType::None,
"None",
"Not Connected",
nullptr,
0,
nullptr,
0,
Controller::VibrationCapabilities::NoVibration};
static const Controller::ControllerInfo* s_controller_info[] = {
&s_none_info, &DigitalController::INFO, &AnalogController::INFO, &AnalogJoystick::INFO, &NeGcon::INFO,
&GunCon::INFO, &PlayStationMouse::INFO,
};
Controller::Controller(u32 index) : m_index(index) {}
Controller::~Controller() = default;
@@ -26,19 +41,12 @@ bool Controller::Transfer(const u8 data_in, u8* data_out)
return false;
}
float Controller::GetAxisState(s32 axis_code) const
float Controller::GetBindState(u32 index) const
{
return 0.0f;
}
void Controller::SetAxisState(s32 axis_code, float value) {}
bool Controller::GetButtonState(s32 button_code) const
{
return false;
}
void Controller::SetButtonState(s32 button_code, bool pressed) {}
void Controller::SetBindState(u32 index, float value) {}
u32 Controller::GetButtonStateBits() const
{
@@ -50,17 +58,7 @@ std::optional<u32> Controller::GetAnalogInputBytes() const
return std::nullopt;
}
u32 Controller::GetVibrationMotorCount() const
{
return 0;
}
float Controller::GetVibrationMotorStrength(u32 motor)
{
return 0.0f;
}
void Controller::LoadSettings(const char* section) {}
void Controller::LoadSettings(SettingsInterface& si, const char* section) {}
bool Controller::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode)
{
@@ -72,7 +70,7 @@ std::unique_ptr<Controller> Controller::Create(ControllerType type, u32 index)
switch (type)
{
case ControllerType::DigitalController:
return DigitalController::Create();
return DigitalController::Create(index);
case ControllerType::AnalogController:
return AnalogController::Create(index);
@@ -80,14 +78,14 @@ std::unique_ptr<Controller> Controller::Create(ControllerType type, u32 index)
case ControllerType::AnalogJoystick:
return AnalogJoystick::Create(index);
case ControllerType::NamcoGunCon:
return NamcoGunCon::Create();
case ControllerType::GunCon:
return GunCon::Create(index);
case ControllerType::PlayStationMouse:
return PlayStationMouse::Create();
return PlayStationMouse::Create(index);
case ControllerType::NeGcon:
return NeGcon::Create();
return NeGcon::Create(index);
case ControllerType::None:
default:
@@ -95,179 +93,134 @@ std::unique_ptr<Controller> Controller::Create(ControllerType type, u32 index)
}
}
std::optional<s32> Controller::GetAxisCodeByName(std::string_view button_name) const
const char* Controller::GetDefaultPadType(u32 pad)
{
return (pad == 0) ? "DigitalController" : "None";
}
const Controller::ControllerInfo* Controller::GetControllerInfo(ControllerType type)
{
for (const ControllerInfo* info : s_controller_info)
{
if (type == info->type)
return info;
}
return nullptr;
}
const Controller::ControllerInfo* Controller::GetControllerInfo(const std::string_view& name)
{
for (const ControllerInfo* info : s_controller_info)
{
if (name == info->name)
return info;
}
return nullptr;
}
std::vector<std::pair<std::string, std::string>> Controller::GetControllerTypeNames()
{
std::vector<std::pair<std::string, std::string>> ret;
for (const ControllerInfo* info : s_controller_info)
ret.emplace_back(info->name, info->display_name);
return ret;
}
std::vector<std::string> Controller::GetControllerBinds(const std::string_view& type)
{
std::vector<std::string> ret;
const ControllerInfo* info = GetControllerInfo(type);
if (info)
{
for (u32 i = 0; i < info->num_bindings; i++)
{
const ControllerBindingInfo& bi = info->bindings[i];
if (bi.type == ControllerBindingType::Unknown || bi.type == ControllerBindingType::Motor)
continue;
ret.emplace_back(info->bindings[i].name);
}
}
return ret;
}
std::vector<std::string> Controller::GetControllerBinds(ControllerType type)
{
std::vector<std::string> ret;
const ControllerInfo* info = GetControllerInfo(type);
if (info)
{
for (u32 i = 0; i < info->num_bindings; i++)
{
const ControllerBindingInfo& bi = info->bindings[i];
if (bi.type == ControllerBindingType::Unknown || bi.type == ControllerBindingType::Motor)
continue;
ret.emplace_back(info->bindings[i].name);
}
}
return ret;
}
std::optional<u32> Controller::GetBindIndex(ControllerType type, const std::string_view& bind_name)
{
const ControllerInfo* info = GetControllerInfo(type);
if (!info)
return std::nullopt;
for (u32 i = 0; i < info->num_bindings; i++)
{
if (bind_name == info->bindings[i].name)
return i;
}
return std::nullopt;
}
std::optional<s32> Controller::GetButtonCodeByName(std::string_view button_name) const
Controller::VibrationCapabilities Controller::GetControllerVibrationCapabilities(const std::string_view& type)
{
return std::nullopt;
const ControllerInfo* info = GetControllerInfo(type);
return info ? info->vibration_caps : VibrationCapabilities::NoVibration;
}
Controller::AxisList Controller::GetAxisNames(ControllerType type)
std::tuple<u32, u32> Controller::ConvertPadToPortAndSlot(u32 index)
{
switch (type)
{
case ControllerType::DigitalController:
return DigitalController::StaticGetAxisNames();
case ControllerType::AnalogController:
return AnalogController::StaticGetAxisNames();
case ControllerType::AnalogJoystick:
return AnalogJoystick::StaticGetAxisNames();
case ControllerType::NamcoGunCon:
return NamcoGunCon::StaticGetAxisNames();
case ControllerType::PlayStationMouse:
return PlayStationMouse::StaticGetAxisNames();
case ControllerType::NeGcon:
return NeGcon::StaticGetAxisNames();
case ControllerType::None:
default:
return {};
}
if (index > 4) // [5,6,7]
return std::make_tuple(1, index - 4); // 2B,2C,2D
else if (index > 1) // [2,3,4]
return std::make_tuple(0, index - 1); // 1B,1C,1D
else // [0,1]
return std::make_tuple(index, 0); // 1A,2A
}
Controller::ButtonList Controller::GetButtonNames(ControllerType type)
u32 Controller::ConvertPortAndSlotToPad(u32 port, u32 slot)
{
switch (type)
{
case ControllerType::DigitalController:
return DigitalController::StaticGetButtonNames();
case ControllerType::AnalogController:
return AnalogController::StaticGetButtonNames();
case ControllerType::AnalogJoystick:
return AnalogJoystick::StaticGetButtonNames();
case ControllerType::NamcoGunCon:
return NamcoGunCon::StaticGetButtonNames();
case ControllerType::PlayStationMouse:
return PlayStationMouse::StaticGetButtonNames();
case ControllerType::NeGcon:
return NeGcon::StaticGetButtonNames();
case ControllerType::None:
default:
return {};
}
if (slot == 0)
return port;
else if (port == 0) // slot=[0,1]
return slot + 1; // 2,3,4
else
return slot + 4; // 5,6,7
}
u32 Controller::GetVibrationMotorCount(ControllerType type)
bool Controller::PadIsMultitapSlot(u32 index)
{
switch (type)
{
case ControllerType::DigitalController:
return DigitalController::StaticGetVibrationMotorCount();
case ControllerType::AnalogController:
return AnalogController::StaticGetVibrationMotorCount();
case ControllerType::AnalogJoystick:
return AnalogJoystick::StaticGetVibrationMotorCount();
case ControllerType::NamcoGunCon:
return NamcoGunCon::StaticGetVibrationMotorCount();
case ControllerType::PlayStationMouse:
return PlayStationMouse::StaticGetVibrationMotorCount();
case ControllerType::NeGcon:
return NeGcon::StaticGetVibrationMotorCount();
case ControllerType::None:
default:
return 0;
}
return (index >= 2);
}
std::optional<s32> Controller::GetAxisCodeByName(ControllerType type, std::string_view axis_name)
bool Controller::PortAndSlotIsMultitap(u32 port, u32 slot)
{
switch (type)
{
case ControllerType::DigitalController:
return DigitalController::StaticGetAxisCodeByName(axis_name);
case ControllerType::AnalogController:
return AnalogController::StaticGetAxisCodeByName(axis_name);
case ControllerType::AnalogJoystick:
return AnalogJoystick::StaticGetAxisCodeByName(axis_name);
case ControllerType::NamcoGunCon:
return NamcoGunCon::StaticGetAxisCodeByName(axis_name);
case ControllerType::PlayStationMouse:
return PlayStationMouse::StaticGetAxisCodeByName(axis_name);
case ControllerType::NeGcon:
return NeGcon::StaticGetAxisCodeByName(axis_name);
case ControllerType::None:
default:
return std::nullopt;
}
return (slot != 0);
}
std::optional<s32> Controller::GetButtonCodeByName(ControllerType type, std::string_view button_name)
std::string Controller::GetSettingsSection(u32 pad)
{
switch (type)
{
case ControllerType::DigitalController:
return DigitalController::StaticGetButtonCodeByName(button_name);
case ControllerType::AnalogController:
return AnalogController::StaticGetButtonCodeByName(button_name);
case ControllerType::AnalogJoystick:
return AnalogJoystick::StaticGetButtonCodeByName(button_name);
case ControllerType::NamcoGunCon:
return NamcoGunCon::StaticGetButtonCodeByName(button_name);
case ControllerType::PlayStationMouse:
return PlayStationMouse::StaticGetButtonCodeByName(button_name);
case ControllerType::NeGcon:
return NeGcon::StaticGetButtonCodeByName(button_name);
case ControllerType::None:
default:
return std::nullopt;
}
}
Controller::SettingList Controller::GetSettings(ControllerType type)
{
switch (type)
{
case ControllerType::DigitalController:
return DigitalController::StaticGetSettings();
case ControllerType::AnalogController:
return AnalogController::StaticGetSettings();
case ControllerType::AnalogJoystick:
return AnalogJoystick::StaticGetSettings();
case ControllerType::NamcoGunCon:
return NamcoGunCon::StaticGetSettings();
case ControllerType::NeGcon:
return NeGcon::StaticGetSettings();
case ControllerType::PlayStationMouse:
return PlayStationMouse::StaticGetSettings();
default:
return {};
}
return fmt::format("Pad{}", pad + 1u);
}
+83 -40
View File
@@ -6,36 +6,67 @@
#include <optional>
#include <string>
#include <string_view>
#include <tuple>
#include <vector>
class SettingsInterface;
class StateWrapper;
class HostInterface;
enum class GenericInputBinding : u8;
class Controller
{
public:
enum class AxisType : u8
enum class ControllerBindingType : u8
{
Full,
Half
Unknown,
Button,
Axis,
HalfAxis,
Motor,
Macro
};
using ButtonList = std::vector<std::pair<std::string, s32>>;
using AxisList = std::vector<std::tuple<std::string, s32, AxisType>>;
using SettingList = std::vector<SettingInfo>;
enum class VibrationCapabilities : u8
{
NoVibration,
LargeSmallMotors,
SingleMotor,
Count
};
Controller();
struct ControllerBindingInfo
{
const char* name;
const char* display_name;
u32 bind_index;
ControllerBindingType type;
GenericInputBinding generic_mapping;
};
struct ControllerInfo
{
ControllerType type;
const char* name;
const char* display_name;
const ControllerBindingInfo* bindings;
u32 num_bindings;
const SettingInfo* settings;
u32 num_settings;
VibrationCapabilities vibration_caps;
};
/// Default stick deadzone/sensitivity.
static constexpr float DEFAULT_STICK_DEADZONE = 0.0f;
static constexpr float DEFAULT_STICK_SENSITIVITY = 1.33f;
Controller(u32 index);
virtual ~Controller();
/// Returns the type of controller.
virtual ControllerType GetType() const = 0;
/// Gets the integer code for an axis in the specified controller type.
virtual std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const;
/// Gets the integer code for a button in the specified controller type.
virtual std::optional<s32> GetButtonCodeByName(std::string_view button_name) const;
virtual void Reset();
virtual bool DoState(StateWrapper& sw, bool apply_input_state);
@@ -46,16 +77,10 @@ public:
virtual bool Transfer(const u8 data_in, u8* data_out);
/// Changes the specified axis state. Values are normalized from -1..1.
virtual float GetAxisState(s32 axis_code) const;
virtual float GetBindState(u32 index) const;
/// Changes the specified axis state. Values are normalized from -1..1.
virtual void SetAxisState(s32 axis_code, float value);
/// Returns the specified button state.
virtual bool GetButtonState(s32 button_code) const;
/// Changes the specified button state.
virtual void SetButtonState(s32 button_code, bool pressed);
/// Changes the specified bind state. Values are normalized from -1..1.
virtual void SetBindState(u32 index, float value);
/// Returns a bitmask of the current button states, 1 = on.
virtual u32 GetButtonStateBits() const;
@@ -63,14 +88,8 @@ public:
/// Returns analog input bytes packed as a u32. Values are specific to controller type.
virtual std::optional<u32> GetAnalogInputBytes() const;
/// Returns the number of vibration motors.
virtual u32 GetVibrationMotorCount() const;
/// Queries the state of the specified vibration motor. Values are normalized from 0..1.
virtual float GetVibrationMotorStrength(u32 motor);
/// Loads/refreshes any per-controller settings.
virtual void LoadSettings(const char* section);
virtual void LoadSettings(SettingsInterface& si, const char* section);
/// Returns the software cursor to use for this controller, if any.
virtual bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode);
@@ -78,21 +97,45 @@ public:
/// Creates a new controller of the specified type.
static std::unique_ptr<Controller> Create(ControllerType type, u32 index);
/// Returns the default type for the specified port.
static const char* GetDefaultPadType(u32 pad);
/// Returns a list of controller type names. Pair of [name, display name].
static std::vector<std::pair<std::string, std::string>> GetControllerTypeNames();
/// Returns the list of binds for the specified controller type.
static std::vector<std::string> GetControllerBinds(const std::string_view& type);
static std::vector<std::string> GetControllerBinds(ControllerType type);
/// Gets the integer code for an axis in the specified controller type.
static std::optional<s32> GetAxisCodeByName(ControllerType type, std::string_view axis_name);
static std::optional<u32> GetBindIndex(ControllerType type, const std::string_view& bind_name);
/// Gets the integer code for a button in the specified controller type.
static std::optional<s32> GetButtonCodeByName(ControllerType type, std::string_view button_name);
/// Returns the vibration configuration for the specified controller type.
static VibrationCapabilities GetControllerVibrationCapabilities(const std::string_view& type);
/// Returns a list of axises for the specified controller type.
static AxisList GetAxisNames(ControllerType type);
/// Returns general information for the specified controller type.
static const ControllerInfo* GetControllerInfo(ControllerType type);
static const ControllerInfo* GetControllerInfo(const std::string_view& name);
/// Returns a list of buttons for the specified controller type.
static ButtonList GetButtonNames(ControllerType type);
/// Converts a global pad index to a multitap port and slot.
static std::tuple<u32, u32> ConvertPadToPortAndSlot(u32 index);
/// Returns the number of vibration motors.
static u32 GetVibrationMotorCount(ControllerType type);
/// Converts a multitap port and slot to a global pad index.
static u32 ConvertPortAndSlotToPad(u32 port, u32 slot);
/// Returns settings for the controller.
static SettingList GetSettings(ControllerType type);
/// Returns true if the given pad index is a multitap slot.
static bool PadIsMultitapSlot(u32 index);
static bool PortAndSlotIsMultitap(u32 port, u32 slot);
/// Returns the configuration section for the specified gamepad.
static std::string GetSettingsSection(u32 pad);
/// Applies an analog deadzone/sensitivity.
static float ApplyAnalogDeadzoneSensitivity(float deadzone, float sensitivity, float value)
{
return (value < deadzone) ? 0.0f : ((value - deadzone) / (1.0f - deadzone) * sensitivity);
}
protected:
u32 m_index;
};
+2 -2
View File
@@ -9,7 +9,7 @@
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM' Or '$(Platform)'=='ARM64')">WITH_RECOMPILER=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM64') And ('$(BuildingForUWP)'!='true')">WITH_MMAP_FASTMEM=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\rcheevos\include;$(SolutionDir)dep\rapidjson\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\rcheevos\include;$(SolutionDir)dep\rapidjson\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="('$(BuildingForUWP)'!='true' And '$(Platform)'!='ARM64')">$(SolutionDir)dep\rainterface;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Platform)'=='x64'">$(SolutionDir)dep\xbyak\xbyak;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@@ -19,7 +19,7 @@
<ItemDefinitionGroup>
<Lib>
<AdditionalDependencies>$(RootBuildDir)rcheevos\rcheevos.lib;$(RootBuildDir)imgui\imgui.lib;$(RootBuildDir)stb\stb.lib;$(RootBuildDir)xxhash\xxhash.lib;$(RootBuildDir)zlib\zlib.lib;$(RootBuildDir)util\util.lib;$(RootBuildDir)common\common.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>$(RootBuildDir)tinyxml2\tinyxml2.lib;$(RootBuildDir)rcheevos\rcheevos.lib;$(RootBuildDir)imgui\imgui.lib;$(RootBuildDir)stb\stb.lib;$(RootBuildDir)xxhash\xxhash.lib;$(RootBuildDir)zlib\zlib.lib;$(RootBuildDir)util\util.lib;$(RootBuildDir)common\common.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies Condition="('$(BuildingForUWP)'!='true' And '$(Platform)'!='ARM64')">$(RootBuildDir)rainterface\rainterface.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies Condition="'$(Platform)'=='ARM64'">$(RootBuildDir)vixl\vixl.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Lib>
+6 -10
View File
@@ -9,7 +9,6 @@
<ClCompile Include="cdrom.cpp" />
<ClCompile Include="cdrom_async_reader.cpp" />
<ClCompile Include="cheats.cpp" />
<ClCompile Include="cheevos.cpp" />
<ClCompile Include="cpu_core.cpp" />
<ClCompile Include="cpu_disasm.cpp" />
<ClCompile Include="cpu_code_cache.cpp" />
@@ -33,6 +32,7 @@
</ClCompile>
<ClCompile Include="cpu_types.cpp" />
<ClCompile Include="digital_controller.cpp" />
<ClCompile Include="game_database.cpp" />
<ClCompile Include="gpu_backend.cpp" />
<ClCompile Include="gpu_commands.cpp" />
<ClCompile Include="gpu_hw_d3d11.cpp" />
@@ -47,18 +47,16 @@
<ClCompile Include="gpu.cpp" />
<ClCompile Include="gpu_hw.cpp" />
<ClCompile Include="gpu_hw_opengl.cpp" />
<ClCompile Include="host.cpp" />
<ClCompile Include="host_display.cpp" />
<ClCompile Include="host_interface.cpp" />
<ClCompile Include="host_interface_progress_callback.cpp" />
<ClCompile Include="imgui_fullscreen.cpp" />
<ClCompile Include="imgui_styles.cpp" />
<ClCompile Include="interrupt_controller.cpp" />
<ClCompile Include="libcrypt_game_codes.cpp" />
<ClCompile Include="mdec.cpp" />
<ClCompile Include="memory_card.cpp" />
<ClCompile Include="memory_card_image.cpp" />
<ClCompile Include="multitap.cpp" />
<ClCompile Include="namco_guncon.cpp" />
<ClCompile Include="guncon.cpp" />
<ClCompile Include="negcon.cpp" />
<ClCompile Include="pad.cpp" />
<ClCompile Include="controller.cpp" />
@@ -83,7 +81,7 @@
<ClInclude Include="cdrom.h" />
<ClInclude Include="cdrom_async_reader.h" />
<ClInclude Include="cheats.h" />
<ClInclude Include="cheevos.h" />
<ClInclude Include="achievements.h" />
<ClInclude Include="cpu_core.h" />
<ClInclude Include="cpu_core_private.h" />
<ClInclude Include="cpu_disasm.h" />
@@ -101,6 +99,7 @@
<ExcludedFromBuild Condition="'$(Platform)'=='Win32'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="digital_controller.h" />
<ClInclude Include="game_database.h" />
<ClInclude Include="gpu_backend.h" />
<ClInclude Include="gpu_hw_d3d11.h" />
<ClInclude Include="gpu_hw_d3d12.h" />
@@ -119,18 +118,15 @@
<ClInclude Include="gte_types.h" />
<ClInclude Include="host.h" />
<ClInclude Include="host_display.h" />
<ClInclude Include="host_interface.h" />
<ClInclude Include="host_interface_progress_callback.h" />
<ClInclude Include="host_settings.h" />
<ClInclude Include="imgui_fullscreen.h" />
<ClInclude Include="imgui_styles.h" />
<ClInclude Include="interrupt_controller.h" />
<ClInclude Include="libcrypt_game_codes.h" />
<ClInclude Include="mdec.h" />
<ClInclude Include="memory_card.h" />
<ClInclude Include="memory_card_image.h" />
<ClInclude Include="multitap.h" />
<ClInclude Include="namco_guncon.h" />
<ClInclude Include="guncon.h" />
<ClInclude Include="negcon.h" />
<ClInclude Include="pad.h" />
<ClInclude Include="controller.h" />
+6 -10
View File
@@ -10,7 +10,6 @@
<ClCompile Include="gpu.cpp" />
<ClCompile Include="gpu_hw_opengl.cpp" />
<ClCompile Include="gpu_hw.cpp" />
<ClCompile Include="host_interface.cpp" />
<ClCompile Include="interrupt_controller.cpp" />
<ClCompile Include="cdrom.cpp" />
<ClCompile Include="gte.cpp" />
@@ -40,7 +39,7 @@
<ClCompile Include="timing_event.cpp" />
<ClCompile Include="cdrom_async_reader.cpp" />
<ClCompile Include="psf_loader.cpp" />
<ClCompile Include="namco_guncon.cpp" />
<ClCompile Include="guncon.cpp" />
<ClCompile Include="playstation_mouse.cpp" />
<ClCompile Include="negcon.cpp" />
<ClCompile Include="gpu_hw_vulkan.cpp" />
@@ -58,9 +57,8 @@
<ClCompile Include="texture_replacements.cpp" />
<ClCompile Include="multitap.cpp" />
<ClCompile Include="gpu_hw_d3d12.cpp" />
<ClCompile Include="imgui_fullscreen.cpp" />
<ClCompile Include="imgui_styles.cpp" />
<ClCompile Include="cheevos.cpp" />
<ClCompile Include="host.cpp" />
<ClCompile Include="game_database.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="types.h" />
@@ -74,7 +72,6 @@
<ClInclude Include="gpu.h" />
<ClInclude Include="gpu_hw_opengl.h" />
<ClInclude Include="gpu_hw.h" />
<ClInclude Include="host_interface.h" />
<ClInclude Include="interrupt_controller.h" />
<ClInclude Include="cdrom.h" />
<ClInclude Include="gte.h" />
@@ -101,7 +98,7 @@
<ClInclude Include="timing_event.h" />
<ClInclude Include="cdrom_async_reader.h" />
<ClInclude Include="psf_loader.h" />
<ClInclude Include="namco_guncon.h" />
<ClInclude Include="guncon.h" />
<ClInclude Include="playstation_mouse.h" />
<ClInclude Include="negcon.h" />
<ClInclude Include="gpu_hw_vulkan.h" />
@@ -123,10 +120,9 @@
<ClInclude Include="multitap.h" />
<ClInclude Include="gpu_hw_d3d12.h" />
<ClInclude Include="gdb_protocol.h" />
<ClInclude Include="imgui_fullscreen.h" />
<ClInclude Include="imgui_styles.h" />
<ClInclude Include="cheevos.h" />
<ClInclude Include="host.h" />
<ClInclude Include="host_settings.h" />
<ClInclude Include="achievements.h" />
<ClInclude Include="game_database.h" />
</ItemGroup>
</Project>
+19 -23
View File
@@ -7,7 +7,7 @@
#include "cpu_disasm.h"
#include "cpu_recompiler_thunks.h"
#include "gte.h"
#include "host_interface.h"
#include "host.h"
#include "pgxp.h"
#include "settings.h"
#include "system.h"
@@ -1691,8 +1691,8 @@ bool AddBreakpoint(VirtualMemoryAddress address, bool auto_clear, bool enabled)
if (!auto_clear)
{
g_host_interface->ReportFormattedDebuggerMessage(
g_host_interface->TranslateString("DebuggerMessage", "Added breakpoint at 0x%08X."), address);
Host::ReportFormattedDebuggerMessage(Host::TranslateString("DebuggerMessage", "Added breakpoint at 0x%08X."),
address);
}
return true;
@@ -1718,8 +1718,8 @@ bool RemoveBreakpoint(VirtualMemoryAddress address)
if (it == s_breakpoints.end())
return false;
g_host_interface->ReportFormattedDebuggerMessage(
g_host_interface->TranslateString("DebuggerMessage", "Removed breakpoint at 0x%08X."), address);
Host::ReportFormattedDebuggerMessage(Host::TranslateString("DebuggerMessage", "Removed breakpoint at 0x%08X."),
address);
s_breakpoints.erase(it);
UpdateDebugDispatcherFlag();
@@ -1750,8 +1750,8 @@ bool AddStepOverBreakpoint()
if (!IsCallInstruction(inst))
{
g_host_interface->ReportFormattedDebuggerMessage(
g_host_interface->TranslateString("DebuggerMessage", "0x%08X is not a call instruction."), g_state.regs.pc);
Host::ReportFormattedDebuggerMessage(Host::TranslateString("DebuggerMessage", "0x%08X is not a call instruction."),
g_state.regs.pc);
return false;
}
@@ -1760,16 +1760,15 @@ bool AddStepOverBreakpoint()
if (IsBranchInstruction(inst))
{
g_host_interface->ReportFormattedDebuggerMessage(
g_host_interface->TranslateString("DebuggerMessage", "Can't step over double branch at 0x%08X"), g_state.regs.pc);
Host::ReportFormattedDebuggerMessage(
Host::TranslateString("DebuggerMessage", "Can't step over double branch at 0x%08X"), g_state.regs.pc);
return false;
}
// skip the delay slot
bp_pc += sizeof(Instruction);
g_host_interface->ReportFormattedDebuggerMessage(
g_host_interface->TranslateString("DebuggerMessage", "Stepping over to 0x%08X."), bp_pc);
Host::ReportFormattedDebuggerMessage(Host::TranslateString("DebuggerMessage", "Stepping over to 0x%08X."), bp_pc);
return AddBreakpoint(bp_pc, true);
}
@@ -1785,25 +1784,22 @@ bool AddStepOutBreakpoint(u32 max_instructions_to_search)
Instruction inst;
if (!SafeReadInstruction(ret_pc, &inst.bits))
{
g_host_interface->ReportFormattedDebuggerMessage(
g_host_interface->TranslateString("DebuggerMessage",
"Instruction read failed at %08X while searching for function end."),
Host::ReportFormattedDebuggerMessage(
Host::TranslateString("DebuggerMessage", "Instruction read failed at %08X while searching for function end."),
ret_pc);
return false;
}
if (IsReturnInstruction(inst))
{
g_host_interface->ReportFormattedDebuggerMessage(
g_host_interface->TranslateString("DebuggerMessage", "Stepping out to 0x%08X."), ret_pc);
Host::ReportFormattedDebuggerMessage(Host::TranslateString("DebuggerMessage", "Stepping out to 0x%08X."), ret_pc);
return AddBreakpoint(ret_pc, true);
}
}
g_host_interface->ReportFormattedDebuggerMessage(
g_host_interface->TranslateString("DebuggerMessage",
"No return instruction found after %u instructions for step-out at %08X."),
Host::ReportFormattedDebuggerMessage(
Host::TranslateString("DebuggerMessage", "No return instruction found after %u instructions for step-out at %08X."),
max_instructions_to_search, g_state.regs.pc);
return false;
@@ -1857,18 +1853,18 @@ ALWAYS_INLINE_RELEASE static bool BreakpointCheck()
}
else
{
g_host_interface->PauseSystem(true);
System::PauseSystem(true);
if (bp.auto_clear)
{
g_host_interface->ReportFormattedDebuggerMessage("Stopped execution at 0x%08X.", pc);
Host::ReportFormattedDebuggerMessage("Stopped execution at 0x%08X.", pc);
s_breakpoints.erase(s_breakpoints.begin() + i);
count--;
UpdateDebugDispatcherFlag();
}
else
{
g_host_interface->ReportFormattedDebuggerMessage("Hit breakpoint %u at 0x%08X.", bp.number, pc);
Host::ReportFormattedDebuggerMessage("Hit breakpoint %u at 0x%08X.", bp.number, pc);
i++;
}
}
@@ -1979,7 +1975,7 @@ void SingleStep()
{
s_single_step = true;
ExecuteDebug();
g_host_interface->ReportFormattedDebuggerMessage("Stepped to 0x%08X.", g_state.regs.pc);
Host::ReportFormattedDebuggerMessage("Stepped to 0x%08X.", g_state.regs.pc);
}
namespace CodeCache {
+52 -97
View File
@@ -1,10 +1,10 @@
#include "digital_controller.h"
#include "common/assert.h"
#include "host_interface.h"
#include "host.h"
#include "system.h"
#include "util/state_wrapper.h"
DigitalController::DigitalController() = default;
DigitalController::DigitalController(u32 index) : Controller(index) {}
DigitalController::~DigitalController() = default;
@@ -13,16 +13,6 @@ ControllerType DigitalController::GetType() const
return ControllerType::DigitalController;
}
std::optional<s32> DigitalController::GetAxisCodeByName(std::string_view axis_name) const
{
return StaticGetAxisCodeByName(axis_name);
}
std::optional<s32> DigitalController::GetButtonCodeByName(std::string_view button_name) const
{
return StaticGetButtonCodeByName(button_name);
}
void DigitalController::Reset()
{
m_transfer_state = TransferState::Idle;
@@ -42,18 +32,25 @@ bool DigitalController::DoState(StateWrapper& sw, bool apply_input_state)
return true;
}
bool DigitalController::GetButtonState(s32 button_code) const
float DigitalController::GetBindState(u32 index) const
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return false;
const u16 bit = u16(1) << static_cast<u8>(button_code);
return ((m_button_state & bit) == 0);
if (index < static_cast<u32>(Button::Count))
{
return static_cast<float>(((m_button_state >> index) & 1u) ^ 1u);
}
else
{
return 0.0f;
}
}
void DigitalController::SetButtonState(Button button, bool pressed)
void DigitalController::SetBindState(u32 index, float value)
{
const u16 bit = u16(1) << static_cast<u8>(button);
if (index >= static_cast<u32>(Button::Count))
return;
const bool pressed = (value >= 0.5f);
const u16 bit = u16(1) << static_cast<u8>(index);
if (pressed)
{
if (m_button_state & bit)
@@ -70,14 +67,6 @@ void DigitalController::SetButtonState(Button button, bool pressed)
}
}
void DigitalController::SetButtonState(s32 button_code, bool pressed)
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return;
SetButtonState(static_cast<Button>(button_code), pressed);
}
u32 DigitalController::GetButtonStateBits() const
{
return m_button_state ^ 0xFFFF;
@@ -146,87 +135,53 @@ bool DigitalController::Transfer(const u8 data_in, u8* data_out)
}
}
std::unique_ptr<DigitalController> DigitalController::Create()
std::unique_ptr<DigitalController> DigitalController::Create(u32 index)
{
return std::make_unique<DigitalController>();
return std::make_unique<DigitalController>(index);
}
std::optional<s32> DigitalController::StaticGetAxisCodeByName(std::string_view button_name)
{
return std::nullopt;
}
std::optional<s32> DigitalController::StaticGetButtonCodeByName(std::string_view button_name)
{
#define BUTTON(name) \
if (button_name == #name) \
static const Controller::ControllerBindingInfo s_binding_info[] = {
#define BUTTON(name, display_name, button, genb) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Button::name))); \
name, display_name, static_cast<u32>(button), Controller::ControllerBindingType::Button, genb \
}
BUTTON(Select);
BUTTON(L3);
BUTTON(R3);
BUTTON(Start);
BUTTON(Up);
BUTTON(Right);
BUTTON(Down);
BUTTON(Left);
BUTTON(L2);
BUTTON(R2);
BUTTON(L1);
BUTTON(R1);
BUTTON(Triangle);
BUTTON(Circle);
BUTTON(Cross);
BUTTON(Square);
return std::nullopt;
BUTTON("Up", "D-Pad Up", DigitalController::Button::Up, GenericInputBinding::DPadUp),
BUTTON("Right", "D-Pad Right", DigitalController::Button::Right, GenericInputBinding::DPadRight),
BUTTON("Down", "D-Pad Down", DigitalController::Button::Down, GenericInputBinding::DPadDown),
BUTTON("Left", "D-Pad Left", DigitalController::Button::Left, GenericInputBinding::DPadLeft),
BUTTON("Triangle", "Triangle", DigitalController::Button::Triangle, GenericInputBinding::Triangle),
BUTTON("Circle", "Circle", DigitalController::Button::Circle, GenericInputBinding::Circle),
BUTTON("Cross", "Cross", DigitalController::Button::Cross, GenericInputBinding::Cross),
BUTTON("Square", "Square", DigitalController::Button::Square, GenericInputBinding::Square),
BUTTON("Select", "Select", DigitalController::Button::Select, GenericInputBinding::Select),
BUTTON("Start", "Start", DigitalController::Button::Start, GenericInputBinding::Start),
BUTTON("L1", "L1", DigitalController::Button::L1, GenericInputBinding::L1),
BUTTON("R1", "R1", DigitalController::Button::R1, GenericInputBinding::R1),
BUTTON("L2", "L2", DigitalController::Button::L2, GenericInputBinding::L2),
BUTTON("R2", "R2", DigitalController::Button::R2, GenericInputBinding::R2),
#undef BUTTON
}
};
Controller::AxisList DigitalController::StaticGetAxisNames()
{
return {};
}
static const SettingInfo s_settings[] = {
{SettingInfo::Type::Boolean, "ForcePopnControllerMode",
TRANSLATABLE("DigitalController", "Force Pop'n Controller Mode"),
TRANSLATABLE("DigitalController", "Forces the Digital Controller to act as a Pop'n Controller."), "false"}};
Controller::ButtonList DigitalController::StaticGetButtonNames()
{
return {{TRANSLATABLE("DigitalController", "Up"), static_cast<s32>(Button::Up)},
{TRANSLATABLE("DigitalController", "Down"), static_cast<s32>(Button::Down)},
{TRANSLATABLE("DigitalController", "Left"), static_cast<s32>(Button::Left)},
{TRANSLATABLE("DigitalController", "Right"), static_cast<s32>(Button::Right)},
{TRANSLATABLE("DigitalController", "Select"), static_cast<s32>(Button::Select)},
{TRANSLATABLE("DigitalController", "Start"), static_cast<s32>(Button::Start)},
{TRANSLATABLE("DigitalController", "Triangle"), static_cast<s32>(Button::Triangle)},
{TRANSLATABLE("DigitalController", "Cross"), static_cast<s32>(Button::Cross)},
{TRANSLATABLE("DigitalController", "Circle"), static_cast<s32>(Button::Circle)},
{TRANSLATABLE("DigitalController", "Square"), static_cast<s32>(Button::Square)},
{TRANSLATABLE("DigitalController", "L1"), static_cast<s32>(Button::L1)},
{TRANSLATABLE("DigitalController", "L2"), static_cast<s32>(Button::L2)},
{TRANSLATABLE("DigitalController", "R1"), static_cast<s32>(Button::R1)},
{TRANSLATABLE("DigitalController", "R2"), static_cast<s32>(Button::R2)}};
}
const Controller::ControllerInfo DigitalController::INFO = {ControllerType::DigitalController,
"DigitalController",
TRANSLATABLE("ControllerType", "Digital Controller"),
s_binding_info,
countof(s_binding_info),
s_settings,
countof(s_settings),
Controller::VibrationCapabilities::NoVibration};
u32 DigitalController::StaticGetVibrationMotorCount()
void DigitalController::LoadSettings(SettingsInterface& si, const char* section)
{
return 0;
}
Controller::SettingList DigitalController::StaticGetSettings()
{
static constexpr std::array<SettingInfo, 1> settings = {
{{SettingInfo::Type::Boolean, "ForcePopnControllerMode",
TRANSLATABLE("DigitalController", "Force Pop'n Controller Mode"),
TRANSLATABLE("DigitalController", "Forces the Digital Controller to act as a Pop'n Controller."), "false"}}};
return SettingList(settings.begin(), settings.end());
}
void DigitalController::LoadSettings(const char* section)
{
Controller::LoadSettings(section);
m_popn_controller_mode = g_host_interface->GetBoolSettingValue(section, "ForcePopnControllerMode", false);
Controller::LoadSettings(si, section);
m_popn_controller_mode = si.GetBoolValue(section, "ForcePopnControllerMode", false);
}
u8 DigitalController::GetButtonsLSBMask() const
+7 -15
View File
@@ -28,34 +28,26 @@ public:
Count
};
DigitalController();
static const Controller::ControllerInfo INFO;
DigitalController(u32 index);
~DigitalController() override;
static std::unique_ptr<DigitalController> Create();
static std::optional<s32> StaticGetAxisCodeByName(std::string_view button_name);
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
static AxisList StaticGetAxisNames();
static ButtonList StaticGetButtonNames();
static u32 StaticGetVibrationMotorCount();
static SettingList StaticGetSettings();
static std::unique_ptr<DigitalController> Create(u32 index);
ControllerType GetType() const override;
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
std::optional<s32> GetButtonCodeByName(std::string_view button_name) const override;
void Reset() override;
bool DoState(StateWrapper& sw, bool apply_input_state) override;
bool GetButtonState(s32 button_code) const override;
void SetButtonState(s32 button_code, bool pressed) override;
float GetBindState(u32 index) const override;
void SetBindState(u32 index, float value) override;
u32 GetButtonStateBits() const override;
void ResetTransferState() override;
bool Transfer(const u8 data_in, u8* data_out) override;
void SetButtonState(Button button, bool pressed);
void LoadSettings(const char* section) override;
void LoadSettings(SettingsInterface& si, const char* section) override;
private:
enum class TransferState : u8
+2 -1
View File
@@ -6,6 +6,7 @@
#include "cpu_code_cache.h"
#include "cpu_core.h"
#include "gpu.h"
#include "host.h"
#include "imgui.h"
#include "interrupt_controller.h"
#include "mdec.h"
@@ -626,7 +627,7 @@ void DMA::DrawDebugStateWindow()
{"MDECin", "MDECout", "GPU", "CDROM", "SPU", "PIO", "OTC"}};
static constexpr std::array<const char*, 4> sync_mode_names = {{"Manual", "Request", "LinkedList", "Reserved"}};
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
const float framebuffer_scale = Host::GetOSDScale();
ImGui::SetNextWindowSize(ImVec2(850.0f * framebuffer_scale, 250.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("DMA State", nullptr))
File diff suppressed because it is too large Load Diff
+122
View File
@@ -0,0 +1,122 @@
#pragma once
#include "core/types.h"
#include "util/cd_image_hasher.h"
#include <bitset>
#include <map>
#include <string>
#include <string_view>
#include <vector>
class CDImage;
struct Settings;
namespace GameDatabase {
enum class CompatibilityRating : u32
{
Unknown = 0,
DoesntBoot = 1,
CrashesInIntro = 2,
CrashesInGame = 3,
GraphicalAudioIssues = 4,
NoIssues = 5,
Count,
};
enum class Trait : u32
{
ForceInterpreter,
ForceSoftwareRenderer,
ForceSoftwareRendererForReadbacks,
ForceInterlacing,
DisableTrueColor,
DisableUpscaling,
DisableScaledDithering,
DisableForceNTSCTimings,
DisableWidescreen,
DisablePGXP,
DisablePGXPCulling,
DisablePGXPTextureCorrection,
DisablePGXPDepthBuffer,
ForcePGXPVertexCache,
ForcePGXPCPUMode,
ForceRecompilerMemoryExceptions,
ForceRecompilerICache,
ForceRecompilerLUTFastmem,
Count
};
struct Entry
{
// TODO: Make string_view.
std::string serial;
std::string title;
std::string genre;
std::string developer;
std::string publisher;
u64 release_date;
u8 min_players;
u8 max_players;
u8 min_blocks;
u8 max_blocks;
u32 supported_controllers;
CompatibilityRating compatibility;
std::bitset<static_cast<int>(Trait::Count)> traits{};
std::optional<s16> display_active_start_offset;
std::optional<s16> display_active_end_offset;
std::optional<s8> display_line_start_offset;
std::optional<s8> display_line_end_offset;
std::optional<u32> dma_max_slice_ticks;
std::optional<u32> dma_halt_ticks;
std::optional<u32> gpu_fifo_size;
std::optional<u32> gpu_max_run_ahead;
std::optional<float> gpu_pgxp_tolerance;
std::optional<float> gpu_pgxp_depth_threshold;
ALWAYS_INLINE bool HasTrait(Trait trait) const { return traits[static_cast<int>(trait)]; }
void ApplySettings(Settings& settings, bool display_osd_messages) const;
};
void EnsureLoaded();
void Unload();
const Entry* GetEntryForDisc(CDImage* image);
const Entry* GetEntryForSerial(const std::string_view& serial);
const Entry* GetEntryForCode(const std::string_view& code);
std::string GetSerialForDisc(CDImage* image);
std::string GetSerialForPath(const char* path);
const char* GetTraitName(Trait trait);
const char* GetTraitDisplayName(Trait trait);
const char* GetCompatibilityRatingName(CompatibilityRating rating);
const char* GetCompatibilityRatingDisplayName(CompatibilityRating rating);
/// Map of track hashes for image verification
struct TrackData
{
TrackData(std::vector<std::string> codes, std::string revisionString, uint32_t revision)
: codes(std::move(codes)), revisionString(revisionString), revision(revision)
{
}
friend bool operator==(const TrackData& left, const TrackData& right)
{
// 'revisionString' is deliberately ignored in comparisons as it's redundant with comparing 'revision'! Do not
// change!
return left.codes == right.codes && left.revision == right.revision;
}
std::vector<std::string> codes;
std::string revisionString;
uint32_t revision;
};
using TrackHashesMap = std::multimap<CDImageHasher::Hash, TrackData>;
const TrackHashesMap& GetTrackHashesMap();
void EnsureTrackHashesMapLoaded();
} // namespace GameDatabase
+1 -1
View File
@@ -4,7 +4,7 @@
#include "common/log.h"
#include "common/string_util.h"
#include "cpu_core.h"
#include "frontend-common/common_host_interface.h"
#include "frontend-common/common_host.h"
#include "system.h"
#include <functional>
#include <iomanip>
+20 -7
View File
@@ -4,8 +4,8 @@
#include "common/log.h"
#include "common/string_util.h"
#include "dma.h"
#include "host.h"
#include "host_display.h"
#include "host_interface.h"
#include "imgui.h"
#include "interrupt_controller.h"
#include "settings.h"
@@ -24,9 +24,8 @@ GPU::GPU() = default;
GPU::~GPU() = default;
bool GPU::Initialize(HostDisplay* host_display)
bool GPU::Initialize()
{
m_host_display = host_display;
m_force_progressive_scan = g_settings.gpu_disable_interlacing;
m_force_ntsc_timings = g_settings.gpu_force_ntsc_timings;
m_crtc_tick_event = TimingEvents::CreateTimingEvent(
@@ -41,6 +40,16 @@ bool GPU::Initialize(HostDisplay* host_display)
m_max_run_ahead = g_settings.gpu_max_run_ahead;
m_console_is_pal = System::IsPALRegion();
UpdateCRTCConfig();
g_host_display->SetDisplayLinearFiltering(g_settings.display_linear_filtering);
g_host_display->SetDisplayIntegerScaling(g_settings.display_integer_scaling);
g_host_display->SetDisplayStretch(g_settings.display_stretch);
if (g_settings.display_post_processing &&
!g_host_display->SetPostProcessingChain(g_settings.display_post_process_chain))
{
Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Failed to load post processing shader chain."), 20.0f);
}
return true;
}
@@ -59,6 +68,10 @@ void GPU::UpdateSettings()
// Crop mode calls this, so recalculate the display area
UpdateCRTCDisplayParameters();
g_host_display->SetDisplayLinearFiltering(g_settings.display_linear_filtering);
g_host_display->SetDisplayIntegerScaling(g_settings.display_integer_scaling);
g_host_display->SetDisplayStretch(g_settings.display_stretch);
}
bool GPU::IsHardwareRenderer()
@@ -962,9 +975,9 @@ void GPU::UpdateCommandTickEvent()
bool GPU::ConvertScreenCoordinatesToBeamTicksAndLines(s32 window_x, s32 window_y, float x_scale, u32* out_tick,
u32* out_line) const
{
auto [display_x, display_y] = m_host_display->ConvertWindowCoordinatesToDisplayCoordinates(
window_x, window_y, m_host_display->GetWindowWidth(), m_host_display->GetWindowHeight(),
m_host_display->GetDisplayTopMargin());
auto [display_x, display_y] = g_host_display->ConvertWindowCoordinatesToDisplayCoordinates(
window_x, window_y, g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight(),
g_host_display->GetDisplayTopMargin());
if (x_scale != 1.0f)
{
@@ -1551,7 +1564,7 @@ bool GPU::DumpVRAMToFile(const char* filename, u32 width, u32 height, u32 stride
void GPU::DrawDebugStateWindow()
{
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
const float framebuffer_scale = Host::GetOSDScale();
ImGui::SetNextWindowSize(ImVec2(450.0f * framebuffer_scale, 550.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("GPU", nullptr))
+1 -3
View File
@@ -75,7 +75,7 @@ public:
virtual GPURenderer GetRendererType() const = 0;
virtual bool Initialize(HostDisplay* host_display);
virtual bool Initialize();
virtual void Reset(bool clear_vram);
virtual bool DoState(StateWrapper& sw, HostDisplayTexture** save_to_texture, bool update_display);
@@ -319,8 +319,6 @@ protected:
AddCommandTicks(std::max(width, height));
}
HostDisplay* m_host_display = nullptr;
std::unique_ptr<TimingEvent> m_crtc_tick_event;
std::unique_ptr<TimingEvent> m_command_tick_event;
+2 -2
View File
@@ -173,7 +173,7 @@ void GPUBackend::StartGPUThread()
{
m_gpu_loop_done.store(false);
m_use_gpu_thread = true;
m_gpu_thread = std::thread(&GPUBackend::RunGPULoop, this);
m_gpu_thread.Start([this]() { RunGPULoop(); });
Log_InfoPrint("GPU thread started.");
}
@@ -184,7 +184,7 @@ void GPUBackend::StopGPUThread()
m_gpu_loop_done.store(true);
WakeGPUThread();
m_gpu_thread.join();
m_gpu_thread.Join();
m_use_gpu_thread = false;
Log_InfoPrint("GPU thread stopped.");
}
+3 -1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "common/event.h"
#include "common/heap_array.h"
#include "common/threading.h"
#include "gpu_types.h"
#include <atomic>
#include <condition_variable>
@@ -20,6 +21,7 @@ public:
virtual ~GPUBackend();
ALWAYS_INLINE u16* GetVRAM() const { return m_vram_ptr; }
ALWAYS_INLINE const Threading::Thread* GetThread() const { return m_use_gpu_thread ? &m_gpu_thread : nullptr; }
virtual bool Initialize(bool force_thread);
virtual void UpdateSettings();
@@ -67,7 +69,7 @@ protected:
Common::Event m_sync_event;
std::atomic_bool m_gpu_thread_sleeping{false};
std::atomic_bool m_gpu_loop_done{false};
std::thread m_gpu_thread;
Threading::Thread m_gpu_thread;
bool m_use_gpu_thread = false;
std::mutex m_sync_mutex;
+27 -31
View File
@@ -4,6 +4,7 @@
#include "common/log.h"
#include "cpu_core.h"
#include "gpu_sw_backend.h"
#include "host.h"
#include "imgui.h"
#include "pgxp.h"
#include "settings.h"
@@ -43,14 +44,14 @@ GPU_HW::~GPU_HW()
}
}
bool GPU_HW::Initialize(HostDisplay* host_display)
bool GPU_HW::Initialize()
{
if (!GPU::Initialize(host_display))
if (!GPU::Initialize())
return false;
m_resolution_scale = CalculateResolutionScale();
m_multisamples = std::min(g_settings.gpu_multisamples, m_max_multisamples);
m_render_api = host_display->GetRenderAPI();
m_render_api = g_host_display->GetRenderAPI();
m_per_sample_shading = g_settings.gpu_per_sample_shading && m_supports_per_sample_shading;
m_true_color = g_settings.gpu_true_color;
m_scaled_dithering = g_settings.gpu_scaled_dithering;
@@ -61,29 +62,26 @@ bool GPU_HW::Initialize(HostDisplay* host_display)
if (m_multisamples != g_settings.gpu_multisamples)
{
g_host_interface->AddFormattedOSDMessage(
20.0f, g_host_interface->TranslateString("OSDMessage", "%ux MSAA is not supported, using %ux instead."),
g_settings.gpu_multisamples, m_multisamples);
Host::AddFormattedOSDMessage(20.0f,
Host::TranslateString("OSDMessage", "%ux MSAA is not supported, using %ux instead."),
g_settings.gpu_multisamples, m_multisamples);
}
if (!m_per_sample_shading && g_settings.gpu_per_sample_shading)
{
g_host_interface->AddOSDMessage(
g_host_interface->TranslateStdString("OSDMessage", "SSAA is not supported, using MSAA instead."), 20.0f);
Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "SSAA is not supported, using MSAA instead."), 20.0f);
}
if (!m_supports_dual_source_blend && TextureFilterRequiresDualSourceBlend(m_texture_filtering))
{
g_host_interface->AddFormattedOSDMessage(
20.0f,
g_host_interface->TranslateString("OSDMessage",
"Texture filter '%s' is not supported with the current renderer."),
Host::AddFormattedOSDMessage(
20.0f, Host::TranslateString("OSDMessage", "Texture filter '%s' is not supported with the current renderer."),
Settings::GetTextureFilterDisplayName(m_texture_filtering));
m_texture_filtering = GPUTextureFilter::Nearest;
}
if (!m_supports_adaptive_downsampling && g_settings.gpu_resolution_scale > 1 &&
g_settings.gpu_downsample_mode == GPUDownsampleMode::Adaptive)
{
g_host_interface->AddOSDMessage(
g_host_interface->TranslateStdString(
Host::AddOSDMessage(
Host::TranslateStdString(
"OSDMessage", "Adaptive downsampling is not supported with the current renderer, using box filter instead."),
20.0f);
}
@@ -149,27 +147,26 @@ void GPU_HW::UpdateHWSettings(bool* framebuffer_changed, bool* shaders_changed)
if (m_resolution_scale != resolution_scale)
{
g_host_interface->AddKeyedFormattedOSDMessage(
Host::AddKeyedFormattedOSDMessage(
"ResolutionScale", 10.0f,
g_host_interface->TranslateString("OSDMessage", "Resolution scale set to %ux (display %ux%u, VRAM %ux%u)"),
resolution_scale, m_crtc_state.display_vram_width * resolution_scale,
resolution_scale * m_crtc_state.display_vram_height, VRAM_WIDTH * resolution_scale,
VRAM_HEIGHT * resolution_scale);
Host::TranslateString("OSDMessage", "Resolution scale set to %ux (display %ux%u, VRAM %ux%u)"), resolution_scale,
m_crtc_state.display_vram_width * resolution_scale, resolution_scale * m_crtc_state.display_vram_height,
VRAM_WIDTH * resolution_scale, VRAM_HEIGHT * resolution_scale);
}
if (m_multisamples != multisamples || m_per_sample_shading != per_sample_shading)
{
if (per_sample_shading)
{
g_host_interface->AddKeyedFormattedOSDMessage(
"Multisampling", 10.0f,
g_host_interface->TranslateString("OSDMessage", "Multisample anti-aliasing set to %ux (SSAA)."), multisamples);
Host::AddKeyedFormattedOSDMessage(
"Multisampling", 10.0f, Host::TranslateString("OSDMessage", "Multisample anti-aliasing set to %ux (SSAA)."),
multisamples);
}
else
{
g_host_interface->AddKeyedFormattedOSDMessage(
"Multisampling", 10.0f,
g_host_interface->TranslateString("OSDMessage", "Multisample anti-aliasing set to %ux."), multisamples);
Host::AddKeyedFormattedOSDMessage("Multisampling", 10.0f,
Host::TranslateString("OSDMessage", "Multisample anti-aliasing set to %ux."),
multisamples);
}
}
@@ -215,7 +212,7 @@ u32 GPU_HW::CalculateResolutionScale() const
(m_console_is_pal ? (PAL_VERTICAL_ACTIVE_END - PAL_VERTICAL_ACTIVE_START) :
(NTSC_VERTICAL_ACTIVE_END - NTSC_VERTICAL_ACTIVE_START));
const s32 preferred_scale =
static_cast<s32>(std::ceil(static_cast<float>(m_host_display->GetWindowHeight()) / height));
static_cast<s32>(std::ceil(static_cast<float>(g_host_display->GetWindowHeight()) / height));
Log_InfoPrintf("Height = %d, preferred scale = %d", height, preferred_scale);
scale = static_cast<u32>(std::clamp<s32>(preferred_scale, 1, m_max_resolution_scale));
@@ -229,10 +226,9 @@ u32 GPU_HW::CalculateResolutionScale() const
if (g_settings.gpu_resolution_scale != 0)
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
10.0f,
g_host_interface->TranslateString("OSDMessage",
"Resolution scale %ux not supported for adaptive smoothing, using %ux."),
Host::TranslateString("OSDMessage", "Resolution scale %ux not supported for adaptive smoothing, using %ux."),
scale, new_scale);
}
@@ -1410,7 +1406,7 @@ void GPU_HW::DrawRendererStats(bool is_idle_frame)
const auto& stats = m_last_renderer_stats;
ImGui::Columns(2);
ImGui::SetColumnWidth(0, 200.0f * ImGui::GetIO().DisplayFramebufferScale.x);
ImGui::SetColumnWidth(0, 200.0f * Host::GetOSDScale());
ImGui::TextUnformatted("Resolution Scale:");
ImGui::NextColumn();
@@ -1487,7 +1483,7 @@ void GPU_HW::ShaderCompileProgressTracker::Increment()
const u64 tv = Common::Timer::GetCurrentValue();
if ((tv - m_start_time) >= m_min_time && (tv - m_last_update_time) >= m_update_interval)
{
g_host_interface->DisplayLoadingScreen(m_title.c_str(), 0, static_cast<int>(m_total), static_cast<int>(m_progress));
Host::DisplayLoadingScreen(m_title.c_str(), 0, static_cast<int>(m_total), static_cast<int>(m_progress));
m_last_update_time = tv;
}
}
+1 -1
View File
@@ -33,7 +33,7 @@ public:
GPU_HW();
virtual ~GPU_HW();
virtual bool Initialize(HostDisplay* host_display) override;
virtual bool Initialize() override;
virtual void Reset(bool clear_vram) override;
virtual bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display) override;
+24 -22
View File
@@ -6,7 +6,6 @@
#include "gpu_hw_shadergen.h"
#include "gpu_sw_backend.h"
#include "host_display.h"
#include "host_interface.h"
#include "shader_cache_version.h"
#include "system.h"
#include "util/state_wrapper.h"
@@ -16,8 +15,11 @@ GPU_HW_D3D11::GPU_HW_D3D11() = default;
GPU_HW_D3D11::~GPU_HW_D3D11()
{
if (m_host_display)
m_host_display->ClearDisplayTexture();
if (g_host_display)
{
g_host_display->ClearDisplayTexture();
ResetGraphicsAPIState();
}
if (m_context)
m_context->ClearState();
@@ -31,22 +33,22 @@ GPURenderer GPU_HW_D3D11::GetRendererType() const
return GPURenderer::HardwareD3D11;
}
bool GPU_HW_D3D11::Initialize(HostDisplay* host_display)
bool GPU_HW_D3D11::Initialize()
{
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::D3D11)
if (!Host::AcquireHostDisplay(HostDisplay::RenderAPI::D3D11))
{
Log_ErrorPrintf("Host render API is incompatible");
return false;
}
m_device = static_cast<ID3D11Device*>(host_display->GetRenderDevice());
m_context = static_cast<ID3D11DeviceContext*>(host_display->GetRenderContext());
m_device = static_cast<ID3D11Device*>(g_host_display->GetRenderDevice());
m_context = static_cast<ID3D11DeviceContext*>(g_host_display->GetRenderContext());
if (!m_device || !m_context)
return false;
SetCapabilities();
if (!GPU_HW::Initialize(host_display))
if (!GPU_HW::Initialize())
return false;
if (!CreateFramebuffer())
@@ -122,7 +124,7 @@ bool GPU_HW_D3D11::DoState(StateWrapper& sw, HostDisplayTexture** host_texture,
{
delete tex;
tex = m_host_display
tex = g_host_display
->CreateTexture(m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 1, 1,
m_vram_texture.GetSamples(), HostDisplayPixelFormat::RGBA8, nullptr, 0, false)
.release();
@@ -178,7 +180,7 @@ void GPU_HW_D3D11::UpdateSettings()
RestoreGraphicsAPIState();
ReadVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT);
ResetGraphicsAPIState();
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
CreateFramebuffer();
}
@@ -501,10 +503,10 @@ void GPU_HW_D3D11::DestroyStateObjects()
bool GPU_HW_D3D11::CompileShaders()
{
D3D11::ShaderCache shader_cache;
shader_cache.Open(g_host_interface->GetShaderCacheBasePath(), m_device->GetFeatureLevel(), SHADER_CACHE_VERSION,
shader_cache.Open(EmuFolders::Cache, m_device->GetFeatureLevel(), SHADER_CACHE_VERSION,
g_settings.gpu_use_debug_device);
GPU_HW_ShaderGen shadergen(m_host_display->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading,
GPU_HW_ShaderGen shadergen(g_host_display->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading,
m_true_color, m_scaled_dithering, m_texture_filtering, m_using_uv_limits,
m_pgxp_depth_buffer, m_supports_dual_source_blend);
@@ -826,7 +828,7 @@ void GPU_HW_D3D11::ClearDisplay()
{
GPU_HW::ClearDisplay();
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
static constexpr std::array<float, 4> clear_color = {0.0f, 0.0f, 0.0f, 1.0f};
m_context->ClearRenderTargetView(m_display_texture.GetD3DRTV(), clear_color.data());
@@ -841,23 +843,23 @@ void GPU_HW_D3D11::UpdateDisplay()
if (IsUsingMultisampling())
{
UpdateVRAMReadTexture();
m_host_display->SetDisplayTexture(m_vram_read_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(m_vram_read_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
m_vram_read_texture.GetWidth(), m_vram_read_texture.GetHeight(), 0, 0,
m_vram_read_texture.GetWidth(), m_vram_read_texture.GetHeight());
}
else
{
m_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 0, 0,
m_vram_texture.GetWidth(), m_vram_texture.GetHeight());
}
m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
g_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
}
else
{
m_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
g_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
m_crtc_state.display_origin_left, m_crtc_state.display_origin_top,
m_crtc_state.display_vram_width, m_crtc_state.display_vram_height,
GetDisplayAspectRatio());
@@ -875,7 +877,7 @@ void GPU_HW_D3D11::UpdateDisplay()
if (IsDisplayDisabled())
{
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
}
else if (!m_GPUSTAT.display_area_color_depth_24 && interlaced == InterlacedRenderMode::None &&
!IsUsingMultisampling() && (scaled_vram_offset_x + scaled_display_width) <= m_vram_texture.GetWidth() &&
@@ -889,7 +891,7 @@ void GPU_HW_D3D11::UpdateDisplay()
}
else
{
m_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), scaled_vram_offset_x,
scaled_vram_offset_y, scaled_display_width, scaled_display_height);
}
@@ -921,7 +923,7 @@ void GPU_HW_D3D11::UpdateDisplay()
}
else
{
m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
m_display_texture.GetWidth(), m_display_texture.GetHeight(), 0, 0,
scaled_display_width, scaled_display_height);
}
@@ -1202,7 +1204,7 @@ void GPU_HW_D3D11::DownsampleFramebufferAdaptive(D3D11::Texture& source, u32 lef
RestoreGraphicsAPIState();
m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
m_display_texture.GetWidth(), m_display_texture.GetHeight(), left, top, width,
height);
}
@@ -1227,7 +1229,7 @@ void GPU_HW_D3D11::DownsampleFramebufferBoxFilter(D3D11::Texture& source, u32 le
RestoreGraphicsAPIState();
m_host_display->SetDisplayTexture(m_downsample_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(m_downsample_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
m_downsample_texture.GetWidth(), m_downsample_texture.GetHeight(), ds_left, ds_top,
ds_width, ds_height);
}
+1 -1
View File
@@ -22,7 +22,7 @@ public:
GPURenderer GetRendererType() const override;
bool Initialize(HostDisplay* host_display) override;
bool Initialize() override;
void Reset(bool clear_vram) override;
bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display) override;
+16 -18
View File
@@ -11,7 +11,6 @@
#include "common/timer.h"
#include "gpu_hw_shadergen.h"
#include "host_display.h"
#include "host_interface.h"
#include "system.h"
Log_SetChannel(GPU_HW_D3D12);
@@ -19,9 +18,9 @@ GPU_HW_D3D12::GPU_HW_D3D12() = default;
GPU_HW_D3D12::~GPU_HW_D3D12()
{
if (m_host_display)
if (g_host_display)
{
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
ResetGraphicsAPIState();
}
@@ -33,9 +32,9 @@ GPURenderer GPU_HW_D3D12::GetRendererType() const
return GPURenderer::HardwareD3D12;
}
bool GPU_HW_D3D12::Initialize(HostDisplay* host_display)
bool GPU_HW_D3D12::Initialize()
{
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::D3D12)
if (!Host::AcquireHostDisplay(HostDisplay::RenderAPI::D3D12))
{
Log_ErrorPrintf("Host render API is incompatible");
return false;
@@ -43,7 +42,7 @@ bool GPU_HW_D3D12::Initialize(HostDisplay* host_display)
SetCapabilities();
if (!GPU_HW::Initialize(host_display))
if (!GPU_HW::Initialize())
return false;
if (!CreateRootSignatures())
@@ -144,7 +143,7 @@ void GPU_HW_D3D12::UpdateSettings()
}
// Everything should be finished executing before recreating resources.
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
g_d3d12_context->ExecuteCommandList(true);
if (framebuffer_changed)
@@ -413,10 +412,9 @@ bool GPU_HW_D3D12::CreateTextureBuffer()
bool GPU_HW_D3D12::CompilePipelines()
{
D3D12::ShaderCache shader_cache;
shader_cache.Open(g_host_interface->GetShaderCacheBasePath(), g_d3d12_context->GetFeatureLevel(),
g_settings.gpu_use_debug_device);
shader_cache.Open(EmuFolders::Cache, g_d3d12_context->GetFeatureLevel(), g_settings.gpu_use_debug_device);
GPU_HW_ShaderGen shadergen(m_host_display->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading,
GPU_HW_ShaderGen shadergen(g_host_display->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading,
m_true_color, m_scaled_dithering, m_texture_filtering, m_using_uv_limits,
m_pgxp_depth_buffer, m_supports_dual_source_blend);
@@ -852,7 +850,7 @@ void GPU_HW_D3D12::ClearDisplay()
{
GPU_HW::ClearDisplay();
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
static constexpr float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
m_display_texture.TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
@@ -869,23 +867,23 @@ void GPU_HW_D3D12::UpdateDisplay()
if (IsUsingMultisampling())
{
UpdateVRAMReadTexture();
m_host_display->SetDisplayTexture(&m_vram_read_texture, HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(&m_vram_read_texture, HostDisplayPixelFormat::RGBA8,
m_vram_read_texture.GetWidth(), m_vram_read_texture.GetHeight(), 0, 0,
m_vram_read_texture.GetWidth(), m_vram_read_texture.GetHeight());
}
else
{
m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
m_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
g_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
m_vram_texture.GetHeight(), 0, 0, m_vram_texture.GetWidth(),
m_vram_texture.GetHeight());
}
m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
g_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
}
else
{
m_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
g_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
m_crtc_state.display_origin_left, m_crtc_state.display_origin_top,
m_crtc_state.display_vram_width, m_crtc_state.display_vram_height,
GetDisplayAspectRatio());
@@ -903,14 +901,14 @@ void GPU_HW_D3D12::UpdateDisplay()
if (IsDisplayDisabled())
{
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
}
else if (!m_GPUSTAT.display_area_color_depth_24 && interlaced == InterlacedRenderMode::None &&
!IsUsingMultisampling() && (scaled_vram_offset_x + scaled_display_width) <= m_vram_texture.GetWidth() &&
(scaled_vram_offset_y + scaled_display_height) <= m_vram_texture.GetHeight())
{
m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
m_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
g_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
m_vram_texture.GetHeight(), scaled_vram_offset_x, scaled_vram_offset_y,
scaled_display_width, scaled_display_height);
}
@@ -938,7 +936,7 @@ void GPU_HW_D3D12::UpdateDisplay()
m_display_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
m_host_display->SetDisplayTexture(&m_display_texture, HostDisplayPixelFormat::RGBA8, m_display_texture.GetWidth(),
g_host_display->SetDisplayTexture(&m_display_texture, HostDisplayPixelFormat::RGBA8, m_display_texture.GetWidth(),
m_display_texture.GetHeight(), 0, 0, scaled_display_width,
scaled_display_height);
+1 -1
View File
@@ -20,7 +20,7 @@ public:
GPURenderer GetRendererType() const override;
bool Initialize(HostDisplay* host_display) override;
bool Initialize() override;
void Reset(bool clear_vram) override;
void ResetGraphicsAPIState() override;
+27 -27
View File
@@ -3,6 +3,7 @@
#include "common/log.h"
#include "common/timer.h"
#include "gpu_hw_shadergen.h"
#include "host.h"
#include "host_display.h"
#include "shader_cache_version.h"
#include "system.h"
@@ -24,9 +25,9 @@ GPU_HW_OpenGL::~GPU_HW_OpenGL()
if (m_texture_buffer_r16ui_texture != 0)
glDeleteTextures(1, &m_texture_buffer_r16ui_texture);
if (m_host_display)
if (g_host_display)
{
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
ResetGraphicsAPIState();
}
@@ -40,31 +41,30 @@ GPURenderer GPU_HW_OpenGL::GetRendererType() const
return GPURenderer::HardwareOpenGL;
}
bool GPU_HW_OpenGL::Initialize(HostDisplay* host_display)
bool GPU_HW_OpenGL::Initialize()
{
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::OpenGL &&
host_display->GetRenderAPI() != HostDisplay::RenderAPI::OpenGLES)
if (!Host::AcquireHostDisplay(HostDisplay::RenderAPI::OpenGL))
{
Log_ErrorPrintf("Host render API type is incompatible");
return false;
}
const bool opengl_is_available =
((host_display->GetRenderAPI() == HostDisplay::RenderAPI::OpenGL &&
((g_host_display->GetRenderAPI() == HostDisplay::RenderAPI::OpenGL &&
(GLAD_GL_VERSION_3_0 || GLAD_GL_ARB_uniform_buffer_object)) ||
(host_display->GetRenderAPI() == HostDisplay::RenderAPI::OpenGLES && GLAD_GL_ES_VERSION_3_0));
(g_host_display->GetRenderAPI() == HostDisplay::RenderAPI::OpenGLES && GLAD_GL_ES_VERSION_3_0));
if (!opengl_is_available)
{
g_host_interface->AddOSDMessage(
g_host_interface->TranslateStdString("OSDMessage", "OpenGL renderer unavailable, your driver or hardware is not "
"recent enough. OpenGL 3.1 or OpenGL ES 3.0 is required."),
20.0f);
Host::AddOSDMessage(Host::TranslateStdString("OSDMessage",
"OpenGL renderer unavailable, your driver or hardware is not "
"recent enough. OpenGL 3.1 or OpenGL ES 3.0 is required."),
20.0f);
return false;
}
SetCapabilities(host_display);
SetCapabilities();
if (!GPU_HW::Initialize(host_display))
if (!GPU_HW::Initialize())
return false;
if (!CreateFramebuffer())
@@ -133,7 +133,7 @@ bool GPU_HW_OpenGL::DoState(StateWrapper& sw, HostDisplayTexture** host_texture,
{
delete tex;
tex = m_host_display
tex = g_host_display
->CreateTexture(m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 1, 1,
m_vram_texture.GetSamples(), HostDisplayPixelFormat::RGBA8, nullptr, 0, false)
.release();
@@ -252,7 +252,7 @@ void GPU_HW_OpenGL::UpdateSettings()
RestoreGraphicsAPIState();
ReadVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT);
ResetGraphicsAPIState();
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
CreateFramebuffer();
}
if (shaders_changed)
@@ -297,7 +297,7 @@ std::tuple<s32, s32> GPU_HW_OpenGL::ConvertToFramebufferCoordinates(s32 x, s32 y
return std::make_tuple(x, static_cast<s32>(static_cast<s32>(VRAM_HEIGHT) - y));
}
void GPU_HW_OpenGL::SetCapabilities(HostDisplay* host_display)
void GPU_HW_OpenGL::SetCapabilities()
{
GLint max_texture_size = VRAM_WIDTH;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
@@ -510,10 +510,10 @@ bool GPU_HW_OpenGL::CreateTextureBuffer()
bool GPU_HW_OpenGL::CompilePrograms()
{
GL::ShaderCache shader_cache;
shader_cache.Open(IsGLES(), g_host_interface->GetShaderCacheBasePath(), SHADER_CACHE_VERSION);
shader_cache.Open(IsGLES(), EmuFolders::Cache, SHADER_CACHE_VERSION);
const bool use_binding_layout = GPU_HW_ShaderGen::UseGLSLBindingLayout();
GPU_HW_ShaderGen shadergen(m_host_display->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading,
GPU_HW_ShaderGen shadergen(g_host_display->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading,
m_true_color, m_scaled_dithering, m_texture_filtering, m_using_uv_limits,
m_pgxp_depth_buffer, m_supports_dual_source_blend);
@@ -844,7 +844,7 @@ void GPU_HW_OpenGL::ClearDisplay()
{
GPU_HW::ClearDisplay();
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
m_display_texture.BindFramebuffer(GL_DRAW_FRAMEBUFFER);
glDisable(GL_SCISSOR_TEST);
@@ -864,7 +864,7 @@ void GPU_HW_OpenGL::UpdateDisplay()
{
UpdateVRAMReadTexture();
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_read_texture.GetGLId())),
g_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_read_texture.GetGLId())),
HostDisplayPixelFormat::RGBA8, m_vram_read_texture.GetWidth(),
static_cast<s32>(m_vram_read_texture.GetHeight()), 0,
m_vram_read_texture.GetHeight(), m_vram_read_texture.GetWidth(),
@@ -872,17 +872,17 @@ void GPU_HW_OpenGL::UpdateDisplay()
}
else
{
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())),
g_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())),
HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
static_cast<s32>(m_vram_texture.GetHeight()), 0, m_vram_texture.GetHeight(),
m_vram_texture.GetWidth(), -static_cast<s32>(m_vram_texture.GetHeight()));
}
m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
g_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
}
else
{
m_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
g_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
m_crtc_state.display_origin_left, m_crtc_state.display_origin_top,
m_crtc_state.display_vram_width, m_crtc_state.display_vram_height,
GetDisplayAspectRatio());
@@ -900,7 +900,7 @@ void GPU_HW_OpenGL::UpdateDisplay()
if (IsDisplayDisabled())
{
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
}
else if (!m_GPUSTAT.display_area_color_depth_24 && interlaced == GPU_HW::InterlacedRenderMode::None &&
!IsUsingMultisampling() && (scaled_vram_offset_x + scaled_display_width) <= m_vram_texture.GetWidth() &&
@@ -913,7 +913,7 @@ void GPU_HW_OpenGL::UpdateDisplay()
}
else
{
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())),
g_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())),
HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
m_vram_texture.GetHeight(), scaled_vram_offset_x,
m_vram_texture.GetHeight() - scaled_vram_offset_y, scaled_display_width,
@@ -960,7 +960,7 @@ void GPU_HW_OpenGL::UpdateDisplay()
}
else
{
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())),
g_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())),
HostDisplayPixelFormat::RGBA8, m_display_texture.GetWidth(),
m_display_texture.GetHeight(), 0, scaled_display_height, scaled_display_width,
-static_cast<s32>(scaled_display_height));
@@ -1353,7 +1353,7 @@ void GPU_HW_OpenGL::DownsampleFramebufferBoxFilter(GL::Texture& source, u32 left
RestoreGraphicsAPIState();
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_downsample_texture.GetGLId())),
g_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_downsample_texture.GetGLId())),
HostDisplayPixelFormat::RGBA8, m_downsample_texture.GetWidth(),
m_downsample_texture.GetHeight(), ds_left,
m_downsample_texture.GetHeight() - ds_top, ds_width, -static_cast<s32>(ds_height));
+2 -2
View File
@@ -18,7 +18,7 @@ public:
GPURenderer GetRendererType() const override;
bool Initialize(HostDisplay* host_display) override;
bool Initialize() override;
void Reset(bool clear_vram) override;
bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display) override;
@@ -57,7 +57,7 @@ private:
std::tuple<s32, s32> ConvertToFramebufferCoordinates(s32 x, s32 y);
void SetCapabilities(HostDisplay* host_display);
void SetCapabilities();
bool CreateFramebuffer();
void ClearFramebuffer();
void CopyFramebufferForState(GLenum target, GLuint src_texture, u32 src_fbo, u32 src_x, u32 src_y, GLuint dst_texture,
+19 -20
View File
@@ -9,7 +9,6 @@
#include "common/vulkan/util.h"
#include "gpu_hw_shadergen.h"
#include "host_display.h"
#include "host_interface.h"
#include "system.h"
#include "util/state_wrapper.h"
Log_SetChannel(GPU_HW_Vulkan);
@@ -18,9 +17,9 @@ GPU_HW_Vulkan::GPU_HW_Vulkan() = default;
GPU_HW_Vulkan::~GPU_HW_Vulkan()
{
if (m_host_display)
if (g_host_display)
{
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
ResetGraphicsAPIState();
}
@@ -32,9 +31,9 @@ GPURenderer GPU_HW_Vulkan::GetRendererType() const
return GPURenderer::HardwareVulkan;
}
bool GPU_HW_Vulkan::Initialize(HostDisplay* host_display)
bool GPU_HW_Vulkan::Initialize()
{
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::Vulkan)
if (!Host::AcquireHostDisplay(HostDisplay::RenderAPI::Vulkan))
{
Log_ErrorPrintf("Host render API is incompatible");
return false;
@@ -43,7 +42,7 @@ bool GPU_HW_Vulkan::Initialize(HostDisplay* host_display)
Assert(g_vulkan_shader_cache);
SetCapabilities();
if (!GPU_HW::Initialize(host_display))
if (!GPU_HW::Initialize())
return false;
if (!CreatePipelineLayouts())
@@ -144,7 +143,7 @@ bool GPU_HW_Vulkan::DoState(StateWrapper& sw, HostDisplayTexture** host_texture,
{
delete htex;
htex = m_host_display
htex = g_host_display
->CreateTexture(m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 1, 1,
m_vram_texture.GetSamples(), HostDisplayPixelFormat::RGBA8, nullptr, 0, false)
.release();
@@ -179,7 +178,7 @@ void GPU_HW_Vulkan::ResetGraphicsAPIState()
EndRenderPass();
if (m_host_display->GetDisplayTextureHandle() == &m_vram_texture)
if (g_host_display->GetDisplayTextureHandle() == &m_vram_texture)
{
m_vram_texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
@@ -218,7 +217,7 @@ void GPU_HW_Vulkan::UpdateSettings()
}
// Everything should be finished executing before recreating resources.
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
g_vulkan_context->ExecuteCommandBuffer(true);
if (framebuffer_changed)
@@ -923,7 +922,7 @@ bool GPU_HW_Vulkan::CompilePipelines()
VkDevice device = g_vulkan_context->GetDevice();
VkPipelineCache pipeline_cache = g_vulkan_shader_cache->GetPipelineCache();
GPU_HW_ShaderGen shadergen(m_host_display->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading,
GPU_HW_ShaderGen shadergen(g_host_display->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading,
m_true_color, m_scaled_dithering, m_texture_filtering, m_using_uv_limits,
m_pgxp_depth_buffer, m_supports_dual_source_blend);
@@ -1405,7 +1404,7 @@ void GPU_HW_Vulkan::ClearDisplay()
GPU_HW::ClearDisplay();
EndRenderPass();
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
VkCommandBuffer cmdbuf = g_vulkan_context->GetCurrentCommandBuffer();
const Vulkan::Util::DebugScope debugScope(cmdbuf, "GPU_HW_Vulkan::ClearDisplay");
@@ -1435,22 +1434,22 @@ void GPU_HW_Vulkan::UpdateDisplay()
UpdateVRAMReadTexture();
}
m_host_display->SetDisplayTexture(&m_vram_read_texture, HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(&m_vram_read_texture, HostDisplayPixelFormat::RGBA8,
m_vram_read_texture.GetWidth(), m_vram_read_texture.GetHeight(), 0, 0,
m_vram_read_texture.GetWidth(), m_vram_read_texture.GetHeight());
}
else
{
m_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
g_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
m_vram_texture.GetHeight(), 0, 0, m_vram_texture.GetWidth(),
m_vram_texture.GetHeight());
}
m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
g_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
}
else
{
m_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
g_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
m_crtc_state.display_origin_left, m_crtc_state.display_origin_top,
m_crtc_state.display_vram_width, m_crtc_state.display_vram_height,
GetDisplayAspectRatio());
@@ -1468,7 +1467,7 @@ void GPU_HW_Vulkan::UpdateDisplay()
if (IsDisplayDisabled())
{
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
}
else if (!m_GPUSTAT.display_area_color_depth_24 && interlaced == InterlacedRenderMode::None &&
!IsUsingMultisampling() && (scaled_vram_offset_x + scaled_display_width) <= m_vram_texture.GetWidth() &&
@@ -1481,7 +1480,7 @@ void GPU_HW_Vulkan::UpdateDisplay()
}
else
{
m_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
g_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
m_vram_texture.GetHeight(), scaled_vram_offset_x, scaled_vram_offset_y,
scaled_display_width, scaled_display_height);
}
@@ -1528,7 +1527,7 @@ void GPU_HW_Vulkan::UpdateDisplay()
}
else
{
m_host_display->SetDisplayTexture(&m_display_texture, HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(&m_display_texture, HostDisplayPixelFormat::RGBA8,
m_display_texture.GetWidth(), m_display_texture.GetHeight(), 0, 0,
scaled_display_width, scaled_display_height);
RestoreGraphicsAPIState();
@@ -1938,7 +1937,7 @@ void GPU_HW_Vulkan::DownsampleFramebufferBoxFilter(Vulkan::Texture& source, u32
RestoreGraphicsAPIState();
m_host_display->SetDisplayTexture(&m_downsample_texture, HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(&m_downsample_texture, HostDisplayPixelFormat::RGBA8,
m_downsample_texture.GetWidth(), m_downsample_texture.GetHeight(), ds_left, ds_top,
ds_width, ds_height);
}
@@ -2037,7 +2036,7 @@ void GPU_HW_Vulkan::DownsampleFramebufferAdaptive(Vulkan::Texture& source, u32 l
}
RestoreGraphicsAPIState();
m_host_display->SetDisplayTexture(&m_display_texture, HostDisplayPixelFormat::RGBA8, m_display_texture.GetWidth(),
g_host_display->SetDisplayTexture(&m_display_texture, HostDisplayPixelFormat::RGBA8, m_display_texture.GetWidth(),
m_display_texture.GetHeight(), left, top, width, height);
}
+1 -1
View File
@@ -17,7 +17,7 @@ public:
GPURenderer GetRendererType() const override;
bool Initialize(HostDisplay* host_display) override;
bool Initialize() override;
void Reset(bool clear_vram) override;
bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display) override;
+19 -15
View File
@@ -36,8 +36,8 @@ GPU_SW::GPU_SW()
GPU_SW::~GPU_SW()
{
m_backend.Shutdown();
if (m_host_display)
m_host_display->ClearDisplayTexture();
if (g_host_display)
g_host_display->ClearDisplayTexture();
}
GPURenderer GPU_SW::GetRendererType() const
@@ -45,9 +45,13 @@ GPURenderer GPU_SW::GetRendererType() const
return GPURenderer::Software;
}
bool GPU_SW::Initialize(HostDisplay* host_display)
bool GPU_SW::Initialize()
{
if (!GPU::Initialize(host_display) || !m_backend.Initialize(false))
// we need something to draw in.. but keep the current api if we have one
if (!g_host_display && !Host::AcquireHostDisplay(HostDisplay::GetPreferredAPI()))
return false;
if (!GPU::Initialize() || !m_backend.Initialize(false))
return false;
static constexpr auto formats_for_16bit = make_array(HostDisplayPixelFormat::RGB565, HostDisplayPixelFormat::RGBA5551,
@@ -57,7 +61,7 @@ bool GPU_SW::Initialize(HostDisplay* host_display)
HostDisplayPixelFormat::RGBA5551);
for (const HostDisplayPixelFormat format : formats_for_16bit)
{
if (m_host_display->SupportsDisplayPixelFormat(format))
if (g_host_display->SupportsDisplayPixelFormat(format))
{
m_16bit_display_format = format;
break;
@@ -65,7 +69,7 @@ bool GPU_SW::Initialize(HostDisplay* host_display)
}
for (const HostDisplayPixelFormat format : formats_for_24bit)
{
if (m_host_display->SupportsDisplayPixelFormat(format))
if (g_host_display->SupportsDisplayPixelFormat(format))
{
m_24bit_display_format = format;
break;
@@ -233,7 +237,7 @@ void GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32 width, u32 height, u32 field
if (!interlaced)
{
if (!m_host_display->BeginSetDisplayPixels(display_format, width, height, reinterpret_cast<void**>(&dst_ptr),
if (!g_host_display->BeginSetDisplayPixels(display_format, width, height, reinterpret_cast<void**>(&dst_ptr),
&dst_stride))
{
return;
@@ -285,11 +289,11 @@ void GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32 width, u32 height, u32 field
if (!interlaced)
{
m_host_display->EndSetDisplayPixels();
g_host_display->EndSetDisplayPixels();
}
else
{
m_host_display->SetDisplayPixels(display_format, width, height, m_display_texture_buffer.data(), output_stride);
g_host_display->SetDisplayPixels(display_format, width, height, m_display_texture_buffer.data(), output_stride);
}
}
@@ -327,7 +331,7 @@ void GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 heigh
if (!interlaced)
{
if (!m_host_display->BeginSetDisplayPixels(display_format, width, height, reinterpret_cast<void**>(&dst_ptr),
if (!g_host_display->BeginSetDisplayPixels(display_format, width, height, reinterpret_cast<void**>(&dst_ptr),
&dst_stride))
{
return;
@@ -443,11 +447,11 @@ void GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 heigh
if (!interlaced)
{
m_host_display->EndSetDisplayPixels();
g_host_display->EndSetDisplayPixels();
}
else
{
m_host_display->SetDisplayPixels(display_format, width, height, m_display_texture_buffer.data(), output_stride);
g_host_display->SetDisplayPixels(display_format, width, height, m_display_texture_buffer.data(), output_stride);
}
}
@@ -486,14 +490,14 @@ void GPU_SW::UpdateDisplay()
if (!g_settings.debugging.show_vram)
{
m_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
g_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
m_crtc_state.display_origin_left, m_crtc_state.display_origin_top,
m_crtc_state.display_vram_width, m_crtc_state.display_vram_height,
GetDisplayAspectRatio());
if (IsDisplayDisabled())
{
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
return;
}
@@ -534,7 +538,7 @@ void GPU_SW::UpdateDisplay()
else
{
CopyOut15Bit(m_16bit_display_format, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, 0, false, false);
m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
g_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
}
}
+8 -1
View File
@@ -7,6 +7,11 @@
#include <memory>
#include <vector>
namespace Threading
{
class Thread;
}
class HostDisplayTexture;
class GPU_SW final : public GPU
@@ -15,9 +20,11 @@ public:
GPU_SW();
~GPU_SW() override;
ALWAYS_INLINE const GPU_SW_Backend& GetBackend() const { return m_backend; }
GPURenderer GetRendererType() const override;
bool Initialize(HostDisplay* host_display) override;
bool Initialize() override;
bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display) override;
void Reset(bool clear_vram) override;
void UpdateSettings() override;
+3 -5
View File
@@ -5,7 +5,6 @@
#include "cpu_core.h"
#include "cpu_core_private.h"
#include "host_display.h"
#include "host_interface.h"
#include "pgxp.h"
#include "settings.h"
#include "timing_event.h"
@@ -188,15 +187,14 @@ void UpdateAspectRatio()
{
case DisplayAspectRatio::MatchWindow:
{
const HostDisplay* display = g_host_interface->GetDisplay();
if (!display)
if (!g_host_display)
{
s_aspect_ratio = DisplayAspectRatio::R4_3;
return;
}
num = display->GetWindowWidth();
denom = display->GetWindowHeight();
num = g_host_display->GetWindowWidth();
denom = g_host_display->GetWindowHeight();
}
break;
+273
View File
@@ -0,0 +1,273 @@
#include "guncon.h"
#include "common/assert.h"
#include "common/log.h"
#include "gpu.h"
#include "host.h"
#include "host_display.h"
#include "resources.h"
#include "system.h"
#include "util/state_wrapper.h"
#include <array>
Log_SetChannel(GunCon);
static constexpr std::array<u8, static_cast<size_t>(GunCon::Button::Count)> s_button_indices = {{13, 3, 14}};
GunCon::GunCon(u32 index) : Controller(index) {}
GunCon::~GunCon() = default;
ControllerType GunCon::GetType() const
{
return ControllerType::GunCon;
}
void GunCon::Reset()
{
m_transfer_state = TransferState::Idle;
}
bool GunCon::DoState(StateWrapper& sw, bool apply_input_state)
{
if (!Controller::DoState(sw, apply_input_state))
return false;
u16 button_state = m_button_state;
u16 position_x = m_position_x;
u16 position_y = m_position_y;
sw.Do(&button_state);
sw.Do(&position_x);
sw.Do(&position_y);
if (apply_input_state)
{
m_button_state = button_state;
m_position_x = position_x;
m_position_y = position_y;
}
sw.Do(&m_transfer_state);
return true;
}
float GunCon::GetBindState(u32 index) const
{
if (index >= s_button_indices.size())
return 0.0f;
const u32 bit = s_button_indices[index];
return static_cast<float>(((m_button_state >> bit) & 1u) ^ 1u);
}
void GunCon::SetBindState(u32 index, float value)
{
const bool pressed = (value >= 0.5f);
if (index == static_cast<u32>(Button::ShootOffscreen))
{
if (m_shoot_offscreen != pressed)
{
m_shoot_offscreen = pressed;
SetBindState(static_cast<u32>(Button::Trigger), pressed);
}
return;
}
if (pressed)
m_button_state &= ~(u16(1) << s_button_indices[static_cast<u8>(index)]);
else
m_button_state |= u16(1) << s_button_indices[static_cast<u8>(index)];
}
void GunCon::ResetTransferState()
{
m_transfer_state = TransferState::Idle;
}
bool GunCon::Transfer(const u8 data_in, u8* data_out)
{
static constexpr u16 ID = 0x5A63;
switch (m_transfer_state)
{
case TransferState::Idle:
{
*data_out = 0xFF;
if (data_in == 0x01)
{
m_transfer_state = TransferState::Ready;
return true;
}
return false;
}
case TransferState::Ready:
{
if (data_in == 0x42)
{
*data_out = Truncate8(ID);
m_transfer_state = TransferState::IDMSB;
return true;
}
*data_out = 0xFF;
return false;
}
case TransferState::IDMSB:
{
*data_out = Truncate8(ID >> 8);
m_transfer_state = TransferState::ButtonsLSB;
return true;
}
case TransferState::ButtonsLSB:
{
*data_out = Truncate8(m_button_state);
m_transfer_state = TransferState::ButtonsMSB;
return true;
}
case TransferState::ButtonsMSB:
{
*data_out = Truncate8(m_button_state >> 8);
m_transfer_state = TransferState::XLSB;
return true;
}
case TransferState::XLSB:
{
UpdatePosition();
*data_out = Truncate8(m_position_x);
m_transfer_state = TransferState::XMSB;
return true;
}
case TransferState::XMSB:
{
*data_out = Truncate8(m_position_x >> 8);
m_transfer_state = TransferState::YLSB;
return true;
}
case TransferState::YLSB:
{
*data_out = Truncate8(m_position_y);
m_transfer_state = TransferState::YMSB;
return true;
}
case TransferState::YMSB:
{
*data_out = Truncate8(m_position_y >> 8);
m_transfer_state = TransferState::Idle;
return false;
}
default:
{
UnreachableCode();
return false;
}
}
}
void GunCon::UpdatePosition()
{
// get screen coordinates
const s32 mouse_x = g_host_display->GetMousePositionX();
const s32 mouse_y = g_host_display->GetMousePositionY();
// are we within the active display area?
u32 tick, line;
if (mouse_x < 0 || mouse_y < 0 ||
!g_gpu->ConvertScreenCoordinatesToBeamTicksAndLines(mouse_x, mouse_y, m_x_scale, &tick, &line) ||
m_shoot_offscreen)
{
Log_DebugPrintf("Lightgun out of range for window coordinates %d,%d", mouse_x, mouse_y);
m_position_x = 0x01;
m_position_y = 0x0A;
return;
}
// 8MHz units for X = 44100*768*11/7 = 53222400 / 8000000 = 6.6528
const double divider = static_cast<double>(g_gpu->GetCRTCFrequency()) / 8000000.0;
m_position_x = static_cast<u16>(static_cast<float>(tick) / static_cast<float>(divider));
m_position_y = static_cast<u16>(line);
Log_DebugPrintf("Lightgun window coordinates %d,%d -> tick %u line %u 8mhz ticks %u", mouse_x, mouse_y, tick, line,
m_position_x);
}
std::unique_ptr<GunCon> GunCon::Create(u32 index)
{
return std::make_unique<GunCon>(index);
}
static const Controller::ControllerBindingInfo s_binding_info[] = {
#define BUTTON(name, display_name, button, genb) \
{ \
name, display_name, static_cast<u32>(button), Controller::ControllerBindingType::Button, genb \
}
BUTTON("Trigger", "Trigger", GunCon::Button::Trigger, GenericInputBinding::R2),
BUTTON("ShootOffscreen", "ShootOffscreen", GunCon::Button::ShootOffscreen, GenericInputBinding::L2),
BUTTON("A", "A", GunCon::Button::A, GenericInputBinding::Cross),
BUTTON("B", "B", GunCon::Button::B, GenericInputBinding::Circle),
#undef BUTTON
};
static const SettingInfo s_settings[] = {
{SettingInfo::Type::Path, "CrosshairImagePath", TRANSLATABLE("NamcoGunCon", "Crosshair Image Path"),
TRANSLATABLE("NamcoGunCon", "Path to an image to use as a crosshair/cursor.")},
{SettingInfo::Type::Float, "CrosshairScale", TRANSLATABLE("NamcoGunCon", "Crosshair Image Scale"),
TRANSLATABLE("NamcoGunCon", "Scale of crosshair image on screen."), "1.0", "0.0001", "100.0", "0.10"},
{SettingInfo::Type::Float, "XScale", TRANSLATABLE("NamcoGunCon", "X Scale"),
TRANSLATABLE("NamcoGunCon", "Scales X coordinates relative to the center of the screen."), "1.0", "0.01", "2.0",
"0.01"}};
const Controller::ControllerInfo GunCon::INFO = {ControllerType::GunCon,
"GunCon",
TRANSLATABLE("ControllerType", "GunCon"),
s_binding_info,
countof(s_binding_info),
s_settings,
countof(s_settings),
Controller::VibrationCapabilities::NoVibration};
void GunCon::LoadSettings(SettingsInterface& si, const char* section)
{
Controller::LoadSettings(si, section);
std::string path = si.GetStringValue(section, "CrosshairImagePath");
if (path != m_crosshair_image_path)
{
m_crosshair_image_path = std::move(path);
if (m_crosshair_image_path.empty() || !m_crosshair_image.LoadFromFile(m_crosshair_image_path.c_str()))
{
m_crosshair_image.Invalidate();
}
}
#ifndef __ANDROID__
if (!m_crosshair_image.IsValid())
{
m_crosshair_image.SetPixels(Resources::CROSSHAIR_IMAGE_WIDTH, Resources::CROSSHAIR_IMAGE_HEIGHT,
Resources::CROSSHAIR_IMAGE_DATA.data());
}
#endif
m_crosshair_image_scale = si.GetFloatValue(section, "CrosshairScale", 1.0f);
m_x_scale = si.GetFloatValue(section, "XScale", 1.0f);
}
bool GunCon::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode)
{
if (!m_crosshair_image.IsValid())
return false;
*image = &m_crosshair_image;
*image_scale = m_crosshair_image_scale;
*relative_mode = false;
return true;
}
+10 -17
View File
@@ -4,7 +4,7 @@
#include <optional>
#include <string_view>
class NamcoGunCon final : public Controller
class GunCon final : public Controller
{
public:
enum class Button : u8
@@ -16,34 +16,27 @@ public:
Count
};
NamcoGunCon();
~NamcoGunCon() override;
static const Controller::ControllerInfo INFO;
static std::unique_ptr<NamcoGunCon> Create();
static std::optional<s32> StaticGetAxisCodeByName(std::string_view button_name);
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
static AxisList StaticGetAxisNames();
static ButtonList StaticGetButtonNames();
static u32 StaticGetVibrationMotorCount();
static SettingList StaticGetSettings();
GunCon(u32 index);
~GunCon() override;
static std::unique_ptr<GunCon> Create(u32 index);
ControllerType GetType() const override;
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
std::optional<s32> GetButtonCodeByName(std::string_view button_name) const override;
void Reset() override;
bool DoState(StateWrapper& sw, bool apply_input_state) override;
void LoadSettings(const char* section) override;
void LoadSettings(SettingsInterface& si, const char* section) override;
bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode) override;
bool GetButtonState(s32 button_code) const override;
void SetButtonState(s32 button_code, bool pressed) override;
float GetBindState(u32 index) const override;
void SetBindState(u32 index, float value) override;
void ResetTransferState() override;
bool Transfer(const u8 data_in, u8* data_out) override;
void SetButtonState(Button button, bool pressed);
private:
void UpdatePosition();
+32
View File
@@ -0,0 +1,32 @@
#include "host.h"
#include "common/string_util.h"
#include <cstdarg>
void Host::ReportFormattedErrorAsync(const std::string_view& title, const char* format, ...)
{
std::va_list ap;
va_start(ap, format);
std::string message(StringUtil::StdStringFromFormatV(format, ap));
va_end(ap);
ReportErrorAsync(title, message);
}
bool Host::ConfirmFormattedMessage(const std::string_view& title, const char* format, ...)
{
std::va_list ap;
va_start(ap, format);
std::string message = StringUtil::StdStringFromFormatV(format, ap);
va_end(ap);
return ConfirmMessage(title, message);
}
void Host::ReportFormattedDebuggerMessage(const char* format, ...)
{
std::va_list ap;
va_start(ap, format);
std::string message = StringUtil::StdStringFromFormatV(format, ap);
va_end(ap);
ReportDebuggerMessage(message);
}
+89
View File
@@ -1,12 +1,67 @@
#pragma once
#include "common/string.h"
#include "common/types.h"
#include <ctime>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
struct WindowInfo;
enum class AudioBackend : u8;
class AudioStream;
class CDImage;
/// Marks a core string as being translatable.
#define TRANSLATABLE(context, str) str
/// Generic input bindings. These roughly match a DualShock 4 or XBox One controller.
/// They are used for automatic binding to PS2 controller types, and for big picture mode navigation.
enum class GenericInputBinding : u8
{
Unknown,
DPadUp,
DPadRight,
DPadLeft,
DPadDown,
LeftStickUp,
LeftStickRight,
LeftStickDown,
LeftStickLeft,
L3,
RightStickUp,
RightStickRight,
RightStickDown,
RightStickLeft,
R3,
Triangle, // Y on XBox pads.
Circle, // B on XBox pads.
Cross, // A on XBox pads.
Square, // X on XBox pads.
Select, // Share on DS4, View on XBox pads.
Start, // Options on DS4, Menu on XBox pads.
System, // PS button on DS4, Guide button on XBox pads.
L1, // LB on Xbox pads.
L2, // Left trigger on XBox pads.
R1, // RB on XBox pads.
R2, // Right trigger on Xbox pads.
SmallMotor, // High frequency vibration.
LargeMotor, // Low frequency vibration.
Count,
};
namespace Host {
/// Reads a file from the resources directory of the application.
/// This may be outside of the "normal" filesystem on platforms such as Mac.
@@ -15,6 +70,18 @@ std::optional<std::vector<u8>> ReadResourceFile(const char* filename);
/// Reads a resource file file from the resources directory as a string.
std::optional<std::string> ReadResourceFileToString(const char* filename);
/// Returns the modified time of a resource.
std::optional<std::time_t> GetResourceFileTimestamp(const char* filename);
/// Translates a string to the current language.
TinyString TranslateString(const char* context, const char* str, const char* disambiguation = nullptr, int n = -1);
std::string TranslateStdString(const char* context, const char* str, const char* disambiguation = nullptr, int n = -1);
std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend);
/// Returns the scale of OSD elements.
float GetOSDScale();
/// Adds OSD messages, duration is in seconds.
void AddOSDMessage(std::string message, float duration = 2.0f);
void AddKeyedOSDMessage(std::string key, std::string message, float duration = 2.0f);
@@ -26,4 +93,26 @@ void ClearOSDMessages();
/// Displays an asynchronous error on the UI thread, i.e. doesn't block the caller.
void ReportErrorAsync(const std::string_view& title, const std::string_view& message);
void ReportFormattedErrorAsync(const std::string_view& title, const char* format, ...);
/// Displays a synchronous confirmation on the UI thread, i.e. blocks the caller.
bool ConfirmMessage(const std::string_view& title, const std::string_view& message);
bool ConfirmFormattedMessage(const std::string_view& title, const char* format, ...);
/// Debugger feedback.
void ReportDebuggerMessage(const std::string_view& message);
void ReportFormattedDebuggerMessage(const char* format, ...);
/// Displays a loading screen with the logo, rendered with ImGui. Use when executing possibly-time-consuming tasks
/// such as compiling shaders when starting up.
void DisplayLoadingScreen(const char* message, int progress_min = -1, int progress_max = -1, int progress_value = -1);
/// Internal method used by pads to dispatch vibration updates to input sources.
/// Intensity is normalized from 0 to 1.
void SetPadVibrationIntensity(u32 pad_index, float large_or_single_motor_intensity, float small_motor_intensity);
/// Enables "relative" mouse mode, locking the cursor position and returning relative coordinates.
void SetMouseMode(bool relative, bool hide_cursor);
/// Safely executes a function on the VM thread.
void RunOnCPUThread(std::function<void()> function, bool block = false);
} // namespace Host
+62 -1
View File
@@ -5,7 +5,6 @@
#include "common/log.h"
#include "common/string_util.h"
#include "common/timer.h"
#include "host_interface.h"
#include "stb_image.h"
#include "stb_image_resize.h"
#include "stb_image_write.h"
@@ -16,10 +15,72 @@
#include <vector>
Log_SetChannel(HostDisplay);
std::unique_ptr<HostDisplay> g_host_display;
HostDisplayTexture::~HostDisplayTexture() = default;
HostDisplay::~HostDisplay() = default;
HostDisplay::RenderAPI HostDisplay::GetPreferredAPI()
{
#ifdef _WIN32
return RenderAPI::D3D11;
#else
return RenderAPI::OpenGL;
#endif
}
bool HostDisplay::ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate)
{
if (!mode.empty())
{
std::string_view::size_type sep1 = mode.find('x');
if (sep1 != std::string_view::npos)
{
std::optional<u32> owidth = StringUtil::FromChars<u32>(mode.substr(0, sep1));
sep1++;
while (sep1 < mode.length() && std::isspace(mode[sep1]))
sep1++;
if (owidth.has_value() && sep1 < mode.length())
{
std::string_view::size_type sep2 = mode.find('@', sep1);
if (sep2 != std::string_view::npos)
{
std::optional<u32> oheight = StringUtil::FromChars<u32>(mode.substr(sep1, sep2 - sep1));
sep2++;
while (sep2 < mode.length() && std::isspace(mode[sep2]))
sep2++;
if (oheight.has_value() && sep2 < mode.length())
{
std::optional<float> orefresh_rate = StringUtil::FromChars<float>(mode.substr(sep2));
if (orefresh_rate.has_value())
{
*width = owidth.value();
*height = oheight.value();
*refresh_rate = orefresh_rate.value();
return true;
}
}
}
}
}
}
*width = 0;
*height = 0;
*refresh_rate = 0;
return false;
}
std::string HostDisplay::GetFullscreenModeString(u32 width, u32 height, float refresh_rate)
{
return StringUtil::StdStringFromFormat("%u x %u @ %f hz", width, height, refresh_rate);
}
bool HostDisplay::UsesLowerLeftOrigin() const
{
const RenderAPI api = GetRenderAPI();
+31
View File
@@ -62,6 +62,15 @@ public:
virtual ~HostDisplay();
/// Returns the default/preferred API for the system.
static RenderAPI GetPreferredAPI();
/// Parses a fullscreen mode into its components (width * height @ refresh hz)
static bool ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate);
/// Converts a fullscreen mode to a string.
static std::string GetFullscreenModeString(u32 width, u32 height, float refresh_rate);
ALWAYS_INLINE const WindowInfo& GetWindowInfo() const { return m_window_info; }
ALWAYS_INLINE s32 GetWindowWidth() const { return static_cast<s32>(m_window_info.surface_width); }
ALWAYS_INLINE s32 GetWindowHeight() const { return static_cast<s32>(m_window_info.surface_height); }
@@ -290,3 +299,25 @@ protected:
bool m_display_integer_scaling = false;
bool m_display_stretch = false;
};
/// Returns a pointer to the current host display abstraction. Assumes AcquireHostDisplay() has been caled.
extern std::unique_ptr<HostDisplay> g_host_display;
namespace Host {
/// Creates the host display. This may create a new window. The API used depends on the current configuration.
bool AcquireHostDisplay(HostDisplay::RenderAPI api);
/// Destroys the host display. This may close the display window.
void ReleaseHostDisplay();
/// Returns false if the window was completely occluded. If frame_skip is set, the frame won't be
/// displayed, but the GPU command queue will still be flushed.
//bool BeginPresentFrame(bool frame_skip);
/// Presents the frame to the display, and renders OSD elements.
//void EndPresentFrame();
/// Provided by the host; renders the display.
void RenderDisplay();
void InvalidateDisplay();
} // namespace Host
File diff suppressed because it is too large Load Diff
-225
View File
@@ -1,225 +0,0 @@
#pragma once
#include "common/string.h"
#include "common/timer.h"
#include "settings.h"
#include "types.h"
#include <chrono>
#include <deque>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <vector>
enum LOGLEVEL;
class AudioStream;
class ByteStream;
class CDImage;
class HostDisplay;
class GameList;
struct SystemBootParameters;
namespace BIOS {
struct ImageInfo;
}
class HostInterface
{
public:
enum : u32
{
AUDIO_SAMPLE_RATE = 44100,
AUDIO_CHANNELS = 2,
DEFAULT_AUDIO_BUFFER_SIZE = 2048
};
HostInterface();
virtual ~HostInterface();
/// Access to host display.
ALWAYS_INLINE HostDisplay* GetDisplay() const { return m_display.get(); }
ALWAYS_INLINE bool HasDisplay() const { return static_cast<bool>(m_display.get()); }
/// Access to host audio stream.
ALWAYS_INLINE AudioStream* GetAudioStream() const { return m_audio_stream.get(); }
/// Initializes the emulator frontend.
virtual bool Initialize();
/// Shuts down the emulator frontend.
virtual void Shutdown();
virtual bool BootSystem(std::shared_ptr<SystemBootParameters> parameters);
virtual void PauseSystem(bool paused);
virtual void ResetSystem();
virtual void DestroySystem();
/// Loads state from the specified filename.
bool LoadState(const char* filename);
virtual void ReportError(const char* message);
virtual void ReportMessage(const char* message);
virtual void ReportDebuggerMessage(const char* message);
virtual bool ConfirmMessage(const char* message);
void ReportFormattedError(const char* format, ...) printflike(2, 3);
void ReportFormattedMessage(const char* format, ...) printflike(2, 3);
void ReportFormattedDebuggerMessage(const char* format, ...) printflike(2, 3);
bool ConfirmFormattedMessage(const char* format, ...) printflike(2, 3);
/// Adds OSD messages, duration is in seconds.
virtual void AddOSDMessage(std::string message, float duration = 2.0f) = 0;
virtual void AddKeyedOSDMessage(std::string key, std::string message, float duration = 2.0f) = 0;
virtual void RemoveKeyedOSDMessage(std::string key) = 0;
void AddFormattedOSDMessage(float duration, const char* format, ...) printflike(3, 4);
void AddKeyedFormattedOSDMessage(std::string key, float duration, const char* format, ...) printflike(4, 5);
/// Returns the base user directory path.
ALWAYS_INLINE const std::string& GetUserDirectory() const { return m_user_directory; }
/// Returns a path relative to the user directory.
std::string GetUserDirectoryRelativePath(const char* format, ...) const printflike(2, 3);
/// Returns a path relative to the application directory (for system files).
std::string GetProgramDirectoryRelativePath(const char* format, ...) const printflike(2, 3);
/// Returns a string which can be used as part of a filename, based on the current date/time.
static TinyString GetTimestampStringForFileName();
/// Displays a loading screen with the logo, rendered with ImGui. Use when executing possibly-time-consuming tasks
/// such as compiling shaders when starting up.
virtual void DisplayLoadingScreen(const char* message, int progress_min = -1, int progress_max = -1,
int progress_value = -1) = 0;
/// Retrieves information about specified game from game list.
virtual void GetGameInfo(const char* path, CDImage* image, std::string* code, std::string* title) = 0;
/// Returns the directory where per-game memory cards will be saved.
virtual std::string GetMemoryCardDirectory() const;
/// Returns the default path to a memory card.
virtual std::string GetSharedMemoryCardPath(u32 slot) const;
/// Returns the default path to a memory card for a specific game.
virtual std::string GetGameMemoryCardPath(const char* game_code, u32 slot) const;
/// Returns the path to the shader cache directory.
virtual std::string GetShaderCacheBasePath() const;
/// Returns a setting value from the configuration.
virtual std::string GetStringSettingValue(const char* section, const char* key, const char* default_value = "") = 0;
/// Returns a boolean setting from the configuration.
virtual bool GetBoolSettingValue(const char* section, const char* key, bool default_value = false);
/// Returns an integer setting from the configuration.
virtual int GetIntSettingValue(const char* section, const char* key, int default_value = 0);
/// Returns a float setting from the configuration.
virtual float GetFloatSettingValue(const char* section, const char* key, float default_value = 0.0f);
/// Returns a string list from the configuration.
virtual std::vector<std::string> GetSettingStringList(const char* section, const char* key) = 0;
/// Returns the settings interface.
virtual SettingsInterface* GetSettingsInterface() = 0;
virtual std::lock_guard<std::recursive_mutex> GetSettingsLock() = 0;
/// Translates a string to the current language.
virtual TinyString TranslateString(const char* context, const char* str, const char* disambiguation = nullptr,
int n = -1) const;
virtual std::string TranslateStdString(const char* context, const char* str, const char* disambiguation = nullptr,
int n = -1) const;
/// Returns the path to the directory to search for BIOS images.
virtual std::string GetBIOSDirectory();
/// Loads the BIOS image for the specified region.
std::optional<std::vector<u8>> GetBIOSImage(ConsoleRegion region);
/// Searches for a BIOS image for the specified region in the specified directory. If no match is found, the first
/// BIOS image within 512KB and 4MB will be used.
std::optional<std::vector<u8>> FindBIOSImageInDirectory(ConsoleRegion region, const char* directory);
/// Returns a list of filenames and descriptions for BIOS images in a directory.
std::vector<std::pair<std::string, const BIOS::ImageInfo*>> FindBIOSImagesInDirectory(const char* directory);
/// Returns true if any BIOS images are found in the configured BIOS directory.
bool HasAnyBIOSImages();
/// Opens a file in the DuckStation "package".
/// This is the APK for Android builds, or the program directory for standalone builds.
virtual std::unique_ptr<ByteStream> OpenPackageFile(const char* path, u32 flags) = 0;
virtual void OnRunningGameChanged(const std::string& path, CDImage* image, const std::string& game_code,
const std::string& game_title) = 0;
virtual void OnSystemPerformanceCountersUpdated() = 0;
/// Called when the display is invalidated (e.g. a state is loaded).
virtual void OnDisplayInvalidated() = 0;
/// Called when achievements data is loaded.
virtual void OnAchievementsRefreshed() = 0;
protected:
virtual bool AcquireHostDisplay() = 0;
virtual void ReleaseHostDisplay() = 0;
virtual std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend) = 0;
virtual s32 GetAudioOutputVolume() const;
virtual void OnSystemCreated() = 0;
virtual void OnSystemPaused(bool paused) = 0;
virtual void OnSystemDestroyed() = 0;
virtual void OnControllerTypeChanged(u32 slot) = 0;
/// Restores all settings to defaults.
virtual void SetDefaultSettings(SettingsInterface& si);
/// Loads settings to m_settings and any frontend-specific parameters.
virtual void LoadSettings(SettingsInterface& si);
/// Saves current settings variables to ini.
virtual void SaveSettings(SettingsInterface& si);
/// Checks and fixes up any incompatible settings.
virtual void FixIncompatibleSettings(bool display_osd_messages);
/// Checks for settings changes, std::move() the old settings away for comparing beforehand.
virtual void CheckForSettingsChanges(const Settings& old_settings);
/// Switches the GPU renderer by saving state, recreating the display window, and restoring state (if needed).
virtual void RecreateSystem();
/// Enables "relative" mouse mode, locking the cursor position and returning relative coordinates.
virtual void SetMouseMode(bool relative, bool hide_cursor) = 0;
/// Call when host display size changes, use with "match display" aspect ratio setting.
virtual void OnHostDisplayResized();
/// Sets the user directory to the program directory, i.e. "portable mode".
void SetUserDirectoryToProgramDirectory();
/// Quick switch between software and hardware rendering.
void ToggleSoftwareRendering();
/// Adjusts the internal (render) resolution of the hardware backends.
void ModifyResolutionScale(s32 increment);
/// Updates software cursor state, based on controllers.
void UpdateSoftwareCursor();
bool SaveState(const char* filename);
void CreateAudioStream();
std::unique_ptr<HostDisplay> m_display;
std::unique_ptr<AudioStream> m_audio_stream;
std::string m_program_directory;
std::string m_user_directory;
};
#define TRANSLATABLE(context, str) str
extern HostInterface* g_host_interface;
+24 -10
View File
@@ -1,10 +1,14 @@
#include "host_interface_progress_callback.h"
#include "common/log.h"
#include "host.h"
Log_SetChannel(HostInterfaceProgressCallback);
HostInterfaceProgressCallback::HostInterfaceProgressCallback() : BaseProgressCallback() {}
void HostInterfaceProgressCallback::PushState() { BaseProgressCallback::PushState(); }
void HostInterfaceProgressCallback::PushState()
{
BaseProgressCallback::PushState();
}
void HostInterfaceProgressCallback::PopState()
{
@@ -57,32 +61,42 @@ void HostInterfaceProgressCallback::Redraw(bool force)
return;
m_last_progress_percent = percent;
g_host_interface->DisplayLoadingScreen(m_status_text, 0, static_cast<int>(m_progress_range),
static_cast<int>(m_progress_value));
Host::DisplayLoadingScreen(m_status_text, 0, static_cast<int>(m_progress_range), static_cast<int>(m_progress_value));
}
void HostInterfaceProgressCallback::DisplayError(const char* message) { Log_ErrorPrint(message); }
void HostInterfaceProgressCallback::DisplayError(const char* message)
{
Log_ErrorPrint(message);
}
void HostInterfaceProgressCallback::DisplayWarning(const char* message) { Log_WarningPrint(message); }
void HostInterfaceProgressCallback::DisplayWarning(const char* message)
{
Log_WarningPrint(message);
}
void HostInterfaceProgressCallback::DisplayInformation(const char* message) { Log_InfoPrint(message); }
void HostInterfaceProgressCallback::DisplayInformation(const char* message)
{
Log_InfoPrint(message);
}
void HostInterfaceProgressCallback::DisplayDebugMessage(const char* message) { Log_DevPrint(message); }
void HostInterfaceProgressCallback::DisplayDebugMessage(const char* message)
{
Log_DevPrint(message);
}
void HostInterfaceProgressCallback::ModalError(const char* message)
{
Log_ErrorPrint(message);
g_host_interface->ReportError(message);
Host::ReportErrorAsync("Error", message);
}
bool HostInterfaceProgressCallback::ModalConfirmation(const char* message)
{
Log_InfoPrint(message);
return g_host_interface->ConfirmMessage(message);
return Host::ConfirmMessage("Confirm", message);
}
void HostInterfaceProgressCallback::ModalInformation(const char* message)
{
Log_InfoPrint(message);
g_host_interface->ReportMessage(message);
}
@@ -1,6 +1,5 @@
#pragma once
#include "common/progress_callback.h"
#include "host_interface.h"
class HostInterfaceProgressCallback final : public BaseProgressCallback
{
+4 -1
View File
@@ -18,13 +18,16 @@ double GetBaseDoubleSettingValue(const char* section, const char* key, double de
std::vector<std::string> GetBaseStringListSetting(const char* section, const char* key);
// Allows the emucore to write settings back to the frontend. Use with care.
// You should call CommitBaseSettingChanges() after finishing writing, or it may not be written to disk.
// You should call CommitBaseSettingChanges() if you directly write to the layer (i.e. not these functions), or it may
// not be written to disk.
void SetBaseBoolSettingValue(const char* section, const char* key, bool value);
void SetBaseIntSettingValue(const char* section, const char* key, s32 value);
void SetBaseUIntSettingValue(const char* section, const char* key, u32 value);
void SetBaseFloatSettingValue(const char* section, const char* key, float value);
void SetBaseStringSettingValue(const char* section, const char* key, const char* value);
void SetBaseStringListSettingValue(const char* section, const char* key, const std::vector<std::string>& values);
bool AddValueToBaseStringListSetting(const char* section, const char* key, const char* value);
bool RemoveValueFromBaseStringListSetting(const char* section, const char* key, const char* value);
void DeleteBaseSettingValue(const char* section, const char* key);
void CommitBaseSettingChanges();
-56
View File
@@ -1,56 +0,0 @@
#include "imgui_styles.h"
void ImGui::StyleColorsDarker(ImGuiStyle* dst)
{
ImGuiStyle* style = dst ? dst : &ImGui::GetStyle();
ImVec4* colors = style->Colors;
colors[ImGuiCol_Text] = ImVec4(0.95f, 0.96f, 0.98f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.36f, 0.42f, 0.47f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
colors[ImGuiCol_ChildBg] = ImVec4(0.15f, 0.18f, 0.22f, 1.00f);
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
colors[ImGuiCol_Border] = ImVec4(0.08f, 0.10f, 0.12f, 1.00f);
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_FrameBg] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.12f, 0.20f, 0.28f, 1.00f);
colors[ImGuiCol_FrameBgActive] = ImVec4(0.09f, 0.12f, 0.14f, 1.00f);
colors[ImGuiCol_TitleBg] = ImVec4(0.09f, 0.12f, 0.14f, 0.65f);
colors[ImGuiCol_TitleBgActive] = ImVec4(0.08f, 0.10f, 0.12f, 1.00f);
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
colors[ImGuiCol_MenuBarBg] = ImVec4(0.15f, 0.18f, 0.22f, 1.00f);
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.39f);
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.18f, 0.22f, 0.25f, 1.00f);
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.09f, 0.21f, 0.31f, 1.00f);
colors[ImGuiCol_CheckMark] = ImVec4(0.28f, 0.56f, 1.00f, 1.00f);
colors[ImGuiCol_SliderGrab] = ImVec4(0.28f, 0.56f, 1.00f, 1.00f);
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.37f, 0.61f, 1.00f, 1.00f);
colors[ImGuiCol_Button] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
colors[ImGuiCol_ButtonHovered] = ImVec4(0.33f, 0.38f, 0.46f, 1.00f);
colors[ImGuiCol_ButtonActive] = ImVec4(0.27f, 0.32f, 0.38f, 1.00f);
colors[ImGuiCol_Header] = ImVec4(0.20f, 0.25f, 0.29f, 0.55f);
colors[ImGuiCol_HeaderHovered] = ImVec4(0.33f, 0.38f, 0.46f, 1.00f);
colors[ImGuiCol_HeaderActive] = ImVec4(0.27f, 0.32f, 0.38f, 1.00f);
colors[ImGuiCol_Separator] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.33f, 0.38f, 0.46f, 1.00f);
colors[ImGuiCol_SeparatorActive] = ImVec4(0.27f, 0.32f, 0.38f, 1.00f);
colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.25f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.33f, 0.38f, 0.46f, 1.00f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.27f, 0.32f, 0.38f, 1.00f);
colors[ImGuiCol_Tab] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
colors[ImGuiCol_TabHovered] = ImVec4(0.33f, 0.38f, 0.46f, 1.00f);
colors[ImGuiCol_TabActive] = ImVec4(0.27f, 0.32f, 0.38f, 1.00f);
colors[ImGuiCol_TabUnfocused] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
}
-6
View File
@@ -1,6 +0,0 @@
#pragma once
#include <imgui.h>
namespace ImGui {
void StyleColorsDarker(ImGuiStyle* dst = nullptr);
} // namespace ImGui
+2 -1
View File
@@ -2,6 +2,7 @@
#include "common/log.h"
#include "cpu_core.h"
#include "dma.h"
#include "host.h"
#include "imgui.h"
#include "interrupt_controller.h"
#include "system.h"
@@ -722,7 +723,7 @@ void MDEC::HandleSetScaleCommand()
void MDEC::DrawDebugStateWindow()
{
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
const float framebuffer_scale = Host::GetOSDScale();
ImGui::SetNextWindowSize(ImVec2(300.0f * framebuffer_scale, 350.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("MDEC State", nullptr))
+9 -7
View File
@@ -3,7 +3,7 @@
#include "common/file_system.h"
#include "common/log.h"
#include "common/string_util.h"
#include "host_interface.h"
#include "host.h"
#include "system.h"
#include "util/state_wrapper.h"
#include <cstdio>
@@ -273,8 +273,8 @@ std::unique_ptr<MemoryCard> MemoryCard::Open(std::string_view filename)
if (!mc->LoadFromFile())
{
Log_InfoPrintf("Memory card at '%s' could not be read, formatting.", mc->m_filename.c_str());
g_host_interface->AddFormattedOSDMessage(
5.0f, g_host_interface->TranslateString("OSDMessage", "Memory card at '%s' could not be read, formatting."),
Host::AddFormattedOSDMessage(
5.0f, Host::TranslateString("OSDMessage", "Memory card at '%s' could not be read, formatting."),
mc->m_filename.c_str());
mc->Format();
}
@@ -309,8 +309,8 @@ bool MemoryCard::SaveIfChanged(bool display_osd_message)
{
if (display_osd_message)
{
g_host_interface->AddFormattedOSDMessage(
20.0f, g_host_interface->TranslateString("OSDMessage", "Failed to save memory card to '%s'"),
Host::AddFormattedOSDMessage(
20.0f, Host::TranslateString("OSDMessage", "Failed to save memory card to '%s'"),
m_filename.c_str());
}
@@ -318,8 +318,10 @@ bool MemoryCard::SaveIfChanged(bool display_osd_message)
}
if (display_osd_message)
g_host_interface->AddFormattedOSDMessage(
2.0f, g_host_interface->TranslateString("OSDMessage", "Saved memory card to '%s'"), m_filename.c_str());
{
Host::AddFormattedOSDMessage(
2.0f, Host::TranslateString("OSDMessage", "Saved memory card to '%s'"), m_filename.c_str());
}
return true;
}
-1
View File
@@ -4,7 +4,6 @@
#include "common/log.h"
#include "common/path.h"
#include "common/string_util.h"
#include "host_interface.h"
#include "system.h"
#include "util/shiftjis.h"
#include "util/state_wrapper.h"
-314
View File
@@ -1,314 +0,0 @@
#include "namco_guncon.h"
#include "common/assert.h"
#include "common/log.h"
#include "gpu.h"
#include "host_display.h"
#include "host_interface.h"
#include "resources.h"
#include "system.h"
#include "util/state_wrapper.h"
#include <array>
Log_SetChannel(NamcoGunCon);
NamcoGunCon::NamcoGunCon() = default;
NamcoGunCon::~NamcoGunCon() = default;
ControllerType NamcoGunCon::GetType() const
{
return ControllerType::NamcoGunCon;
}
std::optional<s32> NamcoGunCon::GetAxisCodeByName(std::string_view axis_name) const
{
return StaticGetAxisCodeByName(axis_name);
}
std::optional<s32> NamcoGunCon::GetButtonCodeByName(std::string_view button_name) const
{
return StaticGetButtonCodeByName(button_name);
}
void NamcoGunCon::Reset()
{
m_transfer_state = TransferState::Idle;
}
bool NamcoGunCon::DoState(StateWrapper& sw, bool apply_input_state)
{
if (!Controller::DoState(sw, apply_input_state))
return false;
u16 button_state = m_button_state;
u16 position_x = m_position_x;
u16 position_y = m_position_y;
sw.Do(&button_state);
sw.Do(&position_x);
sw.Do(&position_y);
if (apply_input_state)
{
m_button_state = button_state;
m_position_x = position_x;
m_position_y = position_y;
}
sw.Do(&m_transfer_state);
return true;
}
bool NamcoGunCon::GetButtonState(s32 button_code) const
{
if (button_code < 0 || button_code > static_cast<s32>(Button::B))
return false;
const u16 bit = u16(1) << static_cast<u8>(button_code);
return ((m_button_state & bit) == 0);
}
void NamcoGunCon::SetButtonState(Button button, bool pressed)
{
if (button == Button::ShootOffscreen)
{
if (m_shoot_offscreen != pressed)
{
m_shoot_offscreen = pressed;
SetButtonState(Button::Trigger, pressed);
}
return;
}
static constexpr std::array<u8, static_cast<size_t>(Button::Count)> indices = {{13, 3, 14}};
if (pressed)
m_button_state &= ~(u16(1) << indices[static_cast<u8>(button)]);
else
m_button_state |= u16(1) << indices[static_cast<u8>(button)];
}
void NamcoGunCon::SetButtonState(s32 button_code, bool pressed)
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return;
SetButtonState(static_cast<Button>(button_code), pressed);
}
void NamcoGunCon::ResetTransferState()
{
m_transfer_state = TransferState::Idle;
}
bool NamcoGunCon::Transfer(const u8 data_in, u8* data_out)
{
static constexpr u16 ID = 0x5A63;
switch (m_transfer_state)
{
case TransferState::Idle:
{
*data_out = 0xFF;
if (data_in == 0x01)
{
m_transfer_state = TransferState::Ready;
return true;
}
return false;
}
case TransferState::Ready:
{
if (data_in == 0x42)
{
*data_out = Truncate8(ID);
m_transfer_state = TransferState::IDMSB;
return true;
}
*data_out = 0xFF;
return false;
}
case TransferState::IDMSB:
{
*data_out = Truncate8(ID >> 8);
m_transfer_state = TransferState::ButtonsLSB;
return true;
}
case TransferState::ButtonsLSB:
{
*data_out = Truncate8(m_button_state);
m_transfer_state = TransferState::ButtonsMSB;
return true;
}
case TransferState::ButtonsMSB:
{
*data_out = Truncate8(m_button_state >> 8);
m_transfer_state = TransferState::XLSB;
return true;
}
case TransferState::XLSB:
{
UpdatePosition();
*data_out = Truncate8(m_position_x);
m_transfer_state = TransferState::XMSB;
return true;
}
case TransferState::XMSB:
{
*data_out = Truncate8(m_position_x >> 8);
m_transfer_state = TransferState::YLSB;
return true;
}
case TransferState::YLSB:
{
*data_out = Truncate8(m_position_y);
m_transfer_state = TransferState::YMSB;
return true;
}
case TransferState::YMSB:
{
*data_out = Truncate8(m_position_y >> 8);
m_transfer_state = TransferState::Idle;
return false;
}
default:
{
UnreachableCode();
return false;
}
}
}
void NamcoGunCon::UpdatePosition()
{
// get screen coordinates
const HostDisplay* display = g_host_interface->GetDisplay();
const s32 mouse_x = display->GetMousePositionX();
const s32 mouse_y = display->GetMousePositionY();
// are we within the active display area?
u32 tick, line;
if (mouse_x < 0 || mouse_y < 0 ||
!g_gpu->ConvertScreenCoordinatesToBeamTicksAndLines(mouse_x, mouse_y, m_x_scale, &tick, &line) ||
m_shoot_offscreen)
{
Log_DebugPrintf("Lightgun out of range for window coordinates %d,%d", mouse_x, mouse_y);
m_position_x = 0x01;
m_position_y = 0x0A;
return;
}
// 8MHz units for X = 44100*768*11/7 = 53222400 / 8000000 = 6.6528
const double divider = static_cast<double>(g_gpu->GetCRTCFrequency()) / 8000000.0;
m_position_x = static_cast<u16>(static_cast<float>(tick) / static_cast<float>(divider));
m_position_y = static_cast<u16>(line);
Log_DebugPrintf("Lightgun window coordinates %d,%d -> tick %u line %u 8mhz ticks %u", mouse_x, mouse_y, tick, line,
m_position_x);
}
std::unique_ptr<NamcoGunCon> NamcoGunCon::Create()
{
return std::make_unique<NamcoGunCon>();
}
std::optional<s32> NamcoGunCon::StaticGetAxisCodeByName(std::string_view button_name)
{
return std::nullopt;
}
std::optional<s32> NamcoGunCon::StaticGetButtonCodeByName(std::string_view button_name)
{
#define BUTTON(name) \
if (button_name == #name) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Button::name))); \
}
BUTTON(Trigger);
BUTTON(ShootOffscreen);
BUTTON(A);
BUTTON(B);
return std::nullopt;
#undef BUTTON
}
Controller::AxisList NamcoGunCon::StaticGetAxisNames()
{
return {};
}
Controller::ButtonList NamcoGunCon::StaticGetButtonNames()
{
return {{TRANSLATABLE("NamcoGunCon", "Trigger"), static_cast<s32>(Button::Trigger)},
{TRANSLATABLE("NamcoGunCon", "ShootOffscreen"), static_cast<s32>(Button::ShootOffscreen)},
{TRANSLATABLE("NamcoGunCon", "A"), static_cast<s32>(Button::A)},
{TRANSLATABLE("NamcoGunCon", "B"), static_cast<s32>(Button::B)}};
}
u32 NamcoGunCon::StaticGetVibrationMotorCount()
{
return 0;
}
Controller::SettingList NamcoGunCon::StaticGetSettings()
{
static constexpr std::array<SettingInfo, 3> settings = {
{{SettingInfo::Type::Path, "CrosshairImagePath", TRANSLATABLE("NamcoGunCon", "Crosshair Image Path"),
TRANSLATABLE("NamcoGunCon", "Path to an image to use as a crosshair/cursor.")},
{SettingInfo::Type::Float, "CrosshairScale", TRANSLATABLE("NamcoGunCon", "Crosshair Image Scale"),
TRANSLATABLE("NamcoGunCon", "Scale of crosshair image on screen."), "1.0", "0.0001", "100.0", "0.10"},
{SettingInfo::Type::Float, "XScale", TRANSLATABLE("NamcoGunCon", "X Scale"),
TRANSLATABLE("NamcoGunCon", "Scales X coordinates relative to the center of the screen."), "1.0", "0.01", "2.0",
"0.01"}}};
return SettingList(settings.begin(), settings.end());
}
void NamcoGunCon::LoadSettings(const char* section)
{
Controller::LoadSettings(section);
std::string path = g_host_interface->GetStringSettingValue(section, "CrosshairImagePath");
if (path != m_crosshair_image_path)
{
m_crosshair_image_path = std::move(path);
if (m_crosshair_image_path.empty() ||
!m_crosshair_image.LoadFromFile(m_crosshair_image_path.c_str()))
{
m_crosshair_image.Invalidate();
}
}
#ifndef __ANDROID__
if (!m_crosshair_image.IsValid())
{
m_crosshair_image.SetPixels(Resources::CROSSHAIR_IMAGE_WIDTH, Resources::CROSSHAIR_IMAGE_HEIGHT,
Resources::CROSSHAIR_IMAGE_DATA.data());
}
#endif
m_crosshair_image_scale = g_host_interface->GetFloatSettingValue(section, "CrosshairScale", 1.0f);
m_x_scale = g_host_interface->GetFloatSettingValue(section, "XScale", 1.0f);
}
bool NamcoGunCon::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode)
{
if (!m_crosshair_image.IsValid())
return false;
*image = &m_crosshair_image;
*image_scale = m_crosshair_image_scale;
*relative_mode = false;
return true;
}
+108 -146
View File
@@ -1,12 +1,16 @@
#include "negcon.h"
#include "common/assert.h"
#include "common/log.h"
#include "host_interface.h"
#include "host.h"
#include "system.h"
#include "util/state_wrapper.h"
#include <array>
#include <cmath>
NeGcon::NeGcon()
// Mapping of Button to index of corresponding bit in m_button_state
static constexpr std::array<u8, static_cast<size_t>(NeGcon::Button::Count)> s_button_indices = {3, 4, 5, 6,
7, 11, 12, 13};
NeGcon::NeGcon(u32 index) : Controller(index)
{
m_axis_state.fill(0x00);
m_axis_state[static_cast<u8>(Axis::Steering)] = 0x80;
@@ -19,16 +23,6 @@ ControllerType NeGcon::GetType() const
return ControllerType::NeGcon;
}
std::optional<s32> NeGcon::GetAxisCodeByName(std::string_view axis_name) const
{
return StaticGetAxisCodeByName(axis_name);
}
std::optional<s32> NeGcon::GetButtonCodeByName(std::string_view button_name) const
{
return StaticGetButtonCodeByName(button_name);
}
void NeGcon::Reset()
{
m_transfer_state = TransferState::Idle;
@@ -48,73 +42,73 @@ bool NeGcon::DoState(StateWrapper& sw, bool apply_input_state)
return true;
}
float NeGcon::GetAxisState(s32 axis_code) const
float NeGcon::GetBindState(u32 index) const
{
if (axis_code < 0 || axis_code >= static_cast<s32>(Axis::Count))
return 0.0f;
if (axis_code == static_cast<s32>(Axis::Steering))
return (((static_cast<float>(m_axis_state[static_cast<s32>(Axis::Steering)]) / 255.0f) * 2.0f) - 1.0f);
else
return (static_cast<float>(m_axis_state[static_cast<s32>(axis_code)]) / 255.0f);
}
void NeGcon::SetAxisState(s32 axis_code, float value)
{
if (axis_code < 0 || axis_code >= static_cast<s32>(Axis::Count))
return;
// Steering Axis: -1..1 -> 0..255
if (axis_code == static_cast<s32>(Axis::Steering))
if (index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringLeft)) ||
index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringRight)))
{
const float float_value =
(std::abs(value) < m_steering_deadzone) ?
0.0f :
std::copysign((std::abs(value) - m_steering_deadzone) / (1.0f - m_steering_deadzone), value);
const u8 u8_value = static_cast<u8>(std::clamp(std::round(((float_value + 1.0f) / 2.0f) * 255.0f), 0.0f, 255.0f));
SetAxisState(static_cast<Axis>(axis_code), u8_value);
return;
return static_cast<float>(m_half_axis_state[index - static_cast<u32>(Button::Count)]) * (1.0f / 255.0f);
}
else if (index >= static_cast<u32>(Button::Count))
{
// less one because of the two steering axes
const u32 sub_index = index - (static_cast<u32>(Button::Count) + 1);
if (sub_index >= m_axis_state.size())
return 0.0f;
// I, II, L: -1..1 -> 0..255
const u8 u8_value = static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f));
SetAxisState(static_cast<Axis>(axis_code), u8_value);
}
void NeGcon::SetAxisState(Axis axis, u8 value)
{
m_axis_state[static_cast<u8>(axis)] = value;
}
bool NeGcon::GetButtonState(s32 button_code) const
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return false;
const u16 bit = u16(1) << static_cast<u8>(button_code);
return ((m_button_state & bit) == 0);
}
void NeGcon::SetButtonState(s32 button_code, bool pressed)
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return;
SetButtonState(static_cast<Button>(button_code), pressed);
}
void NeGcon::SetButtonState(Button button, bool pressed)
{
// Mapping of Button to index of corresponding bit in m_button_state
static constexpr std::array<u8, static_cast<size_t>(Button::Count)> indices = {3, 4, 5, 6, 7, 11, 12, 13};
if (pressed)
m_button_state &= ~(u16(1) << indices[static_cast<u8>(button)]);
return static_cast<float>(m_axis_state[sub_index]) * (1.0f / 255.0f);
}
else
m_button_state |= u16(1) << indices[static_cast<u8>(button)];
{
const u32 bit = s_button_indices[index];
return static_cast<float>(((m_button_state >> bit) & 1u) ^ 1u);
}
}
void NeGcon::SetBindState(u32 index, float value)
{
// Steering Axis: -1..1 -> 0..255
if (index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringLeft)) ||
index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringRight)))
{
value = ApplyAnalogDeadzoneSensitivity(m_steering_deadzone, 1.0f, value);
m_half_axis_state[index - static_cast<u32>(Button::Count)] =
static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f));
// Merge left/right. Seems to be inverted.
m_axis_state[static_cast<u32>(Axis::Steering)] =
((m_half_axis_state[1] != 0) ? (127u + ((m_half_axis_state[1] + 1u) / 2u)) :
(127u - (m_half_axis_state[0] / 2u)));
}
else if (index >= static_cast<u32>(Button::Count))
{
// less one because of the two steering axes
const u32 sub_index = index - (static_cast<u32>(Button::Count) + 1);
if (sub_index >= m_axis_state.size())
return;
m_axis_state[sub_index] = static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f));
}
else if (index < static_cast<u32>(Button::Count))
{
const u16 bit = u16(1) << s_button_indices[static_cast<u8>(index)];
if (value >= 0.5f)
{
if (m_button_state & bit)
System::SetRunaheadReplayFlag();
m_button_state &= ~bit;
}
else
{
if (!(m_button_state & bit))
System::SetRunaheadReplayFlag();
m_button_state |= bit;
}
}
}
u32 NeGcon::GetButtonStateBits() const
@@ -221,87 +215,55 @@ bool NeGcon::Transfer(const u8 data_in, u8* data_out)
}
}
std::unique_ptr<NeGcon> NeGcon::Create()
std::unique_ptr<NeGcon> NeGcon::Create(u32 index)
{
return std::make_unique<NeGcon>();
return std::make_unique<NeGcon>(index);
}
std::optional<s32> NeGcon::StaticGetAxisCodeByName(std::string_view axis_name)
{
#define AXIS(name) \
if (axis_name == #name) \
static const Controller::ControllerBindingInfo s_binding_info[] = {
#define BUTTON(name, display_name, button, genb) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Axis::name))); \
name, display_name, static_cast<u32>(button), Controller::ControllerBindingType::Button, genb \
}
#define AXIS(name, display_name, halfaxis, genb) \
{ \
name, display_name, static_cast<u32>(NeGcon::Button::Count) + static_cast<u32>(halfaxis), \
Controller::ControllerBindingType::HalfAxis, genb \
}
AXIS(Steering);
AXIS(I);
AXIS(II);
AXIS(L);
return std::nullopt;
BUTTON("Up", "D-Pad Up", NeGcon::Button::Up, GenericInputBinding::DPadUp),
BUTTON("Right", "D-Pad Right", NeGcon::Button::Right, GenericInputBinding::DPadRight),
BUTTON("Down", "D-Pad Down", NeGcon::Button::Down, GenericInputBinding::DPadDown),
BUTTON("Left", "D-Pad Left", NeGcon::Button::Left, GenericInputBinding::DPadLeft),
BUTTON("Start", "Start", NeGcon::Button::Start, GenericInputBinding::Start),
BUTTON("A", "A Button", NeGcon::Button::A, GenericInputBinding::Circle),
BUTTON("B", "B Button", NeGcon::Button::B, GenericInputBinding::Triangle),
AXIS("I", "I Button", NeGcon::HalfAxis::I, GenericInputBinding::L2),
AXIS("II", "II Button", NeGcon::HalfAxis::II, GenericInputBinding::R2),
AXIS("L", "Left Trigger", NeGcon::HalfAxis::L, GenericInputBinding::L1),
BUTTON("R", "Right Trigger", NeGcon::Button::R, GenericInputBinding::R1),
AXIS("SteeringLeft", "Steering (Twist) Left", NeGcon::HalfAxis::SteeringLeft, GenericInputBinding::LeftStickLeft),
AXIS("SteeringRight", "Steering (Twist) Right", NeGcon::HalfAxis::SteeringRight, GenericInputBinding::LeftStickRight),
#undef AXIS
}
std::optional<s32> NeGcon::StaticGetButtonCodeByName(std::string_view button_name)
{
#define BUTTON(name) \
if (button_name == #name) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Button::name))); \
}
BUTTON(Up);
BUTTON(Down);
BUTTON(Left);
BUTTON(Right);
BUTTON(A);
BUTTON(B);
BUTTON(R);
BUTTON(Start);
return std::nullopt;
#undef BUTTON
}
};
Controller::AxisList NeGcon::StaticGetAxisNames()
static const SettingInfo s_settings[] = {
{SettingInfo::Type::Float, "SteeringDeadzone", TRANSLATABLE("NeGcon", "Steering Axis Deadzone"),
TRANSLATABLE("NeGcon", "Sets deadzone size for steering axis."), "0.00f", "0.00f", "0.99f", "0.01f"}};
const Controller::ControllerInfo NeGcon::INFO = {ControllerType::NeGcon,
"NeGcon",
TRANSLATABLE("ControllerType", "NeGcon"),
s_binding_info,
countof(s_binding_info),
s_settings,
countof(s_settings),
Controller::VibrationCapabilities::NoVibration};
void NeGcon::LoadSettings(SettingsInterface& si, const char* section)
{
return {{TRANSLATABLE("NeGcon", "Steering"), static_cast<s32>(Axis::Steering), AxisType::Full},
{TRANSLATABLE("NeGcon", "I"), static_cast<s32>(Axis::I), AxisType::Half},
{TRANSLATABLE("NeGcon", "II"), static_cast<s32>(Axis::II), AxisType::Half},
{TRANSLATABLE("NeGcon", "L"), static_cast<s32>(Axis::L), AxisType::Half}};
}
Controller::ButtonList NeGcon::StaticGetButtonNames()
{
return {{TRANSLATABLE("NeGcon", "Up"), static_cast<s32>(Button::Up)},
{TRANSLATABLE("NeGcon", "Down"), static_cast<s32>(Button::Down)},
{TRANSLATABLE("NeGcon", "Left"), static_cast<s32>(Button::Left)},
{TRANSLATABLE("NeGcon", "Right"), static_cast<s32>(Button::Right)},
{TRANSLATABLE("NeGcon", "A"), static_cast<s32>(Button::A)},
{TRANSLATABLE("NeGcon", "B"), static_cast<s32>(Button::B)},
{TRANSLATABLE("NeGcon", "R"), static_cast<s32>(Button::R)},
{TRANSLATABLE("NeGcon", "Start"), static_cast<s32>(Button::Start)}};
}
u32 NeGcon::StaticGetVibrationMotorCount()
{
return 0;
}
Controller::SettingList NeGcon::StaticGetSettings()
{
static constexpr std::array<SettingInfo, 1> settings = {
{{SettingInfo::Type::Float, "SteeringDeadzone", TRANSLATABLE("NeGcon", "Steering Axis Deadzone"),
TRANSLATABLE("NeGcon", "Sets deadzone size for steering axis."), "0.00f", "0.00f", "0.99f", "0.01f"}}};
return SettingList(settings.begin(), settings.end());
}
void NeGcon::LoadSettings(const char* section)
{
Controller::LoadSettings(section);
m_steering_deadzone = g_host_interface->GetFloatSettingValue(section, "SteeringDeadzone", 0.10f);
Controller::LoadSettings(si, section);
m_steering_deadzone = si.GetFloatValue(section, "SteeringDeadzone", 0.10f);
}
+21 -19
View File
@@ -30,40 +30,38 @@ public:
Count
};
NeGcon();
enum class HalfAxis : u8
{
SteeringLeft,
SteeringRight,
I,
II,
L,
Count
};
static const Controller::ControllerInfo INFO;
NeGcon(u32 index);
~NeGcon() override;
static std::unique_ptr<NeGcon> Create();
static std::optional<s32> StaticGetAxisCodeByName(std::string_view axis_name);
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
static AxisList StaticGetAxisNames();
static ButtonList StaticGetButtonNames();
static u32 StaticGetVibrationMotorCount();
static SettingList StaticGetSettings();
static std::unique_ptr<NeGcon> Create(u32 index);
ControllerType GetType() const override;
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
std::optional<s32> GetButtonCodeByName(std::string_view button_name) const override;
void Reset() override;
bool DoState(StateWrapper& sw, bool apply_input_state) override;
float GetAxisState(s32 axis_code) const override;
void SetAxisState(s32 axis_code, float value) override;
bool GetButtonState(s32 button_code) const override;
void SetButtonState(s32 button_code, bool pressed) override;
float GetBindState(u32 index) const override;
void SetBindState(u32 index, float value) override;
void ResetTransferState() override;
bool Transfer(const u8 data_in, u8* data_out) override;
void SetAxisState(Axis axis, u8 value);
void SetButtonState(Button button, bool pressed);
u32 GetButtonStateBits() const override;
std::optional<u32> GetAnalogInputBytes() const override;
void LoadSettings(const char* section) override;
void LoadSettings(SettingsInterface& si, const char* section) override;
private:
enum class TransferState : u8
@@ -81,10 +79,14 @@ private:
std::array<u8, static_cast<u8>(Axis::Count)> m_axis_state{};
// steering, merged to m_axis_state
std::array<u8, 2> m_half_axis_state{};
// buttons are active low; bits 0-2, 8-10, 14-15 are not used and are always high
u16 m_button_state = UINT16_C(0xFFFF);
TransferState m_transfer_state = TransferState::Idle;
float m_steering_deadzone = 0.00f;
};
+18 -21
View File
@@ -1,7 +1,7 @@
#include "pad.h"
#include "common/log.h"
#include "controller.h"
#include "host_interface.h"
#include "host.h"
#include "interrupt_controller.h"
#include "memory_card.h"
#include "multitap.h"
@@ -66,17 +66,17 @@ bool Pad::DoStateController(StateWrapper& sw, u32 i)
// UI notification portion is separated from emulation portion (intentional condition check redundancy)
if (g_settings.load_devices_from_save_states)
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
10.0f,
g_host_interface->TranslateString(
"OSDMessage", "Save state contains controller type %s in port %u, but %s is used. Switching."),
Host::TranslateString("OSDMessage",
"Save state contains controller type %s in port %u, but %s is used. Switching."),
Settings::GetControllerTypeName(state_controller_type), i + 1u,
Settings::GetControllerTypeName(controller_type));
}
else
{
g_host_interface->AddFormattedOSDMessage(
10.0f, g_host_interface->TranslateString("OSDMessage", "Ignoring mismatched controller type %s in port %u."),
Host::AddFormattedOSDMessage(
10.0f, Host::TranslateString("OSDMessage", "Ignoring mismatched controller type %s in port %u."),
Settings::GetControllerTypeName(state_controller_type), i + 1u);
}
@@ -129,10 +129,10 @@ bool Pad::DoStateMemcard(StateWrapper& sw, u32 i)
if (card_present_in_state && !m_memory_cards[i] && g_settings.load_devices_from_save_states)
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
20.0f,
g_host_interface->TranslateString(
"OSDMessage", "Memory card %u present in save state but not in system. Creating temporary card."),
Host::TranslateString("OSDMessage",
"Memory card %u present in save state but not in system. Creating temporary card."),
i + 1u);
m_memory_cards[i] = MemoryCard::Create();
}
@@ -170,10 +170,10 @@ bool Pad::DoStateMemcard(StateWrapper& sw, u32 i)
}
else
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
20.0f,
g_host_interface->TranslateString(
"OSDMessage", "Memory card %u from save state does match current card data. Simulating replugging."),
Host::TranslateString("OSDMessage",
"Memory card %u from save state does match current card data. Simulating replugging."),
i + 1u);
// this is a potentially serious issue - some games cache info from memcards and jumping around
@@ -188,10 +188,9 @@ bool Pad::DoStateMemcard(StateWrapper& sw, u32 i)
}
else
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
20.0f,
g_host_interface->TranslateString("OSDMessage",
"Memory card %u present in save state but not in system. Ignoring card."),
Host::TranslateString("OSDMessage", "Memory card %u present in save state but not in system. Ignoring card."),
i + 1u);
}
@@ -202,19 +201,17 @@ bool Pad::DoStateMemcard(StateWrapper& sw, u32 i)
{
if (g_settings.load_devices_from_save_states)
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
20.0f,
g_host_interface->TranslateString("OSDMessage",
"Memory card %u present in system but not in save state. Removing card."),
Host::TranslateString("OSDMessage", "Memory card %u present in system but not in save state. Removing card."),
i + 1u);
m_memory_cards[i].reset();
}
else
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
20.0f,
g_host_interface->TranslateString("OSDMessage",
"Memory card %u present in system but not in save state. Replugging card."),
Host::TranslateString("OSDMessage", "Memory card %u present in system but not in save state. Replugging card."),
i + 1u);
m_memory_cards[i]->Reset();
}
+44 -80
View File
@@ -2,17 +2,19 @@
#include "common/assert.h"
#include "common/log.h"
#include "gpu.h"
#include "host.h"
#include "host_display.h"
#include "host_interface.h"
#include "system.h"
#include "util/state_wrapper.h"
#include <array>
Log_SetChannel(PlayStationMouse);
PlayStationMouse::PlayStationMouse()
static constexpr std::array<u8, static_cast<size_t>(PlayStationMouse::Button::Count)> s_button_indices = {{11, 10}};
PlayStationMouse::PlayStationMouse(u32 index) : Controller(index)
{
m_last_host_position_x = g_host_interface->GetDisplay()->GetMousePositionX();
m_last_host_position_y = g_host_interface->GetDisplay()->GetMousePositionY();
m_last_host_position_x = g_host_display->GetMousePositionX();
m_last_host_position_y = g_host_display->GetMousePositionY();
}
PlayStationMouse::~PlayStationMouse() = default;
@@ -22,16 +24,6 @@ ControllerType PlayStationMouse::GetType() const
return ControllerType::PlayStationMouse;
}
std::optional<s32> PlayStationMouse::GetAxisCodeByName(std::string_view axis_name) const
{
return StaticGetAxisCodeByName(axis_name);
}
std::optional<s32> PlayStationMouse::GetButtonCodeByName(std::string_view button_name) const
{
return StaticGetButtonCodeByName(button_name);
}
void PlayStationMouse::Reset()
{
m_transfer_state = TransferState::Idle;
@@ -59,30 +51,24 @@ bool PlayStationMouse::DoState(StateWrapper& sw, bool apply_input_state)
return true;
}
bool PlayStationMouse::GetButtonState(s32 button_code) const
float PlayStationMouse::GetBindState(u32 index) const
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return false;
if (index >= s_button_indices.size())
return 0.0f;
const u16 bit = u16(1) << static_cast<u8>(button_code);
return ((m_button_state & bit) == 0);
const u32 bit = s_button_indices[index];
return static_cast<float>(((m_button_state >> bit) & 1u) ^ 1u);
}
void PlayStationMouse::SetButtonState(Button button, bool pressed)
void PlayStationMouse::SetBindState(u32 index, float value)
{
static constexpr std::array<u8, static_cast<size_t>(Button::Count)> indices = {{11, 10}};
if (pressed)
m_button_state &= ~(u16(1) << indices[static_cast<u8>(button)]);
else
m_button_state |= u16(1) << indices[static_cast<u8>(button)];
}
void PlayStationMouse::SetButtonState(s32 button_code, bool pressed)
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
if (index > s_button_indices.size())
return;
SetButtonState(static_cast<Button>(button_code), pressed);
if (value >= 0.5f)
m_button_state &= ~(u16(1) << s_button_indices[index]);
else
m_button_state |= u16(1) << s_button_indices[index];
}
void PlayStationMouse::ResetTransferState()
@@ -168,9 +154,8 @@ bool PlayStationMouse::Transfer(const u8 data_in, u8* data_out)
void PlayStationMouse::UpdatePosition()
{
// get screen coordinates
const HostDisplay* display = g_host_interface->GetDisplay();
const s32 mouse_x = display->GetMousePositionX();
const s32 mouse_y = display->GetMousePositionY();
const s32 mouse_x = g_host_display->GetMousePositionX();
const s32 mouse_y = g_host_display->GetMousePositionY();
const s32 delta_x = mouse_x - m_last_host_position_x;
const s32 delta_y = mouse_y - m_last_host_position_y;
m_last_host_position_x = mouse_x;
@@ -183,63 +168,42 @@ void PlayStationMouse::UpdatePosition()
m_delta_y = static_cast<s8>(std::clamp<s32>(delta_y, std::numeric_limits<s8>::min(), std::numeric_limits<s8>::max()));
}
std::unique_ptr<PlayStationMouse> PlayStationMouse::Create()
std::unique_ptr<PlayStationMouse> PlayStationMouse::Create(u32 index)
{
return std::make_unique<PlayStationMouse>();
return std::make_unique<PlayStationMouse>(index);
}
std::optional<s32> PlayStationMouse::StaticGetAxisCodeByName(std::string_view button_name)
{
return std::nullopt;
}
std::optional<s32> PlayStationMouse::StaticGetButtonCodeByName(std::string_view button_name)
{
#define BUTTON(name) \
if (button_name == #name) \
static const Controller::ControllerBindingInfo s_binding_info[] = {
#define BUTTON(name, display_name, button, genb) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Button::name))); \
name, display_name, static_cast<u32>(button), Controller::ControllerBindingType::Button, genb \
}
BUTTON(Left);
BUTTON(Right);
return std::nullopt;
BUTTON("Left", "Left Button", PlayStationMouse::Button::Left, GenericInputBinding::Cross),
BUTTON("Right", "Right Button", PlayStationMouse::Button::Right, GenericInputBinding::Circle),
#undef BUTTON
}
};
Controller::AxisList PlayStationMouse::StaticGetAxisNames()
static const SettingInfo s_settings[] = {
{SettingInfo::Type::Boolean, "RelativeMouseMode", TRANSLATABLE("PlayStationMouse", "Relative Mouse Mode"),
TRANSLATABLE("PlayStationMouse", "Locks the mouse cursor to the window, use for FPS games."), "false"},
};
const Controller::ControllerInfo PlayStationMouse::INFO = {ControllerType::PlayStationMouse,
"PlayStationMouse",
TRANSLATABLE("ControllerType", "PlayStation Mouse"),
s_binding_info,
countof(s_binding_info),
s_settings,
countof(s_settings),
Controller::VibrationCapabilities::NoVibration};
void PlayStationMouse::LoadSettings(SettingsInterface& si, const char* section)
{
return {};
}
Controller::LoadSettings(si, section);
Controller::ButtonList PlayStationMouse::StaticGetButtonNames()
{
return {{TRANSLATABLE("PlayStationMouse", "Left"), static_cast<s32>(Button::Left)},
{TRANSLATABLE("PlayStationMouse", "Right"), static_cast<s32>(Button::Right)}};
}
u32 PlayStationMouse::StaticGetVibrationMotorCount()
{
return 0;
}
Controller::SettingList PlayStationMouse::StaticGetSettings()
{
static constexpr std::array<SettingInfo, 1> settings = {{
{SettingInfo::Type::Boolean, "RelativeMouseMode", TRANSLATABLE("PlayStationMouse", "Relative Mouse Mode"),
TRANSLATABLE("PlayStationMouse", "Locks the mouse cursor to the window, use for FPS games."), "false"},
}};
return SettingList(settings.begin(), settings.end());
}
void PlayStationMouse::LoadSettings(const char* section)
{
Controller::LoadSettings(section);
m_use_relative_mode = g_host_interface->GetBoolSettingValue(section, "RelativeMouseMode");
m_use_relative_mode = si.GetBoolValue(section, "RelativeMouseMode", false);
}
bool PlayStationMouse::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode)
+7 -15
View File
@@ -14,33 +14,25 @@ public:
Count
};
PlayStationMouse();
static const Controller::ControllerInfo INFO;
PlayStationMouse(u32 index);
~PlayStationMouse() override;
static std::unique_ptr<PlayStationMouse> Create();
static std::optional<s32> StaticGetAxisCodeByName(std::string_view button_name);
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
static AxisList StaticGetAxisNames();
static ButtonList StaticGetButtonNames();
static u32 StaticGetVibrationMotorCount();
static SettingList StaticGetSettings();
static std::unique_ptr<PlayStationMouse> Create(u32 index);
ControllerType GetType() const override;
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
std::optional<s32> GetButtonCodeByName(std::string_view button_name) const override;
void Reset() override;
bool DoState(StateWrapper& sw, bool apply_input_state) override;
bool GetButtonState(s32 button_code) const override;
void SetButtonState(s32 button_code, bool pressed) override;
float GetBindState(u32 index) const override;
void SetBindState(u32 index, float value) override;
void ResetTransferState() override;
bool Transfer(const u8 data_in, u8* data_out) override;
void SetButtonState(Button button, bool pressed);
void LoadSettings(const char* section) override;
void LoadSettings(SettingsInterface& si, const char* section) override;
bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode) override;
private:
+305 -36
View File
@@ -1,14 +1,19 @@
#include "settings.h"
#include "achievements.h"
#include "common/assert.h"
#include "common/file_system.h"
#include "common/log.h"
#include "common/make_array.h"
#include "common/path.h"
#include "common/string_util.h"
#include "controller.h"
#include "host.h"
#include "host_display.h"
#include "host_interface.h"
#include <algorithm>
#include <array>
#include <cctype>
#include <numeric>
Log_SetChannel(Settings);
Settings g_settings;
@@ -68,7 +73,16 @@ float SettingInfo::FloatStepValue() const
return step_value ? StringUtil::FromChars<float>(step_value).value_or(fallback_value) : fallback_value;
}
Settings::Settings() = default;
Settings::Settings()
{
controller_types[0] = DEFAULT_CONTROLLER_1_TYPE;
memory_card_types[0] = DEFAULT_MEMORY_CARD_1_TYPE;
for (u32 i = 1; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{
controller_types[i] = DEFAULT_CONTROLLER_2_TYPE;
memory_card_types[i] = DEFAULT_MEMORY_CARD_2_TYPE;
}
}
bool Settings::HasAnyPerGameMemoryCards() const
{
@@ -151,6 +165,7 @@ void Settings::Load(SettingsInterface& si)
pause_on_focus_loss = si.GetBoolValue("Main", "PauseOnFocusLoss", false);
pause_on_menu = si.GetBoolValue("Main", "PauseOnMenu", true);
save_state_on_exit = si.GetBoolValue("Main", "SaveStateOnExit", true);
create_save_state_backups = si.GetBoolValue("Main", "CreateSaveStateBackups", true);
confim_power_off = si.GetBoolValue("Main", "ConfirmPowerOff", true);
load_devices_from_save_states = si.GetBoolValue("Main", "LoadDevicesFromSaveStates", false);
apply_game_settings = si.GetBoolValue("Main", "ApplyGameSettings", true);
@@ -186,8 +201,8 @@ void Settings::Load(SettingsInterface& si)
gpu_use_thread = si.GetBoolValue("GPU", "UseThread", true);
gpu_use_software_renderer_for_readbacks = si.GetBoolValue("GPU", "UseSoftwareRendererForReadbacks", false);
gpu_threaded_presentation = si.GetBoolValue("GPU", "ThreadedPresentation", true);
gpu_true_color = si.GetBoolValue("GPU", "TrueColor", false);
gpu_scaled_dithering = si.GetBoolValue("GPU", "ScaledDithering", false);
gpu_true_color = si.GetBoolValue("GPU", "TrueColor", true);
gpu_scaled_dithering = si.GetBoolValue("GPU", "ScaledDithering", true);
gpu_texture_filter =
ParseTextureFilterName(
si.GetStringValue("GPU", "TextureFilter", GetTextureFilterName(DEFAULT_GPU_TEXTURE_FILTER)).c_str())
@@ -233,17 +248,21 @@ void Settings::Load(SettingsInterface& si)
display_post_processing = si.GetBoolValue("Display", "PostProcessing", false);
display_show_osd_messages = si.GetBoolValue("Display", "ShowOSDMessages", true);
display_show_fps = si.GetBoolValue("Display", "ShowFPS", false);
display_show_vps = si.GetBoolValue("Display", "ShowVPS", false);
display_show_speed = si.GetBoolValue("Display", "ShowSpeed", false);
display_show_resolution = si.GetBoolValue("Display", "ShowResolution", false);
display_show_cpu = si.GetBoolValue("Display", "ShowCPU", false);
display_show_status_indicators = si.GetBoolValue("Display", "ShowStatusIndicators", true);
display_show_inputs = si.GetBoolValue("Display", "ShowInputs", false);
display_show_enhancements = si.GetBoolValue("Display", "ShowEnhancements", false);
display_all_frames = si.GetBoolValue("Display", "DisplayAllFrames", false);
display_internal_resolution_screenshots = si.GetBoolValue("Display", "InternalResolutionScreenshots", false);
video_sync_enabled = si.GetBoolValue("Display", "VSync", DEFAULT_VSYNC_VALUE);
display_post_process_chain = si.GetStringValue("Display", "PostProcessChain", "");
display_max_fps = si.GetFloatValue("Display", "MaxFPS", DEFAULT_DISPLAY_MAX_FPS);
display_osd_scale = si.GetFloatValue("Display", "OSDScale", DEFAULT_OSD_SCALE);
cdrom_readahead_sectors = static_cast<u8>(si.GetIntValue("CDROM", "ReadaheadSectors", DEFAULT_CDROM_READAHEAD_SECTORS));
cdrom_readahead_sectors =
static_cast<u8>(si.GetIntValue("CDROM", "ReadaheadSectors", DEFAULT_CDROM_READAHEAD_SECTORS));
cdrom_region_check = si.GetBoolValue("CDROM", "RegionCheck", false);
cdrom_load_image_to_ram = si.GetBoolValue("CDROM", "LoadImageToRAM", false);
cdrom_mute_cd_audio = si.GetBoolValue("CDROM", "MuteCDAudio", false);
@@ -255,7 +274,7 @@ void Settings::Load(SettingsInterface& si)
.value_or(DEFAULT_AUDIO_BACKEND);
audio_output_volume = si.GetIntValue("Audio", "OutputVolume", 100);
audio_fast_forward_volume = si.GetIntValue("Audio", "FastForwardVolume", 100);
audio_buffer_size = si.GetIntValue("Audio", "BufferSize", HostInterface::DEFAULT_AUDIO_BUFFER_SIZE);
audio_buffer_size = si.GetIntValue("Audio", "BufferSize", DEFAULT_AUDIO_BUFFER_SIZE);
audio_resampling = si.GetBoolValue("Audio", "Resampling", true);
audio_output_muted = si.GetBoolValue("Audio", "OutputMuted", false);
audio_sync_enabled = si.GetBoolValue("Audio", "Sync", true);
@@ -269,18 +288,31 @@ void Settings::Load(SettingsInterface& si)
bios_patch_tty_enable = si.GetBoolValue("BIOS", "PatchTTYEnable", false);
bios_patch_fast_boot = si.GetBoolValue("BIOS", "PatchFastBoot", DEFAULT_FAST_BOOT_VALUE);
controller_types[0] =
ParseControllerTypeName(
si.GetStringValue("Controller1", "Type", GetControllerTypeName(DEFAULT_CONTROLLER_1_TYPE)).c_str())
.value_or(DEFAULT_CONTROLLER_1_TYPE);
multitap_mode =
ParseMultitapModeName(
si.GetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(DEFAULT_MULTITAP_MODE)).c_str())
.value_or(DEFAULT_MULTITAP_MODE);
controller_types[0] = ParseControllerTypeName(si.GetStringValue(Controller::GetSettingsSection(0).c_str(), "Type",
GetControllerTypeName(DEFAULT_CONTROLLER_1_TYPE))
.c_str())
.value_or(DEFAULT_CONTROLLER_1_TYPE);
const std::array<bool, 2> mtap_enabled = {{IsPort1MultitapEnabled(), IsPort2MultitapEnabled()}};
for (u32 i = 1; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{
controller_types[i] =
ParseControllerTypeName(si.GetStringValue(TinyString::FromFormat("Controller%u", i + 1u), "Type",
GetControllerTypeName(DEFAULT_CONTROLLER_2_TYPE))
.c_str())
.value_or(DEFAULT_CONTROLLER_2_TYPE);
// Ignore types when multitap not enabled
const auto [port, slot] = Controller::ConvertPadToPortAndSlot(i);
if (Controller::PadIsMultitapSlot(slot) && !mtap_enabled[port])
{
controller_types[i] = ControllerType::None;
continue;
}
controller_types[i] = ParseControllerTypeName(si.GetStringValue(Controller::GetSettingsSection(i).c_str(), "Type",
GetControllerTypeName(DEFAULT_CONTROLLER_2_TYPE))
.c_str())
.value_or(DEFAULT_CONTROLLER_2_TYPE);
}
memory_card_types[0] =
@@ -293,13 +325,14 @@ void Settings::Load(SettingsInterface& si)
.value_or(DEFAULT_MEMORY_CARD_2_TYPE);
memory_card_paths[0] = si.GetStringValue("MemoryCards", "Card1Path", "");
memory_card_paths[1] = si.GetStringValue("MemoryCards", "Card2Path", "");
memory_card_directory = si.GetStringValue("MemoryCards", "Directory", "");
memory_card_use_playlist_title = si.GetBoolValue("MemoryCards", "UsePlaylistTitle", true);
multitap_mode =
ParseMultitapModeName(
si.GetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(DEFAULT_MULTITAP_MODE)).c_str())
.value_or(DEFAULT_MULTITAP_MODE);
achievements_enabled = si.GetBoolValue("Cheevos", "Enabled", false);
achievements_test_mode = si.GetBoolValue("Cheevos", "TestMode", false);
achievements_unofficial_test_mode = si.GetBoolValue("Cheevos", "UnofficialTestMode", false);
achievements_use_first_disc_from_playlist = si.GetBoolValue("Cheevos", "UseFirstDiscFromPlaylist", true);
achievements_rich_presence = si.GetBoolValue("Cheevos", "RichPresence", true);
achievements_challenge_mode = si.GetBoolValue("Cheevos", "ChallengeMode", false);
log_level = ParseLogLevelName(si.GetStringValue("Logging", "LogLevel", GetLogLevelName(DEFAULT_LOG_LEVEL)).c_str())
.value_or(DEFAULT_LOG_LEVEL);
@@ -349,6 +382,7 @@ void Settings::Save(SettingsInterface& si) const
si.SetBoolValue("Main", "PauseOnFocusLoss", pause_on_focus_loss);
si.SetBoolValue("Main", "PauseOnMenu", pause_on_menu);
si.SetBoolValue("Main", "SaveStateOnExit", save_state_on_exit);
si.SetBoolValue("Main", "CreateSaveStateBackups", create_save_state_backups);
si.SetBoolValue("Main", "ConfirmPowerOff", confim_power_off);
si.SetBoolValue("Main", "LoadDevicesFromSaveStates", load_devices_from_save_states);
si.SetBoolValue("Main", "ApplyGameSettings", apply_game_settings);
@@ -410,18 +444,21 @@ void Settings::Save(SettingsInterface& si) const
si.SetBoolValue("Display", "PostProcessing", display_post_processing);
si.SetBoolValue("Display", "ShowOSDMessages", display_show_osd_messages);
si.SetBoolValue("Display", "ShowFPS", display_show_fps);
si.SetBoolValue("Display", "ShowVPS", display_show_vps);
si.SetBoolValue("Display", "ShowSpeed", display_show_speed);
si.SetBoolValue("Display", "ShowResolution", display_show_resolution);
si.SetBoolValue("Display", "ShowCPU", display_show_cpu);
si.SetBoolValue("Display", "ShowStatusIndicators", display_show_status_indicators);
si.SetBoolValue("Display", "ShowInputs", display_show_inputs);
si.SetBoolValue("Display", "ShowEnhancements", display_show_enhancements);
si.SetBoolValue("Display", "DisplayAllFrames", display_all_frames);
si.SetBoolValue("Display", "InternalResolutionScreenshots", display_internal_resolution_screenshots);
si.SetBoolValue("Display", "VSync", video_sync_enabled);
if (display_post_process_chain.empty())
si.DeleteValue("Display", "PostProcessChain");
else
si.SetStringValue("Display", "PostProcessChain", display_post_process_chain.c_str());
si.SetFloatValue("Display", "MaxFPS", display_max_fps);
si.SetFloatValue("Display", "OSDScale", display_osd_scale);
si.SetIntValue("CDROM", "ReadaheadSectors", cdrom_readahead_sectors);
si.SetBoolValue("CDROM", "RegionCheck", cdrom_region_check);
@@ -465,14 +502,17 @@ void Settings::Save(SettingsInterface& si) const
else
si.DeleteValue("MemoryCards", "Card2Path");
if (!memory_card_directory.empty())
si.SetStringValue("MemoryCards", "Directory", memory_card_directory.c_str());
else
si.DeleteValue("MemoryCards", "Directory");
si.SetBoolValue("MemoryCards", "UsePlaylistTitle", memory_card_use_playlist_title);
si.SetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(multitap_mode));
si.SetBoolValue("Cheevos", "Enabled", achievements_enabled);
si.SetBoolValue("Cheevos", "TestMode", achievements_test_mode);
si.SetBoolValue("Cheevos", "UnofficialTestMode", achievements_unofficial_test_mode);
si.SetBoolValue("Cheevos", "UseFirstDiscFromPlaylist", achievements_use_first_disc_from_playlist);
si.SetBoolValue("Cheevos", "RichPresence", achievements_rich_presence);
si.SetBoolValue("Cheevos", "ChallengeMode", achievements_challenge_mode);
si.SetStringValue("Logging", "LogLevel", GetLogLevelName(log_level));
si.SetStringValue("Logging", "LogFilter", log_filter.c_str());
si.SetBoolValue("Logging", "LogToConsole", log_to_console);
@@ -502,6 +542,104 @@ void Settings::Save(SettingsInterface& si) const
texture_replacements.dump_vram_write_height_threshold);
}
void Settings::FixIncompatibleSettings(bool display_osd_messages)
{
if (g_settings.disable_all_enhancements)
{
Log_WarningPrintf("All enhancements disabled by config setting.");
g_settings.cpu_overclock_enable = false;
g_settings.cpu_overclock_active = false;
g_settings.enable_8mb_ram = false;
g_settings.gpu_resolution_scale = 1;
g_settings.gpu_multisamples = 1;
g_settings.gpu_per_sample_shading = false;
g_settings.gpu_true_color = false;
g_settings.gpu_scaled_dithering = false;
g_settings.gpu_texture_filter = GPUTextureFilter::Nearest;
g_settings.gpu_disable_interlacing = false;
g_settings.gpu_force_ntsc_timings = false;
g_settings.gpu_widescreen_hack = false;
g_settings.gpu_pgxp_enable = false;
g_settings.gpu_24bit_chroma_smoothing = false;
g_settings.cdrom_read_speedup = 1;
g_settings.cdrom_seek_speedup = 1;
g_settings.cdrom_mute_cd_audio = false;
g_settings.texture_replacements.enable_vram_write_replacements = false;
g_settings.bios_patch_fast_boot = false;
g_settings.bios_patch_tty_enable = false;
}
if (g_settings.display_integer_scaling && g_settings.display_linear_filtering)
{
Log_WarningPrintf("Disabling linear filter due to integer upscaling.");
g_settings.display_linear_filtering = false;
}
if (g_settings.display_integer_scaling && g_settings.display_stretch)
{
Log_WarningPrintf("Disabling stretch due to integer upscaling.");
g_settings.display_stretch = false;
}
if (g_settings.gpu_pgxp_enable)
{
if (g_settings.gpu_renderer == GPURenderer::Software)
{
if (display_osd_messages)
{
Host::AddOSDMessage(
Host::TranslateStdString("OSDMessage", "PGXP is incompatible with the software renderer, disabling PGXP."),
10.0f);
}
g_settings.gpu_pgxp_enable = false;
}
}
#ifndef WITH_MMAP_FASTMEM
if (g_settings.cpu_fastmem_mode == CPUFastmemMode::MMap)
{
Log_WarningPrintf("mmap fastmem is not available on this platform, using LUT instead.");
g_settings.cpu_fastmem_mode = CPUFastmemMode::LUT;
}
#endif
#if defined(__ANDROID__) && defined(__arm__) && !defined(__aarch64__) && !defined(_M_ARM64)
if (g_settings.rewind_enable)
{
Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Rewind is not supported on 32-bit ARM for Android."),
30.0f);
g_settings.rewind_enable = false;
}
#endif
// if challenge mode is enabled, disable things like rewind since they use save states
if (Achievements::ChallengeModeActive())
{
g_settings.emulation_speed =
(g_settings.emulation_speed != 0.0f) ? std::max(g_settings.emulation_speed, 1.0f) : 0.0f;
g_settings.fast_forward_speed =
(g_settings.fast_forward_speed != 0.0f) ? std::max(g_settings.fast_forward_speed, 1.0f) : 0.0f;
g_settings.turbo_speed = (g_settings.turbo_speed != 0.0f) ? std::max(g_settings.turbo_speed, 1.0f) : 0.0f;
g_settings.rewind_enable = false;
g_settings.auto_load_cheats = false;
if (g_settings.cpu_overclock_enable && g_settings.GetCPUOverclockPercent() < 100)
{
g_settings.cpu_overclock_enable = false;
g_settings.UpdateOverclockActive();
}
g_settings.debugging.enable_gdb_server = false;
g_settings.debugging.show_vram = false;
g_settings.debugging.show_gpu_state = false;
g_settings.debugging.show_cdrom_state = false;
g_settings.debugging.show_spu_state = false;
g_settings.debugging.show_timers_state = false;
g_settings.debugging.show_mdec_state = false;
g_settings.debugging.show_dma_state = false;
g_settings.debugging.dump_cpu_to_vram_copies = false;
g_settings.debugging.dump_vram_to_cpu_copies = false;
}
}
static std::array<const char*, LOGLEVEL_COUNT> s_log_level_names = {
{"None", "Error", "Warning", "Perf", "Info", "Verbose", "Dev", "Profile", "Debug", "Trace"}};
static std::array<const char*, LOGLEVEL_COUNT> s_log_level_display_names = {
@@ -655,14 +793,12 @@ const char* Settings::GetCPUFastmemModeDisplayName(CPUFastmemMode mode)
static constexpr auto s_gpu_renderer_names = make_array(
#ifdef _WIN32
"D3D11",
"D3D12",
"D3D11", "D3D12",
#endif
"Vulkan", "OpenGL", "Software");
static constexpr auto s_gpu_renderer_display_names = make_array(
#ifdef _WIN32
TRANSLATABLE("GPURenderer", "Hardware (D3D11)"),
TRANSLATABLE("GPURenderer", "Hardware (D3D12)"),
TRANSLATABLE("GPURenderer", "Hardware (D3D11)"), TRANSLATABLE("GPURenderer", "Hardware (D3D12)"),
#endif
TRANSLATABLE("GPURenderer", "Hardware (Vulkan)"), TRANSLATABLE("GPURenderer", "Hardware (OpenGL)"),
TRANSLATABLE("GPURenderer", "Software"));
@@ -812,12 +948,11 @@ float Settings::GetDisplayAspectRatioValue() const
{
case DisplayAspectRatio::MatchWindow:
{
const HostDisplay* display = g_host_interface->GetDisplay();
if (!display)
if (!g_host_display)
return s_display_aspect_ratio_values[static_cast<int>(DEFAULT_DISPLAY_ASPECT_RATIO)];
const u32 width = display->GetWindowWidth();
const u32 height = display->GetWindowHeight() - display->GetDisplayTopMargin();
const u32 width = g_host_display->GetWindowWidth();
const u32 height = g_host_display->GetWindowHeight() - g_host_display->GetDisplayTopMargin();
return static_cast<float>(width) / static_cast<float>(height);
}
@@ -887,11 +1022,11 @@ const char* Settings::GetAudioBackendDisplayName(AudioBackend backend)
}
static std::array<const char*, 7> s_controller_type_names = {
{"None", "DigitalController", "AnalogController", "AnalogJoystick", "NamcoGunCon", "PlayStationMouse", "NeGcon"}};
{"None", "DigitalController", "AnalogController", "AnalogJoystick", "GunCon", "PlayStationMouse", "NeGcon"}};
static std::array<const char*, 7> s_controller_display_names = {
{TRANSLATABLE("ControllerType", "None"), TRANSLATABLE("ControllerType", "Digital Controller"),
TRANSLATABLE("ControllerType", "Analog Controller (DualShock)"), TRANSLATABLE("ControllerType", "Analog Joystick"),
TRANSLATABLE("ControllerType", "Namco GunCon"), TRANSLATABLE("ControllerType", "PlayStation Mouse"),
TRANSLATABLE("ControllerType", "GunCon"), TRANSLATABLE("ControllerType", "PlayStation Mouse"),
TRANSLATABLE("ControllerType", "NeGcon")}};
std::optional<ControllerType> Settings::ParseControllerTypeName(const char* str)
@@ -951,6 +1086,30 @@ const char* Settings::GetMemoryCardTypeDisplayName(MemoryCardType type)
return s_memory_card_type_display_names[static_cast<int>(type)];
}
std::string Settings::GetDefaultSharedMemoryCardName(u32 slot)
{
return fmt::format("shared_card_{}.mcd", slot + 1);
}
std::string Settings::GetSharedMemoryCardPath(u32 slot) const
{
std::string ret;
if (memory_card_paths[slot].empty())
ret = Path::Combine(EmuFolders::MemoryCards, GetDefaultSharedMemoryCardName(slot));
else if (!Path::IsAbsolute(memory_card_paths[slot]))
ret = Path::Combine(EmuFolders::MemoryCards, memory_card_paths[slot]);
else
ret = memory_card_paths[slot];
return ret;
}
std::string Settings::GetGameMemoryCardPath(const char* game_code, u32 slot)
{
return Path::Combine(EmuFolders::MemoryCards, fmt::format("{}_{}.mcd", game_code, slot + 1));
}
static std::array<const char*, 4> s_multitap_enable_mode_names = {{"Disabled", "Port1Only", "Port2Only", "BothPorts"}};
static std::array<const char*, 4> s_multitap_enable_mode_display_names = {
{TRANSLATABLE("MultitapMode", "Disabled"), TRANSLATABLE("MultitapMode", "Enable on Port 1 Only"),
@@ -979,3 +1138,113 @@ const char* Settings::GetMultitapModeDisplayName(MultitapMode mode)
{
return s_multitap_enable_mode_display_names[static_cast<size_t>(mode)];
}
std::string EmuFolders::AppRoot;
std::string EmuFolders::DataRoot;
std::string EmuFolders::Bios;
std::string EmuFolders::Cache;
std::string EmuFolders::Cheats;
std::string EmuFolders::Covers;
std::string EmuFolders::Dumps;
std::string EmuFolders::GameSettings;
std::string EmuFolders::InputProfiles;
std::string EmuFolders::MemoryCards;
std::string EmuFolders::Resources;
std::string EmuFolders::SaveStates;
std::string EmuFolders::Screenshots;
std::string EmuFolders::Shaders;
std::string EmuFolders::Textures;
void EmuFolders::SetDefaults()
{
Bios = Path::Combine(DataRoot, "bios");
Cache = Path::Combine(DataRoot, "cache");
Cheats = Path::Combine(DataRoot, "cheats");
Covers = Path::Combine(DataRoot, "covers");
Dumps = Path::Combine(DataRoot, "dump");
GameSettings = Path::Combine(DataRoot, "gamesettings");
InputProfiles = Path::Combine(DataRoot, "inputprofiles");
MemoryCards = Path::Combine(DataRoot, "memcards");
SaveStates = Path::Combine(DataRoot, "savestates");
Screenshots = Path::Combine(DataRoot, "screenshots");
Shaders = Path::Combine(DataRoot, "shaders");
Textures = Path::Combine(DataRoot, "textures");
}
static std::string LoadPathFromSettings(SettingsInterface& si, const std::string& root, const char* section,
const char* name, const char* def)
{
std::string value = si.GetStringValue(section, name, def);
if (value.empty())
value = def;
if (!Path::IsAbsolute(value))
value = Path::Combine(root, value);
return value;
}
void EmuFolders::LoadConfig(SettingsInterface& si)
{
Bios = LoadPathFromSettings(si, DataRoot, "BIOS", "SearchDirectory", "bios");
Cache = LoadPathFromSettings(si, DataRoot, "Folders", "Cache", "cache");
Cheats = LoadPathFromSettings(si, DataRoot, "Folders", "Cheats", "cheats");
Covers = LoadPathFromSettings(si, DataRoot, "Folders", "Covers", "covers");
Dumps = LoadPathFromSettings(si, DataRoot, "Folders", "Dumps", "dump");
GameSettings = LoadPathFromSettings(si, DataRoot, "Folders", "GameSettings", "gamesettings");
InputProfiles = LoadPathFromSettings(si, DataRoot, "Folders", "InputProfiles", "inputprofiles");
MemoryCards = LoadPathFromSettings(si, DataRoot, "MemoryCards", "Directory", "memcards");
SaveStates = LoadPathFromSettings(si, DataRoot, "Folders", "Savestates", "savestates");
Screenshots = LoadPathFromSettings(si, DataRoot, "Folders", "Snapshots", "screenshots");
Shaders = LoadPathFromSettings(si, DataRoot, "Folders", "Snapshots", "shaders");
Textures = LoadPathFromSettings(si, DataRoot, "Folders", "Textures", "textures");
Log_DevPrintf("BIOS Directory: %s", Bios.c_str());
Log_DevPrintf("Cache Directory: %s", Cache.c_str());
Log_DevPrintf("Cheats Directory: %s", Cheats.c_str());
Log_DevPrintf("Covers Directory: %s", Covers.c_str());
Log_DevPrintf("Dumps Directory: %s", Dumps.c_str());
Log_DevPrintf("Game Settings Directory: %s", GameSettings.c_str());
Log_DevPrintf("Input Profile Directory: %s", InputProfiles.c_str());
Log_DevPrintf("MemoryCards Directory: %s", MemoryCards.c_str());
Log_DevPrintf("SaveStates Directory: %s", SaveStates.c_str());
Log_DevPrintf("Screenshots Directory: %s", Screenshots.c_str());
Log_DevPrintf("Shaders Directory: %s", Shaders.c_str());
Log_DevPrintf("Textures Directory: %s", Textures.c_str());
}
void EmuFolders::Save(SettingsInterface& si)
{
// convert back to relative
si.SetStringValue("BIOS", "SearchDirectory", Path::MakeRelative(Bios, DataRoot).c_str());
si.SetStringValue("Folders", "Cache", Path::MakeRelative(Cache, DataRoot).c_str());
si.SetStringValue("Folders", "Cheats", Path::MakeRelative(Cheats, DataRoot).c_str());
si.SetStringValue("Folders", "Covers", Path::MakeRelative(Covers, DataRoot).c_str());
si.SetStringValue("Folders", "Dumps", Path::MakeRelative(Dumps, DataRoot).c_str());
si.SetStringValue("Folders", "GameSettings", Path::MakeRelative(Dumps, GameSettings).c_str());
si.SetStringValue("Folders", "InputProfiles", Path::MakeRelative(InputProfiles, DataRoot).c_str());
si.SetStringValue("MemoryCards", "Directory", Path::MakeRelative(MemoryCards, DataRoot).c_str());
si.SetStringValue("Folders", "SaveStates", Path::MakeRelative(SaveStates, DataRoot).c_str());
si.SetStringValue("Folders", "Screenshots", Path::MakeRelative(Screenshots, DataRoot).c_str());
si.SetStringValue("Folders", "Shaders", Path::MakeRelative(Shaders, DataRoot).c_str());
si.SetStringValue("Folders", "Textures", Path::MakeRelative(Textures, DataRoot).c_str());
}
bool EmuFolders::EnsureFoldersExist()
{
bool result = FileSystem::EnsureDirectoryExists(Bios.c_str(), false);
result = FileSystem::EnsureDirectoryExists(Cache.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Path::Combine(Cache, "achievement_badge").c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Path::Combine(Cache, "achievement_gameicon").c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Cheats.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Covers.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Dumps.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Path::Combine(Dumps, "audio").c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Path::Combine(Dumps, "textures").c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(GameSettings.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(InputProfiles.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(MemoryCards.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(SaveStates.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Screenshots.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Shaders.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Textures.c_str(), false) && result;
return result;
}
+86 -28
View File
@@ -44,9 +44,9 @@ struct Settings
{
Settings();
ConsoleRegion region = ConsoleRegion::Auto;
ConsoleRegion region = DEFAULT_CONSOLE_REGION;
CPUExecutionMode cpu_execution_mode = CPUExecutionMode::Interpreter;
CPUExecutionMode cpu_execution_mode = DEFAULT_CPU_EXECUTION_MODE;
u32 cpu_overclock_numerator = 1;
u32 cpu_overclock_denominator = 1;
bool cpu_overclock_enable = false;
@@ -54,23 +54,24 @@ struct Settings
bool cpu_recompiler_memory_exceptions = false;
bool cpu_recompiler_block_linking = true;
bool cpu_recompiler_icache = false;
CPUFastmemMode cpu_fastmem_mode = CPUFastmemMode::Disabled;
CPUFastmemMode cpu_fastmem_mode = DEFAULT_CPU_FASTMEM_MODE;
float emulation_speed = 1.0f;
float fast_forward_speed = 0.0f;
float turbo_speed = 0.0f;
bool sync_to_host_refresh_rate = true;
bool sync_to_host_refresh_rate = false;
bool increase_timer_resolution = true;
bool inhibit_screensaver = false;
bool inhibit_screensaver = true;
bool start_paused = false;
bool start_fullscreen = false;
bool pause_on_focus_loss = false;
bool pause_on_menu = true;
bool save_state_on_exit = true;
bool create_save_state_backups = false;
bool confim_power_off = true;
bool load_devices_from_save_states = false;
bool apply_game_settings = true;
bool auto_load_cheats = false;
bool auto_load_cheats = true;
bool disable_all_enhancements = false;
bool rewind_enable = false;
@@ -78,7 +79,7 @@ struct Settings
u32 rewind_save_slots = 10;
u32 runahead_frames = 0;
GPURenderer gpu_renderer = GPURenderer::Software;
GPURenderer gpu_renderer = DEFAULT_GPU_RENDERER;
std::string gpu_adapter;
std::string display_post_process_chain;
u32 gpu_resolution_scale = 1;
@@ -88,10 +89,10 @@ struct Settings
bool gpu_threaded_presentation = true;
bool gpu_use_debug_device = false;
bool gpu_per_sample_shading = false;
bool gpu_true_color = false;
bool gpu_scaled_dithering = false;
GPUTextureFilter gpu_texture_filter = GPUTextureFilter::Nearest;
GPUDownsampleMode gpu_downsample_mode = GPUDownsampleMode::Disabled;
bool gpu_true_color = true;
bool gpu_scaled_dithering = true;
GPUTextureFilter gpu_texture_filter = DEFAULT_GPU_TEXTURE_FILTER;
GPUDownsampleMode gpu_downsample_mode = DEFAULT_GPU_DOWNSAMPLE_MODE;
bool gpu_disable_interlacing = true;
bool gpu_force_ntsc_timings = false;
bool gpu_widescreen_hack = false;
@@ -102,8 +103,8 @@ struct Settings
bool gpu_pgxp_cpu = false;
bool gpu_pgxp_preserve_proj_fp = false;
bool gpu_pgxp_depth_buffer = false;
DisplayCropMode display_crop_mode = DisplayCropMode::None;
DisplayAspectRatio display_aspect_ratio = DisplayAspectRatio::Auto;
DisplayCropMode display_crop_mode = DEFAULT_DISPLAY_CROP_MODE;
DisplayAspectRatio display_aspect_ratio = DEFAULT_DISPLAY_ASPECT_RATIO;
u16 display_aspect_ratio_custom_numerator = 0;
u16 display_aspect_ratio_custom_denominator = 0;
s16 display_active_start_offset = 0;
@@ -118,16 +119,19 @@ struct Settings
bool display_post_processing = false;
bool display_show_osd_messages = true;
bool display_show_fps = false;
bool display_show_vps = false;
bool display_show_speed = false;
bool display_show_resolution = false;
bool display_show_cpu = false;
bool display_show_status_indicators = true;
bool display_show_inputs = false;
bool display_show_enhancements = false;
bool display_all_frames = false;
bool display_internal_resolution_screenshots = false;
bool video_sync_enabled = DEFAULT_VSYNC_VALUE;
float display_osd_scale = 1.0f;
float display_max_fps = DEFAULT_DISPLAY_MAX_FPS;
float gpu_pgxp_tolerance = -1.0f;
float gpu_pgxp_depth_clear_threshold = 300.0f / 4096.0f;
float gpu_pgxp_depth_clear_threshold = DEFAULT_GPU_PGXP_DEPTH_THRESHOLD;
u8 cdrom_readahead_sectors = DEFAULT_CDROM_READAHEAD_SECTORS;
bool cdrom_region_check = false;
@@ -136,20 +140,30 @@ struct Settings
u32 cdrom_read_speedup = 1;
u32 cdrom_seek_speedup = 1;
AudioBackend audio_backend = AudioBackend::Cubeb;
AudioBackend audio_backend = DEFAULT_AUDIO_BACKEND;
s32 audio_output_volume = 100;
s32 audio_fast_forward_volume = 100;
u32 audio_buffer_size = 2048;
bool audio_resampling = false;
u32 audio_buffer_size = DEFAULT_AUDIO_BUFFER_SIZE;
bool audio_resampling = true;
bool audio_output_muted = false;
bool audio_sync_enabled = true;
bool audio_dump_on_boot = true;
bool audio_dump_on_boot = false;
// timing hacks section
TickCount dma_max_slice_ticks = 1000;
TickCount dma_halt_ticks = 100;
u32 gpu_fifo_size = 128;
TickCount gpu_max_run_ahead = 128;
TickCount dma_max_slice_ticks = DEFAULT_DMA_MAX_SLICE_TICKS;
TickCount dma_halt_ticks = DEFAULT_DMA_HALT_TICKS;
u32 gpu_fifo_size = DEFAULT_GPU_FIFO_SIZE;
TickCount gpu_max_run_ahead = DEFAULT_GPU_MAX_RUN_AHEAD;
#ifdef WITH_CHEEVOS
// achievements
bool achievements_enabled : 1;
bool achievements_test_mode : 1;
bool achievements_unofficial_test_mode : 1;
bool achievements_use_first_disc_from_playlist : 1;
bool achievements_rich_presence : 1;
bool achievements_challenge_mode : 1;
#endif
struct DebugSettings
{
@@ -191,7 +205,7 @@ struct Settings
// TODO: Controllers, memory cards, etc.
bool bios_patch_tty_enable = false;
bool bios_patch_fast_boot = false;
bool bios_patch_fast_boot = DEFAULT_FAST_BOOT_VALUE;
bool enable_8mb_ram = false;
std::array<ControllerType, NUM_CONTROLLER_AND_CARD_PORTS> controller_types{};
@@ -199,16 +213,15 @@ struct Settings
std::array<MemoryCardType, NUM_CONTROLLER_AND_CARD_PORTS> memory_card_types{};
std::array<std::string, NUM_CONTROLLER_AND_CARD_PORTS> memory_card_paths{};
std::string memory_card_directory;
bool memory_card_use_playlist_title = true;
MultitapMode multitap_mode = MultitapMode::Disabled;
MultitapMode multitap_mode = DEFAULT_MULTITAP_MODE;
std::array<TinyString, NUM_CONTROLLER_AND_CARD_PORTS> GeneratePortLabels() const;
LOGLEVEL log_level = LOGLEVEL_INFO;
LOGLEVEL log_level = DEFAULT_LOG_LEVEL;
std::string log_filter;
bool log_to_console = false;
bool log_to_console = DEFAULT_LOG_TO_CONSOLE;
bool log_to_debug = false;
bool log_to_window = false;
bool log_to_file = false;
@@ -241,6 +254,15 @@ struct Settings
float GetDisplayAspectRatioValue() const;
ALWAYS_INLINE bool IsPort1MultitapEnabled() const
{
return (multitap_mode == MultitapMode::Port1Only || multitap_mode == MultitapMode::BothPorts);
}
ALWAYS_INLINE bool IsPort2MultitapEnabled() const
{
return (multitap_mode == MultitapMode::Port1Only || multitap_mode == MultitapMode::BothPorts);
}
ALWAYS_INLINE static bool IsPerGameMemoryCardType(MemoryCardType type)
{
return (type == MemoryCardType::PerGame || type == MemoryCardType::PerGameTitle ||
@@ -248,6 +270,13 @@ struct Settings
}
bool HasAnyPerGameMemoryCards() const;
/// Returns the default path to a memory card.
static std::string GetDefaultSharedMemoryCardName(u32 slot);
std::string GetSharedMemoryCardPath(u32 slot) const;
/// Returns the default path to a memory card for a specific game.
static std::string GetGameMemoryCardPath(const char* game_code, u32 slot);
static void CPUOverclockPercentToFraction(u32 percent, u32* numerator, u32* denominator);
static u32 CPUOverclockFractionToPercent(u32 numerator, u32 denominator);
@@ -268,6 +297,8 @@ struct Settings
void Load(SettingsInterface& si);
void Save(SettingsInterface& si) const;
void FixIncompatibleSettings(bool display_osd_messages);
static std::optional<LOGLEVEL> ParseLogLevelName(const char* str);
static const char* GetLogLevelName(LOGLEVEL level);
static const char* GetLogLevelDisplayName(LOGLEVEL level);
@@ -356,6 +387,7 @@ struct Settings
static constexpr DisplayCropMode DEFAULT_DISPLAY_CROP_MODE = DisplayCropMode::Overscan;
static constexpr DisplayAspectRatio DEFAULT_DISPLAY_ASPECT_RATIO = DisplayAspectRatio::Auto;
static constexpr float DEFAULT_OSD_SCALE = 100.0f;
static constexpr u8 DEFAULT_CDROM_READAHEAD_SECTORS = 8;
@@ -367,6 +399,8 @@ struct Settings
static constexpr LOGLEVEL DEFAULT_LOG_LEVEL = LOGLEVEL_INFO;
static constexpr u32 DEFAULT_AUDIO_BUFFER_SIZE = 2048;
// Enable console logging by default on Linux platforms.
#if defined(__linux__) && !defined(__ANDROID__)
static constexpr bool DEFAULT_LOG_TO_CONSOLE = true;
@@ -387,3 +421,27 @@ struct Settings
};
extern Settings g_settings;
namespace EmuFolders {
extern std::string AppRoot;
extern std::string DataRoot;
extern std::string Bios;
extern std::string Cache;
extern std::string Cheats;
extern std::string Covers;
extern std::string Dumps;
extern std::string GameSettings;
extern std::string InputProfiles;
extern std::string MemoryCards;
extern std::string Resources;
extern std::string SaveStates;
extern std::string Screenshots;
extern std::string Shaders;
extern std::string Textures;
// Assumes that AppRoot and DataRoot have been initialized.
void SetDefaults();
bool EnsureFoldersExist();
void LoadConfig(SettingsInterface& si);
void Save(SettingsInterface& si);
} // namespace EmuFolders
-1
View File
@@ -1,7 +1,6 @@
#include "sio.h"
#include "common/log.h"
#include "controller.h"
#include "host_interface.h"
#include "interrupt_controller.h"
#include "memory_card.h"
#include "util/state_wrapper.h"
+35 -5
View File
@@ -3,7 +3,7 @@
#include "common/file_system.h"
#include "common/log.h"
#include "dma.h"
#include "host_interface.h"
#include "host.h"
#include "imgui.h"
#include "interrupt_controller.h"
#include "system.h"
@@ -30,11 +30,39 @@ void SPU::Initialize()
"SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD,
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<SPU*>(param)->ExecuteTransfer(ticks); }, this,
false);
m_audio_stream = g_host_interface->GetAudioStream();
m_null_audio_stream = AudioStream::CreateNullAudioStream();
m_null_audio_stream->Reconfigure(SAMPLE_RATE, SAMPLE_RATE, NUM_CHANNELS, Settings::DEFAULT_AUDIO_BUFFER_SIZE);
CreateOutputStream();
Reset();
}
void SPU::CreateOutputStream()
{
Log_InfoPrintf("Creating '%s' audio stream, sample rate = %u, channels = %u, buffer size = %u",
Settings::GetAudioBackendName(g_settings.audio_backend), SAMPLE_RATE, NUM_CHANNELS,
g_settings.audio_buffer_size);
m_audio_stream = Host::CreateAudioStream(g_settings.audio_backend);
if (!m_audio_stream ||
!m_audio_stream->Reconfigure(SAMPLE_RATE, SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_size))
{
Host::ReportErrorAsync("Error", "Failed to create or configure audio stream, falling back to null output.");
m_audio_stream.reset();
m_audio_stream = AudioStream::CreateNullAudioStream();
m_audio_stream->Reconfigure(SAMPLE_RATE, SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_size);
}
m_audio_stream->SetOutputVolume(System::GetAudioOutputVolume());
}
void SPU::RecreateOutputStream()
{
m_audio_stream.reset();
CreateOutputStream();
}
void SPU::CPUClockChanged()
{
// (X * D) / N / 768 -> (X * D) / (N * 768)
@@ -1781,11 +1809,13 @@ void SPU::Execute(TickCount ticks)
m_ticks_carry = (ticks + m_ticks_carry) % SYSCLK_TICKS_PER_SPU_TICK;
}
AudioStream* output_stream = m_audio_output_muted ? m_null_audio_stream.get() : m_audio_stream.get();
while (remaining_frames > 0)
{
s16* output_frame_start;
u32 output_frame_space = remaining_frames;
m_audio_stream->BeginWrite(&output_frame_start, &output_frame_space);
output_stream->BeginWrite(&output_frame_start, &output_frame_space);
s16* output_frame = output_frame_start;
const u32 frames_in_this_batch = std::min(remaining_frames, output_frame_space);
@@ -1888,7 +1918,7 @@ void SPU::Execute(TickCount ticks)
if (m_dump_writer)
m_dump_writer->WriteFrames(output_frame_start, frames_in_this_batch);
m_audio_stream->EndWrite(frames_in_this_batch);
output_stream->EndWrite(frames_in_this_batch);
remaining_frames -= frames_in_this_batch;
}
}
@@ -1898,7 +1928,7 @@ void SPU::UpdateEventInterval()
// Don't generate more than the audio buffer since in a single slice, otherwise we'll both overflow the buffers when
// we do write it, and the audio thread will underflow since it won't have enough data it the game isn't messing with
// the SPU state.
const u32 max_slice_frames = g_host_interface->GetAudioStream()->GetBufferSize();
const u32 max_slice_frames = m_audio_stream->GetBufferSize();
// TODO: Make this predict how long until the interrupt will be hit instead...
const u32 interval = (m_SPUCNT.enable && m_SPUCNT.irq9_enable) ? 1 : max_slice_frames;
+16 -3
View File
@@ -11,6 +11,8 @@
class StateWrapper;
class AudioStream;
namespace Common {
class WAVWriter;
}
@@ -24,6 +26,7 @@ public:
{
RAM_SIZE = 512 * 1024,
RAM_MASK = RAM_SIZE - 1,
SAMPLE_RATE = 44100,
};
SPU();
@@ -61,16 +64,21 @@ public:
std::array<u8, RAM_SIZE>& GetRAM() { return m_ram; }
/// Change output stream - used for runahead.
ALWAYS_INLINE void SetAudioStream(AudioStream* stream) { m_audio_stream = stream; }
// TODO: Make it use system "running ahead" flag
ALWAYS_INLINE bool IsAudioOutputMuted() const { return m_audio_output_muted; }
void SetAudioOutputMuted(bool muted) { m_audio_output_muted = muted; }
ALWAYS_INLINE AudioStream* GetOutputStream() const { return m_audio_stream.get(); }
void RecreateOutputStream();
private:
static constexpr u32 SPU_BASE = 0x1F801C00;
static constexpr u32 NUM_CHANNELS = 2;
static constexpr u32 NUM_VOICES = 24;
static constexpr u32 NUM_VOICE_REGISTERS = 8;
static constexpr u32 VOICE_ADDRESS_SHIFT = 3;
static constexpr u32 NUM_SAMPLES_PER_ADPCM_BLOCK = 28;
static constexpr u32 NUM_SAMPLES_FROM_LAST_ADPCM_BLOCK = 3;
static constexpr u32 SAMPLE_RATE = 44100;
static constexpr u32 SYSCLK_TICKS_PER_SPU_TICK = System::MASTER_CLOCK / SAMPLE_RATE; // 0x300
static constexpr s16 ENVELOPE_MIN_VOLUME = 0;
static constexpr s16 ENVELOPE_MAX_VOLUME = 0x7FFF;
@@ -376,10 +384,15 @@ private:
void UpdateTransferEvent();
void UpdateDMARequest();
void CreateOutputStream();
std::unique_ptr<TimingEvent> m_tick_event;
std::unique_ptr<TimingEvent> m_transfer_event;
std::unique_ptr<Common::WAVWriter> m_dump_writer;
AudioStream* m_audio_stream = nullptr;
std::unique_ptr<AudioStream> m_audio_stream;
std::unique_ptr<AudioStream> m_null_audio_stream;
bool m_audio_output_muted = false;
TickCount m_ticks_carry = 0;
TickCount m_cpu_ticks_per_spu_tick = 0;
TickCount m_cpu_tick_divider = 0;
+1969 -341
View File
File diff suppressed because it is too large Load Diff
+245 -16
View File
@@ -1,6 +1,5 @@
#pragma once
#include "common/timer.h"
#include "host_interface.h"
#include "settings.h"
#include "timing_event.h"
#include "types.h"
@@ -20,26 +19,50 @@ class CheatList;
struct SystemBootParameters
{
SystemBootParameters();
SystemBootParameters(SystemBootParameters&& other);
SystemBootParameters(const SystemBootParameters&);
SystemBootParameters(SystemBootParameters&&);
SystemBootParameters(std::string filename_);
~SystemBootParameters();
std::string filename;
std::string save_state;
std::optional<bool> override_fast_boot;
std::optional<bool> override_fullscreen;
std::optional<bool> override_start_paused;
std::unique_ptr<ByteStream> state_stream;
u32 media_playlist_index = 0;
bool load_image_to_ram = false;
bool force_software_renderer = false;
};
struct SaveStateInfo
{
std::string path;
std::time_t timestamp;
s32 slot;
bool global;
};
struct ExtendedSaveStateInfo
{
std::string title;
std::string game_code;
std::string media_path;
std::time_t timestamp;
u32 screenshot_width;
u32 screenshot_height;
std::vector<u32> screenshot_data;
};
namespace System {
enum : u32
{
// 5 megabytes is sufficient for now, at the moment they're around 4.3MB, or 10.3MB with 8MB RAM enabled.
MAX_SAVE_STATE_SIZE = 11 * 1024 * 1024
MAX_SAVE_STATE_SIZE = 11 * 1024 * 1024,
PER_GAME_SAVE_STATE_SLOTS = 10,
GLOBAL_SAVE_STATE_SLOTS = 10
};
enum : TickCount
@@ -58,13 +81,16 @@ enum class State
extern TickCount g_ticks_per_second;
/// Returns true if the filename is a PlayStation executable we can inject.
bool IsExeFileName(const char* path);
bool IsExeFileName(const std::string_view& path);
/// Returns true if the filename is a Portable Sound Format file we can uncompress/load.
bool IsPsfFileName(const char* path);
bool IsPsfFileName(const std::string_view& path);
/// Returns true if the filename is one we can load.
bool IsLoadableFilename(const char* path);
bool IsLoadableFilename(const std::string_view& path);
/// Returns true if the filename is a save state.
bool IsSaveStateFilename(const std::string_view& path);
/// Returns the preferred console type for a disc.
ConsoleRegion GetConsoleRegionForDiscRegion(DiscRegion region);
@@ -82,6 +108,12 @@ DiscRegion GetRegionForExe(const char* path);
DiscRegion GetRegionForPsf(const char* path);
std::optional<DiscRegion> GetRegionForPath(const char* image_path);
/// Returns the path for the game settings ini file for the specified serial.
std::string GetGameSettingsPath(const std::string_view& game_serial);
/// Returns the path for the input profile ini file with the specified name (may not exist).
std::string GetInputProfilePath(const std::string_view& name);
State GetState();
void SetState(State new_state);
bool IsRunning();
@@ -93,6 +125,7 @@ bool IsStartupCancelled();
void CancelPendingStartup();
ConsoleRegion GetRegion();
DiscRegion GetDiscRegion();
bool IsPALRegion();
ALWAYS_INLINE TickCount GetTicksPerSecond()
@@ -137,22 +170,44 @@ const std::string& GetRunningPath();
const std::string& GetRunningCode();
const std::string& GetRunningTitle();
// TODO: Move to PerformanceMetrics
float GetFPS();
float GetVPS();
float GetEmulationSpeed();
float GetAverageFrameTime();
float GetWorstFrameTime();
float GetThrottleFrequency();
float GetCPUThreadUsage();
float GetCPUThreadAverageTime();
float GetSWThreadUsage();
float GetSWThreadAverageTime();
bool Boot(const SystemBootParameters& params);
void Reset();
void Shutdown();
/// Loads global settings (i.e. EmuConfig).
void LoadSettings(bool display_osd_messages);
void SetDefaultSettings(SettingsInterface& si);
bool LoadState(ByteStream* state, bool update_display = true);
bool SaveState(ByteStream* state, u32 screenshot_size = 256);
/// Reloads settings, and applies any changes present.
void ApplySettings(bool display_osd_messages);
/// Reloads game specific settings, and applys any changes present.
bool ReloadGameSettings(bool display_osd_messages);
bool BootSystem(SystemBootParameters parameters);
void PauseSystem(bool paused);
void ResetSystem();
/// Loads state from the specified filename.
bool LoadState(const char* filename);
bool SaveState(const char* filename, bool backup_existing_save);
/// Runs the VM until the CPU execution is canceled.
void Execute();
/// Switches the GPU renderer by saving state, recreating the display window, and restoring state (if needed).
void RecreateSystem();
/// Recreates the GPU component, saving/loading the state so it is preserved. Call when the GPU renderer changes.
bool RecreateGPU(GPURenderer renderer, bool update_display = true);
bool RecreateGPU(GPURenderer renderer, bool force_recreate_display = false, bool update_display = true);
void SingleStepCPU();
void RunFrame();
@@ -160,7 +215,6 @@ void RunFrames();
/// Sets target emulation speed.
float GetTargetSpeed();
void SetTargetSpeed(float speed);
/// Adjusts the throttle frequency, i.e. how many times we should sleep per second.
void SetThrottleFrequency(float frequency);
@@ -183,7 +237,10 @@ void ResetControllers();
void UpdateMemoryCardTypes();
void UpdatePerGameMemoryCards();
bool HasMemoryCard(u32 slot);
/// Swaps memory cards in slot 1/2.
void SwapMemoryCards();
void UpdateMultitaps();
/// Dumps RAM to a file.
@@ -230,6 +287,132 @@ void ApplyCheatCode(const CheatCode& code);
/// Sets or clears the provided cheat list, applying every frame.
void SetCheatList(std::unique_ptr<CheatList> cheats);
/// Checks for settings changes, std::move() the old settings away for comparing beforehand.
void CheckForSettingsChanges(const Settings& old_settings);
/// Updates throttler.
void UpdateSpeedLimiterState();
/// Toggles fast forward state.
bool IsFastForwardEnabled();
void SetFastForwardEnabled(bool enabled);
/// Toggles turbo state.
bool IsTurboEnabled();
void SetTurboEnabled(bool enabled);
/// Toggles rewind state.
bool IsRewinding();
void SetRewindState(bool enabled);
void DoFrameStep();
void DoToggleCheats();
/// Returns the path to a save state file. Specifying an index of -1 is the "resume" save state.
std::string GetGameSaveStateFileName(const std::string_view& game_code, s32 slot);
/// Returns the path to a save state file. Specifying an index of -1 is the "resume" save state.
std::string GetGlobalSaveStateFileName(s32 slot);
/// Returns the most recent resume save state.
std::string GetMostRecentResumeSaveStatePath();
/// Returns the path to the cheat file for the specified game title.
std::string GetCheatFileName();
/// Powers off the system, optionally saving the resume state.
void ShutdownSystem(bool save_resume_state);
/// Returns true if an undo load state exists.
bool CanUndoLoadState();
/// Returns save state info for the undo slot, if present.
std::optional<ExtendedSaveStateInfo> GetUndoSaveStateInfo();
/// Undoes a load state, i.e. restores the state prior to the load.
bool UndoLoadState();
/// Returns a list of save states for the specified game code.
std::vector<SaveStateInfo> GetAvailableSaveStates(const char* game_code);
/// Returns save state info if present. If game_code is null or empty, assumes global state.
std::optional<SaveStateInfo> GetSaveStateInfo(const char* game_code, s32 slot);
/// Returns save state info from opened save state stream.
std::optional<ExtendedSaveStateInfo> GetExtendedSaveStateInfo(const char* path);
/// Deletes save states for the specified game code. If resume is set, the resume state is deleted too.
void DeleteSaveStates(const char* game_code, bool resume);
/// Returns intended output volume considering fast forwarding.
s32 GetAudioOutputVolume();
void UpdateVolume();
/// Returns true if currently dumping audio.
bool IsDumpingAudio();
/// Starts dumping audio to a file. If no file name is provided, one will be generated automatically.
bool StartDumpingAudio(const char* filename = nullptr);
/// Stops dumping audio to file if it has been started.
void StopDumpingAudio();
/// Saves a screenshot to the specified file. IF no file name is provided, one will be generated automatically.
bool SaveScreenshot(const char* filename = nullptr, bool full_resolution = true, bool apply_aspect_ratio = true,
bool compress_on_thread = true);
/// Loads the cheat list from the specified file.
bool LoadCheatList(const char* filename);
/// Loads the cheat list for the current game title from the user directory.
bool LoadCheatListFromGameTitle();
/// Loads the cheat list for the current game code from the built-in code database.
bool LoadCheatListFromDatabase();
/// Saves the current cheat list to the game title's file.
bool SaveCheatList();
/// Saves the current cheat list to the specified file.
bool SaveCheatList(const char* filename);
/// Deletes the cheat list, if present.
bool DeleteCheatList();
/// Removes all cheats from the cheat list.
void ClearCheatList(bool save_to_file);
/// Enables/disabled the specified cheat code.
void SetCheatCodeState(u32 index, bool enabled, bool save_to_file);
/// Immediately applies the specified cheat code.
void ApplyCheatCode(u32 index);
/// Temporarily toggles post-processing on/off.
void TogglePostProcessing();
/// Reloads post processing shaders with the current configuration.
void ReloadPostProcessingShaders();
/// Toggle Widescreen Hack and Aspect Ratio
void ToggleWidescreen();
/// Returns true if fast forwarding or slow motion is currently active.
bool IsRunningAtNonStandardSpeed();
/// Quick switch between software and hardware rendering.
void ToggleSoftwareRendering();
/// Updates software cursor state, based on controllers.
void UpdateSoftwareCursor();
/// Resizes the render window to the display size, with an optional scale.
/// If the scale is set to 0, the internal resolution will be used, otherwise it is treated as a multiplier to 1x.
void RequestDisplaySize(float scale = 0.0f);
/// Call when host display size changes, use with "match display" aspect ratio setting.
void HostDisplayResized();
//////////////////////////////////////////////////////////////////////////
// Memory Save States (Rewind and Runahead)
//////////////////////////////////////////////////////////////////////////
@@ -237,8 +420,54 @@ void CalculateRewindMemoryUsage(u32 num_saves, u64* ram_usage, u64* vram_usage);
void ClearMemorySaveStates();
void UpdateMemorySaveStateSettings();
bool LoadRewindState(u32 skip_saves = 0, bool consume_state = true);
bool IsRewinding();
void SetRewinding(bool enabled);
void SetRunaheadReplayFlag();
} // namespace System
namespace Host {
/// Called with the settings lock held, when system settings are being loaded (should load input sources, etc).
void LoadSettings(SettingsInterface& si, std::unique_lock<std::mutex>& lock);
/// Called after settings are updated.
void CheckForSettingsChanges(const Settings& old_settings);
/// Called when the VM is starting initialization, but has not been completed yet.
void OnSystemStarting();
/// Called when the VM is created.
void OnSystemStarted();
/// Called when the VM is shut down or destroyed.
void OnSystemDestroyed();
/// Called when the VM is paused.
void OnSystemPaused();
/// Called when the VM is resumed after being paused.
void OnSystemResumed();
/// Called when performance metrics are updated, approximately once a second.
void OnPerformanceCountersUpdated();
/// Provided by the host; called when the running executable changes.
void OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name);
/// Provided by the host; called once per frame at guest vsync.
void PumpMessagesOnCPUThread();
/// Requests a specific display window size.
void RequestResizeHostDisplay(s32 width, s32 height);
/// Requests shut down and exit of the hosting application. This may not actually exit,
/// if the user cancels the shutdown confirmation.
void RequestExit(bool save_state_if_running);
/// Requests shut down of the current virtual machine.
void RequestSystemShutdown(bool allow_confirm, bool allow_save_state);
/// Returns true if the hosting application is currently fullscreen.
bool IsFullscreen();
/// Alters fullscreen state of hosting application.
void SetFullscreen(bool enabled);
} // namespace Host
+14 -12
View File
@@ -1,10 +1,12 @@
#include "texture_replacements.h"
#include "common/file_system.h"
#include "common/log.h"
#include "common/path.h"
#include "common/platform.h"
#include "common/string_util.h"
#include "common/timer.h"
#include "host_interface.h"
#include "fmt/format.h"
#include "host.h"
#include "settings.h"
#include "xxhash.h"
#if defined(CPU_X86) || defined(CPU_X64)
@@ -118,7 +120,12 @@ void TextureReplacements::Shutdown()
std::string TextureReplacements::GetSourceDirectory() const
{
return g_host_interface->GetUserDirectoryRelativePath("textures/%s", m_game_id.c_str());
return Path::Combine(EmuFolders::Textures, m_game_id);
}
std::string TextureReplacements::GetDumpDirectory() const
{
return Path::Combine(EmuFolders::Dumps, Path::Combine("textures", m_game_id));
}
TextureReplacementHash TextureReplacements::GetVRAMWriteHash(u32 width, u32 height, const void* pixels) const
@@ -133,19 +140,14 @@ std::string TextureReplacements::GetVRAMWriteDumpFilename(u32 width, u32 height,
return {};
const TextureReplacementHash hash = GetVRAMWriteHash(width, height, pixels);
std::string filename = g_host_interface->GetUserDirectoryRelativePath("dump/textures/%s/vram-write-%s.png",
m_game_id.c_str(), hash.ToString().c_str());
const std::string dump_directory(GetDumpDirectory());
std::string filename(Path::Combine(dump_directory, fmt::format("vram-write-{}.png", hash.ToString())));
if (FileSystem::FileExists(filename.c_str()))
return {};
const std::string dump_directory =
g_host_interface->GetUserDirectoryRelativePath("dump/textures/%s", m_game_id.c_str());
if (!FileSystem::DirectoryExists(dump_directory.c_str()) &&
!FileSystem::CreateDirectory(dump_directory.c_str(), false))
{
if (!FileSystem::EnsureDirectoryExists(dump_directory.c_str(), false))
return {};
}
return filename;
}
@@ -288,8 +290,8 @@ void TextureReplacements::PreloadTextures()
#define UPDATE_PROGRESS() \
if (last_update_time.GetTimeSeconds() >= UPDATE_INTERVAL) \
{ \
g_host_interface->DisplayLoadingScreen("Preloading replacement textures...", 0, static_cast<int>(total_textures), \
static_cast<int>(num_textures_loaded)); \
Host::DisplayLoadingScreen("Preloading replacement textures...", 0, static_cast<int>(total_textures), \
static_cast<int>(num_textures_loaded)); \
last_update_time.Reset(); \
}
+1
View File
@@ -69,6 +69,7 @@ private:
ReplacmentType* replacement_type);
std::string GetSourceDirectory() const;
std::string GetDumpDirectory() const;
TextureReplacementHash GetVRAMWriteHash(u32 width, u32 height, const void* pixels) const;
std::string GetVRAMWriteDumpFilename(u32 width, u32 height, const void* pixels) const;
+1 -1
View File
@@ -126,7 +126,7 @@ enum class ControllerType
DigitalController,
AnalogController,
AnalogJoystick,
NamcoGunCon,
GunCon,
PlayStationMouse,
NeGcon,
Count
-2
View File
@@ -8,8 +8,6 @@ target_link_libraries(duckstation-nogui PRIVATE core common imgui glad frontend-
if(USE_SDL2)
target_sources(duckstation-nogui PRIVATE
imgui_impl_sdl.cpp
imgui_impl_sdl.h
sdl_host_interface.cpp
sdl_host_interface.h
sdl_key_names.h
@@ -5,7 +5,6 @@
<ClCompile Include="vty_host_interface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="imgui_impl_sdl.cpp" />
<ClCompile Include="nogui_host_interface.cpp" />
<ClCompile Include="sdl_host_interface.cpp" />
<ClCompile Include="main.cpp" />
@@ -18,7 +17,6 @@
<ClInclude Include="evdev_key_names.h">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="imgui_impl_sdl.h" />
<ClInclude Include="nogui_host_interface.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="sdl_host_interface.h" />
@@ -37,15 +35,12 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{0A172B2E-DC67-49FC-A4C1-975F93C586C4}</ProjectGuid>
</PropertyGroup>
<Import Project="..\..\dep\msvc\vsprops\Win32Application.props" />
<Import Project="..\frontend-common\frontend-common.props" />
<ItemDefinitionGroup>
<Link>
<AdditionalDependencies>$(RootBuildDir)frontend-common\frontend-common.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<Import Project="..\..\dep\msvc\vsprops\Targets.props" />
</Project>
@@ -3,14 +3,12 @@
<ItemGroup>
<ClCompile Include="main.cpp" />
<ClCompile Include="sdl_host_interface.cpp" />
<ClCompile Include="imgui_impl_sdl.cpp" />
<ClCompile Include="nogui_host_interface.cpp" />
<ClCompile Include="win32_host_interface.cpp" />
<ClCompile Include="vty_host_interface.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="sdl_host_interface.h" />
<ClInclude Include="imgui_impl_sdl.h" />
<ClInclude Include="sdl_key_names.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="nogui_host_interface.h" />
-259
View File
@@ -1,259 +0,0 @@
// dear imgui: Platform Binding for SDL2
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
// (Requires: SDL 2.0. Prefer SDL 2.0.4+ for full feature support.)
// Implemented features:
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Clipboard support.
// [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// Missing features:
// [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME.
// [ ] Platform: Multi-viewport + Minimized windows seems to break mouse wheel events (at least under Windows).
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2019-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter.
// 2019-04-23: Inputs: Added support for SDL_GameController (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized.
// 2018-12-21: Inputs: Workaround for Android/iOS which don't seem to handle focus related calls.
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
// 2018-11-14: Changed the signature of ImGui_ImplSDL2_ProcessEvent() to take a 'const SDL_Event*'.
// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
// 2018-06-08: Misc: Extracted imgui_impl_sdl.cpp/.h away from the old combined SDL2+OpenGL/Vulkan examples.
// 2018-06-08: Misc: ImGui_ImplSDL2_InitForOpenGL() now takes a SDL_GLContext parameter.
// 2018-05-09: Misc: Fixed clipboard paste memory leak (we didn't call SDL_FreeMemory on the data returned by SDL_GetClipboardText).
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.
// 2018-02-16: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
// 2018-02-05: Misc: Using SDL_GetPerformanceCounter() instead of SDL_GetTicks() to be able to handle very high framerate (1000+ FPS).
// 2018-02-05: Inputs: Keyboard mapping is using scancodes everywhere instead of a confusing mixture of keycodes and scancodes.
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
// 2018-01-19: Inputs: When available (SDL 2.0.4+) using SDL_CaptureMouse() to retrieve coordinates outside of client area when dragging. Otherwise (SDL 2.0.3 and before) testing for SDL_WINDOW_INPUT_FOCUS instead of SDL_WINDOW_MOUSE_FOCUS.
// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert.
// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).
// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
#include "imgui.h"
#include "imgui_impl_sdl.h"
// SDL
// (the multi-viewports feature requires SDL features supported from SDL 2.0.4+. SDL 2.0.5+ is highly recommended)
#include <SDL.h>
#include <SDL_syswm.h>
#if defined(__APPLE__)
#include "TargetConditionals.h"
#endif
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE SDL_VERSION_ATLEAST(2,0,4)
#define SDL_HAS_WINDOW_ALPHA SDL_VERSION_ATLEAST(2,0,5)
#define SDL_HAS_ALWAYS_ON_TOP SDL_VERSION_ATLEAST(2,0,5)
#define SDL_HAS_USABLE_DISPLAY_BOUNDS SDL_VERSION_ATLEAST(2,0,5)
#define SDL_HAS_PER_MONITOR_DPI SDL_VERSION_ATLEAST(2,0,4)
#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6)
#define SDL_HAS_MOUSE_FOCUS_CLICKTHROUGH SDL_VERSION_ATLEAST(2,0,5)
#if !SDL_HAS_VULKAN
static const Uint32 SDL_WINDOW_VULKAN = 0x10000000;
#endif
// Data
static SDL_Window* g_Window = NULL;
static Uint64 g_Time = 0;
static bool g_MousePressed[3] = { false, false, false };
static SDL_Cursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {};
static char* g_ClipboardTextData = NULL;
static const char* ImGui_ImplSDL2_GetClipboardText(void*)
{
if (g_ClipboardTextData)
SDL_free(g_ClipboardTextData);
g_ClipboardTextData = SDL_GetClipboardText();
return g_ClipboardTextData;
}
static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text)
{
SDL_SetClipboardText(text);
}
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field.
bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
{
ImGuiIO& io = ImGui::GetIO();
switch (event->type)
{
case SDL_MOUSEWHEEL:
{
if (event->wheel.x > 0) io.MouseWheelH += 1;
if (event->wheel.x < 0) io.MouseWheelH -= 1;
if (event->wheel.y > 0) io.MouseWheel += 1;
if (event->wheel.y < 0) io.MouseWheel -= 1;
return true;
}
case SDL_MOUSEBUTTONDOWN:
{
if (event->button.button == SDL_BUTTON_LEFT) g_MousePressed[0] = true;
if (event->button.button == SDL_BUTTON_RIGHT) g_MousePressed[1] = true;
if (event->button.button == SDL_BUTTON_MIDDLE) g_MousePressed[2] = true;
return true;
}
case SDL_TEXTINPUT:
{
io.AddInputCharactersUTF8(event->text.text);
return true;
}
case SDL_KEYDOWN:
case SDL_KEYUP:
{
int key = event->key.keysym.scancode;
IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown));
io.KeysDown[key] = (event->type == SDL_KEYDOWN);
io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
return true;
}
}
return false;
}
void ImGui_ImplSDL2_Init(SDL_Window* window)
{
g_Window = window;
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendPlatformName = "imgui_impl_sdl";
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP;
io.KeyMap[ImGuiKey_DownArrow] = SDL_SCANCODE_DOWN;
io.KeyMap[ImGuiKey_PageUp] = SDL_SCANCODE_PAGEUP;
io.KeyMap[ImGuiKey_PageDown] = SDL_SCANCODE_PAGEDOWN;
io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME;
io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END;
io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT;
io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE;
io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE;
io.KeyMap[ImGuiKey_Space] = SDL_SCANCODE_SPACE;
io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN;
io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE;
io.KeyMap[ImGuiKey_KeyPadEnter] = SDL_SCANCODE_RETURN2;
io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A;
io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C;
io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V;
io.KeyMap[ImGuiKey_X] = SDL_SCANCODE_X;
io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y;
io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z;
io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText;
io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText;
io.ClipboardUserData = NULL;
g_MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
g_MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
g_MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
g_MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
g_MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
g_MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
}
void ImGui_ImplSDL2_Shutdown()
{
g_Window = NULL;
// Destroy last known clipboard data
if (g_ClipboardTextData)
SDL_free(g_ClipboardTextData);
g_ClipboardTextData = NULL;
// Destroy SDL mouse cursors
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
SDL_FreeCursor(g_MouseCursors[cursor_n]);
memset(g_MouseCursors, 0, sizeof(g_MouseCursors));
}
// This code is incredibly messy because some of the functions we need for full viewport support are not available in SDL < 2.0.4.
static void ImGui_ImplSDL2_UpdateMousePosAndButtons()
{
ImGuiIO& io = ImGui::GetIO();
// [1]
// Only when requested by io.WantSetMousePos: set OS mouse pos from Dear ImGui mouse pos.
// (rarely used, mostly when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
if (io.WantSetMousePos)
{
SDL_WarpMouseInWindow(g_Window, (int)io.MousePos.x, (int)io.MousePos.y);
}
else
{
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
}
// [2]
// Set Dear ImGui mouse pos from OS mouse pos + get buttons. (this is the common behavior)
int mouse_x_local, mouse_y_local;
Uint32 mouse_buttons = SDL_GetMouseState(&mouse_x_local, &mouse_y_local);
io.MouseDown[0] = g_MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame.
io.MouseDown[1] = g_MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
io.MouseDown[2] = g_MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0;
g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false;
// SDL 2.0.3 and before: single-viewport only
if (SDL_GetWindowFlags(g_Window) & SDL_WINDOW_INPUT_FOCUS)
io.MousePos = ImVec2((float)mouse_x_local, (float)mouse_y_local);
}
static void ImGui_ImplSDL2_UpdateMouseCursor()
{
ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
return;
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
{
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
SDL_ShowCursor(SDL_FALSE);
}
else
{
// Show OS mouse cursor
SDL_SetCursor(g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]);
SDL_ShowCursor(SDL_TRUE);
}
}
void ImGui_ImplSDL2_NewFrame()
{
ImGuiIO& io = ImGui::GetIO();
// Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
static Uint64 frequency = SDL_GetPerformanceFrequency();
Uint64 current_time = SDL_GetPerformanceCounter();
io.DeltaTime = g_Time > 0 ? (float)((double)(current_time - g_Time) / frequency) : (float)(1.0f / 60.0f);
g_Time = current_time;
ImGui_ImplSDL2_UpdateMousePosAndButtons();
ImGui_ImplSDL2_UpdateMouseCursor();
}
-28
View File
@@ -1,28 +0,0 @@
// dear imgui: Platform Binding for SDL2
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
// Implemented features:
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Clipboard support.
// [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// Missing features:
// [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME.
// [ ] Platform: Multi-viewport + Minimized windows seems to break mouse wheel events (at least under Windows).
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
#pragma once
#include "imgui.h"
struct SDL_Window;
typedef union SDL_Event SDL_Event;
IMGUI_IMPL_API void ImGui_ImplSDL2_Init(SDL_Window* window);
IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown();
IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame();
IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event);
+1 -1
View File
@@ -2,7 +2,7 @@
#include "common/window_info.h"
#include "core/host_display.h"
#include "core/host_interface.h"
#include "frontend-common/common_host_interface.h"
#include "frontend-common/common_host.h"
#include <array>
#include <deque>
#include <functional>
+1 -1
View File
@@ -302,7 +302,7 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
s32 window_width, window_height;
SDL_GetWindowSize(m_window, &window_width, &window_height);
m_display->ResizeRenderWindow(window_width, window_height);
OnHostDisplayResized();
HostDisplayResized();
}
break;
+42 -21
View File
@@ -6,6 +6,7 @@ set(SRCS
resources/resources.qrc
aboutdialog.cpp
aboutdialog.h
aboutdialog.ui
advancedsettingswidget.cpp
advancedsettingswidget.h
advancedsettingswidget.ui
@@ -18,19 +19,32 @@ set(SRCS
biossettingswidget.cpp
biossettingswidget.h
biossettingswidget.ui
cheatmanagerdialog.cpp
cheatmanagerdialog.h
cheatmanagerdialog.ui
cheatcodeeditordialog.cpp
cheatcodeeditordialog.h
cheatcodeeditordialog.ui
cheatmanagerdialog.cpp
cheatmanagerdialog.h
cheatmanagerdialog.ui
collapsiblewidget.cpp
collapsiblewidget.h
consolesettingswidget.cpp
consolesettingswidget.h
consolesettingswidget.ui
controllersettingswidget.cpp
controllersettingswidget.h
controllerbindingwidget_analog_controller.ui
controllerbindingwidget_analog_joystick.ui
controllerbindingwidget_digital_controller.ui
controllerbindingwidget_guncon.ui
controllerbindingwidget_negcon.ui
controllerbindingwidgets.cpp
controllerbindingwidgets.h
controllerbindingwidget.ui
controllerglobalsettingswidget.cpp
controllerglobalsettingswidget.h
controllerglobalsettingswidget.ui
controllersettingsdialog.cpp
controllersettingsdialog.h
controllersettingsdialog.ui
controllersettingwidgetbinder.h
debuggermodels.cpp
debuggermodels.h
debuggerwindow.cpp
@@ -39,14 +53,22 @@ set(SRCS
displaysettingswidget.cpp
displaysettingswidget.h
displaysettingswidget.ui
displaywidget.cpp
displaywidget.h
emptygamelistwidget.ui
emulationsettingswidget.cpp
emulationsettingswidget.h
emulationsettingswidget.ui
enhancementsettingswidget.cpp
enhancementsettingswidget.h
enhancementsettingswidget.ui
foldersettingswidget.cpp
foldersettingswidget.h
foldersettingswidget.ui
gamelistmodel.cpp
gamelistmodel.h
gamelistrefreshthread.cpp
gamelistrefreshthread.h
gamelistsearchdirectoriesmodel.cpp
gamelistsearchdirectoriesmodel.h
gamelistsettingswidget.cpp
@@ -54,9 +76,10 @@ set(SRCS
gamelistsettingswidget.ui
gamelistwidget.cpp
gamelistwidget.h
gamepropertiesdialog.cpp
gamepropertiesdialog.h
gamepropertiesdialog.ui
gamelistwidget.ui
gamesummarywidget.cpp
gamesummarywidget.h
gamesummarywidget.ui
gdbconnection.cpp
gdbconnection.h
gdbserver.cpp
@@ -69,11 +92,8 @@ set(SRCS
inputbindingdialog.cpp
inputbindingdialog.h
inputbindingdialog.ui
inputbindingmonitor.cpp
inputbindingmonitor.h
inputbindingwidgets.cpp
inputbindingwidgets.h
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
@@ -92,17 +112,18 @@ set(SRCS
postprocessingsettingswidget.ui
postprocessingshaderconfigwidget.cpp
postprocessingshaderconfigwidget.h
qtdisplaywidget.cpp
qtdisplaywidget.h
qthostinterface.cpp
qthostinterface.h
qthost.cpp
qthost.h
qtkeycodes.cpp
qtprogresscallback.cpp
qtprogresscallback.h
qtutils.cpp
qtutils.h
resource.h
settingsdialog.cpp
settingsdialog.h
settingsdialog.ui
settingwidgetbinder.h
)
if(ENABLE_CHEEVOS)
@@ -135,11 +156,11 @@ set(TS_FILES
)
set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/translations")
qt5_add_translation(QM_FILES ${TS_FILES})
qt6_add_translation(QM_FILES ${TS_FILES})
add_executable(duckstation-qt ${SRCS} ${QM_FILES})
target_include_directories(duckstation-qt PRIVATE "${Qt5Gui_PRIVATE_INCLUDE_DIRS}")
target_link_libraries(duckstation-qt PRIVATE frontend-common core common imgui glad minizip scmversion Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network)
target_include_directories(duckstation-qt PRIVATE "${Qt6Gui_PRIVATE_INCLUDE_DIRS}" "${CMAKE_CURRENT_SOURCE_DIR}")
target_link_libraries(duckstation-qt PRIVATE frontend-common core common imgui glad minizip scmversion Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network)
if(WIN32)
# We want a Windows subsystem application not console.
@@ -148,7 +169,7 @@ if(WIN32)
DEBUG_POSTFIX "-debug")
# Copy in Qt DLLs. Borrowed from Dolphin.
get_target_property(MOC_EXECUTABLE_LOCATION Qt5::moc IMPORTED_LOCATION)
get_target_property(MOC_EXECUTABLE_LOCATION Qt6::moc IMPORTED_LOCATION)
get_filename_component(QT_BINARY_DIRECTORY "${MOC_EXECUTABLE_LOCATION}" DIRECTORY)
find_program(WINDEPLOYQT_EXE windeployqt HINTS "${QT_BINARY_DIRECTORY}")
add_custom_command(TARGET duckstation-qt POST_BUILD
@@ -186,11 +207,11 @@ if(APPLE)
set_source_files_properties("${CMAKE_CURRENT_SOURCE_DIR}/DuckStation.icns" PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
# Copy Qt plugins into the bundle
get_target_property(qtcocoa_location Qt5::QCocoaIntegrationPlugin LOCATION)
get_target_property(qtcocoa_location Qt6::QCocoaIntegrationPlugin LOCATION)
target_sources(duckstation-qt PRIVATE "${qtcocoa_location}")
set_source_files_properties("${qtcocoa_location}" PROPERTIES MACOSX_PACKAGE_LOCATION MacOS/platforms)
get_target_property(qtmacstyle_location Qt5::QMacStylePlugin LOCATION)
get_target_property(qtmacstyle_location Qt6::QMacStylePlugin LOCATION)
target_sources(duckstation-qt PRIVATE "${qtmacstyle_location}")
set_source_files_properties("${qtmacstyle_location}" PROPERTIES MACOSX_PACKAGE_LOCATION MacOS/styles)
@@ -1,6 +1,6 @@
#include "achievementlogindialog.h"
#include "core/cheevos.h"
#include "qthostinterface.h"
#include "frontend-common/achievements.h"
#include "qthost.h"
#include <QtWidgets/QMessageBox>
AchievementLoginDialog::AchievementLoginDialog(QWidget* parent) : QDialog(parent)
@@ -24,8 +24,8 @@ void AchievementLoginDialog::loginClicked()
m_ui.status->setText(tr("Logging in..."));
enableUI(false);
QtHostInterface::GetInstance()->executeOnEmulationThread([this, username, password]() {
const bool result = Cheevos::Login(username.toStdString().c_str(), password.toStdString().c_str());
Host::RunOnCPUThread([this, username, password]() {
const bool result = Achievements::Login(username.toStdString().c_str(), password.toStdString().c_str());
QMetaObject::invokeMethod(this, "processLoginResult", Qt::QueuedConnection, Q_ARG(bool, result));
});
}

Some files were not shown because too many files have changed in this diff Show More