Compare commits

...

39 Commits

Author SHA1 Message Date
nmzik 8cecaf4e30 Merge PR #716: Fix Unicode paths on Windows
Preserve native Windows paths for filesystem operations, command-line options,
and game icons. Complete Unicode handling in recursive file listings and file
logging, restore unmounting by host directory, and extend regression coverage.

Original implementation by Augusto Domingos (@AghastyGD); retain all five of
his PR commits and include the reviewed follow-up fixes.

Closes #714

Co-authored-by: Augusto Domingos <augustodomingosvasco@gmail.com>
2026-09-20 02:14:22 +04:00
nmzik 82b2711cdf audio: play ATRAC9 sampler blocks 2026-09-20 01:47:43 +04:00
nmzik 35a275e97d avplayer: signal when playback ends 2026-09-20 01:47:43 +04:00
nmzik 303a0203b6 audio: play NGS2 PCM buffers 2026-09-20 01:47:43 +04:00
Çağatay Güçlü cfbaee07f0 Persist SaveDataMemory2 save data to disk (fixes Astro Bot progress not saving) (#659)
* Persist save-data memory across restarts

* Fix SetSaveDataMemory2 data loss, split saves per user, add atomic writes everywhere

- SaveDataSetSaveDataMemory2 treated data_num=0 as "no entries" even when
  a valid data pointer was supplied. Astro Bot calls this with data_num=0
  meaning "one entry, at data[0]" (an implicit-count convention), so every
  real save write from it was silently dropped as a no-op. Default
  data_num to 1 when data is non-null, matching that convention.
- SaveDataSetupSaveDataMemory2/Get/SetSaveDataMemory2 rejected any call
  that included a non-null icon pointer. Icon persistence isn't
  implemented, but titles are allowed to pass one anyway; rejecting the
  whole call over it made every title that does so fail outright. Ignore
  icon instead of rejecting.
- SaveDataMount3/SaveDataTransferringMount/SaveDataDelete built their
  save-data path from title+dir_name only, dropping the user_id PS5
  local users are keyed by (unlike the newer SetSaveDataMemory2 layout,
  which already nests by user_id). Nest these under user_id too so saves
  from different local users can no longer collide. No migration for the
  old flat layout - the project is still pre-release/experimental.
- Classic SaveDataMount3-mounted titles (Blasphemous, Dead Cells, ...)
  write their save files directly through the guest's normal file I/O
  once mounted, bypassing SaveData's own write path entirely, so they
  had none of the crash-safety SetSaveDataMemory2 already had. Added the
  same protection at the file-system layer: a guest file opened under a
  /savedataN mount that recreates the file from scratch (O_CREAT and/or
  O_TRUNC) is now written to a .tmp shadow file and atomically replaces
  the real one on close, instead of being truncated in place.
- Both write paths now back up the file they're about to replace into a
  sibling "backups" folder (same filename, no added suffix) before the
  atomic replace, so a manual recovery copy of the previous save always
  survives a write - even one that "succeeds" but wasn't wanted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Fix save-memory compatibility and preserve existing saves

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 00:51:22 +04:00
Augusto Domingos fe45265fcd preserve native paths when creating directories 2026-09-19 17:53:15 +02:00
Augusto Domingos 8b294eb5b8 Add unicode path regression coverage 2026-09-19 15:48:30 +02:00
Augusto Domingos 07c9978ac6 keep game icon paths native in windows 2026-09-19 14:45:24 +02:00
Augusto Domingos b61b3f2a40 handle UTF-8 command-line paths 2026-09-19 14:12:35 +02:00
Augusto Domingos 001dfea501 Preserve native windows paths in filesystem operations 2026-09-19 13:11:55 +02:00
nmzik f100f785da libs: fill controller and broadcast status outputs 2026-09-19 05:16:16 +04:00
nmzik ba1854bbdc net: start pool IDs at 1 2026-09-19 04:59:28 +04:00
nmzik ea6aae8577 shader: V_MAD_I16 2026-09-19 04:43:48 +04:00
nmzik 5b7d3343cf shader: V_CMP_NLT_F16 2026-09-18 12:25:13 +04:00
nmzik 394e638905 shader: V_MUL_LO_U16 2026-09-18 11:21:57 +04:00
nmzik c2b7b0b58c Revert "BC7-RGBA32_UINT views (Vulkan validation)"
This reverts commit 165234b159.
2026-09-18 11:12:36 +04:00
nmzik 1d2f59dfa4 ajm: reject invalid batch storage 2026-09-18 09:11:55 +04:00
nmzik 007d7ea2eb audio: infer stereo RAW AAC layout from channel-pair packets
Co-authored-by: BlackCAT <52720489+psnwd@users.noreply.github.com>
2026-09-18 09:11:55 +04:00
nmzik 95d3d9d3ad shader: fix packed integer negation
Co-authored-by: shadowbeat070 <70843316+shadowbeat070@users.noreply.github.com>
2026-09-18 09:11:55 +04:00
nmzik bfc8cd0e0b shader: DPP8 moves
Co-authored-by: foufouadi <attar.foued@gmail.com>
Co-authored-by: fxpw <fxpw@outlook.com>
2026-09-18 09:11:55 +04:00
nmzik 165234b159 BC7-RGBA32_UINT views (Vulkan validation)
Co-authored-by: Techx3 <56268269+Techx3@users.noreply.github.com>
Co-authored-by: shadowbeat070 <70843316+shadowbeat070@users.noreply.github.com>
Co-authored-by: SueryRD <the.ai.tester2@gmail.com>
Co-authored-by: BlackCAT <52720489+psnwd@users.noreply.github.com>
2026-09-18 09:11:55 +04:00
nmzik 6493674726 shader: dual-source blending (Vulkan) 2026-09-18 09:11:55 +04:00
nmzik cbedf5707f UDP P2P sockets 2026-09-18 09:11:55 +04:00
nmzik 57c5b206ca fix network errno 2026-09-18 09:11:55 +04:00
nmzik 87b4417aca fix initial NP sign-in callback 2026-09-18 09:11:55 +04:00
nmzik 104530eafc fix Windows build (#680)
fix Windows Vulkan barrier macro
2026-09-17 21:32:54 +04:00
nmzik 6cfde37e98 avoid extra allocations 2026-09-17 20:35:36 +04:00
nmzik c5cebc1d19 remove unused helpers and the -64 suffix 2026-09-17 20:35:36 +04:00
nmzik 697b18b366 replace ByteBuffer with std::vector 2026-09-17 20:35:36 +04:00
nmzik 5b2935e36c remove the extra virtual memory layer 2026-09-17 20:35:36 +04:00
nmzik 5dc644edb4 clean up graphics helpers 2026-09-17 20:35:36 +04:00
nmzik 1211958b4e clean up runtime helpers 2026-09-17 20:35:36 +04:00
nmzik 218e7200bb clean up launcher helpers 2026-09-17 20:35:36 +04:00
nmzik 2bf78a94e0 remove string wrappers 2026-09-17 20:35:36 +04:00
nmzik cc8e02380f remove magic_enum wrappers
Co-authored-by: Saadi <cosxmos@gmail.com>
2026-09-17 20:35:36 +04:00
nmzik c66d977c1d shader: encode SNORM16 formatted stores for skinning buffers 2026-09-17 17:40:05 +04:00
nmzik 4b77686566 shader: V_CMP_NGT_F16 2026-09-17 16:56:32 +04:00
c0sxm0s a6144f794b launcher: report cheat file save failures (#669)
PatchesDialog::Save silently ignored QSaveFile failures, leaving the
status label showing the old "Loaded N cheat(s)" text so the user could
not tell that their selection was not saved. Report the failure in the
status label and the log instead.
2026-09-17 16:13:35 +04:00
Mininaut bab1c3614c libc: treat negative dynamic printf precision as omitted (#625) 2026-09-17 15:18:04 +04:00
114 changed files with 4067 additions and 3080 deletions
+3
View File
@@ -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)
+7
View File
@@ -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)
-166
View File
@@ -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_ */
-4
View File
@@ -24,8 +24,4 @@ std::string Debug::GetLinker() {
#endif
}
std::string Debug::GetBitness() {
return "64";
}
} // namespace Common
-1
View File
@@ -10,7 +10,6 @@ namespace Common {
namespace Debug {
std::string GetCompiler();
std::string GetLinker();
std::string GetBitness();
} // namespace Debug
} // namespace Common
+53 -84
View File
@@ -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
View File
@@ -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);
-97
View File
@@ -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_ */
+1 -2
View File
@@ -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));
}
-42
View File
@@ -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_ */
-1
View File
@@ -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);
+1 -4
View File
@@ -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;
+29 -31
View File
@@ -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
-53
View File
@@ -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_ */
+1 -5
View File
@@ -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.
-27
View File
@@ -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 -32
View File
@@ -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);
+31 -33
View File
@@ -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
View File
@@ -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_ */
-4
View File
@@ -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);
-1
View File
@@ -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();
-17
View File
@@ -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(&current_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);
-6
View File
@@ -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();
-63
View File
@@ -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
+2 -10
View File
@@ -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));
}
}
+1
View File
@@ -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));
}
-2
View File
@@ -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;
+12 -18
View File
@@ -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;
@@ -868,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());
@@ -953,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;
@@ -982,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;
}
@@ -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>
@@ -228,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,
@@ -300,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,
@@ -378,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,
@@ -666,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),
@@ -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;
@@ -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);
@@ -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;
+17 -27
View File
@@ -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); }
+1 -5
View File
@@ -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
+1 -1
View File
@@ -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
+11 -9
View File
@@ -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
View File
@@ -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;
}
+2 -9
View File
@@ -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);
-20
View File
@@ -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 -1
View File
@@ -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();
-4
View File
@@ -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 -13
View File
@@ -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;
-4
View File
@@ -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 -2
View File
@@ -1,9 +1,8 @@
#ifndef MANDATORY_LINEEDIT_H
#define MANDATORY_LINEEDIT_H
#include "common.h"
#include <QLineEdit>
#include <QObject>
#include <QString>
class QPaintEvent;
-4
View File
@@ -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);
+1 -13
View File
@@ -307,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();
@@ -401,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"));
@@ -428,7 +416,7 @@ void ConfigurationEditDialog::save() {
return;
}
update_info();
UpdateInfo(m_info, *m_ui);
emit accept();
}
+3 -7
View File
@@ -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));
}
}
+4 -19
View File
@@ -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()));
-4
View File
@@ -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();
+4 -17
View File
@@ -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);
+7 -3
View File
@@ -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."));
}
+12 -25
View File
@@ -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
View File
@@ -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
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+14
View File
@@ -812,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();
+1
View File
@@ -81,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);
+6 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+5 -5
View File
@@ -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();
}
+1 -1
View File
@@ -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;
+1
View File
@@ -11,6 +11,7 @@
#include "loader/symbolDatabase.h"
#include <algorithm>
#include <cstring>
#include <vector>
namespace Libs {
+1
View File
@@ -1,3 +1,4 @@
#include "common/assert.h"
#include "common/emulatorConfig.h"
#include "graphics/guest_gpu/tile.h"
#include "libs/agc.h"
+4 -7
View File
@@ -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
View File
@@ -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
+1
View File
@@ -206,6 +206,7 @@ LIB_DEFINE(InitPad_1) {
LIB_FUNC("rIZnR6eSpvk", Controller::PadResetOrientation);
LIB_FUNC("vDLMoJLde8I", PadSetTiltCorrectionState);
LIB_FUNC("gjP9-KQzoUk", Controller::PadGetControllerInformation);
LIB_FUNC("fCWdlnmB1Ks", Controller::PadIsRemoteController);
LIB_FUNC("hGbf2QTBmqc", PadGetExtControllerInformationStub);
LIB_FUNC("2JgFB2n9oUM", Controller::PadSetTriggerEffect);
LIB_FUNC("YndgXqQVV7c", Controller::PadReadState);
+5 -9
View File
@@ -13,15 +13,11 @@ LIB_VERSION("Psml", 1, "Psml", 1, 1);
namespace Psml {
constexpr int Error(uint32_t value) {
return static_cast<int>(value);
}
constexpr int PSML_ERROR_NOT_INITIALIZED = Error(0x8a810001u);
constexpr int PSML_ERROR_INVALID_OBJECT = Error(0x8a810005u);
constexpr int PSML_ERROR_INVALID_POINTER = Error(0x8a810009u);
constexpr int PSML_ERROR_INVALID_VALUE = Error(0x8a81000du);
constexpr int PSML_ERROR_NULL_OBJECT = Error(0x8a810014u);
constexpr int PSML_ERROR_NOT_INITIALIZED = static_cast<int>(0x8a810001u);
constexpr int PSML_ERROR_INVALID_OBJECT = static_cast<int>(0x8a810005u);
constexpr int PSML_ERROR_INVALID_POINTER = static_cast<int>(0x8a810009u);
constexpr int PSML_ERROR_INVALID_VALUE = static_cast<int>(0x8a81000du);
constexpr int PSML_ERROR_NULL_OBJECT = static_cast<int>(0x8a810014u);
constexpr uint32_t SHARED_RESOURCES_MAGIC = 0xa9c4;
constexpr uint32_t CONTEXT_MAGIC = 0x9231;
+306 -53
View File
@@ -14,7 +14,9 @@
#include <algorithm>
#include <cstring>
#include <ctime>
#include <deque>
#include <map>
#include <vector>
namespace Libs {
@@ -176,6 +178,13 @@ struct SaveDataMemorySet2 {
uint8_t reserved[24];
};
struct SaveDataMemorySync {
int32_t user_id;
uint32_t slot_id;
uint32_t option;
uint8_t reserved[28];
};
struct SaveDataTransferringMount {
int32_t user_id;
const SceSaveDataTitleId* title_id;
@@ -230,23 +239,155 @@ static constexpr uint32_t SAVE_DATA_COMMIT_MODE_BACKUP_ASYNC = 1u;
static constexpr uint32_t SAVE_DATA_EVENT_TYPE_UMOUNT_BACKUP_END = 1u;
static constexpr uint32_t SAVE_DATA_EVENT_TYPE_BACKUP_END = 2u;
static constexpr uint32_t SAVE_DATA_EVENT_TYPE_MEMORY_SYNC_END = 3u;
static constexpr uint32_t SAVE_DATA_EVENT_TYPE_COMMIT_BACKUP_END = 4u;
static std::vector<uint8_t> g_save_data_memory(0x10000);
static constexpr size_t SAVE_DATA_MEMORY_MAX_SIZE = 32 * 1024 * 1024;
static constexpr uint32_t SAVE_DATA_MEMORY_SET_PARAM = 1u;
static constexpr uint32_t SAVE_DATA_MEMORY_DOUBLE_BUFFER = 2u;
struct SaveDataMemory {
std::vector<uint8_t> data;
SaveDataParam param {};
uint32_t option = 0;
};
static std::map<std::filesystem::path, SaveDataMemory> g_save_data_memory;
static int32_t g_next_transaction_resource = 1;
static std::deque<SaveDataEvent> g_save_data_events;
static SaveDataMountSlots g_mount_slots;
static Common::Mutex g_mount_mutex;
static bool valid_path_component(std::string_view name) {
return !name.empty() && name != "." && name != ".." &&
name.find_first_not_of(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.@") ==
std::string_view::npos;
}
template <size_t N>
static bool valid_path_component(const char (&name)[N]) {
const auto end = std::find(name, name + N, '\0');
return end != name + N && valid_path_component(std::string_view(name, end - name));
}
static std::string get_title_id() {
std::string title_id;
if (!Loader::SystemContentParamSfoGetString("TITLE_ID", &title_id) || title_id.empty()) {
if (!Loader::SystemContentParamSfoGetString("TITLE_ID", &title_id) ||
!valid_path_component(title_id)) {
title_id = "UNKNOWN";
}
return title_id;
}
static std::string memory_dir_name(uint32_t slot_id) {
return "sce_sdmemory" + (slot_id == 0 ? std::string() : std::to_string(slot_id));
}
static std::filesystem::path save_directory(std::string_view title_id, std::string_view dir_name,
int32_t user_id) {
const auto directory = std::filesystem::path(SAVE_DATA_DIR) / title_id / dir_name;
for (uint32_t slot = 0; slot < 4; slot++) {
if (dir_name == memory_dir_name(slot)) {
return directory / std::to_string(user_id);
}
}
return directory;
}
static std::filesystem::path memory_directory(int32_t user_id, uint32_t slot_id) {
return save_directory(get_title_id(), memory_dir_name(slot_id), user_id);
}
static bool valid_memory_range(const SaveDataMemoryData& data, size_t size) {
return data.buf != nullptr && data.offset >= 0 && static_cast<uint64_t>(data.offset) <= size &&
data.buf_size <= size - static_cast<size_t>(data.offset);
}
// Keep memory.dat as raw guest bytes. Replacing a flushed temporary file protects the
// previous save if writing fails or the emulator exits partway through an update.
static int write_memory_file(const std::filesystem::path& path, const void* data, uint32_t size) {
const auto temporary = std::filesystem::path(path.string() + ".tmp");
Common::File file;
if (!file.Create(temporary)) {
return SAVE_DATA_ERROR_INTERNAL;
}
uint32_t written = 0;
file.Write(data, size, &written);
const bool flushed = file.Flush();
file.Close();
std::error_code error;
if (written == size && flushed) {
// Common::File::RenameFile deliberately refuses to replace an existing file.
std::filesystem::rename(temporary, path, error);
if (!error) {
return OK;
}
}
std::filesystem::remove(temporary, error);
return SAVE_DATA_ERROR_INTERNAL;
}
static int save_memory(const std::filesystem::path& directory, const SaveDataMemory& memory) {
std::error_code error;
std::filesystem::create_directories(directory, error);
if (error) {
return SAVE_DATA_ERROR_INTERNAL;
}
// Display metadata is a separate host sidecar, leaving memory.dat as raw guest bytes.
int result = write_memory_file(directory / "param.bin", &memory.param, sizeof(memory.param));
if (result == OK) {
result =
write_memory_file(directory / "memory.dat", memory.data.data(), memory.data.size());
}
return result;
}
static int load_memory(const std::filesystem::path& directory, SaveDataMemory* memory,
size_t* existed_size) {
std::error_code error;
const bool exists = std::filesystem::exists(directory / "memory.dat", error);
if (error) {
return SAVE_DATA_ERROR_INTERNAL;
}
if (!exists) {
return OK;
}
Common::File file(directory / "memory.dat", Common::File::Mode::Read);
if (file.IsInvalid()) {
return SAVE_DATA_ERROR_INTERNAL;
}
*existed_size = file.Size();
if (*existed_size == 0 || *existed_size > SAVE_DATA_MEMORY_MAX_SIZE) {
return SAVE_DATA_ERROR_BROKEN;
}
const auto size = static_cast<uint32_t>(std::min(*existed_size, memory->data.size()));
uint32_t read = 0;
file.Read(memory->data.data(), size, &read);
if (read != size) {
return SAVE_DATA_ERROR_BROKEN;
}
file.Close();
const bool has_param = std::filesystem::exists(directory / "param.bin", error);
if (error) {
return SAVE_DATA_ERROR_INTERNAL;
}
if (has_param) {
if (!file.Open(directory / "param.bin", Common::File::Mode::Read)) {
return SAVE_DATA_ERROR_INTERNAL;
}
if (file.Size() != sizeof(memory->param)) {
return SAVE_DATA_ERROR_BROKEN;
}
file.Read(&memory->param, sizeof(memory->param), &read);
if (read != sizeof(memory->param)) {
return SAVE_DATA_ERROR_BROKEN;
}
}
return OK;
}
static void queue_save_data_event(uint32_t type, int32_t user_id,
const SceSaveDataTitleId* title_id,
const SceSaveDataDirName* dir_name, int32_t error_code = OK) {
@@ -322,6 +463,7 @@ int KYTY_SYSV_ABI SaveDataTerminate() {
return SAVE_DATA_ERROR_BUSY;
}
g_save_data_events.clear();
g_save_data_memory.clear();
return OK;
}
@@ -347,10 +489,17 @@ int KYTY_SYSV_ABI SaveDataDirNameSearch(const SaveDataDirNameSearchCond* cond,
PRINT_NAME();
if (cond == nullptr || result == nullptr ||
(cond->title_id != nullptr && !valid_path_component(cond->title_id->data)) ||
(result->dir_names_num != 0 && result->dir_names == nullptr) ||
(cond->dir_name != nullptr &&
std::memchr(cond->dir_name->data, 0, sizeof(cond->dir_name->data)) == nullptr) ||
static_cast<uint32_t>(cond->key) > static_cast<uint32_t>(SaveDataSortKey::FreeBlocks) ||
static_cast<uint32_t>(cond->order) > static_cast<uint32_t>(SaveDataSortOrder::Descent)) {
return SAVE_DATA_ERROR_PARAMETER;
}
if (cond->user_id < 0) {
return SAVE_DATA_ERROR_INVALID_LOGIN_USER;
}
LOGF("\t user_id = %d\n"
"\t title_id = %s\n"
@@ -369,14 +518,15 @@ int KYTY_SYSV_ABI SaveDataDirNameSearch(const SaveDataDirNameSearchCond* cond,
std::memset(result->reserved, 0, sizeof(result->reserved));
result->pad2 = 0;
Common::LockGuard lock(g_mount_mutex);
const std::string title_id = cond->title_id != nullptr ? cond->title_id->data : get_title_id();
const auto root = std::filesystem::path(SAVE_DATA_DIR) / title_id;
std::vector<std::string> dir_list;
std::string root =
Common::FixDirectorySlash((std::string(SAVE_DATA_DIR) + "/" + get_title_id()));
if (Common::File::IsDirectoryExisting(root)) {
for (const auto& entry: Common::File::GetDirEntries(root)) {
if (!entry.is_file && entry.name != "." && entry.name != ".." &&
!Common::StartsWith(entry.name, "sce_")) {
!entry.name.starts_with("sce_") && valid_path_component(entry.name)) {
if (cond->dir_name == nullptr || cond->dir_name->data[0] == '\0' ||
dir_name_match(Common::ToLower(entry.name).c_str(),
Common::ToLower(std::string(cond->dir_name->data)).c_str())) {
@@ -423,9 +573,13 @@ int KYTY_SYSV_ABI SaveDataDirNameSearch(const SaveDataDirNameSearchCond* cond,
int KYTY_SYSV_ABI SaveDataMount3(const SaveDataMount3* mount, SaveDataMountResult* mount_result) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(mount == nullptr);
EXIT_NOT_IMPLEMENTED(mount_result == nullptr);
EXIT_NOT_IMPLEMENTED(mount->dir_name == nullptr);
if (mount == nullptr || mount_result == nullptr || mount->dir_name == nullptr ||
!valid_path_component(mount->dir_name->data)) {
return SAVE_DATA_ERROR_PARAMETER;
}
if (mount->user_id < 0) {
return SAVE_DATA_ERROR_INVALID_LOGIN_USER;
}
LOGF("\t user_id = %d\n"
"\t dir_name = %s\n"
@@ -439,9 +593,8 @@ int KYTY_SYSV_ABI SaveDataMount3(const SaveDataMount3* mount, SaveDataMountResul
*mount_result = {};
Common::LockGuard lock(g_mount_mutex);
const std::string dir_name = mount->dir_name->data;
const std::string mount_dir =
std::string(SAVE_DATA_DIR) + "/" + get_title_id() + "/" + dir_name;
const std::string dir_name = mount->dir_name->data;
const auto mount_dir = save_directory(get_title_id(), dir_name, mount->user_id).string();
const bool create = ((mount->mount_mode & 4u) != 0);
const bool create2 = ((mount->mount_mode & 32u) != 0);
const bool open = (!create && !create2 && ((mount->mount_mode & 3u) != 0));
@@ -481,9 +634,13 @@ int KYTY_SYSV_ABI SaveDataSetupSaveDataMemory2(const SaveDataMemorySetup2* setup
SaveDataMemorySetupResult* result) {
PRINT_NAME();
if (setup_param == nullptr) {
if (setup_param == nullptr || setup_param->slot_id >= 4 || setup_param->memory_size == 0 ||
setup_param->memory_size > SAVE_DATA_MEMORY_MAX_SIZE || (setup_param->option & ~3u) != 0) {
return SAVE_DATA_ERROR_PARAMETER;
}
if (setup_param->user_id < 0) {
return SAVE_DATA_ERROR_INVALID_LOGIN_USER;
}
LOGF("\t option = 0x%08" PRIx32 "\n"
"\t user_id = %" PRId32 "\n"
@@ -493,13 +650,41 @@ int KYTY_SYSV_ABI SaveDataSetupSaveDataMemory2(const SaveDataMemorySetup2* setup
setup_param->option, setup_param->user_id, static_cast<uint64_t>(setup_param->memory_size),
static_cast<uint64_t>(setup_param->icon_memory_size), setup_param->slot_id);
if (setup_param->memory_size > g_save_data_memory.size()) {
g_save_data_memory.resize(setup_param->memory_size);
Common::LockGuard lock(g_mount_mutex);
const auto directory = memory_directory(setup_param->user_id, setup_param->slot_id);
if (g_save_data_memory.contains(directory) ||
g_mount_slots.FindAvailable(memory_dir_name(setup_param->slot_id)) ==
SaveDataMountSlots::BUSY) {
return SAVE_DATA_ERROR_BUSY;
}
size_t total = setup_param->memory_size *
((setup_param->option & SAVE_DATA_MEMORY_DOUBLE_BUFFER) != 0 ? 2 : 1);
for (const auto& [path, memory]: g_save_data_memory) {
total +=
memory.data.size() * ((memory.option & SAVE_DATA_MEMORY_DOUBLE_BUFFER) != 0 ? 2 : 1);
}
if (g_save_data_memory.size() >= 4 || total > SAVE_DATA_MEMORY_MAX_SIZE) {
return SAVE_DATA_ERROR_LIMITATION_OVER;
}
SaveDataMemory memory;
memory.data.resize(setup_param->memory_size);
memory.option = setup_param->option;
std::snprintf(memory.param.title, sizeof(memory.param.title), "Saved Data");
size_t existed_size = 0;
const int status = load_memory(directory, &memory, &existed_size);
if (status != OK) {
return status;
}
if (existed_size == 0 && setup_param->init_param != nullptr &&
(memory.option & SAVE_DATA_MEMORY_SET_PARAM) != 0) {
memory.param = *setup_param->init_param;
memory.param.mtime = 0;
}
g_save_data_memory.emplace(directory, std::move(memory));
if (result != nullptr) {
*result = {};
result->existed_memory_size = g_save_data_memory.size();
result->existed_memory_size = existed_size;
}
return OK;
@@ -508,9 +693,12 @@ int KYTY_SYSV_ABI SaveDataSetupSaveDataMemory2(const SaveDataMemorySetup2* setup
int KYTY_SYSV_ABI SaveDataGetSaveDataMemory2(SaveDataMemoryGet2* get_param) {
PRINT_NAME();
if (get_param == nullptr) {
if (get_param == nullptr || get_param->slot_id >= 4) {
return SAVE_DATA_ERROR_PARAMETER;
}
if (get_param->user_id < 0) {
return SAVE_DATA_ERROR_INVALID_LOGIN_USER;
}
LOGF("\t user_id = %" PRId32 "\n"
"\t data = 0x%016" PRIx64 "\n"
@@ -521,20 +709,23 @@ int KYTY_SYSV_ABI SaveDataGetSaveDataMemory2(SaveDataMemoryGet2* get_param) {
reinterpret_cast<uint64_t>(get_param->param), reinterpret_cast<uint64_t>(get_param->icon),
get_param->slot_id);
Common::LockGuard lock(g_mount_mutex);
const auto it =
g_save_data_memory.find(memory_directory(get_param->user_id, get_param->slot_id));
if (it == g_save_data_memory.end()) {
return SAVE_DATA_ERROR_MEMORY_NOT_READY;
}
const auto& memory = it->second;
if ((get_param->param != nullptr && (memory.option & SAVE_DATA_MEMORY_SET_PARAM) == 0) ||
(get_param->data != nullptr && !valid_memory_range(*get_param->data, memory.data.size()))) {
return SAVE_DATA_ERROR_PARAMETER;
}
if (get_param->data != nullptr) {
auto* data = get_param->data;
if (data->buf == nullptr || data->offset < 0) {
return SAVE_DATA_ERROR_PARAMETER;
}
const auto offset = static_cast<size_t>(data->offset);
if (offset + data->buf_size > g_save_data_memory.size()) {
g_save_data_memory.resize(offset + data->buf_size);
}
std::memcpy(data->buf, g_save_data_memory.data() + offset, data->buf_size);
const auto& data = *get_param->data;
std::memcpy(data.buf, memory.data.data() + data.offset, data.buf_size);
}
if (get_param->param != nullptr) {
std::memset(get_param->param, 0, sizeof(*get_param->param));
*get_param->param = memory.param;
}
if (get_param->icon != nullptr) {
get_param->icon->data_size = 0;
@@ -546,9 +737,13 @@ int KYTY_SYSV_ABI SaveDataGetSaveDataMemory2(SaveDataMemoryGet2* get_param) {
int KYTY_SYSV_ABI SaveDataSetSaveDataMemory2(const SaveDataMemorySet2* set_param) {
PRINT_NAME();
if (set_param == nullptr) {
if (set_param == nullptr || set_param->slot_id >= 4 || set_param->data_num > 5 ||
(set_param->data == nullptr && set_param->data_num != 0)) {
return SAVE_DATA_ERROR_PARAMETER;
}
if (set_param->user_id < 0) {
return SAVE_DATA_ERROR_INVALID_LOGIN_USER;
}
LOGF("\t user_id = %" PRId32 "\n"
"\t data = 0x%016" PRIx64 "\n"
@@ -560,23 +755,42 @@ int KYTY_SYSV_ABI SaveDataSetSaveDataMemory2(const SaveDataMemorySet2* set_param
reinterpret_cast<uint64_t>(set_param->param), reinterpret_cast<uint64_t>(set_param->icon),
set_param->data_num, set_param->slot_id);
const uint32_t data_num = (set_param->data_num == 0 ? 1 : set_param->data_num);
if (set_param->data != nullptr) {
for (uint32_t i = 0; i < data_num; i++) {
const auto& data = set_param->data[i];
if (data.buf == nullptr || data.offset < 0) {
return SAVE_DATA_ERROR_PARAMETER;
}
const auto offset = static_cast<size_t>(data.offset);
if (offset + data.buf_size > g_save_data_memory.size()) {
g_save_data_memory.resize(offset + data.buf_size);
}
std::memcpy(g_save_data_memory.data() + offset, data.buf, data.buf_size);
Common::LockGuard lock(g_mount_mutex);
const auto it =
g_save_data_memory.find(memory_directory(set_param->user_id, set_param->slot_id));
if (it == g_save_data_memory.end()) {
return SAVE_DATA_ERROR_MEMORY_NOT_READY;
}
if (set_param->param != nullptr && (it->second.option & SAVE_DATA_MEMORY_SET_PARAM) == 0) {
return SAVE_DATA_ERROR_PARAMETER;
}
// Some games supply one descriptor with an implicit zero count.
const uint32_t data_num =
set_param->data != nullptr && set_param->data_num == 0 ? 1 : set_param->data_num;
for (uint32_t i = 0; i < data_num; i++) {
if (!valid_memory_range(set_param->data[i], it->second.data.size())) {
return SAVE_DATA_ERROR_PARAMETER;
}
}
if (data_num == 0 && set_param->param == nullptr) {
return OK;
}
return OK;
// Save the complete update before publishing it so progress survives process exit.
auto updated = it->second;
for (uint32_t i = 0; i < data_num; i++) {
const auto& data = set_param->data[i];
std::memcpy(updated.data.data() + data.offset, data.buf, data.buf_size);
}
if (set_param->param != nullptr) {
updated.param = *set_param->param;
}
updated.param.mtime = static_cast<int64_t>(std::time(nullptr));
const int status = save_memory(it->first, updated);
if (status == OK) {
it->second = std::move(updated);
}
return status;
}
int KYTY_SYSV_ABI SaveDataTransferringMount(const SaveDataTransferringMount* mount,
@@ -584,9 +798,13 @@ int KYTY_SYSV_ABI SaveDataTransferringMount(const SaveDataTransferringMount* mou
PRINT_NAME();
if (mount == nullptr || mount_result == nullptr || mount->title_id == nullptr ||
mount->dir_name == nullptr) {
mount->dir_name == nullptr || !valid_path_component(mount->title_id->data) ||
!valid_path_component(mount->dir_name->data)) {
return SAVE_DATA_ERROR_PARAMETER;
}
if (mount->user_id < 0) {
return SAVE_DATA_ERROR_INVALID_LOGIN_USER;
}
LOGF("\t user_id = %" PRId32 "\n"
"\t title_id = %s\n"
@@ -597,8 +815,7 @@ int KYTY_SYSV_ABI SaveDataTransferringMount(const SaveDataTransferringMount* mou
Common::LockGuard lock(g_mount_mutex);
const std::string dir_name = mount->dir_name->data;
const std::string mount_dir =
std::string(SAVE_DATA_DIR) + "/" + mount->title_id->data + "/" + dir_name;
const auto mount_dir = save_directory(mount->title_id->data, dir_name, mount->user_id).string();
const int slot = g_mount_slots.FindAvailable(dir_name);
if (slot == SaveDataMountSlots::BUSY) {
return SAVE_DATA_ERROR_BUSY;
@@ -669,6 +886,7 @@ int KYTY_SYSV_ABI SaveDataCommit(const SaveDataCommitParam* param) {
param->resource, param->commit_mode);
if ((param->commit_mode & SAVE_DATA_COMMIT_MODE_BACKUP_ASYNC) != 0) {
Common::LockGuard lock(g_mount_mutex);
queue_save_data_event(SAVE_DATA_EVENT_TYPE_COMMIT_BACKUP_END, 0, nullptr, nullptr);
}
@@ -678,9 +896,13 @@ int KYTY_SYSV_ABI SaveDataCommit(const SaveDataCommitParam* param) {
int KYTY_SYSV_ABI SaveDataDelete(const SaveDataDelete* del) {
PRINT_NAME();
if (del == nullptr || del->dir_name == nullptr) {
if (del == nullptr || del->dir_name == nullptr || !valid_path_component(del->dir_name->data) ||
(del->title_id != nullptr && !valid_path_component(del->title_id->data))) {
return SAVE_DATA_ERROR_PARAMETER;
}
if (del->user_id < 0) {
return SAVE_DATA_ERROR_INVALID_LOGIN_USER;
}
LOGF("\t user_id = %" PRId32 "\n"
"\t title_id = %s\n"
@@ -688,13 +910,17 @@ int KYTY_SYSV_ABI SaveDataDelete(const SaveDataDelete* del) {
del->user_id, del->title_id != nullptr ? del->title_id->data : "<default>",
del->dir_name->data);
std::string dir =
std::string(SAVE_DATA_DIR) + "/" + get_title_id() + "/" + std::string(del->dir_name->data);
if (Common::File::IsDirectoryExisting(dir)) {
Common::File::DeleteDirectory(dir);
Common::LockGuard lock(g_mount_mutex);
const auto dir = save_directory(del->title_id != nullptr ? del->title_id->data : get_title_id(),
del->dir_name->data, del->user_id);
if (g_mount_slots.FindAvailable(del->dir_name->data) == SaveDataMountSlots::BUSY ||
g_save_data_memory.contains(dir)) {
return SAVE_DATA_ERROR_BUSY;
}
std::error_code error;
std::filesystem::remove_all(dir, error);
return OK;
return error ? SAVE_DATA_ERROR_INTERNAL : OK;
}
int KYTY_SYSV_ABI SaveDataGetParam(const SaveDataMountPoint* mount_point, uint32_t param_type,
@@ -760,10 +986,35 @@ int KYTY_SYSV_ABI SaveDataSaveIconByPath(const SaveDataMountPoint* mount_point,
return OK;
}
int KYTY_SYSV_ABI SaveDataSyncSaveDataMemory(const void* sync_param) {
int KYTY_SYSV_ABI SaveDataSyncSaveDataMemory(const SaveDataMemorySync* sync_param) {
PRINT_NAME();
LOGF("\t sync_param = 0x%016" PRIx64 "\n", reinterpret_cast<uint64_t>(sync_param));
if (sync_param == nullptr || sync_param->slot_id >= 4 || sync_param->option > 1) {
return SAVE_DATA_ERROR_PARAMETER;
}
if (sync_param->user_id < 0) {
return SAVE_DATA_ERROR_INVALID_LOGIN_USER;
}
Common::LockGuard lock(g_mount_mutex);
const auto it =
g_save_data_memory.find(memory_directory(sync_param->user_id, sync_param->slot_id));
if (it == g_save_data_memory.end()) {
return SAVE_DATA_ERROR_MEMORY_NOT_READY;
}
const auto dir_name = memory_dir_name(sync_param->slot_id);
if (g_mount_slots.FindAvailable(dir_name) == SaveDataMountSlots::BUSY) {
return SAVE_DATA_ERROR_BUSY;
}
const int status = save_memory(it->first, it->second);
if (sync_param->option == 1) {
return status;
}
SceSaveDataTitleId title {};
SceSaveDataDirName directory {};
std::snprintf(title.data, sizeof(title.data), "%s", get_title_id().c_str());
std::snprintf(directory.data, sizeof(directory.data), "%s", dir_name.c_str());
queue_save_data_event(SAVE_DATA_EVENT_TYPE_MEMORY_SYNC_END, sync_param->user_id, &title,
&directory, status);
return OK;
}
@@ -779,6 +1030,7 @@ int KYTY_SYSV_ABI SaveDataGetEventResult(const void* event_param, SaveDataEvent*
return SAVE_DATA_ERROR_PARAMETER;
}
Common::LockGuard lock(g_mount_mutex);
if (g_save_data_events.empty()) {
return SAVE_DATA_ERROR_NOT_FOUND;
}
@@ -812,6 +1064,7 @@ int KYTY_SYSV_ABI SaveDataBackup(const SaveDataBackup* backup) {
backup->user_id, backup->title_id != nullptr ? backup->title_id->data : "<default>",
backup->dir_name->data);
Common::LockGuard lock(g_mount_mutex);
queue_save_data_event(SAVE_DATA_EVENT_TYPE_BACKUP_END, backup->user_id, backup->title_id,
backup->dir_name);
+297 -88
View File
@@ -14,18 +14,18 @@
#endif
#else
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
#include "common/assert.h"
#include "common/byteBuffer.h"
#include "common/common.h"
#include "common/emulatorConfig.h"
#include "common/logging/log.h"
#include "common/stringUtils.h"
#include "common/threads.h"
#include "kernel/pthread.h"
#include "libs/errno.h"
@@ -40,9 +40,13 @@
#include <cstdio>
#include <cstring>
#include <fmt/format.h>
#include <fmt/ranges.h>
#include <limits>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <utility>
#include <vector>
namespace Libs::Network {
@@ -252,7 +256,7 @@ void Shutdown() {
int Network::PoolCreate(const char* name, int size) {
Common::LockGuard lock(m_mutex);
for (int id = 0; id < POOLS_MAX; id++) {
for (int id = 1; id < POOLS_MAX; id++) {
if (!m_pools[id].used) {
m_pools[id].used = true;
m_pools[id].size = size;
@@ -844,9 +848,37 @@ using SocketLength = socklen_t;
using SocketIoLength = size_t;
#endif
struct SocketTransport {
explicit SocketTransport(NativeSocket value): socket(value) {}
~SocketTransport() { Close(); }
int Close() {
if (socket == INVALID_NATIVE_SOCKET) {
return 0;
}
const auto value = std::exchange(socket, INVALID_NATIVE_SOCKET);
#if defined(_WIN32)
return closesocket(value);
#else
return ::close(value);
#endif
}
NativeSocket socket;
};
struct P2pEndpoint {
bool bound = false;
uint32_t address = 0;
uint16_t port = 0;
uint16_t virtual_port = 0;
int nonblocking = 0;
int broadcast = 0;
int send_buffer = 9216;
int receive_buffer = 40 * 1024;
};
struct SocketSlot {
bool used = false;
NativeSocket socket = INVALID_NATIVE_SOCKET;
std::shared_ptr<SocketTransport> transport;
std::optional<P2pEndpoint> p2p;
};
struct NetTimeval {
@@ -998,6 +1030,16 @@ static int ConvertSocketOptionLevel(int level) {
return (level == 0xffff ? SOL_SOCKET : level);
}
static int* P2pSocketOption(P2pEndpoint& endpoint, int option) {
switch (option) {
case 0x0020: return &endpoint.broadcast;
case 0x1001: return &endpoint.send_buffer;
case 0x1002: return &endpoint.receive_buffer;
case 0x1200: return &endpoint.nonblocking;
default: return nullptr;
}
}
static int ConvertMessageFlags(int flags) {
constexpr int guest_msg_peek = 0x00000002;
constexpr int guest_msg_dontroute = 0x00000004;
@@ -1104,19 +1146,30 @@ static int ConvertHostSockaddr(const sockaddr_storage* addr, SocketLength addrle
return 0;
}
static bool GetSocketBackend(int guest_fd, NativeSocket* out) {
static bool GetSocketBackend(int guest_fd, NativeSocket* out, SocketSlot* state = nullptr) {
EXIT_IF(out == nullptr);
if (guest_fd < 0 || guest_fd >= SOCKET_FD_MAX) {
SetGuestSocketError(Posix::POSIX_EBADF);
return false;
}
Common::LockGuard lock(g_socket_mutex);
if (!g_sockets[static_cast<size_t>(guest_fd)].used) {
const auto& slot = g_sockets[static_cast<size_t>(guest_fd)];
if (!slot.transport) {
SetGuestSocketError(Posix::POSIX_EBADF);
return false;
}
// Native-only operations cannot act on a transport shared by virtual ports.
if (slot.p2p && state == nullptr) {
SetGuestSocketError(Posix::POSIX_EOPNOTSUPP);
return false;
}
*out = g_sockets[static_cast<size_t>(guest_fd)].socket;
*out = slot.transport->socket;
if (state != nullptr) {
*state = slot;
}
return true;
}
@@ -1126,10 +1179,10 @@ bool KYTY_SYSV_ABI IsSocket(int s) {
}
Common::LockGuard lock(g_socket_mutex);
return g_sockets[static_cast<size_t>(s)].used;
return g_sockets[static_cast<size_t>(s)].transport != nullptr;
}
static bool TakeSocketBackend(int guest_fd, NativeSocket* out) {
static bool TakeSocketBackend(int guest_fd, SocketSlot* out) {
EXIT_IF(out == nullptr);
if (guest_fd < 0 || guest_fd >= SOCKET_FD_MAX) {
@@ -1138,22 +1191,24 @@ static bool TakeSocketBackend(int guest_fd, NativeSocket* out) {
Common::LockGuard lock(g_socket_mutex);
auto& slot = g_sockets[static_cast<size_t>(guest_fd)];
if (!slot.used) {
if (!slot.transport) {
return false;
}
*out = slot.socket;
*out = std::move(slot);
slot = {};
return true;
}
static int AllocSocketFd(NativeSocket socket) {
static int AllocSocketFd(const std::shared_ptr<SocketTransport>& transport, bool p2p = false) {
Common::LockGuard lock(g_socket_mutex);
for (int fd = SOCKET_FD_MIN; fd < SOCKET_FD_MAX; fd++) {
auto& slot = g_sockets[static_cast<size_t>(fd)];
if (!slot.used) {
slot.used = true;
slot.socket = socket;
if (!slot.transport) {
slot.transport = transport;
if (p2p) {
slot.p2p.emplace();
}
return fd;
}
}
@@ -1528,10 +1583,10 @@ int KYTY_SYSV_ABI EpollWait(int eid, NetEpollEvent* events, int maxevents, int t
return 0;
}
#if defined(_WIN32)
struct HostRegistration {
EpollRegistration guest;
NativeSocket socket = INVALID_NATIVE_SOCKET;
SocketSlot state;
};
fd_set host_read {};
@@ -1543,14 +1598,23 @@ int KYTY_SYSV_ABI EpollWait(int eid, NetEpollEvent* events, int maxevents, int t
std::vector<HostRegistration> host_registrations;
host_registrations.reserve(registrations.size());
int host_nfds = 0;
for (const auto& registration: registrations) {
NativeSocket socket = INVALID_NATIVE_SOCKET;
if (!GetSocketBackend(registration.id, &socket)) {
SocketSlot state;
if (!GetSocketBackend(registration.id, &socket, &state)) {
continue;
}
#if defined(_WIN32)
if (host_registrations.size() >= FD_SETSIZE) {
return SetGuestSocketError(Posix::POSIX_EINVAL);
}
#else
if (socket >= FD_SETSIZE) {
return SetGuestSocketError(Posix::POSIX_EINVAL);
}
host_nfds = std::max(host_nfds, socket + 1);
#endif
if ((registration.event.events & EPOLL_IN) != 0) {
FD_SET(socket, &host_read);
}
@@ -1558,7 +1622,7 @@ int KYTY_SYSV_ABI EpollWait(int eid, NetEpollEvent* events, int maxevents, int t
FD_SET(socket, &host_write);
}
FD_SET(socket, &host_except);
host_registrations.push_back({registration, socket});
host_registrations.push_back({registration, socket, std::move(state)});
}
if (host_registrations.empty()) {
@@ -1573,8 +1637,8 @@ int KYTY_SYSV_ABI EpollWait(int eid, NetEpollEvent* events, int maxevents, int t
host_timeout_ptr = &host_timeout;
}
const int result = ::select(0, &host_read, &host_write, &host_except, host_timeout_ptr);
if (result == SOCKET_ERROR) {
const int result = ::select(host_nfds, &host_read, &host_write, &host_except, host_timeout_ptr);
if (result < 0) {
return SetHostSocketError();
}
if (result == 0) {
@@ -1584,10 +1648,15 @@ int KYTY_SYSV_ABI EpollWait(int eid, NetEpollEvent* events, int maxevents, int t
int count = 0;
for (const auto& registration: host_registrations) {
uint32_t ready = 0;
if (FD_ISSET(registration.socket, &host_read)) {
if ((registration.guest.event.events & EPOLL_IN) != 0 &&
FD_ISSET(registration.socket, &host_read)) {
if (registration.state.p2p) {
return SetGuestSocketError(Posix::POSIX_EOPNOTSUPP);
}
ready |= EPOLL_IN;
}
if (FD_ISSET(registration.socket, &host_write)) {
if ((registration.guest.event.events & EPOLL_OUT) != 0 &&
FD_ISSET(registration.socket, &host_write)) {
ready |= EPOLL_OUT;
}
if (FD_ISSET(registration.socket, &host_except)) {
@@ -1607,10 +1676,6 @@ int KYTY_SYSV_ABI EpollWait(int eid, NetEpollEvent* events, int maxevents, int t
}
}
return count;
#else
(void)timeout;
return SetGuestSocketError(Posix::POSIX_ENOSYS);
#endif
}
int KYTY_SYSV_ABI EpollDestroy(int eid) {
@@ -1632,18 +1697,13 @@ int KYTY_SYSV_ABI SocketClose(int s) {
LOGF("\t s = %d\n", s);
NativeSocket socket = INVALID_NATIVE_SOCKET;
if (!TakeSocketBackend(s, &socket)) {
SocketSlot slot;
if (!TakeSocketBackend(s, &slot)) {
return NET_ERROR_EBADF;
}
RemoveSocketFromEpolls(s);
#if defined(_WIN32)
const int result = closesocket(socket);
#else
const int result = ::close(socket);
#endif
if (result != 0) {
if (slot.transport.use_count() == 1 && slot.transport->Close() != 0) {
return NET_ERROR_EBADF;
}
@@ -1668,18 +1728,31 @@ int KYTY_SYSV_ABI Socket(int family, int type, int protocol) {
return -1;
}
NativeSocket socket = ::socket(host_family, type, protocol);
constexpr int socket_dgram_p2p = 6;
const bool p2p = type == socket_dgram_p2p;
if (p2p && (family != 2 || protocol != 0)) {
return SetGuestSocketError(Posix::POSIX_EPROTONOSUPPORT);
}
NativeSocket socket = ::socket(host_family, p2p ? SOCK_DGRAM : type, protocol);
if (socket == INVALID_NATIVE_SOCKET) {
return SetHostSocketError();
}
const int fd = AllocSocketFd(socket);
if (fd < 0) {
auto transport = std::make_shared<SocketTransport>(socket);
if (p2p) {
// Logical sockets keep their own blocking mode; the shared transport never blocks.
#if defined(_WIN32)
closesocket(socket);
u_long enabled = 1;
const int result = ioctlsocket(socket, FIONBIO, &enabled);
#else
::close(socket);
const int result = ::fcntl(socket, F_SETFL, O_NONBLOCK);
#endif
if (result != 0) {
return SetHostSocketError();
}
}
const int fd = AllocSocketFd(transport, p2p);
if (fd < 0) {
*Posix::GetErrorAddr() = Posix::POSIX_EMFILE;
return -1;
}
@@ -1697,8 +1770,11 @@ int KYTY_SYSV_ABI Bind(int s, const void* addr, uint32_t addrlen) {
s, reinterpret_cast<uint64_t>(addr), addrlen);
NativeSocket socket = INVALID_NATIVE_SOCKET;
if (addr == nullptr || !GetSocketBackend(s, &socket)) {
*Posix::GetErrorAddr() = (addr == nullptr ? Posix::POSIX_EFAULT : Posix::POSIX_EBADF);
SocketSlot state;
if (addr == nullptr) {
return SetGuestSocketError(Posix::POSIX_EFAULT);
}
if (!GetSocketBackend(s, &socket, &state)) {
return -1;
}
@@ -1708,6 +1784,67 @@ int KYTY_SYSV_ABI Bind(int s, const void* addr, uint32_t addrlen) {
return -1;
}
if (state.p2p) {
if (addrlen < 16) {
return SetGuestSocketError(Posix::POSIX_EINVAL);
}
const auto& in = *reinterpret_cast<const sockaddr_in*>(&host_addr);
uint32_t address = 0;
std::memcpy(&address, &in.sin_addr, sizeof(address));
uint16_t virtual_port = 0;
std::memcpy(&virtual_port, static_cast<const uint8_t*>(addr) + 8, sizeof(virtual_port));
const auto requested_port = ntohs(virtual_port);
if (requested_port == 0) {
return SetGuestSocketError(Posix::POSIX_EOPNOTSUPP);
}
if (ntohs(in.sin_port) != 3658) {
return SetGuestSocketError(Posix::POSIX_EINVAL);
}
if (requested_port == 5353 || requested_port >= 32768) {
return SetGuestSocketError(Posix::POSIX_EACCES);
}
Common::LockGuard lock(g_socket_mutex);
auto& slot = g_sockets[static_cast<size_t>(s)];
if (slot.transport != state.transport) {
return SetGuestSocketError(Posix::POSIX_EBADF);
}
auto& endpoint = *slot.p2p;
if (endpoint.bound) {
return SetGuestSocketError(Posix::POSIX_EINVAL);
}
auto conflicts = [&](uint16_t port) {
return std::any_of(g_sockets.begin(), g_sockets.end(), [&](const auto& other) {
return other.p2p && other.p2p->bound && other.p2p->port == in.sin_port &&
other.p2p->address == address && other.p2p->virtual_port == port;
});
};
if (conflicts(virtual_port)) {
return SetGuestSocketError(Posix::POSIX_EADDRINUSE);
}
std::shared_ptr<SocketTransport> shared;
for (const auto& other: g_sockets) {
if (other.p2p && other.p2p->bound && other.p2p->port == in.sin_port &&
other.p2p->address == address) {
shared = other.transport;
break;
}
}
if (shared) {
slot.transport = std::move(shared);
} else if (::bind(socket, reinterpret_cast<const sockaddr*>(&host_addr), host_addrlen) !=
0) {
return SetHostSocketError();
}
endpoint.bound = true;
endpoint.address = address;
endpoint.port = in.sin_port;
endpoint.virtual_port = virtual_port;
LOGF("\t UDPP2P bound UDP port=%u virtual port=%u\n", ntohs(endpoint.port),
ntohs(endpoint.virtual_port));
return 0;
}
if (::bind(socket, reinterpret_cast<const sockaddr*>(&host_addr), host_addrlen) != 0) {
return SetHostSocketError();
}
@@ -1724,8 +1861,10 @@ int KYTY_SYSV_ABI Connect(int s, const void* addr, uint32_t addrlen) {
s, reinterpret_cast<uint64_t>(addr), addrlen);
NativeSocket socket = INVALID_NATIVE_SOCKET;
if (addr == nullptr || !GetSocketBackend(s, &socket)) {
*Posix::GetErrorAddr() = (addr == nullptr ? Posix::POSIX_EFAULT : Posix::POSIX_EBADF);
if (addr == nullptr) {
return SetGuestSocketError(Posix::POSIX_EFAULT);
}
if (!GetSocketBackend(s, &socket)) {
return -1;
}
@@ -1751,7 +1890,6 @@ int KYTY_SYSV_ABI Listen(int s, int backlog) {
NativeSocket socket = INVALID_NATIVE_SOCKET;
if (!GetSocketBackend(s, &socket)) {
*Posix::GetErrorAddr() = Posix::POSIX_EBADF;
return -1;
}
@@ -1772,7 +1910,6 @@ int KYTY_SYSV_ABI Accept(int s, void* addr, uint32_t* addrlen) {
NativeSocket socket = INVALID_NATIVE_SOCKET;
if (!GetSocketBackend(s, &socket)) {
*Posix::GetErrorAddr() = Posix::POSIX_EBADF;
return -1;
}
@@ -1784,13 +1921,9 @@ int KYTY_SYSV_ABI Accept(int s, void* addr, uint32_t* addrlen) {
return SetHostSocketError();
}
const int fd = AllocSocketFd(accepted);
auto transport = std::make_shared<SocketTransport>(accepted);
const int fd = AllocSocketFd(transport);
if (fd < 0) {
#if defined(_WIN32)
closesocket(accepted);
#else
::close(accepted);
#endif
*Posix::GetErrorAddr() = Posix::POSIX_EMFILE;
return -1;
}
@@ -1819,7 +1952,6 @@ int KYTY_SYSV_ABI Shutdown(int s, int how) {
NativeSocket socket = INVALID_NATIVE_SOCKET;
if (!GetSocketBackend(s, &socket)) {
*Posix::GetErrorAddr() = Posix::POSIX_EBADF;
return -1;
}
@@ -1848,12 +1980,14 @@ int KYTY_SYSV_ABI Getsockname(int s, void* addr, uint32_t* addrlen) {
"\t addrlen = 0x%016" PRIx64 "\n",
s, reinterpret_cast<uint64_t>(addr), reinterpret_cast<uint64_t>(addrlen));
NativeSocket socket = INVALID_NATIVE_SOCKET;
const bool socket_ok = GetSocketBackend(s, &socket);
if (addr == nullptr || addrlen == nullptr || !socket_ok) {
*Posix::GetErrorAddr() = (!socket_ok ? Posix::POSIX_EBADF : Posix::POSIX_EFAULT);
NativeSocket socket = INVALID_NATIVE_SOCKET;
SocketSlot state;
if (!GetSocketBackend(s, &socket, &state)) {
return -1;
}
if (addr == nullptr || addrlen == nullptr) {
return SetGuestSocketError(Posix::POSIX_EFAULT);
}
sockaddr_storage host_addr {};
SocketLength host_addrlen = sizeof(host_addr);
@@ -1861,7 +1995,11 @@ int KYTY_SYSV_ABI Getsockname(int s, void* addr, uint32_t* addrlen) {
return SetHostSocketError();
}
return ConvertHostSockaddr(&host_addr, host_addrlen, addr, addrlen);
const int result = ConvertHostSockaddr(&host_addr, host_addrlen, addr, addrlen);
if (result == 0 && state.p2p) {
std::memcpy(static_cast<uint8_t*>(addr) + 8, &state.p2p->virtual_port, sizeof(uint16_t));
}
return result;
}
int KYTY_SYSV_ABI Getsockopt(int s, int level, int optname, void* optval, uint32_t* optlen) {
@@ -1872,12 +2010,26 @@ int KYTY_SYSV_ABI Getsockopt(int s, int level, int optname, void* optval, uint32
"\t optname = 0x%08" PRIx32 "\n",
s, static_cast<uint32_t>(level), static_cast<uint32_t>(optname));
NativeSocket socket = INVALID_NATIVE_SOCKET;
const bool socket_ok = GetSocketBackend(s, &socket);
if (optval == nullptr || optlen == nullptr || !socket_ok) {
*Posix::GetErrorAddr() = (!socket_ok ? Posix::POSIX_EBADF : Posix::POSIX_EFAULT);
NativeSocket socket = INVALID_NATIVE_SOCKET;
SocketSlot state;
if (!GetSocketBackend(s, &socket, &state)) {
return -1;
}
if (optval == nullptr || optlen == nullptr) {
return SetGuestSocketError(Posix::POSIX_EFAULT);
}
if (state.p2p) {
const auto* value = level == 0xffff ? P2pSocketOption(*state.p2p, optname) : nullptr;
if (value == nullptr) {
return SetGuestSocketError(Posix::POSIX_ENOPROTOOPT);
}
if (*optlen < sizeof(int)) {
return SetGuestSocketError(Posix::POSIX_EINVAL);
}
std::memcpy(optval, value, sizeof(int));
*optlen = sizeof(int);
return 0;
}
// Guest socket options: SOL_SOCKET=0xffff, SO_ERROR=0x1007.
const bool socket_error = (level == 0xffff && optname == 0x1007);
@@ -1912,10 +2064,38 @@ int KYTY_SYSV_ABI Setsockopt(int s, int level, int optname, const void* optval,
s, static_cast<uint32_t>(level), static_cast<uint32_t>(optname), optlen);
NativeSocket socket = INVALID_NATIVE_SOCKET;
if (optval == nullptr || !GetSocketBackend(s, &socket)) {
*Posix::GetErrorAddr() = (optval == nullptr ? Posix::POSIX_EFAULT : Posix::POSIX_EBADF);
SocketSlot state;
if (optval == nullptr) {
return SetGuestSocketError(Posix::POSIX_EFAULT);
}
if (!GetSocketBackend(s, &socket, &state)) {
return -1;
}
if (state.p2p) {
Common::LockGuard lock(g_socket_mutex);
auto& slot = g_sockets[static_cast<size_t>(s)];
if (slot.transport != state.transport) {
return SetGuestSocketError(Posix::POSIX_EBADF);
}
auto* option = level == 0xffff ? P2pSocketOption(*slot.p2p, optname) : nullptr;
if (option == nullptr) {
return SetGuestSocketError(Posix::POSIX_ENOPROTOOPT);
}
if (optlen != sizeof(int)) {
return SetGuestSocketError(Posix::POSIX_EINVAL);
}
int value = 0;
std::memcpy(&value, optval, sizeof(value));
if (optname == 0x1001 || optname == 0x1002) {
if (value <= 0) {
return SetGuestSocketError(Posix::POSIX_EINVAL);
}
*option = optname == 0x1002 ? std::min(value, 512 * 1024) : value;
} else {
*option = value != 0;
}
return 0;
}
#if defined(_WIN32)
constexpr int ORBIS_SO_NBIO = 0x1200;
@@ -1962,8 +2142,11 @@ int64_t KYTY_SYSV_ABI Sendto(int s, const void* buf, uint64_t len, int flags, co
reinterpret_cast<uint64_t>(addr), addrlen);
NativeSocket socket = INVALID_NATIVE_SOCKET;
if (buf == nullptr || !GetSocketBackend(s, &socket)) {
*Posix::GetErrorAddr() = (buf == nullptr ? Posix::POSIX_EFAULT : Posix::POSIX_EBADF);
if (buf == nullptr) {
return SetGuestSocketError(Posix::POSIX_EFAULT);
}
// UDPP2P requires protocol framing; the native-only lookup rejects raw sends.
if (!GetSocketBackend(s, &socket)) {
return -1;
}
@@ -2017,7 +2200,6 @@ int64_t KYTY_SYSV_ABI Recvfrom(int s, void* buf, uint64_t len, int flags, void*
NativeSocket socket = INVALID_NATIVE_SOCKET;
if (!GetSocketBackend(s, &socket)) {
*Posix::GetErrorAddr() = Posix::POSIX_EBADF;
return -1;
}
@@ -2107,7 +2289,7 @@ int KYTY_SYSV_ABI Select(int nfds, void* readfds, void* writefds, void* exceptfd
} else
#endif
if (!GetSocketBackend(fd, &socket)) {
return SetGuestSocketError(Posix::POSIX_EBADF);
return -1;
}
#if defined(_WIN32)
if ((read && host_read.fd_count == FD_SETSIZE) ||
@@ -3103,17 +3285,10 @@ static HostNetworkInfo QueryHostNetworkInfo() {
#endif
}
[[maybe_unused]] static bool HostNetworkConnected() {
return QueryHostNetworkInfo().connected;
}
static bool NetCtlConnected() {
if (!g_net_ctl_status_initialized.load()) {
// g_net_ctl_connected = HostNetworkConnected();
g_net_ctl_connected = false;
g_net_ctl_status_initialized = true;
// LOGF("\t host network connected = %s\n", (g_net_ctl_connected.load() ? "true" :
// "false"));
LOGF("\t host network connected = false (forced offline)\n");
}
@@ -3123,10 +3298,8 @@ static bool NetCtlConnected() {
int KYTY_SYSV_ABI NetCtlInit() {
PRINT_NAME();
// g_net_ctl_connected = HostNetworkConnected();
g_net_ctl_connected = false;
g_net_ctl_status_initialized = true;
// LOGF("\t host network connected = %s\n", (g_net_ctl_connected.load() ? "true" : "false"));
LOGF("\t host network connected = false (forced offline)\n");
return OK;
@@ -3380,14 +3553,24 @@ struct NpCheckPremiumResult {
uint8_t reserved[32];
};
constexpr int np_error_invalid_argument = -2141913085; /* 0x80550003 */
constexpr int np_error_signed_out = -2141913082; /* 0x80550006 */
constexpr int np_error_invalid_size = -2141913071; /* 0x80550011 */
constexpr int np_error_aborted = -2141913070; /* 0x80550012 */
constexpr int np_error_request_max = -2141913069; /* 0x80550013 */
constexpr int np_error_request_not_found = -2141913068; /* 0x80550014 */
constexpr int np_error_invalid_id = -2141913067; /* 0x80550015 */
constexpr int np_request_max = 128;
constexpr int np_error_invalid_argument = -2141913085; /* 0x80550003 */
constexpr int np_error_signed_out = -2141913082; /* 0x80550006 */
constexpr int np_error_callback_already_registered = -2141913080; /* 0x80550008 */
constexpr int np_error_invalid_size = -2141913071; /* 0x80550011 */
constexpr int np_error_aborted = -2141913070; /* 0x80550012 */
constexpr int np_error_request_max = -2141913069; /* 0x80550013 */
constexpr int np_error_request_not_found = -2141913068; /* 0x80550014 */
constexpr int np_error_invalid_id = -2141913067; /* 0x80550015 */
constexpr int np_request_max = 128;
constexpr uint32_t np_state_signed_out = 1;
struct NpStateCallback {
NpStateCallbackA callback = nullptr;
void* userdata = nullptr;
bool pending = false;
};
static std::vector<NpStateCallback> g_np_state_callbacks;
enum class NpRequestState {
Free,
@@ -3458,6 +3641,17 @@ static int np_complete_signed_out_locked(NpRequest* request) {
int KYTY_SYSV_ABI NpCheckCallback() {
PRINT_NAME();
std::vector<NpStateCallback> pending;
for (auto& entry: g_np_state_callbacks) {
if (entry.pending) {
pending.push_back(entry);
entry.pending = false;
}
}
for (const auto& entry: pending) {
entry.callback(Config::GetUserId(), np_state_signed_out, entry.userdata);
}
return OK;
}
@@ -3469,7 +3663,7 @@ int KYTY_SYSV_ABI NpSetNpTitleId(const NpTitleId* title_id, const NpTitleSecret*
LOGF("\t title_id = %.12s\n"
"\t title_secret = %s\n",
title_id->id, Common::HexFromBin(Common::ByteBuffer(title_secret->data, 128)).c_str());
title_id->id, fmt::format("{:02X}", fmt::join(title_secret->data, "")));
return OK;
}
@@ -3506,6 +3700,21 @@ int KYTY_SYSV_ABI NpUnregisterStateCallback() {
return OK;
}
int KYTY_SYSV_ABI NpRegisterStateCallbackA(NpStateCallbackA callback, void* userdata) {
PRINT_NAME();
if (callback == nullptr) {
return np_error_invalid_argument;
}
for (const auto& entry: g_np_state_callbacks) {
if (entry.callback == callback) {
return np_error_callback_already_registered;
}
}
g_np_state_callbacks.push_back({callback, userdata, true});
return static_cast<int>(g_np_state_callbacks.size());
}
void KYTY_SYSV_ABI NpRegisterGamePresenceCallback(void* /*callback*/, void* /*userdata*/) {
PRINT_NAME();
}
@@ -3811,7 +4020,7 @@ int KYTY_SYSV_ABI NpGetState(int user_id, uint32_t* state) {
LOGF("\t user_id = %d\n", user_id);
*state = 1; // Signed out
*state = np_state_signed_out;
return OK;
}
+3
View File
@@ -167,11 +167,14 @@ struct NpCreateAsyncRequestParameter;
struct NpCheckPremiumParameter;
struct NpCheckPremiumResult;
using NpStateCallbackA = KYTY_SYSV_ABI void (*)(int user_id, uint32_t state, void* userdata);
int KYTY_SYSV_ABI NpCheckCallback();
int KYTY_SYSV_ABI NpSetNpTitleId(const NpTitleId* title_id, const NpTitleSecret* title_secret);
int KYTY_SYSV_ABI NpSetContentRestriction(const NpContentRestriction* restriction);
int KYTY_SYSV_ABI NpRegisterStateCallback(void* callback, void* userdata);
int KYTY_SYSV_ABI NpUnregisterStateCallback();
int KYTY_SYSV_ABI NpRegisterStateCallbackA(NpStateCallbackA callback, void* userdata);
void KYTY_SYSV_ABI NpRegisterGamePresenceCallback(void* callback, void* userdata);
int KYTY_SYSV_ABI NpRegisterPlusEventCallback(void* callback, void* userdata);
int KYTY_SYSV_ABI NpRegisterPremiumEventCallback(void* callback, void* userdata);
+1
View File
@@ -1,5 +1,6 @@
#include "loader/gamePatch.h"
#include "common/assert.h"
#include "common/stringUtils.h"
#include "common/virtualMemory.h"
#include "kernel/memory.h"
+3 -7
View File
@@ -103,11 +103,6 @@ static ZydisDecoder& GetDecoder() {
return decoder;
}
static ZyanStatus DecodeInstruction(ZydisDecodedInstruction& instruction,
ZydisDecodedOperand* operands, void* data, u64 size) {
return ZydisDecoderDecodeFull(&GetDecoder(), data, size, &instruction, operands);
}
#endif
#if defined(_WIN32)
@@ -177,8 +172,9 @@ uintptr_t GetRelativeTarget(const DecodedCodeInstruction& decoded) {
DecodedCodeInstruction DecodeCodeInstruction(uintptr_t address, uintptr_t end) {
DecodedCodeInstruction decoded {.address = address};
const auto status = DecodeInstruction(decoded.instruction, decoded.operands.data(),
reinterpret_cast<void*>(address), end - address);
const auto status = ZydisDecoderDecodeFull(&GetDecoder(), reinterpret_cast<void*>(address),
end - address, &decoded.instruction,
decoded.operands.data());
if (!ZYAN_SUCCESS(status)) {
decoded.instruction.length = 0;
return decoded;

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