mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-25 20:36:54 +01:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8cecaf4e30 | |||
| 82b2711cdf | |||
| 35a275e97d | |||
| 303a0203b6 | |||
| cfbaee07f0 | |||
| fe45265fcd | |||
| 8b294eb5b8 | |||
| 07c9978ac6 | |||
| b61b3f2a40 | |||
| 001dfea501 | |||
| f100f785da | |||
| ba1854bbdc | |||
| ea6aae8577 | |||
| 5b7d3343cf | |||
| 394e638905 | |||
| c2b7b0b58c | |||
| 1d2f59dfa4 | |||
| 007d7ea2eb | |||
| 95d3d9d3ad | |||
| bfc8cd0e0b | |||
| 165234b159 | |||
| 6493674726 | |||
| cbedf5707f | |||
| 57c5b206ca | |||
| 87b4417aca | |||
| 104530eafc | |||
| 6cfde37e98 | |||
| c5cebc1d19 | |||
| 697b18b366 | |||
| 5b2935e36c | |||
| 5dc644edb4 | |||
| 1211958b4e | |||
| 218e7200bb | |||
| 2bf78a94e0 | |||
| cc8e02380f | |||
| c66d977c1d | |||
| 4b77686566 | |||
| a6144f794b | |||
| bab1c3614c | |||
| de1545e19a | |||
| ce629e0391 | |||
| 6c03b4aaa5 | |||
| 0627e23e92 | |||
| 4e6f4f99d3 | |||
| f30afc184e | |||
| 228cf3fbf4 |
@@ -0,0 +1,37 @@
|
||||
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
|
||||
|
||||
# Keep inherited review profiles, automatic reviews, tools, and pre-merge checks.
|
||||
inheritance: true
|
||||
|
||||
reviews:
|
||||
# Trim supplemental output and keep the remaining walkthrough collapsed.
|
||||
high_level_summary: false
|
||||
review_status: false
|
||||
review_details: false
|
||||
collapse_walkthrough: true
|
||||
changed_files_summary: false
|
||||
sequence_diagrams: false
|
||||
estimate_code_review_effort: false
|
||||
related_issues: false
|
||||
related_prs: false
|
||||
in_progress_fortune: false
|
||||
poem: false
|
||||
enable_prompt_for_ai_agents: false
|
||||
|
||||
# Hide standard finishing-touch controls; this also disables their commands.
|
||||
finishing_touches:
|
||||
docstrings:
|
||||
enabled: false
|
||||
unit_tests:
|
||||
enabled: false
|
||||
simplify:
|
||||
enabled: false
|
||||
autofix:
|
||||
enabled: false
|
||||
fix_ci:
|
||||
enabled: false
|
||||
resolve_merge_conflict:
|
||||
enabled: false
|
||||
|
||||
chat:
|
||||
art: false
|
||||
Vendored
+6
-4
@@ -2,15 +2,17 @@
|
||||
"cmake.sourceDirectory": "${workspaceFolder}",
|
||||
"cmake.buildDirectory": "${workspaceFolder}/_Build/vscode-clang",
|
||||
"cmake.generator": "Ninja",
|
||||
"cmake.useVsDeveloperEnvironment": "always",
|
||||
"cmake.environment": {
|
||||
"CC": "clang-cl",
|
||||
"CXX": "clang-cl",
|
||||
"PATH": "${env:ProgramFiles}/Microsoft Visual Studio/18/Community/VC/Tools/Llvm/x64/bin;${env:ProgramFiles}/Microsoft Visual Studio/18/Community/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja;${env:PATH}"
|
||||
"CXX": "clang-cl"
|
||||
},
|
||||
"cmake.configureSettings": {
|
||||
"CMAKE_C_COMPILER": "clang-cl",
|
||||
"CMAKE_CXX_COMPILER": "clang-cl",
|
||||
"CMAKE_PREFIX_PATH": "C:/Qt/6.11.0/msvc2022_64"
|
||||
}
|
||||
},
|
||||
"C_Cpp.default.intelliSenseMode": "windows-clang-x64",
|
||||
"C_Cpp.default.compileCommands": "${workspaceFolder}/_Build/vscode-clang/compile_commands.json",
|
||||
"C_Cpp.default.cppStandard": "c++20"
|
||||
}
|
||||
|
||||
|
||||
Vendored
+3
@@ -86,6 +86,9 @@ set(SPDLOG_BUILD_WARNINGS OFF CACHE BOOL "" FORCE)
|
||||
set(SPDLOG_INSTALL OFF CACHE BOOL "" FORCE)
|
||||
set(SPDLOG_FMT_EXTERNAL ON CACHE BOOL "" FORCE)
|
||||
set(SPDLOG_NO_EXCEPTIONS ON CACHE BOOL "" FORCE)
|
||||
if(WIN32)
|
||||
set(SPDLOG_WCHAR_FILENAMES ON CACHE BOOL "" FORCE)
|
||||
endif()
|
||||
add_subdirectory(spdlog EXCLUDE_FROM_ALL)
|
||||
|
||||
if (NOT TARGET FFmpeg::ffmpeg)
|
||||
|
||||
@@ -450,6 +450,12 @@ add_executable(audio_out2_port_tests EXCLUDE_FROM_ALL
|
||||
target_link_libraries(audio_out2_port_tests common fmt::fmt)
|
||||
target_include_directories(audio_out2_port_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(save_data_memory_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/SaveDataMemoryTests.cpp"
|
||||
)
|
||||
target_link_libraries(save_data_memory_tests common fmt::fmt)
|
||||
target_include_directories(save_data_memory_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(event_queue_lifetime_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/EventQueueLifetimeTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/kernel/eventQueue.cpp"
|
||||
@@ -534,6 +540,7 @@ if(BUILD_TESTING)
|
||||
add_test(NAME kernel_file_system COMMAND $<TARGET_FILE:kernel_file_system_tests>)
|
||||
add_test(NAME sync_on_address COMMAND $<TARGET_FILE:sync_on_address_tests>)
|
||||
add_test(NAME audio_out2_port COMMAND $<TARGET_FILE:audio_out2_port_tests>)
|
||||
add_test(NAME save_data_memory COMMAND $<TARGET_FILE:save_data_memory_tests>)
|
||||
add_test(NAME shader_recompiler_compute COMMAND $<TARGET_FILE:shader_recompiler_compute_tests>)
|
||||
add_test(NAME shader_recompiler_alignbyte
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --alignbyte-only)
|
||||
@@ -601,6 +608,21 @@ if(BUILD_TESTING)
|
||||
)
|
||||
endif()
|
||||
|
||||
# Test Darwin signal-context restoration for SSE4a instructions.
|
||||
if(APPLE AND (CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64" OR
|
||||
(NOT CMAKE_OSX_ARCHITECTURES AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64)$")))
|
||||
add_executable(macos_sse4a_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/macosSse4aTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/loader/x64InstructionEmulator.cpp"
|
||||
)
|
||||
target_link_libraries(macos_sse4a_tests common fmt::fmt Zydis::Zydis)
|
||||
target_include_directories(macos_sse4a_tests PRIVATE ${inc_headers})
|
||||
if(BUILD_TESTING)
|
||||
add_test(NAME macos_sse4a COMMAND $<TARGET_FILE:macos_sse4a_tests>)
|
||||
set_tests_properties(macos_sse4a PROPERTIES TIMEOUT 15)
|
||||
add_dependencies(kyty_tests macos_sse4a_tests)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_executable(kyty_emulator "${KYTY_SOURCE_DIR}/main.cpp" ${kyty_emulator_src})
|
||||
configure_macos_guest_address_space(kyty_emulator)
|
||||
|
||||
@@ -137,6 +137,7 @@ the Vulkan/SPIR-V validation rules.
|
||||
- Visual Studio 2022 or Build Tools 2022 with the **Desktop development with C++** workload and
|
||||
**C++ Clang tools for Windows** component
|
||||
- Qt 6 for MSVC 2022 64-bit, including Concurrent, Network, and Widgets
|
||||
- [glslang](https://github.com/KhronosGroup/glslang/releases) (`glslangValidator`) on `PATH`
|
||||
|
||||
The Microsoft C++ compiler (`cl.exe`) is not supported; use `clang-cl`.
|
||||
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
#ifndef KYTY_COMMON_BYTEBUFFER_H_
|
||||
#define KYTY_COMMON_BYTEBUFFER_H_
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef> // IWYU pragma: export
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace Common {
|
||||
|
||||
using Byte = std::byte;
|
||||
|
||||
class ByteBuffer {
|
||||
public:
|
||||
using DataType = std::vector<Byte>;
|
||||
|
||||
static constexpr uint32_t INVALID_INDEX = static_cast<uint32_t>(-1);
|
||||
|
||||
using iterator = DataType::iterator;
|
||||
using const_iterator = DataType::const_iterator;
|
||||
|
||||
ByteBuffer() = default;
|
||||
ByteBuffer(const ByteBuffer&) = default;
|
||||
ByteBuffer(ByteBuffer&&) = default;
|
||||
|
||||
ByteBuffer& operator=(const ByteBuffer&) = default;
|
||||
ByteBuffer& operator=(ByteBuffer&&) = default;
|
||||
|
||||
explicit ByteBuffer(uint32_t size): m_data(size) {}
|
||||
|
||||
ByteBuffer(std::initializer_list<uint8_t> list) {
|
||||
for (const uint8_t& e: list) {
|
||||
m_data.emplace_back(static_cast<Byte>(e));
|
||||
}
|
||||
}
|
||||
|
||||
explicit ByteBuffer(const void* buf, uint32_t size)
|
||||
: ByteBuffer(size) // @suppress("Ambiguous problem")
|
||||
{
|
||||
EXIT_IF(buf == nullptr && size != 0);
|
||||
|
||||
if (size != 0) {
|
||||
std::memcpy(m_data.data(), buf, size);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] uint32_t Size() const { return static_cast<uint32_t>(m_data.size()); }
|
||||
[[nodiscard]] bool IsEmpty() const { return m_data.empty(); }
|
||||
|
||||
void Clear() { m_data.clear(); }
|
||||
|
||||
void Add(const Byte& val) { m_data.push_back(val); }
|
||||
void Add(Byte&& val) { m_data.push_back(std::move(val)); }
|
||||
|
||||
void Add(const Byte* val, uint32_t num) {
|
||||
EXIT_IF(val == nullptr && num != 0);
|
||||
m_data.insert(m_data.end(), val, val + num);
|
||||
}
|
||||
|
||||
void Add(const ByteBuffer& v) {
|
||||
if (this == &v) {
|
||||
const ByteBuffer copy(v);
|
||||
Add(copy.GetDataConst(), copy.Size());
|
||||
return;
|
||||
}
|
||||
Add(v.GetDataConst(), v.Size());
|
||||
}
|
||||
|
||||
void InsertAt(uint32_t index, const Byte& val) {
|
||||
EXIT_IF(index > Size());
|
||||
m_data.insert(m_data.begin() + index, val);
|
||||
}
|
||||
|
||||
void InsertAt(uint32_t index, Byte&& val) {
|
||||
EXIT_IF(index > Size());
|
||||
m_data.insert(m_data.begin() + index, std::move(val));
|
||||
}
|
||||
|
||||
[[nodiscard]] uint32_t Find(const Byte& t) const {
|
||||
const auto it = std::find(m_data.begin(), m_data.end(), t);
|
||||
return (it == m_data.end() ? INVALID_INDEX
|
||||
: static_cast<uint32_t>(std::distance(m_data.begin(), it)));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Contains(const Byte& t) const { return Find(t) != INVALID_INDEX; }
|
||||
|
||||
bool Remove(const Byte& t) {
|
||||
const auto it = std::find(m_data.begin(), m_data.end(), t);
|
||||
if (it == m_data.end()) {
|
||||
return false;
|
||||
}
|
||||
m_data.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoveAt(uint32_t index, uint32_t count = 1) {
|
||||
if (index >= Size() || count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t erase_count = std::min(count, Size() - index);
|
||||
m_data.erase(m_data.begin() + index, m_data.begin() + index + erase_count);
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IndexValid(uint32_t index) const { return index < Size(); }
|
||||
|
||||
Byte& operator[](uint32_t index) {
|
||||
EXIT_IF(index >= Size());
|
||||
return m_data[index];
|
||||
}
|
||||
|
||||
const Byte& operator[](uint32_t index) const {
|
||||
EXIT_IF(index >= Size());
|
||||
return m_data[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] const Byte& At(uint32_t index) const {
|
||||
EXIT_IF(index >= Size());
|
||||
return m_data[index];
|
||||
}
|
||||
|
||||
Byte* GetData() { return m_data.data(); }
|
||||
[[nodiscard]] const Byte* GetData() const { return m_data.data(); }
|
||||
[[nodiscard]] const Byte* GetDataConst() const { return m_data.data(); }
|
||||
|
||||
void Sort() {
|
||||
std::sort(m_data.begin(), m_data.end(), [](Byte a, Byte b) {
|
||||
return std::to_integer<uint8_t>(a) < std::to_integer<uint8_t>(b);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename OP>
|
||||
void Sort(OP&& comp_func) {
|
||||
std::sort(m_data.begin(), m_data.end(), std::forward<OP>(comp_func));
|
||||
}
|
||||
|
||||
bool operator==(const ByteBuffer& s) const { return m_data == s.m_data; }
|
||||
bool operator!=(const ByteBuffer& s) const { return !(*this == s); }
|
||||
|
||||
iterator begin() { return m_data.begin(); } // NOLINT(readability-identifier-naming)
|
||||
iterator end() { return m_data.end(); } // NOLINT(readability-identifier-naming)
|
||||
[[nodiscard]] const_iterator begin() const {
|
||||
return m_data.begin();
|
||||
} // NOLINT(readability-identifier-naming)
|
||||
[[nodiscard]] const_iterator end() const {
|
||||
return m_data.end();
|
||||
} // NOLINT(readability-identifier-naming)
|
||||
[[nodiscard]] const_iterator cbegin() const {
|
||||
return m_data.cbegin();
|
||||
} // NOLINT(readability-identifier-naming)
|
||||
[[nodiscard]] const_iterator cend() const {
|
||||
return m_data.cend();
|
||||
} // NOLINT(readability-identifier-naming)
|
||||
|
||||
private:
|
||||
DataType m_data;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#endif /* KYTY_COMMON_BYTEBUFFER_H_ */
|
||||
@@ -24,8 +24,4 @@ std::string Debug::GetLinker() {
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string Debug::GetBitness() {
|
||||
return "64";
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -10,7 +10,6 @@ namespace Common {
|
||||
namespace Debug {
|
||||
std::string GetCompiler();
|
||||
std::string GetLinker();
|
||||
std::string GetBitness();
|
||||
} // namespace Debug
|
||||
|
||||
} // namespace Common
|
||||
|
||||
+53
-84
@@ -136,10 +136,6 @@ bool File::OpenInMem(void* buf, uint32_t buf_size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool File::OpenInMem(ByteBuffer& buf) {
|
||||
return OpenInMem(buf.GetData(), buf.Size());
|
||||
}
|
||||
|
||||
bool File::CreateInMem() {
|
||||
EXIT_IF(m_p->f != nullptr);
|
||||
|
||||
@@ -249,88 +245,78 @@ void File::Printf(const char* format, ...) {
|
||||
Write(s.data(), static_cast<uint32_t>(s.size()));
|
||||
}
|
||||
|
||||
static std::filesystem::path WithoutTrailingSeparator(
|
||||
const std::filesystem::path& path) {
|
||||
if (!path.empty() && !path.has_filename() &&
|
||||
path != path.root_path()) {
|
||||
return path.parent_path();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
bool File::IsDirectoryExisting(const std::filesystem::path& path) {
|
||||
auto path_str = PathToGenericString(path);
|
||||
return SysFileIsDirectoryExisting(
|
||||
Common::EndsWith(path_str, "/") ? Common::RemoveLast(path_str, 1) : path_str);
|
||||
return SysFileIsDirectoryExisting(WithoutTrailingSeparator(path));
|
||||
}
|
||||
|
||||
bool File::IsFileExisting(const std::filesystem::path& name) {
|
||||
return SysFileIsFileExisting(name);
|
||||
}
|
||||
|
||||
bool File::CreateDirectory(
|
||||
const std::filesystem::path& path) // @suppress("Member declaration not found")
|
||||
{
|
||||
auto path_str = PathToGenericString(path);
|
||||
return SysFileCreateDirectory(Common::EndsWith(path_str, "/") ? Common::RemoveLast(path_str, 1)
|
||||
: path_str);
|
||||
bool File::CreateDirectory(const std::filesystem::path& path) {
|
||||
return SysFileCreateDirectory(
|
||||
WithoutTrailingSeparator(path));
|
||||
}
|
||||
|
||||
bool File::DeleteDirectory(const std::filesystem::path& path) {
|
||||
auto path_str = PathToGenericString(path);
|
||||
return SysFileDeleteDirectory(Common::EndsWith(path_str, "/") ? Common::RemoveLast(path_str, 1)
|
||||
: path_str);
|
||||
return SysFileDeleteDirectory(
|
||||
WithoutTrailingSeparator(path));
|
||||
}
|
||||
|
||||
bool File::CreateDirectories(const std::filesystem::path& path) {
|
||||
std::string real_path = Common::ReplaceChar(PathToGenericString(path), '\\', '/');
|
||||
const auto normalized_path =
|
||||
WithoutTrailingSeparator(path);
|
||||
|
||||
std::vector<std::string> list = Common::Split(real_path, "/");
|
||||
auto current_path = normalized_path.root_path();
|
||||
|
||||
std::string p;
|
||||
for (const auto& component:
|
||||
normalized_path.relative_path()) {
|
||||
current_path /= component;
|
||||
|
||||
for (uint32_t si = 0; si < list.size(); si++) {
|
||||
const std::string& s = list[si];
|
||||
if (IsDirectoryExisting(current_path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (si != 0 || Common::StartsWith(real_path, "/")) {
|
||||
p += "/";
|
||||
}
|
||||
if (!CreateDirectory(current_path)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
p += s;
|
||||
|
||||
if (IsDirectoryExisting(p)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!CreateDirectory(p)) // @suppress("Invalid arguments")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool File::DeleteDirectories(const std::filesystem::path& path) {
|
||||
std::string real_path = Common::ReplaceChar(PathToGenericString(path), '\\', '/');
|
||||
const auto normalized_path =
|
||||
WithoutTrailingSeparator(path);
|
||||
|
||||
std::vector<std::string> list = Common::Split(real_path, "/");
|
||||
std::vector<std::filesystem::path> directories;
|
||||
auto current_path = normalized_path.root_path();
|
||||
|
||||
std::string p;
|
||||
std::vector<std::string> list2;
|
||||
for (const auto& component:
|
||||
normalized_path.relative_path()) {
|
||||
current_path /= component;
|
||||
directories.push_back(current_path);
|
||||
}
|
||||
|
||||
for (uint32_t si = 0; si < list.size(); si++) {
|
||||
const std::string& s = list[si];
|
||||
for (auto it = directories.rbegin();
|
||||
it != directories.rend(); ++it) {
|
||||
if (!DeleteDirectory(*it)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (si != 0 || Common::StartsWith(real_path, "/")) {
|
||||
p += "/";
|
||||
}
|
||||
|
||||
p += s;
|
||||
|
||||
list2.push_back(p);
|
||||
}
|
||||
|
||||
uint32_t num = list2.size();
|
||||
|
||||
for (uint32_t si = num - 1; si < num; si--) {
|
||||
if (!DeleteDirectory(list2[si])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool File::DeleteFile(
|
||||
@@ -345,7 +331,7 @@ bool File::Flush() {
|
||||
return SysFileFlush(*m_p->f);
|
||||
}
|
||||
|
||||
ByteBuffer File::ReadWholeBuffer() {
|
||||
std::vector<std::byte> File::ReadWholeBuffer() {
|
||||
EXIT_IF(IsInvalid());
|
||||
EXIT_IF(Tell() != 0);
|
||||
|
||||
@@ -353,25 +339,14 @@ ByteBuffer File::ReadWholeBuffer() {
|
||||
|
||||
EXIT_IF((s >> 32u) != 0);
|
||||
|
||||
ByteBuffer buf(s);
|
||||
const auto read_size = static_cast<uint32_t>(s);
|
||||
std::vector<std::byte> buf(read_size);
|
||||
|
||||
Read(buf.GetData(), s);
|
||||
Read(buf.data(), read_size);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
ByteBuffer File::Read(uint32_t size) {
|
||||
ByteBuffer buf(size);
|
||||
uint32_t b = 0;
|
||||
Read(buf.GetData(), size, &b);
|
||||
buf.RemoveAt(b, size - b);
|
||||
return buf;
|
||||
}
|
||||
|
||||
void File::Write(const ByteBuffer& buf, uint32_t* bytes_written) {
|
||||
Write(buf.GetDataConst(), buf.Size(), bytes_written);
|
||||
}
|
||||
|
||||
DateTime File::GetLastAccessTimeUTC(const std::filesystem::path& name) {
|
||||
SysTimeStruct t {};
|
||||
SysFileToSystemTimeUtc(SysFileGetLastAccessTimeUtc(name), t);
|
||||
@@ -475,13 +450,7 @@ std::vector<File::FindInfo> File::FindFiles(const std::filesystem::path& path) {
|
||||
|
||||
SysFileFindFiles(path, files);
|
||||
|
||||
auto path_str = PathToGenericString(path);
|
||||
uint32_t len = static_cast<uint32_t>(path_str.size());
|
||||
|
||||
if (!Common::EndsWith(path_str, "/") && !Common::EndsWith(path_str, "\\")) {
|
||||
len++;
|
||||
}
|
||||
|
||||
const auto normalized_path = path.lexically_normal();
|
||||
std::vector<File::FindInfo> ret;
|
||||
ret.reserve(files.size());
|
||||
|
||||
@@ -489,7 +458,7 @@ std::vector<File::FindInfo> File::FindFiles(const std::filesystem::path& path) {
|
||||
File::FindInfo r {};
|
||||
|
||||
r.path_with_name = f.path_with_name;
|
||||
r.rel_path_with_name = Common::Mid(PathToGenericString(f.path_with_name), len);
|
||||
r.rel_path_with_name = f.path_with_name.lexically_normal().lexically_relative(normalized_path);
|
||||
r.size = f.size;
|
||||
|
||||
SysTimeStruct at {};
|
||||
|
||||
+4
-7
@@ -1,10 +1,10 @@
|
||||
#ifndef KYTY_COMMON_FILE_H_
|
||||
#define KYTY_COMMON_FILE_H_
|
||||
|
||||
#include "common/byteBuffer.h"
|
||||
#include "common/common.h"
|
||||
#include "common/dateTime.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -51,7 +51,6 @@ public:
|
||||
bool Create(const std::filesystem::path& name);
|
||||
bool Open(const std::filesystem::path& name, Mode mode);
|
||||
bool OpenInMem(void* buf, uint32_t buf_size);
|
||||
bool OpenInMem(ByteBuffer& buf); // NOLINT(google-runtime-references)
|
||||
bool CreateInMem();
|
||||
|
||||
void Close();
|
||||
@@ -73,14 +72,12 @@ public:
|
||||
|
||||
void GetLastAccessAndWriteTimeUTC(DateTime* access, DateTime* write);
|
||||
|
||||
void Read(void* data, uint32_t size, uint32_t* bytes_read = nullptr);
|
||||
ByteBuffer Read(uint32_t size);
|
||||
void Write(const void* data, uint32_t size, uint32_t* bytes_written = nullptr);
|
||||
void Write(const ByteBuffer& buf, uint32_t* bytes_written = nullptr);
|
||||
void Read(void* data, uint32_t size, uint32_t* bytes_read = nullptr);
|
||||
void Write(const void* data, uint32_t size, uint32_t* bytes_written = nullptr);
|
||||
|
||||
void Printf(const char* format, ...) KYTY_FORMAT_PRINTF(2, 3);
|
||||
|
||||
ByteBuffer ReadWholeBuffer();
|
||||
std::vector<std::byte> ReadWholeBuffer();
|
||||
|
||||
static uint64_t Size(const std::filesystem::path& name);
|
||||
|
||||
|
||||
@@ -54,103 +54,6 @@ inline uint32_t hash(const void* key, uint32_t key_len) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
inline uint32_t hash8(uint8_t key) {
|
||||
uint32_t hash = 0;
|
||||
|
||||
uint8_t* ptr = &key;
|
||||
|
||||
hash += ptr[0];
|
||||
hash += (hash << 10u);
|
||||
hash ^= (hash >> 6u);
|
||||
|
||||
hash += (hash << 3u);
|
||||
hash ^= (hash >> 11u);
|
||||
hash += (hash << 15u);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
inline uint32_t hash16(uint16_t key) {
|
||||
uint32_t hash = 0;
|
||||
|
||||
auto* ptr = reinterpret_cast<uint8_t*>(&key);
|
||||
|
||||
hash += ptr[0];
|
||||
hash += (hash << 10u);
|
||||
hash ^= (hash >> 6u);
|
||||
hash += ptr[1];
|
||||
hash += (hash << 10u);
|
||||
hash ^= (hash >> 6u);
|
||||
|
||||
hash += (hash << 3u);
|
||||
hash ^= (hash >> 11u);
|
||||
hash += (hash << 15u);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
inline uint32_t hash32(uint32_t key) {
|
||||
uint32_t hash = 0;
|
||||
|
||||
auto* ptr = reinterpret_cast<uint8_t*>(&key);
|
||||
|
||||
hash += ptr[0];
|
||||
hash += (hash << 10u);
|
||||
hash ^= (hash >> 6u);
|
||||
hash += ptr[1];
|
||||
hash += (hash << 10u);
|
||||
hash ^= (hash >> 6u);
|
||||
hash += ptr[2];
|
||||
hash += (hash << 10u);
|
||||
hash ^= (hash >> 6u);
|
||||
hash += ptr[3];
|
||||
hash += (hash << 10u);
|
||||
hash ^= (hash >> 6u);
|
||||
|
||||
hash += (hash << 3u);
|
||||
hash ^= (hash >> 11u);
|
||||
hash += (hash << 15u);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
inline uint32_t hash64(uint64_t key) {
|
||||
uint32_t hash = 0;
|
||||
|
||||
auto* ptr = reinterpret_cast<uint8_t*>(&key);
|
||||
|
||||
hash += ptr[0];
|
||||
hash += (hash << 10u);
|
||||
hash ^= (hash >> 6u);
|
||||
hash += ptr[1];
|
||||
hash += (hash << 10u);
|
||||
hash ^= (hash >> 6u);
|
||||
hash += ptr[2];
|
||||
hash += (hash << 10u);
|
||||
hash ^= (hash >> 6u);
|
||||
hash += ptr[3];
|
||||
hash += (hash << 10u);
|
||||
hash ^= (hash >> 6u);
|
||||
hash += ptr[4];
|
||||
hash += (hash << 10u);
|
||||
hash ^= (hash >> 6u);
|
||||
hash += ptr[5];
|
||||
hash += (hash << 10u);
|
||||
hash ^= (hash >> 6u);
|
||||
hash += ptr[6];
|
||||
hash += (hash << 10u);
|
||||
hash ^= (hash >> 6u);
|
||||
hash += ptr[7];
|
||||
hash += (hash << 10u);
|
||||
hash ^= (hash >> 6u);
|
||||
|
||||
hash += (hash << 3u);
|
||||
hash ^= (hash >> 11u);
|
||||
hash += (hash << 15u);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#endif /* KYTY_COMMON_HASH_H_ */
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
@@ -47,7 +46,7 @@ std::shared_ptr<spdlog::logger> MakeFileLogger(std::string name
|
||||
}
|
||||
|
||||
auto sink =
|
||||
std::make_shared<spdlog::sinks::basic_file_sink_mt>(Common::PathToString(path), true);
|
||||
std::make_shared<spdlog::sinks::basic_file_sink_mt>(path.native(), true);
|
||||
return MakeLogger(std::move(name), std::move(sink));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#ifndef KYTY_COMMON_MAGICENUM_H_
|
||||
#define KYTY_COMMON_MAGICENUM_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "magic_enum.hpp" // IWYU pragma: export
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename E>
|
||||
inline std::string EnumName(E v) {
|
||||
auto str = magic_enum::enum_name(v);
|
||||
return std::string(str.data(), str.length());
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
inline std::string EnumName8(E v) {
|
||||
auto str = magic_enum::enum_name(v);
|
||||
return std::string(str.data(), str.length());
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
inline E EnumValue(const std::string& str, E default_value) {
|
||||
auto v = magic_enum::enum_cast<E>(str.c_str());
|
||||
if (v.has_value()) {
|
||||
return v.value();
|
||||
}
|
||||
return default_value;
|
||||
}
|
||||
|
||||
#define KYTY_ENUM_RANGE(e, mx, mn) \
|
||||
namespace magic_enum::customize { \
|
||||
template <> \
|
||||
struct enum_range<e> { \
|
||||
static constexpr int min = (mx); \
|
||||
static constexpr int max = (mn); \
|
||||
}; \
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#endif /* KYTY_COMMON_MAGICENUM_H_ */
|
||||
@@ -52,7 +52,6 @@ uint64_t SysFileTell(sys_file_t& f); // NOLINT(google-runt
|
||||
bool SysFileTruncate(sys_file_t& f, uint64_t size); // NOLINT(google-runtime-references)
|
||||
bool SysFileUnlink(sys_file_t& f,
|
||||
const std::filesystem::path& name); // NOLINT(google-runtime-references)
|
||||
void SysFileWrite(uint32_t n, sys_file_t& f); // NOLINT(google-runtime-references)
|
||||
uint64_t SysFileSize(const std::filesystem::path& file_name);
|
||||
bool SysFileIsError(sys_file_t& f); // NOLINT(google-runtime-references)
|
||||
bool SysFileIsDirectoryExisting(const std::filesystem::path& path);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <filesystem>
|
||||
@@ -151,10 +152,6 @@ void SysFileWrite(const void* data, uint32_t size, sys_file_t& f, uint32_t* byte
|
||||
}
|
||||
}
|
||||
|
||||
void SysFileWrite(uint32_t n, sys_file_t& f) {
|
||||
SysFileWrite(&n, 4, f);
|
||||
}
|
||||
|
||||
sys_file_t* SysFileCreate(const std::filesystem::path& file_name) {
|
||||
auto* ret = new sys_file_t;
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#else
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/platform/sysVirtual.h"
|
||||
#include "common/virtualMemory.h"
|
||||
|
||||
#include <atomic>
|
||||
@@ -28,12 +27,12 @@
|
||||
#define KYTY_FIXED_NOREPLACE
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
namespace Common::VirtualMemory {
|
||||
|
||||
static pthread_mutex_t g_virtual_mutex {};
|
||||
static std::map<uintptr_t, size_t>* g_allocs = nullptr;
|
||||
|
||||
void SysVirtualInit() {
|
||||
void Init() {
|
||||
pthread_mutexattr_t attr {};
|
||||
|
||||
pthread_mutexattr_init(&attr);
|
||||
@@ -48,19 +47,19 @@ void SysVirtualInit() {
|
||||
g_allocs = new std::map<uintptr_t, size_t>;
|
||||
}
|
||||
|
||||
static int get_protection_flag(VirtualMemory::Mode mode) {
|
||||
static int get_protection_flag(Mode mode) {
|
||||
int protect = PROT_NONE;
|
||||
switch (mode) {
|
||||
case VirtualMemory::Mode::Read: protect = PROT_READ; break;
|
||||
case VirtualMemory::Mode::Write:
|
||||
case VirtualMemory::Mode::ReadWrite: protect = PROT_READ | PROT_WRITE; break; // NOLINT
|
||||
case VirtualMemory::Mode::Execute: protect = PROT_EXEC; break;
|
||||
case VirtualMemory::Mode::ExecuteRead: protect = PROT_EXEC | PROT_READ; break; // NOLINT
|
||||
case VirtualMemory::Mode::ExecuteWrite:
|
||||
case VirtualMemory::Mode::ExecuteReadWrite:
|
||||
case Mode::Read: protect = PROT_READ; break;
|
||||
case Mode::Write:
|
||||
case Mode::ReadWrite: protect = PROT_READ | PROT_WRITE; break; // NOLINT
|
||||
case Mode::Execute: protect = PROT_EXEC; break;
|
||||
case Mode::ExecuteRead: protect = PROT_EXEC | PROT_READ; break; // NOLINT
|
||||
case Mode::ExecuteWrite:
|
||||
case Mode::ExecuteReadWrite:
|
||||
protect = PROT_EXEC | PROT_WRITE | PROT_READ;
|
||||
break; // NOLINT
|
||||
case VirtualMemory::Mode::NoAccess:
|
||||
case Mode::NoAccess:
|
||||
default: protect = PROT_NONE; break;
|
||||
}
|
||||
return protect;
|
||||
@@ -130,7 +129,7 @@ static void* map_anonymous(uintptr_t addr, size_t size, int protect, int flags)
|
||||
return mmap(nullptr, size, protect, flags, -1, 0); // NOLINT
|
||||
}
|
||||
|
||||
uint64_t SysVirtualAlloc(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
|
||||
uint64_t Alloc(uint64_t address, uint64_t size, Mode mode) {
|
||||
EXIT_IF(g_allocs == nullptr);
|
||||
|
||||
auto addr = static_cast<uintptr_t>(address);
|
||||
@@ -154,8 +153,7 @@ static uintptr_t align_up(uintptr_t addr, uint64_t alignment) {
|
||||
return (addr + alignment - 1) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
uint64_t SysVirtualAllocAligned(uint64_t address, uint64_t size, VirtualMemory::Mode mode,
|
||||
uint64_t alignment) {
|
||||
uint64_t AllocAligned(uint64_t address, uint64_t size, Mode mode, uint64_t alignment) {
|
||||
if (alignment == 0) {
|
||||
return 0;
|
||||
}
|
||||
@@ -224,7 +222,7 @@ uint64_t SysVirtualAllocAligned(uint64_t address, uint64_t size, VirtualMemory::
|
||||
}
|
||||
|
||||
if (ptr == MAP_FAILED) {
|
||||
return SysVirtualAllocAligned(address, size, mode, alignment << 1u);
|
||||
return AllocAligned(address, size, mode, alignment << 1u);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&g_virtual_mutex);
|
||||
@@ -281,7 +279,7 @@ static bool is_mapped(void* ptr, size_t length) {
|
||||
}
|
||||
#endif
|
||||
|
||||
bool SysVirtualAllocFixed(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
|
||||
bool AllocFixed(uint64_t address, uint64_t size, Mode mode) {
|
||||
EXIT_IF(g_allocs == nullptr);
|
||||
|
||||
auto addr = static_cast<uintptr_t>(address);
|
||||
@@ -318,15 +316,15 @@ bool SysVirtualAllocFixed(uint64_t address, uint64_t size, VirtualMemory::Mode m
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SysVirtualCommit(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
|
||||
return SysVirtualProtect(address, size, mode);
|
||||
bool Commit(uint64_t address, uint64_t size, Mode mode) {
|
||||
return Protect(address, size, mode);
|
||||
}
|
||||
|
||||
uint64_t SysVirtualReserve(uint64_t address, uint64_t size) {
|
||||
return SysVirtualReserveAligned(address, size, 1);
|
||||
uint64_t Reserve(uint64_t address, uint64_t size) {
|
||||
return ReserveAligned(address, size, 1);
|
||||
}
|
||||
|
||||
uint64_t SysVirtualReserveAligned(uint64_t address, uint64_t size, uint64_t alignment) {
|
||||
uint64_t ReserveAligned(uint64_t address, uint64_t size, uint64_t alignment) {
|
||||
if (alignment == 0) {
|
||||
return 0;
|
||||
}
|
||||
@@ -395,7 +393,7 @@ uint64_t SysVirtualReserveAligned(uint64_t address, uint64_t size, uint64_t alig
|
||||
}
|
||||
|
||||
if (ptr == MAP_FAILED) {
|
||||
return SysVirtualReserveAligned(address, size, alignment << 1u);
|
||||
return ReserveAligned(address, size, alignment << 1u);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&g_virtual_mutex);
|
||||
@@ -405,7 +403,7 @@ uint64_t SysVirtualReserveAligned(uint64_t address, uint64_t size, uint64_t alig
|
||||
return ret_addr;
|
||||
}
|
||||
|
||||
bool SysVirtualReserveFixed(uint64_t address, uint64_t size) {
|
||||
bool ReserveFixed(uint64_t address, uint64_t size) {
|
||||
EXIT_IF(g_allocs == nullptr);
|
||||
|
||||
auto addr = static_cast<uintptr_t>(address);
|
||||
@@ -441,9 +439,9 @@ bool SysVirtualReserveFixed(uint64_t address, uint64_t size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SysVirtualDecommit(uint64_t address, uint64_t size) {
|
||||
bool Decommit(uint64_t address, uint64_t size) {
|
||||
// Drop physical pages while preserving the reservation.
|
||||
if (!SysVirtualProtect(address, size, VirtualMemory::Mode::NoAccess)) {
|
||||
if (!Protect(address, size, Mode::NoAccess)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -467,7 +465,7 @@ bool SysVirtualDecommit(uint64_t address, uint64_t size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SysVirtualFree(uint64_t address) {
|
||||
bool Free(uint64_t address) {
|
||||
EXIT_IF(g_allocs == nullptr);
|
||||
size_t size = 0;
|
||||
|
||||
@@ -487,7 +485,7 @@ bool SysVirtualFree(uint64_t address) {
|
||||
return munmap(reinterpret_cast<void*>(addr), size) == 0;
|
||||
}
|
||||
|
||||
bool SysVirtualFreeRange(uint64_t address, uint64_t size) {
|
||||
bool FreeRange(uint64_t address, uint64_t size) {
|
||||
EXIT_IF(g_allocs == nullptr);
|
||||
if (size == 0 || (address & 0xfffu) != 0 || (size & 0xfffu) != 0) {
|
||||
return false;
|
||||
@@ -543,7 +541,7 @@ bool SysVirtualFreeRange(uint64_t address, uint64_t size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
|
||||
bool Protect(uint64_t address, uint64_t size, Mode mode) {
|
||||
const auto addr = static_cast<uintptr_t>(address);
|
||||
const auto page_start = addr >> 12u;
|
||||
const auto page_end = (addr + size - 1) >> 12u;
|
||||
@@ -551,10 +549,10 @@ bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode
|
||||
get_protection_flag(mode)) == 0;
|
||||
}
|
||||
|
||||
bool SysVirtualFlushInstructionCache(uint64_t /*address*/, uint64_t /*size*/) {
|
||||
bool FlushInstructionCache(uint64_t /*address*/, uint64_t /*size*/) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
} // namespace Common::VirtualMemory
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
#ifndef KYTY_COMMON_PLATFORM_SYSSWAPBYTEORDER_H_
|
||||
#define KYTY_COMMON_PLATFORM_SYSSWAPBYTEORDER_H_
|
||||
|
||||
#include "common/common.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
inline uint16_t SwapByteOrder16(uint16_t value) {
|
||||
uint16_t hi = value << 8u;
|
||||
uint16_t lo = value >> 8u;
|
||||
return hi | lo;
|
||||
}
|
||||
|
||||
inline uint32_t SwapByteOrder32(uint32_t value) {
|
||||
return __builtin_bswap32(value);
|
||||
}
|
||||
|
||||
inline uint64_t SwapByteOrder64(uint64_t value) {
|
||||
return __builtin_bswap64(value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void SwapByteOrder(T& x) {
|
||||
if (sizeof(x) == 2) {
|
||||
if (std::is_signed_v<T>) {
|
||||
x = static_cast<std::make_signed_t<T>>(
|
||||
SwapByteOrder16(static_cast<std::make_unsigned_t<uint16_t>>(x)));
|
||||
} else {
|
||||
x = SwapByteOrder16(x);
|
||||
}
|
||||
}
|
||||
if (sizeof(x) == 4) {
|
||||
if (std::is_signed_v<T>) {
|
||||
x = static_cast<std::make_signed_t<T>>(
|
||||
SwapByteOrder32(static_cast<std::make_unsigned_t<uint32_t>>(x)));
|
||||
} else {
|
||||
x = SwapByteOrder32(x);
|
||||
}
|
||||
}
|
||||
if (sizeof(x) == 8) {
|
||||
if (std::is_signed_v<T>) {
|
||||
x = static_cast<std::make_signed_t<T>>(
|
||||
SwapByteOrder64(static_cast<std::make_unsigned_t<uint64_t>>(x)));
|
||||
} else {
|
||||
x = SwapByteOrder64(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void NoSwapByteOrder(T& x) {}
|
||||
|
||||
#endif /* KYTY_COMMON_PLATFORM_SYSSWAPBYTEORDER_H_ */
|
||||
@@ -151,10 +151,6 @@ inline void SysTimeTToSystem(time_t t, SysTimeStruct& s) {
|
||||
SysFileToSystemTimeUtc(ft, s);
|
||||
}
|
||||
|
||||
inline time_t SysTimegm(struct tm* tm) {
|
||||
return timegm(tm);
|
||||
}
|
||||
|
||||
inline void SysSystemToFileTimeUtc(const SysTimeStruct& f, SysFileTimeStruct& t) {
|
||||
struct tm i {};
|
||||
|
||||
@@ -165,7 +161,7 @@ inline void SysSystemToFileTimeUtc(const SysTimeStruct& f, SysFileTimeStruct& t)
|
||||
i.tm_min = f.Minute;
|
||||
i.tm_sec = f.Second;
|
||||
|
||||
t.is_invalid = (f.is_invalid || (t.time = SysTimegm(&i)) == static_cast<time_t>(-1));
|
||||
t.is_invalid = (f.is_invalid || (t.time = ::timegm(&i)) == static_cast<time_t>(-1));
|
||||
}
|
||||
|
||||
// Retrieves the current local date and time.
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
#ifndef KYTY_COMMON_PLATFORM_SYSVIRTUAL_H_
|
||||
#define KYTY_COMMON_PLATFORM_SYSVIRTUAL_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/virtualMemory.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
void SysVirtualInit();
|
||||
|
||||
uint64_t SysVirtualAlloc(uint64_t address, uint64_t size, VirtualMemory::Mode mode);
|
||||
uint64_t SysVirtualAllocAligned(uint64_t address, uint64_t size, VirtualMemory::Mode mode,
|
||||
uint64_t alignment);
|
||||
bool SysVirtualAllocFixed(uint64_t address, uint64_t size, VirtualMemory::Mode mode);
|
||||
bool SysVirtualCommit(uint64_t address, uint64_t size, VirtualMemory::Mode mode);
|
||||
uint64_t SysVirtualReserve(uint64_t address, uint64_t size);
|
||||
uint64_t SysVirtualReserveAligned(uint64_t address, uint64_t size, uint64_t alignment);
|
||||
bool SysVirtualReserveFixed(uint64_t address, uint64_t size);
|
||||
bool SysVirtualDecommit(uint64_t address, uint64_t size);
|
||||
bool SysVirtualFree(uint64_t address);
|
||||
bool SysVirtualFreeRange(uint64_t address, uint64_t size);
|
||||
bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode);
|
||||
bool SysVirtualFlushInstructionCache(uint64_t address, uint64_t size);
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#endif /* KYTY_COMMON_PLATFORM_SYSVIRTUAL_H_ */
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
@@ -135,10 +136,6 @@ void SysFileWrite(const void* data, uint32_t size, sys_file_t& f, uint32_t* byte
|
||||
}
|
||||
}
|
||||
|
||||
void SysFileWrite(uint32_t n, sys_file_t& f) {
|
||||
SysFileWrite(&n, 4, f);
|
||||
}
|
||||
|
||||
sys_file_t* SysFileCreate(const std::filesystem::path& file_name) {
|
||||
auto* ret = new sys_file_t;
|
||||
|
||||
@@ -480,18 +477,10 @@ bool SysFileSetLastAccessAndWriteTimeUtc(const std::filesystem::path& name,
|
||||
}
|
||||
|
||||
void SysFileFindFiles(const std::filesystem::path& path, std::vector<sys_file_find_t>& out) {
|
||||
std::string real_path = Common::ReplaceChar(Common::PathToGenericString(path), '\\', '/');
|
||||
if (!Common::EndsWith(real_path, "/")) {
|
||||
real_path += "/";
|
||||
}
|
||||
const auto pattern = path / L"*";
|
||||
|
||||
std::string pattern = real_path + "*";
|
||||
|
||||
HANDLE h = nullptr;
|
||||
WIN32_FIND_DATAW data;
|
||||
|
||||
auto wide_pattern = std::filesystem::path(pattern).wstring();
|
||||
h = FindFirstFileW(wide_pattern.c_str(), &data);
|
||||
WIN32_FIND_DATAW data {};
|
||||
HANDLE h = FindFirstFileW(pattern.c_str(), &data);
|
||||
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
@@ -499,18 +488,19 @@ void SysFileFindFiles(const std::filesystem::path& path, std::vector<sys_file_fi
|
||||
|
||||
do {
|
||||
std::filesystem::path file_name(data.cFileName);
|
||||
auto file_name_str = Common::PathToString(file_name);
|
||||
|
||||
if (file_name_str == "." || file_name_str == "..") {
|
||||
if (file_name == L"." || file_name == L"..") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto full_path = path / file_name;
|
||||
|
||||
if ((data.dwFileAttributes & static_cast<DWORD>(FILE_ATTRIBUTE_DIRECTORY)) != 0u) {
|
||||
SysFileFindFiles(std::filesystem::path(real_path) / file_name, out);
|
||||
SysFileFindFiles(full_path, out);
|
||||
} else {
|
||||
sys_file_find_t r {};
|
||||
|
||||
r.path_with_name = std::filesystem::path(real_path) / file_name;
|
||||
r.path_with_name = full_path;
|
||||
r.size = (static_cast<uint64_t>(data.nFileSizeHigh) << 32u) +
|
||||
static_cast<uint64_t>(data.nFileSizeLow);
|
||||
r.last_access_time.is_invalid = false;
|
||||
@@ -518,7 +508,7 @@ void SysFileFindFiles(const std::filesystem::path& path, std::vector<sys_file_fi
|
||||
r.last_write_time.is_invalid = false;
|
||||
r.last_write_time.time = data.ftLastWriteTime;
|
||||
|
||||
out.push_back(r);
|
||||
out.push_back(std::move(r));
|
||||
}
|
||||
|
||||
} while (FindNextFileW(h, &data) != 0);
|
||||
@@ -527,18 +517,10 @@ void SysFileFindFiles(const std::filesystem::path& path, std::vector<sys_file_fi
|
||||
}
|
||||
|
||||
void SysFileGetDents(const std::filesystem::path& path, std::vector<sys_dir_entry_t>& out) {
|
||||
std::string real_path = Common::ReplaceChar(Common::PathToGenericString(path), '\\', '/');
|
||||
if (!Common::EndsWith(real_path, "/")) {
|
||||
real_path += "/";
|
||||
}
|
||||
const auto pattern = path / L"*";
|
||||
|
||||
std::string pattern = real_path + "*";
|
||||
|
||||
HANDLE h = nullptr;
|
||||
WIN32_FIND_DATAW data;
|
||||
|
||||
auto wide_pattern = std::filesystem::path(pattern).wstring();
|
||||
h = FindFirstFileW(wide_pattern.c_str(), &data);
|
||||
WIN32_FIND_DATAW data {};
|
||||
HANDLE h = FindFirstFileW(pattern.c_str(), &data);
|
||||
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
@@ -552,7 +534,7 @@ void SysFileGetDents(const std::filesystem::path& path, std::vector<sys_dir_entr
|
||||
r.is_file = ((data.dwFileAttributes & static_cast<DWORD>(FILE_ATTRIBUTE_DIRECTORY)) == 0u);
|
||||
r.name = Common::PathToString(file_name);
|
||||
|
||||
out.push_back(r);
|
||||
out.push_back(std::move(r));
|
||||
|
||||
} while (FindNextFileW(h, &data) != 0);
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <windows.h> // IWYU pragma: keep
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/platform/sysVirtual.h"
|
||||
#include "common/virtualMemory.h"
|
||||
|
||||
// IWYU pragma: no_include <basetsd.h>
|
||||
@@ -22,33 +21,33 @@
|
||||
// IWYU pragma: no_include <winerror.h>
|
||||
// IWYU pragma: no_include <wtypes.h>
|
||||
|
||||
namespace Common {
|
||||
namespace Common::VirtualMemory {
|
||||
|
||||
static DWORD GetProtectionFlag(VirtualMemory::Mode mode) {
|
||||
static DWORD GetProtectionFlag(Mode mode) {
|
||||
DWORD protect = PAGE_NOACCESS;
|
||||
switch (mode) {
|
||||
case VirtualMemory::Mode::Read: protect = PAGE_READONLY; break;
|
||||
case Mode::Read: protect = PAGE_READONLY; break;
|
||||
|
||||
case VirtualMemory::Mode::Write:
|
||||
case VirtualMemory::Mode::ReadWrite: protect = PAGE_READWRITE; break;
|
||||
case Mode::Write:
|
||||
case Mode::ReadWrite: protect = PAGE_READWRITE; break;
|
||||
|
||||
case VirtualMemory::Mode::Execute: protect = PAGE_EXECUTE; break;
|
||||
case Mode::Execute: protect = PAGE_EXECUTE; break;
|
||||
|
||||
case VirtualMemory::Mode::ExecuteRead: protect = PAGE_EXECUTE_READ; break;
|
||||
case Mode::ExecuteRead: protect = PAGE_EXECUTE_READ; break;
|
||||
|
||||
case VirtualMemory::Mode::ExecuteWrite:
|
||||
case VirtualMemory::Mode::ExecuteReadWrite: protect = PAGE_EXECUTE_READWRITE; break;
|
||||
case Mode::ExecuteWrite:
|
||||
case Mode::ExecuteReadWrite: protect = PAGE_EXECUTE_READWRITE; break;
|
||||
|
||||
case VirtualMemory::Mode::NoAccess:
|
||||
case Mode::NoAccess:
|
||||
default: protect = PAGE_NOACCESS; break;
|
||||
}
|
||||
return protect;
|
||||
}
|
||||
|
||||
void SysVirtualInit() {}
|
||||
void Init() {}
|
||||
|
||||
uint64_t SysVirtualAlloc(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
|
||||
auto ptr = (address == 0 ? SysVirtualAllocAligned(address, size, mode, 1)
|
||||
uint64_t Alloc(uint64_t address, uint64_t size, Mode mode) {
|
||||
auto ptr = (address == 0 ? AllocAligned(address, size, mode, 1)
|
||||
: reinterpret_cast<uintptr_t>(VirtualAlloc(
|
||||
reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
|
||||
static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE),
|
||||
@@ -59,7 +58,7 @@ uint64_t SysVirtualAlloc(uint64_t address, uint64_t size, VirtualMemory::Mode mo
|
||||
if (err != ERROR_INVALID_ADDRESS) {
|
||||
printf("VirtualAlloc() failed: 0x%08" PRIx32 "\n", err);
|
||||
} else {
|
||||
return SysVirtualAllocAligned(address, size, mode, 1);
|
||||
return AllocAligned(address, size, mode, 1);
|
||||
}
|
||||
}
|
||||
return ptr;
|
||||
@@ -80,8 +79,7 @@ static uint64_t AlignUp(uint64_t addr, uint64_t alignment) {
|
||||
return (addr + alignment - 1) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
uint64_t SysVirtualAllocAligned(uint64_t address, uint64_t size, VirtualMemory::Mode mode,
|
||||
uint64_t alignment) {
|
||||
uint64_t AllocAligned(uint64_t address, uint64_t size, Mode mode, uint64_t alignment) {
|
||||
if (alignment == 0) {
|
||||
printf("VirtualAlloc2 failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
|
||||
return 0;
|
||||
@@ -135,13 +133,13 @@ uint64_t SysVirtualAllocAligned(uint64_t address, uint64_t size, VirtualMemory::
|
||||
printf("VirtualAlloc2(alignment = 0x%016" PRIx64 ") failed: 0x%08" PRIx32 "\n",
|
||||
alignment, err);
|
||||
} else {
|
||||
return SysVirtualAllocAligned(address, size, mode, alignment << 1u);
|
||||
return AllocAligned(address, size, mode, alignment << 1u);
|
||||
}
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
bool SysVirtualAllocFixed(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
|
||||
bool AllocFixed(uint64_t address, uint64_t size, Mode mode) {
|
||||
auto ptr = reinterpret_cast<uintptr_t>(VirtualAlloc(
|
||||
reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
|
||||
static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE), GetProtectionFlag(mode)));
|
||||
@@ -161,7 +159,7 @@ bool SysVirtualAllocFixed(uint64_t address, uint64_t size, VirtualMemory::Mode m
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SysVirtualCommit(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
|
||||
bool Commit(uint64_t address, uint64_t size, Mode mode) {
|
||||
auto ptr = reinterpret_cast<uintptr_t>(
|
||||
VirtualAlloc(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
|
||||
static_cast<DWORD>(MEM_COMMIT), GetProtectionFlag(mode)));
|
||||
@@ -179,8 +177,8 @@ bool SysVirtualCommit(uint64_t address, uint64_t size, VirtualMemory::Mode mode)
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t SysVirtualReserve(uint64_t address, uint64_t size) {
|
||||
auto ptr = (address == 0 ? SysVirtualReserveAligned(address, size, 1)
|
||||
uint64_t Reserve(uint64_t address, uint64_t size) {
|
||||
auto ptr = (address == 0 ? ReserveAligned(address, size, 1)
|
||||
: reinterpret_cast<uintptr_t>(VirtualAlloc(
|
||||
reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
|
||||
static_cast<DWORD>(MEM_RESERVE), PAGE_NOACCESS)));
|
||||
@@ -190,13 +188,13 @@ uint64_t SysVirtualReserve(uint64_t address, uint64_t size) {
|
||||
if (err != ERROR_INVALID_ADDRESS) {
|
||||
printf("VirtualAlloc(MEM_RESERVE) failed: 0x%08" PRIx32 "\n", err);
|
||||
} else {
|
||||
return SysVirtualReserveAligned(address, size, 1);
|
||||
return ReserveAligned(address, size, 1);
|
||||
}
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
uint64_t SysVirtualReserveAligned(uint64_t address, uint64_t size, uint64_t alignment) {
|
||||
uint64_t ReserveAligned(uint64_t address, uint64_t size, uint64_t alignment) {
|
||||
if (alignment == 0) {
|
||||
printf("VirtualAlloc2(MEM_RESERVE) failed: 0x%08" PRIx32 "\n",
|
||||
static_cast<uint32_t>(GetLastError()));
|
||||
@@ -250,13 +248,13 @@ uint64_t SysVirtualReserveAligned(uint64_t address, uint64_t size, uint64_t alig
|
||||
"\n",
|
||||
alignment, err);
|
||||
} else {
|
||||
return SysVirtualReserveAligned(address, size, alignment << 1u);
|
||||
return ReserveAligned(address, size, alignment << 1u);
|
||||
}
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
bool SysVirtualReserveFixed(uint64_t address, uint64_t size) {
|
||||
bool ReserveFixed(uint64_t address, uint64_t size) {
|
||||
auto ptr = reinterpret_cast<uintptr_t>(
|
||||
VirtualAlloc(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
|
||||
static_cast<DWORD>(MEM_RESERVE), PAGE_NOACCESS));
|
||||
@@ -276,7 +274,7 @@ bool SysVirtualReserveFixed(uint64_t address, uint64_t size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SysVirtualDecommit(uint64_t address, uint64_t size) {
|
||||
bool Decommit(uint64_t address, uint64_t size) {
|
||||
if (VirtualFree(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
|
||||
MEM_DECOMMIT) == 0) {
|
||||
printf("VirtualFree(MEM_DECOMMIT) failed: 0x%08" PRIx32 "\n",
|
||||
@@ -286,7 +284,7 @@ bool SysVirtualDecommit(uint64_t address, uint64_t size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SysVirtualFree(uint64_t address) {
|
||||
bool Free(uint64_t address) {
|
||||
if (VirtualFree(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), 0, MEM_RELEASE) ==
|
||||
0) {
|
||||
printf("VirtualFree() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
|
||||
@@ -295,7 +293,7 @@ bool SysVirtualFree(uint64_t address) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SysVirtualFreeRange(uint64_t address, uint64_t size) {
|
||||
bool FreeRange(uint64_t address, uint64_t size) {
|
||||
if (address == 0 || size == 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -313,10 +311,10 @@ bool SysVirtualFreeRange(uint64_t address, uint64_t size) {
|
||||
}
|
||||
current = next;
|
||||
}
|
||||
return current - address == size && SysVirtualFree(address);
|
||||
return current - address == size && Free(address);
|
||||
}
|
||||
|
||||
bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
|
||||
bool Protect(uint64_t address, uint64_t size, Mode mode) {
|
||||
DWORD old_protect = 0;
|
||||
if (VirtualProtect(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
|
||||
GetProtectionFlag(mode), &old_protect) == 0) {
|
||||
@@ -326,7 +324,7 @@ bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SysVirtualFlushInstructionCache(uint64_t address, uint64_t size) {
|
||||
bool FlushInstructionCache(uint64_t address, uint64_t size) {
|
||||
if (::FlushInstructionCache(GetCurrentProcess(),
|
||||
reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)),
|
||||
size) == 0) {
|
||||
@@ -337,6 +335,6 @@ bool SysVirtualFlushInstructionCache(uint64_t address, uint64_t size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
} // namespace Common::VirtualMemory
|
||||
|
||||
#endif
|
||||
|
||||
+8
-178
@@ -1,7 +1,6 @@
|
||||
#ifndef KYTY_COMMON_STRING_UTILS_H_
|
||||
#define KYTY_COMMON_STRING_UTILS_H_
|
||||
|
||||
#include "common/byteBuffer.h"
|
||||
#include "common/common.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -9,7 +8,6 @@
|
||||
#include <charconv>
|
||||
#include <codecvt>
|
||||
#include <filesystem>
|
||||
#include <fmt/format.h>
|
||||
#include <locale>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -18,8 +16,6 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
constexpr uint32_t FIND_INVALID_INDEX = static_cast<uint32_t>(-1);
|
||||
|
||||
inline std::string PathToString(const std::filesystem::path& path) {
|
||||
#if defined(__cpp_char8_t)
|
||||
auto u8 = path.u8string();
|
||||
@@ -38,8 +34,12 @@ inline std::string PathToGenericString(const std::filesystem::path& path) {
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool IsSpace(char c) {
|
||||
return std::isspace(static_cast<unsigned char>(c)) != 0;
|
||||
inline std::filesystem::path PathFromUtf8(std::string_view text) {
|
||||
#if defined(__cpp_char8_t)
|
||||
return std::filesystem::path(std::u8string(text.begin(), text.end()));
|
||||
#else
|
||||
return std::filesystem::u8path(text.begin(), text.end());
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool EqualNoCase(std::string_view lhs, std::string_view rhs) {
|
||||
@@ -63,65 +63,6 @@ inline std::string ToLower(std::string_view text) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline bool ContainsStr(std::string_view text, std::string_view needle) {
|
||||
return text.find(needle) != std::string_view::npos;
|
||||
}
|
||||
|
||||
inline bool StartsWith(std::string_view text, std::string_view prefix) {
|
||||
return text.starts_with(prefix);
|
||||
}
|
||||
|
||||
inline bool StartsWith(std::string_view text, char prefix) {
|
||||
return !text.empty() && text.front() == prefix;
|
||||
}
|
||||
|
||||
inline bool EndsWith(std::string_view text, std::string_view suffix) {
|
||||
return text.ends_with(suffix);
|
||||
}
|
||||
|
||||
inline bool EndsWith(std::string_view text, char suffix) {
|
||||
return !text.empty() && text.back() == suffix;
|
||||
}
|
||||
|
||||
inline bool ContainsChar(std::string_view text, char needle) {
|
||||
return text.find(needle) != std::string_view::npos;
|
||||
}
|
||||
|
||||
inline bool IndexValid(std::string_view text, uint32_t index) {
|
||||
return index < text.size();
|
||||
}
|
||||
|
||||
inline uint32_t FindIndex(std::string_view text, std::string_view needle, size_t from = 0) {
|
||||
const auto pos = text.find(needle, from);
|
||||
return pos == std::string_view::npos ? FIND_INVALID_INDEX : static_cast<uint32_t>(pos);
|
||||
}
|
||||
|
||||
inline uint32_t FindIndex(std::string_view text, char needle, size_t from = 0) {
|
||||
const auto pos = text.find(needle, from);
|
||||
return pos == std::string_view::npos ? FIND_INVALID_INDEX : static_cast<uint32_t>(pos);
|
||||
}
|
||||
|
||||
inline uint32_t FindLastIndex(std::string_view text, std::string_view needle,
|
||||
size_t from = FIND_INVALID_INDEX) {
|
||||
if (needle.empty()) {
|
||||
return 0;
|
||||
}
|
||||
if (from == FIND_INVALID_INDEX || from > text.size()) {
|
||||
from = text.size();
|
||||
}
|
||||
const auto pos = text.rfind(needle, from);
|
||||
return pos == std::string_view::npos ? FIND_INVALID_INDEX : static_cast<uint32_t>(pos);
|
||||
}
|
||||
|
||||
inline uint32_t FindLastIndex(std::string_view text, char needle,
|
||||
size_t from = FIND_INVALID_INDEX) {
|
||||
if (from == FIND_INVALID_INDEX || from > text.size()) {
|
||||
from = text.size();
|
||||
}
|
||||
const auto pos = text.rfind(needle, from);
|
||||
return pos == std::string_view::npos ? FIND_INVALID_INDEX : static_cast<uint32_t>(pos);
|
||||
}
|
||||
|
||||
inline std::string Mid(std::string_view text, size_t first, size_t count = std::string_view::npos) {
|
||||
if (first >= text.size()) {
|
||||
return {};
|
||||
@@ -129,49 +70,18 @@ inline std::string Mid(std::string_view text, size_t first, size_t count = std::
|
||||
return std::string(text.substr(first, count));
|
||||
}
|
||||
|
||||
inline std::string Left(std::string_view text, size_t count) {
|
||||
return Mid(text, 0, count);
|
||||
}
|
||||
|
||||
inline std::string Right(std::string_view text, size_t count) {
|
||||
if (count >= text.size()) {
|
||||
return std::string(text);
|
||||
}
|
||||
return Mid(text, text.size() - count, count);
|
||||
}
|
||||
|
||||
inline std::string RemoveLast(std::string_view text, size_t count) {
|
||||
if (count >= text.size()) {
|
||||
return {};
|
||||
}
|
||||
return Left(text, text.size() - count);
|
||||
return std::string(text.substr(0, text.size() - count));
|
||||
}
|
||||
|
||||
inline std::string RemoveFirst(std::string_view text, size_t count) {
|
||||
if (count >= text.size()) {
|
||||
return {};
|
||||
}
|
||||
return Right(text, text.size() - count);
|
||||
}
|
||||
|
||||
inline std::string TrimRight(std::string_view text) {
|
||||
size_t end = text.size();
|
||||
while (end > 0 && IsSpace(text[end - 1])) {
|
||||
end--;
|
||||
}
|
||||
return std::string(text.substr(0, end));
|
||||
}
|
||||
|
||||
inline std::string TrimLeft(std::string_view text) {
|
||||
size_t first = 0;
|
||||
while (first < text.size() && IsSpace(text[first])) {
|
||||
first++;
|
||||
}
|
||||
return std::string(text.substr(first));
|
||||
}
|
||||
|
||||
inline std::string Trim(std::string_view text) {
|
||||
return TrimRight(TrimLeft(text));
|
||||
return std::string(text.substr(count));
|
||||
}
|
||||
|
||||
inline std::string ReplaceChar(std::string_view text, char old_char, char new_char) {
|
||||
@@ -204,16 +114,6 @@ inline std::string FilenameWithoutDirectory(std::string_view text) {
|
||||
return pos == std::string_view::npos ? std::string(text) : std::string(text.substr(pos + 1));
|
||||
}
|
||||
|
||||
inline std::string FilenameWithoutExtension(std::string_view text) {
|
||||
const auto pos = text.find_last_of('.');
|
||||
return pos == std::string_view::npos ? std::string(text) : std::string(text.substr(0, pos));
|
||||
}
|
||||
|
||||
inline std::string ExtensionWithoutFilename(std::string_view text) {
|
||||
const auto pos = text.find_last_of('.');
|
||||
return pos == std::string_view::npos ? std::string() : std::string(text.substr(pos));
|
||||
}
|
||||
|
||||
inline std::string FixFilenameSlash(std::string_view text) {
|
||||
return ReplaceChar(text, '\\', '/');
|
||||
}
|
||||
@@ -257,65 +157,12 @@ inline std::vector<std::string> Split(std::string_view text, char sep, bool keep
|
||||
return Split(text, std::string_view(&sep, 1), keep_empty);
|
||||
}
|
||||
|
||||
inline std::string Concat(const std::vector<std::string>& list, std::string_view sep) {
|
||||
std::string ret;
|
||||
for (size_t i = 0; i < list.size(); i++) {
|
||||
if (i != 0) {
|
||||
ret += sep;
|
||||
}
|
||||
ret += list[i];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline std::string Concat(const std::vector<std::string>& list, char sep) {
|
||||
return Concat(list, std::string_view(&sep, 1));
|
||||
}
|
||||
|
||||
inline uint32_t ToUint32(std::string_view text, int base = 10) {
|
||||
uint32_t value = 0;
|
||||
std::from_chars(text.data(), text.data() + text.size(), value, base);
|
||||
return value;
|
||||
}
|
||||
|
||||
inline uint64_t ToUint64(std::string_view text, int base = 10) {
|
||||
uint64_t value = 0;
|
||||
std::from_chars(text.data(), text.data() + text.size(), value, base);
|
||||
return value;
|
||||
}
|
||||
|
||||
inline int32_t ToInt32(std::string_view text, int base = 10) {
|
||||
int32_t value = 0;
|
||||
std::from_chars(text.data(), text.data() + text.size(), value, base);
|
||||
return value;
|
||||
}
|
||||
|
||||
inline int64_t ToInt64(std::string_view text, int base = 10) {
|
||||
int64_t value = 0;
|
||||
std::from_chars(text.data(), text.data() + text.size(), value, base);
|
||||
return value;
|
||||
}
|
||||
|
||||
inline double ToDouble(std::string_view text) {
|
||||
return std::stod(std::string(text));
|
||||
}
|
||||
|
||||
inline float ToFloat(std::string_view text) {
|
||||
return std::stof(std::string(text));
|
||||
}
|
||||
|
||||
inline std::string SafeCsv(std::string_view text) {
|
||||
const bool add_space =
|
||||
!text.empty() && (text.front() == '+' || text.front() == '=' || text.front() == '-');
|
||||
if (text.find('"') != std::string_view::npos || text.find(';') != std::string_view::npos ||
|
||||
text.find('+') != std::string_view::npos || text.find('=') != std::string_view::npos ||
|
||||
text.find('-') != std::string_view::npos) {
|
||||
return "\"" + std::string(add_space ? " " : "") +
|
||||
ReplaceStr(std::string(text), "\"", "\"\"") + "\"";
|
||||
}
|
||||
return std::string(text);
|
||||
}
|
||||
|
||||
inline std::string Utf16ToUtf8(std::u16string_view utf16) {
|
||||
if (utf16.empty()) {
|
||||
return {};
|
||||
@@ -324,23 +171,6 @@ inline std::string Utf16ToUtf8(std::u16string_view utf16) {
|
||||
return convert.to_bytes(utf16.data(), utf16.data() + utf16.size());
|
||||
}
|
||||
|
||||
inline ByteBuffer HexToBin(std::string_view text) {
|
||||
ByteBuffer ret;
|
||||
for (size_t i = 0; i + 1 < text.size(); i += 2) {
|
||||
ret.Add(static_cast<Byte>((ToUint32(text.substr(i, 2), 16) & 0xffu)));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline std::string HexFromBin(const ByteBuffer& bin) {
|
||||
std::string ret;
|
||||
ret.reserve(bin.Size() * 2);
|
||||
for (uint32_t i = 0; i < bin.Size(); i++) {
|
||||
ret += fmt::format("{:02X}", std::to_integer<uint8_t>(bin.At(i)));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#endif /* KYTY_COMMON_STRING_UTILS_H_ */
|
||||
|
||||
@@ -253,10 +253,6 @@ void Thread::Detach() {
|
||||
m_thread->m_thread.detach();
|
||||
}
|
||||
|
||||
void Thread::Sleep(uint32_t millis) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(millis));
|
||||
}
|
||||
|
||||
void Thread::SleepMicro(uint32_t micros) {
|
||||
#ifdef KYTY_WIN_CS
|
||||
SleepHighResolution100ns(static_cast<uint64_t>(micros) * 10);
|
||||
|
||||
@@ -31,7 +31,6 @@ public:
|
||||
// The id is unique and can't be reused by another thread.
|
||||
[[nodiscard]] int GetUniqueId() const;
|
||||
|
||||
static void Sleep(uint32_t millis);
|
||||
static void SleepMicro(uint32_t micros);
|
||||
static void SleepNano(uint64_t nanos);
|
||||
static bool IsMainThread();
|
||||
|
||||
@@ -62,23 +62,6 @@ double Timer::GetTimeS() const {
|
||||
return (static_cast<double>(current_time - m_StartTime)) / static_cast<double>(m_Frequency);
|
||||
}
|
||||
|
||||
// return time in ticks
|
||||
uint64_t Timer::GetTicks() const {
|
||||
if (m_is_paused) {
|
||||
return (m_PauseTime - m_StartTime);
|
||||
}
|
||||
|
||||
uint64_t current_time = 0;
|
||||
SysQueryPerformanceCounter(¤t_time);
|
||||
|
||||
return (current_time - m_StartTime);
|
||||
}
|
||||
|
||||
// return ticks frequency
|
||||
uint64_t Timer::GetFrequency() const {
|
||||
return m_Frequency;
|
||||
}
|
||||
|
||||
uint64_t Timer::QueryPerformanceFrequency() {
|
||||
uint64_t ret = 0;
|
||||
SysQueryPerformanceFrequency(&ret);
|
||||
|
||||
@@ -24,12 +24,6 @@ public:
|
||||
// return time in seconds
|
||||
[[nodiscard]] double GetTimeS() const;
|
||||
|
||||
// return time in ticks
|
||||
[[nodiscard]] uint64_t GetTicks() const;
|
||||
|
||||
// return ticks frequency
|
||||
[[nodiscard]] uint64_t GetFrequency() const;
|
||||
|
||||
KYTY_CLASS_NO_COPY(Timer);
|
||||
|
||||
[[nodiscard]] static uint64_t QueryPerformanceFrequency();
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
#include "common/virtualMemory.h"
|
||||
|
||||
#include "common/platform/sysVirtual.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
namespace VirtualMemory {
|
||||
|
||||
void Init() {
|
||||
SysVirtualInit();
|
||||
}
|
||||
|
||||
uint64_t Alloc(uint64_t address, uint64_t size, Mode mode) {
|
||||
return SysVirtualAlloc(address, size, mode);
|
||||
}
|
||||
|
||||
uint64_t AllocAligned(uint64_t address, uint64_t size, Mode mode, uint64_t alignment) {
|
||||
return SysVirtualAllocAligned(address, size, mode, alignment);
|
||||
}
|
||||
|
||||
bool AllocFixed(uint64_t address, uint64_t size, Mode mode) {
|
||||
return SysVirtualAllocFixed(address, size, mode);
|
||||
}
|
||||
|
||||
bool Commit(uint64_t address, uint64_t size, Mode mode) {
|
||||
return SysVirtualCommit(address, size, mode);
|
||||
}
|
||||
|
||||
uint64_t Reserve(uint64_t address, uint64_t size) {
|
||||
return SysVirtualReserve(address, size);
|
||||
}
|
||||
|
||||
uint64_t ReserveAligned(uint64_t address, uint64_t size, uint64_t alignment) {
|
||||
return SysVirtualReserveAligned(address, size, alignment);
|
||||
}
|
||||
|
||||
bool ReserveFixed(uint64_t address, uint64_t size) {
|
||||
return SysVirtualReserveFixed(address, size);
|
||||
}
|
||||
|
||||
bool Decommit(uint64_t address, uint64_t size) {
|
||||
return SysVirtualDecommit(address, size);
|
||||
}
|
||||
|
||||
bool Free(uint64_t address) {
|
||||
return SysVirtualFree(address);
|
||||
}
|
||||
|
||||
bool FreeRange(uint64_t address, uint64_t size) {
|
||||
return SysVirtualFreeRange(address, size);
|
||||
}
|
||||
|
||||
bool Protect(uint64_t address, uint64_t size, Mode mode) {
|
||||
return SysVirtualProtect(address, size, mode);
|
||||
}
|
||||
|
||||
bool FlushInstructionCache(uint64_t address, uint64_t size) {
|
||||
return SysVirtualFlushInstructionCache(address, size);
|
||||
}
|
||||
|
||||
} // namespace VirtualMemory
|
||||
|
||||
} // namespace Common
|
||||
@@ -386,8 +386,12 @@ void CommandProcessor::WriteReferenceClock(uint64_t dst_address, uint32_t num_by
|
||||
}
|
||||
const auto value = Sync::ReadReferenceClock();
|
||||
std::memcpy(reinterpret_cast<void*>(dst_address), &value, num_bytes);
|
||||
LOGF("\t copy_data reference clock: dst=0x%016" PRIx64 " value=0x%016" PRIx64 " size=%u\n",
|
||||
dst_address, value, num_bytes);
|
||||
static std::atomic<uint32_t> clock_log_count {0};
|
||||
if (clock_log_count.fetch_add(1) < 64) {
|
||||
LOGF("\t copy_data reference clock: dst=0x%016" PRIx64 " value=0x%016" PRIx64
|
||||
" size=%u\n",
|
||||
dst_address, value, num_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandProcessor::DmaData(uint8_t engine, uint8_t dst_sel, uint8_t dst_cache_policy,
|
||||
|
||||
@@ -4,15 +4,12 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/profiler.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
#include "graphics/guest_gpu/gpu_format.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <fmt/format.h>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
@@ -1322,13 +1319,8 @@ void TileGetTextureSize(Prospero::BufferFormat format, uint32_t width, uint32_t
|
||||
return;
|
||||
}
|
||||
if (total_size != nullptr && total_size->size == 0) {
|
||||
std::vector<std::string> list;
|
||||
list.push_back(fmt::format("format = {}", static_cast<uint32_t>(format)));
|
||||
list.push_back(fmt::format("width = {}", width));
|
||||
list.push_back(fmt::format("height = {}", height));
|
||||
list.push_back(fmt::format("levels = {}", levels));
|
||||
list.push_back(fmt::format("tile = {}", static_cast<uint32_t>(tile)));
|
||||
EXIT("unknown format:\n%s\n", Common::Concat(list, '\n').c_str());
|
||||
EXIT("unknown format:\nformat = %u\nwidth = %u\nheight = %u\nlevels = %u\ntile = %u\n",
|
||||
static_cast<uint32_t>(format), width, height, levels, static_cast<uint32_t>(tile));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <windows.h>
|
||||
#undef min
|
||||
#undef max
|
||||
#undef MemoryBarrier
|
||||
#elif defined(__APPLE__)
|
||||
#include <pthread.h>
|
||||
#elif defined(__linux__)
|
||||
|
||||
@@ -593,6 +593,21 @@ PipelineCache::GraphicsPrograms PipelineCache::GetGraphicsPrograms(
|
||||
ShaderParams pixel_params;
|
||||
if (pixel_active) {
|
||||
pixel_params = PrepareProgram(pixel_regs, sh, target_export_mapping, pixel_info);
|
||||
const auto& blend = context.GetBlendControl(0);
|
||||
const auto is_dual_source = [](uint8_t factor) {
|
||||
return factor >= static_cast<uint8_t>(Prospero::BlendFactor::kSrc1Color) &&
|
||||
factor <= static_cast<uint8_t>(Prospero::BlendFactor::kOneMinusSrc1Alpha);
|
||||
};
|
||||
pixel_info.dual_source_blending =
|
||||
blend.enable && !context.GetRenderTarget(0).info.blend_bypass &&
|
||||
(is_dual_source(blend.color_srcblend) || is_dual_source(blend.color_destblend) ||
|
||||
(blend.separate_alpha_blend &&
|
||||
(is_dual_source(blend.alpha_srcblend) || is_dual_source(blend.alpha_destblend))));
|
||||
if (pixel_info.dual_source_blending) {
|
||||
// MRT1 supplies a second blend source for the same render target as MRT0.
|
||||
pixel_info.target_output_mode[1] = pixel_info.target_output_mode[0];
|
||||
pixel_info.target_export_mapping[1] = pixel_info.target_export_mapping[0];
|
||||
}
|
||||
}
|
||||
if (context.GetClipControl().clip_disable) {
|
||||
const auto& viewport = context.GetScreenViewport().viewports[0];
|
||||
|
||||
@@ -46,8 +46,8 @@ vk::PipelineStageFlags ShaderPipelineStages(vk::ShaderStageFlags stages) {
|
||||
return result;
|
||||
}
|
||||
|
||||
VulkanMemoryBarrier MakeShaderWriteDependency() {
|
||||
VulkanMemoryBarrier barrier {};
|
||||
vk::MemoryBarrier MakeShaderWriteDependency() {
|
||||
vk::MemoryBarrier barrier {};
|
||||
barrier.srcAccessMask = vk::AccessFlagBits::eShaderWrite;
|
||||
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite |
|
||||
vk::AccessFlagBits::eVertexAttributeRead |
|
||||
@@ -58,15 +58,15 @@ VulkanMemoryBarrier MakeShaderWriteDependency() {
|
||||
return barrier;
|
||||
}
|
||||
|
||||
VulkanMemoryBarrier MakeShaderAccessDependency() {
|
||||
VulkanMemoryBarrier barrier {};
|
||||
vk::MemoryBarrier MakeShaderAccessDependency() {
|
||||
vk::MemoryBarrier barrier {};
|
||||
barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
|
||||
barrier.dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
|
||||
return barrier;
|
||||
}
|
||||
|
||||
VulkanMemoryBarrier MakeShaderWriteHazardDependency() {
|
||||
VulkanMemoryBarrier barrier {};
|
||||
vk::MemoryBarrier MakeShaderWriteHazardDependency() {
|
||||
vk::MemoryBarrier barrier {};
|
||||
barrier.srcAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
|
||||
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
|
||||
return barrier;
|
||||
|
||||
@@ -10,9 +10,9 @@ struct ShaderStageRuntime;
|
||||
|
||||
vk::ShaderStageFlagBits NativeShaderStage(ShaderType stage);
|
||||
vk::PipelineStageFlags ShaderPipelineStages(vk::ShaderStageFlags stages);
|
||||
VulkanMemoryBarrier MakeShaderAccessDependency();
|
||||
VulkanMemoryBarrier MakeShaderWriteHazardDependency();
|
||||
VulkanMemoryBarrier MakeShaderWriteDependency();
|
||||
vk::MemoryBarrier MakeShaderAccessDependency();
|
||||
vk::MemoryBarrier MakeShaderWriteHazardDependency();
|
||||
vk::MemoryBarrier MakeShaderWriteDependency();
|
||||
vk::BufferMemoryBarrier MakeGdsDependency(vk::Buffer buffer);
|
||||
bool HasShaderBufferWrites(const ShaderStageRuntime& runtime);
|
||||
void ShaderAccessBarrier(vk::CommandBuffer vk_buffer, vk::PipelineStageFlags source_stages);
|
||||
|
||||
@@ -225,7 +225,7 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ShaderAddressValid(sh_ctx.GetCs().cs_regs.data_addr)) {
|
||||
if (sh_ctx.GetCs().cs_regs.data_addr == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -421,7 +421,7 @@ static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuf
|
||||
static bool DrawHasValidVertexShader(const HW::Shader& sh_ctx) {
|
||||
|
||||
const auto& vs = sh_ctx.GetVs();
|
||||
return ShaderAddressValid(vs.es_regs.data_addr);
|
||||
return vs.es_regs.data_addr != 0;
|
||||
}
|
||||
|
||||
static bool PixelShaderHasDepthOrCoverageSideEffects(const HW::ShaderRegisters& sh_regs) {
|
||||
@@ -608,7 +608,7 @@ static bool DrawHasActivePixelShader(const CommandBuffer& buffer) {
|
||||
const auto& ctx = buffer.GetRegisters();
|
||||
const auto& sh_regs = ctx.GetShaderRegisters();
|
||||
const bool has_color_output = (ctx.GetRenderTargetMask() & sh_regs.m_cbShaderMask) != 0;
|
||||
return ShaderAddressValid(buffer.GetShaders().GetPs().ps_regs.data_addr) &&
|
||||
return buffer.GetShaders().GetPs().ps_regs.data_addr != 0 &&
|
||||
(has_color_output || PixelShaderHasDepthOrCoverageSideEffects(sh_regs));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
using VulkanMemoryBarrier = vk::MemoryBarrier;
|
||||
|
||||
vk::Format VulkanFormat(Prospero::BufferFormat guest_format);
|
||||
void RequireVulkanSuccess(vk::Result result, const char* operation);
|
||||
vk::ShaderModule CompileSPV(std::span<const uint32_t> code, vk::Device device);
|
||||
|
||||
@@ -22,9 +22,6 @@
|
||||
|
||||
// IWYU pragma: no_include <intrin.h>
|
||||
|
||||
#define KYTY_ENABLE_DEBUG_PRINTF
|
||||
#define KYTY_DBG_INPUT
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
struct Presenter::Frame {
|
||||
|
||||
@@ -49,7 +49,6 @@
|
||||
// IWYU pragma: no_include <intrin.h>
|
||||
|
||||
#define KYTY_ENABLE_DEBUG_PRINTF
|
||||
#define KYTY_DBG_INPUT
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
@@ -611,10 +610,11 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
|
||||
graphics.mesh_shader_properties.maxMeshOutputPrimitives,
|
||||
graphics.mesh_shader_properties.maxMeshSharedMemorySize);
|
||||
}
|
||||
// VulkanFindPhysicalDevice already checked the required creation features. These two
|
||||
// VulkanFindPhysicalDevice already checked the required creation features. These
|
||||
// requirements are specific to this creation path and are not part of device selection.
|
||||
EXIT_NOT_IMPLEMENTED(supported_features2.features.shaderInt64 != VK_TRUE);
|
||||
EXIT_NOT_IMPLEMENTED(supported_features2.features.vertexPipelineStoresAndAtomics != VK_TRUE);
|
||||
EXIT_NOT_IMPLEMENTED(supported_features2.features.dualSrcBlend != VK_TRUE);
|
||||
vk::PhysicalDeviceFeatures device_features {};
|
||||
device_features.fragmentStoresAndAtomics = VK_TRUE;
|
||||
device_features.samplerAnisotropy = VK_TRUE;
|
||||
@@ -625,6 +625,7 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
|
||||
device_features.shaderStorageImageWriteWithoutFormat = VK_TRUE;
|
||||
device_features.shaderImageGatherExtended = VK_TRUE;
|
||||
device_features.independentBlend = VK_TRUE;
|
||||
device_features.dualSrcBlend = VK_TRUE;
|
||||
device_features.tessellationShader = VK_TRUE;
|
||||
device_features.sampleRateShading = VK_TRUE;
|
||||
device_features.depthBiasClamp = VK_TRUE;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "common/systemInfo.h"
|
||||
#include "common/threads.h"
|
||||
#include "common/timer.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
@@ -45,6 +46,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <vulkan/vk_platform.h>
|
||||
#include <filesystem>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_NO_SIMD
|
||||
@@ -52,7 +54,6 @@
|
||||
|
||||
// IWYU pragma: no_include <intrin.h>
|
||||
|
||||
#define KYTY_ENABLE_DEBUG_PRINTF
|
||||
#define KYTY_DBG_INPUT
|
||||
|
||||
namespace Libs::Graphics {
|
||||
@@ -180,12 +181,6 @@ struct EventDisplay {
|
||||
DisplayOrientation orientation;
|
||||
};
|
||||
|
||||
constexpr uint32_t KYTY_SDL_BUTTON_LMASK = SDL_BUTTON_LMASK; // NOLINT(hicpp-signed-bitwise)
|
||||
constexpr uint32_t KYTY_SDL_BUTTON_MMASK = SDL_BUTTON_MMASK; // NOLINT(hicpp-signed-bitwise)
|
||||
constexpr uint32_t KYTY_SDL_BUTTON_RMASK = SDL_BUTTON_RMASK; // NOLINT(hicpp-signed-bitwise)
|
||||
constexpr uint32_t KYTY_SDL_BUTTON_X1MASK = SDL_BUTTON_X1MASK; // NOLINT(hicpp-signed-bitwise)
|
||||
constexpr uint32_t KYTY_SDL_BUTTON_X2MASK = SDL_BUTTON_X2MASK; // NOLINT(hicpp-signed-bitwise)
|
||||
|
||||
namespace {
|
||||
|
||||
std::unique_ptr<WindowContext> g_window;
|
||||
@@ -193,7 +188,6 @@ std::unique_ptr<WindowContext> g_window;
|
||||
} // namespace
|
||||
|
||||
constexpr const char* KYTY_SDL_WINDOW_CAPTION = "Game";
|
||||
constexpr int KYTY_SDL_WINDOWPOS_CENTERED = SDL_WINDOWPOS_CENTERED; /*NOLINT(hicpp-signed-bitwise)*/
|
||||
|
||||
static void SetPause(WindowLoopState& game, bool flag) {
|
||||
LOGF("Pause: %s\n", flag ? "true" : "false");
|
||||
@@ -614,11 +608,11 @@ void WindowContext::ProcessEvent(double time_s) {
|
||||
|
||||
mb.down = false;
|
||||
mb.up = false;
|
||||
mb.left = ((event->motion.state & KYTY_SDL_BUTTON_LMASK) != 0u);
|
||||
mb.middle = ((event->motion.state & KYTY_SDL_BUTTON_MMASK) != 0u);
|
||||
mb.right = ((event->motion.state & KYTY_SDL_BUTTON_RMASK) != 0u);
|
||||
mb.x1 = ((event->motion.state & KYTY_SDL_BUTTON_X1MASK) != 0u);
|
||||
mb.x2 = ((event->motion.state & KYTY_SDL_BUTTON_X2MASK) != 0u);
|
||||
mb.left = ((event->motion.state & SDL_BUTTON_LMASK) != 0u);
|
||||
mb.middle = ((event->motion.state & SDL_BUTTON_MMASK) != 0u);
|
||||
mb.right = ((event->motion.state & SDL_BUTTON_RMASK) != 0u);
|
||||
mb.x1 = ((event->motion.state & SDL_BUTTON_X1MASK) != 0u);
|
||||
mb.x2 = ((event->motion.state & SDL_BUTTON_X2MASK) != 0u);
|
||||
mb.touch = (event->motion.which == SDL_TOUCH_MOUSEID);
|
||||
mb.pressed = false;
|
||||
mb.released = false;
|
||||
@@ -713,6 +707,20 @@ void WindowContext::ProcessEvent(double time_s) {
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLERSENSORUPDATE: {
|
||||
const auto& sensor = event->csensor;
|
||||
if (sensor.sensor == SDL_SENSOR_ACCEL || sensor.sensor == SDL_SENSOR_GYRO) {
|
||||
Controller::SetSensor(sensor.which,
|
||||
sensor.sensor == SDL_SENSOR_ACCEL ? Controller::Sensor::Accel
|
||||
: Controller::Sensor::Gyro,
|
||||
sensor.data,
|
||||
sensor.timestamp_us != 0
|
||||
? sensor.timestamp_us
|
||||
: static_cast<uint64_t>(sensor.timestamp) * 1000);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
case SDL_CONTROLLERDEVICEREMAPPED: {
|
||||
@@ -854,8 +862,8 @@ static void WindowCreate(WindowContext& context) {
|
||||
window_flags |= static_cast<uint32_t>(SDL_WINDOW_BORDERLESS);
|
||||
}
|
||||
#endif
|
||||
context.window = SDL_CreateWindow(KYTY_SDL_WINDOW_CAPTION, KYTY_SDL_WINDOWPOS_CENTERED,
|
||||
KYTY_SDL_WINDOWPOS_CENTERED, width, height, window_flags);
|
||||
context.window = SDL_CreateWindow(KYTY_SDL_WINDOW_CAPTION, SDL_WINDOWPOS_CENTERED,
|
||||
SDL_WINDOWPOS_CENTERED, width, height, window_flags);
|
||||
|
||||
if (context.window == nullptr) {
|
||||
EXIT("%s\n", SDL_GetError());
|
||||
@@ -939,10 +947,10 @@ struct WindowIcon {
|
||||
}
|
||||
};
|
||||
|
||||
static void WindowLoadPngIcon(const std::string& path, WindowIcon* icon) {
|
||||
static void WindowLoadPngIcon(const std::filesystem::path& path, WindowIcon* icon) {
|
||||
Common::File f;
|
||||
if (!f.Open(path, Common::File::Mode::Read)) {
|
||||
EXIT("Can't open icon file %s\n", path.c_str());
|
||||
EXIT("Can't open icon file %s\n", Common::PathToString(path).c_str());
|
||||
}
|
||||
|
||||
int width = 0;
|
||||
@@ -968,7 +976,7 @@ void WindowContext::UpdateIcon() {
|
||||
static bool icon_loaded = false;
|
||||
|
||||
if (!icon_loaded) {
|
||||
std::string icon_path;
|
||||
std::filesystem::path icon_path;
|
||||
if (Loader::SystemContentGetIconPath(&icon_path)) {
|
||||
WindowLoadPngIcon(icon_path, &icon);
|
||||
}
|
||||
|
||||
@@ -105,8 +105,6 @@ void ClearEmbeddedFetchVectorLanes(EmbeddedFetchVectorLanes* lanes, uint32_t reg
|
||||
lanes->erase(first, last);
|
||||
}
|
||||
|
||||
using EmbeddedFetchData = Frontend::EmbeddedFetchPlan;
|
||||
|
||||
bool IsDecodedSgpr(const Decoder::Operand& op) {
|
||||
return op.kind == Decoder::OperandKind::Sgpr || op.kind == Decoder::OperandKind::VccLo ||
|
||||
op.kind == Decoder::OperandKind::VccHi;
|
||||
@@ -210,13 +208,12 @@ int BufferTableAttribFromOffset(uint32_t raw_offset, int dword) {
|
||||
return static_cast<int>((raw_offset + static_cast<uint32_t>(dword) * 4u) / 16u);
|
||||
}
|
||||
|
||||
EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded,
|
||||
const ShaderVertexInputInfo* input_info,
|
||||
uint32_t user_data_base, uint32_t user_data_count,
|
||||
uint32_t wave_size) {
|
||||
Frontend::EmbeddedFetchPlan DetectEmbeddedVertexFetch(
|
||||
const Decoder::Program& decoded, const ShaderVertexInputInfo* input_info,
|
||||
uint32_t user_data_base, uint32_t user_data_count, uint32_t wave_size) {
|
||||
const uint32_t vertex_index_reg = input_info->logical_stage == ShaderType::Local ? 2u : 5u;
|
||||
const uint32_t instance_index_reg = input_info->logical_stage == ShaderType::Local ? 5u : 8u;
|
||||
EmbeddedFetchData data;
|
||||
Frontend::EmbeddedFetchPlan data;
|
||||
data.loads.reserve(input_info->resources_num);
|
||||
int32_t vertex_offset_candidate = -1;
|
||||
int32_t instance_offset_candidate = -1;
|
||||
@@ -560,7 +557,7 @@ TranslateResult TranslateProgram(std::span<const uint32_t> code, const CompileOp
|
||||
static_cast<uint64_t>(cfg.natural_loops.size()), phase_ms());
|
||||
}
|
||||
|
||||
EmbeddedFetchData embedded_fetch;
|
||||
Frontend::EmbeddedFetchPlan embedded_fetch;
|
||||
if ((options.stage == ShaderType::Vertex || options.stage == ShaderType::Local) &&
|
||||
options.input_info.vertex != nullptr && options.input_info.vertex->fetch_embedded) {
|
||||
embedded_fetch = DetectEmbeddedVertexFetch(
|
||||
|
||||
@@ -138,7 +138,7 @@ uint32_t EmitDppWriteCondition(ValueEmitContext& ctx, const IR::DppMoveFlags& fl
|
||||
state.builder.AddFunction(spv::OpLogicalAnd, TypeBool(state), masks_ok, bank_ok, row_ok);
|
||||
uint32_t writable = masks_ok;
|
||||
if (!flags.bound_control) {
|
||||
const auto target = EmitDppTargetLane(state, flags.control);
|
||||
const auto target = EmitDppTargetLane(state, flags);
|
||||
const auto bounded = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpLogicalAnd, TypeBool(state), bounded, writable,
|
||||
target.valid);
|
||||
@@ -611,7 +611,7 @@ uint32_t EmitUndefU1(EmitterState& state, const IR::Inst& inst) {
|
||||
uint32_t EmitDppMoveU32(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
auto& state = ctx.state;
|
||||
const auto flags = inst.Flags<IR::DppMoveFlags>();
|
||||
const auto target = EmitDppTargetLane(state, flags.control);
|
||||
const auto target = EmitDppTargetLane(state, flags);
|
||||
const auto shuffled = ctx.Shuffle(inst, 0, target.lane);
|
||||
if (flags.fetch_inactive) {
|
||||
return shuffled;
|
||||
|
||||
@@ -2,24 +2,26 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||
|
||||
DppTargetLane EmitDppQuadPermTargetLane(EmitterState& state, uint32_t subid, uint32_t control) {
|
||||
const auto quad_base = state.builder.AllocateId();
|
||||
DppTargetLane EmitDppPermTargetLane(EmitterState& state, uint32_t subid, uint32_t control,
|
||||
uint32_t lane_bits) {
|
||||
const auto lane_mask = (1u << lane_bits) - 1u;
|
||||
const auto group_base = state.builder.AllocateId();
|
||||
const auto lane = state.builder.AllocateId();
|
||||
const auto shift = state.builder.AllocateId();
|
||||
const auto selected0 = state.builder.AllocateId();
|
||||
const auto selected = state.builder.AllocateId();
|
||||
const auto target = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), quad_base, subid,
|
||||
ConstantU32(state, 0xfffffffcu));
|
||||
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), group_base, subid,
|
||||
ConstantU32(state, ~lane_mask));
|
||||
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), lane, subid,
|
||||
ConstantU32(state, 3));
|
||||
state.builder.AddFunction(spv::OpShiftLeftLogical, TypeU32(state), shift, lane,
|
||||
ConstantU32(state, 1));
|
||||
ConstantU32(state, lane_mask));
|
||||
state.builder.AddFunction(spv::OpIMul, TypeU32(state), shift, lane,
|
||||
ConstantU32(state, lane_bits));
|
||||
state.builder.AddFunction(spv::OpShiftRightLogical, TypeU32(state), selected0,
|
||||
ConstantU32(state, control), shift);
|
||||
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), selected, selected0,
|
||||
ConstantU32(state, 3));
|
||||
state.builder.AddFunction(spv::OpBitwiseOr, TypeU32(state), target, quad_base, selected);
|
||||
ConstantU32(state, lane_mask));
|
||||
state.builder.AddFunction(spv::OpBitwiseOr, TypeU32(state), target, group_base, selected);
|
||||
return {target, ConstantBool(state, true)};
|
||||
}
|
||||
|
||||
@@ -89,10 +91,14 @@ DppTargetLane EmitDppMirrorTargetLane(EmitterState& state, uint32_t subid, bool
|
||||
return {target, ConstantBool(state, true)};
|
||||
}
|
||||
|
||||
DppTargetLane EmitDppTargetLane(EmitterState& state, uint32_t control) {
|
||||
DppTargetLane EmitDppTargetLane(EmitterState& state, const IR::DppMoveFlags& flags) {
|
||||
const auto subid = EmitSubgroupLocalInvocationId(state);
|
||||
const auto control = flags.control;
|
||||
if (flags.dpp8) {
|
||||
return EmitDppPermTargetLane(state, subid, control, 3u);
|
||||
}
|
||||
if (control <= 0xffu) {
|
||||
return EmitDppQuadPermTargetLane(state, subid, control);
|
||||
return EmitDppPermTargetLane(state, subid, control, 2u);
|
||||
}
|
||||
if (control >= 0x101u && control <= 0x10fu) {
|
||||
return EmitDppRowShiftTargetLane(state, subid, control & 0xfu, true);
|
||||
|
||||
@@ -308,8 +308,6 @@ uint32_t ConstantI32(EmitterState& state, int32_t value);
|
||||
|
||||
uint32_t ConstantF32(EmitterState& state, uint32_t bits);
|
||||
|
||||
uint32_t FloatBits(float value);
|
||||
|
||||
uint32_t ConstantF32Value(EmitterState& state, float value);
|
||||
|
||||
uint32_t ConstantBool(EmitterState& state, bool value);
|
||||
@@ -329,7 +327,8 @@ void EmitMeshAllocate(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||
uint32_t MeshOutputPointer(EmitterState& state, IR::StageOutputKind kind, uint32_t index = 0);
|
||||
uint32_t MeshPrimitivePointer(EmitterState& state);
|
||||
|
||||
DppTargetLane EmitDppQuadPermTargetLane(EmitterState& state, uint32_t subid, uint32_t control);
|
||||
DppTargetLane EmitDppPermTargetLane(EmitterState& state, uint32_t subid, uint32_t control,
|
||||
uint32_t lane_bits);
|
||||
|
||||
DppTargetLane EmitDppRowShiftTargetLane(EmitterState& state, uint32_t subid, uint32_t amount,
|
||||
bool left);
|
||||
@@ -338,7 +337,7 @@ DppTargetLane EmitDppRowRotateRightTargetLane(EmitterState& state, uint32_t subi
|
||||
|
||||
DppTargetLane EmitDppMirrorTargetLane(EmitterState& state, uint32_t subid, bool half_row);
|
||||
|
||||
DppTargetLane EmitDppTargetLane(EmitterState& state, uint32_t control);
|
||||
DppTargetLane EmitDppTargetLane(EmitterState& state, const IR::DppMoveFlags& flags);
|
||||
|
||||
uint32_t EmitSubgroupLocalInvocationId(EmitterState& state);
|
||||
|
||||
|
||||
@@ -514,31 +514,6 @@ void StoreWord(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo mem)
|
||||
});
|
||||
}
|
||||
|
||||
void FormattedStorePrepared(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem,
|
||||
uint32_t component, const MemoryResourceAccess& resource,
|
||||
uint32_t data) {
|
||||
const auto info = Format::GetFormatInfo(BufferFormat(ctx, mem));
|
||||
if (info.type == Format::ComponentType::Unknown) {
|
||||
StoreWordPrepared(ctx, inst, RebaseRawComponent(mem, component), resource, data);
|
||||
return;
|
||||
}
|
||||
if (component >= info.component_count) return;
|
||||
const auto bits = info.component_bits[component];
|
||||
const auto component_mem = RebaseFormattedComponent(mem, info, component);
|
||||
if (bits == 8u || bits == 16u) {
|
||||
StoreSubwordPrepared(ctx, inst, component_mem, resource, bits, data);
|
||||
} else {
|
||||
StoreWordPrepared(ctx, inst, component_mem, resource, data);
|
||||
}
|
||||
}
|
||||
|
||||
void FormattedStore(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem) {
|
||||
EmitIfCondition(ctx.state, ctx.Arg(inst, inst.NumArgs() - 1), [&]() {
|
||||
const auto resource = PrepareMemoryResourceAccess(ctx.state, mem);
|
||||
FormattedStorePrepared(ctx, inst, mem, 0u, resource, ctx.Arg(inst, inst.NumArgs() - 2));
|
||||
});
|
||||
}
|
||||
|
||||
spv::Op SpirvAtomicOpcode(IR::ValueOpcode opcode) {
|
||||
switch (opcode) {
|
||||
case IR::ValueOpcode::BufferAtomicCmpSwap32: return spv::OpAtomicCompareExchange;
|
||||
@@ -729,6 +704,11 @@ void StoreFormattedInBounds(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
|
||||
uint32_t data) {
|
||||
if (component >= plan.info.component_count) return;
|
||||
const auto bits = plan.info.component_bits[component];
|
||||
if (plan.info.type == Format::ComponentType::Snorm && bits == 16u) {
|
||||
const auto value = EmitBitCastF32U32(ctx.state, data);
|
||||
data = EmitPackSnorm2x16(
|
||||
ctx.state, EmitCompositeConstructF32x2(ctx.state, value, ConstantF32Value(ctx.state, 0.0f)));
|
||||
}
|
||||
if (bits == 8u || bits == 16u) {
|
||||
StoreSubwordInBounds(ctx, mem, plan.resource, plan.addresses[component],
|
||||
plan.indices[component], bits, data);
|
||||
@@ -737,6 +717,23 @@ void StoreFormattedInBounds(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
|
||||
}
|
||||
}
|
||||
|
||||
void FormattedStore(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem) {
|
||||
EmitIfCondition(ctx.state, ctx.Arg(inst, inst.NumArgs() - 1), [&]() {
|
||||
const auto resource = PrepareMemoryResourceAccess(ctx.state, mem);
|
||||
const auto data = ctx.Arg(inst, inst.NumArgs() - 2);
|
||||
const auto info = Format::GetFormatInfo(BufferFormat(ctx, mem));
|
||||
if (info.type == Format::ComponentType::Unknown) {
|
||||
StoreWordPrepared(ctx, inst, RebaseRawComponent(mem, 0u), resource, data);
|
||||
return;
|
||||
}
|
||||
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, info, 1u,
|
||||
FormattedAccess::Store);
|
||||
EmitIfCondition(ctx.state, plan.in_bounds, [&]() {
|
||||
StoreFormattedInBounds(ctx, mem, plan, 0u, data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
uint32_t LoadWideBuffer(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t components) {
|
||||
auto& state = ctx.state;
|
||||
return EmitValueOrDefaultIfCondition(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||
|
||||
@@ -256,14 +257,8 @@ uint32_t ConstantF32(EmitterState& state, uint32_t bits) {
|
||||
return state.builder.Constant(spv::OpConstant, TypeF32(state), bits);
|
||||
}
|
||||
|
||||
uint32_t FloatBits(float value) {
|
||||
uint32_t bits = 0;
|
||||
std::memcpy(&bits, &value, sizeof(bits));
|
||||
return bits;
|
||||
}
|
||||
|
||||
uint32_t ConstantF32Value(EmitterState& state, float value) {
|
||||
return ConstantF32(state, FloatBits(value));
|
||||
return ConstantF32(state, std::bit_cast<uint32_t>(value));
|
||||
}
|
||||
|
||||
uint32_t ConstantBool(EmitterState& state, bool value) {
|
||||
@@ -558,8 +553,17 @@ void DefineOutputs(EmitterState& state) {
|
||||
const auto type = uint_output ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4);
|
||||
binding.variable_id = DefineInterfaceVariable(state, type, spv::StorageClassOutput,
|
||||
binding.debug_name.c_str());
|
||||
const bool dual_source = binding.kind == IR::StageOutputKind::Mrt &&
|
||||
state.program.stage == ShaderType::Pixel &&
|
||||
state.input_info.pixel->dual_source_blending;
|
||||
EXIT_NOT_IMPLEMENTED(dual_source && binding.index > 1);
|
||||
state.builder.AddAnnotation(spv::OpDecorate, binding.variable_id,
|
||||
spv::DecorationLocation, binding.location);
|
||||
spv::DecorationLocation,
|
||||
dual_source ? 0u : binding.location);
|
||||
if (dual_source) {
|
||||
state.builder.AddAnnotation(spv::OpDecorate, binding.variable_id,
|
||||
spv::DecorationIndex, binding.index);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "graphics/shader/recompiler/frontend/decode/OpcodeTable.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Decoder {
|
||||
namespace {
|
||||
@@ -203,18 +204,6 @@ constexpr auto MIMG_SAMPLE_OPS = Detail::MakeOpcodeTable<0x100>(MIMG_SAMPLE_OPCO
|
||||
constexpr auto MIMG_GATHER_OPS = Detail::MakeOpcodeTable<0x100>(MIMG_GATHER_OPCODE_LIST);
|
||||
constexpr auto MIMG_ATOMIC_OPS = Detail::MakeOpcodeTable<0x100>(MIMG_ATOMIC_OPCODE_LIST);
|
||||
|
||||
const MimgSampleInfo* LookupSample(uint32_t opcode) {
|
||||
return Detail::FindOpcode(MIMG_SAMPLE_OPS, opcode);
|
||||
}
|
||||
|
||||
const MimgGatherInfo* LookupGather(uint32_t opcode) {
|
||||
return Detail::FindOpcode(MIMG_GATHER_OPS, opcode);
|
||||
}
|
||||
|
||||
const Detail::OpcodeMap* LookupAtomic(uint32_t opcode) {
|
||||
return Detail::FindOpcode(MIMG_ATOMIC_OPS, opcode);
|
||||
}
|
||||
|
||||
Opcode DecodeMimgOpcode(uint32_t opcode, const MimgSampleInfo* sample, const MimgGatherInfo* gather,
|
||||
const Detail::OpcodeMap* atomic) {
|
||||
if (sample != nullptr) {
|
||||
@@ -280,10 +269,6 @@ uint32_t CountDmaskComponents(uint32_t dmask) {
|
||||
return count != 0 ? count : 1u;
|
||||
}
|
||||
|
||||
bool IsSingleDmaskBit(uint32_t dmask) {
|
||||
return dmask != 0u && (dmask & (dmask - 1u)) == 0u;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ImageAddressComponent ImageAddressComponentLayout(uint32_t flags, uint32_t component) {
|
||||
@@ -332,9 +317,9 @@ void DecodeMimg(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const bool r128 = ((word0 >> 15u) & 0x1u) != 0u;
|
||||
const bool a16 = ((word1 >> 30u) & 0x1u) != 0u;
|
||||
const bool d16 = ((word1 >> 31u) & 0x1u) != 0u;
|
||||
const auto* sample = LookupSample(opcode);
|
||||
const auto* gather = LookupGather(opcode);
|
||||
const auto* atomic = LookupAtomic(opcode);
|
||||
const auto* sample = Detail::FindOpcode(MIMG_SAMPLE_OPS, opcode);
|
||||
const auto* gather = Detail::FindOpcode(MIMG_GATHER_OPS, opcode);
|
||||
const auto* atomic = Detail::FindOpcode(MIMG_ATOMIC_OPS, opcode);
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word_count = word_count;
|
||||
@@ -364,7 +349,7 @@ void DecodeMimg(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
SetUnsupported(inst, Family::MIMG, opcode, "MIMG opcode is not implemented");
|
||||
}
|
||||
if (gather != nullptr && !IsSingleDmaskBit(inst.dmask)) {
|
||||
if (gather != nullptr && !std::has_single_bit(inst.dmask)) {
|
||||
SetUnsupported(inst, Family::MIMG, opcode,
|
||||
"MIMG image gather requires exactly one dmask bit");
|
||||
}
|
||||
@@ -382,7 +367,7 @@ void DecodeMimg(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
}
|
||||
|
||||
const char* MimgSampleOpcodeName(uint32_t opcode) {
|
||||
const auto* sample = LookupSample(opcode);
|
||||
const auto* sample = Detail::FindOpcode(MIMG_SAMPLE_OPS, opcode);
|
||||
return sample != nullptr ? sample->name : nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ constexpr OpcodeMap SOP2_OPCODE_LIST[] = {
|
||||
{0x1cu, Opcode::S_XNOR_B32}, {0x1du, Opcode::S_XNOR_B64},
|
||||
{0x1eu, Opcode::S_LSHL_B32}, {0x1fu, Opcode::S_LSHL_B64},
|
||||
{0x20u, Opcode::S_LSHR_B32}, {0x21u, Opcode::S_LSHR_B64},
|
||||
{0x22u, Opcode::S_ASHR_I32}, {0x24u, Opcode::S_BFM_B32},
|
||||
{0x22u, Opcode::S_ASHR_I32}, {0x23u, Opcode::S_ASHR_I64},
|
||||
{0x24u, Opcode::S_BFM_B32},
|
||||
{0x25u, Opcode::S_BFM_B64}, {0x26u, Opcode::S_MUL_I32},
|
||||
{0x27u, Opcode::S_BFE_U32}, {0x28u, Opcode::S_BFE_I32},
|
||||
{0x29u, Opcode::S_BFE_U64}, {0x2cu, Opcode::S_ABSDIFF_I32},
|
||||
|
||||
@@ -14,10 +14,6 @@
|
||||
namespace Libs::Graphics::ShaderRecompiler::Decoder {
|
||||
namespace {
|
||||
|
||||
uint32_t FloatBits(float value) {
|
||||
return std::bit_cast<uint32_t>(value);
|
||||
}
|
||||
|
||||
bool HasLiteral(const Instruction& inst) {
|
||||
return inst.src0.kind == OperandKind::LiteralConstant ||
|
||||
inst.src1.kind == OperandKind::LiteralConstant ||
|
||||
@@ -242,7 +238,7 @@ void DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand) {
|
||||
if (code >= 240u && code <= 247u) {
|
||||
constexpr float values[] = {0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f, 4.0f, -4.0f};
|
||||
operand.kind = OperandKind::FloatInlineConstant;
|
||||
operand.value = FloatBits(values[code - 240u]);
|
||||
operand.value = std::bit_cast<uint32_t>(values[code - 240u]);
|
||||
return;
|
||||
}
|
||||
if (code >= 256u && code <= 511u) {
|
||||
@@ -260,7 +256,7 @@ void DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand) {
|
||||
case 239u: operand.kind = OperandKind::PopsExitingWaveId; return;
|
||||
case 248u:
|
||||
operand.kind = OperandKind::FloatInlineConstant;
|
||||
operand.value = FloatBits(0.15915494309189535f);
|
||||
operand.value = std::bit_cast<uint32_t>(0.15915494309189535f);
|
||||
return;
|
||||
case 251u: operand.kind = OperandKind::VccZ; return;
|
||||
case 252u: operand.kind = OperandKind::ExecZ; return;
|
||||
@@ -476,8 +472,9 @@ std::string OperandToString(const Operand& operand) {
|
||||
text += ".clamp";
|
||||
}
|
||||
if (operand.dpp) {
|
||||
text += fmt::format(".dpp(ctrl=0x{:x},fi={},bc={})", operand.dpp_ctrl,
|
||||
operand.dpp_fetch_inactive ? 1u : 0u, operand.dpp_bound_ctrl ? 1u : 0u);
|
||||
text += fmt::format(".{}(ctrl=0x{:x},fi={},bc={})", operand.dpp8 ? "dpp8" : "dpp",
|
||||
operand.dpp_ctrl, operand.dpp_fetch_inactive ? 1u : 0u,
|
||||
operand.dpp_bound_ctrl ? 1u : 0u);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SHADERDECODER_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/magicEnum.h"
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
@@ -106,6 +106,7 @@ enum class Opcode {
|
||||
S_LSHR_B32,
|
||||
S_LSHR_B64,
|
||||
S_ASHR_I32,
|
||||
S_ASHR_I64,
|
||||
S_MUL_I32,
|
||||
S_MUL_HI_U32,
|
||||
S_MUL_HI_I32,
|
||||
@@ -227,6 +228,7 @@ enum class Opcode {
|
||||
V_CVT_PKRTZ_F16_F32,
|
||||
V_CVT_PK_U8_F32,
|
||||
V_MAD_F32,
|
||||
V_MAD_I16,
|
||||
V_MAD_I32_I24,
|
||||
V_MAD_U32_U24,
|
||||
V_MAD_U64_U32,
|
||||
@@ -299,6 +301,7 @@ enum class Opcode {
|
||||
V_SUBREV_NC_U32,
|
||||
V_ADD_NC_U16,
|
||||
V_SUB_NC_U16,
|
||||
V_MUL_LO_U16,
|
||||
V_MAX_U16,
|
||||
V_MAX_I16,
|
||||
V_MIN_U16,
|
||||
@@ -377,7 +380,9 @@ enum class Opcode {
|
||||
V_CMP_GT_F16,
|
||||
V_CMP_LG_F16,
|
||||
V_CMP_GE_F16,
|
||||
V_CMP_NGT_F16,
|
||||
V_CMP_NEQ_F16,
|
||||
V_CMP_NLT_F16,
|
||||
V_CMPX_LT_F16,
|
||||
V_CMPX_EQ_F16,
|
||||
V_CMPX_LE_F16,
|
||||
@@ -665,6 +670,7 @@ struct Operand {
|
||||
bool absolute = false;
|
||||
bool clamp = false;
|
||||
bool dpp = false;
|
||||
bool dpp8 = false;
|
||||
};
|
||||
|
||||
struct Instruction {
|
||||
|
||||
@@ -83,10 +83,6 @@ constexpr Vop2OpcodeInfo VOP2_OPCODE_LIST[] = {
|
||||
|
||||
constexpr auto VOP2_OPS = Detail::MakeOpcodeTable<0x40>(VOP2_OPCODE_LIST);
|
||||
|
||||
constexpr Opcode LookupVop2Opcode(uint32_t encoding) {
|
||||
return Detail::LookupOpcode(VOP2_OPS, encoding);
|
||||
}
|
||||
|
||||
constexpr OpcodeMap VOP1_OPCODE_LIST[] = {
|
||||
{0x00u, Opcode::V_NOP},
|
||||
{0x01u, Opcode::V_MOV_B32},
|
||||
@@ -250,7 +246,8 @@ constexpr VopcOpcodeInfo VOPC_OPCODE_LIST[] = {
|
||||
{0xf5u, Opcode::V_CMPX_NE_U64, false}, {0xc9u, Opcode::V_CMP_LT_F16},
|
||||
{0xcau, Opcode::V_CMP_EQ_F16}, {0xcbu, Opcode::V_CMP_LE_F16},
|
||||
{0xccu, Opcode::V_CMP_GT_F16}, {0xcdu, Opcode::V_CMP_LG_F16},
|
||||
{0xceu, Opcode::V_CMP_GE_F16}, {0xedu, Opcode::V_CMP_NEQ_F16},
|
||||
{0xceu, Opcode::V_CMP_GE_F16}, {0xebu, Opcode::V_CMP_NGT_F16},
|
||||
{0xedu, Opcode::V_CMP_NEQ_F16}, {0xeeu, Opcode::V_CMP_NLT_F16},
|
||||
{0xd9u, Opcode::V_CMPX_LT_F16}, {0xdau, Opcode::V_CMPX_EQ_F16},
|
||||
{0xdbu, Opcode::V_CMPX_LE_F16}, {0xdcu, Opcode::V_CMPX_GT_F16},
|
||||
{0xdeu, Opcode::V_CMPX_GE_F16}, {0xfbu, Opcode::V_CMPX_NGT_F16},
|
||||
@@ -298,6 +295,7 @@ constexpr OpcodeMap VOP3_OPCODE_LIST[] = {
|
||||
{0x300u, Opcode::V_LSHRREV_B64},
|
||||
{0x303u, Opcode::V_ADD_NC_U16},
|
||||
{0x304u, Opcode::V_SUB_NC_U16},
|
||||
{0x305u, Opcode::V_MUL_LO_U16},
|
||||
{0x307u, Opcode::V_LSHRREV_B16},
|
||||
{0x308u, Opcode::V_ASHRREV_I16},
|
||||
{0x309u, Opcode::V_MAX_U16},
|
||||
@@ -314,6 +312,7 @@ constexpr OpcodeMap VOP3_OPCODE_LIST[] = {
|
||||
{0x345u, Opcode::V_XAD_U32},
|
||||
{0x346u, Opcode::V_LSHL_ADD_U32},
|
||||
{0x347u, Opcode::V_ADD_LSHL_U32},
|
||||
{0x35eu, Opcode::V_MAD_I16},
|
||||
{0x360u, Opcode::V_READLANE_B32},
|
||||
{0x361u, Opcode::V_WRITELANE_B32},
|
||||
{0x362u, Opcode::V_LDEXP_F32},
|
||||
@@ -374,7 +373,7 @@ Opcode LookupVop3Opcode(uint32_t opcode) {
|
||||
if (IsUnsupportedVop3EncodedVop2Alias(opcode - 0x100u)) {
|
||||
return Opcode::UNSUPPORTED;
|
||||
}
|
||||
return LookupVop2Opcode(opcode - 0x100u);
|
||||
return Detail::LookupOpcode(VOP2_OPS, opcode - 0x100u);
|
||||
}
|
||||
if (opcode >= 0x180u && opcode <= 0x1ffu) {
|
||||
return Detail::LookupOpcode(VOP3_ENCODED_VOP1_OPS, opcode - 0x180u);
|
||||
@@ -426,13 +425,14 @@ bool IsNativeVop3F16TernaryOpcode(Opcode opcode) {
|
||||
}
|
||||
|
||||
bool IsNativeVop3I16TernaryOpcode(Opcode opcode) {
|
||||
return opcode == Opcode::V_MED3_I16;
|
||||
return opcode == Opcode::V_MED3_I16 || opcode == Opcode::V_MAD_I16;
|
||||
}
|
||||
|
||||
bool IsNativeVop3B16BinaryOpcode(Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Opcode::V_ADD_NC_U16:
|
||||
case Opcode::V_SUB_NC_U16:
|
||||
case Opcode::V_MUL_LO_U16:
|
||||
case Opcode::V_MAX_U16:
|
||||
case Opcode::V_MAX_I16:
|
||||
case Opcode::V_MIN_U16:
|
||||
@@ -685,10 +685,15 @@ void DecodeVop1Sdwa(uint32_t pc, std::span<const uint32_t> code, uint32_t word_i
|
||||
ReadLiteralOperands(code, word_index, inst);
|
||||
}
|
||||
|
||||
void ApplyDppModifier(Operand& operand, uint32_t modifier) {
|
||||
void ApplyDppModifier(Operand& operand, uint32_t modifier, uint32_t encoding) {
|
||||
operand.dpp = true;
|
||||
if (encoding == 233u) {
|
||||
operand.dpp8 = true;
|
||||
operand.dpp_ctrl = modifier >> 8u;
|
||||
return;
|
||||
}
|
||||
operand.negate = ((modifier >> 20u) & 0x1u) != 0u;
|
||||
operand.absolute = ((modifier >> 21u) & 0x1u) != 0u;
|
||||
operand.dpp = true;
|
||||
operand.dpp_ctrl = (modifier >> 8u) & 0x1ffu;
|
||||
operand.dpp_fetch_inactive = ((modifier >> 18u) & 0x1u) != 0u;
|
||||
operand.dpp_bound_ctrl = ((modifier >> 19u) & 0x1u) != 0u;
|
||||
@@ -709,7 +714,7 @@ void DecodeVop1Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_in
|
||||
DecodeVectorGpr(vdst, inst.dst);
|
||||
}
|
||||
DecodeScalarSource(src0 + 256u, pc, inst.src0);
|
||||
ApplyDppModifier(inst.src0, modifier);
|
||||
ApplyDppModifier(inst.src0, modifier, code[word_index] & 0x1ffu);
|
||||
inst.src_count = 1;
|
||||
|
||||
if (!IsVop1FloatSourceOpcode(inst.opcode) && (inst.src0.negate || inst.src0.absolute)) {
|
||||
@@ -824,7 +829,9 @@ bool IsVopcFloatCompareOpcode(Opcode opcode) {
|
||||
case Opcode::V_CMP_GT_F16:
|
||||
case Opcode::V_CMP_LG_F16:
|
||||
case Opcode::V_CMP_GE_F16:
|
||||
case Opcode::V_CMP_NGT_F16:
|
||||
case Opcode::V_CMP_NEQ_F16:
|
||||
case Opcode::V_CMP_NLT_F16:
|
||||
case Opcode::V_CMPX_LT_F16:
|
||||
case Opcode::V_CMPX_EQ_F16:
|
||||
case Opcode::V_CMPX_LE_F16:
|
||||
@@ -1046,7 +1053,7 @@ void DecodeVop2Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_in
|
||||
DecodeVectorGpr(vdst, inst.dst);
|
||||
DecodeVectorGpr(vsrc1, inst.src1);
|
||||
DecodeScalarSource(src0 + 256u, pc, inst.src0);
|
||||
ApplyDppModifier(inst.src0, modifier);
|
||||
ApplyDppModifier(inst.src0, modifier, code[word_index] & 0x1ffu);
|
||||
inst.src1.negate = ((modifier >> 22u) & 0x1u) != 0u;
|
||||
inst.src1.absolute = ((modifier >> 23u) & 0x1u) != 0u;
|
||||
const bool packed_fmac = inst.opcode == Opcode::V_PK_FMAC_F16;
|
||||
@@ -1155,7 +1162,7 @@ void DecodeVopcDpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_in
|
||||
DecodeVectorGpr(vsrc1, inst.src1);
|
||||
DecodeScalarSource(src0 + 256u, pc, inst.src0);
|
||||
inst.dst.kind = IsVopcCompareExec(inst.opcode) ? OperandKind::ExecLo : OperandKind::VccLo;
|
||||
ApplyDppModifier(inst.src0, modifier);
|
||||
ApplyDppModifier(inst.src0, modifier, code[word_index] & 0x1ffu);
|
||||
inst.src1.negate = ((modifier >> 22u) & 0x1u) != 0u;
|
||||
inst.src1.absolute = ((modifier >> 23u) & 0x1u) != 0u;
|
||||
inst.src_count = 2;
|
||||
@@ -1171,6 +1178,7 @@ uint32_t NativeVop3SourceCount(Opcode opcode) {
|
||||
case Opcode::V_MUL_I32_I24:
|
||||
case Opcode::V_ADD_NC_U16:
|
||||
case Opcode::V_SUB_NC_U16:
|
||||
case Opcode::V_MUL_LO_U16:
|
||||
case Opcode::V_MAX_U16:
|
||||
case Opcode::V_MAX_I16:
|
||||
case Opcode::V_MIN_U16:
|
||||
@@ -1355,7 +1363,8 @@ bool SupportsNativeVop3ResultModifiers(Opcode opcode) {
|
||||
}
|
||||
|
||||
bool SupportsNativeVop3Clamp(Opcode opcode) {
|
||||
return SupportsNativeVop3ResultModifiers(opcode) || UsesInexactClampControl(opcode);
|
||||
return SupportsNativeVop3ResultModifiers(opcode) || UsesInexactClampControl(opcode) ||
|
||||
opcode == Opcode::V_MAD_I16;
|
||||
}
|
||||
|
||||
bool HasUnsupportedNativeVop3Modifiers(Opcode opcode, bool permlane, bool mad_mix,
|
||||
@@ -1376,7 +1385,7 @@ bool HasUnsupportedNativeVop3Modifiers(Opcode opcode, bool permlane, bool mad_mi
|
||||
return opcode != Opcode::V_FMA_F16 && (clamp != 0u || omod != 0u);
|
||||
}
|
||||
if (IsNativeVop3I16TernaryOpcode(opcode)) {
|
||||
return abs != 0u || clamp != 0u || omod != 0u || neg != 0u;
|
||||
return abs != 0u || (clamp != 0u && !clamp_modifier) || omod != 0u || neg != 0u;
|
||||
}
|
||||
if (IsNativeVop3B16BinaryOpcode(opcode)) {
|
||||
return abs != 0u || clamp != 0u || omod != 0u || neg != 0u;
|
||||
@@ -1468,7 +1477,7 @@ void DecodeVop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.pc = pc;
|
||||
inst.family = Family::VOP2;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = LookupVop2Opcode(opcode);
|
||||
inst.opcode = Detail::LookupOpcode(VOP2_OPS, opcode);
|
||||
SetRawWords(inst, code, word_index, 1);
|
||||
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
@@ -1510,6 +1519,7 @@ void DecodeVop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
return;
|
||||
}
|
||||
switch (src0) {
|
||||
case 233u: DecodeVop1Dpp(pc, code, word_index, opcode, vdst, inst); return;
|
||||
case 249u: DecodeVop1Sdwa(pc, code, word_index, opcode, vdst, inst); return;
|
||||
case 250u: DecodeVop1Dpp(pc, code, word_index, opcode, vdst, inst); return;
|
||||
default: break;
|
||||
|
||||
@@ -166,9 +166,9 @@ void Translator::V_CVT_PK_U8_F32(const Decoder::Instruction& inst) {
|
||||
}
|
||||
|
||||
void Translator::V_PACK_B32_F16(const Decoder::Instruction& inst) {
|
||||
const auto low = ReadF16LaneBits(inst.src0, false);
|
||||
const auto low = Read16LaneBits(inst.src0, false);
|
||||
const auto high =
|
||||
ir.ShiftLeftLogical(ReadF16LaneBits(inst.src1, false), IR::U32(IR::Value(16u)));
|
||||
ir.ShiftLeftLogical(Read16LaneBits(inst.src1, false), IR::U32(IR::Value(16u)));
|
||||
WriteOperand(DestinationOperand(inst), ir.BitwiseOr(low, high));
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,18 @@ bool Translator::Integer16Binary(const Decoder::Instruction& inst, IR::ValueOpco
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::V_MAD_I16(const Decoder::Instruction& inst) {
|
||||
const auto lhs = ReadU16AsU32(inst.src0, true);
|
||||
const auto rhs = ReadU16AsU32(inst.src1, true);
|
||||
auto result = ir.IAdd(ir.IMul(lhs, rhs), ReadU16AsU32(inst.src2, true));
|
||||
if (inst.dst.clamp) {
|
||||
result = IR::U32(ir.Emit(IR::ValueOpcode::SMax32, {result, IR::Value(0xffff8000u)}));
|
||||
result = IR::U32(ir.Emit(IR::ValueOpcode::SMin32, {result, IR::Value(0x7fffu)}));
|
||||
}
|
||||
Write16Bits(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::V_MED3_I16(const Decoder::Instruction& inst) {
|
||||
const auto result = IR::U32(ir.Emit(
|
||||
IR::ValueOpcode::SMedTri32, {ReadU16AsU32(inst.src0, true), ReadU16AsU32(inst.src1, true),
|
||||
@@ -168,6 +180,21 @@ bool Translator::SimpleInteger(const Decoder::Instruction& inst, IR::ValueOpcode
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::S_ASHR_I64(const Decoder::Instruction& inst) {
|
||||
// Signed 64-bit sources sign-extend literals; generic B64 operands zero-extend them.
|
||||
const auto source =
|
||||
inst.src0.kind == Decoder::OperandKind::LiteralConstant
|
||||
? ir.ConstructU64(
|
||||
ReadU32(inst.src0),
|
||||
IR::U32(IR::Value((inst.src0.value & 0x80000000u) ? 0xffffffffu : 0u)))
|
||||
: ReadU64(inst.src0);
|
||||
const auto result =
|
||||
ir.Emit(IR::ValueOpcode::ShiftRightArithmetic64, {source, ReadU32(inst.src1)});
|
||||
WriteOperand(inst.dst, result);
|
||||
ir.SetScc(IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::ComposedIntegerBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
||||
bool negate_rhs, bool negate_result, bool update_scc) {
|
||||
const auto lhs = ReadU32(inst.src0);
|
||||
|
||||
@@ -34,6 +34,7 @@ Decoder::Operand OffsetDecodedRegister(const Decoder::Operand& operand, uint32_t
|
||||
result.negate_hi = false;
|
||||
result.absolute = false;
|
||||
result.dpp_ctrl = 0;
|
||||
result.dpp8 = false;
|
||||
result.dpp_row_mask = 0xf;
|
||||
result.dpp_bank_mask = 0xf;
|
||||
result.explicit_sdwa_dst = false;
|
||||
|
||||
@@ -179,6 +179,7 @@ bool Translator::EmitScalar(const Decoder::Instruction& inst) {
|
||||
case O::S_LSHR_B64:
|
||||
return SimpleInteger(inst, IR::ValueOpcode::ShiftRightLogical64, IR::Type::U64, false,
|
||||
false, true);
|
||||
case O::S_ASHR_I64: return S_ASHR_I64(inst);
|
||||
|
||||
case O::S_ANDN2_B32:
|
||||
return ComposedIntegerBinary(inst, IR::ValueOpcode::BitwiseAnd32, true, false, true);
|
||||
|
||||
@@ -9,6 +9,17 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
||||
|
||||
static IR::DppMoveFlags DppFlags(const Decoder::Operand& operand) {
|
||||
return {
|
||||
.control = operand.dpp_ctrl,
|
||||
.row_mask = operand.dpp_row_mask,
|
||||
.bank_mask = operand.dpp_bank_mask,
|
||||
.fetch_inactive = operand.dpp_fetch_inactive,
|
||||
.bound_control = operand.dpp_bound_ctrl,
|
||||
.dpp8 = operand.dpp8,
|
||||
};
|
||||
}
|
||||
|
||||
const Decoder::Operand& Translator::SourceAt(const Decoder::Instruction& inst, uint32_t index) {
|
||||
switch (index) {
|
||||
case 0: return inst.src0;
|
||||
@@ -30,6 +41,7 @@ Decoder::Operand Translator::DestinationOperand(const Decoder::Instruction& inst
|
||||
continue;
|
||||
}
|
||||
destination.dpp = true;
|
||||
destination.dpp8 = source.dpp8;
|
||||
destination.dpp_ctrl = source.dpp_ctrl;
|
||||
destination.dpp_row_mask = source.dpp_row_mask;
|
||||
destination.dpp_bank_mask = source.dpp_bank_mask;
|
||||
@@ -106,6 +118,7 @@ Decoder::Operand Translator::PlainOperand(const Decoder::Operand& operand) {
|
||||
result.dpp_fetch_inactive = false;
|
||||
result.dpp_bound_ctrl = false;
|
||||
result.dpp = false;
|
||||
result.dpp8 = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -167,14 +180,8 @@ IR::U32 Translator::ReadScalarCode(uint32_t code) {
|
||||
|
||||
IR::U32 Translator::ApplyBitSourceModifiers(const Decoder::Operand& operand, IR::U32 value) {
|
||||
if (operand.dpp) {
|
||||
const IR::DppMoveFlags flags {
|
||||
.control = static_cast<uint16_t>(operand.dpp_ctrl),
|
||||
.row_mask = static_cast<uint8_t>(operand.dpp_row_mask),
|
||||
.bank_mask = static_cast<uint8_t>(operand.dpp_bank_mask),
|
||||
.fetch_inactive = operand.dpp_fetch_inactive,
|
||||
.bound_control = operand.dpp_bound_ctrl,
|
||||
};
|
||||
value = IR::U32(ir.Emit(IR::ValueOpcode::DppMoveU32, {value, ir.GetExec()}, flags));
|
||||
value =
|
||||
IR::U32(ir.Emit(IR::ValueOpcode::DppMoveU32, {value, ir.GetExec()}, DppFlags(operand)));
|
||||
}
|
||||
if (operand.sdwa_sel != 6u) {
|
||||
uint32_t offset = 0;
|
||||
@@ -301,15 +308,8 @@ void Translator::WriteRawU32(const Decoder::Operand& operand, IR::U32 value) {
|
||||
const auto reg = static_cast<IR::VectorReg>(operand.reg);
|
||||
const auto old = ir.GetVectorReg(reg);
|
||||
if (operand.dpp) {
|
||||
const IR::DppMoveFlags flags {
|
||||
.control = static_cast<uint16_t>(operand.dpp_ctrl),
|
||||
.row_mask = static_cast<uint8_t>(operand.dpp_row_mask),
|
||||
.bank_mask = static_cast<uint8_t>(operand.dpp_bank_mask),
|
||||
.fetch_inactive = operand.dpp_fetch_inactive,
|
||||
.bound_control = operand.dpp_bound_ctrl,
|
||||
};
|
||||
value = IR::U32(
|
||||
ir.Emit(IR::ValueOpcode::DppUpdateU32, {value, old, ir.GetExec()}, flags));
|
||||
value = IR::U32(ir.Emit(IR::ValueOpcode::DppUpdateU32, {value, old, ir.GetExec()},
|
||||
DppFlags(operand)));
|
||||
} else {
|
||||
value = ir.Select(ir.GetExec(), value, old);
|
||||
}
|
||||
@@ -541,10 +541,7 @@ IR::U32 Translator::ReadU16LaneRaw(const Decoder::Operand& operand, bool high_la
|
||||
|
||||
IR::U32 Translator::ReadU16LaneAsU32(const Decoder::Operand& operand, bool high_lane,
|
||||
bool sign_extend) {
|
||||
auto value = ReadU16LaneRaw(operand, high_lane);
|
||||
if (high_lane ? operand.negate_hi : operand.negate) {
|
||||
value = ir.BitwiseAnd(ir.ISub(IR::U32(IR::Value(0u)), value), IR::U32(IR::Value(0xffffu)));
|
||||
}
|
||||
auto value = Read16LaneBits(operand, high_lane);
|
||||
if (sign_extend || operand.sdwa_sext) {
|
||||
value = IR::U32(
|
||||
ir.Emit(IR::ValueOpcode::BitFieldSExtract, {value, IR::Value(0u), IR::Value(16u)}));
|
||||
@@ -556,12 +553,13 @@ IR::U32 Translator::ReadU16AsU32(const Decoder::Operand& operand, bool sign_exte
|
||||
return ReadU16LaneAsU32(operand, false, sign_extend);
|
||||
}
|
||||
|
||||
IR::U32 Translator::ReadF16LaneBits(const Decoder::Operand& operand, bool high_lane) {
|
||||
IR::U32 Translator::Read16LaneBits(const Decoder::Operand& operand, bool high_lane) {
|
||||
auto value = ReadU16LaneRaw(operand, high_lane);
|
||||
if (operand.absolute) {
|
||||
value = ir.BitwiseAnd(value, IR::U32(IR::Value(0x7fffu)));
|
||||
}
|
||||
if (high_lane ? operand.negate_hi : operand.negate) {
|
||||
// RDNA2 source NEG flips the sign bit, including packed integer operands.
|
||||
value = ir.BitwiseXor(value, IR::U32(IR::Value(0x8000u)));
|
||||
}
|
||||
return value;
|
||||
|
||||
@@ -44,7 +44,7 @@ private:
|
||||
IR::U32 ReadU16LaneRaw(const Decoder::Operand& operand, bool high_lane);
|
||||
IR::U32 ReadU16LaneAsU32(const Decoder::Operand& operand, bool high_lane, bool sign_extend);
|
||||
IR::U32 ReadU16AsU32(const Decoder::Operand& operand, bool sign_extend);
|
||||
IR::U32 ReadF16LaneBits(const Decoder::Operand& operand, bool high_lane);
|
||||
IR::U32 Read16LaneBits(const Decoder::Operand& operand, bool high_lane);
|
||||
std::array<IR::U32, 2> ExtractU64(IR::U64 value);
|
||||
void WriteU32Pair(const Decoder::Operand& operand, const std::array<IR::U32, 2>& value);
|
||||
IR::U32 ConditionBit(const Decoder::Operand& operand);
|
||||
@@ -164,6 +164,7 @@ private:
|
||||
bool FloatCube(const Decoder::Instruction& inst, uint32_t result_kind);
|
||||
bool Integer16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool arithmetic);
|
||||
bool Integer16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool sign);
|
||||
bool V_MAD_I16(const Decoder::Instruction& inst);
|
||||
bool V_MED3_I16(const Decoder::Instruction& inst);
|
||||
bool PackedInteger16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
||||
bool arithmetic);
|
||||
@@ -176,6 +177,7 @@ private:
|
||||
bool negate_result);
|
||||
bool SimpleInteger(const Decoder::Instruction& inst, IR::ValueOpcode opcode, IR::Type type,
|
||||
bool reverse, bool mask_shift_count, bool update_scc);
|
||||
bool S_ASHR_I64(const Decoder::Instruction& inst);
|
||||
bool ComposedIntegerBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
||||
bool negate_rhs, bool negate_result, bool update_scc);
|
||||
bool V_AND_OR_B32(const Decoder::Instruction& inst);
|
||||
|
||||
@@ -264,6 +264,9 @@ bool Translator::EmitVector(const Decoder::Instruction& inst) {
|
||||
case O::V_CMPX_LE_F16:
|
||||
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdLessThanEqual32, true, true);
|
||||
return true;
|
||||
case O::V_CMP_NGT_F16:
|
||||
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordLessThanEqual32, true, false);
|
||||
return true;
|
||||
case O::V_CMPX_NGT_F16:
|
||||
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordLessThanEqual32, true, true);
|
||||
return true;
|
||||
@@ -273,6 +276,9 @@ bool Translator::EmitVector(const Decoder::Instruction& inst) {
|
||||
case O::V_CMPX_NEQ_F16:
|
||||
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordNotEqual32, true, true);
|
||||
return true;
|
||||
case O::V_CMP_NLT_F16:
|
||||
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordGreaterThanEqual32, true, false);
|
||||
return true;
|
||||
case O::V_CMPX_NLT_F16:
|
||||
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordGreaterThanEqual32, true, true);
|
||||
return true;
|
||||
@@ -321,6 +327,8 @@ bool Translator::EmitVector(const Decoder::Instruction& inst) {
|
||||
case O::V_ADD_NC_I16: return Integer16Binary(inst, IR::ValueOpcode::IAdd32, false);
|
||||
case O::V_SUB_NC_U16:
|
||||
case O::V_SUB_NC_I16: return Integer16Binary(inst, IR::ValueOpcode::ISub32, false);
|
||||
case O::V_MUL_LO_U16: return Integer16Binary(inst, IR::ValueOpcode::IMul32, false);
|
||||
case O::V_MAD_I16: return V_MAD_I16(inst);
|
||||
case O::V_MED3_I16: return V_MED3_I16(inst);
|
||||
case O::V_MIN_I16: return Integer16Binary(inst, IR::ValueOpcode::SMin32, true);
|
||||
case O::V_MAX_I16: return Integer16Binary(inst, IR::ValueOpcode::SMax32, true);
|
||||
|
||||
@@ -34,11 +34,12 @@ struct ImageOpcodeInfo {
|
||||
};
|
||||
|
||||
struct DppMoveFlags {
|
||||
uint16_t control = 0;
|
||||
uint8_t row_mask = 0xf;
|
||||
uint8_t bank_mask = 0xf;
|
||||
uint32_t control : 24 = 0;
|
||||
uint32_t row_mask : 4 = 0xf;
|
||||
uint32_t bank_mask : 4 = 0xf;
|
||||
bool fetch_inactive = false;
|
||||
bool bound_control = false;
|
||||
bool dpp8 = false;
|
||||
};
|
||||
static_assert(sizeof(DppMoveFlags) <= sizeof(uint64_t));
|
||||
static_assert(std::is_trivially_copyable_v<DppMoveFlags>);
|
||||
|
||||
@@ -479,8 +479,6 @@ private:
|
||||
return std::bit_cast<float>(static_cast<uint32_t>(bits));
|
||||
}
|
||||
|
||||
static uint64_t Float32Bits(float value) { return std::bit_cast<uint32_t>(value); }
|
||||
|
||||
bool EvaluateWide(Value value, uint64_t& result) {
|
||||
value = value.Resolve();
|
||||
if (value.IsImmediate()) {
|
||||
@@ -490,7 +488,7 @@ private:
|
||||
case Type::U16: result = value.U16(); return true;
|
||||
case Type::U32: result = value.U32(); return true;
|
||||
case Type::U64: result = value.U64(); return true;
|
||||
case Type::F32: result = Float32Bits(value.F32Value()); return true;
|
||||
case Type::F32: result = std::bit_cast<uint32_t>(value.F32Value()); return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@@ -733,7 +731,7 @@ private:
|
||||
return false;
|
||||
case ValueOpcode::ConvertF32U32:
|
||||
if (Arg(inst, 0, a)) {
|
||||
result = Float32Bits(static_cast<float>(static_cast<uint32_t>(a)));
|
||||
result = std::bit_cast<uint32_t>(static_cast<float>(static_cast<uint32_t>(a)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -750,13 +748,13 @@ private:
|
||||
return false;
|
||||
case ValueOpcode::FPMul32:
|
||||
if (binary()) {
|
||||
result = Float32Bits(Float32(a) * Float32(b));
|
||||
result = std::bit_cast<uint32_t>(Float32(a) * Float32(b));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case ValueOpcode::FPTrunc32:
|
||||
if (Arg(inst, 0, a)) {
|
||||
result = Float32Bits(std::trunc(Float32(a)));
|
||||
result = std::bit_cast<uint32_t>(std::trunc(Float32(a)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -60,12 +60,12 @@ public:
|
||||
: parameters(parameters_) {
|
||||
builder.AddMemoryModel(spv::AddressingModelLogical, spv::MemoryModelGLSL450);
|
||||
|
||||
void_type = Type(spv::OpTypeVoid);
|
||||
uint_type = Type(spv::OpTypeInt, 32u, 0u);
|
||||
int_type = Type(spv::OpTypeInt, 32u, 1u);
|
||||
float_type = Type(spv::OpTypeFloat, 32u);
|
||||
vec4_float_type = Type(spv::OpTypeVector, float_type, 4u);
|
||||
function_type = Type(spv::OpTypeFunction, void_type);
|
||||
void_type = builder.Type(spv::OpTypeVoid);
|
||||
uint_type = builder.Type(spv::OpTypeInt, 32u, 0u);
|
||||
int_type = builder.Type(spv::OpTypeInt, 32u, 1u);
|
||||
float_type = builder.Type(spv::OpTypeFloat, 32u);
|
||||
vec4_float_type = builder.Type(spv::OpTypeVector, float_type, 4u);
|
||||
function_type = builder.Type(spv::OpTypeFunction, void_type);
|
||||
|
||||
per_vertex_type = builder.DecoratedType(
|
||||
spv::OpTypeStruct,
|
||||
@@ -76,12 +76,12 @@ public:
|
||||
ptr_input_vec4_float = Pointer(spv::StorageClassInput, vec4_float_type);
|
||||
ptr_output_vec4_float = Pointer(spv::StorageClassOutput, vec4_float_type);
|
||||
if (model == spv::ExecutionModelTessellationControl) {
|
||||
bool_type = Type(spv::OpTypeBool);
|
||||
vec2_bool_type = Type(spv::OpTypeVector, bool_type, 2u);
|
||||
vec2_float_type = Type(spv::OpTypeVector, float_type, 2u);
|
||||
bool_type = builder.Type(spv::OpTypeBool);
|
||||
vec2_bool_type = builder.Type(spv::OpTypeVector, bool_type, 2u);
|
||||
vec2_float_type = builder.Type(spv::OpTypeVector, float_type, 2u);
|
||||
ptr_output_float = Pointer(spv::StorageClassOutput, float_type);
|
||||
} else {
|
||||
vec3_float_type = Type(spv::OpTypeVector, float_type, 3u);
|
||||
vec3_float_type = builder.Type(spv::OpTypeVector, float_type, 3u);
|
||||
ptr_input_float = Pointer(spv::StorageClassInput, float_type);
|
||||
}
|
||||
}
|
||||
@@ -161,8 +161,8 @@ public:
|
||||
Store(Access(ptr_output_vec4_float, outputs[i], invocation), value);
|
||||
}
|
||||
|
||||
Emit(spv::OpReturn);
|
||||
Emit(spv::OpFunctionEnd);
|
||||
builder.AddFunction(spv::OpReturn);
|
||||
builder.AddFunction(spv::OpFunctionEnd);
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
@@ -184,27 +184,22 @@ public:
|
||||
Load(vec4_float_type, Access(ptr_input_vec4_float, inputs[i], index)));
|
||||
}
|
||||
|
||||
Emit(spv::OpReturn);
|
||||
Emit(spv::OpFunctionEnd);
|
||||
builder.AddFunction(spv::OpReturn);
|
||||
builder.AddFunction(spv::OpFunctionEnd);
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename... Args>
|
||||
uint32_t Type(spv::Op opcode, Args... operands) {
|
||||
return builder.Type(opcode, operands...);
|
||||
}
|
||||
|
||||
uint32_t Constant(uint32_t type, uint32_t value) {
|
||||
return builder.Constant(spv::OpConstant, type, value);
|
||||
}
|
||||
|
||||
uint32_t Pointer(spv::StorageClass storage, uint32_t type) {
|
||||
return Type(spv::OpTypePointer, storage, type);
|
||||
return builder.Type(spv::OpTypePointer, storage, type);
|
||||
}
|
||||
|
||||
uint32_t Array(uint32_t type, uint32_t size) {
|
||||
return Type(spv::OpTypeArray, type, Uint(size));
|
||||
return builder.Type(spv::OpTypeArray, type, Uint(size));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
@@ -221,11 +216,6 @@ private:
|
||||
return id;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void Emit(spv::Op opcode, Args... operands) {
|
||||
builder.AddFunction(opcode, operands...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
uint32_t Access(uint32_t pointer_type, uint32_t base, Args... indices) {
|
||||
return Result(spv::OpAccessChain, pointer_type, base, indices...);
|
||||
@@ -233,7 +223,7 @@ private:
|
||||
|
||||
uint32_t Load(uint32_t type, uint32_t pointer) { return Result(spv::OpLoad, type, pointer); }
|
||||
|
||||
void Store(uint32_t pointer, uint32_t value) { Emit(spv::OpStore, pointer, value); }
|
||||
void Store(uint32_t pointer, uint32_t value) { builder.AddFunction(spv::OpStore, pointer, value); }
|
||||
|
||||
uint32_t Int(uint32_t value) { return Constant(int_type, value); }
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "common/common.h"
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/magicEnum.h"
|
||||
#include "common/profiler.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
@@ -722,6 +721,7 @@ void BuildStageStaticKey(const ShaderPixelInputInfo& info, std::vector<uint32_t>
|
||||
key.push_back(static_cast<uint32_t>(info.ps_depth_export_enable));
|
||||
key.push_back(static_cast<uint32_t>(info.ps_sample_mask_export_enable));
|
||||
key.push_back(static_cast<uint32_t>(info.ps_early_z));
|
||||
key.push_back(static_cast<uint32_t>(info.dual_source_blending));
|
||||
key.insert(key.end(), std::begin(info.target_output_mode), std::end(info.target_output_mode));
|
||||
for (uint32_t base = 0; base < info.target_export_mapping.size(); base += 4u) {
|
||||
uint32_t packed = 0;
|
||||
@@ -1006,8 +1006,4 @@ void ShaderDbgDumpInputInfo(const ShaderComputeInputInfo& info) {
|
||||
info.group_id[1] ? "true" : "false", info.group_id[2] ? "true" : "false");
|
||||
}
|
||||
|
||||
bool ShaderAddressValid(uint64_t addr) {
|
||||
return reinterpret_cast<const uint32_t*>(addr) != nullptr;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
@@ -175,6 +175,7 @@ struct ShaderPixelInputInfo {
|
||||
bool ps_depth_export_enable = false;
|
||||
bool ps_sample_mask_export_enable = false;
|
||||
bool ps_sample_shading = false;
|
||||
bool dual_source_blending = false;
|
||||
bool ps_early_z = false;
|
||||
bool ps_execute_on_noop = false;
|
||||
ShaderStageRuntime stage;
|
||||
@@ -303,7 +304,6 @@ void ShaderMapUserData(uint64_t addr, const ShaderMappedData& data);
|
||||
void ShaderDbgDumpInputInfo(const ShaderVertexInputInfo& info);
|
||||
void ShaderDbgDumpInputInfo(const ShaderPixelInputInfo& info);
|
||||
void ShaderDbgDumpInputInfo(const ShaderComputeInputInfo& info);
|
||||
bool ShaderAddressValid(uint64_t addr);
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
|
||||
@@ -251,16 +251,15 @@ void FileDescriptors::CloseAll() {
|
||||
void MountPoints::Mount(const std::filesystem::path& folder, const std::string& point) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
auto folder_str = Common::FixDirectorySlash(Common::PathToGenericString(folder));
|
||||
auto point_str = Common::FixDirectorySlash(point);
|
||||
|
||||
Umount(point_str);
|
||||
|
||||
MountPair p;
|
||||
p.dir = folder_str;
|
||||
p.dir = folder;
|
||||
p.point = point_str;
|
||||
|
||||
m_mount_pairs.push_back(p);
|
||||
m_mount_pairs.push_back(std::move(p));
|
||||
}
|
||||
|
||||
void MountPoints::Umount(const std::string& folder_or_point) {
|
||||
@@ -270,7 +269,7 @@ void MountPoints::Umount(const std::string& folder_or_point) {
|
||||
|
||||
const auto it = std::find_if(
|
||||
m_mount_pairs.begin(), m_mount_pairs.end(), [&folder_or_point_str](const MountPair& p) {
|
||||
return Common::PathToGenericString(p.dir) == folder_or_point_str ||
|
||||
return Common::FixDirectorySlash(Common::PathToGenericString(p.dir)) == folder_or_point_str ||
|
||||
p.point == folder_or_point_str;
|
||||
});
|
||||
if (it != m_mount_pairs.end()) {
|
||||
@@ -334,23 +333,26 @@ std::filesystem::path MountPoints::ResolvePath(const std::string& mounted_name)
|
||||
|
||||
// Match the entire guest path so a mount root works with or without a trailing slash.
|
||||
const auto mounted_path = Common::FixDirectorySlash(mounted_name);
|
||||
const auto it = std::find_if(
|
||||
const auto it = std::find_if(
|
||||
m_mount_pairs.begin(), m_mount_pairs.end(),
|
||||
[&mounted_path](const MountPair& p) { return Common::StartsWith(mounted_path, p.point); });
|
||||
[&mounted_path](const MountPair& p) { return mounted_path.starts_with(p.point); });
|
||||
if (it != m_mount_pairs.end()) {
|
||||
const auto& p = *it;
|
||||
auto rel_path = Common::RemoveFirst(Common::FixFilenameSlash(mounted_name), p.point.size());
|
||||
while (Common::StartsWith(rel_path, '/')) {
|
||||
while (rel_path.starts_with('/')) {
|
||||
rel_path = Common::RemoveFirst(rel_path, 1);
|
||||
}
|
||||
|
||||
const auto native_rel_path = Common::PathFromUtf8(rel_path);
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
if (HasWindowsForbiddenFilenameCharacter(rel_path)) {
|
||||
::printf("FileSystem: Windows-incompatible guest filename: %s\n",
|
||||
mounted_name.c_str());
|
||||
}
|
||||
return p.dir / rel_path;
|
||||
return p.dir / native_rel_path;
|
||||
#else
|
||||
return ResolvePathIgnoringCase(p.dir / rel_path);
|
||||
return ResolvePathIgnoringCase(p.dir / native_rel_path);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+10
-13
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/magicEnum.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/threads.h"
|
||||
#include "common/virtualMemory.h"
|
||||
@@ -14,9 +13,11 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <magic_enum.hpp>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@@ -2324,8 +2325,8 @@ int32_t KYTY_SYSV_ABI KernelMapNamedFlexibleMemory(void** addr_in_out, size_t le
|
||||
"\t flags = 0x%08" PRIx32 "\n"
|
||||
"\t name = %s\n"
|
||||
"\t gpu_mode = %s\n",
|
||||
in_addr, out_addr, len, Common::EnumName(mode).c_str(), static_cast<uint32_t>(flags), name,
|
||||
Common::EnumName(gpu_mode).c_str());
|
||||
in_addr, out_addr, len, magic_enum::enum_name(mode), static_cast<uint32_t>(flags), name,
|
||||
magic_enum::enum_name(gpu_mode));
|
||||
|
||||
MapGpuRange(out_addr, len);
|
||||
|
||||
@@ -3041,8 +3042,8 @@ int KYTY_SYSV_ABI KernelMapDirectMemory(void** addr, size_t len, int prot, int f
|
||||
"\t shared = %s\n"
|
||||
"\t reason = %s\n",
|
||||
in_addr, out_addr, static_cast<uint64_t>(direct_memory_start), len,
|
||||
Common::EnumName(mode).c_str(), static_cast<uint32_t>(flags), alignment,
|
||||
Common::EnumName(gpu_mode).c_str(), shared_backing ? "yes" : "no", shared_reason);
|
||||
magic_enum::enum_name(mode), static_cast<uint32_t>(flags), alignment,
|
||||
magic_enum::enum_name(gpu_mode), shared_backing ? "yes" : "no", shared_reason);
|
||||
|
||||
if (out_addr == 0) {
|
||||
if (consumed_reservation) {
|
||||
@@ -3305,7 +3306,7 @@ static bool ReplaceFixedRangeWithReserved(uint64_t start, uint64_t size) {
|
||||
"\t reserve-fixed replace: backend unmap failed at 0x%016" PRIx64
|
||||
", size=0x%016" PRIx64 ", type=%s\n",
|
||||
chunk.range.start, chunk.range.size,
|
||||
Common::EnumName(chunk.range.type).c_str());
|
||||
magic_enum::enum_name(chunk.range.type));
|
||||
if (!restore_chunks()) {
|
||||
EXIT("reserve-fixed backend-unmap rollback failed\n");
|
||||
}
|
||||
@@ -3769,7 +3770,7 @@ int KYTY_SYSV_ABI KernelMprotect(const void* addr, size_t len, int prot) {
|
||||
}
|
||||
g_virtual_ranges->Protect(aligned_addr, aligned_len, prot);
|
||||
|
||||
LOGF("\t prot: %s -> %s\n", Common::EnumName(old_mode).c_str(), Common::EnumName(mode).c_str());
|
||||
LOGF("\t prot: %s -> %s\n", magic_enum::enum_name(old_mode), magic_enum::enum_name(mode));
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -3879,10 +3880,6 @@ static bool IsAligned(uint64_t value, uint64_t alignment) {
|
||||
return alignment == 0 || (value & (alignment - 1u)) == 0;
|
||||
}
|
||||
|
||||
static bool IsPowerOfTwo(uint64_t value) {
|
||||
return value != 0 && (value & (value - 1u)) == 0;
|
||||
}
|
||||
|
||||
static void MemoryPoolSubtractCommitted(uint64_t len) {
|
||||
auto current = g_memory_pool_committed.load(std::memory_order_relaxed);
|
||||
while (current != 0) {
|
||||
@@ -3904,7 +3901,7 @@ int KYTY_SYSV_ABI KernelMemoryPoolExpand(int64_t search_start, int64_t search_en
|
||||
if (search_start < 0 || search_end <= search_start || len == 0 ||
|
||||
(len & (POOL_PAGE_SIZE - 1u)) != 0 || phys_addr_out == nullptr ||
|
||||
(alignment != 0 &&
|
||||
(!IsPowerOfTwo(alignment) || (alignment & (POOL_PAGE_SIZE - 1u)) != 0))) {
|
||||
(!std::has_single_bit(alignment) || (alignment & (POOL_PAGE_SIZE - 1u)) != 0))) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
if (static_cast<uint64_t>(search_end - search_start) < len) {
|
||||
@@ -3953,7 +3950,7 @@ int KYTY_SYSV_ABI KernelMemoryPoolReserve(void* addr_in, size_t len, size_t alig
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
if (alignment != 0 &&
|
||||
(!IsPowerOfTwo(alignment) || !IsAligned(alignment, POOL_RESERVE_ALIGNMENT))) {
|
||||
(!std::has_single_bit(alignment) || !IsAligned(alignment, POOL_RESERVE_ALIGNMENT))) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ static uint64_t KernelGetTscFrequencyNative() {
|
||||
}
|
||||
|
||||
KernelReadTscNative();
|
||||
Common::Thread::Sleep(1);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
KernelReadTscNative();
|
||||
|
||||
const auto host_start = Common::Timer::QueryPerformanceCounter();
|
||||
@@ -993,7 +993,7 @@ static void FreeDetachedThreads(void* /*arg*/) {
|
||||
auto* pthread_pool = g_pthread_context->GetPthreadPool();
|
||||
|
||||
while (true) {
|
||||
Common::Thread::Sleep(10000);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
|
||||
pthread_pool->FreeDetachedThreads();
|
||||
}
|
||||
}
|
||||
@@ -1202,13 +1202,6 @@ static int32_t GetDstSeconds() {
|
||||
#endif
|
||||
}
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
static void sec_to_timeval(KernelTimeval* ts, double sec) {
|
||||
ts->tv_sec = static_cast<int64_t>(sec);
|
||||
ts->tv_usec = static_cast<int64_t>((sec - static_cast<double>(ts->tv_sec)) * 1000000.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool GetPosixClockId(KernelClockid clock_id, clockid_t* out) {
|
||||
EXIT_IF(out == nullptr);
|
||||
|
||||
|
||||
@@ -329,12 +329,4 @@
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>cancel_button</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>ConfigurationEditDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef LAUNCHER_INCLUDE_COMMON_H_
|
||||
#define LAUNCHER_INCLUDE_COMMON_H_
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include <QArgument>
|
||||
#include <QObject>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#define KYTY_QT_CLASS_NO_COPY(name) \
|
||||
public: \
|
||||
name(const name&) = delete; /* NOLINT(bugprone-macro-parentheses) */ \
|
||||
name& operator=(const name&) = delete; /* NOLINT(bugprone-macro-parentheses) */ \
|
||||
name(name&&) noexcept = delete; /* NOLINT(bugprone-macro-parentheses) */ \
|
||||
name& operator=(name&&) noexcept = delete; /* NOLINT(bugprone-macro-parentheses) */
|
||||
|
||||
#endif /* LAUNCHER_INCLUDE_COMMON_H_ */
|
||||
@@ -1,7 +1,6 @@
|
||||
#ifndef COMPATIBILITY_DATABASE_H
|
||||
#define COMPATIBILITY_DATABASE_H
|
||||
|
||||
#include "common.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#include <QMap>
|
||||
@@ -15,7 +14,6 @@ struct CompatibilityEntry {
|
||||
|
||||
class CompatibilityDatabase: public QObject {
|
||||
Q_OBJECT
|
||||
KYTY_QT_CLASS_NO_COPY(CompatibilityDatabase);
|
||||
|
||||
public:
|
||||
explicit CompatibilityDatabase(bool local, QObject* parent = nullptr);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#ifndef LAUNCHER_INCLUDE_CONFIGURATION_H_
|
||||
#define LAUNCHER_INCLUDE_CONFIGURATION_H_
|
||||
|
||||
#include "common.h"
|
||||
#include "common/emulatorConfig.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QChar>
|
||||
#include <QMetaEnum>
|
||||
#include <QMetaType>
|
||||
#include <QObject>
|
||||
#include <QSettings>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#ifndef CONFIGURATION_EDIT_DIALOG_H
|
||||
#define CONFIGURATION_EDIT_DIALOG_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
@@ -22,7 +20,6 @@ class ConfigurationEditDialog;
|
||||
|
||||
class ConfigurationEditDialog: public QDialog {
|
||||
Q_OBJECT
|
||||
KYTY_QT_CLASS_NO_COPY(ConfigurationEditDialog);
|
||||
|
||||
public:
|
||||
explicit ConfigurationEditDialog(Configuration& info, QWidget* parent = nullptr);
|
||||
@@ -31,7 +28,6 @@ public:
|
||||
static void WriteSettings(QSettings& s);
|
||||
static void ReadSettings(QSettings& s);
|
||||
|
||||
void SetTitle(const QString& str);
|
||||
void SetGameDirectories(const QStringList& dirs);
|
||||
[[nodiscard]] QStringList GetGameDirectories() const;
|
||||
|
||||
@@ -55,8 +51,6 @@ protected:
|
||||
|
||||
/*slots:*/
|
||||
|
||||
void update_info();
|
||||
void adjust_size();
|
||||
void save();
|
||||
void clear();
|
||||
void add_game_directory();
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#ifndef CONFIGURATION_ITEM_H
|
||||
#define CONFIGURATION_ITEM_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QTreeWidgetItem>
|
||||
|
||||
@@ -23,8 +21,6 @@ public:
|
||||
void Update();
|
||||
bool operator<(const QTreeWidgetItem& other) const override;
|
||||
|
||||
KYTY_QT_CLASS_NO_COPY(ConfigurationItem);
|
||||
|
||||
Configuration& GetInfo() { return *m_info; }
|
||||
[[nodiscard]] const Configuration& GetInfo() const { return *m_info; }
|
||||
// void SetInfo(Configuration* info);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#ifndef CONFIGURATION_LIST_WIDGET_H
|
||||
#define CONFIGURATION_LIST_WIDGET_H
|
||||
|
||||
#include "common.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#include <QMap>
|
||||
@@ -14,7 +13,6 @@ class CompatibilityDatabase;
|
||||
class QEvent;
|
||||
class QTreeWidgetItem;
|
||||
class QPoint;
|
||||
class MainDialog;
|
||||
|
||||
namespace Ui {
|
||||
class ConfigurationListWidget;
|
||||
@@ -22,27 +20,20 @@ class ConfigurationListWidget;
|
||||
|
||||
class ConfigurationListWidget: public QWidget {
|
||||
Q_OBJECT
|
||||
KYTY_QT_CLASS_NO_COPY(ConfigurationListWidget);
|
||||
|
||||
public:
|
||||
explicit ConfigurationListWidget(QWidget* parent = nullptr);
|
||||
~ConfigurationListWidget() override;
|
||||
|
||||
void SetRunEnabled(bool flag) { m_run_enabled = flag; }
|
||||
[[nodiscard]] bool IsRunEnabled() const { return m_run_enabled; }
|
||||
void SetRunEnabled(bool flag) { m_run_enabled = flag; }
|
||||
|
||||
[[nodiscard]] const ConfigurationItem* GetSelectedItem() const { return m_selected_item; }
|
||||
ConfigurationItem* GetSelectedItem() { return m_selected_item; }
|
||||
|
||||
void SetMainDialog(MainDialog* main_dialog) { m_main_dialog = main_dialog; }
|
||||
MainDialog* GetMainDialog() { return m_main_dialog; }
|
||||
|
||||
[[nodiscard]] const QString& GetSettingsFile() const { return m_settings_file; }
|
||||
[[nodiscard]] const QStringList& GetGameDirectories() const { return m_game_dirs; }
|
||||
[[nodiscard]] const QStringList& GetHostInputMapping() const {
|
||||
return m_global_info.host_input_mapping;
|
||||
}
|
||||
[[nodiscard]] bool CanViewSelectedTrophies() const;
|
||||
|
||||
bool EnsureGameDirectory();
|
||||
void ScanGameDirectory();
|
||||
@@ -66,8 +57,6 @@ protected slots:
|
||||
void delete_configuartion();
|
||||
void edit_global_settings();
|
||||
void edit_input_mapping();
|
||||
void run_configuration();
|
||||
void list_currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous);
|
||||
void list_itemDoubleClicked(QTreeWidgetItem* witem, int column);
|
||||
void show_context_menu(const QPoint& pos);
|
||||
void open_game_folder();
|
||||
@@ -84,7 +73,6 @@ private:
|
||||
ConfigurationItem* m_selected_item = nullptr;
|
||||
bool m_run_enabled = true;
|
||||
Ui::ConfigurationListWidget* m_ui = nullptr;
|
||||
MainDialog* m_main_dialog = nullptr;
|
||||
QString m_settings_file;
|
||||
QStringList m_game_dirs;
|
||||
Configuration m_global_info;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#ifndef MAIN_DIALOG_H
|
||||
#define MAIN_DIALOG_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QString>
|
||||
|
||||
@@ -15,11 +13,9 @@ class QResizeEvent;
|
||||
class Configuration;
|
||||
class MainDialog: public QDialog {
|
||||
Q_OBJECT
|
||||
KYTY_QT_CLASS_NO_COPY(MainDialog);
|
||||
|
||||
signals:
|
||||
void Start();
|
||||
void Quit();
|
||||
void Resize();
|
||||
|
||||
public:
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#ifndef MANDATORY_LINEEDIT_H
|
||||
#define MANDATORY_LINEEDIT_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
class QPaintEvent;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#ifndef PATCHES_DIALOG_H
|
||||
#define PATCHES_DIALOG_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QString>
|
||||
|
||||
@@ -16,8 +14,6 @@ public:
|
||||
explicit PatchesDialog(const Configuration& game, QWidget* parent = nullptr);
|
||||
~PatchesDialog() override = default;
|
||||
|
||||
KYTY_QT_CLASS_NO_COPY(PatchesDialog);
|
||||
|
||||
[[nodiscard]] static bool IsSupportedTitleId(const QString& title_id);
|
||||
[[nodiscard]] static QString PatchPlanPath(const QString& title_id);
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#ifndef TROPHY_VIEWER_DIALOG_H
|
||||
#define TROPHY_VIEWER_DIALOG_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class QTabWidget;
|
||||
@@ -10,8 +8,6 @@ class QWidget;
|
||||
|
||||
class Configuration;
|
||||
class TrophyViewerDialog: public QDialog {
|
||||
KYTY_QT_CLASS_NO_COPY(TrophyViewerDialog);
|
||||
|
||||
public:
|
||||
explicit TrophyViewerDialog(QWidget* parent = nullptr);
|
||||
|
||||
|
||||
@@ -108,6 +108,7 @@ ConfigurationEditDialog::ConfigurationEditDialog(Configuration& info, QWidget* p
|
||||
InitGameDirectories();
|
||||
|
||||
connect(m_ui->ok_button, &QPushButton::clicked, this, &ConfigurationEditDialog::save);
|
||||
connect(m_ui->cancel_button, &QPushButton::clicked, this, &QDialog::reject);
|
||||
connect(m_ui->clear_button, &QPushButton::clicked, this, &ConfigurationEditDialog::clear);
|
||||
connect(m_ui->comboBox_shader_log_direction, &QComboBox::currentTextChanged, this,
|
||||
[this](const QString& text) {
|
||||
@@ -213,7 +214,7 @@ void ConfigurationEditDialog::Init(const Configuration& info) {
|
||||
#endif
|
||||
QVulkanInstance instance;
|
||||
instance.setApiVersion(QVersionNumber(1, 3, 0));
|
||||
#if !defined(__APPLE__)
|
||||
#if !defined(__APPLE__) && QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
|
||||
instance.setFlags(QVulkanInstance::NoPortabilityDrivers);
|
||||
#endif
|
||||
if (instance.create()) {
|
||||
@@ -306,10 +307,6 @@ void ConfigurationEditDialog::InitGameDirectories() {
|
||||
update_game_directory_buttons();
|
||||
}
|
||||
|
||||
void ConfigurationEditDialog::SetTitle(const QString& str) {
|
||||
setWindowTitle(str);
|
||||
}
|
||||
|
||||
void ConfigurationEditDialog::SetGameDirectories(const QStringList& dirs) {
|
||||
m_show_game_dirs = true;
|
||||
m_game_dirs_list->clear();
|
||||
@@ -400,14 +397,6 @@ static void UpdateInfo(Configuration& info, Ui::ConfigurationEditDialog& ui) {
|
||||
info.profiler_enabled = ui.checkBox_profiler->isChecked();
|
||||
}
|
||||
|
||||
void ConfigurationEditDialog::update_info() {
|
||||
UpdateInfo(m_info, *m_ui);
|
||||
}
|
||||
|
||||
void ConfigurationEditDialog::adjust_size() {
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
void ConfigurationEditDialog::save() {
|
||||
if (MandatoryLineEdit::FindEmpty(this)) {
|
||||
QMessageBox::critical(this, tr("Save failed"), tr("Please fill all mandatory fields"));
|
||||
@@ -427,7 +416,7 @@ void ConfigurationEditDialog::save() {
|
||||
return;
|
||||
}
|
||||
|
||||
update_info();
|
||||
UpdateInfo(m_info, *m_ui);
|
||||
|
||||
emit accept();
|
||||
}
|
||||
|
||||
@@ -77,10 +77,6 @@ void SetStatus(QComboBox* combo, Configuration::GameStatus status) {
|
||||
combo->setCurrentIndex(index >= 0 ? index : 0);
|
||||
}
|
||||
|
||||
QIcon StandardIcon(QStyle::StandardPixmap icon) {
|
||||
return QApplication::style()->standardIcon(icon);
|
||||
}
|
||||
|
||||
QString GetPathText(const Configuration& info) {
|
||||
return !info.game_path.isEmpty() ? info.game_path : info.basedir;
|
||||
}
|
||||
@@ -278,11 +274,11 @@ void ConfigurationItem::UpdateIcon() {
|
||||
}
|
||||
|
||||
if (m_running) {
|
||||
setIcon(NameColumn, StandardIcon(QStyle::SP_MediaPlay));
|
||||
setIcon(NameColumn, QApplication::style()->standardIcon(QStyle::SP_MediaPlay));
|
||||
} else if (m_info->custom_settings) {
|
||||
setIcon(NameColumn, StandardIcon(QStyle::SP_FileIcon));
|
||||
setIcon(NameColumn, QApplication::style()->standardIcon(QStyle::SP_FileIcon));
|
||||
} else {
|
||||
setIcon(NameColumn, StandardIcon(QStyle::SP_ComputerIcon));
|
||||
setIcon(NameColumn, QApplication::style()->standardIcon(QStyle::SP_ComputerIcon));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "configurationListWidget.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "compatibilityDatabase.h"
|
||||
#include "configuration.h"
|
||||
#include "configurationEditDialog.h"
|
||||
@@ -232,7 +231,7 @@ ConfigurationListWidget::ConfigurationListWidget(QWidget* parent)
|
||||
connect(m_ui->delete_button, &QToolButton::clicked, this,
|
||||
&ConfigurationListWidget::delete_configuartion);
|
||||
connect(m_ui->cfgs_list, &QTreeWidget::currentItemChanged, this,
|
||||
&ConfigurationListWidget::list_currentItemChanged);
|
||||
&ConfigurationListWidget::SelectItem);
|
||||
connect(m_ui->cfgs_list, &QTreeWidget::itemDoubleClicked, this,
|
||||
&ConfigurationListWidget::list_itemDoubleClicked);
|
||||
connect(m_ui->cfgs_list, &QTreeWidget::customContextMenuRequested, this,
|
||||
@@ -676,7 +675,7 @@ void ConfigurationListWidget::edit_configuration() {
|
||||
}
|
||||
|
||||
ConfigurationEditDialog dlg(item->GetInfo(), this);
|
||||
dlg.SetTitle(tr("Edit game settings"));
|
||||
dlg.setWindowTitle(tr("Edit game settings"));
|
||||
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
item->GetInfo().custom_settings = true;
|
||||
@@ -708,7 +707,7 @@ void ConfigurationListWidget::edit_global_settings() {
|
||||
info.name = tr("Global settings");
|
||||
|
||||
ConfigurationEditDialog dlg(info, this);
|
||||
dlg.SetTitle(tr("Global settings"));
|
||||
dlg.setWindowTitle(tr("Global settings"));
|
||||
dlg.SetGameDirectories(m_game_dirs);
|
||||
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
@@ -731,15 +730,6 @@ void ConfigurationListWidget::ClearCustomSettings(ConfigurationItem* item) {
|
||||
delete m_custom_infos.take(item->GetInfo().game_path);
|
||||
}
|
||||
|
||||
void ConfigurationListWidget::run_configuration() {
|
||||
emit Run();
|
||||
}
|
||||
|
||||
bool ConfigurationListWidget::CanViewSelectedTrophies() const {
|
||||
return m_selected_item != nullptr &&
|
||||
TrophyViewerDialog::HasTrophyData(&m_selected_item->GetInfo());
|
||||
}
|
||||
|
||||
void ConfigurationListWidget::ViewTrophies() {
|
||||
auto* current = static_cast<ConfigurationItem*>(m_ui->cfgs_list->currentItem());
|
||||
auto* item = current != nullptr ? current : m_selected_item;
|
||||
@@ -852,11 +842,6 @@ void ConfigurationListWidget::SelectItem(QTreeWidgetItem* witem) {
|
||||
emit Select();
|
||||
}
|
||||
|
||||
void ConfigurationListWidget::list_currentItemChanged(QTreeWidgetItem* current,
|
||||
QTreeWidgetItem* /*previous*/) {
|
||||
SelectItem(current);
|
||||
}
|
||||
|
||||
void ConfigurationListWidget::list_itemDoubleClicked(QTreeWidgetItem* witem, int /*column*/) {
|
||||
SelectItem(witem);
|
||||
if (m_run_enabled) {
|
||||
@@ -877,7 +862,7 @@ void ConfigurationListWidget::show_context_menu(const QPoint& pos) {
|
||||
const bool has_trophy_data =
|
||||
item != nullptr && TrophyViewerDialog::HasTrophyData(&item->GetInfo());
|
||||
|
||||
QAction* action_run = menu.addAction(tr("Run"), this, SLOT(run_configuration()));
|
||||
QAction* action_run = menu.addAction(tr("Run"), this, SIGNAL(Run()));
|
||||
QAction* action_open_folder =
|
||||
menu.addAction(style()->standardIcon(QStyle::SP_DirOpenIcon), tr("Open game folder"), this,
|
||||
SLOT(open_game_folder()));
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
#include "launcherTheme.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QArgument>
|
||||
#include <QObject>
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
QApplication a(argc, argv);
|
||||
@@ -11,8 +9,6 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
MainDialog w;
|
||||
|
||||
QObject::connect(&a, &QApplication::aboutToQuit, &w, &MainDialog::Quit);
|
||||
|
||||
w.emit Start();
|
||||
|
||||
w.show();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "mainDialog.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "configuration.h"
|
||||
#include "configurationItem.h"
|
||||
#include "configurationListWidget.h"
|
||||
@@ -17,6 +16,7 @@
|
||||
#include <QIODevice>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QProcess>
|
||||
#include <QRadioButton>
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <QVariant>
|
||||
#include <QtCore>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "ui_main_dialog.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
@@ -58,20 +60,8 @@ constexpr char SETTINGS_MAIN_DIALOG[] = "MainDialog";
|
||||
constexpr char SETTINGS_MAIN_LAST_GEOMETRY[] = "geometry";
|
||||
constexpr char SETTINGS_CHECK_UPDATES[] = "check_updates_on_startup";
|
||||
|
||||
class DetachableProcess: public QProcess {
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
explicit DetachableProcess(QObject* parent = nullptr): QProcess(parent) {}
|
||||
void Detach() {
|
||||
this->waitForStarted();
|
||||
setProcessState(QProcess::NotRunning);
|
||||
}
|
||||
};
|
||||
|
||||
class MainDialogPrivate: public QObject {
|
||||
Q_OBJECT
|
||||
KYTY_QT_CLASS_NO_COPY(MainDialogPrivate);
|
||||
|
||||
public:
|
||||
explicit MainDialogPrivate(QObject* parent = nullptr): QObject(parent) {}
|
||||
@@ -99,7 +89,7 @@ private:
|
||||
UpdateChecker* m_update_checker = nullptr;
|
||||
QString m_interpreter;
|
||||
|
||||
/*DetachableProcess*/ QProcess m_process;
|
||||
QProcess m_process;
|
||||
|
||||
QPointer<ConfigurationItem> m_running_item;
|
||||
};
|
||||
@@ -121,7 +111,6 @@ void MainDialogPrivate::Setup(MainDialog* main_dialog) {
|
||||
|
||||
m_main_dialog = main_dialog;
|
||||
m_update_checker = new UpdateChecker(main_dialog);
|
||||
m_ui->widget->SetMainDialog(main_dialog);
|
||||
m_ui->check_updates_on_startup->setChecked(g_check_updates_on_startup);
|
||||
m_ui->check_updates_link->setVisible(UpdateChecker::IsSupported());
|
||||
m_ui->check_updates_on_startup->setVisible(UpdateChecker::IsSupported());
|
||||
@@ -154,8 +143,6 @@ void MainDialogPrivate::Setup(MainDialog* main_dialog) {
|
||||
Update();
|
||||
});
|
||||
|
||||
// connect(main_dialog, &MainDialog::Quit, [=]() { m_process.Detach(); });
|
||||
|
||||
m_ui->label_settings_file->setText(tr("Settings file: ") + m_ui->widget->GetSettingsFile());
|
||||
|
||||
m_main_dialog->restoreGeometry(g_last_geometry);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
@@ -129,8 +130,11 @@ void PatchesDialog::Save() {
|
||||
document.setObject(root);
|
||||
|
||||
QSaveFile output(path);
|
||||
if (output.open(QIODevice::WriteOnly) && output.write(document.toJson()) >= 0 &&
|
||||
output.commit()) {
|
||||
m_status->setText(tr("Cheat selection saved."));
|
||||
if (!output.open(QIODevice::WriteOnly) || output.write(document.toJson()) < 0 ||
|
||||
!output.commit()) {
|
||||
qWarning() << "Could not save cheat file:" << output.errorString();
|
||||
m_status->setText(tr("Could not save cheat file: %1").arg(output.errorString()));
|
||||
return;
|
||||
}
|
||||
m_status->setText(tr("Cheat selection saved."));
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <QLabel>
|
||||
#include <QMap>
|
||||
#include <QMessageBox>
|
||||
#include <QObject>
|
||||
#include <QPixmap>
|
||||
#include <QRegularExpression>
|
||||
#include <QSet>
|
||||
@@ -32,8 +33,9 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QVariant>
|
||||
#include <QWidget>
|
||||
#include <QtEndian>
|
||||
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -83,21 +85,6 @@ static bool CanRead(const QByteArray& data, qsizetype offset, qsizetype size) {
|
||||
return offset >= 0 && size >= 0 && offset <= data.size() && size <= data.size() - offset;
|
||||
}
|
||||
|
||||
static quint32 ReadBe32(const QByteArray& data, qsizetype offset) {
|
||||
const auto* p = reinterpret_cast<const uchar*>(data.constData() + offset);
|
||||
return (static_cast<quint32>(p[0]) << 24u) | (static_cast<quint32>(p[1]) << 16u) |
|
||||
(static_cast<quint32>(p[2]) << 8u) | static_cast<quint32>(p[3]);
|
||||
}
|
||||
|
||||
static quint64 ReadBe64(const QByteArray& data, qsizetype offset) {
|
||||
return (static_cast<quint64>(ReadBe32(data, offset)) << 32u) |
|
||||
static_cast<quint64>(ReadBe32(data, offset + 4));
|
||||
}
|
||||
|
||||
static bool FitsQSizeType(quint64 value) {
|
||||
return value <= static_cast<quint64>(std::numeric_limits<qsizetype>::max());
|
||||
}
|
||||
|
||||
static QString ReadFixedString(const QByteArray& data, qsizetype offset, qsizetype max_size) {
|
||||
if (!CanRead(data, offset, max_size)) {
|
||||
return {};
|
||||
@@ -125,33 +112,33 @@ static bool ReadUcp(const QString& file_name, QMap<QString, QByteArray>& files,
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto magic = ReadBe32(data, 0x00);
|
||||
const auto magic = qFromBigEndian<quint32>(data.constData() + 0x00);
|
||||
if (magic != UCP_MAGIC) {
|
||||
error = QObject::tr("%1 has an invalid trophy package magic.")
|
||||
.arg(QFileInfo(file_name).fileName());
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto version = ReadBe32(data, 0x04);
|
||||
const auto version = qFromBigEndian<quint32>(data.constData() + 0x04);
|
||||
if (version != UCP_VERSION) {
|
||||
error = QObject::tr("%1 uses unsupported trophy package version %2.")
|
||||
.arg(QFileInfo(file_name).fileName(), QString::number(version));
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto declared_size = ReadBe64(data, 0x08);
|
||||
const auto declared_size = qFromBigEndian<quint64>(data.constData() + 0x08);
|
||||
if (declared_size > static_cast<quint64>(data.size())) {
|
||||
error = QObject::tr("%1 is truncated.").arg(QFileInfo(file_name).fileName());
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto file_count = ReadBe32(data, 0x10);
|
||||
const auto toc_offset = static_cast<quint64>(ReadBe32(data, 0x14));
|
||||
const auto file_count = qFromBigEndian<quint32>(data.constData() + 0x10);
|
||||
const auto toc_offset = static_cast<quint64>(qFromBigEndian<quint32>(data.constData() + 0x14));
|
||||
const auto data_size = static_cast<quint64>(data.size());
|
||||
|
||||
const quint64 table_size = UCP_TOC_SKIP + static_cast<quint64>(file_count) * UCP_ENTRY_LEN;
|
||||
if (toc_offset > data_size || table_size > data_size - toc_offset ||
|
||||
!FitsQSizeType(toc_offset + table_size)) {
|
||||
!std::in_range<qsizetype>(toc_offset + table_size)) {
|
||||
error = QObject::tr("%1 has an invalid table of contents.")
|
||||
.arg(QFileInfo(file_name).fileName());
|
||||
return false;
|
||||
@@ -162,14 +149,14 @@ static bool ReadUcp(const QString& file_name, QMap<QString, QByteArray>& files,
|
||||
static_cast<quint64>(i) * UCP_ENTRY_LEN);
|
||||
UcpEntry entry;
|
||||
entry.name = ReadFixedString(data, entry_offset, UCP_NAME_LEN).trimmed();
|
||||
entry.offset = ReadBe64(data, entry_offset + 0x20);
|
||||
entry.size = ReadBe64(data, entry_offset + 0x28);
|
||||
entry.offset = qFromBigEndian<quint64>(data.constData() + entry_offset + 0x20);
|
||||
entry.size = qFromBigEndian<quint64>(data.constData() + entry_offset + 0x28);
|
||||
|
||||
if (entry.name.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (entry.offset > data_size || entry.size > data_size - entry.offset ||
|
||||
!FitsQSizeType(entry.offset) || !FitsQSizeType(entry.size)) {
|
||||
!std::in_range<qsizetype>(entry.offset) || !std::in_range<qsizetype>(entry.size)) {
|
||||
error = QObject::tr("%1 has an invalid entry for %2.")
|
||||
.arg(QFileInfo(file_name).fileName(), entry.name);
|
||||
return false;
|
||||
|
||||
+3
-2
@@ -849,8 +849,9 @@ int KYTY_SYSV_ABI AjmMemoryUnregister(uint32_t context, void* ptr) {
|
||||
int KYTY_SYSV_ABI AjmBatchInitialize(void* buffer, size_t size, AjmBatchInfo* info) {
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buffer == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED(info == nullptr);
|
||||
if (buffer == nullptr || info == nullptr) {
|
||||
return AJM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
info->buffer = buffer;
|
||||
info->offset = 0;
|
||||
|
||||
+32
-22
@@ -38,9 +38,9 @@ static constexpr uint32_t AJM_DEC_M4AAC_MAX_SAMPLING_FREQ_INDEX = 11;
|
||||
|
||||
class AjmAacDecoder final: public AjmDecoder {
|
||||
public:
|
||||
AjmAacDecoder(uint32_t channels, uint32_t sample_rate, AjmSampleEncoding encoding,
|
||||
AjmAacDecoder(uint32_t max_channels, uint32_t sample_rate, AjmSampleEncoding encoding,
|
||||
uint64_t flags)
|
||||
: AjmDecoder(channels, sample_rate, encoding), m_flags(flags) {
|
||||
: AjmDecoder(0, sample_rate, encoding), m_max_channels(max_channels), m_flags(flags) {
|
||||
m_codec = avcodec_find_decoder(AV_CODEC_ID_AAC);
|
||||
if (m_codec == nullptr) {
|
||||
LOGF("AJM AAC: avcodec_find_decoder failed\n");
|
||||
@@ -94,6 +94,11 @@ public:
|
||||
((m_flags & AJM_INSTANCE_FLAG_DEC_M4AAC_ENABLE_NONDELAY_OUTPUT) != 0 ? 0u : 2u);
|
||||
m_total_decoded_samples = 0;
|
||||
m_bitrate = 0;
|
||||
m_channels = 0;
|
||||
m_raw_configured = false;
|
||||
if (m_config_number == AJM_DEC_M4AAC_CONFIG_NUMBER_RAW) {
|
||||
m_sample_rate = SamplingRateFromIndex(m_sampling_freq_index);
|
||||
}
|
||||
if (m_is_initialized) {
|
||||
OpenDecoder();
|
||||
}
|
||||
@@ -189,26 +194,6 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_config_number == AJM_DEC_M4AAC_CONFIG_NUMBER_RAW) {
|
||||
const auto sample_index =
|
||||
std::min(m_sampling_freq_index, AJM_DEC_M4AAC_MAX_SAMPLING_FREQ_INDEX);
|
||||
const auto channels = std::clamp<uint32_t>(m_channels, 1u, AJM_DEC_M4AAC_MAX_CHANNELS);
|
||||
constexpr uint32_t audio_object_type = 2;
|
||||
|
||||
auto* extradata = static_cast<uint8_t*>(av_mallocz(2 + AV_INPUT_BUFFER_PADDING_SIZE));
|
||||
if (extradata == nullptr) {
|
||||
Release();
|
||||
return;
|
||||
}
|
||||
|
||||
extradata[0] = static_cast<uint8_t>((audio_object_type << 3u) | (sample_index >> 1u));
|
||||
extradata[1] = static_cast<uint8_t>(((sample_index & 0x1u) << 7u) |
|
||||
(std::min(channels, 7u) << 3u));
|
||||
m_codec_context->extradata = extradata;
|
||||
m_codec_context->extradata_size = 2;
|
||||
SetFormat(channels, SamplingRateFromIndex(sample_index), m_sample_encoding);
|
||||
}
|
||||
|
||||
if (avcodec_open2(m_codec_context, m_codec, nullptr) < 0) {
|
||||
LOGF("AJM AAC: decoder initialization failed\n");
|
||||
Release();
|
||||
@@ -222,6 +207,10 @@ private:
|
||||
}
|
||||
|
||||
const auto channels = static_cast<uint32_t>(std::max(frame->ch_layout.nb_channels, 1));
|
||||
if (channels > m_max_channels) {
|
||||
result->result = AJM_RESULT_TOO_MANY_CHANNELS;
|
||||
return false;
|
||||
}
|
||||
const auto bpf = channels * AjmBytesPerSample(m_sample_encoding);
|
||||
if (bpf == 0) {
|
||||
result->result = AJM_RESULT_INVALID_PARAMETER;
|
||||
@@ -306,6 +295,25 @@ private:
|
||||
return false;
|
||||
}
|
||||
std::memcpy(packet->data, packet_data, static_cast<size_t>(packet_size));
|
||||
if (m_config_number == AJM_DEC_M4AAC_CONFIG_NUMBER_RAW && !m_raw_configured) {
|
||||
// Only indexed stereo starts with a channel pair; the instance limit is not
|
||||
// the stream's channel count. A leading SCE remains ambiguous.
|
||||
const auto channels = (packet_data[0] >> 5u) == 1u
|
||||
? 2u
|
||||
: std::clamp(m_max_channels, 1u, AJM_DEC_M4AAC_MAX_CHANNELS);
|
||||
auto* extradata = av_packet_new_side_data(packet, AV_PKT_DATA_NEW_EXTRADATA, 2);
|
||||
if (extradata == nullptr) {
|
||||
av_packet_free(&packet);
|
||||
result->result = AJM_RESULT_CODEC_ERROR | AJM_RESULT_FATAL;
|
||||
return false;
|
||||
}
|
||||
constexpr uint32_t audio_object_type = 2;
|
||||
extradata[0] = static_cast<uint8_t>((audio_object_type << 3u) |
|
||||
(m_sampling_freq_index >> 1u));
|
||||
extradata[1] = static_cast<uint8_t>(((m_sampling_freq_index & 1u) << 7u) |
|
||||
(std::min(channels, 7u) << 3u));
|
||||
m_raw_configured = true;
|
||||
}
|
||||
|
||||
int rc = avcodec_send_packet(m_codec_context, packet);
|
||||
av_packet_free(&packet);
|
||||
@@ -353,12 +361,14 @@ private:
|
||||
|
||||
const AVCodec* m_codec = nullptr;
|
||||
AVCodecContext* m_codec_context = nullptr;
|
||||
const uint32_t m_max_channels;
|
||||
uint64_t m_flags = 0;
|
||||
uint32_t m_config_number = AJM_DEC_M4AAC_CONFIG_NUMBER_ADTS;
|
||||
uint32_t m_sampling_freq_index = 3;
|
||||
uint32_t m_skip_frames = 2;
|
||||
uint32_t m_bitrate = 0;
|
||||
bool m_is_initialized = false;
|
||||
bool m_raw_configured = false;
|
||||
};
|
||||
|
||||
} // namespace Libs::Audio::Ajm
|
||||
|
||||
@@ -72,6 +72,7 @@ constexpr int32_t AJM_RESULT_INVALID_DATA = 0x00000002;
|
||||
constexpr int32_t AJM_RESULT_INVALID_PARAMETER = 0x00000004;
|
||||
constexpr int32_t AJM_RESULT_PARTIAL_INPUT = 0x00000008;
|
||||
constexpr int32_t AJM_RESULT_NOT_ENOUGH_ROOM = 0x00000010;
|
||||
constexpr int32_t AJM_RESULT_TOO_MANY_CHANNELS = 0x00000040;
|
||||
constexpr int32_t AJM_RESULT_CODEC_ERROR = 0x40000000;
|
||||
constexpr int32_t AJM_RESULT_FATAL = static_cast<int32_t>(0x80000000u);
|
||||
|
||||
|
||||
+547
-136
@@ -5,11 +5,11 @@
|
||||
#include "common/common.h"
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/magicEnum.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/threads.h"
|
||||
#include "kernel/pthread.h"
|
||||
#include "kernel/semaphore.h"
|
||||
#include "libs/ajm/atrac9_decoder.h"
|
||||
#include "libs/audio_internal.h"
|
||||
#include "libs/errno.h"
|
||||
#include "libs/libs.h"
|
||||
@@ -19,7 +19,10 @@
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <magic_enum.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "libatrac9.h"
|
||||
@@ -800,8 +803,7 @@ int KYTY_SYSV_ABI AudioOutOpen(int user_id, int type, int index, uint32_t len, u
|
||||
default:;
|
||||
}
|
||||
|
||||
LOGF("\t param = %u (format=%u, %s)\n", param, format_param,
|
||||
Common::EnumName(format).c_str());
|
||||
LOGF("\t param = %u (format=%u, %s)\n", param, format_param, magic_enum::enum_name(format));
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(format == Audio::Format::Unknown);
|
||||
|
||||
@@ -966,7 +968,7 @@ static int OpenPort(int user_id, int type, int index, uint32_t len, uint32_t fre
|
||||
default: return AUDIO_IN_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
LOGF("\t param = %u (%s)\n", param, Common::EnumName(format).c_str());
|
||||
LOGF("\t param = %u (%s)\n", param, magic_enum::enum_name(format));
|
||||
|
||||
EXIT_IF(g_audio == nullptr);
|
||||
|
||||
@@ -1825,26 +1827,146 @@ enum class Ngs2RackType {
|
||||
CustomSampler,
|
||||
};
|
||||
|
||||
struct Ngs2UserFxContext {
|
||||
void* common;
|
||||
void* param;
|
||||
void* work;
|
||||
uintptr_t user_data;
|
||||
uint32_t max_voices;
|
||||
uint32_t voice_index;
|
||||
uint64_t reserved[4] {};
|
||||
};
|
||||
|
||||
struct Ngs2UserFxProcessContext {
|
||||
float** channels;
|
||||
void* common;
|
||||
const void* param;
|
||||
void* work;
|
||||
void* state;
|
||||
uintptr_t user_data;
|
||||
uint32_t flags;
|
||||
uint32_t input_channels;
|
||||
uint32_t output_channels;
|
||||
uint32_t grain_samples;
|
||||
uint32_t sample_rate;
|
||||
uint32_t reserved {};
|
||||
uint64_t reserved2[4] {};
|
||||
};
|
||||
|
||||
struct Ngs2UserFxOption {
|
||||
Ngs2CustomModuleOption header;
|
||||
int KYTY_SYSV_ABI (*setup)(Ngs2UserFxContext*);
|
||||
int KYTY_SYSV_ABI (*cleanup)(Ngs2UserFxContext*);
|
||||
uintptr_t control;
|
||||
int KYTY_SYSV_ABI (*process)(Ngs2UserFxProcessContext*);
|
||||
size_t common_size;
|
||||
size_t param_size;
|
||||
size_t work_size;
|
||||
uintptr_t user_data;
|
||||
};
|
||||
|
||||
struct Ngs2RackInternal {
|
||||
Ngs2Internal* ngs = nullptr;
|
||||
Ngs2RackInternal* next = nullptr;
|
||||
Ngs2RackType type = Ngs2RackType::Sampler;
|
||||
Ngs2RackOptionUnion option;
|
||||
Ngs2ContextBufferInfo buffer_info;
|
||||
Ngs2BufferAllocator allocator;
|
||||
Ngs2Internal* ngs = nullptr;
|
||||
Ngs2RackInternal* next = nullptr;
|
||||
Ngs2RackType type = Ngs2RackType::Sampler;
|
||||
Ngs2RackOptionUnion option;
|
||||
Ngs2ContextBufferInfo buffer_info;
|
||||
Ngs2BufferAllocator allocator;
|
||||
std::array<std::vector<uint8_t>, 24> common;
|
||||
std::array<Ngs2UserFxOption, 24> fx {};
|
||||
};
|
||||
|
||||
enum class Ngs2VoicePlayState { Empty, Playing, Paused, Stopped };
|
||||
|
||||
enum class Ngs2VoicePlayEvent { None, Play, Pause, Resume, Stop, StopImm, Kill };
|
||||
|
||||
struct Ngs2VoiceInternal {
|
||||
Ngs2VoicePlayEvent event = Ngs2VoicePlayEvent::None;
|
||||
Ngs2VoicePlayState state = Ngs2VoicePlayState::Empty;
|
||||
Ngs2RackInternal* rack = nullptr;
|
||||
uintptr_t callback = 0;
|
||||
uintptr_t callback_data = 0;
|
||||
uint32_t callback_flags = 0;
|
||||
struct Port {
|
||||
Ngs2VoiceInternal* dest = nullptr;
|
||||
uint32_t input = 0;
|
||||
float volume = 1.0f;
|
||||
int32_t matrix = -1;
|
||||
};
|
||||
struct Block {
|
||||
const uint8_t* data;
|
||||
Ngs2WaveformBlock info;
|
||||
uint32_t cursor = 0;
|
||||
size_t data_cursor = 0;
|
||||
uint32_t skip_remaining = info.num_skip_samples;
|
||||
};
|
||||
struct Module {
|
||||
std::vector<uint8_t> param, work, state;
|
||||
uint32_t flags = 1;
|
||||
};
|
||||
Ngs2VoicePlayEvent event = Ngs2VoicePlayEvent::None;
|
||||
Ngs2VoicePlayState state = Ngs2VoicePlayState::Empty;
|
||||
Ngs2RackInternal* rack = nullptr;
|
||||
uintptr_t callback = 0;
|
||||
uintptr_t callback_data = 0;
|
||||
uint32_t callback_flags = 0;
|
||||
std::deque<Block> blocks;
|
||||
std::vector<Port> ports;
|
||||
std::vector<std::vector<float>> matrices;
|
||||
std::vector<Module> modules;
|
||||
std::vector<float> samples;
|
||||
std::vector<float> output_matrix;
|
||||
uint32_t channels = 0;
|
||||
uint32_t output_id = 0;
|
||||
bool rendering = false;
|
||||
bool rendered = false;
|
||||
bool has_samples = false;
|
||||
|
||||
std::unique_ptr<Ajm::AjmAt9Decoder> decoder;
|
||||
std::vector<float> decoded_frame;
|
||||
bool accepts_blocks = true;
|
||||
|
||||
void SetupSampler(const Ngs2WaveformFormat& format) {
|
||||
EXIT_NOT_IMPLEMENTED(format.sample_rate != rack->ngs->option.sample_rate ||
|
||||
format.frame_margin != 0 || format.frame_offset != 0);
|
||||
channels = format.num_channels;
|
||||
blocks.clear();
|
||||
decoder.reset();
|
||||
decoded_frame.clear();
|
||||
accepts_blocks = true;
|
||||
if (format.waveform_type == NGS2_WAVEFORM_TYPE_ATRAC9) {
|
||||
decoder = std::make_unique<Ajm::AjmAt9Decoder>(channels, format.sample_rate,
|
||||
Ajm::AjmSampleEncoding::Float, 0);
|
||||
const auto result =
|
||||
decoder->Initialize(&format.config_data, sizeof(format.config_data));
|
||||
EXIT_NOT_IMPLEMENTED(result.result != OK);
|
||||
EXIT_NOT_IMPLEMENTED(result.format.channel_num != channels ||
|
||||
result.format.sampling_frequency != format.sample_rate);
|
||||
Ajm::AjmSidebandDecAt9CodecInfo info {};
|
||||
decoder->WriteCodecInfo(&info, sizeof(info), result);
|
||||
EXIT_NOT_IMPLEMENTED(info.frame_samples > std::numeric_limits<uint16_t>::max());
|
||||
decoded_frame.resize(info.frame_samples * channels);
|
||||
} else {
|
||||
EXIT_NOT_IMPLEMENTED(format.waveform_type != 0x12);
|
||||
}
|
||||
}
|
||||
|
||||
void SetMatrix(uint32_t index, const float* levels, uint32_t count) {
|
||||
EXIT_NOT_IMPLEMENTED(index >= matrices.size());
|
||||
matrices[index].assign(levels, levels + count);
|
||||
}
|
||||
void SetVolume(uint32_t index, float volume) {
|
||||
EXIT_NOT_IMPLEMENTED(index >= ports.size());
|
||||
ports[index].volume = volume;
|
||||
}
|
||||
void SetPortMatrix(uint32_t index, int32_t matrix) {
|
||||
EXIT_NOT_IMPLEMENTED(index >= ports.size());
|
||||
ports[index].matrix = matrix;
|
||||
}
|
||||
void SetEvent(uint32_t id) {
|
||||
switch (id) {
|
||||
case 1: event = Ngs2VoicePlayEvent::Play; break;
|
||||
case 2: event = Ngs2VoicePlayEvent::Stop; break;
|
||||
case 4: event = Ngs2VoicePlayEvent::StopImm; break;
|
||||
case 8: event = Ngs2VoicePlayEvent::Kill; break;
|
||||
case 16: event = Ngs2VoicePlayEvent::Pause; break;
|
||||
case 32: event = Ngs2VoicePlayEvent::Resume; break;
|
||||
default: EXIT("unknown event_id: 0x%08" PRIx32 "\n", id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Ngs2VoiceParamHeader {
|
||||
@@ -1932,6 +2054,9 @@ static Ngs2RackInternal* g_racks_list = nullptr;
|
||||
static std::atomic_uint32_t g_next_ngs_uid = 1;
|
||||
static Common::Mutex g_racks_mutex;
|
||||
|
||||
static_assert(sizeof(Ngs2UserFxContext) == 72);
|
||||
static_assert(sizeof(Ngs2UserFxProcessContext) == 104);
|
||||
static_assert(sizeof(Ngs2UserFxOption) == 72);
|
||||
static_assert(sizeof(Ngs2SystemOption) == 144);
|
||||
static_assert(sizeof(Ngs2SystemInfo) == 184);
|
||||
static_assert(sizeof(Ngs2RackOption) == 176);
|
||||
@@ -2273,6 +2398,35 @@ int KYTY_SYSV_ABI Ngs2SystemDestroy(uintptr_t system_handle, Ngs2ContextBufferIn
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void Ngs2InitModules(Ngs2RackInternal& rack) {
|
||||
if (!Ngs2RackIsCustom(rack.type)) {
|
||||
return;
|
||||
}
|
||||
const auto& custom = rack.option.custom_sampler.custom_rack_option;
|
||||
EXIT_NOT_IMPLEMENTED(custom.num_buffers != 1);
|
||||
auto* voices = reinterpret_cast<Ngs2VoiceInternal*>(&rack + 1);
|
||||
for (uint32_t m = 0; m < custom.num_modules; ++m) {
|
||||
if (custom.module[m].module_id != 0x1f) {
|
||||
continue;
|
||||
}
|
||||
auto& fx = rack.fx[m];
|
||||
fx = *reinterpret_cast<const Ngs2UserFxOption*>(custom.module[m].option);
|
||||
rack.common[m].resize(fx.common_size);
|
||||
for (uint32_t i = 0; i < rack.option.common.max_voices; ++i) {
|
||||
auto& module = voices[i].modules[m];
|
||||
module.param.resize(fx.param_size);
|
||||
module.work.resize(fx.work_size);
|
||||
module.state.resize(custom.module[m].state_size);
|
||||
if (fx.setup != nullptr) {
|
||||
Ngs2UserFxContext context {
|
||||
rack.common[m].data(), module.param.data(), module.work.data(),
|
||||
fx.user_data, rack.option.common.max_voices, i};
|
||||
EXIT_NOT_IMPLEMENTED(fx.setup(&context) != OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI Ngs2RackCreate(uintptr_t system_handle, uint32_t rack_id,
|
||||
const Ngs2RackOption* option,
|
||||
const Ngs2ContextBufferInfo* buffer_info, uintptr_t* handle) {
|
||||
@@ -2314,7 +2468,7 @@ int KYTY_SYSV_ABI Ngs2RackCreate(uintptr_t system_handle, uint32_t rack_id,
|
||||
static_cast<uint64_t>(buffer_info->host_buffer_size));
|
||||
|
||||
auto* ngs = reinterpret_cast<Ngs2Internal*>(system_handle);
|
||||
auto* rack = static_cast<Ngs2RackInternal*>(buffer_info->host_buffer);
|
||||
auto* rack = new (buffer_info->host_buffer) Ngs2RackInternal {};
|
||||
auto* voices = reinterpret_cast<Ngs2VoiceInternal*>(rack + 1);
|
||||
|
||||
Common::LockGuard lock(ngs->mutex);
|
||||
@@ -2361,7 +2515,7 @@ int KYTY_SYSV_ABI Ngs2RackCreate(uintptr_t system_handle, uint32_t rack_id,
|
||||
default: EXIT("unknown rack_id: 0x%" PRIx32 "\n", rack_id);
|
||||
}
|
||||
|
||||
LOGF("\t type = %s\n", Common::EnumName(rack->type).c_str());
|
||||
LOGF("\t type = %s\n", magic_enum::enum_name(rack->type));
|
||||
|
||||
rack->allocator = Ngs2BufferAllocator();
|
||||
rack->buffer_info = *buffer_info;
|
||||
@@ -2374,10 +2528,15 @@ int KYTY_SYSV_ABI Ngs2RackCreate(uintptr_t system_handle, uint32_t rack_id,
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < option->max_voices; i++) {
|
||||
voices[i].rack = rack;
|
||||
voices[i].event = Ngs2VoicePlayEvent::None;
|
||||
voices[i].state = Ngs2VoicePlayState::Empty;
|
||||
auto& voice = *std::construct_at(voices + i);
|
||||
voice.rack = rack;
|
||||
voice.ports.resize(option->max_ports);
|
||||
voice.matrices.resize(option->max_matrices);
|
||||
if (Ngs2RackIsCustom(rack->type)) {
|
||||
voice.modules.resize(rack->option.custom_sampler.custom_rack_option.num_modules);
|
||||
}
|
||||
}
|
||||
Ngs2InitModules(*rack);
|
||||
|
||||
*handle = reinterpret_cast<uintptr_t>(rack);
|
||||
|
||||
@@ -2407,22 +2566,9 @@ int KYTY_SYSV_ABI Ngs2RackCreateWithAllocator(uintptr_t system_handle, uint32_t
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(option->size < sizeof(Ngs2RackOption));
|
||||
|
||||
LOGF("\t rack_id = 0x%" PRIx32 "\n"
|
||||
"\t name = %.64s\n"
|
||||
"\t flags = %u\n"
|
||||
"\t max_grain_samples = %u\n"
|
||||
"\t max_voices = %u\n"
|
||||
"\t max_input_delay_blocks = %u\n"
|
||||
"\t max_matrices = %u\n"
|
||||
"\t max_ports = %u\n"
|
||||
"\t max_voice_channels = %u\n"
|
||||
"\t max_output_channels = %u\n"
|
||||
"\t alloc_handler = 0x%016" PRIx64 "\n"
|
||||
LOGF("\t alloc_handler = 0x%016" PRIx64 "\n"
|
||||
"\t free_handler = 0x%016" PRIx64 "\n"
|
||||
"\t user_data = 0x%016" PRIx64 "\n",
|
||||
rack_id, option->name, option->flags, option->max_grain_samples, option->max_voices,
|
||||
option->max_input_delay_blocks, option->max_matrices, option->max_ports,
|
||||
option->max_voice_channels, option->max_output_channels,
|
||||
reinterpret_cast<uint64_t>(allocator->alloc_handler),
|
||||
reinterpret_cast<uint64_t>(allocator->free_handler),
|
||||
static_cast<uint64_t>(allocator->user_data));
|
||||
@@ -2478,26 +2624,41 @@ int KYTY_SYSV_ABI Ngs2RackDestroy(uintptr_t rack_handle, Ngs2ContextBufferInfo*
|
||||
if (ngs == nullptr) {
|
||||
return ERROR_INVALID_RACK_HANDLE;
|
||||
}
|
||||
|
||||
Ngs2ContextBufferInfo context_buffer;
|
||||
Ngs2BufferAllocator allocator;
|
||||
{
|
||||
Common::LockGuard lock(ngs->mutex);
|
||||
Common::LockGuard racks_lock(g_racks_mutex);
|
||||
|
||||
auto** link = &g_racks_list;
|
||||
while (*link != nullptr && *link != rack) {
|
||||
link = &(*link)->next;
|
||||
{
|
||||
Common::LockGuard racks_lock(g_racks_mutex);
|
||||
auto** link = &g_racks_list;
|
||||
while (*link != nullptr && *link != rack) {
|
||||
link = &(*link)->next;
|
||||
}
|
||||
if (*link == nullptr) {
|
||||
return ERROR_INVALID_RACK_HANDLE;
|
||||
}
|
||||
*link = rack->next;
|
||||
}
|
||||
if (*link == nullptr) {
|
||||
return ERROR_INVALID_RACK_HANDLE;
|
||||
auto* voices = reinterpret_cast<Ngs2VoiceInternal*>(rack + 1);
|
||||
for (uint32_t i = 0; i < rack->option.common.max_voices; ++i) {
|
||||
for (size_t m = 0; m < voices[i].modules.size(); ++m) {
|
||||
const auto& fx = rack->fx[m];
|
||||
if (fx.cleanup != nullptr) {
|
||||
auto& module = voices[i].modules[m];
|
||||
Ngs2UserFxContext context {rack->common[m].data(),
|
||||
module.param.data(),
|
||||
module.work.data(),
|
||||
fx.user_data,
|
||||
rack->option.common.max_voices,
|
||||
i};
|
||||
EXIT_NOT_IMPLEMENTED(fx.cleanup(&context) != OK);
|
||||
}
|
||||
}
|
||||
std::destroy_at(voices + i);
|
||||
}
|
||||
|
||||
*link = rack->next;
|
||||
context_buffer = rack->buffer_info;
|
||||
allocator = rack->allocator;
|
||||
rack->ngs = nullptr;
|
||||
rack->next = nullptr;
|
||||
std::destroy_at(rack);
|
||||
}
|
||||
|
||||
if (allocator.free_handler != nullptr) {
|
||||
@@ -2540,75 +2701,231 @@ int KYTY_SYSV_ABI Ngs2RackUnlock(uintptr_t rack_handle) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void Ngs2ApplyEvent(Ngs2VoiceInternal& voice) {
|
||||
switch (voice.event) {
|
||||
case Ngs2VoicePlayEvent::None: break;
|
||||
case Ngs2VoicePlayEvent::Play:
|
||||
if (voice.state == Ngs2VoicePlayState::Empty) {
|
||||
voice.state = Ngs2VoicePlayState::Playing;
|
||||
}
|
||||
break;
|
||||
case Ngs2VoicePlayEvent::Pause:
|
||||
if (voice.state == Ngs2VoicePlayState::Playing) {
|
||||
voice.state = Ngs2VoicePlayState::Paused;
|
||||
}
|
||||
break;
|
||||
case Ngs2VoicePlayEvent::Resume:
|
||||
if (voice.state == Ngs2VoicePlayState::Paused) {
|
||||
voice.state = Ngs2VoicePlayState::Playing;
|
||||
}
|
||||
break;
|
||||
case Ngs2VoicePlayEvent::Stop:
|
||||
if (voice.state == Ngs2VoicePlayState::Playing) {
|
||||
voice.state = Ngs2VoicePlayState::Stopped;
|
||||
}
|
||||
break;
|
||||
case Ngs2VoicePlayEvent::StopImm:
|
||||
case Ngs2VoicePlayEvent::Kill: voice.state = Ngs2VoicePlayState::Empty; break;
|
||||
}
|
||||
voice.event = Ngs2VoicePlayEvent::None;
|
||||
}
|
||||
|
||||
static void Ngs2ConsumeSamples(Ngs2VoiceInternal& voice, uint32_t grain) {
|
||||
uint32_t output = 0;
|
||||
while (output < grain && !voice.blocks.empty()) {
|
||||
auto& block = voice.blocks.front();
|
||||
auto count = std::min(grain - output, block.info.num_samples - block.cursor);
|
||||
const int16_t* pcm = nullptr;
|
||||
const float* decoded = nullptr;
|
||||
if (voice.decoder != nullptr) {
|
||||
const auto skip =
|
||||
std::min(block.skip_remaining,
|
||||
static_cast<uint32_t>(voice.decoded_frame.size() / voice.channels));
|
||||
Ajm::AjmGaplessState gapless;
|
||||
gapless.Set({block.info.num_samples - block.cursor, static_cast<uint16_t>(skip), 0},
|
||||
true);
|
||||
const auto result = voice.decoder->Decode(
|
||||
block.data + block.data_cursor, block.info.data_size - block.data_cursor,
|
||||
voice.decoded_frame.data(), voice.decoded_frame.size() * sizeof(float), false,
|
||||
&gapless);
|
||||
EXIT_NOT_IMPLEMENTED(result.result != OK || result.input_consumed == 0);
|
||||
block.data_cursor += result.input_consumed;
|
||||
block.skip_remaining -= skip - gapless.current.skip_samples;
|
||||
const auto decoded_samples =
|
||||
static_cast<uint32_t>(result.output_written / (sizeof(float) * voice.channels));
|
||||
if (decoded_samples == 0) {
|
||||
continue;
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(decoded_samples > count);
|
||||
count = decoded_samples;
|
||||
decoded = voice.decoded_frame.data();
|
||||
} else {
|
||||
pcm = reinterpret_cast<const int16_t*>(block.data) +
|
||||
(block.info.num_skip_samples + block.cursor) * voice.channels;
|
||||
}
|
||||
for (uint32_t c = 0; c < voice.channels; ++c) {
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
const auto index = i * voice.channels + c;
|
||||
voice.samples[c * grain + output + i] =
|
||||
decoded != nullptr ? decoded[index] : pcm[index] / 32768.0f;
|
||||
}
|
||||
}
|
||||
output += count;
|
||||
block.cursor += count;
|
||||
if (block.cursor == block.info.num_samples) {
|
||||
struct CallbackInfo {
|
||||
uintptr_t data, voice;
|
||||
uint32_t flag, reserved;
|
||||
uintptr_t user;
|
||||
const void* block_data;
|
||||
size_t size;
|
||||
uint32_t repeats, attributes;
|
||||
} info {voice.callback_data,
|
||||
reinterpret_cast<uintptr_t>(&voice),
|
||||
1,
|
||||
0,
|
||||
block.info.user_data,
|
||||
block.data,
|
||||
block.info.data_size,
|
||||
0,
|
||||
0};
|
||||
static_assert(sizeof(CallbackInfo) == 56);
|
||||
voice.blocks.pop_front();
|
||||
if (voice.blocks.empty() && !voice.accepts_blocks) {
|
||||
voice.state = Ngs2VoicePlayState::Empty;
|
||||
}
|
||||
if (voice.callback != 0 && (voice.callback_flags & 1u) != 0) {
|
||||
reinterpret_cast<void KYTY_SYSV_ABI (*)(const CallbackInfo*)>(voice.callback)(
|
||||
&info);
|
||||
}
|
||||
}
|
||||
}
|
||||
voice.has_samples = output != 0;
|
||||
}
|
||||
|
||||
static void Ngs2RenderVoice(Ngs2VoiceInternal& voice, const std::vector<Ngs2VoiceInternal*>& voices,
|
||||
uint32_t grain) {
|
||||
if (voice.rendered) {
|
||||
return;
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(voice.rendering);
|
||||
voice.rendering = true;
|
||||
voice.samples.assign(grain * voice.channels, 0.0f);
|
||||
voice.has_samples = false;
|
||||
if (voice.state == Ngs2VoicePlayState::Playing) {
|
||||
if (voice.rack->type == Ngs2RackType::CustomSampler) {
|
||||
Ngs2ConsumeSamples(voice, grain);
|
||||
}
|
||||
for (auto* source: voices) {
|
||||
for (const auto& port: source->ports) {
|
||||
if (port.dest != &voice || port.volume == 0.0f) {
|
||||
continue;
|
||||
}
|
||||
Ngs2RenderVoice(*source, voices, grain);
|
||||
if (!source->has_samples) {
|
||||
continue;
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(port.input != 0);
|
||||
const auto* matrix = port.matrix < 0 ? nullptr : &source->matrices.at(port.matrix);
|
||||
for (uint32_t dst = 0; dst < voice.channels; ++dst) {
|
||||
for (uint32_t src = 0; src < source->channels; ++src) {
|
||||
const float level =
|
||||
port.volume * (matrix == nullptr
|
||||
? (src == dst ? 1.0f : 0.0f)
|
||||
: matrix->at(dst * source->channels + src));
|
||||
for (uint32_t i = 0; i < grain; ++i) {
|
||||
voice.samples[dst * grain + i] +=
|
||||
source->samples[src * grain + i] * level;
|
||||
}
|
||||
}
|
||||
}
|
||||
voice.has_samples = true;
|
||||
}
|
||||
}
|
||||
if (voice.has_samples) {
|
||||
const auto& custom = voice.rack->option.custom_sampler.custom_rack_option;
|
||||
for (size_t m = 0; m < voice.modules.size(); ++m) {
|
||||
EXIT_NOT_IMPLEMENTED(custom.module[m].module_id != 0x1f);
|
||||
EXIT_NOT_IMPLEMENTED(custom.module[m].source_buffer_id != 0 ||
|
||||
custom.module[m].dest_buffer_id != 0);
|
||||
auto& module = voice.modules[m];
|
||||
const auto& fx = voice.rack->fx[m];
|
||||
std::vector<float*> channels(voice.channels);
|
||||
for (uint32_t c = 0; c < voice.channels; ++c) {
|
||||
channels[c] = voice.samples.data() + c * grain;
|
||||
}
|
||||
Ngs2UserFxProcessContext context {channels.data(),
|
||||
voice.rack->common[m].data(),
|
||||
module.param.data(),
|
||||
module.work.data(),
|
||||
module.state.data(),
|
||||
fx.user_data,
|
||||
module.flags,
|
||||
voice.channels,
|
||||
voice.channels,
|
||||
grain,
|
||||
voice.rack->ngs->option.sample_rate};
|
||||
EXIT_NOT_IMPLEMENTED(fx.process(&context) != OK);
|
||||
module.flags = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
voice.rendering = false;
|
||||
voice.rendered = true;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI Ngs2SystemRender(uintptr_t system_handle, const Ngs2RenderBufferInfo* buffer_info,
|
||||
uint32_t num_buffer_info) {
|
||||
static std::atomic_uint32_t render_log_count = 0;
|
||||
const auto log_index = render_log_count.fetch_add(1, std::memory_order_relaxed);
|
||||
const bool log_this_call = (log_index < 16 || (log_index % 600) == 0);
|
||||
|
||||
if (log_this_call) {
|
||||
PRINT_NAME();
|
||||
LOGF("\t call_count = %" PRIu32 "\n", log_index + 1);
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buffer_info == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED(system_handle == 0);
|
||||
EXIT_NOT_IMPLEMENTED(num_buffer_info == 0);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buffer_info == nullptr || system_handle == 0 || num_buffer_info == 0);
|
||||
auto* ngs = reinterpret_cast<Ngs2Internal*>(system_handle);
|
||||
|
||||
Common::LockGuard lock(ngs->mutex);
|
||||
|
||||
for (uint32_t i = 0; i < num_buffer_info; i++) {
|
||||
std::vector<Ngs2VoiceInternal*> voices;
|
||||
{
|
||||
Common::LockGuard racks_lock(g_racks_mutex);
|
||||
for (auto* rack = g_racks_list; rack != nullptr; rack = rack->next) {
|
||||
if (rack->ngs != ngs) {
|
||||
continue;
|
||||
}
|
||||
auto* items = reinterpret_cast<Ngs2VoiceInternal*>(rack + 1);
|
||||
for (uint32_t i = 0; i < rack->option.common.max_voices; ++i) {
|
||||
Ngs2ApplyEvent(items[i]);
|
||||
items[i].rendered = false;
|
||||
if (items[i].channels != 0) {
|
||||
voices.push_back(items + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < num_buffer_info; ++i) {
|
||||
if (buffer_info[i].buffer != nullptr && buffer_info[i].buffer_size != 0) {
|
||||
std::memset(buffer_info[i].buffer, 0, buffer_info[i].buffer_size);
|
||||
}
|
||||
}
|
||||
|
||||
Common::LockGuard racks_lock(g_racks_mutex);
|
||||
for (auto* rack = g_racks_list; rack != nullptr; rack = rack->next) {
|
||||
if (rack->ngs == ngs) {
|
||||
auto* voices = reinterpret_cast<Ngs2VoiceInternal*>(rack + 1);
|
||||
|
||||
for (uint32_t i = 0; i < rack->option.common.max_voices; i++) {
|
||||
auto& voice = voices[i];
|
||||
switch (voice.event) {
|
||||
case Ngs2VoicePlayEvent::None:
|
||||
if (voice.state == Ngs2VoicePlayState::Playing ||
|
||||
voice.state == Ngs2VoicePlayState::Stopped) {
|
||||
voice.state = Ngs2VoicePlayState::Empty;
|
||||
}
|
||||
break;
|
||||
case Ngs2VoicePlayEvent::Play:
|
||||
if (voice.state == Ngs2VoicePlayState::Empty) {
|
||||
voice.state = Ngs2VoicePlayState::Playing;
|
||||
}
|
||||
break;
|
||||
case Ngs2VoicePlayEvent::Pause:
|
||||
if (voice.state == Ngs2VoicePlayState::Playing) {
|
||||
voice.state = Ngs2VoicePlayState::Paused;
|
||||
}
|
||||
break;
|
||||
case Ngs2VoicePlayEvent::Resume:
|
||||
if (voice.state == Ngs2VoicePlayState::Paused) {
|
||||
voice.state = Ngs2VoicePlayState::Playing;
|
||||
}
|
||||
break;
|
||||
case Ngs2VoicePlayEvent::Stop:
|
||||
if (voice.state == Ngs2VoicePlayState::Playing) {
|
||||
voice.state = Ngs2VoicePlayState::Stopped;
|
||||
}
|
||||
break;
|
||||
case Ngs2VoicePlayEvent::StopImm:
|
||||
case Ngs2VoicePlayEvent::Kill: voice.state = Ngs2VoicePlayState::Empty; break;
|
||||
const auto grain = ngs->option.num_grain_samples;
|
||||
for (auto* voice: voices) {
|
||||
Ngs2RenderVoice(*voice, voices, grain);
|
||||
if (voice->rack->type != Ngs2RackType::Mastering || !voice->has_samples) {
|
||||
continue;
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(voice->output_id >= num_buffer_info);
|
||||
const auto& output = buffer_info[voice->output_id];
|
||||
EXIT_NOT_IMPLEMENTED(output.buffer == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED(output.waveform_type != 0x18 ||
|
||||
output.num_channels != voice->channels);
|
||||
EXIT_NOT_IMPLEMENTED(output.buffer_size < grain * output.num_channels * sizeof(float));
|
||||
auto* pcm = static_cast<float*>(output.buffer);
|
||||
for (uint32_t dst = 0; dst < output.num_channels; ++dst) {
|
||||
for (uint32_t src = 0; src < voice->channels; ++src) {
|
||||
const float level = voice->output_matrix.empty()
|
||||
? (src == dst ? 1.0f : 0.0f)
|
||||
: voice->output_matrix.at(src * output.num_channels + dst);
|
||||
for (uint32_t i = 0; i < grain; ++i) {
|
||||
pcm[i * output.num_channels + dst] += voice->samples[src * grain + i] * level;
|
||||
}
|
||||
voice.event = Ngs2VoicePlayEvent::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngs->render_count++;
|
||||
|
||||
++ngs->render_count;
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -2945,6 +3262,7 @@ int KYTY_SYSV_ABI Ngs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamH
|
||||
case 0x0001: {
|
||||
EXIT_NOT_IMPLEMENTED(param->size != sizeof(Ngs2VoiceMatrixLevelsParam));
|
||||
const auto* ml = reinterpret_cast<const Ngs2VoiceMatrixLevelsParam*>(param);
|
||||
voice->SetMatrix(ml->matrix_id, ml->levels, ml->num_levels);
|
||||
LOGF("\t matrix_id = %u\n"
|
||||
"\t num_levels = %u\n"
|
||||
"\t levels = 0x%016" PRIx64 "\n",
|
||||
@@ -2955,6 +3273,7 @@ int KYTY_SYSV_ABI Ngs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamH
|
||||
EXIT_NOT_IMPLEMENTED(param->size != sizeof(Ngs2VoicePortVolumeParam));
|
||||
const auto* volume =
|
||||
reinterpret_cast<const Ngs2VoicePortVolumeParam*>(param);
|
||||
voice->SetVolume(volume->port, volume->level);
|
||||
LOGF("\t port = %u\n"
|
||||
"\t level = %f\n",
|
||||
volume->port, volume->level);
|
||||
@@ -2963,6 +3282,7 @@ int KYTY_SYSV_ABI Ngs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamH
|
||||
case 0x0003: {
|
||||
EXIT_NOT_IMPLEMENTED(param->size != sizeof(Ngs2VoicePortMatrixParam));
|
||||
const auto* pm = reinterpret_cast<const Ngs2VoicePortMatrixParam*>(param);
|
||||
voice->SetPortMatrix(pm->port, pm->matrix_id);
|
||||
LOGF("\t port = %u\n"
|
||||
"\t matrix_id = %d\n",
|
||||
pm->port, pm->matrix_id);
|
||||
@@ -2979,6 +3299,10 @@ int KYTY_SYSV_ABI Ngs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamH
|
||||
case 0x0005: {
|
||||
EXIT_NOT_IMPLEMENTED(param->size != sizeof(Ngs2VoicePatchParam));
|
||||
const auto* patch = reinterpret_cast<const Ngs2VoicePatchParam*>(param);
|
||||
EXIT_NOT_IMPLEMENTED(patch->port >= voice->ports.size());
|
||||
voice->ports[patch->port].dest =
|
||||
reinterpret_cast<Ngs2VoiceInternal*>(patch->dest_handle);
|
||||
voice->ports[patch->port].input = patch->dest_input_id;
|
||||
LOGF("\t connect->port = %u\n"
|
||||
"\t connect->dest_input_id = %u\n"
|
||||
"\t connect->dest_handle = 0x%016" PRIx64 "\n",
|
||||
@@ -2988,15 +3312,7 @@ int KYTY_SYSV_ABI Ngs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamH
|
||||
case 0x0006: {
|
||||
EXIT_NOT_IMPLEMENTED(param->size != sizeof(Ngs2VoiceEventParam));
|
||||
const auto* event = reinterpret_cast<const Ngs2VoiceEventParam*>(param);
|
||||
switch (event->event_id) {
|
||||
case 0x0001: voice->event = Ngs2VoicePlayEvent::Play; break;
|
||||
case 0x0002: voice->event = Ngs2VoicePlayEvent::Stop; break;
|
||||
case 0x0004: voice->event = Ngs2VoicePlayEvent::StopImm; break;
|
||||
case 0x0008: voice->event = Ngs2VoicePlayEvent::Kill; break;
|
||||
case 0x0010: voice->event = Ngs2VoicePlayEvent::Pause; break;
|
||||
case 0x0020: voice->event = Ngs2VoicePlayEvent::Resume; break;
|
||||
default: EXIT("unknown event_id: 0x%08" PRIx32 "\n", event->event_id);
|
||||
}
|
||||
voice->SetEvent(event->event_id);
|
||||
LOGF("\t event = %u\n", event->event_id);
|
||||
break;
|
||||
}
|
||||
@@ -3021,24 +3337,89 @@ int KYTY_SYSV_ABI Ngs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamH
|
||||
case 0x1000: EXIT_NOT_IMPLEMENTED(voice->rack->type != Ngs2RackType::Sampler); break;
|
||||
case 0x2000: EXIT_NOT_IMPLEMENTED(voice->rack->type != Ngs2RackType::Submixer); break;
|
||||
case 0x2001: EXIT_NOT_IMPLEMENTED(voice->rack->type != Ngs2RackType::Reverb); break;
|
||||
case 0x3000: EXIT_NOT_IMPLEMENTED(voice->rack->type != Ngs2RackType::Mastering); break;
|
||||
case 0x4000: {
|
||||
EXIT_NOT_IMPLEMENTED(!Ngs2RackIsCustom(voice->rack->type));
|
||||
auto cid = param->id & 0xffffu;
|
||||
auto module_id = (cid >> 8u) & 0xffu;
|
||||
auto ctl_id = (cid >> 5u) & 0x7u;
|
||||
auto module_no = cid & 0x1fu;
|
||||
LOGF("\t custom module_id = 0x%02" PRIx32 ", ctl_id = 0x%" PRIx32
|
||||
", module_no = %" PRIu32 "\n",
|
||||
module_id, ctl_id, module_no);
|
||||
case 0x3000: {
|
||||
EXIT_NOT_IMPLEMENTED(voice->rack->type != Ngs2RackType::Mastering);
|
||||
const auto* values = reinterpret_cast<const uint32_t*>(param + 1);
|
||||
switch (param->id & 0xffffu) {
|
||||
case 0: voice->channels = values[0]; break;
|
||||
case 1: {
|
||||
const auto* matrix =
|
||||
reinterpret_cast<const Ngs2VoiceMatrixLevelsParam*>(param);
|
||||
EXIT_NOT_IMPLEMENTED(matrix->matrix_id != 5);
|
||||
voice->output_matrix.assign(matrix->levels,
|
||||
matrix->levels + matrix->num_levels);
|
||||
break;
|
||||
}
|
||||
case 5: voice->output_id = values[0]; break;
|
||||
default: EXIT("unsupported mastering control: 0x%08" PRIx32 "\n", param->id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x4001:
|
||||
case 0x4000: {
|
||||
EXIT_NOT_IMPLEMENTED(!Ngs2RackIsCustom(voice->rack->type));
|
||||
const auto index = param->id & 0x1fu;
|
||||
EXIT_NOT_IMPLEMENTED((param->id & 0xffffffe0u) != 0x40001f00u ||
|
||||
index >= voice->modules.size());
|
||||
struct FxParam {
|
||||
Ngs2VoiceParamHeader header;
|
||||
const void* data;
|
||||
size_t size;
|
||||
};
|
||||
const auto& fx = *reinterpret_cast<const FxParam*>(param);
|
||||
auto& module = voice->modules[index];
|
||||
EXIT_NOT_IMPLEMENTED(voice->rack->fx[index].control != 0 ||
|
||||
fx.size != module.param.size());
|
||||
std::memcpy(module.param.data(), fx.data, fx.size);
|
||||
module.flags |= 2;
|
||||
break;
|
||||
}
|
||||
case 0x4001: {
|
||||
EXIT_NOT_IMPLEMENTED(voice->rack->type != Ngs2RackType::CustomSampler);
|
||||
switch (param->id & 0xffffu) {
|
||||
case 0: {
|
||||
const auto& format =
|
||||
*reinterpret_cast<const Ngs2WaveformFormat*>(param + 1);
|
||||
voice->SetupSampler(format);
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
struct BlocksParam {
|
||||
Ngs2VoiceParamHeader header;
|
||||
const uint8_t* data;
|
||||
uint32_t flags, count;
|
||||
const Ngs2WaveformBlock* blocks;
|
||||
};
|
||||
const auto& blocks = *reinterpret_cast<const BlocksParam*>(param);
|
||||
EXIT_NOT_IMPLEMENTED(!voice->accepts_blocks ||
|
||||
(blocks.flags != 0 && blocks.flags != 0x11));
|
||||
voice->accepts_blocks = (blocks.flags & 1u) != 0;
|
||||
for (uint32_t i = 0; i < blocks.count; ++i) {
|
||||
const auto& block = blocks.blocks[i];
|
||||
EXIT_NOT_IMPLEMENTED(block.num_repeats != 0 || block.num_samples == 0);
|
||||
EXIT_NOT_IMPLEMENTED(
|
||||
voice->decoder == nullptr &&
|
||||
(uint64_t(block.num_skip_samples) + block.num_samples) *
|
||||
voice->channels * 2 >
|
||||
block.data_size);
|
||||
voice->blocks.push_back({blocks.data + block.data_offset, block});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
EXIT_NOT_IMPLEMENTED(*reinterpret_cast<const float*>(param + 1) != 1.0f);
|
||||
break;
|
||||
default: EXIT("unsupported sampler control: 0x%08" PRIx32 "\n", param->id);
|
||||
}
|
||||
break;
|
||||
case 0x4002:
|
||||
EXIT_NOT_IMPLEMENTED(voice->rack->type != Ngs2RackType::CustomSubmixer);
|
||||
}
|
||||
case 0x4002: {
|
||||
EXIT_NOT_IMPLEMENTED(voice->rack->type != Ngs2RackType::CustomSubmixer ||
|
||||
(param->id & 0xffffu) != 0);
|
||||
const auto* channels = reinterpret_cast<const uint32_t*>(param + 1);
|
||||
EXIT_NOT_IMPLEMENTED(channels[0] != channels[1]);
|
||||
voice->channels = channels[0];
|
||||
break;
|
||||
}
|
||||
case 0x4003:
|
||||
EXIT_NOT_IMPLEMENTED(voice->rack->type != Ngs2RackType::CustomMastering);
|
||||
break;
|
||||
@@ -3056,14 +3437,44 @@ int KYTY_SYSV_ABI Ngs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamH
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI Ngs2VoiceRunCommands(uintptr_t voice_handle, const void* commands,
|
||||
uint32_t num_commands, uint32_t flags) {
|
||||
PRINT_NAME();
|
||||
|
||||
(void)voice_handle;
|
||||
(void)commands;
|
||||
(void)num_commands;
|
||||
(void)flags;
|
||||
|
||||
size_t num_commands) {
|
||||
struct Command {
|
||||
uint32_t id;
|
||||
uint8_t flags, type;
|
||||
uint16_t count;
|
||||
union {
|
||||
uint32_t u;
|
||||
int32_t i;
|
||||
float f;
|
||||
const float* levels;
|
||||
} value;
|
||||
};
|
||||
auto* voice = reinterpret_cast<Ngs2VoiceInternal*>(voice_handle);
|
||||
Common::LockGuard lock(voice->rack->ngs->mutex);
|
||||
const auto* params = static_cast<const Command*>(commands);
|
||||
for (size_t i = 0; i < num_commands; ++i) {
|
||||
const auto& p = params[i];
|
||||
const auto index = p.id >> 24;
|
||||
switch (p.id & 0xffffffu) {
|
||||
case 2:
|
||||
EXIT_NOT_IMPLEMENTED(p.type != 4);
|
||||
voice->SetEvent(p.value.u);
|
||||
break;
|
||||
case 5:
|
||||
EXIT_NOT_IMPLEMENTED(p.type != 0x11);
|
||||
voice->SetMatrix(index, p.value.levels, p.count);
|
||||
break;
|
||||
case 6:
|
||||
EXIT_NOT_IMPLEMENTED(p.type != 1);
|
||||
voice->SetVolume(index, p.value.f);
|
||||
break;
|
||||
case 7:
|
||||
EXIT_NOT_IMPLEMENTED(p.type != 3);
|
||||
voice->SetPortMatrix(index, p.value.i);
|
||||
break;
|
||||
default: EXIT("unknown command: 0x%08" PRIx32 "\n", p.id);
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -3120,7 +3531,7 @@ int KYTY_SYSV_ABI Ngs2VoiceGetState(uintptr_t voice_handle, Ngs2VoiceState* stat
|
||||
sampler->waveform_data = nullptr;
|
||||
break;
|
||||
}
|
||||
default: EXIT("unknown type: %s\n", Common::EnumName(voice->rack->type).c_str());
|
||||
default: EXIT("unknown type: %s\n", magic_enum::enum_name(voice->rack->type));
|
||||
}
|
||||
|
||||
return OK;
|
||||
|
||||
+1
-1
@@ -336,7 +336,7 @@ int KYTY_SYSV_ABI Ngs2RackGetVoiceHandle(uintptr_t rack_handle, uint32_t voice_i
|
||||
uintptr_t* handle);
|
||||
int KYTY_SYSV_ABI Ngs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamHeader* param_list);
|
||||
int KYTY_SYSV_ABI Ngs2VoiceRunCommands(uintptr_t voice_handle, const void* commands,
|
||||
uint32_t num_commands, uint32_t flags);
|
||||
size_t num_commands);
|
||||
int KYTY_SYSV_ABI Ngs2VoiceGetState(uintptr_t voice_handle, Ngs2VoiceState* state,
|
||||
size_t state_size);
|
||||
int KYTY_SYSV_ABI Ngs2VoiceGetStateFlags(uintptr_t voice_handle, uint32_t* state_flags);
|
||||
|
||||
+82
-56
@@ -1,6 +1,5 @@
|
||||
#include "common/common.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/magicEnum.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "kernel/fileSystem.h"
|
||||
#include "kernel/pthread.h"
|
||||
@@ -16,6 +15,7 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <magic_enum.hpp>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
@@ -117,6 +117,13 @@ struct AvPlayerEventReplacement {
|
||||
void* object_pointer = nullptr;
|
||||
AvPlayerEventCallback event_callback = nullptr;
|
||||
};
|
||||
static void emit_event(AvPlayerEventReplacement event, int32_t id, void* data = nullptr) {
|
||||
if (event.event_callback != nullptr) {
|
||||
LOGF("\t event_id = %d\n", id);
|
||||
event.event_callback(event.object_pointer, id, 0, data);
|
||||
}
|
||||
}
|
||||
|
||||
struct AvPlayerUri {
|
||||
const char* name = nullptr;
|
||||
uint32_t length = 0;
|
||||
@@ -639,9 +646,9 @@ private:
|
||||
|
||||
class Source {
|
||||
public:
|
||||
Source(AvPlayerMemAllocator m, AvPlayerFileReplacement f, int32_t video_buffers, bool vdec2,
|
||||
uint32_t demux_buffer_size)
|
||||
: mem(m), file(f),
|
||||
Source(AvPlayerMemAllocator m, AvPlayerFileReplacement f, AvPlayerEventReplacement e,
|
||||
int32_t video_buffers, bool vdec2, uint32_t demux_buffer_size)
|
||||
: mem(m), file(f), event(e),
|
||||
max_video_buffers(std::clamp(video_buffers <= 0 ? 2 : video_buffers, 2, 16)),
|
||||
use_vdec2(vdec2),
|
||||
video_packets(
|
||||
@@ -817,21 +824,26 @@ public:
|
||||
return result;
|
||||
}
|
||||
int Stop() {
|
||||
std::scoped_lock lifecycle_lock(lifecycle_mutex);
|
||||
bool was_playing_video = false;
|
||||
bool was_playing_video = false;
|
||||
AvPlayerEventReplacement stop_event;
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
was_playing_video = !stopped && video_id.has_value();
|
||||
stopped = true;
|
||||
}
|
||||
StopWorkers();
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
ResetNoLock();
|
||||
std::scoped_lock lifecycle_lock(lifecycle_mutex);
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
was_playing_video = !stopped && video_id.has_value();
|
||||
stopped = true;
|
||||
}
|
||||
StopWorkers();
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
ResetNoLock();
|
||||
}
|
||||
stop_event = TakeStopEvent();
|
||||
}
|
||||
if (was_playing_video) {
|
||||
::printf("AvPlayer video stopped\n");
|
||||
}
|
||||
emit_event(stop_event, AVPLAYER_EVENT_STATE_STOP);
|
||||
return 0;
|
||||
}
|
||||
void Pause() {
|
||||
@@ -860,9 +872,7 @@ public:
|
||||
}
|
||||
bool Active() const {
|
||||
std::lock_guard lock(mutex);
|
||||
return !stopped && !pipeline_failed &&
|
||||
(!demux_eof || !video_done || !audio_done || !video_frames.Empty() ||
|
||||
!audio_frames.Empty());
|
||||
return !stopped && !pipeline_failed && !DrainedNoLock();
|
||||
}
|
||||
uint64_t CurrentTime() const {
|
||||
std::lock_guard lock(mutex);
|
||||
@@ -945,6 +955,7 @@ public:
|
||||
current_video = std::move(*frame);
|
||||
*out = current_video->info;
|
||||
RecordLoopBoundary(*current_video);
|
||||
completion_changed.notify_all();
|
||||
if (deliver_seek_frame) {
|
||||
seek_video_frame_pending = false;
|
||||
}
|
||||
@@ -976,6 +987,7 @@ public:
|
||||
current_audio->info.details.audio.language_code, 4);
|
||||
last_audio_ts = out->time_stamp;
|
||||
RecordLoopBoundary(*current_audio);
|
||||
completion_changed.notify_all();
|
||||
return true;
|
||||
}
|
||||
std::optional<int32_t> TakeWarning() {
|
||||
@@ -988,6 +1000,13 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool DrainedNoLock() const {
|
||||
return demux_eof && video_done && audio_done && video_frames.Empty() &&
|
||||
audio_frames.Empty();
|
||||
}
|
||||
AvPlayerEventReplacement TakeStopEvent() {
|
||||
return stop_event_sent.exchange(true) ? AvPlayerEventReplacement {} : event;
|
||||
}
|
||||
void RecordLoopBoundary(const ReadyFrame& frame) {
|
||||
if (frame.timestamp_offset > last_output_loop_offset) {
|
||||
last_output_loop_offset = frame.timestamp_offset;
|
||||
@@ -1002,23 +1021,17 @@ private:
|
||||
streamer.reset();
|
||||
}
|
||||
void ResetNoLock(bool retain_output_buffers = false) {
|
||||
std::optional<ReadyFrame> delivered_video;
|
||||
std::optional<ReadyFrame> delivered_audio;
|
||||
std::deque<std::unique_ptr<GuestBuffer>> in_flight_video;
|
||||
if (retain_output_buffers) {
|
||||
delivered_video = std::move(current_video);
|
||||
delivered_audio = std::move(current_audio);
|
||||
in_flight_video = std::move(retired_video);
|
||||
}
|
||||
video_packets.Clear();
|
||||
audio_packets.Clear();
|
||||
video_frames.Clear();
|
||||
audio_frames.Clear();
|
||||
video_buffers.Clear();
|
||||
audio_buffers.Clear();
|
||||
retired_video.clear();
|
||||
current_video.reset();
|
||||
current_audio.reset();
|
||||
if (!retain_output_buffers) {
|
||||
retired_video.clear();
|
||||
current_video.reset();
|
||||
current_audio.reset();
|
||||
}
|
||||
if (sws != nullptr) {
|
||||
sws_freeContext(sws);
|
||||
sws = nullptr;
|
||||
@@ -1042,9 +1055,6 @@ private:
|
||||
last_audio_ts = 0;
|
||||
last_output_loop_offset = 0;
|
||||
pending_loop_warnings = 0;
|
||||
current_video = std::move(delivered_video);
|
||||
current_audio = std::move(delivered_audio);
|
||||
retired_video = std::move(in_flight_video);
|
||||
}
|
||||
void AutoEnable() {
|
||||
for (uint32_t i = 0; i < fmt->nb_streams; i++) {
|
||||
@@ -1174,6 +1184,7 @@ private:
|
||||
return true;
|
||||
}
|
||||
bool StartWorkers() {
|
||||
std::unique_lock lock(mutex);
|
||||
worker_stop = false;
|
||||
interrupt_io = false;
|
||||
pipeline_failed = false;
|
||||
@@ -1183,20 +1194,26 @@ private:
|
||||
if ((video_id && !CreateWorker(video_thread, VideoDecoderEntry, "AvPlayerVideoDecoder")) ||
|
||||
(audio_id && !CreateWorker(audio_thread, AudioDecoderEntry, "AvPlayerAudioDecoder")) ||
|
||||
!CreateWorker(demux_thread, DemuxEntry, "AvPlayerDemuxer")) {
|
||||
lock.unlock();
|
||||
StopWorkers();
|
||||
return false;
|
||||
}
|
||||
stop_event_sent = false;
|
||||
return true;
|
||||
}
|
||||
void NotifyWorkers() {
|
||||
completion_changed.notify_all();
|
||||
video_packets.Notify();
|
||||
audio_packets.Notify();
|
||||
video_buffers.Notify();
|
||||
audio_buffers.Notify();
|
||||
}
|
||||
void StopWorkers() {
|
||||
worker_stop = true;
|
||||
interrupt_io = true;
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
worker_stop = true;
|
||||
interrupt_io = true;
|
||||
}
|
||||
NotifyWorkers();
|
||||
if (demux_thread != nullptr) {
|
||||
LibKernel::PthreadJoin(demux_thread, nullptr);
|
||||
@@ -1212,9 +1229,12 @@ private:
|
||||
}
|
||||
}
|
||||
void FailPipeline() {
|
||||
pipeline_failed = true;
|
||||
worker_stop = true;
|
||||
interrupt_io = true;
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
pipeline_failed = true;
|
||||
worker_stop = true;
|
||||
interrupt_io = true;
|
||||
}
|
||||
NotifyWorkers();
|
||||
}
|
||||
void Demux() {
|
||||
@@ -1269,6 +1289,13 @@ private:
|
||||
demux_eof = true;
|
||||
video_packets.Notify();
|
||||
audio_packets.Notify();
|
||||
std::unique_lock lock(mutex);
|
||||
completion_changed.wait(lock, [&] { return worker_stop || DrainedNoLock(); });
|
||||
if (!worker_stop) {
|
||||
const auto stop_event = TakeStopEvent();
|
||||
lock.unlock();
|
||||
emit_event(stop_event, AVPLAYER_EVENT_STATE_STOP);
|
||||
}
|
||||
}
|
||||
void VideoDecoder() {
|
||||
Decoder(video_packets, video_ctx, video_buffers, video_frames, video_done,
|
||||
@@ -1316,7 +1343,11 @@ private:
|
||||
break;
|
||||
}
|
||||
}
|
||||
done = true;
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
done = true;
|
||||
}
|
||||
completion_changed.notify_all();
|
||||
}
|
||||
bool DecodePacket(AVCodecContext* codec, AVPacket* packet, uint64_t timestamp_offset,
|
||||
WorkQueue<std::unique_ptr<GuestBuffer>>& buffers,
|
||||
@@ -1607,6 +1638,7 @@ private:
|
||||
}
|
||||
AvPlayerMemAllocator mem;
|
||||
AvPlayerFileReplacement file;
|
||||
AvPlayerEventReplacement event;
|
||||
int max_video_buffers = 2;
|
||||
bool use_vdec2 = false;
|
||||
AvPlayerSourceType source_type = AvPlayerSourceUnknown;
|
||||
@@ -1640,6 +1672,8 @@ private:
|
||||
std::atomic_bool demux_eof {true};
|
||||
std::atomic_bool video_done {true};
|
||||
std::atomic_bool audio_done {true};
|
||||
std::atomic_bool stop_event_sent {true};
|
||||
std::condition_variable completion_changed;
|
||||
std::mutex lifecycle_mutex;
|
||||
mutable std::mutex mutex;
|
||||
bool stopped = true;
|
||||
@@ -1673,19 +1707,13 @@ struct AvPlayerInternal {
|
||||
static bool valid_allocators(const AvPlayerMemAllocator& m) {
|
||||
return m.allocate && m.deallocate && m.allocate_texture && m.deallocate_texture;
|
||||
}
|
||||
static void emit_event(AvPlayerInternal* h, int32_t id, void* data = nullptr) {
|
||||
if (h != nullptr && h->event.event_callback != nullptr) {
|
||||
LOGF("\t event_id = %d\n", id);
|
||||
h->event.event_callback(h->event.object_pointer, id, 0, data);
|
||||
}
|
||||
}
|
||||
static void pump_warnings(AvPlayerInternal* h) {
|
||||
if (h == nullptr || h->source == nullptr) {
|
||||
return;
|
||||
}
|
||||
while (auto w = h->source->TakeWarning()) {
|
||||
int32_t warning = *w;
|
||||
emit_event(h, AVPLAYER_EVENT_WARNING_ID, &warning);
|
||||
emit_event(h->event, AVPLAYER_EVENT_WARNING_ID, &warning);
|
||||
}
|
||||
}
|
||||
static AvPlayerInternal* create_player(const AvPlayerMemAllocator& mem,
|
||||
@@ -1713,17 +1741,17 @@ static int add_source(AvPlayerInternal* h, const std::string& filename, AvPlayer
|
||||
}
|
||||
bool vdec2 =
|
||||
h->post_init.video_decoder_init.decoder_type.video_type == AvPlayerVideoDecoderSoftware2;
|
||||
auto s = std::make_unique<Source>(h->mem, h->file, h->video_buffers, vdec2,
|
||||
auto s = std::make_unique<Source>(h->mem, h->file, h->event, h->video_buffers, vdec2,
|
||||
h->post_init.demux_video_buffer_size);
|
||||
if (auto rc = s->Init(filename, type); rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
h->source = std::move(s);
|
||||
emit_event(h, AVPLAYER_EVENT_STATE_READY);
|
||||
emit_event(h->event, AVPLAYER_EVENT_STATE_READY);
|
||||
if (h->auto_start) {
|
||||
auto rc = h->source->Start();
|
||||
if (rc == 0) {
|
||||
emit_event(h, AVPLAYER_EVENT_STATE_PLAY);
|
||||
emit_event(h->event, AVPLAYER_EVENT_STATE_PLAY);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1758,7 +1786,7 @@ static void log_init_common(const AvPlayerMemAllocator& mem, const AvPlayerFileR
|
||||
"= %d\n\t base_priority "
|
||||
" = %u\n\t auto_start = %u\n\t default_language "
|
||||
" = %s\n",
|
||||
Common::EnumName(debug_level).c_str(), buffers, priority, auto_start,
|
||||
magic_enum::enum_name(debug_level), buffers, priority, auto_start,
|
||||
language == nullptr ? "(null)" : language);
|
||||
}
|
||||
|
||||
@@ -1872,7 +1900,7 @@ int KYTY_SYSV_ABI AvPlayerStart(AvPlayerInternal* h) {
|
||||
}
|
||||
auto rc = h->source->Start();
|
||||
if (rc == 0) {
|
||||
emit_event(h, AVPLAYER_EVENT_STATE_PLAY);
|
||||
emit_event(h->event, AVPLAYER_EVENT_STATE_PLAY);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1887,7 +1915,7 @@ int KYTY_SYSV_ABI AvPlayerStartEx(AvPlayerInternal* h, const void* start_info_ex
|
||||
: static_cast<const AvPlayerStartInfoEx*>(start_info_ex)->start_time_milliseconds;
|
||||
auto rc = h->source->Start(ms);
|
||||
if (rc == 0) {
|
||||
emit_event(h, AVPLAYER_EVENT_STATE_PLAY);
|
||||
emit_event(h->event, AVPLAYER_EVENT_STATE_PLAY);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1896,9 +1924,7 @@ int KYTY_SYSV_ABI AvPlayerStop(AvPlayerInternal* h) {
|
||||
if (h == nullptr || h->source == nullptr) {
|
||||
return AVPLAYER_ERROR_INVALID_PARAMS;
|
||||
}
|
||||
auto rc = h->source->Stop();
|
||||
emit_event(h, AVPLAYER_EVENT_STATE_STOP);
|
||||
return rc;
|
||||
return h->source->Stop();
|
||||
}
|
||||
int KYTY_SYSV_ABI AvPlayerPause(AvPlayerInternal* h) {
|
||||
PRINT_NAME();
|
||||
@@ -1906,7 +1932,7 @@ int KYTY_SYSV_ABI AvPlayerPause(AvPlayerInternal* h) {
|
||||
return AVPLAYER_ERROR_INVALID_PARAMS;
|
||||
}
|
||||
h->source->Pause();
|
||||
emit_event(h, AVPLAYER_EVENT_STATE_PAUSE);
|
||||
emit_event(h->event, AVPLAYER_EVENT_STATE_PAUSE);
|
||||
return 0;
|
||||
}
|
||||
int KYTY_SYSV_ABI AvPlayerResume(AvPlayerInternal* h) {
|
||||
@@ -1915,7 +1941,7 @@ int KYTY_SYSV_ABI AvPlayerResume(AvPlayerInternal* h) {
|
||||
return AVPLAYER_ERROR_INVALID_PARAMS;
|
||||
}
|
||||
h->source->Resume();
|
||||
emit_event(h, AVPLAYER_EVENT_STATE_PLAY);
|
||||
emit_event(h->event, AVPLAYER_EVENT_STATE_PLAY);
|
||||
return 0;
|
||||
}
|
||||
int KYTY_SYSV_ABI AvPlayerSetLooping(AvPlayerInternal* h, Bool loop) {
|
||||
@@ -2017,7 +2043,7 @@ int KYTY_SYSV_ABI AvPlayerJumpToTime(AvPlayerInternal* h, uint64_t time_ms) {
|
||||
auto rc = h->source->Jump(time_ms);
|
||||
if (rc == 0) {
|
||||
int32_t w = AVPLAYER_WARNING_JUMP_COMPLETE;
|
||||
emit_event(h, AVPLAYER_EVENT_WARNING_ID, &w);
|
||||
emit_event(h->event, AVPLAYER_EVENT_WARNING_ID, &w);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
+126
-2
@@ -13,6 +13,8 @@
|
||||
#include "libs/padData.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
@@ -87,6 +89,9 @@ struct ControllerState {
|
||||
uint32_t buttons = 0;
|
||||
int axes[static_cast<int>(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0};
|
||||
Touch touch[2];
|
||||
std::array<float, 3> accel {0.0f, 1.0f, 0.0f};
|
||||
std::array<float, 3> gyro {};
|
||||
std::array<float, 4> orientation {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
};
|
||||
|
||||
class GameController {
|
||||
@@ -101,6 +106,9 @@ public:
|
||||
void Axis(int id, Axis axis, int value);
|
||||
void RightStick(int id, int x, int y);
|
||||
void TouchPad(int id, int finger, bool down, float x, float y);
|
||||
void Motion(int id, Sensor sensor, const float* data, uint64_t time_us);
|
||||
void SetMotionSensorState(bool enable);
|
||||
void ResetOrientation();
|
||||
void ResetInputState();
|
||||
void ReleaseHostPads();
|
||||
void GetConnectionInfo(bool* flag, int* count);
|
||||
@@ -121,6 +129,8 @@ private:
|
||||
int m_active_id = -1;
|
||||
bool m_connected = false;
|
||||
int m_connected_count = 0;
|
||||
bool m_motion_enabled = true;
|
||||
uint64_t m_gyro_time = 0;
|
||||
ControllerState m_state;
|
||||
ControllerState m_states[STATES_MAX];
|
||||
bool m_obtained[STATES_MAX] {};
|
||||
@@ -144,7 +154,16 @@ static void pad_fill_data(PadData* data, const ControllerState& state, bool conn
|
||||
data->right_stick_y = state.axes[static_cast<int>(Axis::RightY)];
|
||||
data->analog_buttons_l2 = state.axes[static_cast<int>(Axis::TriggerLeft)];
|
||||
data->analog_buttons_r2 = state.axes[static_cast<int>(Axis::TriggerRight)];
|
||||
data->orientation_w = 1.0f;
|
||||
data->acceleration_x = state.accel[0];
|
||||
data->acceleration_y = state.accel[1];
|
||||
data->acceleration_z = state.accel[2];
|
||||
data->angular_velocity_x = state.gyro[0];
|
||||
data->angular_velocity_y = state.gyro[1];
|
||||
data->angular_velocity_z = state.gyro[2];
|
||||
data->orientation_x = state.orientation[0];
|
||||
data->orientation_y = state.orientation[1];
|
||||
data->orientation_z = state.orientation[2];
|
||||
data->orientation_w = state.orientation[3];
|
||||
for (const auto& touch: state.touch) {
|
||||
if (touch.down) {
|
||||
auto& output = data->touch_data.touch[data->touch_data.touch_num++];
|
||||
@@ -273,6 +292,18 @@ void GameController::Connect(int id) {
|
||||
|
||||
m_connected_ids.push_back(id);
|
||||
|
||||
if (id != HOST_INPUT_CONTROLLER_ID) {
|
||||
if (auto* pad = SDL_GameControllerFromInstanceID(static_cast<SDL_JoystickID>(id));
|
||||
pad != nullptr) {
|
||||
for (auto sensor: {SDL_SENSOR_ACCEL, SDL_SENSOR_GYRO}) {
|
||||
if (SDL_GameControllerHasSensor(pad, sensor) &&
|
||||
SDL_GameControllerSetSensorEnabled(pad, sensor, SDL_TRUE) != 0) {
|
||||
LOGF("\t enabling controller sensor failed: %s\n", SDL_GetError());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CheckActive();
|
||||
}
|
||||
|
||||
@@ -311,6 +342,7 @@ void GameController::CheckActive() {
|
||||
m_active_id = new_active_id;
|
||||
m_connected = new_connected;
|
||||
m_state = {};
|
||||
m_gyro_time = 0;
|
||||
m_states_num = 0;
|
||||
m_first_state = 0;
|
||||
m_next_touch_id = 1;
|
||||
@@ -402,9 +434,81 @@ void GameController::TouchPad(int id, int finger, bool down, float x, float y) {
|
||||
}
|
||||
}
|
||||
|
||||
void GameController::Motion(int id, Sensor sensor, const float* data, uint64_t time_us) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
if (id != m_active_id || !m_motion_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert acceleration to G; angular velocity is already in rad/s.
|
||||
if (sensor == Sensor::Accel) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
m_state.accel[i] = data[i] / SDL_STANDARD_GRAVITY;
|
||||
}
|
||||
} else {
|
||||
std::copy_n(data, 3, m_state.gyro.begin());
|
||||
// Do not extrapolate a single sample across lost reports (e.g. loss of window focus).
|
||||
constexpr uint64_t max_gyro_interval_us = 100000;
|
||||
if (m_gyro_time != 0 && time_us > m_gyro_time &&
|
||||
time_us - m_gyro_time <= max_gyro_interval_us) {
|
||||
const float dt = static_cast<float>(time_us - m_gyro_time) * 0.000001f;
|
||||
const float speed =
|
||||
std::sqrt(data[0] * data[0] + data[1] * data[1] + data[2] * data[2]);
|
||||
const float half_angle = speed * dt * 0.5f;
|
||||
const float scale = speed > 0.0f ? std::sin(half_angle) / speed : 0.0f;
|
||||
const float x = data[0] * scale;
|
||||
const float y = data[1] * scale;
|
||||
const float z = data[2] * scale;
|
||||
const float w = std::cos(half_angle);
|
||||
const auto q = m_state.orientation;
|
||||
// Accumulate body-local rotation relative to connection / orientation reset.
|
||||
m_state.orientation = {q[3] * x + q[0] * w + q[1] * z - q[2] * y,
|
||||
q[3] * y - q[0] * z + q[1] * w + q[2] * x,
|
||||
q[3] * z + q[0] * y - q[1] * x + q[2] * w,
|
||||
q[3] * w - q[0] * x - q[1] * y - q[2] * z};
|
||||
float length = 0.0f;
|
||||
for (float value: m_state.orientation) {
|
||||
length += value * value;
|
||||
}
|
||||
length = std::sqrt(length);
|
||||
for (float& value: m_state.orientation) {
|
||||
value /= length;
|
||||
}
|
||||
}
|
||||
m_gyro_time = time_us;
|
||||
}
|
||||
m_state.time = LibKernel::KernelGetProcessTime();
|
||||
AddState();
|
||||
}
|
||||
|
||||
void GameController::SetMotionSensorState(bool enable) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
if (m_motion_enabled != enable) {
|
||||
m_motion_enabled = enable;
|
||||
m_gyro_time = 0;
|
||||
m_state.accel = {0.0f, 1.0f, 0.0f};
|
||||
m_state.gyro = {};
|
||||
m_state.time = LibKernel::KernelGetProcessTime();
|
||||
AddState();
|
||||
}
|
||||
}
|
||||
|
||||
void GameController::ResetOrientation() {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
m_state.orientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
m_gyro_time = 0;
|
||||
m_state.time = LibKernel::KernelGetProcessTime();
|
||||
AddState();
|
||||
}
|
||||
|
||||
void GameController::ResetInputState() {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
m_state = {};
|
||||
m_state.buttons = 0;
|
||||
std::fill_n(m_state.axes, 4, 128);
|
||||
std::fill_n(m_state.axes + 4, 2, 0);
|
||||
for (auto& touch: m_state.touch) {
|
||||
touch = {};
|
||||
}
|
||||
m_state.time = LibKernel::KernelGetProcessTime();
|
||||
m_states_num = 0;
|
||||
m_first_state = 0;
|
||||
@@ -580,6 +684,10 @@ void SetTouchPad(int id, int finger, bool down, float x, float y) {
|
||||
g_controller->TouchPad(id, finger, down, x, y);
|
||||
}
|
||||
|
||||
void SetSensor(int id, Sensor sensor, const float* data, uint64_t time_us) {
|
||||
g_controller->Motion(id, sensor, data, time_us);
|
||||
}
|
||||
|
||||
void ResetInputState() {
|
||||
g_controller->ResetInputState();
|
||||
}
|
||||
@@ -646,6 +754,7 @@ int KYTY_SYSV_ABI PadSetMotionSensorState(int handle, bool enable) {
|
||||
}
|
||||
|
||||
LOGF("\t enable = %s\n", (enable ? "true" : "false"));
|
||||
g_controller->SetMotionSensorState(enable);
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -669,6 +778,7 @@ int KYTY_SYSV_ABI PadResetOrientation(int handle) {
|
||||
return PAD_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
g_controller->ResetOrientation();
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -702,6 +812,20 @@ int KYTY_SYSV_ABI PadGetControllerInformation(int handle, PadControllerInformati
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PadIsRemoteController(int handle, bool* is_remote) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (handle != 1) {
|
||||
return PAD_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
if (is_remote == nullptr) {
|
||||
return PAD_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
*is_remote = false;
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PadReadState(int handle, PadData* data) {
|
||||
PRINT_NAME();
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@ enum class Axis {
|
||||
AxisMax
|
||||
};
|
||||
|
||||
enum class Sensor { Accel, Gyro };
|
||||
|
||||
struct PadControllerInformation;
|
||||
struct PadData;
|
||||
struct PadVibrationParam;
|
||||
@@ -69,6 +71,7 @@ void SetButton(int id, uint32_t button, bool down);
|
||||
void SetAxis(int id, Axis axis, int value);
|
||||
void SetRightStick(int id, int x, int y);
|
||||
void SetTouchPad(int id, int finger, bool down, float x, float y);
|
||||
void SetSensor(int id, Sensor sensor, const float* data, uint64_t time_us);
|
||||
void ResetInputState();
|
||||
|
||||
int KYTY_SYSV_ABI PadInit();
|
||||
@@ -78,6 +81,7 @@ int KYTY_SYSV_ABI PadSetMotionSensorState(int handle, bool enable);
|
||||
int KYTY_SYSV_ABI PadSetAngularVelocityDeadbandState(int handle, bool enable);
|
||||
int KYTY_SYSV_ABI PadResetOrientation(int handle);
|
||||
int KYTY_SYSV_ABI PadGetControllerInformation(int handle, PadControllerInformation* info);
|
||||
int KYTY_SYSV_ABI PadIsRemoteController(int handle, bool* is_remote);
|
||||
int KYTY_SYSV_ABI PadReadState(int handle, PadData* data);
|
||||
int KYTY_SYSV_ABI PadRead(int handle, PadData* data, int num);
|
||||
int KYTY_SYSV_ABI PadSetVibration(int handle, const PadVibrationParam* param);
|
||||
|
||||
@@ -527,7 +527,12 @@ static int kyty_printf_internal(bool sn, char* sn_s, size_t sn_n, const char* fo
|
||||
} else if (*format == '*') {
|
||||
// const int prec = (int)va_arg(va, int);
|
||||
const int prec = VaArg_int(va_list);
|
||||
precision = prec > 0 ? static_cast<unsigned int>(prec) : 0U;
|
||||
if (prec < 0) {
|
||||
// A negative dynamic precision is treated as omitted.
|
||||
flags &= ~FLAGS_PRECISION;
|
||||
} else {
|
||||
precision = static_cast<unsigned int>(prec);
|
||||
}
|
||||
format++;
|
||||
}
|
||||
}
|
||||
|
||||
+26
-29
@@ -7,6 +7,7 @@
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
@@ -18,35 +19,31 @@
|
||||
namespace Libs::Ime {
|
||||
namespace {
|
||||
|
||||
constexpr int Error(uint32_t value) {
|
||||
return static_cast<int32_t>(value);
|
||||
}
|
||||
|
||||
constexpr int ERROR_BUSY = Error(0x80bc0001);
|
||||
constexpr int ERROR_NOT_OPENED = Error(0x80bc0002);
|
||||
constexpr int ERROR_CONNECTION_FAILED = Error(0x80bc0004);
|
||||
constexpr int ERROR_EVENT_OVERFLOW = Error(0x80bc0007);
|
||||
constexpr int ERROR_INVALID_TEXT = Error(0x80bc0006);
|
||||
constexpr int ERROR_INVALID_USER_ID = Error(0x80bc0010);
|
||||
constexpr int ERROR_INVALID_TYPE = Error(0x80bc0011);
|
||||
constexpr int ERROR_INVALID_LANGUAGES = Error(0x80bc0012);
|
||||
constexpr int ERROR_INVALID_ENTER_LABEL = Error(0x80bc0013);
|
||||
constexpr int ERROR_INVALID_INPUT_METHOD = Error(0x80bc0014);
|
||||
constexpr int ERROR_INVALID_OPTION = Error(0x80bc0015);
|
||||
constexpr int ERROR_INVALID_MAX_TEXT_LENGTH = Error(0x80bc0016);
|
||||
constexpr int ERROR_INVALID_TEXT_BUFFER = Error(0x80bc0017);
|
||||
constexpr int ERROR_INVALID_POSX = Error(0x80bc0018);
|
||||
constexpr int ERROR_INVALID_POSY = Error(0x80bc0019);
|
||||
constexpr int ERROR_INVALID_HALIGN = Error(0x80bc001a);
|
||||
constexpr int ERROR_INVALID_VALIGN = Error(0x80bc001b);
|
||||
constexpr int ERROR_INVALID_EXTENDED = Error(0x80bc001c);
|
||||
constexpr int ERROR_INVALID_WORK = Error(0x80bc0020);
|
||||
constexpr int ERROR_INVALID_HANDLER = Error(0x80bc0022);
|
||||
constexpr int ERROR_NO_RESOURCE_ID = Error(0x80bc0023);
|
||||
constexpr int ERROR_INVALID_MODE = Error(0x80bc0024);
|
||||
constexpr int ERROR_INVALID_PARAM = Error(0x80bc0030);
|
||||
constexpr int ERROR_INVALID_ADDRESS = Error(0x80bc0031);
|
||||
constexpr int ERROR_INVALID_RESERVED = Error(0x80bc0032);
|
||||
constexpr int ERROR_BUSY = static_cast<int32_t>(0x80bc0001);
|
||||
constexpr int ERROR_NOT_OPENED = static_cast<int32_t>(0x80bc0002);
|
||||
constexpr int ERROR_CONNECTION_FAILED = static_cast<int32_t>(0x80bc0004);
|
||||
constexpr int ERROR_EVENT_OVERFLOW = static_cast<int32_t>(0x80bc0007);
|
||||
constexpr int ERROR_INVALID_TEXT = static_cast<int32_t>(0x80bc0006);
|
||||
constexpr int ERROR_INVALID_USER_ID = static_cast<int32_t>(0x80bc0010);
|
||||
constexpr int ERROR_INVALID_TYPE = static_cast<int32_t>(0x80bc0011);
|
||||
constexpr int ERROR_INVALID_LANGUAGES = static_cast<int32_t>(0x80bc0012);
|
||||
constexpr int ERROR_INVALID_ENTER_LABEL = static_cast<int32_t>(0x80bc0013);
|
||||
constexpr int ERROR_INVALID_INPUT_METHOD = static_cast<int32_t>(0x80bc0014);
|
||||
constexpr int ERROR_INVALID_OPTION = static_cast<int32_t>(0x80bc0015);
|
||||
constexpr int ERROR_INVALID_MAX_TEXT_LENGTH = static_cast<int32_t>(0x80bc0016);
|
||||
constexpr int ERROR_INVALID_TEXT_BUFFER = static_cast<int32_t>(0x80bc0017);
|
||||
constexpr int ERROR_INVALID_POSX = static_cast<int32_t>(0x80bc0018);
|
||||
constexpr int ERROR_INVALID_POSY = static_cast<int32_t>(0x80bc0019);
|
||||
constexpr int ERROR_INVALID_HALIGN = static_cast<int32_t>(0x80bc001a);
|
||||
constexpr int ERROR_INVALID_VALIGN = static_cast<int32_t>(0x80bc001b);
|
||||
constexpr int ERROR_INVALID_EXTENDED = static_cast<int32_t>(0x80bc001c);
|
||||
constexpr int ERROR_INVALID_WORK = static_cast<int32_t>(0x80bc0020);
|
||||
constexpr int ERROR_INVALID_HANDLER = static_cast<int32_t>(0x80bc0022);
|
||||
constexpr int ERROR_NO_RESOURCE_ID = static_cast<int32_t>(0x80bc0023);
|
||||
constexpr int ERROR_INVALID_MODE = static_cast<int32_t>(0x80bc0024);
|
||||
constexpr int ERROR_INVALID_PARAM = static_cast<int32_t>(0x80bc0030);
|
||||
constexpr int ERROR_INVALID_ADDRESS = static_cast<int32_t>(0x80bc0031);
|
||||
constexpr int ERROR_INVALID_RESERVED = static_cast<int32_t>(0x80bc0032);
|
||||
|
||||
constexpr uint32_t VALID_OPTIONS = 0x00007bff;
|
||||
constexpr uint32_t VALID_EXTENDED_OPTIONS = 0x00004fde;
|
||||
|
||||
+22
-25
@@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
@@ -13,31 +14,27 @@ namespace Libs::Dialog::ImeDialog {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int Error(uint32_t value) {
|
||||
return static_cast<int32_t>(value);
|
||||
}
|
||||
|
||||
constexpr int ERROR_BUSY = Error(0x80bc0001);
|
||||
constexpr int ERROR_INVALID_USER_ID = Error(0x80bc0010);
|
||||
constexpr int ERROR_INVALID_TYPE = Error(0x80bc0011);
|
||||
constexpr int ERROR_INVALID_LANGUAGES = Error(0x80bc0012);
|
||||
constexpr int ERROR_INVALID_ENTER_LABEL = Error(0x80bc0013);
|
||||
constexpr int ERROR_INVALID_INPUT_METHOD = Error(0x80bc0014);
|
||||
constexpr int ERROR_INVALID_OPTION = Error(0x80bc0015);
|
||||
constexpr int ERROR_INVALID_MAX_TEXT_LENGTH = Error(0x80bc0016);
|
||||
constexpr int ERROR_INVALID_TEXT_BUFFER = Error(0x80bc0017);
|
||||
constexpr int ERROR_INVALID_POSX = Error(0x80bc0018);
|
||||
constexpr int ERROR_INVALID_POSY = Error(0x80bc0019);
|
||||
constexpr int ERROR_INVALID_HALIGN = Error(0x80bc001a);
|
||||
constexpr int ERROR_INVALID_VALIGN = Error(0x80bc001b);
|
||||
constexpr int ERROR_INVALID_EXTENDED = Error(0x80bc001c);
|
||||
constexpr int ERROR_INVALID_PARAM = Error(0x80bc0030);
|
||||
constexpr int ERROR_INVALID_ADDRESS = Error(0x80bc0031);
|
||||
constexpr int ERROR_INVALID_RESERVED = Error(0x80bc0032);
|
||||
constexpr int ERROR_INVALID_TITLE = Error(0x80bc0101);
|
||||
constexpr int ERROR_NOT_RUNNING = Error(0x80bc0105);
|
||||
constexpr int ERROR_NOT_FINISHED = Error(0x80bc0106);
|
||||
constexpr int ERROR_NOT_IN_USE = Error(0x80bc0107);
|
||||
constexpr int ERROR_BUSY = static_cast<int32_t>(0x80bc0001);
|
||||
constexpr int ERROR_INVALID_USER_ID = static_cast<int32_t>(0x80bc0010);
|
||||
constexpr int ERROR_INVALID_TYPE = static_cast<int32_t>(0x80bc0011);
|
||||
constexpr int ERROR_INVALID_LANGUAGES = static_cast<int32_t>(0x80bc0012);
|
||||
constexpr int ERROR_INVALID_ENTER_LABEL = static_cast<int32_t>(0x80bc0013);
|
||||
constexpr int ERROR_INVALID_INPUT_METHOD = static_cast<int32_t>(0x80bc0014);
|
||||
constexpr int ERROR_INVALID_OPTION = static_cast<int32_t>(0x80bc0015);
|
||||
constexpr int ERROR_INVALID_MAX_TEXT_LENGTH = static_cast<int32_t>(0x80bc0016);
|
||||
constexpr int ERROR_INVALID_TEXT_BUFFER = static_cast<int32_t>(0x80bc0017);
|
||||
constexpr int ERROR_INVALID_POSX = static_cast<int32_t>(0x80bc0018);
|
||||
constexpr int ERROR_INVALID_POSY = static_cast<int32_t>(0x80bc0019);
|
||||
constexpr int ERROR_INVALID_HALIGN = static_cast<int32_t>(0x80bc001a);
|
||||
constexpr int ERROR_INVALID_VALIGN = static_cast<int32_t>(0x80bc001b);
|
||||
constexpr int ERROR_INVALID_EXTENDED = static_cast<int32_t>(0x80bc001c);
|
||||
constexpr int ERROR_INVALID_PARAM = static_cast<int32_t>(0x80bc0030);
|
||||
constexpr int ERROR_INVALID_ADDRESS = static_cast<int32_t>(0x80bc0031);
|
||||
constexpr int ERROR_INVALID_RESERVED = static_cast<int32_t>(0x80bc0032);
|
||||
constexpr int ERROR_INVALID_TITLE = static_cast<int32_t>(0x80bc0101);
|
||||
constexpr int ERROR_NOT_RUNNING = static_cast<int32_t>(0x80bc0105);
|
||||
constexpr int ERROR_NOT_FINISHED = static_cast<int32_t>(0x80bc0106);
|
||||
constexpr int ERROR_NOT_IN_USE = static_cast<int32_t>(0x80bc0107);
|
||||
|
||||
constexpr uint32_t VALID_OPTIONS = 0x00007bff;
|
||||
constexpr uint32_t VALID_EXTENDED_OPTIONS = 0x00005fde;
|
||||
|
||||
@@ -51,17 +51,17 @@ static bool TryGetTitleIdFromContentId(std::string* title_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t dash = Common::FindIndex(content_id, '-');
|
||||
if (!Common::IndexValid(content_id, dash)) {
|
||||
const auto dash = content_id.find('-');
|
||||
if (dash == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t underscore = Common::FindIndex(content_id, '_', dash + 1);
|
||||
if (!Common::IndexValid(content_id, underscore) || underscore <= dash + 1) {
|
||||
const auto underscore = content_id.find('_', dash + 1);
|
||||
if (underscore == std::string::npos || underscore <= dash + 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*title_id = Common::Mid(content_id, dash + 1, underscore - dash - 1);
|
||||
*title_id = content_id.substr(dash + 1, underscore - dash - 1);
|
||||
return !title_id->empty();
|
||||
}
|
||||
|
||||
|
||||
@@ -1052,7 +1052,7 @@ static int32_t KYTY_SYSV_ABI AudiodecDecode(int32_t handle, AudiodecCtrl* ctrl)
|
||||
ctrl->pPcmItem->uiPcmSize = static_cast<uint32_t>(result.output_written);
|
||||
audiodec_fill_info(ctrl, decoder);
|
||||
auto* info = static_cast<AudiodecM4aacInfo*>(ctrl->pBsiInfo);
|
||||
info->iResult = (result.result & AJM_RESULT_CODEC_ERROR) != 0
|
||||
info->iResult = (result.result & (AJM_RESULT_CODEC_ERROR | AJM_RESULT_TOO_MANY_CHANNELS)) != 0
|
||||
? AUDIODEC_M4AAC_RESULT_DECODE_ERROR
|
||||
: (result.result & AJM_RESULT_PARTIAL_INPUT) != 0
|
||||
? AUDIODEC_M4AAC_RESULT_INSUFFICIENT_DATA : 0;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "loader/symbolDatabase.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "graphics/guest_gpu/tile.h"
|
||||
#include "libs/agc.h"
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
@@ -68,10 +69,6 @@ using KernelModule = int32_t;
|
||||
using get_thread_atexit_count_func_t = KYTY_SYSV_ABI int (*)(KernelModule);
|
||||
using thread_atexit_report_func_t = KYTY_SYSV_ABI void (*)(KernelModule);
|
||||
|
||||
static uint32_t sha1_rol(uint32_t value, uint32_t bits) {
|
||||
return (value << bits) | (value >> (32u - bits));
|
||||
}
|
||||
|
||||
static std::array<uint8_t, 20> sha1_digest(const uint8_t* data, size_t size) {
|
||||
uint32_t h0 = 0x67452301u;
|
||||
uint32_t h1 = 0xefcdab89u;
|
||||
@@ -99,7 +96,7 @@ static std::array<uint8_t, 20> sha1_digest(const uint8_t* data, size_t size) {
|
||||
(static_cast<uint32_t>(msg[j + 2]) << 8u) | static_cast<uint32_t>(msg[j + 3]);
|
||||
}
|
||||
for (int i = 16; i < 80; i++) {
|
||||
w[i] = sha1_rol(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
|
||||
w[i] = std::rotl(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
|
||||
}
|
||||
|
||||
uint32_t a = h0;
|
||||
@@ -125,10 +122,10 @@ static std::array<uint8_t, 20> sha1_digest(const uint8_t* data, size_t size) {
|
||||
k = 0xca62c1d6u;
|
||||
}
|
||||
|
||||
const auto temp = sha1_rol(a, 5) + f + e + k + w[i];
|
||||
const auto temp = std::rotl(a, 5) + f + e + k + w[i];
|
||||
e = d;
|
||||
d = c;
|
||||
c = sha1_rol(b, 30);
|
||||
c = std::rotl(b, 30);
|
||||
b = a;
|
||||
a = temp;
|
||||
}
|
||||
|
||||
+62
-43
@@ -8,22 +8,13 @@
|
||||
#include "loader/symbolDatabase.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define NET_CALL(func) \
|
||||
[&]() { \
|
||||
auto result = func; \
|
||||
if (result < 0) { \
|
||||
*GetNetErrorAddr() = result; \
|
||||
} \
|
||||
return result; \
|
||||
}()
|
||||
|
||||
namespace Libs {
|
||||
|
||||
namespace Network::Net {
|
||||
@@ -43,8 +34,15 @@ KYTY_SYSV_ABI int* GetNetErrorAddr() {
|
||||
return &g_net_errno;
|
||||
}
|
||||
|
||||
static int FinishNetCall(int result) {
|
||||
if (result < 0) {
|
||||
g_net_errno = result - Network::NET_ERROR_EPERM + 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI NetInit() {
|
||||
return NET_CALL(Net::NetInit());
|
||||
return FinishNetCall(Net::NetInit());
|
||||
}
|
||||
|
||||
static int PosixToNetError(int error) {
|
||||
@@ -66,13 +64,7 @@ static int PosixToNetError(int error) {
|
||||
}
|
||||
|
||||
static int FinishSocketCall(int result) {
|
||||
if (result >= 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const int net_error = PosixToNetError(*Posix::GetErrorAddr());
|
||||
*GetNetErrorAddr() = net_error;
|
||||
return net_error;
|
||||
return FinishNetCall(result < 0 ? PosixToNetError(*Posix::GetErrorAddr()) : result);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI NetSocket(const char* name, int family, int type, int protocol) {
|
||||
@@ -102,48 +94,48 @@ int KYTY_SYSV_ABI NetGetsockname(int s, void* addr, uint32_t* addrlen) {
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI NetPoolCreate(const char* name, int size, int flags) {
|
||||
return NET_CALL(Net::NetPoolCreate(name, size, flags));
|
||||
return FinishNetCall(Net::NetPoolCreate(name, size, flags));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI NetPoolDestroy(int memid) {
|
||||
return NET_CALL(Net::NetPoolDestroy(memid));
|
||||
return FinishNetCall(Net::NetPoolDestroy(memid));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI NetResolverCreate(const char* name, int memid, int flags) {
|
||||
return NET_CALL(Net::NetResolverCreate(name, memid, flags));
|
||||
return FinishNetCall(Net::NetResolverCreate(name, memid, flags));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI NetResolverDestroy(int rid) {
|
||||
return NET_CALL(Net::NetResolverDestroy(rid));
|
||||
return FinishNetCall(Net::NetResolverDestroy(rid));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI NetResolverStartNtoa(int rid, const char* hostname, void* addr, int timeout,
|
||||
int retry, int flags) {
|
||||
return NET_CALL(Net::NetResolverStartNtoa(rid, hostname, addr, timeout, retry, flags));
|
||||
return FinishNetCall(Net::NetResolverStartNtoa(rid, hostname, addr, timeout, retry, flags));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI NetInetPton(int af, const char* src, void* dst) {
|
||||
return NET_CALL(Net::NetInetPton(af, src, dst));
|
||||
return FinishNetCall(Net::NetInetPton(af, src, dst));
|
||||
}
|
||||
|
||||
const char* KYTY_SYSV_ABI NetInetNtop(int af, const void* src, char* dst, uint32_t size) {
|
||||
const char* result = Net::NetInetNtop(af, src, dst, size);
|
||||
if (result == nullptr) {
|
||||
*GetNetErrorAddr() = PosixToNetError(*Posix::GetErrorAddr());
|
||||
FinishNetCall(PosixToNetError(*Posix::GetErrorAddr()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI NetEtherNtostr(const Net::NetEtherAddr* n, char* str, size_t len) {
|
||||
return NET_CALL(Net::NetEtherNtostr(n, str, len));
|
||||
return FinishNetCall(Net::NetEtherNtostr(n, str, len));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI NetGetMacAddress(Net::NetEtherAddr* addr, int flags) {
|
||||
return NET_CALL(Net::NetGetMacAddress(addr, flags));
|
||||
return FinishNetCall(Net::NetGetMacAddress(addr, flags));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI NetGetSockInfo(int s, void* info, int n, int flags) {
|
||||
return NET_CALL(Net::NetGetSockInfo(s, info, n, flags));
|
||||
return FinishNetCall(Net::NetGetSockInfo(s, info, n, flags));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI NetEpollCreate(const char* name, int flags) {
|
||||
@@ -163,7 +155,7 @@ int KYTY_SYSV_ABI NetEpollDestroy(int eid) {
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI NetSocketClose(int s) {
|
||||
return NET_CALL(Net::SocketClose(s));
|
||||
return FinishNetCall(Net::SocketClose(s));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI NetSetsockopt(int s, int level, int optname, const void* optval,
|
||||
@@ -1465,7 +1457,7 @@ LIB_DEFINE(InitNet_1_NpManager) {
|
||||
LIB_FUNC("Ec63y59l9tw", NpManager::NpSetNpTitleId);
|
||||
LIB_FUNC("A2CQ3kgSopQ", NpManager::NpSetContentRestriction);
|
||||
LIB_FUNC("VfRSmPmj8Q8", NpManager::NpRegisterStateCallback);
|
||||
LIB_FUNC("qQJfO8HAiaY", NpManager::NpRegisterStateCallback);
|
||||
LIB_FUNC("qQJfO8HAiaY", NpManager::NpRegisterStateCallbackA);
|
||||
LIB_FUNC("M3wFXbYQtAA", NpManager::NpUnregisterStateCallback);
|
||||
LIB_FUNC("uFJpaKNBAj4", NpManager::NpRegisterGamePresenceCallback);
|
||||
LIB_FUNC("GImICnh+boA", NpManager::NpRegisterPlusEventCallback);
|
||||
@@ -3118,16 +3110,6 @@ static std::map<int64_t, NpWebApi2Request>& NpWebApi2Requests() {
|
||||
return requests;
|
||||
}
|
||||
|
||||
[[maybe_unused]] static std::string NpWebApi2MakeResponse(const NpWebApi2Request& request) {
|
||||
if (request.api_group.find("sessionManager") != std::string::npos ||
|
||||
request.path.find("sessions") != std::string::npos ||
|
||||
request.path.find("Sessions") != std::string::npos) {
|
||||
return R"({"gameSessions":[],"playerSessions":[]})";
|
||||
}
|
||||
|
||||
return "{}";
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI NpWebApi2Initialize(int lib_http_ctx_id, size_t pool_size) {
|
||||
PRINT_NAME();
|
||||
|
||||
@@ -3224,12 +3206,10 @@ NpWebApi2SendRequest(int64_t request_id, const void* data, size_t data_size,
|
||||
return NP_WEBAPI2_ERROR_REQUEST_NOT_FOUND;
|
||||
}
|
||||
|
||||
// request->second.response = NpWebApi2MakeResponse(request->second);
|
||||
request->second.response.clear();
|
||||
request->second.read_offset = 0;
|
||||
|
||||
if (response_info_option != nullptr) {
|
||||
// response_info_option->http_status = 200;
|
||||
response_info_option->http_status = 0;
|
||||
response_info_option->response_data_size = request->second.response.size();
|
||||
if (response_info_option->error_object != nullptr &&
|
||||
@@ -3238,7 +3218,6 @@ NpWebApi2SendRequest(int64_t request_id, const void* data, size_t data_size,
|
||||
}
|
||||
}
|
||||
|
||||
// return 0;
|
||||
return NP_WEBAPI2_ERROR_NOT_SIGNED_IN;
|
||||
}
|
||||
|
||||
@@ -3743,6 +3722,21 @@ namespace LibGameLiveStreaming {
|
||||
|
||||
LIB_VERSION("GameLiveStreaming", 1, "GameLiveStreaming", 1, 1);
|
||||
|
||||
constexpr int GAME_LIVE_STREAMING_ERROR_INVALID_PARAM = -2136997886;
|
||||
|
||||
struct GameLiveStreamingStatus2 {
|
||||
int32_t user_id;
|
||||
bool is_on_air;
|
||||
uint32_t spectator_count;
|
||||
uint32_t text_message_count;
|
||||
uint32_t command_message_count;
|
||||
uint8_t reserved[52];
|
||||
};
|
||||
|
||||
static_assert(sizeof(GameLiveStreamingStatus2) == 72);
|
||||
static_assert(offsetof(GameLiveStreamingStatus2, is_on_air) == 4);
|
||||
static_assert(offsetof(GameLiveStreamingStatus2, spectator_count) == 8);
|
||||
|
||||
static int KYTY_SYSV_ABI GameLiveStreamingInitialize(size_t heap_size) {
|
||||
PRINT_NAME();
|
||||
|
||||
@@ -3757,9 +3751,34 @@ static int KYTY_SYSV_ABI GameLiveStreamingTerminate() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI GameLiveStreamingGetCurrentStatus2(GameLiveStreamingStatus2* status) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (status == nullptr) {
|
||||
return GAME_LIVE_STREAMING_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
std::memset(status, 0, sizeof(*status));
|
||||
status->user_id = -1;
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI GameLiveStreamingGetSocialFeedbackMessagesCount(int type, uint32_t* count) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (type < 1 || type > 3 || count == nullptr) {
|
||||
return GAME_LIVE_STREAMING_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
*count = 0;
|
||||
return OK;
|
||||
}
|
||||
|
||||
LIB_DEFINE(InitPlatform_1_GameLiveStreaming) {
|
||||
LIB_FUNC("kvYEw2lBndk", LibGameLiveStreaming::GameLiveStreamingInitialize);
|
||||
LIB_FUNC("9yK6Fk8mKOQ", LibGameLiveStreaming::GameLiveStreamingTerminate);
|
||||
LIB_FUNC("lK8dLBNp9OE", LibGameLiveStreaming::GameLiveStreamingGetCurrentStatus2);
|
||||
LIB_FUNC("yeQKjHETi40", LibGameLiveStreaming::GameLiveStreamingGetSocialFeedbackMessagesCount);
|
||||
}
|
||||
|
||||
} // namespace LibGameLiveStreaming
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user