mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-12 20:55:56 +00:00
Merge branch 'main' into shader_cache
This commit is contained in:
commit
f9f75ef085
109 changed files with 2961 additions and 1883 deletions
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
|
@ -225,6 +225,9 @@ jobs:
|
||||||
archives: qtbase qttools
|
archives: qtbase qttools
|
||||||
modules: qtmultimedia
|
modules: qtmultimedia
|
||||||
|
|
||||||
|
- name: Workaround Qt <=6.9.1 issue
|
||||||
|
run: sed -i '' '/target_link_libraries(WrapOpenGL::WrapOpenGL INTERFACE ${__opengl_agl_fw_path})/d' ${{env.QT_ROOT_DIR}}/lib/cmake/Qt6/FindWrapOpenGL.cmake
|
||||||
|
|
||||||
- name: Cache CMake Configuration
|
- name: Cache CMake Configuration
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
env:
|
env:
|
||||||
|
|
|
@ -654,6 +654,7 @@ set(COMMON src/common/logging/backend.cpp
|
||||||
src/common/assert.cpp
|
src/common/assert.cpp
|
||||||
src/common/assert.h
|
src/common/assert.h
|
||||||
src/common/binary_helper.h
|
src/common/binary_helper.h
|
||||||
|
src/common/bit_array.h
|
||||||
src/common/bit_field.h
|
src/common/bit_field.h
|
||||||
src/common/bounded_threadsafe_queue.h
|
src/common/bounded_threadsafe_queue.h
|
||||||
src/common/concepts.h
|
src/common/concepts.h
|
||||||
|
@ -914,9 +915,10 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
|
||||||
src/video_core/buffer_cache/buffer.h
|
src/video_core/buffer_cache/buffer.h
|
||||||
src/video_core/buffer_cache/buffer_cache.cpp
|
src/video_core/buffer_cache/buffer_cache.cpp
|
||||||
src/video_core/buffer_cache/buffer_cache.h
|
src/video_core/buffer_cache/buffer_cache.h
|
||||||
src/video_core/buffer_cache/memory_tracker_base.h
|
src/video_core/buffer_cache/memory_tracker.h
|
||||||
src/video_core/buffer_cache/range_set.h
|
src/video_core/buffer_cache/range_set.h
|
||||||
src/video_core/buffer_cache/word_manager.h
|
src/video_core/buffer_cache/region_definitions.h
|
||||||
|
src/video_core/buffer_cache/region_manager.h
|
||||||
src/video_core/renderer_vulkan/liverpool_to_vk.cpp
|
src/video_core/renderer_vulkan/liverpool_to_vk.cpp
|
||||||
src/video_core/renderer_vulkan/liverpool_to_vk.h
|
src/video_core/renderer_vulkan/liverpool_to_vk.h
|
||||||
src/video_core/renderer_vulkan/shader_cache.cpp
|
src/video_core/renderer_vulkan/shader_cache.cpp
|
||||||
|
@ -955,6 +957,10 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
|
||||||
src/video_core/renderer_vulkan/host_passes/fsr_pass.h
|
src/video_core/renderer_vulkan/host_passes/fsr_pass.h
|
||||||
src/video_core/renderer_vulkan/host_passes/pp_pass.cpp
|
src/video_core/renderer_vulkan/host_passes/pp_pass.cpp
|
||||||
src/video_core/renderer_vulkan/host_passes/pp_pass.h
|
src/video_core/renderer_vulkan/host_passes/pp_pass.h
|
||||||
|
src/video_core/texture_cache/blit_helper.cpp
|
||||||
|
src/video_core/texture_cache/blit_helper.h
|
||||||
|
src/video_core/texture_cache/host_compatibility.cpp
|
||||||
|
src/video_core/texture_cache/host_compatibility.h
|
||||||
src/video_core/texture_cache/image.cpp
|
src/video_core/texture_cache/image.cpp
|
||||||
src/video_core/texture_cache/image.h
|
src/video_core/texture_cache/image.h
|
||||||
src/video_core/texture_cache/image_info.cpp
|
src/video_core/texture_cache/image_info.cpp
|
||||||
|
@ -968,8 +974,6 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
|
||||||
src/video_core/texture_cache/tile_manager.cpp
|
src/video_core/texture_cache/tile_manager.cpp
|
||||||
src/video_core/texture_cache/tile_manager.h
|
src/video_core/texture_cache/tile_manager.h
|
||||||
src/video_core/texture_cache/types.h
|
src/video_core/texture_cache/types.h
|
||||||
src/video_core/texture_cache/host_compatibility.cpp
|
|
||||||
src/video_core/texture_cache/host_compatibility.h
|
|
||||||
src/video_core/page_manager.cpp
|
src/video_core/page_manager.cpp
|
||||||
src/video_core/page_manager.h
|
src/video_core/page_manager.h
|
||||||
src/video_core/multi_level_page_table.h
|
src/video_core/multi_level_page_table.h
|
||||||
|
|
411
src/common/bit_array.h
Normal file
411
src/common/bit_array.h
Normal file
|
@ -0,0 +1,411 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
#include "common/types.h"
|
||||||
|
|
||||||
|
#ifdef __AVX2__
|
||||||
|
#define BIT_ARRAY_USE_AVX
|
||||||
|
#include <immintrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
class BitArray {
|
||||||
|
static_assert(N % 64 == 0, "BitArray size must be a multiple of 64 bits.");
|
||||||
|
|
||||||
|
static constexpr size_t BITS_PER_WORD = 64;
|
||||||
|
static constexpr size_t WORD_COUNT = N / BITS_PER_WORD;
|
||||||
|
static constexpr size_t WORDS_PER_AVX = 4;
|
||||||
|
static constexpr size_t AVX_WORD_COUNT = WORD_COUNT / WORDS_PER_AVX;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Range = std::pair<size_t, size_t>;
|
||||||
|
|
||||||
|
class Iterator {
|
||||||
|
public:
|
||||||
|
explicit Iterator(const BitArray& bit_array_, u64 start) : bit_array(bit_array_) {
|
||||||
|
range = bit_array.FirstRangeFrom(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator& operator++() {
|
||||||
|
range = bit_array.FirstRangeFrom(range.second);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Iterator& other) const {
|
||||||
|
return range == other.range;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Iterator& other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Range& operator*() const {
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Range* operator->() const {
|
||||||
|
return ⦥
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const BitArray& bit_array;
|
||||||
|
Range range;
|
||||||
|
};
|
||||||
|
|
||||||
|
using const_iterator = Iterator;
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
using value_type = Range;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = const Range*;
|
||||||
|
using reference = const Range&;
|
||||||
|
|
||||||
|
BitArray() = default;
|
||||||
|
BitArray(const BitArray& other) = default;
|
||||||
|
BitArray& operator=(const BitArray& other) = default;
|
||||||
|
BitArray(BitArray&& other) noexcept = default;
|
||||||
|
BitArray& operator=(BitArray&& other) noexcept = default;
|
||||||
|
~BitArray() = default;
|
||||||
|
|
||||||
|
BitArray(const BitArray& other, size_t start, size_t end) {
|
||||||
|
if (start >= end || end > N) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const size_t first_word = start / BITS_PER_WORD;
|
||||||
|
const size_t last_word = (end - 1) / BITS_PER_WORD;
|
||||||
|
const size_t start_bit = start % BITS_PER_WORD;
|
||||||
|
const size_t end_bit = (end - 1) % BITS_PER_WORD;
|
||||||
|
const u64 start_mask = ~((1ULL << start_bit) - 1);
|
||||||
|
const u64 end_mask = end_bit == BITS_PER_WORD - 1 ? ~0ULL : (1ULL << (end_bit + 1)) - 1;
|
||||||
|
if (first_word == last_word) {
|
||||||
|
data[first_word] = other.data[first_word] & (start_mask & end_mask);
|
||||||
|
} else {
|
||||||
|
data[first_word] = other.data[first_word] & start_mask;
|
||||||
|
size_t i = first_word + 1;
|
||||||
|
#ifdef BIT_ARRAY_USE_AVX
|
||||||
|
for (; i + WORDS_PER_AVX <= last_word; i += WORDS_PER_AVX) {
|
||||||
|
const __m256i current =
|
||||||
|
_mm256_loadu_si256(reinterpret_cast<const __m256i*>(&other.data[i]));
|
||||||
|
_mm256_storeu_si256(reinterpret_cast<__m256i*>(&data[i]), current);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for (; i < last_word; ++i) {
|
||||||
|
data[i] = other.data[i];
|
||||||
|
}
|
||||||
|
data[last_word] = other.data[last_word] & end_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BitArray(const BitArray& other, const Range& range)
|
||||||
|
: BitArray(other, range.first, range.second) {}
|
||||||
|
|
||||||
|
const_iterator begin() const {
|
||||||
|
return Iterator(*this, 0);
|
||||||
|
}
|
||||||
|
const_iterator end() const {
|
||||||
|
return Iterator(*this, N);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr void Set(size_t idx) {
|
||||||
|
data[idx / BITS_PER_WORD] |= (1ULL << (idx % BITS_PER_WORD));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr void Unset(size_t idx) {
|
||||||
|
data[idx / BITS_PER_WORD] &= ~(1ULL << (idx % BITS_PER_WORD));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr bool Get(size_t idx) const {
|
||||||
|
return (data[idx / BITS_PER_WORD] & (1ULL << (idx % BITS_PER_WORD))) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void SetRange(size_t start, size_t end) {
|
||||||
|
if (start >= end || end > N) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const size_t first_word = start / BITS_PER_WORD;
|
||||||
|
const size_t last_word = (end - 1) / BITS_PER_WORD;
|
||||||
|
const size_t start_bit = start % BITS_PER_WORD;
|
||||||
|
const size_t end_bit = (end - 1) % BITS_PER_WORD;
|
||||||
|
const u64 start_mask = ~((1ULL << start_bit) - 1);
|
||||||
|
const u64 end_mask = end_bit == BITS_PER_WORD - 1 ? ~0ULL : (1ULL << (end_bit + 1)) - 1;
|
||||||
|
if (first_word == last_word) {
|
||||||
|
data[first_word] |= start_mask & end_mask;
|
||||||
|
} else {
|
||||||
|
data[first_word] |= start_mask;
|
||||||
|
size_t i = first_word + 1;
|
||||||
|
#ifdef BIT_ARRAY_USE_AVX
|
||||||
|
const __m256i value = _mm256_set1_epi64x(-1);
|
||||||
|
for (; i + WORDS_PER_AVX <= last_word; i += WORDS_PER_AVX) {
|
||||||
|
_mm256_storeu_si256(reinterpret_cast<__m256i*>(&data[i]), value);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for (; i < last_word; ++i) {
|
||||||
|
data[i] = ~0ULL;
|
||||||
|
}
|
||||||
|
data[last_word] |= end_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void UnsetRange(size_t start, size_t end) {
|
||||||
|
if (start >= end || end > N) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t first_word = start / BITS_PER_WORD;
|
||||||
|
const size_t last_word = (end - 1) / BITS_PER_WORD;
|
||||||
|
const size_t start_bit = start % BITS_PER_WORD;
|
||||||
|
const size_t end_bit = (end - 1) % BITS_PER_WORD;
|
||||||
|
const u64 start_mask = (1ULL << start_bit) - 1;
|
||||||
|
const u64 end_mask = end_bit == BITS_PER_WORD - 1 ? 0ULL : ~((1ULL << (end_bit + 1)) - 1);
|
||||||
|
if (first_word == last_word) {
|
||||||
|
data[first_word] &= start_mask | end_mask;
|
||||||
|
} else {
|
||||||
|
data[first_word] &= start_mask;
|
||||||
|
size_t i = first_word + 1;
|
||||||
|
#ifdef BIT_ARRAY_USE_AVX
|
||||||
|
const __m256i value = _mm256_setzero_si256();
|
||||||
|
for (; i + WORDS_PER_AVX <= last_word; i += WORDS_PER_AVX) {
|
||||||
|
_mm256_storeu_si256(reinterpret_cast<__m256i*>(&data[i]), value);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for (; i < last_word; ++i) {
|
||||||
|
data[i] = 0ULL;
|
||||||
|
}
|
||||||
|
data[last_word] &= end_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr void SetRange(const Range& range) {
|
||||||
|
SetRange(range.first, range.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr void UnsetRange(const Range& range) {
|
||||||
|
UnsetRange(range.first, range.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr void Clear() {
|
||||||
|
data.fill(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr void Fill() {
|
||||||
|
data.fill(~0ULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr bool None() const {
|
||||||
|
u64 result = 0;
|
||||||
|
for (const auto& word : data) {
|
||||||
|
result |= word;
|
||||||
|
}
|
||||||
|
return result == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr bool Any() const {
|
||||||
|
return !None();
|
||||||
|
}
|
||||||
|
|
||||||
|
Range FirstRangeFrom(size_t start) const {
|
||||||
|
if (start >= N) {
|
||||||
|
return {N, N};
|
||||||
|
}
|
||||||
|
const auto find_end_bit = [&](size_t word) {
|
||||||
|
#ifdef BIT_ARRAY_USE_AVX
|
||||||
|
const __m256i all_one = _mm256_set1_epi64x(-1);
|
||||||
|
for (; word + WORDS_PER_AVX <= WORD_COUNT; word += WORDS_PER_AVX) {
|
||||||
|
const __m256i current =
|
||||||
|
_mm256_loadu_si256(reinterpret_cast<const __m256i*>(&data[word]));
|
||||||
|
const __m256i cmp = _mm256_cmpeq_epi64(current, all_one);
|
||||||
|
if (_mm256_movemask_epi8(cmp) != 0xFFFFFFFF) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for (; word < WORD_COUNT; ++word) {
|
||||||
|
if (data[word] != ~0ULL) {
|
||||||
|
return (word * BITS_PER_WORD) + std::countr_one(data[word]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return N;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto word_bits = [&](size_t index, u64 word) {
|
||||||
|
const int empty_bits = std::countr_zero(word);
|
||||||
|
const int ones_count = std::countr_one(word >> empty_bits);
|
||||||
|
const size_t start_bit = index * BITS_PER_WORD + empty_bits;
|
||||||
|
if (ones_count + empty_bits < BITS_PER_WORD) {
|
||||||
|
return Range{start_bit, start_bit + ones_count};
|
||||||
|
}
|
||||||
|
return Range{start_bit, find_end_bit(index + 1)};
|
||||||
|
};
|
||||||
|
|
||||||
|
const size_t start_word = start / BITS_PER_WORD;
|
||||||
|
const size_t start_bit = start % BITS_PER_WORD;
|
||||||
|
const u64 masked_first = data[start_word] & (~((1ULL << start_bit) - 1));
|
||||||
|
if (masked_first) {
|
||||||
|
return word_bits(start_word, masked_first);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t word = start_word + 1;
|
||||||
|
#ifdef BIT_ARRAY_USE_AVX
|
||||||
|
for (; word + WORDS_PER_AVX <= WORD_COUNT; word += WORDS_PER_AVX) {
|
||||||
|
const __m256i current =
|
||||||
|
_mm256_loadu_si256(reinterpret_cast<const __m256i*>(&data[word]));
|
||||||
|
if (!_mm256_testz_si256(current, current)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for (; word < WORD_COUNT; ++word) {
|
||||||
|
if (data[word] != 0) {
|
||||||
|
return word_bits(word, data[word]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {N, N};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr Range FirstRange() const {
|
||||||
|
return FirstRangeFrom(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Range LastRangeFrom(size_t end) const {
|
||||||
|
if (end == 0) {
|
||||||
|
return {0, 0};
|
||||||
|
}
|
||||||
|
if (end > N) {
|
||||||
|
end = N;
|
||||||
|
}
|
||||||
|
const auto find_start_bit = [&](size_t word) {
|
||||||
|
#ifdef BIT_ARRAY_USE_AVX
|
||||||
|
const __m256i all_zero = _mm256_setzero_si256();
|
||||||
|
for (; word >= WORDS_PER_AVX; word -= WORDS_PER_AVX) {
|
||||||
|
const __m256i current = _mm256_loadu_si256(
|
||||||
|
reinterpret_cast<const __m256i*>(&data[word - WORDS_PER_AVX]));
|
||||||
|
const __m256i cmp = _mm256_cmpeq_epi64(current, all_zero);
|
||||||
|
if (_mm256_movemask_epi8(cmp) != 0xFFFFFFFF) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for (; word > 0; --word) {
|
||||||
|
if (data[word - 1] != ~0ULL) {
|
||||||
|
return word * BITS_PER_WORD - std::countl_one(data[word - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size_t(0);
|
||||||
|
};
|
||||||
|
const auto word_bits = [&](size_t index, u64 word) {
|
||||||
|
const int empty_bits = std::countl_zero(word);
|
||||||
|
const int ones_count = std::countl_one(word << empty_bits);
|
||||||
|
const size_t end_bit = index * BITS_PER_WORD - empty_bits;
|
||||||
|
if (empty_bits + ones_count < BITS_PER_WORD) {
|
||||||
|
return Range{end_bit - ones_count, end_bit};
|
||||||
|
}
|
||||||
|
return Range{find_start_bit(index - 1), end_bit};
|
||||||
|
};
|
||||||
|
const size_t end_word = ((end - 1) / BITS_PER_WORD) + 1;
|
||||||
|
const size_t end_bit = (end - 1) % BITS_PER_WORD;
|
||||||
|
u64 masked_last = data[end_word - 1];
|
||||||
|
if (end_bit < BITS_PER_WORD - 1) {
|
||||||
|
masked_last &= (1ULL << (end_bit + 1)) - 1;
|
||||||
|
}
|
||||||
|
if (masked_last) {
|
||||||
|
return word_bits(end_word, masked_last);
|
||||||
|
}
|
||||||
|
size_t word = end_word - 1;
|
||||||
|
#ifdef BIT_ARRAY_USE_AVX
|
||||||
|
for (; word >= WORDS_PER_AVX; word -= WORDS_PER_AVX) {
|
||||||
|
const __m256i current =
|
||||||
|
_mm256_loadu_si256(reinterpret_cast<const __m256i*>(&data[word - WORDS_PER_AVX]));
|
||||||
|
if (!_mm256_testz_si256(current, current)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for (; word > 0; --word) {
|
||||||
|
if (data[word - 1] != 0) {
|
||||||
|
return word_bits(word, data[word - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {0, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr Range LastRange() const {
|
||||||
|
return LastRangeFrom(N);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr size_t Size() const {
|
||||||
|
return N;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr BitArray& operator|=(const BitArray& other) {
|
||||||
|
for (size_t i = 0; i < WORD_COUNT; ++i) {
|
||||||
|
data[i] |= other.data[i];
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr BitArray& operator&=(const BitArray& other) {
|
||||||
|
for (size_t i = 0; i < WORD_COUNT; ++i) {
|
||||||
|
data[i] &= other.data[i];
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr BitArray& operator^=(const BitArray& other) {
|
||||||
|
for (size_t i = 0; i < WORD_COUNT; ++i) {
|
||||||
|
data[i] ^= other.data[i];
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr BitArray& operator~() {
|
||||||
|
for (size_t i = 0; i < WORD_COUNT; ++i) {
|
||||||
|
data[i] = ~data[i];
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr BitArray operator|(const BitArray& other) const {
|
||||||
|
BitArray result = *this;
|
||||||
|
result |= other;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr BitArray operator&(const BitArray& other) const {
|
||||||
|
BitArray result = *this;
|
||||||
|
result &= other;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr BitArray operator^(const BitArray& other) const {
|
||||||
|
BitArray result = *this;
|
||||||
|
result ^= other;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr BitArray operator~() const {
|
||||||
|
BitArray result = *this;
|
||||||
|
result = ~result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr bool operator==(const BitArray& other) const {
|
||||||
|
u64 result = 0;
|
||||||
|
for (size_t i = 0; i < WORD_COUNT; ++i) {
|
||||||
|
result |= data[i] ^ other.data[i];
|
||||||
|
}
|
||||||
|
return result == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr bool operator!=(const BitArray& other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::array<u64, WORD_COUNT> data{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Common
|
|
@ -42,7 +42,6 @@ static std::string logFilter;
|
||||||
static std::string logType = "sync";
|
static std::string logType = "sync";
|
||||||
static std::string userName = "shadPS4";
|
static std::string userName = "shadPS4";
|
||||||
static std::string chooseHomeTab;
|
static std::string chooseHomeTab;
|
||||||
static std::string backButtonBehavior = "left";
|
|
||||||
static bool useSpecialPad = false;
|
static bool useSpecialPad = false;
|
||||||
static int specialPadClass = 1;
|
static int specialPadClass = 1;
|
||||||
static bool isMotionControlsEnabled = true;
|
static bool isMotionControlsEnabled = true;
|
||||||
|
@ -81,10 +80,6 @@ static std::vector<GameInstallDir> settings_install_dirs = {};
|
||||||
std::vector<bool> install_dirs_enabled = {};
|
std::vector<bool> install_dirs_enabled = {};
|
||||||
std::filesystem::path settings_addon_install_dir = {};
|
std::filesystem::path settings_addon_install_dir = {};
|
||||||
std::filesystem::path save_data_path = {};
|
std::filesystem::path save_data_path = {};
|
||||||
u32 mw_themes = 0;
|
|
||||||
std::vector<std::string> m_elf_viewer;
|
|
||||||
std::vector<std::string> m_recent_files;
|
|
||||||
std::string emulator_language = "en_US";
|
|
||||||
static bool isFullscreen = false;
|
static bool isFullscreen = false;
|
||||||
static std::string fullscreenMode = "Windowed";
|
static std::string fullscreenMode = "Windowed";
|
||||||
static bool isHDRAllowed = false;
|
static bool isHDRAllowed = false;
|
||||||
|
@ -209,10 +204,6 @@ std::string getChooseHomeTab() {
|
||||||
return chooseHomeTab;
|
return chooseHomeTab;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getBackButtonBehavior() {
|
|
||||||
return backButtonBehavior;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getUseSpecialPad() {
|
bool getUseSpecialPad() {
|
||||||
return useSpecialPad;
|
return useSpecialPad;
|
||||||
}
|
}
|
||||||
|
@ -428,10 +419,6 @@ void setChooseHomeTab(const std::string& type) {
|
||||||
chooseHomeTab = type;
|
chooseHomeTab = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBackButtonBehavior(const std::string& type) {
|
|
||||||
backButtonBehavior = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setUseSpecialPad(bool use) {
|
void setUseSpecialPad(bool use) {
|
||||||
useSpecialPad = use;
|
useSpecialPad = use;
|
||||||
}
|
}
|
||||||
|
@ -484,24 +471,6 @@ void setAddonInstallDir(const std::filesystem::path& dir) {
|
||||||
settings_addon_install_dir = dir;
|
settings_addon_install_dir = dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setMainWindowTheme(u32 theme) {
|
|
||||||
mw_themes = theme;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setElfViewer(const std::vector<std::string>& elfList) {
|
|
||||||
m_elf_viewer.resize(elfList.size());
|
|
||||||
m_elf_viewer = elfList;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setRecentFiles(const std::vector<std::string>& recentFiles) {
|
|
||||||
m_recent_files.resize(recentFiles.size());
|
|
||||||
m_recent_files = recentFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setEmulatorLanguage(std::string language) {
|
|
||||||
emulator_language = language;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setGameInstallDirs(const std::vector<std::filesystem::path>& dirs_config) {
|
void setGameInstallDirs(const std::vector<std::filesystem::path>& dirs_config) {
|
||||||
settings_install_dirs.clear();
|
settings_install_dirs.clear();
|
||||||
for (const auto& dir : dirs_config) {
|
for (const auto& dir : dirs_config) {
|
||||||
|
@ -543,22 +512,6 @@ std::filesystem::path getAddonInstallDir() {
|
||||||
return settings_addon_install_dir;
|
return settings_addon_install_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 getMainWindowTheme() {
|
|
||||||
return mw_themes;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> getElfViewer() {
|
|
||||||
return m_elf_viewer;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> getRecentFiles() {
|
|
||||||
return m_recent_files;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string getEmulatorLanguage() {
|
|
||||||
return emulator_language;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetLanguage() {
|
u32 GetLanguage() {
|
||||||
return m_language;
|
return m_language;
|
||||||
}
|
}
|
||||||
|
@ -620,7 +573,6 @@ void load(const std::filesystem::path& path) {
|
||||||
|
|
||||||
cursorState = toml::find_or<int>(input, "cursorState", HideCursorState::Idle);
|
cursorState = toml::find_or<int>(input, "cursorState", HideCursorState::Idle);
|
||||||
cursorHideTimeout = toml::find_or<int>(input, "cursorHideTimeout", 5);
|
cursorHideTimeout = toml::find_or<int>(input, "cursorHideTimeout", 5);
|
||||||
backButtonBehavior = toml::find_or<std::string>(input, "backButtonBehavior", "left");
|
|
||||||
useSpecialPad = toml::find_or<bool>(input, "useSpecialPad", false);
|
useSpecialPad = toml::find_or<bool>(input, "useSpecialPad", false);
|
||||||
specialPadClass = toml::find_or<int>(input, "specialPadClass", 1);
|
specialPadClass = toml::find_or<int>(input, "specialPadClass", 1);
|
||||||
isMotionControlsEnabled = toml::find_or<bool>(input, "isMotionControlsEnabled", true);
|
isMotionControlsEnabled = toml::find_or<bool>(input, "isMotionControlsEnabled", true);
|
||||||
|
@ -668,7 +620,6 @@ void load(const std::filesystem::path& path) {
|
||||||
const toml::value& gui = data.at("GUI");
|
const toml::value& gui = data.at("GUI");
|
||||||
|
|
||||||
load_game_size = toml::find_or<bool>(gui, "loadGameSizeEnabled", true);
|
load_game_size = toml::find_or<bool>(gui, "loadGameSizeEnabled", true);
|
||||||
mw_themes = toml::find_or<int>(gui, "theme", 0);
|
|
||||||
|
|
||||||
const auto install_dir_array =
|
const auto install_dir_array =
|
||||||
toml::find_or<std::vector<std::u8string>>(gui, "installDirs", {});
|
toml::find_or<std::vector<std::u8string>>(gui, "installDirs", {});
|
||||||
|
@ -693,9 +644,6 @@ void load(const std::filesystem::path& path) {
|
||||||
save_data_path = toml::find_fs_path_or(gui, "saveDataPath", {});
|
save_data_path = toml::find_fs_path_or(gui, "saveDataPath", {});
|
||||||
|
|
||||||
settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {});
|
settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {});
|
||||||
m_elf_viewer = toml::find_or<std::vector<std::string>>(gui, "elfDirs", {});
|
|
||||||
m_recent_files = toml::find_or<std::vector<std::string>>(gui, "recentFiles", {});
|
|
||||||
emulator_language = toml::find_or<std::string>(gui, "emulatorLanguage", "en_US");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.contains("Settings")) {
|
if (data.contains("Settings")) {
|
||||||
|
@ -708,19 +656,6 @@ void load(const std::filesystem::path& path) {
|
||||||
const toml::value& keys = data.at("Keys");
|
const toml::value& keys = data.at("Keys");
|
||||||
trophyKey = toml::find_or<std::string>(keys, "TrophyKey", "");
|
trophyKey = toml::find_or<std::string>(keys, "TrophyKey", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the loaded language is in the allowed list
|
|
||||||
const std::vector<std::string> allowed_languages = {
|
|
||||||
"ar_SA", "da_DK", "de_DE", "el_GR", "en_US", "es_ES", "fa_IR", "fi_FI",
|
|
||||||
"fr_FR", "hu_HU", "id_ID", "it_IT", "ja_JP", "ko_KR", "lt_LT", "nb_NO",
|
|
||||||
"nl_NL", "pl_PL", "pt_BR", "pt_PT", "ro_RO", "ru_RU", "sq_AL", "sv_SE",
|
|
||||||
"tr_TR", "uk_UA", "vi_VN", "zh_CN", "zh_TW", "ca_ES", "sr_CS"};
|
|
||||||
|
|
||||||
if (std::find(allowed_languages.begin(), allowed_languages.end(), emulator_language) ==
|
|
||||||
allowed_languages.end()) {
|
|
||||||
emulator_language = "en_US"; // Default to en_US if not in the list
|
|
||||||
save(path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sortTomlSections(toml::ordered_value& data) {
|
void sortTomlSections(toml::ordered_value& data) {
|
||||||
|
@ -792,7 +727,6 @@ void save(const std::filesystem::path& path) {
|
||||||
data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup;
|
data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup;
|
||||||
data["Input"]["cursorState"] = cursorState;
|
data["Input"]["cursorState"] = cursorState;
|
||||||
data["Input"]["cursorHideTimeout"] = cursorHideTimeout;
|
data["Input"]["cursorHideTimeout"] = cursorHideTimeout;
|
||||||
data["Input"]["backButtonBehavior"] = backButtonBehavior;
|
|
||||||
data["Input"]["useSpecialPad"] = useSpecialPad;
|
data["Input"]["useSpecialPad"] = useSpecialPad;
|
||||||
data["Input"]["specialPadClass"] = specialPadClass;
|
data["Input"]["specialPadClass"] = specialPadClass;
|
||||||
data["Input"]["isMotionControlsEnabled"] = isMotionControlsEnabled;
|
data["Input"]["isMotionControlsEnabled"] = isMotionControlsEnabled;
|
||||||
|
@ -855,7 +789,6 @@ void save(const std::filesystem::path& path) {
|
||||||
|
|
||||||
data["GUI"]["addonInstallDir"] =
|
data["GUI"]["addonInstallDir"] =
|
||||||
std::string{fmt::UTF(settings_addon_install_dir.u8string()).data};
|
std::string{fmt::UTF(settings_addon_install_dir.u8string()).data};
|
||||||
data["GUI"]["emulatorLanguage"] = emulator_language;
|
|
||||||
data["Settings"]["consoleLanguage"] = m_language;
|
data["Settings"]["consoleLanguage"] = m_language;
|
||||||
|
|
||||||
// Sorting of TOML sections
|
// Sorting of TOML sections
|
||||||
|
@ -864,42 +797,6 @@ void save(const std::filesystem::path& path) {
|
||||||
std::ofstream file(path, std::ios::binary);
|
std::ofstream file(path, std::ios::binary);
|
||||||
file << data;
|
file << data;
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
saveMainWindow(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void saveMainWindow(const std::filesystem::path& path) {
|
|
||||||
toml::ordered_value data;
|
|
||||||
|
|
||||||
std::error_code error;
|
|
||||||
if (std::filesystem::exists(path, error)) {
|
|
||||||
try {
|
|
||||||
std::ifstream ifs;
|
|
||||||
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
|
||||||
ifs.open(path, std::ios_base::binary);
|
|
||||||
data = toml::parse<toml::ordered_type_config>(
|
|
||||||
ifs, std::string{fmt::UTF(path.filename().u8string()).data});
|
|
||||||
} catch (const std::exception& ex) {
|
|
||||||
fmt::print("Exception trying to parse config file. Exception: {}\n", ex.what());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (error) {
|
|
||||||
fmt::print("Filesystem error: {}\n", error.message());
|
|
||||||
}
|
|
||||||
fmt::print("Saving new configuration file {}\n", fmt::UTF(path.u8string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
data["GUI"]["theme"] = mw_themes;
|
|
||||||
data["GUI"]["elfDirs"] = m_elf_viewer;
|
|
||||||
data["GUI"]["recentFiles"] = m_recent_files;
|
|
||||||
|
|
||||||
// Sorting of TOML sections
|
|
||||||
sortTomlSections(data);
|
|
||||||
|
|
||||||
std::ofstream file(path, std::ios::binary);
|
|
||||||
file << data;
|
|
||||||
file.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDefaultValues() {
|
void setDefaultValues() {
|
||||||
|
@ -920,7 +817,6 @@ void setDefaultValues() {
|
||||||
cursorState = HideCursorState::Idle;
|
cursorState = HideCursorState::Idle;
|
||||||
cursorHideTimeout = 5;
|
cursorHideTimeout = 5;
|
||||||
trophyNotificationDuration = 6.0;
|
trophyNotificationDuration = 6.0;
|
||||||
backButtonBehavior = "left";
|
|
||||||
useSpecialPad = false;
|
useSpecialPad = false;
|
||||||
specialPadClass = 1;
|
specialPadClass = 1;
|
||||||
isDebugDump = false;
|
isDebugDump = false;
|
||||||
|
@ -937,7 +833,6 @@ void setDefaultValues() {
|
||||||
vkHostMarkers = false;
|
vkHostMarkers = false;
|
||||||
vkGuestMarkers = false;
|
vkGuestMarkers = false;
|
||||||
rdocEnable = false;
|
rdocEnable = false;
|
||||||
emulator_language = "en_US";
|
|
||||||
m_language = 1;
|
m_language = 1;
|
||||||
gpuId = -1;
|
gpuId = -1;
|
||||||
compatibilityData = false;
|
compatibilityData = false;
|
||||||
|
@ -967,7 +862,7 @@ l3 = x
|
||||||
r3 = m
|
r3 = m
|
||||||
|
|
||||||
options = enter
|
options = enter
|
||||||
touchpad = space
|
touchpad_center = space
|
||||||
|
|
||||||
pad_up = up
|
pad_up = up
|
||||||
pad_down = down
|
pad_down = down
|
||||||
|
@ -999,7 +894,7 @@ r2 = r2
|
||||||
r3 = r3
|
r3 = r3
|
||||||
|
|
||||||
options = options
|
options = options
|
||||||
touchpad = back
|
touchpad_center = back
|
||||||
|
|
||||||
pad_up = pad_up
|
pad_up = pad_up
|
||||||
pad_down = pad_down
|
pad_down = pad_down
|
||||||
|
|
|
@ -18,77 +18,96 @@ enum HideCursorState : int { Never, Idle, Always };
|
||||||
|
|
||||||
void load(const std::filesystem::path& path);
|
void load(const std::filesystem::path& path);
|
||||||
void save(const std::filesystem::path& path);
|
void save(const std::filesystem::path& path);
|
||||||
void saveMainWindow(const std::filesystem::path& path);
|
|
||||||
|
|
||||||
std::string getTrophyKey();
|
std::string getTrophyKey();
|
||||||
void setTrophyKey(std::string key);
|
void setTrophyKey(std::string key);
|
||||||
|
bool getIsFullscreen();
|
||||||
|
void setIsFullscreen(bool enable);
|
||||||
|
std::string getFullscreenMode();
|
||||||
|
void setFullscreenMode(std::string mode);
|
||||||
|
u32 getScreenWidth();
|
||||||
|
u32 getScreenHeight();
|
||||||
|
void setScreenWidth(u32 width);
|
||||||
|
void setScreenHeight(u32 height);
|
||||||
|
bool debugDump();
|
||||||
|
void setDebugDump(bool enable);
|
||||||
|
s32 getGpuId();
|
||||||
|
void setGpuId(s32 selectedGpuId);
|
||||||
|
bool allowHDR();
|
||||||
|
void setAllowHDR(bool enable);
|
||||||
|
bool collectShadersForDebug();
|
||||||
|
void setCollectShaderForDebug(bool enable);
|
||||||
|
bool showSplash();
|
||||||
|
void setShowSplash(bool enable);
|
||||||
|
std::string sideTrophy();
|
||||||
|
void setSideTrophy(std::string side);
|
||||||
|
bool nullGpu();
|
||||||
|
void setNullGpu(bool enable);
|
||||||
|
bool copyGPUCmdBuffers();
|
||||||
|
void setCopyGPUCmdBuffers(bool enable);
|
||||||
|
bool dumpShaders();
|
||||||
|
void setDumpShaders(bool enable);
|
||||||
|
u32 vblankDiv();
|
||||||
|
void setVblankDiv(u32 value);
|
||||||
|
bool getisTrophyPopupDisabled();
|
||||||
|
void setisTrophyPopupDisabled(bool disable);
|
||||||
|
s16 getCursorState();
|
||||||
|
void setCursorState(s16 cursorState);
|
||||||
|
bool vkValidationEnabled();
|
||||||
|
void setVkValidation(bool enable);
|
||||||
|
bool vkValidationSyncEnabled();
|
||||||
|
void setVkSyncValidation(bool enable);
|
||||||
|
bool getVkCrashDiagnosticEnabled();
|
||||||
|
void setVkCrashDiagnosticEnabled(bool enable);
|
||||||
|
bool getVkHostMarkersEnabled();
|
||||||
|
void setVkHostMarkersEnabled(bool enable);
|
||||||
|
bool getVkGuestMarkersEnabled();
|
||||||
|
void setVkGuestMarkersEnabled(bool enable);
|
||||||
|
bool getEnableDiscordRPC();
|
||||||
|
void setEnableDiscordRPC(bool enable);
|
||||||
|
bool isRdocEnabled();
|
||||||
|
void setRdocEnabled(bool enable);
|
||||||
|
std::string getLogType();
|
||||||
|
void setLogType(const std::string& type);
|
||||||
|
std::string getLogFilter();
|
||||||
|
void setLogFilter(const std::string& type);
|
||||||
|
double getTrophyNotificationDuration();
|
||||||
|
void setTrophyNotificationDuration(double newTrophyNotificationDuration);
|
||||||
|
int getCursorHideTimeout();
|
||||||
|
void setCursorHideTimeout(int newcursorHideTimeout);
|
||||||
|
void setSeparateLogFilesEnabled(bool enabled);
|
||||||
|
bool getSeparateLogFilesEnabled();
|
||||||
|
u32 GetLanguage();
|
||||||
|
void setLanguage(u32 language);
|
||||||
|
void setUseSpecialPad(bool use);
|
||||||
|
bool getUseSpecialPad();
|
||||||
|
void setSpecialPadClass(int type);
|
||||||
|
int getSpecialPadClass();
|
||||||
|
bool getPSNSignedIn();
|
||||||
|
void setPSNSignedIn(bool sign); // no ui setting
|
||||||
|
bool patchShaders(); // no set
|
||||||
|
bool fpsColor(); // no set
|
||||||
|
bool isNeoModeConsole();
|
||||||
|
void setNeoMode(bool enable); // no ui setting
|
||||||
|
bool isDevKitConsole(); // no set
|
||||||
|
bool vkValidationGpuEnabled(); // no set
|
||||||
|
bool getIsMotionControlsEnabled();
|
||||||
|
void setIsMotionControlsEnabled(bool use);
|
||||||
|
|
||||||
|
// TODO
|
||||||
bool GetLoadGameSizeEnabled();
|
bool GetLoadGameSizeEnabled();
|
||||||
std::filesystem::path GetSaveDataPath();
|
std::filesystem::path GetSaveDataPath();
|
||||||
void setLoadGameSizeEnabled(bool enable);
|
void setLoadGameSizeEnabled(bool enable);
|
||||||
bool getIsFullscreen();
|
|
||||||
std::string getFullscreenMode();
|
|
||||||
bool isNeoModeConsole();
|
|
||||||
bool isDevKitConsole();
|
|
||||||
bool getisTrophyPopupDisabled();
|
|
||||||
bool getEnableDiscordRPC();
|
|
||||||
bool getCompatibilityEnabled();
|
bool getCompatibilityEnabled();
|
||||||
bool getCheckCompatibilityOnStartup();
|
bool getCheckCompatibilityOnStartup();
|
||||||
bool getPSNSignedIn();
|
|
||||||
|
|
||||||
std::string getLogFilter();
|
|
||||||
std::string getLogType();
|
|
||||||
std::string getUserName();
|
std::string getUserName();
|
||||||
std::string getChooseHomeTab();
|
std::string getChooseHomeTab();
|
||||||
|
|
||||||
s16 getCursorState();
|
|
||||||
int getCursorHideTimeout();
|
|
||||||
double getTrophyNotificationDuration();
|
|
||||||
std::string getBackButtonBehavior();
|
|
||||||
bool getUseSpecialPad();
|
|
||||||
int getSpecialPadClass();
|
|
||||||
bool getIsMotionControlsEnabled();
|
|
||||||
bool GetUseUnifiedInputConfig();
|
bool GetUseUnifiedInputConfig();
|
||||||
void SetUseUnifiedInputConfig(bool use);
|
void SetUseUnifiedInputConfig(bool use);
|
||||||
bool GetOverrideControllerColor();
|
bool GetOverrideControllerColor();
|
||||||
void SetOverrideControllerColor(bool enable);
|
void SetOverrideControllerColor(bool enable);
|
||||||
int* GetControllerCustomColor();
|
int* GetControllerCustomColor();
|
||||||
void SetControllerCustomColor(int r, int b, int g);
|
void SetControllerCustomColor(int r, int b, int g);
|
||||||
|
|
||||||
u32 getScreenWidth();
|
|
||||||
u32 getScreenHeight();
|
|
||||||
s32 getGpuId();
|
|
||||||
bool allowHDR();
|
|
||||||
|
|
||||||
bool debugDump();
|
|
||||||
bool collectShadersForDebug();
|
|
||||||
bool showSplash();
|
|
||||||
std::string sideTrophy();
|
|
||||||
bool nullGpu();
|
|
||||||
bool copyGPUCmdBuffers();
|
|
||||||
bool dumpShaders();
|
|
||||||
bool patchShaders();
|
|
||||||
bool isRdocEnabled();
|
|
||||||
bool fpsColor();
|
|
||||||
u32 vblankDiv();
|
|
||||||
|
|
||||||
void setDebugDump(bool enable);
|
|
||||||
void setCollectShaderForDebug(bool enable);
|
|
||||||
void setShowSplash(bool enable);
|
|
||||||
void setSideTrophy(std::string side);
|
|
||||||
void setNullGpu(bool enable);
|
|
||||||
void setAllowHDR(bool enable);
|
|
||||||
void setCopyGPUCmdBuffers(bool enable);
|
|
||||||
void setDumpShaders(bool enable);
|
|
||||||
void setVblankDiv(u32 value);
|
|
||||||
void setGpuId(s32 selectedGpuId);
|
|
||||||
void setScreenWidth(u32 width);
|
|
||||||
void setScreenHeight(u32 height);
|
|
||||||
void setIsFullscreen(bool enable);
|
|
||||||
void setFullscreenMode(std::string mode);
|
|
||||||
void setisTrophyPopupDisabled(bool disable);
|
|
||||||
void setEnableDiscordRPC(bool enable);
|
|
||||||
void setLanguage(u32 language);
|
|
||||||
void setNeoMode(bool enable);
|
|
||||||
void setUserName(const std::string& type);
|
void setUserName(const std::string& type);
|
||||||
void setChooseHomeTab(const std::string& type);
|
void setChooseHomeTab(const std::string& type);
|
||||||
void setGameInstallDirs(const std::vector<std::filesystem::path>& dirs_config);
|
void setGameInstallDirs(const std::vector<std::filesystem::path>& dirs_config);
|
||||||
|
@ -96,57 +115,19 @@ void setAllGameInstallDirs(const std::vector<GameInstallDir>& dirs_config);
|
||||||
void setSaveDataPath(const std::filesystem::path& path);
|
void setSaveDataPath(const std::filesystem::path& path);
|
||||||
void setCompatibilityEnabled(bool use);
|
void setCompatibilityEnabled(bool use);
|
||||||
void setCheckCompatibilityOnStartup(bool use);
|
void setCheckCompatibilityOnStartup(bool use);
|
||||||
void setPSNSignedIn(bool sign);
|
|
||||||
|
|
||||||
void setCursorState(s16 cursorState);
|
|
||||||
void setCursorHideTimeout(int newcursorHideTimeout);
|
|
||||||
void setTrophyNotificationDuration(double newTrophyNotificationDuration);
|
|
||||||
void setBackButtonBehavior(const std::string& type);
|
|
||||||
void setUseSpecialPad(bool use);
|
|
||||||
void setSpecialPadClass(int type);
|
|
||||||
void setIsMotionControlsEnabled(bool use);
|
|
||||||
|
|
||||||
void setLogType(const std::string& type);
|
|
||||||
void setLogFilter(const std::string& type);
|
|
||||||
void setSeparateLogFilesEnabled(bool enabled);
|
|
||||||
bool getSeparateLogFilesEnabled();
|
|
||||||
void setVkValidation(bool enable);
|
|
||||||
void setVkSyncValidation(bool enable);
|
|
||||||
void setRdocEnabled(bool enable);
|
|
||||||
|
|
||||||
bool vkValidationEnabled();
|
|
||||||
bool vkValidationSyncEnabled();
|
|
||||||
bool vkValidationGpuEnabled();
|
|
||||||
bool getVkCrashDiagnosticEnabled();
|
|
||||||
bool getVkHostMarkersEnabled();
|
|
||||||
bool getVkGuestMarkersEnabled();
|
|
||||||
void setVkCrashDiagnosticEnabled(bool enable);
|
|
||||||
void setVkHostMarkersEnabled(bool enable);
|
|
||||||
void setVkGuestMarkersEnabled(bool enable);
|
|
||||||
|
|
||||||
// Gui
|
// Gui
|
||||||
bool addGameInstallDir(const std::filesystem::path& dir, bool enabled = true);
|
bool addGameInstallDir(const std::filesystem::path& dir, bool enabled = true);
|
||||||
void removeGameInstallDir(const std::filesystem::path& dir);
|
void removeGameInstallDir(const std::filesystem::path& dir);
|
||||||
void setGameInstallDirEnabled(const std::filesystem::path& dir, bool enabled);
|
void setGameInstallDirEnabled(const std::filesystem::path& dir, bool enabled);
|
||||||
void setAddonInstallDir(const std::filesystem::path& dir);
|
void setAddonInstallDir(const std::filesystem::path& dir);
|
||||||
void setMainWindowTheme(u32 theme);
|
|
||||||
void setElfViewer(const std::vector<std::string>& elfList);
|
|
||||||
void setRecentFiles(const std::vector<std::string>& recentFiles);
|
|
||||||
void setEmulatorLanguage(std::string language);
|
|
||||||
|
|
||||||
const std::vector<std::filesystem::path> getGameInstallDirs();
|
const std::vector<std::filesystem::path> getGameInstallDirs();
|
||||||
const std::vector<bool> getGameInstallDirsEnabled();
|
const std::vector<bool> getGameInstallDirsEnabled();
|
||||||
std::filesystem::path getAddonInstallDir();
|
std::filesystem::path getAddonInstallDir();
|
||||||
u32 getMainWindowTheme();
|
|
||||||
std::vector<std::string> getElfViewer();
|
|
||||||
std::vector<std::string> getRecentFiles();
|
|
||||||
std::string getEmulatorLanguage();
|
|
||||||
|
|
||||||
void setDefaultValues();
|
void setDefaultValues();
|
||||||
|
|
||||||
// todo: name and function location pending
|
// todo: name and function location pending
|
||||||
std::filesystem::path GetFoolproofKbmConfigFile(const std::string& game_id = "");
|
std::filesystem::path GetFoolproofKbmConfigFile(const std::string& game_id = "");
|
||||||
|
|
||||||
// settings
|
|
||||||
u32 GetLanguage();
|
|
||||||
}; // namespace Config
|
}; // namespace Config
|
||||||
|
|
|
@ -186,7 +186,9 @@ public:
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
size_t WriteRaw(const void* data, size_t size) const {
|
size_t WriteRaw(const void* data, size_t size) const {
|
||||||
return std::fwrite(data, sizeof(T), size, file);
|
auto bytes = std::fwrite(data, sizeof(T), size, file);
|
||||||
|
std::fflush(file);
|
||||||
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|
|
@ -293,6 +293,7 @@ s64 PS4_SYSV_ABI write(s32 fd, const void* buf, size_t nbytes) {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return file->f.WriteRaw<u8>(buf, nbytes);
|
return file->f.WriteRaw<u8>(buf, nbytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -750,7 +751,24 @@ s32 PS4_SYSV_ABI posix_rename(const char* from, const char* to) {
|
||||||
*__Error() = POSIX_ENOTEMPTY;
|
*__Error() = POSIX_ENOTEMPTY;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On Windows, std::filesystem::rename will error if the file has been opened before.
|
||||||
std::filesystem::copy(src_path, dst_path, std::filesystem::copy_options::overwrite_existing);
|
std::filesystem::copy(src_path, dst_path, std::filesystem::copy_options::overwrite_existing);
|
||||||
|
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||||
|
auto file = h->GetFile(src_path);
|
||||||
|
if (file) {
|
||||||
|
// We need to force ReadWrite if the file had Write access before
|
||||||
|
// Otherwise f.Open will clear the file contents.
|
||||||
|
auto access_mode = file->f.GetAccessMode() == Common::FS::FileAccessMode::Write
|
||||||
|
? Common::FS::FileAccessMode::ReadWrite
|
||||||
|
: file->f.GetAccessMode();
|
||||||
|
file->f.Close();
|
||||||
|
std::filesystem::remove(src_path);
|
||||||
|
file->f.Open(dst_path, access_mode);
|
||||||
|
} else {
|
||||||
|
std::filesystem::remove(src_path);
|
||||||
|
}
|
||||||
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,21 +76,21 @@ static PS4_SYSV_ABI void stack_chk_fail() {
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
static thread_local int g_posix_errno = 0;
|
static thread_local s32 g_posix_errno = 0;
|
||||||
|
|
||||||
int* PS4_SYSV_ABI __Error() {
|
s32* PS4_SYSV_ABI __Error() {
|
||||||
return &g_posix_errno;
|
return &g_posix_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrSceToPosix(int error) {
|
void ErrSceToPosix(s32 error) {
|
||||||
g_posix_errno = error - ORBIS_KERNEL_ERROR_UNKNOWN;
|
g_posix_errno = error - ORBIS_KERNEL_ERROR_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ErrnoToSceKernelError(int error) {
|
s32 ErrnoToSceKernelError(s32 error) {
|
||||||
return error + ORBIS_KERNEL_ERROR_UNKNOWN;
|
return error + ORBIS_KERNEL_ERROR_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetPosixErrno(int e) {
|
void SetPosixErrno(s32 e) {
|
||||||
// Some error numbers are different between supported OSes
|
// Some error numbers are different between supported OSes
|
||||||
switch (e) {
|
switch (e) {
|
||||||
case EPERM:
|
case EPERM:
|
||||||
|
@ -132,15 +132,15 @@ void SetPosixErrno(int e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t g_mspace_atomic_id_mask = 0;
|
static u64 g_mspace_atomic_id_mask = 0;
|
||||||
static uint64_t g_mstate_table[64] = {0};
|
static u64 g_mstate_table[64] = {0};
|
||||||
|
|
||||||
struct HeapInfoInfo {
|
struct HeapInfoInfo {
|
||||||
uint64_t size = sizeof(HeapInfoInfo);
|
u64 size = sizeof(HeapInfoInfo);
|
||||||
uint32_t flag;
|
u32 flag;
|
||||||
uint32_t getSegmentInfo;
|
u32 getSegmentInfo;
|
||||||
uint64_t* mspace_atomic_id_mask;
|
u64* mspace_atomic_id_mask;
|
||||||
uint64_t* mstate_table;
|
u64* mstate_table;
|
||||||
};
|
};
|
||||||
|
|
||||||
void PS4_SYSV_ABI sceLibcHeapGetTraceInfo(HeapInfoInfo* info) {
|
void PS4_SYSV_ABI sceLibcHeapGetTraceInfo(HeapInfoInfo* info) {
|
||||||
|
@ -159,7 +159,7 @@ struct OrbisKernelUuid {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OrbisKernelUuid) == 0x10);
|
static_assert(sizeof(OrbisKernelUuid) == 0x10);
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) {
|
s32 PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) {
|
||||||
if (!orbisUuid) {
|
if (!orbisUuid) {
|
||||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -176,7 +176,7 @@ int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) {
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI kernel_ioctl(int fd, u64 cmd, VA_ARGS) {
|
s32 PS4_SYSV_ABI kernel_ioctl(s32 fd, u64 cmd, VA_ARGS) {
|
||||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||||
auto* file = h->GetFile(fd);
|
auto* file = h->GetFile(fd);
|
||||||
if (file == nullptr) {
|
if (file == nullptr) {
|
||||||
|
@ -190,7 +190,7 @@ int PS4_SYSV_ABI kernel_ioctl(int fd, u64 cmd, VA_ARGS) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
VA_CTX(ctx);
|
VA_CTX(ctx);
|
||||||
int result = file->device->ioctl(cmd, &ctx);
|
s32 result = file->device->ioctl(cmd, &ctx);
|
||||||
LOG_TRACE(Lib_Kernel, "ioctl: fd = {:X} cmd = {:X} result = {}", fd, cmd, result);
|
LOG_TRACE(Lib_Kernel, "ioctl: fd = {:X} cmd = {:X} result = {}", fd, cmd, result);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
ErrSceToPosix(result);
|
ErrSceToPosix(result);
|
||||||
|
@ -204,15 +204,15 @@ const char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI _sigprocmask() {
|
s32 PS4_SYSV_ABI _sigprocmask() {
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_getpagesize() {
|
s32 PS4_SYSV_ABI posix_getpagesize() {
|
||||||
return 16_KB;
|
return 16_KB;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_getsockname(Libraries::Net::OrbisNetId s,
|
s32 PS4_SYSV_ABI posix_getsockname(Libraries::Net::OrbisNetId s,
|
||||||
Libraries::Net::OrbisNetSockaddr* addr, u32* paddrlen) {
|
Libraries::Net::OrbisNetSockaddr* addr, u32* paddrlen) {
|
||||||
auto* netcall = Common::Singleton<Libraries::Net::NetInternal>::Instance();
|
auto* netcall = Common::Singleton<Libraries::Net::NetInternal>::Instance();
|
||||||
auto sock = netcall->FindSocket(s);
|
auto sock = netcall->FindSocket(s);
|
||||||
|
@ -221,7 +221,7 @@ int PS4_SYSV_ABI posix_getsockname(Libraries::Net::OrbisNetId s,
|
||||||
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
|
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int returncode = sock->GetSocketAddress(addr, paddrlen);
|
s32 returncode = sock->GetSocketAddress(addr, paddrlen);
|
||||||
if (returncode >= 0) {
|
if (returncode >= 0) {
|
||||||
LOG_ERROR(Lib_Net, "return code : {:#x}", (u32)returncode);
|
LOG_ERROR(Lib_Net, "return code : {:#x}", (u32)returncode);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -230,6 +230,19 @@ int PS4_SYSV_ABI posix_getsockname(Libraries::Net::OrbisNetId s,
|
||||||
LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode);
|
LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stubbed on non-devkit consoles
|
||||||
|
s32 PS4_SYSV_ABI sceKernelGetGPI() {
|
||||||
|
LOG_DEBUG(Kernel, "called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stubbed on non-devkit consoles
|
||||||
|
s32 PS4_SYSV_ABI sceKernelSetGPO() {
|
||||||
|
LOG_DEBUG(Kernel, "called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
|
void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
|
||||||
service_thread = std::jthread{KernelServiceThread};
|
service_thread = std::jthread{KernelServiceThread};
|
||||||
|
|
||||||
|
@ -277,6 +290,9 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
|
||||||
LIB_FUNCTION("3e+4Iv7IJ8U", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_accept);
|
LIB_FUNCTION("3e+4Iv7IJ8U", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_accept);
|
||||||
LIB_FUNCTION("aNeavPDNKzA", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_sendmsg);
|
LIB_FUNCTION("aNeavPDNKzA", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_sendmsg);
|
||||||
LIB_FUNCTION("pxnCmagrtao", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_listen);
|
LIB_FUNCTION("pxnCmagrtao", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_listen);
|
||||||
|
|
||||||
|
LIB_FUNCTION("4oXYe9Xmk0Q", "libkernel", 1, "libkernel", 1, 1, sceKernelGetGPI);
|
||||||
|
LIB_FUNCTION("ca7v6Cxulzs", "libkernel", 1, "libkernel", 1, 1, sceKernelSetGPO);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Libraries::Kernel
|
} // namespace Libraries::Kernel
|
||||||
|
|
|
@ -12,10 +12,10 @@ class SymbolsResolver;
|
||||||
|
|
||||||
namespace Libraries::Kernel {
|
namespace Libraries::Kernel {
|
||||||
|
|
||||||
void ErrSceToPosix(int result);
|
void ErrSceToPosix(s32 result);
|
||||||
int ErrnoToSceKernelError(int e);
|
s32 ErrnoToSceKernelError(s32 e);
|
||||||
void SetPosixErrno(int e);
|
void SetPosixErrno(s32 e);
|
||||||
int* PS4_SYSV_ABI __Error();
|
s32* PS4_SYSV_ABI __Error();
|
||||||
|
|
||||||
template <class F, F f>
|
template <class F, F f>
|
||||||
struct OrbisWrapperImpl;
|
struct OrbisWrapperImpl;
|
||||||
|
@ -33,7 +33,7 @@ struct OrbisWrapperImpl<PS4_SYSV_ABI R (*)(Args...), f> {
|
||||||
|
|
||||||
#define ORBIS(func) (Libraries::Kernel::OrbisWrapperImpl<decltype(&(func)), func>::wrap)
|
#define ORBIS(func) (Libraries::Kernel::OrbisWrapperImpl<decltype(&(func)), func>::wrap)
|
||||||
|
|
||||||
int* PS4_SYSV_ABI __Error();
|
s32* PS4_SYSV_ABI __Error();
|
||||||
|
|
||||||
void RegisterKernel(Core::Loader::SymbolsResolver* sym);
|
void RegisterKernel(Core::Loader::SymbolsResolver* sym);
|
||||||
|
|
||||||
|
|
|
@ -199,6 +199,10 @@ int PS4_SYSV_ABI sceNpTrophyDestroyContext(OrbisNpTrophyContext context) {
|
||||||
Common::SlotId contextId;
|
Common::SlotId contextId;
|
||||||
contextId.index = context - 1;
|
contextId.index = context - 1;
|
||||||
|
|
||||||
|
if (contextId.index >= trophy_contexts.size()) {
|
||||||
|
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
|
||||||
ContextKey contextkey = trophy_contexts[contextId];
|
ContextKey contextkey = trophy_contexts[contextId];
|
||||||
trophy_contexts.erase(contextId);
|
trophy_contexts.erase(contextId);
|
||||||
contexts_internal.erase(contextkey);
|
contexts_internal.erase(contextkey);
|
||||||
|
|
|
@ -447,21 +447,18 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
|
||||||
|
|
||||||
// Only do this on handle 1 for now
|
// Only do this on handle 1 for now
|
||||||
if (engine && handle == 1) {
|
if (engine && handle == 1) {
|
||||||
const auto gyro_poll_rate = engine->GetAccelPollRate();
|
auto now = std::chrono::steady_clock::now();
|
||||||
if (gyro_poll_rate != 0.0f) {
|
float deltaTime =
|
||||||
auto now = std::chrono::steady_clock::now();
|
std::chrono::duration_cast<std::chrono::microseconds>(now - controller->GetLastUpdate())
|
||||||
float deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(
|
.count() /
|
||||||
now - controller->GetLastUpdate())
|
1000000.0f;
|
||||||
.count() /
|
controller->SetLastUpdate(now);
|
||||||
1000000.0f;
|
Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation();
|
||||||
controller->SetLastUpdate(now);
|
Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation();
|
GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity, deltaTime,
|
||||||
Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
lastOrientation, outputOrientation);
|
||||||
GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
|
pData->orientation = outputOrientation;
|
||||||
deltaTime, lastOrientation, outputOrientation);
|
controller->SetLastOrientation(outputOrientation);
|
||||||
pData->orientation = outputOrientation;
|
|
||||||
controller->SetLastOrientation(outputOrientation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pData->touchData.touchNum =
|
pData->touchData.touchNum =
|
||||||
(state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0);
|
(state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0);
|
||||||
|
|
|
@ -22,25 +22,25 @@ static Core::FileSys::MntPoints* g_mnt = Common::Singleton<Core::FileSys::MntPoi
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const std::unordered_map<std::string, std::string> default_title = {
|
static const std::unordered_map<int, std::string> default_title = {
|
||||||
{"ja_JP", "セーブデータ"},
|
{0/*"ja_JP"*/, "セーブデータ"},
|
||||||
{"en_US", "Saved Data"},
|
{1/*"en_US"*/, "Saved Data"},
|
||||||
{"fr_FR", "Données sauvegardées"},
|
{2/*"fr_FR"*/, "Données sauvegardées"},
|
||||||
{"es_ES", "Datos guardados"},
|
{3/*"es_ES"*/, "Datos guardados"},
|
||||||
{"de_DE", "Gespeicherte Daten"},
|
{4/*"de_DE"*/, "Gespeicherte Daten"},
|
||||||
{"it_IT", "Dati salvati"},
|
{5/*"it_IT"*/, "Dati salvati"},
|
||||||
{"nl_NL", "Opgeslagen data"},
|
{6/*"nl_NL"*/, "Opgeslagen data"},
|
||||||
{"pt_PT", "Dados guardados"},
|
{7/*"pt_PT"*/, "Dados guardados"},
|
||||||
{"ru_RU", "Сохраненные данные"},
|
{8/*"ru_RU"*/, "Сохраненные данные"},
|
||||||
{"ko_KR", "저장 데이터"},
|
{9/*"ko_KR"*/, "저장 데이터"},
|
||||||
{"zh_CN", "保存数据"},
|
{10/*"zh_CN"*/, "保存数据"},
|
||||||
{"fi_FI", "Tallennetut tiedot"},
|
{12/*"fi_FI"*/, "Tallennetut tiedot"},
|
||||||
{"sv_SE", "Sparade data"},
|
{13/*"sv_SE"*/, "Sparade data"},
|
||||||
{"da_DK", "Gemte data"},
|
{14/*"da_DK"*/, "Gemte data"},
|
||||||
{"no_NO", "Lagrede data"},
|
{15/*"no_NO"*/, "Lagrede data"},
|
||||||
{"pl_PL", "Zapisane dane"},
|
{16/*"pl_PL"*/, "Zapisane dane"},
|
||||||
{"pt_BR", "Dados salvos"},
|
{17/*"pt_BR"*/, "Dados salvos"},
|
||||||
{"tr_TR", "Kayıtlı Veriler"},
|
{19/*"tr_TR"*/, "Kayıtlı Veriler"},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
@ -71,9 +71,9 @@ fs::path SaveInstance::GetParamSFOPath(const fs::path& dir_path) {
|
||||||
|
|
||||||
void SaveInstance::SetupDefaultParamSFO(PSF& param_sfo, std::string dir_name,
|
void SaveInstance::SetupDefaultParamSFO(PSF& param_sfo, std::string dir_name,
|
||||||
std::string game_serial) {
|
std::string game_serial) {
|
||||||
std::string locale = Config::getEmulatorLanguage();
|
int locale = Config::GetLanguage();
|
||||||
if (!default_title.contains(locale)) {
|
if (!default_title.contains(locale)) {
|
||||||
locale = "en_US";
|
locale = 1; // default to en_US if not found
|
||||||
}
|
}
|
||||||
|
|
||||||
#define P(type, key, ...) param_sfo.Add##type(std::string{key}, __VA_ARGS__)
|
#define P(type, key, ...) param_sfo.Add##type(std::string{key}, __VA_ARGS__)
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include "common/enum.h"
|
#include "common/enum.h"
|
||||||
#include "common/singleton.h"
|
#include "common/singleton.h"
|
||||||
|
|
|
@ -58,10 +58,7 @@ Emulator::Emulator() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Emulator::~Emulator() {
|
Emulator::~Emulator() {}
|
||||||
const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
|
|
||||||
Config::saveMainWindow(config_dir / "config.toml");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Emulator::Run(std::filesystem::path file, const std::vector<std::string> args) {
|
void Emulator::Run(std::filesystem::path file, const std::vector<std::string> args) {
|
||||||
if (std::filesystem::is_directory(file)) {
|
if (std::filesystem::is_directory(file)) {
|
||||||
|
|
|
@ -66,22 +66,25 @@ auto output_array = std::array{
|
||||||
ControllerOutput(LEFTJOYSTICK_HALFMODE),
|
ControllerOutput(LEFTJOYSTICK_HALFMODE),
|
||||||
ControllerOutput(RIGHTJOYSTICK_HALFMODE),
|
ControllerOutput(RIGHTJOYSTICK_HALFMODE),
|
||||||
ControllerOutput(KEY_TOGGLE),
|
ControllerOutput(KEY_TOGGLE),
|
||||||
|
ControllerOutput(MOUSE_GYRO_ROLL_MODE),
|
||||||
|
|
||||||
// Button mappings
|
// Button mappings
|
||||||
ControllerOutput(SDL_GAMEPAD_BUTTON_NORTH), // Triangle
|
ControllerOutput(SDL_GAMEPAD_BUTTON_NORTH), // Triangle
|
||||||
ControllerOutput(SDL_GAMEPAD_BUTTON_EAST), // Circle
|
ControllerOutput(SDL_GAMEPAD_BUTTON_EAST), // Circle
|
||||||
ControllerOutput(SDL_GAMEPAD_BUTTON_SOUTH), // Cross
|
ControllerOutput(SDL_GAMEPAD_BUTTON_SOUTH), // Cross
|
||||||
ControllerOutput(SDL_GAMEPAD_BUTTON_WEST), // Square
|
ControllerOutput(SDL_GAMEPAD_BUTTON_WEST), // Square
|
||||||
ControllerOutput(SDL_GAMEPAD_BUTTON_LEFT_SHOULDER), // L1
|
ControllerOutput(SDL_GAMEPAD_BUTTON_LEFT_SHOULDER), // L1
|
||||||
ControllerOutput(SDL_GAMEPAD_BUTTON_LEFT_STICK), // L3
|
ControllerOutput(SDL_GAMEPAD_BUTTON_LEFT_STICK), // L3
|
||||||
ControllerOutput(SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER), // R1
|
ControllerOutput(SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER), // R1
|
||||||
ControllerOutput(SDL_GAMEPAD_BUTTON_RIGHT_STICK), // R3
|
ControllerOutput(SDL_GAMEPAD_BUTTON_RIGHT_STICK), // R3
|
||||||
ControllerOutput(SDL_GAMEPAD_BUTTON_START), // Options
|
ControllerOutput(SDL_GAMEPAD_BUTTON_START), // Options
|
||||||
ControllerOutput(SDL_GAMEPAD_BUTTON_TOUCHPAD), // TouchPad
|
ControllerOutput(SDL_GAMEPAD_BUTTON_TOUCHPAD_LEFT), // TouchPad
|
||||||
ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_UP), // Up
|
ControllerOutput(SDL_GAMEPAD_BUTTON_TOUCHPAD_CENTER), // TouchPad
|
||||||
ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_DOWN), // Down
|
ControllerOutput(SDL_GAMEPAD_BUTTON_TOUCHPAD_RIGHT), // TouchPad
|
||||||
ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_LEFT), // Left
|
ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_UP), // Up
|
||||||
ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_RIGHT), // Right
|
ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_DOWN), // Down
|
||||||
|
ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_LEFT), // Left
|
||||||
|
ControllerOutput(SDL_GAMEPAD_BUTTON_DPAD_RIGHT), // Right
|
||||||
|
|
||||||
// Axis mappings
|
// Axis mappings
|
||||||
// ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_LEFTX, false),
|
// ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_LEFTX, false),
|
||||||
|
@ -130,6 +133,12 @@ static OrbisPadButtonDataOffset SDLGamepadToOrbisButton(u8 button) {
|
||||||
return OPBDO::Options;
|
return OPBDO::Options;
|
||||||
case SDL_GAMEPAD_BUTTON_TOUCHPAD:
|
case SDL_GAMEPAD_BUTTON_TOUCHPAD:
|
||||||
return OPBDO::TouchPad;
|
return OPBDO::TouchPad;
|
||||||
|
case SDL_GAMEPAD_BUTTON_TOUCHPAD_LEFT:
|
||||||
|
return OPBDO::TouchPad;
|
||||||
|
case SDL_GAMEPAD_BUTTON_TOUCHPAD_CENTER:
|
||||||
|
return OPBDO::TouchPad;
|
||||||
|
case SDL_GAMEPAD_BUTTON_TOUCHPAD_RIGHT:
|
||||||
|
return OPBDO::TouchPad;
|
||||||
case SDL_GAMEPAD_BUTTON_BACK:
|
case SDL_GAMEPAD_BUTTON_BACK:
|
||||||
return OPBDO::TouchPad;
|
return OPBDO::TouchPad;
|
||||||
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
|
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
|
||||||
|
@ -176,7 +185,7 @@ InputBinding GetBindingFromString(std::string& line) {
|
||||||
if (string_to_keyboard_key_map.find(t) != string_to_keyboard_key_map.end()) {
|
if (string_to_keyboard_key_map.find(t) != string_to_keyboard_key_map.end()) {
|
||||||
input = InputID(InputType::KeyboardMouse, string_to_keyboard_key_map.at(t));
|
input = InputID(InputType::KeyboardMouse, string_to_keyboard_key_map.at(t));
|
||||||
} else if (string_to_axis_map.find(t) != string_to_axis_map.end()) {
|
} else if (string_to_axis_map.find(t) != string_to_axis_map.end()) {
|
||||||
input = InputID(InputType::Axis, (u32)string_to_axis_map.at(t).axis);
|
input = InputID(InputType::Axis, string_to_axis_map.at(t).axis);
|
||||||
} else if (string_to_cbutton_map.find(t) != string_to_cbutton_map.end()) {
|
} else if (string_to_cbutton_map.find(t) != string_to_cbutton_map.end()) {
|
||||||
input = InputID(InputType::Controller, string_to_cbutton_map.at(t));
|
input = InputID(InputType::Controller, string_to_cbutton_map.at(t));
|
||||||
} else {
|
} else {
|
||||||
|
@ -227,19 +236,15 @@ void ParseInputConfig(const std::string game_id = "") {
|
||||||
line.erase(std::remove_if(line.begin(), line.end(),
|
line.erase(std::remove_if(line.begin(), line.end(),
|
||||||
[](unsigned char c) { return std::isspace(c); }),
|
[](unsigned char c) { return std::isspace(c); }),
|
||||||
line.end());
|
line.end());
|
||||||
|
|
||||||
if (line.empty()) {
|
if (line.empty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Truncate lines starting at #
|
// Truncate lines starting at #
|
||||||
std::size_t comment_pos = line.find('#');
|
std::size_t comment_pos = line.find('#');
|
||||||
if (comment_pos != std::string::npos) {
|
if (comment_pos != std::string::npos) {
|
||||||
line = line.substr(0, comment_pos);
|
line = line.substr(0, comment_pos);
|
||||||
}
|
}
|
||||||
// Remove trailing semicolon
|
|
||||||
if (!line.empty() && line[line.length() - 1] == ';') {
|
|
||||||
line = line.substr(0, line.length() - 1);
|
|
||||||
}
|
|
||||||
if (line.empty()) {
|
if (line.empty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -254,8 +259,13 @@ void ParseInputConfig(const std::string game_id = "") {
|
||||||
|
|
||||||
std::string output_string = line.substr(0, equal_pos);
|
std::string output_string = line.substr(0, equal_pos);
|
||||||
std::string input_string = line.substr(equal_pos + 1);
|
std::string input_string = line.substr(equal_pos + 1);
|
||||||
std::size_t comma_pos = input_string.find(',');
|
// Remove trailing semicolon from input_string
|
||||||
|
if (!input_string.empty() && input_string[input_string.length() - 1] == ';' &&
|
||||||
|
input_string != ";") {
|
||||||
|
line = line.substr(0, line.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t comma_pos = input_string.find(',');
|
||||||
auto parseInt = [](const std::string& s) -> std::optional<int> {
|
auto parseInt = [](const std::string& s) -> std::optional<int> {
|
||||||
try {
|
try {
|
||||||
return std::stoi(s);
|
return std::stoi(s);
|
||||||
|
@ -373,7 +383,6 @@ void ParseInputConfig(const std::string game_id = "") {
|
||||||
BindingConnection connection(InputID(), nullptr);
|
BindingConnection connection(InputID(), nullptr);
|
||||||
auto button_it = string_to_cbutton_map.find(output_string);
|
auto button_it = string_to_cbutton_map.find(output_string);
|
||||||
auto axis_it = string_to_axis_map.find(output_string);
|
auto axis_it = string_to_axis_map.find(output_string);
|
||||||
|
|
||||||
if (binding.IsEmpty()) {
|
if (binding.IsEmpty()) {
|
||||||
LOG_WARNING(Input, "Invalid format at line: {}, data: \"{}\", skipping line.",
|
LOG_WARNING(Input, "Invalid format at line: {}, data: \"{}\", skipping line.",
|
||||||
lineCount, line);
|
lineCount, line);
|
||||||
|
@ -411,7 +420,7 @@ void ParseInputConfig(const std::string game_id = "") {
|
||||||
u32 GetMouseWheelEvent(const SDL_Event& event) {
|
u32 GetMouseWheelEvent(const SDL_Event& event) {
|
||||||
if (event.type != SDL_EVENT_MOUSE_WHEEL && event.type != SDL_EVENT_MOUSE_WHEEL_OFF) {
|
if (event.type != SDL_EVENT_MOUSE_WHEEL && event.type != SDL_EVENT_MOUSE_WHEEL_OFF) {
|
||||||
LOG_WARNING(Input, "Something went wrong with wheel input parsing!");
|
LOG_WARNING(Input, "Something went wrong with wheel input parsing!");
|
||||||
return (u32)-1;
|
return SDL_UNMAPPED;
|
||||||
}
|
}
|
||||||
if (event.wheel.y > 0) {
|
if (event.wheel.y > 0) {
|
||||||
return SDL_MOUSE_WHEEL_UP;
|
return SDL_MOUSE_WHEEL_UP;
|
||||||
|
@ -422,7 +431,7 @@ u32 GetMouseWheelEvent(const SDL_Event& event) {
|
||||||
} else if (event.wheel.x < 0) {
|
} else if (event.wheel.x < 0) {
|
||||||
return SDL_MOUSE_WHEEL_LEFT;
|
return SDL_MOUSE_WHEEL_LEFT;
|
||||||
}
|
}
|
||||||
return (u32)-1;
|
return SDL_UNMAPPED;
|
||||||
}
|
}
|
||||||
|
|
||||||
InputEvent InputBinding::GetInputEventFromSDLEvent(const SDL_Event& e) {
|
InputEvent InputBinding::GetInputEventFromSDLEvent(const SDL_Event& e) {
|
||||||
|
@ -432,16 +441,19 @@ InputEvent InputBinding::GetInputEventFromSDLEvent(const SDL_Event& e) {
|
||||||
return InputEvent(InputType::KeyboardMouse, e.key.key, e.key.down, 0);
|
return InputEvent(InputType::KeyboardMouse, e.key.key, e.key.down, 0);
|
||||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||||
return InputEvent(InputType::KeyboardMouse, (u32)e.button.button, e.button.down, 0);
|
return InputEvent(InputType::KeyboardMouse, static_cast<u32>(e.button.button),
|
||||||
|
e.button.down, 0);
|
||||||
case SDL_EVENT_MOUSE_WHEEL:
|
case SDL_EVENT_MOUSE_WHEEL:
|
||||||
case SDL_EVENT_MOUSE_WHEEL_OFF:
|
case SDL_EVENT_MOUSE_WHEEL_OFF:
|
||||||
return InputEvent(InputType::KeyboardMouse, GetMouseWheelEvent(e),
|
return InputEvent(InputType::KeyboardMouse, GetMouseWheelEvent(e),
|
||||||
e.type == SDL_EVENT_MOUSE_WHEEL, 0);
|
e.type == SDL_EVENT_MOUSE_WHEEL, 0);
|
||||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||||
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
||||||
return InputEvent(InputType::Controller, (u32)e.gbutton.button, e.gbutton.down, 0);
|
return InputEvent(InputType::Controller, static_cast<u32>(e.gbutton.button), e.gbutton.down,
|
||||||
|
0); // clang made me do it
|
||||||
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
||||||
return InputEvent(InputType::Axis, (u32)e.gaxis.axis, true, e.gaxis.value / 256);
|
return InputEvent(InputType::Axis, static_cast<u32>(e.gaxis.axis), true,
|
||||||
|
e.gaxis.value / 256); // this too
|
||||||
default:
|
default:
|
||||||
return InputEvent();
|
return InputEvent();
|
||||||
}
|
}
|
||||||
|
@ -499,14 +511,21 @@ void ControllerOutput::FinalizeUpdate() {
|
||||||
}
|
}
|
||||||
old_button_state = new_button_state;
|
old_button_state = new_button_state;
|
||||||
old_param = *new_param;
|
old_param = *new_param;
|
||||||
float touchpad_x = 0;
|
|
||||||
if (button != SDL_GAMEPAD_BUTTON_INVALID) {
|
if (button != SDL_GAMEPAD_BUTTON_INVALID) {
|
||||||
switch (button) {
|
switch (button) {
|
||||||
case SDL_GAMEPAD_BUTTON_TOUCHPAD:
|
case SDL_GAMEPAD_BUTTON_TOUCHPAD_LEFT:
|
||||||
touchpad_x = Config::getBackButtonBehavior() == "left" ? 0.25f
|
LOG_INFO(Input, "Topuchpad left");
|
||||||
: Config::getBackButtonBehavior() == "right" ? 0.75f
|
controller->SetTouchpadState(0, new_button_state, 0.25f, 0.5f);
|
||||||
: 0.5f;
|
controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
|
||||||
controller->SetTouchpadState(0, new_button_state, touchpad_x, 0.5f);
|
break;
|
||||||
|
case SDL_GAMEPAD_BUTTON_TOUCHPAD_CENTER:
|
||||||
|
LOG_INFO(Input, "Topuchpad center");
|
||||||
|
controller->SetTouchpadState(0, new_button_state, 0.50f, 0.5f);
|
||||||
|
controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
|
||||||
|
break;
|
||||||
|
case SDL_GAMEPAD_BUTTON_TOUCHPAD_RIGHT:
|
||||||
|
LOG_INFO(Input, "Topuchpad right");
|
||||||
|
controller->SetTouchpadState(0, new_button_state, 0.75f, 0.5f);
|
||||||
controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
|
controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
|
||||||
break;
|
break;
|
||||||
case LEFTJOYSTICK_HALFMODE:
|
case LEFTJOYSTICK_HALFMODE:
|
||||||
|
@ -519,6 +538,9 @@ void ControllerOutput::FinalizeUpdate() {
|
||||||
// to do it, and it would be inconvenient to force it here, when AddUpdate does the job just
|
// to do it, and it would be inconvenient to force it here, when AddUpdate does the job just
|
||||||
// fine, and a toggle doesn't have to checked against every input that's bound to it, it's
|
// fine, and a toggle doesn't have to checked against every input that's bound to it, it's
|
||||||
// enough that one is pressed
|
// enough that one is pressed
|
||||||
|
case MOUSE_GYRO_ROLL_MODE:
|
||||||
|
SetMouseGyroRollMode(new_button_state);
|
||||||
|
break;
|
||||||
default: // is a normal key (hopefully)
|
default: // is a normal key (hopefully)
|
||||||
controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
|
controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
|
||||||
break;
|
break;
|
||||||
|
@ -570,7 +592,7 @@ void ControllerOutput::FinalizeUpdate() {
|
||||||
bool UpdatePressedKeys(InputEvent event) {
|
bool UpdatePressedKeys(InputEvent event) {
|
||||||
// Skip invalid inputs
|
// Skip invalid inputs
|
||||||
InputID input = event.input;
|
InputID input = event.input;
|
||||||
if (input.sdl_id == (u32)-1) {
|
if (input.sdl_id == SDL_UNMAPPED) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (input.type == InputType::Axis) {
|
if (input.type == InputType::Axis) {
|
||||||
|
|
|
@ -23,6 +23,10 @@
|
||||||
#define SDL_MOUSE_WHEEL_LEFT SDL_EVENT_MOUSE_WHEEL + 5
|
#define SDL_MOUSE_WHEEL_LEFT SDL_EVENT_MOUSE_WHEEL + 5
|
||||||
#define SDL_MOUSE_WHEEL_RIGHT SDL_EVENT_MOUSE_WHEEL + 7
|
#define SDL_MOUSE_WHEEL_RIGHT SDL_EVENT_MOUSE_WHEEL + 7
|
||||||
|
|
||||||
|
#define SDL_GAMEPAD_BUTTON_TOUCHPAD_LEFT SDL_GAMEPAD_BUTTON_COUNT + 1
|
||||||
|
#define SDL_GAMEPAD_BUTTON_TOUCHPAD_CENTER SDL_GAMEPAD_BUTTON_COUNT + 2
|
||||||
|
#define SDL_GAMEPAD_BUTTON_TOUCHPAD_RIGHT SDL_GAMEPAD_BUTTON_COUNT + 3
|
||||||
|
|
||||||
// idk who already used what where so I just chose a big number
|
// idk who already used what where so I just chose a big number
|
||||||
#define SDL_EVENT_MOUSE_WHEEL_OFF SDL_EVENT_USER + 10
|
#define SDL_EVENT_MOUSE_WHEEL_OFF SDL_EVENT_USER + 10
|
||||||
|
|
||||||
|
@ -31,6 +35,9 @@
|
||||||
#define BACK_BUTTON 0x00040000
|
#define BACK_BUTTON 0x00040000
|
||||||
|
|
||||||
#define KEY_TOGGLE 0x00200000
|
#define KEY_TOGGLE 0x00200000
|
||||||
|
#define MOUSE_GYRO_ROLL_MODE 0x00400000
|
||||||
|
|
||||||
|
#define SDL_UNMAPPED UINT32_MAX - 1
|
||||||
|
|
||||||
namespace Input {
|
namespace Input {
|
||||||
using Input::Axis;
|
using Input::Axis;
|
||||||
|
@ -49,7 +56,7 @@ class InputID {
|
||||||
public:
|
public:
|
||||||
InputType type;
|
InputType type;
|
||||||
u32 sdl_id;
|
u32 sdl_id;
|
||||||
InputID(InputType d = InputType::Count, u32 i = (u32)-1) : type(d), sdl_id(i) {}
|
InputID(InputType d = InputType::Count, u32 i = SDL_UNMAPPED) : type(d), sdl_id(i) {}
|
||||||
bool operator==(const InputID& o) const {
|
bool operator==(const InputID& o) const {
|
||||||
return type == o.type && sdl_id == o.sdl_id;
|
return type == o.type && sdl_id == o.sdl_id;
|
||||||
}
|
}
|
||||||
|
@ -63,7 +70,7 @@ public:
|
||||||
return *this != InputID();
|
return *this != InputID();
|
||||||
}
|
}
|
||||||
std::string ToString() {
|
std::string ToString() {
|
||||||
return fmt::format("({}: {:x})", input_type_names[(u8)type], sdl_id);
|
return fmt::format("({}: {:x})", input_type_names[static_cast<u8>(type)], sdl_id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -96,12 +103,19 @@ const std::map<std::string, u32> string_to_cbutton_map = {
|
||||||
{"options", SDL_GAMEPAD_BUTTON_START},
|
{"options", SDL_GAMEPAD_BUTTON_START},
|
||||||
|
|
||||||
// these are outputs only (touchpad can only be bound to itself)
|
// these are outputs only (touchpad can only be bound to itself)
|
||||||
{"touchpad", SDL_GAMEPAD_BUTTON_TOUCHPAD},
|
{"touchpad_left", SDL_GAMEPAD_BUTTON_TOUCHPAD_LEFT},
|
||||||
|
{"touchpad_center", SDL_GAMEPAD_BUTTON_TOUCHPAD_CENTER},
|
||||||
|
{"touchpad_right", SDL_GAMEPAD_BUTTON_TOUCHPAD_RIGHT},
|
||||||
{"leftjoystick_halfmode", LEFTJOYSTICK_HALFMODE},
|
{"leftjoystick_halfmode", LEFTJOYSTICK_HALFMODE},
|
||||||
{"rightjoystick_halfmode", RIGHTJOYSTICK_HALFMODE},
|
{"rightjoystick_halfmode", RIGHTJOYSTICK_HALFMODE},
|
||||||
|
|
||||||
// this is only for input
|
// this is only for input
|
||||||
{"back", SDL_GAMEPAD_BUTTON_BACK},
|
{"back", SDL_GAMEPAD_BUTTON_BACK},
|
||||||
|
{"lpaddle_high", SDL_GAMEPAD_BUTTON_LEFT_PADDLE1},
|
||||||
|
{"lpaddle_low", SDL_GAMEPAD_BUTTON_LEFT_PADDLE2},
|
||||||
|
{"rpaddle_high", SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1},
|
||||||
|
{"rpaddle_low", SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2},
|
||||||
|
{"mouse_gyro_roll_mode", MOUSE_GYRO_ROLL_MODE},
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::map<std::string, AxisMapping> string_to_axis_map = {
|
const std::map<std::string, AxisMapping> string_to_axis_map = {
|
||||||
|
@ -124,6 +138,7 @@ const std::map<std::string, AxisMapping> string_to_axis_map = {
|
||||||
{"axis_right_y", {SDL_GAMEPAD_AXIS_RIGHTY, 127}},
|
{"axis_right_y", {SDL_GAMEPAD_AXIS_RIGHTY, 127}},
|
||||||
};
|
};
|
||||||
const std::map<std::string, u32> string_to_keyboard_key_map = {
|
const std::map<std::string, u32> string_to_keyboard_key_map = {
|
||||||
|
// alphanumeric
|
||||||
{"a", SDLK_A},
|
{"a", SDLK_A},
|
||||||
{"b", SDLK_B},
|
{"b", SDLK_B},
|
||||||
{"c", SDLK_C},
|
{"c", SDLK_C},
|
||||||
|
@ -160,6 +175,73 @@ const std::map<std::string, u32> string_to_keyboard_key_map = {
|
||||||
{"7", SDLK_7},
|
{"7", SDLK_7},
|
||||||
{"8", SDLK_8},
|
{"8", SDLK_8},
|
||||||
{"9", SDLK_9},
|
{"9", SDLK_9},
|
||||||
|
|
||||||
|
// symbols
|
||||||
|
{"`", SDLK_GRAVE},
|
||||||
|
{"~", SDLK_TILDE},
|
||||||
|
{"!", SDLK_EXCLAIM},
|
||||||
|
{"@", SDLK_AT},
|
||||||
|
{"#", SDLK_HASH},
|
||||||
|
{"$", SDLK_DOLLAR},
|
||||||
|
{"%", SDLK_PERCENT},
|
||||||
|
{"^", SDLK_CARET},
|
||||||
|
{"&", SDLK_AMPERSAND},
|
||||||
|
{"*", SDLK_ASTERISK},
|
||||||
|
{"(", SDLK_LEFTPAREN},
|
||||||
|
{")", SDLK_RIGHTPAREN},
|
||||||
|
{"-", SDLK_MINUS},
|
||||||
|
{"_", SDLK_UNDERSCORE},
|
||||||
|
{"=", SDLK_EQUALS},
|
||||||
|
{"+", SDLK_PLUS},
|
||||||
|
{"[", SDLK_LEFTBRACKET},
|
||||||
|
{"]", SDLK_RIGHTBRACKET},
|
||||||
|
{"{", SDLK_LEFTBRACE},
|
||||||
|
{"}", SDLK_RIGHTBRACE},
|
||||||
|
{"\\", SDLK_BACKSLASH},
|
||||||
|
{"|", SDLK_PIPE},
|
||||||
|
{";", SDLK_SEMICOLON},
|
||||||
|
{":", SDLK_COLON},
|
||||||
|
{"'", SDLK_APOSTROPHE},
|
||||||
|
{"\"", SDLK_DBLAPOSTROPHE},
|
||||||
|
{",", SDLK_COMMA},
|
||||||
|
{"<", SDLK_LESS},
|
||||||
|
{".", SDLK_PERIOD},
|
||||||
|
{">", SDLK_GREATER},
|
||||||
|
{"/", SDLK_SLASH},
|
||||||
|
{"?", SDLK_QUESTION},
|
||||||
|
|
||||||
|
// special keys
|
||||||
|
{"escape", SDLK_ESCAPE},
|
||||||
|
{"printscreen", SDLK_PRINTSCREEN},
|
||||||
|
{"scrolllock", SDLK_SCROLLLOCK},
|
||||||
|
{"pausebreak", SDLK_PAUSE},
|
||||||
|
{"backspace", SDLK_BACKSPACE},
|
||||||
|
{"delete", SDLK_DELETE},
|
||||||
|
{"insert", SDLK_INSERT},
|
||||||
|
{"home", SDLK_HOME},
|
||||||
|
{"end", SDLK_END},
|
||||||
|
{"pgup", SDLK_PAGEUP},
|
||||||
|
{"pgdown", SDLK_PAGEDOWN},
|
||||||
|
{"tab", SDLK_TAB},
|
||||||
|
{"capslock", SDLK_CAPSLOCK},
|
||||||
|
{"enter", SDLK_RETURN},
|
||||||
|
{"lshift", SDLK_LSHIFT},
|
||||||
|
{"rshift", SDLK_RSHIFT},
|
||||||
|
{"lctrl", SDLK_LCTRL},
|
||||||
|
{"rctrl", SDLK_RCTRL},
|
||||||
|
{"lalt", SDLK_LALT},
|
||||||
|
{"ralt", SDLK_RALT},
|
||||||
|
{"lmeta", SDLK_LGUI},
|
||||||
|
{"rmeta", SDLK_RGUI},
|
||||||
|
{"lwin", SDLK_LGUI},
|
||||||
|
{"rwin", SDLK_RGUI},
|
||||||
|
{"space", SDLK_SPACE},
|
||||||
|
{"up", SDLK_UP},
|
||||||
|
{"down", SDLK_DOWN},
|
||||||
|
{"left", SDLK_LEFT},
|
||||||
|
{"right", SDLK_RIGHT},
|
||||||
|
|
||||||
|
// keypad
|
||||||
{"kp0", SDLK_KP_0},
|
{"kp0", SDLK_KP_0},
|
||||||
{"kp1", SDLK_KP_1},
|
{"kp1", SDLK_KP_1},
|
||||||
{"kp2", SDLK_KP_2},
|
{"kp2", SDLK_KP_2},
|
||||||
|
@ -170,43 +252,16 @@ const std::map<std::string, u32> string_to_keyboard_key_map = {
|
||||||
{"kp7", SDLK_KP_7},
|
{"kp7", SDLK_KP_7},
|
||||||
{"kp8", SDLK_KP_8},
|
{"kp8", SDLK_KP_8},
|
||||||
{"kp9", SDLK_KP_9},
|
{"kp9", SDLK_KP_9},
|
||||||
{"comma", SDLK_COMMA},
|
{"kp.", SDLK_KP_PERIOD},
|
||||||
{"period", SDLK_PERIOD},
|
{"kp,", SDLK_KP_COMMA},
|
||||||
{"question", SDLK_QUESTION},
|
{"kp/", SDLK_KP_DIVIDE},
|
||||||
{"semicolon", SDLK_SEMICOLON},
|
{"kp*", SDLK_KP_MULTIPLY},
|
||||||
{"minus", SDLK_MINUS},
|
{"kp-", SDLK_KP_MINUS},
|
||||||
{"underscore", SDLK_UNDERSCORE},
|
{"kp+", SDLK_KP_PLUS},
|
||||||
{"lparenthesis", SDLK_LEFTPAREN},
|
{"kp=", SDLK_KP_EQUALS},
|
||||||
{"rparenthesis", SDLK_RIGHTPAREN},
|
{"kpenter", SDLK_KP_ENTER},
|
||||||
{"lbracket", SDLK_LEFTBRACKET},
|
|
||||||
{"rbracket", SDLK_RIGHTBRACKET},
|
// mouse
|
||||||
{"lbrace", SDLK_LEFTBRACE},
|
|
||||||
{"rbrace", SDLK_RIGHTBRACE},
|
|
||||||
{"backslash", SDLK_BACKSLASH},
|
|
||||||
{"dash", SDLK_SLASH},
|
|
||||||
{"enter", SDLK_RETURN},
|
|
||||||
{"space", SDLK_SPACE},
|
|
||||||
{"tab", SDLK_TAB},
|
|
||||||
{"backspace", SDLK_BACKSPACE},
|
|
||||||
{"escape", SDLK_ESCAPE},
|
|
||||||
{"left", SDLK_LEFT},
|
|
||||||
{"right", SDLK_RIGHT},
|
|
||||||
{"up", SDLK_UP},
|
|
||||||
{"down", SDLK_DOWN},
|
|
||||||
{"lctrl", SDLK_LCTRL},
|
|
||||||
{"rctrl", SDLK_RCTRL},
|
|
||||||
{"lshift", SDLK_LSHIFT},
|
|
||||||
{"rshift", SDLK_RSHIFT},
|
|
||||||
{"lalt", SDLK_LALT},
|
|
||||||
{"ralt", SDLK_RALT},
|
|
||||||
{"lmeta", SDLK_LGUI},
|
|
||||||
{"rmeta", SDLK_RGUI},
|
|
||||||
{"lwin", SDLK_LGUI},
|
|
||||||
{"rwin", SDLK_RGUI},
|
|
||||||
{"home", SDLK_HOME},
|
|
||||||
{"end", SDLK_END},
|
|
||||||
{"pgup", SDLK_PAGEUP},
|
|
||||||
{"pgdown", SDLK_PAGEDOWN},
|
|
||||||
{"leftbutton", SDL_BUTTON_LEFT},
|
{"leftbutton", SDL_BUTTON_LEFT},
|
||||||
{"rightbutton", SDL_BUTTON_RIGHT},
|
{"rightbutton", SDL_BUTTON_RIGHT},
|
||||||
{"middlebutton", SDL_BUTTON_MIDDLE},
|
{"middlebutton", SDL_BUTTON_MIDDLE},
|
||||||
|
@ -216,15 +271,9 @@ const std::map<std::string, u32> string_to_keyboard_key_map = {
|
||||||
{"mousewheeldown", SDL_MOUSE_WHEEL_DOWN},
|
{"mousewheeldown", SDL_MOUSE_WHEEL_DOWN},
|
||||||
{"mousewheelleft", SDL_MOUSE_WHEEL_LEFT},
|
{"mousewheelleft", SDL_MOUSE_WHEEL_LEFT},
|
||||||
{"mousewheelright", SDL_MOUSE_WHEEL_RIGHT},
|
{"mousewheelright", SDL_MOUSE_WHEEL_RIGHT},
|
||||||
{"kpperiod", SDLK_KP_PERIOD},
|
|
||||||
{"kpcomma", SDLK_KP_COMMA},
|
// no binding
|
||||||
{"kpdivide", SDLK_KP_DIVIDE},
|
{"unmapped", SDL_UNMAPPED},
|
||||||
{"kpmultiply", SDLK_KP_MULTIPLY},
|
|
||||||
{"kpminus", SDLK_KP_MINUS},
|
|
||||||
{"kpplus", SDLK_KP_PLUS},
|
|
||||||
{"kpenter", SDLK_KP_ENTER},
|
|
||||||
{"kpequals", SDLK_KP_EQUALS},
|
|
||||||
{"capslock", SDLK_CAPSLOCK},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void ParseInputConfig(const std::string game_id);
|
void ParseInputConfig(const std::string game_id);
|
||||||
|
@ -320,6 +369,7 @@ public:
|
||||||
// returns an InputEvent based on the event type (keyboard, mouse buttons/wheel, or controller)
|
// returns an InputEvent based on the event type (keyboard, mouse buttons/wheel, or controller)
|
||||||
static InputEvent GetInputEventFromSDLEvent(const SDL_Event& e);
|
static InputEvent GetInputEventFromSDLEvent(const SDL_Event& e);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ControllerOutput {
|
class ControllerOutput {
|
||||||
static GameController* controller;
|
static GameController* controller;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "input/controller.h"
|
#include "input/controller.h"
|
||||||
#include "input_mouse.h"
|
#include "input_mouse.h"
|
||||||
|
@ -13,12 +14,19 @@ namespace Input {
|
||||||
|
|
||||||
int mouse_joystick_binding = 0;
|
int mouse_joystick_binding = 0;
|
||||||
float mouse_deadzone_offset = 0.5, mouse_speed = 1, mouse_speed_offset = 0.1250;
|
float mouse_deadzone_offset = 0.5, mouse_speed = 1, mouse_speed_offset = 0.1250;
|
||||||
|
bool mouse_gyro_roll_mode = false;
|
||||||
Uint32 mouse_polling_id = 0;
|
Uint32 mouse_polling_id = 0;
|
||||||
bool mouse_enabled = false;
|
MouseMode mouse_mode = MouseMode::Off;
|
||||||
|
|
||||||
// We had to go through 3 files of indirection just to update a flag
|
// Switches mouse to a set mode or turns mouse emulation off if it was already in that mode.
|
||||||
void ToggleMouseEnabled() {
|
// Returns whether the mode is turned on.
|
||||||
mouse_enabled = !mouse_enabled;
|
bool ToggleMouseModeTo(MouseMode m) {
|
||||||
|
if (mouse_mode == m) {
|
||||||
|
mouse_mode = MouseMode::Off;
|
||||||
|
} else {
|
||||||
|
mouse_mode = m;
|
||||||
|
}
|
||||||
|
return mouse_mode == m;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetMouseToJoystick(int joystick) {
|
void SetMouseToJoystick(int joystick) {
|
||||||
|
@ -31,10 +39,11 @@ void SetMouseParams(float mdo, float ms, float mso) {
|
||||||
mouse_speed_offset = mso;
|
mouse_speed_offset = mso;
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) {
|
void SetMouseGyroRollMode(bool mode) {
|
||||||
auto* controller = (GameController*)param;
|
mouse_gyro_roll_mode = mode;
|
||||||
if (!mouse_enabled)
|
}
|
||||||
return interval;
|
|
||||||
|
void EmulateJoystick(GameController* controller, u32 interval) {
|
||||||
|
|
||||||
Axis axis_x, axis_y;
|
Axis axis_x, axis_y;
|
||||||
switch (mouse_joystick_binding) {
|
switch (mouse_joystick_binding) {
|
||||||
|
@ -47,7 +56,7 @@ Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) {
|
||||||
axis_y = Axis::RightY;
|
axis_y = Axis::RightY;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return interval; // no update needed
|
return; // no update needed
|
||||||
}
|
}
|
||||||
|
|
||||||
float d_x = 0, d_y = 0;
|
float d_x = 0, d_y = 0;
|
||||||
|
@ -67,7 +76,35 @@ Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) {
|
||||||
controller->Axis(0, axis_x, GetAxis(-0x80, 0x7f, 0));
|
controller->Axis(0, axis_x, GetAxis(-0x80, 0x7f, 0));
|
||||||
controller->Axis(0, axis_y, GetAxis(-0x80, 0x7f, 0));
|
controller->Axis(0, axis_y, GetAxis(-0x80, 0x7f, 0));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr float constant_down_accel[3] = {0.0f, 10.0f, 0.0f};
|
||||||
|
void EmulateGyro(GameController* controller, u32 interval) {
|
||||||
|
// LOG_INFO(Input, "todo gyro");
|
||||||
|
float d_x = 0, d_y = 0;
|
||||||
|
SDL_GetRelativeMouseState(&d_x, &d_y);
|
||||||
|
controller->Acceleration(1, constant_down_accel);
|
||||||
|
float gyro_from_mouse[3] = {-d_y / 100, -d_x / 100, 0.0f};
|
||||||
|
if (mouse_gyro_roll_mode) {
|
||||||
|
gyro_from_mouse[1] = 0.0f;
|
||||||
|
gyro_from_mouse[2] = -d_x / 100;
|
||||||
|
}
|
||||||
|
controller->Gyro(1, gyro_from_mouse);
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) {
|
||||||
|
auto* controller = (GameController*)param;
|
||||||
|
switch (mouse_mode) {
|
||||||
|
case MouseMode::Joystick:
|
||||||
|
EmulateJoystick(controller, interval);
|
||||||
|
break;
|
||||||
|
case MouseMode::Gyro:
|
||||||
|
EmulateGyro(controller, interval);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
return interval;
|
return interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,21 @@
|
||||||
|
|
||||||
namespace Input {
|
namespace Input {
|
||||||
|
|
||||||
void ToggleMouseEnabled();
|
enum MouseMode {
|
||||||
|
Off = 0,
|
||||||
|
Joystick,
|
||||||
|
Gyro,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ToggleMouseModeTo(MouseMode m);
|
||||||
void SetMouseToJoystick(int joystick);
|
void SetMouseToJoystick(int joystick);
|
||||||
void SetMouseParams(float mouse_deadzone_offset, float mouse_speed, float mouse_speed_offset);
|
void SetMouseParams(float mouse_deadzone_offset, float mouse_speed, float mouse_speed_offset);
|
||||||
|
void SetMouseGyroRollMode(bool mode);
|
||||||
|
|
||||||
// Polls the mouse for changes, and simulates joystick movement from it.
|
void EmulateJoystick(GameController* controller, u32 interval);
|
||||||
|
void EmulateGyro(GameController* controller, u32 interval);
|
||||||
|
|
||||||
|
// Polls the mouse for changes
|
||||||
Uint32 MousePolling(void* param, Uint32 id, Uint32 interval);
|
Uint32 MousePolling(void* param, Uint32 id, Uint32 interval);
|
||||||
|
|
||||||
} // namespace Input
|
} // namespace Input
|
||||||
|
|
|
@ -12,7 +12,8 @@
|
||||||
#include "main_window_themes.h"
|
#include "main_window_themes.h"
|
||||||
#include "ui_about_dialog.h"
|
#include "ui_about_dialog.h"
|
||||||
|
|
||||||
AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDialog) {
|
AboutDialog::AboutDialog(std::shared_ptr<gui_settings> gui_settings, QWidget* parent)
|
||||||
|
: QDialog(parent), ui(new Ui::AboutDialog), m_gui_settings(std::move(gui_settings)) {
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
preloadImages();
|
preloadImages();
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ void AboutDialog::preloadImages() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AboutDialog::updateImagesForCurrentTheme() {
|
void AboutDialog::updateImagesForCurrentTheme() {
|
||||||
Theme currentTheme = static_cast<Theme>(Config::getMainWindowTheme());
|
Theme currentTheme = static_cast<Theme>(m_gui_settings->GetValue(gui::gen_theme).toInt());
|
||||||
bool isDarkTheme = (currentTheme == Theme::Dark || currentTheme == Theme::Green ||
|
bool isDarkTheme = (currentTheme == Theme::Dark || currentTheme == Theme::Green ||
|
||||||
currentTheme == Theme::Blue || currentTheme == Theme::Violet);
|
currentTheme == Theme::Blue || currentTheme == Theme::Violet);
|
||||||
if (isDarkTheme) {
|
if (isDarkTheme) {
|
||||||
|
@ -188,7 +189,7 @@ void AboutDialog::removeHoverEffect(QLabel* label) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AboutDialog::isDarkTheme() const {
|
bool AboutDialog::isDarkTheme() const {
|
||||||
Theme currentTheme = static_cast<Theme>(Config::getMainWindowTheme());
|
Theme currentTheme = static_cast<Theme>(m_gui_settings->GetValue(gui::gen_theme).toInt());
|
||||||
return currentTheme == Theme::Dark || currentTheme == Theme::Green ||
|
return currentTheme == Theme::Dark || currentTheme == Theme::Green ||
|
||||||
currentTheme == Theme::Blue || currentTheme == Theme::Violet;
|
currentTheme == Theme::Blue || currentTheme == Theme::Violet;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include "gui_settings.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class AboutDialog;
|
class AboutDialog;
|
||||||
|
@ -17,7 +18,7 @@ class AboutDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AboutDialog(QWidget* parent = nullptr);
|
explicit AboutDialog(std::shared_ptr<gui_settings> gui_settings, QWidget* parent = nullptr);
|
||||||
~AboutDialog();
|
~AboutDialog();
|
||||||
bool eventFilter(QObject* obj, QEvent* event);
|
bool eventFilter(QObject* obj, QEvent* event);
|
||||||
|
|
||||||
|
@ -33,4 +34,5 @@ private:
|
||||||
|
|
||||||
QPixmap originalImages[5];
|
QPixmap originalImages[5];
|
||||||
QPixmap invertedImages[5];
|
QPixmap invertedImages[5];
|
||||||
|
std::shared_ptr<gui_settings> m_gui_settings;
|
||||||
};
|
};
|
||||||
|
|
|
@ -39,14 +39,28 @@ private:
|
||||||
"pad_left", "pad_right", "axis_left_x", "axis_left_y", "axis_right_x",
|
"pad_left", "pad_right", "axis_left_x", "axis_left_y", "axis_right_x",
|
||||||
"axis_right_y", "back"};
|
"axis_right_y", "back"};
|
||||||
|
|
||||||
const QStringList ButtonOutputs = {"cross", "circle", "square", "triangle", "l1",
|
const QStringList ButtonOutputs = {"cross",
|
||||||
"r1", "l2", "r2", "l3",
|
"circle",
|
||||||
|
"square",
|
||||||
|
"triangle",
|
||||||
|
"l1",
|
||||||
|
"r1",
|
||||||
|
"l2",
|
||||||
|
"r2",
|
||||||
|
"l3",
|
||||||
|
|
||||||
"r3", "options", "pad_up",
|
"r3",
|
||||||
|
"options",
|
||||||
|
"pad_up",
|
||||||
|
|
||||||
"pad_down",
|
"pad_down",
|
||||||
|
|
||||||
"pad_left", "pad_right", "touchpad", "unmapped"};
|
"pad_left",
|
||||||
|
"pad_right",
|
||||||
|
"touchpad_left",
|
||||||
|
"touchpad_center",
|
||||||
|
"touchpad_right",
|
||||||
|
"unmapped"};
|
||||||
|
|
||||||
const QStringList StickOutputs = {"axis_left_x", "axis_left_y", "axis_right_x", "axis_right_y",
|
const QStringList StickOutputs = {"axis_left_x", "axis_left_y", "axis_right_x", "axis_right_y",
|
||||||
"unmapped"};
|
"unmapped"};
|
||||||
|
|
|
@ -3,10 +3,12 @@
|
||||||
|
|
||||||
#include "elf_viewer.h"
|
#include "elf_viewer.h"
|
||||||
|
|
||||||
ElfViewer::ElfViewer(QWidget* parent) : QTableWidget(parent) {
|
ElfViewer::ElfViewer(std::shared_ptr<gui_settings> gui_settings, QWidget* parent)
|
||||||
dir_list_std = Config::getElfViewer();
|
: QTableWidget(parent), m_gui_settings(std::move(gui_settings)) {
|
||||||
for (const auto& str : dir_list_std) {
|
|
||||||
dir_list.append(QString::fromStdString(str));
|
list = gui_settings::Var2List(m_gui_settings->GetValue(gui::gen_elfDirs));
|
||||||
|
for (const auto& str : list) {
|
||||||
|
dir_list.append(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckElfFolders();
|
CheckElfFolders();
|
||||||
|
@ -55,11 +57,11 @@ void ElfViewer::OpenElfFolder() {
|
||||||
}
|
}
|
||||||
std::ranges::sort(m_elf_list);
|
std::ranges::sort(m_elf_list);
|
||||||
OpenElfFiles();
|
OpenElfFiles();
|
||||||
dir_list_std.clear();
|
list.clear();
|
||||||
for (auto dir : dir_list) {
|
for (auto dir : dir_list) {
|
||||||
dir_list_std.push_back(dir.toStdString());
|
list.push_back(dir);
|
||||||
}
|
}
|
||||||
Config::setElfViewer(dir_list_std);
|
m_gui_settings->SetValue(gui::gen_elfDirs, gui_settings::List2Var(list));
|
||||||
} else {
|
} else {
|
||||||
// qDebug() << "Folder selection canceled.";
|
// qDebug() << "Folder selection canceled.";
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
class ElfViewer : public QTableWidget {
|
class ElfViewer : public QTableWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit ElfViewer(QWidget* parent = nullptr);
|
explicit ElfViewer(std::shared_ptr<gui_settings> gui_settings, QWidget* parent = nullptr);
|
||||||
QStringList m_elf_list;
|
QStringList m_elf_list;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -21,7 +21,8 @@ private:
|
||||||
Core::Loader::Elf m_elf_file;
|
Core::Loader::Elf m_elf_file;
|
||||||
QStringList dir_list;
|
QStringList dir_list;
|
||||||
QStringList elf_headers_list;
|
QStringList elf_headers_list;
|
||||||
std::vector<std::string> dir_list_std;
|
QList<QString> list;
|
||||||
|
std::shared_ptr<gui_settings> m_gui_settings;
|
||||||
|
|
||||||
void SetTableItem(QTableWidget* game_list, int row, int column, QString itemStr) {
|
void SetTableItem(QTableWidget* game_list, int row, int column, QString itemStr) {
|
||||||
QTableWidgetItem* item = new QTableWidgetItem();
|
QTableWidgetItem* item = new QTableWidgetItem();
|
||||||
|
|
|
@ -34,7 +34,8 @@ GameGridFrame::GameGridFrame(std::shared_ptr<gui_settings> gui_settings,
|
||||||
connect(this->horizontalScrollBar(), &QScrollBar::valueChanged, this,
|
connect(this->horizontalScrollBar(), &QScrollBar::valueChanged, this,
|
||||||
&GameGridFrame::RefreshGridBackgroundImage);
|
&GameGridFrame::RefreshGridBackgroundImage);
|
||||||
connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) {
|
connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) {
|
||||||
m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, m_compat_info, this, false);
|
m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, m_compat_info,
|
||||||
|
m_gui_settings, this, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,8 @@ GameListFrame::GameListFrame(std::shared_ptr<gui_settings> gui_settings,
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) {
|
connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) {
|
||||||
m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, m_compat_info, this, true);
|
m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, m_compat_info,
|
||||||
|
m_gui_settings, this, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(this, &QTableWidget::cellClicked, this, [=, this](int row, int column) {
|
connect(this, &QTableWidget::cellClicked, this, [=, this](int row, int column) {
|
||||||
|
|
|
@ -32,8 +32,10 @@ class GuiContextMenus : public QObject {
|
||||||
public:
|
public:
|
||||||
void RequestGameMenu(const QPoint& pos, QVector<GameInfo>& m_games,
|
void RequestGameMenu(const QPoint& pos, QVector<GameInfo>& m_games,
|
||||||
std::shared_ptr<CompatibilityInfoClass> m_compat_info,
|
std::shared_ptr<CompatibilityInfoClass> m_compat_info,
|
||||||
QTableWidget* widget, bool isList) {
|
std::shared_ptr<gui_settings> settings, QTableWidget* widget,
|
||||||
|
bool isList) {
|
||||||
QPoint global_pos = widget->viewport()->mapToGlobal(pos);
|
QPoint global_pos = widget->viewport()->mapToGlobal(pos);
|
||||||
|
std::shared_ptr<gui_settings> m_gui_settings = std::move(settings);
|
||||||
int itemID = 0;
|
int itemID = 0;
|
||||||
if (isList) {
|
if (isList) {
|
||||||
itemID = widget->currentRow();
|
itemID = widget->currentRow();
|
||||||
|
@ -357,7 +359,7 @@ public:
|
||||||
|
|
||||||
QString gameName = QString::fromStdString(m_games[itemID].name);
|
QString gameName = QString::fromStdString(m_games[itemID].name);
|
||||||
TrophyViewer* trophyViewer =
|
TrophyViewer* trophyViewer =
|
||||||
new TrophyViewer(trophyPath, gameTrpPath, gameName, allTrophyGames);
|
new TrophyViewer(m_gui_settings, trophyPath, gameTrpPath, gameName, allTrophyGames);
|
||||||
trophyViewer->show();
|
trophyViewer->show();
|
||||||
connect(widget->parent(), &QWidget::destroyed, trophyViewer,
|
connect(widget->parent(), &QWidget::destroyed, trophyViewer,
|
||||||
[trophyViewer]() { trophyViewer->deleteLater(); });
|
[trophyViewer]() { trophyViewer->deleteLater(); });
|
||||||
|
|
|
@ -17,6 +17,12 @@ const QString game_grid = "game_grid";
|
||||||
const gui_value gen_checkForUpdates = gui_value(general_settings, "checkForUpdates", false);
|
const gui_value gen_checkForUpdates = gui_value(general_settings, "checkForUpdates", false);
|
||||||
const gui_value gen_showChangeLog = gui_value(general_settings, "showChangeLog", false);
|
const gui_value gen_showChangeLog = gui_value(general_settings, "showChangeLog", false);
|
||||||
const gui_value gen_updateChannel = gui_value(general_settings, "updateChannel", "Release");
|
const gui_value gen_updateChannel = gui_value(general_settings, "updateChannel", "Release");
|
||||||
|
const gui_value gen_recentFiles =
|
||||||
|
gui_value(main_window, "recentFiles", QVariant::fromValue(QList<QString>()));
|
||||||
|
const gui_value gen_guiLanguage = gui_value(general_settings, "guiLanguage", "en_US");
|
||||||
|
const gui_value gen_elfDirs =
|
||||||
|
gui_value(main_window, "elfDirs", QVariant::fromValue(QList<QString>()));
|
||||||
|
const gui_value gen_theme = gui_value(general_settings, "theme", 0);
|
||||||
|
|
||||||
// main window settings
|
// main window settings
|
||||||
const gui_value mw_geometry = gui_value(main_window, "geometry", QByteArray());
|
const gui_value mw_geometry = gui_value(main_window, "geometry", QByteArray());
|
||||||
|
|
|
@ -32,14 +32,34 @@ KBMSettings::KBMSettings(std::shared_ptr<GameInfoClass> game_info_get, QWidget*
|
||||||
ui->ProfileComboBox->addItem(QString::fromStdString(m_game_info->m_games[i].serial));
|
ui->ProfileComboBox->addItem(QString::fromStdString(m_game_info->m_games[i].serial));
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonsList = {
|
ButtonsList = {ui->CrossButton,
|
||||||
ui->CrossButton, ui->CircleButton, ui->TriangleButton, ui->SquareButton,
|
ui->CircleButton,
|
||||||
ui->L1Button, ui->R1Button, ui->L2Button, ui->R2Button,
|
ui->TriangleButton,
|
||||||
ui->L3Button, ui->R3Button, ui->OptionsButton, ui->TouchpadButton,
|
ui->SquareButton,
|
||||||
ui->DpadUpButton, ui->DpadDownButton, ui->DpadLeftButton, ui->DpadRightButton,
|
ui->L1Button,
|
||||||
ui->LStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, ui->LStickRightButton,
|
ui->R1Button,
|
||||||
ui->RStickUpButton, ui->RStickDownButton, ui->RStickLeftButton, ui->RStickRightButton,
|
ui->L2Button,
|
||||||
ui->LHalfButton, ui->RHalfButton};
|
ui->R2Button,
|
||||||
|
ui->L3Button,
|
||||||
|
ui->R3Button,
|
||||||
|
ui->OptionsButton,
|
||||||
|
ui->TouchpadLeftButton,
|
||||||
|
ui->TouchpadCenterButton,
|
||||||
|
ui->TouchpadRightButton,
|
||||||
|
ui->DpadUpButton,
|
||||||
|
ui->DpadDownButton,
|
||||||
|
ui->DpadLeftButton,
|
||||||
|
ui->DpadRightButton,
|
||||||
|
ui->LStickUpButton,
|
||||||
|
ui->LStickDownButton,
|
||||||
|
ui->LStickLeftButton,
|
||||||
|
ui->LStickRightButton,
|
||||||
|
ui->RStickUpButton,
|
||||||
|
ui->RStickDownButton,
|
||||||
|
ui->RStickLeftButton,
|
||||||
|
ui->RStickRightButton,
|
||||||
|
ui->LHalfButton,
|
||||||
|
ui->RHalfButton};
|
||||||
|
|
||||||
ButtonConnects();
|
ButtonConnects();
|
||||||
SetUIValuestoMappings("default");
|
SetUIValuestoMappings("default");
|
||||||
|
@ -144,187 +164,73 @@ void KBMSettings::EnableMappingButtons() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KBMSettings::SaveKBMConfig(bool CloseOnSave) {
|
void KBMSettings::SaveKBMConfig(bool close_on_save) {
|
||||||
std::string output_string = "", input_string = "";
|
std::string output_string = "", input_string = "";
|
||||||
std::vector<std::string> lines, inputs;
|
std::vector<std::string> lines, inputs;
|
||||||
|
|
||||||
|
// Comment lines for config file
|
||||||
lines.push_back("#Feeling lost? Check out the Help section!");
|
lines.push_back("#Feeling lost? Check out the Help section!");
|
||||||
lines.push_back("");
|
lines.push_back("");
|
||||||
lines.push_back("#Keyboard bindings");
|
lines.push_back("#Keyboard bindings");
|
||||||
lines.push_back("");
|
lines.push_back("");
|
||||||
|
|
||||||
input_string = ui->CrossButton->text().toStdString();
|
// Lambda to reduce repetitive code for mapping buttons to config lines
|
||||||
output_string = "cross";
|
auto add_mapping = [&](const QString& buttonText, const std::string& output_name) {
|
||||||
lines.push_back(output_string + " = " + input_string);
|
input_string = buttonText.toStdString();
|
||||||
if (input_string != "unmapped")
|
output_string = output_name;
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->CircleButton->text().toStdString();
|
|
||||||
output_string = "circle";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->TriangleButton->text().toStdString();
|
|
||||||
output_string = "triangle";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->SquareButton->text().toStdString();
|
|
||||||
output_string = "square";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
lines.push_back("");
|
|
||||||
|
|
||||||
input_string = ui->DpadUpButton->text().toStdString();
|
|
||||||
output_string = "pad_up";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->DpadDownButton->text().toStdString();
|
|
||||||
output_string = "pad_down";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->DpadLeftButton->text().toStdString();
|
|
||||||
output_string = "pad_left";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->DpadRightButton->text().toStdString();
|
|
||||||
output_string = "pad_right";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
lines.push_back("");
|
|
||||||
|
|
||||||
input_string = ui->L1Button->text().toStdString();
|
|
||||||
output_string = "l1";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->R1Button->text().toStdString();
|
|
||||||
output_string = "r1";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->L2Button->text().toStdString();
|
|
||||||
output_string = "l2";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->R2Button->text().toStdString();
|
|
||||||
output_string = "r2";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->L3Button->text().toStdString();
|
|
||||||
output_string = "l3";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->R3Button->text().toStdString();
|
|
||||||
output_string = "r3";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
lines.push_back("");
|
|
||||||
|
|
||||||
input_string = ui->OptionsButton->text().toStdString();
|
|
||||||
output_string = "options";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->TouchpadButton->text().toStdString();
|
|
||||||
output_string = "touchpad";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
lines.push_back("");
|
|
||||||
|
|
||||||
input_string = ui->LStickUpButton->text().toStdString();
|
|
||||||
output_string = "axis_left_y_minus";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->LStickDownButton->text().toStdString();
|
|
||||||
output_string = "axis_left_y_plus";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->LStickLeftButton->text().toStdString();
|
|
||||||
output_string = "axis_left_x_minus";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->LStickRightButton->text().toStdString();
|
|
||||||
output_string = "axis_left_x_plus";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
lines.push_back("");
|
|
||||||
|
|
||||||
input_string = ui->RStickUpButton->text().toStdString();
|
|
||||||
output_string = "axis_right_y_minus";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->RStickDownButton->text().toStdString();
|
|
||||||
output_string = "axis_right_y_plus";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->RStickLeftButton->text().toStdString();
|
|
||||||
output_string = "axis_right_x_minus";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->RStickRightButton->text().toStdString();
|
|
||||||
output_string = "axis_right_x_plus";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
lines.push_back("");
|
|
||||||
|
|
||||||
input_string = ui->MouseJoystickBox->currentText().toStdString();
|
|
||||||
output_string = "mouse_to_joystick";
|
|
||||||
if (input_string != "unmapped")
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
if (input_string != "unmapped") {
|
||||||
|
inputs.push_back(input_string);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
input_string = ui->LHalfButton->text().toStdString();
|
add_mapping(ui->CrossButton->text(), "cross");
|
||||||
output_string = "leftjoystick_halfmode";
|
add_mapping(ui->CircleButton->text(), "circle");
|
||||||
lines.push_back(output_string + " = " + input_string);
|
add_mapping(ui->TriangleButton->text(), "triangle");
|
||||||
if (input_string != "unmapped")
|
add_mapping(ui->SquareButton->text(), "square");
|
||||||
inputs.push_back(input_string);
|
|
||||||
|
|
||||||
input_string = ui->RHalfButton->text().toStdString();
|
lines.push_back("");
|
||||||
output_string = "rightjoystick_halfmode";
|
|
||||||
lines.push_back(output_string + " = " + input_string);
|
add_mapping(ui->DpadUpButton->text(), "pad_up");
|
||||||
if (input_string != "unmapped")
|
add_mapping(ui->DpadDownButton->text(), "pad_down");
|
||||||
inputs.push_back(input_string);
|
add_mapping(ui->DpadLeftButton->text(), "pad_left");
|
||||||
|
add_mapping(ui->DpadRightButton->text(), "pad_right");
|
||||||
|
|
||||||
|
lines.push_back("");
|
||||||
|
|
||||||
|
add_mapping(ui->L1Button->text(), "l1");
|
||||||
|
add_mapping(ui->R1Button->text(), "r1");
|
||||||
|
add_mapping(ui->L2Button->text(), "l2");
|
||||||
|
add_mapping(ui->R2Button->text(), "r2");
|
||||||
|
add_mapping(ui->L3Button->text(), "l3");
|
||||||
|
add_mapping(ui->R3Button->text(), "r3");
|
||||||
|
|
||||||
|
lines.push_back("");
|
||||||
|
|
||||||
|
add_mapping(ui->TouchpadLeftButton->text(), "touchpad_left");
|
||||||
|
add_mapping(ui->TouchpadCenterButton->text(), "touchpad_center");
|
||||||
|
add_mapping(ui->TouchpadRightButton->text(), "touchpad_right");
|
||||||
|
add_mapping(ui->OptionsButton->text(), "options");
|
||||||
|
|
||||||
|
lines.push_back("");
|
||||||
|
|
||||||
|
add_mapping(ui->LStickUpButton->text(), "axis_left_y_minus");
|
||||||
|
add_mapping(ui->LStickDownButton->text(), "axis_left_y_plus");
|
||||||
|
add_mapping(ui->LStickLeftButton->text(), "axis_left_x_minus");
|
||||||
|
add_mapping(ui->LStickRightButton->text(), "axis_left_x_plus");
|
||||||
|
|
||||||
|
lines.push_back("");
|
||||||
|
|
||||||
|
add_mapping(ui->RStickUpButton->text(), "axis_right_y_minus");
|
||||||
|
add_mapping(ui->RStickDownButton->text(), "axis_right_y_plus");
|
||||||
|
add_mapping(ui->RStickLeftButton->text(), "axis_right_x_minus");
|
||||||
|
add_mapping(ui->RStickRightButton->text(), "axis_right_x_plus");
|
||||||
|
|
||||||
|
lines.push_back("");
|
||||||
|
|
||||||
|
add_mapping(ui->MouseJoystickBox->currentText(), "mouse_to_joystick");
|
||||||
|
add_mapping(ui->LHalfButton->text(), "leftjoystick_halfmode");
|
||||||
|
add_mapping(ui->RHalfButton->text(), "rightjoystick_halfmode");
|
||||||
|
|
||||||
std::string DOString = std::format("{:.2f}", (ui->DeadzoneOffsetSlider->value() / 100.f));
|
std::string DOString = std::format("{:.2f}", (ui->DeadzoneOffsetSlider->value() / 100.f));
|
||||||
std::string SMString = std::format("{:.1f}", (ui->SpeedMultiplierSlider->value() / 10.f));
|
std::string SMString = std::format("{:.1f}", (ui->SpeedMultiplierSlider->value() / 10.f));
|
||||||
|
@ -374,6 +280,7 @@ void KBMSettings::SaveKBMConfig(bool CloseOnSave) {
|
||||||
// Prevent duplicate inputs for KBM as this breaks the engine
|
// Prevent duplicate inputs for KBM as this breaks the engine
|
||||||
bool duplicateFound = false;
|
bool duplicateFound = false;
|
||||||
QSet<QString> duplicateMappings;
|
QSet<QString> duplicateMappings;
|
||||||
|
|
||||||
for (auto it = inputs.begin(); it != inputs.end(); ++it) {
|
for (auto it = inputs.begin(); it != inputs.end(); ++it) {
|
||||||
if (std::find(it + 1, inputs.end(), *it) != inputs.end()) {
|
if (std::find(it + 1, inputs.end(), *it) != inputs.end()) {
|
||||||
duplicateFound = true;
|
duplicateFound = true;
|
||||||
|
@ -415,7 +322,7 @@ QString(tr("Cannot bind any unique input more than once. Duplicate inputs mapped
|
||||||
Config::SetUseUnifiedInputConfig(!ui->PerGameCheckBox->isChecked());
|
Config::SetUseUnifiedInputConfig(!ui->PerGameCheckBox->isChecked());
|
||||||
Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml");
|
Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml");
|
||||||
|
|
||||||
if (CloseOnSave)
|
if (close_on_save)
|
||||||
QWidget::close();
|
QWidget::close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,7 +339,9 @@ void KBMSettings::SetDefault() {
|
||||||
ui->R2Button->setText("o");
|
ui->R2Button->setText("o");
|
||||||
ui->R3Button->setText("m");
|
ui->R3Button->setText("m");
|
||||||
|
|
||||||
ui->TouchpadButton->setText("space");
|
ui->TouchpadLeftButton->setText("space");
|
||||||
|
ui->TouchpadCenterButton->setText("unmapped");
|
||||||
|
ui->TouchpadRightButton->setText("unmapped");
|
||||||
ui->OptionsButton->setText("enter");
|
ui->OptionsButton->setText("enter");
|
||||||
|
|
||||||
ui->DpadUpButton->setText("up");
|
ui->DpadUpButton->setText("up");
|
||||||
|
@ -481,7 +390,6 @@ void KBMSettings::SetUIValuestoMappings(std::string config_id) {
|
||||||
|
|
||||||
if (std::find(ControllerInputs.begin(), ControllerInputs.end(), input_string) ==
|
if (std::find(ControllerInputs.begin(), ControllerInputs.end(), input_string) ==
|
||||||
ControllerInputs.end()) {
|
ControllerInputs.end()) {
|
||||||
|
|
||||||
if (output_string == "cross") {
|
if (output_string == "cross") {
|
||||||
ui->CrossButton->setText(QString::fromStdString(input_string));
|
ui->CrossButton->setText(QString::fromStdString(input_string));
|
||||||
} else if (output_string == "circle") {
|
} else if (output_string == "circle") {
|
||||||
|
@ -512,8 +420,12 @@ void KBMSettings::SetUIValuestoMappings(std::string config_id) {
|
||||||
ui->DpadRightButton->setText(QString::fromStdString(input_string));
|
ui->DpadRightButton->setText(QString::fromStdString(input_string));
|
||||||
} else if (output_string == "options") {
|
} else if (output_string == "options") {
|
||||||
ui->OptionsButton->setText(QString::fromStdString(input_string));
|
ui->OptionsButton->setText(QString::fromStdString(input_string));
|
||||||
} else if (output_string == "touchpad") {
|
} else if (output_string == "touchpad_left") {
|
||||||
ui->TouchpadButton->setText(QString::fromStdString(input_string));
|
ui->TouchpadLeftButton->setText(QString::fromStdString(input_string));
|
||||||
|
} else if (output_string == "touchpad_center") {
|
||||||
|
ui->TouchpadCenterButton->setText(QString::fromStdString(input_string));
|
||||||
|
} else if (output_string == "touchpad_right") {
|
||||||
|
ui->TouchpadRightButton->setText(QString::fromStdString(input_string));
|
||||||
} else if (output_string == "axis_left_x_minus") {
|
} else if (output_string == "axis_left_x_minus") {
|
||||||
ui->LStickLeftButton->setText(QString::fromStdString(input_string));
|
ui->LStickLeftButton->setText(QString::fromStdString(input_string));
|
||||||
} else if (output_string == "axis_left_x_plus") {
|
} else if (output_string == "axis_left_x_plus") {
|
||||||
|
@ -541,7 +453,7 @@ void KBMSettings::SetUIValuestoMappings(std::string config_id) {
|
||||||
if (comma_pos != std::string::npos) {
|
if (comma_pos != std::string::npos) {
|
||||||
std::string DOstring = line.substr(equal_pos + 1, comma_pos);
|
std::string DOstring = line.substr(equal_pos + 1, comma_pos);
|
||||||
float DOffsetValue = std::stof(DOstring) * 100.0;
|
float DOffsetValue = std::stof(DOstring) * 100.0;
|
||||||
int DOffsetInt = int(DOffsetValue);
|
int DOffsetInt = static_cast<int>(DOffsetValue);
|
||||||
ui->DeadzoneOffsetSlider->setValue(DOffsetInt);
|
ui->DeadzoneOffsetSlider->setValue(DOffsetInt);
|
||||||
QString LabelValue = QString::number(DOffsetInt / 100.0, 'f', 2);
|
QString LabelValue = QString::number(DOffsetInt / 100.0, 'f', 2);
|
||||||
QString LabelString = tr("Deadzone Offset (def 0.50):") + " " + LabelValue;
|
QString LabelString = tr("Deadzone Offset (def 0.50):") + " " + LabelValue;
|
||||||
|
@ -551,12 +463,8 @@ void KBMSettings::SetUIValuestoMappings(std::string config_id) {
|
||||||
std::size_t comma_pos2 = SMSOstring.find(',');
|
std::size_t comma_pos2 = SMSOstring.find(',');
|
||||||
if (comma_pos2 != std::string::npos) {
|
if (comma_pos2 != std::string::npos) {
|
||||||
std::string SMstring = SMSOstring.substr(0, comma_pos2);
|
std::string SMstring = SMSOstring.substr(0, comma_pos2);
|
||||||
float SpeedMultValue = std::stof(SMstring) * 10.0;
|
float SpeedMultValue = std::clamp(std::stof(SMstring) * 10.0f, 1.0f, 50.0f);
|
||||||
int SpeedMultInt = int(SpeedMultValue);
|
int SpeedMultInt = static_cast<int>(SpeedMultValue);
|
||||||
if (SpeedMultInt < 1)
|
|
||||||
SpeedMultInt = 1;
|
|
||||||
if (SpeedMultInt > 50)
|
|
||||||
SpeedMultInt = 50;
|
|
||||||
ui->SpeedMultiplierSlider->setValue(SpeedMultInt);
|
ui->SpeedMultiplierSlider->setValue(SpeedMultInt);
|
||||||
LabelValue = QString::number(SpeedMultInt / 10.0, 'f', 1);
|
LabelValue = QString::number(SpeedMultInt / 10.0, 'f', 1);
|
||||||
LabelString = tr("Speed Multiplier (def 1.0):") + " " + LabelValue;
|
LabelString = tr("Speed Multiplier (def 1.0):") + " " + LabelValue;
|
||||||
|
@ -564,7 +472,7 @@ void KBMSettings::SetUIValuestoMappings(std::string config_id) {
|
||||||
|
|
||||||
std::string SOstring = SMSOstring.substr(comma_pos2 + 1);
|
std::string SOstring = SMSOstring.substr(comma_pos2 + 1);
|
||||||
float SOffsetValue = std::stof(SOstring) * 1000.0;
|
float SOffsetValue = std::stof(SOstring) * 1000.0;
|
||||||
int SOffsetInt = int(SOffsetValue);
|
int SOffsetInt = static_cast<int>(SOffsetValue);
|
||||||
ui->SpeedOffsetSlider->setValue(SOffsetInt);
|
ui->SpeedOffsetSlider->setValue(SOffsetInt);
|
||||||
LabelValue = QString::number(SOffsetInt / 1000.0, 'f', 3);
|
LabelValue = QString::number(SOffsetInt / 1000.0, 'f', 3);
|
||||||
LabelString = tr("Speed Offset (def 0.125):") + " " + LabelValue;
|
LabelString = tr("Speed Offset (def 0.125):") + " " + LabelValue;
|
||||||
|
@ -662,6 +570,16 @@ void KBMSettings::SetMapping(QString input) {
|
||||||
MappingCompleted = true;
|
MappingCompleted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper lambda to get the modified button text based on the current keyboard modifiers
|
||||||
|
auto GetModifiedButton = [](Qt::KeyboardModifiers modifier, const std::string& m_button,
|
||||||
|
const std::string& n_button) -> QString {
|
||||||
|
if (QApplication::keyboardModifiers() & modifier) {
|
||||||
|
return QString::fromStdString(m_button);
|
||||||
|
} else {
|
||||||
|
return QString::fromStdString(n_button);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
bool KBMSettings::eventFilter(QObject* obj, QEvent* event) {
|
bool KBMSettings::eventFilter(QObject* obj, QEvent* event) {
|
||||||
if (event->type() == QEvent::Close) {
|
if (event->type() == QEvent::Close) {
|
||||||
if (HelpWindowOpen) {
|
if (HelpWindowOpen) {
|
||||||
|
@ -682,213 +600,7 @@ bool KBMSettings::eventFilter(QObject* obj, QEvent* event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (keyEvent->key()) {
|
switch (keyEvent->key()) {
|
||||||
case Qt::Key_Space:
|
// alphanumeric
|
||||||
pressedKeys.insert("space");
|
|
||||||
break;
|
|
||||||
case Qt::Key_Comma:
|
|
||||||
if (Qt::KeypadModifier & QApplication::keyboardModifiers()) {
|
|
||||||
pressedKeys.insert("kpcomma");
|
|
||||||
} else {
|
|
||||||
pressedKeys.insert("comma");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Qt::Key_Period:
|
|
||||||
if (Qt::KeypadModifier & QApplication::keyboardModifiers()) {
|
|
||||||
pressedKeys.insert("kpperiod");
|
|
||||||
} else {
|
|
||||||
pressedKeys.insert("period");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Qt::Key_Slash:
|
|
||||||
if (Qt::KeypadModifier & QApplication::keyboardModifiers())
|
|
||||||
pressedKeys.insert("kpdivide");
|
|
||||||
break;
|
|
||||||
case Qt::Key_Asterisk:
|
|
||||||
if (Qt::KeypadModifier & QApplication::keyboardModifiers())
|
|
||||||
pressedKeys.insert("kpmultiply");
|
|
||||||
break;
|
|
||||||
case Qt::Key_Question:
|
|
||||||
pressedKeys.insert("question");
|
|
||||||
break;
|
|
||||||
case Qt::Key_Semicolon:
|
|
||||||
pressedKeys.insert("semicolon");
|
|
||||||
break;
|
|
||||||
case Qt::Key_Minus:
|
|
||||||
if (Qt::KeypadModifier & QApplication::keyboardModifiers()) {
|
|
||||||
pressedKeys.insert("kpminus");
|
|
||||||
} else {
|
|
||||||
pressedKeys.insert("minus");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Qt::Key_Plus:
|
|
||||||
if (Qt::KeypadModifier & QApplication::keyboardModifiers()) {
|
|
||||||
pressedKeys.insert("kpplus");
|
|
||||||
} else {
|
|
||||||
pressedKeys.insert("plus");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Qt::Key_ParenLeft:
|
|
||||||
pressedKeys.insert("lparenthesis");
|
|
||||||
break;
|
|
||||||
case Qt::Key_ParenRight:
|
|
||||||
pressedKeys.insert("rparenthesis");
|
|
||||||
break;
|
|
||||||
case Qt::Key_BracketLeft:
|
|
||||||
pressedKeys.insert("lbracket");
|
|
||||||
break;
|
|
||||||
case Qt::Key_BracketRight:
|
|
||||||
pressedKeys.insert("rbracket");
|
|
||||||
break;
|
|
||||||
case Qt::Key_BraceLeft:
|
|
||||||
pressedKeys.insert("lbrace");
|
|
||||||
break;
|
|
||||||
case Qt::Key_BraceRight:
|
|
||||||
pressedKeys.insert("rbrace");
|
|
||||||
break;
|
|
||||||
case Qt::Key_Backslash:
|
|
||||||
pressedKeys.insert("backslash");
|
|
||||||
break;
|
|
||||||
case Qt::Key_Tab:
|
|
||||||
pressedKeys.insert("tab");
|
|
||||||
break;
|
|
||||||
case Qt::Key_Backspace:
|
|
||||||
pressedKeys.insert("backspace");
|
|
||||||
break;
|
|
||||||
case Qt::Key_Return:
|
|
||||||
pressedKeys.insert("enter");
|
|
||||||
break;
|
|
||||||
case Qt::Key_Enter:
|
|
||||||
pressedKeys.insert("kpenter");
|
|
||||||
break;
|
|
||||||
case Qt::Key_Home:
|
|
||||||
pressedKeys.insert("home");
|
|
||||||
break;
|
|
||||||
case Qt::Key_End:
|
|
||||||
pressedKeys.insert("end");
|
|
||||||
break;
|
|
||||||
case Qt::Key_PageDown:
|
|
||||||
pressedKeys.insert("pgdown");
|
|
||||||
break;
|
|
||||||
case Qt::Key_PageUp:
|
|
||||||
pressedKeys.insert("pgup");
|
|
||||||
break;
|
|
||||||
case Qt::Key_CapsLock:
|
|
||||||
pressedKeys.insert("capslock");
|
|
||||||
break;
|
|
||||||
case Qt::Key_Escape:
|
|
||||||
pressedKeys.insert("unmapped");
|
|
||||||
break;
|
|
||||||
case Qt::Key_Shift:
|
|
||||||
if (keyEvent->nativeScanCode() == rshift) {
|
|
||||||
pressedKeys.insert("rshift");
|
|
||||||
} else {
|
|
||||||
pressedKeys.insert("lshift");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Qt::Key_Alt:
|
|
||||||
if (keyEvent->nativeScanCode() == ralt) {
|
|
||||||
pressedKeys.insert("ralt");
|
|
||||||
} else {
|
|
||||||
pressedKeys.insert("lalt");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Qt::Key_Control:
|
|
||||||
if (keyEvent->nativeScanCode() == rctrl) {
|
|
||||||
pressedKeys.insert("rctrl");
|
|
||||||
} else {
|
|
||||||
pressedKeys.insert("lctrl");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Qt::Key_Meta:
|
|
||||||
activateWindow();
|
|
||||||
#ifdef _WIN32
|
|
||||||
pressedKeys.insert("lwin");
|
|
||||||
#else
|
|
||||||
pressedKeys.insert("lmeta");
|
|
||||||
#endif
|
|
||||||
case Qt::Key_1:
|
|
||||||
if (Qt::KeypadModifier & QApplication::keyboardModifiers()) {
|
|
||||||
pressedKeys.insert("kp1");
|
|
||||||
} else {
|
|
||||||
pressedKeys.insert("1");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Qt::Key_2:
|
|
||||||
if (Qt::KeypadModifier & QApplication::keyboardModifiers()) {
|
|
||||||
pressedKeys.insert("kp2");
|
|
||||||
} else {
|
|
||||||
pressedKeys.insert("2");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Qt::Key_3:
|
|
||||||
if (Qt::KeypadModifier & QApplication::keyboardModifiers()) {
|
|
||||||
pressedKeys.insert("kp3");
|
|
||||||
} else {
|
|
||||||
pressedKeys.insert("3");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Qt::Key_4:
|
|
||||||
if (Qt::KeypadModifier & QApplication::keyboardModifiers()) {
|
|
||||||
pressedKeys.insert("kp4");
|
|
||||||
} else {
|
|
||||||
pressedKeys.insert("4");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Qt::Key_5:
|
|
||||||
if (Qt::KeypadModifier & QApplication::keyboardModifiers()) {
|
|
||||||
pressedKeys.insert("kp5");
|
|
||||||
} else {
|
|
||||||
pressedKeys.insert("5");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Qt::Key_6:
|
|
||||||
if (Qt::KeypadModifier & QApplication::keyboardModifiers()) {
|
|
||||||
pressedKeys.insert("kp6");
|
|
||||||
} else {
|
|
||||||
pressedKeys.insert("6");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Qt::Key_7:
|
|
||||||
if (Qt::KeypadModifier & QApplication::keyboardModifiers()) {
|
|
||||||
pressedKeys.insert("kp7");
|
|
||||||
} else {
|
|
||||||
pressedKeys.insert("7");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Qt::Key_8:
|
|
||||||
if (Qt::KeypadModifier & QApplication::keyboardModifiers()) {
|
|
||||||
pressedKeys.insert("kp8");
|
|
||||||
} else {
|
|
||||||
pressedKeys.insert("8");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Qt::Key_9:
|
|
||||||
if (Qt::KeypadModifier & QApplication::keyboardModifiers()) {
|
|
||||||
pressedKeys.insert("kp9");
|
|
||||||
} else {
|
|
||||||
pressedKeys.insert("9");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Qt::Key_0:
|
|
||||||
if (Qt::KeypadModifier & QApplication::keyboardModifiers()) {
|
|
||||||
pressedKeys.insert("kp0");
|
|
||||||
} else {
|
|
||||||
pressedKeys.insert("0");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Qt::Key_Up:
|
|
||||||
activateWindow();
|
|
||||||
pressedKeys.insert("up");
|
|
||||||
break;
|
|
||||||
case Qt::Key_Down:
|
|
||||||
pressedKeys.insert("down");
|
|
||||||
break;
|
|
||||||
case Qt::Key_Left:
|
|
||||||
pressedKeys.insert("left");
|
|
||||||
break;
|
|
||||||
case Qt::Key_Right:
|
|
||||||
pressedKeys.insert("right");
|
|
||||||
break;
|
|
||||||
case Qt::Key_A:
|
case Qt::Key_A:
|
||||||
pressedKeys.insert("a");
|
pressedKeys.insert("a");
|
||||||
break;
|
break;
|
||||||
|
@ -962,13 +674,232 @@ bool KBMSettings::eventFilter(QObject* obj, QEvent* event) {
|
||||||
pressedKeys.insert("x");
|
pressedKeys.insert("x");
|
||||||
break;
|
break;
|
||||||
case Qt::Key_Y:
|
case Qt::Key_Y:
|
||||||
pressedKeys.insert("Y");
|
pressedKeys.insert("y");
|
||||||
break;
|
break;
|
||||||
case Qt::Key_Z:
|
case Qt::Key_Z:
|
||||||
pressedKeys.insert("z");
|
pressedKeys.insert("z");
|
||||||
break;
|
break;
|
||||||
|
case Qt::Key_0:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp0", "0"));
|
||||||
|
break;
|
||||||
|
case Qt::Key_1:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp1", "1"));
|
||||||
|
break;
|
||||||
|
case Qt::Key_2:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp2", "2"));
|
||||||
|
break;
|
||||||
|
case Qt::Key_3:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp3", "3"));
|
||||||
|
break;
|
||||||
|
case Qt::Key_4:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp4", "4"));
|
||||||
|
break;
|
||||||
|
case Qt::Key_5:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp5", "5"));
|
||||||
|
break;
|
||||||
|
case Qt::Key_6:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp6", "6"));
|
||||||
|
break;
|
||||||
|
case Qt::Key_7:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp7", "7"));
|
||||||
|
break;
|
||||||
|
case Qt::Key_8:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp8", "8"));
|
||||||
|
break;
|
||||||
|
case Qt::Key_9:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp9", "9"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
// symbols
|
||||||
|
case Qt::Key_Exclam:
|
||||||
|
pressedKeys.insert("!");
|
||||||
|
break;
|
||||||
|
case Qt::Key_At:
|
||||||
|
pressedKeys.insert("@");
|
||||||
|
break;
|
||||||
|
case Qt::Key_NumberSign:
|
||||||
|
pressedKeys.insert("#");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Dollar:
|
||||||
|
pressedKeys.insert("$");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Percent:
|
||||||
|
pressedKeys.insert("%");
|
||||||
|
break;
|
||||||
|
case Qt::Key_AsciiCircum:
|
||||||
|
pressedKeys.insert("^");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Ampersand:
|
||||||
|
pressedKeys.insert("&");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Asterisk:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp*", "*"));
|
||||||
|
break;
|
||||||
|
case Qt::Key_ParenLeft:
|
||||||
|
pressedKeys.insert("(");
|
||||||
|
break;
|
||||||
|
case Qt::Key_ParenRight:
|
||||||
|
pressedKeys.insert(")");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Minus:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp-", "-"));
|
||||||
|
break;
|
||||||
|
case Qt::Key_Underscore:
|
||||||
|
pressedKeys.insert("_");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Equal:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp=", "="));
|
||||||
|
break;
|
||||||
|
case Qt::Key_Plus:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp+", "+"));
|
||||||
|
break;
|
||||||
|
case Qt::Key_BracketLeft:
|
||||||
|
pressedKeys.insert("[");
|
||||||
|
break;
|
||||||
|
case Qt::Key_BracketRight:
|
||||||
|
pressedKeys.insert("]");
|
||||||
|
break;
|
||||||
|
case Qt::Key_BraceLeft:
|
||||||
|
pressedKeys.insert("{");
|
||||||
|
break;
|
||||||
|
case Qt::Key_BraceRight:
|
||||||
|
pressedKeys.insert("}");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Backslash:
|
||||||
|
pressedKeys.insert("\\");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Bar:
|
||||||
|
pressedKeys.insert("|");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Semicolon:
|
||||||
|
pressedKeys.insert(";");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Colon:
|
||||||
|
pressedKeys.insert(":");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Apostrophe:
|
||||||
|
pressedKeys.insert("'");
|
||||||
|
break;
|
||||||
|
case Qt::Key_QuoteDbl:
|
||||||
|
pressedKeys.insert("\"");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Comma:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp,", ","));
|
||||||
|
break;
|
||||||
|
case Qt::Key_Less:
|
||||||
|
pressedKeys.insert("<");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Period:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp.", "."));
|
||||||
|
break;
|
||||||
|
case Qt::Key_Greater:
|
||||||
|
pressedKeys.insert(">");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Slash:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp/", "/"));
|
||||||
|
break;
|
||||||
|
case Qt::Key_Question:
|
||||||
|
pressedKeys.insert("question");
|
||||||
|
break;
|
||||||
|
|
||||||
|
// special keys
|
||||||
|
case Qt::Key_Print:
|
||||||
|
pressedKeys.insert("printscreen");
|
||||||
|
break;
|
||||||
|
case Qt::Key_ScrollLock:
|
||||||
|
pressedKeys.insert("scrolllock");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Pause:
|
||||||
|
pressedKeys.insert("pausebreak");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Backspace:
|
||||||
|
pressedKeys.insert("backspace");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Insert:
|
||||||
|
pressedKeys.insert("insert");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Delete:
|
||||||
|
pressedKeys.insert("delete");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Home:
|
||||||
|
pressedKeys.insert("home");
|
||||||
|
break;
|
||||||
|
case Qt::Key_End:
|
||||||
|
pressedKeys.insert("end");
|
||||||
|
break;
|
||||||
|
case Qt::Key_PageUp:
|
||||||
|
pressedKeys.insert("pgup");
|
||||||
|
break;
|
||||||
|
case Qt::Key_PageDown:
|
||||||
|
pressedKeys.insert("pgdown");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Tab:
|
||||||
|
pressedKeys.insert("tab");
|
||||||
|
break;
|
||||||
|
case Qt::Key_CapsLock:
|
||||||
|
pressedKeys.insert("capslock");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Return:
|
||||||
|
pressedKeys.insert("enter");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Enter:
|
||||||
|
pressedKeys.insert(GetModifiedButton(Qt::ShiftModifier, "kpenter", "enter"));
|
||||||
|
break;
|
||||||
|
case Qt::Key_Shift:
|
||||||
|
if (keyEvent->nativeScanCode() == LSHIFT_KEY) {
|
||||||
|
pressedKeys.insert("lshift");
|
||||||
|
} else {
|
||||||
|
pressedKeys.insert("rshift");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Qt::Key_Alt:
|
||||||
|
if (keyEvent->nativeScanCode() == LALT_KEY) {
|
||||||
|
pressedKeys.insert("lalt");
|
||||||
|
} else {
|
||||||
|
pressedKeys.insert("ralt");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Qt::Key_Control:
|
||||||
|
if (keyEvent->nativeScanCode() == LCTRL_KEY) {
|
||||||
|
pressedKeys.insert("lctrl");
|
||||||
|
} else {
|
||||||
|
pressedKeys.insert("rctrl");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Qt::Key_Meta:
|
||||||
|
activateWindow();
|
||||||
|
#ifdef _WIN32
|
||||||
|
pressedKeys.insert("lwin");
|
||||||
|
#else
|
||||||
|
pressedKeys.insert("lmeta");
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case Qt::Key_Space:
|
||||||
|
pressedKeys.insert("space");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Up:
|
||||||
|
activateWindow();
|
||||||
|
pressedKeys.insert("up");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Down:
|
||||||
|
pressedKeys.insert("down");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Left:
|
||||||
|
pressedKeys.insert("left");
|
||||||
|
break;
|
||||||
|
case Qt::Key_Right:
|
||||||
|
pressedKeys.insert("right");
|
||||||
|
break;
|
||||||
|
|
||||||
|
// cancel mapping
|
||||||
|
case Qt::Key_Escape:
|
||||||
|
pressedKeys.insert("unmapped");
|
||||||
|
break;
|
||||||
|
|
||||||
|
// default case
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
// bottom text
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -987,8 +918,17 @@ bool KBMSettings::eventFilter(QObject* obj, QEvent* event) {
|
||||||
case Qt::MiddleButton:
|
case Qt::MiddleButton:
|
||||||
pressedKeys.insert("middlebutton");
|
pressedKeys.insert("middlebutton");
|
||||||
break;
|
break;
|
||||||
|
case Qt::XButton1:
|
||||||
|
pressedKeys.insert("sidebuttonback");
|
||||||
|
break;
|
||||||
|
case Qt::XButton2:
|
||||||
|
pressedKeys.insert("sidebuttonforward");
|
||||||
|
break;
|
||||||
|
|
||||||
|
// default case
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
// bottom text
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1019,22 +959,16 @@ bool KBMSettings::eventFilter(QObject* obj, QEvent* event) {
|
||||||
if (wheelEvent->angleDelta().x() > 5) {
|
if (wheelEvent->angleDelta().x() > 5) {
|
||||||
if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) {
|
if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) {
|
||||||
// QT changes scrolling to horizontal for all widgets with the alt modifier
|
// QT changes scrolling to horizontal for all widgets with the alt modifier
|
||||||
if (Qt::AltModifier & QApplication::keyboardModifiers()) {
|
pressedKeys.insert(
|
||||||
pressedKeys.insert("mousewheelup");
|
GetModifiedButton(Qt::AltModifier, "mousewheelup", "mousewheelright"));
|
||||||
} else {
|
|
||||||
pressedKeys.insert("mousewheelright");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::information(this, tr("Cannot set mapping"),
|
QMessageBox::information(this, tr("Cannot set mapping"),
|
||||||
tr("Mousewheel cannot be mapped to stick outputs"));
|
tr("Mousewheel cannot be mapped to stick outputs"));
|
||||||
}
|
}
|
||||||
} else if (wheelEvent->angleDelta().x() < -5) {
|
} else if (wheelEvent->angleDelta().x() < -5) {
|
||||||
if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) {
|
if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) {
|
||||||
if (Qt::AltModifier & QApplication::keyboardModifiers()) {
|
pressedKeys.insert(
|
||||||
pressedKeys.insert("mousewheeldown");
|
GetModifiedButton(Qt::AltModifier, "mousewheeldown", "mousewheelleft"));
|
||||||
} else {
|
|
||||||
pressedKeys.insert("mousewheelleft");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::information(this, tr("Cannot set mapping"),
|
QMessageBox::information(this, tr("Cannot set mapping"),
|
||||||
tr("Mousewheel cannot be mapped to stick outputs"));
|
tr("Mousewheel cannot be mapped to stick outputs"));
|
||||||
|
|
|
@ -4,6 +4,18 @@
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include "game_info.h"
|
#include "game_info.h"
|
||||||
|
|
||||||
|
// macros > declaring constants
|
||||||
|
// also, we were only using one counterpart
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define LCTRL_KEY 29
|
||||||
|
#define LALT_KEY 56
|
||||||
|
#define LSHIFT_KEY 42
|
||||||
|
#else
|
||||||
|
#define LCTRL_KEY 37
|
||||||
|
#define LALT_KEY 64
|
||||||
|
#define LSHIFT_KEY 50
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class KBMSettings;
|
class KBMSettings;
|
||||||
}
|
}
|
||||||
|
@ -25,22 +37,6 @@ private:
|
||||||
std::unique_ptr<Ui::KBMSettings> ui;
|
std::unique_ptr<Ui::KBMSettings> ui;
|
||||||
std::shared_ptr<GameInfoClass> m_game_info;
|
std::shared_ptr<GameInfoClass> m_game_info;
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
const int lctrl = 29;
|
|
||||||
const int rctrl = 57373;
|
|
||||||
const int lalt = 56;
|
|
||||||
const int ralt = 57400;
|
|
||||||
const int lshift = 42;
|
|
||||||
const int rshift = 54;
|
|
||||||
#else
|
|
||||||
const int lctrl = 37;
|
|
||||||
const int rctrl = 105;
|
|
||||||
const int lalt = 64;
|
|
||||||
const int ralt = 108;
|
|
||||||
const int lshift = 50;
|
|
||||||
const int rshift = 62;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool eventFilter(QObject* obj, QEvent* event) override;
|
bool eventFilter(QObject* obj, QEvent* event) override;
|
||||||
void ButtonConnects();
|
void ButtonConnects();
|
||||||
void SetUIValuestoMappings(std::string config_id);
|
void SetUIValuestoMappings(std::string config_id);
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1234</width>
|
<width>1235</width>
|
||||||
<height>796</height>
|
<height>842</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
@ -44,8 +44,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1214</width>
|
<width>1215</width>
|
||||||
<height>746</height>
|
<height>792</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="layoutWidget">
|
<widget class="QWidget" name="layoutWidget">
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1211</width>
|
<width>1211</width>
|
||||||
<height>741</height>
|
<height>791</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="RemapLayout">
|
<layout class="QHBoxLayout" name="RemapLayout">
|
||||||
|
@ -793,7 +793,7 @@
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="layout_system_buttons" stretch="0">
|
<layout class="QVBoxLayout" name="layout_system_buttons" stretch="0,0">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox_4">
|
<widget class="QGroupBox" name="groupBox_4">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
@ -825,8 +825,11 @@
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumHeight">
|
<property name="minimumSize">
|
||||||
<number>48</number>
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>24</height>
|
||||||
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="focusPolicy">
|
<property name="focusPolicy">
|
||||||
<enum>Qt::FocusPolicy::NoFocus</enum>
|
<enum>Qt::FocusPolicy::NoFocus</enum>
|
||||||
|
@ -844,8 +847,11 @@
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumHeight">
|
<property name="minimumSize">
|
||||||
<number>48</number>
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>24</height>
|
||||||
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="focusPolicy">
|
<property name="focusPolicy">
|
||||||
<enum>Qt::FocusPolicy::NoFocus</enum>
|
<enum>Qt::FocusPolicy::NoFocus</enum>
|
||||||
|
@ -858,6 +864,55 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="gb_options">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>160</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Options</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="gb_start_layout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="OptionsButton">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="focusPolicy">
|
||||||
|
<enum>Qt::FocusPolicy::NoFocus</enum>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>unmapped</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -1067,34 +1122,13 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="gb_touchpad">
|
<widget class="QGroupBox" name="gb_touchpadleft">
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>160</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Touchpad Click</string>
|
<string>Touchpad Left</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_9">
|
<layout class="QVBoxLayout" name="verticalLayout_17">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="TouchpadButton">
|
<widget class="QPushButton" name="TouchpadLeftButton">
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="focusPolicy">
|
|
||||||
<enum>Qt::FocusPolicy::NoFocus</enum>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>unmapped</string>
|
<string>unmapped</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -1150,6 +1184,22 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="gb_touchpadcenter">
|
||||||
|
<property name="title">
|
||||||
|
<string>Touchpad Center</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_11">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="TouchpadCenterButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>unmapped</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -1204,7 +1254,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="gb_options">
|
<widget class="QGroupBox" name="gb_touchpadright">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
|
@ -1218,23 +1268,11 @@
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Options</string>
|
<string>Touchpad Right</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="gb_start_layout">
|
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||||
<property name="leftMargin">
|
|
||||||
<number>5</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>5</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>5</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>5</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="OptionsButton">
|
<widget class="QPushButton" name="TouchpadRightButton">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
|
|
|
@ -78,16 +78,16 @@ HelpDialog::HelpDialog(bool* open_flag, QWidget* parent) : QDialog(parent) {
|
||||||
|
|
||||||
// Add expandable sections to container layout
|
// Add expandable sections to container layout
|
||||||
auto* quickstartSection = new ExpandableSection(tr("Quickstart"), quickstart());
|
auto* quickstartSection = new ExpandableSection(tr("Quickstart"), quickstart());
|
||||||
auto* faqSection = new ExpandableSection(tr("FAQ"), faq());
|
|
||||||
auto* syntaxSection = new ExpandableSection(tr("Syntax"), syntax());
|
auto* syntaxSection = new ExpandableSection(tr("Syntax"), syntax());
|
||||||
auto* specialSection = new ExpandableSection(tr("Special Bindings"), special());
|
|
||||||
auto* bindingsSection = new ExpandableSection(tr("Keybindings"), bindings());
|
auto* bindingsSection = new ExpandableSection(tr("Keybindings"), bindings());
|
||||||
|
auto* specialSection = new ExpandableSection(tr("Special Bindings"), special());
|
||||||
|
auto* faqSection = new ExpandableSection(tr("FAQ"), faq());
|
||||||
|
|
||||||
containerLayout->addWidget(quickstartSection);
|
containerLayout->addWidget(quickstartSection);
|
||||||
containerLayout->addWidget(faqSection);
|
|
||||||
containerLayout->addWidget(syntaxSection);
|
containerLayout->addWidget(syntaxSection);
|
||||||
containerLayout->addWidget(specialSection);
|
|
||||||
containerLayout->addWidget(bindingsSection);
|
containerLayout->addWidget(bindingsSection);
|
||||||
|
containerLayout->addWidget(specialSection);
|
||||||
|
containerLayout->addWidget(faqSection);
|
||||||
containerLayout->addStretch(1);
|
containerLayout->addStretch(1);
|
||||||
|
|
||||||
// Scroll area wrapping the container
|
// Scroll area wrapping the container
|
||||||
|
@ -110,3 +110,160 @@ HelpDialog::HelpDialog(bool* open_flag, QWidget* parent) : QDialog(parent) {
|
||||||
connect(specialSection, &ExpandableSection::expandedChanged, this, &HelpDialog::adjustSize);
|
connect(specialSection, &ExpandableSection::expandedChanged, this, &HelpDialog::adjustSize);
|
||||||
connect(bindingsSection, &ExpandableSection::expandedChanged, this, &HelpDialog::adjustSize);
|
connect(bindingsSection, &ExpandableSection::expandedChanged, this, &HelpDialog::adjustSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper functions that store the text contents for each tab inb the HelpDialog menu
|
||||||
|
QString HelpDialog::quickstart() {
|
||||||
|
return R"(
|
||||||
|
The keyboard and controller remapping backend, GUI, and documentation have been written by kalaposfos.
|
||||||
|
|
||||||
|
In this section, you will find information about the project and its features, as well as help setting up your ideal setup.
|
||||||
|
To view the config file's syntax, check out the Syntax tab, for keybind names, visit Normal Keybinds and Special Bindings, and if you are here to view emulator-wide keybinds, you can find it in the FAQ section.
|
||||||
|
This project began because I disliked the original, unchangeable keybinds. Rather than waiting for someone else to do it, I implemented this myself. From the default keybinds, you can clearly tell this was a project built for Bloodborne, but obviously, you can make adjustments however you like.)";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString HelpDialog::faq() {
|
||||||
|
return R"(
|
||||||
|
Q: What are the emulator-wide keybinds?
|
||||||
|
A:
|
||||||
|
-F12: Triggers Renderdoc capture
|
||||||
|
-F11: Toggles fullscreen
|
||||||
|
-F10: Toggles FPS counter
|
||||||
|
-Ctrl+F10: Open the debug menu
|
||||||
|
-F9: Pauses the emulator if the debug menu is open
|
||||||
|
-F8: Reparses the config file while in-game
|
||||||
|
-F7: Toggles mouse capture and mouse input
|
||||||
|
-F6: Toggles mouse-to-gyro emulation
|
||||||
|
|
||||||
|
Q: How do I switch between mouse and controller joystick input? Why is it even required?
|
||||||
|
A: Pressing F7 toggles between mouse and controller joystick input. It is required because the program polls the mouse input, which means it checks mouse movement every frame. If it didn't move, the code would manually set the emulator's virtual controller to 0 (to the center), even if other input devices would update it.
|
||||||
|
|
||||||
|
Q: What happens if I accidentally make a typo in the config?
|
||||||
|
A: The code recognises the line as wrong and skips it, so the rest of the file will get parsed, but that line in question will be treated like a comment line. You can find these lines in the log if you search for 'input_handler'.
|
||||||
|
|
||||||
|
Q: I want to bind <input> to <output>, but your code doesn't support <input>!
|
||||||
|
A: Some keys are intentionally omitted, but if you read the bindings through, and you're sure it is not there and isn't one of the intentionally disabled ones, open an issue on https://github.com/shadps4-emu/shadPS4.
|
||||||
|
|
||||||
|
Q: What does default.ini do?
|
||||||
|
A: If you're using per-game configs, it's the base from which all new games generate their config file. If you use the unified config, then default.ini is used for every game directly instead.
|
||||||
|
|
||||||
|
Q: What does the use Per-game Config checkbox do?
|
||||||
|
A: It controls whether the config is loaded from CUSAXXXXX.ini for a game or from default.ini. This way, if you only want to manage one set of bindings, you can do so, but if you want to use a different setup for every game, that's possible as well.)";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString HelpDialog::syntax() {
|
||||||
|
return R"(
|
||||||
|
Below is the file format for mouse, keyboard, and controller inputs:
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- You can bind up to 3 inputs to one button.
|
||||||
|
- Adding '#' at the beginning of a line creates a comment.
|
||||||
|
- Extra whitespace doesn't affect input.
|
||||||
|
<output>=<input>; is just as valid as <output> = <input>;
|
||||||
|
- ';' at the end of a line is also optional.
|
||||||
|
|
||||||
|
Syntax (aka how a line can look like):
|
||||||
|
#Comment line
|
||||||
|
<controller_button> = <input>, <input>, <input>;
|
||||||
|
<controller_button> = <input>, <input>;
|
||||||
|
<controller_button> = <input>;
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
#Interact
|
||||||
|
cross = e;
|
||||||
|
#Heavy attack (in BB)
|
||||||
|
r2 = leftbutton, lshift;
|
||||||
|
#Move forward
|
||||||
|
axis_left_y_minus = w;)";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString HelpDialog::bindings() {
|
||||||
|
return R"(
|
||||||
|
The following names should be interpreted without the '' around them. For inputs that have left and right versions, only the left one is shown, but the right version also works.
|
||||||
|
(Example: 'lshift', 'rshift')
|
||||||
|
|
||||||
|
Keyboard:
|
||||||
|
Alphabet:
|
||||||
|
'a', 'b', ..., 'z'
|
||||||
|
Numbers:
|
||||||
|
'0', '1', ..., '9'
|
||||||
|
Keypad:
|
||||||
|
'kp 0', 'kp 1', ..., 'kp 9',
|
||||||
|
'kp .', 'kp ,', 'kp /', 'kp *', 'kp -', 'kp +', 'kp =', 'kp enter'
|
||||||
|
Symbols:
|
||||||
|
'`', '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '=', '+', '{', '}', '[', ']', '\', '|',
|
||||||
|
';', ':', ''', '"', ',', '<', '.', '>', '/', '?'
|
||||||
|
Special keys:
|
||||||
|
'escape (text editor only)', 'printscreen', 'scrolllock', 'pausebreak',
|
||||||
|
'backspace', 'insert', 'delete', 'home', 'end', 'pgup', 'pgdown', 'tab',
|
||||||
|
'capslock', 'enter', 'space'
|
||||||
|
Arrow keys:
|
||||||
|
'up', 'down', 'left', 'right'
|
||||||
|
Modifier keys:
|
||||||
|
'lctrl', 'lshift', 'lalt', 'lwin' = 'lmeta' (same input, different names, so if you are not on Windows and don't like calling this the Windows key, there is an alternative)
|
||||||
|
|
||||||
|
Mouse:
|
||||||
|
'leftbutton', 'rightbutton', 'middlebutton', 'sidebuttonforward',
|
||||||
|
'sidebuttonback'
|
||||||
|
The following wheel inputs cannot be bound to axis input, only button:
|
||||||
|
'mousewheelup', 'mousewheeldown', 'mousewheelleft',
|
||||||
|
'mousewheelright'
|
||||||
|
|
||||||
|
Controller:
|
||||||
|
The touchpad currently can't be rebound to anything else, but you can bind buttons to it.
|
||||||
|
If you have a controller that has different names for buttons, it will still work. Just look up the equivalent names for that controller.
|
||||||
|
The same left-right rule still applies here.
|
||||||
|
Buttons:
|
||||||
|
'triangle', 'circle', 'cross', 'square', 'l1', 'l3',
|
||||||
|
'options', touchpad', 'up', 'down', 'left', 'right'
|
||||||
|
Input-only:
|
||||||
|
'lpaddle_low', 'lpaddle_high'
|
||||||
|
Output-only:
|
||||||
|
'touchpad_left', 'touchpad_center', 'touchpad_right'
|
||||||
|
Axes if you bind them to a button input:
|
||||||
|
'axis_left_x_plus', 'axis_left_x_minus', 'axis_left_y_plus', 'axis_left_y_minus',
|
||||||
|
'axis_right_x_plus', ..., 'axis_right_y_minus',
|
||||||
|
'l2'
|
||||||
|
Axes if you bind them to another axis input:
|
||||||
|
'axis_left_x', 'axis_left_y', 'axis_right_x', 'axis_right_y',
|
||||||
|
'l2'
|
||||||
|
|
||||||
|
Invalid Inputs:
|
||||||
|
'F1-F12' are reserved for emulator-wide keybinds, and cannot be bound to controller inputs.)";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString HelpDialog::special() {
|
||||||
|
return R"(
|
||||||
|
There are some extra bindings you can put in the config file that don't correspond to a controller input but something else.
|
||||||
|
You can find these here, with detailed comments, examples, and suggestions for most of them.
|
||||||
|
|
||||||
|
'leftjoystick_halfmode' and 'rightjoystick_halfmode' = <key>;
|
||||||
|
These are a pair of input modifiers that change the way keyboard button-bound axes work. By default, those push the joystick to the max in their respective direction, but if their respective 'joystick_halfmode' modifier value is true, they only push it... halfway. With this, you can change from run to walk in games like Bloodborne.
|
||||||
|
|
||||||
|
'mouse_to_joystick' = 'none', 'left' or 'right';
|
||||||
|
This binds the mouse movement to either joystick. If it receives a value that is not 'left' or 'right', it defaults to 'none'.
|
||||||
|
|
||||||
|
'mouse_movement_params' = float, float, float;
|
||||||
|
(If you don't know what a float is, it is a data type that stores decimal numbers.)
|
||||||
|
Default values: 0.5, 1, 0.125
|
||||||
|
Let's break each parameter down:
|
||||||
|
1st: 'mouse_deadzone_offset' should have a value between 0 and 1 (it gets clamped to that range anyway), with 0 being no offset and 1 being pushing the joystick to the max in the direction the mouse moved.
|
||||||
|
This controls the minimum distance the joystick gets moved when moving the mouse. If set to 0, it will emulate raw mouse input, which doesn't work very well due to deadzones preventing input if the movement is not large enough.
|
||||||
|
2nd: 'mouse_speed' is just a standard multiplier to the mouse input speed.
|
||||||
|
If you input a negative number, the axis directions get reversed. Keep in mind that the offset can still push it back to positive if it's big enough.
|
||||||
|
3rd: 'mouse_speed_offset' should be in the 0 to 1 range, with 0 being no offset and 1 being offsetting to the max possible value.
|
||||||
|
Let's set 'mouse_deadzone_offset' to 0.5, and 'mouse_speed_offset' to 0: This means that if we move the mouse very slowly, it still inputs a half-strength joystick input, and if we increase the speed, it would stay that way until we move faster than half the max speed. If we instead set this to 0.25, we now only need to move the mouse faster than the 0.5-0.25=0.25=quarter of the max speed, to get an increase in joystick speed. If we set it to 0.5, then even moving the mouse at 1 pixel per frame will result in a faster-than-minimum speed.
|
||||||
|
|
||||||
|
'key_toggle' = <key>, <key_to_toggle>;
|
||||||
|
This assigns a key to another key, and if pressed, toggles that key's virtual value. If it's on, then it doesn't matter if the key is pressed or not, the input handler will treat it as if it's pressed.
|
||||||
|
Let's say we want to be able to toggle 'l1' with 't'. You can then bind 'l1' to a key you won't use, like 'kpenter'. Then bind 't' to toggle that. You will end up with this:
|
||||||
|
l1 = kpenter;
|
||||||
|
key_toggle = t, kpenter;
|
||||||
|
|
||||||
|
'analog_deadzone' = <device>, <value>, <value>;
|
||||||
|
This sets the deadzone range for various inputs. The first value is the minimum deadzone while the second is the maximum. Values go from 1 to 127 (no deadzone to max deadzone).
|
||||||
|
If you only want a minimum or maximum deadzone, set the other value to 1 or 127 respectively.
|
||||||
|
Valid devices: 'leftjoystick', 'rightjoystick', 'l2', 'r2'
|
||||||
|
|
||||||
|
'mouse_gyro_roll_mode':
|
||||||
|
Controls whether moving the mouse sideways causes a panning or a rolling motion while mouse-to-gyro emulation is active.)";
|
||||||
|
}
|
||||||
|
|
|
@ -42,135 +42,9 @@ protected:
|
||||||
private:
|
private:
|
||||||
bool* help_open_ptr;
|
bool* help_open_ptr;
|
||||||
|
|
||||||
QString quickstart() {
|
QString quickstart();
|
||||||
return
|
QString faq();
|
||||||
R"(The keyboard and controller remapping backend, GUI and documentation have been written by kalaposfos
|
QString syntax();
|
||||||
|
QString bindings();
|
||||||
In this section, you will find information about the project, its features and help on setting up your ideal setup.
|
QString special();
|
||||||
To view the config file's syntax, check out the Syntax tab, for keybind names, visit Normal Keybinds and Special Bindings, and if you are here to view emulator-wide keybinds, you can find it in the FAQ section.
|
|
||||||
This project started out because I didn't like the original unchangeable keybinds, but rather than waiting for someone else to do it, I implemented this myself. From the default keybinds, you can clearly tell this was a project built for Bloodborne, but ovbiously you can make adjustments however you like.
|
|
||||||
)";
|
|
||||||
}
|
|
||||||
QString faq() {
|
|
||||||
return
|
|
||||||
R"(Q: What are the emulator-wide keybinds?
|
|
||||||
A: -F12: Triggers Renderdoc capture
|
|
||||||
-F11: Toggles fullscreen
|
|
||||||
-F10: Toggles FPS counter
|
|
||||||
-Ctrl F10: Open the debug menu
|
|
||||||
-F9: Pauses emultor, if the debug menu is open
|
|
||||||
-F8: Reparses the config file while in-game
|
|
||||||
-F7: Toggles mouse capture and mouse input
|
|
||||||
|
|
||||||
Q: How do I change between mouse and controller joystick input, and why is it even required?
|
|
||||||
A: You can switch between them with F7, and it is required, because mouse input is done with polling, which means mouse movement is checked every frame, and if it didn't move, the code manually sets the emulator's virtual controller to 0 (back to the center), even if other input devices would update it.
|
|
||||||
|
|
||||||
Q: What happens if I accidentally make a typo in the config?
|
|
||||||
A: The code recognises the line as wrong, and skip it, so the rest of the file will get parsed, but that line in question will be treated like a comment line. You can find these lines in the log, if you search for 'input_handler'.
|
|
||||||
|
|
||||||
Q: I want to bind <input> to <output>, but your code doesn't support <input>!
|
|
||||||
A: Some keys are intentionally omitted, but if you read the bindings through, and you're sure it is not there and isn't one of the intentionally disabled ones, open an issue on https://github.com/shadps4-emu/shadPS4.
|
|
||||||
|
|
||||||
Q: What does default.ini do?
|
|
||||||
A: If you're using per-game configs, it's the base from which all new games generate their config file. If you use the unified config, then this is used for every game directly instead.
|
|
||||||
|
|
||||||
Q: What does the use Per-game Config checkbox do?
|
|
||||||
A: It controls whether the config is loaded from CUSAXXXXX.ini for a game, or from default.ini. This way, if you only want to manage one set of bindings, you can do so, but if you want to use a different setup for every game, that's possible as well.
|
|
||||||
)";
|
|
||||||
}
|
|
||||||
QString syntax() {
|
|
||||||
return
|
|
||||||
R"(This is the full list of currently supported mouse, keyboard and controller inputs, and how to use them.
|
|
||||||
Emulator-reserved keys: F1 through F12
|
|
||||||
|
|
||||||
Syntax (aka how a line can look like):
|
|
||||||
#Comment line
|
|
||||||
<controller_button> = <input>, <input>, <input>;
|
|
||||||
<controller_button> = <input>, <input>;
|
|
||||||
<controller_button> = <input>;
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
#Interact
|
|
||||||
cross = e;
|
|
||||||
#Heavy attack (in BB)
|
|
||||||
r2 = leftbutton, lshift;
|
|
||||||
#Move forward
|
|
||||||
axis_left_y_minus = w;
|
|
||||||
|
|
||||||
You can make a comment line by putting # as the first character.
|
|
||||||
Whitespace doesn't matter, <output>=<input>; is just as valid as <output> = <input>;
|
|
||||||
';' at the ends of lines is also optional.
|
|
||||||
)";
|
|
||||||
}
|
|
||||||
QString bindings() {
|
|
||||||
return
|
|
||||||
R"(The following names should be interpreted without the '' around them, and for inputs that have left and right versions, only the left one is shown, but the right can be inferred from that.
|
|
||||||
Example: 'lshift', 'rshift'
|
|
||||||
|
|
||||||
Keyboard:
|
|
||||||
Alphabet: 'a', 'b', ..., 'z'
|
|
||||||
Numbers: '0', '1', ..., '9'
|
|
||||||
Keypad: 'kp0', kp1', ..., 'kp9', 'kpperiod', 'kpcomma',
|
|
||||||
'kpdivide', 'kpmultiply', 'kpdivide', 'kpplus', 'kpminus', 'kpenter'
|
|
||||||
Punctuation and misc:
|
|
||||||
'space', 'comma', 'period', 'question', 'semicolon', 'minus', 'plus', 'lparenthesis', 'lbracket', 'lbrace', 'backslash', 'dash',
|
|
||||||
'enter', 'tab', backspace', 'escape'
|
|
||||||
Arrow keys: 'up', 'down', 'left', 'right'
|
|
||||||
Modifier keys:
|
|
||||||
'lctrl', 'lshift', 'lalt', 'lwin' = 'lmeta' (same input, different names, so if you are not on Windows and don't like calling this the Windows key, there is an alternative)
|
|
||||||
|
|
||||||
Mouse:
|
|
||||||
'leftbutton', 'rightbutton', 'middlebutton', 'sidebuttonforward', 'sidebuttonback'
|
|
||||||
The following wheel inputs cannot be bound to axis input, only button:
|
|
||||||
'mousewheelup', 'mousewheeldown', 'mousewheelleft', 'mousewheelright'
|
|
||||||
|
|
||||||
Controller:
|
|
||||||
The touchpad currently can't be rebound to anything else, but you can bind buttons to it.
|
|
||||||
If you have a controller that has different names for buttons, it will still work, just look up what are the equivalent names for that controller
|
|
||||||
The same left-right rule still applies here.
|
|
||||||
Buttons:
|
|
||||||
'triangle', 'circle', 'cross', 'square', 'l1', 'l3',
|
|
||||||
'options', touchpad', 'up', 'down', 'left', 'right'
|
|
||||||
Axes if you bind them to a button input:
|
|
||||||
'axis_left_x_plus', 'axis_left_x_minus', 'axis_left_y_plus', 'axis_left_y_minus',
|
|
||||||
'axis_right_x_plus', ..., 'axis_right_y_minus',
|
|
||||||
'l2'
|
|
||||||
Axes if you bind them to another axis input:
|
|
||||||
'axis_left_x' 'axis_left_y' 'axis_right_x' 'axis_right_y',
|
|
||||||
'l2'
|
|
||||||
)";
|
|
||||||
}
|
|
||||||
QString special() {
|
|
||||||
return
|
|
||||||
R"(There are some extra bindings you can put into the config file, that don't correspond to a controller input, but rather something else.
|
|
||||||
You can find these here, with detailed comments, examples and suggestions for most of them.
|
|
||||||
|
|
||||||
'leftjoystick_halfmode' and 'rightjoystick_halfmode' = <key>;
|
|
||||||
These are a pair of input modifiers, that change the way keyboard button bound axes work. By default, those push the joystick to the max in their respective direction, but if their respective joystick_halfmode modifier value is true, they only push it... halfway. With this, you can change from run to walk in games like Bloodborne.
|
|
||||||
|
|
||||||
'mouse_to_joystick' = 'none', 'left' or 'right';
|
|
||||||
This binds the mouse movement to either joystick. If it recieves a value that is not 'left' or 'right', it defaults to 'none'.
|
|
||||||
|
|
||||||
'mouse_movement_params' = float, float, float;
|
|
||||||
(If you don't know what a float is, it is a data type that stores non-whole numbers.)
|
|
||||||
Default values: 0.5, 1, 0.125
|
|
||||||
Let's break each parameter down:
|
|
||||||
1st: mouse_deadzone_offset: this value should have a value between 0 and 1 (It gets clamped to that range anyway), with 0 being no offset and 1 being pushing the joystick to the max in the direction the mouse moved.
|
|
||||||
This controls the minimum distance the joystick gets moved, when moving the mouse. If set to 0, it will emulate raw mouse input, which doesn't work very well due to deadzones preventing input if the movement is not large enough.
|
|
||||||
2nd: mouse_speed: It's just a standard multiplier to the mouse input speed.
|
|
||||||
If you input a negative number, the axis directions get reversed (Keep in mind that the offset can still push it back to positive, if it's big enough)
|
|
||||||
3rd: mouse_speed_offset: This also should be in the 0 to 1 range, with 0 being no offset and 1 being offsetting to the max possible value.
|
|
||||||
This is best explained through an example: Let's set mouse_deadzone to 0.5, and this to 0: This means that if we move the mousevery slowly, it still inputs a half-strength joystick input, and if we increase the speed, it would stay that way until we move faster than half the max speed. If we instead set this to 0.25, we now only need to move the mouse faster than the 0.5-0.25=0.25=quarter of the max speed, to get an increase in joystick speed. If we set it to 0.5, then even moving the mouse at 1 pixel per frame will result in a faster-than-minimum speed.
|
|
||||||
|
|
||||||
'key_toggle' = <key>, <key_to_toggle>;
|
|
||||||
This assigns a key to another key, and if pressed, toggles that key's virtual value. If it's on, then it doesn't matter if the key is pressed or not, the input handler will treat it as if it's pressed.
|
|
||||||
You can make an input toggleable with this, for example: Let's say we want to be able to toggle l1 with t. You can then bind l1 to a key you won't use, like kpenter, then bind t to toggle that, so you will end up with this:
|
|
||||||
l1 = kpenter;
|
|
||||||
key_toggle = t, kpenter;
|
|
||||||
'analog_deadzone' = <device>, <value>, <value>;
|
|
||||||
Values go from 1 to 127 (no deadzone to max deadzone), first is the inner, second is the outer deadzone
|
|
||||||
If you only want inner or outer deadzone, set the other to 1 or 127, respectively
|
|
||||||
Devices: leftjoystick, rightjoystick, l2, r2
|
|
||||||
)";
|
|
||||||
}
|
|
||||||
};
|
};
|
|
@ -39,8 +39,6 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||||
|
|
||||||
MainWindow::~MainWindow() {
|
MainWindow::~MainWindow() {
|
||||||
SaveWindowState();
|
SaveWindowState();
|
||||||
const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
|
|
||||||
Config::saveMainWindow(config_dir / "config.toml");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MainWindow::Init() {
|
bool MainWindow::Init() {
|
||||||
|
@ -297,7 +295,7 @@ void MainWindow::CreateDockWindows() {
|
||||||
m_game_list_frame->setObjectName("gamelist");
|
m_game_list_frame->setObjectName("gamelist");
|
||||||
m_game_grid_frame.reset(new GameGridFrame(m_gui_settings, m_game_info, m_compat_info, this));
|
m_game_grid_frame.reset(new GameGridFrame(m_gui_settings, m_game_info, m_compat_info, this));
|
||||||
m_game_grid_frame->setObjectName("gamegridlist");
|
m_game_grid_frame->setObjectName("gamegridlist");
|
||||||
m_elf_viewer.reset(new ElfViewer(this));
|
m_elf_viewer.reset(new ElfViewer(m_gui_settings, this));
|
||||||
m_elf_viewer->setObjectName("elflist");
|
m_elf_viewer->setObjectName("elflist");
|
||||||
|
|
||||||
int table_mode = m_gui_settings->GetValue(gui::gl_mode).toInt();
|
int table_mode = m_gui_settings->GetValue(gui::gl_mode).toInt();
|
||||||
|
@ -492,7 +490,7 @@ void MainWindow::CreateConnects() {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
connect(ui->aboutAct, &QAction::triggered, this, [this]() {
|
connect(ui->aboutAct, &QAction::triggered, this, [this]() {
|
||||||
auto aboutDialog = new AboutDialog(this);
|
auto aboutDialog = new AboutDialog(m_gui_settings, this);
|
||||||
aboutDialog->exec();
|
aboutDialog->exec();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -771,14 +769,14 @@ void MainWindow::CreateConnects() {
|
||||||
|
|
||||||
QString gameName = QString::fromStdString(firstGame.name);
|
QString gameName = QString::fromStdString(firstGame.name);
|
||||||
TrophyViewer* trophyViewer =
|
TrophyViewer* trophyViewer =
|
||||||
new TrophyViewer(trophyPath, gameTrpPath, gameName, allTrophyGames);
|
new TrophyViewer(m_gui_settings, trophyPath, gameTrpPath, gameName, allTrophyGames);
|
||||||
trophyViewer->show();
|
trophyViewer->show();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Themes
|
// Themes
|
||||||
connect(ui->setThemeDark, &QAction::triggered, &m_window_themes, [this]() {
|
connect(ui->setThemeDark, &QAction::triggered, &m_window_themes, [this]() {
|
||||||
m_window_themes.SetWindowTheme(Theme::Dark, ui->mw_searchbar);
|
m_window_themes.SetWindowTheme(Theme::Dark, ui->mw_searchbar);
|
||||||
Config::setMainWindowTheme(static_cast<int>(Theme::Dark));
|
m_gui_settings->SetValue(gui::gen_theme, static_cast<int>(Theme::Dark));
|
||||||
if (isIconBlack) {
|
if (isIconBlack) {
|
||||||
SetUiIcons(false);
|
SetUiIcons(false);
|
||||||
isIconBlack = false;
|
isIconBlack = false;
|
||||||
|
@ -786,7 +784,7 @@ void MainWindow::CreateConnects() {
|
||||||
});
|
});
|
||||||
connect(ui->setThemeLight, &QAction::triggered, &m_window_themes, [this]() {
|
connect(ui->setThemeLight, &QAction::triggered, &m_window_themes, [this]() {
|
||||||
m_window_themes.SetWindowTheme(Theme::Light, ui->mw_searchbar);
|
m_window_themes.SetWindowTheme(Theme::Light, ui->mw_searchbar);
|
||||||
Config::setMainWindowTheme(static_cast<int>(Theme::Light));
|
m_gui_settings->SetValue(gui::gen_theme, static_cast<int>(Theme::Light));
|
||||||
if (!isIconBlack) {
|
if (!isIconBlack) {
|
||||||
SetUiIcons(true);
|
SetUiIcons(true);
|
||||||
isIconBlack = true;
|
isIconBlack = true;
|
||||||
|
@ -794,7 +792,7 @@ void MainWindow::CreateConnects() {
|
||||||
});
|
});
|
||||||
connect(ui->setThemeGreen, &QAction::triggered, &m_window_themes, [this]() {
|
connect(ui->setThemeGreen, &QAction::triggered, &m_window_themes, [this]() {
|
||||||
m_window_themes.SetWindowTheme(Theme::Green, ui->mw_searchbar);
|
m_window_themes.SetWindowTheme(Theme::Green, ui->mw_searchbar);
|
||||||
Config::setMainWindowTheme(static_cast<int>(Theme::Green));
|
m_gui_settings->SetValue(gui::gen_theme, static_cast<int>(Theme::Green));
|
||||||
if (isIconBlack) {
|
if (isIconBlack) {
|
||||||
SetUiIcons(false);
|
SetUiIcons(false);
|
||||||
isIconBlack = false;
|
isIconBlack = false;
|
||||||
|
@ -802,7 +800,7 @@ void MainWindow::CreateConnects() {
|
||||||
});
|
});
|
||||||
connect(ui->setThemeBlue, &QAction::triggered, &m_window_themes, [this]() {
|
connect(ui->setThemeBlue, &QAction::triggered, &m_window_themes, [this]() {
|
||||||
m_window_themes.SetWindowTheme(Theme::Blue, ui->mw_searchbar);
|
m_window_themes.SetWindowTheme(Theme::Blue, ui->mw_searchbar);
|
||||||
Config::setMainWindowTheme(static_cast<int>(Theme::Blue));
|
m_gui_settings->SetValue(gui::gen_theme, static_cast<int>(Theme::Blue));
|
||||||
if (isIconBlack) {
|
if (isIconBlack) {
|
||||||
SetUiIcons(false);
|
SetUiIcons(false);
|
||||||
isIconBlack = false;
|
isIconBlack = false;
|
||||||
|
@ -810,7 +808,7 @@ void MainWindow::CreateConnects() {
|
||||||
});
|
});
|
||||||
connect(ui->setThemeViolet, &QAction::triggered, &m_window_themes, [this]() {
|
connect(ui->setThemeViolet, &QAction::triggered, &m_window_themes, [this]() {
|
||||||
m_window_themes.SetWindowTheme(Theme::Violet, ui->mw_searchbar);
|
m_window_themes.SetWindowTheme(Theme::Violet, ui->mw_searchbar);
|
||||||
Config::setMainWindowTheme(static_cast<int>(Theme::Violet));
|
m_gui_settings->SetValue(gui::gen_theme, static_cast<int>(Theme::Violet));
|
||||||
if (isIconBlack) {
|
if (isIconBlack) {
|
||||||
SetUiIcons(false);
|
SetUiIcons(false);
|
||||||
isIconBlack = false;
|
isIconBlack = false;
|
||||||
|
@ -818,7 +816,7 @@ void MainWindow::CreateConnects() {
|
||||||
});
|
});
|
||||||
connect(ui->setThemeGruvbox, &QAction::triggered, &m_window_themes, [this]() {
|
connect(ui->setThemeGruvbox, &QAction::triggered, &m_window_themes, [this]() {
|
||||||
m_window_themes.SetWindowTheme(Theme::Gruvbox, ui->mw_searchbar);
|
m_window_themes.SetWindowTheme(Theme::Gruvbox, ui->mw_searchbar);
|
||||||
Config::setMainWindowTheme(static_cast<int>(Theme::Gruvbox));
|
m_gui_settings->SetValue(gui::gen_theme, static_cast<int>(Theme::Gruvbox));
|
||||||
if (isIconBlack) {
|
if (isIconBlack) {
|
||||||
SetUiIcons(false);
|
SetUiIcons(false);
|
||||||
isIconBlack = false;
|
isIconBlack = false;
|
||||||
|
@ -826,7 +824,7 @@ void MainWindow::CreateConnects() {
|
||||||
});
|
});
|
||||||
connect(ui->setThemeTokyoNight, &QAction::triggered, &m_window_themes, [this]() {
|
connect(ui->setThemeTokyoNight, &QAction::triggered, &m_window_themes, [this]() {
|
||||||
m_window_themes.SetWindowTheme(Theme::TokyoNight, ui->mw_searchbar);
|
m_window_themes.SetWindowTheme(Theme::TokyoNight, ui->mw_searchbar);
|
||||||
Config::setMainWindowTheme(static_cast<int>(Theme::TokyoNight));
|
m_gui_settings->SetValue(gui::gen_theme, static_cast<int>(Theme::TokyoNight));
|
||||||
if (isIconBlack) {
|
if (isIconBlack) {
|
||||||
SetUiIcons(false);
|
SetUiIcons(false);
|
||||||
isIconBlack = false;
|
isIconBlack = false;
|
||||||
|
@ -834,7 +832,7 @@ void MainWindow::CreateConnects() {
|
||||||
});
|
});
|
||||||
connect(ui->setThemeOled, &QAction::triggered, &m_window_themes, [this]() {
|
connect(ui->setThemeOled, &QAction::triggered, &m_window_themes, [this]() {
|
||||||
m_window_themes.SetWindowTheme(Theme::Oled, ui->mw_searchbar);
|
m_window_themes.SetWindowTheme(Theme::Oled, ui->mw_searchbar);
|
||||||
Config::setMainWindowTheme(static_cast<int>(Theme::Oled));
|
m_gui_settings->SetValue(gui::gen_theme, static_cast<int>(Theme::Oled));
|
||||||
if (isIconBlack) {
|
if (isIconBlack) {
|
||||||
SetUiIcons(false);
|
SetUiIcons(false);
|
||||||
isIconBlack = false;
|
isIconBlack = false;
|
||||||
|
@ -981,7 +979,7 @@ void MainWindow::InstallDirectory() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::SetLastUsedTheme() {
|
void MainWindow::SetLastUsedTheme() {
|
||||||
Theme lastTheme = static_cast<Theme>(Config::getMainWindowTheme());
|
Theme lastTheme = static_cast<Theme>(m_gui_settings->GetValue(gui::gen_theme).toInt());
|
||||||
m_window_themes.SetWindowTheme(lastTheme, ui->mw_searchbar);
|
m_window_themes.SetWindowTheme(lastTheme, ui->mw_searchbar);
|
||||||
|
|
||||||
switch (lastTheme) {
|
switch (lastTheme) {
|
||||||
|
@ -1122,33 +1120,32 @@ void MainWindow::HandleResize(QResizeEvent* event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::AddRecentFiles(QString filePath) {
|
void MainWindow::AddRecentFiles(QString filePath) {
|
||||||
std::vector<std::string> vec = Config::getRecentFiles();
|
QList<QString> list = gui_settings::Var2List(m_gui_settings->GetValue(gui::gen_recentFiles));
|
||||||
if (!vec.empty()) {
|
if (!list.empty()) {
|
||||||
if (filePath.toStdString() == vec.at(0)) {
|
if (filePath == list.at(0)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto it = std::find(vec.begin(), vec.end(), filePath.toStdString());
|
auto it = std::find(list.begin(), list.end(), filePath);
|
||||||
if (it != vec.end()) {
|
if (it != list.end()) {
|
||||||
vec.erase(it);
|
list.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vec.insert(vec.begin(), filePath.toStdString());
|
list.insert(list.begin(), filePath);
|
||||||
if (vec.size() > 6) {
|
if (list.size() > 6) {
|
||||||
vec.pop_back();
|
list.pop_back();
|
||||||
}
|
}
|
||||||
Config::setRecentFiles(vec);
|
m_gui_settings->SetValue(gui::gen_recentFiles, gui_settings::List2Var(list));
|
||||||
const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
|
|
||||||
Config::saveMainWindow(config_dir / "config.toml");
|
|
||||||
CreateRecentGameActions(); // Refresh the QActions.
|
CreateRecentGameActions(); // Refresh the QActions.
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::CreateRecentGameActions() {
|
void MainWindow::CreateRecentGameActions() {
|
||||||
m_recent_files_group = new QActionGroup(this);
|
m_recent_files_group = new QActionGroup(this);
|
||||||
ui->menuRecent->clear();
|
ui->menuRecent->clear();
|
||||||
std::vector<std::string> vec = Config::getRecentFiles();
|
QList<QString> list = gui_settings::Var2List(m_gui_settings->GetValue(gui::gen_recentFiles));
|
||||||
for (int i = 0; i < vec.size(); i++) {
|
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
QAction* recentFileAct = new QAction(this);
|
QAction* recentFileAct = new QAction(this);
|
||||||
recentFileAct->setText(QString::fromStdString(vec.at(i)));
|
recentFileAct->setText(list.at(i));
|
||||||
ui->menuRecent->addAction(recentFileAct);
|
ui->menuRecent->addAction(recentFileAct);
|
||||||
m_recent_files_group->addAction(recentFileAct);
|
m_recent_files_group->addAction(recentFileAct);
|
||||||
}
|
}
|
||||||
|
@ -1165,7 +1162,7 @@ void MainWindow::CreateRecentGameActions() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::LoadTranslation() {
|
void MainWindow::LoadTranslation() {
|
||||||
auto language = QString::fromStdString(Config::getEmulatorLanguage());
|
auto language = m_gui_settings->GetValue(gui::gen_guiLanguage).toString();
|
||||||
|
|
||||||
const QString base_dir = QStringLiteral(":/translations");
|
const QString base_dir = QStringLiteral(":/translations");
|
||||||
QString base_path = QStringLiteral("%1/%2.qm").arg(base_dir).arg(language);
|
QString base_path = QStringLiteral("%1/%2.qm").arg(base_dir).arg(language);
|
||||||
|
@ -1190,8 +1187,8 @@ void MainWindow::LoadTranslation() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::OnLanguageChanged(const std::string& locale) {
|
void MainWindow::OnLanguageChanged(const QString& locale) {
|
||||||
Config::setEmulatorLanguage(locale);
|
m_gui_settings->SetValue(gui::gen_guiLanguage, locale);
|
||||||
|
|
||||||
LoadTranslation();
|
LoadTranslation();
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ private Q_SLOTS:
|
||||||
void ShowGameList();
|
void ShowGameList();
|
||||||
void RefreshGameTable();
|
void RefreshGameTable();
|
||||||
void HandleResize(QResizeEvent* event);
|
void HandleResize(QResizeEvent* event);
|
||||||
void OnLanguageChanged(const std::string& locale);
|
void OnLanguageChanged(const QString& locale);
|
||||||
void toggleLabelsUnderIcons();
|
void toggleLabelsUnderIcons();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -75,3 +75,17 @@ void settings::SetValue(const QString& key, const QString& name, const QVariant&
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
QVariant settings::List2Var(const QList<QString>& list) {
|
||||||
|
QByteArray ba;
|
||||||
|
QDataStream stream(&ba, QIODevice::WriteOnly);
|
||||||
|
stream << list;
|
||||||
|
return QVariant(ba);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QString> settings::Var2List(const QVariant& var) {
|
||||||
|
QList<QString> list;
|
||||||
|
QByteArray ba = var.toByteArray();
|
||||||
|
QDataStream stream(&ba, QIODevice::ReadOnly);
|
||||||
|
stream >> list;
|
||||||
|
return list;
|
||||||
|
}
|
|
@ -35,6 +35,8 @@ public:
|
||||||
|
|
||||||
QVariant GetValue(const QString& key, const QString& name, const QVariant& def) const;
|
QVariant GetValue(const QString& key, const QString& name, const QVariant& def) const;
|
||||||
QVariant GetValue(const gui_value& entry) const;
|
QVariant GetValue(const gui_value& entry) const;
|
||||||
|
static QVariant List2Var(const QList<QString>& list);
|
||||||
|
static QList<QString> Var2List(const QVariant& var);
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
/** Remove entry */
|
/** Remove entry */
|
||||||
|
|
|
@ -123,11 +123,6 @@ SettingsDialog::SettingsDialog(std::shared_ptr<gui_settings> gui_settings,
|
||||||
ui->hideCursorComboBox->addItem(tr("Idle"));
|
ui->hideCursorComboBox->addItem(tr("Idle"));
|
||||||
ui->hideCursorComboBox->addItem(tr("Always"));
|
ui->hideCursorComboBox->addItem(tr("Always"));
|
||||||
|
|
||||||
ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Left"), "left");
|
|
||||||
ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Center"), "center");
|
|
||||||
ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Right"), "right");
|
|
||||||
ui->backButtonBehaviorComboBox->addItem(tr("None"), "none");
|
|
||||||
|
|
||||||
InitializeEmulatorLanguages();
|
InitializeEmulatorLanguages();
|
||||||
LoadValuesFromConfig();
|
LoadValuesFromConfig();
|
||||||
|
|
||||||
|
@ -366,7 +361,6 @@ SettingsDialog::SettingsDialog(std::shared_ptr<gui_settings> gui_settings,
|
||||||
// Input
|
// Input
|
||||||
ui->hideCursorGroupBox->installEventFilter(this);
|
ui->hideCursorGroupBox->installEventFilter(this);
|
||||||
ui->idleTimeoutGroupBox->installEventFilter(this);
|
ui->idleTimeoutGroupBox->installEventFilter(this);
|
||||||
ui->backButtonBehaviorGroupBox->installEventFilter(this);
|
|
||||||
|
|
||||||
// Graphics
|
// Graphics
|
||||||
ui->graphicsAdapterGroupBox->installEventFilter(this);
|
ui->graphicsAdapterGroupBox->installEventFilter(this);
|
||||||
|
@ -534,10 +528,6 @@ void SettingsDialog::LoadValuesFromConfig() {
|
||||||
indexTab = 0;
|
indexTab = 0;
|
||||||
ui->tabWidgetSettings->setCurrentIndex(indexTab);
|
ui->tabWidgetSettings->setCurrentIndex(indexTab);
|
||||||
|
|
||||||
QString backButtonBehavior = QString::fromStdString(
|
|
||||||
toml::find_or<std::string>(data, "Input", "backButtonBehavior", "left"));
|
|
||||||
int index = ui->backButtonBehaviorComboBox->findData(backButtonBehavior);
|
|
||||||
ui->backButtonBehaviorComboBox->setCurrentIndex(index != -1 ? index : 0);
|
|
||||||
ui->motionControlsCheckBox->setChecked(
|
ui->motionControlsCheckBox->setChecked(
|
||||||
toml::find_or<bool>(data, "Input", "isMotionControlsEnabled", true));
|
toml::find_or<bool>(data, "Input", "isMotionControlsEnabled", true));
|
||||||
|
|
||||||
|
@ -594,7 +584,7 @@ void SettingsDialog::OnLanguageChanged(int index) {
|
||||||
|
|
||||||
ui->retranslateUi(this);
|
ui->retranslateUi(this);
|
||||||
|
|
||||||
emit LanguageChanged(ui->emulatorLanguageComboBox->itemData(index).toString().toStdString());
|
emit LanguageChanged(ui->emulatorLanguageComboBox->itemData(index).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::OnCursorStateChanged(s16 index) {
|
void SettingsDialog::OnCursorStateChanged(s16 index) {
|
||||||
|
@ -666,8 +656,6 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) {
|
||||||
text = tr("Hide Cursor:\\nChoose when the cursor will disappear:\\nNever: You will always see the mouse.\\nidle: Set a time for it to disappear after being idle.\\nAlways: you will never see the mouse.");
|
text = tr("Hide Cursor:\\nChoose when the cursor will disappear:\\nNever: You will always see the mouse.\\nidle: Set a time for it to disappear after being idle.\\nAlways: you will never see the mouse.");
|
||||||
} else if (elementName == "idleTimeoutGroupBox") {
|
} else if (elementName == "idleTimeoutGroupBox") {
|
||||||
text = tr("Hide Idle Cursor Timeout:\\nThe duration (seconds) after which the cursor that has been idle hides itself.");
|
text = tr("Hide Idle Cursor Timeout:\\nThe duration (seconds) after which the cursor that has been idle hides itself.");
|
||||||
} else if (elementName == "backButtonBehaviorGroupBox") {
|
|
||||||
text = tr("Back Button Behavior:\\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Graphics
|
// Graphics
|
||||||
|
@ -745,8 +733,6 @@ bool SettingsDialog::eventFilter(QObject* obj, QEvent* event) {
|
||||||
|
|
||||||
void SettingsDialog::UpdateSettings() {
|
void SettingsDialog::UpdateSettings() {
|
||||||
|
|
||||||
const QVector<std::string> TouchPadIndex = {"left", "center", "right", "none"};
|
|
||||||
Config::setBackButtonBehavior(TouchPadIndex[ui->backButtonBehaviorComboBox->currentIndex()]);
|
|
||||||
Config::setIsFullscreen(screenModeMap.value(ui->displayModeComboBox->currentText()) !=
|
Config::setIsFullscreen(screenModeMap.value(ui->displayModeComboBox->currentText()) !=
|
||||||
"Windowed");
|
"Windowed");
|
||||||
Config::setFullscreenMode(
|
Config::setFullscreenMode(
|
||||||
|
@ -886,4 +872,5 @@ void SettingsDialog::setDefaultValues() {
|
||||||
} else {
|
} else {
|
||||||
m_gui_settings->SetValue(gui::gen_updateChannel, "Nightly");
|
m_gui_settings->SetValue(gui::gen_updateChannel, "Nightly");
|
||||||
}
|
}
|
||||||
|
m_gui_settings->SetValue(gui::gen_guiLanguage, "en_US");
|
||||||
}
|
}
|
|
@ -32,7 +32,7 @@ public:
|
||||||
int exec() override;
|
int exec() override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void LanguageChanged(const std::string& locale);
|
void LanguageChanged(const QString& locale);
|
||||||
void CompatibilityChanged();
|
void CompatibilityChanged();
|
||||||
void BackgroundOpacityChanged(int opacity);
|
void BackgroundOpacityChanged(int opacity);
|
||||||
|
|
||||||
|
|
|
@ -1613,36 +1613,6 @@
|
||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>11</number>
|
<number>11</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="backButtonBehaviorGroupBox">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="title">
|
|
||||||
<string>Back Button Behavior</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="BackButtonLayout">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>11</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QComboBox" name="backButtonBehaviorComboBox"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="motionControlsCheckBox">
|
<widget class="QCheckBox" name="motionControlsCheckBox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>تعذّر الحفظ</translation>
|
<translation>تعذّر الحفظ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation>لا يمكن تعيين نفس الزر لأكثر من وظيفة</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation>اضغط زرًا</translation>
|
<translation>اضغط زرًا</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>إلغاء</translation>
|
<translation>إلغاء</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation type="unfinished">Unable to Save</translation>
|
<translation type="unfinished">Unable to Save</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation type="unfinished">Cannot bind any unique input more than once</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation type="unfinished">Press a key</translation>
|
<translation type="unfinished">Press a key</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation type="unfinished">Cancel</translation>
|
<translation type="unfinished">Cancel</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>Speichern nicht möglich</translation>
|
<translation>Speichern nicht möglich</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation>Kann keine eindeutige Eingabe mehr als einmal zuordnen</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation>Drücken Sie eine Taste</translation>
|
<translation>Drücken Sie eine Taste</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>Abbrechen</translation>
|
<translation>Abbrechen</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation type="unfinished">Unable to Save</translation>
|
<translation type="unfinished">Unable to Save</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation type="unfinished">Cannot bind any unique input more than once</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation type="unfinished">Press a key</translation>
|
<translation type="unfinished">Press a key</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation type="unfinished">Cancel</translation>
|
<translation type="unfinished">Cancel</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1056,10 +1056,6 @@
|
||||||
<source>L3</source>
|
<source>L3</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Touchpad Click</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Mouse to Joystick</source>
|
<source>Mouse to Joystick</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
|
@ -1152,10 +1148,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
|
@ -1184,6 +1176,24 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation type="unfinished">Cancel</translation>
|
<translation type="unfinished">Cancel</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Touchpad Left</source>
|
||||||
|
<translation type="unfinished">Touchpad Left</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Touchpad Center</source>
|
||||||
|
<translation type="unfinished">Touchpad Center</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Touchpad Right</source>
|
||||||
|
<translation type="unfinished">Touchpad Right</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
@ -1542,10 +1552,6 @@
|
||||||
<source>Controller</source>
|
<source>Controller</source>
|
||||||
<translation>Controller</translation>
|
<translation>Controller</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Back Button Behavior</source>
|
|
||||||
<translation>Back Button Behavior</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Graphics</source>
|
<source>Graphics</source>
|
||||||
<translation>Graphics</translation>
|
<translation>Graphics</translation>
|
||||||
|
@ -1782,10 +1788,6 @@
|
||||||
<source>Hide Idle Cursor Timeout:\nThe duration (seconds) after which the cursor that has been idle hides itself.</source>
|
<source>Hide Idle Cursor Timeout:\nThe duration (seconds) after which the cursor that has been idle hides itself.</source>
|
||||||
<translation>Hide Idle Cursor Timeout:\nThe duration (seconds) after which the cursor that has been idle hides itself.</translation>
|
<translation>Hide Idle Cursor Timeout:\nThe duration (seconds) after which the cursor that has been idle hides itself.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad.</source>
|
|
||||||
<translation>Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad.</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</source>
|
<source>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</source>
|
||||||
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
|
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
|
||||||
|
@ -1810,22 +1812,6 @@
|
||||||
<source>Always</source>
|
<source>Always</source>
|
||||||
<translation>Always</translation>
|
<translation>Always</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Touchpad Left</source>
|
|
||||||
<translation>Touchpad Left</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Touchpad Right</source>
|
|
||||||
<translation>Touchpad Right</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Touchpad Center</source>
|
|
||||||
<translation>Touchpad Center</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>None</source>
|
|
||||||
<translation>None</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Graphics Device:\nOn multiple GPU systems, select the GPU the emulator will use from the drop down list,\nor select "Auto Select" to automatically determine it.</source>
|
<source>Graphics Device:\nOn multiple GPU systems, select the GPU the emulator will use from the drop down list,\nor select "Auto Select" to automatically determine it.</source>
|
||||||
<translation>Graphics Device:\nOn multiple GPU systems, select the GPU the emulator will use from the drop down list,\nor select "Auto Select" to automatically determine it.</translation>
|
<translation>Graphics Device:\nOn multiple GPU systems, select the GPU the emulator will use from the drop down list,\nor select "Auto Select" to automatically determine it.</translation>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>No se Pudo Guardar</translation>
|
<translation>No se Pudo Guardar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation>No se puede vincular ninguna entrada única más de una vez</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation>Pulsa una tecla</translation>
|
<translation>Pulsa una tecla</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>Cancelar</translation>
|
<translation>Cancelar</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation type="unfinished">Unable to Save</translation>
|
<translation type="unfinished">Unable to Save</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation type="unfinished">Cannot bind any unique input more than once</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation type="unfinished">Press a key</translation>
|
<translation type="unfinished">Press a key</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation type="unfinished">Cancel</translation>
|
<translation type="unfinished">Cancel</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation type="unfinished">Unable to Save</translation>
|
<translation type="unfinished">Unable to Save</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation type="unfinished">Cannot bind any unique input more than once</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation type="unfinished">Press a key</translation>
|
<translation type="unfinished">Press a key</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation type="unfinished">Cancel</translation>
|
<translation type="unfinished">Cancel</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>Impossible de sauvegarder</translation>
|
<translation>Impossible de sauvegarder</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation>Impossible de lier une entrée unique plus d'une fois</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation>Appuyez sur un bouton</translation>
|
<translation>Appuyez sur un bouton</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>Annuler</translation>
|
<translation>Annuler</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation type="unfinished">Unable to Save</translation>
|
<translation type="unfinished">Unable to Save</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation type="unfinished">Cannot bind any unique input more than once</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation type="unfinished">Press a key</translation>
|
<translation type="unfinished">Press a key</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation type="unfinished">Cancel</translation>
|
<translation type="unfinished">Cancel</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation type="unfinished">Unable to Save</translation>
|
<translation type="unfinished">Unable to Save</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation type="unfinished">Cannot bind any unique input more than once</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation type="unfinished">Press a key</translation>
|
<translation type="unfinished">Press a key</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation type="unfinished">Cancel</translation>
|
<translation type="unfinished">Cancel</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>Impossibile Salvare</translation>
|
<translation>Impossibile Salvare</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation>Non è possibile associare qualsiasi input univoco più di una volta</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation>Premi un tasto</translation>
|
<translation>Premi un tasto</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>Annulla</translation>
|
<translation>Annulla</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation>Non è possibile associare più di una volta qualsiasi input univoco. Sono presenti input duplicati mappati ai seguenti pulsanti:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation type="unfinished">Unable to Save</translation>
|
<translation type="unfinished">Unable to Save</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation type="unfinished">Cannot bind any unique input more than once</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation type="unfinished">Press a key</translation>
|
<translation type="unfinished">Press a key</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation type="unfinished">Cancel</translation>
|
<translation type="unfinished">Cancel</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation type="unfinished">Unable to Save</translation>
|
<translation type="unfinished">Unable to Save</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation type="unfinished">Cannot bind any unique input more than once</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation type="unfinished">Press a key</translation>
|
<translation type="unfinished">Press a key</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation type="unfinished">Cancel</translation>
|
<translation type="unfinished">Cancel</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation type="unfinished">Unable to Save</translation>
|
<translation type="unfinished">Unable to Save</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation type="unfinished">Cannot bind any unique input more than once</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation type="unfinished">Press a key</translation>
|
<translation type="unfinished">Press a key</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation type="unfinished">Cancel</translation>
|
<translation type="unfinished">Cancel</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -337,7 +337,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The update has been downloaded, press OK to install.</source>
|
<source>The update has been downloaded, press OK to install.</source>
|
||||||
<translation>Oppdateringen ble lastet ned, trykk OK for å installere.</translation>
|
<translation>Oppdateringen er lastet ned, trykk OK for å installere.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to save the update file at</source>
|
<source>Failed to save the update file at</source>
|
||||||
|
@ -990,7 +990,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>unmapped</source>
|
<source>unmapped</source>
|
||||||
<translation>Ikke satt opp</translation>
|
<translation>Ikke tildelt</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Left</source>
|
<source>Left</source>
|
||||||
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>Klarte ikke lagre</translation>
|
<translation>Klarte ikke lagre</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation>Kan ikke tildele unike oppsett mer enn en gang</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation>Trykk på en tast</translation>
|
<translation>Trykk på en tast</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>Avbryt</translation>
|
<translation>Avbryt</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation>Kan ikke tildele samme inndata mer enn én gang. Dupliserte inndata tildeles følgende taster:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation type="unfinished">Unable to Save</translation>
|
<translation type="unfinished">Unable to Save</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation type="unfinished">Cannot bind any unique input more than once</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation type="unfinished">Press a key</translation>
|
<translation type="unfinished">Press a key</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation type="unfinished">Cancel</translation>
|
<translation type="unfinished">Cancel</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>Zapisywanie nie powiodło się</translation>
|
<translation>Zapisywanie nie powiodło się</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation>Nie można powiązać żadnych unikalnych danych wejściowych więcej niż raz</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation>Naciśnij klawisz</translation>
|
<translation>Naciśnij klawisz</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>Anuluj</translation>
|
<translation>Anuluj</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>Não foi possível salvar</translation>
|
<translation>Não foi possível salvar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation>Não é possível vincular qualquer entrada única mais de uma vez</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation>Aperte uma tecla</translation>
|
<translation>Aperte uma tecla</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>Cancelar</translation>
|
<translation>Cancelar</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation>Não é possível atribuir a mesma entrada mais de uma vez. Entradas duplicadas foram atribuídas aos seguintes botões:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>Não foi possível guardar</translation>
|
<translation>Não foi possível guardar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation>Não é possível vincular qualquer entrada única mais de uma vez</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation>Pressione uma tecla</translation>
|
<translation>Pressione uma tecla</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>Cancelar</translation>
|
<translation>Cancelar</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation type="unfinished">Unable to Save</translation>
|
<translation type="unfinished">Unable to Save</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation type="unfinished">Cannot bind any unique input more than once</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation type="unfinished">Press a key</translation>
|
<translation type="unfinished">Press a key</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation type="unfinished">Cancel</translation>
|
<translation type="unfinished">Cancel</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1138,7 +1138,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config.</source>
|
<source>This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config.</source>
|
||||||
<translation>Эта кнопка копирует настройки из общего конфига в текущий выбранный профиль, и не может быть использован, когда выбранный профиль это общий конфиг.</translation>
|
<translation>Эта кнопка копирует настройки из общего конфига в текущий выбранный профиль, и не может быть использован, когда выбранный профиль - это общий конфиг.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Copy values from Common Config</source>
|
<source>Copy values from Common Config</source>
|
||||||
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>Не удаётся сохранить</translation>
|
<translation>Не удаётся сохранить</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation>Невозможно привязать уникальный ввод более одного раза</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation>Нажмите кнопку</translation>
|
<translation>Нажмите кнопку</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>Отмена</translation>
|
<translation>Отмена</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation>Невозможно привязать уникальный ввод более одного раза. Дублированные вводы назначены на следующие кнопки:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation type="unfinished">Unable to Save</translation>
|
<translation type="unfinished">Unable to Save</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation type="unfinished">Cannot bind any unique input more than once</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation type="unfinished">Press a key</translation>
|
<translation type="unfinished">Press a key</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation type="unfinished">Cancel</translation>
|
<translation type="unfinished">Cancel</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>Ruajtja Dështoi</translation>
|
<translation>Ruajtja Dështoi</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation>Asnjë hyrje unike nuk mund të caktohet më shumë se një herë</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation>Shtyp një tast</translation>
|
<translation>Shtyp një tast</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>Anulo</translation>
|
<translation>Anulo</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation>Nuk mund të caktohet e njëjta hyrje unike më shumë se një herë. Hyrjet e dublikuara janë caktuar në butonët e mëposhtëm:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation type="unfinished">Unable to Save</translation>
|
<translation type="unfinished">Unable to Save</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation type="unfinished">Cannot bind any unique input more than once</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation type="unfinished">Press a key</translation>
|
<translation type="unfinished">Press a key</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation type="unfinished">Cancel</translation>
|
<translation type="unfinished">Cancel</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -158,7 +158,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game.</source>
|
<source>No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game.</source>
|
||||||
<translation>Inga fusk hittades för detta spel i denna version av det valda förrådet. Prova ett annat förråd eller en annan version av spelet</translation>
|
<translation>Inga fusk hittades för detta spel i denna version av det valda förrådet. Prova ett annat förråd eller en annan version av spelet.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Cheats Downloaded Successfully</source>
|
<source>Cheats Downloaded Successfully</source>
|
||||||
|
@ -166,7 +166,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>You have successfully downloaded the cheats for this version of the game from the selected repository. You can try downloading from another repository, if it is available it will also be possible to use it by selecting the file from the list.</source>
|
<source>You have successfully downloaded the cheats for this version of the game from the selected repository. You can try downloading from another repository, if it is available it will also be possible to use it by selecting the file from the list.</source>
|
||||||
<translation>Du har hämtat ner fusken för denna version av spelet från valt förråd. Du kan försöka att hämta från andra förråd, om de är tillgängliga så kan det vara möjligt att använda det genom att välja det genom att välja filen från listan</translation>
|
<translation>Du har hämtat ner fusken för denna version av spelet från valt förråd. Du kan försöka att hämta från andra förråd, om de är tillgängliga så kan det vara möjligt att använda det genom att välja det genom att välja filen från listan.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to save:</source>
|
<source>Failed to save:</source>
|
||||||
|
@ -182,7 +182,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game.</source>
|
<source>Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game.</source>
|
||||||
<translation>Patchhämtningen är färdig! Alla patchar tillgängliga för alla spel har hämtats och de behövs inte hämtas individuellt för varje spel som med fusk. Om patchen inte dyker upp kan det bero på att den inte finns för det specifika serienumret och versionen av spelet</translation>
|
<translation>Patchhämtningen är färdig! Alla patchar tillgängliga för alla spel har hämtats och de behövs inte hämtas individuellt för varje spel som med fusk. Om patchen inte dyker upp kan det bero på att den inte finns för det specifika serienumret och versionen av spelet.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to parse JSON data from HTML.</source>
|
<source>Failed to parse JSON data from HTML.</source>
|
||||||
|
@ -261,7 +261,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The Auto Updater allows up to 60 update checks per hour.\nYou have reached this limit. Please try again later.</source>
|
<source>The Auto Updater allows up to 60 update checks per hour.\nYou have reached this limit. Please try again later.</source>
|
||||||
<translation>Den automatiska uppdateraren tillåter upp till 60 uppdateringskontroller per timme.\nDu har uppnått denna gräns. Försök igen senare</translation>
|
<translation>Den automatiska uppdateraren tillåter upp till 60 uppdateringskontroller per timme.\nDu har uppnått denna gräns. Försök igen senare.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to parse update information.</source>
|
<source>Failed to parse update information.</source>
|
||||||
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>Kunde inte spara</translation>
|
<translation>Kunde inte spara</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation>Kan inte binda någon unik inmatning fler än en gång</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation>Tryck på en tangent</translation>
|
<translation>Tryck på en tangent</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>Avbryt</translation>
|
<translation>Avbryt</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation>Det går inte att binda samma unika inmatning mer än en gång. Dubbla inmatningar har mappats till följande knappar:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
@ -1728,47 +1732,47 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Console Language:\nSets the language that the PS4 game uses.\nIt's recommended to set this to a language the game supports, which will vary by region.</source>
|
<source>Console Language:\nSets the language that the PS4 game uses.\nIt's recommended to set this to a language the game supports, which will vary by region.</source>
|
||||||
<translation>Konsollspråk:\nStäller in språket som PS4-spelet använder.\nDet rekommenderas att ställa in detta till ett språk som spelet har stöd för, vilket kan skilja sig mellan regioner</translation>
|
<translation>Konsollspråk:\nStäller in språket som PS4-spelet använder.\nDet rekommenderas att ställa in detta till ett språk som spelet har stöd för, vilket kan skilja sig mellan regioner.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Emulator Language:\nSets the language of the emulator's user interface.</source>
|
<source>Emulator Language:\nSets the language of the emulator's user interface.</source>
|
||||||
<translation>Emulatorspråk:\nStäller in språket för emulatorns användargränssnitt</translation>
|
<translation>Emulatorspråk:\nStäller in språket för emulatorns användargränssnitt.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting.</source>
|
<source>Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting.</source>
|
||||||
<translation>Visa startskärm:\nVisar spelets startskärm (en speciell bild) när spelet startas</translation>
|
<translation>Visa startskärm:\nVisar spelets startskärm (en speciell bild) när spelet startas.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Enable Discord Rich Presence:\nDisplays the emulator icon and relevant information on your Discord profile.</source>
|
<source>Enable Discord Rich Presence:\nDisplays the emulator icon and relevant information on your Discord profile.</source>
|
||||||
<translation>Aktivera Discord Rich Presence:\nVisar emulatorikonen och relevant information på din Discord-profil</translation>
|
<translation>Aktivera Discord Rich Presence:\nVisar emulatorikonen och relevant information på din Discord-profil.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Username:\nSets the PS4's account username, which may be displayed by some games.</source>
|
<source>Username:\nSets the PS4's account username, which may be displayed by some games.</source>
|
||||||
<translation>Användarnamn:\nStäller in PS4ans användarkonto, som kan visas av vissa spel</translation>
|
<translation>Användarnamn:\nStäller in PS4ans användarkonto, som kan visas av vissa spel.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</source>
|
<source>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</source>
|
||||||
<translation>Trofényckel:\nNyckel som används för att avkryptera troféer. Måste hämtas från din konsoll (jailbroken).\nMåste innehålla endast hex-tecken</translation>
|
<translation>Trofényckel:\nNyckel som används för att avkryptera troféer. Måste hämtas från din konsoll (jailbroken).\nMåste innehålla endast hex-tecken.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Log Type:\nSets whether to synchronize the output of the log window for performance. May have adverse effects on emulation.</source>
|
<source>Log Type:\nSets whether to synchronize the output of the log window for performance. May have adverse effects on emulation.</source>
|
||||||
<translation>Loggtyp:\nStäller in huruvida synkronisering av utdata för loggfönstret för prestanda. Kan ha inverkan på emulationen</translation>
|
<translation>Loggtyp:\nStäller in huruvida synkronisering av utdata för loggfönstret för prestanda. Kan ha inverkan på emulationen.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Log Filter:\nFilters the log to only print specific information.\nExamples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nLevels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it.</source>
|
<source>Log Filter:\nFilters the log to only print specific information.\nExamples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nLevels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it.</source>
|
||||||
<translation>Loggfilter:\nFiltrera loggen till att endast skriva ut specifik information.\nExempel: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nNivåer: Trace, Debug, Info, Warning, Error, Critical - i den ordningen, en specifik nivå som tystar alla nivåer före den i listan och loggar allting efter den</translation>
|
<translation>Loggfilter:\nFiltrera loggen till att endast skriva ut specifik information.\nExempel: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nNivåer: Trace, Debug, Info, Warning, Error, Critical - i den ordningen, en specifik nivå som tystar alla nivåer före den i listan och loggar allting efter den.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Update:\nRelease: Official versions released every month that may be very outdated, but are more reliable and tested.\nNightly: Development versions that have all the latest features and fixes, but may contain bugs and are less stable.</source>
|
<source>Update:\nRelease: Official versions released every month that may be very outdated, but are more reliable and tested.\nNightly: Development versions that have all the latest features and fixes, but may contain bugs and are less stable.</source>
|
||||||
<translation>Uppdatering:\nRelease: Officiella versioner som släpps varje månad som kan vara mycket utdaterade, men är mer pålitliga och testade.\nNightly: Utvecklingsversioner som har de senaste funktionerna och fixarna, men kan innehålla fel och är mindre stabila</translation>
|
<translation>Uppdatering:\nRelease: Officiella versioner som släpps varje månad som kan vara mycket utdaterade, men är mer pålitliga och testade.\nNightly: Utvecklingsversioner som har de senaste funktionerna och fixarna, men kan innehålla fel och är mindre stabila.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Background Image:\nControl the opacity of the game background image.</source>
|
<source>Background Image:\nControl the opacity of the game background image.</source>
|
||||||
<translation>Bakgrundsbild:\nKontrollerar opaciteten för spelets bakgrundsbild</translation>
|
<translation>Bakgrundsbild:\nKontrollerar opaciteten för spelets bakgrundsbild.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI.</source>
|
<source>Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI.</source>
|
||||||
<translation>Spela upp titelmusik:\nOm ett spel har stöd för det kan speciell musik spelas upp från spelet i gränssnittet</translation>
|
<translation>Spela upp titelmusik:\nOm ett spel har stöd för det kan speciell musik spelas upp från spelet i gränssnittet.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</source>
|
<source>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</source>
|
||||||
|
@ -1776,27 +1780,27 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Hide Cursor:\nChoose when the cursor will disappear:\nNever: You will always see the mouse.\nidle: Set a time for it to disappear after being idle.\nAlways: you will never see the mouse.</source>
|
<source>Hide Cursor:\nChoose when the cursor will disappear:\nNever: You will always see the mouse.\nidle: Set a time for it to disappear after being idle.\nAlways: you will never see the mouse.</source>
|
||||||
<translation>Dölj pekare:\nVälj när muspekaren ska försvinna:\nAldrig: Du kommer alltid se muspekaren.\nOverksam: Ställ in en tid för när den ska försvinna efter den inte använts.\nAlltid: du kommer aldrig se muspekaren</translation>
|
<translation>Dölj pekare:\nVälj när muspekaren ska försvinna:\nAldrig: Du kommer alltid se muspekaren.\nOverksam: Ställ in en tid för när den ska försvinna efter den inte använts.\nAlltid: du kommer aldrig se muspekaren.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Hide Idle Cursor Timeout:\nThe duration (seconds) after which the cursor that has been idle hides itself.</source>
|
<source>Hide Idle Cursor Timeout:\nThe duration (seconds) after which the cursor that has been idle hides itself.</source>
|
||||||
<translation>Dölj pekare vid overksam:\nLängden (sekunder) efter vilken som muspekaren som har varit overksam döljer sig själv</translation>
|
<translation>Dölj pekare vid overksam:\nLängden (sekunder) efter vilken som muspekaren som har varit overksam döljer sig själv.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad.</source>
|
<source>Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad.</source>
|
||||||
<translation>Beteende för bakåtknapp:\nStäller in handkontrollerns bakåtknapp för att emulera ett tryck på angivna positionen på PS4ns touchpad</translation>
|
<translation>Beteende för bakåtknapp:\nStäller in handkontrollerns bakåtknapp för att emulera ett tryck på angivna positionen på PS4ns touchpad.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</source>
|
<source>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</source>
|
||||||
<translation>Visa kompatibilitetsdata:\nVisar information om spelkompatibilitet i tabellvyn. Aktivera "Uppdatera kompatibilitet vid uppstart" för att få uppdaterad information</translation>
|
<translation>Visa kompatibilitetsdata:\nVisar information om spelkompatibilitet i tabellvyn. Aktivera "Uppdatera kompatibilitet vid uppstart" för att få uppdaterad information.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</source>
|
<source>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</source>
|
||||||
<translation>Uppdatera kompatibilitet vid uppstart:\nUppdatera automatiskt kompatibilitetsdatabasen när shadPS4 startar</translation>
|
<translation>Uppdatera kompatibilitet vid uppstart:\nUppdatera automatiskt kompatibilitetsdatabasen när shadPS4 startar.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Update Compatibility Database:\nImmediately update the compatibility database.</source>
|
<source>Update Compatibility Database:\nImmediately update the compatibility database.</source>
|
||||||
<translation>Uppdatera kompatibilitetsdatabasen:\nUppdaterar kompatibilitetsdatabasen direkt</translation>
|
<translation>Uppdatera kompatibilitetsdatabasen:\nUppdaterar kompatibilitetsdatabasen direkt.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Never</source>
|
<source>Never</source>
|
||||||
|
@ -1828,23 +1832,23 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Graphics Device:\nOn multiple GPU systems, select the GPU the emulator will use from the drop down list,\nor select "Auto Select" to automatically determine it.</source>
|
<source>Graphics Device:\nOn multiple GPU systems, select the GPU the emulator will use from the drop down list,\nor select "Auto Select" to automatically determine it.</source>
|
||||||
<translation>Grafikenhet:\nFör system med flera GPUer kan du välja den GPU som emulatorn ska använda från rullgardinsmenyn,\neller välja "Auto Select" för att automatiskt bestämma det</translation>
|
<translation>Grafikenhet:\nFör system med flera GPUer kan du välja den GPU som emulatorn ska använda från rullgardinsmenyn,\neller välja "Auto Select" för att automatiskt bestämma det.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Width/Height:\nSets the size of the emulator window at launch, which can be resized during gameplay.\nThis is different from the in-game resolution.</source>
|
<source>Width/Height:\nSets the size of the emulator window at launch, which can be resized during gameplay.\nThis is different from the in-game resolution.</source>
|
||||||
<translation>Bredd/Höjd:\nStäller in storleken för emulatorfönstret vid uppstart, som kan storleksändras under spelning.\nDetta är inte det samma som spelupplösningen</translation>
|
<translation>Bredd/Höjd:\nStäller in storleken för emulatorfönstret vid uppstart, som kan storleksändras under spelning.\nDetta är inte det samma som spelupplösningen.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Vblank Divider:\nThe frame rate at which the emulator refreshes at is multiplied by this number. Changing this may have adverse effects, such as increasing the game speed, or breaking critical game functionality that does not expect this to change!</source>
|
<source>Vblank Divider:\nThe frame rate at which the emulator refreshes at is multiplied by this number. Changing this may have adverse effects, such as increasing the game speed, or breaking critical game functionality that does not expect this to change!</source>
|
||||||
<translation>Vblank Divider:\nBildfrekvensen som emulatorn uppdaterar vid multipliceras med detta tal. Ändra detta kan ha inverkan på saker, såsom ökad spelhastighet eller göra sönder kritisk spelfunktionalitet, som inte förväntar sig denna ändring</translation>
|
<translation>Vblank Divider:\nBildfrekvensen som emulatorn uppdaterar vid multipliceras med detta tal. Ändra detta kan ha inverkan på saker, såsom ökad spelhastighet eller göra sönder kritisk spelfunktionalitet, som inte förväntar sig denna ändring!</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Enable Shaders Dumping:\nFor the sake of technical debugging, saves the games shaders to a folder as they render.</source>
|
<source>Enable Shaders Dumping:\nFor the sake of technical debugging, saves the games shaders to a folder as they render.</source>
|
||||||
<translation>Aktivera Shaders Dumping:\nFör teknisk felsökning, sparar spelets shaders till en mapp när de renderas</translation>
|
<translation>Aktivera Shaders Dumping:\nFör teknisk felsökning, sparar spelets shaders till en mapp när de renderas.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card.</source>
|
<source>Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card.</source>
|
||||||
<translation>Aktivera Null GPU:\nFör teknisk felsökning, inaktiverar spelrenderingen som om det inte fanns något grafikkort</translation>
|
<translation>Aktivera Null GPU:\nFör teknisk felsökning, inaktiverar spelrenderingen som om det inte fanns något grafikkort.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format.</source>
|
<source>Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format.</source>
|
||||||
|
@ -1852,31 +1856,31 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Game Folders:\nThe list of folders to check for installed games.</source>
|
<source>Game Folders:\nThe list of folders to check for installed games.</source>
|
||||||
<translation>Spelmappar:\nListan över mappar att leta i efter installerade spel</translation>
|
<translation>Spelmappar:\nListan över mappar att leta i efter installerade spel.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Add:\nAdd a folder to the list.</source>
|
<source>Add:\nAdd a folder to the list.</source>
|
||||||
<translation>Aktivera separat uppdateringsmapp:\nAktiverar installation av speluppdateringar till en separat mapp för enkel hantering.\nDetta kan manuellt skapas genom att lägga till den uppackade uppdateringen till spelmappen med namnet "CUSA00000-UPDATE" där CUSA ID matchar spelets id</translation>
|
<translation>Lägg till:\Lägg till en mapp till listan.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Remove:\nRemove a folder from the list.</source>
|
<source>Remove:\nRemove a folder from the list.</source>
|
||||||
<translation>Ta bort:\nTa bort en mapp från listan</translation>
|
<translation>Ta bort:\nTa bort en mapp från listan.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Enable Debug Dumping:\nSaves the import and export symbols and file header information of the currently running PS4 program to a directory.</source>
|
<source>Enable Debug Dumping:\nSaves the import and export symbols and file header information of the currently running PS4 program to a directory.</source>
|
||||||
<translation>Aktivera felsökningsdumpning:\nSparar import och export av symboler och fil-header-information för aktuellt körande PS4-program till en katalog</translation>
|
<translation>Aktivera felsökningsdumpning:\nSparar import och export av symboler och fil-header-information för aktuellt körande PS4-program till en katalog.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Enable Vulkan Validation Layers:\nEnables a system that validates the state of the Vulkan renderer and logs information about its internal state.\nThis will reduce performance and likely change the behavior of emulation.</source>
|
<source>Enable Vulkan Validation Layers:\nEnables a system that validates the state of the Vulkan renderer and logs information about its internal state.\nThis will reduce performance and likely change the behavior of emulation.</source>
|
||||||
<translation>Aktivera Vulkan Validation Layers:\nAktiverar ett system som validerar tillståndet för Vulkan renderer och loggar information om dess interna tillstånd.\nDetta kommer minska prestandan och antagligen ändra beteendet för emuleringen</translation>
|
<translation>Aktivera Vulkan Validation Layers:\nAktiverar ett system som validerar tillståndet för Vulkan renderer och loggar information om dess interna tillstånd.\nDetta kommer minska prestandan och antagligen ändra beteendet för emuleringen.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Enable Vulkan Synchronization Validation:\nEnables a system that validates the timing of Vulkan rendering tasks.\nThis will reduce performance and likely change the behavior of emulation.</source>
|
<source>Enable Vulkan Synchronization Validation:\nEnables a system that validates the timing of Vulkan rendering tasks.\nThis will reduce performance and likely change the behavior of emulation.</source>
|
||||||
<translation>Aktivera Vulkan Synchronization Validation:\nAktiverar ett system som validerar timing för Vulkan rendering tasks.\nDetta kommer minska prestandan och antagligen ändra beteendet för emuleringen</translation>
|
<translation>Aktivera Vulkan Synchronization Validation:\nAktiverar ett system som validerar timing för Vulkan rendering tasks.\nDetta kommer minska prestandan och antagligen ändra beteendet för emuleringen.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Enable RenderDoc Debugging:\nIf enabled, the emulator will provide compatibility with Renderdoc to allow capture and analysis of the currently rendered frame.</source>
|
<source>Enable RenderDoc Debugging:\nIf enabled, the emulator will provide compatibility with Renderdoc to allow capture and analysis of the currently rendered frame.</source>
|
||||||
<translation>Aktivera RenderDoc-felsökning:\nOm aktiverad kommer emulatorn att tillhandahålla kompatibilitet med Renderdoc för att tillåta fångst och analys för aktuell renderad bildruta</translation>
|
<translation>Aktivera RenderDoc-felsökning:\nOm aktiverad kommer emulatorn att tillhandahålla kompatibilitet med Renderdoc för att tillåta fångst och analys för aktuell renderad bildruta.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10).</source>
|
<source>Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10).</source>
|
||||||
|
@ -1884,27 +1888,27 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work.</source>
|
<source>Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work.</source>
|
||||||
<translation>Krashdiagnostik:\nSkapar en .yaml-fil med information om Vulkan-tillståndet vid tid för kraschen.\nAnvändbart för felsökning av 'Device lost'-fel. Om du har aktiverat detta bör du aktivera felsökningsmarkörer för Värd OCH Gäst.\nFungerar inte på Intel GPUer.\nDu behöver aktivera Vulkan Validation Layers och Vulkan SDK för att detta ska fungera</translation>
|
<translation>Krashdiagnostik:\nSkapar en .yaml-fil med information om Vulkan-tillståndet vid tid för kraschen.\nAnvändbart för felsökning av 'Device lost'-fel. Om du har aktiverat detta bör du aktivera felsökningsmarkörer för Värd OCH Gäst.\nFungerar inte på Intel GPUer.\nDu behöver aktivera Vulkan Validation Layers och Vulkan SDK för att detta ska fungera.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes.</source>
|
<source>Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes.</source>
|
||||||
<translation>Kopiera GPU-buffertar:\nGör att man kan komma runt race conditions som involverar GPU submits.\nKan eller kan inte hjälpa med PM4 type 0-kraschar</translation>
|
<translation>Kopiera GPU-buffertar:\nGör att man kan komma runt race conditions som involverar GPU submits.\nKan eller kan inte hjälpa med PM4 type 0-kraschar.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc.</source>
|
<source>Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc.</source>
|
||||||
<translation>Felsökningsmarkörer för värd:\nInfogar informationsliknande markörer i emulatorn för specifika AMDGPU-kommandon runt Vulkan-kommandon, så väl som ger resurser felsökningsnamn.\nOm du har detta aktiverat bör du aktivera Kraschdiagnostik.\nAnvändbart för program som RenderDoc</translation>
|
<translation>Felsökningsmarkörer för värd:\nInfogar informationsliknande markörer i emulatorn för specifika AMDGPU-kommandon runt Vulkan-kommandon, så väl som ger resurser felsökningsnamn.\nOm du har detta aktiverat bör du aktivera Kraschdiagnostik.\nAnvändbart för program som RenderDoc.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc.</source>
|
<source>Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc.</source>
|
||||||
<translation>Felsökningsmarkörer för gäst:\nInfogar felsökningsmarkörer som själva spelet har lagt till i kommandobufferten.\nOm du har aktiverat detta bör du aktivera Kraschdiagnostik.\nAnvändbart för program som RenderDoc</translation>
|
<translation>Felsökningsmarkörer för gäst:\nInfogar felsökningsmarkörer som själva spelet har lagt till i kommandobufferten.\nOm du har aktiverat detta bör du aktivera Kraschdiagnostik.\nAnvändbart för program som RenderDoc.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Save Data Path:\nThe folder where game save data will be saved.</source>
|
<source>Save Data Path:\nThe folder where game save data will be saved.</source>
|
||||||
<translation>Sökväg för sparat data:\nSökvägen där spelets sparade data kommer att sparas</translation>
|
<translation>Sökväg för sparat data:\nSökvägen där spelets sparade data kommer att sparas.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Browse:\nBrowse for a folder to set as the save data path.</source>
|
<source>Browse:\nBrowse for a folder to set as the save data path.</source>
|
||||||
<translation>Bläddra:\nBläddra efter en mapp att ställa in som sökväg för sparat data</translation>
|
<translation>Bläddra:\nBläddra efter en mapp att ställa in som sökväg för sparat data.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Release</source>
|
<source>Release</source>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>Kaydedilemedi</translation>
|
<translation>Kaydedilemedi</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation>Herhangi bir benzersiz girdi birden fazla kez bağlanamaz</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation>Bir tuşa basın</translation>
|
<translation>Bir tuşa basın</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>İptal</translation>
|
<translation>İptal</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>Не вдалося зберегти</translation>
|
<translation>Не вдалося зберегти</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation>Не можна прив'язати кнопку вводу більш ніж один раз</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation>Натисніть клавішу</translation>
|
<translation>Натисніть клавішу</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>Відмінити</translation>
|
<translation>Відмінити</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation type="unfinished">Unable to Save</translation>
|
<translation type="unfinished">Unable to Save</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation type="unfinished">Cannot bind any unique input more than once</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation type="unfinished">Press a key</translation>
|
<translation type="unfinished">Press a key</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>Hủy</translation>
|
<translation>Hủy</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>无法保存</translation>
|
<translation>无法保存</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation>不能绑定重复的按键</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation>按下按键</translation>
|
<translation>按下按键</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>取消</translation>
|
<translation>取消</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation>不能多次绑定任何同一输入。请重新映射以下按键的输入:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -1152,10 +1152,6 @@
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>無法儲存</translation>
|
<translation>無法儲存</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Cannot bind any unique input more than once</source>
|
|
||||||
<translation>任何唯一的鍵位都不能重複連結</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Press a key</source>
|
<source>Press a key</source>
|
||||||
<translation>按下按鍵</translation>
|
<translation>按下按鍵</translation>
|
||||||
|
@ -1184,6 +1180,14 @@
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>取消</translation>
|
<translation>取消</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</source>
|
||||||
|
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
|
||||||
|
|
||||||
|
%1</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
|
|
|
@ -104,14 +104,16 @@ void TrophyViewer::updateTableFilters() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TrophyViewer::TrophyViewer(QString trophyPath, QString gameTrpPath, QString gameName,
|
TrophyViewer::TrophyViewer(std::shared_ptr<gui_settings> gui_settings, QString trophyPath,
|
||||||
|
QString gameTrpPath, QString gameName,
|
||||||
const QVector<TrophyGameInfo>& allTrophyGames)
|
const QVector<TrophyGameInfo>& allTrophyGames)
|
||||||
: QMainWindow(), allTrophyGames_(allTrophyGames), currentGameName_(gameName) {
|
: QMainWindow(), allTrophyGames_(allTrophyGames), currentGameName_(gameName),
|
||||||
|
m_gui_settings(std::move(gui_settings)) {
|
||||||
this->setWindowTitle(tr("Trophy Viewer") + " - " + currentGameName_);
|
this->setWindowTitle(tr("Trophy Viewer") + " - " + currentGameName_);
|
||||||
this->setAttribute(Qt::WA_DeleteOnClose);
|
this->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
tabWidget = new QTabWidget(this);
|
tabWidget = new QTabWidget(this);
|
||||||
|
|
||||||
auto lan = Config::getEmulatorLanguage();
|
auto lan = m_gui_settings->GetValue(gui::gen_guiLanguage).toString();
|
||||||
if (lan == "en_US" || lan == "zh_CN" || lan == "zh_TW" || lan == "ja_JP" || lan == "ko_KR" ||
|
if (lan == "en_US" || lan == "zh_CN" || lan == "zh_TW" || lan == "ja_JP" || lan == "ko_KR" ||
|
||||||
lan == "lt_LT" || lan == "nb_NO" || lan == "nl_NL") {
|
lan == "lt_LT" || lan == "nb_NO" || lan == "nl_NL") {
|
||||||
useEuropeanDateFormat = false;
|
useEuropeanDateFormat = false;
|
||||||
|
@ -463,7 +465,7 @@ void TrophyViewer::SetTableItem(QTableWidget* parent, int row, int column, QStri
|
||||||
item->setTextAlignment(Qt::AlignCenter);
|
item->setTextAlignment(Qt::AlignCenter);
|
||||||
item->setFont(QFont("Arial", 12, QFont::Bold));
|
item->setFont(QFont("Arial", 12, QFont::Bold));
|
||||||
|
|
||||||
Theme theme = static_cast<Theme>(Config::getMainWindowTheme());
|
Theme theme = static_cast<Theme>(m_gui_settings->GetValue(gui::gen_theme).toInt());
|
||||||
|
|
||||||
if (theme == Theme::Light) {
|
if (theme == Theme::Light) {
|
||||||
item->setForeground(QBrush(Qt::black));
|
item->setForeground(QBrush(Qt::black));
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "core/file_format/trp.h"
|
#include "core/file_format/trp.h"
|
||||||
|
#include "gui_settings.h"
|
||||||
|
|
||||||
struct TrophyGameInfo {
|
struct TrophyGameInfo {
|
||||||
QString name;
|
QString name;
|
||||||
|
@ -34,7 +35,8 @@ class TrophyViewer : public QMainWindow {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit TrophyViewer(
|
explicit TrophyViewer(
|
||||||
QString trophyPath, QString gameTrpPath, QString gameName = "",
|
std::shared_ptr<gui_settings> gui_settings, QString trophyPath, QString gameTrpPath,
|
||||||
|
QString gameName = "",
|
||||||
const QVector<TrophyGameInfo>& allTrophyGames = QVector<TrophyGameInfo>());
|
const QVector<TrophyGameInfo>& allTrophyGames = QVector<TrophyGameInfo>());
|
||||||
|
|
||||||
void updateTrophyInfo();
|
void updateTrophyInfo();
|
||||||
|
@ -77,4 +79,5 @@ private:
|
||||||
}
|
}
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
|
std::shared_ptr<gui_settings> m_gui_settings;
|
||||||
};
|
};
|
||||||
|
|
|
@ -474,11 +474,16 @@ void WindowSDL::OnKeyboardMouseInput(const SDL_Event* event) {
|
||||||
Input::ParseInputConfig(std::string(Common::ElfInfo::Instance().GameSerial()));
|
Input::ParseInputConfig(std::string(Common::ElfInfo::Instance().GameSerial()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Toggle mouse capture and movement input
|
// Toggle mouse capture and joystick input emulation
|
||||||
else if (input_id == SDLK_F7) {
|
else if (input_id == SDLK_F7) {
|
||||||
Input::ToggleMouseEnabled();
|
|
||||||
SDL_SetWindowRelativeMouseMode(this->GetSDLWindow(),
|
SDL_SetWindowRelativeMouseMode(this->GetSDLWindow(),
|
||||||
!SDL_GetWindowRelativeMouseMode(this->GetSDLWindow()));
|
Input::ToggleMouseModeTo(Input::MouseMode::Joystick));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Toggle mouse capture and gyro input emulation
|
||||||
|
else if (input_id == SDLK_F6) {
|
||||||
|
SDL_SetWindowRelativeMouseMode(this->GetSDLWindow(),
|
||||||
|
Input::ToggleMouseModeTo(Input::MouseMode::Gyro));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Toggle fullscreen
|
// Toggle fullscreen
|
||||||
|
|
|
@ -271,7 +271,8 @@ void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ct
|
||||||
if (info.has_image_query) {
|
if (info.has_image_query) {
|
||||||
ctx.AddCapability(spv::Capability::ImageQuery);
|
ctx.AddCapability(spv::Capability::ImageQuery);
|
||||||
}
|
}
|
||||||
if (info.uses_atomic_float_min_max && profile.supports_image_fp32_atomic_min_max) {
|
if ((info.uses_image_atomic_float_min_max && profile.supports_image_fp32_atomic_min_max) ||
|
||||||
|
(info.uses_buffer_atomic_float_min_max && profile.supports_buffer_fp32_atomic_min_max)) {
|
||||||
ctx.AddExtension("SPV_EXT_shader_atomic_float_min_max");
|
ctx.AddExtension("SPV_EXT_shader_atomic_float_min_max");
|
||||||
ctx.AddCapability(spv::Capability::AtomicFloat32MinMaxEXT);
|
ctx.AddCapability(spv::Capability::AtomicFloat32MinMaxEXT);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,7 @@ Id SharedAtomicU32(EmitContext& ctx, Id offset, Id value,
|
||||||
const Id shift_id{ctx.ConstU32(2U)};
|
const Id shift_id{ctx.ConstU32(2U)};
|
||||||
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)};
|
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)};
|
||||||
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 4u)};
|
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 4u)};
|
||||||
const Id pointer{
|
const Id pointer{ctx.EmitSharedMemoryAccess(ctx.shared_u32, ctx.shared_memory_u32, index)};
|
||||||
ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, ctx.u32_zero_value, index)};
|
|
||||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||||
return AccessBoundsCheck<32>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
return AccessBoundsCheck<32>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
||||||
return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value);
|
return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value);
|
||||||
|
@ -32,8 +31,7 @@ Id SharedAtomicU32IncDec(EmitContext& ctx, Id offset,
|
||||||
const Id shift_id{ctx.ConstU32(2U)};
|
const Id shift_id{ctx.ConstU32(2U)};
|
||||||
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)};
|
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)};
|
||||||
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 4u)};
|
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 4u)};
|
||||||
const Id pointer{
|
const Id pointer{ctx.EmitSharedMemoryAccess(ctx.shared_u32, ctx.shared_memory_u32, index)};
|
||||||
ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, ctx.u32_zero_value, index)};
|
|
||||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||||
return AccessBoundsCheck<32>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
return AccessBoundsCheck<32>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
||||||
return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics);
|
return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics);
|
||||||
|
@ -45,17 +43,24 @@ Id SharedAtomicU64(EmitContext& ctx, Id offset, Id value,
|
||||||
const Id shift_id{ctx.ConstU32(3U)};
|
const Id shift_id{ctx.ConstU32(3U)};
|
||||||
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)};
|
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)};
|
||||||
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 8u)};
|
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 8u)};
|
||||||
const Id pointer{
|
const Id pointer{ctx.EmitSharedMemoryAccess(ctx.shared_u64, ctx.shared_memory_u64, index)};
|
||||||
ctx.OpAccessChain(ctx.shared_u64, ctx.shared_memory_u64, ctx.u32_zero_value, index)};
|
|
||||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||||
return AccessBoundsCheck<64>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
return AccessBoundsCheck<64>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
||||||
return (ctx.*atomic_func)(ctx.U64, pointer, scope, semantics, value);
|
return (ctx.*atomic_func)(ctx.U64, pointer, scope, semantics, value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <bool is_float = false>
|
||||||
Id BufferAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value,
|
Id BufferAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value,
|
||||||
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
|
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
|
||||||
const auto& buffer = ctx.buffers[handle];
|
const auto& buffer = ctx.buffers[handle];
|
||||||
|
const auto type = [&] {
|
||||||
|
if constexpr (is_float) {
|
||||||
|
return ctx.F32[1];
|
||||||
|
} else {
|
||||||
|
return ctx.U32[1];
|
||||||
|
}
|
||||||
|
}();
|
||||||
if (Sirit::ValidId(buffer.offset)) {
|
if (Sirit::ValidId(buffer.offset)) {
|
||||||
address = ctx.OpIAdd(ctx.U32[1], address, buffer.offset);
|
address = ctx.OpIAdd(ctx.U32[1], address, buffer.offset);
|
||||||
}
|
}
|
||||||
|
@ -63,8 +68,8 @@ Id BufferAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id
|
||||||
const auto [id, pointer_type] = buffer[EmitContext::PointerType::U32];
|
const auto [id, pointer_type] = buffer[EmitContext::PointerType::U32];
|
||||||
const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index);
|
const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index);
|
||||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||||
return AccessBoundsCheck<32>(ctx, index, buffer.size_dwords, [&] {
|
return AccessBoundsCheck<32, 1, is_float>(ctx, index, buffer.size_dwords, [&] {
|
||||||
return (ctx.*atomic_func)(ctx.U32[1], ptr, scope, semantics, value);
|
return (ctx.*atomic_func)(type, ptr, scope, semantics, value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,6 +204,24 @@ Id EmitBufferAtomicUMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre
|
||||||
return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicUMin);
|
return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicUMin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Id EmitBufferAtomicFMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
||||||
|
if (ctx.profile.supports_buffer_fp32_atomic_min_max) {
|
||||||
|
return BufferAtomicU32<true>(ctx, inst, handle, address, value,
|
||||||
|
&Sirit::Module::OpAtomicFMin);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto u32_value = ctx.OpBitcast(ctx.U32[1], value);
|
||||||
|
const auto sign_bit_set =
|
||||||
|
ctx.OpBitFieldUExtract(ctx.U32[1], u32_value, ctx.ConstU32(31u), ctx.ConstU32(1u));
|
||||||
|
|
||||||
|
const auto result = ctx.OpSelect(
|
||||||
|
ctx.F32[1], sign_bit_set,
|
||||||
|
EmitBitCastF32U32(ctx, EmitBufferAtomicUMax32(ctx, inst, handle, address, u32_value)),
|
||||||
|
EmitBitCastF32U32(ctx, EmitBufferAtomicSMin32(ctx, inst, handle, address, u32_value)));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Id EmitBufferAtomicSMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
Id EmitBufferAtomicSMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
||||||
return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicSMax);
|
return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicSMax);
|
||||||
}
|
}
|
||||||
|
@ -207,6 +230,24 @@ Id EmitBufferAtomicUMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre
|
||||||
return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicUMax);
|
return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicUMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Id EmitBufferAtomicFMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
||||||
|
if (ctx.profile.supports_buffer_fp32_atomic_min_max) {
|
||||||
|
return BufferAtomicU32<true>(ctx, inst, handle, address, value,
|
||||||
|
&Sirit::Module::OpAtomicFMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto u32_value = ctx.OpBitcast(ctx.U32[1], value);
|
||||||
|
const auto sign_bit_set =
|
||||||
|
ctx.OpBitFieldUExtract(ctx.U32[1], u32_value, ctx.ConstU32(31u), ctx.ConstU32(1u));
|
||||||
|
|
||||||
|
const auto result = ctx.OpSelect(
|
||||||
|
ctx.F32[1], sign_bit_set,
|
||||||
|
EmitBitCastF32U32(ctx, EmitBufferAtomicUMin32(ctx, inst, handle, address, u32_value)),
|
||||||
|
EmitBitCastF32U32(ctx, EmitBufferAtomicSMax32(ctx, inst, handle, address, u32_value)));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Id EmitBufferAtomicInc32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
Id EmitBufferAtomicInc32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
||||||
return BufferAtomicU32IncDec(ctx, inst, handle, address, &Sirit::Module::OpAtomicIIncrement);
|
return BufferAtomicU32IncDec(ctx, inst, handle, address, &Sirit::Module::OpAtomicIIncrement);
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,8 +92,10 @@ Id EmitBufferAtomicIAdd64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre
|
||||||
Id EmitBufferAtomicISub32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
|
Id EmitBufferAtomicISub32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
|
||||||
Id EmitBufferAtomicSMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
|
Id EmitBufferAtomicSMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
|
||||||
Id EmitBufferAtomicUMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
|
Id EmitBufferAtomicUMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
|
||||||
|
Id EmitBufferAtomicFMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
|
||||||
Id EmitBufferAtomicSMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
|
Id EmitBufferAtomicSMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
|
||||||
Id EmitBufferAtomicUMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
|
Id EmitBufferAtomicUMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
|
||||||
|
Id EmitBufferAtomicFMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
|
||||||
Id EmitBufferAtomicInc32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
|
Id EmitBufferAtomicInc32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
|
||||||
Id EmitBufferAtomicDec32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
|
Id EmitBufferAtomicDec32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
|
||||||
Id EmitBufferAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
|
Id EmitBufferAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
|
||||||
|
|
|
@ -14,8 +14,7 @@ Id EmitLoadSharedU16(EmitContext& ctx, Id offset) {
|
||||||
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 2u)};
|
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 2u)};
|
||||||
|
|
||||||
return AccessBoundsCheck<16>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
return AccessBoundsCheck<16>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
||||||
const Id pointer =
|
const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u16, ctx.shared_memory_u16, index);
|
||||||
ctx.OpAccessChain(ctx.shared_u16, ctx.shared_memory_u16, ctx.u32_zero_value, index);
|
|
||||||
return ctx.OpLoad(ctx.U16, pointer);
|
return ctx.OpLoad(ctx.U16, pointer);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -26,8 +25,7 @@ Id EmitLoadSharedU32(EmitContext& ctx, Id offset) {
|
||||||
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 4u)};
|
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 4u)};
|
||||||
|
|
||||||
return AccessBoundsCheck<32>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
return AccessBoundsCheck<32>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
||||||
const Id pointer =
|
const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u32, ctx.shared_memory_u32, index);
|
||||||
ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, ctx.u32_zero_value, index);
|
|
||||||
return ctx.OpLoad(ctx.U32[1], pointer);
|
return ctx.OpLoad(ctx.U32[1], pointer);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -38,8 +36,7 @@ Id EmitLoadSharedU64(EmitContext& ctx, Id offset) {
|
||||||
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 8u)};
|
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 8u)};
|
||||||
|
|
||||||
return AccessBoundsCheck<64>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
return AccessBoundsCheck<64>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
||||||
const Id pointer{
|
const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u64, ctx.shared_memory_u64, index);
|
||||||
ctx.OpAccessChain(ctx.shared_u64, ctx.shared_memory_u64, ctx.u32_zero_value, index)};
|
|
||||||
return ctx.OpLoad(ctx.U64, pointer);
|
return ctx.OpLoad(ctx.U64, pointer);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -50,8 +47,7 @@ void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) {
|
||||||
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 2u)};
|
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 2u)};
|
||||||
|
|
||||||
AccessBoundsCheck<16>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
AccessBoundsCheck<16>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
||||||
const Id pointer =
|
const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u16, ctx.shared_memory_u16, index);
|
||||||
ctx.OpAccessChain(ctx.shared_u16, ctx.shared_memory_u16, ctx.u32_zero_value, index);
|
|
||||||
ctx.OpStore(pointer, value);
|
ctx.OpStore(pointer, value);
|
||||||
return Id{0};
|
return Id{0};
|
||||||
});
|
});
|
||||||
|
@ -63,8 +59,7 @@ void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) {
|
||||||
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 4u)};
|
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 4u)};
|
||||||
|
|
||||||
AccessBoundsCheck<32>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
AccessBoundsCheck<32>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
||||||
const Id pointer =
|
const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u32, ctx.shared_memory_u32, index);
|
||||||
ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, ctx.u32_zero_value, index);
|
|
||||||
ctx.OpStore(pointer, value);
|
ctx.OpStore(pointer, value);
|
||||||
return Id{0};
|
return Id{0};
|
||||||
});
|
});
|
||||||
|
@ -76,8 +71,7 @@ void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) {
|
||||||
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 8u)};
|
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 8u)};
|
||||||
|
|
||||||
AccessBoundsCheck<64>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
AccessBoundsCheck<64>(ctx, index, ctx.ConstU32(num_elements), [&] {
|
||||||
const Id pointer{
|
const Id pointer = ctx.EmitSharedMemoryAccess(ctx.shared_u64, ctx.shared_memory_u64, index);
|
||||||
ctx.OpAccessChain(ctx.shared_u64, ctx.shared_memory_u64, ctx.u32_zero_value, index)};
|
|
||||||
ctx.OpStore(pointer, value);
|
ctx.OpStore(pointer, value);
|
||||||
return Id{0};
|
return Id{0};
|
||||||
});
|
});
|
||||||
|
|
|
@ -972,7 +972,12 @@ void EmitContext::DefineImagesAndSamplers() {
|
||||||
const Id id{AddGlobalVariable(sampler_pointer_type, spv::StorageClass::UniformConstant)};
|
const Id id{AddGlobalVariable(sampler_pointer_type, spv::StorageClass::UniformConstant)};
|
||||||
Decorate(id, spv::Decoration::Binding, binding.unified++);
|
Decorate(id, spv::Decoration::Binding, binding.unified++);
|
||||||
Decorate(id, spv::Decoration::DescriptorSet, 0U);
|
Decorate(id, spv::Decoration::DescriptorSet, 0U);
|
||||||
Name(id, fmt::format("{}_{}{}", stage, "samp", samp_desc.sharp_idx));
|
auto sharp_desc = std::holds_alternative<u32>(samp_desc.sampler)
|
||||||
|
? fmt::format("sgpr:{}", std::get<u32>(samp_desc.sampler))
|
||||||
|
: fmt::format("inline:{:#x}:{:#x}",
|
||||||
|
std::get<AmdGpu::Sampler>(samp_desc.sampler).raw0,
|
||||||
|
std::get<AmdGpu::Sampler>(samp_desc.sampler).raw1);
|
||||||
|
Name(id, fmt::format("{}_{}{}", stage, "samp", sharp_desc));
|
||||||
samplers.push_back(id);
|
samplers.push_back(id);
|
||||||
interfaces.push_back(id);
|
interfaces.push_back(id);
|
||||||
}
|
}
|
||||||
|
@ -995,19 +1000,26 @@ void EmitContext::DefineSharedMemory() {
|
||||||
|
|
||||||
const u32 num_elements{Common::DivCeil(shared_memory_size, element_size)};
|
const u32 num_elements{Common::DivCeil(shared_memory_size, element_size)};
|
||||||
const Id array_type{TypeArray(element_type, ConstU32(num_elements))};
|
const Id array_type{TypeArray(element_type, ConstU32(num_elements))};
|
||||||
Decorate(array_type, spv::Decoration::ArrayStride, element_size);
|
|
||||||
|
|
||||||
const Id struct_type{TypeStruct(array_type)};
|
const auto mem_type = [&] {
|
||||||
MemberDecorate(struct_type, 0u, spv::Decoration::Offset, 0u);
|
if (num_types > 1) {
|
||||||
|
const Id struct_type{TypeStruct(array_type)};
|
||||||
|
Decorate(struct_type, spv::Decoration::Block);
|
||||||
|
MemberDecorate(struct_type, 0u, spv::Decoration::Offset, 0u);
|
||||||
|
return struct_type;
|
||||||
|
} else {
|
||||||
|
return array_type;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
const Id pointer = TypePointer(spv::StorageClass::Workgroup, struct_type);
|
const Id pointer = TypePointer(spv::StorageClass::Workgroup, mem_type);
|
||||||
const Id element_pointer = TypePointer(spv::StorageClass::Workgroup, element_type);
|
const Id element_pointer = TypePointer(spv::StorageClass::Workgroup, element_type);
|
||||||
const Id variable = AddGlobalVariable(pointer, spv::StorageClass::Workgroup);
|
const Id variable = AddGlobalVariable(pointer, spv::StorageClass::Workgroup);
|
||||||
Name(variable, name);
|
Name(variable, name);
|
||||||
interfaces.push_back(variable);
|
interfaces.push_back(variable);
|
||||||
|
|
||||||
if (num_types > 1) {
|
if (num_types > 1) {
|
||||||
Decorate(struct_type, spv::Decoration::Block);
|
Decorate(array_type, spv::Decoration::ArrayStride, element_size);
|
||||||
Decorate(variable, spv::Decoration::Aliased);
|
Decorate(variable, spv::Decoration::Aliased);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -203,6 +203,14 @@ public:
|
||||||
return final_result;
|
return final_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Id EmitSharedMemoryAccess(const Id result_type, const Id shared_mem, const Id index) {
|
||||||
|
if (std::popcount(static_cast<u32>(info.shared_types)) > 1) {
|
||||||
|
return OpAccessChain(result_type, shared_mem, u32_zero_value, index);
|
||||||
|
}
|
||||||
|
// Workgroup layout struct omitted.
|
||||||
|
return OpAccessChain(result_type, shared_mem, index);
|
||||||
|
}
|
||||||
|
|
||||||
Info& info;
|
Info& info;
|
||||||
const RuntimeInfo& runtime_info;
|
const RuntimeInfo& runtime_info;
|
||||||
const Profile& profile;
|
const Profile& profile;
|
||||||
|
|
|
@ -90,6 +90,10 @@ void Translator::EmitVectorMemory(const GcnInst& inst) {
|
||||||
return BUFFER_ATOMIC(AtomicOp::Inc, inst);
|
return BUFFER_ATOMIC(AtomicOp::Inc, inst);
|
||||||
case Opcode::BUFFER_ATOMIC_DEC:
|
case Opcode::BUFFER_ATOMIC_DEC:
|
||||||
return BUFFER_ATOMIC(AtomicOp::Dec, inst);
|
return BUFFER_ATOMIC(AtomicOp::Dec, inst);
|
||||||
|
case Opcode::BUFFER_ATOMIC_FMIN:
|
||||||
|
return BUFFER_ATOMIC(AtomicOp::Fmin, inst);
|
||||||
|
case Opcode::BUFFER_ATOMIC_FMAX:
|
||||||
|
return BUFFER_ATOMIC(AtomicOp::Fmax, inst);
|
||||||
|
|
||||||
// MIMG
|
// MIMG
|
||||||
// Image load operations
|
// Image load operations
|
||||||
|
@ -357,6 +361,10 @@ void Translator::BUFFER_ATOMIC(AtomicOp op, const GcnInst& inst) {
|
||||||
return ir.BufferAtomicInc(handle, address, buffer_info);
|
return ir.BufferAtomicInc(handle, address, buffer_info);
|
||||||
case AtomicOp::Dec:
|
case AtomicOp::Dec:
|
||||||
return ir.BufferAtomicDec(handle, address, buffer_info);
|
return ir.BufferAtomicDec(handle, address, buffer_info);
|
||||||
|
case AtomicOp::Fmin:
|
||||||
|
return ir.BufferAtomicFMin(handle, address, vdata_val, buffer_info);
|
||||||
|
case AtomicOp::Fmax:
|
||||||
|
return ir.BufferAtomicFMax(handle, address, vdata_val, buffer_info);
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
@ -531,8 +539,10 @@ IR::Value EmitImageSample(IR::IREmitter& ir, const GcnInst& inst, const IR::Scal
|
||||||
// Load first dword of T# and S#. We will use them as the handle that will guide resource
|
// Load first dword of T# and S#. We will use them as the handle that will guide resource
|
||||||
// tracking pass where to read the sharps. This will later also get patched to the SPIRV texture
|
// tracking pass where to read the sharps. This will later also get patched to the SPIRV texture
|
||||||
// binding index.
|
// binding index.
|
||||||
const IR::Value handle =
|
const IR::Value handle = ir.GetScalarReg(tsharp_reg);
|
||||||
ir.CompositeConstruct(ir.GetScalarReg(tsharp_reg), ir.GetScalarReg(sampler_reg));
|
const IR::Value inline_sampler =
|
||||||
|
ir.CompositeConstruct(ir.GetScalarReg(sampler_reg), ir.GetScalarReg(sampler_reg + 1),
|
||||||
|
ir.GetScalarReg(sampler_reg + 2), ir.GetScalarReg(sampler_reg + 3));
|
||||||
|
|
||||||
// Determine how many address registers need to be passed.
|
// Determine how many address registers need to be passed.
|
||||||
// The image type is unknown, so add all 4 possible base registers and resolve later.
|
// The image type is unknown, so add all 4 possible base registers and resolve later.
|
||||||
|
@ -568,7 +578,8 @@ IR::Value EmitImageSample(IR::IREmitter& ir, const GcnInst& inst, const IR::Scal
|
||||||
const IR::Value address4 = get_addr_reg(12);
|
const IR::Value address4 = get_addr_reg(12);
|
||||||
|
|
||||||
// Issue the placeholder IR instruction.
|
// Issue the placeholder IR instruction.
|
||||||
IR::Value texel = ir.ImageSampleRaw(handle, address1, address2, address3, address4, info);
|
IR::Value texel =
|
||||||
|
ir.ImageSampleRaw(handle, address1, address2, address3, address4, inline_sampler, info);
|
||||||
if (info.is_depth && !gather) {
|
if (info.is_depth && !gather) {
|
||||||
// For non-gather depth sampling, only return a single value.
|
// For non-gather depth sampling, only return a single value.
|
||||||
texel = ir.CompositeExtract(texel, 0);
|
texel = ir.CompositeExtract(texel, 0);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <boost/container/small_vector.hpp>
|
#include <boost/container/small_vector.hpp>
|
||||||
#include <boost/container/static_vector.hpp>
|
#include <boost/container/static_vector.hpp>
|
||||||
|
@ -91,11 +92,15 @@ struct ImageResource {
|
||||||
using ImageResourceList = boost::container::small_vector<ImageResource, NumImages>;
|
using ImageResourceList = boost::container::small_vector<ImageResource, NumImages>;
|
||||||
|
|
||||||
struct SamplerResource {
|
struct SamplerResource {
|
||||||
u32 sharp_idx;
|
std::variant<u32, AmdGpu::Sampler> sampler;
|
||||||
AmdGpu::Sampler inline_sampler{};
|
|
||||||
u32 associated_image : 4;
|
u32 associated_image : 4;
|
||||||
u32 disable_aniso : 1;
|
u32 disable_aniso : 1;
|
||||||
|
|
||||||
|
SamplerResource(u32 sharp_idx, u32 associated_image_, bool disable_aniso_)
|
||||||
|
: sampler{sharp_idx}, associated_image{associated_image_}, disable_aniso{disable_aniso_} {}
|
||||||
|
SamplerResource(AmdGpu::Sampler sampler_)
|
||||||
|
: sampler{sampler_}, associated_image{0}, disable_aniso(0) {}
|
||||||
|
|
||||||
constexpr AmdGpu::Sampler GetSharp(const Info& info) const noexcept;
|
constexpr AmdGpu::Sampler GetSharp(const Info& info) const noexcept;
|
||||||
};
|
};
|
||||||
using SamplerResourceList = boost::container::small_vector<SamplerResource, NumSamplers>;
|
using SamplerResourceList = boost::container::small_vector<SamplerResource, NumSamplers>;
|
||||||
|
@ -210,7 +215,8 @@ struct Info {
|
||||||
bool has_image_query{};
|
bool has_image_query{};
|
||||||
bool has_perspective_interp{};
|
bool has_perspective_interp{};
|
||||||
bool has_linear_interp{};
|
bool has_linear_interp{};
|
||||||
bool uses_atomic_float_min_max{};
|
bool uses_buffer_atomic_float_min_max{};
|
||||||
|
bool uses_image_atomic_float_min_max{};
|
||||||
bool uses_lane_id{};
|
bool uses_lane_id{};
|
||||||
bool uses_group_quad{};
|
bool uses_group_quad{};
|
||||||
bool uses_group_ballot{};
|
bool uses_group_ballot{};
|
||||||
|
@ -318,7 +324,9 @@ constexpr AmdGpu::Image ImageResource::GetSharp(const Info& info) const noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr AmdGpu::Sampler SamplerResource::GetSharp(const Info& info) const noexcept {
|
constexpr AmdGpu::Sampler SamplerResource::GetSharp(const Info& info) const noexcept {
|
||||||
return inline_sampler ? inline_sampler : info.ReadUdSharp<AmdGpu::Sampler>(sharp_idx);
|
return std::holds_alternative<AmdGpu::Sampler>(sampler)
|
||||||
|
? std::get<AmdGpu::Sampler>(sampler)
|
||||||
|
: info.ReadUdSharp<AmdGpu::Sampler>(std::get<u32>(sampler));
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr AmdGpu::Image FMaskResource::GetSharp(const Info& info) const noexcept {
|
constexpr AmdGpu::Image FMaskResource::GetSharp(const Info& info) const noexcept {
|
||||||
|
|
|
@ -504,12 +504,22 @@ Value IREmitter::BufferAtomicIMin(const Value& handle, const Value& address, con
|
||||||
: Inst(Opcode::BufferAtomicUMin32, Flags{info}, handle, address, value);
|
: Inst(Opcode::BufferAtomicUMin32, Flags{info}, handle, address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value IREmitter::BufferAtomicFMin(const Value& handle, const Value& address, const Value& value,
|
||||||
|
BufferInstInfo info) {
|
||||||
|
return Inst(Opcode::BufferAtomicFMin32, Flags{info}, handle, address, value);
|
||||||
|
}
|
||||||
|
|
||||||
Value IREmitter::BufferAtomicIMax(const Value& handle, const Value& address, const Value& value,
|
Value IREmitter::BufferAtomicIMax(const Value& handle, const Value& address, const Value& value,
|
||||||
bool is_signed, BufferInstInfo info) {
|
bool is_signed, BufferInstInfo info) {
|
||||||
return is_signed ? Inst(Opcode::BufferAtomicSMax32, Flags{info}, handle, address, value)
|
return is_signed ? Inst(Opcode::BufferAtomicSMax32, Flags{info}, handle, address, value)
|
||||||
: Inst(Opcode::BufferAtomicUMax32, Flags{info}, handle, address, value);
|
: Inst(Opcode::BufferAtomicUMax32, Flags{info}, handle, address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value IREmitter::BufferAtomicFMax(const Value& handle, const Value& address, const Value& value,
|
||||||
|
BufferInstInfo info) {
|
||||||
|
return Inst(Opcode::BufferAtomicFMax32, Flags{info}, handle, address, value);
|
||||||
|
}
|
||||||
|
|
||||||
Value IREmitter::BufferAtomicInc(const Value& handle, const Value& address, BufferInstInfo info) {
|
Value IREmitter::BufferAtomicInc(const Value& handle, const Value& address, BufferInstInfo info) {
|
||||||
return Inst(Opcode::BufferAtomicInc32, Flags{info}, handle, address);
|
return Inst(Opcode::BufferAtomicInc32, Flags{info}, handle, address);
|
||||||
}
|
}
|
||||||
|
@ -1964,9 +1974,9 @@ Value IREmitter::ImageAtomicExchange(const Value& handle, const Value& coords, c
|
||||||
|
|
||||||
Value IREmitter::ImageSampleRaw(const Value& handle, const Value& address1, const Value& address2,
|
Value IREmitter::ImageSampleRaw(const Value& handle, const Value& address1, const Value& address2,
|
||||||
const Value& address3, const Value& address4,
|
const Value& address3, const Value& address4,
|
||||||
TextureInstInfo info) {
|
const Value& inline_sampler, TextureInstInfo info) {
|
||||||
return Inst(Opcode::ImageSampleRaw, Flags{info}, handle, address1, address2, address3,
|
return Inst(Opcode::ImageSampleRaw, Flags{info}, handle, address1, address2, address3, address4,
|
||||||
address4);
|
inline_sampler);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value IREmitter::ImageSampleImplicitLod(const Value& handle, const Value& coords, const F32& bias,
|
Value IREmitter::ImageSampleImplicitLod(const Value& handle, const Value& coords, const F32& bias,
|
||||||
|
|
|
@ -140,8 +140,12 @@ public:
|
||||||
const Value& value, BufferInstInfo info);
|
const Value& value, BufferInstInfo info);
|
||||||
[[nodiscard]] Value BufferAtomicIMin(const Value& handle, const Value& address,
|
[[nodiscard]] Value BufferAtomicIMin(const Value& handle, const Value& address,
|
||||||
const Value& value, bool is_signed, BufferInstInfo info);
|
const Value& value, bool is_signed, BufferInstInfo info);
|
||||||
|
[[nodiscard]] Value BufferAtomicFMin(const Value& handle, const Value& address,
|
||||||
|
const Value& value, BufferInstInfo info);
|
||||||
[[nodiscard]] Value BufferAtomicIMax(const Value& handle, const Value& address,
|
[[nodiscard]] Value BufferAtomicIMax(const Value& handle, const Value& address,
|
||||||
const Value& value, bool is_signed, BufferInstInfo info);
|
const Value& value, bool is_signed, BufferInstInfo info);
|
||||||
|
[[nodiscard]] Value BufferAtomicFMax(const Value& handle, const Value& address,
|
||||||
|
const Value& value, BufferInstInfo info);
|
||||||
[[nodiscard]] Value BufferAtomicInc(const Value& handle, const Value& address,
|
[[nodiscard]] Value BufferAtomicInc(const Value& handle, const Value& address,
|
||||||
BufferInstInfo info);
|
BufferInstInfo info);
|
||||||
[[nodiscard]] Value BufferAtomicDec(const Value& handle, const Value& address,
|
[[nodiscard]] Value BufferAtomicDec(const Value& handle, const Value& address,
|
||||||
|
@ -349,7 +353,8 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] Value ImageSampleRaw(const Value& handle, const Value& address1,
|
[[nodiscard]] Value ImageSampleRaw(const Value& handle, const Value& address1,
|
||||||
const Value& address2, const Value& address3,
|
const Value& address2, const Value& address3,
|
||||||
const Value& address4, TextureInstInfo info);
|
const Value& address4, const Value& inline_sampler,
|
||||||
|
TextureInstInfo info);
|
||||||
|
|
||||||
[[nodiscard]] Value ImageSampleImplicitLod(const Value& handle, const Value& body,
|
[[nodiscard]] Value ImageSampleImplicitLod(const Value& handle, const Value& body,
|
||||||
const F32& bias, const Value& offset,
|
const F32& bias, const Value& offset,
|
||||||
|
|
|
@ -71,8 +71,10 @@ bool Inst::MayHaveSideEffects() const noexcept {
|
||||||
case Opcode::BufferAtomicISub32:
|
case Opcode::BufferAtomicISub32:
|
||||||
case Opcode::BufferAtomicSMin32:
|
case Opcode::BufferAtomicSMin32:
|
||||||
case Opcode::BufferAtomicUMin32:
|
case Opcode::BufferAtomicUMin32:
|
||||||
|
case Opcode::BufferAtomicFMin32:
|
||||||
case Opcode::BufferAtomicSMax32:
|
case Opcode::BufferAtomicSMax32:
|
||||||
case Opcode::BufferAtomicUMax32:
|
case Opcode::BufferAtomicUMax32:
|
||||||
|
case Opcode::BufferAtomicFMax32:
|
||||||
case Opcode::BufferAtomicInc32:
|
case Opcode::BufferAtomicInc32:
|
||||||
case Opcode::BufferAtomicDec32:
|
case Opcode::BufferAtomicDec32:
|
||||||
case Opcode::BufferAtomicAnd32:
|
case Opcode::BufferAtomicAnd32:
|
||||||
|
|
|
@ -125,8 +125,10 @@ OPCODE(BufferAtomicIAdd64, U64, Opaq
|
||||||
OPCODE(BufferAtomicISub32, U32, Opaque, Opaque, U32 )
|
OPCODE(BufferAtomicISub32, U32, Opaque, Opaque, U32 )
|
||||||
OPCODE(BufferAtomicSMin32, U32, Opaque, Opaque, U32 )
|
OPCODE(BufferAtomicSMin32, U32, Opaque, Opaque, U32 )
|
||||||
OPCODE(BufferAtomicUMin32, U32, Opaque, Opaque, U32 )
|
OPCODE(BufferAtomicUMin32, U32, Opaque, Opaque, U32 )
|
||||||
|
OPCODE(BufferAtomicFMin32, U32, Opaque, Opaque, F32 )
|
||||||
OPCODE(BufferAtomicSMax32, U32, Opaque, Opaque, U32 )
|
OPCODE(BufferAtomicSMax32, U32, Opaque, Opaque, U32 )
|
||||||
OPCODE(BufferAtomicUMax32, U32, Opaque, Opaque, U32 )
|
OPCODE(BufferAtomicUMax32, U32, Opaque, Opaque, U32 )
|
||||||
|
OPCODE(BufferAtomicFMax32, U32, Opaque, Opaque, F32 )
|
||||||
OPCODE(BufferAtomicInc32, U32, Opaque, Opaque, )
|
OPCODE(BufferAtomicInc32, U32, Opaque, Opaque, )
|
||||||
OPCODE(BufferAtomicDec32, U32, Opaque, Opaque, )
|
OPCODE(BufferAtomicDec32, U32, Opaque, Opaque, )
|
||||||
OPCODE(BufferAtomicAnd32, U32, Opaque, Opaque, U32, )
|
OPCODE(BufferAtomicAnd32, U32, Opaque, Opaque, U32, )
|
||||||
|
@ -412,7 +414,7 @@ OPCODE(ConvertU8U32, U8, U32,
|
||||||
OPCODE(ConvertU32U8, U32, U8, )
|
OPCODE(ConvertU32U8, U32, U8, )
|
||||||
|
|
||||||
// Image operations
|
// Image operations
|
||||||
OPCODE(ImageSampleRaw, F32x4, Opaque, F32x4, F32x4, F32x4, F32, )
|
OPCODE(ImageSampleRaw, F32x4, Opaque, F32x4, F32x4, F32x4, F32, Opaque, )
|
||||||
OPCODE(ImageSampleImplicitLod, F32x4, Opaque, F32x4, F32, Opaque, )
|
OPCODE(ImageSampleImplicitLod, F32x4, Opaque, F32x4, F32, Opaque, )
|
||||||
OPCODE(ImageSampleExplicitLod, F32x4, Opaque, Opaque, F32, Opaque, )
|
OPCODE(ImageSampleExplicitLod, F32x4, Opaque, Opaque, F32, Opaque, )
|
||||||
OPCODE(ImageSampleDrefImplicitLod, F32x4, Opaque, Opaque, F32, F32, Opaque, )
|
OPCODE(ImageSampleDrefImplicitLod, F32x4, Opaque, Opaque, F32, F32, Opaque, )
|
||||||
|
|
|
@ -21,8 +21,10 @@ bool IsBufferAtomic(const IR::Inst& inst) {
|
||||||
case IR::Opcode::BufferAtomicISub32:
|
case IR::Opcode::BufferAtomicISub32:
|
||||||
case IR::Opcode::BufferAtomicSMin32:
|
case IR::Opcode::BufferAtomicSMin32:
|
||||||
case IR::Opcode::BufferAtomicUMin32:
|
case IR::Opcode::BufferAtomicUMin32:
|
||||||
|
case IR::Opcode::BufferAtomicFMin32:
|
||||||
case IR::Opcode::BufferAtomicSMax32:
|
case IR::Opcode::BufferAtomicSMax32:
|
||||||
case IR::Opcode::BufferAtomicUMax32:
|
case IR::Opcode::BufferAtomicUMax32:
|
||||||
|
case IR::Opcode::BufferAtomicFMax32:
|
||||||
case IR::Opcode::BufferAtomicInc32:
|
case IR::Opcode::BufferAtomicInc32:
|
||||||
case IR::Opcode::BufferAtomicDec32:
|
case IR::Opcode::BufferAtomicDec32:
|
||||||
case IR::Opcode::BufferAtomicAnd32:
|
case IR::Opcode::BufferAtomicAnd32:
|
||||||
|
@ -168,7 +170,7 @@ public:
|
||||||
|
|
||||||
u32 Add(const SamplerResource& desc) {
|
u32 Add(const SamplerResource& desc) {
|
||||||
const u32 index{Add(sampler_resources, desc, [this, &desc](const auto& existing) {
|
const u32 index{Add(sampler_resources, desc, [this, &desc](const auto& existing) {
|
||||||
return desc.sharp_idx == existing.sharp_idx;
|
return desc.sampler == existing.sampler;
|
||||||
})};
|
})};
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
@ -351,8 +353,7 @@ void PatchBufferSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors&
|
||||||
void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& descriptors) {
|
void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& descriptors) {
|
||||||
const auto pred = [](const IR::Inst* inst) -> std::optional<const IR::Inst*> {
|
const auto pred = [](const IR::Inst* inst) -> std::optional<const IR::Inst*> {
|
||||||
const auto opcode = inst->GetOpcode();
|
const auto opcode = inst->GetOpcode();
|
||||||
if (opcode == IR::Opcode::CompositeConstructU32x2 || // IMAGE_SAMPLE (image+sampler)
|
if (opcode == IR::Opcode::ReadConst || // IMAGE_LOAD (image only)
|
||||||
opcode == IR::Opcode::ReadConst || // IMAGE_LOAD (image only)
|
|
||||||
opcode == IR::Opcode::GetUserData) {
|
opcode == IR::Opcode::GetUserData) {
|
||||||
return inst;
|
return inst;
|
||||||
}
|
}
|
||||||
|
@ -360,9 +361,7 @@ void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors&
|
||||||
};
|
};
|
||||||
const auto result = IR::BreadthFirstSearch(&inst, pred);
|
const auto result = IR::BreadthFirstSearch(&inst, pred);
|
||||||
ASSERT_MSG(result, "Unable to find image sharp source");
|
ASSERT_MSG(result, "Unable to find image sharp source");
|
||||||
const IR::Inst* producer = result.value();
|
const IR::Inst* tsharp_handle = result.value();
|
||||||
const bool has_sampler = producer->GetOpcode() == IR::Opcode::CompositeConstructU32x2;
|
|
||||||
const auto tsharp_handle = has_sampler ? producer->Arg(0).InstRecursive() : producer;
|
|
||||||
|
|
||||||
// Read image sharp.
|
// Read image sharp.
|
||||||
const auto tsharp = TrackSharp(tsharp_handle, info);
|
const auto tsharp = TrackSharp(tsharp_handle, info);
|
||||||
|
@ -427,29 +426,32 @@ void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors&
|
||||||
|
|
||||||
if (inst.GetOpcode() == IR::Opcode::ImageSampleRaw) {
|
if (inst.GetOpcode() == IR::Opcode::ImageSampleRaw) {
|
||||||
// Read sampler sharp.
|
// Read sampler sharp.
|
||||||
const auto [sampler_binding, sampler] = [&] -> std::pair<u32, AmdGpu::Sampler> {
|
const auto sampler_binding = [&] -> u32 {
|
||||||
ASSERT(producer->GetOpcode() == IR::Opcode::CompositeConstructU32x2);
|
const auto sampler = inst.Arg(5).InstRecursive();
|
||||||
const IR::Value& handle = producer->Arg(1);
|
ASSERT(sampler && sampler->GetOpcode() == IR::Opcode::CompositeConstructU32x4);
|
||||||
|
const auto handle = sampler->Arg(0);
|
||||||
// Inline sampler resource.
|
// Inline sampler resource.
|
||||||
if (handle.IsImmediate()) {
|
if (handle.IsImmediate()) {
|
||||||
LOG_WARNING(Render_Vulkan, "Inline sampler detected");
|
LOG_DEBUG(Render_Vulkan, "Inline sampler detected");
|
||||||
const auto inline_sampler = AmdGpu::Sampler{.raw0 = handle.U32()};
|
const auto [s1, s2, s3, s4] =
|
||||||
const auto binding = descriptors.Add(SamplerResource{
|
std::tuple{sampler->Arg(0), sampler->Arg(1), sampler->Arg(2), sampler->Arg(3)};
|
||||||
.sharp_idx = std::numeric_limits<u32>::max(),
|
ASSERT(s1.IsImmediate() && s2.IsImmediate() && s3.IsImmediate() &&
|
||||||
.inline_sampler = inline_sampler,
|
s4.IsImmediate());
|
||||||
});
|
const auto inline_sampler = AmdGpu::Sampler{
|
||||||
return {binding, inline_sampler};
|
.raw0 = u64(s2.U32()) << 32 | u64(s1.U32()),
|
||||||
|
.raw1 = u64(s4.U32()) << 32 | u64(s3.U32()),
|
||||||
|
};
|
||||||
|
const auto binding = descriptors.Add(SamplerResource{inline_sampler});
|
||||||
|
return binding;
|
||||||
|
} else {
|
||||||
|
// Normal sampler resource.
|
||||||
|
const auto ssharp_handle = handle.InstRecursive();
|
||||||
|
const auto& [ssharp_ud, disable_aniso] = TryDisableAnisoLod0(ssharp_handle);
|
||||||
|
const auto ssharp = TrackSharp(ssharp_ud, info);
|
||||||
|
const auto binding =
|
||||||
|
descriptors.Add(SamplerResource{ssharp, image_binding, disable_aniso});
|
||||||
|
return binding;
|
||||||
}
|
}
|
||||||
// Normal sampler resource.
|
|
||||||
const auto ssharp_handle = handle.InstRecursive();
|
|
||||||
const auto& [ssharp_ud, disable_aniso] = TryDisableAnisoLod0(ssharp_handle);
|
|
||||||
const auto ssharp = TrackSharp(ssharp_ud, info);
|
|
||||||
const auto binding = descriptors.Add(SamplerResource{
|
|
||||||
.sharp_idx = ssharp,
|
|
||||||
.associated_image = image_binding,
|
|
||||||
.disable_aniso = disable_aniso,
|
|
||||||
});
|
|
||||||
return {binding, info.ReadUdSharp<AmdGpu::Sampler>(ssharp)};
|
|
||||||
}();
|
}();
|
||||||
// Patch image and sampler handle.
|
// Patch image and sampler handle.
|
||||||
inst.SetArg(0, ir.Imm32(image_binding | sampler_binding << 16));
|
inst.SetArg(0, ir.Imm32(image_binding | sampler_binding << 16));
|
||||||
|
|
|
@ -92,7 +92,11 @@ void Visit(Info& info, const IR::Inst& inst) {
|
||||||
break;
|
break;
|
||||||
case IR::Opcode::ImageAtomicFMax32:
|
case IR::Opcode::ImageAtomicFMax32:
|
||||||
case IR::Opcode::ImageAtomicFMin32:
|
case IR::Opcode::ImageAtomicFMin32:
|
||||||
info.uses_atomic_float_min_max = true;
|
info.uses_image_atomic_float_min_max = true;
|
||||||
|
break;
|
||||||
|
case IR::Opcode::BufferAtomicFMax32:
|
||||||
|
case IR::Opcode::BufferAtomicFMin32:
|
||||||
|
info.uses_buffer_atomic_float_min_max = true;
|
||||||
break;
|
break;
|
||||||
case IR::Opcode::LaneId:
|
case IR::Opcode::LaneId:
|
||||||
info.uses_lane_id = true;
|
info.uses_lane_id = true;
|
||||||
|
|
|
@ -28,6 +28,7 @@ struct Profile {
|
||||||
bool supports_native_cube_calc{};
|
bool supports_native_cube_calc{};
|
||||||
bool supports_trinary_minmax{};
|
bool supports_trinary_minmax{};
|
||||||
bool supports_robust_buffer_access{};
|
bool supports_robust_buffer_access{};
|
||||||
|
bool supports_buffer_fp32_atomic_min_max{};
|
||||||
bool supports_image_fp32_atomic_min_max{};
|
bool supports_image_fp32_atomic_min_max{};
|
||||||
bool supports_workgroup_explicit_memory_layout{};
|
bool supports_workgroup_explicit_memory_layout{};
|
||||||
bool has_broken_spirv_clamp{};
|
bool has_broken_spirv_clamp{};
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include "common/slot_vector.h"
|
#include "common/slot_vector.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "video_core/buffer_cache/buffer.h"
|
#include "video_core/buffer_cache/buffer.h"
|
||||||
#include "video_core/buffer_cache/memory_tracker_base.h"
|
#include "video_core/buffer_cache/memory_tracker.h"
|
||||||
#include "video_core/buffer_cache/range_set.h"
|
#include "video_core/buffer_cache/range_set.h"
|
||||||
#include "video_core/multi_level_page_table.h"
|
#include "video_core/multi_level_page_table.h"
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "video_core/buffer_cache/word_manager.h"
|
#include "video_core/buffer_cache/region_manager.h"
|
||||||
|
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
|
|
28
src/video_core/buffer_cache/region_definitions.h
Normal file
28
src/video_core/buffer_cache/region_definitions.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include "common/bit_array.h"
|
||||||
|
#include "common/types.h"
|
||||||
|
|
||||||
|
namespace VideoCore {
|
||||||
|
|
||||||
|
constexpr u64 PAGES_PER_WORD = 64;
|
||||||
|
constexpr u64 BYTES_PER_PAGE = 4_KB;
|
||||||
|
|
||||||
|
constexpr u64 HIGHER_PAGE_BITS = 22;
|
||||||
|
constexpr u64 HIGHER_PAGE_SIZE = 1ULL << HIGHER_PAGE_BITS;
|
||||||
|
constexpr u64 HIGHER_PAGE_MASK = HIGHER_PAGE_SIZE - 1ULL;
|
||||||
|
constexpr u64 NUM_REGION_PAGES = HIGHER_PAGE_SIZE / BYTES_PER_PAGE;
|
||||||
|
|
||||||
|
enum class Type {
|
||||||
|
CPU,
|
||||||
|
GPU,
|
||||||
|
Writeable,
|
||||||
|
};
|
||||||
|
|
||||||
|
using RegionBits = Common::BitArray<NUM_REGION_PAGES>;
|
||||||
|
|
||||||
|
} // namespace VideoCore
|
208
src/video_core/buffer_cache/region_manager.h
Normal file
208
src/video_core/buffer_cache/region_manager.h
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <utility>
|
||||||
|
#include "common/div_ceil.h"
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include "common/adaptive_mutex.h"
|
||||||
|
#else
|
||||||
|
#include "common/spin_lock.h"
|
||||||
|
#endif
|
||||||
|
#include "common/debug.h"
|
||||||
|
#include "common/types.h"
|
||||||
|
#include "video_core/buffer_cache/region_definitions.h"
|
||||||
|
#include "video_core/page_manager.h"
|
||||||
|
|
||||||
|
namespace VideoCore {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows tracking CPU and GPU modification of pages in a contigious 4MB virtual address region.
|
||||||
|
* Information is stored in bitsets for spacial locality and fast update of single pages.
|
||||||
|
*/
|
||||||
|
class RegionManager {
|
||||||
|
public:
|
||||||
|
explicit RegionManager(PageManager* tracker_, VAddr cpu_addr_)
|
||||||
|
: tracker{tracker_}, cpu_addr{cpu_addr_} {
|
||||||
|
cpu.Fill();
|
||||||
|
gpu.Clear();
|
||||||
|
writeable.Fill();
|
||||||
|
}
|
||||||
|
explicit RegionManager() = default;
|
||||||
|
|
||||||
|
void SetCpuAddress(VAddr new_cpu_addr) {
|
||||||
|
cpu_addr = new_cpu_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VAddr GetCpuAddr() const {
|
||||||
|
return cpu_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr size_t SanitizeAddress(size_t address) {
|
||||||
|
return static_cast<size_t>(std::max<s64>(static_cast<s64>(address), 0LL));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Type type>
|
||||||
|
RegionBits& GetRegionBits() noexcept {
|
||||||
|
static_assert(type != Type::Writeable);
|
||||||
|
if constexpr (type == Type::CPU) {
|
||||||
|
return cpu;
|
||||||
|
} else if constexpr (type == Type::GPU) {
|
||||||
|
return gpu;
|
||||||
|
} else if constexpr (type == Type::Writeable) {
|
||||||
|
return writeable;
|
||||||
|
} else {
|
||||||
|
static_assert(false, "Invalid type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Type type>
|
||||||
|
const RegionBits& GetRegionBits() const noexcept {
|
||||||
|
static_assert(type != Type::Writeable);
|
||||||
|
if constexpr (type == Type::CPU) {
|
||||||
|
return cpu;
|
||||||
|
} else if constexpr (type == Type::GPU) {
|
||||||
|
return gpu;
|
||||||
|
} else if constexpr (type == Type::Writeable) {
|
||||||
|
return writeable;
|
||||||
|
} else {
|
||||||
|
static_assert(false, "Invalid type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the state of a range of pages
|
||||||
|
*
|
||||||
|
* @param dirty_addr Base address to mark or unmark as modified
|
||||||
|
* @param size Size in bytes to mark or unmark as modified
|
||||||
|
*/
|
||||||
|
template <Type type, bool enable>
|
||||||
|
void ChangeRegionState(u64 dirty_addr, u64 size) noexcept(type == Type::GPU) {
|
||||||
|
RENDERER_TRACE;
|
||||||
|
const size_t offset = dirty_addr - cpu_addr;
|
||||||
|
const size_t start_page = SanitizeAddress(offset) / BYTES_PER_PAGE;
|
||||||
|
const size_t end_page = Common::DivCeil(SanitizeAddress(offset + size), BYTES_PER_PAGE);
|
||||||
|
if (start_page >= NUM_REGION_PAGES || end_page <= start_page) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::scoped_lock lk{lock};
|
||||||
|
static_assert(type != Type::Writeable);
|
||||||
|
|
||||||
|
RegionBits& bits = GetRegionBits<type>();
|
||||||
|
if constexpr (enable) {
|
||||||
|
bits.SetRange(start_page, end_page);
|
||||||
|
} else {
|
||||||
|
bits.UnsetRange(start_page, end_page);
|
||||||
|
}
|
||||||
|
if constexpr (type == Type::CPU) {
|
||||||
|
UpdateProtection<!enable>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loop over each page in the given range, turn off those bits and notify the tracker if
|
||||||
|
* needed. Call the given function on each turned off range.
|
||||||
|
*
|
||||||
|
* @param query_cpu_range Base CPU address to loop over
|
||||||
|
* @param size Size in bytes of the CPU range to loop over
|
||||||
|
* @param func Function to call for each turned off region
|
||||||
|
*/
|
||||||
|
template <Type type, bool clear>
|
||||||
|
void ForEachModifiedRange(VAddr query_cpu_range, s64 size, auto&& func) {
|
||||||
|
RENDERER_TRACE;
|
||||||
|
const size_t offset = query_cpu_range - cpu_addr;
|
||||||
|
const size_t start_page = SanitizeAddress(offset) / BYTES_PER_PAGE;
|
||||||
|
const size_t end_page = Common::DivCeil(SanitizeAddress(offset + size), BYTES_PER_PAGE);
|
||||||
|
if (start_page >= NUM_REGION_PAGES || end_page <= start_page) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::scoped_lock lk{lock};
|
||||||
|
static_assert(type != Type::Writeable);
|
||||||
|
|
||||||
|
RegionBits& bits = GetRegionBits<type>();
|
||||||
|
RegionBits mask(bits, start_page, end_page);
|
||||||
|
|
||||||
|
// TODO: this will not be needed once we handle readbacks
|
||||||
|
if constexpr (type == Type::GPU) {
|
||||||
|
mask &= ~writeable;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& [start, end] : mask) {
|
||||||
|
func(cpu_addr + start * BYTES_PER_PAGE, (end - start) * BYTES_PER_PAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (clear) {
|
||||||
|
bits.UnsetRange(start_page, end_page);
|
||||||
|
if constexpr (type == Type::CPU) {
|
||||||
|
UpdateProtection<true>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when a region has been modified
|
||||||
|
*
|
||||||
|
* @param offset Offset in bytes from the start of the buffer
|
||||||
|
* @param size Size in bytes of the region to query for modifications
|
||||||
|
*/
|
||||||
|
template <Type type>
|
||||||
|
[[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept {
|
||||||
|
RENDERER_TRACE;
|
||||||
|
const size_t start_page = SanitizeAddress(offset) / BYTES_PER_PAGE;
|
||||||
|
const size_t end_page = Common::DivCeil(SanitizeAddress(offset + size), BYTES_PER_PAGE);
|
||||||
|
if (start_page >= NUM_REGION_PAGES || end_page <= start_page) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// std::scoped_lock lk{lock}; // Is this needed?
|
||||||
|
static_assert(type != Type::Writeable);
|
||||||
|
|
||||||
|
const RegionBits& bits = GetRegionBits<type>();
|
||||||
|
RegionBits test(bits, start_page, end_page);
|
||||||
|
|
||||||
|
// TODO: this will not be needed once we handle readbacks
|
||||||
|
if constexpr (type == Type::GPU) {
|
||||||
|
test &= ~writeable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return test.Any();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Notify tracker about changes in the CPU tracking state of a word in the buffer
|
||||||
|
*
|
||||||
|
* @param word_index Index to the word to notify to the tracker
|
||||||
|
* @param current_bits Current state of the word
|
||||||
|
* @param new_bits New state of the word
|
||||||
|
*
|
||||||
|
* @tparam add_to_tracker True when the tracker should start tracking the new pages
|
||||||
|
*/
|
||||||
|
template <bool add_to_tracker>
|
||||||
|
void UpdateProtection() {
|
||||||
|
RENDERER_TRACE;
|
||||||
|
RegionBits mask = cpu ^ writeable;
|
||||||
|
|
||||||
|
if (mask.None()) {
|
||||||
|
return; // No changes to the CPU tracking state
|
||||||
|
}
|
||||||
|
|
||||||
|
writeable = cpu;
|
||||||
|
tracker->UpdatePageWatchersForRegion<add_to_tracker>(cpu_addr, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
|
||||||
|
Common::AdaptiveMutex lock;
|
||||||
|
#else
|
||||||
|
Common::SpinLock lock;
|
||||||
|
#endif
|
||||||
|
PageManager* tracker;
|
||||||
|
VAddr cpu_addr = 0;
|
||||||
|
RegionBits cpu;
|
||||||
|
RegionBits gpu;
|
||||||
|
RegionBits writeable;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace VideoCore
|
|
@ -1,296 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <mutex>
|
|
||||||
#include <span>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
#include "common/adaptive_mutex.h"
|
|
||||||
#else
|
|
||||||
#include "common/spin_lock.h"
|
|
||||||
#endif
|
|
||||||
#include "common/debug.h"
|
|
||||||
#include "common/types.h"
|
|
||||||
#include "video_core/page_manager.h"
|
|
||||||
|
|
||||||
namespace VideoCore {
|
|
||||||
|
|
||||||
constexpr u64 PAGES_PER_WORD = 64;
|
|
||||||
constexpr u64 BYTES_PER_PAGE = 4_KB;
|
|
||||||
constexpr u64 BYTES_PER_WORD = PAGES_PER_WORD * BYTES_PER_PAGE;
|
|
||||||
|
|
||||||
constexpr u64 HIGHER_PAGE_BITS = 22;
|
|
||||||
constexpr u64 HIGHER_PAGE_SIZE = 1ULL << HIGHER_PAGE_BITS;
|
|
||||||
constexpr u64 HIGHER_PAGE_MASK = HIGHER_PAGE_SIZE - 1ULL;
|
|
||||||
constexpr u64 NUM_REGION_WORDS = HIGHER_PAGE_SIZE / BYTES_PER_WORD;
|
|
||||||
|
|
||||||
enum class Type {
|
|
||||||
CPU,
|
|
||||||
GPU,
|
|
||||||
Untracked,
|
|
||||||
};
|
|
||||||
|
|
||||||
using WordsArray = std::array<u64, NUM_REGION_WORDS>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows tracking CPU and GPU modification of pages in a contigious 4MB virtual address region.
|
|
||||||
* Information is stored in bitsets for spacial locality and fast update of single pages.
|
|
||||||
*/
|
|
||||||
class RegionManager {
|
|
||||||
public:
|
|
||||||
explicit RegionManager(PageManager* tracker_, VAddr cpu_addr_)
|
|
||||||
: tracker{tracker_}, cpu_addr{cpu_addr_} {
|
|
||||||
cpu.fill(~u64{0});
|
|
||||||
gpu.fill(0);
|
|
||||||
untracked.fill(~u64{0});
|
|
||||||
}
|
|
||||||
explicit RegionManager() = default;
|
|
||||||
|
|
||||||
void SetCpuAddress(VAddr new_cpu_addr) {
|
|
||||||
cpu_addr = new_cpu_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
VAddr GetCpuAddr() const {
|
|
||||||
return cpu_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr u64 ExtractBits(u64 word, size_t page_start, size_t page_end) {
|
|
||||||
constexpr size_t number_bits = sizeof(u64) * 8;
|
|
||||||
const size_t limit_page_end = number_bits - std::min(page_end, number_bits);
|
|
||||||
u64 bits = (word >> page_start) << page_start;
|
|
||||||
bits = (bits << limit_page_end) >> limit_page_end;
|
|
||||||
return bits;
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr std::pair<size_t, size_t> GetWordPage(VAddr address) {
|
|
||||||
const size_t converted_address = static_cast<size_t>(address);
|
|
||||||
const size_t word_number = converted_address / BYTES_PER_WORD;
|
|
||||||
const size_t amount_pages = converted_address % BYTES_PER_WORD;
|
|
||||||
return std::make_pair(word_number, amount_pages / BYTES_PER_PAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Func>
|
|
||||||
void IterateWords(size_t offset, size_t size, Func&& func) const {
|
|
||||||
RENDERER_TRACE;
|
|
||||||
using FuncReturn = std::invoke_result_t<Func, std::size_t, u64>;
|
|
||||||
static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>;
|
|
||||||
const size_t start = static_cast<size_t>(std::max<s64>(static_cast<s64>(offset), 0LL));
|
|
||||||
const size_t end = static_cast<size_t>(std::max<s64>(static_cast<s64>(offset + size), 0LL));
|
|
||||||
if (start >= HIGHER_PAGE_SIZE || end <= start) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto [start_word, start_page] = GetWordPage(start);
|
|
||||||
auto [end_word, end_page] = GetWordPage(end + BYTES_PER_PAGE - 1ULL);
|
|
||||||
constexpr size_t num_words = NUM_REGION_WORDS;
|
|
||||||
start_word = std::min(start_word, num_words);
|
|
||||||
end_word = std::min(end_word, num_words);
|
|
||||||
const size_t diff = end_word - start_word;
|
|
||||||
end_word += (end_page + PAGES_PER_WORD - 1ULL) / PAGES_PER_WORD;
|
|
||||||
end_word = std::min(end_word, num_words);
|
|
||||||
end_page += diff * PAGES_PER_WORD;
|
|
||||||
constexpr u64 base_mask{~0ULL};
|
|
||||||
for (size_t word_index = start_word; word_index < end_word; word_index++) {
|
|
||||||
const u64 mask = ExtractBits(base_mask, start_page, end_page);
|
|
||||||
start_page = 0;
|
|
||||||
end_page -= PAGES_PER_WORD;
|
|
||||||
if constexpr (BOOL_BREAK) {
|
|
||||||
if (func(word_index, mask)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
func(word_index, mask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void IteratePages(u64 mask, auto&& func) const {
|
|
||||||
RENDERER_TRACE;
|
|
||||||
size_t offset = 0;
|
|
||||||
while (mask != 0) {
|
|
||||||
const size_t empty_bits = std::countr_zero(mask);
|
|
||||||
offset += empty_bits;
|
|
||||||
mask >>= empty_bits;
|
|
||||||
|
|
||||||
const size_t continuous_bits = std::countr_one(mask);
|
|
||||||
func(offset, continuous_bits);
|
|
||||||
mask = continuous_bits < PAGES_PER_WORD ? (mask >> continuous_bits) : 0;
|
|
||||||
offset += continuous_bits;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change the state of a range of pages
|
|
||||||
*
|
|
||||||
* @param dirty_addr Base address to mark or unmark as modified
|
|
||||||
* @param size Size in bytes to mark or unmark as modified
|
|
||||||
*/
|
|
||||||
template <Type type, bool enable>
|
|
||||||
void ChangeRegionState(u64 dirty_addr, u64 size) noexcept(type == Type::GPU) {
|
|
||||||
std::scoped_lock lk{lock};
|
|
||||||
std::span<u64> state_words = Span<type>();
|
|
||||||
IterateWords(dirty_addr - cpu_addr, size, [&](size_t index, u64 mask) {
|
|
||||||
if constexpr (type == Type::CPU) {
|
|
||||||
UpdateProtection<!enable>(index, untracked[index], mask);
|
|
||||||
}
|
|
||||||
if constexpr (enable) {
|
|
||||||
state_words[index] |= mask;
|
|
||||||
if constexpr (type == Type::CPU) {
|
|
||||||
untracked[index] |= mask;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state_words[index] &= ~mask;
|
|
||||||
if constexpr (type == Type::CPU) {
|
|
||||||
untracked[index] &= ~mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loop over each page in the given range, turn off those bits and notify the tracker if
|
|
||||||
* needed. Call the given function on each turned off range.
|
|
||||||
*
|
|
||||||
* @param query_cpu_range Base CPU address to loop over
|
|
||||||
* @param size Size in bytes of the CPU range to loop over
|
|
||||||
* @param func Function to call for each turned off region
|
|
||||||
*/
|
|
||||||
template <Type type, bool clear>
|
|
||||||
void ForEachModifiedRange(VAddr query_cpu_range, s64 size, auto&& func) {
|
|
||||||
RENDERER_TRACE;
|
|
||||||
std::scoped_lock lk{lock};
|
|
||||||
static_assert(type != Type::Untracked);
|
|
||||||
|
|
||||||
std::span<u64> state_words = Span<type>();
|
|
||||||
const size_t offset = query_cpu_range - cpu_addr;
|
|
||||||
bool pending = false;
|
|
||||||
size_t pending_offset{};
|
|
||||||
size_t pending_pointer{};
|
|
||||||
const auto release = [&]() {
|
|
||||||
func(cpu_addr + pending_offset * BYTES_PER_PAGE,
|
|
||||||
(pending_pointer - pending_offset) * BYTES_PER_PAGE);
|
|
||||||
};
|
|
||||||
IterateWords(offset, size, [&](size_t index, u64 mask) {
|
|
||||||
RENDERER_TRACE;
|
|
||||||
if constexpr (type == Type::GPU) {
|
|
||||||
mask &= ~untracked[index];
|
|
||||||
}
|
|
||||||
const u64 word = state_words[index] & mask;
|
|
||||||
if constexpr (clear) {
|
|
||||||
if constexpr (type == Type::CPU) {
|
|
||||||
UpdateProtection<true>(index, untracked[index], mask);
|
|
||||||
untracked[index] &= ~mask;
|
|
||||||
}
|
|
||||||
state_words[index] &= ~mask;
|
|
||||||
}
|
|
||||||
const size_t base_offset = index * PAGES_PER_WORD;
|
|
||||||
IteratePages(word, [&](size_t pages_offset, size_t pages_size) {
|
|
||||||
RENDERER_TRACE;
|
|
||||||
const auto reset = [&]() {
|
|
||||||
pending_offset = base_offset + pages_offset;
|
|
||||||
pending_pointer = base_offset + pages_offset + pages_size;
|
|
||||||
};
|
|
||||||
if (!pending) {
|
|
||||||
reset();
|
|
||||||
pending = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (pending_pointer == base_offset + pages_offset) {
|
|
||||||
pending_pointer += pages_size;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
release();
|
|
||||||
reset();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (pending) {
|
|
||||||
release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true when a region has been modified
|
|
||||||
*
|
|
||||||
* @param offset Offset in bytes from the start of the buffer
|
|
||||||
* @param size Size in bytes of the region to query for modifications
|
|
||||||
*/
|
|
||||||
template <Type type>
|
|
||||||
[[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept {
|
|
||||||
static_assert(type != Type::Untracked);
|
|
||||||
|
|
||||||
const std::span<const u64> state_words = Span<type>();
|
|
||||||
bool result = false;
|
|
||||||
IterateWords(offset, size, [&](size_t index, u64 mask) {
|
|
||||||
if constexpr (type == Type::GPU) {
|
|
||||||
mask &= ~untracked[index];
|
|
||||||
}
|
|
||||||
const u64 word = state_words[index] & mask;
|
|
||||||
if (word != 0) {
|
|
||||||
result = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* Notify tracker about changes in the CPU tracking state of a word in the buffer
|
|
||||||
*
|
|
||||||
* @param word_index Index to the word to notify to the tracker
|
|
||||||
* @param current_bits Current state of the word
|
|
||||||
* @param new_bits New state of the word
|
|
||||||
*
|
|
||||||
* @tparam add_to_tracker True when the tracker should start tracking the new pages
|
|
||||||
*/
|
|
||||||
template <bool add_to_tracker>
|
|
||||||
void UpdateProtection(u64 word_index, u64 current_bits, u64 new_bits) const {
|
|
||||||
RENDERER_TRACE;
|
|
||||||
constexpr s32 delta = add_to_tracker ? 1 : -1;
|
|
||||||
u64 changed_bits = (add_to_tracker ? current_bits : ~current_bits) & new_bits;
|
|
||||||
VAddr addr = cpu_addr + word_index * BYTES_PER_WORD;
|
|
||||||
IteratePages(changed_bits, [&](size_t offset, size_t size) {
|
|
||||||
tracker->UpdatePageWatchers<delta>(addr + offset * BYTES_PER_PAGE,
|
|
||||||
size * BYTES_PER_PAGE);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <Type type>
|
|
||||||
std::span<u64> Span() noexcept {
|
|
||||||
if constexpr (type == Type::CPU) {
|
|
||||||
return cpu;
|
|
||||||
} else if constexpr (type == Type::GPU) {
|
|
||||||
return gpu;
|
|
||||||
} else if constexpr (type == Type::Untracked) {
|
|
||||||
return untracked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <Type type>
|
|
||||||
std::span<const u64> Span() const noexcept {
|
|
||||||
if constexpr (type == Type::CPU) {
|
|
||||||
return cpu;
|
|
||||||
} else if constexpr (type == Type::GPU) {
|
|
||||||
return gpu;
|
|
||||||
} else if constexpr (type == Type::Untracked) {
|
|
||||||
return untracked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
|
|
||||||
Common::AdaptiveMutex lock;
|
|
||||||
#else
|
|
||||||
Common::SpinLock lock;
|
|
||||||
#endif
|
|
||||||
PageManager* tracker;
|
|
||||||
VAddr cpu_addr = 0;
|
|
||||||
WordsArray cpu;
|
|
||||||
WordsArray gpu;
|
|
||||||
WordsArray untracked;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace VideoCore
|
|
|
@ -11,6 +11,7 @@ set(SHADER_FILES
|
||||||
detilers/micro_32bpp.comp
|
detilers/micro_32bpp.comp
|
||||||
detilers/micro_64bpp.comp
|
detilers/micro_64bpp.comp
|
||||||
detilers/micro_8bpp.comp
|
detilers/micro_8bpp.comp
|
||||||
|
color_to_ms_depth.frag
|
||||||
fault_buffer_process.comp
|
fault_buffer_process.comp
|
||||||
fs_tri.vert
|
fs_tri.vert
|
||||||
fsr.comp
|
fsr.comp
|
||||||
|
|
15
src/video_core/host_shaders/color_to_ms_depth.frag
Normal file
15
src/video_core/host_shaders/color_to_ms_depth.frag
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#version 450 core
|
||||||
|
#extension GL_EXT_samplerless_texture_functions : require
|
||||||
|
|
||||||
|
layout (binding = 0, set = 0) uniform texture2D color;
|
||||||
|
|
||||||
|
layout (location = 0) in vec2 uv;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
ivec2 coord = ivec2(uv * vec2(textureSize(color, 0).xy));
|
||||||
|
gl_FragDepth = texelFetch(color, coord, 0)[gl_SampleID];
|
||||||
|
}
|
|
@ -48,19 +48,15 @@ struct PageManager::Impl {
|
||||||
u8 AddDelta() {
|
u8 AddDelta() {
|
||||||
if constexpr (delta == 1) {
|
if constexpr (delta == 1) {
|
||||||
return ++num_watchers;
|
return ++num_watchers;
|
||||||
} else {
|
} else if constexpr (delta == -1) {
|
||||||
ASSERT_MSG(num_watchers > 0, "Not enough watchers");
|
ASSERT_MSG(num_watchers > 0, "Not enough watchers");
|
||||||
return --num_watchers;
|
return --num_watchers;
|
||||||
|
} else {
|
||||||
|
return num_watchers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UpdateProtectRange {
|
|
||||||
VAddr addr;
|
|
||||||
u64 size;
|
|
||||||
Core::MemoryPermission perms;
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr size_t ADDRESS_BITS = 40;
|
static constexpr size_t ADDRESS_BITS = 40;
|
||||||
static constexpr size_t NUM_ADDRESS_PAGES = 1ULL << (40 - PAGE_BITS);
|
static constexpr size_t NUM_ADDRESS_PAGES = 1ULL << (40 - PAGE_BITS);
|
||||||
inline static Vulkan::Rasterizer* rasterizer;
|
inline static Vulkan::Rasterizer* rasterizer;
|
||||||
|
@ -190,66 +186,122 @@ struct PageManager::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
template <s32 delta>
|
template <bool track>
|
||||||
void UpdatePageWatchers(VAddr addr, u64 size) {
|
void UpdatePageWatchers(VAddr addr, u64 size) {
|
||||||
RENDERER_TRACE;
|
RENDERER_TRACE;
|
||||||
boost::container::small_vector<UpdateProtectRange, 16> update_ranges;
|
|
||||||
{
|
|
||||||
std::scoped_lock lk(lock);
|
|
||||||
|
|
||||||
size_t page = addr >> PAGE_BITS;
|
size_t page = addr >> PAGE_BITS;
|
||||||
auto perms = cached_pages[page].Perm();
|
auto perms = cached_pages[page].Perm();
|
||||||
u64 range_begin = 0;
|
u64 range_begin = 0;
|
||||||
u64 range_bytes = 0;
|
u64 range_bytes = 0;
|
||||||
|
|
||||||
const auto release_pending = [&] {
|
const auto release_pending = [&] {
|
||||||
if (range_bytes > 0) {
|
if (range_bytes > 0) {
|
||||||
RENDERER_TRACE;
|
RENDERER_TRACE;
|
||||||
// Add pending (un)protect action
|
// Perform pending (un)protect action
|
||||||
update_ranges.push_back({range_begin << PAGE_BITS, range_bytes, perms});
|
Protect(range_begin << PAGE_BITS, range_bytes, perms);
|
||||||
range_bytes = 0;
|
range_bytes = 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Iterate requested pages
|
std::scoped_lock lk(lock);
|
||||||
const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
|
|
||||||
const u64 aligned_addr = page << PAGE_BITS;
|
|
||||||
const u64 aligned_end = page_end << PAGE_BITS;
|
|
||||||
ASSERT_MSG(rasterizer->IsMapped(aligned_addr, aligned_end - aligned_addr),
|
|
||||||
"Attempted to track non-GPU memory at address {:#x}, size {:#x}.",
|
|
||||||
aligned_addr, aligned_end - aligned_addr);
|
|
||||||
|
|
||||||
for (; page != page_end; ++page) {
|
// Iterate requested pages
|
||||||
PageState& state = cached_pages[page];
|
const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
|
||||||
|
const u64 aligned_addr = page << PAGE_BITS;
|
||||||
|
const u64 aligned_end = page_end << PAGE_BITS;
|
||||||
|
ASSERT_MSG(rasterizer->IsMapped(aligned_addr, aligned_end - aligned_addr),
|
||||||
|
"Attempted to track non-GPU memory at address {:#x}, size {:#x}.", aligned_addr,
|
||||||
|
aligned_end - aligned_addr);
|
||||||
|
|
||||||
// Apply the change to the page state
|
for (; page != page_end; ++page) {
|
||||||
const u8 new_count = state.AddDelta<delta>();
|
PageState& state = cached_pages[page];
|
||||||
|
|
||||||
|
// Apply the change to the page state
|
||||||
|
const u8 new_count = state.AddDelta<track ? 1 : -1>();
|
||||||
|
|
||||||
|
if (auto new_perms = state.Perm(); new_perms != perms) [[unlikely]] {
|
||||||
// If the protection changed add pending (un)protect action
|
// If the protection changed add pending (un)protect action
|
||||||
if (auto new_perms = state.Perm(); new_perms != perms) [[unlikely]] {
|
release_pending();
|
||||||
release_pending();
|
perms = new_perms;
|
||||||
perms = new_perms;
|
} else if (range_bytes != 0) {
|
||||||
}
|
// If the protection did not change, extend the current range
|
||||||
|
range_bytes += PAGE_SIZE;
|
||||||
// If the page must be (un)protected, add it to the pending range
|
|
||||||
if ((new_count == 0 && delta < 0) || (new_count == 1 && delta > 0)) {
|
|
||||||
if (range_bytes == 0) {
|
|
||||||
range_begin = page;
|
|
||||||
}
|
|
||||||
range_bytes += PAGE_SIZE;
|
|
||||||
} else {
|
|
||||||
release_pending();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add pending (un)protect action
|
// Only start a new range if the page must be (un)protected
|
||||||
release_pending();
|
if (range_bytes == 0 && ((new_count == 0 && !track) || (new_count == 1 && track))) {
|
||||||
|
range_begin = page;
|
||||||
|
range_bytes = PAGE_SIZE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush deferred protects
|
// Add pending (un)protect action
|
||||||
for (const auto& range : update_ranges) {
|
release_pending();
|
||||||
Protect(range.addr, range.size, range.perms);
|
}
|
||||||
|
|
||||||
|
template <bool track>
|
||||||
|
void UpdatePageWatchersForRegion(VAddr base_addr, RegionBits& mask) {
|
||||||
|
RENDERER_TRACE;
|
||||||
|
auto start_range = mask.FirstRange();
|
||||||
|
auto end_range = mask.LastRange();
|
||||||
|
|
||||||
|
if (start_range.second == end_range.second) {
|
||||||
|
// Optimization: if all pages are contiguous, use the regular UpdatePageWatchers
|
||||||
|
const VAddr start_addr = base_addr + (start_range.first << PAGE_BITS);
|
||||||
|
const u64 size = (start_range.second - start_range.first) << PAGE_BITS;
|
||||||
|
|
||||||
|
UpdatePageWatchers<track>(start_addr, size);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t base_page = (base_addr >> PAGE_BITS);
|
||||||
|
auto perms = cached_pages[base_page + start_range.first].Perm();
|
||||||
|
u64 range_begin = 0;
|
||||||
|
u64 range_bytes = 0;
|
||||||
|
|
||||||
|
const auto release_pending = [&] {
|
||||||
|
if (range_bytes > 0) {
|
||||||
|
RENDERER_TRACE;
|
||||||
|
// Perform pending (un)protect action
|
||||||
|
Protect((range_begin << PAGE_BITS), range_bytes, perms);
|
||||||
|
range_bytes = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::scoped_lock lk(lock);
|
||||||
|
|
||||||
|
// Iterate pages
|
||||||
|
for (size_t page = start_range.first; page < end_range.second; ++page) {
|
||||||
|
PageState& state = cached_pages[base_page + page];
|
||||||
|
const bool update = mask.Get(page);
|
||||||
|
|
||||||
|
// Apply the change to the page state
|
||||||
|
const u8 new_count = update ? state.AddDelta<track ? 1 : -1>() : state.AddDelta<0>();
|
||||||
|
|
||||||
|
if (auto new_perms = state.Perm(); new_perms != perms) [[unlikely]] {
|
||||||
|
// If the protection changed add pending (un)protect action
|
||||||
|
release_pending();
|
||||||
|
perms = new_perms;
|
||||||
|
} else if (range_bytes != 0) {
|
||||||
|
// If the protection did not change, extend the current range
|
||||||
|
range_bytes += PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the page is not being updated, skip it
|
||||||
|
if (!update) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only start a new range if the page must be (un)protected
|
||||||
|
if (range_bytes == 0 && ((new_count == 0 && !track) || (new_count == 1 && track))) {
|
||||||
|
range_begin = base_page + page;
|
||||||
|
range_bytes = PAGE_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add pending (un)protect action
|
||||||
|
release_pending();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<PageState, NUM_ADDRESS_PAGES> cached_pages{};
|
std::array<PageState, NUM_ADDRESS_PAGES> cached_pages{};
|
||||||
|
@ -273,12 +325,21 @@ void PageManager::OnGpuUnmap(VAddr address, size_t size) {
|
||||||
impl->OnUnmap(address, size);
|
impl->OnUnmap(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <s32 delta>
|
template <bool track>
|
||||||
void PageManager::UpdatePageWatchers(VAddr addr, u64 size) const {
|
void PageManager::UpdatePageWatchers(VAddr addr, u64 size) const {
|
||||||
impl->UpdatePageWatchers<delta>(addr, size);
|
impl->UpdatePageWatchers<track>(addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template void PageManager::UpdatePageWatchers<1>(VAddr addr, u64 size) const;
|
template <bool track>
|
||||||
template void PageManager::UpdatePageWatchers<-1>(VAddr addr, u64 size) const;
|
void PageManager::UpdatePageWatchersForRegion(VAddr base_addr, RegionBits& mask) const {
|
||||||
|
impl->UpdatePageWatchersForRegion<track>(base_addr, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
template void PageManager::UpdatePageWatchers<true>(VAddr addr, u64 size) const;
|
||||||
|
template void PageManager::UpdatePageWatchers<false>(VAddr addr, u64 size) const;
|
||||||
|
template void PageManager::UpdatePageWatchersForRegion<true>(VAddr base_addr,
|
||||||
|
RegionBits& mask) const;
|
||||||
|
template void PageManager::UpdatePageWatchersForRegion<false>(VAddr base_addr,
|
||||||
|
RegionBits& mask) const;
|
||||||
|
|
||||||
} // namespace VideoCore
|
} // namespace VideoCore
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
#include "video_core/buffer_cache//region_definitions.h"
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
class Rasterizer;
|
class Rasterizer;
|
||||||
|
@ -28,9 +29,14 @@ public:
|
||||||
void OnGpuUnmap(VAddr address, size_t size);
|
void OnGpuUnmap(VAddr address, size_t size);
|
||||||
|
|
||||||
/// Updates watches in the pages touching the specified region.
|
/// Updates watches in the pages touching the specified region.
|
||||||
template <s32 delta>
|
template <bool track>
|
||||||
void UpdatePageWatchers(VAddr addr, u64 size) const;
|
void UpdatePageWatchers(VAddr addr, u64 size) const;
|
||||||
|
|
||||||
|
/// Updates watches in the pages touching the specified region
|
||||||
|
/// using a mask.
|
||||||
|
template <bool track>
|
||||||
|
void UpdatePageWatchersForRegion(VAddr base_addr, RegionBits& mask) const;
|
||||||
|
|
||||||
/// Returns page aligned address.
|
/// Returns page aligned address.
|
||||||
static constexpr VAddr GetPageAddr(VAddr addr) {
|
static constexpr VAddr GetPageAddr(VAddr addr) {
|
||||||
return Common::AlignDown(addr, PAGE_SIZE);
|
return Common::AlignDown(addr, PAGE_SIZE);
|
||||||
|
|
|
@ -281,6 +281,8 @@ bool Instance::CreateDevice() {
|
||||||
if (shader_atomic_float2) {
|
if (shader_atomic_float2) {
|
||||||
shader_atomic_float2_features =
|
shader_atomic_float2_features =
|
||||||
feature_chain.get<vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT>();
|
feature_chain.get<vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT>();
|
||||||
|
LOG_INFO(Render_Vulkan, "- shaderBufferFloat32AtomicMinMax: {}",
|
||||||
|
shader_atomic_float2_features.shaderBufferFloat32AtomicMinMax);
|
||||||
LOG_INFO(Render_Vulkan, "- shaderImageFloat32AtomicMinMax: {}",
|
LOG_INFO(Render_Vulkan, "- shaderImageFloat32AtomicMinMax: {}",
|
||||||
shader_atomic_float2_features.shaderImageFloat32AtomicMinMax);
|
shader_atomic_float2_features.shaderImageFloat32AtomicMinMax);
|
||||||
}
|
}
|
||||||
|
@ -355,6 +357,7 @@ bool Instance::CreateDevice() {
|
||||||
.independentBlend = features.independentBlend,
|
.independentBlend = features.independentBlend,
|
||||||
.geometryShader = features.geometryShader,
|
.geometryShader = features.geometryShader,
|
||||||
.tessellationShader = features.tessellationShader,
|
.tessellationShader = features.tessellationShader,
|
||||||
|
.sampleRateShading = features.sampleRateShading,
|
||||||
.dualSrcBlend = features.dualSrcBlend,
|
.dualSrcBlend = features.dualSrcBlend,
|
||||||
.logicOp = features.logicOp,
|
.logicOp = features.logicOp,
|
||||||
.multiDrawIndirect = features.multiDrawIndirect,
|
.multiDrawIndirect = features.multiDrawIndirect,
|
||||||
|
@ -432,6 +435,8 @@ bool Instance::CreateDevice() {
|
||||||
.legacyVertexAttributes = true,
|
.legacyVertexAttributes = true,
|
||||||
},
|
},
|
||||||
vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT{
|
vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT{
|
||||||
|
.shaderBufferFloat32AtomicMinMax =
|
||||||
|
shader_atomic_float2_features.shaderBufferFloat32AtomicMinMax,
|
||||||
.shaderImageFloat32AtomicMinMax =
|
.shaderImageFloat32AtomicMinMax =
|
||||||
shader_atomic_float2_features.shaderImageFloat32AtomicMinMax,
|
shader_atomic_float2_features.shaderImageFloat32AtomicMinMax,
|
||||||
},
|
},
|
||||||
|
|
|
@ -165,6 +165,13 @@ public:
|
||||||
return amd_shader_trinary_minmax;
|
return amd_shader_trinary_minmax;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true when the shaderBufferFloat32AtomicMinMax feature of
|
||||||
|
/// VK_EXT_shader_atomic_float2 is supported.
|
||||||
|
bool IsShaderAtomicFloatBuffer32MinMaxSupported() const {
|
||||||
|
return shader_atomic_float2 &&
|
||||||
|
shader_atomic_float2_features.shaderBufferFloat32AtomicMinMax;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true when the shaderImageFloat32AtomicMinMax feature of
|
/// Returns true when the shaderImageFloat32AtomicMinMax feature of
|
||||||
/// VK_EXT_shader_atomic_float2 is supported.
|
/// VK_EXT_shader_atomic_float2 is supported.
|
||||||
bool IsShaderAtomicFloatImage32MinMaxSupported() const {
|
bool IsShaderAtomicFloatImage32MinMaxSupported() const {
|
||||||
|
@ -324,6 +331,9 @@ public:
|
||||||
return driver_id != vk::DriverId::eMoltenvk;
|
return driver_id != vk::DriverId::eMoltenvk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determines if a format is supported for a set of feature flags.
|
||||||
|
[[nodiscard]] bool IsFormatSupported(vk::Format format, vk::FormatFeatureFlags2 flags) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Creates the logical device opportunistically enabling extensions
|
/// Creates the logical device opportunistically enabling extensions
|
||||||
bool CreateDevice();
|
bool CreateDevice();
|
||||||
|
@ -338,9 +348,6 @@ private:
|
||||||
/// Gets the supported feature flags for a format.
|
/// Gets the supported feature flags for a format.
|
||||||
[[nodiscard]] vk::FormatFeatureFlags2 GetFormatFeatureFlags(vk::Format format) const;
|
[[nodiscard]] vk::FormatFeatureFlags2 GetFormatFeatureFlags(vk::Format format) const;
|
||||||
|
|
||||||
/// Determines if a format is supported for a set of feature flags.
|
|
||||||
[[nodiscard]] bool IsFormatSupported(vk::Format format, vk::FormatFeatureFlags2 flags) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
vk::UniqueInstance instance;
|
vk::UniqueInstance instance;
|
||||||
vk::PhysicalDevice physical_device;
|
vk::PhysicalDevice physical_device;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue