UI: Massive revamp, new features and improvements
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 += "_";
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace FrontendCommon {
|
||||
namespace Common {
|
||||
|
||||
class HTTPDownloader
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <signal.h>
|
||||
Log_SetChannel(HTTPDownloaderCurl);
|
||||
|
||||
namespace FrontendCommon {
|
||||
namespace Common {
|
||||
|
||||
HTTPDownloaderCurl::HTTPDownloaderCurl() : HTTPDownloader() {}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <mutex>
|
||||
#include <curl/curl.h>
|
||||
|
||||
namespace FrontendCommon {
|
||||
namespace Common {
|
||||
|
||||
class HTTPDownloaderCurl final : public HTTPDownloader
|
||||
{
|
||||
|
||||
@@ -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)) {}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include <winrt/windows.Web.Http.h>
|
||||
|
||||
namespace FrontendCommon {
|
||||
namespace Common {
|
||||
|
||||
class HTTPDownloaderUWP final : public HTTPDownloader
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ Log_SetChannel(HTTPDownloaderWinHttp);
|
||||
|
||||
#pragma comment(lib, "winhttp.lib")
|
||||
|
||||
namespace FrontendCommon {
|
||||
namespace Common {
|
||||
|
||||
HTTPDownloaderWinHttp::HTTPDownloaderWinHttp() : HTTPDownloader() {}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include <winhttp.h>
|
||||
|
||||
namespace FrontendCommon {
|
||||
namespace Common {
|
||||
|
||||
class HTTPDownloaderWinHttp final : public HTTPDownloader
|
||||
{
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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))
|
||||
|
||||
@@ -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
@@ -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))
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+135
-182
@@ -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
@@ -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
@@ -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
@@ -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" />
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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));
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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;
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
#include <imgui.h>
|
||||
|
||||
namespace ImGui {
|
||||
void StyleColorsDarker(ImGuiStyle* dst = nullptr);
|
||||
} // namespace ImGui
|
||||
+2
-1
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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,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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+245
-16
@@ -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
|
||||
|
||||
@@ -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(); \
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -126,7 +126,7 @@ enum class ControllerType
|
||||
DigitalController,
|
||||
AnalogController,
|
||||
AnalogJoystick,
|
||||
NamcoGunCon,
|
||||
GunCon,
|
||||
PlayStationMouse,
|
||||
NeGcon,
|
||||
Count
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user